All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.badlogic.gdx.ai.btree.Task Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright 2014 See AUTHORS file.
 * 
 * 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 com.badlogic.gdx.ai.btree;

import com.badlogic.gdx.ai.btree.annotation.TaskConstraint;
import com.badlogic.gdx.utils.Pool.Poolable;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import com.badlogic.gdx.utils.reflect.ReflectionException;

/** This is the abstract base class of all behavior tree tasks. The {@code Task} of a behavior tree has a status, one control and a
 * list of children.
 * 
 * @param  type of the blackboard object that tasks use to read or modify game state
 * 
 * @author implicit-invocation
 * @author davebaol */
@TaskConstraint
public abstract class Task implements Poolable {

	/** The enumeration of the values that a task's status can have.
	 * 
	 * @author davebaol */
	public enum Status {
		/** Means that the task has never run or has been reset. */
		FRESH,
		/** Means that the task needs to run again. */
		RUNNING,
		/** Means that the task returned a failure result. */
		FAILED,
		/** Means that the task returned a success result. */
		SUCCEEDED,
		/** Means that the task has been terminated by an ancestor. */
		CANCELLED;
	}

	/** The clone strategy (if any) that {@link #cloneTask()} will use. Defaults to {@code null}, meaning that {@link #copyTo(Task)}
	 * is used instead. In this case, properly overriding this method in each task is developer's responsibility but this gives you
	 * the opportunity to target GWT.
	 * 

* For instance, if you don't care about GWT, you can let Kryo make a deep copy for you like that * *

	 * 
	 *    Task.TASK_CLONER = new TaskCloner() {
	 *       Kryo kryo;
	 *       
	 *       {@literal @}Override
	 *       public {@code  Task cloneTask (Task} task) {
	 *          if (kryo == null) {
	 *             kryo = new Kryo();
	 *             kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
	 *          }
	 *          return kryo.copy(task);
	 *       }
	 *    };
	 * 
	 * 
*/ public static TaskCloner TASK_CLONER = null; /** The status of this task. */ protected Status status = Status.FRESH; /** The parent of this task */ protected Task control; /** The behavior tree this task belongs to. */ protected BehaviorTree tree; /** The guard of this task */ protected Task guard; /** This method will add a child to the list of this task's children * * @param child the child task which will be added * @return the index where the child has been added. * @throws IllegalStateException if the child cannot be added for whatever reason. */ public final int addChild (Task child) { int index = addChildToTask(child); if (tree != null && tree.listeners != null) tree.notifyChildAdded(this, index); return index; } /** This method will add a child to the list of this task's children * * @param child the child task which will be added * @return the index where the child has been added. * @throws IllegalStateException if the child cannot be added for whatever reason. */ protected abstract int addChildToTask (Task child); /** Returns the number of children of this task. * * @return an int giving the number of children of this task */ public abstract int getChildCount (); /** Returns the child at the given index. */ public abstract Task getChild (int i); /** Returns the blackboard object of the behavior tree this task belongs to. * @throws IllegalStateException if this task has never run */ public E getObject () { if (tree == null) throw new IllegalStateException("This task has never run"); return tree.getObject(); } /** Returns the guard of this task. */ public Task getGuard () { return guard; } /** Sets the guard of this task. * @param guard the guard */ public void setGuard (Task guard) { this.guard = guard; } /** Returns the status of this task. */ public final Status getStatus () { return status; } /** This method will set a task as this task's control (parent) * * @param control the parent task */ public final void setControl (Task control) { this.control = control; this.tree = control.tree; } /** Checks the guard of this task. * @param control the parent task * @return {@code true} if guard evaluation succeeds or there's no guard; {@code false} otherwise. * @throws IllegalStateException if guard evaluation returns any status other than {@link Status#SUCCEEDED} and * {@link Status#FAILED}. */ public boolean checkGuard (Task control) { // No guard to check if (guard == null) return true; // Check the guard of the guard recursively if (!guard.checkGuard(control)) return false; // Use the tree's guard evaluator task to check the guard of this task guard.setControl(control.tree.guardEvaluator); guard.start(); guard.run(); switch (guard.getStatus()) { case SUCCEEDED: return true; case FAILED: return false; default: throw new IllegalStateException("Illegal guard status '" + guard.getStatus() + "'. Guards must either succeed or fail in one step."); } } /** This method will be called once before this task's first run. */ public void start () { } /** This method will be called by {@link #success()}, {@link #fail()} or {@link #cancel()}, meaning that this task's status has * just been set to {@link Status#SUCCEEDED}, {@link Status#FAILED} or {@link Status#CANCELLED} respectively. */ public void end () { } /** This method contains the update logic of this task. The actual implementation MUST call {@link #running()}, * {@link #success()} or {@link #fail()} exactly once. */ public abstract void run (); /** This method will be called in {@link #run()} to inform control that this task needs to run again */ public final void running () { Status previousStatus = status; status = Status.RUNNING; if (tree.listeners != null && tree.listeners.size > 0) tree.notifyStatusUpdated(this, previousStatus); if (control != null) control.childRunning(this, this); } /** This method will be called in {@link #run()} to inform control that this task has finished running with a success result */ public final void success () { Status previousStatus = status; status = Status.SUCCEEDED; if (tree.listeners != null && tree.listeners.size > 0) tree.notifyStatusUpdated(this, previousStatus); end(); if (control != null) control.childSuccess(this); } /** This method will be called in {@link #run()} to inform control that this task has finished running with a failure result */ public final void fail () { Status previousStatus = status; status = Status.FAILED; if (tree.listeners != null && tree.listeners.size > 0) tree.notifyStatusUpdated(this, previousStatus); end(); if (control != null) control.childFail(this); } /** This method will be called when one of the children of this task succeeds * * @param task the task that succeeded */ public abstract void childSuccess (Task task); /** This method will be called when one of the children of this task fails * * @param task the task that failed */ public abstract void childFail (Task task); /** This method will be called when one of the ancestors of this task needs to run again * * @param runningTask the task that needs to run again * @param reporter the task that reports, usually one of this task's children */ public abstract void childRunning (Task runningTask, Task reporter); /** Terminates this task and all its running children. This method MUST be called only if this task is running. */ public final void cancel () { cancelRunningChildren(0); Status previousStatus = status; status = Status.CANCELLED; if (tree.listeners != null && tree.listeners.size > 0) tree.notifyStatusUpdated(this, previousStatus); end(); } /** Terminates the running children of this task starting from the specified index up to the end. * @param startIndex the start index */ protected void cancelRunningChildren (int startIndex) { for (int i = startIndex, n = getChildCount(); i < n; i++) { Task child = getChild(i); if (child.status == Status.RUNNING) child.cancel(); } } /** Resets this task to make it restart from scratch on next run. */ public void resetTask () { if (status == Status.RUNNING) cancel(); for (int i = 0, n = getChildCount(); i < n; i++) { getChild(i).resetTask(); } status = Status.FRESH; tree = null; control = null; } /** Clones this task to a new one. If you don't specify a clone strategy through {@link #TASK_CLONER} the new task is * instantiated via reflection and {@link #copyTo(Task)} is invoked. * @return the cloned task * @throws TaskCloneException if the task cannot be successfully cloned. */ @SuppressWarnings("unchecked") public Task cloneTask () { if (TASK_CLONER != null) { try { return TASK_CLONER.cloneTask(this); } catch (Throwable t) { throw new TaskCloneException(t); } } try { Task clone = copyTo(ClassReflection.newInstance(this.getClass())); clone.guard = guard == null ? null : guard.cloneTask(); return clone; } catch (ReflectionException e) { throw new TaskCloneException(e); } } /** Copies this task to the given task. This method is invoked by {@link #cloneTask()} only if {@link #TASK_CLONER} is * {@code null} which is its default value. * @param task the task to be filled * @return the given task for chaining * @throws TaskCloneException if the task cannot be successfully copied. */ protected abstract Task copyTo (Task task); @Override public void reset() { control = null; guard = null; status = Status.FRESH; tree = null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy