Dependencies: lib_2da, lib_anon, lib_array, lib_fn, lib_ini, lib_sfo, lib_struct, lib_sugar, lib_tools
Functions from unidentified sources: edit_area
Library for editing WED files, mostly for cropping and reflecting areas and copying polygons from one to another.
THIS IS NOT FULLY FUNCTIONAL atm - one function needs to be SFOv2-ified. & best to test the whole thing on WR/LI.
wed_add_door(name:s, tiles:s)=() patch
Add a door to the current wed file. Supply the name (to match the are file) and a string of k=>v pairs identifying the tile maps when the door is closed
wed_build(width:i, height:i, tis:s, wed:s)=() action
Build a new, blank WED file of the specified height and width.
wed_crop_area(x:i, xlen:i, y:i, ylen:i, area_old:s, area_new:s)=() action
Copy an area, cropping it to the desired size (in tiles).
After doing it, you might want to rebuild the TIS in NI, as it's quite inefficiently stored in this algorithm
*Mostly* polygons need to be ordered with the lowest point first (as per IESDP). But this is NOT always correct and I can't work out the true algorithm.
EE only
wed_delete_polygons(area:s, polygons:s)=() action
Delete wall polygons by number.
wed_duplicate_area(area_old:s, area_new:s)=() action
Make an identical but differently-named copy of an area
wed_flip_area(area:s, file_loc:s, script_array:s)=() action
Flip an area along its vertical axis
You need to supply (in 'file_loc') a properly-named TIS file and associated PVRZ files, plus (for legacy use) a legacy TIS file with a 'v' name suffix, plus the height,search and light maps
DO NOT do the inversion in Paint, as it apparently scrambles transparency layers. Photoshop works.
You can also pass the program an array of scripts (in the form $[script_array]("[script]")="") whose coordinates need to be inverted.
We are probably assuming ARE v1.0 in some places.
wed_get_polygons(xmin:i, xmax:i, ymin:i, ymax:i, wed:s, output_file:s)=() action
Extract all the polygons in a specified region of a wed file.
wed_remap_search_map_colors(map:s, reference:s, map_out:s)=() action
Take a 4-bit spat out by Photoshop and remap it to the standard 4-bit SR palette. (Use L^2 distance in color space if no exact match)
(I have tried to add 8bit=>4bit to this, but it's erratic and not a priority to fix)
wed_swap_searchmap_code(old:i, new:i, map:s)=() action
wed_swap_searchmap_code(old:i, new:i)=() patch
??map IWD to BG2 searchmap?? NB not dimorphic
wed_transcribe_polygons(delta_x:i, delta_y:i, st, retch_numerator:i=1, st, retch_denominator:i=1, st, retch_base_x:i, st, retch_base_y:i, from_area:s, to_area:s, polygons:s)=() action
Transcribe the wall polygons with the associated numbers from one wed file to another, optionally adjusting their vertices and bounding boxes by delta and stretching around a base point.
Polygons must be listed sequentially.
These should not be called from outside the library itself.
wed_append_to_vertices(x:i, y:i, vertices:s)=(vertices:s) patch
Add a vertex to a string of vertices
wed_check_for_multiples()=(value:s) patch
Check if a WED file uses the multiple-tile functionality (used for things like flapping flags in Candlekeep and some fireplaces; not currently supported in wed_crop_area)
wed_clip_polygon_block(x:i, xlen:i, y:i, ylen:i, polygons_in:s, vertices_in:s)=(polygons_out:s, vertices_out:s) patch
wed_remap_this_tile
wed_count_wallgroups()=(value:s) patch
Find how many wallgroups a wed should have
wed_crop_are_file(x:i, y:i, xlen:i, ylen:i, area:s)=() action
Crop an area file of all out-of-bounds elements, and readjust other elements to the right coordinates
Requires SFOv1 functionality
wed_crop_doors(x:i, y:i, xlen:i, ylen:i)=() patch
Remove doors that are out of bounds and move the polygons of the rest.
wed_crop_polygons(x:i, xlen:i, y:i, ylen:i)=() patch
Crop the wall polygons
wed_deduplicate_vertices(vertices:s)=(vertices:s) patch
Given a string of vertices, remove consecutive duplicates from the string
wed_delete_wallgroups()=() patch
Delete all wallgroups and associated polygons
Here we assume that (i) the door polygons strictly follow the wall polygons (ii) the door polygons are in order (so the index of the first door polygon indicates where the door polygons start) (iii) the vertex indexes used by the door polygons strictly follow those used by the wall polygons (iv) the vertex indexes used by the door polygons again appear in order
wed_find_crossing_points(x1:i, x2:i, y1:i, y2:i, xmin:i, xmax:i, ymin:i, ymax:i)=(xcross1:s, ycross1:s, xcross2:s, ycross2:s) patch
Function to find when there is a crossing point on a boundary between two points. If there are two, return the closest one to the first point
wed_find_quadrant(x:i, xmin:i, xmax:i, y:i, ymin:i, ymax:i)=(quadrant:s) patch
Find what quadrant a point is in.
wed_flip_bounding_box(width_total:i, offset:i, layout:i)=() patch
Take a bounding box and flip it L/R Multiple layouts. 1=LRTB, 2=LTRB
wed_flip_orientation(offset:i)=() patch
Take an orientation and flip it L-R
wed_flip_tile(height:i, width:i, tile_number:i)=(new_number:s) patch
Take a tile and flip its index L-R
wed_flip_vertex(width_total:i, offset:i, is_searchmap:i)=() patch
Take a vertex and flip it L/R
wed_get_vertex(ind:i, vertices:s)=(x:s, y:s) patch
Get a vertex from a point in a string of vertices
wed_insert_polygon_block(polygons:s, vertices:s)=() patch
Given a block of polygons and another of vertices, insert then into a file as its wall group (destructive overwrite)
wed_invert_vertex_sequence_order(initial_vertex_offset:i, initial_vertex_index:i, vertex_number:i, width_total:i, is_searchmap:i)=() patch
Take the sequence of vertices defining a polygon. Flip each element, then reverse the order, from 0 1 2 3 ... N to 0 N ... 3 2 1
wed_l_r_crossing_point(x1:i, x2:i, y1:i, y2:i, bdy:i)=(xcross:s, ycross:s) patch
Function to find when the line between two points crosses the vertical line x=bdy
wed_order_wall_vertices(vertices:s)=(vertices:s) patch
A wall polygon needs to start with the lowest (=largest y value) term. Enforce this.
If there are two points that share a lowest value, I think they need to be the sequential. Normally that will occur automatically, but we need to allow for the first and last being joint lowest
wed_realign_vertices(x:i, y:i)=() patch
Move the vertices in to the top left corner (x,y are in tiles)
wed_realign_wall_polygon_bbs(x:i, y:i)=() patch
Move the wall-polygon bounding boxes in to the top left corner (x,y are in tiles)
wed_rebuild_wall_groups()=() patch
Rebuild wall groups i.e. go through the wall polygons and build the wall groups from scratch
wed_rectangle_overlap(xmin_1:i, xmax_1:i, ymin_1:i, ymax_1:i, xmin_2:i, xmax_2:i, ymin_2:i, ymax_2:i)=(overlap:s) patch
Check if two rectangles overlap
wed_remap_this_tile(x:i, xlen:i, y:i, ylen:i, height:i, width:i, tile_number:i)=(value:s) patch
wed_remove_tiles(x:i, y:i, xlen:i, ylen:i)=() patch
Remove tiles from a WED, on the assumption we're cropping.
wed_search_map_truncate(x:i, y:i, xlen:i, ylen:i, type:s)=() patch
Truncate a bitmap (search map, etc)
These are uncompressed grids of data in 4-bit per-pixel encoding (see IESDP), or 8-bit for the light map. Note that the bitmaps are stretched vertically compared to the displayed areas. There are 4 pixels per tile horizontally, 4*4/3 pixels per tile vertically
data begins at 0x36 +4* num_colors goes from the bottom left corner, not top left as per BG conventions
A complication is that in the uncompressed BMP format, data rows must be multiples of 4 bytes. That means that for the 4-bits-per-pixel maps (HT and SR), if xlen is an odd number the rows need to be null-padded.
wed_stretch_point(x:i, y:i, x_0:i, y_0:i, numerator:i=1, denominator:i=1)=(x:s, y:s) patch
Stretch a point away from a reference point.
wed_truncate_bitmap_row(row_num:i, xmin:i, ymin:i, xmax:i, ymax:i, divisor:i, row:s)=(row_out:s, row_empty:s) patch
wed_truncate_polygon(xmax:i, ymax:i, xmin:i, ymin:i, bb_ymax:i, vertices:s)=(vertices:s, bb_xmin:s, bb_xmax:s, bb_ymin:s, bb_ymax:s) patch
Truncate a polygon onto a rectangle (and also return its bounding box)
This is a non-trivial mathematical problem. Naive solutions end up getting confused when corners get cut off, or when a line between two points crosses the boundary twice.
Here's my solution:
To complicate matters, polygons *mostly* need to be listed with the highest-y-value (i.e. lowest) point first. But this is not *always* true, and sometimes forcing the order breaks the polygon. What the actual rule is, I don't know, despite an afternoon's research.
So: the plan is, ain't broke don't fix. *If* the first polygon obeys the rule, *and* it gets truncated, then we'll reorder. Otherwise, not.
This generates duplicates, so deduplicate it at the end.
wed_truncate_wall(xmin:i, xmax:i, ymin:i, ymax:i, vertices:s)=(vertices:s, in_bounds:s) patch
Truncate the line defining a hovering wall baseline
wed_u_d_crossing_point(x1:i, x2:i, y1:i, y2:i, bdy:i)=(xcross:s, ycross:s) patch
Function to find when the line between two points crosses the horizontal line y=bdy
wed_update_bb(x:i, y:i, bb_xmin:i, bb_xmax:i, bb_ymin:i, bb_ymax:i)=(bb_xmin:s, bb_xmax:s, bb_ymin:s, bb_ymax:s) patch
update a bb so that it includes a point