private void DoWalkCycleOffsets( float armLength, ref Vector3 rightHand, ref Vector3 leftHand, ref List <float> shoulderAngle, ref List <float> handSwingAngle, ref JointLister shoulderPos, bool carrying, SimpleCurve cycleHandsSwingAngle, float offsetJoint) { // Has the pawn something in his hands? if (carrying) { return; } Rot4 rot = this.BodyFacing; // Basic values if pawn is carrying stuff float x = 0; float x2 = -x; float y = Offsets.YOffset_Behind; float y2 = y; float z; float z2; // Offsets for hands from the pawn center z = z2 = -armLength; if (rot.IsHorizontal) { x = x2 = 0f; if (rot == Rot4.East) { y2 = -0.5f; } else { y = -0.05f; } } else if (rot == Rot4.North) { y = y2 = -0.02f; x *= -1; x2 *= -1; } // Swing the hands, try complete the cycle if (this.CompAnimator.IsMoving) { WalkCycleDef walkCycle = this.CompAnimator.WalkCycle; float percent = this.CompAnimator.MovedPercent; if (rot.IsHorizontal) { float lookie = rot == Rot4.West ? -1f : 1f; float f = lookie * offsetJoint; shoulderAngle[0] = shoulderAngle[1] = lookie * walkCycle?.shoulderAngle ?? 0f; shoulderPos.RightJoint.x += f; shoulderPos.LeftJoint.x += f; handSwingAngle[0] = handSwingAngle[1] = (rot == Rot4.West ? -1 : 1) * cycleHandsSwingAngle.Evaluate(percent); } else { z += cycleHandsSwingAngle.Evaluate(percent) / 500; z2 -= cycleHandsSwingAngle.Evaluate(percent) / 500; z += walkCycle?.shoulderAngle / 800 ?? 0f; z2 += walkCycle?.shoulderAngle / 800 ?? 0f; } } if (MainTabWindow_BaseAnimator.Panic || this.Pawn.Fleeing() || this.Pawn.IsBurning()) { float offset = 1f + armLength; x *= offset; z *= offset; x2 *= offset; z2 *= offset; handSwingAngle[0] += 180f; handSwingAngle[1] += 180f; shoulderAngle[0] = shoulderAngle[1] = 0f; } rightHand = new Vector3(x, y, z); leftHand = new Vector3(x2, y2, z2); }
public override void DrawHands(Quaternion bodyQuat, Vector3 drawPos, bool portrait, Thing carriedThing = null, bool flip = false) { if (this.Pawn.Dead || this.Pawn.Downed) { return; } if (portrait && !HarmonyPatchesFS.AnimatorIsOpen()) { return; } if (!this.CompAnimator.Props.bipedWithHands) { return; } // return if hands already drawn on carrything bool carrying = carriedThing != null; if (this.CarryStuff() && !carrying) { return; } if (carrying) { this.ApplyEquipmentWobble(ref drawPos); carriedThing.DrawAt(drawPos, flip); } BodyAnimDef body = this.CompAnimator.BodyAnim; Rot4 rot = this.BodyFacing; if (body == null) { return; } JointLister shoulperPos = this.GetJointPositions(JointType.Shoulder, body.shoulderOffsets[rot.AsInt], body.shoulderOffsets[Rot4.North.AsInt].x, carrying, this.Pawn.ShowWeaponOpenly()); List <float> handSwingAngle = new List <float> { 0f, 0f }; List <float> shoulderAngle = new List <float> { 0f, 0f }; Vector3 rightHand = Vector3.zero; Vector3 leftHand = Vector3.zero; WalkCycleDef walkCycle = this.CompAnimator.WalkCycle; PoseCycleDef poseCycle = this.CompAnimator.PoseCycle; if (walkCycle != null) { float offsetJoint = walkCycle.ShoulderOffsetHorizontalX.Evaluate(this.CompAnimator.MovedPercent); this.DoWalkCycleOffsets( body.armLength, ref rightHand, ref leftHand, ref shoulderAngle, ref handSwingAngle, ref shoulperPos, carrying, walkCycle.HandsSwingAngle, offsetJoint); } if (poseCycle != null) { this.DoPoseCycleOffsets(ref rightHand, ref shoulderAngle, ref handSwingAngle, poseCycle); } this.DoAttackAnimationHandOffsets(ref handSwingAngle, ref rightHand, false); this.GetBipedMesh(out Mesh handMeshRight, out Mesh handMeshLeft); Material matLeft = this.LeftHandMat; Material matRight = this.RightHandMat; if (MainTabWindow_BaseAnimator.Colored) { matLeft = this.CompAnimator.PawnBodyGraphic?.HandGraphicLeftCol?.MatSingle; matRight = this.CompAnimator.PawnBodyGraphic?.HandGraphicRightCol?.MatSingle; } else if (carriedThing == null) { switch (rot.AsInt) { case 1: matLeft = this.LeftHandShadowMat; break; case 3: matRight = this.RightHandShadowMat; break; } } bool drawLeft = matLeft != null && this.CompAnimator.BodyStat.HandLeft != PartStatus.Missing; bool drawRight = matRight != null && this.CompAnimator.BodyStat.HandRight != PartStatus.Missing; if (drawLeft) { Quaternion quat; Vector3 position; bool noTween = false; if (!this.CompAnimator.IsMoving && this.CompAnimator.HasLeftHandPosition) { position = this.CompAnimator.SecondHandPosition; quat = this.CompAnimator.WeaponQuat; noTween = true; } else { shoulperPos.LeftJoint = bodyQuat * shoulperPos.LeftJoint; leftHand = bodyQuat * leftHand.RotatedBy(-handSwingAngle[0] - shoulderAngle[0]); position = drawPos + shoulperPos.LeftJoint + leftHand; quat = bodyQuat * Quaternion.AngleAxis(-handSwingAngle[0], Vector3.up); } TweenThing handLeft = TweenThing.HandLeft; this.DrawTweenedHand(position, handMeshLeft, matLeft, quat, handLeft, portrait, noTween); //GenDraw.DrawMeshNowOrLater( // handMeshLeft, position, // quat, // matLeft, // portrait); } if (drawRight) { Quaternion quat; Vector3 position; bool noTween = false; if (this.CompAnimator.FirstHandPosition != Vector3.zero) { quat = this.CompAnimator.WeaponQuat; position = this.CompAnimator.FirstHandPosition; noTween = true; } else { shoulperPos.RightJoint = bodyQuat * shoulperPos.RightJoint; rightHand = bodyQuat * rightHand.RotatedBy(handSwingAngle[1] - shoulderAngle[1]); position = drawPos + shoulperPos.RightJoint + rightHand; quat = bodyQuat * Quaternion.AngleAxis(handSwingAngle[1], Vector3.up); } TweenThing handRight = TweenThing.HandRight; this.DrawTweenedHand(position, handMeshRight, matRight, quat, handRight, portrait, noTween); // GenDraw.DrawMeshNowOrLater( // handMeshRight, position, // quat, // matRight, // portrait); } if (MainTabWindow_BaseAnimator.Develop) { // for debug Material centerMat = GraphicDatabase.Get <Graphic_Single>( "Hands/Human_Hand_dev", ShaderDatabase.CutoutSkin, Vector2.one, Color.white).MatSingle; GenDraw.DrawMeshNowOrLater( handMeshLeft, drawPos + shoulperPos.LeftJoint + new Vector3(0, -0.301f, 0), bodyQuat * Quaternion.AngleAxis(-shoulderAngle[0], Vector3.up), centerMat, portrait); GenDraw.DrawMeshNowOrLater( handMeshRight, drawPos + shoulperPos.RightJoint + new Vector3(0, 0.301f, 0), bodyQuat * Quaternion.AngleAxis(-shoulderAngle[1], Vector3.up), centerMat, portrait); } }
public override void DrawFeet(Quaternion bodyQuat, Quaternion footQuat, Vector3 rootLoc, bool portrait) { if (this.Pawn.Dead || this.Pawn.Downed) { return; } /// No feet while sitting at a table Job curJob = this.Pawn.CurJob; if (curJob != null) { if (curJob.def == JobDefOf.Ingest && !this.Pawn.Rotation.IsHorizontal) { if (curJob.targetB.IsValid) { for (int i = 0; i < 4; i++) { Rot4 rotty = new Rot4(i); IntVec3 intVec = this.Pawn.Position + rotty.FacingCell; if (intVec == curJob.targetB) { return; } } } } } if (portrait && !HarmonyPatchesFS.AnimatorIsOpen()) { return; } Quaternion drawQuat = this.CompAnimator.IsMoving ? footQuat : bodyQuat; Rot4 rot = this.BodyFacing; // Basic values BodyAnimDef body = this.CompAnimator.BodyAnim; if (body == null) { return; } JointLister groundPos = this.GetJointPositions(JointType.Hip, body.hipOffsets[rot.AsInt], body.hipOffsets[Rot4.North.AsInt].x); Vector3 rightFootCycle = Vector3.zero; Vector3 leftFootCycle = Vector3.zero; float footAngleRight = 0; float footAngleLeft = 0; float offsetJoint = 0; WalkCycleDef cycle = this.CompAnimator.WalkCycle; if (cycle != null) { offsetJoint = cycle.HipOffsetHorizontalX.Evaluate(this.CompAnimator.MovedPercent); this.DoWalkCycleOffsets( ref rightFootCycle, ref leftFootCycle, ref footAngleRight, ref footAngleLeft, ref offsetJoint, cycle.FootPositionX, cycle.FootPositionZ, cycle.FootAngle); } this.GetBipedMesh(out Mesh footMeshRight, out Mesh footMeshLeft); Material matRight; Material matLeft; if (MainTabWindow_BaseAnimator.Colored) { matRight = this.CompAnimator.PawnBodyGraphic?.FootGraphicRightCol?.MatAt(rot); matLeft = this.CompAnimator.PawnBodyGraphic?.FootGraphicLeftCol?.MatAt(rot); } else { Material rightFoot = this.CompAnimator.PawnBodyGraphic?.FootGraphicRight?.MatAt(rot); Material leftFoot = this.CompAnimator.PawnBodyGraphic?.FootGraphicLeft?.MatAt(rot); Material leftShadow = this.CompAnimator.PawnBodyGraphic?.FootGraphicLeftShadow?.MatAt(rot); Material rightShadow = this.CompAnimator.PawnBodyGraphic?.FootGraphicRightShadow?.MatAt(rot); switch (rot.AsInt) { default: matRight = this.Flasher.GetDamagedMat(rightFoot); matLeft = this.Flasher.GetDamagedMat(leftFoot); break; case 1: matRight = this.Flasher.GetDamagedMat(rightFoot); matLeft = this.Flasher.GetDamagedMat(leftShadow); break; case 3: matRight = this.Flasher.GetDamagedMat(rightShadow); matLeft = this.Flasher.GetDamagedMat(leftFoot); break; } } bool drawRight = matRight != null && this.CompAnimator.BodyStat.FootRight != PartStatus.Missing; bool drawLeft = matLeft != null && this.CompAnimator.BodyStat.FootLeft != PartStatus.Missing; groundPos.LeftJoint = drawQuat * groundPos.LeftJoint; groundPos.RightJoint = drawQuat * groundPos.RightJoint; leftFootCycle = drawQuat * leftFootCycle; rightFootCycle = drawQuat * rightFootCycle; Vector3 ground = rootLoc + drawQuat * new Vector3(0, 0, OffsetGroundZ); if (drawLeft) { // TweenThing leftFoot = TweenThing.FootLeft; // PawnPartsTweener tweener = this.CompAnimator.PartTweener; // if (tweener != null) { Vector3 position = ground + groundPos.LeftJoint + leftFootCycle; // tweener.PartPositions[(int)leftFoot] = position; // tweener.PreThingPosCalculation(leftFoot, spring: SpringTightness.Stff); GenDraw.DrawMeshNowOrLater( footMeshLeft, position, // tweener.TweenedPartsPos[(int)leftFoot], drawQuat * Quaternion.AngleAxis(footAngleLeft, Vector3.up), matLeft, portrait); } } if (drawRight) { // TweenThing rightFoot = TweenThing.FootRight; // PawnPartsTweener tweener = this.CompAnimator.PartTweener; // if (tweener != null) // { Vector3 position = ground + groundPos.RightJoint + rightFootCycle; // tweener.PartPositions[(int)rightFoot] = position; // tweener.PreThingPosCalculation(rightFoot, spring: SpringTightness.Stff); GenDraw.DrawMeshNowOrLater( footMeshRight, position, // tweener.TweenedPartsPos[(int)rightFoot], drawQuat * Quaternion.AngleAxis(footAngleRight, Vector3.up), matRight, portrait); // } } if (MainTabWindow_BaseAnimator.Develop) { // for debug Material centerMat = GraphicDatabase .Get <Graphic_Single>("Hands/Ground", ShaderDatabase.Transparent, Vector2.one, Color.red).MatSingle; GenDraw.DrawMeshNowOrLater( footMeshLeft, ground + groundPos.LeftJoint + new Vector3(offsetJoint, -0.301f, 0), drawQuat * Quaternion.AngleAxis(0, Vector3.up), centerMat, portrait); GenDraw.DrawMeshNowOrLater( footMeshRight, ground + groundPos.RightJoint + new Vector3(offsetJoint, 0.301f, 0), drawQuat * Quaternion.AngleAxis(0, Vector3.up), centerMat, portrait); Material hipMat = GraphicDatabase .Get <Graphic_Single>("Hands/Human_Hand_dev", ShaderDatabase.Transparent, Vector2.one, Color.blue).MatSingle; GenDraw.DrawMeshNowOrLater( footMeshLeft, groundPos.LeftJoint + new Vector3(offsetJoint, -0.301f, 0), drawQuat * Quaternion.AngleAxis(0, Vector3.up), centerMat, portrait); // UnityEngine.Graphics.DrawMesh(handsMesh, center + new Vector3(0, 0.301f, z), // Quaternion.AngleAxis(0, Vector3.up), centerMat, 0); // UnityEngine.Graphics.DrawMesh(handsMesh, center + new Vector3(0, 0.301f, z2), // Quaternion.AngleAxis(0, Vector3.up), centerMat, 0); } }
protected JointLister GetJointPositions(JointType jointType, Vector3 offsets, float jointWidth, bool carrying = false, bool armed = false) { Rot4 rot = this.BodyFacing; JointLister joints = new JointLister { jointType = jointType }; float leftX = offsets.x; float rightX = offsets.x; float leftZ = offsets.z; float rightZ = offsets.z; float offsetY = Offsets.YOffset_HandsFeetOver; float leftY = offsetY; bool offsetsCarrying = false; switch (jointType) { case JointType.Shoulder: offsetY = armed ? -Offsets.YOffset_HandsFeet : Offsets.YOffset_HandsFeetOver; leftY = this.CompAnimator.IsMoving ? Offsets.YOffset_HandsFeetOver : offsetY; if (carrying) { offsetsCarrying = true; } break; } float rightY = offsetY; if (offsetsCarrying) { leftX = -jointWidth / 2; rightX = jointWidth / 2; leftZ = -0.025f; rightZ = -leftZ; } else if (rot.IsHorizontal) { float offsetX = jointWidth * 0.1f; float offsetZ = jointWidth * 0.2f; if (rot == Rot4.East) { leftY = -Offsets.YOffset_Behind; leftZ += +offsetZ; } else { rightY = -Offsets.YOffset_Behind; rightZ += offsetZ; } leftX += offsetX; rightX -= offsetX; } else { leftX = -rightX; } if (rot == Rot4.North) { leftY = rightY = -Offsets.YOffset_Behind; // leftX *= -1; // rightX *= -1; } joints.RightJoint = new Vector3(rightX, rightY, rightZ); joints.LeftJoint = new Vector3(leftX, leftY, leftZ); return(joints); }
protected virtual void DrawFrontPaws(Quaternion bodyQuat, Quaternion footQuat, Vector3 rootLoc, bool portrait) { if (!this.CompAnimator.Props.quadruped) { return; } // Basic values BodyAnimDef body = this.CompAnimator.BodyAnim; Rot4 rot = this.BodyFacing; if (body == null) { return; } JointLister jointPositions = this.GetJointPositions(JointType.Shoulder, body.shoulderOffsets[rot.AsInt], body.shoulderOffsets[Rot4.North.AsInt].x); Vector3 rightFootAnim = Vector3.zero; Vector3 leftFootAnim = Vector3.zero; float footAngleRight = 0f; float footAngleLeft = 0f; float offsetJoint = 0; WalkCycleDef cycle = this.CompAnimator.WalkCycle; if (cycle != null) { offsetJoint = cycle.ShoulderOffsetHorizontalX.Evaluate(this.CompAnimator.MovedPercent); // Center = drawpos of carryThing this.DoWalkCycleOffsets( ref rightFootAnim, ref leftFootAnim, ref footAngleRight, ref footAngleLeft, ref offsetJoint, cycle.FrontPawPositionX, cycle.FrontPawPositionZ, cycle.FrontPawAngle); } this.GetBipedMesh(out Mesh footMeshRight, out Mesh footMeshLeft); Material matLeft; Material matRight; if (MainTabWindow_BaseAnimator.Colored) { matRight = this.CompAnimator.PawnBodyGraphic?.FrontPawGraphicRightCol?.MatAt(rot); matLeft = this.CompAnimator.PawnBodyGraphic?.FrontPawGraphicLeftCol?.MatAt(rot); } else { switch (rot.AsInt) { default: matRight = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic?.FrontPawGraphicRight ?.MatAt(rot)); matLeft = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic?.FrontPawGraphicLeft ?.MatAt(rot)); break; case 1: matRight = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic?.FrontPawGraphicRight ?.MatAt(rot)); matLeft = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic ?.FrontPawGraphicLeftShadow?.MatAt(rot)); break; case 3: matRight = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic ?.FrontPawGraphicRightShadow?.MatAt(rot)); matLeft = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic?.FrontPawGraphicLeft ?.MatAt(rot)); break; } } Quaternion drawQuat = this.CompAnimator.IsMoving ? footQuat : bodyQuat; Vector3 ground = rootLoc + drawQuat * new Vector3(0, 0, OffsetGroundZ); if (matLeft != null) { if (this.CompAnimator.BodyStat.FootLeft != PartStatus.Missing) { GenDraw.DrawMeshNowOrLater( footMeshLeft, ground + jointPositions.LeftJoint + leftFootAnim, drawQuat * Quaternion.AngleAxis(footAngleLeft, Vector3.up), matLeft, portrait); } } if (matRight != null) { if (this.CompAnimator.BodyStat.FootRight != PartStatus.Missing) { GenDraw.DrawMeshNowOrLater( footMeshRight, ground + jointPositions.RightJoint + rightFootAnim, drawQuat * Quaternion.AngleAxis(footAngleRight, Vector3.up), matRight, portrait); } } if (MainTabWindow_BaseAnimator.Develop) { // for debug Material centerMat = GraphicDatabase .Get <Graphic_Single>("Hands/Ground", ShaderDatabase.Transparent, Vector2.one, Color.cyan).MatSingle; GenDraw.DrawMeshNowOrLater( footMeshLeft, ground + jointPositions.LeftJoint + new Vector3(offsetJoint, 0.301f, 0), drawQuat * Quaternion.AngleAxis(0, Vector3.up), centerMat, portrait); GenDraw.DrawMeshNowOrLater( footMeshRight, ground + jointPositions.RightJoint + new Vector3(offsetJoint, 0.301f, 0), drawQuat * Quaternion.AngleAxis(0, Vector3.up), centerMat, portrait); // UnityEngine.Graphics.DrawMesh(handsMesh, center + new Vector3(0, 0.301f, z), // Quaternion.AngleAxis(0, Vector3.up), centerMat, 0); // UnityEngine.Graphics.DrawMesh(handsMesh, center + new Vector3(0, 0.301f, z2), // Quaternion.AngleAxis(0, Vector3.up), centerMat, 0); } }
public override void DrawFeet(Vector3 rootLoc, bool portrait) { if (portrait && !this.CompAnimator.AnimatorOpen) { return; } int legsCount = 4; float legSpan = 1f; Vector3 ground = rootLoc; ground.z += OffsetGround; Rot4 rot = this.BodyFacing; // Basic values BodyAnimDef body = this.CompAnimator.BodyAnim; JointLister groundPos = this.GetJointPositions( body.hipOffsets[rot.AsInt], body.hipOffsets[Rot4.North.AsInt].x); WalkCycleDef cycle = this.CompAnimator.WalkCycle; Vector3 rightFootAnim = Vector3.zero; Vector3 leftFootAnim = Vector3.zero; float footAngleRight = 0; float footAngleLeft = 0; float offsetJoint = 0; if (cycle != null) { offsetJoint = cycle.HipOffsetHorizontalX.Evaluate(this.MovedPercent); this.DoWalkCycleOffsets( ref rightFootAnim, ref leftFootAnim, ref footAngleRight, ref footAngleLeft, ref offsetJoint, cycle.FootPositionX, cycle.FootPositionZ, cycle.FootAngle); } this.GetMeshesFoot(out Mesh footMeshRight, out Mesh footMeshLeft); float bodyAngle = 0f; Pawn pawn = this.Pawn; if (pawn.Downed || pawn.Dead) { bodyAngle = pawn.Drawer.renderer.wiggler.downedAngle; } Material matRight; Material matLeft; if (!MainTabWindow_Animator.Colored) { switch (rot.AsInt) { default: matRight = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic?.FootGraphicRight ?.MatAt(rot)); matLeft = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic?.FootGraphicLeft ?.MatAt(rot)); break; case 1: matRight = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic?.FootGraphicRight ?.MatAt(rot)); matLeft = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic ?.FootGraphicLeftShadow ?.MatAt(rot)); break; case 3: matRight = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic ?.FootGraphicRightShadow ?.MatAt(rot)); matLeft = this.Flasher.GetDamagedMat(this.CompAnimator.PawnBodyGraphic?.FootGraphicLeft ?.MatAt(rot)); break; } } else { matRight = this.CompAnimator.PawnBodyGraphic?.FootGraphicRightCol?.MatAt(rot); matLeft = this.CompAnimator.PawnBodyGraphic?.FootGraphicLeftCol?.MatAt(rot); } bool drawRight = matRight != null && this.CompAnimator.BodyStat.FootRight != PartStatus.Missing; bool drawLeft = matLeft != null && this.CompAnimator.BodyStat.FootLeft != PartStatus.Missing; if (drawLeft) { GenDraw.DrawMeshNowOrLater( footMeshLeft, (ground + groundPos.LeftJoint + leftFootAnim) .RotatedBy(bodyAngle), Quaternion.AngleAxis(bodyAngle + footAngleLeft, Vector3.up), matLeft, portrait); } if (drawRight) { GenDraw.DrawMeshNowOrLater( footMeshRight, (ground + groundPos.RightJoint + rightFootAnim) .RotatedBy(bodyAngle), Quaternion.AngleAxis(bodyAngle + footAngleRight, Vector3.up), matRight, portrait); } if (MainTabWindow_Animator.Develop) { // for debug Material centerMat = GraphicDatabase .Get <Graphic_Single>("Hands/Ground", ShaderDatabase.Transparent, Vector2.one, Color.red).MatSingle; GenDraw.DrawMeshNowOrLater( footMeshLeft, ground.RotatedBy(bodyAngle) + groundPos.LeftJoint + new Vector3(offsetJoint, -0.301f, 0), Quaternion.AngleAxis(0, Vector3.up), centerMat, portrait); GenDraw.DrawMeshNowOrLater( footMeshRight, ground.RotatedBy(bodyAngle) + groundPos.RightJoint + new Vector3(offsetJoint, 0.301f, 0), Quaternion.AngleAxis(0, Vector3.up), centerMat, portrait); // UnityEngine.Graphics.DrawMesh(handsMesh, center + new Vector3(0, 0.301f, z), // Quaternion.AngleAxis(0, Vector3.up), centerMat, 0); // UnityEngine.Graphics.DrawMesh(handsMesh, center + new Vector3(0, 0.301f, z2), // Quaternion.AngleAxis(0, Vector3.up), centerMat, 0); } }