View Issue Details

IDProjectCategoryView StatusLast Update
0005050The Dark ModScript/Defpublic23.03.2020 00:05
ReporterDragofer Assigned To 
PrioritynormalSeveritynormalReproducibilityalways
Status newResolutionopen 
Product VersionTDM 2.07 
Summary0005050: Electric lights don't toggle hide/show of targeted entities
DescriptionSwitching an electric light on or off will not toggle targeted entities between hide/show. An example usage case would be to let rays of moonlight appear when the light is switched off. To my knowledge this only works with lamps that have def_attached light entities, where the def_attached light entity targets the hidden entity via for instance "set target0 on flame".
Steps To ReproduceFind attached a small test map with an electric light and a lantern. Toggling the lantern will toggle the hidden lit version of the window. However, flipping the switch of the electric light will have no effect on the window.
TagsFix

Relationships

related to 0002564 new add "toggleTargets" to tdm_light_holder script object 

Activities

Dragofer

Dragofer

08.09.2019 15:37

developer  

toggle_hide_show.map.txt (6,159 bytes)   
Version 2
// entity 0
{
"classname" "worldspawn"
"editor_drMapPos1" "145.117 609.786 279.045"
"editor_drMapAngle1" "-48 150.8 0"
"editor_drMapPos2" "414.798 255.441 -264.933"
"editor_drMapAngle2" "2.10002 84.5976 0"
"editor_drMapPos3" "-749.97 -1606.21 -470.066"
"editor_drMapAngle3" "-3.29998 6.59757 0"
"editor_drMapPos4" "1014.05 -953.679 -556.857"
"editor_drMapAngle4" "-10.2 355.498 0"
"editor_drMapPos5" "987.599 3513.79 -319.877"
"editor_drMapAngle5" "-61.1999 172.198 0"
"editor_drMapPos6" "-1032.5 -2840 739.5"
"editor_drMapAngle6" "-77.0999 175.798 0"
"editor_drMapPos7" "952.822 285.005 1366.04"
"editor_drMapAngle7" "-74.8 149.373 0"
"editor_drMapPos8" "976 424 320"
"editor_drMapAngle8" "-3 15.3467 0"
"editor_drMapPos9" "409.146 547.464 392.83"
"editor_drMapAngle9" "-16.8 284.947 0"
"editor_drLastCameraPos" "29.5975 -22.8642 72.2517"
"editor_drLastCameraAngle" "-32.1 168.9 0"
// primitive 0
{
brushDef3
{
( 0 0 1 -144 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 1 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 1 0 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 -1 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( -1 0 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 0 -1 128 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/darkmod/wood/boards/pier_platform" 0 0 0
}
}
// primitive 1
{
brushDef3
{
( 0 0 1 -128 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 1 0 -80 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 1 0 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 0 -1 0 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( -1 0 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 -1 0 64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/darkmod/wood/boards/pier_platform" 0 0 0
}
}
// primitive 2
{
brushDef3
{
( 0 0 1 -128 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 1 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 1 0 0 -80 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 0 -1 0 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 -1 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( -1 0 0 64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/darkmod/wood/boards/pier_platform" 0 0 0
}
}
// primitive 3
{
brushDef3
{
( 0 1 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 1 0 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 0 -1 -16 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 -1 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( -1 0 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 0 1 0 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/darkmod/wood/boards/pier_platform" 0 0 0
}
}
// primitive 4
{
brushDef3
{
( 0 0 1 -128 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 1 0 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 0 -1 0 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 -1 0 -80 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( -1 0 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 1 0 64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/darkmod/wood/boards/pier_platform" 0 0 0
}
}
// primitive 5
{
brushDef3
{
( 0 0 1 -128 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 1 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 0 -1 0 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 0 -1 0 -64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( -1 0 0 -80 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/common/caulk" 0 0 0
( 1 0 0 64 ) ( ( 0.015625 0 0 ) ( 0 0.015625 0 ) ) "textures/darkmod/wood/boards/pier_platform" 0 0 0
}
}
// primitive 6
{
patchDef2
{
"textures/darkmod/window/diamond_pattern02/diamond_pattern02"
( 3 3 0 0 0 )
(
( ( 16 54 24 0 0 ) ( 16 54 48 0 0.5 ) ( 16 54 72 0 1 ) )
( ( 32 54 24 0.5 0 ) ( 32 54 48 0.5 0.5 ) ( 32 54 72 0.5 1 ) )
( ( 48 54 24 1 0 ) ( 48 54 48 1 0.5 ) ( 48 54 72 1 1 ) )
)
}
}
}
// entity 1
{
"classname" "atdm:wall_sphere_lit"
"name" "atdm_wall_sphere_lit_1"
"origin" "-40 48 32"
"light_center" "0 0 28"
"light_radius" "180 180 180"
"model" "models/darkmod/lights/non-extinguishable/lamp_wall_sphere.ase"
"s_shader" "light_flicker_104"
"target0" "func_static_1"
}
// entity 2
{
"classname" "info_player_start"
"name" "info_player_start_1"
"origin" "-8 -48 0"
"angle" "90.000000"
}
// entity 3
{
"classname" "atdm:switch_flip"
"name" "atdm_switch_flip_1"
"origin" "-8 0 32"
"rotation" "1 0 0 0 1 0 0 0 1"
"rotate" "0 0 45"
"target0" "atdm_wall_sphere_lit_1"
"model" "models/darkmod/mechanical/switches/switch_rotate_lever.ase"
"frob_distance" "80"
"frob_box_size" "80"
}
// entity 4
{
"classname" "func_static"
"name" "func_static_1"
"model" "func_static_1"
"origin" "32 52 48"
"hide" "1"
// primitive 0
{
patchDef2
{
"textures/darkmod/window/diamond_pattern02/diamond_pattern02_brightlit"
( 3 3 0 0 0 )
(
( ( 16 52 24 0 0 ) ( 16 52 48 0 0.5 ) ( 16 52 72 0 1 ) )
( ( 32 52 24 0.5 0 ) ( 32 52 48 0.5 0.5 ) ( 32 52 72 0.5 1 ) )
( ( 48 52 24 1 0 ) ( 48 52 48 1 0.5 ) ( 48 52 72 1 1 ) )
)
}
}
}
// entity 5
{
"classname" "atdm:moveable_lantern_oil_hand02_lit"
"name" "atdm_moveable_lantern_oil_hand02_lit_1"
"origin" "-56 -16 12"
"rotation" "1 0 0 0 1 0 0 0 1"
"set target0 on light" "func_static_1"
}
// entity 6
{
"classname" "light"
"name" "ambient_world"
"origin" "56 -56 120"
"light_center" "0 0 0"
"light_radius" "320 320 320"
"_color" "0.1 0.1 0.1"
"texture" "lights/ambientlightnfo"
}
toggle_hide_show.map.txt (6,159 bytes)   
VanishedOne

VanishedOne

08.09.2019 18:06

developer   ~0011837

Last edited: 08.09.2019 18:26

View 2 revisions

I see it's only the electric light that generates these console errors:

WARNING:script/tdm_light_holders.script(212): Thread 'tdm_light_holder::LightsT
oggleResponse': Function 'Close' not supported on entity 'func_static_1'
WARNING:script/tdm_light_holders.script(213): Thread 'tdm_light_holder::LightsT
oggleResponse': Function 'Off' not supported on entity 'func_static_1'
WARNING:script/tdm_light_holders.script(173): Thread 'tdm_light_holder::LightsT
oggleResponse': Function 'Open' not supported on entity 'func_static_1'
WARNING:script/tdm_light_holders.script(174): Thread 'tdm_light_holder::LightsT
oggleResponse': Function 'On' not supported on entity 'func_static_1'

Edit: at the moment my guess is that tdm_light_holder is intercepting STIM_TRIGGER, and tries to do various things to the target but not simply activate it.

Dragofer

Dragofer

08.09.2019 18:13

developer   ~0011838

That might have to do with what I saw when I targeted doors from lamps: the doors would open/close when the lamps are lit/extinguished. Only wanted to swap the window skins on the doors.
VanishedOne

VanishedOne

08.09.2019 18:33

developer   ~0011839

Apparently calling Open/Close was added in 0002676.

That leads me to http://forums.thedarkmod.com/index.php?/topic/12418-beleaguered-fence-broken-due-to-tdm-change/ -- 'So it would seem like extinguishable light targetting something else do not work anymore. Looks like a tdm change has broken something and most likely this applies to all maps with extinguishable lights targeting something.'

'The reason was the removal of the activateTargets( self ); call. I see that there were problems with that, but I guess setting up a proxy script function should do the trick?'

'The problem with "activateTargets()" is that it is different from On/Off, it always toggles, that is not what the light holder entities are supposed to do, they are supposed to turn things on/off in sync.'
VanishedOne

VanishedOne

08.09.2019 20:34

developer   ~0011840

Last edited: 08.03.2020 16:23

View 2 revisions

I'm not sure The Beleaguered Fence has quite the original behaviour in its final revision: I found the same as http://forums.thedarkmod.com/index.php?/topic/11298-fan-mission-the-beleaguered-fence-by-sotha-20100623/&do=findComment&comment=323573 even on the highest difficulty, with the door already open. However, I confirmed that the torch *does* still target the door, and that the current behaviour is

torch on -> door opens (or stays open the first time)
torch off -> door closes

while with the original behaviour restored in the light holder script, the torch toggles the door again, so because it now starts open it becomes

torch on -> door closes
torch off -> door opens

The script could be modified to treat lights and light holders specially and simply activate other targets, which is what I'm trying now. Personally I think giving it special handling for frob movers was a mistake; they have nothing to do with light state. However, it's always *possible*, however unlikely, that some FM relies on the current non-toggle behaviour for frob mover targets. As noted, in TBF it does make a difference even if you have to go out of your way to discover it.
VanishedOne

VanishedOne

09.09.2019 14:51

developer   ~0011841

Attaching my altered light holder script in case anyone wants to try it. I haven't tested it thoroughly, but the test map now works (with a nice lack of console spam) and routine gameplay seems normal so far.
tdm_light_holders.script (11,929 bytes)   
// vim:ts=4:sw=4:cindent
/***********************************************************************

A script object that can be used on any entity (movable, frobable etc.)
that acts as a holder for other light entities. Examples are candle
holders, oil lamps etc.

This script object just adds a few functions that allow to call script
events on the holder, like LightsOff(), LightsOn() and LightsToggle().

These events cause all bound lights to change state, and this in turn
will allow model and skin changes on the light holder.

Code copied from tdm_frobactions.script. Author: Tels

Note: the delay for noshadows is defined in scripts/tdm_lights.script

***********************************************************************/

#ifndef __DARKMOD_LIGHT_HOLDERS__
#define __DARKMOD_LIGHT_HOLDERS__

object tdm_light_holder
{
	boolean m_bExtinguished;
	// if someone toggles the light, store which entity this was, in case
	// it is also bound to us, to avoid toggling it again, resulting in endless
	// trigger loops
	entity activator;

	void init();
	void LightsOff();
	void LightsOn();
	void LightsToggle();
    void LightsToggleResponse(entity e, entity f); // Obsttorte #3760
    
	// internal routines:
	void toggle_children( float state );
	void toggle_child( entity child, float state, boolean isTarget );
};

void tdm_light_holder::init()
{
	entity child;
	float ind;

	//sys.println(sys.getTime() + ": DEBUG (holder::init): initializing light holder " + getName());
	// grayman #2603 - for the case where the holder is a light, fire initial visual stim
	StimClearIgnoreList (STIM_VISUAL);
	StimEnable (STIM_VISUAL, 1);
    
    // Obsttorte #3760
	ResponseAdd(STIM_TRIGGER);
	ResponseSetAction(STIM_TRIGGER,"LightsToggleResponse");
	ResponseEnable(STIM_TRIGGER,1);
    
	if (getKey("extinguished") == "0")
	{
    	//sys.println(sys.getTime() + ": DEBUG (holder::init): initializing light holder " + getName() + " on");
		m_bExtinguished = false;
	}
	else
	{
     	//sys.println(sys.getTime() + ": DEBUG (holder::init): initializing light holder '" + getName() + "' off");
		LightsOff();		// grayman #2823 - tell yourself and your children to go out

		// grayman #2905 - set self (in case we're a light) and light children that we were spawned off

    	//sys.println(sys.getTime() + ": DEBUG (holder::init): calling setStartedOff for self " + getName());
		setStartedOff();
		for ( ind = 0 ; ind < numBindChildren( ) ; ind++ )
		{
			child = getBindChild( ind );
			if ( child.isLight() )
			{
    			//sys.println(sys.getTime() + ": DEBUG (holder::init): calling setStartedOff for child " + getName());
				child.setStartedOff();
			}
		}
	}

	if (getKey("noshadows_lit") == "1")
	{
		//sys.println(sys.getTime() + ": DEBUG: light holder setting shadows");
		noShadows( !m_bExtinguished );
	}
}

// Helper function for toggling any bound or targetted light objects (like flames)
// or other entities like movers:
void tdm_light_holder::toggle_children( float state )
{
	//sys.println(sys.getTime() + ": DEBUG (holder::toggle_children): state = " + state);
	entity child;
	float ind;

	for( ind = 0; ind < numBindChildren( ); ind++ )
	{
		child = getBindChild( ind );
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_children): toggling child " + child.getName());
		toggle_child( child, state, false );
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_children): setting _light_toggled key to 1 on child " + child.getName());
		child.setKey( "_light_toggled", "1");	// mark as done
	}
	// and now do the same for our targets, but ignore any we already did
	for( ind = 0; ind < numTargets( ); ind++ )
	{
		child = getTarget( ind );
		//sys.println(sys.getTime() + ": DEBUG toggle target: " + child.getName() + " state = " + state); 
		toggle_child( child, state, true );
	}
	for( ind = 0; ind < numBindChildren( ); ind++ )
	{
		child = getBindChild( ind );
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_children): setting _light_toggled key to 0 on child " + child.getName());
		child.setKey( "_light_toggled", 0);	// reset mark
	}
}

// Helper function for toggling one bound or targeted object (like flames or movers)
void tdm_light_holder::toggle_child( entity child, float state, boolean isTarget )
{
	if ( child == $null_entity )
	{
		return;
	}
	//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " starting ...");
	// #3048: avoid endless loops when this child is a mover bound to us, and linked back to us
	if (child.getIntKey("_light_toggled") == 1)	// marked as done?
	{
		//sys.println(sys.getTime() + ": DEBUG child " + child.getName() + " already toggled");
		return;
	}

	if (child.getIntKey("noactivate") != 0)	// or as noactivate?
	{
		//sys.println(sys.getTime() + ": DEBUG child has noactivate");
		return;
	}

	//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): " + child.getName() + " state = " + state); 
	string unlit_skin = child.getKey( "skin_unlit" );
	string lit_skin = child.getKey( "skin_lit" );
	string cur_skin = child.getKey( "skin" );
	//sys.println(sys.getTime() + ": DEBUG skin: " + cur_skin + " skin_unlit: " + unlit_skin + " lit_skin: " + lit_skin);

	if ( state > 0) // 1 = turn on 
	{
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " will be relit");

		// tels: call this in a sep. thread, otherwise it doesn't return until the 
		// ext. animation+particle have finished, which can be several seconds.
		// this poses problems when you ignite/extinguish more than one bound flame:
		if (child.hasFunction("frob_ignite") )
		{
			//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " using frob_ignite() in a new thread");
			thread callFunctionOn( "frob_ignite", child);
		}
		else
	   	{
			if (child.hasFunction("LightsOn") )
			{
				// child is a light holder?
				//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " using LightsOn() because the child is a light holder, in a new thread");
				thread callFunctionOn( "LightsOn", child);
			} else
			{
				//sys.println(sys.getTime() + ": DEBUG light on");
				if ( child.isLight() ) child.On();
				else if (isTarget) child.activate(self);
			}
		}

		// #3109: in case the spawnarg is set and the current skin is different,
		// the swap the skin, too. For lights, or light holder, the skin might
		// already have been swapped, so skip this step:
		if (lit_skin)
		   	{
			if (cur_skin != lit_skin)
				{
				//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): setting lit skin");
				child.setSkin( lit_skin );
				// store for later retrival by scripts
				child.setKey( "skin", lit_skin );
				}
			}
	}
	else // 0 = turn off
	{
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " will be extinguished");
		if (child.hasFunction("frob_extinguish") )
		{
			//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): " + child.getName() + ".frob_extinguish() is being called in a new thread");
			thread callFunctionOn( "frob_extinguish", child );
		}
		else
	   	{
			if (child.hasFunction("LightsOff") )
			{
				// child is a light holder?
				thread callFunctionOn( "LightsOff", child);
			} else
			{
				//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): " + child.getName() + ".Off()");
				if ( child.isLight() ) child.Off();
				else if (isTarget) child.activate(self);
			}
		}
		// #3109: in case the spawnarg is set and the current skin is different,
		// then swap the skin, too. For lights, or light holder, the skin might
		// already have been swapped, so skip this step:
		if (unlit_skin)
		{
			if (cur_skin != unlit_skin)
			{
				//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): setting unlit skin");
				child.setSkin( unlit_skin );
				// store for later retrival by scripts
				child.setKey( "skin", unlit_skin );
			}
		}
	}
	// grayman #2603 - We have changed state, so entities that noticed us last time should notice us again
	// grayman #4392 - But only if we're a light.
	if ( child.isLight() )
	{
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " clearing ignore stim list and enabling visual stim");
		child.StimClearIgnoreList (STIM_VISUAL);
		child.StimEnable (STIM_VISUAL, 1);
	}
}

// Tels: to be used on f.i. candles, extinguishes the bound flame(s)
// and these in turn will set the unlit skin on us (the holder):
void tdm_light_holder::LightsOff()
{
	//sys.println(sys.getTime() + ": DEBUG (holder::LightsOff): " + getName());

	// nothing to do
	if (m_bExtinguished)
	{
		//sys.println(sys.getTime() + ": DEBUG (holder::LightsOff): " + getName() + " - already extinguished, nothing to do");
	   return;
	}

	// do this as first step to prevent endless recursion
	m_bExtinguished = true;

	//sys.println(sys.getTime() + ": DEBUG (holder::LightsOff): " + getName() + " calling toggle_children(OFF)");
	toggle_children( 0 );

	//sys.println(sys.getTime() + ": DEBUG (holder::LightsOff): " + getName() + " calling Off() on itself");
	// in case the light holder itself is a light?
	Off();
	//fadeOutLight(0.5);

	// if requested, toggle our shadows on (after the light is off!)
	if ( getIntKey("noshadows_lit") == 1 )
	{
		// delay turning on the shadow a bit to give the light a chance to fade out
		if ( getKey("lightType") == "AIUSE_LIGHTTYPE_TORCH" )
		{
			//sys.println(sys.getTime() + ": DEBUG: LightsOff - torch shadows ON on " + getName());
			noShadowsDelayed( false, SHADOW_SWITCH_DELAY_TORCH );
		}
		else
		{
			//sys.println(sys.getTime() + ": DEBUG: LightsOff - electric shadows ON on " + getName());
			noShadowsDelayed( false, SHADOW_SWITCH_DELAY_ELECTRIC );
		}
	}

	// only if the spawnarg is set
	string skin = getKey( "skin_unlit" );
	if (skin)
	{
		setSkin( skin );
		// store for later retrieval by scripts
		setKey( "skin", skin );
	}

	// grayman #2603 - for the case where the holder is a light
	// We have changed state, so entities that noticed us last time should notice us again
	StimClearIgnoreList (STIM_VISUAL);
	StimEnable (STIM_VISUAL, 1);
}

void tdm_light_holder::LightsOn()
{
	// sys.println(sys.getTime() + ": DEBUG (holder::LightsOn): " + getName());
	// nothing to do
	if (!m_bExtinguished)
	{
		//sys.println(sys.getTime() + ": DEBUG (holder::LightsOn): " + getName() + " - already lit, nothing to do");
		return;
	}

	m_bExtinguished = false;

	//sys.println(sys.getTime() + ": DEBUG (holder::LightsOn): " + getName() + " calling toggle_children(ON)");
	toggle_children( 1 );

	// if requested, toggle our shadows off before turning the light on
	// do so immediately, otherwise the light might cast briefly a shadow
	if (getIntKey("noshadows_lit") == 1)
	{
		noShadows(true);
	}

	// in case the light holder itself is a light?
	On();
	// TODO: make this configurable via spawnarg:
	// fadeInLight(0.5);

	// only if the spawnarg is set
	string skin = getKey( "skin_lit" );
	if (skin)
	{
		setSkin( skin );
		// store for later retrieval by scripts
		setKey( "skin", skin );
	}

	// grayman #2603 - for the case where the holder is a light
	// We have changed state, so entities that noticed us last time should notice us again
	StimClearIgnoreList (STIM_VISUAL);
	StimEnable (STIM_VISUAL, 1);
}

// Obsttorte #3760
void tdm_light_holder::LightsToggleResponse(entity e, entity f)
{
	LightsToggle();
}

// Tels: this one toggles bound flames on/off, depending on the
// state the flame is currently in.
void tdm_light_holder::LightsToggle()
{
	if (m_bExtinguished)
	{
		// turn on
		//sys.println(sys.getTime() + ": DEBUG (holder::LightsToggle): on " + getKey("name") + " calling LightsOn()");
		LightsOn();
	} else
	{
		// turn off
		//sys.println(sys.getTime() + ": DEBUG (holder::LightsToggle): off " + getKey("name") + " calling LightsOff()");
		LightsOff();
	}
}

#endif //__DARKMOD_LIGHT_HOLDERS__
tdm_light_holders.script (11,929 bytes)   
VanishedOne

VanishedOne

23.03.2020 00:05

developer   ~0012299

Added more isLight() checks to quieten console warnings (idLight script events were being called on light holders regardless of whether they were lights). Now I can set "extinguished" "1" on my atdm:lamp_wall_gasflame03_lit and not get warnings. I'm attaching the updated script, though as of now I haven't extensively trialled it.
tdm_light_holders-2.script (11,980 bytes)   
// vim:ts=4:sw=4:cindent
/***********************************************************************

A script object that can be used on any entity (movable, frobable etc.)
that acts as a holder for other light entities. Examples are candle
holders, oil lamps etc.

This script object just adds a few functions that allow to call script
events on the holder, like LightsOff(), LightsOn() and LightsToggle().

These events cause all bound lights to change state, and this in turn
will allow model and skin changes on the light holder.

Code copied from tdm_frobactions.script. Author: Tels

Note: the delay for noshadows is defined in scripts/tdm_lights.script

***********************************************************************/

#ifndef __DARKMOD_LIGHT_HOLDERS__
#define __DARKMOD_LIGHT_HOLDERS__

object tdm_light_holder
{
	boolean m_bExtinguished;
	// if someone toggles the light, store which entity this was, in case
	// it is also bound to us, to avoid toggling it again, resulting in endless
	// trigger loops
	entity activator;

	void init();
	void LightsOff();
	void LightsOn();
	void LightsToggle();
    void LightsToggleResponse(entity e, entity f); // Obsttorte #3760
    
	// internal routines:
	void toggle_children( float state );
	void toggle_child( entity child, float state, boolean isTarget );
};

void tdm_light_holder::init()
{
	entity child;
	float ind;

	//sys.println(sys.getTime() + ": DEBUG (holder::init): initializing light holder " + getName());
	// grayman #2603 - for the case where the holder is a light, fire initial visual stim
	StimClearIgnoreList (STIM_VISUAL);
	StimEnable (STIM_VISUAL, 1);
    
    // Obsttorte #3760
	ResponseAdd(STIM_TRIGGER);
	ResponseSetAction(STIM_TRIGGER,"LightsToggleResponse");
	ResponseEnable(STIM_TRIGGER,1);
    
	if (getKey("extinguished") == "0")
	{
    	//sys.println(sys.getTime() + ": DEBUG (holder::init): initializing light holder " + getName() + " on");
		m_bExtinguished = false;
	}
	else
	{
     	//sys.println(sys.getTime() + ": DEBUG (holder::init): initializing light holder '" + getName() + "' off");
		LightsOff();		// grayman #2823 - tell yourself and your children to go out

		// grayman #2905 - set self (in case we're a light) and light children that we were spawned off

    	//sys.println(sys.getTime() + ": DEBUG (holder::init): calling setStartedOff for self " + getName());
		if ( isLight() ) setStartedOff();
		for ( ind = 0 ; ind < numBindChildren( ) ; ind++ )
		{
			child = getBindChild( ind );
			if ( child.isLight() )
			{
    			//sys.println(sys.getTime() + ": DEBUG (holder::init): calling setStartedOff for child " + getName());
				child.setStartedOff();
			}
		}
	}

	if (getKey("noshadows_lit") == "1")
	{
		//sys.println(sys.getTime() + ": DEBUG: light holder setting shadows");
		noShadows( !m_bExtinguished );
	}
}

// Helper function for toggling any bound or targetted light objects (like flames)
// or other entities like movers:
void tdm_light_holder::toggle_children( float state )
{
	//sys.println(sys.getTime() + ": DEBUG (holder::toggle_children): state = " + state);
	entity child;
	float ind;

	for( ind = 0; ind < numBindChildren( ); ind++ )
	{
		child = getBindChild( ind );
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_children): toggling child " + child.getName());
		toggle_child( child, state, false );
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_children): setting _light_toggled key to 1 on child " + child.getName());
		child.setKey( "_light_toggled", "1");	// mark as done
	}
	// and now do the same for our targets, but ignore any we already did
	for( ind = 0; ind < numTargets( ); ind++ )
	{
		child = getTarget( ind );
		//sys.println(sys.getTime() + ": DEBUG toggle target: " + child.getName() + " state = " + state); 
		toggle_child( child, state, true );
	}
	for( ind = 0; ind < numBindChildren( ); ind++ )
	{
		child = getBindChild( ind );
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_children): setting _light_toggled key to 0 on child " + child.getName());
		child.setKey( "_light_toggled", 0);	// reset mark
	}
}

// Helper function for toggling one bound or targeted object (like flames or movers)
void tdm_light_holder::toggle_child( entity child, float state, boolean isTarget )
{
	if ( child == $null_entity )
	{
		return;
	}
	//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " starting ...");
	// #3048: avoid endless loops when this child is a mover bound to us, and linked back to us
	if (child.getIntKey("_light_toggled") == 1)	// marked as done?
	{
		//sys.println(sys.getTime() + ": DEBUG child " + child.getName() + " already toggled");
		return;
	}

	if (child.getIntKey("noactivate") != 0)	// or as noactivate?
	{
		//sys.println(sys.getTime() + ": DEBUG child has noactivate");
		return;
	}

	//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): " + child.getName() + " state = " + state); 
	string unlit_skin = child.getKey( "skin_unlit" );
	string lit_skin = child.getKey( "skin_lit" );
	string cur_skin = child.getKey( "skin" );
	//sys.println(sys.getTime() + ": DEBUG skin: " + cur_skin + " skin_unlit: " + unlit_skin + " lit_skin: " + lit_skin);

	if ( state > 0) // 1 = turn on 
	{
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " will be relit");

		// tels: call this in a sep. thread, otherwise it doesn't return until the 
		// ext. animation+particle have finished, which can be several seconds.
		// this poses problems when you ignite/extinguish more than one bound flame:
		if (child.hasFunction("frob_ignite") )
		{
			//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " using frob_ignite() in a new thread");
			thread callFunctionOn( "frob_ignite", child);
		}
		else
	   	{
			if (child.hasFunction("LightsOn") )
			{
				// child is a light holder?
				//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " using LightsOn() because the child is a light holder, in a new thread");
				thread callFunctionOn( "LightsOn", child);
			} else
			{
				//sys.println(sys.getTime() + ": DEBUG light on");
				if ( child.isLight() ) child.On();
				else if (isTarget) child.activate(self);
			}
		}

		// #3109: in case the spawnarg is set and the current skin is different,
		// the swap the skin, too. For lights, or light holder, the skin might
		// already have been swapped, so skip this step:
		if (lit_skin)
		   	{
			if (cur_skin != lit_skin)
				{
				//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): setting lit skin");
				child.setSkin( lit_skin );
				// store for later retrival by scripts
				child.setKey( "skin", lit_skin );
				}
			}
	}
	else // 0 = turn off
	{
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " will be extinguished");
		if (child.hasFunction("frob_extinguish") )
		{
			//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): " + child.getName() + ".frob_extinguish() is being called in a new thread");
			thread callFunctionOn( "frob_extinguish", child );
		}
		else
	   	{
			if (child.hasFunction("LightsOff") )
			{
				// child is a light holder?
				thread callFunctionOn( "LightsOff", child);
			} else
			{
				//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): " + child.getName() + ".Off()");
				if ( child.isLight() ) child.Off();
				else if (isTarget) child.activate(self);
			}
		}
		// #3109: in case the spawnarg is set and the current skin is different,
		// then swap the skin, too. For lights, or light holder, the skin might
		// already have been swapped, so skip this step:
		if (unlit_skin)
		{
			if (cur_skin != unlit_skin)
			{
				//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): setting unlit skin");
				child.setSkin( unlit_skin );
				// store for later retrival by scripts
				child.setKey( "skin", unlit_skin );
			}
		}
	}
	// grayman #2603 - We have changed state, so entities that noticed us last time should notice us again
	// grayman #4392 - But only if we're a light.
	if ( child.isLight() )
	{
		//sys.println(sys.getTime() + ": DEBUG (holder::toggle_child): child " + child.getName() + " clearing ignore stim list and enabling visual stim");
		child.StimClearIgnoreList (STIM_VISUAL);
		child.StimEnable (STIM_VISUAL, 1);
	}
}

// Tels: to be used on f.i. candles, extinguishes the bound flame(s)
// and these in turn will set the unlit skin on us (the holder):
void tdm_light_holder::LightsOff()
{
	//sys.println(sys.getTime() + ": DEBUG (holder::LightsOff): " + getName());

	// nothing to do
	if (m_bExtinguished)
	{
		//sys.println(sys.getTime() + ": DEBUG (holder::LightsOff): " + getName() + " - already extinguished, nothing to do");
	   return;
	}

	// do this as first step to prevent endless recursion
	m_bExtinguished = true;

	//sys.println(sys.getTime() + ": DEBUG (holder::LightsOff): " + getName() + " calling toggle_children(OFF)");
	toggle_children( 0 );

	//sys.println(sys.getTime() + ": DEBUG (holder::LightsOff): " + getName() + " calling Off() on itself");
	// in case the light holder itself is a light?
	if ( isLight() ) Off();
	//fadeOutLight(0.5);

	// if requested, toggle our shadows on (after the light is off!)
	if ( getIntKey("noshadows_lit") == 1 )
	{
		// delay turning on the shadow a bit to give the light a chance to fade out
		if ( getKey("lightType") == "AIUSE_LIGHTTYPE_TORCH" )
		{
			//sys.println(sys.getTime() + ": DEBUG: LightsOff - torch shadows ON on " + getName());
			noShadowsDelayed( false, SHADOW_SWITCH_DELAY_TORCH );
		}
		else
		{
			//sys.println(sys.getTime() + ": DEBUG: LightsOff - electric shadows ON on " + getName());
			noShadowsDelayed( false, SHADOW_SWITCH_DELAY_ELECTRIC );
		}
	}

	// only if the spawnarg is set
	string skin = getKey( "skin_unlit" );
	if (skin)
	{
		setSkin( skin );
		// store for later retrieval by scripts
		setKey( "skin", skin );
	}

	// grayman #2603 - for the case where the holder is a light
	// We have changed state, so entities that noticed us last time should notice us again
	StimClearIgnoreList (STIM_VISUAL);
	StimEnable (STIM_VISUAL, 1);
}

void tdm_light_holder::LightsOn()
{
	// sys.println(sys.getTime() + ": DEBUG (holder::LightsOn): " + getName());
	// nothing to do
	if (!m_bExtinguished)
	{
		//sys.println(sys.getTime() + ": DEBUG (holder::LightsOn): " + getName() + " - already lit, nothing to do");
		return;
	}

	m_bExtinguished = false;

	//sys.println(sys.getTime() + ": DEBUG (holder::LightsOn): " + getName() + " calling toggle_children(ON)");
	toggle_children( 1 );

	// if requested, toggle our shadows off before turning the light on
	// do so immediately, otherwise the light might cast briefly a shadow
	if (getIntKey("noshadows_lit") == 1)
	{
		noShadows(true);
	}

	// in case the light holder itself is a light?
	if ( isLight() ) On();
	// TODO: make this configurable via spawnarg:
	// fadeInLight(0.5);

	// only if the spawnarg is set
	string skin = getKey( "skin_lit" );
	if (skin)
	{
		setSkin( skin );
		// store for later retrieval by scripts
		setKey( "skin", skin );
	}

	// grayman #2603 - for the case where the holder is a light
	// We have changed state, so entities that noticed us last time should notice us again
	StimClearIgnoreList (STIM_VISUAL);
	StimEnable (STIM_VISUAL, 1);
}

// Obsttorte #3760
void tdm_light_holder::LightsToggleResponse(entity e, entity f)
{
	LightsToggle();
}

// Tels: this one toggles bound flames on/off, depending on the
// state the flame is currently in.
void tdm_light_holder::LightsToggle()
{
	if (m_bExtinguished)
	{
		// turn on
		//sys.println(sys.getTime() + ": DEBUG (holder::LightsToggle): on " + getKey("name") + " calling LightsOn()");
		LightsOn();
	} else
	{
		// turn off
		//sys.println(sys.getTime() + ": DEBUG (holder::LightsToggle): off " + getKey("name") + " calling LightsOff()");
		LightsOff();
	}
}

#endif //__DARKMOD_LIGHT_HOLDERS__
tdm_light_holders-2.script (11,980 bytes)   

Issue History

Date Modified Username Field Change
08.09.2019 15:37 Dragofer New Issue
08.09.2019 15:37 Dragofer Tag Attached: Fix
08.09.2019 15:37 Dragofer File Added: toggle_hide_show.map.txt
08.09.2019 18:06 VanishedOne Note Added: 0011837
08.09.2019 18:13 Dragofer Note Added: 0011838
08.09.2019 18:26 VanishedOne Note Edited: 0011837 View Revisions
08.09.2019 18:33 VanishedOne Note Added: 0011839
08.09.2019 19:08 VanishedOne Relationship added related to 0002564
08.09.2019 20:34 VanishedOne Note Added: 0011840
09.09.2019 14:51 VanishedOne File Added: tdm_light_holders.script
09.09.2019 14:51 VanishedOne Note Added: 0011841
08.03.2020 16:23 VanishedOne Note Edited: 0011840 View Revisions
23.03.2020 00:05 VanishedOne Note Added: 0012299
23.03.2020 00:05 VanishedOne File Added: tdm_light_holders-2.script