import { makeInlineText } from "./text" export interface Behaviour { load?: (entity: T) => void, unload?: (entity: T) => void, update?: (entity: T) => void, draw?: (entity: T) => void } export function addBehaviour(obj: T, behaviour: Behaviour){ obj.behaviours.push(behaviour) } export function removeBehaviour(obj: T, behaviour: Behaviour){ const idx = obj.behaviours.findIndex(x => x === behaviour) if(idx !== -1) obj.behaviours.splice(idx, 1) } export type Creator = (objIn: A) => B export type Builder = Creator, A> export type Extender = Creator, B> export function combine(fn1: Builder): Builder export function combine(fn1: Builder, fn2: Extender): Builder export function combine(fn1: Builder, fn2: Extender, fn3: Extender): Builder export function combine(fn1: Builder, fn2: Extender, fn3: Extender, fn4: Extender): Builder export function combine(fn1: Builder, fn2: Extender, fn3: Extender, fn4: Extender, fn5: Extender): Builder export function combine(fn1: Builder, fn2: Extender, fn3: Extender, fn4: Extender, fn5: Extender, fn6: Extender): Builder export function combine(fn1: Builder, fn2?: Extender, fn3?: Extender, fn4?: Extender, fn5?: Extender, fn6?: Extender): Builder | Builder | Builder | Builder | Builder | Builder { if(fn2 && fn3 && fn4 && fn5 && fn6) return combine(combine(fn1, fn2, fn3, fn4, fn5), fn6) if(fn2 && fn3 && fn4 && fn5) return combine(combine(fn1, fn2, fn3, fn4), fn5) if(fn2 && fn3 && fn4) return combine(combine(fn1, fn2, fn3), fn4) if(fn2 && fn3) return combine(combine(fn1, fn2), fn3) if(fn2) return (objIn: Partial) => fn2(>fn1(objIn)) return fn1 } export interface HasIdentity { id: number } export interface HasPosition { position: Vector2 } export interface HasColor { color: Color } export interface HasBoundingBox { boundingBox: Rectangle } export interface HasBehaviour { behaviours: Behaviour[] } export type Entity = HasIdentity & HasBehaviour export type EntityOf = Entity & T export const hasDefaultFn = (obj: T, key: keyof T, valueFn: () => any) => { if(obj[key] === undefined) obj[key] = valueFn() } export const hasDefault = (obj: T, key: keyof T, value: any) => { if(obj[key] === undefined) obj[key] = value } export const withComponent = (fn?: (obj: Partial) => void) => (obj: Partial) => { if(fn){fn(obj)}; return obj } export const which = (behaviour: Behaviour) => (obj: T) => { addBehaviour(obj,behaviour); return obj; } let ID = 0 export const withIdentity = withComponent(x => hasDefaultFn(x,'id', () => ID++)) export const withBehaviour = withComponent(x => hasDefaultFn(x, 'behaviours', () => [])) export const withPosition = withComponent(x => hasDefaultFn(x, 'position', () => new Vector2(0,0))) export const withColor = withComponent(x => hasDefaultFn(x, 'color', () => new Color(255,255,255,255))) export const withBoundingBox = withComponent(x => hasDefaultFn(x, 'boundingBox', () => new Rectangle(0,0,0,0))) export const makeEntity: Builder = combine(withIdentity, withBehaviour) export const debugRectDrawFn = (obj: HasBoundingBox, color = GREEN) => drawRectangleLines(obj.boundingBox.x, obj.boundingBox.y, obj.boundingBox.width, obj.boundingBox.height, color) export const debugRectDrawBehaviour = { draw: debugRectDrawFn } export interface HasMouseInteraction { isClicked: boolean hasMouseOver: boolean, hasMouseEntered: boolean, hasMouseLeft: boolean debugClickable: boolean, onClick?: () => void } export const withMouseInteraction = withComponent() export const checksBoundingBoxClicks: Behaviour = { update: obj => { const over = checkCollisionPointRec(getMousePosition(), obj.boundingBox) obj.hasMouseEntered = !obj.hasMouseOver && over obj.hasMouseLeft = obj.hasMouseOver && !over obj.hasMouseOver = over obj.isClicked = obj.hasMouseOver && isMouseButtonPressed(MOUSE_BUTTON_LEFT) if(obj.hasMouseEntered) setMouseCursor(MOUSE_CURSOR_POINTING_HAND) if(obj.hasMouseLeft) setMouseCursor(MOUSE_CURSOR_DEFAULT) }, draw: obj => { if(obj.debugClickable){ debugRectDrawFn(obj, obj.hasMouseOver ? RED : GREEN) drawCircle(getMouseX(), getMouseY(), 10, YELLOW) } }, unload: obj => obj.hasMouseOver && setMouseCursor(MOUSE_CURSOR_DEFAULT) }