├── README.md ├── plugin.yml └── src └── FNPC ├── Main.php ├── SystemProvider.php ├── Tasks └── QuickSystemTask.php ├── Utils ├── Converter.php └── PNG.php └── npc ├── CommandNPC.php ├── NPC.php ├── ReplyNPC.php └── TeleportNPC.php /README.md: -------------------------------------------------------------------------------- 1 | # FNPC 2 | 一款优秀的NPC插件,适合大部分服务器使用
3 | This is a great NPC plugin.
4 |
5 | 使用方法请输入/fnpc help查看
6 | Type /fnpc help to display help message.
7 |
8 | [捐赠链接](http://pl.zxda.net/plugins/11.html)
9 | [Donate link](http://pl.zxda.net/plugins/11.html)
10 |
11 | 注意,捐赠版和此版本无任何区别
12 | Donate version is same to this version.
13 |
14 | 开源版本遵循GPL v3(捐赠版不在此范围内)
15 | This version follow GPL v3 (Donate version NOT follow it).
16 | 17 | # 联系方式 18 | QQ:765569811,1943601164
19 | E-Mail:FENGberd@gmail.com
20 | 21 | # 其他 22 | 如果你有兴趣请看提交记录ad3fbc9b09的[README.md](https://github.com/fengberd/FNPC/blob/ad3fbc9b0909680019ccb7f504207ecf37d0eb1c/README.md)文件 23 | 24 | -------------------------------------------------------------------------------- /plugin.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2016 FENGberd.All rights reserved. 2 | # GitHub Project: 3 | # https://github.com/fengberd/FNPC 4 | 5 | name: FNPC 6 | main: FNPC\Main 7 | version: 1.1.8 8 | api: [1.0.0] 9 | load: POSTWORLD 10 | author: "FENGberd" 11 | commands: 12 | fnpc: 13 | aliases: ["npc"] 14 | permission: FNPC.command.fnpc 15 | description: "NPC系统指令" 16 | usage: "使用 /fnpc help 查看帮助" 17 | permissions: 18 | FNPC.*: 19 | description: "根权限" 20 | default: op 21 | FNPC.command.*: 22 | description: "使用命令权限" 23 | default: op 24 | FNPC.command.fnpc: 25 | description: "NPC命令使用权限" 26 | default: op 27 | 28 | -------------------------------------------------------------------------------- /src/FNPC/Main.php: -------------------------------------------------------------------------------- 1 | isAbstract() && (!isset(self::$registeredNPC[$name]) || $force)) 48 | { 49 | self::$registeredNPC[$name]=array($className,$description); 50 | NPC::reloadUnknownNPC(); 51 | unset($className,$class,$force,$name,$description); 52 | return true; 53 | } 54 | unset($className,$class,$force,$name,$description); 55 | return false; 56 | } 57 | 58 | public function onEnable() 59 | { 60 | $start=microtime(true); 61 | if(!self::$obj instanceof Main) 62 | { 63 | self::$obj=$this; 64 | self::registerNpc('normal','普通NPC(无实际功能)',NPC::class,true); 65 | self::registerNpc('reply','回复型NPC(使用/fnpc chat)',ReplyNPC::class,true); 66 | self::registerNpc('command','指令型NPC(使用/fnpc command)',CommandNPC::class,true); 67 | self::registerNpc('teleport','传送型NPC(使用/fnpc teleport或/fnpc transfer)',TeleportNPC::class,true); 68 | } 69 | $base='\\pocketmine\\entity\\Entity::'; 70 | if(!defined($base.'DATA_NAMETAG') || !defined($base.'DATA_FLAGS') || !defined($base.'DATA_FLAG_CAN_SHOW_NAMETAG') || !defined($base.'DATA_FLAG_ALWAYS_SHOW_NAMETAG')) 71 | { 72 | $this->getLogger()->warning('当前核心存在奇葩问题,将导致无法正常显示NPC名称'); 73 | } 74 | if(defined($base.'DATA_LEAD_HOLDER') && !class_exists('\\pocketmine\\network\\protocol\\SetEntityLinkPacket',false)) 75 | { 76 | $this->getLogger()->warning('你这奇葩核心删掉了SetEntityLink包,我不敢保证在玩家和NPC之间不会出现奇怪的绳子'); 77 | } 78 | $reflect=new \ReflectionClass('\\pocketmine\\entity\\Entity'); 79 | $reflect=$reflect->getDefaultProperties(); 80 | if(!isset($reflect['dataProperties'])) 81 | { 82 | throw new \Exception('抱歉,你正在使用一个FNPC无法兼容的智障核心'); 83 | } 84 | NPC::$metadata=$reflect['dataProperties']; 85 | SystemProvider::init($this); 86 | NPC::init(); 87 | 88 | $this->initTasks(); 89 | $this->getServer()->getPluginManager()->registerEvents($this,$this); 90 | $this->getLogger()->info(TextFormat::GREEN.'NPC数据加载完毕,耗时'.(microtime(true)-$start).'秒'); 91 | } 92 | 93 | public function initTasks() 94 | { 95 | $this->quickSystemTask=new Tasks\QuickSystemTask($this); 96 | $this->getServer()->getScheduler()->scheduleRepeatingTask($this->quickSystemTask,1); 97 | } 98 | 99 | public function onCommand(\pocketmine\command\CommandSender $sender,\pocketmine\command\Command $command,$label,array $args) 100 | { 101 | unset($command,$label); 102 | if(!isset($args[0])) 103 | { 104 | unset($sender,$args); 105 | return false; 106 | } 107 | if(isset($args[1]) && is_numeric($args[1])) 108 | { 109 | $sender->sendMessage('[NPC] '.TextFormat::YELLOW.'纯数字ID会导致无法找到NPC,请使用英文/中文/混合ID'); 110 | } 111 | switch($args[0]) 112 | { 113 | case 'type': 114 | $data=TextFormat::GREEN.'=========='.TextFormat::YELLOW.'FNPC Type List'.TextFormat::GREEN.'=========='; 115 | foreach(self::$registeredNPC as $key=>$val) 116 | { 117 | $data.="\n".TextFormat::YELLOW.$key.TextFormat::WHITE.' - '.TextFormat::AQUA.$val[1]; 118 | unset($key,$val); 119 | } 120 | $sender->sendMessage($data); 121 | unset($data); 122 | break; 123 | case 'add': 124 | if(!isset($args[3])) 125 | { 126 | unset($sender,$args); 127 | return false; 128 | } 129 | if(isset(NPC::$pool[$args[2]])) 130 | { 131 | $sender->sendMessage('[NPC] '.TextFormat::RED.'已存在同ID的NPC'); 132 | break; 133 | } 134 | $args[1]=strtolower($args[1]); 135 | if(!isset(self::$registeredNPC[$args[1]])) 136 | { 137 | $sender->sendMessage('[NPC] '.TextFormat::RED.'指定类型不存在 ,请使用 /fnpc type 查看可用类型'); 138 | break; 139 | } 140 | $npc=new self::$registeredNPC[$args[1]][0]($args[2],$args[3],$sender->x,$sender->y,$sender->z); 141 | $npc->level=$sender->getLevel()->getFolderName(); 142 | $npc->spawnToAll(); 143 | $npc->save(); 144 | unset($npc); 145 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'NPC创建成功'); 146 | break; 147 | case 'transfer': 148 | if(!isset($args[3])) 149 | { 150 | unset($sender,$args); 151 | return false; 152 | } 153 | if(!isset(NPC::$pool[$args[1]])) 154 | { 155 | $sender->sendMessage('[NPC] '.TextFormat::RED.'不存在此NPC'); 156 | break; 157 | } 158 | if(!NPC::$pool[$args[1]] instanceof TeleportNPC) 159 | { 160 | $sender->sendMessage('[NPC] '.TextFormat::RED.'该NPC不是传送型NPC'); 161 | break; 162 | } 163 | NPC::$pool[$args[1]]->setTeleport(array( 164 | 'ip'=>$args[2], 165 | 'port'=>$args[3] 166 | )); 167 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'NPC传送点设置成功'); 168 | break; 169 | case 'remove': 170 | if(!isset($args[1])) 171 | { 172 | unset($sender,$args); 173 | return false; 174 | } 175 | if(!isset(NPC::$pool[$args[1]])) 176 | { 177 | $sender->sendMessage('[NPC] '.TextFormat::RED.'不存在此NPC'); 178 | break; 179 | } 180 | NPC::$pool[$args[1]]->close(); 181 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'移除成功'); 182 | break; 183 | case 'reset': 184 | if(!isset($args[1])) 185 | { 186 | unset($sender,$args); 187 | return false; 188 | } 189 | if(!isset(NPC::$pool[$args[1]])) 190 | { 191 | $sender->sendMessage('[NPC] '.TextFormat::RED.'不存在此NPC'); 192 | break; 193 | } 194 | $npc=NPC::$pool[$args[1]]; 195 | if($npc instanceof TeleportNPC) 196 | { 197 | $npc->setTeleport(false); 198 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'NPC传送点移除成功'); 199 | } 200 | else if($npc instanceof CommandNPC) 201 | { 202 | $npc->command=array(); 203 | $npc->save(); 204 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'NPC指令清空成功'); 205 | } 206 | break; 207 | case 'teleport': 208 | if(!isset($args[1])) 209 | { 210 | unset($sender,$args); 211 | return false; 212 | } 213 | if(!isset(NPC::$pool[$args[1]])) 214 | { 215 | $sender->sendMessage('[NPC] '.TextFormat::RED.'不存在此NPC'); 216 | break; 217 | } 218 | if(!NPC::$pool[$args[1]] instanceof TeleportNPC) 219 | { 220 | $sender->sendMessage('[NPC] '.TextFormat::RED.'该NPC不是传送型NPC'); 221 | break; 222 | } 223 | NPC::$pool[$args[1]]->setTeleport($sender); 224 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'NPC传送点设置成功'); 225 | break; 226 | case 'command': 227 | if(!isset($args[2])) 228 | { 229 | unset($sender,$args); 230 | return false; 231 | } 232 | if(!isset(NPC::$pool[$args[1]])) 233 | { 234 | $sender->sendMessage('[NPC] '.TextFormat::RED.'不存在此NPC'); 235 | break; 236 | } 237 | if(!NPC::$pool[$args[1]] instanceof CommandNPC) 238 | { 239 | $sender->sendMessage('[NPC] '.TextFormat::RED.'该NPC不是指令型NPC'); 240 | break; 241 | } 242 | switch($args[2]) 243 | { 244 | case 'add': 245 | if(!isset($args[3])) 246 | { 247 | unset($sender,$args); 248 | return false; 249 | } 250 | $cmd=''; 251 | for($i=3;$iaddCommand($cmd); 261 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'NPC指令添加成功'); 262 | break; 263 | case 'remove': 264 | if(!isset($args[3])) 265 | { 266 | unset($sender,$args); 267 | return false; 268 | } 269 | $cmd=''; 270 | for($i=3;$iremoveCommand($cmd)) 280 | { 281 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'NPC指令移除成功'); 282 | } 283 | else 284 | { 285 | $sender->sendMessage('[NPC] '.TextFormat::RED.'NPC未添加该指令'); 286 | } 287 | break; 288 | case 'list': 289 | $msg=TextFormat::GREEN.'===NPC指令列表==='."\n"; 290 | foreach(NPC::$pool[$args[1]]->command as $cmd) 291 | { 292 | $msg.=TextFormat::YELLOW.$cmd."\n"; 293 | unset($cmd); 294 | } 295 | $sender->sendMessage($msg); 296 | unset($msg); 297 | break; 298 | default: 299 | unset($sender,$args); 300 | return false; 301 | } 302 | break; 303 | case 'chat': 304 | if(!isset($args[2])) 305 | { 306 | unset($sender,$args); 307 | return false; 308 | } 309 | if(!isset(NPC::$pool[$args[1]])) 310 | { 311 | $sender->sendMessage('[NPC] '.TextFormat::RED.'不存在此NPC'); 312 | break; 313 | } 314 | if(!NPC::$pool[$args[1]] instanceof ReplyNPC) 315 | { 316 | $sender->sendMessage('[NPC] '.TextFormat::RED.'该NPC不是回复型NPC'); 317 | break; 318 | } 319 | switch($args[2]) 320 | { 321 | case 'add': 322 | if(!isset($args[3])) 323 | { 324 | unset($sender,$args); 325 | return false; 326 | } 327 | $cmd=''; 328 | for($i=3;$iaddChat($cmd); 338 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'NPC对话数据添加成功'); 339 | break; 340 | case 'remove': 341 | if(!isset($args[3])) 342 | { 343 | unset($sender,$args); 344 | return false; 345 | } 346 | $cmd=''; 347 | for($i=3;$iremoveChat($cmd)) 357 | { 358 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'NPC对话数据移除成功'); 359 | } 360 | else 361 | { 362 | $sender->sendMessage('[NPC] '.TextFormat::RED.'NPC未添加该对话数据'); 363 | } 364 | break; 365 | default: 366 | unset($sender,$args); 367 | return false; 368 | } 369 | break; 370 | case 'name': 371 | if(!isset($args[2])) 372 | { 373 | unset($sender,$args); 374 | return false; 375 | } 376 | if(!isset(NPC::$pool[$args[1]])) 377 | { 378 | $sender->sendMessage('[NPC] '.TextFormat::RED.'不存在此NPC'); 379 | break; 380 | } 381 | NPC::$pool[$args[1]]->setName($args[2]); 382 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'NameTag设置成功'); 383 | break; 384 | case 'skin': 385 | if(!isset($args[2])) 386 | { 387 | unset($sender,$args); 388 | return false; 389 | } 390 | if(!isset(NPC::$pool[$args[1]])) 391 | { 392 | $sender->sendMessage('[NPC] '.TextFormat::RED.'不存在此NPC'); 393 | break; 394 | } 395 | switch(NPC::$pool[$args[1]]->setPNGSkin($args[2],false)) 396 | { 397 | case 0: 398 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'皮肤更换成功'); 399 | break; 400 | case -1: 401 | $sender->sendMessage('[NPC] '.TextFormat::RED.'皮肤文件不存在,请检查输入的路径是否正确,需要加.png'); 402 | break; 403 | case -2: 404 | $sender->sendMessage('[NPC] '.TextFormat::RED.'皮肤文件无效,请使用MCPE可以正常加载的png皮肤'); 405 | break; 406 | case -3: 407 | default: 408 | $sender->sendMessage('[NPC] '.TextFormat::RED.'未知错误,请检查皮肤路径是否正确以及是否可以在MCPE正常使用'); 409 | break; 410 | } 411 | break; 412 | case 'item': 413 | if(!isset($args[2])) 414 | { 415 | unset($sender,$args); 416 | return false; 417 | } 418 | if(!isset(NPC::$pool[$args[1]])) 419 | { 420 | $sender->sendMessage('[NPC] '.TextFormat::RED.'不存在此NPC'); 421 | break; 422 | } 423 | $item=explode(':',$args[2]); 424 | if(!isset($item[1])) 425 | { 426 | $item[1]=0; 427 | } 428 | $item[0]=intval($item[0]); 429 | $item[1]=intval($item[1]); 430 | NPC::$pool[$args[1]]->setHandItem(\pocketmine\item\Item::get($item[0],$item[1])); 431 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'手持物品更换成功'); 432 | break; 433 | case 'tphere': 434 | case 'teleporthere': 435 | if(!isset($args[1])) 436 | { 437 | unset($sender,$args); 438 | return false; 439 | } 440 | if(!isset(NPC::$pool[$args[1]])) 441 | { 442 | $sender->sendMessage('[NPC] '.TextFormat::RED.'不存在此NPC'); 443 | break; 444 | } 445 | NPC::$pool[$args[1]]->teleport($sender); 446 | $sender->sendMessage('[NPC] '.TextFormat::GREEN.'传送成功'); 447 | break; 448 | case 'help': 449 | $help=TextFormat::GREEN.'===NPC系统指令帮助==='."\n"; 450 | $help.=TextFormat::GREEN.'所有指令前面必须加/fnpc '."\n"; 451 | $help.=TextFormat::YELLOW.'add - 添加一个NPC'."\n"; 452 | $help.=TextFormat::YELLOW.'type - 列出可用的Type类型'."\n"; 453 | $help.=TextFormat::YELLOW.'remove - 移除一个NPC'."\n"; 454 | $help.=TextFormat::YELLOW.'skin - 设置NPC皮肤'."\n"; 455 | $help.=TextFormat::YELLOW.'name - 设置NPC名称'."\n"; 456 | $help.=TextFormat::YELLOW.'command - 添加/删除NPC指令'."\n"; 457 | $help.=TextFormat::YELLOW.'command list - 列出NPC指令'."\n"; 458 | $help.=TextFormat::YELLOW.'tphere - 把NPC传送过来'."\n"; 459 | $help.=TextFormat::YELLOW.'teleport - 设置NPC传送目标为你的位置'."\n"; 460 | $help.=TextFormat::YELLOW.'transfer - 设置NPC跨服传送'."\n"; 461 | $help.=TextFormat::YELLOW.'reset - 重置NPC的设置'."\n"; 462 | $help.=TextFormat::YELLOW.'chat - 添加/删除NPC对话数据'."\n"; 463 | $help.=TextFormat::YELLOW.'item - 设置NPC手持物品'."\n"; 464 | $help.=TextFormat::YELLOW.'help - 查看帮助'; 465 | $sender->sendMessage($help); 466 | unset($help); 467 | break; 468 | default: 469 | unset($sender,$args); 470 | return false; 471 | } 472 | unset($sender,$args); 473 | return true; 474 | } 475 | 476 | public function onPlayerMove(\pocketmine\event\player\PlayerMoveEvent $event) 477 | { 478 | NPC::playerMove($event->getPlayer()); 479 | unset($event); 480 | } 481 | 482 | public function onDataPacketReceive(\pocketmine\event\server\DataPacketReceiveEvent $event) 483 | { 484 | NPC::packetReceive($event->getPlayer(),$event->getPacket()); 485 | unset($event); 486 | } 487 | 488 | public function onPlayerJoin(\pocketmine\event\player\PlayerJoinEvent $event) 489 | { 490 | NPC::spawnAllTo($event->getPlayer()); 491 | unset($event); 492 | } 493 | 494 | public function onEntityLevelChange(\pocketmine\event\entity\EntityLevelChangeEvent $event) 495 | { 496 | if($event->getEntity() instanceof \pocketmine\Player) 497 | { 498 | NPC::spawnAllTo($event->getEntity(),$event->getTarget()); 499 | } 500 | unset($event); 501 | } 502 | } 503 | 504 | -------------------------------------------------------------------------------- /src/FNPC/SystemProvider.php: -------------------------------------------------------------------------------- 1 | getServer(); 19 | } 20 | 21 | public static function debug($message) 22 | { 23 | self::$plugin->getLogger()->debug($message); 24 | unset($message); 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/FNPC/Tasks/QuickSystemTask.php: -------------------------------------------------------------------------------- 1 | plugin=$plugin; 18 | } 19 | 20 | public function onRun($currentTick) 21 | { 22 | $this->plugin=$this->getOwner(); 23 | \FNPC\npc\NPC::tick(); 24 | } 25 | } 26 | ?> 27 | -------------------------------------------------------------------------------- /src/FNPC/Utils/Converter.php: -------------------------------------------------------------------------------- 1 | getDataFolder().'skins/cache/'.basename($path).'.cache')) 19 | { 20 | return file_get_contents(SystemProvider::$plugin->getDataFolder().'skins/cache/'.basename($path).'.cache'); 21 | } 22 | if(!is_file($path)) 23 | { 24 | return -1; 25 | } 26 | $png=new PNG($path); 27 | if($png->status===false) 28 | { 29 | return -2; 30 | } 31 | $png->decode(); 32 | $skin=''; 33 | foreach($png->color as $v) 34 | { 35 | foreach($v as $k) 36 | { 37 | $data=self::hex(array($k->r,$k->g,$k->b,$k->a)); 38 | $skin.=$data[0].$data[1].$data[2].$data[3]; 39 | unset($k); 40 | } 41 | unset($v); 42 | } 43 | unset($png,$useCache); 44 | file_put_contents(SystemProvider::$plugin->getDataFolder().'skins/cache/'.basename($path).'.cache',$skin); 45 | return $skin; 46 | } 47 | catch(\Exception $e) 48 | { 49 | return -3; 50 | } 51 | } 52 | 53 | private static function hex($data) 54 | { 55 | $arr=array(0,1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'); 56 | foreach($data as $num) 57 | { 58 | $t=""; 59 | $num=intval($num); 60 | if($num===0) 61 | { 62 | $aOutChar[]=hex2bin('00'); 63 | unset($t,$num); 64 | continue; 65 | } 66 | while($num>0) 67 | { 68 | $t=$arr[$num%16].$t; 69 | $num=floor($num/16); 70 | } 71 | if(strlen($t)==1) 72 | { 73 | $t='0'.$t; 74 | } 75 | $aOutChar[]=hex2bin($t); 76 | unset($t,$num); 77 | } 78 | unset($arr,$data); 79 | return $aOutChar; 80 | } 81 | 82 | public static function PPM2Skin($ppm) 83 | { 84 | if(substr($ppm,0,2)!=='P6') 85 | { 86 | SystemProvider::$plugin->getLogger()->info(\pocketmine\utils\TextFormat::RED.'错误的PPM图片,仅支持Binary格式'); 87 | unset($ppm); 88 | return false; 89 | } 90 | $stream=fopen('php://memory','r+'); 91 | fwrite($stream,$ppm); 92 | rewind($stream); 93 | fgets($stream); 94 | fgets($stream); 95 | fgets($stream); 96 | fgets($stream); 97 | $ppm=stream_get_contents($stream); 98 | fclose($stream); 99 | $offset=0; 100 | $str=''; 101 | while(isset($ppm{$offset})) 102 | { 103 | $str.=$ppm{$offset++}.$ppm{$offset++}.$ppm{$offset++}.chr(0); 104 | } 105 | unset($stream,$offset,$ppm); 106 | return $str; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/FNPC/Utils/PNG.php: -------------------------------------------------------------------------------- 1 | png=$png; 53 | if($this->png!=null && $this->read($this->png)!==false) 54 | { 55 | $this->status=true; 56 | } 57 | else 58 | { 59 | $this->status=false; 60 | } 61 | } 62 | 63 | //读取1字节 64 | public function readBits8() 65 | { 66 | $t=''; 67 | $t=unpack("C", fread($this->fp, 1)); 68 | return $t[1]; 69 | } 70 | 71 | //读取双字节 72 | public function readBH16() 73 | { 74 | $t=''; 75 | $t=unpack("n", fread($this->fp, 2)); 76 | return $t[1]; 77 | } 78 | 79 | //读取双字节 80 | public function readBits16() 81 | { 82 | $t=''; 83 | $t=unpack("S", fread($this->fp, 2)); 84 | return $t[1]; 85 | } 86 | 87 | //读取四字节 88 | public function readBH32() 89 | { 90 | $t=''; 91 | $t=unpack("N", fread($this->fp, 4)); 92 | return $t[1]; 93 | } 94 | 95 | //读取四字节 96 | public function readBits32() 97 | { 98 | $t=''; 99 | $t=unpack("L", fread($this->fp, 4)); 100 | return $t[1]; 101 | } 102 | 103 | public function version() 104 | { 105 | fseek($this->fp, 1); 106 | $this->info['version']=fread($this->fp, 3); 107 | return $this->info['version']; 108 | } 109 | 110 | public function seek($s) 111 | { 112 | fseek($this->fp, $s); 113 | return $this; 114 | } 115 | 116 | public function rBName() 117 | { 118 | return fread($this->fp, 4); 119 | } 120 | 121 | public function rSize($s) 122 | { 123 | return fread($this->fp, $s); 124 | } 125 | 126 | public function rCRC() 127 | { 128 | return $this->readBH32(); 129 | } 130 | 131 | //读取一个块 132 | public function rBlock() 133 | { 134 | $b=new ArrayObject(); 135 | $b->length=$this->readBH32(); 136 | $b->name=$this->rBName(); 137 | if ($b->length>0) { 138 | $b->data=$this->rSize($b->length); 139 | } else { 140 | $b->data=''; 141 | } 142 | $b->crc=$this->rCRC(); 143 | if (crc32($b->name . $b->data)==$b->crc) { 144 | $b->checkcrc=true; 145 | } else { 146 | $b->checkcrc=false; 147 | } 148 | return $b; 149 | } 150 | 151 | //IHDR文件头数据块 152 | public function pIHDR($obj) 153 | { 154 | //宽度四字节 155 | $t=unpack("N", substr($obj->data, 0, 4)); 156 | $this->info['width']=$t[1]; 157 | //高度四字节 158 | $t=unpack("N", substr($obj->data, 4, 4)); 159 | $this->info['height']=$t[1]; 160 | 161 | /* 162 | * Bit depth 1 byte 图像深度: 163 | * 索引彩色图像:1,2,4或8 164 | * 灰度图像:1,2,4,8或16 165 | * 真彩色图像:8或16 166 | */ 167 | $t=unpack("C", substr($obj->data, 8, 1)); 168 | $this->info['bitdepth']=$t[1]; 169 | 170 | /* 171 | * ColorType 1 byte 颜色类型: 172 | * 0:灰度图像, 1,2,4,8或16 173 | * 2:真彩色图像,8或16 174 | * 3:索引彩色图像,1,2,4或8 175 | * 4:带α通道数据的灰度图像,8或16 176 | * 6:带α通道数据的真彩色图像,8或16 177 | */ 178 | $t=unpack("C", substr($obj->data, 9, 1)); 179 | $this->info['colortype']=$t[1]; 180 | 181 | //lz77的算法 182 | $t=unpack("C", substr($obj->data, 10, 1)); 183 | $this->info['lz77']=$t[1]; 184 | 185 | //滤波器方法 186 | $t=unpack("C", substr($obj->data, 11, 1)); 187 | $this->info['filter']=$t[1]; 188 | 189 | //是否隔行扫描 190 | $t=unpack("C", substr($obj->data, 12, 1)); 191 | $this->info['interlace']=$t[1]; 192 | return $this; 193 | } 194 | 195 | //样本有效位数据块 196 | public function psBit($obj) 197 | { 198 | $this->info['sbit']=array(); 199 | for ($i=0; $i<$obj->length; $i++) { 200 | $t=unpack("C", substr($obj->data, $i, 1)); 201 | $this->info['sbit'][$i]=$t[1]; 202 | } 203 | return $this; 204 | } 205 | 206 | //调调色板读取 207 | public function pPLTE($obj) 208 | { 209 | $this->info['plte']=array(); 210 | $j=0; 211 | for ($i=0; $i<$obj->length; $i+=3) { 212 | $r=unpack("C", substr($obj->data, $i, 1)); 213 | $this->info['plte'][$j]['r']=$r[1]; 214 | $g=unpack("C", substr($obj->data, $i+1, 1)); 215 | $this->info['plte'][$j]['g']=$g[1]; 216 | $b=unpack("C", substr($obj->data, $i+2, 1)); 217 | $this->info['plte'][$j]['b']=$b[1]; 218 | $this->info['plte'][$j]['rgb']=$this->rgbHex($r[1]) . $this->rgbHex($g[1]) . $this->rgbHex($b[1]); 219 | $j++; 220 | } 221 | return $this; 222 | } 223 | 224 | //图像透明数据块 225 | public function ptRNS($obj) 226 | { 227 | $this->info['trns']=array(); 228 | for ($i=0; $i<$obj->length; $i++) { 229 | $t=unpack("C", substr($obj->data, $i, 1)); 230 | $this->info['trns'][$i]=$t[1]; 231 | } 232 | return $this; 233 | } 234 | 235 | //物理像素尺寸数据块 236 | public function ppHYs($obj) 237 | { 238 | $this->info['phys']=array(); 239 | $t=unpack("N", substr($obj->data, 0, 4)); 240 | $this->info['phys']['x']=$t[1]; 241 | $t=unpack("N", substr($obj->data, 4, 4)); 242 | $this->info['phys']['y']=$t[1]; 243 | $t=unpack("C", substr($obj->data, 8, 1)); 244 | $this->info['phys']['unit']=$t[1]; 245 | return $this; 246 | } 247 | 248 | /* 249 | * 文本信息数据块 250 | * 分隔行为02的话 是用自已的值去xor上面相同位置的值 251 | * 00 1A50B8FF 000003FF 99CC33FF 252 | * 如果是01的话用后面的数据加上前边的RGB 253 | * 用02数据RGB加上上面数据 254 | * 03 为上一行数据除以2+本行数据 RGBA分别运算 255 | * 如果不是第一列,那么本列前面的对应的字节加上一行对应的字节然后平均; 256 | * 257 | * 04 左边加上上边减去左上角,得到的值再减去(左边,上边,左上边 那个值最小就取那个。 258 | * 02 00000000 0000FC00 00000000 259 | */ 260 | public function ptEXt($obj) 261 | { 262 | $this->info['create']=$obj->data; 263 | return $this; 264 | } 265 | 266 | //图像数据块 267 | public function pIDAT($obj) 268 | { 269 | $this->data=gzuncompress($obj->data); 270 | $this->seek=0; 271 | return $this; 272 | } 273 | 274 | //物理像素尺寸数据块 275 | public function pcHRM($obj) 276 | { 277 | $this->info['chrm']=array(); 278 | $t=unpack("N", substr($obj->data, 0, 4)); 279 | $this->info['chrm']['wx']=$t[1]; 280 | $t=unpack("N", substr($obj->data, 4, 4)); 281 | $this->info['chrm']['wy']=$t[1]; 282 | $t=unpack("N", substr($obj->data, 8, 4)); 283 | $this->info['chrm']['rx']=$t[1]; 284 | $t=unpack("N", substr($obj->data, 12, 4)); 285 | $this->info['chrm']['ry']=$t[1]; 286 | $t=unpack("N", substr($obj->data, 16, 4)); 287 | $this->info['chrm']['gx']=$t[1]; 288 | $t=unpack("N", substr($obj->data, 20, 4)); 289 | $this->info['chrm']['gy']=$t[1]; 290 | $t=unpack("N", substr($obj->data, 24, 4)); 291 | $this->info['chrm']['bx']=$t[1]; 292 | $t=unpack("N", substr($obj->data, 28, 4)); 293 | $this->info['chrm']['by']=$t[1]; 294 | return $this; 295 | } 296 | 297 | //读取文件信息 298 | public function read($png=null) 299 | { 300 | if ($this->png=null) 301 | $this->png=$png; 302 | $this->fp=fopen($png,'rb'); 303 | if ($this->readBits8()==0x89 && $this->version()=='PNG' && $this->readBH32()==0x0D0A1A0A) { 304 | do { 305 | $obj=$this->rBlock(); 306 | switch ($obj->name) { 307 | case 'IHDR': 308 | $this->pIHDR($obj); 309 | break; 310 | case 'sBIT': 311 | $this->psBit($obj); 312 | break; 313 | case 'PLTE': 314 | $this->pPLTE($obj); 315 | break; 316 | case 'tRNS': 317 | $this->ptRNS($obj); 318 | break; 319 | case 'pHYs': 320 | $this->ppHYs($obj); 321 | break; 322 | case 'cHRM': 323 | $this->pcHRM($obj); 324 | break; 325 | case 'tEXt': 326 | $this->ptEXt($obj); 327 | break; 328 | case 'IDAT': 329 | $this->pIDAT($obj); 330 | break; 331 | case 'IEND': 332 | break; 333 | 334 | } 335 | } while (!feof($this->fp) && $obj->name!='IEND'); 336 | fclose($this->fp); 337 | return true; 338 | } 339 | else 340 | { 341 | return false; 342 | } 343 | } 344 | 345 | //解码文件 346 | function decode() 347 | { 348 | /* 349 | 颜色类型: 350 | 0:灰度图像, 1,2,4,8或16 351 | 2:真彩色图像,8或16 352 | 3:索引彩色图像,1,2,4或8 353 | 4:带α通道数据的灰度图像,8或16 354 | 6:带α通道数据的真彩色图像,8或16 355 | */ 356 | switch ($this->info['colortype']) { 357 | case '0': 358 | break; 359 | case '2': 360 | $this->png24row(); 361 | break; 362 | case '3': 363 | $this->indexrow(); 364 | break; 365 | case '4': 366 | break; 367 | case '6': 368 | $this->png32row(); 369 | break; 370 | } 371 | 372 | } 373 | 374 | //png32真彩读取 375 | function png32row() 376 | { 377 | $tcolor=array(); 378 | $rh=array(); 379 | for ($ii=0; $ii<$this->info['height']; $ii++) { 380 | $rh[$ii]=$this->rRowHeader(); 381 | for ($i=0; $i<$this->info['width']; $i++) { 382 | $t=$this->rRGBA(); 383 | $tcolor[$ii][$i]=$t; 384 | switch ($rh[$ii]) { 385 | case '0': 386 | $this->color[$ii][$i]=new ArrayObject(); 387 | $this->color[$ii][$i]->r=$t->r; 388 | $this->color[$ii][$i]->g=$t->g; 389 | $this->color[$ii][$i]->b=$t->b; 390 | $this->color[$ii][$i]->a=$t->a; 391 | break; 392 | case '1': 393 | if ($i==0) { 394 | $this->color[$ii][$i]=new ArrayObject(); 395 | $this->color[$ii][$i]->r=$t->r; 396 | $this->color[$ii][$i]->g=$t->g; 397 | $this->color[$ii][$i]->b=$t->b; 398 | $this->color[$ii][$i]->a=$t->a; 399 | } else { 400 | $this->color[$ii][$i]=new ArrayObject(); 401 | $this->color[$ii][$i]->r=$this->filt1($t->r, $this->color[$ii][$i-1]->r); 402 | $this->color[$ii][$i]->g=$this->filt1($t->g, $this->color[$ii][$i-1]->g); 403 | $this->color[$ii][$i]->b=$this->filt1($t->b, $this->color[$ii][$i-1]->b); 404 | $this->color[$ii][$i]->a=$this->filt1($t->a, $this->color[$ii][$i-1]->a); 405 | } 406 | 407 | break; 408 | case '2': 409 | $this->color[$ii][$i]=new ArrayObject(); 410 | $this->color[$ii][$i]->r=$this->filt2($t->r, $this->color[$ii-1][$i]->r); 411 | $this->color[$ii][$i]->g=$this->filt2($t->g, $this->color[$ii-1][$i]->g); 412 | $this->color[$ii][$i]->b=$this->filt2($t->b, $this->color[$ii-1][$i]->b); 413 | $this->color[$ii][$i]->a=$this->filt2($t->a, $this->color[$ii-1][$i]->a); 414 | break; 415 | case '3': 416 | if ($i==0) { 417 | $this->color[$ii][$i]=new ArrayObject(); 418 | $this->color[$ii][$i]->r=$this->filt3($t->r, $this->color[$ii-1][$i]->r, 0); 419 | $this->color[$ii][$i]->g=$this->filt3($t->g, $this->color[$ii-1][$i]->g, 0); 420 | $this->color[$ii][$i]->b=$this->filt3($t->b, $this->color[$ii-1][$i]->b, 0); 421 | $this->color[$ii][$i]->a=$this->filt3($t->a, $this->color[$ii-1][$i]->a, 0); 422 | } else { 423 | $this->color[$ii][$i]=new ArrayObject(); 424 | $this->color[$ii][$i]->r=$this->filt3($t->r, $this->color[$ii-1][$i]->r, $this->color[$ii][$i-1]->r); 425 | $this->color[$ii][$i]->g=$this->filt3($t->g, $this->color[$ii-1][$i]->g, $this->color[$ii][$i-1]->g); 426 | $this->color[$ii][$i]->b=$this->filt3($t->b, $this->color[$ii-1][$i]->b, $this->color[$ii][$i-1]->b); 427 | $this->color[$ii][$i]->a=$this->filt3($t->a, $this->color[$ii-1][$i]->a, $this->color[$ii][$i-1]->a); 428 | } 429 | break; 430 | case '4': 431 | if ($i==0) { 432 | $this->color[$ii][$i]=new ArrayObject(); 433 | $this->color[$ii][$i]->r=$this->filt4($t->r, $this->color[$ii-1][$i]->r, $this->color[$ii-1][$i]->r, 0); 434 | $this->color[$ii][$i]->g=$this->filt4($t->g, $this->color[$ii-1][$i]->g, $this->color[$ii-1][$i]->g, 0); 435 | $this->color[$ii][$i]->b=$this->filt4($t->b, $this->color[$ii-1][$i]->b, $this->color[$ii-1][$i]->b, 0); 436 | $this->color[$ii][$i]->a=$this->filt4($t->a, $this->color[$ii-1][$i]->a, $this->color[$ii-1][$i]->a, 0); 437 | } else { 438 | $this->color[$ii][$i]=new ArrayObject(); 439 | $this->color[$ii][$i]->r=$this->filt4($t->r, $this->color[$ii-1][$i]->r, $this->color[$ii][$i-1]->r, $this->color[$ii-1][$i-1]->r); 440 | $this->color[$ii][$i]->g=$this->filt4($t->g, $this->color[$ii-1][$i]->g, $this->color[$ii][$i-1]->g, $this->color[$ii-1][$i-1]->g); 441 | $this->color[$ii][$i]->b=$this->filt4($t->b, $this->color[$ii-1][$i]->b, $this->color[$ii][$i-1]->b, $this->color[$ii-1][$i-1]->b); 442 | $this->color[$ii][$i]->a=$this->filt4($t->a, $this->color[$ii-1][$i]->a, $this->color[$ii][$i-1]->a, $this->color[$ii-1][$i-1]->a); 443 | } 444 | break; 445 | } 446 | } 447 | } 448 | } 449 | 450 | //png24 真彩读取 451 | function png24row() 452 | { 453 | $tcolor=array(); 454 | $rh=array(); 455 | for ($ii=0; $ii<$this->info['height']; $ii++) { 456 | $rh[$ii]=$this->rRowHeader(); 457 | for ($i=0; $i<$this->info['width']; $i++) { 458 | $t=$this->rRGB(); 459 | $tcolor[$ii][$i]=$t; 460 | switch ($rh[$ii]) { 461 | case '0': 462 | $this->color[$ii][$i]=new ArrayObject(); 463 | $this->color[$ii][$i]->r=$t->r; 464 | $this->color[$ii][$i]->g=$t->g; 465 | $this->color[$ii][$i]->b=$t->b; 466 | break; 467 | case '1': 468 | if ($i==0) { 469 | $this->color[$ii][$i]=new ArrayObject(); 470 | $this->color[$ii][$i]->r=$t->r; 471 | $this->color[$ii][$i]->g=$t->g; 472 | $this->color[$ii][$i]->b=$t->b; 473 | } else { 474 | $this->color[$ii][$i]=new ArrayObject(); 475 | $this->color[$ii][$i]->r=$this->filt1($t->r, $this->color[$ii][$i-1]->r); 476 | $this->color[$ii][$i]->g=$this->filt1($t->g, $this->color[$ii][$i-1]->g); 477 | $this->color[$ii][$i]->b=$this->filt1($t->b, $this->color[$ii][$i-1]->b); 478 | } 479 | 480 | break; 481 | case '2': 482 | $this->color[$ii][$i]=new ArrayObject(); 483 | $this->color[$ii][$i]->r=$this->filt2($t->r, $this->color[$ii-1][$i]->r); 484 | $this->color[$ii][$i]->g=$this->filt2($t->g, $this->color[$ii-1][$i]->g); 485 | $this->color[$ii][$i]->b=$this->filt2($t->b, $this->color[$ii-1][$i]->b); 486 | break; 487 | case '3': 488 | if ($i==0) { 489 | $this->color[$ii][$i]=new ArrayObject(); 490 | $this->color[$ii][$i]->r=$this->filt3($t->r, $this->color[$ii-1][$i]->r, 0); 491 | $this->color[$ii][$i]->g=$this->filt3($t->g, $this->color[$ii-1][$i]->g, 0); 492 | $this->color[$ii][$i]->b=$this->filt3($t->b, $this->color[$ii-1][$i]->b, 0); 493 | } else { 494 | $this->color[$ii][$i]=new ArrayObject(); 495 | $this->color[$ii][$i]->r=$this->filt3($t->r, $this->color[$ii-1][$i]->r, $this->color[$ii][$i-1]->r); 496 | $this->color[$ii][$i]->g=$this->filt3($t->g, $this->color[$ii-1][$i]->g, $this->color[$ii][$i-1]->g); 497 | $this->color[$ii][$i]->b=$this->filt3($t->b, $this->color[$ii-1][$i]->b, $this->color[$ii][$i-1]->b); 498 | } 499 | break; 500 | case '4': 501 | if ($i==0) { 502 | $this->color[$ii][$i]=new ArrayObject(); 503 | $this->color[$ii][$i]->r=$this->filt4($t->r, $this->color[$ii-1][$i]->r, $this->color[$ii-1][$i]->r, 0); 504 | $this->color[$ii][$i]->g=$this->filt4($t->g, $this->color[$ii-1][$i]->g, $this->color[$ii-1][$i]->g, 0); 505 | $this->color[$ii][$i]->b=$this->filt4($t->b, $this->color[$ii-1][$i]->b, $this->color[$ii-1][$i]->b, 0); 506 | } else { 507 | $this->color[$ii][$i]=new ArrayObject(); 508 | $this->color[$ii][$i]->r=$this->filt4($t->r, $this->color[$ii-1][$i]->r, $this->color[$ii][$i-1]->r, $this->color[$ii-1][$i-1]->r); 509 | $this->color[$ii][$i]->g=$this->filt4($t->g, $this->color[$ii-1][$i]->g, $this->color[$ii][$i-1]->g, $this->color[$ii-1][$i-1]->g); 510 | $this->color[$ii][$i]->b=$this->filt4($t->b, $this->color[$ii-1][$i]->b, $this->color[$ii][$i-1]->b, $this->color[$ii-1][$i-1]->b); 511 | } 512 | break; 513 | } 514 | } 515 | } 516 | } 517 | 518 | //索引色读取一行 519 | function indexrow() 520 | { 521 | for ($ii=0; $ii<$this->info['height']; $ii++) { 522 | $rowchar=ceil($this->info['width']*($this->info['bitdepth'])/8); 523 | $rowheader=$this->rRowHeader(); 524 | //一行的偏移 525 | $this->wseek=0; 526 | $rowdata=substr($this->data, $this->seek, $rowchar); 527 | $this->seek+=$rowchar; 528 | $cdata=''; 529 | for ($i=strlen($rowdata)-1; $i>=0; $i--) { 530 | $t=unpack("C", $rowdata[$i]); 531 | $cdata=str_pad(decbin($t[1]), 8, "0", STR_PAD_LEFT) . $cdata; 532 | } 533 | for ($i=0; $i<$this->info['width']; $i++) { 534 | $this->color[$ii][$i]=new ArrayObject(); 535 | $this->color[$ii][$i]->r=$this->info['plte'][bindec(substr($cdata, $this->wseek, $this->info['bitdepth']))]['r']; 536 | $this->color[$ii][$i]->g=$this->info['plte'][bindec(substr($cdata, $this->wseek, $this->info['bitdepth']))]['g']; 537 | $this->color[$ii][$i]->b=$this->info['plte'][bindec(substr($cdata, $this->wseek, $this->info['bitdepth']))]['b']; 538 | $this->wseek+=$this->info['bitdepth']; 539 | } 540 | 541 | } 542 | } 543 | 544 | /* 545 | * 读取每行分隔 546 | * 32 24 位png时候可能会有 01 02 03 04等目前还不知道是什么 547 | * 别的程序生成没有,但是firework生成有,可能跟透明度有关和重复有关 548 | */ 549 | function rRowHeader() 550 | { 551 | $t=unpack("C", substr($this->data, $this->seek, 1)); 552 | $this->seek+=1; 553 | return $t[1]; 554 | } 555 | 556 | function rBits() 557 | { 558 | $t=unpack("C", substr($this->data, $this->seek, 1)); 559 | $this->seek+=1; 560 | return $t[1]; 561 | } 562 | 563 | function rBitC() 564 | { 565 | $t=unpack("n", substr($this->data, $this->seek, 2)); 566 | $this->seek+=2; 567 | return $t[1]; 568 | } 569 | 570 | function rRGB() 571 | { 572 | $rgb=new ArrayObject(); 573 | $t=unpack("C", substr($this->data, $this->seek, 1)); 574 | $this->seek+=1; 575 | $rgb->r=$t[1]; 576 | $t=unpack("C", substr($this->data, $this->seek, 1)); 577 | $this->seek+=1; 578 | $rgb->g=$t[1]; 579 | $t=unpack("C", substr($this->data, $this->seek, 1)); 580 | $this->seek+=1; 581 | $rgb->b=$t[1]; 582 | 583 | return $rgb; 584 | } 585 | 586 | function rRGBA() 587 | { 588 | $rgb=new ArrayObject(); 589 | $t=unpack("C", substr($this->data, $this->seek, 1)); 590 | $this->seek+=1; 591 | $rgb->r=$t[1]; 592 | $t=unpack("C", substr($this->data, $this->seek, 1)); 593 | $this->seek+=1; 594 | $rgb->g=$t[1]; 595 | $t=unpack("C", substr($this->data, $this->seek, 1)); 596 | $this->seek+=1; 597 | $rgb->b=$t[1]; 598 | $t=unpack("C", substr($this->data, $this->seek, 1)); 599 | $this->seek+=1; 600 | $rgb->a=$t[1]; 601 | 602 | return $rgb; 603 | } 604 | 605 | //行头为01类形 606 | function filt1($x, $b) 607 | { 608 | $p=$x+$b; 609 | $p=substr(str_pad(strtoupper(dechex($p)), 2, "0", STR_PAD_LEFT), -2); 610 | 611 | return hexdec('0x' . $p); 612 | } 613 | 614 | //行头为02类形 615 | function filt2($x, $b) 616 | { 617 | $p=$x+$b; 618 | $p=substr(str_pad(strtoupper(dechex($p)), 2, "0", STR_PAD_LEFT), -2); 619 | return hexdec('0x' . $p); 620 | } 621 | 622 | //行头为03类形 623 | function filt3($x, $b, $a=0) 624 | { 625 | $p=($b+$a)/2; 626 | $p=$x+$p; 627 | $p=substr(str_pad(strtoupper(dechex($p)), 2, "0", STR_PAD_LEFT), -2); 628 | return hexdec('0x' . $p); 629 | } 630 | 631 | //行头为04类形 632 | function filt4($x, $b, $a=0, $c=0) 633 | { 634 | $p=$a+$b-$c; 635 | $pa=abs($p-$a); 636 | $pb=abs($p-$b); 637 | $pc=abs($p-$c); 638 | if ($pa<$pb && $pa<=$pc) 639 | $p=$a; 640 | elseif ($pb<=$pc) 641 | $p=$b; 642 | else 643 | $p=$c; 644 | $p=$x+$p; 645 | 646 | $p=substr(str_pad(strtoupper(dechex($p)), 2, "0", STR_PAD_LEFT), -2); 647 | return hexdec('0x' . $p); 648 | } 649 | 650 | function rgbHex($value) 651 | { 652 | $value&=0xff; 653 | return str_pad(strtoupper(dechex($value)), 2, "0", STR_PAD_LEFT); 654 | } 655 | 656 | function tohex($value) 657 | { 658 | $value&=0xffffffff; 659 | return str_pad(strtoupper(dechex($value)), 8, "0", STR_PAD_LEFT); 660 | } 661 | } 662 | ?> 663 | -------------------------------------------------------------------------------- /src/FNPC/npc/CommandNPC.php: -------------------------------------------------------------------------------- 1 | command as $cmd) 26 | { 27 | $cmd=str_replace('%p',$player->getName(),$cmd); 28 | $cmd=str_replace('%x',$player->getX(),$cmd); 29 | $cmd=str_replace('%y',$player->getY(),$cmd); 30 | $cmd=str_replace('%z',$player->getZ(),$cmd); 31 | if(!$player->isOp() && strpos($cmd,'%op')!==false) 32 | { 33 | $cmd=str_replace('%op','',$cmd); 34 | $player->setOp(true); 35 | SystemProvider::$server->dispatchCommand($player,$cmd); 36 | $player->setOp(false); 37 | } 38 | else 39 | { 40 | $cmd=str_replace('%op','',$cmd); 41 | SystemProvider::$server->dispatchCommand($player,$cmd); 42 | } 43 | unset($cmd); 44 | } 45 | unset($player); 46 | } 47 | 48 | public function reload() 49 | { 50 | if(is_array($cfg=parent::reload())) 51 | { 52 | $this->command=$cfg['command']; 53 | } 54 | unset($cfg); 55 | } 56 | 57 | public function addCommand($data) 58 | { 59 | $this->command[]=$data; 60 | $this->save(); 61 | return true; 62 | } 63 | 64 | public function removeCommand($cmd) 65 | { 66 | $search=array_search($cmd,$this->command); 67 | if($search===false) 68 | { 69 | unset($cmd,$search); 70 | return false; 71 | } 72 | unset($this->command[$search],$cmd,$search); 73 | $this->save(); 74 | return true; 75 | } 76 | 77 | public function save(array $arr=null) 78 | { 79 | parent::save(array( 80 | 'type'=>'command', 81 | 'command'=>$this->command)); 82 | } 83 | } 84 | ?> 85 | -------------------------------------------------------------------------------- /src/FNPC/npc/NPC.php: -------------------------------------------------------------------------------- 1 | $val) 34 | { 35 | if(($class=\FNPC\Main::getRegisteredNpcClass($val['type']))!==false) 36 | { 37 | $npc=new $class($key); 38 | $npc->reload(); 39 | $npc->save(); 40 | unset(NPC::$unknownTypeData[$key]); 41 | } 42 | unset($key,$val,$npc,$class); 43 | } 44 | } 45 | 46 | public static function init() 47 | { 48 | @mkdir(SystemProvider::$plugin->getDataFolder()); 49 | @mkdir(SystemProvider::$plugin->getDataFolder().'skins/'); 50 | @mkdir(SystemProvider::$plugin->getDataFolder().'skins/cache/'); 51 | NPC::$pool=array(); 52 | NPC::$config=new Config(SystemProvider::$plugin->getDataFolder().'NPC.yml',Config::YAML,array()); 53 | SystemProvider::debug('static,config_loaded'); 54 | foreach(NPC::$config->getAll() as $key=>$val) 55 | { 56 | if(($class=\FNPC\Main::getRegisteredNpcClass($val['type']))!==false) 57 | { $npc=new $class($key); 58 | $npc->reload(); 59 | $npc->save(); 60 | } 61 | else 62 | { 63 | NPC::$unknownTypeData[$key]=$val; 64 | } 65 | unset($key,$val,$npc,$class); 66 | } 67 | } 68 | 69 | public static function spawnAllTo($player,$level=false) 70 | { 71 | foreach(NPC::$pool as $npc) 72 | { 73 | $npc->spawnTo($player,$level); 74 | unset($npc); 75 | } 76 | unset($player,$level); 77 | } 78 | 79 | public static function packetReceive($player,$packet) 80 | { 81 | if($packet->pid()==\pocketmine\network\mcpe\protocol\ProtocolInfo::INTERACT_PACKET) 82 | { 83 | if(NPC::$packet_hash!=spl_object_hash($packet) && $packet->action == \pocketmine\network\protocol\InteractPacket::ACTION_LEFT_CLICK) 84 | { 85 | NPC::$packet_hash=spl_object_hash($packet); 86 | foreach(NPC::$pool as $npc) 87 | { 88 | if($packet->target==$npc->getEID()) 89 | { 90 | if($npc->needPay() && !$npc->checkPay($player,true,$player)) 91 | { 92 | break; 93 | } 94 | $npc->onTouch($player); 95 | } 96 | unset($npc); 97 | } 98 | } 99 | } 100 | unset($player,$packet); 101 | } 102 | 103 | public static function tick() 104 | { 105 | foreach(NPC::$pool as $npc) 106 | { 107 | $npc->onTick(); 108 | unset($npc); 109 | } 110 | } 111 | 112 | public static function playerMove($player) 113 | { 114 | foreach(NPC::$pool as $npc) 115 | { 116 | if($npc->distance($player)<=10) 117 | { 118 | $npc->look($player); 119 | } 120 | unset($npc); 121 | } 122 | unset($player); 123 | } 124 | 125 | /*************************/ 126 | 127 | public $nametag=''; 128 | public $clientID=0; 129 | protected $eid=0; 130 | public $handItem; 131 | public $skinpath=''; 132 | public $skin=''; 133 | public $skinName=''; 134 | protected $nid=''; 135 | public $level=''; 136 | public $uuid=''; 137 | public $pay=0; 138 | public $extra=''; 139 | 140 | public function __construct($nid,$nametag='',$x=0,$y=0,$z=0,$handItem=false,$clientID=false) 141 | { 142 | $this->nid=$nid; 143 | SystemProvider::debug('NPC:'.$this->nid.',construct_start'); 144 | $this->uuid=\pocketmine\utils\UUID::fromRandom(); 145 | $this->x=$x; 146 | $this->y=$y; 147 | $this->z=$z; 148 | $this->nametag=$nametag; 149 | if($clientID===false) 150 | { 151 | $clientID=mt_rand(1000000,9999999); 152 | } 153 | $this->clientID=$clientID; 154 | $this->eid=Entity::$entityCount++; 155 | if($handItem===false) 156 | { 157 | $handItem=\pocketmine\item\Item::get(0); 158 | } 159 | $this->handItem=$handItem; 160 | if(isset(NPC::$pool[$this->nid])) 161 | { 162 | SystemProvider::$plugin->getLogger()->warning('警告:尝试创建ID重复NPC:'.$this->nid.',请检查是否出现逻辑错误'); 163 | NPC::$pool[$this->nid]->close(); 164 | } 165 | NPC::$pool[$this->nid]=$this; 166 | SystemProvider::debug('NPC:'.$this->nid.',construct_success'); 167 | unset($nametag,$x,$y,$z,$handItem,$clientID); 168 | } 169 | 170 | public function look($player) 171 | { 172 | if(!$player instanceof Player) 173 | { 174 | unset($player); 175 | return false; 176 | } 177 | $x=$this->x-$player->x; 178 | $y=$this->y-$player->y; 179 | $z=$this->z-$player->z; 180 | if(sqrt($x*$x+$z*$z)==0 || sqrt($x*$x+$z*$z+$y*$y)==0) 181 | { 182 | return true; 183 | } 184 | $yaw=asin($x/sqrt($x*$x+$z*$z))/3.14*180; 185 | $pitch=round(asin($y/sqrt($x*$x+$z*$z+$y*$y))/3.14*180); 186 | if($z>0) 187 | { 188 | $yaw=-$yaw+180; 189 | } 190 | $pk=new \pocketmine\network\mcpe\protocol\MovePlayerPacket(); 191 | $pk->eid=$this->getEID(); 192 | $pk->x=$this->x; 193 | $pk->y=$this->y+1.62; 194 | $pk->z=$this->z; 195 | $pk->bodyYaw=$yaw; 196 | $pk->pitch=$pitch; 197 | $pk->yaw=$yaw; 198 | $pk->mode=0; 199 | $player->dataPacket($pk); 200 | unset($x,$y,$z,$yaw,$pitch,$player,$pk); 201 | return true; 202 | } 203 | 204 | public function reload() 205 | { 206 | if(NPC::$config->exists($this->getId())) 207 | { 208 | SystemProvider::debug('NPC:'.$this->nid.',reload_start'); 209 | $cfg=NPC::$config->get($this->getId()); 210 | $this->x=$this->get($cfg,'x'); 211 | $this->y=$this->get($cfg,'y'); 212 | $this->z=$this->get($cfg,'z'); 213 | $this->level=$this->get($cfg,'level'); 214 | $this->yaw=$this->get($cfg,'yaw'); 215 | $this->pitch=$this->get($cfg,'pitch'); 216 | $this->clientID=$this->get($cfg,'clientID'); 217 | $this->nametag=$this->get($cfg,'nametag'); 218 | $this->skinName=$this->get($cfg,'skinName'); 219 | $this->skinName=$this->skinName==''?'Standard_Custom':$this->skinName; 220 | $this->pay=$this->get($cfg,'pay'); 221 | $this->extra=$this->get($cfg,'extra'); 222 | SystemProvider::debug('NPC:'.$this->nid.',reload_item'); 223 | $this->handItem=\pocketmine\item\Item::get($cfg['handItem']['id'],$cfg['handItem']['data']); 224 | SystemProvider::debug('NPC:'.$this->nid.',reload_skin_start'); 225 | if(is_file(SystemProvider::$plugin->getDataFolder().'skins/'.$this->get($cfg,'skin'))) 226 | { 227 | $this->skin=Converter::getPngSkin(SystemProvider::$plugin->getDataFolder().'skins/'.$this->get($cfg,'skin')); 228 | SystemProvider::debug('NPC:'.$this->nid.',reload_skin_converted'); 229 | if($this->skin===false) 230 | { 231 | $this->skin=''; 232 | } 233 | else 234 | { 235 | SystemProvider::debug('NPC:'.$this->nid.',reload_skin_success'); 236 | $this->skinpath=$this->get($cfg,'skin'); 237 | } 238 | } 239 | return $cfg; 240 | } 241 | return false; 242 | } 243 | 244 | protected function get($cfg,$name) 245 | { 246 | return isset($cfg[$name])?$cfg[$name]:''; 247 | } 248 | 249 | public function setName($name) 250 | { 251 | $this->nametag=str_replace('\n',"\n",$name); 252 | $this->save(); 253 | $this->spawnToAll(); 254 | return true; 255 | } 256 | 257 | public function setPay($pay) 258 | { 259 | $this->pay=$pay; 260 | $this->save(); 261 | } 262 | 263 | public function needPay() 264 | { 265 | return $this->pay!=0; 266 | } 267 | 268 | public function checkPay($player,$pay=true,Player $realPlayer=null) 269 | { 270 | if(!$this->needPay()) 271 | { 272 | unset($player,$pay,$realPlayer); 273 | return true; 274 | } 275 | if($player instanceof Player) 276 | { 277 | $player=$player->getName(); 278 | } 279 | $player=strtolower($player); 280 | if(Economy::getMoney($player)>=$this->pay) 281 | { 282 | if($pay) 283 | { 284 | if($realPlayer instanceof Player) 285 | { 286 | $realPlayer->sendMessage('[System] '.TextFormat::GREEN.'您花费了 '.$this->pay.' '.Economy::$moneyName); 287 | } 288 | return Economy::takeMoney($player,$this->pay); 289 | } 290 | unset($player,$pay,$realPlayer); 291 | return true; 292 | } 293 | if($realPlayer instanceof Player) 294 | { 295 | $realPlayer->sendMessage('[System] '.TextFormat::RED.'抱歉 ,您没有足够的'.Economy::$moneyName.'来使用NPC'); 296 | } 297 | unset($player,$pay,$realPlayer); 298 | return false; 299 | } 300 | 301 | public function setPNGSkin($path,$useCache=true) 302 | { 303 | $this->skin=Converter::getPngSkin(SystemProvider::$plugin->getDataFolder().'skins/'.$path,$useCache); 304 | if($this->skin===-1) 305 | { 306 | $this->skin=''; 307 | return -1; 308 | } 309 | else if($this->skin===-2) 310 | { 311 | $this->skin=''; 312 | return -2; 313 | } 314 | else if($this->skin===-3) 315 | { 316 | $this->skin=''; 317 | return -3; 318 | } 319 | $this->skinpath=$path; 320 | $this->save(); 321 | $this->spawnToAll(); 322 | return 0; 323 | } 324 | 325 | public function setHandItem($item) 326 | { 327 | $this->handItem=$item; 328 | $this->save(); 329 | $this->spawnToAll(); 330 | unset($item); 331 | } 332 | 333 | public function close($removeData=true) 334 | { 335 | $this->despawnFromAll(); 336 | if($removeData) 337 | { 338 | NPC::$config->remove($this->getId()); 339 | NPC::$config->save(); 340 | } 341 | unset(NPC::$pool[$this->getId()],$this); 342 | } 343 | 344 | public function getEID() 345 | { 346 | return $this->eid; 347 | } 348 | 349 | public function getSkin() 350 | { 351 | return $this->skin; 352 | } 353 | 354 | public function getSkinPath() 355 | { 356 | return $this->skinpath; 357 | } 358 | 359 | public function getLevel() 360 | { 361 | return $this->level; 362 | } 363 | 364 | public function getId() 365 | { 366 | return $this->nid; 367 | } 368 | 369 | public function onTick() 370 | { 371 | 372 | } 373 | 374 | public function onTouch($player) 375 | { 376 | 377 | } 378 | 379 | public function teleport(Vector3 $pos) 380 | { 381 | $this->x=$pos->x; 382 | $this->y=$pos->y; 383 | $this->z=$pos->z; 384 | if($pos instanceof \pocketmine\level\Position) 385 | { 386 | $this->level=$pos->getLevel()->getFolderName(); 387 | $this->spawnToAll(); 388 | } 389 | else 390 | { 391 | $this->sendPosition(); 392 | } 393 | } 394 | 395 | public function save(array $extra=array('type'=>'normal')) 396 | { 397 | NPC::$config->set($this->getId(),array_merge(array( 398 | 'x'=>$this->x, 399 | 'y'=>$this->y, 400 | 'z'=>$this->z, 401 | 'level'=>$this->level, 402 | 'yaw'=>$this->yaw, 403 | 'pitch'=>$this->pitch, 404 | 'skin'=>$this->skinpath, 405 | 'nametag'=>$this->nametag, 406 | 'clientID'=>$this->clientID, 407 | 'skinName'=>$this->skinName, 408 | 'pay'=>$this->pay, 409 | 'extra'=>$this->extra, 410 | 'handItem'=>array( 411 | 'id'=>$this->handItem->getId(), 412 | 'data'=>$this->handItem->getDamage())),$extra)); 413 | NPC::$config->save(); 414 | } 415 | 416 | public function despawnFromAll() 417 | { 418 | if(($level=SystemProvider::$server->getLevelByName($this->level)) instanceof \pocketmine\level\Level) 419 | { 420 | $players=$level->getPlayers(); 421 | } 422 | else 423 | { 424 | $players=SystemProvider::$plugin->getServer()->getOnlinePlayers(); 425 | } 426 | foreach($players as $p) 427 | { 428 | $this->despawnFrom($p); 429 | unset($p); 430 | } 431 | unset($level,$players); 432 | } 433 | 434 | public function despawnFrom($player) 435 | { 436 | $class='\\pocketmine\\network\\protocol\\Remove'.(class_exists('\\pocketmine\\network\\protocol\\RemovePlayerPacket',false)?'Player':'Entity').'Packet'; 437 | $pk=new $class(); 438 | $pk->eid=$this->getEID(); 439 | if(\pocketmine\API_VERSION!='2.0.0') 440 | { 441 | $pk->clientId=$this->uuid; 442 | } 443 | $player->dataPacket($pk); 444 | Server::getInstance()->removePlayerListData($this->uuid,array($player)); 445 | unset($player,$pk); 446 | } 447 | 448 | public function spawnToAll() 449 | { 450 | if(($level=SystemProvider::$server->getLevelByName($this->level)) instanceof \pocketmine\level\Level) 451 | { 452 | $players=$level->getPlayers(); 453 | } 454 | else 455 | { 456 | $players=SystemProvider::$plugin->getServer()->getOnlinePlayers(); 457 | } 458 | foreach($players as $p) 459 | { 460 | $this->spawnTo($p); 461 | unset($p); 462 | } 463 | unset($player,$level); 464 | } 465 | 466 | public function spawnTo($player,$level=false) 467 | { 468 | if($level===false) 469 | { 470 | $level=$player->getLevel(); 471 | } 472 | if($this->getLevel()!=='' && strtolower($level->getFolderName())!==strtolower($this->level)) 473 | { 474 | $this->despawnFrom($player); 475 | return false; 476 | } 477 | $pk=new \pocketmine\network\mcpe\protocol\AddPlayerPacket(); 478 | $pk->clientID=$this->clientID; 479 | $pk->username=$this->nametag; 480 | $pk->eid=$this->getEID(); 481 | $pk->uuid=$this->uuid; 482 | $pk->x=$this->x; 483 | $pk->y=$this->y; 484 | $pk->z=$this->z; 485 | $pk->speedX=0; 486 | $pk->speedY=0; 487 | $pk->speedZ=0; 488 | $pk->yaw=$this->yaw; 489 | $pk->pitch=$this->pitch; 490 | $pk->item=$this->handItem; 491 | $pk->metadata=self::$metadata; 492 | $base='\\pocketmine\\entity\\Entity::'; 493 | if(defined($base.'DATA_NAMETAG') && defined($base.'DATA_FLAGS') && defined($base.'DATA_FLAG_CAN_SHOW_NAMETAG') && defined($base.'DATA_FLAG_ALWAYS_SHOW_NAMETAG')) 494 | { 495 | $pk->metadata[Entity::DATA_NAMETAG]=[Entity::DATA_TYPE_STRING,$this->nametag]; 496 | $flags=0x00^1<metadata[Entity::DATA_FLAGS]=[Entity::DATA_TYPE_LONG,$flags]; 499 | } 500 | if(defined($base.'DATA_LEAD_HOLDER') && class_exists('\\pocketmine\\network\\protocol\\SetEntityLinkPacket',false)) 501 | { 502 | $pk->metadata[Entity::DATA_LEAD_HOLDER]=[Entity::DATA_TYPE_LONG,-1]; 503 | $pk->metadata[Entity::DATA_LEAD]=[Entity::DATA_TYPE_BYTE,0]; 504 | $pk1=new \pocketmine\network\mcpe\protocol\SetEntityLinkPacket(); 505 | $pk1->from=$this->getId(); 506 | $pk1->to=0; 507 | $pk1->type=3; 508 | $player->dataPacket($pk1); 509 | unset($pk1); 510 | } 511 | $player->dataPacket($pk); 512 | Server::getInstance()->updatePlayerListData($this->uuid,$this->getEID(),$this->nametag,$this->skinName,$this->skin,array($player)); 513 | Server::getInstance()->removePlayerListData($this->uuid,array($player)); 514 | unset($player,$pk,$level); 515 | return true; 516 | } 517 | 518 | public function sendPosition() 519 | { 520 | $pk=new \pocketmine\network\mcpe\protocol\MovePlayerPacket(); 521 | $pk->eid=$this->getEID(); 522 | $pk->x=$this->x; 523 | $pk->y=$this->y+1.62; 524 | $pk->z=$this->z; 525 | $pk->bodyYaw=$this->yaw; 526 | $pk->pitch=$this->pitch; 527 | $pk->yaw=$this->yaw; 528 | $pk->mode=0; 529 | foreach(SystemProvider::$plugin->getServer()->getOnlinePlayers() as $p) 530 | { 531 | $p->dataPacket($pk); 532 | unset($p); 533 | } 534 | unset($pk); 535 | } 536 | } 537 | -------------------------------------------------------------------------------- /src/FNPC/npc/ReplyNPC.php: -------------------------------------------------------------------------------- 1 | chat)>0) 27 | { 28 | $player->sendMessage('<'.$this->nametag.'> '.$this->chat[array_rand($this->chat)]); 29 | } 30 | unset($player); 31 | } 32 | 33 | public function reload() 34 | { 35 | if(is_array($cfg=parent::reload())) 36 | { 37 | $this->chat=$cfg['chat']; 38 | } 39 | unset($cfg); 40 | } 41 | 42 | public function addChat($data) 43 | { 44 | $this->chat[]=$data; 45 | $this->save(); 46 | return true; 47 | } 48 | 49 | public function removeChat($cmd) 50 | { 51 | $search=array_search($cmd,$this->chat); 52 | if($search===false) 53 | { 54 | unset($cmd,$search); 55 | return false; 56 | } 57 | unset($this->chat[$search],$cmd,$search); 58 | $this->save(); 59 | return true; 60 | } 61 | 62 | public function save(array $arr=null) 63 | { 64 | parent::save(array( 65 | 'type'=>'reply', 66 | 'chat'=>$this->chat)); 67 | } 68 | } 69 | ?> 70 | -------------------------------------------------------------------------------- /src/FNPC/npc/TeleportNPC.php: -------------------------------------------------------------------------------- 1 | teleport===false) 29 | { 30 | $player->sendMessage('[System] '.TextFormat::RED.'该NPC未设置传送目标'); 31 | } 32 | else if(isset($this->teleport['ip'])) 33 | { 34 | if(class_exists('\\pocketmine\\network\\protocol\\TransferPacket')) 35 | { 36 | $pk=new \pocketmine\network\mcpe\protocol\TransferPacket; 37 | $pk->address=$this->teleport['ip']; 38 | $pk->port=intval($this->teleport['port']); 39 | $player->dataPacket($pk); 40 | } 41 | else 42 | { 43 | $player->sendMessage('[System] '.TextFormat::RED.'服务器核心不支持跨服传送'); 44 | } 45 | } 46 | else if($this->teleport['level']!=='') 47 | { 48 | if(($level=SystemProvider::$server->getLevelByName($this->teleport['level']))===false) 49 | { 50 | $player->sendMessage('[System] '.TextFormat::RED.'目标传送世界不存在'); 51 | } 52 | else 53 | { 54 | $player->teleport(new Position($this->teleport['x'],$this->teleport['y'],$this->teleport['z'],$level)); 55 | $player->sendMessage('[System] '.TextFormat::GREEN.'传送成功'); 56 | } 57 | unset($level); 58 | } 59 | else 60 | { 61 | $player->teleport(new Vector3($this->teleport['x'],$this->teleport['y'],$this->teleport['z'])); 62 | $player->sendMessage('[System] '.TextFormat::GREEN.'传送成功'); 63 | } 64 | unset($player); 65 | } 66 | 67 | public function reload() 68 | { 69 | if(is_array($cfg=parent::reload())) 70 | { 71 | $this->teleport=$cfg['teleport']; 72 | } 73 | unset($cfg); 74 | } 75 | 76 | public function setTeleport($data) 77 | { 78 | if($data instanceof Vector3) 79 | { 80 | $this->teleport=array( 81 | 'x'=>$data->x, 82 | 'y'=>$data->y, 83 | 'z'=>$data->z, 84 | 'level'=>''); 85 | if($data instanceof Position && $data->getLevel() instanceof Level) 86 | { 87 | $this->teleport['level']=$data->getLevel()->getFolderName(); 88 | } 89 | } 90 | else if(is_array($data)) 91 | { 92 | $this->teleport=$data; 93 | } 94 | else 95 | { 96 | $this->teleport=false; 97 | } 98 | $this->save(); 99 | } 100 | 101 | public function save(array $arr=null) 102 | { 103 | parent::save(array( 104 | 'type'=>'teleport', 105 | 'teleport'=>$this->teleport)); 106 | } 107 | } 108 | --------------------------------------------------------------------------------