1 //
2 //
3 
4 
5 #include "ship.h"
6 #include "texture.h"
7 #include "object.h"
8 #include "subsystem.h"
9 #include "shipclass.h"
10 #include "cockpit_display.h"
11 #include "weaponclass.h"
12 #include "ship_bank.h"
13 #include "team.h"
14 #include "order.h"
15 #include "enums.h"
16 #include "wing.h"
17 #include "vecmath.h"
18 
19 #include "ship/shiphit.h"
20 #include "hud/hudshield.h"
21 #include "playerman/player.h"
22 #include "mission/missionlog.h"
23 #include "ai/aigoals.h"
24 #include "ship/shipfx.h"
25 #include "hud/hudets.h"
26 #include "object/object.h"
27 #include "model/model.h"
28 #include "ship/ship.h"
29 #include "parse/parselo.h"
30 
31 extern void ship_reset_disabled_physics(object *objp, int ship_class);
32 
33 namespace scripting {
34 namespace api {
35 
36 //**********HANDLE: shiptextures
37 ADE_OBJ(l_ShipTextures, object_h, "shiptextures", "Ship textures handle");
38 
39 ADE_FUNC(__len, l_ShipTextures, NULL, "Number of textures on ship", "number", "Number of textures on ship, or 0 if handle is invalid")
40 {
41 	object_h *objh;
42 	if(!ade_get_args(L, "o", l_ShipTextures.GetPtr(&objh)))
43 		return ade_set_error(L, "i", 0);
44 
45 	if(!objh->IsValid())
46 		return ade_set_error(L, "i", 0);
47 
48 	polymodel *pm = model_get(Ship_info[Ships[objh->objp->instance].ship_info_index].model_num);
49 
50 	if(pm == NULL)
51 		return ade_set_error(L, "i", 0);
52 
53 	return ade_set_args(L, "i", pm->n_textures*TM_NUM_TYPES);
54 }
55 
56 ADE_INDEXER(l_ShipTextures, "number/string IndexOrTextureFilename", "Array of ship textures", "texture", "Texture, or invalid texture handle on failure")
57 {
58 	object_h *oh;
59 	const char* s;
60 	texture_h* tdx = nullptr;
61 	if (!ade_get_args(L, "os|o", l_ShipTextures.GetPtr(&oh), &s, l_Texture.GetPtr(&tdx)))
62 		return ade_set_error(L, "o", l_Texture.Set(texture_h()));
63 
64 	if (!oh->IsValid() || s==NULL)
65 		return ade_set_error(L, "o", l_Texture.Set(texture_h()));
66 
67 	ship *shipp = &Ships[oh->objp->instance];
68 	polymodel *pm = model_get(Ship_info[shipp->ship_info_index].model_num);
69 	int final_index = -1;
70 	int i;
71 
72 	char fname[MAX_FILENAME_LEN];
73 	if (shipp->ship_replacement_textures != NULL)
74 	{
75 		for(i = 0; i < MAX_REPLACEMENT_TEXTURES; i++)
76 		{
77 			bm_get_filename(shipp->ship_replacement_textures[i], fname);
78 
79 			if(!strextcmp(fname, s)) {
80 				final_index = i;
81 				break;
82 			}
83 		}
84 	}
85 
86 	if(final_index < 0)
87 	{
88 		for (i = 0; i < pm->n_textures; i++)
89 		{
90 			int tm_num = pm->maps[i].FindTexture(s);
91 			if(tm_num > -1)
92 			{
93 				final_index = i*TM_NUM_TYPES+tm_num;
94 				break;
95 			}
96 		}
97 	}
98 
99 	if (final_index < 0)
100 	{
101 		final_index = atoi(s) - 1;	//Lua->FS2
102 
103 		if (final_index < 0 || final_index >= MAX_REPLACEMENT_TEXTURES)
104 			return ade_set_error(L, "o", l_Texture.Set(texture_h()));
105 	}
106 
107 	if (ADE_SETTING_VAR) {
108 		if (shipp->ship_replacement_textures == NULL) {
109 			shipp->ship_replacement_textures = (int *) vm_malloc(MAX_REPLACEMENT_TEXTURES * sizeof(int));
110 
111 			for (i = 0; i < MAX_REPLACEMENT_TEXTURES; i++)
112 				shipp->ship_replacement_textures[i] = -1;
113 		}
114 
115 		if (tdx != nullptr) {
116 			if (tdx->isValid())
117 				shipp->ship_replacement_textures[final_index] = tdx->handle;
118 			else
119 				shipp->ship_replacement_textures[final_index] = -1;
120 		}
121 	}
122 
123 	if (shipp->ship_replacement_textures != NULL && shipp->ship_replacement_textures[final_index] >= 0)
124 		return ade_set_args(L, "o", l_Texture.Set(texture_h(shipp->ship_replacement_textures[final_index])));
125 	else
126 		return ade_set_args(L, "o", l_Texture.Set(texture_h(pm->maps[final_index / TM_NUM_TYPES].textures[final_index % TM_NUM_TYPES].GetTexture())));
127 }
128 
129 ADE_FUNC(isValid, l_ShipTextures, NULL, "Detects whether handle is valid", "boolean", "true if valid, false if handle is invalid, nil if a syntax/type error occurs")
130 {
131 	object_h *oh;
132 	if(!ade_get_args(L, "o", l_ShipTextures.GetPtr(&oh)))
133 		return ADE_RETURN_NIL;
134 
135 	return ade_set_args(L, "b", oh->IsValid());
136 }
137 
138 //**********HANDLE: Ship
139 ADE_OBJ_DERIV(l_Ship, object_h, "ship", "Ship handle", l_Object);
140 
141 ADE_INDEXER(l_Ship, "string/number NameOrIndex", "Array of ship subsystems", "subsystem", "Subsystem handle, or invalid subsystem handle if index or ship handle is invalid")
142 {
143 	object_h *objh;
144 	const char* s      = nullptr;
145 	ship_subsys_h *sub = nullptr;
146 	if(!ade_get_args(L, "o|so", l_Ship.GetPtr(&objh), &s, l_Subsystem.GetPtr(&sub)))
147 		return ade_set_error(L, "o", l_Subsystem.Set(ship_subsys_h()));
148 
149 	if(!objh->IsValid())
150 		return ade_set_error(L, "o", l_Subsystem.Set(ship_subsys_h()));
151 
152 	ship *shipp = &Ships[objh->objp->instance];
153 	ship_subsys *ss = ship_get_subsys(shipp, s);
154 
155 	if(ss == NULL)
156 	{
157 		int idx = atoi(s);
158 		if(idx > 0 && idx <= ship_get_num_subsys(shipp))
159 		{
160 			idx--; //Lua->FS2
161 			ss = ship_get_indexed_subsys(shipp, idx);
162 		}
163 	}
164 
165 	if(ss == NULL)
166 		return ade_set_error(L, "o", l_Subsystem.Set(ship_subsys_h()));
167 
168 	return ade_set_args(L, "o", l_Subsystem.Set(ship_subsys_h(objh->objp, ss)));
169 }
170 
171 ADE_FUNC(__len, l_Ship, NULL, "Number of subsystems on ship", "number", "Subsystem number, or 0 if handle is invalid")
172 {
173 	object_h *objh;
174 	if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
175 		return ade_set_error(L, "i", 0);
176 
177 	if(!objh->IsValid())
178 		return ade_set_error(L, "i", 0);
179 
180 	return ade_set_args(L, "i", ship_get_num_subsys(&Ships[objh->objp->instance]));
181 }
182 
183 ADE_VIRTVAR(ShieldArmorClass, l_Ship, "string", "Current Armor class of the ships' shield", "string", "Armor class name, or empty string if none is set")
184 {
185 	object_h *objh;
186 	const char* s    = nullptr;
187 	const char *name = nullptr;
188 
189 	if(!ade_get_args(L, "o|s", l_Ship.GetPtr(&objh), &s))
190 		return ade_set_error(L, "s", "");
191 
192 	if(!objh->IsValid())
193 		return ade_set_error(L, "s", "");
194 
195 	ship *shipp = &Ships[objh->objp->instance];
196 	int atindex;
197 	if (ADE_SETTING_VAR && s != nullptr) {
198 		atindex = armor_type_get_idx(s);
199 		shipp->shield_armor_type_idx = atindex;
200 	} else {
201 		atindex = shipp->shield_armor_type_idx;
202 	}
203 
204 	if (atindex != -1)
205 		name = Armor_types[atindex].GetNamePtr();
206 	else
207 		name = "";
208 
209 	return ade_set_args(L, "s", name);
210 }
211 
212 ADE_VIRTVAR(ArmorClass, l_Ship, "string", "Current Armor class", "string", "Armor class name, or empty string if none is set")
213 {
214 	object_h *objh;
215 	const char* s    = nullptr;
216 	const char *name = nullptr;
217 
218 	if(!ade_get_args(L, "o|s", l_Ship.GetPtr(&objh), &s))
219 		return ade_set_error(L, "s", "");
220 
221 	if(!objh->IsValid())
222 		return ade_set_error(L, "s", "");
223 
224 	ship *shipp = &Ships[objh->objp->instance];
225 	int atindex;
226 	if (ADE_SETTING_VAR && s != nullptr) {
227 		atindex = armor_type_get_idx(s);
228 		shipp->armor_type_idx = atindex;
229 	} else {
230 		atindex = shipp->armor_type_idx;
231 	}
232 
233 	if (atindex != -1)
234 		name = Armor_types[atindex].GetNamePtr();
235 	else
236 		name = "";
237 
238 	return ade_set_args(L, "s", name);
239 }
240 
241 ADE_VIRTVAR(Name, l_Ship, "string", "Ship name. This is the actual name of the ship. Use <i>getDisplayString</i> to get the string which should be displayed to the player.", "string", "Ship name, or empty string if handle is invalid")
242 {
243 	object_h *objh;
244 	const char* s = nullptr;
245 	if(!ade_get_args(L, "o|s", l_Ship.GetPtr(&objh), &s))
246 		return ade_set_error(L, "s", "");
247 
248 	if(!objh->IsValid())
249 		return ade_set_error(L, "s", "");
250 
251 	ship *shipp = &Ships[objh->objp->instance];
252 
253 	if(ADE_SETTING_VAR && s != nullptr) {
254 		auto len = sizeof(shipp->ship_name);
255 		strncpy(shipp->ship_name, s, len);
256 		shipp->ship_name[len - 1] = 0;
257 	}
258 
259 	return ade_set_args(L, "s", shipp->ship_name);
260 }
261 
262 ADE_VIRTVAR(DisplayName, l_Ship, "string", "Ship display name", "string", "The display name of the ship or empty if there is no display string")
263 {
264 	object_h *objh;
265 	const char* s = nullptr;
266 	if(!ade_get_args(L, "o|s", l_Ship.GetPtr(&objh), &s))
267 		return ade_set_error(L, "s", "");
268 
269 	if(!objh->IsValid())
270 		return ade_set_error(L, "s", "");
271 
272 	ship *shipp = &Ships[objh->objp->instance];
273 
274 	if(ADE_SETTING_VAR && s != nullptr) {
275 		shipp->display_name = s;
276 
277 		// for compatibility reasons, if we are setting this to the empty string, clear the flag
278 		shipp->flags.set(Ship::Ship_Flags::Has_display_name, s[0] != 0);
279 	}
280 
281 	return ade_set_args(L, "s", shipp->display_name.c_str());
282 }
283 
284 ADE_VIRTVAR(AfterburnerFuelLeft, l_Ship, "number", "Afterburner fuel left", "number", "Afterburner fuel left, or 0 if handle is invalid")
285 {
286 	object_h *objh;
287 	float fuel = -1.0f;
288 	if(!ade_get_args(L, "o|f", l_Ship.GetPtr(&objh), &fuel))
289 		return ade_set_error(L, "f", 0.0f);
290 
291 	if(!objh->IsValid())
292 		return ade_set_error(L, "f", 0.0f);
293 
294 	ship *shipp = &Ships[objh->objp->instance];
295 
296 	if(ADE_SETTING_VAR && fuel >= 0.0f)
297 		shipp->afterburner_fuel = fuel;
298 
299 	return ade_set_args(L, "f", shipp->afterburner_fuel);
300 }
301 
302 ADE_VIRTVAR(AfterburnerFuelMax, l_Ship, "number", "Afterburner fuel capacity", "number", "Afterburner fuel capacity, or 0 if handle is invalid")
303 {
304 	object_h *objh;
305 	float fuel = -1.0f;
306 	if(!ade_get_args(L, "o|f", l_Ship.GetPtr(&objh), &fuel))
307 		return ade_set_error(L, "f", 0.0f);
308 
309 	if(!objh->IsValid())
310 		return ade_set_error(L, "f", 0.0f);
311 
312 	ship_info *sip = &Ship_info[Ships[objh->objp->instance].ship_info_index];
313 
314 	if(ADE_SETTING_VAR && fuel >= 0.0f)
315 		sip->afterburner_fuel_capacity = fuel;
316 
317 	return ade_set_args(L, "f", sip->afterburner_fuel_capacity);
318 }
319 
320 ADE_VIRTVAR(Class, l_Ship, "shipclass", "Ship class", "shipclass", "Ship class, or invalid shipclass handle if ship handle is invalid")
321 {
322 	object_h *objh;
323 	int idx=-1;
324 	if(!ade_get_args(L, "o|o", l_Ship.GetPtr(&objh), l_Shipclass.Get(&idx)))
325 		return ade_set_error(L, "o", l_Shipclass.Set(-1));
326 
327 	if(!objh->IsValid())
328 		return ade_set_error(L, "o", l_Shipclass.Set(-1));
329 
330 	ship *shipp = &Ships[objh->objp->instance];
331 
332 	if(ADE_SETTING_VAR && idx > -1) {
333 		change_ship_type(objh->objp->instance, idx, 1);
334 		if (shipp == Player_ship) {
335 			set_current_hud();
336 		}
337 	}
338 
339 	if(shipp->ship_info_index < 0)
340 		return ade_set_error(L, "o", l_Shipclass.Set(-1));
341 
342 	return ade_set_args(L, "o", l_Shipclass.Set(shipp->ship_info_index));
343 }
344 
345 ADE_VIRTVAR(CountermeasuresLeft, l_Ship, "number", "Number of countermeasures left", "number", "Countermeasures left, or 0 if ship handle is invalid")
346 {
347 	object_h *objh;
348 	int newcm = -1;
349 	if(!ade_get_args(L, "o|i", l_Ship.GetPtr(&objh), &newcm))
350 		return ade_set_error(L, "i", 0);
351 
352 	if(!objh->IsValid())
353 		return ade_set_error(L, "i", 0);
354 
355 	ship *shipp = &Ships[objh->objp->instance];
356 
357 	if(ADE_SETTING_VAR && newcm > -1)
358 		shipp->cmeasure_count = newcm;
359 
360 	return ade_set_args(L, "i", shipp->cmeasure_count);
361 }
362 
363 ADE_VIRTVAR(CockpitDisplays, l_Ship, "displays", "An array of the cockpit displays on this ship.<br>NOTE: Only the ship of the player has these", "displays", "displays handle or invalid handle on error")
364 {
365 	object_h *objh;
366 	cockpit_displays_h *cdh = NULL;
367 	if(!ade_get_args(L, "o|o", l_Ship.GetPtr(&objh), l_CockpitDisplays.GetPtr(&cdh)))
368 		return ade_set_error(L, "o", l_CockpitDisplays.Set(cockpit_displays_h()));
369 
370 	if(!objh->IsValid())
371 		return ade_set_error(L, "o", l_CockpitDisplays.Set(cockpit_displays_h()));
372 
373 	if(ADE_SETTING_VAR)
374 	{
375 		LuaError(L, "Attempted to use incomplete feature: Cockpit displays copy");
376 	}
377 
378 	return ade_set_args(L, "o", l_CockpitDisplays.Set(cockpit_displays_h(objh->objp)));
379 }
380 
381 ADE_VIRTVAR(CountermeasureClass, l_Ship, "weaponclass", "Weapon class mounted on this ship's countermeasure point", "weaponclass", "Countermeasure hardpoint weapon class, or invalid weaponclass handle if no countermeasure class or ship handle is invalid")
382 {
383 	object_h *objh;
384 	int newcm = -1;
385 	if(!ade_get_args(L, "o|o", l_Ship.GetPtr(&objh), l_Weaponclass.Get(&newcm)))
386 		return ade_set_error(L, "o", l_Weaponclass.Set(-1));;
387 
388 	if(!objh->IsValid())
389 		return ade_set_error(L, "o", l_Weaponclass.Set(-1));;
390 
391 	ship *shipp = &Ships[objh->objp->instance];
392 
393 	if(ADE_SETTING_VAR) {
394 		shipp->current_cmeasure = newcm;
395 	}
396 
397 	if(shipp->current_cmeasure > -1)
398 		return ade_set_args(L, "o", l_Weaponclass.Set(shipp->current_cmeasure));
399 	else
400 		return ade_set_error(L, "o", l_Weaponclass.Set(-1));;
401 }
402 
403 ADE_VIRTVAR(HitpointsMax, l_Ship, "number", "Total hitpoints", "number", "Ship maximum hitpoints, or 0 if handle is invalid")
404 {
405 	object_h *objh;
406 	float newhits = -1;
407 	if(!ade_get_args(L, "o|f", l_Ship.GetPtr(&objh), &newhits))
408 		return ade_set_error(L, "f", 0.0f);
409 
410 	if(!objh->IsValid())
411 		return ade_set_error(L, "f", 0.0f);
412 
413 	ship *shipp = &Ships[objh->objp->instance];
414 
415 	if(ADE_SETTING_VAR && newhits > -1)
416 		shipp->ship_max_hull_strength = newhits;
417 
418 	return ade_set_args(L, "f", shipp->ship_max_hull_strength);
419 }
420 
421 ADE_VIRTVAR(ShieldRegenRate, l_Ship, "number", "Maximum percentage/100 of shield energy regenerated per second. For example, 0.02 = 2% recharge per second.", "number", "Ship maximum shield regeneration rate, or 0 if handle is invalid")
422 {
423 	object_h *objh;
424 	float new_shield_regen = -1;
425 	if (!ade_get_args(L, "o|f", l_Ship.GetPtr(&objh), &new_shield_regen))
426 		return ade_set_error(L, "f", 0.0f);
427 
428 	if(!objh->IsValid())
429 		return ade_set_error(L, "f", 0.0f);
430 
431 	ship *shipp = &Ships[objh->objp->instance];
432 
433 	if (ADE_SETTING_VAR && new_shield_regen > -1)
434 		shipp->max_shield_regen_per_second = new_shield_regen;
435 
436 	return ade_set_args(L, "f", shipp->max_shield_regen_per_second);
437 }
438 
439 ADE_VIRTVAR(WeaponRegenRate, l_Ship, "number", "Maximum percentage/100 of weapon energy regenerated per second. For example, 0.02 = 2% recharge per second.", "number", "Ship maximum weapon regeneration rate, or 0 if handle is invalid")
440 {
441 	object_h *objh;
442 	float new_weapon_regen = -1;
443 	if (!ade_get_args(L, "o|f", l_Ship.GetPtr(&objh), &new_weapon_regen))
444 		return ade_set_error(L, "f", 0.0f);
445 
446 	if(!objh->IsValid())
447 		return ade_set_error(L, "f", 0.0f);
448 
449 	ship *shipp = &Ships[objh->objp->instance];
450 
451 	if (ADE_SETTING_VAR && new_weapon_regen > -1)
452 		shipp->max_weapon_regen_per_second = new_weapon_regen;
453 
454 	return ade_set_args(L, "f", shipp->max_weapon_regen_per_second);
455 }
456 
457 ADE_VIRTVAR(WeaponEnergyLeft, l_Ship, "number", "Current weapon energy reserves", "number", "Ship current weapon energy reserve level, or 0 if invalid")
458 {
459 	object_h *objh;
460 	float neweng = -1;
461 	if(!ade_get_args(L, "o|f", l_Ship.GetPtr(&objh), &neweng))
462 		return ade_set_error(L, "f", 0.0f);
463 
464 	if(!objh->IsValid())
465 		return ade_set_error(L, "f", 0.0f);
466 
467 	ship *shipp = &Ships[objh->objp->instance];
468 
469 	if(ADE_SETTING_VAR && neweng > -1)
470 		shipp->weapon_energy = neweng;
471 
472 	return ade_set_args(L, "f", shipp->weapon_energy);
473 }
474 
475 ADE_VIRTVAR(WeaponEnergyMax, l_Ship, "number", "Maximum weapon energy", "number", "Ship maximum weapon energy reserve level, or 0 if invalid")
476 {
477 	object_h *objh;
478 	float neweng = -1;
479 	if(!ade_get_args(L, "o|f", l_Ship.GetPtr(&objh), &neweng))
480 		return ade_set_error(L, "f", 0.0f);
481 
482 	if(!objh->IsValid())
483 		return ade_set_error(L, "f", 0.0f);
484 
485 	ship_info *sip = &Ship_info[Ships[objh->objp->instance].ship_info_index];
486 
487 	if(ADE_SETTING_VAR && neweng > -1)
488 		sip->max_weapon_reserve = neweng;
489 
490 	return ade_set_args(L, "f", sip->max_weapon_reserve);
491 }
492 
493 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")
494 {
495 	object_h *objh;
496 	float fov = -1;
497 	if(!ade_get_args(L, "o|f", l_Ship.GetPtr(&objh), &fov))
498 		return ade_set_error(L, "f", 0.0f);
499 
500 	if(!objh->IsValid())
501 		return ade_set_error(L, "f", 0.0f);
502 
503 	ship *shipp = &Ships[objh->objp->instance];
504 
505 	if(ADE_SETTING_VAR && fov >= 0.0f) {
506 		if (fov > 180.0)
507 			fov = 180.0;
508 
509 		shipp->autoaim_fov = fov * PI / 180.0f;
510 	}
511 
512 	return ade_set_args(L, "f", shipp->autoaim_fov * 180.0f / PI);
513 }
514 
515 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")
516 {
517 	object_h *objh;
518 	bool trig = false;
519 	if(!ade_get_args(L, "o|b", l_Ship.GetPtr(&objh), &trig))
520 		return ADE_RETURN_NIL;
521 
522 	if(!objh->IsValid())
523 		return ADE_RETURN_NIL;
524 
525 	ship *shipp = &Ships[objh->objp->instance];
526 
527 	if(ADE_SETTING_VAR)
528 	{
529 		if(trig)
530 			shipp->flags.set(Ship::Ship_Flags::Trigger_down);
531 		else
532 			shipp->flags.remove(Ship::Ship_Flags::Trigger_down);
533 	}
534 
535 	if (shipp->flags[Ship::Ship_Flags::Trigger_down])
536 		return ADE_RETURN_TRUE;
537 	else
538 		return ADE_RETURN_FALSE;
539 }
540 
541 
542 ADE_VIRTVAR(PrimaryBanks, l_Ship, "weaponbanktype", "Array of primary weapon banks", "weaponbanktype", "Primary weapon banks, or invalid weaponbanktype handle if ship handle is invalid")
543 {
544 	object_h *objh;
545 	ship_banktype_h *swh = nullptr;
546 	if(!ade_get_args(L, "o|o", l_Ship.GetPtr(&objh), l_WeaponBankType.GetPtr(&swh)))
547 		return ade_set_error(L, "o", l_WeaponBankType.Set(ship_banktype_h()));
548 
549 	if(!objh->IsValid())
550 		return ade_set_error(L, "o", l_WeaponBankType.Set(ship_banktype_h()));
551 
552 	ship_weapon *dst = &Ships[objh->objp->instance].weapons;
553 
554 	if(ADE_SETTING_VAR && swh && swh->IsValid()) {
555 		ship_weapon *src = &Ships[swh->objp->instance].weapons;
556 
557 		dst->current_primary_bank = src->current_primary_bank;
558 		dst->num_primary_banks = src->num_primary_banks;
559 
560 		memcpy(dst->next_primary_fire_stamp, src->next_primary_fire_stamp, sizeof(dst->next_primary_fire_stamp));
561 		memcpy(dst->primary_animation_done_time, src->primary_animation_done_time, sizeof(dst->primary_animation_done_time));
562 		memcpy(dst->primary_animation_position, src->primary_animation_position, sizeof(dst->primary_animation_position));
563 		memcpy(dst->primary_bank_ammo, src->primary_bank_ammo, sizeof(dst->primary_bank_ammo));
564 		memcpy(dst->primary_bank_capacity, src->primary_bank_capacity, sizeof(dst->primary_bank_capacity));
565 		memcpy(dst->primary_bank_rearm_time, src->primary_bank_rearm_time, sizeof(dst->primary_bank_rearm_time));
566 		memcpy(dst->primary_bank_start_ammo, src->primary_bank_start_ammo, sizeof(dst->primary_bank_start_ammo));
567 		memcpy(dst->primary_bank_weapons, src->primary_bank_weapons, sizeof(dst->primary_bank_weapons));
568 	}
569 
570 	return ade_set_args(L, "o", l_WeaponBankType.Set(ship_banktype_h(objh->objp, dst, SWH_PRIMARY)));
571 }
572 
573 ADE_VIRTVAR(SecondaryBanks, l_Ship, "weaponbanktype", "Array of secondary weapon banks", "weaponbanktype", "Secondary weapon banks, or invalid weaponbanktype handle if ship handle is invalid")
574 {
575 	object_h *objh;
576 	ship_banktype_h *swh = nullptr;
577 	if(!ade_get_args(L, "o|o", l_Ship.GetPtr(&objh), l_WeaponBankType.GetPtr(&swh)))
578 		return ade_set_error(L, "o", l_WeaponBankType.Set(ship_banktype_h()));
579 
580 	if(!objh->IsValid())
581 		return ade_set_error(L, "o", l_WeaponBankType.Set(ship_banktype_h()));
582 
583 	ship_weapon *dst = &Ships[objh->objp->instance].weapons;
584 
585 	if(ADE_SETTING_VAR && swh && swh->IsValid()) {
586 		ship_weapon *src = &Ships[swh->objp->instance].weapons;
587 
588 		dst->current_secondary_bank = src->current_secondary_bank;
589 		dst->num_secondary_banks = src->num_secondary_banks;
590 
591 		memcpy(dst->next_secondary_fire_stamp, src->next_secondary_fire_stamp, sizeof(dst->next_secondary_fire_stamp));
592 		memcpy(dst->secondary_animation_done_time, src->secondary_animation_done_time, sizeof(dst->secondary_animation_done_time));
593 		memcpy(dst->secondary_animation_position, src->secondary_animation_position, sizeof(dst->secondary_animation_position));
594 		memcpy(dst->secondary_bank_ammo, src->secondary_bank_ammo, sizeof(dst->secondary_bank_ammo));
595 		memcpy(dst->secondary_bank_capacity, src->secondary_bank_capacity, sizeof(dst->secondary_bank_capacity));
596 		memcpy(dst->secondary_bank_rearm_time, src->secondary_bank_rearm_time, sizeof(dst->secondary_bank_rearm_time));
597 		memcpy(dst->secondary_bank_start_ammo, src->secondary_bank_start_ammo, sizeof(dst->secondary_bank_start_ammo));
598 		memcpy(dst->secondary_bank_weapons, src->secondary_bank_weapons, sizeof(dst->secondary_bank_weapons));
599 		memcpy(dst->secondary_next_slot, src->secondary_next_slot, sizeof(dst->secondary_next_slot));
600 	}
601 
602 	return ade_set_args(L, "o", l_WeaponBankType.Set(ship_banktype_h(objh->objp, dst, SWH_SECONDARY)));
603 }
604 
605 ADE_VIRTVAR(TertiaryBanks, l_Ship, "weaponbanktype", "Array of tertiary weapon banks", "weaponbanktype", "Tertiary weapon banks, or invalid weaponbanktype handle if ship handle is invalid")
606 {
607 	object_h *objh;
608 	ship_banktype_h *swh = nullptr;
609 	if(!ade_get_args(L, "o|o", l_Ship.GetPtr(&objh), l_WeaponBankType.GetPtr(&swh)))
610 		return ade_set_error(L, "o", l_WeaponBankType.Set(ship_banktype_h()));
611 
612 	if(!objh->IsValid())
613 		return ade_set_error(L, "o", l_WeaponBankType.Set(ship_banktype_h()));
614 
615 	ship_weapon *dst = &Ships[objh->objp->instance].weapons;
616 
617 	if(ADE_SETTING_VAR && swh && swh->IsValid()) {
618 		ship_weapon *src = &Ships[swh->objp->instance].weapons;
619 
620 		dst->current_tertiary_bank = src->current_tertiary_bank;
621 		dst->num_tertiary_banks = src->num_tertiary_banks;
622 
623 		dst->next_tertiary_fire_stamp = src->next_tertiary_fire_stamp;
624 		dst->tertiary_bank_ammo = src->tertiary_bank_ammo;
625 		dst->tertiary_bank_capacity = src->tertiary_bank_capacity;
626 		dst->tertiary_bank_rearm_time = src->tertiary_bank_rearm_time;
627 		dst->tertiary_bank_start_ammo = src->tertiary_bank_start_ammo;
628 	}
629 
630 	return ade_set_args(L, "o", l_WeaponBankType.Set(ship_banktype_h(objh->objp, dst, SWH_TERTIARY)));
631 }
632 
633 ADE_VIRTVAR(Target, l_Ship, "object", "Target of ship. Value may also be a deriviative of the 'object' class, such as 'ship'.", "object", "Target object, or invalid object handle if no target or ship handle is invalid")
634 {
635 	object_h *objh;
636 	object_h *newh = nullptr;
637 	//WMC - Maybe use two argument return capabilities of Lua to set/return subsystem?
638 	if(!ade_get_args(L, "o|o", l_Ship.GetPtr(&objh), l_Object.GetPtr(&newh)))
639 		return ade_set_error(L, "o", l_Object.Set(object_h()));
640 
641 	if(!objh->IsValid())
642 		return ade_set_error(L, "o", l_Object.Set(object_h()));
643 
644 	ai_info *aip = NULL;
645 	if(Ships[objh->objp->instance].ai_index > -1)
646 		aip = &Ai_info[Ships[objh->objp->instance].ai_index];
647 	else
648 		return ade_set_error(L, "o", l_Object.Set(object_h()));
649 
650 	if(ADE_SETTING_VAR && newh)
651 	{
652 		if(aip->target_signature != newh->sig)
653 		{
654 			if(newh->IsValid())
655 			{
656 				aip->target_objnum = OBJ_INDEX(newh->objp);
657 				aip->target_signature = newh->sig;
658 				aip->target_time = 0.0f;
659 
660 				if (aip == Player_ai)
661 					hud_shield_hit_reset(newh->objp);
662 			}
663 			else
664 			{
665 				aip->target_objnum = -1;
666 				aip->target_signature = -1;
667 				aip->target_time = 0.0f;
668 			}
669 
670 			set_targeted_subsys(aip, NULL, -1);
671 		}
672 	}
673 
674 	return ade_set_object_with_breed(L, aip->target_objnum);
675 }
676 
677 ADE_VIRTVAR(TargetSubsystem, l_Ship, "subsystem", "Target subsystem of ship.", "subsystem", "Target subsystem, or invalid subsystem handle if no target or ship handle is invalid")
678 {
679 	object_h *oh;
680 	ship_subsys_h *newh = nullptr;
681 	//WMC - Maybe use two argument return capabilities of Lua to set/return subsystem?
682 	if(!ade_get_args(L, "o|o", l_Ship.GetPtr(&oh), l_Subsystem.GetPtr(&newh)))
683 		return ade_set_error(L, "o", l_Subsystem.Set(ship_subsys_h()));
684 
685 	if(!oh->IsValid())
686 		return ade_set_error(L, "o", l_Subsystem.Set(ship_subsys_h()));
687 
688 	ai_info *aip = NULL;
689 	if(Ships[oh->objp->instance].ai_index > -1)
690 		aip = &Ai_info[Ships[oh->objp->instance].ai_index];
691 	else
692 		return ade_set_error(L, "o", l_Subsystem.Set(ship_subsys_h()));
693 
694 	if(ADE_SETTING_VAR)
695 	{
696 		if(newh && newh->isSubsystemValid())
697 		{
698 			if (aip == Player_ai) {
699 				if (aip->target_signature != newh->sig)
700 					hud_shield_hit_reset(newh->objp);
701 
702 				Ships[Objects[newh->ss->parent_objnum].instance].last_targeted_subobject[Player_num] = newh->ss;
703 			}
704 
705 			aip->target_objnum = OBJ_INDEX(newh->objp);
706 			aip->target_signature = newh->sig;
707 			aip->target_time = 0.0f;
708 			set_targeted_subsys(aip, newh->ss, aip->target_objnum);
709 		}
710 		else
711 		{
712 			aip->target_objnum = -1;
713 			aip->target_signature = -1;
714 			aip->target_time = 0.0f;
715 
716 			set_targeted_subsys(aip, NULL, -1);
717 		}
718 	}
719 
720 	return ade_set_args(L, "o", l_Subsystem.Set(ship_subsys_h(&Objects[aip->target_objnum], aip->targeted_subsys)));
721 }
722 
723 ADE_VIRTVAR(Team, l_Ship, "team", "Ship's team", "team", "Ship team, or invalid team handle if ship handle is invalid")
724 {
725 	object_h *oh=NULL;
726 	int nt=-1;
727 	if(!ade_get_args(L, "o|o", l_Ship.GetPtr(&oh), l_Team.Get(&nt)))
728 		return ade_set_error(L, "o", l_Team.Set(-1));
729 
730 	if(!oh->IsValid())
731 		return ade_set_error(L, "o", l_Team.Set(-1));
732 
733 	ship *shipp = &Ships[oh->objp->instance];
734 
735 	if(ADE_SETTING_VAR && nt > -1) {
736 		shipp->team = nt;
737 	}
738 
739 	return ade_set_args(L, "o", l_Team.Set(shipp->team));
740 }
741 
742 ADE_VIRTVAR(Textures, l_Ship, "shiptextures", "Gets ship textures", "shiptextures", "Ship textures, or invalid shiptextures handle if ship handle is invalid")
743 {
744 	object_h *sh = nullptr;
745 	object_h *dh;
746 	if(!ade_get_args(L, "o|o", l_Ship.GetPtr(&dh), l_Ship.GetPtr(&sh)))
747 		return ade_set_error(L, "o", l_ShipTextures.Set(object_h()));
748 
749 	if(!dh->IsValid())
750 		return ade_set_error(L, "o", l_ShipTextures.Set(object_h()));
751 
752 	if(ADE_SETTING_VAR && sh && sh->IsValid()) {
753 		ship *src = &Ships[sh->objp->instance];
754 		ship *dest = &Ships[dh->objp->instance];
755 
756 		if (src->ship_replacement_textures != NULL)
757 		{
758 			if (dest->ship_replacement_textures == NULL)
759 				dest->ship_replacement_textures = (int *) vm_malloc(MAX_REPLACEMENT_TEXTURES * sizeof(int));
760 
761 			memcpy(dest->ship_replacement_textures, src->ship_replacement_textures, MAX_REPLACEMENT_TEXTURES * sizeof(int));
762 		}
763 	}
764 
765 	return ade_set_args(L, "o", l_ShipTextures.Set(object_h(dh->objp)));
766 }
767 
768 ADE_VIRTVAR(FlagAffectedByGravity, l_Ship, "boolean", "Checks for the \"affected-by-gravity\" flag", "boolean", "True if flag is set, false if flag is not set and nil on error")
769 {
770 	object_h *objh=NULL;
771 	bool set = false;
772 
773 	if (!ade_get_args(L, "o|b", l_Ship.GetPtr(&objh), &set))
774 		return ADE_RETURN_NIL;
775 
776 	if (!objh->IsValid())
777 		return ADE_RETURN_NIL;
778 
779 	ship *shipp = &Ships[objh->objp->instance];
780 
781 	if(ADE_SETTING_VAR)
782 	{
783 		shipp->flags.set(Ship::Ship_Flags::Affected_by_gravity, set);
784 	}
785 
786 	if (shipp->flags[Ship::Ship_Flags::Affected_by_gravity])
787 		return ADE_RETURN_TRUE;
788 	else
789 		return ADE_RETURN_FALSE;
790 }
791 
792 ADE_VIRTVAR(Disabled, l_Ship, "boolean", "The disabled state of this ship", "boolean", "true if ship is diabled, false otherwise")
793 {
794 	object_h *objh=NULL;
795 	bool set = false;
796 
797 	if (!ade_get_args(L, "o|b", l_Ship.GetPtr(&objh), &set))
798 		return ADE_RETURN_FALSE;
799 
800 	if (!objh->IsValid())
801 		return ADE_RETURN_FALSE;
802 
803 	ship *shipp = &Ships[objh->objp->instance];
804 
805 	if(ADE_SETTING_VAR)
806 	{
807 		shipp->flags.set(Ship::Ship_Flags::Disabled, set);
808 		if(set)
809 		{
810 			mission_log_add_entry(LOG_SHIP_DISABLED, shipp->ship_name, NULL );
811 		}
812 		else
813 		{
814 			ship_reset_disabled_physics( &Objects[shipp->objnum], shipp->ship_info_index );
815 		}
816 	}
817 
818 	if (shipp->flags[Ship::Ship_Flags::Disabled])
819 		return ADE_RETURN_TRUE;
820 	else
821 		return ADE_RETURN_FALSE;
822 }
823 
824 ADE_VIRTVAR(Stealthed, l_Ship, "boolean", "Stealth status of this ship", "boolean", "true if stealthed, false otherwise or on error")
825 {
826 	object_h *objh=NULL;
827 	bool stealthed = false;
828 
829 	if (!ade_get_args(L, "o|b", l_Ship.GetPtr(&objh), &stealthed))
830 		return ADE_RETURN_FALSE;
831 
832 	if (!objh->IsValid())
833 		return ADE_RETURN_FALSE;
834 
835 	ship *shipp = &Ships[objh->objp->instance];
836 
837 	if(ADE_SETTING_VAR)
838 	{
839 		shipp->flags.set(Ship::Ship_Flags::Stealth, stealthed);
840 	}
841 
842 	if (shipp->flags[Ship::Ship_Flags::Stealth])
843 		return ADE_RETURN_TRUE;
844 	else
845 		return ADE_RETURN_FALSE;
846 }
847 
848 ADE_VIRTVAR(HiddenFromSensors, l_Ship, "boolean", "Hidden from sensors status of this ship", "boolean", "true if invisible to hidden from sensors, false otherwise or on error")
849 {
850 	object_h *objh=NULL;
851 	bool hidden = false;
852 
853 	if (!ade_get_args(L, "o|b", l_Ship.GetPtr(&objh), &hidden))
854 		return ADE_RETURN_FALSE;
855 
856 	if (!objh->IsValid())
857 		return ADE_RETURN_FALSE;
858 
859 	ship *shipp = &Ships[objh->objp->instance];
860 
861 	if(ADE_SETTING_VAR)
862 	{
863 		shipp->flags.set(Ship::Ship_Flags::Hidden_from_sensors, hidden);
864 	}
865 
866 	if (shipp->flags[Ship::Ship_Flags::Hidden_from_sensors])
867 		return ADE_RETURN_TRUE;
868 	else
869 		return ADE_RETURN_FALSE;
870 }
871 
872 ADE_VIRTVAR(Gliding, l_Ship, "boolean", "Specifies whether this ship is currently gliding or not.", "boolean", "true if gliding, false otherwise or in case of error")
873 {
874 	object_h *objh=NULL;
875 	bool gliding = false;
876 
877 	if (!ade_get_args(L, "o|b", l_Ship.GetPtr(&objh), &gliding))
878 		return ADE_RETURN_FALSE;
879 
880 	if (!objh->IsValid())
881 		return ADE_RETURN_FALSE;
882 
883 	ship *shipp = &Ships[objh->objp->instance];
884 
885 	if(ADE_SETTING_VAR)
886 	{
887 		if (Ship_info[shipp->ship_info_index].can_glide)
888 		{
889 			object_set_gliding(&Objects[shipp->objnum], gliding, true);
890 		}
891 	}
892 
893 	if (objh->objp->phys_info.flags & PF_GLIDING || objh->objp->phys_info.flags & PF_FORCE_GLIDE)
894 		return ADE_RETURN_TRUE;
895 	else
896 		return ADE_RETURN_FALSE;
897 }
898 
899 ADE_VIRTVAR(EtsEngineIndex, l_Ship, "number", "(SET not implemented, see EtsSetIndexes)", "number", "Ships ETS Engine index value, 0 to MAX_ENERGY_INDEX")
900 {
901 	object_h *objh=NULL;
902 	int ets_idx = 0;
903 
904 	if (!ade_get_args(L, "o|i", l_Ship.GetPtr(&objh), &ets_idx))
905 		return ade_set_error(L, "i", 0);
906 
907 	if (!objh->IsValid())
908 		return ade_set_error(L, "i", 0);
909 
910 	if(ADE_SETTING_VAR)
911 		LuaError(L, "Attempted to set incomplete feature: ETS Engine Index (see EtsSetIndexes)");
912 
913 	return ade_set_args(L, "i", Ships[objh->objp->instance].engine_recharge_index);
914 }
915 
916 ADE_VIRTVAR(EtsShieldIndex, l_Ship, "number", "(SET not implemented, see EtsSetIndexes)", "number", "Ships ETS Shield index value, 0 to MAX_ENERGY_INDEX")
917 {
918 	object_h *objh=NULL;
919 	int ets_idx = 0;
920 
921 	if (!ade_get_args(L, "o|i", l_Ship.GetPtr(&objh), &ets_idx))
922 		return ade_set_error(L, "i", 0);
923 
924 	if (!objh->IsValid())
925 		return ade_set_error(L, "i", 0);
926 
927 	if(ADE_SETTING_VAR)
928 		LuaError(L, "Attempted to set incomplete feature: ETS Shield Index (see EtsSetIndexes)");
929 
930 	return ade_set_args(L, "i", Ships[objh->objp->instance].shield_recharge_index);
931 }
932 
933 ADE_VIRTVAR(EtsWeaponIndex, l_Ship, "number", "(SET not implemented, see EtsSetIndexes)", "number", "Ships ETS Weapon index value, 0 to MAX_ENERGY_INDEX")
934 {
935 	object_h *objh=NULL;
936 	int ets_idx = 0;
937 
938 	if (!ade_get_args(L, "o|i", l_Ship.GetPtr(&objh), &ets_idx))
939 		return ade_set_error(L, "i", 0);
940 
941 	if (!objh->IsValid())
942 		return ade_set_error(L, "i", 0);
943 
944 	if(ADE_SETTING_VAR)
945 		LuaError(L, "Attempted to set incomplete feature: ETS Weapon Index (see EtsSetIndexes)");
946 
947 	return ade_set_args(L, "i", Ships[objh->objp->instance].weapon_recharge_index);
948 }
949 
950 ADE_VIRTVAR(Orders, l_Ship, "shiporders", "Array of ship orders", "shiporders", "Ship orders, or invalid handle if ship handle is invalid")
951 {
952 	object_h *objh = NULL;
953 	object_h *newh = NULL;
954 	if(!ade_get_args(L, "o|o", l_Ship.GetPtr(&objh), l_ShipOrders.GetPtr(&newh)))
955 		return ade_set_error(L, "o", l_ShipOrders.Set(object_h()));
956 
957 	if(!objh->IsValid())
958 		return ade_set_error(L, "o", l_ShipOrders.Set(object_h()));;
959 
960 	if(ADE_SETTING_VAR)
961 	{
962 		LuaError(L, "Attempted to use incomplete feature: Ai orders copy. Use giveOrder instead");
963 	}
964 
965 	return ade_set_args(L, "o", l_ShipOrders.Set(object_h(objh->objp)));
966 }
967 
968 ADE_FUNC(getCenterPosition, l_Ship, nullptr, "Returns the position of the ship's physical center, which may not be the position of the origin of the model", "vector", "World position of the center of the ship, or nil if an error occurred")
969 {
970 	object_h *shiph;
971 	vec3d center_pos, actual_local_center;
972 
973 	if (!ade_get_args(L, "o", l_Ship.GetPtr(&shiph)))
974 		return ADE_RETURN_NIL;
975 
976 	// find local center
977 	ship_class_get_actual_center(&Ship_info[Ships[shiph->objp->instance].ship_info_index], &actual_local_center);
978 
979 	// find world position of the center
980 	vm_vec_unrotate(&center_pos, &actual_local_center, &shiph->objp->orient);
981 	vm_vec_add2(&center_pos, &shiph->objp->pos);
982 
983 	return ade_set_args(L, "o", l_Vector.Set(center_pos));
984 }
985 
986 ADE_FUNC(kill, l_Ship, "[object Killer, vector Hitpos]", "Kills the ship. Set \"Killer\" to a ship (or a weapon fired by that ship) to credit it for the kill in the mission log. Set it to the ship being killed to self-destruct. Set \"Hitpos\" to the world coordinates of the weapon impact.", "boolean", "True if successful, false or nil otherwise")
987 {
988 	object_h *victim, *killer = nullptr;
989 	vec3d *hitpos = nullptr;
990 	if(!ade_get_args(L, "o|oo", l_Ship.GetPtr(&victim), l_Object.GetPtr(&killer), l_Vector.GetPtr(&hitpos)))
991 		return ADE_RETURN_NIL;
992 
993 	if(!victim->IsValid())
994 		return ADE_RETURN_NIL;
995 
996 	if(killer && !killer->IsValid())
997 		return ADE_RETURN_NIL;
998 
999 	// use the current hull percentage for damage-after-death purposes
1000 	// (note that this does not actually affect scoring)
1001 	float percent_killed = get_hull_pct(victim->objp);
1002 
1003 	ship_hit_kill(victim->objp, killer ? killer->objp : nullptr, hitpos, percent_killed, (killer && victim->sig == killer->sig), true);
1004 
1005 	return ADE_RETURN_TRUE;
1006 }
1007 
1008 ADE_FUNC(addShipEffect, l_Ship, "string name, number durationMillis", "Activates an effect for this ship. Effect names are defined in Post_processing.tbl, and need to be implemented in the main shader. This functions analogous to the ship-effect sexp. NOTE: only one effect can be active at any time, adding new effects will override effects already in progress.\n", "boolean", "Returns true if the effect was successfully added, false otherwise") {
1009 	object_h *shiph;
1010 	const char* effect = nullptr;
1011 	int duration;
1012 	int effect_num;
1013 
1014 	if (!ade_get_args(L, "o|si", l_Ship.GetPtr(&shiph), &effect, &duration))
1015 		return ade_set_error(L, "b", false);
1016 
1017 	if (!shiph->IsValid())
1018 		return ade_set_error(L, "b", false);
1019 
1020 	effect_num = get_effect_from_name(effect);
1021 	if (effect_num == -1)
1022 		return ade_set_error(L, "b", false);
1023 
1024 	ship* shipp = &Ships[shiph->objp->instance];
1025 
1026 	shipp->shader_effect_active = true;
1027 	shipp->shader_effect_num = effect_num;
1028 	shipp->shader_effect_duration = duration;
1029 	shipp->shader_effect_start_time = timer_get_milliseconds();
1030 
1031 	return ade_set_args(L, "b", true);
1032 }
1033 
1034 ADE_FUNC(hasShipExploded, l_Ship, NULL, "Checks if the ship explosion event has already happened", "number", "Returns 1 if first explosion timestamp is passed, 2 if second is passed, 0 otherwise")
1035 {
1036 	object_h *shiph;
1037 	if(!ade_get_args(L, "o", l_Ship.GetPtr(&shiph)))
1038 		return ade_set_error(L, "i", 0);
1039 
1040 	if(!shiph->IsValid())
1041 		return ade_set_error(L, "i", 0);
1042 
1043 	ship *shipp = &Ships[shiph->objp->instance];
1044 
1045 	if (shipp->flags[Ship::Ship_Flags::Dying]) {
1046 		if (shipp->final_death_time == 0) {
1047 			return ade_set_args(L, "i", 2);
1048 		}
1049 		if (shipp->pre_death_explosion_happened == 1) {
1050 			return ade_set_args(L, "i", 1);
1051 		}
1052 		return ade_set_args(L, "i", 3);
1053 	}
1054 
1055 	return ade_set_args(L, "i", 0);
1056 }
1057 
1058 ADE_FUNC(isDepartingWarp, l_Ship, nullptr, "Checks if the ship is departing via warp", "boolean", "True if the Depart_warp flag is set, false otherwise")
1059 {
1060 	object_h *shiph;
1061 	if (!ade_get_args(L, "o", l_Ship.GetPtr(&shiph)))
1062 		return ade_set_error(L, "b", false);
1063 
1064 	if (!shiph->IsValid())
1065 		return ade_set_error(L, "b", false);
1066 
1067 	ship *shipp = &Ships[shiph->objp->instance];
1068 
1069 	return ade_set_args(L, "b", shipp->flags[Ship::Ship_Flags::Depart_warp]);
1070 }
1071 
1072 ADE_FUNC(isDepartingDockbay, l_Ship, nullptr, "Checks if the ship is departing via warp", "boolean", "True if the Depart_dockbay flag is set, false otherwise")
1073 {
1074 	object_h *shiph;
1075 	if (!ade_get_args(L, "o", l_Ship.GetPtr(&shiph)))
1076 		return ade_set_error(L, "b", false);
1077 
1078 	if (!shiph->IsValid())
1079 		return ade_set_error(L, "b", false);
1080 
1081 	ship *shipp = &Ships[shiph->objp->instance];
1082 
1083 	return ade_set_args(L, "b", shipp->flags[Ship::Ship_Flags::Depart_dockbay]);
1084 }
1085 
1086 ADE_FUNC(isDying, l_Ship, nullptr, "Checks if the ship is dying (doing its death roll or exploding)", "boolean", "True if the Dying flag is set, false otherwise")
1087 {
1088 	object_h *shiph;
1089 	if (!ade_get_args(L, "o", l_Ship.GetPtr(&shiph)))
1090 		return ade_set_error(L, "b", false);
1091 
1092 	if (!shiph->IsValid())
1093 		return ade_set_error(L, "b", false);
1094 
1095 	ship *shipp = &Ships[shiph->objp->instance];
1096 
1097 	return ade_set_args(L, "b", shipp->flags[Ship::Ship_Flags::Dying]);
1098 }
1099 
1100 ADE_FUNC(fireCountermeasure, l_Ship, NULL, "Launches a countermeasure from the ship", "boolean", "Whether countermeasure was launched or not")
1101 {
1102 	object_h *objh;
1103 	if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1104 		return ade_set_error(L, "b", false);
1105 
1106 	if(!objh->IsValid())
1107 		return ade_set_error(L, "b", false);
1108 
1109 	return ade_set_args(L, "b", ship_launch_countermeasure(objh->objp) != 0);
1110 }
1111 
1112 ADE_FUNC(firePrimary, l_Ship, NULL, "Fires ship primary bank(s)", "number", "Number of primary banks fired")
1113 {
1114 	object_h *objh;
1115 	if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1116 		return ade_set_error(L, "i", 0);
1117 
1118 	if(!objh->IsValid())
1119 		return ade_set_error(L, "i", 0);
1120 
1121 	int i = 0;
1122 	i += ship_fire_primary(objh->objp, 0);
1123 	i += ship_fire_primary(objh->objp, 1);
1124 
1125 	return ade_set_args(L, "i", i);
1126 }
1127 
1128 ADE_FUNC(fireSecondary, l_Ship, NULL, "Fires ship secondary bank(s)", "number", "Number of secondary banks fired")
1129 {
1130 	object_h *objh;
1131 	if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1132 		return ade_set_error(L, "i", 0);
1133 
1134 	if(!objh->IsValid())
1135 		return ade_set_error(L, "i", 0);
1136 
1137 	return ade_set_args(L, "i", ship_fire_secondary(objh->objp, 0));
1138 }
1139 
1140 ADE_FUNC(getAnimationDoneTime, l_Ship, "number Type, number Subtype", "Gets time that animation will be done", "number", "Time (seconds), or 0 if ship handle is invalid")
1141 {
1142 	object_h *objh;
1143 	const char* s = nullptr;
1144 	int subtype=-1;
1145 	if(!ade_get_args(L, "o|si", l_Ship.GetPtr(&objh), &s, &subtype))
1146 		return ade_set_error(L, "f", 0.0f);
1147 
1148 	if(!objh->IsValid())
1149 		return ade_set_error(L, "f", 0.0f);
1150 
1151 	auto type = model_anim_match_type(s);
1152 	if(type == AnimationTriggerType::None)
1153 		return ADE_RETURN_FALSE;
1154 
1155 	int time_ms = model_anim_get_time_type(&Ships[objh->objp->instance], type, subtype);
1156 	float time_s = (float)time_ms / 1000.0f;
1157 
1158 	return ade_set_args(L, "f", time_s);
1159 }
1160 
1161 ADE_FUNC(clearOrders, l_Ship, NULL, "Clears a ship's orders list", "boolean", "True if successful, otherwise false or nil")
1162 {
1163 	object_h *objh = NULL;
1164 	if(!ade_get_args(L, "o", l_Object.GetPtr(&objh)))
1165 		return ADE_RETURN_NIL;
1166 	if(!objh->IsValid())
1167 		return ade_set_error(L, "b", false);
1168 
1169 	//The actual clearing of the goals
1170 	ai_clear_ship_goals( &Ai_info[Ships[objh->objp->instance].ai_index]);
1171 
1172 	return ADE_RETURN_TRUE;
1173 }
1174 
1175 ADE_FUNC(giveOrder, l_Ship, "enumeration Order, [object Target=nil, subsystem TargetSubsystem=nil, number Priority=1.0, shipclass TargetShipclass=nil]", "Uses the goal code to execute orders", "boolean", "True if order was given, otherwise false or nil")
1176 {
1177 	object_h *objh = NULL;
1178 	enum_h *eh = NULL;
1179 	float priority = 1.0f;
1180 	int sclass = -1;
1181 	object_h *tgh = NULL;
1182 	ship_subsys_h *tgsh = NULL;
1183 	if(!ade_get_args(L, "oo|oofo", l_Object.GetPtr(&objh), l_Enum.GetPtr(&eh), l_Object.GetPtr(&tgh), l_Subsystem.GetPtr(&tgsh), &priority, l_Shipclass.Get(&sclass)))
1184 		return ADE_RETURN_NIL;
1185 
1186 	if(!objh->IsValid() || !eh->IsValid())
1187 		return ade_set_error(L, "b", false);
1188 
1189 	//wtf...
1190 	if(priority < 0.0f)
1191 		return ade_set_error(L, "b", false);
1192 
1193 	if(priority > 1.0f)
1194 		priority = 1.0f;
1195 
1196 	bool tgh_valid = tgh && tgh->IsValid();
1197 	bool tgsh_valid = tgsh && tgsh->isSubsystemValid();
1198 	int ai_mode = AI_GOAL_NONE;
1199 	int ai_submode = -1234567;
1200 	char *ai_shipname = NULL;
1201 	switch(eh->index)
1202 	{
1203 		case LE_ORDER_ATTACK:
1204 		{
1205 			if(tgsh_valid)
1206 			{
1207 				ai_mode = AI_GOAL_DESTROY_SUBSYSTEM;
1208 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1209 				ai_submode = ship_get_subsys_index( &Ships[tgsh->objp->instance], tgsh->ss->system_info->subobj_name );
1210 			}
1211 			else if(tgh_valid && tgh->objp->type == OBJ_WEAPON)
1212 			{
1213 				ai_mode = AI_GOAL_CHASE_WEAPON;
1214 				ai_submode = tgh->objp->instance;
1215 			}
1216 			else if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1217 			{
1218 				ai_mode = AI_GOAL_CHASE;
1219 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1220 				ai_submode = SM_ATTACK;
1221 			}
1222 			break;
1223 		}
1224 		case LE_ORDER_DOCK:
1225 		{
1226 			ai_shipname = Ships[tgh->objp->instance].ship_name;
1227 			ai_mode = AI_GOAL_DOCK;
1228 			ai_submode = AIS_DOCK_0;
1229 			break;
1230 		}
1231 		case LE_ORDER_WAYPOINTS:
1232 		{
1233 			if(tgh_valid && tgh->objp->type == OBJ_WAYPOINT)
1234 			{
1235 				ai_mode = AI_GOAL_WAYPOINTS;
1236 				waypoint_list *wp_list = find_waypoint_list_with_instance(tgh->objp->instance);
1237 				if(wp_list != NULL)
1238 					ai_shipname = wp_list->get_name();
1239 			}
1240 			break;
1241 		}
1242 		case LE_ORDER_WAYPOINTS_ONCE:
1243 		{
1244 			if(tgh_valid && tgh->objp->type == OBJ_WAYPOINT)
1245 			{
1246 				ai_mode = AI_GOAL_WAYPOINTS_ONCE;
1247 				waypoint_list *wp_list = find_waypoint_list_with_instance(tgh->objp->instance);
1248 				if(wp_list != NULL)
1249 					ai_shipname = wp_list->get_name();
1250 			}
1251 			break;
1252 		}
1253 		case LE_ORDER_DEPART:
1254 		{
1255 			ai_mode = AI_GOAL_WARP;
1256 			ai_submode = -1;
1257 			break;
1258 		}
1259 		case LE_ORDER_FORM_ON_WING:
1260 		{
1261 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1262 			{
1263 				ai_mode = AI_GOAL_FORM_ON_WING;
1264 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1265 				ai_submode = 0;
1266 			}
1267 			break;
1268 		}
1269 		case LE_ORDER_UNDOCK:
1270 		{
1271 			ai_mode = AI_GOAL_UNDOCK;
1272 			ai_submode = AIS_UNDOCK_0;
1273 
1274 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1275 			{
1276 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1277 			}
1278 			break;
1279 		}
1280 		case LE_ORDER_GUARD:
1281 		{
1282 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1283 			{
1284 				ai_mode = AI_GOAL_GUARD;
1285 				ai_submode = AIS_GUARD_PATROL;
1286 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1287 			}
1288 			break;
1289 		}
1290 		case LE_ORDER_DISABLE:
1291 		{
1292 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1293 			{
1294 				ai_mode = AI_GOAL_DISABLE_SHIP;
1295 				ai_submode = -SUBSYSTEM_ENGINE;
1296 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1297 			}
1298 			break;
1299 		}
1300 		case LE_ORDER_DISARM:
1301 		{
1302 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1303 			{
1304 				ai_mode = AI_GOAL_DISARM_SHIP;
1305 				ai_submode = -SUBSYSTEM_TURRET;
1306 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1307 			}
1308 			break;
1309 		}
1310 		case LE_ORDER_ATTACK_ANY:
1311 		{
1312 			ai_mode = AI_GOAL_CHASE_ANY;
1313 			ai_submode = SM_ATTACK;
1314 			break;
1315 		}
1316 		case LE_ORDER_IGNORE:
1317 		{
1318 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1319 			{
1320 				ai_mode = AI_GOAL_IGNORE_NEW;
1321 				ai_submode = 0;
1322 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1323 			}
1324 			break;
1325 		}
1326 		case LE_ORDER_EVADE:
1327 		{
1328 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1329 			{
1330 				ai_mode = AI_GOAL_EVADE_SHIP;
1331 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1332 			}
1333 			break;
1334 		}
1335 		case LE_ORDER_STAY_NEAR:
1336 		{
1337 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1338 			{
1339 				ai_mode = AI_GOAL_STAY_NEAR_SHIP;
1340 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1341 				ai_submode = -1;
1342 			}
1343 			break;
1344 		}
1345 		case LE_ORDER_KEEP_SAFE_DISTANCE:
1346 		{
1347 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1348 			{
1349 				ai_mode = AI_GOAL_KEEP_SAFE_DISTANCE;
1350 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1351 				ai_submode = -1;
1352 			}
1353 			break;
1354 		}
1355 		case LE_ORDER_REARM:
1356 		{
1357 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1358 			{
1359 				ai_mode = AI_GOAL_REARM_REPAIR;
1360 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1361 				ai_submode = 0;
1362 			}
1363 			break;
1364 		}
1365 		case LE_ORDER_STAY_STILL:
1366 		{
1367 			ai_mode = AI_GOAL_STAY_STILL;
1368 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1369 			{
1370 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1371 			}
1372 			break;
1373 		}
1374 		case LE_ORDER_PLAY_DEAD:
1375 		{
1376 			ai_mode = AI_GOAL_PLAY_DEAD;
1377 			break;
1378 		}
1379 		case LE_ORDER_PLAY_DEAD_PERSISTENT:
1380 		{
1381 			ai_mode = AI_GOAL_PLAY_DEAD_PERSISTENT;
1382 			break;
1383 		}
1384 		case LE_ORDER_FLY_TO:
1385 		{
1386 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1387 			{
1388 				ai_mode = AI_GOAL_FLY_TO_SHIP;
1389 				ai_shipname = Ships[tgh->objp->instance].ship_name;
1390 				ai_submode = 0;
1391 			}
1392 			break;
1393 		}
1394 		case LE_ORDER_ATTACK_WING:
1395 		{
1396 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1397 			{
1398 				ship *shipp = &Ships[tgh->objp->instance];
1399 				if (shipp->wingnum != -1)
1400 				{
1401 					ai_mode = AI_GOAL_CHASE_WING;
1402 					ai_shipname = Wings[shipp->wingnum].name;
1403 					ai_submode = SM_ATTACK;
1404 				}
1405 			}
1406 			break;
1407 		}
1408 		case LE_ORDER_GUARD_WING:
1409 		{
1410 			if(tgh_valid && tgh->objp->type == OBJ_SHIP)
1411 			{
1412 				ship *shipp = &Ships[tgh->objp->instance];
1413 				if (shipp->wingnum != -1)
1414 				{
1415 					ai_mode = AI_GOAL_GUARD_WING;
1416 					ai_shipname = Wings[shipp->wingnum].name;
1417 					ai_submode = AIS_GUARD_STATIC;
1418 				}
1419 			}
1420 			break;
1421 		}
1422 		case LE_ORDER_ATTACK_SHIP_CLASS:
1423 		{
1424 			if(sclass >= 0)
1425 			{
1426 				ai_mode = AI_GOAL_CHASE_SHIP_CLASS;
1427 				ai_shipname = Ship_info[sclass].name;
1428 				ai_submode = SM_ATTACK;
1429 			}
1430 			break;
1431 		}
1432 	}
1433 
1434 	//Nothing got set!
1435 	if(ai_mode == AI_GOAL_NONE)
1436 		return ade_set_error(L, "b", false);
1437 
1438 	//Fire off the goal
1439 	ai_add_ship_goal_scripting(ai_mode, ai_submode, (int)(priority*100.0f), ai_shipname, &Ai_info[Ships[objh->objp->instance].ai_index]);
1440 
1441 	return ADE_RETURN_TRUE;
1442 }
1443 
1444 ADE_FUNC(doManeuver,
1445 	l_Ship,
1446 	"number Duration, number Heading, number Pitch, number Bank, boolean ApplyAllRotation, number Vertical, number "
1447 	"Sideways, number Forward, boolean ApplyAllMovement, number ManeuverBitfield",
1448 	"Sets ship maneuver over the defined time period",
1449 	"boolean",
1450 	"True if maneuver order was given, otherwise false or nil")
1451 {
1452 	object_h *objh;
1453 	float heading, pitch, bank, up, sideways, forward;
1454 	bool apply_all_rotate = false, apply_all_move = false;
1455 	int duration, maneuver_flags = 0;
1456 	if(!ade_get_args(L, "oifffbfffb|i", l_Ship.GetPtr(&objh), &duration, &heading, &pitch, &bank, &apply_all_rotate, &up, &sideways, &forward, &apply_all_move, &maneuver_flags))
1457 		return ADE_RETURN_NIL;
1458 
1459 	ship *shipp = &Ships[objh->objp->instance];
1460 	ai_info *aip = &Ai_info[shipp->ai_index];
1461 	control_info *cip = &aip->ai_override_ci;
1462 
1463 	if (!(maneuver_flags & CIF_DONT_OVERRIDE_OLD_MANEUVERS)) {
1464 		aip->ai_override_flags.reset();
1465 	}
1466 
1467 	bool applied_rot = false;
1468 	bool applied_lat = false;
1469 	if (apply_all_rotate) {
1470 		aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Full_rot);
1471 		cip->heading = heading;
1472 		cip->pitch = pitch;
1473 		cip->bank = bank;
1474 		applied_rot = true;
1475 	} else {
1476 		if (heading != 0) {
1477 			cip->heading = heading;
1478 			aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Heading);
1479 			applied_rot = true;
1480 		}
1481 		if (pitch != 0) {
1482 			cip->pitch = pitch;
1483 			aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Pitch);
1484 			applied_rot = true;
1485 		}
1486 		if (bank != 0) {
1487 			cip->bank = bank;
1488 			aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Roll);
1489 			applied_rot = true;
1490 		}
1491 	}
1492 	if (apply_all_move) {
1493 		aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Full_lat);
1494 		cip->vertical = up;
1495 		cip->sideways = sideways;
1496 		cip->forward = forward;
1497 		applied_lat = true;
1498 	} else {
1499 		if (up != 0) {
1500 			cip->vertical = up;
1501 			aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Up);
1502 			applied_lat = true;
1503 		}
1504 		if (sideways != 0) {
1505 			cip->sideways = sideways;
1506 			aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Sideways);
1507 			applied_lat = true;
1508 		}
1509 		if (forward != 0) {
1510 			cip->forward = forward;
1511 			aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Forward);
1512 			applied_lat = true;
1513 		}
1514 	}
1515 
1516 	// handle infinite timestamps
1517 	if (duration >= 2) {
1518 		if (applied_rot)
1519 			aip->ai_override_rot_timestamp = timestamp(duration);
1520 		if (applied_lat)
1521 			aip->ai_override_lat_timestamp = timestamp(duration);
1522 	}
1523 	else {
1524 		if (applied_rot) {
1525 			aip->ai_override_rot_timestamp = timestamp(10);
1526 			aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Rotational_never_expire);
1527 		}
1528 		if (applied_lat) {
1529 			aip->ai_override_lat_timestamp = timestamp(10);
1530 			aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Lateral_never_expire);
1531 		}
1532 	}
1533 
1534 	if (maneuver_flags & CIF_DONT_BANK_WHEN_TURNING) {
1535 		aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Dont_bank_when_turning);
1536 	}
1537 	if (maneuver_flags & CIF_DONT_CLAMP_MAX_VELOCITY) {
1538 		aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Dont_clamp_max_velocity);
1539 	}
1540 	if (maneuver_flags & CIF_INSTANTANEOUS_ACCELERATION) {
1541 		aip->ai_override_flags.set(AI::Maneuver_Override_Flags::Instantaneous_acceleration);
1542 	}
1543 
1544 	return ADE_RETURN_TRUE;
1545 }
1546 
1547 ADE_FUNC(triggerAnimation, l_Ship, "string Type, [number Subtype, boolean Forwards, boolean Instant]",
1548 		 "Triggers an animation. Type is the string name of the animation type, "
1549 			 "Subtype is the subtype number, such as weapon bank #, Forwards and Instant are boolean, defaulting to true & false respectively."
1550 			 "<br><strong>IMPORTANT: Function is in testing and should not be used with official mod releases</strong>",
1551 		 "boolean",
1552 		 "True if successful, false or nil otherwise")
1553 {
1554 	object_h *objh;
1555 	const char* s = nullptr;
1556 	bool b = true;
1557 	bool instant = false;
1558 	int subtype=-1;
1559 	if(!ade_get_args(L, "o|sibb", l_Ship.GetPtr(&objh), &s, &subtype, &b, &instant))
1560 		return ADE_RETURN_NIL;
1561 
1562 	if(!objh->IsValid())
1563 		return ADE_RETURN_NIL;
1564 
1565 	auto type = model_anim_match_type(s);
1566 	if(type == AnimationTriggerType::None)
1567 		return ADE_RETURN_FALSE;
1568 
1569 	int dir = 1;
1570 	if(!b)
1571 		dir = -1;
1572 
1573 	model_anim_start_type(&Ships[objh->objp->instance], type, subtype, dir, instant);
1574 
1575 	return ADE_RETURN_TRUE;
1576 }
1577 
1578 ADE_FUNC(warpIn, l_Ship, NULL, "Warps ship in", "boolean", "True if successful, or nil if ship handle is invalid")
1579 {
1580 	object_h *objh;
1581 	if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1582 		return ADE_RETURN_NIL;
1583 
1584 	if(!objh->IsValid())
1585 		return ADE_RETURN_NIL;
1586 
1587 	shipfx_warpin_start(objh->objp);
1588 
1589 	return ADE_RETURN_TRUE;
1590 }
1591 
1592 ADE_FUNC(warpOut, l_Ship, NULL, "Warps ship out", "boolean", "True if successful, or nil if ship handle is invalid")
1593 {
1594 	object_h *objh;
1595 	if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1596 		return ADE_RETURN_NIL;
1597 
1598 	if(!objh->IsValid())
1599 		return ADE_RETURN_NIL;
1600 
1601 	shipfx_warpout_start(objh->objp);
1602 
1603 	return ADE_RETURN_TRUE;
1604 }
1605 
1606 ADE_FUNC(canWarp, l_Ship, nullptr, "Checks whether ship has a working subspace drive, is allowed to use it, and is not disabled or limited by subsystem strength.", "boolean", "True if successful, or nil if ship handle is invalid")
1607 {
1608 	object_h *objh;
1609 	if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1610 		return ADE_RETURN_NIL;
1611 
1612 	if(!objh->IsValid())
1613 		return ADE_RETURN_NIL;
1614 
1615 	ship *shipp = &Ships[objh->objp->instance];
1616 	if( ship_can_warp_full_check(shipp) ){
1617 		return ADE_RETURN_TRUE;
1618 	}
1619 
1620 	return ADE_RETURN_FALSE;
1621 }
1622 
1623 ADE_FUNC(canBayDepart, l_Ship, nullptr, "Checks whether ship has a bay departure location and if its mother ship is present.", "boolean", "True if successful, or nil if ship handle is invalid")
1624 {
1625 	object_h *objh;
1626 	if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1627 		return ADE_RETURN_NIL;
1628 
1629 	if(!objh->IsValid())
1630 		return ADE_RETURN_NIL;
1631 
1632 	ship *shipp = &Ships[objh->objp->instance];
1633 	if( ship_can_bay_depart(shipp) ){
1634 		return ADE_RETURN_TRUE;
1635 	}
1636 
1637 	return ADE_RETURN_FALSE;
1638 }
1639 
1640 // Aardwolf's function for finding if a ship should be drawn as blue on the radar/minimap
1641 ADE_FUNC(isWarpingIn, l_Ship, NULL, "Checks if ship is warping in", "boolean", "True if the ship is warping in, false or nil otherwise")
1642 {
1643 	object_h *objh;
1644 	if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1645 		return ADE_RETURN_NIL;
1646 
1647 	if(!objh->IsValid())
1648 		return ADE_RETURN_NIL;
1649 
1650 	ship *shipp = &Ships[objh->objp->instance];
1651 	if(shipp->is_arriving(ship::warpstage::STAGE1, false)){
1652 		return ADE_RETURN_TRUE;
1653 	}
1654 
1655 	return ADE_RETURN_FALSE;
1656 }
1657 
1658 ADE_FUNC(getEMP, l_Ship, NULL, "Returns the current emp effect strength acting on the object", "number", "Current EMP effect strength or NIL if object is invalid")
1659 {
1660 	object_h *objh = NULL;
1661 	object *obj = NULL;
1662 
1663 	if (!ade_get_args(L, "o", l_Ship.GetPtr(&objh))) {
1664 		return ADE_RETURN_NIL;
1665 	}
1666 
1667 	if(!objh->IsValid())
1668 		return ADE_RETURN_NIL;
1669 
1670 	obj = objh->objp;
1671 
1672 	ship *shipp = &Ships[obj->instance];
1673 
1674 	return ade_set_args(L, "f", shipp->emp_intensity);
1675 }
1676 
1677 ADE_FUNC(getTimeUntilExplosion, l_Ship, nullptr, "Returns the time in seconds until the ship explodes (the ship's final_death_time timestamp)", "number", "Time until explosion or -1, if invalid handle or ship isn't exploding")
1678 {
1679 	object_h *objh = nullptr;
1680 
1681 	if (!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1682 		return ade_set_error(L, "f", -1.0f);
1683 	if(!objh->IsValid())
1684 		return ade_set_error(L, "f", -1.0f);
1685 
1686 	ship *shipp = &Ships[objh->objp->instance];
1687 
1688 	if (!timestamp_valid(shipp->final_death_time))
1689 		return ade_set_args(L, "f", -1.0f);
1690 
1691 	int time_until = timestamp_until(shipp->final_death_time);
1692 
1693 	return ade_set_args(L, "f", (i2fl(time_until) / 1000.0f));
1694 }
1695 
1696 ADE_FUNC(setTimeUntilExplosion, l_Ship, "number Time", "Sets the time in seconds until the ship explodes (the ship's final_death_time timestamp).  This function will only work if the ship is in its death roll but hasn't exploded yet, which can be checked via isDying() or getTimeUntilExplosion().", "boolean", "True if successful, false if the ship is invalid or not currently exploding")
1697 {
1698 	object_h *objh = nullptr;
1699 	float delta_s;
1700 
1701 	if (!ade_get_args(L, "of", l_Ship.GetPtr(&objh), &delta_s))
1702 		return ade_set_error(L, "b", false);
1703 	if (!objh->IsValid())
1704 		return ade_set_error(L, "b", false);
1705 
1706 	ship *shipp = &Ships[objh->objp->instance];
1707 
1708 	if (!timestamp_valid(shipp->final_death_time))
1709 		return ade_set_args(L, "b", false);
1710 
1711 	int delta_ms = fl2i(delta_s * 1000.0f);
1712 	if (delta_ms < 2)
1713 		delta_ms = 2;
1714 
1715 	shipp->final_death_time = timestamp(delta_ms);
1716 
1717 	return ade_set_args(L, "b", true);
1718 }
1719 
1720 ADE_FUNC(getCallsign, l_Ship, NULL, "Gets the callsign of the ship in the current mission", "string", "The callsign or an empty string if the ship doesn't have a callsign or an error occurs")
1721 {
1722 	object_h *objh = NULL;
1723 
1724 	if (!ade_get_args(L, "o", l_Ship.GetPtr(&objh))) {
1725 		return ade_set_error(L, "s", "");
1726 	}
1727 
1728 	if(!objh->IsValid())
1729 		return ade_set_error(L, "s", "");
1730 
1731 	ship *shipp = &Ships[objh->objp->instance];
1732 
1733 	if (shipp->callsign_index < 0)
1734 		return ade_set_args(L, "s", "");
1735 
1736 	char temp_callsign[NAME_LENGTH];
1737 
1738 	*temp_callsign = 0;
1739 	strcpy(temp_callsign, mission_parse_lookup_callsign_index(shipp->callsign_index));
1740 
1741 	if (*temp_callsign)
1742 		return ade_set_args(L, "s", temp_callsign);
1743 	else
1744 		return ade_set_args(L, "s", "");
1745 }
1746 
1747 ADE_FUNC(getAltClassName, l_Ship, NULL, "Gets the alternate class name of the ship", "string", "The alternate class name or an empty string if the ship doesn't have such a thing or an error occurs")
1748 {
1749 	object_h *objh = NULL;
1750 
1751 	if (!ade_get_args(L, "o", l_Ship.GetPtr(&objh))) {
1752 		return ade_set_error(L, "s", "");
1753 	}
1754 
1755 	if(!objh->IsValid())
1756 		return ade_set_error(L, "s", "");
1757 
1758 	ship *shipp = &Ships[objh->objp->instance];
1759 
1760 	if (shipp->alt_type_index < 0)
1761 		return ade_set_args(L, "s", "");
1762 
1763 	char temp_altname[NAME_LENGTH];
1764 
1765 	strcpy_s(temp_altname, mission_parse_lookup_alt_index(shipp->alt_type_index));
1766 
1767 	if (*temp_altname)
1768 		return ade_set_args(L, "s", temp_altname);
1769 	else
1770 		return ade_set_args(L, "s", "");
1771 }
1772 
1773 ADE_FUNC(getMaximumSpeed, l_Ship, "[number energy = 0.333]", "Gets the maximum speed of the ship with the given energy on the engines", "number", "The maximum speed or -1 on error")
1774 {
1775 	object_h *objh = NULL;
1776 	float energy = 0.333f;
1777 
1778 	if (!ade_get_args(L, "o|f", l_Ship.GetPtr(&objh), &energy)) {
1779 		return ade_set_error(L, "f", -1.0f);
1780 	}
1781 
1782 	if(!objh->IsValid())
1783 		return ade_set_error(L, "f", -1.0f);
1784 
1785 	if (energy < 0.0f || energy > 1.0f)
1786 	{
1787 		LuaError(L, "Invalid energy level %f! Needs to be in [0, 1].", energy);
1788 
1789 		return ade_set_args(L, "f", -1.0f);
1790 	}
1791 	else
1792 	{
1793 		return ade_set_args(L, "f", ets_get_max_speed(objh->objp, energy));
1794 	}
1795 }
1796 
1797 ADE_FUNC(EtsSetIndexes, l_Ship, "number EngineIndex, number ShieldIndex, number WeaponIndex",
1798 		 "Sets ships ETS systems to specified values",
1799 		 "boolean",
1800 		 "True if successful, false if target ships ETS was missing, or only has one system")
1801 {
1802 	object_h *objh=NULL;
1803 	int ets_idx[num_retail_ets_gauges] = {0};
1804 
1805 	if (!ade_get_args(L, "oiii", l_Ship.GetPtr(&objh), &ets_idx[ENGINES], &ets_idx[SHIELDS], &ets_idx[WEAPONS]))
1806 		return ADE_RETURN_FALSE;
1807 
1808 	if (!objh->IsValid())
1809 		return ADE_RETURN_FALSE;
1810 
1811 	sanity_check_ets_inputs(ets_idx);
1812 
1813 	int sindex = objh->objp->instance;
1814 	if (validate_ship_ets_indxes(sindex, ets_idx)) {
1815 		Ships[sindex].engine_recharge_index = ets_idx[ENGINES];
1816 		Ships[sindex].shield_recharge_index = ets_idx[SHIELDS];
1817 		Ships[sindex].weapon_recharge_index = ets_idx[WEAPONS];
1818 		return ADE_RETURN_TRUE;
1819 	} else {
1820 		return ADE_RETURN_FALSE;
1821 	}
1822 }
1823 
1824 ADE_FUNC(getWing, l_Ship, NULL, "Returns the ship's wing", "wing", "Wing handle, or invalid wing handle if ship is not part of a wing")
1825 {
1826 	object_h *objh = NULL;
1827 	ship *shipp = NULL;
1828 
1829 	if (!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1830 		return ade_set_error(L, "o", l_Wing.Set(-1));
1831 
1832 	if(!objh->IsValid())
1833 		return ade_set_error(L, "o", l_Wing.Set(-1));
1834 
1835 	shipp = &Ships[objh->objp->instance];
1836 	return ade_set_args(L, "o", l_Wing.Set(shipp->wingnum));
1837 }
1838 
1839 ADE_FUNC(getDisplayString, l_Ship, nullptr, "Returns the string which should be used when displaying the name of the ship to the player", "string", "The display string or empty if handle is invalid")
1840 {
1841 	object_h *objh = nullptr;
1842 	ship *shipp = nullptr;
1843 
1844 	if (!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1845 		return ade_set_error(L, "s", "");
1846 
1847 	if(!objh->IsValid())
1848 		return ade_set_error(L, "s", "");
1849 
1850 	shipp = &Ships[objh->objp->instance];
1851 	return ade_set_args(L, "s", shipp->get_display_name());
1852 }
1853 
1854 ADE_FUNC(vanish, l_Ship, nullptr, "Vanishes this ship from the mission. Works in Singleplayer only and will cause the ship exit to not be logged.", "boolean", "True if the deletion was successful, false otherwise.")
1855 {
1856 
1857 	object_h* objh = nullptr;
1858 
1859 	if (!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
1860 		return ade_set_error(L, "b", false);
1861 
1862 	if (!objh->IsValid())
1863 		return ade_set_error(L, "b", false);
1864 
1865 	ship_actually_depart(objh->objp->instance, SHIP_VANISHED);
1866 
1867 	return ade_set_args(L, "b", true);
1868 }
1869 
1870 
1871 }
1872 }
1873