1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include "Projectile.h"
4 #include "Map/MapInfo.h"
5 #include "Rendering/Colors.h"
6 #include "Rendering/GL/VertexArray.h"
7 #include "Sim/Projectiles/ProjectileHandler.h"
8 #include "Sim/Misc/QuadField.h"
9 #include "Sim/Units/Unit.h"
10 #include "Sim/Units/UnitHandler.h"
11 #include "System/Matrix44f.h"
12 
13 CR_BIND_DERIVED(CProjectile, CExpGenSpawnable, )
14 
15 CR_REG_METADATA(CProjectile,
16 (
17 	CR_MEMBER(synced),
18 	CR_MEMBER(weapon),
19 	CR_MEMBER(piece),
20 	CR_MEMBER(hitscan),
21 
22 	CR_MEMBER(luaMoveCtrl),
23 	CR_MEMBER(checkCol),
24 	CR_MEMBER(ignoreWater),
25 	CR_MEMBER(deleteMe),
26 
27 	CR_MEMBER(castShadow),
28 	CR_MEMBER(drawSorted),
29 
30 	CR_MEMBER_BEGINFLAG(CM_Config),
31 		CR_MEMBER(dir),
32 	CR_MEMBER_ENDFLAG(CM_Config),
33 	CR_MEMBER(drawPos),
34 
35 	CR_MEMBER(mygravity),
36 	CR_IGNORED(sortDist),
37 
38 	CR_MEMBER(ownerID),
39 	CR_MEMBER(teamID),
40 	CR_MEMBER(cegID),
41 
42 	CR_MEMBER(projectileType),
43 	CR_MEMBER(collisionFlags),
44 
45 	CR_MEMBER(qfCellData)
46 ))
47 
48 CR_BIND(CProjectile::QuadFieldCellData, )
49 
50 
51 
52 //////////////////////////////////////////////////////////////////////
53 // Construction/Destruction
54 //////////////////////////////////////////////////////////////////////
55 bool CProjectile::inArray = false;
56 CVertexArray* CProjectile::va = NULL;
57 
58 
CProjectile()59 CProjectile::CProjectile()
60 	: synced(false)
61 	, weapon(false)
62 	, piece(false)
63 	, hitscan(false)
64 
65 	, luaMoveCtrl(false)
66 	, checkCol(true)
67 	, ignoreWater(false)
68 	, deleteMe(false)
69 
70 	, castShadow(false)
71 	, drawSorted(true)
72 
73 	, mygravity(mapInfo? mapInfo->map.gravity: 0.0f)
74 
75 	, ownerID(-1u)
76 	, teamID(-1u)
77 	, cegID(-1u)
78 
79 	, projectileType(-1u)
80 	, collisionFlags(0)
81 {
82 }
83 
CProjectile(const float3 & pos,const float3 & spd,const CUnit * owner,bool isSynced,bool isWeapon,bool isPiece,bool isHitScan)84 CProjectile::CProjectile(
85 	const float3& pos,
86 	const float3& spd,
87 	const CUnit* owner,
88 	bool isSynced,
89 	bool isWeapon,
90 	bool isPiece,
91 	bool isHitScan
92 ): CExpGenSpawnable(pos, spd)
93 
94 	, synced(isSynced)
95 	, weapon(isWeapon)
96 	, piece(isPiece)
97 	, hitscan(isHitScan)
98 
99 	, luaMoveCtrl(false)
100 	, checkCol(true)
101 	, ignoreWater(false)
102 	, deleteMe(false)
103 
104 	, castShadow(false)
105 	, drawSorted(true)
106 
107 	, dir(ZeroVector) // set via Init()
108 	, mygravity(mapInfo? mapInfo->map.gravity: 0.0f)
109 
110 	, ownerID(-1u)
111 	, teamID(-1u)
112 	, cegID(-1u)
113 
114 	, projectileType(-1u)
115 	, collisionFlags(0)
116 {
117 	SetRadiusAndHeight(1.7f, 0.0f);
118 	Init(owner, ZeroVector);
119 }
120 
Detach()121 void CProjectile::Detach() {
122 	// SYNCED
123 	if (synced) {
124 		quadField->RemoveProjectile(this);
125 	}
126 	CExpGenSpawnable::Detach();
127 }
128 
~CProjectile()129 CProjectile::~CProjectile() {
130 	// UNSYNCED
131 	assert(!synced || detached);
132 }
133 
Init(const CUnit * owner,const float3 & offset)134 void CProjectile::Init(const CUnit* owner, const float3& offset)
135 {
136 	if (owner != NULL) {
137 		// must be set before the AddProjectile call
138 		ownerID = owner->id;
139 		teamID = owner->team;
140 	}
141 	if (!hitscan) {
142 		SetPosition(pos + offset);
143 		SetVelocityAndSpeed(speed);
144 	}
145 	if (!weapon && !piece) {
146 		// NOTE:
147 		//   new CWeapon- and CPieceProjectile*'s add themselves
148 		//   to CProjectileHandler (other code needs to be able
149 		//   to dyna-cast CProjectile*'s to those derived types,
150 		//   and adding them here would throw away too much RTTI)
151 		projectileHandler->AddProjectile(this);
152 	}
153 	if (synced && !weapon) {
154 		quadField->AddProjectile(this);
155 	}
156 }
157 
158 
Update()159 void CProjectile::Update()
160 {
161 	if (luaMoveCtrl)
162 		return;
163 
164 	SetPosition(pos + speed);
165 	SetVelocityAndSpeed(speed + (UpVector * mygravity));
166 }
167 
168 
Collision()169 void CProjectile::Collision()
170 {
171 	deleteMe = true;
172 	checkCol = false;
173 }
174 
Collision(CUnit * unit)175 void CProjectile::Collision(CUnit* unit)
176 {
177 	Collision();
178 }
179 
Collision(CFeature * feature)180 void CProjectile::Collision(CFeature* feature)
181 {
182 	Collision();
183 }
184 
185 
DrawOnMinimap(CVertexArray & lines,CVertexArray & points)186 void CProjectile::DrawOnMinimap(CVertexArray& lines, CVertexArray& points)
187 {
188 	points.AddVertexQC(pos, color4::whiteA);
189 }
190 
DrawArray()191 int CProjectile::DrawArray()
192 {
193 	va->DrawArrayTC(GL_QUADS);
194 
195 	// draw-index gets divided by 24 because each element is
196 	// 12 + 4 + 4 + 4 = 24 bytes in size (pos + u + v + color)
197 	// for each type of "projectile"
198 	const int idx = (va->drawIndex() / 24);
199 
200 	va = GetVertexArray();
201 	va->Initialize();
202 	inArray = false;
203 
204 	return idx;
205 }
206 
owner() const207 CUnit* CProjectile::owner() const {
208 	// NOTE:
209 	//   this death dependency optimization using "ownerID" is logically flawed:
210 	//   because ID's are reused it could return a unit that is not the original
211 	//   owner (unlikely however unless ID's get recycled very rapidly)
212 	CUnit* unit = unitHandler->GetUnit(ownerID);
213 
214 	return unit;
215 }
216 
GetTransformMatrix(bool offsetPos) const217 CMatrix44f CProjectile::GetTransformMatrix(bool offsetPos) const {
218 	float3 xdir;
219 	float3 ydir;
220 
221 	if (math::fabs(dir.y) < 0.95f) {
222 		xdir = dir.cross(UpVector);
223 		xdir.SafeANormalize();
224 	} else {
225 		xdir.x = 1.0f;
226 	}
227 
228 	ydir = xdir.cross(dir);
229 
230 	return (CMatrix44f(drawPos + (dir * radius * 0.9f * offsetPos), -xdir, ydir, dir));
231 }
232 
233