File: InterceptHandler.cpp

package info (click to toggle)
spring 106.0%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 55,316 kB
  • sloc: cpp: 543,954; ansic: 44,800; python: 12,575; java: 12,201; awk: 5,889; sh: 1,796; asm: 1,546; xml: 655; perl: 405; php: 211; objc: 194; makefile: 76; sed: 2
file content (155 lines) | stat: -rw-r--r-- 4,861 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */

#include <limits>
#include <algorithm>

#include "InterceptHandler.h"

#include "Map/Ground.h"
#include "Sim/Misc/GlobalSynced.h"
#include "Sim/Misc/TeamHandler.h"
#include "Sim/Weapons/Weapon.h"
#include "Sim/Projectiles/WeaponProjectiles/WeaponProjectile.h"
#include "Sim/Units/Unit.h"
#include "Sim/Weapons/WeaponDef.h"
#include "System/EventHandler.h"
#include "System/float3.h"
#include "System/SpringMath.h"
#include "System/creg/STL_Deque.h"


CR_BIND_DERIVED(CInterceptHandler, CObject, )
CR_REG_METADATA(CInterceptHandler, (
	CR_MEMBER(interceptors),
	CR_MEMBER(interceptables)
))

CInterceptHandler interceptHandler;



void CInterceptHandler::Update(bool forced) {
	if (((gs->frameNum % UNIT_SLOWUPDATE_RATE) != 0) && !forced)
		return;

	for (CWeapon* w: interceptors) {
		const WeaponDef* wDef = w->weaponDef;
		const CUnit* wOwner = w->owner;

		assert(wDef->interceptor || wDef->isShield);

		for (CWeaponProjectile* p: interceptables) {
			if (!p->CanBeInterceptedBy(wDef))
				continue;
			if (w->HasIncomingProjectile(p->id))
				continue;

			const int pAllyTeam = p->GetAllyteamID();

			if (teamHandler.IsValidAllyTeam(pAllyTeam) && teamHandler.Ally(wOwner->allyteam, pAllyTeam))
				continue;

			// note: will be called every Update so long as gadget does not return true
			if (!eventHandler.AllowWeaponInterceptTarget(wOwner, w, p))
				continue;

			// there are four cases when an interceptor <w> should fire at a projectile <p>:
			//     1. p's target position inside w's interception circle (w's owner can move!)
			//     2. p's current position inside w's interception circle
			//     3. p's projected impact position inside w's interception circle
			//     4. p's trajectory intersects w's interception circle
			//
			// these checks all need to be evaluated periodically, not just
			// when a projectile is created and handed to AddInterceptTarget
			const float weaponDist = w->aimFromPos.distance(p->pos);
			const float impactDist = CGround::LineGroundCol(p->pos, p->pos + p->dir * weaponDist);

			const float3& pImpactPos = p->pos + p->dir * impactDist;
			const float3& pTargetPos = p->GetTargetPos();
			const float3  pWeaponVec = p->pos - w->aimFromPos;

			if (w->aimFromPos.SqDistance2D(pTargetPos) < Square(wDef->coverageRange)) {
				w->AddDeathDependence(p, DEPENDENCE_INTERCEPT);
				w->AddIncomingProjectile(p->id);
				continue; // 1
			}

			if (false /*wDef->noFlyThroughIntercept*/) {
				// <w> is just a static interceptor and fires only at projectiles
				// TARGETED within its current interception area; any projectiles
				// CROSSING its interception area aren't targeted
				//XXX implement in lua?
				continue;
			}

			if (pWeaponVec.SqLength2D() < Square(wDef->coverageRange)) {
				w->AddDeathDependence(p, DEPENDENCE_INTERCEPT);
				w->AddIncomingProjectile(p->id);
				continue; // 2
			}

			if (w->aimFromPos.SqDistance2D(pImpactPos) < Square(wDef->coverageRange)) {
				const float3 pTargetDir = (pTargetPos - p->pos).SafeNormalize();
				const float3 pImpactDir = (pImpactPos - p->pos).SafeNormalize();

				// the projected impact position can briefly shift into the covered
				// area during transition from vertical to horizontal flight, so we
				// perform an extra test (NOTE: assumes non-parabolic trajectory)
				if (pTargetDir.dot(pImpactDir) >= 0.999f) {
					w->AddDeathDependence(p, DEPENDENCE_INTERCEPT);
					w->AddIncomingProjectile(p->id);
					continue; // 3
				}
			}

			const float3 pMinSepPos = p->pos + p->dir * Clamp(-(pWeaponVec.dot(p->dir)), 0.0f, impactDist);
			const float3 pMinSepVec = w->aimFromPos - pMinSepPos;

			if (pMinSepVec.SqLength() < Square(wDef->coverageRange)) {
				w->AddDeathDependence(p, DEPENDENCE_INTERCEPT);
				w->AddIncomingProjectile(p->id);
				continue; // 4
			}
		}
	}
}



void CInterceptHandler::AddInterceptorWeapon(CWeapon* weapon)
{
	interceptors.push_back(weapon);
}


void CInterceptHandler::RemoveInterceptorWeapon(CWeapon* weapon)
{
	auto it = std::find(interceptors.begin(), interceptors.end(), weapon);
	if (it != interceptors.end()) {
		interceptors.erase(it);
	}
}


void CInterceptHandler::AddInterceptTarget(CWeaponProjectile* target, const float3& destination)
{
	// keep track of all interceptable projectiles
	interceptables.push_back(target);

	// if the target projectile dies in any way, we need to remove it
	// (we cannot rely on any interceptor telling us, because they may
	// die before the interceptable itself does)
	AddDeathDependence(target, DEPENDENCE_INTERCEPTABLE);

	Update(true);
}


void CInterceptHandler::DependentDied(CObject* o)
{
	auto it = std::find(interceptables.begin(), interceptables.end(), static_cast<CWeaponProjectile*>(o));
	if (it != interceptables.end()) {
		interceptables.erase(it);
	}
}