[{"data":1,"prerenderedAt":2004},["ShallowReactive",2],{"doc-global_essentials\u002Fadvanced\u002Fplugins":3},{"id":4,"title":5,"body":6,"description":16,"extension":1997,"meta":1998,"navigation":551,"path":2000,"seo":2001,"stem":2002,"__hash__":2003},"docs\u002Fdocs\u002Fglobal_essentials\u002Fadvanced\u002Fplugins.md","Plugins",{"type":7,"value":8,"toc":1958},"minimark",[9,13,17,20,23,28,58,65,86,89,96,99,101,105,110,113,173,177,180,231,235,238,300,305,457,466,485,487,491,497,597,600,602,606,617,621,627,717,720,784,817,823,827,835,839,883,889,891,895,919,923,926,963,967,973,1103,1112,1114,1118,1125,1179,1231,1234,1236,1240,1251,1258,1264,1274,1398,1404,1414,1439,1445,1449,1509,1513,1526,1536,1545,1547,1551,1563,1565,1569,1576,1582,1586,1592,1640,1644,1655,1736,1740,1757,1770,1772,1776,1779,1783,1786,1898,1902,1905,1911,1914,1918,1926,1932,1935,1939,1948,1954],[10,11,5],"h1",{"id":12},"plugins",[14,15,16],"p",{},"Dryad Engine provides comprehensive data management through its built-in forms - characters, items, dungeons, and more. But what if your game needs custom data types that don't fit into the standard categories?",[14,18,19],{},"Plugins solve this by letting you create your own forms directly in the Engine Editor. Define a custom schema, and the engine generates forms for you to work with your game-specific data.",[21,22],"hr",{},[24,25,27],"h2",{"id":26},"creating-a-plugin","Creating a Plugin",[29,30,31,40,47,55],"ol",{},[32,33,34,35,39],"li",{},"Go to ",[36,37,38],"strong",{},"Dev → Plugins"," in the Engine Editor",[32,41,42,43,46],{},"Click ",[36,44,45],{},"Add"," to create a new plugin",[32,48,49,50,54],{},"Give it an ID (e.g., ",[51,52,53],"code",{},"my_plugin",")",[32,56,57],{},"Define your custom tabs and schemas",[14,59,60,61,64],{},"When you save, the engine creates (inside ",[51,62,63],{},"games_files\u002F[your_game]\u002F[your_game_or_mod]\u002F","):",[66,67,68,74,80],"ul",{},[32,69,70,73],{},[51,71,72],{},"plugins\u002F{pluginId}\u002Fplugin.json"," - Your plugin definition",[32,75,76,79],{},[51,77,78],{},"plugins\u002F{pluginId}\u002Fscripts\u002F"," - Auto-loaded JavaScript files",[32,81,82,85],{},[51,83,84],{},"plugins\u002F{pluginId}\u002Fcss\u002F"," - Auto-loaded stylesheets",[14,87,88],{},"Your form data is saved to:",[66,90,91],{},[32,92,93],{},[51,94,95],{},"plugins_data\u002F{pluginId}\u002F{tabId}.json",[14,97,98],{},"A new tab appears in the Engine Editor with your custom forms.",[21,100],{},[24,102,104],{"id":103},"plugin-structure","Plugin Structure",[106,107,109],"h3",{"id":108},"meta","Meta",[14,111,112],{},"Basic information about your plugin:",[114,115,116,129],"table",{},[117,118,119],"thead",{},[120,121,122,126],"tr",{},[123,124,125],"th",{},"Field",[123,127,128],{},"Description",[130,131,132,143,153,163],"tbody",{},[120,133,134,140],{},[135,136,137],"td",{},[51,138,139],{},"name",[135,141,142],{},"Display name shown in the editor",[120,144,145,150],{},[135,146,147],{},[51,148,149],{},"description",[135,151,152],{},"What the plugin does",[120,154,155,160],{},[135,156,157],{},[51,158,159],{},"author",[135,161,162],{},"Who created it",[120,164,165,170],{},[135,166,167],{},[51,168,169],{},"version",[135,171,172],{},"Version number (e.g., \"1.0.0\")",[106,174,176],{"id":175},"tabs","Tabs",[14,178,179],{},"Each tab becomes a form in the Engine Editor. Configure:",[114,181,182,190],{},[117,183,184],{},[120,185,186,188],{},[123,187,125],{},[123,189,128],{},[130,191,192,202,211,221],{},[120,193,194,199],{},[135,195,196],{},[51,197,198],{},"id",[135,200,201],{},"Unique identifier for the tab",[120,203,204,208],{},[135,205,206],{},[51,207,139],{},[135,209,210],{},"Display name (defaults to id)",[120,212,213,218],{},[135,214,215],{},[51,216,217],{},"isArray",[135,219,220],{},"If true, tab holds multiple items; if false, a single object",[120,222,223,228],{},[135,224,225],{},[51,226,227],{},"schema",[135,229,230],{},"Array of field definitions",[106,232,234],{"id":233},"schema-fields","Schema Fields",[14,236,237],{},"Each field in your schema defines one form input:",[114,239,240,248],{},[117,241,242],{},[120,243,244,246],{},[123,245,125],{},[123,247,128],{},[130,249,250,260,270,280,290],{},[120,251,252,257],{},[135,253,254],{},[51,255,256],{},"propertyId",[135,258,259],{},"Property name in the data object",[120,261,262,267],{},[135,263,264],{},[51,265,266],{},"label",[135,268,269],{},"Display label for the field",[120,271,272,277],{},[135,273,274],{},[51,275,276],{},"tooltip",[135,278,279],{},"Help text shown on hover",[120,281,282,287],{},[135,283,284],{},[51,285,286],{},"type",[135,288,289],{},"Field type (see below)",[120,291,292,297],{},[135,293,294],{},[51,295,296],{},"defaultValue",[135,298,299],{},"Default value for new items",[14,301,302],{},[36,303,304],{},"Field Types:",[114,306,307,316],{},[117,308,309],{},[120,310,311,314],{},[123,312,313],{},"Type",[123,315,128],{},[130,317,318,328,338,348,358,368,378,388,398,408,418,428,437,447],{},[120,319,320,325],{},[135,321,322],{},[51,323,324],{},"string",[135,326,327],{},"Single-line text input",[120,329,330,335],{},[135,331,332],{},[51,333,334],{},"number",[135,336,337],{},"Numeric input",[120,339,340,345],{},[135,341,342],{},[51,343,344],{},"boolean",[135,346,347],{},"Checkbox toggle",[120,349,350,355],{},[135,351,352],{},[51,353,354],{},"textarea",[135,356,357],{},"Multi-line text",[120,359,360,365],{},[135,361,362],{},[51,363,364],{},"htmlarea",[135,366,367],{},"Rich text editor",[120,369,370,375],{},[135,371,372],{},[51,373,374],{},"chooseOne",[135,376,377],{},"Dropdown select",[120,379,380,385],{},[135,381,382],{},[51,383,384],{},"chooseMany",[135,386,387],{},"Multi-select",[120,389,390,395],{},[135,391,392],{},[51,393,394],{},"string[]",[135,396,397],{},"Array of strings",[120,399,400,405],{},[135,401,402],{},[51,403,404],{},"number[]",[135,406,407],{},"Array of numbers",[120,409,410,415],{},[135,411,412],{},[51,413,414],{},"file",[135,416,417],{},"Single file picker",[120,419,420,425],{},[135,421,422],{},[51,423,424],{},"file[]",[135,426,427],{},"Multiple file picker",[120,429,430,434],{},[135,431,432],{},[51,433,227],{},[135,435,436],{},"Nested object",[120,438,439,444],{},[135,440,441],{},[51,442,443],{},"schema[]",[135,445,446],{},"Array of nested objects",[120,448,449,454],{},[135,450,451],{},[51,452,453],{},"color",[135,455,456],{},"Color picker",[14,458,459,460,462,463,465],{},"For ",[51,461,374],{}," and ",[51,464,384],{},", you can:",[66,467,468,475],{},[32,469,470,471,474],{},"Provide static ",[51,472,473],{},"options"," array",[32,476,477,478,481,482,54],{},"Use ",[51,479,480],{},"fromFile"," to load options from another data file (e.g., ",[51,483,484],{},"\"character_traits\"",[21,486],{},[24,488,490],{"id":489},"accessing-plugin-data","Accessing Plugin Data",[14,492,477,493,496],{},[51,494,495],{},"game.getData()"," with the unified path API:",[498,499,504],"pre",{"className":500,"code":501,"language":502,"meta":503,"style":503},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F Get your plugin's data\nlet myData = game.getData(\"plugins_data\u002Fmy_plugin\u002Fmy_tab\");\n\n\u002F\u002F Iterate over items (if isArray: true)\nfor (let [id, item] of myData) {\n  console.log(item.name, item.customField);\n}\n","javascript","",[51,505,506,515,546,553,559,579,591],{"__ignoreMap":503},[507,508,511],"span",{"class":509,"line":510},"line",1,[507,512,514],{"class":513},"sJ8bj","\u002F\u002F Get your plugin's data\n",[507,516,518,522,526,529,532,536,539,543],{"class":509,"line":517},2,[507,519,521],{"class":520},"szBVR","let",[507,523,525],{"class":524},"sVt8B"," myData ",[507,527,528],{"class":520},"=",[507,530,531],{"class":524}," game.",[507,533,535],{"class":534},"sScJk","getData",[507,537,538],{"class":524},"(",[507,540,542],{"class":541},"sZZnC","\"plugins_data\u002Fmy_plugin\u002Fmy_tab\"",[507,544,545],{"class":524},");\n",[507,547,549],{"class":509,"line":548},3,[507,550,552],{"emptyLinePlaceholder":551},true,"\n",[507,554,556],{"class":509,"line":555},4,[507,557,558],{"class":513},"\u002F\u002F Iterate over items (if isArray: true)\n",[507,560,562,565,568,570,573,576],{"class":509,"line":561},5,[507,563,564],{"class":520},"for",[507,566,567],{"class":524}," (",[507,569,521],{"class":520},[507,571,572],{"class":524}," [id, item] ",[507,574,575],{"class":520},"of",[507,577,578],{"class":524}," myData) {\n",[507,580,582,585,588],{"class":509,"line":581},6,[507,583,584],{"class":524},"  console.",[507,586,587],{"class":534},"log",[507,589,590],{"class":524},"(item.name, item.customField);\n",[507,592,594],{"class":509,"line":593},7,[507,595,596],{"class":524},"}\n",[14,598,599],{},"The path follows the same pattern as other game data files.",[21,601],{},[24,603,605],{"id":604},"plugin-script-loading","Plugin Script Loading",[14,607,608,609,612,613,616],{},"Plugins must explicitly specify which scripts to load via the ",[51,610,611],{},"scripts"," field in ",[51,614,615],{},"plugin.json",". This allows you to use ES6 imports without double-loading modules.",[106,618,620],{"id":619},"script-configuration","Script Configuration",[14,622,623,624,626],{},"Specify entry point script(s) in the ",[51,625,611],{}," array. Only listed scripts will be loaded by the engine:",[498,628,632],{"className":629,"code":630,"language":631,"meta":503,"style":503},"language-json shiki shiki-themes github-light github-dark","{\n  \"meta\": {\n    \"name\": \"My Plugin\",\n    \"id\": \"my_plugin\",\n    \"version\": \"1.0.0\"\n  },\n  \"scripts\": [\"main.mjs\"],\n  \"tabs\": []\n}\n","json",[51,633,634,639,648,662,674,684,689,703,712],{"__ignoreMap":503},[507,635,636],{"class":509,"line":510},[507,637,638],{"class":524},"{\n",[507,640,641,645],{"class":509,"line":517},[507,642,644],{"class":643},"sj4cs","  \"meta\"",[507,646,647],{"class":524},": {\n",[507,649,650,653,656,659],{"class":509,"line":548},[507,651,652],{"class":643},"    \"name\"",[507,654,655],{"class":524},": ",[507,657,658],{"class":541},"\"My Plugin\"",[507,660,661],{"class":524},",\n",[507,663,664,667,669,672],{"class":509,"line":555},[507,665,666],{"class":643},"    \"id\"",[507,668,655],{"class":524},[507,670,671],{"class":541},"\"my_plugin\"",[507,673,661],{"class":524},[507,675,676,679,681],{"class":509,"line":561},[507,677,678],{"class":643},"    \"version\"",[507,680,655],{"class":524},[507,682,683],{"class":541},"\"1.0.0\"\n",[507,685,686],{"class":509,"line":581},[507,687,688],{"class":524},"  },\n",[507,690,691,694,697,700],{"class":509,"line":593},[507,692,693],{"class":643},"  \"scripts\"",[507,695,696],{"class":524},": [",[507,698,699],{"class":541},"\"main.mjs\"",[507,701,702],{"class":524},"],\n",[507,704,706,709],{"class":509,"line":705},8,[507,707,708],{"class":643},"  \"tabs\"",[507,710,711],{"class":524},": []\n",[507,713,715],{"class":509,"line":714},9,[507,716,596],{"class":524},[14,718,719],{},"Your main entry point can then use ES6 imports for other modules:",[498,721,723],{"className":500,"code":722,"language":502,"meta":503,"style":503},"\u002F\u002F scripts\u002Fmain.mjs (loaded by engine)\nimport { combatLogic } from '.\u002Fcombat-utils.mjs';\nimport { BattleUI } from '.\u002Fui-components.mjs';\n\n\u002F\u002F Your plugin initialization code here\nconsole.log('Plugin loaded!');\n",[51,724,725,730,747,761,765,770],{"__ignoreMap":503},[507,726,727],{"class":509,"line":510},[507,728,729],{"class":513},"\u002F\u002F scripts\u002Fmain.mjs (loaded by engine)\n",[507,731,732,735,738,741,744],{"class":509,"line":517},[507,733,734],{"class":520},"import",[507,736,737],{"class":524}," { combatLogic } ",[507,739,740],{"class":520},"from",[507,742,743],{"class":541}," '.\u002Fcombat-utils.mjs'",[507,745,746],{"class":524},";\n",[507,748,749,751,754,756,759],{"class":509,"line":548},[507,750,734],{"class":520},[507,752,753],{"class":524}," { BattleUI } ",[507,755,740],{"class":520},[507,757,758],{"class":541}," '.\u002Fui-components.mjs'",[507,760,746],{"class":524},[507,762,763],{"class":509,"line":555},[507,764,552],{"emptyLinePlaceholder":551},[507,766,767],{"class":509,"line":561},[507,768,769],{"class":513},"\u002F\u002F Your plugin initialization code here\n",[507,771,772,775,777,779,782],{"class":509,"line":581},[507,773,774],{"class":524},"console.",[507,776,587],{"class":534},[507,778,538],{"class":524},[507,780,781],{"class":541},"'Plugin loaded!'",[507,783,545],{"class":524},[498,785,787],{"className":500,"code":786,"language":502,"meta":503,"style":503},"\u002F\u002F scripts\u002Fcombat-utils.mjs (imported, not loaded standalone)\nexport function combatLogic() {\n  \u002F\u002F Implementation\n}\n",[51,788,789,794,808,813],{"__ignoreMap":503},[507,790,791],{"class":509,"line":510},[507,792,793],{"class":513},"\u002F\u002F scripts\u002Fcombat-utils.mjs (imported, not loaded standalone)\n",[507,795,796,799,802,805],{"class":509,"line":517},[507,797,798],{"class":520},"export",[507,800,801],{"class":520}," function",[507,803,804],{"class":534}," combatLogic",[507,806,807],{"class":524},"() {\n",[507,809,810],{"class":509,"line":548},[507,811,812],{"class":513},"  \u002F\u002F Implementation\n",[507,814,815],{"class":509,"line":555},[507,816,596],{"class":524},[14,818,819,822],{},[36,820,821],{},"Only list entry points in the scripts array"," - imported modules should not be included.",[106,824,826],{"id":825},"folder-structure","Folder Structure",[498,828,833],{"className":829,"code":831,"language":832},[830],"language-text","plugins\u002Fmy_plugin\u002F\n├── plugin.json\n├── scripts\u002F\n│   ├── main.mjs           \u003C- Listed in scripts array\n│   ├── combat-utils.mjs   \u003C- Imported by main.mjs\n│   └── ui-components.mjs  \u003C- Imported by main.mjs\n└── css\u002F\n    └── my_styles.css      \u003C- CSS auto-loads (no need to specify)\n","text",[51,834,831],{"__ignoreMap":503},[106,836,838],{"id":837},"common-patterns","Common Patterns",[114,840,841,851],{},[117,842,843],{},[120,844,845,848],{},[123,846,847],{},"Pattern",[123,849,850],{},"Configuration",[130,852,853,863,873],{},[120,854,855,858],{},[135,856,857],{},"Single entry point",[135,859,860],{},[51,861,862],{},"\"scripts\": [\"main.mjs\"]",[120,864,865,868],{},[135,866,867],{},"Multiple entry points",[135,869,870],{},[51,871,872],{},"\"scripts\": [\"init.mjs\", \"ui.mjs\"]",[120,874,875,878],{},[135,876,877],{},"No scripts needed",[135,879,880],{},[51,881,882],{},"\"scripts\": []",[14,884,885,888],{},[36,886,887],{},"Note:"," CSS files always auto-load - no need to specify them in your configuration.",[21,890],{},[24,892,894],{"id":893},"data-injection","Data Injection",[14,896,897,898,901,902,905,906,905,909,905,912,915,916,918],{},"The ",[51,899,900],{},"data"," field lets your plugin inject entries into existing game data files. Any game data file can be targeted: ",[51,903,904],{},"locale",", ",[51,907,908],{},"character_attributes",[51,910,911],{},"character_traits",[51,913,914],{},"character_stats",", etc. Plugin data has lower precedence than the game's own data — if both define the same ",[51,917,198],{},", the game's version wins.",[106,920,922],{"id":921},"editor-format","Editor Format",[14,924,925],{},"In the Engine Editor, each data entry has two fields:",[114,927,928,936],{},[117,929,930],{},[120,931,932,934],{},[123,933,125],{},[123,935,128],{},[130,937,938,953],{},[120,939,940,945],{},[135,941,942],{},[51,943,944],{},"fileName",[135,946,947,948,905,951,54],{},"Target file (e.g., ",[51,949,950],{},"\"locale\"",[51,952,484],{},[120,954,955,960],{},[135,956,957],{},[51,958,959],{},"fileData",[135,961,962],{},"JSON data to inject (array for array files, object for single files)",[106,964,966],{"id":965},"object-shorthand","Object Shorthand",[14,968,969,970,972],{},"When writing ",[51,971,615],{}," by hand, you can use the object shorthand — each key is a file name, each value is the data array:",[498,974,976],{"className":629,"code":975,"language":631,"meta":503,"style":503},"{\n  \"data\": {\n    \"locale\": [\n      { \"id\": \"greeting\", \"val\": \"Hello, traveler!\", \"uid\": \"myPlug01\" }\n    ],\n    \"character_attributes\": [\n      { \"uid\": \"attr_mood\", \"id\": \"mood\", \"values\": [\"happy\", \"sad\", \"angry\"] }\n    ]\n  }\n}\n",[51,977,978,982,989,997,1033,1038,1045,1088,1093,1098],{"__ignoreMap":503},[507,979,980],{"class":509,"line":510},[507,981,638],{"class":524},[507,983,984,987],{"class":509,"line":517},[507,985,986],{"class":643},"  \"data\"",[507,988,647],{"class":524},[507,990,991,994],{"class":509,"line":548},[507,992,993],{"class":643},"    \"locale\"",[507,995,996],{"class":524},": [\n",[507,998,999,1002,1005,1007,1010,1012,1015,1017,1020,1022,1025,1027,1030],{"class":509,"line":555},[507,1000,1001],{"class":524},"      { ",[507,1003,1004],{"class":643},"\"id\"",[507,1006,655],{"class":524},[507,1008,1009],{"class":541},"\"greeting\"",[507,1011,905],{"class":524},[507,1013,1014],{"class":643},"\"val\"",[507,1016,655],{"class":524},[507,1018,1019],{"class":541},"\"Hello, traveler!\"",[507,1021,905],{"class":524},[507,1023,1024],{"class":643},"\"uid\"",[507,1026,655],{"class":524},[507,1028,1029],{"class":541},"\"myPlug01\"",[507,1031,1032],{"class":524}," }\n",[507,1034,1035],{"class":509,"line":561},[507,1036,1037],{"class":524},"    ],\n",[507,1039,1040,1043],{"class":509,"line":581},[507,1041,1042],{"class":643},"    \"character_attributes\"",[507,1044,996],{"class":524},[507,1046,1047,1049,1051,1053,1056,1058,1060,1062,1065,1067,1070,1072,1075,1077,1080,1082,1085],{"class":509,"line":593},[507,1048,1001],{"class":524},[507,1050,1024],{"class":643},[507,1052,655],{"class":524},[507,1054,1055],{"class":541},"\"attr_mood\"",[507,1057,905],{"class":524},[507,1059,1004],{"class":643},[507,1061,655],{"class":524},[507,1063,1064],{"class":541},"\"mood\"",[507,1066,905],{"class":524},[507,1068,1069],{"class":643},"\"values\"",[507,1071,696],{"class":524},[507,1073,1074],{"class":541},"\"happy\"",[507,1076,905],{"class":524},[507,1078,1079],{"class":541},"\"sad\"",[507,1081,905],{"class":524},[507,1083,1084],{"class":541},"\"angry\"",[507,1086,1087],{"class":524},"] }\n",[507,1089,1090],{"class":509,"line":705},[507,1091,1092],{"class":524},"    ]\n",[507,1094,1095],{"class":509,"line":714},[507,1096,1097],{"class":524},"  }\n",[507,1099,1101],{"class":509,"line":1100},10,[507,1102,596],{"class":524},[14,1104,1105,1106,1108,1109,1111],{},"The engine converts this to the ",[51,1107,944],{},"\u002F",[51,1110,959],{}," format automatically on load.",[21,1113],{},[24,1115,1117],{"id":1116},"service-registry","Service Registry",[14,1119,1120,1121,1124],{},"Plugins can expose utility functions for other scripts via ",[51,1122,1123],{},"game.registerService()",". This is useful when a plugin provides logic that game scripts need to call.",[498,1126,1130],{"className":1127,"code":1128,"language":1129,"meta":503,"style":503},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F In plugin main.mjs — register a service\nfunction resolvePronouns(charId) {\n    \u002F\u002F ... pronoun resolution logic\n}\ngame.registerService('gender_pov', { resolvePronouns });\n","js",[51,1131,1132,1137,1154,1159,1163],{"__ignoreMap":503},[507,1133,1134],{"class":509,"line":510},[507,1135,1136],{"class":513},"\u002F\u002F In plugin main.mjs — register a service\n",[507,1138,1139,1142,1145,1147,1151],{"class":509,"line":517},[507,1140,1141],{"class":520},"function",[507,1143,1144],{"class":534}," resolvePronouns",[507,1146,538],{"class":524},[507,1148,1150],{"class":1149},"s4XuR","charId",[507,1152,1153],{"class":524},") {\n",[507,1155,1156],{"class":509,"line":548},[507,1157,1158],{"class":513},"    \u002F\u002F ... pronoun resolution logic\n",[507,1160,1161],{"class":509,"line":555},[507,1162,596],{"class":524},[507,1164,1165,1168,1171,1173,1176],{"class":509,"line":561},[507,1166,1167],{"class":524},"game.",[507,1169,1170],{"class":534},"registerService",[507,1172,538],{"class":524},[507,1174,1175],{"class":541},"'gender_pov'",[507,1177,1178],{"class":524},", { resolvePronouns });\n",[498,1180,1182],{"className":1127,"code":1181,"language":1129,"meta":503,"style":503},"\u002F\u002F In a game script — consume the service\nconst { resolvePronouns } = game.getService('gender_pov');\nconst pronouns = resolvePronouns(characterId);\n",[51,1183,1184,1189,1216],{"__ignoreMap":503},[507,1185,1186],{"class":509,"line":510},[507,1187,1188],{"class":513},"\u002F\u002F In a game script — consume the service\n",[507,1190,1191,1194,1197,1200,1203,1205,1207,1210,1212,1214],{"class":509,"line":517},[507,1192,1193],{"class":520},"const",[507,1195,1196],{"class":524}," { ",[507,1198,1199],{"class":643},"resolvePronouns",[507,1201,1202],{"class":524}," } ",[507,1204,528],{"class":520},[507,1206,531],{"class":524},[507,1208,1209],{"class":534},"getService",[507,1211,538],{"class":524},[507,1213,1175],{"class":541},[507,1215,545],{"class":524},[507,1217,1218,1220,1223,1226,1228],{"class":509,"line":548},[507,1219,1193],{"class":520},[507,1221,1222],{"class":643}," pronouns",[507,1224,1225],{"class":520}," =",[507,1227,1144],{"class":534},[507,1229,1230],{"class":524},"(characterId);\n",[14,1232,1233],{},"Plugin scripts load before game scripts, so services registered by plugins are available when game scripts run. If a game script re-registers placeholders or overrides behavior, it takes precedence.",[21,1235],{},[24,1237,1239],{"id":1238},"typed-services","Typed Services",[14,1241,1242,1243,1246,1247,1250],{},"Plugins can expose typed service APIs so that game scripts get autocomplete and typo protection on ",[51,1244,1245],{},"game.getService()"," calls. This uses TypeScript declaration merging on the ",[51,1248,1249],{},"Game"," interface.",[106,1252,1254,1255],{"id":1253},"_1-define-service-types-in-the-plugins-dtypesdts","1. Define service types in the plugin's ",[51,1256,1257],{},"dtypes.d.ts",[498,1259,1262],{"className":1260,"code":1261,"language":832},[830],"plugins\u002Fmy_plugin\u002F\n├── plugin.json\n├── scripts\u002F\n│   ├── main.mjs\n│   └── dtypes.d.ts     \u003C- Service types defined here\n",[51,1263,1261],{"__ignoreMap":503},[14,1265,1266,1267,1270,1271,1273],{},"In ",[51,1268,1269],{},"scripts\u002Fdtypes.d.ts",", define a type for each service and add a ",[51,1272,1209],{}," overload via declaration merging:",[498,1275,1279],{"className":1276,"code":1277,"language":1278,"meta":503,"style":503},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F Service type\ntype MyPluginService = {\n  resolvePronouns(charId: string): { he: string; him: string; his: string };\n};\n\n\u002F\u002F Declaration merging — adds typed overload to Game.getService()\ninterface Game {\n  getService(id: 'gender_pov'): MyPluginService;\n}\n","ts",[51,1280,1281,1286,1298,1348,1353,1357,1362,1372,1394],{"__ignoreMap":503},[507,1282,1283],{"class":509,"line":510},[507,1284,1285],{"class":513},"\u002F\u002F Service type\n",[507,1287,1288,1290,1293,1295],{"class":509,"line":517},[507,1289,286],{"class":520},[507,1291,1292],{"class":534}," MyPluginService",[507,1294,1225],{"class":520},[507,1296,1297],{"class":524}," {\n",[507,1299,1300,1303,1305,1307,1310,1313,1315,1317,1319,1322,1324,1326,1329,1332,1334,1336,1338,1341,1343,1345],{"class":509,"line":548},[507,1301,1302],{"class":534},"  resolvePronouns",[507,1304,538],{"class":524},[507,1306,1150],{"class":1149},[507,1308,1309],{"class":520},":",[507,1311,1312],{"class":643}," string",[507,1314,54],{"class":524},[507,1316,1309],{"class":520},[507,1318,1196],{"class":524},[507,1320,1321],{"class":1149},"he",[507,1323,1309],{"class":520},[507,1325,1312],{"class":643},[507,1327,1328],{"class":524},"; ",[507,1330,1331],{"class":1149},"him",[507,1333,1309],{"class":520},[507,1335,1312],{"class":643},[507,1337,1328],{"class":524},[507,1339,1340],{"class":1149},"his",[507,1342,1309],{"class":520},[507,1344,1312],{"class":643},[507,1346,1347],{"class":524}," };\n",[507,1349,1350],{"class":509,"line":555},[507,1351,1352],{"class":524},"};\n",[507,1354,1355],{"class":509,"line":561},[507,1356,552],{"emptyLinePlaceholder":551},[507,1358,1359],{"class":509,"line":581},[507,1360,1361],{"class":513},"\u002F\u002F Declaration merging — adds typed overload to Game.getService()\n",[507,1363,1364,1367,1370],{"class":509,"line":593},[507,1365,1366],{"class":520},"interface",[507,1368,1369],{"class":534}," Game",[507,1371,1297],{"class":524},[507,1373,1374,1377,1379,1381,1383,1386,1388,1390,1392],{"class":509,"line":705},[507,1375,1376],{"class":534},"  getService",[507,1378,538],{"class":524},[507,1380,198],{"class":1149},[507,1382,1309],{"class":520},[507,1384,1385],{"class":541}," 'gender_pov'",[507,1387,54],{"class":524},[507,1389,1309],{"class":520},[507,1391,1292],{"class":534},[507,1393,746],{"class":524},[507,1395,1396],{"class":509,"line":714},[507,1397,596],{"class":524},[106,1399,1401,1402],{"id":1400},"_2-reference-plugin-types-from-the-games-dtypesdts","2. Reference plugin types from the game's ",[51,1403,1257],{},[14,1405,1406,1407,1409,1410,1413],{},"Each game has its own ",[51,1408,1257],{}," (in ",[51,1411,1412],{},"games_assets\u002F{game}\u002F_core\u002Fscripts\u002F","). Add a single reference to the plugin's types:",[498,1415,1417],{"className":1276,"code":1416,"language":1278,"meta":503,"style":503},"\u002F\u002F\u002F \u003Creference path=\"..\u002F..\u002F..\u002F..\u002Fgames_files\u002F{game}\u002F_core\u002Fplugins\u002Fmy_plugin\u002Fscripts\u002Fdtypes.d.ts\" \u002F>\n",[51,1418,1419],{"__ignoreMap":503},[507,1420,1421,1424,1428,1431,1433,1436],{"class":509,"line":510},[507,1422,1423],{"class":513},"\u002F\u002F\u002F \u003C",[507,1425,1427],{"class":1426},"s9eBZ","reference",[507,1429,1430],{"class":534}," path",[507,1432,528],{"class":520},[507,1434,1435],{"class":541},"\"..\u002F..\u002F..\u002F..\u002Fgames_files\u002F{game}\u002F_core\u002Fplugins\u002Fmy_plugin\u002Fscripts\u002Fdtypes.d.ts\"",[507,1437,1438],{"class":513}," \u002F>\n",[14,1440,1441,1442,1444],{},"Every game script already references the game's ",[51,1443,1257],{},", so plugin types cascade to all scripts automatically.",[106,1446,1448],{"id":1447},"_3-use-in-game-scripts","3. Use in game scripts",[498,1450,1452],{"className":1127,"code":1451,"language":1129,"meta":503,"style":503},"\u002F\u002F Autocomplete works — no manual cast needed\nconst pronouns = game.getService('gender_pov').resolvePronouns(charId);\n\n\u002F\u002F Typos are caught at compile time\ngame.getService('gender_pvo'); \u002F\u002F Error: no overload matches\n",[51,1453,1454,1459,1483,1487,1492],{"__ignoreMap":503},[507,1455,1456],{"class":509,"line":510},[507,1457,1458],{"class":513},"\u002F\u002F Autocomplete works — no manual cast needed\n",[507,1460,1461,1463,1465,1467,1469,1471,1473,1475,1478,1480],{"class":509,"line":517},[507,1462,1193],{"class":520},[507,1464,1222],{"class":643},[507,1466,1225],{"class":520},[507,1468,531],{"class":524},[507,1470,1209],{"class":534},[507,1472,538],{"class":524},[507,1474,1175],{"class":541},[507,1476,1477],{"class":524},").",[507,1479,1199],{"class":534},[507,1481,1482],{"class":524},"(charId);\n",[507,1484,1485],{"class":509,"line":548},[507,1486,552],{"emptyLinePlaceholder":551},[507,1488,1489],{"class":509,"line":555},[507,1490,1491],{"class":513},"\u002F\u002F Typos are caught at compile time\n",[507,1493,1494,1496,1498,1500,1503,1506],{"class":509,"line":561},[507,1495,1167],{"class":524},[507,1497,1209],{"class":534},[507,1499,538],{"class":524},[507,1501,1502],{"class":541},"'gender_pvo'",[507,1504,1505],{"class":524},"); ",[507,1507,1508],{"class":513},"\u002F\u002F Error: no overload matches\n",[24,1510,1512],{"id":1511},"global-vs-game-plugins","Global vs Game Plugins",[14,1514,1515,1518,1519,1522,1523,1477],{},[36,1516,1517],{},"Global plugins"," come bundled with the engine in ",[51,1520,1521],{},"engine_files\u002Fplugins\u002F",". These provide optional features that any game can enable (like ",[51,1524,1525],{},"global_essentials",[14,1527,1528,1531,1532,1535],{},[36,1529,1530],{},"Game plugins"," are created by you in ",[51,1533,1534],{},"games_files\u002F{gameId}\u002F{modId}\u002Fplugins\u002F",". These are specific to your game and can be shared with the community.",[14,1537,1538,1539,612,1541,1544],{},"To enable plugins, add them to your manifest's ",[51,1540,12],{},[36,1542,1543],{},"General → Manifest",".",[21,1546],{},[24,1548,1550],{"id":1549},"sharing-plugins","Sharing Plugins",[14,1552,1553,1554,1557,1558,1560,1561,1544],{},"You can share specific plugins from your ",[51,1555,1556],{},"plugins\u002F"," folder with the community, or place community plugins into your ",[51,1559,1556],{}," folder to use them. Don't forget to enable the plugin in ",[36,1562,1543],{},[21,1564],{},[24,1566,1568],{"id":1567},"plugin-documentation","Plugin Documentation",[14,1570,1571,1572,1575],{},"Plugins can ship their own documentation that appears in the engine's Docs Viewer. Add a ",[51,1573,1574],{},"docs\u002F"," folder to your plugin with the following convention:",[498,1577,1580],{"className":1578,"code":1579,"language":832},[830],"plugins\u002Fmy_plugin\u002F\n├── plugin.json\n├── docs\u002F\n│   ├── tree.json              \u003C- Navigation structure (language-independent)\n│   └── en\u002F                    \u003C- One folder per locale\n│       ├── headers.json       \u003C- Display names for categories and pages\n│       └── guide\u002F             \u003C- Category folder (matches tree.json key)\n│           ├── overview.md\n│           └── usage.md\n├── scripts\u002F\n└── css\u002F\n",[51,1581,1579],{"__ignoreMap":503},[106,1583,1585],{"id":1584},"treejson","tree.json",[14,1587,1588,1589,64],{},"Defines the navigation tree. Keys are category folder names, values are arrays of page filenames (without ",[51,1590,1591],{},".md",[498,1593,1595],{"className":629,"code":1594,"language":631,"meta":503,"style":503},"{\n  \"guide\": [\"overview\", \"usage\"],\n  \"reference\": [\"api\", \"emitters\"]\n}\n",[51,1596,1597,1601,1618,1636],{"__ignoreMap":503},[507,1598,1599],{"class":509,"line":510},[507,1600,638],{"class":524},[507,1602,1603,1606,1608,1611,1613,1616],{"class":509,"line":517},[507,1604,1605],{"class":643},"  \"guide\"",[507,1607,696],{"class":524},[507,1609,1610],{"class":541},"\"overview\"",[507,1612,905],{"class":524},[507,1614,1615],{"class":541},"\"usage\"",[507,1617,702],{"class":524},[507,1619,1620,1623,1625,1628,1630,1633],{"class":509,"line":548},[507,1621,1622],{"class":643},"  \"reference\"",[507,1624,696],{"class":524},[507,1626,1627],{"class":541},"\"api\"",[507,1629,905],{"class":524},[507,1631,1632],{"class":541},"\"emitters\"",[507,1634,1635],{"class":524},"]\n",[507,1637,1638],{"class":509,"line":555},[507,1639,596],{"class":524},[106,1641,1643],{"id":1642},"headersjson","headers.json",[14,1645,1646,1647,1650,1651,1654],{},"Maps category and page IDs to display names. Use ",[51,1648,1649],{},"\"category\""," for the section header and ",[51,1652,1653],{},"\"category.page\""," for individual pages:",[498,1656,1658],{"className":629,"code":1657,"language":631,"meta":503,"style":503},"{\n  \"guide\": \"Guide\",\n  \"guide.overview\": \"Overview\",\n  \"guide.usage\": \"Usage\",\n  \"reference\": \"Reference\",\n  \"reference.api\": \"API\",\n  \"reference.emitters\": \"Emitters\"\n}\n",[51,1659,1660,1664,1675,1687,1699,1710,1722,1732],{"__ignoreMap":503},[507,1661,1662],{"class":509,"line":510},[507,1663,638],{"class":524},[507,1665,1666,1668,1670,1673],{"class":509,"line":517},[507,1667,1605],{"class":643},[507,1669,655],{"class":524},[507,1671,1672],{"class":541},"\"Guide\"",[507,1674,661],{"class":524},[507,1676,1677,1680,1682,1685],{"class":509,"line":548},[507,1678,1679],{"class":643},"  \"guide.overview\"",[507,1681,655],{"class":524},[507,1683,1684],{"class":541},"\"Overview\"",[507,1686,661],{"class":524},[507,1688,1689,1692,1694,1697],{"class":509,"line":555},[507,1690,1691],{"class":643},"  \"guide.usage\"",[507,1693,655],{"class":524},[507,1695,1696],{"class":541},"\"Usage\"",[507,1698,661],{"class":524},[507,1700,1701,1703,1705,1708],{"class":509,"line":561},[507,1702,1622],{"class":643},[507,1704,655],{"class":524},[507,1706,1707],{"class":541},"\"Reference\"",[507,1709,661],{"class":524},[507,1711,1712,1715,1717,1720],{"class":509,"line":581},[507,1713,1714],{"class":643},"  \"reference.api\"",[507,1716,655],{"class":524},[507,1718,1719],{"class":541},"\"API\"",[507,1721,661],{"class":524},[507,1723,1724,1727,1729],{"class":509,"line":593},[507,1725,1726],{"class":643},"  \"reference.emitters\"",[507,1728,655],{"class":524},[507,1730,1731],{"class":541},"\"Emitters\"\n",[507,1733,1734],{"class":509,"line":705},[507,1735,596],{"class":524},[106,1737,1739],{"id":1738},"markdown-files","Markdown files",[14,1741,1742,1743,1745,1746,1749,1750,1752,1753,1756],{},"Place ",[51,1744,1591],{}," files inside ",[51,1747,1748],{},"docs\u002F{locale}\u002F{category}\u002F",". Each file corresponds to a page in ",[51,1751,1585],{},". The file's ",[51,1754,1755],{},"# h1"," heading is displayed at the top of the page.",[14,1758,1759,1760,1762,1763,1765,1766,1769],{},"The Docs Viewer automatically discovers plugins with a ",[51,1761,1574],{}," folder — both global plugins (",[51,1764,1521],{},") and game plugins (",[51,1767,1768],{},"games_files\u002F{game}\u002F{mod}\u002Fplugins\u002F","). A searchable dropdown lets the user switch between plugin docs.",[21,1771],{},[24,1773,1775],{"id":1774},"docs-renderer-syntax","Docs Renderer Syntax",[14,1777,1778],{},"Documentation pages use standard Markdown rendered via MarkdownIt with a few extras.",[106,1780,1782],{"id":1781},"standard-markdown","Standard Markdown",[14,1784,1785],{},"All standard Markdown is supported:",[114,1787,1788,1798],{},[117,1789,1790],{},[120,1791,1792,1795],{},[123,1793,1794],{},"Syntax",[123,1796,1797],{},"Result",[130,1799,1800,1814,1834,1844,1855,1868,1878,1888],{},[120,1801,1802,1811],{},[135,1803,1804,1807,1808],{},[51,1805,1806],{},"# Heading"," through ",[51,1809,1810],{},"#### Heading",[135,1812,1813],{},"Section headings (h1–h4)",[120,1815,1816,1825],{},[135,1817,1818,1821,1822],{},[51,1819,1820],{},"**bold**"," \u002F ",[51,1823,1824],{},"*italic*",[135,1826,1827,1821,1830],{},[36,1828,1829],{},"bold",[1831,1832,1833],"em",{},"italic",[120,1835,1836,1841],{},[135,1837,1838],{},[51,1839,1840],{},"`inline code`",[135,1842,1843],{},"Inline code",[120,1845,1846,1852],{},[135,1847,1848,1851],{},[51,1849,1850],{},"```"," fenced blocks",[135,1853,1854],{},"Code blocks with syntax highlighting",[120,1856,1857,1865],{},[135,1858,1859,1821,1862],{},[51,1860,1861],{},"- item",[51,1863,1864],{},"1. item",[135,1866,1867],{},"Unordered \u002F ordered lists",[120,1869,1870,1875],{},[135,1871,1872],{},[51,1873,1874],{},"> quote",[135,1876,1877],{},"Blockquotes",[120,1879,1880,1885],{},[135,1881,1882],{},[51,1883,1884],{},"|col|col|",[135,1886,1887],{},"Tables",[120,1889,1890,1895],{},[135,1891,1892],{},[51,1893,1894],{},"[text](url)",[135,1896,1897],{},"External links",[106,1899,1901],{"id":1900},"code-blocks","Code Blocks",[14,1903,1904],{},"Fenced code blocks support language-specific syntax highlighting. Specify the language after the opening backticks:",[498,1906,1909],{"className":1907,"code":1908,"language":832},[830],"```js\ngame.log('Hello!');\n```\n\n```json\n{ \"key\": \"value\" }\n```\n",[51,1910,1908],{"__ignoreMap":503},[14,1912,1913],{},"A copy button appears on hover for all code blocks.",[106,1915,1917],{"id":1916},"internal-links","Internal Links",[14,1919,477,1920,1923,1924,1309],{},[51,1921,1922],{},"->category.page"," to create a clickable link to another docs page within the same plugin. The link text is resolved from ",[51,1925,1643],{},[498,1927,1930],{"className":1928,"code":1929,"language":832},[830],"See ->guide.overview for an introduction.\n",[51,1931,1929],{"__ignoreMap":503},[14,1933,1934],{},"This renders as a clickable link showing the display name (e.g., \"Overview\") that navigates to that page.",[106,1936,1938],{"id":1937},"images","Images",[14,1940,477,1941,1944,1945,1947],{},[51,1942,1943],{},"@path\u002Fto\u002Fimage.png"," to embed an image. The path is relative to the plugin's ",[51,1946,1574],{}," folder:",[498,1949,1952],{"className":1950,"code":1951,"language":832},[830],"@en\u002Fimages\u002Fscreenshot.png\n",[51,1953,1951],{"__ignoreMap":503},[1955,1956,1957],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":503,"searchDepth":517,"depth":517,"links":1959},[1960,1961,1966,1967,1972,1976,1977,1984,1985,1986,1991],{"id":26,"depth":517,"text":27},{"id":103,"depth":517,"text":104,"children":1962},[1963,1964,1965],{"id":108,"depth":548,"text":109},{"id":175,"depth":548,"text":176},{"id":233,"depth":548,"text":234},{"id":489,"depth":517,"text":490},{"id":604,"depth":517,"text":605,"children":1968},[1969,1970,1971],{"id":619,"depth":548,"text":620},{"id":825,"depth":548,"text":826},{"id":837,"depth":548,"text":838},{"id":893,"depth":517,"text":894,"children":1973},[1974,1975],{"id":921,"depth":548,"text":922},{"id":965,"depth":548,"text":966},{"id":1116,"depth":517,"text":1117},{"id":1238,"depth":517,"text":1239,"children":1978},[1979,1981,1983],{"id":1253,"depth":548,"text":1980},"1. Define service types in the plugin's dtypes.d.ts",{"id":1400,"depth":548,"text":1982},"2. Reference plugin types from the game's dtypes.d.ts",{"id":1447,"depth":548,"text":1448},{"id":1511,"depth":517,"text":1512},{"id":1549,"depth":517,"text":1550},{"id":1567,"depth":517,"text":1568,"children":1987},[1988,1989,1990],{"id":1584,"depth":548,"text":1585},{"id":1642,"depth":548,"text":1643},{"id":1738,"depth":548,"text":1739},{"id":1774,"depth":517,"text":1775,"children":1992},[1993,1994,1995,1996],{"id":1781,"depth":548,"text":1782},{"id":1900,"depth":548,"text":1901},{"id":1916,"depth":548,"text":1917},{"id":1937,"depth":548,"text":1938},"md",{"plugin":1525,"category":1999,"page":12},"advanced","\u002Fdocs\u002Fglobal_essentials\u002Fadvanced\u002Fplugins",{"title":5,"description":16},"docs\u002Fglobal_essentials\u002Fadvanced\u002Fplugins","9tf_OEqAJI7eDKxXAZ5DBo5UPZ8cqaJkAONNAJJa5b0",1779582261983]