Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chase and attack #88

Merged
merged 9 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading