dxx-rebirth/similar/editor/eobject.cpp

897 lines
26 KiB
C++

/*
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING.txt.
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below.
* See COPYING.txt for license details.
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* Editor object functions.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include <string.h>
#include "inferno.h"
#include "segment.h"
#include "editor.h"
#include "editor/esegment.h"
#include "editor/eobject.h"
#include "maths.h"
#include "dxxerror.h"
#include "kdefs.h"
#include "object.h"
#include "robot.h"
#include "game.h"
#include "bm.h"
#include "3d.h" // For g3_point_to_vec
#include "fvi.h"
#include "vclip.h"
#include "powerup.h"
#include "hostage.h"
#include "player.h"
#include "gameseg.h"
#include "cntrlcen.h"
#include "compiler-range_for.h"
#include "d_levelstate.h"
#define OBJ_SCALE (F1_0/2)
#define OBJ_DEL_SIZE (F1_0/2)
//returns the number of the first object in a segment, skipping the player
static objnum_t get_first_object(fvcobjptr &vcobjptr, const unique_segment &seg)
{
const auto id = seg.objects;
if (id == object_none)
return object_none;
auto &o = *vcobjptr(id);
if (&o == ConsoleObject)
return o.next;
return id;
}
//returns the number of the next object in a segment, skipping the player
static objnum_t get_next_object(const unique_segment &seg, objnum_t id)
{
auto &Objects = LevelUniqueObjectState.Objects;
auto &vcobjptr = Objects.vcptr;
auto &vmobjptr = Objects.vmptr;
if (id == object_none)
return get_first_object(vcobjptr, seg);
for (auto o = vmobjptr(id);;)
{
id = o->next;
if (id == object_none)
return get_first_object(vcobjptr, seg);
o = vmobjptr(id);
if (o != ConsoleObject)
return id;
}
}
//@@// ------------------------------------------------------------------------------------------------------
//@@// this should be called whenever the current segment may have changed
//@@// If Cur_object_seg != Cursegp, then update various variables.
//@@// this used to be called update_due_to_new_segment()
//@@void ObjectUpdateCurrent(void)
//@@{
//@@ if (Cur_object_seg != Cursegp) {
//@@ Cur_object_seg = Cursegp;
//@@ Cur_object_index = get_first_object(Cur_object_seg);
//@@ Update_flags |= UF_WORLD_CHANGED;
//@@ }
//@@
//@@}
namespace dsx {
// ------------------------------------------------------------------------------------
int place_object(d_level_unique_object_state &LevelUniqueObjectState, const d_level_shared_polygon_model_state &LevelSharedPolygonModelState, const d_robot_info_array &Robot_info, const d_level_shared_segment_state &LevelSharedSegmentState, d_level_unique_segment_state &LevelUniqueSegmentState, const vmsegptridx_t segp, const vms_vector &object_pos, short object_type, short object_id)
{
vms_matrix seg_matrix;
med_extract_matrix_from_segment(segp, seg_matrix);
imobjptridx_t objnum = object_none;
auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
switch (object_type)
{
case OBJ_HOSTAGE:
{
objnum = obj_create(LevelUniqueObjectState, LevelSharedSegmentState, LevelUniqueSegmentState, OBJ_HOSTAGE, -1,
segp,object_pos,&seg_matrix,HOSTAGE_SIZE,
object::control_type::None, object::movement_type::None,RT_HOSTAGE);
if ( objnum == object_none)
return 0;
const vmobjptridx_t obj = objnum;
// Fill in obj->id and other hostage info
obj->id = 0;
obj->control_source = object::control_type::powerup;
obj->rtype.vclip_info.vclip_num = Hostage_vclip_num[object_id];
obj->rtype.vclip_info.frametime = Vclip[obj->rtype.vclip_info.vclip_num].frame_time;
obj->rtype.vclip_info.framenum = 0;
break;
}
case OBJ_ROBOT:
{
segnum_t hide_segment;
if (Markedsegp)
hide_segment = Markedsegp;
else
hide_segment = segment_none;
objnum = robot_create(Robot_info, object_id, segp, object_pos,
&seg_matrix, Polygon_models[Robot_info[object_id].model_num].rad,
Robot_info[object_id].attack_type ?
// robots which lunge forward to attack cannot have behavior type still.
ai_behavior::AIB_NORMAL :
ai_behavior::AIB_STILL,
hide_segment);
if ( objnum == object_none)
return 0;
const vmobjptridx_t obj = objnum;
//Set polygon-object-specific data
obj->rtype.pobj_info.model_num = Robot_info[get_robot_id(obj)].model_num;
obj->rtype.pobj_info.subobj_flags = 0;
//set Physics info
obj->mtype.phys_info.mass = Robot_info[get_robot_id(obj)].mass;
obj->mtype.phys_info.drag = Robot_info[get_robot_id(obj)].drag;
obj->mtype.phys_info.flags |= (PF_LEVELLING);
obj->shields = Robot_info[get_robot_id(obj)].strength;
break;
}
case OBJ_POWERUP:
{
objnum = obj_create(LevelUniqueObjectState, LevelSharedSegmentState, LevelUniqueSegmentState, OBJ_POWERUP, object_id,
segp, object_pos, &seg_matrix, Powerup_info[object_id].size,
object::control_type::powerup, object::movement_type::None, RT_POWERUP);
if ( objnum == object_none)
return 0;
const vmobjptridx_t obj = objnum;
//set powerup-specific data
obj->rtype.vclip_info.vclip_num = Powerup_info[get_powerup_id(obj)].vclip_num;
obj->rtype.vclip_info.frametime = Vclip[obj->rtype.vclip_info.vclip_num].play_time/Vclip[obj->rtype.vclip_info.vclip_num].num_frames;
obj->rtype.vclip_info.framenum = 0;
if (get_powerup_id(obj) == POW_VULCAN_WEAPON)
obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
else
obj->ctype.powerup_info.count = 1;
break;
}
case OBJ_CNTRLCEN:
{
objnum = obj_create(LevelUniqueObjectState, LevelSharedSegmentState, LevelUniqueSegmentState, OBJ_CNTRLCEN, object_id, segp, object_pos,
&seg_matrix, Polygon_models[object_id].rad,
object::control_type::cntrlcen, object::movement_type::None, RT_POLYOBJ);
if ( objnum == object_none)
return 0;
const vmobjptridx_t obj = objnum;
//Set polygon-object-specific data
obj->shields = 0; // stored in Reactor_strength or calculated
#if defined(DXX_BUILD_DESCENT_I)
obj->rtype.pobj_info.model_num = ObjId[object_type];
#elif defined(DXX_BUILD_DESCENT_II)
obj->rtype.pobj_info.model_num = Reactors[object_id].model_num;
#endif
obj->rtype.pobj_info.subobj_flags = 0;
break;
}
case OBJ_PLAYER: {
objnum = obj_create(LevelUniqueObjectState, LevelSharedSegmentState, LevelUniqueSegmentState, OBJ_PLAYER, object_id, segp, object_pos,
&seg_matrix, Polygon_models[Player_ship->model_num].rad,
object::control_type::None, object::movement_type::physics, RT_POLYOBJ);
if ( objnum == object_none)
return 0;
const vmobjptridx_t obj = objnum;
//Set polygon-object-specific data
obj->rtype.pobj_info.model_num = Player_ship->model_num;
obj->rtype.pobj_info.subobj_flags = 0;
//for (i=0;i<MAX_SUBMODELS;i++)
// vm_angvec_zero(&obj->rtype.pobj_info.anim_angles[i]);
//set Physics info
vm_vec_zero(obj->mtype.phys_info.velocity);
obj->mtype.phys_info.mass = Player_ship->mass;
obj->mtype.phys_info.drag = Player_ship->drag;
obj->mtype.phys_info.flags |= PF_TURNROLL | PF_LEVELLING | PF_WIGGLE;
obj->shields = i2f(100);
break;
}
default:
return 0;
}
Cur_object_index = objnum;
//Cur_object_seg = Cursegp;
Update_flags |= UF_WORLD_CHANGED;
return 1;
}
// ------------------------------------------------------------------------------------------------------
// Count number of player objects, return value.
static int compute_num_players(void)
{
auto &Objects = LevelUniqueObjectState.Objects;
auto &vcobjptr = Objects.vcptr;
int count = 0;
range_for (const auto &&objp, vcobjptr)
{
if (objp->type == OBJ_PLAYER)
count++;
}
return count;
}
int ObjectMakeCoop(void)
{
auto &Objects = LevelUniqueObjectState.Objects;
auto &vmobjptr = Objects.vmptr;
Assert(Cur_object_index != object_none);
Assert(Cur_object_index < MAX_OBJECTS);
// Assert(Objects[Cur_object_index.type == OBJ_PLAYER);
const auto &&objp = vmobjptr(Cur_object_index);
if (objp->type == OBJ_PLAYER)
{
objp->type = OBJ_COOP;
editor_status("You just made a player object COOPERATIVE");
} else
editor_status("This is not a player object");
return 1;
}
// ------------------------------------------------------------------------------------------------------
// Place current object at center of current segment.
int ObjectPlaceObject(void)
{
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
auto &Objects = LevelUniqueObjectState.Objects;
auto &Vertices = LevelSharedVertexState.get_vertices();
auto &vmobjptr = Objects.vmptr;
int old_cur_object_index;
if (Cur_object_type == OBJ_PLAYER)
{
int num_players = compute_num_players();
Assert(num_players <= MAX_MULTI_PLAYERS);
if (num_players > MAX_PLAYERS)
editor_status("You just placed a cooperative player object");
if (num_players == MAX_MULTI_PLAYERS) {
editor_status_fmt("Can't place player object. Already %i players.", MAX_MULTI_PLAYERS);
return -1;
}
}
//update_due_to_new_segment();
auto &vcvertptr = Vertices.vcptr;
const auto cur_object_loc = compute_segment_center(vcvertptr, Cursegp);
old_cur_object_index = Cur_object_index;
const auto rval = place_object(LevelUniqueObjectState, LevelSharedPolygonModelState, LevelSharedRobotInfoState.Robot_info, LevelSharedSegmentState, LevelUniqueSegmentState, Cursegp, cur_object_loc, Cur_object_type, Cur_object_id);
if (old_cur_object_index != Cur_object_index)
vmobjptr(Cur_object_index)->rtype.pobj_info.tmap_override = -1;
return rval;
}
// ------------------------------------------------------------------------------------------------------
// Place current object at center of current segment.
int ObjectPlaceObjectTmap(void)
{
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
auto &Objects = LevelUniqueObjectState.Objects;
auto &Vertices = LevelSharedVertexState.get_vertices();
int old_cur_object_index;
//update_due_to_new_segment();
auto &vcvertptr = Vertices.vcptr;
const auto cur_object_loc = compute_segment_center(vcvertptr, Cursegp);
old_cur_object_index = Cur_object_index;
const auto rval = place_object(LevelUniqueObjectState, LevelSharedPolygonModelState, LevelSharedRobotInfoState.Robot_info, LevelSharedSegmentState, LevelUniqueSegmentState, Cursegp, cur_object_loc, Cur_object_type, Cur_object_id);
if ((Cur_object_index != old_cur_object_index) && (Objects[Cur_object_index].render_type == RT_POLYOBJ))
Objects[Cur_object_index].rtype.pobj_info.tmap_override = CurrentTexture;
else
editor_status("Unable to apply current texture map to this object.");
return rval;
}
// ------------------------------------------------------------------------------------------------------
int ObjectSelectNextinSegment(void)
{
auto &Objects = LevelUniqueObjectState.Objects;
//update_due_to_new_segment();
//Assert(Cur_object_seg == Cursegp);
const unique_segment &objuseg = Cursegp;
if (Cur_object_index == object_none) {
Cur_object_index = objuseg.objects;
} else {
if (Objects[Cur_object_index].segnum != Cursegp)
Cur_object_index = objuseg.objects;
}
//Debug: make sure current object is in current segment
objnum_t id;
for (id = objuseg.objects; id != Cur_object_index && id != object_none; id = Objects[id].next)
{
}
Assert(id == Cur_object_index); //should have found object
// Select the next object, wrapping back to start if we are at the end of the linked list for this segment.
if (id != object_none)
Cur_object_index = get_next_object(objuseg, Cur_object_index);
Update_flags |= UF_WORLD_CHANGED;
return 1;
}
//Moves to next object in the mine, skipping the player
int ObjectSelectNextInMine()
{ int i;
auto &Objects = LevelUniqueObjectState.Objects;
auto &vcobjptr = Objects.vcptr;
for (i=0;i<MAX_OBJECTS;i++) {
Cur_object_index++;
if (Cur_object_index>= MAX_OBJECTS ) Cur_object_index= 0;
const auto &&objp = vcobjptr(Cur_object_index);
if (objp->type != OBJ_NONE && objp != ConsoleObject)
{
Cursegp = imsegptridx(objp->segnum);
med_create_new_segment_from_cursegp();
//Cur_object_seg = Cursegp;
return 1;
}
}
Cur_object_index = object_none;
Update_flags |= UF_WORLD_CHANGED;
return 0;
}
//Moves to next object in the mine, skipping the player
int ObjectSelectPrevInMine()
{ int i;
auto &Objects = LevelUniqueObjectState.Objects;
auto &vcobjptr = Objects.vcptr;
for (i=0;i<MAX_OBJECTS;i++) {
if (!(Cur_object_index --))
Cur_object_index = MAX_OBJECTS-1;
const auto &&objp = vcobjptr(Cur_object_index);
if (objp->type != OBJ_NONE && objp != ConsoleObject)
{
Cursegp = imsegptridx(objp->segnum);
med_create_new_segment_from_cursegp();
//Cur_object_seg = Cursegp;
return 1;
}
}
Cur_object_index = object_none;
Update_flags |= UF_WORLD_CHANGED;
return 0;
}
// ------------------------------------------------------------------------------------------------------
// Delete current object, if it exists.
// If it doesn't exist, reformat Matt's hard disk, even if he is in Boston.
int ObjectDelete(void)
{
auto &Objects = LevelUniqueObjectState.Objects;
auto &vmobjptridx = Objects.vmptridx;
if (Cur_object_index != object_none) {
auto delete_objnum = Cur_object_index;
ObjectSelectNextinSegment();
obj_delete(LevelUniqueObjectState, Segments, vmobjptridx(delete_objnum));
if (delete_objnum == Cur_object_index)
Cur_object_index = object_none;
Update_flags |= UF_WORLD_CHANGED;
}
return 1;
}
// -----------------------------------------------------------------------------------------------------------------
// Object has moved to another segment, (or at least poked through).
// If still in mine, that is legal, so relink into new segment.
// Return value: 0 = in mine, 1 = not in mine
static int move_object_within_mine(fvmobjptr &vmobjptr, segment_array &Segments, fvcvertptr &vcvertptr, const vmobjptridx_t obj, const vms_vector &newpos)
{
range_for (const auto &&segp, Segments.vmptridx)
{
if (get_seg_masks(vcvertptr, obj->pos, segp, 0).centermask == sidemask_t{})
{
fvi_info hit_info;
// See if the radius pokes through any wall.
const auto fate = find_vector_intersection(fvi_query{
obj->pos,
newpos,
fvi_query::unused_ignore_obj_list,
fvi_query::unused_LevelUniqueObjectState,
fvi_query::unused_Robot_info,
0,
object_none,
}, obj->segnum, obj->size, hit_info);
if (fate != fvi_hit_type::Wall)
{
if (segp != obj->segnum)
obj_relink(vmobjptr, Segments.vmptr, obj, segp);
obj->pos = newpos;
return 0;
}
}
}
return 1;
}
// Return 0 if object is in expected segment, else return 1
static int verify_object_seg(fvmobjptr &vmobjptr, segment_array &Segments, fvcvertptr &vcvertptr, const vmobjptridx_t objp, const vms_vector &newpos)
{
const auto &&result = get_seg_masks(vcvertptr, newpos, Segments.vcptr(objp->segnum), objp->size);
if (result.facemask == 0)
return 0;
else
return move_object_within_mine(vmobjptr, Segments, vcvertptr, objp, newpos);
}
namespace {
class extract_fvec_from_segment
{
public:
static vms_vector get(fvcvertptr &vcvertptr, const shared_segment &segp)
{
vms_vector v;
extract_forward_vector_from_segment(vcvertptr, segp, v);
return v;
}
};
class extract_rvec_from_segment
{
public:
static vms_vector get(fvcvertptr &vcvertptr, const shared_segment &segp)
{
vms_vector v;
extract_right_vector_from_segment(vcvertptr, segp, v);
return v;
}
};
class extract_uvec_from_segment
{
public:
static vms_vector get(fvcvertptr &vcvertptr, const shared_segment &segp)
{
vms_vector v;
extract_up_vector_from_segment(vcvertptr, segp, v);
return v;
}
};
static int ObjectMoveFailed()
{
editor_status("No current object, cannot move.");
return 1;
}
static int ObjectMovePos(const vmobjptridx_t obj, vms_vector &&vec, int scale)
{
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
auto &Objects = LevelUniqueObjectState.Objects;
auto &Vertices = LevelSharedVertexState.get_vertices();
auto &vmobjptr = Objects.vmptr;
vm_vec_normalize(vec);
const auto &&newpos = vm_vec_add(obj->pos, vm_vec_scale(vec, scale));
auto &vcvertptr = Vertices.vcptr;
if (!verify_object_seg(vmobjptr, Segments, vcvertptr, obj, newpos))
obj->pos = newpos;
Update_flags |= UF_WORLD_CHANGED;
return 1;
}
template <typename extract_type, int direction>
static int ObjectMove()
{
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
auto &Objects = LevelUniqueObjectState.Objects;
auto &Vertices = LevelSharedVertexState.get_vertices();
auto &vmobjptridx = Objects.vmptridx;
const auto i = Cur_object_index;
if (i == object_none)
return ObjectMoveFailed();
const auto &&obj = vmobjptridx(i);
auto &vcvertptr = Vertices.vcptr;
return ObjectMovePos(obj, extract_type::get(vcvertptr, vcsegptr(obj->segnum)), direction * OBJ_SCALE);
}
}
// ------------------------------------------------------------------------------------------------------
int ObjectMoveForward(void)
{
return ObjectMove<extract_fvec_from_segment, 1>();
}
// ------------------------------------------------------------------------------------------------------
int ObjectMoveBack(void)
{
return ObjectMove<extract_fvec_from_segment, -1>();
}
// ------------------------------------------------------------------------------------------------------
int ObjectMoveLeft(void)
{
return ObjectMove<extract_rvec_from_segment, -1>();
}
// ------------------------------------------------------------------------------------------------------
int ObjectMoveRight(void)
{
return ObjectMove<extract_rvec_from_segment, 1>();
}
// ------------------------------------------------------------------------------------------------------
int ObjectSetDefault(void)
{
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
auto &Objects = LevelUniqueObjectState.Objects;
auto &Vertices = LevelSharedVertexState.get_vertices();
auto &vmobjptr = Objects.vmptr;
//update_due_to_new_segment();
if (Cur_object_index == object_none) {
editor_status("No current object, cannot move.");
return 1;
}
const auto &&objp = vmobjptr(Cur_object_index);
auto &vcvertptr = Vertices.vcptr;
compute_segment_center(vcvertptr, objp->pos, vcsegptr(objp->segnum));
Update_flags |= UF_WORLD_CHANGED;
return 1;
}
// ------------------------------------------------------------------------------------------------------
int ObjectMoveUp(void)
{
return ObjectMove<extract_uvec_from_segment, 1>();
}
// ------------------------------------------------------------------------------------------------------
int ObjectMoveDown(void)
{
return ObjectMove<extract_uvec_from_segment, -1>();
}
// ------------------------------------------------------------------------------------------------------
static int rotate_object(const vmobjptridx_t obj, int p, int b, int h)
{
vms_angvec ang;
// vm_extract_angles_matrix( &ang,&obj->orient);
// ang.p += p;
// ang.b += b;
// ang.h += h;
ang.p = p;
ang.b = b;
ang.h = h;
const auto rotmat = vm_angles_2_matrix(ang);
obj->orient = vm_matrix_x_matrix(obj->orient, rotmat);
// vm_angles_2_matrix(&obj->orient, &ang);
Update_flags |= UF_WORLD_CHANGED;
return 1;
}
static void reset_object(const vmobjptridx_t obj)
{
med_extract_matrix_from_segment(vcsegptr(obj->segnum), obj->orient);
}
int ObjectResetObject()
{
auto &Objects = LevelUniqueObjectState.Objects;
auto &vmobjptridx = Objects.vmptridx;
reset_object(vmobjptridx(Cur_object_index));
Update_flags |= UF_WORLD_CHANGED;
return 1;
}
int ObjectFlipObject()
{
auto &Objects = LevelUniqueObjectState.Objects;
auto &vmobjptr = Objects.vmptr;
const auto m = &vmobjptr(Cur_object_index)->orient;
vm_vec_negate(m->uvec);
vm_vec_negate(m->rvec);
Update_flags |= UF_WORLD_CHANGED;
return 1;
}
template <int p, int b, int h>
int ObjectChangeRotation()
{
auto &Objects = LevelUniqueObjectState.Objects;
auto &vmobjptridx = Objects.vmptridx;
return rotate_object(vmobjptridx(Cur_object_index), p, b, h);
}
template int ObjectDecreaseBank();
template int ObjectIncreaseBank();
template int ObjectDecreasePitch();
template int ObjectIncreasePitch();
template int ObjectDecreaseHeading();
template int ObjectIncreaseHeading();
template int ObjectDecreaseBankBig();
template int ObjectIncreaseBankBig();
template int ObjectDecreasePitchBig();
template int ObjectIncreasePitchBig();
template int ObjectDecreaseHeadingBig();
template int ObjectIncreaseHeadingBig();
// -----------------------------------------------------------------------------------------------------
// Move object around based on clicks in 2d screen.
// Slide an object parallel to the 2d screen, to a point on a vector.
// The vector is defined by a point on the 2d screen and the eye.
// V = vector from eye to 2d screen point.
// E = eye
// F = forward vector from eye
// O = 3-space location of object
// D = depth of object given forward vector F
// = (OE dot norm(F))
// Must solve intersection of:
// E + tV ( equation of vector from eye through point on 2d screen)
// Fs + D ( equation of plane parallel to 2d screen, at depth D)
// = Fx(Ex + tVx) + Fy(Ey + tVy) + Fz(Ez + tVz) + D = 0
//
// FxEx + FyEy + FzEz - D
// t = - ----------------------
// VxFx + VyFy + VzFz
static void move_object_to_position(const vmobjptridx_t objp, const vms_vector &newpos)
{
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
auto &Objects = LevelUniqueObjectState.Objects;
auto &Vertices = LevelSharedVertexState.get_vertices();
auto &vmobjptr = Objects.vmptr;
auto &vcvertptr = Vertices.vcptr;
if (get_seg_masks(vcvertptr, newpos, vcsegptr(objp->segnum), objp->size).facemask == 0)
{
objp->pos = newpos;
} else {
if (verify_object_seg(vmobjptr, Segments, vcvertptr, objp, newpos)) {
fvi_info hit_info;
auto temp_viewer_obj = *Viewer;
auto viewer_segnum = find_object_seg(LevelSharedSegmentState, LevelUniqueSegmentState, *Viewer);
temp_viewer_obj.segnum = viewer_segnum;
// If the viewer is outside the mine, get him in the mine!
if (viewer_segnum == segment_none) {
editor_status("Unable to move object, viewer not in mine. Aborting");
return;
#if 0
vms_vector last_outside_pos;
// While outside mine, move towards object
count = 0;
while (viewer_segnum == segment_none) {
vms_vector temp_vec;
last_outside_pos = temp_viewer_obj.pos;
vm_vec_avg(&temp_vec, &temp_viewer_obj.pos, newpos);
temp_viewer_obj.pos = temp_vec;
viewer_segnum = find_object_seg(&temp_viewer_obj);
temp_viewer_obj.segnum = viewer_segnum;
if (count > 5) {
editor_status("Unable to move object, can't get viewer in mine. Aborting");
return;
}
}
count = 0;
// While inside mine, move away from object.
while (viewer_segnum != segment_none) {
vms_vector temp_vec;
vm_vec_avg(&temp_vec, &temp_viewer_obj.pos, &last_outside_pos);
temp_viewer_obj.pos = temp_vec;
update_object_seg(&temp_viewer_obj);
viewer_segnum = find_object_seg(&temp_viewer_obj);
temp_viewer_obj.segnum = viewer_segnum;
if (count > 5) {
editor_status("Unable to move object, can't get viewer back out of mine. Aborting");
return;
}
}
#endif
}
const auto fate = find_vector_intersection(fvi_query{
temp_viewer_obj.pos,
newpos,
fvi_query::unused_ignore_obj_list,
fvi_query::unused_LevelUniqueObjectState,
fvi_query::unused_Robot_info,
0,
object_none,
}, temp_viewer_obj.segnum, temp_viewer_obj.size, hit_info);
if (fate == fvi_hit_type::Wall)
{
objp->pos = hit_info.hit_pnt;
const auto &&segp = find_object_seg(LevelSharedSegmentState, LevelUniqueSegmentState, objp);
if (segp != segment_none)
obj_relink(vmobjptr, vmsegptr, objp, segp);
} else {
editor_status("Attempted to move object out of mine. Object not moved.");
}
}
}
Update_flags |= UF_WORLD_CHANGED;
}
static void move_object_to_vector(const vms_vector &vec_through_screen, fix delta_distance)
{
auto &Objects = LevelUniqueObjectState.Objects;
auto &vmobjptridx = Objects.vmptridx;
const auto &&objp = vmobjptridx(Cur_object_index);
const auto result = vm_vec_scale_add(Viewer->pos, vec_through_screen, vm_vec_dist(Viewer->pos, objp->pos) + delta_distance);
move_object_to_position(objp, result);
}
}
static void move_object_to_mouse_click_delta(fix delta_distance)
{
short xcrd,ycrd;
vms_vector vec_through_screen;
if (Cur_object_index == object_none) {
editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!");
return;
}
xcrd = GameViewBox->b1_drag_x1;
ycrd = GameViewBox->b1_drag_y1;
med_point_2_vec(&_canv_editor_game, vec_through_screen, xcrd, ycrd);
move_object_to_vector(vec_through_screen, delta_distance);
}
void move_object_to_mouse_click(void)
{
move_object_to_mouse_click_delta(0);
}
namespace dsx {
int ObjectMoveNearer(void)
{
auto &Objects = LevelUniqueObjectState.Objects;
auto &vcobjptr = Objects.vcptr;
if (Cur_object_index == object_none) {
editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!");
return 1;
}
// move_object_to_mouse_click_delta(-4*F1_0); // Move four units closer to eye
const auto &&result = vm_vec_normalized(vm_vec_sub(vcobjptr(Cur_object_index)->pos, Viewer->pos));
move_object_to_vector(result, -4*F1_0);
return 1;
}
int ObjectMoveFurther(void)
{
auto &Objects = LevelUniqueObjectState.Objects;
auto &vcobjptr = Objects.vcptr;
if (Cur_object_index == object_none) {
editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!");
return 1;
}
// move_object_to_mouse_click_delta(+4*F1_0); // Move four units further from eye
const auto &&result = vm_vec_normalized(vm_vec_sub(vcobjptr(Cur_object_index)->pos, Viewer->pos));
move_object_to_vector(result, 4*F1_0);
return 1;
}
}