In this tutorial we will be creating a new Action for the Scenario Module of Terasology. We will be creating an action that will give a player some items.
To start off we need to create a new Component that will be used to identify the action For this example we will be naming our new component ScenarioSecondaryGiveItemComponent.java
. All actions have an indicator component that indicates they are an action, this is our secondary indicator is what allows us to distinguish our specific action vs others.
Now we need to write our component class, for an indicator we need to do two steps, first we need to annotate the class with a @Replicate
annotation and have it implement the Component
interface. This results in our class looking like this:
import org.terasology.gestalt.entitysystem.component.Component;
import org.terasology.engine.network.Replicate;
@Replicate
public class ScenarioSecondaryGiveItemComponent implements Component<ScenarioSecondaryGiveItemComponent> {
}
Now that we have our indicator component complete we need to create the Prefab
file for the Action.
This file needs to be placed inside the module's <module-name>/assets/prefabs
directory. In this case we will name the file givePlayerItemAction.prefab
A scenario action prefab needs to have a few components:
- A network component
- A ScenarioIndicatorAction component
- A ScenarioLogicLabel component
- A Secondary Indicator component
- A ScenarioLogicText component
- A ScenarioArgumentContainer(If the ScenarioLogicText text has any parameters, detailed below)
This is always required for every action within the Scenario module, it allows the action to be used on multiplayer.
This is the component that indicates to the system that this prefab is an action, it is required for all actions.
This component includes the dropdown text for the component, the text is placed in the "name" field of the component.
This is the component we created before that allows the Scenario system to identify the specific action.
This component includes the text that is parsed by the Scenario system in order to create our more detailed information of the action and includes any parameters that need to be satisfied for the action. This text is include in the "text" field and any parameters should be indicated as the following [<identifier-for-parameter>:ValueType]
Where ValueType is the type of the parameter, these can include Integer
, Item
, Block
, Player
, Region
, Comparator
, and String
.
This component MUST be included if the ScenarioLogicText component indicates that the action requires any parameters.
If we take everything from above we can create our prefab file:
givePlayerItemAction.prefab
{
"Network":{},
"ScenarioIndicatorAction":{},
"ScenarioLogicLabel":{
"name":"Give item"
},
"ScenarioSecondaryGiveItem":{},
"ScenarioLogicText":{
"text":"Give [player:Player] [amount:Integer] [item:Item]."
},
"ScenarioArgumentContainer":{}
}
For this action we have three parameters, one indicates which player we are giving items, an integer that has how many of that item, and the last is the item itself.
An action requires a code implementation in two locations. ConvertEntitySystem
and ActionEventSystem
This is the most simple portion of the code and only requires us to indicate to the system how to serialize our action. For any action we want to use the default serialization. This means that we need to add to the system by adding a new event listener based on our indicator component.
@ReceiveEvent
public void onConvertEntityEvent(ConvertScenarioEntityEvent event, EntityRef entity, ScenarioSecondaryGiveItemComponent component) {
DefaultSerialize(event, entity);
}
This is the system that will actually evaluate our action when it is triggered, in order to setup our action we need to create a new event listener that will trigger off a EventTriggerEvent
for an entity that has our new ScenarioSecondaryGiveItemComponent
@ReceiveEvent //Give Item
public void onEventTriggerEvent(EventTriggerEvent event, EntityRef entity, ScenarioSecondaryGiveItemComponent action) {
}
Now we need to grab the values from our Argument container that has all of the parameters we setup in our ScenarioLogicText
component in the Prefab.
Map<String, EntityRef> variables = entity.getComponent(ScenarioArgumentContainerComponent.class).arguments;
With this map we can evaluate the parameters based on the keys we gave them in order to use them for actually carrying out the action.
EvaluateItemPrefabEvent itemEvaluateEvent = new EvaluateItemPrefabEvent(event.informationEntity);
variables.get("item").send(itemEvaluateEvent);
Prefab itemPrefab = itemEvaluateEvent.getResult();
EvaluateIntEvent intEvaluateEvent = new EvaluateIntEvent(event.informationEntity);
variables.get("amount").send(intEvaluateEvent);
int amount = intEvaluateEvent.getResult();
ScenarioValuePlayerComponent.PlayerType player = variables.get("player").getComponent(ScenarioValuePlayerComponent.class).type;
Now that we have all of our evaluated values we want to actually follow through with our action and give the player the correct number of items
for (int i = 0; i < amount; i++) {
EntityRef item = entityManager.create(itemPrefab);
if (player == ScenarioValuePlayerComponent.PlayerType.TRIGGERING_PLAYER) {
EntityRef giveEntity = event.informationEntity.getComponent(InfoTriggeringEntityComponent.class).entity;
GiveItemEvent giveItemEvent = new GiveItemEvent(giveEntity);
item.send(giveItemEvent);
}
}
Our overall event listener:
@ReceiveEvent //Give Item
public void onEventTriggerEvent(EventTriggerEvent event, EntityRef entity, ScenarioSecondaryGiveItemComponent action) {
Map<String, EntityRef> variables = entity.getComponent(ScenarioArgumentContainerComponent.class).arguments;
EvaluateItemPrefabEvent itemEvaluateEvent = new EvaluateItemPrefabEvent(event.informationEntity);
variables.get("item").send(itemEvaluateEvent);
Prefab itemPrefab = itemEvaluateEvent.getResult();
EvaluateIntEvent intEvaluateEvent = new EvaluateIntEvent(event.informationEntity);
variables.get("amount").send(intEvaluateEvent);
int amount = intEvaluateEvent.getResult();
ScenarioValuePlayerComponent.PlayerType player = variables.get("player").getComponent(ScenarioValuePlayerComponent.class).type;
for (int i = 0; i < amount; i++) {
EntityRef item = entityManager.create(itemPrefab);
if (player == ScenarioValuePlayerComponent.PlayerType.TRIGGERING_PLAYER) {
EntityRef giveEntity = event.informationEntity.getComponent(InfoTriggeringEntityComponent.class).entity;
GiveItemEvent giveItemEvent = new GiveItemEvent(giveEntity);
item.send(giveItemEvent);
}
}
}
This should now complete our new action and allow it to be used inside the Scenario Module. Launch the game and test out your new action!