Author Topic: [CODE REVIEW] zookeeper's post-3.7.0 features  (Read 12362 times)

0 Members and 1 Guest are viewing this topic.

Offline zookeeper

  • *knock knock* Who's there? Poe. Poe who?
  • 210
[CODE REVIEW] zookeeper's post-3.7.0 features
Here's a bunch of nice things up for review. I'll add a few more to the list sometime soon.

Allowing Controlconfigdefaults.tbl to define several sets of defaults and making the "Defaults" button in the control config screen cycle between them, thus allowing mods to ship several control presets for the user to choose from:

Code: [Select]
Index: controlsconfig.cpp
===================================================================
--- controlsconfig.cpp (revision 9640)
+++ controlsconfig.cpp (working copy)
@@ -135,6 +135,7 @@
  int size;
  int *index;  // array (size) of Control_config indices of replaced elements
  config_item *list;  // array (size) of original elements
+ int reset_to_preset; // if >=0, then we ignore the above list and simply reset to the given preset instead
  config_item_undo *next;
 };
 
@@ -174,6 +175,7 @@
 static int Conflicts_tabs[NUM_TABS];
 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX];  // buttons for each line of text in list
 static UI_WINDOW Ui_window;
+static unsigned int Defaults_cycle_pos; // the controls preset that was last selected
 
 static struct {
  int key;  // index of other control in conflict with this one
@@ -534,6 +536,8 @@
  ptr->list = NULL;
  }
 
+ ptr->reset_to_preset = -1;
+
  return ptr;
 }
 
@@ -566,41 +570,45 @@
  return -1;
  }
 
- if (Config_item_undo->index[0] & JOY_AXIS) {
- tab = SHIP_TAB;
+ if (Config_item_undo->reset_to_preset > -1) {
+ control_config_reset_defaults(Config_item_undo->reset_to_preset);
  } else {
- tab = Control_config[Config_item_undo->index[0]].tab;
- }
+ if (Config_item_undo->index[0] & JOY_AXIS) {
+ tab = SHIP_TAB;
+ } else {
+ tab = Control_config[Config_item_undo->index[0]].tab;
+ }
 
- for (i=1; i<Config_item_undo->size; i++) {
- if (Config_item_undo->index[i] & JOY_AXIS) {
- if (tab != SHIP_TAB) {
- tab = -1;
- }
+ for (i=1; i<Config_item_undo->size; i++) {
+ if (Config_item_undo->index[i] & JOY_AXIS) {
+ if (tab != SHIP_TAB) {
+ tab = -1;
+ }
 
- } else {
- if (Control_config[Config_item_undo->index[i]].tab != tab) {
- tab = -1;
+ } else {
+ if (Control_config[Config_item_undo->index[i]].tab != tab) {
+ tab = -1;
+ }
  }
  }
- }
 
- if (tab >= 0) {
- Tab = tab;
- }
+ if (tab >= 0) {
+ Tab = tab;
+ }
 
- for (i=0; i<Config_item_undo->size; i++) {
- z = Config_item_undo->index[i];
- if (z & JOY_AXIS) {
- config_item *ptr;
+ for (i=0; i<Config_item_undo->size; i++) {
+ z = Config_item_undo->index[i];
+ if (z & JOY_AXIS) {
+ config_item *ptr;
 
- z &= ~JOY_AXIS;
- ptr = &Config_item_undo->list[i];
- Axis_map_to[z] = ptr->joy_id;
- Invert_axis[z] = ptr->used;
+ z &= ~JOY_AXIS;
+ ptr = &Config_item_undo->list[i];
+ Axis_map_to[z] = ptr->joy_id;
+ Invert_axis[z] = ptr->used;
 
- } else {
- Control_config[z] = Config_item_undo->list[i];
+ } else {
+ Control_config[z] = Config_item_undo->list[i];
+ }
  }
  }
 
@@ -860,10 +868,25 @@
  int i, j, total = 0;
  config_item_undo *ptr;
  config_item item;
-
+ config_item *preset;
+ bool cycling_presets = false;
+
+ // If there are presets, then we'll cycle to the next preset and reset to that
+ if (Control_config_presets.size() >= 1) {
+ cycling_presets = true;
+
+ if (++Defaults_cycle_pos >= Control_config_presets.size())
+ Defaults_cycle_pos = 0;
+
+ preset = Control_config_presets[Defaults_cycle_pos];
+ } else {
+ // If there are no presets, then we'll always reset to the hardcoded defaults
+ preset = Control_config;
+ }
+
  // first, determine how many bindings need to be changed
  for (i=0; i<CCFG_MAX; i++) {
- if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
+ if ((Control_config[i].key_id != preset[i].key_default) || (Control_config[i].joy_id != preset[i].joy_default)) {
  total++;
  }
  }
@@ -874,7 +897,7 @@
  }
  }
 
- if (!total) {
+ if (!total && !cycling_presets) {
  gamesnd_play_iface(SND_GENERAL_FAIL);
  return -1;
  }
@@ -882,7 +905,7 @@
  // now, back up the old bindings so we can undo if we want to
  ptr = get_undo_block(total);
  for (i=j=0; i<CCFG_MAX; i++) {
- if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
+ if ((Control_config[i].key_id != preset[i].key_default) || (Control_config[i].joy_id != preset[i].joy_default)) {
  ptr->index[j] = i;
  ptr->list[j] = Control_config[i];
  j++;
@@ -901,23 +924,43 @@
  j++;
  }
  }
+
  Assert(j == total);
- control_config_reset_defaults();
+
+ if (cycling_presets)
+ control_config_reset_defaults(Defaults_cycle_pos);
+ else
+ control_config_reset_defaults();
+
  control_config_conflict_check();
  control_config_list_prepare();
  gamesnd_play_iface(SND_RESET_PRESSED);
  return 0;
 }
 
-// This sets all the controls to their default values
-void control_config_reset_defaults()
+// This sets all the controls to the default values in the given preset
+// If no preset is given, the hardcoded defaults of Control_config are used
+void control_config_reset_defaults(int presetnum)
 {
  int i;
+ config_item *preset;
 
+ if (presetnum >= 0)
+ preset = Control_config_presets[presetnum];
+ else
+ preset = Control_config;
+
  // Reset keyboard defaults
  for (i=0; i<CCFG_MAX; i++) {
- Control_config[i].key_id = Control_config[i].key_default;
- Control_config[i].joy_id = Control_config[i].joy_default;
+ // Note that key_default, joy_default and text_default are NOT
+ // overwritten here; they should retain the values of the first preset
+ Control_config[i].text = preset[i].text;
+ Control_config[i].key_id = preset[i].key_default;
+ Control_config[i].joy_id = preset[i].joy_default;
+ Control_config[i].tab = preset[i].tab;
+ Control_config[i].hasXSTR = preset[i].hasXSTR;
+ Control_config[i].type = preset[i].type;
+ Control_config[i].disabled = preset[i].disabled;
  }
 
  for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
@@ -1241,6 +1284,8 @@
  Control_config_backup[i] = Control_config[i];
  }
 
+ Defaults_cycle_pos = 0;
+
  common_set_interface_palette(NOX("ControlConfigPalette"));  // set the interface palette
  Ui_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0);
  Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
Index: controlsconfig.h
===================================================================
--- controlsconfig.h (revision 9640)
+++ controlsconfig.h (working copy)
@@ -8,6 +8,7 @@
 */
 
 
+#include "globalincs/pstypes.h"
 
 #ifndef CONTROLS_CONFIG_H
 #define CONTROLS_CONFIG_H
@@ -229,6 +230,7 @@
 extern int Invert_axis_defaults[];
 
 extern config_item Control_config[]; // stores the keyboard configuration
+extern SCP_vector<config_item*> Control_config_presets; // tabled control presets
 extern char **Scan_code_text;
 extern char **Joy_button_text;
 
@@ -241,7 +243,7 @@
 
 void control_config_cancel_exit();
 
-void control_config_reset_defaults();
+void control_config_reset_defaults(int presetnum=-1);
 int translate_key_to_index(char *key);
 char *translate_key(char *key);
 char *textify_scancode(int code);
Index: controlsconfigcommon.cpp
===================================================================
--- controlsconfigcommon.cpp (revision 9640)
+++ controlsconfigcommon.cpp (working copy)
@@ -348,6 +348,8 @@
 char **Scan_code_text = Scan_code_text_english;
 char **Joy_button_text = Joy_button_text_english;
 
+SCP_vector<config_item*> Control_config_presets;
+
 void set_modifier_status()
 {
  int i;
@@ -373,7 +375,10 @@
  }
 }
 
-int translate_key_to_index(char *key)
+// If find_override is set to true, then this returns the index of the action
+// which has been bound to the given key. Otherwise, the index of the action
+// which has the given key as its default key will be returned.
+int translate_key_to_index(char *key, bool find_override)
 {
  int i, index = -1, alt = 0, shift = 0, max_scan_codes;
 
@@ -575,9 +580,11 @@
     reset_parse();
     
  // start parsing
- required_string("#ControlConfigOverride");
+ while(optional_string("#ControlConfigOverride")) {
+ config_item *cfg_preset = new config_item[CCFG_MAX + 1];
+ std::copy(Control_config, Control_config + CCFG_MAX + 1, cfg_preset);
+ Control_config_presets.push_back(cfg_preset);
 
- // read fonts
  while (required_string_either("#End","$Bind Name:"))
     {
         const int iBufferLength = 64;
@@ -589,7 +596,7 @@
         const size_t cCntrlAryLength = sizeof(Control_config) / sizeof(Control_config[0]);
         for (size_t i = 0; i < cCntrlAryLength; ++i)
         {
-            config_item& r_ccConfig = Control_config[i];
+            config_item& r_ccConfig = cfg_preset[i];
             
             if (!strcmp(szTempBuffer, r_ccConfig.text))
             {
@@ -645,8 +652,16 @@
             }
         }
     }
+
+    required_string("#End");
+    }
     
-    required_string("#End");
+ // Overwrite the control config with the first preset we found, except for
+ // key_default, joy_default and text_default which ought to retain the
+ // hardcoded values
+ if (Control_config_presets.size() > 0) {
+ std::copy(Control_config_presets[0], Control_config_presets[0] + CCFG_MAX + 1, Control_config);
+ }
 }
 
 #define ADD_ENUM_TO_ENUM_MAP(Enum) mEnumNameToVal[#Enum] = (Enum);

Autoaim-related features (1 and 2 are intertwined in HudGaugeReticle::render so they're all in one patch):
1. Allow the HUD reticle switch to its .ani's second frame (if any) when the target is within your autoaim FOV.
2. Allows sounds to be played when the target enters and/or leaves your autoaim FOV.
3. Exposes the FOV value to Lua and makes it a per-ship property (rather than per-shiptype only).

Code: [Select]
Index: hud/hudparse.cpp
===================================================================
--- hud/hudparse.cpp (revision 9640)
+++ hud/hudparse.cpp (working copy)
@@ -43,6 +43,7 @@
 
 // Goober5000
 int Hud_reticle_style = HUD_RETICLE_STYLE_FS2;
+bool Hud_reticle_2nd_frame_for_autoaim = false;
 
 bool Hud_retail = true;
 
@@ -246,6 +247,10 @@
  stuff_boolean(&Lock_targetbox_mode);
  }
 
+ if (optional_string("$Use Second Reticle Frame For Autoaim:")) {
+ stuff_boolean(&Hud_reticle_2nd_frame_for_autoaim);
+ }
+
  if(optional_string("$Reticle Style:")) {
  int temp = required_string_either("FS1", "FS2");
 
Index: hud/hudparse.h
===================================================================
--- hud/hudparse.h (revision 9640)
+++ hud/hudparse.h (working copy)
@@ -20,6 +20,7 @@
 #define HUD_RETICLE_STYLE_FS2 1
 
 extern int Hud_reticle_style;
+extern bool Hud_reticle_2nd_frame_for_autoaim;
 
 //Functions
 int hud_get_gauge_index(char* name);
Index: hud/hudreticle.cpp
===================================================================
--- hud/hudreticle.cpp (revision 9640)
+++ hud/hudreticle.cpp (working copy)
@@ -20,6 +20,7 @@
 #include "weapon/emp.h"
 #include "localization/localize.h"
 #include "network/multi.h"
+#include "weapon/weapon.h"
 
 #define NUM_RETICLE_ANIS 11 // keep up to date when modifying the number of reticle ani files
 
@@ -228,6 +229,7 @@
 HudGaugeReticle::HudGaugeReticle():
 HudGauge(HUD_OBJECT_CENTER_RETICLE, HUD_CENTER_RETICLE, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_TOPDOWN | VM_OTHER_SHIP), 255, 255, 255)
 {
+ has_autoaim_lock = false;
 }
 
 void HudGaugeReticle::initBitmaps(char *fname)
@@ -247,9 +249,39 @@
 
 void HudGaugeReticle::render(float frametime)
 {
+ ship_info *sip = &Ship_info[Player_ship->ship_info_index];
+
+ if (Hud_reticle_2nd_frame_for_autoaim || sip->autoaim_lock_snd > -1 || sip->autoaim_lost_snd > -1) {
+ ship *shipp = &Ships[Objects[Player->objnum].instance];
+ ship_weapon *swp = &shipp->weapons;
+ ai_info *aip = &Ai_info[shipp->ai_index];
+
+ if (aip->target_objnum != -1) {
+ bool autoaiming = false;
+
+ autoaiming = in_autoaim_fov(shipp, swp->current_primary_bank, &Objects[aip->target_objnum]);
+
+ if (autoaiming) {
+ if (!has_autoaim_lock && sip->autoaim_lock_snd > -1) {
+ snd_play( &Snds[sip->autoaim_lock_snd]);
+ }
+ has_autoaim_lock = true;
+ }
+ else {
+ if (has_autoaim_lock && sip->autoaim_lost_snd > -1) {
+ snd_play( &Snds[sip->autoaim_lost_snd]);
+ }
+ has_autoaim_lock = false;
+ }
+ }
+ }
+
  setGaugeColor(HUD_C_BRIGHT);
 
- renderBitmap(crosshair.first_frame, position[0], position[1]);
+ if (Hud_reticle_2nd_frame_for_autoaim && crosshair.num_frames > 1 && has_autoaim_lock)
+ renderBitmap(crosshair.first_frame + 1, position[0], position[1]);
+ else
+ renderBitmap(crosshair.first_frame, position[0], position[1]);
 
  if (firepoint_display) {
  fp.clear();
Index: hud/hudreticle.h
===================================================================
--- hud/hudreticle.h (revision 9640)
+++ hud/hudreticle.h (working copy)
@@ -43,6 +43,7 @@
  int firepoint_size;
  int firepoint_scale_x;
  int firepoint_scale_y;
+ bool has_autoaim_lock;
 public:
  HudGaugeReticle();
  void render(float frametime);
Index: parse/lua.cpp
===================================================================
--- parse/lua.cpp (revision 9640)
+++ parse/lua.cpp (working copy)
@@ -7970,6 +7970,28 @@
  return ade_set_args(L, "f", sip->max_weapon_reserve);
 }
 
+ADE_VIRTVAR(AutoaimFOV, l_Ship, "number", "FOV of ship's autoaim, if any", "number", "FOV (in degrees), or 0 if ship uses no autoaim or if handle is invalid")
+{
+ object_h *objh;
+ float fov = -1;
+ if(!ade_get_args(L, "o|f", l_Ship.GetPtr(&objh), &fov))
+ return ade_set_error(L, "f", 0.0f);
+
+ if(!objh->IsValid())
+ return ade_set_error(L, "f", 0.0f);
+
+ ship *shipp = &Ships[objh->objp->instance];
+
+ if(ADE_SETTING_VAR && fov >= 0.0f) {
+        if (fov > 180.0)
+            fov = 180.0;
+
+ shipp->autoaim_fov = fov * PI / 180.0f;
+    }
+
+ return ade_set_args(L, "f", shipp->autoaim_fov * 180.0f / PI);
+}
+
 ADE_VIRTVAR(PrimaryTriggerDown, l_Ship, "boolean", "Determines if primary trigger is pressed or not", "boolean", "True if pressed, false if not, nil if ship handle is invalid")
 {
  object_h *objh;
Index: ship/ship.cpp
===================================================================
--- ship/ship.cpp (revision 9640)
+++ ship/ship.cpp (working copy)
@@ -706,6 +706,8 @@
  sip->minimum_convergence_distance = 0.0f;
  sip->convergence_distance = 100.0f;
  vm_vec_zero(&sip->convergence_offset);
+ sip->autoaim_lock_snd = -1;
+ sip->autoaim_lost_snd = -1;
 
  sip->warpin_snd_start = -1;
  sip->warpout_snd_start = -1;
@@ -1952,20 +1954,27 @@
 
  if(optional_string("$Autoaim FOV:"))
  {
- int fov_temp;
- stuff_int(&fov_temp);
+ float fov_temp;
+ stuff_float(&fov_temp);
 
  // Make sure it is a reasonable value
- fov_temp = (((fov_temp % 360) + 360) % 360) / 2;
+ if (fov_temp < 0.0f)
+ fov_temp = 0.0f;
 
+ if (fov_temp > 180.0f)
+ fov_temp = 180.0f;
+
  sip->aiming_flags |= AIM_FLAG_AUTOAIM;
- sip->autoaim_fov = (float)fov_temp * PI / 180.0f;
+ sip->autoaim_fov = fov_temp * PI / 180.0f;
 
  if(optional_string("+Converging Autoaim"))
  sip->aiming_flags |= AIM_FLAG_AUTOAIM_CONVERGENCE;
 
  if(optional_string("+Minimum Distance:"))
  stuff_float(&sip->minimum_convergence_distance);
+
+ parse_sound("+Autoaim Lock Snd:", &sip->autoaim_lock_snd, sip->name);
+ parse_sound("+Autoaim Lost Snd:", &sip->autoaim_lost_snd, sip->name);
  }
 
  if(optional_string("$Convergence:"))
@@ -5121,6 +5130,8 @@
  shipp->secondary_team_name = "<none>";
  shipp->team_change_time = 0;
  shipp->team_change_timestamp = 0;
+
+ shipp->autoaim_fov = sip->autoaim_fov;
 }
 
 /**
@@ -9701,6 +9712,70 @@
 
 float ship_get_subsystem_strength( ship *shipp, int type );
 
+/**
+ * Checks whether a ship would use autoaim if it were to fire its primaries at
+ * the given object, taking lead into account.
+ *
+ * @param shipp the ship to check
+ * @param bank_to_fire the assumed primary bank
+ * @param obj the object to check; must be the ship's current target
+ */
+
+bool in_autoaim_fov(ship *shipp, int bank_to_fire, object *obj)
+{
+ // Most of the code of this function has been duplicated from
+ // ship_fire_primary(), because it is not easy to encapsulate cleanly.
+
+ ship_info *sip;
+ ai_info *aip;
+ ship_weapon *swp;
+
+ bool has_autoaim, has_converging_autoaim;
+ float autoaim_fov = 0;
+ float dist_to_target = 0;
+
+ vec3d plr_to_target_vec;
+ vec3d player_forward_vec = Objects[shipp->objnum].orient.vec.fvec;
+
+ vec3d target_position;
+
+ sip = &Ship_info[shipp->ship_info_index];
+ aip = &Ai_info[shipp->ai_index];
+
+ swp = &shipp->weapons;
+ int weapon = swp->primary_bank_weapons[bank_to_fire];
+ weapon_info* winfo_p = &Weapon_info[weapon];
+
+ has_converging_autoaim = ((sip->aiming_flags & AIM_FLAG_AUTOAIM_CONVERGENCE || (The_mission.ai_profile->player_autoaim_fov[Game_skill_level] > 0.0f && !( Game_mode & GM_MULTIPLAYER ))) && aip->target_objnum != -1);
+ has_autoaim = ((has_converging_autoaim || (sip->aiming_flags & AIM_FLAG_AUTOAIM)) && aip->target_objnum != -1);
+
+ if (!has_autoaim)
+ return FALSE;
+
+ autoaim_fov = MAX(shipp->autoaim_fov, The_mission.ai_profile->player_autoaim_fov[Game_skill_level]);
+
+ if (aip->targeted_subsys != NULL)
+ {
+ get_subsystem_world_pos(&Objects[aip->target_objnum], aip->targeted_subsys, &target_position);
+ }
+ else
+ {
+ target_position = obj->pos;
+ }
+
+ dist_to_target = vm_vec_dist_quick(&Objects[shipp->objnum].pos, &target_position);
+
+ hud_calculate_lead_pos(&plr_to_target_vec, &target_position, obj, winfo_p, dist_to_target);
+ vm_vec_sub2(&plr_to_target_vec, &Objects[shipp->objnum].pos);
+
+ float angle_to_target = vm_vec_delta_ang(&player_forward_vec, &plr_to_target_vec, NULL);
+
+ if (angle_to_target <= autoaim_fov)
+ return true;
+ else
+ return false;
+}
+
 // fires a primary weapon for the given object.  It also handles multiplayer cases.
 // in multiplayer, the starting network signature, and number of banks fired are sent
 // to all the clients in the game. All the info is passed to send_primary at the end of
@@ -9805,7 +9880,7 @@
 
  if (needs_target_pos) {
  if (has_autoaim) {
- autoaim_fov = MAX(sip->autoaim_fov, The_mission.ai_profile->player_autoaim_fov[Game_skill_level]);
+ autoaim_fov = MAX(shipp->autoaim_fov, The_mission.ai_profile->player_autoaim_fov[Game_skill_level]);
  }
 
  // If a subsystem is targeted, fire in that direction instead
Index: ship/ship.h
===================================================================
--- ship/ship.h (revision 9640)
+++ ship/ship.h (working copy)
@@ -795,6 +795,7 @@
  fix team_change_timestamp;
  int team_change_time;
 
+ float autoaim_fov;
 } ship;
 
 struct ai_target_priority {
@@ -1403,6 +1404,7 @@
  float minimum_convergence_distance;
  float convergence_distance;
  vec3d convergence_offset;
+ int autoaim_lock_snd, autoaim_lost_snd;
 
  float emp_resistance_mod;
 
@@ -1577,6 +1579,7 @@
 extern void ship_destroy_instantly(object *ship_obj, int shipnum);
 extern void ship_actually_depart(int shipnum, int method = SHIP_DEPARTED_WARP);
 
+extern bool in_autoaim_fov(ship *shipp, int bank_to_fire, object *obj);
 extern int ship_fire_primary_debug(object *objp); // Fire the debug laser.
 extern int ship_stop_fire_primary(object * obj);
 extern int ship_fire_primary(object * objp, int stream_weapons, int force = 0);

Lua function gr.drawArc for drawing filled and unfilled arcs, and allowing gr.drawCircle to draw unfilled circles:

Code: [Select]
Index: graphics/2d.h
===================================================================
--- graphics/2d.h (revision 9640)
+++ graphics/2d.h (working copy)
@@ -317,6 +317,8 @@
  void (*gf_gradient)(int x1, int y1, int x2, int y2, bool resize);
 
  void (*gf_circle)(int x, int y, int r, bool resize);
+ void (*gf_unfilled_circle)(int x, int y, int r, bool resize);
+ void (*gf_arc)(int x, int y, float r, float angle_start, float angle_end, bool fill, bool resize);
  void (*gf_curve)(int x, int y, int r, int direction);
 
  // Integer line. Used to draw a fast but pixely line. 
@@ -652,6 +654,17 @@
 {
  (*gr_screen.gf_circle)(xc,yc,d,resize);
 }
+
+__inline void gr_unfilled_circle(int xc, int yc, int d, bool resize = true)
+{
+ (*gr_screen.gf_unfilled_circle)(xc,yc,d,resize);
+}
+
+__inline void gr_arc(int xc, int yc, float r, float angle_start, float angle_end, bool fill, bool resize = true)
+{
+ (*gr_screen.gf_arc)(xc,yc,r,angle_start,angle_end,fill,resize);
+}
+
 #define gr_curve GR_CALL(gr_screen.gf_curve)
 
 __inline void gr_line(int x1, int y1, int x2, int y2, bool resize = true)
Index: graphics/gropengl.cpp
===================================================================
--- graphics/gropengl.cpp (revision 9640)
+++ graphics/gropengl.cpp (working copy)
@@ -1773,6 +1773,8 @@
 // gr_screen.gf_shade = gr_opengl_shade;
  gr_screen.gf_string = gr_opengl_string;
  gr_screen.gf_circle = gr_opengl_circle;
+ gr_screen.gf_unfilled_circle = gr_opengl_unfilled_circle;
+ gr_screen.gf_arc = gr_opengl_arc;
  gr_screen.gf_curve = gr_opengl_curve;
 
  gr_screen.gf_line = gr_opengl_line;
Index: graphics/gropengldraw.cpp
===================================================================
--- graphics/gropengldraw.cpp (revision 9640)
+++ graphics/gropengldraw.cpp (working copy)
@@ -802,58 +802,219 @@
 
 void gr_opengl_circle(int xc, int yc, int d, bool resize)
 {
- int p, x, y, r;
+ gr_opengl_arc(xc, yc, d / 2.0f, 0.0f, 360.0f, true, resize);
+}
 
+void gr_opengl_unfilled_circle(int xc, int yc, int d, bool resize)
+{
+ int r = d / 2;
+ int segments = MAX((int)(r), 8); // seems like a good approximation
+ float theta = 2 * PI / float(segments - 1);
+ float c = cosf(theta);
+ float s = sinf(theta);
+ float t;
+
+ float x1 = 1.0f;
+ float y1 = 0.0f;
+ float x2 = x1;
+ float y2 = y1;
+
+ float linewidth;
+ glGetFloatv(GL_LINE_WIDTH, &linewidth);
+
+ float halflinewidth = linewidth / 2.0f;
+ float inner_rad = r - halflinewidth;
+ float outer_rad = r + halflinewidth;
+
+ int do_resize = 0;
+
  if ( resize && (gr_screen.custom_size || (gr_screen.rendering_to_texture != -1)) ) {
  gr_resize_screen_pos(&xc, &yc);
+ do_resize = 1;
  }
 
- r = d / 2;
- p = 3 - d;
- x = 0;
- y = r;
+ // Big clip
+ if ( (xc+outer_rad) < gr_screen.clip_left ) {
+ return;
+ }
 
+ if ( (xc-outer_rad) > gr_screen.clip_right ) {
+ return;
+ }
+
+ if ( (yc+outer_rad) < gr_screen.clip_top ) {
+ return;
+ }
+
+ if ( (yc-outer_rad) > gr_screen.clip_bottom ) {
+ return;
+ }
+
+ int offset_x = ((do_resize) ? gr_screen.offset_x_unscaled : gr_screen.offset_x);
+ int offset_y = ((do_resize) ? gr_screen.offset_y_unscaled : gr_screen.offset_y);
+
+ GL_state.SetTextureSource(TEXTURE_SOURCE_NONE);
+ GL_state.SetAlphaBlendMode(ALPHA_BLEND_ALPHA_BLEND_ALPHA);
+ GL_state.SetZbufferType(ZBUFFER_TYPE_NONE);
+
+ GLfloat *circle = new GLfloat[segments * 4];
+
+ for (int i=0; i < segments * 4; i+=4) {
+ circle[i] = i2fl(xc + (x2 * outer_rad) + offset_x);
+ circle[i+1] = i2fl(yc + (y2 * outer_rad) + offset_y);
+
+ circle[i+2] = i2fl(xc + (x2 * inner_rad) + offset_x);
+ circle[i+3] = i2fl(yc + (y2 * inner_rad) + offset_y);
+
+ t = x2;
+ x2 = c * x1 - s * y1;
+ y2 = s * t + c * y1;
+
+ x1 = x2;
+ y1 = y2;
+ }
+
+ gr_opengl_set_2d_matrix();
+
+ GL_state.Color(gr_screen.current_color.red, gr_screen.current_color.green, gr_screen.current_color.blue, gr_screen.current_color.alpha);
+
+ GL_state.Array.EnableClientVertex();
+ GL_state.Array.VertexPointer(2, GL_FLOAT, 0, circle);
+
+ glDrawArrays(GL_QUAD_STRIP, 0, segments * 2);
+
+ GL_state.Array.DisableClientVertex();
+
+ GL_CHECK_FOR_ERRORS("end of opengl_unfilled_circle()");
+
+ gr_opengl_end_2d_matrix();
+
+ delete [] circle;
+}
+
+void gr_opengl_arc(int xc, int yc, float r, float angle_start, float angle_end, bool fill, bool resize)
+{
+ // Ensure that angle_start < angle_end
+ if (angle_end < angle_start) {
+ float temp = angle_start;
+ angle_start = angle_end;
+ angle_end = temp;
+ }
+
+ float arc_length_ratio;
+ arc_length_ratio = MIN(angle_end - angle_start, 360.0f) / 360.0f;
+
+ // This seems a good approximation of how many segments are needed
+ int segments = MAX((int)(r * arc_length_ratio), 4);
+ float theta = 2 * PI / float(segments - 1) * arc_length_ratio;
+ float c = cosf(theta);
+ float s = sinf(theta);
+ float t;
+
+ float x1 = cosf(ANG_TO_RAD(angle_start));
+ float y1 = sinf(ANG_TO_RAD(angle_start));
+ float x2 = x1;
+ float y2 = y1;
+
+ float halflinewidth = 0.0f;
+ float inner_rad = 0.0f; // only used if fill==false
+ float outer_rad = r;
+
+ if (!fill) {
+ float linewidth;
+ glGetFloatv(GL_LINE_WIDTH, &linewidth);
+
+ halflinewidth = linewidth / 2.0f;
+ inner_rad = r - halflinewidth;
+ outer_rad = r + halflinewidth;
+ }
+
+ int do_resize = 0;
+
+ if ( resize && (gr_screen.custom_size || (gr_screen.rendering_to_texture != -1)) ) {
+ gr_resize_screen_pos(&xc, &yc);
+ do_resize = 1;
+ }
+
  // Big clip
- if ( (xc+r) < gr_screen.clip_left ) {
+ if ( (xc+outer_rad) < gr_screen.clip_left ) {
  return;
  }
 
- if ( (xc-r) > gr_screen.clip_right ) {
+ if ( (xc-outer_rad) > gr_screen.clip_right ) {
  return;
  }
 
- if ( (yc+r) < gr_screen.clip_top ) {
+ if ( (yc+outer_rad) < gr_screen.clip_top ) {
  return;
  }
 
- if ( (yc-r) > gr_screen.clip_bottom ) {
+ if ( (yc-outer_rad) > gr_screen.clip_bottom ) {
  return;
  }
 
- while (x < y) {
- // Draw the first octant
- gr_opengl_line(xc-y, yc-x, xc+y, yc-x, false);
- if (x > 0) // Don't draw the center horizontal line twice
- gr_opengl_line(xc-y, yc+x, xc+y, yc+x, false);
+ int offset_x = ((do_resize) ? gr_screen.offset_x_unscaled : gr_screen.offset_x);
+ int offset_y = ((do_resize) ? gr_screen.offset_y_unscaled : gr_screen.offset_y);
 
- if (p < 0) {
- p += (x << 2) + 6;
- } else {
- // Draw the second octant
- gr_opengl_line(xc-x, yc-y, xc+x, yc-y, false);
- gr_opengl_line(xc-x, yc+y, xc+x, yc+y, false);
+ GL_state.SetTextureSource(TEXTURE_SOURCE_NONE);
+ GL_state.SetAlphaBlendMode(ALPHA_BLEND_ALPHA_BLEND_ALPHA);
+ GL_state.SetZbufferType(ZBUFFER_TYPE_NONE);
 
- p += ((x - y) << 2) + 10;
- y--;
+ GLfloat *arc;
+
+ gr_opengl_set_2d_matrix();
+ GL_state.Color(gr_screen.current_color.red, gr_screen.current_color.green, gr_screen.current_color.blue, gr_screen.current_color.alpha);
+ GL_state.Array.EnableClientVertex();
+
+ if (fill) {
+ arc = new GLfloat[segments * 2 + 2];
+
+ arc[0] = i2fl(xc);
+ arc[1] = i2fl(yc);
+
+ for (int i=2; i < segments * 2 + 2; i+=2) {
+ arc[i] = i2fl(xc + (x2 * outer_rad) + offset_x);
+ arc[i+1] = i2fl(yc + (y2 * outer_rad) + offset_y);
+
+ t = x2;
+ x2 = c * x1 - s * y1;
+ y2 = s * t + c * y1;
+
+ x1 = x2;
+ y1 = y2;
  }
 
- x++;
+ GL_state.Array.VertexPointer(2, GL_FLOAT, 0, arc);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, segments + 1);
+ } else {
+ arc = new GLfloat[segments * 4];
+
+ for (int i=0; i < segments * 4; i+=4) {
+ arc[i] = i2fl(xc + (x2 * outer_rad) + offset_x);
+ arc[i+1] = i2fl(yc + (y2 * outer_rad) + offset_y);
+
+ arc[i+2] = i2fl(xc + (x2 * inner_rad) + offset_x);
+ arc[i+3] = i2fl(yc + (y2 * inner_rad) + offset_y);
+
+ t = x2;
+ x2 = c * x1 - s * y1;
+ y2 = s * t + c * y1;
+
+ x1 = x2;
+ y1 = y2;
+ }
+
+ GL_state.Array.VertexPointer(2, GL_FLOAT, 0, arc);
+ glDrawArrays(GL_QUAD_STRIP, 0, segments * 2);
  }
 
- if (x == y) {
- gr_opengl_line(xc-x, yc-y, xc+x, yc-y, false);
- gr_opengl_line(xc-x, yc+y, xc+x, yc+y, false);
- }
+ GL_state.Array.DisableClientVertex();
+
+ GL_CHECK_FOR_ERRORS("end of opengl_arc()");
+
+ gr_opengl_end_2d_matrix();
+
+ delete [] arc;
 }
 
 void gr_opengl_curve(int xc, int yc, int r, int direction)
Index: graphics/gropengldraw.h
===================================================================
--- graphics/gropengldraw.h (revision 9640)
+++ graphics/gropengldraw.h (working copy)
@@ -21,6 +21,8 @@
 void gr_opengl_pixel(int x, int y, bool resize);
 void gr_opengl_gradient(int x1, int y1, int x2, int y2, bool resize);
 void gr_opengl_circle(int xc, int yc, int d, bool resize);
+void gr_opengl_unfilled_circle(int xc, int yc, int d, bool resize);
+void gr_opengl_arc(int xc, int yc, float r, float angle_start, float angle_end, bool fill, bool resize);
 void gr_opengl_curve(int xc, int yc, int r, int direction);
 void gr_opengl_scaler(vertex *va, vertex *vb, bool bw_bitmap );
 void gr_opengl_cross_fade(int bmap1, int bmap2, int x1, int y1, int x2, int y2, float pct);
Index: graphics/grstub.cpp
===================================================================
--- graphics/grstub.cpp (revision 9640)
+++ graphics/grstub.cpp (working copy)
@@ -106,6 +106,10 @@
 {
 }
 
+void gr_stub_unfilled_circle( int xc, int yc, int d, bool resize )
+{
+}
+
 void gr_stub_cleanup(int minimize)
 {
 }
@@ -747,6 +751,7 @@
 // gr_screen.gf_shade = gr_stub_shade;
  gr_screen.gf_string = gr_stub_string;
  gr_screen.gf_circle = gr_stub_circle;
+ gr_screen.gf_unfilled_circle = gr_stub_unfilled_circle;
  gr_screen.gf_curve = gr_stub_curve;
 
  gr_screen.gf_line = gr_stub_line;
Index: parse/lua.cpp
===================================================================
--- parse/lua.cpp (revision 9640)
+++ parse/lua.cpp (working copy)
@@ -12109,22 +12109,46 @@
  return ADE_RETURN_TRUE;
 }
 
-ADE_FUNC(drawCircle, l_Graphics, "number Radius, number X, number Y", "Draws a circle", NULL, NULL)
+ADE_FUNC(drawCircle, l_Graphics, "number Radius, number X, number Y, [boolean Filled=true", "Draws a circle", NULL, NULL)
 {
  if(!Gr_inited)
  return ADE_RETURN_NIL;
 
  int x,y,ra;
+ bool fill = true;
 
- if(!ade_get_args(L, "iii", &ra,&x,&y))
+ if(!ade_get_args(L, "iii|b", &ra,&x,&y,&fill))
  return ADE_RETURN_NIL;
 
- //WMC - Circle takes...diameter.
- gr_circle(x,y, ra*2, false);
+ if (fill) {
+ //WMC - Circle takes...diameter.
+ gr_circle(x,y, ra*2, false);
+ } else {
+ gr_unfilled_circle(x,y, ra*2, false);
+ }
 
  return ADE_RETURN_NIL;
 }
 
+ADE_FUNC(drawArc, l_Graphics, "number Radius, number X, number Y, number StartAngle, number EndAngle, [boolean Filled=true]", "Draws an arc", NULL, NULL)
+{
+ if(!Gr_inited)
+ return ADE_RETURN_NIL;
+
+ int x,y;
+ float ra,angle_start,angle_end;
+ bool fill = true;
+
+ if(!ade_get_args(L, "fiiff|b", &ra,&x,&y,&angle_start,&angle_end,&fill)) {
+ return ADE_RETURN_NIL;
+ }
+
+ gr_arc(x,y, ra, angle_start, angle_end, fill, false);
+
+ return ADE_RETURN_NIL;
+}
+
+
 ADE_FUNC(drawCurve, l_Graphics, "number X, number Y, number Radius", "Draws a curve", NULL, NULL)
 {
  if(!Gr_inited)

Making is-facing accept target objects other than ships:
Code: [Select]
Index: sexp.cpp
===================================================================
--- sexp.cpp (revision 9640)
+++ sexp.cpp (working copy)
@@ -14923,23 +14923,26 @@
  }
  node = CDR(node);
 
- ship *target_shipp = sexp_get_ship_from_node(node);
- if (target_shipp == NULL) {
- // hasn't arrived yet
- if (mission_parse_get_arrival_ship(CTEXT(node)) != NULL) {
- return SEXP_CANT_EVAL;
- }
- // not found and won't arrive: invalid
+ object_ship_wing_point_team oswpt;
+ sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(node));
+
+ if (oswpt.type == OSWPT_TYPE_SHIP && sexp_get_ship_from_node(node) == NULL) {
  return SEXP_KNOWN_FALSE;
  }
+
+ // only true if ship has departed or not yet arrived
+ if (oswpt.type == OSWPT_TYPE_EXITED || oswpt.type == OSWPT_TYPE_PARSE_OBJECT) {
+ return SEXP_CANT_EVAL;
+ }
+
+ origin_objp = &Objects[origin_shipp->objnum];
+ target_objp = oswpt.objp;
+
  node = CDR(node);
 
  double angle = atof(CTEXT(node));
  node = CDR(node);
 
- origin_objp = &Objects[origin_shipp->objnum];
- target_objp = &Objects[target_shipp->objnum];
-
  // check optional distance argument
  if (node > 0 && (sexp_distance3(origin_objp, target_objp) > eval_num(node))) {
  return SEXP_FALSE;
@@ -26612,8 +26615,10 @@
  return OPF_SHIP;
 
  case OP_IS_FACING:
- if (argnum < 2)
+ if (argnum == 0)
  return OPF_SHIP;
+ else if (argnum == 1)
+ return OPF_SHIP_POINT;
  else
  return OPF_POSITIVE;
 
@@ -29836,14 +29841,14 @@
  "\t2:\tAngle in degrees of the forward cone." },
 
  { OP_IS_FACING, "Is Facing (Boolean training operator)\r\n"
- "\tIs true as long as the second ship is within the first ship's specified "
+ "\tIs true as long as the second object is within the first ship's specified "
  "forward cone.  A forward cone is defined as any point that the angle between the "
- "vector of the point and the player, and the forward facing vector is within the "
- "given angle. If the distance between the two ships is greather than "
- "the fourth parameter, this will return false.\r\n\r\n"
+ "vector of the ship and point, and the forward facing vector is within the "
+ "given angle. If the distance between the two is greater than the fourth"
+ "parameter, this will return false.\r\n\r\n"
  "Returns a boolean value.  Takes 3 or 4 argument...\r\n"
  "\t1:\tShip to check from.\r\n"
- "\t2:\tShip to check is within forward cone.\r\n"
+ "\t2:\tObject to check is within forward cone.\r\n"
  "\t3:\tAngle in degrees of the forward cone.\r\n"
  "\t4:\tRange in meters (optional)."},
 

Lua function for checking whether a ship is warp-capable:

Code: [Select]
Index: lua.cpp
===================================================================
--- lua.cpp (revision 9640)
+++ lua.cpp (working copy)
@@ -8861,6 +8861,23 @@
  return ADE_RETURN_TRUE;
 }
 
+ADE_FUNC(canWarp, l_Ship, NULL, "Checks whether ship has a working subspace drive and is allowed to use it", "boolean", "True if successful, or nil if ship handle is invalid")
+{
+ object_h *objh;
+ if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
+ return ADE_RETURN_NIL;
+
+ if(!objh->IsValid())
+ return ADE_RETURN_NIL;
+
+ ship *shipp = &Ships[objh->objp->instance];
+ if(shipp->flags & SF2_NO_SUBSPACE_DRIVE){
+ return ADE_RETURN_FALSE;
+ }
+
+ return ADE_RETURN_TRUE;
+}
+
 // Aardwolf's function for finding if a ship should be drawn as blue on the radar/minimap
 ADE_FUNC(isWarpingIn, l_Ship, NULL, "Checks if ship is warping in", "boolean", "True if the ship is warping in, false or nil otherwise")
 {


EDIT: A few little features for controlling how secondaries behave when launched:

1. A "no homing speed ramp" weapon flag, which removes the hard-coded 1 second long speed ramp-up when a locked-on secondary is fired.
2. An $Acceleration Time tabling option for secondaries, which controls how long it takes for the weapon to attain full speed, regardless of whether it's dumbfired or locked-on.
3. An $Velocity Inherit tabling option to specify the percentage of the parent ship's velocity to inherit when additive weapon velocity is used.

Code: [Select]
Index: weapons.cpp
===================================================================
--- weapons.cpp (revision 9646)
+++ weapons.cpp (working copy)
@@ -648,6 +648,8 @@
  weaponp->wi_flags3 |= WIF3_NOLINK;
  else if (!stricmp(NOX("same emp time for capships"), weapon_strings[i]))
  weaponp->wi_flags3 |= WIF3_USE_EMP_TIME_FOR_CAPSHIP_TURRETS;
+ else if (!stricmp(NOX("no homing speed ramp"), weapon_strings[i]))
+ weaponp->wi_flags3 |= WIF3_NO_HOMING_SPEED_RAMP;
  else
  Warning(LOCATION, "Bogus string in weapon flags: %s\n", weapon_strings[i]);
  }
@@ -837,6 +839,8 @@
 
  wip->mass = 1.0f;
  wip->max_speed = 10.0f;
+ wip->acceleration_time = 0.0f;
+ wip->vel_inherit_amount = 1.0f;
  wip->free_flight_time = 0.0f;
  wip->fire_wait = 1.0f;
  wip->damage = 0.0f;
@@ -1658,6 +1662,14 @@
  }
  }
 
+ if(optional_string("$Acceleration Time:")) {
+ stuff_float(&wip->acceleration_time);
+ }
+
+ if(optional_string("$Velocity Inherit:")) {
+ stuff_float(&wip->vel_inherit_amount);
+ }
+
  if(optional_string("$Free Flight Time:")) {
  stuff_float(&(wip->free_flight_time));
  } else if(first_time && is_homing) {
@@ -4085,6 +4097,16 @@
  else {
  obj->phys_info.speed = max_speed;
  }
+
+ if (wip->acceleration_time > 0.0f) {
+ if (Missiontime - wp->creation_time < fl2f(wip->acceleration_time)) {
+ float t;
+
+ t = f2fl(Missiontime - wp->creation_time) / wip->acceleration_time;
+ obj->phys_info.speed = wp->launch_speed + (wp->weapon_max_vel - wp->launch_speed) * t;
+ }
+ }
+
  // set velocity using whatever speed we have
  vm_vec_copy_scale( &obj->phys_info.desired_vel, &obj->orient.vec.fvec, obj->phys_info.speed);
 
@@ -4359,8 +4381,18 @@
  } else
  obj->phys_info.speed = max_speed;
 
- // For first second of weapon's life, it doesn't fly at top speed.  It ramps up.
- if (Missiontime - wp->creation_time < i2f(1)) {
+
+ if (wip->acceleration_time > 0.0f) {
+ // Ramp up speed linearly for the given duration
+ if (Missiontime - wp->creation_time < fl2f(wip->acceleration_time)) {
+ float t;
+
+ t = f2fl(Missiontime - wp->creation_time) / wip->acceleration_time;
+ obj->phys_info.speed = wp->launch_speed + (wp->weapon_max_vel - wp->launch_speed) * t;
+ }
+ } else if (!(wip->wi_flags3 & WIF3_NO_HOMING_SPEED_RAMP) && Missiontime - wp->creation_time < i2f(1)) {
+ // Default behavior:
+ // For first second of weapon's life, it doesn't fly at top speed.  It ramps up.
  float t;
 
  t = f2fl(Missiontime - wp->creation_time);
@@ -5192,12 +5224,19 @@
 
  wp->weapon_max_vel = objp->phys_info.max_vel.xyz.z;
 
+ if (wip->acceleration_time > 0.0f)
+ wp->launch_speed = 0.0f;
+
  // Turey - maybe make the initial speed of the weapon take into account the velocity of the parent.
  // Improves aiming during gliding.
  if ((parent_objp != NULL) && (The_mission.ai_profile->flags & AIPF_USE_ADDITIVE_WEAPON_VELOCITY)) {
+ float pspeed = vm_vec_mag( &parent_objp->phys_info.vel );
  vm_vec_add2( &objp->phys_info.vel, &parent_objp->phys_info.vel );
- wp->weapon_max_vel += vm_vec_mag( &parent_objp->phys_info.vel );
+ wp->weapon_max_vel += pspeed * wip->vel_inherit_amount;
  objp->phys_info.speed = vm_vec_mag(&objp->phys_info.vel);
+
+ if (wip->acceleration_time > 0.0f)
+ wp->launch_speed += pspeed;
  }
 
  // create the corkscrew
Index: weapon.h
===================================================================
--- weapon.h (revision 9646)
+++ weapon.h (working copy)
@@ -113,8 +113,9 @@
 #define WIF2_CIWS (1 << 30) // This weapons' burst and shockwave damage can damage bombs (Basically, a reverse for TAKES_BLAST/SHOCKWAVE_DAMAGE
 #define WIF2_ANTISUBSYSBEAM (1 << 31) // This beam can target subsystems as per normal
 
-#define WIF3_NOLINK (1 << 0) // This weapon can not be linked with others
+#define WIF3_NOLINK (1 << 0) // This weapon can not be linked with others
 #define WIF3_USE_EMP_TIME_FOR_CAPSHIP_TURRETS (1 << 1) // override MAX_TURRET_DISRUPT_TIME in emp.cpp - Goober5000
+#define WIF3_NO_HOMING_SPEED_RAMP (1 << 2) // Disables the 1s long speed ramping when firing locked-on secondaries
 
 
 #define WIF_HOMING (WIF_HOMING_HEAT | WIF_HOMING_ASPECT | WIF_HOMING_JAVELIN)
@@ -214,6 +215,8 @@
  float alpha_current; // the current alpha value
 
  float weapon_max_vel; // might just as well store the data here
+ float launch_speed; // the initial forward speed (can vary due to additive velocity or acceleration)
+ // only gets set when weapon_info->acceleration_time is used
 
  bool collisionOccured;
  mc_info collisionInfo; // The last collision of this weapon or NULL if it had none
@@ -336,6 +339,8 @@
  float laser_head_radius, laser_tail_radius;
 
  float max_speed; // initial speed of the weapon
+ float acceleration_time; // how long it takes to reach max speed (secondaries only)
+ float vel_inherit_amount; // how much of the parent ship's velocity is inherited (0.0..1.0)
  float free_flight_time;
  float mass; // mass of the weapon
  float fire_wait; // fire rate -- amount of time before you can refire the weapon

EDIT2: Allows a ship's orders to be accessed and their types queried from Lua:

Code: [Select]
Index: lua.cpp
===================================================================
--- lua.cpp (revision 9646)
+++ lua.cpp (working copy)
@@ -2430,31 +2430,38 @@
 {
  object_h objh;
  int odx;
+ int ai_mode;
  int sig;
 
  order_h() {
  objh = object_h();
  odx = -1;
+ ai_mode = -1;
  sig = -1;
  }
 
- order_h(object *objp, int n_odx)
+ order_h(object *objp, int mode, int n_odx)
  {
  objh = object_h(objp);
- if(objh.IsValid() && objh.objp->type == OBJ_SHIP && odx > -1 && odx < MAX_AI_GOALS)
+ if(objh.IsValid() && objh.objp->type == OBJ_SHIP && n_odx > -1 && n_odx < MAX_AI_GOALS)
  {
  odx = n_odx;
+ ai_mode = mode;
  sig = Ai_info[Ships[objh.objp->instance].ai_index].goals[odx].signature;
  }
  else
  {
  odx = -1;
+ ai_mode = -1;
  sig = -1;
  }
  }
 
  bool IsValid()
  {
+ if (objh.objp == NULL)
+ return false;
+
  return (this != NULL && objh.IsValid() && objh.objp->type == OBJ_SHIP && odx > -1 && odx < MAX_AI_GOALS && sig == Ai_info[Ships[objh.objp->instance].ai_index].goals[odx].signature);
  }
 };
@@ -2477,10 +2484,133 @@
  return ADE_RETURN_TRUE;
 }
 
+ADE_FUNC(getType, l_Order, NULL, "Gets the type of the order.", "enumeration", "The type of the order as one of the ORDER_* enumerations.")
+{
+ enum_h *ordertype = NULL;
+ order_h *ohp = NULL;
+
+ if(!ade_get_args(L, "o|o", l_Order.GetPtr(&ohp), l_Enum.GetPtr(&ordertype)))
+ return ade_set_args(L, "o", l_Enum.Set(enum_h()));
+
+ if(!ohp->IsValid())
+ return ade_set_args(L, "o", l_Enum.Set(enum_h()));
+
+ int enumnum = -1;
+
+ switch(ohp->ai_mode)
+ {
+ case AI_GOAL_CHASE:
+ {
+ enumnum = LE_ORDER_ATTACK;
+ break;
+ }
+ case AI_GOAL_DOCK:
+ {
+ enumnum = LE_ORDER_DOCK;
+ break;
+ }
+ case AI_GOAL_WAYPOINTS:
+ {
+ enumnum = LE_ORDER_WAYPOINTS;
+ break;
+ }
+ case AI_GOAL_WAYPOINTS_ONCE:
+ {
+ enumnum = LE_ORDER_WAYPOINTS_ONCE;
+ break;
+ }
+ case AI_GOAL_WARP:
+ {
+ enumnum = LE_ORDER_DEPART;
+ break;
+ }
+ case AI_GOAL_FORM_ON_WING:
+ {
+ enumnum = LE_ORDER_FORM_ON_WING;
+ break;
+ }
+ case AI_GOAL_UNDOCK:
+ {
+ enumnum = LE_ORDER_UNDOCK;
+ break;
+ }
+ case AI_GOAL_GUARD:
+ {
+ enumnum = LE_ORDER_GUARD;
+ break;
+ }
+ case AI_GOAL_DISABLE_SHIP:
+ {
+ enumnum = LE_ORDER_DISABLE;
+ break;
+ }
+ case AI_GOAL_DISARM_SHIP:
+ {
+ enumnum = LE_ORDER_DISARM;
+ break;
+ }
+ case AI_GOAL_CHASE_ANY:
+ {
+ enumnum = LE_ORDER_ATTACK_ANY;
+ break;
+ }
+ case AI_GOAL_IGNORE_NEW:
+ {
+ enumnum = LE_ORDER_IGNORE;
+ break;
+ }
+ case AI_GOAL_EVADE_SHIP:
+ {
+ enumnum = LE_ORDER_EVADE;
+ break;
+ }
+ case AI_GOAL_STAY_NEAR_SHIP:
+ {
+ enumnum = LE_ORDER_STAY_NEAR;
+ break;
+ }
+ case AI_GOAL_KEEP_SAFE_DISTANCE:
+ {
+ enumnum = LE_ORDER_KEEP_SAFE_DISTANCE;
+ break;
+ }
+ case AI_GOAL_REARM_REPAIR:
+ {
+ enumnum = LE_ORDER_REARM;
+ break;
+ }
+ case AI_GOAL_STAY_STILL:
+ {
+ enumnum = LE_ORDER_STAY_STILL;
+ break;
+ }
+ case AI_GOAL_PLAY_DEAD:
+ {
+ enumnum = LE_ORDER_PLAY_DEAD;
+ break;
+ }
+ case AI_GOAL_FLY_TO_SHIP:
+ {
+ enumnum = LE_ORDER_FLY_TO;
+ }
+ }
+
+ return ade_set_args(L, "o", l_Enum.Set(enumnum));
+}
+
+ADE_FUNC(isValid, l_Order, NULL, "Detects whether handle is valid", "boolean", "true if valid, false if handle is invalid, nil if a syntax/type error occurs")
+{
+ order_h *ohp = NULL;
+ if(!ade_get_args(L, "o", l_Order.GetPtr(&ohp)))
+ return ADE_RETURN_NIL;
+
+ return ade_set_args(L, "b", ohp->IsValid());
+}
+
 //**********HANDLE: shiporders
 ade_obj<object_h> l_ShipOrders("shiporders", "Ship orders");
 
-ADE_FUNC(__len, l_ShipOrders, NULL, "Number of textures on ship", "number", "Number of textures on ship, or 0 if handle is invalid")
+ADE_FUNC(__len, l_ShipOrders, NULL, "Number of orders the ship has", "number", "Number of orders on ship, or 0 if handle is invalid")
 {
  object_h *objh = NULL;
  if(!ade_get_args(L, "o", l_ShipOrders.GetPtr(&objh)))
@@ -2492,55 +2622,25 @@
  return ade_set_args(L, "i", ai_goal_num(&Ai_info[Ships[objh->objp->instance].ai_index].goals[0]));
 }
 
-ADE_INDEXER(l_ShipOrders, "number Index/string TextureFilename", "Array of ship orders", "order", "Order, or invalid texture handle on failure")
+ADE_INDEXER(l_ShipOrders, "number Index", "Array of ship orders", "order", "Order, or invalid order handle on failure")
 {
  object_h *objh = NULL;
- char *s = NULL;
- order_h *oh = NULL;
  int i;
 
- if (!ade_get_args(L, "os|o", l_ShipOrders.GetPtr(&objh), &s, l_Order.GetPtr(&oh)))
+ if (!ade_get_args(L, "oi", l_ShipOrders.GetPtr(&objh), &i))
  return ade_set_error(L, "o", l_Order.Set(order_h()));
 
- if (!objh->IsValid() || s==NULL)
+ i--; //Lua->FS2
+
+ if (!objh->IsValid() || i < 0 || i >= MAX_AI_GOALS)
  return ade_set_error(L, "o", l_Order.Set(order_h()));
 
  ai_info *aip = &Ai_info[Ships[objh->objp->instance].ai_index];
 
- //Determine index
- int idx = atoi(s) - 1; //Lua->FS2
-
- if (idx < 0 || idx >= MAX_AI_GOALS)
- return ade_set_error(L, "o", l_Order.Set(order_h()));
-
- int num = 0;
- for(i = 0; i < MAX_AI_GOALS; i++)
- {
- if(aip->goals[i].ai_mode != AI_GOAL_NONE)
- {
- if(idx == num)
- break;
-
- num++;
- }
- }
-
- if(i >= MAX_AI_GOALS)
- return ade_set_error(L, "o", l_Order.Set(order_h()));
-
- if (ADE_SETTING_VAR)
- {
- if(!oh->IsValid())
- {
- ai_remove_ship_goal(aip, i);
- }
- else
- {
- aip->goals[i] = Ai_info[Ships[oh->objh.objp->instance].ai_index].goals[oh->odx];
- }
- }
-
- return ade_set_args(L, "o", l_Order.Set(order_h(objh->objp, i)));
+ if (aip->goals[i].ai_mode != AI_GOAL_NONE)
+ return ade_set_args(L, "o", l_Order.Set(order_h(objh->objp, aip->goals[i].ai_mode, i)));
+ else
+ return ade_set_args(L, "o", l_Order.Set(order_h()));
 }
 
 ADE_FUNC(isValid, l_ShipOrders, NULL, "Detects whether handle is valid", "boolean", "true if valid, false if handle is invalid, nil if a syntax/type error occurs")
@@ -8190,6 +8290,18 @@
  return ade_set_args(L, "o", l_Team.Set(shipp->team));
 }
 
+ADE_VIRTVAR(Orders, l_Ship, "shiporders", "Gets ship orders", "shiporders", "Ship orders, or invalid shiporders handle if ship handle is invalid")
+{
+ object_h *dh;
+ if(!ade_get_args(L, "o", l_Ship.GetPtr(&dh)))
+ return ade_set_error(L, "o", l_ShipOrders.Set(object_h()));
+
+ if(!dh->IsValid())
+ return ade_set_error(L, "o", l_ShipOrders.Set(object_h()));
+
+ return ade_set_args(L, "o", l_ShipOrders.Set(object_h(dh->objp)));
+}
+
 ADE_VIRTVAR(Textures, l_Ship, "shiptextures", "Gets ship textures", "shiptextures", "Ship textures, or invalid shiptextures handle if ship handle is invalid")
 {
  object_h *sh;
« Last Edit: April 24, 2013, 03:36:49 pm by zookeeper »

 

Offline headdie

  • i don't use punctuation lol
  • 212
  • Lawful Neutral with a Chaotic outook
    • Skype
    • Twitter
    • Headdie on Deviant Art
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Some interesting looking stuff in there
Minister of Interstellar Affairs Sol Union - Retired
quote General Battuta - "FRED is canon!"
Contact me at [email protected]
My Release Thread, Old Release Thread, Celestial Objects Thread, My rubbish attempts at art

 

Offline chief1983

  • Still lacks a custom title
  • Moderator
  • 212
  • ⬇️⬆️⬅️⬅️🅰➡️⬇️
    • Skype
    • Steam
    • Twitter
    • Fate of the Galaxy
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
The changes are all sponsored by FotG too.
Fate of the Galaxy - Now Hiring!  Apply within | Diaspora | SCP Home | Collada Importer for PCS2
Karajorma's 'How to report bugs' | Mantis
#freespace | #scp-swc | #diaspora | #SCP | #hard-light on EsperNet

"You may not sell or otherwise commercially exploit the source or things you created based on the source." -- Excerpt from FSO license, for reference

Nuclear1:  Jesus Christ zack you're a little too hamyurger for HLP right now...
iamzack:  i dont have hamynerge i just want ptatoc hips D:
redsniper:  Platonic hips?!
iamzack:  lays

 

Offline Dragon

  • Citation needed
  • 212
  • The sky is the limit.
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Does this also allow to change the reticle when there's no autoaim, but you have the target perfectly in your reticle?

 

Offline zookeeper

  • *knock knock* Who's there? Poe. Poe who?
  • 210
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Does this also allow to change the reticle when there's no autoaim, but you have the target perfectly in your reticle?

Nope. You'd have to give the player's ship a tiny FOV radius for that.

 

Offline Dragon

  • Citation needed
  • 212
  • The sky is the limit.
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Would that be hard to add this feature? I think it'd be nicer than a dinky FOV hack.

 

Offline General Battuta

  • Poe's Law In Action
  • 214
  • i wonder when my postcount will exceed my iq
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Assuming you're using the retail FS2 style gunsight (boresight + lead indicator) rather than the Diaspora disturbed reticle style, do these reticle changes occur when you're aiming at the target, or at the target's leadpoint? Changing the reticle when the target is perfectly in your sights isn't much use if you need to be leading the target.
« Last Edit: April 21, 2013, 12:08:42 pm by General Battuta »

 

Offline Dragon

  • Citation needed
  • 212
  • The sky is the limit.
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Leadpoint, like in Wing Commander. I've got the idea from there.

 

Offline zookeeper

  • *knock knock* Who's there? Poe. Poe who?
  • 210
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Would that be hard to add this feature? I think it'd be nicer than a dinky FOV hack.

Probably not hard, but then I'd need to come up with the new tabling syntax and all, which I don't really want to do.

Assuming you're using the retail FS2 style gunsight (boresight + lead indicator) rather than the Diaspora disturbed reticle style, do these reticle changes occur when you're aiming at the target, or at the target's leadpoint? Changing the reticle when the target is perfectly in your sights isn't much use if you need to be leading the target.

At the target's leadpoint. Also, currently both the sounds and reticle change work
only with +Lead Indicator as +Lead Sight is an entirely separate gauge code-wise, but it can't be hard to port this over to that. I think I'll do that before I commit anything.

 

Offline Dragon

  • Citation needed
  • 212
  • The sky is the limit.
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Probably not hard, but then I'd need to come up with the new tabling syntax and all, which I don't really want to do.
You wouldn't. It'd default to this behavior when no autoaim is specified. Defining autoaim would enable the current behavior. I don't see what's there to control in the table.

 

Offline zookeeper

  • *knock knock* Who's there? Poe. Poe who?
  • 210
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Probably not hard, but then I'd need to come up with the new tabling syntax and all, which I don't really want to do.
You wouldn't. It'd default to this behavior when no autoaim is specified. Defining autoaim would enable the current behavior. I don't see what's there to control in the table.

The sound references. With my patch you'd activate the sounds like this:

Code: [Select]
$Autoaim FOV: 8
  +Converging Autoaim
  +Minimum Distance: 100
  +Autoaim Lock Snd: 29
  +Autoaim Lost Snd: 8

So they'd either need to be moved out of the FOV definitions or duplicated there. Easily done but I don't fancy coming up with the names.

 

Offline Dragon

  • Citation needed
  • 212
  • The sky is the limit.
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Do +Gun On Target Snd: and +Gun Off Target Snd: sound good?

 

Offline zookeeper

  • *knock knock* Who's there? Poe. Poe who?
  • 210
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Do +Gun On Target Snd: and +Gun Off Target Snd: sound good?

Yeah. I'll see what I can do, but no promises.

 

Offline Swifty

  • 210
  • I reject your fantasy & substitute my own
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
I really don't like polluting the HUD table with more global switches.

 

Offline zookeeper

  • *knock knock* Who's there? Poe. Poe who?
  • 210
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
I really don't like polluting the HUD table with more global switches.

Fair enough, I suppose I can move it to the appropriate individual gauge definition(s).

 

Offline Swifty

  • 210
  • I reject your fantasy & substitute my own
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Yeah that would be very much preferable, thanks.

 

Offline zookeeper

  • *knock knock* Who's there? Poe. Poe who?
  • 210
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Added one more patch to the first post, this time a few secondary-related features.

EDIT: And also added one to expose ship orders to Lua a bit better.
« Last Edit: April 24, 2013, 03:37:44 pm by zookeeper »

 

Offline Alan Bolte

  • 28
  • Deneb III
    • @Compellor
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Oooh, acceleration. You can make rockets that actually move like rockets.
Anything worth doing is worth analyzing to death -Iranon

 

Offline Spoon

  • 212
  • ヾ(´︶`♡)ノ
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
Quote
2. An $Acceleration Time tabling option for secondaries, which controls how long it takes for the weapon to attain full speed, regardless of whether it's dumbfired or locked-on.
Do want.
Urutorahappī!!

[02:42] <@Axem> spoon somethings wrong
[02:42] <@Axem> critically wrong
[02:42] <@Axem> im happy with these missions now
[02:44] <@Axem> well
[02:44] <@Axem> with 2 of them

 

Offline Dragon

  • Citation needed
  • 212
  • The sky is the limit.
Re: [CODE REVIEW] zookeeper's post-3.7.0 features
I want it all. :) This is absolutely necessary for some things I'd like to do.