https://wiki.beyondunreal.com/w/api.php?action=feedcontributions&user=Rejecht&feedformat=atomUnreal Wiki - User contributions [en]2017-11-18T07:49:41ZUser contributionsMediaWiki 1.25.1https://wiki.beyondunreal.com/User:Rejecht/Getting_Started_UE4?diff=46059User:Rejecht/Getting Started UE42014-08-16T16:56:17Z<p>Rejecht: /* Unreal Engine 4 */</p>
<hr />
<div>=== Unreal Engine 4 ===<br />
<br />
==== Microsoft Visual Studio Express 2013 for Windows Desktop ====<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/VisualStudioSetup/index.html Setting Up Visual Studio for UE4]<br />
<br />
=== Protocol ===<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html Coding Standard]<br />
<br />
Naming Conventions * Methods * Style * Organization<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/index.html Development Setup]<br />
<br />
=== Replication ===<br />
<br />
Game types and replicated values?<br />
<br />
# Declare the UPROPERTY as replicated in C++<br />
# Initialize the state of the variable in the game type constructor<br />
# Declare the type of replication in the game type GetLifetimeReplicatedProps function<br />
<br />
=== Low Level ===<br />
<br />
==== Structure ====<br />
<br />
Explains the basic client only {Camera, HUD}, client and server {Pawn, Character, PlayerState, GameState}, and server-only {Controller} junction points.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/index.html Unreal Architecture]<br />
<br />
==== Objects ====<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Objects/Optimizations/index.html Unreal Object Handling]<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Objects/Optimizations/index.html UObject Instance Creation]<br />
<br />
==== Pointers ====<br />
<br />
When referring to something you use may shared pointers (pointer + reference count), shared references, or weak pointers.<br />
<br />
The shared pointer types are optimal for higher level or lower frequency access.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/SmartPointerLibrary/index.html Unreal Smart Pointer Library]<br />
<br />
.IsValid() is used on shared pointers to check if they are NULL.<br />
<br />
===== Usage Patterns =====<br />
<br />
* Caching a pointer to something using a weak pointer does not touch reference counting thus requires a check against NULL before access (and it gets set to NULL automatically when the instance it points to gets destroyed).<br />
* The shared pointer itself requires a check against IsValid before access.<br />
* Custom C++ classes may want to implement FSerializableObject to handle reference counting when they refer to other UObject classes.<br />
* Assigning NULL to a smart pointer may force a deletion of the object.<br />
<br />
==== Actors ====<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Actors/Components/index.html Components]<br />
<br />
===== Destruction =====<br />
<br />
[https://docs.unrealengine.com/latest/INT/API/Runtime/Engine/GameFramework/AActor/IsPendingKillPending/index.html IsPendingKillPending]<br />
<br />
=== API Overview ===<br />
<br />
https://docs.unrealengine.com/latest/INT/API/index.html</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht/Getting_Started_UE4?diff=46056User:Rejecht/Getting Started UE42014-08-10T18:11:42Z<p>Rejecht: /* Actors */</p>
<hr />
<div>=== Unreal Engine 4 ===<br />
<br />
<br />
<br />
=== Protocol ===<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html Coding Standard]<br />
<br />
Naming Conventions * Methods * Style * Organization<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/index.html Development Setup]<br />
<br />
=== Replication ===<br />
<br />
Game types and replicated values?<br />
<br />
# Declare the UPROPERTY as replicated in C++<br />
# Initialize the state of the variable in the game type constructor<br />
# Declare the type of replication in the game type GetLifetimeReplicatedProps function<br />
<br />
=== Low Level ===<br />
<br />
==== Structure ====<br />
<br />
Explains the basic client only {Camera, HUD}, client and server {Pawn, Character, PlayerState, GameState}, and server-only {Controller} junction points.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/index.html Unreal Architecture]<br />
<br />
==== Objects ====<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Objects/Optimizations/index.html Unreal Object Handling]<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Objects/Optimizations/index.html UObject Instance Creation]<br />
<br />
==== Pointers ====<br />
<br />
When referring to something you use may shared pointers (pointer + reference count), shared references, or weak pointers.<br />
<br />
The shared pointer types are optimal for higher level or lower frequency access.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/SmartPointerLibrary/index.html Unreal Smart Pointer Library]<br />
<br />
.IsValid() is used on shared pointers to check if they are NULL.<br />
<br />
===== Usage Patterns =====<br />
<br />
* Caching a pointer to something using a weak pointer does not touch reference counting thus requires a check against NULL before access (and it gets set to NULL automatically when the instance it points to gets destroyed).<br />
* The shared pointer itself requires a check against IsValid before access.<br />
* Custom C++ classes may want to implement FSerializableObject to handle reference counting when they refer to other UObject classes.<br />
* Assigning NULL to a smart pointer may force a deletion of the object.<br />
<br />
==== Actors ====<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Actors/Components/index.html Components]<br />
<br />
===== Destruction =====<br />
<br />
[https://docs.unrealengine.com/latest/INT/API/Runtime/Engine/GameFramework/AActor/IsPendingKillPending/index.html IsPendingKillPending]<br />
<br />
=== API Overview ===<br />
<br />
https://docs.unrealengine.com/latest/INT/API/index.html</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht/Getting_Started_UE4?diff=46055User:Rejecht/Getting Started UE42014-08-10T17:53:34Z<p>Rejecht: /* Actors */</p>
<hr />
<div>=== Unreal Engine 4 ===<br />
<br />
<br />
<br />
=== Protocol ===<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html Coding Standard]<br />
<br />
Naming Conventions * Methods * Style * Organization<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/index.html Development Setup]<br />
<br />
=== Replication ===<br />
<br />
Game types and replicated values?<br />
<br />
# Declare the UPROPERTY as replicated in C++<br />
# Initialize the state of the variable in the game type constructor<br />
# Declare the type of replication in the game type GetLifetimeReplicatedProps function<br />
<br />
=== Low Level ===<br />
<br />
==== Structure ====<br />
<br />
Explains the basic client only {Camera, HUD}, client and server {Pawn, Character, PlayerState, GameState}, and server-only {Controller} junction points.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/index.html Unreal Architecture]<br />
<br />
==== Objects ====<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Objects/Optimizations/index.html Unreal Object Handling]<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Objects/Optimizations/index.html UObject Instance Creation]<br />
<br />
==== Pointers ====<br />
<br />
When referring to something you use may shared pointers (pointer + reference count), shared references, or weak pointers.<br />
<br />
The shared pointer types are optimal for higher level or lower frequency access.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/SmartPointerLibrary/index.html Unreal Smart Pointer Library]<br />
<br />
.IsValid() is used on shared pointers to check if they are NULL.<br />
<br />
===== Usage Patterns =====<br />
<br />
* Caching a pointer to something using a weak pointer does not touch reference counting thus requires a check against NULL before access (and it gets set to NULL automatically when the instance it points to gets destroyed).<br />
* The shared pointer itself requires a check against IsValid before access.<br />
* Custom C++ classes may want to implement FSerializableObject to handle reference counting when they refer to other UObject classes.<br />
* Assigning NULL to a smart pointer may force a deletion of the object.<br />
<br />
==== Actors ====<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Actors/Components/index.html Components]<br />
<br />
=== API Overview ===<br />
<br />
https://docs.unrealengine.com/latest/INT/API/index.html</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht/Getting_Started_UE4?diff=46054User:Rejecht/Getting Started UE42014-08-10T17:23:12Z<p>Rejecht: /* Pointers */</p>
<hr />
<div>=== Unreal Engine 4 ===<br />
<br />
<br />
<br />
=== Protocol ===<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html Coding Standard]<br />
<br />
Naming Conventions * Methods * Style * Organization<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/index.html Development Setup]<br />
<br />
=== Replication ===<br />
<br />
Game types and replicated values?<br />
<br />
# Declare the UPROPERTY as replicated in C++<br />
# Initialize the state of the variable in the game type constructor<br />
# Declare the type of replication in the game type GetLifetimeReplicatedProps function<br />
<br />
=== Low Level ===<br />
<br />
==== Structure ====<br />
<br />
Explains the basic client only {Camera, HUD}, client and server {Pawn, Character, PlayerState, GameState}, and server-only {Controller} junction points.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/index.html Unreal Architecture]<br />
<br />
==== Objects ====<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Objects/Optimizations/index.html Unreal Object Handling]<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Objects/Optimizations/index.html UObject Instance Creation]<br />
<br />
==== Pointers ====<br />
<br />
When referring to something you use may shared pointers (pointer + reference count), shared references, or weak pointers.<br />
<br />
The shared pointer types are optimal for higher level or lower frequency access.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/SmartPointerLibrary/index.html Unreal Smart Pointer Library]<br />
<br />
.IsValid() is used on shared pointers to check if they are NULL.<br />
<br />
===== Usage Patterns =====<br />
<br />
* Caching a pointer to something using a weak pointer does not touch reference counting thus requires a check against NULL before access (and it gets set to NULL automatically when the instance it points to gets destroyed).<br />
* The shared pointer itself requires a check against IsValid before access.<br />
* Custom C++ classes may want to implement FSerializableObject to handle reference counting when they refer to other UObject classes.<br />
* Assigning NULL to a smart pointer may force a deletion of the object.<br />
<br />
==== Actors ====<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Actors/Components/index.html Components]</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht/Getting_Started_UE4?diff=46053User:Rejecht/Getting Started UE42014-08-10T16:54:20Z<p>Rejecht: /* Structure */</p>
<hr />
<div>=== Unreal Engine 4 ===<br />
<br />
<br />
<br />
=== Protocol ===<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html Coding Standard]<br />
<br />
Naming Conventions * Methods * Style * Organization<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/index.html Development Setup]<br />
<br />
=== Replication ===<br />
<br />
Game types and replicated values?<br />
<br />
# Declare the UPROPERTY as replicated in C++<br />
# Initialize the state of the variable in the game type constructor<br />
# Declare the type of replication in the game type GetLifetimeReplicatedProps function<br />
<br />
=== Low Level ===<br />
<br />
==== Structure ====<br />
<br />
Explains the basic client only {Camera, HUD}, client and server {Pawn, Character, PlayerState, GameState}, and server-only {Controller} junction points.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/index.html Unreal Architecture]<br />
<br />
==== Pointers ====<br />
<br />
When referring to something you use may shared pointers (pointer + reference count), shared references, or weak pointers.<br />
<br />
The shared pointer types are optimal for higher level or lower frequency access.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/SmartPointerLibrary/index.html Unreal Smart Pointer Library]<br />
<br />
.IsValid() is used on shared pointers to check if they are NULL.</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht/Getting_Started_UE4?diff=46052User:Rejecht/Getting Started UE42014-08-10T16:52:54Z<p>Rejecht: /* Pointers */</p>
<hr />
<div>=== Unreal Engine 4 ===<br />
<br />
<br />
<br />
=== Protocol ===<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html Coding Standard]<br />
<br />
Naming Conventions * Methods * Style * Organization<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/index.html Development Setup]<br />
<br />
=== Replication ===<br />
<br />
Game types and replicated values?<br />
<br />
# Declare the UPROPERTY as replicated in C++<br />
# Initialize the state of the variable in the game type constructor<br />
# Declare the type of replication in the game type GetLifetimeReplicatedProps function<br />
<br />
=== Low Level ===<br />
<br />
=== Structure ===<br />
<br />
Explains the junction points.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/index.html Unreal Architecture]<br />
<br />
==== Pointers ====<br />
<br />
When referring to something you use may shared pointers (pointer + reference count), shared references, or weak pointers.<br />
<br />
The shared pointer types are optimal for higher level or lower frequency access.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/SmartPointerLibrary/index.html Unreal Smart Pointer Library]<br />
<br />
.IsValid() is used on shared pointers to check if they are NULL.</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht/Getting_Started_UE4?diff=46051User:Rejecht/Getting Started UE42014-08-10T16:51:06Z<p>Rejecht: /* Pointers */</p>
<hr />
<div>=== Unreal Engine 4 ===<br />
<br />
<br />
<br />
=== Protocol ===<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html Coding Standard]<br />
<br />
Naming Conventions * Methods * Style * Organization<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/index.html Development Setup]<br />
<br />
=== Replication ===<br />
<br />
Game types and replicated values?<br />
<br />
# Declare the UPROPERTY as replicated in C++<br />
# Initialize the state of the variable in the game type constructor<br />
# Declare the type of replication in the game type GetLifetimeReplicatedProps function<br />
<br />
=== Low Level ===<br />
<br />
=== Structure ===<br />
<br />
Explains the junction points.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/index.html Unreal Architecture]<br />
<br />
==== Pointers ====<br />
<br />
When referring to something you use shared pointers, shared references, or weak pointers.<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/SmartPointerLibrary/index.html Unreal Smart Pointer Library]<br />
<br />
IsValid is used on shared pointers to check if they are NULL.</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht/Getting_Started_UE4?diff=46050User:Rejecht/Getting Started UE42014-08-10T16:37:20Z<p>Rejecht: /* Replication */</p>
<hr />
<div>=== Unreal Engine 4 ===<br />
<br />
<br />
<br />
=== Protocol ===<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html Coding Standard]<br />
<br />
Naming Conventions * Methods * Style * Organization<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/index.html Development Setup]<br />
<br />
=== Replication ===<br />
<br />
Game types and replicated values?<br />
<br />
# Declare the UPROPERTY as replicated in C++<br />
# Initialize the state of the variable in the game type constructor<br />
# Declare the type of replication in the game type GetLifetimeReplicatedProps function<br />
<br />
=== Low Level ===<br />
<br />
==== Pointers ====<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/SmartPointerLibrary/WeakPointer/index.html Weak Pointers]</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht/Getting_Started_UE4?diff=46045User:Rejecht/Getting Started UE42014-08-08T11:16:17Z<p>Rejecht: </p>
<hr />
<div>=== Unreal Engine 4 ===<br />
<br />
<br />
<br />
=== Protocol ===<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html Coding Standard]<br />
<br />
Naming Conventions * Methods * Style * Organization<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/index.html Development Setup]<br />
<br />
=== Replication ===<br />
<br />
Game types and replicated values?<br />
<br />
# Declare the UPROPERTY as replicated in C++<br />
# Initialize the state of the variable in the game type constructor<br />
# Declare the type of replication in the game type GetLifetimeReplicatedProps function</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=46044User:Rejecht2014-08-08T11:11:16Z<p>Rejecht: /* About */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Articles ===<br />
<br />
[[User:Rejecht/Key_Bindings_(UT2004)]]<br />
<br />
=== Where ===<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
=== UE 4 ===<br />
<br />
[[User:Rejecht/Getting Started UE4]]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Countless Hours Wasted ===<br />
<br />
==== Demo Recording & Playback ====<br />
<br />
# Playing back a demo recorded in a networked game results in several text messages being duplicated. For example, orders sent to bots, so it was never a bug in the mod, but my lack of awareness of this issue in the first place.<br />
<br />
=== Nice to know ===<br />
<br />
==== ClientFlash does nothing ====<br />
<br />
(It's used to fade all screen colors from a starting color.)<br />
<br />
Check these dependencies:<br />
#ScreenFlashes in UT2004.ini must be True for the driver you use.<br />
#Check (ScreenFlashScaling > 0.0f) in User.ini.<br />
<br />
If the settings check out, check if something prevents PlayerController.ViewFlash () from getting called.<br />
<br />
==== Having no pawn is the same as being in a Dead state ====<br />
<br />
The game has a hard-coded client-side rule that forces the player to enter a dead state when it does not find a Pawn.<br />
<br />
Triggers:<br />
* [NM_Standalone|NM_ListenServer|NM_Client] (Pawn == None)<br />
<br />
Cause:<br />
* PlayerController.(PlayerWalking)PlayerMove ()<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Servers do not know the locations of spectators ====<br />
<br />
Spectators are in control of the location, and do not get sneaky state information, nor do they send location information to the server in the same way as a player with a pawn does.<br />
<br />
The sources for UT3 1.5 try to solve this problem with ServerSetSpectatorLocation, however, this function can't possibly work as intended, as it does not update the position when the camera moves, but rather sets the last position and stays there. To track the camera, use PlayerCalcView, and throttle calls to your own ServerSetSpectatorLocation from the client (no need to call each tick). You also need to make sure PlayerMove (movement keys) does not offset the view from where the camera is, unless the player has freed the camera.<br />
<br />
It is assumed that this has something to do with sounds not playing in the right location, but not yet proven.<br />
<br />
==== xPawn.ChangedWeapon breaks simulated function chain ====<br />
<br />
Pawn.ChangedWeapon is declared simulated, however, in the derived xPawn class, it is redeclared non-simulated while still being called from simulated functions, thus breaking the chain until the Pawn becomes ROLE_AutonomousProxy, which may take some time in netplay.<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
Also note that this is a bad real-world example, because all channel values are always odd numbers. For example, if the red channel value is 50, the color code becomes 51. See the second example for the real world use of such a function, or you care about accuracy.<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript><br />
<br />
For the sake of completeness, a version that applies to the real world, using Max to keep the value within the allowed range [1, 255].<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Max (Color.R, 1))<br />
$ Chr (Max (Color.G, 1))<br />
$ Chr (Max (Color.B, 1))<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht/Getting_Started_UE4?diff=46043User:Rejecht/Getting Started UE42014-08-08T11:09:53Z<p>Rejecht: Created page with ''''Unreal Engine 4''' '''Protocol''' [https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html Coding Standard] Naming Conventions * Method…'</p>
<hr />
<div>'''Unreal Engine 4'''<br />
<br />
<br />
<br />
'''Protocol'''<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html Coding Standard]<br />
<br />
Naming Conventions * Methods * Style * Organization<br />
<br />
[https://docs.unrealengine.com/latest/INT/Programming/Development/index.html Development Setup]</div>Rejechthttps://wiki.beyondunreal.com/Key_Bindings_(UT2004)?diff=46039Key Bindings (UT2004)2014-08-01T21:39:11Z<p>Rejecht: </p>
<hr />
<div>Source: DefUser.ini (UT2004v3369)<br />
<br />
'''[Engine.Input]'''<br />
<nowiki><br />
Aliases[0]=(Command="Button bFire | Fire",Alias=Fire)<br />
Aliases[1]=(Command="Button bAltFire | AltFire",Alias=AltFire)<br />
Aliases[2]=(Command="Axis aBaseY Speed=+300.0",Alias=MoveForward)<br />
Aliases[3]=(Command="Axis aBaseY Speed=-300.0",Alias=MoveBackward)<br />
Aliases[4]=(Command="Axis aBaseX Speed=-40.0",Alias=TurnLeft)<br />
Aliases[5]=(Command="Axis aBaseX Speed=+40.0",Alias=TurnRight)<br />
Aliases[6]=(Command="Axis aStrafe Speed=-300.0",Alias=StrafeLeft)<br />
Aliases[7]=(Command="Axis aStrafe Speed=+300.0",Alias=StrafeRight)<br />
Aliases[8]=(Command="Jump | Axis aUp Speed=+300.0",Alias=Jump)<br />
Aliases[9]=(Command="Button bDuck | Axis aUp Speed=-300.0",Alias=Duck)<br />
Aliases[10]=(Command="Button bLook",Alias=Look)<br />
Aliases[11]=(Command="Toggle bLook",Alias=LookToggle)<br />
Aliases[12]=(Command="ActivateItem",Alias=InventoryActivate)<br />
Aliases[13]=(Command="NextItem",Alias=InventoryNext)<br />
Aliases[14]=(Command="PrevItem",Alias=InventoryPrevious)<br />
Aliases[15]=(Command="Axis aLookUp Speed=+25.0",Alias=LookUp)<br />
Aliases[16]=(Command="Axis aLookUp Speed=-25.0",Alias=LookDown)<br />
Aliases[17]=(Command="Button bSnapLevel",Alias=CenterView)<br />
Aliases[18]=(Command="Button bRun",Alias=Walking)<br />
Aliases[19]=(Command="Button bStrafe",Alias=Strafe)<br />
Aliases[20]=(Command="NextWeapon",Alias=NextWeapon)<br />
Aliases[21]=(Command="Button bFreeLook",Alias=FreeLook)<br />
Aliases[22]=(Command="ViewClass Pawn",Alias=ViewTeam)<br />
Aliases[23]=(Command="Button bTurnToNearest",Alias=TurnToNearest)<br />
Aliases[24]=(Command="Button bTurn180",Alias=Turn180)<br />
Aliases[25]=(Command="ShowScores|OnRelease ShowScores",Alias=ScoreToggle)<br />
Aliases[26]=(Command="InGameChat",Alias=InGameChat)<br />
Aliases[27]=(Command="ServerInfo",Alias=ServerInfo)<br />
Aliases[28]=(Command="Button bVoiceTalk",Alias=VoiceTalk)<br />
Aliases[29]=(Command="Speak Team|Button bVoiceTalk|OnRelease SpeakLast",Alias=ToggleTeamChat)<br />
Aliases[30]=(Command="Speak Local|Button bVoiceTalk|OnRelease SpeakLast",Alias=ToggleLocalChat)<br />
Aliases[31]=(Command="Speak Public|Button bVoiceTalk|OnRelease SpeakLast",Alias=TogglePublicChat)<br />
Aliases[32]=(Command="Axis aBaseX SpeedBase=100.0 DeadZone=0.1",Alias="SpaceFighter_JoyX")<br />
Aliases[33]=(Command="Axis aLookUp SpeedBase=100.0 DeadZone=0.1",Alias="SpaceFighter_JoyY")<br />
Aliases[34]=(Command="Axis aUp Speed=+300.0 | Axis aStrafe SpeedBase=300.0 DeadZone=0.1",Alias="SpaceFighter_JoyV")<br />
Aliases[35]=(Command="Axis aBaseY SpeedBase=300.0 DeadZone=0.1 Invert=-1",Alias="SpaceFighter_JoySlider1")<br />
Aliases[36]=(Command="",Alias=None)<br />
Aliases[37]=(Command="",Alias=None)<br />
Aliases[38]=(Command="",Alias=None)<br />
Aliases[39]=(Command="",Alias=None)<br />
<br />
0=SwitchWeapon 0<br />
1=SwitchWeapon 1<br />
2=SwitchWeapon 2<br />
3=SwitchWeapon 3<br />
4=SwitchWeapon 4<br />
5=SwitchWeapon 5<br />
6=SwitchWeapon 6<br />
7=SwitchWeapon 7<br />
8=SwitchWeapon 8<br />
9=SwitchWeapon 9<br />
<br />
F1=ShowScores<br />
F2=InGameChat<br />
F3=ShowStats<br />
F4=ToggleBehindView<br />
F5=ToggleFreeCam<br />
F6=Stat Net<br />
F7=ToggleFreeCamSwivel<br />
F8=NextStats<br />
F9=shot<br />
F10=Cancel<br />
F11=MusicMenu<br />
F12=ToggleRadarMap<br />
F13=<br />
F14=<br />
F15=<br />
F16=<br />
F17=<br />
F18=<br />
F19=<br />
F20=<br />
F21=<br />
F22=<br />
F23=<br />
F24=AltFire<br />
<br />
A=StrafeLeft<br />
B=SwitchToLastWeapon<br />
C=Duck<br />
D=StrafeRight<br />
E=Use<br />
F=VoiceTalk<br />
G=SwitchToBestWeapon<br />
H=<br />
I=ToggleTeamChat<br />
j=Taunt pthrust<br />
K=Taunt asssmack<br />
L=Taunt throatcut<br />
M=BasePath 0<br />
N=BasePath 1<br />
O=ToggleLocalChat<br />
P=TogglePublicChat<br />
Q=SwitchWeapon 10<br />
R=TeamTalk<br />
S=MoveBackward<br />
T=Talk<br />
U=Use<br />
V=SpeechMenuToggle<br />
W=MoveForward<br />
X=ShowVoteMenu<br />
Y=writetolog<br />
Z=Strafe<br />
<br />
Alt=<br />
Attn=<br />
Backslash=ThrowWeapon<br />
Backspace=Jump<br />
Cancel=<br />
CapsLock=LookToggle<br />
Comma=StrafeLeft<br />
CrSel=<br />
Ctrl=Jump<br />
Delete=LookUp<br />
Down=MoveBackward<br />
End=CenterView|ACTOR ALIGN SNAPTOFLOOR ALIGN=1<br />
Enter=Use<br />
Equals=GrowHUD<br />
Escape=ShowMenu<br />
ErEof=<br />
Execute=<br />
ExSel=<br />
GreySlash=<br />
GreyStar=<br />
GreyMinus=<br />
GreyPlus=<br />
Help=<br />
home=<br />
Insert=<br />
LControl=<br />
Left=StrafeLeft<br />
LeftBracket=InventoryPrevious<br />
LShift=<br />
Minus=ShrinkHUD<br />
NoName=<br />
None=<br />
OemClear=<br />
PA1=<br />
PageDown=LookDown<br />
PageUp=<br />
Pause=Pause<br />
Period=StrafeRight<br />
Play=<br />
Print=<br />
PrintScrn=<br />
RControl=<br />
Right=StrafeRight<br />
RightBracket=InventoryNext<br />
RShift=<br />
ScrollLock=<br />
Select=<br />
Semicolon=Taunt gesture_point<br />
Separator=<br />
Shift=Duck<br />
SingleQuote=Strafe<br />
Slash=NextWeapon<br />
Space=Jump<br />
Tab=Type<br />
Tilde=ConsoleToggle<br />
Up=MoveForward<br />
Zoom=<br />
<br />
LeftMouse=Fire<br />
MiddleMouse=MoveForward<br />
RightMouse=AltFire<br />
MouseWheelDown=NextWeapon<br />
MouseWheelUp=PrevWeapon<br />
MouseX=Count bXAxis | Axis aMouseX Speed=2.0<br />
MouseY=Count bYAxis | Axis aMouseY Speed=2.0<br />
MouseZ=<br />
<br />
NumLock=<br />
NumPad7=StrafeLeft<br />
NumPad8=MoveForward<br />
NumPad9=StrafeRight<br />
NumPadPeriod=<br />
<br />
Joy1=Fire<br />
Joy2=Jump<br />
Joy3=AltFire<br />
Joy4=Duck<br />
Joy5=NextWeapon<br />
Joy6=SwitchWeapon 2<br />
Joy7=SwitchWeapon 3<br />
Joy8=SwitchWeapon 4<br />
Joy9=SwitchWeapon 9<br />
Joy10=SwitchWeapon 0<br />
Joy11=InventoryPrevious<br />
Joy12=InventoryActivate<br />
Joy13=MoveForward<br />
Joy14=StrafeRight<br />
Joy15=MoveBackward<br />
Joy16=StrafeLeft<br />
JoyX=Axis aStrafe SpeedBase=300.0 DeadZone=0.1<br />
JoyY=Axis aBaseY SpeedBase=300.0 DeadZone=0.1 Invert=-1<br />
JoyZ=<br />
JoyR=<br />
JoyU=<br />
JoyV=Axis aBaseX SpeedBase=2.0 DeadZone=0.4<br />
JoySlider1=Axis aLookUp SpeedBase=2.0 DeadZone=0.4<br />
JoySlider2=</nowiki></div>Rejechthttps://wiki.beyondunreal.com/Key_Bindings_(UT2004)?diff=46038Key Bindings (UT2004)2014-08-01T21:38:47Z<p>Rejecht: </p>
<hr />
<div>Source: DefUser.ini (UT2004v3369)<br />
<br />
<nowiki>'''[Engine.Input]'''<br />
Aliases[0]=(Command="Button bFire | Fire",Alias=Fire)<br />
Aliases[1]=(Command="Button bAltFire | AltFire",Alias=AltFire)<br />
Aliases[2]=(Command="Axis aBaseY Speed=+300.0",Alias=MoveForward)<br />
Aliases[3]=(Command="Axis aBaseY Speed=-300.0",Alias=MoveBackward)<br />
Aliases[4]=(Command="Axis aBaseX Speed=-40.0",Alias=TurnLeft)<br />
Aliases[5]=(Command="Axis aBaseX Speed=+40.0",Alias=TurnRight)<br />
Aliases[6]=(Command="Axis aStrafe Speed=-300.0",Alias=StrafeLeft)<br />
Aliases[7]=(Command="Axis aStrafe Speed=+300.0",Alias=StrafeRight)<br />
Aliases[8]=(Command="Jump | Axis aUp Speed=+300.0",Alias=Jump)<br />
Aliases[9]=(Command="Button bDuck | Axis aUp Speed=-300.0",Alias=Duck)<br />
Aliases[10]=(Command="Button bLook",Alias=Look)<br />
Aliases[11]=(Command="Toggle bLook",Alias=LookToggle)<br />
Aliases[12]=(Command="ActivateItem",Alias=InventoryActivate)<br />
Aliases[13]=(Command="NextItem",Alias=InventoryNext)<br />
Aliases[14]=(Command="PrevItem",Alias=InventoryPrevious)<br />
Aliases[15]=(Command="Axis aLookUp Speed=+25.0",Alias=LookUp)<br />
Aliases[16]=(Command="Axis aLookUp Speed=-25.0",Alias=LookDown)<br />
Aliases[17]=(Command="Button bSnapLevel",Alias=CenterView)<br />
Aliases[18]=(Command="Button bRun",Alias=Walking)<br />
Aliases[19]=(Command="Button bStrafe",Alias=Strafe)<br />
Aliases[20]=(Command="NextWeapon",Alias=NextWeapon)<br />
Aliases[21]=(Command="Button bFreeLook",Alias=FreeLook)<br />
Aliases[22]=(Command="ViewClass Pawn",Alias=ViewTeam)<br />
Aliases[23]=(Command="Button bTurnToNearest",Alias=TurnToNearest)<br />
Aliases[24]=(Command="Button bTurn180",Alias=Turn180)<br />
Aliases[25]=(Command="ShowScores|OnRelease ShowScores",Alias=ScoreToggle)<br />
Aliases[26]=(Command="InGameChat",Alias=InGameChat)<br />
Aliases[27]=(Command="ServerInfo",Alias=ServerInfo)<br />
Aliases[28]=(Command="Button bVoiceTalk",Alias=VoiceTalk)<br />
Aliases[29]=(Command="Speak Team|Button bVoiceTalk|OnRelease SpeakLast",Alias=ToggleTeamChat)<br />
Aliases[30]=(Command="Speak Local|Button bVoiceTalk|OnRelease SpeakLast",Alias=ToggleLocalChat)<br />
Aliases[31]=(Command="Speak Public|Button bVoiceTalk|OnRelease SpeakLast",Alias=TogglePublicChat)<br />
Aliases[32]=(Command="Axis aBaseX SpeedBase=100.0 DeadZone=0.1",Alias="SpaceFighter_JoyX")<br />
Aliases[33]=(Command="Axis aLookUp SpeedBase=100.0 DeadZone=0.1",Alias="SpaceFighter_JoyY")<br />
Aliases[34]=(Command="Axis aUp Speed=+300.0 | Axis aStrafe SpeedBase=300.0 DeadZone=0.1",Alias="SpaceFighter_JoyV")<br />
Aliases[35]=(Command="Axis aBaseY SpeedBase=300.0 DeadZone=0.1 Invert=-1",Alias="SpaceFighter_JoySlider1")<br />
Aliases[36]=(Command="",Alias=None)<br />
Aliases[37]=(Command="",Alias=None)<br />
Aliases[38]=(Command="",Alias=None)<br />
Aliases[39]=(Command="",Alias=None)<br />
<br />
0=SwitchWeapon 0<br />
1=SwitchWeapon 1<br />
2=SwitchWeapon 2<br />
3=SwitchWeapon 3<br />
4=SwitchWeapon 4<br />
5=SwitchWeapon 5<br />
6=SwitchWeapon 6<br />
7=SwitchWeapon 7<br />
8=SwitchWeapon 8<br />
9=SwitchWeapon 9<br />
<br />
F1=ShowScores<br />
F2=InGameChat<br />
F3=ShowStats<br />
F4=ToggleBehindView<br />
F5=ToggleFreeCam<br />
F6=Stat Net<br />
F7=ToggleFreeCamSwivel<br />
F8=NextStats<br />
F9=shot<br />
F10=Cancel<br />
F11=MusicMenu<br />
F12=ToggleRadarMap<br />
F13=<br />
F14=<br />
F15=<br />
F16=<br />
F17=<br />
F18=<br />
F19=<br />
F20=<br />
F21=<br />
F22=<br />
F23=<br />
F24=AltFire<br />
<br />
A=StrafeLeft<br />
B=SwitchToLastWeapon<br />
C=Duck<br />
D=StrafeRight<br />
E=Use<br />
F=VoiceTalk<br />
G=SwitchToBestWeapon<br />
H=<br />
I=ToggleTeamChat<br />
j=Taunt pthrust<br />
K=Taunt asssmack<br />
L=Taunt throatcut<br />
M=BasePath 0<br />
N=BasePath 1<br />
O=ToggleLocalChat<br />
P=TogglePublicChat<br />
Q=SwitchWeapon 10<br />
R=TeamTalk<br />
S=MoveBackward<br />
T=Talk<br />
U=Use<br />
V=SpeechMenuToggle<br />
W=MoveForward<br />
X=ShowVoteMenu<br />
Y=writetolog<br />
Z=Strafe<br />
<br />
Alt=<br />
Attn=<br />
Backslash=ThrowWeapon<br />
Backspace=Jump<br />
Cancel=<br />
CapsLock=LookToggle<br />
Comma=StrafeLeft<br />
CrSel=<br />
Ctrl=Jump<br />
Delete=LookUp<br />
Down=MoveBackward<br />
End=CenterView|ACTOR ALIGN SNAPTOFLOOR ALIGN=1<br />
Enter=Use<br />
Equals=GrowHUD<br />
Escape=ShowMenu<br />
ErEof=<br />
Execute=<br />
ExSel=<br />
GreySlash=<br />
GreyStar=<br />
GreyMinus=<br />
GreyPlus=<br />
Help=<br />
home=<br />
Insert=<br />
LControl=<br />
Left=StrafeLeft<br />
LeftBracket=InventoryPrevious<br />
LShift=<br />
Minus=ShrinkHUD<br />
NoName=<br />
None=<br />
OemClear=<br />
PA1=<br />
PageDown=LookDown<br />
PageUp=<br />
Pause=Pause<br />
Period=StrafeRight<br />
Play=<br />
Print=<br />
PrintScrn=<br />
RControl=<br />
Right=StrafeRight<br />
RightBracket=InventoryNext<br />
RShift=<br />
ScrollLock=<br />
Select=<br />
Semicolon=Taunt gesture_point<br />
Separator=<br />
Shift=Duck<br />
SingleQuote=Strafe<br />
Slash=NextWeapon<br />
Space=Jump<br />
Tab=Type<br />
Tilde=ConsoleToggle<br />
Up=MoveForward<br />
Zoom=<br />
<br />
LeftMouse=Fire<br />
MiddleMouse=MoveForward<br />
RightMouse=AltFire<br />
MouseWheelDown=NextWeapon<br />
MouseWheelUp=PrevWeapon<br />
MouseX=Count bXAxis | Axis aMouseX Speed=2.0<br />
MouseY=Count bYAxis | Axis aMouseY Speed=2.0<br />
MouseZ=<br />
<br />
NumLock=<br />
NumPad7=StrafeLeft<br />
NumPad8=MoveForward<br />
NumPad9=StrafeRight<br />
NumPadPeriod=<br />
<br />
Joy1=Fire<br />
Joy2=Jump<br />
Joy3=AltFire<br />
Joy4=Duck<br />
Joy5=NextWeapon<br />
Joy6=SwitchWeapon 2<br />
Joy7=SwitchWeapon 3<br />
Joy8=SwitchWeapon 4<br />
Joy9=SwitchWeapon 9<br />
Joy10=SwitchWeapon 0<br />
Joy11=InventoryPrevious<br />
Joy12=InventoryActivate<br />
Joy13=MoveForward<br />
Joy14=StrafeRight<br />
Joy15=MoveBackward<br />
Joy16=StrafeLeft<br />
JoyX=Axis aStrafe SpeedBase=300.0 DeadZone=0.1<br />
JoyY=Axis aBaseY SpeedBase=300.0 DeadZone=0.1 Invert=-1<br />
JoyZ=<br />
JoyR=<br />
JoyU=<br />
JoyV=Axis aBaseX SpeedBase=2.0 DeadZone=0.4<br />
JoySlider1=Axis aLookUp SpeedBase=2.0 DeadZone=0.4<br />
JoySlider2=</nowiki></div>Rejechthttps://wiki.beyondunreal.com/Talk:Key_Bindings_(UT2004)?diff=46037Talk:Key Bindings (UT2004)2014-08-01T21:35:18Z<p>Rejecht: </p>
<hr />
<div>What's the purpose of this page? It's an orphan and its information doesn't quite look like it'd be useful for editing directly. On the UnrealWiki you'd probably expect a DefUser.ini dump instead, while the pretty-colored textual representation is more a thing for Liandri Archives. —[[User:Wormbo|Wormbo]] 20:06, 1 August 2014 (UTC)<br />
<br />
Not sure where to link this in, and nevermind the stylish stuff. I basically wanted to have a page to refer to when it comes to key bindings. I'll keep the current version under my user page and replace this one with a preliminary DefUser.ini copy/paste for now. --[[User:Rejecht|Rejecht]] 21:35, 1 August 2014 (UTC)</div>Rejechthttps://wiki.beyondunreal.com/Key_Bindings_(UT2004)?diff=46035Key Bindings (UT2004)2014-08-01T18:48:56Z<p>Rejecht: Created page with 'Note: Based on a Norwegian keyboard layout thus showing æ, ø (oslash), å (aring) and TDLR (I assume "tødler"). {| class="wikitable" style="text-align: center; background-col…'</p>
<hr />
<div>Note: Based on a Norwegian keyboard layout thus showing æ, ø (oslash), å (aring) and TDLR (I assume "tødler").<br />
<br />
{| class="wikitable" style="text-align: center; background-color: #0B3B6A; color: #FFB900; font-weight: bold;"<br />
! Action<br />
! Key 1<br />
! Key 2<br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Movement<br />
|-<br />
| Forward<br />
| W<br />
| Up<br />
|-<br />
| Backward<br />
| S<br />
| Down<br />
|-<br />
| Strafe Left<br />
| A<br />
| Left<br />
|-<br />
| Strafe Right<br />
| D<br />
| Right<br />
|-<br />
| Jump<br />
| CTRL<br />
| Space<br />
|-<br />
| Walk<br />
| <br />
| <br />
|-<br />
| Crouch<br />
| C<br />
| Shift<br />
|-<br />
| Strafe Toggle<br />
| Z<br />
| æ<br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Looking<br />
|-<br />
| Turn Left<br />
| <br />
| <br />
|-<br />
| Turn Right<br />
| <br />
| <br />
|-<br />
| Look Up<br />
| Delete<br />
| <br />
|-<br />
| Look Down<br />
| Page Down<br />
| <br />
|-<br />
| Center View<br />
| <br />
| <br />
|-<br />
| Toggle "BehindView"<br />
| F4<br />
| <br />
|-<br />
| Toggle Camera Mode<br />
| F5<br />
| <br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Weapons<br />
|-<br />
| Fire<br />
| Left Mouse Button<br />
| Joy 1<br />
|-<br />
| Alt-Fire<br />
| Right Mouse Button<br />
| F24<br />
|-<br />
| Throw Weapon<br />
| |<br />
| <br />
|-<br />
| Best Weapon<br />
| G<br />
| <br />
|-<br />
| Next Weapon<br />
| Mouse Wheel Down<br />
| '<br />
|-<br />
| Previous Weapon<br />
| Mouse Wheel Up<br />
| <br />
|-<br />
| Last Weapon<br />
| B<br />
| <br />
|-<br />
| Weapon Selection<br />
| <br />
| <br />
|-<br />
| Super Weapon<br />
| 0<br />
| Joy 10<br />
|-<br />
| Shield Gun<br />
| 1<br />
| <br />
|-<br />
| Assault Rifle<br />
| 2<br />
| Joy 6<br />
|-<br />
| Bio-Rifle<br />
| 3<br />
| Joy 7<br />
|-<br />
| Shock Rifle<br />
| 4<br />
| Joy 8<br />
|-<br />
| Link Gun<br />
| 5<br />
| <br />
|-<br />
| Minigun<br />
| 6<br />
| <br />
|-<br />
| Flak Cannon<br />
| 7<br />
| <br />
|-<br />
| Rocket Launcher<br />
| 8<br />
| <br />
|-<br />
| Lightning Rifle<br />
| 9<br />
| Joy 9<br />
|-<br />
| Translocator<br />
| Q<br />
| <br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Communication<br />
|-<br />
| Say<br />
| T<br />
| <br />
|-<br />
| Team Say<br />
| R<br />
| <br />
|-<br />
| In-Game Chat<br />
| F2<br />
| <br />
|-<br />
| Toggle Speech Menu<br />
| V<br />
| <br />
|-<br />
| Activate Microphone<br />
| F<br />
| <br />
|-<br />
| Speak in Public Channel<br />
| <br />
| <br />
|-<br />
| Speak in Local Channel<br />
| <br />
| <br />
|-<br />
| Speak in Team Channel<br />
| <br />
| <br />
|-<br />
| Toggle Public Channel<br />
| P<br />
| <br />
|-<br />
| Toggle Local Channel<br />
| O<br />
| <br />
|-<br />
| Toggle Team Channel<br />
| I<br />
| <br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Taunts<br />
|-<br />
| Pelvic Thrust<br />
| J<br />
| <br />
|-<br />
| Ass Smack<br />
| K<br />
| <br />
|-<br />
| Throat Cut<br />
| L<br />
| <br />
|-<br />
| Brag<br />
| "TDLR"<br />
| <br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | HUD<br />
|-<br />
| Grow HUD<br />
| +<br />
| <br />
|-<br />
| Shrink HUD<br />
| -<br />
| <br />
|-<br />
| Toggle Radar Map<br />
| F12<br />
| <br />
|-<br />
| Toggle Scoreboard<br />
| F1<br />
| <br />
|-<br />
| Scoreboard<br />
| <br />
| <br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Game<br />
|-<br />
| Use<br />
| E<br />
| U<br />
|-<br />
| Pause<br />
| Pause<br />
| <br />
|-<br />
| Take Screen Shot<br />
| F9<br />
| <br />
|-<br />
| Find Red Base<br />
| M<br />
| <br />
|-<br />
| Find Blue Base<br />
| N<br />
| <br />
|-<br />
| Next Inventory Item<br />
| å<br />
| <br />
|-<br />
| Previous Inventory Item<br />
| Joy 11<br />
| \<br />
|-<br />
| Activate Current Inventory Item<br />
| Joy 12<br />
| <br />
|-<br />
| Show Personal Stats<br />
| F3<br />
| <br />
|-<br />
| View Next Player's Stats<br />
| F8<br />
| <br />
|-<br />
| Server Info<br />
| <br />
| <br />
|-<br />
| Vehicle Horn<br />
| <br />
| <br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Miscellaneous<br />
|-<br />
| Toggle Menu<br />
| Escape<br />
| <br />
|-<br />
| Music Player<br />
| F11<br />
| <br />
|-<br />
| Voting Menu<br />
| X<br />
| <br />
|-<br />
| Toggle Console<br />
| ø<br />
| <br />
|-<br />
| Toggle Connection Status<br />
| F6<br />
| <br />
|-<br />
| Cancel Pending Connection<br />
| F10<br />
| <br />
|}</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=46034User:Rejecht2014-07-31T17:55:30Z<p>Rejecht: Removed forums, added link to article</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Articles ===<br />
<br />
[[User:Rejecht/Key_Bindings_(UT2004)]]<br />
<br />
=== Where ===<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Countless Hours Wasted ===<br />
<br />
==== Demo Recording & Playback ====<br />
<br />
# Playing back a demo recorded in a networked game results in several text messages being duplicated. For example, orders sent to bots, so it was never a bug in the mod, but my lack of awareness of this issue in the first place.<br />
<br />
=== Nice to know ===<br />
<br />
==== ClientFlash does nothing ====<br />
<br />
(It's used to fade all screen colors from a starting color.)<br />
<br />
Check these dependencies:<br />
#ScreenFlashes in UT2004.ini must be True for the driver you use.<br />
#Check (ScreenFlashScaling > 0.0f) in User.ini.<br />
<br />
If the settings check out, check if something prevents PlayerController.ViewFlash () from getting called.<br />
<br />
==== Having no pawn is the same as being in a Dead state ====<br />
<br />
The game has a hard-coded client-side rule that forces the player to enter a dead state when it does not find a Pawn.<br />
<br />
Triggers:<br />
* [NM_Standalone|NM_ListenServer|NM_Client] (Pawn == None)<br />
<br />
Cause:<br />
* PlayerController.(PlayerWalking)PlayerMove ()<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Servers do not know the locations of spectators ====<br />
<br />
Spectators are in control of the location, and do not get sneaky state information, nor do they send location information to the server in the same way as a player with a pawn does.<br />
<br />
The sources for UT3 1.5 try to solve this problem with ServerSetSpectatorLocation, however, this function can't possibly work as intended, as it does not update the position when the camera moves, but rather sets the last position and stays there. To track the camera, use PlayerCalcView, and throttle calls to your own ServerSetSpectatorLocation from the client (no need to call each tick). You also need to make sure PlayerMove (movement keys) does not offset the view from where the camera is, unless the player has freed the camera.<br />
<br />
It is assumed that this has something to do with sounds not playing in the right location, but not yet proven.<br />
<br />
==== xPawn.ChangedWeapon breaks simulated function chain ====<br />
<br />
Pawn.ChangedWeapon is declared simulated, however, in the derived xPawn class, it is redeclared non-simulated while still being called from simulated functions, thus breaking the chain until the Pawn becomes ROLE_AutonomousProxy, which may take some time in netplay.<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
Also note that this is a bad real-world example, because all channel values are always odd numbers. For example, if the red channel value is 50, the color code becomes 51. See the second example for the real world use of such a function, or you care about accuracy.<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript><br />
<br />
For the sake of completeness, a version that applies to the real world, using Max to keep the value within the allowed range [1, 255].<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Max (Color.R, 1))<br />
$ Chr (Max (Color.G, 1))<br />
$ Chr (Max (Color.B, 1))<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht/Key_Bindings_(UT2004)?diff=46033User:Rejecht/Key Bindings (UT2004)2014-07-31T15:04:52Z<p>Rejecht: Created page with '{| class="wikitable" style="text-align: center; background-color: #0B3B6A; color: #FFB900; font-weight: bold;" ! Action ! Key 1 ! Key 2 |- | style="background-color: #18184A; tex…'</p>
<hr />
<div>{| class="wikitable" style="text-align: center; background-color: #0B3B6A; color: #FFB900; font-weight: bold;"<br />
! Action<br />
! Key 1<br />
! Key 2<br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Movement<br />
|-<br />
| Forward<br />
| W<br />
| Up<br />
|-<br />
| Backward<br />
| S<br />
| Down<br />
|-<br />
| Strafe Left<br />
| A<br />
| Left<br />
|-<br />
| Strafe Right<br />
| D<br />
| Right<br />
|-<br />
| Jump<br />
| CTRL<br />
| Space<br />
|-<br />
| Walk<br />
| <br />
| <br />
|-<br />
| Crouch<br />
| C<br />
| Shift<br />
|-<br />
| Strafe Toggle<br />
| Z<br />
| æ<br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Looking<br />
|-<br />
| Turn Left<br />
| <br />
| <br />
|-<br />
| Turn Right<br />
| <br />
| <br />
|-<br />
| Look Up<br />
| Delete<br />
| <br />
|-<br />
| Look Down<br />
| Page Down<br />
| <br />
|-<br />
| Center View<br />
| <br />
| <br />
|-<br />
| Toggle "BehindView"<br />
| F4<br />
| <br />
|-<br />
| Toggle Camera Mode<br />
| F5<br />
| <br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Weapons<br />
|-<br />
| Fire<br />
| Left Mouse Button<br />
| Joy 1<br />
|-<br />
| Alt-Fire<br />
| Right Mouse Button<br />
| F24<br />
|-<br />
| Throw Weapon<br />
| |<br />
| <br />
|-<br />
| Best Weapon<br />
| G<br />
| <br />
|-<br />
| Next Weapon<br />
| Mouse Wheel Down<br />
| '<br />
|-<br />
| Previous Weapon<br />
| Mouse Wheel Up<br />
| <br />
|-<br />
| Last Weapon<br />
| B<br />
| <br />
|-<br />
| Weapon Selection<br />
| <br />
| <br />
|-<br />
| Super Weapon<br />
| 0<br />
| Joy 10<br />
|-<br />
| Shield Gun<br />
| 1<br />
| <br />
|-<br />
| Assault Rifle<br />
| 2<br />
| Joy 6<br />
|-<br />
| Bio-Rifle<br />
| 3<br />
| Joy 7<br />
|-<br />
| Shock Rifle<br />
| 4<br />
| Joy 8<br />
|-<br />
| Link Gun<br />
| 5<br />
| <br />
|-<br />
| Minigun<br />
| 6<br />
| <br />
|-<br />
| Flak Cannon<br />
| 7<br />
| <br />
|-<br />
| Rocket Launcher<br />
| 8<br />
| <br />
|-<br />
| Lightning Rifle<br />
| 9<br />
| Joy 9<br />
|-<br />
| Translocator<br />
| Q<br />
| <br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Communication<br />
|-<br />
| Say<br />
| T<br />
| <br />
|-<br />
| Team Say<br />
| R<br />
| <br />
|-<br />
| In-Game Chat<br />
| F2<br />
| <br />
|-<br />
| Toggle Speech Menu<br />
| V<br />
| <br />
|-<br />
| Activate Microphone<br />
| F<br />
| <br />
|-<br />
| Speak in Public Channel<br />
| <br />
| <br />
|-<br />
| Speak in Local Channel<br />
| <br />
| <br />
|-<br />
| Speak in Team Channel<br />
| <br />
| <br />
|-<br />
| Toggle Public Channel<br />
| P<br />
| <br />
|-<br />
| Toggle Local Channel<br />
| O<br />
| <br />
|-<br />
| Toggle Team Channel<br />
| I<br />
| <br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Taunts<br />
|-<br />
| Pelvic Thrust<br />
| J<br />
| <br />
|-<br />
| Ass Smack<br />
| K<br />
| <br />
|-<br />
| Throat Cut<br />
| L<br />
| <br />
|-<br />
| Brag<br />
| "TDLR"<br />
| <br />
|-<br />
| colspan="3" | HUD<br />
|-<br />
| Grow HUD<br />
| +<br />
| <br />
|-<br />
| Shrink HUD<br />
| -<br />
| <br />
|-<br />
| Toggle Radar Map<br />
| F12<br />
| <br />
|-<br />
| Toggle Scoreboard<br />
| F1<br />
| <br />
|-<br />
| Scoreboard<br />
| <br />
| <br />
|-<br />
| colspan="3" | Game<br />
|-<br />
| Use<br />
| E<br />
| U<br />
|-<br />
| Pause<br />
| Pause<br />
| <br />
|-<br />
| Take Screen Shot<br />
| F9<br />
| <br />
|-<br />
| Find Red Base<br />
| M<br />
| <br />
|-<br />
| Find Blue Base<br />
| N<br />
| <br />
|-<br />
| Next Inventory Item<br />
| å<br />
| <br />
|-<br />
| Previous Inventory Item<br />
| Joy 11<br />
| \<br />
|-<br />
| Activate Current Inventory Item<br />
| Joy 12<br />
| <br />
|-<br />
| Show Personal Stats<br />
| F3<br />
| <br />
|-<br />
| View Next Player's Stats<br />
| F8<br />
| <br />
|-<br />
| Server Info<br />
| <br />
| <br />
|-<br />
| Vehicle Horn<br />
| <br />
| <br />
|-<br />
| style="background-color: #18184A; text-align: left;" colspan="3" | Miscellaneous<br />
|-<br />
| Toggle Menu<br />
| Escape<br />
| <br />
|-<br />
| Music Player<br />
| F11<br />
| <br />
|-<br />
| Voting Menu<br />
| X<br />
| <br />
|-<br />
| Toggle Console<br />
| ø<br />
| <br />
|-<br />
| Toggle Connection Status<br />
| F6<br />
| <br />
|-<br />
| Cancel Pending Connection<br />
| F10<br />
| <br />
|}</div>Rejechthttps://wiki.beyondunreal.com/UE2:FileLog_(UT2004)?diff=46029UE2:FileLog (UT2004)2014-06-30T19:06:17Z<p>Rejecht: Added Warning</p>
<hr />
<div>{{infobox class<br />
| package = Engine<br />
| parent1 = Info<br />
| parent2 = Actor<br />
| parent3 = Object<br />
}}<br />
{{autogenerated}}<br />
Class: Engine.FileLog<br />
Parent: Engine.Info<br />
<br />
Creates a log device.<br />
Important notes about this class since version 2225:<br />
- the log file is always closed when destroyed<br />
- open log files have the extention .tmp and change to .log when<br />
closed<br />
- old .tmp files will be overwritten<br />
- limited freedom in file extentions, allowed extentions:<br />
log, txt, html, htm<br />
<br />
==Properties==<br />
====LogAr====<br />
'''Type:''' [[pointer]]<br />
<br />
<!-- enter variable description --><br />
<br />
====LogFileName====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' const<br />
<br />
<!-- enter variable description --><br />
<br />
====TempFileName====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' const<br />
<br />
<!-- enter variable description --><br />
<br />
==Native functions==<br />
====CloseLog====<br />
{{code|native final function '''CloseLog''' ()}}<br />
<br />
<!-- enter function description --><br />
<br />
====Logf====<br />
{{code|native final function '''Logf''' ([[string]]&nbsp;'''LogString''')}}<br />
<br />
Writes LogString to the file.<br />
<br />
'''Warning''': This function will crash the instance--or possibly corrupt memory enough to cause a crash on exit or garbage collection--if a string is longer than 1024 characters or somewhere above that number. Your script should work around this issue by splitting the LogString up before passing it to this function.<br />
<br />
====OpenLog====<br />
{{code|native final function '''OpenLog''' ([[string]]&nbsp;'''FName''', optional&nbsp;[[string]]&nbsp;'''FExt''', optional&nbsp;[[bool]]&nbsp;'''bOverwrite''')}}<br />
<br />
<!-- enter function description --></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45915User:Rejecht2013-12-25T13:05:52Z<p>Rejecht: /* ClientFlash does nothing */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Countless Hours Wasted ===<br />
<br />
==== Demo Recording & Playback ====<br />
<br />
# Playing back a demo recorded in a networked game results in several text messages being duplicated. For example, orders sent to bots, so it was never a bug in the mod, but my lack of awareness of this issue in the first place.<br />
<br />
=== Nice to know ===<br />
<br />
==== ClientFlash does nothing ====<br />
<br />
(It's used to fade all screen colors from a starting color.)<br />
<br />
Check these dependencies:<br />
#ScreenFlashes in UT2004.ini must be True for the driver you use.<br />
#Check (ScreenFlashScaling > 0.0f) in User.ini.<br />
<br />
If the settings check out, check if something prevents PlayerController.ViewFlash () from getting called.<br />
<br />
==== Having no pawn is the same as being in a Dead state ====<br />
<br />
The game has a hard-coded client-side rule that forces the player to enter a dead state when it does not find a Pawn.<br />
<br />
Triggers:<br />
* [NM_Standalone|NM_ListenServer|NM_Client] (Pawn == None)<br />
<br />
Cause:<br />
* PlayerController.(PlayerWalking)PlayerMove ()<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Servers do not know the locations of spectators ====<br />
<br />
Spectators are in control of the location, and do not get sneaky state information, nor do they send location information to the server in the same way as a player with a pawn does.<br />
<br />
The sources for UT3 1.5 try to solve this problem with ServerSetSpectatorLocation, however, this function can't possibly work as intended, as it does not update the position when the camera moves, but rather sets the last position and stays there. To track the camera, use PlayerCalcView, and throttle calls to your own ServerSetSpectatorLocation from the client (no need to call each tick). You also need to make sure PlayerMove (movement keys) does not offset the view from where the camera is, unless the player has freed the camera.<br />
<br />
It is assumed that this has something to do with sounds not playing in the right location, but not yet proven.<br />
<br />
==== xPawn.ChangedWeapon breaks simulated function chain ====<br />
<br />
Pawn.ChangedWeapon is declared simulated, however, in the derived xPawn class, it is redeclared non-simulated while still being called from simulated functions, thus breaking the chain until the Pawn becomes ROLE_AutonomousProxy, which may take some time in netplay.<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
Also note that this is a bad real-world example, because all channel values are always odd numbers. For example, if the red channel value is 50, the color code becomes 51. See the second example for the real world use of such a function, or you care about accuracy.<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript><br />
<br />
For the sake of completeness, a version that applies to the real world, using Max to keep the value within the allowed range [1, 255].<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Max (Color.R, 1))<br />
$ Chr (Max (Color.G, 1))<br />
$ Chr (Max (Color.B, 1))<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45914User:Rejecht2013-12-25T13:04:53Z<p>Rejecht: /* Having no pawn is the same as being in a Dead state */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Countless Hours Wasted ===<br />
<br />
==== Demo Recording & Playback ====<br />
<br />
# Playing back a demo recorded in a networked game results in several text messages being duplicated. For example, orders sent to bots, so it was never a bug in the mod, but my lack of awareness of this issue in the first place.<br />
<br />
=== Nice to know ===<br />
<br />
==== ClientFlash does nothing ====<br />
<br />
(It's used to fade all screen colors from a starting color.)<br />
<br />
Check these dependencies:<br />
1. ScreenFlashes in UT2004.ini must be True for the driver you use.<br />
2. Check (ScreenFlashScaling > 0.0f) in User.ini.<br />
<br />
If the settings check out, check if something prevents PlayerController.ViewFlash () from getting called.<br />
<br />
==== Having no pawn is the same as being in a Dead state ====<br />
<br />
The game has a hard-coded client-side rule that forces the player to enter a dead state when it does not find a Pawn.<br />
<br />
Triggers:<br />
* [NM_Standalone|NM_ListenServer|NM_Client] (Pawn == None)<br />
<br />
Cause:<br />
* PlayerController.(PlayerWalking)PlayerMove ()<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Servers do not know the locations of spectators ====<br />
<br />
Spectators are in control of the location, and do not get sneaky state information, nor do they send location information to the server in the same way as a player with a pawn does.<br />
<br />
The sources for UT3 1.5 try to solve this problem with ServerSetSpectatorLocation, however, this function can't possibly work as intended, as it does not update the position when the camera moves, but rather sets the last position and stays there. To track the camera, use PlayerCalcView, and throttle calls to your own ServerSetSpectatorLocation from the client (no need to call each tick). You also need to make sure PlayerMove (movement keys) does not offset the view from where the camera is, unless the player has freed the camera.<br />
<br />
It is assumed that this has something to do with sounds not playing in the right location, but not yet proven.<br />
<br />
==== xPawn.ChangedWeapon breaks simulated function chain ====<br />
<br />
Pawn.ChangedWeapon is declared simulated, however, in the derived xPawn class, it is redeclared non-simulated while still being called from simulated functions, thus breaking the chain until the Pawn becomes ROLE_AutonomousProxy, which may take some time in netplay.<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
Also note that this is a bad real-world example, because all channel values are always odd numbers. For example, if the red channel value is 50, the color code becomes 51. See the second example for the real world use of such a function, or you care about accuracy.<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript><br />
<br />
For the sake of completeness, a version that applies to the real world, using Max to keep the value within the allowed range [1, 255].<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Max (Color.R, 1))<br />
$ Chr (Max (Color.G, 1))<br />
$ Chr (Max (Color.B, 1))<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45529User:Rejecht2013-11-03T10:46:30Z<p>Rejecht: /* xPawn.ChangedWeapon breaks simulated function chain */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Countless Hours Wasted ===<br />
<br />
==== Demo Recording & Playback ====<br />
<br />
# Playing back a demo recorded in a networked game results in several text messages being duplicated. For example, orders sent to bots, so it was never a bug in the mod, but my lack of awareness of this issue in the first place.<br />
<br />
=== Nice to know ===<br />
<br />
==== Having no pawn is the same as being in a Dead state ====<br />
<br />
The game has a hard-coded client-side rule that forces the player to enter a dead state when it does not find a Pawn.<br />
<br />
Triggers:<br />
* [NM_Standalone|NM_ListenServer|NM_Client] (Pawn == None)<br />
<br />
Cause:<br />
* PlayerController.(PlayerWalking)PlayerMove ()<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Servers do not know the locations of spectators ====<br />
<br />
Spectators are in control of the location, and do not get sneaky state information, nor do they send location information to the server in the same way as a player with a pawn does.<br />
<br />
The sources for UT3 1.5 try to solve this problem with ServerSetSpectatorLocation, however, this function can't possibly work as intended, as it does not update the position when the camera moves, but rather sets the last position and stays there. To track the camera, use PlayerCalcView, and throttle calls to your own ServerSetSpectatorLocation from the client (no need to call each tick). You also need to make sure PlayerMove (movement keys) does not offset the view from where the camera is, unless the player has freed the camera.<br />
<br />
It is assumed that this has something to do with sounds not playing in the right location, but not yet proven.<br />
<br />
==== xPawn.ChangedWeapon breaks simulated function chain ====<br />
<br />
Pawn.ChangedWeapon is declared simulated, however, in the derived xPawn class, it is redeclared non-simulated while still being called from simulated functions, thus breaking the chain until the Pawn becomes ROLE_AutonomousProxy, which may take some time in netplay.<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
Also note that this is a bad real-world example, because all channel values are always odd numbers. For example, if the red channel value is 50, the color code becomes 51. See the second example for the real world use of such a function, or you care about accuracy.<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript><br />
<br />
For the sake of completeness, a version that applies to the real world, using Max to keep the value within the allowed range [1, 255].<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Max (Color.R, 1))<br />
$ Chr (Max (Color.G, 1))<br />
$ Chr (Max (Color.B, 1))<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45528User:Rejecht2013-11-03T10:38:45Z<p>Rejecht: /* NumLives is not the number of lives */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Countless Hours Wasted ===<br />
<br />
==== Demo Recording & Playback ====<br />
<br />
# Playing back a demo recorded in a networked game results in several text messages being duplicated. For example, orders sent to bots, so it was never a bug in the mod, but my lack of awareness of this issue in the first place.<br />
<br />
=== Nice to know ===<br />
<br />
==== Having no pawn is the same as being in a Dead state ====<br />
<br />
The game has a hard-coded client-side rule that forces the player to enter a dead state when it does not find a Pawn.<br />
<br />
Triggers:<br />
* [NM_Standalone|NM_ListenServer|NM_Client] (Pawn == None)<br />
<br />
Cause:<br />
* PlayerController.(PlayerWalking)PlayerMove ()<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== xPawn.ChangedWeapon breaks simulated function chain ====<br />
<br />
Pawn.ChangedWeapon is declared simulated, however, in the derived xPawn class, it is redeclared non-simulated while still being called from simulated functions, thus breaking the chain until the Pawn becomes ROLE_AutonomousProxy, which may take some time in netplay.<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
Also note that this is a bad real-world example, because all channel values are always odd numbers. For example, if the red channel value is 50, the color code becomes 51. See the second example for the real world use of such a function, or you care about accuracy.<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript><br />
<br />
For the sake of completeness, a version that applies to the real world, using Max to keep the value within the allowed range [1, 255].<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Max (Color.R, 1))<br />
$ Chr (Max (Color.G, 1))<br />
$ Chr (Max (Color.B, 1))<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45527User:Rejecht2013-11-03T10:35:41Z<p>Rejecht: /* Players who are out can sometimes respawn */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Countless Hours Wasted ===<br />
<br />
==== Demo Recording & Playback ====<br />
<br />
# Playing back a demo recorded in a networked game results in several text messages being duplicated. For example, orders sent to bots, so it was never a bug in the mod, but my lack of awareness of this issue in the first place.<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== xPawn.ChangedWeapon breaks simulated function chain ====<br />
<br />
Pawn.ChangedWeapon is declared simulated, however, in the derived xPawn class, it is redeclared non-simulated while still being called from simulated functions, thus breaking the chain until the Pawn becomes ROLE_AutonomousProxy, which may take some time in netplay.<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
Also note that this is a bad real-world example, because all channel values are always odd numbers. For example, if the red channel value is 50, the color code becomes 51. See the second example for the real world use of such a function, or you care about accuracy.<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript><br />
<br />
For the sake of completeness, a version that applies to the real world, using Max to keep the value within the allowed range [1, 255].<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Max (Color.R, 1))<br />
$ Chr (Max (Color.G, 1))<br />
$ Chr (Max (Color.B, 1))<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45369User:Rejecht2013-04-13T11:19:49Z<p>Rejecht: /* Countless Hours Wasted */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Countless Hours Wasted ===<br />
<br />
==== Demo Recording & Playback ====<br />
<br />
# Playing back a demo recorded in a networked game results in several text messages being duplicated. For example, orders sent to bots, so it was never a bug in the mod, but my lack of awareness of this issue in the first place.<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
Also note that this is a bad real-world example, because all channel values are always odd numbers. For example, if the red channel value is 50, the color code becomes 51. See the second example for the real world use of such a function, or you care about accuracy.<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript><br />
<br />
For the sake of completeness, a version that applies to the real world, using Max to keep the value within the allowed range [1, 255].<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Max (Color.R, 1))<br />
$ Chr (Max (Color.G, 1))<br />
$ Chr (Max (Color.B, 1))<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45368User:Rejecht2013-04-13T11:19:31Z<p>Rejecht: /* Countless Hours Wasted */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Countless Hours Wasted ===<br />
<br />
# Playing back a demo recorded in a networked game results in several text messages being duplicated. For example, orders sent to bots, so it was never a bug in the mod, but my lack of awareness of this issue in the first place.<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
Also note that this is a bad real-world example, because all channel values are always odd numbers. For example, if the red channel value is 50, the color code becomes 51. See the second example for the real world use of such a function, or you care about accuracy.<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript><br />
<br />
For the sake of completeness, a version that applies to the real world, using Max to keep the value within the allowed range [1, 255].<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Max (Color.R, 1))<br />
$ Chr (Max (Color.G, 1))<br />
$ Chr (Max (Color.B, 1))<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45367User:Rejecht2013-04-13T11:18:48Z<p>Rejecht: </p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Countless Hours Wasted ===<br />
<br />
# Playing back a demo recorded in a networked game results in several text messages being duplicated. For example, orders sent to bots.<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
Also note that this is a bad real-world example, because all channel values are always odd numbers. For example, if the red channel value is 50, the color code becomes 51. See the second example for the real world use of such a function, or you care about accuracy.<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript><br />
<br />
For the sake of completeness, a version that applies to the real world, using Max to keep the value within the allowed range [1, 255].<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Max (Color.R, 1))<br />
$ Chr (Max (Color.G, 1))<br />
$ Chr (Max (Color.B, 1))<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45317User:Rejecht2013-02-24T16:23:08Z<p>Rejecht: Using a bitwise or operator: Added clarification and real-world application</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
Also note that this is a bad real-world example, because all channel values are always odd numbers. For example, if the red channel value is 50, the color code becomes 51. See the second example for the real world use of such a function, or you care about accuracy.<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript><br />
<br />
For the sake of completeness, a version that applies to the real world, using Max to keep the value within the allowed range [1, 255].<br />
<br />
<uscript>function string GetText_ColorCode (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Max (Color.R, 1))<br />
$ Chr (Max (Color.G, 1))<br />
$ Chr (Max (Color.B, 1))<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45217User:Rejecht2012-10-15T18:28:05Z<p>Rejecht: /* Added: Example: Using a bitwise and operator III */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise and operator III===<br />
To show that you are not testing against the bit number, but a value. You have to compare using powers of two.<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
Index = 255;<br />
<br />
if ((Index & 128) == 128) // Test if bit #7 (2^7 = 128) is set. You have to compare and check using powers of two.<br />
{<br />
Log ("["$Index$"] Set"); // Bit #7 was set<br />
}<br />
else<br />
{<br />
Log ("["$Index$"] Cleared"); // Bit #7 was cleared<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [255] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45216User:Rejecht2012-10-15T18:23:16Z<p>Rejecht: /* Example: Using a bitwise and operator II */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set. You have to compare using powers of two.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45214User:Rejecht2012-10-07T16:21:13Z<p>Rejecht: /* About */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
<br />
== Mods ==<br />
<br />
An overview of mods (game types, maps, mutators) can be found on [https://sites.google.com/site/rejecht/ rejecht's notes].<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45213User:Rejecht2012-10-07T16:13:13Z<p>Rejecht: /* About */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions since about 1992.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
* rejecht on [http://www.moddb.com/members/rejecht ModDB]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45188User:Rejecht2012-08-05T05:11:36Z<p>Rejecht: /* ImportText && "Bad termination in:" */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== General Protection Fault ====<br />
<br />
History:<br />
# FScriptCompiler::CompileConst<br />
# <- Const<br />
# <- FScriptCompiler::CompileDeclaration<br />
# <- FScriptCompiler::CompileStatement<br />
# <- FirstPass<br />
# <- TryCompile<br />
# <- FScriptCompiler::CompileScript<br />
# <- (Class PackageName.ClassName, Pass 0, Line %Line_Number%)<br />
# <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript <- MakeScript<br />
# <- DoScripts<br />
# <- UEditorEngine::MakeScripts<br />
# <- UMakeCommandlet::Main<br />
<br />
Cause:<br />
* Declaring the same variable name as const value twice.<br />
<br />
File content example:<br />
<UScript><br />
class Example extends Actor;<br />
<br />
const crash = 1;<br />
const crash = 2; // Cause of crash.</UScript><br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45187User:Rejecht2012-08-05T04:47:46Z<p>Rejecht: /* ImportText && "Bad termination in:" */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
* The compiler probably uses a static text buffer of 4096 characters per line.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45186User:Rejecht2012-08-05T04:43:20Z<p>Rejecht: /* ImportText && "Bad termination in:" */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
<br />
Solutions:<br />
# Partition up the dynamic array into groups of dynamic arrays (acceptable).<br />
# Change over to a static array - replace '''Count = ArrayVariableName.Length''' with '''Count = ArrayCount (ArrayVariableName)'''.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45185User:Rejecht2012-08-05T04:38:26Z<p>Rejecht: /* ImportText && "Bad termination in:" */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as '''"Bad termination in:"''' at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45184User:Rejecht2012-08-05T04:37:41Z<p>Rejecht: /* ImportText && "Bad termination in:" */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as [["Bad termination in:"]] at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
* Elements in a static array are serialized to a single line per element.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45183User:Rejecht2012-08-05T04:33:49Z<p>Rejecht: /* Unreal Tournament 2004 Bugs || Errors */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 - Compiler Errors ===<br />
<br />
==== ImportText && "Bad termination in:" ====<br />
<br />
Problem:<br />
* A dynamic array with a lot of elements will not be stored correctly in the resulting int file.<br />
<br />
Cause:<br />
* The compiler cuts off the dynamic array after 4096 characters. After the 4096th character, the int file will contain garbage. This is in turn logged by '''ImportText''' upon accessing the dynamic array as [["Bad termination in:"]] at runtime.<br />
<br />
Trivia:<br />
* Elements in a dynamic array are serialized to a single line in an int file.<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45122User:Rejecht2012-05-27T07:10:33Z<p>Rejecht: /* Tips */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
==Tips==<br />
<br />
===Example: Using a bitwise and operator===<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
===Example: Using a bitwise and operator II===<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
===Example: Using a bitwise or operator===<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45121User:Rejecht2012-05-27T07:09:56Z<p>Rejecht: /* Tips */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
=Tips=<br />
<br />
==Example: Using a bitwise and operator==<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
==Example: Using a bitwise and operator II==<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
==Example: Using a bitwise or operator==<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45120User:Rejecht2012-05-27T07:09:19Z<p>Rejecht: /* (...) more to come. */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
= Tips =<br />
<br />
==Example: Using a bitwise and operator==<br />
<br />
Theory: You can check if a number is even {0, 2, 4, ...} or odd (1, 3, 5, ...) by testing the least significant binary digit of an integer (two's complement). If it's 0, this is an even number (0, 2, 4, ...). If it's 1, this is an odd number {1, 3, 5, ...}.<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 4; ++Index)<br />
{<br />
if ((Index & 1) == 0) // Test if the least significant bit is cleared.<br />
{<br />
Log ("["$Index$"] Even");<br />
}<br />
<br />
if ((Index & 1) == 1) // Test if the least significant bit is set.<br />
{<br />
Log ("["$Index$"] Odd");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [0] Even<br />
// ScriptLog: [1] Odd<br />
// ScriptLog: [2] Even<br />
// ScriptLog: [3] Odd</uscript><br />
<br />
==Example: Using a bitwise and operator II==<br />
<br />
<uscript>function Example ()<br />
{<br />
local int Index;<br />
<br />
for (Index = 0; Index < 8; ++Index)<br />
{<br />
if ((Index & 2) == 2) // Test if bit #2 is set.<br />
{<br />
Log ("["$Index$"] Set");<br />
}<br />
}<br />
}<br />
<br />
// Output:<br />
// ScriptLog: [2] Set<br />
// ScriptLog: [3] Set<br />
// ScriptLog: [6] Set<br />
// ScriptLog: [7] Set</uscript><br />
<br />
==Example: Using a bitwise or operator==<br />
<br />
Theory: The color-coded text strings have the format 0x1B (ANSI Escape Code) followed by three bytes representing the red, green, and blue channels, where each channel must be in the range [1, 255] because the value 0 (NUL) terminates the string. The function in this example takes a color and uses the bitwise or operator to make sure that the value of each color channel is always at least 1. Note that there is no alpha channel (the string is opaque). Note that StrLen counts color codes.<br />
<br />
<uscript>function string GetTextColorCoded (Color Color)<br />
{<br />
return<br />
(<br />
Chr (0x1B)<br />
$ Chr (Color.R | 1)<br />
$ Chr (Color.G | 1)<br />
$ Chr (Color.B | 1)<br />
);<br />
}</uscript></div>Rejechthttps://wiki.beyondunreal.com/UE2:GameInfo_properties_(UT2004)?diff=45104UE2:GameInfo properties (UT2004)2012-04-26T11:12:43Z<p>Rejecht: /* TimeLimit */</p>
<hr />
<div>{{infobox class part<br />
| class = GameInfo<br />
| part = properties<br />
| parent1 = Info<br />
| parent2 = Actor<br />
| parent3 = Object<br />
}}<br />
{{autogenerated}}<br />
==Properties==<br />
===Property group 'GameInfo'===<br />
====AccessControlClass====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Engine.AccessControl"<br />
<br />
====Acronym====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "???"<br />
<br />
====BroadcastClass====<br />
'''Type:''' [[class]]<{{cl|BroadcastHandler}}><br />
<br />
<!-- enter variable description --><br />
<br />
====BroadcastHandlerClass====<br />
'''Type:''' [[string]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Engine.BroadcastHandler"<br />
<br />
====CurrentGameProfile====<br />
'''Type:''' {{cl|GameProfile}}<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' transient<br />
<br />
<!-- enter variable description --><br />
<br />
====DecoTextName====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
deprecated ?<br />
<br />
====Description====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache, localized<br />
<br />
<!-- enter variable description --><br />
<br />
====GameName====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache, localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Game"<br />
<br />
====GameReplicationInfoClass====<br />
'''Type:''' [[class]]<{{cl|GameReplicationInfo}}><br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' [[Types#Class|Class]]'{{cl|GameReplicationInfo|Engine}}'<br />
<br />
====ResetCountDown====<br />
'''Type:''' [[int]]<br />
<br />
<!-- enter variable description --><br />
<br />
====ResetTimeDelay====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
time (seconds) before restarting teams<br />
<br />
====SaveGameManifest====<br />
'''Type:''' {{cl|Manifest}}<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' private, const, transient<br />
<br />
<!-- enter variable description --><br />
<br />
====ScreenShotName====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
<!-- enter variable description --><br />
<br />
====VoiceReplicationInfoClass====<br />
'''Type:''' [[class]]<{{cl|VoiceChatReplicationInfo}}><br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' [[Types#Class|Class]]'{{cl|VoiceChatReplicationInfo|Engine}}'<br />
<br />
===Internal variables===<br />
====AccessControl====<br />
'''Type:''' {{cl|AccessControl}}<br />
<br />
AccessControl controls whether players can enter and/or become admins<br />
<br />
====AutoAim====<br />
'''Type:''' [[float]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' 0.93<br />
<br />
====bAdminCanPause====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====bAllowBehindView====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====bAllowMPGameSpeed====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bAllowVehicles====<br />
'''Type:''' [[bool]]<br />
<br />
are vehicles allowed in this gametype?<br />
<br />
====bAllowWeaponThrowing====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' True<br />
<br />
====bAlreadyChanged====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bAlternateMode====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
====BaseMutator====<br />
'''Type:''' {{cl|Mutator}}<br />
<br />
linked list of Mutators (for modifying actors as they enter the game)<br />
<br />
====bAttractCam====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bCanChangeSkin====<br />
'''Type:''' [[bool]]<br />
<br />
Allow player to change skins in game.<br />
<br />
'''Default value:''' True<br />
<br />
====bCanViewOthers====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' True<br />
<br />
====bChangeLevels====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' True<br />
<br />
====bDelayedStart====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' True<br />
<br />
====BeaconName====<br />
'''Type:''' [[string]]<br />
<br />
Identifying string used for finding LAN servers.<br />
<br />
====bEnableStatLogging====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
If True, games will log<br />
<br />
====bForceClassicView====<br />
'''Type:''' [[bool]]<br />
<br />
OBSOLETE<br />
<br />
====bGameEnded====<br />
'''Type:''' [[bool]]<br />
<br />
set when game ends<br />
<br />
====bGameRestarted====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====BindColor====<br />
'''Type:''' {{tl|Color|Object|structs}}<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' <br />
{| class="list defaults"<br />
! {{tl|Color||Object structs|Member}}<br />
! Value<br />
|-<br />
| A<br />
| 255<br />
|-<br />
| G<br />
| 128<br />
|-<br />
| R<br />
| 128<br />
|}<br />
<br />
====bIsSaveGame====<br />
'''Type:''' [[bool]]<br />
<br />
stays true during entire game (unlike LevelInfo property)<br />
<br />
====bLargeGameVOIP====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====bLiberalVehiclePaths====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bLoggingGame====<br />
'''Type:''' [[bool]]<br />
<br />
Does this gametype log?<br />
<br />
====bLowGore====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
OBSOLETE - use GoreLevel instead;<br />
<br />
====bModViewShake====<br />
'''Type:''' [[bool]]<br />
<br />
for mutators to turn off weaponviewshake<br />
<br />
'''Default value:''' True<br />
<br />
====bMustJoinBeforeStart====<br />
'''Type:''' [[bool]]<br />
<br />
players can only spectate if they join after the game starts<br />
<br />
====BotMenuType====<br />
'''Type:''' [[string]]<br />
<br />
Type of bot menu to display. -NOT USED-<br />
<br />
====bOverTime====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bPauseable====<br />
'''Type:''' [[bool]]<br />
<br />
Whether the game is pauseable.<br />
<br />
'''Default value:''' True<br />
<br />
====bRestartLevel====<br />
'''Type:''' [[bool]]<br />
<br />
Level should be restarted when player dies<br />
<br />
'''Default value:''' True<br />
<br />
====BroadcastHandler====<br />
'''Type:''' {{cl|BroadcastHandler}}<br />
<br />
handles message (text and localized) broadcasts<br />
<br />
====bTeamGame====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
This is a team game.<br />
<br />
====bTestMode====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bWaitingToStartMatch====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bWeaponShouldViewShake====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' True<br />
<br />
====bWeaponStay====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
Whether or not weapons stay when picked up.<br />
<br />
====bWelcomePending====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====CallSigns====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Static arrays|Array size]]:''' 15<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value, index 0:''' "ALPHA"<br />
<br />
'''Default value, index 10:''' "KILO"<br />
<br />
'''Default value, index 11:''' "LIMA"<br />
<br />
'''Default value, index 12:''' "MIKE"<br />
<br />
'''Default value, index 13:''' "NOVEMBER"<br />
<br />
'''Default value, index 14:''' "OSCAR"<br />
<br />
'''Default value, index 15:''' "BRAVO"<br />
<br />
'''Default value, index 16:''' "CHARLIE"<br />
<br />
'''Default value, index 17:''' "DELTA"<br />
<br />
'''Default value, index 18:''' "ECHO"<br />
<br />
'''Default value, index 19:''' "FOXTROT"<br />
<br />
'''Default value, index 20:''' "GOLF"<br />
<br />
'''Default value, index 21:''' "HOTEL"<br />
<br />
'''Default value, index 22:''' "INDIA"<br />
<br />
'''Default value, index 23:''' "JULIET"<br />
<br />
====CurrentID====<br />
'''Type:''' [[int]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' 1<br />
<br />
====DeathMessageClass====<br />
'''Type:''' [[class]]<{{cl|LocalMessage}}><br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' [[Types#Class|Class]]'{{cl|LocalMessage|Engine}}'<br />
<br />
====DefaultPlayerClassName====<br />
'''Type:''' [[string]]<br />
<br />
<!-- enter variable description --><br />
<br />
====DefaultPlayerName====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Player"<br />
<br />
====DemoCommand====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' transient<br />
<br />
<!-- enter variable description --><br />
<br />
====FearCostFallOff====<br />
'''Type:''' [[float]]<br />
<br />
how fast the FearCost in NavigationPoints falls off<br />
<br />
'''Default value:''' 0.95<br />
<br />
====GameDifficulty====<br />
'''Type:''' [[float]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' 1.0<br />
<br />
====GameMessageClass====<br />
'''Type:''' [[class]]<{{cl|GameMessage}}><br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' [[Types#Class|Class]]'{{cl|GameMessage|Engine}}'<br />
<br />
====GameOptionsMenuType====<br />
'''Type:''' [[string]]<br />
<br />
Type of options dropdown to display. -NOT USED-<br />
<br />
====GameReplicationInfo====<br />
'''Type:''' {{cl|GameReplicationInfo}}<br />
<br />
<!-- enter variable description --><br />
<br />
====GameRulesModifiers====<br />
'''Type:''' {{cl|GameRules}}<br />
<br />
linked list of modifier classes which affect game rules<br />
<br />
====GameSpeed====<br />
'''Type:''' [[float]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
Scale applied to game rate.<br />
<br />
'''Default value:''' 1.0<br />
<br />
====GameStats====<br />
'''Type:''' {{cl|GameStats}}<br />
<br />
Holds the GameStats actor<br />
<br />
====GameStatsClass====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====GameUMenuType====<br />
'''Type:''' [[string]]<br />
<br />
Type of Game dropdown to display. (Used as map menu in Onslaught)<br />
<br />
====GIPropDescText====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Static arrays|Array size]]:''' 15 (<code>{{tl|GIPROPNUM||GameInfo}}</code>)<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value, index 0:''' "Set the skill of your bot opponents."<br />
<br />
'''Default value, index 1:''' "When enabled, weapons will always be available for pickup."<br />
<br />
'''Default value, index 10:''' "When enabled, players can throw their current weapon out."<br />
<br />
'''Default value, index 11:''' "Controls whether players can switch to a third person view."<br />
<br />
'''Default value, index 12:''' "Controls whether administrators can pause the game."<br />
<br />
'''Default value, index 13:''' "Specifies how long to wait before kicking idle player from server."<br />
<br />
'''Default value, index 14:''' "When enabled, some weapons cause view shaking while firing."<br />
<br />
'''Default value, index 15:''' "Enable this option to reduce the amount of blood and guts you see."<br />
<br />
'''Default value, index 16:''' "Controls how fast time passes in the game."<br />
<br />
'''Default value, index 17:''' "Sets the maximum number of spectators that can watch the game."<br />
<br />
'''Default value, index 18:''' "Sets the maximum number of players that can join this server."<br />
<br />
'''Default value, index 19:''' "The game ends when someone reaches this score."<br />
<br />
'''Default value, index 20:''' "Limits how many times players can respawn after dying."<br />
<br />
'''Default value, index 21:''' "The game ends after this many minutes of play."<br />
<br />
'''Default value, index 22:''' "Enable this option to send game statistics to the UT2004 global stats server"<br />
<br />
====GIPropsDisplayText====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Static arrays|Array size]]:''' 15 (<code>{{tl|GIPROPNUM||GameInfo}}</code>)<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value, index 0:''' "Bot Skill"<br />
<br />
'''Default value, index 1:''' "Weapons Stay"<br />
<br />
'''Default value, index 10:''' "Allow Weapon Throwing"<br />
<br />
'''Default value, index 11:''' "Allow Behind View"<br />
<br />
'''Default value, index 12:''' "Allow Admin Pausing"<br />
<br />
'''Default value, index 13:''' "Kick Idlers Time"<br />
<br />
'''Default value, index 14:''' "Weapons shake view"<br />
<br />
'''Default value, index 15:''' "Reduce Gore Level"<br />
<br />
'''Default value, index 16:''' "Game Speed"<br />
<br />
'''Default value, index 17:''' "Max Spectators"<br />
<br />
'''Default value, index 18:''' "Max Players"<br />
<br />
'''Default value, index 19:''' "Goal Score"<br />
<br />
'''Default value, index 20:''' "Max Lives"<br />
<br />
'''Default value, index 21:''' "Time Limit"<br />
<br />
'''Default value, index 22:''' "World Stats Logging"<br />
<br />
====GIPropsExtras====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Static arrays|Array size]]:''' 2<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "0.000000;Novice;1.000000;Average;2.000000;Experienced;3.000000;Skilled;4.000000;Adept;5.000000;Masterful;6.000000;Inhuman;7.000000;Godlike"<br />
<br />
====GoalScore====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
what score is needed to end the match<br />
<br />
====GoreLevel====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' 2<br />
<br />
====GoreLevelText====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Static arrays|Array size]]:''' 3<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value, index 0:''' "No Gore"<br />
<br />
'''Default value, index 1:''' "Reduced Gore"<br />
<br />
'''Default value, index 2:''' "Full Gore"<br />
<br />
====HUDSettingsMenu====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
Optional GUI page for configuring HUD options specific to this gametype<br />
<br />
====HUDType====<br />
'''Type:''' [[string]]<br />
<br />
HUD class this game uses.<br />
<br />
'''Default value:''' "Engine.HUD"<br />
<br />
====MaplistHandler====<br />
'''Type:''' {{cl|MaplistManagerBase}}<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' transient<br />
<br />
<!-- enter variable description --><br />
<br />
====MaplistHandlerClass====<br />
'''Type:''' [[class]]<{{cl|MaplistManagerBase}}><br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' [[Types#Class|Class]]'{{cl|MaplistManager|Engine}}'<br />
<br />
====MaplistHandlerType====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====MapListType====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
Maplist this game uses.<br />
<br />
====MapPrefix====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
Prefix characters for names of maps for this game type.<br />
<br />
====MaxIdleTime====<br />
'''Type:''' [[float]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====MaxLives====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
max number of lives for match, unless overruled by level's GameDetails<br />
<br />
====MaxPlayers====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' 16<br />
<br />
====MaxSpectators====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
Maximum number of spectators.<br />
<br />
'''Default value:''' 2<br />
<br />
====MultiplayerUMenuType====<br />
'''Type:''' [[string]]<br />
<br />
Type of Multiplayer dropdown to display. -NOT USED-<br />
<br />
====MutatorClass====<br />
'''Type:''' [[string]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Engine.Mutator"<br />
<br />
====NoBindString====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "[None]"<br />
<br />
====NumBots====<br />
'''Type:''' [[int]]<br />
<br />
number of non-human players (AI controlled but participating as a player)<br />
<br />
====NumPlayers====<br />
'''Type:''' [[int]]<br />
<br />
number of human players<br />
<br />
====NumSpectators====<br />
'''Type:''' [[int]]<br />
<br />
Current number of spectators.<br />
<br />
====OtherMesgGroup====<br />
'''Type:''' [[name]]<br />
<br />
<!-- enter variable description --><br />
<br />
====PlayerControllerClass====<br />
'''Type:''' [[class]]<{{cl|PlayerController}}><br />
<br />
type of player controller to spawn for players logging in<br />
<br />
====PlayerControllerClassName====<br />
'''Type:''' [[string]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Engine.PlayerController"<br />
<br />
====RulesMenuType====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
Type of rules menu to display.<br />
<br />
====ScoreBoardType====<br />
'''Type:''' [[string]]<br />
<br />
Type of class<Menu> to use for scoreboards. (gam)<br />
<br />
====SecurityClass====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====ServerSkillLevel====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
The Server Skill Level ( 0 - 2, Beginner - Advanced )<br />
<br />
====SettingsMenuType====<br />
'''Type:''' [[string]]<br />
<br />
Type of settings menu to display. -NOT USED-<br />
<br />
====StartTime====<br />
'''Type:''' [[float]]<br />
<br />
<!-- enter variable description --><br />
<br />
====TimeLimit====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
TimeLimit has three contexts.<br />
<br />
# (TimeLimit < 0) -> TimeLimit behaviour is not defined.<br />
# (TimeLimit == 0) -> TimeLimit is not enforced and thus RemainingTime is never decremented and thus TimeLimit is not an end-game criteria.<br />
# (TimeLimit > 0) -> TimeLimit represents the number of minutes [1, 999] and is used to initialize RemainingTime (seconds) which is then decremented every second.<br />
<br />
TimeLimit is affected by:<br />
* (DefaultMaxLives > 0) -> TimeLimit = 0<br />
* ((GoalScore == 0) && (TimeLimit == 0)) -> TimeLimit = 20 -- See the DeathMatch.CheckReady function.<br />
<br />
TimeLimit affects:<br />
* (TimeLimit > 0) --> --RemainingTime<br />
<br />
TimeLimit triggers:<br />
* EndGame ("TimeLimit")<br />
<br />
====VehicleList====<br />
'''Type:''' {{cl|Vehicle}}<br />
<br />
<!-- enter variable description --><br />
<br />
====VoiceReplicationInfo====<br />
'''Type:''' {{cl|VoiceChatReplicationInfo}}<br />
<br />
<!-- enter variable description --><br />
<br />
====VotingHandler====<br />
'''Type:''' {{cl|VotingHandler}}<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' transient<br />
<br />
<!-- enter variable description --><br />
<br />
====VotingHandlerClass====<br />
'''Type:''' [[class]]<{{cl|VotingHandler}}><br />
<br />
<!-- enter variable description --><br />
<br />
====VotingHandlerType====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "xVoting.xVotingHandler"</div>Rejechthttps://wiki.beyondunreal.com/UE2:GameInfo_properties_(UT2004)?diff=45103UE2:GameInfo properties (UT2004)2012-04-26T11:05:58Z<p>Rejecht: /* TimeLimit */</p>
<hr />
<div>{{infobox class part<br />
| class = GameInfo<br />
| part = properties<br />
| parent1 = Info<br />
| parent2 = Actor<br />
| parent3 = Object<br />
}}<br />
{{autogenerated}}<br />
==Properties==<br />
===Property group 'GameInfo'===<br />
====AccessControlClass====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Engine.AccessControl"<br />
<br />
====Acronym====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "???"<br />
<br />
====BroadcastClass====<br />
'''Type:''' [[class]]<{{cl|BroadcastHandler}}><br />
<br />
<!-- enter variable description --><br />
<br />
====BroadcastHandlerClass====<br />
'''Type:''' [[string]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Engine.BroadcastHandler"<br />
<br />
====CurrentGameProfile====<br />
'''Type:''' {{cl|GameProfile}}<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' transient<br />
<br />
<!-- enter variable description --><br />
<br />
====DecoTextName====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
deprecated ?<br />
<br />
====Description====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache, localized<br />
<br />
<!-- enter variable description --><br />
<br />
====GameName====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache, localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Game"<br />
<br />
====GameReplicationInfoClass====<br />
'''Type:''' [[class]]<{{cl|GameReplicationInfo}}><br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' [[Types#Class|Class]]'{{cl|GameReplicationInfo|Engine}}'<br />
<br />
====ResetCountDown====<br />
'''Type:''' [[int]]<br />
<br />
<!-- enter variable description --><br />
<br />
====ResetTimeDelay====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
time (seconds) before restarting teams<br />
<br />
====SaveGameManifest====<br />
'''Type:''' {{cl|Manifest}}<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' private, const, transient<br />
<br />
<!-- enter variable description --><br />
<br />
====ScreenShotName====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
<!-- enter variable description --><br />
<br />
====VoiceReplicationInfoClass====<br />
'''Type:''' [[class]]<{{cl|VoiceChatReplicationInfo}}><br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' [[Types#Class|Class]]'{{cl|VoiceChatReplicationInfo|Engine}}'<br />
<br />
===Internal variables===<br />
====AccessControl====<br />
'''Type:''' {{cl|AccessControl}}<br />
<br />
AccessControl controls whether players can enter and/or become admins<br />
<br />
====AutoAim====<br />
'''Type:''' [[float]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' 0.93<br />
<br />
====bAdminCanPause====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====bAllowBehindView====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====bAllowMPGameSpeed====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bAllowVehicles====<br />
'''Type:''' [[bool]]<br />
<br />
are vehicles allowed in this gametype?<br />
<br />
====bAllowWeaponThrowing====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' True<br />
<br />
====bAlreadyChanged====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bAlternateMode====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
====BaseMutator====<br />
'''Type:''' {{cl|Mutator}}<br />
<br />
linked list of Mutators (for modifying actors as they enter the game)<br />
<br />
====bAttractCam====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bCanChangeSkin====<br />
'''Type:''' [[bool]]<br />
<br />
Allow player to change skins in game.<br />
<br />
'''Default value:''' True<br />
<br />
====bCanViewOthers====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' True<br />
<br />
====bChangeLevels====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' True<br />
<br />
====bDelayedStart====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' True<br />
<br />
====BeaconName====<br />
'''Type:''' [[string]]<br />
<br />
Identifying string used for finding LAN servers.<br />
<br />
====bEnableStatLogging====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
If True, games will log<br />
<br />
====bForceClassicView====<br />
'''Type:''' [[bool]]<br />
<br />
OBSOLETE<br />
<br />
====bGameEnded====<br />
'''Type:''' [[bool]]<br />
<br />
set when game ends<br />
<br />
====bGameRestarted====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====BindColor====<br />
'''Type:''' {{tl|Color|Object|structs}}<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' <br />
{| class="list defaults"<br />
! {{tl|Color||Object structs|Member}}<br />
! Value<br />
|-<br />
| A<br />
| 255<br />
|-<br />
| G<br />
| 128<br />
|-<br />
| R<br />
| 128<br />
|}<br />
<br />
====bIsSaveGame====<br />
'''Type:''' [[bool]]<br />
<br />
stays true during entire game (unlike LevelInfo property)<br />
<br />
====bLargeGameVOIP====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====bLiberalVehiclePaths====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bLoggingGame====<br />
'''Type:''' [[bool]]<br />
<br />
Does this gametype log?<br />
<br />
====bLowGore====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
OBSOLETE - use GoreLevel instead;<br />
<br />
====bModViewShake====<br />
'''Type:''' [[bool]]<br />
<br />
for mutators to turn off weaponviewshake<br />
<br />
'''Default value:''' True<br />
<br />
====bMustJoinBeforeStart====<br />
'''Type:''' [[bool]]<br />
<br />
players can only spectate if they join after the game starts<br />
<br />
====BotMenuType====<br />
'''Type:''' [[string]]<br />
<br />
Type of bot menu to display. -NOT USED-<br />
<br />
====bOverTime====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bPauseable====<br />
'''Type:''' [[bool]]<br />
<br />
Whether the game is pauseable.<br />
<br />
'''Default value:''' True<br />
<br />
====bRestartLevel====<br />
'''Type:''' [[bool]]<br />
<br />
Level should be restarted when player dies<br />
<br />
'''Default value:''' True<br />
<br />
====BroadcastHandler====<br />
'''Type:''' {{cl|BroadcastHandler}}<br />
<br />
handles message (text and localized) broadcasts<br />
<br />
====bTeamGame====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
This is a team game.<br />
<br />
====bTestMode====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bWaitingToStartMatch====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====bWeaponShouldViewShake====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' True<br />
<br />
====bWeaponStay====<br />
'''Type:''' [[bool]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
Whether or not weapons stay when picked up.<br />
<br />
====bWelcomePending====<br />
'''Type:''' [[bool]]<br />
<br />
<!-- enter variable description --><br />
<br />
====CallSigns====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Static arrays|Array size]]:''' 15<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value, index 0:''' "ALPHA"<br />
<br />
'''Default value, index 10:''' "KILO"<br />
<br />
'''Default value, index 11:''' "LIMA"<br />
<br />
'''Default value, index 12:''' "MIKE"<br />
<br />
'''Default value, index 13:''' "NOVEMBER"<br />
<br />
'''Default value, index 14:''' "OSCAR"<br />
<br />
'''Default value, index 15:''' "BRAVO"<br />
<br />
'''Default value, index 16:''' "CHARLIE"<br />
<br />
'''Default value, index 17:''' "DELTA"<br />
<br />
'''Default value, index 18:''' "ECHO"<br />
<br />
'''Default value, index 19:''' "FOXTROT"<br />
<br />
'''Default value, index 20:''' "GOLF"<br />
<br />
'''Default value, index 21:''' "HOTEL"<br />
<br />
'''Default value, index 22:''' "INDIA"<br />
<br />
'''Default value, index 23:''' "JULIET"<br />
<br />
====CurrentID====<br />
'''Type:''' [[int]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' 1<br />
<br />
====DeathMessageClass====<br />
'''Type:''' [[class]]<{{cl|LocalMessage}}><br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' [[Types#Class|Class]]'{{cl|LocalMessage|Engine}}'<br />
<br />
====DefaultPlayerClassName====<br />
'''Type:''' [[string]]<br />
<br />
<!-- enter variable description --><br />
<br />
====DefaultPlayerName====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Player"<br />
<br />
====DemoCommand====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' transient<br />
<br />
<!-- enter variable description --><br />
<br />
====FearCostFallOff====<br />
'''Type:''' [[float]]<br />
<br />
how fast the FearCost in NavigationPoints falls off<br />
<br />
'''Default value:''' 0.95<br />
<br />
====GameDifficulty====<br />
'''Type:''' [[float]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' 1.0<br />
<br />
====GameMessageClass====<br />
'''Type:''' [[class]]<{{cl|GameMessage}}><br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' [[Types#Class|Class]]'{{cl|GameMessage|Engine}}'<br />
<br />
====GameOptionsMenuType====<br />
'''Type:''' [[string]]<br />
<br />
Type of options dropdown to display. -NOT USED-<br />
<br />
====GameReplicationInfo====<br />
'''Type:''' {{cl|GameReplicationInfo}}<br />
<br />
<!-- enter variable description --><br />
<br />
====GameRulesModifiers====<br />
'''Type:''' {{cl|GameRules}}<br />
<br />
linked list of modifier classes which affect game rules<br />
<br />
====GameSpeed====<br />
'''Type:''' [[float]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
Scale applied to game rate.<br />
<br />
'''Default value:''' 1.0<br />
<br />
====GameStats====<br />
'''Type:''' {{cl|GameStats}}<br />
<br />
Holds the GameStats actor<br />
<br />
====GameStatsClass====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====GameUMenuType====<br />
'''Type:''' [[string]]<br />
<br />
Type of Game dropdown to display. (Used as map menu in Onslaught)<br />
<br />
====GIPropDescText====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Static arrays|Array size]]:''' 15 (<code>{{tl|GIPROPNUM||GameInfo}}</code>)<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value, index 0:''' "Set the skill of your bot opponents."<br />
<br />
'''Default value, index 1:''' "When enabled, weapons will always be available for pickup."<br />
<br />
'''Default value, index 10:''' "When enabled, players can throw their current weapon out."<br />
<br />
'''Default value, index 11:''' "Controls whether players can switch to a third person view."<br />
<br />
'''Default value, index 12:''' "Controls whether administrators can pause the game."<br />
<br />
'''Default value, index 13:''' "Specifies how long to wait before kicking idle player from server."<br />
<br />
'''Default value, index 14:''' "When enabled, some weapons cause view shaking while firing."<br />
<br />
'''Default value, index 15:''' "Enable this option to reduce the amount of blood and guts you see."<br />
<br />
'''Default value, index 16:''' "Controls how fast time passes in the game."<br />
<br />
'''Default value, index 17:''' "Sets the maximum number of spectators that can watch the game."<br />
<br />
'''Default value, index 18:''' "Sets the maximum number of players that can join this server."<br />
<br />
'''Default value, index 19:''' "The game ends when someone reaches this score."<br />
<br />
'''Default value, index 20:''' "Limits how many times players can respawn after dying."<br />
<br />
'''Default value, index 21:''' "The game ends after this many minutes of play."<br />
<br />
'''Default value, index 22:''' "Enable this option to send game statistics to the UT2004 global stats server"<br />
<br />
====GIPropsDisplayText====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Static arrays|Array size]]:''' 15 (<code>{{tl|GIPROPNUM||GameInfo}}</code>)<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value, index 0:''' "Bot Skill"<br />
<br />
'''Default value, index 1:''' "Weapons Stay"<br />
<br />
'''Default value, index 10:''' "Allow Weapon Throwing"<br />
<br />
'''Default value, index 11:''' "Allow Behind View"<br />
<br />
'''Default value, index 12:''' "Allow Admin Pausing"<br />
<br />
'''Default value, index 13:''' "Kick Idlers Time"<br />
<br />
'''Default value, index 14:''' "Weapons shake view"<br />
<br />
'''Default value, index 15:''' "Reduce Gore Level"<br />
<br />
'''Default value, index 16:''' "Game Speed"<br />
<br />
'''Default value, index 17:''' "Max Spectators"<br />
<br />
'''Default value, index 18:''' "Max Players"<br />
<br />
'''Default value, index 19:''' "Goal Score"<br />
<br />
'''Default value, index 20:''' "Max Lives"<br />
<br />
'''Default value, index 21:''' "Time Limit"<br />
<br />
'''Default value, index 22:''' "World Stats Logging"<br />
<br />
====GIPropsExtras====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Static arrays|Array size]]:''' 2<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "0.000000;Novice;1.000000;Average;2.000000;Experienced;3.000000;Skilled;4.000000;Adept;5.000000;Masterful;6.000000;Inhuman;7.000000;Godlike"<br />
<br />
====GoalScore====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
what score is needed to end the match<br />
<br />
====GoreLevel====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' 2<br />
<br />
====GoreLevelText====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Static arrays|Array size]]:''' 3<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value, index 0:''' "No Gore"<br />
<br />
'''Default value, index 1:''' "Reduced Gore"<br />
<br />
'''Default value, index 2:''' "Full Gore"<br />
<br />
====HUDSettingsMenu====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
Optional GUI page for configuring HUD options specific to this gametype<br />
<br />
====HUDType====<br />
'''Type:''' [[string]]<br />
<br />
HUD class this game uses.<br />
<br />
'''Default value:''' "Engine.HUD"<br />
<br />
====MaplistHandler====<br />
'''Type:''' {{cl|MaplistManagerBase}}<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' transient<br />
<br />
<!-- enter variable description --><br />
<br />
====MaplistHandlerClass====<br />
'''Type:''' [[class]]<{{cl|MaplistManagerBase}}><br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' [[Types#Class|Class]]'{{cl|MaplistManager|Engine}}'<br />
<br />
====MaplistHandlerType====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====MapListType====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
Maplist this game uses.<br />
<br />
====MapPrefix====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
Prefix characters for names of maps for this game type.<br />
<br />
====MaxIdleTime====<br />
'''Type:''' [[float]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====MaxLives====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
max number of lives for match, unless overruled by level's GameDetails<br />
<br />
====MaxPlayers====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' 16<br />
<br />
====MaxSpectators====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
Maximum number of spectators.<br />
<br />
'''Default value:''' 2<br />
<br />
====MultiplayerUMenuType====<br />
'''Type:''' [[string]]<br />
<br />
Type of Multiplayer dropdown to display. -NOT USED-<br />
<br />
====MutatorClass====<br />
'''Type:''' [[string]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Engine.Mutator"<br />
<br />
====NoBindString====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' localized<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "[None]"<br />
<br />
====NumBots====<br />
'''Type:''' [[int]]<br />
<br />
number of non-human players (AI controlled but participating as a player)<br />
<br />
====NumPlayers====<br />
'''Type:''' [[int]]<br />
<br />
number of human players<br />
<br />
====NumSpectators====<br />
'''Type:''' [[int]]<br />
<br />
Current number of spectators.<br />
<br />
====OtherMesgGroup====<br />
'''Type:''' [[name]]<br />
<br />
<!-- enter variable description --><br />
<br />
====PlayerControllerClass====<br />
'''Type:''' [[class]]<{{cl|PlayerController}}><br />
<br />
type of player controller to spawn for players logging in<br />
<br />
====PlayerControllerClassName====<br />
'''Type:''' [[string]]<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "Engine.PlayerController"<br />
<br />
====RulesMenuType====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' cache<br />
<br />
Type of rules menu to display.<br />
<br />
====ScoreBoardType====<br />
'''Type:''' [[string]]<br />
<br />
Type of class<Menu> to use for scoreboards. (gam)<br />
<br />
====SecurityClass====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
====ServerSkillLevel====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
The Server Skill Level ( 0 - 2, Beginner - Advanced )<br />
<br />
====SettingsMenuType====<br />
'''Type:''' [[string]]<br />
<br />
Type of settings menu to display. -NOT USED-<br />
<br />
====StartTime====<br />
'''Type:''' [[float]]<br />
<br />
<!-- enter variable description --><br />
<br />
====TimeLimit====<br />
'''Type:''' [[int]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' config<br />
<br />
TimeLimit has three contexts.<br />
<br />
# (TimeLimit < 0) -> TimeLimit behaviour is not defined.<br />
# (TimeLimit == 0) -> TimeLimit is not enforced and thus RemainingTime is never decremented and thus TimeLimit is not an end-game criteria.<br />
# (TimeLimit > 0) -> TimeLimit represents the number of minutes [1, 999] and is used to initialize RemainingTime (seconds) which is then decremented every second.<br />
<br />
TimeLimit is affected by:<br />
* (DefaultMaxLives > 0) -> TimeLimit = 0<br />
* ((GoalScore == 0) && (TimeLimit == 0)) -> TimeLimit = 20 -- See the DeathMatch.CheckReady function.<br />
<br />
TimeLimit affects:<br />
* (TimeLimit > 0) --> --RemainingTime<br />
<br />
TimeLimit triggers:<br />
* CheckEndGame ("TimeLimit")<br />
<br />
====VehicleList====<br />
'''Type:''' {{cl|Vehicle}}<br />
<br />
<!-- enter variable description --><br />
<br />
====VoiceReplicationInfo====<br />
'''Type:''' {{cl|VoiceChatReplicationInfo}}<br />
<br />
<!-- enter variable description --><br />
<br />
====VotingHandler====<br />
'''Type:''' {{cl|VotingHandler}}<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' transient<br />
<br />
<!-- enter variable description --><br />
<br />
====VotingHandlerClass====<br />
'''Type:''' [[class]]<{{cl|VotingHandler}}><br />
<br />
<!-- enter variable description --><br />
<br />
====VotingHandlerType====<br />
'''Type:''' [[string]]<br />
<br />
'''[[Variables#Modifiers|Modifiers]]:''' globalconfig<br />
<br />
<!-- enter variable description --><br />
<br />
'''Default value:''' "xVoting.xVotingHandler"</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45071User:Rejecht2012-03-15T18:11:41Z<p>Rejecht: /* InitTeamSymbols may sometimes give both teams the same team symbol */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Count_TeamSymbols - 1);<br />
Index_TeamSymbol[1] = Rand (Count_TeamSymbols - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
=== (...) more to come. ===</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45064User:Rejecht2012-03-08T22:22:12Z<p>Rejecht: /* PlayTakeHit never played drowning sound */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Group_Text_TeamSymbol.Length - 1);<br />
Index_TeamSymbol[1] = Rand (Group_Text_TeamSymbol.Length - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a metaclass reference (Syntax: Class<Classname>).<br />
* Use IsA with an instance of a class.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
=== (...) more to come. ===</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45063User:Rejecht2012-03-08T22:07:02Z<p>Rejecht: /* Where */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Group_Text_TeamSymbol.Length - 1);<br />
Index_TeamSymbol[1] = Rand (Group_Text_TeamSymbol.Length - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a class object (Class<DamageType> Class_DamageType).<br />
* Use IsA with an instance of a class object.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
=== (...) more to come. ===</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45062User:Rejecht2012-03-08T22:06:23Z<p>Rejecht: /* Where */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
'''Fora'''<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forum]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forum]<br />
* rejecht on [http://www.tcpclan.net/phpforum/profile.php?mode=viewprofile&u=566 The Clan Punishers' Forum]<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forum]<br />
<br />
'''Other'''<br />
<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
'''Websites'''<br />
<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Group_Text_TeamSymbol.Length - 1);<br />
Index_TeamSymbol[1] = Rand (Group_Text_TeamSymbol.Length - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a class object (Class<DamageType> Class_DamageType).<br />
* Use IsA with an instance of a class object.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
=== (...) more to come. ===</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45061User:Rejecht2012-03-08T21:59:50Z<p>Rejecht: /* About */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Where ===<br />
<br />
* rejecht on the [http://forums.beyondunreal.com/member.php?u=59753 Beyond Unreal Forums]<br />
* rejecht on the [http://forums.epicgames.com/members/1358276-rejecht Epic Games Forums]<br />
* rejecht on [https://sites.google.com/site/rejecht/ Google Sites - rejecht's notes]<br />
** rejecht's notes is poorly maintained. It is what it is.<br />
* rejecht on [http://ut2004.titaninternet.co.uk/forums/member.php?u=1901 Titan Onslaught Forums]<br />
* rejecht on [https://twitter.com/#!/rejecht Twitter]<br />
<br />
== Experience ==<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Unreal Tournament 2004 Bugs || Errors ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly. This is by design, but not necessarily desirable.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before calling ScoreKill, since the DeathMatch implementation expects (NumLives >= MaxLives) and sets bOutofLives accordingly.<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Bad: Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Bad: Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
The best solution is to track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this behavior, in case someone actually wants the default behavior.<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
''(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
''<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution is simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Group_Text_TeamSymbol.Length - 1);<br />
Index_TeamSymbol[1] = Rand (Group_Text_TeamSymbol.Length - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Triggers:<br />
* Pawn is in a water volume and the match has entered the MatchOver state.<br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn.<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a class object (Class<DamageType> Class_DamageType).<br />
* Use IsA with an instance of a class object.<br />
<br />
Trivia:<br />
* I don't think the actual drowning sounds were included anyway.<br />
<br />
=== (...) more to come. ===</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45027User:Rejecht2012-03-05T12:48:22Z<p>Rejecht: /* InitTeamSymbols may sometimes give both teams the same team symbol */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Game mechanical errors discovered in Unreal Tournament 2004 ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before or after calling ScoreKill, since it expects (NumLives >= MaxLives).<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Best: Track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this, in case someone actually wants the default behavior.<br />
# Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution was simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
('''Index_MaxTeams''' == 2)<br />
<br />
The (Count_TeamSymbols < 10) check is an early warning system for someone who managed to change the team symbols--this implementation is adapted for at least ten different team symbols.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Group_Text_TeamSymbol.Length - 1);<br />
Index_TeamSymbol[1] = Rand (Group_Text_TeamSymbol.Length - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn .<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a class object (Class<DamageType> Class_DamageType).<br />
* Use IsA with an instance of a class object.<br />
<br />
(...) more to come.</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45026User:Rejecht2012-03-05T12:46:00Z<p>Rejecht: /* Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Game mechanical errors discovered in Unreal Tournament 2004 ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before or after calling ScoreKill, since it expects (NumLives >= MaxLives).<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Best: Track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this, in case someone actually wants the default behavior.<br />
# Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
(First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing. This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.)<br />
<br />
So, sometimes, when a player presses the Fire button, just right after a controller enters one of the Player* states (PlayerWalking), ServerSpectate gets called, which destroys the pawn, enters a spectating state, and then ignores ClientRestart calls.<br />
<br />
The first solution was simply to ignore ServerSpectate in the Player* states in PlayerController; such as PlayerWaiting, PlayerWalking, WaitingforPawn.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Group_Text_TeamSymbol.Length - 1);<br />
Index_TeamSymbol[1] = Rand (Group_Text_TeamSymbol.Length - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn .<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a class object (Class<DamageType> Class_DamageType).<br />
* Use IsA with an instance of a class object.<br />
<br />
(...) more to come.</div>Rejechthttps://wiki.beyondunreal.com/User:Rejecht?diff=45025User:Rejecht2012-03-05T12:41:26Z<p>Rejecht: /* Players who are out can sometimes respawn */</p>
<hr />
<div>== About ==<br />
<br />
Haphazard hobbyist programming sessions.<br />
<br />
=== Nice to know ===<br />
<br />
==== NumLives is not the number of lives ====<br />
NumLives is the number of deaths (the number of lives used).<br />
<br />
==== Errors found in Unreal Engine 2.5 may also be present in the UDK ====<br />
<br />
=== Game mechanical errors discovered in Unreal Tournament 2004 ===<br />
<br />
==== Players who are out can sometimes respawn ====<br />
<br />
Triggers for this error:<br />
* It must be a TeamGame.<br />
* (MaxLives > 0) - Assume that this example has (MaxLives == 1)<br />
* TeamGame.ScoreKill has the errors.<br />
<br />
By design?<br />
* Team kills are not counted as deaths, thus giving the player another chance. This might be desirable in some game types, however, a simple solution is to track the NumLives of the killed player before and after the TeamGame.ScoreKill call; if NumLives is unchanged, decide whether to call Super(DeathMatch).ScoreKill to deal with this, depending on whether this behavior is wanted.<br />
<br />
Consequences:<br />
* The player can respawn despite having died the allotted MaxLives.<br />
<br />
Cause:<br />
* The ScoreKill logic in TeamGame does not always call Super.ScoreKill, which is the one handling the NumLives count and sets bOutofLives accordingly.<br />
<br />
First attempt:<br />
* OK, so let's just override ScoreKill and increment NumLives ourselves, before or after calling ScoreKill, since it expects (NumLives >= MaxLives).<br />
<br />
First consequence of the first attempt:<br />
While this solved the original problem, it also introduced another problem:<br />
* This triggers LateEntryLives (which defaults to 1), forcing latecomers to become spectators (bOnlySpectator) at login, and some people losing their hair because they now have a new unwanted behaviour.<br />
This is because some players now get (NumLives == 2), and others get (NumLives == 1), depending on the circumstances in TeamGame.ScoreKill. Login checks LateEntryLives against all players and boots the player to a spectator slot when it finds (NumLives (2) > LateEntryLives (1)).<br />
<br />
Other possible approaches to solve the first consequence:<br />
# Best: Track changes in NumLives and act accordingly after a Super.ScoreKill call to the TeamGame implementation. Also, add a choice whether to trigger this, in case someone actually wants the default behavior.<br />
# Set LateEntryLives to MAXINT in InitGame to override any ini setting, unless you want to use LateEntryLives.<br />
# Preincrement NumLives before Super.ScoreKill and clamp the value postcall (the first attempt).<br />
<br />
==== AskforPawn and ClientGotoState ====<br />
<br />
AskforPawn insists on calling ClientGotoState ('Spectating', 'Begin'), but there is no Begin: label in the Spectating state, which leads to an error.<br />
<br />
The UDK solves this in the ClientGotoState function, but this code base also gets rid of the logic forcing the client to a spectating state.<br />
<br />
==== Sometimes a player pawn spawns and gets possessed, but immediately gets destroyed again ====<br />
<br />
First, figuring out how to reproduce the error took some time, then the technique to trigger it again during testing.<br />
This one took 3/4 of a day to figure out and solve; logging/tracing client-side and server-side state changes step by effing step.<br />
<br />
So, sometimes, when a player presses the Fire button, right after a controller enters one of the Player* states, ServerSpectate gets called, which destroys the pawn, enters a spectating state, which ignores ClientRestart calls.<br />
<br />
The first solution was simply to ignore ServerSpectate in states such as Player* states in PlayerController; PlayerWaiting, PlayerWalking, etc.<br />
<br />
==== InitTeamSymbols may sometimes give both teams the same team symbol ====<br />
<br />
DeathMatch.InitTeamSymbols is replaced with the following implementation.<br />
<br />
Instead of getting in trouble with more random numbers, it just picks the next team symbol in the array, wrapping around to zero.<br />
<br />
<uscript><br />
// *UT2004Fix*<br />
// (Replaces) DeathMatch.InitTeamSymbols<br />
// - Sometimes both teams would get the same team symbol.<br />
<br />
event InitTeamSymbols ()<br />
{<br />
local array<string> Group_Text_TeamSymbol;<br />
local int Count_TeamSymbols;<br />
local int Index_TeamSymbol[Index_MaxTeams];<br />
local Texture Texture_TeamSymbol;<br />
<br />
// ** Attempt to get a list of team symbols.<br />
<br />
if<br />
(<br />
(GameReplicationInfo.TeamSymbols[0] != None)<br />
&&<br />
(GameReplicationInfo.TeamSymbols[1] != None)<br />
)<br />
{<br />
return; // Already loaded.<br />
}<br />
<br />
// ** Team Symbol Names<br />
<br />
Class'CacheManager'.Static.GetTeamSymbolList (Group_Text_TeamSymbol, true);<br />
<br />
Count_TeamSymbols = Group_Text_TeamSymbol.Length;<br />
<br />
if (Count_TeamSymbols < 10)<br />
{<br />
Warn ("(Count_TeamSymbols < 10)");<br />
<br />
return;<br />
}<br />
<br />
// ** Randomization<br />
<br />
Index_TeamSymbol[0] = Rand (Group_Text_TeamSymbol.Length - 1);<br />
Index_TeamSymbol[1] = Rand (Group_Text_TeamSymbol.Length - 9);<br />
<br />
// **<br />
// - Handle Equality<br />
<br />
if (Index_TeamSymbol[0] == Index_TeamSymbol[1])<br />
{<br />
++Index_TeamSymbol[1];<br />
<br />
if (Index_TeamSymbol[1] >= Count_TeamSymbols)<br />
{<br />
Index_TeamSymbol[1] = 0;<br />
}<br />
}<br />
<br />
// ** Red Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[0]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[0] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[0] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[0]]);<br />
}<br />
<br />
// ** Blue Team<br />
<br />
Texture_TeamSymbol = Texture(DynamicLoadObject (Group_Text_TeamSymbol[Index_TeamSymbol[1]], Class'Texture'));<br />
<br />
if (Texture_TeamSymbol != None)<br />
{<br />
GameReplicationInfo.TeamSymbols[1] = Texture_TeamSymbol;<br />
}<br />
else<br />
{<br />
Warn ("(Texture_TeamSymbol[1] == None)"@Group_Text_TeamSymbol[Index_TeamSymbol[1]]);<br />
}<br />
<br />
// ** Replication<br />
<br />
GameReplicationInfo.TeamSymbolNotify ();<br />
}<br />
<br />
</uscript><br />
<br />
==== ToggleScreenShotMode does not restore a variety of state ====<br />
<br />
Scope:<br />
* PlayerController.ToggleScreenShotMode<br />
<br />
Type:<br />
* Save/Restore<br />
<br />
Fix:<br />
# Remember and restore Handedness, bHideVehicleNoEntryIndicator.<br />
<br />
==== TakeDrowningDamage ==== <br />
<br />
Scope:<br />
* Pawn.TakeDrowningDamage<br />
<br />
Type:<br />
* Access Error<br />
<br />
Fix:<br />
# Check (Controller != None) before calling Super.TakeDrowningDamage in a subclass of pawn .<br />
<br />
==== PlayTakeHit never played drowning sound ====<br />
<br />
Scope:<br />
* xPawn.PlayTakeHit<br />
<br />
Type:<br />
* Class Check Error<br />
<br />
Corrections:<br />
# Find: DamageType.IsA('Drowned')<br />
# Replace With: DamageType == Class'Drowned'<br />
<br />
Reminder:<br />
* Do not use IsA with a class object (Class<DamageType> Class_DamageType).<br />
* Use IsA with an instance of a class object.<br />
<br />
(...) more to come.</div>Rejecht