News | Forum | People | FAQ | Links | Search | Register | Log in
Coding Help
This is a counterpart to the "Mapping Help" thread. If you need help with QuakeC coding, or questions about how to do some engine modification, this is the place for you! We've got a few coders here on the forum and hopefully someone knows the answer.
First | Previous | Next | Last
Brilliant Stuff Spike! 
Now if someone had time to update: http://quakewiki.org/wiki/EXT_CSQC#Sending_Data_to_the_Client

:) Maybe when I'm finished with my project in a couple months I'll update that page. 
Returning Null... 
in c++

So, in java, you might do something like this:

class Foo
{
��int a;
��int b;
��Foo(int x)
��{
����a = x;
����b = x;
��}
}

Foo getFoo(boolean z)
{
��if (z)
����return Foo(5);
��else
����return null;
}

So you'd get a pointer to a new Foo object or not depending on the argument z.��This is nice.

But what about in c++?��The Foo class is tiny, and it would be best to return it by value.��But if we wanted to return null, this isn't possible anymore.

Is the only solution to that really this:

bool getFoo(boolean z, Foo& f)
{
��if (z)
��{
����f = Foo(5)
����return true;
��}
��else
��{
����return false;
��}
}
 
 
granted, you could return pointers to new objects on the heap, but then you've got to delete them after which is a pain. 
Necros 
I shall preface this with a quote by Tony Hoare:

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

Don't abuse null as a special return value indicating that nothing could be returned. It's really bad style (I do it all the time though because it's easy - go figure).

There are several ways to avoid null in this scenario:

1. Provide a method that checks whether a result will be available before you invoke your get method. There is a name for this idiom, but I can't remember it right now. It's basically like this:


bool canFoo(int x);
Foo getFoo(int x);


In getFoo, write an assertion that canFoo is true to detect bugs in debug mode.

However, this has the disadvantage that any logic that determines whether a return value is available may have to be executed twice: Once in canFoo and once in getFoo when the value is computed.

2) Create a default instance of Foo that is always returned when no value is available. This instance might even be able to behave in a sensible way, maybe displaying an error message when some of its methods are called.

3) Return a std::pair<boolean, Foo> where the first element indicates whether a result was available or not. This is basically the same as returning NULL from the method when you use pointers, but it makes the calling code a bit uglier because the syntax to access the elements of std::pair is a bit awkward. I sometimes write my own wrappers instead of std::pair for this.

Personally, I will use of these options in the order I presented them, so 3 is a catch-all that I'll use if 1 and 2 are not feasible. In either case, you put the burden of checking whether a result exists on the caller, but that's no worse than using NULL.

Side note: Some languages such as LUA allow you to return more than one value from a function. I consider this the best solution because you can write something along the lines of


bool valid, Foo foo = bar.getFoo(5);
if (valid) { ... }
 
 
I notice you didn't have my own suggestion in your list above... Is there something wrong with passing a reference that you will store the actual result in to the function? I figure, as long as you're careful with your const declarations, there shouldn't be any danger of accidentally mangling anything important. 
I Don't Like Out Parameters 
And I try to avoid them where I can. That's why. Also if you know that your Foo need not be changed, you should make it const, but you can't with your approach because getFoo cannot take a const reference. 
 
Ah ok, I would use it more for short live temporary things anyway.

Something you'd create on the stack before calling the method, then read what you got back if the method returned true (and ignore it if it was false).

using a pair seems like the least hacky and most efficient way of doing it otherwise.

method #2 means you either need dummy values which you need to know mean the object is garbage or you need to include a boolean value into the class that you can check to see if the data is good or not, but that boolean may not always be relevant.

method #1 is good, but you might be doing a check twice, or some steps needed to check in canFoo() might be used to do the work in getFoo() so at the least you can be repeating some steps.

Anyway, thanks for your input. I am starting to really warm to this language! 
Constness 
I am a total const nazi. Every variable / parameter that can be const, should be. The same goes for member functions. This is the best feature that C++ has over Java, in my opinion. I would really suggest that you become a const nazi too, because it leads to cleaner code if you give some thought about whether a function can have side effects or not, and functions without side effects are much safer!

You're right about option #2 - it's seldomly applicable, but when it is, it works very well and leads to cleaner code because the caller doesn't have to worry about whether the return value is valid or not. 
 
I appreciate languages like Python and Erlang that allow you to naturally return arbitrary tuples from functions. Or that are dynamically typed so that you can just return a special error token from a function that would normally return some other type.

I'd probably go with solution 3, since another gotcha with method #1 is that there's a race window between checking and fetching the data (if the data is mutable). 
Only If There Are Concurrent Threads. 
But yeah, that's true. 
Random Weirdness 
Interesting bit...

if you forward declare a class, you can use a pointer.
if you don't include the header for that class, when you call delete on the object, it silently fails to run the destructor.

that had me stumped for a while... 
Clang Has A Compiler Warning For This. 
Be sure to enable all compiler warnings you can. It will safe you a lot of headaches. 
Monster_friend 
I made a zombie friend, that does escort the player and, as long as the player doesn't shoot it, only attacks other.

One thing that strikes me is, that when the zombie has killed a monster, it won't return to the player to follow.
It just stand with the killed corps, and as long here are no enemies, it stays there.

How do I make it follow the player again after killing?

zomby_friend 
This Would Be My Approach 
But it will probably require some messing about to get it to work. You could drop this in a repeating function you call in the run and stand frames.

if (self.enemy.health < 0)
{
self.movetarget = "";
};


Basically, if my enemy is dead then set my movetarget (walk destination) to null, which will mean the first entity in the list - the player.

Won't follow other players in coop. 
OK 
Out of the blue to ask this perculiar thing without explaining the changed qc.
In ai.qc in Found_Target I placed this part:

/*
Zomby_friend starts fight
*/

// ------------------------------------------------
float() FindMonster =
// ------------------------------------------------
{
local entity beast;

if (self.attack_state != ESCORTING)
return FALSE;

if (self.enemy)
return FALSE;

beast = findradius(self.origin, 1500);

while(beast)
{
if ( (beast.flags & FL_MONSTER) && visible(beast) && beast != self && beast.health > 0)
self.enemy = beast;
beast = beast.chain;
}

if (!self.enemy)
return FALSE;

FoundTarget();
return TRUE;

};


So I think it's the best place, now only puzzling. 
Big Ol' Parms Post 
Been a while since I've had a chance to write something up to completion, here's a write-up on the parms mechanism, after the posts in the Mapping Help thread on that topic.

http://tomeofpreach.wordpress.com/2014/12/28/sending-values-between-levels/

To necros: I think that since the article manages to compress the player data into 3 parms values, it should be feasible to use cvars to smuggle that info - scratch1-3 would be enough. So as long as you accept you aren't getting anything like half-life's transition zones that can take other entities with them, a hub system should be feasible.

I guess the other practical things are to use some of the space in the third parameter as an index to select which spawn point the player should transfer to, and working out exactly how to send the command. Probably the best way would be as a single semicolon separated string, setting the scratch variables, then a "map" command in case we haven't visited this map yet, then the "load" command to attempt to load the save game for the map in progress. Probably overlooking something but it sounds solid enough... 
 
oh wow, that's crazy... you are a mad genius. :)

the savegame as hub system does have some other flaws though, for example, say a fiend leaps at you as you go through a hub save/load transition. when you return to the map, that fiend will still be floating in mid air to hit you. :(

I suppose you could designate transition zones as off limits so any monster in the zone when the a hub savegame is loaded would be move away or something.

ehhh, maybe it's better to use that scripting extension that lets you read/write to text files... 
Texting 
that scripting extension that lets you read/write to text files...

whoa - what's this and what engines support it? 
Oh Also 
Preach that is an excellent post. 
Exclusion Zone 
I think once you accept that the monsters aren't gonna transition with you through a loading zone, you'd have to start with a recommendation to mappers that loading zones be designed to be isolated, free of items and largely inaccessible to monsters. You could enforce this somewhat on the mod side using code that runs with save game detection. When you notice that the player just loaded a saved game, findradius all the monsters near the player, and reset them back to their spawn location, optionally non-alert too.

That might be a neat idea to do with some of the further away monsters too, perhaps just the ones with patrol routes so that there's a limit to how much a sneaky player can exploit it. That would make it a bit more like Metal Gear Solid style zone transitions, which I think is a better model to have in mind than the Half-Life level continuity which is too hard to achieve. Mimicking the fade-to-black cutscene entering the loading zone and fade up on the other side would mask some of these "defensive moves"... 
 
http://quakewiki.org/wiki/FRIK_FILE

i don't know much about it, but i think it was used in that top-down RPG mod... which I am blanking on the name right now. 
Prydon Gate? 
well googling "frik file" gives me almost no more information other than that page you linked, and that Darkplaces supports it, so I'm guessing it's probably something that was supported in that one engine? 
 
well, yeah, in theory, any engine that supports the FRIK_FILE extension, but afaik, it's just DP.

it is a powerful system though, you could, in theory, transfer player info with a text file between savegame loading as well as use it to transfer over monsters in the trigger zone so that it behaved like half life. (although you might run into trouble if you brought over monsters that were not precached in the map originally). 
Transferring Monsters 
You might have to work really hard to get monsters which weren't present before loaded in the level, but I think it might be possible. The thing you have going for you is that you can notice early enough that you need to run extra precaches, as the transfer data is available on the initial frame.

The thing you have to contend with is the modelindex field in the saved game you're loading (assuming the player has already visited the level) - if you precache extra models you'll probably invalidate the existing modelindex values, especially since you'd likely have to do all of this extra precaching during worldspawn. Given the choice we would prefer to do all the precaches after the other spawn-functions, where they couldn't wreck things.

So what we'd have to do is lots of clean-up when we get our onload function running. We could start with the idea that we need to do setmodel(self, self.model) on all the entities at this stage, and then try and work out all the exceptions we need to flag. Should be possible to get that working on anything that isn't a modelindex entity hack, and you know, you can't always save that kind of thing in a mod.

I did get excited for a little bit when I thought about how much storage you'd need for a monster, and figured I could get it down to 34 bits a monster. For a second I was excited as the parms provide enough space for 9 such monsters plus a player! Then I remembered we didn't have the parms and was saddened... 
Quick Question 
Is it possible to remove (or at least, hide) the "Saving to..." line that appears as a console message on top left of the screen which displays the full path to .sav file, by modifying the progs file? Lately I noticed that longish message distracts me greatly. 
First | Previous | Next | Last
You must be logged in to post in this thread.
Website copyright © 2002-2024 John Fitzgibbons. All posts are copyright their respective authors.