1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2006 The GemRB Project
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 *
19 */
20
21 #include "ProjectileServer.h"
22
23 #include "GameData.h"
24 #include "Interface.h"
25 #include "PluginMgr.h"
26 #include "ProjectileMgr.h"
27 #include "SymbolMgr.h"
28
29 namespace GemRB {
30
31 //////////////////////////////////////////////////////////////////////
32 // Construction/Destruction
33 //////////////////////////////////////////////////////////////////////
34
35 #define MAX_PROJ_IDX 0x1fff
36
ProjectileServer()37 ProjectileServer::ProjectileServer()
38 {
39 projectilecount = -1;
40 projectiles = NULL;
41 explosioncount = -1;
42 explosions = NULL;
43 }
44
~ProjectileServer()45 ProjectileServer::~ProjectileServer()
46 {
47 if (projectiles) {
48 delete[] projectiles;
49 }
50 if (explosions) {
51 delete[] explosions;
52 }
53 }
54
CreateDefaultProjectile(unsigned int idx)55 Projectile *ProjectileServer::CreateDefaultProjectile(unsigned int idx)
56 {
57 Projectile *pro = new Projectile();
58 //int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Type);
59 //memset(&pro->Type, 0, strlength );
60 int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Speed);
61 memset(&pro->Speed, 0, strlength );
62
63 //take care, this projectile is not freed up by the server
64 if(idx==(unsigned int) ~0 ) {
65 return pro;
66 }
67
68 projectiles[idx].projectile = pro;
69 pro->SetIdentifiers(projectiles[idx].resname, idx);
70 return ReturnCopy(idx);
71 }
72
73 //this function can return only projectiles listed in projectl.ids
GetProjectileByName(const ieResRef resname)74 Projectile *ProjectileServer::GetProjectileByName(const ieResRef resname)
75 {
76 if (!core->IsAvailable(IE_PRO_CLASS_ID)) {
77 return NULL;
78 }
79 unsigned int idx=GetHighestProjectileNumber();
80 while(idx--) {
81 if (!strnicmp(resname, projectiles[idx].resname,8) ) {
82 return GetProjectile(idx);
83 }
84 }
85 return NULL;
86 }
87
GetProjectileByIndex(unsigned int idx)88 Projectile *ProjectileServer::GetProjectileByIndex(unsigned int idx)
89 {
90 if (!core->IsAvailable(IE_PRO_CLASS_ID)) {
91 return NULL;
92 }
93 if (idx>=GetHighestProjectileNumber()) {
94 return GetProjectile(0);
95 }
96
97 return GetProjectile(idx);
98 }
99
ReturnCopy(unsigned int idx)100 Projectile *ProjectileServer::ReturnCopy(unsigned int idx)
101 {
102 Projectile *pro = new Projectile();
103 Projectile *old = projectiles[idx].projectile;
104 //int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Type);
105 //memcpy(&pro->Type, &old->Type, strlength );
106 int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Speed);
107 memcpy(&pro->Speed, &old->Speed, strlength );
108 //FIXME: copy extension data too, or don't alter the extension
109 if (old->Extension) {
110 pro->Extension = old->Extension;
111 }
112 pro->SetIdentifiers(projectiles[idx].resname, idx);
113 return pro;
114 }
115
GetProjectile(unsigned int idx)116 Projectile *ProjectileServer::GetProjectile(unsigned int idx)
117 {
118 if (projectiles[idx].projectile) {
119 return ReturnCopy(idx);
120 }
121 DataStream* str = gamedata->GetResource( projectiles[idx].resname, IE_PRO_CLASS_ID );
122 PluginHolder<ProjectileMgr> sm(IE_PRO_CLASS_ID);
123 if (!sm) {
124 delete ( str );
125 return CreateDefaultProjectile(idx);
126 }
127 if (!sm->Open(str)) {
128 return CreateDefaultProjectile(idx);
129 }
130 Projectile *pro = new Projectile();
131 projectiles[idx].projectile = pro;
132 pro->SetIdentifiers(projectiles[idx].resname, idx);
133
134 sm->GetProjectile( pro );
135 int Type = 0xff;
136
137 if(pro->Extension) {
138 Type = pro->Extension->ExplType;
139 }
140 if(Type<0xff) {
141 ieResRef const *res;
142
143 //fill the spread field according to the hardcoded explosion type
144 res = GetExplosion(Type,0);
145 if(res) {
146 strnuprcpy(pro->Extension->Spread,*res,sizeof(ieResRef)-1);
147 }
148
149 //if the hardcoded explosion type has a center animation
150 //override the VVC animation field with it
151 res = GetExplosion(Type,1);
152 if(res) {
153 pro->Extension->AFlags|=PAF_VVC;
154 strnuprcpy(pro->Extension->VVCRes,*res,sizeof(ieResRef)-1);
155 }
156
157 //fill the secondary spread field out
158 res = GetExplosion(Type,2);
159 if(res) {
160 strnuprcpy(pro->Extension->Secondary,*res,sizeof(ieResRef)-1);
161 }
162
163 //the explosion sound, used for the first explosion
164 //(overrides an original field)
165 res = GetExplosion(Type,3);
166 if(res) {
167 strnuprcpy(pro->Extension->SoundRes,*res,sizeof(ieResRef)-1);
168 }
169
170 //the area sound (used for subsequent explosions)
171 res = GetExplosion(Type,4);
172 if(res) {
173 strnuprcpy(pro->Extension->AreaSound,*res,sizeof(ieResRef)-1);
174 }
175
176 //fill the explosion/spread animation flags
177 pro->Extension->APFlags = GetExplosionFlags(Type);
178 }
179
180 pro->autofree = true;
181 return ReturnCopy(idx);
182 }
183
InitExplosion()184 int ProjectileServer::InitExplosion()
185 {
186 if (explosioncount>=0) {
187 return explosioncount;
188 }
189
190 AutoTable explist("areapro");
191 if (explist) {
192 explosioncount = 0;
193
194 unsigned int rows = explist->GetRowCount();
195 //cannot handle 0xff and it is easier to set up the fields
196 //without areapro.2da anyway. So this isn't a restriction
197 if(rows>254) {
198 rows=254;
199 }
200 explosioncount = rows;
201 explosions = new ExplosionEntry[rows];
202
203 while(rows--) {
204 int i;
205
206 for(i=0;i<AP_RESCNT;i++) {
207 strnuprcpy(explosions[rows].resources[i], explist->QueryField(rows, i), 8);
208 }
209 //using i so the flags field will always be after the resources
210 explosions[rows].flags = atoi(explist->QueryField(rows,i));
211 }
212 }
213 return explosioncount;
214 }
215
PrepareSymbols(Holder<SymbolMgr> projlist)216 unsigned int ProjectileServer::PrepareSymbols(Holder<SymbolMgr> projlist) {
217 unsigned int count = 0;
218
219 unsigned int rows = (unsigned int) projlist->GetSize();
220 while(rows--) {
221 unsigned int value = projlist->GetValueIndex(rows);
222 if (value>MAX_PROJ_IDX) {
223 //value = MAX_PROJ_IDX;
224 Log(WARNING, "ProjectileServer", "Too high projectilenumber");
225 continue; // ignore
226 }
227 if (value > count) {
228 count = value;
229 }
230 }
231
232 return count;
233 }
234
AddSymbols(Holder<SymbolMgr> projlist)235 void ProjectileServer::AddSymbols(Holder<SymbolMgr> projlist) {
236 unsigned int rows = (unsigned int) projlist->GetSize();
237 while(rows--) {
238 unsigned int value = projlist->GetValueIndex(rows);
239 if (value>MAX_PROJ_IDX) {
240 continue;
241 }
242 if (value >= (unsigned int)projectilecount) {
243 // this should never happen!
244 error("ProjectileServer", "Too high projectilenumber while adding projectiles\n");
245 }
246 strnuprcpy(projectiles[value].resname, projlist->GetStringIndex(rows), 8);
247 }
248 }
249
GetHighestProjectileNumber()250 unsigned int ProjectileServer::GetHighestProjectileNumber()
251 {
252 if (projectilecount>=0) {
253 // already read the projectiles
254 return (unsigned int) projectilecount;
255 }
256
257 // built-in gemrb projectiles and game/mod-provided projectiles
258 unsigned int gemresource = core->LoadSymbol("gemprjtl");
259 Holder<SymbolMgr> gemprojlist = core->GetSymbol(gemresource);
260 unsigned int resource = core->LoadSymbol("projectl");
261 Holder<SymbolMgr> projlist = core->GetSymbol(resource);
262
263 // first, we must calculate how many projectiles we have
264 if (gemprojlist) {
265 projectilecount = PrepareSymbols(gemprojlist) + 1;
266 }
267 if (projlist) {
268 unsigned int temp = PrepareSymbols(projlist) + 1;
269 if (projectilecount == -1 || temp > (unsigned int)projectilecount)
270 projectilecount = temp;
271 }
272
273 // then, allocate space for them all
274 if (projectilecount == -1) {
275 // no valid projectiles files..
276 projectilecount = 1;
277 }
278 projectiles = new ProjectileEntry[projectilecount];
279
280 // finally, we must read the projectile resrefs
281 if (projlist) {
282 AddSymbols(projlist);
283 core->DelSymbol(resource);
284 }
285 // gemprojlist is second because it always overrides game/mod-supplied projectiles
286 if (gemprojlist) {
287 AddSymbols(gemprojlist);
288 core->DelSymbol(gemresource);
289 }
290
291 return (unsigned int) projectilecount;
292 }
293
294 //return various flags for the explosion type
GetExplosionFlags(unsigned int idx)295 int ProjectileServer::GetExplosionFlags(unsigned int idx)
296 {
297 if (explosioncount==-1) {
298 if (InitExplosion()<0) {
299 Log(ERROR, "ProjectileServer", "Problem with explosions!");
300 explosioncount=0;
301 }
302 }
303 if (idx>=(unsigned int) explosioncount) {
304 return 0;
305 }
306
307 return explosions[idx].flags;
308 }
309
GetExplosion(unsigned int idx,int type)310 ieResRef const *ProjectileServer::GetExplosion(unsigned int idx, int type)
311 {
312 if (explosioncount==-1) {
313 if (InitExplosion()<0) {
314 Log(ERROR, "ProjectileServer", "Problem with explosions");
315 explosioncount=0;
316 }
317 }
318 if (idx>=(unsigned int) explosioncount) {
319 return NULL;
320 }
321 ieResRef const *ret = NULL;
322
323 ret = &explosions[idx].resources[type];
324 if (ret && *ret[0]=='*') ret = NULL;
325
326 return ret;
327 }
328
329 }
330