// // this is the smarts for the rocket launcher in coop // // if there is a player behind/below the carrier, and we can shoot, and we can trace a LOS to them .. // pick one of the group, and let it rip void CCarrier::CoopCheck () { // no more than 4 players in coop, so.. static std::vector<CPlayerEntity*> targets; targets.clear(); // if we're not in coop, this is a noop if (!(Game.GameMode & GAME_COOPERATIVE)) return; // if we are, and we have recently fired, bail if (RefireWait > Level.Frame) return; // cycle through players for (int player = 1; player <= Game.MaxClients; player++) { CPlayerEntity *ent = entity_cast<CPlayerEntity>(Game.Entities[player].Entity); if (!ent->GetInUse()) continue; if (IsInBack(Entity, ent) || IsBelow(Entity, ent)) { CTrace tr (Entity->State.GetOrigin(), ent->State.GetOrigin(), Entity, CONTENTS_MASK_SOLID); if (tr.Fraction == 1.0) targets.push_back (ent); } } if (targets.empty()) return; int target = irandom(targets.size()); // make sure to prevent rapid fire rockets RefireWait = Level.Frame + CARRIER_ROCKET_TIME; // save off the real enemy IBaseEntity *OldEnemy = *Entity->Enemy; // set the new guy as temporary enemy Entity->Enemy = targets[target]; Rocket (); // put the real enemy back Entity->Enemy = OldEnemy; // we're done return; }
/* \brief 二分查找法,求多边形上方向是u的极点,设凸多边形是逆时针顺序的。 */ int ExtremePoint_BinarySearch( const Point2D* p, int n, const Vector2D& u) { int a = 0, b = n, m; int upA = Direction(p[1] - p[0],u) , upM; if( upA<=0 && !IsAbove(p[n-1],p[0],u) ) return 0; while(true) { m = (a + b) / 2; upM = Direction(p[(m+1)%n] - p[m],u); if( upM<=0 && !IsAbove(p[m-1],p[m],u) ) return m; if( upA>0 ) { if( upM<0 ) { //选择[a,m] b = m; } else if( IsAbove(p[a],p[m],u) ) { //选择[a,m] b = m; } else { //选择[m,b] a = m; upA = upM; } } else { if( upM>0 ) { //选择[m,b] a = m; upA = upM; } else if( IsBelow(p[a],p[m],u) ) { //选择[a,m] b = m; } else { //选择[m,b] a = m; upA = upM; } } } return 0; }
bool CCarrier::CheckAttack () { if ((Entity->Enemy->EntityFlags & EF_HURTABLE) && entity_cast<IHurtableEntity>(*Entity->Enemy)->Health > 0) { // see if any entities are in the way of the shot vec3f spot1 = Entity->State.GetOrigin() + vec3f(0, 0, Entity->ViewHeight), spot2 = Entity->Enemy->State.GetOrigin() + vec3f(0, 0, Entity->Enemy->ViewHeight); CTrace tr (spot1, spot2, Entity, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA); // do we have a clear shot? if (tr.Entity != Entity->Enemy) { // go ahead and spawn stuff if we're mad a a client if ((Entity->Enemy->EntityFlags & EF_PLAYER) && MonsterSlots > 2) { AttackState = AS_BLIND; return true; } // PGM - we want them to go ahead and shoot at info_notnulls if they can. if (Entity->Enemy->GetSolid() != SOLID_NOT || tr.Fraction < 1.0) //PGM return false; } } EnemyInfront = IsInFront(Entity, *Entity->Enemy); bool EnemyInback = IsInBack(Entity, *Entity->Enemy); bool EnemyBelow = IsBelow (Entity, *Entity->Enemy); EnemyRange = Range (Entity, *Entity->Enemy); IdealYaw = (Entity->Enemy->State.GetOrigin() - Entity->State.GetOrigin()).ToYaw(); // PMM - shoot out the back if appropriate if ((EnemyInback) || (!EnemyInfront && EnemyBelow)) { // this is using wait because the attack is supposed to be independent if (Level.Frame >= RefireWait) { RefireWait = Level.Frame + CARRIER_ROCKET_TIME; Attack (); if (frand() < 0.6) AttackState = AS_SLIDING; else AttackState = AS_STRAIGHT; return true; } } // melee attack if (EnemyRange == RANGE_MELEE) { AttackState = AS_MISSILE; return true; } float chance = 0.0f; if (AIFlags & AI_STAND_GROUND) chance = 0.4f; else { switch (EnemyRange) { case RANGE_MELEE: case RANGE_NEAR: case RANGE_MID: chance = 0.8f; break; case RANGE_FAR: chance = 0.5f; break; }; } // PGM - go ahead and shoot every time if it's a info_notnull if ((Entity->Enemy->GetSolid() == SOLID_NOT) || (frand () < chance)) { AttackState = AS_MISSILE; return true; } if (AIFlags & AI_FLY) { if (frand() < 0.6) AttackState = AS_SLIDING; else AttackState = AS_STRAIGHT; } return false; }
void CCarrier::Attack () { AIFlags &= ~AI_HOLD_FRAME; if (!Entity->Enemy) return; bool EnemyInback = IsInBack(Entity, *Entity->Enemy); EnemyInfront = IsInFront (Entity, *Entity->Enemy); bool EnemyBelow = IsBelow (Entity, *Entity->Enemy); if (BadArea) { if ((EnemyInback) || (EnemyBelow)) CurrentMove = &CarrierMoveAttackRocket; else if ((frand() < 0.1) || (Level.Frame < AttackFinished)) CurrentMove = &CarrierMovePreAttackMG; else { Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]); CurrentMove = &CarrierMoveAttackRail; } return; } if (AttackState == AS_BLIND) { CurrentMove = &CarrierMoveSpawn; return; } if (!EnemyInback && !EnemyInfront && !EnemyBelow) // to side and not under { if ((frand() < 0.1) || (Level.Frame < AttackFinished)) CurrentMove = &CarrierMovePreAttackMG; else { Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]); CurrentMove = &CarrierMoveAttackRail; } return; } if (EnemyInfront) { float range = (Entity->Enemy->State.GetOrigin() - Entity->State.GetOrigin()).Length(); if (range <= 125) { if ((frand() < 0.8) || (Level.Frame < AttackFinished)) CurrentMove = &CarrierMovePreAttackMG; else { Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]); CurrentMove = &CarrierMoveAttackRail; } } else if (range < 600) { float luck = frand(); if (MonsterSlots > 2) { if (luck <= 0.20) CurrentMove = &CarrierMovePreAttackMG; else if (luck <= 0.40) CurrentMove = &CarrierMoveAttackPreGrenade; else if ((luck <= 0.7) && !(Level.Frame < AttackFinished)) { Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]); CurrentMove = &CarrierMoveAttackRail; } else CurrentMove = &CarrierMoveSpawn; } else { if (luck <= 0.30) CurrentMove = &CarrierMovePreAttackMG; else if (luck <= 0.65) CurrentMove = &CarrierMoveAttackPreGrenade; else if (Level.Frame >= AttackFinished) { Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]); CurrentMove = &CarrierMoveAttackRail; } else CurrentMove = &CarrierMovePreAttackMG; } } else // won't use grenades at this range { float luck = frand(); if (MonsterSlots > 2) { if (luck < 0.3) CurrentMove = &CarrierMovePreAttackMG; else if ((luck < 0.65) && !(Level.Frame < AttackFinished)) { Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]); RailFireSpot = Entity->Enemy->State.GetOrigin() + vec3f(0, 0, Entity->Enemy->ViewHeight); CurrentMove = &CarrierMoveAttackRail; } else CurrentMove = &CarrierMoveSpawn; } else { if ((luck < 0.45f) || (Level.Frame < AttackFinished)) CurrentMove = &CarrierMovePreAttackMG; else { Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]); CurrentMove = &CarrierMoveAttackRail; } } } } else if ((EnemyBelow) || (EnemyInback)) CurrentMove = &CarrierMoveAttackRocket; }