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-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. |
||
18 | */ |
||
19 | |||
20 | /* |
||
21 | * |
||
22 | * Editor object functions. |
||
23 | * |
||
24 | */ |
||
25 | |||
26 | #include <stdio.h> |
||
27 | #include <stdlib.h> |
||
28 | #include <stdarg.h> |
||
29 | #include <math.h> |
||
30 | #include <string.h> |
||
31 | |||
32 | #include "inferno.h" |
||
33 | #include "segment.h" |
||
34 | #include "editor.h" |
||
35 | #include "editor/esegment.h" |
||
36 | #include "editor/eobject.h" |
||
37 | |||
38 | #include "objpage.h" |
||
39 | #include "maths.h" |
||
40 | #include "dxxerror.h" |
||
41 | #include "kdefs.h" |
||
42 | #include "object.h" |
||
43 | #include "robot.h" |
||
44 | #include "game.h" |
||
45 | #include "ai.h" |
||
46 | #include "bm.h" |
||
47 | #include "3d.h" // For g3_point_to_vec |
||
48 | #include "fvi.h" |
||
49 | |||
50 | #include "powerup.h" |
||
51 | #include "fuelcen.h" |
||
52 | #include "hostage.h" |
||
53 | #include "medrobot.h" |
||
54 | #include "player.h" |
||
55 | #include "gameseg.h" |
||
56 | #include "cntrlcen.h" |
||
57 | |||
58 | #include "compiler-range_for.h" |
||
59 | #include "segiter.h" |
||
60 | |||
61 | #define OBJ_SCALE (F1_0/2) |
||
62 | #define OBJ_DEL_SIZE (F1_0/2) |
||
63 | |||
64 | //returns the number of the first object in a segment, skipping the player |
||
65 | static objnum_t get_first_object(fvcobjptr &vcobjptr, const unique_segment &seg) |
||
66 | { |
||
67 | const auto id = seg.objects; |
||
68 | if (id == object_none) |
||
69 | return object_none; |
||
70 | auto &o = *vcobjptr(id); |
||
71 | if (&o == ConsoleObject) |
||
72 | return o.next; |
||
73 | return id; |
||
74 | } |
||
75 | |||
76 | //returns the number of the next object in a segment, skipping the player |
||
77 | static objnum_t get_next_object(const vmsegptr_t seg,objnum_t id) |
||
78 | { |
||
79 | auto &Objects = LevelUniqueObjectState.Objects; |
||
80 | auto &vcobjptr = Objects.vcptr; |
||
81 | auto &vmobjptr = Objects.vmptr; |
||
82 | if (id == object_none) |
||
83 | return get_first_object(vcobjptr, seg); |
||
84 | for (auto o = vmobjptr(id);;) |
||
85 | { |
||
86 | id = o->next; |
||
87 | if (id == object_none) |
||
88 | return get_first_object(vcobjptr, seg); |
||
89 | o = vmobjptr(id); |
||
90 | if (o != ConsoleObject) |
||
91 | return id; |
||
92 | } |
||
93 | } |
||
94 | |||
95 | |||
96 | //@@// ------------------------------------------------------------------------------------------------------ |
||
97 | //@@// this should be called whenever the current segment may have changed |
||
98 | //@@// If Cur_object_seg != Cursegp, then update various variables. |
||
99 | //@@// this used to be called update_due_to_new_segment() |
||
100 | //@@void ObjectUpdateCurrent(void) |
||
101 | //@@{ |
||
102 | //@@ if (Cur_object_seg != Cursegp) { |
||
103 | //@@ Cur_object_seg = Cursegp; |
||
104 | //@@ Cur_object_index = get_first_object(Cur_object_seg); |
||
105 | //@@ Update_flags |= UF_WORLD_CHANGED; |
||
106 | //@@ } |
||
107 | //@@ |
||
108 | //@@} |
||
109 | |||
110 | namespace dsx { |
||
111 | |||
112 | // ------------------------------------------------------------------------------------ |
||
113 | int place_object(const vmsegptridx_t segp, const vms_vector &object_pos, short object_type, short object_id) |
||
114 | { |
||
115 | vms_matrix seg_matrix; |
||
116 | |||
117 | med_extract_matrix_from_segment(segp, seg_matrix); |
||
118 | |||
119 | imobjptridx_t objnum = object_none; |
||
120 | auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models; |
||
121 | auto &Robot_info = LevelSharedRobotInfoState.Robot_info; |
||
122 | switch (object_type) |
||
123 | { |
||
124 | |||
125 | case OBJ_HOSTAGE: |
||
126 | { |
||
127 | objnum = obj_create(OBJ_HOSTAGE, -1, |
||
128 | segp,object_pos,&seg_matrix,HOSTAGE_SIZE, |
||
129 | CT_NONE,MT_NONE,RT_HOSTAGE); |
||
130 | |||
131 | if ( objnum == object_none) |
||
132 | return 0; |
||
133 | |||
134 | const vmobjptridx_t obj = objnum; |
||
135 | |||
136 | // Fill in obj->id and other hostage info |
||
137 | obj->id = 0; |
||
138 | |||
139 | obj->control_type = CT_POWERUP; |
||
140 | |||
141 | obj->rtype.vclip_info.vclip_num = Hostage_vclip_num[object_id]; |
||
142 | obj->rtype.vclip_info.frametime = Vclip[obj->rtype.vclip_info.vclip_num].frame_time; |
||
143 | obj->rtype.vclip_info.framenum = 0; |
||
144 | break; |
||
145 | } |
||
146 | case OBJ_ROBOT: |
||
147 | { |
||
148 | segnum_t hide_segment; |
||
149 | if (Markedsegp) |
||
150 | hide_segment = Markedsegp; |
||
151 | else |
||
152 | hide_segment = segment_none; |
||
153 | |||
154 | objnum = robot_create(object_id, segp, object_pos, |
||
155 | &seg_matrix, Polygon_models[Robot_info[object_id].model_num].rad, |
||
156 | Robot_info[object_id].attack_type ? |
||
157 | // robots which lunge forward to attack cannot have behavior type still. |
||
158 | ai_behavior::AIB_NORMAL : |
||
159 | ai_behavior::AIB_STILL, |
||
160 | hide_segment); |
||
161 | |||
162 | if ( objnum == object_none) |
||
163 | return 0; |
||
164 | |||
165 | const vmobjptridx_t obj = objnum; |
||
166 | |||
167 | //Set polygon-object-specific data |
||
168 | |||
169 | obj->rtype.pobj_info.model_num = Robot_info[get_robot_id(obj)].model_num; |
||
170 | obj->rtype.pobj_info.subobj_flags = 0; |
||
171 | |||
172 | //set Physics info |
||
173 | |||
174 | obj->mtype.phys_info.mass = Robot_info[get_robot_id(obj)].mass; |
||
175 | obj->mtype.phys_info.drag = Robot_info[get_robot_id(obj)].drag; |
||
176 | |||
177 | obj->mtype.phys_info.flags |= (PF_LEVELLING); |
||
178 | |||
179 | obj->shields = Robot_info[get_robot_id(obj)].strength; |
||
180 | break; |
||
181 | } |
||
182 | case OBJ_POWERUP: |
||
183 | { |
||
184 | objnum = obj_create(OBJ_POWERUP, object_id, |
||
185 | segp, object_pos, &seg_matrix, Powerup_info[object_id].size, |
||
186 | CT_POWERUP, MT_NONE, RT_POWERUP); |
||
187 | |||
188 | if ( objnum == object_none) |
||
189 | return 0; |
||
190 | |||
191 | const vmobjptridx_t obj = objnum; |
||
192 | |||
193 | //set powerup-specific data |
||
194 | |||
195 | obj->rtype.vclip_info.vclip_num = Powerup_info[get_powerup_id(obj)].vclip_num; |
||
196 | obj->rtype.vclip_info.frametime = Vclip[obj->rtype.vclip_info.vclip_num].play_time/Vclip[obj->rtype.vclip_info.vclip_num].num_frames; |
||
197 | obj->rtype.vclip_info.framenum = 0; |
||
198 | |||
199 | if (get_powerup_id(obj) == POW_VULCAN_WEAPON) |
||
200 | obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT; |
||
201 | else |
||
202 | obj->ctype.powerup_info.count = 1; |
||
203 | break; |
||
204 | } |
||
205 | case OBJ_CNTRLCEN: |
||
206 | { |
||
207 | objnum = obj_create(OBJ_CNTRLCEN, object_id, segp, object_pos, |
||
208 | &seg_matrix, Polygon_models[object_id].rad, |
||
209 | CT_CNTRLCEN, MT_NONE, RT_POLYOBJ); |
||
210 | |||
211 | if ( objnum == object_none) |
||
212 | return 0; |
||
213 | |||
214 | const vmobjptridx_t obj = objnum; |
||
215 | |||
216 | //Set polygon-object-specific data |
||
217 | obj->shields = 0; // stored in Reactor_strength or calculated |
||
218 | #if defined(DXX_BUILD_DESCENT_I) |
||
219 | obj->rtype.pobj_info.model_num = ObjId[object_type]; |
||
220 | #elif defined(DXX_BUILD_DESCENT_II) |
||
221 | obj->rtype.pobj_info.model_num = Reactors[object_id].model_num; |
||
222 | #endif |
||
223 | obj->rtype.pobj_info.subobj_flags = 0; |
||
224 | |||
225 | break; |
||
226 | } |
||
227 | case OBJ_PLAYER: { |
||
228 | objnum = obj_create(OBJ_PLAYER, object_id, segp, object_pos, |
||
229 | &seg_matrix, Polygon_models[Player_ship->model_num].rad, |
||
230 | CT_NONE, MT_PHYSICS, RT_POLYOBJ); |
||
231 | |||
232 | if ( objnum == object_none) |
||
233 | return 0; |
||
234 | |||
235 | const vmobjptridx_t obj = objnum; |
||
236 | |||
237 | //Set polygon-object-specific data |
||
238 | |||
239 | obj->rtype.pobj_info.model_num = Player_ship->model_num; |
||
240 | obj->rtype.pobj_info.subobj_flags = 0; |
||
241 | //for (i=0;i<MAX_SUBMODELS;i++) |
||
242 | // vm_angvec_zero(&obj->rtype.pobj_info.anim_angles[i]); |
||
243 | |||
244 | //set Physics info |
||
245 | |||
246 | vm_vec_zero(obj->mtype.phys_info.velocity); |
||
247 | obj->mtype.phys_info.mass = Player_ship->mass; |
||
248 | obj->mtype.phys_info.drag = Player_ship->drag; |
||
249 | obj->mtype.phys_info.flags |= PF_TURNROLL | PF_LEVELLING | PF_WIGGLE; |
||
250 | obj->shields = i2f(100); |
||
251 | break; |
||
252 | } |
||
253 | default: |
||
254 | return 0; |
||
255 | } |
||
256 | |||
257 | Cur_object_index = objnum; |
||
258 | //Cur_object_seg = Cursegp; |
||
259 | |||
260 | Update_flags |= UF_WORLD_CHANGED; |
||
261 | |||
262 | return 1; |
||
263 | } |
||
264 | |||
265 | // ------------------------------------------------------------------------------------------------------ |
||
266 | // Count number of player objects, return value. |
||
267 | static int compute_num_players(void) |
||
268 | { |
||
269 | auto &Objects = LevelUniqueObjectState.Objects; |
||
270 | auto &vcobjptr = Objects.vcptr; |
||
271 | int count = 0; |
||
272 | |||
273 | range_for (const auto &&objp, vcobjptr) |
||
274 | { |
||
275 | if (objp->type == OBJ_PLAYER) |
||
276 | count++; |
||
277 | } |
||
278 | |||
279 | return count; |
||
280 | |||
281 | } |
||
282 | |||
283 | int ObjectMakeCoop(void) |
||
284 | { |
||
285 | auto &Objects = LevelUniqueObjectState.Objects; |
||
286 | auto &vmobjptr = Objects.vmptr; |
||
287 | Assert(Cur_object_index != object_none); |
||
288 | Assert(Cur_object_index < MAX_OBJECTS); |
||
289 | // Assert(Objects[Cur_object_index.type == OBJ_PLAYER); |
||
290 | |||
291 | const auto &&objp = vmobjptr(Cur_object_index); |
||
292 | if (objp->type == OBJ_PLAYER) |
||
293 | { |
||
294 | objp->type = OBJ_COOP; |
||
295 | editor_status("You just made a player object COOPERATIVE"); |
||
296 | } else |
||
297 | editor_status("This is not a player object"); |
||
298 | |||
299 | return 1; |
||
300 | } |
||
301 | |||
302 | // ------------------------------------------------------------------------------------------------------ |
||
303 | // Place current object at center of current segment. |
||
304 | int ObjectPlaceObject(void) |
||
305 | { |
||
306 | auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); |
||
307 | auto &Objects = LevelUniqueObjectState.Objects; |
||
308 | auto &Vertices = LevelSharedVertexState.get_vertices(); |
||
309 | auto &vmobjptr = Objects.vmptr; |
||
310 | int old_cur_object_index; |
||
311 | int rval; |
||
312 | if (Cur_object_type == OBJ_PLAYER) |
||
313 | { |
||
314 | int num_players = compute_num_players(); |
||
315 | Assert(num_players <= MAX_MULTI_PLAYERS); |
||
316 | if (num_players > MAX_PLAYERS) |
||
317 | editor_status("You just placed a cooperative player object"); |
||
318 | if (num_players == MAX_MULTI_PLAYERS) { |
||
319 | editor_status_fmt("Can't place player object. Already %i players.", MAX_MULTI_PLAYERS); |
||
320 | return -1; |
||
321 | } |
||
322 | } |
||
323 | |||
324 | //update_due_to_new_segment(); |
||
325 | auto &vcvertptr = Vertices.vcptr; |
||
326 | const auto cur_object_loc = compute_segment_center(vcvertptr, Cursegp); |
||
327 | |||
328 | old_cur_object_index = Cur_object_index; |
||
329 | rval = place_object(Cursegp, cur_object_loc, Cur_object_type, Cur_object_id); |
||
330 | |||
331 | if (old_cur_object_index != Cur_object_index) |
||
332 | vmobjptr(Cur_object_index)->rtype.pobj_info.tmap_override = -1; |
||
333 | |||
334 | return rval; |
||
335 | |||
336 | } |
||
337 | |||
338 | // ------------------------------------------------------------------------------------------------------ |
||
339 | // Place current object at center of current segment. |
||
340 | int ObjectPlaceObjectTmap(void) |
||
341 | { |
||
342 | auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); |
||
343 | auto &Objects = LevelUniqueObjectState.Objects; |
||
344 | auto &Vertices = LevelSharedVertexState.get_vertices(); |
||
345 | int rval, old_cur_object_index; |
||
346 | //update_due_to_new_segment(); |
||
347 | auto &vcvertptr = Vertices.vcptr; |
||
348 | const auto cur_object_loc = compute_segment_center(vcvertptr, Cursegp); |
||
349 | |||
350 | old_cur_object_index = Cur_object_index; |
||
351 | rval = place_object(Cursegp, cur_object_loc, Cur_object_type, Cur_object_id); |
||
352 | |||
353 | if ((Cur_object_index != old_cur_object_index) && (Objects[Cur_object_index].render_type == RT_POLYOBJ)) |
||
354 | Objects[Cur_object_index].rtype.pobj_info.tmap_override = CurrentTexture; |
||
355 | else |
||
356 | editor_status("Unable to apply current texture map to this object."); |
||
357 | |||
358 | return rval; |
||
359 | } |
||
360 | |||
361 | // ------------------------------------------------------------------------------------------------------ |
||
362 | int ObjectSelectNextinSegment(void) |
||
363 | { |
||
364 | auto &Objects = LevelUniqueObjectState.Objects; |
||
365 | //update_due_to_new_segment(); |
||
366 | |||
367 | //Assert(Cur_object_seg == Cursegp); |
||
368 | |||
369 | const vmsegptr_t objsegp = Cursegp; |
||
370 | if (Cur_object_index == object_none) { |
||
371 | Cur_object_index = objsegp->objects; |
||
372 | } else { |
||
373 | if (Objects[Cur_object_index].segnum != Cursegp) |
||
374 | Cur_object_index = objsegp->objects; |
||
375 | } |
||
376 | |||
377 | |||
378 | //Debug: make sure current object is in current segment |
||
379 | objnum_t id; |
||
380 | for (id=objsegp->objects;(id != Cur_object_index) && (id != object_none);id=Objects[id].next); |
||
381 | Assert(id == Cur_object_index); //should have found object |
||
382 | |||
383 | // Select the next object, wrapping back to start if we are at the end of the linked list for this segment. |
||
384 | if (id != object_none) |
||
385 | Cur_object_index = get_next_object(objsegp,Cur_object_index); |
||
386 | |||
387 | Update_flags |= UF_WORLD_CHANGED; |
||
388 | |||
389 | return 1; |
||
390 | |||
391 | } |
||
392 | |||
393 | //Moves to next object in the mine, skipping the player |
||
394 | int ObjectSelectNextInMine() |
||
395 | { int i; |
||
396 | auto &Objects = LevelUniqueObjectState.Objects; |
||
397 | auto &vcobjptr = Objects.vcptr; |
||
398 | for (i=0;i<MAX_OBJECTS;i++) { |
||
399 | Cur_object_index++; |
||
400 | if (Cur_object_index>= MAX_OBJECTS ) Cur_object_index= 0; |
||
401 | |||
402 | const auto &&objp = vcobjptr(Cur_object_index); |
||
403 | if (objp->type != OBJ_NONE && objp != ConsoleObject) |
||
404 | { |
||
405 | Cursegp = imsegptridx(objp->segnum); |
||
406 | med_create_new_segment_from_cursegp(); |
||
407 | //Cur_object_seg = Cursegp; |
||
408 | return 1; |
||
409 | } |
||
410 | } |
||
411 | Cur_object_index = object_none; |
||
412 | |||
413 | Update_flags |= UF_WORLD_CHANGED; |
||
414 | |||
415 | return 0; |
||
416 | } |
||
417 | |||
418 | //Moves to next object in the mine, skipping the player |
||
419 | int ObjectSelectPrevInMine() |
||
420 | { int i; |
||
421 | auto &Objects = LevelUniqueObjectState.Objects; |
||
422 | auto &vcobjptr = Objects.vcptr; |
||
423 | for (i=0;i<MAX_OBJECTS;i++) { |
||
424 | if (!(Cur_object_index --)) |
||
425 | Cur_object_index = MAX_OBJECTS-1; |
||
426 | |||
427 | const auto &&objp = vcobjptr(Cur_object_index); |
||
428 | if (objp->type != OBJ_NONE && objp != ConsoleObject) |
||
429 | { |
||
430 | Cursegp = imsegptridx(objp->segnum); |
||
431 | med_create_new_segment_from_cursegp(); |
||
432 | //Cur_object_seg = Cursegp; |
||
433 | return 1; |
||
434 | } |
||
435 | } |
||
436 | Cur_object_index = object_none; |
||
437 | |||
438 | Update_flags |= UF_WORLD_CHANGED; |
||
439 | |||
440 | return 0; |
||
441 | } |
||
442 | |||
443 | // ------------------------------------------------------------------------------------------------------ |
||
444 | // Delete current object, if it exists. |
||
445 | // If it doesn't exist, reformat Matt's hard disk, even if he is in Boston. |
||
446 | int ObjectDelete(void) |
||
447 | { |
||
448 | auto &Objects = LevelUniqueObjectState.Objects; |
||
449 | auto &vmobjptridx = Objects.vmptridx; |
||
450 | |||
451 | if (Cur_object_index != object_none) { |
||
452 | auto delete_objnum = Cur_object_index; |
||
453 | ObjectSelectNextinSegment(); |
||
454 | |||
455 | obj_delete(LevelUniqueObjectState, Segments, vmobjptridx(delete_objnum)); |
||
456 | |||
457 | if (delete_objnum == Cur_object_index) |
||
458 | Cur_object_index = object_none; |
||
459 | |||
460 | Update_flags |= UF_WORLD_CHANGED; |
||
461 | } |
||
462 | |||
463 | return 1; |
||
464 | } |
||
465 | |||
466 | // ----------------------------------------------------------------------------------------------------------------- |
||
467 | // Object has moved to another segment, (or at least poked through). |
||
468 | // If still in mine, that is legal, so relink into new segment. |
||
469 | // Return value: 0 = in mine, 1 = not in mine |
||
470 | static int move_object_within_mine(fvmobjptr &vmobjptr, segment_array &Segments, fvcvertptr &vcvertptr, const vmobjptridx_t obj, const vms_vector &newpos) |
||
471 | { |
||
472 | range_for (const auto &&segp, Segments.vmptridx) |
||
473 | { |
||
474 | if (get_seg_masks(vcvertptr, obj->pos, segp, 0).centermask == 0) { |
||
475 | int fate; |
||
476 | fvi_info hit_info; |
||
477 | fvi_query fq; |
||
478 | |||
479 | // See if the radius pokes through any wall. |
||
480 | fq.p0 = &obj->pos; |
||
481 | fq.startseg = obj->segnum; |
||
482 | fq.p1 = &newpos; |
||
483 | fq.rad = obj->size; |
||
484 | fq.thisobjnum = object_none; |
||
485 | fq.ignore_obj_list.first = nullptr; |
||
486 | fq.flags = 0; |
||
487 | |||
488 | fate = find_vector_intersection(fq, hit_info); |
||
489 | |||
490 | if (fate != HIT_WALL) { |
||
491 | if (segp != obj->segnum) |
||
492 | obj_relink(vmobjptr, Segments.vmptr, obj, segp); |
||
493 | obj->pos = newpos; |
||
494 | return 0; |
||
495 | } |
||
496 | } |
||
497 | } |
||
498 | return 1; |
||
499 | } |
||
500 | |||
501 | // Return 0 if object is in expected segment, else return 1 |
||
502 | static int verify_object_seg(fvmobjptr &vmobjptr, segment_array &Segments, fvcvertptr &vcvertptr, const vmobjptridx_t objp, const vms_vector &newpos) |
||
503 | { |
||
504 | const auto &&result = get_seg_masks(vcvertptr, newpos, Segments.vcptr(objp->segnum), objp->size); |
||
505 | if (result.facemask == 0) |
||
506 | return 0; |
||
507 | else |
||
508 | return move_object_within_mine(vmobjptr, Segments, vcvertptr, objp, newpos); |
||
509 | } |
||
510 | |||
511 | namespace { |
||
512 | |||
513 | class extract_fvec_from_segment |
||
514 | { |
||
515 | public: |
||
516 | static vms_vector get(fvcvertptr &vcvertptr, const shared_segment &segp) |
||
517 | { |
||
518 | vms_vector v; |
||
519 | extract_forward_vector_from_segment(vcvertptr, segp, v); |
||
520 | return v; |
||
521 | } |
||
522 | }; |
||
523 | |||
524 | class extract_rvec_from_segment |
||
525 | { |
||
526 | public: |
||
527 | static vms_vector get(fvcvertptr &vcvertptr, const shared_segment &segp) |
||
528 | { |
||
529 | vms_vector v; |
||
530 | extract_right_vector_from_segment(vcvertptr, segp, v); |
||
531 | return v; |
||
532 | } |
||
533 | }; |
||
534 | |||
535 | class extract_uvec_from_segment |
||
536 | { |
||
537 | public: |
||
538 | static vms_vector get(fvcvertptr &vcvertptr, const shared_segment &segp) |
||
539 | { |
||
540 | vms_vector v; |
||
541 | extract_up_vector_from_segment(vcvertptr, segp, v); |
||
542 | return v; |
||
543 | } |
||
544 | }; |
||
545 | |||
546 | static int ObjectMoveFailed() |
||
547 | { |
||
548 | editor_status("No current object, cannot move."); |
||
549 | return 1; |
||
550 | } |
||
551 | |||
552 | static int ObjectMovePos(const vmobjptridx_t obj, vms_vector &&vec, int scale) |
||
553 | { |
||
554 | auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); |
||
555 | auto &Objects = LevelUniqueObjectState.Objects; |
||
556 | auto &Vertices = LevelSharedVertexState.get_vertices(); |
||
557 | auto &vmobjptr = Objects.vmptr; |
||
558 | vm_vec_normalize(vec); |
||
559 | const auto &&newpos = vm_vec_add(obj->pos, vm_vec_scale(vec, scale)); |
||
560 | auto &vcvertptr = Vertices.vcptr; |
||
561 | if (!verify_object_seg(vmobjptr, Segments, vcvertptr, obj, newpos)) |
||
562 | obj->pos = newpos; |
||
563 | Update_flags |= UF_WORLD_CHANGED; |
||
564 | return 1; |
||
565 | } |
||
566 | |||
567 | template <typename extract_type, int direction> |
||
568 | static int ObjectMove() |
||
569 | { |
||
570 | auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); |
||
571 | auto &Objects = LevelUniqueObjectState.Objects; |
||
572 | auto &Vertices = LevelSharedVertexState.get_vertices(); |
||
573 | auto &vmobjptridx = Objects.vmptridx; |
||
574 | const auto i = Cur_object_index; |
||
575 | if (i == object_none) |
||
576 | return ObjectMoveFailed(); |
||
577 | const auto &&obj = vmobjptridx(i); |
||
578 | auto &vcvertptr = Vertices.vcptr; |
||
579 | return ObjectMovePos(obj, extract_type::get(vcvertptr, vcsegptr(obj->segnum)), direction * OBJ_SCALE); |
||
580 | } |
||
581 | |||
582 | } |
||
583 | |||
584 | // ------------------------------------------------------------------------------------------------------ |
||
585 | int ObjectMoveForward(void) |
||
586 | { |
||
587 | return ObjectMove<extract_fvec_from_segment, 1>(); |
||
588 | } |
||
589 | |||
590 | // ------------------------------------------------------------------------------------------------------ |
||
591 | int ObjectMoveBack(void) |
||
592 | { |
||
593 | return ObjectMove<extract_fvec_from_segment, -1>(); |
||
594 | } |
||
595 | |||
596 | // ------------------------------------------------------------------------------------------------------ |
||
597 | int ObjectMoveLeft(void) |
||
598 | { |
||
599 | return ObjectMove<extract_rvec_from_segment, -1>(); |
||
600 | } |
||
601 | |||
602 | // ------------------------------------------------------------------------------------------------------ |
||
603 | int ObjectMoveRight(void) |
||
604 | { |
||
605 | return ObjectMove<extract_rvec_from_segment, 1>(); |
||
606 | } |
||
607 | |||
608 | // ------------------------------------------------------------------------------------------------------ |
||
609 | int ObjectSetDefault(void) |
||
610 | { |
||
611 | auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); |
||
612 | auto &Objects = LevelUniqueObjectState.Objects; |
||
613 | auto &Vertices = LevelSharedVertexState.get_vertices(); |
||
614 | auto &vmobjptr = Objects.vmptr; |
||
615 | //update_due_to_new_segment(); |
||
616 | |||
617 | if (Cur_object_index == object_none) { |
||
618 | editor_status("No current object, cannot move."); |
||
619 | return 1; |
||
620 | } |
||
621 | |||
622 | const auto &&objp = vmobjptr(Cur_object_index); |
||
623 | auto &vcvertptr = Vertices.vcptr; |
||
624 | compute_segment_center(vcvertptr, objp->pos, vcsegptr(objp->segnum)); |
||
625 | |||
626 | Update_flags |= UF_WORLD_CHANGED; |
||
627 | |||
628 | return 1; |
||
629 | } |
||
630 | |||
631 | |||
632 | // ------------------------------------------------------------------------------------------------------ |
||
633 | int ObjectMoveUp(void) |
||
634 | { |
||
635 | return ObjectMove<extract_uvec_from_segment, 1>(); |
||
636 | } |
||
637 | |||
638 | // ------------------------------------------------------------------------------------------------------ |
||
639 | int ObjectMoveDown(void) |
||
640 | { |
||
641 | return ObjectMove<extract_uvec_from_segment, -1>(); |
||
642 | } |
||
643 | |||
644 | // ------------------------------------------------------------------------------------------------------ |
||
645 | |||
646 | static int rotate_object(const vmobjptridx_t obj, int p, int b, int h) |
||
647 | { |
||
648 | vms_angvec ang; |
||
649 | // vm_extract_angles_matrix( &ang,&obj->orient); |
||
650 | |||
651 | // ang.p += p; |
||
652 | // ang.b += b; |
||
653 | // ang.h += h; |
||
654 | |||
655 | ang.p = p; |
||
656 | ang.b = b; |
||
657 | ang.h = h; |
||
658 | |||
659 | const auto rotmat = vm_angles_2_matrix(ang); |
||
660 | obj->orient = vm_matrix_x_matrix(obj->orient, rotmat); |
||
661 | // vm_angles_2_matrix(&obj->orient, &ang); |
||
662 | |||
663 | Update_flags |= UF_WORLD_CHANGED; |
||
664 | |||
665 | return 1; |
||
666 | } |
||
667 | |||
668 | static void reset_object(const vmobjptridx_t obj) |
||
669 | { |
||
670 | med_extract_matrix_from_segment(vcsegptr(obj->segnum), obj->orient); |
||
671 | } |
||
672 | |||
673 | int ObjectResetObject() |
||
674 | { |
||
675 | auto &Objects = LevelUniqueObjectState.Objects; |
||
676 | auto &vmobjptridx = Objects.vmptridx; |
||
677 | reset_object(vmobjptridx(Cur_object_index)); |
||
678 | |||
679 | Update_flags |= UF_WORLD_CHANGED; |
||
680 | |||
681 | return 1; |
||
682 | } |
||
683 | |||
684 | |||
685 | int ObjectFlipObject() |
||
686 | { |
||
687 | auto &Objects = LevelUniqueObjectState.Objects; |
||
688 | auto &vmobjptr = Objects.vmptr; |
||
689 | const auto m = &vmobjptr(Cur_object_index)->orient; |
||
690 | |||
691 | vm_vec_negate(m->uvec); |
||
692 | vm_vec_negate(m->rvec); |
||
693 | |||
694 | Update_flags |= UF_WORLD_CHANGED; |
||
695 | |||
696 | return 1; |
||
697 | } |
||
698 | |||
699 | template <int p, int b, int h> |
||
700 | int ObjectChangeRotation() |
||
701 | { |
||
702 | auto &Objects = LevelUniqueObjectState.Objects; |
||
703 | auto &vmobjptridx = Objects.vmptridx; |
||
704 | return rotate_object(vmobjptridx(Cur_object_index), p, b, h); |
||
705 | } |
||
706 | |||
707 | template int ObjectDecreaseBank(); |
||
708 | template int ObjectIncreaseBank(); |
||
709 | template int ObjectDecreasePitch(); |
||
710 | template int ObjectIncreasePitch(); |
||
711 | template int ObjectDecreaseHeading(); |
||
712 | template int ObjectIncreaseHeading(); |
||
713 | |||
714 | template int ObjectDecreaseBankBig(); |
||
715 | template int ObjectIncreaseBankBig(); |
||
716 | template int ObjectDecreasePitchBig(); |
||
717 | template int ObjectIncreasePitchBig(); |
||
718 | template int ObjectDecreaseHeadingBig(); |
||
719 | template int ObjectIncreaseHeadingBig(); |
||
720 | |||
721 | // ----------------------------------------------------------------------------------------------------- |
||
722 | // Move object around based on clicks in 2d screen. |
||
723 | // Slide an object parallel to the 2d screen, to a point on a vector. |
||
724 | // The vector is defined by a point on the 2d screen and the eye. |
||
725 | |||
726 | // V = vector from eye to 2d screen point. |
||
727 | // E = eye |
||
728 | // F = forward vector from eye |
||
729 | // O = 3-space location of object |
||
730 | |||
731 | // D = depth of object given forward vector F |
||
732 | // = (OE dot norm(F)) |
||
733 | |||
734 | // Must solve intersection of: |
||
735 | // E + tV ( equation of vector from eye through point on 2d screen) |
||
736 | // Fs + D ( equation of plane parallel to 2d screen, at depth D) |
||
737 | // = Fx(Ex + tVx) + Fy(Ey + tVy) + Fz(Ez + tVz) + D = 0 |
||
738 | // |
||
739 | // FxEx + FyEy + FzEz - D |
||
740 | // t = - ---------------------- |
||
741 | // VxFx + VyFy + VzFz |
||
742 | |||
743 | static void move_object_to_position(const vmobjptridx_t objp, const vms_vector &newpos) |
||
744 | { |
||
745 | auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); |
||
746 | auto &Objects = LevelUniqueObjectState.Objects; |
||
747 | auto &Vertices = LevelSharedVertexState.get_vertices(); |
||
748 | auto &vmobjptr = Objects.vmptr; |
||
749 | auto &vcvertptr = Vertices.vcptr; |
||
750 | if (get_seg_masks(vcvertptr, newpos, vcsegptr(objp->segnum), objp->size).facemask == 0) |
||
751 | { |
||
752 | objp->pos = newpos; |
||
753 | } else { |
||
754 | if (verify_object_seg(vmobjptr, Segments, vcvertptr, objp, newpos)) { |
||
755 | int fate; |
||
756 | object temp_viewer_obj; |
||
757 | fvi_query fq; |
||
758 | fvi_info hit_info; |
||
759 | |||
760 | temp_viewer_obj = *Viewer; |
||
761 | auto viewer_segnum = find_object_seg(LevelSharedSegmentState, LevelUniqueSegmentState, *Viewer); |
||
762 | temp_viewer_obj.segnum = viewer_segnum; |
||
763 | |||
764 | // If the viewer is outside the mine, get him in the mine! |
||
765 | if (viewer_segnum == segment_none) { |
||
766 | editor_status("Unable to move object, viewer not in mine. Aborting"); |
||
767 | return; |
||
768 | #if 0 |
||
769 | vms_vector last_outside_pos; |
||
770 | // While outside mine, move towards object |
||
771 | count = 0; |
||
772 | while (viewer_segnum == segment_none) { |
||
773 | vms_vector temp_vec; |
||
774 | |||
775 | last_outside_pos = temp_viewer_obj.pos; |
||
776 | |||
777 | vm_vec_avg(&temp_vec, &temp_viewer_obj.pos, newpos); |
||
778 | temp_viewer_obj.pos = temp_vec; |
||
779 | viewer_segnum = find_object_seg(&temp_viewer_obj); |
||
780 | temp_viewer_obj.segnum = viewer_segnum; |
||
781 | |||
782 | if (count > 5) { |
||
783 | editor_status("Unable to move object, can't get viewer in mine. Aborting"); |
||
784 | return; |
||
785 | } |
||
786 | } |
||
787 | |||
788 | count = 0; |
||
789 | // While inside mine, move away from object. |
||
790 | while (viewer_segnum != segment_none) { |
||
791 | |||
792 | vms_vector temp_vec; |
||
793 | |||
794 | vm_vec_avg(&temp_vec, &temp_viewer_obj.pos, &last_outside_pos); |
||
795 | temp_viewer_obj.pos = temp_vec; |
||
796 | update_object_seg(&temp_viewer_obj); |
||
797 | viewer_segnum = find_object_seg(&temp_viewer_obj); |
||
798 | temp_viewer_obj.segnum = viewer_segnum; |
||
799 | |||
800 | if (count > 5) { |
||
801 | editor_status("Unable to move object, can't get viewer back out of mine. Aborting"); |
||
802 | return; |
||
803 | } |
||
804 | } |
||
805 | #endif |
||
806 | } |
||
807 | |||
808 | fq.p0 = &temp_viewer_obj.pos; |
||
809 | fq.startseg = temp_viewer_obj.segnum; |
||
810 | fq.p1 = &newpos; |
||
811 | fq.rad = temp_viewer_obj.size; |
||
812 | fq.thisobjnum = object_none; |
||
813 | fq.ignore_obj_list.first = nullptr; |
||
814 | fq.flags = 0; |
||
815 | |||
816 | fate = find_vector_intersection(fq, hit_info); |
||
817 | if (fate == HIT_WALL) { |
||
818 | |||
819 | objp->pos = hit_info.hit_pnt; |
||
820 | const auto &&segp = find_object_seg(LevelSharedSegmentState, LevelUniqueSegmentState, objp); |
||
821 | if (segp != segment_none) |
||
822 | obj_relink(vmobjptr, vmsegptr, objp, segp); |
||
823 | } else { |
||
824 | editor_status("Attempted to move object out of mine. Object not moved."); |
||
825 | } |
||
826 | } |
||
827 | } |
||
828 | |||
829 | Update_flags |= UF_WORLD_CHANGED; |
||
830 | } |
||
831 | |||
832 | static void move_object_to_vector(const vms_vector &vec_through_screen, fix delta_distance) |
||
833 | { |
||
834 | auto &Objects = LevelUniqueObjectState.Objects; |
||
835 | auto &vmobjptridx = Objects.vmptridx; |
||
836 | const auto &&objp = vmobjptridx(Cur_object_index); |
||
837 | const auto result = vm_vec_scale_add(Viewer->pos, vec_through_screen, vm_vec_dist(Viewer->pos, objp->pos) + delta_distance); |
||
838 | move_object_to_position(objp, result); |
||
839 | } |
||
840 | |||
841 | } |
||
842 | |||
843 | static void move_object_to_mouse_click_delta(fix delta_distance) |
||
844 | { |
||
845 | short xcrd,ycrd; |
||
846 | vms_vector vec_through_screen; |
||
847 | |||
848 | if (Cur_object_index == object_none) { |
||
849 | editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!"); |
||
850 | return; |
||
851 | } |
||
852 | |||
853 | xcrd = GameViewBox->b1_drag_x1; |
||
854 | ycrd = GameViewBox->b1_drag_y1; |
||
855 | |||
856 | med_point_2_vec(&_canv_editor_game, vec_through_screen, xcrd, ycrd); |
||
857 | |||
858 | move_object_to_vector(vec_through_screen, delta_distance); |
||
859 | |||
860 | } |
||
861 | |||
862 | void move_object_to_mouse_click(void) |
||
863 | { |
||
864 | move_object_to_mouse_click_delta(0); |
||
865 | } |
||
866 | |||
867 | namespace dsx { |
||
868 | |||
869 | int ObjectMoveNearer(void) |
||
870 | { |
||
871 | auto &Objects = LevelUniqueObjectState.Objects; |
||
872 | auto &vcobjptr = Objects.vcptr; |
||
873 | if (Cur_object_index == object_none) { |
||
874 | editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!"); |
||
875 | return 1; |
||
876 | } |
||
877 | |||
878 | // move_object_to_mouse_click_delta(-4*F1_0); // Move four units closer to eye |
||
879 | |||
880 | const auto &&result = vm_vec_normalized(vm_vec_sub(vcobjptr(Cur_object_index)->pos, Viewer->pos)); |
||
881 | move_object_to_vector(result, -4*F1_0); |
||
882 | |||
883 | return 1; |
||
884 | } |
||
885 | |||
886 | int ObjectMoveFurther(void) |
||
887 | { |
||
888 | auto &Objects = LevelUniqueObjectState.Objects; |
||
889 | auto &vcobjptr = Objects.vcptr; |
||
890 | if (Cur_object_index == object_none) { |
||
891 | editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!"); |
||
892 | return 1; |
||
893 | } |
||
894 | |||
895 | // move_object_to_mouse_click_delta(+4*F1_0); // Move four units further from eye |
||
896 | |||
897 | const auto &&result = vm_vec_normalized(vm_vec_sub(vcobjptr(Cur_object_index)->pos, Viewer->pos)); |
||
898 | move_object_to_vector(result, 4*F1_0); |
||
899 | |||
900 | return 1; |
||
901 | } |
||
902 | |||
903 | } |