// ENTITY BASE CLASS
public class Entity {
 
}
 
// COMPONENTS (INTERFACES)
public interface ITangibleEntity {
    Vector3 Position { get; set; }
    Vector3 Velocity { get; set; }
}
 
public interface IModelledEntity : ITangibleEntity {
    ModelReference Model { get; set; }
    float Scaling { get; set; }
 
    bool CameraCanSeeThis(Camera camera);
}
 
public interface IAudibleEntity : ITangibleEntity {
    SoundReference Sound { get; set; }
    float SoundRadius { get; set; }
 
    bool CameraCanHearThis(Camera camera);
}
 
// COMPONENTS (DEFAULT IMPLEMENTATIONS)
public class DefaultTangibleEntityImpl : ITangibleEntity {
    public Vector3 Position { get; set; }
    public Vector3 Velocity { get; set; }
}
 
public class DefaultModelledEntityImpl : IModelledEntity {
    private readonly DefaultTangibleEntityImpl tangibleImpl;
 
    public Vector3 Position {
        get { return tangibleImpl.Position; }
        set { tangibleImpl.Position = value; }
    }
 
    public Vector3 Velocity {
        get { return tangibleImpl.Position; }
        set { tangibleImpl.Velocity = value; }
    }
 
    public DefaultModelledEntityImpl(DefaultTangibleEntityImpl tangibleImpl) {
        this.tangibleImpl = tangibleImpl;
    }
 
    public ModelReference Model { get; set; }
    public float Scaling { get; set; }
 
    public bool CameraCanSeeThis(Camera camera) {
        return !PhysicsSystem.RayTest(camera.Position, Position).IsObscured;
    }
}
 
public class DefaultAudibleEntityImpl : IAudibleEntity {
    private readonly DefaultTangibleEntityImpl tangibleImpl;
 
    public Vector3 Position {
        get { return tangibleImpl.Position; }
        set { tangibleImpl.Position = value; }
    }
 
    public Vector3 Velocity {
        get { return tangibleImpl.Position; }
        set { tangibleImpl.Velocity = value; }
    }
 
    public DefaultAudibleEntityImpl(DefaultTangibleEntityImpl tangibleImpl) {
        this.tangibleImpl = tangibleImpl;
    }
 
    public SoundReference Sound { get; set; }
    public float SoundRadius { get; set; }
 
    public bool CameraCanHearThis(Camera camera) {
        return SoundRadius > Vector3.Distance(camera.Position, Position);
    }
}
 
// ENTITIES
public class ArmourPowerupEntity : Entity, IModelledEntity {
    private const float DEFAULT_ARMOUR_SCALING = 1f;
    private readonly DefaultTangibleEntityImpl tangibleImpl;
    private readonly DefaultModelledEntityImpl modelledImpl;
 
    public ModelReference Model {
        get { return modelledImpl.Model; }
        set { modelledImpl.Model = value; }
    }
    public float Scaling {
        get { return modelledImpl.Scaling; }
        set { modelledImpl.Scaling = value; }
    }
    public Vector3 Position {
        get { return tangibleImpl.Position; }
        set { tangibleImpl.Position = value; }
    }
    public Vector3 Velocity {
        get { return tangibleImpl.Velocity; }
        set { tangibleImpl.Velocity = value; }
    }
 
    public ArmourPowerupEntity(Vector3 powerupPosition)
        : this(powerupPosition, DEFAULT_ARMOUR_SCALING) { }
 
    public ArmourPowerupEntity(Vector3 powerupPosition, float customScaling) {
        tangibleImpl = new DefaultTangibleEntityImpl();
        modelledImpl = new DefaultModelledEntityImpl(tangibleImpl);
        Model = ModelDatabase.ArmourModel;
        Position = powerupPosition;
        Scaling = customScaling;
    }
 
    public bool CameraCanSeeThis(Camera camera) {
        return modelledImpl.CameraCanSeeThis(camera);
    }
}
 
public class RocketProjectileEntity : Entity, IModelledEntity, IAudibleEntity {
    private readonly DefaultTangibleEntityImpl tangibleImpl;
    private readonly DefaultModelledEntityImpl modelledImpl;
    private readonly DefaultAudibleEntityImpl audibleImpl;
 
    public ModelReference Model {
        get { return modelledImpl.Model; }
        set { modelledImpl.Model = value; }
    }
    public float Scaling {
        get { return modelledImpl.Scaling; }
        set { modelledImpl.Scaling = value; }
    }
    public SoundReference Sound {
        get { return audibleImpl.Sound; }
        set { audibleImpl.Sound = value; }
    }
    public float SoundRadius {
        get { return audibleImpl.SoundRadius; }
        set { audibleImpl.SoundRadius = value; }
    }
    public Vector3 Position {
        get { return tangibleImpl.Position; }
        set { tangibleImpl.Position = value; }
    }
    public Vector3 Velocity {
        get { return tangibleImpl.Velocity; }
        set { tangibleImpl.Velocity = value; }
    }
 
    public RocketProjectileEntity(Vector3 firingPoint, Vector3 firingVelocity, bool isBigRocket) {
        tangibleImpl = new DefaultTangibleEntityImpl();
        modelledImpl = new DefaultModelledEntityImpl(tangibleImpl);
        audibleImpl = new DefaultAudibleEntityImpl(tangibleImpl);
        Model = ModelDatabase.RocketModel;
        Position = firingPoint;
        Velocity = firingVelocity;
        Scaling = isBigRocket ? 2f : 1f;
        Sound = SoundDatabase.RocketSound;
        SoundRadius = 1000f;
    }
 
    public bool CameraCanSeeThis(Camera camera) {
        return modelledImpl.CameraCanSeeThis(camera);
    }
 
    public bool CameraCanHearThis(Camera camera) {
        return audibleImpl.CameraCanHearThis(camera);
    }
}