/* Edit3D::changeThingZ * Changes the Z height of selected 3d mode things by [amount] *******************************************************************/ void Edit3D::changeThingZ(int amount) const { // Ignore for doom format if (context_.map().currentFormat() == MAP_DOOM) return; // Go through 3d selection auto& selection_3d = context_.selection(); for (unsigned a = 0; a < selection_3d.size(); a++) { // Check if thing if (selection_3d[a].type == ItemType::Thing) { MapThing* thing = context_.map().getThing(selection_3d[a].index); if (thing) { // Change z height context_.recordPropertyChangeUndoStep(thing); double z = thing->intProperty("height"); z += amount; thing->setIntProperty("height", z); } } } }
/* 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()); } } }
/* 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(); }
/* Edit3D::changeHeight * Changes the height of objects, depending on type: * Things: Z height * Flat: height * Wall: vertical offset *******************************************************************/ void Edit3D::changeHeight(int amount) const { // Get items to process vector<MapEditor::Item> items; auto& selection_3d = context_.selection(); auto hilight_3d = context_.hilightItem(); auto& map = context_.map(); if (selection_3d.empty() && hilight_3d.index >= 0) { if (hilight_3d.type != ItemType::Thing || map.currentFormat() != MAP_DOOM) items.push_back(hilight_3d); } else for (unsigned a = 0; a < selection_3d.size(); a++) { if (selection_3d[a].type != ItemType::Thing || map.currentFormat() != MAP_DOOM) items.push_back(selection_3d[a]); } if (items.empty()) return; // Begin undo level context_.beginUndoRecordLocked("Change Height", true, false, false); // Go through items for (unsigned a = 0; a < items.size(); a++) { auto type = items[a].type; // Thing if (type == ItemType::Thing) { MapThing* thing = map.getThing(items[a].index); if (thing) { double z = thing->intProperty("height"); z += amount; thing->setIntProperty("height", z); } } // Wall if (type == ItemType::WallBottom || type == ItemType::WallMiddle || type == ItemType::WallTop) { auto side = map.getSide(items[a].index); if (side) { string ofs = "offsety"; // If offsets are linked, just change the whole side offset if (link_offset_) { int offset = side->intProperty(ofs); side->setIntProperty(ofs, offset + amount); continue; } // Unlinked offsets, build string (offsety_[top/mid/bottom]) else if (items[a].type == ItemType::WallBottom) ofs += "_bottom"; else if (items[a].type == ItemType::WallTop) ofs += "_top"; else ofs += "_mid"; // Change the offset float offset = side->floatProperty(ofs); side->setFloatProperty(ofs, offset + amount); } } // Floor else if (type == ItemType::Floor) { // Get sector auto sector = map.getSector(items[a].index); // Change height if (sector) sector->setFloorHeight(sector->getFloorHeight() + amount); } // Ceiling else if (type == ItemType::Ceiling) { // Get sector auto sector = map.getSector(items[a].index); // Change height if (sector) sector->setCeilingHeight(sector->getCeilingHeight() + amount); } } // End undo level context_.endUndoRecord(); // Editor message if (items.size() > 0) { if (amount > 0) context_.addEditorMessage(S_FMT("Height increased by %d", amount)); else context_.addEditorMessage(S_FMT("Height decreased by %d", -amount)); } }