Modding and components and inheritance

ironchefpython

Member
Contributor
Architecture
So I'm working on the modding API, and a java parser/interpreter for it. A question came up in IRC, regarding the inheritance of components.

Here's a gist of an example mod that adds items, materials, metals, and tools to the game. https://gist.github.com/2471570

First, is it easy to understand?

Second, how would you improve the syntax for clarity?

Third, how would you write the same thing if you couldn't use components in components?
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Does the use of inheritance like that still allow adding and removing components after instantiation?

I'll give an example based around the current terasology concepts (components are basically the same but no inheritance, prefabs are like prototypes I believe - descriptions for things that can be created but don't actually exist, but can still be inspected). I'll leave events alone for now, for the most part. Might give you some ideas.
Code:
// 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 "<metal" notation means.
    }
}
// For performance reason I probably wouldn't create a new texture for each instance of an ingot. Plus how would this work after save+load? Could be in some startup code for the mod to create these textures.
"events": {
"init": function(event) {
var $this = event.target;
$this.texture = game.colorizeTexture("ingot.png", $this.metal.color);
}
}
});

// Tool remains a component, but material is provided by item.
var Tool = game.modManager.registerComponent({
"id": "tool",
"properties" : {
    "durability": game.primitives.INTEGER,
    "bonus_target": game.modManager.Function,
    "bonus": "$material.hardness"
}
"events": {
"init": function(event) {
var $this= event.target; // Event target is the container, not the component.
$this.durability = Math.pow($this.item.primary_material.durability, 3);
},
"use": function(event) {
var $this = event.equipped;
if ($this.tool.bonus_target(event.target)) {
event.action.time /= $this.bonus;
}
},
"harvest": function(event) {
var $this = event.equipped;
$this.tool.durability--;
if ($this.durability == 0) {
$this.dispatchEvent("break");
}
},
"break": function(event) {
event.target.destroy();
},
}
});


var Axe = game.modManager.registerPrefab({
    "id" : "axe",
    "components" : {
        "item" : {
            "name" : "${material.name} Axe",
            "renderWithIcon": true,
            "stack_size": 1    
        },
        "craftable": {
            "recipe": "$material under $material over $material over blah",
        },
        "tool": {
                "bonus_target": function(obj) {
                 return obj.hasComponent(game.modManager.registerComponent("vanilla.timber"))
        }
    },
"events": {
"init": function(event) {
var $this = event.target;
$this.texture = game.colorizeTexture("axe.png", $this.material.color);
}
}
});

CopperIngot = game.modManager.registerPrefab({
    "id" = "CopperIngot",
    // Prefabs support parentage in the engine, in which case the components and their settings are applied as a delta to 
    // the parent prefab
    "parent" = "MetalIngot",
    "components" = {
        "item" : {
            "primary_material" : game.modManager.getPrefab("Copper"); // Although I guess we don't even need to look it up
        }
    }
});
IronIngot = game.modManager.registerPrefab({
    "id" = "IronIngot",
    "parent" = "MetalIngot",
    "components" = {
        "item" : {
            "primary_material" : game.modManager.getPrefab("Iron");
        }
    }
});
CopperAxe = game.modManager.registerPrefab({
    "id" = "CopperAxe",
    "parent" = "Axe",
    "components" = {
        "item" : {
            "primary_material" : game.modManager.getPrefab("Copper");
        }
    }
});
IronAxe = game.modManager.registerPrefab({
    "id" = "IronAxe",
    "parent" = "Axe",
    "components" = {
        "item" : {
            "primary_material" : game.modManager.getPrefab("Iron");
        }
    }
});
 

AlbireoX

Unsuccessful Javascript Evangelist
Contributor
Logistics
I know you're going to say bikeshed, but...

Component.registerComponent would better be Component.create. Makes it easier to understand :p

Also, like I said, you should use defaults instead of types in the properties:{} object.

Actually, it'd be cool if you had a syntax like Type.valueOf("String"). It makes it more flexible, such as if you wanted

Code:
Type.valueOf("Prototype < Metal, Item");
(which means a Prototype with a Metal and Item component). I don't know where that would go if there were defaults, though.
 

Cervator

Org Co-Founder & Project Lead
Contributor
Design
Logistics
SpecOps
The shed is red, cuz red is better! :D

I'm concerned to keep the flow/terminology between the built-in ES and any sort of component system in a mod layer very easy and non-ambiguous.

My favorite example of the power of ES in-game is a Portal entity. That's a collection of blocks (might store data in a "BlockComponent", a game concept Portal (data stored in a PortalComponent), and optionally a creature spawner (data in a SpawnerComponent). We'll have a portal prefab that when invoked will create a single entity with all that set up.

The way it will work is that a player presses 'e' on a block in-game, the engine looks for a BlockComponent that contains that coordinate, and if found executes an "activate" event on the owning entity. That's the event system Immortius did within the ES, or at least my rough understanding of it.

Anything attached/registered to that entity now goes through its event routine, if any. Finding a PortalComponent the engine processes it through PortalSystem, which consumes the event to toggle spawning on/off. It does that simply by attaching a SpawnerComponent to that entity, or removes it if it already owns one.

Separately there'll be a SpawnerSystem that every x seconds polls all entities with a SpawnerComponent attached and spawns a gelcube under certain conditions.

The true power is that with all that in place you can now turn around and attach a SpawnerComponent to a gelcube and without any other work you've now got a Gelatinous Cube Queen, plus a doomsday scenario as gelcubes populate the world exponentially :D

So what I'm getting at is that just from an engine perspective registering/creating a component says to me that we're looking at a new Component type with the data supplied in the properties block in JSON/JS that we can then attach/detach to entities in-game to create content. But some of the mod stuff sounds like you're instantiating a component that itself is a thing, rather than an entity with components attached that all-together defines what that thing is and allows the game to process it in different ways

I'm not sure I have a point here other than trying to explain everything better to myself. Unless it helps clear up anything.
 
Top