Let's see if i can share it quick
First thing is to add the Parry type in the HitConfirmType enum in MoveInfo.cs
It becomes
public enum HitConfirmType {
Hit,
Throw,
Parry
}
It will also become available in the inspector to set your move's hit type as a Parry hit
In MoveEditorWindow.cs, this line
castingValues.Add(new Vector3(hit.activeFramesBegin, hit.activeFramesEnds, (hit.hitConfirmType == HitConfirmType.Throw ? 1: 0)));
Becomes this line
castingValues.Add(new Vector3(hit.activeFramesBegin, hit.activeFramesEnds, (hit.hitConfirmType == HitConfirmType.Throw || hit.hitConfirmType == HitConfirmType.Parry ? 1: 0)));
¸
That does....something....
This line
else if (moveInfo.hits[i].hitConfirmType == HitConfirmType.Throw)
Becomes this, to let the move editor show the throw confirm move and stuff for a parry, which essentially work as a throw (attempt, confirm, reaction)
else if (moveInfo.hits[i].hitConfirmType == HitConfirmType.Throw || moveInfo.hits[i].hitConfirmType == HitConfirmType.Parry)
This line
if (moveInfo.moveClassification.hitConfirmType == HitConfirmType.Throw)
Becomes this, for some reason
if (moveInfo.moveClassification.hitConfirmType == HitConfirmType.Throw || moveInfo.moveClassification.hitConfirmType == HitConfirmType.Parry)
That takes care of the move setup and the move editor
Now in ControlsScript.cs
This
// Throw
}else if (hit.hitConfirmType == HitConfirmType.Throw){
CastMove(hit.throwMove, true);
return;
Becomes this, to make the parry work as a throw confirm when it connects
// Throw/Parry
}else if (hit.hitConfirmType == HitConfirmType.Throw || hit.hitConfirmType == HitConfirmType.Parry){
CastMove(hit.throwMove, true);
return;
This code
// Parry
}else if (opControlsScript.potentialParry > 0
&& opControlsScript.currentMove == null
&& hit.hitConfirmType != HitConfirmType.Throw
&& opControlsScript.TestParryStances(hit.hitType)
){
Becomes this, to make normal UFE parries not consider parry hits just like it doesn't consider throw hits
// Parry
}else if (opControlsScript.potentialParry > 0
&& opControlsScript.currentMove == null
&& hit.hitConfirmType != HitConfirmType.Throw
&& hit.hitConfirmType != HitConfirmType.Parry
&& opControlsScript.TestParryStances(hit.hitType)
){
Now we're in the final file to modify: HitBoxesScript.cs
This code
public Vector3[] TestCollision(HurtBox[] hurtBoxes, HitConfirmType hitConfirmType) {
if (isHit && hitConfirmType == HitConfirmType.Hit) return new Vector3[] { };
Becomes this, to make hit and parry collisions happen only once (I think)
public Vector3[] TestCollision(HurtBox[] hurtBoxes, HitConfirmType hitConfirmType)
{
if(isHit && (hitConfirmType == HitConfirmType.Hit || hitConfirmType == HitConfirmType.Parry))
{
return new Vector3[] { };
}
A few lines down, this code
return HitBoxesScript.TestCollision(this.hitBoxes, hurtBoxes, hitConfirmType, controlsScript.mirror);
Becomes this, to call the new collision test for the parry, we'll want to test against active hurt boxes to parry an incoming attack and not any hit collider on the character that would enter the parry zone
// A parry will check against other hurt boxes, not hit boxes
if(hitConfirmType == HitConfirmType.Parry)
{
return HitBoxesScript.TestCollision(this.activeHurtBoxes, hurtBoxes, hitConfirmType, controlsScript.mirror);
}
return HitBoxesScript.TestCollision(this.hitBoxes, hurtBoxes, hitConfirmType, controlsScript.mirror);
Now to display the active parry frames when editing the move (how throws are orange and hits are...some other color, Cyan)
This code
if (hitConfirmType == HitConfirmType.Throw){
Gizmos.color = new Color(1f, .5f, 0);
}else{
Gizmos.color = Color.cyan;
}
Becomes this, of course put whatever color you want, I put red, arbitrarily
if(hitConfirmType == HitConfirmType.Throw)
{
Gizmos.color = new Color(1f, .5f, 0);
}
else if (hitConfirmType == HitConfirmType.Parry)
{
Gizmos.color = new Color(1f, 0.0f, 0.0f);
}
else
{
Gizmos.color = Color.cyan;
}
And finally, the actual new function to test collision for the parry on the opponent's active hurt boxes.
public static Vector3[] TestCollision(HurtBox[] activeHurtBoxes, HurtBox[] hurtBoxes, HitConfirmType hitConfirmType, int mirror) {
if(activeHurtBoxes == null) return new Vector3[]{ };
foreach (HurtBox activeHurtBox in activeHurtBoxes) {
activeHurtBox.state = 0;
//drawRect.Clear();
foreach (HurtBox hurtBox in hurtBoxes) {
Vector3 hurtBoxPosition = hurtBox.position;
Vector3 activeHurtBoxPosition = activeHurtBox.position;
float dist = 0;
bool collisionConfirm = false;
if (!UFE.config.detect3D_Hits){
hurtBoxPosition.z = -1;
activeHurtBoxPosition.z = -1;
}
if (hurtBox.shape == HitBoxShape.circle){
if (activeHurtBox.shape == HitBoxShape.circle){
dist = Vector3.Distance(hurtBoxPosition, activeHurtBoxPosition);
if (dist <= hurtBox.radius + activeHurtBox.radius) collisionConfirm = true;
}else if (activeHurtBox.shape == HitBoxShape.rectangle){
Rect activeHurtBoxRectanglePosition = new Rect(activeHurtBox.rect);
activeHurtBoxRectanglePosition.x *= -mirror;
activeHurtBoxRectanglePosition.width *= -mirror;
activeHurtBoxRectanglePosition.x += activeHurtBoxPosition.x;
activeHurtBoxRectanglePosition.y += activeHurtBoxPosition.y;
if (activeHurtBox.followXBounds) {
activeHurtBoxRectanglePosition.x = activeHurtBox.rendererBounds.x - (activeHurtBox.rect.width / 2);
activeHurtBoxRectanglePosition.width = (activeHurtBox.rendererBounds.width + activeHurtBox.rect.width) - activeHurtBox.rendererBounds.x;
}
if (activeHurtBox.followYBounds) {
activeHurtBoxRectanglePosition.y = activeHurtBox.rendererBounds.y - (activeHurtBox.rect.height / 2);
activeHurtBoxRectanglePosition.height = (activeHurtBox.rendererBounds.height + activeHurtBox.rect.height) - activeHurtBox.rendererBounds.y;
}
dist = distancePointToRectangle(hurtBoxPosition, activeHurtBoxRectanglePosition);
if (hurtBox.radius >= dist) collisionConfirm = true;
/*if (collisionConfirm && !hurtBox.isBlock) {
Debug.Log("------------------");
Debug.Log(hurtBoxPosition);
Debug.Log(activeHurtBox.bodyPart + " - " + activeHurtBoxRectanglePosition);
Debug.Log("xMin/xMax,yMin/yMax : " + activeHurtBoxRectanglePosition.xMin + "/" + activeHurtBoxRectanglePosition.xMax + ", " + activeHurtBoxRectanglePosition.yMin + "/" + activeHurtBoxRectanglePosition.yMax);
Debug.Log(hurtBox.radius + " >= " + dist + " = " + collisionConfirm);
}*/
}
}else if (hurtBox.shape == HitBoxShape.rectangle){
/* Overlap doesn't work with negative width
Rect hurtBoxRectanglePosition = new Rect(hurtBox.rect);
hurtBoxRectanglePosition.x *= -mirror;
hurtBoxRectanglePosition.width *= -mirror;
hurtBoxRectanglePosition.x += hurtBoxPosition.x;
hurtBoxRectanglePosition.y += hurtBoxPosition.y;*/
float mirrorDiff = mirror < 0 ? hurtBox.rect.width : 0f;
Rect hurtBoxRectanglePosition = new Rect(((hurtBox.rect.x + mirrorDiff) * mirror) + hurtBoxPosition.x,
hurtBox.rect.y + hurtBoxPosition.y,
hurtBox.rect.width, hurtBox.rect.height);
if (activeHurtBox.shape == HitBoxShape.circle){
if (hurtBox.followXBounds){
hurtBoxRectanglePosition.x = hurtBox.rendererBounds.x - (hurtBox.rect.width/2);
hurtBoxRectanglePosition.width = (hurtBox.rendererBounds.width + hurtBox.rect.width) - hurtBox.rendererBounds.x;
}
if (hurtBox.followYBounds){
hurtBoxRectanglePosition.y = hurtBox.rendererBounds.y - (hurtBox.rect.height/2);
hurtBoxRectanglePosition.height = (hurtBox.rendererBounds.height + hurtBox.rect.height) - hurtBox.rendererBounds.y;
}
dist = distancePointToRectangle(activeHurtBoxPosition, hurtBoxRectanglePosition);
if (dist <= activeHurtBox.radius) collisionConfirm = true;
}else if (activeHurtBox.shape == HitBoxShape.rectangle){
/* Overlap doesn't work with negative width
Rect activeHurtBoxRectanglePosition = new Rect(activeHurtBox.rect);
activeHurtBoxRectanglePosition.x *= -mirror;
activeHurtBoxRectanglePosition.width *= -mirror;
activeHurtBoxRectanglePosition.x += activeHurtBoxPosition.x;
activeHurtBoxRectanglePosition.y += activeHurtBoxPosition.y;*/
mirrorDiff = mirror > 0 ? activeHurtBox.rect.width : 0f;
Rect activeHurtBoxRectanglePosition = new Rect(((activeHurtBox.rect.x + mirrorDiff) * - mirror) + activeHurtBoxPosition.x,
activeHurtBox.rect.y + activeHurtBoxPosition.y,
activeHurtBox.rect.width, activeHurtBox.rect.height);
if (activeHurtBox.followXBounds){
activeHurtBoxRectanglePosition.x = activeHurtBox.rendererBounds.x - (activeHurtBox.rect.width/2);
activeHurtBoxRectanglePosition.width = (activeHurtBox.rendererBounds.width + activeHurtBox.rect.width) - activeHurtBox.rendererBounds.x;
}
if (activeHurtBox.followYBounds){
activeHurtBoxRectanglePosition.y = activeHurtBox.rendererBounds.y - (activeHurtBox.rect.height/2);
activeHurtBoxRectanglePosition.height = (activeHurtBox.rendererBounds.height + activeHurtBox.rect.height) - activeHurtBox.rendererBounds.y;
}
if (hurtBox.followXBounds){
hurtBoxRectanglePosition.x = hurtBox.rendererBounds.x - (hurtBox.rect.width/2);
hurtBoxRectanglePosition.width = (hurtBox.rendererBounds.width + hurtBox.rect.width) - hurtBox.rendererBounds.x;
}
if (hurtBox.followYBounds){
hurtBoxRectanglePosition.y = hurtBox.rendererBounds.y - (hurtBox.rect.height/2);
hurtBoxRectanglePosition.height = (hurtBox.rendererBounds.height + hurtBox.rect.height) - hurtBox.rendererBounds.y;
}
if (hurtBoxRectanglePosition.Overlaps(activeHurtBoxRectanglePosition)) collisionConfirm = true;
}
}
if (collisionConfirm) {
activeHurtBox.state = 1;
return new Vector3[]{hurtBoxPosition, activeHurtBoxPosition, (hurtBoxPosition + activeHurtBoxPosition)/2};
}
}
}
foreach (HurtBox activeHurtBox in activeHurtBoxes) {
if (activeHurtBox.state == 1) activeHurtBox.state = 0;
}
return new Vector3[]{ };
}
Note that you have to add a "state" member in the HurtBox class at the top of the file we're currently editing
public int state{get;set;}
So as far as the moves, make your parry attempt that will have a hurt box covering your entire character with a Parry hit confirm type, make your move invincible during the active frames of the parry, so that if someone let the parry go a bit he can punish you for parrying for nothing. Set the parry confirm move as you would with a throw.
The rest is pretty much like setting up a normal throw with the confirm and the opponent override with the reaction move. There's just no tech to setup. When someone attacks your parry, the hurt boxes will clash and your parry will connect and do the confirm and reaction moves.
Makes sense? Let me know if you try it, works well for me so far.