1 /*
2 ** thingdef.cpp
3 **
4 ** Actor definitions
5 **
6 **---------------------------------------------------------------------------
7 ** Copyright 2002-2008 Christoph Oelckers
8 ** Copyright 2004-2008 Randy Heit
9 ** All rights reserved.
10 **
11 ** Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions
13 ** are met:
14 **
15 ** 1. Redistributions of source code must retain the above copyright
16 ** notice, this list of conditions and the following disclaimer.
17 ** 2. Redistributions in binary form must reproduce the above copyright
18 ** notice, this list of conditions and the following disclaimer in the
19 ** documentation and/or other materials provided with the distribution.
20 ** 3. The name of the author may not be used to endorse or promote products
21 ** derived from this software without specific prior written permission.
22 ** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
23 ** covered by the terms of the GNU General Public License as published by
24 ** the Free Software Foundation; either version 2 of the License, or (at
25 ** your option) any later version.
26 **
27 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
28 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
31 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
32 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 **---------------------------------------------------------------------------
38 **
39 */
40
41 #include "gi.h"
42 #include "actor.h"
43 #include "info.h"
44 #include "sc_man.h"
45 #include "tarray.h"
46 #include "w_wad.h"
47 #include "templates.h"
48 #include "r_defs.h"
49 #include "a_pickups.h"
50 #include "s_sound.h"
51 #include "cmdlib.h"
52 #include "p_lnspec.h"
53 #include "a_action.h"
54 #include "decallib.h"
55 #include "m_random.h"
56 #include "i_system.h"
57 #include "p_local.h"
58 #include "doomerrors.h"
59 #include "a_hexenglobal.h"
60 #include "a_weaponpiece.h"
61 #include "p_conversation.h"
62 #include "v_text.h"
63 #include "thingdef.h"
64 #include "thingdef_exp.h"
65 #include "a_sharedglobal.h"
66
67 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
68 void InitThingdef();
69 void ParseDecorate (FScanner &sc);
70
71 // STATIC FUNCTION PROTOTYPES --------------------------------------------
72 const PClass *QuestItemClasses[31];
73 PSymbolTable GlobalSymbols;
74
75 //==========================================================================
76 //
77 // Starts a new actor definition
78 //
79 //==========================================================================
CreateNewActor(const FScriptPosition & sc,FName typeName,FName parentName,bool native)80 FActorInfo *CreateNewActor(const FScriptPosition &sc, FName typeName, FName parentName, bool native)
81 {
82 const PClass *replacee = NULL;
83 PClass *ti = NULL;
84 FActorInfo *info = NULL;
85
86 PClass *parent = RUNTIME_CLASS(AActor);
87
88 if (parentName != NAME_None)
89 {
90 parent = const_cast<PClass *> (PClass::FindClass (parentName));
91
92 const PClass *p = parent;
93 while (p != NULL)
94 {
95 if (p->TypeName == typeName)
96 {
97 sc.Message(MSG_ERROR, "'%s' inherits from a class with the same name", typeName.GetChars());
98 break;
99 }
100 p = p->ParentClass;
101 }
102
103 if (parent == NULL)
104 {
105 sc.Message(MSG_ERROR, "Parent type '%s' not found in %s", parentName.GetChars(), typeName.GetChars());
106 parent = RUNTIME_CLASS(AActor);
107 }
108 else if (!parent->IsDescendantOf(RUNTIME_CLASS(AActor)))
109 {
110 sc.Message(MSG_ERROR, "Parent type '%s' is not an actor in %s", parentName.GetChars(), typeName.GetChars());
111 parent = RUNTIME_CLASS(AActor);
112 }
113 else if (parent->ActorInfo == NULL)
114 {
115 sc.Message(MSG_ERROR, "uninitialized parent type '%s' in %s", parentName.GetChars(), typeName.GetChars());
116 parent = RUNTIME_CLASS(AActor);
117 }
118 }
119
120 if (native)
121 {
122 ti = (PClass*)PClass::FindClass(typeName);
123 if (ti == NULL)
124 {
125 sc.Message(MSG_ERROR, "Unknown native class '%s'", typeName.GetChars());
126 goto create;
127 }
128 else if (ti != RUNTIME_CLASS(AActor) && ti->ParentClass->NativeClass() != parent->NativeClass())
129 {
130 sc.Message(MSG_ERROR, "Native class '%s' does not inherit from '%s'", typeName.GetChars(), parentName.GetChars());
131 parent = RUNTIME_CLASS(AActor);
132 goto create;
133 }
134 else if (ti->ActorInfo != NULL)
135 {
136 sc.Message(MSG_ERROR, "Redefinition of internal class '%s'", typeName.GetChars());
137 goto create;
138 }
139 ti->InitializeActorInfo();
140 info = ti->ActorInfo;
141 }
142 else
143 {
144 create:
145 ti = parent->CreateDerivedClass (typeName, parent->Size);
146 info = ti->ActorInfo;
147 }
148
149 // Copy class lists from parent
150 info->ForbiddenToPlayerClass = parent->ActorInfo->ForbiddenToPlayerClass;
151 info->RestrictedToPlayerClass = parent->ActorInfo->RestrictedToPlayerClass;
152 info->VisibleToPlayerClass = parent->ActorInfo->VisibleToPlayerClass;
153
154 if (parent->ActorInfo->DamageFactors != NULL)
155 {
156 // copy damage factors from parent
157 info->DamageFactors = new DmgFactors;
158 *info->DamageFactors = *parent->ActorInfo->DamageFactors;
159 }
160 if (parent->ActorInfo->PainChances != NULL)
161 {
162 // copy pain chances from parent
163 info->PainChances = new PainChanceList;
164 *info->PainChances = *parent->ActorInfo->PainChances;
165 }
166 if (parent->ActorInfo->ColorSets != NULL)
167 {
168 // copy color sets from parent
169 info->ColorSets = new FPlayerColorSetMap;
170 *info->ColorSets = *parent->ActorInfo->ColorSets;
171 }
172 info->Replacee = info->Replacement = NULL;
173 info->DoomEdNum = -1;
174 return info;
175 }
176
177 //==========================================================================
178 //
179 //
180 //
181 //==========================================================================
182
SetReplacement(FScanner & sc,FActorInfo * info,FName replaceName)183 void SetReplacement(FScanner &sc, FActorInfo *info, FName replaceName)
184 {
185 // Check for "replaces"
186 if (replaceName != NAME_None)
187 {
188 // Get actor name
189 const PClass *replacee = PClass::FindClass (replaceName);
190
191 if (replacee == NULL)
192 {
193 sc.ScriptMessage("Replaced type '%s' not found for %s", replaceName.GetChars(), info->Class->TypeName.GetChars());
194 return;
195 }
196 else if (replacee->ActorInfo == NULL)
197 {
198 sc.ScriptMessage("Replaced type '%s' for %s is not an actor", replaceName.GetChars(), info->Class->TypeName.GetChars());
199 return;
200 }
201 if (replacee != NULL)
202 {
203 replacee->ActorInfo->Replacement = info;
204 info->Replacee = replacee->ActorInfo;
205 }
206 }
207
208 }
209
210 //==========================================================================
211 //
212 // Finalizes an actor definition
213 //
214 //==========================================================================
215
FinishActor(const FScriptPosition & sc,FActorInfo * info,Baggage & bag)216 void FinishActor(const FScriptPosition &sc, FActorInfo *info, Baggage &bag)
217 {
218 PClass *ti = info->Class;
219 AActor *defaults = (AActor*)ti->Defaults;
220
221 try
222 {
223 bag.statedef.FinishStates (info, defaults);
224 }
225 catch (CRecoverableError &err)
226 {
227 sc.Message(MSG_ERROR, "%s", err.GetMessage());
228 bag.statedef.MakeStateDefines(NULL);
229 return;
230 }
231 bag.statedef.InstallStates (info, defaults);
232 bag.statedef.MakeStateDefines(NULL);
233 if (bag.DropItemSet)
234 {
235 if (bag.DropItemList == NULL)
236 {
237 if (ti->Meta.GetMetaInt (ACMETA_DropItems) != 0)
238 {
239 ti->Meta.SetMetaInt (ACMETA_DropItems, 0);
240 }
241 }
242 else
243 {
244 ti->Meta.SetMetaInt (ACMETA_DropItems,
245 StoreDropItemChain(bag.DropItemList));
246 }
247 }
248 if (ti->IsDescendantOf (RUNTIME_CLASS(AInventory)))
249 {
250 defaults->flags |= MF_SPECIAL;
251 }
252
253 // Weapons must be checked for all relevant states. They may crash the game otherwise.
254 if (ti->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
255 {
256 FState * ready = ti->ActorInfo->FindState(NAME_Ready);
257 FState * select = ti->ActorInfo->FindState(NAME_Select);
258 FState * deselect = ti->ActorInfo->FindState(NAME_Deselect);
259 FState * fire = ti->ActorInfo->FindState(NAME_Fire);
260
261 // Consider any weapon without any valid state abstract and don't output a warning
262 // This is for creating base classes for weapon groups that only set up some properties.
263 if (ready || select || deselect || fire)
264 {
265 if (!ready)
266 {
267 sc.Message(MSG_ERROR, "Weapon %s doesn't define a ready state.\n", ti->TypeName.GetChars());
268 }
269 if (!select)
270 {
271 sc.Message(MSG_ERROR, "Weapon %s doesn't define a select state.\n", ti->TypeName.GetChars());
272 }
273 if (!deselect)
274 {
275 sc.Message(MSG_ERROR, "Weapon %s doesn't define a deselect state.\n", ti->TypeName.GetChars());
276 }
277 if (!fire)
278 {
279 sc.Message(MSG_ERROR, "Weapon %s doesn't define a fire state.\n", ti->TypeName.GetChars());
280 }
281 }
282 }
283 }
284
285 //==========================================================================
286 //
287 // Do some postprocessing after everything has been defined
288 //
289 //==========================================================================
290
FinishThingdef()291 static void FinishThingdef()
292 {
293 int errorcount = StateParams.ResolveAll();
294
295 for (unsigned i = 0;i < PClass::m_Types.Size(); i++)
296 {
297 PClass * ti = PClass::m_Types[i];
298
299 // Skip non-actors
300 if (!ti->IsDescendantOf(RUNTIME_CLASS(AActor))) continue;
301
302 if (ti->Size == (unsigned)-1)
303 {
304 Printf("Class %s referenced but not defined\n", ti->TypeName.GetChars());
305 errorcount++;
306 continue;
307 }
308
309 AActor *def = GetDefaultByType(ti);
310
311 if (!def)
312 {
313 Printf("No ActorInfo defined for class '%s'\n", ti->TypeName.GetChars());
314 errorcount++;
315 continue;
316 }
317 }
318 if (errorcount > 0)
319 {
320 I_Error("%d errors during actor postprocessing", errorcount);
321 }
322
323 // Since these are defined in DECORATE now the table has to be initialized here.
324 for(int i=0;i<31;i++)
325 {
326 char fmt[20];
327 mysnprintf(fmt, countof(fmt), "QuestItem%d", i+1);
328 QuestItemClasses[i] = PClass::FindClass(fmt);
329 }
330 }
331
332
333
334 //==========================================================================
335 //
336 // LoadActors
337 //
338 // Called from FActor::StaticInit()
339 //
340 //==========================================================================
341
LoadActors()342 void LoadActors ()
343 {
344 int lastlump, lump;
345
346 StateParams.Clear();
347 GlobalSymbols.ReleaseSymbols();
348 DropItemList.Clear();
349 FScriptPosition::ResetErrorCounter();
350 InitThingdef();
351 lastlump = 0;
352 while ((lump = Wads.FindLump ("DECORATE", &lastlump)) != -1)
353 {
354 FScanner sc(lump);
355 ParseDecorate (sc);
356 }
357 if (FScriptPosition::ErrorCounter > 0)
358 {
359 I_Error("%d errors while parsing DECORATE scripts", FScriptPosition::ErrorCounter);
360 }
361 FinishThingdef();
362 }
363
364