Skip to content

Commit

Permalink
moved juego constructors to a file in the library. added deep-copy fu…
Browse files Browse the repository at this point in the history
…nction to Entity
  • Loading branch information
gorkermann committed Oct 19, 2023
1 parent eb378cf commit 81fdb80
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 44 deletions.
13 changes: 9 additions & 4 deletions Anim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export class AnimTarget {
}

let frameId = 0;
let constructors = { 'AnimFrame': () => new AnimFrame(), 'Vec2': () => new Vec2() }
let animConstructors = { 'AnimFrame': () => new AnimFrame(), 'Vec2': () => new Vec2() }

export class AnimFrame {
id: number;
Expand Down Expand Up @@ -643,10 +643,11 @@ export class Anim {
return match;
}

pushFrame( frame: AnimFrame, options: PushFrameOptions={} ) {
pushFrame( frame: AnimFrame, options: PushFrameOptions={} ): boolean {
if ( options.delay === undefined || options.delay < 0 ) options.delay = 0;
if ( options.threadIndex === undefined || options.threadIndex < 0 ) options.threadIndex = 0;

let success = true;
let threadIndex = Math.floor( options.threadIndex );

try {
Expand All @@ -671,7 +672,11 @@ export class Anim {
}
} catch ( ex: any ) {
console.error( ex.message );

success = false;
}

return success;
}

getThread( sequenceKey: string, threadIndex: number=0 ): Array<AnimFrame> {
Expand Down Expand Up @@ -732,11 +737,11 @@ export class Anim {

// copy sequence
// TODO: move this elsewhere
let toaster = new tp.Toaster( constructors );
let toaster = new tp.Toaster( animConstructors );
let json = tp.toJSON( this.sequences[sequenceKey], toaster );
toaster.cleanAddrIndex();

toaster = new tp.Toaster( constructors );
toaster = new tp.Toaster( animConstructors );
let frames = tp.fromJSON( json, toaster ) as Array<AnimFrame>;
tp.resolveList( [frames], toaster );

Expand Down
24 changes: 24 additions & 0 deletions Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

import * as tp from './lib/toastpoint.js'
import { constructors, nameMap } from './constructors.js'

import { Anim } from './Anim.js'
import { rangeEdit, Range } from './Editable.js'
Expand Down Expand Up @@ -143,6 +144,29 @@ export class Entity {
return flat;
}

/**
* Returns a deep copy the entity
*
* This has the potential to get very silly, if for instance an entity happens to have a pointer
* to the level it's in, since it will make a deep copy of the level, and then *every other* entity in the level
*
* @return {Entity} the copy
*/
copy(): Entity {
let toaster = new tp.Toaster( constructors );
let json = tp.toJSON( this, toaster );
toaster.cleanAddrIndex();

toaster = new tp.Toaster( constructors );
let copy = tp.fromJSON( json, toaster ) as Entity;
tp.resolveList( [copy], toaster );

copy.parent = null;
// material can be either an internal or external pointer

return copy;
}

// don't override! override subDestructor instead
destructor() {
if ( this.removeThis ) return;
Expand Down
14 changes: 14 additions & 0 deletions Inspector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,14 @@ class InspectorPanel {

this.dom.appendChild( div );
}

drawHelpers( context: CanvasRenderingContext2D ) {
for ( let field of this.fields ) {
for ( let helper of field.helperEntities ) {
helper.draw( context );
}
}
}
}

let _panels: Dict<InspectorPanel> = {};
Expand Down Expand Up @@ -303,4 +311,10 @@ export class Inspector {
static getPanelsDict(): Dict<InspectorPanel> {
return _panels;
}

static drawPanelHelpers( context: CanvasRenderingContext2D ) {
for ( let key in _panels ) {
_panels[key].drawHelpers( context );
}
}
}
33 changes: 33 additions & 0 deletions constructors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export type Newable = { new ( ...args: any[] ): any }

export function factory( newable: Newable ): ( () => Object ) {
return () => {
let obj = new newable();
return obj;
}
}

// map from class names to constructors
// used to make highlightable text in instructions
export let classMap: { [ key: string ]: Newable } = {};

// list of constructor functions
// (need to access static props, not sure how to define this as a type in TS, so type is vague)

// if a class is not in this list, it is instantiated as new Class()
export let constructors : { [key: string]: () => Object } = {};

export function addClass( className: string, constr: Newable ) {
classMap[className] = constr;

if ( !( className in constructors ) ) {
constructors[className] = factory( classMap[className] );
}

if ( !( className in nameMap ) ) {
nameMap[constr.name] = className;
}
}

// create a map so constructor names can be retrieved post-obfuscation
export let nameMap: { [ key: string ]: string } = {};
33 changes: 33 additions & 0 deletions objDef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { addClass, constructors } from './constructors.js'

import { Chrono, Anim, AnimField, AnimFrame, AnimTarget, PhysField } from './Anim.js'
import { Camera } from './Camera.js'
import { Entity } from './Entity.js'
import { GridArea } from './GridArea.js'
import { Line } from './Line.js'
import { Material } from './Material.js'
import { Shape } from './Shape.js'
import { Sound } from './Sound.js'
import { TileArray } from './TileArray.js'
import { Vec2 } from './Vec2.js'

export let empty = 0; // export so that webpack doesn't ignore the file

addClass( 'Vec2', Vec2 ); // no loops
addClass( 'Material', Material ); // no loops
addClass( 'Line', Line ); // no loops
addClass( 'Shape', Shape ); // could loop via .parent but currently not stored by parent
addClass( 'Entity', Entity ); // no loops
addClass( 'GridArea', GridArea );
addClass( 'TileArray', TileArray );
addClass( 'Anim', Anim );
addClass( 'AnimField', AnimField );
addClass( 'AnimFrame', AnimFrame );
addClass( 'Chrono', Chrono );
addClass( 'PhysField', PhysField );
addClass( 'Sound', Sound );
addClass( 'Camera', Camera );
addClass( 'AnimTarget', AnimTarget );

// define special constructors here
// constructors[className] = () => new Class( false );
69 changes: 52 additions & 17 deletions panel/AnimPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import md5 from 'md5'

import { Anim, AnimFrame, AnimField, AnimTarget } from '../Anim.js'
import { Dropdown } from '../Dropdown.js'
import { Entity } from '../Entity.js'
import { create, createTooltip, clear } from '../domutil.js'
import { Editable, extRangeEdit, Range } from '../Editable.js'
import { Inspector } from '../Inspector.js'
Expand Down Expand Up @@ -90,6 +91,7 @@ export class AnimPanel extends Field {
selectedFrameIndex: number = -1;
selectedFieldKey: string = '';
selectedSequenceKey: string = '';
drawFrameIndex: number = -1;

editingTarget: boolean = false;
editingField: boolean = false;
Expand Down Expand Up @@ -136,10 +138,11 @@ export class AnimPanel extends Field {

for ( let thread of threads ) {
for ( let frame of thread ) {
prehash += frame.id + ',';
prehash += frame.id;

for ( let key in frame.targets ) {
prehash += JSON.stringify( frame.targets[key] );
prehash += key;
prehash += frame.targets[key].value;
}
}
}
Expand Down Expand Up @@ -187,26 +190,31 @@ export class AnimPanel extends Field {
}
}

createFrameCell( frame: AnimFrame, inRow: HTMLTableRowElement, index: number ): HTMLTableColElement {
let cell = create( 'td', {}, inRow ) as HTMLTableColElement;
setFromThread( anim: Anim, thread: Array<AnimFrame>, frameIndex: number=-1 ) {
let touched: Dict<boolean> = {};

cell.onmouseup = () => {
this.selectedFrameIndex = index;
if ( frameIndex < 0 ) frameIndex = thread.length - 1;

// set object values
if ( this.selectedSequenceKey ) {
let thread = this.anim.getThread( this.selectedSequenceKey );
let touched: Dict<boolean> = {};
for ( let i = frameIndex; i < thread.length; i++ ) {
for ( let key in thread[i].targets ) {
if ( touched[key] ) continue;

for ( let i = index; i < thread.length; i++ ) {
for ( let key in thread[i].targets ) {
if ( touched[key] ) continue;
anim.fields[key].set( thread[i].targets[key].value );

this.anim.fields[key].set( frame.targets[key].value );
touched[key] = true;
}
}
}

touched[key] = true;
}
}
createFrameCell( frame: AnimFrame, inRow: HTMLTableRowElement, frameIndex: number ): HTMLTableColElement {
let cell = create( 'td', {}, inRow ) as HTMLTableColElement;

cell.onmouseup = () => {
this.selectedFrameIndex = frameIndex;

// set object values (only when viewing a sequence)
if ( this.selectedSequenceKey ) {
this.setFromThread( this.anim, this.anim.getThread( this.selectedSequenceKey ), frameIndex );
}

// edit frame fields
Expand All @@ -219,6 +227,21 @@ export class AnimPanel extends Field {
this.fieldSection.updateDom();
}

cell.onmouseenter = () => {

// copy object
this.helperEntities = [];

if ( this.linkedObjs[0] instanceof Entity ) {
let copy = this.linkedObjs[0].copy();

this.setFromThread( copy.anim, this.anim.getThread( this.selectedSequenceKey ), frameIndex );
copy.drawWireframe = true;

this.helperEntities.push( copy );
}
}

return cell;
}

Expand Down Expand Up @@ -282,6 +305,18 @@ export class AnimPanel extends Field {
if ( x.length > maxThreadLength ) maxThreadLength = x.length;
} );

// copy object
this.helperEntities = [];

if ( this.linkedObjs[0] instanceof Entity ) {
let copy = this.linkedObjs[0].copy();

this.setFromThread( copy.anim, this.anim.getThread( this.selectedSequenceKey ) );
copy.drawWireframe = true;

this.helperEntities.push( copy );
}

// container
clear( this.frameDom );

Expand Down
3 changes: 3 additions & 0 deletions panel/Field.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Debug } from '../Debug.js'
import { Dropdown } from '../Dropdown.js'
import { Editable, Range, rangeEdit } from '../Editable.js'
import { Entity } from '../Entity.js'
import { Vec2 } from '../Vec2.js'

import { create } from '../domutil.js'
Expand Down Expand Up @@ -108,6 +109,8 @@ export class Field {
firstUpdate: boolean = true;
oldVal: any = null;

helperEntities: Array<Entity> = [];

private gotInput: boolean = false;
protected multiple: boolean = false;
protected overridden: boolean = false;
Expand Down
47 changes: 24 additions & 23 deletions test/test_Anim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ function test_Anim( tf: TestFuncs ) {
tf.THROWS( () => new AnimField( obj, 'xx', 1 ) );

// no field with key 'a'
tf.THROWS( () => anim.pushFrame( new AnimFrame( {
'a': new AnimTarget( 0 )
} ) ) );
tf.ASSERT_EQ( anim.threads.length, 0 );
tf.ASSERT_EQ( anim.pushFrame( new AnimFrame( { 'a': new AnimTarget( 0 ) } ) ), false );
tf.ASSERT_EQ( anim.threads.length, 0 );

// ok
tf.ASSERT_EQ( obj.x, 0 );
Expand Down Expand Up @@ -166,12 +166,10 @@ function test_Anim( tf: TestFuncs ) {
'b': new AnimTarget( new Vec2( 0, -1 ) ),
} ) );

// type mismatch
tf.THROWS( () => {
anim.pushFrame( new AnimFrame( {
'a': new AnimTarget( 1 ),
} ) );
} );
// type mismatch, no push
tf.ASSERT_EQ( anim.threads.length, 0 );
tf.ASSERT_EQ( anim.pushFrame( new AnimFrame( { 'a': new AnimTarget( 1 ) } ) ), false );
tf.ASSERT_EQ( anim.threads.length, 0 );

anim.update( 1.0, 1 );

Expand Down Expand Up @@ -534,17 +532,19 @@ function test_AnimFunc( tf: TestFuncs ) {
'x': { value: -2 },
} ) );

tf.THROWS( () => { anim.pushFrame( new AnimFrame( {}, [
{ caller: null, funcName: 'A' } // no caller object
] ) ) } );
tf.ASSERT_EQ( anim.threads.length, 0 );
tf.ASSERT_EQ(
anim.pushFrame( new AnimFrame( {}, [{ caller: null, funcName: 'A' }] ) ), // no caller object
false );

tf.THROWS( () => { anim.pushFrame( new AnimFrame( {}, [
{ caller: obj, funcName: 'y' } // not a function
] ) ) } );
tf.ASSERT_EQ(
anim.pushFrame( new AnimFrame( {}, [{ caller: obj, funcName: 'y' }] ) ), // not a function
false );

tf.THROWS( () => { anim.pushFrame( new AnimFrame( {}, [
{ caller: obj, funcName: 'x' } // not a function
] ) ) } );
tf.ASSERT_EQ(
anim.pushFrame( new AnimFrame( {}, [{ caller: obj, funcName: 'x' }] ) ), // not a function
false );
tf.ASSERT_EQ( anim.threads.length, 0 );

anim.pushFrame( new AnimFrame( {}, [
{ caller: obj, funcName: 'A' }
Expand Down Expand Up @@ -804,11 +804,12 @@ function test_threads( tf: TestFuncs ) {
'x': { value: 2, expireOnReach: true }, // no throw, since collisions with default frame are ignored
} ) ); // thread 0 by default

tf.THROWS( () => {
anim.pushFrame( new AnimFrame( {
'x': { value: 2, expireOnReach: true }, // x is already present in thread 0 (duplicate target)
} ), { threadIndex: 1 } );
} );
tf.ASSERT_EQ( anim.threads.length, 1 );
tf.ASSERT_EQ(
// x is already present in thread 0 (duplicate target)
anim.pushFrame( new AnimFrame( { 'x': { value: 2, expireOnReach: true } } ), { threadIndex: 1 } ),
false );
tf.ASSERT_EQ( anim.threads.length, 1 );

anim.pushFrame( new AnimFrame( {
'y': { value: 2, expireOnReach: true }, // no throw, since collisions with default frame are ignored
Expand Down
Loading

0 comments on commit 81fdb80

Please sign in to comment.