Index: AE/packages/31000GlassBreakingMoves/About this patch.txt
===================================================================
--- AE/packages/31000GlassBreakingMoves/About this patch.txt	(revision 965)
+++ AE/packages/31000GlassBreakingMoves/About this patch.txt	(revision 965)
@@ -0,0 +1,40 @@
+Glass Breaking Moves
+Technical Notes
+by Iritscen (March 2014)
+
+Here is some detailed documentation on the patch for anyone who's interested.  If you are not familiar with the terms "patch mod" or "XML patching", first read http://wiki.oni2.net/Making_a_patch_mod.
+
+The way this patch makes glass-breaking work is to add to Oni a particle called "glass_break", logically enough. glass_break performs the action "Glass Charge" for the duration of its lifetime.
+
+Next, the particle is registered for use by a character by editing their ONCC (see Type 1 below). Finally (and this is the complex part), the particle is added to every animation that we desire to break glass. Generally speaking that means all attacks, and blownup and knockdown animations.
+
+Because we need to tread as lightly as possible in adding particles to Oni, the particle needs to be added intelligently. The complexity of the process requires the use of JavaScript through the @CUSTOM_CODE feature in XmlTools.
+
+Below are the five types of JS-driven patches performed by this mod. They are listed by the name-matching pattern used to collect the files to be patched; the count of vanilla files captured by this pattern is also listed. These pattern matches come to a grand total of 711 files. Note, however, that the patch will also apply to any active mods, which may contain additional ONCC and TRAM files.
+
+Type 1:
+Pattern: ONCC*
+Matches: 123 files
+Purpose: Looks for an existing glass_break particle in the ONCP registry, and adds it if not present.
+
+Type 2:
+Pattern: TRAM*blownup*, TRAM*tgt*
+Matches: 9 + 56 files (65 total)
+Purpose: Blownup animations (like knockdowns, below) have no existing Attack data, because falling characters do not strike other characters in vanilla Oni. Therefore, we simply add the glass_break particle to Head and RightFoot, so both ends of the character can break glass if passing through it. The lifetime of the particle is the duration of the animation.
+         "tgt" animations are the animations for the target (victim) of a throw. These _do_ have Attack data, generally listing many bones as candidates for colliding with and hurting other characters. We do not want to add glass_break to all these bones, so we use the same patching code as for blownups, adding glass_break to Head and RightFoot.
+
+Type 3:
+Pattern: TRAM*knockdown*
+Matches: 13 files
+Purpose: Like blownups above, knockdown animations have no existing Attack data. We don't want to use the exact code that we used for blownups, though, because the feet are unlikely to strike glass when the character falls down (this animation is also used much more frequently, so it is more important to be sparing with the particle). Therefore, we just add glass_break to Head for the duration of the animation.
+
+Type 4:
+Pattern: TRAM*comb*, TRAM*kick*, TRAM*punch*, TRAM*getup_k*, TRAM*getupfront*, TRAM*slide*
+Matches: 78 + 166 + 137 + 12 + 17 + 99 files (509 total)
+Purpose: The attack animations in Oni are captured by these patterns. Here, we look at what bones are used in the Attack in order to tell which bones might strike glass — but we need to be selective. E.g., Mutant Muro's heavy kick, TRAMMUTCOMkick_heavy (this is the butt slam attack) uses both the left and right thighs, calves, and feet for collision, for a total of six bones. The patch code for Type 4 determines which bones are outermost for each extremity used in the attack; in this case, it will determine that they are LeftFoot and RightFoot. Once the outermost bones are determined, glass_break is added to each of them, for the same duration as the attack's collision, e.g. frames 43-48.
+         In order to avoid particle overload, the patch has two safety measures. First, it will not add particles to an animation past a total count of 16 (including dust, contrails, etc.), because >16 particles in a TRAM will crash Oni. Secondly, the patch starts off by removing all existing glass_break particles listed for the animation. In previous versions of the Anniversary Edition, a set of TRAMs was distributed that had been edited by an automated tool to include glass_break. This tool did not hesitate to add glass_break for all attacking bones, and some of these modded TRAMs were used as the basis for new TRAMs made for new characters. Thus, we need to clear any existing glass_break assignments before adding them to the outermost bones as described above.
+
+Type 5:
+Pattern: TRAMKONCOMrun_throw_fw
+Matches: 1 file
+Purpose: This patch runs only for one animation, Konoko's Lariat. It is the only notable throw animation which has an attack component (not to be confused with throw target animations, which all have attack components), therefore no other throws need to be patched. This "throw" allows Konoko to kick nearby enemies while swinging around a victim's neck, so her feet have been assigned as attack bones. For this attack we do not need JavaScript because we know what to expect from this one animation. We simply add glass_break to LeftFoot for frames 29-39, matching the duration of the attack data for her feet.
Index: AE/packages/31000GlassBreakingMoves/Mod_Info.cfg
===================================================================
--- AE/packages/31000GlassBreakingMoves/Mod_Info.cfg	(revision 963)
+++ AE/packages/31000GlassBreakingMoves/Mod_Info.cfg	(revision 965)
@@ -3,3 +3,3 @@
 ModVersion -> 1.1
 Creator -> Gumby/geyser/s10k/Iritscen
-Readme -> Makes all attacks break glass.
+Readme -> Makes attacks and other colliding animations break glass. Total files patched: 711 (plus any new animations in active mods which conform to the same naming patterns as the vanilla animations). See included file "About this patch" for detailed documentation.
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/ONCC-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/ONCC-.oni-patch	(revision 963)
+++ AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/ONCC-.oni-patch	(revision 965)
@@ -1,10 +1,30 @@
 @XML_TOOLS Version "2.0"
 
-@ADD_INSIDE_NODES ElementName "Particles" ParentElementName "ONCP"
-<xml>
-	<ONCPParticle>
-		<Name>glass_break</Name>
-		<Type>glass_break</Type>
-		<BodyPart>None</BodyPart>
-	</ONCPParticle>
-</xml>
+@CUSTOM_CODE
+<code>
+	var myBuilder = new JSXMLBuilder();
+	myBuilder.load($xmlData);
+	var elements = myBuilder.elements[0];
+	
+	// If there are no attacks in this TRAM, ignore it
+	if (!elements.childElement("ONCP").childElement("Particles"))
+		return;
+						
+	// Check if glass_break is already registered for the character
+	var particles = elements.childElement("ONCP").childElement("Particles");
+	for (var i = 0; (particles.childElement(i)); i++)
+	{
+		var particle = particles.childElement(i);
+		if (particle.childElement("Name").text == "glass_break")
+			return;
+	}
+
+	// If we're still here, register glass_break
+	myBuilder.addElementAt("ONCPParticle",
+				"",
+				"<Name>glass_break</Name>\
+                       		 <Type>glass_break</Type>\
+                 		 <BodyPart>None</BodyPart>",
+				particles.index + 1,
+				particles.level + 1);
+</code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-blownup-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-blownup-.oni-patch	(revision 963)
+++ AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-blownup-.oni-patch	(revision 965)
@@ -3,43 +3,62 @@
 @CUSTOM_CODE
 <code>
-	function contains(fullStringOrArray, subString){
-		return fullStringOrArray.indexOf(subString)!=-1;
-	}
-	
-	function removeFromArray(_array, _value){
-		_array.splice(_array.indexOf(_value), 1);
-	}
-
 	var myBuilder = new JSXMLBuilder();
 	myBuilder.load($xmlData);
 
 	var elements = myBuilder.elements[0];
+	var particles = elements.childElement("Animation").childElement("Particles");
+	var added_to_head = false, added_to_feet = false;
 	
-	var particles = elements.childElement("Animation").childElement("Particles");
-						
-	// Check if any of the existing particles contains a head bone
-	for (var i=0; (particles.childElement(i)); i++){ // the condition is to check if the child element exists (!= undefined)
-		var currElement=particles.childElement(i);
-
-		if(currElement.childElement("Bone").text=="Head"){
-			if(currElement.childElement("Name").text=="glass_break"){
-				return; // not necessary to add
-			}
-			else{
-				// gather all the necessary info
-				var int_start=currElement.childElement("Start").text;
-				var int_end=currElement.childElement("End").text;
-				
-				// Insert the new glass particle (for when he touches glass with head
-				myBuilder.addElementAt("Particle","",
-				"<Start>"+int_start+"</Start>\
-                <End>"+int_end+"</End>\
-                <Bone>Head</Bone>\
-                <Name>glass_break</Name>",particles.index+1,particles.level+1);
-				break;
-			}
+	// Check if glass_break is assigned to Head and one of the feet;
+	// remove it from any other bones if present
+	for (var i = 0; (particles.childElement(i)); i++)
+	{
+		var particle = particles.childElement(i);
+		var bone = particle.childElement("Bone").text;
+		var name = particle.childElement("Name").text;
+		if (name == "glass_break")
+		{
+			if (bone == "Head")
+				added_to_head = true;
+			else if (bone == "RightFoot")
+				added_to_feet = true;
+			else
+				myBuilder.removeElement(particle.index);
 		}
 	}
+
+	// Exit if we are past Oni's limit on TRAM particles
+	if (particles.length >= 16)
+		return;
+
+	// Add the glass_break particle to the desired bones if it's not there already
+	var heights = elements.childElement("Animation").childElement("Heights");
+	var anim_length;
+	for (anim_length = 0; (heights.childElement(anim_length)); anim_length++) {;}
+	if (!added_to_head)
+		myBuilder.addElementAt("Particle",
+				       "",
+			 	       "<Start>0</Start>\
+                			<End>" + (anim_length-1) + "</End>\
+                			<Bone>Head</Bone>\
+                			<Name>glass_break</Name>",
+				       particles.index + 1,
+				       particles.level + 1);
+
+	// Exit if we are past Oni's limit on TRAM particles
+	if (particles.length >= 16)
+		return;
+
+	if (!added_to_feet)
+		myBuilder.addElementAt("Particle",
+				       "",
+			 	       "<Start>0</Start>\
+                			<End>" + (anim_length-1) + "</End>\
+                			<Bone>RightFoot</Bone>\
+                			<Name>glass_break</Name>",
+				       particles.index + 1,
+				       particles.level + 1);
  
-	$xmlData=myBuilder.generateXML(); // update the global variable with the new XML
+	// Update the global variable with the new XML
+	$xmlData = myBuilder.generateXML();
 </code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-comb-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-comb-.oni-patch	(revision 963)
+++ AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-comb-.oni-patch	(revision 965)
@@ -3,54 +3,148 @@
 @CUSTOM_CODE
 <code>
-	function contains(fullStringOrArray, subString){
-		return fullStringOrArray.indexOf(subString)!=-1;
-	}
+	// |———————————————————————————————————Code best viewed at this width————————————————————————————————————————|
+
+	// Load XML data
+	var myBuilder = new JSXMLBuilder();
+	myBuilder.load($xmlData);
+	var elements = myBuilder.elements[0];
 	
-	function removeFromArray(_array, _value){
-		_array.splice(_array.indexOf(_value), 1);
+	// If there are no attacks in this TRAM, ignore it
+	if (!elements.childElement("Animation").childElement("Attacks"))
+		return;
+	
+	// Gather all the necessary info
+	var particles   = elements.childElement("Animation").childElement("Particles");
+	var attack      = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
+	var hit_start   = attack.childElement("Start").text;
+	var hit_end     = attack.childElement("End").text;
+	var array_bones = attack.childElement("Bones").text.split(" ");
+						
+	// Remove glass_break if it is already assigned to any bones, because it's probably not been assigned the
+	// way we want it to be
+	for (var i = 0; (particles.childElement(i)); i++)
+	{
+		var particle = particles.childElement(i);
+		if (particle.childElement("Name").text == "glass_break")
+			myBuilder.removeElement(particle.index);
 	}
 
-	var myBuilder = new JSXMLBuilder();
-	myBuilder.load($xmlData);
-
-	var elements = myBuilder.elements[0];
-	
-	var particles = elements.childElement("Animation").childElement("Particles");
-	
-			
-	if(!elements.childElement("Animation").childElement("Attacks")){ // if no attacks found ignore file
-		return;
-	}
-	
-	var attackElement = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
-	
-	var int_start,int_end,array_bones;
-	
-	// gather all the necessary info
-	int_start=attackElement.childElement("Start").text;
-	int_end=attackElement.childElement("End").text;
-	array_bones=attackElement.childElement("Bones").text.split(" ");
-						
-	// Check if any of the existing particles correspond to the same bone and glass_break
-	for (var i=0; (particles.childElement(i)); i++){ // the condition is to check if the child element exists (!= undefined)
-		var currElement=particles.childElement(i);
-
-		if(contains(array_bones,currElement.childElement("Bone").text) && currElement.childElement("Name").text=="glass_break"){
-			removeFromArray(array_bones,currElement.childElement("Bone").text); // not necessary to add
+	// Find the outermost bone of each type
+	// The "type" in bone_type[] refers to the extremity of the body to which a bone belongs ("mid" counts as
+	// as an extremity because the head is part of "mid"); this is a "parallel array" with array_bones[]
+	var bone_type = new Array(19);
+	// The "ext" in bone_ext[] refers to the "outermostness" of the bone, that is, how far out on the extremity
+	// this bone is; this is a parallel array with array_bones[]
+	var bone_ext = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+	// The extremity_ arrays are parallel arrays which store the highest "outermostness" value found for each
+	// extremity, among all the bones in that extremity that are listed in the attack
+	var extremity_name = ["mid", "left_arm", "right_arm", "left_leg", "right_leg"];
+	var extremity_max = [0, 0, 0, 0, 0];
+	for (var i = 0; i < array_bones.length; i++)
+	{
+		var bone = array_bones[i];
+		if (bone == "Head" || bone == "Neck" || bone == "Chest" || bone == "Mid" || bone == "Pelvis")
+		{
+			bone_type[i] = "mid";
+			if (bone == "Neck")
+				bone_ext[i] = 1;
+			else if (bone == "Head")
+				bone_ext[i] = 2;
+			// The rest of these bones are extremity '0'
+		}
+		else if (bone == "LeftShoulder" || bone == "LeftArm" || bone == "LeftWrist" || bone == "LeftFist")
+		{
+			bone_type[i] = "left_arm";
+			if (bone == "LeftShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "LeftArm")
+				bone_ext[i] = 2;
+			else if (bone == "LeftWrist")
+				bone_ext[i] = 3;
+			else if (bone == "LeftFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "RightShoulder" || bone == "RightArm" || bone == "RightWrist" || bone == "RightFist")
+		{
+			bone_type[i] = "right_arm";
+			if (bone == "RightShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "RightArm")
+				bone_ext[i] = 2;
+			else if (bone == "RightWrist")
+				bone_ext[i] = 3;
+			else if (bone == "RightFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "LeftThigh" || bone == "LeftCalf" || bone == "LeftFoot")
+		{
+			bone_type[i] = "left_leg";
+			if (bone == "LeftThigh")
+				bone_ext[i] = 1;
+			else if (bone == "LeftCalf")
+				bone_ext[i] = 2;
+			else if (bone == "LeftFoot")
+				bone_ext[i] = 3;
+		}
+		else if (bone == "RightThigh" || bone == "RightCalf" || bone == "RightFoot")
+		{
+			bone_type[i] = "right_leg";
+			if (bone == "RightThigh")
+				bone_ext[i] = 1;
+			else if (bone == "RightCalf")
+				bone_ext[i] = 2;
+			else if (bone == "RightFoot")
+				bone_ext[i] = 3;
 		}
 	}
-	
-	// Insert the new glass particles
-	for (var i = 0; i < array_bones.length; i++) {
-		if( (contains(array_bones[i],"Foot")) && !contains(array_bones[i],"Calf") && !contains(array_bones[i],"Tight") 
-		||  (contains(array_bones[i],"Fist")) && !contains(array_bones[i],"Wrist")){
-		myBuilder.addElementAt("Particle","",
-				"<Start>"+int_start+"</Start>\
-                <End>"+int_end+"</End>\
-                <Bone>"+array_bones[i]+"</Bone>\
-                <Name>glass_break</Name>",particles.index+1,particles.level+1);
+
+	// Find outermost bone for each extremity, among the bones listed in the Attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (extremity_name[b] == bone_type[a])
+			{
+				if (bone_ext[a] > extremity_max[b])
+					extremity_max[b] = bone_ext[a];
+			}
 		}
 	}
+
+	// Add a glass_break particle for every outermost bone in the attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		// Move to next bone if this is not the outermost attacking bone on this extremity
+		var add_this_bone = false;
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (bone_type[a] == extremity_name[b])
+			{
+				if (bone_ext[a] == extremity_max[b])
+					add_this_bone = true;
+			}
+		}
+		if (!add_this_bone)
+			continue;
+
+		// Exit if we are past Oni's limit on TRAM particles
+		if (particles.length >= 16)
+		{
+			echo("Reached maximum of 16 particles for this TRAM, exiting…");
+			return;
+		}
+
+		echo("Adding glass_break to " + array_bones[a]);
+		var par_string = "<Start>" + hit_start + "</Start><End>" + hit_end + "</End><Bone>" + array_bones[a] + "</Bone><Name>glass_break</Name>";
+
+		// Add glass_break to bone for time period that bone has collision status
+		myBuilder.addElementAt("Particle",
+				       "",
+				       par_string,
+				       particles.index + 1,
+				       particles.level + 1);
+	}
  
-	$xmlData=myBuilder.generateXML(); // update the global variable with the new XML
+	// Update the global variable with the new XML
+	$xmlData = myBuilder.generateXML();
 </code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-getup_k-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-getup_k-.oni-patch	(revision 965)
+++ AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-getup_k-.oni-patch	(revision 965)
@@ -0,0 +1,150 @@
+@XML_TOOLS Version "2.0"
+
+@CUSTOM_CODE
+<code>
+	// |———————————————————————————————————Code best viewed at this width————————————————————————————————————————|
+
+	// Load XML data
+	var myBuilder = new JSXMLBuilder();
+	myBuilder.load($xmlData);
+	var elements = myBuilder.elements[0];
+	
+	// If there are no attacks in this TRAM, ignore it
+	if (!elements.childElement("Animation").childElement("Attacks"))
+		return;
+	
+	// Gather all the necessary info
+	var particles   = elements.childElement("Animation").childElement("Particles");
+	var attack      = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
+	var hit_start   = attack.childElement("Start").text;
+	var hit_end     = attack.childElement("End").text;
+	var array_bones = attack.childElement("Bones").text.split(" ");
+						
+	// Remove glass_break if it is already assigned to any bones, because it's probably not been assigned the
+	// way we want it to be
+	for (var i = 0; (particles.childElement(i)); i++)
+	{
+		var particle = particles.childElement(i);
+		if (particle.childElement("Name").text == "glass_break")
+			myBuilder.removeElement(particle.index);
+	}
+
+	// Find the outermost bone of each type
+	// The "type" in bone_type[] refers to the extremity of the body to which a bone belongs ("mid" counts as
+	// as an extremity because the head is part of "mid"); this is a "parallel array" with array_bones[]
+	var bone_type = new Array(19);
+	// The "ext" in bone_ext[] refers to the "outermostness" of the bone, that is, how far out on the extremity
+	// this bone is; this is a parallel array with array_bones[]
+	var bone_ext = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+	// The extremity_ arrays are parallel arrays which store the highest "outermostness" value found for each
+	// extremity, among all the bones in that extremity that are listed in the attack
+	var extremity_name = ["mid", "left_arm", "right_arm", "left_leg", "right_leg"];
+	var extremity_max = [0, 0, 0, 0, 0];
+	for (var i = 0; i < array_bones.length; i++)
+	{
+		var bone = array_bones[i];
+		if (bone == "Head" || bone == "Neck" || bone == "Chest" || bone == "Mid" || bone == "Pelvis")
+		{
+			bone_type[i] = "mid";
+			if (bone == "Neck")
+				bone_ext[i] = 1;
+			else if (bone == "Head")
+				bone_ext[i] = 2;
+			// The rest of these bones are extremity '0'
+		}
+		else if (bone == "LeftShoulder" || bone == "LeftArm" || bone == "LeftWrist" || bone == "LeftFist")
+		{
+			bone_type[i] = "left_arm";
+			if (bone == "LeftShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "LeftArm")
+				bone_ext[i] = 2;
+			else if (bone == "LeftWrist")
+				bone_ext[i] = 3;
+			else if (bone == "LeftFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "RightShoulder" || bone == "RightArm" || bone == "RightWrist" || bone == "RightFist")
+		{
+			bone_type[i] = "right_arm";
+			if (bone == "RightShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "RightArm")
+				bone_ext[i] = 2;
+			else if (bone == "RightWrist")
+				bone_ext[i] = 3;
+			else if (bone == "RightFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "LeftThigh" || bone == "LeftCalf" || bone == "LeftFoot")
+		{
+			bone_type[i] = "left_leg";
+			if (bone == "LeftThigh")
+				bone_ext[i] = 1;
+			else if (bone == "LeftCalf")
+				bone_ext[i] = 2;
+			else if (bone == "LeftFoot")
+				bone_ext[i] = 3;
+		}
+		else if (bone == "RightThigh" || bone == "RightCalf" || bone == "RightFoot")
+		{
+			bone_type[i] = "right_leg";
+			if (bone == "RightThigh")
+				bone_ext[i] = 1;
+			else if (bone == "RightCalf")
+				bone_ext[i] = 2;
+			else if (bone == "RightFoot")
+				bone_ext[i] = 3;
+		}
+	}
+
+	// Find outermost bone for each extremity, among the bones listed in the Attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (extremity_name[b] == bone_type[a])
+			{
+				if (bone_ext[a] > extremity_max[b])
+					extremity_max[b] = bone_ext[a];
+			}
+		}
+	}
+
+	// Add a glass_break particle for every outermost bone in the attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		// Move to next bone if this is not the outermost attacking bone on this extremity
+		var add_this_bone = false;
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (bone_type[a] == extremity_name[b])
+			{
+				if (bone_ext[a] == extremity_max[b])
+					add_this_bone = true;
+			}
+		}
+		if (!add_this_bone)
+			continue;
+
+		// Exit if we are past Oni's limit on TRAM particles
+		if (particles.length >= 16)
+		{
+			echo("Reached maximum of 16 particles for this TRAM, exiting…");
+			return;
+		}
+
+		echo("Adding glass_break to " + array_bones[a]);
+		var par_string = "<Start>" + hit_start + "</Start><End>" + hit_end + "</End><Bone>" + array_bones[a] + "</Bone><Name>glass_break</Name>";
+
+		// Add glass_break to bone for time period that bone has collision status
+		myBuilder.addElementAt("Particle",
+				       "",
+				       par_string,
+				       particles.index + 1,
+				       particles.level + 1);
+	}
+ 
+	// Update the global variable with the new XML
+	$xmlData = myBuilder.generateXML();
+</code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-getupfront-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-getupfront-.oni-patch	(revision 963)
+++ AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-getupfront-.oni-patch	(revision 965)
@@ -3,53 +3,148 @@
 @CUSTOM_CODE
 <code>
-	function contains(fullStringOrArray, subString){
-		return fullStringOrArray.indexOf(subString)!=-1;
-	}
+	// |———————————————————————————————————Code best viewed at this width————————————————————————————————————————|
+
+	// Load XML data
+	var myBuilder = new JSXMLBuilder();
+	myBuilder.load($xmlData);
+	var elements = myBuilder.elements[0];
 	
-	function removeFromArray(_array, _value){
-		_array.splice(_array.indexOf(_value), 1);
+	// If there are no attacks in this TRAM, ignore it
+	if (!elements.childElement("Animation").childElement("Attacks"))
+		return;
+	
+	// Gather all the necessary info
+	var particles   = elements.childElement("Animation").childElement("Particles");
+	var attack      = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
+	var hit_start   = attack.childElement("Start").text;
+	var hit_end     = attack.childElement("End").text;
+	var array_bones = attack.childElement("Bones").text.split(" ");
+						
+	// Remove glass_break if it is already assigned to any bones, because it's probably not been assigned the
+	// way we want it to be
+	for (var i = 0; (particles.childElement(i)); i++)
+	{
+		var particle = particles.childElement(i);
+		if (particle.childElement("Name").text == "glass_break")
+			myBuilder.removeElement(particle.index);
 	}
 
-	var myBuilder = new JSXMLBuilder();
-	myBuilder.load($xmlData);
-
-	var elements = myBuilder.elements[0];
-	
-	var particles = elements.childElement("Animation").childElement("Particles");
-	
-			
-	if(!elements.childElement("Animation").childElement("Attacks")){ // if no attacks found ignore file
-		return;
-	}
-	
-	var attackElement = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
-	
-	var int_start,int_end,array_bones;
-	
-	// gather all the necessary info
-	int_start=attackElement.childElement("Start").text;
-	int_end=attackElement.childElement("End").text;
-	array_bones=attackElement.childElement("Bones").text.split(" ");
-						
-	// Check if any of the existing particles correspond to the same bone and glass_break
-	for (var i=0; (particles.childElement(i)); i++){ // the condition is to check if the child element exists (!= undefined)
-		var currElement=particles.childElement(i);
-
-		if(contains(array_bones,currElement.childElement("Bone").text) && currElement.childElement("Name").text=="glass_break"){
-			removeFromArray(array_bones,currElement.childElement("Bone").text); // not necessary to add
+	// Find the outermost bone of each type
+	// The "type" in bone_type[] refers to the extremity of the body to which a bone belongs ("mid" counts as
+	// as an extremity because the head is part of "mid"); this is a "parallel array" with array_bones[]
+	var bone_type = new Array(19);
+	// The "ext" in bone_ext[] refers to the "outermostness" of the bone, that is, how far out on the extremity
+	// this bone is; this is a parallel array with array_bones[]
+	var bone_ext = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+	// The extremity_ arrays are parallel arrays which store the highest "outermostness" value found for each
+	// extremity, among all the bones in that extremity that are listed in the attack
+	var extremity_name = ["mid", "left_arm", "right_arm", "left_leg", "right_leg"];
+	var extremity_max = [0, 0, 0, 0, 0];
+	for (var i = 0; i < array_bones.length; i++)
+	{
+		var bone = array_bones[i];
+		if (bone == "Head" || bone == "Neck" || bone == "Chest" || bone == "Mid" || bone == "Pelvis")
+		{
+			bone_type[i] = "mid";
+			if (bone == "Neck")
+				bone_ext[i] = 1;
+			else if (bone == "Head")
+				bone_ext[i] = 2;
+			// The rest of these bones are extremity '0'
+		}
+		else if (bone == "LeftShoulder" || bone == "LeftArm" || bone == "LeftWrist" || bone == "LeftFist")
+		{
+			bone_type[i] = "left_arm";
+			if (bone == "LeftShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "LeftArm")
+				bone_ext[i] = 2;
+			else if (bone == "LeftWrist")
+				bone_ext[i] = 3;
+			else if (bone == "LeftFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "RightShoulder" || bone == "RightArm" || bone == "RightWrist" || bone == "RightFist")
+		{
+			bone_type[i] = "right_arm";
+			if (bone == "RightShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "RightArm")
+				bone_ext[i] = 2;
+			else if (bone == "RightWrist")
+				bone_ext[i] = 3;
+			else if (bone == "RightFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "LeftThigh" || bone == "LeftCalf" || bone == "LeftFoot")
+		{
+			bone_type[i] = "left_leg";
+			if (bone == "LeftThigh")
+				bone_ext[i] = 1;
+			else if (bone == "LeftCalf")
+				bone_ext[i] = 2;
+			else if (bone == "LeftFoot")
+				bone_ext[i] = 3;
+		}
+		else if (bone == "RightThigh" || bone == "RightCalf" || bone == "RightFoot")
+		{
+			bone_type[i] = "right_leg";
+			if (bone == "RightThigh")
+				bone_ext[i] = 1;
+			else if (bone == "RightCalf")
+				bone_ext[i] = 2;
+			else if (bone == "RightFoot")
+				bone_ext[i] = 3;
 		}
 	}
-	
-	// Insert the new glass particles
-	for (var i = 0; i < array_bones.length; i++) {
-		if(array_bones[i]=="RightFoot" || array_bones[i]=="LeftFoot" || array_bones[i]=="RightFist" || array_bones[i]=="LeftFist"){
-		myBuilder.addElementAt("Particle","",
-				"<Start>"+int_start+"</Start>\
-                <End>"+int_end+"</End>\
-                <Bone>"+array_bones[i]+"</Bone>\
-                <Name>glass_break</Name>",particles.index+1,particles.level+1);
+
+	// Find outermost bone for each extremity, among the bones listed in the Attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (extremity_name[b] == bone_type[a])
+			{
+				if (bone_ext[a] > extremity_max[b])
+					extremity_max[b] = bone_ext[a];
+			}
 		}
 	}
+
+	// Add a glass_break particle for every outermost bone in the attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		// Move to next bone if this is not the outermost attacking bone on this extremity
+		var add_this_bone = false;
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (bone_type[a] == extremity_name[b])
+			{
+				if (bone_ext[a] == extremity_max[b])
+					add_this_bone = true;
+			}
+		}
+		if (!add_this_bone)
+			continue;
+
+		// Exit if we are past Oni's limit on TRAM particles
+		if (particles.length >= 16)
+		{
+			echo("Reached maximum of 16 particles for this TRAM, exiting…");
+			return;
+		}
+
+		echo("Adding glass_break to " + array_bones[a]);
+		var par_string = "<Start>" + hit_start + "</Start><End>" + hit_end + "</End><Bone>" + array_bones[a] + "</Bone><Name>glass_break</Name>";
+
+		// Add glass_break to bone for time period that bone has collision status
+		myBuilder.addElementAt("Particle",
+				       "",
+				       par_string,
+				       particles.index + 1,
+				       particles.level + 1);
+	}
  
-	$xmlData=myBuilder.generateXML(); // update the global variable with the new XML
+	// Update the global variable with the new XML
+	$xmlData = myBuilder.generateXML();
 </code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-kick-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-kick-.oni-patch	(revision 963)
+++ AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-kick-.oni-patch	(revision 965)
@@ -3,53 +3,148 @@
 @CUSTOM_CODE
 <code>
-	function contains(fullStringOrArray, subString){
-		return fullStringOrArray.indexOf(subString)!=-1;
-	}
+	// |———————————————————————————————————Code best viewed at this width————————————————————————————————————————|
+
+	// Load XML data
+	var myBuilder = new JSXMLBuilder();
+	myBuilder.load($xmlData);
+	var elements = myBuilder.elements[0];
 	
-	function removeFromArray(_array, _value){
-		_array.splice(_array.indexOf(_value), 1);
+	// If there are no attacks in this TRAM, ignore it
+	if (!elements.childElement("Animation").childElement("Attacks"))
+		return;
+	
+	// Gather all the necessary info
+	var particles   = elements.childElement("Animation").childElement("Particles");
+	var attack      = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
+	var hit_start   = attack.childElement("Start").text;
+	var hit_end     = attack.childElement("End").text;
+	var array_bones = attack.childElement("Bones").text.split(" ");
+						
+	// Remove glass_break if it is already assigned to any bones, because it's probably not been assigned the
+	// way we want it to be
+	for (var i = 0; (particles.childElement(i)); i++)
+	{
+		var particle = particles.childElement(i);
+		if (particle.childElement("Name").text == "glass_break")
+			myBuilder.removeElement(particle.index);
 	}
 
-	var myBuilder = new JSXMLBuilder();
-	myBuilder.load($xmlData);
-
-	var elements = myBuilder.elements[0];
-	
-	var particles = elements.childElement("Animation").childElement("Particles");
-	
-			
-	if(!elements.childElement("Animation").childElement("Attacks")){ // if no attacks found ignore file
-		return;
-	}
-	
-	var attackElement = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
-	
-	var int_start,int_end,array_bones;
-	
-	// gather all the necessary info
-	int_start=attackElement.childElement("Start").text;
-	int_end=attackElement.childElement("End").text;
-	array_bones=attackElement.childElement("Bones").text.split(" ");
-						
-	// Check if any of the existing particles correspond to the same bone and glass_break
-	for (var i=0; (particles.childElement(i)); i++){ // the condition is to check if the child element exists (!= undefined)
-		var currElement=particles.childElement(i);
-
-		if(contains(array_bones,currElement.childElement("Bone").text) && currElement.childElement("Name").text=="glass_break"){
-			removeFromArray(array_bones,currElement.childElement("Bone").text); // not necessary to add
+	// Find the outermost bone of each type
+	// The "type" in bone_type[] refers to the extremity of the body to which a bone belongs ("mid" counts as
+	// as an extremity because the head is part of "mid"); this is a "parallel array" with array_bones[]
+	var bone_type = new Array(19);
+	// The "ext" in bone_ext[] refers to the "outermostness" of the bone, that is, how far out on the extremity
+	// this bone is; this is a parallel array with array_bones[]
+	var bone_ext = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+	// The extremity_ arrays are parallel arrays which store the highest "outermostness" value found for each
+	// extremity, among all the bones in that extremity that are listed in the attack
+	var extremity_name = ["mid", "left_arm", "right_arm", "left_leg", "right_leg"];
+	var extremity_max = [0, 0, 0, 0, 0];
+	for (var i = 0; i < array_bones.length; i++)
+	{
+		var bone = array_bones[i];
+		if (bone == "Head" || bone == "Neck" || bone == "Chest" || bone == "Mid" || bone == "Pelvis")
+		{
+			bone_type[i] = "mid";
+			if (bone == "Neck")
+				bone_ext[i] = 1;
+			else if (bone == "Head")
+				bone_ext[i] = 2;
+			// The rest of these bones are extremity '0'
+		}
+		else if (bone == "LeftShoulder" || bone == "LeftArm" || bone == "LeftWrist" || bone == "LeftFist")
+		{
+			bone_type[i] = "left_arm";
+			if (bone == "LeftShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "LeftArm")
+				bone_ext[i] = 2;
+			else if (bone == "LeftWrist")
+				bone_ext[i] = 3;
+			else if (bone == "LeftFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "RightShoulder" || bone == "RightArm" || bone == "RightWrist" || bone == "RightFist")
+		{
+			bone_type[i] = "right_arm";
+			if (bone == "RightShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "RightArm")
+				bone_ext[i] = 2;
+			else if (bone == "RightWrist")
+				bone_ext[i] = 3;
+			else if (bone == "RightFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "LeftThigh" || bone == "LeftCalf" || bone == "LeftFoot")
+		{
+			bone_type[i] = "left_leg";
+			if (bone == "LeftThigh")
+				bone_ext[i] = 1;
+			else if (bone == "LeftCalf")
+				bone_ext[i] = 2;
+			else if (bone == "LeftFoot")
+				bone_ext[i] = 3;
+		}
+		else if (bone == "RightThigh" || bone == "RightCalf" || bone == "RightFoot")
+		{
+			bone_type[i] = "right_leg";
+			if (bone == "RightThigh")
+				bone_ext[i] = 1;
+			else if (bone == "RightCalf")
+				bone_ext[i] = 2;
+			else if (bone == "RightFoot")
+				bone_ext[i] = 3;
 		}
 	}
-	
-	// Insert the new glass particles
-	for (var i = 0; i < array_bones.length; i++) {
-		if(array_bones[i]=="RightFoot" || array_bones[i]=="LeftFoot" || array_bones[i]=="RightFist" || array_bones[i]=="LeftFist"){
-		myBuilder.addElementAt("Particle","",
-				"<Start>"+int_start+"</Start>\
-                <End>"+int_end+"</End>\
-                <Bone>"+array_bones[i]+"</Bone>\
-                <Name>glass_break</Name>",particles.index+1,particles.level+1);
+
+	// Find outermost bone for each extremity, among the bones listed in the Attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (extremity_name[b] == bone_type[a])
+			{
+				if (bone_ext[a] > extremity_max[b])
+					extremity_max[b] = bone_ext[a];
+			}
 		}
 	}
+
+	// Add a glass_break particle for every outermost bone in the attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		// Move to next bone if this is not the outermost attacking bone on this extremity
+		var add_this_bone = false;
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (bone_type[a] == extremity_name[b])
+			{
+				if (bone_ext[a] == extremity_max[b])
+					add_this_bone = true;
+			}
+		}
+		if (!add_this_bone)
+			continue;
+
+		// Exit if we are past Oni's limit on TRAM particles
+		if (particles.length >= 16)
+		{
+			echo("Reached maximum of 16 particles for this TRAM, exiting…");
+			return;
+		}
+
+		echo("Adding glass_break to " + array_bones[a]);
+		var par_string = "<Start>" + hit_start + "</Start><End>" + hit_end + "</End><Bone>" + array_bones[a] + "</Bone><Name>glass_break</Name>";
+
+		// Add glass_break to bone for time period that bone has collision status
+		myBuilder.addElementAt("Particle",
+				       "",
+				       par_string,
+				       particles.index + 1,
+				       particles.level + 1);
+	}
  
-	$xmlData=myBuilder.generateXML(); // update the global variable with the new XML
+	// Update the global variable with the new XML
+	$xmlData = myBuilder.generateXML();
 </code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-knockdown-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-knockdown-.oni-patch	(revision 963)
+++ AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-knockdown-.oni-patch	(revision 965)
@@ -3,43 +3,37 @@
 @CUSTOM_CODE
 <code>
-	function contains(fullStringOrArray, subString){
-		return fullStringOrArray.indexOf(subString)!=-1;
-	}
-	
-	function removeFromArray(_array, _value){
-		_array.splice(_array.indexOf(_value), 1);
-	}
-
 	var myBuilder = new JSXMLBuilder();
 	myBuilder.load($xmlData);
 
 	var elements = myBuilder.elements[0];
-	
 	var particles = elements.childElement("Animation").childElement("Particles");
 						
-	// Check if any of the existing particles contains a head bone
-	for (var i=0; (particles.childElement(i)); i++){ // the condition is to check if the child element exists (!= undefined)
-		var currElement=particles.childElement(i);
+	// Check to see if glass_break is already added to Head
+	for (var i = 0; (particles.childElement(i)); i++)
+	{
+		var particle = particles.childElement(i);
+		if (particle.childElement("Bone").text == "Head" &&
+		    particle.childElement("Name").text == "glass_break")
+			return;
+	}
 
-		if(currElement.childElement("Bone").text=="Head"){
-			if(currElement.childElement("Name").text=="glass_break"){
-				return; // not necessary to add
-			}
-			else{
-				// gather all the necessary info
-				var int_start=currElement.childElement("Start").text;
-				var int_end=currElement.childElement("End").text;
+	// Exit if we are past Oni's limit on TRAM particles
+	if (particles.length >= 16)
+		return;
 				
-				// Insert the new glass particle (for when he touches glass with head
-				myBuilder.addElementAt("Particle","",
-				"<Start>"+int_start+"</Start>\
-                <End>"+int_end+"</End>\
-                <Bone>Head</Bone>\
-                <Name>glass_break</Name>",particles.index+1,particles.level+1);
-				break;
-			}
-		}
-	}
+	// Add the glass_break particle to Head for duration of animation
+	var heights = elements.childElement("Animation").childElement("Heights");
+	var anim_length;
+	for (anim_length = 0; (heights.childElement(anim_length)); anim_length++) {;}
+	myBuilder.addElementAt("Particle",
+			       "",
+			       "<Start>0</Start>\
+                		<End>" + (anim_length-1) + "</End>\
+                		<Bone>Head</Bone>\
+                		<Name>glass_break</Name>",
+			       particles.index + 1,
+			       particles.level + 1);
  
-	$xmlData=myBuilder.generateXML(); // update the global variable with the new XML
+	// Update the global variable with the new XML
+	$xmlData = myBuilder.generateXML();
 </code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-punch-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-punch-.oni-patch	(revision 963)
+++ AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-punch-.oni-patch	(revision 965)
@@ -3,53 +3,148 @@
 @CUSTOM_CODE
 <code>
-	function contains(fullStringOrArray, subString){
-		return fullStringOrArray.indexOf(subString)!=-1;
-	}
+	// |———————————————————————————————————Code best viewed at this width————————————————————————————————————————|
+
+	// Load XML data
+	var myBuilder = new JSXMLBuilder();
+	myBuilder.load($xmlData);
+	var elements = myBuilder.elements[0];
 	
-	function removeFromArray(_array, _value){
-		_array.splice(_array.indexOf(_value), 1);
+	// If there are no attacks in this TRAM, ignore it
+	if (!elements.childElement("Animation").childElement("Attacks"))
+		return;
+	
+	// Gather all the necessary info
+	var particles   = elements.childElement("Animation").childElement("Particles");
+	var attack      = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
+	var hit_start   = attack.childElement("Start").text;
+	var hit_end     = attack.childElement("End").text;
+	var array_bones = attack.childElement("Bones").text.split(" ");
+						
+	// Remove glass_break if it is already assigned to any bones, because it's probably not been assigned the
+	// way we want it to be
+	for (var i = 0; (particles.childElement(i)); i++)
+	{
+		var particle = particles.childElement(i);
+		if (particle.childElement("Name").text == "glass_break")
+			myBuilder.removeElement(particle.index);
 	}
 
-	var myBuilder = new JSXMLBuilder();
-	myBuilder.load($xmlData);
-
-	var elements = myBuilder.elements[0];
-	
-	var particles = elements.childElement("Animation").childElement("Particles");
-	
-			
-	if(!elements.childElement("Animation").childElement("Attacks")){ // if no attacks found ignore file
-		return;
-	}
-	
-	var attackElement = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
-	
-	var int_start,int_end,array_bones;
-	
-	// gather all the necessary info
-	int_start=attackElement.childElement("Start").text;
-	int_end=attackElement.childElement("End").text;
-	array_bones=attackElement.childElement("Bones").text.split(" ");
-						
-	// Check if any of the existing particles correspond to the same bone and glass_break
-	for (var i=0; (particles.childElement(i)); i++){ // the condition is to check if the child element exists (!= undefined)
-		var currElement=particles.childElement(i);
-
-		if(contains(array_bones,currElement.childElement("Bone").text) && currElement.childElement("Name").text=="glass_break"){
-			removeFromArray(array_bones,currElement.childElement("Bone").text); // not necessary to add
+	// Find the outermost bone of each type
+	// The "type" in bone_type[] refers to the extremity of the body to which a bone belongs ("mid" counts as
+	// as an extremity because the head is part of "mid"); this is a "parallel array" with array_bones[]
+	var bone_type = new Array(19);
+	// The "ext" in bone_ext[] refers to the "outermostness" of the bone, that is, how far out on the extremity
+	// this bone is; this is a parallel array with array_bones[]
+	var bone_ext = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+	// The extremity_ arrays are parallel arrays which store the highest "outermostness" value found for each
+	// extremity, among all the bones in that extremity that are listed in the attack
+	var extremity_name = ["mid", "left_arm", "right_arm", "left_leg", "right_leg"];
+	var extremity_max = [0, 0, 0, 0, 0];
+	for (var i = 0; i < array_bones.length; i++)
+	{
+		var bone = array_bones[i];
+		if (bone == "Head" || bone == "Neck" || bone == "Chest" || bone == "Mid" || bone == "Pelvis")
+		{
+			bone_type[i] = "mid";
+			if (bone == "Neck")
+				bone_ext[i] = 1;
+			else if (bone == "Head")
+				bone_ext[i] = 2;
+			// The rest of these bones are extremity '0'
+		}
+		else if (bone == "LeftShoulder" || bone == "LeftArm" || bone == "LeftWrist" || bone == "LeftFist")
+		{
+			bone_type[i] = "left_arm";
+			if (bone == "LeftShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "LeftArm")
+				bone_ext[i] = 2;
+			else if (bone == "LeftWrist")
+				bone_ext[i] = 3;
+			else if (bone == "LeftFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "RightShoulder" || bone == "RightArm" || bone == "RightWrist" || bone == "RightFist")
+		{
+			bone_type[i] = "right_arm";
+			if (bone == "RightShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "RightArm")
+				bone_ext[i] = 2;
+			else if (bone == "RightWrist")
+				bone_ext[i] = 3;
+			else if (bone == "RightFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "LeftThigh" || bone == "LeftCalf" || bone == "LeftFoot")
+		{
+			bone_type[i] = "left_leg";
+			if (bone == "LeftThigh")
+				bone_ext[i] = 1;
+			else if (bone == "LeftCalf")
+				bone_ext[i] = 2;
+			else if (bone == "LeftFoot")
+				bone_ext[i] = 3;
+		}
+		else if (bone == "RightThigh" || bone == "RightCalf" || bone == "RightFoot")
+		{
+			bone_type[i] = "right_leg";
+			if (bone == "RightThigh")
+				bone_ext[i] = 1;
+			else if (bone == "RightCalf")
+				bone_ext[i] = 2;
+			else if (bone == "RightFoot")
+				bone_ext[i] = 3;
 		}
 	}
-	
-	// Insert the new glass particles
-	for (var i = 0; i < array_bones.length; i++) {
-		if(array_bones[i]=="RightFoot" || array_bones[i]=="LeftFoot" || array_bones[i]=="RightFist" || array_bones[i]=="LeftFist"){
-		myBuilder.addElementAt("Particle","",
-				"<Start>"+int_start+"</Start>\
-                <End>"+int_end+"</End>\
-                <Bone>"+array_bones[i]+"</Bone>\
-                <Name>glass_break</Name>",particles.index+1,particles.level+1);
+
+	// Find outermost bone for each extremity, among the bones listed in the Attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (extremity_name[b] == bone_type[a])
+			{
+				if (bone_ext[a] > extremity_max[b])
+					extremity_max[b] = bone_ext[a];
+			}
 		}
 	}
+
+	// Add a glass_break particle for every outermost bone in the attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		// Move to next bone if this is not the outermost attacking bone on this extremity
+		var add_this_bone = false;
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (bone_type[a] == extremity_name[b])
+			{
+				if (bone_ext[a] == extremity_max[b])
+					add_this_bone = true;
+			}
+		}
+		if (!add_this_bone)
+			continue;
+
+		// Exit if we are past Oni's limit on TRAM particles
+		if (particles.length >= 16)
+		{
+			echo("Reached maximum of 16 particles for this TRAM, exiting…");
+			return;
+		}
+
+		echo("Adding glass_break to " + array_bones[a]);
+		var par_string = "<Start>" + hit_start + "</Start><End>" + hit_end + "</End><Bone>" + array_bones[a] + "</Bone><Name>glass_break</Name>";
+
+		// Add glass_break to bone for time period that bone has collision status
+		myBuilder.addElementAt("Particle",
+				       "",
+				       par_string,
+				       particles.index + 1,
+				       particles.level + 1);
+	}
  
-	$xmlData=myBuilder.generateXML(); // update the global variable with the new XML
+	// Update the global variable with the new XML
+	$xmlData = myBuilder.generateXML();
 </code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-run_thw_fw_p_tgt-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-run_thw_fw_p_tgt-.oni-patch	(revision 963)
+++ 	(revision )
@@ -1,55 +1,0 @@
-@XML_TOOLS Version "2.0"
-
-@CUSTOM_CODE
-<code>
-	function contains(fullStringOrArray, subString){
-		return fullStringOrArray.indexOf(subString)!=-1;
-	}
-	
-	function removeFromArray(_array, _value){
-		_array.splice(_array.indexOf(_value), 1);
-	}
-
-	var myBuilder = new JSXMLBuilder();
-	myBuilder.load($xmlData);
-
-	var elements = myBuilder.elements[0];
-	
-	var particles = elements.childElement("Animation").childElement("Particles");
-	
-			
-	if(!elements.childElement("Animation").childElement("Attacks")){ // if no attacks found ignore file
-		return;
-	}
-	
-	var attackElement = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
-	
-	var int_start,int_end,array_bones;
-	
-	// gather all the necessary info
-	int_start=attackElement.childElement("Start").text;
-	int_end=attackElement.childElement("End").text;
-	array_bones=attackElement.childElement("Bones").text.split(" ");
-						
-	// Check if any of the existing particles correspond to the same bone and glass_break
-	for (var i=0; (particles.childElement(i)); i++){ // the condition is to check if the child element exists (!= undefined)
-		var currElement=particles.childElement(i);
-
-		if(contains(array_bones,currElement.childElement("Bone").text) && currElement.childElement("Name").text=="glass_break"){
-			removeFromArray(array_bones,currElement.childElement("Bone").text); // not necessary to add
-		}
-	}
-	
-	// Insert the new glass particles
-	for (var i = 0; i < array_bones.length; i++) {
-		if(array_bones[i]=="RightFoot" || array_bones[i]=="LeftFoot" || array_bones[i]=="RightFist" || array_bones[i]=="LeftFist"){
-		myBuilder.addElementAt("Particle","",
-				"<Start>"+int_start+"</Start>\
-                <End>"+int_end+"</End>\
-                <Bone>"+array_bones[i]+"</Bone>\
-                <Name>glass_break</Name>",particles.index+1,particles.level+1);
-		}
-	}
- 
-	$xmlData=myBuilder.generateXML(); // update the global variable with the new XML
-</code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-slide-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-slide-.oni-patch	(revision 963)
+++ AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-slide-.oni-patch	(revision 965)
@@ -3,53 +3,148 @@
 @CUSTOM_CODE
 <code>
-	function contains(fullStringOrArray, subString){
-		return fullStringOrArray.indexOf(subString)!=-1;
-	}
+	// |———————————————————————————————————Code best viewed at this width————————————————————————————————————————|
+
+	// Load XML data
+	var myBuilder = new JSXMLBuilder();
+	myBuilder.load($xmlData);
+	var elements = myBuilder.elements[0];
 	
-	function removeFromArray(_array, _value){
-		_array.splice(_array.indexOf(_value), 1);
+	// If there are no attacks in this TRAM, ignore it
+	if (!elements.childElement("Animation").childElement("Attacks"))
+		return;
+	
+	// Gather all the necessary info
+	var particles   = elements.childElement("Animation").childElement("Particles");
+	var attack      = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
+	var hit_start   = attack.childElement("Start").text;
+	var hit_end     = attack.childElement("End").text;
+	var array_bones = attack.childElement("Bones").text.split(" ");
+						
+	// Remove glass_break if it is already assigned to any bones, because it's probably not been assigned the
+	// way we want it to be
+	for (var i = 0; (particles.childElement(i)); i++)
+	{
+		var particle = particles.childElement(i);
+		if (particle.childElement("Name").text == "glass_break")
+			myBuilder.removeElement(particle.index);
 	}
 
-	var myBuilder = new JSXMLBuilder();
-	myBuilder.load($xmlData);
-
-	var elements = myBuilder.elements[0];
-	
-	var particles = elements.childElement("Animation").childElement("Particles");
-	
-			
-	if(!elements.childElement("Animation").childElement("Attacks")){ // if no attacks found ignore file
-		return;
-	}
-	
-	var attackElement = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
-	
-	var int_start,int_end,array_bones;
-	
-	// gather all the necessary info
-	int_start=attackElement.childElement("Start").text;
-	int_end=attackElement.childElement("End").text;
-	array_bones=attackElement.childElement("Bones").text.split(" ");
-						
-	// Check if any of the existing particles correspond to the same bone and glass_break
-	for (var i=0; (particles.childElement(i)); i++){ // the condition is to check if the child element exists (!= undefined)
-		var currElement=particles.childElement(i);
-
-		if(contains(array_bones,currElement.childElement("Bone").text) && currElement.childElement("Name").text=="glass_break"){
-			removeFromArray(array_bones,currElement.childElement("Bone").text); // not necessary to add
+	// Find the outermost bone of each type
+	// The "type" in bone_type[] refers to the extremity of the body to which a bone belongs ("mid" counts as
+	// as an extremity because the head is part of "mid"); this is a "parallel array" with array_bones[]
+	var bone_type = new Array(19);
+	// The "ext" in bone_ext[] refers to the "outermostness" of the bone, that is, how far out on the extremity
+	// this bone is; this is a parallel array with array_bones[]
+	var bone_ext = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+	// The extremity_ arrays are parallel arrays which store the highest "outermostness" value found for each
+	// extremity, among all the bones in that extremity that are listed in the attack
+	var extremity_name = ["mid", "left_arm", "right_arm", "left_leg", "right_leg"];
+	var extremity_max = [0, 0, 0, 0, 0];
+	for (var i = 0; i < array_bones.length; i++)
+	{
+		var bone = array_bones[i];
+		if (bone == "Head" || bone == "Neck" || bone == "Chest" || bone == "Mid" || bone == "Pelvis")
+		{
+			bone_type[i] = "mid";
+			if (bone == "Neck")
+				bone_ext[i] = 1;
+			else if (bone == "Head")
+				bone_ext[i] = 2;
+			// The rest of these bones are extremity '0'
+		}
+		else if (bone == "LeftShoulder" || bone == "LeftArm" || bone == "LeftWrist" || bone == "LeftFist")
+		{
+			bone_type[i] = "left_arm";
+			if (bone == "LeftShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "LeftArm")
+				bone_ext[i] = 2;
+			else if (bone == "LeftWrist")
+				bone_ext[i] = 3;
+			else if (bone == "LeftFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "RightShoulder" || bone == "RightArm" || bone == "RightWrist" || bone == "RightFist")
+		{
+			bone_type[i] = "right_arm";
+			if (bone == "RightShoulder")
+				bone_ext[i] = 1;
+			else if (bone == "RightArm")
+				bone_ext[i] = 2;
+			else if (bone == "RightWrist")
+				bone_ext[i] = 3;
+			else if (bone == "RightFist")
+				bone_ext[i] = 4;
+		}
+		else if (bone == "LeftThigh" || bone == "LeftCalf" || bone == "LeftFoot")
+		{
+			bone_type[i] = "left_leg";
+			if (bone == "LeftThigh")
+				bone_ext[i] = 1;
+			else if (bone == "LeftCalf")
+				bone_ext[i] = 2;
+			else if (bone == "LeftFoot")
+				bone_ext[i] = 3;
+		}
+		else if (bone == "RightThigh" || bone == "RightCalf" || bone == "RightFoot")
+		{
+			bone_type[i] = "right_leg";
+			if (bone == "RightThigh")
+				bone_ext[i] = 1;
+			else if (bone == "RightCalf")
+				bone_ext[i] = 2;
+			else if (bone == "RightFoot")
+				bone_ext[i] = 3;
 		}
 	}
-	
-	// Insert the new glass particles
-	for (var i = 0; i < array_bones.length; i++) {
-		if(array_bones[i]=="RightFoot" || array_bones[i]=="LeftFoot" || array_bones[i]=="RightFist" || array_bones[i]=="LeftFist"){
-		myBuilder.addElementAt("Particle","",
-				"<Start>"+int_start+"</Start>\
-                <End>"+int_end+"</End>\
-                <Bone>"+array_bones[i]+"</Bone>\
-                <Name>glass_break</Name>",particles.index+1,particles.level+1);
+
+	// Find outermost bone for each extremity, among the bones listed in the Attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (extremity_name[b] == bone_type[a])
+			{
+				if (bone_ext[a] > extremity_max[b])
+					extremity_max[b] = bone_ext[a];
+			}
 		}
 	}
+
+	// Add a glass_break particle for every outermost bone in the attack
+	for (var a = 0; a < array_bones.length; a++)
+	{
+		// Move to next bone if this is not the outermost attacking bone on this extremity
+		var add_this_bone = false;
+		for (var b = 0; b < extremity_name.length; b++)
+		{
+			if (bone_type[a] == extremity_name[b])
+			{
+				if (bone_ext[a] == extremity_max[b])
+					add_this_bone = true;
+			}
+		}
+		if (!add_this_bone)
+			continue;
+
+		// Exit if we are past Oni's limit on TRAM particles
+		if (particles.length >= 16)
+		{
+			echo("Reached maximum of 16 particles for this TRAM, exiting…");
+			return;
+		}
+
+		echo("Adding glass_break to " + array_bones[a]);
+		var par_string = "<Start>" + hit_start + "</Start><End>" + hit_end + "</End><Bone>" + array_bones[a] + "</Bone><Name>glass_break</Name>";
+
+		// Add glass_break to bone for time period that bone has collision status
+		myBuilder.addElementAt("Particle",
+				       "",
+				       par_string,
+				       particles.index + 1,
+				       particles.level + 1);
+	}
  
-	$xmlData=myBuilder.generateXML(); // update the global variable with the new XML
+	// Update the global variable with the new XML
+	$xmlData = myBuilder.generateXML();
 </code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-tgt-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-tgt-.oni-patch	(revision 965)
+++ AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-tgt-.oni-patch	(revision 965)
@@ -0,0 +1,64 @@
+@XML_TOOLS Version "2.0"
+
+@CUSTOM_CODE
+<code>
+	var myBuilder = new JSXMLBuilder();
+	myBuilder.load($xmlData);
+
+	var elements = myBuilder.elements[0];
+	var particles = elements.childElement("Animation").childElement("Particles");
+	var added_to_head = false, added_to_feet = false;
+	
+	// Check if glass_break is assigned to Head and one of the feet;
+	// remove it from any other bones if present
+	for (var i = 0; (particles.childElement(i)); i++)
+	{
+		var particle = particles.childElement(i);
+		var bone = particle.childElement("Bone").text;
+		var name = particle.childElement("Name").text;
+		if (name == "glass_break")
+		{
+			if (bone == "Head")
+				added_to_head = true;
+			else if (bone == "RightFoot")
+				added_to_feet = true;
+			else
+				myBuilder.removeElement(particle.index);
+		}
+	}
+
+	// Exit if we are past Oni's limit on TRAM particles
+	if (particles.length >= 16)
+		return;
+
+	// Add the glass_break particle to the desired bones if it's not there already
+	var heights = elements.childElement("Animation").childElement("Heights");
+	var anim_length;
+	for (anim_length = 0; (heights.childElement(anim_length)); anim_length++) {;}
+	if (!added_to_head)
+		myBuilder.addElementAt("Particle",
+				       "",
+			 	       "<Start>0</Start>\
+                			<End>" + (anim_length-1) + "</End>\
+                			<Bone>Head</Bone>\
+                			<Name>glass_break</Name>",
+				       particles.index + 1,
+				       particles.level + 1);
+
+	// Exit if we are past Oni's limit on TRAM particles
+	if (particles.length >= 16)
+		return;
+
+	if (!added_to_feet)
+		myBuilder.addElementAt("Particle",
+				       "",
+			 	       "<Start>0</Start>\
+                			<End>" + (anim_length-1) + "</End>\
+                			<Bone>RightFoot</Bone>\
+                			<Name>glass_break</Name>",
+				       particles.index + 1,
+				       particles.level + 1);
+ 
+	// Update the global variable with the new XML
+	$xmlData = myBuilder.generateXML();
+</code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-throw-.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAM-throw-.oni-patch	(revision 963)
+++ 	(revision )
@@ -1,55 +1,0 @@
-@XML_TOOLS Version "2.0"
-
-@CUSTOM_CODE
-<code>
-	function contains(fullStringOrArray, subString){
-		return fullStringOrArray.indexOf(subString)!=-1;
-	}
-	
-	function removeFromArray(_array, _value){
-		_array.splice(_array.indexOf(_value), 1);
-	}
-
-	var myBuilder = new JSXMLBuilder();
-	myBuilder.load($xmlData);
-
-	var elements = myBuilder.elements[0];
-	
-	var particles = elements.childElement("Animation").childElement("Particles");
-	
-			
-	if(!elements.childElement("Animation").childElement("Attacks")){ // if no attacks found ignore file
-		return;
-	}
-	
-	var attackElement = elements.childElement("Animation").childElement("Attacks").childElement("Attack");
-	
-	var int_start,int_end,array_bones;
-	
-	// gather all the necessary info
-	int_start=attackElement.childElement("Start").text;
-	int_end=attackElement.childElement("End").text;
-	array_bones=attackElement.childElement("Bones").text.split(" ");
-						
-	// Check if any of the existing particles correspond to the same bone and glass_break
-	for (var i=0; (particles.childElement(i)); i++){ // the condition is to check if the child element exists (!= undefined)
-		var currElement=particles.childElement(i);
-
-		if(contains(array_bones,currElement.childElement("Bone").text) && currElement.childElement("Name").text=="glass_break"){
-			removeFromArray(array_bones,currElement.childElement("Bone").text); // not necessary to add
-		}
-	}
-	
-	// Insert the new glass particles
-	for (var i = 0; i < array_bones.length; i++) {
-		if(array_bones[i]=="RightFoot" || array_bones[i]=="LeftFoot" || array_bones[i]=="RightFist" || array_bones[i]=="LeftFist"){
-		myBuilder.addElementAt("Particle","",
-				"<Start>"+int_start+"</Start>\
-                <End>"+int_end+"</End>\
-                <Bone>"+array_bones[i]+"</Bone>\
-                <Name>glass_break</Name>",particles.index+1,particles.level+1);
-		}
-	}
- 
-	$xmlData=myBuilder.generateXML(); // update the global variable with the new XML
-</code>
Index: AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAMKONCOMrun_throw_fw.oni-patch
===================================================================
--- AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAMKONCOMrun_throw_fw.oni-patch	(revision 965)
+++ AE/packages/31000GlassBreakingMoves/patches/common/level0_Final/TRAMKONCOMrun_throw_fw.oni-patch	(revision 965)
@@ -0,0 +1,11 @@
+@XML_TOOLS Version "2.0"
+
+@ADD_INSIDE_NODES ElementName "Particles"
+<xml>
+    <Particle>
+        <Start>29</Start>
+        <End>39</End>
+        <Bone>LeftFoot</Bone>
+        <Name>glass_break</Name>
+    </Particle>
+</xml>
