├── 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 |
--------------------------------------------------------------------------------