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