package com.mygdx.game.objects;
import com.badlogic.gdx.ai.btree.BehaviorTree;
import com.badlogic.gdx.ai.btree.utils.BehaviorTreeLibrary;
import com.badlogic.gdx.ai.btree.utils.BehaviorTreeLibraryManager;
import com.badlogic.gdx.ai.btree.utils.BehaviorTreeParser;
import com.badlogic.gdx.ai.msg.MessageManager;
import com.badlogic.gdx.ai.msg.Telegram;
import com.badlogic.gdx.ai.msg.Telegraph;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.utils.AnimationController;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.bullet.collision.btCollisionShape;
import com.mygdx.game.GameScreen;
import com.mygdx.game.objects.dog.TaskAnimation;
import com.mygdx.game.pathfinding.Triangle;
import com.mygdx.game.steerers.FollowPathSteerer;
import com.mygdx.game.steerers.WanderSteerer;
import com.mygdx.game.utilities.AnimationListener;
import com.mygdx.game.utilities.Constants;
/**
* A dog character whose brain is modeled by a behavior tree.
*
* @author jsjolund
* @author davebaol
*/
public class DogCharacter extends GameCharacter implements Telegraph {
public enum DogArmature {
HEAD("head"), FRONT_SPINE("front_spine");
public final String id;
DogArmature(String id) {
this.id = id;
}
}
public static class DogSteerSettings implements SteerSettings {
public static float maxLinearAcceleration = 50f;
public static float maxLinearSpeed = 2f;
public static float maxAngularAcceleration = 100f;
public static float maxAngularSpeed = 15f;
public static float idleFriction = 0.9f;
public static float zeroLinearSpeedThreshold = 0.001f;
public static float runMultiplier = 3f;
public static float timeToTarget = 0.1f;
public static float arrivalTolerance = 0.1f;
public static float decelerationRadius = 0.5f;
public static float predictionTime = 0f;
public static float pathOffset = 1f;
@Override
public float getTimeToTarget() {
return timeToTarget;
}
@Override
public float getArrivalTolerance() {
return arrivalTolerance;
}
@Override
public float getDecelerationRadius() {
return decelerationRadius;
}
@Override
public float getPredictionTime() {
return predictionTime;
}
@Override
public float getPathOffset() {
return pathOffset;
}
@Override
public float getZeroLinearSpeedThreshold() {
return zeroLinearSpeedThreshold;
}
@Override
public float getIdleFriction() {
return idleFriction;
}
}
public String dogName;
public final BehaviorTree<DogCharacter> tree;
public final AnimationController animations;
public final FollowPathSteerer followPathSteerer;
public final WanderSteerer wanderSteerer;
public HumanCharacter human;
public boolean humanWantToPlay;
public boolean humanIsDead;
public boolean stickThrown;
public boolean stickCarried;
public boolean alreadyCriedForHumanDeath;
private final static Vector3 TMP_V1 = new Vector3();
private final static Vector3 TMP_V2 = new Vector3();
private final static Vector3 TMP_V3 = new Vector3();
/*
* Fields used to switch animation
*/
public TaskAnimation currentTaskAnimation;
public AnimationListener currentAnimationListener;
public TaskAnimation monitoredTaskAnimation;
public float switchAnimationTime;
static {
// Make the behavior tree library parser log
BehaviorTreeLibraryManager.getInstance().setLibrary(new BehaviorTreeLibrary(BehaviorTreeParser.DEBUG_HIGH));
}
public DogCharacter(Model model, String name,
Vector3 location, Vector3 rotation, Vector3 scale,
btCollisionShape shape, float mass,
short belongsToFlag, short collidesWithFlag,
boolean callback, boolean noDeactivate) {
super(model, name,
location, rotation, scale,
shape, mass,
belongsToFlag, collidesWithFlag,
callback, noDeactivate,
new DogSteerSettings());
// Create behavior tree through the library
BehaviorTreeLibraryManager btlm = BehaviorTreeLibraryManager.getInstance();
this.tree = btlm.createBehaviorTree("btrees/dog.btree", this);
// Create animation controller
animations = new AnimationController(modelInstance);
// Create path follower
followPathSteerer = new FollowPathSteerer(this);
// Create wander steerer
wanderSteerer = new WanderSteerer(this);
// Init flags
humanWantToPlay = false;
stickThrown = false;
alreadyCriedForHumanDeath = false;
humanIsDead = false;
}
public boolean followPath(Triangle targetTriangle, Vector3 targetPoint) {
return followPathSteerer.calculateNewPath(targetTriangle, targetPoint);
}
@Override
public void update(float deltaTime) {
super.update(deltaTime);
// Step the tree either directly or through the editor
//
// TODO: handle this in a better way
// Ideally the dog should not know of the tree editor
GameScreen.screen.stage.btreeController.step(this, deltaTime);
if (stickCarried) {
Vector3 mouthPos = getBoneMidpointWorldPosition(DogArmature.HEAD.id, TMP_V1);
Vector3 headDirection = getBoneDirection(DogArmature.HEAD.id, TMP_V2);
human.stick.modelTransform.setToLookAt(headDirection, TMP_V3.set(Constants.V3_UP).scl(-1));
human.stick.modelTransform.setTranslation(mouthPos);
human.stick.body.setWorldTransform(human.stick.modelTransform);
}
}
@Override
public boolean handleMessage(Telegram telegram) {
switch (telegram.message) {
case Constants.MSG_DOG_LETS_PLAY:
humanWantToPlay = true;
stickThrown = false;
break;
case Constants.MSG_DOG_LETS_STOP_PLAYING:
humanWantToPlay = false;
break;
case Constants.MSG_DOG_HUMAN_IS_DEAD:
humanIsDead = true;
humanWantToPlay = false;
alreadyCriedForHumanDeath = false;
break;
case Constants.MSG_DOG_HUMAN_IS_RESURRECTED:
humanIsDead = false;
alreadyCriedForHumanDeath = false;
break;
case Constants.MSG_DOG_STICK_THROWN:
stickThrown = true;
break;
}
// Update GUI buttons if the dog's owner is selected
if (this.human != null && this.human.selected) {
MessageManager.getInstance().dispatchMessage(Constants.MSG_GUI_UPDATE_DOG_BUTTON, this.human);
}
return true;
}
public boolean isHumanCloseEnough(float radius) {
return human.getPosition().dst2(this.getPosition()) < radius * radius;
}
public void setCarryStick() {
stickCarried = true;
}
public void giveStickToHuman() {
stickCarried = false;
stickThrown = false;
human.assignStick(human.stick);
}
}