int GatherShadowLightSources (void) { tObject *objP; int h, i, j, k, n, m = gameOpts->render.shadows.nLights; short *pnl; // tDynLight *pl; tShaderLight *psl; vmsVector vLightDir; psl = gameData.render.lights.dynamic.shader.lights; for (h = 0, i = gameData.render.lights.dynamic.nLights; i; i--, psl++) psl->bShadow = psl->bExclusive = 0; FORALL_OBJS (objP, h) { if (gameData.render.mine.bObjectRendered [h] != gameStates.render.nFrameFlipFlop) continue; pnl = gameData.render.lights.dynamic.nNearestSegLights + objP->info.nSegment * MAX_NEAREST_LIGHTS; k = h * MAX_SHADOW_LIGHTS; for (i = n = 0; (n < m) && (*pnl >= 0); i++, pnl++) { psl = gameData.render.lights.dynamic.shader.lights + *pnl; if (!psl->info.bState) continue; if (!CanSeePoint (objP, &objP->info.position.vPos, &psl->info.vPos, objP->info.nSegment)) continue; vLightDir = objP->info.position.vPos - psl->info.vPos; vmsVector::Normalize(vLightDir); if (n) { for (j = 0; j < n; j++) if (abs (vmsVector::Dot(vLightDir, gameData.render.shadows.vLightDir[j])) > 2 * F1_0 / 3) // 60 deg break; if (j < n) continue; } gameData.render.shadows.vLightDir [n] = vLightDir; gameData.render.shadows.objLights [k + n++] = *pnl; psl->bShadow = 1; } gameData.render.shadows.objLights [k + n] = -1; } psl = gameData.render.lights.dynamic.shader.lights; for (h = 0, i = gameData.render.lights.dynamic.nLights; i; i--, psl++) if (psl->bShadow) h++; return h; }
int GatherShadowLightSources (void) { CObject *objP; int h, i, j, k, l, n, m = gameOpts->render.shadows.nLights; short *pnl; // CDynLight *pl; CDynLight* prl; CFixVector vLightDir; n = lightManager.LightCount (1); for (h = l = 0; l < n; l++) { prl = lightManager.RenderLights (h); prl->render.bShadow = prl->render.bExclusive = 0; } FORALL_OBJS (objP, h) { h = objP->Index (); if (gameData.render.mine.bObjectRendered [h] != gameStates.render.nFrameFlipFlop) continue; pnl = lightManager.NearestSegLights () + objP->info.nSegment * MAX_NEAREST_LIGHTS; k = h * MAX_SHADOW_LIGHTS; for (i = n = 0; (n < m) && (*pnl >= 0); i++, pnl++) { prl = lightManager.RenderLights (*pnl); if (!prl->render.bState) continue; if (!CanSeePoint (objP, &objP->info.position.vPos, &prl->info.vPos, objP->info.nSegment)) continue; vLightDir = objP->info.position.vPos - prl->info.vPos; CFixVector::Normalize (vLightDir); if (n) { for (j = 0; j < n; j++) if (abs (CFixVector::Dot (vLightDir, gameData.render.shadows.vLightDir[j])) > I2X (2) / 3) // 60 deg break; if (j < n) continue; } gameData.render.shadows.vLightDir [n] = vLightDir; gameData.render.shadows.objLights [k + n++] = *pnl; prl->render.bShadow = 1; } gameData.render.shadows.objLights [k + n] = -1; }
/** * Checks if the mob can detect the target using it's detection (sight, sound, etc) * This is used to aggro and deaggro (Mobs start to deaggro after failing to detect target). **/ bool CMobController::CanDetectTarget(CBattleEntity* PTarget, bool forceSight) { if (PTarget->isDead() || PTarget->animation == ANIMATION_CHOCOBO) return false; float verticalDistance = abs(PMob->loc.p.y - PTarget->loc.p.y); if (verticalDistance > 8) { return false; } auto detects = PMob->m_Detects; auto currentDistance = distance(PTarget->loc.p, PMob->loc.p) + PTarget->getMod(MOD_STEALTH); bool detectSight = (detects & DETECT_SIGHT) || forceSight; bool hasInvisible = false; bool hasSneak = false; if (!PMob->m_TrueDetection) { hasInvisible = PTarget->StatusEffectContainer->HasStatusEffectByFlag(EFFECTFLAG_INVISIBLE); hasSneak = PTarget->StatusEffectContainer->HasStatusEffect(EFFECT_SNEAK); } if (detectSight && !hasInvisible && currentDistance < PMob->getMobMod(MOBMOD_SIGHT_RANGE) && isFaceing(PMob->loc.p, PTarget->loc.p, 40)) { return CanSeePoint(PTarget->loc.p); } if ((PMob->m_Behaviour & BEHAVIOUR_AGGRO_AMBUSH) && currentDistance < 3 && !hasSneak) { return true; } if ((detects & DETECT_HEARING) && currentDistance < PMob->getMobMod(MOBMOD_SOUND_RANGE) && !hasSneak) { return CanSeePoint(PTarget->loc.p); } // everything below require distance to be below 20 if (currentDistance > 20) { return false; } if ((detects & DETECT_LOWHP) && PTarget->GetHPP() < 75) { return CanSeePoint(PTarget->loc.p); } if ((detects & DETECT_MAGIC) && PTarget->PAI->IsCurrentState<CMagicState>() && static_cast<CMagicState*>(PTarget->PAI->GetCurrentState())->GetSpell()->hasMPCost()) { return CanSeePoint(PTarget->loc.p); } if ((detects & DETECT_WEAPONSKILL) && PTarget->PAI->IsCurrentState<CWeaponSkillState>()) { return CanSeePoint(PTarget->loc.p); } if ((detects & DETECT_JOBABILITY) && PTarget->PAI->IsCurrentState<CAbilityState>()) { return CanSeePoint(PTarget->loc.p); } return false; }
//------------------------------------------------------------------------------------------- //sequence this weapon object for this _frame_ (underscores added here to aid MK in his searching!) void DoWeaponSequence (CObject *objP) { CObject *gmObjP; fix xWeaponSpeed, xScaleFactor, xDistToTarget; Assert (objP->info.controlType == CT_WEAPON); // Ok, this is a big hack by MK. // If you want an CObject to last for exactly one frame, then give it a lifeleft of ONE_FRAME_TIME if (objP->info.xLifeLeft == ONE_FRAME_TIME) { if (IsMultiGame) objP->info.xLifeLeft = OMEGA_MULTI_LIFELEFT; else objP->info.xLifeLeft = 0; objP->info.renderType = RT_NONE; } if (objP->info.xLifeLeft < 0) { // We died of old age objP->Die (); if (WI_damage_radius (objP->info.nId)) objP->ExplodeBadassWeapon (objP->info.position.vPos); return; } //delete weapons that are not moving xWeaponSpeed = objP->mType.physInfo.velocity.Mag(); if (!((gameData.app.nFrameCount ^ objP->info.nSignature) & 3) && (objP->info.nType == OBJ_WEAPON) && (objP->info.nId != FLARE_ID) && (gameData.weapons.info [objP->info.nId].speed [gameStates.app.nDifficultyLevel] > 0) && (xWeaponSpeed < I2X (2))) { ReleaseObject (objP->Index ()); return; } if ((objP->info.nType == OBJ_WEAPON) && (objP->info.nId == FUSION_ID)) { //always set fusion weapon to max vel CFixVector::Normalize (objP->mType.physInfo.velocity); objP->mType.physInfo.velocity *= (WI_speed (objP->info.nId,gameStates.app.nDifficultyLevel)); } // For homing missiles, turn towards target. (unless it's the guided missile) if ((gameData.laser.xUpdateTime >= I2X (1) / 40) && (objP->info.nType == OBJ_WEAPON) && (gameStates.app.cheats.bHomingWeapons || WI_homingFlag (objP->info.nId)) && !(objP->info.nFlags & PF_HAS_BOUNCED) && !((objP->info.nId == GUIDEDMSL_ID) && (objP == (gmObjP = gameData.objs.guidedMissile [OBJECTS [objP->cType.laserInfo.parent.nObject].info.nId].objP)) && (objP->info.nSignature == gmObjP->info.nSignature))) { fix xFrameTime; for (xFrameTime = gameData.laser.xUpdateTime; xFrameTime >= I2X (1) / 40; xFrameTime -= I2X (1) / 40) { CFixVector vVecToObject, vNewVel; fix dot = I2X (1); fix speed, xMaxSpeed, xDist; int nObjId = objP->info.nId; // For first 1/2 second of life, missile flies straight. //if (objP->cType.laserInfo.xCreationTime + HomingMslStraightTime (nObjId) < gameData.time.xGame) { int nHomingTarget = objP->cType.laserInfo.nHomingTarget; // If it's time to do tracking, then it's time to grow up, stop bouncing and start exploding!. if ((nObjId == ROBOT_SMARTMINE_BLOB_ID) || (nObjId == ROBOT_SMARTMSL_BLOB_ID) || (nObjId == SMARTMINE_BLOB_ID) || (nObjId == SMARTMSL_BLOB_ID) || (nObjId == EARTHSHAKER_MEGA_ID)) objP->mType.physInfo.flags &= ~PF_BOUNCE; // Make sure the CObject we are tracking is still trackable. nHomingTarget = TrackHomingTarget (nHomingTarget, objP, &dot); if (nHomingTarget != -1) { if (nHomingTarget == LOCALPLAYER.nObject) { xDistToTarget = CFixVector::Dist (objP->info.position.vPos, OBJECTS [nHomingTarget].info.position.vPos); if ((xDistToTarget < LOCALPLAYER.homingObjectDist) || (LOCALPLAYER.homingObjectDist < 0)) LOCALPLAYER.homingObjectDist = xDistToTarget; } vVecToObject = OBJECTS [nHomingTarget].info.position.vPos - objP->info.position.vPos; xDist = CFixVector::Normalize (vVecToObject); vNewVel = objP->mType.physInfo.velocity; speed = CFixVector::Normalize (vNewVel); xMaxSpeed = WI_speed (objP->info.nId,gameStates.app.nDifficultyLevel); if (speed + I2X (1) < xMaxSpeed) { speed += FixMul (xMaxSpeed, I2X (1) / 80); if (speed > xMaxSpeed) speed = xMaxSpeed; } if (EGI_FLAG (bEnhancedShakers, 0, 0, 0) && (objP->info.nId == EARTHSHAKER_MEGA_ID)) { fix h = (objP->info.xLifeLeft + I2X (1) - 1) / I2X (1); if (h > 7) vVecToObject *= (I2X (1) / (h - 6)); } #if 0 vVecToObject *= HomingMslScale (); #endif vNewVel += vVecToObject; // The boss' smart children track better... if (gameData.weapons.info [objP->info.nId].renderType != WEAPON_RENDER_POLYMODEL) vNewVel += vVecToObject; CFixVector::Normalize (vNewVel); CFixVector vOldVel = objP->mType.physInfo.velocity; objP->mType.physInfo.velocity = vNewVel; objP->mType.physInfo.velocity *= speed; CFixVector vTest = objP->info.position.vPos + vNewVel * xDist; if (!CanSeePoint (NULL, &objP->info.position.vPos, &vTest, objP->info.nSegment, 3 * objP->info.xSize / 2)) objP->mType.physInfo.velocity = vOldVel; else { // Subtract off life proportional to amount turned. For hardest turn, it will lose 2 seconds per second. dot = abs (I2X (1) - dot); objP->info.xLifeLeft -= FixMul (dot * 32, I2X (1) / 40); } // Only polygon OBJECTS have visible orientation, so only they should turn. if (gameData.weapons.info [objP->info.nId].renderType == WEAPON_RENDER_POLYMODEL) HomingMissileTurnTowardsVelocity (objP, &vNewVel); // vNewVel is normalized velocity. } } } } // Make sure weapon is not moving faster than allowed speed. if ((objP->info.nType == OBJ_WEAPON) && (xWeaponSpeed > WI_speed (objP->info.nId, gameStates.app.nDifficultyLevel))) { // Only slow down if not allowed to move. Makes sense, huh? Allows proxbombs to get moved by physics force. --MK, 2/13/96 if (WI_speed (objP->info.nId, gameStates.app.nDifficultyLevel)) { xScaleFactor = FixDiv (WI_speed (objP->info.nId,gameStates.app.nDifficultyLevel), xWeaponSpeed); objP->mType.physInfo.velocity *= xScaleFactor; } } }