Files
tyra-linux/demo/src/states/game/enemy/enemy.cpp
T
2022-08-20 16:38:44 +02:00

160 lines
4.1 KiB
C++

/*
# _____ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licensed under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "states/game/enemy/enemy.hpp"
#include <functional>
using Tyra::Color;
using Tyra::Math;
namespace Demo {
Enemy::Enemy(Engine* engine, const EnemyInfo& t_info) {
info = t_info;
mesh = new DynamicMesh(*info.motherMesh);
auto* bodyMaterial = mesh->getMaterialByName("BodyMaterial");
auto* clothMaterial = mesh->getMaterialByName("ClothMaterial");
TYRA_ASSERT(bodyMaterial != nullptr && clothMaterial != nullptr,
"Body and cloth materials not found!");
info.bodyTexture->addLink(bodyMaterial->id);
info.clothTexture->addLink(clothMaterial->id);
float r = Math::randomf(75.0F, 128.0F);
float g = Math::randomf(75.0F, 128.0F);
float b = Math::randomf(75.0F, 128.0F);
bodyMaterial->ambient.set(r, g, b);
clothMaterial->ambient.set(r, g, b);
walkSequence = {0, 1, 2};
fightSequence = {3, 4, 5};
terrainLeftUp = info.terrainLeftUp;
terrainRightDown = info.terrainRightDown;
setMeshToSpawn();
mesh->animation.setSequence(walkSequence);
mesh->animation.loop = true;
mesh->animation.setCallback(
std::bind(&Enemy::animationCallback, this, std::placeholders::_1));
mesh->translation.translateY(-5.0F);
audio = &engine->audio;
audio->adpcm.setVolume(30, info.adpcmChannel);
allocateOptions();
pair = new RendererDynamicPair{mesh, options};
}
Enemy::~Enemy() {
delete mesh;
delete options;
delete pair;
}
void Enemy::setMeshToSpawn() {
auto spawnPoint =
Vec4(Math::randomf(terrainLeftUp.x, terrainRightDown.x), 0.0F,
Math::randomf(terrainLeftUp.z, terrainRightDown.z), 1.0F);
mesh->setPosition(spawnPoint);
}
void Enemy::update(const Heightmap& heightmap, const Vec4& playerPosition,
const PlayerShootAction& shootAction) {
auto* enemyPosition = mesh->getPosition();
handlePlayerShoot(shootAction);
auto diff = *enemyPosition - playerPosition;
auto ang = Math::atan2(diff.x, diff.z);
if (diff.length() > 150.0F) {
walk(heightmap, diff);
} else {
fight();
}
const float naturalRotation = 3.1F;
mesh->rotation.identity();
ang += naturalRotation;
mesh->rotation.rotateByAngle(ang, Vec4(0.0F, 1.0F, 0.0F, 0.0F));
mesh->update();
}
void Enemy::handlePlayerShoot(const PlayerShootAction& shootAction) {
if (!shootAction.isShooting) {
return;
}
const auto& ray = shootAction.ray.value();
auto bbox =
mesh->getCurrentBoundingBox().getTransformed(mesh->getModelMatrix());
auto isOnEnemy = ray.intersectBox(bbox.min(), bbox.max());
if (isOnEnemy) {
setMeshToSpawn();
audio->adpcm.tryPlay(info.adpcmDeath, info.adpcmChannel);
}
}
void Enemy::walk(const Heightmap& heightmap, const Vec4& positionDiff) {
if (isFighting) {
mesh->animation.setSequence(walkSequence);
}
isFighting = false;
isWalking = true;
auto* enemyPosition = mesh->getPosition();
auto normalized = positionDiff;
normalized.normalize();
const float speed = 2.5F;
auto nextPos = *enemyPosition - normalized * speed;
nextPos.y = heightmap.getHeightOffset(nextPos) - 60.0F;
mesh->translation.identity();
mesh->translation.translate(nextPos);
}
void Enemy::fight() {
if (isWalking) {
mesh->animation.setSequence(fightSequence);
}
isFighting = true;
isWalking = false;
}
void Enemy::animationCallback(const AnimationSequenceCallback& callback) {
if (callback == AnimationSequenceCallback::AnimationSequenceCallback_Loop) {
if (isFighting) {
audio->adpcm.tryPlay(info.adpcmPunch, info.adpcmChannel);
}
}
}
void Enemy::allocateOptions() {
options = new DynPipOptions();
options->frustumCulling =
Tyra::PipelineFrustumCulling::PipelineFrustumCulling_None;
}
} // namespace Demo