mirror of https://github.com/mode777/rayjs.git
Add editor
This commit is contained in:
parent
2f537f649c
commit
28122c54a3
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
|
@ -1,13 +1,14 @@
|
|||
import { Behaviour, Builder, Creator, Entity, HasBoundingBox, HasMouseInteraction, HasPosition, HasSize, combine, hasDefault, hasDefaultFn, makeEntity, which, withBoundingBox, withComponent, withMouseInteraction, withPosition, withSize } from "./entity"
|
||||
import { Behaviour, Builder, Creator, Entity, HasBehaviour, HasBoundingBox, HasMouseInteraction, HasPosition, HasSize, combine, hasDefault, hasDefaultFn, makeEntity, which, withBehaviour, withBoundingBox, withComponent, withMouseInteraction, withPosition, withSize } from "./entity"
|
||||
import { entityAdd, gameRun, gameSetClearColor } from "./game"
|
||||
import { resourceUnload, textureLoad } from "./resource"
|
||||
import { HasText, withText } from "./text"
|
||||
|
||||
const withGuiSize = withComponent<HasSize>(x => hasDefaultFn(x, 'size', () => new Vector2(100,20)))
|
||||
|
||||
type UiControl = Entity & HasPosition & HasSize
|
||||
const makeControl = combine(makeEntity, withPosition, withSize)
|
||||
const makeControl = combine(makeEntity, withPosition, withGuiSize)
|
||||
|
||||
type Button = Entity & HasPosition & HasSize & HasMouseInteraction & HasText
|
||||
type Button = UiControl & HasMouseInteraction & HasText
|
||||
const drawsButton: Behaviour<Button> = {
|
||||
draw: x => {
|
||||
x.isClicked = guiButton(new Rectangle(x.position.x,x.position.y,x.size.x, x.size.y), x.text)
|
||||
|
@ -15,9 +16,7 @@ const drawsButton: Behaviour<Button> = {
|
|||
}
|
||||
}
|
||||
const makeButton: Builder<Button> = combine(
|
||||
makeEntity,
|
||||
withPosition,
|
||||
withSize,
|
||||
makeControl,
|
||||
withMouseInteraction,
|
||||
withComponent<HasText>(x => hasDefault(x, 'text', 'Button')),
|
||||
which(drawsButton))
|
||||
|
@ -30,7 +29,7 @@ interface HasChangedEvent {
|
|||
onChange?: () => void
|
||||
}
|
||||
const withChangedEvent = withComponent<HasChangedEvent>()
|
||||
type Textbox = Entity & HasPosition & HasSize & HasText & HasActive & HasChangedEvent
|
||||
type Textbox = UiControl & HasText & HasActive & HasChangedEvent
|
||||
const drawsTextbox: Behaviour<Textbox> = {
|
||||
draw: x => {
|
||||
if(guiTextBox(new Rectangle(x.position.x,x.position.y,x.size.x, x.size.y), x, x.active)){
|
||||
|
@ -39,64 +38,77 @@ const drawsTextbox: Behaviour<Textbox> = {
|
|||
}
|
||||
}
|
||||
}
|
||||
const makeTextbox: Builder<Textbox> = combine(makeEntity,
|
||||
withGuiBounds,
|
||||
const makeTextbox: Builder<Textbox> = combine(
|
||||
makeControl,
|
||||
withText,
|
||||
withActive,
|
||||
withChangedEvent,
|
||||
which(drawsTextbox))
|
||||
type CheckBox = Entity & HasBoundingBox & HasText & HasActive & HasChangedEvent
|
||||
type CheckBox = UiControl & HasText & HasActive & HasChangedEvent
|
||||
const drawsCheckbox: Behaviour<CheckBox> = {
|
||||
draw: c => {
|
||||
const old = c.active
|
||||
c.active = guiCheckBox(c.boundingBox, c.text, c.active)
|
||||
if(old != c.active && c.onChange) c.onChange()
|
||||
draw: x => {
|
||||
const old = x.active
|
||||
x.active = guiCheckBox(new Rectangle(x.position.x,x.position.y,x.size.x, x.size.y), x.text, x.active)
|
||||
if(old != x.active && x.onChange) x.onChange()
|
||||
}
|
||||
}
|
||||
const makeCheckbox: Builder<CheckBox> = combine(makeEntity,
|
||||
withGuiBounds,
|
||||
const makeCheckbox: Builder<CheckBox> = combine(
|
||||
makeControl,
|
||||
withText,
|
||||
withActive,
|
||||
withChangedEvent,
|
||||
which(drawsCheckbox))
|
||||
|
||||
type Label = Entity & HasBoundingBox & HasText
|
||||
const drawsLabel: Behaviour<Label> = { draw: c => guiLabel(c.boundingBox, c.text) }
|
||||
const makeLabel: Builder<Label> = combine(makeEntity, withGuiBounds, withText, which(drawsLabel))
|
||||
type Label = UiControl & HasText
|
||||
const drawsLabel: Behaviour<Label> = { draw: x => guiLabel(new Rectangle(x.position.x,x.position.y,x.size.x, x.size.y), x.text) }
|
||||
const makeLabel: Builder<Label> = combine(makeControl, withText, which(drawsLabel))
|
||||
|
||||
type WindowBox = Entity & HasBoundingBox & HasText
|
||||
const drawsWindowBox: Behaviour<WindowBox> = { draw: c => guiWindowBox(c.boundingBox, c.text) }
|
||||
const builderWindowBox: Builder<WindowBox> = combine(makeEntity, withGuiBounds, withText, which(drawsWindowBox))
|
||||
type WindowBox = UiControl & HasText
|
||||
const drawsWindowBox: Behaviour<WindowBox> = { draw: x => guiWindowBox(new Rectangle(x.position.x,x.position.y,x.size.x, x.size.y), x.text) }
|
||||
const builderWindowBox: Builder<WindowBox> = combine(makeControl, withText, which(drawsWindowBox))
|
||||
|
||||
type GroupBox = Entity & HasBoundingBox & HasText
|
||||
const drawsGroupBox: Behaviour<WindowBox> = { draw: c => guiGroupBox(c.boundingBox, c.text) }
|
||||
const makeGroupBox: Builder<GroupBox> = combine(makeEntity, withGuiBounds, withText, which(drawsGroupBox))
|
||||
type GroupBox = UiControl & HasText
|
||||
const drawsGroupBox: Behaviour<WindowBox> = { draw: x => guiGroupBox(new Rectangle(x.position.x,x.position.y,x.size.x, x.size.y), x.text) }
|
||||
const makeGroupBox: Builder<GroupBox> = combine(makeControl, withText, which(drawsGroupBox))
|
||||
|
||||
type Line = Entity & HasBoundingBox & HasText
|
||||
const drawsLine: Behaviour<Line> = { draw: c => guiLine(c.boundingBox, c.text) }
|
||||
const makeLine: Builder<Line> = combine(makeEntity, withGuiBounds, withText, which(drawsLine))
|
||||
type Line = UiControl & HasText
|
||||
const drawsLine: Behaviour<Line> = { draw: x => guiLine(new Rectangle(x.position.x,x.position.y,x.size.x, x.size.y), x.text) }
|
||||
const makeLine: Builder<Line> = combine(makeControl, withText, which(drawsLine))
|
||||
|
||||
type Panel = Entity & HasBoundingBox & HasText
|
||||
const drawsPanel: Behaviour<Panel> = { draw: c => guiPanel(c.boundingBox, c.text) }
|
||||
const makePanel: Builder<Panel> = combine(makeEntity, withGuiBounds, withText, which(drawsPanel))
|
||||
type Panel = UiControl & HasText
|
||||
const drawsPanel: Behaviour<Panel> = { draw: x => guiPanel(new Rectangle(x.position.x,x.position.y,x.size.x, x.size.y), x.text) }
|
||||
const makePanel: Builder<Panel> = combine(makeControl, withText, which(drawsPanel))
|
||||
|
||||
interface HasScrollView {
|
||||
contentArea: Rectangle,
|
||||
scroll: Vector2,
|
||||
viewArea: Rectangle
|
||||
}
|
||||
const withScrollView = withComponent<HasScrollView & HasBoundingBox>(x => {
|
||||
hasDefaultFn(x, 'contentArea', () => new Rectangle(x.boundingBox!.x, x.boundingBox!.y, x.boundingBox!.width, x.boundingBox!.height))
|
||||
hasDefaultFn(x, 'viewArea', () => new Rectangle(x.boundingBox!.x, x.boundingBox!.y, x.boundingBox!.width, x.boundingBox!.height))
|
||||
hasDefaultFn(x, 'scroll', () => new Vector2(0,0))
|
||||
})
|
||||
type ScrollPanel = Entity & HasBoundingBox & Partial<HasText> & HasScrollView
|
||||
const drawsScrollView: Behaviour<ScrollPanel> = {
|
||||
draw: s => {
|
||||
s.viewArea = guiScrollPanel(s.boundingBox, s.text, s.contentArea, s.scroll)
|
||||
}
|
||||
}
|
||||
const makeScollPanel: Builder<ScrollPanel> = combine(makeEntity, withComponent<Partial<HasText>>(), withGuiBounds, withScrollView, )
|
||||
// interface HasScrollView {
|
||||
// contentArea: Rectangle,
|
||||
// scroll: Vector2,
|
||||
// viewArea: Rectangle
|
||||
// }
|
||||
// const withScrollView = withComponent<HasScrollView & HasPosition & HasSize>(x => {
|
||||
// hasDefaultFn(x, 'contentArea', () => new Rectangle(x.boundingBox!.x, x.boundingBox!.y, x.boundingBox!.width, x.boundingBox!.height))
|
||||
// hasDefaultFn(x, 'viewArea', () => new Rectangle(x.boundingBox!.x, x.boundingBox!.y, x.boundingBox!.width, x.boundingBox!.height))
|
||||
// hasDefaultFn(x, 'scroll', () => new Vector2(0,0))
|
||||
// })
|
||||
// type ScrollPanel = UiControl & Partial<HasText> & HasScrollView
|
||||
// const drawsScrollView: Behaviour<ScrollPanel> = {
|
||||
// draw: s => {
|
||||
// s.viewArea = guiScrollPanel(s.boundingBox, s.text, s.contentArea, s.scroll)
|
||||
// }
|
||||
// }
|
||||
// const makeScollPanel: Builder<ScrollPanel> = combine(makeEntity, withComponent<Partial<HasText>>(), withGuiBounds, withScrollView, )
|
||||
|
||||
// interface HasChildControls {
|
||||
// controls: UiControl[]
|
||||
// }
|
||||
// const withChildControls = withComponent<HasChildControls>(x => hasDefault(x, 'controls', []))
|
||||
|
||||
// type StackPanel = UiControl & HasChildControls
|
||||
// const drawsStackLayout: Behaviour<StackPanel> = {
|
||||
// update: x => {
|
||||
|
||||
// }
|
||||
// }
|
||||
// const makeStackPanel = combine(makeControl, withChildControls)
|
||||
|
||||
interface HasGuiGlobalState {}
|
||||
type GuiGlobalState = Entity & HasGuiGlobalState
|
||||
|
@ -105,25 +117,122 @@ const appliesGuiGlobalState: Behaviour<GuiGlobalState> = {
|
|||
}
|
||||
const guiState: GuiGlobalState = combine(makeEntity,withComponent<HasGuiGlobalState>(), which(appliesGuiGlobalState))({})
|
||||
|
||||
interface HasTexture {
|
||||
texture: string,
|
||||
textureResource: Texture
|
||||
}
|
||||
const loadsTexture: Behaviour<HasTexture> = {
|
||||
load: x => x.textureResource = textureLoad(x.texture),
|
||||
unload: x => resourceUnload(x.texture)
|
||||
}
|
||||
const withTexture: Builder<HasTexture> = withComponent<HasTexture>()
|
||||
|
||||
interface HasTiles {
|
||||
tileWidth: number,
|
||||
tileHeight: number
|
||||
}
|
||||
const withTiles = withComponent<HasTiles>(x => {
|
||||
hasDefault(x, 'tileWidth', 16)
|
||||
hasDefault(x, 'tileHeight', 16)
|
||||
})
|
||||
|
||||
interface HasTilemap {
|
||||
width: number,
|
||||
height: number,
|
||||
tileData: number[]
|
||||
}
|
||||
const withTilemap = withComponent<HasTilemap>(x => {
|
||||
hasDefault(x, 'width', 0)
|
||||
hasDefault(x, 'height', 0)
|
||||
hasDefaultFn(x, 'tileData', () => [])
|
||||
})
|
||||
type Tilemap = Entity & HasPosition & HasTexture & HasTiles & HasTilemap
|
||||
const drawsTilemap: Behaviour<Tilemap> = {
|
||||
draw: map => {
|
||||
const position = new Vector2(0,0)
|
||||
const src = new Rectangle(0,0,map.tileWidth,map.tileHeight)
|
||||
const tilesX = Math.floor(map.textureResource.width/map.tileWidth)
|
||||
for (let y = 0; y < map.width; y++) {
|
||||
for (let x = 0; x < map.height; x++) {
|
||||
let tileId = map.tileData[y*map.width+x]
|
||||
if(tileId === 0) continue
|
||||
tileId--
|
||||
position.x = map.position.x + x * map.tileWidth
|
||||
position.y = map.position.y + y * map.tileHeight
|
||||
src.x = (tileId % tilesX) * map.tileWidth
|
||||
src.y = Math.floor(tileId / tilesX) * map.tileHeight
|
||||
drawTextureRec(map.textureResource, src, position, WHITE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const makeTilemap: Builder<Tilemap> = combine(makeEntity,
|
||||
withPosition,
|
||||
withTexture,
|
||||
withTiles,
|
||||
withTilemap,
|
||||
which<Tilemap>(loadsTexture),
|
||||
which(drawsTilemap))
|
||||
|
||||
interface HasChildren {
|
||||
children: Entity[]
|
||||
}
|
||||
const withChildren = withComponent<HasChildren>(x => hasDefaultFn(x, 'children', () => []))
|
||||
type Container = Entity & HasChildren
|
||||
const processesChildren: Behaviour<Container> = {
|
||||
load: x => x.children.forEach(y => y.behaviours.forEach(z => z.load ? z.load(y) : undefined)),
|
||||
update: x => x.children.forEach(y => y.behaviours.forEach(z => z.update ? z.update(y) : undefined)),
|
||||
draw: x => x.children.forEach(y => {
|
||||
y.behaviours.forEach(z => z.beforeDraw ? z.beforeDraw(y) : undefined)
|
||||
y.behaviours.forEach(z => z.draw ? z.draw(y) : undefined)
|
||||
y.behaviours.forEach(z => z.afterDraw ? z.afterDraw(y) : undefined)
|
||||
}),
|
||||
unload: x => x.children.forEach(y => y.behaviours.forEach(z => z.unload ? z.unload(y) : undefined))
|
||||
}
|
||||
const makeContainer = combine(makeEntity, withChildren, which(processesChildren))
|
||||
|
||||
interface HasCamera2D {
|
||||
camera2D: Camera2D
|
||||
}
|
||||
const withCamera2D = withComponent<HasCamera2D>(x => hasDefaultFn(x,'camera2D',() => new Camera2D(new Vector2(getScreenWidth()/2.0, getScreenHeight()/2.0),new Vector2(0,0), 0, 1)))
|
||||
type Space2D = Container & HasCamera2D
|
||||
const applies2dSpace: Behaviour<Space2D> = {
|
||||
beforeDraw: x => beginMode2D(x.camera2D),
|
||||
afterDraw: x => endMode2D()
|
||||
}
|
||||
const makeSpace2D = combine(makeContainer, withCamera2D, which(applies2dSpace))
|
||||
|
||||
gameRun({ width: 800, height: 600, title: 'My Editor', flags: FLAG_WINDOW_RESIZABLE }, async (quit) => {
|
||||
const map = makeTilemap({
|
||||
texture: "resources/tilemap_packed.png",
|
||||
width: 16,
|
||||
height: 16,
|
||||
tileData: new Array(16*16).fill(13)
|
||||
})
|
||||
const space2d = makeSpace2D({})
|
||||
space2d.children.push(map)
|
||||
space2d.camera2D.zoom = 2
|
||||
space2d.camera2D.target = new Vector2((map.width/2)*map.tileWidth,(map.height/2)*map.tileHeight)
|
||||
entityAdd(space2d)
|
||||
|
||||
const but = makeButton({
|
||||
position: new Vector2(10,10),
|
||||
text: 'Click Me!',
|
||||
onClick: () => but.boundingBox.x += 20
|
||||
onClick: () => but.position.x += 20
|
||||
})
|
||||
const tb = makeTextbox({
|
||||
text: but.text,
|
||||
boundingBox: new Rectangle(10, 50, 100, 20),
|
||||
position: new Vector2(10,50),
|
||||
onChange: () => but.text = tb.text
|
||||
})
|
||||
const cb = makeCheckbox({
|
||||
boundingBox: new Rectangle(10, 75, 20, 20),
|
||||
position: new Vector2(10,75),
|
||||
size: new Vector2(20,20),
|
||||
text: "Check Me!",
|
||||
onChange: () => cb.text = "Checkbox is "+ (cb.active ? "checked" : "not checked")
|
||||
})
|
||||
const l = makeLabel({
|
||||
boundingBox: new Rectangle(10, 100, 100, 20),
|
||||
position: new Vector2(10,100),
|
||||
text: "This is a label"
|
||||
})
|
||||
entityAdd(guiState)
|
||||
|
@ -131,4 +240,6 @@ gameRun({ width: 800, height: 600, title: 'My Editor', flags: FLAG_WINDOW_RESIZA
|
|||
entityAdd(tb)
|
||||
entityAdd(cb)
|
||||
entityAdd(l)
|
||||
|
||||
|
||||
})
|
||||
|
|
|
@ -4,7 +4,9 @@ export interface Behaviour<T> {
|
|||
load?: (entity: T) => void,
|
||||
unload?: (entity: T) => void,
|
||||
update?: (entity: T) => void,
|
||||
beforeDraw?: (entity: T) => void
|
||||
draw?: (entity: T) => void
|
||||
afterDraw?: (entity: T) => void
|
||||
}
|
||||
|
||||
export function addBehaviour<T extends HasBehaviour>(obj: T, behaviour: Behaviour<T>){
|
||||
|
@ -25,13 +27,15 @@ export function combine<A,B,C>(fn1: Builder<A>, fn2: Extender<A,B>, fn3: Extende
|
|||
export function combine<A,B,C,D>(fn1: Builder<A>, fn2: Extender<A,B>, fn3: Extender<A&B,C>, fn4: Extender<A&B&C,D>): Builder<A&B&C&D>
|
||||
export function combine<A,B,C,D,E>(fn1: Builder<A>, fn2: Extender<A,B>, fn3: Extender<A&B,C>, fn4: Extender<A&B&C,D>, fn5: Extender<A&B&C&D,E>): Builder<A&B&C&D&E>
|
||||
export function combine<A,B,C,D,E,F>(fn1: Builder<A>, fn2: Extender<A,B>, fn3: Extender<A&B,C>, fn4: Extender<A&B&C,D>, fn5: Extender<A&B&C&D,E>, fn6: Extender<A&B&C&D&E,F>): Builder<A&B&C&D&E&F>
|
||||
export function combine<A,B,C,D,E,F>(fn1: Builder<A>, fn2?: Extender<A,B>, fn3?: Extender<A&B,C>, fn4?: Extender<A&B&C,D>, fn5?: Extender<A&B&C&D,E>, fn6?: Extender<A&B&C&D&E,F>): Builder<A> | Builder<A&B> | Builder<A&B&C> | Builder<A&B&C&D> | Builder<A&B&C&D&E> | Builder<A&B&C&D&E&F>
|
||||
export function combine<A,B,C,D,E,F,G>(fn1: Builder<A>, fn2: Extender<A,B>, fn3: Extender<A&B,C>, fn4: Extender<A&B&C,D>, fn5: Extender<A&B&C&D,E>, fn6: Extender<A&B&C&D&E,F>, fn7: Extender<A&B&C&D&E&F,G>): Builder<A&B&C&D&E&F&G>
|
||||
export function combine<A,B,C,D,E,F,G>(fn1: Builder<A>, fn2?: Extender<A,B>, fn3?: Extender<A&B,C>, fn4?: Extender<A&B&C,D>, fn5?: Extender<A&B&C&D,E>, fn6?: Extender<A&B&C&D&E,F>, fn7?: Extender<A&B&C&D&E&F,G>): Builder<A> | Builder<A&B> | Builder<A&B&C> | Builder<A&B&C&D> | Builder<A&B&C&D&E> | Builder<A&B&C&D&E&F> | Builder<A&B&C&D&E&F&G>
|
||||
{
|
||||
if(fn2 && fn3 && fn4 && fn5 && fn6 && fn7) return combine(combine(fn1, fn2, fn3, fn4, fn5, fn6), fn7)
|
||||
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<A&B>) => <A&B>fn2(<A&Partial<B>>fn1(objIn))
|
||||
if(fn2) return (objIn: Partial<A&B> = {}) => <A&B>fn2(<A&Partial<B>>fn1(objIn))
|
||||
return fn1
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,11 @@ export const gameRun = (options: Partial<WindowConfig>, startupCallback: (quit:
|
|||
beginDrawing()
|
||||
clearBackground(gameClearColor)
|
||||
drawText("Active promises: "+ promiseUpdateList.length, 10,10, 8, RAYWHITE)
|
||||
entitiyList.forEach(e => e.behaviours.forEach(b => b.draw ? b.draw(e) : undefined))
|
||||
entitiyList.forEach(e => {
|
||||
e.behaviours.forEach(b => b.beforeDraw ? b.beforeDraw(e) : undefined)
|
||||
e.behaviours.forEach(b => b.draw ? b.draw(e) : undefined)
|
||||
e.behaviours.forEach(b => b.afterDraw ? b.afterDraw(e) : undefined)
|
||||
})
|
||||
endDrawing()
|
||||
}
|
||||
entitiyList.forEach(x => entityUnload(x))
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Compiler } from "inkjs";
|
|||
|
||||
gameRun({ width: 800, height: 400, title: "The Intercept" }, async (quit) => {
|
||||
const source = loadFileText("resources/intercept.ink")
|
||||
const c = new Compiler(source)
|
||||
const c = new Compiler(source!)
|
||||
const story = c.Compile()
|
||||
traceLog(LOG_INFO, "[INK] Story loaded")
|
||||
|
||||
|
|
Loading…
Reference in New Issue