You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
5.9 KiB
223 lines
5.9 KiB
#include <tqcolor.h>
|
|
#include <tqfontmetrics.h>
|
|
#include <tqpainter.h>
|
|
#include <tqpixmap.h>
|
|
#include <tqtimer.h>
|
|
#include <tqwidget.h>
|
|
|
|
#include <tdeglobalsettings.h>
|
|
|
|
#include "screen.h"
|
|
#include "sprite.h"
|
|
#include "aasaver.h"
|
|
|
|
Screen::Screen(AASaver* widget): m_widget(widget)
|
|
{
|
|
TQFontMetrics fm(TDEGlobalSettings::fixedFont());
|
|
|
|
// Compute cell geometries.
|
|
m_cellW = fm.maxWidth();
|
|
m_cellH = fm.lineSpacing();
|
|
|
|
// Computer number of full cells that will fit.
|
|
m_width = widget->width() / m_cellW;
|
|
m_height = widget->height() / m_cellH;
|
|
|
|
// Calculate offset needed to evenly distribute excess screen space.
|
|
m_offX = (widget->width() - m_width * m_cellW) / 2;
|
|
m_offY = (widget->height() - m_height * m_cellH) / 2;
|
|
|
|
// Create double buffer.
|
|
m_backBuffer = TQPixmap(m_widget->size());
|
|
m_backBuffer.fill(black);
|
|
|
|
// FIXME: handle resizing!
|
|
|
|
// Setup animation timer.
|
|
TQTimer* timer = new TQTimer(this);
|
|
connect(timer, TQ_SIGNAL(timeout()), TQ_SLOT(doAnimate()));
|
|
|
|
timer->start(msPerTick());
|
|
}
|
|
|
|
int Screen::msPerTick() const
|
|
{
|
|
return 50;
|
|
}
|
|
|
|
Screen::~Screen()
|
|
{
|
|
}
|
|
|
|
void Screen::updateSpan(int x, int y, const TQPixmap &updatePixmap)
|
|
{
|
|
if (y < 0 || y >= m_height) return;
|
|
|
|
TQPoint upperLeft(m_offX + x * m_cellW, m_offY + y * m_cellH);
|
|
bitBlt(&m_backBuffer, upperLeft, &updatePixmap, updatePixmap.rect(), TQt::CopyROP);
|
|
m_widget->update(TQRect(upperLeft, updatePixmap.size()));
|
|
}
|
|
|
|
void Screen::clearSpan(int x, int y, const TQPixmap &clearPixmap)
|
|
{
|
|
if (y < 0 || y >= m_height) return;
|
|
|
|
TQPoint upperLeft(m_offX + x * m_cellW, m_offY + y * m_cellH);
|
|
bitBlt(&m_backBuffer, upperLeft, &clearPixmap, clearPixmap.rect(), TQt::CopyROP);
|
|
m_widget->update(TQRect(upperLeft, clearPixmap.size()));
|
|
}
|
|
|
|
//Actually paints the region on the widget.
|
|
void Screen::paint(TQRegion r)
|
|
{
|
|
TQPainter p(m_widget);
|
|
TQMemArray<TQRect> rects = r.rects();
|
|
|
|
for (int r = 0; r < rects.size(); ++r)
|
|
{
|
|
//Determine the grid locations described by the rect
|
|
TQRect bound = rects[r];
|
|
|
|
bitBlt(m_widget, bound.topLeft(), &m_backBuffer, bound, TQt::CopyROP);
|
|
} //for rect in region
|
|
};
|
|
|
|
/**
|
|
* Utility type used to faciliate sorting of the Sprite list in order to
|
|
* implement the Painter's Algorithm when painting the back buffer.
|
|
*/
|
|
struct ZKey
|
|
{
|
|
/**
|
|
* Logical depth of sprite. Now 0 is farthest away from the eyes, unlike
|
|
* with Sprite::depth().
|
|
*/
|
|
int z;
|
|
|
|
Sprite* addr;
|
|
|
|
ZKey(): z(0), addr(0)
|
|
{}
|
|
|
|
ZKey(Sprite* spr): z(1000 - spr->depth()), addr(spr)
|
|
{}
|
|
|
|
bool operator<(const ZKey& other) const
|
|
{
|
|
if (z < other.z) return true;
|
|
if (z > other.z) return false;
|
|
|
|
return addr < other.addr;
|
|
}
|
|
};
|
|
|
|
void Screen::doAnimate()
|
|
{
|
|
//First, rebuild a new list of sprites, and build a dirty region
|
|
TQRegion dirtyRegion;
|
|
|
|
TQValueVector<Sprite*> sprites;
|
|
TQValueVector<Sprite*> colliders;
|
|
|
|
// Look for sprites that can suffer a collision.
|
|
for (unsigned pos = 0; pos < m_sprites.size(); ++pos)
|
|
{
|
|
if(m_sprites[pos]->canCollide())
|
|
colliders.append(m_sprites[pos]);
|
|
}
|
|
|
|
// Find collisions.
|
|
// FIXME: Use transparent regions for accuracy.
|
|
for (unsigned pos = 0; pos < colliders.size(); ++pos)
|
|
for (unsigned sprite = 0; sprite < m_sprites.size(); ++sprite)
|
|
{
|
|
if(m_sprites[sprite] == colliders[pos])
|
|
continue;
|
|
|
|
if(colliders[pos]->geom().intersects(m_sprites[sprite]->geom()))
|
|
colliders[pos]->collision(m_sprites[sprite]);
|
|
}
|
|
|
|
//Retain all live existing sprites
|
|
for (int pos = 0; pos < m_sprites.size(); ++pos)
|
|
{
|
|
Sprite* sprite = m_sprites[pos];
|
|
TQRect oldRect = sprite->geom();
|
|
if (!sprite->isKilled()) {
|
|
bool dirty = sprite->tickUpdate();
|
|
|
|
if (dirty)
|
|
dirtyRegion |= oldRect | sprite->geom();
|
|
|
|
if (!sprite->isKilled())
|
|
sprites.append(sprite);
|
|
}
|
|
|
|
if (sprite->isKilled()) //note:may be made true by updateTick!
|
|
{
|
|
dirtyRegion |= oldRect;
|
|
delete sprite;
|
|
}
|
|
}
|
|
|
|
//Add new sprites.
|
|
for (int pos = 0; pos < m_addedSprites.size(); ++pos)
|
|
{
|
|
dirtyRegion |= m_addedSprites[pos]->geom();
|
|
sprites.append(m_addedSprites[pos]);
|
|
}
|
|
|
|
m_addedSprites.clear();
|
|
m_sprites = sprites;
|
|
|
|
//Compute the list of sprites affected. Note that this is
|
|
//done iteratively until fixed point.
|
|
TQValueVector<Sprite*> paintSprites;
|
|
TQValueVector<Sprite*> remSprites;
|
|
|
|
bool changed;
|
|
do
|
|
{
|
|
changed = false;
|
|
remSprites.clear();
|
|
|
|
for (int c = 0; c < sprites.size(); ++c)
|
|
{
|
|
Sprite* sprite = sprites[c];
|
|
|
|
if (dirtyRegion.intersect(sprite->geom()).isEmpty())
|
|
remSprites.append(sprite); //not to be painted thus far
|
|
else
|
|
{
|
|
//This sprite is to be painted
|
|
paintSprites.append(sprite);
|
|
|
|
//make sure we repaint everything overlapping it
|
|
dirtyRegion |= sprite->geom();
|
|
changed = true;
|
|
}
|
|
}
|
|
sprites = remSprites;
|
|
}
|
|
while (changed);
|
|
|
|
//Z-sort the items.
|
|
TQMap<ZKey, Sprite* > sorted;
|
|
for (int pos = 0; pos < paintSprites.size(); ++pos)
|
|
sorted[ZKey(paintSprites[pos])] = paintSprites[pos];
|
|
|
|
//Paint, in Z-order
|
|
for (TQMapIterator<ZKey, Sprite*> i = sorted.begin();
|
|
i != sorted.end(); ++i)
|
|
i.data()->paint();
|
|
|
|
// Make sure black strip at edge is still present.
|
|
if(!paintSprites.isEmpty())
|
|
{
|
|
TQPainter p(&m_backBuffer);
|
|
p.fillRect(m_backBuffer.width() - m_offX, 0, m_offX, m_backBuffer.height(), TQt::black);
|
|
}
|
|
}
|
|
|
|
#include "screen.moc"
|