Кодим на QUAKEC
-
Скрыть объявление
Друзья, в это тяжёлое и непонятное для всех нас время мы просим вас воздержаться от любых упоминаний политики на форуме, - этим ситуации не поможешь, а только возникнут ненужные ссоры и обиды. Это касается также шуток и юмора на тему конфликта. Пусть войны будут только виртуальными, а политики решают разногласия дипломатическим путём. С уважением, администрация Old-Games.RU.
-
Скрыть объявлениеЕсли Вы видите это сообщение, значит, вы ещё не зарегистрировались на нашем форуме.
Зарегистрируйтесь, если вы хотите принять участие в обсуждениях. Перед регистрацией примите к сведению:
- Не регистрируйтесь с никами типа asdfdadhgd, 354621 и тому подобными, не несущими смысловой нагрузки (ник должен быть читаемым!): такие пользователи будут сразу заблокированы!
- Не регистрируйте больше одной учётной записи. Если у вас возникли проблемы при регистрации, то вы можете воспользоваться формой обратной связи внизу страницы.
- Регистрируйтесь с реально существующими E-mail адресами, иначе вы не сможете завершить регистрацию.
- Обязательно ознакомьтесь с правилами поведения на нашем форуме, чтобы избежать дальнейших конфликтов и непонимания.
С уважением, администрация форума Old-Games.RU
-
Я как тот грузин, что вижу, то пою. Поэтому и пишу о том в чём сейчас копаюсь. Писать о том, в чём я хорошо разбираюсь, мне неинтересно, у меня такое ощущение, что это должны знать все, закидаю спецтерминами, и вы ничего не поймёте.
А копаюсь я сейчас в коде кваки. И честно сказать, кодер из меня такой себе. Но так даже лучше, ощущения свежи и все ошибки ещё в памяти - новичкам будет проще. Сейчас будет буквально пошаговая инструкция:
Итак quakec. Что это такое? По ссылке есть информация, но если в двух словах - это не код движка - это код игры. Язык похож на С, что понятно из названия, и его тоже нужно компилить, специальным компилятором (слава богу не Visual Studio). Тут почти безальтернативно - fteqcc, а ещё лучше fteqccgui (у него есть рабочий интерфейс, а не просто командная строка).
Допустим, мы прямо сейчас решили покодить для кваки. Качаем исходники Quake (игры, не движка), это специальная версия, почищенная от говна, сохраняем куда нибудь на хард (рекомендую создать отдельную директорию, типа: С://QSources, потому что если уж вы начнёте, всяких исходников и примеров там будет море). Клонируем все файлы оттуда в какую нибудь новую папку, чтобы путь выглядел как ниубдь так:
С://QSources/yoursourcename/qc
Копируем в ту же папку (папку qc) fteqccgui. Он работает очень просто, можно проверить прямо сейчас. Открываем его, нажимаем кнопочку Сompile, прога должна написать DONE (если пишет error, значит очевидно что-то не так), и в директории выше (yoursourcename) должен создаться файлик progs.dat.
Далее, сделаем простой мод. Создадим в папке игры Quake ...у вас её ещё нет? Тогда инструкция:
Сегодня даже необязательно пиратить игру. Есть совершенно фришный аналог LibreQuake. Он содержит все ресурсы кваки, просто в изменённом виде, обходя авторские права. Ну ладно вру не все, там есть заглушки, но смысл именно в том, что можно запустить любой мод не опасаясь ошибок и глюков. Качаем любой форк кваки, VKQuake напримерникакого отношения к вконтакте он не имеет vk - сокращение от Vulkan, то есть движок опрерирует не OpenGL, а Vulkan, Ironwail хороший форк, но...не видит новую систему частиц, либо тот-же Darkplaces. Везде есть свои плюсы и минусы, но сейчас это неважно, код заработает везде.
Качаем движок в любое место, например C://Engines/VKQuake, распаковываем, и кидаем в папку с движком, папку id1 из LibreQuake. Можете даже запустить поиграть, правда ванильных карт там нет, какие-то свои, и огр сделан забавной заглушкой, но это всё мелочи.
Создаём в той же директории папку с любым названием, например как у меня npc. Чем короче название тем лучше, вам с этим ещё работать и работать. Должно получится что-то вроде C://Engines/VKQuake/npc. В эту же папку нужно будет кидать файл progs.dat, но сейчас в этом мало смысла.
Исходники я взял отсюда, но там они были неполные, и не делали всё что надо, даже совет на странице неправильный. Поэтому мне пришлось их сильно править. Итак, поехали.
Создаём в папке с исходниками файл NPC.qc (я пишу новые файлы большими буквами, чтобы сразу их видеть, но компилятору на это наплевать). Открываем его любым текстовым редактором, лучше всего Notepad++Сайт которого сейчас лежит, как и половина интернета в этой стране, задолбали!, и вставляем туда этот код:
//Я добавил в код свои комменты, но ниже будут подробные объяснения:
// ORIGNAL STUFFS BY ceriux
// Other stuff by Dr. Shadowborg
//id0 was here too
// ceriux was here.
// Dr. Shadowborg was here, and yea doth did he fixith it.
void() npc_idle =
{
local entity selffoo; // because I need to do some entity foobing.
// Check to see if player is within range, and is actually facing it too.
// in short, if your further than 120 qu, can't see the npc or aren't facing
// it enough, it will quit interacting.
if(self.enemy != world)
{
selffoo = self; // this is the foobing I was talking about.
self = self.enemy; // more foobing.
if((vlen(self.origin - selffoo.origin) > 120) || !visible(selffoo) || !infront(selffoo))
{
selffoo.enemy = world;
self.currentnpc = world;
self.npc_menu = 0;
centerprint(self, "\n");
}
self = selffoo; // foobing around finished.
}
// face the player it's currently interacting with, and do so as long as it's
// in menu mode.
if(self.enemy != world){
ai_face();
}
//id0
//Здесь я добавил анимацию персонажа. В Quake покадровая система анимации, она запакована в модели (mdl)
//У меня idle начинается с 0 и заканчивается на 20, а с 20 до 30 персонаж машет рукой.
//Когда помахал, анимация сбрасывается снова на idle. Это и просиходит в коде ниже.
if(self.frame >= 21){
// if frame greater than 30, reset frame to 0.
if(self.frame >= 30)
self.frame = 0;
}
else{
// if frame greater than 20, reset frame to 0.
if(self.frame >= 20)
self.frame = 0;
}
// Advance frame by one.
self.frame = self.frame + 1;
// repeat this idle function every 0.1 seconds.
self.think = npc_idle;
self.nextthink = time + 0.1;
};
// ceriux was here too.
// And yea did the borgh cometh here as well, and so too did he fixith.
void() npc_test =
{
//id0
//прекэшинг модели (и всего остального), то есть подготовка движка к их загрузке.
//Лучше всего всегда так делать.
// precache. Because precache is good.
precache_model ("progs/uma.mdl");
// Sets what the hell it's supposed to look like.
setmodel (self, "progs/uma.mdl");
//id0
// Здесь назначается звук,который лежит в папке "sounds/npc"
//папку sounds указывать необязательно, движок сам знает.
//Со временем можно сделать, чтобы каждый нпс говорил свою фразу, сейчас это просто возглас.
precache_sound ("npc/_reitanna__euh3.wav");
// Sets the bounding box size for the npc. Because it will look dumb
// otherwise.
setsize(self, '-8 -8 -24', '8 8 30');
// Unless you wanna go Half-life hazard course hologram, it would be
// pretty creepy to have an NPC be non-solid...
self.solid = SOLID_SLIDEBOX;
// so it can be affected by gravity 'n stuffs.
self.movetype = MOVETYPE_STEP;
// So that we know what the hell this is by way of npc, not npc_test.
self.classname = "npc";
// so he can properly face you when you talk to him.
self.ideal_yaw = self.angles * '0 1 0';
if (!self.yaw_speed)
self.yaw_speed = 20;
// I'm starting to loathe what droptofloor() does. DRS Note to self:
// create new droptofloor() function that doesn't suck, and isn't
// gay about things like items being within monster bboxes...
droptofloor();
// gotta set what it's supposed to be doing...Namely hanging around
// looking cool and everything.
self.think = npc_idle;
self.nextthink = time + 0.1;
};
// And yea, did Dr. Shadowborg creatith the following.
// Because we needs some way to interact with the beeyatch, and I don't
// have time to cook up some fancypants "touch with player lockdown and
// only works when facing" thangie right now. Maybe I will later though!
// Just like an W_Fireaxe(), except that it's a npc_use(). =P
void() npc_use =
{
local vector source;
local vector org;
makevectors (self.v_angle);
source = self.origin + '0 0 16';
traceline (source, source + v_forward*64, FALSE, self);
if (trace_fraction == 1.0)
return;
org = trace_endpos - v_forward*4;
if (trace_ent.classname == "npc"){
//id0
// Здесь советовали сделать так: self.npc_menu = trace_ent.npc_menu;
// Но это не работает.
// if we hits an npc, then yays, we can talk to him!
//id0: - А это работает:
self.message = trace_ent.message;
self.npc_menu = 1;
sound (trace_ent, CHAN_VOICE, "npc/_reitanna__euh3.wav", 1, ATTN_NORM);
self.currentnpc = trace_ent;
// Advance frame to 21
//if(self.frame < 20)
trace_ent.frame = 21;
// if somebody else was interacting with npc, then cancel his
// interaction.
if(trace_ent.enemy != world){
trace_ent.enemy.npc_menu = 0;
centerprint(trace_ent, "\n");
}
// Because it would look dumb talking to a npc that doesn't face you.
trace_ent.enemy = self;
}
else{
// hit wall
// FOOL! WHY ARE YOU TALKING TO THAT WALL?! WALLS CANT TALK!
// ...Or can they? O_o
sound (self, CHAN_WEAPON, "player/axhit2.wav", 1, ATTN_NORM);
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);
}
self.attack_finished = time + 0.5; // No spamming usekey. Or I KEEEEEEL YOU.
};
Далее создаём там-же файл NPC_MENUS.qc и копируем туда это:
// ceriux was here.
//id0 was here too
// ceriux was here.
//id0 was here too
void() Menus =
{
if (!self.npc_menu)
return;
if (self.npc_menu == 1) {// DRS Fixzors again. This time a CR and also ==
//centerprint(self, "Hello! I am a test NPC\n Press 1 to exit\n");
//id0
//Вверху то, что было^
//А вот как надо:
if(self.message != "")
centerprint(self, self.message);
// Здесь движок проверяет вписано ли сообщение в entity с уровня игры
// и если оно есть печатает на экране
}
};
// ceriux was here.
void(float inp) Select =
{
if (!self.npc_menu)
return;
if (self.npc_menu == 1)
{
if (inp == 1)
{
self.npc_menu = 0;
self.impulse = 0;
self.currentnpc.enemy = world; // No longer interacting with player.
centerprint(self, "\n"); // DRS: Clear the screen NOW!
}
}
Menus();
self.npc_menutime = time + 0.2; // DRS: DAMMIT I SAID NO CENTERPRINT SPAMMAGE! =P
};
//////////////////////////////////////////////////////////////////////////////////
Но это ещё не всё. В коде добавлены переменные, которых ещё нет. Создаём файл GAMEDEFS.QC (чтобы не забивать родной defc.qc ерундой) и копируем туда вот это:
//functions
void(float inp) Select;
void() SecondImpulse;
void() Menus;
void() npc_use; // Because just looking at an npc meaningfully won't get you
// to second base. (DRS)
//floats
.float npc_menu;
.float npc_menutime; // Someone set us up the menutime! For great justice! (DRS)
.entity currentnpc; // so that the player can keep track of who he's talking to.
//////////////////////////////////////////////////////////////////////////////////
Сохраняем всё это, и можно закрыть.
Затем открываем файл weapons.qc и находим блок:
void() ImpulseCommands =
{
if (self.impulse >= 1 && self.impulse <= 8)
W_ChangeWeapon();
if (self.impulse == 9)
CheatCommand();
if (self.impulse == 10)
CycleWeaponCommand();
if (self.impulse == 11)
ServerflagsCommand();
if (self.impulse == 12)
CycleWeaponBack();
if (self.impulse == 255)
QuadCheat();
self.impulse = 0;
};
//////////////////////////////////////////////////////////////////////////////////
Меняем его вот так:
void() ImpulseCommands =
{
if (self.npc_menu < 1) // if menu's are on use second impulse commands
{
if (self.impulse >= 1 && self.impulse <= 8)
W_ChangeWeapon();
if (self.impulse == 9)
CheatCommand();
if (self.impulse == 10)
CycleWeaponCommand();
if (self.impulse == 11)
ServerflagsCommand();
if (self.impulse == 12)
CycleWeaponBack();
//ВНИМАНИЕ!!! В других исходниках тут может быть CycleWeaponReverseCommand (); вместо CycleWeaponBack();
//Просто замените строчку.
if (self.impulse == 255)
QuadCheat();
if(self.impulse == 13)
npc_use();
self.impulse = 0;
}
else // If they don't have a class do this
{
SecondImpulse(); // call special impulse commands below
}
};
Добавляем сразу под этим блоком ещё один:
void() SecondImpulse =
{
if (self.impulse == 1)
Select(1);
};
Сохраняем и закрываем.
////////////////////////////////////////////////////////////////
Далее в файле client.qc
ищем блок кода:
void() PlayerPreThink =
и добавляем в самом низу, перед };
//id0
//Здесь мы хардкодом добавлям кпоку e для "использовать"
//По хорошему нужно делать более изящно, но пока так.
stuffcmd(self,"alias use \"impulse 13\"\n");
stuffcmd(self,"bind e use \n");
Теперь ищем там же
void() PlayerPostThink =
и так же внизу добавляем, после CheckPowerups();
и перед закрывающей скобкой.
// Dr. Shadowborg was here. No unnecessary centerprint spamage here boy!
if(self.npc_menutime < time)
Select(0); // don't forget to give a void that normally request a value a value. Or the compiler monster will eat you.
Сохраняем и закрываем.
//////////////////////////////////////////////////////////////////////////////////
Но и это ещё не всё, надо рассказать движку о нашем неписе. Для этого открываем файл progs.src
и добавляем сразу после строки:
defs.qc.
строку
gamedefs.qc
затем добавляем в самом низу ещё две строчки:
npc_menus.qc
npc.qc
Сохраняем всё и компилим.
Если не скомпилилось - смотрим в код ошибки, и смотрим мои комменты, например почему-то в некоторых исходниках, в файле weapons.qc вместо CycleWeaponReverseCommand (); идёт CycleWeaponBack(); или наоборот.
Если-же всё скомпилилось, всё жёлтенькое или беленькое, а не красненькое, значит в папке выше создался файл progs.dat. Это хорошо, но сначала некоторые пояснения:
В файле npc.qc прописаны харкодом моделька персонажа, анимации и звуки, например:
setmodel (self, "progs/uma.mdl");
precache_sound ("npc/_reitanna__euh3.wav");
и другие.
Можно сделать, чтобы у каждого объекта на уровне могли быть свои звуки и модели, но сейчас у меня не было такой задачи. Можно либо закомментить это всё, но тогда бы будете видеть пустое место, либо прописать свои пути в коде, например (progs/knight.mdl) - моделька рыцаря из самой кваки. анимацию она будет играть другую, но это неважно для теста.
Звуки, если что, должны лежать в папке мода /sound и далее подпапки, здесь это /npc. Движок сразу ищет там, писать sound/ не нужно. В то время как модели могут лежать в любой папке мода, например models/итд...
Звуки тоже можно прописать родные, например precache_sound ("buttons/switch02.wav");
Прописываем что надо, сохраняем и компилим. Если результат успешный, копируем созданный progs.dat в папку мода. Она пока пустая, но это неважно. Можете положить туда свои ассеты, если хотите.
Дальше самое интересное.
Сначала качаем вот эту штуку. Это компилятор для первой кваки. Распаковываем куда нибудь.
Теперь открываем любой редактор, который может делать уровни для первой кваки, TrenchBroom например. По хорошему он должен быть настроен, все пути должны быть прописаны в fgd файле, но сейчас это не столь важно. Открываем редактор, new map, выбираем игру Quake 1.
Редактор открылся. Создаём куб, (вообще он там уже есть, просто выбираем его), шифтом растягиваем побольше (500 хватит), затем делаем из него комнату (ctrl-shift-k). Оставляем его в покое, он нам больше не нужен. Устанавливаем лампочку, чтобы хоть чего то видеть в игре. Правая кнопка, create point entity/ light/ light. Таскаем в окне боковой проекции повыше, чтоб он не на полу лежала. Не забудем создать точку спавна игрока:
Теперь, точно так же создаём пустой объект (на самом деле неважно какой, всё равно переименуем). Правая кнопка, create point entity/ info/ null. Тоже поднимем повыше, чтоб сквозь пол не провалился. Переименовываем его в npc_test, вот так:
Затем создаём новую строку, и называем message. Пишем туда что угодно, например:
I am cutiepie\n Press 1 to exit\n
С кириллицей, к сожалению, из коробки квака не дружит, нужно долго плясать с бубном.
Да, в коде мы прописали выход на единицу. Не очень изящно, зато работает. \n - это перенос строки.
Сохраняем карту в папку maps в директории мода, например npc/maps/x1.map
Компилим карту. Сначала открываем Run/Compile map, создаём новый профиль, и выставляем все пути как на картинке, нужно указать путь к компилятору, который вы недавно скачали:
Первым назначаем exportMap с параметром ${WORK_DIR_PATH}/${MAP_BASE_NAME}-compile.map
Второй указываем runTool назначаем путь к qbsp.exe с параметром ${MAP_FULL_NAME}
Далее идёт vis.exe с параметром ${MAP_BASE_NAME}.bsp
И последним light.exe . Тут вообще можно указать уйму параметров, но сейчас важно чтобы уровень просто скомпилился. Указываем просто ${MAP_BASE_NAME}.bsp
Нажимаем Compile. Если всё правильно указано - уровень скомпилится.
Далее можно указать параметры запуска. Это необязательно, зато очень удобно. Нажимаем Launch (внизу, слева), Configure Engines, и создаём новый профиль. Называем его как угодно, главное после не запутаться, и указываем путь к исполняемому файлу, например vkQuake.exe.
Закрываем окошко, нажимаем на наш профиль, и указываем параметры запуска, к примеру -game npc +map x1
npc - папка мода, x1 - название карты:
Тыркаем Launch. Если всё правильно, то вы увидите примерно это:
Подходим к неписю, тыркаем E, должно произойти вот это:
Уррррааа!
Да, текст хрен знает где, и вообще к коду много вопросов, но это уже совсем другая история. Если ничего не работает, убедитесь, что всё сделано по инструкции. Да и я, хоть всё проверил, и перепроверил, мог ошибиться где-то, так что ругайте меня в комментариях.
И на всякий случай, вот мои исходники.
Хотел написать ещё много, как там всё работает и почему, и что, например вот эту надпись, такую, казалось бы, простую вещь, чрезвычайно сложно исправить, даже положение на экране, а всё из за клиентов и серверов, что кодеры кваки целую систему придумали, которая это обходит... но это слишком всеобъемлющая тема, а я уже устал. Всех благ!
Вложения:
Метки:GoldSrcFreeman, Leiji, Revolter и 7 другим нравится это. -
Комментарии
Сортировать комментарии по