From 6ca1fcfaad1896494892c751def1b87dc357521c Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 3 May 2012 13:24:21 -0400 Subject: [PATCH] First commit --- .classpath | 8 + .project | 18 + .settings/org.eclipse.jdt.core.prefs | 11 + api.md | 651 ++++++++++++++ ivy.xml | 14 + .../modapi/AbstractDynamicProperty.java | 19 + .../ironchefpython/modapi/ActivateEvent.java | 16 + .../ironchefpython/modapi/CustomEvent.java | 38 + .../modapi/CustomEventFactory.java | 25 + .../modapi/DynamicProperty.java | 16 + .../ironchefpython/modapi/EventFactory.java | 10 + .../ironchefpython/modapi/JsModManager.java | 254 ++++++ src/org/ironchefpython/modapi/ModManager.java | 109 +++ src/org/ironchefpython/modapi/Prototype.java | 119 +++ .../modapi/error/GeneralModdingException.java | 9 + .../error/InvalidComponentRegistration.java | 31 + .../error/InvalidEventRegistration.java | 9 + .../modapi/error/NoSuchEventFactory.java | 10 + .../modapi/error/PropertyError.java | 14 + .../error/UnregisteredEventException.java | 9 + .../modapi/primitives/BooleanProperty.java | 63 ++ .../modapi/primitives/CalculatedProperty.java | 76 ++ .../modapi/primitives/ColorProperty.java | 35 + .../modapi/primitives/FunctionProperty.java | 38 + .../modapi/primitives/NumberProperty.java | 64 ++ .../modapi/primitives/PrototypeProperty.java | 53 ++ .../modapi/primitives/StringProperty.java | 35 + .../modapi/primitives/TextureProperty.java | 36 + src/org/mockengine/AbstractEventTarget.java | 39 + src/org/mockengine/Component.java | 16 + src/org/mockengine/Engine.java | 8 + src/org/mockengine/Entity.java | 7 + src/org/mockengine/Event.java | 6 + src/org/mockengine/EventTarget.java | 7 + src/org/mockengine/Handler.java | 8 + src/org/mockengine/MockEngine.java | 7 + src/org/mockengine/Player.java | 5 + src/org/mockengine/PlayerState.java | 5 + src/org/mockengine/event/AbstractEvent.java | 20 + src/org/mockengine/event/PlayerEvent.java | 25 + src/org/mozilla/javascript/JSON.java | 825 ++++++++++++++++++ ...Chris A's conflicted copy 2012-05-03).java | 111 +++ .../ironchefpython/rhinodemo/HealthTest.java | 110 +++ .../rhinodemo/JavassistTest.java | 36 + .../org/ironchefpython/rhinodemo/ModTest.java | 35 + .../ironchefpython/rhinodemo/anothermod.js | 167 ++++ test/org/ironchefpython/rhinodemo/health.js | 70 ++ test/org/ironchefpython/rhinodemo/immot.js | 174 ++++ .../org/ironchefpython/rhinodemo/testing.java | 13 + test/org/ironchefpython/rhinodemo/testmod.js | 251 ++++++ 50 files changed, 3735 insertions(+) create mode 100644 .classpath create mode 100644 .project create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 api.md create mode 100644 ivy.xml create mode 100644 src/org/ironchefpython/modapi/AbstractDynamicProperty.java create mode 100644 src/org/ironchefpython/modapi/ActivateEvent.java create mode 100644 src/org/ironchefpython/modapi/CustomEvent.java create mode 100644 src/org/ironchefpython/modapi/CustomEventFactory.java create mode 100644 src/org/ironchefpython/modapi/DynamicProperty.java create mode 100644 src/org/ironchefpython/modapi/EventFactory.java create mode 100644 src/org/ironchefpython/modapi/JsModManager.java create mode 100644 src/org/ironchefpython/modapi/ModManager.java create mode 100644 src/org/ironchefpython/modapi/Prototype.java create mode 100644 src/org/ironchefpython/modapi/error/GeneralModdingException.java create mode 100644 src/org/ironchefpython/modapi/error/InvalidComponentRegistration.java create mode 100644 src/org/ironchefpython/modapi/error/InvalidEventRegistration.java create mode 100644 src/org/ironchefpython/modapi/error/NoSuchEventFactory.java create mode 100644 src/org/ironchefpython/modapi/error/PropertyError.java create mode 100644 src/org/ironchefpython/modapi/error/UnregisteredEventException.java create mode 100644 src/org/ironchefpython/modapi/primitives/BooleanProperty.java create mode 100644 src/org/ironchefpython/modapi/primitives/CalculatedProperty.java create mode 100644 src/org/ironchefpython/modapi/primitives/ColorProperty.java create mode 100644 src/org/ironchefpython/modapi/primitives/FunctionProperty.java create mode 100644 src/org/ironchefpython/modapi/primitives/NumberProperty.java create mode 100644 src/org/ironchefpython/modapi/primitives/PrototypeProperty.java create mode 100644 src/org/ironchefpython/modapi/primitives/StringProperty.java create mode 100644 src/org/ironchefpython/modapi/primitives/TextureProperty.java create mode 100644 src/org/mockengine/AbstractEventTarget.java create mode 100644 src/org/mockengine/Component.java create mode 100644 src/org/mockengine/Engine.java create mode 100644 src/org/mockengine/Entity.java create mode 100644 src/org/mockengine/Event.java create mode 100644 src/org/mockengine/EventTarget.java create mode 100644 src/org/mockengine/Handler.java create mode 100644 src/org/mockengine/MockEngine.java create mode 100644 src/org/mockengine/Player.java create mode 100644 src/org/mockengine/PlayerState.java create mode 100644 src/org/mockengine/event/AbstractEvent.java create mode 100644 src/org/mockengine/event/PlayerEvent.java create mode 100644 src/org/mozilla/javascript/JSON.java create mode 100644 test/org/ironchefpython/rhinodemo/HealthTest (Chris A's conflicted copy 2012-05-03).java create mode 100644 test/org/ironchefpython/rhinodemo/HealthTest.java create mode 100644 test/org/ironchefpython/rhinodemo/JavassistTest.java create mode 100644 test/org/ironchefpython/rhinodemo/ModTest.java create mode 100644 test/org/ironchefpython/rhinodemo/anothermod.js create mode 100644 test/org/ironchefpython/rhinodemo/health.js create mode 100644 test/org/ironchefpython/rhinodemo/immot.js create mode 100644 test/org/ironchefpython/rhinodemo/testing.java create mode 100644 test/org/ironchefpython/rhinodemo/testmod.js diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..eec66f8 --- /dev/null +++ b/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..beddc9a --- /dev/null +++ b/.project @@ -0,0 +1,18 @@ + + + RhinoTechDemo + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + org.apache.ivyde.eclipse.ivynature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..8000cd6 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/api.md b/api.md new file mode 100644 index 0000000..348a260 --- /dev/null +++ b/api.md @@ -0,0 +1,651 @@ +ModTheMod API +============= +The __ModTheMod API__ seeks to allow anyone to be able to write mods. + +ModManager +------------ + +The central manager for all mods. Handles defining of mods and Component types. Accessed by a global variable `manager`. + +### Methods + +* `Prototype registerPrototype(Object definition)` -- Register a new prototype with the Mod Manager. The prototype definition is a Javascript object that has proprties for the `id`, prototype `properties`, event `handlers`, parent `prototypes`. + +Example: +```javascript +var ItemComponent = manager.registerPrototype({ + "id": "item", + "properties": { + "name": manager.stringType, + "renderWithIcon": manager.booleanType, + "stack_size": manager.numberType, + "texture": manager.textureType + } +}); +``` + +* `Prototype registerPrototype(Prototype parent, Object definition)` -- Register a new prototype with the Mod Manager. The prototype definition is a Javascript object that has proprties for the `id`, prototype `properties`, event `handlers`, parent `prototypes` + + + + + + + + +* Mod getMod `(name)` -- Gets a mod with the given name. + +* Mod defineMod `(name, params, initialize(mod))` -- Defines a new mod with the given name and params. Initialize takes in the mod created as a parameter and +should add everything to the mod. + +* void include `(name)` -- Includes a mod to be downloaded. + +* void require `(name)` -- Forces a mod to be downloaded. The mod will not be loaded if this mod is not found or downloaded. + +* Mod[] getMods `()` -- Gets a list of all currently loaded mods. + +Mod +---------- +A Mod represents, well, a mod. + +Mods are created when they are loaded. The Mod object can be accessed within the mod itself with the `mod` variable in the mod�s global scope. + +### Properties + +* name `` -- The name of the mod. + +* description `` -- Javascript object containing a description, url, author, revision, and an array of strings called �uses� which contains all of the mods the component depends on. + +### Methods + +None so far. + +Composite +---------- +A Composite is a section of a mod that provides certain functionality. Composites are created by giving them certain properties which are put through a Factory to give them functionality. + +Both properties and methods are accessed directly through the dot operator on the Component. + +### Params + + + +* properties -- Javascript object containing the default properties of the component. Null means the property must be set. + +* methods -- Javascript object containing the methods to be used. The �this� keyword refers to the part that the method will be part of. + +### Methods + +* Object getProperty(property) -- Gets the value of the given property. + +* boolean composedOf(String factory) -- Checks if the Component is composed of a component of the given factory. + +* [Component] create[Component](name, params, initialize(component)) -- Creates a component with the given name, params, and runs the initializer after the Component is created via the associated factory. Components are accessed through the fields of the component. + +Component +---------- +A Component is an object containing the behaviors of how a component will work. (TODO: change the terminology on this) Example of creation: + +```javascript +var liquidComponent = Game.defineComponent({ + name: "liquid", + components: { + block: { + mesh: "deform", + collision: "liquid", + notchId: this.notchId + } + }, + properties: { + placementObstacle: false, + notchId: 1 + } +}); +``` + +By defining a component as so, a new method in Game is created. This method would be `Game.register[Component]` For example, a component named �liquid� would now be able to be created with the `Game.registerLiquid` method. + +The `Game.register` method has two forms which are described in `Game`. + +Game +---------- +The Game object represents the state of the engine. It is an abstraction over the underlying engine. It can be accessed through the global variable �game�. There is one Game object per mod; one scope per mod. + +### Methods + +* boolean addEventListener(name, callback) -- Adds an event listener to the specified event with the specified callback. The name should be something like �item_use�; the first Component of the name is the type of event, and the second is the actual action of the event. + +* Player getPlayer(name) -- Gets a Player by their name. + +* Event fireEvent(event) -- Calls an event. Events look like this: { type: �Event name�, myvar: �blah� }. + +* Composite register(name, components) -- Registers a composite with the mod under the given name with the given components. Components is an object formatted as so: `{ component: { property: value }, component2: {}}�` + +* Composite registerComposite(type, name, properties) -- Registers a composite with the mod under the given name with the given properties. The properties are the properties of the given component. The composite created is a composite consisting of one component -- the component given. + +* Composite registerComposite(type, name, properties, components) -- Registers a composite with the mod under the given name with the given properties and components. The properties are the properties of the given component. The composite created is a composite consisting of one component -- the component given. Components is an object formatted as so: `{ component: { property: value }, component2: {}}` These components override any other components if they are set. + +Command +---------- +A command. + +### Properties +* String[] aliases -- Potential aliases for the command. + +Item +---------- +An item. + +Block < Item +---------- + +Entity +---------- + + + + + +Objects + + +Game + +The Game object represents the engine state. In fact an alternate name for this object could be �Engine�. + +Methods +addBlock( /*block definition */ ) +Adds a new type of block to the game. A reference to the new block type is returned. + +addItem( /*Item defintion*/ ) +Adds a new type of item to the game. A reference to the new item type is returned. + +addEvent(EventName, Callback) +Add a callback to be executed when a particular event occurs + +addBinding( /* keybind definition */ ) +Adds a new event that the user can bind to a key. + +Events +* Game events (new, load, start, save, quit) +* Window Events (resize, minimize, restore, maximize, getfocus, losefocus) +* World events (enter, leave) +* Chunk events (load, unload) +* UI Events (mousedown, mouseup, click, dblclick, mousemove, mouseover, mouseout, keydown, keyup) +* Block events (create, place, added, remove, interact) +* Item events (place, pickup, drop, interact) + +Window + +The Window object represents the viewport into the world. There could potentially be multiple windows for the same game if the screen is split, or another window is opened. + +doMouse(MouseEvent) +Simulate a mouse event as if enacted by the player. + +doKey(KeyEvent) +Simulate a keyboard event as if enacted by the player. + +setCamera( /* positioning information */ ) +Change the camera view somehow. Might be useful for a cutscene, for a deathcam, for switch to 3rd person, to simulate optics (telescope, binoculars) + + +Events +* Window Events (resize, minimize, restore, maximize, getfocus, losefocus) +* UI Events (mousedown, mouseup, click, dblclick, mousemove, mouseover, mouseout, keydown, keyup) + +World + +The world is the physical collection of blocks that the player navigates through. There could be multiple worlds for the same game (different dimensions, etc.) + +Events +* World events (enter, leave) +* Chunk events (load, unload) +* Block events (create, place, added, remove, interact) + +Region + +A region is a volume of blocks in the World. The purpose of this object is to act as a bounding box for a subset of the World. World modification events that occur within the region are available for capture. This can help a mod writer limit the number of events fired, assuming the mod is able to define a locality for the part of the world it operates in. + +Events +* Chunk events (load, unload) +* Block events (create, place, added, remove, interact) + +Chunk + +A chunk is a special-purpose region that is used for persistence and segmenting. + +Events +* Chunk events (load, unload) +* Block events (create, place, added, remove, interact) + + +Blockset + +A blockset is a collection of blocks for iteration and set operations. + +Events +* Block events (create, place, added, remove, interact) + + +Block + +A block is a physical block located in the world + +Events +* Block events (create, place, added, remove, interact) + +Item + +An Item can be present on the ground in the world, or can be picked up, carried, or used by a player (or npc) + +Events +* Item events (place, pickup, drop, interact) + +UIElement + +A UIElement is used to create custom screens for the player to interact with, for crafting, or NPC dialog, etc. + + + + +Glimpse + +(Note this needs a better name) Imagine a ray cast from the current camera position, and extending out to infinity. The block in which the camera is currently positioned has an exit point for this ray, and every subsequent block has both an entrance and exit point. An object is created when a UI event needs to determine what block(s) the user was pointing at when the event was fired. This object can be used to determine the exact camera positioning, as well as the exact points in a block that the camera is looking at. + +Properties +block - The block. +entry - The facet the ray enters (will be null for the first block in an event) +exit - The facet the ray exits +points - An ordered array of points where the ray �intersects� the block and block contents. This is only two points unless the block contains a collision mesh that does not fill the entire block, in which case it may be an arbitrary number of points. + + +Event + +The event object + +Properties +cancelable - A boolean indicating whether the event is cancelable. +handler - The instance that the event handler was bound to. A single event can be handled by multiple handlers, belonging to one or more different instances, and the handler property may change for each invocation. +selection - The block(s) or facet(s) or item(s) currently selected. (or null if there is no current selection) +target - The instance that the event is targeted upon. This will vary depending on event type. +type - A string indicating event type. + +Methods +stopPropagation() - Stops the propagation of this event to subsequent handlers. + + +UIEvent (extends Event) + +The UIEvent is fired in response to a keyboard or mouse (or joystick? gamepad?) action. The UIEvent object inherits all the properties and methods of the Event object. + +Properties +target - The target of a UIEvent is *always* the Window instance that received the event. + +Methods +nextGlimpse() - Returns the next Glimpse object +nextVisibleGlimpse() - Returns the next Glimpse object for a non-transparent block +nextOpaqueGlimpse() - Returns the next Glimpse object for a non-translucent block + + + +KeyEvent (extends UIEvent) + +The event object + +Properties +screenX - The X coordinate of the mouse pointer on the user�s monitor +screenY - The Y coordinate of the mouse pointer on the user�s monitor +windowX - The X coordinate of the mouse pointer within Window instance that received the event. +windowY - The Y coordinate of the mouse pointer within Window instance that received the event. +ctrlKey - True if the control key was down when the event was fired +shiftKey - True if the shift key was down when the event was fired +altKey - True if the alt key was down when the event was fired +metaKey - True if the meta key was down when the event was fired +button1 - True if the mouse button 1 was down when the event was fired +button2 - True if the mouse button 2 was down when the event was fired +button3 - True if the mouse button 3 was down when the event was fired +button4 - True if the mouse button 4 was down when the event was fired +button5 - True if the mouse button 5 was down when the event was fired +key - A code representing the key that fired the event. + + +MouseEvent + + + +Keybind + +A keybind object is an event that was added to the game at runtime, and the user can fire this event by binding the keybind event to a key, and then pressing the key. + +Methods +press() +Simulate the user pressing the key bound to this keybind. + +Events +activate + + + + +Events + +load(Chunk) + +The load event fires when a chunk is loaded into memory. This can be either by loading it from disk (or over the network), or by generating a previously nonexistent chunk. The primary parameter is the chunk object. +generate(Chunk) + +The generat event fires when a chunk is loaded into memory. This can be either by loading it from disk (or over the network), or by generating a previously nonexistent chunk.The primary parameter is the chunk object. +unload(Chunk) + +The unload event fires when a chunk is flushed from memory. The primary parameter is the chunk object. +place(ItemInstance) + +The placed event fires when an item in the player inventory is is being placed into the game world. Returning �false� from the callback will cancel the placement of the item, and the item will not leave the player�s inventory. The primary parameter is the item object. +create(BlockType) + +The placed event fires when a block has been created to a mechanism other than the player placing an object from player inventory. Returning �false� from the callback will cancel the creation of the block. The primary parameter is the type object of the block being created. +added(BlockInstance) + +The placed event fires after a block has added to the world. The primary parameter is the newly placed block instance. +remove(BlockInstance) + +The placed event fires before a block has been removed from the world. Returning �false� from the callback will cancel the removal of the block. The primary parameter is the newly placed block instance. + +interact(BlockInstance) + +The interact event fires when the UI decides a player (or npc) is interacting with an �interactable� block. This might be a button or switch or door. The primary parameter is the block instance being interacted with. + +mousedown(MouseEvent) + +mouseup(MouseEvent) + +click(MouseEvent) + +dblclick(MouseEvent) + +mousemove(MouseEvent) + +mouseover(MouseEvent) + +mouseout(MouseEvent) + + + +mousedown, mouseup and click,dblclick, mousemove and finally mouseover and mouseout. +interact + +mouse + +key + +click +key + +place +destroy +interact + +Chunk + +MouseEvent + +Region + +Item + +Block + +MouseEvent + +UI +pickup +drop + + + +// Game.addBlock accepts an object, reads the properties and creates a new +// block type and adds it to the game world. It returns a reference to the +// newly created block type. + +Torch = Game.addBlock({ + name: "Torch", + placedAs: "wallItem", + model: "litTorch" +}); + +UnlitTorch = Game.addBlock({ + name: "Burnt Out Torch", + placedAs: "wallItem", + model: "unlitTorch" +}); + +// Blocks have a number of event handlers. Here, we are referencing the +// "onPlace" event handler, called when a player (or NPC) places this +// block + +Torch.addEvent("onPlace", function(){ + // Note that "this" refers to the current torch being placed + this.luminescence = 15; + this.fuel = 256; +}); + +// BlockSet is a Rhino Java Host Object that can contain arbitrary sets of +// blocks. It exists for the convenience of the modder for easy membership +// testing and iteration. +torches = new BlockSet(); + + +// The Game object also has a number of event handlers, "loadChunk" is called +// every time a chunk is loaded into the game world (either by being newly +// created, or by being loaded from network or disk) +Game.addEvent("loadChunk", function() { + // Note that "this" refers to the current chunk being loaded + + // this.blocks returns a read-only BlockSet + blocks = this.blocks; + for (var i = 0; i < blocks.length; i++) { + if (blocks.typeId == Torch.typeId) { + torches.add(block); + } + } +}); + +Game.addEvent("loadChunk", function() { + // BlockSet.removeAll removes all blocks from the blockset that are also + // present in the collection passed as a parameter. + torches.removeAll(this.blocks); +}); + +// setTimeout and setInterval work exactly as expected +setInterval(function() { + for (var i = 0; i < torches.length; i++) { + torch = torches[i]; + torch.fuel--; + torch.luminescence = Math.max(torch.fuel, 15); + if(torch.fuel == 0) { + torches.remove(torch); + torch.replaceWith(UnlitTorch); + } + } +}, 100000); + + + +Game.addEvent("onClick", function() { + // Note that "this" refers to the current mouse event + + if (this.button == 2) { + this.relatedTarget.break(); + } +}); + +// Block is the root +Block + + + + +// Add a cubbyhole to represent the head equipment slot associated with the +// player +var headCub = game.createCubbyhole(); + +// adding the new cubbyhole to the player assigns the contents of the cubbyhole +// to the player for ownership purposes +player.addCubbyhole(headCub); + +// Add an event handler that fires when an item is dropped into this cubbyhole +headCub.addEvent("drop", function(item) { + + // We test to make sure that any item dropped here must have a head slot + // property + if (item.slot != "head") { + + // returning false disallows the drop event. + return false; + } + + // if the item fits in this slot, have the player equip the item. Note that + // we are returning the value from the equip event, so if the equip event + // handler prevents the equip action, we also prevent the item from being + // dropped in this cubbyhole + return player.equip(this); +}); + +// Similarly, add a cubbyhole for the chest +var chestCub = game.createCubbyhole(); +player.addCubbyhole(chestCub); +headCub.addEvent("drop", function(item) { + if (item.slot != "chest") { + return false; + } + return player.equip(this); +}); + +// and legs +var legCub = game.createCubbyhole(); +player.addCubbyhole(legCub); +headCub.addEvent("drop", function(item) { + if (item.slot != "leg") { + return false; + } + return player.equip(this); +}); + +// and feet +var feetCub = game.createCubbyhole(); +player.addCubbyhole(feetCub); +headCub.addEvent("drop", function(item) { + if (item.slot != "feet") { + return false; + } + return player.equip(this); +}); + +// We need a global variable to store the currently equipped item +var equipped = 1; + +// Now we create cubbyholes for the quickslots 1 through 0 +var equipCubs = []; +for (var int i = 0; i <= 9; i++) { + + // execute the following in a closure to caputre the value of i + function(i) { + // create a new cubbyhole, and associate it with the player + var cub = game.createCubbyhole(); + player.addCubbyhole(cub); + + // Add a new keybinding to the game engine. When this keybinding is + // invoked the item in the associated cubbyhole will be equipped. The + // default keys to bind to this actions are the number keys through 0 + var bind = game.createBinding("Equip Item " + i, i); + bind.addEvent("invoke", function() { + player.equip(cub); + equipped = i; + }); + + equipCubs[i] = cub; + }(i); +} + +// when the mousewheel is scrolled up, switch to the previous item +game.addEvent("mousescrollup", function() { + eqippped = --equipped % 10; + player.equip(equipCubs[equipped]) +}); + +// when the mousewheel is scrolled down, switch to the next item +game.addEvent("mousescrolldown", function() { + eqippped = ++equipped % 10; + player.equip(equipCubs[equipped]) +}); + + +// Let's add the 4 craft cubbyholes +var craftCubs = []; +for (var int i = 0; i < 4; i++) { + var cub = game.createCubbyhole(); + player.addCubbyhole(cub); + craftCubs[i] = cub; +} + + +// And finally we make the inventory cubbyholes +var invCubs = []; +for (var int i = 0; i < 40; i++) { + var cub = game.createCubbyhole(); + player.addCubbyhole(cub); + invCubs[i] = cub; +} + + +// Now we need to build the UI. We start by creating all of the individual +// panels, that will be assembled into the full interface + +// create a UI panel to hold the 4 armor cubbyholes +var armorPanel = new LayoutGrid(4, 1); +armorPanel.append(headCub); +armorPanel.append(chestCub); +armorPanel.append(legCub); +armorPanel.append(feetCub); + +// create a UI panel to hold the 4 crafting input cubbyholes +var craftInputPanel = new LayoutGrid(2, 2); +for (var int i = 0; i < 4; i++) { + craftInputPanel.append(craftCubs[i]); +} + +// create a UI panel to hold the crafting output cubbyhole +var craftOutputPanel = new LayoutGrid(1, 1); +craftOutputPanel.append(Crafting.createOutputCubby(craftCubs, 2)); + +// create a UI panel to hold the player inventory +var invPanel = new LayoutGrid(4, 10); +for (var int i = 0; i < 40; i++) { + invPanel.append(invCubs[i]); +} + +// create a UI panel to hold the player equipment quick slots +var equipPanel = new LayoutGrid(1, 10); +for (var int i = 0; i < 10; i++) { + equipPanel.append(equipCubs[i]); +} + +// arrange the invididual items at the top of the UI into a single panel +var topPanel = new LayoutGrid(1,5); +topPanel.append(armorPanel); +topPanel.append(player.getModelPanel()); +topPanel.append(craftInputPanel); +topPanel.append("arrow.png"); +topPanel.append(craftOutputPanel); + +// and finally arrange everything into the final layout +var charPanel = new LayoutGrid(3,1); +charPanel.append(topPanel); +charPanel.append(invPanel); +charPanel.append(equipPanel); + +// create a key binding for the inventory screen, and we're done! +var bind = game.createBinding("Show character", "e"); +bind.addEvent("invoke", function() { + charPanel.show(); +}); diff --git a/ivy.xml b/ivy.xml new file mode 100644 index 0000000..1a4aa14 --- /dev/null +++ b/ivy.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/org/ironchefpython/modapi/AbstractDynamicProperty.java b/src/org/ironchefpython/modapi/AbstractDynamicProperty.java new file mode 100644 index 0000000..380b5c2 --- /dev/null +++ b/src/org/ironchefpython/modapi/AbstractDynamicProperty.java @@ -0,0 +1,19 @@ +package org.ironchefpython.modapi; + +import org.ironchefpython.modapi.error.PropertyError; + +public abstract class AbstractDynamicProperty implements DynamicProperty { + public DynamicProperty cloneWith(Object object) throws PropertyError { + if (object == null) { + return this; + } + // FIXME (return a component or child component based on the + // string passed) + throw new NoSuchMethodError(); + } + + public Object getValue() { + // FIXME produce a better result for a null value + throw new NoSuchMethodError(); + } +} diff --git a/src/org/ironchefpython/modapi/ActivateEvent.java b/src/org/ironchefpython/modapi/ActivateEvent.java new file mode 100644 index 0000000..26a6cf9 --- /dev/null +++ b/src/org/ironchefpython/modapi/ActivateEvent.java @@ -0,0 +1,16 @@ +package org.ironchefpython.modapi; + +import org.mockengine.Entity; +import org.mockengine.PlayerState; + +public class ActivateEvent { + private PlayerState playerState; + private Entity target; + + public ActivateEvent(PlayerState pstate, Entity target) { + this.playerState = pstate; + this.target = target; + } + + +} diff --git a/src/org/ironchefpython/modapi/CustomEvent.java b/src/org/ironchefpython/modapi/CustomEvent.java new file mode 100644 index 0000000..dfaccaa --- /dev/null +++ b/src/org/ironchefpython/modapi/CustomEvent.java @@ -0,0 +1,38 @@ +package org.ironchefpython.modapi; + +import java.util.Map; + + +import org.mockengine.Entity; +import org.mockengine.Event; +import org.mockengine.EventTarget; + + +public class CustomEvent implements Event { +// public CustomEvent(EventManager ) + + private EventTarget target; + private String type; + private Map params; + + public CustomEvent(String type, Map properties, Map params) { + this.type = type; + this.params = params; + } + + public EventTarget getCurrentTarget() { + return target; + } + + public String getType() { + return type; + } + + public Object get(String name) { + return params.get(name); + } + + public Entity getTarget() { + return null; + } +} diff --git a/src/org/ironchefpython/modapi/CustomEventFactory.java b/src/org/ironchefpython/modapi/CustomEventFactory.java new file mode 100644 index 0000000..335ab97 --- /dev/null +++ b/src/org/ironchefpython/modapi/CustomEventFactory.java @@ -0,0 +1,25 @@ +package org.ironchefpython.modapi; + +import java.util.*; + +import org.mockengine.Event; + +public class CustomEventFactory implements EventFactory { + private Map properties; + private String type; + + public CustomEventFactory(String type, Map properties) { + this.type = type; + this.properties = properties; + } + + public Event makeEvent(Map params) { + return new CustomEvent(type, properties, params); + } + + @Override + public String getType() { + return type; + } + +} diff --git a/src/org/ironchefpython/modapi/DynamicProperty.java b/src/org/ironchefpython/modapi/DynamicProperty.java new file mode 100644 index 0000000..d255a91 --- /dev/null +++ b/src/org/ironchefpython/modapi/DynamicProperty.java @@ -0,0 +1,16 @@ +package org.ironchefpython.modapi; + +import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.NotFoundException; + +import org.ironchefpython.modapi.error.PropertyError; + +public interface DynamicProperty { + + public DynamicProperty cloneWith(Object object) throws PropertyError; + public Object getValue(); + public void addToClass(String key, CtClass comp) throws CannotCompileException, NotFoundException; + public Class getType(); + +} diff --git a/src/org/ironchefpython/modapi/EventFactory.java b/src/org/ironchefpython/modapi/EventFactory.java new file mode 100644 index 0000000..bb65ae6 --- /dev/null +++ b/src/org/ironchefpython/modapi/EventFactory.java @@ -0,0 +1,10 @@ +package org.ironchefpython.modapi; + +import java.util.Map; + +import org.mockengine.Event; + +public interface EventFactory { + Event makeEvent(Map params); + String getType(); +} diff --git a/src/org/ironchefpython/modapi/JsModManager.java b/src/org/ironchefpython/modapi/JsModManager.java new file mode 100644 index 0000000..9b4e50a --- /dev/null +++ b/src/org/ironchefpython/modapi/JsModManager.java @@ -0,0 +1,254 @@ +package org.ironchefpython.modapi; + + +import java.io.*; +import java.util.*; + +import org.mozilla.javascript.*; +import org.ironchefpython.modapi.error.*; +import org.ironchefpython.modapi.primitives.*; + +import org.mockengine.Engine; +import org.mockengine.Entity; +import org.mockengine.Event; +import org.mockengine.Handler; + +public class JsModManager extends ModManager { + private static final String INIT_SUFFIX = "!!INIT"; + private Context cx; + private Scriptable scope; + private Map methods; + + + public JsModManager(Engine game, String modName) { + super(game, modName); + cx = Context.enter(); + scope = cx.initStandardObjects(); + + ScriptableObject.putProperty(scope, "console", Context.javaToJS(new Console(), scope)); + ScriptableObject.putProperty(scope, "manager", Context.javaToJS(new Facade(), scope)); + + methods = new HashMap(); + } + + public void runScript(InputStream is, String name) throws IOException { + cx.compileReader(new InputStreamReader(is), name, 0, null).exec(cx, scope); + } + + public void runScript(File f) throws IOException { + cx.compileReader(new FileReader(f), f.getName(), 0, null).exec(cx, scope); + } + + public void exec(String s) { + cx.evaluateString(scope, s, "", 0, null); + } + + public class Console { + public void log(String s) { + System.err.println("js> " + s); + } + } + + public Object callMethod(Object obj, String name) { + return methods.get(obj.getClass().getName()+'!'+name).call(cx, scope, (Scriptable) Context.javaToJS(obj, scope), null); + } + + public void callInitializer(Object obj) { +System.out.println(obj.getClass().getName()+INIT_SUFFIX); +System.out.println(methods.keySet()); + methods.get(obj.getClass().getName()+INIT_SUFFIX).call(cx, scope, (Scriptable) Context.javaToJS(obj, scope), null); + } + + public class Facade { + @SuppressWarnings("unchecked") + public EventFactory registerEvent(ScriptableObject jsObject) throws InvalidEventRegistration { +//System.out.println(JSON.stringify(cx, scope, jsObject, null, null)); + + String type = (String) jsObject.get("type"); + Map properties = parseProperties((Map) jsObject.get("properties")); + EventFactory result = new CustomEventFactory(type, properties); + return JsModManager.this.registerEvent(result); + } + + public Prototype registerPrototype(ScriptableObject jsObject) throws GeneralModdingException { + return registerPrototype(null, jsObject); + } + + + @SuppressWarnings("unchecked") + public Prototype registerPrototype(Prototype prototype, ScriptableObject jsObject) throws GeneralModdingException { + String id = (String) jsObject.get("id"); + Map properties = new HashMap(); + + // If we have an explicit parent, the top-level jsObject is used to clone the properties. + if (prototype != null) { + properties.putAll(cloneProperties(prototype.getPropertyMap(), (Map) jsObject)); + } + + // For every parent component included in the component definition, + // clone the properties into a collection and modify this collection + // based on the jsobject provided for each collection + Map includes = (Map) jsObject.get("includes"); + if (includes != null) { + for (Map.Entry e1 : includes.entrySet()) { + properties.putAll(cloneProperties(getPrototype(e1.getKey()).getPropertyMap(), (Map) e1.getValue())); + } + } + + // At this point properties will have cloned version of the included + // properties, now we layer the defined properties on top of it. + + + properties.putAll(parseProperties((Map) jsObject.get("properties"))); + + Prototype result = new Prototype(id); + for (Map.Entry e : properties.entrySet()) { + + DynamicProperty p = e.getValue(); + String name = e.getKey(); + result.addProperty(name, p); + if (p instanceof CalculatedProperty) { + methods.put(id+"Component!"+name, (Callable) p.getValue()); + } + } + + + Map listeners = (Map) jsObject.get("listeners"); + + + + if (listeners != null) { + for (Map.Entry e : listeners.entrySet()) { + Handler handler = new JsHandler(e.getValue()); + result.addEventListener(e.getKey(), handler); + } + } + + Prototype.ConstructorParams constructor = (Prototype.ConstructorParams) jsObject.get("constructor"); +// getConstructorSig(constructor, properties); + result.addConstructor(constructor); + methods.put(id+"Component"+INIT_SUFFIX, constructor.getInitializer()); + return JsModManager.this.registerPrototype(result); + } + + public Prototype.ConstructorParams makeConstructor(String[] provided, Callable initializer) { + return new Prototype.ConstructorParams(provided, initializer); + } + + public DynamicProperty getStringType() { + return StringProperty.STRING_TYPE; + } + + public DynamicProperty getNumberType() { + return NumberProperty.NUMBER_TYPE; + } + + public DynamicProperty getBooleanType() { + return BooleanProperty.BOOLEAN_TYPE; + } + + public DynamicProperty getColorType() { + return ColorProperty.COLOR_TYPE; + } + + public DynamicProperty getFunctionType() { + return FunctionProperty.FUNCTION_TYPE; + } + + + public DynamicProperty getTextureType() { + return TextureProperty.TEXTURE_TYPE; + } + + public DynamicProperty calculatedType(DynamicProperty type, Callable callable) { + return new CalculatedProperty(type, callable); + } + + + + public EventFactory getEvent(String name) throws UnregisteredEventException { + return JsModManager.this.getEvent(name); + } + + public Prototype getPrototype(String name) + throws InvalidComponentRegistration { + return JsModManager.this.getPrototype(name); + } + + public Prototype[] getPrototypes() { + return JsModManager.this.getPrototypes(); + } + } + + +// private Class[] getConstructorSig(NativeFunction obj, +// Map properties) { +// +// try { +// Method getParamCount = obj.getClass().getDeclaredMethod( +// "getParamCount"); +// getParamCount.setAccessible(true); +// Method getParamOrVarName = obj.getClass().getDeclaredMethod( +// "getParamOrVarName", int.class); +// getParamOrVarName.setAccessible(true); +// int count = (Integer) getParamCount.invoke(obj); +// Class[] result = new Class[count]; +// for (int i = 0; i < count; i++) { +// String name = (String) getParamOrVarName.invoke(obj, i); +// result[i] = properties.get(name).getType(); +// } +// return result; +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// } + + private Map parseProperties(Map source) { + Map result = new HashMap(); + if (source != null) { + for (Map.Entry e: source.entrySet()) { + result.put(e.getKey(), makeProperty(e.getValue())); + } + } + return result; + } + + + private Map cloneProperties( + Map source, Map jsObject) + throws PropertyError { + Map result = new HashMap(); + for (Map.Entry e : source.entrySet()) { + String propName = e.getKey(); + result.put(propName, e.getValue().cloneWith(jsObject.get(propName))); + } + return result; + } + + private DynamicProperty makeProperty(Object def) { + if (def instanceof DynamicProperty) { + return (DynamicProperty) def; + } else if (def instanceof Callable) { + return new FunctionProperty((Callable) def); + } + throw new NoSuchMethodError(); + } + + + + public class JsHandler implements Handler { + private Callable function; + + public JsHandler(Callable f) { + this.function = f; + } + + public void handleEvent(Event event, Entity currentTarget) { + Scriptable thisObj = null; // get this from currentTarget + function.call(cx, scope, thisObj, new Object[]{event}); + + } + + + } +} diff --git a/src/org/ironchefpython/modapi/ModManager.java b/src/org/ironchefpython/modapi/ModManager.java new file mode 100644 index 0000000..d0d23a6 --- /dev/null +++ b/src/org/ironchefpython/modapi/ModManager.java @@ -0,0 +1,109 @@ +package org.ironchefpython.modapi; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; + + +import org.ironchefpython.modapi.error.GeneralModdingException; +import org.ironchefpython.modapi.error.InvalidComponentRegistration; +import org.ironchefpython.modapi.error.InvalidEventRegistration; +import org.ironchefpython.modapi.error.PropertyError; +import org.ironchefpython.modapi.error.UnregisteredEventException; +import org.ironchefpython.modapi.primitives.BooleanProperty; +import org.ironchefpython.modapi.primitives.ColorProperty; +import org.ironchefpython.modapi.primitives.FunctionProperty; +import org.ironchefpython.modapi.primitives.NumberProperty; +import org.ironchefpython.modapi.primitives.StringProperty; +import org.ironchefpython.modapi.primitives.TextureProperty; +import org.mockengine.*; +import org.mozilla.javascript.*; + + + +public class ModManager { + + private Map prototypes; + private Map events; + private Engine game; + private String name; + private static Map mods = new HashMap(); + + + + public static final String[] STRING_ARRAY = new String[0]; + + public ModManager(Engine game, String modName) { + this.name = modName; + mods.put(name, this); + this.game = game; + prototypes = new HashMap(); + events = new HashMap(); + } + + public Engine getGame() { + return game; + } + + public EventFactory registerEvent(EventFactory factory) throws InvalidEventRegistration { + String type = factory.getType(); + if (events.containsKey(type)) { + throw new InvalidEventRegistration(type); + } + events.put(type, factory); + return factory; + } + + public EventFactory getEvent(String name) throws UnregisteredEventException { + EventFactory result = events.get(name); + if (result == null) { + throw new UnregisteredEventException(name); + } + return result; + } + + + public Prototype getPrototype(String name) throws InvalidComponentRegistration { + Prototype result = prototypes.get(name); + if (result == null) { + throw InvalidComponentRegistration.unregisteredPrototype(name); + } + return result; + } + + + + + + + public Prototype registerPrototype(Prototype prototype) throws InvalidComponentRegistration { + String id = prototype.getId(); + if (prototypes.containsKey(id)) { + throw InvalidComponentRegistration.duplicateRegistration(id); + } + prototypes.put(id, prototype); + return prototype; + } + + public Prototype[] getPrototypes() { + return prototypes.values().toArray(new Prototype[prototypes.size()]); + } + + + + + + + +//void makeTools(ModManager m, Prototype material) { +// String id = material.getId() + "_axe"; +// Prototype axeWithMat = new Prototype(id, m.getPrototype("Axe")); +// axeWithMat.setProperty("material", material); +// m.registerPrototype(axeWithMat); +//} + + + + + +} diff --git a/src/org/ironchefpython/modapi/Prototype.java b/src/org/ironchefpython/modapi/Prototype.java new file mode 100644 index 0000000..0ca3c5c --- /dev/null +++ b/src/org/ironchefpython/modapi/Prototype.java @@ -0,0 +1,119 @@ +package org.ironchefpython.modapi; + +import java.util.*; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; + +import org.ironchefpython.modapi.Prototype.ConstructorParams; +import org.ironchefpython.modapi.error.GeneralModdingException; +import org.ironchefpython.modapi.error.InvalidComponentRegistration; +import org.ironchefpython.modapi.primitives.PrototypeProperty; +import org.ironchefpython.modapi.primitives.CalculatedProperty.CallablePointer; +import org.mockengine.*; +import org.mozilla.javascript.Callable; +import org.mozilla.javascript.ScriptableObject; + +public class Prototype implements EventTarget { + + + private String id; + private Map properties; + private DynamicProperty type; + private Constructor constructor; + + public Prototype(String id, Map properties, + Map listeners, Collection includes) + throws InvalidComponentRegistration { + this(id); + if (properties != null) { + this.properties.putAll(properties); + } + + } + + public Prototype(String id) throws InvalidComponentRegistration { + if (id == null) { + throw InvalidComponentRegistration.missingId(); + } + this.id = id; + this.properties = new HashMap(); + type = new PrototypeProperty.PrototypeType(this); + } + + public String getId() { + return id; + } + + public void addProperty(String propName, DynamicProperty prop) { + properties.put(propName, prop); + } + + public void addEventListener(String type, Handler handler) { + // TODO Auto-generated method stub + } + + public Collection getProperties() { + // TODO Auto-generated method stub + return null; + } + + public Object getPropertyValue(String propertyName) { +//System.out.println(properties.keySet()); + return properties.get(propertyName).getValue(); + } + + public Map getPropertyMap() { + // TODO make sure this is immutable. + return properties; + } + + public DynamicProperty getType() { + + return this.type; + } + + public interface EventReceiver { + public void onEvent(T event, Entity entity); + } + + + public void addConstructor(ConstructorParams params) { + this.constructor = new Constructor(params); + } + + public Constructor getConstructor() { + return constructor; + } + + public static class ConstructorParams { + protected String[] provided; + protected Callable initializer; + public ConstructorParams(String[] provided, Callable initializer) { + this.provided = provided; + this.initializer = initializer; + } + public Callable getInitializer() { + return initializer; + } + } + + public class Constructor extends ConstructorParams { + public Constructor(ConstructorParams params) { + super(params.provided, params.initializer); + } + + public CtClass[] getParamClasses() throws NotFoundException { + CtClass[] result = new CtClass[provided.length]; + for (int i = 0; i < provided.length; i++) { + result[i] = ClassPool.getDefault().get(properties.get(provided[i]).getType().getCanonicalName()); + }; + return result; + } + + public String[] getProvided() { + return provided; + } + } +} diff --git a/src/org/ironchefpython/modapi/error/GeneralModdingException.java b/src/org/ironchefpython/modapi/error/GeneralModdingException.java new file mode 100644 index 0000000..928d299 --- /dev/null +++ b/src/org/ironchefpython/modapi/error/GeneralModdingException.java @@ -0,0 +1,9 @@ +package org.ironchefpython.modapi.error; + +public class GeneralModdingException extends Exception { + public GeneralModdingException(String message) { + super(message); + } + + private static final long serialVersionUID = 1L; +} diff --git a/src/org/ironchefpython/modapi/error/InvalidComponentRegistration.java b/src/org/ironchefpython/modapi/error/InvalidComponentRegistration.java new file mode 100644 index 0000000..f221661 --- /dev/null +++ b/src/org/ironchefpython/modapi/error/InvalidComponentRegistration.java @@ -0,0 +1,31 @@ +package org.ironchefpython.modapi.error; + +public class InvalidComponentRegistration extends GeneralModdingException { + private static final long serialVersionUID = 1L; + + public InvalidComponentRegistration(String message) { + super(message); + } + + public static InvalidComponentRegistration duplicateRegistration(String id) { + return new InvalidComponentRegistration( + "Component already registered with name '" + id + "'"); + } + + public static InvalidComponentRegistration missingId() { + return new InvalidComponentRegistration( + "Component requires id property"); + } + + public static InvalidComponentRegistration unregisteredComponent(String id) { + return new InvalidComponentRegistration( + "No Component registered with id '" + id + "'"); + } + + public static InvalidComponentRegistration unregisteredPrototype(String id) { + return new InvalidComponentRegistration( + "No Prototype registered with id '" + id + "'"); + } + + +} diff --git a/src/org/ironchefpython/modapi/error/InvalidEventRegistration.java b/src/org/ironchefpython/modapi/error/InvalidEventRegistration.java new file mode 100644 index 0000000..e9a4870 --- /dev/null +++ b/src/org/ironchefpython/modapi/error/InvalidEventRegistration.java @@ -0,0 +1,9 @@ +package org.ironchefpython.modapi.error; + +public class InvalidEventRegistration extends GeneralModdingException { + private static final long serialVersionUID = 1L; + + public InvalidEventRegistration(String type) { + super("Event already registered with type '" + type + "'"); + } +} diff --git a/src/org/ironchefpython/modapi/error/NoSuchEventFactory.java b/src/org/ironchefpython/modapi/error/NoSuchEventFactory.java new file mode 100644 index 0000000..7f84394 --- /dev/null +++ b/src/org/ironchefpython/modapi/error/NoSuchEventFactory.java @@ -0,0 +1,10 @@ +package org.ironchefpython.modapi.error; + + +public class NoSuchEventFactory extends GeneralModdingException { + private static final long serialVersionUID = 1L; + + public NoSuchEventFactory(String name) { + super("No Event Factory registered with name '" + name + "'"); + } +} diff --git a/src/org/ironchefpython/modapi/error/PropertyError.java b/src/org/ironchefpython/modapi/error/PropertyError.java new file mode 100644 index 0000000..c40d8f6 --- /dev/null +++ b/src/org/ironchefpython/modapi/error/PropertyError.java @@ -0,0 +1,14 @@ +package org.ironchefpython.modapi.error; + +public class PropertyError extends GeneralModdingException { + private static final long serialVersionUID = 1L; + + public PropertyError(String message) { + super(message); + } + + public static PropertyError IllegalAccessException(String cls) { + return new PropertyError( + "Cannot change the value of a '" + cls + "' instance"); + } +} diff --git a/src/org/ironchefpython/modapi/error/UnregisteredEventException.java b/src/org/ironchefpython/modapi/error/UnregisteredEventException.java new file mode 100644 index 0000000..ab4dde8 --- /dev/null +++ b/src/org/ironchefpython/modapi/error/UnregisteredEventException.java @@ -0,0 +1,9 @@ +package org.ironchefpython.modapi.error; + +public class UnregisteredEventException extends Exception { + private static final long serialVersionUID = 1L; + + public UnregisteredEventException(String id) { + super("No Event registered with id '" + id + "'"); + } +} diff --git a/src/org/ironchefpython/modapi/primitives/BooleanProperty.java b/src/org/ironchefpython/modapi/primitives/BooleanProperty.java new file mode 100644 index 0000000..f2f0c8e --- /dev/null +++ b/src/org/ironchefpython/modapi/primitives/BooleanProperty.java @@ -0,0 +1,63 @@ +package org.ironchefpython.modapi.primitives; + +import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.CtField; +import javassist.Modifier; + +import org.ironchefpython.modapi.AbstractDynamicProperty; +import org.ironchefpython.modapi.DynamicProperty; +import org.ironchefpython.modapi.error.PropertyError; + +public class BooleanProperty implements DynamicProperty { + public static DynamicProperty BOOLEAN_TYPE = new AbstractDynamicProperty() { + public DynamicProperty cloneWith(Object object) { + return (object == null ? this : new BooleanProperty(Boolean.valueOf(object.toString()))); + } + + public void addToClass(String key, CtClass comp) throws CannotCompileException { + CtField f = new CtField(CtClass.booleanType, key, comp); + comp.addField(f); + } + + public Class getType() { + return boolean.class; + } + + + + }; + + private boolean value; + + public BooleanProperty(boolean bool) { + this.value = bool; + } + + public DynamicProperty cloneWith(Object object) throws PropertyError { + if (object != null) { + throw PropertyError.IllegalAccessException(BooleanProperty.class.getName()); + } + return this; + } + + public Object getValue() { + return value; + } + + public void addToClass(String key, CtClass comp) + throws CannotCompileException { + CtField f = new CtField(CtClass.booleanType, key, comp); + f.setModifiers(Modifier.STATIC); + comp.addField(f, CtField.Initializer.constant(value)); + } + + public Class getType() { + return boolean.class; + } + + + + + +} diff --git a/src/org/ironchefpython/modapi/primitives/CalculatedProperty.java b/src/org/ironchefpython/modapi/primitives/CalculatedProperty.java new file mode 100644 index 0000000..c190e72 --- /dev/null +++ b/src/org/ironchefpython/modapi/primitives/CalculatedProperty.java @@ -0,0 +1,76 @@ +package org.ironchefpython.modapi.primitives; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtField; +import javassist.Modifier; +import javassist.NotFoundException; + +import org.ironchefpython.modapi.AbstractDynamicProperty; +import org.ironchefpython.modapi.DynamicProperty; +import org.ironchefpython.modapi.Prototype; +import org.ironchefpython.modapi.error.PropertyError; +import org.mozilla.javascript.Callable; + +public class CalculatedProperty implements DynamicProperty { + + public static class CalculatedType extends AbstractDynamicProperty { + private DynamicProperty type; + public CalculatedType(DynamicProperty type) { + this.type = type; + } + public DynamicProperty cloneWith(Object object) { + // FIXME validate that object is a callable + // + return object == null ? this : new CalculatedProperty(type, (Callable)object); + } + public void addToClass(String key, CtClass comp) throws CannotCompileException, NotFoundException { + throw new NoSuchMethodError(); + } + public Class getType() { + return type.getType(); + } + + } + + private DynamicProperty type; + private Callable callable; + + public CalculatedProperty(DynamicProperty type, Callable callable) { + this.callable = callable; + } + + public DynamicProperty cloneWith(Object object) throws PropertyError { + if (object != null) { + throw PropertyError.IllegalAccessException(CalculatedProperty.class.getName()); + } + return this; + } + + public Object getValue() { + return callable; + } + + public void addToClass(String key, CtClass comp) + throws CannotCompileException, NotFoundException { + CtField f = new CtField(ClassPool.getDefault().get(CallablePointer.class.getName()), key, comp); + f.setModifiers(Modifier.STATIC + Modifier.PUBLIC); + comp.addField(f, CtField.Initializer.byNew(ClassPool.getDefault().get(CallablePointer.class.getName()), new String[]{key})); + } + + + public static class CallablePointer { + String name; + public CallablePointer(String[] args) { + this.name = args[0]; + } + } + + public Class getType() { + return type.getType(); + } + +} + + diff --git a/src/org/ironchefpython/modapi/primitives/ColorProperty.java b/src/org/ironchefpython/modapi/primitives/ColorProperty.java new file mode 100644 index 0000000..ac47eea --- /dev/null +++ b/src/org/ironchefpython/modapi/primitives/ColorProperty.java @@ -0,0 +1,35 @@ +package org.ironchefpython.modapi.primitives; + +import org.ironchefpython.modapi.AbstractDynamicProperty; +import org.ironchefpython.modapi.DynamicProperty; +import org.ironchefpython.modapi.error.PropertyError; + +public class ColorProperty implements DynamicProperty { + public static DynamicProperty COLOR_TYPE = new AbstractDynamicProperty() { + public DynamicProperty cloneWith(Object object) { + return object == null ? this : new ColorProperty(object.toString()); + } + + + }; + + private String value; + + public ColorProperty(String color) { + this.value = color; + } + + public DynamicProperty cloneWith(Object object) throws PropertyError { + if (object != null) { + throw PropertyError.IllegalAccessException(BooleanProperty.class.getName()); + } + return this; + } + + public Object getValue() { + return value; + } + + + +} diff --git a/src/org/ironchefpython/modapi/primitives/FunctionProperty.java b/src/org/ironchefpython/modapi/primitives/FunctionProperty.java new file mode 100644 index 0000000..25b3c5f --- /dev/null +++ b/src/org/ironchefpython/modapi/primitives/FunctionProperty.java @@ -0,0 +1,38 @@ +package org.ironchefpython.modapi.primitives; + +import org.ironchefpython.modapi.AbstractDynamicProperty; +import org.ironchefpython.modapi.DynamicProperty; +import org.ironchefpython.modapi.error.PropertyError; +import org.mozilla.javascript.Callable; + + +public class FunctionProperty implements DynamicProperty { + public static DynamicProperty FUNCTION_TYPE = new AbstractDynamicProperty() { + public DynamicProperty cloneWith(Object object) { + return object == null ? this : new FunctionProperty((Callable)object); + } + + + }; + + private Callable value; + + public FunctionProperty(Callable object) { + this.value = object; + } + + public DynamicProperty cloneWith(Object object) throws PropertyError { + if (object != null) { + throw PropertyError.IllegalAccessException(BooleanProperty.class.getName()); + } + return this; + } + + public Object getValue() { + return value; + } + + + + +} diff --git a/src/org/ironchefpython/modapi/primitives/NumberProperty.java b/src/org/ironchefpython/modapi/primitives/NumberProperty.java new file mode 100644 index 0000000..086d6cb --- /dev/null +++ b/src/org/ironchefpython/modapi/primitives/NumberProperty.java @@ -0,0 +1,64 @@ +package org.ironchefpython.modapi.primitives; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtField; +import javassist.Modifier; +import javassist.NotFoundException; + +import org.ironchefpython.modapi.AbstractDynamicProperty; +import org.ironchefpython.modapi.DynamicProperty; +import org.ironchefpython.modapi.error.PropertyError; + +public class NumberProperty implements DynamicProperty { + public static final Class JAVA_CLASS = Number.class; + public static DynamicProperty NUMBER_TYPE = new AbstractDynamicProperty() { + public DynamicProperty cloneWith(Object object) { + return object == null ? this : new NumberProperty(Float.valueOf(object.toString())); + } + + public void addToClass(String key, CtClass comp) throws CannotCompileException, NotFoundException { + CtField f = new CtField(ClassPool.getDefault().get(JAVA_CLASS.getCanonicalName()), key, comp); + f.setModifiers(Modifier.PUBLIC); + comp.addField(f); + } + + public Class getType() { + return JAVA_CLASS; + } + + + }; + + private float value; + + public NumberProperty(float f) { + this.value = f; + } + + public DynamicProperty cloneWith(Object object) throws PropertyError { + if (object != null) { + throw PropertyError.IllegalAccessException(BooleanProperty.class.getName()); + } + return this; + } + + public Object getValue() { + return value; + } + + public void addToClass(String key, CtClass comp) + throws CannotCompileException, NotFoundException { + CtField f = new CtField(ClassPool.getDefault().get(JAVA_CLASS.getCanonicalName()), key, comp); + f.setModifiers(Modifier.STATIC + Modifier.PUBLIC); + comp.addField(f, CtField.Initializer.constant(value)); + } + + public Class getType() { + return JAVA_CLASS; + } + + + +} diff --git a/src/org/ironchefpython/modapi/primitives/PrototypeProperty.java b/src/org/ironchefpython/modapi/primitives/PrototypeProperty.java new file mode 100644 index 0000000..5b3999e --- /dev/null +++ b/src/org/ironchefpython/modapi/primitives/PrototypeProperty.java @@ -0,0 +1,53 @@ +package org.ironchefpython.modapi.primitives; + +import org.ironchefpython.modapi.AbstractDynamicProperty; +import org.ironchefpython.modapi.DynamicProperty; +import org.ironchefpython.modapi.Prototype; +import org.ironchefpython.modapi.error.PropertyError; +import org.mozilla.javascript.Callable; + + +public class PrototypeProperty implements DynamicProperty { + public static DynamicProperty PROTOTYPE_TYPE = new AbstractDynamicProperty() { + public DynamicProperty cloneWith(Object object) { + return object == null ? this : new PrototypeProperty((Prototype)object); + } + + + }; + + public static class PrototypeType extends AbstractDynamicProperty { + private Prototype type; + public PrototypeType(Prototype type) { + this.type = type; + } + public DynamicProperty cloneWith(Object object) { + // FIXME validate that type is an ancestor of object + return object == null ? this : new PrototypeProperty((Prototype)object); + } + + } + + + + private Prototype value; + + public PrototypeProperty(Prototype object) { + this.value = object; + } + + public DynamicProperty cloneWith(Object object) throws PropertyError { + if (object != null) { + throw PropertyError.IllegalAccessException(PrototypeProperty.class.getName()); + } + return this; + } + + public Object getValue() { + return value; + } + + + + +} diff --git a/src/org/ironchefpython/modapi/primitives/StringProperty.java b/src/org/ironchefpython/modapi/primitives/StringProperty.java new file mode 100644 index 0000000..31e7538 --- /dev/null +++ b/src/org/ironchefpython/modapi/primitives/StringProperty.java @@ -0,0 +1,35 @@ +package org.ironchefpython.modapi.primitives; + +import org.ironchefpython.modapi.AbstractDynamicProperty; +import org.ironchefpython.modapi.DynamicProperty; +import org.ironchefpython.modapi.error.PropertyError; + +public class StringProperty implements DynamicProperty { + public static DynamicProperty STRING_TYPE = new AbstractDynamicProperty() { + public DynamicProperty cloneWith(Object object) { + return object == null ? this : new StringProperty(object.toString()); + } + + }; + + private String value; + + public StringProperty(String string) { + this.value = string; + } + + public DynamicProperty cloneWith(Object object) throws PropertyError { + if (object != null) { + throw PropertyError.IllegalAccessException(BooleanProperty.class.getName()); + } + return this; + } + + public Object getValue() { + return value; + } + + + + +} diff --git a/src/org/ironchefpython/modapi/primitives/TextureProperty.java b/src/org/ironchefpython/modapi/primitives/TextureProperty.java new file mode 100644 index 0000000..dac744b --- /dev/null +++ b/src/org/ironchefpython/modapi/primitives/TextureProperty.java @@ -0,0 +1,36 @@ +package org.ironchefpython.modapi.primitives; + +import org.ironchefpython.modapi.AbstractDynamicProperty; +import org.ironchefpython.modapi.DynamicProperty; +import org.ironchefpython.modapi.error.PropertyError; + +public class TextureProperty implements DynamicProperty { + public static DynamicProperty TEXTURE_TYPE = new AbstractDynamicProperty() { + public DynamicProperty cloneWith(Object object) { + return object == null ? this : new TextureProperty(object); + } + + + }; + + private Object value; + + public TextureProperty(Object object) { + this.value = object; // FIXME do real texture loading + } + + public DynamicProperty cloneWith(Object object) throws PropertyError { + if (object != null) { + throw PropertyError.IllegalAccessException(BooleanProperty.class.getName()); + } + return this; + } + + public Object getValue() { + return value; + } + + + + +} \ No newline at end of file diff --git a/src/org/mockengine/AbstractEventTarget.java b/src/org/mockengine/AbstractEventTarget.java new file mode 100644 index 0000000..7443e2a --- /dev/null +++ b/src/org/mockengine/AbstractEventTarget.java @@ -0,0 +1,39 @@ +package org.mockengine; + + +import java.util.Collection; + +import org.mockengine.*; + + + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +public abstract class AbstractEventTarget implements EventTarget { + private Multimap bubbleEvents; + + public AbstractEventTarget() { + bubbleEvents = ArrayListMultimap.create(); + } + + public Collection getEventHandlers(String name) { + return bubbleEvents.get(name); + } + + public void addEventListener(String type, Handler listener, boolean useCapture) { + if (useCapture) { + throw new UnsupportedOperationException("Capture not implemented yet"); + } + bubbleEvents.put(type, listener); + } + + public void addEventListener(String type, Handler listener) { + addEventListener(type, listener, false); + } + + public void removeEventListener(String type, Handler listener) { + throw new UnsupportedOperationException("Not implemented yet"); + } + +} diff --git a/src/org/mockengine/Component.java b/src/org/mockengine/Component.java new file mode 100644 index 0000000..3f1dc21 --- /dev/null +++ b/src/org/mockengine/Component.java @@ -0,0 +1,16 @@ +package org.mockengine; + +import java.util.Collection; +import java.util.Map; + +import org.ironchefpython.modapi.DynamicProperty; + +public interface Component { + + public String getId(); + public Collection getProperties(); + public Object getPropertyValue(String propertyName); + public Map getPropertyMap(); + public DynamicProperty getType(); + +} diff --git a/src/org/mockengine/Engine.java b/src/org/mockengine/Engine.java new file mode 100644 index 0000000..3e87191 --- /dev/null +++ b/src/org/mockengine/Engine.java @@ -0,0 +1,8 @@ +package org.mockengine; + +import org.ironchefpython.modapi.*; + +public interface Engine extends EventTarget { + + +} diff --git a/src/org/mockengine/Entity.java b/src/org/mockengine/Entity.java new file mode 100644 index 0000000..7df6420 --- /dev/null +++ b/src/org/mockengine/Entity.java @@ -0,0 +1,7 @@ +package org.mockengine; + +import org.ironchefpython.modapi.Prototype; + +public interface Entity extends EventTarget { + public Prototype getType(); +} diff --git a/src/org/mockengine/Event.java b/src/org/mockengine/Event.java new file mode 100644 index 0000000..90fa714 --- /dev/null +++ b/src/org/mockengine/Event.java @@ -0,0 +1,6 @@ +package org.mockengine; + +public interface Event { + public String getType(); + public EventTarget getTarget(); +} diff --git a/src/org/mockengine/EventTarget.java b/src/org/mockengine/EventTarget.java new file mode 100644 index 0000000..9280075 --- /dev/null +++ b/src/org/mockengine/EventTarget.java @@ -0,0 +1,7 @@ +package org.mockengine; + + + +public interface EventTarget { + public void addEventListener(String type, Handler handler); +} diff --git a/src/org/mockengine/Handler.java b/src/org/mockengine/Handler.java new file mode 100644 index 0000000..126ec2b --- /dev/null +++ b/src/org/mockengine/Handler.java @@ -0,0 +1,8 @@ +package org.mockengine; + +public interface Handler { + + void handleEvent(Event event, Entity currentTarget); + + +} diff --git a/src/org/mockengine/MockEngine.java b/src/org/mockengine/MockEngine.java new file mode 100644 index 0000000..e480a0a --- /dev/null +++ b/src/org/mockengine/MockEngine.java @@ -0,0 +1,7 @@ +package org.mockengine; + +import org.ironchefpython.modapi.*; + +public class MockEngine extends AbstractEventTarget implements Engine { + +} diff --git a/src/org/mockengine/Player.java b/src/org/mockengine/Player.java new file mode 100644 index 0000000..bb40bdb --- /dev/null +++ b/src/org/mockengine/Player.java @@ -0,0 +1,5 @@ +package org.mockengine; + +public class Player { + +} diff --git a/src/org/mockengine/PlayerState.java b/src/org/mockengine/PlayerState.java new file mode 100644 index 0000000..f94e005 --- /dev/null +++ b/src/org/mockengine/PlayerState.java @@ -0,0 +1,5 @@ +package org.mockengine; + +public interface PlayerState { + +} diff --git a/src/org/mockengine/event/AbstractEvent.java b/src/org/mockengine/event/AbstractEvent.java new file mode 100644 index 0000000..3b171ea --- /dev/null +++ b/src/org/mockengine/event/AbstractEvent.java @@ -0,0 +1,20 @@ +package org.mockengine.event; + +import org.mockengine.Entity; +import org.mockengine.Event; + + +public abstract class AbstractEvent implements Event { + private final String type; + + public AbstractEvent(String type) { + this.type = type; + } + + @Override + public String getType() { + return type; + } + + +} diff --git a/src/org/mockengine/event/PlayerEvent.java b/src/org/mockengine/event/PlayerEvent.java new file mode 100644 index 0000000..32a2c8b --- /dev/null +++ b/src/org/mockengine/event/PlayerEvent.java @@ -0,0 +1,25 @@ +package org.mockengine.event; + +import org.mockengine.Player; + +/** + * Represents a player-related event. + * @author simplyianm + * + */ +public abstract class PlayerEvent extends AbstractEvent { + private final Player player; + + public PlayerEvent(String type, Player player) { + super("player:" + type); + this.player = player; + } + + /** + * @return the player + */ + public Player getPlayer() { + return player; + } + +} diff --git a/src/org/mozilla/javascript/JSON.java b/src/org/mozilla/javascript/JSON.java new file mode 100644 index 0000000..abba052 --- /dev/null +++ b/src/org/mozilla/javascript/JSON.java @@ -0,0 +1,825 @@ +/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Matthew Crumley + * Raphael Speyer + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import org.mozilla.javascript.json.JsonParser; + +import java.util.Stack; +import java.util.Collection; +import java.util.Map; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Arrays; +import java.util.List; +import java.util.LinkedList; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +import java.lang.reflect.InvocationTargetException; + +/** + * This class implements the JSON native object. + * See ECMA 15.12. + * @author Matthew Crumley, Raphael Speyer + */ +public final class JSON extends IdScriptableObject +{ + static final long serialVersionUID = -4567599697595654984L; + + private static final Object JSON_TAG = "JSON"; + + private static final int MAX_STRINGIFY_GAP_LENGTH = 10; + + static void init(Scriptable scope, boolean sealed) + { + JSON obj = new JSON(); + obj.activatePrototypeMap(MAX_ID); + obj.setPrototype(getObjectPrototype(scope)); + obj.setParentScope(scope); + if (sealed) { obj.sealObject(); } + ScriptableObject.defineProperty(scope, "JSON", obj, + ScriptableObject.DONTENUM); + } + + private JSON() + { + } + + @Override + public String getClassName() { return "JSON"; } + + @Override + protected void initPrototypeId(int id) + { + if (id <= LAST_METHOD_ID) { + String name; + int arity; + switch (id) { + case Id_toSource: arity = 0; name = "toSource"; break; + case Id_parse: arity = 2; name = "parse"; break; + case Id_stringify: arity = 3; name = "stringify"; break; + default: throw new IllegalStateException(String.valueOf(id)); + } + initPrototypeMethod(JSON_TAG, id, name, arity); + } else { + throw new IllegalStateException(String.valueOf(id)); + } + } + + @Override + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(JSON_TAG)) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int methodId = f.methodId(); + switch (methodId) { + case Id_toSource: + return "JSON"; + + case Id_parse: { + String jtext = ScriptRuntime.toString(args, 0); + Object reviver = null; + if (args.length > 1) { + reviver = args[1]; + } + if (reviver instanceof Callable) { + return parse(cx, scope, jtext, (Callable) reviver); + } else { + return parse(cx, scope, jtext); + } + } + + case Id_stringify: { + Object value = null, replacer = null, space = null; + switch (args.length) { + default: + case 3: space = args[2]; + case 2: replacer = args[1]; + case 1: value = args[0]; + case 0: + } + return stringify(cx, scope, value, replacer, space); + } + + default: throw new IllegalStateException(String.valueOf(methodId)); + } + } + + private static Object parse(Context cx, Scriptable scope, String jtext) { + try { + return new JsonParser(cx, scope).parseValue(jtext); + } catch (JsonParser.ParseException ex) { + throw ScriptRuntime.constructError("SyntaxError", ex.getMessage()); + } + } + + public static Object parse(Context cx, Scriptable scope, String jtext, + Callable reviver) + { + Object unfiltered = parse(cx, scope, jtext); + Scriptable root = cx.newObject(scope); + root.put("", root, unfiltered); + return walk(cx, scope, reviver, root, ""); + } + + private static Object walk(Context cx, Scriptable scope, Callable reviver, + Scriptable holder, Object name) + { + final Object property; + if (name instanceof Number) { + property = holder.get( ((Number) name).intValue(), holder); + } else { + property = holder.get( ((String) name), holder); + } + + if (property instanceof Scriptable) { + Scriptable val = ((Scriptable) property); + if (val instanceof NativeArray) { + int len = (int) ((NativeArray) val).getLength(); + for (int i = 0; i < len; i++) { + Object newElement = walk(cx, scope, reviver, val, i); + if (newElement == Undefined.instance) { + val.delete(i); + } else { + val.put(i, val, newElement); + } + } + } else { + Object[] keys = val.getIds(); + for (Object p : keys) { + Object newElement = walk(cx, scope, reviver, val, p); + if (newElement == Undefined.instance) { + if (p instanceof Number) + val.delete(((Number) p).intValue()); + else + val.delete((String) p); + } else { + if (p instanceof Number) + val.put(((Number) p).intValue(), val, newElement); + else + val.put((String) p, val, newElement); + } + } + } + } + + return reviver.call(cx, scope, holder, new Object[] { name, property }); + } + + private static String repeat(char c, int count) { + char chars[] = new char[count]; + Arrays.fill(chars, c); + return new String(chars); + } + + private static class StringifyState { + StringifyState(Context cx, Scriptable scope, String indent, String gap, + Callable replacer, List propertyList, + Object space) + { + this.cx = cx; + this.scope = scope; + + this.indent = indent; + this.gap = gap; + this.replacer = replacer; + this.propertyList = propertyList; + this.space = space; + } + + Stack stack = new Stack(); + Stack jstack = new Stack(); + // consider making completely static for better caching? + HashMap,HashMap> beanMaps = new HashMap,HashMap>(); + HashMap,HashMap> fieldMaps = new HashMap,HashMap>(); + String indent; + String gap; + Callable replacer; + List propertyList; + Object space; + + Context cx; + Scriptable scope; + } + + public static Object stringify(Context cx, Scriptable scope, Object value, + Object replacer, Object space) + { + String indent = ""; + String gap = ""; + + List propertyList = null; + Callable replacerFunction = null; + + if (replacer instanceof Callable) { + replacerFunction = (Callable) replacer; + } else if (replacer instanceof NativeArray) { + propertyList = new LinkedList(); + NativeArray replacerArray = (NativeArray) replacer; + for (int i : replacerArray.getIndexIds()) { + Object v = replacerArray.get(i, replacerArray); + if (v instanceof String || v instanceof Number) { + propertyList.add(v); + } else if (v instanceof NativeString || v instanceof NativeNumber) { + propertyList.add(ScriptRuntime.toString(v)); + } + } + } + + if (space instanceof NativeNumber) { + space = ScriptRuntime.toNumber(space); + } else if (space instanceof NativeString) { + space = ScriptRuntime.toString(space); + } + + if (space instanceof Number) { + int gapLength = (int) ScriptRuntime.toInteger(space); + gapLength = Math.min(MAX_STRINGIFY_GAP_LENGTH, gapLength); + gap = (gapLength > 0) ? repeat(' ', gapLength) : ""; + space = gapLength; + } else if (space instanceof String) { + gap = (String) space; + if (gap.length() > MAX_STRINGIFY_GAP_LENGTH) { + gap = gap.substring(0, MAX_STRINGIFY_GAP_LENGTH); + } + } + + StringifyState state = new StringifyState(cx, scope, + indent, + gap, + replacerFunction, + propertyList, + space); + + ScriptableObject wrapper = new NativeObject(); + wrapper.setParentScope(scope); + wrapper.setPrototype(ScriptableObject.getObjectPrototype(scope)); + wrapper.defineProperty("", value, 0); + return str("", wrapper, state); + } + + private static Object str(Object key, Scriptable holder, + StringifyState state) + { + Object value = null; + if (key instanceof String) { + value = getProperty(holder, (String) key); + } else { + value = getProperty(holder, ((Number) key).intValue()); + } + + if (value instanceof NativeJavaArray) + return jja(((NativeJavaArray) value).unwrap(), state); + if (value instanceof NativeJavaObject) + return jo((Scriptable) value, state); + if (value instanceof Scriptable) { + Object toJSON = getProperty((Scriptable) value, "toJSON"); + if (toJSON instanceof Callable) { + value = callMethod(state.cx, (Scriptable) value, "toJSON", + new Object[] { key }); + } + } + + if (state.replacer != null) { + value = state.replacer.call(state.cx, state.scope, holder, + new Object[] { key, value }); + } + + + if (value instanceof NativeNumber) { + value = ScriptRuntime.toNumber(value); + } else if (value instanceof NativeString) { + value = ScriptRuntime.toString(value); + } else if (value instanceof NativeBoolean) { + value = ((NativeBoolean) value).getDefaultValue(ScriptRuntime.BooleanClass); + } + + if (value == null) return "null"; + if (value.equals(Boolean.TRUE)) return "true"; + if (value.equals(Boolean.FALSE)) return "false"; + + if (value instanceof String) { + return quote((String) value); + } + + if (value instanceof Number) { + double d = ((Number) value).doubleValue(); + if (d == d && d != Double.POSITIVE_INFINITY && + d != Double.NEGATIVE_INFINITY) + { + return ScriptRuntime.toString(value); + } else { + return "null"; + } + } + + if (value instanceof Scriptable && !(value instanceof Callable)) { + if (value instanceof NativeArray) { + return ja((NativeArray) value, state); + } + return jo((Scriptable) value, state); + } + + return Undefined.instance; + } + + private static String join(Collection objs, String delimiter) { + if (objs == null || objs.isEmpty()) { + return ""; + } + Iterator iter = objs.iterator(); + if (!iter.hasNext()) return ""; + StringBuilder builder = new StringBuilder(iter.next().toString()); + while (iter.hasNext()) { + builder.append(delimiter).append(iter.next().toString()); + } + return builder.toString(); + } + + private static String toBeanName(String name) { + if (name == null || name.length() == 0) + return name; + + if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) + && Character.isUpperCase(name.charAt(0))) { + return name; + } + char chars[] = name.toCharArray(); + chars[0] = Character.toLowerCase(chars[0]); + return new String(chars); + } + private static HashMap beanMap(Object o, StringifyState s) { + Class clazz = o.getClass(); + if (s.beanMaps.containsKey(clazz)) + return s.beanMaps.get(clazz); + HashMap clazzBeanMap; + + if (clazz != Class.class) + clazzBeanMap = beanMap(Class.class, s); + else + clazzBeanMap = new HashMap(); + + Method[] methods = clazz.getMethods(); + HashMap beanMap = new HashMap(); + for (Method m : methods) { + if (Modifier.isStatic(m.getModifiers())) + continue; + String name = m.getName(); + if (name.startsWith("is") + && Character.isUpperCase(name.charAt(2))) { + Class rtype = m.getReturnType(); + if ((rtype == Boolean.class || rtype == boolean.class) + && m.getParameterTypes().length == 0) { + String beanProp = toBeanName(name.substring(2)); + if (!clazzBeanMap.containsKey(beanProp)) + beanMap.put(beanProp, m); + } + } + if (name.startsWith("get") + && Character.isUpperCase(name.charAt(3)) + && m.getParameterTypes().length == 0) { + String beanProp = toBeanName(name.substring(3)); + if (!clazzBeanMap.containsKey(beanProp)) + beanMap.put(beanProp, m); + } + } + s.beanMaps.put(clazz, beanMap); + return beanMap; + } + private static HashMap fieldMap(Object o, StringifyState s) { + Class clazz = o.getClass(); + if (s.fieldMaps.containsKey(clazz)) + return s.fieldMaps.get(clazz); + Field[] fields = clazz.getFields(); + HashMap fieldMap = new HashMap(); + for (Field f : fields) { + if (Modifier.isStatic(f.getModifiers())) + continue; + String name = f.getName(); + fieldMap.put(name, f); + } + s.fieldMaps.put(clazz, fieldMap); + return fieldMap; + } + private static String mjo(Map value, StringifyState state) { + if (value == null) + return "null"; + + state.jstack.push(value); + + String stepback = state.indent; + state.indent = state.indent + state.gap; + List partial = new LinkedList(); + + for (Object p : value.keySet()) { + Object strP = jjo(value.get(p), state); + if (strP != null) { + String member = quote(p.toString()) + ":"; + if (state.gap.length() > 0) { + member = member + " "; + } + member = member + strP; + partial.add(member); + } + } + + final String finalValue; + + if (partial.isEmpty()) { + finalValue = "{}"; + } else { + if (state.gap.length() == 0) { + finalValue = '{' + join(partial, ",") + '}'; + } else { + String separator = ",\n" + state.indent; + String properties = join(partial, separator); + finalValue = "{\n" + state.indent + properties + '\n' + + stepback + '}'; + } + } + + state.jstack.pop(); + state.indent = stepback; + return finalValue; + } + private static String jjo(Object value, StringifyState state) { + + if (value == null) + return "null"; + + // serialize a Map as if it were an object with properties + if (value instanceof Map) + return mjo((Map) value, state); + + // serialize it as if it were an array + if (value instanceof Collection) + return cja((Collection) value, state); + + // boolean values and numbers may be unquoted + if (value instanceof Boolean || value instanceof Number) + return value.toString(); + + Class clazz = value.getClass(); + // primitives get away with no quoting + if (clazz.isPrimitive() && clazz != char.class) + return value.toString(); + + // an array + if (clazz.isArray()) + return jja(value, state); + + // assume everything in java.lang.** to be a quoted string + if (clazz.getName().startsWith("java.lang.")) + return quote(value.toString()); + + state.jstack.push(value); + + String stepback = state.indent; + state.indent = state.indent + state.gap; + + Map beanMap = beanMap(value, state); + Map fieldMap = fieldMap(value, state); + Map nameMap = new IdentityHashMap(); + for (String p : beanMap.keySet()) + nameMap.put(beanMap.get(p), p); + + List partial = new LinkedList(); + LinkedList members = new LinkedList(); + + members.addAll(beanMap.values()); + members.addAll(fieldMap.values()); + + for (Member m : members) { + Object beanVal = null; + String name = m.getName(); + try { + if (m instanceof Method) { + beanVal = ((Method) m).invoke(value); + name = nameMap.get((Method) m); + } else if (m instanceof Field) { + beanVal = ((Field) m).get(value); + } else + throw new IllegalArgumentException(m.getClass().getName()); + // remove cyclic references + if (state.jstack.search(beanVal) != -1) + continue; + } + catch (IllegalAccessException e) { continue; } + catch (InvocationTargetException e) { continue; } + Object strP = jjo(beanVal, state); + if (strP != null) { + String member = quote(name) + ":"; + if (state.gap.length() > 0) { + member = member + " "; + } + member = member + strP; + partial.add(member); + } + } + + final String finalValue; + + if (partial.isEmpty()) { + finalValue = "{}"; + } else { + if (state.gap.length() == 0) { + finalValue = '{' + join(partial, ",") + '}'; + } else { + String separator = ",\n" + state.indent; + String properties = join(partial, separator); + finalValue = "{\n" + state.indent + properties + '\n' + + stepback + '}'; + } + } + + state.jstack.pop(); + state.indent = stepback; + return finalValue; + } + + private static String jo(Scriptable value, StringifyState state) { + if (value instanceof NativeJavaObject) { + Object v = ((NativeJavaObject)value).unwrap(); + return jjo(v, state); + } + + if (state.stack.search(value) != -1) { + throw ScriptRuntime.typeError0("msg.cyclic.value"); + } + state.stack.push(value); + + String stepback = state.indent; + state.indent = state.indent + state.gap; + Object[] k = null; + if (state.propertyList != null) { + k = state.propertyList.toArray(); + } else { + k = value.getIds(); + } + + List partial = new LinkedList(); + + for (Object p : k) { + Object strP = str(p, value, state); + if (strP != Undefined.instance) { + String member = quote(p.toString()) + ":"; + if (state.gap.length() > 0) { + member = member + " "; + } + member = member + strP; + partial.add(member); + } + } + + final String finalValue; + + if (partial.isEmpty()) { + finalValue = "{}"; + } else { + if (state.gap.length() == 0) { + finalValue = '{' + join(partial, ",") + '}'; + } else { + String separator = ",\n" + state.indent; + String properties = join(partial, separator); + finalValue = "{\n" + state.indent + properties + '\n' + + stepback + '}'; + } + } + + state.stack.pop(); + state.indent = stepback; + return finalValue; + } + + private static String ja(NativeArray value, StringifyState state) { + if (state.stack.search(value) != -1) { + throw ScriptRuntime.typeError0("msg.cyclic.value"); + } + state.stack.push(value); + + String stepback = state.indent; + state.indent = state.indent + state.gap; + List partial = new LinkedList(); + + int len = (int) value.getLength(); + for (int index = 0; index < len; index++) { + Object strP = str(index, value, state); + if (strP == Undefined.instance) { + partial.add("null"); + } else { + partial.add(strP); + } + } + + final String finalValue; + + if (partial.isEmpty()) { + finalValue = "[]"; + } else { + if (state.gap.length() == 0) { + finalValue = '[' + join(partial, ",") + ']'; + } else { + String separator = ",\n" + state.indent; + String properties = join(partial, separator); + finalValue = "[\n" + state.indent + properties + '\n' + stepback + ']'; + } + } + + state.stack.pop(); + state.indent = stepback; + return finalValue; + } + + private static String jja(Object value, StringifyState state) { + if (state.jstack.search(value) != -1) { + throw ScriptRuntime.typeError0("msg.cyclic.value"); + } + state.jstack.push(value); + + String stepback = state.indent; + state.indent = state.indent + state.gap; + List partial = new LinkedList(); + + int len = Array.getLength(value); + for (int index = 0; index < len; index++) { + Object strP = jjo(Array.get(value, index), state); + if (strP == null) { + partial.add("null"); + } else { + partial.add(strP); + } + } + + final String finalValue; + + if (partial.isEmpty()) { + finalValue = "[]"; + } else { + if (state.gap.length() == 0) { + finalValue = '[' + join(partial, ",") + ']'; + } else { + String separator = ",\n" + state.indent; + String properties = join(partial, separator); + finalValue = "[\n" + state.indent + properties + '\n' + stepback + ']'; + } + } + + state.jstack.pop(); + state.indent = stepback; + return finalValue; + } + private static String cja(Collection value, StringifyState state) { + if (state.jstack.search(value) != -1) { + throw ScriptRuntime.typeError0("msg.cyclic.value"); + } + state.jstack.push(value); + + String stepback = state.indent; + state.indent = state.indent + state.gap; + List partial = new LinkedList(); + + for (Object o : value) { + Object strP = jjo(o, state); + if (strP == null) { + partial.add("null"); + } else { + partial.add(strP); + } + } + + final String finalValue; + + if (partial.isEmpty()) { + finalValue = "[]"; + } else { + if (state.gap.length() == 0) { + finalValue = '[' + join(partial, ",") + ']'; + } else { + String separator = ",\n" + state.indent; + String properties = join(partial, separator); + finalValue = "[\n" + state.indent + properties + '\n' + stepback + ']'; + } + } + + state.jstack.pop(); + state.indent = stepback; + return finalValue; + } + + private static String quote(String string) { + StringBuffer product = new StringBuffer(string.length()+2); // two extra chars for " on either side + product.append('"'); + int length = string.length(); + for (int i = 0; i < length; i++) { + char c = string.charAt(i); + switch (c) { + case '"': + product.append("\\\""); + break; + case '\\': + product.append("\\\\"); + break; + case '\b': + product.append("\\b"); + break; + case '\f': + product.append("\\f"); + break; + case '\n': + product.append("\\n"); + break; + case '\r': + product.append("\\r"); + break; + case '\t': + product.append("\\t"); + break; + default: + if (c < ' ') { + product.append("\\u"); + String hex = String.format("%04x", (int) c); + product.append(hex); + } + else { + product.append(c); + } + break; + } + } + product.append('"'); + return product.toString(); + } + +// #string_id_map# + + @Override + protected int findPrototypeId(String s) + { + int id; +// #generated# Last update: 2009-05-25 16:01:00 EDT + { id = 0; String X = null; + L: switch (s.length()) { + case 5: X="parse";id=Id_parse; break L; + case 8: X="toSource";id=Id_toSource; break L; + case 9: X="stringify";id=Id_stringify; break L; + } + if (X!=null && X!=s && !X.equals(s)) id = 0; + } +// #/generated# + return id; + } + + private static final int + Id_toSource = 1, + Id_parse = 2, + Id_stringify = 3, + LAST_METHOD_ID = 3, + MAX_ID = 3; + +// #/string_id_map# +} \ No newline at end of file diff --git a/test/org/ironchefpython/rhinodemo/HealthTest (Chris A's conflicted copy 2012-05-03).java b/test/org/ironchefpython/rhinodemo/HealthTest (Chris A's conflicted copy 2012-05-03).java new file mode 100644 index 0000000..beadb2a --- /dev/null +++ b/test/org/ironchefpython/rhinodemo/HealthTest (Chris A's conflicted copy 2012-05-03).java @@ -0,0 +1,111 @@ +package org.ironchefpython.rhinodemo; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Map; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.CtNewConstructor; +import javassist.CtNewMethod; + +import org.ironchefpython.modapi.*; +import org.ironchefpython.modapi.error.GeneralModdingException; +import org.ironchefpython.modapi.error.InvalidEventRegistration; +import org.ironchefpython.modapi.primitives.CalculatedProperty.CallablePointer; +import org.junit.Test; +import org.mockengine.Engine; +import org.mockengine.MockEngine; +import org.mockito.ArgumentCaptor; +import org.mozilla.javascript.Callable; + +import org.w3c.dom.events.EventListener; +import org.w3c.dom.events.MouseEvent; + + +import static org.mockito.Mockito.*; + +public class HealthTest { + + + public static void main(String[] args) throws Exception { + new HealthTest().mockTest(); + } + + @Test + public void mockTest() throws Exception { + + Engine game = new MockEngine(); + JsModManager mm = new JsModManager(game, "health"); + mm.registerEvent(new CustomEventFactory("full_health", null)); + mm.registerEvent(new CustomEventFactory("no_health", null)); + mm.runScript(HealthTest.class.getResourceAsStream("health.js"), "health.js"); + + ClassPool cp = new ClassPool(); + cp.appendSystemPath(); + + for (Prototype p : mm.getPrototypes()) { + Class cls = ComponentFactory(cp, p); + Constructor c = cls.getConstructor(Number.class, Number.class, Number.class); + Object o = c.newInstance(1,2,3); + mm.callInitializer(o); + CallablePointer call = (CallablePointer) cls.getField("ratio").get(null); +// System.out.println(mm.callMethod(obj, call.)) + } + +// mm.exec("game.addEventListener('test', function(evt) { console.log(evt.button) }, false)"); + +// ArgumentCaptor argument = ArgumentCaptor.forClass(EventListener.class); +// verify(game).addEventListener(anyString(), argument.capture(), eq(false)); +// +// MouseEvent uiEvent= mock(MouseEvent.class); +// when(uiEvent.getType()).thenReturn("mouseevent"); +// when(uiEvent.getButton()).thenReturn((short) 1); +// argument.getValue().handleEvent(uiEvent); + } + + + + public static Class ComponentFactory(ClassPool classPool, Prototype p) throws Exception { + CtClass comp = classPool.makeClass(p.getId()+"Component"); + comp.addConstructor(CtNewConstructor.defaultConstructor(comp)); + + + + + for (Map.Entry e : p.getPropertyMap().entrySet()) { + System.out.println(e.getValue().getClass()); + e.getValue().addToClass(e.getKey(), comp); + } + + CtClass[] types = p.getConstructor().getParamClasses(); + String body = "{"; + String[] provided = p.getConstructor().getProvided(); + for (int i = 0; i < provided.length; i++) { + body += "this." + provided[i] + "=$" + (i+1) + ";"; + } + body += "}"; +System.out.println(body); + comp.addConstructor(CtNewConstructor.make(types, new CtClass[0], body, comp)); + + return comp.toClass(); + +// CtField f = new CtField() +// CtNewMethod.make() + + // +// cc.addConstructor(CtNewConstructor.defaultConstructor(cc)); +// cc.addConstructor(CtNewConstructor.make("public Test(String name) {System.out.println(\"making \" + name); }", cc)); +// CtMethod m = CtNewMethod.make("public void test1() {System.out.println(\"test\"); }", cc); +// cc.addMethod(m); +// Class cls = cc.toClass(); +// Constructor c = cls.getConstructor(String.class); +// Object object = c.newInstance("test2"); +// Method method = cls.getMethod("test1"); +// method.invoke(object); + } +} diff --git a/test/org/ironchefpython/rhinodemo/HealthTest.java b/test/org/ironchefpython/rhinodemo/HealthTest.java new file mode 100644 index 0000000..a01d766 --- /dev/null +++ b/test/org/ironchefpython/rhinodemo/HealthTest.java @@ -0,0 +1,110 @@ +package org.ironchefpython.rhinodemo; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Map; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.CtNewConstructor; +import javassist.CtNewMethod; + +import org.ironchefpython.modapi.*; +import org.ironchefpython.modapi.error.GeneralModdingException; +import org.ironchefpython.modapi.error.InvalidEventRegistration; +import org.junit.Test; +import org.mockengine.Engine; +import org.mockengine.MockEngine; +import org.mockito.ArgumentCaptor; +import org.mozilla.javascript.Callable; + +import org.w3c.dom.events.EventListener; +import org.w3c.dom.events.MouseEvent; + + +import static org.mockito.Mockito.*; + +public class HealthTest { + + + public static void main(String[] args) throws Exception { + new HealthTest().mockTest(); + } + + @Test + public void mockTest() throws Exception { + + Engine game = new MockEngine(); + JsModManager mm = new JsModManager(game, "health"); + mm.registerEvent(new CustomEventFactory("full_health", null)); + mm.registerEvent(new CustomEventFactory("no_health", null)); + mm.runScript(HealthTest.class.getResourceAsStream("health.js"), "health.js"); + + ClassPool cp = new ClassPool(); + cp.appendSystemPath(); + + for (Prototype p : mm.getPrototypes()) { + Class cls = ComponentFactory(cp, p); + Constructor c = cls.getConstructor(Number.class, Number.class, Number.class); + Object o = c.newInstance(1,2,3); + mm.callInitializer(o); + System.out.println(mm.callMethod(o, "ratio")); + + } + +// mm.exec("game.addEventListener('test', function(evt) { console.log(evt.button) }, false)"); + +// ArgumentCaptor argument = ArgumentCaptor.forClass(EventListener.class); +// verify(game).addEventListener(anyString(), argument.capture(), eq(false)); +// +// MouseEvent uiEvent= mock(MouseEvent.class); +// when(uiEvent.getType()).thenReturn("mouseevent"); +// when(uiEvent.getButton()).thenReturn((short) 1); +// argument.getValue().handleEvent(uiEvent); + } + + + + public static Class ComponentFactory(ClassPool classPool, Prototype p) throws Exception { + CtClass comp = classPool.makeClass(p.getId()+"Component"); + comp.addConstructor(CtNewConstructor.defaultConstructor(comp)); + + + + + for (Map.Entry e : p.getPropertyMap().entrySet()) { + System.out.println(e.getValue().getClass()); + e.getValue().addToClass(e.getKey(), comp); + } + + CtClass[] types = p.getConstructor().getParamClasses(); + String body = "{"; + String[] provided = p.getConstructor().getProvided(); + for (int i = 0; i < provided.length; i++) { + body += "this." + provided[i] + "=$" + (i+1) + ";"; + } + body += "}"; +System.out.println(body); + comp.addConstructor(CtNewConstructor.make(types, new CtClass[0], body, comp)); + + return comp.toClass(); + +// CtField f = new CtField() +// CtNewMethod.make() + + // +// cc.addConstructor(CtNewConstructor.defaultConstructor(cc)); +// cc.addConstructor(CtNewConstructor.make("public Test(String name) {System.out.println(\"making \" + name); }", cc)); +// CtMethod m = CtNewMethod.make("public void test1() {System.out.println(\"test\"); }", cc); +// cc.addMethod(m); +// Class cls = cc.toClass(); +// Constructor c = cls.getConstructor(String.class); +// Object object = c.newInstance("test2"); +// Method method = cls.getMethod("test1"); +// method.invoke(object); + } +} diff --git a/test/org/ironchefpython/rhinodemo/JavassistTest.java b/test/org/ironchefpython/rhinodemo/JavassistTest.java new file mode 100644 index 0000000..05c20b6 --- /dev/null +++ b/test/org/ironchefpython/rhinodemo/JavassistTest.java @@ -0,0 +1,36 @@ +package org.ironchefpython.rhinodemo; + + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.CtNewConstructor; +import javassist.CtNewMethod; + +public class JavassistTest { + + /** + * @param args + * @throws Exception + */ + public static void main(String[] args) throws Exception { + ClassPool cp = new ClassPool(); + cp.appendSystemPath(); + + CtClass cc = cp.makeClass("Test"); + cc.addConstructor(CtNewConstructor.defaultConstructor(cc)); + cc.addConstructor(CtNewConstructor.make("public Test(String name) {System.out.println(\"making \" + name); }", cc)); + CtMethod m = CtNewMethod.make("public void test1() {System.out.println(\"test\"); }", cc); + cc.addMethod(m); + Class cls = cc.toClass(); + Constructor c = cls.getConstructor(String.class); + Object object = c.newInstance("test2"); + Method method = cls.getMethod("test1"); + method.invoke(object); + + } + +} diff --git a/test/org/ironchefpython/rhinodemo/ModTest.java b/test/org/ironchefpython/rhinodemo/ModTest.java new file mode 100644 index 0000000..4e9f195 --- /dev/null +++ b/test/org/ironchefpython/rhinodemo/ModTest.java @@ -0,0 +1,35 @@ +package org.ironchefpython.rhinodemo; + +import java.io.IOException; + +import org.ironchefpython.modapi.JsModManager; +import org.junit.Test; +import org.mockengine.Engine; +import org.mockengine.MockEngine; + +public class ModTest { + + public static void main(String[] args) throws IOException { + new ModTest().mockTest(); + } + + @Test + public void mockTest() throws IOException { + Engine game = new MockEngine(); + + + JsModManager mm = new JsModManager(game, "test"); + + mm.runScript(ModTest.class.getResourceAsStream("testmod.js"), "testmod.js"); + +// mm.exec("game.addEventListener('test', function(evt) { console.log(evt.button) }, false)"); + +// ArgumentCaptor argument = ArgumentCaptor.forClass(EventListener.class); +// verify(game).addEventListener(anyString(), argument.capture(), eq(false)); +// +// MouseEvent uiEvent= mock(MouseEvent.class); +// when(uiEvent.getType()).thenReturn("mouseevent"); +// when(uiEvent.getButton()).thenReturn((short) 1); +// argument.getValue().handleEvent(uiEvent); + } +} diff --git a/test/org/ironchefpython/rhinodemo/anothermod.js b/test/org/ironchefpython/rhinodemo/anothermod.js new file mode 100644 index 0000000..c30f55a --- /dev/null +++ b/test/org/ironchefpython/rhinodemo/anothermod.js @@ -0,0 +1,167 @@ +/* + * This is another mod. Things are best explained in code. + */ + +// Requiring some stuff. +// Should the SDK be required automatically? +var SDK = require("sdk"), + Gun = require("gunmod").Gun; + +// Event definition. +var ToolBreakEvent = exports.ToolBreakEvent = mm.defineEvent({ + "name": "tool:break", + "attributes": { + "tool": "Entity < Tool" // Defined the same as tool properties + } +}); + +// Prototype definition. +var tool = exports.tool = SDK.Item.extend({ + // Id is tool + "id": "tool", + + // Properties + "properties": { + "durability": "Number", // The engine will call Type.valueOf("Number") or something + // and will make sure that all of these will be Number. + "color": "String" + }, + + // Handlers: Event handlers for the tool, called when an Entity + "handlers": { + "item:use": function(event) { + var entity = event.target; + if (!event.clicked.matches("Entity < Block")) { + return; + } + entity.durability -= 3; + + // Example event cancellation + if (event.clicked.name.equalsIgnoreCase("Obsidian")) { + event.cancelled = true; + } + } + }, + + // Bind: Function called after everything is set. + // If after this function is called there are unset properties, + // the entity will not be created and a runtime exception will be thrown. + "bind": function(entity) { + if (!entity.texture) { + var texture = SDK.Texture.generateFromColor("#ff0000"); + entity.texture = texture; + } + } +}); + +// Example of a function that registers 4 prototypes. +var createToolSet = exports.createToolSet = function(material) { + return { + axe: SDK.Axe.define({ + id: material.id + "_axe", + name: material.name + " Axe", + material: material + }), + hoe: SDK.How.define({ + id: material.id + "_hoe", + name: material.name + " Hoe", + material: material + }), + sword: SDK.Sword.define({ + id: material.id + "_sword", + name: material.name + " Sword", + material: material + }), + shovel: SDK.Shovel.define({ + id: material.id + "_shovel", + name: material.name + " Shovel", + material: material + }) + }; +}; + + +// Example of defining a new Gun to use in the game. +var SuperGun = exports.SuperGun = Gun.define({ + name: "Super Gun", + texture: "supergun.png", // If instance of string, alias of SDK.Texture.textureFromFile("supergun.png") + sound: "supergun.ogg" +}); + +// Example of listening to an event. +Game.listen("player:jump", function(event) { + var player = event.player; // Alias of event.target. + if (!player.inventory.contains(SuperGun)) { + player.inventory.add(SuperGun.create()); + } +}); + +// Add the listener to players, and not all entities that can jump. +SDK.Player.addEventListener("jump", function(event) { + if (!this.inventory.contains(manager.getPrototype("SuperGun"))) { + game.giveItem("SuperGun", this); + } +}); + +// Example of listening to an event again. Alternative to explicitly specifying handlers. +SuperGun.listen("gun:fire", function(event) { + console.log("The gun " + event.target + " was fired."); +}); + +// Example of extending a Gun that will be extended more. +var MachineGun = exports.MachineGun = Gun.extend({ + id: "MachineGun", + properties: { + awesomeness: "Number" + } +}); + +// Example of overriding and extending something. (define) +var SuperDuperGun = SuperGun.define({ + id: "SuperDuperGun", + name: "Super Duper Gun", + texture: "superdupergun.png" +}); + +// Example of overriding and extending something. (extend) +var Cannon = SuperGun.extend({ + "id": "cannon", + "attributes": { //Instance-specific + "cooldown": "Number" + }, + "properties": { + "ship_compatible": "[Prototype < Ship]" // Array of Prototypes < Ship + }, + "events": { + "gun:fire" : function(event) { + var cannon = event.gun; // Alias for event.target + if (cannon.cooldown > 0) { + event.cancelled = true; + return; + } + cannon.cooldown = 6000; + }, + "entity:update": function(event) { // Incredibly inefficient, but I + // want to demonstrate this event. + var cannon = event.gun; + + var oldCooldown = cannon.cooldown; + cannon.cooldown--; + + var cannonState = cannon.cooldown % 600; + var oldState = oldCooldown % 600; + + if (cannonState == oldState) { + return; + } + + // Would go to a 45 degree angle. Makes no sense. + cannon.transform.orientation.setPitch(cannonState * 4.5); + } + }, + "init": function(cannon) { + if (!cannon.gauge) { + cannon.gauge = 4; // Default 4 gauge. + } + } +}); diff --git a/test/org/ironchefpython/rhinodemo/health.js b/test/org/ironchefpython/rhinodemo/health.js new file mode 100644 index 0000000..72f17de --- /dev/null +++ b/test/org/ironchefpython/rhinodemo/health.js @@ -0,0 +1,70 @@ +var fallingDamageSpeedThreshold = 20; +var excessSpeedDamageMultiplier = 10; + +var FULL_HEALTH_EVENT = manager.getEvent("full_health"); +var NO_HEALTH_EVENT = manager.getEvent("no_health"); + +var applyDamage = function(entity, amount, instigator) { + if (this.currentHealth <= 0) { + return; + } + this.timeSinceLastDamage = 0; + this.currentHealth -= amount; + if (this.currentHealth <= 0) { + entity.send(NO_HEALTH_EVENT, instigator); + } +} + +Health = manager.registerPrototype({ + "id": "Health", + "properties": { + "maxHealth": manager.numberType, + "regenRate": manager.numberType, + "waitBeforeRegen": manager.numberType, + "currentHealth": manager.numberType, + "timeSinceLastDamage": manager.numberType, + "partialRegen": manager.numberType, + "ratio": manager.calculatedType(manager.numberType, function() { + return this.currentHealth / this.maxHealth; + }) + }, + "constructor": manager.makeConstructor(["maxHealth", "regenRate", "waitBeforeRegen"], function() { + this.timeSinceLastDamage = 0; + this.partialRegen = 0; + this.currentHealth = this.maxHealth; + console.log("amazing"); + }), + "handlers": { + "damage": function(event) { + applyDamage.call(this, event.target, event.amount, event.instigator); + }, + "land": function(event) { + if (event.velocity.y < 0 && -event.velocity.y > thisfallingDamageSpeedThreshold) { + var damage = Math.int((-event.velocity.y - fallingDamageSpeedThreshold) * excessSpeedDamageMultiplier); + if (damage > 0) { + applyDamage.call(this, entity.target, damage, null); + } + } + }, + "healFull": function(event) { + this.currentHealth = this.maxHealth; + } + }, + "update": function(delta, entity) { + if (health.currentHealth <= 0 || health.currentHealth == health.maxHealth || health.regenRate == 0) { + return; + } + this.timeSinceLastDamage += delta; + if (this.timeSinceLastDamage >= this.waitBeforeRegen) { + this.partialRegen += delta * this.regenRate; + if (this.partialRegen >= 1) { + this.currentHealth = Math.round(Math.min(this.maxHealth, this.currentHealth + this.partialRegen)); + this.partialRegen %= 1; + if (this.currentHealth == this.maxHealth) { + entity.send(FULL_HEALTH_EVENT); + } + } + } + } +}); + diff --git a/test/org/ironchefpython/rhinodemo/immot.js b/test/org/ironchefpython/rhinodemo/immot.js new file mode 100644 index 0000000..ed68430 --- /dev/null +++ b/test/org/ironchefpython/rhinodemo/immot.js @@ -0,0 +1,174 @@ +// This seems good. Would add a primary material settings here +var ItemComponent = game.modManager.registerComponent({ + "id": "item", + "properties": { + "name": game.modManager.StringType, + "renderWithIcon": game.modManager.BooleanType, + "stack_size": game.modManager.NumberType, + "texture": game.modManager.TextureType, + "primary_material": game.modManager.PrefabRefType + } +}); + +// Nor do I don't mind this +var Material = game.modManager.registerComponent({ +"id": "material", +"properties": { +"color": game.modManager.ColorType, +"hardness": game.modManager.NumberType +} +}); + +// I'll go with an additional component for the sake of example +var Metal = game.modManager.registerComponent({ +"id": "metal" +"properties": { +"conductivity": game.modManager.NumberType +} +}); + +// Actual materials are prefabs composed of at least a material component +Iron = game.modManager.registerPrefab({ +"id" = "Iron", +"components" = { + "material" = { + "name": "Iron", + "color": "#E6E7E8", + "hardness": 4, + "durability": 5 + }, + "metal" = { + "conductivity" = 0.7 + } +} +}); + +Copper = game.modManager.registerPrefab({ +"id" = "Copper", +"components" = { + "material" = { + "name": "Copper", + "color": "#B87333", + "hardness": 3, + "durability": 5 + }, + "metal" = { + "conductivitiy" = 1 + } +}); + +// Metal Ingot is a prefab for an item +var MetalIngot = game.modManager.registerPrefab({ +"id": "metal_ingot", +"components" : { + "itemComponent" : { + "name": "${material.name} Ingot", + "renderWithIcon": true, + "stack_size": 64 + // Skip primary material? Dunno what the "