Page1, Page 2, Page 3, Page 4, Page 5, Page 6, Page 7
Game Programming Tutorial (Page 7)
This is page 7 of step by step guide to programming a 2D side-scrolling game with some 3D. Now we'll go ahead and build the tile editor and
gamemap editor.
I think the only thing we add to definitions and includes is the ALLOW_EDIT and #include "editor_vars.h"
Editor_vars.h compiles its stuff only if ALLOW_EDIT is defined (contains ifdef ALLOW_EDIT)
so I put it after ALLOW_EDIT define.
#include
"headers.h"
#define
TIMING_DELAY 8
#define
ALLOW_EDIT //to be commented out for the actual game release
#define
LEVEL_WIDTH 200
#define
LEVEL_HEIGHT 50
#include
"editor_vars.h"
//TILE vars:
stTileType tile[521]={0};
I thought 500 or so tile types should be plenty. If you use tile[521]={0}; <-- the ={0} part sets
all the structs members to = 0 ... which saves a lot of time initializing variables...
stTile g_tiles[LEVEL_WIDTH+14][LEVEL_HEIGHT+14]={0};
The above sets up a "2-dimensional" array - basically allocates a static(unchanging) amount of
system RAM to hold a grid of tiles (the gamemap) of size LEVEL_WIDTH x LEVEL_HEIGHT..
I added some padding to reduce chance of reading memory illegally(which could cause freeze).
You could also use ALLOC define to create some dynamically allocated level areas - but I find
just using a big chunk and using what I need from it works fine.
//text / input / file saving vars:
char
g_text[201];
int
last_key_state[256];
D3DCOLOR text_color, default_color = 0xFF008811;
RECT text_position; //add
LPD3DXFONT g_font = NULL;
LPD3DXFONT g_font2 = NULL;
TCHAR* g_current_directory;
unsigned
int
g_directory_buf_size, g_size_of_directory = 260;
bool
g_escape_key_pressed=false;
int
g_text_index=0, last_char, flasher=0, character_repeat_time=0, key_waiting=20;
Just some vars for text based stuff and text input during render looping..
//device / directX object vars:
bool
DeviceLost=false;
LPD3DXSPRITE g_sprite = NULL;
D3DLIGHT9 g_light;
float
LPX=0.0f,LPY=0.0f,LPZ=200.0f; //light position
bool
g_scene_done=true;
bool
g_begin_scene=false;
g_scene_done and g_begin_scene are used to report any mistakes with Begin and
End scene stuff -- ie: using BEGIN_SCENE twice in a row without ending the scene
g_sprite (we only need one of these) - as mentioned before, g_sprite is an interface for rendering
sprites using the d3dx sprite functions. The sprites themselves are kept track of using RECT's
and properties in our own structs.
//matrices / vectors:
D3DXMATRIX g_viewing_matrix;
D3DXMATRIX g_projection_matrix;
D3DXMATRIX g_world_matrix;
D3DXMATRIX g_matrix_2d;
D3DXVECTOR2 g_sprite_scale=D3DXVECTOR2(1,1);
D3DXVECTOR2 g_sprite_center=D3DXVECTOR2(0,0);
D3DXVECTOR2 g_sprite_trans=D3DXVECTOR2(0,0);
D3DXVECTOR2 g_sprite_rot_center=D3DXVECTOR2(0,0);
The first 3 variable are more useful if everything is rendered in OBJECTSPACE. If we do decide to
render the game in "object space" instead of regular sprite mode - then these 3 variables will be
useful for moving the camera around and doing other visual type tricks.
The other globals I put here for 2D scaling and rotation and translation stuff that need to be
kept track of globally.
// game / tile / drawing:
int
g_level=1;
int
tile_x=16,tile_y=16; //actual tile location in tile map for center of screen
float
tile_x_off=0.0f, tile_y_off=0.0f; //tile_map_position offset (0-64) for scrolling
D3DCOLOR g_screen_tint=0xFFFFFFFF;
int
g_num_tile_types=0;
TEXTURE tile_texture;
TEXTURE helpers;
//RECT g_screen_rect={0};
DRAWING_MEMORY rec_images[800];
LPDIRECT3DVERTEXBUFFER9 g_vb=NULL;
TEXTURE g_texture[521]; //these will store 3D cube texture maps:
tile_x and tile_y keep track of the current tile position the main character is
at/over in the game map (center tile).
tile_x_off and tile_y_off -- The section of visible tiles is moved horizontally and vertically
a bit each frame - and the total offset amount is kept track of by these variables... When the
tile_x_off for example exceeds a tile's typical width (64), then it will reset to a zero offset
and change the tile_x... If it were moving in the negative direction and it went past -64 then
since the background is moving left and the hero is travelling right - the tile_x is increased
by 1 when the offset is reset to 0. This way we can loop through a section of visible tiles only,
starting from tile_x-13 to tile_x+13 (and tile_y-10 to tile_y+10)..
Here's the idea in psuedocode:
tile_x_off-=hero.velocity.x;
if (tile_x_off<-64) {tile_x_off=0; tile_x++;}
I'll explain more of this in the actual function..
//---------------
// M A I N
//---------------
int
WINAPI WinMain(HINSTANCE hInstC,HINSTANCE hInstP,LPSTR lpCmdLine,int nCmdShow) {
BEGIN_MEMORY_LEAK_CHECK;
RESET_REPORT;
INIT_WINDOW;
g_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0);
INIT_D3D_SIMPLE;
InitVars();
LoadGraphics(true);
while
(!g_exit_game) {
DWORD starting_point = GetTickCount();
message_pump();
if
(g_WindowReactivate==1) { //begin
g_WindowReactivate=0;ShowWindow(g_hwnd,SW_MAXIMIZE);UpdateWindow(g_hwnd);SetFocus(g_hwnd);continue
;
}
if
(!g_bWindowActive) continue
;
//ALL INPUT GOES HERE!!!
if
(KEYPRESS(RIGHT)) {GameScroll(-1.0f,0.0f); }
if
(KEYPRESS(LEFT)) {GameScroll(1.0f,0.0f); }
if
(KEYPRESS(DOWN)) GameScroll(0.0f,-1.0f);
if
(KEYPRESS(UP)) GameScroll(0.0f,1.0f);
if
(KEYPRESS(A)) {ViewDesc.Position.z++; UpdateViewMatrix();}//zoom++;
if
(KEYPRESS(Z)) {ViewDesc.Position.z--; UpdateViewMatrix();}//zoom--;
^- At first, the sprites are rendered in OBJECTSPACE, so I added this (check out UpdateViewMatrix)
to show how you can play with the camera -- this just zooms in and out - but you could also make
the camera look in other directions or rotate - or even distort the perspective.
Below I added the call to get_editor_input -- which only exists if this is an editor compile.
#ifdef
ALLOW_EDIT
get_editor_input();
#endif
if
(PRESS_ESCAPE) PostMessage(g_hwnd, WM_DESTROY, 0, 0);
render();
SAFE_DO; SAFE_WHILE((GetTickCount() - starting_point) < TIMING_DELAY);
}//<---main_loop---
CleanupGraphics();
END_D3D;
END_WINDOW;
UnhookWindowsHookEx( g_hKeyboardHook );
SHOW_PROBLEMS;
SHOW_MEMORY_LEAK_DATA;
return
0;
}//-----------------------------end main------------------------------------------
You may notice I changed the game scroll method so that 2 images are used and swap places as
they scroll by for a repeating scrolling background.
//---------------------
// G A M E S C R O L L
//---------------------
void
GameScroll(float
x, float
y) {
if
(((x<0)&&(tile_x<LEVEL_WIDTH))||((x>0)&&(tile_x>0))) {
float
tempx=g_far_background.x;
tempx+=x;
if
(tempx<0) tempx=g_width+tempx;
if
(tempx>=g_width) tempx=(tempx-g_width);
g_far_background.x=tempx;
g_far_background.pos2.x=tempx-0.5f;
g_far_background.pos1.x=tempx-g_far_background.texture.width;
float
mx=g_mid_background.x;
mx=mx+x+x;
if
(mx<0) mx=g_width+mx;
if
(mx>=g_width) mx=(mx-g_width);
g_mid_background.x=mx;
g_mid_background.pos1.x=mx-1;
g_mid_background.pos2.x=mx-g_mid_background.texture.width;
tile_x_off+=(x*4);
if
(tile_x_off>63) {
tile_x_off=0+(tile_x_off-64);
tile_x--;
}
if
(tile_x_off<0) {
tile_x_off=64-(0-tile_x_off);
tile_x++;
}
}
Above you can see that I added the code for scrolling the gamemap (tiles grid)
by adjusting the x and y offset based on speed input (multiplied by 4 because this layer
of images move by 4 times faster than the far background because it's closer to us)
When the offset is greater than 64 I subjtract 64 to reset it and adjust the tiles we're
looking at -- this creates the illusion that all the tiles are scrolling by smoothly.
if
(((y<0)&&(tile_y<LEVEL_HEIGHT))||((y>0)&&(tile_y>0))) {
float
my=g_mid_background.y;
my+=(y/4.0f);
g_mid_background.y=my;
g_mid_background.pos1.y=my; g_mid_background.pos2.y=my;
tile_y_off+=(y*4);
if
(tile_y_off>63) {
tile_y_off=0+(tile_y_off-64);
tile_y--;
}
if
(tile_y_off<0) {
tile_y_off=64-(0-tile_y_off);
tile_y++;
}
}
}//GameScroll
// I N I T L I G H T S
void
init_lights(void
)
{ //LIGHT_TYPE D3DLIGHT_SPOT D3DLIGHT_POINT //DIFFUSE_RGBA //AMBIENT_RGBA //SPECULAR_RGBA //Light Direction //Light Position //RANGE //Attenuation 1,2,3 //Falloff,Theta,Phi //light#
CREATE_LIGHT(g_light, D3DLIGHT_POINT,
1.0f,1.0f,1.0f,1.0f, 0.3f,0.3f,0.3f,0.3f, 0.6f,0.6f,0.6f,0.4f,
1.0f,1.0f,-1.0f, LPX,LPY,LPZ, 3000.0f, 1.0f,0.0f,0.0f, 0.12f,0.898f,1.047f,1);
return
;
}
//---------------------------------------------------------------
// U P D A T E V I E W M A T R I X
//---------------------------------------------------------------
void
UpdateViewMatrix() {
D3DXMatrixLookAtLH(&g_viewing_matrix,&ViewDesc.Position,
&D3DXVECTOR3(0.0f,0.0f,0.0f),&D3DXVECTOR3(0.0f,-1.0f,0.0f));
g_gpu->SetTransform(D3DTS_VIEW,&g_viewing_matrix);
}//END UpdateViewMatrix
UpdateViewMatrix is used for updating the camera position, orientation/angle by building a matrix
for view transformation based on what we want the camera to look at. We might use this in object
space -- for 2D games, we'd probably only zoom in or make an earth quake effect or something like that.
#define
SET_D3D(p1,p2) g_gpu->SetRenderState(p1, p2);
//------------------------------
// I N I T R E N D E R V A R S
//------------------------------
void
InitRenderVars() {
SET_D3D(D3DRS_LIGHTING, TRUE); init_lights();
SET_D3D(D3DRS_ZENABLE, TRUE);
SET_D3D(D3DRS_AMBIENT, D3DCOLOR_XRGB(100,100,100));
g_gpu->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
g_gpu->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);
g_gpu->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_CURRENT);
Normally we wouldn't use any lighting for 2D games - but since I want to play with
lights a bit with some 3D elements I'd like to add - I enable lighting. You will find
that setting lighting to false will enhance graphics performance a bit if your only
rendering 2D and have tons and tons of sprites.
Also normally you would want to set ZENABLE to false -- but for some 3D stuff I set
it to true -- performance is actually better with it as false and again - it's not
needed for 2D -- just render in order of depth.
//setup camera:
ViewDesc.Position.x=0.0f;ViewDesc.Position.y=0.0f;ViewDesc.Position.z=600;
ViewDesc.Target.x=ViewDesc.Target.y=ViewDesc.Target.z=0.0f;
ViewDesc.xa=ViewDesc.ya=0.0f;
ViewDesc.uni_scale=1.0f;
D3DXMatrixIdentity(&ViewDesc.RotationMatrix);
D3DXMatrixIdentity(&ViewDesc.TranslationMatrix);
D3DXMatrixIdentity(&ViewDesc.ScaleMatrix);
D3DVIEWPORT9 vp; vp.X = 0; vp.Y = 0; vp.Width = g_width; vp.Height = g_height; vp.MinZ = 0.0f; vp.MaxZ = 100.0f; g_gpu->SetViewport(&vp);
D3DXMatrixIdentity(&g_world_matrix);
D3DXMatrixIdentity(&g_viewing_matrix);
D3DXMatrixIdentity(&g_projection_matrix);
D3DXMatrixLookAtLH(&g_viewing_matrix,&ViewDesc.Position, &ViewDesc.Target, &D3DXVECTOR3 (0.0f, -1.0f, 0.0f));
D3DXMatrixPerspectiveFovLH(&g_projection_matrix,D3DXToRadian(45),(float
)g_width/(float
)g_height,NEAR_Z,FAR_Z);
g_gpu->SetTransform(D3DTS_VIEW,&g_viewing_matrix);
g_gpu->SetTransform(D3DTS_PROJECTION,&g_projection_matrix);
//(could set world matrix here too)
}//InitRenderVars
//---------------------------
// L O A D G R A P H I C S
//---------------------------
void
LoadGraphics(bool
restore_all) {
if
(restore_all) {
//create 2D graphics processing objects:
InitRenderVars();
if
(!g_sprite) {
if
(FAILED(D3DXCreateSprite(g_gpu, &g_sprite))) FATAL("D3DXCreateSprite FAILED",Problem);
D3DXFONT_DESC FontDesc = {14,0,800,0,false,DEFAULT_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_PITCH,"Arial"};
D3DXFONT_DESC FontDesc2 = {16,0,800,0,false,DEFAULT_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_PITCH,"Arial"};
D3DXCreateFontIndirect(g_gpu,&FontDesc,&g_font);
D3DXCreateFontIndirect(g_gpu,&FontDesc2,&g_font2);
}
helpers.LoadTexture("Graphics/helpers.dds");
Restore all is true only when we've lost the 3D device and we needed to reset all resources..
Here we created the sprite object and a couple different fonts to use in directX. The helpers.dds
is a texture map which I'll use for stuff like shadows or semi-transparent text boxes or whatever.
//set up 3D cube for box tiles: -------------------------
int
i=0,scal1=64; //(-1 to +1 gives us -64 to +64 which is 128x128 in size)
do
{
g_box_verts[i].x=g_box_verts[i].x*scal1;
g_box_verts[i].y=g_box_verts[i].y*scal1;
g_box_verts[i].z=g_box_verts[i].z*scal1;
i++;
}while
(i<NUM_BOX_VERTICES);
Set scale to 64 and scaled all the vertices in the vertex list for the 3D cube object...
this way cube tiles will be the same size as a flat tile(if at correct distance)
//I made it as write only - but we might want to use dynamic instead:
ghr=g_gpu->CreateVertexBuffer(NUM_BOX_VERTICES*sizeof
(stMyVertex), D3DUSAGE_WRITEONLY,
MY_CUSTOM_VERTEX, D3DPOOL_MANAGED, &g_vb,NULL);
if
(FAILED(ghr)) FatalError("Error creating vertex buffer");
unsigned
char
*vb_vertices;
ghr=g_vb->Lock(0,0,(void
**)&vb_vertices,0);
if
(FAILED(ghr)) FatalError("Error Locking triangle buffer");
memcpy(vb_vertices, g_box_verts, sizeof
(g_box_verts) );
g_vb->Unlock();
//------------------------------------------------------
Here I created a vertex buffer on the graphics card and locked it so we can transfer the vertex
list into the buffer (using the vertex format we specified) -- and unlock it again so the
card is free to use it without interruption -- and thus we now have a 3D cube object we can
render set up in graphics card memory.
..
Load textures for editor:
#ifdef
ALLOW_EDIT
EDITOR_TEXTURE_LOADING;
#endif
}
switch
(g_level) {
case
1:
g_far_background.texture.LoadTexture("Graphics/far_background1.dds");
g_mid_background.texture.LoadTexture("Graphics/mid_background1.dds");
tile_texture.LoadTexture("Graphics/tiles1.dds");
break
;
}
load_tilemap(); //automatically loads tilemap based on level
We load the backgrounds and tile graphics -- load_tilemap however actually loads a map
of how tiles in the tile graphics map are to be understood. In the tile-map editor - we
can customize tile properties and regions on a graphics image of the tiles -- this is
saved as a so called tilemap file. This is different from the gamemap editor which will
allow us to build, load and save a game map (a grid of tile entries for an world / level).
g_far_background.x=g_far_background.y=g_mid_background.x=g_mid_background.y=0;
g_far_background.width=g_far_background.texture.width; //set to same as image by default
g_far_background.height=g_far_background.texture.height;
g_far_background.r1.left=1;
g_far_background.r1.top=0; g_far_background.r1.bottom=g_far_background.height;
g_far_background.r2.top=0; g_far_background.r2.bottom=g_far_background.height;
g_far_background.r2.right=g_width-1;
VEC_ZERO(g_far_background.pos1); VEC_ZERO(g_far_background.pos2);
g_mid_background.width=g_mid_background.texture.width;
g_mid_background.height=g_mid_background.texture.height;
g_mid_background.r1.left=1;
g_mid_background.r1.top=0; g_mid_background.r1.bottom=g_mid_background.height;
g_mid_background.r2.top=0; g_mid_background.r2.bottom=g_mid_background.height;
g_mid_background.r2.left=0; //added
g_mid_background.r2.right=g_mid_background.texture.width-1;
VEC_ZERO(g_mid_background.pos1); VEC_ZERO(g_mid_background.pos2);
g_mid_background.y=0.0f;
//Above we just initialize the background scroller vars
}//LoadGraphics
//----------------------------
// I N I T V A R S
//----------------------------
void
InitVars() {
#ifndef
ALLOW_EDIT
ShowCursor(FALSE);
#else
INIT_EDITOR_VARS;
#endif
mouse_pos.x=20.0f; mouse_pos.y=20.0f; mouse_pos.z=0.0f;
g_current_directory = new TCHAR[g_size_of_directory]; //DIRECTORY
g_directory_buf_size = GetCurrentDirectory(g_size_of_directory, g_current_directory);
}//InitVars
//-------------------------------
// C L E A N U P G R A P H I C S
//-------------------------------
void
CleanupGraphics() {
//release graphics:
SAFE_RELEASE_FINAL(g_far_background.texture.tx);
SAFE_RELEASE_FINAL(g_mid_background.texture.tx);
SAFE_RELEASE_FINAL(tile_texture.tx);
SAFE_RELEASE_FINAL(helpers.tx);
#ifdef
ALLOW_EDIT
RELEASE_EDITOR_STUFF;
#endif
//release objects:
SAFE_RELEASE_FINAL(g_vb);
SAFE_RELEASE_FINAL(g_font);
SAFE_RELEASE_FINAL(g_font2);
SAFE_RELEASE_FINAL(g_sprite);
SAFE_RELEASE_FINAL(g_zbuffer);
if
(DeviceLost) return
; //don't release stuff after this until game exits...
SAFE_DELETE(g_current_directory);
ShowCursor(TRUE);
}//CleanupGraphics
//----------------
// R E N D E R
//----------------
void
render() {
HANDLE_LOST_DEVICES( , );
BEGIN_SCENE("render");
D3DXMatrixIdentity(&g_world_matrix);
D3DXMatrixTranslation(&g_world_matrix, (FLOAT)-g_half_width, (FLOAT)-g_half_height, 0);
g_gpu->SetTransform(D3DTS_WORLD, &g_world_matrix);
Actually we don't need to do this here - since the world matrix is not changing
this should be moved into init_render_vars and only done once. If we were actually
changing the world matrix (like in a 3D game) then we'd do this - but not in the
render loop - but probably only when the world matrix actually changes - after the
player has actually changed position.
g_sprite->Begin(D3DXSPRITE_ALPHABLEND | D3DXSPRITE_OBJECTSPACE); // | D3DXSPRITE_SORT_DEPTH_BACKTOFRONT
DRAW(g_far_background.texture,NULL,&g_far_background.pos1);
DRAW(g_far_background.texture,NULL,&g_far_background.pos2);
DRAW(g_mid_background.texture,&g_mid_background.r2,&g_mid_background.pos1);
DRAW(g_mid_background.texture,&g_mid_background.r2,&g_mid_background.pos2);
g_sprite->End();
#ifdef
ALLOW_EDIT //show editor menu
g_sprite->Begin(D3DXSPRITE_ALPHABLEND); DRAW(edit_menu_texture,NULL,NULL); g_sprite->End();
#endif
END_SCENE("render");
}//render
So far, more as an example - initially I have it rendering 2D in objectspace when the program
first starts -- and you can play with zooming in and out by pressing A or Z (which works
by changing the viewing matrix)
// R E S E T U S E R I N P U T M E M O R Y
// resets all input characters and all keys to pressed (so key must be unpressed before it can record the key as input)
void
reset_user_input_memory() {
int
a=0; SAFE_DO; g_text[a]='\0';a++;}while
(a<200);
g_text_index=0;
a=0; TEST_ARRAY(last_key_state,255);
SAFE_DO;
last_key_state[a]=1; //set as pressed so it must wait for an unpress before registering the key as useable
a++;
}while
(a<255);
}//reset_user_input_memory
//----------------------------
// P O L L U S E R I N P U T
//----------------------------
//function that is used to update a text entry that a user is working on while other directX
//stuff is running in the background (without interuption) [returns true if enter is pressed]
bool
PollUserInput(const
char
* dialog, LONG x, LONG y, int
max_input_size) {
//show message:
int
length=strlen(dialog)*4+3;
RECT r; r.left=(LONG)(g_half_width-length);r.right=(LONG)(g_half_width+length);r.top=(LONG)(y-9);r.bottom=(LONG)(y+9);
D3DXMatrixIdentity(&g_matrix_2d); g_sprite->SetTransform(&g_matrix_2d);
TEXT_POSITION(x,y-24);
PRINT_TEXT(dialog);
TEXT_POSITION(x,(y-9));
//show current input text after message
TEST_ARRAY(g_text,g_text_index);
flasher++;
if
(flasher<5) {
g_text[g_text_index]=0x7C; g_text_index++;
}
g_font->DrawText(g_sprite, g_text,-1,&text_position,DT_LEFT | DT_TOP ,0xFFFF00AA);
if
(flasher<5) {
g_text[g_text_index]='\0'; g_text_index--; g_text[g_text_index]='\0';
}
if
(flasher>10) flasher=0;
//update text based on input (using timing and handling key holding)
//return false but return true if last input was enter and size>0
int
key=9999, add_key=9999; //add_key is any key that is added to actual text
int
a=0; bool
shift=false;
if
(KEY_IS_DOWN(KEY_SHIFT)) shift=true;
TEST_ARRAY(last_key_state,255);
SAFE_DO;
if
(KEY_IS_DOWN(a)) {
if
(last_key_state[a]==0) { //new keypress - capture it:
key=a;
if
(key==0x20) add_key=key; //SPACE
if
(key==KEY_MINUS) {add_key=0x2D; if
(shift) add_key=0x5F;}//DASH
if
(key==KEY_QUOTE) {add_key=0x27;}
if
((key>=0x30)&&(key<=0x39)) add_key=key; //NUMBERS
if
((key>=0x41)&&(key<=0x5A)) add_key=key; //LETTERS
if
(last_char!=key) key_waiting=20;
character_repeat_time=0; last_char=key;
} else
{
character_repeat_time++;
}
last_key_state[a]=1;
if
(character_repeat_time>key_waiting) {last_key_state[a]=0; character_repeat_time=0; key_waiting=10;}
}
if
(KEY_IS_UP(a)) {
last_key_state[a]=0;
}
a++;
}while
(a<255);
if
(key<9999) {
if
(add_key<9999) { //alphabetic key to add
if
(g_text_index<max_input_size) { //MAX_INPUT_SIZE
g_text[g_text_index]=add_key;
g_text_index++; TEST_ARRAY(g_text,g_text_index);
}
} else
{
//test for backspace or enter
if
(key==KEY_BACKSPACE) {
if
(g_text_index>0) {
g_text[g_text_index]='\0'; g_text_index--; g_text[g_text_index]='\0'; key_waiting=8;
} else
{g_text[0]='\0'; g_text[1]='\0';}
}
if
(key==KEY_DELETE) {a=0; SAFE_DO; g_text[a]='\0';a++;}while
(a<=g_text_index); g_text_index=0;}
if
(key==KEY_ENTER) {
if
(g_text_index>0) return
true; //done
}
}
}
return
false; //not done
}//PollUserInput
The above 2 functions are used when you need to get input from a user which needs to
be updated in a rendering loop in DirectX. We could probably move these functions
into GameInput module.. maybe I'll do that later..
//--------------------
//P R O M P T U S E R (Y/N)
//--------------------
bool
PromptUser(const
char
* str) {
bool
ret_val=false;
text_color=0xFFFF4400; TEXT_POSITION(5,g_height-80);
PRINT_TEXT(str); g_sprite->End(); END_SCENE("Prompt User");
do
{ message_pump();
if
(KEYPRESS(N)) {ret_val=false; break
;}
if
(KEYPRESS(Y)) {ret_val=true; break
;}
}while
(!KEYPRESS(ESCAPE)); BEGIN_SCENE("Prompt User"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
return
ret_val;
}//PromptUser
Just for doing a quick user prompt -- like "Are you sure you want to quit? (Y/N)
This one assumes a sprite scene was being rendered and thus quits and resumes
the process appropriately.
// D R A W B L A C K R E C T
//useful for drawing a semi-transparent black square to draw text on over a background
void
draw_black_rect(int
x1, int
y1, int
x2, int
y2) {
RECT r;
int
b=y1, xr=x2;
SAFE_DO;
int
a=x1;
SAFE_DO;
xr=(x2-a);
if
(xr>255) xr=255;
SET_RECT(r,0,0,xr,20);
DRAW(helpers,&r,&VEC((FLOAT)a,(FLOAT)b,0));
a+=255;
}while
(a<x2);
b+=20;
}while
(b<y2);
}
The helpers.dss image contains a semi-transparent black block which
can be used for making a partly see-thru rectangle for writing text
over (which makes the text easier to read).
// C O P Y T E X T U R E D A T A
// I copy the piece of texture from the tile map into its own texture so I don't need to change
// the texture coordinates for cube tiles.
void
CopyTextureData(int
a, RECT r) {
RECT dr;
dr.left=dr.top=0; dr.right=dr.bottom=128; // for now I'm just assuming 3D tiles are always 128x128 cubes
D3DXCreateTexture(g_gpu,128,128,1,0,D3DFMT_A8R8G8B8,D3DPOOL_MANAGED, &g_texture[a].tx);
LPDIRECT3DSURFACE9 source = NULL;
LPDIRECT3DSURFACE9 dest = NULL;
tile_texture.tx->GetSurfaceLevel(0, &source);
g_texture[a].tx->GetSurfaceLevel(0, &dest);
D3DXLoadSurfaceFromSurface(dest, NULL, NULL, source, NULL, &r, D3DX_FILTER_NONE, 0);
source->Release();
dest->Release();
}
The comment basically explains what this does.
1) create empty texture
2) get source surface
3) get destination surface
4) copy based on r (source region/rect to copy from)
Now when drawing a 3D cube -- all we need to do is change textures
when drawing and can just use the same cube object (don't need unique texture coordinates)
//-------------------------------------
// L O A D T I L E M A P (auto-loader)
//-------------------------------------
//loads data that determines how to interpret the tilemap(tiles bitmap)
void
load_tilemap() {
FILE *f=NULL;
char
filename[100];
sprintf_s(filename, _T("Tilemaps/tilemap%d.tma"), g_level);
if
(!FileExists(filename)) {REPORT_D("tilemap not yet created for this level",g_level); return
;}
fopen_s(&f, filename, "rb");
if
(!f) { REPORT("file read error"); } else
{
fread(&g_num_tile_types, sizeof
(g_num_tile_types), 1, f);
fread(&tile, sizeof
(tile[0]), g_num_tile_types, f);
fclose(f);
}
//set up needed textures for cubes
int
a=0;
SAFE_DO {
if
(tile[a].layer_type==LAYER_BOX) {
CopyTextureData(a,tile[a].r);
}
a++;
}SAFE_WHILE(a<g_num_tile_types);
}
Go to the folder containing your source files and Graphics directory
and add another 2 directories/folders - the first one is Tilemaps
and the second one you should add is Gamemaps.
If the requested tilemap exists, we first find out how many tile
types are to be stored and then read that quantity into the
array of tile structs.
//-------------------
// R E S E T T I L E
//-------------------
void
reset_tile(int
a,int b) {
ZeroMemory(&g_tiles[a][b], sizeof
g_tiles[a][b]);
g_tiles[a][b].color=0xFFFFFFFF;
g_tiles[a][b].type=-1;
g_tiles[a][b].depth=INFRONT;
g_tiles[a][b].i2=-1;
g_tiles[a][b].scale=g_tiles[a][b].scale2=1.0f;
}//reset_tile
Reset's a grid/gamemap tile to it's initial empty state.
The following function takes the a and b coordinate of a tile
on the gamemap and adds a tile of type tn (index into tile array
which holds info about what the tile is)
so g_tiles[a][b].i = tn which looks up tile[tn]
If remove occupied is true - it replaces a tile whether occupied
or not..
All gamemap tiles that the tile[tn] would cover will be set to
point to the master tile(top-left corner tile) which points to
which holds the index and other info about this tile...
I use a master tile because it speeds up the loop and prevents
redundant rendering.
//---------------
// A D D T I L E
//---------------
void
add_tile(int
a,int b,int tn,bool allow_occupied,bool remove_occupied) {
int
ma,mb,mi;
int
b1=0,b2=b;
bool
occupied=false;
if
(remove_occupied) allow_occupied=false;
if
(!allow_occupied) {
//check if already occupied:
SAFE_DO;
int
a1=0,a2=a;
SAFE_DO;
if
(g_tiles[a2][b2].type>-1) {
occupied=true;
if
(remove_occupied) {
ma=g_tiles[a2][b2].m_a;
mb=g_tiles[a2][b2].m_b;
mi=g_tiles[ma][mb].i;
int
b3=mb, b4=0;
SAFE_DO {
int
a3=ma, a4=0;
SAFE_DO {
reset_tile(a3,b3);
a3++; a4++;
}SAFE_WHILE(a4<tile[mi].tiles_wide);
b3++; b4++;
}SAFE_WHILE(b4<tile[mi].tiles_high);
}
}
a1++;a2++;
}while
(a1<tile[tn].tiles_wide);
b1++;b2++;
}while
(b1<tile[tn].tiles_high);
}
Here we loop thru tiles on the gamemap based on width and height in
tiles and if they are occupied (g_tiles[a2][b2].type>-1) then we set
it as occupied -- in which case if they are to be removed - we reset them..
if
(remove_occupied) occupied=false;
if
(!occupied) {
b1=0;b2=b;
SAFE_DO;
int
a1=0,a2=a;
SAFE_DO;
g_tiles[a2][b2].type=tile[tn].type;
g_tiles[a2][b2].m_a=a; g_tiles[a2][b2].m_b=b;
g_tiles[a2][b2].depth=tile[tn].depth;
a1++;a2++;
}while
(a1<tile[tn].tiles_wide);
b1++;b2++;
}while
(b1<tile[tn].tiles_high);
g_tiles[a][b].type=tile[tn].type;
g_tiles[a][b].i=tn;
g_tiles[a][b].i2=-1; g_tiles[a][b].scale=g_tiles[a][b].scale2=1.0f;
COPY_VECTOR(g_tiles[a][b].offset,tile[tn].offset);
g_gamemap_changed=true;
So if we're allowed to fill in these tiles - we go ahead
and fill in the gamemap tile's details - such as which tile
type it is (SOLID, PASS, LIQUID, etc..), which tile is the
master tile, what the tile's depth, the index to the tilemap info
(tn), and copy the initial offset preset in the tile info
(we set gamemap changed to true so it will prompt for save if exiting)
If the ALT key was pressed - it allows insertion of a second tile in
this place:
} else
if
(KEYPRESS(ALT)) {
ma=g_tiles[a][b].m_a; mb=g_tiles[a][b].m_b;
g_tiles[ma][mb].i2=tn;
COPY_VECTOR(g_tiles[ma][mb].offset2,tile[tn].offset);
float
adif=(a-ma)*64.0f;
float
bdif=(b-mb)*64.0f;
g_tiles[ma][mb].offset2.x+=adif;
g_tiles[ma][mb].offset2.y+=bdif;
}
}//add_tile
//---------------------
// R E M O V E T I L E
//---------------------
void
remove_tile(int
a, int
b) {
int
ma=g_tiles[a][b].m_a, mb=g_tiles[a][b].m_b;
int
mi=g_tiles[ma][mb].i;
int
wide=tile[mi].tiles_wide, high=tile[mi].tiles_high;
int
b1=0,b2=mb;
SAFE_DO;
int
a1=0,a2=ma;
SAFE_DO;
reset_tile(a2,b2);
a1++; a2++;
}while
(a1<wide);
b1++; b2++;
}while
(b1<high);
reset_tile(a,b);
g_gamemap_changed=true;
}//remove tile
The above just resets an occupied tile region on the gamemap
to what it was before any tiles were layed there.
// C H A N G E T I L E T Y P E
// useful if a wall or something becomes passable (also used in editor to make tiles act unusually)
void
change_tile_type(int
a, int
b, int
d, bool
all) {
int
ma, mb, mi;
int
width, height;
Sleep(160);
if
(!all) {
g_tiles[a][b].type+=d;
if
(g_tiles[a][b].type>=NUM_TYPES) g_tiles[a][b].type=0;
if
(g_tiles[a][b].type<0) g_tiles[a][b].type=NUM_TYPES-1;
return
;
}
If we only want to change one gamemap tile's type - we
return right away after changing the 1 at (a,b)
Otherwise as below - we change all the tiles occupied by this game-tile
to the new type (SOLID, LIQUID, PASS, etc...):
ma=g_tiles[a][b].m_a; mb=g_tiles[a][b].m_b;
mi=g_tiles[ma][mb].i;
width=tile[mi].tiles_wide; height=tile[mi].tiles_high;
int
new_type=g_tiles[ma][mb].type+d;
if
(new_type>=NUM_TYPES) new_type=0;
if
(new_type<0) new_type=NUM_TYPES-1;
int
y=0, bb=mb;
SAFE_DO {
int
x=0, aa=ma;
SAFE_DO {
g_tiles[aa][bb].type=new_type;
x++; aa++;
}SAFE_WHILE(x<width);
y++; bb++;
}SAFE_WHILE(y<height);
}//change tile type
//-----------------
// D R A W C U B E
//-----------------
// draws a 3D tile
void
draw_cube(int
a, int
b, VEC pz, D3DCOLOR col) {
int
i=g_tiles[a][b].i;
D3DXMATRIX matWorld, matRotate, matScale, matTrans;
D3DXMatrixIdentity(&matWorld); g_sprite->SetTransform(&g_matrix_2d);
//rotate locally first:
D3DXMatrixRotationZ(&matRotate, g_tiles[a][b].rotation);
//scale locally first:
D3DXMatrixScaling(&matScale, g_tiles[a][b].scale, g_tiles[a][b].scale, g_tiles[a][b].scale);
//translation to new coordinates:
D3DXMatrixTranslation(&matTrans,pz.x-332,pz.y-236,-200.0f);
//*Make sure you multiply by the translation last!
matWorld = matRotate *matScale * matTrans;
g_gpu->SetTransform(D3DTS_WORLD,&matWorld);
Basically what we just did was rotate the object,
scale it, then translate it(move it) and set a matrix to represent that
desired set of actions - and tell the graphics processor to set that
as the current world transformation matrix (in other words do the
desired stuff to whatever object(s) we're about to render)..
As below - we set the texture for this 3D cube and since there
are 3 vertices per face - the number of faces to draw is the
number of existing vertices in the vertex-list divided by 3
g_gpu->SetTexture(0,g_texture[i].tx);
g_gpu->DrawPrimitive(D3DPT_TRIANGLELIST,0,NUM_BOX_VERTICES/3);
g_gpu->SetTexture(0,NULL);
D3DXMatrixIdentity(&g_matrix_2d); g_sprite->SetTransform(&g_matrix_2d);
}//draw_cube
//-----------------
// D R A W T I L E
//-----------------
Draws a tile for gamemap g_tiles[a][b] at screen position pz with tint col
void
draw_tile(int
a, int
b, VEC pz, bool
overlapper, D3DCOLOR col) {
int
i;
float
scale,rot;
(If a secondary tile, use index2, scale2, rotation2...) :
if
(overlapper) {
i=g_tiles[a][b].i2;
scale=g_tiles[a][b].scale2;
rot=g_tiles[a][b].rotation2;
} else
{
i=g_tiles[a][b].i;
scale=g_tiles[a][b].scale;
rot=g_tiles[a][b].rotation;
}
//Rotate and scale around the center of the tile cluster:
g_sprite_rot_center=D3DXVECTOR2((float
)tile[i].tiles_wide*32.0f,(float
)tile[i].tiles_high*32.0f);
g_sprite_center=g_sprite_rot_center;
g_sprite_scale=D3DXVECTOR2(scale,scale);
//set tile position in screen coordinates:
g_sprite_trans=D3DXVECTOR2(pz.x,pz.y);
if non-animated - just set the transformation matrix and draw the tile...
source coordinates are kept in tile[i].r and it is tinted by col
if
(tile[i].animation_type==NO_ANIM) {
D3DXMatrixTransformation2D(&g_matrix_2d,&g_sprite_center,0.0,&g_sprite_scale,&g_sprite_rot_center,rot,&g_sprite_trans); g_sprite->SetTransform(&g_matrix_2d);
g_sprite->Draw(tile_texture.tx,&tile[i].r,NULL,NULL,col);
}
else
if
(tile[i].num_frames>=2) {
for animated ones, source coordinates are in tile[i].animation[]
and frame is selected by tile[i].frame
D3DXMatrixTransformation2D(&g_matrix_2d,&g_sprite_center,0.0,&g_sprite_scale,&g_sprite_rot_center,rot,&g_sprite_trans); g_sprite->SetTransform(&g_matrix_2d);
g_sprite->Draw(tile_texture.tx,&tile[i].animation[tile[i].frame],NULL,NULL,col);
}
if
((tile[i].animation_type==SCROLL_X)||(tile[i].animation_type==SCROLL_Y)) {
//this works like background scroller did originally, but we're
scrolling a tile in place instead - like for waterfalls or conveyor belts
RECT dum_r;
LONG overflow=0;
VEC p; p.z=0; p.x=pz.x; p.y=pz.y;
if
(tile[i].animation_type==SCROLL_X) {
overflow=(LONG)tile[i].scroll_pos.x-tile[i].r.left;
CopyRect(&dum_r,&tile[i].r);
dum_r.left=tile[i].r.left;
dum_r.right=tile[i].r.right-overflow;
p.x=tile[i].scroll_pos.x-tile[i].r.left+pz.x;
g_sprite_trans=D3DXVECTOR2(p.x,p.y);
D3DXMatrixTransformation2D(&g_matrix_2d,&g_sprite_center,0.0,&g_sprite_scale,&g_sprite_rot_center,rot,&g_sprite_trans); g_sprite->SetTransform(&g_matrix_2d);
g_sprite->Draw(tile_texture.tx,&dum_r,NULL,NULL,col);
dum_r.left=dum_r.right;
dum_r.right=tile[i].r.right;
p.x=pz.x;
g_sprite_trans=D3DXVECTOR2(p.x,p.y);
D3DXMatrixTransformation2D(&g_matrix_2d,&g_sprite_center,0.0,&g_sprite_scale,&g_sprite_rot_center,rot,&g_sprite_trans); g_sprite->SetTransform(&g_matrix_2d);
g_sprite->Draw(tile_texture.tx,&dum_r,NULL,NULL,col);
}
if
(tile[i].animation_type==SCROLL_Y) {
overflow=(LONG)tile[i].scroll_pos.y-tile[i].r.top;
CopyRect(&dum_r,&tile[i].r);
dum_r.top=tile[i].r.top;
dum_r.bottom=tile[i].r.bottom-overflow;
p.y=tile[i].scroll_pos.y-tile[i].r.top+pz.y;
g_sprite_trans=D3DXVECTOR2(p.x,p.y);
D3DXMatrixTransformation2D(&g_matrix_2d,&g_sprite_center,0.0,&g_sprite_scale,&g_sprite_rot_center,rot,&g_sprite_trans); g_sprite->SetTransform(&g_matrix_2d);
g_sprite->Draw(tile_texture.tx,&dum_r,NULL,NULL,col);
dum_r.top=dum_r.bottom;
dum_r.bottom=tile[i].r.bottom;
p.y=pz.y;
g_sprite_trans=D3DXVECTOR2(p.x,p.y);
D3DXMatrixTransformation2D(&g_matrix_2d,&g_sprite_center,0.0,&g_sprite_scale,&g_sprite_rot_center,rot,&g_sprite_trans); g_sprite->SetTransform(&g_matrix_2d);
g_sprite->Draw(tile_texture.tx,&dum_r,NULL,NULL,col);
}
}
Always a good idea to reset the 2D transform matrix so we don't
get any weird unwanted off-screen drawing problems somewhere else
D3DXMatrixIdentity(&g_matrix_2d); g_sprite->SetTransform(&g_matrix_2d);
}//draw_tile
//-------------------------
// A N I M A T E T I L E S
//-------------------------
void
animate_tiles() {
int
i=0;
SAFE_DO {
if
(tile[i].num_frames>0) {
if
(tile[i].num_frames>=2) {
tile[i].timer++;
if
(tile[i].animation_type==PING_PONG) {
if
(tile[i].timer>tile[i].wait_time) {tile[i].frame+=tile[i].frame_direction; tile[i].timer=0;}
if
(tile[i].frame>=tile[i].num_frames) {tile[i].frame_direction=-1; tile[i].frame--;}
if
(tile[i].frame<=0) tile[i].frame_direction=1;
} else
{
if
(tile[i].timer>tile[i].wait_time) {tile[i].frame++; tile[i].timer=0;}
if
(tile[i].frame>=tile[i].num_frames) tile[i].frame=0;
}
}
}
The animation frame updates every time the tile[i].timer var exceeds
the wait_time and resets timer to 0. If it's a ping pong type then the frame
goes 0 to numframes and then numframes to 0 and so on.. (thus frame_direction)
If it is a type SCROLL_X or SCROLL_Y, then we update the scroll position
at the speed which is stored in wait_time(for this):
if
(tile[i].animation_type==SCROLL_Y) {
tile[i].scroll_pos.y+=tile[i].wait_time;
if
(tile[i].scroll_pos.y<=tile[i].r.top) tile[i].scroll_pos.y=tile[i].r.bottom-(tile[i].r.top-tile[i].scroll_pos.y);
if
(tile[i].scroll_pos.y>=tile[i].r.bottom) tile[i].scroll_pos.y=tile[i].r.top+(tile[i].scroll_pos.y-tile[i].r.bottom);
}
if
(tile[i].animation_type==SCROLL_X) {
tile[i].scroll_pos.x+=tile[i].wait_time;
if
(tile[i].scroll_pos.x<=tile[i].r.left) tile[i].scroll_pos.x=tile[i].r.right-(tile[i].r.left-tile[i].scroll_pos.x);
if
(tile[i].scroll_pos.x>=tile[i].r.right) tile[i].scroll_pos.x=tile[i].r.left+(tile[i].scroll_pos.x-tile[i].r.right);
}
i++;
}SAFE_WHILE(i<g_num_tile_types);
}//draw_tile
//---------------------------------------
// D R A W B A C K T I L E L A Y E R S
//---------------------------------------
If there are layers further back - this will draw them scaled
and scrolled at a scaled offset so that they appear to move
by further away from the camera. This can be done in an
entirely 2D mode (not OBJECTSPACE) and still look 3D.
Actually - the entire editor's 2D rendering is done in
2D mode - with 3D stuff rendered behind everything first.
void
draw_back_tile_layers(float
scale) {
VEC pos; pos.z=0;
int
a,b,ar;
g_sprite_scale=D3DXVECTOR2(scale,scale);
g_sprite_center=D3DXVECTOR2(0,0);
g_sprite_rot_center=D3DXVECTOR2(0,0);
b=tile_y-10;
float
aam=64*scale;
float
xam=g_half_width-aam*13+(int
)tile_x_off*scale;
float
yy=g_half_height-aam*10+(int
)tile_y_off*scale;
aam -- the normal tile size scaled - memorized into aam
to reduce redundant calculation and make code look less chaotic
xam -- the starting left position of the tiles for the tile rendering loop
(with the offset and tile size scaled for a depth distance effect/look)
[note - after each loop thru y position - xx will be set to this
precalculated value]
yy -- starting y or top position of tiles for tile rendering loop
(same idea as with xam)
[note - this is not same as with xx cuz yy is the outside loop]
int
ma,mb;
VEC pz; pz.z=0.0f;
ar=tile_x-13;
SAFE_DO {
if
((b<0)||(b>LEVEL_HEIGHT)) {b++; yy+=aam; continue
;} //skip if out of bounds
if
(yy>(g_height+100)) break
;
a=ar;
if
(a>=0) if
((tile[g_tiles[a][b].i].r.bottom+yy)<-100) {b++; yy+=aam; continue
;}
Note - we must make sure a and b are valid array indices before attempting
a memory access with them - which is why we have if a>=0 for example..
We loop through the tiles vertically (based on scaled amount 64*scale - aam) and
skip ahead through the loop if out of bounds - or stop if below the bottom row
(b>y>g_height+100) [+100 as padding in case of large negative offset of tile - might need more]
float
xx=xam;
After each yy loop we reset xx to xam for the horizontal tile loop
SAFE_DO {
if
((a<0)||(a>LEVEL_WIDTH)) {a++; xx+=aam; continue
;} //skip it if there's nothing there
ma=g_tiles[a][b].m_a; mb=g_tiles[a][b].m_b;
if
((ma!=a)||(mb!=b)) {a++; xx+=aam; continue
;} //only draw the master tile!
//Only draw if it's actually occupied by a valid tile type:
if
(g_tiles[a][b].type>-1) {
int
ind=g_tiles[a][b].i;
if
((tile[ind].layer_type==LAYER_FLAT)||(tile[ind].layer_type==LAYER_BOX)) {a++; xx+=aam; continue
;}
if
(xx>(g_width+100)) break
;
if
((tile[ind].r.right+xx)<-100) {a++; xx+=aam; continue
;}
Get the tile index for checking tile info - skip
ahead if the tile doesn't have back layers or is out of bounds...
Next - get the actual rendering position by adding the scaled offset of
the tile to its position - and set the transformation and draw it...
pz.x=xx+g_tiles[ma][mb].offset.x*scale;
pz.y=yy+g_tiles[ma][mb].offset.y*scale;
g_sprite_trans=D3DXVECTOR2(pz.x,pz.y);
D3DXMatrixTransformation2D(&g_matrix_2d,&g_sprite_center,0.0,&g_sprite_scale,&g_sprite_rot_center,0,&g_sprite_trans);
g_sprite->SetTransform(&g_matrix_2d);
g_sprite->Draw(tile_texture.tx,&tile[ind].r,NULL,NULL,0xBBAAAAAA);
}
xx+=aam; a++;
}SAFE_WHILE(a<tile_x+13);
yy+=aam; b++;
}SAFE_WHILE(b<tile_y+10);
D3DXMatrixIdentity(&g_matrix_2d); g_sprite->SetTransform(&g_matrix_2d);
}//draw_back_tile_layers
// D R A W F R O N T T I L E L A Y E R S
This works basically the same way as the last one
except that we are drawing in front of the tiles (layer2 and layer3)
With front layers - if done well - we can make a fur like effect
adding more depth to fiberous materials like grass, fuzz, feather, hay, etc...
I found 2 extra layers works fine - in fact you could use multiple scales
applied to only one extra layer image to do the same trick.
void
draw_front_tile_layers(float
scale, int
layer) {
float
aam=64*scale;
g_sprite_scale=D3DXVECTOR2(scale,scale);
int
a,ar,ma,mb,b=tile_y-10;
float
yy=g_half_height-64*scale*10+(int
)tile_y_off*scale;
float
xam=g_half_width-aam*13+(int
)tile_x_off*scale;
ar=tile_x-13;
VEC pz; pz.z=0.0f;
SAFE_DO;
if
((b<0)||(b>LEVEL_HEIGHT)) {b++; yy+=aam; continue
;} //skip if out of bounds
if
(yy>(g_height+100)) break
;
a=ar;
if
(a>=0) if
((tile[g_tiles[a][b].i].r.bottom+yy)<-100) {b++; yy+=aam; continue
;}
float
xx=xam;
SAFE_DO;
if
((a<0)||(a>LEVEL_WIDTH)) {a++; xx+=aam; continue
;} //skip it if there's nothing there
ma=g_tiles[a][b].m_a; mb=g_tiles[a][b].m_b;
if
((a!=ma)||(b!=mb)) {a++; xx+=aam; continue
;}
if
(g_tiles[a][b].type>-1) {
int
ind=g_tiles[a][b].i;
if
(layer==2) if
(tile[ind].layer2.right<1) {a++; xx+=aam; continue
;}
if
(layer==3) if
(tile[ind].layer3.right<1) {a++; xx+=aam; continue
;}
if
(xx>(g_width+100)) break
;
if
((tile[ind].r.right+xx)<-100) {a++; xx+=aam; continue
;}
pz.x=xx+g_tiles[ma][mb].offset.x*scale;
pz.y=yy+g_tiles[ma][mb].offset.y*scale;
g_sprite_trans=D3DXVECTOR2(pz.x,pz.y);
D3DXMatrixTransformation2D(&g_matrix_2d,&g_sprite_center,0.0,&g_sprite_scale,&g_sprite_rot_center,0,&g_sprite_trans);
g_sprite->SetTransform(&g_matrix_2d);
if
(layer==2) g_sprite->Draw(tile_texture.tx,&tile[ind].layer2,NULL,NULL,0xFFFFFFFF);
if
(layer==3) g_sprite->Draw(tile_texture.tx,&tile[ind].layer2,NULL,NULL,0xFFFFFFFF);
}
xx+=aam; a++;
}while
(a<tile_x+13);
yy+=aam; b++;
}while
(b<tile_y+10);
D3DXMatrixIdentity(&g_matrix_2d); g_sprite->SetTransform(&g_matrix_2d);
}//draw_front_tile_layers
//------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------
I keep all the editor code below and all the game code above.
If you want you can right click and use outlining options to
collapse selected sections of code... Unfortunately it doesn't save the
customized selection outline -- but if you're doing a long session...
You could also do this in another module(a bit tricky tho) - but personally I
prefer to leave it below...
//collapsable editor code - region starts here.......................
#ifdef
ALLOW_EDIT //--------- G A M E E D I T O R--------------------------------
float
panx=0, pany=0;
panx and pany are for keeping track of horizontal and vertical
panning (done with dragging with left mouse button held down)
// S T R I N G C O N T A I N S
//can send strings of minimum 4 characters long to use as filters.
//you can use NULL if you are only using 1 or 2 filters like this: string_contains(data,"blab",NULL,NULL)
bool
string_contains(WIN32_FIND_DATA &data, const
char
* str1, const
char
* str2, const
char
* str3) { if
(str1==NULL) return
false;
if
((data.cFileName[0]==str1[0])&&(data.cFileName[1]==str1[1])&&(data.cFileName[2]==str1[2])&&(data.cFileName[3]==str1[3])) return
true; if
(str2==NULL) return
false;
if
((data.cFileName[0]==str2[0])&&(data.cFileName[1]==str2[1])&&(data.cFileName[2]==str2[2])&&(data.cFileName[3]==str2[3])) return
true; if
(str3==NULL) return
false;
if
((data.cFileName[0]==str3[0])&&(data.cFileName[1]==str3[1])&&(data.cFileName[2]==str3[2])&&(data.cFileName[3]==str3[3])) return
true;
return
false;
}
I'm just using the above to filter out file names that start
with certain 4 character words - like if I don't want to
see backup files for example - I could add the filter str as BACK
so any file that starts with BACK will not be listed...
//---------------------
// C H O O S E F I L E
//---------------------
//allow user to select a file from list of available files of some type
bool
choose_file(char
filename[100], const
char
* file_directory, const
char
* search_for, const
char
* ignore1, const
char
* ignore2, const
char
* ignore3,
const
char
* str1) {
Allow user to choose a file specifying directory and file type
to search for (ie: .gmp) -- and also can specify some file names to ignore...
bool
done=false, b_use_backup_directory=false;
POINT cp;
WIN32_FIND_DATA data;
char
directory[MAX_PATH];
char
original_file[100]; strcpy_s(original_file, filename);
int
num_files;
char
files[300][100]; //allow up to 300 files of 100 char
files[][] -- will be a list of file names available to choose from within
the directory / search-path
(goto label)
find_files_addr:
num_files=0;
strcpy_s(directory, MAX_PATH, g_current_directory);
strcat_s(directory, MAX_PATH, file_directory);
if
(b_use_backup_directory) strcat_s(directory,"\\backup\\");
strcat_s(directory, search_for);
ie: c:\...\projects\game\tilemaps\*.tmp
HANDLE h=FindFirstFile(directory,&data);
if(h!=INVALID_HANDLE_VALUE) {
SAFE_DO;
if (string_contains(data,ignore1,ignore2,ignore3)) continue; //filter out stuff we don't want to see
for(int i=0; i<lstrlen(data.cFileName); i++) files[num_files][i]=char(data.cFileName[i]);
files[num_files][lstrlen(data.cFileName)]='\0';
(gets current file name into files[][])
num_files++;
} while(FindNextFile(h,&data));
} else {ERR("Directory doesn't exist."); FindClose(h); return b_use_backup_directory;}
FindClose(h);
Now that we have all the file names - let's loop
for user input and wait for them to select one of the files
int a=0,scroll_pos=0,marked=-1;
SAFE_DO;
HANDLE_LOST_DEVICES( , ); BEGIN_SCENE("choose file"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
TEXT_POSITION(10,10); PRINT_TEXT(str1); //ie: "please choose a file to configure"
if (PRESS_ESCAPE) {strcpy_s(filename,100,original_file); done=true;}
if (KEYPRESS(ENTER)) done=true;
if (KEYPRESS(UP)) if (scroll_pos>0) scroll_pos--;
if (KEYPRESS(DOWN)) if (scroll_pos<(num_files-1)) scroll_pos++;
if (KEYPRESS(CONTROL)) {
if (KEYPRESS(B)) {
if (!b_use_backup_directory) {b_use_backup_directory=true; Sleep(160); g_sprite->End(); END_SCENE("choose file"); goto find_files_addr;}
else {b_use_backup_directory=false; Sleep(160); g_sprite->End(); END_SCENE("choose file"); goto find_files_addr;}
}
}
Get input for scrolling list up and down and if CNTRL+B - set
it to get the list from the backup directory (goto find_files_addr)
LONG p=40; a=0;
int sp=scroll_pos;
SAFE_DO;
if (sp==marked) text_color=0xFFAA0000;
TEXT_POSITION(10,p);
PRINT_TEXT(files[sp]); text_color=default_color;
p+=20; a++; sp++; if (sp>=num_files) break;
}while(a<25); //show up to 25 files on screen
Shows the list of files
Next we show some instructions:
TEXT_POSITION(500,270); PRINT_TEXT("Default file selection: ");
TEXT_POSITION(500,290); text_color=0xFFAA0000; PRINT_TEXT(filename); text_color=default_color;
TEXT_POSITION(500,330); PRINT_TEXT("Press ENTER to choose this.");
TEXT_POSITION(500,530); text_color=0xFF443322; PRINT_TEXT("Press CTRL+B to switch to backup directory."); text_color=default_color;
if (left_mouse_clicked) {
left_mouse_clicked=false;
GetCursorPos(&cp); UpdateMousePos(&cp);
p=40; a=0; sp=scroll_pos;
SAFE_DO;
TEXT_POSITION(10,p); RECT tp; tp.left=text_position.left; tp.right=100; tp.top=text_position.top-2; tp.bottom=tp.top+20;
(looping) for each text's RECT check if there's a mouse
click with mouse coordinates set inside that rect.. - check for selection...
(..make sure to call message_pump so mouse input and stuff us updated too)
if (mouse_inside(tp)) {
strcpy_s(filename, 100, files[sp]); marked=sp;
break;
}
p+=20; a++; sp++; if (sp>=num_files) break;
}while(a<25);
}
g_sprite->End(); END_SCENE("choose file"); message_pump();
}while(!done);
return b_use_backup_directory;
}//choose file
// G E T N U M B E R
int
GetNumber(const
char
* str, LONG x, LONG y) {
if
(PollUserInput(str,x, y, 5)) {
return
STR_TO_INT(g_text);
}
return
-1000;
}
A function which polls for number input and returns as actual int.
// C H E C K I D C L I C K
//used for determining which coordinate we're trying to input for (for manual input of bounding rectangle)
enum
CLICK_ID {NO_ID, START_X, START_Y, END_X, END_Y} this_clickid;
void
check_id_click(int
new_id, int
&id, RECT &tile_rect) {
if
(new_id!=id) {
int
n=STR_TO_INT(g_text);
switch
(id) {
case
START_X: tile_rect.left=n; break
;
case
START_Y: tile_rect.top=n; break
;
case
END_X: tile_rect.right=n; break
;
case
END_Y: tile_rect.bottom=n; break
;
}
if
(new_id>0) {
switch
(new_id) {
case
START_X: sprintf_s(g_text,_T("%d"),tile_rect.left); break
;
case
START_Y: sprintf_s(g_text,_T("%d"),tile_rect.top); break
;
case
END_X: sprintf_s(g_text,_T("%d"),tile_rect.right); break
;
case
END_Y: sprintf_s(g_text,_T("%d"),tile_rect.bottom); break
;
}
g_text_index=strlen(g_text);
int
a=g_text_index; SAFE_DO; g_text[a]='\0';a++;}while
(a<200);
} else
reset_user_input_memory();
}
id=new_id;
}
// D R A W B O X (bounding selection rect)
void
draw_box(RECT rect, D3DCOLOR color) {
if
(!g_sprite) {REPORT("g_sprite?"); return
;} if
(!line) {REPORT("line?"); return
;}
D3DXVECTOR2 lineList[]={D3DXVECTOR2((FLOAT)rect.left, (FLOAT)rect.top),D3DXVECTOR2((FLOAT)rect.right,(FLOAT)rect.top),D3DXVECTOR2((FLOAT)rect.right,(FLOAT)rect.bottom),D3DXVECTOR2((FLOAT)rect.left,(FLOAT)rect.bottom),D3DXVECTOR2((FLOAT)rect.left,(FLOAT)rect.top)};
line->SetWidth(1.0f);
g_sprite->End();
line->Begin();
line->Draw(lineList, 5, color);
line->End();
g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
}
I probably should have called this function draw_rect or draw_selection..
especially since we have a function for drawing cube tiles - it could
be confusing... This sets up a line list from a supplied rect and draws
the lines that make up the selection rect.
// S N A P C O O R D
void
snap_coord(LONG &coord, int
unit) {
if
(unit<1) return
;
LONG num=coord/unit*unit;
coord=num;
}
This takes a number and snaps it to the closest point that
is part of a unit of divisions of some hidden snap grid. Let's
say I have a point(22,46) and I want the closest point in units
of 5 for example -- this would make the coordinate (20,45)
This is useful for example for lining up images with each other more easily.
// T I L E D E L E T E
void
TileDelete(int
&i) {
if
(g_num_tile_types==0) {
if
(i!=0) REPORT_D("i!=1 : ",i);
ZeroMemory(&tile[0], sizeof
(tile[0])); tile[0].depth=INFRONT; tile[0].frame_direction=1; tile[0].type=SOLID;
g_num_tile_types=0; i=0;
return
;
}
if
(i==g_num_tile_types) {
ZeroMemory(&tile[i], sizeof
(tile[i])); tile[i].depth=INFRONT; tile[i].frame_direction=1; tile[i].type=SOLID;
if
(i>0) i--; if
(g_num_tile_types>0) g_num_tile_types--;
return
;
}
int
a=i;
SAFE_DO;
memcpy(&tile[a], &tile[(a+1)], sizeof
tile[a]);
a++;
}while
(a<=g_num_tile_types); a=g_num_tile_types;
ZeroMemory(&tile[a], sizeof
(tile[a])); tile[a].depth=INFRONT; tile[a].frame_direction=1; tile[a].type=SOLID;
if
(i>0) i--; if
(g_num_tile_types>0) g_num_tile_types--;
}
This deletes a tile type (for tilemap editor).
First I make sure to just delete the tile if it is the last tile
in the list (and then return) - but if it is not - I need to squash
the list a bit - so I copy all the tiles after the one being deleted,
back one index in the list - and then delete the last entry cuz
it's now a redundant copy of the 2nd last one. Then subtract 1 from
number of tile types..
//-----------------------
// S A V E T I L E M A P
//-----------------------
void
SaveTilemap() {
int
done=0;
char
filename[100];
g_sprite->End(); END_SCENE("save tilemap");
It is assumed that we were rendering a scene when this
function was called - so we end it and start a new one
for the user input loop below..
reset_user_input_memory(); Sleep(250);
sprintf_s(filename, _T("tilemap%d.tma"), g_level); //default tilemap to save as (based on g_level)
Reset user input stuff and set the default file
name to be tilemap1.tma or tilemap2.tma, etc - depending on current
level the editor's set to edit.
char
file[100];
do
{
BEGIN_SCENE("save tilemap"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
TEXT_POSITION(10,g_half_height-20); text_color=default_color;
PRINT_TEXT("Press ENTER to accept default filename or type in a new file name to SAVE AS: ");
if
(PollUserInput("NEW FILE: ", 10, g_half_height+25, 20)) {
strcat_s(g_text,".tma");
strcpy_s(filename,100,g_text);
strcpy_s(file,"Tilemaps/"); strcat_s(file,filename);
REPORT_S("file = ",file);
done=1;
} else
if
(KEYPRESS(ENTER)) { strcpy_s(file,"Tilemaps/"); strcat_s(file,filename); REPORT_S("file = ",file); done=1;}
g_sprite->End(); END_SCENE("save tilemap");
if
(KEYPRESS(ESCAPE)) done=2;
}while
(done==0);
If PollUserInput receives a valid file name, the filename is created from
g_text(input from PollUserInput) with .tma extension added and file
becomes the filename with the Tilemaps path added in front of it - so
it will be saved in to the correct folder.
If PollUserInput does not receive a valid file name - we check
if the user pressed ENTER and if so we use the default level based
filename (ie: Tilemaps/tilemap1.tma)
If escape was pressed - we just exit the loop by setting done to 2
if
(done==1) {
FILE *f=NULL;
fopen_s(&f,file,"wb");
if
(!f) { REPORT("file creation error"); }
else
{
fwrite(&g_num_tile_types, sizeof
(g_num_tile_types), 1, f);
fwrite(&tile, sizeof
(tile[0]), g_num_tile_types, f);
fclose(f);
}
We open the file for wrtie binary (wb) and first specify
the number of tile types(so we can load properly later) - and then write
then actual array of structs by specifying the size and quantity.
}
BEGIN_SCENE("save tilemap"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
}
//-----------------------
// L O A D T I L E M A P (choice loader)
//-----------------------
void
LoadTilemap() {
char
file[100];
char
filename[100];
FILE *f=NULL;
sprintf_s(filename, _T("tilemap%d.tma"), g_level); //default tilemap to load (based on g_level)
g_sprite->End(); END_SCENE("load tilemap");
//file, what to look for, what to ignore
if
(!choose_file(filename, "\\Tilemaps\\", "*.tma", "far_","mid_","edit", "Please choose a tile bitmap to conf: "))
strcpy_s(file,"Tilemaps/");
else strcpy_s(file,"Tilemaps/backup/");
Allow user to choose the file to load from a list
(choose_file function - excluding files that start with far_,mid_,edit...)
and if it was a backup file - we'll load from the Tilemaps/backup directory
strcat_s(file,filename);
REPORT_S("file = ",file);
fopen_s(&f, file, "rb");
if (!f) { REPORT("file read error(tilemap)"); } else {
fread(&g_num_tile_types, sizeof(g_num_tile_types), 1, f);
//fread(&tile, sizeof(tile[0]), 521, f);
fread(&tile, sizeof(tile[0]), g_num_tile_types, f);
fclose(f);
}
Load in the tile array based on g_num_tile_types
and restart previous scene...
BEGIN_SCENE("load tilemap"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
}
//-----------------------
// C O N F I G T I L E S
//-----------------------
// for user configuration of how tiles are set up (tilemap / tiles bitmap information setup)
enum
CONFIG_MODE {SELECT_TILE, CONFIG_RECT, CONFIG_TILE_TYPES, ADD_LAYER2, ADD_LAYER3, ADD_ANIMATION} this_configmode;
void
config_tiles() {
int
config_mode=SELECT_TILE;
bool
last_left_mouse_button=false, first_hit=true, snap=true, s_pressed=false;
int
last_mouse_pos_x=-1, last_mouse_pos_y=-1;
int
copy_offset_from=0, copy_rect_from=0;
int
id=0, num_tile_clusters=0, num=0, last_id=0, current_tile=g_num_tile_types;
bool
new_tile=true, delete_key=true, press_a=true, press_d=true;
POINT cp;
TEXTURE texture;
char
filename[100]; //must be 100 (must be same as others for strcpy to work)
char
text[100];
VEC last_mouse; GetCursorPos(&cp); UpdateMousePos(&cp); last_mouse.x=mouse_pos.x; last_mouse.y=mouse_pos.y;
int
mouse_button=VK_RBUTTON;
if
(GetSystemMetrics(SM_SWAPBUTTON)) mouse_button=VK_LBUTTON;
(I preset mouse_button as the right mouse button - but if the mouse is set up
backwards - it interprets the signal for left mouse button as right)
sprintf_s(filename, _T("tiles%d.dds"), g_level); //default tiles to edit (based on g_level)
//file, what to look for, what to ignore
char
file[100];
if
(!choose_file(filename, "\\Graphics\\", "*.dds", "far_","mid_","edit", "Please choose a tile bitmap to configure: "))
strcpy_s(file,"Graphics/");
else strcpy_s(file,"Graphics/backup/");
strcat_s(file,filename);
First we call choose_file - to allow the user to select a bitmap file (.dds)
which holds images for tiles we'd like to prepare..
left_mouse_clicked=false; Sleep(300);
texture.LoadTexture(file);
then - actually load the file/image into a texture
bool
done=false;
RECT r, tile_rect, panned_rect;>
Initialize tile rect (used for showing selected region that is tile):
SetRectEmpty(&tile_rect);
VEC pos; pos.z=0;
Start the main config tiles loop:
SAFE_DO;
DWORD starting_point = GetTickCount();
HANDLE_LOST_DEVICES( SAFE_RELEASE(texture.tx);, texture.LoadTexture(file); );
BEGIN_SCENE("config tiles"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
if
(PRESS_ESCAPE) done=true;
First we store the time at the beginning of the loop (for timing)
Handle any lost graphics device - restoring also the additional texture
Start the sprite scene - this time just in plain 2D rendering mode (not OBJECTSPACE)
pos.x=0+panx; pos.y=0+pany;
DRAWC(grid,NULL,&pos,0xFF999999);
DRAW(texture,NULL,&pos);
TEXT_POSITION(g_half_width,10); PRINT_TEXT(filename);
position the grid and tiles texture at 0,0 with offset of panx, pany
(which is so you can pan the view by right-dragging the mouse)
GetCursorPos(&cp); UpdateMousePos(&cp);
mouse_pos.x-=panx; mouse_pos.y-=pany; //move mouse coords relative to pan
sprintf_s(text, _T("(%d, %d)"), (int
)mouse_pos.x, (int
)mouse_pos.y);
TEXT_POSITION(g_width-120,10); text_color=0xFFAA0000; PRINT_TEXT(text); text_color=0xFFAA0088;
(shows mouse coords over tiles map - useful for precision selecting)
switch
(config_mode) {
case
SELECT_TILE:
if
(g_num_tile_types<1) {config_mode=CONFIG_RECT; new_tile=true; break
;}
draw_black_rect(0,g_half_height+100,250,g_half_height+200);
PRINT_TEXT_EXT(10,g_half_height+120,0xFFDD0033,"Left click to select a tile to edit");
PRINT_TEXT_EXT(10,g_half_height+140,0xFFAA0022,"(or press S to start a new tile)");
draw_black_rect(0,g_height-40,250,g_height);
TEXT_POSITION(10,g_height-40); PRINT_TEXT("(hold right-mouse button to pan)");
if
(left_mouse_clicked) {
int
a=0;
SAFE_DO;
if
(mouse_inside(tile[a].r)) {REPORT_D("Found: ",a); current_tile=a; CopyRect(&tile_rect,&tile[a].r); config_mode=CONFIG_RECT; new_tile=false; break
;}
a++;
}while
(a<=g_num_tile_types);
left_mouse_clicked=false;
}
if
(KEYPRESS(S)) {current_tile=g_num_tile_types; config_mode=CONFIG_RECT; new_tile=true;}
break
;
case
ADD_ANIMATION:
case
ADD_LAYER2:
case
ADD_LAYER3:
case
CONFIG_RECT: {// C O N F I G R E C T
int
i=current_tile;
PRINT_T(g_width-200,10,0xFFCCCC00,"Edit/Files:");
PRINT_T(g_width-200,10,0xFFDD2200,"ALT+S / ALT+O SAVE/OPEN tile layout");
if
(KEYPRESS(ALT)) {
if
(KEYPRESS(S)) SaveTilemap();
if
(KEYPRESS(O)) LoadTilemap();
}
draw_black_rect(0,g_half_height+100,250,g_half_height+200);
TEXT_POSITION(10,g_half_height+100); PRINT_TEXT_D("Starting position X: ",tile_rect.left,175);
TEXT_POSITION(10,g_half_height+120); PRINT_TEXT_D("Starting position Y: ",tile_rect.top,175);
TEXT_POSITION(10,g_half_height+140); text_color=0xFF007722; PRINT_TEXT("(press S to toggle snap)");
TEXT_POSITION(200, g_half_height+140); text_color=0xFF3300AA;
if
(snap) PRINT_TEXT("(on)"); if
(!snap) PRINT_TEXT("(off)");
text_color=text_color=0xFFAA0088;
TEXT_POSITION(10,g_half_height+160); PRINT_TEXT_D("Ending position X: ",tile_rect.right,175);
TEXT_POSITION(10,g_half_height+180); PRINT_TEXT_D("Ending position Y: ",tile_rect.bottom,175);
if
(g_num_tile_types>0) {
PRINT_TEXT_EXT(10,g_height-30,0xFF882233,"TAB to go to tile selector");
if
(KEYPRESS(TAB)) config_mode=SELECT_TILE;
}
draw_black_rect(0,g_height-40,250,g_height);
TEXT_POSITION(10,g_height-40); PRINT_TEXT("(hold right-mouse button to pan)");
switch
(config_mode) {
case
CONFIG_RECT: TEXT_POSITION(10,g_height-20); PRINT_TEXT("Press SPACE to accept RECT coordinates"); break
;
case
ADD_LAYER2: TEXT_POSITION(10,g_height-20); PRINT_TEXT("Press SPACE to accept Layer2 coordinates"); break
;
case
ADD_LAYER3: TEXT_POSITION(10,g_height-20); PRINT_TEXT("Press SPACE to accept Layer3 coordinates"); break
;
case
ADD_ANIMATION:
TEXT_POSITION(10,g_height-20); PRINT_TEXT("Press SPACE to accept animation frame coordinates");
break
;
}
if
(!left_mouse_clicked) {
first_hit=true; last_mouse_pos_x=-1; last_mouse_pos_y=-1;last_left_mouse_button=false;
if
((tile_rect.right>tile_rect.left)&&(tile_rect.bottom>tile_rect.top)) {
CopyRect(&panned_rect,&tile_rect); MoveRect(panned_rect,(LONG)panx,(LONG)pany);
draw_box(panned_rect,0xFFEE2200);
}
}
if
(left_mouse_clicked) {
bool
hit=false;
mouse_pos.x+=panx; mouse_pos.y+=pany;
r.left=10; r.right=260; r.top=g_half_height+100; r.bottom=r.top+18;
if
(mouse_inside(r)) {check_id_click(START_X,id,tile_rect); hit=true;}
else
{
r.top+=20; r.bottom+=20;
if
(mouse_inside(r)) {check_id_click(START_Y,id,tile_rect); hit=true;}
else
{
r.top+=40; r.bottom+=40;
if
(mouse_inside(r)) {check_id_click(END_X,id,tile_rect); hit=true;}
else
{
r.top+=20; r.bottom+=20;
if
(mouse_inside(r)) {check_id_click(END_Y,id,tile_rect); hit=true;}
}
}
}
mouse_pos.x-=panx; mouse_pos.y-=pany;
if
(hit) left_mouse_clicked=false;
if
(!hit) {
check_id_click(0,id,tile_rect);
//test for mouse-based selection:
if
(last_left_mouse_button) {
if
((first_hit)||((mouse_pos.x>tile_rect.left)&&(mouse_pos.y>tile_rect.top))) {
if
(first_hit) {
first_hit=false;
tile_rect.left=last_mouse_pos_x; tile_rect.right=(LONG)mouse_pos.x;
tile_rect.top=last_mouse_pos_y; tile_rect.bottom=(LONG)mouse_pos.y;
if
(snap) snap_coord(tile_rect.left,16);
if
(snap) snap_coord(tile_rect.right,16);
if
(snap) snap_coord(tile_rect.top,16);
if
(snap) snap_coord(tile_rect.bottom,16);
} else
{
tile_rect.right=(LONG)mouse_pos.x;
tile_rect.bottom=(LONG)mouse_pos.y;
if
(snap) snap_coord(tile_rect.right,16);
if
(snap) snap_coord(tile_rect.bottom,16);
CopyRect(&panned_rect,&tile_rect); MoveRect(panned_rect,(LONG)panx,(LONG)pany);
draw_box(panned_rect,0xffeebbff);
}
}
} else
first_hit=true;
last_mouse_pos_x=(int
)mouse_pos.x;
last_mouse_pos_y=(int
)mouse_pos.y;
last_left_mouse_button=true;
}
}
if
(id>0) { //if get number:
num=GetNumber(" ",175,r.top+10);
if
(num>-1000) {
switch
(id) {
case
START_X: tile_rect.left=num; break
;
case
START_Y: tile_rect.top=num; REPORT_D("top=",tile_rect.top);break
;
case
END_X: tile_rect.right=num; REPORT_D("right=",tile_rect.right);break
;
case
END_Y: tile_rect.bottom=num; REPORT_D("bottom=",tile_rect.bottom);break
;
}
id=0;
reset_user_input_memory();
}
}
if
(config_mode==ADD_ANIMATION) {
if
(((tile_rect.right-tile_rect.left)>0)&&((tile_rect.bottom-tile_rect.top)>0)) {
if
((!press_a)&&(KEYPRESS(A))) {
press_a=true;
int
k=0; SAFE_DO; //make sure not to make unwanted copies of this animation frame
if
((tile[i].animation[k].left==tile_rect.left)&&(tile[i].animation[k].right==tile_rect.right)&&(tile[i].animation[k].top==tile_rect.top)&&(tile[i].animation[k].bottom==tile_rect.bottom)) {k=99999; break
;}
k++;
}while
(k<tile[i].num_frames);
if
(k<99999) {
CopyRect(&tile[i].animation[tile[i].num_frames],&tile_rect);
CopyRect(&tile_rect, &tile[i].r);
if
(tile[i].num_frames<MAX_TILE_ANIMATION) tile[i].num_frames++; else
REPORT_D("tile num_frames will exceed MAX_TILE_ANIMATION ",tile[i].num_frames);
}
}
if
(KEY_IS_UP(KEY_A)) press_a=false;
}
if
((!press_d)&&(KEYPRESS(D))) {
press_d=true;
if
(tile[i].num_frames>0) {
tile[i].num_frames--;
} else
tile[i].num_frames=0;
}
if
(KEY_IS_UP(KEY_D)) press_d=false;
//show animation frame selections:
RECT dum_r; SetRectEmpty(&dum_r);
if
(tile[i].num_frames>0) {
int
u=0; SAFE_DO(u);
CopyRect(&dum_r,&tile[i].animation[u]); MoveRect(dum_r,(LONG)panx,(LONG)pany);
int
k=0; k=u*10; if
(k>255) k=255;
D3DCOLOR col=D3DCOLOR_ARGB(255,k,(255-k),0);
draw_box(dum_r, col); TEXT_POSITION(dum_r.left, dum_r.top); text_color=col;
sprintf_s(g_str, _T("%d"), (u+1)); PRINT_TEXT(g_str);
u++;
}while
(u<tile[i].num_frames);
}
}
if
(KEYPRESS(SHIFT)) {
if
(KEYPRESS(C)) {copy_rect_from=i;}
if
(KEYPRESS(V)) {
tile_rect.right=tile_rect.left+(tile[copy_rect_from].r.right-tile[copy_rect_from].r.left);
tile_rect.bottom=tile_rect.top+(tile[copy_rect_from].r.bottom-tile[copy_rect_from].r.top);
};
}
if
(KEYPRESS(SPACE)) {
i=current_tile;
TEST_ARRAY(tile,g_num_tile_types); TEST_ARRAY(tile,i);
if
(((tile_rect.right-tile_rect.left)>0)&&((tile_rect.bottom-tile_rect.top)>0)) {
switch
(config_mode) {
case
ADD_LAYER2: CopyRect(&tile[i].layer2,&tile_rect); CopyRect(&tile_rect,&tile[i].r); break
;
case
ADD_LAYER3: CopyRect(&tile[i].layer3,&tile_rect); CopyRect(&tile_rect,&tile[i].r); break
;
case
CONFIG_RECT:
CopyRect(&tile[i].r,&tile_rect); CopyRect(&tile[i].o, &tile[i].r);
tile[i].ref=g_reference_maker; g_reference_maker++;
break
;
case
ADD_ANIMATION:
int
k=0; SAFE_DO; //make sure not to make unwanted copies of this animation frame
if
((tile[i].animation[k].left==tile_rect.left)&&(tile[i].animation[k].right==tile_rect.right)&&(tile[i].animation[k].top==tile_rect.top)&&(tile[i].animation[k].bottom==tile_rect.bottom)) {k=99999; break
;}
k++;
}while
(k<tile[i].num_frames);
if
(k>=99999) break
;
if
((tile[i].r.left!=tile_rect.left)||(tile[i].r.right!=tile_rect.right)||(tile[i].r.top!=tile_rect.top)||(tile[i].r.bottom!=tile_rect.bottom)) {
CopyRect(&tile[i].animation[tile[i].num_frames],&tile_rect);
CopyRect(&tile_rect, &tile[i].r);
if
(tile[i].num_frames<MAX_TILE_ANIMATION) tile[i].num_frames++; else
REPORT_D("tile num_frames will exceed MAX_TILE_ANIMATION ",tile[i].num_frames);
}
break
;
}
config_mode=CONFIG_TILE_TYPES;
} else
{
if
(PromptUser("No coordinates selected. Continue Anyway? (Y/N)")) {
config_mode=CONFIG_TILE_TYPES;
}
}
//offx=(LONG)tile[current_tile].offset.x; offy=(LONG)tile[current_tile].offset.y;
//if ((tile[current_tile].tiles_wide>0)&&(tile[current_tile].tiles_high>0)) CopyRect(&tile_rect,&tile[current_tile].o);
delete_key=true;
}
} break
; //^^^config_rect^^^
case
CONFIG_TILE_TYPES: // C O N F I G T I L E T Y P E S
RECT dum_r; SetRectEmpty(&dum_r);
int
i=current_tile;
TEST_ARRAY(tile,i);
CopyRect(&panned_rect,&tile[i].r); MoveRect(panned_rect,(LONG)panx,(LONG)pany); draw_box(panned_rect, 0xFFEE0000);
if
(tile[i].layer2.right>0) {CopyRect(&dum_r,&tile[i].layer2); MoveRect(dum_r,(LONG)panx,(LONG)pany); draw_box(dum_r, 0xFF330099);}
if
(tile[i].layer3.right>0) {CopyRect(&dum_r,&tile[i].layer3); MoveRect(dum_r,(LONG)panx,(LONG)pany); draw_box(dum_r, 0xFF110077);}
//show animation frame selections:
if
(tile[i].num_frames>0) {
int
u;
u=0; SAFE_DO(u);
CopyRect(&dum_r,&tile[i].animation[u]); MoveRect(dum_r,(LONG)panx,(LONG)pany);
int
k=0; k=u*10; if
(k>255) k=255;
D3DCOLOR col=D3DCOLOR_ARGB(255,k,(255-k),0);
draw_box(dum_r, col); TEXT_POSITION(dum_r.left, dum_r.top); text_color=col;
sprintf_s(g_str, _T("%d"), (u+1)); PRINT_TEXT(g_str);
u++;
}while
(u<tile[i].num_frames);
if
(tile[i].num_frames>=2) {
if
(tile[i].animation_type==NO_ANIM) {tile[i].animation_type=LOOPING; if
(tile[i].wait_time==0) tile[i].wait_time=12;}
tile[i].timer++;
if
(tile[i].animation_type==PING_PONG) {
if
(tile[i].timer>tile[i].wait_time) {tile[i].frame+=tile[i].frame_direction; tile[i].timer=0;}
if
(tile[i].frame>=tile[i].num_frames) {tile[i].frame_direction=-1; tile[i].frame--;}
if
(tile[i].frame<=0) tile[i].frame_direction=1;
} else
{
if
(tile[i].timer>tile[i].wait_time) {tile[i].frame++; tile[i].timer=0;}
if
(tile[i].frame>=tile[i].num_frames) tile[i].frame=0;
}
u=tile[i].frame;
VEC p; p.z=0; p.x=(float
)tile[i].r.left+panx; p.y=(float
)tile[i].r.top+pany;
DRAW(texture,&tile[i].animation[u],&p);
}
}
if
(tile[i].animation_type==SCROLL_Y) {
tile[i].scroll_pos.y+=tile[i].wait_time;//((float)tile[i].wait_time/10.0f);
if
(tile[i].scroll_pos.y<=tile[i].r.top) tile[i].scroll_pos.y=tile[i].r.bottom-(tile[i].r.top-tile[i].scroll_pos.y);
if
(tile[i].scroll_pos.y>=tile[i].r.bottom) tile[i].scroll_pos.y=tile[i].r.top+(tile[i].scroll_pos.y-tile[i].r.bottom);
}
if
(tile[i].animation_type==SCROLL_X) {
tile[i].scroll_pos.x+=tile[i].wait_time;//((float)tile[i].wait_time/10.0f);
if
(tile[i].scroll_pos.x<=tile[i].r.left) tile[i].scroll_pos.x=tile[i].r.right-(tile[i].r.left-tile[i].scroll_pos.x);
if
(tile[i].scroll_pos.x>=tile[i].r.right) tile[i].scroll_pos.x=tile[i].r.left+(tile[i].scroll_pos.x-tile[i].r.right);
}
if
((tile[i].animation_type==SCROLL_X)||(tile[i].animation_type==SCROLL_Y)) {
LONG overflow=0;
VEC p; p.z=0; p.x=(float
)tile[i].r.left+panx; p.y=(float
)tile[i].r.top+pany;
if
(tile[i].animation_type==SCROLL_X) {
overflow=(LONG)tile[i].scroll_pos.x-tile[i].r.left;
CopyRect(&dum_r,&tile[i].r);
dum_r.left=tile[i].r.left+1; dum_r.right=tile[i].r.right-overflow;
p.x=tile[i].scroll_pos.x+panx;
DRAW(texture,&dum_r,&p);
dum_r.left=dum_r.right;//-1;//
dum_r.right=tile[i].r.right;//-1;
p.x=(float
)tile[i].r.left+panx;//+1;
DRAW(texture,&dum_r,&p);
}
if
(tile[i].animation_type==SCROLL_Y) {
overflow=(LONG)tile[i].scroll_pos.y-tile[i].r.top;
CopyRect(&dum_r,&tile[i].r);
dum_r.top=tile[i].r.top+1; dum_r.bottom=tile[i].r.bottom-overflow;
p.y=tile[i].scroll_pos.y+pany;
DRAW(texture,&dum_r,&p);
dum_r.top=dum_r.bottom;//-1;//
dum_r.bottom=tile[i].r.bottom;//-1;
p.y=(float
)tile[i].r.top+pany;//+1;
DRAW(texture,&dum_r,&p);
}
}
if
((tile[i].tiles_wide<=0)&&(tile[i].tiles_high<=0)) { //make a guess if this is the first time
if
(tile[i].r.right>tile[i].r.left) {
if
(tile[i].tiles_wide<1) tile[i].tiles_wide=(tile[i].r.right+1-tile[i].r.left)/64;
}
if
(tile[i].tiles_wide<1) tile[i].tiles_wide=1;
if
(tile[i].r.bottom>tile[i].r.top) {
if
(tile[i].tiles_high<1) tile[i].tiles_high=(tile[i].r.bottom+1-tile[i].r.top)/64;
}
if
(tile[i].tiles_high<1) tile[i].tiles_high=1;
REPORT("reset");
} else
("not reset");
int
hp=g_width-220;
draw_black_rect(hp,20,g_width,580); text_color=0xFFCCCC00;
TEXT_POSITION(hp,20); PRINT_TEXT("Type:");
PRINT_T(hp,10,0xFFBB0000,"1 = SOLID");
PRINT_T(hp,10,0xFF999999,"2 = PASSABLE");
PRINT_T(hp,10,0xFF0000BB,"3 = LIQUID");
PRINT_T(hp,10,0xFF5511AA,"4 = BOUNCE");
PRINT_T(hp,10,0xFFAA2266,"5 = BUTTON");
PRINT_T(hp,10,0xFF8833AA,"6 = Jump Passable Solid");
PRINT_T(hp,20,0xFFCCCC00,"Infront/Behind:");
PRINT_T(hp,10,0xFF888888,"Q = BEHIND");
PRINT_T(hp,10,0xFFAAAAAA,"W = INFRONT");
PRINT_T(hp,10,0xFF555577,"SHIFT Q = FAR BEHIND");
PRINT_T(hp,10,0xFFCCAAAA,"SHIFT W = NEAR INFRONT");
PRINT_T(hp,20,0xFFCCCC00,"Effect:");
PRINT_T(hp,10,0xFF777777,"Numpad 0 = NONE");
PRINT_T(hp,10,0xFFDD7700,"Numpad 5 = DAMAGE");
PRINT_T(hp,10,0xFFDD7700,"(numpad)SPIKES(up,dwn,left,rght)");
PRINT_T(hp,10,0xFF009933,"Break/Pause = BREAKS");
PRINT_T(hp,10,0xFF0055AA,"* = slippery");
PRINT_T(hp,10,0xFF995522,"+ = door");
PRINT_T(hp,10,0xFF9955FF,"- = portal");
PRINT_T(hp,20,0xFFCCCC00,"Layers:");
PRINT_T(hp,10,0xFF888888,"E = add layer2 (shift+E=delete)");
PRINT_T(hp,10,0xFFAAAAAA,"R = add layer3 (shift+R=delete)");
PRINT_T(hp,10,0xFF990011,"T = change layering type");
PRINT_T(hp,20,0xFFCCCC00,"Animation:");
PRINT_T(hp,10,0xFF998888,"A = Add animation frame");
PRINT_T(hp,10,0xFF11DD88,"H = No animation");
PRINT_T(hp,10,0xFF775555,"D = delete animation frame");
PRINT_T(hp,10,0xFF008888,"K = scroll horizontally (timer=speed)");
PRINT_T(hp,10,0xFF888800,"L = scroll vertically (timer=speed)");
PRINT_T(hp,10,0xFF1234BB,"F = Loop animation (1234,1234)");
PRINT_T(hp,10,0xFF12BB34,"G = Ping-Pong animation (1234,4321)");
PRINT_T(hp,10,0xFFAA8800,"{ = set timer/timing (slower)");
PRINT_T(hp,10,0xFFAA8800,"} = set timer/timing (faster)");
PRINT_T(hp,10,0xFF333333,"...");
PRINT_T(hp,10,0xFFCCCC00,"Edit/Files:");
PRINT_T(hp,10,0xFFDD2200,"ALT+S / ALT+O SAVE/OPEN tile layout");
PRINT_T(hp,10,0xFFAA8800,"PageUp/PageDown = tab thru tiles");
PRINT_T(hp,10,0xFF889900,"(Delete = delete tile)");
PRINT_T(hp,10,0xFF00DD33,"(arrow keys = tile offset)");
PRINT_T(hp,10,0xFFAA7700,"Ctrl+C/Ctrl+V (copy paste offset)");
PRINT_T(hp,10,0xFF449900,"Shift+C/Shift+V(copy paste RECT size)");
TEXT_POSITION(hp,500); PRINT_TEXT("(S = toggle snap)");
TEXT_POSITION(hp,520); PRINT_TEXT("(left click: set top-left tile)");
PRINT_TEXT_EXT(hp,540,0xFF008800,"(hold right-mouse button to pan)");
PRINT_TEXT_EXT(hp,560,0xFFDD2200,"Press ENTER when finished with tile");
PRINT_TEXT_EXT(hp,580,0xFF006600,"(TAB to return to rect edit)");
text_color=0xFFAA0088;
//draw the effected tiles:
if
(KEYPRESS(TAB)) {config_mode=CONFIG_RECT; Sleep(150);}
if
(KEYPRESS(1)) tile[i].type=SOLID;
if
(KEYPRESS(2)) tile[i].type=PASS;
if
(KEYPRESS(3)) tile[i].type=LIQUID;
if
(KEYPRESS(4)) tile[i].type=BOUNCE;
if
(KEYPRESS(5)) tile[i].type=BUTTON;
if
(KEYPRESS(6)) tile[i].type=JUMP_PASSABLE;
if
(KEYPRESS(Q)) {tile[i].depth=BEHIND; tile[i].offset.z=0.3f; if
(KEYPRESS(SHIFT)) {tile[i].depth=FAR_BEHIND; tile[i].offset.z=0.1f;}}
if
(KEYPRESS(W)) {tile[i].depth=INFRONT; tile[i].offset.z=0.5f; if
(KEYPRESS(SHIFT)) {tile[i].depth=NEAR_INFRONT; tile[i].offset.z=0.7f;}}
if
(KEYPRESS(T)) {tile[i].layer_type++; if
(tile[i].layer_type>=MAX_LAYER_TYPES) tile[i].layer_type=0; Sleep(150);}
if
(KEYPRESS(NUMPAD0)) tile[i].effect=NONE;
if
(KEYPRESS(NUMPAD5)) tile[i].effect=DAMAGE;
if
(KEYPRESS(NUMUP)) tile[i].effect=SPIKE_UP;
if
(KEYPRESS(NUMLEFT)) tile[i].effect=SPIKE_LEFT;
if
(KEYPRESS(NUMRIGHT)) tile[i].effect=SPIKE_RIGHT;
if
(KEYPRESS(NUMDOWN)) tile[i].effect=SPIKE_DOWN;
if
(KEYPRESS(PAUSE)) tile[i].effect=BREAKS;
if
(KEYPRESS(MULTIPLY)) tile[i].effect=SLIPPERY;
if
(KEYPRESS(ADD)) tile[i].effect=DOOR;
if
(KEYPRESS(SUBTRACT)) tile[i].effect=PORTAL;
if
(KEYPRESS(E)) {if
(!KEYPRESS(SHIFT)) config_mode=ADD_LAYER2; else
SetRectEmpty(&tile[i].layer2); }
if
(KEYPRESS(R)) {if
(!KEYPRESS(SHIFT)) config_mode=ADD_LAYER3; else
SetRectEmpty(&tile[i].layer3); }
if
(KEYPRESS(A)) {
config_mode=ADD_ANIMATION;
if
(tile[i].num_frames==0) {
CopyRect(&tile[i].animation[0],&tile[i].r);
tile[i].num_frames=1;
}
}
if
(KEYPRESS(K)) {tile[i].animation_type=SCROLL_X; VEC_ZERO(tile[i].scroll_pos); tile[i].scroll_pos.x=(float
)tile[i].r.left; tile[i].scroll_pos.y=(float
)tile[i].r.top; if
(tile[i].wait_time==0) tile[i].wait_time=1; }
if
(KEYPRESS(L)) {tile[i].animation_type=SCROLL_Y; VEC_ZERO(tile[i].scroll_pos); tile[i].scroll_pos.x=(float
)tile[i].r.left; tile[i].scroll_pos.y=(float
)tile[i].r.top; if
(tile[i].wait_time==0) tile[i].wait_time=1; }
if
(KEYPRESS(F)) tile[i].animation_type=LOOPING;
if
(KEYPRESS(G)) tile[i].animation_type=PING_PONG;
if
(KEYPRESS(H)) tile[i].animation_type=NO_ANIM;
if
(KEYPRESS(LBRACKET)) {tile[i].wait_time--; Sleep(150);}
if
(KEYPRESS(RBRACKET)) {tile[i].wait_time++; Sleep(150);}
if
(KEYPRESS(CONTROL)) {
if
(KEYPRESS(C)) {copy_offset_from=i;}
if
(KEYPRESS(V)) {COPY_VECTOR(tile[i].offset,tile[copy_offset_from].offset); tile[i].offx=tile[copy_offset_from].offx; tile[i].offy=tile[copy_offset_from].offy; }
}
if
(KEYPRESS(SHIFT)) {
if
(KEYPRESS(C)) {copy_rect_from=i;}
if
(KEYPRESS(V)) {
tile[i].r.right=tile[i].r.left+(tile[copy_rect_from].r.right-tile[copy_rect_from].r.left);
tile[i].r.bottom=tile[i].r.top+(tile[copy_rect_from].r.bottom-tile[copy_rect_from].r.top);
};
}
if
(KEYPRESS(ALT)) {
if
(KEYPRESS(S)) SaveTilemap();
if
(KEYPRESS(O)) LoadTilemap();
}
if
((!delete_key)&&(KEYPRESS(DELETE))) {if
(g_num_tile_types<1) {TileDelete(i); current_tile=i; delete_key=true; config_mode=CONFIG_RECT;} TileDelete(i); current_tile=i; delete_key=true;}
if
((!press_d)&&(KEYPRESS(D))) {
press_d=true;
if
(tile[current_tile].num_frames>0) {
tile[current_tile].num_frames--;
} else
tile[current_tile].num_frames=0;
}
if
(KEY_IS_UP(KEY_D)) press_d=false;
if
(KEY_IS_UP(KEY_DELETE)) delete_key=false;
if
(KEYPRESS(PAGEUP)) {
if
(current_tile>0) {
current_tile--; i=current_tile;
Sleep(200); break
;
}
}
if
(KEYPRESS(PAGEDOWN)) {
if
(current_tile<g_num_tile_types) {
current_tile++; i=current_tile;
Sleep(200); break
;
}
}
if
(done) {g_num_tile_types++; current_tile=g_num_tile_types;}
if
(KEYPRESS(ENTER)) {
config_mode=CONFIG_RECT;
g_num_tile_types++; current_tile=g_num_tile_types;
}
draw_black_rect(0,g_half_height+100,250,g_half_height+200); text_color=default_color;
TEXT_POSITION(10,g_half_height+100); PRINT_TEXT("Tile type: "); TEXT_POSITION(160,g_half_height+100);
switch
(tile[i].type) {
case
SOLID: text_color=0xFFBB0000; PRINT_TEXT("SOLID"); break
;
case
PASS: text_color=0xFF999999; PRINT_TEXT("PASSABLE"); break
;
case
LIQUID: text_color=0xFF0000BB; PRINT_TEXT("LIQUID"); break
;
case
BOUNCE: text_color=0xFF551188; PRINT_TEXT("BOUNCE"); break
;
case
BUTTON: text_color=0xFFAA2266; PRINT_TEXT("BUTTON"); break
;
case
JUMP_PASSABLE: text_color=0xFF8833AA; PRINT_TEXT("JUMP PASSABLE (solid)"); break
;
}
text_color=default_color; TEXT_POSITION(10,g_half_height+110); PRINT_TEXT("Depth: "); TEXT_POSITION(160,g_half_height+110);
switch
(tile[i].depth) {
case
BEHIND: text_color=0xFF888888; PRINT_TEXT("BEHIND"); break
;
case
INFRONT: text_color=0xFFAAAAAA; PRINT_TEXT("INFRONT"); break
;
case
FAR_BEHIND: text_color=0xFF555577; PRINT_TEXT("FAR BEHIND"); break
;
case
NEAR_INFRONT: text_color=0xFFCCAAAA; PRINT_TEXT("NEAR INFRONT"); break
;
}
text_color=default_color; TEXT_POSITION(10,g_half_height+120); PRINT_TEXT("Effect: "); TEXT_POSITION(160,g_half_height+120);
switch
(tile[i].effect) {
case
NONE: text_color=0xFF777777; PRINT_TEXT("NONE"); break
;
case
DAMAGE: text_color=0xFFDD7700; PRINT_TEXT("DAMAGE"); break
;
case
SPIKE_UP: text_color=0xFFDD7700; PRINT_TEXT("SPIKE UP"); break
;
case
SPIKE_DOWN: text_color=0xFFDD7700; PRINT_TEXT("SPIKE DOWN"); break
;
case
SPIKE_LEFT: text_color=0xFFDD7700; PRINT_TEXT("SPIKE LEFT"); break
;
case
SPIKE_RIGHT: text_color=0xFFDD7700; PRINT_TEXT("SPIKE RIGHT"); break
;
case
BREAKS: text_color=0xFF009933; PRINT_TEXT("BREAKS"); break
;
case
SLIPPERY: text_color=0xFF0055AA; PRINT_TEXT("SLIPPERY"); break
;
case
DOOR: text_color=0xFF995522; PRINT_TEXT("DOOR"); break
;
case
PORTAL: text_color=0xFF9955FF; PRINT_TEXT("PORTAL"); break
;
}
text_color=default_color; TEXT_POSITION(10,g_half_height+130); PRINT_TEXT("Animated effect: "); TEXT_POSITION(160,g_half_height+130);
switch
(tile[i].animation_type) {
case
NO_ANIM: text_color=0xFF554433; PRINT_TEXT("No effects for animation"); break
;
case
SCROLL_X: text_color=0xFF008888; PRINT_TEXT("Scrolls horizontally"); break
;
case
SCROLL_Y: text_color=0xFF888800; PRINT_TEXT("Scrolls vertically"); break
;
case
LOOPING: text_color=0xFFBB8800; PRINT_TEXT("Looping"); break
;
case
PING_PONG: text_color=0xFFBBDD00; PRINT_TEXT("Ping-Pong"); break
;
}
sprintf_s(g_str, _T("TIMING/speed: %d"), tile[i].wait_time);
text_color=default_color; TEXT_POSITION(10,g_half_height+140); PRINT_TEXT(g_str);
text_color=0xFF990011; TEXT_POSITION(10,g_half_height+150); PRINT_TEXT("Layering Type: "); TEXT_POSITION(160,g_half_height+150);
switch
(tile[i].layer_type) {
case
LAYER_FLAT: text_color=0xFF0000BB; PRINT_TEXT("FLAT"); break
;
case
LAYER_BOX: text_color=0xFFBB4422; PRINT_TEXT("BOX"); break
;
case
LAYER_DEPTH: text_color=0xFF880044; PRINT_TEXT("DEPTH layers"); break
;
case
LAYER_MAX: text_color=0xFF00EE00; PRINT_TEXT("MAX layers"); break
;
}
text_color=default_color; TEXT_POSITION(10,g_height-80);
PRINT_TEXT_D("Offset X: ",(int
)tile[i].offset.x,196);
TEXT_POSITION(10,g_height-60); PRINT_TEXT_D("Offset Y: ",(int
)tile[i].offset.y,196);
TEXT_POSITION(10,g_height-40); PRINT_TEXT("(use arrow keys to change [active]tiles offset)");
PRINT_TEXT_EXT(10,g_height-20,0xFF007722,"(press S to toggle snap)");
PRINT_TEXT_EXT(10,g_height-100,0xFFBBAA00,"Hold+Drag left mouse button to define active tiles region")
TEXT_POSITION(340,g_height-80); PRINT_TEXT_D("tiles wide: ",tile[i].tiles_wide,440);
TEXT_POSITION(340,g_height-60); PRINT_TEXT_D("tiles high: ",tile[i].tiles_high,440);
if
(KEYPRESS(UP)) {tile[i].offy++; Sleep(10);}
if
(KEYPRESS(DOWN)) {tile[i].offy--; Sleep(10);}
if
(KEYPRESS(LEFT)) {tile[i].offx++; Sleep(10);}
if
(KEYPRESS(RIGHT)) {tile[i].offx--; Sleep(10);}
LONG ofx=tile[i].offx, ofy=tile[i].offy;
if
(snap) {ofx=ofx/4*4; ofy=ofy/4*4;}
tile[i].offset.x=(float
)(tile[i].r.left-tile[i].o.left+ofx);
tile[i].offset.y=(float
)(tile[i].r.top-tile[i].o.top+ofy);
tile[i].tiles_wide=(tile[i].o.right+1-tile[i].o.left)/64;
tile[i].tiles_high=(tile[i].o.bottom+1-tile[i].o.top)/64;
float
tx, ty=(float
)tile[i].r.top-tile[i].offset.y;
RECT rc;
int
b=0; //draw tile types:
SAFE_DO;
int
a=0; tx=(float
)tile[i].r.left-tile[i].offset.x;
SAFE_DO;
pos.x=tx+panx; pos.y=ty+pany;
switch
(tile[i].type) {
case
SOLID: SET_RECT(rc,0,0,64,64); DRAW(tile_types_texture,&rc,&pos); break
;
case
PASS: SET_RECT(rc,0,64,64,128); DRAW(tile_types_texture,&rc,&pos); break
;
case
LIQUID: SET_RECT(rc,64,0,128,64); DRAW(tile_types_texture,&rc,&pos); break
;
case
BOUNCE: SET_RECT(rc,128,0,192,64); DRAW(tile_types_texture,&rc,&pos); break
;
case
BUTTON: SET_RECT(rc,128,64,192,128); DRAW(tile_types_texture,&rc,&pos); break
;
case
JUMP_PASSABLE: SET_RECT(rc,0,0,64,64); DRAWC(tile_types_texture,&rc,&pos,0xFF7700FF); break
;
}
a++; tx+=64;
}while
(a<tile[i].tiles_wide);
b++; ty+=64;
}while
(b<tile[i].tiles_high);
//test mouse clicks:
if
(!left_mouse_clicked) {
first_hit=true; last_mouse_pos_x=-1; last_mouse_pos_y=-1;last_left_mouse_button=false;
if
((tile[i].o.right>tile[i].o.left)&&(tile[i].o.bottom>tile[i].o.top)) {
CopyRect(&panned_rect,&tile[i].o); MoveRect(panned_rect,(LONG)panx,(LONG)pany);
draw_box(panned_rect,0xFFEE2200);
}
}
if
(left_mouse_clicked) {
bool
hit=false;
if
(hit) left_mouse_clicked=false;
if
(!hit) {
if
(last_left_mouse_button) {
if
((first_hit)||((mouse_pos.x>tile[i].o.left)&&(mouse_pos.y>tile[i].o.top))) {
if
(first_hit) {
first_hit=false;
tile[i].offx=0; tile[i].offy=0;
tile[i].o.left=last_mouse_pos_x; tile[i].o.right=(LONG)mouse_pos.x+64; //default assumes 64x64 minimum
tile[i].o.top=last_mouse_pos_y; tile[i].o.bottom=(LONG)mouse_pos.y+64; //default assumes 64x64 minimum
if
(snap) snap_coord(tile[i].o.left,16);
if
(snap) snap_coord(tile[i].o.right,16);
if
(snap) snap_coord(tile[i].o.top,16);
if
(snap) snap_coord(tile[i].o.bottom,16);
} else
{
tile[i].o.right=(LONG)mouse_pos.x;
tile[i].o.bottom=(LONG)mouse_pos.y;
if
(snap) snap_coord(tile[i].o.right,16);
if
(snap) snap_coord(tile[i].o.bottom,16);
CopyRect(&panned_rect,&tile[i].o); MoveRect(panned_rect,(LONG)panx,(LONG)pany);
draw_box(panned_rect,0xffeebbff);
}
}
} else
first_hit=true;
last_mouse_pos_x=(int
)mouse_pos.x;
last_mouse_pos_y=(int
)mouse_pos.y;
last_left_mouse_button=true;
}
}
TEXT_POSITION(230,g_height-20);
if
(snap) PRINT_TEXT("(on)"); if
(!snap) PRINT_TEXT("(off)");
break
;
}
if
(KEYPRESS(S)) {
if
(!s_pressed) {s_pressed=true;
if
(snap) snap=false; else
snap=true;
}
} else
s_pressed=false;
mouse_pos.x+=panx; mouse_pos.y+=pany; //redo what we undid above to prevent jerkiness
if
(KEY_IS_DOWN(mouse_button)) {
panx+=mouse_pos.x-last_mouse.x;
pany+=mouse_pos.y-last_mouse.y;
}
last_mouse.x=mouse_pos.x; last_mouse.y=mouse_pos.y;
text_color=default_color;
g_sprite->End(); END_SCENE("config_tiles"); message_pump();
SAFE_DO; }while
((GetTickCount() - starting_point) < TIMING_DELAY);
if
(KEYPRESS(PRINTSCREEN)) GetScreenShot("screenshot.bmp",-1);
}while
(!done);
SAFE_RELEASE(texture.tx);
}//config_tiles
#define
COL 0xFFFFFFFF
//not sure I like the following define - it's often not necessary to set all of these - but here it is anyway:
#define
DRAW_ROTATE_SCALE(tex,rec,rot,scale,trans_x,trans_y,sc1,rc1,clr)\
g_sprite_scale=D3DXVECTOR2(scale,scale);\
g_sprite_center=D3DXVECTOR2(sc1,sc1);\
g_sprite_trans=D3DXVECTOR2(trans_x,trans_y);\
g_sprite_rot_center=D3DXVECTOR2(rc1,rc1);\
D3DXMatrixTransformation2D(&g_matrix_2d,&g_sprite_center,0.0,&g_sprite_scale,&g_sprite_rot_center,rot,&g_sprite_trans);\
g_sprite->SetTransform(&g_matrix_2d);\
if
(rec.right==0) g_sprite->Draw(tex##.tx,NULL,NULL,NULL,clr);\
else
if
(tex##.tx!=NULL) g_sprite->Draw(tex##.tx,&##rec,NULL,NULL,clr);\
D3DXMatrixIdentity(&g_matrix_2d); g_sprite->SetTransform(&g_matrix_2d);
//-------------------------
// T I L E S E L E C T O R
//-------------------------
// for picking tiles to paint into the world
int
tile_selector() {
POINT cp;
RECT r;
int
i=-1;
float
scale=0.5f;
bool
done=false;
g_sprite->End(); END_SCENE("tile_selector");
SAFE_DO;
BEGIN_SCENE("tile_selector"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
HANDLE_LOST_DEVICES( , );
GetCursorPos(&cp); UpdateMousePos(&cp);
if
(PRESS_ESCAPE) done=true;
RECT rec={0}; DRAW_ROTATE_SCALE(tile_texture,rec,0,scale,0,0,0,0,COL);
int
a=0;
do
{
CopyRect(&r,&tile[a].r);
r.left=(LONG)((float
)r.left*scale); r.right=(LONG)((float
)r.right*scale); r.top=(LONG)((float
)r.top*scale); r.bottom=(LONG)((float
)r.bottom*scale);
if
(mouse_inside(r)) {
draw_box(r,0xFFAA0000);
if
(left_mouse_clicked) {left_mouse_clicked=false; i=a; done=true;}
}
a++;
}while
(a<g_num_tile_types);
g_sprite->End(); END_SCENE("tile_selector"); message_pump();
}while
(!done);
BEGIN_SCENE("tile_selector"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
return
i;
}//tile_selector
//-----------------------
// S A V E G A M E M A P (custom)
//-----------------------
void
SaveGamemap() {
int
done=0;
char
filename[100];
g_sprite->End(); END_SCENE("save gamemap");
reset_user_input_memory(); Sleep(250);
if
(strlen(g_custom_file)>0) {strcpy_s(filename,g_custom_file); REPORT("strlen>0");} else
sprintf_s(filename, _T("gamemap%d.lev"), g_level); //default gamemap to save as (based on g_level)
char
file[100];
do
{
BEGIN_SCENE("save gamemap"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
TEXT_POSITION(10,g_half_height-60); text_color=0xFF88DD00;
PRINT_TEXT(filename);
TEXT_POSITION(10,g_half_height-20); text_color=default_color;
PRINT_TEXT("Press ENTER to use default filename or type in a new file name to SAVE AS: ");
if
(PollUserInput("NEW FILE: ", 10, g_half_height+25, 20)) {
strcat_s(g_text,".lev");
strcpy_s(filename,100,g_text);
strcpy_s(file,"Gamemaps/"); strcat_s(file,filename);
done=1;
} else
if
(KEYPRESS(ENTER)) { strcpy_s(file,"Gamemaps/"); strcat_s(file,filename); REPORT_S("file = ",file); done=1;}
g_sprite->End(); END_SCENE("save gamemap");
if
(KEYPRESS(ESCAPE)) done=2;
}while
(done==0);
if
(done==1) {
FILE *f=NULL;
fopen_s(&f,file,"wb");
if
(!f) { REPORT("file creation error"); }
else
{
fwrite(&g_tiles, sizeof
(g_tiles[0][0]), ((LEVEL_WIDTH+10)*(LEVEL_HEIGHT+10)), f );
fclose(f);
if
(strlen(g_custom_file)<1) {strcat_s(g_custom_file,filename); REPORT("strlen < 1");}
}
}
BEGIN_SCENE("save gamemap"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
}
//--------------------------------
// E D I T O R F I L E S M E N U
//--------------------------------
void
editor_files_menu() {
// for loading or saving game / level maps
FILE *f=NULL;
char
filename[100];
POINT cp;
D3DCOLOR col=default_color;
RECT r;
enum
SELECT_TYPE {NO_SELECT, GET_SELECT, OPEN_DEFAULT, SAVE_DEFAULT, OPEN_CUSTOM, SAVE_CUSTOM, NEW_FILE};
int
selection=GET_SELECT;
bool
done=false, open_ok=false;
sprintf_s(filename, _T("Gamemaps/Gamemap%d.lev"), g_level); //default level name
g_sprite->End(); END_SCENE("editor_files_menu");
SAFE_DO;
BEGIN_SCENE("editor_files_menu"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
HANDLE_LOST_DEVICES( , );
GetCursorPos(&cp); UpdateMousePos(&cp);
switch
(selection) {
case
GET_SELECT:
SET_RECT(r,10,100,130,112); if
(mouse_inside(r)) {col=0xFF66BB00; if
(left_mouse_clicked) {left_mouse_clicked=false; selection=OPEN_DEFAULT;}}
PRINT_TEXT_EXT(10,100,col,"OPEN default level file (Alt+O)"); PRINT_TEXT_EXT(260,100,col,filename); col=default_color; TEXT_POSITION(10,100);
SET_RECT(r,10,120,130,132); if
(mouse_inside(r)) {col=0xFF66BB00; if
(left_mouse_clicked) {left_mouse_clicked=false; selection=SAVE_DEFAULT;}}
PRINT_T(10,20,col,"SAVE default level file (Alt+S)"); PRINT_TEXT_EXT(260,120,col,filename); col=default_color; TEXT_POSITION(10,120);
SET_RECT(r,10,160,100,172); if
(mouse_inside(r)) {col=0xFF66BB00; if
(left_mouse_clicked) {left_mouse_clicked=false; selection=OPEN_CUSTOM;}}
PRINT_T(10,40,col,"OPEN custom file"); col=default_color;
SET_RECT(r,10,180,100,192); if
(mouse_inside(r)) {col=0xFF66BB00; if
(left_mouse_clicked) {left_mouse_clicked=false; selection=SAVE_CUSTOM;}}
PRINT_T(10,20,col,"SAVE custom file"); col=default_color;
SET_RECT(r,10,220,42,232); if
(mouse_inside(r)) {col=0xFF66BB00; if
(left_mouse_clicked) {left_mouse_clicked=false; selection=NEW_FILE;}}
PRINT_T(10,40,col,"NEW"); col=default_color;
SET_RECT(r,10,240,42,252); if
(mouse_inside(r)) {col=0xFF66BB00; if
(left_mouse_clicked) {left_mouse_clicked=false; selection=NO_SELECT; done=true;}}
PRINT_T(10,20,col,"EXIT"); col=default_color;
if
(KEYPRESS(ALT)) {
if
(KEYPRESS(O)) selection=OPEN_DEFAULT;
if
(KEYPRESS(S)) selection=SAVE_DEFAULT;
}
break
;
case
OPEN_DEFAULT:
open_ok=true;
if
(g_gamemap_changed) {
open_ok=PromptUser("Open file without saving??? (Y/N)");
}
if
(open_ok) {
fopen_s(&f,filename,"rb");
if
(!f) { REPORT("file read error"); }
else
{
fread(&g_tiles, sizeof
(g_tiles[0][0]), ((LEVEL_WIDTH+10)*(LEVEL_HEIGHT+10)), f );
fclose(f);
}
g_gamemap_changed=false;
}
done=true;
break
;
case
SAVE_DEFAULT:
if
(FileExists(filename)) {
SYSTEMTIME st;
GetSystemTime(&st);
char
backupfilename[100];
sprintf_s(backupfilename, _T("Gamemaps/backup/Gamemap%d_%d_%d.lev"), g_level,st.wMonth,st.wDay); //default level name
CopyFile(filename,backupfilename,false);
}
fopen_s(&f,filename,"wb");
if
(!f) { REPORT("file creation error"); }
else
{
fwrite(&g_tiles, sizeof
(g_tiles[0][0]), ((LEVEL_WIDTH+10)*(LEVEL_HEIGHT+10)), f );
fclose(f);
}
done=true;
break
;
case
OPEN_CUSTOM:
open_ok=true;
if
(g_gamemap_changed) {
open_ok=PromptUser("Open file without saving??? (Y/N)");
}
if
(open_ok) {
char
file[100];
sprintf_s(filename, _T("Gamemap%d.lev"), g_level); //default tilemap to load (based on g_level)
g_sprite->End(); END_SCENE("editor files menu(open custom)");
//file, what to look for, what to ignore
if
(!choose_file(filename, "\\Gamemaps\\", "*.lev", "____","lock","edit", "Please choose a file to load: "))
strcpy_s(file,"Gamemaps/");
else strcpy_s(file,"Gamemaps/backup/");
strcat_s(file,filename);
fopen_s(&f, file, "rb");
if (!f) { REPORT_S("Can't read gamemap file: ",file); } else {
fread(&g_tiles, sizeof(g_tiles[0][0]), ((LEVEL_WIDTH+10)*(LEVEL_HEIGHT+10)), f );
fclose(f);
}
BEGIN_SCENE("load tilemap"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
g_gamemap_changed=false;
strcpy_s(g_custom_file,filename);
}
done=true;
break;
case SAVE_CUSTOM:
SaveGamemap();
done=true;
break;
case NEW_FILE:
bool delete_changes=true;
if (g_gamemap_changed) {
delete_changes=PromptUser("Delete changes? (Y/N)");
}
if (delete_changes) {
TEST_ARRAY2(g_tiles,LEVEL_WIDTH+10,LEVEL_HEIGHT+10);
int b=0,a=0;
SAFE_DO;
a=0;
SAFE_DO;
reset_tile(a,b);
a++;SAFE_WHILE(a<LEVEL_WIDTH+10);
b++;SAFE_WHILE(b<LEVEL_HEIGHT+10);
g_gamemap_changed=false;
}
done=true;
break;
}
if (PRESS_ESCAPE) {
if (g_gamemap_changed) {
if (PromptUser("Exit without saving? (Y/N)")) done=true;
} else done=true;
}
g_sprite->End(); END_SCENE("editor_files_menu"); message_pump();
}while(!done);
BEGIN_SCENE("editor_files_menu"); g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
}//editor_files_menu
// S E T T I L E O F F S E T
void
set_tile_offset(int
a, int
b, float
x, float
y) {
int
ma, mb;//, mi;
Sleep(25);
ma=g_tiles[a][b].m_a; mb=g_tiles[a][b].m_b;
g_tiles[ma][mb].offset.x+=x;
g_tiles[ma][mb].offset.y+=y;
}//set_tile_offset
// R E S E T T I L E O F F S E T
void
reset_tile_offset(int
a, int
b) {
int
ma, mb, mi;
Sleep(25);
ma=g_tiles[a][b].m_a; mb=g_tiles[a][b].m_b;
mi=g_tiles[a][b].i;
g_tiles[ma][mb].offset.x=tile[mi].offset.x;
g_tiles[ma][mb].offset.y=tile[mi].offset.y;
g_tiles[ma][mb].rotation=g_tiles[ma][mb].rotation2=0.0f;
g_tiles[ma][mb].scale=g_tiles[ma][mb].scale2=1.0f;
g_tiles[ma][mb].type=tile[mi].type;
}//reset_tile_offset
//----------------------------
// E D I T O R M I N I M A P
//----------------------------
void
editor_mini_map() {
POINT cp;
RECT rc;
int
a,b,ta,tb,x,y,i;//,td;
float
scale=0.25f;
g_sprite_scale=D3DXVECTOR2(scale,scale);
g_sprite_center=D3DXVECTOR2(0,0); g_sprite_rot_center=D3DXVECTOR2(0,0);
g_sprite->End(); END_SCENE("editor_mini_map");
bool
done=false, first_click=true;
RECT r,sel={0};
int
copy_a1=-1, copy_b1=-1, copy_a2=-1, copy_b2=-1;
do
{
GetCursorPos(&cp); UpdateMousePos(&cp);
g_sprite->Begin(D3DXSPRITE_ALPHABLEND); BEGIN_SCENE("editor_mini_map");
b=-40;
tb=tile_y+b;
if
(tb<0) {b-=tb; tb=0;}
y=b*16+g_half_height+(int
)tile_y_off;
SAFE_DO {
tb=tile_y+b; if
(tb<0) {b++; y+=16; continue
;}
if
(tb>=LEVEL_HEIGHT) break
;
a=-52;
ta=tile_x+a;
if
(ta<0) {a-=ta; ta=0;}
x=a*16+g_half_width+(int
)tile_x_off;
SAFE_DO {
ta=tile_x+a; if
(ta<0) {a++; x+=16; continue
;}
if
(ta>=LEVEL_WIDTH) break
;
r.left=(LONG)x-1; r.right=(LONG)x+17; r.top=(LONG)y-1; r.bottom=(LONG)y+17;
if
(mouse_inside(r)) {
if
(left_mouse_clicked) {
if
(KEYPRESS(SHIFT)) {
if
(first_click) {
sel.left=sel.right=(LONG)x; copy_a1=ta; copy_a2=ta;
sel.top=sel.bottom=(LONG)y; copy_b1=tb; copy_b2=tb;
first_click=false;
} else
{
sel.right=(LONG)x+64L; copy_a2=ta;
sel.bottom=(LONG)y+64L; copy_b2=tb;
}
}
} else
first_click=true;
if
(copy_a2>-1) {
bool
move_it=false;
if
(KEYPRESS(M)) move_it=true;
if
((KEYPRESS(V))||(move_it)) {
int
sb=tb;
int
v1=copy_b1;
SAFE_DO {
int
sa=ta;
int
h1=copy_a1;
SAFE_DO {
TEST_ARRAY2(g_tiles,sa,sb); TEST_ARRAY2(g_tiles,h1,v1);
if
(g_tiles[h1][v1].type>-1) //don't copy empty tiles
memcpy(&g_tiles[sa][sb],&g_tiles[h1][v1],sizeof g_tiles[h1][v1]);
int
adif=h1-g_tiles[h1][v1].m_a;
int
bdif=v1-g_tiles[h1][v1].m_b;
g_tiles[sa][sb].m_a=sa+adif;
g_tiles[sa][sb].m_b=sb+bdif;
h1++; sa++;
}SAFE_WHILE(h1<copy_a2);
v1++; sb++;
}SAFE_WHILE(v1<copy_b2);
}
if
(move_it) {
int
v1=copy_b1;
SAFE_DO {
int
h1=copy_a1;
SAFE_DO {
TEST_ARRAY2(g_tiles,h1,v1);
remove_tile(h1,v1);
h1++;
}SAFE_WHILE(h1<copy_a2);
v1++;
}SAFE_WHILE(v1<copy_b2);
copy_a1=copy_a2=copy_b1=copy_b2=-1;
}
}
}
g_sprite_trans=D3DXVECTOR2((float
)x,(float
)y);
D3DXMatrixTransformation2D(&g_matrix_2d,&g_sprite_center,0.0,&g_sprite_scale,&g_sprite_rot_center,0,&g_sprite_trans);
g_sprite->SetTransform(&g_matrix_2d);
SET_RECT(rc,0,64,64,128);
g_sprite->Draw(g_far_background.texture.tx,&rc,NULL,NULL,0x66888888);
if
((g_tiles[ta][tb].m_a!=ta)||(g_tiles[ta][tb].m_b!=tb)) {a++; x+=16; continue
;}
i=g_tiles[ta][tb].i;
//if ((ta==0)&&(tb==0)) {a++; x+=16; continue;}
if
(g_tiles[ta][tb].type>-1) {
g_sprite_trans=D3DXVECTOR2((float
)x,(float
)y);
D3DXMatrixTransformation2D(&g_matrix_2d,&g_sprite_center,0.0,&g_sprite_scale,&g_sprite_rot_center,0,&g_sprite_trans);
g_sprite->SetTransform(&g_matrix_2d);
g_sprite->Draw(tile_texture.tx,&tile[i].r,NULL,NULL,0xFFDDDDDD);
}
a++; x+=16;
}SAFE_WHILE(a<tile_x+52);
b++; y+=16;
}SAFE_WHILE(b<tile_y+40);
if
(KEYPRESS(RIGHT)) GameScroll(-16.0f,0.0f);
if
(KEYPRESS(LEFT)) GameScroll(16.0f,0.0f);
if
(KEYPRESS(DOWN)) GameScroll(0.0f,-16.0f);
if
(KEYPRESS(UP)) GameScroll(0.0f,16.0f);
if
((KEYPRESS(ESCAPE))||(KEYPRESS(ENTER))) {done=true;}
if
(copy_a2>-1) {
sel.left=g_half_width-832+(copy_a1-(tile_x-52))*16+(int
)tile_x_off; //52*16
sel.top=g_half_height-640+(copy_b1-(tile_y-40))*16+(int
)tile_y_off; //40*16
sel.right=sel.left+(copy_a2-copy_a1)*16;
sel.bottom=sel.top+(copy_b2-copy_b1)*16;
D3DCOLOR col=D3DCOLOR_ARGB(255,RAND_INT(255),RAND_INT(255),RAND_INT(255));
draw_box(sel,col);
}
g_sprite->End(); END_SCENE("editor_mini_map"); message_pump();
}while
(!done);
Sleep(100);
g_sprite->Begin(D3DXSPRITE_ALPHABLEND); BEGIN_SCENE("editor_mini_map");
D3DXMatrixIdentity(&g_matrix_2d); g_sprite->SetTransform(&g_matrix_2d);
}//editor_mini_map
//-------------------
// E D I T L E V E L
//-------------------
void
edit_level() {
bool
done=false,remove_occupied=false,first_click=true;
RECT rc,r,sel={0};
VEC pos; pos.z=0;
POINT cp;
int
tn=0; //<--index of current tile to paint
D3DCOLOR col=0xFF999999;
bool
show_types=true, show_instructions=false;
int
do_fill=0; //determine whether to fill an area or not
int
la,ga,lb,gb; //filling variables
int
copy_a1=-1, copy_b1=-1, copy_a2=-1, copy_b2=-1;
int
mouse_button=VK_RBUTTON;
int
sp_a, sp_b; //starting point for tiles fill
if
(GetSystemMetrics(SM_SWAPBUTTON)) mouse_button=VK_LBUTTON;
D3DXMatrixIdentity(&g_world_matrix);
g_gpu->SetTransform(D3DTS_WORLD, &g_world_matrix);
g_gpu->SetFVF(MY_CUSTOM_VERTEX);
g_gpu->SetStreamSource(0,g_vb,0,sizeof(stMyVertex));
SAFE_DO;
DWORD starting_point = GetTickCount();
HANDLE_LOST_DEVICES( , );
BEGIN_SCENE("edit level");
g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
GetCursorPos(&cp); UpdateMousePos(&cp);
if
(PRESS_ESCAPE) done=true;
if
(KEYPRESS(SHIFT)) {
if
(KEYPRESS(QUESTION)) {
if
(show_types) show_types=false; else
show_types=true; Sleep(150);
}
}
if
(KEYPRESS(ALT)) {
if
(KEYPRESS(R)) {
if
(remove_occupied) {remove_occupied=false; Sleep(160);} else
{remove_occupied=true; Sleep(160);}
}
}
if
(KEYPRESS(RIGHT)) {GameScroll(-2.0f,0.0f); }
if
(KEYPRESS(LEFT)) {GameScroll(2.0f,0.0f); }
if
(KEYPRESS(DOWN)) GameScroll(0.0f,-2.0f);
if
(KEYPRESS(UP)) GameScroll(0.0f,2.0f);
if
(KEYPRESS(DELETE)) if
((tile_x-1)>0) {tile_x-=1; tile_x_off=0; GameScroll(0.0f,0.0f);}
if
(KEYPRESS(END)) if
((tile_x+1)<LEVEL_WIDTH) {tile_x+=1; tile_x_off=0; GameScroll(0.0f,0.0f);}
if
(KEYPRESS(PAGEUP)) if
((tile_y-1)>0) {tile_y-=1; tile_y_off=0; GameScroll(0.0f,0.0f);}
if
(KEYPRESS(PAGEDOWN)) if
((tile_y+1)<LEVEL_HEIGHT) {tile_y+=1; tile_y_off=0; GameScroll(0.0f,0.0f);}
D3DCOLOR bcl=0xFF666666;
if
(!show_types) bcl=0xFFFFFFFF;
g_far_background.pos1.z=1;g_mid_background.pos1.z=1;
g_far_background.pos2.z=1;g_mid_background.pos2.z=1;
DRAWC(g_far_background.texture,NULL,&g_far_background.pos1,bcl);
DRAWC(g_far_background.texture,NULL,&g_far_background.pos2,bcl);
DRAWC(g_mid_background.texture,&g_mid_background.r2,&g_mid_background.pos1,bcl);
DRAWC(g_mid_background.texture,&g_mid_background.r2,&g_mid_background.pos2,bcl);
bool
control_clicked=false; //control button clicked or just painting tiles?
if
(left_mouse_clicked) {
r.left=450; r.top=g_height-25; r.right=560; r.bottom=g_height;
if
(mouse_inside(r)) {
left_mouse_clicked=false;
int
i=tile_selector();
if
(i>-1) tn=i;
control_clicked=true;
}
r.left=50; r.top=g_height-25; r.right=160; r.bottom=g_height;
if
(mouse_inside(r)) {
left_mouse_clicked=false;
editor_files_menu();
control_clicked=true;
}
r.left=260; r.top=g_height-25; r.right=370; r.bottom=g_height;
if
(mouse_inside(r)) {
left_mouse_clicked=false;
editor_mini_map();
control_clicked=true;
}
r.left=656; r.top=g_height-25; r.right=785; r.bottom=g_height;
if
(mouse_inside(r)) {
left_mouse_clicked=false;
if
(!show_instructions) show_instructions=true; else
show_instructions=false;
control_clicked=true;
}
}
int
ln=tn;
if
(KEYPRESS(X)) {tn++; Sleep(200); if
(tn>=g_num_tile_types) tn=0;}
if
(KEYPRESS(Z)) {tn--; Sleep(200); if
(tn<0) tn=g_num_tile_types-1;}
if
(KEYPRESS(SQUIGGLE)) tn=0;
if
(KEYPRESS(1)) tn=1; if
(KEYPRESS(2)) tn=2; if
(KEYPRESS(3)) tn=3; if
(KEYPRESS(4)) tn=4; if
(KEYPRESS(5)) tn=5; if
(KEYPRESS(6)) tn=6; if
(KEYPRESS(7)) tn=7; if
(KEYPRESS(8)) tn=8; if
(KEYPRESS(9)) tn=9; if
(KEYPRESS(0)) tn=10;
if
(KEYPRESS(Q)) tn=11; if
(KEYPRESS(W)) tn=12; if
(KEYPRESS(E)) tn=13; if
(KEYPRESS(R)) tn=14; if
(KEYPRESS(T)) tn=15; if
(KEYPRESS(Y)) tn=16; if
(KEYPRESS(U)) tn=17; if
(KEYPRESS(I)) tn=18; if
(KEYPRESS(O)) tn=19; if
(KEYPRESS(P)) tn=20;
if
(KEYPRESS(A)) tn=21; if
(KEYPRESS(S)) tn=22; if
(KEYPRESS(D)) tn=23; if
(KEYPRESS(F)) tn=24; if
(KEYPRESS(G)) tn=25; if
(KEYPRESS(H)) tn=26; if
(KEYPRESS(J)) tn=27; if
(KEYPRESS(K)) tn=28; if
(KEYPRESS(L)) tn=29;
if
(tn>=g_num_tile_types) tn=ln;
g_gpu->SetFVF(MY_CUSTOM_VERTEX);
g_gpu->SetStreamSource(0,g_vb,0,sizeof(stMyVertex));
animate_tiles();
g_sprite->End();
int
a,b,y,ar=tile_x-13;
int
ma,mb;
int
cnt=0; //for counting images to be drawn closer
VEC p2; p2.z=0;
b=tile_y-10; y=g_half_height-64*10+(int
)tile_y_off;
SAFE_DO;
if
((b<0)||(b>LEVEL_HEIGHT)) {b++; y+=64; continue
;} //skip if out of bounds
if
(y>(g_height+100)) break
;
a=ar;
if
(a>=0) if
((tile[g_tiles[a][b].i].r.bottom+y)<-100) {b++; y+=64; continue
;}
int
x=g_half_width-64*13+(int
)tile_x_off;
SAFE_DO;
if
((a<0)||(a>LEVEL_WIDTH)) {a++; x+=64; continue
;} //skip it if there's nothing there
//TEST_ARRAY2(g_tiles,a,b);
ma=g_tiles[a][b].m_a; mb=g_tiles[a][b].m_b;
if
(x>(g_width+100)) break
;
if
((tile[g_tiles[ma][mb].i].r.right+x)<-100) {a++; x+=64; continue
;}
pos.x=(float
)x; pos.y=(float
)y;
if
(g_tiles[a][b].type>-1) {
ma=g_tiles[a][b].m_a; mb=g_tiles[a][b].m_b;
if
((ma==a)&&(mb==b)) {
float
offx=g_tiles[ma][mb].offset.x, offy=g_tiles[ma][mb].offset.y;
float
off2x=g_tiles[ma][mb].offset2.x, off2y=g_tiles[ma][mb].offset2.y;
int
ind=g_tiles[a][b].i;
VEC pz, pz2; pz.z=pz2.z=0.0f;
pz.x=pos.x+offx; pz.y=pos.y+offy;
pz2.x=pos.x+off2x; pz2.y=pos.y+off2y;
if
(tile[ind].layer_type==LAYER_BOX) draw_cube(a,b,pz2,0xFFFFFFFF);
rec_images[cnt].a=a; rec_images[cnt].b=b; rec_images[cnt].tn=ind;
COPY_VECTOR(rec_images[cnt].pos, pz);
COPY_VECTOR(rec_images[cnt].pos2, pz2);
cnt++;
}
}
x+=64; a++;
}while
(a<tile_x+13);
y+=64; b++;
}while
(b<tile_y+10);
g_sprite->Begin(D3DXSPRITE_ALPHABLEND);
draw_back_tile_layers(0.92f);
if
(cnt>0) {
int
c=0;
SAFE_DO {
a=rec_images[c].a; b=rec_images[c].b;
if
(g_tiles[a][b].depth==FAR_BEHIND) {
if
(g_tiles[a][b].i2>-1) draw_tile(a,b,rec_images[c].pos2,true,0xFFFFFFFF);
draw_tile(a,b,rec_images[c].pos,false,0xFFFFFFFF);
}
c++;
}SAFE_WHILE(c<cnt);
c=0;
SAFE_DO {
a=rec_images[c].a; b=rec_images[c].b;
if
(g_tiles[a][b].depth==BEHIND) {
if
(g_tiles[a][b].i2>-1) draw_tile(a,b,rec_images[c].pos2,true,0xFFFFFFFF);
draw_tile(a,b,rec_images[c].pos,false,0xFFFFFFFF);
}
c++;
}SAFE_WHILE(c<cnt);
c=0;
SAFE_DO {
a=rec_images[c].a; b=rec_images[c].b;
if
(g_tiles[a][b].depth==INFRONT) {
if
(g_tiles[a][b].i2>-1) draw_tile(a,b,rec_images[c].pos2,true,0xFFFFFFFF);
draw_tile(a,b,rec_images[c].pos,false,0xFFFFFFFF);
}
c++;
}SAFE_WHILE(c<cnt);
c=0;
SAFE_DO {
a=rec_images[c].a; b=rec_images[c].b;
if
(g_tiles[a][b].depth==NEAR_INFRONT) {
if
(g_tiles[a][b].i2>-1) draw_tile(a,b,rec_images[c].pos2,true,0xFFFFFFFF);
draw_tile(a,b,rec_images[c].pos,false,0xFFFFFFFF);
}
c++;
}SAFE_WHILE(c<cnt);
}
draw_front_tile_layers(1.02f,2);
draw_front_tile_layers(1.04f,3);
b=tile_y-10; y=g_half_height-64*10+(int
)tile_y_off;
SAFE_DO;
if
((b<0)||(b>LEVEL_HEIGHT)) {b++; y+=64; continue
;} //skip if out of bounds
if
(y>(g_height+100)) break
;
a=ar;
if
(a>=0) if
((tile[g_tiles[a][b].i].r.bottom+y)<-100) {b++; y+=64; continue
;}
int
x=g_half_width-64*13+(int
)tile_x_off;
SAFE_DO;
if
((a<0)||(a>LEVEL_WIDTH)) {a++; x+=64; continue
;} //skip it if there's nothing there
//TEST_ARRAY2(g_tiles,a,b);
ma=g_tiles[a][b].m_a; mb=g_tiles[a][b].m_b;
if
(x>(g_width+100)) break
;
if
((tile[g_tiles[ma][mb].i].r.right+x)<-100) {a++; x+=64; continue
;}
pos.x=(float
)x; pos.y=(float
)y;
r.left=(LONG)pos.x-1; r.right=(LONG)pos.x+65; r.top=(LONG)pos.y-1; r.bottom=(LONG)pos.y+65;
col=0xFF999999;
if
(mouse_inside(r)) {
col=0xFFFFBB00;
VEC pz; pz.z=0.0f; pz.x=pos.x+tile[tn].offset.x; pz.y=pos.y+tile[tn].offset.y;
DRAWC(tile_texture,&tile[tn].r,&pz,0x77FFFFFF);
if
(KEYPRESS(PERIOD)) {
if
(KEYPRESS(SHIFT)) change_tile_type(a,b,1,true);
else
change_tile_type(a,b,1,false);
}
if
(KEYPRESS(COMMA)) {
if
(KEYPRESS(SHIFT)) change_tile_type(a,b,-1,true);
else
change_tile_type(a,b,-1,false);
}
if
(KEYPRESS(ADD)) {
g_tiles[ma][mb].depth--;
if
(g_tiles[ma][mb].depth<0) g_tiles[ma][mb].depth=0;
}
if
(KEYPRESS(SUBTRACT)) {
g_tiles[ma][mb].depth++;
if
(g_tiles[ma][mb].depth>NEAR_INFRONT) g_tiles[ma][mb].depth=NEAR_INFRONT;
}
if
(KEYPRESS(LBRACKET)) {g_tiles[ma][mb].rotation-=0.01f; if
(g_tiles[ma][mb].rotation<0.0f) g_tiles[ma][mb].rotation=6.28f;}
if
(KEYPRESS(RBRACKET)) {g_tiles[ma][mb].rotation+=0.01f; if
(g_tiles[ma][mb].rotation>6.28f) g_tiles[ma][mb].rotation=0.0f;}
if
(KEYPRESS(COLON)) {g_tiles[ma][mb].scale-=0.01f; if
(g_tiles[ma][mb].scale<0.001f) g_tiles[ma][mb].scale=0.001f;}
if
(KEYPRESS(QUOTE)) {g_tiles[ma][mb].scale+=0.01f; if
(g_tiles[ma][mb].scale>100.0f) g_tiles[ma][mb].scale=100.0f;}
if
(copy_a2>-1) {
bool
move_it=false;
if
(KEYPRESS(M)) move_it=true;
if
((KEYPRESS(V))||(move_it)) {
int
sb=b;
int
v1=copy_b1;
SAFE_DO {
int
sa=a;
int
h1=copy_a1;
SAFE_DO {
TEST_ARRAY2(g_tiles,sa,sb); TEST_ARRAY2(g_tiles,h1,v1);
if
(g_tiles[h1][v1].type>-1) //don't copy empty tiles
memcpy(&g_tiles[sa][sb],&g_tiles[h1][v1],sizeof g_tiles[h1][v1]);
int
adif=h1-g_tiles[h1][v1].m_a;
int
bdif=v1-g_tiles[h1][v1].m_b;
g_tiles[sa][sb].m_a=sa+adif;
g_tiles[sa][sb].m_b=sb+bdif;
h1++; sa++;
}SAFE_WHILE(h1<copy_a2);
v1++; sb++;
}SAFE_WHILE(v1<copy_b2);
}
if
(move_it) {
int
v1=copy_b1;
SAFE_DO {
int
h1=copy_a1;
SAFE_DO {
TEST_ARRAY2(g_tiles,h1,v1);
remove_tile(h1,v1);
h1++;
}SAFE_WHILE(h1<copy_a2);
v1++;
}SAFE_WHILE(v1<copy_b2);
copy_a1=copy_a2=copy_b1=copy_b2=-1;
}
}
if
(KEYPRESS(NUMLEFT)) set_tile_offset(a,b,-1,0);
if
(KEYPRESS(NUMRIGHT)) set_tile_offset(a,b,1,0);
if
(KEYPRESS(NUMUP)) set_tile_offset(a,b,0,-1);
if
(KEYPRESS(NUMDOWN)) set_tile_offset(a,b,0,1);
if
(KEYPRESS(NUMPAD5)) reset_tile_offset(a,b);
if
(left_mouse_clicked) {
if
(KEYPRESS(CONTROL)) {sp_a=a;sp_b=b; do_fill=1; left_mouse_clicked=false;}
else
if
(do_fill==1) {
if
(a<sp_a) {la=a; ga=sp_a;} else
{la=sp_a; ga=a;}
if
(b<sp_b) {lb=b; gb=sp_b;} else
{lb=sp_b; gb=b;}
do_fill=2;
}
if
(!control_clicked) {
//filling code begin----------------:
if
(do_fill==2) {
do_fill=0;
SAFE_DO;
int
al=la;
SAFE_DO;
add_tile(al,lb,tn,false,remove_occupied);
al++;
}while
(al<ga);
lb++;
}while
(lb<gb);
}
//----------------------------------^
if
((do_fill<1)||(do_fill>2)) {
//SELECT region for copy and paste-----------------:
if
(KEYPRESS(SHIFT)) {
if
(first_click) {
sel.left=sel.right=(LONG)pos.x; copy_a1=a; copy_a2=a;
sel.top=sel.bottom=(LONG)pos.y; copy_b1=b; copy_b2=b;
first_click=false;
} else
{
sel.right=(LONG)pos.x+64L; copy_a2=a;
sel.bottom=(LONG)pos.y+64L; copy_b2=b;
}
} else
{
first_click=true;
add_tile(a,b,tn,false,remove_occupied);
if
(do_fill>2) do_fill=0;
if
((do_fill==0)&&(KEYPRESS(INSERT))) {add_tile(a,b,tn,true,remove_occupied); if
(do_fill>2) do_fill=0;}
}
}
}
} else
first_click=true;
if
(KEY_IS_DOWN(mouse_button)) {
SetRectEmpty(&sel); copy_a1=copy_a2=copy_b1=copy_b2=-1;
if
(KEYPRESS(CONTROL)) {do_fill=3; sp_a=a; sp_b=b;}
else
if
(do_fill==3) {
if
(a<sp_a) {la=a; ga=sp_a;} else
{la=sp_a; ga=a;}
if
(b<sp_b) {lb=b; gb=sp_b;} else
{lb=sp_b; gb=b;}
do_fill=4;
}
//erase area code begin----------------
if
(do_fill==4) {
if
(PromptUser("Delete tiles? (Y/N)")) do_fill=5; else
do_fill=0;
if
(do_fill==5) {
do_fill=0;
SAFE_DO;
int
al=la;
SAFE_DO;
remove_tile(al,lb);
al++;
}while
(al<ga);
lb++;
}while
(lb<gb);
}
}
//-------------------------------------
remove_tile(a,b);
}
}
if
(show_types) {SET_RECT(rc,0,64,64,128); DRAWC(tile_types_texture,&rc,&pos,col);}
x+=64; a++;
}while
(a<tile_x+13);
y+=64; b++;
}while
(b<tile_y+10);
if
(show_types) {
b=tile_y-10; y=g_half_height-64*10+(int
)tile_y_off;
SAFE_DO;
if
((b<0)||(b>LEVEL_HEIGHT)) {b++; y+=64; continue
;} //skip if out of bounds
if
(y>(g_height+100)) break
;
a=ar;
if
(a>=0) if
((tile[g_tiles[a][b].i].r.bottom+y)<-100) {b++; y+=64; continue
;}
int
x=g_half_width-64*13+(int
)tile_x_off;
SAFE_DO;
if
((a<0)||(a>LEVEL_WIDTH)) {a++; x+=64; continue
;} //skip it if there's nothing there
ma=g_tiles[a][b].m_a; mb=g_tiles[a][b].m_b;
if
(x>(g_width+100)) break
;
if
((tile[g_tiles[ma][mb].i].r.right+x)<-100) {a++; x+=64; continue
;}
pos.x=(float
)x; pos.y=(float
)y;
switch
(g_tiles[a][b].type) {
case
SOLID: SET_RECT(rc,0,0,64,64); DRAW(tile_types_texture,&rc,&pos); break
;
case
PASS: SET_RECT(rc,0,64,64,128); DRAW(tile_types_texture,&rc,&pos); break
;
case
LIQUID: SET_RECT(rc,64,0,128,64); DRAW(tile_types_texture,&rc,&pos); break
;
case
BOUNCE: SET_RECT(rc,128,0,192,64); DRAW(tile_types_texture,&rc,&pos); break
;
case
BUTTON: SET_RECT(rc,128,64,192,128); DRAW(tile_types_texture,&rc,&pos); break
;
case
JUMP_PASSABLE: SET_RECT(rc,0,0,64,64); DRAWC(tile_types_texture,&rc,&pos,0xFF7700FF); break
;
}
x+=64; a++;
}while
(a<tile_x+13);
y+=64; b++;
}while
(b<tile_y+10);
}
if
(copy_a2>-1) {
sel.left=g_half_width-832+(copy_a1-(tile_x-13))*64+(int
)tile_x_off; //13*64
sel.top=g_half_height-640+(copy_b1-(tile_y-10))*64+(int
)tile_y_off; //10*64
sel.right=sel.left+(copy_a2-copy_a1)*64;
sel.bottom=sel.top+(copy_b2-copy_b1)*64;
D3DCOLOR col=D3DCOLOR_ARGB(255,RAND_INT(255),RAND_INT(255),RAND_INT(255));
draw_box(sel,col); //draw_box(sel,0xFF00DD11);
}
//show files tab
pos.x=40; pos.y=(float
)g_height-27; SET_RECT(rc,0,226,162,255); DRAW(tile_types_texture,&rc,&pos);
pos.x=240; DRAW(tile_types_texture,&rc,&pos);
pos.x=440; DRAW(tile_types_texture,&rc,&pos);
pos.x=640; DRAW(tile_types_texture,&rc,&pos);
PRINT_TEXT_EXT(70,g_height-18,0xFF550066,"FILES");
PRINT_TEXT_EXT(270,g_height-18,0xFF550066,"MINI-MAP");
PRINT_TEXT_EXT(470,g_height-18,0xFF550066,"TILE SELECTOR");
PRINT_TEXT_EXT(670,g_height-18,0xFF550066,"INSTRUCTIONS");
PRINT_TEXT_EXT(10,10,default_color,"ALT+R = remove occupied");
if
(remove_occupied) {PRINT_TEXT_EXT(170,10,0xFF88DD00,"(ON)");}
if
(!remove_occupied) {PRINT_TEXT_EXT(170,10,0xFF666600,"(OFF)");}
PRINT_TEXT_EXT(10,20,default_color,"< > to change tile-type (shift<> = all)");
PRINT_TEXT_EXT(10,30,default_color,"- + to change tiles-depth");
PRINT_TEXT_EXT(10,40,default_color,"numpad = change tiles-offset");
if
(show_instructions) {
draw_black_rect(g_width-200,0,g_width,g_height);
int
hp=g_width-190;
PRINT_TEXT_EXT(hp,20,0xFFBB1100,"SHIFT+drag = selection");
PRINT_T(hp,20,0xFF992200,"selection: V = paste copy");
PRINT_T(hp,20,0xFF881100,"selection: M = remove and paste");
PRINT_T(hp,30,default_color,"PageUp/PageDown = fast up/down");
PRINT_T(hp,20,default_color,"Delete/End = fast left/right");
PRINT_T(hp,30,0xFFBB1100,"Z, X = change tile selection");
PRINT_T(hp,20,0xFFFF1100,"< > [,.] = change tile type");
PRINT_T(hp,20,0xFFAA3300,"- + = change tile depth setting");
PRINT_T(hp,30,0xFF11BB00,"ALT+R = toggle 'remove selected'");
PRINT_T(hp,20,0xFF0033BB,"? = toggle grid view on/off");
PRINT_T(hp,30,0xFF887700,"[ ] = rotate");
PRINT_T(hp,20,0xFF66AA00,"; ' = scale");
PRINT_T(hp,30,default_color,"right-click = delete");
PRINT_T(hp,20,0xFF009933,"Control+click = start-point for fill");
PRINT_T(hp,20,0xFF008844,"start_fill + click = fill (endpoint)");
PRINT_T(hp,30,0xFF007744,"Ctrl+Rclick = start-point delete");
PRINT_T(hp,20,0xFF006644,"Shift+Rclick = end-point delete");
PRINT_T(hp,20,0xFF005544,"insert+click = forced tile insertion");
PRINT_T(hp,20,0xFF004466,"alt+click = add tile behind a tile");
}
g_sprite->End();
END_SCENE("config_tiles"); message_pump();
SAFE_DO; }while
((GetTickCount() - starting_point) < TIMING_DELAY);
if
(KEYPRESS(PRINTSCREEN)) GetScreenShot("screenshot.bmp",-1);
}while
(!done);
}//edit_level
//------------------------------
// G E T E D I T O R I N P U T
//------------------------------
enum
EDIT_MENU_ID {NONE_SELECTED, CONFIG_TILES, EDIT_LEVEL, TEST_GAME, CONFIG_AI} this_editmenuid;
void
get_editor_input() {
int
editor_menu=NONE_SELECTED;
POINT cp;
GetCursorPos(&cp); UpdateMousePos(&cp);
if
(left_mouse_clicked) {
left_mouse_clicked=false;
if
(mouse_pos.y<33) {
if
((mouse_pos.x>0)&&(mouse_pos.x<184)) editor_menu=CONFIG_TILES;
if
((mouse_pos.x>187)&&(mouse_pos.x<367)) editor_menu=EDIT_LEVEL;
if
((mouse_pos.x>381)&&(mouse_pos.x<571)) editor_menu=TEST_GAME;
if
((mouse_pos.x>592)&&(mouse_pos.x<774)) editor_menu=CONFIG_AI;
}
}
switch
(editor_menu) {
case
CONFIG_TILES:
config_tiles(); editor_menu=NONE_SELECTED;
break
;
case
EDIT_LEVEL:
edit_level(); editor_menu=NONE_SELECTED;
break
;
case
TEST_GAME:
break
;
case
CONFIG_AI:
break
;
}
}//get_editor_input
//.........................collapsable editor code (region ends here)
#endif
//-----------------------G A M E E D I T O R ---------------------------------
(^_^)
(updated April 4, 2011)
(Note that the following source code has some instructions in main.cpp (comments at top) for fixing some build errors)