DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

SWF_Events.cpp (17289B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 #pragma hdrstop
     29 #include "../idlib/precompiled.h"
     30 
     31 /*
     32 ===================
     33 idSWF::HitTest
     34 ===================
     35 */
     36 idSWFScriptObject * idSWF::HitTest( idSWFSpriteInstance * spriteInstance, const swfRenderState_t & renderState, int x, int y, idSWFScriptObject * parentObject ) {
     37 
     38 	if ( spriteInstance->parent != NULL ) {
     39 		swfDisplayEntry_t * thisDisplayEntry = spriteInstance->parent->FindDisplayEntry( spriteInstance->depth );
     40 		if ( thisDisplayEntry->cxf.mul.w + thisDisplayEntry->cxf.add.w < 0.001f ) {
     41 			return NULL;
     42 		}
     43 	}
     44 
     45 	if ( !spriteInstance->isVisible ) {
     46 		return NULL;
     47 	}
     48 
     49 	if ( spriteInstance->scriptObject->HasValidProperty( "onRelease" ) 
     50 		|| spriteInstance->scriptObject->HasValidProperty( "onPress" ) 
     51 		|| spriteInstance->scriptObject->HasValidProperty( "onRollOver" )
     52 		|| spriteInstance->scriptObject->HasValidProperty( "onRollOut" )
     53 		|| spriteInstance->scriptObject->HasValidProperty( "onDrag" ) 
     54 		) {
     55 		parentObject = spriteInstance->scriptObject;
     56 	}
     57 
     58 	// rather than returning the first object we find, we actually want to return the last object we find
     59 	idSWFScriptObject * returnObject = NULL;
     60 	
     61 	float xOffset = spriteInstance->xOffset;
     62 	float yOffset = spriteInstance->yOffset;
     63 	
     64 	for ( int i = 0; i < spriteInstance->displayList.Num(); i++ ) {
     65 		const swfDisplayEntry_t & display = spriteInstance->displayList[i];
     66 		idSWFDictionaryEntry * entry = FindDictionaryEntry( display.characterID );
     67 		if ( entry == NULL ) {
     68 			continue;
     69 		}
     70 		swfRenderState_t renderState2;
     71 		renderState2.matrix = display.matrix.Multiply( renderState.matrix );
     72 		renderState2.ratio = display.ratio;
     73 
     74 		if ( entry->type == SWF_DICT_SPRITE ) {
     75 			idSWFScriptObject * object = HitTest( display.spriteInstance, renderState2, x, y, parentObject );
     76 			if ( object != NULL && object->Get( "_visible" ).ToBool() ) {
     77 				returnObject = object;
     78 			}
     79 		} else if ( entry->type == SWF_DICT_SHAPE && ( parentObject != NULL ) ) {
     80 			idSWFShape * shape = entry->shape;
     81 			for ( int i = 0; i < shape->fillDraws.Num(); i++ ) {
     82 				const idSWFShapeDrawFill & fill = shape->fillDraws[i];
     83 				for ( int j = 0; j < fill.indices.Num(); j+=3 ) {
     84 					idVec2 xy1 = renderState2.matrix.Transform( fill.startVerts[fill.indices[j+0]] );
     85 					idVec2 xy2 = renderState2.matrix.Transform( fill.startVerts[fill.indices[j+1]] );
     86 					idVec2 xy3 = renderState2.matrix.Transform( fill.startVerts[fill.indices[j+2]] );
     87 
     88 					idMat3 edgeEquations;
     89 					edgeEquations[0].Set( xy1.x + xOffset, xy1.y + yOffset, 1.0f );
     90 					edgeEquations[1].Set( xy2.x + xOffset, xy2.y + yOffset, 1.0f );
     91 					edgeEquations[2].Set( xy3.x + xOffset, xy3.y + yOffset, 1.0f );
     92 					edgeEquations.InverseSelf();
     93 
     94 					idVec3 p( x, y, 1.0f );
     95 					idVec3 signs = p * edgeEquations;
     96 
     97 					bool bx = signs.x > 0;
     98 					bool by = signs.y > 0;
     99 					bool bz = signs.z > 0;
    100 					if ( bx == by && bx == bz ) {
    101 						// point inside
    102 						returnObject = parentObject;
    103 					}
    104 				}
    105 			}
    106 		} else if ( entry->type == SWF_DICT_MORPH ) {
    107 			// FIXME: this should be roughly the same as SWF_DICT_SHAPE
    108 		} else if ( entry->type == SWF_DICT_TEXT ) {
    109 			// FIXME: this should be roughly the same as SWF_DICT_SHAPE
    110 		} else if ( entry->type == SWF_DICT_EDITTEXT ) {
    111 			idSWFScriptObject * editObject = NULL;
    112 
    113 			if ( display.textInstance->scriptObject.HasProperty( "onRelease" ) || display.textInstance->scriptObject.HasProperty( "onPress" ) ) {
    114 				// if the edit box itself can be clicked, then we want to return it when it's clicked on
    115 				editObject = &display.textInstance->scriptObject;
    116 			} else if ( parentObject != NULL ) {
    117 				// otherwise, we want to return the parent object
    118 				editObject = parentObject;
    119 			}
    120 
    121 			if ( editObject == NULL ) {
    122 				continue;
    123 			}
    124 
    125 			if ( display.textInstance->text.IsEmpty() ) {
    126 				continue;
    127 			}
    128 
    129 			const idSWFEditText * shape = entry->edittext;
    130 			const idSWFEditText * text = display.textInstance->GetEditText();
    131 			float textLength = display.textInstance->GetTextLength();
    132 
    133 			float lengthDiff = fabs( shape->bounds.br.x - shape->bounds.tl.x ) - textLength;
    134 			
    135 			idVec3 tl; 
    136 			idVec3 tr; 
    137 			idVec3 br; 
    138 			idVec3 bl;
    139 
    140 			float xOffset = spriteInstance->xOffset;
    141 			float yOffset = spriteInstance->yOffset;
    142 
    143 			float topOffset = 0.0f;
    144 
    145 			if ( text->align == SWF_ET_ALIGN_LEFT ) {
    146 				tl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x  + xOffset, shape->bounds.tl.y + topOffset + yOffset ) ); 
    147 				tr.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x - lengthDiff + xOffset, shape->bounds.tl.y + topOffset + yOffset ) ); 
    148 				br.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x - lengthDiff + xOffset, shape->bounds.br.y + topOffset + yOffset ) ); 
    149 				bl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );									
    150 			} else if ( text->align == SWF_ET_ALIGN_RIGHT ) {
    151 				tl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + lengthDiff + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
    152 				tr.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
    153 				br.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
    154 				bl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + lengthDiff + xOffset, shape->bounds.br.y + topOffset + yOffset ) );				
    155 			} else if ( text->align == SWF_ET_ALIGN_CENTER ) {
    156 				float middle = ( ( shape->bounds.br.x + xOffset ) + ( shape->bounds.tl.x + xOffset ) ) / 2.0f;
    157 				tl.ToVec2() = renderState2.matrix.Transform( idVec2( middle - ( textLength / 2.0f ), shape->bounds.tl.y + topOffset + yOffset ) );
    158 				tr.ToVec2() = renderState2.matrix.Transform( idVec2( middle + ( textLength / 2.0f ), shape->bounds.tl.y + topOffset + yOffset ) );
    159 				br.ToVec2() = renderState2.matrix.Transform( idVec2( middle + ( textLength / 2.0f ), shape->bounds.br.y + topOffset + yOffset ) );
    160 				bl.ToVec2() = renderState2.matrix.Transform( idVec2( middle - ( textLength / 2.0f ), shape->bounds.br.y + topOffset + yOffset ) );				
    161 			} else {
    162 				tl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
    163 				tr.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
    164 				br.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
    165 				bl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
    166 			}
    167 
    168 			tl.z = 1.0f;
    169 			tr.z = 1.0f;
    170 			br.z = 1.0f;
    171 			bl.z = 1.0f;
    172 
    173 			idMat3 edgeEquations;
    174 			edgeEquations[0] = tl;
    175 			edgeEquations[1] = tr;
    176 			edgeEquations[2] = br;
    177 			edgeEquations.InverseSelf();
    178 
    179 			idVec3 p( x, y, 1.0f );
    180 			idVec3 signs = p * edgeEquations;
    181 
    182 			bool bx = signs.x > 0;
    183 			bool by = signs.y > 0;
    184 			bool bz = signs.z > 0;
    185 			if ( bx == by && bx == bz ) {
    186 				// point inside top right triangle
    187 				returnObject = editObject;
    188 			}
    189 
    190 			edgeEquations[0] = tl;
    191 			edgeEquations[1] = br;
    192 			edgeEquations[2] = bl;
    193 			edgeEquations.InverseSelf();
    194 			signs = p * edgeEquations;
    195 
    196 			bx = signs.x > 0;
    197 			by = signs.y > 0;
    198 			bz = signs.z > 0;
    199 			if ( bx == by && bx == bz ) {
    200 				// point inside bottom left triangle
    201 				returnObject = editObject;
    202 			}
    203 		}
    204 	}
    205 	return returnObject;
    206 }
    207 
    208 /*
    209 ===================
    210 idSWF::HandleEvent
    211 ===================
    212 */
    213 bool idSWF::HandleEvent( const sysEvent_t * event ) {
    214 	if ( !IsLoaded() || !IsActive() || ( !inhibitControl && useInhibtControl ) ) {
    215 		return false;
    216 	}
    217 	if ( event->evType == SE_KEY ) {
    218 		if ( event->evValue == K_MOUSE1 ) {
    219 			mouseEnabled = true;
    220 			idSWFScriptVar var;
    221 			if ( event->evValue2 ) {
    222 
    223 				idSWFScriptVar waitInput = globals->Get( "waitInput" );
    224 				if ( waitInput.IsFunction() ) { 
    225 					useMouse = false;
    226 					idSWFParmList waitParms;
    227 					waitParms.Append( event->evValue );
    228 					waitInput.GetFunction()->Call( NULL, waitParms );
    229 					waitParms.Clear();
    230 				} else {
    231 					useMouse = true;
    232 				}
    233 
    234 				idSWFScriptObject * hitObject = HitTest( mainspriteInstance, swfRenderState_t(), mouseX, mouseY, NULL );
    235 				if ( hitObject != NULL ) {
    236 					mouseObject = hitObject;
    237 					mouseObject->AddRef();
    238 					
    239 					var = hitObject->Get( "onPress" );
    240 					if ( var.IsFunction() ) {
    241 						idSWFParmList parms;
    242 						parms.Append( event->inputDevice );
    243 						var.GetFunction()->Call( hitObject, parms );
    244 						parms.Clear();
    245 						return true;
    246 					}
    247 
    248 					idSWFScriptVar var = hitObject->Get( "onDrag" );
    249 					if ( var.IsFunction() ) {
    250 						idSWFParmList parms;
    251 						parms.Append( mouseX );
    252 						parms.Append( mouseY );
    253 						parms.Append( true );
    254 						var.GetFunction()->Call( hitObject, parms );
    255 						parms.Clear();
    256 						return true;
    257 					}
    258 				}
    259 
    260 				idSWFParmList parms;
    261 				parms.Append( hitObject );
    262 				Invoke( "setHitObject", parms );
    263 
    264 			} else {
    265 				if ( mouseObject ) {
    266 					var = mouseObject->Get( "onRelease" );
    267 					if ( var.IsFunction() ) {
    268 						idSWFParmList parms;
    269 						parms.Append( mouseObject ); // FIXME: Remove this
    270 						var.GetFunction()->Call( mouseObject, parms );
    271 					}					
    272 					mouseObject->Release();
    273 					mouseObject = NULL;
    274 				}
    275 				if ( hoverObject ) {
    276 					hoverObject->Release();
    277 					hoverObject = NULL;
    278 				}
    279 
    280 				if ( var.IsFunction() ) {
    281 					return true;
    282 				}
    283 			}
    284 			
    285 			return false;
    286 		}
    287 		const char * keyName = idKeyInput::KeyNumToString( (keyNum_t)event->evValue );
    288 		idSWFScriptVar var = shortcutKeys->Get( keyName );
    289 		// anything more than 32 levels of indirection we can be pretty sure is an infinite loop
    290 		for ( int runaway = 0; runaway < 32; runaway++ ) {
    291 			idSWFParmList eventParms;
    292 			eventParms.Clear();
    293 			eventParms.Append( event->inputDevice );
    294 			if ( var.IsString() ) {
    295 				// alias to another key
    296 				var = shortcutKeys->Get( var.ToString() );
    297 				continue;
    298 			} else if ( var.IsObject() ) {
    299 				// if this object is a sprite, send fake mouse events to it
    300 				idSWFScriptObject * object = var.GetObject();
    301 				// make sure we don't send an onRelease event unless we have already sent that object an onPress
    302 				bool wasPressed = object->Get( "_pressed" ).ToBool();
    303 				object->Set( "_pressed", event->evValue2 );
    304 				if ( event->evValue2 ) {
    305 					var = object->Get( "onPress" );
    306 				} else if ( wasPressed ) {
    307 					var = object->Get( "onRelease" );
    308 				}
    309 				if ( var.IsFunction() ) {
    310 					var.GetFunction()->Call( object, eventParms );
    311 					return true;
    312 				}
    313 			} else if ( var.IsFunction() ) {
    314 				if ( event->evValue2 ) {
    315 					// anonymous functions only respond to key down events
    316 					var.GetFunction()->Call( NULL, eventParms );
    317 					return true;
    318 				}
    319 				return false;
    320 			}
    321 
    322 			idSWFScriptVar useFunction = globals->Get( "useFunction" );
    323 			if ( useFunction.IsFunction() && event->evValue2 ) {
    324 				const char * action = idKeyInput::GetBinding( event->evValue );
    325 				if ( idStr::Cmp( "_use", action ) == 0 ) {
    326 					useFunction.GetFunction()->Call( NULL, idSWFParmList() );
    327 				}				
    328 			}
    329 
    330 			idSWFScriptVar waitInput = globals->Get( "waitInput" );
    331 			if ( waitInput.IsFunction() ) { 
    332 				useMouse = false;
    333 				if ( event->evValue2 ) {
    334 					idSWFParmList waitParms;
    335 					waitParms.Append( event->evValue );
    336 					waitInput.GetFunction()->Call( NULL, waitParms );
    337 				}
    338 			} else {
    339 				useMouse = true;
    340 			}
    341 
    342 			idSWFScriptVar focusWindow = globals->Get( "focusWindow" );
    343 			if ( focusWindow.IsObject() ) {
    344 				idSWFScriptVar onKey = focusWindow.GetObject()->Get( "onKey" );
    345 				if ( onKey.IsFunction() ) {
    346 
    347 					// make sure we don't send an onRelease event unless we have already sent that object an onPress
    348 					idSWFScriptObject * object = focusWindow.GetObject();
    349 					bool wasPressed = object->Get( "_kpressed" ).ToBool();
    350 					object->Set( "_kpressed", event->evValue2 );
    351 					if ( event->evValue2 || wasPressed ) {
    352 						idSWFParmList parms;
    353 						parms.Append( event->evValue );
    354 						parms.Append( event->evValue2 );
    355 						onKey.GetFunction()->Call( focusWindow.GetObject(), parms ).ToBool();
    356 						return true;
    357 					} else if ( event->evValue == K_LSHIFT || event->evValue == K_RSHIFT ) {
    358 						idSWFParmList parms;
    359 						parms.Append( event->evValue );
    360 						parms.Append( event->evValue2 );
    361 						onKey.GetFunction()->Call( focusWindow.GetObject(), parms ).ToBool();
    362 					}
    363 				}
    364 			}
    365 			return false;
    366 		}
    367 		idLib::Warning( "Circular reference in %s shortcutKeys.%s", filename.c_str(), keyName );
    368 	} else if ( event->evType == SE_CHAR ) {
    369 		idSWFScriptVar focusWindow = globals->Get( "focusWindow" );
    370 		if ( focusWindow.IsObject() ) {
    371 			idSWFScriptVar onChar = focusWindow.GetObject()->Get( "onChar" );
    372 			if ( onChar.IsFunction() ) {
    373 				idSWFParmList parms;
    374 				parms.Append( event->evValue );
    375 				parms.Append( idKeyInput::KeyNumToString( (keyNum_t)event->evValue ) );
    376 				onChar.GetFunction()->Call( focusWindow.GetObject(), parms ).ToBool();
    377 				return true;
    378 			}
    379 		}
    380 	} else if ( event->evType == SE_MOUSE_ABSOLUTE || event->evType == SE_MOUSE ) {
    381 		mouseEnabled = true;
    382 		isMouseInClientArea = true;
    383 
    384 		// Mouse position in screen space needs to be converted to SWF space
    385 		if ( event->evType == SE_MOUSE_ABSOLUTE ) {
    386 			const float pixelAspect = renderSystem->GetPixelAspect();
    387 			const float sysWidth = renderSystem->GetWidth() * ( pixelAspect > 1.0f ? pixelAspect : 1.0f );
    388 			const float sysHeight = renderSystem->GetHeight() / ( pixelAspect < 1.0f ? pixelAspect : 1.0f );
    389 			float scale = swfScale * sysHeight / (float)frameHeight;
    390 			float invScale = 1.0f / scale;
    391 			float tx = 0.5f * ( sysWidth - ( frameWidth * scale ) );
    392 			float ty = 0.5f * ( sysHeight - ( frameHeight * scale ) );
    393 
    394 			mouseX = idMath::Ftoi( ( static_cast<float>( event->evValue ) - tx ) * invScale );
    395 			mouseY = idMath::Ftoi( ( static_cast<float>( event->evValue2 ) - ty ) * invScale );
    396 		} else {
    397 
    398 			mouseX += event->evValue;
    399 			mouseY += event->evValue2;
    400 
    401 			mouseX = Max( Min( mouseX, idMath::Ftoi( frameWidth + renderBorder ) ), idMath::Ftoi( 0.0f - renderBorder ) );
    402 			mouseY = Max( Min( mouseY, idMath::Ftoi(frameHeight) ), 0 );
    403 		}
    404 
    405 		bool retVal = false;
    406 
    407 		idSWFScriptObject * hitObject = HitTest( mainspriteInstance, swfRenderState_t(), mouseX, mouseY, NULL );
    408 		if ( hitObject != NULL ) {
    409 			hasHitObject = true;
    410 		} else {
    411 			hasHitObject = false;
    412 		}
    413 
    414 		if ( hitObject != hoverObject ) {
    415 			// First check to see if we should call onRollOut on our previous hoverObject
    416 			if ( hoverObject != NULL ) {
    417 				idSWFScriptVar var = hoverObject->Get( "onRollOut" );
    418 				if ( var.IsFunction() ) {
    419 					var.GetFunction()->Call( hoverObject, idSWFParmList() );
    420 					retVal = true;
    421 				}
    422 				hoverObject->Release();
    423 				hoverObject = NULL;
    424 			}
    425 			// Then call onRollOver on our hitObject
    426 			if ( hitObject != NULL ) {
    427 				hoverObject = hitObject;
    428 				hoverObject->AddRef();
    429 				idSWFScriptVar var = hitObject->Get( "onRollOver" );
    430 				if ( var.IsFunction() ) {
    431 					var.GetFunction()->Call( hitObject, idSWFParmList() );
    432 					retVal = true;
    433 				}
    434 			}
    435 		}
    436 		if ( mouseObject != NULL ) {
    437 			idSWFScriptVar var = mouseObject->Get( "onDrag" );
    438 			if ( var.IsFunction() ) {
    439 				idSWFParmList parms;
    440 				parms.Append( mouseX );
    441 				parms.Append( mouseY );
    442 				parms.Append( false );
    443 				var.GetFunction()->Call( mouseObject, parms );
    444 				return true;
    445 			}
    446 		}
    447 		return retVal;
    448 	} else if ( event->evType == SE_MOUSE_LEAVE ) {
    449 		isMouseInClientArea = false;
    450 	} else if ( event->evType == SE_JOYSTICK ) {
    451 		idSWFParmList parms;
    452 		parms.Append( event->evValue );
    453 		parms.Append( event->evValue2 / 32.0f );
    454 		Invoke( "onJoystick", parms );
    455 	}
    456 	return false;
    457 }