Minor things worth mentioning

A refuge for those migrating from the fallen DXEditing.com and a place for general discussion relating to Deus Ex editing (coding, mapping, etc).
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

If you find it annoying that players have no real Tick() function (well they have, but it's just called for GEntry player) or that PlayerTick() just seems to be clientside, MultiplayerTick() is hooked into ServerMove, etc.

Code: Select all

UBOOL AHXPlayerPawn::Tick( FLOAT DeltaTime, enum ELevelTick TickType )
{
	UBOOL RetVal = Super::Tick( DeltaTime, TickType );

	if ( XLevel )
	{
		UGameEngine* GameEngine = Cast<UGameEngine>( XLevel->Engine );

		if ( GameEngine && GameEngine->GEntry != XLevel )
		{
			eventTickStuff( DeltaTime );
		}
	}

	return RetVal;
}
+ Add the eventTickStuff() callback.

*NOW* it's time to clean this fucking playerpawn code up.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

Extended Spawn() function which exposes the bNoCollisionFail variable to unrealscript. So you can spawn an actor regardless of collision problems (and without setting the bCollideWorld=False in your replacment classes or reducing collision size). This is nice when you want to replace some really bad placed actors like the RepairBot on the boat in HK, etc. I guess this sorts out my last replacement failed to spawn problems.

Code: Select all

//
// Spawn an actor. Returns an actor of the specified class, not
// of class Actor (this is hardcoded in the compiler). Returns None
// if the actor could not be spawned (either the actor wouldn't fit in
// the specified location, or the actor list is full).
// Defaults to spawning at the spawner's location.
//
// HX_NOTE:
// Exposed ULevel::SpawnActor() bNoCollisionFail to uc, but the return
// value is now Actor, and needs to be casted.
//
native(3219) final function actor SpawnExt
(
	class<actor>      SpawnClass,
	optional actor	  SpawnOwner,
	optional name     SpawnTag,
	optional vector   SpawnLocation,
	optional rotator  SpawnRotation,
	optional bool			SpawnNoCollisionFail
);

Code: Select all

void AHXMutator::execSpawnExt( FFrame& Stack, void*const Result )
{
	P_GET_OBJECT( UClass, SpawnClass );
	P_GET_ACTOR_OPTX( SpawnOwner, NULL );
	P_GET_NAME_OPTX( SpawnTag, NAME_None );
	P_GET_VECTOR_OPTX( SpawnLocation, Location );
	P_GET_ROTATOR_OPTX( SpawnRotation, Rotation );
	P_GET_UBOOL_OPTX( SpawnNoCollisionFail, 0 );
	P_FINISH;

	if ( SpawnClass )
	{
		AActor* A = GetLevel()->SpawnActor( SpawnClass, NAME_None, SpawnOwner, Instigator, SpawnLocation, SpawnRotation, NULL, SpawnNoCollisionFail, 0 );
	
		if ( A )
		{
			A->Tag = SpawnTag;
			
			*(AActor **)Result = A;
		}
		else
		{
			*(AActor **)Result = NULL;
		}
	}
	else
	{
		*(AActor **)Result = NULL;
	}
}
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Cybernetic pig
Illuminati
Posts: 2284
Joined: Thu Mar 08, 2012 3:21 am

Re: Minor things worth mentioning

Post by Cybernetic pig »

I guess this sorts out my last replacement failed to spawn problems.
I don't suppose by spawn "problems" you also mean the "you can not drop this here" message for inventory items?
It would be nice if this was not so restrictive and subsequently annoying. I considered looking into it a while back but it is likely restrictive for a reason, engine related, but you just may have found a work around perhaps.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

Cybernetic pig wrote:I don't suppose by spawn "problems" you also mean the "you can not drop this here" message for inventory items?
No, just when spawning actors.
I considered looking into it a while back but it is likely restrictive for a reason, engine related
Actually it doesn't seem engine related.

DeusExPlayer.DropItem()

Code: Select all

		GetAxes(Rotation, X, Y, Z);
		dropVect = Location + (CollisionRadius + 2*item.CollisionRadius) * X;
		dropVect.Z += BaseEyeHeight;

		// check to see if we're blocked by terrain
		if (!FastTrace(dropVect))
		{
			ClientMessage(CannotDropHere);
			return False;
		}
So this check is basically a FastTrace(). However you are free to try other Drop locations.
The interessting bit is actually that the item is probably spawned at a different location, not the location which is used as a start for trace, which is a bit weired, so changing the above check to match the actual drop location should be worth a try.

Code: Select all

			GetAxes(ViewRotation, X, Y, Z);
			dropVect = Location + 0.8 * CollisionRadius * X;
			dropVect.Z += BaseEyeHeight;
/edit
Also the above check doesn't match the drop location if you have an other item highlighted. So a slight rearrangement of the code and *MATCHING* checks for the droplocations should be useful.

/edit2
Also weapon mods should only be turned of if you actually drop the weapon.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

As HX takes quite some time to compile and i just got new hardware, the question arose how to benchmark (unrealscript) compile times. As I'm using a makefile for MSVC6 integration i came up with this solution:
Grab timer.

Makefile:

Code: Select all

# HXCoop.u NMAKE Makefile

HX_U = "HX.u"
HX_UCC = "ucc make INI=HX.ini USERINI=HXUser.ini"

$(HX_U):
	cd ../../System
	-timer /nologo
	-del $(HX_U)
	$(HX_UCC)
	-hexed -e 8 00 $(HX_U)
	-timer /s /nologo
Output:
--------------------Konfiguration: HX_uc - Win32 Release--------------------
Microsoft (R) Program Maintenance-Dienstprogramm: Version 6.00.9782.0
Copyright (C) Microsoft Corp 1988-1998. Alle Rechte vorbehalten.
cd ../../System
timer /nologo
Timer started: 14.1.2015 19:53:56
del "HX.u"
"ucc make INI=HX.ini USERINI=HXUser.ini"
--------------------Core--------------------
--------------------Engine--------------------
--------------------Editor--------------------
--------------------Fire--------------------
--------------------IpDrv--------------------
--------------------UWindow--------------------
--------------------UBrowser--------------------
--------------------Extension--------------------
--------------------DeusExUI--------------------
--------------------ConSys--------------------
--------------------DeusExConversations--------------------
--------------------DeusExSounds--------------------
--------------------DeusExItems--------------------
--------------------DeusExDeco--------------------
--------------------DeusExCharacters--------------------
--------------------DeusExConText--------------------
--------------------DeusExConAudioAIBarks--------------------
--------------------MPCharacters--------------------
--------------------DeusExText--------------------
--------------------IpServer--------------------
--------------------DeusEx--------------------
--------------------HX--------------------
Analyzing...
Parsing HXPlayerPawn
[..]
Parsing HXGoal
Compiling HXPlayerPawn
[..]
Compiling HXGoal
Success - 0 error(s), 0 warnings
hexed -e 8 00 HX.u
timer /s /nologo
98.2 seconds

HX.u - 0 Fehler, 0 Warnung(en)
/edit:
Now with new hardware (or more like new used hardware) and a custom ucc.exe which increases thread/process priority i cut the compile time in half. However, as increasing priority seemed to increase compile time by 10-20s, here is the src/build of my custom ucc.exe: http://coding.hanfling.de/UCC_hp.zip
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Adding UObject classes to the Launch.exe.

Post by Hanfling »

As i wanted to replace USystem with my own ULaunchSystem to have some nice UProperty backed up settings and shit for the launcher, without the penalty of an extra package, i needed to figure out how to make sure that they can get loaded. Now i can edit my LaunchSystem in the preferences window, etc.

Replacement for IMPLEMENT_CLASS() macro.

Code: Select all

/*----------------------------------------------------------------------------
	UObjects in Launch support code.
----------------------------------------------------------------------------*/

extern "C" DLL_EXPORT TCHAR GInternalPackage[];

#define IMPLEMENT_INTERNAL_CLASS(TClass) \
	UClass TClass::PrivateStaticClass \
	( \
		EC_NativeConstructor, \
		sizeof(TClass), \
		TClass::StaticClassFlags, \
		TClass::Super::StaticClass(), \
		TClass::WithinClass::StaticClass(), \
		FGuid(TClass::GUID1,TClass::GUID2,TClass::GUID3,TClass::GUID4), \
		TEXT(#TClass)+1, \
		GInternalPackage, \
		StaticConfigName(), \
		RF_Public | RF_Standalone | RF_Transient | RF_Native, \
		(void(*)(void*))TClass::InternalConstructor, \
		(void(UObject::*)())TClass::StaticConstructor \
	); \
	extern "C" DLL_EXPORT UClass* autoclass##TClass;\
	DLL_EXPORT UClass* autoclass##TClass = TClass::StaticClass();

Code: Select all

/*-----------------------------------------------------------------------------
	Global variables.
-----------------------------------------------------------------------------*/

extern "C" {TCHAR GInternalPackage[64]=TEXT("LaunchInternal");}
Right after appInit()

Code: Select all

		// Hook in Launch package.
		UPackage* LaunchPkg = UObject::CreatePackage( NULL, GInternalPackage );
		LaunchPkg->DllHandle = hInstance;

		// Hook in LaunchSystem.
		GSys->RemoveFromRoot();
		delete GSys;
		GSys = new ULaunchSystem;
		GSys->AddToRoot();
		for( INT i=0; i<GSys->Suppress.Num(); i++ )
			GSys->Suppress(i).SetFlags( RF_Suppress );
		GSys->PostLoad();
*.int File:

Code: Select all

[General]
Start=Deus Ex (Starting)
Exit=Deus Ex (Exiting)
Run=Deus Ex (Running)
Product=Deus Ex

[Public]
Preferences=(Caption="Advanced",Parent="Advanced Options")
Preferences=(Caption="Launch File System",Parent="Advanced",Class=LaunchInternal.LaunchSystem,Immediate=True)
Note: This still works after you rename the exe file!

/edit:
If you want an exe specific package name just copy appPackage() over to GInternalPackage and add a prefix or make sure you dont have a package with that name. Or does it work even with having such a package? Worth to investigate
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

GetPropertyText() / SetPropertyText() Will not warn you if the property is either const or private and just silently fail. Hard to figure that out, but i had not much look replacing these with my own development versions which would fail. As they have no hardcoded native id, i can't just set an index in the GNatives tables, but a search over all fields failed, but that might be some caused by some frampointer / import redirection stuff. But probably they do get loaded every time into the exact same slot as they are definined in UObject.

This took quite some time to figure out working on my UnrealScript version of RootWindow.ResizeRoot(). But it's rather cool to an UnrealScript event handle the resizing / selections of of overrall drawscale for windows. Still need to expose Pre/PostRenderWindows, and PreRender() has quite some heavy canvas setup it, PostRender() is not that interessting at all, but i should go ahead and add some native functions to RF playern to Push/Pop the Canvas->Frame. Also ClampMousePosition() at the end of ResizeRoot() still needs to be handled by C++ code. I hope i can get as much as possible out of this to unrealscript.

Anyone interessted in figuring out how that all can be used to get some non integer drawing scale with correct render/mouse clipping? It should be possible with this all exposed.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

Hardcoded

Code: Select all

SET InputExt
Should be replaced with

Code: Select all

SET ini:Engine.Engine.Input
They did the same for AudioDevices but not for Input. But this way it will still work with old InputExt, but uc code will not break when using another Input= option in ini file. ^^
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

Mh yeah... "What happens when i set GIsEditor in Launcher..."

Some trial and error.. .dafuuuuuuuuuqq...
http://coding.hanfling.de/Launch__.png
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

Finally put out a release of my new rewitten launcher:
http://coding.hanfling.de/launch/releas ... 150425.zip
All kind of weired stuff like a distinct ini file set per executable. e.g. name it DwardMod.exe, so it will use DwardMod.ini/DwardModUser.ini, it will use (if found) DwarfModDefault.ini/DwarfModDefUser.ini. Has an audio device selection and resolution selection dialogue build in, disables DEP, etc. etc, BUT it does not have any dll hooking hacks in to solve stuff which is outside the scope of a launcher. But it's appeal is rather much still the one of the old unreal engine 1 launchers. Some more useful documentation follows at some point as builds for other games.

Oh and it has an UCC replacement in, called LCC.exe, rename it as you like, but copy the LCC.ini too, and select the ini set your game uses by setting the Game= option.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

Today we got some crash for UED2 when saving a map. Björn ran windgb and it yielded a line like:

Code: Select all

(2fb0.1820): Stack overflow - code c00000fd (first chance)
As one is actually able to increase the stack size of an exectuble post build, we ran:

Code: Select all

editbin UnrealEd.exe /STACK:0x1000000
To increase the stack size to 16 Mb and it did save again.

However at the same time we also added the largeaddressaware flag, so it can use up to 3GB instead of 2GB on a 64bit machine ("can't hurt"):

Code: Select all

editbin /LARGEADDRESSAWARE UnrealEd.exe
Setting these for Ued1 makes sense too.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

Seems like a hit a compiler bug.

Code: Select all

static function InitSkillPoints( HXGameInfo Game, HXSteve Steve )
{
	// 6575 are DeusEx starting points.
	Steve.SkillPointsTotal = Max( Steve.SkillPointsTotal, (1.0-Game.SkillPointPenalty)*6575.0+Game.StartingSkillPointsBonus );

	//Log( "Steve.SkillPointsTotal = " $ Steve.SkillPointsTotal, 'MissionScript' );
}
Steve.SkillPointsTotal is int, Game.StartingSkillPointsBonus is int, Game.SkillPointPenalty is a float in the range [0,1]. Max ist int Max( int, int ).
However if SkillPointsTotal=0, SkillPointPenalty>0.0 (and StartingSkillPointsBonus=0), SkillPointsTotal end up beeing 0. Looks like (1.0-Game.SkillPointPenalty) gets truncated to an int before it gets multiplied with 6575.0, although it should actually be not. Bad thing about it is that it probably relates to every calculation done inside parameter list for an int parameter of a function. Maybe even for Int() casts too. This really... sucks... and give me quite a bad feeling about dozens of probably spot of errors of this kind.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

Started adding a bit of DX2 support to UE Explorer / UELib:
https://github.com/hanfling/Unreal-Library
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

There is also a command for importing single text files into a text package to complement the ALLDEUSEXTEXT command.

Code: Select all

#EXEC DEUSEXTEXT IMPORT FILE=<FileName>
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Minor things worth mentioning

Post by Hanfling »

If someone needs it. HXMenuScreenCustomizeKeys. Basically removed the HX prefix for conveniance here. Basically it features three changes compared to DeusEx 1112fm.
- Support for Mouse4/Mouse5 in terms of Unreal Tournament 2004 Keymapping. (I'm working on an WinDrvExt with RawMouse support now, which also adds these buttons, so it's nice to have them useable in the customize keys screen)
- Uses ini:Engine.Engine.Input setting instead of hardcoded Extension.InputExt setting, to make it more flexible (and account for the actually setting in the ini).
- Removes the annoying number of defprops defined needs to be exactly the ArrayCount() on KeyDisplayNames/FunctionText and related. So one can just add properties if needed without any need to adjust the arrays.

You might need to adjust the DeusEx.int file:

Code: Select all

 KeyDisplayNames[71]="(InputKey=IK_UnknownC1,displayName="Mouse Button 4")"
 KeyDisplayNames[72]="(InputKey=IK_UnknownC2,displayName="Mouse Button 5")"
Testing/Feedback/Suggestions are appreciated. Further support for Gamepad/etc. stuff will be added at a later point.

Code: Select all

//=============================================================================
// MenuScreenCustomizeKeys.
//
// Notes:
//  * WinDrvExt uses Unreal Tournament 2004 Key/Button/Axis mapping.
//		- IK_UnknownC1 ~ IK_Mouse4,
//		- IK_UnknownC2 ~ IK_Mouse5
//
// Todo:
//  * Evaluate IK_Mouse4/IK_Mouse5 behaviour in RawMouseButtonPressed().
//=============================================================================
class MenuScreenCustomizeKeys expands MenuUIScreenWindow;

var MenuUIListHeaderButtonWindow BtnHeaderAction;
var MenuUIListHeaderButtonWindow BtnHeaderAssigned;
var MenuUIListWindow						 LstKeys;
var MenuUIScrollAreaWindow			 WinScroll;

struct S_KeyDisplayItem
{
	var EInputKey 			 InputKey;
	var localized String DisplayName;
};

var localized S_KeyDisplayItem KeyDisplayNames[128];
var localized string					 FunctionText[96];
var string										 MenuValues1[96];
var string										 MenuValues2[96];
var string										 AliasNames[96];
var string										 PendingCommands[100];

var int  Pending;
var int  Selection; 	
var bool bWaitingForInput;

var localized string NoneText;
var localized string StrHeaderActionLabel;
var localized string StrHeaderAssignedLabel;
var localized string WaitingHelpText;
var localized string InputHelpText;
var localized string ReassignedFromLabel;

// ----------------------------------------------------------------------
// InitWindow()
//
// Initialize the Window
// ----------------------------------------------------------------------

event InitWindow()
{
	Super.InitWindow();

	Pending = 0;
	Selection = -1;
	bWaitingForInput = False;
	BuildKeyBindings();

	CreateKeyList();
	CreateHeaderButtons();
	PopulateKeyList();
	ShowHelp( WaitingHelpText );
}

// ----------------------------------------------------------------------
// VirtualKeyPressed() 
// ----------------------------------------------------------------------

event bool VirtualKeyPressed( EInputKey Key, bool bRepeat )
{
	if ( !bWaitingForInput ) 
	{
		// If the user presses [Delete] or [Backspace], then clear this setting.
		switch ( Key )
		{
			case IK_Delete:
			case IK_Backspace:
				ClearFunction();
				return True;
				break;

			default:
				return Super.VirtualKeyPressed( Key, bRepeat );
				break;
		}
	}

	// First check to see if we're waiting for the user to select a Keyboard or mouse/joystick Button to override. 
	WaitingForInput( False );
                                                                                
	ProcessKeySelection( Key, Mid(string(GetEnum(enum'EInputKey',Key)),3), GetKeyDisplayName(Key) );

	return True;
}

// ----------------------------------------------------------------------
// RawMouseButtonPressed()
// ----------------------------------------------------------------------

event bool RawMouseButtonPressed( float PointX, float PointY, EInputKey Button, EInputState iState )
{
	if ( !bWaitingForInput )
		return False;

	if ( iState!=IST_Release )
		return True;

	// First check to see if we're waiting for the user to select a 
	// Keyboard or mouse/joystick Button to override. 
	//
	// Ignore everything but mouse Button and wheel presses
	switch ( Button )
	{
		case IK_MouseWheelUp:
		case IK_MouseWheelDown:
		case IK_LeftMouse:
		case IK_RightMouse:
		case IK_MiddleMouse:
		case IK_UnknownC1: // IK_Mouse4.
		case IK_UnknownC2: // IK_Mouse5.
			ProcessKeySelection( Button, Mid(string(GetEnum(enum'EInputKey',Button)),3), GetKeyDisplayName(Button));
			WaitingForInput(False);
			break;
	}

	return True;
}

// ----------------------------------------------------------------------
// ListRowActivated()
//
// User double-clicked on one of the rows, meaning he/she/it wants 
// to redefine one of the functions
// ----------------------------------------------------------------------

event bool ListRowActivated(window list, int RowId)
{
	// Show help.
	ShowHelp( InputHelpText );
	Selection = LstKeys.RowIdToIndex( RowId );
	WaitingForInput( True );
	return True;
}

// ----------------------------------------------------------------------
// WaitingForInput()
// ----------------------------------------------------------------------

function WaitingForInput( bool bWaiting )
{
 	if ( bWaiting )
	{
		ShowHelp( InputHelpText );
		SetSelectability( True );
		SetFocusWindow( Self );
		GrabMouse();
		Root.LockMouse( True, False );
		Root.ShowCursor( False );
	}
	else
	{
		ShowHelp( WaitingHelpText );
		SetSelectability( False );
		UngrabMouse();
		Root.LockMouse( False, False );
		Root.ShowCursor( True );

		// Set the focus back to the list.
		SetFocusWindow( LstKeys );
	}

	bWaitingForInput = bWaiting;
}

// ----------------------------------------------------------------------
// SaveSettings()
// ----------------------------------------------------------------------

function SaveSettings()
{
	ProcessPending();
}

// ----------------------------------------------------------------------
// ClearFunction()
// ----------------------------------------------------------------------

function ClearFunction()
{
	local int RowId;
	local int RowIndex;

	RowId = LstKeys.GetSelectedRow();

	if ( RowId!=0 )
	{
		RowIndex = LstKeys.RowIdToIndex( RowId );

		if ( MenuValues2[RowIndex]!="" && CanRemapKey(MenuValues2[RowIndex]) )
		{
			AddPending("SET ini:Engine.Engine.Input " $ GetKeyFromDisplayName(MenuValues2[RowIndex]));
			MenuValues2[RowIndex] = "";
		}

		if ( MenuValues1[RowIndex]!="" && CanRemapKey(MenuValues1[RowIndex]) )
		{
			AddPending("SET ini:Engine.Engine.Input " $ GetKeyFromDisplayName(MenuValues1[RowIndex]));
			MenuValues1[RowIndex] = MenuValues2[RowIndex];
			MenuValues2[RowIndex] = "";
		}

		// Update the buttons.
		RefreshKeyBindings();
	}
}

// ----------------------------------------------------------------------
// BuildKeyBindings()
// ----------------------------------------------------------------------

function BuildKeyBindings()
{
	local int i, j, Pos;
	local string KeyName;
	local string Alias;

	// First, clear all the existing Keybinding display strings in the MenuValues[1|2] arrays.
	for( i=0; i<ArrayCount(MenuValues1); i++ )
	{
		MenuValues1[i] = "";
		MenuValues2[i] = "";
	}

	// Now loop through all the Keynames and generate human-readable versions of Keys that are mapped.
	for ( i=0; i<255; i++ )
	{
		KeyName = Player.ConsoleCommand ( "KEYNAME " $ i );

		if ( KeyName!="" )
		{
			Alias = Player.ConsoleCommand( "KEYBINDING " $ KeyName );

			if ( Alias!="" )
			{
				Pos = InStr( Alias, " " );
				if ( Pos!=-1 )
					Alias = Left( Alias, Pos );

				for ( j=0; j<ArrayCount(AliasNames); j++ )
				{
					if ( AliasNames[j]!="" && AliasNames[j]==Alias )
					{
						if ( MenuValues1[j] == "" )
							MenuValues1[j] = GetKeyDisplayNameFromKeyName(KeyName);
						else if ( MenuValues2[j] == "" )
							MenuValues2[j] = GetKeyDisplayNameFromKeyName(KeyName);
					}
				}
			}
		}
	}
}

// ----------------------------------------------------------------------
// ProcessPending()
// ----------------------------------------------------------------------

function ProcessPending()
{
	local int i;

	for ( i=0; i<Pending; i++ )
		Player.ConsoleCommand( PendingCommands[i] );
		
	Pending = 0;
}

// ----------------------------------------------------------------------
// AddPending()
// ----------------------------------------------------------------------

function AddPending( String NewCommand )
{
	PendingCommands[Pending] = NewCommand;
	Pending++;
	if ( Pending==100 )
		ProcessPending();
}

// ----------------------------------------------------------------------
// GetKeyFromDisplayName()
// ----------------------------------------------------------------------

function String GetKeyFromDisplayName( String DisplayName )
{
	local int KeyIndex;

	for ( KeyIndex=0; KeyIndex<ArrayCount(KeyDisplayNames); KeyIndex++ )
		if ( KeyDisplayNames[KeyIndex].InputKey!=IK_None && DisplayName==KeyDisplayNames[KeyIndex].DisplayName )
			return Mid( String(GetEnum(enum'EInputKey',KeyDisplayNames[KeyIndex].InputKey)), 3 );

	return DisplayName;
}

// ----------------------------------------------------------------------
// GetKeyDisplayName()
// ----------------------------------------------------------------------

function String GetKeyDisplayName( EInputKey InputKey )
{
	local int KeyIndex;

	for ( KeyIndex=0; KeyIndex<ArrayCount(KeyDisplayNames); KeyIndex++ )
		if ( KeyDisplayNames[KeyIndex].InputKey!=IK_None && InputKey==KeyDisplayNames[KeyIndex].InputKey)
			return KeyDisplayNames[KeyIndex].DisplayName;

	return Mid( String(GetEnum(enum'EInputKey',InputKey)), 3 );
}
	
// ----------------------------------------------------------------------
// GetKeyDisplayNameFromKeyName()
// ----------------------------------------------------------------------

function String GetKeyDisplayNameFromKeyName( String KeyName )
{
	local int KeyIndex;

	for ( KeyIndex=0; KeyIndex<ArrayCount(KeyDisplayNames); KeyIndex++ )
		if ( KeyDisplayNames[KeyIndex].InputKey!=IK_None && Mid(string(GetEnum(enum'EInputKey',KeyDisplayNames[KeyIndex].InputKey)),3)==KeyName )
			return KeyDisplayNames[KeyIndex].DisplayName;

	return KeyName;
}

// ----------------------------------------------------------------------
// CanRemapKey()
// ----------------------------------------------------------------------

function bool CanRemapKey( String KeyName )
{
	return KeyName!="F1" && KeyName!="F2"; // hack - DEUS_EX STM
}

// ----------------------------------------------------------------------
// ProcessKeySelection()
// ----------------------------------------------------------------------

function ProcessKeySelection( int KeyNo, string KeyName, string KeyDisplayName )
{
	local int i;

	// Some Keys CANNOT be assigned:
	//
	// 1.  Escape
	// 2.  Function Keys (used by Augs)
	// 3.  Number Keys (used by Object Belt)
	// 4.  Tilde (used for console)
	// 5.  Pause (used to pause game)
	// 6.  Print Screen (Well, duh)
	//

	// Make sure the user enters a valid Key (Escape and functionKeys can't be assigned)
	switch ( KeyName )
	{
		case "":
		case "Escape":
		case "Tilde":
		case "PrintScrn":
		case "Pause":
			return;
			break;

		default:
			// Function Keys and 0-9
			if ( (KeyNo>=0x70 && KeyNo<=0x81) || (KeyNo>=48 && KeyNo<=57) )
				return;
			break;
	}

	// Don't waste our time if this Key is already assigned here
	if ( MenuValues1[Selection]==KeyDisplayName || MenuValues2[Selection]==KeyDisplayName )
	   return;

	// Now check to make sure there are no overlapping assignments.
	for ( i=0; i<ArrayCount(AliasNames); i++ )
	{
		if ( AliasNames[i]!="" )
		{
			if ( MenuValues2[i]==KeyDisplayName )
			{
				ShowHelp( Sprintf(ReassignedFromLabel, KeyDisplayName, FunctionText[i]) );
				AddPending("SET ini:Engine.Engine.Input " $ GetKeyFromDisplayName(MenuValues2[i]) );
				MenuValues2[i] = "";
			}

			if ( MenuValues1[i]==KeyDisplayName )
			{
				ShowHelp( Sprintf(ReassignedFromLabel, KeyDisplayName, FunctionText[i]) );
				AddPending( "SET ini:Engine.Engine.Input " $ GetKeyFromDisplayName(MenuValues1[i]) );
				MenuValues1[i] = MenuValues2[i];
				MenuValues2[i] = "";
			}
		}
	}

	// Now assign the Key, trying the first space if it's empty, but using the second space if necessary.  If both slots
	// are filled, then move the second entry into the first and put the new assignment in the second slot.
	if ( MenuValues1[Selection]=="" ) 
	{
		MenuValues1[Selection] = KeyDisplayName;
	}
	else if ( MenuValues2[Selection]=="" )
	{
		MenuValues2[Selection] = KeyDisplayName;
	}
	else if ( CanRemapKey(MenuValues1[Selection]) )
	{
		// Undo first Key assignment.
		AddPending( "SET ini:Engine.Engine.Input " $ GetKeyFromDisplayName(MenuValues1[Selection]) );
		MenuValues1[Selection] = MenuValues2[Selection];
		MenuValues2[Selection] = KeyDisplayName;
	}
	else if (CanRemapKey(MenuValues2[Selection]))
	{
		// Undo second Key assignment
		AddPending( "SET ini:Engine.Engine.Input " $ GetKeyFromDisplayName(MenuValues2[Selection]) );
		MenuValues2[Selection] = KeyDisplayName;
	}
	AddPending( "SET ini:Engine.Engine.Input "$KeyName$" "$AliasNames[Selection] );

	// Update the buttons.
	RefreshKeyBindings();
}

// ----------------------------------------------------------------------
// CreateKeyList()
//
// Creates the listbox containing the Key bindings
// ----------------------------------------------------------------------

function CreateKeyList()
{
	WinScroll = CreateScrollAreaWindow(WinClient);

	WinScroll.SetPos( 11, 23 );
	WinScroll.SetSize( 369, 268 );

	LstKeys = MenuUIListWindow(WinScroll.clipWindow.NewChild(Class'MenuUIListWindow'));
	LstKeys.EnableMultiSelect( False );
	LstKeys.EnableAutoExpandColumns( False );
	LstKeys.EnableHotKeys( False );

	LstKeys.SetNumColumns( 2 );

	LstKeys.SetColumnWidth( 0, 164 );
	LstKeys.SetColumnType ( 0, COLTYPE_String );
	LstKeys.SetColumnWidth( 1, 205 );
	LstKeys.SetColumnType ( 1, COLTYPE_String );
}

// ----------------------------------------------------------------------
// PopulateKeyList()
// ----------------------------------------------------------------------

function PopulateKeyList()
{
	local int KeyIndex;

	// First erase the old list.
	LstKeys.DeleteAllRows();

	for ( KeyIndex=0; KeyIndex<ArrayCount(AliasNames); KeyIndex++ )
		if ( AliasNames[KeyIndex]!="" )
			LstKeys.AddRow( FunctionText[KeyIndex] $ ";" $ GetInputDisplayText(KeyIndex) );
}

// ----------------------------------------------------------------------
// CreateHeaderButtons()
// ----------------------------------------------------------------------

function CreateHeaderButtons()
{
	BtnHeaderAction   = CreateHeaderButton( 10,  3, 162, StrHeaderActionLabel,   WinClient );
	BtnHeaderAssigned = CreateHeaderButton( 175, 3, 157, StrHeaderAssignedLabel, WinClient );

	BtnHeaderAction.SetSensitivity( False );
	BtnHeaderAssigned.SetSensitivity( False );
}

// ----------------------------------------------------------------------
// GetInputDisplayText()
// ----------------------------------------------------------------------

function String GetInputDisplayText(int KeyIndex)
{
	if ( MenuValues1[KeyIndex]=="" )
		return NoneText;
	else if ( MenuValues2[KeyIndex]!="" )
		return MenuValues1[KeyIndex] $ "," @ MenuValues2[KeyIndex];
	else
		return MenuValues1[KeyIndex];
}

// ----------------------------------------------------------------------
// RefreshKeyBindings()
// ----------------------------------------------------------------------

function RefreshKeyBindings()
{
	local int KeyIndex;
	local int RowId;

	for( KeyIndex=0; KeyIndex<ArrayCount(AliasNames); KeyIndex++ )
	{
		if ( AliasNames[KeyIndex]!="" )
		{
			RowId = LstKeys.IndexToRowId( KeyIndex );
			LstKeys.SetField( RowId, 1, GetInputDisplayText(KeyIndex) );
		}
	}
}

// ----------------------------------------------------------------------
// ResetToDefaults()
// ----------------------------------------------------------------------

function ResetToDefaults()
{
	Pending = 0;
	Selection = -1;
	bWaitingForInput = False;
	BuildKeyBindings();
	PopulateKeyList();
	ShowHelp(WaitingHelpText);
}

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

defaultproperties
{
	FunctionText(0)="Fire Weapon/Use object in hand"
	FunctionText(1)="Use object in world"
	FunctionText(2)="Drop/Throw Item"
	FunctionText(3)="Put Away Item"
	FunctionText(4)="Move Forward"
	FunctionText(5)="Move Backward"
	FunctionText(6)="Turn Left"
	FunctionText(7)="Turn Right"
	FunctionText(8)="Strafe Left"
	FunctionText(9)="Strafe Right"
	FunctionText(10)="Lean Left"
	FunctionText(11)="Lean Right"
	FunctionText(12)="Jump"
	FunctionText(13)="Crouch"
	FunctionText(14)="Mouse Look"
	FunctionText(15)="Look Up"
	FunctionText(16)="Look Down"
	FunctionText(17)="Center View"
	FunctionText(18)="Walk/Run"
	FunctionText(19)="Toggle Walk/Run"
	FunctionText(20)="Strafe"
	FunctionText(21)="Select Next Belt Item"
	FunctionText(22)="Select Previous Belt Item"
	FunctionText(23)="Reload Weapon"
	FunctionText(24)="Toggle Scope"
	FunctionText(25)="Toggle Laser Sight"
	FunctionText(26)="Activate All Augmentations"
	FunctionText(27)="Deactivate All Augmentations"
	FunctionText(28)="Change Ammo"
	FunctionText(29)="Take Screenshot"
	FunctionText(30)="Activate Inventory Screen"
	FunctionText(31)="Activate Health Screen"
	FunctionText(32)="Activate Augmentations Screen"
	FunctionText(33)="Activate Skills Screen"
	FunctionText(34)="Activate Goals/Notes Screen"
	FunctionText(35)="Activate Conversations Screen"
	FunctionText(36)="Activate Images Screen"
	FunctionText(37)="Activate Logs Screen"
	FunctionText(38)="Quick Save"
	FunctionText(39)="Quick Load"
	FunctionText(40)="Toggle Crosshairs"
	FunctionText(41)="Toggle Hit Display"
	FunctionText(42)="Toggle Compass"
	FunctionText(43)="Toggle Augmentation Display"
	FunctionText(44)="Toggle Object Belt"
	FunctionText(45)="Toggle Ammo Display"
	FunctionText(46)="F3"
	FunctionText(47)="F4"
	FunctionText(48)="F5"
	FunctionText(49)="F6"
	FunctionText(50)="F7"
	FunctionText(51)="F8"
	FunctionText(52)="F9"
	FunctionText(53)="F10"
	FunctionText(54)="F11"
	FunctionText(55)="F12"
	FunctionText(56)="Show MultiPlayer Scores"
	FunctionText(57)="Send Message To Team"
	FunctionText(58)="Send Message To All"
	FunctionText(59)="Activate MultiPlayer Skill Menu"
	FunctionText(60)="Show MultiPlayer Kill Details"
	AliasNames(0)="ParseLeftClick|Fire"
	AliasNames(1)="ParseRightClick"
	AliasNames(2)="DropItem"
	AliasNames(3)="PutInHand"
	AliasNames(4)="MoveForward"
	AliasNames(5)="MoveBackward"
	AliasNames(6)="TurnLeft"
	AliasNames(7)="TurnRight"
	AliasNames(8)="StrafeLeft"
	AliasNames(9)="StrafeRight"
	AliasNames(10)="LeanLeft"
	AliasNames(11)="LeanRight"
	AliasNames(12)="Jump"
	AliasNames(13)="Duck"
	AliasNames(14)="Look"
	AliasNames(15)="LookUp"
	AliasNames(16)="LookDown"
	AliasNames(17)="CenterView"
	AliasNames(18)="Walking"
	AliasNames(19)="ToggleWalk"
	AliasNames(20)="Strafe"
	AliasNames(21)="NextBeltItem"
	AliasNames(22)="PrevBeltItem"
	AliasNames(23)="ReloadWeapon"
	AliasNames(24)="ToggleScope"
	AliasNames(25)="ToggleLaser"
	AliasNames(26)="ActivateAllAugs"
	AliasNames(27)="DeactivateAllAugs"
	AliasNames(28)="SwitchAmmo"
	AliasNames(29)="Shot"
	AliasNames(30)="ShowInventoryWindow"
	AliasNames(31)="ShowHealthWindow"
	AliasNames(32)="ShowAugmentationsWindow"
	AliasNames(33)="ShowSkillsWindow"
	AliasNames(34)="ShowGoalsWindow"
	AliasNames(35)="ShowConversationsWindow"
	AliasNames(36)="ShowImagesWindow"
	AliasNames(37)="ShowLogsWindow"
	AliasNames(38)="QuickSave"
	AliasNames(39)="QuickLoad"
	AliasNames(40)="ToggleCrosshair"
	AliasNames(41)="ToggleHitDisplay"
	AliasNames(42)="ToggleCompass"
	AliasNames(43)="ToggleAugDisplay"
	AliasNames(44)="ToggleObjectBelt"
	AliasNames(45)="ToggleAmmoDisplay"
	AliasNames(46)="DualMapF3"
	AliasNames(47)="DualMapF4"
	AliasNames(48)="DualMapF5"
	AliasNames(49)="DualMapF6"
	AliasNames(50)="DualMapF7"
	AliasNames(51)="DualMapF8"
	AliasNames(52)="DualMapF9"
	AliasNames(53)="DualMapF10"
	AliasNames(54)="DualMapF11"
	AliasNames(55)="DualMapF12"
	AliasNames(56)="ShowScores"
	AliasNames(57)="TeamTalk"
	AliasNames(58)="Talk"
	AliasNames(59)="BuySkills"
	AliasNames(60)="KillerProfile"
	KeyDisplayNames(0)=(InputKey=IK_LeftMouse,displayName="Left Mouse Button")
	KeyDisplayNames(1)=(InputKey=IK_RightMouse,displayName="Right Mouse Button")
	KeyDisplayNames(2)=(InputKey=IK_MiddleMouse,displayName="Middle Mouse Button")
	KeyDisplayNames(3)=(InputKey=IK_CapsLock,displayName="CAPS Lock")
	KeyDisplayNames(4)=(InputKey=IK_PageUp,displayName="Page Up")
	KeyDisplayNames(5)=(InputKey=IK_PageDown,displayName="Page Down")
	KeyDisplayNames(6)=(InputKey=IK_PrintScrn,displayName="Print Screen")
	KeyDisplayNames(7)=(InputKey=IK_GreyStar,displayName="NumPad Asterisk")
	KeyDisplayNames(8)=(InputKey=IK_GreyPlus,displayName="NumPad Plus")
	KeyDisplayNames(9)=(InputKey=IK_GreyMinus,displayName="NumPad Minus")
	KeyDisplayNames(10)=(InputKey=IK_GreySlash,displayName="NumPad Slash")
	KeyDisplayNames(11)=(InputKey=IK_NumPadPeriod,displayName="NumPad Period")
	KeyDisplayNames(12)=(InputKey=IK_NumLock,displayName="Num Lock")
	KeyDisplayNames(13)=(InputKey=IK_ScrollLock,displayName="Scroll Lock")
	KeyDisplayNames(14)=(InputKey=IK_LShift,displayName="Left Shift")
	KeyDisplayNames(15)=(InputKey=IK_RShift,displayName="Right Shift")
	KeyDisplayNames(16)=(InputKey=IK_LControl,displayName="Left Control")
	KeyDisplayNames(17)=(InputKey=IK_RControl,displayName="Right Control")
	KeyDisplayNames(18)=(InputKey=IK_MouseWheelUp,displayName="Mouse Wheel Up")
	KeyDisplayNames(19)=(InputKey=IK_MouseWheelDown,displayName="Mouse Wheel Down")
	KeyDisplayNames(20)=(InputKey=IK_MouseX,displayName="Mouse X")
	KeyDisplayNames(21)=(InputKey=IK_MouseY,displayName="Mouse Y")
	KeyDisplayNames(22)=(InputKey=IK_MouseZ,displayName="Mouse Z")
	KeyDisplayNames(23)=(InputKey=IK_MouseW,displayName="Mouse W")
	KeyDisplayNames(24)=(InputKey=IK_LeftBracket,displayName="Left Bracket")
	KeyDisplayNames(25)=(InputKey=IK_RightBracket,displayName="Right Bracket")
	KeyDisplayNames(26)=(InputKey=IK_SingleQuote,displayName="Single Quote")
	KeyDisplayNames(27)=(InputKey=IK_Joy1,displayName="Joystick Button 1")
	KeyDisplayNames(28)=(InputKey=IK_Joy2,displayName="Joystick Button 2")
	KeyDisplayNames(29)=(InputKey=IK_Joy3,displayName="Joystick Button 3")
	KeyDisplayNames(30)=(InputKey=IK_Joy4,displayName="Joystick Button 4")
	KeyDisplayNames(31)=(InputKey=IK_JoyX,displayName="Joystick X")
	KeyDisplayNames(32)=(InputKey=IK_JoyY,displayName="Joystick Y")
	KeyDisplayNames(33)=(InputKey=IK_JoyZ,displayName="Joystick Z")
	KeyDisplayNames(34)=(InputKey=IK_JoyR,displayName="Joystick R")
	KeyDisplayNames(35)=(InputKey=IK_JoyU,displayName="Joystick U")
	KeyDisplayNames(36)=(InputKey=IK_JoyV,displayName="Joystick V")
	KeyDisplayNames(37)=(InputKey=IK_JoyPovUp,displayName="Joystick Pov Up")
	KeyDisplayNames(38)=(InputKey=IK_JoyPovDown,displayName="Joystick Pov Down")
	KeyDisplayNames(39)=(InputKey=IK_JoyPovLeft,displayName="Joystick Pov Left")
	KeyDisplayNames(40)=(InputKey=IK_JoyPovRight,displayName="Joystick Pov Right")
	KeyDisplayNames(41)=(InputKey=IK_Ctrl,displayName="Control")
	KeyDisplayNames(42)=(InputKey=IK_Left,displayName="Left Arrow")
	KeyDisplayNames(43)=(InputKey=IK_Right,displayName="Right Arrow")
	KeyDisplayNames(44)=(InputKey=IK_Up,displayName="Up Arrow")
	KeyDisplayNames(45)=(InputKey=IK_Down,displayName="Down Arrow")
	KeyDisplayNames(46)=(InputKey=IK_Insert,displayName="Insert")
	KeyDisplayNames(47)=(InputKey=IK_Home,displayName="Home")
	KeyDisplayNames(48)=(InputKey=IK_Delete,displayName="Delete")
	KeyDisplayNames(49)=(InputKey=IK_End,displayName="End")
	KeyDisplayNames(50)=(InputKey=IK_NumPad0,displayName="NumPad 0")
	KeyDisplayNames(51)=(InputKey=IK_NumPad1,displayName="NumPad 1")
	KeyDisplayNames(52)=(InputKey=IK_NumPad2,displayName="NumPad 2")
	KeyDisplayNames(53)=(InputKey=IK_NumPad3,displayName="NumPad 3")
	KeyDisplayNames(54)=(InputKey=IK_NumPad4,displayName="NumPad 4")
	KeyDisplayNames(55)=(InputKey=IK_NumPad5,displayName="NumPad 5")
	KeyDisplayNames(56)=(InputKey=IK_NumPad6,displayName="NumPad 6")
	KeyDisplayNames(57)=(InputKey=IK_NumPad7,displayName="NumPad 7")
	KeyDisplayNames(58)=(InputKey=IK_NumPad8,displayName="NumPad 8")
	KeyDisplayNames(59)=(InputKey=IK_NumPad9,displayName="NumPad 9")
	KeyDisplayNames(60)=(InputKey=IK_Period,displayName="Period")
	KeyDisplayNames(61)=(InputKey=IK_Comma,displayName="Comma")
	KeyDisplayNames(62)=(InputKey=IK_Backslash,displayName="Backslash")
	KeyDisplayNames(63)=(InputKey=IK_Semicolon,displayName="Semicolon")
	KeyDisplayNames(64)=(InputKey=IK_Equals,displayName="Equals")
	KeyDisplayNames(65)=(InputKey=IK_Slash,displayName="Slash")
	KeyDisplayNames(66)=(InputKey=IK_Enter,displayName="Enter")
	KeyDisplayNames(67)=(InputKey=IK_Alt,displayName="Alt")
	KeyDisplayNames(68)=(InputKey=IK_Backspace,displayName="Backspace")
	KeyDisplayNames(69)=(InputKey=IK_Shift,displayName="Shift")
	KeyDisplayNames(70)=(InputKey=IK_Space,displayName="Space")
	//KeyDisplayNames(71)=(InputKey=IK_Mouse4,displayName="Mouse Button 4")
	//KeyDisplayNames(72)=(InputKey=IK_Mouse5,displayName="Mouse Button 5")
	KeyDisplayNames(71)=(InputKey=IK_UnknownC1,displayName="Mouse Button 4")
	KeyDisplayNames(72)=(InputKey=IK_UnknownC2,displayName="Mouse Button 5")
	NoneText="[None]"
	StrHeaderActionLabel="Action"
	StrHeaderAssignedLabel="Assigned Key/Button"
	WaitingHelpText="Select the function you wish to remap and then press [Enter] or Double-Click.  Press [Delete] to remove Key bindings."
	InputHelpText="Please press the Key or Button you wish to assign to this function.  Press [ESC] to cancel."
	ReassignedFromLabel="'%s' reassigned from '%s'"
	ActionButtons(0)=(Align=HALIGN_Right,Action=AB_Cancel)
	ActionButtons(1)=(Align=HALIGN_Right,Action=AB_OK)
	ActionButtons(2)=(Action=AB_Reset)
	Title="Keyboard/Mouse Settings"
	ClientWidth=384
	ClientHeight=366
	ClientTextures(0)=Texture'DeusExUI.UserInterface.MenuCustomizeKeysBackground_1'
	ClientTextures(1)=Texture'DeusExUI.UserInterface.MenuCustomizeKeysBackground_2'
	ClientTextures(2)=Texture'DeusExUI.UserInterface.MenuCustomizeKeysBackground_3'
	ClientTextures(3)=Texture'DeusExUI.UserInterface.MenuCustomizeKeysBackground_4'
	TextureCols=2
	bHelpAlwaysOn=True
	HelpPosY=312
}
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
Post Reply