Some time ago, I asked m!m if he could try to incorporate weapon ricochet function into his particle script. He said that it wasn't possible, but perhaps could be done as an in-engine feature. Initially, I didn't want to bother you with that, but recently m!m told me that he looked into this and send me a patch for the feature.
The problem is, it doesn't really work. The weapon created by it is removed as soon as it hits something. Could you take a look at this?
The patch:
Index: code/object/collideshipweapon.cpp
===================================================================
--- code/object/collideshipweapon.cpp (revision 7936)
+++ code/object/collideshipweapon.cpp (working copy)
@@ -64,16 +64,61 @@
* Deal with weapon-ship hit stuff.
* Separated from check_collision routine below because of multiplayer reasons.
*/
-void ship_weapon_do_hit_stuff(object *ship_obj, object *weapon_obj, vec3d *world_hitpos, vec3d *hitpos, int quadrant_num, int submodel_num, vec3d /*not a pointer intentionaly*/ hit_dir)
+bool ship_weapon_do_hit_stuff(object *ship_obj, object *weapon_obj, vec3d *world_hitpos, vec3d *hitpos, int quadrant_num, int submodel_num, vec3d /*not a pointer intentionaly*/ hit_dir)
{
weapon *wp = &Weapons[weapon_obj->instance];
weapon_info *wip = &Weapon_info[wp->weapon_info_index];
ship *shipp = &Ships[ship_obj->instance];
float damage;
- vec3d force;
+ vec3d force;
+
+ bool ricochet_happened = false;
+ if (Armor_types[shipp->armor_type_idx].GetRicochetProbability(wip->damage_type_idx) > 0.0f)
+ {
+ ArmorType *type = &Armor_types[shipp->armor_type_idx];
+ vec3d vel = weapon_obj->phys_info.vel;
+
+ vm_vec_normalize(&vel);
+ vm_vec_scale(&vel, -1.0f); // Required to get a correct result from vm_vec_dot
+
+ // assume hit_dir is normalized
+ float dot = vm_vec_dot(&vel, &hit_dir);
+
+ float result = dot * type->GetRicochetProbability(wip->damage_type_idx);
+
+ ricochet_happened = result > ((float) myrand() / RAND_MAX);
+
+ if (ricochet_happened)
+ {
+ float pct = get_hull_pct(ship_obj);
+ if (pct > type->GetRicochetStartLevel(wip->damage_type_idx))
+ ricochet_happened = false;
+
+ if (pct < type->GetRicochetEndLevel(wip->damage_type_idx))
+ ricochet_happened = false;
+ }
+
+ if (ricochet_happened)
+ {
+ wp->damage_scale *= type->GetRicochetDamageFactor(wip->damage_type_idx) * dot;
+
+ vec3d vec;
+ float real_dot = vm_vec_dot(&weapon_obj->phys_info.vel, &hit_dir);
+
+ vm_vec_scale2(&vec, real_dot, 2);
+
+ vec3d out;
+
+ vm_vec_sub(&out, &weapon_obj->phys_info.vel, &vec);
+
+ weapon_obj->phys_info.vel = out;
+ weapon_obj->phys_info.desired_vel = out;
+ }
+ }
+
// Apply hit & damage & stuff to weapon
- weapon_hit(weapon_obj, ship_obj, world_hitpos, quadrant_num);
+ weapon_hit(weapon_obj, ship_obj, world_hitpos, quadrant_num, ricochet_happened);
damage = wip->damage;
@@ -109,6 +154,7 @@
ship_apply_whack( &force, hitpos, ship_obj );
}
+ return ricochet_happened;
}
extern int Framecount;
Index: code/ship/ship.cpp
===================================================================
--- code/ship/ship.cpp (revision 7936)
+++ code/ship/ship.cpp (working copy)
@@ -16461,6 +16461,46 @@
return 0.0f;
}
+float ArmorType::GetRicochetProbability(int damage_type_idx)
+{
+ ArmorDamageType *adtp = getDamageType(damage_type_idx);
+
+ if (adtp == NULL)
+ return 0.0f;
+
+ return adtp->ricochet_probability;
+}
+
+float ArmorType::GetRicochetDamageFactor(int damage_type_idx)
+{
+ ArmorDamageType *adtp = getDamageType(damage_type_idx);
+
+ if (adtp == NULL)
+ return 0.0f;
+
+ return adtp->ricochet_damage_factor;
+}
+
+float ArmorType::GetRicochetStartLevel(int damage_type_idx)
+{
+ ArmorDamageType *adtp = getDamageType(damage_type_idx);
+
+ if (adtp == NULL)
+ return 0.0f;
+
+ return adtp->ricochet_start_level;
+}
+
+float ArmorType::GetRicochetEndLevel(int damage_type_idx)
+{
+ ArmorDamageType *adtp = getDamageType(damage_type_idx);
+
+ if (adtp == NULL)
+ return 0.0f;
+
+ return adtp->ricochet_end_level;
+}
+
//***********************************Member functions
ArmorType::ArmorType(char* in_name)
@@ -16544,6 +16584,47 @@
no_content = false;
}
+ adt.ricochet_probability = 0.0f;
+ adt.ricochet_damage_factor = 1.0f;
+ adt.ricochet_start_level = 1.0f;
+ adt.ricochet_end_level = 0.0f;
+
+ if (optional_string("+Weapon Ricochet Probability:"))
+ {
+ stuff_float(&temp_float);
+ CLAMP(temp_float, 0.0f, 1.0f);
+
+ adt.ricochet_probability = temp_float;
+ no_content = false;
+ }
+
+ if (optional_string("+Weapon Ricochet Damage Factor:"))
+ {
+ stuff_float(&temp_float);
+ CLAMP(temp_float, 0.0f, 1.0f);
+
+ adt.ricochet_damage_factor = temp_float;
+ no_content = false;
+ }
+
+ if (optional_string("+Weapon Ricochet Start Level:"))
+ {
+ stuff_float(&temp_float);
+ CLAMP(temp_float, 0.0f, 100.0f);
+
+ adt.ricochet_start_level = temp_float / 100.0f;
+ no_content = false;
+ }
+
+ if (optional_string("+Weapon Ricochet End Level:"))
+ {
+ stuff_float(&temp_float);
+ CLAMP(temp_float, 0.0f, 100.0f);
+
+ adt.ricochet_end_level = temp_float / 100.0f;
+ no_content = false;
+ }
+
//If we have calculations in this damage type, add it
if(!no_content)
{
@@ -16556,6 +16637,26 @@
} while(optional_string("$Damage Type:"));
}
+ArmorDamageType *ArmorType::getDamageType(int damage_type_idx)
+{
+ if (damage_type_idx < 0)
+ return NULL;
+
+ ArmorDamageType *adtp = NULL;
+
+ //Find the entry in the weapon that corresponds to the given weapon damage type
+ for(int i = 0; i < (int) DamageTypes.size(); i++)
+ {
+ if(DamageTypes[i].DamageTypeIndex == damage_type_idx)
+ {
+ adtp = &DamageTypes[i];
+ break;
+ }
+ }
+
+ return adtp;
+}
+
//********************************Global functions
int armor_type_get_idx(char* name)
Index: code/ship/ship.h
===================================================================
--- code/ship/ship.h (revision 7936)
+++ code/ship/ship.h (working copy)
@@ -176,6 +176,11 @@
float piercing_start_pct;
int piercing_type;
+ // ricochet data
+ float ricochet_probability;
+ float ricochet_damage_factor;
+ float ricochet_start_level;
+ float ricochet_end_level;
public:
void clear();
};
@@ -186,6 +191,10 @@
char Name[NAME_LENGTH];
SCP_vector<ArmorDamageType> DamageTypes;
+
+protected:
+ ArmorDamageType *getDamageType(int damage_type_idx);
+
public:
ArmorType(char* in_name);
int flags;
@@ -197,6 +206,12 @@
float GetShieldPiercePCT(int damage_type_idx);
int GetPiercingType(int damage_type_idx);
float GetPiercingLimit(int damage_type_idx);
+
+ float GetRicochetProbability(int damage_type_idx);
+ float GetRicochetDamageFactor(int damage_type_idx);
+ float GetRicochetStartLevel(int damage_type_idx);
+ float GetRicochetEndLevel(int damage_type_idx);
+
//Set
void ParseData();
Index: code/weapon/weapon.h
===================================================================
--- code/weapon/weapon.h (revision 7936)
+++ code/weapon/weapon.h (working copy)
@@ -214,6 +214,7 @@
float damage_ship[MAX_WEP_DAMAGE_SLOTS]; // damage applied from each player
int damage_ship_id[MAX_WEP_DAMAGE_SLOTS]; // signature of the damager (corresponds to each entry in damage_ship)
+ float damage_scale; // a factor to influence the damage dealt by this weapon
} weapon;
@@ -599,7 +600,7 @@
void weapon_maybe_spew_particle(object *obj);
bool weapon_armed(weapon *wp, bool hit_target);
-void weapon_hit( object * weapon_obj, object * other_obj, vec3d * hitpos, int quadrant = -1 );
+void weapon_hit( object * weapon_obj, object * other_obj, vec3d * hitpos, int quadrant = -1, bool ricichet_happened = false );
int cmeasure_name_lookup(char *name);
void spawn_child_weapons( object *objp );
Index: code/weapon/weapons.cpp
===================================================================
--- code/weapon/weapons.cpp (revision 7936)
+++ code/weapon/weapons.cpp (working copy)
@@ -5375,6 +5375,8 @@
wp->damage_ship_id[i] = -1;
}
+ wp->damage_scale = 1.0f;
+
return objnum;
}
// Spawn child weapons from object *objp.
@@ -5928,7 +5930,7 @@
// This function is called when a weapon hits something (or, in the case of
// missiles explodes for any particular reason)
//
-void weapon_hit( object * weapon_obj, object * other_obj, vec3d * hitpos, int quadrant )
+void weapon_hit( object * weapon_obj, object * other_obj, vec3d * hitpos, int quadrant, bool ricochet_happened )
{
Assert(weapon_obj != NULL);
if(weapon_obj == NULL){
@@ -6056,7 +6058,8 @@
}
}
- weapon_obj->flags |= OF_SHOULD_BE_DEAD;
+ if (!ricochet_happened)
+ weapon_obj->flags |= OF_SHOULD_BE_DEAD;
//Set shockwaves flag
int sw_flag = SW_WEAPON;
@@ -6843,5 +6846,5 @@
}
}
- return total_scale;
+ return total_scale * wp->damage_scale;
}