void OurEnemyActor::walkingAgent(ACTORid enemyID, EnemyTeam **team, int teamCount) { float origin[3] = {0,0,0}; //敵人位置 float enemyPos[3]; FnActor enemy; enemy.Object(enemyID); enemy.GetPosition(enemyPos); //自己位置 float selfPos[3]; FnActor self; self.Object(aID); self.GetPosition(selfPos); float selfFDir[3], selfUDir[3]; actor.GetDirection(selfFDir,selfUDir); float distance = twoPointDis(selfPos, enemyPos); float flockingPos[3]; flockingPosition(flockingPos, selfPos, enemyPos, team, teamCount); float flockingDis = twoPointDis(flockingPos, origin); float newPos[3]; newPos[0] = flockingPos[0]+selfPos[0]; newPos[1] = flockingPos[1]+selfPos[1]; newPos[2] = flockingPos[2]+selfPos[2]; //先轉向再移動,再轉回來 actorFaceTo(aID, newPos[0], newPos[1], newPos[2]); actor.MoveForward(flockingDis,true); //actor.SetWorldDirection(selfFDir,selfUDir); actorFaceTo(aID, enemyPos[0], enemyPos[1], enemyPos[2]); //actor.SetPosition(newPos); if(distance < AWARE_DISTANCE) { if(distance <= COMBAT_DISTANCE) { sendAction(ourCombatIdleAction); } else { sendAction(ourRunAction); actor.MoveForward(SPEED, TRUE, FALSE, 0.0f, TRUE); } } else { sendAction(ourIdleAction); } }
int ActorStateMachine::ChangeState(ActorState s, BOOL forceSet){ if (forceSet == FALSE && this->state == s){ return 0;// keep the past action play }else{ this->state = s; } if (s == STATEIDLE || s == STATERUN || s == STATEDAMAGE || s == STATEDIE ||s == STATEGUARD){ if (s == STATEIDLE){ this->SetNewAction("CombatIdle"); }else if (s == STATERUN){ this->SetNewAction("Run"); }else if (s == STATEDAMAGE){ FnActor actor; actor.Object(this->character); //actor.MoveForward(-MOVE_LENGTH,TRUE, FALSE, 0.0, TRUE); //this->SetNewAction("LightDamage"); this->currentAttackIndex = 0; this->lastAttackIndex = 0; this->SetNewAction("HeavyDamage"); }else if (s == STATEDIE){ this->SetNewAction("Die"); }else if (s == STATEGUARD){ this->SetNewAction("Guard"); } }else if (s == STATEATTACK){ // Serial attack start; this->startAttack = TRUE; }else if (s == STATEVANISH){ FnWorld gw; gw.Object(gID); gw.SetTexturePath("Data\\FXs\\Textures"); gw.SetObjectPath("Data\\FXs\\Models"); float pos[3]; FnActor actor; actor.Object(this->character); actor.GetWorldPosition(pos); fxDie = new eF3DFX(sID); fxDie->SetWorkPath("Data\\FXs"); BOOL beOK = fxDie->Load("dust3"); eF3DBaseFX *fx; int i, numFX = fxDie->NumberFXs(); for (i = 0; i < numFX; i++) { fx = fxDie->GetFX(i); fx->InitPosition(pos); } } return 0; }
BOOL ActorStateMachine::initActionIDMap(char *ActionFilename){ FILE *fp = fopen(ActionFilename,"r"); if (fp == NULL){ sprintf(debug, "%s ActionFilename failed: %s\n", debug, ActionFilename); return FALSE; } char systemName[100]; char designName[100]; int ret; FnActor actor; actor.Object(this->character); ACTIONid aID; while (!feof(fp)){ ret = fscanf(fp, "%s %s", systemName, designName); if (ret != 2){ sprintf(debug, "%s fscanf actionID failed\n", debug, systemName, designName); return FALSE; } aID = actor.GetBodyAction(NULL,designName); if (aID == FAILED_ID){ sprintf(debug, "%s init actionID %s %s failed\n", debug, systemName, designName); continue; } string actionName(systemName); this->ActionIDMap[actionName] = aID; } return TRUE; }
BOOL ActorStateMachine::initLife(){ this->life = (int) MAX_LIFE; FnScene scene; scene.Object(sID); bloodID = scene.CreateObject(ROOT); FnObject blood; blood.Object(bloodID); //FnBillBoard blood; //blood.Object(bloodID, 0); FnActor actor; actor.Object(this->character); OBJECTid baseID = actor.GetBaseObject(); float pos[3], size[2], color[3]; pos[0] = 0.0f; pos[1] = 0.0f; pos[2] = 80.0f; size[0] = 50.0f; size[1] = 2.0f; color[0] = 1.0f; color[1] = color[2] = 0.0f; blood.Billboard(pos, size, NULL, 0, color); blood.SetParent(baseID); return TRUE; }
BOOL ActorStateMachine::UpdateEffectiveAttack(){ FnActor actor; actor.Object(this->character); float frame = actor.QueryCurrentFrame(0); //sprintf(debug, "%s frame:%lf\n", debug, frame ); if (frame > 15.0){ this->effectiveAttack = TRUE; } /* if (this->attackKeyQueue[currentAttackIndex] == ULTIMATE_ATT){ if (frame > 20.0){ this->newAttack = TRUE; this->effectiveAttack = TRUE; } }else if (this->attackKeyQueue[currentAttackIndex] == HEAVY_ATT){ if (frame > 20.0){ this->effectiveAttack = TRUE; } }else if (this->currentAttackIndex > 0){ if (frame > 10.0){ this->effectiveAttack = TRUE; } }*/ return FALSE; }
BOOL ActorStateMachine::PlayAttackAction(int skip){ FnActor actor; actor.Object(this->character); //ACTIONid actionID; char attackName[20] = "\0"; if (this->startAttack == TRUE){// first attack this->lastAttackFrame = 0.0f; this->startAttack = FALSE; // reset //sprintf(debug, "%sstart attack\n", debug); if (this->attackKeyQueue[currentAttackIndex] == NORMAL_ATT ){ sprintf(attackName, "N%d", currentAttackIndex + 1); // attackName should be "N1" }else if (this->attackKeyQueue[currentAttackIndex] == HEAVY_ATT){ sprintf(attackName, "H%d", currentAttackIndex + 1); // attackName should be "H1" } string systemName(attackName); this->SetNewAction(systemName); // it performs the new attack // set the flag here and BattleRoom will check it. this->newAttack = TRUE; }else{ // the attack name should be refine from reading the file BOOL ret = actor.Play(0,ONCE, (float)skip, TRUE,TRUE); this->UpdateEffectiveAttack(); if (ret == FALSE){ this->lastAttackFrame = 0.0f; // play the next one this->effectiveAttack = FALSE; currentAttackIndex++; if (currentAttackIndex >= lastAttackIndex){ // finish attacking this->ChangeState(STATEIDLE);// should be change into combatidle; this->attackDisable = FALSE; currentAttackIndex = 0; lastAttackIndex = 0; return FALSE; }else if (this->attackKeyQueue[currentAttackIndex] == NORMAL_ATT){ // get the next one attacking pos sprintf(attackName, "N%d", currentAttackIndex + 1); }else if (this->attackKeyQueue[currentAttackIndex] == HEAVY_ATT){ // get the next one attacking pos sprintf(attackName, "H%d", currentAttackIndex); }else{ sprintf(debug, "%s next Attack fail condition\n", debug); return FALSE; } string systemName(attackName); this->SetNewAction(systemName); // it performs the new attack // set the flag here and BattleRoom will check it and set the flag to FALSE after checking. this->newAttack = TRUE; } } //sprintf(debug, "%s lastAttack%d\n", debug, lastAttackIndex ); return TRUE; }
int ActorStateMachine::AttackEnemy(float enemyPos[3], SHOT_CODE *shot_code){ if (shot_code != NULL){ *shot_code = FALSE; } // the return value is the attack power FnActor actor; actor.Object(this->character); float attackerPos[3], attackerDir[3]; actor.GetWorldPosition(attackerPos); actor.GetWorldDirection(attackerDir,NULL); float frame = actor.QueryCurrentFrame(0); float dist = 0.0; for (int i = 0;i< 3;i++){ dist += (attackerPos[i] - enemyPos[i]) * (attackerPos[i] - enemyPos[i]); } //sprintf(debug, "%s dist = %lf\n",debug,dist); if ( dist >= ROBOT_ATTACKRANGE ){ return 0; // no attack power } float cosine,dotProduct; //float v[3]; dotProduct = 0.0; for (int i = 0;i< 3;i++){ dotProduct += (enemyPos[i] - attackerPos[i]) * attackerDir[i]; } float length = 0.0; for (int i = 0;i< 3;i++){ length += (enemyPos[i] - attackerPos[i])* (enemyPos[i] - attackerPos[i]); } cosine = dotProduct / sqrt(length); //sprintf(debug, "%s cosine = %lf\n",debug,cosine); if (this->attackKeyQueue[currentAttackIndex] == HEAVY_ATT || currentAttackIndex == MAXATTACK -1){ *shot_code = BIG_SHOT; }else { *shot_code = SMALL_SHOT; } if (this->currentAttackIndex == 0){ if (cosine > 0.8){ // normal or heavy attack, only attack the front side enemy //sprintf(debug, "%s attack power = %d\n",debug,1); return 1; } }else if (this->currentAttackIndex <= 3){ if (cosine >= 0.0){ //sprintf(debug, "%s attack power = %d\n",debug,2); return 3; } } return 0; }
void ActorStateMachine::TakeDamage(int damage, SHOT_CODE shot_code, float *attackerPos ){ FnActor actor; actor.Object(character); float pos[3]; float dir[3]; actor.GetWorldPosition(pos); actor.GetWorldDirection(dir, NULL); if ( shot_code != STUCK_SHOT && attackerPos !=NULL){ float newDir[3]; newDir[0] = attackerPos[0] - pos[0]; newDir[1] = attackerPos[1] - pos[1]; newDir[2] = 0.0f; actor.SetWorldDirection(newDir,NULL); if (shot_code == BIG_SHOT){ actor.MoveForward(-OUTSHOT_DIS,TRUE, FALSE, 0.0, TRUE); //sprintf(debug, "%s OUTSHOT_DIS\n", debug); }else if (shot_code == SMALL_SHOT){ actor.MoveForward(-SMALL_OUTSHOT_DIS,TRUE, FALSE, 0.0, TRUE); //sprintf(debug, "%s SMALL_OUTSHOT_DIS\n", debug); } actor.SetWorldDirection(dir,NULL); } if (this->state == STATEGUARD){ FnAudio audio; audio.Object(audioG); audio.Play(ONCE); return; // no damage }else{ FnAudio audio; audio.Object(audioD); //if (audio.IsPlaying() == FALSE){ audio.Play(ONCE); //} } this->life -= damage; //sprintf(debug, "%s life=%d\n", debug, this->life); if (this->life <= 0) { this->ChangeState(STATEDIE, TRUE); }else { this->ChangeState(STATEDAMAGE, TRUE); } this->UpdateLifeBillboard(); }
BOOL ActorStateMachine::SetNewAction(string systemName){ ACTIONid actionID = this->ActionIDMap[systemName]; FnActor actor; actor.Object(this->character); if (actor.MakeCurrentAction(0,NULL,actionID) == FAILED_ID){ sprintf(debug, "%s make current action %s fail\n", debug, systemName.c_str()); return FALSE; }else{ //sprintf(debug, "%s %s make successful\n", debug, systemName.c_str()); } if (actor.Play(0,START, 0.0, FALSE,TRUE) == FALSE){ sprintf(debug, "%s %s play action failed\n", debug, systemName.c_str()); return FALSE; }else{ //sprintf(debug, "%s %s play successful\n", debug, systemName.c_str()); } return TRUE; }
void OurActor::ourPlayAction() { FnActor actor; actor.Object( aID ); if(::actorChangePose( aID, current_OurAction->actID )) { current_frame = 0; } //it's a loop action? if( current_OurAction->type == Action_type::ACTION_IDLE() || current_OurAction->type == Action_type::ACTION_WALK() ) { bool notOver; notOver = actor.Play(0, ONCE, current_OurAction->play_speed, false, true); if( notOver ) current_frame += current_OurAction->play_speed; else { current_frame = 0; actor.MakeCurrentAction(0, NULL, current_OurAction->actID); } } else { bool notOver; notOver = actor.Play(0, ONCE, current_OurAction->play_speed, FALSE, TRUE); if( !notOver ) { if( current_OurAction->type.value != Action_type::ACTION_DIE() ) current_OurAction = ourIdleAction; } else current_frame += current_OurAction->play_speed; } playActionAudio(); playActionFx(); }
BOOL ActorStateMachine::PlayAction(int skip){ FnActor actor; actor.Object(this->character); if (this->CanBeControl() == TRUE){ actor.Play(0,LOOP, (float)skip, FALSE,TRUE); }else if (this->state == STATEATTACK){ this->PlayAttackAction(skip); }else if (this->state == STATEDAMAGE){ BOOL ret = actor.Play(0,ONCE, (float)skip, TRUE,TRUE); if (ret == FALSE){ //sprintf(debug, "%s damage end\n",debug); this->ChangeState(STATEIDLE); } }else if (this->state == STATEDIE){ BOOL ret = actor.Play(0,ONCE, (float)skip, TRUE,TRUE); if (ret == FALSE){ sprintf(debug, "%s character die\n",debug); this->ChangeState(STATEVANISH); } }else if (this->state == STATEVANISH){ if (this->fxDie != NULL) { BOOL beOK = this->fxDie->Play((float) skip); if (!beOK) { //fxDie->Reset(); // make it from the starting position and play it again // should delete the character delete fxDie; this->fxDie = NULL; FnScene scene; scene.Object(sID); scene.DeleteActor(this->character); } } } return TRUE; }
void AIControl::moveTowardLyubu() { for (int i = 0;i< this->npcStateMachineList.size(); i++){ if (npcStateMachineList[i]->CanBeControl() == FALSE){ continue; } int npcId = npcStateMachineList[i]->character; FnActor lyubu; FnActor npc; lyubu.Object(this->lyubuId); npc.Object(npcId); float lyubuPos[3]; float npcPos[3]; lyubu.GetWorldPosition(lyubuPos); npc.GetWorldPosition(npcPos); float distance; distance = sqrt((npcPos[0] - lyubuPos[0]) * (npcPos[0] - lyubuPos[0]) + (npcPos[1] - lyubuPos[1]) * (npcPos[1] - lyubuPos[1])); //+ (npcPos[2] - lyubuPos[2]) * (npcPos[2] - lyubuPos[2])); //sprintf(debug, "%s x = %f y = %f z = %f\n",debug, lyubuPos[0], lyubuPos[1], lyubuPos[2]); if (distance > ATTACK_DISTANCE && ((npcStateMachineList[i] == this->bossStateMachine && distance < BOSS_KEEP_TRACK_DISTANCE) || distance < KEEP_TRACK_DISTANCE)) { //turn toward lyubu float newFDir[3], normalize, offset; if (npcStateMachineList[i] != this->bossStateMachine) { srand ( time(NULL) + i); offset = rand() % NPC_MOVE_OFFSET + 1; lyubuPos[0] += offset; lyubuPos[1] += offset; lyubuPos[2] += offset; } newFDir[0] = lyubuPos[0] - npcPos[0]; newFDir[1] = lyubuPos[1] - npcPos[1]; newFDir[2] = lyubuPos[2] - npcPos[2]; normalize = sqrt(newFDir[0] * newFDir[0] + newFDir[1] * newFDir[1] + newFDir[2] * newFDir[2]); newFDir[0] /= normalize; newFDir[1] /= normalize; newFDir[2] /= normalize; float npcFDir[3], npcUDir[3]; npc.GetWorldDirection(npcFDir, npcUDir); npc.SetWorldDirection(newFDir, npcUDir); //move forward int block = npc.MoveForward(MOVE_DISTANCE,TRUE, FALSE, 0.0, TRUE); if (block) { //sprintf(debug, "%s npc is blocked\n",debug); while (npc.MoveForward(MOVE_DISTANCE,TRUE, FALSE, 0.0, TRUE)) { sprintf(debug, "%s npc turn right\n",debug); npc.TurnRight(300); npc.MoveForward(MOVE_DISTANCE,TRUE, FALSE, 0.0, TRUE); } } npcStateMachineList[i]->ChangeState(STATERUN, FALSE); } else if (distance <= ATTACK_DISTANCE) { //before attack, turn toward lyubu float newFDir[3], normalize, offset; newFDir[0] = lyubuPos[0] - npcPos[0]; newFDir[1] = lyubuPos[1] - npcPos[1]; newFDir[2] = lyubuPos[2] - npcPos[2]; normalize = sqrt(newFDir[0] * newFDir[0] + newFDir[1] * newFDir[1] + newFDir[2] * newFDir[2]); newFDir[0] /= normalize; newFDir[1] /= normalize; newFDir[2] /= normalize; float npcFDir[3], npcUDir[3]; npc.GetWorldDirection(npcFDir, npcUDir); npc.SetWorldDirection(newFDir, npcUDir); //sprintf(debug, "%s distance = %f\n",debug,distance); srand ( time(NULL) + i); float rate = rand() % 100; if (this->npcStateMachineList[i] == this->bossStateMachine){ if (rate > GUARD_RATE ) { npcStateMachineList[i]->AppendAttackCode(NORMAL_ATT); npcStateMachineList[i]->AppendAttackCode(NORMAL_ATT); npcStateMachineList[i]->AppendAttackCode(HEAVY_ATT); }else{ npcStateMachineList[i]->CharacterSetGuard(); } }else { if (rate > GUARD_RATE * 2) { npcStateMachineList[i]->AppendAttackCode(NORMAL_ATT); } else{ npcStateMachineList[i]->CharacterSetIdle(); } } } else { npcStateMachineList[i]->CharacterSetIdle(); } } }
//計算在這個群體中適當的位置 void OurEnemyActor::flockingPosition(float *newPos, float *selfPos, float *targetPos, EnemyTeam **team, int teamCount) { float attraction[3] = {0,0,0}; //共有多少隊友離你夠近,會影響到調整距離 int member_num = 0; for(int i = 0; i < teamCount; i++) { //驚動狀態的隊伍才考慮 //if(team[i]->aware) { //separation & cohesion for(int mi = 0; mi < team[i]->member_num; mi++) { //只檢查沒掛的人,且不要把自己算進去 if(team[i]->members[mi]->HP > 0 && team[i]->members[mi]->aID != this->aID) { float friPos[3]; FnActor fri; fri.Object(team[i]->members[mi]->aID); fri.GetPosition(friPos); float dis = sqrt(pow(friPos[0]-selfPos[0],2) +pow(friPos[1]-selfPos[1],2) +pow(friPos[2]-selfPos[2],2)); if(dis < OVERLAP_DISTANCE ||(team[i] == this->team && dis < CROWDED_DISTANCE)) { //separation if(dis == 0) { attraction[0] += 1; attraction[1] += 1; } else { attraction[0] += -(friPos[0]-selfPos[0])*pow(1-dis/CROWDED_DISTANCE,2); attraction[1] += -(friPos[1]-selfPos[1])*pow(1-dis/CROWDED_DISTANCE,2); attraction[2] += -(friPos[2]-selfPos[2])*pow(1-dis/CROWDED_DISTANCE,2); } } else if(team[i] == this->team) { //cohesion attraction[0] += (friPos[0]-selfPos[0])*pow((dis-CROWDED_DISTANCE)/(AWARE_DISTANCE-CROWDED_DISTANCE),2); attraction[1] += (friPos[1]-selfPos[1])*pow((dis-CROWDED_DISTANCE)/(AWARE_DISTANCE-CROWDED_DISTANCE),2); attraction[2] += (friPos[2]-selfPos[2])*pow((dis-CROWDED_DISTANCE)/(AWARE_DISTANCE-CROWDED_DISTANCE),2); } member_num++; } } } } //若與呂布重疊就分離 float dis = sqrt(pow(targetPos[0]-selfPos[0],2) +pow(targetPos[1]-selfPos[1],2) +pow(targetPos[2]-selfPos[2],2)); if(dis < OVERLAP_DISTANCE) { //separation if(dis == 0) { attraction[0] += 1; attraction[1] += 1; } else { attraction[0] += -(targetPos[0]-selfPos[0])*pow(1-dis/CROWDED_DISTANCE,2); attraction[1] += -(targetPos[1]-selfPos[1])*pow(1-dis/CROWDED_DISTANCE,2); attraction[2] += -(targetPos[2]-selfPos[2])*pow(1-dis/CROWDED_DISTANCE,2); } member_num++; } if(member_num > 0) { attraction[0] /= member_num; attraction[1] /= member_num; attraction[2] /= member_num; } /* //cohesion float sum[3] = {0,0,0}; for(int i = 0; i < member_num; i++) { if(team->members[i]->aID != aID) { float friPos[3]; FnActor fri; fri.Object(team->members[i]->aID); fri.GetPosition(friPos); sum[0] += friPos[0]; sum[1] += friPos[1]; sum[2] += friPos[2]; } } float avg[3] = {sum[0]/member_num, sum[1]/member_num, sum[2]/member_num}; float avgDis = sqrt((avg[0]-selfPos[0])*(avg[0]-selfPos[0])+ (avg[1]-selfPos[1])*(avg[1]-selfPos[1])+ (avg[2]-selfPos[2])*(avg[2]-selfPos[2])); float cohesion[3] = {(avg[0]-selfPos[0])*pow((avgDis-crowdedDistance)/(awareDistance-crowdedDistance),2), (avg[1]-selfPos[1])*pow((avgDis-crowdedDistance)/(awareDistance-crowdedDistance),2), (avg[2]-selfPos[2])*pow((avgDis-crowdedDistance)/(awareDistance-crowdedDistance),2)}; */ //align float align[3] = {0,0,0}; /* float sumV[3] = {0,0,0}; for(int i = 0; i < member_num; i++) { if(team->members[i]->aID != aID && team->members[i]->HP > 0) { float friPos[3]; FnActor fri; fri.Object(team->members[i]->aID); fri.GetPosition(friPos); float vx = (targetPos[0]-friPos[0]); float vy = (targetPos[1]-friPos[1]); float vz = (targetPos[2]-friPos[2]); float dis = sqrt(sumV[0]*sumV[0]+sumV[1]*sumV[1]+sumV[2]*sumV[2]); if(dis != 0) { sumV[0] += vx/dis; sumV[1] += vy/dis; sumV[2] += vz/dis; } } } if(member_num > 0) { align[0] = sumV[0]/member_num; align[1] = sumV[1]/member_num; align[1] = sumV[2]/member_num; } */ newPos[0] = attraction[0]+align[0]; newPos[1] = attraction[1]+align[1]; newPos[2] = attraction[2]+align[2]; }