Skip to content

Commit 38c8179

Browse files
committed
ETS updates
Two updates to the energy transfer system. 1. Previously, for fighters missing a system (e.g. shields), the energy would be treated as if all energy from that system was routed to the other systems. So, for example, a fighter's typical speed would be faster without shields than with shields. A new AI profile flag now treats the other systems as if the energy had not been transferred. 2. At some point in development, Volition intended the `$Power Output` ships.tbl field to affect ETS. A new AI profile flag now treats the power output as a factor of energy applied to energy systems.
1 parent 7246bc3 commit 38c8179

8 files changed

Lines changed: 91 additions & 27 deletions

File tree

code/ai/ai_flags.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ namespace AI {
173173
Standard_strafe_used_more,
174174
Unify_usage_ai_shield_manage_delay,
175175
Disable_ai_transferring_energy,
176+
ETS_uses_power_output,
177+
ETS_energy_same_regardless_of_system_presence,
176178

177179
NUM_VALUES
178180
};

code/ai/ai_profiles.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,11 @@ void parse_ai_profiles_tbl(const char *filename)
701701

702702
set_flag(profile, "$disable AI transferring energy:", AI::Profile_Flags::Disable_ai_transferring_energy);
703703

704+
set_flag(profile, "$ETS uses ship class power output:", AI::Profile_Flags::ETS_uses_power_output);
705+
706+
set_flag(profile, "$ETS energy same regardless of system presence:", AI::Profile_Flags::ETS_energy_same_regardless_of_system_presence);
707+
708+
704709
// end of options ----------------------------------------
705710

706711
// if we've been through once already and are at the same place, force a move

code/ai/aicode.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2097,7 +2097,7 @@ float get_wing_lowest_av_ab_speed(object *objp)
20972097
}
20982098
else
20992099
{
2100-
recharge_scale = Energy_levels[shipp->engine_recharge_index] * 2.0f * The_mission.ai_profile->afterburner_recharge_scale[Game_skill_level];
2100+
recharge_scale = ets_power_factor(objp) * Energy_levels[shipp->engine_recharge_index] * 2.0f * The_mission.ai_profile->afterburner_recharge_scale[Game_skill_level];
21012101
recharge_scale = sip->afterburner_recover_rate * recharge_scale / (sip->afterburner_burn_rate + sip->afterburner_recover_rate * recharge_scale);
21022102
lowest_max_av_ab_speed = recharge_scale * (objp->phys_info.afterburner_max_vel.xyz.z - objp->phys_info.max_vel.xyz.z) + objp->phys_info.max_vel.xyz.z;
21032103
}
@@ -2125,7 +2125,7 @@ float get_wing_lowest_av_ab_speed(object *objp)
21252125
}
21262126
else
21272127
{
2128-
recharge_scale = Energy_levels[shipp->engine_recharge_index] * 2.0f * The_mission.ai_profile->afterburner_recharge_scale[Game_skill_level];
2128+
recharge_scale = ets_power_factor(o) * Energy_levels[oshipp->engine_recharge_index] * 2.0f * The_mission.ai_profile->afterburner_recharge_scale[Game_skill_level];
21292129
recharge_scale = osip->afterburner_recover_rate * recharge_scale / (osip->afterburner_burn_rate + osip->afterburner_recover_rate * recharge_scale);
21302130
cur_max = recharge_scale * (o->phys_info.afterburner_max_vel.xyz.z - o->phys_info.max_vel.xyz.z) + o->phys_info.max_vel.xyz.z;
21312131
}
@@ -4789,7 +4789,7 @@ void ai_fly_to_target_position(const vec3d* target_pos, bool* pl_done_p=NULL, bo
47894789
max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
47904790
max_allowed_ab_speed = 0.95f * get_wing_lowest_av_ab_speed(Pl_objp);
47914791
if (ab_allowed) {
4792-
float self_ab_scale = Energy_levels[shipp->engine_recharge_index] * 2.0f * The_mission.ai_profile->afterburner_recharge_scale[Game_skill_level];
4792+
float self_ab_scale = ets_power_factor(Pl_objp) * Energy_levels[shipp->engine_recharge_index] * 2.0f * The_mission.ai_profile->afterburner_recharge_scale[Game_skill_level];
47934793
self_ab_scale = sip->afterburner_recover_rate * self_ab_scale / (sip->afterburner_burn_rate + sip->afterburner_recover_rate * self_ab_scale);
47944794
self_ab_speed = 0.95f * (self_ab_scale * (Pl_objp->phys_info.afterburner_max_vel.xyz.z - Pl_objp->phys_info.max_vel.xyz.z) + Pl_objp->phys_info.max_vel.xyz.z);
47954795
}

code/hud/hudets.cpp

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
#include "weapon/weapon.h"
2626
#include "globalincs/alphacolors.h"
2727

28-
float Energy_levels[NUM_ENERGY_LEVELS] = {0.0f, 0.0833f, 0.167f, 0.25f, 0.333f, 0.417f, 0.5f, 0.583f, 0.667f, 0.75f, 0.833f, 0.9167f, 1.0f};
28+
float Energy_levels[NUM_ENERGY_LEVELS] = {0.0f, 1.0f/12, 2.0f/12, 3.0f/12, 4.0f/12, 5.0f/12, 6.0f/12, 7.0f/12, 8.0f/12, 9.0f/12, 10.0f/12, 11.0f/12, 1.0f};
2929
bool Weapon_energy_cheat = false;
3030

3131
// -------------------------------------------------------------------------------------------------
@@ -54,6 +54,53 @@ void ets_init_ship(object* obj)
5454
set_default_recharge_rates(obj);
5555
}
5656

57+
int ets_properties(object* objp)
58+
{
59+
int properties = 0;
60+
ship* ship_p = &Ships[objp->instance];
61+
ship_info* ship_info_p = &Ship_info[ship_p->ship_info_index];
62+
63+
if (ship_has_energy_weapons(ship_p))
64+
properties |= HAS_WEAPONS;
65+
66+
if (!(objp->flags[Object::Object_Flags::No_shields]) && !ship_info_p->flags[Ship::Info_Flags::Intrinsic_no_shields])
67+
properties |= HAS_SHIELDS;
68+
69+
if (ship_has_engine_power(ship_p))
70+
properties |= HAS_ENGINES;
71+
72+
return properties;
73+
}
74+
75+
// returns the energy that should be dedicated towards a single ETS system
76+
// in retail, this is always 1.0
77+
float ets_power_factor(object *objp, bool include_power_output)
78+
{
79+
auto shipp = &Ships[objp->instance];
80+
int properties = ets_properties(objp);
81+
82+
if (The_mission.ai_profile->flags[AI::Profile_Flags::ETS_energy_same_regardless_of_system_presence] && (properties != (HAS_WEAPONS | HAS_SHIELDS | HAS_ENGINES)))
83+
{
84+
// in retail, the effect of having a missing system (e.g. an unshielded ship) is as if all that energy were redirected to other systems, so take the inverse of that
85+
constexpr float missing_single_factor = 2.0f/3;
86+
constexpr float missing_double_factor = 1.0f/3;
87+
88+
float missing_factor = (properties == HAS_WEAPONS || properties == HAS_SHIELDS || properties == HAS_ENGINES) ? missing_double_factor : missing_single_factor;
89+
90+
if (The_mission.ai_profile->flags[AI::Profile_Flags::ETS_uses_power_output] && include_power_output)
91+
return Ship_info[shipp->ship_info_index].power_output * missing_factor;
92+
else
93+
return missing_factor;
94+
}
95+
else
96+
{
97+
if (The_mission.ai_profile->flags[AI::Profile_Flags::ETS_uses_power_output] && include_power_output)
98+
return Ship_info[shipp->ship_info_index].power_output;
99+
else
100+
return 1.0f;
101+
}
102+
}
103+
57104
// -------------------------------------------------------------------------------------------------
58105
// update_ets() is called once per frame for every OBJ_SHIP in the game.
59106
// The amount of energy to send to the weapons and shields is calculated.
@@ -83,14 +130,15 @@ void update_ets(object* objp, float fl_frametime)
83130
return;
84131
}
85132

133+
// See? Volition did, at one point, intend for power output to affect ETS!
86134
// new_energy = fl_frametime * sinfo_p->power_output;
87135

88136
// update weapon energy
89137
max_new_weapon_energy = fl_frametime * ship_p->max_weapon_regen_per_second * max_g;
90138
if ( objp->flags[Object::Object_Flags::Player_ship] ) {
91-
ship_p->weapon_energy += Energy_levels[ship_p->weapon_recharge_index] * max_new_weapon_energy * The_mission.ai_profile->weapon_energy_scale[Game_skill_level];
139+
ship_p->weapon_energy += ets_power_factor(objp) * Energy_levels[ship_p->weapon_recharge_index] * max_new_weapon_energy * The_mission.ai_profile->weapon_energy_scale[Game_skill_level];
92140
} else {
93-
ship_p->weapon_energy += Energy_levels[ship_p->weapon_recharge_index] * max_new_weapon_energy;
141+
ship_p->weapon_energy += ets_power_factor(objp) * Energy_levels[ship_p->weapon_recharge_index] * max_new_weapon_energy;
94142
}
95143

96144
if ( ship_p->weapon_energy > sinfo_p->max_weapon_reserve ){
@@ -100,9 +148,9 @@ void update_ets(object* objp, float fl_frametime)
100148
float shield_delta;
101149
max_new_shield_energy = fl_frametime * ship_p->max_shield_regen_per_second * shield_get_max_strength(ship_p, true); // recharge rate is unaffected by $Max Shield Recharge
102150
if ( objp->flags[Object::Object_Flags::Player_ship] ) {
103-
shield_delta = Energy_levels[ship_p->shield_recharge_index] * max_new_shield_energy * The_mission.ai_profile->shield_energy_scale[Game_skill_level];
151+
shield_delta = ets_power_factor(objp) * Energy_levels[ship_p->shield_recharge_index] * max_new_shield_energy * The_mission.ai_profile->shield_energy_scale[Game_skill_level];
104152
} else {
105-
shield_delta = Energy_levels[ship_p->shield_recharge_index] * max_new_shield_energy;
153+
shield_delta = ets_power_factor(objp) * Energy_levels[ship_p->shield_recharge_index] * max_new_shield_energy;
106154
}
107155

108156
if (Missiontime - Ai_info[ship_p->ai_index].last_hit_time < fl2f(sinfo_p->shield_regen_hit_delay))
@@ -170,16 +218,32 @@ void update_ets(object* objp, float fl_frametime)
170218

171219
float ets_get_max_speed(object* objp, float engine_energy)
172220
{
221+
// NOTE: ets_power_factor() doesn't need to be called in this function, because all the factors cancel out. But
222+
// the system presence does need to be checked since it affects the recharge indexes.
223+
173224
Assertion(objp != NULL, "Invalid object pointer passed!");
174225
Assertion(objp->type == OBJ_SHIP, "Object needs to be a ship object!");
175226
Assertion(engine_energy >= 0.0f && engine_energy <= 1.0f, "Invalid float passed, needs to be in [0, 1], was %f!", engine_energy);
176227

177228
ship* shipp = &Ships[objp->instance];
178-
179229
ship_info* sip = &Ship_info[shipp->ship_info_index];
180230

231+
float initial_engine_recharge_energy_level;
232+
if (The_mission.ai_profile->flags[AI::Profile_Flags::ETS_energy_same_regardless_of_system_presence])
233+
{
234+
int properties = ets_properties(objp);
235+
if (properties == (HAS_WEAPONS | HAS_SHIELDS | HAS_ENGINES))
236+
initial_engine_recharge_energy_level = Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX];
237+
else if (properties == HAS_WEAPONS || properties == HAS_SHIELDS || properties == HAS_ENGINES)
238+
initial_engine_recharge_energy_level = Energy_levels[ALL_INDEX];
239+
else
240+
initial_engine_recharge_energy_level = Energy_levels[ONE_HALF_INDEX];
241+
}
242+
else
243+
initial_engine_recharge_energy_level = Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX];
244+
181245
// check for a shortcuts first before doing linear interpolation
182-
if ( engine_energy == Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX] ){
246+
if ( engine_energy == initial_engine_recharge_energy_level ){
183247
return sip->max_speed;
184248
} else if ( engine_energy == 0.0f ){
185249
return 0.5f * sip->max_speed;
@@ -188,11 +252,11 @@ float ets_get_max_speed(object* objp, float engine_energy)
188252
} else {
189253
// do a linear interpolation to find the current max speed, using points (0,1/2 default_max_speed) (.333,default_max_speed)
190254
// x = x1 + (y-y1) * (x2-x1) / (y2-y1);
191-
if ( engine_energy < Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX] ){
192-
return 0.5f*sip->max_speed + (engine_energy * (0.5f*sip->max_speed) ) / Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX];
255+
if ( engine_energy < initial_engine_recharge_energy_level ){
256+
return 0.5f*sip->max_speed + (engine_energy * (0.5f*sip->max_speed) ) / initial_engine_recharge_energy_level;
193257
} else {
194258
// do a linear interpolation to find the current max speed, using points (.333,default_max_speed) (1,max_overclock_speed)
195-
return sip->max_speed + (engine_energy - Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX]) * (sip->max_overclocked_speed - sip->max_speed) / (1.0f - Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX]);
259+
return sip->max_speed + (engine_energy - initial_engine_recharge_energy_level) * (sip->max_overclocked_speed - sip->max_speed) / (1.0f - initial_engine_recharge_energy_level);
196260
}
197261
}
198262
}
@@ -203,6 +267,7 @@ void ets_update_max_speed(object* ship_objp)
203267
Assertion(ship_objp->type == OBJ_SHIP, "Object needs to be a ship object!");
204268

205269
// calculate the top speed of the ship based on the energy flow to engines
270+
// (note: this doesn't need the power factor; see comments in ets_get_max_speed())
206271
float x = Energy_levels[Ships[ship_objp->instance].engine_recharge_index];
207272
ship_objp->phys_info.max_vel.xyz.z = ets_get_max_speed(ship_objp, x);
208273
}
@@ -335,23 +400,13 @@ void set_recharge_rates(object* obj, int shields, int weapons, int engines) {
335400
// engines to their default levels
336401
void set_default_recharge_rates(object* obj)
337402
{
338-
int ship_properties;
339-
340403
ship* ship_p = &Ships[obj->instance];
341404
ship_info* ship_info_p = &Ship_info[ship_p->ship_info_index];
342405

343406
if ( ship_info_p->power_output == 0 )
344407
return;
345408

346-
ship_properties = 0;
347-
if (ship_has_energy_weapons(ship_p))
348-
ship_properties |= HAS_WEAPONS;
349-
350-
if (!(obj->flags[Object::Object_Flags::No_shields]) && !ship_info_p->flags[Ship::Info_Flags::Intrinsic_no_shields])
351-
ship_properties |= HAS_SHIELDS;
352-
353-
if (ship_has_engine_power(ship_p))
354-
ship_properties |= HAS_ENGINES;
409+
int ship_properties = ets_properties(obj);
355410

356411
// the default charge rate depends on what systems are on each ship
357412
switch ( ship_properties ) {

code/hud/hudets.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ enum SYSTEM_TYPE {WEAPONS, SHIELDS, ENGINES};
4242

4343
void update_ets(object* obj, float fl_frametime);
4444
void ets_init_ship(object* obj);
45+
int ets_properties(object* objp);
46+
float ets_power_factor(object* objp, bool include_power_output = true);
4547
void ai_manage_ets(object* obj);
4648

4749
void increase_recharge_rate(object* obj, SYSTEM_TYPE enum_value);

code/model/animation/modelanimation_driver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ namespace animation {
111111
int objnum = get_pmi_objnum(pmi);
112112
Assertion(objnum >= 0, "Invalid object used in animation property driver!");
113113
Assertion(Objects[objnum].type == OBJ_SHIP, "Non-ship object used in ship animation property driver!");
114-
return Energy_levels[Ships[Objects[objnum].instance].*ets_property];
114+
return ets_power_factor(&Objects[objnum], false) * Energy_levels[Ships[Objects[objnum].instance].*ets_property];
115115
}
116116

117117
std::function<float(polymodel_instance*)> parse_ship_property_driver_source() {

code/parse/sexp.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20507,7 +20507,7 @@ int sexp_gse_recharge_pct(int node, int op_num)
2050720507
return SEXP_NAN_FOREVER;
2050820508

2050920509
// recharge pct
20510-
return (int)(100.0f * Energy_levels[index]);
20510+
return (int)(100.0f * ets_power_factor(ship_entry->objp(), false) * Energy_levels[index]);
2051120511
}
2051220512

2051320513
/*

code/ship/afterburner.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ void afterburners_update(object *objp, float fl_frametime)
235235

236236
if ( shipp->afterburner_fuel < sip->afterburner_fuel_capacity ) {
237237
float recharge_scale;
238-
recharge_scale = Energy_levels[shipp->engine_recharge_index] * 2.0f * The_mission.ai_profile->afterburner_recharge_scale[Game_skill_level];
238+
recharge_scale = ets_power_factor(objp) * Energy_levels[shipp->engine_recharge_index] * 2.0f * The_mission.ai_profile->afterburner_recharge_scale[Game_skill_level];
239239
shipp->afterburner_fuel += (sip->afterburner_recover_rate * fl_frametime * recharge_scale);
240240

241241
if ( shipp->afterburner_fuel > sip->afterburner_fuel_capacity){

0 commit comments

Comments
 (0)