So, I and other people on BP talked a bit about introducing "Equipment" type weapons.
Basic concept would be that you would equip these just as you would a primary or secondary weapon, only that instead of shooting bolts of plasma, beams, or other go-boom type stuff at people, these would basically alter bits and pieces of gameplay by calling scripts.
To that end, I present these test builds. There are several alterations to weapons.tbl and scripting.tbl, detailed below.
weapons.tblTo define a piece of equipment, the following additions to weapons.tbl have been made. After the "#Primary Weapons" list, you can now define a list of equipment to be mounted in a primary slot by using "#Primary Mount Equipment". To define a piece of equipment that uses a secondary slot, use "#Secondary Mount Equipment", to be inserted after "#Secondary Weapons".
Example:
#Primary Weapons
[List of weapons]
#End
#Primary Mount Equipment
[List of weapons]
#End
#Secondary Weapons
[List of weapons]
#End
#Secondary Mount Equipment
[List of weapons]
#End
scripting.tblThe meat of this Equipment system is to be found here. I have added a "$Equipment" conditional hook to that end. Here's how to use it:
#Conditional Hooks
$Equipment: <Equipment name as defined in weapons.tbl>
There are also several new actions available, which can be used both with "$Equipment", as well as the old "$Weapon Class" hooks.
- $On Weapon Equipped
- This will be run on every frame, for every ship that has the defined Weapon or Equipment installed
- $On Weapon Fired
- Run once when the weapon is fired
- $On Weapon Selected
- Run once when the weapon is selected
- $On Weapon Deselected
- Run once when the weapon is deselected.
For easy access to the ship that carries the equipment, you can access the "User" object from the hv array. There is also a "Target" object available there, which contains the ship's current target.
Test builds can be found here:
http://blueplanet.fsmods.net/E/FSO%20Tertiaries.7zCode patch:
Index: object/object.cpp
===================================================================
--- object/object.cpp (revision 6461)
+++ object/object.cpp (working copy)
@@ -1408,6 +1408,20 @@
// move post
obj_move_all_post(objp, frametime);
+
+ //Equipment script processing
+ if (objp->type == OBJ_SHIP) {
+ ship* shipp = &Ships[objp->instance];
+ object* target;
+ if (Ai_info[shipp->ai_index].target_objnum != -1)
+ target = &Objects[Ai_info[shipp->ai_index].target_objnum];
+ else
+ target = NULL;
+ if (objp == Player_obj && Player_ai->target_objnum != -1)
+ target = &Objects[Player_ai->target_objnum];
+ Script_system.SetHookObjects(2, "User", objp, "Target", target);
+ Script_system.RunCondition(CHA_ONWPEQUIPPED, 0, NULL, objp);
+ }
}
objp = GET_NEXT(objp);
}
Index: parse/scripting.cpp
===================================================================
--- parse/scripting.cpp (revision 6461)
+++ parse/scripting.cpp (working copy)
@@ -38,6 +38,7 @@
{"KeyPress", CHC_KEYPRESS, 0},
{"Version", CHC_VERSION, 0},
{"Application", CHC_APPLICATION, 0},
+ {"Equipment", CHC_EQUIPMENT, 0}
};
int Num_script_conditions = sizeof(Script_conditions)/sizeof(flag_def_list);
@@ -66,6 +67,10 @@
{"On Death", CHA_DEATH, 0},
{"On Mission End", CHA_MISSIONEND, 0},
{"On Weapon Delete", CHA_ONWEAPONDELETE, 0},
+ {"On Weapon Equipped", CHA_ONWPEQUIPPED, 0},
+ {"On Weapon Fired", CHA_ONWPFIRED, 0},
+ {"On Weapon Selected", CHA_ONWPSELECTED, 0},
+ {"On Weapon Deselected", CHA_ONWPDESELECTED, 0}
};
int Num_script_actions = sizeof(Script_actions)/sizeof(flag_def_list);
@@ -245,6 +250,54 @@
if(stricmp(Ships[objp->instance].ship_name, scp->data.name))
return false;
break;
+ case CHC_EQUIPMENT: {
+ if(objp == NULL || objp->type != OBJ_SHIP)
+ return false;
+
+ ship* shipp = &Ships[objp->instance];
+ switch (action) {
+ case CHA_ONWPSELECTED:
+ if( !((Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name == scp->data.name) || (Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name == scp->data.name)) )
+ return false;
+ break;
+ case CHA_ONWPDESELECTED:
+ if ( !( ((Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.previous_primary_bank]].name == scp->data.name) && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.previous_primary_bank]].name != scp->data.name)) || ((Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.previous_secondary_bank]].name == scp->data.name) && (Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.previous_secondary_bank]].name != scp->data.name)) ))
+ return false;
+ break;
+ case CHA_ONWPEQUIPPED: {
+ bool equipped = false;
+ for(int i = 0; i < 3; i++) {
+ if (!equipped) {
+ if ( !stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[i]].name, scp->data.name) )
+ equipped = false;
+ else {
+ equipped = true;
+ break;
+ }
+ }
+ }
+
+ if (!equipped) {
+ for(int i = 0; i < 4; i++) {
+ if (!equipped) {
+ if ( !stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[i]].name, scp->data.name) )
+ equipped = false;
+ else {
+ equipped = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!equipped)
+ return false;
+
+ break;
+ }
+ }
+ break;
+ }
case CHC_MISSION:
{
//WMC - Get mission filename with Mission_filename
@@ -270,12 +323,63 @@
return false;
break;
}
- case CHC_WEAPONCLASS:
- if(objp == NULL || objp->type != OBJ_WEAPON)
- return false;
- if(stricmp(Weapon_info[Weapons[objp->instance].weapon_info_index].name, scp->data.name))
- return false;
- break;
+ case CHC_WEAPONCLASS:
+ {
+ if (!(action == CHA_ONWPSELECTED || action == CHA_ONWPDESELECTED || action == CHA_ONWPEQUIPPED || action == CHA_ONWPFIRED)) {
+ if(objp == NULL || objp->type != OBJ_WEAPON)
+ return false;
+ else if(stricmp(Weapon_info[Weapons[objp->instance].weapon_info_index].name, scp->data.name) != 0)
+ return false;
+ } else if(objp == NULL || objp->type != OBJ_SHIP) {
+ return false;
+ } else {
+
+ // Okay, if we're still here, then objp is both valid and a ship
+ ship* shipp = &Ships[objp->instance];
+ switch (action) {
+ case CHA_ONWPSELECTED:
+ if( !((Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name == scp->data.name) || (Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name == scp->data.name)) )
+ return false;
+ break;
+ case CHA_ONWPDESELECTED:
+ if ( !( ((Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.previous_primary_bank]].name == scp->data.name) && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.previous_primary_bank]].name != scp->data.name)) || ((Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.previous_secondary_bank]].name == scp->data.name) && (Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.previous_secondary_bank]].name != scp->data.name)) ))
+ return false;
+ break;
+ case CHA_ONWPEQUIPPED: {
+ bool equipped = false;
+ for(int i = 0; i < 3; i++) {
+ if (!equipped) {
+ if ( !stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[i]].name, scp->data.name) )
+ equipped = false;
+ else {
+ equipped = true;
+ break;
+ }
+ }
+ }
+
+ if (!equipped) {
+ for(int i = 0; i < 4; i++) {
+ if (!equipped) {
+ if ( !stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[i]].name, scp->data.name) )
+ equipped = false;
+ else {
+ equipped = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!equipped)
+ return false;
+
+ break;
+ }
+ }
+ } // case CHC_WEAPONCLASS
+ break;
+ }
case CHC_OBJECTTYPE:
if(objp == NULL)
return false;
@@ -1190,6 +1294,7 @@
case CHC_OBJECTTYPE:
case CHC_VERSION:
case CHC_APPLICATION:
+ case CHC_EQUIPMENT:
default:
stuff_string(sct.data.name, F_NAME, NAME_LENGTH);
break;
Index: parse/scripting.h
===================================================================
--- parse/scripting.h (revision 6461)
+++ parse/scripting.h (working copy)
@@ -36,6 +36,7 @@
#define CHC_KEYPRESS 8
#define CHC_VERSION 9
#define CHC_APPLICATION 10
+#define CHC_EQUIPMENT 11
//Actions
#define CHA_NONE -1
@@ -61,6 +62,10 @@
#define CHA_ONSTATESTART 19
#define CHA_ONSTATEEND 20
#define CHA_ONWEAPONDELETE 21
+#define CHA_ONWPEQUIPPED 22
+#define CHA_ONWPFIRED 23
+#define CHA_ONWPSELECTED 24
+#define CHA_ONWPDESELECTED 25
struct script_condition
Index: ship/ship.cpp
===================================================================
--- ship/ship.cpp (revision 6461)
+++ ship/ship.cpp (working copy)
@@ -75,6 +75,7 @@
#include "autopilot/autopilot.h"
#include "cmdline/cmdline.h"
#include "object/objcollide.h"
+#include "parse/scripting.h"
@@ -4532,10 +4533,13 @@
swp->current_primary_bank = -1;
swp->current_secondary_bank = -1;
+ swp->previous_primary_bank = -1;
+ swp->previous_secondary_bank = -1;
if ( sip->num_primary_banks > 0 ) {
if ( swp->primary_bank_weapons[BANK_1] >= 0 ) {
swp->current_primary_bank = BANK_1;
+ swp->previous_primary_bank = BANK_1;
} else {
swp->current_primary_bank = -1;
}
@@ -4547,6 +4551,7 @@
if ( sip->num_secondary_banks > 0 ) {
if ( swp->secondary_bank_weapons[BANK_1] >= 0 ) {
swp->current_secondary_bank = BANK_1;
+ swp->previous_secondary_bank = BANK_1;
} else {
swp->current_secondary_bank = -1;
}
@@ -9423,8 +9428,12 @@
}
// now handle the energy as usual
- // deplete the weapon reserve energy by the amount of energy used to fire the weapon
- shipp->weapon_energy -= points*numtimes * winfo_p->energy_consumed;
+ // deplete the weapon reserve energy by the amount of energy used to fire the weapon
+ // Only subtract the energy amount required for equipment operation once
+ if (winfo_p->subtype == WP_TERTIARY_LASER)
+ shipp->weapon_energy -= winfo_p->energy_consumed;
+ else
+ shipp->weapon_energy -= points*numtimes * winfo_p->energy_consumed;
// note for later: option for fuel!
// Mark all these weapons as in the same group
@@ -9752,6 +9761,17 @@
}
}
+ object *objp = &Objects[shipp->objnum];
+ object* target;
+ if (Ai_info[shipp->ai_index].target_objnum != -1)
+ target = &Objects[Ai_info[shipp->ai_index].target_objnum];
+ else
+ target = NULL;
+ if (objp == Player_obj && Player_ai->target_objnum != -1)
+ target = &Objects[Player_ai->target_objnum];
+ Script_system.SetHookObjects(2, "User", objp, "Target", target);
+ Script_system.RunCondition(CHA_ONWPFIRED, 0, NULL, objp);
+
return num_fired;
}
@@ -10260,7 +10280,10 @@
}
int pnt_index=start_slot;
- for ( j = start_slot; j <= end_slot; j++ ) {
+ //If this is a tertiary weapon, only subtract one piece of ammo
+ if (wip->subtype == WP_TERTIARY_MISSILE) {
+ swp->secondary_bank_ammo[bank]--;
+ } else for ( j = start_slot; j <= end_slot; j++ ) {
int weapon_num;
swp->secondary_next_slot[bank]++;
@@ -10444,6 +10467,17 @@
}
+ object *objp = &Objects[shipp->objnum];
+ object* target;
+ if (Ai_info[shipp->ai_index].target_objnum != -1)
+ target = &Objects[Ai_info[shipp->ai_index].target_objnum];
+ else
+ target = NULL;
+ if (objp == Player_obj && Player_ai->target_objnum != -1)
+ target = &Objects[Player_ai->target_objnum];
+ Script_system.SetHookObjects(2, "User", objp, "Target", target);
+ Script_system.RunCondition(CHA_ONWPFIRED, 0, NULL, objp);
+
return num_fired;
}
@@ -10633,6 +10667,11 @@
// make sure we're okay
Assert((swp->current_primary_bank >= 0) && (swp->current_primary_bank < swp->num_primary_banks));
+ if(swp->current_primary_bank != original_bank)
+ swp->previous_primary_bank = original_bank;
+ else
+ swp->previous_primary_bank = swp->current_primary_bank;
+
// if this ship is ballistics-equipped, and we cycled, then we had to verify some stuff,
// so we should check if we actually changed banks
if ( (swp->current_primary_bank != original_bank) || ((shipp->flags & SF_PRIMARY_LINKED) != original_link_flag) )
@@ -10642,6 +10681,18 @@
snd_play( &Snds[SND_PRIMARY_CYCLE], 0.0f );
}
ship_primary_changed(shipp);
+ object* objp = &Objects[shipp->objnum];
+ object* target;
+ if (Ai_info[shipp->ai_index].target_objnum != -1)
+ target = &Objects[Ai_info[shipp->ai_index].target_objnum];
+ else
+ target = NULL;
+ if (objp == Player_obj && Player_ai->target_objnum != -1)
+ target = &Objects[Player_ai->target_objnum];
+ Script_system.SetHookObjects(2, "User", objp, "Target", target);
+ Script_system.RunCondition(CHA_ONWPSELECTED, 0, NULL, objp);
+ Script_system.SetHookObjects(2, "User", objp, "Target", target);
+ Script_system.RunCondition(CHA_ONWPDESELECTED, 0, NULL, objp);
return 1;
}
@@ -10659,6 +10710,18 @@
}
ship_primary_changed(shipp);
+ object* target;
+ if (Ai_info[shipp->ai_index].target_objnum != -1)
+ target = &Objects[Ai_info[shipp->ai_index].target_objnum];
+ else
+ target = NULL;
+ if (objp == Player_obj && Player_ai->target_objnum != -1)
+ target = &Objects[Player_ai->target_objnum];
+ Script_system.SetHookObjects(2, "User", objp, "Target", target);
+ Script_system.RunCondition(CHA_ONWPSELECTED, 0, NULL, objp);
+ Script_system.SetHookObjects(2, "User", objp, "Target", target);
+ Script_system.RunCondition(CHA_ONWPDESELECTED, 0, NULL, objp);
+
return 1;
}
@@ -10725,11 +10788,28 @@
if ( swp->current_secondary_bank != original_bank )
{
+ if(swp->current_primary_bank != original_bank)
+ swp->previous_primary_bank = original_bank;
+ else
+ swp->previous_primary_bank = swp->current_primary_bank;
if ( objp == Player_obj )
{
snd_play( &Snds[SND_SECONDARY_CYCLE], 0.0f );
}
ship_secondary_changed(shipp);
+
+ object* objp = &Objects[shipp->objnum];
+ object* target;
+ if (Ai_info[shipp->ai_index].target_objnum != -1)
+ target = &Objects[Ai_info[shipp->ai_index].target_objnum];
+ else
+ target = NULL;
+ if (objp == Player_obj && Player_ai->target_objnum != -1)
+ target = &Objects[Player_ai->target_objnum];
+ Script_system.SetHookObjects(2, "User", objp, "Target", target);
+ Script_system.RunCondition(CHA_ONWPSELECTED, 0, NULL, objp);
+ Script_system.SetHookObjects(2, "User", objp, "Target", target);
+ Script_system.RunCondition(CHA_ONWPDESELECTED, 0, NULL, objp);
return 1;
}
} // end if
Index: ship/ship.h
===================================================================
--- ship/ship.h (revision 6461)
+++ ship/ship.h (working copy)
@@ -105,6 +105,10 @@
int current_secondary_bank; // currently selected secondary bank
int current_tertiary_bank;
+ int previous_primary_bank;
+ int previous_secondary_bank; // currently selected secondary bank
+ int previous_tertiary_bank;
+
int next_primary_fire_stamp[MAX_SHIP_PRIMARY_BANKS]; // next time this primary bank can fire
int last_primary_fire_stamp[MAX_SHIP_PRIMARY_BANKS]; // last time this primary bank fired (mostly used by SEXPs)
int next_secondary_fire_stamp[MAX_SHIP_SECONDARY_BANKS]; // next time this secondary bank can fire
Index: weapon/weapon.h
===================================================================
--- weapon/weapon.h (revision 6461)
+++ weapon/weapon.h (working copy)
@@ -22,10 +22,12 @@
struct object;
struct ship_subsys;
-#define WP_UNUSED - 1
-#define WP_LASER 0 // PLEASE NOTE that this flag specifies ballistic primaries as well - Goober5000
-#define WP_MISSILE 1
-#define WP_BEAM 2
+#define WP_UNUSED -1
+#define WP_LASER 0 // PLEASE NOTE that this flag specifies ballistic primaries as well - Goober5000
+#define WP_MISSILE 1
+#define WP_BEAM 2
+#define WP_TERTIARY_LASER 3
+#define WP_TERTIARY_MISSILE 4
extern char *Weapon_subtype_names[];
extern int Num_weapon_subtypes;
@@ -109,7 +111,7 @@
#define WIF_HOMING (WIF_HOMING_HEAT | WIF_HOMING_ASPECT | WIF_HOMING_JAVELIN)
#define WIF_LOCKED_HOMING (WIF_HOMING_ASPECT | WIF_HOMING_JAVELIN)
-#define WIF_HURTS_BIG_SHIPS (WIF_BOMB | WIF_BEAM | WIF_HUGE | WIF_BIG_ONLY)
+#define WIF_HURTS_BIG_SHIPS (WIF_BOMB | WIF_BEAM | WIF_HUGE | WIF_BIG_ONLY)
#define WEAPON_EXHAUST_DELTA_TIME 75 // Delay in milliseconds between exhaust blobs
@@ -468,6 +470,7 @@
float target_lead_scaler;
int targeting_priorities[32];
int num_targeting_priorities;
+
} weapon_info;
// Data structure to track the active missiles
Index: weapon/weapons.cpp
===================================================================
--- weapon/weapons.cpp (revision 6461)
+++ weapon/weapons.cpp (working copy)
@@ -1107,6 +1107,10 @@
wip->subtype = WP_LASER;
} else if(!stricmp("Secondary", fname)) {
wip->subtype = WP_MISSILE;
+ } else if(!stricmp("Primary Equipment", fname)) {
+ wip->subtype = WP_TERTIARY_LASER;
+ } else if(!stricmp("Secondary Equipment", fname)) {
+ wip->subtype = WP_TERTIARY_MISSILE;
} else {
Warning(LOCATION, "Unknown subtype on weapon '%s'", wip->name);
}
@@ -1189,7 +1193,6 @@
if ( optional_string("$Submodel Rotation Acceleration:") )
stuff_float(&wip->weapon_submodel_rotate_accell);
-
// No POF or AVI file specified, render as special laser type.(?)
ubyte r,g,b;
@@ -2563,6 +2566,16 @@
required_string("#End");
}
+ if(optional_string("#Primary Mount Equipment"))
+ {
+ while (required_string_either("#End", "$Name:"))
+ {
+ if ( parse_weapon(WP_TERTIARY_LASER, Parsing_modular_table) < 0 )
+ {
+ continue;
+ }
+ }
+ }
if(optional_string("#Secondary Weapons"))
{
@@ -2575,6 +2588,17 @@
required_string("#End");
}
+ if(optional_string("#Secondary Mount Equipment"))
+ {
+ while (required_string_either("#End", "$Name:"))
+ {
+ if ( parse_weapon(WP_TERTIARY_MISSILE, Parsing_modular_table) < 0 )
+ {
+ continue;
+ }
+ }
+ }
+
if(optional_string("#Beam Weapons"))
{
while (required_string_either("#End", "$Name:")) {
@@ -4710,6 +4734,11 @@
return -1;
}
+ //If this is an "Equipment" type weapon, we're done here
+ if (weapon_type == WP_TERTIARY_LASER || weapon_type == WP_TERTIARY_MISSILE)
+ return -1;
+
+
num_deleted = 0;
if (Num_weapons >= MAX_WEAPONS-5) {
Example tbl using the $Weapon Class hook:
#Conditional Hooks
$Weapon Class: Subach HL-7
$On Weapon Equipped:
[
subach = {}
target = {}
subach.obj = hv.User
if subach.obj:isValid() then
subach.ship = mn.getObjectFromSignature(subach.obj:getSignature())
end
target.obj = hv.Target
targetname = nil
if target.obj ~= nil then
if target.obj:isValid() then
target.ship = mn.getObjectFromSignature(target.obj:getSignature())
end
if target.ship ~= nil then
targetname = target.ship.Name
end
end
if targetname == nil then
targetname = "Nothing!"
end
ba.print("Ship " .. subach.ship.Name .. " carries a Subach while targeting " .. targetname .. "\n")
]
$On Weapon Fired:
[
subach = {}
target = {}
subach.obj = hv.User
if subach.obj:isValid() then
subach.ship = mn.getObjectFromSignature(subach.obj:getSignature())
end
target.obj = hv.Target
targetname = nil
if target.obj ~= nil then
if target.obj:isValid() then
target.ship = mn.getObjectFromSignature(target.obj:getSignature())
end
if target.ship ~= nil then
targetname = target.ship.Name
end
end
if targetname == nil then
targetname = "Nothing!"
end
ba.print("Ship " .. subach.ship.Name .. " has fired a Subach while targeting " .. targetname .. "\n")
]
$On Weapon Selected:
[
subach = {}
target = {}
subach.obj = hv.User
if subach.obj:isValid() then
subach.ship = mn.getObjectFromSignature(subach.obj:getSignature())
end
target.obj = hv.Target
targetname = nil
if target.obj ~= nil then
if target.obj:isValid() then
target.ship = mn.getObjectFromSignature(target.obj:getSignature())
end
if target.ship ~= nil then
targetname = target.ship.Name
end
end
if targetname == nil then
targetname = "Nothing!"
end
ba.print("Ship " .. subach.ship.Name .. " has selected a Subach while targeting " .. targetname .. "\n")
]
$On Weapon Deselected:
[
subach = {}
target = {}
subach.obj = hv.User
if subach.obj:isValid() then
subach.ship = mn.getObjectFromSignature(subach.obj:getSignature())
end
target.obj = hv.Target
targetname = nil
if target.obj ~= nil then
if target.obj:isValid() then
target.ship = mn.getObjectFromSignature(target.obj:getSignature())
end
if target.ship ~= nil then
targetname = target.ship.Name
end
end
if targetname == nil then
targetname = "Nothing!"
end
ba.print("Ship " .. subach.ship.Name .. " has not selected a Subach while targeting " .. targetname .. "\n")
]
#End