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