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.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§ion=boardrules)**, pkt.1.6. - **The Emperor**, wyłączność: [**StalkerTeam.**](http://stalkerteam.pl/)
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
**Autor: The Emperor dla StalkerTeam**