This patch allows shields which extend a given distance away from the ship's hull, without requiring a shield mesh. This is good for capships, ships with odd shapes with which shield meshes tend to not work right, or ships with large moving parts which would require a huge shield mesh to cover. Or simply if you don't feel like making shield meshes. However, the shield impact effects which normally get projected onto the shield mesh itself won't occur.
Since it'd look bad if, for example, you were close to a capship and your fighter lasers would hit its shields as soon as you pressed the trigger, weapons are always guaranteed to travel at least a distance equal to the thickness of the shield before they're allowed to impact it; unless of course the weapon hits the hull, in which case it is treated as a shield hit, just like when surface shields are used.
To enable the shields for a particular ship type, you need to give the ship type the "auto spread shields" flag and then specify the shield thickness underneath the $Shields attribute:
...
$SBank Capacity: ( )
$Shields: 500
+Auto Spread: 50 ;makes the shield 50m thick
+Spread From LOD: 2 ;OPTIONAL: projects the shield based on LOD2, helps performance a lot
$Shield Color: 100, 100, 100
...
Later on I might make it a ship-specific rather than shiptype-specific attribute and expose it to Lua, but for now it's defined per shiptype only. I could also add an option to make hits closer to the hull deal more damage than hits to the outer boundary of the shield if someone has a particular need for that, or for disabling the above-mentioned exception to the minimum weapon travel distances. You can make other suggestions, but I won't promise anything.
And here's the patch itself (also as an attachment):
Index: ship/ship.h
===================================================================
--- ship/ship.h (revision 9629)
+++ ship/ship.h (working copy)
@@ -918,8 +918,9 @@
#define SIF2_NO_ETS (1 << 13) // The E - No ETS on this ship class
#define SIF2_NO_LIGHTING (1 << 14) // Valathil - No lighting for this ship
#define SIF2_DYN_PRIMARY_LINKING (1 << 15) // RSAXVC - Dynamically generate weapon linking options
+#define SIF2_AUTO_SPREAD_SHIELDS (1 << 16) // zookeeper - auto spread shields
// !!! IF YOU ADD A FLAG HERE BUMP MAX_SHIP_FLAGS !!!
-#define MAX_SHIP_FLAGS 16 // Number of distinct flags for flags field in ship_info struct
+#define MAX_SHIP_FLAGS 17 // Number of distinct flags for flags field in ship_info struct
#define SIF_DEFAULT_VALUE 0
#define SIF2_DEFAULT_VALUE 0
@@ -1290,6 +1291,8 @@
float max_hull_strength; // Max hull strength of this class of ship.
float max_shield_strength;
+ float auto_shield_spread;
+ int auto_shield_spread_from_lod;
float hull_repair_rate; //How much of the hull is repaired every second
float subsys_repair_rate; //How fast
Index: ship/ship.cpp
===================================================================
--- ship/ship.cpp (revision 9629)
+++ ship/ship.cpp (working copy)
@@ -305,6 +305,7 @@
{ "no pain flash", SIF2_NO_PAIN_FLASH, 1 },
{ "no ets", SIF2_NO_ETS, 1 },
{ "no lighting", SIF2_NO_LIGHTING, 1 },
+ { "auto spread shields", SIF2_AUTO_SPREAD_SHIELDS, 1 },
// to keep things clean, obsolete options go last
{ "ballistic primaries", -1, 255 }
@@ -806,6 +807,8 @@
}
sip->max_shield_strength = 0.0f;
+ sip->auto_shield_spread = 0.0f;
+ sip->auto_shield_spread_from_lod = -1;
sip->shield_color[0] = 255;
sip->shield_color[1] = 255;
sip->shield_color[2] = 255;
@@ -2428,9 +2431,23 @@
stuff_bool_list(sip->draw_secondary_models, sip->num_secondary_banks);
}
- if(optional_string("$Shields:"))
+ if(optional_string("$Shields:")) {
stuff_float(&sip->max_shield_strength);
+ if(optional_string("+Auto Spread:")) {
+ stuff_float(&sip->auto_shield_spread);
+ }
+ if(optional_string("+Spread From LOD:")) {
+ int temp;
+ stuff_int(&temp);
+
+ if (temp > sip->num_detail_levels)
+ Warning(LOCATION, "+Spread From LOD for %s was %i whereas ship only has %i detail levels, ignoring...", sip->name, temp, sip->num_detail_levels);
+ else
+ sip->auto_shield_spread_from_lod = temp;
+ }
+ }
+
// optional shield color
if(optional_string("$Shield Color:")){
stuff_ubyte(&sip->shield_color[0]);
@@ -8782,6 +8799,12 @@
} else
shipp->shield_integrity = NULL;
+ // Bump the object radius to ensure that collision detection works right
+ // even when spread shields extend outside the models natural radius
+ if (sip->flags2 & SIF2_AUTO_SPREAD_SHIELDS) {
+ Objects[objnum].radius += sip->auto_shield_spread;
+ }
+
// allocate memory for keeping glow point bank status (enabled/disabled)
{
bool val = true; // default value, enabled
Index: object/collideshipweapon.cpp
===================================================================
--- object/collideshipweapon.cpp (revision 9629)
+++ object/collideshipweapon.cpp (working copy)
@@ -193,41 +193,136 @@
// will absorb it when it hits the hull instead. This has no fancy graphical effect, though.
// Someone should make one.
- // set flags
- mc_shield.flags = MC_CHECK_SHIELD;
- mc_hull.flags = MC_CHECK_MODEL;
-
// check both kinds of collisions
- int shield_collision = (pm->shield.ntris > 0) ? model_collide(&mc_shield) : 0;
- int hull_collision = model_collide(&mc_hull);
+ int shield_collision = 0;
+ int hull_collision = 0;
// check shields for impact
- if (!(ship_objp->flags & OF_NO_SHIELDS))
- {
- // pick out the shield quadrant
- if (shield_collision)
- quadrant_num = get_quadrant(&mc_shield.hit_point);
- else if (hull_collision && (sip->flags2 & SIF2_SURFACE_SHIELDS)) {
- vec3d local_pos, local_pos_rot;
- vm_vec_sub(&local_pos, &mc_hull.hit_point_world, &ship_objp->pos);
- vm_vec_rotate(&local_pos_rot, &local_pos, &ship_objp->orient);
- quadrant_num = get_quadrant(&local_pos_rot);
+ if (!(ship_objp->flags & OF_NO_SHIELDS)) {
+ if (sip->flags2 & SIF2_AUTO_SPREAD_SHIELDS) {
+ // The weapon is not allowed to impact the shield before it reaches this point
+ vec3d shield_ignored_until = weapon_objp->last_pos;
+
+ float weapon_flown_for = vm_vec_dist(&wp->start_pos, &weapon_objp->last_pos);
+
+ // If weapon hasn't yet flown a distance greater than the maximum ignore
+ // range, then some part of the currently checked range needs to be
+ // ignored
+ if (weapon_flown_for < sip->auto_shield_spread) {
+ vm_vec_sub(&shield_ignored_until, &weapon_end_pos, &wp->start_pos);
+ vm_vec_normalize(&shield_ignored_until);
+ vm_vec_scale(&shield_ignored_until, sip->auto_shield_spread);
+ vm_vec_add2(&shield_ignored_until, &wp->start_pos);
+ }
+
+ float this_range = vm_vec_dist(&weapon_objp->last_pos, &weapon_end_pos);
+
+ // The range during which the weapon is not allowed to collide with the
+ // shield, except if it actually hits the hull
+ float ignored_range;
+
+ // If the weapon has not yet surpassed the ignore range, calculate the
+ // remaining ignore range
+ if (vm_vec_dist(&wp->start_pos, &shield_ignored_until) > weapon_flown_for)
+ ignored_range = vm_vec_dist(&weapon_objp->last_pos, &shield_ignored_until);
+ else
+ ignored_range = 0.0f;
+
+ // The range during which the weapon may impact the shield
+ float active_range = this_range - ignored_range;
+
+ // During the ignored range, we only check for a ray collision with
+ // the model
+ if (ignored_range > 0.0f) {
+ mc_shield.flags = MC_CHECK_MODEL;
+ mc_shield.p1 = &shield_ignored_until;
+
+ shield_collision = model_collide(&mc_shield);
+
+ mc_shield.p1 = &weapon_end_pos;
+ mc_shield.hit_dist = mc_shield.hit_dist * (ignored_range / this_range);
+ }
+
+ // If no collision with the model found in the ignore range, only
+ // then do we check for sphereline collisions with the model during the
+ // non-ignored range
+ if (!shield_collision && weapon_flown_for + this_range > sip->auto_shield_spread) {
+ mc_shield.p0 = &shield_ignored_until;
+
+ mc_shield.p1 = &weapon_end_pos;
+
+ mc_shield.radius = sip->auto_shield_spread;
+
+ mc_shield.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE;
+
+ if (sip->auto_shield_spread_from_lod > -1) {
+ polymodel *pm = model_get(sip->model_num);
+ mc_shield.submodel_num = pm->detail[sip->auto_shield_spread_from_lod];
+ }
+
+ shield_collision = model_collide(&mc_shield);
+
+ mc_shield.submodel_num = -1;
+
+ // Because we manipulated p0 and p1 above, hit_dist will be
+ // relative to the values we used, not the values the rest of
+ // the code expects; this fixes that
+ mc_shield.p0 = &weapon_objp->last_pos;
+ mc_shield.p1 = &weapon_end_pos;
+ mc_shield.hit_dist = (ignored_range + (active_range * mc_shield.hit_dist)) / this_range;
+ }
+
+ if (shield_collision) {
+ // If we used a sphereline check, then the collision point will lie
+ // somewhere on the ship's hull; this re-positions it to lie on the
+ // correct point along the weapon's path
+ if (mc_shield.flags & MC_CHECK_SPHERELINE) {
+ vec3d tempv;
+ vm_vec_sub(&tempv, mc_shield.p1, mc_shield.p0);
+ vm_vec_scale(&tempv, mc_shield.hit_dist);
+ vm_vec_add2(&tempv, mc_shield.p0);
+ mc_shield.hit_point_world = tempv;
+ }
+
+ // Re-calculate hit_point because it's likely pointing to the wrong
+ // place
+ vec3d tempv;
+ vm_vec_sub(&tempv, &mc_shield.hit_point_world, &ship_objp->pos);
+ vm_vec_rotate(&mc_shield.hit_point, &tempv, &ship_objp->orient);
+ }
+ } else if (sip->flags2 & SIF2_SURFACE_SHIELDS) {
+ mc_shield.flags = MC_CHECK_MODEL;
+ shield_collision = model_collide(&mc_shield);
+ } else {
+ // Normal collision check against a shield mesh
+ mc_shield.flags = MC_CHECK_SHIELD;
+ shield_collision = (pm->shield.ntris > 0) ? model_collide(&mc_shield) : 0;
}
+ }
+ // If we found a shield collision but were only checking for a simple model
+ // collision, we can re-use the same collision info for the hull as well
+ if (shield_collision && mc_shield.flags == MC_CHECK_MODEL) {
+ memcpy(&mc_hull, &mc_shield, sizeof(mc_info));
+ hull_collision = shield_collision;
+ } else {
+ mc_hull.flags = MC_CHECK_MODEL;
+ hull_collision = model_collide(&mc_hull);
+ }
+
+ if (shield_collision) {
+ // pick out the shield quadrant
+ quadrant_num = get_quadrant(&mc_shield.hit_point);
+
// make sure that the shield is active in that quadrant
- if ((quadrant_num >= 0) && ((shipp->flags & SF_DYING) || !ship_is_shield_up(ship_objp, quadrant_num)))
+ if (shipp->flags & SF_DYING || !ship_is_shield_up(ship_objp, quadrant_num))
quadrant_num = -1;
// see if we hit the shield
- if (quadrant_num >= 0)
- {
+ if (quadrant_num >= 0) {
// do the hit effect
- if (shield_collision)
- add_shield_point(OBJ_INDEX(ship_objp), mc_shield.shield_hit_tri, &mc_shield.hit_point);
- else {
- /* TODO */
- }
-
+ add_shield_point(OBJ_INDEX(ship_objp), mc_shield.shield_hit_tri, &mc_shield.hit_point);
+
// if this weapon pierces the shield, then do the hit effect, but act like a shield collision never occurred;
// otherwise, we have a valid hit on this shield
if (wip->wi_flags2 & WIF2_PIERCE_SHIELDS)
@@ -339,6 +434,8 @@
Assert( ship->type == OBJ_SHIP );
Assert( weapon->type == OBJ_WEAPON );
+ ship_info *sip = &Ship_info[Ships[ship->instance].ship_info_index];
+
// Don't check collisions for player if past first warpout stage.
if ( Player->control_mode > PCM_WARPOUT_STAGE1) {
if ( ship == Player_obj )
@@ -352,11 +449,13 @@
// If it does hit, don't check the pair until about 200 ms before collision.
// If it does not hit and is within error tolerance, cull the pair.
- if ( (Ship_info[Ships[ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) && (Weapon_info[Weapons[weapon->instance].weapon_info_index].subtype == WP_LASER) ) {
+ if ( (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) && (Weapon_info[Weapons[weapon->instance].weapon_info_index].subtype == WP_LASER) ) {
// Check when within ~1.1 radii.
// This allows good transition between sphere checking (leaving the laser about 200 ms from radius) and checking
// within the sphere with little time between. There may be some time for "small" big ships
- if ( vm_vec_dist_squared(&ship->pos, &weapon->pos) < (1.2f*ship->radius*ship->radius) ) {
+ // Note: culling ships with auto spread shields seems to waste more performance than it saves,
+ // so we're not doing that here
+ if ( !(sip->flags2 & SIF2_AUTO_SPREAD_SHIELDS) && vm_vec_dist_squared(&ship->pos, &weapon->pos) < (1.2f*ship->radius*ship->radius) ) {
return check_inside_radius_for_big_ships( ship, weapon, pair );
}
}
P.S. Feel free to suggest a better name.
[attachment deleted by ninja]