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