bool fixProblem(unsigned index, unsigned fix_type, MapEditor* editor) { if (index >= things.size()) return false; if (fix_type == 0) { MapThing* thing = things[index]; MapLine* line = lines[index]; // Get nearest line point to thing fpoint2_t np = MathStuff::closestPointOnLine(thing->xPos(), thing->yPos(), line->x1(), line->y1(), line->x2(), line->y2()); // Get distance to move double r = theGameConfiguration->thingType(thing->getType())->getRadius(); double dist = MathStuff::distance(0, 0, r, r); editor->beginUndoRecord("Move Thing", true, false, false); // Move along line direction map->moveThing(thing->getIndex(), np.x - (line->frontVector().x * dist), np.y - (line->frontVector().y * dist)); editor->endUndoRecord(); return true; } return false; }
void MapSpecials::applyLineSlopeThing(SLADEMap* map, MapThing* thing) { int lineid = thing->intProperty("arg0"); if (!lineid) { LOG_MESSAGE(1, "Ignoring line slope thing %d with no lineid argument", thing->getIndex()); return; } // These are computed on first use, to avoid extra work if no lines match MapSector* containing_sector = nullptr; double thingz; vector<MapLine*> lines; map->getLinesById(lineid, lines); for (unsigned b = 0; b < lines.size(); b++) { MapLine* line = lines[b]; // Line slope things only affect the sector on the side of the line // that faces the thing double side = MathStuff::lineSide(thing->point(), line->seg()); MapSector* target = nullptr; if (side < 0) target = line->backSector(); else if (side > 0) target = line->frontSector(); if (!target) continue; // Need to know the containing sector's height to find the thing's true height if (!containing_sector) { int containing_sector_idx = map->sectorAt(thing->point()); if (containing_sector_idx < 0) return; containing_sector = map->getSector(containing_sector_idx); thingz = ( containing_sector->getPlane<p>().height_at(thing->point()) + thing->floatProperty("height") ); } // Three points: endpoints of the line, and the thing itself plane_t target_plane = target->getPlane<p>(); fpoint3_t p1(lines[b]->x1(), lines[b]->y1(), target_plane.height_at(lines[b]->point1())); fpoint3_t p2(lines[b]->x2(), lines[b]->y2(), target_plane.height_at(lines[b]->point2())); fpoint3_t p3(thing->xPos(), thing->yPos(), thingz); target->setPlane<p>(MathStuff::planeFromTriangle(p1, p2, p3)); } }
/* MapSector::updateBBox * Calculates the sector's bounding box *******************************************************************/ void MapSector::updateBBox() { // Reset bounding box bbox.reset(); for (unsigned a = 0; a < connected_sides.size(); a++) { MapLine* line = connected_sides[a]->getParentLine(); if (!line) continue; bbox.extend(line->v1()->xPos(), line->v1()->yPos()); bbox.extend(line->v2()->xPos(), line->v2()->yPos()); } text_point.set(0, 0); setGeometryUpdated(); }
bool fixProblem(unsigned index, unsigned fix_type, MapEditor* editor) { if (index >= lines.size()) return false; MapLine* line = map->getLine(lines[index]); if (line->s2()) { // Flip if (fix_type == 0) { line->flip(); return true; } // Create sector else if (fix_type == 1) { fpoint2_t pos = line->dirTabPoint(0.1); editor->createSector(pos.x, pos.y); doCheck(); return true; } } else { // Delete if (fix_type == 0) { map->removeLine(line); doCheck(); return true; } // Create sector else if (fix_type == 1) { fpoint2_t pos = line->dirTabPoint(0.1); editor->createSector(pos.x, pos.y); doCheck(); return true; } } return false; }
/* MapLine::copy * Copies another map object [c] *******************************************************************/ void MapLine::copy(MapObject* c) { if(getObjType() != c->getObjType()) return; MapObject::copy(c); MapLine* l = static_cast<MapLine*>(c); if(side1 && l->side1) side1->copy(l->side1); if(side2 && l->side2) side2->copy(l->side2); setIntProperty("special", l->intProperty("special")); }
/* MapSector::isWithin * Returns true if the point is inside the sector *******************************************************************/ bool MapSector::isWithin(fpoint2_t point) { // Check with bbox first if (!boundingBox().contains(point)) return false; // Find nearest line in the sector double dist; double min_dist = 999999; MapLine* nline = nullptr; for (unsigned a = 0; a < connected_sides.size(); a++) { // Calculate distance to line //if (connected_sides[a] == NULL) { // LOG_MESSAGE(3, "Warning: connected side #%i is a NULL pointer!", a); // continue; //} else if (connected_sides[a]->getParentLine() == NULL) { // LOG_MESSAGE(3, "Warning: connected side #%i has a NULL pointer parent line!", connected_sides[a]->getIndex()); // continue; //} dist = connected_sides[a]->getParentLine()->distanceTo(point); // Check distance if (dist < min_dist) { nline = connected_sides[a]->getParentLine(); min_dist = dist; } } // No nearest (shouldn't happen) if (!nline) return false; // Check the side of the nearest line double side = MathStuff::lineSide(point, nline->seg()); if (side >= 0 && nline->frontSector() == this) return true; else if (side < 0 && nline->backSector() == this) return true; else return false; }
/** Removes the map entry of the given font. * If the font is locked (because it's already in use) nothing happens. * @param[in] mapline parsed font data * @return true if entry has been removed */ bool FontMap::remove (const MapLine &mapline) { bool removed = false; if (!mapline.texname().empty()) { vector<Subfont*> subfonts; if (mapline.sfd()) mapline.sfd()->subfonts(subfonts); else subfonts.push_back(nullptr); for (const Subfont *subfont : subfonts) { string fontname = mapline.texname()+(subfont ? subfont->id() : ""); auto it = _entries.find(fontname); if (it != _entries.end() && !it->second->locked) { _entries.erase(it); removed = true; } } } return removed; }
/* MapSector::distanceTo * Returns the minimum distance from the point to the closest line in * the sector *******************************************************************/ double MapSector::distanceTo(fpoint2_t point, double maxdist) { // Init if (maxdist < 0) maxdist = 9999999; // Check bounding box first if (!bbox.is_valid()) updateBBox(); double min_dist = 9999999; double dist = MathStuff::distanceToLine(point, bbox.left_side()); if (dist < min_dist) min_dist = dist; dist = MathStuff::distanceToLine(point, bbox.top_side()); if (dist < min_dist) min_dist = dist; dist = MathStuff::distanceToLine(point, bbox.right_side()); if (dist < min_dist) min_dist = dist; dist = MathStuff::distanceToLine(point, bbox.bottom_side()); if (dist < min_dist) min_dist = dist; if (min_dist > maxdist && !bbox.contains(point)) return -1; // Go through connected sides for (unsigned a = 0; a < connected_sides.size(); a++) { // Get side parent line MapLine* line = connected_sides[a]->getParentLine(); if (!line) continue; // Check distance dist = line->distanceTo(point); if (dist < min_dist) min_dist = dist; } return min_dist; }
/* MapSector::getVertices * Adds all vertices that are part of the sector to [list] *******************************************************************/ bool MapSector::getVertices(vector<MapObject*>& list) { // Go through connected sides MapLine* line; for (unsigned a = 0; a < connected_sides.size(); a++) { line = connected_sides[a]->getParentLine(); // Add the side's parent line's vertices to the list if they doesn't already exist if (line->v1() && std::find(list.begin(), list.end(), line->v1()) == list.end()) list.push_back(line->v1()); if (line->v2() && std::find(list.begin(), list.end(), line->v2()) == list.end()) list.push_back(line->v2()); } return true; }
void doCheck() { double radius; // Get list of lines to check vector<MapLine*> check_lines; MapLine* line; for (unsigned a = 0; a < map->nLines(); a++) { line = map->getLine(a); // Skip if line is 2-sided and not blocking if (line->s2() && !theGameConfiguration->lineBasicFlagSet("blocking", line, map->currentFormat())) continue; check_lines.push_back(line); } // Go through things for (unsigned a = 0; a < map->nThings(); a++) { MapThing* thing = map->getThing(a); ThingType* tt = theGameConfiguration->thingType(thing->getType()); // Skip if not a solid thing if (!tt->isSolid()) continue; radius = tt->getRadius() - 1; // Go through lines for (unsigned b = 0; b < check_lines.size(); b++) { line = check_lines[b]; // Check intersection if (MathStuff::boxLineIntersect(thing->xPos() - radius, thing->yPos() - radius, thing->xPos() + radius, thing->yPos() + radius, line->x1(), line->y1(), line->x2(), line->y2())) { things.push_back(thing); lines.push_back(line); break; } } } }
/** Appends given map line data to the font map if there is no entry for the corresponding * font in the map yet. * @param[in] mapline parsed font data * @return true if data has been appended */ bool FontMap::append (const MapLine &mapline) { bool appended = false; if (!mapline.texname().empty()) { if (!mapline.fontfname().empty() || !mapline.encname().empty()) { vector<Subfont*> subfonts; if (mapline.sfd()) mapline.sfd()->subfonts(subfonts); else subfonts.push_back(nullptr); for (Subfont *subfont : subfonts) { string fontname = mapline.texname()+(subfont ? subfont->id() : ""); auto it = _entries.find(fontname); if (it == _entries.end()) { _entries.emplace(fontname, util::make_unique<Entry>(mapline, subfont)); appended = true; } } } } return appended; }
/** Replaces the map data of the given font. * If the font is locked (because it's already in use) nothing happens. * @param[in] mapline parsed font data * @return true if data has been replaced */ bool FontMap::replace (const MapLine &mapline) { if (mapline.texname().empty()) return false; if (mapline.fontfname().empty() && mapline.encname().empty()) return remove(mapline); vector<Subfont*> subfonts; if (mapline.sfd()) mapline.sfd()->subfonts(subfonts); else subfonts.push_back(nullptr); for (Subfont *subfont : subfonts) { string fontname = mapline.texname()+(subfont ? subfont->id() : ""); auto it = _entries.find(fontname); if (it == _entries.end()) _entries.emplace(fontname, util::make_unique<Entry>(mapline, subfont)); else if (!it->second->locked) *it->second = Entry(mapline, subfont); } return true; }
/* MapArchClipboardItem::pasteToMap * Pastes copied architecture to [map] at [position] *******************************************************************/ vector<MapVertex*> MapArchClipboardItem::pasteToMap(SLADEMap* map, fpoint2_t position) { std::map<MapVertex*, MapVertex*> vertMap; std::map<MapSector*, MapSector*> sectMap; std::map<MapSide*, MapSide*> sideMap; // Not used yet... // std::map<MapLine*, MapLine*> lineMap; // Add vertices vector<MapVertex*> new_verts; for (unsigned a = 0; a < vertices.size(); a++) { new_verts.push_back(map->createVertex(position.x + vertices[a]->xPos(), position.y + vertices[a]->yPos())); new_verts.back()->copy(vertices[a]); vertMap[vertices[a]] = new_verts.back(); } // Add sectors for (unsigned a = 0; a < sectors.size(); a++) { MapSector* new_sector = map->createSector(); new_sector->copy(sectors[a]); sectMap[sectors[a]] = new_sector; } // Add sides int first_new_side = map->nSides(); for (unsigned a = 0; a < sides.size(); a++) { // Get relative sector MapSector* sector = findInMap(sectMap, sides[a]->getSector()); MapSide* new_side = map->createSide(sector); new_side->copy(sides[a]); sideMap[sides[a]] = new_side; } // Add lines int first_new_line = map->nLines(); for (unsigned a = 0; a < lines.size(); a++) { // Get relative vertices MapVertex* v1 = findInMap(vertMap, lines[a]->v1()); MapVertex* v2 = findInMap(vertMap, lines[a]->v2()); if (!v1) { wxLogMessage("no v1"); continue; } if (!v2) { wxLogMessage("no v2"); } MapLine* newline = map->createLine(v1, v2, true); newline->copy(lines[a]); // lineMap[lines[a]] = newline; // Set relative sides bool s1 = false; bool s2 = !(lines[a]->s2()); MapSide* newS1 = findInMap(sideMap, lines[a]->s1()); MapSide* newS2 = findInMap(sideMap, lines[a]->s2()); if(newS1) newline->setS1(newS1); if(newS2) newline->setS2(newS2); // Set important flags (needed when copying from Doom/Hexen format to UDMF) // Won't be needed when proper map format conversion stuff is implemented theGameConfiguration->setLineBasicFlag( "twosided", newline, map->currentFormat(), (newS1 && newS2) ); theGameConfiguration->setLineBasicFlag( "blocking", newline, map->currentFormat(), !newS2 ); } // TODO: // - Split lines // - Merge lines //// Fix sector references //// TODO: figure out what lines are 'outside' on copy, only fix said lines //for (unsigned a = first_new_line; a < map->nLines(); a++) //{ // MapLine* line = map->getLine(a); // MapSector* sec1 = map->getLineSideSector(line, true); // MapSector* sec2 = map->getLineSideSector(line, false); // int i1 = -1; // int i2 = -2; // if (sec1) i1 = sec1->getIndex(); // if (sec2) i2 = sec2->getIndex(); // map->setLineSector(a, i1, true); // map->setLineSector(a, i2, false); //} return new_verts; }
/* MapArchClipboardItem::addLines * Copies [lines] and all related map structures *******************************************************************/ void MapArchClipboardItem::addLines(vector<MapLine*> lines) { // Get sectors and sides to copy vector<MapSector*> copy_sectors; vector<MapSide*> copy_sides; for (unsigned a = 0; a < lines.size(); a++) { MapSide* s1 = lines[a]->s1(); MapSide* s2 = lines[a]->s2(); // Front side if (s1) { copy_sides.push_back(s1); if (std::find(copy_sectors.begin(), copy_sectors.end(), s1->getSector()) == copy_sectors.end()) copy_sectors.push_back(s1->getSector()); } // Back side if (s2) { copy_sides.push_back(s2); if (std::find(copy_sectors.begin(), copy_sectors.end(), s2->getSector()) == copy_sectors.end()) copy_sectors.push_back(s2->getSector()); } } // Copy sectors for (unsigned a = 0; a < copy_sectors.size(); a++) { MapSector* copy = new MapSector(NULL); copy->copy(copy_sectors[a]); sectors.push_back(copy); } // Copy sides for (unsigned a = 0; a < copy_sides.size(); a++) { MapSide* copy = new MapSide(); copy->copy(copy_sides[a]); // Set relative sector for (unsigned b = 0; b < copy_sectors.size(); b++) { if (copy_sides[a]->getSector() == copy_sectors[b]) { copy->setSector(sectors[b]); break; } } sides.push_back(copy); } // Get vertices to copy (and determine midpoint) double min_x = 9999999; double max_x = -9999999; double min_y = 9999999; double max_y = -9999999; vector<MapVertex*> copy_verts; for (unsigned a = 0; a < lines.size(); a++) { MapVertex* v1 = lines[a]->v1(); MapVertex* v2 = lines[a]->v2(); // Add vertices to copy list if (std::find(copy_verts.begin(), copy_verts.end(), v1) == copy_verts.end()) copy_verts.push_back(v1); if (std::find(copy_verts.begin(), copy_verts.end(), v2) == copy_verts.end()) copy_verts.push_back(v2); // Update min/max if (v1->xPos() < min_x) min_x = v1->xPos(); if (v1->xPos() > max_x) max_x = v1->xPos(); if (v1->yPos() < min_y) min_y = v1->yPos(); if (v1->yPos() > max_y) max_y = v1->yPos(); if (v2->xPos() < min_x) min_x = v2->xPos(); if (v2->xPos() > max_x) max_x = v2->xPos(); if (v2->yPos() < min_y) min_y = v2->yPos(); if (v2->yPos() > max_y) max_y = v2->yPos(); } // Determine midpoint double mid_x = min_x + ((max_x - min_x) * 0.5); double mid_y = min_y + ((max_y - min_y) * 0.5); this->midpoint.set(mid_x, mid_y); // Copy vertices for (unsigned a = 0; a < copy_verts.size(); a++) { MapVertex* copy = new MapVertex(copy_verts[a]->xPos() - mid_x, copy_verts[a]->yPos() - mid_y); copy->copy(copy_verts[a]); vertices.push_back(copy); } // Copy lines for (unsigned a = 0; a < lines.size(); a++) { // Get relative sides MapSide* s1 = NULL; MapSide* s2 = NULL; bool s1_found = false; bool s2_found = !(lines[a]->s2()); for (unsigned b = 0; b < copy_sides.size(); b++) { if (lines[a]->s1() == copy_sides[b]) { s1 = sides[b]; s1_found = true; } if (lines[a]->s2() == copy_sides[b]) { s2 = sides[b]; s2_found = true; } if (s1_found && s2_found) break; } // Get relative vertices MapVertex* v1 = NULL; MapVertex* v2 = NULL; for (unsigned b = 0; b < copy_verts.size(); b++) { if (lines[a]->v1() == copy_verts[b]) v1 = vertices[b]; if (lines[a]->v2() == copy_verts[b]) v2 = vertices[b]; if (v1 && v2) break; } // Copy line MapLine* copy = new MapLine(v1, v2, s1, s2); copy->copy(lines[a]); this->lines.push_back(copy); } }
/* MapArchClipboardItem::pasteToMap * Pastes copied architecture to [map] at [position] *******************************************************************/ vector<MapVertex*> MapArchClipboardItem::pasteToMap(SLADEMap* map, fpoint2_t position) { std::map<MapVertex*, MapVertex*> vertMap; std::map<MapSector*, MapSector*> sectMap; std::map<MapSide*, MapSide*> sideMap; // Not used yet... // std::map<MapLine*, MapLine*> lineMap; // Add vertices vector<MapVertex*> new_verts; for (unsigned a = 0; a < vertices.size(); a++) { new_verts.push_back(map->createVertex(position.x + vertices[a]->xPos(), position.y + vertices[a]->yPos())); new_verts.back()->copy(vertices[a]); vertMap[vertices[a]] = new_verts.back(); } // Add sectors for (unsigned a = 0; a < sectors.size(); a++) { MapSector* new_sector = map->createSector(); new_sector->copy(sectors[a]); sectMap[sectors[a]] = new_sector; } // Add sides int first_new_side = map->nSides(); for (unsigned a = 0; a < sides.size(); a++) { // Get relative sector MapSector* sector = findInMap(sectMap, sides[a]->getSector()); MapSide* new_side = map->createSide(sector); new_side->copy(sides[a]); sideMap[sides[a]] = new_side; } // Add lines int first_new_line = map->nLines(); for (unsigned a = 0; a < lines.size(); a++) { // Get relative vertices MapVertex* v1 = findInMap(vertMap, lines[a]->v1()); MapVertex* v2 = findInMap(vertMap, lines[a]->v2()); if (!v1) { wxLogMessage("no v1"); continue; } if (!v2) { wxLogMessage("no v2"); } MapLine* newline = map->createLine(v1, v2, true); newline->copy(lines[a]); // lineMap[lines[a]] = newline; // Set relative sides bool s1 = false; bool s2 = !(lines[a]->s2()); MapSide* newS1 = findInMap(sideMap, lines[a]->s1()); MapSide* newS2 = findInMap(sideMap, lines[a]->s2()); if(newS1) newline->setS1(newS1); if(newS2) newline->setS2(newS2); } // TODO: // - Split lines // - Merge lines // Fix sector references // TODO: figure out what lines are 'outside' on copy, only fix said lines for (unsigned a = first_new_line; a < map->nLines(); a++) { MapLine* line = map->getLine(a); MapSector* sec1 = map->getLineSideSector(line, true); MapSector* sec2 = map->getLineSideSector(line, false); int i1 = -1; int i2 = -2; if (sec1) i1 = sec1->getIndex(); if (sec2) i2 = sec2->getIndex(); map->setLineSector(a, i1, true); map->setLineSector(a, i2, false); } return new_verts; }
/* InfoOverlay3D::update * Updates the info text for the object of [item_type] at [item_index] * in [map] *******************************************************************/ void InfoOverlay3D::update(int item_index, int item_type, SLADEMap* map) { // Clear current info info.clear(); info2.clear(); // Setup variables current_type = item_type; texname = ""; texture = NULL; thing_icon = false; int map_format = theMapEditor->currentMapDesc().format; // Wall if (item_type == MapEditor::SEL_SIDE_BOTTOM || item_type == MapEditor::SEL_SIDE_MIDDLE || item_type == MapEditor::SEL_SIDE_TOP) { // Get line and side MapSide* side = map->getSide(item_index); if (!side) return; MapLine* line = side->getParentLine(); if (!line) return; object = side; // --- Line/side info --- info.push_back(S_FMT("Line #%d", line->getIndex())); if (side == line->s1()) info.push_back(S_FMT("Front Side #%d", side->getIndex())); else info.push_back(S_FMT("Back Side #%d", side->getIndex())); // Relevant flags string flags = ""; if (theGameConfiguration->lineBasicFlagSet("dontpegtop", line, map_format)) flags += "Upper Unpegged, "; if (theGameConfiguration->lineBasicFlagSet("dontpegbottom", line, map_format)) flags += "Lower Unpegged, "; if (theGameConfiguration->lineBasicFlagSet("blocking", line, map_format)) flags += "Blocking, "; if (!flags.IsEmpty()) flags.RemoveLast(2); info.push_back(flags); info.push_back(S_FMT("Length: %d", (int)line->getLength())); // Other potential info: special, sector# // --- Wall part info --- // Part if (item_type == MapEditor::SEL_SIDE_BOTTOM) info2.push_back("Lower Texture"); else if (item_type == MapEditor::SEL_SIDE_MIDDLE) info2.push_back("Middle Texture"); else info2.push_back("Upper Texture"); // Offsets if (theGameConfiguration->udmfNamespace() == "zdoom") { // Get x offset info int xoff = side->intProperty("offsetx"); double xoff_part = 0; if (item_type == MapEditor::SEL_SIDE_BOTTOM) xoff_part = side->floatProperty("offsetx_bottom"); else if (item_type == MapEditor::SEL_SIDE_MIDDLE) xoff_part = side->floatProperty("offsetx_mid"); else xoff_part = side->floatProperty("offsetx_top"); // Add x offset string string xoff_info; if (xoff_part == 0) xoff_info = S_FMT("%d", xoff); else if (xoff_part > 0) xoff_info = S_FMT("%1.2f (%d+%1.2f)", (double)xoff+xoff_part, xoff, xoff_part); else xoff_info = S_FMT("%1.2f (%d-%1.2f)", (double)xoff+xoff_part, xoff, -xoff_part); // Get y offset info int yoff = side->intProperty("offsety"); double yoff_part = 0; if (item_type == MapEditor::SEL_SIDE_BOTTOM) yoff_part = side->floatProperty("offsety_bottom"); else if (item_type == MapEditor::SEL_SIDE_MIDDLE) yoff_part = side->floatProperty("offsety_mid"); else yoff_part = side->floatProperty("offsety_top"); // Add y offset string string yoff_info; if (yoff_part == 0) yoff_info = S_FMT("%d", yoff); else if (yoff_part > 0) yoff_info = S_FMT("%1.2f (%d+%1.2f)", (double)yoff+yoff_part, yoff, yoff_part); else yoff_info = S_FMT("%1.2f (%d-%1.2f)", (double)yoff+yoff_part, yoff, -yoff_part); info2.push_back(S_FMT("Offsets: %s, %s", xoff_info, yoff_info)); } else { // Basic offsets info2.push_back(S_FMT("Offsets: %d, %d", side->intProperty("offsetx"), side->intProperty("offsety"))); } // ZDoom UDMF extras if (theGameConfiguration->udmfNamespace() == "zdoom") { // Scale double xscale, yscale; if (item_type == MapEditor::SEL_SIDE_BOTTOM) { xscale = side->floatProperty("scalex_bottom"); yscale = side->floatProperty("scaley_bottom"); } else if (item_type == MapEditor::SEL_SIDE_MIDDLE) { xscale = side->floatProperty("scalex_mid"); yscale = side->floatProperty("scaley_mid"); } else { xscale = side->floatProperty("scalex_top"); yscale = side->floatProperty("scaley_top"); } info2.push_back(S_FMT("Scale: %1.2fx, %1.2fx", xscale, yscale)); } else { info2.push_back(""); } // Height of this section of the wall // TODO this is wrong in the case of slopes, but slope support only // exists in the 3.1.1 branch fpoint2_t left_point, right_point; MapSide* other_side; if (side == line->s1()) { left_point = line->v1()->getPoint(0); right_point = line->v2()->getPoint(0); other_side = line->s2(); } else { left_point = line->v2()->getPoint(0); right_point = line->v1()->getPoint(0); other_side = line->s1(); } MapSector* this_sector = side->getSector(); MapSector* other_sector = NULL; if (other_side) other_sector = other_side->getSector(); double left_height, right_height; if (item_type == MapEditor::SEL_SIDE_MIDDLE && other_sector) { // A two-sided line's middle area is the smallest distance between // both sides' floors and ceilings, which is more complicated with // slopes. plane_t floor1 = this_sector->getFloorPlane(); plane_t floor2 = other_sector->getFloorPlane(); plane_t ceiling1 = this_sector->getCeilingPlane(); plane_t ceiling2 = other_sector->getCeilingPlane(); left_height = min(ceiling1.height_at(left_point), ceiling2.height_at(left_point)) - max(floor1.height_at(left_point), floor2.height_at(left_point)); right_height = min(ceiling1.height_at(right_point), ceiling2.height_at(right_point)) - max(floor1.height_at(right_point), floor2.height_at(right_point)); } else { plane_t top_plane, bottom_plane; if (item_type == MapEditor::SEL_SIDE_MIDDLE) { top_plane = this_sector->getCeilingPlane(); bottom_plane = this_sector->getFloorPlane(); } else { if (!other_sector) return; if (item_type == MapEditor::SEL_SIDE_TOP) { top_plane = this_sector->getCeilingPlane(); bottom_plane = other_sector->getCeilingPlane(); } else { top_plane = other_sector->getFloorPlane(); bottom_plane = this_sector->getFloorPlane(); } } left_height = top_plane.height_at(left_point) - bottom_plane.height_at(left_point); right_height = top_plane.height_at(right_point) - bottom_plane.height_at(right_point); } if (fabs(left_height - right_height) < 0.001) info2.push_back(S_FMT("Height: %d", (int)left_height)); else info2.push_back(S_FMT("Height: %d ~ %d", (int)left_height, (int)right_height)); // Texture if (item_type == MapEditor::SEL_SIDE_BOTTOM) texname = side->getTexLower(); else if (item_type == MapEditor::SEL_SIDE_MIDDLE) texname = side->getTexMiddle(); else texname = side->getTexUpper(); texture = theMapEditor->textureManager().getTexture(texname, theGameConfiguration->mixTexFlats()); } // Floor else if (item_type == MapEditor::SEL_FLOOR || item_type == MapEditor::SEL_CEILING) { // Get sector MapSector* sector = map->getSector(item_index); if (!sector) return; object = sector; // Get basic info int fheight = sector->intProperty("heightfloor"); int cheight = sector->intProperty("heightceiling"); // --- Sector info --- // Sector index info.push_back(S_FMT("Sector #%d", item_index)); // Sector height info.push_back(S_FMT("Total Height: %d", cheight - fheight)); // ZDoom UDMF extras /* if (theGameConfiguration->udmfNamespace() == "zdoom") { // Sector colour rgba_t col = sector->getColour(0, true); info.push_back(S_FMT("Colour: R%d, G%d, B%d", col.r, col.g, col.b)); } */ // --- Flat info --- // Height if (item_type == MapEditor::SEL_FLOOR) info2.push_back(S_FMT("Floor Height: %d", fheight)); else info2.push_back(S_FMT("Ceiling Height: %d", cheight)); // Light int light = sector->intProperty("lightlevel"); if (theGameConfiguration->udmfNamespace() == "zdoom") { // Get extra light info int fl = 0; bool abs = false; if (item_type == MapEditor::SEL_FLOOR) { fl = sector->intProperty("lightfloor"); abs = sector->boolProperty("lightfloorabsolute"); } else { fl = sector->intProperty("lightceiling"); abs = sector->boolProperty("lightceilingabsolute"); } // Set if absolute if (abs) { light = fl; fl = 0; } // Add info string if (fl == 0) info2.push_back(S_FMT("Light: %d", light)); else if (fl > 0) info2.push_back(S_FMT("Light: %d (%d+%d)", light+fl, light, fl)); else info2.push_back(S_FMT("Light: %d (%d-%d)", light+fl, light, -fl)); } else info2.push_back(S_FMT("Light: %d", light)); // ZDoom UDMF extras if (theGameConfiguration->udmfNamespace() == "zdoom") { // Offsets double xoff, yoff; if (item_type == MapEditor::SEL_FLOOR) { xoff = sector->floatProperty("xpanningfloor"); yoff = sector->floatProperty("ypanningfloor"); } else { xoff = sector->floatProperty("xpanningceiling"); yoff = sector->floatProperty("ypanningceiling"); } info2.push_back(S_FMT("Offsets: %1.2f, %1.2f", xoff, yoff)); // Scaling double xscale, yscale; if (item_type == MapEditor::SEL_FLOOR) { xscale = sector->floatProperty("xscalefloor"); yscale = sector->floatProperty("yscalefloor"); } else { xscale = sector->floatProperty("xscaleceiling"); yscale = sector->floatProperty("yscaleceiling"); } info2.push_back(S_FMT("Scale: %1.2fx, %1.2fx", xscale, yscale)); } // Texture if (item_type == MapEditor::SEL_FLOOR) texname = sector->getFloorTex(); else texname = sector->getCeilingTex(); texture = theMapEditor->textureManager().getFlat(texname, theGameConfiguration->mixTexFlats()); } // Thing else if (item_type == MapEditor::SEL_THING) { // index, type, position, sector, zpos, height?, radius? // Get thing MapThing* thing = map->getThing(item_index); if (!thing) return; object = thing; // Index info.push_back(S_FMT("Thing #%d", item_index)); // Position if (theMapEditor->currentMapDesc().format == MAP_HEXEN || theMapEditor->currentMapDesc().format == MAP_UDMF) info.push_back(S_FMT("Position: %d, %d, %d", (int)thing->xPos(), (int)thing->yPos(), (int)thing->floatProperty("height"))); else info.push_back(S_FMT("Position: %d, %d", (int)thing->xPos(), (int)thing->yPos())); // Type ThingType* tt = theGameConfiguration->thingType(thing->getType()); if (tt->getName() == "Unknown") info2.push_back(S_FMT("Type: %d", thing->getType())); else info2.push_back(S_FMT("Type: %s", tt->getName())); // Args if (theMapEditor->currentMapDesc().format == MAP_HEXEN || (theMapEditor->currentMapDesc().format == MAP_UDMF && theGameConfiguration->getUDMFProperty("arg0", MOBJ_THING))) { // Get thing args int args[5]; args[0] = thing->intProperty("arg0"); args[1] = thing->intProperty("arg1"); args[2] = thing->intProperty("arg2"); args[3] = thing->intProperty("arg3"); args[4] = thing->intProperty("arg4"); string argstr = tt->getArgsString(args); if (argstr.IsEmpty()) info2.push_back("No Args"); else info2.push_back(argstr); } // Sector int sector = map->sectorAt(thing->point()); if (sector >= 0) info2.push_back(S_FMT("In Sector #%d", sector)); else info2.push_back("No Sector"); // Texture texture = theMapEditor->textureManager().getSprite(tt->getSprite(), tt->getTranslation(), tt->getPalette()); if (!texture) { if (use_zeth_icons && tt->getZeth() >= 0) texture = theMapEditor->textureManager().getEditorImage(S_FMT("zethicons/zeth%02d", tt->getZeth())); if (!texture) texture = theMapEditor->textureManager().getEditorImage(S_FMT("thing/%s", tt->getIcon())); thing_icon = true; } texname = ""; } last_update = theApp->runTimer(); }
/* MapSpecials::processEternitySlopes * Process Eternity slope specials *******************************************************************/ void MapSpecials::processEternitySlopes(SLADEMap* map) { // Eternity plans on having a few slope mechanisms, // which must be evaluated in a specific order. // - Plane_Align, in line order // - vertex triangle slopes, in sector order (wip) // - Plane_Copy, in line order // First things first: reset every sector to flat planes for(unsigned a = 0; a < map->nSectors(); a++) { MapSector* target = map->getSector(a); target->setPlane<FLOOR_PLANE>(plane_t::flat(target->getPlaneHeight<FLOOR_PLANE>())); target->setPlane<CEILING_PLANE>(plane_t::flat(target->getPlaneHeight<CEILING_PLANE>())); } // Plane_Align (line special 181) for(unsigned a = 0; a < map->nLines(); a++) { MapLine* line = map->getLine(a); if(line->getSpecial() != 181) continue; MapSector* sector1 = line->frontSector(); MapSector* sector2 = line->backSector(); if(!sector1 || !sector2) { LOG_MESSAGE(1, "Ignoring Plane_Align on one-sided line %d", line->getIndex()); continue; } if(sector1 == sector2) { LOG_MESSAGE(1, "Ignoring Plane_Align on line %d, which has the same sector on both sides", line->getIndex()); continue; } int floor_arg = line->intProperty("arg0"); if(floor_arg == 1) applyPlaneAlign<FLOOR_PLANE>(line, sector1, sector2); else if(floor_arg == 2) applyPlaneAlign<FLOOR_PLANE>(line, sector2, sector1); int ceiling_arg = line->intProperty("arg1"); if(ceiling_arg == 1) applyPlaneAlign<CEILING_PLANE>(line, sector1, sector2); else if(ceiling_arg == 2) applyPlaneAlign<CEILING_PLANE>(line, sector2, sector1); } // Plane_Copy vector<MapSector*> sectors; for(unsigned a = 0; a < map->nLines(); a++) { MapLine* line = map->getLine(a); if(line->getSpecial() != 118) continue; int tag; MapSector* front = line->frontSector(); MapSector* back = line->backSector(); if((tag = line->intProperty("arg0"))) { sectors.clear(); map->getSectorsByTag(tag, sectors); if(sectors.size()) front->setFloorPlane(sectors[0]->getFloorPlane()); } if((tag = line->intProperty("arg1"))) { sectors.clear(); map->getSectorsByTag(tag, sectors); if(sectors.size()) front->setCeilingPlane(sectors[0]->getCeilingPlane()); } if((tag = line->intProperty("arg2"))) { sectors.clear(); map->getSectorsByTag(tag, sectors); if(sectors.size()) back->setFloorPlane(sectors[0]->getFloorPlane()); } if((tag = line->intProperty("arg3"))) { sectors.clear(); map->getSectorsByTag(tag, sectors); if(sectors.size()) back->setCeilingPlane(sectors[0]->getCeilingPlane()); } // The fifth "share" argument copies from one side of the line to the // other if(front && back) { int share = line->intProperty("arg4"); if((share & 3) == 1) back->setFloorPlane(front->getFloorPlane()); else if((share & 3) == 2) front->setFloorPlane(back->getFloorPlane()); if((share & 12) == 4) back->setCeilingPlane(front->getCeilingPlane()); else if((share & 12) == 8) front->setCeilingPlane(back->getCeilingPlane()); } } }
/* MapSpecials::processZDoomSlopes * Process ZDoom slope specials *******************************************************************/ void MapSpecials::processZDoomSlopes(SLADEMap* map) { // ZDoom has a variety of slope mechanisms, which must be evaluated in a // specific order. // - Plane_Align, in line order // - line slope + sector tilt + vavoom, in thing order // - slope copy things, in thing order // - overwrite vertex heights with vertex height things // - vertex triangle slopes, in sector order // - Plane_Copy, in line order // First things first: reset every sector to flat planes for (unsigned a = 0; a < map->nSectors(); a++) { MapSector* target = map->getSector(a); target->setPlane<FLOOR_PLANE>(plane_t::flat(target->getPlaneHeight<FLOOR_PLANE>())); target->setPlane<CEILING_PLANE>(plane_t::flat(target->getPlaneHeight<CEILING_PLANE>())); } // Plane_Align (line special 181) for (unsigned a = 0; a < map->nLines(); a++) { MapLine* line = map->getLine(a); if (line->getSpecial() != 181) continue; MapSector* sector1 = line->frontSector(); MapSector* sector2 = line->backSector(); if (!sector1 || !sector2) { LOG_MESSAGE(1, "Ignoring Plane_Align on one-sided line %d", line->getIndex()); continue; } if (sector1 == sector2) { LOG_MESSAGE(1, "Ignoring Plane_Align on line %d, which has the same sector on both sides", line->getIndex()); continue; } int floor_arg = line->intProperty("arg0"); if (floor_arg == 1) applyPlaneAlign<FLOOR_PLANE>(line, sector1, sector2); else if (floor_arg == 2) applyPlaneAlign<FLOOR_PLANE>(line, sector2, sector1); int ceiling_arg = line->intProperty("arg1"); if (ceiling_arg == 1) applyPlaneAlign<CEILING_PLANE>(line, sector1, sector2); else if (ceiling_arg == 2) applyPlaneAlign<CEILING_PLANE>(line, sector2, sector1); } // Line slope things (9500/9501), sector tilt things (9502/9503), and // vavoom things (1500/1501), all in the same pass for (unsigned a = 0; a < map->nThings(); a++) { MapThing* thing = map->getThing(a); // Line slope things if (thing->getType() == 9500) applyLineSlopeThing<FLOOR_PLANE>(map, thing); else if (thing->getType() == 9501) applyLineSlopeThing<CEILING_PLANE>(map, thing); // Sector tilt things else if (thing->getType() == 9502) applySectorTiltThing<FLOOR_PLANE>(map, thing); else if (thing->getType() == 9503) applySectorTiltThing<CEILING_PLANE>(map, thing); // Vavoom things else if (thing->getType() == 1500) applyVavoomSlopeThing<FLOOR_PLANE>(map, thing); else if (thing->getType() == 1501) applyVavoomSlopeThing<CEILING_PLANE>(map, thing); } // Slope copy things (9510/9511) for (unsigned a = 0; a < map->nThings(); a++) { MapThing* thing = map->getThing(a); if (thing->getType() == 9510 || thing->getType() == 9511) { int target_idx = map->sectorAt(thing->point()); if (target_idx < 0) continue; MapSector* target = map->getSector(target_idx); // First argument is the tag of a sector whose slope should be copied int tag = thing->intProperty("arg0"); if (!tag) { LOG_MESSAGE(1, "Ignoring slope copy thing in sector %d with no argument", target_idx); continue; } vector<MapSector*> tagged_sectors; map->getSectorsByTag(tag, tagged_sectors); if (tagged_sectors.empty()) { LOG_MESSAGE(1, "Ignoring slope copy thing in sector %d; no sectors have target tag %d", target_idx, tag); continue; } if (thing->getType() == 9510) target->setFloorPlane(tagged_sectors[0]->getFloorPlane()); else target->setCeilingPlane(tagged_sectors[0]->getCeilingPlane()); } } // Vertex height things // These only affect the calculation of slopes and shouldn't be stored in // the map data proper, so instead of actually changing vertex properties, // we store them in a hashmap. VertexHeightMap vertex_floor_heights; VertexHeightMap vertex_ceiling_heights; for (unsigned a = 0; a < map->nThings(); a++) { MapThing* thing = map->getThing(a); if (thing->getType() == 1504 || thing->getType() == 1505) { // TODO there could be more than one vertex at this point MapVertex* vertex = map->vertexAt(thing->xPos(), thing->yPos()); if (vertex) { if (thing->getType() == 1504) vertex_floor_heights[vertex] = thing->floatProperty("height"); else if (thing->getType() == 1505) vertex_ceiling_heights[vertex] = thing->floatProperty("height"); } } } // Vertex heights -- only applies for sectors with exactly three vertices. // Heights may be set by UDMF properties, or by a vertex height thing // placed exactly on the vertex (which takes priority over the prop). vector<MapVertex*> vertices; for (unsigned a = 0; a < map->nSectors(); a++) { MapSector* target = map->getSector(a); vertices.clear(); target->getVertices(vertices); if (vertices.size() != 3) continue; applyVertexHeightSlope<FLOOR_PLANE>(target, vertices, vertex_floor_heights); applyVertexHeightSlope<CEILING_PLANE>(target, vertices, vertex_ceiling_heights); } // Plane_Copy vector<MapSector*> sectors; for (unsigned a = 0; a < map->nLines(); a++) { MapLine* line = map->getLine(a); if (line->getSpecial() != 118) continue; int tag; MapSector* front = line->frontSector(); MapSector* back = line->backSector(); if ((tag = line->intProperty("arg0")) && front) { sectors.clear(); map->getSectorsByTag(tag, sectors); if (sectors.size()) front->setFloorPlane(sectors[0]->getFloorPlane()); } if ((tag = line->intProperty("arg1")) && front) { sectors.clear(); map->getSectorsByTag(tag, sectors); if (sectors.size()) front->setCeilingPlane(sectors[0]->getCeilingPlane()); } if ((tag = line->intProperty("arg2")) && back) { sectors.clear(); map->getSectorsByTag(tag, sectors); if (sectors.size()) back->setFloorPlane(sectors[0]->getFloorPlane()); } if ((tag = line->intProperty("arg3")) && back) { sectors.clear(); map->getSectorsByTag(tag, sectors); if (sectors.size()) back->setCeilingPlane(sectors[0]->getCeilingPlane()); } // The fifth "share" argument copies from one side of the line to the // other if (front && back) { int share = line->intProperty("arg4"); if ((share & 3) == 1) back->setFloorPlane(front->getFloorPlane()); else if ((share & 3) == 2) front->setFloorPlane(back->getFloorPlane()); if ((share & 12) == 4) back->setCeilingPlane(front->getCeilingPlane()); else if ((share & 12) == 8) front->setCeilingPlane(back->getCeilingPlane()); } } }
void doCheck() { bool mixed = theGameConfiguration->mixTexFlats(); // Go through lines for (unsigned a = 0; a < map->nLines(); a++) { MapLine* line = map->getLine(a); // Check front side textures if (line->s1()) { // Get textures string upper = line->s1()->stringProperty("texturetop"); string middle = line->s1()->stringProperty("texturemiddle"); string lower = line->s1()->stringProperty("texturebottom"); // Upper if (upper != "-" && texman->getTexture(upper, mixed) == &(GLTexture::missingTex())) { lines.push_back(line); parts.push_back(TEX_FRONT_UPPER); } // Middle if (middle != "-" && texman->getTexture(middle, mixed) == &(GLTexture::missingTex())) { lines.push_back(line); parts.push_back(TEX_FRONT_MIDDLE); } // Lower if (lower != "-" && texman->getTexture(lower, mixed) == &(GLTexture::missingTex())) { lines.push_back(line); parts.push_back(TEX_FRONT_LOWER); } } // Check back side textures if (line->s2()) { // Get textures string upper = line->s2()->stringProperty("texturetop"); string middle = line->s2()->stringProperty("texturemiddle"); string lower = line->s2()->stringProperty("texturebottom"); // Upper if (upper != "-" && texman->getTexture(upper, mixed) == &(GLTexture::missingTex())) { lines.push_back(line); parts.push_back(TEX_BACK_UPPER); } // Middle if (middle != "-" && texman->getTexture(middle, mixed) == &(GLTexture::missingTex())) { lines.push_back(line); parts.push_back(TEX_BACK_MIDDLE); } // Lower if (lower != "-" && texman->getTexture(lower, mixed) == &(GLTexture::missingTex())) { lines.push_back(line); parts.push_back(TEX_BACK_LOWER); } } } }
void doCheck() { for (unsigned a = 0; a < map->nLines(); a++) { // Check what textures the line needs MapLine* line = map->getLine(a); MapSide* side1 = line->s1(); MapSide* side2 = line->s2(); int needs = line->needsTexture(); // Check for missing textures (front side) if (side1) { // Upper if (needs == TEX_FRONT_UPPER && side1->stringProperty("texturetop") == "-") { lines.push_back(line); parts.push_back(TEX_FRONT_UPPER); } // Middle if (needs == TEX_FRONT_MIDDLE && side1->stringProperty("texturemiddle") == "-") { lines.push_back(line); parts.push_back(TEX_FRONT_MIDDLE); } // Lower if (needs == TEX_FRONT_LOWER && side1->stringProperty("texturebottom") == "-") { lines.push_back(line); parts.push_back(TEX_FRONT_LOWER); } } // Check for missing textures (back side) if (side2) { // Upper if (needs == TEX_BACK_UPPER && side2->stringProperty("texturetop") == "-") { lines.push_back(line); parts.push_back(TEX_BACK_UPPER); } // Middle if (needs == TEX_BACK_MIDDLE && side2->stringProperty("texturemiddle") == "-") { lines.push_back(line); parts.push_back(TEX_BACK_MIDDLE); } // Lower if (needs == TEX_BACK_LOWER && side2->stringProperty("texturebottom") == "-") { lines.push_back(line); parts.push_back(TEX_BACK_LOWER); } } } }
FontMap::Entry::Entry (const MapLine &mapline, Subfont *sf) : fontname(mapline.fontfname()), encname(mapline.encname()), subfont(sf), fontindex(mapline.fontindex()), locked(false), style(mapline.bold(), mapline.extend(), mapline.slant()) { }
void PolygonSplitter::openSector(MapSector* sector) { // Check sector was given if (!sector) return; // Init clear(); // Get list of sides connected to this sector vector<MapSide*>& sides = sector->connectedSides(); // Go through sides MapLine* line; for (unsigned a = 0; a < sides.size(); a++) { line = sides[a]->getParentLine(); // Ignore this side if its parent line has the same sector on both sides if (!line || line->doubleSector()) continue; // Add the edge to the splitter (direction depends on what side of the line this is) if (line->s1() == sides[a]) addEdge(line->v1()->xPos(), line->v1()->yPos(), line->v2()->xPos(), line->v2()->yPos()); else addEdge(line->v2()->xPos(), line->v2()->yPos(), line->v1()->xPos(), line->v1()->yPos()); } }
/* MapCanvas::onKeyDown * Called when a key is pressed within the canvas *******************************************************************/ void MapCanvas::onKeyDown(wxKeyEvent& e) { // Send to editor context_->input().updateKeyModifiersWx(e.GetModifiers()); context_->input().keyDown(KeyBind::keyName(e.GetKeyCode())); // Testing if (Global::debug) { if (e.GetKeyCode() == WXK_F6) { Polygon2D poly; sf::Clock clock; LOG_MESSAGE(1, "Generating polygons..."); for (unsigned a = 0; a < context_->map().nSectors(); a++) { if (!poly.openSector(context_->map().getSector(a))) LOG_MESSAGE(1, "Splitting failed for sector %d", a); } //int ms = clock.GetElapsedTime() * 1000; //LOG_MESSAGE(1, "Polygon generation took %dms", ms); } if (e.GetKeyCode() == WXK_F7) { // Get nearest line int nearest = context_->map().nearestLine(context_->input().mousePosMap(), 999999); MapLine* line = context_->map().getLine(nearest); if (line) { SectorBuilder sbuilder; // Determine line side double side = MathStuff::lineSide(context_->input().mousePosMap(), line->seg()); if (side >= 0) sbuilder.traceSector(&(context_->map()), line, true); else sbuilder.traceSector(&(context_->map()), line, false); } } if (e.GetKeyCode() == WXK_F5) { // Get nearest line int nearest = context_->map().nearestLine(context_->input().mousePosMap(), 999999); MapLine* line = context_->map().getLine(nearest); // Get sectors MapSector* sec1 = context_->map().getLineSideSector(line, true); MapSector* sec2 = context_->map().getLineSideSector(line, false); int i1 = -1; int i2 = -1; if (sec1) i1 = sec1->getIndex(); if (sec2) i2 = sec2->getIndex(); context_->addEditorMessage(S_FMT("Front %d Back %d", i1, i2)); } if (e.GetKeyCode() == WXK_F5 && context_->editMode() == Mode::Sectors) { PolygonSplitter splitter; splitter.setVerbose(true); splitter.openSector(context_->selection().hilightedSector()); Polygon2D temp; splitter.doSplitting(&temp); } } // Update cursor in object edit mode //if (mouse_state == Input::MouseState::ObjectEdit) // determineObjectEditState(); #ifndef __WXMAC__ // Skipping events on OS X doesn't do anything but causes // sound alert (a.k.a. error beep) on every key press if (e.GetKeyCode() != WXK_UP && e.GetKeyCode() != WXK_DOWN && e.GetKeyCode() != WXK_LEFT && e.GetKeyCode() != WXK_RIGHT && e.GetKeyCode() != WXK_NUMPAD_UP && e.GetKeyCode() != WXK_NUMPAD_DOWN && e.GetKeyCode() != WXK_NUMPAD_LEFT && e.GetKeyCode() != WXK_NUMPAD_RIGHT) e.Skip(); #endif // !__WXMAC__ }