After the game has finished loading, it will run the main loop until the player quits. The most important parts of the loop are "game logic" and graphics. Game logic includes all game mechanics like physics, enemies, etc. Game logic happens in steps that are called tics. 35 (=TICRATE) tics happen in one second. Normally the graphics on the screen are updated after running one tic. (in D_Display) However, if the game detects that it is running slowly, multiple tics can be run without updating the graphics between them.
Game logic
The TryRunTics function is responsible for running game logic tics. Before running tics however, it handles player inputs, gui logic and the network connection when in a netgame. It then runs tics by calling G_Ticker, multiple times if the game is lagging behind.
G_Ticker runs one tic. The contents of the tic depend on gamestate, which is most commonly GS_LEVEL (playing a level), but there are other states like GS_INTERMISSION, GS_CUTSCENE and GS_TITLESCREEN. When in GS_LEVEL, G_Ticker calls various HUD logic functions and P_Ticker.
P_Ticker handles all level-related logic, including thinkers. Thinkers are a mechanism used by the engine to implement dynamic elements in a level. A thinker consists of a function pointer and a varying amount of data belonging to the thinker. Effects that change the map, like scrolling textures and moving platforms are implemented with thinkers. All thinkers have their functions executed once on each tic.
Map objects are also a kind of thinker. (or map objects contain a thinker) The thinker points to the P_MobjThinker function, which contains the logic for all map objects and their interactions. This includes states, actions, movement, physics etc.
If a mobj has momentum, P_XYMovement is called to handle movement in the XY-axes. P_ZMovement handles vertical movement.
Collision detection happens in P_CheckPosition, called by code that moves the mobj. Here mobj to mobj collisions are checked by calling PIT_CheckThing on mobjs nearby on the blockmap. Mobj to line collisions are similarly checked using PIT_CheckLine. Various global vars beginning with "tm" (Thing Movement? TryMove? unknown meaning) are set in P_CheckPosition, keeping track of properties related to the mobj that is currently moving and needs collision checking.
Graphics
D_Display handles most/all drawing in the game. Drawing the level is done by calling R_RenderPlayerView in software mode and HWR_RenderPlayerView in OpenGL mode.
HWR_RenderPlayerView
HWR_RenderPlayerView draws the player's view of the level in OpenGL mode. Most of the rendering process actually happens twice, first the skybox is rendered and then the level itself is rendered on top. After clearing the frame buffer and initializing various variables, the rendering begins with HWR_RenderBSPNode((INT32)numnodes-1). This iterates through the BSP tree recursively, starting from the root node. (todo: link to bsp article) During the iteration, HWR_Subsector is called for all subsectors that are visible from the current view.
HWR_Subsector renders the floor and ceiling planes of the subsector by calling HWR_RenderPlane. Planes belonging to FOFs are also rendered. The polyobjects occupying the subsector are rendered entirely in this function. The sector's sprites are checked and retrieved, but their actual rendering will happen later in the process. Finally the linesegs belonging to the subsector are processed by calling HWR_AddLine for them.
HWR_Addline handles all linesegs that are being rendered. At the start the orientation of the seg is checked and lines that face away from the camera are ignored, since they cannot be seen. Segs determined to be not visible by the culling system are also skipped. Single-sided linesegs and thok barriers are added to the culling system, so anything behind them will be skipped. Finally, if the seg is possibly visible, HWR_ProcessSeg is called to render it.
HWR_ProcessSeg renders all wall pieces belonging to a lineseg. That means bottom, middle and top textures, skywalls and fof walls.
After the topmost HWR_RenderBSPNode call has returned, sprites (gathered from HWR_Subsector) are rendered. Sprites are first sorted so that they are rendered from back to front. This is done in order to display translucent sprites correctly. If models are enabled and available, they will replace the sprites they apply to.
After sprites the translucent surfaces are rendered. While the level geometry was being rendered in HWR_RenderBSPNode, all walls and planes that are translucent were added to a list instead of immediately rendering them. These surfaces are called "drawnodes" in the code. (note: drawnodes are also used in software mode but they do not mean exactly the same thing) The drawnode list is also sorted before rendering for correct translucency.
Finally, after rendering the contents of the level, currently enabled postprocessing effects are applied to the screen. This includes screen flashing (armageddon shield blast) and screen waving. (underwater and heat wave)
R_RenderPlayerView
R_RenderPlayerView draws the player's view of the level in software mode. After clearing the screen and initializing some variables R_RenderBSPNode((INT32)numnodes - 1) is called. This iterates through the BSP tree recursively, starting from the root node. (todo: link to bsp article) During the iteration, R_Subsector is called for all subsectors that are visible from the current view.
R_Subsector prepares the floor, ceiling, fof and polyobject planes of the subsector for later drawing. Sprites from the sector are also retrieved for later use. Then polyobject linesegs are drawn and lastly all regular linesegs belonging to the subsector are handled with R_AddLine.
R_AddLine handles culling logic for linesegs. R_ClipPassWallSegment is called for linesegs that are not added to the culling system and R_ClipSolidWallSegment is called for those that are. These two functions also cull the lineseg based on what has previously been added to the culling system. After culling R_StoreWallRange is called for the visible parts of the linesegs.
R_StoreWallRange makes further preparations for planes and fofs and draws the lineseg's bottom and top textures to the screen.
After the topmost HWR_RenderBSPNode call has returned, sprites (gathered from R_Subsector) are clipped against walls they are occluded by in R_ClipSprites. They are however not rendered yet.
Then floor and ceiling planes prepared earlier are rendered by R_DrawPlanes. FOF planes are rendered later.
The last major part of level rendering is R_DrawMasked. This function renders all fof walls and planes, sprites and midtextures. (?) (drawnodes) These have been gathered and prepared earlier during rendering. Before rendering, they are sorted into back-to-front order so that they display correctly.
File/add-on loading
The core files are loaded at game startup with W_InitFile. Any files loaded later while the game is already running are loaded with P_AddWadFile, which also begins with a call to W_InitFile. For loading files at the startup, calling W_InitFile is enough since the game is not fully loaded and there is nothing else to update besides what W_InitFile does. But when the game is fully loaded, certain parts of the game need to be updated to handle the new content added by the add-on. This is handled by P_AddWadFile.
W_InitFile starts with error checking: the file is not added if an identical file is already added, or if there is not enough room for more files in the game.
Next the lump information of the file is retrieved, including the location, length and names of the lumps. (todo: link to something about WAD files) PK3 files are also handled as if their files were lumps. Lua and SOC files are treated as single-lump wad files. This information is added to the list of loaded files. (wadfiles) The lump and patch caches of the file are initialized.
All SOC, Lua and shader data in the file is loaded at the end of W_InitFile.
After P_AddWadFile has called W_InitFile, it does various things needed to get the engine to recognize the newly added content. Sounds that were replaced are unloaded. Cached sprite graphics and all hardware renderer graphics are cleared. Some content is loaded from the file with the functions R_AddSpriteDefs, R_AddSkins, R_PatchSkins, S_LoadMusicDefs and R_LoadSpriteInfoLumps. Some content is loaded by resetting parts of the engine by calling R_LoadTextures, P_InitPicAnims, HWR_ReloadModels and HUD functions, which scan all loaded files, including the new file, for content.