2023-06-06 06:51:42 +00:00
|
|
|
import { withComponent } from "./entity";
|
|
|
|
import { hasDefault } from "./entity";
|
2023-05-29 22:03:29 +00:00
|
|
|
import { Behaviour, Entity, EntityOf } from "./entity";
|
|
|
|
import { forEachReverse } from "./helpers";
|
2023-05-27 13:03:29 +00:00
|
|
|
import { resourceUnloadAll } from "./resource";
|
|
|
|
|
2023-05-30 23:21:20 +00:00
|
|
|
const promiseUpdateList: PromiseContext<any>[] = []
|
2023-05-29 22:03:29 +00:00
|
|
|
const entitiyList: Entity[] = []
|
2023-05-27 13:03:29 +00:00
|
|
|
|
2023-05-20 19:34:27 +00:00
|
|
|
|
2023-05-27 09:35:08 +00:00
|
|
|
const dispatchPromises = () => {
|
|
|
|
for (var i = promiseUpdateList.length - 1; i >= 0; i--) {
|
2023-05-30 23:21:20 +00:00
|
|
|
const p = promiseUpdateList[i]
|
|
|
|
p.update()
|
|
|
|
if (p.isFinished) {
|
2023-05-27 09:35:08 +00:00
|
|
|
promiseUpdateList.splice(i, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-30 23:21:20 +00:00
|
|
|
class PromiseContext<T> {
|
|
|
|
|
|
|
|
private _result: T | null = null;
|
|
|
|
public get result(): T | null {
|
|
|
|
return this._result;
|
|
|
|
}
|
|
|
|
private _error: any | null = null;
|
|
|
|
public get error(): any | null {
|
|
|
|
return this._error;
|
|
|
|
}
|
|
|
|
private _isFinished = false;
|
|
|
|
public get isFinished() {
|
|
|
|
return this._isFinished;
|
|
|
|
}
|
|
|
|
private _isCancellationRequested = false;
|
|
|
|
public get isCancellationRequested() {
|
|
|
|
return this._isCancellationRequested;
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(private readonly resolveFn: (val: T | PromiseLike<T>) => void,
|
|
|
|
private readonly rejectFn: (err: any) => void,
|
|
|
|
private readonly updateFn: (p: PromiseContext<T>) => void){}
|
|
|
|
|
|
|
|
update(){
|
|
|
|
if(!this.isFinished){
|
|
|
|
this.updateFn(this)
|
2023-05-29 22:03:29 +00:00
|
|
|
}
|
2023-05-27 09:35:08 +00:00
|
|
|
}
|
2023-05-30 23:21:20 +00:00
|
|
|
|
|
|
|
resolve(val: T){
|
|
|
|
this._result = val
|
|
|
|
this._isFinished = true
|
|
|
|
this.resolveFn(val)
|
|
|
|
}
|
|
|
|
|
|
|
|
reject(reason: any){
|
|
|
|
this._error = reason
|
|
|
|
this._isFinished = true
|
|
|
|
this.rejectFn(reason)
|
|
|
|
}
|
|
|
|
|
|
|
|
cancel(){
|
|
|
|
this._isCancellationRequested = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export interface ExtendedPromise<T> extends Promise<T> {
|
|
|
|
context: PromiseContext<T>
|
|
|
|
}
|
|
|
|
export const makeUpdateablePromise = <T>(update: (ctx: PromiseContext<T>) => void) => {
|
|
|
|
let context: PromiseContext<T>
|
|
|
|
const promise = <ExtendedPromise<T>>new Promise<T>((resolve, reject) => {
|
|
|
|
context = new PromiseContext<T>(resolve,reject,update)
|
|
|
|
});
|
|
|
|
promise.context = context!
|
|
|
|
promiseUpdateList.unshift(context!)
|
2023-05-27 09:35:08 +00:00
|
|
|
return promise
|
|
|
|
}
|
|
|
|
|
2023-05-29 22:03:29 +00:00
|
|
|
export const entityAdd = (entity: Entity) => {
|
|
|
|
entity.behaviours.forEach(b => b.load ? b.load(entity) : undefined)
|
2023-05-27 13:03:29 +00:00
|
|
|
entitiyList.push(entity)
|
2023-05-29 22:03:29 +00:00
|
|
|
traceLog(LOG_INFO, `GAME: [ID ${entity.id}] loaded entity`)
|
|
|
|
}
|
|
|
|
|
|
|
|
export const entityUnload = (entity: Entity) => {
|
|
|
|
forEachReverse(entity.behaviours, (b, i) => b.unload ? b.unload(entity) : undefined);
|
|
|
|
traceLog(LOG_INFO, `GAME: [ID ${entity.id}] unloaded entity`)
|
2023-05-27 13:03:29 +00:00
|
|
|
}
|
2023-05-27 09:35:08 +00:00
|
|
|
|
2023-05-29 22:03:29 +00:00
|
|
|
export const entityRemove = (entity: Entity) => {
|
2023-05-27 13:03:29 +00:00
|
|
|
// TODO: Do this cached
|
|
|
|
const i = entitiyList.findIndex(x => x.id === entity.id)
|
|
|
|
if (i !== -1) {
|
2023-05-29 22:03:29 +00:00
|
|
|
entityUnload(entity)
|
2023-05-27 13:03:29 +00:00
|
|
|
entitiyList.splice(i, 1)
|
2023-05-20 19:34:27 +00:00
|
|
|
}
|
2023-05-27 13:03:29 +00:00
|
|
|
}
|
2023-05-20 19:34:27 +00:00
|
|
|
|
2023-06-06 06:51:42 +00:00
|
|
|
interface WindowConfig {
|
|
|
|
width: number,
|
|
|
|
height: number,
|
|
|
|
title: string,
|
|
|
|
flags: number
|
|
|
|
}
|
|
|
|
const withConfig = withComponent<WindowConfig>(x => {
|
|
|
|
hasDefault(x, 'width', 640)
|
|
|
|
hasDefault(x, 'height', 480)
|
|
|
|
hasDefault(x, 'title', "Rayjs")
|
|
|
|
hasDefault(x, 'flags', 0)
|
|
|
|
})
|
|
|
|
|
2023-06-06 14:50:38 +00:00
|
|
|
let gameClearColor = BLACK
|
|
|
|
export const gameSetClearColor = (c: Color) => gameClearColor = c
|
|
|
|
|
|
|
|
export const gameRun = (options: Partial<WindowConfig>, startupCallback: (quit: () => void) => void | Promise<void>) => {
|
2023-06-06 06:51:42 +00:00
|
|
|
const config = withConfig(options)
|
|
|
|
setConfigFlags(config.flags)
|
|
|
|
initWindow(config.width,config.height,config.title)
|
2023-05-27 13:03:29 +00:00
|
|
|
setTargetFPS(60)
|
2023-05-29 22:03:29 +00:00
|
|
|
let quit = false
|
|
|
|
let exception: any = null
|
|
|
|
const p = startupCallback(() => quit = true)
|
|
|
|
if(p) p.catch(e => { exception = e })
|
2023-06-06 06:51:42 +00:00
|
|
|
while(!windowShouldClose() && !quit){
|
2023-05-27 13:03:29 +00:00
|
|
|
dispatchPromises()
|
2023-05-29 22:03:29 +00:00
|
|
|
if(exception) throw exception
|
|
|
|
entitiyList.forEach(e => e.behaviours.forEach(b => b.update ? b.update(e) : undefined))
|
2023-05-27 13:03:29 +00:00
|
|
|
beginDrawing()
|
2023-06-06 14:50:38 +00:00
|
|
|
clearBackground(gameClearColor)
|
2023-05-29 22:03:29 +00:00
|
|
|
drawText("Active promises: "+ promiseUpdateList.length, 10,10, 8, RAYWHITE)
|
|
|
|
entitiyList.forEach(e => e.behaviours.forEach(b => b.draw ? b.draw(e) : undefined))
|
2023-05-27 13:03:29 +00:00
|
|
|
endDrawing()
|
2023-05-20 19:34:27 +00:00
|
|
|
}
|
2023-05-29 22:03:29 +00:00
|
|
|
entitiyList.forEach(x => entityUnload(x))
|
|
|
|
entitiyList.splice(0,entitiyList.length)
|
2023-05-27 13:03:29 +00:00
|
|
|
resourceUnloadAll()
|
|
|
|
closeWindow()
|
2023-05-20 19:34:27 +00:00
|
|
|
}
|