News | Forum | People | FAQ | Links | Search | Register | Log in
Teaching Old Progs.dat New Tricks.
You know about the info_notnull explosion hack. You know about making monsters drop weapons or alternate ammo using the .weapon and .ammo_whatever hacks. You know about making items float in the air by spawning them on a temporary platform. Let's have a thread where we talk about new ways to use existing behavior and get novel gameplay. If you're a "retired" mapper, this is a great time to do some armchair level design and suggest ideas you'll never have a chance to use yourself.
First | Previous | Next | Last
Triggering From A Monster 
is it possible to trigger a target with a monster as the activator?

i've tried spawning the monster on top of a trigger_teleport, having the monster walk onto the trigger_teleport (with path corners) and lowering a monster into a trigger_teleport with a func_door.

the trigger_teleport had a target, but somehow i was still the activator when the monster would go through the teleporter.

all i can think of would be to make a dummy monster that targets the entity, and make the spike_touch hack kill the dummy with the owner pointing to the activator monster, but that's convoluted as hell and really ugly. 
Yes, It Can Happen 
For example, if a monster kills another through infighting, and the dead monster had a target, then the killer will be the activator of the target. This caused a very tricky to identify bug in a recent map whose author will remain nameless here...
So it can happen.

Making it happen on demand is a bit trickier. I think I've come up with a hack that will do it though. Add an info_notnull and set its enemy field to the entity number of the monster in question. Then set its use field to trigger_multiple. Then if you use the info_notnull, it will fire its targets with its enemy as the activator.

How it works: originally I was going to try doing it with the "naked trigger_multiple" hack, where you make a trigger that monsters can touch, and have the teleporting monster touch it. That trick relies on short-circuiting the multi_touch function which usually guards against non-player touches.

Unfortunately, skipping that function also skips the line which sets the trigger's enemy to the touching monster/player/fireball. This enemy is then used as the activator - so you still wouldn't get the monster as the activating entity. Instead you get whatever was in the enemy field to being with - world being the default. I got half way through typing up a solution involving the spike_touch hack setting off a trigger_multiple with health, before I saw the short cut that would save an entity...

Not properly tested it btw, but I'm pretty sure that will work as intended! 
Thanks 
that sounds like a much better solution. this brings me to another question i should have asked: how *do* you find the edict numbers?

typing edicts in the console outputs ALL the edicts, and even with condumping, it only actually prints the last 80-100 or so. (the map has over 600 edicts).

all i can think of is to maybe make a progs, but can you even bprint() the edict number? 
Oh 
this always happens, i figure out how to do it after i ask. :P 
Aannnd 
it works. thanks!

btw, it's 'use' 'multi_trigger', not 'trigger_multiple' 
Edict Hunting 
This is actually reminds me of something I've had in the back of my mind as a desirable feature for a "development" quake engine. Better searching of the entity list with the edict command. The biggest improvement would be to be able to specify a classname, like

edicts monster_knight

would output a list of every knight in the map.

Once you have that, partial matches would be the next most handy feature, so that

edicts monster_

would return a list of all the monsters in your map. If you really needed flexability, you could extend it to match other fields instead, like

edicts targetname skulldoor

would give you all the entities targeted by "skulldoor". If anyone other than myself would be interested in that, I might take a look at hacking it together...

There are workarounds until such things are available. If you save the game instead of running the edicts command, you can open the save file in a text editor and get essentially the same information out of it - or even run -condebug and read through the output file at the same time. The only thing that the save game lacks is...entity values themselves! So the latter method is needed for those 
That Would Be Pretty Badass 
except that it's only real use would be for hacks, correct? 
That Would Rock 
It could also be useful for checking targetnames and goalentities etc. Though I would probably use it only for hacks, yeah. 
QC Application 
I'd also find it really useful when debugging mods, although not so many people are going to care about that. I'm gonna go away now and see if I can get the new version of fitz to compile... 
Half Way 
The following replacement code will perform partial matches on classname. Find the function in pr_edict.c and replace it entirely with this. I've only tested it in fitzquake085 so far, but I don't see any reason why it wouldn't work in the original source. Other engines might have changed the functions that it calls, but I'd say even that is unlikely...


/*
=============
ED_PrintEdicts

For debugging, prints all the entities in the current server
PREACH: If supplied with a second argument, only reports entities whose classname initially match the argument
=============
*/
void ED_PrintEdicts (void)
{
�int i;
�edict_t *ent;
�switch (Cmd_Argc())
�{
��case 1:
��{
���for (i=0 ; i<sv.num_edicts ; i++)
����ED_PrintNum (i);
����Con_Printf ("returned %i entities\n", sv.num_edicts);
���break;
��}
��default:
��{
���int j,len;
���len = strlen(Cmd_Argv(1));
���j = 0;
���for (i=0 ; i<sv.num_edicts ; i++)
���{
����ent = EDICT_NUM(i);
����if( !strncmp(Cmd_Argv(1),pr_strings + ent->v.classname,len) )//match initial substring
����{
�����ED_Print(ent);
�����++j;
����}
���}
���Con_Printf ("returned %i of %i entities\n", j, sv.num_edicts);
���break;
��}
�}
}


Getting it to match against any field is a little bit more tricky, so I'm gonna keep working on that now. Once I get that, I'll post the code and a binary. 
And A Follow Up 
Phew, not too bad. You can grab the following function added to fitzquake085 from:

http://www.btinternet.com/~chapterhonour/fhack085.zip


void ED_PrintEdicts (void)
{
�int i;
�edict_t *ent;
�switch (Cmd_Argc())
�{
��case 1: //regular call
��{
���for (i=0 ; i<sv.num_edicts ; i++)
����ED_PrintNum (i);
���Con_Printf ("returned %i entities\n", sv.num_edicts);
���break;
��}
��case 2: //match against classname
��{
���int��j,len;
���len = strlen(Cmd_Argv(1));
���j = 0;
���for (i=0 ; i<sv.num_edicts ; i++)
���{
����ent = EDICT_NUM(i);
����if( !strncmp(Cmd_Argv(1),pr_strings + ent->v.classname,len) )
����{
�����ED_Print(ent);
�����++j;
����}
���}
���Con_Printf ("returned %i of %i entities\n", j, sv.num_edicts);
���break;
��}
��default: //match against given entity field
��{
���int j,len;
���int *v;
���ddef_t *search_field;
���search_field = ED_FindField(Cmd_Argv(1));
���if(!search_field)
���{
����Con_Printf ("Bad field name\n");
����break;
���}
���
���j = 0;
���len = strlen(Cmd_Argv(2));
���for (i=0 ; i<sv.num_edicts ; i++)
���{
����ent = EDICT_NUM(i);
���//this is ugly as hell, WTF Carmack?
����v = (int *)((char *)&ent->v + search_field->ofs*4);
����if( !strncmp( Cmd_Argv(2),PR_UglyValueString( search_field->type, (eval_t *)v),len)) //match initial substring
����{
�����ED_Print(ent);
�����++j;
����}
���}
���Con_Printf ("returned %i of %i entities\n", j, sv.num_edicts);
���break;
��}
�}
}


Only minor bugbear I have with it is that tab still activates autocomplete on the second parameter, but it completes commands, which is unhelpful. Otherwise, it's cool. 
 
//this is ugly as hell, WTF Carmack?
lol

nice work! :) 
Nice One 
Let's see how this works out in the field. 
 
"//this is ugly as hell, WTF Carmack? "

Well, Carmack's reply would probably be something like, "We were trying to ship a game on new technology. This ugliness was maybe .00000001% of the workload. Unimportant." :) 
 
we've covered removing items from the inventory via a func_door function hack.

is it possible to co-opt sigil_touch() to remove runes from the player?

i was thinking if you gave built a trigger from scratch with a touch function of sigil_touch and gave it a spawnflags # corresponding to the appropriate rune (1, 2, 4, 8) except negative, it might be possible to subtract it out of the serverflags global variable.

except that the way they added the flag is in a form i'm not familiar with (and i don't know much about bitwise manipulation except what i've learned here).

serverflags = serverflags | (self.spawnflags & 15);

except that serverflags = serverflags | (serverflags & self.spawnflags);
would have seemed to me the correct way of adding the flag in.
what does the actual line do? will it prevent subtracting the flag off? 
 
serverflags = serverflags | (self.spawnflags & 15);

first, the & character is bitwise AND operator, which means that for each bit, the result is 1 only if both inputs are 1 for that same bit. 15 is 1 + 2 + 4 + 8, So self.spawnflags & 15 is going to only allow those first four bits in self.spawnflags to be used in the next step.

The next step: | is the bitwise OR operator, which means that for each bit, the result is 1 if either input has a 1 for that bit. So serverflags = serverflags | x is going to turn on any bit in serverflags that is in x, and leave on any bit that was already turned on before.

So basically, only the first four bits in serverflags can be turned on with a rune, and, you can't turn any bits off. 
Ah 
damn shame, thanks though :) 
The Meta Hack 
Just in, the latest discovery in the hacking business. A trigger_command in id1! Well, sort of.

As it turns out, it's actually possible to execute console commands from within the map by simply adding a linebreak followed by a string of commands in the "map" field of a trigger_changelevel. For example:

"classname" "trigger_changelevel"
"spawnflags" "1"
�// no_intermission has to be set for this to work
"map" "\ngod; impulse 9"

The trick works because the trigger uses stuffcmd to change the level which we can exploit to run our own commands. However, there's a catch. In the engine, we have a hardcoded line that prevents the changelevel from being issued multiple times, which means this hack can only be used in this form once, and afterwards the player can't proceed to the next map after exiting the level normally. It will just stay in intermission mode forever - one might regard it as acceptable after a large map with no follow-up; or with a Shub ending.

Even in this state, the hack has a lot of potential, especially for mischief. You could, for instance, rename the player to "Asshole" and make his client connect to a server. :D Or mess with his entire config, unbind all keys etc. But since we're all nice and friendly people (right? :P), rather things like changing map-related cvars come to mind, e.g. setting r_wateralpha for the glass hack or adjusting fog values.

Luckily there's a sort of workaround for the changelevel/next map problem mentioned above. After changing cvars for map compatibility, you can add a "restart" or "map yourmap" command to the end of the string to reload the server with the exit trigger intact. Or, if you set any non-permanent stuff (e.g. cheat codes or gravity), you have to add commands to save and reload the game. ->

Following this line of thought, Preach came up with two very useful setups the hack allows us to use.

The first one is a kind of security measure to make sure all necessary commands are executed before the map is played. Set "serverflags 256" in the middle of your command string and use a func_episodegate with spawnflags 256 as a detector. If the episodegate doesn't spawn, the player (presumably) hasn't run the map yet and needs all the stuffcmd set; if it spawns, there's no need to restart.

The other one is an actual autosave option, albeit not a very transparent one. Place trigger_changelevel entities as checkpoints and set their map fields to "\nsave autosave; wait; load autosave" (you could also use "quick", or better yet, the mapname instead of "autosave"). This will save and instantly reload the game on each checkpoint - however, it's quite 'bumpy' and may even cause annoying delays in larger maps and engines with long loading times (DP). In this sense a button-controlled checkpoint system would probably be the best solution. 
Addendum 
Negke and I were discussing this last night for as long as steam would allow, and I think there are some exciting possibilities. However, I have just thought of a potential small wrinkle in the serverflags 256 detection code. Suppose that someone loads the map, instantly receives a string of commands including "serverflags 256;restart", and then begins playing the map on the second time round. Supposing they save the game, they might not load that saved game for a long time. If serverflags 256 has been cleared by subsequent playtime, then on trying to load their savegame they may end up back at the map start!

How did this arise? Well, when a savegame is loaded the engine does two things. Before it looks at the savegame data, the engine runs the first three frame of the lodaded map as if it had just been started from scratch, in order to get all of the precaches loaded. It then updates all of the entities and qc variables to match what was stored in the savegame file. If our hack runs during those first handful of frames, then the restart command will be sent before the savegame is loaded, and so you end up right at the start of the level.

It isn't the end of the world, because now serverflags will be set to 256, and so if you load the saved file a second time it will work correctly. It's just good to make people aware that this problem can occur though. The alternative design would be make sure that your hack trigger fires later than the first three frames, but this has two negative effects. One is that the restart will be more visible to players, as they will see frames rendered first. The other is that in the scenario outlined above, none of your essential console commands get executed when the game is loaded. If the serverflags cvar got reset, it's probable that some of the others did too. 
Renaming The Player 
That reminds me of Zelda on the gameboy, where it was possible to steal from the shop and be called THIEF for the rest of the game. 
Changing The Skybox In Quoth 
Cool trick: you can change the skybox while a map is running in most engines, via an info_command.

The command is "sky [skyname]" (works in Fitzquake, DirectQ, Quakespasm). Unfortunately Darkplaces uses a different name, "loadsky". I guess you could have a pair of info_commands with both commands, but then the player sees an "Unknown command" message for the one their engine doesn't recognize. :-/

This could be used for some neat effects. Maybe have a setting sun sky turn to night after spending some time indoors? 
 
This could be used for some neat effects. Maybe have a setting sun sky turn to night after spending some time indoors?
my lost map pack had this in there.

you can also string multiple console commands together like this:
sky void;loadsky void;fog 0 0 0 0\n
so there's no need to have multiple info_commands.

further, i think it's an undocumented feature, but if you set spawnflag 4, whatever commands are on that particular info_command will be run whenever the map is run or loaded from a save.
this stops the problem with fog/sky settings not being saved because they aren't in the worldspawn settings. 
Ditto 
This could be used for some neat effects. Maybe have a setting sun sky turn to night after spending some time indoors?

We did this in Travail too! We had to cheat a bit for people with older engines though by covering the sky with a non-solid black brush. 
Chthon Style 
Looking for a replacement teleportation effect (as spawn_tfog can't be used anywhere else than 0 0 0 - or so it seems), I came across two more things Chthon allows us to do. They are quite obvious, but I'm posting them just for the record.

Lava splash: Target an info_notnull entity with a "use" "boss_death2" field.
This might be useful for spawning a powerful monster or when lowering a platform into a lava pool. The downside is that this will increase the kill stats by 1, but it should be okay since it evens out the max count.

Meteor shower: An info_notnull with "use" "boss_missle1".
This will start an infite attack of lava balls from a fixed position aimed at the player - for an environmental hazard with a little more kick. Each missle will play Chthon's throwing sound, but that shouldn't be a problem, especially if the entity is high above or in some distance. To stop it, simply killtarget the entity.

Needless to say, a monster_boss has to be placed somewhere in the map for precaching. 
Correction About The Lava Splash 
Use "boss_death9" instead for a more immediate effect, and killtarget the entity 0.1 seconds later to keep it from raising the kills count. 
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.