// See if two lines intersect by doing recursive subdivision. // Bails out if larger distance traveled is less than sum of radii + 1.0f. int collide_subdivide(vec3d *p0, vec3d *p1, float prad, vec3d *q0, vec3d *q1, float qrad) { float a_dist, b_dist, ab_dist; a_dist = vm_vec_dist(p0, p1); b_dist = vm_vec_dist(q0, q1); ab_dist = vm_vec_dist(p1, q1); // See if their spheres intersect if (ab_dist < a_dist + b_dist + prad + qrad) { if (ab_dist < prad + qrad) return 1; else if (vm_vec_dist(p0, q0) < prad + qrad) return 1; else if (MAX(a_dist, b_dist) < prad + qrad + 1.0f) return 0; else { int r1, r2 = 0; vec3d pa, qa; vm_vec_avg(&pa, p0, p1); vm_vec_avg(&qa, q0, q1); r1 = collide_subdivide(p0, &pa, prad, q0, &qa, qrad); if (!r1) r2 = collide_subdivide(&pa, p1, prad, &qa, q1, qrad); return r1 | r2; } } else return 0; }
// Checks weapon-weapon collisions. pair->a and pair->b are weapons. // Returns 1 if all future collisions between these can be ignored int collide_weapon_weapon( obj_pair * pair ) { float A_radius, B_radius; object *A = pair->a; object *B = pair->b; Assert( A->type == OBJ_WEAPON ); Assert( B->type == OBJ_WEAPON ); // Don't allow ship to shoot down its own missile. if (A->parent_sig == B->parent_sig) return 1; // Only shoot down teammate's missile if not traveling in nearly same direction. if (Weapons[A->instance].team == Weapons[B->instance].team) if (vm_vec_dot(&A->orient.vec.fvec, &B->orient.vec.fvec) > 0.7f) return 1; // Ignore collisions involving a bomb if the bomb is not yet armed. weapon *wpA, *wpB; weapon_info *wipA, *wipB; wpA = &Weapons[A->instance]; wpB = &Weapons[B->instance]; wipA = &Weapon_info[wpA->weapon_info_index]; wipB = &Weapon_info[wpB->weapon_info_index]; A_radius = A->radius; B_radius = B->radius; // UnknownPlayer : Should we even be bothering with collision detection is neither one of these is a bomb? if (wipA->wi_flags & WIF_BOMB) { A_radius *= 2; // Makes bombs easier to hit if (wipA->lifetime - wpA->lifeleft < BOMB_ARM_TIME) return 0; } if (wipB->wi_flags & WIF_BOMB) { B_radius *= 2; // Makes bombs easier to hit if (wipB->lifetime - wpB->lifeleft < BOMB_ARM_TIME) return 0; } // Rats, do collision detection. if (collide_subdivide(&A->last_pos, &A->pos, A_radius, &B->last_pos, &B->pos, B_radius)) { ship *sap, *sbp; sap = &Ships[Objects[A->parent].instance]; sbp = &Ships[Objects[B->parent].instance]; // MWA -- commented out next line because it was too long for output window on occation. // Yes -- I should fix the output window, but I don't have time to do it now. //nprintf(("AI", "[%s] %s's missile %i shot down by [%s] %s's laser %i\n", lTeamNames[sbp->team], sbp->ship_name, B->instance, lTeamNames[sap->team], sap->ship_name, A->instance)); if (wipA->wi_flags & WIF_BOMB) { if (wipB->wi_flags & WIF_BOMB) { // Two bombs collide, detonate both. Weapons[A->instance].lifeleft = 0.01f; Weapons[B->instance].lifeleft = 0.01f; Weapons[A->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; Weapons[B->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } else { A->hull_strength -= wipB->damage; if (A->hull_strength < 0.0f) { Weapons[A->instance].lifeleft = 0.01f; Weapons[A->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } } } else if (wipB->wi_flags & WIF_BOMB) { B->hull_strength -= wipA->damage; if (B->hull_strength < 0.0f) { Weapons[B->instance].lifeleft = 0.01f; Weapons[B->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } } float dist = 0.0f; if (Weapons[A->instance].lifeleft == 0.01f) { dist = vm_vec_dist_quick(&A->pos, &wpA->homing_pos); nprintf(("AI", "Frame %i: Weapon %s shot down. Dist: %.1f, inner: %.0f, outer: %.0f\n", Framecount, wipA->name, dist, wipA->inner_radius, wipA->outer_radius)); } if (Weapons[B->instance].lifeleft == 0.01f) { dist = vm_vec_dist_quick(&A->pos, &wpB->homing_pos); nprintf(("AI", "Frame %i: Weapon %s shot down. Dist: %.1f, inner: %.0f, outer: %.0f\n", Framecount, wipB->name, dist, wipB->inner_radius, wipB->outer_radius)); } return 1; } return 0; }
// Checks weapon-weapon collisions. pair->a and pair->b are weapons. // Returns 1 if all future collisions between these can be ignored int collide_weapon_weapon(obj_pair* pair) { float A_radius, B_radius; object* A = pair->a; object* B = pair->b; Assert(A->type == OBJ_WEAPON); Assert(B->type == OBJ_WEAPON); // Don't allow ship to shoot down its own missile. if (A->parent_sig == B->parent_sig) return 1; // Only shoot down teammate's missile if not traveling in nearly same direction. if (Weapons[A->instance].team == Weapons[B->instance].team) if (vm_vec_dot(&A->orient.vec.fvec, &B->orient.vec.fvec) > 0.7f) return 1; // Ignore collisions involving a bomb if the bomb is not yet armed. weapon* wpA, *wpB; weapon_info* wipA, *wipB; wpA = &Weapons[A->instance]; wpB = &Weapons[B->instance]; wipA = &Weapon_info[wpA->weapon_info_index]; wipB = &Weapon_info[wpB->weapon_info_index]; A_radius = A->radius; B_radius = B->radius; // UnknownPlayer : Should we even be bothering with collision detection is neither one of these is a bomb? //WMC - Here's a reason why...scripting now! if (wipA->weapon_hitpoints > 0) { if (!(wipA->wi_flags2 & WIF2_HARD_TARGET_BOMB)) { A_radius *= 2; // Makes bombs easier to hit } if ((wipA->lifetime - wpA->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level]) return 0; } if (wipB->weapon_hitpoints > 0) { if (!(wipB->wi_flags2 & WIF2_HARD_TARGET_BOMB)) { B_radius *= 2; // Makes bombs easier to hit } if ((wipB->lifetime - wpB->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level]) return 0; } // Rats, do collision detection. if (collide_subdivide(&A->last_pos, &A->pos, A_radius, &B->last_pos, &B->pos, B_radius)) { Script_system.SetHookObjects(4, "Weapon", A, "WeaponB", B, "Self", A, "Object", B); bool a_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, A); //Should be reversed Script_system.SetHookObjects(4, "Weapon", B, "WeaponB", A, "Self", B, "Object", A); bool b_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, B); if (!a_override && !b_override) { //Do the normal stuff if no override -C ship* sap, *sbp; sap = &Ships[Objects[A->parent].instance]; sbp = &Ships[Objects[B->parent].instance]; // MWA -- commented out next line because it was too long for output window on occation. // Yes -- I should fix the output window, but I don't have time to do it now. //nprintf(("AI", "[%s] %s's missile %i shot down by [%s] %s's laser %i\n", Iff_info[sbp->team].iff_name, sbp->ship_name, B->instance, Iff_info[sap->team].iff_name, sap->ship_name, A->instance)); if (wipA->weapon_hitpoints > 0) { if (wipB->weapon_hitpoints > 0) { // Two bombs collide, detonate both. Weapons[A->instance].lifeleft = 0.01f; Weapons[B->instance].lifeleft = 0.01f; Weapons[A->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; Weapons[B->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } else { A->hull_strength -= wipB->damage; if (A->hull_strength < 0.0f) { Weapons[A->instance].lifeleft = 0.01f; Weapons[A->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } } } else if (wipB->weapon_hitpoints > 0) { B->hull_strength -= wipA->damage; if (B->hull_strength < 0.0f) { Weapons[B->instance].lifeleft = 0.01f; Weapons[B->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } } #ifndef NDEBUG float dist = 0.0f; if (Weapons[A->instance].lifeleft == 0.01f) { dist = vm_vec_dist_quick(&A->pos, &wpA->homing_pos); //nprintf(("AI", "Frame %i: Weapon %s shot down. Dist: %.1f, inner: %.0f, outer: %.0f\n", Framecount, wipA->name, dist, wipA->inner_radius, wipA->outer_radius)); } if (Weapons[B->instance].lifeleft == 0.01f) { dist = vm_vec_dist_quick(&A->pos, &wpB->homing_pos); //nprintf(("AI", "Frame %i: Weapon %s shot down. Dist: %.1f, inner: %.0f, outer: %.0f\n", Framecount, wipB->name, dist, wipB->inner_radius, wipB->outer_radius)); } #endif } if (!(b_override && !a_override)) { Script_system.SetHookObjects(4, "Weapon", A, "WeaponB", B, "Self", A, "Object", B); Script_system.RunCondition(CHA_COLLIDEWEAPON, '\0', NULL, A); } if ((b_override && !a_override) || (!b_override && !a_override)) { //SHould be reversed Script_system.SetHookObjects(4, "Weapon", B, "WeaponB", A, "Self", B, "Object", A); Script_system.RunCondition(CHA_COLLIDEWEAPON, '\0', NULL, B); } Script_system.RemHookVars(4, "Weapon", "WeaponB", "Self", "ObjectB"); return 1; } return 0; }
/** * Checks weapon-weapon collisions. * @param pair obj_pair pointer to the two objects. pair->a and pair->b are weapons. * @return 1 if all future collisions between these can be ignored */ int collide_weapon_weapon( obj_pair * pair ) { float A_radius, B_radius; object *A = pair->a; object *B = pair->b; Assert( A->type == OBJ_WEAPON ); Assert( B->type == OBJ_WEAPON ); // Don't allow ship to shoot down its own missile. if (A->parent_sig == B->parent_sig) return 1; // Only shoot down teammate's missile if not traveling in nearly same direction. if (Weapons[A->instance].team == Weapons[B->instance].team) if (vm_vec_dot(&A->orient.vec.fvec, &B->orient.vec.fvec) > 0.7f) return 1; // Ignore collisions involving a bomb if the bomb is not yet armed. weapon *wpA, *wpB; weapon_info *wipA, *wipB; wpA = &Weapons[A->instance]; wpB = &Weapons[B->instance]; wipA = &Weapon_info[wpA->weapon_info_index]; wipB = &Weapon_info[wpB->weapon_info_index]; A_radius = A->radius; B_radius = B->radius; if (wipA->weapon_hitpoints > 0) { if (!(wipA->wi_flags2 & WIF2_HARD_TARGET_BOMB)) { A_radius *= 2; // Makes bombs easier to hit } if (wipA->wi_flags & WIF_LOCKED_HOMING) { if ( (wipA->max_lifetime - wpA->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] ) return 0; } else if ( (wipA->lifetime - wpA->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] ) return 0; } if (wipB->weapon_hitpoints > 0) { if (!(wipB->wi_flags2 & WIF2_HARD_TARGET_BOMB)) { B_radius *= 2; // Makes bombs easier to hit } if (wipB->wi_flags & WIF_LOCKED_HOMING) { if ( (wipB->max_lifetime - wpB->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] ) return 0; } else if ( (wipB->lifetime - wpB->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] ) return 0; } // Rats, do collision detection. if (collide_subdivide(&A->last_pos, &A->pos, A_radius, &B->last_pos, &B->pos, B_radius)) { Script_system.SetHookObjects(4, "Weapon", A, "WeaponB", B, "Self",A, "Object", B); bool a_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, A); //Should be reversed Script_system.SetHookObjects(4, "Weapon", B, "WeaponB", A, "Self",B, "Object", A); bool b_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, B); if(!a_override && !b_override) { float aDamage = wipA->damage; if (wipB->armor_type_idx >= 0) aDamage = Armor_types[wipB->armor_type_idx].GetDamage(aDamage, wipA->damage_type_idx, 1.0f); float bDamage = wipB->damage; if (wipA->armor_type_idx >= 0) bDamage = Armor_types[wipA->armor_type_idx].GetDamage(bDamage, wipB->damage_type_idx, 1.0f); if (wipA->weapon_hitpoints > 0) { if (wipB->weapon_hitpoints > 0) { // Two bombs collide, detonate both. if ((wipA->wi_flags & WIF_BOMB) && (wipB->wi_flags & WIF_BOMB)) { Weapons[A->instance].lifeleft = 0.01f; Weapons[B->instance].lifeleft = 0.01f; Weapons[A->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; Weapons[B->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } else { A->hull_strength -= bDamage; B->hull_strength -= aDamage; // safety to make sure either of the weapons die - allow 'bulkier' to keep going if ((A->hull_strength > 0.0f) && (B->hull_strength > 0.0f)) { if (wipA->weapon_hitpoints > wipB->weapon_hitpoints) { B->hull_strength = -1.0f; } else { A->hull_strength = -1.0f; } } if (A->hull_strength < 0.0f) { Weapons[A->instance].lifeleft = 0.01f; Weapons[A->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } if (B->hull_strength < 0.0f) { Weapons[B->instance].lifeleft = 0.01f; Weapons[B->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } } } else { A->hull_strength -= bDamage; Weapons[B->instance].lifeleft = 0.01f; Weapons[B->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; if (A->hull_strength < 0.0f) { Weapons[A->instance].lifeleft = 0.01f; Weapons[A->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } } } else if (wipB->weapon_hitpoints > 0) { B->hull_strength -= aDamage; Weapons[A->instance].lifeleft = 0.01f; Weapons[A->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; if (B->hull_strength < 0.0f) { Weapons[B->instance].lifeleft = 0.01f; Weapons[B->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } } // single player and multiplayer masters evaluate the scoring and kill stuff if (!MULTIPLAYER_CLIENT) { //Save damage for bomb so we can do scoring once it's destroyed. -Halleck if (wipA->wi_flags & WIF_BOMB) { scoring_add_damage_to_weapon(A, B, wipB->damage); //Update stats. -Halleck scoring_eval_hit(A, B, 0); } if (wipB->wi_flags & WIF_BOMB) { scoring_add_damage_to_weapon(B, A, wipA->damage); //Update stats. -Halleck scoring_eval_hit(B, A, 0); } } #ifndef NDEBUG float dist = 0.0f; if (Weapons[A->instance].lifeleft == 0.01f) { dist = vm_vec_dist_quick(&A->pos, &wpA->homing_pos); } if (Weapons[B->instance].lifeleft == 0.01f) { dist = vm_vec_dist_quick(&B->pos, &wpB->homing_pos); } #endif } if(!(b_override && !a_override)) { Script_system.SetHookObjects(4, "Weapon", A, "WeaponB", B, "Self",A, "Object", B); Script_system.RunCondition(CHA_COLLIDEWEAPON, '\0', NULL, A, wpA->weapon_info_index); } if((b_override && !a_override) || (!b_override && !a_override)) { //Should be reversed Script_system.SetHookObjects(4, "Weapon", B, "WeaponB", A, "Self",B, "Object", A); Script_system.RunCondition(CHA_COLLIDEWEAPON, '\0', NULL, B, wpB->weapon_info_index); } Script_system.RemHookVars(4, "Weapon", "WeaponB", "Self","ObjectB"); return 1; } return 0; }
/** * Checks weapon-weapon collisions. * @param pair obj_pair pointer to the two objects. pair->a and pair->b are weapons. * @return 1 if all future collisions between these can be ignored */ int collide_weapon_weapon( obj_pair * pair ) { float A_radius, B_radius; object *A = pair->a; object *B = pair->b; Assert( A->type == OBJ_WEAPON ); Assert( B->type == OBJ_WEAPON ); // Don't allow ship to shoot down its own missile. if (A->parent_sig == B->parent_sig) return 1; // Only shoot down teammate's missile if not traveling in nearly same direction. if (Weapons[A->instance].team == Weapons[B->instance].team) if (vm_vec_dot(&A->orient.vec.fvec, &B->orient.vec.fvec) > 0.7f) return 1; // Ignore collisions involving a bomb if the bomb is not yet armed. weapon *wpA, *wpB; weapon_info *wipA, *wipB; wpA = &Weapons[A->instance]; wpB = &Weapons[B->instance]; wipA = &Weapon_info[wpA->weapon_info_index]; wipB = &Weapon_info[wpB->weapon_info_index]; A_radius = A->radius; B_radius = B->radius; if (wipA->weapon_hitpoints > 0) { if (!(wipA->wi_flags[Weapon::Info_Flags::Hard_target_bomb])) { A_radius *= 2; // Makes bombs easier to hit } if ((The_mission.ai_profile->flags[AI::Profile_Flags::Aspect_invulnerability_fix]) && (wipA->is_locked_homing()) && (wpA->homing_object != &obj_used_list)) { if ( (wipA->max_lifetime - wpA->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] ) return 0; } else if ( (wipA->lifetime - wpA->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] ) return 0; } if (wipB->weapon_hitpoints > 0) { if (!(wipB->wi_flags[Weapon::Info_Flags::Hard_target_bomb])) { B_radius *= 2; // Makes bombs easier to hit } if ((The_mission.ai_profile->flags[AI::Profile_Flags::Aspect_invulnerability_fix]) && (wipB->is_locked_homing()) && (wpB->homing_object != &obj_used_list)) { if ( (wipB->max_lifetime - wpB->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] ) return 0; } else if ( (wipB->lifetime - wpB->lifeleft) < The_mission.ai_profile->delay_bomb_arm_timer[Game_skill_level] ) return 0; } // Rats, do collision detection. if (collide_subdivide(&A->last_pos, &A->pos, A_radius, &B->last_pos, &B->pos, B_radius)) { Script_system.SetHookObjects(4, "Weapon", A, "WeaponB", B, "Self",A, "Object", B); bool a_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, A); //Should be reversed Script_system.SetHookObjects(4, "Weapon", B, "WeaponB", A, "Self",B, "Object", A); bool b_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, B); if(!a_override && !b_override) { float aDamage = wipA->damage; if (wipB->armor_type_idx >= 0) aDamage = Armor_types[wipB->armor_type_idx].GetDamage(aDamage, wipA->damage_type_idx, 1.0f); float bDamage = wipB->damage; if (wipA->armor_type_idx >= 0) bDamage = Armor_types[wipA->armor_type_idx].GetDamage(bDamage, wipB->damage_type_idx, 1.0f); if (wipA->weapon_hitpoints > 0) { if (wipB->weapon_hitpoints > 0) { // Two bombs collide, detonate both. if ((wipA->wi_flags[Weapon::Info_Flags::Bomb]) && (wipB->wi_flags[Weapon::Info_Flags::Bomb])) { wpA->lifeleft = 0.01f; wpB->lifeleft = 0.01f; wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon); wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon); } else { A->hull_strength -= bDamage; B->hull_strength -= aDamage; // safety to make sure either of the weapons die - allow 'bulkier' to keep going if ((A->hull_strength > 0.0f) && (B->hull_strength > 0.0f)) { if (wipA->weapon_hitpoints > wipB->weapon_hitpoints) { B->hull_strength = -1.0f; } else { A->hull_strength = -1.0f; } } if (A->hull_strength < 0.0f) { wpA->lifeleft = 0.01f; wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon); } if (B->hull_strength < 0.0f) { wpB->lifeleft = 0.01f; wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon); } } } else { A->hull_strength -= bDamage; wpB->lifeleft = 0.01f; wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon); if (A->hull_strength < 0.0f) { wpA->lifeleft = 0.01f; wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon); } } } else if (wipB->weapon_hitpoints > 0) { B->hull_strength -= aDamage; wpA->lifeleft = 0.01f; wpA->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon); if (B->hull_strength < 0.0f) { wpB->lifeleft = 0.01f; wpB->weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon); } } // single player and multiplayer masters evaluate the scoring and kill stuff if (!MULTIPLAYER_CLIENT) { // If bomb was destroyed, do scoring if (wipA->wi_flags[Weapon::Info_Flags::Bomb]) { //Update stats. -Halleck scoring_eval_hit(A, B, 0); if (wpA->weapon_flags[Weapon::Weapon_Flags::Destroyed_by_weapon]) { scoring_eval_kill_on_weapon(A, B); } } if (wipB->wi_flags[Weapon::Info_Flags::Bomb]) { //Update stats. -Halleck scoring_eval_hit(B, A, 0); if (wpB->weapon_flags[Weapon::Weapon_Flags::Destroyed_by_weapon]) { scoring_eval_kill_on_weapon(B, A); } } } } if(!(b_override && !a_override)) { Script_system.SetHookObjects(4, "Weapon", A, "WeaponB", B, "Self",A, "Object", B); Script_system.RunCondition(CHA_COLLIDEWEAPON, A, wpA->weapon_info_index); } else { //Should be reversed Script_system.SetHookObjects(4, "Weapon", B, "WeaponB", A, "Self",B, "Object", A); Script_system.RunCondition(CHA_COLLIDEWEAPON, B, wpB->weapon_info_index); } Script_system.RemHookVars(4, "Weapon", "WeaponB", "Self", "Object"); return 1; } return 0; }