# 00095. Как сделать нервометр-

Как сделать нервометр?|Как и обещал в предыдущем уроке, сегодня мы будем учится делать нервометр ( полоска дистанции )! Это довольно интересная фишка, и, думаю, вам стоит о ней знать, что бы использовать её в своих скриптах.|wmysterio|wmysterio||||Всем привет! Начнём с теории, а точнее с логики работы скрипта. Например: у нас есть две машины. В одной их них будет сидеть CJ, в другой - чел, за которым нужно следить. Нам нужно измерить дистанцию между этими актёрами и вывести её в статус-текст. Если эта дистанция будет меньше или больше установленных нами значений, то выводим провал миссии, если всё в порядке - продолжать отображать нашу полоску. И так до тех пор, пока "вражина" не будет в нужном месте.

Рассмотрим опкоды, которые измеряют дистанцию.

```
0509: $RESULT_1 = distance_between_XY $X_1 $Y_1 and_XY $X_2 $Y_2
050A: $RESULT_2 = distance_between_XYZ $X_1 $Y_1 $Z_1 and_XYZ $X_2 $Y_2 $Z_2
```

Здесь:\
$X\_1 $Y\_1 и $X\_1 $Y\_1 $Z\_1 - координаты первой точки\
$X\_2 $Y\_2 и $X\_2 $Y\_2 $Z\_2 - координаты второй точки\
$RESULT\_1 и $RESULT\_2 - в переменную записывается дистанция между первой и второй точкой ( дробное число )

Разница в этих опкодах только в способе измерения. В опкоде "0509" идёт измерение только по осям X и Y, а во втором примере - по всех осях, это значит, что значение "$RESULT\_2" будет всё время больше, чем "$RESULT\_1", но есть исключение, когда уровень высоты обеих точек равен нулю или близок нему. Я предоставлю графики, как измеряется дистанция для обеих опкодов:

| Для опкода 0509                                                                      | Для опкода 050A                                                                      |
| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| ![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/1/42735891.png) | ![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/1/16682504.png) |

В первом варианте какбы делается проекция обеих точек на оси X и Y. Дальше идёт подсчет расстояния между точками на проекции. Во втором варианте никаких проекций не делается, а подсчет расстояния идёт напрямую. Какой из этих вариантов лучше использовать выбираете Вы. Но я советую выбрать второй, так как мы будем полностью рассчитывать расстояние в 3d-мире, что увеличивает точность расстояния.

С расстоянием разобрались. Дальше нам нужно завести переменную, которая будет отображаться в статус-текст. Например "$NERV", в зависимости от дистанции шкала будет двигаться влево или вправо. Об этом чуть позже. Сейчас напишем одноразовый стартер для миссий:

```
{$CLEO}
0000:

:STARTER
thread 'STARTER'
wait 0
0@ = marker.CreateIconAndSphere(15, 2498.324, -1653.6807, 13.4366)

while 80EC: not actor $PLAYER_ACTOR 0 near_point 2498.324 -1653.6807 radius 1.5 1.5
wait 0
end

marker.Disable(0@)
player.CanMove($PLAYER_CHAR) = false
actor.SetImmunities($PLAYER_ACTOR, 1, 1, 1, 1, 1)
0ACF: show_formatted_styled_text "mission" time 2000 style 2
fade 0 1000
wait 1000
0A94: start_custom_mission "mission"
0A93: end_custom_thread
```

Дальше создаём Cleo-миссию и создаём в нужных координатах 2 машины и случайного актёра на месте водителя:

```
{$CLEO .cm}
0000:
//-------------Mission---------------

:MISSION
thread 'MISSION'
gosub @MISSION_29_57
if
wasted_or_busted
jf @MISSION_29_46
gosub @MISSION_29_144

:MISSION_29_46
$ONMISSION = 0
mission_cleanup
end_thread

:MISSION_29_57
increment_mission_attempts
$ONMISSION = 1
model.Load(#ADMIRAL)
038B: load_requested_models

while not model.Available(#ADMIRAL)
wait 0
end

0@ = car.Create(#ADMIRAL, 2493.1401, -1656.6002, 12.9763)
car.Angle(0@) = 89.2929
1@ = car.Create(#ADMIRAL, 2441.4294, -1657.0493, 12.9339)
car.Angle(1@) = 91.161
model.Destroy(#ADMIRAL)
0376: 2@ = create_random_actor_at 2441.6436 -1659.3152 13.3204
072A: put_actor 2@ into_car 1@ driverseat
072A: put_actor $PLAYER_ACTOR into_car 0@ driverseat
wait 1000
camera.Restore_WithJumpCut
Camera.SetBehindPlayer
fade 1 1000
wait 1000
player.CanMove($PLAYER_CHAR) = true
actor.SetImmunities($PLAYER_ACTOR, 0, 0, 0, 0, 0)
3@ = marker.CreateAboveCar(1@)
0AD0: show_formatted_text_lowpriority "Run to car" time 2000
{ Здесь будет объявлен нервометр }

:MISSION_A
wait 0
{ здесь будут условия провала и операции над нервометром }
jump @MISSION_A


0ACF: show_formatted_styled_text "MISSION PASSED" time 2000 style 1
gosub @MISSION_END
return

:MISSION_29_144
wait 0
0ACF: show_formatted_styled_text "~r~MISSION FILED" time 2000 style 1
gosub @MISSION_END
return

:MISSION_END
wait 0
marker.Disable(3@)
if
actor.Defined(2@)
then
actor.RemoveReferences(2@)
actor.DestroyInstantly(2@)
end
if
Car.Defined(1@)
then
car.RemoveReferences(1@)
car.Destroy(1@)
end
if
car.Defined(0@)
then
car.RemoveReferences(0@)
 if
 not actor.InCar($PLAYER_ACTOR, 0@)
 then
 car.Destroy(0@)
 end
end
return
```

Теперь делаем сам нервометр:

```
0407: store_coords_to 7@ 8@ 9@ from_car 0@ with_offset 0.0 3.0 0.0
0407: store_coords_to 10@ 11@ 12@ from_car 1@ with_offset 0.0 -3.0 0.0
050A: $TEMP = distance_between_XYZ 7@ 8@ 9@ and_XYZ 10@ 11@ 12@
$TEMPT *= 1.5
008C: $TEMPT = float $TEMP to_integer
04F7: status_text $TEMP type 1 line 1 GXT 'GXTNAME'
```

Мы вычисляем координаты обеих машин, считаем между ними дистанцию и умножаем её на "1.5". Это нужно для того, что бы сократить дистанцию, при котором будет допускаться провал миссии. После этого переводим дистанцию в целое число, так как дробное статус-тексты не читают, и запускаем полоску.

Далее в цикле мы заведём переменную "$NERV", которая так же будет считать дистанцию между транспортом по тому же алгоритму, но что бы у нас не было недочетов, что эта переменная будет отдавать значение переменной "$TEMP":

```
:MISSION_A
wait 0
0407: store_coords_to 7@ 8@ 9@ from_car 0@ with_offset 0.0 3.0 0.0
0407: store_coords_to 10@ 11@ 12@ from_car 1@ with_offset 0.0 -3.0 0.0
050A: $NERV = distance_between_XYZ 7@ 8@ 9@ and_XYZ 10@ 11@ 12@
$NERV *= 1.5
008C: $NERV = float $NERV to_integer
0086: $TEMP = $NERV // (float)
jump @MISSION_A
```

Теперь нам нужно определится с расстоянием. Я это уже сделал, но Вы можете поменять значения на свои. Я, допустим, выбрал минимальное расстояние - "25" единиц, максимальное - "90". Используя эти числа мы напишем условия, что если игрок не находится на нужном расстоянии, то выводим провал:

```
if OR
$TEMP < 26
$TEMP > 89
then
jump @MISSION_A_111
end
```

Соответственно в метке ":MISSION\_A\_111" напишем, что критическая дистанция и всё такое. Что бы добавить эффект слежения, сделаем условие, что если игрок засигналит или выйдет с тачки, то миссия будет так же провалена, мол "не отвлекайся", "слишком шумно". Так же добавим туда уровень розыска, что если он получит звезду - то миссия провалена:

```
9@ = player.WantedLevel($PLAYER_CHAR)
if OR
9@ > 0
0122: player $PLAYER_CHAR pressing_horn
then
jump @MISSION_A_222
end

if
not actor.InCar($PLAYER_ACTOR, 0@)
then
jump @MISSION_A_333
end
```

Теперь напишем условия, когда миссия будет считаться выполненной:

```
if
00EC: actor 2@ 0 near_point 2064.0496 -1610.6942 radius 3.0 3.0
then
jump @MISSION_B
end
```

Осталось только всё сложить во едино. Мы получили такой код:

```
{$CLEO .cm}
0000:
//-------------Mission---------------

:MISSION
thread 'MISSION'
gosub @MISSION_29_57
if
wasted_or_busted
jf @MISSION_29_46
gosub @MISSION_29_144

:MISSION_29_46
$ONMISSION = 0
mission_cleanup
end_thread

:MISSION_29_57
increment_mission_attempts
$ONMISSION = 1
model.Load(#ADMIRAL)
038B: load_requested_models 

while not model.Available(#ADMIRAL)
wait 0
end

0@ = car.Create(#ADMIRAL, 2493.1401, -1656.6002, 12.9763)
car.Angle(0@) = 89.2929
1@ = car.Create(#ADMIRAL, 2441.4294, -1657.0493, 12.9339)
car.Angle(1@) = 91.161
model.Destroy(#ADMIRAL)
0376: 2@ = create_random_actor_at 2441.6436 -1659.3152 13.3204
072A: put_actor 2@ into_car 1@ driverseat 
072A: put_actor $PLAYER_ACTOR into_car 0@ driverseat 
wait 1000
camera.Restore_WithJumpCut
Camera.SetBehindPlayer
fade 1 1000
wait 1000
player.CanMove($PLAYER_CHAR) = true
actor.SetImmunities($PLAYER_ACTOR, 0, 0, 0, 0, 0)
3@ = marker.CreateAboveCar(1@)
car.DriveTo(1@, 2064.0496, -1610.6942, 13.0976)
car.SetMaxSpeed(1@, 30.0)
0AD0: show_formatted_text_lowpriority "Run to car" time 2000
0407: store_coords_to 7@ 8@ 9@ from_car 0@ with_offset 0.0 3.0 0.0 
0407: store_coords_to 10@ 11@ 12@ from_car 1@ with_offset 0.0 -3.0 0.0 
050A: $TEMP = distance_between_XYZ 7@ 8@ 9@ and_XYZ 10@ 11@ 12@ 
$TEMPT *= 1.5
008C: $TEMP = float $TEMP to_integer
04F7: status_text $TEMP type 1 line 1 GXT 'GXTNAME' 

:MISSION_A
wait 0
if or
actor.Dead($PLAYER_ACTOR)
0741: actor $PLAYER_ACTOR busted 
then
jump @MISSION_29_144
end
0407: store_coords_to 7@ 8@ 9@ from_car 0@ with_offset 0.0 3.0 0.0 
0407: store_coords_to 10@ 11@ 12@ from_car 1@ with_offset 0.0 -3.0 0.0 
050A: $NERV = distance_between_XYZ 7@ 8@ 9@ and_XYZ 10@ 11@ 12@ 
$NERV *= 1.5
008C: $NERV = float $NERV to_integer
0086: $TEMP = $NERV // (float) 

if
01AD: car 1@ sphere 0 near_point 2064.0496 -1610.6942 radius 3.0 3.0 
then
jump @MISSION_B
end

if OR
$TEMP < 26
$TEMP > 89
then
jump @MISSION_A_111
end

9@ = player.WantedLevel($PLAYER_CHAR)
if OR
9@ > 0
0122: player $PLAYER_CHAR pressing_horn 
then
jump @MISSION_A_222
end

if
not actor.InCar($PLAYER_ACTOR, 0@)
then
jump @MISSION_A_333
end

jump @MISSION_A

:MISSION_B
wait 0
marker.Disable(3@)
player.CanMove($PLAYER_CHAR) = false
actor.SetImmunities($PLAYER_ACTOR, 1, 1, 1, 1, 1)
fade 0 1000
wait 1000
0633: AS_actor $PLAYER_ACTOR exit_car 

while actor.InCar($PLAYER_ACTOR, 0@)
wait 0
end

gosub @MISSION_END
camera.Restore_WithJumpCut
Camera.SetBehindPlayer
wait 1000
player.CanMove($PLAYER_CHAR) =true
actor.SetImmunities($PLAYER_ACTOR, 0, 0, 0, 0, 0)
0ACF: show_formatted_styled_text "MISSION PASSED" time 2000 style 1
0394: play_music 1 
return

:MISSION_A_111
wait 0
0ACF: show_formatted_styled_text "~r~MISSION FILED" time 2000 style 1
0AD0: show_formatted_text_lowpriority "Critical dystance" time 2000
gosub @MISSION_END
return

:MISSION_A_222
wait 0
0ACF: show_formatted_styled_text "~r~MISSION FILED" time 2000 style 1
0AD0: show_formatted_text_lowpriority "You pressing horn" time 2000
gosub @MISSION_END
return

:MISSION_A_333
wait 0
0ACF: show_formatted_styled_text "~r~MISSION FILED" time 2000 style 1
0AD0: show_formatted_text_lowpriority "You exit in car" time 2000
gosub @MISSION_END
return

:MISSION_29_144 // ÁËÎÊ ÏÐÎÂÀËÀ ÌÈÑÑÈÈ
wait 0
0ACF: show_formatted_styled_text "~r~MISSION FILED" time 2000 style 1
gosub @MISSION_END
return

:MISSION_END
wait 0
0151: remove_status_text $TEMP
marker.Disable(3@)
if
actor.Defined(2@)
then
actor.RemoveReferences(2@)
actor.DestroyInstantly(2@)
end
if
Car.Defined(1@)
then
car.RemoveReferences(1@)
car.Destroy(1@)
end
if
car.Defined(0@)
then
car.RemoveReferences(0@)
 if
 not actor.InCar($PLAYER_ACTOR, 0@)
 then
 car.Destroy(0@)
 end
end
return
```

Проверка:

![](https://github.com/wmysterio/scm-scripting-lessons/raw/resources/_pu/1/78208314.png)

Отлично! Наша задача выполнена! ;)|1423|1|0|78208314`png`500`300`400`240``\|42735891`png`303`270

```
`\|16682504`png`303`270
```

\`||kak\_sdelat\_nervometr|1504428520


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lessons.sannybuilder.com/00100/00600/00095.-kak-sdelat-nervometr.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
