Official EGwhaven thread.

Moderator: ETTiNGRiNDER

User avatar
ETTiNGRiNDER
Posts: 15
Joined: Sun May 22, 2016 12:23 pm
Contact:

Re: Official EGwhaven thread.

Postby ETTiNGRiNDER » Tue Sep 27, 2016 3:16 am

Thanks for the fixes!

As best as I can understand, the reason OpenWatcom does not work is because of the precompiled libraries (BUILD, HMI, Smacker, etc.) that are needed to build the EXEs, something was changed across versions that breaks libraries compiled with the older versions of Watcom. It's a known problem.

The SMK playback fix seems to work well. The one SMK that it isn't needed for is the descending stairs clip that plays between levels, it was never a problem presumably because it has no audio (Corvin earlier found that turning audio off was a potential workaround for the problem with the other ones.)

As for whether there are still bugs with EGwhaven, more than I'd like. Tinkering with this again I can see that some things I thought were fixed with regards to the clock problems are only reduced by the changes I made, not actually fixed (jump height still clearly varies if I set different cycles speeds on DOSBox). So I will have to dig a lot deeper into what's actually going on with those issues since it's not the simple omission I initially thought.
adambiser
Posts: 13
Joined: Fri Sep 16, 2016 2:06 pm

Re: Official EGwhaven thread.

Postby adambiser » Tue Sep 27, 2016 5:11 am

You're welcome. Glad it worked out.

If I happen to get some free time, I might look into potential jump fixes, too. I'm not very familiar with the games, so it would be hard for me to compare to the pre-DOSBox look-and-feel of things and really know the ins-and-outs of the game itself though.
User avatar
ETTiNGRiNDER
Posts: 15
Joined: Sun May 22, 2016 12:23 pm
Contact:

Re: Official EGwhaven thread.

Postby ETTiNGRiNDER » Fri Sep 30, 2016 3:25 pm

More updates coming soon, but for now I want to say that I've tracked down the exact reason why some of the random checks flake out while others work. For instance, considering the check for poison traps in a chest, which is a fairly simple one:

Code: Select all

            if(krand()&2 == 0) {
               poisoned=1;
               poisontime=7200;
               healthpic(-10);
               strcpy(displaybuf,"Poisoned Chest");
               displaytime=360;
            }


The error is due to a misunderstanding of the quirky way in which C orders with the operators in question. == (equality check) is evaluated before & (bitwise AND), so while the programmers were thinking that the game was first going to do the "krand()&2" part (giving a 0 or 2 based on the krand return), then check if it's zero, what actually happens is:
1. Do "2 == 0". 2 does not equal 0, therefore the result is "false", which is represented as zero.
2. Do "krand() & 0". The zero comes from the "2 == 0" check before. Anything put through a bitwise AND operation with 0 is 0, so the result is always zero (false).
3. Therefore the result of the whole check is always false/zero, and the code inside the if block never executes. You will never find a poison trap on a chest in the vanilla game.
So the issue has nothing to do with the krand() function itself, or even the practice of limiting its returns with bitwise AND (which is how Ken Silverman himself demonstrated the use of it), only the fact that it's being misused due to an incomplete understanding of the finer ins-and-outs of the C language.

Next version will have cleaner/more correct fixes for these instances.

With regards to testing for clock speed related issues: you can do this on DOSBox by messing with the cycles setting. If you do "cycles = 30000", "cycles = 100000" and "cycles = max" and run the game each time, and see that jump height is demonstrably different each time (or even glitches out, usually in the case of max cycles) then you can tell if the problem is still there or not. Likewise for the other issues of that type. The thing that would require an old machine would be trying to replicate exactly how the game behaved on the devs' machines to know what they intended.
adambiser
Posts: 13
Joined: Fri Sep 16, 2016 2:06 pm

Re: Official EGwhaven thread.

Postby adambiser » Fri Sep 30, 2016 4:02 pm

Good catch!

I haven't yet looked into the jump stuff. Do you know what frame rate the game *expects* to run at?

What I think most games do in order to be able to run at different render speeds is create an internal clock that handles the game logic while the main loop still renders frames as fast as it can. The main loop checks the internal logic clock to see if a tick has passed and if so, the game logic is performed before rendering. This ensures a constant rate.

This is what I had plan to look into when I have the chance. I also develop my own games, so I have a bit of time balancing to do.
adambiser
Posts: 13
Joined: Fri Sep 16, 2016 2:06 pm

Re: Official EGwhaven thread.

Postby adambiser » Fri Sep 30, 2016 7:55 pm

I did a regex search for the bitwise comparison error and it's in several places in WH2.

RegEx:

Code: Select all

[^&]&[^&\n\r\)]+=


Not as often in WH1, but there.
User avatar
ETTiNGRiNDER
Posts: 15
Joined: Sun May 22, 2016 12:23 pm
Contact:

Re: Official EGwhaven thread.

Postby ETTiNGRiNDER » Fri Sep 30, 2016 10:55 pm

The BUILD engine, AFAIK, runs with a timer that has 120 ticks per second.

The insidious thing about the clock timing issues in Witchaven (possibly TekWar too, I haven't got my copy of that running correctly yet) is that most of the game uses the engine timing correctly. There are just a few functions, like the vertical movement of the player, and vertical movement of projectiles, that work in some renegade way that's CPU-sensitive, which causes them to manifest noticeable bugs when run on a system that's considerably faster than what the game was developed on. So it's not quite like your typical case from the 80s where a game might be programmed specifically around the speed of an early IBM PC model with no foresight to that otherwise backwards-compatible machines with faster CPUs would come out; it's more of an accidental thing that only became apparent once systems got significantly faster.

Les Bird's page says "From what I remember, the compiler we used to build the games was Watcom C/C++ V9.5 and the target machine was a 486 50+mhz running MS-DOS V5 or higher." The system I've got for old game purposes has a K6-2 at 333mhz; slow by today's standards but still considerably faster than a 486.
adambiser
Posts: 13
Joined: Fri Sep 16, 2016 2:06 pm

Re: Official EGwhaven thread.

Postby adambiser » Sat Oct 01, 2016 6:35 am

EDIT: Well, making these changes makes the goblins spin around in place quickly while moving, so not a desirable change... leaving the post for now.

I'm still digging around. Yes, I see the notes about 120 ticks.

I found bugs that are related to what you found earlier today. I seem to find it in more and more places...
WH1: WHANI.C:

Code: Select all

sprite[i].ang=((krand()&128-256)+sprite[i].ang+1024)&2047;  // NEW

Subtraction also comes before bitwise AND. I think they intended

Code: Select all

sprite[i].ang=(((krand()&128)-256)+sprite[i].ang+1024)&2047;  // NEW


I haven't got TekWar to compile and run properly yet either. It'll run, but the cop graphics change into some black grid sprites for some reason.
Last edited by adambiser on Sun Oct 02, 2016 10:36 pm, edited 1 time in total.
adambiser
Posts: 13
Joined: Fri Sep 16, 2016 2:06 pm

Re: Official EGwhaven thread.

Postby adambiser » Sun Oct 02, 2016 9:43 pm

EDIT: Might be better to read the next post first. I think it solves the sync/timing problems best.

I have been looking at the jump / synctics issue some and I think the best way to manage things is to force every synctic to be handled individually. (I've been doing this from the context of WH1, not WH2, but it should be the same.)

If calculations are done based on an entire range of synctics, then it's very possible that moving from point A to point B instead of taking all the steps in between can really throw things off. The game clock does hope for 120 tics per second, but I doubt that the game does that on the original systems. Even in DOSBox I only get above 120 when running at 320x200 with cycles > ~50%.

I tried moving the synctic calculation out of processinput() and into playloop() and processing each synctic individually and I started to get a consistent jump height. I did have to alter the JUMPVEL and GRAVITYCONSTANT because the player would jump extremely high otherwise. The reason is that GRAVITYCONST was being applied once no matter whether or how many synctics actually occurred. This erratic behavior made things look good when the game runs at around 40 FPS, but at 120, the player makes quick, short jumps. I think the game seems to run well at around 40 FPS. I might be wrong though. It "feels" pretty good at around that speed, but I'm not familiar with how the game runs on a system from that time.

I know that you likely know all or some of this, but I'm making posts sort of like making notes of my own journey into the code. Perhaps some will help, I don't know.

Here are the changes I made:
WHAVEN.C, playloop():

Code: Select all

...
      // Used to be in WHINP.C processinput().
      synctics=totalclock-lockclock;
      if (synctics > 255)
         synctics=255;
      lockclock+=(long)synctics; // Changed to below.
      // lockclock = totalclock;

      drawscreen(plr);
      if (synctics > 0) {
         // Handle each synctic individually so that all calculations are done the same no matter at what speed the game is running.
         long realsynctics = synctics;
         int s;
         synctics = 1;
         for (s = 0; s < realsynctics; s++) {
            // TODO Lava floor is done in here, but it does damage too quickly!  It needs to be toned down.
            // GRAVITYCONSTANT and JUMPVEL were adjusted in LES.H.
            processinput(plr);

            if (netgame) {
                netgetmove();
                netsendmove();
            }
            processobjs(plr);
         }
         synctics = realsynctics;
      }
        animateobjs(plr);     
        animatetags(plr);     
        doanimations((long)synctics);
        dodelayitems((long)synctics);
...

Then in WHINP.C, processinput():

Code: Select all

...
    tics = synctics;
/*   
   i=totalclock-lockclock;
   if (i > 255)
      i=255;
   
   synctics=tics=i;
   lockclock+=(long)synctics;
*/   

LES.H:

Code: Select all

#define GRAVITYCONSTANT  (1<<5);
#define JUMPVEL          (1<<10);

I had to make some changes to the MAK file to be sure that whobj and whinp are re-compiled when LES.H is changed.

It might be worth looking into adding a synctic accumulator into playloop and only process input only when 3 synctics have occurred to keep the game logic clock at 40 FPS. This might also help with other non-synctics based things, such as lava damage. 120 is just too fast for it's game logic clock in my opinion.
Last edited by adambiser on Sun Oct 02, 2016 10:33 pm, edited 1 time in total.
adambiser
Posts: 13
Joined: Fri Sep 16, 2016 2:06 pm

Re: Official EGwhaven thread.

Postby adambiser » Sun Oct 02, 2016 10:11 pm

Here's the code needed to lock the game at 40 FPS. If the frame rate drops below 40 FPS, the game should play the same, just be slower.

playloop()

Code: Select all

...
      synctics=totalclock-lockclock;
      if (synctics >= 3) {
         drawscreen(plr);
         synctics = 3;
         lockclock+=(long)synctics;
         processinput(plr);

         if (netgame) {
             netgetmove();
             netsendmove();
         }
         processobjs(plr);
         animateobjs(plr);
         animatetags(plr);
         doanimations((long)synctics);
         dodelayitems((long)synctics);
      } else {
         synctics = 0;
      }
...

Make the same change to processinput() as above, but leave LES.H as it was originally.

Without moving drawscreen() into the if..then block, movement and jumping was constant but the player attack frames really fly since that animation is locked to the render frame rate and not synctics.

Locking it to 40 seems easier than hunting down all of the synctic-based vs render-frame-based animations and sorting them out...

EDIT: The playerdead method in WHPLR.C also uses synctics. I've changed the loop in there to be like this:

Code: Select all

...
   while ( totalclock < clockgoal ) {
      synctics=totalclock-lockclock;
      if (synctics >= 3) {
         synctics = 3;
         lockclock+=(long)synctics;
         if (plr->horiz < 100+(YDIM>>1)) {
            plr->horiz+=(synctics<<1);
         }
         drawscreen(plr);   
         animateobjs(plr);     
         animatetags(plr);     
         doanimations((long)synctics);
         dodelayitems((long)synctics);
         nextpage();
      } else {
         synctics = 0;
      }
   }
...


EDIT 2:
I happened to notice that at 40 FPS the footstep sounds weren't playing when walking across the wood bridge at the beginning of level 1. When I drop the FPS to 24 using synctics of 5 instead of 3, they start playing again, so perhaps the game is meant for around 24 FPS..?
User avatar
ETTiNGRiNDER
Posts: 15
Joined: Sun May 22, 2016 12:23 pm
Contact:

Re: Official EGwhaven thread.

Postby ETTiNGRiNDER » Sun Oct 23, 2016 11:46 pm

Hi Adam,
I've been trying the speed fix you recommended and it seems to work pretty well with a fast DOSBox. On a slow DOSBox or on my native machine though, it gets flaky, particularly in that weapons can be swung much faster than they should be sometimes.

Return to “Witchaven”

Who is online

Users browsing this forum: No registered users and 1 guest