Сложность: Средняя
Вы хотите, чтобы различные части тела, мертвые тела и
рюкзачки в воде плавали? Тогда вы не должны пропустить этот урок, и даже
с малыми знаниями QuakeC-программирования, вы сможете заставить другие
вещи плавать.
Для начала, нам нужно скопировать неизмененные файлы QuakeC версии 1.06, а также скопировать файл floater.qc, содержание которого находится в самом конце этого урока, разумеется, вам нужно будет только создать файл с именем floater.qc и вставить в него текст, о котором чуть позже...
Итак, создайте в директории с Quake директорию, которую мы назовем FLOATER, и, чтобы Вы могли нормально делать мод, создайте внутри только что созданной директории еще одну директорию, которую назовем SRC.
Теперь скопируйте QuakeC файлы, а также файл floater.qc, в директорию SRC.
Так, как мы с вами внесли в наш мод новый файл, мы
должны добавить его в список файлов для компиляции. Этим списком
является файл progs.src, который сообщает компилятору, какие файлы с исходными кодами принадлежат к нашему моды.
Добавьте floater.qc после defs.qc в progs.src так, как показанно ниже:
Изменения будут начинатся и заканчиватся коментариями // FLOATER.
Изменение файла client.qc. Сейчас мы с вами сделаем некоторое изменение в функции PlayerPreThink (ориентир на 914-ю строчку). После изменение начало функции должно выглядет так:
Добавив floaterPreThink();, мы предоставляем Quake способность контролировать плавающие объекты (для краткости назовем их плавуны).
Этот выполняется всегда на протяжении всей игры с некоторой задержкой
(в один кадр), то есть если существуют какие-либо плавуны, то они сразу
же становятся зависимыми от их текущих характеристик : они могут быть
падающими, плавающими, погружающимися или могут просто лежать на
поверхности.
Погружающийся плавун зависит от скорости падения, но
потом падает на какую-нибудь поверхность, где плавает, как поплавок,
верх-вниз в течение 30 секунд и наконец падает на саму поверхность и
теряет скорость.
Иногда вы сможете найти плавун, который удерживает свою скорость или
кружится вокруг в течении того времени, пока он плавает, как поплавок.
Скажу сразу, я не писал код для чего-нибудь подобного. Еще объекты
иногда удерживают скорость по x или y.
Максумум 32 плавуна могут быть активированны в одно и тоже время (смотрите конфигурации в файле floater.qc)
- в этом случае, когда какой-нибудь активированный плавун достигает
поверхности (дна, пола, земли, и т.д.) нам нужно его деактивировать
(т.е. сделать так, чтобы плавун использовал обычную quake-физику,
например, как у объектов на поверхности).
Чтобы активировать у объекта способность плавания, мы должны вызвать свойство, которое называется floaterEnabl. Его мы опишем в файле floater.qc.
Сделайте изменения в файле player.qc (начиная, примерно, с 466-ой строчки) так, как показанно ниже:
Причина для изменения размеров останков игрока или монстра (далее джибзы) заключается в том, чтобы создать иллюзию плавания объекта в воде, в то время, когда он уже находится на дне.
Следующие изменения в файле player.qc будут начинаются, с примерно 484-ой строчки.
Если сейчас вы откомпилируете и запустите полученное, то вы сможете увидеть джибзы, плавающие в воде. Но можно и дальше пойти.
Вызов свойства floaterEnable заставляет джибз плавать, как только он ныряет под воду.
От этого свойства передаются два параметра.
Первый параметр - объект активируется на плавучесть.
Второй параметр - балансирует центр объекта, когда он в воде или на ее поверхности.
Вы можете сразу следовать на шаг 6, если вам неинтересно, как расчитывается центр объектов, плавающих в воде.
Центр объекта мы подсчитаем следующим образом:
Во-первых, как мы видим центр объекта в Quake?
Все модели в Quake окружает так называемая коробка,
находящаяся в координатах 'X Y Z' (ниже центра модели, назовем их mins) и
'X Y Z' (выше центра модели, назовем их maxs), что дает частичный
размер (maxs - mins) и центр (размер - maxs) коробке. Вы, наверное
спросите, зачем нужна эта коробка? Что ж, она нужна для того, чтобы
задать так называемый контур объекта, который будет принимать на себя
все попадания по объекту, в том числе и попадания других объектов...
Следующая диаграмма покажет размещение головы (в смысле, воображаемой
;), которая имеет коробку с размерами '-16 -16 0', '16 16 56':
примечание : наша диаграма не имеет действительных размеров..
Когда мы проверяем, есть ли голова в воде или нет, мы используем этот
центр. В случае, когда наша голова выше, то объект почти всегда будет в
воде, поэтому этот центр слишком закрытый для дна коробки.
Только координата центра объекта по оси Z важна, когда мы делаем голову
плавающей так, как надо. Подсчитав выражение ((56 - 0) - 56) мы найдем
эту координату. В результате у нас получилось 0, т.е. координата находится фактически на дне коробки.
Чтобы взять плавающую как поплавок голову, мы передаем ей второй параметр свойства floaterEnable.
Значение 5 (я использую 5, так как внешний размер (модель) головы как
правило составляет 20% от всего размера коробки) сделает визуальный
центр головы в воде так, как показано ниже:
Далее немного другой пример. Если тело умирает, то размеры коробки следующие: '-16 -16 -24', '16 16 32'.
В случае мертвого тела, нам нужно изменить центр на -18 (6-24), чтобы изменить визуальный центр для плавуна.
Выше я рассказал не о всех случаях, но тех легких и простых, за которые
можно было бы ухватиться (просто все это достаточно сложно объяснить),
но если вы попробуете порасчитывать различные изменения в центре
плавающего объекта, то я уверен, что вы сможете увидеть, как это все
работает.
1. Изменения в файле client.qc
Первые изменения начинаются примерно с 483-ей строки и заключаются в следующем:
Следующее изменение начинается примерно с 791-ой строки.
2. Изменения в файле player.qc
Во-первых, перейдем на, примерно 504-ую строчку и сделаем следующие изменения:
Далее перейдем на примерно 573-ю строчку:
3. Изменения в файле combat.qc ведутся примерно с 78-ой строчки и заключаются в следующем:
4. В файле world.qc изменения начинаются примерно с 390-ой строки:
Изменения, которые были чуть выше (4), делают копии
мертвых тел игрока, который еще не появился вновь (т.е. до того, как
игрок нажмет на гашетку, чтобы респавнится).
5. Изменения в файле items.qc протекают с 1380-ой строки:
Вот и все, что нам нужно было сделать, теперь компилируйте и запускайте Quake с нашими новыми модификациями.
Надеюсь вы будете в восторге от этого!
--------------------------------------------------------------------------------
P.S.: И еще одна последняя вещь. Вы наверное спросите где же все-таки взять содержимое для файла floater.qc? Отвечаем: оно находится ниже....
/*
==============================================================================
"floater.qc" разработал Alan Kivlin
12 мая 1997 года
Коментарии к модификации
-------------------------------
Данная модификация нужна для того, чтобы делать объекты плавающими в воде и на ее поверхности
Погружающийся объект (назовем его плавун) зависит от скорости падения,
но потом падает на какую-нибудь поверхность, где плавает, как поплавок,
верх-вниз в течение 30 секунд и наконец падает на саму поверхность и
теряет скорость.
Иногда вы сможете найти плавун, который удерживает свою скорость или
кружится вокруг в течении того времени, пока он плавает, как поплавок.
Скажу сразу, я не писал код для чего-нибудь подобного. Еще объекты
иногда удерживают скорость по x или y.
Эта модификация зарегестрирована Alan Kivlin'ом в 1997 году.
Другие авторы МОГУТ использовать эту модификацию, как основу для других своих дополнений.
^^^^^^^^
ВНИМАНИЕ: Alan Kivlin (или как его еще называют Virtuoso) не несет
ответственности за ущерб, психологические воздействия, потерю сна или
усталость после использования данной модификации.
Если вы скопируете это на компакт-диск, то вы будете должны мне одну
копию CD. Если вам это не нравится, то не копируйте это на CD!
Если вы возьмете мою работу и ни разу не упомяните обо мне, то ВЫ СГОРИТЕ В АДУ, так, как горят поросята Фроги в духовке.
Использование этой модификации в комерческих целях привет к тому, что к
вам ночью завалятся дружки Сатаны и вам будет очень плохо, потому, что
надо сначала переговорить со мной (Alan Kivlin), ok?
Спасибо Dave 'Zoid' Kirsch за его копирайт, который я поместил выше.
==============================================================================
*/
// вызов последнего кадра
float fFloaterLastFrame;
// делаем на объекте метку "floating", когда тот плавает
.string sFloating;
// изменения центра плавуна после проверки
.float fOriginOffset;
// флаги состояния плавуна
float FS_FALLING = 1; // падает
float FS_SURFACING = 2; // на поверхности
float FS_FLOATING = 3; // плавает
float FS_SINKING = 4; // опускается
// максимальное число одновременно плавающих объектов
float FLOATER_MAXIMUM = 32;
//----------------------------------------------------------------------------
// возвращаем значение TRUE (правда), если находится в воде, лаве или кислоте
float (entity ent) floaterInWater;
// делаем объект плавающим
void (entity ent, float offset) floaterEnable;
// делаем объект не плавающим
void (entity ent) floaterDisable;
// контролируем плавание всех плавунов
void () floaterPreThink;
//----------------------------------------------------------------------------
/*
==============================================================================
Значение "floaterInWater"
Возвращаем значение TRUE, если находится в воде, лаве или кислоте
==============================================================================
*/
float (entity ent) floaterInWater =
{
local vector where;
local float contents;
where = ent.origin;
where_z = where_z+ent.fOriginOffset;
contents = pointcontents (where);
if (contents >= -5 && contents <= -3) // в воде (-3), кислоте (-4) или лаве (-5) возвращаем TRUE.
return true;
return false;
};
/*
==============================================================================
Функция "floaterenable" вызывает у объекта способность плавать
==============================================================================
*/
void (entity ent, float offset) floaterenable=
{
local float floatercount;
local entity floater, oldest;
oldest = floater = find (world, sfloating, "floating");
while (floater)
{
floatercount = floatercount + 1;
if (floater.ltime <= oldest.ltime) oldest=floater;
floater = find (floater, sfloating, "floating");
}
if (floatercount == floater_maximum)
floaterdisable (oldest);
ent.sfloating = "floating";
ent.state = FS_FALLING;
ent.speed = 0; // сохраним изменения центра, использованного при проверке
ent.foriginoffset = offset; // "стартуем" время опускания плавуна
ent.ltime = time+30+random()*5;
if (floaterinwater(ent))
{
ent.movetype = MOVETYPE_TOSS;
ent.flags = ent.flags | fl_inwater; // ставим флаг "в воде"
}
else
{
ent.movetype = MOVETYPE_BOUNCE;
ent.flags = ent.flags-(ent.flags & fl_inwater); // убираем флаг "в воде"
}
};
/*
==============================================================================
Функция "floaterdisable" отнимает у объекта способность плавать
==============================================================================
*/
void( entity ent ) floaterdisable=
{
// прекращаем плавание
ent.sfloating = string_null;
};
/*
==============================================================================
Функция "floaterprethink" контролирует плавание всех плавунов
==============================================================================
*/
void() floaterprethink=
{
local entity ent;
if (ffloaterlastframe == framecount) return;
ffloaterlastframe = framecount;
ent = find (world, sfloating, "floating");
while (ent)
{
if ((ent.state == FS_FLOATING) && (ent.flags & fl_onground))
{
// если мы на земле, то мы должны уже упасть, это случается
// когда плавун на движущейся платформе, которая покинула воду
ent.state = FS_FALLING;
ent.speed = 0;
}
if (ent.state == FS_FLOATING)
{
if (ent.speed > 0) ent.velocity_z = ent.speed*(1+frametime*8); // плывем вверх
else ent.velocity_z = 0; // плывем вниз
}
else if (ent.state == FS_SURFACING)
// если у объекта стоит флаг "на поверхности", то
{
if (ent.velocity_z > 0) ent.velocity_z = ent.speed; // сохраняем скорость на поверхности
else if (floaterInWater(ent)) ent.state = FS_SINKING; // can't reach the surface so make it sink
}
else if (ent.state == FS_SINKING)
// иначе, если у объекта стоит флаг "погружающийся", то
if (ent.flags & FL_ONGROUND) floaterDisable(ent);
// если он уже на дне, то отбираем у него способность плавать
else ent.velocity_z = 0; // иначе, заставляем его погружаться медленно
if (floaterInWater(ent))
{
if (!(ent.flags & FL_INWATER))
{
ent.movetype = MOVETYPE_TOSS;
// ставим флаг "в воде"
ent.flags = ent.flags | FL_INWATER;
if (ent.state == FS_FLOATING) ent.speed = 72+random()*16;
// начинаем плыть вверх
else if (ent.state == FS_FALLING) sound (ent, CHAN_BODY, "player/h2ojump.wav", 1, ATTN_NORM);
// проигрываем звук вхождения в воду
}
if (ent.state == FS_FALLING)
{
if (!ent.speed)
{
if (ent.velocity_z < 0) // ставим максимальную скорость падения, до того как плавун достигнет поверхности
ent.speed = ent.velocity_z*8;
}
if ((ent.velocity_z <= ent.speed) || (ent.flags & fl_onground))
{
// start surfacing
ent.state = FS_SURFACING;
ent.speed = 128+random()*32;
ent.velocity_z = ent.speed;
ent.velocity_x = 0;
ent.velocity_y = 0;
}
}
}
else
{
if ((ent.flags & fl_inwater))
{
ent.movetype = MOVETYPE_BOUNCE;
//убираем флаг "в воде"
ent.flags = ent.flags-fl_inwater;
if (ent.state == FS_FLOATING) ent.speed = 0; // начинаем плавать вниз
else if (ent.state == FS_SINKING)
{
ent.state = FS_FALLING;
ent.speed = 0;
}
if (ent.state == FS_FALLING) sound (ent, chan_body, "player/h2ojump.wav" , 1, attn_norm); // play leave water sound
}
if (ent.state == FS_SURFACING)
{
// как только объект касается поверхности воды, то выталкиваем его наружу...
ent.velocity_z = ent.speed*1.5;
// ...и заставляем его плыть вниз
ent.state = FS_FLOATING;
ent.speed = 0;
}
}
if (ent.ltime <=time)
{
if (ent.flags & fl_inwater)
{
// если плавун достаточно опустился в воду, то ставим ему флаг "погружающийся"
ent.state = FS_SINKING;
ent.ltime = time+10+random()*5;
}
}
ent.watertype = 0;
// доругие физические движения не работают для плавуна
ent.flags = ent.flags-(ent.flags & fl_onground);
ent = find (ent, sfloating, "floating");
}