Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * Portions of this file are copyright Rebirth contributors and licensed as
3
 * described in COPYING.txt.
4
 * Portions of this file are copyright Parallax Software and licensed
5
 * according to the Parallax license below.
6
 * See COPYING.txt for license details.
7
 
8
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9
SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12
IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14
FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
/*
21
 *
22
 * Code for handling robots
23
 *
24
 */
25
 
26
 
27
#include "dxxerror.h"
28
#include "inferno.h"
29
#include "robot.h"
30
#include "object.h"
31
#include "polyobj.h"
32
#include "physfsx.h"
33
 
34
#include "compiler-range_for.h"
35
#include "partial_range.h"
36
 
37
namespace dcx {
38
unsigned N_robot_joints;
39
}
40
 
41
#if 0
42
static inline void PHYSFSX_writeAngleVec(PHYSFS_File *fp, const vms_angvec &v)
43
{
44
        PHYSFS_writeSLE16(fp, v.p);
45
        PHYSFS_writeSLE16(fp, v.b);
46
        PHYSFS_writeSLE16(fp, v.h);
47
}
48
#endif
49
 
50
namespace dsx {
51
 
52
//given an object and a gun number, return position in 3-space of gun
53
//fills in gun_point
54
void calc_gun_point(vms_vector &gun_point, const object_base &obj, unsigned gun_num)
55
{
56
        Assert(obj.render_type == RT_POLYOBJ || obj.render_type == RT_MORPH);
57
        assert(get_robot_id(obj) < LevelSharedRobotInfoState.N_robot_types);
58
 
59
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
60
        const auto &r = Robot_info[get_robot_id(obj)];
61
        auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
62
        const auto &pm = Polygon_models[r.model_num];
63
 
64
        if (gun_num >= r.n_guns)
65
        {
66
                gun_num = 0;
67
        }
68
 
69
        auto pnt = r.gun_points[gun_num];
70
 
71
        //instance up the tree for this gun
72
        auto &anim_angles = obj.rtype.pobj_info.anim_angles;
73
        for (unsigned mn = r.gun_submodels[gun_num]; mn != 0; mn = pm.submodel_parents[mn])
74
        {
75
                const auto &&m = vm_transposed_matrix(vm_angles_2_matrix(anim_angles[mn]));
76
                const auto tpnt = vm_vec_rotate(pnt,m);
77
 
78
                vm_vec_add(pnt, tpnt, pm.submodel_offsets[mn]);
79
        }
80
 
81
        //now instance for the entire object
82
 
83
        const auto &&m = vm_transposed_matrix(obj.orient);
84
        vm_vec_rotate(gun_point,pnt,m);
85
        vm_vec_add2(gun_point, obj.pos);
86
}
87
 
88
//fills in ptr to list of joints, and returns the number of joints in list
89
//takes the robot type (object id), gun number, and desired state
90
partial_range_t<const jointpos *> robot_get_anim_state(const d_level_shared_robot_info_state::d_robot_info_array &robot_info, const std::array<jointpos, MAX_ROBOT_JOINTS> &robot_joints, const unsigned robot_type, const unsigned gun_num, const unsigned state)
91
{
92
        auto &rirt = robot_info[robot_type];
93
        assert(gun_num <= rirt.n_guns);
94
        auto &as = rirt.anim_states[gun_num][state];
95
        const unsigned o = as.offset;
96
        return partial_range(robot_joints, o, o + as.n_joints);
97
}
98
 
99
#ifndef NDEBUG
100
//for test, set a robot to a specific state
101
static void set_robot_state(object_base &obj, const unsigned state) __attribute_used;
102
static void set_robot_state(object_base &obj, const unsigned state)
103
{
104
        auto &Robot_joints = LevelSharedRobotJointState.Robot_joints;
105
        int g,j,jo;
106
        jointlist *jl;
107
 
108
        assert(obj.type == OBJ_ROBOT);
109
 
110
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
111
        auto &ri = Robot_info[get_robot_id(obj)];
112
 
113
        for (g = 0; g < ri.n_guns + 1; g++)
114
        {
115
 
116
                jl = &ri.anim_states[g][state];
117
 
118
                jo = jl->offset;
119
 
120
                for (j=0;j<jl->n_joints;j++,jo++) {
121
                        int jn;
122
 
123
                        jn = Robot_joints[jo].jointnum;
124
 
125
                        obj.rtype.pobj_info.anim_angles[jn] = Robot_joints[jo].angles;
126
                }
127
        }
128
}
129
#endif
130
 
131
//set the animation angles for this robot.  Gun fields of robot info must
132
//be filled in.
133
void robot_set_angles(robot_info *r,polymodel *pm,std::array<std::array<vms_angvec, MAX_SUBMODELS>, N_ANIM_STATES> &angs)
134
{
135
        auto &Robot_joints = LevelSharedRobotJointState.Robot_joints;
136
        int g,state;
137
        std::array<int, MAX_SUBMODELS> gun_nums;                        //which gun each submodel is part of
138
 
139
        range_for (auto &m, partial_range(gun_nums, 1u, pm->n_models))
140
                m = r->n_guns;          //assume part of body...
141
 
142
        gun_nums[0] = -1;               //body never animates, at least for now
143
 
144
        for (g=0;g<r->n_guns;g++) {
145
                auto m = r->gun_submodels[g];
146
 
147
                while (m != 0) {
148
                        gun_nums[m] = g;                                //...unless we find it in a gun
149
                        m = pm->submodel_parents[m];
150
                }
151
        }
152
 
153
        for (g=0;g<r->n_guns+1;g++) {
154
 
155
                for (state=0;state<N_ANIM_STATES;state++) {
156
 
157
                        r->anim_states[g][state].n_joints = 0;
158
                        r->anim_states[g][state].offset = N_robot_joints;
159
 
160
                        for (unsigned m = 0; m < pm->n_models; ++m)
161
                        {
162
                                if (gun_nums[m] == g) {
163
                                        Robot_joints[N_robot_joints].jointnum = m;
164
                                        Robot_joints[N_robot_joints].angles = angs[state][m];
165
                                        r->anim_states[g][state].n_joints++;
166
                                        N_robot_joints++;
167
                                        Assert(N_robot_joints < MAX_ROBOT_JOINTS);
168
                                }
169
                        }
170
                }
171
        }
172
 
173
}
174
 
175
}
176
 
177
/*
178
 * reads n jointlist structs from a PHYSFS_File
179
 */
180
static void jointlist_read(PHYSFS_File *fp, std::array<jointlist, N_ANIM_STATES> &jl)
181
{
182
        range_for (auto &i, jl)
183
        {
184
                i.n_joints = PHYSFSX_readShort(fp);
185
                i.offset = PHYSFSX_readShort(fp);
186
                if (!i.n_joints)
187
                        /* The custom campaign `Descent 2: Enemy Vignettes` has
188
                         * custom robots with invalid joints.  These joints have
189
                         * invalid offsets, but `n_joints` of 0.  This makes the
190
                         * invalid data easy to detect and clean.
191
                         *
192
                         * When the number of joints is zero, discard the loaded
193
                         * offset and set it to 0, which will always be in range.
194
                         */
195
                        i.offset = 0;
196
        }
197
}
198
 
199
namespace dsx {
200
 
201
imobjptridx_t robot_create(const unsigned id, const vmsegptridx_t segnum, const vms_vector &pos, const vms_matrix *const orient, const fix size, const ai_behavior behavior, const imsegidx_t hide_segment)
202
{
203
        const auto &&objp = obj_create(OBJ_ROBOT, id, segnum, pos, orient, size, CT_AI, MT_PHYSICS, RT_POLYOBJ);
204
        if (objp)
205
                init_ai_object(objp, behavior, hide_segment);
206
        return objp;
207
}
208
 
209
/*
210
 * reads n robot_info structs from a PHYSFS_File
211
 */
212
void robot_info_read(PHYSFS_File *fp, robot_info &ri)
213
{
214
        ri.model_num = PHYSFSX_readInt(fp);
215
#if defined(DXX_BUILD_DESCENT_I)
216
        ri.n_guns = PHYSFSX_readInt(fp);
217
#endif
218
        range_for (auto &j, ri.gun_points)
219
                PHYSFSX_readVector(fp, j);
220
        range_for (auto &j, ri.gun_submodels)
221
                j = PHYSFSX_readByte(fp);
222
 
223
        ri.exp1_vclip_num = PHYSFSX_readShort(fp);
224
        ri.exp1_sound_num = PHYSFSX_readShort(fp);
225
 
226
        ri.exp2_vclip_num = PHYSFSX_readShort(fp);
227
        ri.exp2_sound_num = PHYSFSX_readShort(fp);
228
 
229
#if defined(DXX_BUILD_DESCENT_I)
230
        ri.weapon_type = static_cast<weapon_id_type>(PHYSFSX_readShort(fp));
231
#elif defined(DXX_BUILD_DESCENT_II)
232
        ri.weapon_type = static_cast<weapon_id_type>(PHYSFSX_readByte(fp));
233
        ri.weapon_type2 = static_cast<weapon_id_type>(PHYSFSX_readByte(fp));
234
        ri.n_guns = PHYSFSX_readByte(fp);
235
#endif
236
        ri.contains_id = PHYSFSX_readByte(fp);
237
 
238
        ri.contains_count = PHYSFSX_readByte(fp);
239
        ri.contains_prob = PHYSFSX_readByte(fp);
240
        ri.contains_type = PHYSFSX_readByte(fp);
241
#if defined(DXX_BUILD_DESCENT_I)
242
        ri.score_value = PHYSFSX_readInt(fp);
243
#elif defined(DXX_BUILD_DESCENT_II)
244
        ri.kamikaze = PHYSFSX_readByte(fp);
245
 
246
        ri.score_value = PHYSFSX_readShort(fp);
247
        ri.badass = PHYSFSX_readByte(fp);
248
        ri.energy_drain = PHYSFSX_readByte(fp);
249
#endif
250
 
251
        ri.lighting = PHYSFSX_readFix(fp);
252
        ri.strength = PHYSFSX_readFix(fp);
253
 
254
        ri.mass = PHYSFSX_readFix(fp);
255
        ri.drag = PHYSFSX_readFix(fp);
256
 
257
        range_for (auto &j, ri.field_of_view)
258
                j = PHYSFSX_readFix(fp);
259
        range_for (auto &j, ri.firing_wait)
260
                j = PHYSFSX_readFix(fp);
261
#if defined(DXX_BUILD_DESCENT_II)
262
        range_for (auto &j, ri.firing_wait2)
263
                j = PHYSFSX_readFix(fp);
264
#endif
265
        range_for (auto &j, ri.turn_time)
266
                j = PHYSFSX_readFix(fp);
267
#if defined(DXX_BUILD_DESCENT_I)
268
        for (unsigned j = 0; j < NDL * 2; j++)
269
                        PHYSFSX_readFix(fp);
270
#endif
271
        range_for (auto &j, ri.max_speed)
272
                j = PHYSFSX_readFix(fp);
273
        range_for (auto &j, ri.circle_distance)
274
                j = PHYSFSX_readFix(fp);
275
        range_for (auto &j, ri.rapidfire_count)
276
                j = PHYSFSX_readByte(fp);
277
        range_for (auto &j, ri.evade_speed)
278
                j = PHYSFSX_readByte(fp);
279
 
280
        ri.cloak_type = PHYSFSX_readByte(fp);
281
        ri.attack_type = PHYSFSX_readByte(fp);
282
#if defined(DXX_BUILD_DESCENT_I)
283
        ri.boss_flag = PHYSFSX_readByte(fp);
284
#endif
285
 
286
        ri.see_sound = PHYSFSX_readByte(fp);
287
        ri.attack_sound = PHYSFSX_readByte(fp);
288
        ri.claw_sound = PHYSFSX_readByte(fp);
289
#if defined(DXX_BUILD_DESCENT_II)
290
        ri.taunt_sound = PHYSFSX_readByte(fp);
291
 
292
        ri.boss_flag = PHYSFSX_readByte(fp);
293
        ri.companion = PHYSFSX_readByte(fp);
294
        ri.smart_blobs = PHYSFSX_readByte(fp);
295
        ri.energy_blobs = PHYSFSX_readByte(fp);
296
 
297
        ri.thief = PHYSFSX_readByte(fp);
298
        ri.pursuit = PHYSFSX_readByte(fp);
299
        ri.lightcast = PHYSFSX_readByte(fp);
300
        ri.death_roll = PHYSFSX_readByte(fp);
301
 
302
        ri.flags = PHYSFSX_readByte(fp);
303
        std::array<char, 3> pad;
304
        PHYSFS_read(fp, pad, pad.size(), 1);
305
 
306
        ri.deathroll_sound = PHYSFSX_readByte(fp);
307
        ri.glow = PHYSFSX_readByte(fp);
308
        ri.behavior = static_cast<ai_behavior>(PHYSFSX_readByte(fp));
309
        ri.aim = PHYSFSX_readByte(fp);
310
#endif
311
 
312
        range_for (auto &j, ri.anim_states)
313
                jointlist_read(fp, j);
314
 
315
        ri.always_0xabcd = PHYSFSX_readInt(fp);
316
}
317
}
318
 
319
/*
320
 * reads n jointpos structs from a PHYSFS_File
321
 */
322
void jointpos_read(PHYSFS_File *fp, jointpos &jp)
323
{
324
        jp.jointnum = PHYSFSX_readShort(fp);
325
        PHYSFSX_readAngleVec(&jp.angles, fp);
326
}
327
 
328
#if 0
329
void jointpos_write(PHYSFS_File *fp, const jointpos &jp)
330
{
331
        PHYSFS_writeSLE16(fp, jp.jointnum);
332
        PHYSFSX_writeAngleVec(fp, jp.angles);
333
}
334
#endif