import { vec2 } from 'gl-matrix'; import { Circle, clamp, GameObject, settings } from 'shared'; import { Physical } from '../physics/physical'; import { BoundingBox } from '../physics/bounding-boxes/bounding-box'; import { BoundingBoxBase } from '../physics/bounding-boxes/bounding-box-base'; export class CirclePhysical implements Circle, Physical { readonly isInverted = false; readonly canCollide = true; readonly canMove = true; private _isAirborne = true; private velocity = vec2.create(); public get isAirborne(): boolean { return this._isAirborne; } private _boundingBox: BoundingBox; constructor(private _center: vec2, private _radius: number, private owner: GameObject) { this._boundingBox = new BoundingBox(null); this.recalculateBoundingBox(); } public get boundingBox(): BoundingBoxBase { return this._boundingBox; } public get center(): vec2 { return this._center; } public set center(value: vec2) { this._center = value; this.recalculateBoundingBox(); } public get gameObject(): GameObject { return this.owner; } public get radius(): number { return this._radius; } public set radius(value: number) { this._radius = value; this.recalculateBoundingBox(); } public distance(target: vec2): number { return vec2.distance(target, this.center) - this.radius; } public distanceBetween(target: CirclePhysical): number { return vec2.distance(target.center, this.center) - this.radius - target.radius; } public areIntersecting(other: CirclePhysical): boolean { return other.distance(this.center) < this.radius; } public isInside(other: CirclePhysical): boolean { return other.distance(this.center) < -this.radius; } public getPerimeterPoints(count: number): Array { const result: Array = []; for (let i = 0; i < count; i++) { result.push( vec2.fromValues( Math.cos((2 * Math.PI * i) / count) * this.radius + this.center.x, Math.sin((2 * Math.PI * i) / count) * this.radius + this.center.y ) ); } return result; } private recalculateBoundingBox() { this._boundingBox.xMin = this.center.x - this._radius; this._boundingBox.xMax = this.center.x + this._radius; this._boundingBox.yMin = this.center.y - this._radius; this._boundingBox.yMax = this.center.y + this._radius; } public applyForce(force: vec2, timeInMilliseconds: number) { vec2.add( this.velocity, this.velocity, vec2.scale(vec2.create(), force, timeInMilliseconds) ); vec2.set( this.velocity, clamp(this.velocity.x, -settings.maxVelocityX, settings.maxVelocityX), clamp(this.velocity.y, -settings.maxVelocityY, settings.maxVelocityY) ); } public resetVelocity() { this.velocity = vec2.create(); } /*public step(timeInMilliseconds: number): boolean { vec2.scale( this.velocity, this.velocity, Math.pow(settings.velocityAttenuation, timeInMilliseconds) ); const distance = vec2.scale(vec2.create(), this.velocity, timeInMilliseconds); const distanceLength = vec2.length(distance); const stepCount = Math.ceil(distanceLength / settings.physicsMaxStep); vec2.scale(distance, distance, 1 / stepCount); let wasHit = false; for (let i = 0; i < stepCount; i++) { const { normal, tangent, hitSurface } = this.physics.tryMovingDynamicCircle( this, distance ); if (hitSurface) { vec2.scale(this.velocity, tangent, vec2.dot(tangent, this.velocity)); wasHit = true; } this._isAirborne = !( hitSurface && vec2.dot(normal, settings.gravitationalForce) < 0 ); } return wasHit; }*/ public toJSON(): any { const { center, radius } = this; return { center, radius }; } }