Jump to content
The Dark Mod Forums
demagogue

Newbie DarkRadiant Questions

Recommended Posts

@Dragofer

I'm making a shooter that shoots at the player, using moveTo($player1). It works so far, but it targets the feet of the player. I did bind a func_emiter to the entity that moves to the player and put it higher so it shoots at head level, but when I crouch down, it shoots over the head, ofcourse. Is there a head or torso entity that that I can target?

Share this post


Link to post
Share on other sites

@STRUNKnot that I know of, but what you can do is take the origin of the player and increase its z component. Should go like this.

vector player_position = $player1.getPos();

player_position_z = player_position_z + 40;

moveTo(player_position);

 

Btw, I could suggest taking a look at river.script from my fm Down by the Riverside. It contains lots of the scripting I've done so far, with a fair amount of commenting.

  • Like 1

Share this post


Link to post
Share on other sites

Ah, I must've merged my memory of getOrigin with moveToPos.


Btw, I just remembered you can make scriptobjects if you want to use variable entities. That's how candle holders work, they call all their script functions on their associated entities. 
 

Scriptobjects are on a different level from plain scripting and go in a different folder. I haven't really gotten into them yet. There's a wiki article here, but it seems to assume some prior knowledge, i.e. it says you need definitions, constructors and members but not what they are.

  • Like 1

Share this post


Link to post
Share on other sites

Alright, I've figured out the scriptobjects now, scripting JackFarmer's desired setup as an example. Feel free to correct anything I write below of the way I understand their workings.

sAIidDg.jpg

It's a test map containing 3 cloned setups for a mover (gravestone + speaker + button). The gravestones have numerous custom spawnargs that get looked up by this scriptobject to control various aspects of their movement, i.e. invert 1 and invert_count 4 causes the mover to reverse direction after 4 movements. Both rotation and translation are supported.

The scriptobject consists of 3 smaller scripts. First comes the main script (object reusable_script) where you define variables that should be available for all other scripts and what the names of the other scripts are. There must always be an "init" script, and I've also defined a "move_mover" script that does the actual movement. Note that all the variables start with an m_. This is optional and means "member": a member of this particular scriptobject and not available to any other scripts.

The "init" script gets run at mapstart. The variables that were defined in the main script now get assigned values by looking up spawnargs on entities in your map. These variables can then be used in the "move_mover" script.

The "move_mover" script is basically like a normal script that controls the mover, the button and the speaker. The difference to a normal script is that it uses variable names that are specific to entities associated with that particular mover. So you can simply select 1 mover setup in DR and duplicate it to get more.

Spoiler

#ifndef __REUSABLE_SCRIPT__
#define __REUSABLE_SCRIPT__

object reusable_script
{
	entity	m_speaker;		//the speaker bound to the mover. Unfortunately directly scripting sounds for movers is broken. #5102
	entity	m_button;		//button controlling the mover
	vector	m_translate;		//amount to translate
	vector	m_rotate;		//amount to rotate
	float	m_time;			//total time taken to move
	float	m_accelTime;		//time taken to accelerate
	float	m_decelTime;		//time taken to decelerate

	boolean	m_invert;		//should the mover invert after (invert_count) number of movements
	float	m_invert_count;		//number of movements the mover should make before inverting movement direction
	float	m_trigger_count;	//how many times the mover has moved in one direction before inverting

	void	init();
	void 	move_mover();
};


void reusable_script::init()
{
	m_speaker		= getEntityKey("speaker");
	m_button		= getEntityKey("button");
	m_translate		= getVectorKey("translate");
	m_rotate		= getVectorKey("rotate");
	m_time			= getFloatKey("time");		
	m_accelTime		= getFloatKey("accelTime");
	m_decelTime		= getFloatKey("decelTime");
	m_invert		= getBoolKey("invert");
	m_invert_count		= getFloatKey("invert_count");
	m_trigger_count		= -1;
}


void reusable_script::move_mover()
{
	m_button.setKey("trigger_when_opened", "0");   				//disables button's functionality until script has finished

	if (m_invert)								//if the mapper desires the mover to invert direction after x number of movements (invert = 1)...
	{
		m_trigger_count = m_trigger_count + 1;				//add 1 to the trigger count
		if(m_trigger_count == m_invert_count)				//when trigger count meets desired count before inverting movement...
			{
				m_translate		= -m_translate;		//invert translation vector
				m_rotate		= -m_rotate;		//invert rotation vector
				m_trigger_count 	= 0;			//reset trigger count to 0
			}
	}

	vector m_currentPosition	= self.getOrigin();			//where is the mover now?
	vector m_targetPosition		= m_currentPosition + m_translate;	//add desired translation vector to current position to find target position

	self.time(m_time);
	self.accelTime(m_accelTime);
	self.decelTime(m_decelTime);
	self.rotateOnce(m_rotate);
	self.moveToPos(m_targetPosition);

	sys.trigger(m_speaker);
	sys.waitFor(self);
	sys.trigger(m_speaker);

	m_button.setKey("trigger_when_opened", "1");   				//re-enables button's functionality now that script has finished
}


#endif __REUSABLE_SCRIPT__

 

The only way I could find so far to call the scriptobject is: button -> atdm:target_callobjectfunction with spawnarg "call" "move_mover" -> gravestone mover with spawnarg "scriptobject" "reusable_script".

 

You can have this scriptobject either in your map's .script file, or you can put it in its own .script file in your /script folder. You will need to include the name of that .script file in your tdm_custom_scripts.script file so that it gets loaded. Having it in the /script folder reduces clutter in your map's .script file and makes it available to all of your maps, but you have to restart TDM before changes take effect and you get a blue error window when there's a mistake in the script. (Maybe there's a reloadScripts function?) Click on copy and paste it to a text editor to read the full error message.

The lines at the very top and bottom of the script file (#ifndef) ensure the script file can only be included once.

scriptobject.pk4.txt

  • Like 2

Share this post


Link to post
Share on other sites

I see there is " entity getHead( ) " Returns the entity used for the character's head, if it has one. "

If $player1 has a seperate entity as a head, and this script event can find it .. how can I let it tell me. Can I make the name show up in the console?

I tried:

entity head = $player1.getHead();
sys.print(head);

But that doesnt work ...

Share this post


Link to post
Share on other sites

There is a player model that you can see in mirrors, but I'm not sure if you can use getHead on it. How about getEyePos();?

Also I've always used sys.println();, idk if sys.print(); works

  • Like 1

Share this post


Link to post
Share on other sites

@Dragofer

Ok, so there is also entity "bindToJoint()" and it seems to work, but wherever I bind an entity (to target at) to, it stays at the same hight ... so I guess $player1 is not like a normal AI.

I will try getEyePos() then : )

Is there a way to "know" when the player is crouching?

Edited by STRUNK

Share this post


Link to post
Share on other sites

@Dragofer

OH YES!!!

void MoveToPlayer()

{
    //Have entity "$BindToThis" move to the position of the head/eyes of the player en return to entity "$ReturnToThis"
    //Script is triggered by a "trigger_timer" timer targeting a "atdm:target_callscriptfunction" with spawnarg "call - MoveToPlayer"

    
    $BindToThis.time (0.8);                                 
    $BindToThis.accelTime (0);                             
    $BindToThis.decelTime (0);	                      
    vector head = $player1.getEyePos();
    $BindToThis.moveToPos(head);                        
    sys.wait(1);
    $BindToThis.moveTo($ReturnToThis);                        
    
}

It totally works. Now I have to tweak it for constant speed, for now te speed is dependent on the distance the player is from entity "$ReturnToThis"

I also have a gorgoyle (atm) as a turret and it looks at the player all the time. I did this with a tweaked elemental that stays in one place and gets the script event turnToEntity() that can only be used on AI. The gorgoyle binds to the elemental.

Is there a way of scripting this without a (for me) very complex formula ... ?

  • Like 1

Share this post


Link to post
Share on other sites

Nice - about your func_shooter script, you can use speed() instead of time() for a constant speed. Also no need for the lines accelTime & decelTime if you're setting them to 0. You can use sys.waitFor($BindToThis) instead of sys.wait() to let the script wait however long the projectile needs to reach the player.

As for turning gargoyles towards players, something similar was done in Obsttorte's turning statues script, where the statues turn towards the player whenever he doesn't look at them. It looks like he uses mathematics (cos, sin, tan) to calculate the angle of the vector between statue & player.

  • Like 1

Share this post


Link to post
Share on other sites

@Dragofer

 

Ok, speed works : )

I was just about to get the distance with "float distance = $ReturnToThis.distanceTo($player1);"

Then I wanted to put: "float speed = 1/distance", to feed "$BindToThis.speed (distance);" , but what is the right syntax for "float speed = 1/distance" ?

Also thanx a lot for your help! It's greatly appreciated : )

==

 

It's more like "distance/1000", but it also works

    float distance = $ReturnToThis.distanceTo($player1)
    $BindToThis.time (distance/1000);

 

Edited by STRUNK
  • Like 1

Share this post


Link to post
Share on other sites

Making speed equal to 1/distance would make it slower the further away the player  is. The formula would rather be speed = distance/1, or just distance. It'll always take 1s to travel, so might as well just set a time of 1 for the mover.

Anyway, float x = 1/y; or 1 / y; looks like correct syntax to me, but I don't think I've tried it before.

Share this post


Link to post
Share on other sites
3 minutes ago, Dragofer said:

Making speed equal to 1/distance would make it slower the further away the player  is.

You are totally right : )

Share this post


Link to post
Share on other sites

@Dragofer

I checked out your scriptobject script and map, and it is awsome. This opens a lot of possibilities. Thank you very much for working this out!

Edited by STRUNK
  • Like 1

Share this post


Link to post
Share on other sites

@Dragofer @JackFarmer

I made a custom definition for func_mover_dragofer that has all the used spawnargs and "help" text in it, and it works so far. Only thing is you still need to manually target the atdm:target_callobjectfunction to call the script move_mover, and manually put in the names for the button and speaker. If we somehow can manage to get the name of the button targeting the func_mover_dragofer and have de audio play from the script + target the move_mover script from this definition, it would be rather awsome. Maybe it should have a frob on/of function like a door, and have an interuptable/not interuptable function also, so it can be triggered by frobbing, or any button/switch/trigger you want.

Put a folder "def" in your own mission folder and put tdm_func.def in there. (re-start darkradiant) func_mover_dragofer should be selctable when you rightclick and select Create Entity. It's on the top of the list in "fms".

tdm_func.def

  • Like 1

Share this post


Link to post
Share on other sites

@STRUNKnice, who'd have thought I'd get an entity named after me one day 😎 Good idea to make it a proper entity so that it displays all the custom spawnargs properly in DR.

As you point out there are indeed still a few things that can make the new scriptobject fully versatile. Func_movers aren't really accessible to mappers who don't script, so it may be quite useful to have one that mappers can control by spawnargs instead of scripts.

 

1) the mover could spawn its own speaker (def_attach). A sound for it can be selected from a list provided by a new snd_move spawnarg. (Workaround for bug #5102)

2) maybe I can find a way to let the mover call its move_mover component when triggered or frobbed. Any button could set it in motion then.

3) the move_mover script shouldn't respond to new triggers until it's done. Unless the mapper wants it to be interruptable.

4) the trigger_count spawnarg can be used to make the mover's first inversion occur sooner (i.e. the mover has already moved a few times at map start)

etc.

Something of a stumbling block for me atm is I couldn't yet figure out how to access an entity's rotation. I'd like something like $mover.getRotation() so I know what direction it's facing currently (in x, y and z). Essential for an "interruption" functionality. Well probably mover_door has what I need.

  • Like 1

Share this post


Link to post
Share on other sites

@Dragofer

I think a mover entity like that would be very useful to a lot of mappers. I only began looking into scripting since this year, and before thet I would usually use doors for like everything that has to move. If this entity was available then I would heve used it for sure. Only think I think of now is that it should be able to trigger things, like doors do.

The script event to get rotation of an entity is  vector getAngles()     https://modwiki.dhewm3.org/GetAngles_%28script_event%29

Edited by STRUNK

Share this post


Link to post
Share on other sites

@Dragofer

Thanx for pointing out Obsttorte's turning statues script. He makes use of a system script event "VecToAngles()" that calculates the angle from a given vector.

I took a part of the script and it now totally works without having to bind the statue to an elemental : )

void LookAtPlayer()
{
   //Makes $Statue look at $player1
   
   vector direction=$Statue.getOrigin()-$player1.getOrigin(); 
   direction_z=0;
   $Statue.setAngles(sys.VecToAngles(-direction)+'0 90 0');
  
}

 

Share this post


Link to post
Share on other sites

Took a look at his actual script too now - just need to put that into his while loop like so and you're set:

	while(1)
	{
		if (distanceTo($player1)<1024)
		{   
            vector direction=$Statue.getOrigin()-$player1.getOrigin(); 
   			direction_z=0;
   			$Statue.setAngles(sys.VecToAngles(-direction)+'0 90 0');
        }
   	}
       	sys.waitFrame();
                                       

Probably also bind a trigger_hurt brush to the particle and toggle it on whenever it starts flying. Please do post that to the Trap Workshop when it's finalised (f you're not first reserving it for your own FM).

On my side, looks like the func_mover already supports movement sounds (snd_move, snd_accel, snd_decel etc.), but they don't really seem to work properly unfortunately and are bugtracker-worthy in several ways, i.e. don't play at their intended times, loop forever if the original shader has looping, popping sounds etc. Guess I'll just use my own way and spawnargs for getting sound to play.

Share this post


Link to post
Share on other sites

I've got a container that shall include a standing idle steam bot.

bot.jpg.42693d68767e9a1a3001b1ec0b001232.jpg

 

The bot in the shot above is taken from the char folder and does not show up ingame (I anticipate this is used for character rigging).

How can I create an inactive bot?

Share this post


Link to post
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.


×
×
  • Create New...