1 /*
2  * Copyright 2011-2012 Arx Libertatis Team (see the AUTHORS file)
3  *
4  * This file is part of Arx Libertatis.
5  *
6  * Arx Libertatis is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Arx Libertatis is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Arx Libertatis.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23 
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25 
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28 
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31 
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code.  If not, see
33 <http://www.gnu.org/licenses/>.
34 
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38 
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 // Copyright (c) 1999-2001 ARKANE Studios SA. All rights reserved
44 
45 #include "game/Missile.h"
46 
47 #include <stddef.h>
48 
49 #include "core/GameTime.h"
50 #include "core/Core.h"
51 
52 #include "game/Damage.h"
53 #include "game/Player.h"
54 
55 #include "graphics/Color.h"
56 #include "graphics/GraphicsTypes.h"
57 #include "graphics/Math.h"
58 #include "graphics/data/Mesh.h"
59 #include "graphics/particle/ParticleEffects.h"
60 
61 #include "math/Random.h"
62 #include "math/Vector3.h"
63 
64 #include "platform/Flags.h"
65 
66 #include "scene/Light.h"
67 #include "scene/Interactive.h"
68 #include "scene/GameSound.h"
69 
70 class TextureContainer;
71 
72 struct ARX_MISSILE
73 {
74 	ARX_SPELLS_MISSILE_TYPE type;
75 	Vec3f startpos;
76 	Vec3f velocity;
77 	Vec3f lastpos;
78 	unsigned long timecreation;
79 	unsigned long lastupdate;
80 	unsigned long tolive;
81 	long		longinfo;
82 	long		owner;
83 };
84 
85 static const size_t MAX_MISSILES = 100;
86 static ARX_MISSILE missiles[MAX_MISSILES];
87 
88 // Gets a Free Projectile Slot
ARX_MISSILES_GetFree()89 long ARX_MISSILES_GetFree() {
90 
91 	for(size_t i = 0; i < MAX_MISSILES; i++) {
92 		if(missiles[i].type == MISSILE_NONE) {
93 			return i;
94 		}
95 	}
96 
97 	return -1;
98 }
99 
100 // Kills a missile
ARX_MISSILES_Kill(long i)101 void ARX_MISSILES_Kill(long i)
102 {
103 	switch (missiles[i].type)
104 	{
105 		case MISSILE_FIREBALL :
106 
107 			if (missiles[i].longinfo != -1)
108 			{
109 				DynLight[missiles[i].longinfo].duration = 150;
110 			}
111 
112 			break;
113 		case MISSILE_NONE: break;
114 	}
115 
116 	missiles[i].type = MISSILE_NONE;
117 }
118 
119 //-----------------------------------------------------------------------------
120 // Clear all missiles
ARX_MISSILES_ClearAll()121 void ARX_MISSILES_ClearAll() {
122 	for(size_t i = 0; i < MAX_MISSILES; i++) {
123 		ARX_MISSILES_Kill(i);
124 	}
125 }
126 
127 //-----------------------------------------------------------------------------
128 // Spawns a Projectile using type, starting position/TargetPosition
ARX_MISSILES_Spawn(Entity * io,ARX_SPELLS_MISSILE_TYPE type,const Vec3f * startpos,const Vec3f * targetpos)129 void ARX_MISSILES_Spawn(Entity * io, ARX_SPELLS_MISSILE_TYPE type, const Vec3f * startpos, const Vec3f * targetpos) {
130 
131 	long i(ARX_MISSILES_GetFree());
132 
133 	if (i == -1) return;
134 
135 	missiles[i].owner = (io == NULL) ? -1 : io->index();
136 	missiles[i].type = type;
137 	missiles[i].lastpos = missiles[i].startpos = *startpos;
138 
139 	float dist;
140 
141 	dist = 1.0F / fdist(*startpos, *targetpos);
142 	missiles[i].velocity = (*targetpos - *startpos) * dist;
143 	missiles[i].lastupdate = missiles[i].timecreation = (unsigned long)(arxtime);
144 
145 	switch (type)
146 	{
147 		case MISSILE_NONE: break;
148 		case MISSILE_FIREBALL:
149 		{
150 			missiles[i].tolive = 6000;
151 			missiles[i].velocity *= 0.8f;
152 			missiles[i].longinfo = GetFreeDynLight();
153 
154 			if (missiles[i].longinfo != -1)
155 			{
156 				DynLight[missiles[i].longinfo].intensity = 1.3f;
157 				DynLight[missiles[i].longinfo].exist = 1;
158 				DynLight[missiles[i].longinfo].fallend = 420.f;
159 				DynLight[missiles[i].longinfo].fallstart = 250.f;
160 				DynLight[missiles[i].longinfo].rgb = Color3f(1.f, .8f, .6f);
161 				DynLight[missiles[i].longinfo].pos = *startpos;
162 			}
163 
164 			ARX_SOUND_PlaySFX(SND_SPELL_FIRE_WIND, &missiles[i].startpos, 2.0F);
165 			ARX_SOUND_PlaySFX(SND_SPELL_FIRE_LAUNCH, &missiles[i].startpos, 2.0F);
166 		}
167 	}
168 }
169 
170 //-----------------------------------------------------------------------------
171 // Updates all currently launched projectiles
ARX_MISSILES_Update()172 void ARX_MISSILES_Update()
173 {
174 	long framediff, framediff3;
175 	Vec3f orgn, dest, hit;
176 	TextureContainer * tc = TC_fire;
177 	EERIEPOLY *tp = NULL;
178 	unsigned long tim = (unsigned long)(arxtime);
179 
180 	for (unsigned long i(0); i < MAX_MISSILES; i++)
181 	{
182 		if (missiles[i].type == MISSILE_NONE) continue;
183 
184 		framediff = missiles[i].timecreation + missiles[i].tolive - tim;
185 
186 		if (framediff < 0)
187 		{
188 			ARX_MISSILES_Kill(i);
189 			continue;
190 		}
191 
192 		framediff3 = tim - missiles[i].timecreation;
193 
194 		switch (missiles[i].type)
195 		{
196 			case MISSILE_NONE: break;
197 			case MISSILE_FIREBALL: {
198 				Vec3f pos;
199 
200 				pos = missiles[i].startpos + missiles[i].velocity * framediff3;
201 
202 				if (missiles[i].longinfo != -1)
203 				{
204 					DynLight[missiles[i].longinfo].pos = pos;
205 				}
206 
207 				orgn = missiles[i].lastpos;
208 				dest = pos;
209 
210 				EERIEPOLY *ep;
211 				EERIEPOLY *epp;
212 
213 				Vec3f tro = Vec3f::repeat(70.f);
214 
215 				ep = GetMinPoly(dest.x, dest.y, dest.z);
216 				epp = GetMaxPoly(dest.x, dest.y, dest.z);
217 
218 				if(closerThan(player.pos, pos, 200.f)) {
219 					ARX_MISSILES_Kill(i);
220 					ARX_BOOMS_Add(&pos);
221 					Add3DBoom(&pos);
222 					DoSphericDamage(&dest, 180.0F, 200.0F, DAMAGE_AREAHALF, DAMAGE_TYPE_FIRE | DAMAGE_TYPE_MAGICAL);
223 					break;
224 				}
225 
226 				if (ep  && ep->center.y < dest.y)
227 				{
228 					ARX_MISSILES_Kill(i);
229 					ARX_BOOMS_Add(&dest);
230 					Add3DBoom(&dest);
231 					DoSphericDamage(&dest, 180.0F, 200.0F, DAMAGE_AREAHALF, DAMAGE_TYPE_FIRE | DAMAGE_TYPE_MAGICAL);
232 					break;
233 				}
234 
235 				if (epp && epp->center.y > dest.y)
236 				{
237 					ARX_MISSILES_Kill(i);
238 					ARX_BOOMS_Add(&dest);
239 					Add3DBoom(&dest);
240 					DoSphericDamage(&dest, 180.0F, 200.0F, DAMAGE_AREAHALF, DAMAGE_TYPE_FIRE | DAMAGE_TYPE_MAGICAL);
241 					break;
242 				}
243 
244 				if (EERIELaunchRay3(&orgn, &dest, &hit, tp, 1))
245 				{
246 					ARX_MISSILES_Kill(i);
247 					ARX_BOOMS_Add(&hit);
248 					Add3DBoom(&hit);
249 					DoSphericDamage(&dest, 180.0F, 200.0F, DAMAGE_AREAHALF, DAMAGE_TYPE_FIRE | DAMAGE_TYPE_MAGICAL);
250 					break;
251 				}
252 
253 				if ( !EECheckInPoly(&dest) || EEIsUnderWater(&dest) )
254 				{
255 					ARX_MISSILES_Kill(i);
256 					ARX_BOOMS_Add(&dest);
257 					Add3DBoom(&dest);
258 					DoSphericDamage(&dest, 180.0F, 200.0F, DAMAGE_AREAHALF, DAMAGE_TYPE_FIRE | DAMAGE_TYPE_MAGICAL);
259 					break;
260 				}
261 
262 				long ici = IsCollidingAnyInter(dest.x, dest.y, dest.z, &tro);
263 
264 				if (ici != -1 && ici != missiles[i].owner)
265 				{
266 					ARX_MISSILES_Kill(i);
267 					ARX_BOOMS_Add(&dest);
268 					Add3DBoom(&dest);
269 					DoSphericDamage(&dest, 180.0F, 200.0F, DAMAGE_AREAHALF, DAMAGE_TYPE_FIRE | DAMAGE_TYPE_MAGICAL);
270 					break;
271 				}
272 
273 				PARTICLE_DEF * pd = createParticle();
274 				if(pd) {
275 					pd->ov = pos;
276 					pd->move = missiles[i].velocity;
277 					pd->move += Vec3f(3.f - 6.f * rnd(), 4.f - 12.f * rnd(), 3.f - 6.f * rnd());
278 					pd->tolive = Random::get(500, 1000);
279 					pd->tc = tc;
280 					pd->siz = 12.f * float(missiles[i].tolive - framediff3) * (1.f / 4000);
281 					pd->scale = randomVec(15.f, 20.f);
282 					pd->special = FIRE_TO_SMOKE;
283 				}
284 
285 				missiles[i].lastpos = pos;
286 
287 				break;
288 			}
289 		}
290 
291 		missiles[i].lastupdate = tim;
292 	}
293 }
294