1 /////////////////////////////////////////
2 //
3 // OpenLieroX
4 //
5 // code under LGPL, based on JasonBs work,
6 // enhanced by Dark Charlie and Albert Zeyer
7 //
8 //
9 /////////////////////////////////////////
10
11
12 // Game script class
13 // Created 7/2/02
14 // Jason Boettcher
15
16 /*
17 reference for original Compile* sources:
18 http://openlierox.svn.sourceforge.net/viewvc/openlierox/src/common/CGameScript.cpp?revision=3781&view=markup
19 */
20
21 #include <cstdarg>
22
23 #include "EndianSwap.h"
24 #include "LieroX.h"
25 #include "Debug.h"
26 #include "FindFile.h"
27 #include "StringUtils.h"
28 #include "CGameScript.h"
29 #include "Error.h"
30 #include "ConfigHandler.h"
31 #include "ProjectileDesc.h"
32 #include "WeaponDesc.h"
33 #include "IniReader.h"
34
35
36
initNewWeapons(int num)37 void CGameScript::initNewWeapons(int num) {
38 if(Weapons) delete[] Weapons;
39 SetNumWeapons(num);
40 Weapons = new weapon_t[num];
41 }
42
43
44
45 ///////////////////
46 // Write a string in pascal format
writeString(const std::string & szString,FILE * fp)47 static void writeString(const std::string& szString, FILE *fp)
48 {
49 if(szString == "") return;
50
51 size_t length = szString.size();
52 if(length > 255) {
53 warnings << "i will cut the following string for writing: " << szString << endl;
54 length = 255;
55 }
56 uchar len = (uchar)length;
57
58 fwrite( &len, sizeof(uchar), 1, fp );
59 fwrite( szString.c_str(),sizeof(char), length, fp );
60 }
61
62
63 ///////////////////
64 // Read a string in pascal format
readString(FILE * fp)65 static std::string readString(FILE *fp)
66 {
67 char buf[256];
68
69 uchar length;
70 if(fread( &length, sizeof(uchar), 1, fp ) == 0) return "";
71 if(fread( buf,sizeof(char), length, fp ) < length) return "";
72
73 buf[length] = '\0';
74
75 return buf;
76 }
77
78
79
80 ///////////////////
81 // Save the script (compiler)
Save(const std::string & filename)82 int CGameScript::Save(const std::string& filename)
83 {
84 FILE *fp;
85 int n;
86
87
88 // Open it
89 fp = OpenGameFile(filename,"wb");
90 if(fp == NULL) {
91 errors << "CGameScript::Save: Could not open " << filename << " for writing" << endl;
92 return false;
93 }
94
95 Header.Version = GS_VERSION;
96 strcpy(Header.ID,"Liero Game Script");
97
98
99 // Header
100 gs_header_t tmpheader = Header;
101 EndianSwap(tmpheader.Version);
102 fwrite(&tmpheader,sizeof(gs_header_t),1,fp);
103
104 fwrite_endian_compat((NumWeapons),sizeof(int),1,fp);
105
106 savedProjs.clear();
107
108 // Weapons
109 weapon_t *wpn = Weapons;
110
111 for(n=0;n<NumWeapons;n++,wpn++) {
112
113 writeString(wpn->Name, fp);
114 fwrite_endian_compat((wpn->Type), sizeof(int), 1, fp);
115
116 // Special
117 if(wpn->Type == WPN_SPECIAL) {
118 fwrite_endian_compat((wpn->Special), sizeof(int), 1, fp);
119 fwrite_endian_compat((wpn->tSpecial), sizeof(gs_special_t), 1, fp); // WARNING: this works, because it contains only one int
120 fwrite_endian_compat((wpn->Recharge*10.f), sizeof(float), 1, fp);
121 fwrite_endian_compat((wpn->Drain), sizeof(float), 1, fp);
122 fwrite_endian_compat((wpn->ROF*1000.0f), sizeof(float), 1, fp);
123 fwrite_endian<int>(fp, wpn->LaserSight);
124 }
125
126 // Beam
127 if(wpn->Type == WPN_BEAM) {
128 fwrite_endian_compat((wpn->Recoil), sizeof(int), 1, fp);
129 fwrite_endian_compat((wpn->Recharge*10.f), sizeof(float), 1, fp);
130 fwrite_endian_compat((wpn->Drain), sizeof(float), 1, fp);
131 fwrite_endian_compat((wpn->ROF*1000.0f), sizeof(float), 1, fp);
132 fwrite_endian<int>(fp, wpn->LaserSight);
133
134 wpn->Bm.write(this, fp);
135 }
136
137 // Projectile
138 if(wpn->Type == WPN_PROJECTILE) {
139 fwrite_endian_compat((wpn->Class), sizeof(int), 1, fp);
140 fwrite_endian_compat((wpn->Recoil), sizeof(int), 1, fp);
141 fwrite_endian_compat((wpn->Recharge*10.f), sizeof(float), 1, fp);
142 fwrite_endian_compat((wpn->Drain), sizeof(float), 1, fp);
143 fwrite_endian_compat((wpn->ROF*1000.0f), sizeof(float), 1, fp);
144
145 if(Header.Version <= GS_LX56_VERSION) {
146 fwrite_endian_compat((wpn->Proj.Speed), sizeof(int), 1, fp);
147 fwrite_endian_compat((wpn->Proj.SpeedVar),sizeof(float),1, fp);
148 fwrite_endian_compat((wpn->Proj.Spread),sizeof(float), 1, fp);
149 fwrite_endian_compat((wpn->Proj.Amount),sizeof(int), 1, fp);
150 fwrite_endian<int>(fp, wpn->LaserSight);
151
152 fwrite_endian<int>(fp, wpn->UseSound);
153 if(wpn->UseSound)
154 writeString(wpn->SndFilename, fp);
155
156 // Recursively save the projectile id's
157 SaveProjectile(wpn->Proj.Proj,fp);
158 }
159 else { // newer GS versions
160 fwrite_endian<char>(fp, wpn->LaserSight);
161
162 fwrite_endian<char>(fp, wpn->UseSound);
163 if(wpn->UseSound)
164 writeString(wpn->SndFilename, fp);
165
166 // Recursively save the projectile id's
167 wpn->Proj.write(this, fp);
168 }
169 }
170
171 if(Header.Version > GS_LX56_VERSION)
172 wpn->FinalProj.write(this, fp);
173 }
174
175 // Extra stuff
176
177
178 // Ninja Rope
179 fwrite_endian_compat((RopeLength),sizeof(int),1,fp);
180 fwrite_endian_compat((RestLength),sizeof(int),1,fp);
181 fwrite_endian_compat((Strength),sizeof(float),1,fp);
182
183 // Worm
184 gs_worm_t tmpworm = Worm;
185 EndianSwap(tmpworm.AngleSpeed);
186 EndianSwap(tmpworm.GroundSpeed);
187 EndianSwap(tmpworm.AirSpeed);
188 EndianSwap(tmpworm.Gravity);
189 EndianSwap(tmpworm.JumpForce);
190 EndianSwap(tmpworm.AirFriction);
191 EndianSwap(tmpworm.GroundFriction);
192 fwrite(&tmpworm, sizeof(gs_worm_t), 1, fp);
193
194
195 fclose(fp);
196
197 savedProjs.clear();
198
199 return true;
200 }
201
202
203 ///////////////////
204 // Save a projectile
SaveProjectile(proj_t * proj,FILE * fp)205 bool CGameScript::SaveProjectile(proj_t *proj, FILE *fp)
206 {
207 if(!proj)
208 return false;
209
210 if(Header.Version > GS_LX56_VERSION) {
211 // well, that's slow but it's not worth to create a reverted-map just for saving
212 int index = -1;
213 for(Projectiles::iterator it = projectiles.begin(); it != projectiles.end(); ++it) {
214 if(it->second == proj) {
215 index = it->first;
216 break;
217 }
218 }
219 fwrite_endian<int>(fp, index);
220 if(savedProjs.find(proj) != savedProjs.end()) return false;
221 savedProjs.insert(proj);
222 }
223
224 fwrite_endian_compat((proj->Type), sizeof(int),1,fp);
225 fwrite_endian_compat((proj->Timer.Time), sizeof(float),1,fp);
226 fwrite_endian_compat((proj->Timer.TimeVar),sizeof(float),1,fp);
227 fwrite_endian_compat((proj->Trail.Type), sizeof(int),1,fp);
228 //fwrite_endian_compat(proj->UseCustomGravity, sizeof(int), 1, fp); // use this to test static assert (should fail if sizeof(bool)!=sizeof(int))
229 fwrite_endian<int>(fp, proj->UseCustomGravity);
230
231 if(proj->UseCustomGravity)
232 fwrite_endian_compat((proj->Gravity), sizeof(int), 1, fp);
233
234 fwrite_endian_compat((proj->Dampening), sizeof(int), 1, fp);
235
236 if(Header.Version > GS_LX56_VERSION) {
237 fwrite_endian<int>(fp, proj->AttractiveForce);
238 fwrite_endian<int>(fp, proj->AttractiveForceType);
239 fwrite_endian<int>(fp, proj->AttractiveForceObjects);
240 fwrite_endian<int>(fp, proj->AttractiveForceClasses);
241 fwrite_endian<int>(fp, proj->AttractiveForceRadius);
242 fwrite_endian<bool>(fp, proj->AttractiveForceThroughWalls);
243 fwrite_endian<int>(fp, proj->Width);
244 fwrite_endian<int>(fp, proj->Height);
245 }
246
247 // Pixel type
248 switch(proj->Type) {
249 case PRJ_POLYGON:
250 fwrite_endian<Uint32>(fp, (Uint32)proj->polygon.getPoints().size());
251 for(Polygon2D::Points::const_iterator p = proj->polygon.getPoints().begin(); p != proj->polygon.getPoints().end(); ++p) {
252 fwrite_endian<int>(fp, p->x);
253 fwrite_endian<int>(fp, p->y);
254 }
255
256 // fallthrough to save color
257 case PRJ_RECT:
258 case PRJ_CIRCLE:
259 case PRJ_PIXEL:
260 fwrite_endian_compat(((int)proj->Colour.size()), sizeof(int), 1, fp);
261 for(size_t i = 0; i < proj->Colour.size(); ++i) {
262 if(Header.Version <= GS_LX56_VERSION && i >= 2) {
263 warnings << "SaveProjectile: projectile has more than 2 colors, other colors are ignored for LX56 format" << endl;
264 break;
265 }
266 if(Header.Version <= GS_LX56_VERSION) {
267 fwrite_endian_compat(((int)proj->Colour[i].r), sizeof(int),1,fp);
268 fwrite_endian_compat(((int)proj->Colour[i].g), sizeof(int),1,fp);
269 fwrite_endian_compat(((int)proj->Colour[i].b), sizeof(int),1,fp);
270 } else {
271 fwrite_endian_compat((proj->Colour[i].r), 1,1,fp);
272 fwrite_endian_compat((proj->Colour[i].g), 1,1,fp);
273 fwrite_endian_compat((proj->Colour[i].b), 1,1,fp);
274 fwrite_endian_compat((proj->Colour[i].a), 1,1,fp);
275 }
276 }
277 break;
278
279 case PRJ_IMAGE:
280 writeString(proj->ImgFilename, fp);
281 fwrite_endian<int>(fp, proj->Rotating);
282 fwrite_endian_compat((proj->RotIncrement), sizeof(int), 1, fp);
283 fwrite_endian_compat((proj->RotSpeed), sizeof(int),1,fp);
284 fwrite_endian<int>(fp, proj->UseAngle);
285 fwrite_endian<int>(fp, proj->UseSpecAngle);
286 if(proj->UseAngle || proj->UseSpecAngle)
287 fwrite_endian_compat((proj->AngleImages),sizeof(int),1,fp);
288 fwrite_endian<int>(fp, proj->Animating);
289 if(proj->Animating) {
290 fwrite_endian_compat((proj->AnimRate),sizeof(float),1,fp);
291 fwrite_endian_compat((proj->AnimType),sizeof(int),1,fp);
292 }
293 break;
294
295 case __PRJ_LBOUND: case __PRJ_UBOUND: errors << "SaveProjectile: PRJ BOUND" << endl;
296 }
297
298
299
300
301 //
302 // Hit
303 //
304 if(Header.Version <= GS_LX56_VERSION) {
305 fwrite_endian_compat((proj->Hit.Type),sizeof(int),1,fp);
306
307 // Hit::Explode
308 if(proj->Hit.Type == PJ_EXPLODE) {
309 fwrite_endian_compat((proj->Hit.Damage), sizeof(int),1,fp);
310 fwrite_endian<int>(fp, proj->Hit.Projectiles);
311 fwrite_endian<int>(fp, proj->Hit.UseSound);
312 fwrite_endian_compat((proj->Hit.Shake), sizeof(int),1,fp);
313 if(proj->Hit.UseSound)
314 writeString(proj->Hit.SndFilename, fp);
315 }
316
317 // Hit::Bounce
318 if(proj->Hit.Type == PJ_BOUNCE) {
319 fwrite_endian_compat((proj->Hit.BounceCoeff), sizeof(float), 1,fp);
320 fwrite_endian_compat((proj->Hit.BounceExplode),sizeof(int), 1,fp);
321 }
322
323 // Hit::Carve
324 if(proj->Hit.Type == PJ_CARVE) {
325 fwrite_endian_compat((proj->Hit.Damage), sizeof(int),1,fp);
326 }
327 }
328 else { // newer GS version
329 proj->Hit.write(this, fp);
330 }
331
332
333
334
335 //
336 // Timer
337 //
338 if(proj->Timer.Time > 0) {
339 if(Header.Version <= GS_LX56_VERSION) {
340 fwrite_endian_compat((proj->Timer.Type),sizeof(int),1,fp);
341 if(proj->Timer.Type == PJ_EXPLODE) {
342 fwrite_endian_compat((proj->Timer.Damage), sizeof(int),1,fp);
343 fwrite_endian<int>(fp, proj->Timer.Projectiles);
344 fwrite_endian_compat((proj->Timer.Shake),sizeof(int),1,fp);
345 }
346 }
347 else {
348 proj->Timer.write(this, fp);
349 }
350 }
351
352
353 //
354 // Player hit
355 //
356 if(Header.Version <= GS_LX56_VERSION) {
357 fwrite_endian_compat((proj->PlyHit.Type),sizeof(int),1,fp);
358
359 // PlyHit::Explode || PlyHit::Injure
360 if(proj->PlyHit.Type == PJ_EXPLODE || proj->PlyHit.Type == PJ_INJURE) {
361 fwrite_endian_compat((proj->PlyHit.Damage),sizeof(int),1,fp);
362 fwrite_endian<int>(fp, proj->PlyHit.Projectiles);
363 }
364
365 // PlyHit::Bounce
366 if(proj->PlyHit.Type == PJ_BOUNCE) {
367 fwrite_endian_compat((proj->PlyHit.BounceCoeff),sizeof(float),1,fp);
368 }
369 }
370 else { // newer GS version
371 proj->PlyHit.write(this, fp);
372 }
373
374
375 if(Header.Version <= GS_LX56_VERSION) {
376 // NOTE: this is obsolete, they are not used
377
378 //
379 // Explode
380 //
381 fwrite_endian_compat((proj->Exp.Type), sizeof(int), 1, fp);
382 fwrite_endian_compat((proj->Exp.Damage), sizeof(int), 1, fp);
383 fwrite_endian<int>(fp, proj->Exp.Projectiles);
384 fwrite_endian<int>(fp, proj->Exp.UseSound);
385 if(proj->Exp.UseSound)
386 writeString(proj->Exp.SndFilename, fp);
387
388
389 //
390 // Touch
391 //
392 fwrite_endian_compat((proj->Tch.Type), sizeof(int), 1, fp);
393 fwrite_endian_compat((proj->Tch.Damage), sizeof(int), 1, fp);
394 fwrite_endian<int>(fp, proj->Tch.Projectiles);
395 fwrite_endian<int>(fp, proj->Tch.UseSound);
396 if(proj->Tch.UseSound)
397 writeString(proj->Tch.SndFilename, fp);
398 }
399
400 if(Header.Version > GS_LX56_VERSION) {
401 fwrite_endian<Uint32>(fp, (Uint32)proj->actions.size());
402 for(Uint32 i = 0; i < proj->actions.size(); ++i) {
403 proj->actions[i].write(this, fp);
404 }
405 }
406
407 if(Header.Version > GS_LX56_VERSION) {
408 // always save, there are many cases where we could possibly need it
409 proj->GeneralSpawnInfo.write(this, fp);
410 }
411 else if(proj->Timer.Projectiles || proj->Hit.Projectiles || proj->PlyHit.Projectiles || proj->Exp.Projectiles ||
412 proj->Tch.Projectiles) {
413 fwrite_endian<int>(fp, proj->GeneralSpawnInfo.Useangle);
414 fwrite_endian_compat((proj->GeneralSpawnInfo.Angle), sizeof(int), 1, fp);
415
416 fwrite_endian_compat((proj->GeneralSpawnInfo.Amount), sizeof(int), 1, fp);
417 fwrite_endian_compat((proj->GeneralSpawnInfo.Spread), sizeof(float), 1, fp);
418 fwrite_endian_compat((proj->GeneralSpawnInfo.Speed), sizeof(int), 1, fp);
419 fwrite_endian_compat((proj->GeneralSpawnInfo.SpeedVar), sizeof(float), 1, fp);
420
421 SaveProjectile(proj->GeneralSpawnInfo.Proj,fp);
422 }
423
424
425 // Projectile trail
426 if(proj->Trail.Type == TRL_PROJECTILE) {
427
428 fwrite_endian<int>(fp, proj->Trail.Proj.UseParentVelocityForSpread);
429 fwrite_endian_compat((proj->Trail.Delay*1000.0f), sizeof(float), 1, fp);
430
431 if(Header.Version <= GS_LX56_VERSION) {
432 fwrite_endian_compat((proj->Trail.Proj.Amount), sizeof(int), 1, fp);
433 fwrite_endian_compat((proj->Trail.Proj.Speed), sizeof(int), 1, fp);
434 fwrite_endian_compat((proj->Trail.Proj.SpeedVar), sizeof(float), 1, fp);
435 fwrite_endian_compat((proj->Trail.Proj.Spread), sizeof(float), 1, fp);
436
437 SaveProjectile(proj->Trail.Proj.Proj, fp);
438 }
439 else { // newer GS versions
440 proj->Trail.Proj.write(this, fp);
441 }
442
443 }
444
445 return true;
446 }
447
448
449 ///////////////////
450 // Load the game script from a file (game)
Load(const std::string & dir)451 int CGameScript::Load(const std::string& dir)
452 {
453 // TODO: remove that as soon as we do the gamescript loading in a seperate thread
454 ScopedBackgroundLoadingAni backgroundLoadingAni(320, 280, 50, 50, Color(128,128,128), Color(64,64,64));
455
456 // Already cached externally
457 /*
458 // Try cache first
459 CGameScript *cached = cCache.GetMod(dir);
460 if (cached != NULL) {
461 CopyFrom(cached);
462 return GSE_OK;
463 }
464 */
465 Shutdown();
466
467 int n;
468 std::string filename = dir + "/script.lgs";
469 sDirectory = dir;
470
471 // Open it
472 FILE* fp = OpenGameFile(filename,"rb");
473 if(fp == NULL) {
474 if(IsFileAvailable(dir + "/main.txt")) {
475 hints << "GameScript: '" << dir << "': loading from gamescript source" << endl;
476 if(Compile(dir))
477 return GSE_OK;
478 else {
479 warnings << "GameScript::Load(): could not compile source gamescript '" << dir << "'" << endl;
480 return GSE_BAD;
481 }
482 }
483
484 warnings << "CGameScript::Load(): Could not load file " << filename << endl;
485 return GSE_FILE;
486 }
487
488 // Header
489 fread_compat(Header,sizeof(gs_header_t),1,fp);
490 EndianSwap(Header.Version);
491 // for security
492 fix_markend(Header.ID);
493 fix_markend(Header.ModName);
494
495 // Check ID
496 if(strcmp(Header.ID,"Liero Game Script") != 0) {
497 fclose(fp);
498 SetError("CGameScript::Load(): Bad script id");
499 return GSE_BAD;
500 }
501
502 // Check version
503 if(Header.Version < GS_FIRST_SUPPORTED_VERSION || Header.Version > GS_VERSION) {
504 warnings << "GS:CheckFile: WARNING: " << dir << "/script.lgs has a wrong version";
505 warnings << " (" << (unsigned)Header.Version << ", required is in the range ";
506 warnings << "[" << GS_FIRST_SUPPORTED_VERSION << "," << GS_VERSION << "])" << endl;
507 fclose(fp);
508 SetError("CGameScript::Load(): Bad script version");
509 return GSE_VERSION;
510 }
511
512 // Clear an old mod file
513 modLog("Loading game mod file " + filename);
514 //modLog(" ID = %s", Header.ID);
515 //modLog(" Version = %i", Header.Version);
516
517 fread_compat(NumWeapons,sizeof(int),1,fp);
518 EndianSwap(NumWeapons);
519 //modLog(" NumWeapons = %i", NumWeapons);
520
521 // Do Allocations
522 Weapons = new weapon_t[NumWeapons];
523 if(Weapons == NULL) {
524 SetError("SGameScript::Load(): Out of memory");
525 return GSE_MEM;
526 }
527
528 // Weapons
529 weapon_t *wpn;
530 for(n=0;n<NumWeapons;n++) {
531 wpn = &Weapons[n];
532
533 wpn->ID = n;
534 wpn->Proj.Proj = NULL;
535
536 wpn->Name = readString(fp);
537 fread_compat(wpn->Type, sizeof(int), 1,fp);
538 EndianSwap(wpn->Type);
539
540 // Special
541 if(wpn->Type == WPN_SPECIAL) {
542 fread_compat(wpn->Special, sizeof(int), 1, fp);
543 EndianSwap(wpn->Special);
544 fread_compat(wpn->tSpecial, sizeof(gs_special_t), 1, fp);
545 EndianSwap(wpn->tSpecial.Thrust);
546 fread_compat(wpn->Recharge, sizeof(float), 1, fp);
547 EndianSwap(wpn->Recharge);
548 fread_compat(wpn->Drain, sizeof(float), 1, fp);
549 EndianSwap(wpn->Drain);
550 fread_compat(wpn->ROF, sizeof(float), 1, fp);
551 EndianSwap(wpn->ROF);
552 fread_endian<int>(fp, wpn->LaserSight);
553 }
554
555 // Beam
556 if(wpn->Type == WPN_BEAM) {
557 fread_compat(wpn->Recoil, sizeof(int), 1, fp);
558 EndianSwap(wpn->Recoil);
559 fread_compat(wpn->Recharge, sizeof(float), 1, fp);
560 EndianSwap(wpn->Recharge);
561 fread_compat(wpn->Drain, sizeof(float), 1, fp);
562 EndianSwap(wpn->Drain);
563 fread_compat(wpn->ROF, sizeof(float), 1, fp);
564 EndianSwap(wpn->ROF);
565 fread_endian<int>(fp, wpn->LaserSight);
566
567 wpn->Bm.read(this, fp);
568 }
569
570 // Projectile
571 if(wpn->Type == WPN_PROJECTILE) {
572 fread_compat(wpn->Class,sizeof(int),1,fp);
573 EndianSwap(wpn->Class);
574 fread_compat(wpn->Recoil,sizeof(int),1,fp);
575 EndianSwap(wpn->Recoil);
576 fread_compat(wpn->Recharge,sizeof(float),1,fp);
577 EndianSwap(wpn->Recharge);
578 fread_compat(wpn->Drain,sizeof(float),1,fp);
579 EndianSwap(wpn->Drain);
580 fread_compat(wpn->ROF,sizeof(float),1,fp);
581 EndianSwap(wpn->ROF);
582
583 if(Header.Version <= GS_LX56_VERSION) {
584 fread_compat(wpn->Proj.Speed,sizeof(int),1,fp);
585 EndianSwap(wpn->Proj.Speed);
586 fread_compat(wpn->Proj.SpeedVar,sizeof(float),1,fp);
587 EndianSwap(wpn->Proj.SpeedVar);
588 fread_compat(wpn->Proj.Spread,sizeof(float),1,fp);
589 EndianSwap(wpn->Proj.Spread);
590 fread_compat(wpn->Proj.Amount,sizeof(int),1,fp);
591 EndianSwap(wpn->Proj.Amount);
592 fread_endian<int>(fp, wpn->LaserSight);
593
594 fread_endian<int>(fp, wpn->UseSound);
595 if(wpn->UseSound)
596 wpn->SndFilename = readString(fp);
597
598 wpn->Proj.Proj = LoadProjectile(fp);
599 }
600 else { // newer GS versions
601 fread_endian<char>(fp, wpn->LaserSight);
602
603 fread_endian<char>(fp, wpn->UseSound);
604 if(wpn->UseSound)
605 wpn->SndFilename = readString(fp);
606
607 wpn->Proj.read(this, fp);
608 }
609
610 if(!bDedicated && wpn->UseSound) {
611 // Load the sample
612 wpn->smpSample = LoadGSSample(dir,wpn->SndFilename);
613
614 if(wpn->smpSample == NULL)
615 wpn->UseSound = false;
616 }
617 }
618
619 wpn->ROF /= 1000.0f;
620 wpn->Recharge /= 10.0f;
621
622 if(Header.Version > GS_LX56_VERSION)
623 wpn->FinalProj.read(this, fp);
624 }
625
626
627 // Extra stuff
628
629
630 // Ninja Rope
631 fread_compat(RopeLength,sizeof(int),1,fp);
632 EndianSwap(RopeLength);
633 fread_compat(RestLength,sizeof(int),1,fp);
634 EndianSwap(RestLength);
635 fread_compat(Strength,sizeof(float),1,fp);
636 EndianSwap(Strength);
637
638 // Worm
639 fread_compat(Worm, sizeof(gs_worm_t), 1, fp);
640 EndianSwap(Worm.AngleSpeed);
641 EndianSwap(Worm.GroundSpeed);
642 EndianSwap(Worm.AirSpeed);
643 EndianSwap(Worm.Gravity);
644 EndianSwap(Worm.JumpForce);
645 EndianSwap(Worm.AirFriction);
646 EndianSwap(Worm.GroundFriction);
647
648
649 fclose(fp);
650
651 // Already cached externally
652 // Save to cache
653 //cCache.SaveMod(dir, this);
654
655 loaded = true;
656
657 return GSE_OK;
658 }
659
660
661 ///////////////////
662 // Load a projectile
LoadProjectile(FILE * fp)663 proj_t *CGameScript::LoadProjectile(FILE *fp)
664 {
665 int projIndex = -1;
666 if(Header.Version > GS_LX56_VERSION) {
667 fread_endian<int>(fp, projIndex);
668 std::map<int, proj_t*>::iterator f = projectiles.find(projIndex);
669 if(f != projectiles.end())
670 return f->second;
671 }
672 else
673 projIndex = (int)projectiles.size();
674
675 proj_t *proj = new proj_t;
676 if(proj == NULL)
677 return NULL;
678 projectiles[projIndex] = proj;
679
680 fread_compat(proj->Type, sizeof(int), 1,fp);
681 EndianSwap(proj->Type);
682 fread_compat(proj->Timer.Time, sizeof(float),1,fp);
683 EndianSwap(proj->Timer.Time);
684 fread_compat(proj->Timer.TimeVar, sizeof(float),1,fp);
685 EndianSwap(proj->Timer.TimeVar);
686 fread_compat(proj->Trail.Type, sizeof(int), 1,fp);
687 EndianSwap(proj->Trail.Type);
688 fread_endian<int>(fp, proj->UseCustomGravity);
689 if(proj->UseCustomGravity)
690 {
691 fread_compat(proj->Gravity, sizeof(int), 1, fp);
692 EndianSwap(proj->Gravity);
693 }
694 fread_compat(proj->Dampening, sizeof(int), 1, fp);
695 EndianSwap(proj->Dampening);
696
697 if(Header.Version > GS_LX56_VERSION) {
698 fread_endian<int>(fp, proj->AttractiveForce);
699 fread_endian<int>(fp, proj->AttractiveForceType);
700 fread_endian<int>(fp, proj->AttractiveForceObjects);
701 fread_endian<int>(fp, proj->AttractiveForceClasses);
702 fread_endian<int>(fp, proj->AttractiveForceRadius);
703 fread_endian<bool>(fp, proj->AttractiveForceThroughWalls);
704
705 fread_endian<int>(fp, proj->Width);
706 fread_endian<int>(fp, proj->Height);
707 }
708 else {
709 proj->AttractiveForce = 0;
710 proj->AttractiveForceType = ATT_GRAVITY;
711 proj->AttractiveForceObjects = ATO_NONE;
712 proj->AttractiveForceClasses = 0;
713 proj->AttractiveForceRadius = 0;
714 proj->AttractiveForceThroughWalls = true;
715
716 if(proj->Type == PRJ_PIXEL)
717 proj->Width = proj->Height = 2;
718 else
719 proj->Width = proj->Height = 4;
720 }
721
722 proj->Trail.Proj.Proj = NULL;
723 proj->GeneralSpawnInfo.Proj = NULL;
724 proj->Hit.Projectiles = false;
725 proj->Timer.Projectiles = false;
726 proj->PlyHit.Projectiles = false;
727 proj->Animating = false;
728 proj->Rotating = false;
729 proj->RotIncrement = 0;
730 proj->Timer.Shake = 0;
731
732 switch(proj->Type) {
733 case PRJ_POLYGON: {
734 proj->polygon.clear();
735 Uint32 NumPoints = 0;
736 fread_endian<Uint32>(fp, NumPoints);
737 proj->polygon.startPointAdding();
738 for(Uint32 i = 0; i < NumPoints; ++i) {
739 VectorD2<int> p;
740 fread_endian<int>(fp, p.x);
741 fread_endian<int>(fp, p.y);
742 proj->polygon.addPoint(p);
743 }
744 proj->polygon.endPointAdding();
745 }
746 // fallthrough to read color
747 case PRJ_RECT:
748 case PRJ_CIRCLE:
749 case PRJ_PIXEL:
750 {
751 Uint32 NumColours = 0;
752 fread_endian<int>(fp, NumColours);
753 proj->Colour.resize(NumColours);
754
755 for(size_t i = 0; i < NumColours; ++i) {
756 if(Header.Version <= GS_LX56_VERSION) {
757 int color[3] = {0,0,0};
758 fread_endian<int>(fp, color[0]);
759 fread_endian<int>(fp, color[1]);
760 fread_endian<int>(fp, color[2]);
761 proj->Colour[i] = Color(color[0],color[1],color[2]);
762 } else {
763 fread_compat(proj->Colour[i].r,1,1,fp);
764 fread_compat(proj->Colour[i].g,1,1,fp);
765 fread_compat(proj->Colour[i].b,1,1,fp);
766 fread_compat(proj->Colour[i].a,1,1,fp);
767 }
768 }
769 break;
770 }
771 case PRJ_IMAGE:
772 proj->ImgFilename = readString(fp);
773
774 if(!bDedicated) {
775 proj->bmpImage = LoadGSImage(sDirectory, proj->ImgFilename);
776 if(!proj->bmpImage)
777 modLog("Could not open image '" + proj->ImgFilename + "'");
778 else
779 proj->bmpShadow = GenerateShadowSurface(proj->bmpImage);
780 }
781
782 fread_endian<int>(fp, proj->Rotating);
783 fread_compat(proj->RotIncrement, sizeof(int), 1, fp);
784 EndianSwap(proj->RotIncrement);
785 fread_compat(proj->RotSpeed,sizeof(int),1,fp);
786 EndianSwap(proj->RotSpeed);
787 fread_endian<int>(fp, proj->UseAngle);
788 fread_endian<int>(fp, proj->UseSpecAngle);
789 if(proj->UseAngle || proj->UseSpecAngle)
790 {
791 fread_compat(proj->AngleImages,sizeof(int),1,fp);
792 EndianSwap(proj->AngleImages);
793 }
794 fread_endian<int>(fp, proj->Animating);
795 if(proj->Animating) {
796 fread_compat(proj->AnimRate,sizeof(float),1,fp);
797 EndianSwap(proj->AnimRate);
798 fread_compat(proj->AnimType,sizeof(int),1,fp);
799 EndianSwap(proj->AnimType);
800 }
801 break;
802
803 case __PRJ_LBOUND: case __PRJ_UBOUND: errors << "LoadProjectile: PRJ BOUND" << endl;
804 }
805
806
807 //
808 // Hit
809 //
810 if(Header.Version <= GS_LX56_VERSION) {
811 fread_compat(proj->Hit.Type,sizeof(int),1,fp);
812 EndianSwap(proj->Hit.Type);
813
814 // Hit::Explode
815 if(proj->Hit.Type == PJ_EXPLODE) {
816 fread_compat(proj->Hit.Damage, sizeof(int),1,fp);
817 EndianSwap(proj->Hit.Damage);
818 fread_endian<int>(fp, proj->Hit.Projectiles);
819 fread_endian<int>(fp, proj->Hit.UseSound);
820 fread_compat(proj->Hit.Shake, sizeof(int),1,fp);
821 EndianSwap(proj->Hit.Shake);
822
823 if(proj->Hit.UseSound) {
824 proj->Hit.SndFilename = readString(fp);
825 }
826 }
827
828 // Hit::Bounce
829 if(proj->Hit.Type == PJ_BOUNCE) {
830 fread_compat(proj->Hit.BounceCoeff, sizeof(float), 1, fp);
831 EndianSwap(proj->Hit.BounceCoeff);
832 fread_compat(proj->Hit.BounceExplode, sizeof(int), 1, fp);
833 EndianSwap(proj->Hit.BounceExplode);
834 }
835
836 // Hit::Carve
837 if(proj->Hit.Type == PJ_CARVE) {
838 fread_compat(proj->Hit.Damage, sizeof(int),1,fp);
839 EndianSwap(proj->Hit.Damage);
840 }
841
842 if(!bDedicated && proj->Hit.UseSound) {
843 // Load the sample
844 proj->Hit.Sound = LoadGSSample(sDirectory,proj->Hit.SndFilename);
845
846 if(proj->Hit.Sound == NULL) {
847 proj->Hit.UseSound = false;
848 modLog("Could not open sound '" + proj->Hit.SndFilename + "'");
849 }
850 }
851 }
852 else { // newer GS version
853 proj->Hit.read(this, fp);
854 }
855
856
857
858
859 //
860 // Timer
861 //
862 if(proj->Timer.Time > 0) {
863 if(Header.Version <= GS_LX56_VERSION) {
864 fread_compat(proj->Timer.Type,sizeof(int),1,fp);
865 EndianSwap(proj->Timer.Type);
866 if(proj->Timer.Type == PJ_EXPLODE) {
867 fread_compat(proj->Timer.Damage,sizeof(int),1,fp);
868 EndianSwap(proj->Timer.Damage);
869 fread_endian<int>(fp, proj->Timer.Projectiles);
870 fread_compat(proj->Timer.Shake,sizeof(int),1,fp);
871 EndianSwap(proj->Timer.Shake);
872 }
873 }
874 else {
875 proj->Timer.read(this, fp);
876 }
877 }
878
879
880
881 //
882 // Player hit
883 //
884 if(Header.Version <= GS_LX56_VERSION) {
885 fread_compat(proj->PlyHit.Type,sizeof(int),1,fp);
886 EndianSwap(proj->PlyHit.Type);
887
888 // PlyHit::Explode || PlyHit::Injure
889 if(proj->PlyHit.Type == PJ_INJURE || proj->PlyHit.Type == PJ_EXPLODE) {
890 fread_endian<int>(fp, proj->PlyHit.Damage);
891 fread_endian<int>(fp, proj->PlyHit.Projectiles);
892 }
893
894 // PlyHit::Bounce
895 if(proj->PlyHit.Type == PJ_BOUNCE) {
896 fread_compat(proj->PlyHit.BounceCoeff, sizeof(float), 1, fp);
897 EndianSwap(proj->PlyHit.BounceCoeff);
898 }
899 }
900 else {
901 proj->PlyHit.read(this, fp);
902 }
903
904 if(Header.Version <= GS_LX56_VERSION) {
905 // NOTE: this is obsolete, it is used nowhere
906
907 //
908 // Explode
909 //
910 fread_compat(proj->Exp.Type, sizeof(int), 1, fp);
911 EndianSwap(proj->Exp.Type);
912 fread_compat(proj->Exp.Damage, sizeof(int), 1, fp);
913 EndianSwap(proj->Exp.Damage);
914 fread_endian<int>(fp, proj->Exp.Projectiles);
915 fread_endian<int>(fp, proj->Exp.UseSound);
916 if(proj->Exp.UseSound) {
917 proj->Exp.SndFilename = readString(fp);
918 }
919
920 //
921 // Touch
922 //
923 fread_compat(proj->Tch.Type, sizeof(int), 1, fp);
924 EndianSwap(proj->Tch.Type);
925 fread_compat(proj->Tch.Damage, sizeof(int), 1, fp);
926 EndianSwap(proj->Tch.Damage);
927 fread_endian<int>(fp, proj->Tch.Projectiles);
928 fread_endian<int>(fp, proj->Tch.UseSound);
929 if(proj->Tch.UseSound) {
930 proj->Tch.SndFilename = readString(fp);
931 }
932 }
933
934 if(Header.Version > GS_LX56_VERSION) {
935 Uint32 projHitC = 0;
936 fread_endian<Uint32>(fp, projHitC);
937 proj->actions.resize(projHitC);
938 for(Uint32 i = 0; i < projHitC; ++i) {
939 proj->actions[i].read(this, fp);
940 }
941 }
942
943 if(Header.Version > GS_LX56_VERSION) {
944 proj->GeneralSpawnInfo.read(this, fp);
945 }
946 else if(proj->Timer.Projectiles || proj->Hit.Projectiles || proj->PlyHit.Projectiles || proj->Exp.Projectiles ||
947 proj->Tch.Projectiles) {
948 fread_endian<int>(fp, proj->GeneralSpawnInfo.Useangle);
949 fread_compat(proj->GeneralSpawnInfo.Angle, sizeof(int), 1, fp);
950 EndianSwap(proj->GeneralSpawnInfo.Angle);
951 fread_compat(proj->GeneralSpawnInfo.Amount, sizeof(int), 1, fp);
952 EndianSwap(proj->GeneralSpawnInfo.Amount);
953 fread_compat(proj->GeneralSpawnInfo.Spread, sizeof(float), 1, fp);
954 EndianSwap(proj->GeneralSpawnInfo.Spread);
955 fread_compat(proj->GeneralSpawnInfo.Speed, sizeof(int), 1, fp);
956 EndianSwap(proj->GeneralSpawnInfo.Speed);
957 fread_compat(proj->GeneralSpawnInfo.SpeedVar, sizeof(float), 1, fp);
958 EndianSwap(proj->GeneralSpawnInfo.SpeedVar);
959
960 proj->GeneralSpawnInfo.Proj = LoadProjectile(fp);
961 }
962
963
964 // Projectile trail
965 if(proj->Trail.Type == TRL_PROJECTILE) {
966
967 fread_endian<int>(fp, proj->Trail.Proj.UseParentVelocityForSpread);
968 fread_endian<float>(fp, proj->Trail.Delay);
969 // Change from milli-seconds to seconds
970 proj->Trail.Delay /= 1000.0f;
971
972 if(Header.Version <= GS_LX56_VERSION) {
973 fread_compat(proj->Trail.Proj.Amount, sizeof(int), 1, fp);
974 EndianSwap(proj->Trail.Proj.Amount);
975 fread_compat(proj->Trail.Proj.Speed, sizeof(int), 1, fp);
976 EndianSwap(proj->Trail.Proj.Speed);
977 fread_compat(proj->Trail.Proj.SpeedVar, sizeof(float), 1, fp);
978 EndianSwap(proj->Trail.Proj.SpeedVar);
979 fread_compat(proj->Trail.Proj.Spread, sizeof(float), 1, fp);
980 EndianSwap(proj->Trail.Proj.Spread);
981
982 proj->Trail.Proj.Proj = LoadProjectile(fp);
983 }
984 else { // new GS versions
985 proj->Trail.Proj.read(this, fp);
986 }
987
988 }
989
990 return proj;
991 }
992
993 ///////////////////
994 // Load an image
LoadGSImage(const std::string & dir,const std::string & filename)995 SDL_Surface * CGameScript::LoadGSImage(const std::string& dir, const std::string& filename)
996 {
997 if(bDedicated) return NULL;
998
999 SmartPointer<SDL_Surface> img = NULL;
1000
1001 // First, check the gfx directory in the mod dir
1002 img = LoadGameImage(dir + "/gfx/" + filename, true);
1003 if(img.get()) {
1004 SetColorKey(img.get());
1005 CachedImages.push_back(img);
1006 return img.get();
1007 }
1008
1009 // Check the gfx directory in the data dir
1010 img = LoadGameImage("data/gfx/" + filename, true);
1011 if(img.get())
1012 {
1013 SetColorKey(img.get());
1014 CachedImages.push_back(img);
1015 }
1016 return img.get();
1017 }
1018
1019 ///////////////////
1020 // Load a sample
LoadGSSample(const std::string & dir,const std::string & filename)1021 SoundSample * CGameScript::LoadGSSample(const std::string& dir, const std::string& filename)
1022 {
1023 if(bDedicated) return NULL;
1024
1025 SmartPointer<SoundSample> smp = NULL;
1026
1027 // First, check the sfx directory in the mod dir
1028 smp = LoadSample(dir + "/sfx/" + filename, 10);
1029
1030 if(smp.get())
1031 {
1032 CachedSamples.push_back(smp);
1033 return smp.get();
1034 }
1035
1036 // Check the sounds directory in the data dir
1037 smp = LoadSample("data/sounds/" + filename, 10);
1038 if(smp.get())
1039 CachedSamples.push_back(smp);
1040 return smp.get();
1041 }
1042
1043
1044
1045 ///////////////////
1046 // Find a weapon based on its name
FindWeapon(const std::string & name)1047 const weapon_t *CGameScript::FindWeapon(const std::string& name)
1048 {
1049 if(!Weapons) return NULL;
1050 if(name == "") return NULL;
1051
1052 // Go through each weapon
1053 weapon_t *wpn = Weapons;
1054 for(int n=0;n<NumWeapons;n++,wpn++) {
1055
1056 if(stringcaseequal(wpn->Name, name))
1057 return wpn;
1058 }
1059
1060 return NULL;
1061 }
1062
1063
1064 ///////////////////
1065 // Returns true if the weapon is in the game script
weaponExists(const std::string & szName)1066 bool CGameScript::weaponExists(const std::string& szName)
1067 {
1068 // Go through each weapon
1069 weapon_t *wpn = Weapons;
1070 for(int n=0;n<NumWeapons;n++,wpn++) {
1071
1072 if(stringcasecmp(wpn->Name, szName) == 0)
1073 return true;
1074 }
1075
1076 // Not found
1077 return false;
1078 }
1079
1080
1081
1082 // Helper function
GetProjSize(proj_t * prj)1083 static size_t GetProjSize(proj_t *prj)
1084 {
1085 if (prj)
1086 return prj->Exp.SndFilename.size() + prj->filename.size() +
1087 prj->Hit.SndFilename.size() + prj->ImgFilename.size() +
1088 prj->Tch.SndFilename.size() +
1089 /*GetSurfaceMemorySize(prj->bmpImage.get()) + */
1090 sizeof(proj_t);
1091 else
1092 return 0;
1093 }
1094
1095 //////////////////
1096 // Returns the memory occupied by this gamescript
GetMemorySize()1097 size_t CGameScript::GetMemorySize()
1098 {
1099 size_t res = sizeof(CGameScript) + sDirectory.size();
1100 weapon_t *it = Weapons;
1101 for (int i = 0; i < NumWeapons; i++, it++) {
1102 res += sizeof(weapon_t) + sizeof(SoundSample);
1103 res += it->SndFilename.size();
1104 }
1105 for(Projectiles::iterator i = projectiles.begin(); i != projectiles.end(); ++i) {
1106 res += GetProjSize(i->second);
1107 }
1108
1109 return res;
1110 }
1111
1112
1113 ///////////////////
1114 // Shutdown the game script
Shutdown()1115 void CGameScript::Shutdown()
1116 {
1117 loaded = false;
1118 needCollisionInfo = false;
1119
1120 // Close the log file
1121 if(pModLog) {
1122 fclose(pModLog);
1123 pModLog = NULL;
1124 }
1125
1126 for(Projectiles::iterator i = projectiles.begin(); i != projectiles.end(); ++i) {
1127 ShutdownProjectile(i->second);
1128 }
1129 projectiles.clear();
1130 savedProjs.clear();
1131 projFileIndexes.clear();
1132
1133 if(Weapons)
1134 delete[] Weapons;
1135 Weapons = NULL;
1136 }
1137
1138
1139 ///////////////////
1140 // Shutdown a projectile
ShutdownProjectile(proj_t * prj)1141 void CGameScript::ShutdownProjectile(proj_t *prj)
1142 {
1143 if(prj) {
1144 delete prj;
1145 }
1146 }
1147
1148
1149 ///////////////////
1150 // Check if a file is a valid liero game script
CheckFile(const std::string & dir,std::string & name,bool abs_filename)1151 bool CGameScript::CheckFile(const std::string& dir, std::string& name, bool abs_filename)
1152 {
1153 name = "";
1154
1155 // Open it
1156 FILE *fp = NULL;
1157 if(abs_filename) {
1158 std::string filename;
1159 // we still need to add "/script.lgs" and then do an exact filename search
1160 if(GetExactFileName(dir + "/script.lgs", filename))
1161 fp = fopen(Utf8ToSystemNative(filename).c_str(), "rb");
1162 } else
1163 fp = OpenGameFile(dir + "/script.lgs", "rb");
1164
1165 if(fp == NULL) {
1166 // try source gamescript
1167
1168 std::string filename = dir + "/main.txt";
1169 if(abs_filename) {
1170 if(!GetExactFileName(dir + "/main.txt", filename))
1171 return false;
1172 } else {
1173 if(!IsFileAvailable(filename))
1174 return false;
1175 }
1176
1177 ReadString(filename,"General","ModName", name,"untitled", abs_filename);
1178 return true;
1179 }
1180
1181 // Header
1182 gs_header_t head;
1183 memset(&head,0,sizeof(gs_header_t));
1184 fread_compat(head,sizeof(gs_header_t),1,fp);
1185 fclose(fp);
1186
1187 EndianSwap(head.Version);
1188 // for security
1189 fix_markend(head.ID);
1190 fix_markend(head.ModName);
1191
1192 // Check ID
1193 if(strcmp(head.ID,"Liero Game Script") != 0) {
1194 warnings << "GS:CheckFile: WARNING: " << dir << "/script.lgs is not a Liero game script";
1195 warnings << " (but \"" << head.ID << "\" instead)" << endl;
1196 return false;
1197 }
1198
1199 // Check version
1200 if(head.Version < GS_FIRST_SUPPORTED_VERSION || head.Version > GS_VERSION) {
1201 warnings << "GS:CheckFile: WARNING: " << dir << "/script.lgs has the wrong version";
1202 warnings << " (" << (unsigned)head.Version << ", required is in the range ";
1203 warnings << "[" << GS_FIRST_SUPPORTED_VERSION << "," << GS_VERSION << "])" << endl;
1204 return false;
1205 }
1206
1207 name = head.ModName;
1208 return true;
1209 }
1210
1211
1212 ///////////////////
1213 // Return an error message based on code
getError(int code)1214 std::string CGameScript::getError(int code)
1215 {
1216 std::string text = "Undefined error";
1217
1218 switch(code) {
1219
1220 case GSE_MEM:
1221 text = "Out of memory";
1222 break;
1223
1224 case GSE_VERSION:
1225 text = "Incorrect version";
1226 break;
1227
1228 case GSE_FILE:
1229 text = "Could not open file";
1230 break;
1231
1232 case GSE_BAD:
1233 text = "Bad file format";
1234 break;
1235 }
1236
1237 return text;
1238 }
1239
1240
1241 ///////////////////
1242 // Write info to a mod log file
modLog(const std::string & text)1243 void CGameScript::modLog(const std::string& text)
1244 {
1245 notes << "modLog: " << text << endl;
1246
1247 if(!pModLog) {
1248 pModLog = OpenGameFile("modlog.txt","wt");
1249 if(!pModLog)
1250 return;
1251 fprintf(pModLog,"Log file for mod:\n%s\n--------------------------------\n",Header.ModName);
1252 }
1253
1254 if (text.size() != 0)
1255 fprintf(pModLog,"%s\n", text.c_str());
1256 }
1257
1258 // Not needed with new caching system - game script won't ever change it's state during game
1259 /*
1260 ///////////////////
1261 // Copies infor from anothe gamescript
1262 void CGameScript::CopyFrom(CGameScript *cg)
1263 {
1264 sDirectory = cg->sDirectory;
1265 Header = cg->Header;
1266 NumWeapons = cg->NumWeapons;
1267
1268 // HINT: only a pointer is copied here, because the weapon info does not change
1269 // so it would be wasting of memory if we copied the whole list
1270 Weapons = cg->Weapons;
1271
1272 Worm = cg->Worm;
1273 RopeLength = cg->RopeLength;
1274 RestLength = cg->RestLength;
1275 Strength = cg->Strength;
1276 }
1277 */
1278
SmartPointer_ObjectDeinit(CGameScript * obj)1279 template <> void SmartPointer_ObjectDeinit<CGameScript> ( CGameScript * obj )
1280 {
1281 delete obj;
1282 }
1283
1284
1285
1286
1287
1288
1289 static IniReader::KeywordList compilerKeywords;
1290
InitDefaultCompilerKeywords()1291 void CGameScript::InitDefaultCompilerKeywords()
1292 {
1293 if (compilerKeywords.empty()) {
1294 // Add some keywords
1295 // IMPORTANT: Every keyword (key) need to be different.
1296 compilerKeywords["WPN_PROJECTILE"] = WPN_PROJECTILE;
1297 compilerKeywords["WPN_SPECIAL"] = WPN_SPECIAL;
1298 compilerKeywords["WPN_BEAM"] = WPN_BEAM;
1299
1300 compilerKeywords["WCL_AUTOMATIC"] = WCL_AUTOMATIC;
1301 compilerKeywords["WCL_POWERGUN"] = WCL_POWERGUN;
1302 compilerKeywords["WCL_GRENADE"] = WCL_GRENADE;
1303 compilerKeywords["WCL_MISSILE"] = WCL_MISSILE;
1304 compilerKeywords["WCL_CLOSERANGE"] = WCL_CLOSERANGE;
1305
1306 compilerKeywords["PRJ_PIXEL"] = PRJ_PIXEL;
1307 compilerKeywords["PRJ_IMAGE"] = PRJ_IMAGE;
1308 compilerKeywords["PRJ_CIRCLE"] = PRJ_CIRCLE;
1309 compilerKeywords["PRJ_POLYGON"] = PRJ_POLYGON;
1310 compilerKeywords["PRJ_RECT"] = PRJ_RECT;
1311
1312 // action types
1313 compilerKeywords["Bounce"] = PJ_BOUNCE;
1314 compilerKeywords["Explode"] = PJ_EXPLODE;
1315 compilerKeywords["Injure"] = PJ_INJURE;
1316 compilerKeywords["InjureProj"] = PJ_INJUREPROJ;
1317 compilerKeywords["Carve"] = PJ_CARVE;
1318 compilerKeywords["Dirt"] = PJ_DIRT;
1319 compilerKeywords["GreenDirt"] = PJ_GREENDIRT;
1320 compilerKeywords["Disappear"] = PJ_DISAPPEAR;
1321 compilerKeywords["Nothing"] = PJ_NOTHING;
1322 compilerKeywords["Disappear2"] = PJ_DISAPPEAR2;
1323 compilerKeywords["GoThrough"] = PJ_GOTHROUGH;
1324 compilerKeywords["PlaySound"] = PJ_PLAYSOUND;
1325 compilerKeywords["InjureWorm"] = PJ_INJUREWORM;
1326 compilerKeywords["ChangeRadius"] = PJ_ChangeRadius;
1327 compilerKeywords["OverwriteOwnSpeed"] = PJ_OverwriteOwnSpeed;
1328 compilerKeywords["MultiplyOwnSpeed"] = PJ_MultiplyOwnSpeed;
1329 compilerKeywords["DiffOwnSpeed"] = PJ_DiffOwnSpeed;
1330 compilerKeywords["OverwriteTargetSpeed"] = PJ_OverwriteTargetSpeed;
1331 compilerKeywords["MultiplyTargetSpeed"] = PJ_MultiplyTargetSpeed;
1332 compilerKeywords["DiffTargetSpeed"] = PJ_DiffTargetSpeed;
1333 compilerKeywords["HeadingToNextWorm"] = PJ_HeadingToNextWorm;
1334 compilerKeywords["HeadingToOwner"] = PJ_HeadingToOwner;
1335 compilerKeywords["HeadingToNextOtherWorm"] = PJ_HeadingToNextOtherWorm;
1336 compilerKeywords["HeadingToNextEnemyWorm"] = PJ_HeadingToNextEnemyWorm;
1337 compilerKeywords["HeadingToNextTeamMate"] = PJ_HeadingToNextTeamMate;
1338 compilerKeywords["HeadTargetToUs"] = PJ_HeadTargetToUs;
1339
1340 // event types
1341 compilerKeywords["Timer"] = Proj_Event::PET_TIMER;
1342 compilerKeywords["ProjHit"] = Proj_Event::PET_PROJHIT;
1343 compilerKeywords["WormHit"] = Proj_Event::PET_WORMHIT;
1344 compilerKeywords["TerrainHit"] = Proj_Event::PET_TERRAINHIT;
1345 compilerKeywords["Death"] = Proj_Event::PET_DEATH;
1346 compilerKeywords["Fallback"] = Proj_Event::PET_FALLBACK;
1347
1348 // trail types
1349 compilerKeywords["TRL_NONE"] = TRL_NONE;
1350 compilerKeywords["TRL_SMOKE"] = TRL_SMOKE;
1351 compilerKeywords["TRL_CHEMSMOKE"] = TRL_CHEMSMOKE;
1352 compilerKeywords["TRL_PROJECTILE"] = TRL_PROJECTILE;
1353 compilerKeywords["TRL_DOOMSDAY"] = TRL_DOOMSDAY;
1354 compilerKeywords["TRL_EXPLOSIVE"] = TRL_EXPLOSIVE;
1355
1356 compilerKeywords["SPC_JETPACK"] = SPC_JETPACK;
1357
1358 compilerKeywords["ANI_ONCE"] = ANI_ONCE;
1359 compilerKeywords["ANI_LOOP"] = ANI_LOOP;
1360 compilerKeywords["ANI_PINGPONG"] = ANI_PINGPONG;
1361
1362 compilerKeywords["ATO_NONE"] = ATO_NONE;
1363 compilerKeywords["ATO_PLAYERS"] = ATO_PLAYERS;
1364 compilerKeywords["ATO_PROJECTILES"] = ATO_PROJECTILES;
1365 compilerKeywords["ATO_ROPE"] = ATO_ROPE;
1366 compilerKeywords["ATO_BONUSES"] = ATO_BONUSES;
1367 compilerKeywords["ATO_ALL"] = ATO_ALL;
1368
1369 compilerKeywords["ATC_NONE"] = ATC_NONE;
1370 compilerKeywords["ATC_OWNER"] = ATC_OWNER;
1371 compilerKeywords["ATC_ENEMY"] = ATC_ENEMY;
1372 compilerKeywords["ATC_TEAMMATE"] = ATC_TEAMMATE;
1373 compilerKeywords["ATC_ALL"] = ATC_ALL;
1374
1375 compilerKeywords["ATT_GRAVITY"] = ATT_GRAVITY;
1376 compilerKeywords["ATT_CONSTANT"] = ATT_CONSTANT;
1377 compilerKeywords["ATT_LINEAR"] = ATT_LINEAR;
1378 compilerKeywords["ATT_QUADRATIC"] = ATT_QUADRATIC;
1379
1380 compilerKeywords["true"] = true;
1381 compilerKeywords["false"] = false;
1382 }
1383 }
1384
1385 ///////////////////
1386 // Compile
Compile(const std::string & dir)1387 bool CGameScript::Compile(const std::string& dir)
1388 {
1389 Shutdown();
1390
1391 CGameScript* Game = this;
1392
1393 InitDefaultCompilerKeywords();
1394 IniReader ini(dir + "/Main.txt", compilerKeywords);
1395
1396 if (!ini.Parse()) {
1397 errors << "Error while parsing the gamescript " << dir << endl;
1398 return false;
1399 }
1400
1401
1402
1403 sDirectory = dir;
1404
1405 int num,n;
1406
1407 std::string modname;
1408 ini.ReadString("General","ModName", modname,"untitled");
1409 fix_strncpy(Header.ModName, modname.c_str());
1410
1411 notes << "Compiling '" << modName() << "'" << endl;
1412
1413 ini.ReadInteger("Weapons","NumWeapons",&num,0);
1414
1415
1416 // Weapons
1417 Game->initNewWeapons(num);
1418
1419
1420 // Compile the weapons
1421 for(n=0;n<Game->GetNumWeapons();n++) {
1422 std::string wpn = "Weapon" + itoa(n+1);
1423
1424 std::string weap;
1425 ini.ReadString("Weapons", wpn, weap, "");
1426
1427 if(!CompileWeapon(dir, weap, n))
1428 return false;
1429 }
1430
1431 // Compile the extra stuff
1432 CompileExtra(ini);
1433
1434 loaded = true;
1435
1436 return true;
1437 }
1438
1439
1440 ///////////////////
1441 // Compile a weapon
CompileWeapon(const std::string & dir,const std::string & weapon,int id)1442 bool CGameScript::CompileWeapon(const std::string& dir, const std::string& weapon, int id)
1443 {
1444 CGameScript* Game = this;
1445
1446 weapon_t *Weap = Game->Weapons+id;
1447 IniReader ini(dir + "/" + weapon, compilerKeywords);
1448 if (!ini.Parse()) {
1449 errors << "Error while parsing weapon file " << weapon << endl;
1450 return false;
1451 }
1452
1453 Weap->ID = id;
1454 Weap->Proj.Proj = NULL;
1455 Weap->UseSound = false;
1456 Weap->Special = SPC_NONE;
1457 Weap->Type = WPN_PROJECTILE;
1458 Weap->Proj.Proj = NULL;
1459 Weap->LaserSight = false;
1460
1461 ini.ReadString("General", "Name", Weap->Name, "");
1462 notes << " Compiling Weapon '" << Weap->Name << "'" << endl;
1463
1464 ini.ReadKeyword("General", "Type", (int *)&Weap->Type, WPN_PROJECTILE);
1465
1466
1467
1468 // Special Weapons
1469 if(Weap->Type == WPN_SPECIAL) {
1470
1471 ini.ReadKeyword("General", "Special", (int*)&Weap->Special, SPC_NONE);
1472
1473 // If it is a special weapon, read the values depending on the special weapon
1474 // We don't bother with the 'normal' values
1475 switch(Weap->Special) {
1476 // Jetpack
1477 case SPC_JETPACK:
1478 CompileJetpack(ini, Weap);
1479 break;
1480
1481 default:
1482 notes << " Error: Unknown special type" << endl;
1483 }
1484 return true;
1485 }
1486
1487
1488 // Beam Weapons
1489 if(Weap->Type == WPN_BEAM) {
1490
1491 CompileBeam(ini, Weap);
1492 return true;
1493 }
1494
1495
1496 // Projectile Weapons
1497 ini.ReadKeyword("General","Class",(int*)&Weap->Class,WCL_AUTOMATIC);
1498 ini.ReadInteger("General","Recoil",&Weap->Recoil,0);
1499 ini.ReadFloat("General","Recharge",&Weap->Recharge,0); Weap->Recharge /= 10.0f;
1500 ini.ReadFloat("General","Drain",&Weap->Drain,0);
1501 ini.ReadFloat("General","ROF",&Weap->ROF,0); Weap->ROF /= 1000.0f;
1502 ini.ReadKeyword("General", "LaserSight", &Weap->LaserSight, false);
1503 if(ini.ReadString("General","Sound",Weap->SndFilename,"")) {
1504 Weap->UseSound = true;
1505
1506 if(!bDedicated) {
1507 // Load the sample
1508 Weap->smpSample = LoadGSSample(dir,Weap->SndFilename);
1509 }
1510 }
1511
1512 Weap->Proj.readFromIni(this, dir, ini, "Projectile");
1513
1514 if(Weap->Proj.UseParentVelocityForSpread) {
1515 warnings << "UseProjVelocity is set in Projectile-section (" << weapon << "); this was not supported in LX56 thus we ignore it" << endl;
1516 Weap->Proj.UseParentVelocityForSpread = false;
1517 }
1518
1519 if(!Weap->Proj.Useangle) {
1520 warnings << "Useangle is set in Projectile-section (" << weapon << "); this was not supported in LX56 thus we ignore it" << endl;
1521 Weap->Proj.Useangle = true;
1522 }
1523
1524 if(Weap->Proj.Angle != 0) {
1525 warnings << "Angle is set in Projectile-section (" << weapon << "); this was not supported in LX56 thus we ignore it" << endl;
1526 Weap->Proj.Angle = 0;
1527 }
1528
1529 if(Weap->Proj.Proj == NULL) {
1530 warnings << "projectile not set for weapon " << weapon << endl;
1531 return false;
1532 }
1533
1534 Weap->FinalProj.readFromIni(this, dir, ini, "FinalProj");
1535
1536 return true;
1537 }
1538
1539
1540 ///////////////////
1541 // Compile a beam weapon
CompileBeam(const IniReader & ini,weapon_t * Weap)1542 void CGameScript::CompileBeam(const IniReader& ini, weapon_t *Weap)
1543 {
1544 ini.ReadInteger("General","Recoil",&Weap->Recoil,0);
1545 ini.ReadFloat("General","Recharge",&Weap->Recharge,0); Weap->Recharge /= 10.0f;
1546 ini.ReadFloat("General","Drain",&Weap->Drain,0);
1547 ini.ReadFloat("General","ROF",&Weap->ROF,0); Weap->ROF /= 1000.0f;
1548 if(ini.ReadString("General","Sound",Weap->SndFilename,""))
1549 Weap->UseSound = true;
1550
1551 Weap->Bm.readFromIni(ini, "Beam");
1552 }
1553
1554
1555 ///////////////////
1556 // Compile a projectile
CompileProjectile(const std::string & dir,const std::string & pfile)1557 proj_t *CGameScript::CompileProjectile(const std::string& dir, const std::string& pfile)
1558 {
1559 ProjFileMap::iterator f = projFileIndexes.find(pfile);
1560 if(f != projFileIndexes.end()) {
1561 notes << " Reuse already compiled Projectile '" << pfile << "'" << endl;
1562 return projectiles[f->second];
1563 }
1564
1565 proj_t *proj = new proj_t;
1566 if(proj == NULL)
1567 return NULL;
1568
1569 int projIndex = (int)projectiles.size();
1570 projectiles[projIndex] = proj;
1571 projFileIndexes[pfile] = projIndex;
1572
1573 // Load the projectile
1574 IniReader ini(dir + "/" + pfile, compilerKeywords);
1575 notes << " Compiling Projectile '" << pfile << "'" << endl;
1576
1577 proj->filename = pfile;
1578
1579 proj->Timer.Projectiles = false;
1580 proj->Hit.Projectiles = false;
1581 proj->PlyHit.Projectiles = false;
1582 proj->Exp.Projectiles = false;
1583 proj->Tch.Projectiles = false;
1584 proj->GeneralSpawnInfo.Proj = NULL;
1585 proj->Trail.Proj.Proj = NULL;
1586 proj->Animating = false;
1587 proj->UseCustomGravity = false;
1588
1589 proj->AttractiveForce = 0;
1590 proj->AttractiveForceType = ATT_GRAVITY;
1591 proj->AttractiveForceObjects = ATO_NONE;
1592 proj->AttractiveForceClasses = 0;
1593 proj->AttractiveForceRadius = 0;
1594 proj->AttractiveForceThroughWalls = true;
1595
1596 if (!ini.Parse()) {
1597 warnings << "projectile file " << pfile << " could not be parsed, using defaults..." << endl;
1598 return proj;
1599 }
1600
1601 ini.ReadKeyword("General","Type",(int*)&proj->Type,PRJ_PIXEL);
1602 ini.ReadFloat("General","Timer",&proj->Timer.Time,0);
1603 ini.ReadFloat("General", "TimerVar", &proj->Timer.TimeVar, 0);
1604 ini.ReadKeyword("General","Trail",(int*)&proj->Trail.Type,TRL_NONE);
1605
1606 ini.ReadInteger("General","AttractiveForce", &proj->AttractiveForce, 0);
1607 ini.ReadInteger("General","AttractiveForceRadius", &proj->AttractiveForceRadius, 0);
1608 ini.ReadKeyword("General","AttractiveForceType",(int*)&proj->AttractiveForceType,ATT_GRAVITY);
1609 ini.ReadKeywordList("General","AttractiveForceObjects",&proj->AttractiveForceObjects,0);
1610 ini.ReadKeywordList("General","AttractiveForceClasses",&proj->AttractiveForceClasses,0);
1611 ini.ReadKeyword("General","AttractiveForceThroughWalls",&proj->AttractiveForceThroughWalls,true);
1612
1613 if (ini.ReadInteger("General","Gravity",&proj->Gravity, 0))
1614 proj->UseCustomGravity = true;
1615
1616 ini.ReadFloat("General","Dampening",&proj->Dampening,1.0f);
1617
1618 if(proj->Type == PRJ_PIXEL)
1619 proj->Width = proj->Height = 2;
1620 else
1621 proj->Width = proj->Height = 4;
1622
1623 ini.ReadInteger("General", "Width", &proj->Width, proj->Width);
1624 ini.ReadInteger("General", "Height", &proj->Height, proj->Height);
1625
1626 proj->Colour.clear();
1627 proj->polygon.clear();
1628 switch(proj->Type) {
1629 case PRJ_POLYGON:
1630 proj->polygon.startPointAdding();
1631 for(size_t i = 0; ; ++i) {
1632 VectorD2<int> p;
1633 if(ini.ReadVectorD2("General", "P" + itoa((unsigned)i+1), p) ) {
1634 proj->polygon.addPoint(p);
1635 } else
1636 break;
1637 }
1638 proj->polygon.endPointAdding();
1639
1640 if(proj->polygon.getPoints().empty()) {
1641 warnings << "no points specified for PRJ_POLYGON projectile " << pfile << "; fallback to PRJ_PIXEL" << endl;
1642 proj->Type = PRJ_PIXEL;
1643 }
1644
1645 // fallthrough to read color
1646 case PRJ_RECT:
1647 case PRJ_CIRCLE:
1648 case PRJ_PIXEL:
1649 for(size_t i = 0; ; ++i) {
1650 Color col;
1651 if(ini.ReadColour("General","Colour" + itoa((unsigned)i+1), col, Color()) ) {
1652 proj->Colour.push_back(col);
1653 } else
1654 break;
1655 }
1656 if(proj->Colour.size() == 0) {
1657 warnings << "no colors specified for projectile " << pfile << ", using black" << endl;
1658 proj->Colour.push_back(Color());
1659 }
1660 break;
1661
1662 case PRJ_IMAGE:
1663 ini.ReadString("General","Image",proj->ImgFilename,"");
1664 ini.ReadKeyword("General","Rotating",&proj->Rotating,false);
1665 ini.ReadInteger("General","RotIncrement",&proj->RotIncrement,0);
1666 ini.ReadInteger("General","RotSpeed",&proj->RotSpeed,0);
1667 ini.ReadKeyword("General","UseAngle",&proj->UseAngle,0);
1668 ini.ReadKeyword("General","UseSpecAngle",&proj->UseSpecAngle,0);
1669 if(proj->UseAngle || proj->UseSpecAngle)
1670 ini.ReadInteger("General","AngleImages",&proj->AngleImages,0);
1671
1672 ini.ReadKeyword("General","Animating",&proj->Animating,0);
1673 if(proj->Animating) {
1674 ini.ReadFloat("General","AnimRate",&proj->AnimRate,0);
1675 ini.ReadKeyword("General","AnimType",(int*)&proj->AnimType,ANI_ONCE);
1676 }
1677
1678 if(!bDedicated) {
1679 proj->bmpImage = LoadGSImage(dir, proj->ImgFilename);
1680 if(!proj->bmpImage)
1681 modLog("Could not open image '" + proj->ImgFilename + "'");
1682 else
1683 proj->bmpShadow = GenerateShadowSurface(proj->bmpImage);
1684 }
1685 break;
1686
1687 case __PRJ_LBOUND: case __PRJ_UBOUND: errors << "PRJ BOUND err" << endl;
1688 }
1689
1690 // general Projectile spawn info
1691 {
1692 proj->GeneralSpawnInfo.readFromIni(this, dir, ini, "Projectile");
1693
1694 if(proj->GeneralSpawnInfo.UseParentVelocityForSpread) {
1695 warnings << "UseProjVelocity is set in Projectile-section (" << pfile << "); this was not supported in LX56 thus we ignore it" << endl;
1696 proj->GeneralSpawnInfo.UseParentVelocityForSpread = false;
1697 }
1698 }
1699
1700 proj->Hit.readFromIni(this, dir, ini, "Hit");
1701
1702 if(proj->Hit.needGeneralSpawnInfo() && !proj->GeneralSpawnInfo.isSet()) {
1703 warnings << dir << "/" << pfile << ": Hit section wants to spawn projectiles but there is no spawning information" << endl;
1704 proj->Hit.Projectiles = false;
1705 }
1706
1707 // Timer
1708 if(proj->Timer.Time > 0) {
1709 proj->Timer.readFromIni(this, dir, ini, "Time");
1710
1711 if(proj->Timer.needGeneralSpawnInfo() && !proj->GeneralSpawnInfo.isSet()) {
1712 warnings << dir << "/" << pfile << ": Timer section wants to spawn projectiles but there is no spawning information" << endl;
1713 proj->Timer.Projectiles = false;
1714 }
1715 }
1716
1717 // Player hit
1718 proj->PlyHit.Type = PJ_INJURE;
1719 proj->PlyHit.readFromIni(this, dir, ini, "PlayerHit");
1720
1721 if(proj->PlyHit.Shake != 0) {
1722 warnings << "projectile " << ini.getFileName() << " has PlayerHit.Shake != 0 which was not supported earlier. this is ignored" << endl;
1723 proj->PlyHit.Shake = 0;
1724 }
1725
1726 if(proj->PlyHit.BounceExplode != 0) {
1727 warnings << "projectile " << ini.getFileName() << " has PlayerHit.BounceExplode != 0 which was not supported earlier. this is ignored" << endl;
1728 proj->PlyHit.BounceExplode = 0;
1729 }
1730
1731 // If PJ_PLAYSOUND is set, we support it because PJ_PLAYSOUND didn't existed earlier.
1732 // But you could also just use a custom action to play the sound.
1733 if(proj->PlyHit.UseSound && proj->PlyHit.Type != PJ_PLAYSOUND) {
1734 warnings << "projectile " << ini.getFileName() << " has sound set, which was not supported earlier, thus it's ignored now" << endl;
1735 proj->PlyHit.UseSound = false;
1736 }
1737
1738 if(proj->PlyHit.needGeneralSpawnInfo() && !proj->GeneralSpawnInfo.isSet()) {
1739 warnings << dir << "/" << pfile << ": PlayerHit section wants to spawn projectiles but there is no spawning information" << endl;
1740 proj->PlyHit.Projectiles = false;
1741 }
1742
1743 /*
1744 // OnExplode
1745 ReadKeyword( file, "Explode", "Type", (int*)&proj->Exp.Type, PJ_NOTHING );
1746 ReadInteger( file, "Explode", "Damage", &proj->Exp.Damage, 0 );
1747 ReadKeyword( file, "Explode", "Projectiles",&proj->Exp.Projectiles, false );
1748 ReadInteger( file, "Explode", "Shake", &proj->Exp.Shake, 0 );
1749 proj->Exp.UseSound = false;
1750 if( ReadString(file, "Explode", "Sound", proj->Exp.SndFilename,"") ) {
1751 proj->Exp.UseSound = true;
1752 }
1753
1754 // Touch
1755 ReadKeyword( file, "Touch", "Type", (int*)&proj->Tch.Type, PJ_NOTHING );
1756 ReadInteger( file, "Touch", "Damage", &proj->Tch.Damage, 0 );
1757 ReadKeyword( file, "Touch", "Projectiles",&proj->Tch.Projectiles, false );
1758 ReadInteger( file, "Touch", "Shake", &proj->Tch.Shake, 0 );
1759 proj->Tch.UseSound = false;
1760 if( ReadString(file, "Touch", "Sound", proj->Tch.SndFilename,"") )
1761 proj->Tch.UseSound = true;
1762 */
1763
1764 {
1765 int projHitC = 0;
1766 ini.ReadInteger("General", "ActionNum", &projHitC, 0);
1767 for(int i = 0; i < projHitC; ++i) {
1768 Proj_EventAndAction act;
1769 if(!act.readFromIni(this, dir, ini, "Action" + itoa(i+1))) continue;
1770
1771 if(act.needGeneralSpawnInfo() && !proj->GeneralSpawnInfo.isSet()) {
1772 warnings << dir << "/" << pfile << ": Action" << (i+1) << " section wants to spawn projectiles but there is no spawning information" << endl;
1773 act.Projectiles = false;
1774 }
1775
1776 if(!act.hasAction()) {
1777 warnings << "section Action" << (i+1) << " (" << pfile << ") doesn't have any effect" << endl;
1778 continue;
1779 }
1780
1781 proj->actions.push_back(act);
1782 }
1783 }
1784
1785 // Projectile trail
1786 if(proj->Trail.Type == TRL_PROJECTILE) {
1787 ini.ReadFloat("ProjectileTrail", "Delay", &proj->Trail.Delay, 100); proj->Trail.Delay /= 1000.0f;
1788
1789 // we have some other default values here
1790 proj->Trail.Proj.Amount = 1;
1791 proj->Trail.Proj.Speed = 100;
1792
1793 proj->Trail.Proj.readFromIni(this, dir, ini, "ProjectileTrail");
1794
1795 if(proj->Trail.Proj.Useangle) {
1796 warnings << "Useangle is set in ProjectileTrail-section (" << pfile << "); this was not supported in LX56 thus we ignore it" << endl;
1797 proj->Trail.Proj.Useangle = false;
1798 }
1799
1800 if(proj->Trail.Proj.Angle != 0) {
1801 warnings << "Angle is set in ProjectileTrail-section (" << pfile << "); this was not supported in LX56 thus we ignore it" << endl;
1802 proj->Trail.Proj.Useangle = false;
1803 }
1804
1805 if(proj->Trail.Type == TRL_PROJECTILE && !proj->Trail.Proj.isSet()) {
1806 warnings << dir << "/" << pfile << ": ProjectileTrail section wants to spawn projectiles but there is no spawning information" << endl;
1807 proj->Trail.Type = TRL_NONE;
1808 }
1809 }
1810
1811 return proj;
1812 }
1813
1814
1815 ///////////////////
1816 // Compile the extra stuff
CompileExtra(const IniReader & ini)1817 bool CGameScript::CompileExtra(const IniReader& ini)
1818 {
1819 CGameScript* Game = this;
1820
1821 notes << " Compiling Extras" << endl;
1822
1823 // Ninja Rope
1824 notes << " Compiling Ninja Rope" << endl;
1825
1826 int ropel, restl;
1827 float strength;
1828
1829 ini.ReadInteger("NinjaRope","RopeLength",&ropel,0);
1830 ini.ReadInteger("NinjaRope","RestLength",&restl,0);
1831 ini.ReadFloat("NinjaRope","Strength",&strength,0);
1832
1833 Game->SetRopeLength(ropel);
1834 Game->SetRestLength(restl);
1835 Game->SetStrength(strength);
1836
1837
1838 // Worm
1839 notes << " Compiling Worm" << endl;
1840 gs_worm_t *wrm = &Game->Worm;
1841
1842 ini.ReadFloat( "Worm", "AngleSpeed", &wrm->AngleSpeed, 150);
1843 ini.ReadFloat( "Worm", "GroundSpeed", &wrm->GroundSpeed, 8);
1844 ini.ReadFloat( "Worm", "AirSpeed", &wrm->AirSpeed, 1);
1845 ini.ReadFloat( "Worm", "Gravity", &wrm->Gravity, 175);
1846 ini.ReadFloat( "Worm", "JumpForce", &wrm->JumpForce, -140);
1847 ini.ReadFloat( "Worm", "AirFriction", &wrm->AirFriction, 0);
1848 ini.ReadFloat( "Worm", "GroundFriction", &wrm->GroundFriction, 0.6f);
1849
1850
1851
1852
1853
1854 return true;
1855 }
1856
1857
1858 /*
1859 ===============================
1860
1861 Special items
1862
1863 ===============================
1864 */
1865
1866
1867 ///////////////////
1868 // Compile the jetpack
CompileJetpack(const IniReader & ini,weapon_t * Weap)1869 bool CGameScript::CompileJetpack(const IniReader& ini, weapon_t *Weap)
1870 {
1871 Weap->Proj.Proj = NULL;
1872
1873 ini.ReadInteger("JetPack", "Thrust", (int*)&Weap->tSpecial.Thrust, 0);
1874 ini.ReadFloat("JetPack", "Drain", &Weap->Drain, 0);
1875 ini.ReadFloat("JetPack", "Recharge", &Weap->Recharge, 0); Weap->Recharge /= 10.0f;
1876
1877 Weap->ROF = 1.0f / 1000.0f;
1878
1879 return true;
1880 }
1881
1882
readFromIni(CGameScript * gs,const std::string & dir,const IniReader & ini,const std::string & section)1883 bool Proj_SpawnInfo::readFromIni(CGameScript* gs, const std::string& dir, const IniReader& ini, const std::string& section) {
1884 ini.ReadKeyword(section, "AddParentVel", &AddParentVel, AddParentVel); // new in OLX beta9
1885 ini.ReadMatrixD2(section, "ParentVelFactor", ParentVelFactor, ParentVelFactor); // new in OLX beta9
1886 ini.ReadVectorD2(section, "PosDiff", PosDiff, PosDiff); // new in OLX beta9
1887 ini.ReadVectorD2(section, "SnapToGrid", SnapToGrid, SnapToGrid); // new in OLX beta9
1888
1889 ini.ReadKeyword(section, "Useangle", &Useangle, Useangle);
1890 ini.ReadInteger(section, "Angle", &Angle, Angle);
1891
1892 ini.ReadKeyword(section, "UseProjVelocity", &UseParentVelocityForSpread, UseParentVelocityForSpread);
1893
1894 ini.ReadInteger(section, "Amount", &Amount, Amount);
1895 ini.ReadInteger(section, "Speed", &Speed, Speed);
1896 ini.ReadFloat(section, "SpeedVar", &SpeedVar, SpeedVar);
1897 ini.ReadFloat(section, "Spread", &Spread, Spread);
1898
1899 std::string prjfile;
1900 ini.ReadString(section, "Projectile", prjfile, "");
1901 if (prjfile.size())
1902 Proj = gs->CompileProjectile(dir, prjfile);
1903 else
1904 Proj = NULL;
1905
1906 return true;
1907 }
1908
read(CGameScript * gs,FILE * fp)1909 bool Proj_SpawnInfo::read(CGameScript* gs, FILE* fp) {
1910 if(gs->GetHeader()->Version <= GS_LX56_VERSION) {
1911 errors << "Proj_SpawnInfo::read called for old GS version" << endl;
1912 return false;
1913 }
1914
1915 fread_endian<char>(fp, AddParentVel);
1916 fread_endian_M<float>(fp, ParentVelFactor);
1917 fread_endian_V<int>(fp, PosDiff);
1918 fread_endian_V<int>(fp, SnapToGrid);
1919 fread_endian<char>(fp, Useangle);
1920 fread_endian<int>(fp, Angle);
1921 fread_endian<int>(fp, Amount);
1922 fread_endian<int>(fp, Speed);
1923 fread_endian<float>(fp, SpeedVar);
1924 fread_endian<float>(fp, Spread);
1925 Proj = gs->LoadProjectile(fp);
1926 return Proj != NULL;
1927 }
1928
write(CGameScript * gs,FILE * fp)1929 bool Proj_SpawnInfo::write(CGameScript* gs, FILE* fp) {
1930 if(gs->GetHeader()->Version <= GS_LX56_VERSION) {
1931 errors << "Proj_SpawnInfo::write called for old GS version" << endl;
1932 return false;
1933 }
1934
1935 fwrite_endian<char>(fp, AddParentVel);
1936 fwrite_endian_M<float>(fp, ParentVelFactor);
1937 fwrite_endian_V<int>(fp, PosDiff);
1938 fwrite_endian_V<int>(fp, SnapToGrid);
1939 fwrite_endian<char>(fp, Useangle);
1940 fwrite_endian<int>(fp, Angle);
1941 fwrite_endian<int>(fp, Amount);
1942 fwrite_endian<int>(fp, Speed);
1943 fwrite_endian<float>(fp, SpeedVar);
1944 fwrite_endian<float>(fp, Spread);
1945 return gs->SaveProjectile(Proj, fp);
1946 }
1947
1948
readFromIni(const IniReader & ini,const std::string & section)1949 bool Wpn_Beam::readFromIni(const IniReader& ini, const std::string& section) {
1950 ini.ReadInteger(section, "Damage", &Damage, Damage);
1951 ini.ReadInteger(section, "Length", &Length, Length);
1952 ini.ReadInteger(section, "PlayerDamage", &PlyDamage, PlyDamage);
1953 ini.ReadColour(section, "Colour", Colour, Colour);
1954 ini.ReadInteger(section, "InitWidth", &InitWidth, InitWidth);
1955 ini.ReadFloat(section, "WidthIncrease", &WidthIncrease, WidthIncrease);
1956 ini.ReadKeyword(section, "DistributeDamageOverWidth", &DistributeDamageOverWidth, DistributeDamageOverWidth);
1957 return true;
1958 }
1959
read(CGameScript * gs,FILE * fp)1960 bool Wpn_Beam::read(CGameScript* gs, FILE* fp) {
1961 if(gs->GetHeader()->Version <= GS_LX56_VERSION) {
1962 int r=0,g=0,b=0;
1963 fread_endian<int>(fp, r);
1964 fread_endian<int>(fp, g);
1965 fread_endian<int>(fp, b);
1966 Colour = Color(r,g,b);
1967 }
1968 else {
1969 fread_compat(Colour.r, 1, 1, fp);
1970 fread_compat(Colour.g, 1, 1, fp);
1971 fread_compat(Colour.b, 1, 1, fp);
1972 fread_compat(Colour.a, 1, 1, fp);
1973 }
1974
1975 fread_endian<int>(fp, Damage);
1976 fread_endian<int>(fp, PlyDamage);
1977 fread_endian<int>(fp, Length);
1978
1979 if(gs->GetHeader()->Version > GS_LX56_VERSION) {
1980 fread_endian<int>(fp, InitWidth);
1981 fread_endian<float>(fp, WidthIncrease);
1982 fread_endian<char>(fp, DistributeDamageOverWidth);
1983 }
1984
1985 return true;
1986 }
1987
write(CGameScript * gs,FILE * fp)1988 bool Wpn_Beam::write(CGameScript* gs, FILE* fp) {
1989 if(gs->GetHeader()->Version <= GS_LX56_VERSION) {
1990 fwrite_endian<int>(fp, Colour.r);
1991 fwrite_endian<int>(fp, Colour.g);
1992 fwrite_endian<int>(fp, Colour.b);
1993 }
1994 else {
1995 fwrite_endian<Uint8>(fp, Colour.r);
1996 fwrite_endian<Uint8>(fp, Colour.g);
1997 fwrite_endian<Uint8>(fp, Colour.b);
1998 fwrite_endian<Uint8>(fp, Colour.a);
1999 }
2000
2001 fwrite_endian<int>(fp, Damage);
2002 fwrite_endian<int>(fp, PlyDamage);
2003 fwrite_endian<int>(fp, Length);
2004
2005 if(gs->GetHeader()->Version > GS_LX56_VERSION) {
2006 fwrite_endian<int>(fp, InitWidth);
2007 fwrite_endian<float>(fp, WidthIncrease);
2008 fwrite_endian<char>(fp, DistributeDamageOverWidth);
2009 }
2010
2011 return true;
2012 }
2013
2014
readFromIni(CGameScript * gs,const std::string & dir,const IniReader & ini,const std::string & section,int deepCounter)2015 bool Proj_Action::readFromIni(CGameScript* gs, const std::string& dir, const IniReader& ini, const std::string& section, int deepCounter) {
2016 ini.ReadKeyword(section, "Type", (int*)&Type, Type);
2017 ini.ReadKeyword(section,"Projectiles",&Projectiles,false);
2018 ini.ReadInteger(section,"Damage",&Damage,Damage);
2019 ini.ReadInteger(section,"Shake",&Shake,Shake);
2020
2021 UseSound = false;
2022 if(ini.ReadString(section,"Sound",SndFilename,"")) {
2023 UseSound = true;
2024
2025 if(!bDedicated) {
2026 // Load the sample
2027 Sound = gs->LoadGSSample(dir, SndFilename);
2028
2029 if(Sound == NULL)
2030 gs->modLog(ini.getFileName() + ":" + section + ": Could not open sound '" + SndFilename + "'");
2031 }
2032 }
2033
2034 ini.ReadFloat(section,"BounceCoeff",&BounceCoeff,BounceCoeff);
2035 ini.ReadInteger(section,"BounceExplode",&BounceExplode,BounceExplode);
2036
2037 ini.ReadFloat(section,"GoThroughSpeed",&GoThroughSpeed,GoThroughSpeed);
2038 ini.ReadVectorD2(section, "ChangeRadius", ChangeRadius, ChangeRadius);
2039
2040 switch(Type) {
2041 case PJ_OverwriteOwnSpeed:
2042 case PJ_DiffOwnSpeed:
2043 case PJ_OverwriteTargetSpeed:
2044 case PJ_DiffTargetSpeed:
2045 if(!ini.ReadVectorD2(section, "Speed", Speed, Speed)) {
2046 warnings << "Speed attribute missing in " << dir << "/" << ini.getFileName() << ":" << section << endl;
2047 }
2048 default: break;
2049 }
2050 switch(Type) {
2051 case PJ_MultiplyOwnSpeed:
2052 case PJ_MultiplyTargetSpeed:
2053 case PJ_HeadingToNextWorm:
2054 case PJ_HeadingToOwner:
2055 case PJ_HeadingToNextOtherWorm:
2056 case PJ_HeadingToNextEnemyWorm:
2057 case PJ_HeadingToNextTeamMate:
2058 case PJ_HeadTargetToUs:
2059 if(!ini.ReadMatrixD2(section, "SpeedMult", SpeedMult, SpeedMult)) {
2060 warnings << "SpeedMult attribute missing in " << dir << "/" << ini.getFileName() << ":" << section << endl;
2061 }
2062 default:
2063 // Read it as well but don't complain when missing
2064 ini.ReadMatrixD2(section, "SpeedMult", SpeedMult, SpeedMult);
2065 break;
2066 }
2067
2068 if(Projectiles)
2069 Proj.readFromIni(gs, dir, ini, section + ".Projectile");
2070
2071 std::string addActionSection;
2072 ini.ReadString(section, "Additional", addActionSection, "");
2073 TrimSpaces(addActionSection);
2074 if(addActionSection != "") {
2075 if(deepCounter > 1000) {
2076 warnings << "There is probably an additional action definition loop in " << ini.getFileName() << ":" << section << endl;
2077 return false;
2078 }
2079
2080 additionalAction = new Proj_Action();
2081 additionalAction->Type = PJ_NOTHING;
2082 if(!additionalAction->readFromIni(gs, dir, ini, addActionSection, deepCounter + 1)) {
2083 delete additionalAction; additionalAction = NULL;
2084 return false;
2085 }
2086 else if(!additionalAction->hasAction()) {
2087 warnings << "additional action " << addActionSection << "(in " << ini.getFileName() << ":" << section << ") does not have any effect" << endl;
2088 delete additionalAction; additionalAction = NULL;
2089 }
2090 }
2091 else
2092 additionalAction = NULL;
2093
2094 return true;
2095 }
2096
read(CGameScript * gs,FILE * fp)2097 bool Proj_Action::read(CGameScript* gs, FILE* fp) {
2098 if(gs->GetHeader()->Version <= GS_LX56_VERSION) {
2099 errors << "Proj_Action::read called for old GS version" << endl;
2100 return false;
2101 }
2102
2103 fread_endian<int>(fp, Type);
2104 fread_endian<char>(fp, Projectiles);
2105 fread_endian<int>(fp, Damage);
2106 fread_endian<int>(fp, Shake);
2107 fread_endian<char>(fp, UseSound);
2108 if(UseSound) {
2109 SndFilename = readString(fp);
2110
2111 if(!bDedicated) {
2112 // Load the sample
2113 Sound = gs->LoadGSSample(gs->sDirectory, SndFilename);
2114
2115 if(Sound == NULL)
2116 gs->modLog("Could not open sound '" + SndFilename + "'");
2117 }
2118 }
2119 fread_endian<float>(fp, BounceCoeff);
2120 fread_endian<int>(fp, BounceExplode);
2121 fread_endian<float>(fp, GoThroughSpeed);
2122 fread_endian_V<int>(fp, ChangeRadius);
2123 fread_endian_V<float>(fp, Speed);
2124 fread_endian_M<float>(fp, SpeedMult);
2125 if(Projectiles) Proj.read(gs, fp);
2126
2127 bool haveAddAction = false;
2128 fread_endian<char>(fp, haveAddAction);
2129 if(haveAddAction) {
2130 additionalAction = new Proj_Action();
2131 return additionalAction->read(gs, fp);
2132 }
2133 return true;
2134 }
2135
write(CGameScript * gs,FILE * fp)2136 bool Proj_Action::write(CGameScript* gs, FILE* fp) {
2137 if(gs->GetHeader()->Version <= GS_LX56_VERSION) {
2138 errors << "Proj_Action::write called for old GS version" << endl;
2139 return false;
2140 }
2141
2142 fwrite_endian<int>(fp, Type);
2143 fwrite_endian<char>(fp, Projectiles);
2144 fwrite_endian<int>(fp, Damage);
2145 fwrite_endian<int>(fp, Shake);
2146 fwrite_endian<char>(fp, UseSound);
2147 if(UseSound) writeString(SndFilename, fp);
2148 fwrite_endian<float>(fp, BounceCoeff);
2149 fwrite_endian<int>(fp, BounceExplode);
2150 fwrite_endian<float>(fp, GoThroughSpeed);
2151 fwrite_endian_V<int>(fp, ChangeRadius);
2152 fwrite_endian_V<float>(fp, Speed);
2153 fwrite_endian_M<float>(fp, SpeedMult);
2154
2155 if(Projectiles) Proj.write(gs, fp);
2156
2157 if(!additionalAction)
2158 fwrite_endian<char>(fp, 0);
2159 else {
2160 fwrite_endian<char>(fp, 1);
2161 additionalAction->write(gs, fp);
2162 }
2163 return true;
2164 }
2165
readFromIni(CGameScript * gs,const std::string & dir,const IniReader & ini,const std::string & section)2166 bool Proj_Event::readFromIni(CGameScript* gs, const std::string& dir, const IniReader& ini, const std::string& section) {
2167 if(!ini.ReadKeyword(section, "Type", (int*)&type, type)) {
2168 warnings << ini.getFileName() << ":" << section << ": Type attribute isn't set for event" << endl;
2169 return false;
2170 }
2171 if(get() == NULL) {
2172 warnings << ini.getFileName() << ":" << section << ": Type attribute is invalid for event" << endl;
2173 return false;
2174 }
2175 if(!get()->readFromIni(gs, dir, ini, section))
2176 return false;
2177
2178 if(!get()->canMatch()) {
2179 warnings << ini.getFileName() << ":" << section << ": Event can never occur with these settings" << endl;
2180 return false;
2181 }
2182 return true;
2183 }
2184
read(CGameScript * gs,FILE * fp)2185 bool Proj_Event::read(CGameScript* gs, FILE* fp) {
2186 fread_endian<int>(fp, type);
2187 if(get() == NULL) return false;
2188 return get()->read(gs, fp);
2189 }
2190
write(CGameScript * gs,FILE * fp)2191 bool Proj_Event::write(CGameScript* gs, FILE* fp) {
2192 fwrite_endian<int>(fp, type);
2193 if(get() == NULL) return false;
2194 return get()->write(gs, fp);
2195 }
2196
readFromIni(CGameScript * gs,const std::string & dir,const IniReader & ini,const std::string & section)2197 bool Proj_EventAndAction::readFromIni(CGameScript* gs, const std::string& dir, const IniReader& ini, const std::string& section) {
2198 std::string eventSection;
2199 ini.ReadString(section, "Event", eventSection, eventSection);
2200 if(eventSection == "") {
2201 warnings << ini.getFileName() << ":" << section << ": Event not set" << endl;
2202 return false;
2203 }
2204
2205 while(eventSection != "") {
2206 if(events.size() > 1000) {
2207 warnings << ini.getFileName() << ":" << section << ": Probably there is a loop definition for Event" << endl;
2208 events.clear();
2209 return false;
2210 }
2211 Proj_Event ev;
2212 if(!ev.readFromIni(gs, dir, ini, eventSection)) return events.size() > 0;
2213 events.push_back(ev);
2214 if(!ini.ReadString(std::string(eventSection), "AndEvent", eventSection, eventSection)) break;
2215 }
2216
2217 return Proj_Action::readFromIni(gs, dir, ini, section);
2218 }
2219
read(CGameScript * gs,FILE * fp)2220 bool Proj_EventAndAction::read(CGameScript* gs, FILE* fp) {
2221 Uint32 eventNum = 0;
2222 fread_endian<Uint32>(fp, eventNum);
2223 events.resize(eventNum);
2224
2225 for(Uint32 i = 0; i < eventNum; ++i)
2226 if(!events[i].read(gs, fp)) {
2227 errors << "Proj_EventAndAction: error while reading game script projectile actions" << endl;
2228 events.clear(); // would crash otherwise
2229 return false;
2230 }
2231
2232 return Proj_Action::read(gs, fp);
2233 }
2234
write(CGameScript * gs,FILE * fp)2235 bool Proj_EventAndAction::write(CGameScript* gs, FILE* fp) {
2236 fwrite_endian<Uint32>(fp, (Uint32)events.size());
2237
2238 for(Uint32 i = 0; i < events.size(); ++i)
2239 events[i].write(gs, fp);
2240
2241 return Proj_Action::write(gs, fp);
2242 }
2243
2244
readFromIni(CGameScript * gs,const std::string & dir,const IniReader & ini,const std::string & section)2245 bool Proj_TimerEvent::readFromIni(CGameScript* gs, const std::string& dir, const IniReader& ini, const std::string& section) {
2246 ini.ReadFloat(section, "Delay", &Delay, Delay);
2247 ini.ReadKeyword(section, "Repeat", &Repeat, Repeat);
2248 ini.ReadKeyword(section, "UseGlobalTime", &UseGlobalTime, UseGlobalTime);
2249 ini.ReadInteger(section, "PermanentMode", &PermanentMode, PermanentMode);
2250 return true;
2251 }
2252
read(CGameScript * gs,FILE * fp)2253 bool Proj_TimerEvent::read(CGameScript* gs, FILE* fp) {
2254 fread_endian<float>(fp, Delay);
2255 fread_endian<char>(fp, Repeat);
2256 fread_endian<char>(fp, UseGlobalTime);
2257 fread_endian<int>(fp, PermanentMode);
2258 return true;
2259 }
2260
write(CGameScript * gs,FILE * fp)2261 bool Proj_TimerEvent::write(CGameScript* gs, FILE* fp) {
2262 fwrite_endian<float>(fp, Delay);
2263 fwrite_endian<char>(fp, Repeat);
2264 fwrite_endian<char>(fp, UseGlobalTime);
2265 fwrite_endian<int>(fp, PermanentMode);
2266 return true;
2267 }
2268
readFromIni(CGameScript * gs,const std::string & dir,const IniReader & ini,const std::string & section)2269 bool Proj_ProjHitEvent::readFromIni(CGameScript* gs, const std::string& dir, const IniReader& ini, const std::string& section) {
2270 gs->needCollisionInfo = true;
2271
2272 ownerWorm.readFromIni(gs, dir, ini, section);
2273
2274 ini.ReadInteger(section, "MinHitCount", &MinHitCount, MinHitCount);
2275 ini.ReadInteger(section, "MaxHitCount", &MaxHitCount, MaxHitCount);
2276
2277 if(MaxHitCount >= 0 && MaxHitCount < MinHitCount) {
2278 warnings << ini.getFileName() << ":" << section << ": ignoring MaxHitCount because MaxHitCount < MinHitCount" << endl;
2279 MaxHitCount = -1;
2280 }
2281
2282 ini.ReadInteger(section, "Width", &Width, Width);
2283 ini.ReadInteger(section, "Height", &Height, Height);
2284
2285 ini.ReadKeyword(section, "TargetHealthIsMore", &TargetHealthIsMore, TargetHealthIsMore);
2286 ini.ReadKeyword(section, "TargetHealthIsLess", &TargetHealthIsLess, TargetHealthIsLess);
2287 ini.ReadKeyword(section, "TargetTimeIsMore", &TargetTimeIsMore, TargetTimeIsMore);
2288 ini.ReadKeyword(section, "TargetTimeIsLess", &TargetTimeIsLess, TargetTimeIsLess);
2289
2290 std::string prjfile;
2291 ini.ReadString(section, "Target", prjfile, "");
2292 if(prjfile != "")
2293 Target = gs->CompileProjectile(dir, prjfile);
2294 else
2295 Target = NULL;
2296 return true;
2297 }
2298
read(CGameScript * gs,FILE * fp)2299 bool Proj_ProjHitEvent::read(CGameScript* gs, FILE* fp) {
2300 if(gs->GetHeader()->Version <= GS_LX56_VERSION) {
2301 errors << "Proj_ProjHitEvent::read called for old GS version" << endl;
2302 return false;
2303 }
2304
2305 ownerWorm.read(gs, fp);
2306
2307 gs->needCollisionInfo = true;
2308 fread_endian<int>(fp, MinHitCount);
2309 fread_endian<int>(fp, MaxHitCount);
2310 fread_endian<int>(fp, Width);
2311 fread_endian<int>(fp, Height);
2312 fread_endian<char>(fp, TargetHealthIsMore);
2313 fread_endian<char>(fp, TargetHealthIsLess);
2314 fread_endian<char>(fp, TargetTimeIsMore);
2315 fread_endian<char>(fp, TargetTimeIsLess);
2316
2317 bool hasSpecificTarget = false;
2318 fread_endian<char>(fp, hasSpecificTarget);
2319 if(hasSpecificTarget)
2320 Target = gs->LoadProjectile(fp);
2321 else
2322 Target = NULL;
2323 return !hasSpecificTarget || Target != NULL;
2324 }
2325
write(CGameScript * gs,FILE * fp)2326 bool Proj_ProjHitEvent::write(CGameScript* gs, FILE* fp) {
2327 if(gs->GetHeader()->Version <= GS_LX56_VERSION) {
2328 errors << "Proj_ProjHitEvent::write called for old GS version" << endl;
2329 return false;
2330 }
2331
2332 ownerWorm.write(gs, fp);
2333
2334 fwrite_endian<int>(fp, MinHitCount);
2335 fwrite_endian<int>(fp, MaxHitCount);
2336 fwrite_endian<int>(fp, Width);
2337 fwrite_endian<int>(fp, Height);
2338 fwrite_endian<char>(fp, TargetHealthIsMore);
2339 fwrite_endian<char>(fp, TargetHealthIsLess);
2340 fwrite_endian<char>(fp, TargetTimeIsMore);
2341 fwrite_endian<char>(fp, TargetTimeIsLess);
2342
2343 fwrite_endian<char>(fp, Target != NULL);
2344 if(Target) return gs->SaveProjectile(Target, fp);
2345 return true;
2346 }
2347
2348
2349
readFromIni(CGameScript * gs,const std::string & dir,const IniReader & ini,const std::string & section)2350 bool Proj_WormHitEvent::readFromIni(CGameScript* gs, const std::string& dir, const IniReader& ini, const std::string& section) {
2351 ini.ReadKeyword(section, "SameWormAsProjOwner", &SameWormAsProjOwner, SameWormAsProjOwner);
2352 ini.ReadKeyword(section, "SameTeamAsProjOwner", &SameTeamAsProjOwner, SameTeamAsProjOwner);
2353 ini.ReadKeyword(section, "DiffWormAsProjOwner", &DiffWormAsProjOwner, DiffWormAsProjOwner);
2354 ini.ReadKeyword(section, "DiffTeamAsProjOwner", &DiffTeamAsProjOwner, DiffTeamAsProjOwner);
2355 ini.ReadKeyword(section, "TeamMateOfProjOwner", &TeamMateOfProjOwner, TeamMateOfProjOwner);
2356 ini.ReadKeyword(section, "EnemyOfProjOwner", &EnemyOfProjOwner, EnemyOfProjOwner);
2357 return true;
2358 }
2359
read(CGameScript * gs,FILE * fp)2360 bool Proj_WormHitEvent::read(CGameScript* gs, FILE* fp) {
2361 fread_endian<char>(fp, SameWormAsProjOwner);
2362 fread_endian<char>(fp, SameTeamAsProjOwner);
2363 fread_endian<char>(fp, DiffWormAsProjOwner);
2364 fread_endian<char>(fp, DiffTeamAsProjOwner);
2365 fread_endian<char>(fp, TeamMateOfProjOwner);
2366 fread_endian<char>(fp, EnemyOfProjOwner);
2367 return true;
2368 }
2369
write(CGameScript * gs,FILE * fp)2370 bool Proj_WormHitEvent::write(CGameScript* gs, FILE* fp) {
2371 fwrite_endian<char>(fp, SameWormAsProjOwner);
2372 fwrite_endian<char>(fp, SameTeamAsProjOwner);
2373 fwrite_endian<char>(fp, DiffWormAsProjOwner);
2374 fwrite_endian<char>(fp, DiffTeamAsProjOwner);
2375 fwrite_endian<char>(fp, TeamMateOfProjOwner);
2376 fwrite_endian<char>(fp, EnemyOfProjOwner);
2377 return true;
2378 }
2379
2380
2381
readFromIni(CGameScript * gs,const std::string & dir,const IniReader & ini,const std::string & section)2382 bool Proj_TerrainHitEvent::readFromIni(CGameScript* gs, const std::string& dir, const IniReader& ini, const std::string& section) {
2383 ini.ReadKeyword(section, "MapBound", &MapBound, MapBound);
2384 ini.ReadKeyword(section, "Dirt", &Dirt, Dirt);
2385 ini.ReadKeyword(section, "Rock", &Rock, Rock);
2386 return true;
2387 }
2388
read(CGameScript * gs,FILE * fp)2389 bool Proj_TerrainHitEvent::read(CGameScript* gs, FILE* fp) {
2390 fread_endian<char>(fp, MapBound);
2391 fread_endian<char>(fp, Dirt);
2392 fread_endian<char>(fp, Rock);
2393 return true;
2394 }
2395
write(CGameScript * gs,FILE * fp)2396 bool Proj_TerrainHitEvent::write(CGameScript* gs, FILE* fp) {
2397 fwrite_endian<char>(fp, MapBound);
2398 fwrite_endian<char>(fp, Dirt);
2399 fwrite_endian<char>(fp, Rock);
2400 return true;
2401 }
2402