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