Descent3/manage/generic.cpp
2024-09-24 23:00:53 +03:00

2355 lines
83 KiB
C++

/*
* 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/>.
--- HISTORICAL COMMENTS FOLLOW ---
* $Logfile: /DescentIII/Main/manage/generic.cpp $
* $Revision: 100 $
* $Date: 9/06/01 10:32a $
* $Author: Matt $
*
*
* $Log: /DescentIII/Main/manage/generic.cpp $
*
* 100 9/06/01 10:32a Matt
* Added code to fix problem poping add-on pages when the original pages
* were in the extra.gam file.
*
* 99 4/19/00 5:27p Matt
* From Duane for 1.4
* Mac file extension change
*
* 98 10/26/99 3:30p Jeff
* handle extra.gam addon tablefile
*
* 97 10/22/99 10:41p Jeff
* correctly merged and handle this mac code merge stuff
*
* 96 10/22/99 6:04p Jeff
* fixed bugs and compiler errors resulting from mac code merge
*
* 95 10/22/99 2:56p Kevin
* Mac merge w/memory savings
*
* 94 9/18/99 8:49p Jeff
* fixed bug with addon pages that have dependencies on other pages in the
* addon tablefile
*
* 93 8/11/99 5:32p Jeff
* changes to fix addon tablefile support so it works correctly
*
* 92 4/25/99 10:19p Matt
* Fixed multiplayer and demo problems will killing an object from script,
* and cleaned up the death code a bit in the process.
*
* 91 4/22/99 7:08p Matt
* Reduced the number of object sounds from 3 to 2, since we were only
* using two.
*
* 90 4/19/99 3:57p Matt
* Took out guidebot velocity hack.
*
* 89 4/18/99 4:49p Matt
* Took out code that hacked in ammo amounts for weapon powerups, and
* added code to set the counts on the page and read and write to the
* table file.
*
* 88 4/15/99 5:21p Jason
* sped up table file loading
*
* 87 4/14/99 1:33a Jeff
* fixed case mismatched #includes
*
* 86 4/10/99 5:56p Matt
* Cleaned up object initialization code, including create object & load
* object.
*
* 85 4/06/99 6:02p Matt
* Added score system
*
* 84 3/29/99 11:20a Matt
* Added more flexibility to death delays, and added fade away deaths.
*
* 83 3/27/99 2:15p Jason
* took out bashing of hangturret
*
* 82 3/09/99 1:44p Kevin
* Bashed the aiming point value for the hangturret.
*
* 81 3/04/99 4:47p Jason
* temp fix (ie BAD HACK) for OEM table file woes
*
* 80 2/28/99 6:20p Matt
* Added the ability to set death types for generic objects.
*
* 79 2/23/99 12:39p Jason
* added more options for generic objects
*
* 78 2/23/99 11:02a Matt
* Changed generic object spew info to use -1 for none
*
* 77 2/23/99 10:19a Matt
* Temporary fix
*
* 76 2/16/99 12:38a Matt
* Took out gunboy sound hack
*
* 75 2/02/99 8:44a Chris
* I made buildings with AI work correctly (ie really big robots should be
* buildings)
* anim to and from states are now shorts instead of bytes
*
* 74 1/21/99 11:16p Jeff
* pulled out some structs and defines from header files and moved them
* into separate 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
*
* 73 1/13/99 7:04a Jeff
* put #ifdef around #include <windows.h>
*
* 72 12/30/98 5:17p Jeff
* changes made to handle script name override, to override the name of
* the script to use in a module.
*
* 71 12/29/98 4:30p Jason
* added add-on data functionality
*
* 70 12/17/98 7:26p Jeff
* new script system data
*
* 69 12/01/98 4:31p Chris
*
* 68 12/01/98 4:31p Chris
* Checked in a massive amount of AI work. Improved flocking code. :)
* (Still hacked lightly). In addition, I am moving toward using the
* composite dir. :)
*
* 67 11/19/98 8:36p Chris
* Tweaked
*
* 66 11/19/98 8:26p Chris
* Starting to add generic team avoidance code
*
* 65 11/13/98 12:30p Jason
* changes for weapons
*
* 64 11/06/98 12:35p Jason
* more speedups for manage system
*
* 63 11/05/98 7:54p Jason
* changes for new manage system
*
* 62 11/02/98 6:02p Jason
* made yes network updates much faster
*
* 61 10/23/98 6:43p Matt
* Kill the gunboy's sounds (for the demo).
*
* 60 10/12/98 11:38p Jeff
* wrapped all the Object_info[].description whenever freed...trying to
* find an obscure bug. Added icon_name to manage page of Generic
*
* 59 10/09/98 2:27p Jason
* reorganized table file system
*
* 58 10/08/98 10:03p Jason
* more filtered table file stuff
*
* 57 9/22/98 2:29p Kevin
* moved ships allowed code out of dll and into main app. Also added
* powerup exclusions
*
* 56 8/26/98 8:07p Jason
* added directional lights to objects
*
* 55 8/25/98 3:42p Jason
* fixed generic object problems
*
* 54 8/24/98 2:37p Jason
* made table file more efficient with regards to invalid names
*
* 53 8/19/98 6:29p Chris
*
* 52 8/16/98 6:17p Chris
* Added generic object spewing code
*
* 51 8/15/98 6:12p Chris
* Added 13 new AI fields
*
* 50 8/14/98 4:56p Jeff
* added quad fire mask for weapon battery
*
* 49 6/12/98 1:06p Jason
* added smart loading from local table file
*
* 48 6/04/98 6:44p Jason
* implemented smart FindGenericPage function
*
* 47 6/01/98 10:37a Matt
* Added system to spew powerups when the player dies. Still needs Jason
* to deal with mng_FindSpecificGenericPage().
*
* 46 5/18/98 8:06p Chris
* Removed an unused AI value
*
* 45 5/07/98 1:41p Chris
* Added hit_death_dot
*
* 44 5/03/98 5:38p Chris
* Added sounds to anim page
*
* 43 4/30/98 12:22p Jason
* did some lo-res model optimizations
*
* 42 4/03/98 10:07a Chris
* Added support for objects getting their size computed when the
* polymodel is paged in the first time as an object
*
* 41 4/02/98 3:54p Jason
* first pass in getting polymodel paging to work
*
* 40 3/31/98 3:49p Jason
* added memory lib
*
* 39 3/23/98 10:03a Chris
* Added independent wb animations
*
* 38 3/13/98 5:55p Chris
* Added the new collision spheres
*
* 37 3/11/98 4:59p Chris
* Changed the ComputeDefualtSize function call
*
* 36 3/10/98 6:54p Chris
* All start and end frame for ComputeDefaultSize
*
* 35 3/02/98 6:56p Chris
* Changed melee_dist to melee_damage
*
* 34 3/02/98 6:43p Chris
*
* 33 3/02/98 4:16p Chris
* Added 14 new fields to the AI Settings Dialog/page.
*
* 32 2/23/98 1:22p Jason
* made FindSpecific* read from the local drive, not the net drive - when
* starting the editor
*
* 31 2/06/98 2:15a Chris
* Activated the max_velocity, max_delta_velocity, and max_turn_rate
* fields for AI objects.
*
* 30 2/04/98 11:47a Jason
* added dynamic description field to generic pages
*
* 29 1/29/98 3:29p Chris
* Major update to the AI system.
*
* 28 1/23/98 6:25p Jason
* Got spray weapons working
*
* 27 1/15/98 6:22p Jason
* added safety checks so the network won't copy over a primitive you have
* held locally
*
* 26 1/05/98 3:55p Chris
* Added explosion and ambient sounds
*
* 25 12/22/97 6:19p Chris
* Moved weapon battery firing sound off the projectile (weapon) and into
* the weapon battery.
*
* 24 12/01/97 9:54a Chris
* Added support for concussive forces, generalized robot collisions to
* generic collisions
*
* 23 11/25/97 1:16p Jason
* added lod system for certain objects
*
* 22 11/17/97 10:47p Jason
* fixed loading/saving of flicker_distance
*
* 21 11/05/97 12:22p Chris
* Fixed size of wb_info in the page file
*
* 20 11/06/97 11:09a Jason
* added extra assert to catch errors
*
* 19 10/28/97 12:23p Chris
* We now save out more AI info
*
* 18 10/01/97 11:01a Chris
* Added new support for additional physics items
*
* 17 9/24/97 6:18p Samir
* Use script names instead of script id values to identify scripts.
*
* 16 9/17/97 10:59a Chris
* Added a new way to compute radi
*
* 15 9/10/97 5:26p Jason
* fixed stupid off by one bug
*
* 14 9/10/97 5:17p Jason
* more lighting improvements
*
* 13 9/10/97 11:44a Chris
* Added support for weapon batteries
*
* 12 9/08/97 11:51a Chris
* Extended the weapon system
*
* 11 9/05/97 1:29p Jason
* revamped generic object lighting
*
* 10 9/04/97 5:49p Jason
* made generic pages read/write pulse time for lighting
*
* 9 9/04/97 3:21p Jason
* added pulse timing for lit objects
*
* 8 9/03/97 6:18p Jason
* added code to support powerups/robots/etc that cast light
*
* 7 9/03/97 2:12p Chris
* Added new weapon battery system and made the animation system usable.
*
* 6 8/13/97 4:54p Matt
* Added destroyable flag & hitpoints
*
* 5 8/11/97 6:47p Matt
* Fixed read/write size mismatches in generic page
*
* 4 8/11/97 1:53p Matt
* Ripped out robot & powerup pages, and added generic page
*
* 3 8/08/97 5:25p Matt
* Type was writing as byte but reading as int
*
* 2 8/08/97 3:44p Jason
* added code to support new generic page
*
* 1 8/08/97 1:29p Jason
* file for object_info pages
*
* $NoKeywords: $
*/
#include <cstring>
#include <filesystem>
#include "cfile.h"
#include "manage.h"
#include "genericpage.h"
#include "soundpage.h"
#include "weaponpage.h"
#include "log.h"
#include "pserror.h"
#include "polymodel.h"
#include "weapon.h"
#include "sounds.h"
#include "mem.h"
#include "args.h"
#define GENERICFILE_VERSION 27
// genericpage commands that are read/written
// A command is followed by a byte count describing how many bytes
// are in the data for the command
#define GENERICPAGE_COMMAND_NAME 1
#define GENERICPAGE_COMMAND_END 2
#define GENERICPAGE_COMMAND_IMAGE_NAME 3
#define GENERICPAGE_COMMAND_VERSION 4
#define GENERICPAGE_COMMAND_PHYS_MASS 5
#define GENERICPAGE_COMMAND_PHYS_DRAG 6
#define GENERICPAGE_COMMAND_SIZE 7
#define GENERICPAGE_COMMAND_ANIM_STATES 8
#define GENERICPAGE_COMMAND_FLAGS 9
#define GENERICPAGE_COMMAND_PHYS_FLAGS 10
// AI info
#define GENERICPAGE_COMMAND_AI_INFO 11
#define GENERICPAGE_COMMAND_TYPE 12
#define GENERICPAGE_COMMAND_SCORE 13
#define GENERICPAGE_COMMAND_SCRIPT 14
#define GENERICPAGE_COMMAND_SOUND_NAME 15
#define GENERICPAGE_COMMAND_HITPOINTS 16
#define GENERICPAGE_COMMAND_LIGHT_CAST 17
#define GENERICPAGE_COMMAND_WB_INFO 18
#define GENERICPAGE_COMMAND_WB_WEAPON 19
#define GENERICPAGE_COMMAND_SCRIPTNAME 20
#define GENERICPAGE_COMMAND_ROT_DRAG 21
#define GENERICPAGE_COMMAND_FULL_THRUST 22
#define GENERICPAGE_COMMAND_FULL_ROTTHRUST 23
#define GENERICPAGE_COMMAND_TURNROLL_RATE 24
#define GENERICPAGE_COMMAND_TURNROLL_RATIO 25
#define GENERICPAGE_COMMAND_WIGGLE_AMP 26
#define GENERICPAGE_COMMAND_WIGGLE_FREQ 27
#define GENERICPAGE_COMMAND_INT_VELOCITY 28
#define GENERICPAGE_COMMAND_INT_ROTVEL 29
#define GENERICPAGE_COMMAND_NUM_BOUNCES 30
#define GENERICPAGE_COMMAND_COEFF_REST 31
#define GENERICPAGE_COMMAND_FLICKER_DISTANCE 32
#define GENERICPAGE_COMMAND_LOD_MODELS 33
// Impact stuff
#define GENERICPAGE_COMMAND_IMPACT 34
#define GENERICPAGE_COMMAND_WB_FIRE_SOUND 35
#define GENERICPAGE_COMMAND_WB_FLAGS 36
#define GENERICPAGE_COMMAND_DESCRIPTION 37
#define GENERICPAGE_COMMAND_AI_SOUND_NAME 38
#define GENERICPAGE_COMMAND_LOD_DISTANCE 39
#define GENERICPAGE_COMMAND_ANIM_SOUND_NAME 40
#define GENERICPAGE_COMMAND_HIT_DIE_DOT 41
#define GENERICPAGE_COMMAND_WB_QUADMASK 42
#define GENERICPAGE_COMMAND_AI_INFO2 43
#define GENERICPAGE_COMMAND_DSPEW_INFO 44
#define GENERICPAGE_COMMAND_DIRECTION_DOT 45
#define GENERICPAGE_COMMAND_ICON_NAME 46
void mng_WriteLightingChunk(light_info *lighting_info, CFILE *outfile);
void mng_ReadLightingChunk(light_info *lighting_info, CFILE *infile);
extern bool Running_editor; // in init.cpp
extern char *TablefileNameOverride;
// Sets a page structure to default values
void mng_InitGenericPage(mngs_generic_page *genericpage) {
int i;
memset(genericpage, 0, sizeof(mngs_generic_page));
strcpy(genericpage->image_name, "");
strcpy(genericpage->med_image_name, "");
strcpy(genericpage->lo_image_name, "");
for (i = 0; i < MAX_OBJ_SOUNDS; i++)
strcpy(genericpage->sound_name[i], "");
for (i = 0; i < MAX_AI_SOUNDS; i++)
strcpy(genericpage->ai_sound_name[i], "");
for (i = 0; i < MAX_DSPEW_TYPES; i++) {
strcpy(genericpage->dspew_name[i], "\0");
}
genericpage->objinfo_struct.description = NULL;
genericpage->objinfo_struct.icon_name[0] = '\0';
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++)
for (int j = 0; j < NUM_ANIMS_PER_CLASS; j++)
strcpy(genericpage->anim_sound_name[i][j], "");
genericpage->objinfo_struct.med_lod_distance = DEFAULT_MED_LOD_DISTANCE;
genericpage->objinfo_struct.lo_lod_distance = DEFAULT_LO_LOD_DISTANCE;
genericpage->objinfo_struct.phys_info.hit_die_dot = 1.0f;
genericpage->objinfo_struct.respawn_scalar = 1.0f;
genericpage->ai_info.curiousity = .5f;
genericpage->ai_info.night_vision = .7f;
genericpage->ai_info.fog_vision = .7f;
genericpage->ai_info.lead_accuracy = 1.0f;
genericpage->ai_info.lead_varience = 0.0f;
genericpage->ai_info.fire_spread = 0.0f;
genericpage->ai_info.fight_team = 0.15f;
genericpage->ai_info.fight_same = 0.8f;
genericpage->ai_info.aggression = 0.5f;
genericpage->ai_info.hearing = 1.0f;
genericpage->ai_info.frustration = 0.5f;
genericpage->ai_info.roaming = 0.5f;
genericpage->ai_info.life_preservation = 0.0f;
genericpage->objinfo_struct.module_name[0] = '\0';
for (i = 0; i < MAX_DEATH_TYPES; i++) {
genericpage->objinfo_struct.death_types[i].flags = 0;
genericpage->objinfo_struct.death_types[i].delay_min = 0.0;
genericpage->objinfo_struct.death_types[i].delay_max = 0.0;
genericpage->objinfo_struct.death_probabilities[i] = 0;
}
}
// Given an open file pointer and a generic_page struct, writes that generic page out
void mng_WriteGenericPage(CFILE *outfile, mngs_generic_page *genericpage) {
int i, size;
ASSERT(outfile != NULL);
ASSERT(genericpage != NULL);
ASSERT((strlen(genericpage->image_name)) > 2);
cf_WriteByte(outfile, PAGETYPE_GENERIC);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_VERSION);
cf_WriteByte(outfile, 1);
cf_WriteByte(outfile, GENERICFILE_VERSION);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_TYPE);
cf_WriteByte(outfile, 1);
cf_WriteByte(outfile, genericpage->objinfo_struct.type);
// Write the name
cf_WriteByte(outfile, GENERICPAGE_COMMAND_NAME);
cf_WriteByte(outfile, strlen(genericpage->objinfo_struct.name) + 1);
cf_WriteString(outfile, genericpage->objinfo_struct.name);
// Write the model name
cf_WriteByte(outfile, GENERICPAGE_COMMAND_IMAGE_NAME);
cf_WriteByte(outfile, strlen(genericpage->image_name) + 1);
cf_WriteString(outfile, genericpage->image_name);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_IMPACT);
cf_WriteByte(outfile, 3 * sizeof(float));
cf_WriteFloat(outfile, genericpage->objinfo_struct.impact_size);
cf_WriteFloat(outfile, genericpage->objinfo_struct.impact_time);
cf_WriteFloat(outfile, genericpage->objinfo_struct.damage);
// Write the LOD model names
cf_WriteByte(outfile, GENERICPAGE_COMMAND_LOD_MODELS);
size = strlen(genericpage->med_image_name) + 1;
size += strlen(genericpage->lo_image_name) + 1;
cf_WriteByte(outfile, size);
cf_WriteString(outfile, genericpage->med_image_name);
cf_WriteString(outfile, genericpage->lo_image_name);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_SCORE);
cf_WriteByte(outfile, 1);
cf_WriteByte(outfile, genericpage->objinfo_struct.score);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_SCRIPTNAME);
cf_WriteByte(outfile, strlen("") + 1); // genericpage->objinfo_struct.script_name
cf_WriteString(outfile, "");
if (genericpage->objinfo_struct.description != NULL) {
cf_WriteByte(outfile, GENERICPAGE_COMMAND_DESCRIPTION);
cf_WriteByte(outfile, strlen(genericpage->objinfo_struct.description) + 1);
cf_WriteString(outfile, genericpage->objinfo_struct.description);
}
if (genericpage->objinfo_struct.icon_name[0] != '\0') {
cf_WriteByte(outfile, GENERICPAGE_COMMAND_ICON_NAME);
cf_WriteByte(outfile, strlen(genericpage->objinfo_struct.icon_name) + 1);
cf_WriteString(outfile, genericpage->objinfo_struct.icon_name);
}
cf_WriteByte(outfile, GENERICPAGE_COMMAND_LOD_DISTANCE);
cf_WriteByte(outfile, 8);
cf_WriteFloat(outfile, genericpage->objinfo_struct.med_lod_distance);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lo_lod_distance);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_PHYS_MASS);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.mass);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_PHYS_DRAG);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.drag);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_SIZE);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.size);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_LIGHT_CAST);
cf_WriteByte(outfile, (10 * 4) + 2);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lighting_info.light_distance);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lighting_info.red_light1);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lighting_info.green_light1);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lighting_info.blue_light1);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lighting_info.time_interval);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lighting_info.red_light2);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lighting_info.green_light2);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lighting_info.blue_light2);
cf_WriteInt(outfile, genericpage->objinfo_struct.lighting_info.flags);
cf_WriteInt(outfile, genericpage->objinfo_struct.lighting_info.timebits);
cf_WriteByte(outfile, genericpage->objinfo_struct.lighting_info.angle);
cf_WriteByte(outfile, genericpage->objinfo_struct.lighting_info.lighting_render_type);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_FLICKER_DISTANCE);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lighting_info.flicker_distance);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_DIRECTION_DOT);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lighting_info.directional_dot);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_HITPOINTS);
cf_WriteByte(outfile, 4);
cf_WriteInt(outfile, genericpage->objinfo_struct.hit_points);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_FLAGS);
cf_WriteByte(outfile, 4);
cf_WriteInt(outfile, genericpage->objinfo_struct.flags);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_PHYS_FLAGS);
cf_WriteByte(outfile, 4);
cf_WriteInt(outfile, genericpage->objinfo_struct.phys_info.flags);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_ROT_DRAG);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.rotdrag);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_FULL_THRUST);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.full_thrust);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_FULL_ROTTHRUST);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.full_rotthrust);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_TURNROLL_RATE);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.max_turnroll_rate);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_TURNROLL_RATIO);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.turnroll_ratio);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_WIGGLE_AMP);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.wiggle_amplitude);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_WIGGLE_FREQ);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.wiggles_per_sec);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_INT_VELOCITY);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.velocity.z);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_INT_ROTVEL);
cf_WriteByte(outfile, 12);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.rotvel.x);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.rotvel.y);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.rotvel.z);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_NUM_BOUNCES);
cf_WriteByte(outfile, 4);
cf_WriteInt(outfile, genericpage->objinfo_struct.phys_info.num_bounces);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_COEFF_REST);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.coeff_restitution);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_HIT_DIE_DOT);
cf_WriteByte(outfile, 4);
cf_WriteFloat(outfile, genericpage->objinfo_struct.phys_info.hit_die_dot);
cf_WriteByte(outfile, GENERICPAGE_COMMAND_AI_INFO);
cf_WriteByte(outfile, 76);
cf_WriteInt(outfile, genericpage->ai_info.flags);
cf_WriteByte(outfile, genericpage->ai_info.ai_class);
cf_WriteByte(outfile, genericpage->ai_info.ai_type);
cf_WriteByte(outfile, genericpage->ai_info.movement_type);
cf_WriteByte(outfile, genericpage->ai_info.movement_subtype);
cf_WriteFloat(outfile, genericpage->ai_info.fov);
cf_WriteFloat(outfile, genericpage->ai_info.max_velocity);
cf_WriteFloat(outfile, genericpage->ai_info.max_delta_velocity);
cf_WriteFloat(outfile, genericpage->ai_info.max_turn_rate);
cf_WriteFloat(outfile, genericpage->ai_info.avoid_friends_distance);
// Makes sure there are no bugs as things are added and removed -- ask chris
genericpage->ai_info.notify_flags &= ~AI_NOTIFIES_ALWAYS_ON;
cf_WriteInt(outfile, genericpage->ai_info.notify_flags);
genericpage->ai_info.notify_flags |= AI_NOTIFIES_ALWAYS_ON;
cf_WriteFloat(outfile, genericpage->ai_info.max_delta_turn_rate);
cf_WriteFloat(outfile, genericpage->ai_info.circle_distance);
cf_WriteFloat(outfile, genericpage->ai_info.attack_vel_percent);
cf_WriteFloat(outfile, genericpage->ai_info.dodge_percent);
cf_WriteFloat(outfile, genericpage->ai_info.dodge_vel_percent);
cf_WriteFloat(outfile, genericpage->ai_info.flee_vel_percent);
cf_WriteFloat(outfile, genericpage->ai_info.melee_damage[0]);
cf_WriteFloat(outfile, genericpage->ai_info.melee_damage[1]);
cf_WriteFloat(outfile, genericpage->ai_info.melee_latency[0]);
cf_WriteFloat(outfile, genericpage->ai_info.melee_latency[1]);
for (i = 0; i < MAX_OBJ_SOUNDS; i++) {
cf_WriteByte(outfile, GENERICPAGE_COMMAND_SOUND_NAME);
cf_WriteByte(outfile, strlen(genericpage->sound_name[i]) + 2); // 1 for sound index, 1 for null term
cf_WriteByte(outfile, i);
cf_WriteString(outfile, genericpage->sound_name[i]);
}
for (i = 0; i < MAX_AI_SOUNDS; i++) {
cf_WriteByte(outfile, GENERICPAGE_COMMAND_AI_SOUND_NAME);
cf_WriteByte(outfile, strlen(genericpage->ai_sound_name[i]) + 2); // 1 for sound index, 1 for null term
cf_WriteByte(outfile, i);
cf_WriteString(outfile, genericpage->ai_sound_name[i]);
}
for (i = 0; i < MAX_DSPEW_TYPES; i++) {
cf_WriteByte(outfile, GENERICPAGE_COMMAND_DSPEW_INFO);
cf_WriteByte(outfile, strlen(genericpage->dspew_name[i]) + 9); // 1 for null character
cf_WriteByte(outfile, i);
cf_WriteByte(outfile, genericpage->objinfo_struct.f_dspew);
cf_WriteFloat(outfile, genericpage->objinfo_struct.dspew_percent[i]);
cf_WriteShort(outfile, genericpage->objinfo_struct.dspew_number[i]);
cf_WriteString(outfile, genericpage->dspew_name[i]);
}
cf_WriteByte(outfile, GENERICPAGE_COMMAND_AI_INFO2);
cf_WriteByte(outfile, 13 * sizeof(float));
cf_WriteFloat(outfile, genericpage->ai_info.curiousity);
cf_WriteFloat(outfile, genericpage->ai_info.night_vision);
cf_WriteFloat(outfile, genericpage->ai_info.fog_vision);
cf_WriteFloat(outfile, genericpage->ai_info.lead_accuracy);
cf_WriteFloat(outfile, genericpage->ai_info.lead_varience);
cf_WriteFloat(outfile, genericpage->ai_info.fire_spread);
cf_WriteFloat(outfile, genericpage->ai_info.fight_team);
cf_WriteFloat(outfile, genericpage->ai_info.fight_same);
cf_WriteFloat(outfile, genericpage->ai_info.aggression);
cf_WriteFloat(outfile, genericpage->ai_info.hearing);
cf_WriteFloat(outfile, genericpage->ai_info.frustration);
cf_WriteFloat(outfile, genericpage->ai_info.roaming);
cf_WriteFloat(outfile, genericpage->ai_info.life_preservation);
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++) {
int j;
cf_WriteByte(outfile, GENERICPAGE_COMMAND_ANIM_STATES);
size = sizeof(anim_entry) * NUM_ANIMS_PER_CLASS;
size++; // add a byte to tell which class this is for
cf_WriteByte(outfile, size);
cf_WriteByte(outfile, i);
for (j = 0; j < NUM_ANIMS_PER_CLASS; j++) {
cf_WriteShort(outfile, genericpage->anim[i].elem[j].from);
cf_WriteShort(outfile, genericpage->anim[i].elem[j].to);
cf_WriteFloat(outfile, genericpage->anim[i].elem[j].spc);
}
}
cf_WriteByte(outfile, GENERICPAGE_COMMAND_WB_FLAGS);
cf_WriteByte(outfile, sizeof(int) * MAX_WBS_PER_OBJ);
for (i = 0; i < MAX_WBS_PER_OBJ; i++)
cf_WriteByte(outfile, genericpage->static_wb[i].flags);
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
int j;
cf_WriteByte(outfile, GENERICPAGE_COMMAND_WB_INFO);
size = 202;
cf_WriteByte(outfile, size);
cf_WriteByte(outfile, i);
for (j = 0; j < MAX_WB_GUNPOINTS; j++)
cf_WriteShort(outfile, genericpage->static_wb[i].gp_weapon_index[j]);
cf_WriteShort(outfile, genericpage->static_wb[i].aiming_gp_index);
cf_WriteByte(outfile, genericpage->static_wb[i].num_masks);
for (j = 0; j < MAX_WB_FIRING_MASKS; j++)
cf_WriteByte(outfile, genericpage->static_wb[i].gp_fire_masks[j]);
for (j = 0; j < MAX_WB_FIRING_MASKS; j++)
cf_WriteFloat(outfile, genericpage->static_wb[i].gp_fire_wait[j]);
for (j = 0; j < MAX_WB_FIRING_MASKS; j++) {
cf_WriteFloat(outfile, genericpage->static_wb[i].anim_time[j]);
cf_WriteFloat(outfile, genericpage->static_wb[i].anim_start_frame[j]);
cf_WriteFloat(outfile, genericpage->static_wb[i].anim_fire_frame[j]);
cf_WriteFloat(outfile, genericpage->static_wb[i].anim_end_frame[j]);
}
cf_WriteByte(outfile, genericpage->static_wb[i].aiming_flags);
cf_WriteFloat(outfile, genericpage->static_wb[i].aiming_3d_dot);
cf_WriteFloat(outfile, genericpage->static_wb[i].aiming_3d_dist);
cf_WriteFloat(outfile, genericpage->static_wb[i].aiming_XZ_dot);
}
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
int j;
for (j = 0; j < MAX_WB_GUNPOINTS; j++) {
cf_WriteByte(outfile, GENERICPAGE_COMMAND_WB_WEAPON);
size = strlen(genericpage->weapon_name[i][j]) + 1 + 2; // 1 for the null character and 2 for the 2 indices
cf_WriteByte(outfile, size);
cf_WriteByte(outfile, i);
cf_WriteByte(outfile, j);
cf_WriteString(outfile, genericpage->weapon_name[i][j]);
}
}
cf_WriteByte(outfile, GENERICPAGE_COMMAND_WB_QUADMASK);
cf_WriteByte(outfile, MAX_WBS_PER_OBJ);
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
cf_WriteByte(outfile, genericpage->static_wb[i].gp_quad_fire_mask);
}
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
int j;
for (j = 0; j < MAX_WB_FIRING_MASKS; j++) {
cf_WriteByte(outfile, GENERICPAGE_COMMAND_WB_FIRE_SOUND);
size = strlen(genericpage->fire_sound_name[i][j]) + 1 + 2; // 1 for the null character and 2 for the 2 indices
cf_WriteByte(outfile, size);
cf_WriteByte(outfile, i);
cf_WriteByte(outfile, j);
cf_WriteString(outfile, genericpage->fire_sound_name[i][j]);
}
}
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++) {
int j;
for (j = 0; j < NUM_ANIMS_PER_CLASS; j++) {
cf_WriteByte(outfile, GENERICPAGE_COMMAND_ANIM_SOUND_NAME);
size = strlen(genericpage->anim_sound_name[i][j]) + 1 + 2; // 1 for the null character and 2 for the 2 indices
cf_WriteByte(outfile, size);
cf_WriteByte(outfile, i);
cf_WriteByte(outfile, j);
cf_WriteString(outfile, genericpage->anim_sound_name[i][j]);
}
}
cf_WriteByte(outfile, GENERICPAGE_COMMAND_END); // we're all done
cf_WriteByte(outfile, 0);
}
// Given an open file pointer and a generic_page struct, writes that generic page out
void mng_WriteNewGenericPage(CFILE *outfile, mngs_generic_page *genericpage) {
int i, j;
ASSERT(outfile != NULL);
ASSERT(genericpage != NULL);
ASSERT((strlen(genericpage->image_name)) > 2);
int offset = StartManagePage(outfile, PAGETYPE_GENERIC);
cf_WriteShort(outfile, GENERICFILE_VERSION);
cf_WriteByte(outfile, genericpage->objinfo_struct.type);
// Write out object name
cf_WriteString(outfile, genericpage->objinfo_struct.name);
// Write out model names
cf_WriteString(outfile, genericpage->image_name);
cf_WriteString(outfile, genericpage->med_image_name);
cf_WriteString(outfile, genericpage->lo_image_name);
// Write out impact data
cf_WriteFloat(outfile, genericpage->objinfo_struct.impact_size);
cf_WriteFloat(outfile, genericpage->objinfo_struct.impact_time);
cf_WriteFloat(outfile, genericpage->objinfo_struct.damage);
// Write score
cf_WriteShort(outfile, genericpage->objinfo_struct.score);
// Write ammo
if (genericpage->objinfo_struct.type == OBJ_POWERUP)
cf_WriteShort(outfile, genericpage->objinfo_struct.ammo_count);
// Write script name
cf_WriteString(outfile, ""); // genericpage->objinfo_struct.script_name
// Write module name
cf_WriteString(outfile, genericpage->objinfo_struct.module_name);
// Write scriptname override
cf_WriteString(outfile, genericpage->objinfo_struct.script_name_override);
if (genericpage->objinfo_struct.description != NULL) {
// Write description if there is one
cf_WriteByte(outfile, 1);
cf_WriteString(outfile, genericpage->objinfo_struct.description);
} else
cf_WriteByte(outfile, 0);
// Write icon name
cf_WriteString(outfile, genericpage->objinfo_struct.icon_name);
// Write LOD distances
cf_WriteFloat(outfile, genericpage->objinfo_struct.med_lod_distance);
cf_WriteFloat(outfile, genericpage->objinfo_struct.lo_lod_distance);
// Write physics stuff
mng_WritePhysicsChunk(&genericpage->objinfo_struct.phys_info, outfile);
// Write size
cf_WriteFloat(outfile, genericpage->objinfo_struct.size);
// Write light info
mng_WriteLightingChunk(&genericpage->objinfo_struct.lighting_info, outfile);
// Write hit points
cf_WriteInt(outfile, genericpage->objinfo_struct.hit_points);
// Write flags
cf_WriteInt(outfile, genericpage->objinfo_struct.flags);
// Write AI info
cf_WriteInt(outfile, genericpage->ai_info.flags);
cf_WriteByte(outfile, genericpage->ai_info.ai_class);
cf_WriteByte(outfile, genericpage->ai_info.ai_type);
cf_WriteByte(outfile, genericpage->ai_info.movement_type);
cf_WriteByte(outfile, genericpage->ai_info.movement_subtype);
cf_WriteFloat(outfile, genericpage->ai_info.fov);
cf_WriteFloat(outfile, genericpage->ai_info.max_velocity);
cf_WriteFloat(outfile, genericpage->ai_info.max_delta_velocity);
cf_WriteFloat(outfile, genericpage->ai_info.max_turn_rate);
// Makes sure there are no bugs as things are added and removed -- ask chris
genericpage->ai_info.notify_flags &= ~AI_NOTIFIES_ALWAYS_ON;
cf_WriteInt(outfile, genericpage->ai_info.notify_flags);
genericpage->ai_info.notify_flags |= AI_NOTIFIES_ALWAYS_ON;
cf_WriteFloat(outfile, genericpage->ai_info.max_delta_turn_rate);
cf_WriteFloat(outfile, genericpage->ai_info.circle_distance);
cf_WriteFloat(outfile, genericpage->ai_info.attack_vel_percent);
cf_WriteFloat(outfile, genericpage->ai_info.dodge_percent);
cf_WriteFloat(outfile, genericpage->ai_info.dodge_vel_percent);
cf_WriteFloat(outfile, genericpage->ai_info.flee_vel_percent);
cf_WriteFloat(outfile, genericpage->ai_info.melee_damage[0]);
cf_WriteFloat(outfile, genericpage->ai_info.melee_damage[1]);
cf_WriteFloat(outfile, genericpage->ai_info.melee_latency[0]);
cf_WriteFloat(outfile, genericpage->ai_info.melee_latency[1]);
cf_WriteFloat(outfile, genericpage->ai_info.curiousity);
cf_WriteFloat(outfile, genericpage->ai_info.night_vision);
cf_WriteFloat(outfile, genericpage->ai_info.fog_vision);
cf_WriteFloat(outfile, genericpage->ai_info.lead_accuracy);
cf_WriteFloat(outfile, genericpage->ai_info.lead_varience);
cf_WriteFloat(outfile, genericpage->ai_info.fire_spread);
cf_WriteFloat(outfile, genericpage->ai_info.fight_team);
cf_WriteFloat(outfile, genericpage->ai_info.fight_same);
cf_WriteFloat(outfile, genericpage->ai_info.aggression);
cf_WriteFloat(outfile, genericpage->ai_info.hearing);
cf_WriteFloat(outfile, genericpage->ai_info.frustration);
cf_WriteFloat(outfile, genericpage->ai_info.roaming);
cf_WriteFloat(outfile, genericpage->ai_info.life_preservation);
cf_WriteFloat(outfile, genericpage->ai_info.avoid_friends_distance);
cf_WriteFloat(outfile, genericpage->ai_info.biased_flight_importance);
cf_WriteFloat(outfile, genericpage->ai_info.biased_flight_min);
cf_WriteFloat(outfile, genericpage->ai_info.biased_flight_max);
// Write out objects spewed
for (i = 0; i < MAX_DSPEW_TYPES; i++) {
cf_WriteByte(outfile, genericpage->objinfo_struct.f_dspew);
cf_WriteFloat(outfile, genericpage->objinfo_struct.dspew_percent[i]);
cf_WriteShort(outfile, genericpage->objinfo_struct.dspew_number[i]);
cf_WriteString(outfile, genericpage->dspew_name[i]);
}
// Write out animation info
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++) {
for (j = 0; j < NUM_ANIMS_PER_CLASS; j++) {
cf_WriteShort(outfile, genericpage->anim[i].elem[j].from);
cf_WriteShort(outfile, genericpage->anim[i].elem[j].to);
cf_WriteFloat(outfile, genericpage->anim[i].elem[j].spc);
}
}
// Write out weapon batteries
for (i = 0; i < MAX_WBS_PER_OBJ; i++)
mng_WriteWeaponBatteryChunk(&genericpage->static_wb[i], outfile);
// Write out weapon names
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
for (j = 0; j < MAX_WB_GUNPOINTS; j++)
cf_WriteString(outfile, genericpage->weapon_name[i][j]);
}
// Write out sounds
for (i = 0; i < MAX_OBJ_SOUNDS; i++)
cf_WriteString(outfile, genericpage->sound_name[i]);
for (i = 0; i < MAX_AI_SOUNDS; i++)
cf_WriteString(outfile, genericpage->ai_sound_name[i]);
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
for (j = 0; j < MAX_WB_FIRING_MASKS; j++)
cf_WriteString(outfile, genericpage->fire_sound_name[i][j]);
}
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++) {
for (j = 0; j < NUM_ANIMS_PER_CLASS; j++)
cf_WriteString(outfile, genericpage->anim_sound_name[i][j]);
}
// Write out respawn scalar
cf_WriteFloat(outfile, genericpage->objinfo_struct.respawn_scalar);
// Write out death information
cf_WriteShort(outfile, MAX_DEATH_TYPES);
for (i = 0; i < MAX_DEATH_TYPES; i++) {
cf_WriteInt(outfile, genericpage->objinfo_struct.death_types[i].flags);
cf_WriteFloat(outfile, genericpage->objinfo_struct.death_types[i].delay_min);
cf_WriteFloat(outfile, genericpage->objinfo_struct.death_types[i].delay_max);
cf_WriteByte(outfile, genericpage->objinfo_struct.death_probabilities[i]);
}
EndManagePage(outfile, offset);
}
// Old delay types
#define OLD_DF_DELAY_NONE 0x0000000
#define OLD_DF_DELAY_MIN_MAX 0x0000001
#define OLD_DF_DELAY_FROM_ANIM 0x0000002
#define OLD_DF_DELAY_MASK 0x0000003
#define OLD_DF_DELAY_SHIFT 0
void GenericPageSetPowerupDefaultAmmo(object_info *ip) {
// Default is zero
ip->ammo_count = 0;
// Set for specific types
if (!stricmp(ip->name, "Vauss"))
ip->ammo_count = 5000;
if (!stricmp(ip->name, "Napalm"))
ip->ammo_count = 500;
if (!stricmp(ip->name, "MassDriver"))
ip->ammo_count = 20;
if (!stricmp(ip->name, "Frag"))
ip->ammo_count = 1;
if (!stricmp(ip->name, "ImpactMortar"))
ip->ammo_count = 1;
if (!stricmp(ip->name, "NapalmRocket"))
ip->ammo_count = 1;
if (!stricmp(ip->name, "Cyclone"))
ip->ammo_count = 1;
if (!stricmp(ip->name, "BlackShark"))
ip->ammo_count = 1;
if (!stricmp(ip->name, "Concussion"))
ip->ammo_count = 1;
if (!stricmp(ip->name, "Homing"))
ip->ammo_count = 1;
if (!stricmp(ip->name, "Smart"))
ip->ammo_count = 1;
if (!stricmp(ip->name, "Mega"))
ip->ammo_count = 1;
if (!stricmp(ip->name, "Guided"))
ip->ammo_count = 1;
if (!stricmp(ip->name, "4PackHoming"))
ip->ammo_count = 4;
if (!stricmp(ip->name, "4PackConc"))
ip->ammo_count = 4;
if (!stricmp(ip->name, "4PackFrag"))
ip->ammo_count = 4;
if (!stricmp(ip->name, "4PackGuided"))
ip->ammo_count = 4;
if (!stricmp(ip->name, "Vauss clip"))
ip->ammo_count = 1250;
if (!stricmp(ip->name, "MassDriverAmmo"))
ip->ammo_count = 5;
if (!stricmp(ip->name, "NapalmTank"))
ip->ammo_count = 100;
}
// Reads a generic page from an open file. Returns 0 on error.
int mng_ReadNewGenericPage(CFILE *infile, mngs_generic_page *genericpage) {
int i, j;
ASSERT(infile != NULL);
mng_InitGenericPage(genericpage);
int version = cf_ReadShort(infile);
genericpage->objinfo_struct.type = cf_ReadByte(infile);
// Read object name
cf_ReadString(genericpage->objinfo_struct.name, PAGENAME_LEN, infile);
// Read model names
cf_ReadString(genericpage->image_name, PAGENAME_LEN, infile);
cf_ReadString(genericpage->med_image_name, PAGENAME_LEN, infile);
cf_ReadString(genericpage->lo_image_name, PAGENAME_LEN, infile);
// Read out impact data
genericpage->objinfo_struct.impact_size = cf_ReadFloat(infile);
genericpage->objinfo_struct.impact_time = cf_ReadFloat(infile);
genericpage->objinfo_struct.damage = cf_ReadFloat(infile);
// Read score
if (version >= 24)
genericpage->objinfo_struct.score = cf_ReadShort(infile);
else
genericpage->objinfo_struct.score = cf_ReadByte(infile);
// Read ammo
if (genericpage->objinfo_struct.type == OBJ_POWERUP) {
if (version >= 25)
genericpage->objinfo_struct.ammo_count = cf_ReadShort(infile);
else
GenericPageSetPowerupDefaultAmmo(&genericpage->objinfo_struct);
} else
genericpage->objinfo_struct.ammo_count = 0;
// Read script name
char dummy[256];
cf_ReadString(dummy, PAGENAME_LEN, infile); // genericpage->objinfo_struct.script_name
if (version >= 18) {
cf_ReadString(genericpage->objinfo_struct.module_name, MAX_MODULENAME_LEN, infile);
} else {
genericpage->objinfo_struct.module_name[0] = '\0';
}
if (version >= 19) {
cf_ReadString(genericpage->objinfo_struct.script_name_override, PAGENAME_LEN, infile);
} else {
genericpage->objinfo_struct.script_name_override[0] = '\0';
}
int desc = cf_ReadByte(infile);
if (desc) {
// Read description if there is one
char tempbuf[1024];
cf_ReadString(tempbuf, 1024, infile);
size_t slen = strlen(tempbuf) + 1;
genericpage->objinfo_struct.description = mem_rmalloc<char>(slen);
ASSERT(genericpage->objinfo_struct.description);
strcpy(genericpage->objinfo_struct.description, tempbuf);
} else
genericpage->objinfo_struct.description = NULL;
// Read icon name
cf_ReadString(genericpage->objinfo_struct.icon_name, PAGENAME_LEN, infile);
// Read LOD distances
genericpage->objinfo_struct.med_lod_distance = cf_ReadFloat(infile);
genericpage->objinfo_struct.lo_lod_distance = cf_ReadFloat(infile);
// Read physics stuff
mng_ReadPhysicsChunk(&genericpage->objinfo_struct.phys_info, infile);
// Write size
genericpage->objinfo_struct.size = cf_ReadFloat(infile);
// Write light info
mng_ReadLightingChunk(&genericpage->objinfo_struct.lighting_info, infile);
// Read hit points
genericpage->objinfo_struct.hit_points = cf_ReadInt(infile);
// Read flags
genericpage->objinfo_struct.flags = cf_ReadInt(infile);
// Write AI info
genericpage->ai_info.flags = cf_ReadInt(infile);
genericpage->ai_info.ai_class = cf_ReadByte(infile);
genericpage->ai_info.ai_type = cf_ReadByte(infile);
genericpage->ai_info.movement_type = cf_ReadByte(infile);
genericpage->ai_info.movement_subtype = cf_ReadByte(infile);
genericpage->ai_info.fov = cf_ReadFloat(infile);
genericpage->ai_info.max_velocity = cf_ReadFloat(infile);
genericpage->ai_info.max_delta_velocity = cf_ReadFloat(infile);
genericpage->ai_info.max_turn_rate = cf_ReadFloat(infile);
// Makes sure there are no bugs as things are added and removed -- ask chris
genericpage->ai_info.notify_flags &= ~AI_NOTIFIES_ALWAYS_ON;
genericpage->ai_info.notify_flags = cf_ReadInt(infile);
genericpage->ai_info.notify_flags |= AI_NOTIFIES_ALWAYS_ON;
genericpage->ai_info.max_delta_turn_rate = cf_ReadFloat(infile);
genericpage->ai_info.circle_distance = cf_ReadFloat(infile);
genericpage->ai_info.attack_vel_percent = cf_ReadFloat(infile);
genericpage->ai_info.dodge_percent = cf_ReadFloat(infile);
genericpage->ai_info.dodge_vel_percent = cf_ReadFloat(infile);
genericpage->ai_info.flee_vel_percent = cf_ReadFloat(infile);
genericpage->ai_info.melee_damage[0] = cf_ReadFloat(infile);
genericpage->ai_info.melee_damage[1] = cf_ReadFloat(infile);
genericpage->ai_info.melee_latency[0] = cf_ReadFloat(infile);
genericpage->ai_info.melee_latency[1] = cf_ReadFloat(infile);
genericpage->ai_info.curiousity = cf_ReadFloat(infile);
genericpage->ai_info.night_vision = cf_ReadFloat(infile);
genericpage->ai_info.fog_vision = cf_ReadFloat(infile);
genericpage->ai_info.lead_accuracy = cf_ReadFloat(infile);
genericpage->ai_info.lead_varience = cf_ReadFloat(infile);
genericpage->ai_info.fire_spread = cf_ReadFloat(infile);
genericpage->ai_info.fight_team = cf_ReadFloat(infile);
genericpage->ai_info.fight_same = cf_ReadFloat(infile);
genericpage->ai_info.aggression = cf_ReadFloat(infile);
genericpage->ai_info.hearing = cf_ReadFloat(infile);
genericpage->ai_info.frustration = cf_ReadFloat(infile);
genericpage->ai_info.roaming = cf_ReadFloat(infile);
genericpage->ai_info.life_preservation = cf_ReadFloat(infile);
if (version >= 16) {
genericpage->ai_info.avoid_friends_distance = cf_ReadFloat(infile);
} else if ((genericpage->objinfo_struct.flags | OIF_USES_PHYSICS) && genericpage->ai_info.max_velocity > 0.0f) {
genericpage->ai_info.flags |= AIF_AUTO_AVOID_FRIENDS;
genericpage->ai_info.avoid_friends_distance = genericpage->ai_info.circle_distance / 10.f;
if (genericpage->ai_info.avoid_friends_distance < 4.0f)
genericpage->ai_info.avoid_friends_distance = 4.0f;
} else {
genericpage->ai_info.avoid_friends_distance = 4.0f;
}
if (version >= 17) {
genericpage->ai_info.biased_flight_importance = cf_ReadFloat(infile);
genericpage->ai_info.biased_flight_min = cf_ReadFloat(infile);
genericpage->ai_info.biased_flight_max = cf_ReadFloat(infile);
} else {
genericpage->ai_info.biased_flight_importance = .5f;
genericpage->ai_info.biased_flight_min = 10.0f;
genericpage->ai_info.biased_flight_max = 50.0f;
}
// Write out objects spewed
for (i = 0; i < MAX_DSPEW_TYPES; i++) {
genericpage->objinfo_struct.f_dspew = cf_ReadByte(infile);
genericpage->objinfo_struct.dspew_percent[i] = cf_ReadFloat(infile);
genericpage->objinfo_struct.dspew_number[i] = cf_ReadShort(infile);
// Read spew name
cf_ReadString(genericpage->dspew_name[i], PAGENAME_LEN, infile);
}
// Read out animation info
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++) {
for (j = 0; j < NUM_ANIMS_PER_CLASS; j++) {
if (version < 20) {
genericpage->anim[i].elem[j].from = cf_ReadByte(infile);
genericpage->anim[i].elem[j].to = cf_ReadByte(infile);
} else {
genericpage->anim[i].elem[j].from = cf_ReadShort(infile);
genericpage->anim[i].elem[j].to = cf_ReadShort(infile);
}
genericpage->anim[i].elem[j].spc = cf_ReadFloat(infile);
}
}
// read weapon batteries
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
if (version >= 15)
mng_ReadWeaponBatteryChunk(&genericpage->static_wb[i], infile, 2);
else
mng_ReadWeaponBatteryChunk(&genericpage->static_wb[i], infile, 1);
}
// read weapon names
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
for (j = 0; j < MAX_WB_GUNPOINTS; j++)
cf_ReadString(genericpage->weapon_name[i][j], PAGENAME_LEN, infile);
}
// read sounds
ASSERT(MAX_OBJ_SOUNDS == 2);
for (i = 0; i < MAX_OBJ_SOUNDS; i++)
cf_ReadString(genericpage->sound_name[i], PAGENAME_LEN, infile);
if (version < 26) { // used to be three sounds
char temp_sound_name[PAGENAME_LEN];
cf_ReadString(temp_sound_name, PAGENAME_LEN, infile);
}
for (i = 0; i < MAX_AI_SOUNDS; i++)
cf_ReadString(genericpage->ai_sound_name[i], PAGENAME_LEN, infile);
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
for (j = 0; j < MAX_WB_FIRING_MASKS; j++)
cf_ReadString(genericpage->fire_sound_name[i][j], PAGENAME_LEN, infile);
}
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++) {
for (j = 0; j < NUM_ANIMS_PER_CLASS; j++)
cf_ReadString(genericpage->anim_sound_name[i][j], PAGENAME_LEN, infile);
}
// Read respawn scalar
if (version >= 21)
genericpage->objinfo_struct.respawn_scalar = cf_ReadFloat(infile);
else
genericpage->objinfo_struct.respawn_scalar = 1.0;
if (version >= 22) {
int n_death_types = cf_ReadShort(infile);
for (i = 0; i < n_death_types; i++) {
int flags = cf_ReadInt(infile);
if (version == 22) { // translate death flags
Int3(); // this version no longer supported
}
genericpage->objinfo_struct.death_types[i].flags = flags;
genericpage->objinfo_struct.death_types[i].delay_min = cf_ReadFloat(infile);
genericpage->objinfo_struct.death_types[i].delay_max = cf_ReadFloat(infile);
genericpage->objinfo_struct.death_probabilities[i] = cf_ReadByte(infile);
// Fix up for changed flags
if (version < 27) {
if ((flags & OLD_DF_DELAY_MASK) != OLD_DF_DELAY_MIN_MAX) {
genericpage->objinfo_struct.death_types[i].delay_min = 0.0;
genericpage->objinfo_struct.death_types[i].delay_max = 0.0;
}
flags &= ~DF_UNUSED;
}
}
}
// Set score from hitpoints if old version
if (version < 24) {
if ((genericpage->objinfo_struct.type == OBJ_ROBOT) ||
(genericpage->objinfo_struct.type == OBJ_BUILDING && (genericpage->objinfo_struct.flags & OIF_CONTROL_AI)))
if (genericpage->objinfo_struct.flags & OIF_DESTROYABLE)
genericpage->objinfo_struct.score = 3 * genericpage->objinfo_struct.hit_points;
}
ASSERT(genericpage->objinfo_struct.type != OBJ_NONE);
return 1; // successfully read
}
// Reads a generic page from an open file. Returns 0 on error.
int mng_ReadGenericPage(CFILE *infile, mngs_generic_page *genericpage) {
int done = 0;
char command;
uint16_t len;
int i, temp, version = 0, t;
if (!Old_table_method)
return mng_ReadNewGenericPage(infile, genericpage);
ASSERT(infile != NULL);
mng_InitGenericPage(genericpage);
while (!done) {
// Read in command byte then read in the length of that commands data
command = cf_ReadByte(infile);
len = cf_ReadByte(infile);
switch (command) {
case GENERICPAGE_COMMAND_END:
done = 1;
break;
case GENERICPAGE_COMMAND_VERSION:
version = cf_ReadByte(infile);
break;
case GENERICPAGE_COMMAND_TYPE:
genericpage->objinfo_struct.type = cf_ReadByte(infile);
break;
case GENERICPAGE_COMMAND_SCORE:
genericpage->objinfo_struct.score = cf_ReadByte(infile);
break;
case GENERICPAGE_COMMAND_SCRIPT: // Obsolete as of 09-24-97
cf_ReadByte(infile);
break;
case GENERICPAGE_COMMAND_SCRIPTNAME: {
char dummy[256];
cf_ReadString(dummy, len + 1, infile); // genericpage->objinfo_struct.script_name
} break;
case GENERICPAGE_COMMAND_DESCRIPTION:
genericpage->objinfo_struct.description = (char *)mem_malloc(len + 1);
ASSERT(genericpage->objinfo_struct.description); // out of memory!
cf_ReadString(genericpage->objinfo_struct.description, len + 1, infile);
break;
case GENERICPAGE_COMMAND_ICON_NAME:
cf_ReadString(genericpage->objinfo_struct.icon_name, len + 1, infile);
break;
case GENERICPAGE_COMMAND_IMAGE_NAME: // the name of the generic model
cf_ReadString(genericpage->image_name, len + 1, infile);
break;
case GENERICPAGE_COMMAND_LOD_MODELS: // the name of the lower res models
cf_ReadString(genericpage->med_image_name, len + 1, infile);
cf_ReadString(genericpage->lo_image_name, len + 1, infile);
break;
case GENERICPAGE_COMMAND_NAME:
cf_ReadString(genericpage->objinfo_struct.name, len + 1, infile);
break;
case GENERICPAGE_COMMAND_SIZE:
genericpage->objinfo_struct.size = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_LOD_DISTANCE:
genericpage->objinfo_struct.med_lod_distance = cf_ReadFloat(infile);
genericpage->objinfo_struct.lo_lod_distance = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_DSPEW_INFO: {
int i = cf_ReadByte(infile);
genericpage->objinfo_struct.f_dspew = cf_ReadByte(infile);
genericpage->objinfo_struct.dspew_percent[i] = cf_ReadFloat(infile);
genericpage->objinfo_struct.dspew_number[i] = cf_ReadShort(infile);
cf_ReadString(genericpage->dspew_name[i], len - 7, infile);
} break;
case GENERICPAGE_COMMAND_WB_FLAGS: {
for (int i = 0; i < MAX_WBS_PER_OBJ; i++)
genericpage->static_wb[i].flags = cf_ReadByte(infile);
} break;
case GENERICPAGE_COMMAND_IMPACT:
genericpage->objinfo_struct.impact_size = cf_ReadFloat(infile);
genericpage->objinfo_struct.impact_time = cf_ReadFloat(infile);
genericpage->objinfo_struct.damage = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_FLICKER_DISTANCE:
genericpage->objinfo_struct.lighting_info.flicker_distance = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_DIRECTION_DOT:
genericpage->objinfo_struct.lighting_info.directional_dot = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_AI_INFO2:
genericpage->ai_info.curiousity = cf_ReadFloat(infile);
genericpage->ai_info.night_vision = cf_ReadFloat(infile);
genericpage->ai_info.fog_vision = cf_ReadFloat(infile);
genericpage->ai_info.lead_accuracy = cf_ReadFloat(infile);
genericpage->ai_info.lead_varience = cf_ReadFloat(infile);
genericpage->ai_info.fire_spread = cf_ReadFloat(infile);
genericpage->ai_info.fight_team = cf_ReadFloat(infile);
genericpage->ai_info.fight_same = cf_ReadFloat(infile);
genericpage->ai_info.aggression = cf_ReadFloat(infile);
genericpage->ai_info.hearing = cf_ReadFloat(infile);
genericpage->ai_info.frustration = cf_ReadFloat(infile);
genericpage->ai_info.roaming = cf_ReadFloat(infile);
genericpage->ai_info.life_preservation = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_LIGHT_CAST:
genericpage->objinfo_struct.lighting_info.light_distance = cf_ReadFloat(infile);
genericpage->objinfo_struct.lighting_info.red_light1 = cf_ReadFloat(infile);
genericpage->objinfo_struct.lighting_info.green_light1 = cf_ReadFloat(infile);
genericpage->objinfo_struct.lighting_info.blue_light1 = cf_ReadFloat(infile);
if (version >= 2)
genericpage->objinfo_struct.lighting_info.time_interval = cf_ReadFloat(infile);
if (version >= 3) {
genericpage->objinfo_struct.lighting_info.red_light2 = cf_ReadFloat(infile);
genericpage->objinfo_struct.lighting_info.green_light2 = cf_ReadFloat(infile);
genericpage->objinfo_struct.lighting_info.blue_light2 = cf_ReadFloat(infile);
genericpage->objinfo_struct.lighting_info.flags = cf_ReadInt(infile);
genericpage->objinfo_struct.lighting_info.timebits = cf_ReadInt(infile);
genericpage->objinfo_struct.lighting_info.angle = cf_ReadByte(infile);
}
if (version >= 5) {
genericpage->objinfo_struct.lighting_info.lighting_render_type = cf_ReadByte(infile);
} else
genericpage->objinfo_struct.lighting_info.lighting_render_type = LRT_STATIC;
break;
case GENERICPAGE_COMMAND_HITPOINTS:
genericpage->objinfo_struct.hit_points = cf_ReadInt(infile);
break;
case GENERICPAGE_COMMAND_PHYS_MASS:
genericpage->objinfo_struct.phys_info.mass = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_PHYS_DRAG:
genericpage->objinfo_struct.phys_info.drag = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_ANIM_STATES:
temp = cf_ReadByte(infile);
{
int j;
for (j = 0; j < NUM_ANIMS_PER_CLASS; j++) {
if (version < 20) {
genericpage->anim[temp].elem[j].from = cf_ReadByte(infile);
genericpage->anim[temp].elem[j].to = cf_ReadByte(infile);
} else {
genericpage->anim[temp].elem[j].from = cf_ReadShort(infile);
genericpage->anim[temp].elem[j].to = cf_ReadShort(infile);
}
if (version <= 3)
genericpage->anim[temp].elem[j].spc = 1.0f;
else
genericpage->anim[temp].elem[j].spc = cf_ReadFloat(infile);
}
}
break;
case GENERICPAGE_COMMAND_FLAGS: {
int t = cf_ReadInt(infile);
genericpage->objinfo_struct.flags = t;
break;
}
case GENERICPAGE_COMMAND_WB_INFO: {
int i, j;
i = cf_ReadByte(infile);
if (version <= 7) {
for (j = 0; j < MAX_WB_GUNPOINTS; j++)
genericpage->static_wb[i].gp_weapon_index[j] = cf_ReadInt(infile);
genericpage->static_wb[i].aiming_gp_index = cf_ReadInt(infile);
} else {
for (j = 0; j < MAX_WB_GUNPOINTS; j++)
genericpage->static_wb[i].gp_weapon_index[j] = cf_ReadShort(infile);
genericpage->static_wb[i].aiming_gp_index = cf_ReadShort(infile);
}
genericpage->static_wb[i].num_masks = cf_ReadByte(infile);
for (j = 0; j < MAX_WB_FIRING_MASKS; j++)
genericpage->static_wb[i].gp_fire_masks[j] = cf_ReadByte(infile);
for (j = 0; j < MAX_WB_FIRING_MASKS; j++)
genericpage->static_wb[i].gp_fire_wait[j] = cf_ReadFloat(infile);
for (j = 0; j < MAX_WB_FIRING_MASKS; j++) {
if (version < 12) {
genericpage->static_wb[i].anim_time[j] = 0.0f;
genericpage->static_wb[i].anim_start_frame[j] = 0.0f;
genericpage->static_wb[i].anim_fire_frame[j] = 0.0f;
genericpage->static_wb[i].anim_end_frame[j] = 0.0f;
} else {
genericpage->static_wb[i].anim_time[j] = cf_ReadFloat(infile);
genericpage->static_wb[i].anim_start_frame[j] = cf_ReadFloat(infile);
genericpage->static_wb[i].anim_fire_frame[j] = cf_ReadFloat(infile);
genericpage->static_wb[i].anim_end_frame[j] = cf_ReadFloat(infile);
}
}
genericpage->static_wb[i].aiming_flags = cf_ReadByte(infile);
genericpage->static_wb[i].aiming_3d_dot = cf_ReadFloat(infile);
genericpage->static_wb[i].aiming_3d_dist = cf_ReadFloat(infile);
genericpage->static_wb[i].aiming_XZ_dot = cf_ReadFloat(infile);
break;
}
case GENERICPAGE_COMMAND_WB_QUADMASK: {
for (int i = 0; i < MAX_WBS_PER_OBJ; i++) {
genericpage->static_wb[i].gp_quad_fire_mask = cf_ReadByte(infile);
}
break;
}
case GENERICPAGE_COMMAND_WB_WEAPON: {
int i, j;
i = cf_ReadByte(infile);
j = cf_ReadByte(infile);
cf_ReadString(genericpage->weapon_name[i][j], len - 1, infile);
break;
}
case GENERICPAGE_COMMAND_WB_FIRE_SOUND: {
int i, j;
i = cf_ReadByte(infile);
j = cf_ReadByte(infile);
cf_ReadString(genericpage->fire_sound_name[i][j], len - 1, infile);
break;
}
case GENERICPAGE_COMMAND_ANIM_SOUND_NAME: {
int i, j;
i = cf_ReadByte(infile);
j = cf_ReadByte(infile);
cf_ReadString(genericpage->anim_sound_name[i][j], len - 1, infile);
break;
}
case GENERICPAGE_COMMAND_SOUND_NAME: {
int i;
i = cf_ReadByte(infile);
cf_ReadString(genericpage->sound_name[i], len, infile);
break;
}
case GENERICPAGE_COMMAND_AI_SOUND_NAME: {
int i;
i = cf_ReadByte(infile);
cf_ReadString(genericpage->ai_sound_name[i], len, infile);
break;
}
case GENERICPAGE_COMMAND_AI_INFO:
if (version <= 6) {
genericpage->ai_info.flags = cf_ReadInt(infile);
} else {
// Makes sure there are no bugs as things are added and removed -- ask chris
genericpage->ai_info.notify_flags = AI_NOTIFIES_ALWAYS_ON;
genericpage->ai_info.flags = cf_ReadInt(infile);
genericpage->ai_info.ai_class = cf_ReadByte(infile);
genericpage->ai_info.ai_type = cf_ReadByte(infile);
if (version < 13)
cf_ReadInt(infile);
genericpage->ai_info.movement_type = cf_ReadByte(infile);
genericpage->ai_info.movement_subtype = cf_ReadByte(infile);
genericpage->ai_info.fov = cf_ReadFloat(infile);
if (version >= 10) {
genericpage->ai_info.max_velocity = cf_ReadFloat(infile);
genericpage->ai_info.max_delta_velocity = cf_ReadFloat(infile);
genericpage->ai_info.max_turn_rate = cf_ReadFloat(infile);
// Makes sure there are no bugs as things are added and removed -- ask chris
genericpage->ai_info.notify_flags |= cf_ReadInt(infile);
}
if (version >= 11) {
genericpage->ai_info.max_delta_turn_rate = cf_ReadFloat(infile);
genericpage->ai_info.circle_distance = cf_ReadFloat(infile);
genericpage->ai_info.attack_vel_percent = cf_ReadFloat(infile);
genericpage->ai_info.dodge_percent = cf_ReadFloat(infile);
genericpage->ai_info.dodge_vel_percent = cf_ReadFloat(infile);
genericpage->ai_info.flee_vel_percent = cf_ReadFloat(infile);
genericpage->ai_info.melee_damage[0] = cf_ReadFloat(infile);
genericpage->ai_info.melee_damage[1] = cf_ReadFloat(infile);
genericpage->ai_info.melee_latency[0] = cf_ReadFloat(infile);
genericpage->ai_info.melee_latency[1] = cf_ReadFloat(infile);
} else {
genericpage->ai_info.max_delta_turn_rate = 16000.0;
genericpage->ai_info.circle_distance = 10.0f;
genericpage->ai_info.attack_vel_percent = 1.0f;
genericpage->ai_info.dodge_percent = .4f;
genericpage->ai_info.dodge_vel_percent = .5f;
genericpage->ai_info.flee_vel_percent = 1.0f;
genericpage->ai_info.melee_damage[0] = 9.0f;
genericpage->ai_info.melee_damage[1] = 13.0f;
genericpage->ai_info.melee_latency[0] = 3.1f;
genericpage->ai_info.melee_latency[1] = 4.5f;
}
if (version >= 16) {
genericpage->ai_info.avoid_friends_distance = cf_ReadFloat(infile);
}
}
break;
case GENERICPAGE_COMMAND_PHYS_FLAGS:
genericpage->objinfo_struct.phys_info.flags = cf_ReadInt(infile);
break;
case GENERICPAGE_COMMAND_ROT_DRAG:
genericpage->objinfo_struct.phys_info.rotdrag = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_FULL_THRUST:
genericpage->objinfo_struct.phys_info.full_thrust = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_FULL_ROTTHRUST:
genericpage->objinfo_struct.phys_info.full_rotthrust = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_TURNROLL_RATE:
genericpage->objinfo_struct.phys_info.max_turnroll_rate = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_TURNROLL_RATIO:
genericpage->objinfo_struct.phys_info.turnroll_ratio = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_WIGGLE_AMP:
genericpage->objinfo_struct.phys_info.wiggle_amplitude = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_WIGGLE_FREQ:
genericpage->objinfo_struct.phys_info.wiggles_per_sec = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_INT_VELOCITY:
genericpage->objinfo_struct.phys_info.velocity.z = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_INT_ROTVEL:
genericpage->objinfo_struct.phys_info.rotvel.x = cf_ReadFloat(infile);
genericpage->objinfo_struct.phys_info.rotvel.y = cf_ReadFloat(infile);
genericpage->objinfo_struct.phys_info.rotvel.z = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_NUM_BOUNCES:
genericpage->objinfo_struct.phys_info.num_bounces = cf_ReadInt(infile);
break;
case GENERICPAGE_COMMAND_COEFF_REST:
genericpage->objinfo_struct.phys_info.coeff_restitution = cf_ReadFloat(infile);
break;
case GENERICPAGE_COMMAND_HIT_DIE_DOT:
genericpage->objinfo_struct.phys_info.hit_die_dot = cf_ReadFloat(infile);
break;
default:
// Ignore the ones we don't know
for (i = 0; i < len; i++)
cf_ReadByte(infile);
break;
}
}
ASSERT(genericpage->objinfo_struct.type != OBJ_NONE);
if (version < 16) {
genericpage->ai_info.flags |= AIF_AUTO_AVOID_FRIENDS;
genericpage->ai_info.avoid_friends_distance = genericpage->ai_info.circle_distance / 10.f;
if (genericpage->ai_info.avoid_friends_distance < 4.0f)
genericpage->ai_info.avoid_friends_distance = 4.0f;
}
if (!strnicmp("INVALID", genericpage->med_image_name, 7))
strcpy(genericpage->med_image_name, "");
if (!strnicmp("INVALID", genericpage->lo_image_name, 7))
strcpy(genericpage->lo_image_name, "");
for (i = 0; i < MAX_OBJ_SOUNDS; i++) {
if (!strnicmp("INVALID", genericpage->sound_name[i], 7))
strcpy(genericpage->sound_name[i], "");
}
for (i = 0; i < MAX_DSPEW_TYPES; i++) {
if (!strnicmp("INVALID", genericpage->dspew_name[i], 7))
strcpy(genericpage->dspew_name[i], "");
}
for (i = 0; i < MAX_AI_SOUNDS; i++) {
if (!strnicmp("INVALID", genericpage->ai_sound_name[i], 7))
strcpy(genericpage->ai_sound_name[i], "");
}
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
for (t = 0; t < MAX_WB_FIRING_MASKS; t++) {
if (!strnicmp("INVALID", genericpage->fire_sound_name[i][t], 7))
strcpy(genericpage->fire_sound_name[i][t], "");
}
}
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++) {
for (t = 0; t < NUM_ANIMS_PER_CLASS; t++) {
if (!strnicmp("INVALID", genericpage->anim_sound_name[i][t], 7))
strcpy(genericpage->anim_sound_name[i][t], "");
}
}
return 1; // successfully read
}
// Reads in the generic named "name" into genericpage struct
// Returns 0 on error or couldn't find, else 1 if all is good
int mng_FindSpecificGenericPage(char *name, mngs_generic_page *genericpage, int offset) {
CFILE *infile;
uint8_t pagetype;
int done = 0, found = 0;
int first_try = 1;
std::filesystem::path tablename;
if (Loading_locals) {
infile = cfopen(LocalTableFilename, "rb");
} else if (Loading_addon_table != -1) {
infile = cfopen(AddOnDataTables[Loading_addon_table].AddOnTableFilename, "rb");
} else {
if (Network_up && Starting_editor) {
int farg = FindArg("-filter");
if (farg)
tablename = GameArgs[farg + 1];
else
tablename = LocalTableDir / NET_TABLE;
infile = cfopen(tablename, "rb");
} else {
infile = NULL;
if (TablefileNameOverride) {
infile = cfopen(TablefileNameOverride, "rb");
}
if (!infile)
infile = cfopen(TableFilename, "rb");
}
}
if (!infile) {
LOG_ERROR << "Couldn't open table file to find generic!";
Int3();
return 0;
}
try_again:;
if (offset)
cfseek(infile, offset, SEEK_SET);
// Read in the entire page file until we find the page we want
while (!done) {
if (cfeof(infile)) {
done = 1;
continue;
}
pagetype = cf_ReadByte(infile);
int len = cf_ReadInt(infile);
// If not a generic page, just read it in and ignore it
if (pagetype != PAGETYPE_GENERIC) {
cfseek(infile, len - 4, SEEK_CUR);
continue;
}
mng_ReadNewGenericPage(infile, genericpage);
if (!stricmp(name, genericpage->objinfo_struct.name)) {
// This is the page we want
found = 1;
done = 1;
} else {
if (genericpage->objinfo_struct.description != NULL) {
mem_free(genericpage->objinfo_struct.description);
genericpage->objinfo_struct.description = NULL;
}
genericpage->objinfo_struct.icon_name[0] = '\0';
}
}
cfclose(infile);
if (!found && first_try) {
done = first_try = 0;
infile = cfopen("extra.gam", "rb");
if (infile)
goto try_again;
}
return found; // successful!
}
// Given a generic page, allocs a generic and calls AssignGenericPageTogeneric to actually
// load models and values. Rturns generic handle on success, -1 if fail
int mng_SetAndLoadGeneric(mngs_generic_page *genericpage, CFILE *infile) {
int i, j, n;
bool f_anim = false;
bool f_weapons = false;
bool f_ai = false;
f_ai = (genericpage->objinfo_struct.flags & OIF_CONTROL_AI) ? true : false;
if (f_ai) {
f_weapons = f_anim = true;
} else {
if (genericpage->objinfo_struct.type == OBJ_ROBOT || genericpage->objinfo_struct.type == OBJ_BUILDING) {
f_anim = true;
} else if (genericpage->objinfo_struct.type == OBJ_POWERUP) {
if (f_ai) {
f_anim = true;
} else {
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++) {
for (j = 0; j < NUM_ANIMS_PER_CLASS; j++) {
if (genericpage->anim[i].elem[j].to != 0 || genericpage->anim[i].elem[j].from != 0) {
f_anim = true;
break;
}
}
}
}
} else if (genericpage->objinfo_struct.type == OBJ_CLUTTER) {
if (f_ai) {
f_anim = true;
} else {
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++) {
for (j = 0; j < NUM_ANIMS_PER_CLASS; j++) {
if (genericpage->anim[i].elem[j].to != 0 || genericpage->anim[i].elem[j].from != 0) {
f_anim = true;
break;
}
}
}
}
}
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
for (j = 0; j < genericpage->static_wb[i].num_masks; j++) {
if (genericpage->static_wb[i].gp_fire_masks[j] != 0) {
f_weapons = true;
break;
}
}
}
}
if (Running_editor) {
f_ai = f_weapons = f_anim = true;
}
n = AllocObjectID(genericpage->objinfo_struct.type, f_anim, f_weapons, f_ai);
if (n < 0)
return -1;
if (!mng_AssignGenericPageToObjInfo(genericpage, n, infile))
return -1;
return n;
}
// Given a genericpage and a generic handle, attempts to make generic n correspond to
// to the genericpage.
// Returns 1 on success, 0 otherwise
int mng_AssignGenericPageToObjInfo(mngs_generic_page *genericpage, int n, CFILE *infile) {
object_info *objinfopointer = &Object_info[n];
int img_handle;
int i, j;
// copy our values
// memcpy (objinfopointer,&genericpage->objinfo_struct,sizeof(*objinfopointer));
memcpy(objinfopointer, &genericpage->objinfo_struct,
sizeof(*objinfopointer) - sizeof(anim_elem *) - sizeof(otype_wb_info *) - sizeof(t_ai_info *));
strcpy(objinfopointer->name, genericpage->objinfo_struct.name);
if (objinfopointer->anim)
memcpy(objinfopointer->anim, &genericpage->anim, sizeof(genericpage->anim));
if (objinfopointer->static_wb)
memcpy(objinfopointer->static_wb, &genericpage->static_wb, sizeof(genericpage->static_wb));
if (objinfopointer->ai_info)
memcpy(objinfopointer->ai_info, &genericpage->ai_info, sizeof(genericpage->ai_info));
objinfopointer->multi_allowed = true;
strcpy(objinfopointer->icon_name, genericpage->objinfo_struct.icon_name);
// First see if our image differs from the one on the net
// If it is, make a copy
// If it is a release version, don't do any of this
#ifndef RELEASE
if (Network_up) {
UpdatePrimitive(LocalModelsDir / genericpage->image_name, NetModelsDir / genericpage->image_name,
genericpage->image_name, PAGETYPE_GENERIC, objinfopointer->name);
if (stricmp(genericpage->med_image_name, "INVALID NAME") != 0 && genericpage->med_image_name[0] != 0) {
UpdatePrimitive(LocalModelsDir / genericpage->med_image_name, NetModelsDir / genericpage->med_image_name,
genericpage->med_image_name, PAGETYPE_GENERIC, objinfopointer->name);
}
if (stricmp(genericpage->lo_image_name, "INVALID NAME") != 0 && genericpage->lo_image_name[0] != 0) {
UpdatePrimitive(LocalModelsDir / genericpage->lo_image_name, NetModelsDir / genericpage->lo_image_name,
genericpage->lo_image_name, PAGETYPE_GENERIC, objinfopointer->name);
}
}
#endif
// Try and load our generic model from the disk
img_handle = LoadPolyModel(genericpage->image_name, 1);
if (img_handle < 0) {
LOG_ERROR.printf("Couldn't load file '%s' in AssignGenericPage...", genericpage->image_name);
objinfopointer->render_handle = -1;
return 0;
} else
objinfopointer->render_handle = img_handle;
if (stricmp(genericpage->med_image_name, "INVALID NAME") && genericpage->med_image_name[0] != 0) {
img_handle = LoadPolyModel(genericpage->med_image_name, 1);
if (img_handle < 0) {
LOG_ERROR.printf("Couldn't load file '%s' in AssignGenericPage...", genericpage->med_image_name);
objinfopointer->med_render_handle = -1;
return 0;
} else
objinfopointer->med_render_handle = img_handle;
} else
objinfopointer->med_render_handle = -1;
if (stricmp(genericpage->lo_image_name, "INVALID NAME") && genericpage->lo_image_name[0] != 0) {
img_handle = LoadPolyModel(genericpage->lo_image_name, 1);
if (img_handle < 0) {
LOG_ERROR.printf("Couldn't load file '%s' in AssignGenericPage...", genericpage->lo_image_name);
objinfopointer->lo_render_handle = -1;
return 0;
} else
objinfopointer->lo_render_handle = img_handle;
} else
objinfopointer->lo_render_handle = -1;
// Try and load the various sounds
for (i = 0; i < MAX_OBJ_SOUNDS; i++) {
if (stricmp(genericpage->sound_name[i], "INVALID NAME") && genericpage->sound_name[i][0] != 0) {
int sound_handle = mng_GetGuaranteedSoundPage(genericpage->sound_name[i]);
if (sound_handle < 0) {
LOG_WARNING.printf("Couldn't load sound file '%s' in AssignPowPage %s...", genericpage->sound_name[i],
genericpage->objinfo_struct.name);
objinfopointer->sounds[i] = SOUND_NONE_INDEX;
} else
objinfopointer->sounds[i] = sound_handle;
} else
objinfopointer->sounds[i] = SOUND_NONE_INDEX;
}
for (i = 0; i < MAX_DSPEW_TYPES; i++) {
if (genericpage->dspew_name[i][0] != '\0') {
int obj_handle = mng_GetGuaranteedGenericPage(genericpage->dspew_name[i], infile);
if (obj_handle < 0) {
objinfopointer->dspew[i] = 0;
objinfopointer->dspew_number[i] = 0;
objinfopointer->dspew_percent[i] = 0.0f;
} else {
objinfopointer->dspew[i] = obj_handle;
}
} else {
objinfopointer->dspew[i] = -1;
objinfopointer->dspew_number[i] = 0;
objinfopointer->dspew_percent[i] = 0.0f;
}
}
// Try and load the various sounds
if (objinfopointer->ai_info) {
for (i = 0; i < MAX_AI_SOUNDS; i++) {
if (stricmp(genericpage->ai_sound_name[i], "INVALID NAME") && genericpage->ai_sound_name[i][0] != 0) {
int sound_handle = mng_GetGuaranteedSoundPage(genericpage->ai_sound_name[i]);
if (sound_handle < 0) {
LOG_ERROR.printf("Couldn't load ai sound file '%s' in AssignPowPage %s...", genericpage->ai_sound_name[i],
genericpage->objinfo_struct.name);
objinfopointer->ai_info->sound[i] = SOUND_NONE_INDEX;
} else
objinfopointer->ai_info->sound[i] = sound_handle;
} else
objinfopointer->ai_info->sound[i] = SOUND_NONE_INDEX;
}
}
// Try and load the various weapons
if (objinfopointer->static_wb) {
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
for (j = 0; j < MAX_WB_GUNPOINTS; j++) {
if (genericpage->weapon_name[i][j][0] != '\0') {
int weapon_handle = mng_GetGuaranteedWeaponPage(genericpage->weapon_name[i][j]);
if (weapon_handle < 0) {
LOG_ERROR.printf("Couldn't load weapon file '%s' in AssignPowPage %s...", genericpage->weapon_name[i][j],
genericpage->objinfo_struct.name);
objinfopointer->static_wb[i].gp_weapon_index[j] = LASER_INDEX;
} else
objinfopointer->static_wb[i].gp_weapon_index[j] = weapon_handle;
} else
objinfopointer->static_wb[i].gp_weapon_index[j] = LASER_INDEX;
}
}
// Try and load the various wb sounds
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
for (j = 0; j < MAX_WB_FIRING_MASKS; j++) {
if (genericpage->fire_sound_name[i][j][0] != '\0') {
int fire_sound_handle = mng_GetGuaranteedSoundPage(genericpage->fire_sound_name[i][j]);
if (fire_sound_handle < 0) {
LOG_ERROR.printf("Couldn't load fire sound file '%s' in AssignPowPage %s...",
genericpage->fire_sound_name[i][j], genericpage->objinfo_struct.name);
objinfopointer->static_wb[i].fm_fire_sound_index[j] = SOUND_NONE_INDEX;
} else
objinfopointer->static_wb[i].fm_fire_sound_index[j] = fire_sound_handle;
} else
objinfopointer->static_wb[i].fm_fire_sound_index[j] = SOUND_NONE_INDEX;
}
}
}
// Try and load the various wb sounds
if (objinfopointer->anim) {
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++) {
for (j = 0; j < NUM_ANIMS_PER_CLASS; j++) {
if (stricmp(genericpage->anim_sound_name[i][j], "INVALID NAME") && genericpage->anim_sound_name[i][j][0] != 0) {
int anim_sound_handle = mng_GetGuaranteedSoundPage(genericpage->anim_sound_name[i][j]);
if (anim_sound_handle < 0) {
LOG_ERROR.printf("Couldn't load anim sound file '%s' in AssignPowPage %s...",
genericpage->anim_sound_name[i][j], genericpage->objinfo_struct.name);
objinfopointer->anim[i].elem[j].anim_sound_index = SOUND_NONE_INDEX;
} else
objinfopointer->anim[i].elem[j].anim_sound_index = anim_sound_handle;
} else
objinfopointer->anim[i].elem[j].anim_sound_index = SOUND_NONE_INDEX;
}
}
}
// objinfopointer->size=(ComputeDefaultSize(objinfopointer->render_handle, &objinfopointer->size_offset, 0, 0));
return 1;
}
// Copies values from a generic into a generic_page
void mng_AssignObjInfoToGenericPage(int n, mngs_generic_page *genericpage) {
object_info *objinfopointer = &Object_info[n];
int i, j;
// Assign the values
memcpy(&genericpage->objinfo_struct, objinfopointer, sizeof(*objinfopointer));
strcpy(genericpage->objinfo_struct.name, objinfopointer->name);
if (objinfopointer->anim)
memcpy(&genericpage->anim, objinfopointer->anim, sizeof(genericpage->anim));
if (objinfopointer->static_wb)
memcpy(&genericpage->static_wb, objinfopointer->static_wb, sizeof(genericpage->static_wb));
if (objinfopointer->ai_info)
memcpy(&genericpage->ai_info, objinfopointer->ai_info, sizeof(genericpage->ai_info));
if (objinfopointer->description != NULL) {
int len = strlen(objinfopointer->description);
genericpage->objinfo_struct.description = (char *)mem_malloc(len + 1);
ASSERT(genericpage->objinfo_struct.description);
strcpy(genericpage->objinfo_struct.description, objinfopointer->description);
} else
genericpage->objinfo_struct.description = NULL;
strcpy(genericpage->objinfo_struct.icon_name, objinfopointer->icon_name);
if (objinfopointer->render_handle != -1)
strcpy(genericpage->image_name, Poly_models[objinfopointer->render_handle].name);
else
strcpy(genericpage->image_name, "");
if (objinfopointer->med_render_handle != -1)
strcpy(genericpage->med_image_name, Poly_models[objinfopointer->med_render_handle].name);
else
strcpy(genericpage->med_image_name, "");
if (objinfopointer->lo_render_handle != -1)
strcpy(genericpage->lo_image_name, Poly_models[objinfopointer->lo_render_handle].name);
else
strcpy(genericpage->lo_image_name, "");
for (i = 0; i < MAX_OBJ_SOUNDS; i++) {
if (objinfopointer->sounds[i] != SOUND_NONE_INDEX)
strcpy(genericpage->sound_name[i], Sounds[objinfopointer->sounds[i]].name);
else
strcpy(genericpage->sound_name[i], "");
}
for (i = 0; i < MAX_DSPEW_TYPES; i++) {
if ((objinfopointer->dspew[i] != -1) && Object_info[objinfopointer->dspew[i]].type != OBJ_NONE)
strcpy(genericpage->dspew_name[i], Object_info[objinfopointer->dspew[i]].name);
else
strcpy(genericpage->dspew_name[i], "\0");
}
for (i = 0; i < MAX_AI_SOUNDS; i++) {
if (objinfopointer->ai_info && objinfopointer->ai_info->sound[i] != SOUND_NONE_INDEX)
strcpy(genericpage->ai_sound_name[i], Sounds[objinfopointer->ai_info->sound[i]].name);
else
strcpy(genericpage->ai_sound_name[i], "");
}
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
for (j = 0; j < MAX_WB_FIRING_MASKS; j++) {
if (objinfopointer->static_wb && objinfopointer->static_wb[i].fm_fire_sound_index[j] >= 0)
strcpy(genericpage->fire_sound_name[i][j], Sounds[objinfopointer->static_wb[i].fm_fire_sound_index[j]].name);
else
strcpy(genericpage->fire_sound_name[i][j], "");
}
}
for (i = 0; i < NUM_MOVEMENT_CLASSES; i++) {
for (j = 0; j < NUM_ANIMS_PER_CLASS; j++) {
if (objinfopointer->anim && objinfopointer->anim[i].elem[j].anim_sound_index >= 0)
strcpy(genericpage->anim_sound_name[i][j], Sounds[objinfopointer->anim[i].elem[j].anim_sound_index].name);
else
strcpy(genericpage->anim_sound_name[i][j], "");
}
}
for (i = 0; i < MAX_WBS_PER_OBJ; i++) {
for (j = 0; j < MAX_WB_GUNPOINTS; j++) {
if (objinfopointer->static_wb && objinfopointer->static_wb[i].gp_weapon_index[j] >= 0)
strcpy(genericpage->weapon_name[i][j], Weapons[objinfopointer->static_wb[i].gp_weapon_index[j]].name);
else
strcpy(genericpage->weapon_name[i][j], "Laser");
}
}
}
// Loads a generic found in the net table file. It then allocs a generic and
// then calls SetAndLoadgeneric to actually load in any images/models associated
// with it
void mng_LoadNetGenericPage(CFILE *infile, bool overlay) {
mngs_generic_page genericpage{};
memset(&genericpage, 0, sizeof(mngs_generic_page));
if (mng_ReadNewGenericPage(infile, &genericpage)) {
int n = FindObjectIDName(genericpage.objinfo_struct.name);
if (n != -1) {
if (overlay) {
LOG_DEBUG.printf("OVERLAYING GENERIC %s", genericpage.objinfo_struct.name);
mng_FreePagetypePrimitives(PAGETYPE_GENERIC, genericpage.objinfo_struct.name, 0);
mng_AssignGenericPageToObjInfo(&genericpage, n);
}
// Free allocated memory
if (genericpage.objinfo_struct.description) {
mem_free(genericpage.objinfo_struct.description);
genericpage.objinfo_struct.description = nullptr;
}
return; // A weapon has already loaded this generic
}
int ret = mng_SetAndLoadGeneric(&genericpage, infile);
ASSERT(ret >= 0);
// Free allocated memory
if (genericpage.objinfo_struct.description) {
mem_free(genericpage.objinfo_struct.description);
genericpage.objinfo_struct.description = nullptr;
}
} else
LOG_ERROR.printf("Could not load genericpage named %s!", genericpage.objinfo_struct.name);
}
// Reads a generic page from a local table file. It then allocs a generic and
// loads any images/models associated with that generic
void mng_LoadLocalGenericPage(CFILE *infile) {
mngs_generic_page genericpage;
int ok = 0;
memset(&genericpage, 0, sizeof(mngs_generic_page));
if (mng_ReadNewGenericPage(infile, &genericpage)) {
// Check to see if this is a local copy that is supposed
// to go over a network copy (supersede the net copy)
int i = FindObjectIDName(genericpage.objinfo_struct.name);
if (i != -1) {
// Make sure we really have this page checked out
mngs_Pagelock pl;
strcpy(pl.name, genericpage.objinfo_struct.name);
pl.pagetype = PAGETYPE_GENERIC;
/*if (Network_up && Stand_alone==0)
{
int locked=mng_CheckIfPageOwned(&pl,TableUser);
if (locked!=1)
Int3(); // Your local vs net copies of the lock file do not match
}*/
ok = 1;
bool need_to_load_page = true;
if (Loading_addon_table != -1) {
AddOnTablefile *addon;
int tidx;
// see if we really need to load this page
// check to see if we already have loaded this page (because it was
// a dependancy of another)
addon = &AddOnDataTables[Loading_addon_table];
for (tidx = 0; tidx < addon->Num_addon_tracklocks; tidx++) {
if (addon->Addon_tracklocks[tidx].pagetype == PAGETYPE_GENERIC &&
!stricmp(addon->Addon_tracklocks[tidx].name, genericpage.objinfo_struct.name)) {
// found it!!
LOG_DEBUG.printf("GenericPage: %s previously loaded", genericpage.objinfo_struct.name);
need_to_load_page = false;
break;
}
}
}
if (need_to_load_page) {
mng_FreePagetypePrimitives(PAGETYPE_GENERIC, genericpage.objinfo_struct.name, 0);
mng_AssignGenericPageToObjInfo(&genericpage, i);
// For addon data
if (Loading_addon_table != -1) {
// this is an overlay of some sort..see which we are overlaying
int overlay = 1;
int addidx, tidx;
bool found = false;
for (addidx = Num_addon_tables - 1; addidx >= 0; addidx--) {
if (addidx == Loading_addon_table)
continue;
AddOnTablefile *addon = &AddOnDataTables[addidx];
// look for the page in this table file
for (tidx = 0; tidx < addon->Num_addon_tracklocks; tidx++) {
if (addon->Addon_tracklocks[tidx].pagetype == PAGETYPE_GENERIC &&
!stricmp(addon->Addon_tracklocks[tidx].name, genericpage.objinfo_struct.name)) {
// found it!!
found = true;
overlay = addidx + 2;
break;
}
}
if (found)
break;
}
mng_PushAddonPage(PAGETYPE_GENERIC, genericpage.objinfo_struct.name, overlay);
}
}
} else {
// This is a local generic that has never been checked in
if ((i = mng_SetAndLoadGeneric(&genericpage)) < 0)
ok = 0;
else
ok = 1;
// For addon data
if (ok && Loading_addon_table != -1)
mng_PushAddonPage(PAGETYPE_GENERIC, genericpage.objinfo_struct.name, 0);
}
ASSERT(ok == 1);
if (Loading_addon_table == -1)
mng_AllocTrackLock(genericpage.objinfo_struct.name, PAGETYPE_GENERIC);
} else {
LOG_ERROR.printf("Could not load genericpage named %s!", genericpage.objinfo_struct.name);
}
}
// First searches through the object index to see if the object is already
// loaded. If not, searches in the table file and loads it.
// Returns index of object found, -1 if not
int mng_GetGuaranteedGenericPage(char *name, CFILE *infile) {
int i;
mngs_generic_page page;
// See if it is in memory
i = FindObjectIDName(name);
if (i != -1)
return i;
// Not in memory. Load it from the table file. Start searching from the
// current spot in the open table file
int ret = mng_FindSpecificGenericPage(name, &page, infile ? infile->position : 0);
if (!ret)
return -1;
// We've found it in the table file, now load it.
ret = mng_SetAndLoadGeneric(&page);
ASSERT(ret >= 0);
if (Loading_addon_table != -1) {
// we're loading addon table pages, this will not overlay anything
mng_PushAddonPage(PAGETYPE_GENERIC, page.objinfo_struct.name, 0);
}
return ret;
}