/*
* Copyright 2014 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.logic.behavior.tree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.module.sandbox.API;
/**
* A task run by an {@link Interpreter} for an {@link Actor}.
*
*/
@API
public abstract class Task {
private static final Logger LOG = LoggerFactory.getLogger(Task.class);
private Interpreter interpreter;
private Node node;
private Actor actor;
private Status status = Status.NOT_INITIALIZED;
private Task parent;
protected Task(Node node) {
this.node = node;
}
/**
* Is called when this tasks should initialize itself (first tick)
*/
public void onInitialize() {
}
/**
* Is called on each tick if the Task is {@link Status#RUNNING}
* @return the new Status, this should be one of
* <ul>
* <li>{@link Status#RUNNING} (continue execution)</li>
* <li>{@link Status#SUCCESS} (task finished with success)</li>
* <li>{@link Status#FAILURE} (task finished not / had an error)</li>
* </ul>
*/
public abstract Status update(float dt);
/**
* Is called when is task is terminated with a new status.
*/
public void onTerminate(Status result) {
}
/**
* Is called when a sub tasks finishes
*/
public abstract void handle(Status result);
/**
* Executes on tick on the task, depending on the status:
* <ul>
* <li> {@link Status#NOT_INITIALIZED} : initialize ({@link #onInitialize()}) and set status to {@link Status#RUNNING}</li>
* <li> {@link Status#RUNNING} : {@link #update(float)} the task</li>
* <li> {@link Status#SUSPENDED} : do nothing</li>
* <li> {@link Status#SUCCESS} or {@link Status#FAILURE} : status will be terminated, success or failure can be handled in {@link #onTerminate(Status)}
* <li> {@link Status#TERMINATED} : terminated task</li>
* </ul>
* @param deltaSeconds Seconds since last engine update
* @return The updated status of the task
*/
public Status tick(float deltaSeconds) {
switch (status) {
//not initialized: initialize and set running
case NOT_INITIALIZED: {
onInitialize();
status = Status.RUNNING;
status = this.tick(deltaSeconds);
break;
}
//running: update
case RUNNING: {
Status newStatus = update(deltaSeconds);
if (!(newStatus == Status.RUNNING || newStatus == Status.SUCCESS || newStatus == Status.FAILURE)) {
LOG.warn("update of Task {} returned invalid state {}", this.getClass(), newStatus);
}
status = newStatus;
if (newStatus == Status.FAILURE || newStatus == Status.SUCCESS) {
onTerminate(status);
}
break;
}
//suspended: do nothing
case SUSPENDED:
break;
//end of running: terminate
case FAILURE:
case SUCCESS: {
// onTerminate(status);
status = Status.TERMINATED;
break;
}
//terminated, do nothing
case TERMINATED: {
break;
}
default:
break;
}
return status;
}
public void start(Node child) {
interpreter().start(child, this);
}
public void stop(Status result) {
interpreter().stop(this, result);
}
public Status getStatus() {
return status;
}
public Node getNode() {
return node;
}
public Interpreter interpreter() {
return interpreter;
}
public Actor actor() {
return actor;
}
void setStatus(Status status) {
this.status = status;
}
void setParent(Task parent) {
this.parent = parent;
}
void setActor(Actor actor) {
this.actor = actor;
}
void setInterpreter(Interpreter interpreter) {
this.interpreter = interpreter;
}
Task getParent() {
return parent;
}
}