Initial commit
This commit is contained in:
commit
b2588f5f54
27 changed files with 1097 additions and 0 deletions
158
class_abstractbeing.php
Normal file
158
class_abstractbeing.php
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class AbstractBeing extends Entity implements Being
|
||||||
|
{
|
||||||
|
const MAXITEMS = 5;
|
||||||
|
const VISIBILITY = parent::VSB_VISIBLE;
|
||||||
|
|
||||||
|
private $name;
|
||||||
|
private $items;
|
||||||
|
private $equips;
|
||||||
|
private $speed;
|
||||||
|
private $sight;
|
||||||
|
private $hp;
|
||||||
|
private $mp;
|
||||||
|
private $money;
|
||||||
|
private $exp;
|
||||||
|
|
||||||
|
static public function createName(AbstractBeing $being)
|
||||||
|
{
|
||||||
|
$being->name = 'FAIL_' . uniqid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function giveRandomItems(AbstractBeing $being)
|
||||||
|
{
|
||||||
|
/*$amount = rand(0, static::MAXITEMS);
|
||||||
|
for ($i = 0; $i < $amount; ++$i)
|
||||||
|
{
|
||||||
|
$being->items[] = RandomItemFactory::getItem($being);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function equipRandomItems(AbstractBeing $being)
|
||||||
|
{
|
||||||
|
/*$amount = rand(0, count($being->items));
|
||||||
|
for ($i = 0; $i < $amount; ++$i)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$being->equip($being->items[$i]);
|
||||||
|
} catch (CannotEquipException $e)
|
||||||
|
{ }
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function setRandomStats(AbstractBeing $being)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($name = null, $exp = 0, array $items = null, array $equips = null, $speed = null, $sight = null, $hp = 0, $mp = 0, $money = null, Place $place = null)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
|
||||||
|
if ($items === null)
|
||||||
|
static::giveRandomItems($this);
|
||||||
|
|
||||||
|
if ($equips === null)
|
||||||
|
static::equipRandomItems($this);
|
||||||
|
|
||||||
|
static::setRandomStats($this);
|
||||||
|
if ($speed !== null)
|
||||||
|
$this->speed = $speed;
|
||||||
|
if ($sight !== null)
|
||||||
|
$this->sight = $sight;
|
||||||
|
if ($hp > 0)
|
||||||
|
$this->hp = $hp;
|
||||||
|
if ($mp > 0)
|
||||||
|
$this->mp = $mp;
|
||||||
|
if ($money !== null)
|
||||||
|
$this->money = $money;
|
||||||
|
if ($exp > 0)
|
||||||
|
$this->exp = $exp;
|
||||||
|
|
||||||
|
if ($place != null)
|
||||||
|
$this->setPlace($place);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRandomPlace(Level $level = null)
|
||||||
|
{
|
||||||
|
$coord = $level->getEmptyPlace();
|
||||||
|
$this->setPlace(new Place($level, $coord));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function move($dir)
|
||||||
|
{
|
||||||
|
$newX = $this->place->getCoord()->x;
|
||||||
|
$newY = $this->place->getCoord()->y;
|
||||||
|
if ($dir & DIR_DOWN)
|
||||||
|
++$newY;
|
||||||
|
elseif ($dir & DIR_UP)
|
||||||
|
--$newY;
|
||||||
|
if ($dir & DIR_RIGHT)
|
||||||
|
++$newX;
|
||||||
|
elseif ($dir & DIR_LEFT)
|
||||||
|
--$newX;
|
||||||
|
|
||||||
|
$newCoord = new Coord($newX, $newY);
|
||||||
|
if ($this->place->getLevel()->whatsAt($newCoord) instanceof EmptyFloor)
|
||||||
|
$this->setPlace(new Place($this->place->getLevel(), $newCoord));
|
||||||
|
|
||||||
|
// Clear sight data
|
||||||
|
$this->seenCells = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItems()
|
||||||
|
{
|
||||||
|
return $this->items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEquips()
|
||||||
|
{
|
||||||
|
return $this->equips;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSpeed()
|
||||||
|
{
|
||||||
|
return $this->speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSight()
|
||||||
|
{
|
||||||
|
return $this->sight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHP()
|
||||||
|
{
|
||||||
|
return $this->hp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMP()
|
||||||
|
{
|
||||||
|
return $this->mp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMoney()
|
||||||
|
{
|
||||||
|
return $this->money;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExp()
|
||||||
|
{
|
||||||
|
return $this->exp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
if (DudeStore::getDude()->getBase() == $this)
|
||||||
|
return '@';
|
||||||
|
return parent::__toString();
|
||||||
|
}
|
||||||
|
}
|
9
class_being.php
Normal file
9
class_being.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
interface Being
|
||||||
|
{
|
||||||
|
/*public function tick();
|
||||||
|
public function attack($attack);
|
||||||
|
public function kill(Being $killer);
|
||||||
|
public function move(Place $place);*/
|
||||||
|
}
|
9
class_cannotequipexception.php
Normal file
9
class_cannotequipexception.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class CannotEquipException extends SDGException
|
||||||
|
{
|
||||||
|
public function getMessage()
|
||||||
|
{
|
||||||
|
return 'Cannot equip item: ' . parent::getMessage();
|
||||||
|
}
|
||||||
|
}
|
40
class_coord.php
Normal file
40
class_coord.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Coord
|
||||||
|
{
|
||||||
|
private $x;
|
||||||
|
private $y;
|
||||||
|
|
||||||
|
static public function createFromString($str)
|
||||||
|
{
|
||||||
|
$str = explode(',', $str);
|
||||||
|
return new self($str[0], $str[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($x, $y)
|
||||||
|
{
|
||||||
|
$this->x = $x;
|
||||||
|
$this->y = $y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDistance(Coord $coord)
|
||||||
|
{
|
||||||
|
return round(sqrt(pow($coord->x - $this->x, 2) + pow($coord->y - $this->y, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($name)
|
||||||
|
{
|
||||||
|
if ($name == 'x' || $name == 'y')
|
||||||
|
{
|
||||||
|
return $this->$name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new SDGException('Can only access X and Y in ' . __CLASS__);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return $this->x . ',' . $this->y;
|
||||||
|
}
|
||||||
|
}
|
82
class_display.php
Normal file
82
class_display.php
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Display
|
||||||
|
{
|
||||||
|
const STYLE_NONE = 0;
|
||||||
|
const STYLE_BOLD = 1;
|
||||||
|
const STYLE_ITALIC = 2;
|
||||||
|
const STYLE_UNDERLINE = 4;
|
||||||
|
const STYLE_BLINK = 8;
|
||||||
|
const STYLE_INVERSE = 16;
|
||||||
|
|
||||||
|
const COLOR_NONE = 9;
|
||||||
|
const COLOR_BLACK = 0;
|
||||||
|
const COLOR_RED = 1;
|
||||||
|
const COLOR_GREEN = 2;
|
||||||
|
const COLOR_YELLOW = 3;
|
||||||
|
const COLOR_BLUE = 4;
|
||||||
|
const COLOR_MAGENTA = 5;
|
||||||
|
const COLOR_CYAN = 6;
|
||||||
|
const COLOR_WHITE = 7;
|
||||||
|
|
||||||
|
private $xSize;
|
||||||
|
private $ySize;
|
||||||
|
private $stream;
|
||||||
|
|
||||||
|
public function __construct($xSize, $ySize, $stream)
|
||||||
|
{
|
||||||
|
$this->xSize = $xSize;
|
||||||
|
$this->ySize = $ySize;
|
||||||
|
$this->stream = $stream;
|
||||||
|
$this->color = self::COLOR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($text, $style = self::STYLE_NONE, $color = self::COLOR_NONE)
|
||||||
|
{
|
||||||
|
if ($style != self::STYLE_NONE)
|
||||||
|
$this->setStyle($style);
|
||||||
|
if ($color != self::COLOR_NONE)
|
||||||
|
$this->setColor($color);
|
||||||
|
fwrite($this->stream, $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeTo($x, $y, $text, $style = self::STYLE_NONE, $color = self::COLOR_NONE)
|
||||||
|
{
|
||||||
|
$this->write(ANSI_CSI . $y . ';' . $x . 'H' . $text, $style, $color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCursor($x, $y)
|
||||||
|
{
|
||||||
|
$this->writeTo($x, $y, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setColor($color = self::COLOR_NONE)
|
||||||
|
{
|
||||||
|
$this->write(ANSI_CSI . (30 + $color) . 'm');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle($style = self::STYLE_NONE)
|
||||||
|
{
|
||||||
|
if ($style == self::STYLE_NONE)
|
||||||
|
$this->write(ANSI_CSI . 0 . 'm');
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($style & self::STYLE_BOLD)
|
||||||
|
$this->write(ANSI_CSI . 1 . 'm');
|
||||||
|
if ($style & self::STYLE_ITALIC)
|
||||||
|
$this->write(ANSI_CSI . 3 . 'm');
|
||||||
|
if ($style & self::STYLE_UNDERLINE)
|
||||||
|
$this->write(ANSI_CSI . 4 . 'm');
|
||||||
|
if ($style & self::STYLE_BLINK)
|
||||||
|
$this->write(ANSI_CSI . 5 . 'm');
|
||||||
|
if ($style & self::STYLE_INVERSE)
|
||||||
|
$this->write(ANSI_CSI . 7 . 'm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clear()
|
||||||
|
{
|
||||||
|
$this->write(ANSI_CSI . 2 . 'J');
|
||||||
|
$this->setCursor(null, null);
|
||||||
|
}
|
||||||
|
}
|
19
class_displayfactory.php
Normal file
19
class_displayfactory.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class DisplayFactory
|
||||||
|
{
|
||||||
|
static public function getDefault()
|
||||||
|
{
|
||||||
|
return new Display(TERM_X, TERM_Y, STDOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function terminalSetRaw()
|
||||||
|
{
|
||||||
|
system('stty raw');
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function terminalSetCooked()
|
||||||
|
{
|
||||||
|
system('stty cooked');
|
||||||
|
}
|
||||||
|
}
|
30
class_dude.php
Normal file
30
class_dude.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Dude implements Being
|
||||||
|
{
|
||||||
|
private $base;
|
||||||
|
|
||||||
|
public function __construct(Being $base)
|
||||||
|
{
|
||||||
|
$this->base = $base;
|
||||||
|
DudeStore::add($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBase()
|
||||||
|
{
|
||||||
|
return clone $this->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function move($dir)
|
||||||
|
{
|
||||||
|
$this->base->move($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($method, $arguments)
|
||||||
|
{
|
||||||
|
if (is_callable('parent::' . $method))
|
||||||
|
call_user_func_array('parent::' . $method, $arguments);
|
||||||
|
else
|
||||||
|
throw new SDGException('Cannot call nonexistant method ' . $method);
|
||||||
|
}
|
||||||
|
}
|
16
class_dudestore.php
Normal file
16
class_dudestore.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class DudeStore
|
||||||
|
{
|
||||||
|
static private $dude;
|
||||||
|
|
||||||
|
static public function add(Dude $dude)
|
||||||
|
{
|
||||||
|
self::$dude = $dude;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function getDude()
|
||||||
|
{
|
||||||
|
return self::$dude;
|
||||||
|
}
|
||||||
|
}
|
50
class_dungeon.php
Normal file
50
class_dungeon.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Dungeon
|
||||||
|
{
|
||||||
|
private $levels;
|
||||||
|
private $lowest;
|
||||||
|
private $highest;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->lowest = 0;
|
||||||
|
$this->highest = 0;
|
||||||
|
$this->levels = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addLevelAbove(Level $level)
|
||||||
|
{
|
||||||
|
if (!empty($this->levels))
|
||||||
|
{
|
||||||
|
++$this->highest;
|
||||||
|
}
|
||||||
|
$this->levels[$this->highest] = $level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addLevelBelow(Level $level)
|
||||||
|
{
|
||||||
|
if (empty($this->levels))
|
||||||
|
{
|
||||||
|
--$this->lowest;
|
||||||
|
}
|
||||||
|
$this->levels[$this->lowest] = $level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLevels()
|
||||||
|
{
|
||||||
|
return $this->levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLevel($number)
|
||||||
|
{
|
||||||
|
return (isset($this->levels[$number]))? $this->levels[$number] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLevelNumber(Level $level)
|
||||||
|
{
|
||||||
|
foreach ($levels as $number => $savedLevel)
|
||||||
|
if ($savedLevel == $level)
|
||||||
|
return $number;
|
||||||
|
}
|
||||||
|
}
|
7
class_emptyfloor.php
Normal file
7
class_emptyfloor.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class EmptyFloor extends Entity
|
||||||
|
{
|
||||||
|
const CHAR = '.';
|
||||||
|
const VISIBILITY = parent::VSB_INVISIBLE;
|
||||||
|
}
|
113
class_entity.php
Normal file
113
class_entity.php
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class Entity
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Different visibility types for entities
|
||||||
|
*/
|
||||||
|
const VSB_INVISIBLE = 0;
|
||||||
|
const VSB_VISIBLE = 1;
|
||||||
|
const VSB_SEETHROUGH = 2;
|
||||||
|
|
||||||
|
protected $place;
|
||||||
|
protected $seenCells = array();
|
||||||
|
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return static::CHAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function getChar()
|
||||||
|
{
|
||||||
|
return static::CHAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVisibility()
|
||||||
|
{
|
||||||
|
return static::VISIBILITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPlace(Place $place)
|
||||||
|
{
|
||||||
|
// Remove from old place if exists
|
||||||
|
if ($this->place !== null)
|
||||||
|
$this->place->getLevel()->removeEntity($this->place->getCoord());
|
||||||
|
|
||||||
|
$this->place = $place;
|
||||||
|
|
||||||
|
if ($this->place !== null)
|
||||||
|
$this->place->getLevel()->insertEntity($this->place->getCoord(), $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlace()
|
||||||
|
{
|
||||||
|
return $this->place;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function drawPath(Entity $target, $callback)
|
||||||
|
{
|
||||||
|
$targetX = $target->getPlace()->getCoord()->x;
|
||||||
|
$targetY = $target->getPlace()->getCoord()->y;
|
||||||
|
$startX = $this->getPlace()->getCoord()->x;
|
||||||
|
$startY = $this->getPlace()->getCoord()->y;
|
||||||
|
|
||||||
|
if ($startX == $targetX && $startY == $targetY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$diffX = $targetX - $startX;
|
||||||
|
$diffY = $targetY - $startY;
|
||||||
|
|
||||||
|
$step = max(abs($diffX), abs($diffY));
|
||||||
|
$stepX = $diffX / $step;
|
||||||
|
$stepY = $diffY / $step;
|
||||||
|
if ($stepX != 0)
|
||||||
|
$steps = floor($diffX / $stepX);
|
||||||
|
else
|
||||||
|
$steps = floor($diffY / $stepY);
|
||||||
|
|
||||||
|
$x = $startX;
|
||||||
|
$y = $startY;
|
||||||
|
$i = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
$x += $stepX;
|
||||||
|
$y += $stepY;
|
||||||
|
++$i;
|
||||||
|
if (!$callback(new Coord(floor($x), floor($y))))
|
||||||
|
return;
|
||||||
|
|
||||||
|
} while ((floor($x) != $targetX || floor($y) != $targetY)
|
||||||
|
&& $i < $steps);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canSee(Entity $target)
|
||||||
|
{
|
||||||
|
// Check if already marked as seen
|
||||||
|
if (isset($this->seenCells[$target->getPlace()->getCoord()->x][$target->getPlace()->getCoord()->y]))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check distance
|
||||||
|
if ($this->getSight() < $this->getPlace()->getCoord()->getDistance($target->getPlace()->getCoord()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check each cell on the way for opaque objects
|
||||||
|
$level = $this->getPlace()->getLevel();
|
||||||
|
$return = true;
|
||||||
|
$seenCells =& $this->seenCells;
|
||||||
|
$this->drawPath($target, function(Coord $coord) use (&$return, $level, $target,
|
||||||
|
&$seenCells)
|
||||||
|
{
|
||||||
|
$obj = $level->whatsAt($coord);
|
||||||
|
if ($obj !== null && $obj != $target && $obj->getVisibility() == Entity::VSB_VISIBLE)
|
||||||
|
{
|
||||||
|
$return = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$seenCells[$coord->x][$coord->y] = true;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
}
|
26
class_human.php
Normal file
26
class_human.php
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Human extends AbstractBeing
|
||||||
|
{
|
||||||
|
const CHAR = 'H';
|
||||||
|
|
||||||
|
private $sex;
|
||||||
|
|
||||||
|
public function __construct($name = null, $exp = 0, array $items = null, array $equips = null, $speed = null, $sight = null, $hp = 0, $mp = 0, $money = null, Place $place = null)
|
||||||
|
{
|
||||||
|
$this->sex = rand(0, 1);
|
||||||
|
parent::__construct($name, $exp, $items, $equips, $speed, $sight, $hp, $mp, $money, $place);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSex($sex)
|
||||||
|
{
|
||||||
|
$this->sex = $sex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSex()
|
||||||
|
{
|
||||||
|
if ($this->sex == 0)
|
||||||
|
return 'male';
|
||||||
|
return 'female';
|
||||||
|
}
|
||||||
|
}
|
15
class_item.php
Normal file
15
class_item.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
interface Item
|
||||||
|
{
|
||||||
|
private $name;
|
||||||
|
private $value;
|
||||||
|
|
||||||
|
public function equip(Being wearer);
|
||||||
|
public function unequip(Being wearer);
|
||||||
|
public function sell(Being seller, Being buyer);
|
||||||
|
public function use(Being user);
|
||||||
|
|
||||||
|
public function getName();
|
||||||
|
public function getValue();
|
||||||
|
}
|
298
class_level.php
Normal file
298
class_level.php
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Level
|
||||||
|
{
|
||||||
|
const MIN_STROKES = 5;
|
||||||
|
const MAX_STROKES = 25;
|
||||||
|
|
||||||
|
const BLOOM_MAX = 1, BLOOM_MIN = 0;
|
||||||
|
|
||||||
|
// This needs its own definitions of these so we can use some stupid array tricks
|
||||||
|
const DIR_NONE = -1, DIR_UP = 0, DIR_RIGHT = 1, DIR_DOWN = 2, DIR_LEFT = 3;
|
||||||
|
|
||||||
|
private $coords;
|
||||||
|
private $width;
|
||||||
|
private $height;
|
||||||
|
private $dungeon;
|
||||||
|
private $changes;
|
||||||
|
|
||||||
|
public function __construct(Dungeon $dungeon, $createRandom = true)
|
||||||
|
{
|
||||||
|
$this->coords = array();
|
||||||
|
$this->width = 0;
|
||||||
|
$this->height = 0;
|
||||||
|
|
||||||
|
if ($createRandom)
|
||||||
|
$this->createRandom();
|
||||||
|
|
||||||
|
$this->dungeon = $dungeon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function whatsAt(Coord $place)
|
||||||
|
{
|
||||||
|
return (isset($this->coords[(string) $place]))? $this->coords[(string) $place] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insertEntity(Coord $place, Entity $entity)
|
||||||
|
{
|
||||||
|
if (isset($this->coords[(string) $place])
|
||||||
|
&& !($this->coords[(string) $place] instanceof EmptyFloor))
|
||||||
|
throw new PlaceOccupiedException('Cannot insert, place already taken.');
|
||||||
|
|
||||||
|
$this->coords[(string) $place] = $entity;
|
||||||
|
|
||||||
|
$this->width = max($this->width, $place->x);
|
||||||
|
$this->height = max($this->height, $place->y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeEntity(Coord $place)
|
||||||
|
{
|
||||||
|
if (!isset($this->coords[(string) $place])
|
||||||
|
|| $this->coords[(string) $place] instanceof EmptyFloor)
|
||||||
|
throw new PlaceException('Cannot remove, nothing there.');
|
||||||
|
|
||||||
|
unset($this->coords[(string) $place]);
|
||||||
|
|
||||||
|
$floor = new EmptyFloor();
|
||||||
|
$floor->setPlace(new Place($this, $place));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function printLevel(Display $disp)
|
||||||
|
{
|
||||||
|
foreach ($this->coords as $coord => $content)
|
||||||
|
{
|
||||||
|
// +1 to X and Y since in the terminal they start from 1,1
|
||||||
|
$expCoord = Coord::createFromString($coord);
|
||||||
|
$disp->writeTo($expCoord->x + 1, $expCoord->y + 1, $this->whatsAt($expCoord));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWidth()
|
||||||
|
{
|
||||||
|
return $this->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeight()
|
||||||
|
{
|
||||||
|
return $this->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDungeon()
|
||||||
|
{
|
||||||
|
return $dungeon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEmptyPlace()
|
||||||
|
{
|
||||||
|
$keys = array_keys($this->coords);
|
||||||
|
shuffle($keys);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
$coord = array_pop($keys);
|
||||||
|
|
||||||
|
if ($coord === null)
|
||||||
|
throw new PlaceException('No empty place found for entity.');
|
||||||
|
|
||||||
|
$content = $this->coords[$coord];
|
||||||
|
} while (!($content instanceof EmptyFloor));
|
||||||
|
return Coord::createFromString($coord);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private function createRandom()
|
||||||
|
{
|
||||||
|
$strokes = array();
|
||||||
|
$strokeAmount = rand(static::MIN_STROKES, static::MAX_STROKES);
|
||||||
|
|
||||||
|
$ends = array();
|
||||||
|
|
||||||
|
for ($i = 0; $i < $strokeAmount; ++$i)
|
||||||
|
{
|
||||||
|
$start = null; $dir = 0;
|
||||||
|
if (empty($ends)) // If empty, start somewhere
|
||||||
|
{
|
||||||
|
$start = new Coord(rand(2, TERM_X - 3), rand(2, TERM_Y - 3));
|
||||||
|
$freeDir = rand(self::DIR_UP, self::DIR_LEFT);
|
||||||
|
$ends[] = $start;
|
||||||
|
$strokes[(string) $start] = true;
|
||||||
|
}
|
||||||
|
else // Else find a previous intersection and start there
|
||||||
|
{
|
||||||
|
$freeDir = self::DIR_NONE;
|
||||||
|
shuffle($ends);
|
||||||
|
$endAmnt = count($ends);
|
||||||
|
for ($r = 0; $r < $endAmnt && $freeDir == self::DIR_NONE; ++$r)
|
||||||
|
{
|
||||||
|
$start = $ends[$r];
|
||||||
|
$freeDir = $this->getFreeDir($start, $strokes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No free spots could be found, end algorithm
|
||||||
|
if ($freeDir == self::DIR_NONE)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now create strokes from that place onwards
|
||||||
|
$strokeLines = rand(1, ceil($strokeAmount / 2));
|
||||||
|
|
||||||
|
for ($r = 0; $r < $strokeLines; ++$r)
|
||||||
|
{
|
||||||
|
$newStart = $this->strokeLine($start, $strokes, $ends);
|
||||||
|
if ($newStart === null) // Couldn't find a free direction
|
||||||
|
break;
|
||||||
|
$start = $newStart;
|
||||||
|
$ends[] = $start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widen created strokes
|
||||||
|
$strokes = $this->bloom($strokes);
|
||||||
|
|
||||||
|
// Build walls
|
||||||
|
$this->buildWalls($strokes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFreeDir(Coord $coord, array $reserved)
|
||||||
|
{
|
||||||
|
$dirs = range(self::DIR_UP, self::DIR_LEFT);
|
||||||
|
shuffle($dirs);
|
||||||
|
foreach ($dirs as $dir)
|
||||||
|
{
|
||||||
|
if ($dir == self::DIR_UP && $coord->y > 1 && !isset($reserved[(string) (new Coord($coord->x, $coord->y - 1))]))
|
||||||
|
return $dir;
|
||||||
|
|
||||||
|
elseif ($dir == self::DIR_DOWN && $coord->y < TERM_Y - 2 && !isset($reserved[(string) (new Coord($coord->x, $coord->y + 1))]))
|
||||||
|
return $dir;
|
||||||
|
|
||||||
|
elseif ($dir == self::DIR_RIGHT && $coord->x < TERM_X - 2 && !isset($reserved[(string) (new Coord($coord->x + 1, $coord->y))]))
|
||||||
|
return $dir;
|
||||||
|
|
||||||
|
elseif ($dir == self::DIR_LEFT && $coord->x > 1 && !isset($reserved[(string) (new Coord($coord->x - 1, $coord->y))]))
|
||||||
|
return $dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::DIR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function strokeLine(Coord $start, array &$strokes, array &$ends)
|
||||||
|
{
|
||||||
|
// Get direction
|
||||||
|
$dir = $this->getFreeDir($start, $strokes);
|
||||||
|
|
||||||
|
if ($dir == self::DIR_NONE)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
switch ($dir)
|
||||||
|
{
|
||||||
|
case self::DIR_UP:
|
||||||
|
$maxLen = ceil(($start->y - 1) / 3);
|
||||||
|
break;
|
||||||
|
case self::DIR_DOWN:
|
||||||
|
$maxLen = ceil((TERM_Y - 2 - $start->y) / 3);
|
||||||
|
break;
|
||||||
|
case self::DIR_RIGHT:
|
||||||
|
$maxLen = ceil((TERM_X - 2 - $start->x) / 3);
|
||||||
|
break;
|
||||||
|
case self::DIR_LEFT:
|
||||||
|
$maxLen = ceil(($start->x - 1) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
$length = rand(ceil($maxLen / 2), $maxLen);
|
||||||
|
|
||||||
|
$nextCoord = clone $start;
|
||||||
|
// Now add line to $strokes
|
||||||
|
for ($i = 1; $i <= $length; ++$i)
|
||||||
|
{
|
||||||
|
switch ($dir)
|
||||||
|
{
|
||||||
|
case self::DIR_UP:
|
||||||
|
$nextCoord = new Coord($nextCoord->x, $nextCoord->y - 1);
|
||||||
|
break;
|
||||||
|
case self::DIR_DOWN:
|
||||||
|
$nextCoord = new Coord($nextCoord->x, $nextCoord->y + 1);
|
||||||
|
break;
|
||||||
|
case self::DIR_RIGHT:
|
||||||
|
$nextCoord = new Coord($nextCoord->x + 1, $nextCoord->y);
|
||||||
|
break;
|
||||||
|
case self::DIR_LEFT:
|
||||||
|
$nextCoord = new Coord($nextCoord->x - 1, $nextCoord->y);
|
||||||
|
}
|
||||||
|
$strokes[(string) $nextCoord] = true;
|
||||||
|
$ends[] = $nextCoord;
|
||||||
|
}
|
||||||
|
return $nextCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function bloom(array $reserved)
|
||||||
|
{
|
||||||
|
$bloom = rand(self::BLOOM_MIN, self::BLOOM_MAX);
|
||||||
|
$added = array();
|
||||||
|
foreach ($reserved as $key => $coord)
|
||||||
|
{
|
||||||
|
$coord = Coord::createFromString($key);
|
||||||
|
for ($x = ($coord->x - $bloom); $x <= ($coord->x + $bloom); ++$x)
|
||||||
|
{
|
||||||
|
for ($y = ($coord->y - $bloom); $y <= ($coord->y + $bloom); ++$y)
|
||||||
|
{
|
||||||
|
if ($x > 0 && $x < TERM_X - 1 && $y > 0 && $y < TERM_Y - 1)
|
||||||
|
{
|
||||||
|
$added[(string) (new Coord($x, $y))] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate next bloom size
|
||||||
|
$action = rand(1, 6);
|
||||||
|
switch ($action)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
// Completely new bloom 1/6 of the time
|
||||||
|
$bloom = rand(self::BLOOM_MIN, self::BLOOM_MAX);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// Slightly new bloom 1/6 of the time
|
||||||
|
$vals = array(max($bloom - 1, self::BLOOM_MIN), min($bloom + 1, self::BLOOM_MAX));
|
||||||
|
$bloom = $vals[array_rand($vals)];
|
||||||
|
break;
|
||||||
|
// Else no change to bloom 2/3 of the time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_merge($reserved, $added);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildWalls(array $reserved)
|
||||||
|
{
|
||||||
|
foreach ($reserved as $coord => $dummy)
|
||||||
|
{
|
||||||
|
$coord = Coord::createFromString($coord);
|
||||||
|
// Loop around each cell
|
||||||
|
for ($x = ($coord->x - 1); $x <= ($coord->x + 1); ++$x)
|
||||||
|
{
|
||||||
|
for ($y = ($coord->y - 1); $y <= ($coord->y + 1); ++$y)
|
||||||
|
{
|
||||||
|
if ($x < 0 || $y < 0 || $x >= TERM_X || $y >= TERM_Y)
|
||||||
|
throw new SDGException();
|
||||||
|
if ($x >= 0 && $x < TERM_X && $y >= 0 && $y < TERM_Y && !isset($reserved[$x . ',' . $y])) // This cell is empty
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$wall = new Wall();
|
||||||
|
$wall->setPlace(new Place($this, new Coord($x, $y)));
|
||||||
|
} catch (PlaceOccupiedException $e)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create empty floor tile for reserved place
|
||||||
|
$floor = new EmptyFloor();
|
||||||
|
$floor->setPlace(new Place($this, $coord));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
class_leveldrawer.php
Normal file
50
class_leveldrawer.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class LevelDrawer
|
||||||
|
{
|
||||||
|
private $disp;
|
||||||
|
private $dude;
|
||||||
|
|
||||||
|
public function __construct(Display $disp)
|
||||||
|
{
|
||||||
|
$this->dude = DudeStore::getDude();
|
||||||
|
$this->changeDisplay($disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function changeDisplay(Display $disp)
|
||||||
|
{
|
||||||
|
$this->disp = $disp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function drawEverything()
|
||||||
|
{
|
||||||
|
$this->dude->getPlace()->getLevel()->printLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function drawDudeView()
|
||||||
|
{
|
||||||
|
$this->drawView($this->dude->getBase());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function drawView(AbstractBeing $being)
|
||||||
|
{
|
||||||
|
$radius = $being->getSight();
|
||||||
|
$beingX = $being->getPlace()->getCoord()->x;
|
||||||
|
$beingY = $being->getPlace()->getCoord()->y;
|
||||||
|
|
||||||
|
// Only print the characters inside the being's sight radius
|
||||||
|
for ($x = $beingX - $radius; $x <= $beingX + $radius; ++$x)
|
||||||
|
{
|
||||||
|
for ($y = $beingY - $radius; $y <= $beingY + $radius; ++$y)
|
||||||
|
{
|
||||||
|
$obj = $being->getPlace()->getLevel()->whatsAt(new Coord($x, $y));
|
||||||
|
if ($obj !== null)
|
||||||
|
{
|
||||||
|
// Draw path to object and only display those that can be seen
|
||||||
|
if ($being->canSee($obj))
|
||||||
|
$this->disp->writeTo($x + 1, $y + 1, $obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
class_place.php
Normal file
23
class_place.php
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Place
|
||||||
|
{
|
||||||
|
private $level;
|
||||||
|
private $coord;
|
||||||
|
|
||||||
|
public function __construct(Level $level, Coord $coord)
|
||||||
|
{
|
||||||
|
$this->level = $level;
|
||||||
|
$this->coord = $coord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCoord()
|
||||||
|
{
|
||||||
|
return $this->coord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLevel()
|
||||||
|
{
|
||||||
|
return $this->level;
|
||||||
|
}
|
||||||
|
}
|
6
class_placeexception.php
Normal file
6
class_placeexception.php
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class PlaceException extends SDGException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
6
class_placeoccupiedexception.php
Normal file
6
class_placeoccupiedexception.php
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class PlaceOccupiedException extends PlaceException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
9
class_randomitemfactory.php
Normal file
9
class_randomitemfactory.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class RandomItemFactory
|
||||||
|
{
|
||||||
|
static public function getItem(AbstractBeing $being)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
7
class_room.php
Normal file
7
class_room.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Room
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
}
|
6
class_sdgexception.php
Normal file
6
class_sdgexception.php
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class SDGException extends Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
24
class_settings.php
Normal file
24
class_settings.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Settings
|
||||||
|
{
|
||||||
|
private $settings;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->settings = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set($setting, $value)
|
||||||
|
{
|
||||||
|
$this->settings[$setting] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get($setting)
|
||||||
|
{
|
||||||
|
if (is_object($this->settings[$setting]))
|
||||||
|
return clone $this->settings[$setting];
|
||||||
|
else
|
||||||
|
return $this->settings[$setting];
|
||||||
|
}
|
||||||
|
}
|
19
class_settingsman.php
Normal file
19
class_settingsman.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class SettingsMan
|
||||||
|
{
|
||||||
|
static private $sets;
|
||||||
|
|
||||||
|
static public function get($set = 0)
|
||||||
|
{
|
||||||
|
return self::$sets[$set];
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function add($set = null, Settings $settings)
|
||||||
|
{
|
||||||
|
if ($set !== null)
|
||||||
|
self::$sets[$set] = $settings;
|
||||||
|
else
|
||||||
|
self::$sets[] = $settings;
|
||||||
|
}
|
||||||
|
}
|
7
class_wall.php
Normal file
7
class_wall.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Wall extends Entity
|
||||||
|
{
|
||||||
|
const VISIBILITY = parent::VSB_VISIBLE;
|
||||||
|
const CHAR = '#';
|
||||||
|
}
|
4
inc_ansi.php
Normal file
4
inc_ansi.php
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
define('ANSI_ESC', chr(0x1B));
|
||||||
|
define('ANSI_CSI', ANSI_ESC . '[');
|
10
inc_stuffz.php
Normal file
10
inc_stuffz.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
const DIR_NONE = 0,
|
||||||
|
DIR_UP = 1,
|
||||||
|
DIR_DOWN = 2,
|
||||||
|
DIR_RIGHT = 4,
|
||||||
|
DIR_LEFT = 8;
|
||||||
|
|
||||||
|
const TERM_X = 126,
|
||||||
|
TERM_Y = 40;
|
54
sdg.php
Normal file
54
sdg.php
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Some Dungeon Game
|
||||||
|
// 0.1
|
||||||
|
// © Nicd
|
||||||
|
|
||||||
|
require_once 'inc_stuffz.php';
|
||||||
|
require_once 'inc_ansi.php';
|
||||||
|
|
||||||
|
function __autoload($class)
|
||||||
|
{
|
||||||
|
require_once 'class_' . strtolower($class) . '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
$disp = DisplayFactory::getDefault();
|
||||||
|
$disp->clear();
|
||||||
|
$disp->setColor(Display::COLOR_RED);
|
||||||
|
|
||||||
|
$dungeon = new Dungeon();
|
||||||
|
|
||||||
|
$level = new Level($dungeon);
|
||||||
|
$dungeon->addLevelBelow($level);
|
||||||
|
|
||||||
|
$human = new Human('', 0, null, null, null, 40);
|
||||||
|
$enemy = new Human();
|
||||||
|
$enemy->setRandomPlace($level);
|
||||||
|
$dude = new Dude($human);
|
||||||
|
$human->setRandomPlace($level);
|
||||||
|
|
||||||
|
$draw = new LevelDrawer($disp);
|
||||||
|
|
||||||
|
DisplayFactory::terminalSetRaw();
|
||||||
|
|
||||||
|
$disp->clear();
|
||||||
|
$draw->drawDudeView();
|
||||||
|
|
||||||
|
$input = '';
|
||||||
|
do
|
||||||
|
{
|
||||||
|
$input = fread(STDIN, 1);
|
||||||
|
if (ctype_digit($input))
|
||||||
|
{
|
||||||
|
$dude->move($input);
|
||||||
|
$disp->clear();
|
||||||
|
$draw->drawDudeView();
|
||||||
|
}
|
||||||
|
elseif ($input == 'd')
|
||||||
|
{
|
||||||
|
$enemy->drawPath($disp, $dude->getBase());
|
||||||
|
}
|
||||||
|
} while($input != 'q');
|
||||||
|
|
||||||
|
$disp->setStyle();
|
||||||
|
DisplayFactory::terminalSetCooked();
|
Loading…
Reference in a new issue