In the next release, I’ve done some extensive work on the ability to script custom objects. Since most of us monkeys are probably also coders and since scripting in JME comes up from time to time, I thought there might be some interested here.
There is a more detailed thread on the Mythruna forum: Scripted Objects: WIP status...
But I will try to summarize some of the salient parts here.
This video shows an example of a test mod that I’ve made that implements a simple firewood/campfire system complete with match and being able to put out the fire with a bucket of sand or water:
http://www.youtube.com/watch?v=0R82bKkHtjY
This isn't how real fire will work in Mythruna. Real fire will be a thing all its own that burns any flammable material, jumps from object to object, burns down forests, etc.. This is just an example of how a modder can make their own custom objects and have them interact.
Here is the script mod I used to make those objects:
[java]
/**
* Testing and demonstrating how action-able
* scripted objects can be added to the game.
*/
import mythruna.MaterialType;
import mythruna.es.*;
import mythruna.item.*;
loadBlueprint( "firewood.bp" );
createModel( "logs", 0, "firewood.bp" )
createModel( "campfire", 0, "campfire.bp" )
createModel( "match", 2, "match.bp", Scale.Item );
createModel( "bucket", 0, "bucket.bp", Scale.Item );
createModel( "bucket-sand", 0, "bucket-sand.bp", Scale.Item );
createModel( "bucket-water", 0, "bucket-water.bp", Scale.Item );
// Now create the object templates (classes) for some items
def firewood = objectTemplate( "Firewood" ) {
setModel( "logs" );
addParents( "BaseItem" );
action( ":light" ) { self, tool ->
def obj = entityObject(self);
if( obj.locals.getInt("lit") == 0 ) {
obj.setModel("campfire");
obj.locals.setInt("lit", 1);
echo( "A fire crackles to life." );
} else {
echo( "These logs are already lit." );
}
}
action( ":douse" ) { self, tool ->
def obj = entityObject(self);
if( obj.locals.getInt("lit") == 0 ) {
echo( "The fire is not lit." );
} else {
obj.setModel("logs");
obj.locals.setInt("lit", 0);
echo( "The fire goes out." );
}
}
}
def match = objectTemplate( "Match" ) {
setModel( "match" );
addParents( "BaseTool" );
defaultAction( "Light" ) { self, hit ->
if( hit == null || hit.object == null )
return;
entityObject(hit.object).execute( ":light", self );
}.onlyIf() { self, hit ->
// Right now only if it has a ":light" action
return entityObject(hit.object).hasAction( ":light" );
}
}
def bucket = objectTemplate( "Bucket" ) {
setModel( "bucket" );
addParents( "BaseTool" );
action( ":empty" ) { self ->
def obj = entityObject(self);
// Empty the bucket
obj.locals.setInt("filled", 0);
obj.setModel("bucket");
}
defaultAction( "Empty" ) { self, hit ->
entityObject(self).execute( ":empty" );
echo "The bucket is now empty.";
}.onlyIf() { self, hit ->
if( hit == null || hit.object != null )
return false;
def material = hit.material;
if( material != MaterialType.WATER && material != MaterialType.SAND )
return false;
// Else make sure we are trying to dump the material
// back into its own type
def obj = entityObject(self);
int holds = obj.locals.getInt("filled");
return material.id == holds;
}
defaultAction( "Fill" ) { self, hit ->
def material = hit.material;
def obj = entityObject(self);
int holds = obj.locals.getInt("filled");
if( holds != 0 ) {
echo "The bucket is already full.";
return;
}
// Else the bucket is empty so we can fill it
if( material == MaterialType.WATER ) {
obj.locals.setInt("filled", material.id);
obj.setModel( "bucket-water" );
echo "You filled the bucket with water."
} else if( material == MaterialType.SAND ) {
obj.locals.setInt("filled", material.id);
obj.setModel( "bucket-sand" );
echo "You fill the bucket with sand."
}
}.onlyIf() { self, hit ->
if( hit == null || hit.object != null )
return false;
def material = hit.material;
return material == MaterialType.WATER || material == MaterialType.SAND;
}
defaultAction( "Douse" ) { self, hit ->
entityObject(self).execute( ":empty" );
entityObject(hit.object).execute( ":douse", self );
}.onlyIf() { self, hit ->
if( hit == null )
return false;
// If the target can't be doused, then we won't
// worry about it
if( !entityObject(hit.object).hasAction( ":douse" ) )
return false;
// See if we even contain anything
return entityObject(self).locals.getInt("filled") != 0;
}
}
on( [playerJoined] ) {
type, event ->
println "Making sure the player" + player + " has some standard test tools...";
def items = getContainedItems( player );
def classes = items.collect {
it[ClassInstance.class].classEntity;
}
if( !classes.contains(firewood.classEntity) ) {
println "Need to create firewood in the player's inventory...";
firewood.createInstanceOn( player );
}
if( !classes.contains(match.classEntity) ) {
println "Need to create a match in the player's inventory...";
match.createInstanceOn( player );
}
if( !classes.contains(bucket.classEntity) ) {
println "Need to create a bucket in the player's inventory...";
bucket.createInstanceOn( player );
}
} [/java]
It's a self-contained script that adds the items to the players inventory as well as completely defining their graphics and behaviors. A little Groovy scripting and some API magic.