JNR
laLevel__init.cpp
1 /*
2 
3 Jump'n'Run Engine
4 http://www.atanaslaskov.com/jnr/
5 
6 BSD LICENSE
7 Copyright (c) 2007-2013, Atanas Laskov
8 All rights reserved.
9 
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
12 1. Redistributions of source code must retain the above copyright notice,
13 this list of conditions and the following disclaimer.
14 2. Redistributions in binary form must reproduce the above copyright notice,
15 this list of conditions and the following disclaimer in the documentation
16 and/or other materials provided with the distribution.
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL ATANAS LASKOV BE LIABLE FOR ANY
21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 */
29 
30 //
31 // FILE: laLevel.cpp
32 //
33 // This class represents the entire level; Each level is
34 // composed of several sequential segments, arragned along the X
35 // axis; Segments are organized as a list of laSecment objects
36 //
37 // The level object manages this list, as well as dynamic object
38 // and the tileset object (laTitleset)
39 //
40 // Copyright (C) 2007-2013 Atanas Laskov, <latanas@gmail.com>
41 //
42 #include "stdafx.h"
43 #include "Core-Level-JR.h"
44 #include "laStagesJR.h"
45 
46 double laLevel::_dZoom = 1;
47 
48 laLevel::laLevel(void)
49 {
50  // Object types this abstract factory can create
51  //
58  REGISTER_CLASS( laShooter );
61  //REGISTER_CLASS( laPotion );
62 
63  strcpy(_strFileName, "");
64 
65  _pTileSet = NULL;
66  _nObjCnt = _nSegCnt = 0;
67  _bValid = _bCompleted = M_FALSE;
68 
69  _dZoom = 1;
70  //_dSkyX = 0;
71 
72  _bIntroPresent = M_FALSE;
73  strcpy(_strNextLevel, "none");
74  strcpy(_strFileName, "untitled");
75 }
76 
77 laLevel::~laLevel(void) {
78  discard();
79  MLOG("Level object destroyed");
80 }
81 
82 // Discard level, free memory
83 //
84 void laLevel::discard()
85 {
86  ERRORLEVEL_BEGIN;
87  ASSERT(_bValid, "Not a valid level, cannot be discarded");
88  _bValid = M_FALSE;
89 
90  //Discard objects
91  MLOG("Discarding objects...");
92  for(int i=_nObjCnt-1; i>=0; i--)
93  {
94  delete _arObjects[i];
95  _arObjects[i] = NULL;
96  //MLOG("Object %d discarded.", i);
97  }
98 
99  // Discard segment chain
100  //
101  MLOG("Discarding segment chain...");
102  laSegment *p = _cbFirstSeg.next(), *pp;
103 
104  while( p ) {
105  pp = p->next();
106  delete p;
107  p = pp;
108  }
109 
110  _nSegCnt = 1;
111  _cbFirstSeg._pNext = NULL;
112 
113  laPivot::colliderRange(NULL, 0);
114 
115  // Other stuff
116  //
117  MLOG("Discarding tileset...");
118  delete _pTileSet;
119  _pTileSet = NULL;
120 
121  MLOG("Discarding terrain curviture data...");
122  delete [] _arCurviture_ZOffsets;
123 
124  MLOG("Discarding noise texture...");
125  delete _pNoiseTex;
126  _pNoiseTex = NULL;
127 
128  MLOG("Level object discarded");
129  ERRORLEVEL_END;
130 }
131 
132 // Reset all dynamic objects
133 //
134 void laLevel::reset()
135 {
136  ERRORLEVEL_BEGIN;
137 
138  // Reset objects
139  //
140  for(unsigned i=0; i<_nObjCnt; i++) {
141  _arObjects[i]->respawn();
142  }
143 
144  // Re-distribute objects to segments again
145  // NOTE: If we don't, objects wont migrate to their respawn locations,
146  // until the player happens to visit their last segment of residence.
147  //
148  laSegment *p = &_cbFirstSeg;
149 
150  do{
151  p->_init_object_list(_arObjects, _nObjCnt);
152  }while( p = p->next() );
153 
154  MLOG("Level reset");
155  ERRORLEVEL_END;
156 }
157 
158 // Create empty
159 //
160 void laLevel::create(unsigned nSegCnt, laTileset *pTS)
161 {
162  ERRORLEVEL_BEGIN;
163  ASSERT(pTS, "Nil tileset.");
164 
165  _pTileSet = pTS;
166  _bValid = M_TRUE;
167 
168  _nSegCnt = nSegCnt;
169 
170  // Create segments chain
171  //
172  laSegment *p = &_cbFirstSeg;
173 
174  for(unsigned i=0; i<_nSegCnt - 1; i++)
175  {
176  p->setTS(_pTileSet);
177  p->setLevel(this);
178  p->setBackgroundElement(0);
179 
180  p->next( new laSegment(i+1) );
181  p = p->next();
182  }
183 
184  p->setTS(_pTileSet);
185  p->setLevel(this);
186  p->setBackgroundElement(0);
187 
188  // Objects
189  _nObjCnt = 0;
190 
191  ERRORLEVEL_END;
192 }
193 
194 // Load level file
195 //
196 void laLevel::load(char* strFile, M_BOOL bReload)
197 {
198  ERRORLEVEL_BEGIN;
199  ASSERT(!_bValid, "Level is valid, must discard() it first.");
200  //ASSERT(bReload, "Not Implemented");
201  progressReset("Loading level '%s'...", strFile);
202  strcpy(_strFileName, strFile);
203 
204  laFileParser fp, fxp;
205  char strFilePath[128]=M_DIR_LEVEL, junk[128], strTilesetFile[128], strIntroFile[64];
206  unsigned len;
207 
208  // Open level file
209  //
210  strcat(strFilePath, strFile);
211  fp.fileOpen(strFilePath);
212 
213  _bValid = M_TRUE;
214  srand( ::GetTickCount() );
215 
216  // Section "General"
217  //
218  progressIncrease(0.05, "Loading general section...");
219 
220  try{ fp.readSectionSeparator(junk); }
221  catch(laError& ){ throw laError("Section separator expected but not found (while parsing '%s')", strFile); }
222 
223  _strupr(junk);
224  ASSERT(!strcmp(junk, "GENERAL"), "Section [GENERAL] expected, [%s] found ('%s')", junk, strFile);
225 
226  fp.readText(_strName);
227  fp.readText(strTilesetFile);
228  fp.readUnsigned(&len);
229 
230  // Intro
231  //
232  fp.readText(strIntroFile);
233  fp.readText(_strNextLevel);
234 
235  _bIntroOver = M_FALSE;
236  if( strcmp(strIntroFile, "none") )
237  {
238  laFileParser fp(strIntroFile);
239 
240  fp.readObj( (stageIntro*)(laSystemIntegrator::getGame()->getStage(M_STAGE_INTRO)), M_FALSE);
241 
242  _bIntroPresent = M_TRUE;
243  }
244  else _bIntroPresent = M_FALSE;
245 
246  // Load the tileset
247  //
248  progressIncrease(0.65);
249 
250  _pTileSet = new laTileset();
251 
252  progressSubtask(_pTileSet, 0.6);
253  _pTileSet->load(strTilesetFile);
254  progressSubtask(NULL,0);
255 
256  // Load segments and objects
257  //
258  _load_segments(fp, len);
259  _load_objects(fp);
260 
261  // Noise tex
262  //
263  _pNoiseTex = new laTexture();
264  _pNoiseTex->load( _pTileSet->getBackgroundElement()->getText("terrain-noise-texture") );
265 
266  // Distribute objects to segemnts
267  //
268  progressIncrease(0.1, "Distributing objects to segments...");
269 
270  laSegment *p = &_cbFirstSeg;
271 
272  do{
273  p->_init_object_list(_arObjects, _nObjCnt);
274  }while( p=p->_pNext );
275 
276  progressIncrease(0.1, "Precomputing terrain curviture...");
277  _curviture_precompute();
278 
279  progressIncrease(0.1, "Level loaded.");
280  ERRORLEVEL_END;
281 }
282 
283 // Load list of segments from the level file
284 //
285 void laLevel::_load_segments(laFileParser &fp, unsigned len)
286 {
287  char junk[128];
288  laSegment *p = &_cbFirstSeg;
289  unsigned seg_index =0;
290  laCollisionDomain *pNext;
291 
292  progressIncrease(0, "Creating segment list of lenght %d...", len);
293  create(len, _pTileSet);
294 
295  do{
296  progressIncrease(0.2/len, "Loading segment %d...", seg_index);
297 
298  try{ fp.readSectionSeparator(junk); }
299  catch(laError& ){
300  throw laError("Segment separator (%d) expected but not found", seg_index);
301  }
302 
303  //Read the object
304  try{ fp.readObj(p, M_FALSE); }
305  catch(laError& pe){ throw laError("%s\n\nLevel segment (%d) appears to be corrupted", pe.getText(), seg_index); }
306 
307  //Build collision domains
308  p->bulidDomains(laPoint3(seg_index*M_UNIT*M_SEGW,0,0));
309 
310  // Randomly assign segment background
311  //
312  unsigned nElement = _pTileSet->getSegmentBackground(rand()%_pTileSet->getSegmentBackgroundCount());
313  p->setBackgroundElement(nElement);
314 
315  seg_index++;
316 
317  }while( p=p->_pNext );
318 
319  // Remove empty collision domains
320  _cbFirstSeg.firstDomain()->optimize();
321 
322  // Set inital range; Objects may need this on init ( e[1]. static objects to project shadow )
323  laPivot::colliderRange(_cbFirstSeg.firstNonemptyDomain(), _cbFirstSeg.countDomains(len));
324 
325 }
326 
327 // Load dynamic objects from the level file
328 //
329 void laLevel::_load_objects(laFileParser &fp)
330 {
331  char junk[128], strElementName[32], *strInstanceType;
332  unsigned nElementIndex;
333  laObject *pplr;
334 
335  //Start loading the objects
336  //
337  progressIncrease(0, "Loading dynamic objects...");
338  _nObjCnt = 0;
339 
340  while(1)
341  {
342  try{ fp.readSectionSeparator(junk); }
343  catch(laError& ){ throw laError("Section separator expected but not found"); }
344 
345  _strupr(junk);
346  if(!strcmp(junk, "END")) break;
347 
348  ASSERT(_nObjCnt < M_MAXOBJCNT, "Object count exceeds M_MAXOBJCNT");
349  ASSERT(!strcmp(junk, "OBJECT"), "[OBJECT] expected, [%s] found", junk);
350 
351  // Extract the string ID of the class to be used for the dynamic object
352  //
353  try{ fp.readText(strElementName); }
354  catch(laError& ){ throw laError("Corrupted object (%d) encountered", _nObjCnt); }
355 
356  nElementIndex = _pTileSet->getElementIndex(strElementName);
357 
358  ASSERT(_pTileSet->getElementCnt() > nElementIndex,
359  "Tileset element '%s' was requested by object (%d) but not found", strElementName, _nObjCnt);
360 
361  if( /*(bReload) &&*/ (_nObjCnt==0) )
362  {
363  pplr = _arObjects[_nObjCnt];
364  }
365 
366  // Insantiate a dynamic object of the specified class
367  //
368  strInstanceType = _pTileSet->getElement(nElementIndex)->getInstanceType();
369  _arObjects[_nObjCnt] = (laObject*)instantiate(strInstanceType);
370 
371  // Custom loading from the .lvl file, for the instantiated object
372  //
373  _arObjects[_nObjCnt]->setObject(nElementIndex, (laElement*)_pTileSet->getElement(nElementIndex));
374  _arObjects[_nObjCnt]->setTS(_pTileSet);
375  _arObjects[_nObjCnt]->setLevel(this);
376 
377  try{ fp.readObj(_arObjects[_nObjCnt], M_FALSE); }
378  catch(laError& pe){ throw laError("%s\n\nCorrupted object (%d) encountered", pe.getText(), _nObjCnt); }
379 
380  MLOG("Object %d loaded...", _nObjCnt);
381 
382  //if( /*(bReload) &&*/ (_nObjCnt==0) )
383  // _arObjects[_nObjCnt] = pplr;
384  _nObjCnt++;
385  }
386 }
387 
388 // Save player profile
389 //
390 void laLevel::_save_profile(char* strName)
391 {
392  char strfile[64];
393  sprintf(strfile, "save\\%s.profile", strName);
394 
395  FILE *f = fopen(strfile, "w+");
396  fprintf(f, "[profile]\n");
397 
398  //Point to next level if this one is completed
399  if(_bCompleted) fprintf(f, "[text] load-level: '%s' \n", _strNextLevel);
400  else fprintf(f, "[text] load-level: '%s' \n", _strFileName);
401 
402  //Save player data
403  ((laPlayer*)getPlayer())->saveProfile(f);
404 
405  fprintf(f, "\n[/profile]");
406  fclose(f);
407 }
408 
409 // Precompute terrain curviture (offsets and angles)
410 //
411 void laLevel::_curviture_precompute()
412 {
413  double *pmem = new double [_nSegCnt*M_SEGW * 3];
414 
415  _arCurviture_ZOffsets = pmem;// + 0 * _nSegCnt*M_SEGW;
416  _arCurviture_Angles = pmem + 1 * _nSegCnt*M_SEGW;
417  _arCurviture_Scales = pmem + 2 * _nSegCnt*M_SEGW;
418 
419  laSegment *ps = &_cbFirstSeg;
420  double arParCurrent[4], arParNext[4], arPar[4];
421 
422  ps->_update_curviture_parameters();
423  ps->curvitureParams(arParCurrent);
424 
425  // For every segment
426  for(unsigned i=0; i<_nSegCnt; i++)
427  {
428  ps = ps->next();
429 
430  if(ps) // for the last segment ps->next() == NULL and we don't blend, just keep params
431  {
432  ps->_update_curviture_parameters();
433  ps->curvitureParams(arParNext);
434  }
435 
436  // For every X
437  for(unsigned xs=0; xs<M_SEGW; xs++)
438  {
439  // Blend the paramaters of the current and next segment
440  double w = 1.0 - (double)xs/M_SEGW;
441  for(unsigned p=0; p<4; p++) arPar[p] = w * arParCurrent[p] + (1-w) * arParNext[p];
442 
443  // Precompute offset and rotation
444  //
445  unsigned ix = i*(M_SEGW) + xs;
446  double dx = ix * 0.1;
447 
448  _arCurviture_ZOffsets[ ix ] =
449  arPar[0] * sin( dx/2.0 ) * (M_UNIT) +
450  arPar[1] * sin( dx ) * sin( dx*2 ) * (M_UNIT*2) * (M_UNIT*0.5) +
451  arPar[2] * sin( dx*8 ) * (M_UNIT*0.6); // 0.4
452 
453  if( ix > 0 )
454  {
455  //NOTE: can't use ix+1 for the angle since it doesn't exist yet
456  double dOffsetDelta = _arCurviture_ZOffsets[ix-1] - _arCurviture_ZOffsets[ix];
457 
458  _arCurviture_Angles[ ix-1 ] = atan2( dOffsetDelta, M_UNIT );
459  _arCurviture_Scales[ ix-1 ] = sqrt( M_UNIT*M_UNIT + dOffsetDelta*dOffsetDelta)/M_UNIT;
460  }
461  }
462 
463  memcpy(arParCurrent, arParNext, sizeof(double)*4);
464  }
465 
466  // Last angle & scalse defaults
467  _arCurviture_Angles[ (_nSegCnt)*M_SEGW -1 ] = _arCurviture_Angles[ (_nSegCnt)*M_SEGW -2 ];
468  _arCurviture_Scales[ (_nSegCnt)*M_SEGW -1 ] = _arCurviture_Scales[ (_nSegCnt)*M_SEGW -2 ];
469 }
470 
471 /*void laLevel::createCollisionDomains()
472 {
473  unsigned seg_index =0;
474  laSegment *p = &_cbFirstSeg;
475  laCollisionDomain *pNext;
476 
477  do{
478  //Build collision domains
479  p[2]ulidDomains(laPoint3(seg_index*M_UNIT*M_SEGW,0,0));
480 
481  //Link the collision domain of this segment to the next
482  if(p->_pNext)
483  {
484  pNext = p->_pNext[3]tIndex(0,0)getDomain();
485  }
486  else pNext = NULL;
487 
488  p[3]tIndex(M_SEGW-1, M_SEGH-1)getDomain()->setNextDomain(pNext);
489 
490  unsigned nElement = _pTileSet->getSegmentBackground(rand()%_pTileSet->getSegmentBackgroundCount());
491  p->setBackgroundElement(nElement);
492 
493  seg_index++;
494 
495  }while( p=p->_pNext );
496 }*/
497 
498 /*void laLevel::addObject(laObject *po)
499 {
500  ASSERT( (_nObjCnt+1) >=M_MAXOBJCNT, "laLevel::addObject: Object count exceeds M_MAXOBJCNT" );
501 
502  _arObjects[_nObjCnt++] = po;
503  po->setTS(_pTileSet);
504  po->setLevel(this);
505 }*/
Playable Character.
Definition: laPlayer.h:46
laNamedClass * instantiate(std::string strClassName)
Instantiate class by name.
#define M_UNIT
Unit of 1 meter.
Base Class for Tileset Elements.
Definition: laElement.h:48
#define M_MAXOBJCNT
Count limitations.
Multi-state Level Object.
Definition: laStateObject.h:64
Level finsihing point collectable.
Definition: laFinal.h:41
Terrain Semgnet.
Definition: laSegment.h:48
2D Texture
Definition: laTexture.h:45
Collision Domain.
Object that can be activated by the player.
Adds capabilities and percepts specific to monster creatures.
Definition: laMonster.h:45
Gateway Level Object.
Definition: laGateway.h:43
#define REGISTER_CLASS(class_name)
Handy macro for registering named class prototype.
Error handling class.
Definition: laError.h:46
Base Class for Level Objects.
Definition: laObject.h:43
Tileset Class.
Definition: laTileset.h:46
File Parser.
Definition: laFileParser.h:41
Collectable Level Object.
Definition: laCollectable.h:43