5c7fc7d143
Every user now uses inheritance and a virtual function override. Make callback_handler pure virtual, delete its body, and then delete the member variables that existed only for use in that body. Remove the constructor parameters that initialized those variables, and update all derived classes accordingly.
906 lines
27 KiB
C++
906 lines
27 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.
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* Dialog box to edit robot properties.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include "screens.h"
|
|
#include "inferno.h"
|
|
#include "segment.h"
|
|
#include "event.h"
|
|
#include "editor.h"
|
|
#include "editor/esegment.h"
|
|
#include "editor/medmisc.h"
|
|
#include "timer.h"
|
|
#include "objpage.h"
|
|
#include "maths.h"
|
|
#include "dxxerror.h"
|
|
#include "kdefs.h"
|
|
#include "object.h"
|
|
#include "robot.h"
|
|
#include "game.h"
|
|
#include "powerup.h"
|
|
#include "ai.h"
|
|
#include "hostage.h"
|
|
#include "eobject.h"
|
|
#include "medwall.h"
|
|
#include "medrobot.h"
|
|
#include "eswitch.h"
|
|
#include "ehostage.h"
|
|
#include "key.h"
|
|
#include "centers.h"
|
|
#include "bm.h"
|
|
#include "u_mem.h"
|
|
|
|
#include "compiler-range_for.h"
|
|
#include "d_levelstate.h"
|
|
#include "d_enumerate.h"
|
|
#include <memory>
|
|
|
|
static int GoodyNextID();
|
|
static int GoodyPrevID();
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Variables for this module...
|
|
//-------------------------------------------------------------------------
|
|
namespace dsx {
|
|
|
|
namespace {
|
|
|
|
struct robot_dialog : UI_DIALOG
|
|
{
|
|
using UI_DIALOG::UI_DIALOG;
|
|
std::unique_ptr<UI_GADGET_USERBOX> robotViewBox, containsViewBox;
|
|
std::unique_ptr<UI_GADGET_BUTTON> quitButton, prev_powerup_type, next_powerup_type, prev_powerup_id, next_powerup_id, prev_powerup_count, next_powerup_count, prev_robot_type, next_robot_type, next_segment, prev_object, next_object, delete_object, new_object, set_path;
|
|
std::array<std::unique_ptr<UI_GADGET_RADIO>, 6> initialMode; // Number of boxes, AI modes
|
|
fix64 time;
|
|
vms_angvec angles = {}, goody_angles = {};
|
|
int old_object;
|
|
virtual window_event_result callback_handler(const d_event &) override;
|
|
};
|
|
|
|
static robot_dialog *MainWindow;
|
|
|
|
static void call_init_ai_object(vmobjptridx_t objp, ai_behavior behavior)
|
|
{
|
|
segnum_t hide_segment;
|
|
|
|
if (behavior == ai_behavior::AIB_STATION)
|
|
hide_segment = Cursegp;
|
|
else {
|
|
if (Markedsegp != segment_none)
|
|
hide_segment = Markedsegp;
|
|
else
|
|
hide_segment = Cursegp;
|
|
}
|
|
|
|
init_ai_object(objp, behavior, hide_segment);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Called when user presses "Next Type" button. This only works for polygon
|
|
// objects and it just selects the next polygon model for the current object.
|
|
//-------------------------------------------------------------------------
|
|
static int RobotNextType()
|
|
{
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &vmobjptridx = Objects.vmptridx;
|
|
auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
|
|
if (Cur_object_index != object_none ) {
|
|
const auto &&obj = vmobjptridx(Cur_object_index);
|
|
if (obj->type == OBJ_ROBOT)
|
|
{
|
|
obj->id++;
|
|
if (obj->id >= LevelSharedRobotInfoState.N_robot_types)
|
|
obj->id = 0;
|
|
|
|
//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.flags |= (PF_LEVELLING);
|
|
obj->shields = Robot_info[get_robot_id(obj)].strength;
|
|
call_init_ai_object(obj, ai_behavior::AIB_NORMAL);
|
|
|
|
Cur_object_id = get_robot_id(obj);
|
|
}
|
|
}
|
|
Update_flags |= UF_WORLD_CHANGED;
|
|
return 1;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Called when user presses "Prev Type" button. This only works for polygon
|
|
// objects and it just selects the prev polygon model for the current object.
|
|
//-------------------------------------------------------------------------
|
|
static int RobotPrevType()
|
|
{
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &vmobjptridx = Objects.vmptridx;
|
|
auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
|
|
if (Cur_object_index != object_none ) {
|
|
const auto &&obj = vmobjptridx(Cur_object_index);
|
|
if (obj->type == OBJ_ROBOT)
|
|
{
|
|
if (obj->id == 0 )
|
|
obj->id = LevelSharedRobotInfoState.N_robot_types - 1;
|
|
else
|
|
obj->id--;
|
|
|
|
//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.flags |= (PF_LEVELLING);
|
|
obj->shields = Robot_info[get_robot_id(obj)].strength;
|
|
call_init_ai_object(obj, ai_behavior::AIB_NORMAL);
|
|
|
|
Cur_object_id = get_robot_id(obj);
|
|
}
|
|
}
|
|
Update_flags |= UF_WORLD_CHANGED;
|
|
return 1;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Dummy function for Mike to write.
|
|
//-------------------------------------------------------------------------
|
|
static int med_set_ai_path()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
// #define OBJ_NONE 255 //unused object
|
|
// #define OBJ_WALL 0 //A wall... not really an object, but used for collisions
|
|
// #define OBJ_FIREBALL 1 //a fireball, part of an explosion
|
|
// #define OBJ_ROBOT 2 //an evil enemy
|
|
// #define OBJ_HOSTAGE 3 //a hostage you need to rescue
|
|
// #define OBJ_PLAYER 4 //the player on the console
|
|
// #define OBJ_WEAPON 5 //a laser, missile, etc
|
|
// #define OBJ_CAMERA 6 //a camera to slew around with
|
|
// #define OBJ_POWERUP 7 //a powerup you can pick up
|
|
// #define OBJ_DEBRIS 8 //a piece of robot
|
|
// #define OBJ_CNTRLCEN 9 //the control center
|
|
// #define OBJ_FLARE 10 //the control center
|
|
// #define MAX_OBJECT_TYPES 11
|
|
|
|
|
|
#define GOODY_TYPE_MAX MAX_OBJECT_TYPES
|
|
#define GOODY_X 6
|
|
#define GOODY_Y 132
|
|
|
|
//#define GOODY_ID_MAX_ROBOT 6
|
|
//#define GOODY_ID_MAX_POWERUP 9
|
|
#define GOODY_COUNT_MAX 4
|
|
|
|
int Cur_goody_type = OBJ_POWERUP;
|
|
int Cur_goody_id = 0;
|
|
int Cur_goody_count = 0;
|
|
|
|
static void update_goody_info(void)
|
|
{
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &vmobjptr = Objects.vmptr;
|
|
if (Cur_object_index != object_none ) {
|
|
auto &obj = *vmobjptr(Cur_object_index);
|
|
if (obj.type == OBJ_ROBOT)
|
|
{
|
|
obj.contains_type = Cur_goody_type;
|
|
obj.contains_id = Cur_goody_id;
|
|
obj.contains_count = Cur_goody_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
// #define OBJ_WALL 0 //A wall... not really an object, but used for collisions
|
|
// #define OBJ_FIREBALL 1 //a fireball, part of an explosion
|
|
// #define OBJ_ROBOT 2 //an evil enemy
|
|
// #define OBJ_HOSTAGE 3 //a hostage you need to rescue
|
|
// #define OBJ_PLAYER 4 //the player on the console
|
|
// #define OBJ_WEAPON 5 //a laser, missile, etc
|
|
// #define OBJ_CAMERA 6 //a camera to slew around with
|
|
// #define OBJ_POWERUP 7 //a powerup you can pick up
|
|
// #define OBJ_DEBRIS 8 //a piece of robot
|
|
// #define OBJ_CNTRLCEN 9 //the control center
|
|
// #define OBJ_FLARE 10 //the control center
|
|
// #define MAX_OBJECT_TYPES 11
|
|
|
|
|
|
static int GoodyNextType()
|
|
{
|
|
Cur_goody_type++;
|
|
while (!((Cur_goody_type == OBJ_ROBOT) || (Cur_goody_type == OBJ_POWERUP))) {
|
|
if (Cur_goody_type > GOODY_TYPE_MAX)
|
|
Cur_goody_type=0;
|
|
else
|
|
Cur_goody_type++;
|
|
}
|
|
|
|
GoodyNextID();
|
|
GoodyPrevID();
|
|
|
|
update_goody_info();
|
|
return 1;
|
|
}
|
|
|
|
static int GoodyPrevType()
|
|
{
|
|
Cur_goody_type--;
|
|
while (!((Cur_goody_type == OBJ_ROBOT) || (Cur_goody_type == OBJ_POWERUP))) {
|
|
if (Cur_goody_type < 0)
|
|
Cur_goody_type = GOODY_TYPE_MAX;
|
|
else
|
|
Cur_goody_type--;
|
|
}
|
|
|
|
GoodyNextID();
|
|
GoodyPrevID();
|
|
|
|
update_goody_info();
|
|
return 1;
|
|
}
|
|
|
|
int GoodyNextID()
|
|
{
|
|
Cur_goody_id++;
|
|
if (Cur_goody_type == OBJ_ROBOT) {
|
|
if (Cur_goody_id >= LevelSharedRobotInfoState.N_robot_types)
|
|
Cur_goody_id=0;
|
|
} else {
|
|
if (Cur_goody_id >= N_powerup_types)
|
|
Cur_goody_id=0;
|
|
}
|
|
|
|
update_goody_info();
|
|
return 1;
|
|
}
|
|
|
|
int GoodyPrevID()
|
|
{
|
|
Cur_goody_id--;
|
|
if (Cur_goody_type == OBJ_ROBOT) {
|
|
if (Cur_goody_id < 0)
|
|
Cur_goody_id = LevelSharedRobotInfoState.N_robot_types - 1;
|
|
} else {
|
|
if (Cur_goody_id < 0)
|
|
Cur_goody_id = N_powerup_types-1;
|
|
}
|
|
|
|
update_goody_info();
|
|
return 1;
|
|
}
|
|
|
|
static int GoodyNextCount()
|
|
{
|
|
Cur_goody_count++;
|
|
if (Cur_goody_count > GOODY_COUNT_MAX)
|
|
Cur_goody_count=0;
|
|
|
|
update_goody_info();
|
|
return 1;
|
|
}
|
|
|
|
static int GoodyPrevCount()
|
|
{
|
|
Cur_goody_count--;
|
|
if (Cur_goody_count < 0)
|
|
Cur_goody_count=GOODY_COUNT_MAX;
|
|
|
|
update_goody_info();
|
|
return 1;
|
|
}
|
|
|
|
static int is_legal_type(int the_type)
|
|
{
|
|
return (the_type == OBJ_ROBOT) || (the_type == OBJ_CLUTTER);
|
|
}
|
|
|
|
static int is_legal_type_for_this_window(const imobjidx_t objnum)
|
|
{
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &vmobjptr = Objects.vmptr;
|
|
if (objnum == object_none)
|
|
return 1;
|
|
else
|
|
return is_legal_type(vmobjptr(objnum)->type);
|
|
}
|
|
|
|
static int LocalObjectSelectNextinSegment(void)
|
|
{
|
|
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &Vertices = LevelSharedVertexState.get_vertices();
|
|
auto &vmobjptr = Objects.vmptr;
|
|
int rval, first_obj;
|
|
|
|
rval = ObjectSelectNextinSegment();
|
|
first_obj = Cur_object_index;
|
|
|
|
if (Cur_object_index != object_none) {
|
|
while (!is_legal_type_for_this_window(Cur_object_index)) {
|
|
rval = ObjectSelectNextinSegment();
|
|
if (first_obj == Cur_object_index)
|
|
break;
|
|
}
|
|
|
|
const auto &&objp = vmobjptr(Cur_object_index);
|
|
Cur_goody_type = objp->contains_type;
|
|
Cur_goody_id = objp->contains_id;
|
|
if (objp->contains_count < 0)
|
|
objp->contains_count = 0;
|
|
Cur_goody_count = objp->contains_count;
|
|
}
|
|
|
|
if (Cur_object_index != first_obj)
|
|
{
|
|
auto &vcvertptr = Vertices.vcptr;
|
|
set_view_target_from_segment(vcvertptr, Cursegp);
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
static int LocalObjectSelectNextinMine(void)
|
|
{
|
|
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &Vertices = LevelSharedVertexState.get_vertices();
|
|
auto &vmobjptr = Objects.vmptr;
|
|
int rval, first_obj;
|
|
|
|
rval = ObjectSelectNextInMine();
|
|
|
|
first_obj = Cur_object_index;
|
|
|
|
if (Cur_object_index != object_none) {
|
|
while (!is_legal_type_for_this_window(Cur_object_index)) {
|
|
ObjectSelectNextInMine();
|
|
if (Cur_object_index == first_obj)
|
|
break;
|
|
}
|
|
|
|
const auto &&objp = vmobjptr(Cur_object_index);
|
|
Cur_goody_type = objp->contains_type;
|
|
Cur_goody_id = objp->contains_id;
|
|
if (objp->contains_count < 0)
|
|
objp->contains_count = 0;
|
|
Cur_goody_count = objp->contains_count;
|
|
}
|
|
|
|
if (Cur_object_index != first_obj)
|
|
{
|
|
auto &vcvertptr = Vertices.vcptr;
|
|
set_view_target_from_segment(vcvertptr, Cursegp);
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
static int LocalObjectSelectPrevinMine(void)
|
|
{
|
|
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &Vertices = LevelSharedVertexState.get_vertices();
|
|
auto &vmobjptr = Objects.vmptr;
|
|
int rval, first_obj;
|
|
|
|
rval = ObjectSelectPrevInMine();
|
|
|
|
first_obj = Cur_object_index;
|
|
|
|
if (Cur_object_index != object_none) {
|
|
while (!is_legal_type_for_this_window(Cur_object_index)) {
|
|
ObjectSelectPrevInMine();
|
|
if (first_obj == Cur_object_index)
|
|
break;
|
|
}
|
|
|
|
const auto &&objp = vmobjptr(Cur_object_index);
|
|
Cur_goody_type = objp->contains_type;
|
|
Cur_goody_id = objp->contains_id;
|
|
if (objp->contains_count < 0)
|
|
objp->contains_count = 0;
|
|
Cur_goody_count = objp->contains_count;
|
|
}
|
|
|
|
if (Cur_object_index != first_obj)
|
|
{
|
|
auto &vcvertptr = Vertices.vcptr;
|
|
set_view_target_from_segment(vcvertptr, Cursegp);
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
static int LocalObjectDelete(void)
|
|
{
|
|
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &Vertices = LevelSharedVertexState.get_vertices();
|
|
auto &vcobjptr = Objects.vcptr;
|
|
int rval;
|
|
|
|
rval = ObjectDelete();
|
|
|
|
if (Cur_object_index != object_none) {
|
|
auto &objp = *vcobjptr(Cur_object_index);
|
|
Cur_goody_type = objp.contains_type;
|
|
Cur_goody_id = objp.contains_id;
|
|
Cur_goody_count = objp.contains_count;
|
|
}
|
|
|
|
auto &vcvertptr = Vertices.vcptr;
|
|
set_view_target_from_segment(vcvertptr, Cursegp);
|
|
|
|
return rval;
|
|
}
|
|
|
|
static int LocalObjectPlaceObject(void)
|
|
{
|
|
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &Vertices = LevelSharedVertexState.get_vertices();
|
|
auto &vmobjptr = Objects.vmptr;
|
|
int rval;
|
|
|
|
Cur_goody_count = 0;
|
|
|
|
if (Cur_object_type != OBJ_ROBOT)
|
|
{
|
|
Cur_object_type = OBJ_ROBOT;
|
|
Cur_object_id = 3; // class 1 drone
|
|
Num_object_subtypes = LevelSharedRobotInfoState.N_robot_types;
|
|
}
|
|
|
|
rval = ObjectPlaceObject();
|
|
if (rval == -1)
|
|
return -1;
|
|
|
|
const auto &&objp = vmobjptr(Cur_object_index);
|
|
objp->contains_type = Cur_goody_type;
|
|
objp->contains_id = Cur_goody_id;
|
|
objp->contains_count = Cur_goody_count;
|
|
|
|
auto &vcvertptr = Vertices.vcptr;
|
|
set_view_target_from_segment(vcvertptr, Cursegp);
|
|
|
|
return rval;
|
|
}
|
|
|
|
void close_all_windows(void)
|
|
{
|
|
close_trigger_window();
|
|
close_wall_window();
|
|
close_centers_window();
|
|
#if defined(DXX_BUILD_DESCENT_I)
|
|
hostage_close_window();
|
|
#endif
|
|
robot_close_window();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Called from the editor... does one instance of the robot dialog box
|
|
//-------------------------------------------------------------------------
|
|
int do_robot_dialog()
|
|
{
|
|
// Only open 1 instance of this window...
|
|
if ( MainWindow != NULL ) return 0;
|
|
|
|
// Close other windows
|
|
close_all_windows();
|
|
Cur_goody_count = 0;
|
|
|
|
// Open a window with a quit button
|
|
MainWindow = window_create<robot_dialog>(TMAPBOX_X + 20, TMAPBOX_Y + 20, 765 - TMAPBOX_X, 545 - TMAPBOX_Y, DF_DIALOG);
|
|
return 1;
|
|
}
|
|
|
|
static window_event_result robot_dialog_created(robot_dialog *const r)
|
|
{
|
|
r->quitButton = ui_add_gadget_button(*r, 20, 286, 40, 32, "Done", NULL);
|
|
r->prev_powerup_type = ui_add_gadget_button(*r, GOODY_X+50, GOODY_Y-3, 25, 22, "<<", GoodyPrevType);
|
|
r->next_powerup_type = ui_add_gadget_button(*r, GOODY_X+80, GOODY_Y-3, 25, 22, ">>", GoodyNextType);
|
|
r->prev_powerup_id = ui_add_gadget_button(*r, GOODY_X+50, GOODY_Y+21, 25, 22, "<<", GoodyPrevID);
|
|
r->next_powerup_id = ui_add_gadget_button(*r, GOODY_X+80, GOODY_Y+21, 25, 22, ">>", GoodyNextID);
|
|
r->prev_powerup_count = ui_add_gadget_button(*r, GOODY_X+50, GOODY_Y+45, 25, 22, "<<", GoodyPrevCount);
|
|
r->next_powerup_count = ui_add_gadget_button(*r, GOODY_X+80, GOODY_Y+45, 25, 22, ">>", GoodyNextCount);
|
|
r->initialMode[0] = ui_add_gadget_radio(*r, 6, 58, 16, 16, 0, "Hover");
|
|
r->initialMode[1] = ui_add_gadget_radio(*r, 76, 58, 16, 16, 0, "Normal");
|
|
r->initialMode[2] = ui_add_gadget_radio(*r, 6, 78, 16, 16, 0, "(hide)");
|
|
r->initialMode[3] = ui_add_gadget_radio(*r, 76, 78, 16, 16, 0, "Avoid");
|
|
r->initialMode[4] = ui_add_gadget_radio(*r, 6, 98, 16, 16, 0, "Follow");
|
|
r->initialMode[5] = ui_add_gadget_radio(*r, 76, 98, 16, 16, 0, "Station");
|
|
// The little box the robots will spin in.
|
|
r->robotViewBox = ui_add_gadget_userbox(*r, 155, 5, 150, 125);
|
|
// The little box the robots will spin in.
|
|
r->containsViewBox = ui_add_gadget_userbox(*r, 10, 202, 100, 80);
|
|
// A bunch of buttons...
|
|
int i = 135;
|
|
r->prev_robot_type = ui_add_gadget_button(*r, 190, i, 53, 26, "<<Typ", RobotPrevType);
|
|
r->next_robot_type = ui_add_gadget_button(*r, 247, i, 53, 26, "Typ>>", RobotNextType); i += 29;
|
|
r->next_segment = ui_add_gadget_button(*r, 190, i, 110, 26, "Next in Seg", LocalObjectSelectNextinSegment); i += 29;
|
|
r->prev_object = ui_add_gadget_button(*r, 190, i, 53, 26, "<<Obj", LocalObjectSelectPrevinMine);
|
|
r->next_object = ui_add_gadget_button(*r, 247, i, 53, 26, ">>Obj", LocalObjectSelectNextinMine); i += 29;
|
|
r->delete_object = ui_add_gadget_button(*r, 190, i, 110, 26, "Delete", LocalObjectDelete); i += 29;
|
|
r->new_object = ui_add_gadget_button(*r, 190, i, 110, 26, "Create New", LocalObjectPlaceObject); i += 29;
|
|
r->set_path = ui_add_gadget_button(*r, 190, i, 110, 26, "Set Path", med_set_ai_path);
|
|
r->time = timer_query();
|
|
r->old_object = -2; // Set to some dummy value so everything works ok on the first frame.
|
|
if ( Cur_object_index == object_none)
|
|
LocalObjectSelectNextinMine();
|
|
return window_event_result::handled;
|
|
}
|
|
|
|
void robot_close_window()
|
|
{
|
|
if ( MainWindow!=NULL ) {
|
|
ui_close_dialog(*std::exchange(MainWindow, nullptr));
|
|
}
|
|
|
|
}
|
|
|
|
namespace dsx {
|
|
|
|
namespace {
|
|
|
|
window_event_result robot_dialog::callback_handler(const d_event &event)
|
|
{
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &vcobjptr = Objects.vcptr;
|
|
auto &vmobjptr = Objects.vmptr;
|
|
auto &vmobjptridx = Objects.vmptridx;
|
|
switch(event.type)
|
|
{
|
|
case EVENT_WINDOW_CREATED:
|
|
return robot_dialog_created(this);
|
|
case EVENT_WINDOW_CLOSE:
|
|
MainWindow = NULL;
|
|
return window_event_result::ignored;
|
|
default:
|
|
break;
|
|
}
|
|
fix DeltaTime;
|
|
fix64 Temp;
|
|
int first_object_index;
|
|
int keypress = 0;
|
|
window_event_result rval = window_event_result::ignored;
|
|
|
|
if (event.type == EVENT_KEY_COMMAND)
|
|
keypress = event_key_get(event);
|
|
|
|
Assert(MainWindow != NULL);
|
|
|
|
first_object_index = Cur_object_index;
|
|
while (!is_legal_type_for_this_window(Cur_object_index)) {
|
|
LocalObjectSelectNextinMine();
|
|
if (first_object_index == Cur_object_index) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------
|
|
// Call the ui code..
|
|
//------------------------------------------------------------
|
|
ui_button_any_drawn = 0;
|
|
|
|
//------------------------------------------------------------
|
|
// If we change objects, we need to reset the ui code for all
|
|
// of the radio buttons that control the ai mode. Also makes
|
|
// the current AI mode button be flagged as pressed down.
|
|
//------------------------------------------------------------
|
|
if (old_object != Cur_object_index ) {
|
|
range_for (auto &i, initialMode)
|
|
ui_radio_set_value(*i, 0);
|
|
if ( Cur_object_index != object_none ) {
|
|
auto &behavior = vmobjptr(Cur_object_index)->ctype.ai_info.behavior;
|
|
switch (behavior)
|
|
{
|
|
case ai_behavior::AIB_STILL:
|
|
case ai_behavior::AIB_NORMAL:
|
|
case ai_behavior::AIB_RUN_FROM:
|
|
case ai_behavior::AIB_STATION:
|
|
#if defined(DXX_BUILD_DESCENT_I)
|
|
case ai_behavior::AIB_HIDE:
|
|
case ai_behavior::AIB_FOLLOW_PATH:
|
|
#elif defined(DXX_BUILD_DESCENT_II)
|
|
case ai_behavior::AIB_BEHIND:
|
|
case ai_behavior::AIB_SNIPE:
|
|
case ai_behavior::AIB_FOLLOW:
|
|
#endif
|
|
break;
|
|
default:
|
|
behavior = ai_behavior::AIB_NORMAL;
|
|
break;
|
|
}
|
|
ui_radio_set_value(*initialMode[static_cast<std::size_t>(behavior) - MIN_BEHAVIOR], 1);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------
|
|
// If any of the radio buttons that control the mode are set, then
|
|
// update the cooresponding AI state.
|
|
//------------------------------------------------------------
|
|
range_for (auto &&eim, enumerate(initialMode))
|
|
{
|
|
auto &im = eim.value;
|
|
if (GADGET_PRESSED(im.get()))
|
|
{
|
|
const auto i = eim.idx;
|
|
const auto b = static_cast<ai_behavior>(MIN_BEHAVIOR + i);
|
|
const auto &&objp = vmobjptridx(Cur_object_index);
|
|
auto &behavior = objp->ctype.ai_info.behavior;
|
|
if (behavior != b) {
|
|
behavior = b; // Set the ai_state to the cooresponding radio button
|
|
call_init_ai_object(objp, b);
|
|
rval = window_event_result::handled;
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------
|
|
// Redraw the object in the little 64x64 box
|
|
//------------------------------------------------------------
|
|
if (event.type == EVENT_UI_DIALOG_DRAW)
|
|
{
|
|
// A simple frame time counter for spinning the objects...
|
|
Temp = timer_query();
|
|
DeltaTime = Temp - time;
|
|
time = Temp;
|
|
|
|
gr_set_current_canvas(robotViewBox->canvas);
|
|
if (Cur_object_index != object_none ) {
|
|
const auto &&obj = vmobjptr(Cur_object_index);
|
|
|
|
draw_object_picture(*grd_curcanv, obj->id, angles, obj->type);
|
|
angles.h += fixmul(0x1000, DeltaTime );
|
|
} else {
|
|
// no object, so just blank out
|
|
gr_clear_canvas(*grd_curcanv, CGREY);
|
|
|
|
// LocalObjectSelectNextInMine();
|
|
}
|
|
|
|
//------------------------------------------------------------
|
|
// Redraw the contained object in the other little box
|
|
//------------------------------------------------------------
|
|
gr_set_current_canvas(containsViewBox->canvas);
|
|
if ((Cur_object_index != object_none ) && (Cur_goody_count > 0)) {
|
|
if ( Cur_goody_id > -1 )
|
|
draw_object_picture(*grd_curcanv, Cur_goody_id, goody_angles, Cur_goody_type);
|
|
else
|
|
gr_clear_canvas(*grd_curcanv, CGREY);
|
|
goody_angles.h += fixmul(0x1000, DeltaTime );
|
|
} else {
|
|
// no object, so just blank out
|
|
gr_clear_canvas(*grd_curcanv, CGREY);
|
|
|
|
// LocalObjectSelectNextInMine();
|
|
}
|
|
//------------------------------------------------------------
|
|
// If anything changes in the ui system, redraw all the text that
|
|
// identifies this robot.
|
|
//------------------------------------------------------------
|
|
|
|
const char *id_text;
|
|
const char *type_text;
|
|
|
|
if (Cur_object_index != object_none) {
|
|
const auto &&obj = vmobjptr(Cur_object_index);
|
|
Cur_goody_type = obj->contains_type;
|
|
Cur_goody_id = obj->contains_id;
|
|
if (obj->contains_count < 0)
|
|
obj->contains_count = 0;
|
|
Cur_goody_count = obj->contains_count;
|
|
}
|
|
|
|
ui_dprintf_at( MainWindow, GOODY_X, GOODY_Y, " Type:");
|
|
ui_dprintf_at( MainWindow, GOODY_X, GOODY_Y+24, " ID:");
|
|
ui_dprintf_at( MainWindow, GOODY_X, GOODY_Y+48, "Count:");
|
|
|
|
switch (Cur_goody_type) {
|
|
case OBJ_ROBOT:
|
|
type_text = "Robot ";
|
|
id_text = Robot_names[Cur_goody_id].data();
|
|
break;
|
|
default:
|
|
editor_status_fmt("Illegal contained object type (%i), changing to powerup.", Cur_goody_type);
|
|
Cur_goody_type = OBJ_POWERUP;
|
|
Cur_goody_id = 0;
|
|
DXX_BOOST_FALLTHROUGH;
|
|
case OBJ_POWERUP:
|
|
type_text = "Powerup";
|
|
id_text = Powerup_names[Cur_goody_id].data();
|
|
break;
|
|
}
|
|
|
|
ui_dputs_at( MainWindow, GOODY_X+108, GOODY_Y, type_text);
|
|
ui_dprintf_at( MainWindow, GOODY_X+108, GOODY_Y+24, "%-8s", id_text);
|
|
ui_dprintf_at( MainWindow, GOODY_X+108, GOODY_Y+48, "%i", Cur_goody_count);
|
|
|
|
if ( Cur_object_index != object_none ) {
|
|
const auto id = get_robot_id(vcobjptr(Cur_object_index));
|
|
|
|
ui_dprintf_at( MainWindow, 12, 6, "Robot: %3d ", Cur_object_index );
|
|
ui_dprintf_at( MainWindow, 12, 22, " Id: %3d", id);
|
|
ui_dprintf_at( MainWindow, 12, 38, " Name: %-8s", Robot_names[id].data());
|
|
|
|
} else {
|
|
ui_dprintf_at( MainWindow, 12, 6, "Robot: none" );
|
|
ui_dprintf_at( MainWindow, 12, 22, " Type: ? " );
|
|
ui_dprintf_at( MainWindow, 12, 38, " Name: ________" );
|
|
}
|
|
}
|
|
|
|
if (ui_button_any_drawn || (old_object != Cur_object_index) )
|
|
Update_flags |= UF_WORLD_CHANGED;
|
|
if (GADGET_PRESSED(quitButton.get()) || keypress == KEY_ESC)
|
|
{
|
|
return window_event_result::close;
|
|
}
|
|
|
|
old_object = Cur_object_index;
|
|
return rval;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------
|
|
#define NUM_MATT_THINGS 2
|
|
|
|
#define MATT_LEN 20
|
|
|
|
namespace {
|
|
|
|
struct object_dialog : UI_DIALOG
|
|
{
|
|
explicit object_dialog(short x, short y, short w, short h, enum dialog_flags flags, object &obj);
|
|
struct creation_context
|
|
{
|
|
vmobjptr_t obj;
|
|
creation_context(vmobjptr_t o) :
|
|
obj(o)
|
|
{
|
|
}
|
|
};
|
|
std::unique_ptr<UI_GADGET_INPUTBOX> xtext, ytext, ztext;
|
|
std::array<std::unique_ptr<UI_GADGET_RADIO>, 2> initialMode;
|
|
std::unique_ptr<UI_GADGET_BUTTON> quitButton;
|
|
virtual window_event_result callback_handler(const d_event &) override;
|
|
};
|
|
|
|
static object_dialog *MattWindow;
|
|
|
|
}
|
|
|
|
void object_close_window()
|
|
{
|
|
if ( MattWindow!=NULL ) {
|
|
ui_close_dialog(*std::exchange(MattWindow, nullptr));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Called from the editor... does one instance of the object dialog box
|
|
//-------------------------------------------------------------------------
|
|
int do_object_dialog()
|
|
{
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &vmobjptr = Objects.vmptr;
|
|
if (Cur_object_index == object_none)
|
|
Cur_object_index = object_first;
|
|
|
|
auto obj = vmobjptr(Cur_object_index);
|
|
if (obj->type == OBJ_ROBOT) //don't do this for robots
|
|
return 0;
|
|
|
|
// Only open 1 instance of this window...
|
|
if ( MattWindow != NULL )
|
|
return 0;
|
|
|
|
Cur_goody_count = 0;
|
|
|
|
// Open a window with a quit button
|
|
MattWindow = window_create<object_dialog>(TMAPBOX_X + 20, TMAPBOX_Y + 20, 765 - TMAPBOX_X, 545 - TMAPBOX_Y, DF_DIALOG, *obj);
|
|
return 1;
|
|
}
|
|
|
|
object_dialog::object_dialog(short x, short y, short w, short h, enum dialog_flags flags, object &obj) :
|
|
UI_DIALOG(x, y, w, h, flags)
|
|
{
|
|
quitButton = ui_add_gadget_button(*this, 20, 286, 40, 32, "Done", NULL );
|
|
quitButton->hotkey = KEY_ENTER;
|
|
// These are the radio buttons for each mode
|
|
initialMode[0] = ui_add_gadget_radio(*this, 10, 50, 16, 16, 0, "None" );
|
|
initialMode[1] = ui_add_gadget_radio(*this, 80, 50, 16, 16, 0, "Spinning" );
|
|
initialMode[obj.movement_source == object::movement_type::spinning ? 1 : 0]->flag = 1;
|
|
char message[MATT_LEN];
|
|
snprintf(message, sizeof(message), "%.2f", f2fl(obj.mtype.spin_rate.x));
|
|
xtext = ui_add_gadget_inputbox<MATT_LEN>(*this, 30, 132, message);
|
|
snprintf(message, sizeof(message), "%.2f", f2fl(obj.mtype.spin_rate.y));
|
|
ytext = ui_add_gadget_inputbox<MATT_LEN>(*this, 30, 162, message);
|
|
snprintf(message, sizeof(message), "%.2f", f2fl(obj.mtype.spin_rate.z));
|
|
ztext = ui_add_gadget_inputbox<MATT_LEN>(*this, 30, 192, message);
|
|
ui_gadget_calc_keys(*this);
|
|
keyboard_focus_gadget = initialMode[0].get();
|
|
}
|
|
|
|
window_event_result object_dialog::callback_handler(const d_event &event)
|
|
{
|
|
auto &Objects = LevelUniqueObjectState.Objects;
|
|
auto &vmobjptr = Objects.vmptr;
|
|
switch(event.type)
|
|
{
|
|
case EVENT_WINDOW_CLOSE:
|
|
MattWindow = NULL;
|
|
return window_event_result::ignored;
|
|
default:
|
|
break;
|
|
}
|
|
const auto &&obj = vmobjptr(Cur_object_index);
|
|
int keypress = 0;
|
|
window_event_result rval = window_event_result::ignored;
|
|
|
|
if (event.type == EVENT_KEY_COMMAND)
|
|
keypress = event_key_get(event);
|
|
|
|
//------------------------------------------------------------
|
|
// Call the ui code..
|
|
//------------------------------------------------------------
|
|
ui_button_any_drawn = 0;
|
|
|
|
|
|
if (event.type == EVENT_UI_DIALOG_DRAW)
|
|
{
|
|
ui_dprintf_at(this, 10, 132,"&X:" );
|
|
ui_dprintf_at(this, 10, 162,"&Y:" );
|
|
ui_dprintf_at(this, 10, 192,"&Z:" );
|
|
}
|
|
|
|
if (GADGET_PRESSED(quitButton.get()) || keypress == KEY_ESC)
|
|
{
|
|
if (initialMode[0]->flag)
|
|
obj->movement_source = object::movement_type::None;
|
|
if (initialMode[1]->flag)
|
|
obj->movement_source = object::movement_type::spinning;
|
|
|
|
obj->mtype.spin_rate.x = fl2f(atof(xtext->text.get()));
|
|
obj->mtype.spin_rate.y = fl2f(atof(ytext->text.get()));
|
|
obj->mtype.spin_rate.z = fl2f(atof(ztext->text.get()));
|
|
return window_event_result::close;
|
|
}
|
|
return rval;
|
|
}
|