Skip to content

Commit

Permalink
Merge pull request #88 from ntut-Tu/main
Browse files Browse the repository at this point in the history
chase and attack
  • Loading branch information
jonylu7 authored Jun 11, 2024
2 parents 3c3a82b + c78a3e6 commit 00e3475
Show file tree
Hide file tree
Showing 27 changed files with 433 additions and 90 deletions.
7 changes: 5 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,11 @@ set(INCLUDE_FILES
${INCLUDE_DIR}/Scene/SandBoxScene.hpp


${INCLUDE_DIR}/AI/Enemy.hpp
${INCLUDE_DIR}/AI/EnemyScripts.hpp

${INCLUDE_DIR}/Enemy/Enemy.hpp
${INCLUDE_DIR}/Enemy/EnemyScripts.hpp
${INCLUDE_DIR}/Enemy/AIGroupCommander.hpp


${INCLUDE_DIR}/Core/Context.hpp
${INCLUDE_DIR}/Core/DebugMessageCallback.hpp
Expand Down
179 changes: 178 additions & 1 deletion include/AI/AIGroupCommander.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,184 @@

#ifndef PRACTICALTOOLSFORSIMPLEDESIGN_AIGROUPCOMMANDER_HPP
#define PRACTICALTOOLSFORSIMPLEDESIGN_AIGROUPCOMMANDER_HPP
#include "Mechanics/UnitManager.hpp"
#include "Avatar/Avatar.hpp"
#define GROUP_SIZE 4
#define AUTO_FIND_RANGE 4
#define AUTO_ATTACK_METHOD 2
class AIGroupCommander {
private:
std::shared_ptr<UnitManager> m_PlayerUnitManager;
std::shared_ptr<UnitManager> m_AIUnitManager;
std::shared_ptr<AvatarManager> m_AIAvatarManager;
std::shared_ptr<MapClass> m_Map;

std::vector<std::vector<std::shared_ptr<Avatar>>> m_offensiveGroup;
std::vector<std::shared_ptr<Avatar>> m_defensiveGroup;
public:
AIGroupCommander(std::shared_ptr<UnitManager> PlayerUnitManager,std::shared_ptr<UnitManager> AIUnitManager,std::shared_ptr<MapClass> Map):m_PlayerUnitManager(PlayerUnitManager),m_AIUnitManager(AIUnitManager),m_Map(Map){};
~AIGroupCommander(){};
void Start(){
m_AIAvatarManager = m_AIUnitManager->getAvatarManager();
m_defensiveGroup = m_AIAvatarManager->getAvatarArray();
}
void Update(){
updateDefensiveGroup();
updateOffensiveGroup();
}


void setAllTroopToAttackMode(){
if(m_defensiveGroup.empty()){
return;
}
for(auto i : m_defensiveGroup){
addTroopToOffensiveGroup(i);
}
m_defensiveGroup.clear();
}
void setTroopToAttackMode(int num){
if(m_defensiveGroup.empty()){
return;
}
int max = static_cast<int>(m_defensiveGroup.size());
for(int i=max-1;i>=0 && num>=max-i;i--){
addTroopToOffensiveGroup(m_defensiveGroup[i]);
m_defensiveGroup.pop_back();
}
}

int getDefensiveTroopSize(){
return static_cast<int>(m_defensiveGroup.size());
}

int getOffensiveTroopSize(){
int totalSize = 0;
for (const auto& row : m_offensiveGroup) {
totalSize += static_cast<int>(row.size());
}
return totalSize;
}
protected:
void updateOffensiveGroup(){
for(auto i : m_offensiveGroup){
for(int j=0;j<static_cast<int>(i.size());j++){
if(i[j]->getHealth()->ifDead()){
i.erase(i.begin()+j);
}
}
}
updateOffensiveTroopAttackTarget();
}
void updateDefensiveGroup(){
for(int i = static_cast<int>(m_defensiveGroup.size());i>0;--i ){
if(m_defensiveGroup[i-1]->getHealth()->ifDead()){
m_defensiveGroup.erase(m_defensiveGroup.begin()+i-1);
}else{
autoAttack(m_defensiveGroup[i-1],AUTO_ATTACK_METHOD);
};
}
std::vector<std::shared_ptr<Avatar>> temp = m_AIAvatarManager->getAvatarArray();
for(int i=static_cast<int>(temp.size())-1;i>=0;i--){
if(temp[i]->getAIType()==AI_Type::NONE){
temp[i]->setAIType(AI_Type::DEFENCE);
m_defensiveGroup.push_back(temp[i]);
}
}
}
void addTroopToOffensiveGroup(std::shared_ptr<Avatar> unit){
auto it = std::find_if(m_offensiveGroup.begin(), m_offensiveGroup.end(), [](const std::vector<std::shared_ptr<Avatar>>& group) {
return group.size() < GROUP_SIZE;
});
unit->setAIType(AI_Type::ATTACK);
if (it != m_offensiveGroup.end()) {
it->push_back(unit);
} else {
m_offensiveGroup.push_back(std::vector<std::shared_ptr<Avatar>>());
m_offensiveGroup.back().push_back(unit);
}
}
void autoAttack(std::shared_ptr<Avatar> unit,int method){
switch (method) {
case 1:
for(auto i : m_PlayerUnitManager->getAvatarManager()->getAvatarArray()){
if(i->getDistance(unit->getCurrentLocationInCell())<=AUTO_FIND_RANGE){
//attack
m_AIAvatarManager->assignAttackOrderToAvatar(unit,i->getCurrentLocationInCell(),HouseType::ENEMY);
return;
}
}
break;
case 2:
glm::vec2 targetCell = m_Map->findEnemyInRange(AUTO_FIND_RANGE,unit->getCurrentLocationInCell(),HouseType::ENEMY);
if(targetCell.x == -1 && targetCell.y == -1){
return;
}
//attack
m_AIAvatarManager->assignAttackOrderToAvatar(unit,targetCell,HouseType::ENEMY);
break;
}
}
void updateOffensiveTroopAttackTarget(){
for(int i = static_cast<int>(m_offensiveGroup.size());i>0;--i){
auto& currentGroup = m_offensiveGroup[i-1];
printf("(updateOffensiveTroopAttackTarget)groupSize : %d\n",static_cast<int>(currentGroup.size()));
if(!currentGroup.empty()){
if (!m_AIAvatarManager->ifAvatarHasNemesis(currentGroup.front())){
// find new target
glm::vec2 targetCell = findTargetForOffensiveUnit(currentGroup.front());
if(targetCell.x==-1.f){
printf("(updateOffensiveTroopAttackTarget) No Target\n");
break;
}
// attack
for (int j=static_cast<int>(currentGroup.size());j>0;--j) {
if(currentGroup[j-1]->getHealth()->ifDead()){
currentGroup.erase(currentGroup.begin()+j-1);
}else{
m_AIAvatarManager->assignAttackOrderToAvatar(
currentGroup[j-1], targetCell,HouseType::ENEMY);
}
}
}else{
glm::vec2 targetCell = m_AIAvatarManager->getAvatarNemesisCell(currentGroup.front());
printf("(updateOffensiveTroopAttackTarget)AvatarHasNemesis : {%d,%d}\n",static_cast<int>(targetCell.x),static_cast<int>(targetCell.y));
}
}else{
m_offensiveGroup.erase(m_offensiveGroup.begin()+i-1);
}
}
}

glm::vec2 findTargetForOffensiveUnit(std::shared_ptr<Avatar> unit){
glm::vec2 targetCell = {-1.f,-1.f};
//issue: empty group should delete before this
if(false&&static_cast<int>(m_offensiveGroup.size())>=static_cast<int>(m_PlayerUnitManager->getAvatarManager()->getAvatarArray().size())){
if(!m_PlayerUnitManager->getStructureManager()->getStructureArray()->getBuiltStructureArray().empty()){
targetCell=m_PlayerUnitManager->getStructureManager()->getStructureArray()->getBuiltStructureArray().front()->getCurrentLocationInCell();
}
for(auto i : m_PlayerUnitManager->getStructureManager()->getStructureArray()->getBuiltStructureArray()){
if(unit->getDistance(i->getCurrentLocationInCell())<unit->getDistance(targetCell)){
//attack
targetCell = i->getCurrentLocationInCell();
}
}
}else{
if(!m_PlayerUnitManager->getAvatarManager()->getAvatarArray().empty()){
targetCell=m_PlayerUnitManager->getAvatarManager()->getAvatarArray().front()->getCurrentLocationInCell();
}
for(auto i : m_PlayerUnitManager->getAvatarManager()->getAvatarArray()){
if(i->getDistance(unit->getCurrentLocationInCell())<unit->getDistance(targetCell)){
//attack
targetCell = i->getCurrentLocationInCell();
}
}
}
printf("(findTargetForOffensiveUnit) TargetCell : {%d,%d}\n",targetCell.x,targetCell.y);
return targetCell;
}
};


class AIGroupCommander {};

#endif // PRACTICALTOOLSFORSIMPLEDESIGN_AIGROUPCOMMANDER_HPP
3 changes: 2 additions & 1 deletion include/AI/EnemyScripts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

#ifndef PRACTICALTOOLSFORSIMPLEDESIGN_ENEMYSCRIPTS_HPP
#define PRACTICALTOOLSFORSIMPLEDESIGN_ENEMYSCRIPTS_HPP
#include "Enemy.hpp"
#include "Mechanics/UnitManager.hpp"
#include "AIGroupCommander.hpp"

#define SPACE 4

Expand All @@ -17,6 +17,7 @@ class EnemyScripts{
std::shared_ptr<UnitManager> m_GameObjectManager;
std::shared_ptr<UnitManager> m_EnemyObjectManager;
std::shared_ptr<MapClass> m_Map;
std::shared_ptr<AIGroupCommander> m_AIGroupCommander;

glm::vec2 m_baseCell = {20, 20};
int constructCountX = 0;
Expand Down
9 changes: 8 additions & 1 deletion include/Avatar/Avatar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include "Selectable.hpp"
#include "Unit/Huntable.hpp"
#include "Util/Image.hpp"

enum class AI_Type{NONE,DEFENCE,ATTACK};
class Avatar : public Util::GameObject, public Selectable, public Huntable {

public:
Expand Down Expand Up @@ -62,6 +62,12 @@ class Avatar : public Util::GameObject, public Selectable, public Huntable {

void DrawAvatar();

void setAIType(AI_Type type){
m_aiType=type;
}
AI_Type getAIType(){
return m_aiType;
}
public:
GameObjectID getID() { return m_ID; }

Expand Down Expand Up @@ -104,5 +110,6 @@ class Avatar : public Util::GameObject, public Selectable, public Huntable {

private:
GameObjectID m_ID;
AI_Type m_aiType = AI_Type::NONE;
};
#endif // PRACTICALTOOLSFORSIMPLEDESIGN_AVATAR_HPP
2 changes: 1 addition & 1 deletion include/Avatar/AvatarOrder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#ifndef PRACTICALTOOLSFORSIMPLEDESIGN_AVATARORDER_HPP
#define PRACTICALTOOLSFORSIMPLEDESIGN_AVATARORDER_HPP
enum class AvatarOrderType { SPAWNED, OPEN_FIRE, MOVE, NO_ORDER, TAKEN_DAMAGE };
enum class AvatarOrderType { SPAWNED, OPEN_FIRE,CHASE, MOVE, NO_ORDER, TAKEN_DAMAGE };
class AvatarOrder {
public:
AvatarOrder() {}
Expand Down
1 change: 0 additions & 1 deletion include/Avatar/Infantry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ class Infantry : public Avatar {
// setHp(50);
getMoving()->setMovementSpeed(4);
}

private:
};
#endif // PRACTICALTOOLSFORSIMPLEDESIGN_INFANTRY_HPP
2 changes: 2 additions & 0 deletions include/Avatar/PathUtility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class PathUtility {
MoveDirection currentdir);
static MoveDirection findNewDirWhenCrash(Side side, glm::vec2 currentcell,
MoveDirection currentdir);
static std::string debug_dirToString(MoveDirection dir);

};

#endif // PRACTICALTOOLSFORSIMPLEDESIGN_PATHUTILITY_HPP
29 changes: 29 additions & 0 deletions include/Map/Map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,35 @@ class MapClass : public Core::Drawable {
getTileByCellPosition(position)->removeStructure();
}

glm::vec2 findEnemyInRange(int attackRange,glm::vec2 myCell,HouseType myHouse){
glm::vec2 NullPos={-1,-1};
int x,y=1;
int r=1;
for(r=1;r<attackRange;r++){
y=r;
for(x=-1*r;x<r;x++){
if(getTileByCellPosition({myCell.x+x,myCell.y+y})->ifEnemyAtTile(myHouse)){
return {myCell.x+x,myCell.y+y};
}
}
for(y=r-1;y>=-1*r;y--){
if(getTileByCellPosition({myCell.x+x,myCell.y+y})->ifEnemyAtTile(myHouse)){
return {myCell.x+x,myCell.y+y};
}
}
for(x=r-1;x>-1*r;x--){
if(getTileByCellPosition({myCell.x+x,myCell.y+y})->ifEnemyAtTile(myHouse)){
return {myCell.x+x,myCell.y+y};
}
}
for(y=-1*r+1;y<r;y++){
if(getTileByCellPosition({myCell.x+x,myCell.y+y})->ifEnemyAtTile(myHouse)){
return {myCell.x+x,myCell.y+y};
}
}
}
return NullPos;
}
protected:
void InitGrid();

Expand Down
25 changes: 25 additions & 0 deletions include/Map/Tile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,31 @@ class TileClass {
return false;
}

bool ifEnemyAtTile(HouseType myHouse) {
if(myHouse==HouseType::MY){
if (m_Structure->getID().getHouse() == HouseType::ENEMY) {
return true;
}
for (auto a : m_Avatars) {
if (a->getID().getHouse() == HouseType::ENEMY) {
return true;
}
}
return false;
}else if(myHouse==HouseType::ENEMY){
if (m_Structure->getID().getHouse() == HouseType::MY) {
return true;
}
for (auto a : m_Avatars) {
if (a->getID().getHouse() == HouseType::MY) {
return true;
}
}
return false;
}
}


private:
bool m_TerrainBuildable;
bool m_TerrainWalkable;
Expand Down
12 changes: 11 additions & 1 deletion include/Mechanics/AvatarManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,17 @@ class AvatarManager {

void assignAttackOrderToAvatar(std::shared_ptr<Avatar> unit,
glm::vec2 destcell);

void assignAttackOrderToAvatar(std::shared_ptr<Avatar> avatar,
glm::vec2 destcell,HouseType myHouse);
int getAvatarSize(){
return static_cast<int>(m_AvatarArray.size());
}
bool ifAvatarHasNemesis(std::shared_ptr<Avatar> unit){
return m_NemesisManager->ifAvatarHasNemesis(unit);
}
glm::vec2 getAvatarNemesisCell(std::shared_ptr<Avatar> unit){
return m_NemesisManager->getNemesisCell(unit);
}
protected:
void assignOrderToMyAvatar(std::shared_ptr<Avatar> unit);
void updateTileWhileAvatarMoving(std::shared_ptr<Avatar> unit);
Expand Down
1 change: 1 addition & 0 deletions include/Mechanics/AvatarNavigator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@ class AvatarNavigator {

private:
std::shared_ptr<MapClass> m_Map = std::make_shared<MapClass>();
MoveDirection recursionCrashHandler(MoveDirection currentDir,int count);
};
#endif // PRACTICALTOOLSFORSIMPLEDESIGN_AVATARNAVIGATOR_HPP
6 changes: 6 additions & 0 deletions include/Mechanics/NemesisManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ class NemesisManager {
return false;
}
}
glm::vec2 getNemesisCell(std::shared_ptr<Avatar> avatar){
if(!ifAvatarHasNemesis(avatar)){
return {-1.f,-1.f};
}
return m_Nemesis[avatar]->getCurrentLocationInCell();
}
bool ifNemesisWithinWeaponRange(std::shared_ptr<Avatar> hunter) {
if (ifAvatarHasNemesis(hunter) == false) {
return false;
Expand Down
6 changes: 2 additions & 4 deletions include/Mechanics/Player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,14 @@ class Player {
void addFixedPower(int value) { m_FixedPower += value; }

int getTotalCurrency() { return m_TotalCurrency; }
int getMaxTroopSize() { return m_MaxTroopSize; }


protected:
int
getTotalPower(std::vector<std::shared_ptr<Structure>> m_BuiltStructure) {
int totalPower = 0;
for (int i = 0; i < m_BuiltStructure.size(); i++) {
if(m_BuiltStructure[i]->getHouseType()==HouseType::MY){
totalPower += m_BuiltStructure[i]->getElectricPower();
}
totalPower += m_BuiltStructure[i]->getElectricPower();
}
return totalPower;
}
Expand Down
Loading

0 comments on commit 00e3475

Please sign in to comment.