OPERATION FLASHPOINT TUTORIAL
----------------------------------------------------------------------------------------------------------------------------
 
The fine text on this "Work-In-Progress" reads "This is my 'Ayumi Hamasaki' version, if you don't like it, beat it !!! ~ Raymond Koh"
CUSTOM GRAPHIC USER INTERFACE (MAIN MENU)
RELEASE 1, WRITTEN BY RAYMOND KOH (a.k.a. Takano)

USER PROFICIENCY REQUIREMENT : ADVANCED


PART 0 : INTRO

Public Release
1st May 2004
At the time of writing, Custom Graphic User Interface (I will call it as a GUI from here) is still a highly sought-after subject in the community, especially large mod groups who wish to further add a sense of identity to their already good work.

I didn't receive any assistance when I first started out on this.  It was actually more like I chanced upon this specific area of modification while attempting to do something totally different, but let's not get to that. (^_^)

This is my very first OFP tutorial.  It was only after much thought that I decided to do up and  release it for I believe this knowledge will be very beneficial to the community at large.  It is also an opportunity for me to finally be able to contribute something back to the community (lol).

Oh, you are expected to know everything about preparing textures in OFP (including transparencies and the tools involved to do so) as well as config.cpp coding and your PBOs.  This tutorial is structured to only give you the fundamental understanding of how a GUI is actually constructed.  With the wide range of other possibilities, you have to do some exploration yourself as well.

For now, let's head on to the good stuff right away...

PART 1 : GUI LAYOUT

Default OFP Main Menu GUI Layout
Boring OFP screen
This section attempts to explain how a basic GUI layout works, using something that we all are so familiar with - the default (boring :P) OFP main menu screen.

Break down the interface into individual elements and imagine them in layers that are stacked on top of one another.  So we have...

1.  Black overlay stripes (top & bottom of screen)
2.  OFP logo with a coloured tint
3.  OFP version number
4.  OFP copyright statement
5.  Black tinted translucent layer that shows a camera 
     cut-scene underneath.
6.  Menu Button : 'Player'
7.  Menu Buttons : 'Campaign Game', 'Single Mission', 
     'Multiplayer', 'Mission Editor', 'Options', 'Quit Game'.
8.  2 translucent white lines (one horizontal & one vertical)
9.  Mouse cursor

Every element can be changed (fonts, size, colour, placement etc) - You can edit, remove or even add elements.  This will be covered shortly.


Default OFP Main Menu GUI Layout in Code
If you do not already have a copy of the decompiled config.bin, hop over to the 'OFP: Breathe' website to download 'Binarize' [ here ] which contains version 1.85 of the original config.bin.

Inside the zip file, you will find a bunch of files in the \Binarize\Bin folder (we do not actually need the Binarize utility for this).

Contents of \binarize\bin folder

For this tutorial, we will look at the file 'rscScreen.hpp'.  Open it up using your favourite text editor (WordPad works fine for me).  Scroll around a bit to locate the section as shown below (or do a quick CTRL-F search for 'RscDisplayMain').  This is the GUI in code form for the main menu.

Section that we are interested in rscScreen.hpp

Looking closer, you will find these familiar lines.  Yep, these are the individual elements that was broken down and described earlier on (not in the exact order/naming convention though).

Taking a closer look at this section...

Each element is simply a class (as in config.cpp coding) that is
described under "class RscDisplayMain".


Code
Breakdown
You will also find that the individual classes under "class RscDisplayMain" are defined in the RscScreen.hpp file.  I will breakdown the Background class with a short description of important things to note in the right column.

class Background1 : RscText
{

x = 0.0;
y = 0.0;

w = 1;
h = 0.125;
text = ;

colorBackground[] = {0, 0, 0, 1};

};









Black Overlay Stripe (Top)
Numbers used in x/y/w/h are relative to the screen dimension (in ratio form). 

Ratio will allow accurate object placement regardless of your screen resolution.
Position (x/y) 0.0/0.0 means the top left corner of the screen. w/h represents width/height values.
class Background2 : RscText
{

x = 0.0;
y = 0.875;
w = 1;
h = 0.125;
text = ;

colorBackground[] = {0, 0, 0, 1};

};
Black Overlay Stripe (Bottom)
colourBackground is in the format {R,G,B,A} with a range of 0-1 each (where 0 is min & 1 the max). {0,0,0,1} means black with no transparency.

Starting point of bottom black stripe

Therefore we have a black stripe starting at the extreme left (x=0.0) and 0.875 units down. Size is w=1.0, h=0.125.

Black stripe is rendered across the bottom of screen.
class Background3 : RscText
{

x = 0.0;
y = 0.125;
w = 1;
h = 0.75;
text = ;

colorBackground[] = {0, 0, 0, 0.5};

};
Black Translucent Layer (camera cutscene underneath)
There is actually a black layer with 50% transparency set over a camera cutscene running.
The 0.5 value in {0,0,0,0.5} represents the transparency of the layer.  Half of 1 (the max value) is 0.5, hence the transparency can be said to be set at 50%.

My OFP screen

My OFP screen with black layer

Remember that the x/y value always
describes the starting point with reference from the top left corner.

Top left is where it starts

This is what you get when you put all 3 classes - Background1, Background2 and Background3 together.

Background complete!!!

Applying this concept of element placement / sizing, the remaining elements (classes) from the respective portions
of the code should be pretty straight forward for button text, lines and boxes.

The following elements are added on top of the Background:
OFP logo with a coloured tint
FP1, FP2, FP3

Vertical and Horizontal white lines
Line1, Line2 respectively. 

Menu Buttons
Player - User profile menu
Game - Campaign mode
SingleMission - Single mission mode
Multiplayer - Multiplayer mission mode
Custom - Loads mission editor
Options - Load options menu
Quit - Exit button
Version - OFP version number
Copy - Copyright statement

Misc
Continue
AllMissions



Additional Notes :

$STR_FIELD represents an entry from the stringtable.csv file (this file is found in binarize's bin folder as well). 

For example,
class copy : RscText
  {

    style = ST_MULTI + ST_CENTER + ST_NO_RECT;
    lineSpacing = 1.0;
    text = $STR_CREDITS23;

    x = 0.03; y = 0.93; w = 0.9; h = 0.7;

    colorText[] = {1, 1, 1, 0.5};
    font = FontS;
    sizeEx = 0.016;

  };

$STR_CREDITS23 is found as an entry in Line 2050 in the file stringtable.csv in English language as :

" 2001 Bohemia Interactive Studio and The Codemasters Software Company Limited. All rights reserved. "

You should change the other languages of the same 
entry as well to maintain consistency across your modification.  Then again, you can simply add in a new class for your text instead of going through the hassle of editing the stringtable.csv.  

The decision is entirely up to you, you can either edit an existing entry (class) or add a new one.

- sizeEx affects the font size.. you can try to play with it.

- There are more things that you can do but will not be 
  covered in this tutorial.  Go learn some camera 
  scripting... you can then change the default cutscene 
  running underneath in the main menu.  Or fonts can be 
  changed as well if you do not like the default ones.  I 
  remember seeing a font tutorial somewhere.  If 
  someone can send me a url to put it here, that'll will be 
  good.

- As in config.cpp coding, you can null any sections
  which you may want to omit by adding a double-slash
  //
in front of the line.

- This one is just for fun... here are some other entry 
  references in stringtable.csv (which are not related to
  the GUI though). Change them away if you like!!!
  
 L1168 STR_LOAD_INIT - "The coundown begins..."
 L1169 STR_LOAD_WORLD - "Receiving"
 L1170 STR_LOAD_MISSION - "Get Ready"
 L1171 STR_LOAD_INTRO - "Wait a moment"
 L1172 STR_LOAD_GAME - "Receiving mission status.."
 L1173 STR_SAVE_GAME - "Saving mission status..."
 L1174 STR_AUTOSAVE_GAME - "Updating retry mission"
 L1175 STR_WAIT_NETWORK - "Waiting for opponent..."
 L1176 STR_NETWORK_SEND - "Sending data..."
 
L1177 STR_NETWORK_RECEIVE - "Receiving data..."
 L1178 STR_NETWORK_RECEIVING -"Receiving mission file.."
 L1179 STR_CREATE_SERVER - "Creating server..."
 L1180 STR_CREATE_CLIENT - "Creating client..."
 L1181 STR_SHUTDOWN - "Shutdown..."

PART 2 : PREPARING GRAPHICS FOR GUI

Quite A Pain
In The Arse
This section covers the use of image files as part of the GUI.  For a start, I will talk about the OFP logo found at the top of the screen in the default main menu GUI.

The logo actually comprises of 3 separate classes, FP1, FP2 and FP3.  Each class defines a third of the image (extracted from data.pbo) and adds a colour tint as described by the line colorText[]={0.23,0.3,0.08,0.75}.  

class FP1 : RscPicture
{

text = "OFPlogo1.paa";
colorText[]={0.23,0.3,0.08,0.75};
x = 0.365; y = 0.01; w = 0.09; h = 0.1;

};
class FP2 : RscPicture
{

text = "OFPlogo2.paa";
colorText[]={0.23,0.3,0.08,0.75};
x = 0.455; y = 0.01; w = 0.09; h = 0.1;

};
class FP3 : RscPicture
{

text = "OFPlogo3.paa";
colorText[]={0.23,0.3,0.08,0.75};
x = 0.545; y = 0.01; w = 0.09; h = 0.1;

};
Images are originally 256*256 but are resized to 120*120 to fit this page.

This is how the colour values are derived... the RGB palette has originally a range of 0-255 for each channel.  However, in the code form, 0-1 is used instead. 

Now let's read the line again,
colorText[]={0.23,0.3,0.08,0.75};

Knowing that, one can easily convert these values to and fro.

  R channel : 0.23/1.00 * 255 = 58.65
  G channel : 0.3/1.00 * 255 = 76.5
  B channel : 0.08/1.00 * 255 = 20.4

I usually round up the numbers, so in a standard image editing application, the converted RGB on the palette will look like this image...
RGB palette is what we usually work with

And don't forget the 0.75 transparency (1 being opaque and 0 being totally transparent). Therefore, conceptually we get this:
Ain't this very familiar?
       (  Tint @ 75% Transparency   +   Image   =   End Result  )

The 3 images are then resized (w = 0.09; h = 0.1) and strategically positioned next to each other (x/y values).  You just have to work that out a bit when working on your own GUI for accurate placement.


Summing Up
Coming along this far, I hope you have at least a brief idea of how everything is put together.  You can add new text, lines or graphics by modifying the existing class or add in a new class so long as it is properly defined (don't forget to add it under the "class RscMainDisplay" as well).

After you are done with your coding, these are the last steps:

1. Pop the entire BIN folder (the one you have been 
   working with) into a new folder (name it with your mod's 
   initial!!!) inside \Codemasters\OperationFlashpoint.  This 
   will be your mod folder.


(In my case, my mod folder name is called "PWC")

2. Pathing to external images should be 
    "\pboname\imagename.paa" across your codes. 
    
    Compile all external images into a PBO.  This PBO can 
    be placed either in the default \addons folder or a new 
    \addons folder residing in your mod folder.

3. Add a -mod=modfoldername switch to the end of your 
    execution command.

    Shortcut Example
    "C:\Program Files\Codemasters\OperationFlashpoint\
    FLASHPOINTBETA.EXE" -mod=pwc

This is one of the fastest and easiest way to implement your GUI.  If you wish to compile everything back into config.bin for whatever reason, feel free to do so if you can.

PART 3 : CREATION OF THE PWC GUI

For Inspiration
(or not?)


In this last section, I will briefly go through how I did the GUI for the Project War Chamber mod (as requested).

The original image composite was done up with a canvas size of 1024 * 768 pixels.  Anything smaller would result in quite a crappy image quality after PAA conversion and being blown up full-screen (at probably a higher resolution).  The image is sliced in the into 4 parts (at the red lines).



Now we know that the number of pixels (both horizontal and vertical values) for OFP textures should be in one of the sizes - 2, 4, 8, 16, 32, 64, 128, 256 or 512 (power of 2).  You can see that the heights of all 4 pieces are now out of the size range.  The choice for these values is such that each piece can be easily resized later on using a numeric representation relative to the screen.  You'll see what I mean later on.

The 2 heights (576 and 192) are now being considered to their respective closest in the range of sizes, and so we have 512 and 256.  Resize the pieces (height only) using your favourite image editing application.  The example below shows the "Before & After" for the left pieces.

Height at 576 pixels before scaling... Height at 192 pixels before scaling...
*Sliced pieces (Left Top & Bottom) - Original

Height at 512 pixels after scaling... Height at 256 pixels after scaling...
*Sliced pieces (Left Top & Bottom) - Heights Resized

Each piece is then exported as an uncompressed 32-bit TARGA (RGBA) file and converted to PAA (with alpha/transparency preserved).  We now have 2 x 512*512 and 2 x 512*256 PAA files.


Back to rscScreen.hpp, the following changes are made in "class RscDisplayMain":
1.  Background1, Background 2 classes omitted.
2.  FP1, FP2, FP3 classes omitted.
3.  4 new classes added - PWC1, PWC2, PWC3, PWC4.
4.  2 new classes added - mytext1, mytext2.
5.  Line1 class omitted.
6.  Repositioning of various existing classes.

Earlier on, we picked the values 576 & 192 as the heights (before resizing).  This is because 576 is three-quarter (75%) of 768 and 192 is a quarter (25%) of 768.  Translating this percentage relation into classes PWC1 thru PWC4 makes it easier to insert in the correct x/y/w/h values (at least this is how I think).  It is up to you if you do not wish to follow how I do it.

Back to rscSceen.hpp...

ADDED CLASSES
class PWC1 : RscPicture              [ top left piece ]
{

text = "\pwc\pwc_main1a.paa";
x = 0.00; y = 0.00; w = 0.5; h = 0.75;

};

class PWC2 : RscPicture              [ top right piece ]
{

text = "\pwc\pwc_main1b.paa";
x = 0.50; y = 0.00; w = 0.5; h = 0.75;

};

class PWC3 : RscPicture              [ bottom left piece ]
{

text = "\pwc\pwc_main2a.paa";
x = 0.00; y = 0.75; w = 0.5; h = 0.25;

};

class PWC4 : RscPicture              [ bottom right piece ]
{

text = "\pwc\pwc_main2b.paa";
x = 0.50; y = 0.75; w = 0.5; h = 0.25;

};

class mytext1 : RscText             [ bottom screen text - line 1 ]

{

style = ST_MULTI + ST_CENTER + ST_NO_RECT;
lineSpacing = 1.0;
text = An Army PC Gaming & Simulation Training Initiative;                 

x = 0.048; y = 0.958; w = 0.91; h = 0.7;
colorText[] = {0.921, 0.827, 0.596, 0.5};
font = FontS;

sizeEx = 0.020;

};

class mytext2 : RscText             [ bottom screen text - line 2 ]
{

style = ST_MULTI + ST_CENTER + ST_NO_RECT;
lineSpacing = 1.0;
text = SAF - School of Armour; 

x = 0.048; y = 0.978; w = 0.91; h = 0.7;
colorText[] = {0.921, 0.827, 0.596, 0.5};
font = FontS;
sizeEx = 0.016;

};


MODIFIED/EXISTING CLASSES
class Background3 : RscText             [ coloured tint overlay ]
{

x = 0.0;
y = 0.125;
w = 1;
h = 0.75;

text = ;

colorBackground[] = {0.419, 0.168, 0.000, 0.3};

};


class Line2 : RscText
             [ horizontal white line ]
{

style = ST_LINE;
x = 0.0;
y = 0.82;
w = 1;
h = 0;
text = ;
colorBackground[] = {1, 1, 1, 1};
color[] = {1, 1, 1, 1};
colorText[] = {1, 1, 1, 1};

};

class Continue : RscActiveMenu
{

idc = IDC_MAIN_CONTINUE;
x = 0.05;
y = 0.155;
w = 0.0;
h = 0.0;

font = FontTITLE;
sizeEx = 0.6 * 0.098;

text = $STR_DISP_MAIN_CONTINUE;

};

class Player : RscActiveMenu
{

idc = IDC_MAIN_PLAYER;
x = 0.04;
y = 0.145;
w = 0.5;
h = 0.05;

font = FontTITLE;
sizeEx = 0.6 * 0.098;

text = $STR_DISP_ERROR;
colorText[] = {0.98, 0.25, 0.058, 1};

};

class Game : RscActiveMenu
{

font = FontTITLE;
sizeEx = 0.6 * 0.098;
idc = IDC_MAIN_GAME;

x = 0.78;
y = 0.145;
w = 0.25;
h = 0.05;

text = $STR_DISP_MAIN_GAME;

};

class SingleMission : RscActiveMenu
{

font = FontTITLE;
sizeEx = 0.6 * 0.098;

idc = IDC_MAIN_SINGLE;

x = 0.78;
y = 0.33;
w = 0.52;
h = 0.05;

text = $STR_DISP_MAIN_SINGLE;

default = 1;

};

class Multiplayer : RscActiveMenu
{

font = FontTITLE;
sizeEx = 0.6 * 0.098;

idc = IDC_MAIN_MULTIPLAYER;

x = 0.78;
y = 0.39;
w = 0.52;
h = 0.05;

text = $STR_DISP_MAIN_MULTI;

};

class Custom : RscActiveMenu
{

font = FontTITLE;
sizeEx = 0.6 * 0.098;
style = ST_RIGHT;

idc = IDC_MAIN_EDITOR;
x = 0.21;
y = 0.85;
w = 0.30;
h = 0.05;

text = MISSION EDITOR

};

class Options : RscActiveMenu
{

font = FontTITLE;
sizeEx = 0.6 * 0.098;

idc = IDC_MAIN_OPTIONS;

style = ST_RIGHT;

x = 0.42;
y = 0.85;
w = 0.20;
h = 0.05;

text = OPTIONS

};

class Quit : RscActiveMenu
{

font = FontTITLE;
idc = IDC_MAIN_QUIT;

x = 0.895;
y = 0.85;
w = 0.20;
h = 0.05;

sizeEx = 0.6 * 0.098;

text = EXIT

};

class Date : RscText
{

idc = IDC_MAIN_DATE;

x = 0.4;
y = 0.17;
w = 0.2;
h = 0.05;

style = ST_CENTER;
font = FontS;

sizeEx = 0.02;
text = $STR_DISP_ERROR;

colorText[] = {0.98, 0.25, 0.058, 1};

};

class Version : RscText
{

idc = IDC_MAIN_VERSION;
x = 0.40;
y = 0.915;
w = 0.2;
h = 0.05;

style = ST_CENTER;
font = FontS;

sizeEx = 0.016;
text = $STR_DISP_ERROR;

colorText[] = {1, 1, 1, 0.5};

};

class AllMissions : RscActiveMenu
{

idc = IDC_MAIN_CUSTOM;

x = 0.4;
y = 0.935;
w = 0.52;
h = 0.03;

font = FontTITLEHalf;
sizeEx = 0.5 * 0.05;
colorText[] = {1, 1, 1, 0.25};

text = $STR_DISP_MAIN_DESIGN;

};

class copy : RscText
{

style = ST_MULTI + ST_CENTER + ST_NO_RECT;
lineSpacing = 1.0;
text = OFP Engine;
x = 0.045; y = 0.915; w = 0.91; h = 0.7;
colorText[] = {1, 1, 1, 0.5};

font = FontS;
sizeEx = 0.016;

};

The End
I finished this tutorial in a mad rush and did not have the time to fully go through the chunk of codes up there... most were copied-and-pasted from my many versions.  It can't be that wrong though... and you should be able to figure things out from here. :P

(If you spot any typos or incorrect information, please let me know)

  [ CREDITS / DISCLAIMER ]
 
2004 Written by Raymond Koh K.C. for the OFP community.
  Share and pass on this knowledge!!! However, if you intend to extract this 
  tutorial and host it somewhere, I would appreciate if you have the basic 
  courtesy of linking back to the
Project War Chamber or at least provide a line 
  of credit. :)

  All diagrams are not drawn to scale.


  Like everything else in life, think how you want to apply what you have learnt 
  (if any at all) from this tutorial.  Nothing is guaranteed though everything 
  should work if you have the necessary basics (this is a tutorial for 'Advanced' 
  users anyway).  If your OFP is screwed along the way, get over it and just 
  reinstall.  And please don't come whining to me about it (or BIS even).

 
[ FEEDBACK PLEASE ]
  Tell me how you like this tutorial, this is my first after all !!! There is a 
  feedback form at the bottom of the page at
Project War Chamber.  Maybe this 
  will motivate me to do up more tutorials in future (lol).
 
  Thank you very much.

  ~ Takano


Page Read - 3565

"Operation Flashpoint" is a trademark of Codemasters. Developed by Bohemia Interactive Studio. Published by Codemasters. 2001 Bohemia Interactive and The Codemasters Software Company Limited ("Codemasters"). All rights reserved. "Codemasters" is a registered trademark of Codemasters. All other copyrights or trademarks are the property of their respective owners.

All rights belong to their respective owners and are hereby acknowledged.