1<?php 2use dokuwiki\Extension\SyntaxPlugin; 3 4class syntax_plugin_minecraftrecipe extends SyntaxPlugin { 5 6 public function getType() { 7 return 'container'; 8 } 9 10 public function getPType() { 11 return 'block'; 12 } 13 14 public function getSort() { 15 return 155; 16 } 17 18 public function connectTo($mode) { 19 $this->Lexer->addSpecialPattern('\<recipe(?:\s+type="[^"]*")?\>.*?\</recipe\>', $mode, 'plugin_minecraftrecipe'); 20 } 21 22 public function handle($match, $state, $pos, Doku_Handler $handler) { 23 $type = 'crafting'; // default type 24 if(preg_match('/type="([^"]*)"/', $match, $matches)) { 25 $type = $matches[1]; 26 } 27 28 $content = preg_replace('/^\<recipe(?:\s+type="[^"]*")?\>|\<\/recipe\>$/i', '', $match); 29 $lines = explode("\n", trim($content)); 30 31 switch($type) { 32 case 'smelting': 33 return $this->_handleSmelting($lines); 34 case 'brewing': 35 return $this->_handleBrewing($lines); 36 case 'crafting': 37 default: 38 return $this->_handleCrafting($lines); 39 } 40 } 41 42 protected function _handleCrafting($lines) { 43 $recipe = array(); 44 $result = array(); 45 46 foreach($lines as $line) { 47 $line = trim($line); 48 if(empty($line)) continue; 49 50 if(strpos($line, '->') !== false) { 51 $resultItems = explode(',', trim(substr($line, 2))); 52 $result = array_map('trim', $resultItems); 53 } else { 54 $slots = array_filter(explode(' ', $line)); 55 $row = array(); 56 foreach($slots as $slot) { 57 $items = array_map('trim', explode(',', $slot)); 58 $row[] = $items; 59 } 60 while(count($row) < 3) { 61 $row[] = array('empty'); 62 } 63 if(!empty($row)) { 64 $recipe[] = $row; 65 } 66 } 67 } 68 69 // pad to 3 rows with empty slots 70 while(count($recipe) < 3) { 71 $recipe[] = array(array('empty'), array('empty'), array('empty')); 72 } 73 74 return array( 75 'type' => 'crafting', 76 'recipe' => $recipe, 77 'result' => $result, 78 'shapeless' => false 79 ); 80 } 81 82 protected function _handleSmelting($lines) { 83 $input = array(); 84 $fuel = array(); 85 $result = array(); 86 87 foreach($lines as $index => $line) { 88 $line = trim($line); 89 if(empty($line)) continue; 90 91 if(strpos($line, '->') !== false) { 92 $resultItems = explode(',', trim(substr($line, 2))); 93 $result = array_map('trim', $resultItems); 94 } else if($index === 1) { // Second line is fuel 95 $fuelItems = explode(',', $line); 96 $fuel = array_map('trim', $fuelItems); 97 } else if($index === 0) { // First line is input 98 $inputItems = explode(',', $line); 99 $input = array_map('trim', $inputItems); 100 } 101 } 102 103 if(empty($fuel)) { 104 $fuel = array('empty'); 105 } 106 107 return array( 108 'type' => 'smelting', 109 'input' => $input, 110 'fuel' => $fuel, 111 'result' => $result 112 ); 113 } 114 115 protected function _handleBrewing($lines) { 116 $reagent = array(); 117 $potions = array(); 118 119 foreach($lines as $line) { 120 $line = trim($line); 121 if(empty($line)) continue; 122 123 if(strpos($line, '->') !== false) { 124 $potionItems = explode(' ', trim(substr($line, 2))); 125 foreach($potionItems as $potion) { 126 $potions[] = array_map('trim', explode(',', $potion)); 127 } 128 } else { 129 // first line is reagent 130 $reagentItems = explode(',', $line); 131 $reagent = array_map('trim', $reagentItems); 132 } 133 } 134 135 while(count($potions) < 3) { 136 $potions[] = array('empty'); 137 } 138 139 return array( 140 'type' => 'brewing', 141 'reagent' => $reagent, 142 'potions' => $potions 143 ); 144 } 145 146 protected function _renderBrewing($renderer, $data) { 147 $renderer->doc .= '<div class="minecraft-recipe brewing">'; 148 $renderer->doc .= '<div class="brewing-layout">'; 149 150 $renderer->doc .= '<div class="bubbles"></div>'; 151 $renderer->doc .= '<div class="vertical-arrow"></div>'; 152 $renderer->doc .= '<div class="pipes"></div>'; 153 154 // reagent slot 155 $renderer->doc .= '<div class="reagent grid">'; 156 $renderer->doc .= $this->_renderItem($data['reagent']); 157 $renderer->doc .= '</div>'; 158 159 // potion slots 160 foreach($data['potions'] as $index => $potion) { 161 $renderer->doc .= '<div class="grid potion-' . ($index + 1) . '">'; 162 $renderer->doc .= $this->_renderItem($potion); 163 $renderer->doc .= '</div>'; 164 } 165 166 $renderer->doc .= '</div></div>'; 167 return true; 168 } 169 170 public function render($mode, Doku_Renderer $renderer, $data) { 171 if($mode != 'xhtml') return false; 172 if(!is_array($data)) return false; 173 174 switch($data['type']) { 175 case 'smelting': 176 return $this->_renderSmelting($renderer, $data); 177 case 'brewing': 178 return $this->_renderBrewing($renderer, $data); 179 case 'crafting': 180 default: 181 return $this->_renderCrafting($renderer, $data); 182 } 183 } 184 185 protected function _renderCrafting($renderer, $data) { 186 $renderer->doc .= '<div class="minecraft-recipe">'; 187 $renderer->doc .= '<div class="crafting">'; 188 $renderer->doc .= '<div class="recipe">'; 189 $renderer->doc .= '<div class="table-grid">'; 190 191 if(isset($data['recipe'])) { 192 foreach($data['recipe'] as $row) { 193 foreach($row as $item) { 194 $renderer->doc .= '<span class="grid">'; 195 $renderer->doc .= $this->_renderItem($item); 196 $renderer->doc .= '</span>'; 197 } 198 } 199 } 200 201 $renderer->doc .= '</div></div>'; 202 $renderer->doc .= '<div class="arrow"></div>'; 203 $renderer->doc .= '<div class="crafting-table-output">'; 204 $renderer->doc .= '<span class="grid-large">'; 205 if(isset($data['result'])) { 206 $renderer->doc .= $this->_renderItem($data['result']); 207 } 208 $renderer->doc .= '</span></div></div></div>'; 209 210 return true; 211 } 212 213 protected function _renderSmelting($renderer, $data) { 214 $renderer->doc .= '<div class="minecraft-recipe smelting">'; 215 $renderer->doc .= '<div class="smelting-layout">'; 216 217 // input 218 $renderer->doc .= '<div class="input"><span class="grid">'; 219 $renderer->doc .= $this->_renderItem($data['input']); 220 $renderer->doc .= '</span></div>'; 221 222 // fire icon 223 $renderer->doc .= '<div class="fire"></div>'; 224 225 // fuel 226 $renderer->doc .= '<div class="fuel"><span class="grid">'; 227 $renderer->doc .= $this->_renderItem($data['fuel']); 228 $renderer->doc .= '</span></div>'; 229 230 // arrow 231 $renderer->doc .= '<div class="arrow"></div>'; 232 233 // result 234 $renderer->doc .= '<div class="output"><span class="grid-large">'; 235 $renderer->doc .= $this->_renderItem($data['result']); 236 $renderer->doc .= '</span></div>'; 237 238 $renderer->doc .= '</div></div>'; 239 return true; 240 } 241 242 protected function _renderItem($items) { 243 if(!is_array($items)) { 244 $items = array($items); 245 } 246 247 $allEmpty = true; 248 foreach($items as $item) { 249 $item = strtolower(trim($item)); 250 if($item !== 'empty' && $item !== 'x') { 251 $allEmpty = false; 252 break; 253 } 254 } 255 256 if($allEmpty) return ''; 257 258 $output = '<div class="item-cycle">'; 259 foreach($items as $index => $item) { 260 $item = trim($item); 261 if(strtolower($item) === 'x') continue; 262 if(strtolower($item) === 'empty') continue; 263 264 $localPath = DOKU_PLUGIN . 'minecraftrecipe/images/item/' . strtolower($item) . '.png'; 265 266 if(file_exists($localPath)) { 267 $src = DOKU_BASE . 'lib/plugins/minecraftrecipe/images/item/' . strtolower($item) . '.png'; 268 } else { 269 $wikiItem = str_replace(' ', '_', $item); 270 $src = 'https://minecraft.wiki/images/Invicon_' . $wikiItem . '.png'; // fallback to minecraft.wiki 271 } 272 273 $visibility = ($index === 0) ? '' : ' style="display: none;"'; 274 $output .= '<img src="'.hsc($src).'" alt="'.hsc($item).'" title="'.hsc($item).'" class="item-icon cycle-item"'.$visibility.' data-cycle-index="'.$index.'" />'; 275 } 276 $output .= '</div>'; 277 278 return $output; 279 } 280}