1 /*
2 ** thingdef.cpp
3 **
4 **---------------------------------------------------------------------------
5 ** Copyright 2011 Braden Obrzut
6 ** All rights reserved.
7 **
8 ** Redistribution and use in source and binary forms, with or without
9 ** modification, are permitted provided that the following conditions
10 ** are met:
11 **
12 ** 1. Redistributions of source code must retain the above copyright
13 ** notice, this list of conditions and the following disclaimer.
14 ** 2. Redistributions in binary form must reproduce the above copyright
15 ** notice, this list of conditions and the following disclaimer in the
16 ** documentation and/or other materials provided with the distribution.
17 ** 3. The name of the author may not be used to endorse or promote products
18 ** derived from this software without specific prior written permission.
19 **
20 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 **---------------------------------------------------------------------------
31 **
32 **
33 */
34
35 #include "actor.h"
36 #include "a_inventory.h"
37 #include "doomerrors.h"
38 #include "id_ca.h"
39 #include "lnspec.h"
40 #include "m_random.h"
41 #include "r_sprites.h"
42 #include "scanner.h"
43 #include "w_wad.h"
44 #include "wl_def.h"
45 #include "wl_draw.h"
46 #include "thingdef/thingdef.h"
47 #include "thingdef/thingdef_type.h"
48 #include "thingdef/thingdef_expression.h"
49 #include "thinker.h"
50 #include "templates.h"
51 #include "g_mapinfo.h"
52
53 #include <climits>
54
55 // Code pointer stuff
56 void InitFunctionTable(ActionTable *table);
57 void ReleaseFunctionTable();
58 ActionInfo *LookupFunction(const FName &func, const ActionTable *table);
59
60 typedef DWORD flagstype_t;
61
62 ////////////////////////////////////////////////////////////////////////////////
63
64 #define DEFINE_FLAG(prefix, flag, type, variable) { NATIVE_CLASS(type), prefix##_##flag, #type, #flag, typeoffsetof(A##type,variable) }
65 const struct FlagDef
66 {
67 public:
68 const ClassDef * const &cls;
69 const flagstype_t value;
70 const char* const prefix;
71 const char* const name;
72 const int varOffset;
73 } flags[] =
74 {
75 DEFINE_FLAG(FL, ALWAYSFAST, Actor, flags),
76 DEFINE_FLAG(WF, ALWAYSGRIN, Weapon, weaponFlags),
77 DEFINE_FLAG(IF, ALWAYSPICKUP, Inventory, itemFlags),
78 DEFINE_FLAG(FL, AMBUSH, Actor, flags),
79 DEFINE_FLAG(IF, AUTOACTIVATE, Inventory, itemFlags),
80 DEFINE_FLAG(FL, BRIGHT, Actor, flags),
81 DEFINE_FLAG(FL, CANUSEWALLS, Actor, flags),
82 DEFINE_FLAG(FL, COUNTITEM, Actor, flags),
83 DEFINE_FLAG(FL, COUNTKILL, Actor, flags),
84 DEFINE_FLAG(FL, COUNTSECRET, Actor, flags),
85 DEFINE_FLAG(WF, DONTBOB, Weapon, weaponFlags),
86 DEFINE_FLAG(FL, DONTRIP, Actor, flags),
87 DEFINE_FLAG(FL, DROPBASEDONTARGET, Actor, flags),
88 DEFINE_FLAG(IF, INVBAR, Inventory, itemFlags),
89 DEFINE_FLAG(FL, ISMONSTER, Actor, flags),
90 DEFINE_FLAG(FL, MISSILE, Actor, flags),
91 DEFINE_FLAG(WF, NOALERT, Weapon, weaponFlags),
92 DEFINE_FLAG(WF, NOAUTOFIRE, Weapon, weaponFlags),
93 DEFINE_FLAG(WF, NOGRIN, Weapon, weaponFlags),
94 DEFINE_FLAG(FL, OLDRANDOMCHASE, Actor, flags),
95 DEFINE_FLAG(FL, PICKUP, Actor, flags),
96 DEFINE_FLAG(FL, PLOTONAUTOMAP, Actor, flags),
97 DEFINE_FLAG(FL, RANDOMIZE, Actor, flags),
98 DEFINE_FLAG(FL, RIPPER, Actor, flags),
99 DEFINE_FLAG(FL, REQUIREKEYS, Actor, flags),
100 DEFINE_FLAG(FL, SHOOTABLE, Actor, flags),
101 DEFINE_FLAG(FL, SOLID, Actor, flags)
102 };
103 extern const PropDef properties[];
104
105 ////////////////////////////////////////////////////////////////////////////////
106
StateLabel(const FString & str,const ClassDef * parent,bool noRelative)107 StateLabel::StateLabel(const FString &str, const ClassDef *parent, bool noRelative)
108 {
109 Scanner sc(str.GetChars(), str.Len());
110 Parse(sc, parent, noRelative);
111 }
112
StateLabel(Scanner & sc,const ClassDef * parent,bool noRelative)113 StateLabel::StateLabel(Scanner &sc, const ClassDef *parent, bool noRelative)
114 {
115 Parse(sc, parent, noRelative);
116 }
117
Resolve() const118 const Frame *StateLabel::Resolve() const
119 {
120 return cls->FindStateInList(label) + offset;
121 }
122
Resolve(AActor * owner,const Frame * caller,const Frame * def) const123 const Frame *StateLabel::Resolve(AActor *owner, const Frame *caller, const Frame *def) const
124 {
125 if(isRelative)
126 return caller + offset;
127 else if(isDefault)
128 return def;
129
130 const Frame *frame = owner->GetClass()->FindStateInList(label);
131 if(frame)
132 return frame + offset;
133 return NULL;
134 }
135
Parse(Scanner & sc,const ClassDef * parent,bool noRelative)136 void StateLabel::Parse(Scanner &sc, const ClassDef *parent, bool noRelative)
137 {
138 cls = parent;
139
140 if(!noRelative && sc.CheckToken(TK_IntConst))
141 {
142 isRelative = true;
143 offset = sc->number;
144 return;
145 }
146
147 isRelative = false;
148 isDefault = sc.CheckToken('*');
149 if(isDefault)
150 return;
151
152 sc.MustGetToken(TK_Identifier);
153 label = sc->str;
154 if(sc.CheckToken(TK_ScopeResolution))
155 {
156 if(label.CompareNoCase("Super") == 0)
157 {
158 // This should never happen in normal use, but just in case.
159 if(parent->parent == NULL)
160 sc.ScriptMessage(Scanner::ERROR, "This actor does not have a super class.");
161 cls = parent->parent;
162 }
163 else
164 {
165 do
166 {
167 cls = cls->parent;
168 if(cls == NULL)
169 sc.ScriptMessage(Scanner::ERROR, "%s is not a super class.", label.GetChars());
170 }
171 while(stricmp(cls->GetName().GetChars(), label.GetChars()) != 0);
172 }
173
174 sc.MustGetToken(TK_Identifier);
175 label = sc->str;
176 }
177
178 while(sc.CheckToken('.'))
179 {
180 sc.MustGetToken(TK_Identifier);
181 label = label + "." + sc->str;
182 }
183
184 if(sc.CheckToken('+'))
185 {
186 sc.MustGetToken(TK_IntConst);
187 offset = sc->number;
188 }
189 else
190 offset = 0;
191 }
192
193 ////////////////////////////////////////////////////////////////////////////////
194
195 struct StateDefinition
196 {
197 public:
198 enum NextType
199 {
200 GOTO,
201 LOOP,
202 WAIT,
203 STOP,
204
205 NORMAL
206 };
207
208 FString label;
209 char sprite[5];
210 FString frames;
211 int duration;
212 unsigned randDuration;
213 bool fullbright;
214 NextType nextType;
215 FString nextArg;
216 StateLabel jumpLabel;
217 Frame::ActionCall functions[2];
218 };
219
220 static TArray<const SymbolInfo *> *symbolPool = NULL;
221
SymbolInfo(const ClassDef * cls,const FName & var,const int offset)222 SymbolInfo::SymbolInfo(const ClassDef *cls, const FName &var, const int offset) :
223 cls(cls), var(var), offset(offset)
224 {
225 if(symbolPool == NULL)
226 symbolPool = new TArray<const SymbolInfo *>();
227 symbolPool->Push(this);
228 }
229
SymbolCompare(const void * s1,const void * s2)230 int SymbolCompare(const void *s1, const void *s2)
231 {
232 const Symbol * const sym1 = *((const Symbol **)s1);
233 const Symbol * const sym2 = *((const Symbol **)s2);
234 if(sym1->GetName() < sym2->GetName())
235 return -1;
236 else if(sym1->GetName() > sym2->GetName())
237 return 1;
238 return 0;
239 }
240
241 ////////////////////////////////////////////////////////////////////////////////
242
ExprSin(AActor * self,ExpressionNode::Value & out,ExpressionNode * const * args,FRandom * rng)243 void ExprSin(AActor *self, ExpressionNode::Value &out, ExpressionNode* const *args, FRandom *rng)
244 {
245 out = double(finesine[(args[0]->Evaluate(self).GetInt()%360)*FINEANGLES/360])/FRACUNIT;
246 }
247
ExprCos(AActor * self,ExpressionNode::Value & out,ExpressionNode * const * args,FRandom * rng)248 void ExprCos(AActor *self, ExpressionNode::Value &out, ExpressionNode* const *args, FRandom *rng)
249 {
250 out = double(finecosine[(args[0]->Evaluate(self).GetInt()%360)*FINEANGLES/360])/FRACUNIT;
251 }
252
ExprRandom(AActor * self,ExpressionNode::Value & out,ExpressionNode * const * args,FRandom * rng)253 void ExprRandom(AActor *self, ExpressionNode::Value &out, ExpressionNode* const *args, FRandom *rng)
254 {
255 int64_t min = args[0]->Evaluate(self).GetInt();
256 int64_t max = args[1]->Evaluate(self).GetInt();
257 if(min > max)
258 out = max+(*rng)((int)(min-max+1));
259 else
260 out = min+(*rng)((int)(max-min+1));
261 }
262
ExprFRandom(AActor * self,ExpressionNode::Value & out,ExpressionNode * const * args,FRandom * rng)263 void ExprFRandom(AActor *self, ExpressionNode::Value &out, ExpressionNode* const *args, FRandom *rng)
264 {
265 static const unsigned int randomPrecision = 0x80000000;
266
267 double min = args[0]->Evaluate(self).GetDouble();
268 double max = args[1]->Evaluate(self).GetDouble();
269 out = min+(double((*rng)(randomPrecision))/randomPrecision)*(max-min);
270 }
271
272 static const struct ExpressionFunction
273 {
274 const char* const name;
275 int ret;
276 unsigned short args;
277 bool takesRNG;
278 FunctionSymbol::ExprFunction function;
279 } functions[] =
280 {
281 { "cos", TypeHierarchy::FLOAT, 1, false, ExprCos },
282 { "frandom", TypeHierarchy::FLOAT, 2, true, ExprFRandom },
283 { "random", TypeHierarchy::INT, 2, true, ExprRandom },
284 { "sin", TypeHierarchy::FLOAT, 1, false, ExprSin },
285
286 { NULL, 0, false, false }
287 };
288
289 ////////////////////////////////////////////////////////////////////////////////
290
291 class MetaTable::Data
292 {
293 public:
Data(MetaTable::Type type,uint32_t id)294 Data(MetaTable::Type type, uint32_t id) : id(id), type(type), inherited(false), next(NULL) {}
~Data()295 ~Data()
296 {
297 SetType(MetaTable::INTEGER);
298 }
299
SetType(MetaTable::Type type)300 void SetType(MetaTable::Type type)
301 {
302 // As soon as we try to change the value consider the meta data new
303 inherited = false;
304 if(this->type == type)
305 return;
306
307 if(this->type == MetaTable::STRING)
308 {
309 delete[] value.string;
310 value.string = NULL;
311 }
312
313 this->type = type;
314 }
315
operator =(const Data & other)316 const Data &operator= (const Data &other)
317 {
318 id = other.id;
319 SetType(other.type);
320 inherited = true;
321
322 switch(type)
323 {
324 case MetaTable::INTEGER:
325 value.integer = other.value.integer;
326 break;
327 case MetaTable::STRING:
328 value.string = new char[strlen(other.value.string)+1];
329 strcpy(value.string, other.value.string);
330 break;
331 case MetaTable::FIXED:
332 value.fixedPoint = other.value.fixedPoint;
333 break;
334 }
335
336 return other;
337 }
338
339 uint32_t id;
340 MetaTable::Type type;
341 bool inherited;
342 union
343 {
344 int integer;
345 fixed fixedPoint;
346 char* string;
347 } value;
348 Data *next;
349 };
350
MetaTable()351 MetaTable::MetaTable() : head(NULL)
352 {
353 }
354
~MetaTable()355 MetaTable::~MetaTable()
356 {
357 FreeTable();
358 }
359
CopyMeta(const MetaTable & other)360 void MetaTable::CopyMeta(const MetaTable &other)
361 {
362 Data *data = other.head;
363 while(data)
364 {
365 Data *copyData = FindMetaData(data->id);
366 *copyData = *data;
367
368 data = data->next;
369 }
370 }
371
FindMeta(uint32_t id) const372 MetaTable::Data *MetaTable::FindMeta(uint32_t id) const
373 {
374 Data *data = head;
375
376 while(data != NULL)
377 {
378 if(data->id == id)
379 break;
380
381 data = data->next;
382 }
383
384 return data;
385 }
386
FindMetaData(uint32_t id)387 MetaTable::Data *MetaTable::FindMetaData(uint32_t id)
388 {
389 Data *data = FindMeta(id);
390 if(data == NULL)
391 {
392 data = new MetaTable::Data(MetaTable::INTEGER, id);
393 data->next = head;
394 head = data;
395 }
396
397 return data;
398 }
399
FreeTable()400 void MetaTable::FreeTable()
401 {
402 Data *data = head;
403 while(data != NULL)
404 {
405 Data *prevData = data;
406 data = data->next;
407 delete prevData;
408 }
409 }
410
GetMetaInt(uint32_t id,int def) const411 int MetaTable::GetMetaInt(uint32_t id, int def) const
412 {
413 Data *data = FindMeta(id);
414 if(!data)
415 return def;
416 return data->value.integer;
417 }
418
GetMetaFixed(uint32_t id,fixed def) const419 fixed MetaTable::GetMetaFixed(uint32_t id, fixed def) const
420 {
421 Data *data = FindMeta(id);
422 if(!data)
423 return def;
424 return data->value.fixedPoint;
425 }
426
GetMetaString(uint32_t id) const427 const char* MetaTable::GetMetaString(uint32_t id) const
428 {
429 Data *data = FindMeta(id);
430 if(!data)
431 return NULL;
432 return data->value.string;
433 }
434
IsInherited(uint32_t id)435 bool MetaTable::IsInherited(uint32_t id)
436 {
437 Data *data = FindMetaData(id);
438 return data->inherited;
439 }
440
SetMetaInt(uint32_t id,int value)441 void MetaTable::SetMetaInt(uint32_t id, int value)
442 {
443 Data *data = FindMetaData(id);
444 data->SetType(MetaTable::INTEGER);
445 data->value.integer = value;
446 }
447
SetMetaFixed(uint32_t id,fixed value)448 void MetaTable::SetMetaFixed(uint32_t id, fixed value)
449 {
450 Data *data = FindMetaData(id);
451 data->SetType(MetaTable::FIXED);
452 data->value.fixedPoint = value;
453 }
454
SetMetaString(uint32_t id,const char * value)455 void MetaTable::SetMetaString(uint32_t id, const char* value)
456 {
457 Data *data = FindMetaData(id);
458 if(data->type == MetaTable::STRING && data->value.string != NULL)
459 delete[] data->value.string;
460 else
461 data->SetType(MetaTable::STRING);
462
463 data->value.string = new char[strlen(value)+1];
464 strcpy(data->value.string, value);
465 }
466
467 ////////////////////////////////////////////////////////////////////////////////
468
469 static TMap<int, ClassDef *> EditorNumberTable, ConversationIDTable;
470 SymbolTable ClassDef::globalSymbols;
471 bool ClassDef::bShutdown = false;
472
ClassDef()473 ClassDef::ClassDef() : tentative(false)
474 {
475 defaultInstance = NULL;
476 FlatPointers = Pointers = NULL;
477 replacement = replacee = NULL;
478 }
479
~ClassDef()480 ClassDef::~ClassDef()
481 {
482 if(defaultInstance)
483 {
484 M_Free(defaultInstance);
485 }
486 for(unsigned int i = 0;i < symbols.Size();++i)
487 delete symbols[i];
488 }
489
ClassTable()490 TMap<FName, ClassDef *> &ClassDef::ClassTable()
491 {
492 static TMap<FName, ClassDef *> classTable;
493 return classTable;
494 }
495
496 const size_t ClassDef::POINTER_END = ~(size_t)0;
497 // [BL] Pulled from ZDoom more or less.
498 /*
499 ** dobjtype.cpp
500 ** Implements the type information class
501 **
502 **---------------------------------------------------------------------------
503 ** Copyright 1998-2008 Randy Heit
504 ** All rights reserved.
505 **
506 ** Redistribution and use in source and binary forms, with or without
507 ** modification, are permitted provided that the following conditions
508 ** are met:
509 **
510 ** 1. Redistributions of source code must retain the above copyright
511 ** notice, this list of conditions and the following disclaimer.
512 ** 2. Redistributions in binary form must reproduce the above copyright
513 ** notice, this list of conditions and the following disclaimer in the
514 ** documentation and/or other materials provided with the distribution.
515 ** 3. The name of the author may not be used to endorse or promote products
516 ** derived from this software without specific prior written permission.
517 **
518 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
519 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
520 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
521 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
522 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
523 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
524 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
525 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
526 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
527 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
528 **---------------------------------------------------------------------------
529 **
530 */
531 // Create the FlatPointers array, if it doesn't exist already.
532 // It comprises all the Pointers from superclasses plus this class's own Pointers.
533 // If this class does not define any new Pointers, then FlatPointers will be set
534 // to the same array as the super class's.
BuildFlatPointers()535 void ClassDef::BuildFlatPointers()
536 {
537 if (FlatPointers != NULL)
538 { // Already built: Do nothing.
539 return;
540 }
541 else if (parent == NULL)
542 { // No parent: FlatPointers is the same as Pointers.
543 if (Pointers == NULL)
544 { // No pointers: Make FlatPointers a harmless non-NULL.
545 FlatPointers = &POINTER_END;
546 }
547 else
548 {
549 FlatPointers = Pointers;
550 }
551 }
552 else
553 {
554 const_cast<ClassDef *>(parent)->BuildFlatPointers ();
555 if (Pointers == NULL)
556 { // No new pointers: Just use the same FlatPointers as the parent.
557 FlatPointers = parent->FlatPointers;
558 }
559 else
560 { // New pointers: Create a new FlatPointers array and add them.
561 int numPointers, numSuperPointers;
562
563 // Count pointers defined by this class.
564 for (numPointers = 0; Pointers[numPointers] != POINTER_END; numPointers++)
565 { }
566 // Count pointers defined by superclasses.
567 for (numSuperPointers = 0; parent->FlatPointers[numSuperPointers] != POINTER_END; numSuperPointers++)
568 { }
569
570 // Concatenate them into a new array
571 size_t *flat = new size_t[numPointers + numSuperPointers + 1];
572 if (numSuperPointers > 0)
573 {
574 memcpy (flat, parent->FlatPointers, sizeof(size_t)*numSuperPointers);
575 }
576 memcpy (flat + numSuperPointers, Pointers, sizeof(size_t)*(numPointers+1));
577 FlatPointers = flat;
578 }
579 }
580 }
581
CreateInstance() const582 AActor *ClassDef::CreateInstance() const
583 {
584 if(IsDescendantOf(NATIVE_CLASS(Actor)) && !((AActor*)defaultInstance)->SpawnState)
585 {
586 ((AActor*)defaultInstance)->MeleeState = FindState(NAME_Melee);
587 ((AActor*)defaultInstance)->MissileState = FindState(NAME_Missile);
588 ((AActor*)defaultInstance)->PainState = FindState(NAME_Pain);
589 ((AActor*)defaultInstance)->PathState = FindState(NAME_Path);
590 ((AActor*)defaultInstance)->SpawnState = FindState(NAME_Spawn);
591 ((AActor*)defaultInstance)->SeeState = FindState(NAME_See);
592 }
593
594 AActor *newactor = (AActor *) M_Malloc(size);
595 memcpy((void*)newactor, (void*)defaultInstance, size);
596 ConstructNative(this, newactor);
597 newactor->Init();
598 return newactor;
599 }
600
FindClass(unsigned int ednum)601 const ClassDef *ClassDef::FindClass(unsigned int ednum)
602 {
603 ClassDef **ret = EditorNumberTable.CheckKey(ednum);
604 if(ret == NULL)
605 return NULL;
606 return *ret;
607 }
608
FindClass(const FName & className)609 const ClassDef *ClassDef::FindClass(const FName &className)
610 {
611 ClassDef **ret = ClassTable().CheckKey(className);
612 if(ret == NULL)
613 return NULL;
614 return *ret;
615 }
616
FindConversationClass(unsigned int convid)617 const ClassDef *ClassDef::FindConversationClass(unsigned int convid)
618 {
619 ClassDef **ret = ConversationIDTable.CheckKey(convid);
620 if(ret == NULL)
621 return NULL;
622 return *ret;
623 }
624
FindClassTentative(const FName & className,const ClassDef * parent)625 const ClassDef *ClassDef::FindClassTentative(const FName &className, const ClassDef *parent)
626 {
627 const ClassDef *search = FindClass(className);
628 if(search)
629 {
630 if(!search->parent->IsDescendantOf(parent))
631 I_Error("%s does not inherit %s!", className.GetChars(), parent->GetName().GetChars());
632 return search;
633 }
634
635 ClassDef *newClass = new ClassDef();
636 ClassTable()[className] = newClass;
637
638 newClass->tentative = true;
639 newClass->name = className;
640 newClass->parent = parent;
641 return newClass;
642 }
643
FindFunction(const FName & function,int & specialNum) const644 const ActionInfo *ClassDef::FindFunction(const FName &function, int &specialNum) const
645 {
646 Specials::LineSpecials special = Specials::LookupFunctionNum(function);
647 if(special != Specials::NUM_POSSIBLE_SPECIALS)
648 {
649 specialNum = special;
650 return FindFunction("A_CallSpecial", specialNum);
651 }
652
653 if(actions.Size() != 0)
654 {
655 ActionInfo *func = LookupFunction(function, &actions);
656 if(func)
657 return func;
658 }
659 if(parent)
660 return parent->FindFunction(function, specialNum);
661 return NULL;
662 }
663
FindState(const FName & stateName) const664 const Frame *ClassDef::FindState(const FName &stateName) const
665 {
666 const Frame *ret = FindStateInList(stateName);
667 return ret;
668 }
669
FindStateInList(const FName & stateName) const670 const Frame *ClassDef::FindStateInList(const FName &stateName) const
671 {
672 const unsigned int *ret = stateList.CheckKey(stateName);
673 if(ret == NULL)
674 return (!parent ? NULL : parent->FindStateInList(stateName));
675
676 // Change the frameLists
677 return ResolveStateIndex(*ret);
678 }
679
FindSymbol(const FName & symbol) const680 Symbol *ClassDef::FindSymbol(const FName &symbol) const
681 {
682 unsigned int min = 0;
683 unsigned int max = symbols.Size()-1;
684 unsigned int mid = max/2;
685 if(symbols.Size() > 0)
686 {
687 do
688 {
689 if(symbols[mid]->GetName() == symbol)
690 return symbols[mid];
691
692 if(symbols[mid]->GetName() > symbol)
693 max = mid-1;
694 else if(symbols[mid]->GetName() < symbol)
695 min = mid+1;
696 mid = (min+max)/2;
697 }
698 while(max >= min && max < symbols.Size());
699 }
700
701 if(parent)
702 return parent->FindSymbol(symbol);
703 else if(globalSymbols.Size() > 0)
704 {
705 // Search globals.
706 min = 0;
707 max = globalSymbols.Size()-1;
708 mid = max/2;
709 do
710 {
711 if(globalSymbols[mid]->GetName() == symbol)
712 return globalSymbols[mid];
713
714 if(globalSymbols[mid]->GetName() > symbol)
715 max = mid-1;
716 else if(globalSymbols[mid]->GetName() < symbol)
717 min = mid+1;
718 mid = (min+max)/2;
719 }
720 while(max >= min && max < globalSymbols.Size());
721 }
722 return NULL;
723 }
724
GetReplacement(bool respectMapinfo) const725 const ClassDef *ClassDef::GetReplacement(bool respectMapinfo) const
726 {
727 return replacement ? replacement->GetReplacement(false) : this;
728 }
729
730 struct Goto
731 {
732 public:
733 Frame *frame;
734 FString remapLabel; // Label: goto Label2
735 StateLabel jumpLabel;
736 };
InstallStates(const TArray<StateDefinition> & stateDefs)737 void ClassDef::InstallStates(const TArray<StateDefinition> &stateDefs)
738 {
739 // We need to resolve gotos after we install the states.
740 TArray<Goto> gotos;
741
742 // Count the number of states we need so that we can allocate the memory
743 // in one go and keep our pointers valid.
744 unsigned int numStates = 0;
745 for(unsigned int iter = 0;iter < stateDefs.Size();++iter)
746 {
747 if(!stateDefs[iter].label.IsEmpty() && stateDefs[iter].sprite[0] == 0)
748 continue;
749 numStates += (unsigned int)stateDefs[iter].frames.Len();
750 }
751 frameList.Resize(numStates);
752
753 FString thisLabel;
754 Frame *prevFrame = NULL;
755 Frame *loopPoint = NULL;
756 Frame *thisFrame = &frameList[0];
757 for(unsigned int iter = 0;iter < stateDefs.Size();++iter)
758 {
759 const StateDefinition &thisStateDef = stateDefs[iter];
760
761 // Special case, `Label: stop`, remove state. Hmm... I wonder if ZDoom handles fall throughs on this.
762 if(!thisStateDef.label.IsEmpty() && thisStateDef.sprite[0] == 0)
763 {
764 switch(thisStateDef.nextType)
765 {
766 case StateDefinition::STOP:
767 stateList[thisStateDef.label] = INT_MAX;
768 break;
769 case StateDefinition::NORMAL:
770 stateList[thisStateDef.label] = (unsigned int)(thisFrame - &frameList[0]);
771 continue;
772 case StateDefinition::GOTO:
773 {
774 Goto thisGoto;
775 thisGoto.frame = NULL;
776 thisGoto.remapLabel = thisStateDef.label;
777 thisGoto.jumpLabel = thisStateDef.jumpLabel;
778 gotos.Push(thisGoto);
779 continue;
780 }
781 default:
782 Quit("Tried to use a loop on a frameless state.\n");
783 break;
784 }
785 continue;
786 }
787
788 for(unsigned int i = 0;i < thisStateDef.frames.Len();++i)
789 {
790 if(i == 0 && !thisStateDef.label.IsEmpty())
791 {
792 stateList[thisStateDef.label] = (unsigned int)(thisFrame - &frameList[0]);
793 loopPoint = thisFrame;
794 }
795 memcpy(thisFrame->sprite, thisStateDef.sprite, 4);
796 thisFrame->frame = thisStateDef.frames[i]-'A';
797 thisFrame->duration = thisStateDef.duration;
798 thisFrame->randDuration = thisStateDef.randDuration;
799 thisFrame->fullbright = thisStateDef.fullbright;
800 thisFrame->action = thisStateDef.functions[0];
801 thisFrame->thinker = thisStateDef.functions[1];
802 thisFrame->next = NULL;
803 thisFrame->index = (unsigned int)(thisFrame - &frameList[0]);
804 thisFrame->spriteInf = 0;
805 // Only free the action arguments if we are the last frame using them.
806 thisFrame->freeActionArgs = i == thisStateDef.frames.Len()-1;
807 if(i == thisStateDef.frames.Len()-1) // Handle nextType
808 {
809 if(thisStateDef.nextType == StateDefinition::WAIT)
810 thisFrame->next = thisFrame;
811 else if(thisStateDef.nextType == StateDefinition::LOOP)
812 thisFrame->next = loopPoint;
813 // Add to goto list
814 else if(thisStateDef.nextType == StateDefinition::GOTO)
815 {
816 Goto thisGoto;
817 thisGoto.frame = thisFrame;
818 thisGoto.jumpLabel = thisStateDef.jumpLabel;
819 gotos.Push(thisGoto);
820 }
821 }
822 if(prevFrame != NULL)
823 prevFrame->next = thisFrame;
824
825 if(thisStateDef.nextType == StateDefinition::NORMAL || i != thisStateDef.frames.Len()-1)
826 prevFrame = thisFrame;
827 else
828 prevFrame = NULL;
829 //printf("Adding frame: %s %c %d\n", thisStateDef.sprite, thisFrame->frame, thisFrame->duration);
830 ++thisFrame;
831 }
832 }
833
834 // Safe guard to make sure state counting stays in sync
835 assert(thisFrame == &frameList[frameList.Size()]);
836
837 // Resolve Gotos
838 for(unsigned int iter = 0;iter < gotos.Size();++iter)
839 {
840 const Frame *result = gotos[iter].jumpLabel.Resolve();
841 if(gotos[iter].frame)
842 gotos[iter].frame->next = result;
843 else
844 {
845 unsigned int frameIndex = result->index;
846 const ClassDef *owner = this;
847 while(!owner->IsStateOwner(result))
848 {
849 frameIndex += owner->frameList.Size();
850 owner = owner->parent;
851 }
852 stateList[gotos[iter].remapLabel] = frameIndex;
853 }
854 }
855 }
856
IsDescendantOf(const ClassDef * parent) const857 bool ClassDef::IsDescendantOf(const ClassDef *parent) const
858 {
859 const ClassDef *currentParent = this;
860 while(currentParent != NULL)
861 {
862 if(currentParent == parent)
863 return true;
864 currentParent = currentParent->parent;
865 }
866 return false;
867 }
868
LoadActors()869 void ClassDef::LoadActors()
870 {
871 printf("ClassDef: Loading actor definitions.\n");
872 atterm(&ClassDef::UnloadActors);
873
874 // First iterate through the native classes and fix their parent pointers
875 // In order to keep things simple what I did was in DeclareNativeClass I
876 // force a const ClassDef ** into the parent, so we just need to cast back
877 // and get the value of the pointer.
878 {
879 TMap<FName, ClassDef *>::Iterator iter(ClassTable());
880 TMap<FName, ClassDef *>::Pair *pair;
881 while(iter.NextPair(pair))
882 {
883 ClassDef * const cls = pair->Value;
884 if(cls->parent)
885 cls->parent = *(const ClassDef **)cls->parent;
886 }
887 }
888
889 InitFunctionTable(NULL);
890
891 // Add function symbols
892 const ExpressionFunction *func = functions;
893 do
894 {
895 globalSymbols.Push(new FunctionSymbol(func->name, TypeHierarchy::staticTypes.GetType(TypeHierarchy::PrimitiveTypes(func->ret)), func->args, func->function, func->takesRNG));
896 }
897 while((++func)->name != NULL);
898 qsort(&globalSymbols[0], globalSymbols.Size(), sizeof(globalSymbols[0]), SymbolCompare);
899
900 int lastLump = 0;
901 int lump = 0;
902 while((lump = Wads.FindLump("DECORATE", &lastLump)) != -1)
903 {
904 ParseDecorateLump(lump);
905 }
906
907 ReleaseFunctionTable();
908 delete symbolPool;
909 #if 0
910 // Debug code - Dump actor tree visually.
911 DumpClasses();
912 #endif
913
914 R_InitSprites();
915
916 {
917 unsigned int index = 0;
918
919 TMap<FName, ClassDef *>::Iterator iter(ClassTable());
920 TMap<FName, ClassDef *>::Pair *pair;
921 while(iter.NextPair(pair))
922 {
923 ClassDef * const cls = pair->Value;
924
925 if(cls->tentative)
926 {
927 FString error;
928 error.Format("The actor '%s' is referenced but never defined.", cls->GetName().GetChars());
929 throw CFatalError(error);
930 }
931
932 cls->ClassIndex = index++;
933 for(unsigned int i = 0;i < cls->frameList.Size();++i)
934 cls->frameList[i].spriteInf = R_GetSprite(cls->frameList[i].sprite);
935 }
936 }
937 }
938
ParseActor(Scanner & sc)939 void ClassDef::ParseActor(Scanner &sc)
940 {
941 // Read the header
942 sc.MustGetToken(TK_Identifier);
943 ClassDef **classRef = ClassTable().CheckKey(sc->str);
944 ClassDef *newClass;
945 bool previouslyDefined = classRef != NULL;
946 if(!previouslyDefined)
947 {
948 newClass = new ClassDef();
949 ClassTable()[sc->str] = newClass;
950 }
951 else
952 newClass = *classRef;
953 bool native = false;
954 newClass->name = sc->str;
955 if(sc.CheckToken(':'))
956 {
957 sc.MustGetToken(TK_Identifier);
958 const ClassDef *parent = FindClass(sc->str);
959 if(parent == NULL || parent->tentative)
960 sc.ScriptMessage(Scanner::ERROR, "Could not find parent actor '%s'", sc->str.GetChars());
961 if(newClass->tentative && !parent->IsDescendantOf(newClass->parent))
962 sc.ScriptMessage(Scanner::ERROR, "Parent for actor expected to be '%s'", newClass->parent->GetName().GetChars());
963 newClass->parent = parent;
964 }
965 else
966 {
967 // If no class was specified to inherit from, inherit from AActor, but not for AActor.
968 if(newClass != NATIVE_CLASS(Actor))
969 newClass->parent = NATIVE_CLASS(Actor);
970 }
971
972 // Handle class replacements
973 if(sc.CheckToken(TK_Identifier))
974 {
975 if(sc->str.CompareNoCase("replaces") == 0)
976 {
977 sc.MustGetToken(TK_Identifier);
978
979 if(sc->str.CompareNoCase(newClass->name) == 0)
980 sc.ScriptMessage(Scanner::ERROR, "Actor '%s' attempting to replace itself!", sc->str.GetChars());
981
982 ClassDef *replacee = const_cast<ClassDef *>(FindClassTentative(sc->str, NATIVE_CLASS(Actor)));
983 replacee->replacement = newClass;
984 newClass->replacee = replacee;
985 }
986 else
987 sc.Rewind();
988 }
989
990 if(sc.CheckToken(TK_IntConst))
991 {
992 if(EditorNumberTable.CheckKey(sc->number) != NULL)
993 sc.ScriptMessage(Scanner::WARNING, "Overwriting editor number %d previously assigned to '%s', use replaces instead.", sc->number, EditorNumberTable[sc->number]->GetName().GetChars());
994 EditorNumberTable[sc->number] = newClass;
995 }
996 if(sc.CheckToken(TK_Identifier))
997 {
998 if(sc->str.CompareNoCase("native") == 0)
999 native = true;
1000 else
1001 sc.ScriptMessage(Scanner::ERROR, "Unknown keyword '%s'.", sc->str.GetChars());
1002 }
1003 if(previouslyDefined && !native && !newClass->tentative)
1004 sc.ScriptMessage(Scanner::ERROR, "Actor '%s' already defined.", newClass->name.GetChars());
1005 else
1006 newClass->tentative = false;
1007
1008 if(!native) // Initialize the default instance to the nearest native class.
1009 {
1010 newClass->ConstructNative = newClass->parent->ConstructNative;
1011 newClass->size = newClass->parent->size;
1012
1013 newClass->defaultInstance = (DObject *) M_Malloc(newClass->parent->size);
1014 memcpy((void*)newClass->defaultInstance, (void*)newClass->parent->defaultInstance, newClass->parent->size);
1015 }
1016 else
1017 {
1018 // This could happen if a non-native actor is declared native or
1019 // possibly in the case of a stuck dependency.
1020 if(!newClass->defaultInstance)
1021 sc.ScriptMessage(Scanner::ERROR, "Uninitialized default instance for '%s'.", newClass->GetName().GetChars());
1022
1023 // Copy the parents defaults for native classes
1024 if(newClass->parent)
1025 memcpy((void*)newClass->defaultInstance, (void*)newClass->parent->defaultInstance, newClass->parent->size);
1026 }
1027 // Copy properties and flags.
1028 if(newClass->parent != NULL)
1029 {
1030 memcpy((void*)newClass->defaultInstance, (void*)newClass->parent->defaultInstance, newClass->parent->size);
1031 newClass->defaultInstance->Class = newClass;
1032 newClass->Meta = newClass->parent->Meta;
1033 }
1034
1035 bool actionsSorted = true;
1036 sc.MustGetToken('{');
1037 while(!sc.CheckToken('}'))
1038 {
1039 if(sc.CheckToken('+') || sc.CheckToken('-'))
1040 {
1041 bool set = sc->token == '+';
1042 FString prefix;
1043 sc.MustGetToken(TK_Identifier);
1044 FString flagName = sc->str;
1045 if(sc.CheckToken('.'))
1046 {
1047 prefix = flagName;
1048 sc.MustGetToken(TK_Identifier);
1049 flagName = sc->str;
1050 }
1051 if(!SetFlag(newClass, (AActor*)newClass->defaultInstance, prefix, flagName, set))
1052 sc.ScriptMessage(Scanner::WARNING, "Unknown flag '%s' for actor '%s'.", flagName.GetChars(), newClass->name.GetChars());
1053 }
1054 else
1055 {
1056 sc.MustGetToken(TK_Identifier);
1057 if(sc->str.CompareNoCase("states") == 0)
1058 {
1059 if(!actionsSorted)
1060 InitFunctionTable(&newClass->actions);
1061
1062 TArray<StateDefinition> stateDefs;
1063
1064 sc.MustGetToken('{');
1065 //sc.MustGetToken(TK_Identifier); // We should already have grabbed the identifier in all other cases.
1066 bool needIdentifier = true;
1067 bool infiniteLoopProtection = false;
1068 while(sc->token != '}' && !sc.CheckToken('}'))
1069 {
1070 StateDefinition thisState;
1071 thisState.sprite[0] = thisState.sprite[4] = 0;
1072 thisState.duration = 0;
1073 thisState.randDuration = 0;
1074 thisState.nextType = StateDefinition::NORMAL;
1075
1076 if(needIdentifier)
1077 sc.MustGetToken(TK_Identifier);
1078 else
1079 needIdentifier = true;
1080 FString stateString = sc->str;
1081 if(sc.CheckToken(':'))
1082 {
1083 infiniteLoopProtection = false;
1084 thisState.label = stateString;
1085 // New state
1086 if(sc.CheckToken('}'))
1087 sc.ScriptMessage(Scanner::ERROR, "State defined with no frames.");
1088 sc.MustGetToken(TK_Identifier);
1089
1090 if(sc->str.CompareNoCase("stop") == 0)
1091 {
1092 thisState.nextType = StateDefinition::STOP;
1093 if(!sc.CheckToken('}'))
1094 sc.MustGetToken(TK_Identifier);
1095 }
1096 else if(sc->str.CompareNoCase("goto") == 0)
1097 {
1098 thisState.jumpLabel = StateLabel(sc, newClass, true);
1099 thisState.nextType = StateDefinition::GOTO;
1100 if(!sc.CheckToken('}'))
1101 sc.MustGetToken(TK_Identifier);
1102 }
1103
1104 stateString = sc->str;
1105 }
1106
1107 if(thisState.nextType == StateDefinition::NORMAL &&
1108 (sc.CheckToken(TK_Identifier) || sc.CheckToken(TK_StringConst)))
1109 {
1110 bool invalidSprite = (stateString.Len() != 4);
1111 strncpy(thisState.sprite, stateString, 4);
1112
1113 infiniteLoopProtection = false;
1114 if(invalidSprite) // We now know this is a frame so check sprite length
1115 sc.ScriptMessage(Scanner::ERROR, "Sprite name must be exactly 4 characters long.");
1116
1117 R_LoadSprite(thisState.sprite);
1118 thisState.frames = sc->str;
1119 if(sc.CheckToken('-'))
1120 {
1121 sc.MustGetToken(TK_FloatConst);
1122 thisState.duration = -1;
1123 }
1124 else
1125 {
1126 if(sc.CheckToken(TK_FloatConst))
1127 {
1128 // Eliminate confusion about fractional frame delays
1129 if(!CheckTicsValid(sc->decimal))
1130 sc.ScriptMessage(Scanner::ERROR, "Fractional frame durations must be exactly .5!");
1131
1132 thisState.duration = static_cast<int> (sc->decimal*2);
1133 }
1134 else if(stricmp(thisState.sprite, "goto") == 0)
1135 {
1136 thisState.nextType = StateDefinition::GOTO;
1137 thisState.nextArg = thisState.frames;
1138 thisState.frames = FString();
1139 }
1140 else if(sc.CheckToken(TK_Identifier))
1141 {
1142 if(sc->str.CompareNoCase("random") != 0)
1143 sc.ScriptMessage(Scanner::ERROR, "Expected random frame duration.");
1144
1145 sc.MustGetToken('(');
1146 sc.MustGetToken(TK_FloatConst);
1147 if(!CheckTicsValid(sc->decimal))
1148 sc.ScriptMessage(Scanner::ERROR, "Fractional frame durations must be exactly .5!");
1149 thisState.duration = static_cast<int> (sc->decimal*2);
1150 sc.MustGetToken(',');
1151 sc.MustGetToken(TK_FloatConst);
1152 if(!CheckTicsValid(sc->decimal))
1153 sc.ScriptMessage(Scanner::ERROR, "Fractional frame durations must be exactly .5!");
1154 thisState.randDuration = static_cast<int> (sc->decimal*2);
1155 sc.MustGetToken(')');
1156 }
1157 else
1158 sc.ScriptMessage(Scanner::ERROR, "Expected frame duration.");
1159 }
1160 thisState.functions[0].pointer = thisState.functions[1].pointer = NULL;
1161 thisState.fullbright = false;
1162
1163 do
1164 {
1165 if(sc.CheckToken('}'))
1166 goto FinishState;
1167 else
1168 sc.MustGetToken(TK_Identifier);
1169
1170 if(sc->str.CompareNoCase("bright") == 0)
1171 thisState.fullbright = true;
1172 else
1173 break;
1174 }
1175 while(true);
1176
1177 if(thisState.nextType == StateDefinition::NORMAL)
1178 {
1179 for(int func = 0;func <= 2;func++)
1180 {
1181 if(sc.CheckToken(':'))
1182 {
1183 // We have a state label!
1184 needIdentifier = false;
1185 sc.Rewind();
1186 break;
1187 }
1188 if(sc->str.Len() == 4 || func == 2)
1189 {
1190 if(sc->str.CompareNoCase("goto") == 0)
1191 {
1192 thisState.jumpLabel = StateLabel(sc, newClass, true);
1193 thisState.nextType = StateDefinition::GOTO;
1194 }
1195 else if(sc->str.CompareNoCase("wait") == 0 || sc->str.CompareNoCase("fail") == 0)
1196 {
1197 thisState.nextType = StateDefinition::WAIT;
1198 }
1199 else if(sc->str.CompareNoCase("loop") == 0)
1200 {
1201 thisState.nextType = StateDefinition::LOOP;
1202 }
1203 else if(sc->str.CompareNoCase("stop") == 0)
1204 {
1205 thisState.nextType = StateDefinition::STOP;
1206 }
1207 else
1208 needIdentifier = false;
1209 break;
1210 }
1211 else
1212 {
1213 if(sc->str.CompareNoCase("NOP") != 0)
1214 {
1215 int specialNum = -1;
1216 const ActionInfo *funcInf = newClass->FindFunction(sc->str, specialNum);
1217 if(funcInf)
1218 {
1219 thisState.functions[func].pointer = *funcInf->func;
1220
1221 CallArguments *&ca = thisState.functions[func].args;
1222 ca = new CallArguments();
1223 CallArguments::Value val;
1224 unsigned int argc = 0;
1225
1226 // When using a line special we have to inject a parameter.
1227 if(specialNum >= 0)
1228 {
1229 val.useType = CallArguments::Value::VAL_INTEGER;
1230 val.isExpression = false;
1231 val.val.i = specialNum;
1232 ca->AddArgument(val);
1233 ++argc;
1234 }
1235
1236 if(sc.CheckToken('('))
1237 {
1238 if(funcInf->maxArgs == 0)
1239 sc.MustGetToken(')');
1240 else if(!(funcInf->minArgs == 0 && sc.CheckToken(')')))
1241 {
1242 do
1243 {
1244 val.isExpression = false;
1245
1246 const Type *argType = funcInf->ArgType(argc);
1247 if(argType == TypeHierarchy::staticTypes.GetType(TypeHierarchy::INT) ||
1248 argType == TypeHierarchy::staticTypes.GetType(TypeHierarchy::FLOAT) ||
1249 argType == TypeHierarchy::staticTypes.GetType(TypeHierarchy::BOOL))
1250 {
1251 val.isExpression = true;
1252 if(argType == TypeHierarchy::staticTypes.GetType(TypeHierarchy::INT))
1253 val.useType = CallArguments::Value::VAL_INTEGER;
1254 else
1255 val.useType = CallArguments::Value::VAL_DOUBLE;
1256 val.expr = ExpressionNode::ParseExpression(newClass, TypeHierarchy::staticTypes, sc);
1257 }
1258 else if(argType == TypeHierarchy::staticTypes.GetType(TypeHierarchy::STATE))
1259 {
1260 val.useType = CallArguments::Value::VAL_STATE;
1261 if(sc.CheckToken(TK_IntConst))
1262 {
1263 if(thisState.frames.Len() > 1)
1264 sc.ScriptMessage(Scanner::ERROR, "State offsets not allowed on multistate definitions.");
1265 FString label;
1266 label.Format("%d", sc->number);
1267 val.label = StateLabel(label, newClass);
1268 }
1269 else
1270 {
1271 sc.MustGetToken(TK_StringConst);
1272 val.label = StateLabel(sc->str, newClass);
1273 }
1274 }
1275 else
1276 {
1277 sc.MustGetToken(TK_StringConst);
1278 val.useType = CallArguments::Value::VAL_STRING;
1279 val.str = sc->str;
1280 }
1281 ca->AddArgument(val);
1282 ++argc;
1283
1284 // Check if we can or should take another argument
1285 if(!funcInf->varArgs && argc >= funcInf->maxArgs)
1286 break;
1287 }
1288 while(sc.CheckToken(','));
1289 sc.MustGetToken(')');
1290 }
1291 }
1292 if(argc < funcInf->minArgs)
1293 sc.ScriptMessage(Scanner::ERROR, "Too few arguments.");
1294 else
1295 {
1296 // Push unused defaults.
1297 while(argc < funcInf->maxArgs)
1298 ca->AddArgument(funcInf->defaults[(argc++)-funcInf->minArgs]);
1299 }
1300 }
1301 else
1302 sc.ScriptMessage(Scanner::WARNING, "Could not find function %s.", sc->str.GetChars());
1303 }
1304 }
1305
1306 if(!sc.CheckToken(TK_Identifier))
1307 break;
1308 else if(sc.CheckToken(':'))
1309 {
1310 needIdentifier = false;
1311 sc.Rewind();
1312 break;
1313 }
1314 }
1315 }
1316 }
1317 else
1318 {
1319 thisState.sprite[0] = 0;
1320 needIdentifier = false;
1321 if(infiniteLoopProtection)
1322 sc.ScriptMessage(Scanner::ERROR, "Malformed script.");
1323 infiniteLoopProtection = true;
1324 }
1325 FinishState:
1326 stateDefs.Push(thisState);
1327 }
1328
1329 newClass->InstallStates(stateDefs);
1330 }
1331 else if(sc->str.CompareNoCase("action") == 0)
1332 {
1333 actionsSorted = false;
1334 sc.MustGetToken(TK_Identifier);
1335 if(sc->str.CompareNoCase("native") != 0)
1336 sc.ScriptMessage(Scanner::ERROR, "Custom actions not supported.");
1337 sc.MustGetToken(TK_Identifier);
1338 ActionInfo *funcInf = LookupFunction(sc->str, NULL);
1339 if(!funcInf)
1340 sc.ScriptMessage(Scanner::ERROR, "The specified function %s could not be located.", sc->str.GetChars());
1341 newClass->actions.Push(funcInf);
1342 sc.MustGetToken('(');
1343 if(!sc.CheckToken(')'))
1344 {
1345 bool optRequired = false;
1346 do
1347 {
1348 // If we have processed at least one argument, then we can take varArgs.
1349 if(funcInf->minArgs > 0 && sc.CheckToken(TK_Ellipsis))
1350 {
1351 funcInf->varArgs = true;
1352 break;
1353 }
1354
1355 sc.MustGetToken(TK_Identifier);
1356 const Type *type = TypeHierarchy::staticTypes.GetType(sc->str);
1357 if(type == NULL)
1358 sc.ScriptMessage(Scanner::ERROR, "Unknown type %s.\n", sc->str.GetChars());
1359 funcInf->types.Push(type);
1360
1361 if(sc->str.CompareNoCase("class") == 0)
1362 {
1363 sc.MustGetToken('<');
1364 sc.MustGetToken(TK_Identifier);
1365 sc.MustGetToken('>');
1366 }
1367 sc.MustGetToken(TK_Identifier);
1368 if(optRequired || sc.CheckToken('='))
1369 {
1370 if(optRequired)
1371 sc.MustGetToken('=');
1372 else
1373 optRequired = true;
1374
1375 CallArguments::Value defVal;
1376 defVal.isExpression = false;
1377
1378 if(type == TypeHierarchy::staticTypes.GetType(TypeHierarchy::INT) ||
1379 type == TypeHierarchy::staticTypes.GetType(TypeHierarchy::FLOAT)
1380 )
1381 {
1382 ExpressionNode *node = ExpressionNode::ParseExpression(newClass, TypeHierarchy::staticTypes, sc);
1383 const ExpressionNode::Value &val = node->Evaluate(NULL);
1384 if(type == TypeHierarchy::staticTypes.GetType(TypeHierarchy::INT))
1385 {
1386 defVal.useType = CallArguments::Value::VAL_INTEGER;
1387 defVal.val.i = val.GetInt();
1388 }
1389 else
1390 {
1391 defVal.useType = CallArguments::Value::VAL_DOUBLE;
1392 defVal.val.d = val.GetDouble();
1393 }
1394 delete node;
1395 }
1396 else if(type == TypeHierarchy::staticTypes.GetType(TypeHierarchy::BOOL))
1397 {
1398 sc.MustGetToken(TK_BoolConst);
1399 defVal.useType = CallArguments::Value::VAL_INTEGER;
1400 defVal.val.i = sc->boolean;
1401 }
1402 else if(type == TypeHierarchy::staticTypes.GetType(TypeHierarchy::STATE))
1403 {
1404 defVal.useType = CallArguments::Value::VAL_STATE;
1405 if(sc.CheckToken(TK_IntConst))
1406 sc.ScriptMessage(Scanner::ERROR, "State offsets not allowed for defaults.");
1407 else
1408 {
1409 sc.MustGetToken(TK_StringConst);
1410 defVal.label = StateLabel(sc->str, newClass);
1411 }
1412 }
1413 else
1414 {
1415 sc.MustGetToken(TK_StringConst);
1416 defVal.useType = CallArguments::Value::VAL_STRING;
1417 defVal.str = sc->str;
1418 }
1419 funcInf->defaults.Push(defVal);
1420 }
1421 else
1422 ++funcInf->minArgs;
1423 ++funcInf->maxArgs;
1424 }
1425 while(sc.CheckToken(','));
1426 sc.MustGetToken(')');
1427 }
1428 sc.MustGetToken(';');
1429 }
1430 else if(sc->str.CompareNoCase("native") == 0)
1431 {
1432 sc.MustGetToken(TK_Identifier);
1433 const Type *type = TypeHierarchy::staticTypes.GetType(sc->str);
1434 if(type == NULL)
1435 sc.ScriptMessage(Scanner::ERROR, "Unknown type %s.\n", sc->str.GetChars());
1436 sc.MustGetToken(TK_Identifier);
1437 FName varName(sc->str);
1438 const SymbolInfo *symInf = NULL;
1439 for(unsigned int i = 0;i < symbolPool->Size();++i)
1440 {
1441 // I think the symbol pool will be small enough to do a
1442 // linear search on.
1443 if((*symbolPool)[i]->cls == newClass && (*symbolPool)[i]->var == varName)
1444 {
1445 symInf = (*symbolPool)[i];
1446 break;
1447 }
1448 }
1449 if(symInf == NULL)
1450 sc.ScriptMessage(Scanner::ERROR, "Could not identify symbol %s::%s.\n", newClass->name.GetChars(), varName.GetChars());
1451 sc.MustGetToken(';');
1452
1453 newClass->symbols.Push(new VariableSymbol(varName, type, symInf->offset));
1454 }
1455 else
1456 {
1457 FString className("actor");
1458 FString propertyName = sc->str;
1459 if(sc.CheckToken('.'))
1460 {
1461 className = propertyName;
1462 sc.MustGetToken(TK_Identifier);
1463 propertyName = sc->str;
1464 }
1465
1466 if(!SetProperty(newClass, className, propertyName, sc))
1467 {
1468 do
1469 {
1470 sc.GetNextToken();
1471 }
1472 while(sc.CheckToken(','));
1473 sc.ScriptMessage(Scanner::WARNING, "Unknown property '%s' for actor '%s'.", propertyName.GetChars(), newClass->name.GetChars());
1474 }
1475 }
1476 }
1477 }
1478
1479 // Now sort the symbol table.
1480 qsort(&newClass->symbols[0], newClass->symbols.Size(), sizeof(newClass->symbols[0]), SymbolCompare);
1481 if(!actionsSorted)
1482 InitFunctionTable(&newClass->actions);
1483
1484 // Register conversation id into table if assigned
1485 if(int convid = newClass->Meta.GetMetaInt(AMETA_ConversationID))
1486 ConversationIDTable[convid] = newClass;
1487 }
1488
ParseDecorateLump(int lumpNum)1489 void ClassDef::ParseDecorateLump(int lumpNum)
1490 {
1491 FWadLump lump = Wads.OpenLumpNum(lumpNum);
1492 char* data = new char[Wads.LumpLength(lumpNum)];
1493 lump.Read(data, Wads.LumpLength(lumpNum));
1494 Scanner sc(data, Wads.LumpLength(lumpNum));
1495 sc.SetScriptIdentifier(Wads.GetLumpFullName(lumpNum));
1496
1497 while(sc.TokensLeft())
1498 {
1499 if(sc.CheckToken('#'))
1500 {
1501 sc.MustGetToken(TK_Identifier);
1502 if(sc->str.CompareNoCase("include") != 0)
1503 sc.ScriptMessage(Scanner::ERROR, "Expected 'include' got '%s' instead.", sc->str.GetChars());
1504 sc.MustGetToken(TK_StringConst);
1505
1506 int lmp = Wads.CheckNumForFullName(sc->str, true);
1507 if(lmp == -1)
1508 sc.ScriptMessage(Scanner::ERROR, "Could not find lump \"%s\".", sc->str.GetChars());
1509 ParseDecorateLump(lmp);
1510 continue;
1511 }
1512
1513 sc.MustGetToken(TK_Identifier);
1514 if(sc->str.CompareNoCase("actor") == 0)
1515 {
1516 ParseActor(sc);
1517 }
1518 else if(sc->str.CompareNoCase("const") == 0)
1519 {
1520 sc.MustGetToken(TK_Identifier);
1521 const Type *type = TypeHierarchy::staticTypes.GetType(sc->str);
1522 if(type == NULL)
1523 sc.ScriptMessage(Scanner::ERROR, "Unknown type %s.\n", sc->str.GetChars());
1524 sc.MustGetToken(TK_Identifier);
1525 FName constName(sc->str);
1526 sc.MustGetToken('=');
1527 ExpressionNode *expr = ExpressionNode::ParseExpression(NATIVE_CLASS(Actor), TypeHierarchy::staticTypes, sc);
1528 ConstantSymbol *newSym = new ConstantSymbol(constName, type, expr->Evaluate(NULL));
1529 delete expr;
1530 sc.MustGetToken(';');
1531
1532 // We must insert the constant into the table at the proper place
1533 // now since the next const may try to reference it.
1534 if(globalSymbols.Size() > 0)
1535 {
1536 unsigned int min = 0;
1537 unsigned int max = globalSymbols.Size()-1;
1538 unsigned int mid = max/2;
1539 if(max > 0)
1540 {
1541 do
1542 {
1543 if(globalSymbols[mid]->GetName() > constName)
1544 max = mid-1;
1545 else if(globalSymbols[mid]->GetName() < constName)
1546 min = mid+1;
1547 else
1548 break;
1549 mid = (min+max)/2;
1550 }
1551 while(max >= min && max < globalSymbols.Size());
1552 }
1553 if(globalSymbols[mid]->GetName() <= constName)
1554 ++mid;
1555 globalSymbols.Insert(mid, newSym);
1556 }
1557 else
1558 globalSymbols.Push(newSym);
1559 }
1560 else
1561 sc.ScriptMessage(Scanner::ERROR, "Unknown thing section '%s'.", sc->str.GetChars());
1562 }
1563 delete[] data;
1564 }
1565
ResolveStateIndex(unsigned int index) const1566 const Frame *ClassDef::ResolveStateIndex(unsigned int index) const
1567 {
1568 if(index == INT_MAX) // Deleted state (Label: stop)
1569 return NULL;
1570 if(index > frameList.Size() && parent)
1571 return parent->ResolveStateIndex(index - frameList.Size());
1572 return &frameList[index];
1573 }
1574
SetFlag(const ClassDef * newClass,AActor * instance,const FString & prefix,const FString & flagName,bool set)1575 bool ClassDef::SetFlag(const ClassDef *newClass, AActor *instance, const FString &prefix, const FString &flagName, bool set)
1576 {
1577 int min = 0;
1578 int max = sizeof(flags)/sizeof(FlagDef) - 1;
1579 while(min <= max)
1580 {
1581 int mid = (min+max)/2;
1582 int ret = flagName.CompareNoCase(flags[mid].name);
1583 if(ret == 0 && !prefix.IsEmpty())
1584 ret = prefix.CompareNoCase(flags[mid].prefix);
1585
1586 if(ret == 0)
1587 {
1588 if(!newClass->IsDescendantOf(flags[mid].cls))
1589 return false;
1590
1591 if(set)
1592 *reinterpret_cast<flagstype_t *>((int8_t*)instance + flags[mid].varOffset) |= flags[mid].value;
1593 else
1594 *reinterpret_cast<flagstype_t *>((int8_t*)instance + flags[mid].varOffset) &= ~flags[mid].value;
1595 return true;
1596 }
1597 else if(ret < 0)
1598 max = mid-1;
1599 else
1600 min = mid+1;
1601 }
1602 return false;
1603 }
1604
SetProperty(ClassDef * newClass,const char * className,const char * propName,Scanner & sc)1605 bool ClassDef::SetProperty(ClassDef *newClass, const char* className, const char* propName, Scanner &sc)
1606 {
1607 static unsigned int NUM_PROPERTIES = 0;
1608 if(NUM_PROPERTIES == 0)
1609 {
1610 // Calculate NUM_PROPERTIES if needed.
1611 while(properties[NUM_PROPERTIES++].name != NULL)
1612 ;
1613 }
1614
1615 int min = 0;
1616 int max = NUM_PROPERTIES - 1;
1617 while(min <= max)
1618 {
1619 int mid = (min+max)/2;
1620 int ret = stricmp(properties[mid].name, propName);
1621 if(ret == 0)
1622 {
1623 if(!newClass->IsDescendantOf(properties[mid].className) ||
1624 stricmp(properties[mid].prefix, className) != 0)
1625 sc.ScriptMessage(Scanner::ERROR, "Property %s.%s not available in this scope.\n", properties[mid].className->name.GetChars(), propName);
1626
1627 PropertyParam* params = new PropertyParam[strlen(properties[mid].params)];
1628 // Key:
1629 // K - Keyword (Identifier)
1630 // I - Integer
1631 // F - Float
1632 // S - String
1633 bool optional = false;
1634 bool done = false;
1635 const char* p = properties[mid].params;
1636 unsigned int paramc = 0;
1637 if(*p != 0)
1638 {
1639 do
1640 {
1641 if(*p != 0)
1642 {
1643 while(*p == '_') // Optional
1644 {
1645 optional = true;
1646 p++;
1647 }
1648
1649 bool negate = false;
1650 params[paramc].i = 0; // Try to default to 0
1651
1652 switch(*p)
1653 {
1654 case 'K':
1655 if(!optional)
1656 sc.MustGetToken(TK_Identifier);
1657 else if(!sc.CheckToken(TK_Identifier))
1658 {
1659 done = true;
1660 break;
1661 }
1662 params[paramc].s = new char[sc->str.Len()+1];
1663 strcpy(params[paramc].s, sc->str);
1664 break;
1665 default:
1666 case 'I':
1667 if(sc.CheckToken('('))
1668 {
1669 params[paramc].isExpression = true;
1670 params[paramc].expr = ExpressionNode::ParseExpression(newClass, TypeHierarchy::staticTypes, sc, NULL);
1671 sc.MustGetToken(')');
1672 break;
1673 }
1674 else
1675 params[paramc].isExpression = false;
1676
1677 if(sc.CheckToken('-'))
1678 negate = true;
1679
1680 if(!optional) // Float also includes integers
1681 sc.MustGetToken(TK_FloatConst);
1682 else if(!sc.CheckToken(TK_FloatConst))
1683 {
1684 done = true;
1685 break;
1686 }
1687 params[paramc].i = (negate ? -1 : 1) * static_cast<int64_t> (sc->decimal);
1688 break;
1689 case 'F':
1690 if(sc.CheckToken('-'))
1691 negate = true;
1692
1693 if(!optional)
1694 sc.MustGetToken(TK_FloatConst);
1695 else if(!sc.CheckToken(TK_FloatConst))
1696 {
1697 done = true;
1698 break;
1699 }
1700 params[paramc].f = (negate ? -1 : 1) * sc->decimal;
1701 break;
1702 case 'S':
1703 if(!optional)
1704 sc.MustGetToken(TK_StringConst);
1705 else if(!sc.CheckToken(TK_StringConst))
1706 {
1707 done = true;
1708 break;
1709 }
1710 params[paramc].s = new char[sc->str.Len()+1];
1711 strcpy(params[paramc].s, sc->str);
1712 break;
1713 }
1714 paramc++;
1715 p++;
1716 }
1717 else
1718 sc.GetNextToken();
1719 }
1720 while(sc.CheckToken(','));
1721 }
1722 if(!optional && *p != 0 && *p != '_')
1723 sc.ScriptMessage(Scanner::ERROR, "Not enough parameters.");
1724
1725 properties[mid].handler(newClass, (AActor*)newClass->defaultInstance, paramc, params);
1726
1727 // Clean up
1728 p = properties[mid].params;
1729 unsigned int i = 0;
1730 do
1731 {
1732 if(*p == 'S' || *p == 'K')
1733 delete[] params[i].s;
1734 i++;
1735 }
1736 while(*(++p) != 0 && i < paramc);
1737 delete[] params;
1738 return true;
1739 }
1740 else if(ret > 0)
1741 max = mid-1;
1742 else
1743 min = mid+1;
1744 }
1745 return false;
1746 }
1747
UnloadActors()1748 void ClassDef::UnloadActors()
1749 {
1750 TMap<FName, ClassDef *>::Pair *pair;
1751
1752 // Clean up the frames in case any expressions use the symbols
1753 for(TMap<FName, ClassDef *>::Iterator iter(ClassTable());iter.NextPair(pair);)
1754 pair->Value->frameList.Clear();
1755
1756 // Also contains code from ZDoom
1757
1758 bShutdown = true;
1759
1760 TArray<size_t *> uniqueFPs;
1761 for(TMap<FName, ClassDef *>::Iterator iter(ClassTable());iter.NextPair(pair);)
1762 {
1763 ClassDef *type = pair->Value;
1764 if (type->FlatPointers != &POINTER_END && type->FlatPointers != type->Pointers)
1765 {
1766 // FlatPointers are shared by many classes, so we must check for
1767 // duplicates and only delete those that are unique.
1768 unsigned int j;
1769 for (j = 0; j < uniqueFPs.Size(); ++j)
1770 {
1771 if (type->FlatPointers == uniqueFPs[j])
1772 {
1773 break;
1774 }
1775 }
1776 if (j == uniqueFPs.Size())
1777 {
1778 uniqueFPs.Push(const_cast<size_t *>(type->FlatPointers));
1779 }
1780 }
1781 type->FlatPointers = NULL;
1782
1783 delete type;
1784 }
1785 for (unsigned int i = 0; i < uniqueFPs.Size(); ++i)
1786 {
1787 delete[] uniqueFPs[i];
1788 }
1789
1790 // Also clear globals
1791 // but first clear the damage expression table since it relies on some of the symbols.
1792 AActor::damageExpressions.Clear();
1793 for(unsigned int i = 0;i < globalSymbols.Size();++i)
1794 delete globalSymbols[i];
1795 }
1796
1797 ////////////////////////////////////////////////////////////////////////////////
1798
DumpClasses()1799 void ClassDef::DumpClasses()
1800 {
1801 struct ClassTree
1802 {
1803 public:
1804 ClassTree(const ClassDef *classType) : child(NULL), next(NULL), thisClass(classType)
1805 {
1806 ClassTree **nextChild = &child;
1807 TMap<FName, ClassDef *>::Pair *pair;
1808 for(TMap<FName, ClassDef *>::Iterator iter(ClassTable());iter.NextPair(pair);)
1809 {
1810 if(pair->Value->parent == classType)
1811 {
1812 *nextChild = new ClassTree(pair->Value);
1813 nextChild = &(*nextChild)->next;
1814 }
1815 }
1816 }
1817
1818 ~ClassTree()
1819 {
1820 if(child != NULL)
1821 delete child;
1822 if(next != NULL)
1823 delete next;
1824 }
1825
1826 void Dump(int spacing)
1827 {
1828 for(int i = spacing;i > 0;--i)
1829 printf(" ");
1830 printf("%s\n", thisClass->name.GetChars());
1831 if(child != NULL)
1832 child->Dump(spacing+1);
1833 if(next != NULL)
1834 next->Dump(spacing);
1835 }
1836
1837 ClassTree *child;
1838 ClassTree *next;
1839 const ClassDef *thisClass;
1840 };
1841
1842 ClassTree root(FindClass("Actor"));
1843 root.Dump(0);
1844 }
1845
1846 ////////////////////////////////////////////////////////////////////////////////
1847
~CallArguments()1848 CallArguments::~CallArguments()
1849 {
1850 for(unsigned int i = 0;i < args.Size();++i)
1851 {
1852 if(args[i].isExpression)
1853 delete args[i].expr;
1854 }
1855 }
1856
AddArgument(const CallArguments::Value & val)1857 void CallArguments::AddArgument(const CallArguments::Value &val)
1858 {
1859 args.Push(val);
1860 }
1861
Evaluate(AActor * self)1862 void CallArguments::Evaluate(AActor *self)
1863 {
1864 for(unsigned int i = 0;i < args.Size();++i)
1865 {
1866 if(args[i].isExpression)
1867 {
1868 const ExpressionNode::Value &val = args[i].expr->Evaluate(self);
1869 if(args[i].useType == Value::VAL_INTEGER)
1870 args[i].val.i = val.GetInt();
1871 else
1872 args[i].val.d = val.GetDouble();
1873 }
1874 }
1875 }
1876