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