// tessellates the stroke of the line from a_ to b_ with the given width and a flat cap void QTessellator::tessellateRect(const QPointF &a_, const QPointF &b_, qreal width) { Vertex a = { FloatToQ27Dot5(a_.x()), FloatToQ27Dot5(a_.y()) }; Vertex b = { FloatToQ27Dot5(b_.x()), FloatToQ27Dot5(b_.y()) }; QPointF pa = a_, pb = b_; if (a.y > b.y) { qSwap(a, b); qSwap(pa, pb); } Vertex delta = { b.x - a.x, b.y - a.y }; if (delta.x == 0 && delta.y == 0) return; qreal hw = 0.5 * width; if (delta.x == 0) { Q27Dot5 halfWidth = FloatToQ27Dot5(hw); if (halfWidth == 0) return; Vertex topLeft = { a.x - halfWidth, a.y }; Vertex topRight = { a.x + halfWidth, a.y }; Vertex bottomLeft = { a.x - halfWidth, b.y }; Vertex bottomRight = { a.x + halfWidth, b.y }; QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight }; addTrap(trap); } else if (delta.y == 0) { Q27Dot5 halfWidth = FloatToQ27Dot5(hw); if (halfWidth == 0) return; if (a.x > b.x) qSwap(a.x, b.x); Vertex topLeft = { a.x, a.y - halfWidth }; Vertex topRight = { b.x, a.y - halfWidth }; Vertex bottomLeft = { a.x, a.y + halfWidth }; Vertex bottomRight = { b.x, a.y + halfWidth }; QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight }; addTrap(trap); } else { QPointF perp(pb.y() - pa.y(), pa.x() - pb.x()); qreal length = qSqrt(perp.x() * perp.x() + perp.y() * perp.y()); if (qFuzzyIsNull(length)) return; // need the half of the width perp *= hw / length; QPointF pta = pa + perp; QPointF ptb = pa - perp; QPointF ptc = pb - perp; QPointF ptd = pb + perp; Vertex ta = { FloatToQ27Dot5(pta.x()), FloatToQ27Dot5(pta.y()) }; Vertex tb = { FloatToQ27Dot5(ptb.x()), FloatToQ27Dot5(ptb.y()) }; Vertex tc = { FloatToQ27Dot5(ptc.x()), FloatToQ27Dot5(ptc.y()) }; Vertex td = { FloatToQ27Dot5(ptd.x()), FloatToQ27Dot5(ptd.y()) }; if (ta.y < tb.y) { if (tb.y < td.y) { QTessellator::Trapezoid top = { ta.y, tb.y, &ta, &tb, &ta, &td }; QTessellator::Trapezoid bottom = { td.y, tc.y, &tb, &tc, &td, &tc }; addTrap(top); addTrap(bottom); QTessellator::Trapezoid middle = { tb.y, td.y, &tb, &tc, &ta, &td }; addTrap(middle); } else { QTessellator::Trapezoid top = { ta.y, td.y, &ta, &tb, &ta, &td }; QTessellator::Trapezoid bottom = { tb.y, tc.y, &tb, &tc, &td, &tc }; addTrap(top); addTrap(bottom); if (tb.y != td.y) { QTessellator::Trapezoid middle = { td.y, tb.y, &ta, &tb, &td, &tc }; addTrap(middle); } } } else { if (ta.y < tc.y) { QTessellator::Trapezoid top = { tb.y, ta.y, &tb, &tc, &tb, &ta }; QTessellator::Trapezoid bottom = { tc.y, td.y, &tc, &td, &ta, &td }; addTrap(top); addTrap(bottom); QTessellator::Trapezoid middle = { ta.y, tc.y, &tb, &tc, &ta, &td }; addTrap(middle); } else { QTessellator::Trapezoid top = { tb.y, tc.y, &tb, &tc, &tb, &ta }; QTessellator::Trapezoid bottom = { ta.y, td.y, &tc, &td, &ta, &td }; addTrap(top); addTrap(bottom); if (ta.y != tc.y) { QTessellator::Trapezoid middle = { tc.y, ta.y, &tc, &td, &tb, &ta }; addTrap(middle); } } } } }
int shMapLevel::buildSewer () { int x, y, i, n; int lighting = RNG (2) ? -1 : 1; SewerRoom nodes[NODECOLS][NODEROWS]; mMapType = kSewer; x = RNG (NODECOLS); y = RNG (NODEROWS); //first pass buildSewerHelper (nodes, x, y, 0); //second pass, make more tightly connected for (n = 10; n; n--) { if (RNG(3)) { x = RNG (NODECOLS-1); y = RNG (NODEROWS); nodes[x][y].mWalls[1] = 0; nodes[x+1][y].mWalls[2] = 0; } else { x = RNG (NODECOLS); y = RNG (NODEROWS-1); nodes[x][y].mWalls[3] = 0; nodes[x][y+1].mWalls[0] = 0; } } for (x = 0; x < NODECOLS; x++) { for (y = 0; y < NODEROWS; y++) { if (nodes[x][y].mDeadEnd or !RNG(7)) { nodes[x][y].mBulbous = 1; } } } // force 2 bulbous rooms x = RNG (NODECOLS/3); y = RNG (NODEROWS); nodes[x][y].mBulbous = 1; x = NODECOLS - 1 - RNG (NODECOLS/3); y = RNG (NODEROWS); nodes[x][y].mBulbous = 1; // don't have adjacent bulbous rooms (looks bad) for (x = 0; x < NODECOLS-1; x++) { for (y = 0; y < NODEROWS; y++) { if (nodes[x][y].mBulbous and nodes[x+1][y].mBulbous and nodes[x][y].mWalls[1]) { if (RNG(2)) nodes[x][y].mBulbous = 0; else nodes[x+1][y].mBulbous = 0; } } } for (x = 0; x < NODECOLS; x++) { for (y = 0; y < NODEROWS-1; y++) { if (nodes[x][y].mBulbous and nodes[x][y+1].mBulbous and nodes[x][y].mWalls[3]) { if (RNG(2)) nodes[x][y].mBulbous = 0; else nodes[x][y+1].mBulbous = 0; } } } for (x = 0; x < NODECOLS; x++) { for (y = 0; y < NODEROWS; y++) { buildSewerRoom (nodes, x, y); } } for (n = NDX(2,4); n; n--) { floodMuck (RNG(NODECOLS)*6+5, RNG(NODEROWS)*4+2, kSewage, NDX(8,20)); } for (i = NDX (2, 3); i; --i) { findUnoccupiedSquare (&x, &y); switch (RNG (4)) { case 0: addTrap (x, y, shFeature::kPit); break; case 1: addTrap (x, y, shFeature::kHole); break; case 2: addTrap (x, y, shFeature::kTrapDoor); break; case 3: addTrap (x, y, shFeature::kRadTrap); break; } } for (i = 0; i < 8; i++) { findUnoccupiedSquare (&x, &y); putObject (generateObject (mDLevel), x, y); } for (x = 0; x < mColumns; x++) for (y = 0; y < mRows; y++) setLit (x, y, lighting, lighting, lighting, lighting); return 1; }
// tessellates the given convex polygon void QTessellator::tessellateConvex(const QPointF *points, int nPoints) { Q_ASSERT(points[0] == points[nPoints-1]); --nPoints; d->vertices.nPoints = nPoints; d->vertices.init(nPoints); for (int i = 0; i < nPoints; ++i) { d->vertices[i]->x = FloatToQ27Dot5(points[i].x()); d->vertices[i]->y = FloatToQ27Dot5(points[i].y()); } int left = 0, right = 0; int top = 0; for (int i = 1; i < nPoints; ++i) { if (d->vertices[i]->y < d->vertices[top]->y) top = i; } left = (top + nPoints - 1) % nPoints; right = (top + 1) % nPoints; while (d->vertices[left]->x == d->vertices[top]->x && d->vertices[left]->y == d->vertices[top]->y && left != right) left = (left + nPoints - 1) % nPoints; while (d->vertices[right]->x == d->vertices[top]->x && d->vertices[right]->y == d->vertices[top]->y && left != right) right = (right + 1) % nPoints; if (left == right) return; int dir = 1; Vertex dLeft = { d->vertices[top]->x - d->vertices[left]->x, d->vertices[top]->y - d->vertices[left]->y }; Vertex dRight = { d->vertices[right]->x - d->vertices[top]->x, d->vertices[right]->y - d->vertices[top]->y }; Q27Dot5 cross = dLeft.x * dRight.y - dLeft.y * dRight.x; // flip direction if polygon is clockwise if (cross < 0 || (cross == 0 && dLeft.x > 0)) { qSwap(left, right); dir = -1; } Vertex *lastLeft = d->vertices[top]; Vertex *lastRight = d->vertices[top]; QTessellator::Trapezoid trap; while (lastLeft->y == d->vertices[left]->y && left != right) { lastLeft = d->vertices[left]; left = (left + nPoints - dir) % nPoints; } while (lastRight->y == d->vertices[right]->y && left != right) { lastRight = d->vertices[right]; right = (right + nPoints + dir) % nPoints; } while (true) { trap.top = qMax(lastRight->y, lastLeft->y); trap.bottom = qMin(d->vertices[left]->y, d->vertices[right]->y); trap.topLeft = lastLeft; trap.topRight = lastRight; trap.bottomLeft = d->vertices[left]; trap.bottomRight = d->vertices[right]; if (trap.bottom > trap.top) addTrap(trap); if (left == right) break; if (d->vertices[right]->y < d->vertices[left]->y) { do { lastRight = d->vertices[right]; right = (right + nPoints + dir) % nPoints; } while (lastRight->y == d->vertices[right]->y && left != right); } else { do { lastLeft = d->vertices[left]; left = (left + nPoints - dir) % nPoints; } while (lastLeft->y == d->vertices[left]->y && left != right); } } }
void shMapLevel::mundaneRoom (int sx, int sy, int ex, int ey) { int i, npiles, n; int x, y; if (mDLevel != TOWNLEVEL) { if (RNG (mDLevel + 6) > 9) { darkRoom (sx, sy, ex, ey); } if (!RNG (30)) { /* Radioactive room. */ for (x = sx + 1; x < ex; x++) { for (y = sy + 1; y < ey; y++) { mSquares[x][y].mFlags |= shSquare::kRadioactive; } } } } /* treasure expectation per room: version 0.3: 0.375 piles x 1.1875 obj per pile + (~) 0.125 niches x 0.8 obj per niche ~ .5453125 objs per room version 0.2.9: 0.375 piles x 1.375 obj per pile = .515625 obj per room */ npiles = RNG (4) ? 0 : RNG (2) + RNG (2) + RNG (2); while (npiles--) { x = RNG (sx + 1, ex - 1); y = RNG (sy + 1, ey - 1); if (TESTSQ (x, y, kFloor) and !isObstacle (x, y)) { n = RNG (10) ? 1 : RNG (2, 3); for (i = 0; i < n; i++) { putObject (generateObject (mDLevel), x, y); } } } if (0 == RNG (9)) { x = RNG (sx + 1, ex - 1); y = RNG (sy + 1, ey - 1); if (TESTSQ (x, y, kFloor) and !isObstacle (x, y) and 0 == countObjects (x, y)) { addVat (x, y); } } /* Reticulans of PRIME are weak enough to get instakilled in a pit. Do not place traps at all on first level. */ while (!RNG (12) and mDLevel > 1) { x = RNG (sx + 1, ex - 1); y = RNG (sy + 1, ey - 1); if (TESTSQ (x, y, kFloor) and !isObstacle (x, y) and !getFeature (x, y)) { shFeature::Type ttype; retrap: /* I'm using this retrap label because putting a for or while loop here uncovers a bug in g++! */ /* FIXME: Probably the bug is long gone. Get rid of goto. */ ttype = (shFeature::Type) RNG (shFeature::kPit, shFeature::kPortal); switch (ttype) { case shFeature::kPit: case shFeature::kTrapDoor: break; case shFeature::kAcidPit: if (mDLevel < 7) goto retrap; break; case shFeature::kHole: if (RNG (5)) goto retrap; break; case shFeature::kWeb: goto retrap; case shFeature::kRadTrap: if (mDLevel < 5) goto retrap; break; case shFeature::kPortal: default: /* these traps are unimplemented */ goto retrap; } addTrap (x, y, ttype); } } }