From 4ab313e1b62729c13f2695ecd7ca00a2184ea116 Mon Sep 17 00:00:00 2001 From: Zacam Date: Mon, 14 Feb 2011 17:03:01 +0000 Subject: [PATCH] Trunk Sync - r7014 --- code/ai/aicode.cpp | 4 +- code/ai/aigoals.cpp | 2 +- code/hud/hud.cpp | 2 +- code/hud/hudsquadmsg.cpp | 4 +- code/io/keycontrol.cpp | 4 +- code/mission/missionparse.cpp | 8 +- code/missionui/redalert.cpp | 4 +- code/model/model.h | 1 + code/network/multi_obj.cpp | 4 +- code/network/multimsgs.cpp | 4 +- code/object/object.cpp | 78 ++--- code/parse/lua.cpp | 564 +++++++++++++++++++++++++++++++++- code/ship/ship.cpp | 83 ++--- code/ship/ship.h | 9 +- code/ship/shiphit.cpp | 34 +- 15 files changed, 691 insertions(+), 114 deletions(-) diff --git a/code/ai/aicode.cpp b/code/ai/aicode.cpp index 0c802e68a1..7a2f6904fc 100644 --- a/code/ai/aicode.cpp +++ b/code/ai/aicode.cpp @@ -12011,7 +12011,7 @@ void ai_maybe_launch_cmeasure(object *objp, ai_info *aip) // For ships on player's team, have constant, average chance to fire. // For enemies, increasing chance with higher skill level. if (shipp->team == Player_ship->team) - fire_chance = Ai_classes[aip->ai_class].ai_cmeasure_fire_chance[NUM_SKILL_LEVELS/2]; + fire_chance = The_mission.ai_profile->cmeasure_fire_chance[NUM_SKILL_LEVELS/2]; else fire_chance = aip->ai_cmeasure_fire_chance; @@ -12229,7 +12229,7 @@ void ai_manage_shield(object *objp, ai_info *aip) } else { - delay = Ai_classes[aip->ai_class].ai_shield_manage_delay[NUM_SKILL_LEVELS/2]; + delay = The_mission.ai_profile->shield_manage_delay[NUM_SKILL_LEVELS/2]; } // Scale between 1x and 3x based on ai_class (SUSHI: only if autoscale is on) diff --git a/code/ai/aigoals.cpp b/code/ai/aigoals.cpp index 089f2d3783..f828a94012 100644 --- a/code/ai/aigoals.cpp +++ b/code/ai/aigoals.cpp @@ -1605,7 +1605,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp ) if ((status == SHIP_STATUS_ARRIVED) && (aigp->ai_mode == AI_GOAL_DISARM_SHIP)) { // if the ship has no turrets, we can't disarm it! - if (Ships[ship_name_lookup(aigp->ship_name)].subsys_info[SUBSYSTEM_TURRET].num == 0) + if (Ships[ship_name_lookup(aigp->ship_name)].subsys_info[SUBSYSTEM_TURRET].type_count == 0) return AI_GOAL_NOT_ACHIEVABLE; } diff --git a/code/hud/hud.cpp b/code/hud/hud.cpp index c83812d47b..d93e193c8a 100644 --- a/code/hud/hud.cpp +++ b/code/hud/hud.cpp @@ -2812,7 +2812,7 @@ void HudGaugeSupport::render(float frametime) bool repairing = false; for (i = 0; i < SUBSYSTEM_MAX; i++) { - if (Player_ship->subsys_info[i].num > 0) + if (Player_ship->subsys_info[i].type_count > 0) { if (ship_get_subsystem_strength(Player_ship, i) < 1.0f) { diff --git a/code/hud/hudsquadmsg.cpp b/code/hud/hudsquadmsg.cpp index f964a80625..4464d5214a 100644 --- a/code/hud/hudsquadmsg.cpp +++ b/code/hud/hudsquadmsg.cpp @@ -871,8 +871,8 @@ int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip ) return 0; } - // same as above except for disabled. - if ( (Comm_orders[order].item == DISARM_TARGET_ITEM) && ((shipp->subsys_info[SUBSYSTEM_TURRET].num == 0) || (shipp->subsys_info[SUBSYSTEM_TURRET].current_hits == 0.0f)) ){ + // same as above except for disarmed. + if ( (Comm_orders[order].item == DISARM_TARGET_ITEM) && ((shipp->subsys_info[SUBSYSTEM_TURRET].type_count == 0) || (shipp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f)) ){ return 0; } diff --git a/code/io/keycontrol.cpp b/code/io/keycontrol.cpp index 73dc222c64..fd22fbb998 100644 --- a/code/io/keycontrol.cpp +++ b/code/io/keycontrol.cpp @@ -856,12 +856,12 @@ void process_debug_keys(int k) do_subobj_hit_stuff(objp, Player_obj, &g_subobj_pos, (float) -Player_ai->targeted_subsys->system_info->type, NULL); //100.0f); - if ( sp->subsys_info[SUBSYSTEM_ENGINE].current_hits <= 0.0f ) { + if ( sp->subsys_info[SUBSYSTEM_ENGINE].aggregate_current_hits <= 0.0f ) { mission_log_add_entry(LOG_SHIP_DISABLED, sp->ship_name, NULL ); sp->flags |= SF_DISABLED; // add the disabled flag } - if ( sp->subsys_info[SUBSYSTEM_TURRET].current_hits <= 0.0f ) { + if ( sp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f ) { mission_log_add_entry(LOG_SHIP_DISARMED, sp->ship_name, NULL ); // sp->flags |= SF_DISARMED; // add the disarmed flag } diff --git a/code/mission/missionparse.cpp b/code/mission/missionparse.cpp index 5a314586dd..e8531fcc7f 100644 --- a/code/mission/missionparse.cpp +++ b/code/mission/missionparse.cpp @@ -2159,9 +2159,11 @@ int parse_create_object_sub(p_object *p_objp) { ptr->max_hits = ptr->system_info->max_subsys_strength * (shipp->ship_max_hull_strength / sip->max_hull_strength); - float new_hits; - new_hits = ptr->max_hits * (100.0f - sssp->percent) / 100.f; - shipp->subsys_info[ptr->system_info->type].current_hits -= (ptr->max_hits - new_hits); + float new_hits = ptr->max_hits * (100.0f - sssp->percent) / 100.f; + if (!(ptr->flags & SSF_NO_AGGREGATE)) { + shipp->subsys_info[ptr->system_info->type].aggregate_current_hits -= (ptr->max_hits - new_hits); + } + if ((100.0f - sssp->percent) < 0.5) { ptr->current_hits = 0.0f; diff --git a/code/missionui/redalert.cpp b/code/missionui/redalert.cpp index ac1ad60940..f9274dbbbd 100644 --- a/code/missionui/redalert.cpp +++ b/code/missionui/redalert.cpp @@ -548,7 +548,7 @@ void red_alert_bash_subsys_status(red_alert_ship_status *ras, ship *shipp) list_size = (int)ras->subsys_aggregate_current_hits.size(); CLAMP(list_size, 0, SUBSYSTEM_MAX); for (i = 0; i < list_size; i++) { - shipp->subsys_info[i].current_hits = ras->subsys_aggregate_current_hits[i]; + shipp->subsys_info[i].aggregate_current_hits = ras->subsys_aggregate_current_hits[i]; } } @@ -570,7 +570,7 @@ void red_alert_store_subsys_status(red_alert_ship_status *ras, ship *shipp) } for (i = 0; i < SUBSYSTEM_MAX; i++) - ras->subsys_aggregate_current_hits.push_back( shipp->subsys_info[i].current_hits ); + ras->subsys_aggregate_current_hits[i] = shipp->subsys_info[i].aggregate_current_hits; } diff --git a/code/model/model.h b/code/model/model.h index b235ce215f..2b6d373aed 100644 --- a/code/model/model.h +++ b/code/model/model.h @@ -130,6 +130,7 @@ typedef struct polymodel_instance { #define MSS_FLAG_ALLOW_VANISHING (1 << 27) // allows subsystem to vanish (prevents explosions & sounds effects from being played) #define MSS_FLAG_DAMAGE_AS_HULL (1 << 28) // applies armor damage to subsystem instead of subsystem damage - FUBAR #define MSS_FLAG_TURRET_LOCKED (1 << 29) // Turret starts locked by default - Sushi +#define MSS_FLAG_NO_AGGREGATE (1 << 30) // Don't include with aggregate subsystem types - Goober5000 // definition of stepped rotation struct typedef struct stepped_rotation { diff --git a/code/network/multi_obj.cpp b/code/network/multi_obj.cpp index 307c8c15f7..846292fa1f 100644 --- a/code/network/multi_obj.cpp +++ b/code/network/multi_obj.cpp @@ -912,7 +912,9 @@ int multi_oo_unpack_data(net_player *pl, ubyte *data) // add the value just generated (it was zero'ed above) into the array of generic system types subsys_type = subsysp->system_info->type; // this is the generic type of subsystem Assert ( subsys_type < SUBSYSTEM_MAX ); - shipp->subsys_info[subsys_type].current_hits += val; + if (!(subsysp->flags & SSF_NO_AGGREGATE)) { + shipp->subsys_info[subsys_type].aggregate_current_hits += val; + } subsys_count++; // if we've reached max subsystems for some reason, bail out diff --git a/code/network/multimsgs.cpp b/code/network/multimsgs.cpp index 0771bf3459..af8a567f73 100644 --- a/code/network/multimsgs.cpp +++ b/code/network/multimsgs.cpp @@ -7145,7 +7145,9 @@ void process_client_update_packet(ubyte *data, header *hinfo) // add the value just generated (it was zero'ed above) into the array of generic system types subsys_type = subsysp->system_info->type; // this is the generic type of subsystem Assert ( subsys_type < SUBSYSTEM_MAX ); - shipp->subsys_info[subsys_type].current_hits += fl_val; + if (!(subsysp->flags & SSF_NO_AGGREGATE)) { + shipp->subsys_info[subsys_type].aggregate_current_hits += fl_val; + } n_subsystems++; } } diff --git a/code/object/object.cpp b/code/object/object.cpp index 44b407e736..fcf962a601 100644 --- a/code/object/object.cpp +++ b/code/object/object.cpp @@ -1368,57 +1368,61 @@ void obj_move_all(float frametime) MONITOR_INC( NumObjects, Num_objects ); - objp = GET_FIRST(&obj_used_list); - while( objp !=END_OF_LIST(&obj_used_list) ) { + for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) { // skip objects which should be dead - if ( !(objp->flags&OF_SHOULD_BE_DEAD) ) { - vec3d cur_pos = objp->pos; // Save the current position + if (objp->flags & OF_SHOULD_BE_DEAD) { + continue; + } - // if this is an observer object, skip it - if(objp->type == OBJ_OBSERVER){ - objp = GET_NEXT(objp); - continue; - } + // if this is an observer object, skip it + if (objp->type == OBJ_OBSERVER) { + continue; + } + + vec3d cur_pos = objp->pos; // Save the current position #ifdef OBJECT_CHECK - // if(! ((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)) ){ - obj_check_object( objp ); - // } + // if(! ((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)) ){ + obj_check_object( objp ); + // } #endif - // pre-move - obj_move_all_pre(objp, frametime); + // pre-move + obj_move_all_pre(objp, frametime); - // store last pos and orient - objp->last_pos = cur_pos; - objp->last_orient = objp->orient; + // store last pos and orient + objp->last_pos = cur_pos; + objp->last_orient = objp->orient; + // Goober5000 - skip objects which don't move, but only until they're destroyed + if (!(objp->flags & OF_IMMOBILE && objp->hull_strength > 0.0f)) { // if this is an object which should be interpolated in multiplayer, do so - if(multi_oo_is_interp_object(objp)){ + if (multi_oo_is_interp_object(objp)) { multi_oo_interp(objp); } else { // physics obj_move_call_physics(objp, frametime); } - - // 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); + + // 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); + } } // After all objects have been moved, move all docked objects. @@ -1451,7 +1455,7 @@ void obj_move_all(float frametime) // do pre-collision stuff for beam weapons beam_move_all_pre(); - if ( Collisions_enabled ) { + if ( Collisions_enabled ) { obj_check_all_collisions(); } diff --git a/code/parse/lua.cpp b/code/parse/lua.cpp index 114bec308d..e3f5384679 100644 --- a/code/parse/lua.cpp +++ b/code/parse/lua.cpp @@ -1796,6 +1796,79 @@ ade_obj l_Model("model", "3D Model (POF) handle"); ade_obj l_ModelTextures("modeltextures_h", "Array of materials"); +// Thrusters: +class thrusters_h : public model_h +{ + public: + thrusters_h(polymodel *pm) : model_h(pm){} + thrusters_h() : model_h(){} +}; + +ade_obj l_Thrusters("thrusters", "The thrusters of a model"); + +// Thrusterbank: +struct thrusterbank_h +{ + thruster_bank *bank; + + thrusterbank_h() + { + bank = NULL; + } + + thrusterbank_h(thruster_bank* ba) + { + bank = ba; + } + + thruster_bank *Get() + { + if (!isValid()) + return NULL; + + return bank; + } + + bool isValid() + { + return this != NULL && bank != NULL; + } +}; + +ade_obj l_Thrusterbank("thrusterbank", "A model thrusterbank"); + +// Glowpoint: +struct glowpoint_h +{ + glow_point *point; + + glowpoint_h() + { + } + + glowpoint_h(glow_point* np) + { + point = np; + } + + glow_point* Get() + { + if (!isValid()) + return NULL; + + return point; + } + + bool isValid() + { + return (this != NULL && point != NULL); + } + +}; + +ade_obj l_Glowpoint("glowpoint", "A model glowpoint"); + + ADE_VIRTVAR(Textures, l_Model, "modeltextures", "Model textures", "modeltextures", "Model textures, or an invalid modeltextures handle if the model handle is invalid") { model_h *mdl = NULL; @@ -1815,6 +1888,24 @@ ADE_VIRTVAR(Textures, l_Model, "modeltextures", "Model textures", "modeltextures return ade_set_args(L, "o", l_ModelTextures.Set(modeltextures_h(pm))); } +ADE_VIRTVAR(Thrusters, l_Model, "thrusters", "Model thrusters", "thrusters", "Thrusters of the model or invalid handle") +{ + model_h *mdl = NULL; + thrusters_h *oth = NULL; + if(!ade_get_args(L, "o|o", l_Model.GetPtr(&mdl), l_Thrusters.GetPtr(&oth))) + return ade_set_error(L, "o", l_Thrusters.Set(thrusters_h())); + + polymodel *pm = mdl->Get(); + if(pm == NULL) + return ade_set_error(L, "o", l_Thrusters.Set(thrusters_h())); + + if(ADE_SETTING_VAR && oth->IsValid()) { + LuaError(L, "Attempt to use Incomplete Feature: Thrusters copy"); + } + + return ade_set_args(L, "o", l_Thrusters.Set(thrusters_h(pm))); +} + extern void model_calc_bound_box( vec3d *box, vec3d *big_mn, vec3d *big_mx); ADE_VIRTVAR(BoundingBoxMax, l_Model, "vector", "Model bounding box maximum", "vector", "Model bounding box, or an empty vector if the handle is invalid") @@ -1947,6 +2038,179 @@ ADE_FUNC(isValid, l_Model, NULL, "True if valid, false or nil if not", "boolean" return mdl->IsValid(); } +//**********HANDLE: thrusters +ADE_FUNC(__len, l_Thrusters, NULL, "Number of thruster banks on the model", "number", "Number of thrusterbanks") +{ + thrusters_h *trh; + if(!ade_get_args(L, "o", l_Thrusters.GetPtr(&trh))) + return ade_set_error(L, "i", -1); + + if(!trh->IsValid()) + return ade_set_error(L, "i", -1); + + polymodel *pm = trh->Get(); + + if(pm == NULL) + return ade_set_error(L, "i", -1); + + return ade_set_args(L, "i", pm->n_thrusters); +} + +ADE_INDEXER(l_Thrusters, "number Index", "Array of all thrusterbanks on this thruster", "thrusterbank", "Handle to the thrusterbank or invalid handle if index is invalid") +{ + thrusters_h *trh = NULL; + char *s = NULL; + thrusterbank_h newThr = NULL; + + if (!ade_get_args(L, "os|o", l_Thrusters.GetPtr(&trh), &s, l_Thrusterbank.Get(&newThr))) + return ade_set_error(L, "o", l_Thrusterbank.Set(thrusterbank_h())); + + polymodel *pm = trh->Get(); + + if (!trh->IsValid() || s == NULL || pm == NULL) + return ade_set_error(L, "o", l_Thrusterbank.Set(thrusterbank_h())); + + //Determine index + int idx = atoi(s) - 1; //Lua->FS2 + + if (idx < 0 || idx >= pm->n_thrusters) + return ade_set_error(L, "o", l_Thrusterbank.Set(thrusterbank_h())); + + thruster_bank* bank = &pm->thrusters[idx]; + + if (ADE_SETTING_VAR && trh != NULL) + { + if (newThr.isValid()) + { + pm->thrusters[idx] = *(newThr.Get()); + } + } + + return ade_set_args(L, "o", l_Thrusterbank.Set(bank)); +} + +ADE_FUNC(isValid, l_Thrusters, NULL, "Detects whether handle is valid", "boolean", "true if valid, false if handle is invalid, nil if a syntax/type error occurs") +{ + thrusters_h *trh; + if(!ade_get_args(L, "o", l_Thrusters.GetPtr(&trh))) + return ADE_RETURN_NIL; + + return ade_set_args(L, "b", trh->IsValid()); +} + +//**********HANDLE: thrusterbank +ADE_FUNC(__len, l_Thrusterbank, NULL, "Number of thrusters on this thrusterbank", "number", "Number of thrusters on this bank or 0 if handle is invalid") +{ + thrusterbank_h *tbh = NULL; + if(!ade_get_args(L, "o", l_Thrusterbank.GetPtr(&tbh))) + return ade_set_error(L, "i", -1); + + if(!tbh->isValid()) + return ade_set_error(L, "i", -1); + + thruster_bank* bank = tbh->Get(); + + return ade_set_args(L, "i", bank->num_points); +} + +ADE_INDEXER(l_Thrusterbank, "number Index", "Array of glowpoint", "glowpoint", "Glowpoint, or invalid glowpoint handle on failure") +{ + thrusterbank_h *tbh = NULL; + char *s = NULL; + glowpoint_h *glh = NULL; + + if (!ade_get_args(L, "os|o", l_Thrusterbank.GetPtr(&tbh), &s, l_Glowpoint.GetPtr(&glh))) + return ade_set_error(L, "o", l_Glowpoint.Set(glowpoint_h())); + + if (!tbh->isValid() || s==NULL) + return ade_set_error(L, "o", l_Glowpoint.Set(glowpoint_h())); + + thruster_bank* bank = tbh->Get(); + + //Determine index + int idx = atoi(s) - 1; //Lua->FS2 + + if (idx < 0 || idx >= bank->num_points) + return ade_set_error(L, "o", l_Glowpoint.Set(glowpoint_h())); + + glow_point* glp = &bank->points[idx]; + + if (ADE_SETTING_VAR && glh != NULL) + { + if (glh->isValid()) + { + bank->points[idx] = *(glh->Get()); + } + } + + return ade_set_args(L, "o", l_Glowpoint.Set(glp)); +} + +ADE_FUNC(isValid, l_Thrusterbank, NULL, "Detectes if this handle is valid", "boolean", "true if this handle is valid, false otherwise") +{ + thrusterbank_h* trh; + if(!ade_get_args(L, "o", l_Thrusterbank.GetPtr(&trh))) + return ADE_RETURN_FALSE; + + if (!trh->isValid()) + return ADE_RETURN_FALSE; + + return ade_set_args(L, "b", trh->isValid()); +} + +//**********HANDLE: glowpoint +ADE_VIRTVAR(Position, l_Glowpoint, NULL, "The (local) vector to the position of the glowpoint", "vector", "The local vector to the glowpoint or nil invalid") +{ + glowpoint_h *glh = NULL; + vec3d newVec; + + if(!ade_get_args(L, "o|o", l_Glowpoint.GetPtr(&glh), l_Vector.Get(&newVec))) + return ADE_RETURN_NIL; + + if (!glh->isValid()) + return ADE_RETURN_NIL; + + vec3d vec = glh->point->pnt; + + if (ADE_SETTING_VAR) + { + glh->point->pnt = newVec; + } + + return ade_set_args(L, "o", l_Vector.Set(vec)); +} + +ADE_VIRTVAR(Radius, l_Glowpoint, NULL, "The radius of the glowpoint", "number", "The radius of the glowpoint or -1 of invalid") +{ + glowpoint_h* glh = NULL; + float newVal; + + if(!ade_get_args(L, "o|f", l_Glowpoint.GetPtr(&glh), &newVal)) + return ade_set_error(L, "f", -1.0f); + + if (!glh->isValid()) + return ade_set_error(L, "f", -1.0f); + + float radius = glh->point->radius; + + if (ADE_SETTING_VAR) + { + glh->point->radius = newVal; + } + + return ade_set_args(L, "f", radius); +} + +ADE_FUNC(isValid, l_Glowpoint, NULL, "Returns wether this handle is valid or not", "boolean", "True if handle is valid, false otherwise") +{ + glowpoint_h glh = NULL; + + if(!ade_get_args(L, "o", l_Glowpoint.Get(&glh))) + return ADE_RETURN_FALSE; + + return ade_set_args(L, "b", glh.isValid()); +} + //**********HANDLE: order struct order_h { @@ -3609,6 +3873,27 @@ ADE_VIRTVAR(TechDescription, l_Weaponclass, "string", "Weapon class tech descrip return ade_set_args(L, "s", ""); } +ADE_VIRTVAR(Model, l_Weaponclass, "model", "Model", "model", "Weapon class model, or invalid model handle if weaponclass handle is invalid") +{ + int weapon_info_idx=-1; + model_h *mdl = NULL; + if(!ade_get_args(L, "o|o", l_Weaponclass.Get(&weapon_info_idx), l_Model.GetPtr(&mdl))) + return ade_set_error(L, "o", l_Model.Set(-1)); + + if(weapon_info_idx < 0 || weapon_info_idx > Num_weapon_types) + return ade_set_error(L, "o", l_Model.Set(-1)); + + weapon_info *wip = &Weapon_info[weapon_info_idx]; + + int mid = mdl->GetID(); + + if(ADE_SETTING_VAR && mid > -1) { + wip->model_num = mid; + } + + return ade_set_args(L, "o", l_Model.Set(model_h(wip->model_num))); +} + ADE_VIRTVAR(ArmorFactor, l_Weaponclass, "number", "Amount of weapon damage applied to ship hull (0-1.0)", "number", "Armor factor, or empty string if handle is invalid") { int idx; @@ -3660,6 +3945,23 @@ ADE_VIRTVAR(FireWait, l_Weaponclass, "number", "Weapon fire wait (cooldown time) return ade_set_args(L, "f", Weapon_info[idx].fire_wait); } +ADE_VIRTVAR(FreeFlightTime, l_Weaponclass, "number", "The time the weapon will fly before turing onto its target", "number", "Free flight time or emty string if invalid") +{ + int idx; + float f = 0.0f; + if(!ade_get_args(L, "o|f", l_Weaponclass.Get(&idx), &f)) + return ade_set_error(L, "f", 0.0f); + + if(idx < 0 || idx > Num_weapon_types) + return ade_set_error(L, "f", 0.0f); + + if(ADE_SETTING_VAR) { + Weapon_info[idx].free_flight_time = f; + } + + return ade_set_args(L, "f", Weapon_info[idx].free_flight_time); +} + ADE_VIRTVAR(LifeMax, l_Weaponclass, "number", "Life of weapon in seconds", "number", "Life of weapon, or 0 if handle is invalid") { int idx; @@ -3779,6 +4081,37 @@ ADE_VIRTVAR(Speed, l_Weaponclass, "number", "Weapon max speed, aka $Velocity in return ade_set_args(L, "f", Weapon_info[idx].max_speed); } +ADE_VIRTVAR(Bomb, l_Weaponclass, "boolean", "Is weapon clas flagged as bomb", "boolean", "New flag") +{ + int idx; + bool newVal = false; + if(!ade_get_args(L, "o|b", l_Weaponclass.Get(&idx), &newVal)) + return ADE_RETURN_FALSE; + + if(idx < 0 || idx > Num_weapon_types) + return ADE_RETURN_FALSE; + + weapon_info *info = &Weapon_info[idx]; + + if(ADE_SETTING_VAR) + { + if(newVal) + { + info->wi_flags |= WIF_BOMB; + } + else + { + info->wi_flags &= ~WIF_BOMB; + } + } + + + if (info->wi_flags & WIF_BOMB) + return ADE_RETURN_TRUE; + else + return ADE_RETURN_FALSE; +} + 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; @@ -3803,6 +4136,51 @@ ADE_FUNC(getWeaponClassIndex, l_Weaponclass, NULL, "Gets the index valus of the return ade_set_args(L, "i", idx); } +ADE_FUNC(isLaser, l_Weaponclass, NULL, "Return true if the weapon is a laser (this includes balistic primaries)", "boolean", "true if the weapon is a laser, false otherwise") +{ + int idx; + if(!ade_get_args(L, "o", l_Weaponclass.Get(&idx))) + return ADE_RETURN_NIL; + + if(idx < 0 || idx >= Num_weapon_types) + return ADE_RETURN_FALSE; + + if (Weapon_info[idx].subtype == WP_LASER) + return ADE_RETURN_TRUE; + else + return ADE_RETURN_FALSE; +} + +ADE_FUNC(isMissile, l_Weaponclass, NULL, "Return true if the weapon is a missile", "boolean", "true if the weapon is a missile, false otherwise") +{ + int idx; + if(!ade_get_args(L, "o", l_Weaponclass.Get(&idx))) + return ADE_RETURN_NIL; + + if(idx < 0 || idx >= Num_weapon_types) + return ADE_RETURN_FALSE; + + if (Weapon_info[idx].subtype == WP_MISSILE) + return ADE_RETURN_TRUE; + else + return ADE_RETURN_FALSE; +} + +ADE_FUNC(isBeam, l_Weaponclass, NULL, "Return true if the weapon is a beam", "boolean", "true if the weapon is a beam, false otherwise") +{ + int idx; + if(!ade_get_args(L, "o", l_Weaponclass.Get(&idx))) + return ADE_RETURN_NIL; + + if(idx < 0 || idx >= Num_weapon_types) + return ADE_RETURN_FALSE; + + if (Weapon_info[idx].subtype == WP_BEAM) + return ADE_RETURN_TRUE; + else + return ADE_RETURN_FALSE; +} + //**********HANDLE: Eyepoint struct eye_h { @@ -5688,6 +6066,74 @@ ADE_VIRTVAR(Target, l_Subsystem, "object", "Object targetted by this subsystem. return ade_set_object_with_breed(L, ss->turret_enemy_objnum); } +ADE_VIRTVAR(TurretResets, l_Subsystem, "boolean", "Specifies wether this turrets resets after a certain time of inactivity", "boolean", "true if turret resets, false otherwise") +{ + ship_subsys_h *sso; + bool newVal = false; + if (!ade_get_args(L, "o|b", l_Subsystem.GetPtr(&sso), &newVal)) + return ADE_RETURN_FALSE; + + if (!sso->IsValid()) + return ADE_RETURN_FALSE; + + if(ADE_SETTING_VAR) + { + if(newVal) + { + sso->ss->system_info->flags |= MSS_FLAG_TURRET_RESET_IDLE; + } + else + { + sso->ss->system_info->flags &= ~MSS_FLAG_TURRET_RESET_IDLE; + } + } + + if (sso->ss->system_info->flags & MSS_FLAG_TURRET_RESET_IDLE) + return ADE_RETURN_TRUE; + else + return ADE_RETURN_FALSE; +} + +ADE_VIRTVAR(TurretResetDelay, l_Subsystem, "number", "The time (in milliseconds) after that the turret resets itself", "number", "Reset delay") +{ + ship_subsys_h *sso; + int newVal = -1; + if (!ade_get_args(L, "o|i", l_Subsystem.GetPtr(&sso), &newVal)) + return ade_set_error(L, "i", -1); + + if (!sso->IsValid()) + return ade_set_error(L, "i", -1); + + if (!(sso->ss->system_info->flags & MSS_FLAG_TURRET_RESET_IDLE)) + return ade_set_error(L, "i", -1); + + if(ADE_SETTING_VAR) + { + if ((sso->ss->system_info->flags & MSS_FLAG_TURRET_RESET_IDLE)) + sso->ss->system_info->turret_reset_delay = newVal; + } + + return ade_set_args(L, "i", sso->ss->system_info->turret_reset_delay); +} + +ADE_VIRTVAR(TurnRate, l_Subsystem, "number", "The turn rate", "number", "Turnrate or -1 on error") +{ + ship_subsys_h *sso; + float newVal = -1.0f; + if (!ade_get_args(L, "o|i", l_Subsystem.GetPtr(&sso), &newVal)) + return ade_set_error(L, "i", -1.0f); + + if (!sso->IsValid()) + return ade_set_error(L, "i", -1.0f); + + if(ADE_SETTING_VAR) + { + sso->ss->system_info->turret_turning_rate = newVal; + } + + return ade_set_args(L, "i", sso->ss->system_info->turret_turning_rate); +} + ADE_FUNC(targetingOverride, l_Subsystem, "boolean", "If set to true, AI targeting for this turret is switched off. If set to false, the AI will take over again.", "boolean", "Returns true if successful, false otherwise") { bool targetOverride = false; @@ -5769,6 +6215,65 @@ ADE_FUNC(fireWeapon, l_Subsystem, "[Turret weapon index = 1, Flak range = 100]", return ade_set_args(L, "b", rtn); } +ADE_FUNC(rotateTurret, l_Subsystem, "vector Pos[, boolean reset=false", "Rotates the turret to face Pos or resets the turret to its original state", "number", "Something") +{ + ship_subsys_h *sso; + vec3d pos = vmd_zero_vector; + bool reset = false; + if (!ade_get_args(L, "oo|o", l_Subsystem.GetPtr(&sso), l_Vector.Get(&pos), &reset)) + return ADE_RETURN_NIL; + + //Get default turret info + vec3d gpos, gvec; + model_subsystem *tp = sso->ss->system_info; + //ship_get_global_turret_info(sso->objp, sso->ss->system_info, &gpos, &gvec); + + //Rotate turret position with ship + vm_vec_unrotate(&gpos, &tp->pnt, &sso->objp->orient); + //Add turret position to appropriate world space + vm_vec_add2(&gpos, &sso->objp->pos); + + //Rotate turret heading with turret base and gun + //Now rotate a matrix by angles + vec3d turret_heading = vmd_zero_vector; + matrix m = IDENTITY_MATRIX; + vm_rotate_matrix_by_angles(&m, &sso->ss->submodel_info_1.angs); + vm_rotate_matrix_by_angles(&m, &sso->ss->submodel_info_2.angs); + vm_vec_unrotate(&turret_heading, &tp->turret_norm, &m); + + //Rotate into world space + vm_vec_unrotate(&gvec, &turret_heading, &sso->objp->orient); + + int ret_val = model_rotate_gun(Ship_info[(&Ships[sso->objp->instance])->ship_info_index].model_num, sso->ss->system_info, &Objects[sso->objp->instance].orient, &sso->ss->submodel_info_1.angs, &sso->ss->submodel_info_2.angs, &Objects[sso->objp->instance].pos, &pos, (&Ships[sso->objp->instance])->objnum, reset); + + return ade_set_args(L, "i", ret_val); +} + +ADE_FUNC(getTurretHeading, l_Subsystem, NULL, "Returns the turrets forward vector", "vector", "Returns a normalized version of the forward vector or null vector on error") +{ + ship_subsys_h *sso; + if(!ade_get_args(L, "o", l_Subsystem.GetPtr(&sso))) + return ade_set_error(L, "o", l_Vector.Set(vmd_zero_vector)); + + if(!sso->IsValid()) + return ade_set_error(L, "o", l_Vector.Set(vmd_zero_vector)); + + //Get default turret info + model_subsystem *tp = sso->ss->system_info; + + //Rotate turret heading with turret base and gun + //Now rotate a matrix by angles + vec3d turret_heading = vmd_zero_vector; + matrix m = IDENTITY_MATRIX; + vm_rotate_matrix_by_angles(&m, &sso->ss->submodel_info_1.angs); + vm_rotate_matrix_by_angles(&m, &sso->ss->submodel_info_2.angs); + vm_vec_unrotate(&turret_heading, &tp->turret_norm, &m); + + vm_vec_normalize(&turret_heading); + + return ade_set_args(L, "o", l_Vector.Set(turret_heading)); +} + ADE_FUNC(getFOVs, l_Subsystem, NULL, "Returns current turrets FOVs", "number, number, number", "Standard FOV, maximum barrel elevation, turret base fov.") { ship_subsys_h *sso; @@ -7474,6 +7979,40 @@ ADE_FUNC(isExitCollision, l_Beam, "number", "Checks if the defined collision was return ADE_RETURN_FALSE; } +ADE_FUNC(getStartDirectionInfo, l_Beam, NULL, "Gets the start information about the direction. The vector is a normalized vector from LastStart showing the start direction of a slashing beam", "vector", "The start direction or null vector if invalid") +{ + object_h *objh; + if(!ade_get_args(L, "o", l_Beam.GetPtr(&objh))) + return ade_set_error(L, "o", l_Vector.Set(vmd_zero_vector)); + + beam *bp = NULL; + if(objh->objp->instance > -1) + bp = &Beams[objh->objp->instance]; + else + return ade_set_error(L, "o", l_Vector.Set(vmd_zero_vector)); + + beam_info inf = bp->binfo; + + return ade_set_args(L, "o", l_Vector.Set(inf.dir_a)); +} + +ADE_FUNC(getEndDirectionInfo, l_Beam, NULL, "Gets the end information about the direction. The vector is a normalized vector from LastStart showing the end direction of a slashing beam", "vector", "The start direction or null vector if invalid") +{ + object_h *objh; + if(!ade_get_args(L, "o", l_Beam.GetPtr(&objh))) + return ade_set_error(L, "o", l_Vector.Set(vmd_zero_vector)); + + beam *bp = NULL; + if(objh->objp->instance > -1) + bp = &Beams[objh->objp->instance]; + else + return ade_set_error(L, "o", l_Vector.Set(vmd_zero_vector)); + + beam_info inf = bp->binfo; + + return ade_set_args(L, "o", l_Vector.Set(inf.dir_b)); +} + //**********HANDLE: Wing ade_obj l_Wing("wing", "Wing handle"); @@ -9089,21 +9628,26 @@ ADE_VIRTVAR(MouseControlStatus, l_Mouse, "boolean", "Gets and sets the retail mo if(!mouse_inited) return ADE_RETURN_NIL; - bool mouse_io; - bool mouse_status = false; - if(!(ade_get_args(L, "*|b", &mouse_io))) + bool newVal = false; + if (!ade_get_args(L, "*|b", &newVal)) return ADE_RETURN_NIL; - if(mouse_io) { - Use_mouse_to_fly = 1; - } else { - Use_mouse_to_fly = 0; + if (ADE_SETTING_VAR) + { + if (newVal) + { + Use_mouse_to_fly = true; + } + else + { + Use_mouse_to_fly = false; + } } if (Use_mouse_to_fly) - mouse_status = true; - - return ade_set_args(L, "b", mouse_status); + return ADE_RETURN_TRUE; + else + return ADE_RETURN_FALSE; } //trackir funcs diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index 6087b768fa..9c11e2b087 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -246,6 +246,7 @@ flag_def_list Subsystem_flags[] = { { "allow vanishing", MSS_FLAG_ALLOW_VANISHING, 0 }, { "damage as hull", MSS_FLAG_DAMAGE_AS_HULL, 0 }, { "starts locked", MSS_FLAG_TURRET_LOCKED, 0 }, + { "no aggregate", MSS_FLAG_NO_AGGREGATE, 0 }, }; int Num_subsystem_flags = sizeof(Subsystem_flags)/sizeof(flag_def_list); @@ -4827,21 +4828,23 @@ void ship_recalc_subsys_strength( ship *shipp ) // make the current strength be 1.0. If there are initial conditions on the ship, then // the mission parse code should take care of setting that. for (i = 0; i < SUBSYSTEM_MAX; i++) { - shipp->subsys_info[i].num = 0; - shipp->subsys_info[i].total_hits = 0.0f; - shipp->subsys_info[i].current_hits = 0.0f; + shipp->subsys_info[i].type_count = 0; + shipp->subsys_info[i].aggregate_max_hits = 0.0f; + shipp->subsys_info[i].aggregate_current_hits = 0.0f; } // count all of the subsystems of a particular type. For each generic type of subsystem, we store the // total count of hits. (i.e. for 3 engines, we store the sum of the max_hits for each engine) for ( ship_system = GET_FIRST(&shipp->subsys_list); ship_system != END_OF_LIST(&shipp->subsys_list); ship_system = GET_NEXT(ship_system) ) { - int type; - type = ship_system->system_info->type; - Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) ); - shipp->subsys_info[type].num++; - shipp->subsys_info[type].total_hits += ship_system->max_hits; - shipp->subsys_info[type].current_hits += ship_system->current_hits; + if (!(ship_system->flags & SSF_NO_AGGREGATE)) { + int type = ship_system->system_info->type; + Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) ); + + shipp->subsys_info[type].type_count++; + shipp->subsys_info[type].aggregate_max_hits += ship_system->max_hits; + shipp->subsys_info[type].aggregate_current_hits += ship_system->current_hits; + } //Get rid of any persistent sounds on the subsystem //This is inefficient + sloppy but there's not really an easy way to handle things @@ -4905,7 +4908,7 @@ void ship_recalc_subsys_strength( ship *shipp ) // set any ship flags which should be set. unset the flags since we might be repairing a subsystem // through sexpressions. - if ( (shipp->subsys_info[SUBSYSTEM_ENGINE].num > 0) && (shipp->subsys_info[SUBSYSTEM_ENGINE].current_hits == 0.0f) ){ + if ( (shipp->subsys_info[SUBSYSTEM_ENGINE].type_count > 0) && (shipp->subsys_info[SUBSYSTEM_ENGINE].aggregate_current_hits <= 0.0f) ) { shipp->flags |= SF_DISABLED; } else { shipp->flags &= ~SF_DISABLED; @@ -4913,7 +4916,7 @@ void ship_recalc_subsys_strength( ship *shipp ) } /* - if ( (shipp->subsys_info[SUBSYSTEM_TURRET].num > 0) && (shipp->subsys_info[SUBSYSTEM_TURRET].current_hits == 0.0f) ){ + if ( (shipp->subsys_info[SUBSYSTEM_TURRET].type_count > 0) && (shipp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f) ) { shipp->flags |= SF_DISARMED; } else { shipp->flags &= ~SF_DISARMED; @@ -5056,6 +5059,8 @@ int subsys_set(int objnum, int ignore_subsys_info) ship_system->flags |= SSF_VANISHED; if (model_system->flags & MSS_FLAG_DAMAGE_AS_HULL) ship_system->flags |= SSF_DAMAGE_AS_HULL; + if (model_system->flags & MSS_FLAG_NO_AGGREGATE) + ship_system->flags |= SSF_NO_AGGREGATE; if (model_system->flags & MSS_FLAG_ROTATES) ship_system->flags |= SSF_ROTATES; @@ -7550,24 +7555,26 @@ void ship_auto_repair_frame(int shipnum, float frametime) Assert(ssp->system_info->type >= 0 && ssp->system_info->type < SUBSYSTEM_MAX); ssip = &sp->subsys_info[ssp->system_info->type]; - if ( ssp->current_hits != ssp->max_hits ) { + if ( ssp->current_hits < ssp->max_hits ) { // only repair those subsystems which are not destroyed if ( ssp->max_hits <= 0 || ssp->current_hits <= 0 ) continue; // do incremental repair on the subsystem - ssp->current_hits += ssp->max_hits * real_repair_rate * frametime; - ssip->current_hits += ssip->total_hits * real_repair_rate * frametime; - // check for overflow of current_hits - if ( ssp->current_hits >= ssp->max_hits ) { - // TODO: here is hook for when a subsystem is fully repaired (eg add voice) + ssp->current_hits += ssp->max_hits * real_repair_rate * frametime; + if ( ssp->current_hits > ssp->max_hits ) { ssp->current_hits = ssp->max_hits; } - if ( ssip->current_hits >= ssip->total_hits ) { - ssip->current_hits = ssip->total_hits; - } + + // aggregate repair + if (!(ssp->flags & SSF_NO_AGGREGATE)) { + ssip->aggregate_current_hits += ssip->aggregate_max_hits * real_repair_rate * frametime; + if ( ssip->aggregate_current_hits > ssip->aggregate_max_hits ) { + ssip->aggregate_current_hits = ssip->aggregate_max_hits; + } + } } } // end for } @@ -11869,7 +11876,7 @@ ship_subsys *ship_get_indexed_subsys( ship *sp, int index, vec3d *attacker_pos ) int subsys_type; subsys_type = -index; - if ( sp->subsys_info[subsys_type].current_hits == 0.0f ) // if there are no hits, no subsystem to attack. + if ( sp->subsys_info[subsys_type].aggregate_current_hits <= 0.0f ) // if there are no hits, no subsystem to attack. return NULL; if ( attacker_pos != NULL ) { @@ -11962,18 +11969,20 @@ float ship_get_subsystem_strength( ship *shipp, int type ) ship_subsys *ssp; Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) ); - if ( shipp->subsys_info[type].total_hits == 0.0f ) - return 1.0f; // For a dying ship, all subsystem strengths are zero. if (Objects[shipp->objnum].hull_strength <= 0.0f) return 0.0f; + // short circuit 1 + if (shipp->subsys_info[type].aggregate_max_hits <= 0.0f) + return 1.0f; + // short circuit 0 - if (shipp->subsys_info[type].current_hits <= 0.0f) + if (shipp->subsys_info[type].aggregate_current_hits <= 0.0f) return 0.0f; - strength = shipp->subsys_info[type].current_hits / shipp->subsys_info[type].total_hits; + strength = shipp->subsys_info[type].aggregate_current_hits / shipp->subsys_info[type].aggregate_max_hits; Assert( strength != 0.0f ); if ( (type == SUBSYSTEM_ENGINE) && (strength < 1.0f) ) { @@ -11994,7 +12003,7 @@ float ship_get_subsystem_strength( ship *shipp, int type ) } ssp = GET_NEXT( ssp ); } - strength = percent / (float)shipp->subsys_info[type].num; + strength = percent / (float)shipp->subsys_info[type].type_count; } return strength; @@ -12011,14 +12020,14 @@ void ship_set_subsystem_strength( ship *shipp, int type, float strength ) ship_subsys *ssp; Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) ); - if ( shipp->subsys_info[type].total_hits == 0.0f ) + if ( shipp->subsys_info[type].aggregate_max_hits <= 0.0f ) return; total_current_hits = 0.0f; ssp = GET_FIRST(&shipp->subsys_list); while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) { - if ( ssp->system_info->type == type ) { + if ( (ssp->system_info->type == type) && !(ssp->flags & SSF_NO_AGGREGATE) ) { ssp->current_hits = strength * ssp->max_hits; total_current_hits += ssp->current_hits; } @@ -12026,10 +12035,10 @@ void ship_set_subsystem_strength( ship *shipp, int type, float strength ) } // update the objects integrity, needed since we've bashed the strength of a subsysem - diff = total_current_hits - shipp->subsys_info[type].current_hits; + diff = total_current_hits - shipp->subsys_info[type].aggregate_current_hits; Objects[shipp->objnum].hull_strength += diff; - // fix up the shipp->subsys_info[type] current_hits value - shipp->subsys_info[type].current_hits = total_current_hits; + // fix up the shipp->subsys_info[type] aggregate_current_hits value + shipp->subsys_info[type].aggregate_current_hits = total_current_hits; } #define SHIELD_REPAIR_RATE 0.20f // Percent of shield repaired per second. @@ -12290,9 +12299,11 @@ int ship_do_rearm_frame( object *objp, float frametime ) } // add repair to aggregate strength of subsystems of that type - shipp->subsys_info[subsys_type].current_hits += repair_delta; - if ( shipp->subsys_info[subsys_type].current_hits > shipp->subsys_info[subsys_type].total_hits ) - shipp->subsys_info[subsys_type].current_hits = shipp->subsys_info[subsys_type].total_hits; + if (!(ssp->flags & SSF_NO_AGGREGATE)) { + shipp->subsys_info[subsys_type].aggregate_current_hits += repair_delta; + if ( shipp->subsys_info[subsys_type].aggregate_current_hits > shipp->subsys_info[subsys_type].aggregate_max_hits ) + shipp->subsys_info[subsys_type].aggregate_current_hits = shipp->subsys_info[subsys_type].aggregate_max_hits; + } // check to see if this subsystem was totally non functional before -- if so, then // reset the flags @@ -13645,7 +13656,7 @@ ship_subsys *ship_return_next_subsys(ship *shipp, int type, vec3d *attacker_pos) Assert ( type >= 0 && type < SUBSYSTEM_MAX ); // If aggregate total is 0, that means no subsystem is alive of that type - if ( shipp->subsys_info[type].total_hits <= 0.0f ) + if ( shipp->subsys_info[type].aggregate_max_hits <= 0.0f ) return NULL; // loop through all the subsystems, if we find a match that has some strength, return it @@ -13661,7 +13672,7 @@ ship_subsys *ship_get_closest_subsys_in_sight(ship *sp, int subsys_type, vec3d * Assert ( subsys_type >= 0 && subsys_type < SUBSYSTEM_MAX ); // If aggregate total is 0, that means no subsystem is alive of that type - if ( sp->subsys_info[subsys_type].total_hits <= 0.0f ) + if ( sp->subsys_info[subsys_type].aggregate_max_hits <= 0.0f ) return NULL; ship_subsys *closest_in_sight_subsys; diff --git a/code/ship/ship.h b/code/ship/ship.h index b476c0134c..3f57af0b22 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -265,6 +265,9 @@ typedef struct cockpit_display_info { #define SSF_MISSILES_IGNORE_IF_DEAD (1 << 9) // forces homing missiles to target hull if subsystem is dead before missile hits it. #define SSF_ROTATES (1 << 10) #define SSF_DAMAGE_AS_HULL (1 << 11) // Applies armor damage instead of subsystem damge. - FUBAR +#define SSF_NO_AGGREGATE (1 << 12) // exclude this subsystem from the aggregate subsystem-info tracking - Goober5000 + + // Wanderer #define SSSF_ALIVE (1 << 0) // subsystem has active alive sound #define SSSF_DEAD (1 << 1) // subsystem has active dead sound @@ -369,9 +372,9 @@ typedef struct ship_subsys { // we might have 3 engines), and the relative strength of the subsystem. The #defines in model.h // for SUBSYSTEM_xxx will be used as indices into this array. typedef struct ship_subsys_info { - int num; // number of subsystems of type on this ship; - float total_hits; // total number of hits between all subsystems of this type. - float current_hits; // current count of hits for all subsystems of this type. + int type_count; // number of subsystems of type on this ship; + float aggregate_max_hits; // maximum number of hits for all subsystems of this type. + float aggregate_current_hits; // current count of hits for all subsystems of this type. } ship_subsys_info; diff --git a/code/ship/shiphit.cpp b/code/ship/shiphit.cpp index 81f14ba5af..781c677fe2 100644 --- a/code/ship/shiphit.cpp +++ b/code/ship/shiphit.cpp @@ -183,18 +183,20 @@ void do_subobj_destroyed_stuff( ship *ship_p, ship_subsys *subsys, vec3d* hitpos // next do a quick sanity check on the current hits that we are keeping for the generic subsystems // I think that there might be rounding problems with the floats. This code keeps us safe. - if ( ship_p->subsys_info[type].num == 1 ) { - ship_p->subsys_info[type].current_hits = 0.0f; + if ( ship_p->subsys_info[type].type_count == 1 ) { + ship_p->subsys_info[type].aggregate_current_hits = 0.0f; } else { float hits; ship_subsys *ssp; hits = 0.0f; for ( ssp=GET_FIRST(&ship_p->subsys_list); ssp != END_OF_LIST(&ship_p->subsys_list); ssp = GET_NEXT(ssp) ) { - if ( ssp->system_info->type == type ) + // type matches? + if ( (ssp->system_info->type == type) && !(ssp->flags & SSF_NO_AGGREGATE) ) { hits += ssp->current_hits; + } } - ship_p->subsys_info[type].current_hits = hits; + ship_p->subsys_info[type].aggregate_current_hits = hits; } // store an event in the event log. Also, determine if all turrets or all @@ -243,7 +245,7 @@ void do_subobj_destroyed_stuff( ship *ship_p, ship_subsys *subsys, vec3d* hitpos } if ( psub->type == SUBSYSTEM_TURRET ) { - if ( ship_p->subsys_info[type].current_hits == 0.0f ) { + if ( ship_p->subsys_info[type].aggregate_current_hits <= 0.0f ) { // Don't create "disarmed" event for small ships. if (!(Ship_info[ship_p->ship_info_index].flags & SIF_SMALL_SHIP)) { mission_log_add_entry(LOG_SHIP_DISARMED, ship_p->ship_name, NULL ); @@ -254,7 +256,7 @@ void do_subobj_destroyed_stuff( ship *ship_p, ship_subsys *subsys, vec3d* hitpos // when an engine is destroyed, we must change the max velocity of the ship // to be some fraction of its normal maximum value - if ( ship_p->subsys_info[type].current_hits == 0.0f ) { + if ( ship_p->subsys_info[type].aggregate_current_hits <= 0.0f ) { mission_log_add_entry(LOG_SHIP_DISABLED, ship_p->ship_name, NULL ); ship_p->flags |= SF_DISABLED; // add the disabled flag } @@ -513,9 +515,11 @@ float do_subobj_hit_stuff(object *ship_obj, object *other_obj, vec3d *hitpos, fl Assert(Player_ai->targeted_subsys != NULL); if ( (subsys == Player_ai->targeted_subsys) && (subsys->current_hits > 0) ) { Assert(subsys->system_info->type == (int) -damage); - ship_p->subsys_info[subsys->system_info->type].current_hits -= subsys->current_hits; - if (ship_p->subsys_info[subsys->system_info->type].current_hits < 0) { - ship_p->subsys_info[subsys->system_info->type].current_hits = 0.0f; + if (!(subsys->flags & SSF_NO_AGGREGATE)) { + ship_p->subsys_info[subsys->system_info->type].aggregate_current_hits -= subsys->current_hits; + if (ship_p->subsys_info[subsys->system_info->type].aggregate_current_hits < 0.0f) { + ship_p->subsys_info[subsys->system_info->type].aggregate_current_hits = 0.0f; + } } subsys->current_hits = 0.0f; do_subobj_destroyed_stuff( ship_p, subsys, hitpos ); @@ -681,16 +685,20 @@ float do_subobj_hit_stuff(object *ship_obj, object *other_obj, vec3d *hitpos, fl } subsys->current_hits -= damage_to_apply; - ship_p->subsys_info[subsys->system_info->type].current_hits -= damage_to_apply; + if (!(subsys->flags & SSF_NO_AGGREGATE)) { + ship_p->subsys_info[subsys->system_info->type].aggregate_current_hits -= damage_to_apply; + } if (subsys->current_hits < 0.0f) { damage_left -= subsys->current_hits; - ship_p->subsys_info[subsys->system_info->type].current_hits -= subsys->current_hits; + if (!(subsys->flags & SSF_NO_AGGREGATE)) { + ship_p->subsys_info[subsys->system_info->type].aggregate_current_hits -= subsys->current_hits; + } subsys->current_hits = 0.0f; // set to 0 so repair on subsystem takes immediate effect } - if ( ship_p->subsys_info[subsys->system_info->type].current_hits < 0.0f ){ - ship_p->subsys_info[subsys->system_info->type].current_hits = 0.0f; + if ( ship_p->subsys_info[subsys->system_info->type].aggregate_current_hits < 0.0f ){ + ship_p->subsys_info[subsys->system_info->type].aggregate_current_hits = 0.0f; } // multiplayer clients never blow up subobj stuff on their own