Сложность: Средняя
Вы хотите, чтобы различные части тела, мертвые тела и 
рюкзачки в воде плавали? Тогда вы не должны пропустить этот урок, и даже
 с малыми знаниями 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");
   }