// Path: src/main/java/com/simsilica/es/common/Decay.java
// public class Decay implements EntityComponent, PersistentComponent {
//     private long startTime;
//     private long endTime;
//     
//     public Decay() {
//     }
//  
//     /**
//      *  Creates a decay component with a start/end time that can
//      *  provide things like time remaining and percent remaining.
//      */
//     public Decay( long startTime, long endTime ) {
//         this.startTime = startTime;
//         this.endTime = endTime;
//     }
// 
//     /**
//      *  Creates a decay component with only an end time.  getPercentRemaining()
//      *  will not work sensibly in this case.
//      */
//     public Decay( long endTime ) {
//         this.startTime = endTime;
//         this.endTime = endTime;
//     }
//     
//     public static Decay duration( long startTime, long duration ) {
//         return new Decay(startTime, startTime + duration);
//     }
//     
//     public long getStartTime() {
//         return startTime;
//     }
//     
//     public long getEndTime() {
//         return endTime;
//     }
//     
//     public long getTimeRemaining( long time ) {
//         return Math.max(0, endTime - time);
//     }
//     
//     public boolean isDead( long time ) {
//         return time >= endTime; 
//     }
//     
//     public double getPercentRemaining( long time ) {
//         if( time >= endTime ) {
//             return 0;
//         }
//         if( startTime == endTime ) {
//             return 1;
//         }
//         long remain = endTime - time;        
//         double total = endTime - startTime;
//         return remain / total; 
//     }
//  
//     @Override   
//     public String toString() {
//         return "Decay[startTime=" + startTime + ", endTime=" + endTime + "]";
//     } 
// }


import com.simsilica.es.*;
import com.simsilica.es.common.Decay;
import com.jme3.bullet.collision.*;
import com.jme3.math.*;

            // Calculate the energy of the collision
            Vector3f v1 = (object1 instanceof EntityRigidBody) ? ((EntityRigidBody)object1).getLastVelocity() : Vector3f.ZERO;         
            Vector3f v2 = (object2 instanceof EntityRigidBody) ? ((EntityRigidBody)object2).getLastVelocity() : Vector3f.ZERO;         
 
            // Normal is always pointing towards object1 and away from object2...  because
            // normal comes from getNormalWorldOnB().
            float dot1 = -normal.dot(v1);
            float dot2 = normal.dot(v2);
            energy = dot1 + dot2;
        }

        return Contact.create(object1, object2, wp, normal, energy);
    }

    @Override
    public void collision( EntityPhysicsObject object1, EntityPhysicsObject object2, PhysicsCollisionEvent event ) {
        //System.out.println("collision:" + object1 + " -> " + object2); 

        Contact c = createContact(object1, object2, event);
        createEntity(c);
    }
 
    /**
     *  Called by the collision method to create the contact entity and populate its Contact
     *  and Decay components.  Can be overridden by subclasses as needed to have different behavior.
     *  The default behavior sets a Decay component with no time so it will be removed when the
     *  decay system first detects it.
     */   
    protected EntityId createEntity( Contact c ) {
        EntityId contactEntity = ed.createEntity();

// Path: src/main/java/com/simsilica/es/common/Decay.java
// public class Decay implements EntityComponent, PersistentComponent {
//     private long startTime;
//     private long endTime;
//     
//     public Decay() {
//     }
//  
//     /**
//      *  Creates a decay component with a start/end time that can
//      *  provide things like time remaining and percent remaining.
//      */
//     public Decay( long startTime, long endTime ) {
//         this.startTime = startTime;
//         this.endTime = endTime;
//     }
// 
//     /**
//      *  Creates a decay component with only an end time.  getPercentRemaining()
//      *  will not work sensibly in this case.
//      */
//     public Decay( long endTime ) {
//         this.startTime = endTime;
//         this.endTime = endTime;
//     }
//     
//     public static Decay duration( long startTime, long duration ) {
//         return new Decay(startTime, startTime + duration);
//     }
//     
//     public long getStartTime() {
//         return startTime;
//     }
//     
//     public long getEndTime() {
//         return endTime;
//     }
//     
//     public long getTimeRemaining( long time ) {
//         return Math.max(0, endTime - time);
//     }
//     
//     public boolean isDead( long time ) {
//         return time >= endTime; 
//     }
//     
//     public double getPercentRemaining( long time ) {
//         if( time >= endTime ) {
//             return 0;
//         }
//         if( startTime == endTime ) {
//             return 1;
//         }
//         long remain = endTime - time;        
//         double total = endTime - startTime;
//         return remain / total; 
//     }
//  
//     @Override   
//     public String toString() {
//         return "Decay[startTime=" + startTime + ", endTime=" + endTime + "]";
//     } 
// }


// Path: extensions/bullet/src/main/java/com/simsilica/bullet/DefaultContactPublisher.java
import com.simsilica.es.*;
import com.simsilica.es.common.Decay;
import com.jme3.bullet.collision.*;
import com.jme3.math.*;

            // Calculate the energy of the collision
            Vector3f v1 = (object1 instanceof EntityRigidBody) ? ((EntityRigidBody)object1).getLastVelocity() : Vector3f.ZERO;         
            Vector3f v2 = (object2 instanceof EntityRigidBody) ? ((EntityRigidBody)object2).getLastVelocity() : Vector3f.ZERO;         
 
            // Normal is always pointing towards object1 and away from object2...  because
            // normal comes from getNormalWorldOnB().
            float dot1 = -normal.dot(v1);
            float dot2 = normal.dot(v2);
            energy = dot1 + dot2;
        }

        return Contact.create(object1, object2, wp, normal, energy);
    }

    @Override
    public void collision( EntityPhysicsObject object1, EntityPhysicsObject object2, PhysicsCollisionEvent event ) {
        //System.out.println("collision:" + object1 + " -> " + object2); 

        Contact c = createContact(object1, object2, event);
        createEntity(c);
    }
 
    /**
     *  Called by the collision method to create the contact entity and populate its Contact
     *  and Decay components.  Can be overridden by subclasses as needed to have different behavior.
     *  The default behavior sets a Decay component with no time so it will be removed when the
     *  decay system first detects it.
     */   
    protected EntityId createEntity( Contact c ) {
        EntityId contactEntity = ed.createEntity();

        ed.setComponents(contactEntity, c, new Decay());
