Sort include order.
[gemrb.git] / gemrb / core / IniSpawn.cpp
blob9b6fda1b330033066a75a8adf11c179910bcea9a
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 "Actor.h"
29 #include "GSUtils.h"
30 #include "Game.h"
31 #include "GameData.h"
32 #include "Interface.h"
33 #include "Map.h"
35 static const int StatValues[9]={
36 IE_EA, IE_FACTION, IE_TEAM, IE_GENERAL, IE_RACE, IE_CLASS, IE_SPECIFIC,
37 IE_SEX, IE_ALIGNMENT };
39 IniSpawn::IniSpawn(Map *owner)
41 map = owner;
42 NamelessSpawnArea[0] = 0;
43 NamelessState = 35;
44 NamelessVar = NULL;
45 namelessvarcount = 0;
46 Locals = NULL;
47 localscount = 0;
48 eventspawns = NULL;
49 eventcount = 0;
50 last_spawndate = 0;
53 IniSpawn::~IniSpawn()
55 if (eventspawns) {
56 delete[] eventspawns;
60 Holder<DataFileMgr> GetIniFile(const ieResRef DefaultArea)
62 //the lack of spawn ini files is not a serious problem, happens all the time
63 if (!gamedata->Exists( DefaultArea, IE_INI_CLASS_ID)) {
64 return NULL;
67 DataStream* inifile = gamedata->GetResource( DefaultArea, IE_INI_CLASS_ID );
68 if (!inifile) {
69 return NULL;
71 if (!core->IsAvailable( IE_INI_CLASS_ID )) {
72 printStatus( "ERROR", LIGHT_RED );
73 printMessage( "IniSpawn","No INI Importer Available.\n",LIGHT_RED );
74 return NULL;
77 PluginHolder<DataFileMgr> ini(IE_INI_CLASS_ID);
78 ini->Open(inifile, true ); //autofree
79 return ini;
82 /*** initializations ***/
84 inline int CountElements(const char *s, char separator)
86 int ret = 1;
87 while(*s) {
88 if (*s==separator) ret++;
89 s++;
91 return ret;
94 inline void GetElements(const char *s, ieResRef *storage, int count)
96 while(count--) {
97 ieResRef *field = storage+count;
98 strnuprcpy(*field, s, sizeof(ieResRef)-1);
99 for(size_t i=0;i<sizeof(ieResRef) && (*field)[i];i++) {
100 if ((*field)[i]==',') {
101 (*field)[i]='\0';
102 break;
105 if (!count) break;
106 while(*s && *s!=',') s++;
107 s++;
108 if (*s==' ') s++; //this is because there is one single screwed up entry in ar1100.ini
112 inline void GetElements(const char *s, ieVariable *storage, int count)
114 while(count--) {
115 ieVariable *field = storage+count;
116 strnuprcpy(*field, s, sizeof(ieVariable)-1);
117 for(size_t i=0;i<sizeof(ieVariable) && (*field)[i];i++) {
118 if ((*field)[i]==',') {
119 (*field)[i]='\0';
120 break;
123 while(*s && *s!=',') s++;
124 s++;
128 // possible values implemented in DiffMode, but not needed here
129 // BINARY_LESS_OR_EQUALS 6 //(left has only bits in right)
130 // BINARY_MORE_OR_EQUALS 7 //(left has equal or more bits than right)
131 // BINARY_INTERSECT 8 //(left and right has at least one common bit)
132 // BINARY_NOT_INTERSECT 9 //(no common bits)
133 // BINARY_MORE 10 //left has more bits than right
134 // BINARY_LESS 11 //left has less bits than right
136 int IniSpawn::GetDiffMode(const char *keyword)
138 if (!keyword) return NO_OPERATION; //-1
139 if (keyword[0]==0) return NO_OPERATION; //-1
140 if (!stricmp(keyword,"less_or_equal_to") ) return LESS_OR_EQUALS; //0 (gemrb ext)
141 if (!stricmp(keyword,"equal_to") ) return EQUALS; // 1
142 if (!stricmp(keyword,"less_than") ) return LESS_THAN; // 2
143 if (!stricmp(keyword,"greater_than") ) return GREATER_THAN; //3
144 if (!stricmp(keyword,"greater_or_equal_to") ) return GREATER_THAN; //4 (gemrb ext)
145 if (!stricmp(keyword,"not_equal_to") ) return NOT_EQUALS; //5
146 return NO_OPERATION;
149 //unimplemented tags:
150 // check_crowd
151 // good_mod, law_mod, lady_mod, murder_mod
152 // control_var
153 // spec_area
154 // death_faction
155 // death_team
156 // check_by_view_port
157 // do_not_spawn
158 // time_of_day
159 // hold_selected_point_key
160 // inc_spawn_point_index
161 // find_safest_point
162 // exit
163 // spawn_time_of_day
164 // PST only
165 // auto_buddy
166 // detail_level
167 void IniSpawn::ReadCreature(DataFileMgr *inifile, const char *crittername, CritterEntry &critter)
169 const char *s;
170 int ps;
172 memset(&critter,0,sizeof(critter));
174 //all specvars are using global, but sometimes it is explicitly given
175 s = inifile->GetKeyAsString(crittername,"spec_var",NULL);
176 if (s) {
177 if ((strlen(s)>9) && s[6]==':' && s[7]==':') {
178 strnuprcpy(critter.SpecContext, s, 6);
179 strnlwrcpy(critter.SpecVar, s+8, 32);
180 } else {
181 strnuprcpy(critter.SpecContext, "GLOBAL", 6);
182 strnlwrcpy(critter.SpecVar, s, 32);
186 //add this to specvar at each spawn
187 ps = inifile->GetKeyAsInt(crittername,"spec_var_inc", 0);
188 critter.SpecVarInc=ps;
190 //use this value with spec_var_operation to determine spawn
191 ps = inifile->GetKeyAsInt(crittername,"spec_var_value",0);
192 critter.SpecVarValue=ps;
193 //this operation uses DiffCore
194 s = inifile->GetKeyAsString(crittername,"spec_var_operation","");
195 critter.SpecVarOperator=GetDiffMode(s);
196 //the amount of critters to spawn
197 critter.TotalQuantity = inifile->GetKeyAsInt(crittername,"spec_qty",1);
198 critter.SpawnCount = inifile->GetKeyAsInt(crittername,"create_qty",critter.TotalQuantity);
200 //the creature resource(s)
201 s = inifile->GetKeyAsString(crittername,"cre_file",NULL);
202 if (s) {
203 critter.creaturecount = CountElements(s,',');
204 critter.CreFile=new ieResRef[critter.creaturecount];
205 GetElements(s, critter.CreFile, critter.creaturecount);
206 } else {
207 printMessage( "IniSpawn"," ", LIGHT_RED);
208 printf("Invalid spawn entry: %s\n", crittername);
211 s = inifile->GetKeyAsString(crittername,"point_select",NULL);
213 if (s) {
214 ps=s[0];
215 } else {
216 ps=0;
219 s = inifile->GetKeyAsString(crittername,"spawn_point",NULL);
220 if (s) {
221 //expect more than one spawnpoint
222 if (ps=='r') {
223 //select one of the spawnpoints randomly
224 int count = core->Roll(1,CountElements(s,']'),-1);
225 //go to the selected spawnpoint
226 while(count--) {
227 while(*s++!=']') ;
230 //parse the selected spawnpoint
231 int x,y,o;
232 if (sscanf(s,"[%d.%d:%d]", &x, &y, &o)==3) {
233 critter.SpawnPoint.x=(short) x;
234 critter.SpawnPoint.y=(short) y;
235 critter.Orientation=o;
236 } else {
237 if (sscanf(s,"[%d.%d]", &x, &y)==2) {
238 critter.SpawnPoint.x=(short) x;
239 critter.SpawnPoint.y=(short) y;
240 critter.Orientation=core->Roll(1,16,-1);
245 //store or retrieve spawn point
246 s = inifile->GetKeyAsString(crittername,"spawn_point_global", NULL);
247 if (s) {
248 switch (ps) {
249 case 'e':
250 critter.SpawnPoint.fromDword(CheckVariable(map, s+8,s));
251 break;
252 default:
253 //see save_selected_point
254 //SetVariable(map, s+8, s, critter.SpawnPoint.asDword());
255 break;
259 //take facing from variable
260 s = inifile->GetKeyAsString(crittername,"spawn_facing_global", NULL);
261 if (s) {
262 switch (ps) {
263 case 'e':
264 critter.Orientation=(int) CheckVariable(map, s+8,s);
265 break;
266 default:
267 //see save_selected_point
268 //SetVariable(map, s+8, s, (ieDword) critter.Orientation);
269 break;
273 s = inifile->GetKeyAsString(crittername,"save_selected_point",NULL);
274 if (s) {
275 if ((strlen(s)>9) && s[6]==':' && s[7]==':') {
276 SetVariable(map, s+8, s, critter.SpawnPoint.asDword());
277 } else {
278 SetVariable(map, s, "GLOBAL", critter.SpawnPoint.asDword());
281 s = inifile->GetKeyAsString(crittername,"save_selected_facing",NULL);
282 if (s) {
283 if ((strlen(s)>9) && s[6]==':' && s[7]==':') {
284 SetVariable(map, s+8, s, (ieDword) critter.Orientation);
285 } else {
286 SetVariable(map, s, "GLOBAL", (ieDword) critter.Orientation);
290 //sometimes only the orientation is given, the point is stored in a variable
291 ps = inifile->GetKeyAsInt(crittername,"facing",-1);
292 if (ps!=-1) critter.Orientation = ps;
293 ps = inifile->GetKeyAsInt(crittername, "ai_ea",-1);
294 if (ps!=-1) critter.SetSpec[AI_EA] = (ieByte) ps;
295 ps = inifile->GetKeyAsInt(crittername, "ai_team",-1);
296 if (ps!=-1) critter.SetSpec[AI_TEAM] = (ieByte) ps;
297 ps = inifile->GetKeyAsInt(crittername, "ai_general",-1);
298 if (ps!=-1) critter.SetSpec[AI_GENERAL] = (ieByte) ps;
299 ps = inifile->GetKeyAsInt(crittername, "ai_race",-1);
300 if (ps!=-1) critter.SetSpec[AI_RACE] = (ieByte) ps;
301 ps = inifile->GetKeyAsInt(crittername, "ai_class",-1);
302 if (ps!=-1) critter.SetSpec[AI_CLASS] = (ieByte) ps;
303 ps = inifile->GetKeyAsInt(crittername, "ai_specifics",-1);
304 if (ps!=-1) critter.SetSpec[AI_SPECIFICS] = (ieByte) ps;
305 ps = inifile->GetKeyAsInt(crittername, "ai_gender",-1);
306 if (ps!=-1) critter.SetSpec[AI_GENDER] = (ieByte) ps;
307 ps = inifile->GetKeyAsInt(crittername, "ai_alignment",-1);
308 if (ps!=-1) critter.SetSpec[AI_ALIGNMENT] = (ieByte) ps;
310 s = inifile->GetKeyAsString(crittername,"spec",NULL);
311 if (s) {
312 int x[9];
314 ps = sscanf(s,"[%d.%d.%d.%d.%d.%d.%d.%d.%d]", x, x+1, x+2, x+3, x+4, x+5,
315 x+6, x+7, x+8);
316 if (ps == 0) {
317 strnuprcpy(critter.ScriptName, s, 32);
318 critter.Flags|=CF_CHECK_NAME;
319 memset(critter.Spec,-1,sizeof(critter.Spec));
320 } else {
321 while(ps--) {
322 critter.Spec[ps]=(ieByte) x[ps];
327 s = inifile->GetKeyAsString(crittername,"script_name",NULL);
328 if (s) {
329 strnuprcpy(critter.ScriptName, s, 32);
332 //iwd2 script names (override remains the same)
333 //special 1 == area
334 s = inifile->GetKeyAsString(crittername,"script_special_1",NULL);
335 if (s) {
336 strnuprcpy(critter.AreaScript,s, 8);
338 //special 2 == class
339 s = inifile->GetKeyAsString(crittername,"script_special_2",NULL);
340 if (s) {
341 strnuprcpy(critter.ClassScript,s, 8);
343 //special 3 == general
344 s = inifile->GetKeyAsString(crittername,"script_special_3",NULL);
345 if (s) {
346 strnuprcpy(critter.GeneralScript,s, 8);
348 //team == specific
349 s = inifile->GetKeyAsString(crittername,"script_team",NULL);
350 if (s) {
351 strnuprcpy(critter.SpecificScript,s, 8);
354 //combat == race
355 s = inifile->GetKeyAsString(crittername,"script_combat",NULL);
356 if (s) {
357 strnuprcpy(critter.RaceScript,s, 8);
359 //movement == default
360 s = inifile->GetKeyAsString(crittername,"script_movement",NULL);
361 if (s) {
362 strnuprcpy(critter.DefaultScript,s, 8);
365 //pst script names
366 s = inifile->GetKeyAsString(crittername,"script_override",NULL);
367 if (s) {
368 strnuprcpy(critter.OverrideScript,s, 8);
370 s = inifile->GetKeyAsString(crittername,"script_class",NULL);
371 if (s) {
372 strnuprcpy(critter.ClassScript,s, 8);
374 s = inifile->GetKeyAsString(crittername,"script_race",NULL);
375 if (s) {
376 strnuprcpy(critter.RaceScript,s, 8);
378 s = inifile->GetKeyAsString(crittername,"script_general",NULL);
379 if (s) {
380 strnuprcpy(critter.GeneralScript,s, 8);
382 s = inifile->GetKeyAsString(crittername,"script_default",NULL);
383 if (s) {
384 strnuprcpy(critter.DefaultScript,s, 8);
386 s = inifile->GetKeyAsString(crittername,"script_area",NULL);
387 if (s) {
388 strnuprcpy(critter.AreaScript,s, 8);
390 s = inifile->GetKeyAsString(crittername,"script_specifics",NULL);
391 if (s) {
392 strnuprcpy(critter.SpecificScript,s, 8);
394 s = inifile->GetKeyAsString(crittername,"dialog",NULL);
395 if (s) {
396 strnuprcpy(critter.Dialog,s, 8);
399 //flags
400 if (inifile->GetKeyAsBool(crittername,"death_scriptname",false)) {
401 critter.Flags|=CF_DEATHVAR;
403 //don't spawn when spawnpoint is visible
404 if (inifile->GetKeyAsBool(crittername,"ignore_can_see",false)) {
405 critter.Flags|=CF_IGNORECANSEE;
407 //unsure, but could be similar to previous
408 if (inifile->GetKeyAsBool(crittername,"check_view_port", false)) {
409 critter.Flags|=CF_CHECKVIEWPORT;
411 //unknown, this is used only in pst
412 if (inifile->GetKeyAsBool(crittername,"check_crowd", false)) {
413 critter.Flags|=CF_CHECKCROWD;
415 //unknown, this is used only in pst
416 if (inifile->GetKeyAsBool(crittername,"find_safest_point", false)) {
417 critter.Flags|=CF_SAFESTPOINT;
419 //disable spawn based on game difficulty
420 if (inifile->GetKeyAsBool(crittername,"area_diff_1", false)) {
421 critter.Flags|=CF_NO_DIFF_1;
423 if (inifile->GetKeyAsBool(crittername,"area_diff_2", false)) {
424 critter.Flags|=CF_NO_DIFF_2;
426 if (inifile->GetKeyAsBool(crittername,"area_diff_3", false)) {
427 critter.Flags|=CF_NO_DIFF_3;
431 void IniSpawn::ReadSpawnEntry(DataFileMgr *inifile, const char *entryname, SpawnEntry &entry)
433 const char *s;
435 entry.interval = (unsigned int) inifile->GetKeyAsInt(entryname,"interval",0);
436 //don't default to NULL here, some entries may be missing in original game
437 //an empty default string here will create an empty but consistent entry
438 s = inifile->GetKeyAsString(entryname,"critters","");
439 int crittercount = CountElements(s,',');
440 entry.crittercount=crittercount;
441 entry.critters=new CritterEntry[crittercount];
442 ieVariable *critters = new ieVariable[crittercount];
443 GetElements(s, critters, crittercount);
444 while(crittercount--) {
445 ReadCreature(inifile, critters[crittercount], entry.critters[crittercount]);
447 delete[] critters;
450 void IniSpawn::InitSpawn(const ieResRef DefaultArea)
452 const char *s;
454 Holder<DataFileMgr> inifile = GetIniFile(DefaultArea);
455 if (!inifile) {
456 strnuprcpy(NamelessSpawnArea, DefaultArea, 8);
457 return;
460 s = inifile->GetKeyAsString("nameless","destare",DefaultArea);
461 strnuprcpy(NamelessSpawnArea, s, 8);
462 s = inifile->GetKeyAsString("nameless","point","[0.0]");
463 int x,y;
464 if (sscanf(s,"[%d.%d]", &x, &y)!=2) {
465 x=0;
466 y=0;
468 NamelessSpawnPoint.x=x;
469 NamelessSpawnPoint.y=y;
470 //35 - already standing
471 //36 - getting up
472 NamelessState = inifile->GetKeyAsInt("nameless","state",36);
474 namelessvarcount = inifile->GetKeysCount("namelessvar");
475 if (namelessvarcount) {
476 NamelessVar = new VariableSpec[namelessvarcount];
477 for (y=0;y<namelessvarcount;y++) {
478 const char* Key = inifile->GetKeyNameByIndex("namelessvar",y);
479 strnlwrcpy(NamelessVar[y].Name, Key, 32);
480 NamelessVar[y].Value = inifile->GetKeyAsInt("namelessvar",Key,0);
484 localscount = inifile->GetKeysCount("locals");
485 if (localscount) {
486 Locals = new VariableSpec[localscount];
487 for (y=0;y<localscount;y++) {
488 const char* Key = inifile->GetKeyNameByIndex("locals",y);
489 strnlwrcpy(Locals[y].Name, Key, 32);
490 Locals[y].Value = inifile->GetKeyAsInt("locals",Key,0);
494 s = inifile->GetKeyAsString("spawn_main","enter",NULL);
495 if (s) {
496 ReadSpawnEntry(inifile.get(), s, enterspawn);
498 s = inifile->GetKeyAsString("spawn_main","events",NULL);
499 if (s) {
500 eventcount = CountElements(s,',');
501 eventspawns = new SpawnEntry[eventcount];
502 ieVariable *events = new ieVariable[eventcount];
503 GetElements(s, events, eventcount);
504 int ec = eventcount;
505 while(ec--) {
506 ReadSpawnEntry(inifile.get(), events[ec], eventspawns[ec]);
508 delete[] events;
510 //maybe not correct
511 InitialSpawn();
515 /*** events ***/
517 //respawn nameless after he bit the dust
518 void IniSpawn::RespawnNameless()
520 Game *game = core->GetGame();
521 Actor *nameless = game->GetPC(0, false);
523 if (NamelessSpawnPoint.isnull()) {
524 core->GetGame()->JoinParty(nameless,JP_INITPOS);
525 NamelessSpawnPoint=nameless->Pos;
526 strnuprcpy(NamelessSpawnArea, nameless->Area, 8);
529 nameless->Resurrect();
530 //hardcoded!!!
531 if (NamelessState==36) {
532 nameless->SetStance(IE_ANI_PST_START);
534 int i;
536 for (i=0;i<game->GetPartySize(false);i++) {
537 MoveBetweenAreasCore(game->GetPC(i, false),NamelessSpawnArea,NamelessSpawnPoint,-1, true);
540 //certain variables are set when nameless dies
541 for (i=0;i<namelessvarcount;i++) {
542 SetVariable(game, NamelessVar[i].Name,"GLOBAL", NamelessVar[i].Value);
546 void IniSpawn::SpawnCreature(CritterEntry &critter)
548 if (!critter.creaturecount) {
549 return;
552 ieDword specvar = CheckVariable(map, critter.SpecVar, critter.SpecContext);
554 if (critter.SpecVar[0]) {
555 if (critter.SpecVarOperator>=0) {
556 // dunno if this should be negated
557 if (!DiffCore(specvar, critter.SpecVarValue, critter.SpecVarOperator) ) {
558 return;
560 } else {
561 //ar0203 in PST seems to want the check this way.
562 //if other areas conflict and you want to use (!specvar),
563 //please research further
564 //researched further - ar0203 respawns only if specvar is 1
565 if (!specvar) {
566 return;
571 if (!(critter.Flags&CF_IGNORECANSEE)) {
572 if (map->IsVisible(critter.SpawnPoint, false) ) {
573 return;
577 if (critter.Flags&CF_NO_DIFF_MASK) {
578 ieDword difficulty;
579 ieDword diff_bit;
581 core->GetDictionary()->Lookup("Difficulty Level", difficulty);
582 switch (difficulty)
584 case 0:
585 diff_bit = CF_NO_DIFF_1;
586 break;
587 case 1:
588 diff_bit = CF_NO_DIFF_2;
589 break;
590 case 2:
591 diff_bit = CF_NO_DIFF_3;
592 break;
593 default:
594 diff_bit = 0;
596 if (critter.Flags&diff_bit) {
597 return;
601 if (critter.ScriptName[0] && (critter.Flags&CF_CHECK_NAME) ) {
602 //maybe this one needs to be using getobjectcount as well
603 //currently we cannot count objects with scriptname???
604 if (map->GetActor( critter.ScriptName, 0 )) {
605 return;
607 } else {
608 //Object *object = new Object();
609 Object object;
610 //objectfields based on spec
611 object.objectFields[0]=critter.Spec[0];
612 object.objectFields[1]=critter.Spec[1];
613 object.objectFields[2]=critter.Spec[2];
614 object.objectFields[3]=critter.Spec[3];
615 object.objectFields[4]=critter.Spec[4];
616 object.objectFields[5]=critter.Spec[5];
617 object.objectFields[6]=critter.Spec[6];
618 object.objectFields[7]=critter.Spec[7];
619 object.objectFields[8]=critter.Spec[8];
620 int cnt = GetObjectCount(map, &object);
621 if (cnt>=critter.TotalQuantity) {
622 return;
626 int x = core->Roll(1,critter.creaturecount,-1);
627 Actor* cre = gamedata->GetCreature(critter.CreFile[x]);
628 if (!cre) {
629 return;
632 SetVariable(map, critter.SpecVar, critter.SpecContext, specvar+(ieDword) critter.SpecVarInc);
633 map->AddActor(cre);
634 for (x=0;x<9;x++) {
635 if (critter.SetSpec[x]) {
636 cre->SetBase(StatValues[x], critter.SetSpec[x]);
639 cre->SetPosition( critter.SpawnPoint, 0, 0);//maybe critters could be repositioned
640 cre->SetOrientation(critter.Orientation,false);
641 if (critter.ScriptName[0]) {
642 cre->SetScriptName(critter.ScriptName);
644 if (critter.OverrideScript[0]) {
645 cre->SetScript(critter.OverrideScript, SCR_OVERRIDE);
647 if (critter.ClassScript[0]) {
648 cre->SetScript(critter.ClassScript, SCR_CLASS);
650 if (critter.RaceScript[0]) {
651 cre->SetScript(critter.RaceScript, SCR_RACE);
653 if (critter.GeneralScript[0]) {
654 cre->SetScript(critter.GeneralScript, SCR_GENERAL);
656 if (critter.DefaultScript[0]) {
657 cre->SetScript(critter.DefaultScript, SCR_DEFAULT);
659 if (critter.AreaScript[0]) {
660 cre->SetScript(critter.AreaScript, SCR_AREA);
662 if (critter.SpecificScript[0]) {
663 cre->SetScript(critter.SpecificScript, SCR_SPECIFICS);
665 if (critter.Dialog[0]) {
666 cre->SetDialog(critter.Dialog);
670 void IniSpawn::SpawnGroup(SpawnEntry &event)
672 if (!event.critters) {
673 return;
675 unsigned int interval = event.interval;
676 if (interval) {
677 if(core->GetGame()->GameTime/interval<=last_spawndate/interval) {
678 return;
681 last_spawndate=core->GetGame()->GameTime;
683 for(int i=0;i<event.crittercount;i++) {
684 CritterEntry* critter = event.critters+i;
685 for(int j=0;j<critter->SpawnCount;j++) {
686 SpawnCreature(*critter);
691 //execute the initial spawn
692 void IniSpawn::InitialSpawn()
694 SpawnGroup(enterspawn);
695 //these variables are set when entering first
696 for (int i=0;i<localscount;i++) {
697 SetVariable(map, Locals[i].Name,"LOCALS", Locals[i].Value);
701 //checks if a respawn event occurred
702 void IniSpawn::CheckSpawn()
704 for(int i=0;i<eventcount;i++) {
705 SpawnGroup(eventspawns[i]);