PocketMine-MP 5.16.1 git-585aee9386a787c95e73dd0a05ffca8329606b68
BaseRail.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
22declare(strict_types=1);
23
24namespace pocketmine\block;
25
32use function array_reverse;
33use function array_search;
34use function array_shift;
35use function count;
36use function in_array;
37
38abstract class BaseRail extends Flowable{
39
40 public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
41 if($blockReplace->getAdjacentSupportType(Facing::DOWN)->hasEdgeSupport()){
42 return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
43 }
44
45 return false;
46 }
47
48 public function onPostPlace() : void{
49 $this->tryReconnect();
50 }
51
57 protected static function searchState(array $connections, array $lookup) : ?int{
58 $shape = array_search($connections, $lookup, true);
59 if($shape === false){
60 $shape = array_search(array_reverse($connections), $lookup, true);
61 }
62 return $shape === false ? null : $shape;
63 }
64
72 abstract protected function setShapeFromConnections(array $connections) : void;
73
79 abstract protected function getCurrentShapeConnections() : array;
80
86 private function getConnectedDirections() : array{
88 $connections = [];
89
91 foreach($this->getCurrentShapeConnections() as $connection){
92 $other = $this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND);
93 $otherConnection = Facing::opposite($connection & ~RailConnectionInfo::FLAG_ASCEND);
94
95 if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0){
96 $other = $other->getSide(Facing::UP);
97
98 }elseif(!($other instanceof BaseRail)){ //check for rail sloping up to meet this one
99 $other = $other->getSide(Facing::DOWN);
100 $otherConnection |= RailConnectionInfo::FLAG_ASCEND;
101 }
102
103 if(
104 $other instanceof BaseRail &&
105 in_array($otherConnection, $other->getCurrentShapeConnections(), true)
106 ){
107 $connections[] = $connection;
108 }
109 }
110
111 return $connections;
112 }
113
120 private function getPossibleConnectionDirections(array $constraints) : array{
121 switch(count($constraints)){
122 case 0:
123 //No constraints, can connect in any direction
124 $possible = [
125 Facing::NORTH => true,
126 Facing::SOUTH => true,
127 Facing::WEST => true,
128 Facing::EAST => true
129 ];
130 foreach($possible as $p => $_){
131 $possible[$p | RailConnectionInfo::FLAG_ASCEND] = true;
132 }
133
134 return $possible;
135 case 1:
136 return $this->getPossibleConnectionDirectionsOneConstraint(array_shift($constraints));
137 case 2:
138 return [];
139 default:
140 throw new \InvalidArgumentException("Expected at most 2 constraints, got " . count($constraints));
141 }
142 }
143
148 protected function getPossibleConnectionDirectionsOneConstraint(int $constraint) : array{
149 $opposite = Facing::opposite($constraint & ~RailConnectionInfo::FLAG_ASCEND);
150
151 $possible = [$opposite => true];
152
153 if(($constraint & RailConnectionInfo::FLAG_ASCEND) === 0){
154 //We can slope the other way if this connection isn't already a slope
155 $possible[$opposite | RailConnectionInfo::FLAG_ASCEND] = true;
156 }
157
158 return $possible;
159 }
160
161 private function tryReconnect() : void{
162 $thisConnections = $this->getConnectedDirections();
163 $changed = false;
164
165 $world = $this->position->getWorld();
166 do{
167 $possible = $this->getPossibleConnectionDirections($thisConnections);
168 $continue = false;
169
170 foreach($possible as $thisSide => $_){
171 $otherSide = Facing::opposite($thisSide & ~RailConnectionInfo::FLAG_ASCEND);
172
173 $other = $this->getSide($thisSide & ~RailConnectionInfo::FLAG_ASCEND);
174
175 if(($thisSide & RailConnectionInfo::FLAG_ASCEND) !== 0){
176 $other = $other->getSide(Facing::UP);
177
178 }elseif(!($other instanceof BaseRail)){ //check if other rails can slope up to meet this one
179 $other = $other->getSide(Facing::DOWN);
180 $otherSide |= RailConnectionInfo::FLAG_ASCEND;
181 }
182
183 if(!($other instanceof BaseRail) || count($otherConnections = $other->getConnectedDirections()) >= 2){
184 //we can only connect to a rail that has less than 2 connections
185 continue;
186 }
187
188 $otherPossible = $other->getPossibleConnectionDirections($otherConnections);
189
190 if(isset($otherPossible[$otherSide])){
191 $otherConnections[] = $otherSide;
192 $other->setConnections($otherConnections);
193 $world->setBlock($other->position, $other);
194
195 $changed = true;
196 $thisConnections[] = $thisSide;
197 $continue = count($thisConnections) < 2;
198
199 break; //force recomputing possible directions, since this connection could invalidate others
200 }
201 }
202 }while($continue);
203
204 if($changed){
205 $this->setConnections($thisConnections);
206 $world->setBlock($this->position, $this);
207 }
208 }
209
213 private function setConnections(array $connections) : void{
214 if(count($connections) === 1){
215 $connections[] = Facing::opposite($connections[0] & ~RailConnectionInfo::FLAG_ASCEND);
216 }elseif(count($connections) !== 2){
217 throw new \InvalidArgumentException("Expected exactly 2 connections, got " . count($connections));
218 }
219
220 $this->setShapeFromConnections($connections);
221 }
222
223 public function onNearbyBlockChange() : void{
224 $world = $this->position->getWorld();
225 if(!$this->getAdjacentSupportType(Facing::DOWN)->hasEdgeSupport()){
226 $world->useBreakOn($this->position);
227 }else{
228 foreach($this->getCurrentShapeConnections() as $connection){
229 if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 && !$this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->getSupportType(Facing::UP)->hasEdgeSupport()){
230 $world->useBreakOn($this->position);
231 break;
232 }
233 }
234 }
235 }
236}
setShapeFromConnections(array $connections)
static searchState(array $connections, array $lookup)
Definition: BaseRail.php:57
place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player=null)
Definition: BaseRail.php:40
getPossibleConnectionDirectionsOneConstraint(int $constraint)
Definition: BaseRail.php:148