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