1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** $Id: vc_decorate.cpp 4327 2010-07-24 19:30:53Z firebrand_kh $
11 //**
12 //** Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //** This program is free software; you can redistribute it and/or
15 //** modify it under the terms of the GNU General Public License
16 //** as published by the Free Software Foundation; either version 2
17 //** of the License, or (at your option) any later version.
18 //**
19 //** This program is distributed in the hope that it will be useful,
20 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 //** GNU General Public License for more details.
23 //**
24 //**************************************************************************
25
26 // HEADER FILES ------------------------------------------------------------
27
28 #include "vc_local.h"
29 #include "sv_local.h"
30
31 // MACROS ------------------------------------------------------------------
32
33 // TYPES -------------------------------------------------------------------
34
35 enum
36 {
37 PROPS_HASH_SIZE = 256,
38 FLAGS_HASH_SIZE = 256,
39 };
40
41 enum
42 {
43 OLDDEC_Decoration,
44 OLDDEC_Breakable,
45 OLDDEC_Projectile,
46 OLDDEC_Pickup,
47 };
48
49 enum
50 {
51 BOUNCE_None,
52 BOUNCE_Doom,
53 BOUNCE_Heretic,
54 BOUNCE_Hexen
55 };
56
57 enum
58 {
59 PROP_Int,
60 PROP_IntConst,
61 PROP_IntUnsupported,
62 PROP_BitIndex,
63 PROP_Float,
64 PROP_Speed,
65 PROP_Tics,
66 PROP_TicsSecs,
67 PROP_Percent,
68 PROP_FloatClamped,
69 PROP_FloatClamped2,
70 PROP_FloatOpt2,
71 PROP_Name,
72 PROP_NameLower,
73 PROP_Str,
74 PROP_StrUnsupported,
75 PROP_Class,
76 PROP_Power_Class,
77 PROP_BoolConst,
78 PROP_State,
79 PROP_Game,
80 PROP_SpawnId,
81 PROP_ConversationId,
82 PROP_PainChance,
83 PROP_DamageFactor,
84 PROP_MissileDamage,
85 PROP_VSpeed,
86 PROP_RenderStyle,
87 PROP_Translation,
88 PROP_BloodColour,
89 PROP_BloodType,
90 PROP_StencilColour,
91 PROP_Monster,
92 PROP_Projectile,
93 PROP_BounceType,
94 PROP_ClearFlags,
95 PROP_DropItem,
96 PROP_States,
97 PROP_SkipSuper,
98 PROP_Args,
99 PROP_LowMessage,
100 PROP_PowerupColour,
101 PROP_ColourRange,
102 PROP_DamageScreenColour,
103 PROP_HexenArmor,
104 PROP_StartItem,
105 PROP_MorphStyle,
106 };
107
108 enum
109 {
110 FLAG_Bool,
111 FLAG_Unsupported,
112 FLAG_Byte,
113 FLAG_Float,
114 FLAG_Name,
115 FLAG_Class,
116 FLAG_NoClip,
117 };
118
119 struct VClassFixup
120 {
121 int Offset;
122 VStr Name;
123 VClass* ReqParent;
124 VClass* Class;
125 };
126
127 struct VPropDef
128 {
129 vuint8 Type;
130 int HashNext;
131 VName Name;
132 VField* Field;
133 VField* Field2;
134 VName PropName;
135 union
136 {
137 int IConst;
138 float FMin;
139 };
140 float FMax;
141 VStr CPrefix;
142
143 void SetField(VClass*, const char*);
144 void SetField2(VClass*, const char*);
145 };
146
147 struct VFlagDef
148 {
149 vuint8 Type;
150 int HashNext;
151 VName Name;
152 VField* Field;
153 VField* Field2;
154 union
155 {
156 vuint8 BTrue;
157 float FTrue;
158 };
159 VName NTrue;
160 union
161 {
162 vuint8 BFalse;
163 float FFalse;
164 };
165 VName NFalse;
166
167 void SetField(VClass*, const char*);
168 void SetField2(VClass*, const char*);
169 };
170
171 struct VFlagList
172 {
173 VClass* Class;
174
175 TArray<VPropDef> Props;
176 int PropsHash[PROPS_HASH_SIZE];
177
178 TArray<VFlagDef> Flags;
179 int FlagsHash[FLAGS_HASH_SIZE];
180
181 VPropDef& NewProp(vuint8, VXmlNode*);
182 VFlagDef& NewFlag(vuint8, VXmlNode*);
183 };
184
185 //==========================================================================
186 //
187 // VDecorateInvocation
188 //
189 //==========================================================================
190
191 class VDecorateInvocation : public VExpression
192 {
193 public:
194 VName Name;
195 int NumArgs;
196 VExpression* Args[VMethod::MAX_PARAMS + 1];
197
198 VDecorateInvocation(VName, const TLocation&, int, VExpression**);
199 ~VDecorateInvocation();
200 VExpression* DoResolve(VEmitContext&);
201 void Emit(VEmitContext&);
202 };
203
204 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
205
206 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
207
208 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
209
210 static VExpression* ParseExpressionPriority13(VScriptParser* sc);
211
212 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
213
214 // PUBLIC DATA DEFINITIONS -------------------------------------------------
215
216 TArray<VLineSpecInfo> LineSpecialInfos;
217
218 // PRIVATE DATA DEFINITIONS ------------------------------------------------
219
220 static VPackage* DecPkg;
221
222 static VClass* ActorClass;
223 static VClass* FakeInventoryClass;
224 static VClass* InventoryClass;
225 static VClass* AmmoClass;
226 static VClass* BasicArmorPickupClass;
227 static VClass* BasicArmorBonusClass;
228 static VClass* HealthClass;
229 static VClass* PowerupGiverClass;
230 static VClass* PuzzleItemClass;
231 static VClass* WeaponClass;
232 static VClass* WeaponPieceClass;
233 static VClass* PlayerPawnClass;
234 static VClass* MorphProjectileClass;
235
236 static VMethod* FuncA_Scream;
237 static VMethod* FuncA_NoBlocking;
238 static VMethod* FuncA_ScreamAndUnblock;
239 static VMethod* FuncA_ActiveSound;
240 static VMethod* FuncA_ActiveAndUnblock;
241 static VMethod* FuncA_ExplodeParms;
242 static VMethod* FuncA_FreezeDeath;
243 static VMethod* FuncA_FreezeDeathChunks;
244
245 static TArray<VFlagList> FlagList;
246
247 // CODE --------------------------------------------------------------------
248
249 //==========================================================================
250 //
251 // ParseDecorateDef
252 //
253 //==========================================================================
254
ParseDecorateDef(VXmlDocument & Doc)255 static void ParseDecorateDef(VXmlDocument& Doc)
256 {
257 guard(ParseDecorateDef);
258 for (VXmlNode* N = Doc.Root.FindChild("class"); N; N = N->FindNext())
259 {
260 VStr ClassName = N->GetAttribute("name");
261 VFlagList& Lst = FlagList.Alloc();
262 Lst.Class = VClass::FindClass(*ClassName);
263 for (int i = 0; i < PROPS_HASH_SIZE; i++)
264 {
265 Lst.PropsHash[i] = -1;
266 }
267 for (int i = 0; i < FLAGS_HASH_SIZE; i++)
268 {
269 Lst.FlagsHash[i] = -1;
270 }
271 for (VXmlNode* PN = N->FirstChild; PN; PN = PN->NextSibling)
272 {
273 if (PN->Name == "prop_int")
274 {
275 VPropDef& P = Lst.NewProp(PROP_Int, PN);
276 P.SetField(Lst.Class, *PN->GetAttribute("property"));
277 }
278 else if (PN->Name == "prop_int_const")
279 {
280 VPropDef& P = Lst.NewProp(PROP_IntConst, PN);
281 P.SetField(Lst.Class, *PN->GetAttribute("property"));
282 P.IConst = atoi(*PN->GetAttribute("value"));
283 }
284 else if (PN->Name == "prop_int_unsupported")
285 {
286 VPropDef& P = Lst.NewProp(PROP_IntUnsupported, PN);
287 }
288 else if (PN->Name == "prop_bit_index")
289 {
290 VPropDef& P = Lst.NewProp(PROP_BitIndex, PN);
291 P.SetField(Lst.Class, *PN->GetAttribute("property"));
292 }
293 else if (PN->Name == "prop_float")
294 {
295 VPropDef& P = Lst.NewProp(PROP_Float, PN);
296 P.SetField(Lst.Class, *PN->GetAttribute("property"));
297 }
298 else if (PN->Name == "prop_speed")
299 {
300 VPropDef& P = Lst.NewProp(PROP_Speed, PN);
301 P.SetField(Lst.Class, *PN->GetAttribute("property"));
302 }
303 else if (PN->Name == "prop_tics")
304 {
305 VPropDef& P = Lst.NewProp(PROP_Tics, PN);
306 P.SetField(Lst.Class, *PN->GetAttribute("property"));
307 }
308 else if (PN->Name == "prop_tics_secs")
309 {
310 VPropDef& P = Lst.NewProp(PROP_TicsSecs, PN);
311 P.SetField(Lst.Class, *PN->GetAttribute("property"));
312 }
313 else if (PN->Name == "prop_percent")
314 {
315 VPropDef& P = Lst.NewProp(PROP_Percent, PN);
316 P.SetField(Lst.Class, *PN->GetAttribute("property"));
317 }
318 else if (PN->Name == "prop_float_clamped")
319 {
320 VPropDef& P = Lst.NewProp(PROP_FloatClamped, PN);
321 P.SetField(Lst.Class, *PN->GetAttribute("property"));
322 P.FMin = atof(*PN->GetAttribute("min"));
323 P.FMax = atof(*PN->GetAttribute("max"));
324 }
325 else if (PN->Name == "prop_float_clamped_2")
326 {
327 VPropDef& P = Lst.NewProp(PROP_FloatClamped2, PN);
328 P.SetField(Lst.Class, *PN->GetAttribute("property"));
329 P.SetField2(Lst.Class, *PN->GetAttribute("property2"));
330 P.FMin = atof(*PN->GetAttribute("min"));
331 P.FMax = atof(*PN->GetAttribute("max"));
332 }
333 else if (PN->Name == "prop_float_optional_2")
334 {
335 VPropDef& P = Lst.NewProp(PROP_FloatOpt2, PN);
336 P.SetField(Lst.Class, *PN->GetAttribute("property"));
337 P.SetField2(Lst.Class, *PN->GetAttribute("property2"));
338 }
339 else if (PN->Name == "prop_name")
340 {
341 VPropDef& P = Lst.NewProp(PROP_Name, PN);
342 P.SetField(Lst.Class, *PN->GetAttribute("property"));
343 }
344 else if (PN->Name == "prop_name_lower")
345 {
346 VPropDef& P = Lst.NewProp(PROP_NameLower, PN);
347 P.SetField(Lst.Class, *PN->GetAttribute("property"));
348 }
349 else if (PN->Name == "prop_string")
350 {
351 VPropDef& P = Lst.NewProp(PROP_Str, PN);
352 P.SetField(Lst.Class, *PN->GetAttribute("property"));
353 }
354 else if (PN->Name == "prop_string_unsupported")
355 {
356 VPropDef& P = Lst.NewProp(PROP_StrUnsupported, PN);
357 }
358 else if (PN->Name == "prop_class")
359 {
360 VPropDef& P = Lst.NewProp(PROP_Class, PN);
361 P.SetField(Lst.Class, *PN->GetAttribute("property"));
362 if (PN->HasAttribute("prefix"))
363 {
364 P.CPrefix = PN->GetAttribute("prefix");
365 }
366 }
367 else if (PN->Name == "prop_power_class")
368 {
369 VPropDef& P = Lst.NewProp(PROP_Power_Class, PN);
370 P.SetField(Lst.Class, *PN->GetAttribute("property"));
371 if (PN->HasAttribute("prefix"))
372 {
373 P.CPrefix = PN->GetAttribute("prefix");
374 }
375 }
376 else if (PN->Name == "prop_bool_const")
377 {
378 VPropDef& P = Lst.NewProp(PROP_BoolConst, PN);
379 P.SetField(Lst.Class, *PN->GetAttribute("property"));
380 P.IConst = !PN->GetAttribute("value").ICmp("true");
381 }
382 else if (PN->Name == "prop_state")
383 {
384 VPropDef& P = Lst.NewProp(PROP_State, PN);
385 P.PropName = *PN->GetAttribute("property");
386 }
387 else if (PN->Name == "prop_game")
388 {
389 VPropDef& P = Lst.NewProp(PROP_Game, PN);
390 }
391 else if (PN->Name == "prop_spawn_id")
392 {
393 VPropDef& P = Lst.NewProp(PROP_SpawnId, PN);
394 }
395 else if (PN->Name == "prop_conversation_id")
396 {
397 VPropDef& P = Lst.NewProp(PROP_ConversationId, PN);
398 P.SetField(Lst.Class, "ConversationID");
399 }
400 else if (PN->Name == "prop_pain_chance")
401 {
402 VPropDef& P = Lst.NewProp(PROP_PainChance, PN);
403 P.SetField(Lst.Class, "PainChance");
404 }
405 else if (PN->Name == "prop_damage_factor")
406 {
407 VPropDef& P = Lst.NewProp(PROP_DamageFactor, PN);
408 }
409 else if (PN->Name == "prop_missile_damage")
410 {
411 VPropDef& P = Lst.NewProp(PROP_MissileDamage, PN);
412 P.SetField(Lst.Class, "MissileDamage");
413 }
414 else if (PN->Name == "prop_vspeed")
415 {
416 VPropDef& P = Lst.NewProp(PROP_VSpeed, PN);
417 P.SetField(Lst.Class, "Velocity");
418 }
419 else if (PN->Name == "prop_render_style")
420 {
421 VPropDef& P = Lst.NewProp(PROP_RenderStyle, PN);
422 P.SetField(Lst.Class, *PN->GetAttribute("property"));
423 }
424 else if (PN->Name == "prop_translation")
425 {
426 VPropDef& P = Lst.NewProp(PROP_Translation, PN);
427 P.SetField(Lst.Class, *PN->GetAttribute("property"));
428 }
429 else if (PN->Name == "prop_blood_colour")
430 {
431 VPropDef& P = Lst.NewProp(PROP_BloodColour, PN);
432 P.SetField(Lst.Class, "BloodColour");
433 P.SetField2(Lst.Class, "BloodTranslation");
434 }
435 else if (PN->Name == "prop_blood_type")
436 {
437 VPropDef& P = Lst.NewProp(PROP_BloodType, PN);
438 P.SetField(Lst.Class, "BloodType");
439 P.SetField2(Lst.Class, "BloodSplatterType");
440 }
441 else if (PN->Name == "prop_stencil_colour")
442 {
443 VPropDef& P = Lst.NewProp(PROP_StencilColour, PN);
444 }
445 else if (PN->Name == "prop_monster")
446 {
447 VPropDef& P = Lst.NewProp(PROP_Monster, PN);
448 }
449 else if (PN->Name == "prop_projectile")
450 {
451 VPropDef& P = Lst.NewProp(PROP_Projectile, PN);
452 }
453 else if (PN->Name == "prop_bouncetype")
454 {
455 VPropDef& P = Lst.NewProp(PROP_BounceType, PN);
456 }
457 else if (PN->Name == "prop_clear_flags")
458 {
459 VPropDef& P = Lst.NewProp(PROP_ClearFlags, PN);
460 }
461 else if (PN->Name == "prop_drop_item")
462 {
463 VPropDef& P = Lst.NewProp(PROP_DropItem, PN);
464 }
465 else if (PN->Name == "prop_states")
466 {
467 VPropDef& P = Lst.NewProp(PROP_States, PN);
468 }
469 else if (PN->Name == "prop_skip_super")
470 {
471 VPropDef& P = Lst.NewProp(PROP_SkipSuper, PN);
472 }
473 else if (PN->Name == "prop_args")
474 {
475 VPropDef& P = Lst.NewProp(PROP_Args, PN);
476 P.SetField(Lst.Class, "Args");
477 P.SetField2(Lst.Class, "bArgsDefined");
478 }
479 else if (PN->Name == "prop_low_message")
480 {
481 VPropDef& P = Lst.NewProp(PROP_LowMessage, PN);
482 P.SetField(Lst.Class, "LowHealth");
483 P.SetField2(Lst.Class, "LowHealthMessage");
484 }
485 else if (PN->Name == "prop_powerup_colour")
486 {
487 VPropDef& P = Lst.NewProp(PROP_PowerupColour, PN);
488 P.SetField(Lst.Class, "BlendColour");
489 }
490 else if (PN->Name == "prop_colour_range")
491 {
492 VPropDef& P = Lst.NewProp(PROP_ColourRange, PN);
493 P.SetField(Lst.Class, "TranslStart");
494 P.SetField2(Lst.Class, "TranslEnd");
495 }
496 else if (PN->Name == "prop_damage_screen_colour")
497 {
498 VPropDef& P = Lst.NewProp(PROP_DamageScreenColour, PN);
499 P.SetField(Lst.Class, "DamageScreenColour");
500 }
501 else if (PN->Name == "prop_hexen_armor")
502 {
503 VPropDef& P = Lst.NewProp(PROP_HexenArmor, PN);
504 P.SetField(Lst.Class, "HexenArmor");
505 }
506 else if (PN->Name == "prop_start_item")
507 {
508 VPropDef& P = Lst.NewProp(PROP_StartItem, PN);
509 P.SetField(Lst.Class, "DropItemList");
510 }
511 else if (PN->Name == "prop_morph_style")
512 {
513 VPropDef& P = Lst.NewProp(PROP_MorphStyle, PN);
514 P.SetField(Lst.Class, *PN->GetAttribute("property"));
515 }
516 else if (PN->Name == "flag")
517 {
518 VFlagDef& F = Lst.NewFlag(FLAG_Bool, PN);
519 F.SetField(Lst.Class, *PN->GetAttribute("property"));
520 }
521 else if (PN->Name == "flag_unsupported")
522 {
523 VFlagDef& F = Lst.NewFlag(FLAG_Unsupported, PN);
524 }
525 else if (PN->Name == "flag_byte")
526 {
527 VFlagDef& F = Lst.NewFlag(FLAG_Byte, PN);
528 F.SetField(Lst.Class, *PN->GetAttribute("property"));
529 F.BTrue = atoi(*PN->GetAttribute("true_value"));
530 F.BFalse = atoi(*PN->GetAttribute("false_value"));
531 }
532 else if (PN->Name == "flag_float")
533 {
534 VFlagDef& F = Lst.NewFlag(FLAG_Float, PN);
535 F.SetField(Lst.Class, *PN->GetAttribute("property"));
536 F.FTrue = atof(*PN->GetAttribute("true_value"));
537 F.FFalse = atof(*PN->GetAttribute("false_value"));
538 }
539 else if (PN->Name == "flag_name")
540 {
541 VFlagDef& F = Lst.NewFlag(FLAG_Name, PN);
542 F.SetField(Lst.Class, *PN->GetAttribute("property"));
543 F.NTrue = *PN->GetAttribute("true_value");
544 F.NFalse = *PN->GetAttribute("false_value");
545 }
546 else if (PN->Name == "flag_class")
547 {
548 VFlagDef& F = Lst.NewFlag(FLAG_Class, PN);
549 F.SetField(Lst.Class, *PN->GetAttribute("property"));
550 F.NTrue = *PN->GetAttribute("true_value");
551 F.NFalse = *PN->GetAttribute("false_value");
552 }
553 else if (PN->Name == "flag_noclip")
554 {
555 VFlagDef& F = Lst.NewFlag(FLAG_NoClip, PN);
556 F.SetField(Lst.Class, "bColideWithThings");
557 F.SetField2(Lst.Class, "bColideWithWorld");
558 }
559 else
560 {
561 Sys_Error("Unknown XML node %s", *PN->Name);
562 }
563 }
564 }
565 unguard;
566 }
567
568 //==========================================================================
569 //
570 // VPropDef::SetField
571 //
572 //==========================================================================
573
SetField(VClass * Class,const char * FieldName)574 void VPropDef::SetField(VClass* Class, const char* FieldName)
575 {
576 guard(VPropDef::SetField);
577 Field = Class->FindFieldChecked(FieldName);
578 unguard;
579 }
580
581 //==========================================================================
582 //
583 // VPropDef::SetField2
584 //
585 //==========================================================================
586
SetField2(VClass * Class,const char * FieldName)587 void VPropDef::SetField2(VClass* Class, const char* FieldName)
588 {
589 guard(VPropDef::SetField2);
590 Field2 = Class->FindFieldChecked(FieldName);
591 unguard;
592 }
593
594 //==========================================================================
595 //
596 // VFlagDef::SetField
597 //
598 //==========================================================================
599
SetField(VClass * Class,const char * FieldName)600 void VFlagDef::SetField(VClass* Class, const char* FieldName)
601 {
602 guard(VFlagDef::SetField);
603 Field = Class->FindFieldChecked(FieldName);
604 unguard;
605 }
606
607 //==========================================================================
608 //
609 // VFlagDef::SetField2
610 //
611 //==========================================================================
612
SetField2(VClass * Class,const char * FieldName)613 void VFlagDef::SetField2(VClass* Class, const char* FieldName)
614 {
615 guard(VFlagDef::SetField2);
616 Field2 = Class->FindFieldChecked(FieldName);
617 unguard;
618 }
619
620 //==========================================================================
621 //
622 // VFlagList::NewProp
623 //
624 //==========================================================================
625
NewProp(vuint8 Type,VXmlNode * PN)626 VPropDef& VFlagList::NewProp(vuint8 Type, VXmlNode* PN)
627 {
628 guard(VFlagList::NewProp);
629 VPropDef& P = Props.Alloc();
630 P.Type = Type;
631 P.Name = *PN->GetAttribute("name").ToLower();
632 int HashIndex = GetTypeHash(P.Name) & (PROPS_HASH_SIZE - 1);
633 P.HashNext = PropsHash[HashIndex];
634 PropsHash[HashIndex] = Props.Num() - 1;
635 return P;
636 unguard;
637 }
638
639 //==========================================================================
640 //
641 // VFlagList::NewFlag
642 //
643 //==========================================================================
644
NewFlag(vuint8 Type,VXmlNode * PN)645 VFlagDef& VFlagList::NewFlag(vuint8 Type, VXmlNode* PN)
646 {
647 guard(VFlagList::NewFlag);
648 VFlagDef& F = Flags.Alloc();
649 F.Type = Type;
650 F.Name = *PN->GetAttribute("name").ToLower();
651 int HashIndex = GetTypeHash(F.Name) & (FLAGS_HASH_SIZE - 1);
652 F.HashNext = FlagsHash[HashIndex];
653 FlagsHash[HashIndex] = Flags.Num() - 1;
654 return F;
655 unguard;
656 }
657
658 //==========================================================================
659 //
660 // VDecorateSingleName::VDecorateSingleName
661 //
662 //==========================================================================
663
VDecorateSingleName(const VStr & AName,const TLocation & ALoc)664 VDecorateSingleName::VDecorateSingleName(const VStr& AName,
665 const TLocation& ALoc)
666 : VExpression(ALoc)
667 , Name(AName)
668 {
669 }
670
671 //==========================================================================
672 //
673 // VDecorateSingleName::DoResolve
674 //
675 //==========================================================================
676
DoResolve(VEmitContext & ec)677 VExpression* VDecorateSingleName::DoResolve(VEmitContext& ec)
678 {
679 guard(VDecorateSingleName::DoResolve);
680 VName CheckName = va("decorate_%s", *Name.ToLower());
681 if (ec.SelfClass)
682 {
683 VConstant* Const = ec.SelfClass->FindConstant(CheckName);
684 if (Const)
685 {
686 VExpression* e = new VConstantValue(Const, Loc);
687 delete this;
688 return e->Resolve(ec);
689 }
690
691 VProperty* Prop = ec.SelfClass->FindProperty(CheckName);
692 if (Prop)
693 {
694 if (!Prop->GetFunc)
695 {
696 ParseError(Loc, "Property %s cannot be read", *Name);
697 delete this;
698 return NULL;
699 }
700 VExpression* e = new VInvocation(NULL, Prop->GetFunc, NULL,
701 false, false, Loc, 0, NULL);
702 delete this;
703 return e->Resolve(ec);
704 }
705 }
706
707 CheckName = *Name.ToLower();
708 // Look only for constants defined in DECORATE scripts.
709 VConstant* Const = ec.Package->FindConstant(CheckName);
710 if (Const)
711 {
712 VExpression* e = new VConstantValue(Const, Loc);
713 delete this;
714 return e->Resolve(ec);
715 }
716
717 ParseError(Loc, "Illegal expression identifier %s", *Name);
718 delete this;
719 return NULL;
720 unguard;
721 }
722
723 //==========================================================================
724 //
725 // VDecorateSingleName::Emit
726 //
727 //==========================================================================
728
Emit(VEmitContext &)729 void VDecorateSingleName::Emit(VEmitContext&)
730 {
731 ParseError(Loc, "Should not happen");
732 }
733
734 //==========================================================================
735 //
736 // VDecorateSingleName::IsDecorateSingleName
737 //
738 //==========================================================================
739
IsDecorateSingleName() const740 bool VDecorateSingleName::IsDecorateSingleName() const
741 {
742 return true;
743 }
744
745 //==========================================================================
746 //
747 // VDecorateInvocation::VDecorateInvocation
748 //
749 //==========================================================================
750
VDecorateInvocation(VName AName,const TLocation & ALoc,int ANumArgs,VExpression ** AArgs)751 VDecorateInvocation::VDecorateInvocation(VName AName, const TLocation& ALoc, int ANumArgs,
752 VExpression** AArgs)
753 : VExpression(ALoc)
754 , Name(AName)
755 , NumArgs(ANumArgs)
756 {
757 for (int i = 0; i < NumArgs; i++)
758 Args[i] = AArgs[i];
759 }
760
761 //==========================================================================
762 //
763 // VDecorateInvocation::~VDecorateInvocation
764 //
765 //==========================================================================
766
~VDecorateInvocation()767 VDecorateInvocation::~VDecorateInvocation()
768 {
769 for (int i = 0; i < NumArgs; i++)
770 if (Args[i])
771 {
772 delete Args[i];
773 Args[i] = NULL;
774 }
775 }
776
777 //==========================================================================
778 //
779 // VDecorateInvocation::DoResolve
780 //
781 //==========================================================================
782
DoResolve(VEmitContext & ec)783 VExpression* VDecorateInvocation::DoResolve(VEmitContext& ec)
784 {
785 guard(VDecorateInvocation::DoResolve);
786 if (ec.SelfClass)
787 {
788 // First try with decorate_ prefix, then without.
789 VMethod* M = ec.SelfClass->FindMethod(va("decorate_%s", *Name));
790 if (!M)
791 {
792 M = ec.SelfClass->FindMethod(Name);
793 }
794 if (M)
795 {
796 if (M->Flags & FUNC_Iterator)
797 {
798 ParseError(Loc, "Iterator methods can only be used in foreach statements");
799 delete this;
800 return NULL;
801 }
802 VExpression* e = new VInvocation(NULL, M, NULL,
803 false, false, Loc, NumArgs, Args);
804 NumArgs = 0;
805 delete this;
806 return e->Resolve(ec);
807 }
808 }
809
810 ParseError(Loc, "Unknown method %s", *Name);
811 delete this;
812 return NULL;
813 unguard;
814 }
815
816 //==========================================================================
817 //
818 // VDecorateInvocation::Emit
819 //
820 //==========================================================================
821
Emit(VEmitContext &)822 void VDecorateInvocation::Emit(VEmitContext&)
823 {
824 ParseError(Loc, "Should not happen");
825 }
826
827 //==========================================================================
828 //
829 // GetClassFieldFloat
830 //
831 //==========================================================================
832
GetClassFieldFloat(VClass * Class,VName FieldName)833 static float GetClassFieldFloat(VClass* Class, VName FieldName)
834 {
835 guard(GetClassFieldFloat);
836 VField* F = Class->FindFieldChecked(FieldName);
837 return F->GetFloat((VObject*)Class->Defaults);
838 unguard;
839 }
840
841 //==========================================================================
842 //
843 // GetClassFieldVec
844 //
845 //==========================================================================
846
GetClassFieldVec(VClass * Class,VName FieldName)847 static TVec GetClassFieldVec(VClass* Class, VName FieldName)
848 {
849 guard(GetClassFieldVec);
850 VField* F = Class->FindFieldChecked(FieldName);
851 return F->GetVec((VObject*)Class->Defaults);
852 unguard;
853 }
854
855 //==========================================================================
856 //
857 // GetClassDropItems
858 //
859 //==========================================================================
860
GetClassDropItems(VClass * Class)861 static TArray<VDropItemInfo>& GetClassDropItems(VClass* Class)
862 {
863 guard(GetClassDropItems);
864 VField* F = Class->FindFieldChecked("DropItemList");
865 return *(TArray<VDropItemInfo>*)F->GetFieldPtr((VObject*)Class->Defaults);
866 unguard;
867 }
868
869 //==========================================================================
870 //
871 // GetClassDamageFactors
872 //
873 //==========================================================================
874
GetClassDamageFactors(VClass * Class)875 static TArray<VDamageFactor>& GetClassDamageFactors(VClass* Class)
876 {
877 guard(GetClassDamageFactors);
878 VField* F = Class->FindFieldChecked("DamageFactors");
879 return *(TArray<VDamageFactor>*)F->GetFieldPtr((VObject*)Class->Defaults);
880 unguard;
881 }
882
883 //==========================================================================
884 //
885 // GetClassPainChances
886 //
887 //==========================================================================
888
GetClassPainChances(VClass * Class)889 static TArray<VPainChanceInfo>& GetClassPainChances(VClass* Class)
890 {
891 guard(GetClassPainChances);
892 VField* F = Class->FindFieldChecked("PainChances");
893 return *(TArray<VPainChanceInfo>*)F->GetFieldPtr((VObject*)Class->Defaults);
894 unguard;
895 }
896
897 //==========================================================================
898 //
899 // SetClassFieldInt
900 //
901 //==========================================================================
902
SetClassFieldInt(VClass * Class,VName FieldName,int Value,int Idx=0)903 static void SetClassFieldInt(VClass* Class, VName FieldName, int Value,
904 int Idx = 0)
905 {
906 guard(SetClassFieldInt);
907 VField* F = Class->FindFieldChecked(FieldName);
908 F->SetInt((VObject*)Class->Defaults, Value, Idx);
909 unguard;
910 }
911
912 //==========================================================================
913 //
914 // SetClassFieldByte
915 //
916 //==========================================================================
917
SetClassFieldByte(VClass * Class,VName FieldName,vuint8 Value)918 static void SetClassFieldByte(VClass* Class, VName FieldName, vuint8 Value)
919 {
920 guard(SetClassFieldByte);
921 VField* F = Class->FindFieldChecked(FieldName);
922 F->SetByte((VObject*)Class->Defaults, Value);
923 unguard;
924 }
925
926 //==========================================================================
927 //
928 // SetClassFieldFloat
929 //
930 //==========================================================================
931
SetClassFieldFloat(VClass * Class,VName FieldName,float Value,int Idx=0)932 static void SetClassFieldFloat(VClass* Class, VName FieldName, float Value,
933 int Idx = 0)
934 {
935 guard(SetClassFieldFloat);
936 VField* F = Class->FindFieldChecked(FieldName);
937 F->SetFloat((VObject*)Class->Defaults, Value, Idx);
938 unguard;
939 }
940
941 //==========================================================================
942 //
943 // SetClassFieldBool
944 //
945 //==========================================================================
946
SetClassFieldBool(VClass * Class,VName FieldName,int Value)947 static void SetClassFieldBool(VClass* Class, VName FieldName, int Value)
948 {
949 guard(SetClassFieldBool);
950 VField* F = Class->FindFieldChecked(FieldName);
951 F->SetBool((VObject*)Class->Defaults, Value);
952 unguard;
953 }
954
955 //==========================================================================
956 //
957 // SetClassFieldName
958 //
959 //==========================================================================
960
SetClassFieldName(VClass * Class,VName FieldName,VName Value)961 static void SetClassFieldName(VClass* Class, VName FieldName, VName Value)
962 {
963 guard(SetClassFieldName);
964 VField* F = Class->FindFieldChecked(FieldName);
965 F->SetName((VObject*)Class->Defaults, Value);
966 unguard;
967 }
968
969 //==========================================================================
970 //
971 // SetClassFieldStr
972 //
973 //==========================================================================
974
SetClassFieldStr(VClass * Class,VName FieldName,const VStr & Value)975 static void SetClassFieldStr(VClass* Class, VName FieldName,
976 const VStr& Value)
977 {
978 guard(SetClassFieldStr);
979 VField* F = Class->FindFieldChecked(FieldName);
980 F->SetStr((VObject*)Class->Defaults, Value);
981 unguard;
982 }
983
984 //==========================================================================
985 //
986 // SetClassFieldVec
987 //
988 //==========================================================================
989
SetClassFieldVec(VClass * Class,VName FieldName,const TVec & Value)990 static void SetClassFieldVec(VClass* Class, VName FieldName,
991 const TVec& Value)
992 {
993 guard(SetClassFieldVec);
994 VField* F = Class->FindFieldChecked(FieldName);
995 F->SetVec((VObject*)Class->Defaults, Value);
996 unguard;
997 }
998
999 //==========================================================================
1000 //
1001 // SetFieldByte
1002 //
1003 //==========================================================================
1004
SetFieldByte(VObject * Obj,VName FieldName,vuint8 Value)1005 static void SetFieldByte(VObject* Obj, VName FieldName, vuint8 Value)
1006 {
1007 guard(SetFieldByte);
1008 VField* F = Obj->GetClass()->FindFieldChecked(FieldName);
1009 F->SetByte(Obj, Value);
1010 unguard;
1011 }
1012
1013 //==========================================================================
1014 //
1015 // SetFieldFloat
1016 //
1017 //==========================================================================
1018
SetFieldFloat(VObject * Obj,VName FieldName,float Value,int Idx=0)1019 static void SetFieldFloat(VObject* Obj, VName FieldName, float Value,
1020 int Idx = 0)
1021 {
1022 guard(SetFieldFloat);
1023 VField* F = Obj->GetClass()->FindFieldChecked(FieldName);
1024 F->SetFloat(Obj, Value, Idx);
1025 unguard;
1026 }
1027
1028 //==========================================================================
1029 //
1030 // SetFieldBool
1031 //
1032 //==========================================================================
1033
SetFieldBool(VObject * Obj,VName FieldName,int Value)1034 static void SetFieldBool(VObject* Obj, VName FieldName, int Value)
1035 {
1036 guard(SetFieldBool);
1037 VField* F = Obj->GetClass()->FindFieldChecked(FieldName);
1038 F->SetBool(Obj, Value);
1039 unguard;
1040 }
1041
1042 //==========================================================================
1043 //
1044 // SetFieldName
1045 //
1046 //==========================================================================
1047
SetFieldName(VObject * Obj,VName FieldName,VName Value)1048 static void SetFieldName(VObject* Obj, VName FieldName, VName Value)
1049 {
1050 guard(SetFieldName);
1051 VField* F = Obj->GetClass()->FindFieldChecked(FieldName);
1052 F->SetName(Obj, Value);
1053 unguard;
1054 }
1055
1056 //==========================================================================
1057 //
1058 // SetFieldClass
1059 //
1060 //==========================================================================
1061
SetFieldClass(VObject * Obj,VName FieldName,VClass * Value)1062 static void SetFieldClass(VObject* Obj, VName FieldName, VClass* Value)
1063 {
1064 guard(SetFieldClass);
1065 VField* F = Obj->GetClass()->FindFieldChecked(FieldName);
1066 F->SetClass(Obj, Value);
1067 unguard;
1068 }
1069
1070 //==========================================================================
1071 //
1072 // AddClassFixup
1073 //
1074 //==========================================================================
1075
AddClassFixup(VClass * Class,VName FieldName,const VStr & ClassName,TArray<VClassFixup> & ClassFixups)1076 static void AddClassFixup(VClass* Class, VName FieldName,
1077 const VStr& ClassName, TArray<VClassFixup>& ClassFixups)
1078 {
1079 guard(AddClassFixup);
1080 VField* F = Class->FindFieldChecked(FieldName);
1081 VClassFixup& CF = ClassFixups.Alloc();
1082 CF.Offset = F->Ofs;
1083 CF.Name = ClassName;
1084 CF.ReqParent = F->Type.Class;
1085 CF.Class = Class;
1086 unguard;
1087 }
1088
1089 //==========================================================================
1090 //
1091 // AddClassFixup
1092 //
1093 //==========================================================================
1094
AddClassFixup(VClass * Class,VField * Field,const VStr & ClassName,TArray<VClassFixup> & ClassFixups)1095 static void AddClassFixup(VClass* Class, VField* Field,
1096 const VStr& ClassName, TArray<VClassFixup>& ClassFixups)
1097 {
1098 guard(AddClassFixup);
1099 VClassFixup& CF = ClassFixups.Alloc();
1100 CF.Offset = Field->Ofs;
1101 CF.Name = ClassName;
1102 CF.ReqParent = Field->Type.Class;
1103 CF.Class = Class;
1104 unguard;
1105 }
1106
1107 //==========================================================================
1108 //
1109 // SkipBlock
1110 //
1111 //==========================================================================
1112
SkipBlock(VScriptParser * sc,int Level)1113 static void SkipBlock(VScriptParser* sc, int Level)
1114 {
1115 while (!sc->AtEnd() && Level > 0)
1116 {
1117 if (sc->Check("{"))
1118 {
1119 Level++;
1120 }
1121 else if (sc->Check("}"))
1122 {
1123 Level--;
1124 }
1125 else
1126 {
1127 sc->GetString();
1128 }
1129 }
1130 }
1131
1132 //==========================================================================
1133 //
1134 // ParseMethodCall
1135 //
1136 //==========================================================================
1137
ParseMethodCall(VScriptParser * sc,VName Name,TLocation Loc)1138 static VExpression* ParseMethodCall(VScriptParser* sc, VName Name,
1139 TLocation Loc)
1140 {
1141 guard(ParseMethodCall);
1142 VExpression* Args[VMethod::MAX_PARAMS + 1];
1143 int NumArgs = 0;
1144 if (!sc->Check(")"))
1145 {
1146 do
1147 {
1148 Args[NumArgs] = ParseExpressionPriority13(sc);
1149 if (NumArgs == VMethod::MAX_PARAMS)
1150 ParseError(sc->GetLoc(), "Too many arguments");
1151 else
1152 NumArgs++;
1153 } while (sc->Check(","));
1154 sc->Expect(")");
1155 }
1156 return new VDecorateInvocation(Name, Loc, NumArgs, Args);
1157 unguard;
1158 }
1159
1160 //==========================================================================
1161 //
1162 // ParseExpressionPriority0
1163 //
1164 //==========================================================================
1165
ParseExpressionPriority0(VScriptParser * sc)1166 static VExpression* ParseExpressionPriority0(VScriptParser* sc)
1167 {
1168 guard(ParseExpressionPriority0);
1169 TLocation l = sc->GetLoc();
1170 // Check for quoted strings first,
1171 // since these could also have numbers...
1172 if (sc->CheckQuotedString())
1173 {
1174 int Val = DecPkg->FindString(*sc->String);
1175 return new VStringLiteral(Val, l);
1176 }
1177
1178 if (sc->CheckNumber())
1179 {
1180 vint32 Val = sc->Number;
1181 return new VIntLiteral(Val, l);
1182 }
1183
1184 if (sc->CheckFloat())
1185 {
1186 float Val = sc->Float;
1187 return new VFloatLiteral(Val, l);
1188 }
1189
1190 if (sc->Check("false"))
1191 {
1192 return new VIntLiteral(0, l);
1193 }
1194
1195 if (sc->Check("true"))
1196 {
1197 return new VIntLiteral(1, l);
1198 }
1199
1200 if (sc->Check("("))
1201 {
1202 VExpression* op = ParseExpressionPriority13(sc);
1203 if (!op)
1204 {
1205 ParseError(l, "Expression expected");
1206 }
1207 sc->Expect(")");
1208 return op;
1209 }
1210
1211 if (sc->CheckIdentifier())
1212 {
1213 VStr Name = sc->String;
1214 // Skip random generator ID.
1215 if ((!Name.ICmp("random") || !Name.ICmp("random2")) && sc->Check("["))
1216 {
1217 sc->ExpectString();
1218 sc->Expect("]");
1219 }
1220 if (sc->Check("("))
1221 {
1222 return ParseMethodCall(sc, *Name.ToLower(), l);
1223 }
1224 return new VDecorateSingleName(Name, l);
1225 }
1226
1227 return NULL;
1228 unguard;
1229 }
1230
1231 //==========================================================================
1232 //
1233 // ParseExpressionPriority1
1234 //
1235 //==========================================================================
1236
ParseExpressionPriority1(VScriptParser * sc)1237 static VExpression* ParseExpressionPriority1(VScriptParser* sc)
1238 {
1239 guard(ParseExpressionPriority1);
1240 VExpression* op = ParseExpressionPriority0(sc);
1241
1242 TLocation l = sc->GetLoc();
1243
1244 if (!op)
1245 return NULL;
1246 bool done = false;
1247 do
1248 {
1249 if (sc->Check("["))
1250 {
1251 VExpression* ind = ParseExpressionPriority13(sc);
1252 sc->Expect("]");
1253 op = new VArrayElement(op, ind, l);
1254 }
1255 else
1256 {
1257 done = true;
1258 }
1259 } while (!done);
1260
1261 return op;
1262 unguard;
1263 }
1264
1265 //==========================================================================
1266 //
1267 // ParseExpressionPriority2
1268 //
1269 //==========================================================================
1270
ParseExpressionPriority2(VScriptParser * sc)1271 static VExpression* ParseExpressionPriority2(VScriptParser* sc)
1272 {
1273 guard(ParseExpressionPriority2);
1274 VExpression* op;
1275
1276 TLocation l = sc->GetLoc();
1277
1278 if (sc->Check("+"))
1279 {
1280 op = ParseExpressionPriority2(sc);
1281 return new VUnary(VUnary::Plus, op, l);
1282 }
1283
1284 if (sc->Check("-"))
1285 {
1286 op = ParseExpressionPriority2(sc);
1287 return new VUnary(VUnary::Minus, op, l);
1288 }
1289
1290 if (sc->Check("!"))
1291 {
1292 op = ParseExpressionPriority2(sc);
1293 return new VUnary(VUnary::Not, op, l);
1294 }
1295
1296 if (sc->Check("~"))
1297 {
1298 op = ParseExpressionPriority2(sc);
1299 return new VUnary(VUnary::BitInvert, op, l);
1300 }
1301
1302 return ParseExpressionPriority1(sc);
1303 unguard;
1304 }
1305
1306 //==========================================================================
1307 //
1308 // ParseExpressionPriority3
1309 //
1310 //==========================================================================
1311
ParseExpressionPriority3(VScriptParser * sc)1312 static VExpression* ParseExpressionPriority3(VScriptParser* sc)
1313 {
1314 guard(ParseExpressionPriority3);
1315 VExpression* op1 = ParseExpressionPriority2(sc);
1316 if (!op1)
1317 {
1318 return NULL;
1319 }
1320 bool done = false;
1321 do
1322 {
1323 TLocation l = sc->GetLoc();
1324 if (sc->Check("*"))
1325 {
1326 VExpression* op2 = ParseExpressionPriority2(sc);
1327 op1 = new VBinary(VBinary::Multiply, op1, op2, l);
1328 }
1329 else if (sc->Check("/"))
1330 {
1331 VExpression* op2 = ParseExpressionPriority2(sc);
1332 op1 = new VBinary(VBinary::Divide, op1, op2, l);
1333 }
1334 else if (sc->Check("%"))
1335 {
1336 VExpression* op2 = ParseExpressionPriority2(sc);
1337 op1 = new VBinary(VBinary::Modulus, op1, op2, l);
1338 }
1339 else
1340 {
1341 done = true;
1342 }
1343 }
1344 while (!done);
1345 return op1;
1346 unguard;
1347 }
1348
1349 //==========================================================================
1350 //
1351 // ParseExpressionPriority4
1352 //
1353 //==========================================================================
1354
ParseExpressionPriority4(VScriptParser * sc)1355 static VExpression* ParseExpressionPriority4(VScriptParser* sc)
1356 {
1357 guard(ParseExpressionPriority4);
1358 VExpression* op1 = ParseExpressionPriority3(sc);
1359 if (!op1)
1360 {
1361 return NULL;
1362 }
1363 bool done = false;
1364 do
1365 {
1366 TLocation l = sc->GetLoc();
1367 if (sc->Check("+"))
1368 {
1369 VExpression* op2 = ParseExpressionPriority3(sc);
1370 op1 = new VBinary(VBinary::Add, op1, op2, l);
1371 }
1372 else if (sc->Check("-"))
1373 {
1374 VExpression* op2 = ParseExpressionPriority3(sc);
1375 op1 = new VBinary(VBinary::Subtract, op1, op2, l);
1376 }
1377 else
1378 {
1379 done = true;
1380 }
1381 }
1382 while (!done);
1383 return op1;
1384 unguard;
1385 }
1386
1387 //==========================================================================
1388 //
1389 // ParseExpressionPriority5
1390 //
1391 //==========================================================================
1392
ParseExpressionPriority5(VScriptParser * sc)1393 static VExpression* ParseExpressionPriority5(VScriptParser* sc)
1394 {
1395 guard(ParseExpressionPriority5);
1396 VExpression* op1 = ParseExpressionPriority4(sc);
1397 if (!op1)
1398 {
1399 return NULL;
1400 }
1401 bool done = false;
1402 do
1403 {
1404 TLocation l = sc->GetLoc();
1405 if (sc->Check("<<"))
1406 {
1407 VExpression* op2 = ParseExpressionPriority4(sc);
1408 op1 = new VBinary(VBinary::LShift, op1, op2, l);
1409 }
1410 else if (sc->Check(">>"))
1411 {
1412 VExpression* op2 = ParseExpressionPriority4(sc);
1413 op1 = new VBinary(VBinary::RShift, op1, op2, l);
1414 }
1415 else
1416 {
1417 done = true;
1418 }
1419 }
1420 while (!done);
1421 return op1;
1422 unguard;
1423 }
1424
1425 //==========================================================================
1426 //
1427 // ParseExpressionPriority6
1428 //
1429 //==========================================================================
1430
ParseExpressionPriority6(VScriptParser * sc)1431 static VExpression* ParseExpressionPriority6(VScriptParser* sc)
1432 {
1433 guard(ParseExpressionPriority6);
1434 VExpression* op1 = ParseExpressionPriority5(sc);
1435 if (!op1)
1436 {
1437 return NULL;
1438 }
1439 bool done = false;
1440 do
1441 {
1442 TLocation l = sc->GetLoc();
1443 if (sc->Check("<"))
1444 {
1445 VExpression* op2 = ParseExpressionPriority5(sc);
1446 op1 = new VBinary(VBinary::Less, op1, op2, l);
1447 }
1448 else if (sc->Check("<="))
1449 {
1450 VExpression* op2 = ParseExpressionPriority5(sc);
1451 op1 = new VBinary(VBinary::LessEquals, op1, op2, l);
1452 }
1453 else if (sc->Check(">"))
1454 {
1455 VExpression* op2 = ParseExpressionPriority5(sc);
1456 op1 = new VBinary(VBinary::Greater, op1, op2, l);
1457 }
1458 else if (sc->Check(">="))
1459 {
1460 VExpression* op2 = ParseExpressionPriority5(sc);
1461 op1 = new VBinary(VBinary::GreaterEquals, op1, op2, l);
1462 }
1463 else
1464 {
1465 done = true;
1466 }
1467 }
1468 while (!done);
1469 return op1;
1470 unguard;
1471 }
1472
1473 //==========================================================================
1474 //
1475 // ParseExpressionPriority7
1476 //
1477 //==========================================================================
1478
ParseExpressionPriority7(VScriptParser * sc)1479 static VExpression* ParseExpressionPriority7(VScriptParser* sc)
1480 {
1481 guard(ParseExpressionPriority7);
1482 VExpression* op1 = ParseExpressionPriority6(sc);
1483 if (!op1)
1484 {
1485 return NULL;
1486 }
1487 bool done = false;
1488 do
1489 {
1490 TLocation l = sc->GetLoc();
1491 if (sc->Check("=="))
1492 {
1493 VExpression* op2 = ParseExpressionPriority6(sc);
1494 op1 = new VBinary(VBinary::Equals, op1, op2, l);
1495 }
1496 else if (sc->Check("!="))
1497 {
1498 VExpression* op2 = ParseExpressionPriority6(sc);
1499 op1 = new VBinary(VBinary::NotEquals, op1, op2, l);
1500 }
1501 else
1502 {
1503 done = true;
1504 }
1505 } while (!done);
1506 return op1;
1507 unguard;
1508 }
1509
1510 //==========================================================================
1511 //
1512 // ParseExpressionPriority8
1513 //
1514 //==========================================================================
1515
ParseExpressionPriority8(VScriptParser * sc)1516 static VExpression* ParseExpressionPriority8(VScriptParser* sc)
1517 {
1518 guard(ParseExpressionPriority8);
1519 VExpression* op1 = ParseExpressionPriority7(sc);
1520 if (!op1)
1521 {
1522 return NULL;
1523 }
1524 TLocation l = sc->GetLoc();
1525 while (sc->Check("&"))
1526 {
1527 VExpression* op2 = ParseExpressionPriority7(sc);
1528 op1 = new VBinary(VBinary::And, op1, op2, l);
1529 l = sc->GetLoc();
1530 }
1531 return op1;
1532 unguard;
1533 }
1534
1535 //==========================================================================
1536 //
1537 // ParseExpressionPriority9
1538 //
1539 //==========================================================================
1540
ParseExpressionPriority9(VScriptParser * sc)1541 static VExpression* ParseExpressionPriority9(VScriptParser* sc)
1542 {
1543 guard(ParseExpressionPriority9);
1544 VExpression* op1 = ParseExpressionPriority8(sc);
1545 if (!op1)
1546 {
1547 return NULL;
1548 }
1549 TLocation l = sc->GetLoc();
1550 while (sc->Check("^"))
1551 {
1552 VExpression* op2 = ParseExpressionPriority8(sc);
1553 op1 = new VBinary(VBinary::XOr, op1, op2, l);
1554 l = sc->GetLoc();
1555 }
1556 return op1;
1557 unguard;
1558 }
1559
1560 //==========================================================================
1561 //
1562 // ParseExpressionPriority10
1563 //
1564 //==========================================================================
1565
ParseExpressionPriority10(VScriptParser * sc)1566 static VExpression* ParseExpressionPriority10(VScriptParser* sc)
1567 {
1568 guard(ParseExpressionPriority10);
1569 VExpression* op1 = ParseExpressionPriority9(sc);
1570 if (!op1)
1571 {
1572 return NULL;
1573 }
1574 TLocation l = sc->GetLoc();
1575 while (sc->Check("|"))
1576 {
1577 VExpression* op2 = ParseExpressionPriority9(sc);
1578 op1 = new VBinary(VBinary::Or, op1, op2, l);
1579 l = sc->GetLoc();
1580 }
1581 return op1;
1582 unguard;
1583 }
1584
1585 //==========================================================================
1586 //
1587 // ParseExpressionPriority11
1588 //
1589 //==========================================================================
1590
ParseExpressionPriority11(VScriptParser * sc)1591 static VExpression* ParseExpressionPriority11(VScriptParser* sc)
1592 {
1593 guard(ParseExpressionPriority11);
1594 VExpression* op1 = ParseExpressionPriority10(sc);
1595 if (!op1)
1596 {
1597 return NULL;
1598 }
1599 TLocation l = sc->GetLoc();
1600 while (sc->Check("&&"))
1601 {
1602 VExpression* op2 = ParseExpressionPriority10(sc);
1603 op1 = new VBinaryLogical(VBinaryLogical::And, op1, op2, l);
1604 l = sc->GetLoc();
1605 }
1606 return op1;
1607 unguard;
1608 }
1609
1610 //==========================================================================
1611 //
1612 // ParseExpressionPriority12
1613 //
1614 //==========================================================================
1615
ParseExpressionPriority12(VScriptParser * sc)1616 static VExpression* ParseExpressionPriority12(VScriptParser* sc)
1617 {
1618 guard(ParseExpressionPriority12);
1619 VExpression* op1 = ParseExpressionPriority11(sc);
1620 if (!op1)
1621 {
1622 return NULL;
1623 }
1624 TLocation l = sc->GetLoc();
1625 while (sc->Check("||"))
1626 {
1627 VExpression* op2 = ParseExpressionPriority11(sc);
1628 op1 = new VBinaryLogical(VBinaryLogical::Or, op1, op2, l);
1629 l = sc->GetLoc();
1630 }
1631 return op1;
1632 unguard;
1633 }
1634
1635 //==========================================================================
1636 //
1637 // VParser::ParseExpressionPriority13
1638 //
1639 //==========================================================================
1640
ParseExpressionPriority13(VScriptParser * sc)1641 static VExpression* ParseExpressionPriority13(VScriptParser* sc)
1642 {
1643 guard(ParseExpressionPriority13);
1644 VExpression* op = ParseExpressionPriority12(sc);
1645 if (!op)
1646 {
1647 return NULL;
1648 }
1649 TLocation l = sc->GetLoc();
1650 if (sc->Check("?"))
1651 {
1652 VExpression* op1 = ParseExpressionPriority13(sc);
1653 sc->Expect(":");
1654 VExpression* op2 = ParseExpressionPriority13(sc);
1655 op = new VConditional(op, op1, op2, l);
1656 }
1657 return op;
1658 unguard;
1659 }
1660
1661 //==========================================================================
1662 //
1663 // ParseExpression
1664 //
1665 //==========================================================================
1666
ParseExpression(VScriptParser * sc)1667 static VExpression* ParseExpression(VScriptParser* sc)
1668 {
1669 guard(ParseExpression);
1670 return ParseExpressionPriority13(sc);
1671 unguard;
1672 }
1673
1674 //==========================================================================
1675 //
1676 // ParseConst
1677 //
1678 //==========================================================================
1679
ParseConst(VScriptParser * sc)1680 static void ParseConst(VScriptParser* sc)
1681 {
1682 guard(ParseConst);
1683 sc->SetCMode(true);
1684 sc->Expect("int");
1685 sc->ExpectString();
1686 TLocation Loc = sc->GetLoc();
1687 VStr Name = sc->String.ToLower();
1688 sc->Expect("=");
1689
1690 VExpression* Expr = ParseExpression(sc);
1691 if (!Expr)
1692 {
1693 sc->Error("Constant value expected");
1694 }
1695 else
1696 {
1697 VEmitContext ec(DecPkg);
1698 Expr = Expr->Resolve(ec);
1699 if (Expr)
1700 {
1701 int Val = Expr->GetIntConst();
1702 delete Expr;
1703 Expr = NULL;
1704 VConstant* C = new VConstant(*Name, DecPkg, Loc);
1705 C->Type = TYPE_Int;
1706 C->Value = Val;
1707 }
1708 }
1709 sc->Expect(";");
1710 sc->SetCMode(false);
1711 unguard;
1712 }
1713
1714 //==========================================================================
1715 //
1716 // ParseAction
1717 //
1718 //==========================================================================
1719
ParseAction(VScriptParser * sc,VClass * Class)1720 static void ParseAction(VScriptParser* sc, VClass* Class)
1721 {
1722 guard(ParseAction);
1723 sc->Expect("native");
1724 // Find the method. First try with decorate_ prefix, then without.
1725 sc->ExpectIdentifier();
1726 VMethod* M = Class->FindMethod(va("decorate_%s", *sc->String));
1727 if (!M)
1728 {
1729 M = Class->FindMethod(*sc->String);
1730 }
1731 if (!M)
1732 {
1733 sc->Error(va("Method %s not found in class %s", *sc->String,
1734 Class->GetName()));
1735 }
1736 if (M->ReturnType.Type != TYPE_Void)
1737 {
1738 sc->Error(va("State action %s doesn't return void", *sc->String));
1739 }
1740 VDecorateStateAction& A = Class->DecorateStateActions.Alloc();
1741 A.Name = *sc->String.ToLower();
1742 A.Method = M;
1743 // Skip arguments, right now I don't care bout them.
1744 sc->Expect("(");
1745 while (!sc->Check(")"))
1746 {
1747 sc->ExpectString();
1748 }
1749 sc->Expect(";");
1750 unguard;
1751 }
1752
1753 //==========================================================================
1754 //
1755 // ParseClass
1756 //
1757 //==========================================================================
1758
ParseClass(VScriptParser * sc)1759 static void ParseClass(VScriptParser* sc)
1760 {
1761 guard(ParseClass);
1762 sc->SetCMode(true);
1763 // Get class name and find the class.
1764 sc->ExpectString();
1765 VClass* Class = VClass::FindClass(*sc->String);
1766 if (!Class)
1767 {
1768 sc->Error("Class not found");
1769 }
1770 // I don't care about parent class name because in Vavoom it can be
1771 // different
1772 sc->Expect("extends");
1773 sc->ExpectString();
1774 sc->Expect("native");
1775 sc->Expect("{");
1776 while (!sc->Check("}"))
1777 {
1778 if (sc->Check("action"))
1779 {
1780 ParseAction(sc, Class);
1781 }
1782 else
1783 {
1784 sc->Error("Unknown class property");
1785 }
1786 }
1787 sc->SetCMode(false);
1788 unguard;
1789 }
1790
1791 //==========================================================================
1792 //
1793 // ParseEnum
1794 //
1795 //==========================================================================
1796
ParseEnum(VScriptParser * sc)1797 static void ParseEnum(VScriptParser* sc)
1798 {
1799 guard(ParseEnum);
1800 GCon->Logf("Enum");
1801 sc->Expect("{");
1802 SkipBlock(sc, 1);
1803 unguard;
1804 }
1805
1806 //==========================================================================
1807 //
1808 // ParseFlag
1809 //
1810 //==========================================================================
1811
ParseFlag(VScriptParser * sc,VClass * Class,bool Value,TArray<VClassFixup> & ClassFixups)1812 static bool ParseFlag(VScriptParser* sc, VClass* Class, bool Value,
1813 TArray<VClassFixup>& ClassFixups)
1814 {
1815 guard(ParseFlag);
1816 // Get full name of the flag.
1817 sc->ExpectIdentifier();
1818 VName FlagName(*sc->String.ToLower());
1819 VName ClassFilter(NAME_None);
1820 if (sc->Check("."))
1821 {
1822 sc->ExpectIdentifier();
1823 ClassFilter = FlagName;
1824 FlagName = *sc->String.ToLower();
1825 }
1826 VObject* DefObj = (VObject*)Class->Defaults;
1827
1828 for (int j = 0; j < FlagList.Num(); j++)
1829 {
1830 VFlagList& ClassDef = FlagList[j];
1831 if (ClassFilter != NAME_None &&
1832 ClassDef.Class->LowerCaseName != ClassFilter)
1833 {
1834 continue;
1835 }
1836 if (!Class->IsChildOf(ClassDef.Class))
1837 {
1838 continue;
1839 }
1840 for (int i = ClassDef.FlagsHash[GetTypeHash(FlagName) &
1841 (FLAGS_HASH_SIZE - 1)]; i != -1; i = ClassDef.Flags[i].HashNext)
1842 {
1843 const VFlagDef& F = ClassDef.Flags[i];
1844 if (FlagName == F.Name)
1845 {
1846 switch (F.Type)
1847 {
1848 case FLAG_Bool:
1849 F.Field->SetBool(DefObj, Value);
1850 break;
1851 case FLAG_Unsupported:
1852 GCon->Logf("Unsupported flag %s in %s", *FlagName,
1853 Class->GetName());
1854 break;
1855 case FLAG_Byte:
1856 F.Field->SetByte(DefObj, Value ? F.BTrue : F.BFalse);
1857 break;
1858 case FLAG_Float:
1859 F.Field->SetFloat(DefObj, Value ? F.FTrue : F.FFalse);
1860 break;
1861 case FLAG_Name:
1862 F.Field->SetName(DefObj, Value ? F.NTrue : F.NFalse);
1863 break;
1864 case FLAG_Class:
1865 AddClassFixup(Class, F.Field, Value ?
1866 *F.NTrue : *F.NFalse, ClassFixups);
1867 break;
1868 case FLAG_NoClip:
1869 F.Field->SetBool(DefObj, !Value);
1870 F.Field2->SetBool(DefObj, !Value);
1871 break;
1872 }
1873 return true;
1874 }
1875 }
1876 }
1877 sc->Error(va("Unknown flag %s", *FlagName));
1878 return false;
1879 unguard;
1880 }
1881
1882 //==========================================================================
1883 //
1884 // ParseStateString
1885 //
1886 //==========================================================================
1887
ParseStateString(VScriptParser * sc)1888 static VStr ParseStateString(VScriptParser* sc)
1889 {
1890 guard(ParseStateString);
1891 VStr StateStr;
1892
1893 if (!sc->CheckQuotedString())
1894 {
1895 sc->ExpectIdentifier();
1896 }
1897 StateStr = sc->String;
1898
1899 if (sc->Check("::"))
1900 {
1901 sc->ExpectIdentifier();
1902 StateStr += "::";
1903 StateStr += sc->String;
1904 }
1905
1906 if (sc->Check("."))
1907 {
1908 sc->ExpectIdentifier();
1909 StateStr += ".";
1910 StateStr += sc->String;
1911 }
1912
1913 return StateStr;
1914 unguard;
1915 }
1916
1917 //==========================================================================
1918 //
1919 // ParseStates
1920 //
1921 //==========================================================================
1922
ParseStates(VScriptParser * sc,VClass * Class,TArray<VState * > & States)1923 static bool ParseStates(VScriptParser* sc, VClass* Class,
1924 TArray<VState*>& States)
1925 {
1926 guard(ParseStates);
1927 VState* PrevState = NULL;
1928 VState* LastState = NULL;
1929 VState* LoopStart = NULL;
1930 int NewLabelsStart = Class->StateLabelDefs.Num();
1931
1932 sc->Expect("{");
1933 // Disable escape sequences in states.
1934 sc->SetEscape(false);
1935 while (!sc->Check("}"))
1936 {
1937 TLocation TmpLoc = sc->GetLoc();
1938 VStr TmpName = ParseStateString(sc);
1939
1940 // Goto command.
1941 if (!TmpName.ICmp("Goto"))
1942 {
1943 VName GotoLabel = *ParseStateString(sc);
1944 int GotoOffset = 0;
1945 if (sc->Check("+"))
1946 {
1947 sc->ExpectNumber();
1948 GotoOffset = sc->Number;
1949 }
1950
1951 if (!LastState && NewLabelsStart == Class->StateLabelDefs.Num())
1952 {
1953 sc->Error("Goto before first state");
1954 }
1955 if (LastState)
1956 {
1957 LastState->GotoLabel = GotoLabel;
1958 LastState->GotoOffset = GotoOffset;
1959 }
1960 for (int i = NewLabelsStart; i < Class->StateLabelDefs.Num(); i++)
1961 {
1962 Class->StateLabelDefs[i].GotoLabel = GotoLabel;
1963 Class->StateLabelDefs[i].GotoOffset = GotoOffset;
1964 }
1965 NewLabelsStart = Class->StateLabelDefs.Num();
1966 PrevState = NULL;
1967 continue;
1968 }
1969
1970 // Stop command.
1971 if (!TmpName.ICmp("Stop"))
1972 {
1973 if (!LastState && NewLabelsStart == Class->StateLabelDefs.Num())
1974 {
1975 sc->Error("Stop before first state");
1976 continue;
1977 }
1978 if (LastState)
1979 {
1980 LastState->NextState = NULL;
1981 }
1982 for (int i = NewLabelsStart; i < Class->StateLabelDefs.Num(); i++)
1983 {
1984 Class->StateLabelDefs[i].State = NULL;
1985 }
1986 NewLabelsStart = Class->StateLabelDefs.Num();
1987 PrevState = NULL;
1988 continue;
1989 }
1990
1991 // Wait command.
1992 if (!TmpName.ICmp("Wait") || !TmpName.ICmp("Fail"))
1993 {
1994 if (!LastState)
1995 {
1996 sc->Error(va("%s before first state", *TmpName));
1997 continue;
1998 }
1999 LastState->NextState = LastState;
2000 PrevState = NULL;
2001 continue;
2002 }
2003
2004 // Loop command.
2005 if (!TmpName.ICmp("Loop"))
2006 {
2007 if (!LastState)
2008 {
2009 sc->Error("Loop before first state");
2010 continue;
2011 }
2012 LastState->NextState = LoopStart;
2013 PrevState = NULL;
2014 continue;
2015 }
2016
2017 // Check for label.
2018 if (sc->Check(":"))
2019 {
2020 LastState = NULL;
2021 VStateLabelDef& Lbl = Class->StateLabelDefs.Alloc();
2022 Lbl.Loc = TmpLoc;
2023 Lbl.Name = TmpName;
2024 continue;
2025 }
2026
2027 VState* State = new VState(va("S_%d", States.Num()), Class, TmpLoc);
2028 States.Append(State);
2029
2030 // Sprite name
2031 if (TmpName.Length() != 4)
2032 {
2033 sc->Error("Invalid sprite name");
2034 }
2035 State->SpriteName = *TmpName.ToLower();
2036
2037 // Frame
2038 sc->ExpectString();
2039 char FChar = VStr::ToUpper(sc->String[0]);
2040 if (FChar < 'A' || FChar > ']')
2041 {
2042 sc->Error("Frames must be A-Z, [, \\ or ]");
2043 }
2044 State->Frame = FChar - 'A';
2045 VStr FramesString = sc->String;
2046
2047 // Tics
2048 sc->ExpectNumberWithSign();
2049 if (sc->Number < 0)
2050 {
2051 State->Time = sc->Number;
2052 }
2053 else
2054 {
2055 State->Time = float(sc->Number) / 35.0;
2056 }
2057
2058 bool NeedsUnget = true;
2059 while (sc->GetString() && !sc->Crossed)
2060 {
2061 // Check for bright parameter.
2062 if (!sc->String.ICmp("Bright"))
2063 {
2064 State->Frame |= VState::FF_FULLBRIGHT;
2065 continue;
2066 }
2067
2068 // Check for offsets.
2069 if (!sc->String.ICmp("Offset"))
2070 {
2071 sc->Expect("(");
2072 sc->ExpectNumberWithSign();
2073 State->Misc1 = sc->Number;
2074 sc->Expect(",");
2075 sc->ExpectNumberWithSign();
2076 State->Misc2 = sc->Number;
2077 sc->Expect(")");
2078 continue;
2079 }
2080
2081 // Get function name and parse arguments.
2082 VStr FuncName = sc->String;
2083 VStr FuncNameLower = sc->String.ToLower();
2084 VExpression* Args[VMethod::MAX_PARAMS + 1];
2085 int NumArgs = 0;
2086 if (sc->Check("("))
2087 {
2088 if (!sc->Check(")"))
2089 {
2090 do
2091 {
2092 Args[NumArgs] = ParseExpressionPriority13(sc);
2093 if (NumArgs == VMethod::MAX_PARAMS)
2094 ParseError(sc->GetLoc(), "Too many arguments");
2095 else
2096 NumArgs++;
2097 } while (sc->Check(","));
2098 sc->Expect(")");
2099 }
2100 }
2101
2102 // Find the state action method. First check action specials, then
2103 // state actions.
2104 VMethod* Func = NULL;
2105 for (int i = 0; i < LineSpecialInfos.Num(); i++)
2106 {
2107 if (LineSpecialInfos[i].Name == FuncNameLower)
2108 {
2109 Func = Class->FindMethodChecked("A_ExecActionSpecial");
2110 if (NumArgs > 5)
2111 {
2112 sc->Error("Too many arguments");
2113 }
2114 else
2115 {
2116 // Add missing arguments.
2117 while (NumArgs < 5)
2118 {
2119 Args[NumArgs] = new VIntLiteral(0, sc->GetLoc());
2120 NumArgs++;
2121 }
2122 // Add action special number argument.
2123 Args[5] = new VIntLiteral(LineSpecialInfos[i].Number,
2124 sc->GetLoc());
2125 NumArgs++;
2126 }
2127 break;
2128 }
2129 }
2130 if (!Func)
2131 {
2132 VDecorateStateAction* Act = Class->FindDecorateStateAction(
2133 *FuncNameLower);
2134 Func = Act ? Act->Method : NULL;
2135 }
2136 if (!Func)
2137 {
2138 GCon->Logf("Unknown state action %s in %s", *FuncName, Class->GetName());
2139 }
2140 else if (Func->NumParams || NumArgs)
2141 {
2142 VInvocation* Expr = new VInvocation(NULL, Func, NULL,
2143 false, false, sc->GetLoc(), NumArgs, Args);
2144 Expr->CallerState = State;
2145 Expr->MultiFrameState = FramesString.Length() > 1;
2146 VExpressionStatement* Stmt = new VExpressionStatement(Expr);
2147 VMethod* M = new VMethod(NAME_None, Class, sc->GetLoc());
2148 M->Flags = FUNC_Final;
2149 M->ReturnType = TYPE_Void;
2150 M->Statement = Stmt;
2151 M->ParamsSize = 1;
2152 Class->AddMethod(M);
2153 State->Function = M;
2154 }
2155 else
2156 {
2157 State->Function = Func;
2158 }
2159
2160 // If state function is not assigned, it means something is wrong.
2161 // In that case we need to free argument expressions.
2162 if (!State->Function)
2163 {
2164 for (int i = 0; i < NumArgs; i++)
2165 {
2166 if (Args[i])
2167 {
2168 delete Args[i];
2169 Args[i] = NULL;
2170 }
2171 }
2172 }
2173 NeedsUnget = false;
2174 break;
2175 }
2176 if (NeedsUnget)
2177 {
2178 sc->UnGet();
2179 }
2180
2181 // Link previous state.
2182 if (PrevState)
2183 {
2184 PrevState->NextState = State;
2185 }
2186
2187 // Assign state to the labels.
2188 for (int i = NewLabelsStart; i < Class->StateLabelDefs.Num(); i++)
2189 {
2190 Class->StateLabelDefs[i].State = State;
2191 LoopStart = State;
2192 }
2193 NewLabelsStart = Class->StateLabelDefs.Num();
2194 PrevState = State;
2195 LastState = State;
2196
2197 for (size_t i = 1; i < FramesString.Length(); i++)
2198 {
2199 char FChar = VStr::ToUpper(FramesString[i]);
2200 if (FChar < 'A' || FChar > ']')
2201 {
2202 sc->Error("Frames must be A-Z, [, \\ or ]");
2203 }
2204
2205 // Create a new state.
2206 VState* s2 = new VState(va("S_%d", States.Num()), Class,
2207 sc->GetLoc());
2208 States.Append(s2);
2209 s2->SpriteName = State->SpriteName;
2210 s2->Frame = (State->Frame & VState::FF_FULLBRIGHT) | (FChar - 'A');
2211 s2->Time = State->Time;
2212 s2->Misc1 = State->Misc1;
2213 s2->Misc2 = State->Misc2;
2214 s2->Function = State->Function;
2215
2216 // Link previous state.
2217 PrevState->NextState = s2;
2218 PrevState = s2;
2219 LastState = s2;
2220 }
2221 }
2222 // Re-enable escape sequences.
2223 sc->SetEscape(true);
2224 return true;
2225 unguard;
2226 }
2227
2228 //==========================================================================
2229 //
2230 // ParseParentState
2231 //
2232 // This is for compatibility with old WADs.
2233 //
2234 //==========================================================================
2235
ParseParentState(VScriptParser * sc,VClass * Class,const char * LblName)2236 static void ParseParentState(VScriptParser* sc, VClass* Class,
2237 const char* LblName)
2238 {
2239 guard(ParseParentState);
2240 TLocation TmpLoc = sc->GetLoc();
2241 VState* State;
2242 // If there's a string token on next line, it gets eaten. Is this a bug?
2243 if (sc->GetString() && !sc->Crossed)
2244 {
2245 sc->UnGet();
2246 if (sc->Check("0"))
2247 {
2248 State = NULL;
2249 }
2250 else if (sc->Check("parent"))
2251 {
2252 // Find state in parent class.
2253 sc->ExpectString();
2254 VStateLabel* SLbl = Class->ParentClass->FindStateLabel(*sc->String);
2255 State = SLbl ? SLbl->State : NULL;
2256
2257 // Check for offset.
2258 int Offs = 0;
2259 if (sc->Check("+"))
2260 {
2261 sc->ExpectNumber();
2262 Offs = sc->Number;
2263 }
2264
2265 if (!State && Offs)
2266 {
2267 sc->Error(va("Attempt to get invalid state from actor %s",
2268 Class->GetSuperClass()->GetName()));
2269 }
2270 else if (State)
2271 {
2272 State = State->GetPlus(Offs, true);
2273 }
2274 }
2275 else
2276 {
2277 sc->Error("Invalid state assignment");
2278 }
2279 }
2280 else
2281 {
2282 State = NULL;
2283 }
2284
2285 VStateLabelDef& Lbl = Class->StateLabelDefs.Alloc();
2286 Lbl.Loc = TmpLoc;
2287 Lbl.Name = LblName;
2288 Lbl.State = State;
2289 unguard;
2290 }
2291
2292 //==========================================================================
2293 //
2294 // ParseActor
2295 //
2296 //==========================================================================
2297
ParseActor(VScriptParser * sc,TArray<VClassFixup> & ClassFixups)2298 static void ParseActor(VScriptParser* sc, TArray<VClassFixup>& ClassFixups)
2299 {
2300 guard(ParseActor);
2301 // Parse actor name. In order to allow dots in actor names, this is done
2302 // in non-C mode, so we have to do a little bit more complex parsing.
2303 sc->ExpectString();
2304 VStr NameStr;
2305 VStr ParentStr;
2306 int ColonPos = sc->String.IndexOf(':');
2307 if (ColonPos >= 0)
2308 {
2309 // There's a colon inside, so split up the string.
2310 NameStr = VStr(sc->String, 0, ColonPos);
2311 ParentStr = VStr(sc->String, ColonPos + 1, sc->String.Length() -
2312 ColonPos - 1);
2313 }
2314 else
2315 {
2316 NameStr = sc->String;
2317 }
2318
2319 if (GArgs.CheckParm("-debug_decorate"))
2320 {
2321 sc->Message(va("Parsing class %s", *NameStr));
2322 }
2323
2324 VClass* DupCheck = VClass::FindClassLowerCase(*NameStr.ToLower());
2325 if (DupCheck && DupCheck->MemberType == MEMBER_Class)
2326 {
2327 sc->Message(va("Warning: Redeclared class %s", *NameStr));
2328 }
2329
2330 if (ColonPos < 0)
2331 {
2332 // There's no colon, check if next string starts with it.
2333 sc->ExpectString();
2334 if (sc->String[0] == ':')
2335 {
2336 ColonPos = 0;
2337 ParentStr = VStr(sc->String, 1, sc->String.Length() - 1);
2338 }
2339 else
2340 {
2341 sc->UnGet();
2342 }
2343 }
2344
2345 // If we got colon but no parent class name, then get it.
2346 if (ColonPos >= 0 && ParentStr.IsEmpty())
2347 {
2348 sc->ExpectString();
2349 ParentStr = sc->String;
2350 }
2351
2352 VClass* ParentClass = ActorClass;
2353 if (ParentStr.IsNotEmpty())
2354 {
2355 ParentClass = VClass::FindClassLowerCase(*ParentStr.ToLower());
2356 if (!ParentClass || ParentClass->MemberType != MEMBER_Class)
2357 {
2358 sc->Error(va("Parent class %s not found", *ParentStr));
2359 }
2360 if (!ParentClass->IsChildOf(ActorClass))
2361 {
2362 sc->Error(va("Parent class %s is not an actor class", *ParentStr));
2363 }
2364 }
2365
2366 VClass* Class = ParentClass->CreateDerivedClass(*NameStr, DecPkg,
2367 sc->GetLoc());
2368 DecPkg->ParsedClasses.Append(Class);
2369
2370 if (ParentClass)
2371 {
2372 // Copy class fixups of the parent class.
2373 for (int i = 0; i < ClassFixups.Num(); i++)
2374 {
2375 VClassFixup& CF = ClassFixups[i];
2376 if (CF.Class == ParentClass)
2377 {
2378 VClassFixup& NewCF = ClassFixups.Alloc();
2379 NewCF.Offset = CF.Offset;
2380 NewCF.Name = CF.Name;
2381 NewCF.ReqParent = CF.ReqParent;
2382 NewCF.Class = Class;
2383 }
2384 }
2385 }
2386
2387 VClass* ReplaceeClass = NULL;
2388 if (sc->Check("replaces"))
2389 {
2390 sc->ExpectString();
2391 ReplaceeClass = VClass::FindClassLowerCase(*sc->String.ToLower());
2392 if (!ReplaceeClass || ReplaceeClass->MemberType != MEMBER_Class)
2393 {
2394 sc->Error(va("Replaced class %s not found", *sc->String));
2395 }
2396 if (!ReplaceeClass->IsChildOf(ActorClass))
2397 {
2398 sc->Error(va("Replaced class %s is not an actor class", *sc->String));
2399 }
2400 }
2401
2402 // Time to switch to the C mode.
2403 sc->SetCMode(true);
2404
2405 int GameFilter = 0;
2406 int DoomEdNum = -1;
2407 int SpawnNum = -1;
2408 TArray<VState*> States;
2409 bool DropItemsDefined = false;
2410 VObject* DefObj = (VObject*)Class->Defaults;
2411
2412 if (sc->CheckNumber())
2413 {
2414 if (sc->Number < -1 || sc->Number > 32767)
2415 {
2416 sc->Error("DoomEdNum is out of range [-1, 32767]");
2417 }
2418 DoomEdNum = sc->Number;
2419 }
2420
2421 sc->Expect("{");
2422 while (!sc->Check("}"))
2423 {
2424 if (sc->Check("+"))
2425 {
2426 if (!ParseFlag(sc, Class, true, ClassFixups))
2427 {
2428 return;
2429 }
2430 continue;
2431 }
2432 if (sc->Check("-"))
2433 {
2434 if (!ParseFlag(sc, Class, false, ClassFixups))
2435 {
2436 return;
2437 }
2438 continue;
2439 }
2440
2441 if (sc->Check("action"))
2442 {
2443 ParseAction(sc, Class);
2444 continue;
2445 }
2446
2447 // Get full name of the property.
2448 sc->ExpectIdentifier();
2449 VStr Prop = sc->String;
2450 while (sc->Check("."))
2451 {
2452 sc->ExpectIdentifier();
2453 Prop += ".";
2454 Prop += sc->String;
2455 }
2456 VName PropName = *Prop.ToLower();
2457
2458 bool FoundProp = false;
2459 for (int j = 0; j < FlagList.Num() && !FoundProp; j++)
2460 {
2461 VFlagList& ClassDef = FlagList[j];
2462 if (!Class->IsChildOf(ClassDef.Class))
2463 {
2464 continue;
2465 }
2466 for (int i = ClassDef.PropsHash[GetTypeHash(PropName) &
2467 (PROPS_HASH_SIZE - 1)]; i != -1; i = ClassDef.Props[i].HashNext)
2468 {
2469 VPropDef& P = FlagList[j].Props[i];
2470 if (PropName != P.Name)
2471 {
2472 continue;
2473 }
2474 switch (P.Type)
2475 {
2476 case PROP_Int:
2477 sc->ExpectNumberWithSign();
2478 P.Field->SetInt(DefObj, sc->Number);
2479 break;
2480 case PROP_IntConst:
2481 P.Field->SetInt(DefObj, P.IConst);
2482 break;
2483 case PROP_IntUnsupported:
2484 //FIXME
2485 sc->CheckNumberWithSign();
2486 GCon->Logf("Property %s in %s is not yet supported", *Prop, Class->GetName());
2487 break;
2488 case PROP_BitIndex:
2489 sc->ExpectNumber();
2490 P.Field->SetInt(DefObj, 1 << (sc->Number - 1));
2491 break;
2492 case PROP_Float:
2493 sc->ExpectFloatWithSign();
2494 P.Field->SetFloat(DefObj, sc->Float);
2495 break;
2496 case PROP_Speed:
2497 sc->ExpectFloatWithSign();
2498 P.Field->SetFloat(DefObj, sc->Float * 35.0);
2499 break;
2500 case PROP_Tics:
2501 sc->ExpectNumberWithSign();
2502 P.Field->SetFloat(DefObj, sc->Number / 35.0);
2503 break;
2504 case PROP_TicsSecs:
2505 sc->ExpectNumberWithSign();
2506 P.Field->SetFloat(DefObj, sc->Number >= 0 ?
2507 sc->Number / 35.0 : sc->Number);
2508 break;
2509 case PROP_Percent:
2510 sc->ExpectFloat();
2511 P.Field->SetFloat(DefObj, MID(0, sc->Float, 100) / 100.0);
2512 break;
2513 case PROP_FloatClamped:
2514 sc->ExpectFloatWithSign();
2515 P.Field->SetFloat(DefObj, MID(P.FMin, sc->Float, P.FMax));
2516 break;
2517 case PROP_FloatClamped2:
2518 sc->ExpectFloatWithSign();
2519 P.Field->SetFloat(DefObj, MID(P.FMin, sc->Float, P.FMax));
2520 P.Field2->SetFloat(DefObj, MID(P.FMin, sc->Float, P.FMax));
2521 break;
2522 case PROP_FloatOpt2:
2523 sc->ExpectFloat();
2524 P.Field->SetFloat(DefObj, sc->Float);
2525 P.Field2->SetFloat(DefObj, sc->Float);
2526 if (sc->Check(","))
2527 {
2528 sc->ExpectFloat();
2529 P.Field2->SetFloat(DefObj, sc->Float);
2530 }
2531 else if (sc->CheckFloat())
2532 {
2533 P.Field2->SetFloat(DefObj, sc->Float);
2534 }
2535 break;
2536 case PROP_Name:
2537 sc->ExpectString();
2538 P.Field->SetName(DefObj, *sc->String);
2539 break;
2540 case PROP_NameLower:
2541 sc->ExpectString();
2542 P.Field->SetName(DefObj, *sc->String.ToLower());
2543 break;
2544 case PROP_Str:
2545 sc->ExpectString();
2546 P.Field->SetStr(DefObj, sc->String);
2547 break;
2548 case PROP_StrUnsupported:
2549 //FIXME
2550 sc->ExpectString();
2551 GCon->Logf("Property %s in %s is not yet supported", *Prop, Class->GetName());
2552 break;
2553 case PROP_Class:
2554 sc->ExpectString();
2555 AddClassFixup(Class, P.Field, P.CPrefix + sc->String, ClassFixups);
2556 break;
2557 case PROP_Power_Class:
2558 // This is a very inconvenient shit!
2559 // but ZDoom had to prepend "power" to the name...
2560 sc->ExpectString();
2561 AddClassFixup(Class, P.Field, sc->String.StartsWith("Power") || sc->String.StartsWith("power") ?
2562 sc->String : P.CPrefix + sc->String, ClassFixups);
2563 break;
2564 case PROP_BoolConst:
2565 P.Field->SetBool(DefObj, P.IConst);
2566 break;
2567 case PROP_State:
2568 ParseParentState(sc, Class, *P.PropName);
2569 break;
2570 case PROP_Game:
2571 if (sc->Check("Doom"))
2572 {
2573 GameFilter |= GAME_Doom;
2574 }
2575 else if (sc->Check("Heretic"))
2576 {
2577 GameFilter |= GAME_Heretic;
2578 }
2579 else if (sc->Check("Hexen"))
2580 {
2581 GameFilter |= GAME_Hexen;
2582 }
2583 else if (sc->Check("Strife"))
2584 {
2585 GameFilter |= GAME_Strife;
2586 }
2587 else if (sc->Check("Raven"))
2588 {
2589 GameFilter |= GAME_Raven;
2590 }
2591 else if (sc->Check("Any"))
2592 {
2593 GameFilter |= GAME_Any;
2594 }
2595 else if (GameFilter)
2596 {
2597 sc->Error("Unknown game filter");
2598 }
2599 break;
2600 case PROP_SpawnId:
2601 sc->ExpectNumber();
2602 SpawnNum = sc->Number;
2603 break;
2604 case PROP_ConversationId:
2605 sc->ExpectNumber();
2606 P.Field->SetInt(DefObj, sc->Number);
2607 if (sc->Check(","))
2608 {
2609 sc->ExpectNumberWithSign();
2610 sc->Expect(",");
2611 sc->ExpectNumberWithSign();
2612 }
2613 break;
2614 case PROP_PainChance:
2615 if (sc->CheckNumber())
2616 {
2617 P.Field->SetFloat(DefObj, float(sc->Number) / 256.0);
2618 }
2619 else
2620 {
2621 sc->ExpectString();
2622 VName DamageType = sc->String.ICmp("Normal") ? NAME_None :
2623 VName(*sc->String);
2624 sc->Expect(",");
2625 sc->ExpectNumber();
2626
2627 // Check pain chances array for replacements.
2628 TArray<VPainChanceInfo>& PainChances = GetClassPainChances(Class);
2629 VPainChanceInfo* PC = NULL;
2630 for (int i = 0; i < PainChances.Num(); i++)
2631 {
2632 if (PainChances[i].DamageType == DamageType)
2633 {
2634 PC = &PainChances[i];
2635 break;
2636 }
2637 }
2638 if (!PC)
2639 {
2640 PC = &PainChances.Alloc();
2641 PC->DamageType = DamageType;
2642 }
2643 PC->Chance = float(sc->Number) / 256.0;
2644 }
2645 break;
2646 case PROP_DamageFactor:
2647 {
2648 VName DamageType = NAME_None;
2649 // Check if we only have a number instead of a string, since
2650 // there are some custom WAD files that don't specify a DamageType,
2651 // but specify a DamageFactor
2652 if (!sc->CheckFloat())
2653 {
2654 sc->ExpectString();
2655 VName DamageType = !sc->String.ICmp("Normal") ? NAME_None :
2656 VName(*sc->String);
2657 sc->Expect(",");
2658 sc->ExpectFloat();
2659 }
2660
2661 // Check damage factors array for replacements.
2662 TArray<VDamageFactor> DamageFactors = GetClassDamageFactors(Class);
2663 VDamageFactor* DF = NULL;
2664 for (int i = 0; i < DamageFactors.Num(); i++)
2665 {
2666 if (DamageFactors[i].DamageType == DamageType)
2667 {
2668 DF = &DamageFactors[i];
2669 break;
2670 }
2671 }
2672 if (!DF)
2673 {
2674 DF = &DamageFactors.Alloc();
2675 DF->DamageType = DamageType;
2676 }
2677 DF->Factor = sc->Float;
2678 break;
2679 }
2680 case PROP_MissileDamage:
2681 if (sc->Check("("))
2682 {
2683 VExpression* Expr = ParseExpression(sc);
2684 if (!Expr)
2685 {
2686 ParseError(sc->GetLoc(), "Damage expression expected");
2687 }
2688 else
2689 {
2690 VMethod* M = new VMethod("GetMissileDamage", Class, sc->GetLoc());
2691 M->ReturnTypeExpr = new VTypeExpr(TYPE_Int, sc->GetLoc());
2692 M->ReturnType = TYPE_Int;
2693 M->NumParams = 2;
2694 M->Params[0].Name = "Mask";
2695 M->Params[0].Loc = sc->GetLoc();
2696 M->Params[0].TypeExpr = new VTypeExpr(TYPE_Int, sc->GetLoc());
2697 M->Params[1].Name = "Add";
2698 M->Params[1].Loc = sc->GetLoc();
2699 M->Params[1].TypeExpr = new VTypeExpr(TYPE_Int, sc->GetLoc());
2700 M->Statement = new VReturn(Expr, sc->GetLoc());
2701 Class->AddMethod(M);
2702 M->Define();
2703 }
2704 sc->Expect(")");
2705 }
2706 else
2707 {
2708 sc->ExpectNumber();
2709 P.Field->SetInt(DefObj, sc->Number);
2710 }
2711 break;
2712 case PROP_VSpeed:
2713 {
2714 sc->ExpectFloatWithSign();
2715 TVec Val = P.Field->GetVec(DefObj);
2716 Val.z = sc->Float * 35.0;
2717 P.Field->SetVec(DefObj, Val);
2718 break;
2719 }
2720 case PROP_RenderStyle:
2721 {
2722 int RenderStyle = 0;
2723 if (sc->Check("None"))
2724 {
2725 RenderStyle = STYLE_None;
2726 }
2727 else if (sc->Check("Normal"))
2728 {
2729 RenderStyle = STYLE_Normal;
2730 }
2731 else if (sc->Check("Fuzzy"))
2732 {
2733 RenderStyle = STYLE_Fuzzy;
2734 }
2735 else if (sc->Check("SoulTrans"))
2736 {
2737 RenderStyle = STYLE_SoulTrans;
2738 }
2739 else if (sc->Check("OptFuzzy"))
2740 {
2741 RenderStyle = STYLE_OptFuzzy;
2742 }
2743 else if (sc->Check("Translucent"))
2744 {
2745 RenderStyle = STYLE_Translucent;
2746 }
2747 else if (sc->Check("Add"))
2748 {
2749 RenderStyle = STYLE_Add;
2750 }
2751 else if (sc->Check("Stencil"))
2752 {
2753 //FIXME
2754 GCon->Logf("Render style Stencil in %s is not yet supported", Class->GetName());
2755 }
2756 else if (sc->Check("Shaded"))
2757 {
2758 //FIXME -- This is an aproximated style... but it's not the desired one!
2759 RenderStyle = STYLE_Fuzzy;
2760 }
2761 else
2762 {
2763 sc->Error("Bad render style");
2764 }
2765 P.Field->SetByte(DefObj, RenderStyle);
2766 break;
2767 }
2768 case PROP_Translation:
2769 P.Field->SetInt(DefObj, R_ParseDecorateTranslation(sc,
2770 GameFilter & GAME_Strife ? 7 : 3));
2771 break;
2772 case PROP_BloodColour:
2773 {
2774 vuint32 Col;
2775 if (sc->CheckNumber())
2776 {
2777 int r = MID(0, sc->Number, 255);
2778 sc->Check(",");
2779 sc->ExpectNumber();
2780 int g = MID(0, sc->Number, 255);
2781 sc->Check(",");
2782 sc->ExpectNumber();
2783 int b = MID(0, sc->Number, 255);
2784 Col = 0xff000000 | (r << 16) | (g << 8) | b;
2785 }
2786 else
2787 {
2788 sc->ExpectString();
2789 Col = M_ParseColour(sc->String);
2790 }
2791 P.Field->SetInt(DefObj, Col);
2792 P.Field2->SetInt(DefObj, R_GetBloodTranslation(Col));
2793 break;
2794 }
2795 case PROP_BloodType:
2796 sc->ExpectString();
2797 AddClassFixup(Class, P.Field, sc->String, ClassFixups);
2798 if (sc->Check(","))
2799 {
2800 sc->ExpectString();
2801 }
2802 AddClassFixup(Class, P.Field2, sc->String, ClassFixups);
2803 if (sc->Check(","))
2804 {
2805 sc->ExpectString();
2806 }
2807 AddClassFixup(Class, "AxeBloodType", sc->String, ClassFixups);
2808 break;
2809 case PROP_StencilColour:
2810 //FIXME
2811 if (sc->CheckNumber())
2812 {
2813 sc->ExpectNumber();
2814 sc->ExpectNumber();
2815 }
2816 else
2817 {
2818 sc->ExpectString();
2819 }
2820 GCon->Logf("Property StencilColor in %s is not yet supported", Class->GetName());
2821 break;
2822 case PROP_Monster:
2823 SetClassFieldBool(Class, "bShootable", true);
2824 SetClassFieldBool(Class, "bCountKill", true);
2825 SetClassFieldBool(Class, "bSolid", true);
2826 SetClassFieldBool(Class, "bActivatePushWall", true);
2827 SetClassFieldBool(Class, "bActivateMCross", true);
2828 SetClassFieldBool(Class, "bPassMobj", true);
2829 SetClassFieldBool(Class, "bMonster", true);
2830 SetClassFieldBool(Class, "bCanUseWalls", true);
2831 break;
2832 case PROP_Projectile:
2833 SetClassFieldBool(Class, "bNoBlockmap", true);
2834 SetClassFieldBool(Class, "bNoGravity", true);
2835 SetClassFieldBool(Class, "bDropOff", true);
2836 SetClassFieldBool(Class, "bMissile", true);
2837 SetClassFieldBool(Class, "bActivateImpact", true);
2838 SetClassFieldBool(Class, "bActivatePCross", true);
2839 SetClassFieldBool(Class, "bNoTeleport", true);
2840 if (GGameInfo->Flags & VGameInfo::GIF_DefaultBloodSplatter)
2841 {
2842 SetClassFieldBool(Class, "bBloodSplatter", true);
2843 }
2844 break;
2845 case PROP_BounceType:
2846 if (sc->Check("None"))
2847 {
2848 SetClassFieldByte(Class, "BounceType", BOUNCE_None);
2849 }
2850 else if (sc->Check("Doom"))
2851 {
2852 SetClassFieldByte(Class, "BounceType", BOUNCE_Doom);
2853 SetClassFieldBool(Class, "bBounceWalls", true);
2854 SetClassFieldBool(Class, "bBounceFloors", true);
2855 SetClassFieldBool(Class, "bBounceCeilings", true);
2856 SetClassFieldBool(Class, "bBounceOnActors", true);
2857 SetClassFieldBool(Class, "bBounceAutoOff", true);
2858 }
2859 else if (sc->Check("Heretic"))
2860 {
2861 SetClassFieldByte(Class, "BounceType", BOUNCE_Heretic);
2862 SetClassFieldBool(Class, "bBounceFloors", true);
2863 SetClassFieldBool(Class, "bBounceCeilings", true);
2864 }
2865 else if (sc->Check("Hexen"))
2866 {
2867 SetClassFieldByte(Class, "BounceType", BOUNCE_Hexen);
2868 SetClassFieldBool(Class, "bBounceWalls", true);
2869 SetClassFieldBool(Class, "bBounceFloors", true);
2870 SetClassFieldBool(Class, "bBounceCeilings", true);
2871 SetClassFieldBool(Class, "bBounceOnActors", true);
2872 }
2873 else if (sc->Check("DoomCompat"))
2874 {
2875 SetClassFieldByte(Class, "BounceType", BOUNCE_Doom);
2876 }
2877 else if (sc->Check("HereticCompat"))
2878 {
2879 SetClassFieldByte(Class, "BounceType", BOUNCE_Heretic);
2880 }
2881 else if (sc->Check("HexenCompat"))
2882 {
2883 SetClassFieldByte(Class, "BounceType", BOUNCE_Hexen);
2884 }
2885 else if (sc->Check("Grenade"))
2886 {
2887 // Bounces on walls and flats like ZDoom bounce.
2888 SetClassFieldByte(Class, "BounceType", BOUNCE_Doom);
2889 SetClassFieldBool(Class, "bBounceOnActors", false);
2890 }
2891 else if (sc->Check("Classic"))
2892 {
2893 // Bounces on flats only, but does not die when bouncing.
2894 SetClassFieldByte(Class, "BounceType", BOUNCE_Heretic);
2895 SetClassFieldBool(Class, "bMBFBounce", true);
2896 }
2897 break;
2898 case PROP_ClearFlags:
2899 for (int j = 0; j < FlagList.Num(); j++)
2900 {
2901 if (FlagList[j].Class != ActorClass)
2902 {
2903 continue;
2904 }
2905 for (int i = 0; i < FlagList[j].Flags.Num(); i++)
2906 {
2907 VFlagDef& F = FlagList[j].Flags[i];
2908 switch (F.Type)
2909 {
2910 case FLAG_Bool:
2911 F.Field->SetBool(DefObj, false);
2912 break;
2913 }
2914 }
2915 }
2916 SetClassFieldByte(Class, "BounceType", BOUNCE_None);
2917 SetClassFieldBool(Class, "bColideWithThings", true);
2918 SetClassFieldBool(Class, "bColideWithWorld", true);
2919 SetClassFieldBool(Class, "bPickUp", false);
2920 break;
2921 case PROP_DropItem:
2922 {
2923 if (!DropItemsDefined)
2924 {
2925 GetClassDropItems(Class).Clear();
2926 DropItemsDefined = true;
2927 }
2928 sc->ExpectString();
2929 VDropItemInfo DI;
2930 DI.TypeName = *sc->String.ToLower();
2931 DI.Type = NULL;
2932 DI.Amount = 0;
2933 DI.Chance = 1.0;
2934 bool HaveChance = false;
2935 if (sc->Check(","))
2936 {
2937 sc->ExpectNumber();
2938 HaveChance = true;
2939 }
2940 else
2941 {
2942 HaveChance = sc->CheckNumber();
2943 }
2944 if (HaveChance)
2945 {
2946 DI.Chance = float(sc->Number) / 255.0;
2947 if (sc->Check(","))
2948 {
2949 sc->ExpectNumber();
2950 DI.Amount = sc->Number;
2951 }
2952 else if (sc->CheckNumber())
2953 {
2954 DI.Amount = sc->Number;
2955 }
2956 }
2957 GetClassDropItems(Class).Insert(0, DI);
2958 break;
2959 }
2960 case PROP_States:
2961 if (!ParseStates(sc, Class, States))
2962 {
2963 return;
2964 }
2965 break;
2966 case PROP_SkipSuper:
2967 {
2968 // Preserve items that should not be copied
2969 TArray<VDamageFactor> DamageFactors = GetClassDamageFactors(Class);
2970 TArray<VPainChanceInfo> PainChances = GetClassPainChances(Class);
2971 // Copy default properties.
2972 ActorClass->CopyObject(ActorClass->Defaults, Class->Defaults);
2973 // Copy state labels
2974 Class->StateLabels = ActorClass->StateLabels;
2975 Class->ClassFlags |= CLASS_SkipSuperStateLabels;
2976 // Drop items are reset back to the list of the parent class
2977 GetClassDropItems(Class) = GetClassDropItems(Class->ParentClass);
2978 // Restore items that should not be copied
2979 GetClassDamageFactors(Class) = DamageFactors;
2980 GetClassPainChances(Class) = PainChances;
2981 break;
2982 }
2983 case PROP_Args:
2984 for (int i = 0; i < 5; i++)
2985 {
2986 sc->ExpectNumber();
2987 P.Field->SetInt(DefObj, sc->Number, i);
2988 if (i < 4 && !sc->Check(","))
2989 {
2990 break;
2991 }
2992 }
2993 P.Field2->SetBool(DefObj, true);
2994 break;
2995 case PROP_LowMessage:
2996 sc->ExpectNumber();
2997 P.Field->SetInt(DefObj, sc->Number);
2998 sc->Expect(",");
2999 sc->ExpectString();
3000 P.Field2->SetStr(DefObj, sc->String);
3001 break;
3002 case PROP_PowerupColour:
3003 if (sc->Check("InverseMap"))
3004 {
3005 P.Field->SetInt(DefObj, 0x00123456);
3006 }
3007 else if (sc->Check("GoldMap"))
3008 {
3009 P.Field->SetInt(DefObj, 0x00123457);
3010 }
3011 else if (sc->Check("RedMap"))
3012 {
3013 P.Field->SetInt(DefObj, 0x00123458);
3014 }
3015 else if (sc->Check("GreenMap"))
3016 {
3017 P.Field->SetInt(DefObj, 0x00123459);
3018 }
3019 else
3020 {
3021 int a, r, g, b;
3022 if (sc->CheckNumber())
3023 {
3024 r = MID(0, sc->Number, 255);
3025 sc->Check(",");
3026 sc->ExpectNumber();
3027 g = MID(0, sc->Number, 255);
3028 sc->Check(",");
3029 sc->ExpectNumber();
3030 b = MID(0, sc->Number, 255);
3031 }
3032 else
3033 {
3034 vuint32 Col;
3035 sc->ExpectString();
3036 Col = M_ParseColour(sc->String);
3037 r = (Col >> 16) & 0xff;
3038 g = (Col >> 8) & 0xff;
3039 b = Col & 0xff;
3040 }
3041 sc->Check(",");
3042 sc->ExpectFloat();
3043 a = MID(0, int(sc->Float * 255), 254);
3044 if (a > 250)
3045 {
3046 a = 250;
3047 }
3048 P.Field->SetInt(DefObj, (r << 16) | (g << 8) | b | (a << 24));
3049 }
3050 break;
3051 case PROP_ColourRange:
3052 sc->ExpectNumber();
3053 P.Field->SetInt(DefObj, sc->Number);
3054 sc->Check(",");
3055 sc->ExpectNumber();
3056 P.Field2->SetInt(DefObj, sc->Number);
3057 break;
3058 case PROP_DamageScreenColour:
3059 {
3060 // First number is ignored. Is it a bug?
3061 int Col;
3062 if (sc->CheckNumber())
3063 {
3064 sc->ExpectNumber();
3065 int r = MID(sc->Number, 0, 255);
3066 sc->Check(",");
3067 sc->ExpectNumber();
3068 int g = MID(sc->Number, 0, 255);
3069 sc->Check(",");
3070 sc->ExpectNumber();
3071 int b = MID(sc->Number, 0, 255);
3072 Col = 0xff000000 | (r << 16) | (g << 8) | b;
3073 }
3074 else
3075 {
3076 sc->ExpectString();
3077 Col = M_ParseColour(sc->String);
3078 }
3079 P.Field->SetInt(DefObj, Col);
3080 break;
3081 }
3082 case PROP_HexenArmor:
3083 sc->ExpectFloat();
3084 P.Field->SetFloat(DefObj, sc->Float, 0);
3085 sc->Expect(",");
3086 sc->ExpectFloat();
3087 P.Field->SetFloat(DefObj, sc->Float, 1);
3088 sc->Expect(",");
3089 sc->ExpectFloat();
3090 P.Field->SetFloat(DefObj, sc->Float, 2);
3091 sc->Expect(",");
3092 sc->ExpectFloat();
3093 P.Field->SetFloat(DefObj, sc->Float, 3);
3094 sc->Expect(",");
3095 sc->ExpectFloat();
3096 P.Field->SetFloat(DefObj, sc->Float, 4);
3097 break;
3098 case PROP_StartItem:
3099 {
3100 TArray<VDropItemInfo>& DropItems =
3101 *(TArray<VDropItemInfo>*)P.Field->GetFieldPtr(DefObj);
3102 if (!DropItemsDefined)
3103 {
3104 DropItems.Clear();
3105 DropItemsDefined = true;
3106 }
3107 sc->ExpectString();
3108 VDropItemInfo DI;
3109 DI.TypeName = *sc->String.ToLower();
3110 DI.Type = NULL;
3111 DI.Amount = 0;
3112 DI.Chance = 1.0;
3113 if (sc->Check(","))
3114 {
3115 sc->ExpectNumber();
3116 DI.Amount = sc->Number;
3117 }
3118 else if (sc->CheckNumber())
3119 {
3120 DI.Amount = sc->Number;
3121 }
3122 DropItems.Insert(0, DI);
3123 break;
3124 }
3125 case PROP_MorphStyle:
3126 if (sc->CheckNumber())
3127 {
3128 P.Field->SetInt(DefObj, sc->Number);
3129 }
3130 else
3131 {
3132 bool HaveParen = sc->Check("(");
3133 int Val = 0;
3134 do
3135 {
3136 if (sc->Check("MRF_ADDSTAMINA"))
3137 {
3138 Val |= 1;
3139 }
3140 else if (sc->Check("MRF_FULLHEALTH"))
3141 {
3142 Val |= 2;
3143 }
3144 else if (sc->Check("MRF_UNDOBYTOMEOFPOWER"))
3145 {
3146 Val |= 4;
3147 }
3148 else if (sc->Check("MRF_UNDOBYCHAOSDEVICE"))
3149 {
3150 Val |= 8;
3151 }
3152 else if (sc->Check("MRF_FAILNOTELEFRAG"))
3153 {
3154 Val |= 16;
3155 }
3156 else if (sc->Check("MRF_FAILNOLAUGH"))
3157 {
3158 Val |= 32;
3159 }
3160 else if (sc->Check("MRF_WHENINVULNERABLE"))
3161 {
3162 Val |= 64;
3163 }
3164 else if (sc->Check("MRF_LOSEACTUALWEAPON"))
3165 {
3166 Val |= 128;
3167 }
3168 else if (sc->Check("MRF_NEWTIDBEHAVIOUR"))
3169 {
3170 Val |= 256;
3171 }
3172 else if (sc->Check("MRF_UNDOBYDEATH"))
3173 {
3174 Val |= 512;
3175 }
3176 else if (sc->Check("MRF_UNDOBYDEATHFORCED"))
3177 {
3178 Val |= 1024;
3179 }
3180 else if (sc->Check("MRF_UNDOBYDEATHSAVES"))
3181 {
3182 Val |= 2048;
3183 }
3184 else
3185 {
3186 sc->Error("Bad morph style");
3187 }
3188 }
3189 while (sc->Check("|"));
3190 if (HaveParen)
3191 {
3192 sc->Expect(")");
3193 }
3194 P.Field->SetInt(DefObj, Val);
3195 }
3196 break;
3197 }
3198 FoundProp = true;
3199 break;
3200 }
3201 }
3202 if (FoundProp)
3203 {
3204 continue;
3205 }
3206
3207 sc->Error(va("Unknown property \"%s\"", *Prop));
3208 }
3209
3210 sc->SetCMode(false);
3211
3212 Class->EmitStateLabels();
3213
3214 // Set up linked list of states.
3215 if (States.Num())
3216 {
3217 Class->States = States[0];
3218 for (int i = 0; i < States.Num() - 1; i++)
3219 {
3220 States[i]->Next = States[i + 1];
3221 }
3222
3223 for (int i = 0; i < States.Num(); i++)
3224 {
3225 if (States[i]->GotoLabel != NAME_None)
3226 {
3227 States[i]->NextState = Class->ResolveStateLabel(
3228 States[i]->Loc, States[i]->GotoLabel, States[i]->GotoOffset);
3229 }
3230 }
3231 }
3232
3233 if (DoomEdNum > 0)
3234 {
3235 mobjinfo_t& MI = VClass::GMobjInfos.Alloc();
3236 MI.Class = Class;
3237 MI.DoomEdNum = DoomEdNum;
3238 MI.GameFilter = GameFilter;
3239 }
3240 if (SpawnNum > 0)
3241 {
3242 mobjinfo_t& SI = VClass::GScriptIds.Alloc();
3243 SI.Class = Class;
3244 SI.DoomEdNum = SpawnNum;
3245 SI.GameFilter = GameFilter;
3246 }
3247 if (ReplaceeClass)
3248 {
3249 ReplaceeClass->Replacement = Class;
3250 Class->Replacee = ReplaceeClass;
3251 }
3252 unguard;
3253 }
3254
3255 //==========================================================================
3256 //
3257 // ParseOldDecStates
3258 //
3259 //==========================================================================
3260
ParseOldDecStates(VScriptParser * sc,TArray<VState * > & States,VClass * Class)3261 static void ParseOldDecStates(VScriptParser* sc, TArray<VState*>& States,
3262 VClass* Class)
3263 {
3264 guard(ParseOldDecStates);
3265 TArray<VStr> Tokens;
3266 sc->String.Split(",\t\r\n", Tokens);
3267 for (int TokIdx = 0; TokIdx < Tokens.Num(); TokIdx++)
3268 {
3269 const char* pFrame = *Tokens[TokIdx];
3270 int DurColon = Tokens[TokIdx].IndexOf(':');
3271 float Duration = 4;
3272 if (DurColon >= 0)
3273 {
3274 Duration = atoi(pFrame);
3275 pFrame = *Tokens[TokIdx] + DurColon + 1;
3276 }
3277
3278 bool GotState = false;
3279 while (*pFrame)
3280 {
3281 if (*pFrame == ' ')
3282 {
3283 }
3284 else if (*pFrame == '*')
3285 {
3286 if (!GotState)
3287 {
3288 sc->Error("* must come after a frame");
3289 }
3290 States[States.Num() - 1]->Frame |= VState::FF_FULLBRIGHT;
3291 }
3292 else if (*pFrame < 'A' || *pFrame > ']')
3293 {
3294 sc->Error("Frames must be A-Z, [, \\, or ]");
3295 }
3296 else
3297 {
3298 GotState = true;
3299 VState* State = new VState(va("S_%d", States.Num()), Class,
3300 sc->GetLoc());
3301 States.Append(State);
3302 State->Frame = *pFrame - 'A';
3303 State->Time = Duration >= 0 ? float(Duration) / 35.0 : -1.0;
3304 }
3305 pFrame++;
3306 }
3307 }
3308 unguard;
3309 }
3310
3311 //==========================================================================
3312 //
3313 // ParseOldDecoration
3314 //
3315 //==========================================================================
3316
ParseOldDecoration(VScriptParser * sc,int Type)3317 static void ParseOldDecoration(VScriptParser* sc, int Type)
3318 {
3319 guard(ParseOldDecoration);
3320 // Get name of the class.
3321 sc->ExpectString();
3322 VName ClassName = *sc->String;
3323
3324 // Create class.
3325 VClass* Class = Type == OLDDEC_Pickup ?
3326 FakeInventoryClass->CreateDerivedClass(ClassName, DecPkg,
3327 sc->GetLoc()) :
3328 ActorClass->CreateDerivedClass(ClassName, DecPkg, sc->GetLoc());
3329 DecPkg->ParsedClasses.Append(Class);
3330 if (Type == OLDDEC_Breakable)
3331 {
3332 SetClassFieldBool(Class, "bShootable", true);
3333 }
3334 if (Type == OLDDEC_Projectile)
3335 {
3336 SetClassFieldBool(Class, "bMissile", true);
3337 SetClassFieldBool(Class, "bDropOff", true);
3338 }
3339
3340 // Parse game filters.
3341 int GameFilter = 0;
3342 while (!sc->Check("{"))
3343 {
3344 if (sc->Check("Doom"))
3345 {
3346 GameFilter |= GAME_Doom;
3347 }
3348 else if (sc->Check("Heretic"))
3349 {
3350 GameFilter |= GAME_Heretic;
3351 }
3352 else if (sc->Check("Hexen"))
3353 {
3354 GameFilter |= GAME_Hexen;
3355 }
3356 else if (sc->Check("Strife"))
3357 {
3358 GameFilter |= GAME_Strife;
3359 }
3360 else if (sc->Check("Raven"))
3361 {
3362 GameFilter |= GAME_Raven;
3363 }
3364 else if (sc->Check("Any"))
3365 {
3366 GameFilter |= GAME_Any;
3367 }
3368 else if (GameFilter)
3369 {
3370 sc->Error("Unknown game filter");
3371 }
3372 else
3373 {
3374 sc->Error("Unknown identifier");
3375 }
3376 }
3377
3378 int DoomEdNum = -1;
3379 int SpawnNum = -1;
3380 VName Sprite("tnt1");
3381 VName DeathSprite(NAME_None);
3382 TArray<VState*> States;
3383 int SpawnStart = 0;
3384 int SpawnEnd = 0;
3385 int DeathStart = 0;
3386 int DeathEnd = 0;
3387 bool DiesAway = false;
3388 bool SolidOnDeath = false;
3389 float DeathHeight = 0.0;
3390 int BurnStart = 0;
3391 int BurnEnd = 0;
3392 bool BurnsAway = false;
3393 bool SolidOnBurn = false;
3394 float BurnHeight = 0.0;
3395 int IceStart = 0;
3396 int IceEnd = 0;
3397 bool GenericIceDeath = false;
3398 bool Explosive = false;
3399
3400 while (!sc->Check("}"))
3401 {
3402 if (sc->Check("DoomEdNum"))
3403 {
3404 sc->ExpectNumber();
3405 if (sc->Number < -1 || sc->Number > 32767)
3406 {
3407 sc->Error("DoomEdNum is out of range [-1, 32767]");
3408 }
3409 DoomEdNum = sc->Number;
3410 }
3411 else if (sc->Check("SpawnNum"))
3412 {
3413 sc->ExpectNumber();
3414 if (sc->Number < 0 || sc->Number > 255)
3415 {
3416 sc->Error("SpawnNum is out of range [0, 255]");
3417 }
3418 SpawnNum = sc->Number;
3419 }
3420
3421 // Spawn state
3422 else if (sc->Check("Sprite"))
3423 {
3424 sc->ExpectString();
3425 if (sc->String.Length() != 4)
3426 {
3427 sc->Error("Sprite name must be 4 characters long");
3428 }
3429 Sprite = *sc->String.ToLower();
3430 }
3431 else if (sc->Check("Frames"))
3432 {
3433 sc->ExpectString();
3434 SpawnStart = States.Num();
3435 ParseOldDecStates(sc, States, Class);
3436 SpawnEnd = States.Num();
3437 }
3438
3439 // Death states
3440 else if ((Type == OLDDEC_Breakable || Type == OLDDEC_Projectile) &&
3441 sc->Check("DeathSprite"))
3442 {
3443 sc->ExpectString();
3444 if (sc->String.Length() != 4)
3445 {
3446 sc->Error("Sprite name must be 4 characters long");
3447 }
3448 DeathSprite = *sc->String.ToLower();
3449 }
3450 else if ((Type == OLDDEC_Breakable || Type == OLDDEC_Projectile) &&
3451 sc->Check("DeathFrames"))
3452 {
3453 sc->ExpectString();
3454 DeathStart = States.Num();
3455 ParseOldDecStates(sc, States, Class);
3456 DeathEnd = States.Num();
3457 }
3458 else if (Type == OLDDEC_Breakable && sc->Check("DiesAway"))
3459 {
3460 DiesAway = true;
3461 }
3462 else if (Type == OLDDEC_Breakable && sc->Check("BurnDeathFrames"))
3463 {
3464 sc->ExpectString();
3465 BurnStart = States.Num();
3466 ParseOldDecStates(sc, States, Class);
3467 BurnEnd = States.Num();
3468 }
3469 else if (Type == OLDDEC_Breakable && sc->Check("BurnsAway"))
3470 {
3471 BurnsAway = true;
3472 }
3473 else if (Type == OLDDEC_Breakable && sc->Check("IceDeathFrames"))
3474 {
3475 sc->ExpectString();
3476 IceStart = States.Num();
3477 ParseOldDecStates(sc, States, Class);
3478
3479 // Make a copy of the last state for A_FreezeDeathChunks
3480 VState* State = new VState(va("S_%d", States.Num()), Class,
3481 sc->GetLoc());
3482 States.Append(State);
3483 State->Frame = States[States.Num() - 2]->Frame;
3484
3485 IceEnd = States.Num();
3486 }
3487 else if (Type == OLDDEC_Breakable && sc->Check("GenericIceDeath"))
3488 {
3489 GenericIceDeath = true;
3490 }
3491
3492 // Misc properties
3493 else if (sc->Check("Radius"))
3494 {
3495 sc->ExpectFloat();
3496 SetClassFieldFloat(Class, "Radius", sc->Float);
3497 }
3498 else if (sc->Check("Height"))
3499 {
3500 sc->ExpectFloat();
3501 SetClassFieldFloat(Class, "Height", sc->Float);
3502 }
3503 else if (sc->Check("Mass"))
3504 {
3505 sc->ExpectFloat();
3506 SetClassFieldFloat(Class, "Mass", sc->Float);
3507 }
3508 else if (sc->Check("Scale"))
3509 {
3510 sc->ExpectFloat();
3511 SetClassFieldFloat(Class, "ScaleX", sc->Float);
3512 SetClassFieldFloat(Class, "ScaleY", sc->Float);
3513 }
3514 else if (sc->Check("Alpha"))
3515 {
3516 sc->ExpectFloat();
3517 SetClassFieldFloat(Class, "Alpha", MID(0.0, sc->Float, 1.0));
3518 }
3519 else if (sc->Check("RenderStyle"))
3520 {
3521 int RenderStyle = 0;
3522 if (sc->Check("STYLE_None"))
3523 {
3524 RenderStyle = STYLE_None;
3525 }
3526 else if (sc->Check("STYLE_Normal"))
3527 {
3528 RenderStyle = STYLE_Normal;
3529 }
3530 else if (sc->Check("STYLE_Fuzzy"))
3531 {
3532 RenderStyle = STYLE_Fuzzy;
3533 }
3534 else if (sc->Check("STYLE_SoulTrans"))
3535 {
3536 RenderStyle = STYLE_SoulTrans;
3537 }
3538 else if (sc->Check("STYLE_OptFuzzy"))
3539 {
3540 RenderStyle = STYLE_OptFuzzy;
3541 }
3542 else if (sc->Check("STYLE_Translucent"))
3543 {
3544 RenderStyle = STYLE_Translucent;
3545 }
3546 else if (sc->Check("STYLE_Add"))
3547 {
3548 RenderStyle = STYLE_Add;
3549 }
3550 else
3551 {
3552 sc->Error("Bad render style");
3553 }
3554 SetClassFieldByte(Class, "RenderStyle", RenderStyle);
3555 }
3556 else if (sc->Check("Translation1"))
3557 {
3558 sc->ExpectNumber();
3559 if (sc->Number < 0 || sc->Number > 2)
3560 {
3561 sc->Error("Translation1 is out of range [0, 2]");
3562 }
3563 SetClassFieldInt(Class, "Translation", (TRANSL_Standard <<
3564 TRANSL_TYPE_SHIFT) + sc->Number);
3565 }
3566 else if (sc->Check("Translation2"))
3567 {
3568 sc->ExpectNumber();
3569 if (sc->Number < 0 || sc->Number > MAX_LEVEL_TRANSLATIONS)
3570 {
3571 sc->Error(va("Translation2 is out of range [0, %d]",
3572 MAX_LEVEL_TRANSLATIONS));
3573 }
3574 SetClassFieldInt(Class, "Translation", (TRANSL_Level <<
3575 TRANSL_TYPE_SHIFT) + sc->Number);
3576 }
3577
3578 // Breakable decoration properties.
3579 else if (Type == OLDDEC_Breakable && sc->Check("Health"))
3580 {
3581 sc->ExpectNumber();
3582 SetClassFieldInt(Class, "Health", sc->Number);
3583 }
3584 else if (Type == OLDDEC_Breakable && sc->Check("DeathHeight"))
3585 {
3586 sc->ExpectFloat();
3587 DeathHeight = sc->Float;
3588 }
3589 else if (Type == OLDDEC_Breakable && sc->Check("BurnHeight"))
3590 {
3591 sc->ExpectFloat();
3592 BurnHeight = sc->Float;
3593 }
3594 else if (Type == OLDDEC_Breakable && sc->Check("SolidOnDeath"))
3595 {
3596 SolidOnDeath = true;
3597 }
3598 else if (Type == OLDDEC_Breakable && sc->Check("SolidOnBurn"))
3599 {
3600 SolidOnBurn = true;
3601 }
3602 else if ((Type == OLDDEC_Breakable || Type == OLDDEC_Projectile) &&
3603 sc->Check("DeathSound"))
3604 {
3605 sc->ExpectString();
3606 SetClassFieldName(Class, "DeathSound", *sc->String);
3607 }
3608 else if (Type == OLDDEC_Breakable && sc->Check("BurnDeathSound"))
3609 {
3610 sc->ExpectString();
3611 SetClassFieldName(Class, "ActiveSound", *sc->String);
3612 }
3613
3614 // Projectile properties
3615 else if (Type == OLDDEC_Projectile && sc->Check("Speed"))
3616 {
3617 sc->ExpectFloat();
3618 SetClassFieldFloat(Class, "Speed", sc->Float * 35.0);
3619 }
3620 else if (Type == OLDDEC_Projectile && sc->Check("Damage"))
3621 {
3622 sc->ExpectNumber();
3623 SetClassFieldFloat(Class, "MissileDamage", sc->Number);
3624 }
3625 else if (Type == OLDDEC_Projectile && sc->Check("DamageType"))
3626 {
3627 if (sc->Check("Normal"))
3628 {
3629 SetClassFieldName(Class, "DamageType", NAME_None);
3630 }
3631 else
3632 {
3633 sc->ExpectString();
3634 SetClassFieldName(Class, "DamageType", *sc->String);
3635 }
3636 }
3637 else if (Type == OLDDEC_Projectile && sc->Check("SpawnSound"))
3638 {
3639 sc->ExpectString();
3640 SetClassFieldName(Class, "SightSound", *sc->String);
3641 }
3642 else if (Type == OLDDEC_Projectile && sc->Check("ExplosionRadius"))
3643 {
3644 sc->ExpectNumber();
3645 SetClassFieldFloat(Class, "ExplosionRadius", sc->Number);
3646 Explosive = true;
3647 }
3648 else if (Type == OLDDEC_Projectile && sc->Check("ExplosionDamage"))
3649 {
3650 sc->ExpectNumber();
3651 SetClassFieldFloat(Class, "ExplosionDamage", sc->Number);
3652 Explosive = true;
3653 }
3654 else if (Type == OLDDEC_Projectile && sc->Check("DoNotHurtShooter"))
3655 {
3656 SetClassFieldBool(Class, "bExplosionDontHurtSelf", true);
3657 }
3658 else if (Type == OLDDEC_Projectile && sc->Check("DoomBounce"))
3659 {
3660 SetClassFieldByte(Class, "BounceType", BOUNCE_Doom);
3661 }
3662 else if (Type == OLDDEC_Projectile && sc->Check("HereticBounce"))
3663 {
3664 SetClassFieldByte(Class, "BounceType", BOUNCE_Heretic);
3665 }
3666 else if (Type == OLDDEC_Projectile && sc->Check("HexenBounce"))
3667 {
3668 SetClassFieldByte(Class, "BounceType", BOUNCE_Hexen);
3669 }
3670
3671 // Pickup properties
3672 else if (Type == OLDDEC_Pickup && sc->Check("PickupMessage"))
3673 {
3674 sc->ExpectString();
3675 SetClassFieldStr(Class, "PickupMessage", sc->String);
3676 }
3677 else if (Type == OLDDEC_Pickup && sc->Check("PickupSound"))
3678 {
3679 sc->ExpectString();
3680 SetClassFieldName(Class, "PickupSound", *sc->String);
3681 }
3682 else if (Type == OLDDEC_Pickup && sc->Check("Respawns"))
3683 {
3684 SetClassFieldBool(Class, "bRespawns", true);
3685 }
3686
3687 // Compatibility flags
3688 else if (sc->Check("LowGravity"))
3689 {
3690 SetClassFieldFloat(Class, "Gravity", 0.125);
3691 }
3692 else if (sc->Check("FireDamage"))
3693 {
3694 SetClassFieldName(Class, "DamageType", "Fire");
3695 }
3696
3697 // Flags
3698 else if (sc->Check("Solid"))
3699 {
3700 SetClassFieldBool(Class, "bSolid", true);
3701 }
3702 else if (sc->Check("NoSector"))
3703 {
3704 SetClassFieldBool(Class, "bNoSector", true);
3705 }
3706 else if (sc->Check("NoBlockmap"))
3707 {
3708 SetClassFieldBool(Class, "bNoBlockmap", true);
3709 }
3710 else if (sc->Check("SpawnCeiling"))
3711 {
3712 SetClassFieldBool(Class, "bSpawnCeiling", true);
3713 }
3714 else if (sc->Check("NoGravity"))
3715 {
3716 SetClassFieldBool(Class, "bNoGravity", true);
3717 }
3718 else if (sc->Check("Shadow"))
3719 {
3720 GCon->Logf("Shadow flag is not currently supported");
3721 }
3722 else if (sc->Check("NoBlood"))
3723 {
3724 SetClassFieldBool(Class, "bNoBlood", true);
3725 }
3726 else if (sc->Check("CountItem"))
3727 {
3728 SetClassFieldBool(Class, "bCountItem", true);
3729 }
3730 else if (sc->Check("WindThrust"))
3731 {
3732 SetClassFieldBool(Class, "bWindThrust", true);
3733 }
3734 else if (sc->Check("FloorClip"))
3735 {
3736 SetClassFieldBool(Class, "bFloorClip", true);
3737 }
3738 else if (sc->Check("SpawnFloat"))
3739 {
3740 SetClassFieldBool(Class, "bSpawnFloat", true);
3741 }
3742 else if (sc->Check("NoTeleport"))
3743 {
3744 SetClassFieldBool(Class, "bNoTeleport", true);
3745 }
3746 else if (sc->Check("Ripper"))
3747 {
3748 SetClassFieldBool(Class, "bRip", true);
3749 }
3750 else if (sc->Check("Pushable"))
3751 {
3752 SetClassFieldBool(Class, "bPushable", true);
3753 }
3754 else if (sc->Check("SlidesOnWalls"))
3755 {
3756 SetClassFieldBool(Class, "bSlide", true);
3757 }
3758 else if (sc->Check("CanPass"))
3759 {
3760 SetClassFieldBool(Class, "bPassMobj", true);
3761 }
3762 else if (sc->Check("CannotPush"))
3763 {
3764 SetClassFieldBool(Class, "bCannotPush", true);
3765 }
3766 else if (sc->Check("ThruGhost"))
3767 {
3768 SetClassFieldBool(Class, "bThruGhost", true);
3769 }
3770 else if (sc->Check("NoDamageThrust"))
3771 {
3772 SetClassFieldBool(Class, "bNoDamageThrust", true);
3773 }
3774 else if (sc->Check("Telestomp"))
3775 {
3776 SetClassFieldBool(Class, "bTelestomp", true);
3777 }
3778 else if (sc->Check("FloatBob"))
3779 {
3780 SetClassFieldBool(Class, "bFloatBob", true);
3781 }
3782 else if (sc->Check("ActivateImpact"))
3783 {
3784 SetClassFieldBool(Class, "bActivateImpact", true);
3785 }
3786 else if (sc->Check("CanPushWalls"))
3787 {
3788 SetClassFieldBool(Class, "bActivatePushWall", true);
3789 }
3790 else if (sc->Check("ActivateMCross"))
3791 {
3792 SetClassFieldBool(Class, "bActivateMCross", true);
3793 }
3794 else if (sc->Check("ActivatePCross"))
3795 {
3796 SetClassFieldBool(Class, "bActivatePCross", true);
3797 }
3798 else if (sc->Check("Reflective"))
3799 {
3800 SetClassFieldBool(Class, "bReflective", true);
3801 }
3802 else if (sc->Check("FloorHugger"))
3803 {
3804 SetClassFieldBool(Class, "bIgnoreFloorStep", true);
3805 }
3806 else if (sc->Check("CeilingHugger"))
3807 {
3808 SetClassFieldBool(Class, "bIgnoreCeilingStep", true);
3809 }
3810 else if (sc->Check("DontSplash"))
3811 {
3812 SetClassFieldBool(Class, "bNoSplash", true);
3813 }
3814 else
3815 {
3816 Sys_Error("Unknown property %s", *sc->String);
3817 }
3818 }
3819
3820 if (SpawnEnd == 0)
3821 {
3822 sc->Error(va("%s has no Frames definition", *ClassName));
3823 }
3824 if (Type == OLDDEC_Breakable && DeathEnd == 0)
3825 {
3826 sc->Error(va("%s has no DeathFrames definition", *ClassName));
3827 }
3828 if (GenericIceDeath && IceEnd != 0)
3829 {
3830 sc->Error("IceDeathFrames and GenericIceDeath are mutually exclusive");
3831 }
3832
3833 if (DoomEdNum > 0)
3834 {
3835 mobjinfo_t& MI = VClass::GMobjInfos.Alloc();
3836 MI.Class = Class;
3837 MI.DoomEdNum = DoomEdNum;
3838 MI.GameFilter = GameFilter;
3839 }
3840 if (SpawnNum > 0)
3841 {
3842 mobjinfo_t& SI = VClass::GScriptIds.Alloc();
3843 SI.Class = Class;
3844 SI.DoomEdNum = SpawnNum;
3845 SI.GameFilter = GameFilter;
3846 }
3847
3848 // Set up linked list of states.
3849 Class->States = States[0];
3850 for (int i = 0; i < States.Num() - 1; i++)
3851 {
3852 States[i]->Next = States[i + 1];
3853 }
3854
3855 // Set up default sprite for all states.
3856 for (int i = 0; i < States.Num(); i++)
3857 {
3858 States[i]->SpriteName = Sprite;
3859 }
3860 // Set death sprite if it's defined.
3861 if (DeathSprite != NAME_None && DeathEnd != 0)
3862 {
3863 for (int i = DeathStart; i < DeathEnd; i++)
3864 {
3865 States[i]->SpriteName = DeathSprite;
3866 }
3867 }
3868
3869 // Set up links of spawn states.
3870 if (SpawnEnd - SpawnStart == 1)
3871 {
3872 States[SpawnStart]->Time = -1.0;
3873 }
3874 else
3875 {
3876 for (int i = SpawnStart; i < SpawnEnd - 1; i++)
3877 {
3878 States[i]->NextState = States[i + 1];
3879 }
3880 States[SpawnEnd - 1]->NextState = States[SpawnStart];
3881 }
3882 Class->SetStateLabel("Spawn", States[SpawnStart]);
3883
3884 // Set up links of death states.
3885 if (DeathEnd != 0)
3886 {
3887 for (int i = DeathStart; i < DeathEnd - 1; i++)
3888 {
3889 States[i]->NextState = States[i + 1];
3890 }
3891 if (!DiesAway && Type != OLDDEC_Projectile)
3892 {
3893 States[DeathEnd - 1]->Time = -1.0;
3894 }
3895 if (Type == OLDDEC_Projectile)
3896 {
3897 if (Explosive)
3898 {
3899 States[DeathStart]->Function = FuncA_ExplodeParms;
3900 }
3901 }
3902 else
3903 {
3904 // First death state plays death sound, second makes it
3905 // non-blocking unless it should stay solid.
3906 States[DeathStart]->Function = FuncA_Scream;
3907 if (!SolidOnDeath)
3908 {
3909 if (DeathEnd - DeathStart > 1)
3910 {
3911 States[DeathStart + 1]->Function = FuncA_NoBlocking;
3912 }
3913 else
3914 {
3915 States[DeathStart]->Function = FuncA_ScreamAndUnblock;
3916 }
3917 }
3918
3919 if (!DeathHeight)
3920 {
3921 DeathHeight = GetClassFieldFloat(Class, "Height");
3922 }
3923 SetClassFieldFloat(Class, "DeathHeight", DeathHeight);
3924 }
3925
3926 Class->SetStateLabel("Death", States[DeathStart]);
3927 }
3928
3929 // Set up links of burn death states.
3930 if (BurnEnd != 0)
3931 {
3932 for (int i = BurnStart; i < BurnEnd - 1; i++)
3933 {
3934 States[i]->NextState = States[i + 1];
3935 }
3936 if (!BurnsAway)
3937 {
3938 States[BurnEnd - 1]->Time = -1.0;
3939 }
3940 // First death state plays active sound, second makes it
3941 // non-blocking unless it should stay solid.
3942 States[BurnStart]->Function = FuncA_ActiveSound;
3943 if (!SolidOnBurn)
3944 {
3945 if (BurnEnd - BurnStart > 1)
3946 {
3947 States[BurnStart + 1]->Function = FuncA_NoBlocking;
3948 }
3949 else
3950 {
3951 States[BurnStart]->Function = FuncA_ActiveAndUnblock;
3952 }
3953 }
3954
3955 if (!BurnHeight)
3956 {
3957 BurnHeight = GetClassFieldFloat(Class, "Height");
3958 }
3959 SetClassFieldFloat(Class, "BurnHeight", BurnHeight);
3960
3961 TArray<VName> Names;
3962 Names.Append("Death");
3963 Names.Append("Fire");
3964 Class->SetStateLabel(Names, States[BurnStart]);
3965 }
3966
3967 // Set up links of ice death states.
3968 if (IceEnd != 0)
3969 {
3970 for (int i = IceStart; i < IceEnd - 1; i++)
3971 {
3972 States[i]->NextState = States[i + 1];
3973 }
3974
3975 States[IceEnd - 2]->Time = 5.0 / 35.0;
3976 States[IceEnd - 2]->Function = FuncA_FreezeDeath;
3977
3978 States[IceEnd - 1]->NextState = States[IceEnd - 1];
3979 States[IceEnd - 1]->Time = 1.0 / 35.0;
3980 States[IceEnd - 1]->Function = FuncA_FreezeDeathChunks;
3981
3982 TArray<VName> Names;
3983 Names.Append("Death");
3984 Names.Append("Ice");
3985 Class->SetStateLabel(Names, States[IceStart]);
3986 }
3987 else if (GenericIceDeath)
3988 {
3989 VStateLabel* Lbl = Class->FindStateLabel("GenericIceDeath");
3990 TArray<VName> Names;
3991 Names.Append("Death");
3992 Names.Append("Ice");
3993 Class->SetStateLabel(Names, Lbl ? Lbl->State : NULL);
3994 }
3995 unguard;
3996 }
3997
3998 //==========================================================================
3999 //
4000 // ParseDecorate
4001 //
4002 //==========================================================================
4003
ParseDecorate(VScriptParser * sc,TArray<VClassFixup> & ClassFixups)4004 static void ParseDecorate(VScriptParser* sc, TArray<VClassFixup>& ClassFixups)
4005 {
4006 guard(ParseDecorate);
4007 while (!sc->AtEnd())
4008 {
4009 if (sc->Check("#include"))
4010 {
4011 sc->ExpectString();
4012 int Lump = W_CheckNumForFileName(sc->String);
4013 // Check WAD lump only if it's no longer than 8 characters and
4014 // has no path separator.
4015 if (Lump < 0 && sc->String.Length() <= 8 &&
4016 sc->String.IndexOf('/') < 0)
4017 {
4018 Lump = W_CheckNumForName(VName(*sc->String, VName::AddLower8));
4019 }
4020 if (Lump < 0)
4021 {
4022 sc->Error(va("Lump %s not found", *sc->String));
4023 }
4024 ParseDecorate(new VScriptParser(sc->String,
4025 W_CreateLumpReaderNum(Lump)), ClassFixups);
4026 }
4027 else if (sc->Check("const"))
4028 {
4029 ParseConst(sc);
4030 }
4031 else if (sc->Check("enum"))
4032 {
4033 ParseEnum(sc);
4034 }
4035 else if (sc->Check("class"))
4036 {
4037 ParseClass(sc);
4038 }
4039 else if (sc->Check("actor"))
4040 {
4041 ParseActor(sc, ClassFixups);
4042 }
4043 else if (sc->Check("breakable"))
4044 {
4045 ParseOldDecoration(sc, OLDDEC_Breakable);
4046 }
4047 else if (sc->Check("pickup"))
4048 {
4049 ParseOldDecoration(sc, OLDDEC_Pickup);
4050 }
4051 else if (sc->Check("projectile"))
4052 {
4053 ParseOldDecoration(sc, OLDDEC_Projectile);
4054 }
4055 else
4056 {
4057 ParseOldDecoration(sc, OLDDEC_Decoration);
4058 }
4059 }
4060 delete sc;
4061 sc = NULL;
4062 unguard;
4063 }
4064
4065 //==========================================================================
4066 //
4067 // ReadLineSpecialInfos
4068 //
4069 //==========================================================================
4070
ReadLineSpecialInfos()4071 void ReadLineSpecialInfos()
4072 {
4073 guard(ReadLineSpecialInfos);
4074 VStream* Strm = FL_OpenFileRead("line_specials.txt");
4075 check(Strm);
4076 VScriptParser* sc = new VScriptParser("line_specials.txt", Strm);
4077 while (!sc->AtEnd())
4078 {
4079 VLineSpecInfo& I = LineSpecialInfos.Alloc();
4080 sc->ExpectNumber();
4081 I.Number = sc->Number;
4082 sc->ExpectString();
4083 I.Name = sc->String.ToLower();
4084 }
4085 delete sc;
4086 sc = NULL;
4087 unguard;
4088 }
4089
4090 //==========================================================================
4091 //
4092 // ProcessDecorateScripts
4093 //
4094 //==========================================================================
4095
ProcessDecorateScripts()4096 void ProcessDecorateScripts()
4097 {
4098 guard(ProcessDecorateScripts);
4099 GCon->Logf(NAME_Init, "Parsing DECORATE definition files");
4100 for (int Lump = W_IterateFile(-1, "vavoom_decorate_defs.xml"); Lump != -1;
4101 Lump = W_IterateFile(Lump, "vavoom_decorate_defs.xml"))
4102 {
4103 VStream* Strm = W_CreateLumpReaderNum(Lump);
4104 check(Strm);
4105 VXmlDocument* Doc = new VXmlDocument();
4106 Doc->Parse(*Strm, "vavoom_decorate_defs.xml");
4107 delete Strm;
4108 Strm = NULL;
4109 ParseDecorateDef(*Doc);
4110 delete Doc;
4111 Doc = NULL;
4112 }
4113
4114 GCon->Logf(NAME_Init, "Processing DECORATE scripts");
4115
4116 DecPkg = new VPackage(NAME_decorate);
4117
4118 // Find classes.
4119 ActorClass = VClass::FindClass("Actor");
4120 FakeInventoryClass = VClass::FindClass("FakeInventory");
4121 InventoryClass = VClass::FindClass("Inventory");
4122 AmmoClass = VClass::FindClass("Ammo");
4123 BasicArmorPickupClass = VClass::FindClass("BasicArmorPickup");
4124 BasicArmorBonusClass = VClass::FindClass("BasicArmorBonus");
4125 HealthClass = VClass::FindClass("Health");
4126 PowerupGiverClass = VClass::FindClass("PowerupGiver");
4127 PuzzleItemClass = VClass::FindClass("PuzzleItem");
4128 WeaponClass = VClass::FindClass("Weapon");
4129 WeaponPieceClass = VClass::FindClass("WeaponPiece");
4130 PlayerPawnClass = VClass::FindClass("PlayerPawn");
4131 MorphProjectileClass = VClass::FindClass("MorphProjectile");
4132
4133 // Find methods used by old style decorations.
4134 FuncA_Scream = ActorClass->FindMethodChecked("A_Scream");
4135 FuncA_NoBlocking = ActorClass->FindMethodChecked("A_NoBlocking");
4136 FuncA_ScreamAndUnblock = ActorClass->FindMethodChecked("A_ScreamAndUnblock");
4137 FuncA_ActiveSound = ActorClass->FindMethodChecked("A_ActiveSound");
4138 FuncA_ActiveAndUnblock = ActorClass->FindMethodChecked("A_ActiveAndUnblock");
4139 FuncA_ExplodeParms = ActorClass->FindMethodChecked("A_ExplodeParms");
4140 FuncA_FreezeDeath = ActorClass->FindMethodChecked("A_FreezeDeath");
4141 FuncA_FreezeDeathChunks = ActorClass->FindMethodChecked("A_FreezeDeathChunks");
4142
4143 // Parse scripts.
4144 TArray<VClassFixup> ClassFixups;
4145 for (int Lump = W_IterateNS(-1, WADNS_Global); Lump >= 0;
4146 Lump = W_IterateNS(Lump, WADNS_Global))
4147 {
4148 if (W_LumpName(Lump) == NAME_decorate)
4149 {
4150 ParseDecorate(new VScriptParser(*W_LumpName(Lump),
4151 W_CreateLumpReaderNum(Lump)), ClassFixups);
4152 }
4153 }
4154
4155 // Make sure all import classes were defined.
4156 if (VMemberBase::GDecorateClassImports.Num())
4157 {
4158 for (int i = 0; i < VMemberBase::GDecorateClassImports.Num(); i++)
4159 {
4160 GCon->Logf("Undefined DECORATE class %s",
4161 VMemberBase::GDecorateClassImports[i]->GetName());
4162 }
4163 Sys_Error("Not all DECORATE class imports were defined");
4164 }
4165
4166 GCon->Logf(NAME_Init, "Post-procesing");
4167
4168 // Set class properties.
4169 for (int i = 0; i < ClassFixups.Num(); i++)
4170 {
4171 VClassFixup& CF = ClassFixups[i];
4172 check(CF.ReqParent);
4173 if (!CF.Name.ICmp("None"))
4174 {
4175 *(VClass**)(CF.Class->Defaults + CF.Offset) = NULL;
4176 }
4177 else
4178 {
4179 VClass* C = VClass::FindClassLowerCase(*CF.Name.ToLower());
4180 if (!C)
4181 {
4182 GCon->Logf("No such class %s", *CF.Name);
4183 }
4184 else if (!C->IsChildOf(CF.ReqParent))
4185 {
4186 GCon->Logf("Class %s is not a descendant of %s",
4187 *CF.Name, CF.ReqParent->GetName());
4188 }
4189 else
4190 {
4191 *(VClass**)(CF.Class->Defaults + CF.Offset) = C;
4192 }
4193 }
4194 }
4195 VField* DropItemListField = ActorClass->FindFieldChecked("DropItemList");
4196 for (int i = 0; i < DecPkg->ParsedClasses.Num(); i++)
4197 {
4198 TArray<VDropItemInfo>& List =
4199 *(TArray<VDropItemInfo>*)DropItemListField->GetFieldPtr(
4200 (VObject*)DecPkg->ParsedClasses[i]->Defaults);
4201 for (int j = 0; j < List.Num(); j++)
4202 {
4203 VDropItemInfo& DI = List[j];
4204 if (DI.TypeName == NAME_None)
4205 {
4206 continue;
4207 }
4208 VClass* C = VClass::FindClassLowerCase(DI.TypeName);
4209 if (!C)
4210 {
4211 GCon->Logf("No such class %s", *DI.TypeName);
4212 }
4213 else if (!C->IsChildOf(ActorClass))
4214 {
4215 GCon->Logf("Class %s is not an actor class", *DI.TypeName);
4216 }
4217 else
4218 {
4219 DI.Type = C;
4220 }
4221 }
4222 }
4223
4224 // Emit code.
4225 for (int i = 0; i < DecPkg->ParsedClasses.Num(); i++)
4226 {
4227 if (GArgs.CheckParm("-debug_decorate"))
4228 {
4229 GCon->Logf("Emiting Class %s", *DecPkg->ParsedClasses[i]->GetFullName());
4230 }
4231 DecPkg->ParsedClasses[i]->DecorateEmit();
4232 }
4233 // Compile and set up for execution.
4234 for (int i = 0; i < DecPkg->ParsedClasses.Num(); i++)
4235 {
4236 if (GArgs.CheckParm("-debug_decorate"))
4237 {
4238 GCon->Logf("Compiling Class %s", *DecPkg->ParsedClasses[i]->GetFullName());
4239 }
4240 DecPkg->ParsedClasses[i]->DecoratePostLoad();
4241 }
4242
4243 if (NumErrors)
4244 {
4245 BailOut();
4246 }
4247
4248 VClass::StaticReinitStatesLookup();
4249
4250 TLocation::ClearSourceFiles();
4251 unguard;
4252 }
4253
4254 //==========================================================================
4255 //
4256 // ShutdownDecorate
4257 //
4258 //==========================================================================
4259
ShutdownDecorate()4260 void ShutdownDecorate()
4261 {
4262 guard(ShutdownDecorate);
4263 FlagList.Clear();
4264 LineSpecialInfos.Clear();
4265 unguard;
4266 }
4267
4268 //==========================================================================
4269 //
4270 // VEntity::SetDecorateFlag
4271 //
4272 //==========================================================================
4273
SetDecorateFlag(const VStr & Flag,bool Value)4274 void VEntity::SetDecorateFlag(const VStr& Flag, bool Value)
4275 {
4276 guard(VEntity::SetDecorateFlag);
4277 VName FlagName;
4278 VName ClassFilter(NAME_None);
4279 int DotPos = Flag.IndexOf('.');
4280 if (DotPos >= 0)
4281 {
4282 ClassFilter = *VStr(Flag, 0, DotPos).ToLower();
4283 FlagName = *VStr(Flag, DotPos + 1, Flag.Length() - DotPos - 1).ToLower();
4284 }
4285 else
4286 {
4287 FlagName = *Flag.ToLower();
4288 }
4289 for (int j = 0; j < FlagList.Num(); j++)
4290 {
4291 VFlagList& ClassDef = FlagList[j];
4292 if (ClassFilter != NAME_None &&
4293 ClassDef.Class->LowerCaseName != ClassFilter)
4294 {
4295 continue;
4296 }
4297 if (!IsA(ClassDef.Class))
4298 {
4299 continue;
4300 }
4301 for (int i = ClassDef.FlagsHash[GetTypeHash(FlagName) &
4302 (FLAGS_HASH_SIZE - 1)]; i != -1; i = ClassDef.Flags[i].HashNext)
4303 {
4304 const VFlagDef& F = ClassDef.Flags[i];
4305 if (FlagName == F.Name)
4306 {
4307 switch (F.Type)
4308 {
4309 case FLAG_Bool:
4310 F.Field->SetBool(this, Value);
4311 break;
4312 case FLAG_Unsupported:
4313 GCon->Logf("Unsupported flag %s in %s", *Flag,
4314 GetClass()->GetName());
4315 break;
4316 case FLAG_Byte:
4317 F.Field->SetByte(this, Value ? F.BTrue : F.BFalse);
4318 break;
4319 case FLAG_Float:
4320 F.Field->SetFloat(this, Value ? F.FTrue : F.FFalse);
4321 break;
4322 case FLAG_Name:
4323 F.Field->SetName(this, Value ? F.NTrue : F.NFalse);
4324 break;
4325 case FLAG_Class:
4326 F.Field->SetClass(this, Value ?
4327 F.NTrue != NAME_None ? VClass::FindClass(*F.NTrue) : NULL :
4328 F.NFalse != NAME_None ? VClass::FindClass(*F.NFalse) : NULL);
4329 break;
4330 case FLAG_NoClip:
4331 F.Field->SetBool(this, !Value);
4332 F.Field2->SetBool(this, !Value);
4333 break;
4334 }
4335 return;
4336 }
4337 }
4338 }
4339 GCon->Logf("Unknown flag %s", *Flag);
4340 unguard;
4341 }
4342