Bringing Wolfenstein 3D to Doomsday

2»

Comments

  • Looks like sound effects are not that easy: some are split over multiple chunks. For example, the first one that says "Achtung!" is split over the first and second chunk. I'll need to see if there is a way of finding out how many chunks need to be glued together.

    EDIT: OK, I think i got it. Now to get back to testing it...
  • OK, I got the digital sound effects now. We can't have anything be straight-forward, can we? *sigh* I'll take a look at the WLF music data now. At least I know it has been properly extracted.
  • I have now uploaded the specifications how the AdLib data was sent to the card. There is no point in going deeper into how it works, as that would essentially mean writing the specifications of an AdLib emulator. We can decide at a later point how to handle it, whether we should write our own solution or use an existing library.

    So yes, this means we are done with the data formats now, took less than a month to get it all sorted out. The question is what's next? My suggestion would be to start documenting the game rules while the Doomsday developers prepare the file system to be able to handle Wolfenstein's weird way of storing data all over the place. I am in no hurry to play Wolfenstein in Doomsday, so please don't let my side-project disrupt your roadmap for Doomsday 2, if it's not yet time to do the file system then this can wait.

    I'll see if using the more modern ports for iOS or web browsers is more feasible than the original source or the source of Chocolate Wolfenstein 3D. Since I now have the data it is no longer relevant to stick to vanilla (or chocolate) code anymore.
  • Ten days since my last post, and not much to show for. I'm currently doing AI, but there is just so much of it, like a million functions with exceptions on top of exceptions... I don't think that documenting them as they are in pseudocode is going to be feasible. I'm going to sketch it out on paper instead and try to de-hardcode things. We need less functions in general and less hardcoding.

    And while I'm at it, I might also make it co-op friendly. One issue is that many animations, like shooting, only have one direction. The default would be either to display nothing if the sprite is missing, display the last sprite, or display the sprite facing the player. It should be possible to add the missing frames as a mod though. There is a project called SplitWolf, it's a port that supports co-op gameplay and it adds the missing frames on its own:
    http://www.moddb.com/mods/splitwolf

    We could either include these sprites with the engine (with permission of course) or support is as a plugin. That's not a priority at the moment though.
  • I would strongly suggest you avoid making functionality changes in the process of exploring this. In old games like Wolfenstein 3D, the AI in particular is usually less than well organized and there are most likely dozens of hidden dependencies and/or subtleties that may be accidentally overlooked.

    Personally, if I were to approach this then I would look around for a C++ port of Wolfenstein 3D with a proven level of vanilla accuracy as far as the playsim mechanics are concerned and then use that as the basis of a new game plugin. I'd try to move it lock stock and barrel into a game plugin with as few changes as possible. Only once the game plugin is up and running and has been regression tested vs the original game, would I then consider functionality changes.
  • *sigh* I think you might be right. But look at code like this:
    PUBLIC void A_DeathScream( entity_t *self )
    {
    	switch( self->type ) {
    		case en_mutant:
    			if( g_version->value == SPEAROFDESTINY ) {
    				Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/033.wav" ), 1, ATTN_NORM, 0 );
    			}
    			else {
    				Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/037.wav" ), 1, ATTN_NORM, 0 );
    			}
    			break;
    
    		case en_guard:
    			if( g_version->value == SPEAROFDESTINY ) {
    				Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( dsodsounds[ US_RndT() % 6 ] ), 1, ATTN_NORM, 0 );			
    			}
    			else {
    				Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( dsounds[ US_RndT() % 6 ] ), 1, ATTN_NORM, 0 );
    			}
    			break;
    
    		case en_officer:
    			if( g_version->value == SPEAROFDESTINY ) {
    				Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/046.wav" ), 1, ATTN_NORM, 0 );
    			}
    			else {
    				Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/074.wav" ), 1, ATTN_NORM, 0 );
    			}
    			break;
    
    		case en_ss:
    			if( g_version->value == SPEAROFDESTINY ) {
    				Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/035.wav" ), 1, ATTN_NORM, 0 );
    			}
    			else {
    				Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/046.wav" ), 1, ATTN_NORM, 0 );
    			}
    			break;
    
    		case en_dog:
    			if( g_version->value == SPEAROFDESTINY ) {
    				Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/031.wav" ), 1, ATTN_NORM, 0 );
    			}
    			else {
    				Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/035.wav" ), 1, ATTN_NORM, 0 );
    			}
    			break;
    
    		case en_boss:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/019.wav" ), 1, ATTN_NORM, 0 );
    			break;
    
    		case en_schabbs:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/061.wav" ), 1, ATTN_NORM, 0 );			
    			break;
    
    		case en_fake:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/069.wav" ), 1, ATTN_NORM, 0 );			
    			break;
    
    		case en_mecha:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/084.wav" ), 1, ATTN_NORM, 0 );			
    			break;
    
    		case en_hitler:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/044.wav" ), 1, ATTN_NORM, 0 );
    			break;
    
    		case en_gretel:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/115.wav" ), 1, ATTN_NORM, 0 );
    			break;
    
    		case en_gift:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/091.wav" ), 1, ATTN_NORM, 0 );			
    			break;
    
    		case en_fat:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/119.wav" ), 1, ATTN_NORM, 0 );
    			break;
    
    		case en_spectre:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "lsfx/062.wav" ), 1, ATTN_NORM, 0 );
    			break;
    
    		case en_angel:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/098.wav" ), 1, ATTN_NORM, 0 );
    			break;
    
    		case en_trans:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/070.wav" ), 1, ATTN_NORM, 0 );
    			break;
    
    		case en_uber:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/082.wav" ), 1, ATTN_NORM, 0 );
    			break;
    
    		case en_will:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/072.wav" ), 1, ATTN_NORM, 0 );
    			break;
    
    		case en_death:
    			Sound_StartSound( NULL, 1, CHAN_VOICE, Sound_RegisterSound( "sfx/090.wav" ), 1, ATTN_NORM, 0 );
    			break;
    	}
    }
    
    I mean, come on, who thought this was a good idea? Not only do some actors have a different sound for SOD, but one of them even has a randomised sound effect. Same enemies for different games should be different entities and each entity should have a sound table that lists its possible sound effects to play. A mod could then override that table and add new sound effects.

    Another example, the ghosts and the ubermutant both damage the player when they get within one unit of distance. However, the ubermutant compares tile coordinates while the ghosts compare the actual coordinates. The ghosts have their damage dealt as an exception in a function meant for multiple enemies, but the ubermutant has its own shooting function where it does that. There is no good reason not to use the same functionality, but that's the way it is now. It's a mess.
  • Re: Code example

    That sort of thing is very common in code like that. Particularly older code bases. While we might look at it today and cringe at the amount of needless repetition and "in-lined" special case behaviors, one must bear in mind that this was a commercial project, with a very limited time budget, in an age where long term maintainability wasn't a primary concern.

    Although I would much prefer a clean code base that adheres to modern standards, its far more important that the accuracy of the game mechanics aren't lost in the porting process. We can always clean up the code later, as and when we need to focus on (or understand) a specific area.
  • Yeah, I was too eager when I made that proposition above. I'll just skip the AI in my documentation for now, I have already lost too much time on it. There are other less messy parts to adapt instead. While I'm at it, do you have an idea when it would be feasible to start thinking about implementing Wolf3D in Doomsday?
  • Essentially there is no real reason that porting the game mechanics to a new game plugin couldn't begin almost immediately. Given a firm understanding of the game plugin architecture and high level game mechanics, of course. However I very much expect that the plugin architecture will need to develop in the process. From our perspective, this is primarily a matter of understanding the needs of Wolfenstein 3D and figuring out what we'd need to change to accommodate it in game plugin form. The more you can do to ease this part of the process the sooner development can potentially begin.

    Obviously we have our own goals and roadmap to consider but we can think about slotting in any plugin architecture changes for 1.16 if you did want to get started immediately. It all depends on what is actually needed.
  • I am not a coder so I may be mis-interpreting, but why not work on having Dday convert the Wolf3D maps into something the 'player' can fly around in as a camera (i.e the maps fully textured and complete with stationary mobjs, with un-interactive guards and such) or maybe even move around in by temporally using Doom's player physics (i.e copied from Dday's Doom plugin) and then work on things closely related to the physical map, such as doors and push walls?

    For an example, from my memory (which may of course be wrong) of the released Doom Alphas and Beta's, it looks like ID first rendered the map with un-interactive mobjs and the nesscerry player physics to simply move around. Then it looks like they did basic environment stuff (i.e doors). Making the mobjs actually interact with the player and proper player physics didn't appear to come until later.
  • What you suggest has a much greater up front cost. One would have to implement a map data converter (from grid to geometry), a data translator (from wolf to doom), and then a bunch of data asset readers for the textures, etc... And to do all that you'd have a lot of learning to do about our architecture.

    Another approach is to use the data resources of another game to begin with and get the bulk of the Wolfenstein 3D game logic up and running quickly. Then introduce data asset interpreters over time. There is no requirement for things to look and sound like they do in the original game from the outset.

    Certainly, there are various ways to tackle a porting task such as this.
  • I've never done any porting, so I have no idea how to approach the plugin. My first instinct too was to have a barebones implementation that lets you load a level and walk around in it. Then there would be features added gradually, like texturing the level, making doors work, placing map objects and then giving life to everything. That's how the OpenMW project evolved over the years:
    https://openmw.org/media/

    But if you have a better idea let me know, you are the ones with actual experience. The levels are stored as pairs of 64x64 tile maps; the first map is the level geometry (walls, floors), the second map is the map objects (treasure, corpses, obstacles). I know Doom used polygon-shaped sectors, so would it be reasonable to treat every tile as a square-shaped sector?
  • HiPhish wrote:
    The levels are stored as pairs of 64x64 tile maps; the first map is the level geometry (walls, floors), the second map is the map objects (treasure, corpses, obstacles). I know Doom used polygon-shaped sectors, so would it be reasonable to treat every tile as a square-shaped sector?
    To say for sure, I'd have to look more closely into how the grid is interpreted into geometry in the original game and how that grid is used by the playsim. Are doors represented as grid cells in and of themselves? If so then a direct translation to Sectors would make a lot of sense as an initial translation, however it would better to combine neighbour cells in the same "room" into a single Sector, if the cells share the same properties (could be done later).
  • edited 2014 Oct 29
    I'm not particularly knowledgeable on the Wolf3D engine, so I offer the below with the possibility that it may not be accurate; there are forums of dedicated Wolfer's that basically know the engine inside out due to the history of modding the game via source code and in some cases these mods have features that rival the likes of ZDoom and Doomsday's external modding features.

    Each tile can have a single floor code or single wall tile on plane 1 and a single mobj on plane 2. There looks to be a hardcoded of floor codes in the game, about 40, though I don't know the maximum number the code could support. The game code has a limit of 64 wall tiles/doors with doors having to be at the end of the list.

    Tiles with different floor codes act like Doom's sectors. So long as there isn't a wall or closed door separating them, they are considered linked as far as sound goes (i.e the players gunfire alert's all non-deaf bad guys on linked floor codes). As is my understanding, floor codes don't have to be linked; a Doom comparison would be joined sectors (you could have a single floor code tile on one side of the map in order to alert a bunch of bad guys on the other side of the map, for instance).

    A few floor codes have additional properties tied to them; Wolf3D has a floor code that makes a bad guy standing on it deaf, for instance.

    The engine does appear to have the ability to have a tile without a floor code or wall. But I've personally not ever heard of that being used in a Wolf3D map nor have I personally made a test to see what happens in this case (but I imagine it's been done).
  • I've taken a look at the uncompressed map (yay for the extractor ^^), horizontally connecting doors are represented by the byte-sequence 0x5A 0x00 and vertically connecting doors are represented as 0x5B 0x00. Both are on the first map (architecture map). Batching neighbouring tiles into larger groups goes without saying of course, I'll have to find an algorithm for that when the time comes.
  • Do these "floor codes" change dynamically at any point? (I.e., would batching neighbour cells with common properties into Sectors be invalidated at some point during play? If so then batching may not be worth it).
  • DaniJ wrote:
    Do these "floor codes" change dynamically at any point? (I.e., would batching neighbour cells with common properties into Sectors be invalidated at some point during play? If so then batching may not be worth it).
    Aside from doors and push walls opening/closing, I believe floor codes can never change.

    Both doors and push walls are placed as wall tiles, with the later then having a special mobj type placed on top of it to turn it into a push wall.
  • I'll have o look deeper into it (been focusing on AI only), but from what I can tell so far doors are at runtime replaced with the door objects and a state machine is keeping track. The level has a list of doors, and when an actor tries moving it checks the target tile to see if it's a door. If it is and the door is not open it either waits for the door to finish opening or opens the door if it is closed. The states of a door are closed, opening, open and closing. Doors do not separate same areas; you can see it when you start a new game, the first level starts in your cell and if you fire a shot the guard outside will hear it.
  • HiPhish wrote:
    Doors do not separate same areas; you can see it when you start a new game, the first level starts in your cell and if you fire a shot the guard outside will hear it.
    Indeed, the same floor code is used on both sides of the door. In Doom terms, it would be like joining sectors.

    Random image, that I'm sure most are familiar with; a map editor shot of Wolf3D E1M1:
    http://hwolf3d.dugtrio17.com/projects/hwe7.gif
  • Any update on this?
  • I've been busy with other tasks, but I still wish to continue. The GitHub repo always contains my latest progress, so even if I were to give up anyone could continue from where I left.
  • Quick update, the converter for AdLib sound effect to WLF format is complete now. Thanks to sune for writing the Java version, without him it would have taken me a century to figure this out.

    This means we now have all the data formats under control and at least to my knowledge this makes my extractor programs the first complete and open-source extractor for Wolfenstein 3D. Other extractors I know of are either closed-source or incomplete.
  • nice, good job :)
  • that is impressive. now how long before we can see some progress on the getting it into the engine?
  • No clue; the source code is quite a mess, jumping all over the place. Besides, even if I were done tomorrow reading everything and writing it down, there is still the engine-side of things. The Doomsday Engine needs to incorporate these things gracefully, not in a hacked on add-on that introduces a new if-else block for every little thing. The bulk of work is already done though, the big area left is the enemy behaviour, which is sadly the biggest mess. It looks like every time someone at Id had a new idea they just tacked it on with some if-else block.

    I am also in the process of overhauling the document, Markdown has its limits and you hit them very early. I transitioned to reStructuredText instead, it's similar to Markdown, but stricter and more feature-rich.
    http://docutils.sourceforge.net/rst.html
    Vim is very helpful in this, so it's not like I'm editing every line by hand. But I am still new to reStructuredText, so it takes a while to see what looks and reads best.
  • it's still amazing what your able to do. and with the fast progress you have had i have no doubt that we will see wolf 3d in dday before the year is done.
  • We'll see about that, but keep in mind that I don't work all the time on this. I have never done something like this before, so it is amazing to me as well that I was able to do it. This is the sort of project where you grow along with it, and it is worth it for the extra experience.
  • the hard part for you is to incorporate it into dday once you have everything figured out. thats the biggest hurdle since the last thing is the AI so once thats done translating it to dday should be straightforward with the devs helping.
Sign In or Register to comment.