SetupWishCore: fixed table lookup being out of bounds for the wisest pcs
[gemrb.git] / gemrb / core / IniSpawn.cpp
blob04a68a0eea18aef8623d1973de0a1b2869ce5436
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2007 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 // This class handles the special spawn structures of planescape torment
22 // (stored in .ini format)
24 #include "IniSpawn.h"
26 #include "win32def.h"
28 #include "Game.h"
29 #include "GameData.h"
30 #include "Interface.h"
31 #include "Map.h"
32 #include "GameScript/GSUtils.h"
33 #include "GameScript/Matching.h"
34 #include "Scriptable/Actor.h"
36 static const int StatValues[9]={
37 IE_EA, IE_FACTION, IE_TEAM, IE_GENERAL, IE_RACE, IE_CLASS, IE_SPECIFIC,
38 IE_SEX, IE_ALIGNMENT };
40 IniSpawn::IniSpawn(Map *owner)
42 map = owner;
43 NamelessSpawnArea[0] = 0;
44 NamelessState = 35;
45 NamelessVar = NULL;
46 namelessvarcount = 0;
47 Locals = NULL;
48 localscount = 0;
49 eventspawns = NULL;
50 eventcount = 0;
51 last_spawndate = 0;
54 IniSpawn::~IniSpawn()
56 if (eventspawns) {
57 delete[] eventspawns;
61 Holder<DataFileMgr> GetIniFile(const ieResRef DefaultArea)
63 //the lack of spawn ini files is not a serious problem, happens all the time
64 if (!gamedata->Exists( DefaultArea, IE_INI_CLASS_ID)) {
65 return NULL;
68 DataStream* inifile = gamedata->GetResource( DefaultArea, IE_INI_CLASS_ID );
69 if (!inifile) {
70 return NULL;
72 if (!core->IsAvailable( IE_INI_CLASS_ID )) {
73 printStatus( "ERROR", LIGHT_RED );
74 printMessage( "IniSpawn","No INI Importer Available.\n",LIGHT_RED );
75 return NULL;
78 PluginHolder<DataFileMgr> ini(IE_INI_CLASS_ID);
79 ini->Open(inifile, true ); //autofree
80 return ini;
83 /*** initializations ***/
85 inline int CountElements(const char *s, char separator)
87 int ret = 1;
88 while(*s) {
89 if (*s==separator) ret++;
90 s++;
92 return ret;
95 inline void GetElements(const char *s, ieResRef *storage, int count)
97 while(count--) {
98 ieResRef *field = storage+count;
99 strnuprcpy(*field, s, sizeof(ieResRef)-1);
100 for(size_t i=0;i<sizeof(ieResRef) && (*field)[i];i++) {
101 if ((*field)[i]==',') {
102 (*field)[i]='\0';
103 break;
106 if (!count) break;
107 while(*s && *s!=',') s++;
108 s++;
109 if (*s==' ') s++; //this is because there is one single screwed up entry in ar1100.ini
113 inline void GetElements(const char *s, ieVariable *storage, int count)
115 while(count--) {
116 ieVariable *field = storage+count;
117 strnuprcpy(*field, s, sizeof(ieVariable)-1);
118 for(size_t i=0;i<sizeof(ieVariable) && (*field)[i];i++) {
119 if ((*field)[i]==',') {
120 (*field)[i]='\0';
121 break;
124 while(*s && *s!=',') s++;
125 s++;
129 // possible values implemented in DiffMode, but not needed here
130 // BINARY_LESS_OR_EQUALS 6 //(left has only bits in right)
131 // BINARY_MORE_OR_EQUALS 7 //(left has equal or more bits than right)
132 // BINARY_INTERSECT 8 //(left and right has at least one common bit)
133 // BINARY_NOT_INTERSECT 9 //(no common bits)
134 // BINARY_MORE 10 //left has more bits than right
135 // BINARY_LESS 11 //left has less bits than right
137 int IniSpawn::GetDiffMode(const char *keyword)
139 if (!keyword) return NO_OPERATION; //-1
140 if (keyword[0]==0) return NO_OPERATION; //-1
141 if (!stricmp(keyword,"less_or_equal_to") ) return LESS_OR_EQUALS; //0 (gemrb ext)
142 if (!stricmp(keyword,"equal_to") ) return EQUALS; // 1
143 if (!stricmp(keyword,"less_than") ) return LESS_THAN; // 2
144 if (!stricmp(keyword,"greater_than") ) return GREATER_THAN; //3
145 if (!stricmp(keyword,"greater_or_equal_to") ) return GREATER_THAN; //4 (gemrb ext)
146 if (!stricmp(keyword,"not_equal_to") ) return NOT_EQUALS; //5
147 return NO_OPERATION;
150 //unimplemented tags:
151 // check_crowd
152 // good_mod, law_mod, lady_mod, murder_mod
153 // control_var
154 // spec_area
155 // death_faction
156 // death_team
157 // check_by_view_port
158 // do_not_spawn
159 // time_of_day
160 // hold_selected_point_key
161 // inc_spawn_point_index
162 // find_safest_point
163 // exit
164 // spawn_time_of_day
165 // PST only
166 // auto_buddy
167 // detail_level
168 void IniSpawn::ReadCreature(DataFileMgr *inifile, const char *crittername, CritterEntry &critter)
170 const char *s;
171 int ps;
173 memset(&critter,0,sizeof(critter));
175 //all specvars are using global, but sometimes it is explicitly given
176 s = inifile->GetKeyAsString(crittername,"spec_var",NULL);
177 if (s) {
178 if ((strlen(s)>9) && s[6]==':' && s[7]==':') {
179 strnuprcpy(critter.SpecContext, s, 6);
180 strnlwrcpy(critter.SpecVar, s+8, 32);
181 } else {
182 strnuprcpy(critter.SpecContext, "GLOBAL", 6);
183 strnlwrcpy(critter.SpecVar, s, 32);
187 //add this to specvar at each spawn
188 ps = inifile->GetKeyAsInt(crittername,"spec_var_inc", 0);
189 critter.SpecVarInc=ps;
191 //use this value with spec_var_operation to determine spawn
192 ps = inifile->GetKeyAsInt(crittername,"spec_var_value",0);
193 critter.SpecVarValue=ps;
194 //this operation uses DiffCore
195 s = inifile->GetKeyAsString(crittername,"spec_var_operation","");
196 critter.SpecVarOperator=GetDiffMode(s);
197 //the amount of critters to spawn
198 critter.TotalQuantity = inifile->GetKeyAsInt(crittername,"spec_qty",1);
199 critter.SpawnCount = inifile->GetKeyAsInt(crittername,"create_qty",critter.TotalQuantity);
201 //the creature resource(s)
202 s = inifile->GetKeyAsString(crittername,"cre_file",NULL);
203 if (s) {
204 critter.creaturecount = CountElements(s,',');
205 critter.CreFile=new ieResRef[critter.creaturecount];
206 GetElements(s, critter.CreFile, critter.creaturecount);
207 } else {
208 printMessage( "IniSpawn"," ", LIGHT_RED);
209 printf("Invalid spawn entry: %s\n", crittername);
212 s = inifile->GetKeyAsString(crittername,"point_select",NULL);
214 if (s) {
215 ps=s[0];
216 } else {
217 ps=0;
220 s = inifile->GetKeyAsString(crittername,"spawn_point",NULL);
221 if (s) {
222 //expect more than one spawnpoint
223 if (ps=='r') {
224 //select one of the spawnpoints randomly
225 int count = core->Roll(1,CountElements(s,']'),-1);
226 //go to the selected spawnpoint
227 while(count--) {
228 while(*s++!=']') ;
231 //parse the selected spawnpoint
232 int x,y,o;
233 if (sscanf(s,"[%d.%d:%d]", &x, &y, &o)==3) {
234 critter.SpawnPoint.x=(short) x;
235 critter.SpawnPoint.y=(short) y;
236 critter.Orientation=o;
237 } else {
238 if (sscanf(s,"[%d.%d]", &x, &y)==2) {
239 critter.SpawnPoint.x=(short) x;
240 critter.SpawnPoint.y=(short) y;
241 critter.Orientation=core->Roll(1,16,-1);
246 //store or retrieve spawn point
247 s = inifile->GetKeyAsString(crittername,"spawn_point_global", NULL);
248 if (s) {
249 switch (ps) {
250 case 'e':
251 critter.SpawnPoint.fromDword(CheckVariable(map, s+8,s));
252 break;
253 default:
254 //see save_selected_point
255 //SetVariable(map, s+8, s, critter.SpawnPoint.asDword());
256 break;
260 //take facing from variable
261 s = inifile->GetKeyAsString(crittername,"spawn_facing_global", NULL);
262 if (s) {
263 switch (ps) {
264 case 'e':
265 critter.Orientation=(int) CheckVariable(map, s+8,s);
266 break;
267 default:
268 //see save_selected_point
269 //SetVariable(map, s+8, s, (ieDword) critter.Orientation);
270 break;
274 s = inifile->GetKeyAsString(crittername,"save_selected_point",NULL);
275 if (s) {
276 if ((strlen(s)>9) && s[6]==':' && s[7]==':') {
277 SetVariable(map, s+8, s, critter.SpawnPoint.asDword());
278 } else {
279 SetVariable(map, s, "GLOBAL", critter.SpawnPoint.asDword());
282 s = inifile->GetKeyAsString(crittername,"save_selected_facing",NULL);
283 if (s) {
284 if ((strlen(s)>9) && s[6]==':' && s[7]==':') {
285 SetVariable(map, s+8, s, (ieDword) critter.Orientation);
286 } else {
287 SetVariable(map, s, "GLOBAL", (ieDword) critter.Orientation);
291 //sometimes only the orientation is given, the point is stored in a variable
292 ps = inifile->GetKeyAsInt(crittername,"facing",-1);
293 if (ps!=-1) critter.Orientation = ps;
294 ps = inifile->GetKeyAsInt(crittername, "ai_ea",-1);
295 if (ps!=-1) critter.SetSpec[AI_EA] = (ieByte) ps;
296 ps = inifile->GetKeyAsInt(crittername, "ai_team",-1);
297 if (ps!=-1) critter.SetSpec[AI_TEAM] = (ieByte) ps;
298 ps = inifile->GetKeyAsInt(crittername, "ai_general",-1);
299 if (ps!=-1) critter.SetSpec[AI_GENERAL] = (ieByte) ps;
300 ps = inifile->GetKeyAsInt(crittername, "ai_race",-1);
301 if (ps!=-1) critter.SetSpec[AI_RACE] = (ieByte) ps;
302 ps = inifile->GetKeyAsInt(crittername, "ai_class",-1);
303 if (ps!=-1) critter.SetSpec[AI_CLASS] = (ieByte) ps;
304 ps = inifile->GetKeyAsInt(crittername, "ai_specifics",-1);
305 if (ps!=-1) critter.SetSpec[AI_SPECIFICS] = (ieByte) ps;
306 ps = inifile->GetKeyAsInt(crittername, "ai_gender",-1);
307 if (ps!=-1) critter.SetSpec[AI_GENDER] = (ieByte) ps;
308 ps = inifile->GetKeyAsInt(crittername, "ai_alignment",-1);
309 if (ps!=-1) critter.SetSpec[AI_ALIGNMENT] = (ieByte) ps;
311 s = inifile->GetKeyAsString(crittername,"spec",NULL);
312 if (s) {
313 int x[9];
315 ps = sscanf(s,"[%d.%d.%d.%d.%d.%d.%d.%d.%d]", x, x+1, x+2, x+3, x+4, x+5,
316 x+6, x+7, x+8);
317 if (ps == 0) {
318 strnuprcpy(critter.ScriptName, s, 32);
319 critter.Flags|=CF_CHECK_NAME;
320 memset(critter.Spec,-1,sizeof(critter.Spec));
321 } else {
322 while(ps--) {
323 critter.Spec[ps]=(ieByte) x[ps];
328 s = inifile->GetKeyAsString(crittername,"script_name",NULL);
329 if (s) {
330 strnuprcpy(critter.ScriptName, s, 32);
333 //iwd2 script names (override remains the same)
334 //special 1 == area
335 s = inifile->GetKeyAsString(crittername,"script_special_1",NULL);
336 if (s) {
337 strnuprcpy(critter.AreaScript,s, 8);
339 //special 2 == class
340 s = inifile->GetKeyAsString(crittername,"script_special_2",NULL);
341 if (s) {
342 strnuprcpy(critter.ClassScript,s, 8);
344 //special 3 == general
345 s = inifile->GetKeyAsString(crittername,"script_special_3",NULL);
346 if (s) {
347 strnuprcpy(critter.GeneralScript,s, 8);
349 //team == specific
350 s = inifile->GetKeyAsString(crittername,"script_team",NULL);
351 if (s) {
352 strnuprcpy(critter.SpecificScript,s, 8);
355 //combat == race
356 s = inifile->GetKeyAsString(crittername,"script_combat",NULL);
357 if (s) {
358 strnuprcpy(critter.RaceScript,s, 8);
360 //movement == default
361 s = inifile->GetKeyAsString(crittername,"script_movement",NULL);
362 if (s) {
363 strnuprcpy(critter.DefaultScript,s, 8);
366 //pst script names
367 s = inifile->GetKeyAsString(crittername,"script_override",NULL);
368 if (s) {
369 strnuprcpy(critter.OverrideScript,s, 8);
371 s = inifile->GetKeyAsString(crittername,"script_class",NULL);
372 if (s) {
373 strnuprcpy(critter.ClassScript,s, 8);
375 s = inifile->GetKeyAsString(crittername,"script_race",NULL);
376 if (s) {
377 strnuprcpy(critter.RaceScript,s, 8);
379 s = inifile->GetKeyAsString(crittername,"script_general",NULL);
380 if (s) {
381 strnuprcpy(critter.GeneralScript,s, 8);
383 s = inifile->GetKeyAsString(crittername,"script_default",NULL);
384 if (s) {
385 strnuprcpy(critter.DefaultScript,s, 8);
387 s = inifile->GetKeyAsString(crittername,"script_area",NULL);
388 if (s) {
389 strnuprcpy(critter.AreaScript,s, 8);
391 s = inifile->GetKeyAsString(crittername,"script_specifics",NULL);
392 if (s) {
393 strnuprcpy(critter.SpecificScript,s, 8);
395 s = inifile->GetKeyAsString(crittername,"dialog",NULL);
396 if (s) {
397 strnuprcpy(critter.Dialog,s, 8);
400 //flags
401 if (inifile->GetKeyAsBool(crittername,"death_scriptname",false)) {
402 critter.Flags|=CF_DEATHVAR;
404 //don't spawn when spawnpoint is visible
405 if (inifile->GetKeyAsBool(crittername,"ignore_can_see",false)) {
406 critter.Flags|=CF_IGNORECANSEE;
408 //unsure, but could be similar to previous
409 if (inifile->GetKeyAsBool(crittername,"check_view_port", false)) {
410 critter.Flags|=CF_CHECKVIEWPORT;
412 //unknown, this is used only in pst
413 if (inifile->GetKeyAsBool(crittername,"check_crowd", false)) {
414 critter.Flags|=CF_CHECKCROWD;
416 //unknown, this is used only in pst
417 if (inifile->GetKeyAsBool(crittername,"find_safest_point", false)) {
418 critter.Flags|=CF_SAFESTPOINT;
420 //disable spawn based on game difficulty
421 if (inifile->GetKeyAsBool(crittername,"area_diff_1", false)) {
422 critter.Flags|=CF_NO_DIFF_1;
424 if (inifile->GetKeyAsBool(crittername,"area_diff_2", false)) {
425 critter.Flags|=CF_NO_DIFF_2;
427 if (inifile->GetKeyAsBool(crittername,"area_diff_3", false)) {
428 critter.Flags|=CF_NO_DIFF_3;
432 void IniSpawn::ReadSpawnEntry(DataFileMgr *inifile, const char *entryname, SpawnEntry &entry)
434 const char *s;
436 entry.interval = (unsigned int) inifile->GetKeyAsInt(entryname,"interval",0);
437 //don't default to NULL here, some entries may be missing in original game
438 //an empty default string here will create an empty but consistent entry
439 s = inifile->GetKeyAsString(entryname,"critters","");
440 int crittercount = CountElements(s,',');
441 entry.crittercount=crittercount;
442 entry.critters=new CritterEntry[crittercount];
443 ieVariable *critters = new ieVariable[crittercount];
444 GetElements(s, critters, crittercount);
445 while(crittercount--) {
446 ReadCreature(inifile, critters[crittercount], entry.critters[crittercount]);
448 delete[] critters;
451 void IniSpawn::InitSpawn(const ieResRef DefaultArea)
453 const char *s;
455 Holder<DataFileMgr> inifile = GetIniFile(DefaultArea);
456 if (!inifile) {
457 strnuprcpy(NamelessSpawnArea, DefaultArea, 8);
458 return;
461 s = inifile->GetKeyAsString("nameless","destare",DefaultArea);
462 strnuprcpy(NamelessSpawnArea, s, 8);
463 s = inifile->GetKeyAsString("nameless","point","[0.0]");
464 int x,y;
465 if (sscanf(s,"[%d.%d]", &x, &y)!=2) {
466 x=0;
467 y=0;
469 NamelessSpawnPoint.x=x;
470 NamelessSpawnPoint.y=y;
471 //35 - already standing
472 //36 - getting up
473 NamelessState = inifile->GetKeyAsInt("nameless","state",36);
475 namelessvarcount = inifile->GetKeysCount("namelessvar");
476 if (namelessvarcount) {
477 NamelessVar = new VariableSpec[namelessvarcount];
478 for (y=0;y<namelessvarcount;y++) {
479 const char* Key = inifile->GetKeyNameByIndex("namelessvar",y);
480 strnlwrcpy(NamelessVar[y].Name, Key, 32);
481 NamelessVar[y].Value = inifile->GetKeyAsInt("namelessvar",Key,0);
485 localscount = inifile->GetKeysCount("locals");
486 if (localscount) {
487 Locals = new VariableSpec[localscount];
488 for (y=0;y<localscount;y++) {
489 const char* Key = inifile->GetKeyNameByIndex("locals",y);
490 strnlwrcpy(Locals[y].Name, Key, 32);
491 Locals[y].Value = inifile->GetKeyAsInt("locals",Key,0);
495 s = inifile->GetKeyAsString("spawn_main","enter",NULL);
496 if (s) {
497 ReadSpawnEntry(inifile.get(), s, enterspawn);
499 s = inifile->GetKeyAsString("spawn_main","events",NULL);
500 if (s) {
501 eventcount = CountElements(s,',');
502 eventspawns = new SpawnEntry[eventcount];
503 ieVariable *events = new ieVariable[eventcount];
504 GetElements(s, events, eventcount);
505 int ec = eventcount;
506 while(ec--) {
507 ReadSpawnEntry(inifile.get(), events[ec], eventspawns[ec]);
509 delete[] events;
511 //maybe not correct
512 InitialSpawn();
516 /*** events ***/
518 //respawn nameless after he bit the dust
519 void IniSpawn::RespawnNameless()
521 Game *game = core->GetGame();
522 Actor *nameless = game->GetPC(0, false);
524 if (NamelessSpawnPoint.isnull()) {
525 core->GetGame()->JoinParty(nameless,JP_INITPOS);
526 NamelessSpawnPoint=nameless->Pos;
527 strnuprcpy(NamelessSpawnArea, nameless->Area, 8);
530 nameless->Resurrect();
531 //hardcoded!!!
532 if (NamelessState==36) {
533 nameless->SetStance(IE_ANI_PST_START);
535 int i;
537 for (i=0;i<game->GetPartySize(false);i++) {
538 MoveBetweenAreasCore(game->GetPC(i, false),NamelessSpawnArea,NamelessSpawnPoint,-1, true);
541 //certain variables are set when nameless dies
542 for (i=0;i<namelessvarcount;i++) {
543 SetVariable(game, NamelessVar[i].Name,"GLOBAL", NamelessVar[i].Value);
547 void IniSpawn::SpawnCreature(CritterEntry &critter)
549 if (!critter.creaturecount) {
550 return;
553 ieDword specvar = CheckVariable(map, critter.SpecVar, critter.SpecContext);
555 if (critter.SpecVar[0]) {
556 if (critter.SpecVarOperator>=0) {
557 // dunno if this should be negated
558 if (!DiffCore(specvar, critter.SpecVarValue, critter.SpecVarOperator) ) {
559 return;
561 } else {
562 //ar0203 in PST seems to want the check this way.
563 //if other areas conflict and you want to use (!specvar),
564 //please research further
565 //researched further - ar0203 respawns only if specvar is 1
566 if (!specvar) {
567 return;
572 if (!(critter.Flags&CF_IGNORECANSEE)) {
573 if (map->IsVisible(critter.SpawnPoint, false) ) {
574 return;
578 if (critter.Flags&CF_NO_DIFF_MASK) {
579 ieDword difficulty;
580 ieDword diff_bit;
582 core->GetDictionary()->Lookup("Difficulty Level", difficulty);
583 switch (difficulty)
585 case 0:
586 diff_bit = CF_NO_DIFF_1;
587 break;
588 case 1:
589 diff_bit = CF_NO_DIFF_2;
590 break;
591 case 2:
592 diff_bit = CF_NO_DIFF_3;
593 break;
594 default:
595 diff_bit = 0;
597 if (critter.Flags&diff_bit) {
598 return;
602 if (critter.ScriptName[0] && (critter.Flags&CF_CHECK_NAME) ) {
603 //maybe this one needs to be using getobjectcount as well
604 //currently we cannot count objects with scriptname???
605 if (map->GetActor( critter.ScriptName, 0 )) {
606 return;
608 } else {
609 //Object *object = new Object();
610 Object object;
611 //objectfields based on spec
612 object.objectFields[0]=critter.Spec[0];
613 object.objectFields[1]=critter.Spec[1];
614 object.objectFields[2]=critter.Spec[2];
615 object.objectFields[3]=critter.Spec[3];
616 object.objectFields[4]=critter.Spec[4];
617 object.objectFields[5]=critter.Spec[5];
618 object.objectFields[6]=critter.Spec[6];
619 object.objectFields[7]=critter.Spec[7];
620 object.objectFields[8]=critter.Spec[8];
621 int cnt = GetObjectCount(map, &object);
622 if (cnt>=critter.TotalQuantity) {
623 return;
627 int x = core->Roll(1,critter.creaturecount,-1);
628 Actor* cre = gamedata->GetCreature(critter.CreFile[x]);
629 if (!cre) {
630 return;
633 SetVariable(map, critter.SpecVar, critter.SpecContext, specvar+(ieDword) critter.SpecVarInc);
634 map->AddActor(cre);
635 for (x=0;x<9;x++) {
636 if (critter.SetSpec[x]) {
637 cre->SetBase(StatValues[x], critter.SetSpec[x]);
640 cre->SetPosition( critter.SpawnPoint, 0, 0);//maybe critters could be repositioned
641 cre->SetOrientation(critter.Orientation,false);
642 if (critter.ScriptName[0]) {
643 cre->SetScriptName(critter.ScriptName);
645 if (critter.OverrideScript[0]) {
646 cre->SetScript(critter.OverrideScript, SCR_OVERRIDE);
648 if (critter.ClassScript[0]) {
649 cre->SetScript(critter.ClassScript, SCR_CLASS);
651 if (critter.RaceScript[0]) {
652 cre->SetScript(critter.RaceScript, SCR_RACE);
654 if (critter.GeneralScript[0]) {
655 cre->SetScript(critter.GeneralScript, SCR_GENERAL);
657 if (critter.DefaultScript[0]) {
658 cre->SetScript(critter.DefaultScript, SCR_DEFAULT);
660 if (critter.AreaScript[0]) {
661 cre->SetScript(critter.AreaScript, SCR_AREA);
663 if (critter.SpecificScript[0]) {
664 cre->SetScript(critter.SpecificScript, SCR_SPECIFICS);
666 if (critter.Dialog[0]) {
667 cre->SetDialog(critter.Dialog);
671 void IniSpawn::SpawnGroup(SpawnEntry &event)
673 if (!event.critters) {
674 return;
676 unsigned int interval = event.interval;
677 if (interval) {
678 if(core->GetGame()->GameTime/interval<=last_spawndate/interval) {
679 return;
682 last_spawndate=core->GetGame()->GameTime;
684 for(int i=0;i<event.crittercount;i++) {
685 CritterEntry* critter = event.critters+i;
686 for(int j=0;j<critter->SpawnCount;j++) {
687 SpawnCreature(*critter);
692 //execute the initial spawn
693 void IniSpawn::InitialSpawn()
695 SpawnGroup(enterspawn);
696 //these variables are set when entering first
697 for (int i=0;i<localscount;i++) {
698 SetVariable(map, Locals[i].Name,"LOCALS", Locals[i].Value);
702 //checks if a respawn event occurred
703 void IniSpawn::CheckSpawn()
705 for(int i=0;i<eventcount;i++) {
706 SpawnGroup(eventspawns[i]);