Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курсовая работа пример.docx
Скачиваний:
3
Добавлен:
26.09.2019
Размер:
674.4 Кб
Скачать

Проходимость

Свойство проходимости используется для того, чтобы определить, возможно ли продвижение через данный блок. Если блок помечен как непроходимый, подразделения не могут пересекать его. Главным образом это свойство используется в алгоритмах поиска пути, поскольку процедуры поиска пути должны знать, какие части карты являются проходимыми, а какие — нет. Взгляните на пример, изображенный на рис. 

На рис. видно, что для блоков с изображением воды значение проходимости равно 1, а для блоков с изображением земли значение этого же свойства равно 0. Для алгоритма поиска пути это означает, что по воде нельзя перемещаться. Если юнит, расположенный в правой части карты, требуется переместить в левую часть, надо маневрировать таким образом, чтобы переехать преграду по земляному перешейку. Программа узнает как это сделать, основываясь на карте преград.

Возвышенность

Возвышенность — полезное свойство для тех стратегических игр, на картах которых есть настоящие возвышения. Например, в игре Total Annihilation есть возвышенности, влияющие на стратегию. Пулемет, расположенный на вершине холма может стрелять поверх стен вражеской базы. Хороший способ хранить карту возвышенностей — задавать значение высоты каждого блока. Программа может читать значение высоты блока, чтобы определить линию прицеливания.

Яркость

Следующее свойство, яркость, полезно для реализации эффекта тумана войны. Взгляните на рис. чтобы понять, о чем я говорю.

В ы видите, что блоки в центре рис. яркие, а по мере приближения к краям они становятся все темнее и темнее. Это вызвано тем, что в центре рисунка располагается подразделение игрока, которое может видеть территорию вокруг себя. Присваивая блокам вокруг подразделения различные значения яркости, графическая библиотека реализует эффект области ухудшающейся видимости. Значение яркости, раное 1.0, означает, что блок полностью освещен, а значение, равное 0.0, означает что блок скрыт во тьме. Темные блоки указывают те области, которые данное подразделение не может видеть.

Смещение

Предположим, мы создали дерево высотой 64 пикселя и шириной 32 пикселя. Проблема с таким делевом заключается в том, что когда мы передаем этот блок процедуре визуализации, на изображении будет казаться, что дерево погрузилось в землю, поскольку оно является слишком высоким.

Чтобы решить эту проблему вы должны при визуализации деревьев учитывать смещение. Чтобы вычислить смещение, возьмём высоту обычных блоков и вычтите из нее высоту блока с изображением дерева. В приведенном примере высота дерева равна 64 пикселям, а высота обычного блока — 32 пикселям. Таким образом, смещение будет равно 32 – 64 = –32 пикселя. Теперь, при отображении карты, все деревья будут выводиться в позиции Y + –32. Это перемещает изображение деревьев при визуализации на 32 точки вверх. Если желаете, можете выполнять подобные изменения, добавляя смещения по осям X и Y. В основном данный метод используется для выравнивания блоков, которые не вписываются в стандартный размер блоков.

Отображение двухмерных изометрических блоков

вывод карты

void map_view()

{

char str[10];

int xx,yy;

int m_x=256,m_y=0; //положение карты на экране

//вывод тайлов

for(int y=0;y<24;y++)

for(int x=0;x<24;x++)

{

xx=x+map_x,yy=y+map_y; //для избежания повторных вычислений нарисовать тайл

DdrawBltFast(m_x+x*32,m_y+y*32 //куда

,map[xx][yy].x,map[xx][yy].y //откуда

,32,32,surf_layer[map[xx][yy].layer]); //какая поверхность

}

for(int y=0;y<24;y++)

for(int x=0;x<24;x++)

{

xx=x+map_x,yy=y+map_y; //для избежания повторных вычислений

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

int r=map_obj[xx][yy].type;

int n=map_obj[xx][yy].number;

if(r==c_tree||r==c_rock)

{

int rox=res[n].ox;

int roy=res[n].oy;

int rx=res[n].x;

int ry=res[n].y;

int rw=res[n].w;

int rh=res[n].h;

int rastr=res[n].rastr;

blit_image_trans(m_x+x*32+rox,m_y+y*32+roy //куда

,rx,ry,rw,rh //откуда

,m_x,m_y,SizeWindowX,SizeWindowY //клипирование

,rastr); //какая поверхность

}

if(r==c_unit)

{

//определить какой ролик

int a=unit[n].spr; //какой спрайт

int r=unit[n].rastr; //какой растр

int act=unit[n].anim; //анимация

int dir=unit[n].dir; //направление

int k=unit[n].kadr; //какой кадр

int px=unit[n].px; //положение на карте в пикселах

int py=unit[n].py;

//данные кадра

int ox=an[a].spr[act][dir][k].ox;

int oy=an[a].spr[act][dir][k].oy;

int fx=an[a].spr[act][dir][k].x;

int fy=an[a].spr[act][dir][k].y;

int fw=an[a].spr[act][dir][k].w;

int fh=an[a].spr[act][dir][k].h;

if(r!=-1&&r<image_max)blit_image_trans(m_x+x*32+ox,m_y+y*32+oy,fx,fy,fw,fh

,m_x,m_y,SizeWindowX,SizeWindowY,r);

} } }

Воспроизведение WAV файлов.

На рис. видно, что WinMain() вызывает функцию bInitializeSoundSystem(). Эта функция инициализации выполняет несколько вызовов функций DirectX для инициализации звуковой системы. Затем программа ожидает события мыши и воспроизводит файл WAV с помощью функции vPlaySound().

Класс для чтения файлов формата WAV

CMappedWave::CMappedWave()

{

//Обнуляем все члены класса

pbWaveFile=NULL;

pWaveHeader=NULL;

dwCurPosition=0;

dwDataLength=0;

}

CMappedWave::~CMappedWave()

{

//Закрываем файл, если он был открыт

Close();

}

BOOL CMappedWave::Open(CHAR* szFilename)

{

//Закрываем файл, если он был открыт

Close();

HANDLE hFile,hMappedFile;

DWORD dwFileLength;

//Пытаемся открыть файл

hFile=CreateFile(szFilename,GENERIC_READ,FILE_SHARE_READ,NULL,

OPEN_EXISTING,NULL,NULL);

if (hFile==INVALID_HANDLE_VALUE)

return (FALSE);

//Определяем размер файла

dwFileLength=GetFileSize(hFile,NULL);

//Создаём объект - проекция файла

hMappedFile=CreateFileMapping(hFile,NULL,PAGE_READONLY,

0,dwFileLength,NULL);

//Закрываем описатель файла (но не сам файл!!!)

CloseHandle(hFile);

if (!hMappedFile)

return (FALSE);

//Проецируем файл на адресное пространство

pbWaveFile=(PBYTE)MapViewOfFile(hMappedFile,FILE_MAP_READ,0,0,0);

//Закрываем описатель проекции файла

CloseHandle(hMappedFile);

if (!pbWaveFile)

return (FALSE);

//Инициализируем оставшиеся члены класса

pWaveHeader=(LPWAVEFORMATEX)(pbWaveFile+WAVEFORMATEX_SHIFT);

dwCurPosition=WAV_HEADER_SIZE;

dwDataLength=*((DWORD*)(pbWaveFile+WAV_HEADER_SIZE-sizeof(DWORD)));

return (TRUE);

}

void CMappedWave::Reset()

{

//Сбрасываем текущую позицию в значение по умолчанию

if (pbWaveFile)

dwCurPosition=WAV_HEADER_SIZE;

}

void CMappedWave::Close()

{

if (pbWaveFile)

{

//Отключаем проекцию файла от адресного пространства

UnmapViewOfFile(pbWaveFile);

pbWaveFile=NULL;

}

//Обнуляем члены класса

pWaveHeader=NULL;

dwDataLength=0;

dwCurPosition=0;

}

DWORD CMappedWave::Read(BYTE* pbData,DWORD dwSizeToRead)

{

DWORD dwRemainder;

//Если файл не был открыт, либо все данные прочитаны

//возвращаем 0 - больше делать нечего

if ((!pbWaveFile)||((dwDataLength+WAV_HEADER_SIZE)==dwCurPosition))

return (0);

if (dwSizeToRead<=(dwDataLength+WAV_HEADER_SIZE-dwCurPosition))

{

//Если достаточно данных для чтения

CopyMemory(pbData,pbWaveFile+dwCurPosition,dwSizeToRead);

dwCurPosition+=dwSizeToRead;

dwRemainder=dwSizeToRead;

}

else //Если данных недостаточно

{

//Копируем сколько есть

dwRemainder=dwDataLength-dwCurPosition+WAV_HEADER_SIZE;

CopyMemory(pbData,pbWaveFile+dwCurPosition,dwRemainder);

//Заполняем остаток молчанием

FillMemory(pbData+dwRemainder,dwSizeToRead - dwRemainder,

(pWaveHeader->wBitsPerSample==8) ? 128 : 0);

dwCurPosition+=dwRemainder;

}

return (dwRemainder);

}

DWORD CMappedWave::GetWaveSize()

{

//Возвращаем размер только звуковых данных - без заголовка

if (pbWaveFile)

return (dwDataLength);

else

return (0);

}

Воспроизведение WAV файла.

Создаёт буфер звукового эффекта на основе файла WAV

bool LoadEffect(int* pointer,char* filename)

{

int n=*pointer;

if(n==-1){n=effect_num;}//индикатор говорит,что звук не существует

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

HRESULT hRet;

DSBUFFERDESC dsbd;

// Cоздаём объект для работы с файлом WAV

pWave = new CMappedWave();

if (!pWave)

{

ErrorHandle(hWnd, "pWave");

return (false);

}

//Открываем нужный файл при помощи объекта класса CMappedWave

if (!pWave->Open( filename ))

{

ErrorHandle(hWnd, "pWave-Open music");

return (false);

}

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

//если звуковой буфер существует - уничтожить его

if(effect[n][0]){effect[n][0]->Release();effect[n][0]=NULL;}

if(effect[n][1]){effect[n][1]->Release();effect[n][1]=NULL;}

if(effect[n][2]){effect[n][2]->Release();effect[n][2]=NULL;}

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

//Заполняем структуру описания...

ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );

dsbd.dwSize = sizeof(DSBUFFERDESC);

dsbd.dwFlags = DSBCAPS_STATIC | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME |

DSBCAPS_CTRLFREQUENCY ;

dsbd.dwBufferBytes = pWave->GetWaveSize();

dsbd.lpwfxFormat = pWave->GetWaveHeader();

//... и с её помощью создаём музыкальный буфер!

hRet=pDS->CreateSoundBuffer( &dsbd, &effect[n][0], NULL );

if (hRet!=DS_OK)

{

ErrorHandle(hWnd, "CreateMusicBuffer");

return (false);

}

VOID* pPtr1 = NULL;

VOID* pPtr2 = NULL;

DWORD dwSize1;

DWORD dwSize2;

//Запоминаем размер звуковых данных

DWORD dwWaveSize=pWave->GetWaveSize();

pWave->Reset();

//Пытаемся заблокировать звуковой буфер

hRet = effect[n][0]->Lock( 0, dwWaveSize, &pPtr1, &dwSize1,

&pPtr2, &dwSize2, 0L);

if (hRet!=DS_OK)

{

ErrorHandle(hWnd, "effect[n][0]->Lock");

return (false);

}

//Если получилось, считываем в буфер звуковые данные

pWave->Read((PBYTE)pPtr1,dwWaveSize);

//Не забываем разблокировать буфер

effect[n][0]->Unlock( pPtr1, dwWaveSize, NULL, 0 );

//Дублировать звук

pDS->DuplicateSoundBuffer(effect[n][0],&effect[n][1]);//оказалось никчему

pDS->DuplicateSoundBuffer(effect[n][0],&effect[n][2]);//слишком большое колличество звуков

//если все прошло успешно - передаем номер созданного звукового буфера указателю

*pointer=n;

effect_num++;//увеличить счетчик звуковых эффектов

return (true);

}

//Проигрывает звуковой эффект

void playeffect(int pan,int volume,int buf,int n)

{

// проверить существует ли буфер

if(effect[buf][n]==NULL)return ;

HRESULT hRet;

//Перемещаем в начало текущую позицию проигрывания

effect[buf][n]->SetCurrentPosition(0);

//Установка дальности звука

effect[buf][n]->SetVolume(volume);//DSBVOLUME_MAX==0;DSBVOLUME_MIN==-10000.

//Установка Левого или Правого положения звука

effect[buf][n]->SetPan(pan);// -10000 и 10000

//Начинаем воспроизводить звук

hRet=effect[buf][n]->Play(0, 0, 0);//DSBPLAY_LOOPING );

}

//проигрывание звукового эффекта в тройном экземпляре

void PlayEffect(int pan,int volume,int buf)

{

if(!EffectStatus(buf,0)){playeffect(pan,volume,buf,0);return;}

// else if(!EffectStatus(buf,1)){playeffect(pan,volume,buf,1);return;}//оказались никчему

// else if(!EffectStatus(buf,2)){playeffect(pan,volume,buf,2);return;}

}

//остановка воспроизведения эффекта

void StopEffect(int buf,int n)

{

// проверить существует ли буфер

if(effect[buf][n]==NULL)return ;

effect[buf][n]->Stop();

}

//статус воспроизведения эффекта

bool EffectStatus(int buf,int n)

{

// проверить существует ли буфер

if(effect[buf][n]==NULL)return false;

ULONG status;

effect[buf][n]->GetStatus(&status);

if(status & DSBSTATUS_PLAYING)return true;

return false;

}

Отрисовка спрайтов.

Процедура нарезки фрэймов и создания спрайтов

void sprite_editor()

{

if(SizeWindowX!=1024){ErrorHandle(hWnd,"задании размера экрана");gameover=true;return;}

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

//массив для создания и вывода строки

char str[256];

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

//переменная выбора

int choice=0;

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

//выделение памяти под массив структур фрэймов

memset(frm,0,sizeof(frm));

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

//забить массив роликов нулями

memset(roll,0,sizeof(roll));

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

//забить массив анимации отрицательными значениями

memset(anim,-1,sizeof(anim));

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

//цикл идет пока gameover равен false

while(!gameover)

{

//инициализация задержки

DWORD start_time=GetTickCount();

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

//поставщик сообщений

ProcessMessages();

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

//панель

box(0,0,256,768,gray3);

box(256,0,SizeWindowX-256,768,gray);

rectangle(0,0,256,768,gray4,black);

rectangle(1,1,254,766,white,gray);

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

//область выбора

int x=14,y=25,w=110,h=15;

//кнопка выбора

if(button(x,y,w,h,"open image")){open_rastr();}

//размеры растра

y+=25;

wsprintf(str,"Image's Width %d Hight %d",rastr_w,rastr_h);

printSb(str,x,y);

y+=20;w=70;

//кнопка выбора

if(button(x,y,w,h,"frame")){choice=0;}

if(choice==0)rectangle(x-2,y-2,w+4,h+4,blue);

//кнопка выбора

x+=78;

if(button(x,y,w,h,"roll")){choice=1;}

if(choice==1)rectangle(x-2,y-2,w+4,h+4,blue);

x+=78;

if(button(x,y,w,h,"sprite")){choice=2;}

if(choice==2)rectangle(x-2,y-2,w+4,h+4,blue);

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

//разделительная линия

x=15,y+=25;

box(x,y,225,1,gray);

box(x,y+1,225,1,white);

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

//вызов соответствующей функции

if(choice==0)frame();//если frame

//--------

if(choice==1)Roll();//если roll

//--------

if(choice==2)Sprite();//если sprite

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

//***************************************

//синхронизация частоты кадров

while((GetTickCount()-start_time)<10);

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

//вывод скорости смены кадров и битовый режим

int nFps=UpdateFPS();

wsprintf(str,"%d Fps- %d",BIT_MODE,nFps);

printSb(str,50,5);

//вывод положения курсора

wsprintf(str,"cur_x %d cur_y %d",cur_x,cur_y);

printSb(str,110,5);

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

Flip();//вывести вторичный буфер на экран

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

//***************************************

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

//запрос на выход

//если нажата клавиша ESCAPE

if(KEYDOWN(VK_ESCAPE))gameover=true;

key=0;//обнулить переменную клавиш

rbutton=false;//обнулить состояние правой клавиши мыши

}

Процедура создания анимации персонажей

состоящей из действий - стоит, идет, атакует, умирает.

int sprt[64];

int sprk[64];

void open_sprite();//объявление функции

void Sprite()

{

int ox,oy,fx,fy,fw,fh;

//кнопка выбора

int x=16,y=105,w=70,h=15;

if(button(x,y,w,h,"new sprites")){new_sprite();}//очистка массива спрайтов

x+=77;

if(button(x,y,w,h,"open sprites")){open_sprite();}//открытие файла спрайтов

x+=77;

if(button(x,y,w,h,"save sprites")){save_sprite();}//сохранение спрайтов в файл

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

x=16;

y+=55;

//изменение общей временной задержки

spin(x+35,y,&time);

if(key=='7')time--;

if(key=='9')time++;

if(time<0)time=0;if(time>100)time=100;

//рамка вокруг области

rectangle(x-9,y-8,242,32,gray,white);

rectangle(x-8,y-7,240,30,white,gray);

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

//временная задержка воспроизведение для всего ролика

wsprintf(str,"%d",time);

text_box(x,y,30,15,str);

printSb("общая задержка ( '7' и '9' )",x+65,y+3);

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

y+=35;

//изменение текущего направления

spin(x+35,y,&dir);

//изменение текущего направления

if(key=='a')dir--;

if(key=='s')dir++;

if(dir<0)dir=0;if(dir>7)dir=7;

//рамка вокруг области

rectangle(x-9,y-8,242,32,gray,white);

rectangle(x-8,y-7,240,30,white,gray);

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

//текущее направление

wsprintf(str,"%d",dir+1);

text_box(x,y,30,15,str);

printSb("текущее направление ( 'a' и 's' )",x+65,y+3);

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

y+=35;

//изменение текущего действия

spin(x+35,y,&act);

//изменение текущего действия

if(key=='z')act--;

if(key=='x')act++;

if(act<0)act=0;if(act>7)act=7;

//рамка вокруг области

rectangle(x-9,y-8,242,32,gray,white);

rectangle(x-8,y-7,240,30,white,gray);

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

//текущее действие

wsprintf(str,"%d",act+1);

text_box(x,y,30,15,str);

printSb("текущее действие ( 'z' и 'x' )",x+65,y+3);

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

//удаление ролика

x=8,y+=35,w=240,h=20;

if(button(x,y,w,h,"Удалить ролик ('Delete')")||KEYDOWN(VK_DELETE)){anim[act][dir]=-1;}

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

int r=0;

for(y=0;y<8;y++)

for(x=0;x<8;x++)

{

if(sprt[r]++>time+frm[roll[r].frm[sprk[r]]].time)//задержка

{

sprk[r]++;if(roll[r].frm[sprk[r]]==0||sprk[r]>15)sprk[r]=0;//сменить кадр

sprt[r]=0;

}

//активирование действия

if(CursorLD(256+96*x,80*y,94,78))spr=x;

//показать в палитре все существующие кадры

ox=frm[roll[r].frm[sprk[r]]].ox;

oy=frm[roll[r].frm[sprk[r]]].oy;

fx=frm[roll[r].frm[sprk[r]]].x;

fy=frm[roll[r].frm[sprk[r]]].y;

fw=frm[roll[r].frm[sprk[r]]].w;

fh=frm[roll[r].frm[sprk[r]]].h;

if(rastr!=-1)blit_image_trans(256+1+96*x+ox+48,80*y+oy+55,fx,fy,fw,fh,256+1+96*x,80*y,96,80,rastr);

rectangle(256+96*x,80*y,96,80,black,white);

if(CursorRD(256+96*x,80*y,96,80))anim[act][dir]=r;//создать спрайт

r++;

}

//палитра спрайтов

box(0,640-5,255,1,gray);

box(0,640-4,1024,1,black);

box(0,640-3,1024,1,gray3);

box(0,640-2,1024,1,white);

box(0,640-1,1024,127,gray);

for(int i=0;i<8;i++)

{

rectangle(128*i,640,128,128,black,white);

//активирование действия

if(CursorLD(128*i,640,124,124))act=i;

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

//показать в палитре все существующие спрайты

int a=anim[i][dir];//номер спрайта

ox=frm[roll[a].frm[sprk[a]]].ox;

oy=frm[roll[a].frm[sprk[a]]].oy;

fx=frm[roll[a].frm[sprk[a]]].x;

fy=frm[roll[a].frm[sprk[a]]].y;

fw=frm[roll[a].frm[sprk[a]]].w;

fh=frm[roll[a].frm[sprk[a]]].h;

if(rastr!=-1&&a!=-1)blit_image_trans(128*i+1+ox+64,641+oy+80,fx,fy,fw,fh,128*i+1,641,124,124,rastr);

}

//рамка выделения активного спрайта

rectangle(128*act+1,641,126,126,green);

rectangle(128*act+2,642,124,124,green);

}

Вывод растра c клипированием и с учетом прозрачного цвета

void blit_image_trans(int x,int y,int dx,int dy,int width,int height,int c_x,int c_y,int c_w,int c_h,

UINT pSurface)//поверхность источник

{

//клипирование(урезание невидимых частей) выводимого прямоугольника

if(x<c_x){ dx=dx+(c_x-x);width=width-(c_x-x); x=c_x;if(width>c_w)width=c_w;}

if(y<c_y){ dy=dy+(c_y-y);height=height-(c_y-y); y=c_y;if(height>c_h)height=c_h;}

if(x+width>c_x+c_w)width=c_w-(x-c_x);

if(y+height>c_y+c_h)height=c_h-(y-c_y);

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

RECT rPic;

//Установка размеров копируемого блока данных

SetRect(&rPic, dx, dy, dx+width, dy+height);

//функция DirectDraw для вывода ратра

buffer/*куда копировать*/->BltFast(x, y, //с учётом прозрачного цвета

image[pSurface]/*откуда копировать*/,&rPic,DDBLTFAST_SRCCOLORKEY |DDBLTFAST_WAIT);

}

Волновой алгоритм поиска пути.

функция вычисления пути

int findpath(int radius, int* sx,int* sy, int ex,int ey,WPOINT wpoint[lenpath] )

{

int pStartx=*sx;

int pStarty=*sy;

if(pStartx<1||pStartx>width||pStarty<1||pStarty>height)return -1;

if(ex<1||ex>width||ey<1||ey>height)return -1;

//копирование карты блокировок в карту поиска пути

memcpy(temp, wmap, sizeof wmap);

Start(pStartx,pStarty,radius);//установка возможных точек прибытия

temp[ex][ey]=end;//установка точки финиша

//начать список с конечной позиции(поиск идет от конца к старту)

list_i[0].x=ex;

list_i[0].y=ey;

//"обнулить" счетчик_источник

count_i=1;

bool ok;

//запустить генерацию волн(поиск пути)

for(wave=1;wave<lenpath;wave++)

{

count_p=0;//обнулить счетчик_приемник

ok=Wave();

if(ok)break;

}//end for

if(!ok)return -1;//если путь не найден

//установка новых координат старта

pStartx=new_st.x;

pStarty=new_st.y;

int cur;

//Вернуться от старта к концу по направлениям из карты направлений

//Путаница в названиях - конец это где стоит объект(для программы имеют значения только числа)

// - старт это куда надо прийти

for(int i=0;i<wave;i++)

{

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

wpoint[i].x=pStartx;//передача координать объекту

wpoint[i].y=pStarty;

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

cur=curs[pStartx][pStarty];//взять направление из карты_направлений

pStartx=pStartx+dir[cur].x;//изменение координат

pStarty=pStarty+dir[cur].y;

}

*sx=new_st.x;//передать новые координаты финиша

*sy=new_st.y;

return wave;

}

Вычисление движения объекта

int step(int* x,int* y,int ex,int ey,int a)

{ //a - длина шага

int z,b,tx,ty;

int sx=*x;

int sy=*y;

//направления 7 0 1

6 * 2

5 4 3

if((sx<ex)&&(sy==ey))//направо

{

tx=sx+a;

if(tx>ex)tx=ex;

*x=tx;

*y=sy;

return r; }

Движение для других направлений определяется так же.

Список источников

http://www.iskint.ru/?xid=games-poisk_puti

http://www.koders.com/c/

http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=4064

http://www.boswars.org/download.shtml

http://netlib.narod.ru/library/book0051/