1 /*
2 ** p_states.cpp
3 ** state management
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2008 Randy Heit
7 ** Copyright 2006-2008 Christoph Oelckers
8 ** All rights reserved.
9 **
10 ** Redistribution and use in source and binary forms, with or without
11 ** modification, are permitted provided that the following conditions
12 ** are met:
13 **
14 ** 1. Redistributions of source code must retain the above copyright
15 ** notice, this list of conditions and the following disclaimer.
16 ** 2. Redistributions in binary form must reproduce the above copyright
17 ** notice, this list of conditions and the following disclaimer in the
18 ** documentation and/or other materials provided with the distribution.
19 ** 3. The name of the author may not be used to endorse or promote products
20 ** derived from this software without specific prior written permission.
21 **
22 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 **---------------------------------------------------------------------------
33 **
34 **
35 */
36 #include "actor.h"
37 #include "farchive.h"
38 #include "templates.h"
39 #include "cmdlib.h"
40 #include "i_system.h"
41 #include "c_dispatch.h"
42 #include "v_text.h"
43 #include "thingdef/thingdef.h"
44
45 // Each state is owned by an actor. Actors can own any number of
46 // states, but a single state cannot be owned by more than one
47 // actor. States are archived by recording the actor they belong
48 // to and the index into that actor's list of states.
49
50 // For NULL states, which aren't owned by any actor, the owner
51 // is recorded as AActor with the following state. AActor should
52 // never actually have this many states of its own, so this
53 // is (relatively) safe.
54
55 #define NULL_STATE_INDEX 127
56
57 //==========================================================================
58 //
59 //
60 //==========================================================================
61
operator <<(FArchive & arc,FState * & state)62 FArchive &operator<< (FArchive &arc, FState *&state)
63 {
64 const PClass *info;
65
66 if (arc.IsStoring ())
67 {
68 if (state == NULL)
69 {
70 arc.UserWriteClass (RUNTIME_CLASS(AActor));
71 arc.WriteCount (NULL_STATE_INDEX);
72 return arc;
73 }
74
75 info = FState::StaticFindStateOwner (state);
76
77 if (info != NULL)
78 {
79 arc.UserWriteClass (info);
80 arc.WriteCount ((DWORD)(state - info->ActorInfo->OwnedStates));
81 }
82 else
83 {
84 /* this was never working as intended.
85 I_Error ("Cannot find owner for state %p:\n"
86 "%s %c%c %3d [%p] -> %p", state,
87 sprites[state->sprite].name,
88 state->GetFrame() + 'A',
89 state->GetFullbright() ? '*' : ' ',
90 state->GetTics(),
91 state->GetAction(),
92 state->GetNextState());
93 */
94 }
95 }
96 else
97 {
98 const PClass *info;
99 DWORD ofs;
100
101 arc.UserReadClass (info);
102 ofs = arc.ReadCount ();
103 if (ofs == NULL_STATE_INDEX && info == RUNTIME_CLASS(AActor))
104 {
105 state = NULL;
106 }
107 else if (info->ActorInfo != NULL)
108 {
109 state = info->ActorInfo->OwnedStates + ofs;
110 }
111 else
112 {
113 state = NULL;
114 }
115 }
116 return arc;
117 }
118
119 //==========================================================================
120 //
121 // Find the actor that a state belongs to.
122 //
123 //==========================================================================
124
StaticFindStateOwner(const FState * state)125 const PClass *FState::StaticFindStateOwner (const FState *state)
126 {
127 for (unsigned int i = 0; i < PClass::m_RuntimeActors.Size(); ++i)
128 {
129 FActorInfo *info = PClass::m_RuntimeActors[i]->ActorInfo;
130 if (state >= info->OwnedStates &&
131 state < info->OwnedStates + info->NumOwnedStates)
132 {
133 return info->Class;
134 }
135 }
136
137 return NULL;
138 }
139
140 //==========================================================================
141 //
142 // Find the actor that a state belongs to, but restrict the search to
143 // the specified type and its ancestors.
144 //
145 //==========================================================================
146
StaticFindStateOwner(const FState * state,const FActorInfo * info)147 const PClass *FState::StaticFindStateOwner (const FState *state, const FActorInfo *info)
148 {
149 while (info != NULL)
150 {
151 if (state >= info->OwnedStates &&
152 state < info->OwnedStates + info->NumOwnedStates)
153 {
154 return info->Class;
155 }
156 info = info->Class->ParentClass->ActorInfo;
157 }
158 return NULL;
159 }
160
161
162 //==========================================================================
163 //
164 //
165 //==========================================================================
166
FindLabel(FName label)167 FStateLabel *FStateLabels::FindLabel (FName label)
168 {
169 return const_cast<FStateLabel *>(BinarySearch<FStateLabel, FName> (Labels, NumLabels, &FStateLabel::Label, label));
170 }
171
Destroy()172 void FStateLabels::Destroy ()
173 {
174 for(int i=0; i<NumLabels;i++)
175 {
176 if (Labels[i].Children != NULL)
177 {
178 Labels[i].Children->Destroy();
179 free (Labels[i].Children); // These are malloc'd, not new'd!
180 Labels[i].Children=NULL;
181 }
182 }
183 }
184
185
186 //===========================================================================
187 //
188 // HasStates
189 //
190 // Checks whether the actor has special death states.
191 //
192 //===========================================================================
193
HasSpecialDeathStates() const194 bool AActor::HasSpecialDeathStates () const
195 {
196 const FActorInfo *info = GetClass()->ActorInfo;
197
198 if (info->StateList != NULL)
199 {
200 FStateLabel *slabel = info->StateList->FindLabel (NAME_Death);
201 if (slabel != NULL && slabel->Children != NULL)
202 {
203 for(int i=0;i<slabel->Children->NumLabels;i++)
204 {
205 if (slabel->Children->Labels[i].State != NULL) return true;
206 }
207 }
208 }
209 return false;
210 }
211
212 //==========================================================================
213 //
214 // Creates a list of names from a string. Dots are used as separator
215 //
216 //==========================================================================
217
MakeStateNameList(const char * fname)218 TArray<FName> &MakeStateNameList(const char * fname)
219 {
220 static TArray<FName> namelist(3);
221 FName firstpart, secondpart;
222 char * c;
223
224 // Handle the old names for the existing death states
225 char * name = copystring(fname);
226 firstpart = strtok(name, ".");
227 switch (firstpart)
228 {
229 case NAME_Burn:
230 firstpart = NAME_Death;
231 secondpart = NAME_Fire;
232 break;
233 case NAME_Ice:
234 firstpart = NAME_Death;
235 secondpart = NAME_Ice;
236 break;
237 case NAME_Disintegrate:
238 firstpart = NAME_Death;
239 secondpart = NAME_Disintegrate;
240 break;
241 case NAME_XDeath:
242 firstpart = NAME_Death;
243 secondpart = NAME_Extreme;
244 break;
245 }
246
247 namelist.Clear();
248 namelist.Push(firstpart);
249 if (secondpart!=NAME_None) namelist.Push(secondpart);
250
251 while ((c = strtok(NULL, "."))!=NULL)
252 {
253 FName cc = c;
254 namelist.Push(cc);
255 }
256 delete [] name;
257 return namelist;
258 }
259
260 //===========================================================================
261 //
262 // FindState (multiple names version)
263 //
264 // Finds a state that matches as many of the supplied names as possible.
265 // A state with more names than those provided does not match.
266 // A state with fewer names can match if there are no states with the exact
267 // same number of names.
268 //
269 // The search proceeds like this. For the current class, keeping matching
270 // names until there are no more. If both the argument list and the state
271 // are out of names, it's an exact match, so return it. If the state still
272 // has names, ignore it. If the argument list still has names, remember it.
273 //
274 //===========================================================================
FindState(int numnames,FName * names,bool exact) const275 FState *FActorInfo::FindState (int numnames, FName *names, bool exact) const
276 {
277 FStateLabels *labels = StateList;
278 FState *best = NULL;
279
280 if (labels != NULL)
281 {
282 int count = 0;
283 FStateLabel *slabel = NULL;
284 FName label;
285
286 // Find the best-matching label for this class.
287 while (labels != NULL && count < numnames)
288 {
289 label = *names++;
290 slabel = labels->FindLabel (label);
291
292 if (slabel != NULL)
293 {
294 count++;
295 labels = slabel->Children;
296 best = slabel->State;
297 }
298 else
299 {
300 break;
301 }
302 }
303 if (count < numnames && exact) return NULL;
304 }
305 return best;
306 }
307
308 //==========================================================================
309 //
310 // Finds the state associated with the given string
311 //
312 //==========================================================================
313
FindStateByString(const char * name,bool exact)314 FState *FActorInfo::FindStateByString(const char *name, bool exact)
315 {
316 TArray<FName> &namelist = MakeStateNameList(name);
317 return FindState(namelist.Size(), &namelist[0], exact);
318 }
319
320
321
322
323 //==========================================================================
324 //
325 // Search one list of state definitions for the given name
326 //
327 //==========================================================================
328
FindStateLabelInList(TArray<FStateDefine> & list,FName name,bool create)329 FStateDefine *FStateDefinitions::FindStateLabelInList(TArray<FStateDefine> & list, FName name, bool create)
330 {
331 for(unsigned i = 0; i<list.Size(); i++)
332 {
333 if (list[i].Label == name) return &list[i];
334 }
335 if (create)
336 {
337 FStateDefine def;
338 def.Label = name;
339 def.State = NULL;
340 def.DefineFlags = SDF_NEXT;
341 return &list[list.Push(def)];
342 }
343 return NULL;
344 }
345
346 //==========================================================================
347 //
348 // Finds the address of a state label given by name.
349 // Adds the state label if it doesn't exist
350 //
351 //==========================================================================
352
FindStateAddress(const char * name)353 FStateDefine * FStateDefinitions::FindStateAddress(const char *name)
354 {
355 FStateDefine *statedef = NULL;
356 TArray<FName> &namelist = MakeStateNameList(name);
357 TArray<FStateDefine> *statelist = &StateLabels;
358
359 for(unsigned i = 0; i < namelist.Size(); i++)
360 {
361 statedef = FindStateLabelInList(*statelist, namelist[i], true);
362 statelist = &statedef->Children;
363 }
364 return statedef;
365 }
366
367 //==========================================================================
368 //
369 // Adds a new state to the curremt list
370 //
371 //==========================================================================
372
SetStateLabel(const char * statename,FState * state,BYTE defflags)373 void FStateDefinitions::SetStateLabel (const char *statename, FState *state, BYTE defflags)
374 {
375 FStateDefine *std = FindStateAddress(statename);
376 std->State = state;
377 std->DefineFlags = defflags;
378 }
379
380 //==========================================================================
381 //
382 // Adds a new state to the current list
383 //
384 //==========================================================================
385
AddStateLabel(const char * statename)386 void FStateDefinitions::AddStateLabel (const char *statename)
387 {
388 intptr_t index = StateArray.Size();
389 FStateDefine *std = FindStateAddress(statename);
390 std->State = (FState *)(index+1);
391 std->DefineFlags = SDF_INDEX;
392 laststate = NULL;
393 lastlabel = index;
394 }
395
396 //==========================================================================
397 //
398 // Returns the index a state label points to. May only be called before
399 // installing states.
400 //
401 //==========================================================================
402
GetStateLabelIndex(FName statename)403 int FStateDefinitions::GetStateLabelIndex (FName statename)
404 {
405 FStateDefine *std = FindStateLabelInList(StateLabels, statename, false);
406 if (std == NULL)
407 {
408 return -1;
409 }
410 assert((size_t)std->State <= StateArray.Size() + 1);
411 return (int)((ptrdiff_t)std->State - 1);
412 }
413
414 //==========================================================================
415 //
416 // Finds the state associated with the given name
417 // returns NULL if none found
418 //
419 //==========================================================================
420
FindState(const char * name)421 FState * FStateDefinitions::FindState(const char * name)
422 {
423 FStateDefine * statedef=NULL;
424
425 TArray<FName> &namelist = MakeStateNameList(name);
426
427 TArray<FStateDefine> * statelist = &StateLabels;
428 for(unsigned i=0;i<namelist.Size();i++)
429 {
430 statedef = FindStateLabelInList(*statelist, namelist[i], false);
431 if (statedef == NULL) return NULL;
432 statelist = &statedef->Children;
433 }
434 return statedef? statedef->State : NULL;
435 }
436
437 //==========================================================================
438 //
439 // Creates the final list of states from the state definitions
440 //
441 //==========================================================================
442
labelcmp(const void * a,const void * b)443 static int STACK_ARGS labelcmp(const void * a, const void * b)
444 {
445 FStateLabel * A = (FStateLabel *)a;
446 FStateLabel * B = (FStateLabel *)b;
447 return ((int)A->Label - (int)B->Label);
448 }
449
CreateStateLabelList(TArray<FStateDefine> & statelist)450 FStateLabels * FStateDefinitions::CreateStateLabelList(TArray<FStateDefine> & statelist)
451 {
452 // First delete all empty labels from the list
453 for (int i=statelist.Size()-1;i>=0;i--)
454 {
455 if (statelist[i].Label == NAME_None || (statelist[i].State == NULL && statelist[i].Children.Size() == 0))
456 {
457 statelist.Delete(i);
458 }
459 }
460
461 int count=statelist.Size();
462
463 if (count == 0) return NULL;
464
465 FStateLabels * list = (FStateLabels*)M_Malloc(sizeof(FStateLabels)+(count-1)*sizeof(FStateLabel));
466 list->NumLabels = count;
467
468 for (int i=0;i<count;i++)
469 {
470 list->Labels[i].Label = statelist[i].Label;
471 list->Labels[i].State = statelist[i].State;
472 list->Labels[i].Children = CreateStateLabelList(statelist[i].Children);
473 }
474 qsort(list->Labels, count, sizeof(FStateLabel), labelcmp);
475 return list;
476 }
477
478 //===========================================================================
479 //
480 // InstallStates
481 //
482 // Creates the actor's state list from the current definition
483 //
484 //===========================================================================
485
InstallStates(FActorInfo * info,AActor * defaults)486 void FStateDefinitions::InstallStates(FActorInfo *info, AActor *defaults)
487 {
488 // First ensure we have a valid spawn state.
489 FState *state = FindState("Spawn");
490
491 if (state == NULL)
492 {
493 // A NULL spawn state will crash the engine so set it to something valid.
494 SetStateLabel("Spawn", GetDefault<AActor>()->SpawnState);
495 }
496
497 if (info->StateList != NULL)
498 {
499 info->StateList->Destroy();
500 M_Free(info->StateList);
501 }
502 info->StateList = CreateStateLabelList(StateLabels);
503
504 // Cache these states as member veriables.
505 defaults->SpawnState = info->FindState(NAME_Spawn);
506 defaults->SeeState = info->FindState(NAME_See);
507 // Melee and Missile states are manipulated by the scripted marines so they
508 // have to be stored locally
509 defaults->MeleeState = info->FindState(NAME_Melee);
510 defaults->MissileState = info->FindState(NAME_Missile);
511 }
512
513 //===========================================================================
514 //
515 // MakeStateDefines
516 //
517 // Creates a list of state definitions from an existing actor
518 // Used by Dehacked to modify an actor's state list
519 //
520 //===========================================================================
521
MakeStateList(const FStateLabels * list,TArray<FStateDefine> & dest)522 void FStateDefinitions::MakeStateList(const FStateLabels *list, TArray<FStateDefine> &dest)
523 {
524 dest.Clear();
525 if (list != NULL) for(int i=0;i<list->NumLabels;i++)
526 {
527 FStateDefine def;
528
529 def.Label = list->Labels[i].Label;
530 def.State = list->Labels[i].State;
531 def.DefineFlags = SDF_STATE;
532 dest.Push(def);
533 if (list->Labels[i].Children != NULL)
534 {
535 MakeStateList(list->Labels[i].Children, dest[dest.Size()-1].Children);
536 }
537 }
538 }
539
MakeStateDefines(const PClass * cls)540 void FStateDefinitions::MakeStateDefines(const PClass *cls)
541 {
542 StateArray.Clear();
543 laststate = NULL;
544 laststatebeforelabel = NULL;
545 lastlabel = -1;
546
547 if (cls != NULL && cls->ActorInfo != NULL && cls->ActorInfo->StateList != NULL)
548 {
549 MakeStateList(cls->ActorInfo->StateList, StateLabels);
550 }
551 else
552 {
553 StateLabels.Clear();
554 }
555 }
556
557 //===========================================================================
558 //
559 // AddStateDefines
560 //
561 // Adds a list of states to the current definitions
562 //
563 //===========================================================================
564
AddStateDefines(const FStateLabels * list)565 void FStateDefinitions::AddStateDefines(const FStateLabels *list)
566 {
567 if (list != NULL) for(int i=0;i<list->NumLabels;i++)
568 {
569 if (list->Labels[i].Children == NULL)
570 {
571 if (!FindStateLabelInList(StateLabels, list->Labels[i].Label, false))
572 {
573 FStateDefine def;
574
575 def.Label = list->Labels[i].Label;
576 def.State = list->Labels[i].State;
577 def.DefineFlags = SDF_STATE;
578 StateLabels.Push(def);
579 }
580 }
581 }
582 }
583
584 //==========================================================================
585 //
586 // RetargetState(Pointer)s
587 //
588 // These functions are used when a goto follows one or more labels.
589 // Because multiple labels are permitted to occur consecutively with no
590 // intervening states, it is not enough to remember the last label defined
591 // and adjust it. So these functions search for all labels that point to
592 // the current position in the state array and give them a copy of the
593 // target string instead.
594 //
595 //==========================================================================
596
RetargetStatePointers(intptr_t count,const char * target,TArray<FStateDefine> & statelist)597 void FStateDefinitions::RetargetStatePointers (intptr_t count, const char *target, TArray<FStateDefine> & statelist)
598 {
599 for(unsigned i = 0;i<statelist.Size(); i++)
600 {
601 if (statelist[i].State == (FState*)count && statelist[i].DefineFlags == SDF_INDEX)
602 {
603 if (target == NULL)
604 {
605 statelist[i].State = NULL;
606 statelist[i].DefineFlags = SDF_STOP;
607 }
608 else
609 {
610 statelist[i].State = (FState *)copystring (target);
611 statelist[i].DefineFlags = SDF_LABEL;
612 }
613 }
614 if (statelist[i].Children.Size() > 0)
615 {
616 RetargetStatePointers(count, target, statelist[i].Children);
617 }
618 }
619 }
620
RetargetStates(intptr_t count,const char * target)621 void FStateDefinitions::RetargetStates (intptr_t count, const char *target)
622 {
623 RetargetStatePointers(count, target, StateLabels);
624 }
625
626
627 //==========================================================================
628 //
629 // ResolveGotoLabel
630 //
631 // Resolves any strings being stored in a state's NextState field
632 //
633 //==========================================================================
634
ResolveGotoLabel(AActor * actor,const PClass * mytype,char * name)635 FState *FStateDefinitions::ResolveGotoLabel (AActor *actor, const PClass *mytype, char *name)
636 {
637 const PClass *type=mytype;
638 FState *state;
639 char *namestart = name;
640 char *label, *offset, *pt;
641 int v;
642
643 // Check for classname
644 if ((pt = strstr (name, "::")) != NULL)
645 {
646 const char *classname = name;
647 *pt = '\0';
648 name = pt + 2;
649
650 // The classname may either be "Super" to identify this class's immediate
651 // superclass, or it may be the name of any class that this one derives from.
652 if (stricmp (classname, "Super") == 0)
653 {
654 type = type->ParentClass;
655 actor = GetDefaultByType (type);
656 }
657 else
658 {
659 // first check whether a state of the desired name exists
660 const PClass *stype = PClass::FindClass (classname);
661 if (stype == NULL)
662 {
663 I_Error ("%s is an unknown class.", classname);
664 }
665 if (!stype->IsDescendantOf (RUNTIME_CLASS(AActor)))
666 {
667 I_Error ("%s is not an actor class, so it has no states.", stype->TypeName.GetChars());
668 }
669 if (!stype->IsAncestorOf (type))
670 {
671 I_Error ("%s is not derived from %s so cannot access its states.",
672 type->TypeName.GetChars(), stype->TypeName.GetChars());
673 }
674 if (type != stype)
675 {
676 type = stype;
677 actor = GetDefaultByType (type);
678 }
679 }
680 }
681 label = name;
682 // Check for offset
683 offset = NULL;
684 if ((pt = strchr (name, '+')) != NULL)
685 {
686 *pt = '\0';
687 offset = pt + 1;
688 }
689 v = offset ? strtol (offset, NULL, 0) : 0;
690
691 // Get the state's address.
692 if (type==mytype) state = FindState (label);
693 else state = type->ActorInfo->FindStateByString(label, true);
694
695 if (state != NULL)
696 {
697 state += v;
698 }
699 else if (v != 0)
700 {
701 I_Error ("Attempt to get invalid state %s from actor %s.", label, type->TypeName.GetChars());
702 }
703 else
704 {
705 Printf (TEXTCOLOR_RED "Attempt to get invalid state %s from actor %s.\n", label, type->TypeName.GetChars());
706 }
707 delete[] namestart; // free the allocated string buffer
708 return state;
709 }
710
711 //==========================================================================
712 //
713 // FixStatePointers
714 //
715 // Fixes an actor's default state pointers.
716 //
717 //==========================================================================
718
FixStatePointers(FActorInfo * actor,TArray<FStateDefine> & list)719 void FStateDefinitions::FixStatePointers (FActorInfo *actor, TArray<FStateDefine> & list)
720 {
721 for(unsigned i=0;i<list.Size(); i++)
722 {
723 if (list[i].DefineFlags == SDF_INDEX)
724 {
725 size_t v=(size_t)list[i].State;
726 list[i].State = actor->OwnedStates + v - 1;
727 list[i].DefineFlags = SDF_STATE;
728 }
729 if (list[i].Children.Size() > 0) FixStatePointers(actor, list[i].Children);
730 }
731 }
732
733 //==========================================================================
734 //
735 // ResolveGotoLabels
736 //
737 // Resolves an actor's state pointers that were specified as jumps.
738 //
739 //==========================================================================
740
ResolveGotoLabels(FActorInfo * actor,AActor * defaults,TArray<FStateDefine> & list)741 void FStateDefinitions::ResolveGotoLabels (FActorInfo *actor, AActor *defaults, TArray<FStateDefine> & list)
742 {
743 for(unsigned i=0;i<list.Size(); i++)
744 {
745 if (list[i].State != NULL && list[i].DefineFlags == SDF_LABEL)
746 { // It's not a valid state, so it must be a label string. Resolve it.
747 list[i].State = ResolveGotoLabel (defaults, actor->Class, (char *)list[i].State);
748 list[i].DefineFlags = SDF_STATE;
749 }
750 if (list[i].Children.Size() > 0) ResolveGotoLabels(actor, defaults, list[i].Children);
751 }
752 }
753
754
755 //==========================================================================
756 //
757 // SetGotoLabel
758 //
759 // sets a jump at the current state or retargets a label
760 //
761 //==========================================================================
762
SetGotoLabel(const char * string)763 bool FStateDefinitions::SetGotoLabel(const char *string)
764 {
765 // copy the text - this must be resolved later!
766 if (laststate != NULL)
767 { // Following a state definition: Modify it.
768 laststate->NextState = (FState*)copystring(string);
769 laststate->DefineFlags = SDF_LABEL;
770 laststatebeforelabel = NULL;
771 return true;
772 }
773 else if (lastlabel >= 0)
774 { // Following a label: Retarget it.
775 RetargetStates (lastlabel+1, string);
776 if (laststatebeforelabel != NULL)
777 {
778 laststatebeforelabel->NextState = (FState*)copystring(string);
779 laststatebeforelabel->DefineFlags = SDF_LABEL;
780 laststatebeforelabel = NULL;
781 }
782 return true;
783 }
784 return false;
785 }
786
787 //==========================================================================
788 //
789 // SetStop
790 //
791 // sets a stop operation
792 //
793 //==========================================================================
794
SetStop()795 bool FStateDefinitions::SetStop()
796 {
797 if (laststate != NULL)
798 {
799 laststate->DefineFlags = SDF_STOP;
800 laststatebeforelabel = NULL;
801 return true;
802 }
803 else if (lastlabel >=0)
804 {
805 RetargetStates (lastlabel+1, NULL);
806 if (laststatebeforelabel != NULL)
807 {
808 laststatebeforelabel->DefineFlags = SDF_STOP;
809 laststatebeforelabel = NULL;
810 }
811 return true;
812 }
813 return false;
814 }
815
816 //==========================================================================
817 //
818 // SetWait
819 //
820 // sets a wait or fail operation
821 //
822 //==========================================================================
823
SetWait()824 bool FStateDefinitions::SetWait()
825 {
826 if (laststate != NULL)
827 {
828 laststate->DefineFlags = SDF_WAIT;
829 laststatebeforelabel = NULL;
830 return true;
831 }
832 return false;
833 }
834
835 //==========================================================================
836 //
837 // SetLoop
838 //
839 // sets a loop operation
840 //
841 //==========================================================================
842
SetLoop()843 bool FStateDefinitions::SetLoop()
844 {
845 if (laststate != NULL)
846 {
847 laststate->DefineFlags = SDF_INDEX;
848 laststate->NextState = (FState*)(lastlabel+1);
849 laststatebeforelabel = NULL;
850 return true;
851 }
852 return false;
853 }
854
855 //==========================================================================
856 //
857 // AddStates
858 // adds some state to the current definition set
859 //
860 //==========================================================================
861
AddStates(FState * state,const char * framechars)862 bool FStateDefinitions::AddStates(FState *state, const char *framechars)
863 {
864 bool error = false;
865 int frame = 0;
866
867 while (*framechars)
868 {
869 bool noframe = false;
870
871 if (*framechars == '#')
872 noframe = true;
873 else if (*framechars == '^')
874 frame = '\\' - 'A';
875 else
876 frame = (*framechars & 223) - 'A';
877
878 framechars++;
879 if (frame < 0 || frame > 28)
880 {
881 frame = 0;
882 error = true;
883 }
884
885 state->Frame = frame;
886 state->SameFrame = noframe;
887 StateArray.Push(*state);
888
889 // NODELAY flag is not carried past the first state
890 state->NoDelay = false;
891 }
892 laststate = &StateArray[StateArray.Size() - 1];
893 laststatebeforelabel = laststate;
894 return !error;
895 }
896
897 //==========================================================================
898 //
899 // FinishStates
900 // copies a state block and fixes all state links using the current list of labels
901 //
902 //==========================================================================
903
FinishStates(FActorInfo * actor,AActor * defaults)904 int FStateDefinitions::FinishStates (FActorInfo *actor, AActor *defaults)
905 {
906 int count = StateArray.Size();
907
908 if (count > 0)
909 {
910 FState *realstates = new FState[count];
911 int i;
912
913 memcpy(realstates, &StateArray[0], count*sizeof(FState));
914 actor->OwnedStates = realstates;
915 actor->NumOwnedStates = count;
916
917 // adjust the state pointers
918 // In the case new states are added these must be adjusted, too!
919 FixStatePointers(actor, StateLabels);
920
921 // Fix state pointers that are gotos
922 ResolveGotoLabels(actor, defaults, StateLabels);
923
924 for (i = 0; i < count; i++)
925 {
926 // resolve labels and jumps
927 switch (realstates[i].DefineFlags)
928 {
929 case SDF_STOP: // stop
930 realstates[i].NextState = NULL;
931 break;
932
933 case SDF_WAIT: // wait
934 realstates[i].NextState = &realstates[i];
935 break;
936
937 case SDF_NEXT: // next
938 realstates[i].NextState = (i < count-1 ? &realstates[i+1] : &realstates[0]);
939 break;
940
941 case SDF_INDEX: // loop
942 realstates[i].NextState = &realstates[(size_t)realstates[i].NextState-1];
943 break;
944
945 case SDF_LABEL:
946 realstates[i].NextState = ResolveGotoLabel(defaults, actor->Class, (char *)realstates[i].NextState);
947 break;
948 }
949 }
950 }
951 else
952 {
953 // Fix state pointers that are gotos
954 ResolveGotoLabels(actor, defaults, StateLabels);
955 }
956
957 return count;
958 }
959
960
961 //==========================================================================
962 //
963 // Prints all state label info to the logfile
964 //
965 //==========================================================================
966
DumpStateHelper(FStateLabels * StateList,const FString & prefix)967 void DumpStateHelper(FStateLabels *StateList, const FString &prefix)
968 {
969 for (int i = 0; i < StateList->NumLabels; i++)
970 {
971 if (StateList->Labels[i].State != NULL)
972 {
973 const PClass *owner = FState::StaticFindStateOwner(StateList->Labels[i].State);
974 if (owner == NULL)
975 {
976 Printf(PRINT_LOG, "%s%s: invalid\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars());
977 }
978 else
979 {
980 Printf(PRINT_LOG, "%s%s: %s.%d\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars(),
981 owner->TypeName.GetChars(), int(StateList->Labels[i].State - owner->ActorInfo->OwnedStates));
982 }
983 }
984 if (StateList->Labels[i].Children != NULL)
985 {
986 DumpStateHelper(StateList->Labels[i].Children, prefix + '.' + StateList->Labels[i].Label.GetChars());
987 }
988 }
989 }
990
CCMD(dumpstates)991 CCMD(dumpstates)
992 {
993 for (unsigned int i = 0; i < PClass::m_RuntimeActors.Size(); ++i)
994 {
995 FActorInfo *info = PClass::m_RuntimeActors[i]->ActorInfo;
996 Printf(PRINT_LOG, "State labels for %s\n", info->Class->TypeName.GetChars());
997 DumpStateHelper(info->StateList, "");
998 Printf(PRINT_LOG, "----------------------------\n");
999 }
1000 }
1001