World Interaction System: руководство контент-разработчика и администратора

1. Назначение и текущий статус

Это практическое руководство описывает подготовку статического контента, клиентского представления, server-authoritative runtime и диагностику World Interaction System (WI).

Документ не является разрешением применять SQL, запускать миграции, сервер, клиент, сборку или генераторы. Все SQL-блоки ниже предназначены для reviewed candidate-копий баз. PostgreSQL fresh migrations psql/0020..0026 являются неприменёнными source migrations и не должны выполняться без отдельного DBA/release review и явного разрешения.

Что реально поддерживает текущий runtime

Возможность Статус
Item → skill → InteractionEffect → Collect Runnable только в isolated cloned projection: real target-once item 6363 с временным exact a_num_0/use_skill_id=84, затем обязательный restore 1161/predecessor binding
NPC action → Collect Runtime-open в том же Collect subset
Doodad discovery и exact deterministic DoodadFuncLootItem Runtime-open
Один area-owned Timer/Growth scheduler Runtime-open
Exact item_spawn_doodads placement Runtime-open
Isolated synchronous skill plots with supported concrete effects Runtime-open bounded subset; InteractionEffect plots rejected
Immediate craft с crafts.delay = 0 Runtime-open
Doodad Craft Pack/Start → существующий Craft.xml Runtime-open для discovery/context; phase не меняется
Ordinary corpse Inspect/TakeAll/TakeItem Runtime-open, но только memory-only
Blueprint preview/placement/build contribution/completion Код существует, но production rollout заблокирован geometry/model/projection/PG/release packages
Seed InsideGarden и garden harvest Код существует, но зависит от полностью готового housing runtime
Authored ore/tree spawn и Gather Код существует, но rollout заблокирован spawn/model/map package
Delayed craft (delay != 0) Fail closed; delayed Labor/job path отсутствует
Durable corpse loot Fail closed: нет stable persisted NPC generation
Decoration placement, common farms, authoritative ClimateReact Fail closed

Текущий release verdict — NO-GO. Нет canonical non-zero projection, final SQLite/client/map artifacts, approved PostgreSQL subset, canary isolation и достаточной telemetry.

1.1 Быстрый выбор сценария

Что нужно получить Основные authoring rows Где показывается Runtime/PG boundary Статус/раздел
Использовать предмет и получить другой предмет items.a_num_0 = items.use_skill_idskills.a_indexskill_effects.effect_ideffects.actual_idinteraction_effects.wi_id; skill_products, source/skill_reagents inventory item/skill presentation 0020 + 0021 при labor Isolated runnable fixture 6363→84; требуется self-target через portrait и exact restore, §7
Действие у NPC t_npc.npc_interaction_set_idnpc_interaction_sets.idnpc_interactions exact direct skill_id XOR global skill_constant_key; direct продолжает effect/WI chain, service constant идёт в typed service dispatch interactActions.xml direct Collect writer; services отдельным handler slice Runtime-open, §7
Поставить seed/workbench items.use_skill_id + item_spawn_doodads.item_id/doodad_id → Start phase item-use placement UX, world model 0020..0023, placement context из 0026 Runtime-open, §8
Harvest/Gather с ростом/respawn doodad_funcsDoodadFuncLootItem; doodad_phase_funcs → Growth/Timer doodad action + countdown inventory/labor + object phase/revision Runtime-open narrow subset, §8/§11.3
Крафт у NPC crafts.craft_npc, craft_materials, single craft_products existing Craft.xml immediate existing Craft path; delay=0 Runtime-open, §9
Крафт у doodad DoodadFuncCraftPack или DoodadFuncCraftStart → recipe membership action icon slot opens Craft.xml object generation/revision/context revalidated Runtime-open context, §9
Loot обычного трупа loot_pack_dropping_npcs → pack/group/entries interactActions.xml + lootWindow.xml area memory + normal character inventory save; per-action DB commit и durable corpse tables отсутствуют Runtime-open memory-only, §10
Blueprint/build/garden item_housings, housing graph/polygon, build skill reagents; seed InsideGarden preview/world/build action fresh 0020..0026 Код есть, rollout blocked, §11
Статический ore/tree lc_doodad_spawns → exact doodad graph world model/action/countdown 0020..0023; gather serialization in CArea, без claim table Код есть, package blocked, §11.3
Skill plot skills.plot_idplots → events/conditions/effects/edges обычное skill presentation synchronous bounded executor Не является WI interaction chain; template-only, §11.4
Decorations/common farms/climate соответствующие 0006 tables недостаточны нет positive runtime handler/package отсутствуют Fail closed, §11.5

Правило выбора: сначала выберите одну строку таблицы, затем добавляйте только её closure. Наличие соседних tables не включает соседний сценарий.

2. Граница доверия

Сервер авторитетен для:

Клиентские craft_id, slot, transform и выбранная action — только запрос. Локальная SQLite, UI, model/icon mapping и preview не дают gameplay authority.

2.1 Краткие authoritative flows

Item Collect:
actual inventory slot → item use binding → skill/effect/WI Collect
→ exact reagent/product/labor plan → revalidate → one durable commit → publish result

NPC action:
runtime NPC → prototype/set/action → server-resolved skill target
→ supported Collect plan → revalidate NPC/range/resources → commit

Doodad:
object+generation → current phase+revision → exact func_skill_id/action
→ permission/range/runtime subset → area claim → inventory/phase transaction without Labor
→ successful completion → memory Labor deduction → publish

Craft:
client-local recipe presentation → craft_id + NPC/doodad context
→ server preloaded recipe/context/material/NAS/labor/proficiency validation
→ immediate mutation/result; delay != 0 rejected

Corpse:
NPC death → server loot route/roll → memory-only area corpse
→ eligible participant Inspect → activation/id/revision TakeAll
→ server partial reward plan/claim → shared inventory commit

Housing:
actual blueprint slot + proposed transform → server binding/geometry/owner-limit preview
→ second full validation + footprint claim → plot/carrier/building commit
→ exact build-step actions/reagents/labor/revisions → terminal completion

3. Где находятся данные

3.1 Server static SQLite

Canonical DDL sources:

  1. sqlite/0001_wi_skill_item_bridge.sql — foundation, item/skill/effect/WI/NPC.
  2. sqlite/0002_doodads_and_placement.sql — doodad templates, phases/functions, placement.
  3. sqlite/0003_loot.sql — global skill constants/default corpse action types, loot packs/routes и doodad loot payloads.
  4. sqlite/0004_craft.sql — craft extension, packs and workbench payloads.
  5. sqlite/0005_skill_plots.sql — skill plot graph.
  6. sqlite/0006_housing_farming_and_layout.sql — housing/farm/climate/map adapter tables.

Existing LC tables remain authoritative identities: items.a_index, skills.a_index, effects.id, skill_effects.id, crafts.id, t_npc.a_index.

3.2 Client compact SQLite

3.3 PostgreSQL lc_game

PostgreSQL stores mutable durable state, not the static catalog:

Source Purpose
0020 operation identity and inventory reservations; NAS mutates directly in the cohesive commit
0021 account/character Labor и account-leading character index
0022 durable account-to-static-account-buff assignments; requires 0021 first
0023 doodad instances and deadlines
0024 housing plots/buildings
0025 construction, ACL, decoration state
0026 doodad placement contexts

Во всех WI PostgreSQL tables account_id — integer t_characters.a_user_index, а не text a_portal_index. Fresh chain не содержит delayed Craft, durable corpse, audit/gathering-claim или Labor cleanup migrations; rollback выполняется DB recreation/snapshot.

3.4 Runtime и UI file map

Server:

Client:

3.5 Что именно класть в server и client SQLite

Данные Server static SQLite Client compact SQLite Зачем
LC items/skills identities и gameplay fields Да Да, те же IDs и только нужные client columns Server проверяет gameplay, client разрешает name/icon/tooltip и item-use UX. В baseline/client schema есть items.a_num_0, но нет WI-only items.use_skill_id/use_skill_as_reagent и skill extension tuple из 0001; эти columns принадлежат server projection
effects, concrete effect rows, skill_effects, interaction_effects, wi_*, reagents/products/requirements Да Только если уже нужны существующему client skill path; не authority Server строит и исполняет chain
NPC set/action binding Да Не требуется для authority Action list приходит с server; client получает только action/skill/availability
Doodad templates/phases/functions и item_spawn_doodads Да item_spawn_doodads нужен placement UX; craft-action slots нужны doodad Craft UI Server владеет graph; client лишь routing/presentation
crafts, craft_materials, craft_products, categories и Pack/Start membership Да Да, exact matching recipe IDs/rows Server валидирует cache; Craft.xml читает local catalog и не получает его по сети
Loot packs/routes Да Нет Roll и eligibility только server-side; item presentation уже есть локально
Housing graph/polygons/spawns Да Только item bindings, необходимые UX, плюс matching released map Geometry/ownership только server-side
icons и localized item/skill texts Не нужны gameplay handler-у Да Локальные names/icons/tooltips
lc_model_resources Нет Да Разрешает server-sent model ID в .smc/.mdl resource

Нельзя «применить demo SQL один раз в какую-то SQLite». Для recipe/doodad UX выпускаются две reviewed проекции: server-authoritative и client-presentation. Их shared IDs должны совпасть, а допустимые различия перечисляются в projection manifest.

3.6 Item-grade schema и artifact boundary

4. Startup/load order и all-or-nothing gates

Server load order:

LC item/skill prototypes
→ generic effects and concrete effect payloads
→ WI foundation
→ optional loot
→ optional doodads and craft payload types
→ effect/doodad closure
→ optional climates
→ optional farms
→ optional authored spawns
→ optional housing
→ optional skill plots (or prove no plot links)
→ NPC action bindings
→ atomic immutable snapshot publication
→ existing Craft cache

Правила gates:

Canonical packaging caveat: sqlite/0006_housing_farming_and_layout.sql физически создаёт housing, climate, farm и lc_doodad_spawns gates одним script. Поэтому «установить только housing из 0006» без изменения reviewed migration stream нельзя, а полное применение 0006 одновременно объявляет остальные complete schemas installed (даже если rows empty). Release owner обязан либо дать accepted content/disposition для каждого gate из 0006, либо отдельно одобрить split schema migration. Ручное вырезание CREATE TABLE blocks не является projection.

Canonical DDL также содержит forward FK references: 0001.skills.plot_id→plots(0005), 0001.interaction_effects.doodad_id→doodad_almighties(0002), 0002.doodad_almighties.climate_id→climates(0006). DDL source order сохранён, но content DML нельзя считать независимо deployable только по номеру script: candidate schema preflight обязан доказать фактическое parent-table closure и PRAGMA foreign_key_check на offline copy. Такая executable проверка в рамках этого аудита не запускалась.

skills.target_selection_id=0/NULL означает Unspecified для generic staging legacy skills и не блокирует snapshot. Явное 1..4 обязательно только для skills, enrolled через реальный WI binding (item/effect/NPC/doodad/craft/housing/plot). Не обновляйте все 1618 legacy rows. Аналогично DDL default doodad_almighties.model_kind_id=0 loader отвергает: у каждой doodad row требуется exact 1..7.

5. Важные numeric values

Item target-use dispatch

Meaning Client enum Server enum Value
Once-use item type CItemData::ITEM_ONCEUSE ITYPE_ONCE 2
Target once-use subtype CItemData::ITEM_SUB_TARGET IONCE_TARGET 10

Client CNetworkLibrary::UseSlotItem() входит в target branch только для exact 2/10, берёт skill из items.a_num_0, требует active eTARGET, использует этот skill для delay/range и отправляет MSG_ITEM_TARGET. Server do_ItemTarget() независимо повторяет exact 2/10 check и передаёт тот же getItemNum0() в ResolveItemInteractionSkill(). Для WI binding server затем требует exact equality items.a_num_0 = items.use_skill_id; одного use_skill_id недостаточно.

Skill targets

Meaning Value
Self 0
Friendly / Party / Raid / Hostile / AnyUnit 13 / 14 / 15 / 16 / 17
Position 18
Doodad 20
Pet 22
SourcePosition / Others / FriendlyOthers 26 / 28 / 29
CursorPosition / GeneralUnit / MySlave 30 / 31 / 38
Corpse 39
Selection Unspecified (legacy staging only) 0
Selection Source 1
Selection Target 2
Selection Line 3
Selection Location 4
Relation Any 0

Doodads

Meaning Value
Start phase 1
Normal phase 2
End phase 3
Public permission 0
Owner permission 1
Account permission 6
Invalid placement area 0
InsideGarden 1
OutsideGarden 2

doodad_almighties.model_kind_id также является loader enum, а не permissive zero default:

Meaning Value
Default 1
Plant 2
Flower 3
Grass 4
BigTree 5
MediumTree 6
SmallTree 7

Loader принимает полный enum range, но положительный runtime уже: для action permission реализованы только Public=0, Owner=1, Account=6; остальные значения загружаются, но action discovery возвращает unavailable. Для placement поддержаны только exact InsideGarden=1 и OutsideGarden=2; Invalid=0 не является permissive default.

WI groups имеют фиксированную runtime identity и не относятся к demo range:

wi_groups.id Exact name Handler
1 Craft craft metadata
2 Collect implemented Collect paths
3 Building reserved building identity

5.1 Exact связи и смысл колонок

Откуда → куда Exact columns Что это означает
item → skill target-use dispatch: items.a_num_0; WI authority: items.use_skill_id = skills.a_index; direct path требует a_num_0=use_skill_id; items.use_skill_as_reagent Какое умение client выбирает для target packet и какое server повторно разрешает из фактического item slot; при use_skill_as_reagent=1 direct Collect дополнительно списывает ровно один source item
skill → generic effect skill_effects.skill_id = skills.a_index, skill_effects.effect_id = effects.id, skill_effects.enable=1 Какие enabled effects принадлежат skill
generic effect → interaction payload effects.actual_type='InteractionEffect', effects.actual_id = interaction_effects.id Exact case-sensitive family и concrete payload identity; actual_id не является wi_id; interaction_effects.source_direction current loader читает как boolean 0/1
interaction payload → WI interaction_effects.wi_id = wi_details.wi_id = wi_group_wis.wi_id; wi_group_wis.wi_group_id = wi_groups.id Выбирает handler group и metadata wi_details.lp/apply_expert/distance_sqrt
skill economy skill_reagents.skill_id/item_id/amount/enable, skill_products.skill_id/item_id/amount/enable Server-authored inputs/outputs direct Collect/build paths; строки одного item агрегируются
skill requirements/tools unit_reqs.owner_type='Skill', unit_reqs.owner_id=skills.a_index; skills.or_unit_reqs, mainhand_tool_id, offhand_tool_id Metadata загружается, но current Collect/doodad placement/harvest/build runtime требует отсутствие requirements/tools и or_unit_reqs=0; это не способ включить проверку requirement
NPC → action skill t_npc.npc_interaction_set_id = npc_interaction_sets.id; npc_interactions.npc_interaction_set_id, exact one-of skill_id/skill_constant_key Server публикует positive npc_interactions.id как action ID и resolved positive skill ID как presentation key; source kind/key остаётся typed server dispatch metadata
placement item → doodad item_spawn_doodads.item_id = items.a_index, .doodad_id = doodad_almighties.id; item одновременно обязан иметь items.use_skill_id Размещаемый template выбирается binding-ом, а допустимость placement — skill metadata фактического item
doodad → phase doodad_func_groups.doodad_almighty_id = doodad_almighties.id, .doodad_func_group_kind_id Ровно одна Start phase (1); model берётся из phase .model, иначе template .model
phase → manual action doodad_funcs.doodad_func_group_id, .actual_func_type/.actual_func_id, .func_skill_id, .perm_id, .next_phase func_skill_id — единственный discovery key; polymorphic payload выбирается exact type+ID; SQL FK на payload намеренно нет
phase → automatic function doodad_phase_funcs.doodad_func_group_id, .actual_func_type/.actual_func_id Timer/Growth создают один durable deadline; это не clickable action
loot action → WI/item doodad_func_loot_items.id = doodad_funcs.actual_func_id, .wi_id, .item_id, .count_min/.count_max/.percent/.remain_time/.group_id Current positive handler требует fixed count, 10000, 0, 0, Collect WI и different action next_phase
craft recipe → WI/skill/context crafts.id, .skill_id, .wi_id, .craft_npc, .req_doodad_id; pack/start membership tables Recipe economy связывается с Craft WI; NPC, direct doodad binding и Pack/Start — разные context routes
blueprint → design/build item_housings.item_id/design_id/completion; housing_build_steps.housing_id/step/model_id/skill_id/num_actions completion=0 — blueprint; build step должен иметь exact skill_reagents and contiguous step from 1
housing area → geometry housing_areas.housing_group_id; lc_housing_area_polygons.housing_area_id/world_id/zone_id/area_id/layer_id/is_exclusion; vertices Server-authoritative inclusion/exclusion scope и ordered 1-based X/Z polygon
skill → skill plot skills.plot_id = plots.id; event/condition/effect/edge tables use their exact parent IDs Отдельный synchronous skill executor. plot_effects.actual_id ссылается на concrete effect payload, не на effects.id; InteractionEffect для plot запрещён

6. Demo ID manifest и обязательные замены

Диапазон 900000+ ниже остаётся только для WI-owned identities и map/model scope placeholders. Все внешние item/NPC/skill references являются реальными LC prototypes из export json и статически сверены с Server/wearables.sql; это доказывает baseline identity выбранных LC rows, но не превращает соседние AA graph IDs в production mapping.

6.1 Реальные prototypes, используемые примерами

Для item/NPC/skill rows ключом является соответственно items.a_index, t_npc.a_index, skills.a_index. Имена взяты из localized_texts.json (tbl_name, tbl_column_name='name', idx=<ID>). Icon metadata взята из items.json/skills.jsonicon_idicons.json(id,filename); filenames ниже не выдуманы.

ID Имя ru_ru (en_us) Source evidence Назначение в guide Риск/ограничение
6363 Свиток немоты (Silent Scroll of Keron Mage Collective) items.json:a_index=6363, enabled exact type/subtype 2/10, a_num_0=1161, a_num_1=1, SMC Data\Item\Common\ITEM_treasure02.smc; smc_id.model_name='ITEM_treasure02', mesh Data\Item\Common\ITEM_treasure02.bm, texture Data\Item\Common\Texture\ITEM_treasure.tex; localized_texts.json:items/name+description/6363; icon 2657legacy/items/tex_0010_r10_c01.png; exact row также присутствует в Server/wearables.sql isolated direct Collect source item Наименьший найденный exported consumer closure: только item presentation/model rows и original skill binding; zero exact item-key rows в quest/exchange/equipment/collection/luckydraw/craft/NPC-drop tables и zero semantic source literal. Existing skill 1161 имеет canonical a_targetType=2 (TargetOne), unsupported для WI Collect. Поэтому client clone временно меняет только реально существующий dispatch field a_num_0:1161→84, а server clone — a_num_0:1161→84 плюс WI-only use_skill_id=84/use_skill_as_reagent=1; full per-schema predecessor snapshot/drift/affected-row/restore обязателен. Existing name/description намеренно остаются combat-scroll и не являются production Collect UX
117 Плод древня (Treant Fruit) items.json:a_index=117; localized_texts.json:items/name/117; icon 264legacy/items/tex_0001_r00_c07.png direct Collect product Existing economy item: enabled collection requirements и legacy NPC drop slots; prototype/consumer rows не менять и не удалять
114 Волчий клык (Wolf Fang) items.json:a_index=114; localized_texts.json:items/name/114; icon 261legacy/items/tex_0001_r00_c04.png NPC Collect reagent Existing economy item: enabled collection requirements и legacy NPC drop slots; demo расходует реальные stacks только в isolated clone
115 Волчий коготь (Wolf Claw) items.json:a_index=115; localized_texts.json:items/name/115; icon 262legacy/items/tex_0001_r00_c05.png NPC Collect product Existing economy item: enabled collection requirements и legacy NPC drop slots; demo создаёт additional supply только в isolated clone
876 Семечко (Seed) items.json:a_index=876; localized_texts.json:items/name/876; icon 802legacy/items/tex_0003_r02_c12.png seed/placement item Existing enabled seed identity. Exact-key export audit нашёл presentation/model closure, но отсутствие явной relation не доказывает отсутствие player-held/live use; новый item-use binding поэтому всё равно cloned-isolation-only
11378 Чертеж оружия титана (Drawing of the titan weapon) items.json:a_index=11378, enabled type/subtype 2/6, a_num_0=837; localized_texts.json:items/name/11378; icon 3906legacy/items/tex_0016_r02_c00.png; t_quest.json:a_index=837,a_start_type=1,a_start_data=11378 housing blueprint identity вместо fake 900009 Не housing prototype и не production-safe: это production quest-start drawing. Допустим только как identity-shape fixture на клонированной DB, отдельном character/zone и с exact snapshot/restore use_skill_*; quest row и item prototype не изменять/не удалять
11539 ТЕСТОВЫЙ МАТЕРИАЛ СИНТЕЗА (Carp) items.json:a_index=11539, enabled type/subtype 2/7; localized_texts.json:items/name/11539; icon 4449legacy/items/tex_0018_r06_c12.png; t_synthesis_items.json:id=1,item_id=11539,grade=1,synthesis_exp_add=30000,is_pickup=1 workbench placement identity вместо fake 900011 Явный test material и не equipment/quest/craft material/product, но имеет существующий synthesis consumer. Только cloned DB/character/zone; exact snapshot/restore use_skill_*, synthesis row не менять/не удалять
118 Сырая древесина (Raw Wood) items.json:a_index=118; localized_texts.json:items/name/118; icon 265legacy/items/tex_0001_r00_c08.png harvest reward и housing construction material Existing economy item: enabled collection requirements и legacy NPC drop slots; demo создаёт/расходует additional supply только в isolated clone
110 Олений рог (Deer Horn) items.json:a_index=110; localized_texts.json:items/name/110; icon 257legacy/items/tex_0001_r00_c00.png craft material Existing economy item: enabled collection requirements, legacy NPC drops и baseline craft_materials.id=3,craft_id=6,amount=1; demo не должен менять эти consumers
111 Оленья шкура (Deer Hide) canonical LC profile fixed_grade=1, max_enchantable_grade=-1, gradable=0; a_flag=27, stack cap a_weight=9999; immutable predecessor export json/items.json подтверждает identity/value lineage, но не является generator input; localized_texts.json:items/name/111; icon 258legacy/items/tex_0001_r00_c01.png craft result и corpse drop Existing economy item: enabled collection requirements и legacy NPC drop slots; demo creates an additional craft/corpse supply route only in isolated clone. Initial grade равен 1, поэтому explicit corpse loot обязан иметь grade_id=1
1690 ТЕСТ (Werewolf) t_npc.json:a_index=1690, enabled level 13, Default.xml, no NPC skills; localized_texts.json:npcs/name/1690; model Data\\Item\\qqqq\\test.smc; zero t_npc_regen*.json rows keyed to NPC 1690 NPC interaction и corpse route Явный test NPC с legacy drop slots, но не world-reachable из export. Требуется approved isolated runtime/admin spawn fixture; snapshot/restore exact npc_interaction_set_id; prototype/drop rows не менять/не удалять
1683 Мастерская (Workshop) t_npc.json:a_index=1683, enabled, Default.xml; crafts.json:craft_npc=1683 recipes 1..6; eleven enabled t_item_exchange.a_npc_index=1683; t_npc_regen.json:a_index=3677507 in zone 0, layer 0, (584,109.37000274658203,767.3499755859375); client UIQuestNew.cpp special-cases NPC 1683 for event-exchange caption immediate NPC craft context Reachable shared workshop/event-exchange NPC. Все шесть baseline recipes имеют delay=1000 и остаются отдельным legacy content; guide prototype не меняет, demo recipe разрешён только в cloned isolated projection/zone, не на live spawn
491 Восход (Sunrise) → временно Осмотреть труп / Inspect corpse skills.json:a_index=491: a_base_class=999, show=0, a_targetType=2, icon 5127; localized_texts.json:skills/name+description+tooltip/491; two empty-effect/NULL-animation visual rows weapon_index=0/1; icons.json:5127legacy/skills/tex_0001_r03_c02.png; exact identity также есть в Server/wearables.sql corpse Inspect, action 930005 Export/source consumer audit ниже даёт zero gameplay consumers. Только disposable cloned client/server artifacts: server metadata временно 39/2/0, client target/name/description/tooltip временно corpse-specific; icon 5127 сохраняется. Exact predecessor restore обязателен
492 Недавний восход (Ready for Sunrise) → временно Забрать добычу / Take loot skills.json:a_index=492: a_base_class=999, show=0, a_targetType=0, predecessor icon 5127; localized_texts.json:skills/name+description+tooltip/492; two empty-effect/NULL-animation visual rows weapon_index=0/1; exact identity также есть в Server/wearables.sql. Для demo client skill reference меняется на existing icon 5126legacy/skills/tex_0001_r03_c01.png; global icons row не меняется corpse Take, action 930006 Export/source consumer audit ниже даёт zero gameplay consumers. Только disposable clone: server metadata временно 39/2/0, client target/icon/localization временно corpse-specific; exact predecessor a_targetType=0, icon_id=5127 и все тексты восстанавливаются
504 Зелье ускорения ([Cash] Haste Potion) → временно Store / Магазин a_base_class=999, show=0, a_targetType=0, predecessor icon 4905; exact name/description/tooltip и две empty-effect/NULL-animation visual rows 0/1 global npc_store Reviewed zero gameplay-binding closure; cloned target/icon/text repurpose with exact restore
505 Атака и защита (Power Potion) → временно Storage / Склад То же prototype/visual closure; predecessor icon 4905 global npc_storage Reviewed zero gameplay-binding closure; cloned target/icon/text repurpose with exact restore
506 Зелье силы ([Cash] Strength Potion) → временно Auction / Аукцион То же prototype/visual closure; predecessor icon 5114 global npc_auction Reviewed zero gameplay-binding closure; cloned target/icon/text repurpose with exact restore
507 Зелье ловкости ([Cash] Dex Potion) → временно Create guild / Создать гильдию То же prototype/visual closure; predecessor icon 5115 global npc_guild_create Reviewed zero gameplay-binding closure; cloned target/icon/text repurpose with exact restore

В LC export нет real housing blueprint/completion relation: world_building_templates, world_building_stages, world_building_stage_materials, world_build_category_rules, world_building_cultivation_areas и world_housing_cultivation_rules пусты. Поэтому 11378 выбран не как доказанный housing item, а как ближайший real drawing-shaped prototype по явному запросу real ID. Все drawings 11378..11391 запускают enabled quests 837..850 через a_start_type=1/a_start_data=<item>; 11378 не имеет меньшего production risk и потому строго isolated-test-only. Семантически близкий completion material 11435 отвергнут: enabled exchange 276 создаёт его из 11434, а exchange 278 расходует его у reachable NPC 1683.

Для workbench exact-key audit 11539 нашёл только presentation/model closure и t_synthesis_items.id=1; zero rows в craft_materials, craft_products, quest item fields, item_weapons/item_armors/item_accessories/holdables/wearables, NPC drops и semantic source literals. Поэтому он меньше production tools closure, но не consumer-free. Sibling 11540 отвергнут из-за того же synthesis route с большим synthesis_exp_add=600000; 11608 отвергнут как localized closed-testing title item (type/subtype=2/11, a_num_0=205), а не disposable placement material. Production tools {193..196,252..265} отвергнуты: каждый имеет item_weapons.item_id equipment binding и обслуживает legacy production path.

Low-level set 110/111/114/115/117/118 также не consumer-free: export содержит enabled t_item_collection requirements и legacy t_npc.a_item_* drop slots для этих IDs; 110 дополнительно уже расходуется craft_materials.id=3 в baseline recipe 6. Demo не UPDATE/DELETE эти relations, но новые reward/consumption routes меняют economy, поэтому весь set разрешён только в disposable cloned candidate с отдельными inventories. Для 876 exact item-key relations вне presentation/model closure не найдены, однако это не доказывает отсутствие существующих stacks или runtime/event issuance.

Target-once audit нашёл enabled exact 2/10 prototypes {2382,2848,5068,6360..6366,11319}. 2382 имеет affinity/luckydraw consumers, 2848 — luckydraw, 6361/6362/6365/6366 — collection/luckydraw, 11319 — luckydraw; 5068 и skill 996 имеют пустые en_us/ru_ru names. У 6360/6363/6364 exported gameplay consumer rows не найдены; выбран 6363 с полной localization/icon/model closure и exact baseline SQL row. Это не доказывает отсутствие уже выданных stacks или внешнего event issuance, поэтому fixture всё равно запрещён вне disposable clone.

Original associated skill 1161 также полностью существует в baseline export: a_base_class=999, show=0, canonical a_targetType=2 (TargetOne), legacy a_appRange/a_fireRange/a_fireRange2=5, icon 4905, localized name Немота (Silence), description/tooltip и ровно две NULL-animation visual rows weapon_index=0/1. Он unsuitable для Collect именно из-за unsupported legacy TargetOne; guide его row не UPDATE/DELETE. Temporary skill 84 имеет a_targetType=0 (Self), тот же existing icon 4905, localization и две visual rows; §7 изменяет только его reviewed WI extension tuple и связывает его с patched 6363.a_num_0/use_skill_id.

Server/wearables.sql подтверждает существование перечисленных item/skill/NPC identities, но не является доказательством полного byte-for-byte равенства export rows. В частности, для items 114 и 117 поле a_flag различается (export json: 0; SQL literal: 4294967323). Guide это поле не меняет и не использует; source owner обязан разрешить расхождение отдельно, а predecessor manifest всегда снимается с фактической candidate DB. Ни одно утверждение ниже не полагается на assumed full-row equality.

Skills 84..93 — baseline intersection, а не новые test skills. В skills.json у них a_base_class=999, show=0, a_targetType=0, icon 4905; skill_cast_client_visuals.json уже содержит для каждого exact rows weapon_index=0/1 с NULL animation IDs; icons.json:id=4905 задаёт legacy/skills/tex_0000_r00_c00.png. Имена существующие и намеренно не переименовываются этим demo:

ID Русское имя Demo role Риск
84 Незначительное мгновенное восстановление здоровья isolated direct item Collect Existing hidden technical skill; item 6363 временно переключается с original skill 1161 на 84, только с exact restore
85 Среднее мгновенное восстановление здоровья NPC Collect То же
86 Значительное мгновенное восстановление здоровья doodad harvest Target metadata будет временно изменена на Doodad
87 Малое мгновенное восстановление маны seed placement Existing item-skill identity
88 Незначительное мгновенное восстановление маны immediate NPC craft metadata Existing item-skill identity
89 Среднее мгновенное восстановление маны workbench action/craft metadata Target metadata будет временно изменена на Doodad
90 Значительное мгновенное восстановление маны housing blueprint placement Housing остаётся blocked
91 Малое мгновенное восстановление здоровья и маны housing build contribution Target metadata будет временно изменена на Doodad; housing blocked
92 Незначительное мгновенное восстановление здоровья workbench placement Real item 11539; весь branch isolated-test-only из-за synthesis consumer
93 Среднее мгновенное восстановление здоровья и маны isolated structural plot Template-only

Эти skill names не соответствуют будущему production UX. Поэтому set 84..93 допускается только для disposable isolated technical candidate с exact predecessor tuple; canonical production projection должна выбрать owner-approved dedicated skills/localization. WI-PROTOTYPE-02 consumer audit не нашёл gameplay closure: zero skill_effects.skill_id, zero skill_tree.col_0..7, zero NPC a_skill0..3, zero quest skill reward, zero craft/AI/item binding и zero semantic hardcoded skill-ID comparison в GameServer/client Engine/EntitiesMP *.es. Две visual rows на каждый skill (weapon_index=0/1) и localization являются presentation closure. Совпадающие literals 84..93 в source относятся к item IDs, chances, localization keys и unrelated enums, а quest a_start_data/condition literals — к NPC/item semantics по их type fields. Поэтому IDs приняты без замены, но только для cloned isolated candidate: отсутствие exported consumers не является разрешением превращать hidden legacy identities в production content.

Corpse action skills намеренно выбраны вне 84..93: все десять уже заняты другими guide scenarios и имеют несовместимую metadata, поэтому их cumulative closure не равна zero. Для real technical skills 491/492 повторный exact audit нашёл zero skill_effects.skill_id, zero skill_tree.col_0..7, zero parsed NPC a_skill0..3, zero base_skill_id, quest/craft/tagged/WI skill relation и zero semantic skill-ID comparison/Create в GameServer, client Engine и primary *.es. Numeric source literals 491/492 относятся к NPC prototype checks, localization/UI coordinates; единственный semantic m_skillProtoList.Create(493,1) доказывает, почему соседний 493 отвергнут. Raw items.a_num_0 collisions не являются skill links: для 491 это ровно disabled armor 10559/10560, для 492 — ровно disabled armor 8434/10591/10592/10598/10695/10696/10702; все имеют a_type_idx=1, тогда как item-use skill paths отсутствуют. Export visual closure — exact two rows weapon_index=0/1, empty effects and NULL animation IDs for each skill. Это evidence только для isolated clone, не production ownership.

Global NPC service manifest §7.4 добавляет real NPC prototypes {20,33,89,149,899} и использует отдельные reviewed real skills {504,505,506,507}. Skills 84..93 уже заняты cumulative guide roles и для services не переиспользуются. Для 504..507 export/source audit подтвердил a_base_class=999, show=0, a_targetType=0, zero skill_effects, zero skill-tree/parsed NPC/base-skill/WI/craft bindings, zero enabled once-use items.a_num_0 binding и zero semantic hardcoded skill-ID use; existing non-once item numeric collisions не являются skill links. Client closure содержит exact две empty-effect/NULL-animation visual rows weapon_index=0/1; predecessor icons равны 504/505→4905, 506→5114, 507→5115. NPC evidence: shoppers 20/33 имеют t_shop spawn rows; keeper 149, guild NPC 89 и auction candidate 899 имеют exact t_npc_regen rows. Их prototypes, flags, shop/regen rows, global icons 5123..5126 и visual rows не вставляются и не удаляются; меняются только guarded skill presentation/target/localization fields и nullable NPC set binding с exact restore. Inserted WI-owned set/action IDs {907010..907014,907110..907114} проходят collision preflight и не являются LC prototype IDs.

6.2 Оставшиеся non-prototype placeholders

ID Placeholder purpose Конкретный blocker
900200 LC zone (t_zonedata.a_zone_index) Нет approved configured local zone/map package
900201 LC area index Зависит от выбранной zone и её a_areacount
900202 LC layer Зависит от выбранной zone и её a_ylayer

Внешних item/NPC/skill prototype placeholders больше нет. 900200..900202 — только unresolved map scope, а не item/NPC IDs. Используемые model IDs {950001,950002,950003,950010,950020,950030,950031}, polygon coordinates и model paths остаются именованными demo placeholders до accepted content packages. Guide больше не вставляет synthetic icon IDs: реальные prototype icons перечислены выше. PostgreSQL character 900500 — отдельный diagnostic placeholder, а не static content ID.

Дополнительные demo identities: 912011 — CraftStart payload, 914011 — его doodad action, 920012 — Start→recipe membership, 915100 — authored static spawn, 970001 — plot, 970002 — root event, 970003 — только reserved optional plot_effects.id (сам structural demo его не вставляет). Все они проходят тот же collision review.

Имена non-ID placeholders для manifest: DEMO_ZONE_ID=900200, DEMO_AREA_ID=900201, DEMO_LAYER_ID=900202; DEMO_HOUSING_POLYGON_V1..V4=(100,100),(140,100),(140,140),(100,140); DEMO_STATIC_SPAWN_X/Y/Z/ROTATION=(120,0,120,0); DEMO_TEST_CHARACTER_ID=900500; DEMO_MODEL_PATH_* — строки §12.1. Эти labels не являются SQL variables: при замене значение меняется согласованно во всех authoring, diagnostics и cleanup predicates.

Перед authoring проверьте collisions и prerequisites. В examples используются только explicit column lists, без wildcard selects:

-- Server projection after 0001: capture all three mutable C0 fields.
SELECT a_index, a_type_idx, a_subtype_idx, a_num_0, a_num_1,
       a_enable, a_job_flag, a_flag, a_wearing,
       use_skill_id, use_skill_as_reagent, icon_id
FROM items
WHERE a_index IN (110,111,114,115,117,118,876,6363,11378,11539)
ORDER BY a_index;

-- Client compact projection: WI-only use_skill columns are absent by design.
SELECT a_index, a_type_idx, a_subtype_idx, a_num_0, a_num_1,
       a_enable, a_job_flag, a_flag, a_wearing, icon_id
FROM items
WHERE a_index IN (110,111,114,115,117,118,876,6363,11378,11539)
ORDER BY a_index;

-- Server projection skill tuple after 0001.
SELECT a_index, a_base_class, icon_id,
       show,
       plot_id, consume_lp, actability_group_id, category_id, timing_id,
       a_targetType, target_selection_id, target_relation_id,
       min_range, max_range, casting_time, cooldown_time, channeling_time,
       channeling_tick, channeling_mana, target_dead, target_alive,
       check_terrain, target_water, target_only_water, target_my_npc,
       or_unit_reqs, mainhand_tool_id, offhand_tool_id
FROM skills
WHERE a_index BETWEEN 84 AND 93 OR a_index IN (491,492,504,505,506,507)
ORDER BY a_index;

-- Client projection contains canonical a_targetType, but not the 0001 tuple.
SELECT a_index, a_base_class, show, a_targetType, icon_id
FROM skills
WHERE a_index BETWEEN 84 AND 93 OR a_index IN (491,492,504,505,506,507)
ORDER BY a_index;

SELECT a_index, a_enable, npc_interaction_set_id
FROM t_npc
WHERE a_index IN (1683,1690)
ORDER BY a_index;

-- Original item-associated skill: immutable evidence only, never updated by this demo.
SELECT a_index, a_base_class, show, a_targetType,
       a_appRange, a_fireRange, a_fireRange2, icon_id
FROM skills
WHERE a_index=1161;

SELECT skill_id, weapon_index, ready_ani_id, still_ani_id, fire_ani_id
FROM skill_cast_client_visuals
WHERE skill_id IN (84,491,492,1161)
ORDER BY skill_id, weapon_index;

-- Corpse skills: raw numeric item collisions must remain disabled armor data,
-- not item-use skill bindings. Exact rows/counts are immutable audit evidence.
SELECT a_index, a_type_idx, a_subtype_idx, a_enable, a_num_0
FROM items
WHERE a_num_0 IN (491,492)
ORDER BY a_num_0, a_index;

SELECT tbl_name, tbl_column_name, idx, en_us, ru_ru
FROM localized_texts
WHERE tbl_name='skills' AND idx IN (491,492)
  AND tbl_column_name IN ('name','description','tooltip')
ORDER BY idx, tbl_column_name;

-- Existing consumers of the two isolated-only replacement items must remain exact.
SELECT a_index, a_enable, a_start_type, a_start_data
FROM t_quest
WHERE a_index=837 AND a_enable=1 AND a_start_type=1 AND a_start_data=11378;

SELECT id, type, item_id, grade, synthesis_exp_add, is_pickup
FROM t_synthesis_items
WHERE id=1 AND type=1 AND item_id=11539 AND grade=1
  AND synthesis_exp_add=30000 AND is_pickup=1;

SELECT a_zone_index, a_areacount, a_ylayer
FROM t_zonedata
WHERE a_zone_index = 900200;

Результаты server item, client item, skill и NPC prototype queries должны быть сохранены verbatim в immutable predecessor manifest до первого UPDATE. Для 6363 обе schema-specific manifest rows обязательно сохраняют actual a_num_0=1161; server row дополнительно сохраняет actual use_skill_id/use_skill_as_reagent, которых в client schema нет. Cleanup принимает только exact values через bound :prev_*, и их нельзя заменять assumed defaults. Каждый prototype UPDATE обязан иметь полный predecessor predicate по изменяемому tuple, немедленный changes()/driver affected-row check и затронуть ровно одну row; skill template выполняется отдельно для каждого ID 84..93, также с expected affected rows 1. Иное число означает drift и требует ROLLBACK, а не продолжения INSERTs. Если real row отсутствует либо tuple отличается от evidence выше, остановитесь и выпустите новый reviewed manifest. Для 11378 manifest дополнительно pin-ит unchanged quest relation t_quest(837).a_start_type=1,a_start_data=11378; для 11539 — unchanged synthesis relation t_synthesis_items(1).item_id=11539,grade=1,synthesis_exp_add=30000,is_pickup=1. Skills 491/492 имеют отдельный corpse manifest: server сохраняет exact previous a_targetType/target_selection_id/target_relation_id; client сохраняет previous a_targetType/icon_id и exact en_us/ru_ru каждой name/description/tooltip row. Для 84..93 должны сохраняться a_base_class=999, show=0, icon_id=4905; для 491/492a_base_class=999, show=0, predecessor icon_id=5127. Для каждой row в final client DB обязательны обе существующие skill_cast_client_visuals rows weapon_index=0/1, которые guide не INSERT/UPDATE/DELETE. Global icons rows никогда не UPDATE/DELETE. Между export и Server/wearables.sql доказано совпадение identities и используемой presentation/target metadata, но не полное равенство всех нетронутых полей; AA→LC identity mapping допускается только из отдельного reviewed manifest, а не из равенства integer ID.

Service predecessor manifest отдельно сохраняет server target/range tuple и client a_targetType/icon_id плюс exact 12 localization rows для 504..507. Exact predecessor icons: 504=4905, 505=4905, 506=5114, 507=5115; guessed restore запрещён.

6.3 Placeholder checklist перед копированием SQL

Заменить и записать в manifest нужно всё следующее:

Ни один numeric/path/coordinate placeholder не имеет default production meaning.

6.4 Матрица demo → writes → cleanup → prerequisites

Writes перечисляет только tables данного guide, не owner-created LC prototype/localization rows. Cleanup означает exact IDs/predicates §15 плюс восстановление изменённых prototype columns из manifest.

Demo Класс Writes (insert/update) Reverse cleanup Обязательные prerequisites/blockers
Foundation item/NPC Collect NPC subset runtime-supported; direct 6363→84 runnable only in isolated cloned projection wi_groups, wi_details, wi_group_wis, skills, items.a_num_0/use_skill_*, skill_products, skill_reagents, interaction_effects, effects, skill_effects, npc_interaction_sets, npc_interactions, t_npc NPC binding/actions → skill/effect payload → products/reagents → WI mappings/details → exact item/skill prototype restore Complete 0001 + forward parent closure; matching client/server cloned item row; self-target through player portrait; exact predecessor/restore; 0020/0021; never live/shared item stacks
Seed/workbench doodads Runtime-supported placement/harvest subset; InsideGarden seed blocked without housing doodad_groups, doodad_almighties, doodad_func_groups, Growth/Timer/LootItem payloads, doodad_phase_funcs, doodad_funcs, items, item_spawn_doodads item bindings/actions/functions → payloads → phases/templates/group → item restore Complete 0002/0003; Foundation WI 902003; exact model and map/placement package; 0020..0023/0026
Immediate craft + Pack Runtime-supported immediate subset, template до matching server/client projection craft_c_categories, skills, crafts, craft_materials, craft_products, craft_packs, craft_pack_crafts, doodad_func_craft_packs, doodad_funcs action → membership/payload → product/material → recipe/pack/category → skill restore Complete 0004 and doodad phase 911010; Craft WI 902004; matching client catalog; delay=0
CraftStart alternative Runtime-supported alternative delta doodad_func_craft_starts, doodad_func_craft_start_crafts, doodad_funcs action → membership → payload Base craft recipe 920002; choose intentionally instead of Pack duplicate UX; matching client rows
Corpse loot Runtime-supported memory-only subset server skills 491/492, foundation skill_constants, corpse_action_types, corpse_loot_skill_overrides, loot_packs, loot_groups, loots, loot_pack_dropping_npcs; client skills/localized_texts плюс compact mirrored skill_constants, corpse_action_types route → loot → synthetic group → pack; global actions/constants → exact server/client skill/localization/registry restore Complete server 0001+0003 и client client_0002; exact matching constants/actions, enabled NPC/item and valid item grade; two local visual rows and shipped icons; durable corpse PostgreSQL schema отсутствует
Housing/build/garden Template-only, rollout blocked housing sizes/taxation/design/build/group/area/polygon/item binding; skill_reagents; items item binding/geometry/area/group/build/reagent → design/taxation/sizes → item/skill restore Complete 0006 all gates, §11 blockers, accepted geometry/model/map, fresh 0020..0026
Authored static Gather Loader/runtime shape, package blocked lc_doodad_spawns exact spawn row only §8 graph; accepted map transform/model package; reconciliation approval
Isolated skill plot Structural template-only/no-op plots, plot_events, skills unlink skill → optional exact child → event → plot → remaining skill tuple restore Complete 0005; separate skill 93; no InteractionEffect; concrete gameplay payload intentionally absent
Client models/icons Models template-only; prototype icons reused as-is lc_model_resources; no icon/prototype-icon writes exact model rows only Final client schema, exact sparse model set/assets, existing icon PNGs/localization and two skill_cast_client_visuals rows per loaded skill

7. Foundation demo: item → Collect и NPC action

Класс примера: NPC Collect — runtime-open template после canonical projection, PostgreSQL 0020/0021, isolated canary и client/server prototype closure. Direct item Collect теперь использует real enabled target-once item 6363: его exact LC type/subtype 2/10 уже входит в client ITEM_ONCEUSE/ITEM_SUB_TARGET и server ITYPE_ONCE/IONCE_TARGET dispatch. Original a_num_0=1161 указывает на combat skill с canonical a_targetType=2 (TargetOne), который WI Collect не принимает. Поэтому runnable isolated fixture образует один compatibility set из двух отдельно транзакционных cloned artifacts: client меняет только свой реально существующий dispatch field a_num_0 на 84; server меняет a_num_0 и WI-only binding use_skill_id/use_skill_as_reagent на 84/1. Cross-database atomic transaction здесь не предполагается: traffic открывается только после успешной подготовки обоих artifacts, а teardown восстанавливает оба predecessor artifacts. Это не production repurpose предмета.

7.1 Static SQLite authoring

Скрипт предполагает, что 0001 и требуемая forward-FK parent schema closure из §4 уже находятся в отдельной offline candidate-копии, а перечисленные LC prototypes существуют. Это не разрешение устанавливать пустые optional packages в release artifact.

До блока existing rows wi_groups.id IN (1,2,3) должны быть либо отсутствующими, либо иметь exact pairs 1/Craft, 2/Collect, 3/Building. INSERT OR IGNORE ниже нужен только для shared fixed identities и не разрешает silently принимать другое name. Все остальные inserted primary/unique IDs, включая wi_id=902001..902004, должны отсутствовать по exact table/key collision review.

Skill prototype updates выполняются одним parameterized template по одной row. Desired values, отличающиеся между IDs:

Skill a_targetType target_selection_id consume_lp max_range Роль
84 0 1 0 0.0 direct Collect
85 0 1 0 0.0 NPC Collect (target остаётся source character)
86 20 2 0 8.0 doodad harvest
87 0 1 0 8.0 seed placement
88 0 1 5 0.0 immediate NPC craft metadata
89 20 2 5 8.0 workbench action/craft metadata
90 0 1 0 8.0 housing blueprint placement
91 20 2 1 8.0 housing contribution
92 0 1 0 8.0 workbench placement

Для всех девяти server rows desired plot_id=NULL, actability_group_id=category_id=timing_id=0, target_relation_id=0, min_range=0.0, все timing/water/dead/my-NPC/requirement flags — 0, кроме target_alive=1; tool IDs — NULL. Каждому вызову server template передаются exact :prev_* из одной immutable manifest row и desired :demo_* из таблицы выше; сразу после каждого вызова driver/SELECT changes() обязан подтвердить ровно 1. Client baseline не содержит этот WI extension tuple; там сохраняется/проверяется только существующий skills.a_targetType (для C0 skill 84 он уже canonical 0) и presentation/visual closure.

PRAGMA foreign_keys = ON;
BEGIN IMMEDIATE;

INSERT OR IGNORE INTO wi_groups (id, name) VALUES
    (1, 'Craft'),
    (2, 'Collect'),
    (3, 'Building');

INSERT INTO wi_details (id, wi_id, lp, apply_expert, distance_sqrt) VALUES
    (902101, 902001, 1, 0, 0.0),
    (902102, 902002, 1, 0, 0.0),
    (902103, 902003, 1, 0, 0.0),
    (902104, 902004, 1, 0, 0.0);

INSERT INTO wi_group_wis (id, wi_group_id, wi_id) VALUES
    (902201, 2, 902001),
    (902202, 2, 902002),
    (902203, 2, 902003),
    (902204, 1, 902004);

-- Execute exactly once for each skill 84..92 with the reviewed bindings above.
UPDATE skills SET
    plot_id=:demo_plot_id,
    consume_lp=:demo_consume_lp,
    actability_group_id=:demo_actability_group_id,
    category_id=:demo_category_id,
    timing_id=:demo_timing_id,
    a_targetType=:demo_a_targetType,
    target_selection_id=:demo_target_selection_id,
    target_relation_id=:demo_target_relation_id,
    min_range=:demo_min_range,
    max_range=:demo_max_range,
    casting_time=:demo_casting_time,
    cooldown_time=:demo_cooldown_time,
    channeling_time=:demo_channeling_time,
    channeling_tick=:demo_channeling_tick,
    channeling_mana=:demo_channeling_mana,
    target_dead=:demo_target_dead,
    target_alive=:demo_target_alive,
    check_terrain=:demo_check_terrain,
    target_water=:demo_target_water,
    target_only_water=:demo_target_only_water,
    target_my_npc=:demo_target_my_npc,
    or_unit_reqs=:demo_or_unit_reqs,
    mainhand_tool_id=:demo_mainhand_tool_id,
    offhand_tool_id=:demo_offhand_tool_id
WHERE a_index=:skill_id
  AND a_base_class=999 AND show=0 AND icon_id=4905
  AND plot_id IS :prev_plot_id AND consume_lp IS :prev_consume_lp
  AND actability_group_id IS :prev_actability_group_id AND category_id IS :prev_category_id
  AND timing_id IS :prev_timing_id AND a_targetType IS :prev_a_targetType
  AND target_selection_id IS :prev_target_selection_id AND target_relation_id IS :prev_target_relation_id
  AND min_range IS :prev_min_range AND max_range IS :prev_max_range
  AND casting_time IS :prev_casting_time AND cooldown_time IS :prev_cooldown_time
  AND channeling_time IS :prev_channeling_time AND channeling_tick IS :prev_channeling_tick
  AND channeling_mana IS :prev_channeling_mana AND target_dead IS :prev_target_dead
  AND target_alive IS :prev_target_alive AND check_terrain IS :prev_check_terrain
  AND target_water IS :prev_target_water AND target_only_water IS :prev_target_only_water
  AND target_my_npc IS :prev_target_my_npc AND or_unit_reqs IS :prev_or_unit_reqs
  AND mainhand_tool_id IS :prev_mainhand_tool_id AND offhand_tool_id IS :prev_offhand_tool_id;
SELECT changes() AS affected_server_skill_update; -- require exactly 1 before the next statement

-- Server half of isolated-only runnable 6363→84 fixture.
UPDATE items
SET a_num_0 = 84,
    use_skill_id = 84,
    use_skill_as_reagent = 1
WHERE a_index = 6363
  AND a_type_idx=2 AND a_subtype_idx=10 AND a_num_0=:prev_item_6363_num0
  AND :prev_item_6363_num0=1161
  AND a_num_1=1 AND a_enable=1 AND a_job_flag=511
  AND a_flag=16778257 AND a_wearing=-1 AND icon_id=2657
  AND use_skill_id IS :prev_item_6363_use_skill
  AND use_skill_as_reagent IS :prev_item_6363_use_reagent;
SELECT changes() AS affected_server_item_6363_update; -- require exactly 1

INSERT INTO skill_products (id, skill_id, item_id, amount, enable) VALUES
    (906001, 84, 117, 1, 1),
    (906002, 85, 115, 1, 1);

-- NPC action has no source item, therefore it needs an explicit reagent.
INSERT INTO skill_reagents (id, skill_id, item_id, amount, enable) VALUES
    (906101, 85, 114, 2, 1);

INSERT INTO interaction_effects (id, wi_id, doodad_id, source_direction) VALUES
    (903001, 902001, NULL, 0),
    (903002, 902002, NULL, 0);

INSERT INTO effects (id, actual_type, actual_id, description) VALUES
    (904001, 'InteractionEffect', 903001, 'WI demo: item Collect'),
    (904002, 'InteractionEffect', 903002, 'WI demo: NPC Collect');

INSERT INTO skill_effects (
    id, skill_id, effect_id, always_hit, application_method, chance,
    consume_item_count, consume_item_id, consume_source_item,
    start_level, end_level, friendly, non_friendly,
    source_buff_tag, source_nobuff_tag, target_buff_tag, target_nobuff_tag, target_npc_tag,
    weight, front, back, interaction_success_hit, enable
) VALUES
    (905001, 84, 904001, 1, 1, 100, 0, 0, 0, 1, 55, 1, 1, '', '', '', '', '', 1, 0, 0, 1, 1),
    (905002, 85, 904002, 1, 1, 100, 0, 0, 0, 1, 55, 1, 1, '', '', '', '', '', 1, 0, 0, 1, 1);

INSERT INTO npc_interaction_sets (id, name)
VALUES (907001, 'WI demo NPC Collect');

INSERT INTO npc_interactions (id, npc_interaction_set_id, skill_id)
VALUES (907101, 907001, 85);

UPDATE t_npc
SET npc_interaction_set_id = 907001
WHERE a_index = 1690
  AND a_enable=1
  AND npc_interaction_set_id IS :prev_npc_1690_interaction_set;
SELECT changes() AS affected_server_npc_1690_update; -- require exactly 1

COMMIT;

Client compact DB не получает 0001 и не имеет items.use_skill_id/use_skill_as_reagent. Для C0 его complete symmetric half — только следующий a_num_0 override; добавлять отсутствующие WI-only columns в client schema запрещено:

BEGIN IMMEDIATE;

UPDATE items
SET a_num_0 = 84
WHERE a_index = 6363
  AND a_type_idx=2 AND a_subtype_idx=10
  AND a_num_0=:prev_client_item_6363_num0
  AND :prev_client_item_6363_num0=1161
  AND a_num_1=1 AND a_enable=1 AND a_job_flag=511
  AND a_flag=16778257 AND a_wearing=-1 AND icon_id=2657;
SELECT changes() AS affected_client_item_6363_update; -- require exactly 1

-- Immutable prerequisites, not writes: skill 84 has a_targetType=0,
-- icon_id=4905 and exact visual rows weapon_index 0/1.
COMMIT;

Для cumulative client projection с doodad/build actions из таблицы §7.1 отдельно mirror-ится только реально существующий canonical skills.a_targetType. Baseline skills 84..93 имеют a_targetType=0, поэтому client UPDATE нужен только для desired Doodad skills {86,89,91} и выполняется по одной row:

BEGIN IMMEDIATE;

-- Execute once for skill_id 86, 89 and 91 only; :demo_a_targetType=20.
UPDATE skills
SET a_targetType=:demo_a_targetType
WHERE a_index=:skill_id
  AND a_base_class=999 AND show=0 AND icon_id=4905
  AND a_targetType IS :prev_client_a_targetType
  AND :prev_client_a_targetType=0;
SELECT changes() AS affected_client_skill_target_update; -- require exactly 1 each

COMMIT;

Остальной WI skill extension tuple существует и изменяется только на server. Skill 84 для C0 уже имеет одинаковый canonical Self=0 в baseline client/server prototypes; client preflight проверяет его, но не делает no-op UPDATE.

Все соответствующие server/client transactions выполняются offline. Tool обязан не делать COMMIT, если любой affected-row result не равен 1; если один artifact уже записан, compatibility set не публикуется и восстанавливается из immutable predecessor.

7.2 Runtime conditions

Direct Collect допускается только если:

Для exact 6363 type/subtype уже правильные и не меняются. Runnable test sequence: на isolated character получить отдельный test stack 6363, левой кнопкой выбрать собственный portrait (CSimplePlayerInfoUI::OnLButtonDown() вызывает SetTargetMe()), затем использовать item. Client читает patched a_num_0=84, проходит delay/range с self distance 0, отправляет свой MSG_CHAR_PC/index; server повторно разрешает inventory slot, exact 2/10, equality a_num_0=use_skill_id=84, target Self/Source и authoritative Collect plan. Выбор чужого target корректно даст TargetMismatch. Forged packet не является тестом.

Override 6363.a_num_0 1161→84 допустим только в disposable cloned client/server compatibility set и должен затронуть ровно одну row в каждой отдельной projection transaction. Server дополнительно и тем же guarded способом меняет use_skill_id/use_skill_as_reagent; client этих columns не имеет. a_type_idx, a_subtype_idx, a_num_1, icon/localization, original skill 1161 и все presentation rows не меняются. После C0 остановите consumers и выполните conditional restore §15; при любом drift — ROLLBACK/whole-artifact restore. Existing combat description предмета вводит в заблуждение и делает fixture непригодным для player-facing/live canary, но не блокирует isolated execution.

NPC action дополнительно требует текущий runtime NPC, тот же area/layer, distance не больше 8 м, exact NPC prototype → set → action binding. UI получает actionId, skillId, availability; name/icon берутся из client local skill data.

1690 не имеет keyed row ни в t_npc_regen.json, ни в regen combo/cube/raid exports. Поэтому §7 NPC action и §10 corpse route не появятся от одних static rows. Обязательный prerequisite — уже существующий, отдельно reviewed admin/runtime spawn fixture, который создаёт t_npc.a_index=1690 только в закрытой cloned test zone/area и допускает его controlled death; guide не добавляет guessed regen SQL или spawn command. После teardown восстанавливается весь cloned zone artifact/runtime state. Использовать production monster вместо 1690 ради reachability запрещено: corpse mapping тогда изменит loot path всех его live instances.

7.3 Target, requirements, product и presentation contract

7.4 Global NPC services: Store / Storage / Auction / GuildCreate

Это disposable cloned prototype, совместимый с cumulative guide: skills 84..93 остаются закреплены только за ролями §6.1 и не переиспользуются. Services временно repurpose reviewed real skills 504..507 в остановленных cloned server/client SQLite artifacts и затем exact-но восстанавливают их target/icon/localization predecessors.

Constant Skill Client icon Временное имя en_us / ru_ru NPC binding
npc_store 504 5123legacy/skills/tex_0001_r02_c14.png Store / Магазин 20 и 33, два разных sets
npc_storage 505 5124legacy/skills/tex_0001_r02_c15.png Storage / Склад 149
npc_auction 506 5125legacy/skills/tex_0001_r03_c00.png Auction / Аукцион reviewed candidate 899
npc_guild_create 507 5126legacy/skills/tex_0001_r03_c01.png Create guild / Создать гильдию 89

Все server skills получают exact GeneralUnit(31)/Target(2)/Any(0) и range 0..8m. Client получает a_targetType=31, distinct existing icon references и service localization; существующие две visual rows weapon_index=0/1 с empty effects и NULL animation IDs не изменяются. icons, prototypes и skill_cast_client_visuals не INSERT/DELETE; icon/visual rows вообще не UPDATE.

NPC evidence. 20 (Merchant Geres) и 33 (Weapon Trader Baran) enabled, имеют a_flag=33554705 с NPC_SHOPPER и реальные t_shop rows zone 0, layer 0: (629.75,109.958,743.75) и (618.75,109.964,743.0). Area.cpp создаёт runtime NPC из t_shop, отдельный regen им не нужен. 149 (Storekeeper Bianca) имеет NPC_KEEPER в a_flag=33816848 и regen 3663343, zone 0, layer 0, (593,109.966,739.75). 89 (Royal Guard Captain) имеет NPC_GUILD в a_flag=34144528 и regen 3663187, zone 0, layer 0, (538,109.971,729.5). 899 (Trading Agent Siban) имеет regen 3663842, zone 0, layer 0, (495,109.977,892.5). Auction не имеет отдельного authoritative a_flag readiness gate: exported a_flag1=1 — legacy NPC1_TRADEAGENT interaction marker, но service authorization опирается на authored typed binding и live SubHelper. Поэтому marker не является authority и случайный NPC по нему не назначается.

7.4.1 Server cloned SQLite

Complete constants schema также требует уже authored exact corpse pair §10. До write сохраните оба predecessor result sets verbatim во внешний immutable manifest. Collision/consumer queries ниже должны вернуть zero rows; role/spawn queries — exact evidence rows. Package-specific consumers (doodad_funcs, crafts, housing_build_steps) также должны быть zero, если соответствующие optional tables установлены.

PRAGMA foreign_keys = ON;

SELECT constant_key,skill_id,enabled FROM skill_constants
WHERE constant_key IN ('corpse_inspect','corpse_take')
ORDER BY constant_key COLLATE BINARY; -- exact enabled prerequisite from §10

SELECT constant_key,skill_id,enabled FROM skill_constants
WHERE constant_key IN ('npc_store','npc_storage','npc_auction','npc_guild_create')
   OR skill_id IN (504,505,506,507); -- zero rows
SELECT id,name FROM npc_interaction_sets
WHERE id IN (907010,907011,907012,907013,907014); -- zero rows
SELECT id,npc_interaction_set_id,skill_id,skill_constant_key FROM npc_interactions
WHERE id IN (907110,907111,907112,907113,907114)
   OR skill_id IN (504,505,506,507)
   OR skill_constant_key IN ('npc_store','npc_storage','npc_auction','npc_guild_create'); -- zero rows
SELECT id,skill_id,effect_id,enable FROM skill_effects
WHERE skill_id IN (504,505,506,507); -- zero rows
SELECT id,skill_id,item_id,amount,enable FROM skill_reagents
WHERE skill_id IN (504,505,506,507); -- zero rows
SELECT id,skill_id,item_id,amount,enable FROM skill_products
WHERE skill_id IN (504,505,506,507); -- zero rows
SELECT id,owner_id,owner_type,kind_id,enable FROM unit_reqs
WHERE owner_type='Skill' AND owner_id IN (504,505,506,507); -- zero rows
SELECT a_index,plot_id FROM skills
WHERE a_index IN (504,505,506,507) AND plot_id IS NOT NULL; -- zero rows
SELECT a_index,a_type_idx,a_subtype_idx,a_num_0,use_skill_id FROM items
WHERE use_skill_id IN (504,505,506,507)
   OR (a_enable=1 AND a_type_idx=2
       AND a_num_0 IN (504,505,506,507)); -- zero rows
SELECT num_row,job,subjob,col_0,col_1,col_2,col_3,col_4,col_5,col_6,col_7
FROM skill_tree
WHERE 504 IN (col_0,col_1,col_2,col_3,col_4,col_5,col_6,col_7)
   OR 505 IN (col_0,col_1,col_2,col_3,col_4,col_5,col_6,col_7)
   OR 506 IN (col_0,col_1,col_2,col_3,col_4,col_5,col_6,col_7)
   OR 507 IN (col_0,col_1,col_2,col_3,col_4,col_5,col_6,col_7); -- zero rows

-- Exact predecessor snapshots: require 4 and 5 rows respectively.
SELECT a_index,a_base_class,show,icon_id,plot_id,consume_lp,
       actability_group_id,category_id,timing_id,a_targetType,
       target_selection_id,target_relation_id,min_range,max_range,
       casting_time,cooldown_time,channeling_time,channeling_tick,channeling_mana,
       target_dead,target_alive,check_terrain,target_water,target_only_water,
       target_my_npc,or_unit_reqs,mainhand_tool_id,offhand_tool_id
FROM skills WHERE a_index IN (504,505,506,507) ORDER BY a_index;
SELECT a_index,a_enable,a_flag,a_flag1,npc_interaction_set_id
FROM t_npc WHERE a_index IN (20,33,89,149,899) ORDER BY a_index;

SELECT a_index,a_enable,a_flag,a_flag1,(a_flag&1) AS shopper,
       (a_flag&262144) AS keeper,(a_flag&524288) AS guild
FROM t_npc WHERE a_index IN (20,33,89,149,899) ORDER BY a_index;
SELECT a_keeper_idx,a_zone_num,a_pos_x,a_pos_h,a_pos_z,a_y_layer
FROM t_shop WHERE a_keeper_idx IN (20,33) ORDER BY a_keeper_idx;
SELECT a_index,a_zone_num,a_npc_idx,a_pos_x,a_pos_h,a_pos_z,a_y_layer
FROM t_npc_regen WHERE a_index IN (3663343,3663842,3663187)
ORDER BY a_index;

Если consumer найден, его нельзя удалять ради demo. Каждый changes() ниже обязан быть 1, иначе ROLLBACK; ID-specific :prev_skill_<id>_* берутся только из соответствующей captured predecessor row.

PRAGMA foreign_keys = ON;
BEGIN IMMEDIATE;

UPDATE skills
SET a_targetType=31,target_selection_id=2,target_relation_id=0,
    min_range=0.0,max_range=8.0
WHERE a_index=504
  AND a_base_class=999 AND show=0 AND icon_id=4905
  AND a_targetType IS :prev_skill_504_a_targetType
  AND target_selection_id IS :prev_skill_504_target_selection_id
  AND target_relation_id IS :prev_skill_504_target_relation_id
  AND min_range IS :prev_skill_504_min_range AND max_range IS :prev_skill_504_max_range;
SELECT changes() AS affected_server_service_skill_504; -- 1
UPDATE skills
SET a_targetType=31,target_selection_id=2,target_relation_id=0,
    min_range=0.0,max_range=8.0
WHERE a_index=505 AND a_base_class=999 AND show=0 AND icon_id=4905
  AND a_targetType IS :prev_skill_505_a_targetType
  AND target_selection_id IS :prev_skill_505_target_selection_id
  AND target_relation_id IS :prev_skill_505_target_relation_id
  AND min_range IS :prev_skill_505_min_range AND max_range IS :prev_skill_505_max_range;
SELECT changes() AS affected_server_service_skill_505; -- 1
UPDATE skills
SET a_targetType=31,target_selection_id=2,target_relation_id=0,
    min_range=0.0,max_range=8.0
WHERE a_index=506 AND a_base_class=999 AND show=0 AND icon_id=5114
  AND a_targetType IS :prev_skill_506_a_targetType
  AND target_selection_id IS :prev_skill_506_target_selection_id
  AND target_relation_id IS :prev_skill_506_target_relation_id
  AND min_range IS :prev_skill_506_min_range AND max_range IS :prev_skill_506_max_range;
SELECT changes() AS affected_server_service_skill_506; -- 1
UPDATE skills
SET a_targetType=31,target_selection_id=2,target_relation_id=0,
    min_range=0.0,max_range=8.0
WHERE a_index=507 AND a_base_class=999 AND show=0 AND icon_id=5115
  AND a_targetType IS :prev_skill_507_a_targetType
  AND target_selection_id IS :prev_skill_507_target_selection_id
  AND target_relation_id IS :prev_skill_507_target_relation_id
  AND min_range IS :prev_skill_507_min_range AND max_range IS :prev_skill_507_max_range;
SELECT changes() AS affected_server_service_skill_507; -- 1

INSERT INTO skill_constants (constant_key,skill_id,enabled) VALUES
 ('npc_store',504,1),('npc_storage',505,1),
 ('npc_auction',506,1),('npc_guild_create',507,1);
INSERT INTO npc_interaction_sets (id,name) VALUES
 (907010,'WI demo Store: Merchant Geres'),
 (907011,'WI demo Sto