Jump to content
The Dark Mod Forums

Thelvyn's Thread


New Horizon

Recommended Posts

  • Replies 157
  • Created
  • Last Reply

Top Posters In This Topic

I assume you are referring specifically to Doom 3 scripting versus SDK coding? It would make no sense to talk of "scripting" and "coding" as distinct general concepts outside of some specific environment like this, because they are not.

 

The idea behind doing things with scripts in Doom 3 is to expose functionality that can be modified by the mapper. If it is hardcoded in the SDK you have an inflexible design which is not so amenable to customisation in this way (I have heard it said that Half-Life 2 was a culprit at this). The ideal design has a series of self-contained, atomic functions coded in the SDK which necessarily require code-level support (such as "create entity" or "translate object"), while all of the control logic is written in customisable scripts which can be overridden or modified by mappers on an individual basis.

 

It is certainly not a question of "hard" versus "soft" programming or anything; they are two different tools for two different problems.

Yes I understand the distinction. The scripting is usually setup to be simple enough(Perhaps that is not the proper way to put it but thats how I see it) that the mappers can use it. A lot of people cannot learn C++ but can do scripting as it is in general a lot simpler is what I meant. Some people just cannot wrap their head around C++ it would seem.

 

I don't think we're off to a bad start, but you are misunderstanding the reason why we do things the way we do.

 

As I was saying earlier, this is just our process. It has nothing to do with not trusting you as a person, it's simply what we have had to do in order to figure out if members are serious about sticking around. In the beginning, we had many people sign up and we thought, "yay, we have a new member", only to never hear from them after they agreed to take on a fairly important task. As you might guess, this ends up wasting weeks of our time...when we could have assigned the task to an established member.

 

I pointed out that I've already skipped the interview process for you and placed you into contributor status. This is a sign of trust on our part. Many can't even be bothered to make it through the interview process.

Thank you yes I see that and I do appreciate it.

 

Since you appear extremely eager to do some serious work...we will make an exception and open up the source code to you. Were you able to access the threads in the link I sent you by PM? If so, just follow those directions and contact sparhawk with the information he needs to set up your CVS account so that you can access the code. You can either use wincvs or tortoise cvs. I personally use tortoise since it integrates with the windows shell.

Yes I am serious.

I am reading that thread and doing what is suggested now.

I will try tortoise as you suggest.

 

We do not distrust you, as you've given us no reason to...but we do have a system that we usually follow for our membership system.

 

At any rate, contact Sparhawk when you have gotten the software setup and you should then be good to go.

I understand. Thank you for giving me a chance. I really do just want to help. I want to see this come to completion as much as you do. I like to always have a side project to work on anyway and that has been lacking recently. Will be really nice to play a more modern engine version that doesn't suck as badly as TDS ended up.

I haven't lost my mind. It's backed up on disk!

Oops bad sectors damn floppy's!

Link to comment
Share on other sites

Yes I understand the distinction. The scripting is usually setup to be simple enough(Perhaps that is not the proper way to put it but thats how I see it) that the mappers can use it. A lot of people cannot learn C++ but can do scripting as it is in general a lot simpler is what I meant. Some people just cannot wrap their head around C++ it would seem.

This is more or less true, but that doesn't mean that scripting is in any way inferior to programming. Making something easier doesn't necessarily make it less powerful. :)

 

Scripting is really quite nice once you start using it. Not having to recompile is quite nice. In Doom 3 you can even reload scripts without having to restart the engine (console command "reloadScript"), which is really handy (though you do need to restart the current map, which is fair enough). None of this "make a small change, wait for it to compile, wait for Doom 3 to start, load up the map" stuff. Just make the change, save the file, alt-tab into Doom 3, type reloadScript, reload the map, and you're done. It sounds like a minor thing but it really improves the workflow when you're not waiting around so much.

 

Glad to hear we're making an exception about CVS access.

My games | Public Service Announcement: TDM is not set in the Thief universe. The city in which it takes place is not the City from Thief. The player character is not called Garrett. Any person who contradicts these facts will be subjected to disapproving stares.
Link to comment
Share on other sites

I believe you. So we can both be happy you with your script and I with my C++ :P

Each to his own after all :)

I haven't lost my mind. It's backed up on disk!

Oops bad sectors damn floppy's!

Link to comment
Share on other sites

Most of the bigger tasks probably involve at least some scripting I'm afraid, so you're going to have to get used to it. :)

 

Still, there are a few C++-only tasks, some of which have been mentioned already. Fall damage, modifying AAS, the mouse handler, that sort of thing.

My games | Public Service Announcement: TDM is not set in the Thief universe. The city in which it takes place is not the City from Thief. The player character is not called Garrett. Any person who contradicts these facts will be subjected to disapproving stares.
Link to comment
Share on other sites

Well as long as I can add stuff to make that easier it shouldn't be too awful at least.

As long as it at least closely resembles C or C++ I will manage if I have to.

I haven't lost my mind. It's backed up on disk!

Oops bad sectors damn floppy's!

Link to comment
Share on other sites

I think that you can fix the pathing into water by making the change in function idAASLocal::WalkPathValid

 

see here

// don't optimize through an area near a ledge
if ( file->GetArea( reach->toAreaNum ).flags & AREA_LEDGE ) {
	continue;
}

 

We could just add in

		if( isthiswateroverhishead ) {
		 continue;
	}

 

I do not know what you use for water yet so I cannot be more specific then that at this time.

There may be issues with water detection in idAI as there are in vanilla doom3 sdk code as well to deal with.

 

At least I hope this will fix it :)

I haven't lost my mind. It's backed up on disk!

Oops bad sectors damn floppy's!

Link to comment
Share on other sites

Just to throw my two cents in--we probably want to check a spot lower than the actual head/mouth. Otherwise an AI could be in water up to his shoulders but still making regular attacks or other things that look silly. Probably anything higher than the AI's chest should be off limits.

Link to comment
Share on other sites

That looks promising, good find. We just need to figure out how to set it for an individual AI. Some, like undead, will not care. Some will be able to get thru deeper water than others (rats vs. humans, assuming rats don't swim). I don't see any reference to the AI calculating the path in that function. We need to back out where this gets called from idAI...

 

Looks like it's in idAI::PathToGoal. This calls idAASLocal::WalkPathToGoal. We may have to add an argument to WalkPathToGoal for the max acceptable water level, and put that argument in for the specific AI that's calling idAI::PathToGoal.

Link to comment
Share on other sites

Yes thats where it gets called from. I went down through all the calls to find this.

Why dont we add a variable to check in the idAI class instead of overloading that function.

I haven't lost my mind. It's backed up on disk!

Oops bad sectors damn floppy's!

Link to comment
Share on other sites

Thing is, the idAI class doesn't seem to see any of the individual points along the path. That all seems to be handled calculated within AAS_Local, which is why I thought we could input another optional argument there for the max allowable water level, so we can check it where you've pointed out, in the loop that finds the intermediate points.

 

I just made the following change, with a temporary constant water level, to see if it would work at all.

// TDM: Don't walk in water over the max height possible
testPoint = goalOrigin + file->GetSettings().gravityDir * MaxWaterHeight;
contents = gameLocal.clip.Contents(testPoint, NULL, mat3_identity, -1, NULL );
if( (contents & MASK_WATER) > 0 )
continue;

 

Do we have a testmap for this? It needs to be an incline that goes into water continuously.

Link to comment
Share on other sites

Yes I see what you mean we do not have a pointer to the ai at this point. I thought we did for some reason.

 

If we do this we need to make it a default so other code that calls this(If any) wont be broken. Perhaps we could just pass in a pointer to the AI ?

If its NULL we could ignore it. This would give us flexibility for other things as well down the road. For example if they can fly or if they can jump really far etc. If we are going to put in another parameter why not make it more useful while we are at it ?

I haven't lost my mind. It's backed up on disk!

Oops bad sectors damn floppy's!

Link to comment
Share on other sites

Yes I see what you mean we do not have a pointer to the ai at this point. I thought we did for some reason.

 

If we do this we need to make it a default so other code that calls this(If any) wont be broken. Perhaps we could just pass in a pointer to the AI ?

If its NULL we could ignore it. This would give us flexibility for other things as well down the road. For example if they can fly or if they can jump really far etc. If we are going to put in another parameter why not make it more useful while we are at it ?

 

Yeah, that sounds like a good idea to pass an optional AI pointer. Later on, we could add things like AI that stick to the shadows to hide from other AI by testing the light level at points, etc.

 

I guess we could call back a bool function on the AI giving it the point that the pathing algorithm is looking at as a potential point along the path, and let the AI determine if it likes that point or not. There are many ways of doing it though, whatever you think is best.

 

I just want to try it out with a constant water value to make sure that throwing out a point here does in fact work, but don't have a test map for it. :)

Link to comment
Share on other sites

I like the idea of the callback. However there are different kinds of things checked here though, so we could get a bitmask containing things we would want a callback on.

 

Then just check the corresponding bit as we progress. If they want a callback we call and if they return false we continue.

 

So we could handle all kinds of different situations with minimal overhead if it is not needed after all.

 

As long as we make it virtual then we can just derive other classes from it later for different behavior.

 

So then we would just get a size_t back which would minimize the impact when it is not needed.

I haven't lost my mind. It's backed up on disk!

Oops bad sectors damn floppy's!

Link to comment
Share on other sites

Realised I could test it out with the giant wall of water in test_water.map

 

It doesn't seem to be working yet, don't know why. Here is the code I tried:

 

bool idAASLocal::WalkPathValid( int areaNum, const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin, int travelFlags, idVec3 &endPos, int &endAreaNum ) const {

// ...

		// find the closest floor face split point on the path
		if ( !FloorEdgeSplitPoint( p, reach->toAreaNum, pathPlane, frontPlane, true ) ) {
			continue;
		}

		// TDM: Don't walk in water over the max height possible
		idVec3 test(0,0,1);
		testPoint = goalOrigin + test*10.0f;
		contents = gameLocal.clip.Contents(testPoint, NULL, mat3_identity, -1, NULL );
		if( (contents & MASK_WATER) > 0 )
		{
			DM_LOG(LC_AI,LT_DEBUG)LOGSTRING("AAS Pathing Point %f, %f, %f thrown out due to water contents\r",p.x, p.y, p.z );
			continue;
		}

// ...

 

I do see the message in the log that a point has been thrown out because it's in water. However, the AI still end up walking into the wall of water and drowning when I fire a noisemaker arrow in there. Maybe something else is compelling them to walk to the goal. Maybe we need to do a separate test on the goal point itself, otherwise it assumes it can get to the goal point?

 

I'm not sure why it's not working. You would think if it the point was not valid, it wouldn't walk there. Anyway, PathToGoal is what returns true/false if they can get to the goal, PathToGoal calls idAASLocal::WalkPathToGoal . So if the point is completely surrounded by water, WalkPathToGoal should be false.

 

It looks like there are some cases where WalkPathValid returns false but WalkPathToGoal can still return true. I think the thing we really want to return false if the point is completely surrounded by water would be RouteToGoalArea. Maybe if RouteToGoalArea returns true, it gets confused. [EDIT: Maybe not, there may be other points where WalkPathToGoal is supposed to return false]

Link to comment
Share on other sites

Why would anyone want to steal your code anyway ? What possible use could they have for it ?

Are they going to compete with you ? I do not understand.

 

The code that we implement is usefull for a lot of games. It's our code and we want to keep control when it is actually released, because we are working on this for a long time now and there is a reason why we came to that agreement. Also a lot of mods already asked for several features that we implement, and if we give the code to somebody, before it is finished, it would be nice to get something back in return as well.

 

Also we already had bad experience with releasing unfinished stuff, because people tend to think that the release means a proper release and treat is as such, which in turn means that the samples were considered as a bad reputation because of that.

 

As far as scripting goes I am sure you have very talented people working on that but I would prefer to not be one of them.

 

I also don't like scripting that much, but we all have to do it at one time or the other. Especially when you write some code that interfaces with scripting, you must implement the first scripts yourself, because who else should do it? The priogrammer who implemented the code knows best how to write a sample that shows how it is used from scripting, and of course our main audience will be non-programmers, so we must make sure that they can do all the neccessary stuff from within scripting, which means that we have to write it ourself first.

 

I guess the real point is I feel like you do not trust me and that bothers me which is how we came to this point after all.

 

It's nothing personal. When we had problems we discusses how to tackle such issues in the future and this is the procedure that we defined. Also in otehr high-profile mods it's pretty much the same, and some mods that I looked at are even more stringent. So this is not because we distrust you in particular, we just want to make sure that we get people dedicated to the mod. It's not only about stealing something though, it's also that we don't want to waste time with people who think they want to work for us and then dissappear after some days or weeks. I hope you do understand that this causes problems for is, if we assign something to a person, thinking that this task is being worked on, only to discover after some time, that we wont get anything. This disrupts our planing which in turn hurts team morale as well because poeple have to wait for stuff that they need, which means that they become bored while waiting and turn to other things.

 

If you send me an email with your key please make sure to include "DARKMOD" in the subject line. Otherwise it will get lost among the spam. If you already sent a key, send it again, because I sure haven't seen it and probalby accidently deleted it. Last time I cleared my inbox it was about 500 spams mails.

Gerhard

Link to comment
Share on other sites

Ishtvan@

 

I do not think we are looking at the same code are we ?

 

From my version of idAASLocal::WalkPathValid

// find the furthest floor face split point on the path
	if ( !FloorEdgeSplitPoint( endPos, curAreaNum, pathPlane, frontPlane, false ) ) {
		endPos = origin;
	}

 

Your version as posted is

 

// find the closest floor face split point on the path
		if ( !FloorEdgeSplitPoint( p, reach->toAreaNum, pathPlane, frontPlane, true ) ) {
			continue;
		}

You are getting this from idAASLocal::WalkPathValid ?

It looks like its from an entirely different function.

 

Which is very strange indeed since this is the code I received from you.

 

sparhawk@

 

I will not be releasing it to anyone. Thats not my department. I would not have the right to do that.

 

Scripting stuff: Yes well documentation is one thing, I have to do that at least if I am writing stuff to interface with the script language. That is reasonable and expected.

 

I do not know anything about other mods, I have worked on a BBS, muds(2) and the rest has all been either paid contract work or my own personal utility's, free ware and shareware although I stopped doing that(shareware) a long time ago. Now I either release it as free ware or I just don't release it at all.

 

As I said I have never written any game code before this, this is very different then any other project I have contributed to before. Hence my surprise and shock. I am not used to it is all. Every other project was ok your helping heres cvs access we need such and such done please. But I believe we are past that part now yes ?

 

I have already replied to your email so I did not label it darkmod. Couldn't you just set a rule and send it to a directory if it comes from certain people ? I do this, all the email from this forum for example goes to its own directory so I do not have to look through the spam to find it.

 

Btw the first file I sent was saved directly from puttygen.exe using the button on the program. All I did was supply a filename for it which I then attached to the email. Strange that it was broken.

I haven't lost my mind. It's backed up on disk!

Oops bad sectors damn floppy's!

Link to comment
Share on other sites

I ran into this problem: You need to copy and paste the key from the program itself. Telling it to save a file won't work, because it's a different format that sparhawk can't use.

 

So I suggest you re-generate the key, copying and pasting the key from the program into a file, and then send it to sparhawk again with "darkmod" in the subject line.

 

It's a total pain, I know. It took me about three tries before it worked. :)

 

Darn CVS.

My games | Public Service Announcement: TDM is not set in the Thief universe. The city in which it takes place is not the City from Thief. The player character is not called Garrett. Any person who contradicts these facts will be subjected to disapproving stares.
Link to comment
Share on other sites

I do not think we are looking at the same code are we ?

 

...

 

You are getting this from idAASLocal::WalkPathValid ?

It looks like its from an entirely different function.

 

Yeah, I'm also looking at WalkPathValid. FloorEdgeSplitPoint appears in there twice, once on line 166, and once on line 206. I got the impression that the later call was within the loop where they were trying to calculate the intermediate points.

 

I picked a point within this loop:

for ( reach = area->reach; reach; reach = reach->next ) {

Because it looked like that was where the intermediate points of the path were found/validated. A reachability connects two AAS areas, and it looks like they are walking through reachabilities and picking points here, but I'm not positive.

Link to comment
Share on other sites

I am still trying to piece together where all of this fits together. So many walk functions!

In the time I have this week I am going to try and map it all out to see what pieces go where.

Perhaps then we can make a more informed decision about where we need to splice into it at.

Hopefully I will have cvs access by the end of the week so I can get everything together and determine how I am going to test. So with any luck I will have a working patch by next week.

 

Unless you (the team) would prefer I do the falling AI first of course.

I will need more time to get that correct. I have no doubt about making them take damage but I do doubt my math skills are up to scaling it correctly. Perhaps someone with stronger math skills could assist with that part of it ?

 

Sorry I only have a ged and that was over 20 years ago. I went to chester high(Chester PA) which I am sure means nothing to anyone else here but it was 99% black and I am white. I was not very popular unfortunately and had to fight everyday sadly.

 

I would rather tell you this now then have you believe I can do something I am unsure I can in fact do.

Computing velocity and the effects of gravity and I suppose the hardness maybe of what they are impacting is probably beyond my math skills. Like water for instance.

I haven't lost my mind. It's backed up on disk!

Oops bad sectors damn floppy's!

Link to comment
Share on other sites

But I believe we are past that part now yes ?

 

Right. ;)

 

I have already replied to your email so I did not label it darkmod. Couldn't you just set a rule and send it to a directory if it comes from certain people ?

 

Of course I can. I already have a list of several hundred people who I have to filter... <_<

 

I do this, all the email from this forum for example goes to its own directory so I do not have to look through the spam to find it.

 

I don't know if you read the CVS howto, that I wrote in order to make it easier for people to sign on on CVS. It says there that this forum cvan not be used for sending the key because it breaks it. I also use filters for that purpose though, but it's still easier if you can write DARKMOD in the subjectline, because I already use that same filter for other stuff as well. If I have to filter for your email specifically, I have to see it first, but I already included you in my filter, so now your mails will not get lost anymore.

 

Btw the first file I sent was saved directly from puttygen.exe using the button on the program. All I did was supply a filename for it which I then attached to the email. Strange that it was broken.

 

You should NOT send me the file that it generates, you should send me the key that says something like "use for OpenSSH" or similar. And this key you can not save, because Putty tells you that you have to paste it into your ssh server, unless there is a newer version which also allows to save this particular key. It is one single very long line usually starting with AAA.

Gerhard

Link to comment
Share on other sites

I am still trying to piece together where all of this fits together. So many walk functions!

In the time I have this week I am going to try and map it all out to see what pieces go where.

Perhaps then we can make a more informed decision about where we need to splice into it at.

Hopefully I will have cvs access by the end of the week so I can get everything together and determine how I am going to test. So with any luck I will have a working patch by next week.

 

Unless you (the team) would prefer I do the falling AI first of course.

I will need more time to get that correct. I have no doubt about making them take damage but I do doubt my math skills are up to scaling it correctly. Perhaps someone with stronger math skills could assist with that part of it

It's up to you what you want to do first. Also, I'd be happy to write the mathematical part of the damage from falling depenence on velocity if you want, just tell me where it should go (it should be somewhere where we can call GetPhysics()->GetVelocity(), and also get access to the trace_t data filled in by the ::Collide method, that' why I was thinking just put it in the Collide method ). It won't be too bad, pretty much just testing if the velocity is above some threshold, and if so squaring it and multiplying by some tweak parameter. The harder part is resolving the velocity into a direction parallel to the collision normal. That's why we need access to the trace_t data from ::Collide, to get the normal vector of the surface hit.

 

For math stuff, you might want to find a good book/website to review vectors, as they're used a lot in the Id code. You'll probably need vectors for most things. If you do more in physics, you'll also need to understand rotation matrices to rotate the vectors, and it may help to review high-school level kinematics (galileo equations, forces, impulses, etc).

Link to comment
Share on other sites

For math stuff, you might want to find a good book/website to review vectors, as they're used a lot in the Id code. You'll probably need vectors for most things. If you do more in physics, you'll also need to understand rotation matrices to rotate the vectors, and it may help to review high-school level kinematics (galileo equations, forces, impulses, etc).

 

Highest we had was algebra 2 jeez. My junior year there they were teaching what I learned in my freshman year at my former highschool in delaware though so I am not really surprised. What on earth is kinematics ? I will google it in the meantime but feel free to explain :P

 

Unfortunately I can not do anything tonight I just now got home from north new jersey a nice hour and a half drive and I have been gone all day. Have to work in the morning so not much time left tonight.

Edited by thelvyn

I haven't lost my mind. It's backed up on disk!

Oops bad sectors damn floppy's!

Link to comment
Share on other sites

When I say kinematics I mean things like velocity, forces, acceleration, and doing everything in vectors. You might already have a good understanding of this, I don't know:

 

For example, say you have an object that starts a point s0, which is a vector:

s0 = (s0_x, s0_y, s0_z) [meters]

 

Now you apply a constant velocity v to it at time t = 0, and v is also a vector:

v = (v_x, v_y, v_z) [meters/second]

The new s as a function of time t is given by the following equation of motion:

 

s(t) = s0 + v * t

 

Momentum is defined as mass * velocity [kg * m/s]:

p = m * v

Momentum is kind've a measure of how much something wants to keep going the way it's going. It's harder to stop a heavier object than a light object, and it's harder to stop a faster object than a slower object.

 

Now, velocity can change over time, and that gets us into the realm of acceleration, forces, and impulse.

 

Acceleration is the rate of change of velocity. For a constant acceleration a that turns on at time t=0:

v(t) = v0 + a * t

 

Force is defined as rate of change of momentum. Since momentum is mass * velocity, and we're only looking at one object, our mass is not changing, so force ends up being mass * change in velocity, which is acceleration:

F = m * a

 

In physics, usually what you know to start with is the force. From the force, you can extract the velocity and the position using Calculus, assuming you know the initial velocity and initial position (at time t=0). Note that those equations only really apply to the net force. You could have two forces pulling opposite directions, and if they add to zero net force, F = 0 and nothing happens. If one is made slightly greater than the other, there's a small net force in that direction and it starts going that way.

 

Kinetic energy is defined as:

K.E. = 1/2 * m * v^2

 

Energy is a good measure of, say, how much damage something does. That's why I was saying that when AI fall, the damage they take should be proportional to the square of their velocity.

 

That's all you need to know for simple mods to the D3 physics. It helps to have some idea of what's going on behind the scenes, the main thing being that the time variable gets quantized.

 

Finite Difference Method

In Doom3 and most games, they implement a physics model called Finite Difference Method, where everything is quantized by the time step of one frame in the game (nominally 17 ms at 60 FPS, but can be longer if performance is suffering). Note that if your rendering framerate goes above 60 FPS, the game physics simulation keeps running at 60 FPS, so it's indepenent of the rendering framerate in that respect. However, if you have some scene that hogs CPU to render and slows down FPS to 20, that will also effect the physics and you'll see things happen like a slideshow. That's why some things like bullets flying thru the air are interpolated between frames using the actual time, but don't worry too much about that for now.

 

 

I can't really derive finite differences method in a meaningful way without Calculus, and it's also not super-important for doing the kind of work we're doing, so I'll just give the final equation without any real explanation. I've mentioned that we can start with the force and extract the velocity and position. But what if you only know the force at each t = t_0 + N * Delta_t, where N is the frame number? It turns out that there's an approximation that lets us predict how the position will change from one frame to the next, based on the current value of the force, at some time t_N:

 

[For those interested, this can be derived from Taylor's theorem, approximating the derivative as a finite-difference and taking the first two terms]

 

F = m * a = [ -s(t_N + 2 Delta_t) - 4 s(t_N + Delta_t) + 3 s(t_N) ] / (2 * Delta_t)

 

Again, for the problem we're talking about, you usually know the force, and want to calculate how the object will move. You can use the above equation to approximate how an object will move (its position is given by "s"), due to some force applied to it in that frame, over the frame time Delta_t.

 

You can't always apply this approximation. There are conditions where the time step is too large for what's going on and it becomes unstable. This is why ragdolls flop around like crazy sometimes (that and we're trying to drag them to a point they can't get to because they're blocked by something :) ).

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recent Status Updates

    • Petike the Taffer

      I've finally managed to log in to The Dark Mod Wiki. I'm back in the saddle and before the holidays start in full, I'll be adding a few new FM articles and doing other updates. Written in Stone is already done.
      · 0 replies
    • nbohr1more

      TDM 15th Anniversary Contest is now active! Please declare your participation: https://forums.thedarkmod.com/index.php?/topic/22413-the-dark-mod-15th-anniversary-contest-entry-thread/
       
      · 0 replies
    • JackFarmer

      @TheUnbeholden
      You cannot receive PMs. Could you please be so kind and check your mailbox if it is full (or maybe you switched off the function)?
      · 1 reply
    • OrbWeaver

      I like the new frob highlight but it would nice if it was less "flickery" while moving over objects (especially barred metal doors).
      · 4 replies
    • nbohr1more

      Please vote in the 15th Anniversary Contest Theme Poll
       
      · 0 replies
×
×
  • Create New...