Skip to content

Commit

Permalink
Merge pull request #5585 from Goober5000/beam_api_stuff
Browse files Browse the repository at this point in the history
additional beam-related features for Lua
  • Loading branch information
Goober5000 authored Oct 15, 2023
2 parents 7a4df25 + 53e6edf commit e132fcf
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 19 deletions.
20 changes: 10 additions & 10 deletions code/parse/sexp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20762,7 +20762,7 @@ void sexp_beam_floating_fire(int n)
beam_fire(&fire_info);
}

void sexp_beam_free_one_turret(ship_subsys *turret, bool is_beam, bool free)
void sexp_beam_or_turret_free_one(ship_subsys *turret, bool is_beam, bool free)
{
if (is_beam)
{
Expand Down Expand Up @@ -20815,7 +20815,7 @@ void sexp_beam_or_turret_free_or_lock(int node, bool is_beam, bool free)
if (!turret || turret->system_info->type != SUBSYSTEM_TURRET)
continue;

sexp_beam_free_one_turret(turret, is_beam, free);
sexp_beam_or_turret_free_one(turret, is_beam, free);
}
}

Expand All @@ -20834,7 +20834,7 @@ void sexp_beam_or_turret_free_or_lock_all(int node, bool is_beam, bool free)
if (turret->system_info->type != SUBSYSTEM_TURRET)
continue;

sexp_beam_free_one_turret(turret, is_beam, free);
sexp_beam_or_turret_free_one(turret, is_beam, free);
}
}
}
Expand Down Expand Up @@ -38502,31 +38502,31 @@ SCP_vector<sexp_help_struct> Sexp_help = {
"\ttime there is no player in a given ship, this sexpression will return 0"},

{ OP_BEAM_FREE, "beam-free\r\n"
"\tSets one or more beam weapons to allow firing for a given ship\r\n"
"\tSets one or more turrets to allow beam weapons firing. When a beam-equipped turret is activated, there will be a short random delay (between 50 and 4000 milliseconds) before firing.\r\n"
"\t1: Ship to be operated on\r\n"
"\t2, 3, etc : List of turrets to activate\r\n"},

{ OP_BEAM_FREE_ALL, "beam-free-all\r\n"
"\tSets all beam weapons on the specified ship to be active\r\n"},
"\tSets all beam weapons on the specified ship to be active. For each beam-equipped turret, there will be a different short random delay (between 50 and 4000 milliseconds) before firing.\r\n"},

{ OP_BEAM_LOCK, "beam-lock\r\n"
"\tSets one or more beam weapons to NOT allow firing for a given ship\r\n"
"\tSets one or more turrets to NOT allow beam weapons firing.\r\n"
"\t1: Ship to be operated on\r\n"
"\t2, 3, etc : List of turrets to deactivate\r\n"},

{ OP_BEAM_LOCK_ALL, "beam-lock-all\r\n"
"\tSets all beam weapons on the specified ship to be deactivated\r\n"},
"\tSets all beam turrets on the specified ship to be deactivated\r\n"},

{ OP_TURRET_FREE, "turret-free\r\n"
"\tSets one or more turret weapons to allow firing for a given ship\r\n"
"\tSets one or more turrets to allow standard primary or secondary weapons firing. When a turret is activated, there will be a short random delay (between 50 and 4000 milliseconds) before firing.\r\n"
"\t1: Ship to be operated on\r\n"
"\t2, 3, etc : List of turrets to activate\r\n"},

{ OP_TURRET_FREE_ALL, "turret-free-all\r\n"
"\tSets all turret weapons on the specified ship to be active\r\n"},
"\tSets all standard primary or secondary weapon turrets on the specified ship to be active. For each turret, there will be a different short random delay (between 50 and 4000 milliseconds) before firing.\r\n"},

{ OP_TURRET_LOCK, "turret-lock\r\n"
"\tSets one or more turret weapons to NOT allow firing for a given ship\r\n"
"\tSets one or more turrets to NOT allow standard primary or secondary weapons firing.\r\n"
"\t1: Ship to be operated on\r\n"
"\t2, 3, etc : List of turrets to deactivate\r\n"},

Expand Down
16 changes: 16 additions & 0 deletions code/scripting/api/objs/beam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "vecmath.h"
#include "weaponclass.h"

extern void beam_delete(beam *b);

namespace scripting {
namespace api {

Expand Down Expand Up @@ -380,6 +382,20 @@ ADE_FUNC(getEndDirectionInfo, l_Beam, NULL, "Gets the end information about the
return ade_set_args(L, "o", l_Vector.Set(inf.dir_b));
}

ADE_FUNC(vanish, l_Beam, nullptr, "Vanishes this beam from the mission.", "boolean", "True if the deletion was successful, false otherwise.")
{
object_h* objh;
if (!ade_get_args(L, "o", l_Beam.GetPtr(&objh)))
return ADE_RETURN_FALSE;

if (!objh->IsValid())
return ADE_RETURN_FALSE;

beam_delete(&Beams[objh->objp->instance]);

return ADE_RETURN_TRUE;
}


}
}
57 changes: 56 additions & 1 deletion code/scripting/api/objs/subsystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "network/multimsgs.h"
#include "network/multiutil.h"

void sexp_beam_or_turret_free_one(ship_subsys *turret, bool is_beam, bool free);
bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum, vec3d *turret_pos, vec3d *firing_vec, vec3d *predicted_pos = nullptr, float flak_range_override = 100.0f, bool play_sound = true);

namespace scripting {
Expand Down Expand Up @@ -569,7 +570,7 @@ ADE_VIRTVAR(Radius, l_Subsystem, "number", "The radius of this subsystem", "numb
return ade_set_args(L, "f", sso->ss->system_info->radius);
}

ADE_VIRTVAR(TurretLocked, l_Subsystem, "boolean", "Whether the turret is locked. Setting to true locks the turret, setting to false frees it.", "boolean", "True if turret is locked, false otherwise")
ADE_VIRTVAR(TurretLocked, l_Subsystem, "boolean", "Whether the turret is locked. Setting to true locks the turret; setting to false frees it.", "boolean", "True if turret is locked, false otherwise")
{
ship_subsys_h *sso;
bool newVal = false;
Expand All @@ -587,6 +588,60 @@ ADE_VIRTVAR(TurretLocked, l_Subsystem, "boolean", "Whether the turret is locked.
return ade_set_args(L, "b", (sso->ss->weapons.flags[Ship::Weapon_Flags::Turret_Lock]));
}

ADE_VIRTVAR(TurretLockedWithTimestamp, l_Subsystem, "boolean", "Behaves like TurretLocked, but when the turret is freed, there will be a short random delay (between 50 and 4000 milliseconds) before firing, to be consistent with SEXP behavior.", "boolean", "True if turret is locked, false otherwise")
{
ship_subsys_h *sso;
bool newVal = false;
if (!ade_get_args(L, "o|b", l_Subsystem.GetPtr(&sso), &newVal))
return ade_set_error(L, "b", false);

if (!sso->isSubsystemValid())
return ade_set_error(L, "b", false);

if(ADE_SETTING_VAR)
{
sexp_beam_or_turret_free_one(sso->ss, false, !newVal);
}

return ade_set_args(L, "b", (sso->ss->weapons.flags[Ship::Weapon_Flags::Turret_Lock]));
}

ADE_VIRTVAR(BeamFree, l_Subsystem, "boolean", "Whether the turret is beam-freed. Setting to true beam-frees the turret; setting to false beam-locks it.", "boolean", "True if turret is beam-freed, false otherwise")
{
ship_subsys_h *sso;
bool newVal = false;
if (!ade_get_args(L, "o|b", l_Subsystem.GetPtr(&sso), &newVal))
return ade_set_error(L, "b", false);

if (!sso->isSubsystemValid())
return ade_set_error(L, "b", false);

if(ADE_SETTING_VAR)
{
sso->ss->weapons.flags.set(Ship::Weapon_Flags::Beam_Free, newVal);
}

return ade_set_args(L, "b", (sso->ss->weapons.flags[Ship::Weapon_Flags::Beam_Free]));
}

ADE_VIRTVAR(BeamFreeWithTimestamp, l_Subsystem, "boolean", "Behaves like BeamFree, but when the turret is freed, there will be a short random delay (between 50 and 4000 milliseconds) before firing, to be consistent with SEXP behavior.", "boolean", "True if turret is beam-freed, false otherwise")
{
ship_subsys_h *sso;
bool newVal = false;
if (!ade_get_args(L, "o|b", l_Subsystem.GetPtr(&sso), &newVal))
return ade_set_error(L, "b", false);

if (!sso->isSubsystemValid())
return ade_set_error(L, "b", false);

if(ADE_SETTING_VAR)
{
sexp_beam_or_turret_free_one(sso->ss, true, newVal);
}

return ade_set_args(L, "b", (sso->ss->weapons.flags[Ship::Weapon_Flags::Beam_Free]));
}

ADE_VIRTVAR(NextFireTimestamp, l_Subsystem, "number", "The next time the turret may attempt to fire", "number", "Mission time (seconds) or -1 on error")
{
ship_subsys_h *sso;
Expand Down
60 changes: 60 additions & 0 deletions code/scripting/api/objs/weaponclass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,9 @@ ADE_VIRTVAR(BurstShots, l_Weaponclass, "number", "The number of shots in a burst
if (idx < 0 || idx >= weapon_info_size())
return ade_set_error(L, "i", 0);

if (ADE_SETTING_VAR)
LuaError(L, "Setting BurstShots is not supported");

return ade_set_args(L, "i", Weapon_info[idx].burst_shots + 1);
}

Expand All @@ -703,9 +706,66 @@ ADE_VIRTVAR(BurstDelay, l_Weaponclass, "number", "The time in seconds between sh
if (idx < 0 || idx >= weapon_info_size())
return ade_set_error(L, "f", 0.0f);

if (ADE_SETTING_VAR)
LuaError(L, "Setting BurstDelay is not supported");

return ade_set_args(L, "f", Weapon_info[idx].burst_delay);
}

ADE_VIRTVAR(BeamLife, l_Weaponclass, "number", "The time in seconds that a beam lasts while firing.", "number", "Beam life, or 0 if handle is invalid or the weapon is not a beam")
{
int idx;
if (!ade_get_args(L, "o", l_Weaponclass.Get(&idx)))
return ade_set_error(L, "f", 0.0f);

if (idx < 0 || idx >= weapon_info_size())
return ade_set_error(L, "f", 0.0f);

if (ADE_SETTING_VAR)
LuaError(L, "Setting BeamLife is not supported");

if (Weapon_info[idx].wi_flags[Weapon::Info_Flags::Beam] || Weapon_info[idx].subtype == WP_BEAM)
return ade_set_args(L, "f", Weapon_info[idx].b_info.beam_life);

return ade_set_args(L, "f", 0.0f);
}

ADE_VIRTVAR(BeamWarmup, l_Weaponclass, "number", "The time in seconds that a beam takes to warm up.", "number", "Warmup time, or 0 if handle is invalid or the weapon is not a beam")
{
int idx;
if (!ade_get_args(L, "o", l_Weaponclass.Get(&idx)))
return ade_set_error(L, "f", 0.0f);

if (idx < 0 || idx >= weapon_info_size())
return ade_set_error(L, "f", 0.0f);

if (ADE_SETTING_VAR)
LuaError(L, "Setting BeamWarmup is not supported");

if (Weapon_info[idx].wi_flags[Weapon::Info_Flags::Beam] || Weapon_info[idx].subtype == WP_BEAM)
return ade_set_args(L, "f", i2fl(Weapon_info[idx].b_info.beam_warmup) / MILLISECONDS_PER_SECOND);

return ade_set_args(L, "f", 0.0f);
}

ADE_VIRTVAR(BeamWarmdown, l_Weaponclass, "number", "The time in seconds that a beam takes to warm down.", "number", "Warmdown time, or 0 if handle is invalid or the weapon is not a beam")
{
int idx;
if (!ade_get_args(L, "o", l_Weaponclass.Get(&idx)))
return ade_set_error(L, "f", 0.0f);

if (idx < 0 || idx >= weapon_info_size())
return ade_set_error(L, "f", 0.0f);

if (ADE_SETTING_VAR)
LuaError(L, "Setting BeamWarmdown is not supported");

if (Weapon_info[idx].wi_flags[Weapon::Info_Flags::Beam] || Weapon_info[idx].subtype == WP_BEAM)
return ade_set_args(L, "f", i2fl(Weapon_info[idx].b_info.beam_warmdown) / MILLISECONDS_PER_SECOND);

return ade_set_args(L, "f", 0.0f);
}

ADE_FUNC(isValid, l_Weaponclass, NULL, "Detects whether handle is valid", "boolean", "true if valid, false if handle is invalid, nil if a syntax/type error occurs")
{
int idx;
Expand Down
26 changes: 24 additions & 2 deletions code/scripting/global_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ const std::shared_ptr<Hook<WeaponDeathConditions>> OnMissileDeath = Hook<WeaponD
{"Object", "object", "The object that the weapon hit - a ship, asteroid, or piece of debris. Always set but could be invalid if there is no other object. If this missile was destroyed by another weapon, the 'other object' will be invalid but the DestroyedByWeapon flag will be set."},
});

const std::shared_ptr<Hook<>> OnBeamDeath = Hook<>::Factory(
"On Beam Death", "Called when a beam has been removed from the mission (whether by finishing firing, destruction of turret, etc.).",
{
{"Beam", "beam", "The beam that was removed."},
});

const std::shared_ptr<Hook<>> OnAsteroidCreated = Hook<>::Factory("On Asteroid Created",
"Called when an asteroid has been created.",
{
Expand Down Expand Up @@ -306,10 +312,26 @@ const std::shared_ptr<Hook<WeaponUsedConditions>> OnTurretFired = Hook<WeaponUse
{"Target", "object", "The current target of the shot."},
});

const std::shared_ptr<Hook<WeaponUsedConditions>> OnBeamWarmup = Hook<WeaponUsedConditions>::Factory("On Beam Warmup",
"Invoked when a beam starts warming up to fire.",
{
{"User", "ship", "The ship that is firing the beam."},
{"Beam", "beam", "The spawned beam object."},
{"Target", "object", "The current target of the shot."},
});

const std::shared_ptr<Hook<WeaponUsedConditions>> OnBeamFired = Hook<WeaponUsedConditions>::Factory("On Beam Fire",
"Invoked when a beam is fired.",
"Invoked when a beam starts firing (after warming up).",
{
{"User", "ship", "The ship that is firing the beam."},
{"Beam", "beam", "The spawned beam object."},
{"Target", "object", "The current target of the shot."},
});

const std::shared_ptr<Hook<WeaponUsedConditions>> OnBeamWarmdown = Hook<WeaponUsedConditions>::Factory("On Beam Warmdown",
"Invoked when a beam starts \"warming down\" after firing.",
{
{"User", "ship", "The ship that has fired the turret."},
{"User", "ship", "The ship that is firing the beam."},
{"Beam", "beam", "The spawned beam object."},
{"Target", "object", "The current target of the shot."},
});
Expand Down
3 changes: 3 additions & 0 deletions code/scripting/global_hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ extern const std::shared_ptr<Hook<ShipDeathConditions>> OnShipDeathStarted;
extern const std::shared_ptr<OverridableHook<ShipDeathConditions>> OnShipDeath;
extern const std::shared_ptr<Hook<WeaponDeathConditions>> OnMissileDeathStarted;
extern const std::shared_ptr<Hook<WeaponDeathConditions>> OnMissileDeath;
extern const std::shared_ptr<Hook<>> OnBeamDeath;
extern const std::shared_ptr<Hook<>> OnAsteroidDeath;
extern const std::shared_ptr<Hook<>> OnDebrisDeath;
extern const std::shared_ptr<Hook<SubsystemDeathConditions>> OnSubsystemDestroyed;
Expand All @@ -58,7 +59,9 @@ extern const std::shared_ptr<Hook<WeaponUsedConditions>> OnSecondaryFired;
extern const std::shared_ptr<Hook<WeaponSelectedConditions>> OnWeaponSelected;
extern const std::shared_ptr<Hook<WeaponDeselectedConditions>> OnWeaponDeselected;
extern const std::shared_ptr<Hook<WeaponUsedConditions>> OnTurretFired;
extern const std::shared_ptr<Hook<WeaponUsedConditions>> OnBeamWarmup;
extern const std::shared_ptr<Hook<WeaponUsedConditions>> OnBeamFired;
extern const std::shared_ptr<Hook<WeaponUsedConditions>> OnBeamWarmdown;

extern const std::shared_ptr<OverridableHook<>> OnHudCommMenuOpened;
extern const std::shared_ptr<OverridableHook<>> OnHudCommMenuClosed;
Expand Down
39 changes: 33 additions & 6 deletions code/weapon/beam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -563,8 +563,6 @@ int beam_fire(beam_fire_info *fire_info)
// start the warmup phase
beam_start_warmup(new_item);

beam_set_state(wip, new_item, WeaponState::WARMUP);

return objnum;
}

Expand Down Expand Up @@ -2051,6 +2049,13 @@ void beam_apply_lighting()
// delete a beam
void beam_delete(beam *b)
{
// there isn't much distinction between death and death-started for beams
if (scripting::hooks::OnBeamDeath->isActive()) {
scripting::hooks::OnBeamDeath->run(scripting::hook_param_list(
scripting::hook_param("Beam", 'o', &Objects[b->objnum])
));
}

// remove from active list and put on free list
list_remove(&Beam_used_list, b);
list_append(&Beam_free_list, b);
Expand Down Expand Up @@ -2131,12 +2136,25 @@ int beam_get_model(object *objp)
// start the warmup phase for the beam
void beam_start_warmup(beam *b)
{
auto wip = &Weapon_info[b->weapon_info_index];

// set the warmup stamp
b->warmup_stamp = timestamp(Weapon_info[b->weapon_info_index].b_info.beam_warmup);
b->warmup_stamp = timestamp(wip->b_info.beam_warmup);

// start playing warmup sound
if(!(Game_mode & GM_STANDALONE_SERVER) && (Weapon_info[b->weapon_info_index].b_info.beam_warmup_sound.isValid())){
snd_play_3d(gamesnd_get_game_sound(Weapon_info[b->weapon_info_index].b_info.beam_warmup_sound), &b->last_start, &View_position);
if(!(Game_mode & GM_STANDALONE_SERVER) && (wip->b_info.beam_warmup_sound.isValid())){
snd_play_3d(gamesnd_get_game_sound(wip->b_info.beam_warmup_sound), &b->last_start, &View_position);
}

beam_set_state(wip, b, WeaponState::WARMUP);

if (scripting::hooks::OnBeamWarmup->isActive()) {
scripting::hooks::OnBeamWarmup->run(scripting::hooks::WeaponUsedConditions{ b->objp == nullptr ? nullptr : &Ships[b->objp->instance], b->target, SCP_vector<int>{ b->weapon_info_index }, true },
scripting::hook_param_list(
scripting::hook_param("Beam", 'o', &Objects[b->objnum]),
scripting::hook_param("User", 'o', b->objp),
scripting::hook_param("Target", 'o', b->target)
));
}
}

Expand Down Expand Up @@ -2200,7 +2218,7 @@ int beam_start_firing(beam *b)
beam_set_state(wip, b, WeaponState::FIRING);

if (scripting::hooks::OnBeamFired->isActive()) {
scripting::hooks::OnBeamFired->run(scripting::hooks::WeaponUsedConditions{ &Ships[b->objp->instance], b->target, SCP_vector<int>{ b->weapon_info_index }, true },
scripting::hooks::OnBeamFired->run(scripting::hooks::WeaponUsedConditions{ b->objp == nullptr ? nullptr : &Ships[b->objp->instance], b->target, SCP_vector<int>{ b->weapon_info_index }, true },
scripting::hook_param_list(
scripting::hook_param("Beam", 'o', &Objects[b->objnum]),
scripting::hook_param("User", 'o', b->objp),
Expand Down Expand Up @@ -2238,6 +2256,15 @@ void beam_start_warmdown(beam *b)
}

beam_set_state(&Weapon_info[b->weapon_info_index], b, WeaponState::WARMDOWN);

if (scripting::hooks::OnBeamWarmdown->isActive()) {
scripting::hooks::OnBeamWarmdown->run(scripting::hooks::WeaponUsedConditions{ b->objp == nullptr ? nullptr : &Ships[b->objp->instance], b->target, SCP_vector<int>{ b->weapon_info_index }, true },
scripting::hook_param_list(
scripting::hook_param("Beam", 'o', &Objects[b->objnum]),
scripting::hook_param("User", 'o', b->objp),
scripting::hook_param("Target", 'o', b->target)
));
}
}

// recalculate beam sounds (looping sounds relative to the player)
Expand Down

0 comments on commit e132fcf

Please sign in to comment.