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
Rehashing Old Work 
I didn't bother to dig into the whole backlog of messages here, but I made a (fairly simple) hack that allows for killable "static" entities. I've tested this with flames, but not with other things, I suspect that light globes may work.

step one, make a trigger under the player spawn and make it target an info_notnull called "flame". Make sure this info_notnull is some distance from the player, as touching it too early will crash your engine.

Place a "light_flame_large_yellow" somewhere in the map to precache the model.

give the info_notnull the following keys:

targetname flame
model progs/flame2.mdl
frame 1
use func_wall
think PlaceItem
nextthink 2
mdl progs/flame2.mdl


This should now allow you to killtarget the flame entity. Note that static ents don't occupy the edict list, however this hack will. I guess it will also allow for a touch function to be run on it after this point too.

One final thing to consider when using this hack, touching this entity before its had its "nextthink" even by a monster will crash the game. Even shooting the flame will crash it. I suggest spawning them well away from players and monsters.

The nextthink function forces the notnull entity back to a non-solid non-bsp state whilst retaining the flame model. In the example above this happens 2 seconds after it spawns. 
 
(oh, the trigger should be a trigger once) 
 
those 'this hall selects foo skill' centerprinting triggers

Blimey, I didn't have to go far then to find one. 
@551 
That's a fair amount of work for a killable flame. Rather than messing with thinks and nextthinks and all that, you can just set the modelindex of an info_notnull to that of the spinning flame model (flame2.mdl, check in the console first to see which slot it takes of course) and have a simple displayed model that you can killtarget like normal, no extra futchery with crashing when touched or anything. The flame will automatically do its snazzy little spinning and burning animation, similar to a backpack, and the frame field changes the size with 0 being the smaller flame model (light_flame_small_yellow) and 1 being the larger version. Old example webm here
 
the flame models are static and don't appear in the edict list though? 
 
They're still given a modelindex, no different from the player or enemy models or worldspawn/brush models. Not sure how to find which model has what number in Fitzquake-derived engines but Darkplaces (ugh) has a handy modellist command that will print out every model currently loaded by the game in order. Worldspawn comes first, then count the list by one. Worst case scenario, experiment a bit until you find the right modelindex and remember to place things in the right order that enemies/health changing by difficulty won't break the hack. 
Worst Case Scenario 
You might have to make separate entities for different skill levels, with each having the correct model indices for the corresponding skill levels. :/ 
Move On Up 
Moving the static torch you're using to precache the model as near to the top of the entity list as possible will help to stabilise the modelindex across all the skill levels in your map. Also worth remembering that each time you add a brush entity, that adds one to all the precache modelindex values. So you want the map to be basically finished in that respect before you finish off the hack. 
Rotating Turret Hack Finished (mostly)! 
Hey guys, back for another update. My rotating turret hack is almost 100% done :D I just need to take a few minutes tomorrow to iron out some little kinks related to the offsetting of the body model in relation to the shots and it will be good to go and I can make a demo map and full write up on it. In the meantime here is a weirdly rotated screenshot from QuakeDroid showing it in action in a crappy little box room :)

https://imgur.com/a/IeLhHFe 
The Absolute Fucking Madman 
Eagerly looking forward to seeing it in action. 
 
Nice that a "newer guy" can make cool stuff! 
 
If what he's posted in #tf and related channels in such short time is any indication, rekta is a new maphax savant. 
Thanks 
I mean it's not that impressive, you can do it easily if you have basic programming knowledge and take the time to read the sources to know what fields and functions you can and cant use where... Or in my case, try and figure out why entire segments of code were replaced and moved elsewhere but still in the original files commented out, then get confused when what you want to do isn't working the way it should :D it's not like I'm making Quake Rally 2 or something like that... Though I do like racing games so maybe I'll give that a shot some day ;) 
Rotating Turret Writeup/demo Map (part 1 
Ok, so, here is the write up on the rotating turret hack. Now my setup probably isn't the best but it works fine, at least in SP, so I'll probably keep using it in maps going forward anyway. It's not too difficult to set up but there are several steps involved. Also apparently this is too long to put in one post since the character limit is 5k and I have more than that :O So i will need to divide it into multiple parts.

So, here is how you do it. First at the map origin you need to set up the model of the turret itself. Of course I suppose you could use a custom mdl but these are vanilla maphax we are talking about here :) Now after you make it you might need to move it up a little depending on the type of projectile you want to use so it doesnt look like complete crap when it shoots, I used hknight_shot so I had to move the model up a few units to get it to look right. After you made the model you need to turn it into a brush entity so it will have a model and modelindex value, I used func_wall. And of course you should encase it in a box to prevent leaks and add appropriate lighting. Then add a info_notnull in the spot where you want the turret to be and give it a target name like "turretmain" or something, it will be the main turret body. Now you will want to copy the main model to the notnull, you do that by setting the modelindex and model fields to the same model and modelindex values of the turret model you want to use, if you want to have multiple different styles in your map you need to create multiple models. You can find these values using the "edicts" command in the console, if you want to be able to find the edict numbers of the turret models easier just cut and paste them to the top of your .map file just under worldspawn, that should make things a little easier since now they should be closer to the top of the edict list. In my case the model was *1 and modelindex was 2.0, so I used those. So so far you should have something that looks like this

info_notnull
.....origin = whatever the position is that you placed the notnull at
.....model = *1 (or whatever the model number of your model is)
.....modelindex = 2.0 (same as above)
targetname = whatever you want the targetname to be i guess.

Now if all things went well when you go into your map you should see your turret in the spot where you placed it. Good. But, it doesn't shoot obviously, or rotate either :( SO we need to fix that. Unfortunately I couldn't figure out a way to get any "smart" player detecting functions working so it can only really reliably track 1 player meaning this hack is technically SP only for now. But that's ok, you can just make multiple copies that track specific players, no big deal. First add an "enemy" field and set it to 1, this will cause it to track the first player since they are always the first entity loaded in a map. Now let's get the rotation part out of the way first. For the rotation to work properly we need two things, we need to add a "yaw_speed" field to the turret that contains the speed that we want it to rotate at, and a function that consistently sets some specifc values, specifically ideal_yaw and angles_y. Luckily there is a function that does just that for us, ai_face. So we need to put that somewhere, but where is a little tricky. We can't put it in think because neither ai_face or ChangeYaw (the helper function that ai_face calls that actually rotates the model) resets nextthink meaning it would only run once which isn't exactly ideal. There are 2 options that we as the mapper can consistently control though, use and th_pain. So we can put our rotation function in one and the shooting function in the other, it doesn't really matter which one since the pain state of the turret will need to be activated by a use'd entity shooting at it anyway. So I put ai_face in use and the shooting function in th_pain, in my case hknight_shot. So now it should look like this: 
Part 2 
.....origin = whatever the position is that you placed the notnull at
.....model = *1 (or whatever the model number of your model is)
.....modelindex = 2.0 (same as above)
targetname = whatever you want the targetname to be I guess
yaw_speed = the speed that you want the turret to rotate at
enemy = 1 (or whatever the player number is that you want it to target)
use = ai_face
th_pain = whatever shoot function you want the turret to have, only *MONSTER* shoot functions can reliably be used as far as I can tell since I haven't been able to find a function that will reliably set the values required for you to be able to use player weapons effectively, you can still technically use them, you just can't constantly re-aim them so they will either always shoot in one direction or shoot wherever the player is pointing at the time, depending on how you have it set up. Who knows, maybe that's what you want and you can find a use for that, but this "tutorial" of sorts is about hostile turrets that attack the player, not shoot in the direction they are facing ;) Oh yeah, and the monster whose shoot function you want to use needs to be precached in the map first.

Now as you can probably imagine, we need to set up some triggers so that the turret will do it's thing. So make 2 identically sized trigger volumes that take up the exact same area that will correspond to the line of sight of the turret, of course they can be multiple brushes if you want a concave shape, it just matters that they are targeting the things they should be. One of the triggers should target the turret itself, which when the player steps on it will cause the turret to rotate since its use function causes it to do that. The other trigger will target a new notnull that we need to add in the exact same position as the turret one which is supposed to trigger the turret's pain state, causing it to shoot. It will do that by being inside the body of the turret and constantly shooting it with it's own projectiles. Of course for the turret to be in pain it needs to have health, so you should add a health field to it and set it to whatever you want. You also need to set "armorvalue" and "armortype" as well, so that the turret won't kill itself by being shot by its pain trigger. If you want it to be destroyable you should set the armortype to soak up exactly as much damage as the triggering projectile will give (a tutorial on how to do that is given by Preach here https://tomeofpreach.wordpress.com/2013/05/11/trigger_damagethreshold-simulator/) and make sure that the player will have a weapon at that point that does more damage than that. Finally, you need to add 3 more fields, "solid", "takedamage", and "movetype", and set them to 4, 2, and 7 respectively. The solid and takedamage are set so that it will, you know, be able to be shot and take damage, and movetype 7 (PUSH) is required by solid 4 or else the map will crash.

So now that the turret is all set up to get shot and in doing so shoot at the player, now we need to add the notnull that will be firing those trigger shots. This is suuuuuper easy. just add an info_notnull in the exact same position as the turret and give it a targetname, in my case "turrettrigger", a use of hknight_shot, and an enemy of whatever the edict number of the turret's notnull is, in my case 7. Now when it is triggered, it will shoot at the turret, which will cause it to go into "pain", which will cause it to shoot at the player since that's what it's pain function is set to do! And it will rotate as well assuming you have another trigger covering the same area targeting the main turret itself.

And thats about it! Now you should have a turret that, when triggered, shoots at the player and rotates to track them! The final setup looks something like this:

notnull 1 (turret)
.....origin = whatever the position is that you placed the notnull at
.....modelindex = modelindex of turret model you want to use
.....model = same as above
.....targetname = whatever you want the targetname to be I guess
.....yaw_speed = the speed that you want the turret to rotate at
.....enemy = 1 (or whatever the player number is that you want it to target)
.....use = ai_face
.....th_pain = whatever shoot function you want the turret to have, taking into account the restrictions outlined before
.....health = whatever
.....armortype = minimum value required to negate damage from triggering projectile but not from more powerful player weapons
.....armorvalue = a number high enough to where it wont go down when it gets shot, i believe 999999999 is a good amount, Preach has an article on this topic)
.....solid = 4
.....takedamage = 2
.....movetype = 7

notnull 2 (turret shoot trigger)
.....origin = same as the turret itself
.....targetname = whatever
.....use = hknight_shot
.....enemy = entity number of turret 
Demo Map And Map Source 
Here is a link to a zip containing a small crap box demo map showing the turret in action and the map source https://drive.google.com/file/d/1_2U4tXHpkpqdhfZi-3J1PyZVtOoFXp_r/view?usp=drivesdk I would have taken the time to make a somewhat better demo map but this took a little longer than I expected to write since I suck at trying to explain things lol, so I just decided to throw out the map I used for testing instead of making a somewhat more realistic use case environment. As usual if you have corrections or tips for improvements feel free to point them out :) 
Poorchop 
Haven't read this yet but GG. Grabbing my reading glasses. 
@dumptruck 
Well parts of it might be a little overexplained which accounts for some of the length lol. I wanted to be really informative though and make sure people get it so maybe it's ok. 
@rekta 
Holy shit, you got it working and working well to boot. Hell of a write up and a tricksy workaround with the th_pain bit, almost surprised you didn't find some evil way to make it rotate vertically as well as the usual horizontal rotation. 
Cool Turret 
Another impressive hack, well done. It's also inspired me to finish off an article I've been working on for a while. It's an ode to the oft mentioned function while hacking: SUB_CalcMoveDone

https://tomeofpreach.wordpress.com/2018/07/21/in-praise-of-sub_calcmovedone/

If you want to know the best way to make a gib fountain, or what the "Duke of York" hack is all about, come have a read... 
@Preach 
I am just getting started "cutting and pasting" QuakeC. I followed your recently revised tutorial on monster spawning here:

https://tomeofpreach.wordpress.com/2017/10/08/teleporting-monsters-flag/

I'd like to add a small random delay automatically to each teleporting monster as you can do in Quoth. Reason is I am using custents multiple triggers to trigger these delays spawns. As in Quoth it would be nice to have a little variation on the timing if the monsters all share a targetname.

Would this be as simple as modifying this:


self.think1();
//override the random delay some go functions apply
self.nextthink = time + 0.1;
spawn_tfog (self.origin);
spawn_tdeath(self.origin, self);
}


to this:


self.think1();
//override the random delay some go functions apply
self.nextthink = self.nextthink + random()*0.5;
spawn_tfog (self.origin);
spawn_tdeath(self.origin, self);
}
 
Update 
I was finally able to try this last night. Does do it. Any hints would be appreciated. 
Typo Above 
Doesn't do it. 
No Expert Here But... 
Should it be

self.nextthink = time + random()*0.5 
@Esrael 
I tried that as well. It's going to be a bit more complex I think. 
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.