2024-04-20 16:23:08 +00:00
/*
* Descent 3
* Copyright ( C ) 2024 Parallax Software
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2024-05-06 15:12:44 +00:00
- - - HISTORICAL COMMENTS FOLLOW - - -
2024-04-19 20:58:24 +00:00
* $ Logfile : / DescentIII / Main / editor / Group . cpp $
* $ Revision : 1.1 .1 .1 $
* $ Date : 2003 - 08 - 26 03 : 57 : 38 $
* $ Author : kevinb $
*
* Functions for copying , pasting , etc . of groups
*
* $ Log : not supported by cvs2svn $
*
* 35 9 / 15 / 99 1 : 56 p Matt
* Added the option to allow rooms or groups placed on the terrain to
* either align with the terrain or with gravity .
*
* 34 8 / 30 / 99 1 : 01 p Gwar
* use the " true " center of faces for determining place position of a
* group
*
* 33 8 / 17 / 99 6 : 12 p Gwar
* handle NEWEDITOR generic object management in AttachGroup
*
* 32 8 / 17 / 99 6 : 46 a Gwar
* added group support to NEWEDITOR
*
* 31 5 / 08 / 99 1 : 39 a Matt
* Added a function to delete all objects of a certain type , and support
* for placing and attaching groups to the terrain .
*
* 30 4 / 21 / 99 10 : 19 p Matt
* DeleteGroup ( ) wasn ' t setting the world changed flag
*
* 29 3 / 15 / 99 1 : 29 p Matt
* Added objects & triggers to save / load for groups .
*
* 28 1 / 29 / 99 12 : 48 p Matt
* Rewrote the doorway system
*
* 27 1 / 26 / 99 6 : 02 p Matt
* Fixed ( I hope ) a problem with pasted object having the wrong
* orientation .
*
* 26 1 / 21 / 99 11 : 15 p Jeff
* pulled out some structs and defines from header files and moved them
* into seperate header files so that multiplayer dlls don ' t require major
* game headers , just those new headers . Side effect is a shorter build
* time . Also cleaned up some header file # includes that weren ' t needed .
* This affected polymodel . h , object . h , player . h , vecmat . h , room . h ,
* manage . h and multi . h
*
* 25 1 / 21 / 99 11 : 34 a Matt
* Got rid of portal triggers . Since we don ' t have multi - face portals , a
* face trigger works fine for a portal . Also fixed a few editor / trigger
* bugs .
*
* 24 1 / 14 / 99 11 : 06 a Matt
* Added names to triggers
*
* 23 1 / 08 / 99 2 : 56 p Samir
* Ripped out OSIRIS1 .
*
* 22 12 / 01 / 98 11 : 17 p Matt
* Made copy / paste work with doors .
*
* 21 10 / 08 / 98 4 : 23 p Kevin
* Changed code to comply with memory library usage . Always use mem_malloc
* , mem_free and mem_strdup
*
* 20 9 / 24 / 98 5 : 00 p Matt
* Improved error checking for running out of rooms .
*
* 19 9 / 02 / 98 5 : 57 p Matt
* Added code to make faces match exactly when forming a portal . This
* should get rid of all remaining cracking problems .
*
* 18 9 / 01 / 98 12 : 04 p Matt
* Ripped out multi - face portal code
*
* 17 8 / 15 / 98 10 : 50 p Matt
* When copying a portal , copy its flags
*
* 16 6 / 23 / 98 2 : 43 p Matt
* Changed calls to OutrageMessageBox ( ) & Debug_MessageBox ( ) to deal with
* int return value ( instead of bool ) .
*
* 15 4 / 21 / 98 2 : 43 p Matt
* Added system to disable render updates while deleting large numbers of
* rooms , since the mine is not stable until this operation is done .
*
* 14 2 / 09 / 98 1 : 38 p Matt
* When pasting a group , ask the user if he also wants to paste objects
* and triggers
*
* 13 2 / 04 / 98 6 : 23 p Matt
* Changed object room number to indicate a terrain cell via a flag . Got
* rid of the object flag which used to indicate terrain .
*
* 12 1 / 29 / 98 5 : 50 p Matt
* Changed old camera object type to be viewer object ( for editor ) , and
* now camera objects will just be for game cameras .
*
* 11 1 / 20 / 98 4 : 12 p Samir
* New script housekeeping system .
*
* 10 1 / 15 / 98 7 : 34 p Matt
* Revamped error checking when computing face normals
*
* 9 12 / 23 / 97 11 : 06 a Samir
* Added pserror . h
*
* 8 10 / 13 / 97 6 : 20 p Matt
* Fixed stupid bug copying objects in group
*
* 7 10 / 03 / 97 3 : 37 p Matt
* Fixed bug copying multiple objects in a room , and changed code to not
* copy viewer or player objects
*
* 6 9 / 24 / 97 6 : 18 p Samir
* Use script names instead of script id values to identify scripts .
*
* 5 9 / 22 / 97 2 : 31 p Matt
* Made group code copy & paste objects & triggers
*
* 4 9 / 08 / 97 4 : 05 p Samir
* Fixed some warnings and took out extranneous includes to windows . h in
* important headers .
*
* 3 9 / 02 / 97 6 : 42 p Matt
* Got paste & group save / load working
*
* 2 8 / 29 / 97 5 : 45 p Matt
* Converted group code to work with rooms ( unfinished )
*
* 11 6 / 16 / 97 6 : 40 p Matt
* Fixed bug caused when Selected_segments [ ] was changed while
* DeleteGroup ( ) was indexing through it .
*
* 10 6 / 12 / 97 7 : 17 p Matt
* Deleted now - unused RotateWorld ( ) function
*
* 9 5 / 23 / 97 5 : 46 p Matt
* Added SetMatrixBasedOnSide ( ) functionality to ExtractMatrixFromSeg ( ) .
*
* 8 5 / 13 / 97 11 : 58 a Matt
* Made group file code save & load the level file version number to use
* when reading segments
*
* 7 5 / 08 / 97 5 : 07 p Matt
* When rotating world , rotate the editor ' s wireframe window view vars
*
* 6 5 / 08 / 97 4 : 43 p Matt
* Fixed bug when rotating objects in RotateWorld ( )
*
* 5 5 / 06 / 97 2 : 26 p Jason
* fixed bug in rotate world
*
* 4 5 / 05 / 97 3 : 57 p Matt
* Added code to rotate the mine ( & the objects in it ) to join with the
* terrain .
*
* 3 3 / 31 / 97 5 : 58 p Matt
* Revamped mine update flags
*
* 2 3 / 12 / 97 3 : 25 p Matt
* Added funcs for cut , copy , paste , & delete , and to save and load
* groups .
*
* 1 3 / 11 / 97 9 : 34 p Matt
*
* $ NoKeywords : $
*/
# include "group.h"
# ifndef NEWEDITOR
# include "d3edit.h"
# else
# include "globals.h"
# endif
# include "selectedroom.h"
# include "room.h"
# include "hroom.h"
# include "erooms.h"
# include "loadlevel.h"
# ifndef NEWEDITOR
# include "moveworld.h"
# endif
# include "object.h"
# include "trigger.h"
# include "pserror.h"
# include "doorway.h"
# include "mem.h"
# include <string.h>
# include "door.h"
//Free a group.
//Parameters: g - the group to be freed
void FreeGroup ( group * g )
{
for ( int r = 0 ; r < g - > nrooms ; r + + )
FreeRoom ( & g - > rooms [ r ] ) ;
mem_free ( g - > rooms ) ;
if ( g - > nobjects )
mem_free ( g - > objects ) ;
if ( g - > ntriggers )
mem_free ( g - > triggers ) ;
mem_free ( g ) ;
}
//Copy the given list of rooms to a group
//Parameters: nrooms - the number of rooms in list
// roomnums - pointer to list of room numbers
// attachroom, attachface - where group attaches when pasted
//Returns: pointer to group
group * CopyGroup ( int nrooms , int * roomnums , int attachroom , int attachface )
{
group * g ;
int r ;
int room_xlate [ MAX_ROOMS ] ;
int n_objects = 0 , n_triggers = 0 , n_doors = 0 ;
//Allocate group & room arrays
g = ( group * ) mem_malloc ( sizeof ( * g ) ) ; ASSERT ( g ! = NULL ) ;
g - > nrooms = nrooms ;
g - > rooms = ( room * ) mem_malloc ( sizeof ( * g - > rooms ) * nrooms ) ; ASSERT ( g - > rooms ! = NULL ) ;
//Initialize xlate list
for ( r = 0 ; r < MAX_ROOMS ; r + + )
room_xlate [ r ] = - 1 ;
//Copy the rooms & build xlate list. Count objects while we're at it
for ( r = 0 ; r < nrooms ; r + + ) {
CopyRoom ( & g - > rooms [ r ] , & Rooms [ roomnums [ r ] ] ) ;
room_xlate [ roomnums [ r ] ] = r ;
for ( int o = Rooms [ roomnums [ r ] ] . objects ; o ! = - 1 ; o = Objects [ o ] . next )
if ( ( Objects [ o ] . type ! = OBJ_VIEWER ) & & ( Objects [ o ] . type ! = OBJ_CAMERA ) & & ( Objects [ o ] . type ! = OBJ_PLAYER ) ) {
n_objects + + ;
if ( Objects [ o ] . type = = OBJ_DOOR )
n_doors + + ;
}
}
//Add portals for rooms in the group
for ( r = 0 ; r < nrooms ; r + + )
for ( int p = 0 ; p < Rooms [ roomnums [ r ] ] . num_portals ; p + + ) {
portal * pp = & Rooms [ roomnums [ r ] ] . portals [ p ] ;
if ( ( pp - > croom ! = - 1 ) & & IsRoomSelected ( pp - > croom ) ) {
//link the two rooms
if ( r < room_xlate [ pp - > croom ] ) //only link lower to higher, so we don't double link
LinkRoomsSimple ( g - > rooms , r , pp - > portal_face , room_xlate [ pp - > croom ] , Rooms [ pp - > croom ] . portals [ pp - > cportal ] . portal_face ) ;
//Copy the portal flags
int new_portal_num = g - > rooms [ r ] . faces [ pp - > portal_face ] . portal_num ;
g - > rooms [ r ] . portals [ new_portal_num ] . flags = pp - > flags ;
}
}
//Fill in attach fields
g - > attachroom = room_xlate [ attachroom ] ;
g - > attachface = attachface ;
//Copy objects
g - > nobjects = n_objects ;
g - > ndoors = n_doors ;
if ( n_objects = = 0 )
g - > objects = NULL ;
else {
int objnum = 0 ;
//Get memory for objects
g - > objects = ( object * ) mem_malloc ( sizeof ( * g - > objects ) * n_objects ) ;
//Go through list of rooms & copy the objects
for ( r = 0 ; r < nrooms ; r + + ) {
for ( int o = Rooms [ roomnums [ r ] ] . objects ; o ! = - 1 ; o = Objects [ o ] . next ) {
object * dest = & g - > objects [ objnum ] , * src = & Objects [ o ] ;
if ( ( src - > type = = OBJ_VIEWER ) | | ( src - > type = = OBJ_CAMERA ) | | ( src - > type = = OBJ_PLAYER ) )
continue ; //don't copy cameras
//Copy some selected data
dest - > type = src - > type ;
dest - > id = src - > id ;
dest - > flags = src - > flags ;
dest - > pos = src - > pos ;
dest - > orient = src - > orient ;
dest - > lifeleft = src - > lifeleft ;
dest - > contains_type = src - > contains_type ;
dest - > contains_id = src - > contains_id ;
dest - > contains_count = src - > contains_count ;
if ( src - > control_type = = CT_POWERUP )
dest - > ctype . powerup_info . count = src - > ctype . powerup_info . count ;
if ( src - > type = = OBJ_DOOR )
dest - > shields = src - > shields ;
dest - > roomnum = r ;
objnum + + ;
}
}
}
//Count the triggers in the group
for ( int t = 0 ; t < Num_triggers ; t + + )
if ( room_xlate [ Triggers [ t ] . roomnum ] ! = - 1 )
n_triggers + + ;
//Copy triggers
g - > ntriggers = n_triggers ;
if ( n_triggers = = 0 )
g - > triggers = NULL ;
else {
int trignum = 0 ;
//Get memory for triggers
g - > triggers = ( trigger * ) mem_malloc ( sizeof ( * g - > triggers ) * n_triggers ) ;
for ( t = 0 ; t < Num_triggers ; t + + )
if ( room_xlate [ Triggers [ t ] . roomnum ] ! = - 1 ) {
g - > triggers [ trignum ] = Triggers [ t ] ;
g - > triggers [ trignum ] . roomnum = room_xlate [ Triggers [ t ] . roomnum ] ;
trignum + + ;
}
}
//Return the new group
return g ;
}
extern bool Disable_editor_rendering ;
//Delete the given list of rooms
//Parameters: nrooms - the number of rooms in list
// roomnums - pointer to list of room numbers
void DeleteGroup ( int nrooms , int * roomnums )
{
int i ;
int * troomnums ;
bool save_disable_flag = Disable_editor_rendering ;
Disable_editor_rendering = 1 ;
//Make copy of list of rooms to delete. We must do this because roomnums may be a pointer
//to the global Selected_list, the contents of which may be changed by DeleteRoomFromMine()
troomnums = ( int * ) mem_malloc ( sizeof ( * troomnums ) * nrooms ) ;
for ( i = 0 ; i < nrooms ; i + + )
troomnums [ i ] = roomnums [ i ] ;
//Now delete the rooms
for ( i = 0 ; i < nrooms ; i + + )
DeleteRoomFromMine ( & Rooms [ troomnums [ i ] ] ) ;
//Free up our array
mem_free ( troomnums ) ;
//Regenerate external object rooms
CreateRoomObjects ( ) ;
//Restore old flag
Disable_editor_rendering = save_disable_flag ;
//We're done! Update flags and leave
World_changed = 1 ;
}
//Place the given group at the specified room face
//The function merely causes the group to be drawn in the editor, allowing the user to line it up
//before attaching it. The function AttachGroup() must be called to do the actual attachment.
//Parameters: destroomp, destside - where to place the group
// g - the group to place
void PlaceGroup ( room * destroomp , int destface , group * g )
{
//Clear the placed room, if one exists
Placed_room = - 1 ;
ASSERT ( destroomp - > faces [ destface ] . portal_num = = - 1 ) ;
//Set globals
Placed_group = g ;
Placed_room_orient . fvec = destroomp - > faces [ destface ] . normal ;
Placed_room_angle = 0 ;
Placed_baseroomp = destroomp ;
Placed_baseface = destface ;
//Compute attach points on each face
# ifndef NEWEDITOR
ComputeCenterPointOnFace ( & Placed_room_attachpoint , destroomp , destface ) ;
ComputeCenterPointOnFace ( & Placed_room_origin , & g - > rooms [ g - > attachroom ] , g - > attachface ) ;
# else
float ComputeFaceBoundingCircle ( vector * center , room * rp , int facenum ) ;
ComputeFaceBoundingCircle ( & Placed_room_attachpoint , destroomp , destface ) ;
ComputeFaceBoundingCircle ( & Placed_room_origin , & g - > rooms [ g - > attachroom ] , g - > attachface ) ;
# endif
//Compute initial orientation matrix
ComputePlacedRoomMatrix ( ) ;
//Set the flag
State_changed = 1 ;
}
# include "terrain.h"
//Place the given group at the specified terrain cell
//The function merely causes the group to be drawn in the editor, allowing the user to line it up
//before attaching it. The function AttachGroup() must be called to do the actual attachment.
//Parameters: cellnum - where to place the group
// g - the group to place
void PlaceGroupTerrain ( int cellnum , group * g , bool align_to_terrain )
{
//Clear the placed room, if one exists
Placed_room = - 1 ;
//Set globals
Placed_group = g ;
Placed_room_angle = 0 ;
Placed_baseroomp = NULL ;
if ( align_to_terrain )
Placed_room_orient . fvec = TerrainNormals [ MAX_TERRAIN_LOD - 1 ] [ cellnum ] . normal1 ;
else
Placed_room_orient . fvec = Identity_matrix . uvec ;
//Compute attach point on terrain
ComputeTerrainSegmentCenter ( & Placed_room_attachpoint , cellnum ) ;
//Compute attach points on face
ComputeCenterPointOnFace ( & Placed_room_origin , & g - > rooms [ g - > attachroom ] , g - > attachface ) ;
//Compute initial orientation matrix
ComputePlacedRoomMatrix ( ) ;
//Set the flag
State_changed = 1 ;
}
//Attach the already-placed group
void AttachGroup ( )
{
vector basecenter , attcenter ;
room * baseroomp , * attroomp ;
int baseface , attface ;
int room_xlate [ MAX_ROOMS ] ;
int r ;
ASSERT ( Placed_group ! = NULL ) ;
//Set some vars
baseroomp = Placed_baseroomp ;
baseface = Placed_baseface ;
attroomp = & Placed_group - > rooms [ Placed_group - > attachroom ] ;
attface = Placed_group - > attachface ;
attcenter = Placed_room_origin ;
basecenter = Placed_room_attachpoint ;
//Copy the rooms into the main room list, build the xlate table, and rotate the verts
for ( r = 0 ; r < Placed_group - > nrooms ; r + + ) {
int roomnum ;
room * newroomp ;
//Get a new room & copy into it
roomnum = GetFreeRoom ( 0 ) ;
if ( roomnum = = - 1 ) {
OutrageMessageBox ( " Cannot attach group: Not enough free rooms. " ) ;
for ( int t = 0 ; t < r ; t + + ) //delete the rooms we just created
DeleteRoomFromMine ( & Rooms [ room_xlate [ t ] ] ) ;
return ;
}
newroomp = & Rooms [ roomnum ] ;
CopyRoom ( newroomp , & Placed_group - > rooms [ r ] ) ;
room_xlate [ r ] = roomnum ;
//Rotate verts in the new rooms
for ( int i = 0 ; i < newroomp - > num_verts ; i + + )
newroomp - > verts [ i ] = ( ( newroomp - > verts [ i ] - attcenter ) * Placed_room_rotmat ) + basecenter ;
//Recompute normals for the faces
if ( ! ResetRoomFaceNormals ( newroomp ) )
Int3 ( ) ; //Get Matt
}
//Add portals for new rooms
for ( r = 0 ; r < Placed_group - > nrooms ; r + + )
for ( int p = 0 ; p < Placed_group - > rooms [ r ] . num_portals ; p + + ) {
portal * pp = & Placed_group - > rooms [ r ] . portals [ p ] ;
if ( r < pp - > croom ) //only link lower to higher, so we don't double link
LinkRoomsSimple ( Rooms , room_xlate [ r ] , pp - > portal_face , room_xlate [ pp - > croom ] , Placed_group - > rooms [ pp - > croom ] . portals [ pp - > cportal ] . portal_face ) ;
//Copy the portal flags
int new_portal_num = Rooms [ room_xlate [ r ] ] . faces [ pp - > portal_face ] . portal_num ;
Rooms [ room_xlate [ r ] ] . portals [ new_portal_num ] . flags = pp - > flags ;
}
//Get pointer to the placed attachroom
room * newroomp = & Rooms [ room_xlate [ Placed_group - > attachroom ] ] ;
if ( baseroomp ) {
//Clip the connecting faces against each other
if ( ! ClipFacePair ( newroomp , attface , baseroomp , baseface ) ) {
OutrageMessageBox ( " Error making portal -- faces probably don't overlap. " ) ;
for ( r = 0 ; r < Placed_group - > nrooms ; r + + )
FreeRoom ( & Rooms [ room_xlate [ r ] ] ) ;
return ;
}
//Make the two faces match exactly
MatchPortalFaces ( baseroomp , baseface , newroomp , attface ) ;
//Link the attach room to the base room
LinkRoomsSimple ( Rooms , ROOMNUM ( baseroomp ) , baseface , ROOMNUM ( newroomp ) , attface ) ;
}
//Paste objects
if ( Placed_group - > nobjects ) {
bool place_all = 0 ;
//Ask if the user wants the objects pasted
if ( Placed_group - > nobjects > Placed_group - > ndoors )
place_all = ( OutrageMessageBox ( MBOX_YESNO , " The group you are pasting has %d (non-door) object(s). Do you wish to paste it/them? " , Placed_group - > nobjects - Placed_group - > ndoors ) = = IDYES ) ;
//Add the objects (either just doors or all objects)
for ( int o = 0 ; o < Placed_group - > nobjects ; o + + ) {
object * src = & Placed_group - > objects [ o ] , * dest ;
if ( place_all | | ( src - > type = = OBJ_DOOR ) ) {
vector pos ;
matrix orient ;
int objnum ;
pos = ( ( src - > pos - attcenter ) * Placed_room_rotmat ) + basecenter ;
orient = ~ Placed_room_rotmat * src - > orient ;
objnum = ObjCreate ( src - > type , src - > id , room_xlate [ src - > roomnum ] , & pos , & orient ) ;
# ifdef NEWEDITOR
if ( IS_GENERIC ( src - > type ) )
LevelObjIncrementObject ( src - > id ) ;
# endif
dest = & Objects [ objnum ] ;
dest - > flags = src - > flags ;
dest - > lifeleft = src - > lifeleft ;
dest - > contains_type = src - > contains_type ;
dest - > contains_id = src - > contains_id ;
dest - > contains_count = src - > contains_count ;
if ( dest - > control_type = = CT_POWERUP )
dest - > ctype . powerup_info . count = src - > ctype . powerup_info . count ;
if ( src - > type = = OBJ_DOOR )
dest - > shields = src - > shields ;
}
}
}
//Paste triggers
if ( Placed_group - > ntriggers ) {
//Ask if the user wants the triggers
if ( OutrageMessageBox ( MBOX_YESNO , " The group you are pasting has %d trigger(s). Do you wish to paste it/them? " , Placed_group - > ntriggers ) = = IDYES ) {
//Add the triggers
for ( int t = 0 ; t < Placed_group - > ntriggers ; t + + ) {
trigger * tp = & Placed_group - > triggers [ t ] ;
int trignum ;
trignum = AddTrigger ( " " , room_xlate [ tp - > roomnum ] , tp - > facenum , tp - > activator , NULL ) ;
ASSERT ( trignum ! = - 1 ) ;
Triggers [ trignum ] . flags = tp - > flags ;
}
}
else { //Triggers not being pasted, so must delete any floating trigger faces
for ( r = 0 ; r < Placed_group - > nrooms ; r + + ) {
room * rp = & Rooms [ room_xlate [ r ] ] ;
for ( int f = 0 ; f < rp - > num_faces ; f + + ) {
if ( rp - > faces [ f ] . flags & FF_FLOATING_TRIG )
DeleteRoomFace ( rp , f ) ;
}
}
}
}
//Un-place the now-attached group
Placed_group = NULL ;
//We're done! Update flags and leave
World_changed = 1 ;
}
# define GROUP_FILE_TAG "D3GP"
# define GROUP_FILE_VERSION 3
//Version number changes:
//0 -> 1 Save the level file version to use when reading objects, rooms, etc.
//1 -> 2 Now works for room engine
//2 -> 3 Save objects, doors, & triggers
# define cf_ReadVector(f,v) do {(v)->x=cf_ReadFloat(f); (v)->y=cf_ReadFloat(f); (v)->z=cf_ReadFloat(f); } while (0)
# define cf_WriteVector(f,v) do {cf_WriteFloat((f),(v)->x); cf_WriteFloat((f),(v)->y); cf_WriteFloat((f),(v)->z); } while (0)
# define cf_WriteMatrix(f,m) do {cf_WriteVector((f),&(m)->rvec); cf_WriteVector((f),&(m)->uvec); cf_WriteVector((f),&(m)->fvec); } while (0)
# define cf_ReadMatrix(f,m) do {cf_ReadVector((f),&(m)->rvec); cf_ReadVector((f),&(m)->uvec); cf_ReadVector((f),&(m)->fvec); } while (0)
//Saves an object for a group
void WriteGroupObject ( CFILE * ofile , object * objp )
{
cf_WriteByte ( ofile , objp - > type ) ;
cf_WriteShort ( ofile , objp - > id ) ;
cf_WriteInt ( ofile , objp - > flags ) ;
if ( objp - > type = = OBJ_DOOR )
cf_WriteShort ( ofile , objp - > shields ) ;
cf_WriteInt ( ofile , objp - > roomnum ) ;
cf_WriteVector ( ofile , & objp - > pos ) ;
cf_WriteMatrix ( ofile , & objp - > orient ) ;
cf_WriteByte ( ofile , objp - > contains_type ) ;
cf_WriteByte ( ofile , objp - > contains_id ) ;
cf_WriteByte ( ofile , objp - > contains_count ) ;
cf_WriteFloat ( ofile , objp - > lifeleft ) ;
}
void WriteGroupTrigger ( CFILE * ofile , trigger * tp )
{
cf_WriteShort ( ofile , tp - > roomnum ) ;
cf_WriteShort ( ofile , tp - > facenum ) ;
cf_WriteShort ( ofile , tp - > flags ) ;
cf_WriteShort ( ofile , tp - > activator ) ;
}
//Saves a group to disk in the given filename
void SaveGroup ( char * filename , group * g )
{
CFILE * ifile ;
int i ;
ifile = cfopen ( filename , " wb " ) ;
if ( ! ifile )
return ;
//Write tag & version number
2024-05-24 03:07:26 +00:00
cf_WriteBytes ( ( uint8_t * ) GROUP_FILE_TAG , 4 , ifile ) ;
2024-04-19 20:58:24 +00:00
cf_WriteInt ( ifile , GROUP_FILE_VERSION ) ;
cf_WriteInt ( ifile , LEVEL_FILE_VERSION ) ;
//Write the current list of textures
WriteTextureList ( ifile ) ;
//Write group info
cf_WriteInt ( ifile , g - > nrooms ) ;
cf_WriteInt ( ifile , g - > attachroom ) ;
cf_WriteInt ( ifile , g - > attachface ) ;
cf_WriteInt ( ifile , g - > nobjects ) ;
cf_WriteInt ( ifile , g - > ndoors ) ;
cf_WriteInt ( ifile , g - > ntriggers ) ;
//Write rooms
for ( i = 0 ; i < g - > nrooms ; i + + )
WriteRoom ( ifile , & g - > rooms [ i ] ) ;
//Write objects
for ( i = 0 ; i < g - > nobjects ; i + + )
WriteGroupObject ( ifile , & g - > objects [ i ] ) ;
//Write triggers
for ( i = 0 ; i < g - > ntriggers ; i + + )
WriteGroupTrigger ( ifile , & g - > triggers [ i ] ) ;
//Close the file
cfclose ( ifile ) ;
}
//Reads an object for a group
void ReadGroupObject ( CFILE * ifile , object * objp )
{
objp - > type = cf_ReadByte ( ifile ) ;
objp - > id = cf_ReadShort ( ifile ) ;
objp - > flags = cf_ReadInt ( ifile ) ;
if ( objp - > type = = OBJ_DOOR )
objp - > shields = cf_ReadShort ( ifile ) ;
objp - > roomnum = cf_ReadInt ( ifile ) ;
cf_ReadVector ( ifile , & objp - > pos ) ;
cf_ReadMatrix ( ifile , & objp - > orient ) ;
objp - > contains_type = cf_ReadByte ( ifile ) ;
objp - > contains_id = cf_ReadByte ( ifile ) ;
objp - > contains_count = cf_ReadByte ( ifile ) ;
objp - > lifeleft = cf_ReadFloat ( ifile ) ;
}
void ReadGroupTrigger ( CFILE * ifile , trigger * tp )
{
tp - > roomnum = cf_ReadShort ( ifile ) ;
tp - > facenum = cf_ReadShort ( ifile ) ;
tp - > flags = cf_ReadShort ( ifile ) ;
tp - > activator = cf_ReadShort ( ifile ) ;
}
//Loads a group from disk
group * LoadGroup ( char * filename )
{
CFILE * ifile ;
char tag [ 4 ] ;
int version , level_version ;
group * g ;
int i ;
ifile = cfopen ( filename , " rb " ) ;
if ( ! ifile )
return NULL ;
//Read & check tag
2024-05-24 03:07:26 +00:00
cf_ReadBytes ( ( uint8_t * ) tag , 4 , ifile ) ;
2024-04-19 20:58:24 +00:00
if ( strncmp ( tag , GROUP_FILE_TAG , 4 ) ) {
cfclose ( ifile ) ;
return NULL ;
}
//Read & check version number
version = cf_ReadInt ( ifile ) ;
if ( version > GROUP_FILE_VERSION ) {
cfclose ( ifile ) ;
return NULL ;
}
//Check for out-of-date groups
if ( version < 2 ) {
OutrageMessageBox ( " Sorry - segment groups no longer supported. " ) ;
cfclose ( ifile ) ;
return NULL ;
}
//Read & check level file version number
level_version = cf_ReadInt ( ifile ) ;
if ( level_version > LEVEL_FILE_VERSION ) {
cfclose ( ifile ) ;
return NULL ;
}
//Read the textures & build xlate table
ReadTextureList ( ifile ) ;
//Allocate group
g = ( group * ) mem_malloc ( sizeof ( * g ) ) ;
//Read group info
g - > nrooms = cf_ReadInt ( ifile ) ;
g - > attachroom = cf_ReadInt ( ifile ) ;
g - > attachface = cf_ReadInt ( ifile ) ;
//Read object, door, & trigger info
if ( version > = 3 ) {
g - > nobjects = cf_ReadInt ( ifile ) ;
g - > ndoors = cf_ReadInt ( ifile ) ;
g - > ntriggers = cf_ReadInt ( ifile ) ;
}
else
g - > nobjects = g - > ndoors = g - > ntriggers = 0 ;
//Allocate room and vertex arrays
g - > rooms = ( room * ) mem_malloc ( sizeof ( * g - > rooms ) * g - > nrooms ) ;
//Read rooms
for ( i = 0 ; i < g - > nrooms ; i + + ) {
g - > rooms [ i ] . used = 0 ; //ReadRoom() wants the used flag to be clear
ReadRoom ( ifile , & g - > rooms [ i ] , level_version ) ;
}
//Read objects & triggers
g - > triggers = NULL ;
g - > objects = NULL ;
if ( version > = 3 ) {
//Allocate objects array
if ( g - > nobjects )
g - > objects = ( object * ) mem_malloc ( sizeof ( * g - > objects ) * g - > nobjects ) ;
//Allocate triggers array
if ( g - > ntriggers )
g - > triggers = ( trigger * ) mem_malloc ( sizeof ( * g - > triggers ) * g - > ntriggers ) ;
//Read objects
for ( i = 0 ; i < g - > nobjects ; i + + )
ReadGroupObject ( ifile , & g - > objects [ i ] ) ;
//Read triggers
for ( i = 0 ; i < g - > ntriggers ; i + + )
ReadGroupTrigger ( ifile , & g - > triggers [ i ] ) ;
}
//Close the file
cfclose ( ifile ) ;
return g ;
}