In this tutorial I will teach you about the most important SoHL-entities.
Posted by the-middleman on Nov 28th, 2011
This tutorial is meant for people who have a lot of expirience mapping for Half-Life and at least some expirience with SoHL.
I Allways use SoHL Version 1.54. The entities discribed should be similar in other versions but I can not guarantee that they will work.
I want to tell you about the motion entities. Those entities that make the stuff in your map move. They are complicated but once you master them allmost anything you can think of is possible in half life. People have made gravity-guns with SoHL. At the end I will also give you three examples of how you can use these entities and you can try them out yourself.
The tutorial starts here
movewith calc- and motion- entities
Movewith is one of the most famous features of SoHL. However, once you start working with it you will find out that its possibilites are very limited. What movewith does is copy the movement of one entity and apply that movement to another.
You have a func_train and some func_breakable that moves with the train? If the train starts moving the breakable will move into the same direction. However movewith does not work correctly with rotating objects. If you have a func_tracktrain that takes a turn left, the window will follow the train but not turn accordingly. If the window was at the frontpart of the train in the beginning, now after the turn it is on the left.
Movewith only works with entities that move in a predetermined way; like a train, a door or an elevator. Movewith does NOT work with monsters, pushables or other entities that move "freely".
Movewith has a bug. When the moving entity is being blocked(by a player or a monster...etc.) and comes to a stop. The entities that movewith will keep going and the whole thing will get out of sync.
The movewith function is good for simple movement like:
When using movewith make sure the moving entity wont get stuck:
For any kind of complex movement with rotation, with dynamic not predetermined moving you should use the calc entities.
The calc entities:
This entity lets you determine the position of another entity.
Lets say you want a sprite to stick to a monster_human_grunt. Give the grunt a name and then enter its name in the field "Entity to use[LE]" in the calc_position.
You can choose where exactly on that grunt you want the sprite to be. Look at the "Position to calculate" field. Most options like "Eyes" or "Centre" are self explanatory.
Attachment points are special points in models. Not every model has these. But the human_grunt has an attachment point at its hand. So you can place a sprite(or anything you want) in the grunts hand. Use a model-viewer to find out if and where a model has attachment points.
The option "Random" chooses a random point somewhere inside your grunt. This random position changes all the time. After a second the random position wont be same as it was a second earlier.
Use an env_shooter to spawn firesprites at a grunts random position and it will look like the grunt is on fire!
The last field "Add offset[LV]" lets you alter the determined position. Lets say you choose the eyes position and an offset of 0 0 50. Now your position will be 50 units ABOVE the eyes. If you move your sprite to this calc_positions position it will be 50 units above the grunts eyes.
In the "Add offset[LV]" field you can also enter the name of another calc_entity...to make things more complicated.
The calc_position works not only with monsters but with ALL point- and brush-based entities. Of course not all entities have eyes...
Of course the calc_entity only determines a position. To actually move your sprite to that position you will need a motion entity, which I will explain later.
This entity lets you determine the facing or the movement of other entities. In the field "Entity to use[LE]" enter the name of the entity you want to observe.
"Value to calculate from" has several options:
The value calculated by the calc_subvelocity is allways a vector. You can alter it with the fields "Scale factor[LR] "(multiply) and "Add offset[LV]". In the flags you can tick "Discard X", "Discard Y" and "Discard Z" which will nullify these axis in the vector. "Flip vertical" makes -Z out of Z.
The flag "Normalize" makes the vectors lenth equal 1 but wont change its direction.
(The gun in this picture is supposed to be a func_tank, but it could be any other entity)
Just experiment with these settings you will see how they work.
The calc_velocity_path lets you find out how far it is from point A to point B. Example: You want to know how far away and which direction an enemy is from the player? Enter *player in the "Start position[LP]" field and the monster´s name in the "Destination" field. The calc_velocity_path now creates a vector that is as long as the distance between player and monster, and it points in the direction the player has to go to find that monster.
Again you can make some math alterations to that vector. In "Length Calculation" you can choose some formulas like "Square (X=X*X)", meaning as the distance between monster and player grows the vector will not grow linear but exponentially. Those are all self explaining. "Length factor [LR]" lets you scale the vector by any factor you want.
"Line is blocked by" Here you can choose whether your vector will pass through all obstacles like a ghost(default) or not.
calc_ratio and calc_velocity_polar
I dont know enough about these entities because I hardly ever use them. I cant say much about them.
calc_ratio lets you check values like a monsters health or a watchers count and then scale sprites, sound volumes or other stuff acordingly. Just look at the Attributes, most is self explaining.
calc_velocity_polar I think this lets you rotate an existing vector. Lets say you use a calc_subvelocity(see above) to find out what direction the player is looking. Now enter the subvelocities name at the "Based on velocity[LV]" field of the calc_velocity_polar. Enter 0 45 0 at the "Rotated by angle Y Z X" field.
When the player looks north the calc_subvelocity will point north too. The calc_velocity_polar will rotate this vector by 45 degrees and thus point northeast. I did not test this but it should work.
If you know more about these two entities let me know!
All calc_entities just get values. Motion entities use these values.
The motion_manager when turned on will move an entity the way you want it.
"Meaning of position" Here you decide how your value will affect the target. Lets asume that in the "Target to effect[LE]" field you entered the name of a func_pushable and in the "Position" field 30 0 -4.
What does moving "freely" mean? It is a little difficult to define what this means. But think of the trigger_push. The trigger_push is usually used to create "wind". Entities that move "freely" will be pushed by this wind. Monsters? Yes. Players? Yes. Grenades? Yes. Pushables, gibs, healthkits? Yes. Yes and Yes. But func_walls? No. func_doors? No. Just remember if your entity can me moved by a trigger_push and you will know which setting to use in the motion_manager.
Back to the motion_manager´s options:
In the flags you can select:
So much for the motion_manager. Remember that instead of 30 -4 0 you can use any other set of numbers or, even better, a calc entity to make things really dynamic and cool. Remember that sprite that we wanted to move with the grunts eyes in the calc_position tutorial? Use the motion_manager and you will be able to do that.
When you give a motion_manager a name it will be OFF by default. Remember to trigger it when you start your map.
The trigger_motion works allmost the same. The only difference is that it wont update all the time. Trigger it once and it will change the targets position/facing once. You can use a looped multi_manager to trigger the trigger_motion every 0.1 seconds. Then it will act allmost like a motion manager.
motion_managers are good for steady continous movement like:
- A pushable being moved in one dircetion
- A monster being atracted by another entity
- A func_breakable following a train.
trigger_motions are good for onetime changes like:
- A pushable getting a "kick" into a certain direction
- A func_wall being teleported to some other place.
One last note: When using brush-based entities in SoHL its allways a good idea to give them an origin.
Three examples how to use calc- and motion-entities
An UFO flying above the players head
Make a func_wall that looks like a UFO. Give it an origin brush and a name. Place the UFO somewhere in your map. Where is not important but it should be high enough. Now use a calc_position to determine the players position. Give the calc_position a name. Now place a motion_manager in your map. Make it affect the UFO. In the "Position" field enter the name of the calc_position. Use "Set position[LP]" and in the "Axis to Modify" field use "Not Z". This is to prevent the UFO from changing its altitude. Give the motion_manager a name. Use a trigger_auto to turn on your motion_manager.
Get ingame: After the motion_manager has be activated your UFO should follow you everywhere. Remember your UFO does not change its height. That means if you climb high enough you might even touch it.
If you want to learn more try:
- using offsets in the calc_position
- making the UFO more than just a func_wall. Give it a weapon or something.
A laserpointer for a human grunt
This will give a monster_human grunt a cool laserpointer for his gun. How does this work? The laserpointer will be a env_laser. It will start at the tip of the grunts gun and will point into the direction the gun is pointed.
Place a monster_human_grunt in your map and give it a name. Now we must find out where the tip of his gun is. The human_grunt model has an attachment point at the tip of the gun. Use a calc_position. The "Entity to use" is your grunt and the "Position to calculate" is "Attachment point 0". You now know where you laser has to start.
What direction does the gun point? The grunt has another attachment point at his right hand. When you think of an arrow(or a vector) that starts at his hand and goes to the tip of the gun you know that this arrow will point in the direction he is aiming. Just think how you would hold a gun and how an arrow from your hand to the tip of the gun would look like.
Make another calc_position. This time use "Attachment point 1". Thats his hand.
Now to create the "arrow" we use a calc_veloctiy_path. In the "Start position" enter the name of your "hand" calc_position and in the "Destination" field enter you "guntip" calc_position.
You now have all the information your laserpointer needs. Create a env_beam. In the "Start At(blank = here)[LP]" field enter the name of your "guntip" calc_position and in the "Fire towards" field the name of your calc_velocity_path. In "Meaning of fire towards" choose "Direction [LV]". In the "Projection mode" choose "Extend past endpoint". That means your laser keep going until it hits a wall or something. If you dont choose "Extend past endpoint" your laser will only be as long as the distance between the hand and the guntip...and that means really short and totally not-laser-like.
Youre allmost done. Get ingame and try your laser. To be save make it be off be default and turn the laser on after the map has loaded. You will notice that the laser does not aim exactly right. It will probably go a bit too far up. This is because the hand of the grunt is a little lower than the guntip. You can fix this easily. Get back into worldcraft and change your "hand" calc_positions "Offset". Play around with it until your laser aims correct.
Now comes the fun part. You can design your laser as you want it. Red yellow or green? Really thin(like a laser) or frickin thick? Change the Render Fx to make it flicker or something. You can even give it some damage. Now your grunt can kill stuff by just aiming at it!
Important note: If this grunt dies and gets gibbed(grenade explosion) HL will crash. This will hopefully be fixed in one of the the next spirit versions. Until then make sure your grunt cant be gibbed...dont ask me how...you cant really stop a grunt from blowing himself up with a grenade...they do that alot actually...
If you want to learn more:
Go to the "Entity Guide" which comes with every version of SoHL and look up the env_beams "End sprite" Think how you can use this. Remember you can make stuff appear/happen at a sprites position now.
Picking up objects
Look at this video. Just like in HL² I can pick up items and carry them around.
Writing this tutorial I notice that this is pretty easy to map, but hard to explain. Just hang in there and try to follow what im writing. Ill make plenty of pictures at the end.
To understand this tutorial you should:
This is what we need:
First of all we need a point infront of the player. This is the position that carried objects will follow. Create a calc_position to get the players position. Add a calc_subvelocity to get the players viewangle. Enter the subvelocitys name at the "Offset" field of the calc_position. Now your calc_position is a point in front of the player. Use the "Scale Factor" field to determine how far from the player that point is. Enter a "Scale factor" of 50 and you will hold objects 50 units infront of you. Experiment with this value a little. If you choose a point to far from the player, it looks not realistic. If you choose point to close you might get problems picking up huge crates.
To "carry" objects around you will need a motion_manager. You can use it to make objects follow the point in front of the player. But how does the motion_manager know what object to affect/how does the player "choose" what to carry around?
The answer is a trigger_multiple. Create a trigger_multiple and give it an origin-brush. Use a motion_manager to set its position to that point in front of the player. This trigger_multiple will now allways stay in front of the player. An object inside this trigger will be selected as the one to be picked up. So by looking at an object from close distance you will "wrap" the trigger_multiple "around" that object. In the "Target" field of the trigger multiple enter the name of a motion_manager.
So if an object gets into your trigger_multiple it will trigger the motion_manager and thus be the locus. In the "Target to affect" field of the motionmanager enter *locus. Your motion_manager now knows what entity to affect.
There are different methods on how to "carry" an object but I prefer this one. Use a calc_velocity_path to get the distance from the locus(the object you carry) to the point in front of the player(reffer to the calc_position here). Make the "Length Calculation" "Square". Then have the motion_manager "Set velocity" of the object according to your calc_velocity_path. Meaning: If the object you carry is not where it should be(should in front of the player) it will get some speed so it gets there.
You can now pick up objects by looking at them. Looking at them? Thats not what you wanted, you wanted to press a button to pick them up.
Well its simple. So far I said enter the name of the motion_manager in the target field of the trigger_multiple. So of course as soon as an object is in the trigger_multiple it will get picked up. You can change this by leaving the target of the trigger_multiple blank. Now nothing happens when you look at an object. Add a trigger_changetarget. Make it so that this trigger_changetarget changes the target of the trigger_multiple to the motion_manager. When you trigger the trigger_changetarget your trigger_multiple will now target the motion_manager.
In spirit you can trigger entities by typing fire in the console. This only works when sv_cheats is 1. You can now bind a key so that it will trigger the trigger_changetarget. Type bind "k" "fire ".
Look at an object you want to pick up. Nothing happens because your trigger_multiple has no target. Press k and your trigger_multiple will get a target: the motion_manager.
You must add another trigger_changetarget that will set the trigger_multiples target back to blank shortly after the first trigger_changetarget. This is to make sure the pickup function works more than just once.
You can now pick up objects by looking at them and pressing k. Dropping items is very simple. Just deactivate the motion_manager and the object you were carrying will act normal again...fall down. Bind another key to deactivate the motion_manager. This will be your drop key.
If youre feeling fancy you can use a couple of trigger_relays and trigger_changetargets and make it so that you have one key for both picking up items and dropping them again. Im leaving you to figure it out by yourself. If you can´t do it just use two keys.
Is that all? No. In spirit things are never that easy. The problem is that a trigger_multiple only recognizes objects that move. A crate that just stands still wont be recognized and that means you wont be able to pick it up. Solution. Add a trigger_push with the same size as your trigger_multiple. Give it very little push speed and set its angle to "up". Trigger it when you want to pick up an item. The object you want to pick up will move allmost unnoticable but just enough for the trigger_multiple.
(Man! Huge vectors coming out of a guys pelvis! Scale Factors! I should sell penis enlargement products!)
So far I have allways talked about picking up "objects". But in HL there are no "objects" only entities. The question is what entities can be picked up?
Your trigger_multiple controls what entities can be picked up. Look at the flags first.
The trigger_multiple also has a "Triggered only by entity" field. Here you can enter a name. Now only entities with that name can be picked up. Enter something like "pickmeup" here and then name some entities "pickmeup". The player will only be able to grab these. This is good if you want the pick up option in your map, but dont want the player to become too powerfull.
That is all folks! If you have questions, write me! Also check out my Project Half-Life: Legion which is done entirely in SoHL!