fixed the last commit to set and check the target more often, thanks fuzzie
[gemrb.git] / gemrb / core / ProjectileServer.cpp
blobc3a2e807f38ca03f41519207c7dc1ba6aa002791
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2006 The GemRB Project
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.
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.
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.
21 #include "ProjectileServer.h"
23 #include "GameData.h"
24 #include "Interface.h"
25 #include "ProjectileMgr.h"
26 #include "SymbolMgr.h"
28 //////////////////////////////////////////////////////////////////////
29 // Construction/Destruction
30 //////////////////////////////////////////////////////////////////////
32 #define MAX_PROJ_IDX 0x1fff
34 ProjectileServer::ProjectileServer()
36 projectilecount = -1;
37 projectiles = NULL;
38 explosioncount = -1;
39 explosions = NULL;
42 ProjectileServer::~ProjectileServer()
44 if (projectiles) {
45 delete[] projectiles;
47 if (explosions) {
48 delete[] explosions;
52 Projectile *ProjectileServer::CreateDefaultProjectile(unsigned int idx)
54 Projectile *pro = new Projectile();
55 //int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Type);
56 //memset(&pro->Type, 0, strlength );
57 int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Speed);
58 memset(&pro->Speed, 0, strlength );
60 //take care, this projectile is not freed up by the server
61 if(idx==(unsigned int) ~0 ) {
62 return pro;
65 projectiles[idx].projectile = pro;
66 pro->SetIdentifiers(projectiles[idx].resname, idx);
67 return ReturnCopy(idx);
70 //this function can return only projectiles listed in projectl.ids
71 Projectile *ProjectileServer::GetProjectileByName(const ieResRef resname)
73 if (!core->IsAvailable(IE_PRO_CLASS_ID)) {
74 return NULL;
76 unsigned int idx=GetHighestProjectileNumber();
77 while(idx--) {
78 if (!strnicmp(resname, projectiles[idx].resname,8) ) {
79 return GetProjectile(idx);
82 return NULL;
85 Projectile *ProjectileServer::GetProjectileByIndex(unsigned int idx)
87 if (!core->IsAvailable(IE_PRO_CLASS_ID)) {
88 return NULL;
90 if (idx>=GetHighestProjectileNumber()) {
91 return GetProjectile(0);
94 return GetProjectile(idx);
97 Projectile *ProjectileServer::ReturnCopy(unsigned int idx)
99 Projectile *pro = new Projectile();
100 Projectile *old = projectiles[idx].projectile;
101 //int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Type);
102 //memcpy(&pro->Type, &old->Type, strlength );
103 int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Speed);
104 memcpy(&pro->Speed, &old->Speed, strlength );
105 //FIXME: copy extension data too, or don't alter the extension
106 if (old->Extension) {
107 pro->Extension = old->Extension;
109 pro->SetIdentifiers(projectiles[idx].resname, idx);
110 return pro;
113 Projectile *ProjectileServer::GetProjectile(unsigned int idx)
115 if (projectiles[idx].projectile) {
116 return ReturnCopy(idx);
118 DataStream* str = gamedata->GetResource( projectiles[idx].resname, IE_PRO_CLASS_ID );
119 PluginHolder<ProjectileMgr> sm(IE_PRO_CLASS_ID);
120 if (!sm) {
121 delete ( str );
122 return CreateDefaultProjectile(idx);
124 if (!sm->Open( str, true )) {
125 return CreateDefaultProjectile(idx);
127 Projectile *pro = new Projectile();
128 projectiles[idx].projectile = pro;
129 pro->SetIdentifiers(projectiles[idx].resname, idx);
131 sm->GetProjectile( pro );
132 int Type = 0xff;
134 if(pro->Extension) {
135 Type = pro->Extension->ExplType;
137 if(Type<0xff) {
138 ieResRef const *res;
140 //fill the spread field according to the hardcoded explosion type
141 res = GetExplosion(Type,0);
142 if(res) {
143 strnuprcpy(pro->Extension->Spread,*res,sizeof(ieResRef)-1);
146 //if the hardcoded explosion type has a center animation
147 //override the VVC animation field with it
148 res = GetExplosion(Type,1);
149 if(res) {
150 pro->Extension->AFlags|=PAF_VVC;
151 strnuprcpy(pro->Extension->VVCRes,*res,sizeof(ieResRef)-1);
154 //fill the secondary spread field out
155 res = GetExplosion(Type,2);
156 if(res) {
157 strnuprcpy(pro->Extension->Secondary,*res,sizeof(ieResRef)-1);
160 //the explosion sound, used for the first explosion
161 //(overrides an original field)
162 res = GetExplosion(Type,3);
163 if(res) {
164 strnuprcpy(pro->Extension->SoundRes,*res,sizeof(ieResRef)-1);
167 //the area sound (used for subsequent explosions)
168 res = GetExplosion(Type,4);
169 if(res) {
170 strnuprcpy(pro->Extension->AreaSound,*res,sizeof(ieResRef)-1);
173 //fill the explosion/spread animation flags
174 pro->Extension->APFlags = GetExplosionFlags(Type);
177 pro->autofree = true;
178 return ReturnCopy(idx);
181 int ProjectileServer::InitExplosion()
183 if (explosioncount>=0) {
184 return explosioncount;
187 AutoTable explist("areapro");
188 if (explist) {
189 explosioncount = 0;
191 unsigned int rows = (unsigned int) explist->GetRowCount();
192 //cannot handle 0xff and it is easier to set up the fields
193 //without areapro.2da anyway. So this isn't a restriction
194 if(rows>254) {
195 rows=254;
197 explosioncount = rows;
198 explosions = new ExplosionEntry[rows];
200 while(rows--) {
201 int i;
203 for(i=0;i<AP_RESCNT;i++) {
204 strnuprcpy(explosions[rows].resources[i], explist->QueryField(rows, i), 8);
206 //using i so the flags field will always be after the resources
207 explosions[rows].flags = atoi(explist->QueryField(rows,i));
210 return explosioncount;
213 unsigned int ProjectileServer::PrepareSymbols(Holder<SymbolMgr> projlist) {
214 unsigned int count = 0;
216 unsigned int rows = (unsigned int) projlist->GetSize();
217 while(rows--) {
218 unsigned int value = projlist->GetValueIndex(rows);
219 if (value>MAX_PROJ_IDX) {
220 //value = MAX_PROJ_IDX;
221 printMessage("ProjectileServer","Too high projectilenumber\n", YELLOW);
222 continue; // ignore
224 if (value>(unsigned int) count) {
225 count = (unsigned int) value;
229 return count;
232 void ProjectileServer::AddSymbols(Holder<SymbolMgr> projlist) {
233 unsigned int rows = (unsigned int) projlist->GetSize();
234 while(rows--) {
235 unsigned int value = projlist->GetValueIndex(rows);
236 if (value>MAX_PROJ_IDX) {
237 continue;
239 if (value >= (unsigned int)projectilecount) {
240 // this should never happen!
241 printMessage("ProjectileServer","Too high projectilenumber while adding projectiles\n", RED);
242 abort();
244 strnuprcpy(projectiles[value].resname, projlist->GetStringIndex(rows), 8);
248 unsigned int ProjectileServer::GetHighestProjectileNumber()
250 if (projectilecount>=0) {
251 // already read the projectiles
252 return (unsigned int) projectilecount;
255 // built-in gemrb projectiles and game/mod-provided projectiles
256 unsigned int gemresource = core->LoadSymbol("gemprjtl");
257 Holder<SymbolMgr> gemprojlist = core->GetSymbol(gemresource);
258 unsigned int resource = core->LoadSymbol("projectl");
259 Holder<SymbolMgr> projlist = core->GetSymbol(resource);
261 // first, we must calculate how many projectiles we have
262 if (gemprojlist) {
263 projectilecount = PrepareSymbols(gemprojlist) + 1;
265 if (projlist) {
266 unsigned int temp = PrepareSymbols(projlist) + 1;
267 if (projectilecount == -1 || temp > (unsigned int)projectilecount)
268 projectilecount = temp;
271 // then, allocate space for them all
272 if (projectilecount == -1) {
273 // no valid projectiles files..
274 projectilecount = 1;
276 projectiles = new ProjectileEntry[projectilecount];
278 // finally, we must read the projectile resrefs
279 if (projlist) {
280 AddSymbols(projlist);
281 core->DelSymbol(resource);
283 // gemprojlist is second because it always overrides game/mod-supplied projectiles
284 if (gemprojlist) {
285 AddSymbols(gemprojlist);
286 core->DelSymbol(gemresource);
289 return (unsigned int) projectilecount;
292 //return various flags for the explosion type
293 int ProjectileServer::GetExplosionFlags(unsigned int idx)
295 if (explosioncount==-1) {
296 if (InitExplosion()<0) {
297 printMessage("ProjectileServer","Problem with explosions\n", RED);
298 explosioncount=0;
301 if (idx>=(unsigned int) explosioncount) {
302 return 0;
305 return explosions[idx].flags;
308 ieResRef const *ProjectileServer::GetExplosion(unsigned int idx, int type)
310 if (explosioncount==-1) {
311 if (InitExplosion()<0) {
312 printMessage("ProjectileServer","Problem with explosions\n", RED);
313 explosioncount=0;
316 if (idx>=(unsigned int) explosioncount) {
317 return NULL;
319 ieResRef const *ret = NULL;
321 ret = &explosions[idx].resources[type];
322 if (ret && *ret[0]=='*') ret = NULL;
324 return ret;