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 | } |