[Tutorial]: Skryptowy spawn NPC wraz z funkcją smart terrain'a

Tytułem wprowadzenia…
Wszystkim pasjonatom moddowania oczywistym jest fakt, zgodnie z którym - modyfikacja pliku all.spawn polegająca na wprowadzeniu nowych NPC’ów, mutantów, pojazdów, przedmiotów czy anomalii - wymusza konieczność rozpoczynania nowej rozgrywki by spawnowany obiekt pojawił się w świecie gry. Dodając jakikolwiek obiekt - operuje się w pod-plikach alife_*.ltx (* - nazwa lokacji) uzyskanych w wyniku procesu dekompilacji z użyciem ACDC lub edycji tychże plików xrSpawner’em (edycja bez konieczności dekompilacji). Nastręcza to wiele trudności w sytuacji gdy chcemy dodać np. postać do gry przy jednoczesnej chęci zachowania aktualnego poziomu rozwoju fabuły oraz - zachowania przez dodaną postać - stałej pozycji jako konieczności determinującej jej przetrwanie (brak przypisanego smart_terrain’a). Analizując pliki Solijanki - odkryłem sposób w jaki dodawano postacie za pomocą skryptu, skrypt ten jednak - bo mowa o amk.script - jest tak rozbudowany i posiada tyle odniesień wymuszających w istocie konieczność dodania ponad 10 plików, że postanowiłem poszukać innego sposobu. Odkryłem, że kluczowe w skryptowym dodawaniu postaci są 2 funkcje: read_stalker_params oraz write_stalker_params, które znalazłem również w pliku xrs_utils.script. Jeden tylko plik umożliwia sczytywanie wartości parametrów odpowiedzialnych za koordynaty, profil postaci, schemat działania a także za ich zapis. Dzięki wykorzystaniu w/w pliku oraz funkcji, umożliwiającej spawn obiektu o zadanych zmiennych [np. story_id, nazwa postaci] - nie jesteśmy zmuszeni rozpoczynać nowej gry, ponieważ to nie all.spawn tworzy obiekt ze struktury alife_*.ltx, lecz skrypt. Jak można będzie się przekonać na prezentacji w końcowej części poradnika - re-kompilacja pliku all.spawn, w której wprowadzono sekcję odpowiedzialną za koordynaty, umożliwia skorzystanie z pliku all.spawn (savegame wykorzystujący wersję niezmienioną spawna) - pomimo jego modyfikacji. Modyfikacja bowiem dotyczyła pliku way_*.ltx, czyli części odpowiedzialnej za punkty patrolowe, kierunki patrzenia, centra obozowisk reprezentowane przez punkt, etc. Przeprowadziłem ok. 20 testów zarówno na wersji podstawowej Shoc 1.0004 - jak również na modyfikacji zawierającej nowe mapy. Testy dotyczyły sprawdzania reakcji NPC na ostrzał, rzuty granatem, ataki mutantów, zmianę lokacji (i powrót w m-ce spawnowanych jednostek), wczytywanie zapisu gry w różnym momencie rozgrywki - za każdym razem dodane postacie utrzymywały pozycję. Metoda umożliwia tworzenie pseudo-smart terrain’a. Dlaczego pseudo? - ponieważ nie jest to obiekt o cechach spełniających kryteria parametryzacji dla smart_terrain’a - definiowanego w alife_*.ltx, lecz punkt odniesienia wykorzystywany przez dodane jednostki (spełnia funkcję smart_terrain’a), bez niego - dodane postacie rozeszłyby się na mapie, ginąc od anomalii, ataków wroga czy mutantów. Dzięki tej metodzie - możemy modyfikować każdą wersję stalkera (oraz mody), w której możliwa jest dekompilacja spawn’a, ponieważ znaczenie ma tutaj dodanie punktu odniesienia do pliku(ów) way_*.ltx. Warunkiem jej stosowania jest operowanie wyłącznie w pod-plikach way_*.ltx - all.spawn’a - mowa o sytuacji, gdy chcemy dodać postać do gry - bez jej ponownego rozpoczynania.
Po krótkim wstępie - przejdźmy do clou poradnika.

Tutorial dla Shoc oraz modów z możliwością dekompilacji pliku all.spawn

I. Etap przygotowawczy - zbieramy punkty terenowe.

Moim zamiarem jest dodanie 3 postaci do gry, które zajmować będą określoną pozycję - za przykład obiorę schemat “ogniska” czyli sytuacji w której stalkerzy skupiają się wokół centralnego punktu. Potrzebuję koordynatów dla 3 NPC’ów i omawianego punktu.

Uwaga: Ilość punktów zależy od obranego schematu tj. jeśli moim zamiarem jest np. stworzenie trasy patrolowej dla 1 jednostki - składającej się z 10 waypoint’ów, muszę zebrać 11 współrzędnych (NPC + punkty trasy). Przydatna jest tutaj znajomość budowy danego schematu, ponieważ często schemat (np. snajpera) wymaga dodatkowo zdefiniowania kierunków patrzenia. Schematy pracy stalkerów to temat osobnego poradnika. Najprostszym - bo wymagającym dodania tylko jednego punktu - jest obrane za przykład “ognisko”. Wracając do naszego przykładu - zbieram 4 potrzebne punkty - korzystając z narzędzia “Smartterrain and Waypoint Tools by dez0wave” - dostępnego pod tym linkiem. (opis znajdziemy tutaj: link). Gdyby doszło do sytuacji, iż link będzie nieaktywny - omawiane narzędzie znajdziemy tutaj w postaci załącznika. Poniżej wyciąg współrzędnych z loga gry (koordynaty w wiosce nowicjuszy na Kordonie):

```
! Unknown command: patrolName=spawn_1|anim=stalker_1|pos=-200.4694519043,-20.58443069458,-148.60520935059|game=46|level=52314
Log file has been saved successfully!! Unknown command: patrolName=spawn_2|anim=stalker_2|pos=-202.69140625,-20.110641479492,-143.33158874512|game=57|level=49764* Log file has been saved successfully!! Unknown command: patrolName=spawn_3|anim=stalker_3|pos=-204.28082275391,-20.361850738525,-147.43516540527|game=46|level=48478* Log file has been saved successfully!! Unknown command: patrolName=spawn_4|anim=xyz_point|pos=-207.013442993164,-20.4290790557861,-169.828475952148|game=50|level=45902


Uzyskawszy potrzebne punkty terenowe - przechodzimy do części wykonawczej.

Alternatywne metody uzyskiwania współrzędnych opisuje ten **[temat](http://stalkerin.gameru.net/wiki/index.php?title=%D0%A1%D0%BF%D0%B0%D0%B2%D0%BD_%D1%87%D0%B5%D1%80%D0%B5%D0%B7_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82#.D0.A2.D0.B5.D0.BE.D1.80.D0.B8.D1.8F)**, zawierający linki do narzędzi dla wszystkich wersji Stalkera, sekcja: **часть 1**.

> [**LVID GVID Script SoC**](http://stalkerin.gameru.net/downloads/stutils/LVIDGVID_script.7z)

**II. Etap wykonawczy - tworzenie struktury moda oraz edycja plików.**

[![](https://attachments.stalkerteam.pl/monthly_11_2013/post-3466-0-39481400-1383406724_thumb.png)](https://attachments.stalkerteam.pl/monthly_11_2013/post-3466-0-39481400-1383406724.png)

Na początek skupimy się na oskryptowaniu modyfikacji.

* Tworzymy lub kopiujemy (patrz załącznik) - plik **xrs\_utils.script** do folderu skryptów stalkera [gamedatascripts]. Poniżej plik do pobrania lub zawartość do skopiowania (gdyby doszło do usunięcia załącznika). Jeśli mamy zamiar umieścić zawartość w pliku - pamiętajmy by nosił nazwę **xrs\_utils** i rozszerzenie **.script**, ponieważ jest to nazwa wykorzystywana w funkcji spawna (opis pkt.**2**). Istotne są tutaj funkcje: **read\_stalker\_params** oraz **write\_stalker\_params**. Plik powyższy występuje w wielu modach, w których może się różnić kilkoma sekcjami - dlatego podałem funkcje, które są b. ważne dla działania modyfikacji. Warto sprawdzić ich obecność w przypadku kompilacji z modami zawierającymi ów plik. [xrs\_utils.7z|attachment](upload://wfSbEgnaBH1srUeHgd1P22RLD1n.7z)  

  **Treść skryptu poniżej**:

  > *```
  > -- Autor: xStreamfunction readvu32u8(packet)  local v={}  local len=packet:r_s32()  for i=1,len,1 do    table.insert(v,packet:r_u8())  end  return vendfunction readvu8u8(packet)  local v={}  local len=8  for i=1,len,1 do    table.insert(v,packet:r_u8())  end  return vendfunction readvu32u16(packet)  local v={}  local len=packet:r_s32()  for i=1,len,1 do    table.insert(v,packet:r_u16())  end  return vendfunction writevu32u8(pk,v)  local len=table.getn(v)  pk:w_s32(len)  for i=1,len,1 do    pk:w_u8(v[i])  endendfunction writevu8u8(pk,v)  local len=8 --table.getn(v)  for i=1,len,1 do    pk:w_u8(v[i])  endendfunction writevu32u16(pk,v)  local len=table.getn(v)  pk:w_s32(len)  for i=1,len,1 do    pk:w_u16(v[i])  endendfunction parse_object_packet(ret,stpk,updpk)  ret.gvid=stpk:r_u16()  ret.obf32u1=stpk:r_float()  ret.obs32u2=stpk:r_s32()  ret.lvid=stpk:r_s32()  ret.oflags=stpk:r_s32()  ret.custom=stpk:r_stringZ()  ret.sid=stpk:r_s32()  ret.obs32u3=stpk:r_s32()  return retendfunction fill_object_packet(ret,stpk,updpk)  stpk:w_u16(ret.gvid)  stpk:w_float(ret.obf32u1)  stpk:w_s32(ret.obs32u2)  stpk:w_s32(ret.lvid)  stpk:w_s32(ret.oflags)  stpk:w_stringZ(ret.custom)  stpk:w_s32(ret.sid)  stpk:w_s32(ret.obs32u3)endfunction parse_visual_packet(ret,stpk,updpk)  ret.visual=stpk:r_stringZ()  ret.vsu8u1=stpk:r_u8()  return retendfunction fill_visual_packet(ret,stpk,updpk)  stpk:w_stringZ(ret.visual)  stpk:w_u8(ret.vsu8u1)endfunction parse_dynamic_object_visual(ret,stpk,updpk)  parse_object_packet(ret,stpk,updpk)  parse_visual_packet(ret,stpk,updpk)  return retendfunction fill_dynamic_object_visual(ret,stpk,updpk)  fill_object_packet(ret,stpk,updpk)  fill_visual_packet(ret,stpk,updpk)endfunction parse_creature_packet(ret,stpk,updpk)  parse_dynamic_object_visual(ret,stpk,updpk)  ret.team=stpk:r_u8()  ret.squad=stpk:r_u8()  ret.group=stpk:r_u8()  ret.health=stpk:r_float()  ret.crvu32u16u1=readvu32u16(stpk)  ret.crvu32u16u2=readvu32u16(stpk)    ret.killerid=stpk:r_u16()  ret.game_death_time=readvu8u8(stpk)  ret.updhealth=updpk:r_float()  ret.upds32u1=updpk:r_s32()  ret.updu8u2=updpk:r_u8()  ret.updpos={} -- čëč ďîńňŕâčňü âĺęňîđ? ëŕäíî ďîňîě  ret.updpos.x=updpk:r_float()  ret.updpos.y=updpk:r_float()  ret.updpos.z=updpk:r_float()  ret.updmodel=updpk:r_float()  ret.upddir={}  ret.upddir.x=updpk:r_float()  ret.upddir.y=updpk:r_float()  ret.upddir.z=updpk:r_float()  ret.updteam=updpk:r_u8()  ret.updsquad=updpk:r_u8()  ret.updgroup=updpk:r_u8()    return retendfunction fill_creature_packet(ret,stpk,updpk)  fill_dynamic_object_visual(ret,stpk,updpk)  stpk:w_u8(ret.team)  stpk:w_u8(ret.squad)  stpk:w_u8(ret.group)  stpk:w_float(ret.health)  writevu32u16(stpk,ret.crvu32u16u1)  writevu32u16(stpk,ret.crvu32u16u2)    stpk:w_u16(ret.killerid)  writevu8u8(stpk,ret.game_death_time)  updpk:w_float(ret.updhealth)  updpk:w_s32(ret.upds32u1)  updpk:w_u8(ret.updu8u2)  updpk:w_float(ret.updpos.x)  updpk:w_float(ret.updpos.y)  updpk:w_float(ret.updpos.z)  updpk:w_float(ret.updmodel)  updpk:w_float(ret.upddir.x)  updpk:w_float(ret.upddir.y)  updpk:w_float(ret.upddir.z)  updpk:w_u8(ret.updteam)  updpk:w_u8(ret.updsquad)  updpk:w_u8(ret.updgroup)endfunction parse_monster_packet(ret,stpk,updpk)  parse_creature_packet(ret,stpk,updpk)  ret.baseoutr=stpk:r_stringZ()  ret.baseinr=stpk:r_stringZ()  ret.smtrid=stpk:r_u16()  ret.smtrtaskactive=stpk:r_u8()   ret.updu16u1=updpk:r_u16()  ret.updu16u2=updpk:r_u16()  ret.upds32u3=updpk:r_s32()  ret.upds32u4=updpk:r_s32()  return retendfunction fill_monster_packet(ret,stpk,updpk)  fill_creature_packet(ret,stpk,updpk)  stpk:w_stringZ(ret.baseoutr)  stpk:w_stringZ(ret.baseinr)  stpk:w_u16(ret.smtrid)  stpk:w_u8(ret.smtrtaskactive)   updpk:w_u16(ret.updu16u1)  updpk:w_u16(ret.updu16u2)  updpk:w_s32(ret.upds32u3)  updpk:w_s32(ret.upds32u4)endfunction parse_trader_packet(ret,stpk,updpk)  ret.money=stpk:r_s32()  ret.profile=stpk:r_stringZ()  ret.infammo=stpk:r_s32()  ret.class=stpk:r_stringZ()  ret.communityid=stpk:r_s32()  ret.rank=stpk:r_s32()  ret.reputation=stpk:r_s32()  ret.charname=stpk:r_stringZ()    return retendfunction fill_trader_packet(ret,stpk,updpk)  stpk:w_s32(ret.money)  stpk:w_stringZ(ret.profile)  stpk:w_s32(ret.infammo)  stpk:w_stringZ(ret.class)  stpk:w_s32(ret.communityid)  stpk:w_s32(ret.rank)  stpk:w_s32(ret.reputation)  stpk:w_stringZ(ret.charname)endfunction parse_human_packet(ret,stpk,updpk)  parse_trader_packet(ret,stpk,updpk)  parse_monster_packet(ret,stpk,updpk)  ret.huvu32u8u1=readvu32u8(stpk)  ret.huvu32u8u2=readvu32u8(stpk)    return retendfunction fill_human_packet(ret,stpk,updpk)  fill_trader_packet(ret,stpk,updpk)  fill_monster_packet(ret,stpk,updpk)  writevu32u8(stpk,ret.huvu32u8u1)  writevu32u8(stpk,ret.huvu32u8u2)endfunction parse_skeleton_packet(ret,stpk,updpk)  ret.skeleton=stpk:r_stringZ()  ret.skeleton_flags=stpk:r_u8()  ret.source_id=stpk:r_u16() --  ret.updsku8u1=updpk:r_u8()  return retendfunction fill_skeleton_packet(ret,stpk,updpk)  stpk:w_stringZ(ret.skeleton)  stpk:w_u8(ret.skeleton_flags)  stpk:w_u16(ret.source_id) --  updpk:w_u8(ret.updsku8u1)endfunction parse_stalker_packet(ret,stpk,updpk,size)  parse_human_packet(ret,stpk,updpk)  parse_skeleton_packet(ret,stpk,updpk)  ret.hellodlg=updpk:r_stringZ()  ret.stunk1={}  for i=stpk:r_tell(),size-1,1 do    table.insert(ret.stunk1,stpk:r_u8())  end  return retendfunction fill_stalker_packet(ret,stpk,updpk)  fill_human_packet(ret,stpk,updpk)  fill_skeleton_packet(ret,stpk,updpk)  updpk:w_stringZ(ret.hellodlg)  for i,v in ipairs(ret.stunk1) do    stpk:w_u8(v)  endendfunction parse_se_monster_packet(ret,stpk,updpk,size)  parse_monster_packet(ret,stpk,updpk,size)  parse_skeleton_packet(ret,stpk,updpk,size)  ret.spec_obj_id=stpk:r_u16()  ret.job_online=stpk:r_u8()    if ret.job_online>3 then        ret.state=true        ret.job_online=ret.job_online-4    else        ret.state=false    end  if ret.job_online==3 then    ret.job_online_condlist=stpk:r_stringZ()  end  ret.was_in_smtr=stpk:r_u8()  ret.stunk1={}  for i=stpk:r_tell(),size-1,1 do    table.insert(ret.stunk1,stpk:r_u8())  end  return retendfunction fill_se_monster_packet(ret,stpk,updpk)  fill_monster_packet(ret,stpk,updpk)  fill_skeleton_packet(ret,stpk,updpk)  stpk:w_u16(ret.spec_obj_id)    local st=0    if ret.state then        st=4    end  stpk:w_u8(ret.job_online+st)  if ret.job_online==3 then    stpk:w_stringZ(ret.job_online_condlist)  end  stpk:w_u8(ret.was_in_smtr)  for i,v in ipairs(ret.stunk1) do    stpk:w_u8(v)  end  endfunction read_stalker_params(sobj)  local stpk=net_packet()  local uppk=net_packet()  sobj:STATE_Write(stpk)  sobj:UPDATE_Write(uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  local tbl=xrs_utils.parse_stalker_packet({},stpk,uppk,size)  return tblendfunction read_monster_params(sobj)  local stpk=net_packet()  local uppk=net_packet()  sobj:STATE_Write(stpk)  sobj:UPDATE_Write(uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  local tbl=xrs_utils.parse_se_monster_packet({},stpk,uppk,size)  return tblend-- ňŕáëčöŕ ďŕđŕěĺňđîâ č ńĺđâĺđíűé îáúĺęň íŕ âőîäĺfunction write_stalker_params(tbl,sobj)  local stpk=net_packet()  local uppk=net_packet()  xrs_utils.fill_stalker_packet(tbl,stpk,uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  sobj:STATE_Read(stpk,size)  sobj:UPDATE_Read(uppk)endfunction write_monster_params(tbl,sobj)  local stpk=net_packet()  local uppk=net_packet()  xrs_utils.fill_se_monster_packet(tbl,stpk,uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  sobj:STATE_Read(stpk,size)  sobj:UPDATE_Read(uppk)endfunction get_anomaly_data(sobj)  local stpk=net_packet()  local uppk=net_packet()  sobj:STATE_Write(stpk)  sobj:UPDATE_Write(uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  local t={}    xrs_utils.parse_object_packet(t,stpk,uppk,size)  xrs_utils.parse_shape_packet(t,stpk,uppk,size)    t.restrictor_type = stpk:r_u8()        t.max_power = stpk:r_float()    t.owner_id = stpk:r_s32()    t.enabled_time = stpk:r_s32()    t.disabled_time = stpk:r_s32()    t.start_time_shift = stpk:r_s32()        t.offline_interactive_radius = stpk:r_float()    t.artefact_spawn_count = stpk:r_u16()    t.artefact_position_offset = stpk:r_s32()        t.last_spawn_time_present = stpk:r_u8()        if stpk:r_elapsed() ~= 0 then    end    return tendfunction set_anomaly_data(t,sobj)  local stpk=net_packet()  local uppk=net_packet()      xrs_utils.fill_object_packet(t,stpk,uppk)  xrs_utils.fill_shape_packet(t,stpk,uppk)    stpk:w_u8(t.restrictor_type)        stpk:w_float(t.max_power)    stpk:w_s32(t.owner_id)    stpk:w_s32(t.enabled_time)    stpk:w_s32(t.disabled_time)    stpk:w_s32(t.start_time_shift)        stpk:w_float(t.offline_interactive_radius)    stpk:w_u16(t.artefact_spawn_count)    stpk:w_s32(t.artefact_position_offset)        stpk:w_u8(t.last_spawn_time_present)      local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  sobj:STATE_Read(stpk,size)  sobj:UPDATE_Read(uppk)endfunction get_lc_data(obj)    local packet = net_packet()    obj:STATE_Write(packet)    local t={}    t.game_vertex_id = packet:r_u16()    t.distance = packet:r_float()    t.direct_control = packet:r_s32()    t.level_vertex_id = packet:r_s32()    t.object_flags = packet:r_s32()    t.custom_data = packet:r_stringZ()    t.story_id = packet:r_s32()    t.spawn_story_id = packet:r_s32()        t = xrs_utils.parse_shape_packet(t,packet)        t.restrictor_type = packet:r_u8()    t.dest_game_vertex_id = packet:r_u16()    t.dest_level_vertex_id = packet:r_s32()    t.dest_position = packet:r_vec3()    t.dest_direction = packet:r_vec3()    t.dest_level_name = packet:r_stringZ()    t.dest_graph_point = packet:r_stringZ()    t.silent_mode = packet:r_u8()    if packet:r_elapsed() ~= 0 then        abort("left=%d", packet:r_elapsed())    end    return tendfunction set_lc_data(t,obj)    local packet = net_packet()    obj:STATE_Write(packet)    packet:w_begin(t.game_vertex_id)    packet:w_float(t.distance)    packet:w_s32(t.direct_control)    packet:w_s32(t.level_vertex_id)    packet:w_s32(t.object_flags)    packet:w_stringZ(t.custom_data)    packet:w_s32(t.story_id)    packet:w_s32(t.spawn_story_id)        xrs_utils.fill_shape_packet(t,packet)    packet:w_u8(t.restrictor_type)    packet:w_u16(t.dest_game_vertex_id)    packet:w_s32(t.dest_level_vertex_id)    packet:w_vec3(t.dest_position)    packet:w_vec3(t.dest_direction)    packet:w_stringZ(t.dest_level_name)    packet:w_stringZ(t.dest_graph_point)    packet:w_u8(t.silent_mode)    packet:r_seek(0)    obj:STATE_Read(packet, packet:w_tell())endfunction parse_object_physic_packet(ret,stpk,updpk)    ret.physic_type=stpk:r_s32()    ret.mass=stpk:r_float()    ret.fixed_bones=stpk:r_stringZ()  return retendfunction fill_object_physic_packet(ret,stpk,updpk)    stpk:w_s32(ret.physic_type)    stpk:w_float(ret.mass)    stpk:w_stringZ(ret.fixed_bones)endfunction get_breakable_data(sobj)  local stpk=net_packet()  local uppk=net_packet()  sobj:STATE_Write(stpk)  sobj:UPDATE_Write(uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  local t={}    xrs_utils.parse_object_packet(t,stpk,uppk,size)  xrs_utils.parse_visual_packet(t,stpk,uppk,size)  xrs_utils.parse_object_physic_packet(t,stpk,uppk,size)  return tendfunction set_breakable_data(t,sobj)  local stpk=net_packet()  local uppk=net_packet()      xrs_utils.fill_object_packet(t,stpk,uppk)  xrs_utils.fill_visual_packet(t,stpk,uppk)  xrs_utils.fill_object_physic_packet(t,stpk,uppk)      local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  sobj:STATE_Read(stpk,size)  sobj:UPDATE_Read(uppk)endfunction get_spawner_data(sobj)  local stpk=net_packet()  local uppk=net_packet()  sobj:STATE_Write(stpk)  sobj:UPDATE_Write(uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  local t={}    xrs_utils.parse_object_packet(t,stpk,uppk,size)  xrs_utils.parse_shape_packet(t,stpk,uppk,size)        t.restrictor_type = stpk:r_u8()    t.spawned_obj_count = stpk:r_u8()  return tendfunction set_spawner_data(t,sobj)  local stpk=net_packet()  local uppk=net_packet()      xrs_utils.fill_object_packet(t,stpk,uppk)  xrs_utils.fill_shape_packet(t,stpk,uppk)    stpk:w_u8(t.restrictor_type)    stpk:w_u8(t.spawned_obj_count)      local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  sobj:STATE_Read(stpk,size)  sobj:UPDATE_Read(uppk)endfunction parse_shape_packet(t,stpk,uppk)    local shape_count = stpk:r_u8()    t.shapes={}    for i=1,shape_count do        local shape_type = stpk:r_u8()        t.shapes[i]={}        t.shapes[i].shtype=shape_type        if shape_type == 0 then            -- sphere            t.shapes[i].center = stpk:r_vec3()            t.shapes[i].radius = stpk:r_float()        else            -- box            t.shapes[i].v1 = stpk:r_vec3()            t.shapes[i].v2 = stpk:r_vec3()            t.shapes[i].v3 = stpk:r_vec3()            t.shapes[i].offset = stpk:r_vec3()        end    endendfunction fill_shape_packet(t,stpk,updpk)    stpk:w_u8(table.getn(t.shapes))    for i=1,table.getn(t.shapes) do        stpk:w_u8(t.shapes[i].shtype)        if t.shapes[i].shtype == 0 then            stpk:w_vec3(t.shapes[i].center)            stpk:w_float(t.shapes[i].radius)        else            stpk:w_vec3(t.shapes[i].v1)            stpk:w_vec3(t.shapes[i].v2)            stpk:w_vec3(t.shapes[i].v3)            stpk:w_vec3(t.shapes[i].offset)        end    endendfunction get_restrictor_data(sobj)  local stpk=net_packet()  local uppk=net_packet()  sobj:STATE_Write(stpk)  sobj:UPDATE_Write(uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  local t={}    xrs_utils.parse_object_packet(t,stpk,uppk,size)  xrs_utils.parse_shape_packet(t,stpk,uppk,size)        t.restrictor_type = stpk:r_u8()  return tendfunction set_restrictor_data(t,sobj)  local stpk=net_packet()  local uppk=net_packet()      xrs_utils.fill_object_packet(t,stpk,uppk)  xrs_utils.fill_shape_packet(t,stpk,uppk)    stpk:w_u8(t.restrictor_type)      local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  sobj:STATE_Read(stpk,size)  sobj:UPDATE_Read(uppk)endfunction get_trader_data(sobj)  local stpk=net_packet()  local uppk=net_packet()  sobj:STATE_Write(stpk)  sobj:UPDATE_Write(uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  local t={}    xrs_utils.parse_object_packet(t,stpk,uppk,size)  xrs_utils.parse_visual_packet(t,stpk,uppk,size)  xrs_utils.parse_trader_packet(t,stpk,uppk,size)  return tendfunction set_trader_data(t,sobj)  local stpk=net_packet()  local uppk=net_packet()      xrs_utils.fill_object_packet(t,stpk,uppk)  xrs_utils.fill_visual_packet(t,stpk,uppk)  xrs_utils.fill_trader_packet(t,stpk,uppk)      local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  sobj:STATE_Read(stpk,size)  sobj:UPDATE_Read(uppk)endfunction get_invbox_data(sobj)  local stpk=net_packet()  local uppk=net_packet()  sobj:STATE_Write(stpk)  sobj:UPDATE_Write(uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  local t={}    xrs_utils.parse_object_packet(t,stpk,uppk,size)  xrs_utils.parse_visual_packet(t,stpk,uppk,size)  return tendfunction set_invbox_data(t,sobj)  local stpk=net_packet()  local uppk=net_packet()      xrs_utils.fill_object_packet(t,stpk,uppk)  xrs_utils.fill_visual_packet(t,stpk,uppk)      local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  sobj:STATE_Read(stpk,size)  sobj:UPDATE_Read(uppk)endfunction readvu8uN(packet,n)  local v={}  for i=1,n,1 do    table.insert(v,packet:r_u8())  end  return vendfunction writevu8uN(pk,v)  local len=table.getn(v)  for i=1,len,1 do    pk:w_u8(v[i])  endendfunction parse_item_packet(ret,stpk,updpk)    ret.condition=stpk:r_float()    ret.updnum_items=updpk:r_u8()  ret.updpos={}  ret.updpos.x=updpk:r_float()  ret.updpos.y=updpk:r_float()  ret.updpos.z=updpk:r_float()    ret.updcse_alife_item__unk1_q8v4=readvu8uN(updpk,4)    ret.updcse_alife_item__unk2_q8v3=readvu8uN(updpk,3)    ret.updcse_alife_item__unk3_q8v3=readvu8uN(updpk,3)  return retendfunction fill_item_packet(ret,stpk,updpk)    stpk:w_float(ret.condition)    updpk:w_u8(ret.updnum_items)  updpk:w_float(ret.updpos.x)  updpk:w_float(ret.updpos.y)  updpk:w_float(ret.updpos.z)    writevu8uN(updpk,ret.updcse_alife_item__unk1_q8v4)    writevu8uN(updpk,ret.updcse_alife_item__unk2_q8v3)    writevu8uN(updpk,ret.updcse_alife_item__unk3_q8v3)  return retendfunction parse_item_ammo_packet(ret,stpk,updpk)    ret.ammo_left=stpk:r_u16()    ret.updammo_left=updpk:r_u16()  return retendfunction fill_item_ammo_packet(ret,stpk,updpk)    stpk:w_u16(ret.ammo_left)    updpk:w_u16(ret.updammo_left)  return retendfunction get_ammo_params(sobj)  local stpk=net_packet()  local uppk=net_packet()  sobj:STATE_Write(stpk)  sobj:UPDATE_Write(uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  local t={}    xrs_utils.parse_object_packet(t,stpk,uppk,size)  xrs_utils.parse_visual_packet(t,stpk,uppk,size)  xrs_utils.parse_item_packet(t,stpk,uppk,size)  xrs_utils.parse_item_ammo_packet(t,stpk,uppk,size)  return tendfunction set_ammo_data(t,sobj)  local stpk=net_packet()  local uppk=net_packet()      xrs_utils.fill_object_packet(t,stpk,uppk)  xrs_utils.fill_visual_packet(t,stpk,uppk)  xrs_utils.fill_item_packet(t,stpk,uppk)  xrs_utils.fill_item_ammo_packet(t,stpk,uppk)      local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  sobj:STATE_Read(stpk,size)  sobj:UPDATE_Read(uppk)endfunction get_destroyable_data(sobj)  local stpk=net_packet()  local uppk=net_packet()  sobj:STATE_Write(stpk)  sobj:UPDATE_Write(uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  local t={}    xrs_utils.parse_object_packet(t,stpk,uppk,size)  xrs_utils.parse_visual_packet(t,stpk,uppk,size)  xrs_utils.parse_skeleton_packet(t,stpk,uppk,size)  xrs_utils.parse_object_physic_packet(t,stpk,uppk,size)  return tendfunction set_destroyable_data(t,sobj)  local stpk=net_packet()  local uppk=net_packet()      xrs_utils.fill_object_packet(t,stpk,uppk)  xrs_utils.fill_visual_packet(t,stpk,uppk)  xrs_utils.fill_skeleton_packet(t,stpk,uppk)  xrs_utils.fill_object_physic_packet(t,stpk,uppk)      local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  sobj:STATE_Read(stpk,size)  sobj:UPDATE_Read(uppk)endfunction get_weapon_data(sobj)  local stpk=net_packet()  local uppk=net_packet()  sobj:STATE_Write(stpk)  sobj:UPDATE_Write(uppk)  local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  local t={}  xrs_utils.parse_object_packet(t,stpk,uppk,size)  xrs_utils.parse_visual_packet(t,stpk,uppk,size)  xrs_utils.parse_item_packet(t,stpk,uppk,size)  xrs_utils.parse_item_weapon_packet(t,stpk,uppk,size)  return tendfunction set_weapon_data(t,sobj)  local stpk=net_packet()  local uppk=net_packet()      xrs_utils.fill_object_packet(t,stpk,uppk)  xrs_utils.fill_visual_packet(t,stpk,uppk)  xrs_utils.fill_item_packet(t,stpk,uppk)  xrs_utils.fill_item_weapon_packet(t,stpk,uppk)      local size=stpk:w_tell()  local size1=uppk:w_tell()  stpk:r_seek(0)  uppk:r_seek(0)  sobj:STATE_Read(stpk,size)  sobj:UPDATE_Read(uppk)endfunction parse_item_weapon_packet(ret,stpk,updpk)    ret.ammo_current = stpk:r_u16()    ret.ammo_elapsed = stpk:r_u16()    ret.weapon_state = stpk:r_u8()    ret.addon_flags = stpk:r_u8()    ret.ammo_type = stpk:r_u8()        ret.updcondition = updpk:r_u8()    ret.updweapon_flags = updpk:r_u8()    ret.updammo_elapsed = updpk:r_u16()    ret.updaddon_flags = updpk:r_u8()    ret.updammo_type = updpk:r_u8()    ret.updweapon_state = updpk:r_u8()    ret.updweapon_zoom = updpk:r_u8()    ret.updcurrent_fire_mode = updpk:r_u8()  return retendfunction fill_item_weapon_packet(ret,stpk,updpk)    stpk:w_u16(ret.ammo_current)    stpk:w_u16(ret.ammo_elapsed)    stpk:w_u8(ret.weapon_state)    stpk:w_u8(ret.addon_flags)    stpk:w_u8(ret.ammo_type)        updpk:w_u8(ret.updcondition)    updpk:w_u8(ret.updweapon_flags)    updpk:w_u16(ret.updammo_elapsed)    updpk:w_u8(ret.updaddon_flags)    updpk:w_u8(ret.updammo_type)    updpk:w_u8(ret.updweapon_state)    updpk:w_u8(ret.updweapon_zoom)    updpk:w_u8(ret.updcurrent_fire_mode)  return retend-------------------------------------function parse_ini_section_to_array(ini,section)    local tmp={}    if ini:section_exist(section) then        local result, id, value = nil, nil, nil        for a=0,ini:line_count(section)-1 do            result, id, value = ini:r_line(section,a,"","")            if id~=nil and trim(id)~="" and trim(id)~=nil then                tmp[trim(id)]=trim(value)            end        end    else        dbglog("xrs_utils.parse_ini_section_to_array: no such section: "..tostring(section))    end    return tmpendfunction str_explode(div,str,clear)    local t={}    local cpt = string.find (str, div, 1, true)    if cpt then        repeat            if clear then                table.insert( t, trim(string.sub(str, 1, cpt-1)) )            else                table.insert( t, string.sub(str, 1, cpt-1) )            end            str = string.sub( str, cpt+string.len(div) )            cpt = string.find (str, div, 1, true)        until cpt==nil    end    if clear then        table.insert(t, trim(str))    else        table.insert(t, str)    end    return tendfunction quotemeta(str)    return (string.gsub(s, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%1"))end--äë˙ ďđŕâčëüíîăî ďŕđńčíăŕ çŕďđĺůĺíű ęîěěĺíňŕđčč!!!function parse_custom_data(str)    local t={}    if str then        for section, section_data in string.gfind(str,"%s*%[([^%]]*)%]%s*([^%[%z]*)%s*") do            section = trim(section)            t[section]={}            for line in string.gfind(trim(section_data), "([^n]*)n*") do                if string.find(line,"=")~=nil then                    for k, v in string.gfind(line, "([^=]-)%s*=%s*(.*)") do                        k = trim(k)                        if k~=nil and k~='' and v~=nil then                            t[section][k]=trim(v)                        end                    end                else                    for k, v in string.gfind(line, "(.*)") do                        k = trim(k)                        if k~=nil and k~='' then                            t[section][k]="<<no_value>>"                        end                    end                end            end        end    end    return tendfunction trim (s)    return (string.gsub(s, "^%s*(.-)%s*$", "%1"))endfunction gen_custom_data(tbl)    local str=''    for key, value in pairs(tbl) do        str = str.."n["..key.."]n"        for k, v in pairs(value) do            if v~="<<no_value>>" then                str=str..k.." = "..v.."n"            else                str=str..k.."n"            end        end    end    return strendlocal strfunction dump_table(tbl)  for k,v in pairs(tbl) do    if type(v)=="table" then	  dbglog("~~~ "..tostring(k).." => ")	  dump_table(v)    else	  str="~~~ "..tostring(k).." => "..tostring(v)	  if string.len(str)>200 then	    str=string.sub(str,1,200)	  end	  dbglog(str)    end  end  get_console():execute("flush")end
  > ```*
* Tworzymy kolejny plik skryptu - ja nadałem mu nazwę: **npctest.script**, nazwa pliku jest wykorzystywana do aktywacji funkcji poprzez dialog. Jeśli obierzemy inną nazwę - pamiętajmy aby wywołać funkcję poprzez wpis adekwatny do nadanej nazwy pliku. - patrz pkt. 8 niniejszego opracowania. Plik umieszczamy w folderze skryptów gry [gamedatascripts]. Jego zawartość to zbiór następujących funkcji (dla spawnu 3 NPC):

  *```
  function nowy_npc()local obj =alife():create("test",vector():set(-200.4694519043,-20.58443069458,-148.60520935059),52314,46)local params=xrs_utils.read_stalker_params(obj)params.custom="[logic]ncfg = scriptstest_npc_logic.ltx"params.sid=9000xrs_utils.write_stalker_params(params,obj)local obj =alife():create("test2",vector():set(-202.69140625,-20.110641479492,-143.33158874512),49764,57)local params=xrs_utils.read_stalker_params(obj)params.custom="[logic]ncfg = scriptstest2_npc_logic.ltx"params.sid=9001xrs_utils.write_stalker_params(params,obj)local obj =alife():create("test3",vector():set(-204.28082275391,-20.361850738525,-147.43516540527),48478,46)local params=xrs_utils.read_stalker_params(obj)params.custom="[logic]ncfg = scriptstest3_npc_logic.ltx"params.sid=9002xrs_utils.write_stalker_params(params,obj)end
  ```*

  Opis parametrów oraz działania funkcji - na przykładzie 1 sekcji.

  Funkcja **nowy\_npc()** - w moim przypadku aktywowana dialogiem, powoduje spawn 3 stalkerów (**test**, **test2**, **test3**) na zadane koordynaty. Dzięki wykorzystaniu skryptu **xrs\_utils.script** poprzez wpis: **local params** - możliwe jest sczytanie danych NPC'a [**read\_stalker\_params(obj)**], natomiast fragment: **params.custom** - umożliwia wykorzystanie schematu pracy NPC'a - zdefiniowanego w pliku .ltx o ścieżce określonej wpisem:

  *```
  "[logic]ncfg = scriptstest_npc_logic.ltx"
  ```*

  Ścieżka ta odnosi się do folderu scripts [gamedataconfigscripts] - zawierającego pliki .ltx (utworzymy je później). Początkującym adeptom moddingu chciałbym zwrócić uwagę na fakt, iż gra (mowa o Shoc) zawiera 2 foldery scripts: - jeden odpowiada za skrypty gry, zawierając pliki o rozszerzeniu **.script** [gamedatascripts], drugi - zawarty w folderze config, jest zbiorem plików **.ltx** [gamedataconfigscripts]. By uniknąć ewentualnej pomyłki - zwracam uwagę na ten być może oczywisty fakt. Opis plików konfigurujących schemat - patrz pkt.**6**

  Koordynaty odpowiednio w skrypcie:

  *```
  X: -200.4694519043Y: -20.58443069458Z: -148.60520935059level_vertex_id: 52314game_vertex_id: 46sid=9000 - jest to numeryczny identyfikator postaci - odnoszący się do wpisu zawartego w pliku game_story_ids.ltx [gamedataconfig], patrz pkt. 10
  ```*

  Następnym krokiem jest zdefiniowanie parametrów postaci odpowiedzialnych za wygląd, nazwę, stan uzbrojenia, wyświetlaną ikonę, dialogi, ilość gotówki, etc.
* Ponieważ tworzę jednostki na Kordonie - modyfikuję plik: **character\_desc\_escape.xml** [gamedataconfiggameplay], umieszczając tam wpisy definiujące 3 stalkerów:

  > *```
  > <!-------------------------------------Początek_sekcji------------------------------------------><!-------------------------------------Test----------------------------------------------------->    <specific_character id="test" team_default = "1">   <name>Test</name>   <icon>ui_npc_u_stalker_do_nauchniy</icon>   <map_icon x="0" y="0"></map_icon>   <bio>sim_stalker_master_bio</bio>   <class>test</class>   <community>stalker</community> <terrain_sect>stalker_terrain</terrain_sect>   <money min="100000" max="110000" infinitive="1"></money>   <rank>570</rank>   <reputation>100</reputation>   <visual>actorsdolgstalker_do_nauchniy</visual>   <snd_config>characters_voicehuman_03stalker</snd_config>   <crouch_type>0</crouch_type>   <supplies>   [spawn] n   wpn_groza n   ammo_9x39_ap n   ammo_9x19_fmj n   #include "gameplaycharacter_food.xml" n   #include "gameplaycharacter_drugs.xml"   </supplies>   #include "gameplaycharacter_criticals_6.xml"   #include "gameplaycharacter_dialogs.xml"   </specific_character>      <!-------------------------------------Test2----------------------------------------------------->    <specific_character id="test2" team_default = "1">   <name>Test2</name>   <icon>ui_npc_u_stalker_do_nauchniy</icon>   <map_icon x="0" y="0"></map_icon>   <bio>sim_stalker_master_bio</bio>   <class>test2</class>   <community>stalker</community> <terrain_sect>stalker_terrain</terrain_sect>   <money min="100000" max="110000" infinitive="1"></money>   <rank>571</rank>   <reputation>101</reputation>   <visual>actorsdolgstalker_do_nauchniy</visual>   <snd_config>characters_voicehuman_03stalker</snd_config>   <crouch_type>0</crouch_type>   <supplies>   [spawn] n   wpn_groza n   ammo_9x39_ap n   ammo_9x19_fmj n   #include "gameplaycharacter_food.xml" n   #include "gameplaycharacter_drugs.xml"   </supplies>   #include "gameplaycharacter_criticals_6.xml"   #include "gameplaycharacter_dialogs.xml"   </specific_character>     <!-------------------------------------Test3----------------------------------------------------->    <specific_character id="test3" team_default = "1">   <name>Test3</name>   <icon>ui_npc_u_stalker_do_nauchniy</icon>   <map_icon x="0" y="0"></map_icon>   <bio>sim_stalker_master_bio</bio>   <class>test3</class>   <community>stalker</community> <terrain_sect>stalker_terrain</terrain_sect>   <money min="100000" max="110000" infinitive="1"></money>   <rank>572</rank>   <reputation>102</reputation>   <visual>actorsdolgstalker_do_nauchniy</visual>   <snd_config>characters_voicehuman_03stalker</snd_config>   <crouch_type>0</crouch_type>   <supplies>   [spawn] n   wpn_groza n   ammo_9x39_ap n   ammo_9x19_fmj n   #include "gameplaycharacter_food.xml" n   #include "gameplaycharacter_drugs.xml"   </supplies>   #include "gameplaycharacter_criticals_6.xml"   #include "gameplaycharacter_dialogs.xml"   </specific_character>     <!-------------------------------------Koniec_sekcji----------------------------------------------------->
  > ```*

  Parametry opisujące tworzoną postać zostały omówione w tym **[poradniku](http://stalkerteam.pl/topic/5969-tutorial-tworzenie-stalkerskich-oboz%C3%B3w-oraz-dodawanie-npc-do-gry/)**, w pkt. 8.2 - etapu wykonawczego tworzenia modyfikacji, toteż odsyłam pod adres w celu uzupełnienia informacji.
* W pliku **npc\_profile.xml** [gamedataconfiggameplay] umieszczamy wpisy o następującej treści (dla każdej postaci):

  *```
      <character id="test">          <class>test</class>      </character>        <character id="test2">          <class>test2</class>      </character>    <character id="test3">          <class>test3</class>      </character>
  ```*

  Stanowią one uzupełnienie parametru "**specific\_character id**" z pliku **character\_desc\_escape.xml** - jak również opisanego w pkt.2 i wykorzystywanego przez skrypty identyfikatora dodawanych postaci: **"test"/"test2"/"test3"**
* Do pliku **spawn\_sections.ltx** [gamedataconfigcreatures] dodajemy wpisy, które podobnie jak opisane poprzednio - stanowią uzupełnienie identyfikatorów profilowych: **"test"/"test2"/"test3"**

  *```
  ;--początek sekcji[test]:stalker  $spawn = "respawntest"  character_profile = test  spec_rank = master  community = stalker[test2]:stalker  $spawn = "respawntest2"  character_profile = test2  spec_rank = master  community = stalker[test3]:stalker  $spawn = "respawntest3"  character_profile = test3  spec_rank = master  community = stalker;--koniec sekcji
  ```*

  Pamiętajmy - by tworząc powyższe zestawienie - zachować zgodność parametrów z utworzonym wcześniej profilem postaci, omówionym w pkt. 3. tj:  
  **□** **character\_profile**  
  **□** **spec\_rank** - patrz **[link](http://stalkerteam.pl/topic/5969-tutorial-tworzenie-stalkerskich-oboz%C3%B3w-oraz-dodawanie-npc-do-gry/)**, pkt. 8.5 - w części II poradnika.  
  **□** **community**
* Tworzymy 3 pliki .**ltx** odpowiadające za schemat wykorzystywany przez NPC, opierający się o wykorzystanie punktu odniesienia(punkt dodamy później). Pliki umieszczamy w folderze scripts [lokalizacja:gamedataconfigscripts]. Zgodnie ze ścieżką zawartą w funkcji odpowiedzialnej za spawn NPC, opisanej w pkt. **2** - tworzymy zatem pliki:  
  **□** **test\_npc\_logic.ltx**  
  **□** **test2\_npc\_logic.ltx**  
  **□** **test3\_npc\_logic.ltx**  
    
  Każdy z w/w plików powinien zawierać wpis następujący:

  *```
  [smart_terrains]none = true[logic]active=kamp[kamp]center_point = esc_siv_centr_kamp
  ```*

  Jest to odniesienie do punktu o nazwie "**esc\_siv\_centr\_kamp**", który zostanie dodany do pod-pliku **way\_\*.ltx** stanowiącego część składową all.spawn'a. Ów punkt będą wykorzystywać jednostki oznaczone jako **"test"/"test2"/"test3"** (stalkerzy siedzący przy ognisku - schemat "**kamp**").
* Następnie musimy znaleźć sposób na aktywację utworzonej w pkt. **2** funkcji **nowy\_npc()**. Ja w przykładzie wykorzystam dialog, który muszę wprowadzić do gry. Kilka uzupełniających informacji nt. dialogów i możliwości ich wykorzystania - można przeczytać pod tym **[linkiem](http://stalkerteam.pl/topic/5818-tutorial-tworzenie-zada%C5%84-z-wykorzystaniem-w%C5%82asnego-skryptu/)** do poradnika. Wykorzystam postać Wilka do realizacji tej części modyfikacji, zatem do pliku **character\_desc\_escape.xml** [lokalizacja: gamedataconfiggameplay] w sekcji Wilka tj.

  *```
  specific_character id="esc_wolf"
  ```*

  dodaję w miejsce występowania dialogów wpis:

  *```
  <actor_dialog>testowy_dialog</actor_dialog>
  ```*
* W pliku dialogów z Kordonu tj. **dialogs\_escape.xml** [lokalizacja: gamedataconfiggameplay] - tworzę drzewo dialogowe, dla osadzenia tekstu.

  *```
  <dialog id="testowy_dialog">	    <phrase_list>		    <phrase id="0">			    <text>testowy_dialog_0</text>	    <next>1</next>		    </phrase>		    <phrase id="1">			    <text>testowy_dialog_1</text>			    <action>npctest.nowy_npc</action>	    <next>2</next>		    </phrase>		    <phrase id="2">			    <text>testowy_dialog_2</text>			    <action>dialogs.break_dialog</action>		    </phrase>	    </phrase_list>    </dialog>
  ```*

  gdzie:

  **□** **dialog id="testowy\_dialog"** - jest identyfikatorem użytym w pkt.**7** - wkomponowanym w sekcję dialogów Wilka.

  **□** **testowy\_dialog\_0, testowy\_dialog\_1, testowy\_dialog\_2** są odnośnikami do tekstu, zawartego w pliku **stable\_dialogs\_escape.xml** [lokalizacja: gamedataconfigtextpol] - adekwatnie do lokacji.

  **□ <action>...</action>** - ów tag odpowiada za aktywację funkcji. W tym konkretnym przypadku aktywowana jest funkcja "**nowy\_npc**" zawarta w pliku **npctest.script** - patrz pkt. **2**. Konstrukcja aktywatora to w pierwszej kolejności nazwa pliku bez rozszerzenia, w drugiej - nazwa funkcji. Obie części rozdziela kropka.

  **□** **dialogs.break\_dialog** - ucięcie dialogu realizowane funkcją "break\_dialog" zawartą w pliku dialogs.script

  ***Uwaga***: stworzone drzewo dialogowe to wersja uproszczona, mająca posłużyć za przetestowanie skryptu. Konstrukcja drzewa dialogowego winna posiadać zdefiniowany infoportions, aby wyeliminować możliwość permanentnego spawnowania tych samych jednostek - co mogłoby skutkować błędami! Link w pkt. **7** zawiera przydatne informacje nt. tworzenia infoportions.
* Kolejny etap - to utworzenie właściwego dialogu. Do pliku odpowiadającego za dialogi z Kordonu tj. **stable\_dialogs\_escape.xml** [lokalizacja: gamedataconfigtextpol] dodaję następującą sekcję:

  *```
  <string id="testowy_dialog_0">    <text>Cześć Wilk, słyszałem że możesz w "mgnieniu oka" ściągnąć tu oddział Powinności?</text></string><string id="testowy_dialog_1">    <text>Dobrze słyszałeś - spójrz w kierunku płd. Chcesz się tego nauczyć? - %c[255,1,255,255]przeczytaj Tutorial ze StalkerTeam'u.</text></string><string id="testowy_dialog_2">    <text>O w mordę...</text></string> 
  ```*

  Chcąc wyróżnić fragment tekstu - wystarczy dopisać przed nim **%c[255,1,255,255]**, co odpowiada kolorowi błękitnemu - patrz filmik.
* Do pliku **game\_story\_ids.ltx** [lokalizacja: gamedataconfig] dodajemy identyfikator numeryczny zgodny ze zdefiniowanym profilem .xml oraz funkcją opisaną w pkt. **2** niniejszego rozdziału.

  *```
    9000	    = "test"  9001	    = "test2"  9002	    = "test3"
  ```*

  Po zmodyfikowaniu wszystkich plików i zapisaniu zmian - przystępujemy do modyfikacji pliku **all.spawn**.  
  Poniższa część przedstawiona jest również na filmiku z testem modyfikacji.
* Dekompilujemy plik all.spawn - patrz ten **[poradnik](http://stalkerteam.pl/topic/5838-tutorial-podstawowe-metody-spawnowania-acdcxr-spawnerskrypty-cz-i/)**, cz.I, dodając do powstałych plików **way\_****\*****.ltx** (\* - nazwa mapy) - punkty odniesienia. W moim przykładzie 3 NPC'ów wykorzystuje jeden punkt w wiosce nowicjuszy, zatem dodaję sekcję odpowiadającą za punkt do pliku **way\_l01\_escape.ltx** :

  *```
  [esc_siv_centr_kamp]points = p0p0:name = name00p0:position = -207.013442993164,-20.4290790557861,-169.828475952148p0:game_vertex_id = 50p0:level_vertex_id = 45902
  ```*

  gdzie:

  **□** **[esc\_siv\_centr\_kamp]** - to identyfikator punktu  występujący w plikach opisanych w pkt. **6**  
  Po wprowadzeniu danych - zapisuję plik i przystępuję do procesu kompilacji. Ważne jest aby nie dodawać (lub usuwać) do plików **alife\_\*.ltx** - żadnych obiektów, by nie zmieniać ich ilości. Kompilacja i sposób jej przygotowania - patrz link pkt. **11**.
* Plik **all.spawn** zawierający punkt odniesienia przygotowany dla spawnowanych jednostek  - umieszczamy w folderze spawns. Jak widać na filmiku poniżej - nie potrzeba nowej gry aby jednostki pojawiły się na mapie.

**Prezentacja wprowadzonych modyfikacji:**

http://youtu.be/Q\_ZVj6Zb2iU

Prawa autorskie na podstawie **[regulaminu](http://stalkerteam.pl/index.php?app=forums&module=extras&section=boardrules)**, pkt.1.6. - **The Emperor**, wyłączność: [**StalkerTeam.**](http://stalkerteam.pl/)

\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_

**Autor: The Emperor dla StalkerTeam**
3 polubienia