Frictional Games Forum (read-only)

Full Version: Wolfenstein 3D Port (In Progress)
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I've wanted to do something for a while that showcased a bit of what HPL3 can really do, so I've been tinkering with this for the past couple days. It's gotten to the point where I feel like I can show off the progress I've made.

(Yes, the above game is, in fact, SOMA.)

Features (so far)
  • Full port of most Wolfenstein 3D textures into HPL3 materials/entities (where applicable)
  • Fully featured HUD system (including dynamically animated BJ Blazkowicz - damaged sprites, shifty eyes, and all)
  • Completely mapped out initial level (future levels a definite possibility)
  • Interactable consumable pickups (the effects are implemented, but didn't make it in the video)
  • Control scheme overhaul to more closely match the original Wolfenstein 3D feel
  • Interactable doors (Auto closing)
  • Static enemy previews(AI has yet to be implemented)
  • Simple weapon previews (Clicking "fires" them, but damage and full frame support is pending)

To Do List:
  • Functional weapon system
  • Enemy AI
  • Menu overhaul
  • High score system
  • Music and SFX
  • Other stuff, possibly
Got a preview of the weapons now. They have their proper animations and correctly proc damage frames. (This is when the gun actually "fires", depletes an ammo, and, later when enemies are implemented, does the damage.)

A whole bunch of stuff got implemented and moved around into separate modules, and now the mod has gotten livelier with the addition of audio.

Kudos. [Image: fire-symbol-symbol-for-facebook.png]
After a little bit of figuring out, I've got pathing working. This was an interesting challenge, though. The built-in pathfinding solution in HPL3 is designed for modern 3D style gameplay, but the pathing for Wolfenstein 3D is still quite tile-based. While I probably could've finagled it to work, it probably would've been just as much work to implement my own thing (if not easier). So that's what I did.

In the following video, the guard's path is generated dynamically (albeit at the press of a button) using a custom A* implementation, which the entity then follows. Along the way, it updates the sprite based on whether it is walking and for how long. As a result, you've got your old-school sprite-based and tile-based enemy pathing.

I still have to wire up the guard's AI states so that it will patrol automatically (not to mention notice and attack the player), but this was a major hurdle and it feels good to get past it.
Here we have the beginning of enemy states. Like I said in the previous update, I had to create a custom pathfinding implementation. The way I had to do it meant that I had to create a custom state machine implementation as well. (I wonder how many other things I'm going to have to make from-scratch custom versions of before I'm done.)

Anyway, in this video there are two states: Stand and Path. Stand is what it sounds like - the dude is just standing there, waiting for something to happen. Path is the idle patrol state, where the enemy follows a predefined patrol path. In the Path state, the enemy is also intelligent enough to detect when it's standing in front of a door and will open it before walking through. (Another thing that I had to recreate.) I also upgraded the A* pathfinding to allow for diagonal movement.

Another thing I had to change that I didn't expect to is all the walls on the map have been converted from static objects to entities. The reason for this is because apparently at the high movement speeds required for Wolfenstein 3D, if the player is sprinting, they will walk right through anything that isn't a blocking entity. There's apparently something interesting going on with the physics regarding static entities that let players walk through them when they wouldn't be able to with entities. (Of course, the alternative solution would be to cover all the walls with block boxes, and that would achieve the same result while involving fewer active objects. However, entities of the type StaticProp aren't much more intensive than static objects, and having collision integrated with the wall objects themselves makes creating future levels easier. (Not to mention there aren't so many wall objects that it would be that much of a concern anyway. Most SOMA levels have more dynamic objects in a given major room than these Wolf3D levels have in the entire map.)

Next up is when things start getting real interesting. Now that the state machine and pathfinding are both implemented, it's about time to start with player detection, and shortly thereafter, combat.
Here's a fun update. The enemy attacking mechanic is now fully implemented.

Here's how it works. First, the enemy is in its default state (either standing or patrolling). When the enemy sees the player (currently just a raycast from the enemy to the camera, ignoring FOV or distance for now), it starts a timer. After two seconds have passed, if the player hasn't taken cover, the enemy will enter the Chase state. In this state, the pathing system will switch to move to the player instead. Every time the enemy enters a new node, it has a chance to stop and fire. The chance increases the closer the enemy is to the player. (If the enemy is in the next tile over, the chance is 100%.)

The firing algorithm is fairly straightforward, and I've tried my best to implement it as it is described in Wolfenstein 3D itself. The gist of it is that it rolls two dice - one to see if the enemy "hit", and another to determine how much damage was dealt. The hit chance and damage are both dependent on the distance between the enemy and the player as well as a few other factors.

The result is that the enemy now follows the player around after being aggroed and will also shoot at the player, dealing damage. The random nature of the shooting algorithm does tend to make the enemies' efficiency somewhat inconsistent, however. Sometimes they are basically stormtroopers, while other times I get two-shot from the opposite end of a long room.

Another interesting challenge I had to tackle for this is that I had to modify the pathing algorithm to make the enemies ignore tiles that had another enemy already within it. Without doing that, all the enemies will eventually converge on the same square and will afterwards never leave it, making them look like some vengeful Hindu goddess with a German accent. While amusing, it's not exactly what I'm going for.

With the changes implemented, the path generation itself is unchanged. Instead, when an enemy queries the generated path for the next tile and finds an enemy already within that tile, it will "wander" by choosing a random unoccupied tile among its immediate neighbours. The result actually makes the enemies look far more organic by having them duck and move around as if avoiding my fire.

One part of this update that I have to complain about, though, is the difficulty that I had in figuring out the player detection. As I said, the process is a raycast, but doing a raycast in HPL3 is deceptively difficult. The most basic way of doing this is the cLux_GetClosestEntity function. However, that function takes two primary parameters - a vector for a starting position, and an angle vector for the ray's direction. The angle vector in particular I was having a difficult time with because the math function for generating an angle vector, cMath_Vector3Angle, between two points seems to be bugged. (Almost no matter what they were, the returned vector between the player's position and any given entity's position was infinity.)

Fortunately, I was able to rectify the problem by using cLux_CheckLineOfSight, which fortunately does take two vector positions. However, the loss of the cMath_Vector3Angle function makes me nervous going forward. It's currently too soon to tell, but trying to implement the player's shooting mechanic without that function may prove to be quite difficult.
Welp, here we have it. The first major milestone.

By this, I, of course, mean the mod now has both enemies at you and the ability for you to shoot back. This was an interesting challenge, to say the least. The fortunate thing is that I didn't end up needing to use cMath_Vector3Angle or cLux_GetClosestEntity to accomplish it. In fact, other than a call to CheckIsOnScreen to make sure the target is actually visible, I didn't need to use raycasting at all. Just a simple check of the angle between the camera and the target sufficed.

Now obviously the only reason I was able to get away with doing it like this was because of the nature of Wolf3D. It being essentially a 2D game (don't let the 3D graphics fool you) and driven by pixel art instead of polygons, using angles alone was able to work. Had the game been even slightly more complex (like, say, Doom), I would've had little choice but to resort to raycasts and hit tests, and I'll be honest - after all the drama yesterday regarding cMath_Vector3Angle, I was not eager to get back on that horse.

Anyway, with this, the mod is now officially playable. You can go through shooting and killing Nazis who are trying to do the same to you, and killing a guard will cause ammo to drop where he died. To demonstrate, here's a pretty basic run through of the mod's entire first level. (Attack dogs, unfortunately, aren't quite implemented yet.)

(Disregard the enemy around 1:36. It was an old placeholder billboard that I somehow missed when converting them to proper enemy entities.)

Of course, the enemies are still pretty stupid as evidenced by how easy it is to ambush them. (You could gun down their friend standing two feet away and they literally couldn't care less.) There are also some tweaks that I need to do to the damage formula, as right now getting one-shots on guards is a bit too easy. However, this is a major step in the progress of the mod. After those and a couple other changes, the game itself will essentially be done. After that, I plan to remake the menu system as well to be reminiscent of Wolf3D. (Complete with persistent high score boards. Tongue ) I also plan on making the levels for the rest of episode 1, including the secret level, so that will be 10 levels in total for the finished mod. (I'd consider doing more, but I'd worry that id/Bethesda might not be to keen on me essentially re-uploading a free copy of their game somewhere without their permission. :/ )