1 /** @file d_main.cpp  Doom-specific game initialization.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2005-2013 Daniel Swanson <danij@dengine.net>
5  * @authors Copyright © 2006 Jamie Jones <yagisan@dengine.net>
6  * @authors Copyright © 1993-1996 id Software, Inc.
7  *
8  * @par License
9  * GPL: http://www.gnu.org/licenses/gpl.html
10  *
11  * <small>This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by the
13  * Free Software Foundation; either version 2 of the License, or (at your
14  * option) any later version. This program is distributed in the hope that it
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17  * Public License for more details. You should have received a copy of the GNU
18  * General Public License along with this program; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA</small>
21  */
22 
23 #include "jdoom.h"
24 
25 #include <de/App>
26 #include <de/CommandLine>
27 #include "d_netsv.h"
28 #include "doomv9mapstatereader.h"
29 #include "g_defs.h"
30 #include "gamesession.h"
31 #include "hu_menu.h"
32 #include "hu_stuff.h"
33 #include "hud/widgets/automapwidget.h"
34 #include "m_argv.h"
35 #include "p_map.h"
36 #include "saveslots.h"
37 #include "r_common.h"
38 
39 using namespace de;
40 using namespace common;
41 
42 gamemode_t gameMode;
43 int gameModeBits;
44 
45 // Default font colors.
46 float defFontRGB[3];
47 float defFontRGB2[3];
48 float defFontRGB3[3];
49 
50 // The patches used in drawing the view border.
51 // Percent-encoded.
52 char const *borderGraphics[] = {
53     "Flats:FLOOR7_2", // Background.
54     "BRDR_T", // Top.
55     "BRDR_R", // Right.
56     "BRDR_B", // Bottom.
57     "BRDR_L", // Left.
58     "BRDR_TL", // Top left.
59     "BRDR_TR", // Top right.
60     "BRDR_BR", // Bottom right.
61     "BRDR_BL" // Bottom left.
62 };
63 
D_GetInteger(int id)64 int D_GetInteger(int id)
65 {
66     return Common_GetInteger(id);
67 }
68 
D_GetVariable(int id)69 void *D_GetVariable(int id)
70 {
71     static float bob[2];
72 
73     switch(id)
74     {
75     case DD_PLUGIN_NAME:
76         return (void*)PLUGIN_NAMETEXT;
77 
78     case DD_PLUGIN_NICENAME:
79         return (void*)PLUGIN_NICENAME;
80 
81     case DD_PLUGIN_VERSION_SHORT:
82         return (void*)PLUGIN_VERSION_TEXT;
83 
84     case DD_PLUGIN_VERSION_LONG:
85         return (void*)(PLUGIN_VERSION_TEXTLONG "\n" PLUGIN_DETAILS);
86 
87     case DD_PLUGIN_HOMEURL:
88         return (void*)PLUGIN_HOMEURL;
89 
90     case DD_PLUGIN_DOCSURL:
91         return (void*)PLUGIN_DOCSURL;
92 
93     case DD_GAME_CONFIG:
94         return gameConfigString;
95 
96     case DD_ACTION_LINK:
97         return actionlinks;
98 
99     case DD_XGFUNC_LINK:
100         return xgClasses;
101 
102     case DD_PSPRITE_BOB_X:
103         R_GetWeaponBob(DISPLAYPLAYER, &bob[0], 0);
104         return &bob[0];
105 
106     case DD_PSPRITE_BOB_Y:
107         R_GetWeaponBob(DISPLAYPLAYER, 0, &bob[1]);
108         return &bob[1];
109 
110     case DD_TM_FLOOR_Z:
111         return (void*) &tmFloorZ;
112 
113     case DD_TM_CEILING_Z:
114         return (void*) &tmCeilingZ;
115 
116     default:
117         break;
118     }
119     return 0;
120 }
121 
D_PreInit()122 void D_PreInit()
123 {
124     // Configure default colors:
125     switch(gameMode)
126     {
127     case doom2_hacx:
128         defFontRGB[CR] = .85f;
129         defFontRGB[CG] = 0;
130         defFontRGB[CB] = 0;
131 
132         defFontRGB2[CR] = .2f;
133         defFontRGB2[CG] = .9f;
134         defFontRGB2[CB] = .2f;
135 
136         defFontRGB3[CR] = .2f;
137         defFontRGB3[CG] = .9f;
138         defFontRGB3[CB] = .2f;
139         break;
140 
141     case doom_chex:
142         defFontRGB[CR] = .46f;
143         defFontRGB[CG] = 1;
144         defFontRGB[CB] = .4f;
145 
146         defFontRGB2[CR] = .46f;
147         defFontRGB2[CG] = 1;
148         defFontRGB2[CB] = .4f;
149 
150         defFontRGB3[CR] = 1;
151         defFontRGB3[CG] = 1;
152         defFontRGB3[CB] = .45f;
153         break;
154 
155     default:
156         defFontRGB[CR] = 1;
157         defFontRGB[CG] = 1;
158         defFontRGB[CB] = 1;
159 
160         defFontRGB2[CR] = .85f;
161         defFontRGB2[CG] = 0;
162         defFontRGB2[CB] = 0;
163 
164         defFontRGB3[CR] = 1;
165         defFontRGB3[CG] = .9f;
166         defFontRGB3[CB] = .4f;
167         break;
168     }
169 
170     // Config defaults. The real settings are read from the .cfg files
171     // but these will be used no such files are found.
172     memset(&cfg, 0, sizeof(cfg));
173     cfg.common.playerMoveSpeed = 1;
174     cfg.common.povLookAround = true;
175     cfg.common.screenBlocks = cfg.common.setBlocks = 10;
176     cfg.common.echoMsg = true;
177     cfg.common.lookSpeed = 3;
178     cfg.common.turnSpeed = 1;
179 
180     cfg.common.menuPatchReplaceMode = PRM_ALLOW_TEXT;
181     cfg.common.menuScale = .9f;
182     cfg.common.menuTextGlitter = .5f;
183     cfg.common.menuShadow = 0.33f;
184     cfg.menuQuitSound = true;
185     cfg.common.menuSlam = false;
186     cfg.common.menuShortcutsEnabled = true;
187     cfg.common.menuGameSaveSuggestDescription = true;
188     cfg.common.menuEffectFlags = MEF_TEXT_TYPEIN | MEF_TEXT_SHADOW | MEF_TEXT_GLITTER;
189     cfg.common.menuTextFlashColor[0] = .7f;
190     cfg.common.menuTextFlashColor[1] = .9f;
191     cfg.common.menuTextFlashColor[2] = 1;
192     cfg.common.menuTextFlashSpeed = 4;
193     if(gameMode != doom_chex)
194     {
195         cfg.common.menuCursorRotate = true;
196     }
197     if(gameMode == doom2_hacx)
198     {
199         cfg.common.menuTextColors[0][CR] = cfg.common.menuTextColors[0][CG] = cfg.common.menuTextColors[0][CB] = 1;
200         memcpy(cfg.common.menuTextColors[1], defFontRGB, sizeof(cfg.common.menuTextColors[1]));
201         cfg.common.menuTextColors[2][CR] = cfg.common.menuTextColors[3][CR] = .2f;
202         cfg.common.menuTextColors[2][CG] = cfg.common.menuTextColors[3][CG] = .2f;
203         cfg.common.menuTextColors[2][CB] = cfg.common.menuTextColors[3][CB] = .9f;
204     }
205     else
206     {
207         memcpy(cfg.common.menuTextColors[0], defFontRGB2, sizeof(cfg.common.menuTextColors[0]));
208         if(gameMode == doom_chex)
209         {
210             cfg.common.menuTextColors[1][CR] = .85f;
211             cfg.common.menuTextColors[1][CG] = .3f;
212             cfg.common.menuTextColors[1][CB] = .3f;
213         }
214         else
215         {
216             cfg.common.menuTextColors[1][CR] = 1.f;
217             cfg.common.menuTextColors[1][CG] = .7f;
218             cfg.common.menuTextColors[1][CB] = .3f;
219         }
220         memcpy(cfg.common.menuTextColors[2], defFontRGB,  sizeof(cfg.common.menuTextColors[2]));
221         memcpy(cfg.common.menuTextColors[3], defFontRGB2, sizeof(cfg.common.menuTextColors[3]));
222     }
223 
224     cfg.common.inludePatchReplaceMode = PRM_ALLOW_TEXT;
225 
226     cfg.common.hudPatchReplaceMode = PRM_ALLOW_TEXT;
227     cfg.hudKeysCombine = false;
228     cfg.hudShown[HUD_HEALTH] = true;
229     cfg.hudShown[HUD_ARMOR] = true;
230     cfg.hudShown[HUD_AMMO] = true;
231     cfg.hudShown[HUD_KEYS] = true;
232     cfg.hudShown[HUD_FRAGS] = true;
233     cfg.hudShown[HUD_FACE] = false;
234     cfg.hudShown[HUD_LOG] = true;
235     for(int i = 0; i < NUMHUDUNHIDEEVENTS; ++i) // when the hud/statusbar unhides.
236     {
237         cfg.hudUnHide[i] = 1;
238     }
239     cfg.common.hudScale = .6f;
240 
241     memcpy(cfg.common.hudColor, defFontRGB2, MIN_OF(sizeof(defFontRGB2),
242                                                     sizeof(cfg.common.hudColor)));
243     cfg.common.hudColor[CA] = 1;
244 
245     cfg.common.hudFog = 5;
246     cfg.common.hudIconAlpha = 1;
247     cfg.common.xhairAngle = 0;
248     cfg.common.xhairSize = .5f;
249     cfg.common.xhairLineWidth = 1;
250     cfg.common.xhairVitality = false;
251     cfg.common.xhairColor[0] = 1;
252     cfg.common.xhairColor[1] = 1;
253     cfg.common.xhairColor[2] = 1;
254     cfg.common.xhairColor[3] = 1;
255 
256     cfg.common.filterStrength = .8f;
257     cfg.moveCheckZ = true;
258     cfg.common.jumpPower = 9;
259     cfg.common.airborneMovement = 1;
260     cfg.common.weaponAutoSwitch = 1; // if better
261     cfg.common.noWeaponAutoSwitchIfFiring = false;
262     cfg.common.ammoAutoSwitch = 0; // never
263     cfg.secretMsg = true;
264     cfg.slidingCorpses = false;
265     //cfg.fastMonsters = false;
266     cfg.common.netJumping = true;
267     cfg.common.netEpisode = (char *) "";
268     cfg.common.netMap = 0;
269     cfg.common.netSkill = SM_MEDIUM;
270     cfg.common.netColor = 4;
271     cfg.netBFGFreeLook = 0;    // allow free-aim 0=none 1=not BFG 2=All
272     cfg.common.netMobDamageModifier = 1;
273     cfg.common.netMobHealthModifier = 1;
274     cfg.common.netGravity = -1;        // use map default
275     cfg.common.plrViewHeight = DEFAULT_PLAYER_VIEWHEIGHT;
276     cfg.common.mapTitle = true;
277     cfg.common.automapTitleAtBottom = true;
278     cfg.common.hideIWADAuthor = true;
279     cfg.common.hideUnknownAuthor = true;
280 
281     cfg.common.confirmQuickGameSave = true;
282     cfg.common.confirmRebornLoad = true;
283     cfg.common.loadLastSaveOnReborn = false;
284 
285     cfg.maxSkulls = true;
286     cfg.allowSkullsInWalls = false;
287     cfg.anyBossDeath = false;
288     cfg.monstersStuckInDoors = false;
289     cfg.avoidDropoffs = true;
290     cfg.moveBlock = false;
291     cfg.fallOff = true;
292     cfg.fixOuchFace = true;
293     cfg.fixStatusbarOwnedWeapons = true;
294 
295     cfg.common.statusbarScale = 1;
296     cfg.common.statusbarOpacity = 1;
297     cfg.common.statusbarCounterAlpha = 1;
298 
299     cfg.common.automapCustomColors = 0; // Never.
300     cfg.common.automapL0[0] = .4f; // Unseen areas
301     cfg.common.automapL0[1] = .4f;
302     cfg.common.automapL0[2] = .4f;
303 
304     cfg.common.automapL1[0] = 1.f; // onesided lines
305     cfg.common.automapL1[1] = 0.f;
306     cfg.common.automapL1[2] = 0.f;
307 
308     cfg.common.automapL2[0] = .77f; // floor height change lines
309     cfg.common.automapL2[1] = .6f;
310     cfg.common.automapL2[2] = .325f;
311 
312     cfg.common.automapL3[0] = 1.f; // ceiling change lines
313     cfg.common.automapL3[1] = .95f;
314     cfg.common.automapL3[2] = 0.f;
315 
316     cfg.common.automapMobj[0] = 0.f;
317     cfg.common.automapMobj[1] = 1.f;
318     cfg.common.automapMobj[2] = 0.f;
319 
320     cfg.common.automapBack[0] = 0.f;
321     cfg.common.automapBack[1] = 0.f;
322     cfg.common.automapBack[2] = 0.f;
323     cfg.common.automapOpacity = .7f;
324     cfg.common.automapLineAlpha = .7f;
325     cfg.common.automapLineWidth = 3.0f;
326     cfg.common.automapShowDoors = true;
327     cfg.common.automapDoorGlow = 8;
328     cfg.common.automapHudDisplay = 2;
329     cfg.common.automapRotate = true;
330     cfg.common.automapBabyKeys = false;
331     cfg.common.automapZoomSpeed = .1f;
332     cfg.common.automapPanSpeed = .5f;
333     cfg.common.automapPanResetOnOpen = true;
334     cfg.common.automapOpenSeconds = AUTOMAPWIDGET_OPEN_SECONDS;
335 
336     cfg.common.hudCheatCounterScale = .7f;
337     cfg.common.hudCheatCounterShowWithAutomap = true;
338 
339     if(gameMode == doom_chex)
340     {
341         cfg.hudKeysCombine = true;
342     }
343 
344     cfg.common.msgCount = 4;
345     cfg.common.msgScale = .8f;
346     cfg.common.msgUptime = 5;
347     cfg.common.msgAlign = 0; // Left.
348     cfg.common.msgBlink = 5;
349 
350     if(gameMode == doom2_hacx)
351     {
352         cfg.common.msgColor[CR] = .2f;
353         cfg.common.msgColor[CG] = .2f;
354         cfg.common.msgColor[CB] = .9f;
355     }
356     else
357     {
358         memcpy(cfg.common.msgColor, defFontRGB2, sizeof(cfg.common.msgColor));
359     }
360 
361     cfg.common.chatBeep = true;
362 
363     cfg.killMessages = true;
364     cfg.common.bobWeapon = 1;
365     cfg.common.bobView = 1;
366     cfg.bobWeaponLower = true;
367     cfg.common.cameraNoClip = true;
368     cfg.respawnMonstersNightmare = true;
369 
370     cfg.common.weaponOrder[0] = WT_SIXTH;
371     cfg.common.weaponOrder[1] = WT_NINETH;
372     cfg.common.weaponOrder[2] = WT_FOURTH;
373     cfg.common.weaponOrder[3] = WT_THIRD;
374     cfg.common.weaponOrder[4] = WT_SECOND;
375     cfg.common.weaponOrder[5] = WT_EIGHTH;
376     cfg.common.weaponOrder[6] = WT_FIFTH;
377     cfg.common.weaponOrder[7] = WT_SEVENTH;
378     cfg.common.weaponOrder[8] = WT_FIRST;
379 
380     cfg.common.weaponCycleSequential = true;
381     cfg.berserkAutoSwitch = true;
382 
383     // Use the DOOM transition by default.
384     Con_SetInteger("con-transition", 1);
385 
386     // Do the common pre init routine;
387     G_CommonPreInit();
388 }
389 
D_PostInit()390 void D_PostInit()
391 {
392     CommandLine &cmdLine = DENG2_APP->commandLine();
393 
394     /// @todo Kludge: Border background is different in DOOM2.
395     /// @todo Do this properly!
396     ::borderGraphics[0] = (::gameModeBits & GM_ANY_DOOM2)? "Flats:GRNROCK" : "Flats:FLOOR7_2";
397 
398     G_CommonPostInit();
399 
400     P_InitAmmoInfo();
401     P_InitWeaponInfo();
402     IN_Init();
403 
404     // Game parameters.
405     ::monsterInfight = 0;
406     if(ded_value_t const *infight = Defs().getValueById("AI|Infight"))
407     {
408         ::monsterInfight = String(infight->text).toInt();
409     }
410 
411     // Get skill / episode / map from parms.
412     gfw_SetDefaultRule(skill, /*startSkill =*/ SM_MEDIUM);
413 
414     if (cmdLine.check("-altdeath"))
415     {
416         ::cfg.common.netDeathmatch = 2;
417     }
418     else if (cmdLine.check("-deathmatch"))
419     {
420         ::cfg.common.netDeathmatch = 1;
421     }
422 
423     gfw_SetDefaultRule(fast, cfg.common.defaultRuleFastMonsters);
424 
425     // Apply these rules.
426     gfw_SetDefaultRule(noMonsters,
427                        cmdLine.has("-nomonsters") ||
428                            gfw_GameProfile()->optionValue("noMonsters").isTrue());
429     gfw_SetDefaultRule(respawnMonsters,
430                        cmdLine.has("-respawn") ||
431                            gfw_GameProfile()->optionValue("respawn").isTrue());
432     gfw_SetDefaultRule(fast,
433                        cmdLine.has("-fast") || gfw_GameProfile()->optionValue("fast").isTrue());
434 
435     if (gfw_DefaultRule(deathmatch))
436     {
437         if (int arg = cmdLine.check("-timer", 1))
438         {
439             bool isNumber;
440             int mins = cmdLine.at(arg + 1).toInt(&isNumber);
441             if (isNumber)
442             {
443                 LOG_NOTE("Maps will end after %i %s")
444                         << mins << (mins == 1? "minute" : "minutes");
445             }
446         }
447     }
448 
449     // Load a saved game?
450     if (int arg = cmdLine.check("-loadgame", 1))
451     {
452         if (SaveSlot *sslot = G_SaveSlots().slotByUserInput(cmdLine.at(arg + 1)))
453         {
454             if (sslot->isUserWritable() && G_SetGameActionLoadSession(sslot->id()))
455             {
456                 // No further initialization is to be done.
457                 return;
458             }
459         }
460     }
461 
462     // Change the default skill mode?
463     if (int arg = cmdLine.check("-skill", 1))
464     {
465         int skillNumber = cmdLine.at(arg + 1).toInt();
466         gfw_SetDefaultRule(skill, skillmode_t(skillNumber > 0? skillNumber - 1 : skillNumber));
467     }
468 
469     G_AutoStartOrBeginTitleLoop();
470 }
471 
D_Shutdown()472 void D_Shutdown()
473 {
474     IN_Shutdown();
475     G_CommonShutdown();
476 }
477