1 /*
2 ** g_skill.cpp
3 ** Skill level handling
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2008-2009 Christoph Oelckers
7 ** Copyright 2008-2009 Randy Heit
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 <ctype.h>
37 #include "doomstat.h"
38 #include "d_player.h"
39 #include "g_level.h"
40 #include "g_game.h"
41 #include "gi.h"
42 #include "templates.h"
43 #include "v_font.h"
44 #include "m_fixed.h"
45 #include "gstrings.h"
46 
47 TArray<FSkillInfo> AllSkills;
48 int DefaultSkill = -1;
49 
50 //==========================================================================
51 //
52 // ParseSkill
53 //
54 //==========================================================================
55 
ParseSkill()56 void FMapInfoParser::ParseSkill ()
57 {
58 	FSkillInfo skill;
59 	bool thisisdefault = false;
60 	bool acsreturnisset = false;
61 
62 	skill.AmmoFactor = FRACUNIT;
63 	skill.DoubleAmmoFactor = 2*FRACUNIT;
64 	skill.DropAmmoFactor = -1;
65 	skill.DamageFactor = FRACUNIT;
66 	skill.FastMonsters = false;
67 	skill.SlowMonsters = false;
68 	skill.DisableCheats = false;
69 	skill.EasyBossBrain = false;
70 	skill.EasyKey = false;
71 	skill.AutoUseHealth = false;
72 	skill.RespawnCounter = 0;
73 	skill.RespawnLimit = 0;
74 	skill.Aggressiveness = FRACUNIT;
75 	skill.SpawnFilter = 0;
76 	skill.ACSReturn = 0;
77 	skill.MustConfirm = false;
78 	skill.Shortcut = 0;
79 	skill.TextColor = "";
80 	skill.Replace.Clear();
81 	skill.Replaced.Clear();
82 	skill.MonsterHealth = FRACUNIT;
83 	skill.FriendlyHealth = FRACUNIT;
84 	skill.NoPain = false;
85 	skill.ArmorFactor = FRACUNIT;
86 	skill.Infighting = 0;
87 	skill.HealthFactor = FRACUNIT;
88 
89 	sc.MustGetString();
90 	skill.Name = sc.String;
91 
92 	ParseOpenBrace();
93 
94 	while (sc.GetString ())
95 	{
96 		if (sc.Compare ("ammofactor"))
97 		{
98 			ParseAssign();
99 			sc.MustGetFloat ();
100 			skill.AmmoFactor = FLOAT2FIXED(sc.Float);
101 		}
102 		else if (sc.Compare ("doubleammofactor"))
103 		{
104 			ParseAssign();
105 			sc.MustGetFloat ();
106 			skill.DoubleAmmoFactor = FLOAT2FIXED(sc.Float);
107 		}
108 		else if (sc.Compare ("dropammofactor"))
109 		{
110 			ParseAssign();
111 			sc.MustGetFloat ();
112 			skill.DropAmmoFactor = FLOAT2FIXED(sc.Float);
113 		}
114 		else if (sc.Compare ("damagefactor"))
115 		{
116 			ParseAssign();
117 			sc.MustGetFloat ();
118 			skill.DamageFactor = FLOAT2FIXED(sc.Float);
119 		}
120 		else if (sc.Compare ("fastmonsters"))
121 		{
122 			skill.FastMonsters = true;
123 		}
124 		else if (sc.Compare ("slowmonsters"))
125 		{
126 			skill.SlowMonsters = true;
127 		}
128 		else if (sc.Compare ("disablecheats"))
129 		{
130 			skill.DisableCheats = true;
131 		}
132 		else if (sc.Compare ("easybossbrain"))
133 		{
134 			skill.EasyBossBrain = true;
135 		}
136 		else if (sc.Compare ("easykey"))
137 		{
138 			skill.EasyKey = true;
139 		}
140 		else if (sc.Compare("autousehealth"))
141 		{
142 			skill.AutoUseHealth = true;
143 		}
144 		else if (sc.Compare("respawntime"))
145 		{
146 			ParseAssign();
147 			sc.MustGetFloat ();
148 			skill.RespawnCounter = int(sc.Float*TICRATE);
149 		}
150 		else if (sc.Compare("respawnlimit"))
151 		{
152 			ParseAssign();
153 			sc.MustGetNumber ();
154 			skill.RespawnLimit = sc.Number;
155 		}
156 		else if (sc.Compare("Aggressiveness"))
157 		{
158 			ParseAssign();
159 			sc.MustGetFloat ();
160 			skill.Aggressiveness = FRACUNIT - FLOAT2FIXED(clamp(sc.Float, 0.,1.));
161 		}
162 		else if (sc.Compare("SpawnFilter"))
163 		{
164 			ParseAssign();
165 			if (sc.CheckNumber())
166 			{
167 				if (sc.Number > 0) skill.SpawnFilter |= (1<<(sc.Number-1));
168 			}
169 			else
170 			{
171 				sc.MustGetString ();
172 				if (sc.Compare("baby")) skill.SpawnFilter |= 1;
173 				else if (sc.Compare("easy")) skill.SpawnFilter |= 2;
174 				else if (sc.Compare("normal")) skill.SpawnFilter |= 4;
175 				else if (sc.Compare("hard")) skill.SpawnFilter |= 8;
176 				else if (sc.Compare("nightmare")) skill.SpawnFilter |= 16;
177 			}
178 		}
179 		else if (sc.Compare("ACSReturn"))
180 		{
181 			ParseAssign();
182 			sc.MustGetNumber ();
183 			skill.ACSReturn = sc.Number;
184 			acsreturnisset = true;
185 		}
186 		else if (sc.Compare("ReplaceActor"))
187 		{
188 			ParseAssign();
189 			sc.MustGetString();
190 			FName replaced = sc.String;
191 			ParseComma();
192 			sc.MustGetString();
193 			FName replacer = sc.String;
194 			skill.SetReplacement(replaced, replacer);
195 			skill.SetReplacedBy(replacer, replaced);
196 		}
197 		else if (sc.Compare("Name"))
198 		{
199 			ParseAssign();
200 			sc.MustGetString ();
201 			skill.MenuName = sc.String;
202 		}
203 		else if (sc.Compare("PlayerClassName"))
204 		{
205 			ParseAssign();
206 			sc.MustGetString ();
207 			FName pc = sc.String;
208 			ParseComma();
209 			sc.MustGetString ();
210 			skill.MenuNamesForPlayerClass[pc]=sc.String;
211 		}
212 		else if (sc.Compare("PicName"))
213 		{
214 			ParseAssign();
215 			sc.MustGetString ();
216 			skill.PicName = sc.String;
217 		}
218 		else if (sc.Compare("MustConfirm"))
219 		{
220 			skill.MustConfirm = true;
221 			if (format_type == FMT_New)
222 			{
223 				if (CheckAssign())
224 				{
225 					sc.MustGetString();
226 					skill.MustConfirmText = sc.String;
227 				}
228 			}
229 			else
230 			{
231 				if (sc.CheckToken(TK_StringConst))
232 				{
233 					skill.MustConfirmText = sc.String;
234 				}
235 			}
236 		}
237 		else if (sc.Compare("Key"))
238 		{
239 			ParseAssign();
240 			sc.MustGetString();
241 			skill.Shortcut = tolower(sc.String[0]);
242 		}
243 		else if (sc.Compare("TextColor"))
244 		{
245 			ParseAssign();
246 			sc.MustGetString();
247 			skill.TextColor.Format("[%s]", sc.String);
248 		}
249 		else if (sc.Compare("MonsterHealth"))
250 		{
251 			ParseAssign();
252 			sc.MustGetFloat();
253 			skill.MonsterHealth = FLOAT2FIXED(sc.Float);
254 		}
255 		else if (sc.Compare("FriendlyHealth"))
256 		{
257 			ParseAssign();
258 			sc.MustGetFloat();
259 			skill.FriendlyHealth = FLOAT2FIXED(sc.Float);
260 		}
261 		else if (sc.Compare("NoPain"))
262 		{
263 			skill.NoPain = true;
264 		}
265 		else if (sc.Compare("ArmorFactor"))
266 		{
267 			ParseAssign();
268 			sc.MustGetFloat();
269 			skill.ArmorFactor = FLOAT2FIXED(sc.Float);
270 		}
271 		else if (sc.Compare("HealthFactor"))
272 		{
273 			ParseAssign();
274 			sc.MustGetFloat();
275 			skill.HealthFactor = FLOAT2FIXED(sc.Float);
276 		}
277 		else if (sc.Compare("NoInfighting"))
278 		{
279 			skill.Infighting = LEVEL2_NOINFIGHTING;
280 		}
281 		else if (sc.Compare("TotalInfighting"))
282 		{
283 			skill.Infighting = LEVEL2_TOTALINFIGHTING;
284 		}
285 		else if (sc.Compare("DefaultSkill"))
286 		{
287 			if (DefaultSkill >= 0)
288 			{
289 				sc.ScriptError("%s is already the default skill\n", AllSkills[DefaultSkill].Name.GetChars());
290 			}
291 			thisisdefault = true;
292 		}
293 		else if (!ParseCloseBrace())
294 		{
295 			// Unknown
296 			sc.ScriptMessage("Unknown property '%s' found in skill definition\n", sc.String);
297 			SkipToNext();
298 		}
299 		else
300 		{
301 			break;
302 		}
303 	}
304 	CheckEndOfFile("skill");
305 	for(unsigned int i = 0; i < AllSkills.Size(); i++)
306 	{
307 		if (AllSkills[i].Name == skill.Name)
308 		{
309 			if (!acsreturnisset)
310 			{ // Use the ACS return for the skill we are overwriting.
311 				skill.ACSReturn = AllSkills[i].ACSReturn;
312 			}
313 			AllSkills[i] = skill;
314 			if (thisisdefault)
315 			{
316 				DefaultSkill = i;
317 			}
318 			return;
319 		}
320 	}
321 	if (!acsreturnisset)
322 	{
323 		skill.ACSReturn = AllSkills.Size();
324 	}
325 	if (thisisdefault)
326 	{
327 		DefaultSkill = AllSkills.Size();
328 	}
329 	AllSkills.Push(skill);
330 }
331 
332 //==========================================================================
333 //
334 //
335 //
336 //==========================================================================
337 
G_SkillProperty(ESkillProperty prop)338 int G_SkillProperty(ESkillProperty prop)
339 {
340 	if (AllSkills.Size() > 0)
341 	{
342 		switch(prop)
343 		{
344 		case SKILLP_AmmoFactor:
345 			if (dmflags2 & DF2_YES_DOUBLEAMMO)
346 			{
347 				return AllSkills[gameskill].DoubleAmmoFactor;
348 			}
349 			return AllSkills[gameskill].AmmoFactor;
350 
351 		case SKILLP_DropAmmoFactor:
352 			return AllSkills[gameskill].DropAmmoFactor;
353 
354 		case SKILLP_DamageFactor:
355 			return AllSkills[gameskill].DamageFactor;
356 
357 		case SKILLP_FastMonsters:
358 			return AllSkills[gameskill].FastMonsters  || (dmflags & DF_FAST_MONSTERS);
359 
360 		case SKILLP_SlowMonsters:
361 			return AllSkills[gameskill].SlowMonsters;
362 
363 		case SKILLP_Respawn:
364 			if (dmflags & DF_MONSTERS_RESPAWN && AllSkills[gameskill].RespawnCounter==0)
365 				return TICRATE * gameinfo.defaultrespawntime;
366 			return AllSkills[gameskill].RespawnCounter;
367 
368 		case SKILLP_RespawnLimit:
369 			return AllSkills[gameskill].RespawnLimit;
370 
371 		case SKILLP_Aggressiveness:
372 			return AllSkills[gameskill].Aggressiveness;
373 
374 		case SKILLP_DisableCheats:
375 			return AllSkills[gameskill].DisableCheats;
376 
377 		case SKILLP_AutoUseHealth:
378 			return AllSkills[gameskill].AutoUseHealth;
379 
380 		case SKILLP_EasyBossBrain:
381 			return AllSkills[gameskill].EasyBossBrain;
382 
383 		case SKILLP_EasyKey:
384 			return AllSkills[gameskill].EasyKey;
385 
386 		case SKILLP_SpawnFilter:
387 			return AllSkills[gameskill].SpawnFilter;
388 
389 		case SKILLP_ACSReturn:
390 			return AllSkills[gameskill].ACSReturn;
391 
392 		case SKILLP_MonsterHealth:
393 			return AllSkills[gameskill].MonsterHealth;
394 
395 		case SKILLP_FriendlyHealth:
396 			return AllSkills[gameskill].FriendlyHealth;
397 
398 		case SKILLP_NoPain:
399 			return AllSkills[gameskill].NoPain;
400 
401 		case SKILLP_ArmorFactor:
402 			return AllSkills[gameskill].ArmorFactor;
403 
404 		case SKILLP_HealthFactor:
405 			return AllSkills[gameskill].HealthFactor;
406 
407 		case SKILLP_Infight:
408 			// This property also needs to consider the level flags for the same info.
409 			if (level.flags2 & LEVEL2_TOTALINFIGHTING) return 1;
410 			if (level.flags2 & LEVEL2_NOINFIGHTING) return -1;
411 			if (AllSkills[gameskill].Infighting == LEVEL2_TOTALINFIGHTING) return 1;
412 			if (AllSkills[gameskill].Infighting == LEVEL2_NOINFIGHTING) return -1;
413 			return infighting;
414 		}
415 	}
416 	return 0;
417 }
418 
419 //==========================================================================
420 //
421 //
422 //
423 //==========================================================================
424 
G_SkillName()425 const char * G_SkillName()
426 {
427 	const char *name = AllSkills[gameskill].MenuName;
428 
429 	player_t *player = &players[consoleplayer];
430 	const char *playerclass = player->mo->GetClass()->Meta.GetMetaString(APMETA_DisplayName);
431 
432 	if (playerclass != NULL)
433 	{
434 		FString * pmnm = AllSkills[gameskill].MenuNamesForPlayerClass.CheckKey(playerclass);
435 		if (pmnm != NULL) name = *pmnm;
436 	}
437 
438 	if (*name == '$') name = GStrings(name+1);
439 	return name;
440 }
441 
442 
443 //==========================================================================
444 //
445 //
446 //
447 //==========================================================================
448 
G_VerifySkill()449 void G_VerifySkill()
450 {
451 	if (gameskill >= (int)AllSkills.Size())
452 		gameskill = AllSkills.Size()-1;
453 	else if (gameskill < 0)
454 		gameskill = 0;
455 }
456 
457 //==========================================================================
458 //
459 //
460 //
461 //==========================================================================
462 
operator =(const FSkillInfo & other)463 FSkillInfo &FSkillInfo::operator=(const FSkillInfo &other)
464 {
465 	Name = other.Name;
466 	AmmoFactor = other.AmmoFactor;
467 	DoubleAmmoFactor = other.DoubleAmmoFactor;
468 	DropAmmoFactor = other.DropAmmoFactor;
469 	DamageFactor = other.DamageFactor;
470 	FastMonsters = other.FastMonsters;
471 	SlowMonsters = other.SlowMonsters;
472 	DisableCheats = other.DisableCheats;
473 	AutoUseHealth = other.AutoUseHealth;
474 	EasyBossBrain = other.EasyBossBrain;
475 	EasyKey = other.EasyKey;
476 	RespawnCounter= other.RespawnCounter;
477 	RespawnLimit= other.RespawnLimit;
478 	Aggressiveness= other.Aggressiveness;
479 	SpawnFilter = other.SpawnFilter;
480 	ACSReturn = other.ACSReturn;
481 	MenuName = other.MenuName;
482 	PicName = other.PicName;
483 	MenuNamesForPlayerClass = other.MenuNamesForPlayerClass;
484 	MustConfirm = other.MustConfirm;
485 	MustConfirmText = other.MustConfirmText;
486 	Shortcut = other.Shortcut;
487 	TextColor = other.TextColor;
488 	Replace = other.Replace;
489 	Replaced = other.Replaced;
490 	MonsterHealth = other.MonsterHealth;
491 	FriendlyHealth = other.FriendlyHealth;
492 	NoPain = other.NoPain;
493 	Infighting = other.Infighting;
494 	ArmorFactor = other.ArmorFactor;
495 	HealthFactor = other.HealthFactor;
496 	return *this;
497 }
498 
499 //==========================================================================
500 //
501 //
502 //
503 //==========================================================================
504 
GetTextColor() const505 int FSkillInfo::GetTextColor() const
506 {
507 	if (TextColor.IsEmpty())
508 	{
509 		return CR_UNTRANSLATED;
510 	}
511 	const BYTE *cp = (const BYTE *)TextColor.GetChars();
512 	int color = V_ParseFontColor(cp, 0, 0);
513 	if (color == CR_UNDEFINED)
514 	{
515 		Printf("Undefined color '%s' in definition of skill %s\n", TextColor.GetChars(), Name.GetChars());
516 		color = CR_UNTRANSLATED;
517 	}
518 	return color;
519 }
520 
521 //==========================================================================
522 //
523 // FSkillInfo::SetReplacement
524 //
525 //==========================================================================
526 
SetReplacement(FName a,FName b)527 void FSkillInfo::SetReplacement(FName a, FName b)
528 {
529 	Replace[a] = b;
530 }
531 
532 //==========================================================================
533 //
534 // FSkillInfo::GetReplacement
535 //
536 //==========================================================================
537 
GetReplacement(FName a)538 FName FSkillInfo::GetReplacement(FName a)
539 {
540 	if (Replace.CheckKey(a)) return Replace[a];
541 	else return NAME_None;
542 }
543 
544 //==========================================================================
545 //
546 // FSkillInfo::SetReplaced
547 //
548 //==========================================================================
549 
SetReplacedBy(FName b,FName a)550 void FSkillInfo::SetReplacedBy(FName b, FName a)
551 {
552 	Replaced[b] = a;
553 }
554 
555 //==========================================================================
556 //
557 // FSkillInfo::GetReplaced
558 //
559 //==========================================================================
560 
GetReplacedBy(FName b)561 FName FSkillInfo::GetReplacedBy(FName b)
562 {
563 	if (Replaced.CheckKey(b)) return Replaced[b];
564 	else return NAME_None;
565 }
566