Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
WeaponDesign.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright (C) 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: WeaponDesign.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Weapon Design parameters class
13 */
14 
15 #include "MemDebug.h"
16 #include "WeaponDesign.h"
17 #include "ShipDesign.h"
18 #include "Weapon.h"
19 
20 #include "Game.h"
21 #include "Sprite.h"
22 #include "Light.h"
23 #include "Bitmap.h"
24 #include "Solid.h"
25 #include "Sound.h"
26 #include "DataLoader.h"
27 
28 #include "ParseUtil.h"
29 
30 // +--------------------------------------------------------------------+
31 
32 static List<WeaponDesign> catalog;
33 static List<WeaponDesign> mod_catalog;
34 static bool degrees;
35 
36 #define GET_DEF_BOOL(x) if(defname==(#x))GetDefBool(design->x,pdef,filename)
37 #define GET_DEF_TEXT(x) if(defname==(#x))GetDefText(design->x,pdef,filename)
38 #define GET_DEF_NUM(x) if(defname==(#x))GetDefNumber(design->x,pdef,filename)
39 
40 // +--------------------------------------------------------------------+
41 
43 {
44  type = 0;
45  secret = 0;
46  drone = 0;
47  primary = 0;
48  beam = 0;
49  flak = 0;
50  guided = 0;
51  self_aiming = 0;
52  syncro = 0;
53  value = 0;
54  decoy_type = 0;
55  probe = 0;
56  target_type = 0;
57 
58  visible_stores = 0;
59  nstores = 0;
60  nbarrels = 0;
61 
62  recharge_rate = 0.0f;
63  refire_delay = 0.0f;
64  salvo_delay = 0.0f;
65  charge = 0.0f;
66  min_charge = 0.0f;
67  carry_mass = 0.0f;
68  carry_resist = 0.0f;
69 
70  speed = 0.0f;
71  life = 0.0f;
72  mass = 0.0f;
73  drag = 0.0f;
74  thrust = 0.0f;
75  roll_rate = 0.0f;
76  pitch_rate = 0.0f;
77  yaw_rate = 0.0f;
78  roll_drag = 0.0f;
79  pitch_drag = 0.0f;
80  yaw_drag = 0.0f;
81 
82  min_range = 0.0f;
83  max_range = 0.0f;
84  max_track = 0.0f;
85 
86  graphic_type = 0;
87  width = 0;
88  length = 0;
89 
90  scale = 1.0f;
91  explosion_scale = 0.0f;
92  light = 0.0f;
94  flash_scale = 0.0f;
95  flare_scale = 0.0f;
96 
97  spread_az = 0.0f;
98  spread_el = 0.0f;
99 
100  beauty_img = 0;
101  turret_model = 0;
102  turret_base_model = 0;
103  animation = 0;
104  anim_length = 0;
105  shot_img = 0;
106  shot_model = 0;
107  trail_img = 0;
108  flash_img = 0;
109  flare_img = 0;
110  sound_resource = 0;
111 
112  ammo = -1;
113  ripple_count = 0;
114  capacity = 100.0f;
115  damage = 1.0f;
116  damage_type = 0;
117  penetration = 1.0f;
118  firing_cone = 0.0f;
119  aim_az_max = 1.5f;
120  aim_az_min = -1.5f;
121  aim_az_rest = 0.0f;
122  aim_el_max = 1.5f;
123  aim_el_min = -1.5f;
124  aim_el_rest = 0.0f;
125  slew_rate = (float) (60*DEGREES);
126  turret_axis = 0;
127  lethal_radius = 500.0f;
128  integrity = 100.0f;
129 
130  eject = Vec3(0.0f, -100.0f, 0.0f);
131 
132  det_range = 0.0f;
133  det_count = 0;
134  det_spread = (float) (PI/8);
135 
136  trail_length = 0;
137  trail_width = 0;
138  trail_dim = 0;
139 
140  for (int i = 0; i < MAX_STORES; i++) {
141  muzzle_pts[i] = Vec3(0,0,0);
142  attachments[i] = Vec3(0,0,0);
143  }
144 }
145 
147 {
148  delete turret_model;
149  delete turret_base_model;
150  delete shot_model;
151  delete sound_resource;
152 }
153 
154 // +--------------------------------------------------------------------+
155 
156 void
157 WeaponDesign::Initialize(const char* filename)
158 {
159  Print("Loading Weapon Designs '%s'\n", filename);
160  LoadDesign("Weapons/", filename);
161 }
162 
163 // +--------------------------------------------------------------------+
164 
165 void
167 {
168  catalog.destroy();
169  mod_catalog.destroy();
170 }
171 
172 void
174 {
175  mod_catalog.destroy();
176 }
177 
178 // +--------------------------------------------------------------------+
179 
180 void
181 WeaponDesign::LoadDesign(const char* path, const char* filename, bool mod)
182 {
183  // Load Design File:
184  DataLoader* loader = DataLoader::GetLoader();
185  loader->SetDataPath(path);
186 
187  BYTE* block;
188  int blocklen = loader->LoadBuffer(filename, block, true);
189 
190  Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
191  Term* term = parser.ParseTerm();
192 
193  if (!term) {
194  Print("ERROR: could not parse '%s'\n", filename);
195  return;
196  }
197  else {
198  TermText* file_type = term->isText();
199  if (!file_type || file_type->value() != "WEAPON") {
200  Print("ERROR: invalid weapon design file '%s'\n", filename);
201  return;
202  }
203  }
204 
205  int type = 1;
206  degrees = false;
207 
208  do {
209  delete term;
210 
211  term = parser.ParseTerm();
212 
213  if (term) {
214  TermDef* def = term->isDef();
215  if (def) {
216  Text defname = def->name()->value();
217  defname.setSensitive(false);
218 
219  if (defname == "primary" ||
220  defname == "missile" ||
221  defname == "drone" ||
222  defname == "beam") {
223 
224  if (!def->term() || !def->term()->isStruct()) {
225  Print("WARNING: weapon structure missing in '%s'\n", filename);
226  }
227  else {
228  TermStruct* val = def->term()->isStruct();
229  WeaponDesign* design = new(__FILE__,__LINE__) WeaponDesign;
230 
231  design->type = type++;
232  if (defname == "primary") {
233  design->primary = true;
234  }
235 
236  else if (defname == "beam") {
237  design->primary = true;
238  design->beam = true;
239  design->guided = true;
240 
241  design->spread_az = 0.15f;
242  design->spread_el = 0.15f;
243 
244  }
245 
246  else if (defname == "drone") {
247  design->drone = true;
248  design->penetration = 5.0f;
249  }
250 
251  else { // missile
252  design->penetration = 5.0f;
253  }
254 
255  float sound_min_dist = 1.0f;
256  float sound_max_dist = 100.0e3f;
257 
258  for (int i = 0; i < val->elements()->size(); i++) {
259  TermDef* pdef = val->elements()->at(i)->isDef();
260  if (pdef) {
261  defname = pdef->name()->value();
262  defname.setSensitive(false);
263 
265  else GET_DEF_TEXT(group);
267  else GET_DEF_NUM (guided);
269  else GET_DEF_BOOL(flak);
270  else GET_DEF_BOOL(syncro);
272  else GET_DEF_NUM (value);
273 
274  else if (defname==("degrees")) {
275  GetDefBool(degrees,pdef,filename);
276  }
277 
278  else if (defname==("secret")) {
279  GetDefBool(design->secret,pdef,filename);
280  }
281 
282  else if (defname==("aim_az_max")) {
283  GetDefNumber(design->aim_az_max,pdef,filename);
284  if (degrees) design->aim_az_max *= (float) DEGREES;
285  design->aim_az_min = 0.0f - design->aim_az_max;
286  }
287 
288  else if (defname==("aim_el_max")) {
289  GetDefNumber(design->aim_el_max,pdef,filename);
290  if (degrees) design->aim_el_max *= (float) DEGREES;
291  design->aim_el_min = 0.0f - design->aim_el_max;
292  }
293 
294  else if (defname==("aim_az_min")) {
295  GetDefNumber(design->aim_az_min,pdef,filename);
296  if (degrees) design->aim_az_min *= (float) DEGREES;
297  }
298 
299  else if (defname==("aim_el_min")) {
300  GetDefNumber(design->aim_el_min,pdef,filename);
301  if (degrees) design->aim_el_min *= (float) DEGREES;
302  }
303 
304  else if (defname==("aim_az_rest")) {
305  GetDefNumber(design->aim_az_rest,pdef,filename);
306  if (degrees) design->aim_az_rest *= (float) DEGREES;
307  }
308 
309  else if (defname==("aim_el_rest")) {
310  GetDefNumber(design->aim_el_rest,pdef,filename);
311  if (degrees) design->aim_el_rest *= (float) DEGREES;
312  }
313 
314  else if (defname==("spread_az")) {
315  GetDefNumber(design->spread_az,pdef,filename);
316  if (degrees) design->spread_az *= (float) DEGREES;
317  }
318 
319  else if (defname==("spread_el")) {
320  GetDefNumber(design->spread_el,pdef,filename);
321  if (degrees) design->spread_el *= (float) DEGREES;
322  }
323 
324  else GET_DEF_NUM (capacity);
325  else GET_DEF_NUM (recharge_rate);
326  else GET_DEF_NUM (refire_delay);
327  else GET_DEF_NUM (salvo_delay);
328  else GET_DEF_NUM (ammo);
329  else GET_DEF_NUM (ripple_count);
330  else GET_DEF_NUM (charge);
331  else GET_DEF_NUM (min_charge);
332  else GET_DEF_NUM (carry_mass);
333  else GET_DEF_NUM (carry_resist);
334  else GET_DEF_NUM (damage);
335  else GET_DEF_NUM (penetration);
336  else GET_DEF_NUM (speed);
337  else GET_DEF_NUM (life);
338  else GET_DEF_NUM (mass);
339  else GET_DEF_NUM (drag);
340  else GET_DEF_NUM (thrust);
341  else GET_DEF_NUM (roll_rate);
342  else GET_DEF_NUM (pitch_rate);
343  else GET_DEF_NUM (yaw_rate);
344  else GET_DEF_NUM (roll_drag);
345  else GET_DEF_NUM (pitch_drag);
346  else GET_DEF_NUM (yaw_drag);
347  else GET_DEF_NUM (lethal_radius);
348  else GET_DEF_NUM (integrity);
349 
350  else GET_DEF_NUM (det_range);
351  else GET_DEF_NUM (det_count);
352  else GET_DEF_NUM (det_spread);
353  else GET_DEF_TEXT(det_child);
354 
355  else GET_DEF_NUM (slew_rate);
356 
357  else GET_DEF_NUM (min_range);
358  else GET_DEF_NUM (max_range);
359  else GET_DEF_NUM (max_track);
360 
361  else GET_DEF_NUM (graphic_type);
362  else GET_DEF_NUM (width);
363  else GET_DEF_NUM (length);
364  else GET_DEF_NUM (scale);
366  else GET_DEF_NUM (light);
367  else GET_DEF_NUM (flash_scale);
368  else GET_DEF_NUM (flare_scale);
369 
370  else GET_DEF_NUM (trail_length);
371  else GET_DEF_NUM (trail_width);
372  else GET_DEF_NUM (trail_dim);
373 
374  else GET_DEF_TEXT(beauty);
375  else GET_DEF_TEXT(bitmap);
376  else GET_DEF_TEXT(turret);
378  else GET_DEF_TEXT(model);
379  else GET_DEF_TEXT(trail);
380  else GET_DEF_TEXT(flash);
381  else GET_DEF_TEXT(flare);
382  else GET_DEF_TEXT(sound);
383  else GET_DEF_BOOL(probe);
384  else GET_DEF_NUM (turret_axis);
385  else GET_DEF_NUM (target_type);
386 
387  else if (defname == "animation") {
388  if (design->anim_length < 16) {
389  GetDefText(design->anim_frames[design->anim_length++], pdef, filename);
390  }
391  else {
392  Print("WARNING: too many animation frames for weapon '%s' in '%s'\n",
393  (const char*) design->name, filename);
394  }
395  }
396 
397  else if (defname == "light_color")
398  GetDefColor(design->light_color, pdef, filename);
399 
400  else if (defname == "sound_min_dist")
401  GetDefNumber(sound_min_dist,pdef,filename);
402 
403  else if (defname == "sound_max_dist")
404  GetDefNumber(sound_max_dist,pdef,filename);
405 
406  else if (defname == "muzzle") {
407  if (design->nbarrels < MAX_STORES) {
408  Vec3 a;
409  GetDefVec(a, pdef, filename);
410  design->muzzle_pts[design->nbarrels++] = a;
411  }
412  else {
413  Print("WARNING: too many muzzles for weapon '%s' in '%s'\n",
414  (const char*) design->name, filename);
415  }
416  }
417 
418  else if (defname == "attachment") {
419  if (design->nstores < MAX_STORES) {
420  Vec3 a;
421  GetDefVec(a, pdef, filename);
422  design->attachments[design->nstores++] = a;
423  }
424  else {
425  Print("WARNING: too many attachments for weapon '%s' in '%s'\n",
426  (const char*) design->name, filename);
427  }
428  }
429 
430  else if (defname == "eject")
431  GetDefVec(design->eject,pdef,filename);
432 
433  else if (defname == "decoy") {
434  char typestr[32];
435  GetDefText(typestr, pdef, filename);
436  design->decoy_type = ShipDesign::ClassForName(typestr);
437  }
438 
439  else if (defname == "damage_type") {
440  char typestr[32];
441  GetDefText(typestr, pdef, filename);
442 
443  if (!_stricmp(typestr, "normal"))
444  design->damage_type = DMG_NORMAL;
445 
446  else if (!_stricmp(typestr, "emp"))
447  design->damage_type = DMG_EMP;
448 
449  else if (!_stricmp(typestr, "power"))
450  design->damage_type = DMG_POWER;
451 
452  else
453  Print("WARNING: unknown weapon damage type '%s' in '%s'\n",
454  typestr, filename);
455  }
456 
457 
458  else {
459  Print("WARNING: parameter '%s' ignored in '%s'\n",
460  defname.data(), filename);
461  }
462  }
463  else {
464  Print("WARNING: term ignored in '%s'\n", filename);
465  val->elements()->at(i)->print();
466  }
467  }
468 
469  if (design->description.length()) {
470  design->description = Game::GetText(design->description);
471  }
472 
473  if (design->anim_length > 0) {
474  design->animation = new(__FILE__,__LINE__) Bitmap[design->anim_length];
475  for (int i = 0; i < design->anim_length; i++) {
476  Bitmap* p = design->animation + i;
477  loader->LoadBitmap(design->anim_frames[i], *p, Bitmap::BMP_TRANSLUCENT);
478  p->MakeTexture();
479  }
480  }
481 
482  else if (design->bitmap.length()) {
483  loader->LoadTexture(design->bitmap, design->shot_img, Bitmap::BMP_TRANSLUCENT);
484  }
485 
486  if (design->beauty.length()) {
487  loader->LoadTexture(design->beauty, design->beauty_img, Bitmap::BMP_TRANSLUCENT);
488  }
489 
490  if (design->turret.length()) {
491  Text p;
492  Text t = design->turret;
493  const char* s = strrchr(t.data(), '/');
494 
495  if (s) {
496  p = Text(path) + t.substring(0, s-t.data()+1);
497  t = t.substring(s-t.data()+1, t.length());
498  }
499  else {
500  s = strrchr(t.data(), '\\');
501 
502  if (s) {
503  p = Text(path) + t.substring(0, s-t.data()+1);
504  t = t.substring(s-t.data()+1, t.length());
505  }
506  }
507 
508  if (p.length())
509  loader->SetDataPath(p);
510 
511  design->turret_model = new(__FILE__,__LINE__) Model;
512  design->turret_model->Load(t, design->scale);
513 
514  if (design->turret_base.length()) {
515  t = design->turret_base;
516  s = strrchr(t.data(), '/');
517 
518  if (s) {
519  p = Text(path) + t.substring(0, s-t.data()+1);
520  t = t.substring(s-t.data()+1, t.length());
521  }
522  else {
523  s = strrchr(t.data(), '\\');
524 
525  if (s) {
526  p = Text(path) + t.substring(0, s-t.data()+1);
527  t = t.substring(s-t.data()+1, t.length());
528  }
529  }
530 
531  if (p.length())
532  loader->SetDataPath(p);
533 
534  design->turret_base_model = new(__FILE__,__LINE__) Model;
535  design->turret_base_model->Load(t, design->scale);
536  }
537 
538  loader->SetDataPath(path);
539  }
540 
541  if (design->model.length()) {
542  Text p;
543  Text t = design->model;
544  const char* s = strrchr(t.data(), '/');
545 
546  if (s) {
547  p = Text(path) + t.substring(0, s-t.data()+1);
548  t = t.substring(s-t.data()+1, t.length());
549  }
550  else {
551  s = strrchr(t.data(), '\\');
552 
553  if (s) {
554  p = Text(path) + t.substring(0, s-t.data()+1);
555  t = t.substring(s-t.data()+1, t.length());
556  }
557  }
558 
559  if (p.length())
560  loader->SetDataPath(p);
561 
562  design->shot_model = new(__FILE__,__LINE__) Model;
563  design->shot_model->Load(t, design->scale);
564 
565  loader->SetDataPath(path);
566  }
567 
568  if (design->trail.length()) {
569  loader->LoadTexture(design->trail, design->trail_img, Bitmap::BMP_TRANSLUCENT);
570  }
571 
572  if (design->flash.length()) {
573  loader->LoadTexture(design->flash, design->flash_img, Bitmap::BMP_TRANSLUCENT);
574  }
575 
576  if (design->flare.length()) {
577  loader->LoadTexture(design->flare, design->flare_img, Bitmap::BMP_TRANSLUCENT);
578  }
579 
580  if (design->sound.length()) {
581  int SOUND_FLAGS = Sound::LOCALIZED | Sound::LOC_3D;
582 
583  if (design->beam)
584  SOUND_FLAGS = Sound::LOCALIZED | Sound::LOC_3D | Sound::LOCKED;
585 
586  if (strstr(path, "Mods") == 0)
587  loader->SetDataPath("Sounds/");
588  loader->LoadSound(design->sound, design->sound_resource, SOUND_FLAGS);
589  loader->SetDataPath(path);
590 
591  if (design->sound_resource) {
592  design->sound_resource->SetMinDistance(sound_min_dist);
593  design->sound_resource->SetMaxDistance(sound_max_dist);
594  }
595  }
596 
597  if (design->max_range == 0.0f)
598  design->max_range = design->speed * design->life;
599 
600  if (design->max_track == 0.0f)
601  design->max_track = 3.0f * design->max_range;
602 
603  if (design->probe && design->lethal_radius < 1e3)
604  design->lethal_radius = 50e3f;
605 
606  if (design->beam)
607  design->flak = false;
608 
609  if (design->self_aiming) {
610  if (fabs(design->aim_az_max) > design->firing_cone)
611  design->firing_cone = (float) fabs(design->aim_az_max);
612 
613  if (fabs(design->aim_az_min) > design->firing_cone)
614  design->firing_cone = (float) fabs(design->aim_az_min);
615 
616  if (fabs(design->aim_el_max) > design->firing_cone)
617  design->firing_cone = (float) fabs(design->aim_el_max);
618 
619  if (fabs(design->aim_el_min) > design->firing_cone)
620  design->firing_cone = (float) fabs(design->aim_el_min);
621  }
622 
623  if (mod)
624  mod_catalog.append(design);
625  else
626  catalog.append(design);
627  }
628  }
629 
630  else
631  Print("WARNING: unknown definition '%s' in '%s'\n",
632  def->name()->value().data(), filename);
633  }
634  else {
635  Print("WARNING: term ignored in '%s'\n", filename);
636  term->print();
637  }
638  }
639  }
640  while (term);
641 
642  loader->ReleaseBuffer(block);
643  loader->SetDataPath(0);
644 }
645 
646 // +--------------------------------------------------------------------+
647 
650 {
651  WeaponDesign test;
652  test.type = type;
653 
654  WeaponDesign* result = catalog.find(&test);
655 
656  if (!result)
657  result = mod_catalog.find(&test);
658 
659  return result;
660 }
661 
662 // +--------------------------------------------------------------------+
663 
665 WeaponDesign::Find(const char* name)
666 {
667  for (int i = 0; i < catalog.size(); i++) {
668  WeaponDesign* d = catalog.at(i);
669 
670  if (d->name == name) {
671  return d;
672  }
673  }
674 
675  for (int i = 0; i < mod_catalog.size(); i++) {
676  WeaponDesign* d = mod_catalog.at(i);
677 
678  if (d->name == name) {
679  return d;
680  }
681  }
682 
683  Print("WeaponDesign: no catalog entry for design '%s', checking mods...\n", name);
684  return WeaponDesign::FindModDesign(name);
685 }
686 
689 {
690  Text fname = name;
691  fname += ".def";
692 
693  LoadDesign("Mods/Weapons/", fname, true);
694 
695  for (int i = 0; i < mod_catalog.size(); i++) {
696  WeaponDesign* d = mod_catalog.at(i);
697 
698  if (d->name == name) {
699  Print("WeaponDesign: found mod weapon '%s'\n", d->name.data());
700  return d;
701  }
702  }
703 
704  Print("WeaponDesign: no mod found for design '%s'\n", name);
705  return 0;
706 }
707 
708 // +--------------------------------------------------------------------+
709 
710 int
712 {
713  designs.clear();
714 
715  for (int i = 0; i < catalog.size(); i++) {
716  WeaponDesign* design = catalog[i];
717  designs.append(&design->name);
718  }
719 
720  for (int i = 0; i < mod_catalog.size(); i++) {
721  WeaponDesign* design = mod_catalog[i];
722  designs.append(&design->name);
723  }
724 
725  return designs.size();
726 }