diff --git a/README.md b/README.md index 97f7e265..9e67be50 100644 --- a/README.md +++ b/README.md @@ -965,6 +965,10 @@ Called by the bot subsystem to use an item. Fetch an item ID by a classname; for the bot subsystem. +### Edict_ForceLookAtPoint + +Force the player to look at the given point - used for the nav editor. + ### Entity_IsVisibleToPlayer This function is for item instancing; the rerelease of Quake II supports instanced items, which will display only for the players who haven't picked it up yet. For online players, this simply removes the item if you've gotten it, but for split screen players it will show a ghost where the entity was on players that have already picked it up. diff --git a/rerelease/bg_local.h b/rerelease/bg_local.h index 44fc069e..c91b36c5 100644 --- a/rerelease/bg_local.h +++ b/rerelease/bg_local.h @@ -254,7 +254,8 @@ enum player_stat_t STAT_SELECTED_ITEM_NAME, // [Paril-KEX] STAT_HEALTH_BARS, // two health bar values; 7 bits for value, 1 bit for active - // if active, + // [Paril-KEX] + STAT_ACTIVE_WEAPON, // don't use; just for verification STAT_LAST diff --git a/rerelease/bots/bot_exports.cpp b/rerelease/bots/bot_exports.cpp index 56a8646b..2df67c55 100644 --- a/rerelease/bots/bot_exports.cpp +++ b/rerelease/bots/bot_exports.cpp @@ -149,3 +149,40 @@ int32_t Bot_GetItemID( const char * classname ) { return Item_Invalid; } + +/* +================ +Edict_ForceLookAtPoint +================ +*/ +void Edict_ForceLookAtPoint( edict_t * edict, gvec3_cref_t point ) { + vec3_t viewOrigin = edict->s.origin; + if ( edict->client != nullptr ) { + viewOrigin += edict->client->ps.viewoffset; + } + + const vec3_t ideal = ( point - viewOrigin ).normalized(); + + vec3_t viewAngles = vectoangles( ideal ); + if ( viewAngles.x < -180.0f ) { + viewAngles.x = anglemod( viewAngles.x + 360.0f ); + } + + if ( edict->client != nullptr ) { + edict->client->ps.pmove.delta_angles = ( viewAngles - edict->client->resp.cmd_angles ); + edict->client->ps.viewangles = {}; + edict->client->v_angle = {}; + edict->s.angles = {}; + } +} + +/* +================ +Bot_PickedUpItem + +Check if the given bot has picked up the given item or not. +================ +*/ +bool Bot_PickedUpItem( edict_t * bot, edict_t * item ) { + return item->item_picked_up_by[ ( bot->s.number - 1 ) ]; +} \ No newline at end of file diff --git a/rerelease/bots/bot_exports.h b/rerelease/bots/bot_exports.h index dd2c90f4..b1530cb8 100644 --- a/rerelease/bots/bot_exports.h +++ b/rerelease/bots/bot_exports.h @@ -6,4 +6,6 @@ void Bot_SetWeapon( edict_t * bot, const int weaponIndex, const bool instantSwitch ); void Bot_TriggerEdict( edict_t * bot, edict_t * edict ); int32_t Bot_GetItemID( const char * classname ); -void Bot_UseItem( edict_t * bot, const int32_t itemID ); \ No newline at end of file +void Bot_UseItem( edict_t * bot, const int32_t itemID ); +void Edict_ForceLookAtPoint( edict_t * edict, gvec3_cref_t point ); +bool Bot_PickedUpItem( edict_t * bot, edict_t * item ); diff --git a/rerelease/bots/bot_utils.cpp b/rerelease/bots/bot_utils.cpp index e13f90e1..9d9da04c 100644 --- a/rerelease/bots/bot_utils.cpp +++ b/rerelease/bots/bot_utils.cpp @@ -248,9 +248,6 @@ void Item_UpdateState( edict_t * item ) { } } - // track who has picked us up so far... - item->sv.pickedup_list = item->item_picked_up_by; - const item_id_t itemID = item->item->id; if ( itemID == IT_FLAG1 || itemID == IT_FLAG2 ) { item->sv.ent_flags |= SVFL_IS_OBJECTIVE; @@ -529,4 +526,4 @@ const edict_t * FindActorUnderCrosshair( const edict_t * player ) { } return traceEnt; -} \ No newline at end of file +} diff --git a/rerelease/cg_screen.cpp b/rerelease/cg_screen.cpp index 227fb9ba..4eb702ba 100644 --- a/rerelease/cg_screen.cpp +++ b/rerelease/cg_screen.cpp @@ -1050,7 +1050,7 @@ static void CG_ExecuteLayoutString (const char *s, vrect_t hud_vrect, vrect_t hu width = 3; value = ps->stats[STAT_AMMO]; - int32_t min_ammo = cgi.CL_GetWarnAmmoCount(ps->stats[STAT_ACTIVE_WHEEL_WEAPON]); + int32_t min_ammo = cgi.CL_GetWarnAmmoCount(ps->stats[STAT_ACTIVE_WEAPON]); if (!min_ammo) min_ammo = 5; // back compat @@ -1702,7 +1702,6 @@ static void CG_DrawInventory(const player_state_t *ps, const std::array void gi.LocBroadcast_Print(PRINT_HIGH, "$g_flag_returned", CTFTeamName(CTF_TEAM2)); } + + gi.sound(ent, CHAN_RELIABLE | CHAN_NO_PHS_ADD | CHAN_AUX, gi.soundindex("ctf/flagret.wav"), 1, ATTN_NONE, 0); } // Called from PlayerDie, to drop the flag from a dying player @@ -1087,6 +1089,20 @@ void SetCTFStats(edict_t *ent) p1 = imageindex_i_ctf1t; break; } + + // [Paril-KEX] make sure there is a dropped version on the map somewhere + if (p1 == imageindex_i_ctf1d) + { + e = G_FindByString<&edict_t::classname>(e, "item_flag_team1"); + + if (e == nullptr) + { + CTFResetFlag(CTF_TEAM1); + gi.LocBroadcast_Print(PRINT_HIGH, "$g_flag_returned", + CTFTeamName(CTF_TEAM1)); + gi.sound(ent, CHAN_RELIABLE | CHAN_NO_PHS_ADD | CHAN_AUX, gi.soundindex("ctf/flagret.wav"), 1, ATTN_NONE, 0); + } + } } else if (e->spawnflags.has(SPAWNFLAG_ITEM_DROPPED)) p1 = imageindex_i_ctf1d; // must be dropped @@ -1108,6 +1124,20 @@ void SetCTFStats(edict_t *ent) p2 = imageindex_i_ctf2t; break; } + + // [Paril-KEX] make sure there is a dropped version on the map somewhere + if (p2 == imageindex_i_ctf2d) + { + e = G_FindByString<&edict_t::classname>(e, "item_flag_team2"); + + if (e == nullptr) + { + CTFResetFlag(CTF_TEAM2); + gi.LocBroadcast_Print(PRINT_HIGH, "$g_flag_returned", + CTFTeamName(CTF_TEAM2)); + gi.sound(ent, CHAN_RELIABLE | CHAN_NO_PHS_ADD | CHAN_AUX, gi.soundindex("ctf/flagret.wav"), 1, ATTN_NONE, 0); + } + } } else if (e->spawnflags.has(SPAWNFLAG_ITEM_DROPPED)) p2 = imageindex_i_ctf2d; // must be dropped @@ -2960,7 +2990,7 @@ bool CTFStartClient(edict_t *ent) void CTFObserver(edict_t *ent) { - if (!G_TeamplayEnabled()) + if (!G_TeamplayEnabled() || g_teamplay_force_join->integer) return; // start as 'observer' diff --git a/rerelease/g_ai.cpp b/rerelease/g_ai.cpp index b542b5eb..cd8acc3c 100644 --- a/rerelease/g_ai.cpp +++ b/rerelease/g_ai.cpp @@ -93,7 +93,8 @@ void ai_stand(edict_t *self, float dist) if (self->monsterinfo.aiflags & AI_STAND_GROUND) { // [Paril-KEX] check if we've been pushed out of our point_combat - if (self->movetarget) + if (!(self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) && + self->movetarget && self->movetarget->classname && !strcmp(self->movetarget->classname, "point_combat")) { if (!boxes_intersect(self->absmin, self->absmax, self->movetarget->absmin, self->movetarget->absmax)) { @@ -105,7 +106,7 @@ void ai_stand(edict_t *self, float dist) } } - if (self->enemy) + if (self->enemy && !(self->enemy->classname && !strcmp(self->enemy->classname, "player_noise"))) { v = self->enemy->s.origin - self->s.origin; self->ideal_yaw = vectoyaw(v); @@ -453,6 +454,12 @@ bool infront(edict_t *self, edict_t *other) vec = other->s.origin - self->s.origin; vec.normalize(); dot = vec.dot(forward); + + // [Paril-KEX] if we're an ambush monster, reduce our cone of + // vision to not ruin surprises, unless we already had an enemy. + if (self->spawnflags.has(SPAWNFLAG_MONSTER_AMBUSH) && !self->monsterinfo.trail_time && !self->enemy) + return dot > 0.15f; + return dot > -0.30f; } @@ -730,7 +737,31 @@ bool FindTarget(edict_t *self) return false; if (client == self->enemy) - return true; // JDC false; + { + bool skip_found = true; + + // [Paril-KEX] slight special behavior if we are currently going to a sound + // and we hear a new one; because player noises are re-used, this can leave + // us with the "same" enemy even though it's a different noise. + if (heardit && (self->monsterinfo.aiflags & AI_SOUND_TARGET)) + { + vec3_t temp = client->s.origin - self->s.origin; + self->ideal_yaw = vectoyaw(temp); + + if (!FacingIdeal(self)) + skip_found = false; + else if (!SV_CloseEnough(self, client, 8.f)) + skip_found = false; + + if (!skip_found && (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)) + { + self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); + } + } + + if (skip_found) + return true; // JDC false; + } // ROGUE - hintpath coop fix if ((self->monsterinfo.aiflags & AI_HINT_PATH) && coop->integer) @@ -873,7 +904,8 @@ bool FacingIdeal(edict_t *self) //============================================================================= -MONSTERINFO_CHECKATTACK(M_CheckAttack) (edict_t *self) -> bool +// [Paril-KEX] split this out so we can use it for the other bosses +bool M_CheckAttack_Base(edict_t *self, float stand_ground_chance, float melee_chance, float near_chance, float mid_chance, float far_chance, float strafe_scalar) { vec3_t spot1, spot2; float chance; @@ -903,7 +935,7 @@ MONSTERINFO_CHECKATTACK(M_CheckAttack) (edict_t *self) -> bool spot2[2] += self->enemy->viewheight; tr = gi.traceline(spot1, spot2, self, - MASK_SOLID | CONTENTS_MONSTER | CONTENTS_PLAYER | CONTENTS_SLIME | CONTENTS_LAVA); + MASK_SOLID | CONTENTS_MONSTER | CONTENTS_PLAYER | CONTENTS_SLIME | CONTENTS_LAVA); } else { @@ -938,7 +970,7 @@ MONSTERINFO_CHECKATTACK(M_CheckAttack) (edict_t *self) -> bool { // make sure we're not going to shoot a monster tr = gi.traceline(spot1, self->monsterinfo.blind_fire_target, self, - CONTENTS_MONSTER); + CONTENTS_MONSTER); if (tr.allsolid || tr.startsolid || ((tr.fraction < 1.0f) && (tr.ent != self->enemy))) return false; @@ -982,28 +1014,29 @@ MONSTERINFO_CHECKATTACK(M_CheckAttack) (edict_t *self) -> bool if (level.time < self->monsterinfo.attack_finished) return false; - if (enemy_range > RANGE_MID) - return false; - if (self->monsterinfo.aiflags & AI_STAND_GROUND) { - chance = 0.7f; + chance = stand_ground_chance; } else if (enemy_range <= RANGE_MELEE) { - chance = 0.4f; + chance = melee_chance; } else if (enemy_range <= RANGE_NEAR) { - chance = 0.25f; + chance = near_chance; + } + else if (enemy_range <= RANGE_MID) + { + chance = mid_chance; } else { - chance = 0.06f; + chance = far_chance; } // PGM - go ahead and shoot every time if it's a info_notnull - if ((frandom() < chance) || (!self->enemy->client && self->enemy->solid == SOLID_NOT)) + if ((!self->enemy->client && self->enemy->solid == SOLID_NOT) || (frandom() < chance)) { self->monsterinfo.attack_state = AS_MISSILE; self->monsterinfo.attack_finished = level.time; @@ -1018,6 +1051,7 @@ MONSTERINFO_CHECKATTACK(M_CheckAttack) (edict_t *self) -> bool { // originally, just 0.3 float strafe_chance; + if (!(strcmp(self->classname, "monster_daedalus"))) strafe_chance = 0.8f; else @@ -1026,16 +1060,21 @@ MONSTERINFO_CHECKATTACK(M_CheckAttack) (edict_t *self) -> bool // if enemy is tesla, never strafe if ((self->enemy) && (self->enemy->classname) && (!strcmp(self->enemy->classname, "tesla_mine"))) strafe_chance = 0; + else + strafe_chance *= strafe_scalar; - monster_attack_state_t new_state = AS_STRAIGHT; + if (strafe_chance) + { + monster_attack_state_t new_state = AS_STRAIGHT; - if (frandom() < strafe_chance) - new_state = AS_SLIDING; + if (frandom() < strafe_chance) + new_state = AS_SLIDING; - if (new_state != self->monsterinfo.attack_state) - { - self->monsterinfo.strafe_check_time = level.time + random_time(1_sec, 3_sec); - self->monsterinfo.attack_state = new_state; + if (new_state != self->monsterinfo.attack_state) + { + self->monsterinfo.strafe_check_time = level.time + random_time(1_sec, 3_sec); + self->monsterinfo.attack_state = new_state; + } } } } @@ -1051,6 +1090,11 @@ MONSTERINFO_CHECKATTACK(M_CheckAttack) (edict_t *self) -> bool return false; } +MONSTERINFO_CHECKATTACK(M_CheckAttack) (edict_t *self) -> bool +{ + return M_CheckAttack_Base(self, 0.7f, 0.4f, 0.25f, 0.06f, 0.f, 1.0f); +} + /* ============= ai_run_melee @@ -1222,12 +1266,7 @@ bool ai_checkattack(edict_t *self, float dist) } else { - if (self->monsterinfo.aiflags & AI_BRUTAL) - { - if (self->enemy->health <= self->enemy->gib_health) - hesDeadJim = true; - } - else + if (!(self->monsterinfo.aiflags & AI_BRUTAL)) { if (self->enemy->health <= 0) hesDeadJim = true; diff --git a/rerelease/g_cmds.cpp b/rerelease/g_cmds.cpp index 87b982bb..e6fd65d1 100644 --- a/rerelease/g_cmds.cpp +++ b/rerelease/g_cmds.cpp @@ -3,7 +3,7 @@ #include "g_local.h" #include "m_player.h" -void SelectNextItem(edict_t *ent, item_flags_t itflags) +void SelectNextItem(edict_t *ent, item_flags_t itflags, bool menu = true) { gclient_t *cl; item_id_t i, index; @@ -12,12 +12,12 @@ void SelectNextItem(edict_t *ent, item_flags_t itflags) cl = ent->client; // ZOID - if (cl->menu) + if (menu && cl->menu) { PMenu_Next(ent); return; } - else if (cl->chase_target) + else if (menu && cl->chase_target) { ChaseNext(ent); return; @@ -96,7 +96,7 @@ void ValidateSelectedItem(edict_t *ent) if (cl->pers.inventory[cl->pers.selected_item]) return; // valid - SelectNextItem(ent, IF_ANY); + SelectNextItem(ent, IF_ANY, false); } //================================================================================= @@ -456,7 +456,7 @@ void Cmd_Spawn_f(edict_t *ent) if (other->inuse) gi.linkentity(other); - if (other->svflags & SVF_MONSTER) + if ((other->svflags & SVF_MONSTER) && other->think) other->think(other); } @@ -490,6 +490,19 @@ void Cmd_Teleport_f(edict_t *ent) ent->s.origin[0] = (float) atof(gi.argv(1)); ent->s.origin[1] = (float) atof(gi.argv(2)); ent->s.origin[2] = (float) atof(gi.argv(3)); + + if (gi.argc() >= 4) + { + float pitch = (float) atof(gi.argv(4)); + float yaw = (float) atof(gi.argv(5)); + float roll = (float) atof(gi.argv(6)); + vec3_t ang { pitch, yaw, roll }; + + ent->client->ps.pmove.delta_angles = ( ang - ent->client->resp.cmd_angles ); + ent->client->ps.viewangles = {}; + ent->client->v_angle = {}; + } + gi.linkentity(ent); } @@ -660,10 +673,16 @@ void Cmd_Drop_f(edict_t *ent) return; // ZOID--special case for tech powerups - if (Q_strcasecmp(gi.args(), "tech") == 0 && (it = CTFWhat_Tech(ent)) != nullptr) + if (Q_strcasecmp(gi.args(), "tech") == 0) { - it->drop(ent, it); - ValidateSelectedItem(ent); + it = CTFWhat_Tech(ent); + + if (it) + { + it->drop(ent, it); + ValidateSelectedItem(ent); + } + return; } // ZOID @@ -1033,7 +1052,7 @@ void Cmd_Where_f( edict_t * ent ) { const vec3_t & origin = ent->s.origin; std::string location; - fmt::format_to( std::back_inserter( location ), FMT_STRING( "{:.1f} {:.1f} {:.1f}\n" ), origin[ 0 ], origin[ 1 ], origin[ 2 ] ); + fmt::format_to( std::back_inserter( location ), FMT_STRING( "{:.1f} {:.1f} {:.1f} {:.1f} {:.1f} {:.1f}\n" ), origin[ 0 ], origin[ 1 ], origin[ 2 ], ent->client->ps.viewangles[0], ent->client->ps.viewangles[1], ent->client->ps.viewangles[2] ); gi.LocClient_Print( ent, PRINT_HIGH, "Location: {}\n", location.c_str() ); gi.SendToClipBoard( location.c_str() ); } @@ -1212,9 +1231,7 @@ void Cmd_Wave_f(edict_t *ent) if (do_animate) ent->client->anim_priority = ANIM_WAVE; - constexpr float NOTIFY_DISTANCE = 256.f; - bool notified_anybody = false; - const char *self_notify_msg = nullptr, *other_notify_msg = nullptr, *other_notify_none_msg = nullptr; + const char *other_notify_msg = nullptr, *other_notify_none_msg = nullptr; vec3_t start, dir; P_ProjectSource(ent, ent->client->v_angle, { 0, 0, 0 }, start, dir); @@ -1245,7 +1262,6 @@ void Cmd_Wave_f(edict_t *ent) switch (i) { case GESTURE_FLIP_OFF: - self_notify_msg = "$g_flipoff"; other_notify_msg = "$g_flipoff_other"; other_notify_none_msg = "$g_flipoff_none"; if (do_animate) @@ -1255,7 +1271,6 @@ void Cmd_Wave_f(edict_t *ent) } break; case GESTURE_SALUTE: - self_notify_msg = "$g_salute"; other_notify_msg = "$g_salute_other"; other_notify_none_msg = "$g_salute_none"; if (do_animate) @@ -1265,7 +1280,6 @@ void Cmd_Wave_f(edict_t *ent) } break; case GESTURE_TAUNT: - self_notify_msg = "$g_taunt"; other_notify_msg = "$g_taunt_other"; other_notify_none_msg = "$g_taunt_none"; if (do_animate) @@ -1275,7 +1289,6 @@ void Cmd_Wave_f(edict_t *ent) } break; case GESTURE_WAVE: - self_notify_msg = "$g_wave"; other_notify_msg = "$g_wave_other"; other_notify_none_msg = "$g_wave_none"; if (do_animate) @@ -1286,7 +1299,6 @@ void Cmd_Wave_f(edict_t *ent) break; case GESTURE_POINT: default: - self_notify_msg = "$g_point"; other_notify_msg = "$g_point_other"; other_notify_none_msg = "$g_point_none"; if (do_animate) @@ -1336,7 +1348,7 @@ void Cmd_Wave_f(edict_t *ent) gi.WriteShort(POI_PING + (ent->s.number - 1)); gi.WriteShort(5000); gi.WritePosition(tr.endpos); - gi.WriteShort(gi.imageindex("loc_ping")); + gi.WriteShort(level.pic_ping); gi.WriteByte(208); gi.WriteByte(POI_FLAG_NONE); gi.unicast(player, false); diff --git a/rerelease/g_func.cpp b/rerelease/g_func.cpp index bca3b89e..ae4e26ea 100644 --- a/rerelease/g_func.cpp +++ b/rerelease/g_func.cpp @@ -112,7 +112,27 @@ THINK(Move_Begin) (edict_t *ent) -> void ent->think = Move_Final; } +void Think_AccelMove_New(edict_t *ent); void Think_AccelMove(edict_t *ent); +bool Think_AccelMove_MoveInfo(moveinfo_t *moveinfo); + +constexpr float AccelerationDistance(float target, float rate) +{ + return (target * ((target / rate) + 1) / 2); +} + +inline void Move_Regular(edict_t *ent, const vec3_t &dest, void(*endfunc)(edict_t *self)) +{ + if (level.current_entity == ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent)) + { + Move_Begin(ent); + } + else + { + ent->nextthink = level.time + FRAME_TIME_S; + ent->think = Move_Begin; + } +} void Move_Calc(edict_t *ent, const vec3_t &dest, void(*endfunc)(edict_t *self)) { @@ -124,23 +144,99 @@ void Move_Calc(edict_t *ent, const vec3_t &dest, void(*endfunc)(edict_t *self)) if (ent->moveinfo.speed == ent->moveinfo.accel && ent->moveinfo.speed == ent->moveinfo.decel) { - if (level.current_entity == ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent)) + Move_Regular(ent, dest, endfunc); + } + else + { + // accelerative + ent->moveinfo.current_speed = 0; + + if (gi.tick_rate == 10) + ent->think = Think_AccelMove; + else { - Move_Begin(ent); + // [Paril-KEX] rewritten to work better at higher tickrates + ent->moveinfo.curve_frame = 0; + ent->moveinfo.num_subframes = (0.1f / gi.frame_time_s) - 1; + + float total_dist = ent->moveinfo.remaining_distance; + + std::vector distances; + + if (ent->moveinfo.num_subframes) + { + distances.push_back(0); + ent->moveinfo.curve_frame = 1; + } + else + ent->moveinfo.curve_frame = 0; + + // simulate 10hz movement + while (ent->moveinfo.remaining_distance) + { + if (!Think_AccelMove_MoveInfo(&ent->moveinfo)) + break; + + ent->moveinfo.remaining_distance -= ent->moveinfo.current_speed; + distances.push_back(total_dist - ent->moveinfo.remaining_distance); + } + + if (ent->moveinfo.num_subframes) + distances.push_back(total_dist); + + ent->moveinfo.subframe = 0; + ent->moveinfo.curve_ref = ent->s.origin; + ent->moveinfo.curve_positions = make_savable_memory(distances.size()); + std::copy(distances.begin(), distances.end(), ent->moveinfo.curve_positions.ptr); + + ent->moveinfo.num_frames_done = 0; + + ent->think = Think_AccelMove_New; } - else + + ent->nextthink = level.time + FRAME_TIME_S; + } +} + +THINK(Think_AccelMove_New) (edict_t *ent) -> void +{ + float t = 0.f; + float target_dist; + + if (ent->moveinfo.num_subframes) + { + if (ent->moveinfo.subframe == ent->moveinfo.num_subframes + 1) { - ent->nextthink = level.time + FRAME_TIME_S; - ent->think = Move_Begin; + ent->moveinfo.subframe = 0; + ent->moveinfo.curve_frame++; + + if (ent->moveinfo.curve_frame == ent->moveinfo.curve_positions.count) + { + Move_Final(ent); + return; + } } + + t = (ent->moveinfo.subframe + 1) / ((float) ent->moveinfo.num_subframes + 1); + + target_dist = lerp(ent->moveinfo.curve_positions[ent->moveinfo.curve_frame - 1], ent->moveinfo.curve_positions[ent->moveinfo.curve_frame], t); + ent->moveinfo.subframe++; } else { - // accelerative - ent->moveinfo.current_speed = 0; - ent->think = Think_AccelMove; - ent->nextthink = level.time + FRAME_TIME_S; + if (ent->moveinfo.curve_frame == ent->moveinfo.curve_positions.count) + { + Move_Final(ent); + return; + } + + target_dist = ent->moveinfo.curve_positions[ent->moveinfo.curve_frame++]; } + + ent->moveinfo.num_frames_done++; + vec3_t target_pos = ent->moveinfo.curve_ref + (ent->moveinfo.dir * target_dist); + ent->velocity = (target_pos - ent->s.origin) * (1.f / gi.frame_time_s); + ent->nextthink = level.time + FRAME_TIME_S; } // @@ -269,11 +365,6 @@ The team has completed a frame of movement, so change the speed for the next frame ============== */ -constexpr float AccelerationDistance(float target, float rate) -{ - return (target * ((target / rate) + 1) / 2); -} - void plat_CalcAcceleratedMove(moveinfo_t *moveinfo) { float accel_dist; @@ -382,6 +473,18 @@ void plat_Accelerate(moveinfo_t *moveinfo) return; } +bool Think_AccelMove_MoveInfo (moveinfo_t *moveinfo) +{ + if (moveinfo->current_speed == 0) // starting or blocked + plat_CalcAcceleratedMove(moveinfo); + + plat_Accelerate(moveinfo); + + // will the entire move complete on next frame? + return moveinfo->remaining_distance > moveinfo->current_speed; +} + +// Paril: old acceleration code; this is here only to support old save games. THINK(Think_AccelMove) (edict_t *ent) -> void { // [Paril-KEX] calculate distance dynamically @@ -390,12 +493,13 @@ THINK(Think_AccelMove) (edict_t *ent) -> void else ent->moveinfo.remaining_distance = (ent->moveinfo.end_origin - ent->s.origin).length(); - if (ent->moveinfo.current_speed == 0) // starting or blocked - plat_CalcAcceleratedMove(&ent->moveinfo); - - plat_Accelerate(&ent->moveinfo); - // will the entire move complete on next frame? + if (!Think_AccelMove_MoveInfo(&ent->moveinfo)) + { + Move_Final(ent); + return; + } + if (ent->moveinfo.remaining_distance <= ent->moveinfo.current_speed) { Move_Final(ent); @@ -2233,6 +2337,8 @@ void train_resume(edict_t *self) dest -= vec3_t{1.f, 1.f, 1.f}; } + self->s.sound = self->moveinfo.sound_middle; + self->moveinfo.state = STATE_TOP; self->moveinfo.start_origin = self->s.origin; self->moveinfo.end_origin = dest; diff --git a/rerelease/g_items.cpp b/rerelease/g_items.cpp index 1197d66b..dcda3d72 100644 --- a/rerelease/g_items.cpp +++ b/rerelease/g_items.cpp @@ -137,6 +137,9 @@ THINK(DoRespawn) (edict_t *ent) -> void else { // ZOID + ent->svflags |= SVF_NOCLIENT; + ent->solid = SOLID_NOT; + gi.linkentity(ent); for (count = 0, ent = master; ent; ent = ent->chain, count++) ; @@ -243,7 +246,7 @@ bool Pickup_Powerup(edict_t *ent, edict_t *other) if (deathmatch->integer) { - if (!(ent->spawnflags & SPAWNFLAG_ITEM_DROPPED)) + if (!(ent->spawnflags & SPAWNFLAG_ITEM_DROPPED) && !is_dropped_from_death) SetRespawn(ent, gtime_t::from_sec(ent->item->quantity)); } @@ -307,7 +310,7 @@ void G_CheckPowerArmor(edict_t *ent) if (!ent->client->pers.inventory[IT_AMMO_CELLS]) has_enough_cells = false; else if (ent->client->pers.autoshield >= AUTO_SHIELD_AUTO) - has_enough_cells = !(ent->flags & FL_WANTS_POWER_ARMOR) || ent->client->pers.inventory[IT_AMMO_CELLS] > ent->client->pers.autoshield; + has_enough_cells = (ent->flags & FL_WANTS_POWER_ARMOR) && ent->client->pers.inventory[IT_AMMO_CELLS] > ent->client->pers.autoshield; else has_enough_cells = true; @@ -612,6 +615,10 @@ bool Pickup_Ammo(edict_t *ent, edict_t *other) void Drop_Ammo(edict_t *ent, gitem_t *item) { + // [Paril-KEX] + if (G_CheckInfiniteAmmo(item)) + return; + item_id_t index = item->id; edict_t *dropped = Drop_Item(ent, item); dropped->spawnflags |= SPAWNFLAG_ITEM_DROPPED_PLAYER; @@ -638,7 +645,11 @@ void Drop_Ammo(edict_t *ent, gitem_t *item) THINK(MegaHealth_think) (edict_t *self) -> void { - if (self->owner->health > self->owner->max_health) + if (self->owner->health > self->owner->max_health + //ZOID + && !CTFHasRegeneration(self->owner) + //ZOID + ) { self->nextthink = level.time + 1_sec; self->owner->health -= 1; @@ -668,13 +679,22 @@ bool Pickup_Health(edict_t *ent, edict_t *other) other->health += count; + //ZOID + if (ctf->integer && other->health > 250 && count > 25) + other->health = 250; + //ZOID + if (!(health_flags & HEALTH_IGNORE_MAX)) { if (other->health > other->max_health) other->health = other->max_health; } - if (ent->item->tag & HEALTH_TIMED) + if ((ent->item->tag & HEALTH_TIMED) + //ZOID + && !CTFHasRegeneration(other) + //ZOID + ) { if (!deathmatch->integer) { @@ -735,6 +755,9 @@ bool Pickup_Armor(edict_t *ent, edict_t *other) old_armor_index = ArmorIndex(other); + // [Paril-KEX] for g_start_items + int32_t base_count = ent->count ? ent->count : newinfo ? newinfo->base_count : 0; + // handle armor shards specially if (ent->item->id == IT_ARMOR_SHARD) { @@ -743,11 +766,10 @@ bool Pickup_Armor(edict_t *ent, edict_t *other) else other->client->pers.inventory[old_armor_index] += 2; } - // if player has no armor, just use it else if (!old_armor_index) { - other->client->pers.inventory[ent->item->id] = newinfo->base_count; + other->client->pers.inventory[ent->item->id] = base_count; } // use the better armor @@ -766,7 +788,7 @@ bool Pickup_Armor(edict_t *ent, edict_t *other) // calc new armor values salvage = oldinfo->normal_protection / newinfo->normal_protection; salvagecount = (int) (salvage * other->client->pers.inventory[old_armor_index]); - newcount = newinfo->base_count + salvagecount; + newcount = base_count + salvagecount; if (newcount > newinfo->max_count) newcount = newinfo->max_count; @@ -780,7 +802,7 @@ bool Pickup_Armor(edict_t *ent, edict_t *other) { // calc new armor values salvage = newinfo->normal_protection / oldinfo->normal_protection; - salvagecount = (int) (salvage * newinfo->base_count); + salvagecount = (int) (salvage * base_count); newcount = other->client->pers.inventory[old_armor_index] + salvagecount; if (newcount > oldinfo->max_count) newcount = oldinfo->max_count; @@ -1291,7 +1313,9 @@ void SpawnItem(edict_t *ent, gitem_t *item) { if (item->pickup == Pickup_Armor || item->pickup == Pickup_PowerArmor || item->pickup == Pickup_Powerup || item->pickup == Pickup_Sphere || item->pickup == Pickup_Doppleganger || - (item->flags & IF_HEALTH) || (item->flags & IF_AMMO) || item->pickup == Pickup_Weapon || item->pickup == Pickup_Pack) + (item->flags & IF_HEALTH) || (item->flags & IF_AMMO) || item->pickup == Pickup_Weapon || item->pickup == Pickup_Pack || + item->id == IT_ITEM_BANDOLIER || item->id == IT_ITEM_PACK || + item->id == IT_AMMO_NUKE) { G_FreeEdict(ent); return; @@ -2970,7 +2994,10 @@ gives +1 to maximum health /* use_name */ "Bandolier", /* pickup_name */ "$item_bandolier", /* pickup_name_definite */ "$item_bandolier_def", - /* quantity */ 60 + /* quantity */ 60, + /* ammo */ IT_NULL, + /* chain */ IT_NULL, + /* flags */ IF_POWERUP }, /*QUAKED item_pack (.3 .3 1) (-16 -16 -16) (16 16 16) @@ -2991,6 +3018,9 @@ gives +1 to maximum health /* pickup_name */ "$item_ammo_pack", /* pickup_name_definite */ "$item_ammo_pack_def", /* quantity */ 180, + /* ammo */ IT_NULL, + /* chain */ IT_NULL, + /* flags */ IF_POWERUP }, @@ -3931,14 +3961,20 @@ void InitItems() if (!P_UseCoopInstancedItems() && (it.flags & IF_STAY_COOP)) it.drop = nullptr; } - else if (deathmatch->integer) - { - if (g_dm_weapons_stay->integer && it.drop == Drop_Weapon) - it.drop = nullptr; - } } } +// [Paril-KEX] +inline bool G_CanDropItem(const gitem_t &item) +{ + if (!item.drop) + return false; + else if ((item.flags & IF_WEAPON) && !(item.flags & IF_AMMO) && deathmatch->integer && g_dm_weapons_stay->integer) + return false; + + return true; +} + /* =============== SetItemNames @@ -3988,7 +4024,7 @@ void SetItemNames() (itemlist[i].flags & IF_POWERUP_WHEEL) ? 1 : 0, itemlist[i].sort_id, itemlist[i].quantity_warn, - itemlist[i].drop != nullptr ? 1 : 0 + G_CanDropItem(itemlist[i]) ? 1 : 0 ).data()); itemlist[i].weapon_wheel_index = cs_index; cs_index++; @@ -4010,7 +4046,7 @@ void SetItemNames() gi.imageindex(itemlist[i].icon), (itemlist[i].flags & IF_POWERUP_ONOFF) ? 1 : 0, itemlist[i].sort_id, - itemlist[i].drop != nullptr ? 1 : 0, + G_CanDropItem(itemlist[i]) ? 1 : 0, itemlist[i].ammo ? GetItemByIndex(itemlist[i].ammo)->ammo_wheel_index : -1 ).data()); itemlist[i].powerup_wheel_index = cs_index; diff --git a/rerelease/g_local.h b/rerelease/g_local.h index 46116908..8cb700f2 100644 --- a/rerelease/g_local.h +++ b/rerelease/g_local.h @@ -1177,6 +1177,7 @@ struct level_locals_t bool respawn_intermission; // only set once for respawning players int32_t pic_health; + int32_t pic_ping; int32_t total_secrets; int32_t found_secrets; @@ -1257,6 +1258,8 @@ struct shadow_light_temp_t const char *lightstyletarget = nullptr; }; +void G_LoadShadowLights(); + #include // spawn_temp_t is only used to hold entity field values that @@ -1341,6 +1344,82 @@ DEFINE_DATA_FUNC(moveinfo_blocked, MOVEINFO_BLOCKED, void, edict_t *self, edict_ #define MOVEINFO_BLOCKED(n) \ SAVE_DATA_FUNC(n, MOVEINFO_BLOCKED, void, edict_t *self, edict_t *other) +// a struct that can store type-safe allocations +// of a fixed amount of data. it self-destructs when +// re-assigned. TODO: because edicts are still kind of +// managed like C memory, the destructor may not be +// called for a freed entity if this is stored as a member. +template +struct savable_allocated_memory_t +{ + T *ptr; + size_t count; + + constexpr savable_allocated_memory_t(T *ptr, size_t count) : + ptr(ptr), + count(count) + { + } + + inline ~savable_allocated_memory_t() + { + release(); + } + + // no copy + constexpr savable_allocated_memory_t(const savable_allocated_memory_t &) = delete; + constexpr savable_allocated_memory_t &operator=(const savable_allocated_memory_t &) = delete; + + // free move + constexpr savable_allocated_memory_t(savable_allocated_memory_t &&move) + { + ptr = move.ptr; + count = move.count; + + move.ptr = nullptr; + move.count = 0; + } + + constexpr savable_allocated_memory_t &operator=(savable_allocated_memory_t &&move) + { + ptr = move.ptr; + count = move.count; + + move.ptr = nullptr; + move.count = 0; + + return *this; + } + + inline void release() + { + if (ptr) + { + gi.TagFree(ptr); + count = 0; + ptr = nullptr; + } + } + + constexpr explicit operator T *() { return ptr; } + constexpr explicit operator const T *() const { return ptr; } + + constexpr std::add_lvalue_reference_t operator[](const size_t index) { return ptr[index]; } + constexpr const std::add_lvalue_reference_t operator[](const size_t index) const { return ptr[index]; } + + constexpr size_t size() const { return count * sizeof(T); } + constexpr operator bool() const { return !!ptr; } +}; + +template +inline savable_allocated_memory_t make_savable_memory(size_t count) +{ + if (!count) + return { nullptr, 0 }; + + return { reinterpret_cast(gi.TagMalloc(sizeof(T) * count, tag)), count }; +} + struct moveinfo_t { // fixed data @@ -1372,6 +1451,13 @@ struct moveinfo_t float decel_distance; save_moveinfo_endfunc_t endfunc; save_moveinfo_blocked_t blocked; + + // [Paril-KEX] new accel state + vec3_t curve_ref; + savable_allocated_memory_t curve_positions; + size_t curve_frame; + uint8_t subframe, num_subframes; + size_t num_frames_done; }; struct mframe_t @@ -1693,9 +1779,6 @@ extern level_locals_t level; extern game_export_t globals; extern spawn_temp_t st; -extern int sm_meat_index; -extern int snd_fry; - extern edict_t *g_edicts; #include @@ -2223,7 +2306,8 @@ void HuntTarget(edict_t *self, bool animate_state = true); bool infront(edict_t *self, edict_t *other); bool visible(edict_t *self, edict_t *other, bool through_glass = true); bool FacingIdeal(edict_t *self); - +// [Paril-KEX] generic function +bool M_CheckAttack_Base(edict_t *self, float stand_ground_chance, float melee_chance, float near_chance, float mid_chance, float far_chance, float strafe_scalar); // // g_weapon.c @@ -2731,6 +2815,9 @@ constexpr gtime_t LADDER_SOUND_TIME = 300_ms; // time after damage that we can't respawn on a player for constexpr gtime_t COOP_DAMAGE_RESPAWN_TIME = 2000_ms; +// time after firing that we can't respawn on a player for +constexpr gtime_t COOP_DAMAGE_FIRING_TIME = 2500_ms; + // this structure is cleared on each PutClientInServer(), // except for 'client->pers' struct gclient_t @@ -2917,6 +3004,8 @@ struct gclient_t height_fog_t heightfog; gtime_t last_attacker_time; + // saved - for coop; last time we were in a firing state + gtime_t last_firing_time; }; // ========================================== @@ -3497,4 +3586,64 @@ inline void pierce_args_t::restore() } num_pierced = 0; -} \ No newline at end of file +} + +// [Paril-KEX] these are to fix a legacy bug with cached indices +// in save games. these can *only* be static/globals! +template +struct cached_assetindex +{ + static cached_assetindex *head; + + const char *name; + int32_t index = 0; + cached_assetindex *next = nullptr; + + inline cached_assetindex() + { + next = head; + cached_assetindex::head = this; + } + constexpr operator int32_t() const { return index; } + + // assigned from spawn functions + inline void assign(const char *name) { this->name = name; index = (gi.*T)(name); } + // cleared before SpawnEntities + constexpr void clear() { index = 0; } + // re-find the index for the given cached entry, if we were cached + // by the regular map load + inline void reset() { if (index) index = (gi.*T)(this->name); } + + static void reset_all() + { + auto asset = head; + + while (asset) + { + asset->reset(); + asset = asset->next; + } + } + + static void clear_all() + { + auto asset = head; + + while (asset) + { + asset->clear(); + asset = asset->next; + } + } +}; + +using cached_soundindex = cached_assetindex<&local_game_import_t::soundindex>; +using cached_modelindex = cached_assetindex<&local_game_import_t::modelindex>; +using cached_imageindex = cached_assetindex<&local_game_import_t::imageindex>; + +template<> cached_soundindex *cached_soundindex::head; +template<> cached_modelindex *cached_modelindex::head; +template<> cached_imageindex *cached_imageindex::head; + +extern cached_modelindex sm_meat_index; +extern cached_soundindex snd_fry; \ No newline at end of file diff --git a/rerelease/g_main.cpp b/rerelease/g_main.cpp index f7944354..03638695 100644 --- a/rerelease/g_main.cpp +++ b/rerelease/g_main.cpp @@ -22,8 +22,8 @@ local_game_import_t gi; game_export_t globals; spawn_temp_t st; -int sm_meat_index; -int snd_fry; +cached_modelindex sm_meat_index; +cached_soundindex snd_fry; edict_t *g_edicts; @@ -333,7 +333,7 @@ void InitGame() g_friendly_fire = gi.cvar("g_friendly_fire", "0", CVAR_NOFLAGS); g_dm_force_respawn = gi.cvar("g_dm_force_respawn", "0", CVAR_NOFLAGS); g_dm_force_respawn_time = gi.cvar("g_dm_force_respawn_time", "0", CVAR_NOFLAGS); - g_dm_spawn_farthest = gi.cvar("g_dm_spawn_farthest", "0", CVAR_NOFLAGS); + g_dm_spawn_farthest = gi.cvar("g_dm_spawn_farthest", "1", CVAR_NOFLAGS); g_no_armor = gi.cvar("g_no_armor", "0", CVAR_NOFLAGS); g_dm_allow_exit = gi.cvar("g_dm_allow_exit", "0", CVAR_NOFLAGS); g_infinite_ammo = gi.cvar("g_infinite_ammo", "0", CVAR_LATCH); @@ -446,6 +446,9 @@ Q2GAME_API game_export_t *GetGameAPI(game_import_t *import) globals.Bot_TriggerEdict = Bot_TriggerEdict; globals.Bot_GetItemID = Bot_GetItemID; globals.Bot_UseItem = Bot_UseItem; + globals.Edict_ForceLookAtPoint = Edict_ForceLookAtPoint; + globals.Bot_PickedUpItem = Bot_PickedUpItem; + globals.Entity_IsVisibleToPlayer = Entity_IsVisibleToPlayer; globals.GetShadowLightData = GetShadowLightData; diff --git a/rerelease/g_misc.cpp b/rerelease/g_misc.cpp index 67b013ae..cb3a2a3f 100644 --- a/rerelease/g_misc.cpp +++ b/rerelease/g_misc.cpp @@ -508,6 +508,7 @@ USE(light_use) (edict_t *self, edict_t *other, edict_t *activator) -> void // [Sam-KEX] For keeping track of shadow light parameters and setting them up on // the server side. +// TODO move to level_locals_t struct shadow_light_info_t { int entity_number; @@ -568,6 +569,57 @@ void setup_shadow_lights() shadowlightinfo[i].shadowlight.conedirection[2]).data()); } } + +// fix an oversight in shadow light code that causes +// lights to be ordered wrong on return levels +// if the spawn functions are changed. +// this will work without changing the save/load code. +void G_LoadShadowLights() +{ + for (size_t i = 0; i < level.shadow_light_count; i++) + { + const char *cstr = gi.get_configstring(CS_SHADOWLIGHTS + i); + const char *token = COM_ParseEx(&cstr, ";"); + + if (token && *token) + { + shadowlightinfo[i].entity_number = atoi(token); + + token = COM_ParseEx(&cstr, ";"); + shadowlightinfo[i].shadowlight.lighttype = (shadow_light_type_t) atoi(token); + + token = COM_ParseEx(&cstr, ";"); + shadowlightinfo[i].shadowlight.radius = atof(token); + + token = COM_ParseEx(&cstr, ";"); + shadowlightinfo[i].shadowlight.resolution = atoi(token); + + token = COM_ParseEx(&cstr, ";"); + shadowlightinfo[i].shadowlight.intensity = atof(token); + + token = COM_ParseEx(&cstr, ";"); + shadowlightinfo[i].shadowlight.fade_start = atof(token); + + token = COM_ParseEx(&cstr, ";"); + shadowlightinfo[i].shadowlight.fade_end = atof(token); + + token = COM_ParseEx(&cstr, ";"); + shadowlightinfo[i].shadowlight.lightstyle = atoi(token); + + token = COM_ParseEx(&cstr, ";"); + shadowlightinfo[i].shadowlight.coneangle = atof(token); + + token = COM_ParseEx(&cstr, ";"); + shadowlightinfo[i].shadowlight.conedirection[0] = atof(token); + + token = COM_ParseEx(&cstr, ";"); + shadowlightinfo[i].shadowlight.conedirection[1] = atof(token); + + token = COM_ParseEx(&cstr, ";"); + shadowlightinfo[i].shadowlight.conedirection[2] = atof(token); + } + } +} // --------------------------------------------------------------------------------- static void setup_dynamic_light(edict_t* self) diff --git a/rerelease/g_monster.cpp b/rerelease/g_monster.cpp index cf435435..38b41cd3 100644 --- a/rerelease/g_monster.cpp +++ b/rerelease/g_monster.cpp @@ -791,6 +791,26 @@ void monster_dead(edict_t *self) gi.linkentity(self); } +/* +============= +infront + +returns 1 if the entity is in front (in sight) of self +============= +*/ +static bool projectile_infront(edict_t *self, edict_t *other) +{ + vec3_t vec; + float dot; + vec3_t forward; + + AngleVectors(self->s.angles, forward, nullptr, nullptr); + vec = other->s.origin - self->s.origin; + vec.normalize(); + dot = vec.dot(forward); + return dot > 0.35f; +} + static BoxEdictsResult_t M_CheckDodge_BoxEdictsFilter(edict_t *ent, void *data) { edict_t *self = (edict_t *) data; @@ -804,7 +824,7 @@ static BoxEdictsResult_t M_CheckDodge_BoxEdictsFilter(edict_t *ent, void *data) return BoxEdictsResult_t::Skip; // projectile is behind us, we can't see it - if (!infront(self, ent)) + if (!projectile_infront(self, ent)) return BoxEdictsResult_t::Skip; // will it hit us within 1 second? gives us enough time to dodge @@ -1065,7 +1085,7 @@ USE(monster_triggered_spawn_use) (edict_t *self, edict_t *other, edict_t *activa // we have a one frame delay here so we don't telefrag the guy who activated us self->think = monster_triggered_spawn; self->nextthink = level.time + FRAME_TIME_S; - if (activator->client && !(self->hackflags & HACKFLAG_END_CUTSCENE)) + if (activator && activator->client && !(self->hackflags & HACKFLAG_END_CUTSCENE)) self->enemy = activator; self->use = monster_use; diff --git a/rerelease/g_phys.cpp b/rerelease/g_phys.cpp index 28ad3669..5adb7e73 100644 --- a/rerelease/g_phys.cpp +++ b/rerelease/g_phys.cpp @@ -450,13 +450,6 @@ void SV_Physics_Pusher(edict_t *ent) if (!obstacle->inuse) goto retry; - - // the move failed, bump all nextthink times and back out moves - for (mv = ent; mv; mv = mv->teamchain) - { - if (mv->nextthink > 0_ms) - mv->nextthink += FRAME_TIME_S; - } } else { @@ -849,7 +842,11 @@ void SV_Physics_Step(edict_t *ent) ent->gravity = 1.0; // ======== - G_TouchTriggers(ent); + // [Paril-KEX] this is something N64 does to avoid doors opening + // at the start of a level, which triggers some monsters to spawn. + if (!level.is_n64 || level.time > FRAME_TIME_S) + G_TouchTriggers(ent); + if (!ent->inuse) return; diff --git a/rerelease/g_save.cpp b/rerelease/g_save.cpp index 44d8b561..dd706947 100644 --- a/rerelease/g_save.cpp +++ b/rerelease/g_save.cpp @@ -205,7 +205,8 @@ enum save_type_id_t ST_TIME, // serialized as milliseconds ST_DATA, // serialized as name of data ptr from global list; `tag` = list tag ST_INVENTORY, // serialized as classname => number key/value pairs - ST_REINFORCEMENTS // serialized as array of data + ST_REINFORCEMENTS, // serialized as array of data + ST_SAVABLE_DYNAMIC // serialized similar to ST_FIXED_ARRAY but includes count }; struct save_struct_t; @@ -495,6 +496,21 @@ struct save_type_deducer> } }; +// savable_allocated_memory_t +template +struct save_type_deducer> +{ + static constexpr save_field_t get_save_type(const char *name, size_t offset) + { + auto type = save_type_deducer>::get_save_type(nullptr, 0).type; + + if (type.id <= ST_BOOL || type.id >= ST_DOUBLE) + return { name, offset, { ST_SAVABLE_DYNAMIC, ST_INVALID, Tag, []() { return save_type_deducer>::get_save_type(nullptr, 0).type; } } }; + + return { name, offset, { ST_SAVABLE_DYNAMIC, type.id, Tag } }; + } +}; + // save_data_ref template struct save_type_deducer> @@ -681,6 +697,7 @@ SAVE_STRUCT_START FIELD_AUTO(intermission_angle), // pic_health is set by worldspawn + // pic_ping is set by worldspawn FIELD_AUTO(total_secrets), FIELD_AUTO(found_secrets), @@ -910,6 +927,8 @@ SAVE_STRUCT_START FIELD_AUTO(sound_entity_time), FIELD_AUTO(sound2_entity), FIELD_AUTO(sound2_entity_time), + + FIELD_AUTO(last_firing_time), SAVE_STRUCT_END #undef DECLARE_SAVE_STRUCT // clang-format on @@ -1107,6 +1126,13 @@ SAVE_STRUCT_START FIELD_AUTO(moveinfo.endfunc), FIELD_AUTO(moveinfo.blocked), + FIELD_AUTO(moveinfo.curve_ref), + FIELD_AUTO(moveinfo.curve_positions), + FIELD_AUTO(moveinfo.curve_frame), + FIELD_AUTO(moveinfo.subframe), + FIELD_AUTO(moveinfo.num_subframes), + FIELD_AUTO(moveinfo.num_frames_done), + // monsterinfo_t FIELD_AUTO(monsterinfo.active_move), FIELD_AUTO(monsterinfo.next_move), @@ -1281,7 +1307,7 @@ SAVE_STRUCT_END #undef DECLARE_SAVE_STRUCT // clang-format on -size_t get_simple_type_size(save_type_id_t id) +inline size_t get_simple_type_size(save_type_id_t id, bool fatal = true) { switch (id) { @@ -1309,8 +1335,12 @@ size_t get_simple_type_size(save_type_id_t id) return sizeof(size_t); case ST_ITEM_INDEX: return sizeof(uint32_t); + case ST_SAVABLE_DYNAMIC: + return sizeof(savable_allocated_memory_t); default: - gi.Com_ErrorFmt("Can't calculate static size for type ID {}", (int32_t) id); + if (fatal) + gi.Com_ErrorFmt("Can't calculate static size for type ID {}", (int32_t) id); + break; } return 0; @@ -1319,8 +1349,8 @@ size_t get_simple_type_size(save_type_id_t id) size_t get_complex_type_size(const save_type_t &type) { // these are simple types - if ((type.id >= ST_BOOL && type.id <= ST_DOUBLE) || (type.id >= ST_ENTITY && type.id <= ST_TIME)) - return get_simple_type_size(type.id); + if (auto simple = get_simple_type_size(type.id, false)) + return simple; switch (type.id) { @@ -1448,7 +1478,7 @@ void read_save_type_json(const Json::Value &json, void *data, const save_type_t else if (json.asInt() < INT8_MIN || json.asInt() > INT8_MAX) json_print_error(field, "int8 out of range", false); else - *((int8_t *) data) = json.isInt(); + *((int8_t *) data) = json.asInt(); return; case ST_INT16: if (!json.isInt()) @@ -1622,6 +1652,40 @@ void read_save_type_json(const Json::Value &json, void *data, const save_type_t } } + return; + case ST_SAVABLE_DYNAMIC: + if (!json.isArray()) + json_print_error(field, "expected array", false); + else + { + savable_allocated_memory_t *savptr = (savable_allocated_memory_t *) data; + size_t element_size; + save_type_t element_type; + + if (type->type_resolver) + { + element_type = type->type_resolver(); + element_size = get_complex_type_size(element_type); + } + else + { + element_size = get_simple_type_size((save_type_id_t) type->tag); + element_type = { (save_type_id_t) type->tag }; + } + + savptr->count = json.size(); + savptr->ptr = gi.TagMalloc(element_size * savptr->count, type->count); + + byte *out_element = (byte *) savptr->ptr; + + for (Json::Value::ArrayIndex i = 0; i < savptr->count; i++, out_element += element_size) + { + const Json::Value &v = json[i]; + read_save_type_json(v, out_element, &element_type, + fmt::format("[{}]", i).c_str()); + } + } + return; case ST_BITSET: type->read(data, json, field); @@ -2005,7 +2069,62 @@ bool write_save_type_json(const void *data, const save_type_t *type, bool null_f v.append(std::move(value)); } - output = v; + output = std::move(v); + return true; + } + case ST_SAVABLE_DYNAMIC: { + const savable_allocated_memory_t *savptr = (const savable_allocated_memory_t *) data; + size_t i; + size_t element_size; + save_type_t element_type; + + if (type->type_resolver) + { + element_type = type->type_resolver(); + element_size = get_complex_type_size(element_type); + } + else + { + element_size = get_simple_type_size((save_type_id_t) type->tag); + element_type = { (save_type_id_t) type->tag }; + } + + const uint8_t *element = (const uint8_t *) savptr->ptr; + + if (null_for_empty) + { + if (type->is_empty) + { + if (type->is_empty(data)) + return false; + } + else + { + for (i = 0; i < savptr->count; i++, element += element_size) + { + Json::Value value; + bool valid_value = write_save_type_json(element, &element_type, !element_type.never_empty, value); + + if (valid_value) + break; + } + + if (i == savptr->count) + return false; + } + } + + element = (const uint8_t *) savptr->ptr; + Json::Value v(Json::arrayValue); + + for (i = 0; i < savptr->count; i++, element += element_size) + { + Json::Value value; + write_save_type_json(element, &element_type, false, value); + v.append(std::move(value)); + } + + output = std::move(v); return true; } case ST_BITSET: @@ -2020,7 +2139,7 @@ bool write_save_type_json(const void *data, const save_type_t *type, bool null_f if (null_for_empty && (!valid_value || !obj.size())) return false; - output = obj; + output = std::move(obj); return true; } case ST_ENTITY: { @@ -2134,7 +2253,7 @@ bool write_save_type_json(const void *data, const save_type_t *type, bool null_f if (null_for_empty && inventory.empty()) return false; - output = inventory; + output = std::move(inventory); return true; } case ST_REINFORCEMENTS: { @@ -2165,7 +2284,7 @@ bool write_save_type_json(const void *data, const save_type_t *type, bool null_f reinforcements[i] = obj; } - output = reinforcements; + output = std::move(reinforcements); return true; } default: @@ -2194,7 +2313,7 @@ bool write_save_struct_json(const void *data, const save_struct_t *structure, bo if (null_for_empty && obj.empty()) return false; - output = obj; + output = std::move(obj); return true; } @@ -2458,6 +2577,13 @@ void ReadLevelJson(const char *jsonString) } G_PrecacheInventoryItems(); + + // clear cached indices + cached_soundindex::reset_all(); + cached_modelindex::reset_all(); + cached_imageindex::reset_all(); + + G_LoadShadowLights(); } // [Paril-KEX] @@ -2468,6 +2594,16 @@ bool G_CanSave() gi.LocClient_Print(&g_edicts[1], PRINT_CENTER, "$g_no_save_dead"); return false; } + // don't allow saving during cameras/intermissions as this + // causes the game to act weird when these are loaded + else if (level.intermissiontime) + { + return false; + } return true; -} \ No newline at end of file +} + +/*static*/ template<> cached_soundindex *cached_soundindex::head = nullptr; +/*static*/ template<> cached_modelindex *cached_modelindex::head = nullptr; +/*static*/ template<> cached_imageindex *cached_imageindex::head = nullptr; \ No newline at end of file diff --git a/rerelease/g_spawn.cpp b/rerelease/g_spawn.cpp index 0021fefd..62cbbba9 100644 --- a/rerelease/g_spawn.cpp +++ b/rerelease/g_spawn.cpp @@ -1144,6 +1144,11 @@ parsing textual entity definitions out of an ent file. */ void SpawnEntities(const char *mapname, const char *entities, const char *spawnpoint) { + // clear cached indices + cached_soundindex::clear_all(); + cached_modelindex::clear_all(); + cached_imageindex::clear_all(); + edict_t *ent; int inhibit; const char *com_token; @@ -1509,7 +1514,7 @@ void SP_worldspawn(edict_t *ent) gi.configstring(CS_MAXCLIENTS, G_Fmt("{}", game.maxclients).data()); - if (level.is_n64) + if (level.is_n64 && !deathmatch->integer) { gi.configstring(CONFIG_N64_PHYSICS, "1"); pm_config.n64_physics = true; @@ -1544,7 +1549,7 @@ void SP_worldspawn(edict_t *ent) gi.cvar_set("sv_gravity", st.gravity); } - snd_fry = gi.soundindex("player/fry.wav"); // standing in lava / slime + snd_fry.assign("player/fry.wav"); // standing in lava / slime PrecacheItem(GetItemByIndex(IT_ITEM_COMPASS)); PrecacheItem(GetItemByIndex(IT_WEAPON_BLASTER)); @@ -1583,6 +1588,7 @@ void SP_worldspawn(edict_t *ent) gi.soundindex("*pain75_2.wav"); gi.soundindex("*pain100_1.wav"); gi.soundindex("*pain100_2.wav"); + gi.soundindex("*drown1.wav"); // [Paril-KEX] // sexed models for (auto &item : itemlist) @@ -1624,6 +1630,10 @@ void SP_worldspawn(edict_t *ent) gi.soundindex("player/u_breath1.wav"); gi.soundindex("player/u_breath2.wav"); + gi.soundindex("player/wade1.wav"); + gi.soundindex("player/wade2.wav"); + gi.soundindex("player/wade3.wav"); + gi.soundindex("items/pkup.wav"); // bonus item pickup gi.soundindex("world/land.wav"); // landing thud gi.soundindex("misc/h2ohit1.wav"); // landing splash @@ -1637,7 +1647,7 @@ void SP_worldspawn(edict_t *ent) gi.soundindex("infantry/inflies1.wav"); - sm_meat_index = gi.modelindex("models/objects/gibs/sm_meat/tris.md2"); + sm_meat_index.assign("models/objects/gibs/sm_meat/tris.md2"); gi.modelindex("models/objects/gibs/arm/tris.md2"); gi.modelindex("models/objects/gibs/bone/tris.md2"); gi.modelindex("models/objects/gibs/bone2/tris.md2"); @@ -1646,7 +1656,7 @@ void SP_worldspawn(edict_t *ent) gi.modelindex("models/objects/gibs/head2/tris.md2"); gi.modelindex("models/objects/gibs/sm_metal/tris.md2"); - gi.imageindex("loc_ping"); + level.pic_ping = gi.imageindex("loc_ping"); // // Setup light animation tables. 'a' is total darkness, 'z' is doublebright. diff --git a/rerelease/g_target.cpp b/rerelease/g_target.cpp index 27cdd425..f1fa8ff0 100644 --- a/rerelease/g_target.cpp +++ b/rerelease/g_target.cpp @@ -1327,7 +1327,15 @@ USE(use_target_camera) (edict_t *self, edict_t *other, edict_t *activator) -> vo // respawn any dead clients if (client->health <= 0) + { + // give us our max health back since it will reset + // to pers.health; in instanced items we'd lose the items + // we touched so we always want to respawn with our max. + if (P_UseCoopInstancedItems()) + client->client->pers.health = client->client->pers.max_health = client->max_health; + respawn(client); + } MoveClientToIntermission(client); } @@ -1606,6 +1614,9 @@ static float distance_to_poi(vec3_t start, vec3_t end) if (gi.GetPathToGoal(request, info)) return info.pathDistSqr; + if (info.returnCode == PathReturnCode::NoNavAvailable) + return (end - start).lengthSquared(); + return std::numeric_limits::infinity(); } @@ -1699,6 +1710,13 @@ USE(target_poi_use) (edict_t *ent, edict_t *other, edict_t *activator) -> void else return; } + + // copy over POI stage value + if (ent->count) + { + if (level.current_poi_stage <= ent->count) + level.current_poi_stage = ent->count; + } } else { diff --git a/rerelease/g_trigger.cpp b/rerelease/g_trigger.cpp index 89d5f849..63dd9893 100644 --- a/rerelease/g_trigger.cpp +++ b/rerelease/g_trigger.cpp @@ -480,7 +480,7 @@ constexpr spawnflags_t SPAWNFLAG_PUSH_START_OFF = 0x08_spawnflag; constexpr spawnflags_t SPAWNFLAG_PUSH_CLIP = 0x10_spawnflag; // PGM -static int windsound; +static cached_soundindex windsound; TOUCH(trigger_push_touch) (edict_t *self, edict_t *other, const trace_t &tr, bool other_touching_self) -> void { @@ -601,7 +601,7 @@ void SP_trigger_push(edict_t *self) { InitTrigger(self); if (!(self->spawnflags & SPAWNFLAG_PUSH_SILENT)) - windsound = gi.soundindex("misc/windfly.wav"); + windsound.assign("misc/windfly.wav"); self->touch = trigger_push_touch; // RAFAEL @@ -1054,11 +1054,11 @@ TOUCH(trigger_fog_touch) (edict_t *self, edict_t *other, const trace_t &tr, bool if (self->spawnflags.has(SPAWNFLAG_FOG_AFFECT_FOG)) { other->client->pers.wanted_fog = { - lerp(fog_value_storage->fog.density, fog_value_storage->fog.density_off, dist), - lerp(fog_value_storage->fog.color[0], fog_value_storage->fog.color_off[0], dist), - lerp(fog_value_storage->fog.color[1], fog_value_storage->fog.color_off[1], dist), - lerp(fog_value_storage->fog.color[2], fog_value_storage->fog.color_off[2], dist), - lerp(fog_value_storage->fog.sky_factor, fog_value_storage->fog.sky_factor_off, dist) + lerp(fog_value_storage->fog.density_off, fog_value_storage->fog.density, dist), + lerp(fog_value_storage->fog.color_off[0], fog_value_storage->fog.color[0], dist), + lerp(fog_value_storage->fog.color_off[1], fog_value_storage->fog.color[1], dist), + lerp(fog_value_storage->fog.color_off[2], fog_value_storage->fog.color[2], dist), + lerp(fog_value_storage->fog.sky_factor_off, fog_value_storage->fog.sky_factor, dist) }; } @@ -1066,19 +1066,19 @@ TOUCH(trigger_fog_touch) (edict_t *self, edict_t *other, const trace_t &tr, bool { other->client->pers.wanted_heightfog = { { - lerp(fog_value_storage->heightfog.start_color[0], fog_value_storage->heightfog.start_color_off[0], dist), - lerp(fog_value_storage->heightfog.start_color[1], fog_value_storage->heightfog.start_color_off[1], dist), - lerp(fog_value_storage->heightfog.start_color[2], fog_value_storage->heightfog.start_color_off[2], dist), - lerp(fog_value_storage->heightfog.start_dist, fog_value_storage->heightfog.start_dist_off, dist) + lerp(fog_value_storage->heightfog.start_color_off[0], fog_value_storage->heightfog.start_color[0], dist), + lerp(fog_value_storage->heightfog.start_color_off[1], fog_value_storage->heightfog.start_color[1], dist), + lerp(fog_value_storage->heightfog.start_color_off[2], fog_value_storage->heightfog.start_color[2], dist), + lerp(fog_value_storage->heightfog.start_dist_off, fog_value_storage->heightfog.start_dist, dist) }, { - lerp(fog_value_storage->heightfog.end_color[0], fog_value_storage->heightfog.end_color_off[0], dist), - lerp(fog_value_storage->heightfog.end_color[1], fog_value_storage->heightfog.end_color_off[1], dist), - lerp(fog_value_storage->heightfog.end_color[2], fog_value_storage->heightfog.end_color_off[2], dist), - lerp(fog_value_storage->heightfog.end_dist, fog_value_storage->heightfog.end_dist_off, dist) + lerp(fog_value_storage->heightfog.end_color_off[0], fog_value_storage->heightfog.end_color[0], dist), + lerp(fog_value_storage->heightfog.end_color_off[1], fog_value_storage->heightfog.end_color[1], dist), + lerp(fog_value_storage->heightfog.end_color_off[2], fog_value_storage->heightfog.end_color[2], dist), + lerp(fog_value_storage->heightfog.end_dist_off, fog_value_storage->heightfog.end_dist, dist) }, - lerp(fog_value_storage->heightfog.falloff, fog_value_storage->heightfog.falloff_off, dist), - lerp(fog_value_storage->heightfog.density, fog_value_storage->heightfog.density_off, dist) + lerp(fog_value_storage->heightfog.falloff_off, fog_value_storage->heightfog.falloff, dist), + lerp(fog_value_storage->heightfog.density_off, fog_value_storage->heightfog.density, dist) }; } diff --git a/rerelease/g_utils.cpp b/rerelease/g_utils.cpp index 52fdca26..69ca9752 100644 --- a/rerelease/g_utils.cpp +++ b/rerelease/g_utils.cpp @@ -447,8 +447,13 @@ void G_TouchTriggers(edict_t *ent) // to see if we need to collide against them void G_TouchProjectiles(edict_t *ent, vec3_t previous_origin) { + struct skipped_projectile + { + edict_t *projectile; + int32_t spawn_count; + }; // a bit ugly, but we'll store projectiles we are ignoring here. - static std::vector skipped; + static std::vector skipped; while (true) { @@ -462,7 +467,7 @@ void G_TouchProjectiles(edict_t *ent, vec3_t previous_origin) // always skip this projectile since certain conditions may cause the projectile // to not disappear immediately tr.ent->svflags &= ~SVF_PROJECTILE; - skipped.push_back(tr.ent); + skipped.push_back({ tr.ent, tr.ent->spawn_count }); // if we're both players and it's coop, allow the projectile to "pass" through if (ent->client && tr.ent->owner && tr.ent->owner->client && !G_ShouldPlayersCollide(true)) @@ -472,7 +477,8 @@ void G_TouchProjectiles(edict_t *ent, vec3_t previous_origin) } for (auto &skip : skipped) - skip->svflags |= SVF_PROJECTILE; + if (skip.projectile->inuse && skip.projectile->spawn_count == skip.spawn_count) + skip.projectile->svflags |= SVF_PROJECTILE; skipped.clear(); } @@ -526,7 +532,7 @@ bool KillBox(edict_t *ent, bool from_spawning, mod_id_t mod, bool bsp_clipping) if (hit == ent) continue; - else if (!hit->inuse || !hit->takedamage || !hit->solid || hit->solid == SOLID_TRIGGER) + else if (!hit->inuse || !hit->takedamage || !hit->solid || hit->solid == SOLID_TRIGGER || hit->solid == SOLID_BSP) continue; else if (hit->client && !(mask & CONTENTS_PLAYER)) continue; @@ -539,6 +545,16 @@ bool KillBox(edict_t *ent, bool from_spawning, mod_id_t mod, bool bsp_clipping) continue; } + // [Paril-KEX] don't allow telefragging of friends in coop. + // the player that is about to be telefragged will have collision + // disabled until another time. + if (ent->client && hit->client && coop->integer) + { + hit->clipmask &= ~CONTENTS_PLAYER; + ent->clipmask &= ~CONTENTS_PLAYER; + continue; + } + T_Damage(hit, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, mod); } diff --git a/rerelease/g_weapon.cpp b/rerelease/g_weapon.cpp index 5baa523a..a5c061b4 100644 --- a/rerelease/g_weapon.cpp +++ b/rerelease/g_weapon.cpp @@ -863,7 +863,7 @@ void fire_rail(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int dam if (binary_positional_search(org, start, args.tr.endpos, gi.inPHS, 3)) { gi.WriteByte(svc_temp_entity); - gi.WriteByte(g_instagib->integer ? TE_RAILTRAIL2 : TE_RAILTRAIL); + gi.WriteByte((deathmatch->integer && g_instagib->integer) ? TE_RAILTRAIL2 : TE_RAILTRAIL); gi.WritePosition(start); gi.WritePosition(args.tr.endpos); gi.unicast(player, false, unicast_key); diff --git a/rerelease/game.h b/rerelease/game.h index f4396fbe..c3b3a402 100644 --- a/rerelease/game.h +++ b/rerelease/game.h @@ -110,7 +110,7 @@ constexpr bit_t bit_v = 1ull << n; // game.h -- game dll information visible to server // PARIL_NEW_API - value likely not used by any other Q2-esque engine in the wild -constexpr int32_t GAME_API_VERSION = 2022; +constexpr int32_t GAME_API_VERSION = 2023; constexpr int32_t CGAME_API_VERSION = 2022; // forward declarations @@ -238,6 +238,7 @@ enum contents_t : uint32_t // remaining contents are non-visible, and don't eat brushes + CONTENTS_NO_WATERJUMP = bit_v<13>, // [Paril-KEX] this brush cannot be waterjumped out of CONTENTS_PROJECTILECLIP = bit_v<14>, // [Paril-KEX] projectiles will collide with this CONTENTS_AREAPORTAL = bit_v<15>, @@ -504,6 +505,7 @@ struct pmove_t gvec4_t screen_blend; refdef_flags_t rdflags; // merged with rdflags from server bool jump_sound; // play jump sound + bool step_clip; // we clipped on top of an object from below float impact_delta; // impact delta, for falling damage }; @@ -1740,8 +1742,6 @@ enum sv_ent_flags_t : uint64_t { }; MAKE_ENUM_BITFLAGS( sv_ent_flags_t ); -#include - static constexpr int Max_Armor_Types = 3; struct armorInfo_t { @@ -1780,7 +1780,6 @@ struct sv_entity_t { char netname[ MAX_NETNAME ]; int32_t inventory[ MAX_ITEMS ] = { 0 }; armorInfo_t armor_info[ Max_Armor_Types ]; - std::bitset pickedup_list; }; #ifndef GAME_INCLUDE @@ -2005,9 +2004,10 @@ struct game_import_t void (*Draw_Bounds)(gvec3_cref_t mins, gvec3_cref_t maxs, const rgba_t &color, const float lifeTime, const bool depthTest); void (*Draw_Sphere)(gvec3_cref_t origin, const float radius, const rgba_t &color, const float lifeTime, const bool depthTest); void (*Draw_OrientedWorldText)(gvec3_cref_t origin, const char * text, const rgba_t &color, const float size, const float lifeTime, const bool depthTest); - void (*Draw_StaticWorldText )(gvec3_cref_t origin, gvec3_cref_t angles, const char * text, const rgba_t & color, const float size, const float lifeTime, const bool depthTest); + void (*Draw_StaticWorldText)(gvec3_cref_t origin, gvec3_cref_t angles, const char * text, const rgba_t & color, const float size, const float lifeTime, const bool depthTest); void (*Draw_Cylinder)(gvec3_cref_t origin, const float halfHeight, const float radius, const rgba_t &color, const float lifeTime, const bool depthTest); void (*Draw_Ray)(gvec3_cref_t origin, gvec3_cref_t direction, const float length, const float size, const rgba_t &color, const float lifeTime, const bool depthTest); + void (*Draw_Arrow)(gvec3_cref_t start, gvec3_cref_t end, const float size, const rgba_t & lineColor, const rgba_t & arrowColor, const float lifeTime, const bool depthTest); // scoreboard void (*ReportMatchDetails_Multicast)(bool is_end); @@ -2139,6 +2139,8 @@ struct game_export_t void (*Bot_TriggerEdict)(edict_t * botEdict, edict_t * edict); void (*Bot_UseItem)(edict_t * botEdict, const int32_t itemID); int32_t (*Bot_GetItemID)(const char * classname); + void (*Edict_ForceLookAtPoint)(edict_t * edict, gvec3_cref_t point); + bool (*Bot_PickedUpItem )(edict_t * botEdict, edict_t * itemEdict); // [KEX]: Checks entity visibility instancing bool (*Entity_IsVisibleToPlayer)(edict_t* ent, edict_t* player); @@ -2167,7 +2169,7 @@ struct cg_server_data_t std::array inventory; }; -constexpr int32_t PROTOCOL_VERSION_3XX = 34; +constexpr int32_t PROTOCOL_VERSION_3XX = 34; constexpr int32_t PROTOCOL_VERSION_DEMOS = 2022; constexpr int32_t PROTOCOL_VERSION = 2023; @@ -2260,7 +2262,7 @@ struct cgame_export_t int apiversion; // the init/shutdown functions will be called between levels/connections - // (cgame does not run in menus) + // and when the client initially loads. void (*Init)(); void (*Shutdown)(); diff --git a/rerelease/m_arachnid.cpp b/rerelease/m_arachnid.cpp index 3c5a0627..4adb7bb7 100644 --- a/rerelease/m_arachnid.cpp +++ b/rerelease/m_arachnid.cpp @@ -12,9 +12,9 @@ TANK #include "m_arachnid.h" #include "m_flash.h" -static int sound_pain; -static int sound_death; -static int sound_sight; +static cached_soundindex sound_pain; +static cached_soundindex sound_death; +static cached_soundindex sound_sight; MONSTERINFO_SIGHT(arachnid_sight) (edict_t *self, edict_t *other) -> void { @@ -51,7 +51,7 @@ MONSTERINFO_STAND(arachnid_stand) (edict_t *self) -> void // walk // -static int sound_step; +static cached_soundindex sound_step; void arachnid_footstep(edict_t *self) { @@ -148,7 +148,7 @@ PAIN(arachnid_pain) (edict_t *self, edict_t *other, float kick, int damage, cons M_SetAnimation(self, &arachnid_move_pain2); } -static int sound_charge; +static cached_soundindex sound_charge; void arachnid_charge_rail(edict_t *self) { @@ -229,7 +229,7 @@ mframe_t arachnid_frames_attack_up1[] = { }; MMOVE_T(arachnid_attack_up1) = { FRAME_rails_up1, FRAME_rails_up16, arachnid_frames_attack_up1, arachnid_run }; -static int sound_melee, sound_melee_hit; +static cached_soundindex sound_melee, sound_melee_hit; void arachnid_melee_charge(edict_t *self) { @@ -348,13 +348,13 @@ void SP_monster_arachnid(edict_t *self) return; } - sound_step = gi.soundindex("insane/insane11.wav"); - sound_charge = gi.soundindex("gladiator/railgun.wav"); - sound_melee = gi.soundindex("gladiator/melee3.wav"); - sound_melee_hit = gi.soundindex("gladiator/melee2.wav"); - sound_pain = gi.soundindex("arachnid/pain.wav"); - sound_death = gi.soundindex("arachnid/death.wav"); - sound_sight = gi.soundindex("arachnid/sight.wav"); + sound_step.assign("insane/insane11.wav"); + sound_charge.assign("gladiator/railgun.wav"); + sound_melee.assign("gladiator/melee3.wav"); + sound_melee_hit.assign("gladiator/melee2.wav"); + sound_pain.assign("arachnid/pain.wav"); + sound_death.assign("arachnid/death.wav"); + sound_sight.assign("arachnid/sight.wav"); self->s.modelindex = gi.modelindex("models/monsters/arachnid/tris.md2"); self->mins = { -48, -48, -20 }; diff --git a/rerelease/m_berserk.cpp b/rerelease/m_berserk.cpp index 1ce0a98f..1813b0a8 100644 --- a/rerelease/m_berserk.cpp +++ b/rerelease/m_berserk.cpp @@ -13,15 +13,16 @@ BERSERK constexpr spawnflags_t SPAWNFLAG_BERSERK_NOJUMPING = 8_spawnflag; -static int sound_pain; -static int sound_die; -static int sound_idle; -static int sound_idle2; -static int sound_punch; -static int sound_sight; -static int sound_search; -static int sound_thud; -static int sound_jump; +static cached_soundindex sound_pain; +static cached_soundindex sound_die; +static cached_soundindex sound_idle; +static cached_soundindex sound_idle2; +static cached_soundindex sound_punch; +static cached_soundindex sound_sight; +static cached_soundindex sound_search; +static cached_soundindex sound_thud; +static cached_soundindex sound_explod; +static cached_soundindex sound_jump; MONSTERINFO_SIGHT(berserk_sight) (edict_t *self, edict_t *other) -> void { @@ -215,7 +216,7 @@ void T_SlamRadiusDamage(vec3_t point, edict_t *inflictor, edict_t *attacker, flo vec3_t v; vec3_t dir; - while ((ent = findradius(ent, inflictor->s.origin, radius)) != nullptr) + while ((ent = findradius(ent, inflictor->s.origin, radius * 2.f)) != nullptr) { if (ent == ignore) continue; @@ -223,18 +224,29 @@ void T_SlamRadiusDamage(vec3_t point, edict_t *inflictor, edict_t *attacker, flo continue; if (!CanDamage(ent, inflictor)) continue; + // don't hit players in mid air + if (ent->client && !ent->groundentity) + continue; v = closest_point_to_box(point, ent->s.origin + ent->mins, ent->s.origin + ent->maxs) - point; - points = damage - 0.5f * v.length(); - if (ent == attacker) - points = points * 0.5f; - points = max(1.f, points); + + // calculate contribution amount + float amount = min(1.f, 1.f - (v.length() / radius)); + + // too far away + if (amount <= 0.f) + continue; + + amount *= amount; + + // damage & kick are exponentially scaled + points = max(1.f, damage * amount); dir = (ent->s.origin - point).normalized(); // keep the point at their feet so they always get knocked up point[2] = ent->absmin[2]; - T_Damage(ent, inflictor, attacker, dir, point, dir, (int) points, (int) kick, + T_Damage(ent, inflictor, attacker, dir, point, dir, (int) points, (int) (kick * amount), DAMAGE_RADIUS, mod); if (ent->client) @@ -245,6 +257,7 @@ void T_SlamRadiusDamage(vec3_t point, edict_t *inflictor, edict_t *attacker, flo static void berserk_attack_slam(edict_t *self) { gi.sound(self, CHAN_WEAPON, sound_thud, 1, ATTN_NORM, 0); + gi.sound(self, CHAN_AUTO, sound_explod, 0.75f, ATTN_NORM, 0); gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_BERSERK_SLAM); vec3_t f, r, start; @@ -258,7 +271,7 @@ static void berserk_attack_slam(edict_t *self) self->velocity = {}; self->flags |= FL_KILL_VELOCITY; - T_SlamRadiusDamage(tr.endpos, self, self, 35, 150.f, self, 275, MOD_UNKNOWN); + T_SlamRadiusDamage(tr.endpos, self, self, 8, 300.f, self, 165, MOD_UNKNOWN); } TOUCH(berserk_jump_touch) (edict_t *self, edict_t *other, const trace_t &tr, bool other_touching_self) -> void @@ -309,7 +322,6 @@ void berserk_jump_takeoff(edict_t *self) self->monsterinfo.aiflags |= AI_DUCKED; self->monsterinfo.attack_finished = level.time + 3_sec; self->touch = berserk_jump_touch; - gi.sound(self, CHAN_WEAPON, sound_jump, 1, ATTN_NORM, 0); berserk_high_gravity(self); } @@ -332,15 +344,15 @@ void berserk_check_landing(edict_t *self) } if (level.time > self->monsterinfo.attack_finished) - self->monsterinfo.nextframe = FRAME_slam2; + self->monsterinfo.nextframe = FRAME_slam3; else self->monsterinfo.nextframe = FRAME_slam5; } mframe_t berserk_frames_attack_strike[] = { { ai_charge }, - { ai_charge, 0, berserk_jump_takeoff }, - { ai_move, 0, berserk_high_gravity }, + { ai_charge }, + { ai_move, 0, berserk_jump_takeoff }, { ai_move, 0, berserk_high_gravity }, { ai_move, 0, berserk_check_landing }, { ai_move, 0, monster_footstep }, @@ -440,6 +452,7 @@ MONSTERINFO_ATTACK(berserk_attack) (edict_t *self) -> void { M_SetAnimation(self, &berserk_move_attack_strike); // don't do this for a while, otherwise we just keep doing it + gi.sound(self, CHAN_WEAPON, sound_jump, 1, ATTN_NORM, 0); self->timestamp = level.time + 5_sec; } else if (self->monsterinfo.active_move == &berserk_move_run1 && (range_to(self, self->enemy) <= RANGE_NEAR)) @@ -763,15 +776,16 @@ void SP_monster_berserk(edict_t *self) } // pre-caches - sound_pain = gi.soundindex("berserk/berpain2.wav"); - sound_die = gi.soundindex("berserk/berdeth2.wav"); - sound_idle = gi.soundindex("berserk/beridle1.wav"); - sound_idle2 = gi.soundindex("berserk/idle.wav"); - sound_punch = gi.soundindex("berserk/attack.wav"); - sound_search = gi.soundindex("berserk/bersrch1.wav"); - sound_sight = gi.soundindex("berserk/sight.wav"); - sound_thud = gi.soundindex("mutant/thud1.wav"); - sound_jump = gi.soundindex("berserk/jump.wav"); + sound_pain.assign("berserk/berpain2.wav"); + sound_die.assign("berserk/berdeth2.wav"); + sound_idle.assign("berserk/beridle1.wav"); + sound_idle2.assign("berserk/idle.wav"); + sound_punch.assign("berserk/attack.wav"); + sound_search.assign("berserk/bersrch1.wav"); + sound_sight.assign("berserk/sight.wav"); + sound_thud.assign("mutant/thud1.wav"); + sound_explod.assign("world/explod2.wav"); + sound_jump.assign("berserk/jump.wav"); self->s.modelindex = gi.modelindex("models/monsters/berserk/tris.md2"); diff --git a/rerelease/m_boss2.cpp b/rerelease/m_boss2.cpp index 4c7ecee0..39b53ce6 100644 --- a/rerelease/m_boss2.cpp +++ b/rerelease/m_boss2.cpp @@ -17,11 +17,11 @@ constexpr spawnflags_t SPAWNFLAG_BOSS2_N64 = 8_spawnflag; bool infront(edict_t *self, edict_t *other); -static int sound_pain1; -static int sound_pain2; -static int sound_pain3; -static int sound_death; -static int sound_search1; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_pain3; +static cached_soundindex sound_death; +static cached_soundindex sound_search1; MONSTERINFO_SEARCH(boss2_search) (edict_t *self) -> void { @@ -607,93 +607,10 @@ DIE(boss2_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage M_SetAnimation(self, &boss2_move_death); } +// [Paril-KEX] use generic function MONSTERINFO_CHECKATTACK(Boss2_CheckAttack) (edict_t *self) -> bool { - vec3_t spot1, spot2; - vec3_t temp; - float chance; - trace_t tr; - float enemy_yaw; - - if (self->enemy->health > 0) - { - // see if any entities are in the way of the shot - spot1 = self->s.origin; - spot1[2] += self->viewheight; - spot2 = self->enemy->s.origin; - spot2[2] += self->enemy->viewheight; - - tr = gi.traceline(spot1, spot2, self, CONTENTS_SOLID | CONTENTS_PLAYER | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA); - - // do we have a clear shot? - if (tr.ent != self->enemy && !(tr.ent->svflags & SVF_PLAYER)) - { - // PGM - we want them to go ahead and shoot at info_notnulls if they can. - if (self->enemy->solid != SOLID_NOT || tr.fraction < 1.0f) // PGM - return false; - } - } - - float enemy_range = range_to(self, self->enemy); - temp = self->enemy->s.origin - self->s.origin; - enemy_yaw = vectoyaw(temp); - - self->ideal_yaw = enemy_yaw; - - // melee attack - if (enemy_range <= RANGE_MELEE) - { - if (self->monsterinfo.melee) - self->monsterinfo.attack_state = AS_MELEE; - else - self->monsterinfo.attack_state = AS_MISSILE; - return true; - } - - // missile attack - if (!self->monsterinfo.attack) - return false; - - if (level.time < self->monsterinfo.attack_finished) - return false; - - if (enemy_range > RANGE_MID) - return false; - - if (self->monsterinfo.aiflags & AI_STAND_GROUND) - { - chance = 0.4f; - } - else if (enemy_range <= RANGE_MELEE) - { - chance = 0.8f; - } - else if (enemy_range <= RANGE_NEAR) - { - chance = 0.8f; - } - else - { - chance = 0.8f; - } - - // PGM - go ahead and shoot every time if it's a info_notnull - if ((frandom() < chance) || (self->enemy->solid == SOLID_NOT)) - { - self->monsterinfo.attack_state = AS_MISSILE; - self->monsterinfo.attack_finished = level.time + random_time(2_sec); - return true; - } - - if (self->flags & FL_FLY) - { - if (frandom() < 0.3f) - self->monsterinfo.attack_state = AS_SLIDING; - else - self->monsterinfo.attack_state = AS_STRAIGHT; - } - - return false; + return M_CheckAttack_Base(self, 0.4f, 0.8f, 0.8f, 0.8f, 0.f, 0.f); } /*QUAKED monster_boss2 (1 .5 0) (-56 -56 0) (56 56 80) Ambush Trigger_Spawn Sight Hyperblaster @@ -705,11 +622,11 @@ void SP_monster_boss2(edict_t *self) return; } - sound_pain1 = gi.soundindex("bosshovr/bhvpain1.wav"); - sound_pain2 = gi.soundindex("bosshovr/bhvpain2.wav"); - sound_pain3 = gi.soundindex("bosshovr/bhvpain3.wav"); - sound_death = gi.soundindex("bosshovr/bhvdeth1.wav"); - sound_search1 = gi.soundindex("bosshovr/bhvunqv1.wav"); + sound_pain1.assign("bosshovr/bhvpain1.wav"); + sound_pain2.assign("bosshovr/bhvpain2.wav"); + sound_pain3.assign("bosshovr/bhvpain3.wav"); + sound_death.assign("bosshovr/bhvdeth1.wav"); + sound_search1.assign("bosshovr/bhvunqv1.wav"); gi.soundindex("tank/rocket.wav"); diff --git a/rerelease/m_boss31.cpp b/rerelease/m_boss31.cpp index b164afa4..eba0d830 100644 --- a/rerelease/m_boss31.cpp +++ b/rerelease/m_boss31.cpp @@ -14,20 +14,20 @@ jorg void SP_monster_makron(edict_t *self); -static int sound_pain1; -static int sound_pain2; -static int sound_pain3; -static int sound_idle; -static int sound_death; -static int sound_search1; -static int sound_search2; -static int sound_search3; -static int sound_attack1, sound_attack1_loop, sound_attack1_end; -static int sound_attack2, sound_bfg_fire; -static int sound_firegun; -static int sound_step_left; -static int sound_step_right; -static int sound_death_hit; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_pain3; +static cached_soundindex sound_idle; +static cached_soundindex sound_death; +static cached_soundindex sound_search1; +static cached_soundindex sound_search2; +static cached_soundindex sound_search3; +static cached_soundindex sound_attack1, sound_attack1_loop, sound_attack1_end; +static cached_soundindex sound_attack2, sound_bfg_fire; +static cached_soundindex sound_firegun; +static cached_soundindex sound_step_left; +static cached_soundindex sound_step_right; +static cached_soundindex sound_death_hit; void MakronToss(edict_t *self); @@ -559,88 +559,10 @@ DIE(jorg_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, M_SetAnimation(self, &jorg_move_death); } +// [Paril-KEX] use generic function MONSTERINFO_CHECKATTACK(Jorg_CheckAttack) (edict_t *self) -> bool { - vec3_t spot1, spot2; - vec3_t temp; - float chance; - trace_t tr; - float enemy_yaw; - - if (self->enemy->health > 0) - { - // see if any entities are in the way of the shot - spot1 = self->s.origin; - spot1[2] += self->viewheight; - spot2 = self->enemy->s.origin; - spot2[2] += self->enemy->viewheight; - - tr = gi.traceline(spot1, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_PLAYER | CONTENTS_SLIME | CONTENTS_LAVA); - - // do we have a clear shot? - if (tr.ent != self->enemy && !(tr.ent->svflags & SVF_PLAYER)) - return false; - } - - float enemy_range = range_to(self, self->enemy); - temp = self->enemy->s.origin - self->s.origin; - enemy_yaw = vectoyaw(temp); - - self->ideal_yaw = enemy_yaw; - - // melee attack - if (enemy_range <= RANGE_MELEE) - { - if (self->monsterinfo.melee) - self->monsterinfo.attack_state = AS_MELEE; - else - self->monsterinfo.attack_state = AS_MISSILE; - return true; - } - - // missile attack - if (!self->monsterinfo.attack) - return false; - - if (level.time < self->monsterinfo.attack_finished) - return false; - - if (enemy_range > RANGE_MID) - return false; - - if (self->monsterinfo.aiflags & AI_STAND_GROUND) - { - chance = 0.4f; - } - else if (enemy_range <= RANGE_MELEE) - { - chance = 0.8f; - } - else if (enemy_range <= RANGE_NEAR) - { - chance = 0.4f; - } - else - { - chance = 0.2f; - } - - if (frandom() < chance) - { - self->monsterinfo.attack_state = AS_MISSILE; - self->monsterinfo.attack_finished = level.time + random_time(2_sec); - return true; - } - - if (self->flags & FL_FLY) - { - if (frandom() < 0.3f) - self->monsterinfo.attack_state = AS_SLIDING; - else - self->monsterinfo.attack_state = AS_STRAIGHT; - } - - return false; + return M_CheckAttack_Base(self, 0.4f, 0.8f, 0.4f, 0.2f, 0.0f, 0.f); } void MakronPrecache(); @@ -654,23 +576,23 @@ void SP_monster_jorg(edict_t *self) return; } - sound_pain1 = gi.soundindex("boss3/bs3pain1.wav"); - sound_pain2 = gi.soundindex("boss3/bs3pain2.wav"); - sound_pain3 = gi.soundindex("boss3/bs3pain3.wav"); - sound_death = gi.soundindex("boss3/bs3deth1.wav"); - sound_attack1 = gi.soundindex("boss3/bs3atck1.wav"); - sound_attack1_loop = gi.soundindex("boss3/bs3atck1_loop.wav"); - sound_attack1_end = gi.soundindex("boss3/bs3atck1_end.wav"); - sound_attack2 = gi.soundindex("boss3/bs3atck2.wav"); - sound_search1 = gi.soundindex("boss3/bs3srch1.wav"); - sound_search2 = gi.soundindex("boss3/bs3srch2.wav"); - sound_search3 = gi.soundindex("boss3/bs3srch3.wav"); - sound_idle = gi.soundindex("boss3/bs3idle1.wav"); - sound_step_left = gi.soundindex("boss3/step1.wav"); - sound_step_right = gi.soundindex("boss3/step2.wav"); - sound_firegun = gi.soundindex("boss3/xfire.wav"); - sound_death_hit = gi.soundindex("boss3/d_hit.wav"); - sound_bfg_fire = gi.soundindex("makron/bfg_fire.wav"); + sound_pain1.assign("boss3/bs3pain1.wav"); + sound_pain2.assign("boss3/bs3pain2.wav"); + sound_pain3.assign("boss3/bs3pain3.wav"); + sound_death.assign("boss3/bs3deth1.wav"); + sound_attack1.assign("boss3/bs3atck1.wav"); + sound_attack1_loop.assign("boss3/bs3atck1_loop.wav"); + sound_attack1_end.assign("boss3/bs3atck1_end.wav"); + sound_attack2.assign("boss3/bs3atck2.wav"); + sound_search1.assign("boss3/bs3srch1.wav"); + sound_search2.assign("boss3/bs3srch2.wav"); + sound_search3.assign("boss3/bs3srch3.wav"); + sound_idle.assign("boss3/bs3idle1.wav"); + sound_step_left.assign("boss3/step1.wav"); + sound_step_right.assign("boss3/step2.wav"); + sound_firegun.assign("boss3/xfire.wav"); + sound_death_hit.assign("boss3/d_hit.wav"); + sound_bfg_fire.assign("makron/bfg_fire.wav"); MakronPrecache(); diff --git a/rerelease/m_boss32.cpp b/rerelease/m_boss32.cpp index 976224ab..1e5020af 100644 --- a/rerelease/m_boss32.cpp +++ b/rerelease/m_boss32.cpp @@ -20,20 +20,20 @@ void makron_step_right(edict_t *self); void makronBFG(edict_t *self); void makron_dead(edict_t *self); -static int sound_pain4; -static int sound_pain5; -static int sound_pain6; -static int sound_death; -static int sound_step_left; -static int sound_step_right; -static int sound_attack_bfg; -static int sound_brainsplorch; -static int sound_prerailgun; -static int sound_popup; -static int sound_taunt1; -static int sound_taunt2; -static int sound_taunt3; -static int sound_hit; +static cached_soundindex sound_pain4; +static cached_soundindex sound_pain5; +static cached_soundindex sound_pain6; +static cached_soundindex sound_death; +static cached_soundindex sound_step_left; +static cached_soundindex sound_step_right; +static cached_soundindex sound_attack_bfg; +static cached_soundindex sound_brainsplorch; +static cached_soundindex sound_prerailgun; +static cached_soundindex sound_popup; +static cached_soundindex sound_taunt1; +static cached_soundindex sound_taunt2; +static cached_soundindex sound_taunt3; +static cached_soundindex sound_hit; void makron_taunt(edict_t *self) { @@ -692,88 +692,10 @@ DIE(makron_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damag self->maxs = { 60, 60, 48 }; } +// [Paril-KEX] use generic function MONSTERINFO_CHECKATTACK(Makron_CheckAttack) (edict_t *self) -> bool { - vec3_t spot1, spot2; - vec3_t temp; - float chance; - trace_t tr; - float enemy_yaw; - - if (self->enemy->health > 0) - { - // see if any entities are in the way of the shot - spot1 = self->s.origin; - spot1[2] += self->viewheight; - spot2 = self->enemy->s.origin; - spot2[2] += self->enemy->viewheight; - - tr = gi.traceline(spot1, spot2, self, CONTENTS_SOLID | CONTENTS_PLAYER | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA); - - // do we have a clear shot? - if (tr.ent != self->enemy && !(tr.ent->svflags & SVF_PLAYER)) - return false; - } - - float enemy_range = range_to(self, self->enemy); - temp = self->enemy->s.origin - self->s.origin; - enemy_yaw = vectoyaw(temp); - - self->ideal_yaw = enemy_yaw; - - // melee attack - if (enemy_range <= RANGE_MELEE) - { - if (self->monsterinfo.melee) - self->monsterinfo.attack_state = AS_MELEE; - else - self->monsterinfo.attack_state = AS_MISSILE; - return true; - } - - // missile attack - if (!self->monsterinfo.attack) - return false; - - if (level.time < self->monsterinfo.attack_finished) - return false; - - if (enemy_range > RANGE_MID) - return false; - - if (self->monsterinfo.aiflags & AI_STAND_GROUND) - { - chance = 0.4f; - } - else if (enemy_range <= RANGE_MELEE) - { - chance = 0.8f; - } - else if (enemy_range <= RANGE_NEAR) - { - chance = 0.4f; - } - else - { - chance = 0.2f; - } - - if (frandom() < chance) - { - self->monsterinfo.attack_state = AS_MISSILE; - self->monsterinfo.attack_finished = level.time + random_time(2_sec); - return true; - } - - if (self->flags & FL_FLY) - { - if (frandom() < 0.3f) - self->monsterinfo.attack_state = AS_SLIDING; - else - self->monsterinfo.attack_state = AS_STRAIGHT; - } - - return false; + return M_CheckAttack_Base(self, 0.4f, 0.8f, 0.4f, 0.2f, 0.0f, 0.f); } // @@ -782,20 +704,20 @@ MONSTERINFO_CHECKATTACK(Makron_CheckAttack) (edict_t *self) -> bool void MakronPrecache() { - sound_pain4 = gi.soundindex("makron/pain3.wav"); - sound_pain5 = gi.soundindex("makron/pain2.wav"); - sound_pain6 = gi.soundindex("makron/pain1.wav"); - sound_death = gi.soundindex("makron/death.wav"); - sound_step_left = gi.soundindex("makron/step1.wav"); - sound_step_right = gi.soundindex("makron/step2.wav"); - sound_attack_bfg = gi.soundindex("makron/bfg_fire.wav"); - sound_brainsplorch = gi.soundindex("makron/brain1.wav"); - sound_prerailgun = gi.soundindex("makron/rail_up.wav"); - sound_popup = gi.soundindex("makron/popup.wav"); - sound_taunt1 = gi.soundindex("makron/voice4.wav"); - sound_taunt2 = gi.soundindex("makron/voice3.wav"); - sound_taunt3 = gi.soundindex("makron/voice.wav"); - sound_hit = gi.soundindex("makron/bhit.wav"); + sound_pain4.assign("makron/pain3.wav"); + sound_pain5.assign("makron/pain2.wav"); + sound_pain6.assign("makron/pain1.wav"); + sound_death.assign("makron/death.wav"); + sound_step_left.assign("makron/step1.wav"); + sound_step_right.assign("makron/step2.wav"); + sound_attack_bfg.assign("makron/bfg_fire.wav"); + sound_brainsplorch.assign("makron/brain1.wav"); + sound_prerailgun.assign("makron/rail_up.wav"); + sound_popup.assign("makron/popup.wav"); + sound_taunt1.assign("makron/voice4.wav"); + sound_taunt2.assign("makron/voice3.wav"); + sound_taunt3.assign("makron/voice.wav"); + sound_hit.assign("makron/bhit.wav"); gi.modelindex("models/monsters/boss3/rider/tris.md2"); } diff --git a/rerelease/m_brain.cpp b/rerelease/m_brain.cpp index 364bd30d..0d779ef6 100644 --- a/rerelease/m_brain.cpp +++ b/rerelease/m_brain.cpp @@ -11,20 +11,20 @@ brain #include "g_local.h" #include "m_brain.h" -static int sound_chest_open; -static int sound_tentacles_extend; -static int sound_tentacles_retract; -static int sound_death; -static int sound_idle1; -static int sound_idle2; -static int sound_idle3; -static int sound_pain1; -static int sound_pain2; -static int sound_sight; -static int sound_search; -static int sound_melee1; -static int sound_melee2; -static int sound_melee3; +static cached_soundindex sound_chest_open; +static cached_soundindex sound_tentacles_extend; +static cached_soundindex sound_tentacles_retract; +static cached_soundindex sound_death; +static cached_soundindex sound_idle1; +static cached_soundindex sound_idle2; +static cached_soundindex sound_idle3; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_sight; +static cached_soundindex sound_search; +static cached_soundindex sound_melee1; +static cached_soundindex sound_melee2; +static cached_soundindex sound_melee3; MONSTERINFO_SIGHT(brain_sight) (edict_t *self, edict_t *other) -> void { @@ -321,11 +321,9 @@ mframe_t brain_frames_attack1[] = { }; MMOVE_T(brain_move_attack1) = { FRAME_attak101, FRAME_attak118, brain_frames_attack1, brain_run }; -constexpr spawnflags_t SPAWNFLAG_BRAIN_TENTACLES_HIT = 65536_spawnflag; - void brain_chest_open(edict_t *self) { - self->spawnflags &= ~SPAWNFLAG_BRAIN_TENTACLES_HIT; + self->count = 0; self->monsterinfo.power_armor_type = IT_NULL; gi.sound(self, CHAN_BODY, sound_chest_open, 1, ATTN_NORM, 0); } @@ -334,7 +332,7 @@ void brain_tentacle_attack(edict_t *self) { vec3_t aim = { MELEE_DISTANCE, 0, 8 }; if (fire_hit(self, aim, irandom(10, 15), -600)) - self->spawnflags |= SPAWNFLAG_BRAIN_TENTACLES_HIT; + self->count = 1; else self->monsterinfo.melee_debounce_time = level.time + 3_sec; gi.sound(self, CHAN_WEAPON, sound_tentacles_retract, 1, ATTN_NORM, 0); @@ -343,9 +341,9 @@ void brain_tentacle_attack(edict_t *self) void brain_chest_closed(edict_t *self) { self->monsterinfo.power_armor_type = IT_ITEM_POWER_SCREEN; - if (self->spawnflags.has(SPAWNFLAG_BRAIN_TENTACLES_HIT)) + if (self->count) { - self->spawnflags &= ~SPAWNFLAG_BRAIN_TENTACLES_HIT; + self->count = 0; M_SetAnimation(self, &brain_move_attack1); } } @@ -731,20 +729,20 @@ void SP_monster_brain(edict_t *self) return; } - sound_chest_open = gi.soundindex("brain/brnatck1.wav"); - sound_tentacles_extend = gi.soundindex("brain/brnatck2.wav"); - sound_tentacles_retract = gi.soundindex("brain/brnatck3.wav"); - sound_death = gi.soundindex("brain/brndeth1.wav"); - sound_idle1 = gi.soundindex("brain/brnidle1.wav"); - sound_idle2 = gi.soundindex("brain/brnidle2.wav"); - sound_idle3 = gi.soundindex("brain/brnlens1.wav"); - sound_pain1 = gi.soundindex("brain/brnpain1.wav"); - sound_pain2 = gi.soundindex("brain/brnpain2.wav"); - sound_sight = gi.soundindex("brain/brnsght1.wav"); - sound_search = gi.soundindex("brain/brnsrch1.wav"); - sound_melee1 = gi.soundindex("brain/melee1.wav"); - sound_melee2 = gi.soundindex("brain/melee2.wav"); - sound_melee3 = gi.soundindex("brain/melee3.wav"); + sound_chest_open.assign("brain/brnatck1.wav"); + sound_tentacles_extend.assign("brain/brnatck2.wav"); + sound_tentacles_retract.assign("brain/brnatck3.wav"); + sound_death.assign("brain/brndeth1.wav"); + sound_idle1.assign("brain/brnidle1.wav"); + sound_idle2.assign("brain/brnidle2.wav"); + sound_idle3.assign("brain/brnlens1.wav"); + sound_pain1.assign("brain/brnpain1.wav"); + sound_pain2.assign("brain/brnpain2.wav"); + sound_sight.assign("brain/brnsght1.wav"); + sound_search.assign("brain/brnsrch1.wav"); + sound_melee1.assign("brain/melee1.wav"); + sound_melee2.assign("brain/melee2.wav"); + sound_melee3.assign("brain/melee3.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; diff --git a/rerelease/m_chick.cpp b/rerelease/m_chick.cpp index 9a62e447..835e3c03 100644 --- a/rerelease/m_chick.cpp +++ b/rerelease/m_chick.cpp @@ -18,21 +18,21 @@ void chick_reslash(edict_t *self); void chick_rerocket(edict_t *self); void chick_attack1(edict_t *self); -static int sound_missile_prelaunch; -static int sound_missile_launch; -static int sound_melee_swing; -static int sound_melee_hit; -static int sound_missile_reload; -static int sound_death1; -static int sound_death2; -static int sound_fall_down; -static int sound_idle1; -static int sound_idle2; -static int sound_pain1; -static int sound_pain2; -static int sound_pain3; -static int sound_sight; -static int sound_search; +static cached_soundindex sound_missile_prelaunch; +static cached_soundindex sound_missile_launch; +static cached_soundindex sound_melee_swing; +static cached_soundindex sound_melee_hit; +static cached_soundindex sound_missile_reload; +static cached_soundindex sound_death1; +static cached_soundindex sound_death2; +static cached_soundindex sound_fall_down; +static cached_soundindex sound_idle1; +static cached_soundindex sound_idle2; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_pain3; +static cached_soundindex sound_sight; +static cached_soundindex sound_search; void ChickMoan(edict_t *self) { @@ -795,21 +795,21 @@ void SP_monster_chick(edict_t *self) return; } - sound_missile_prelaunch = gi.soundindex("chick/chkatck1.wav"); - sound_missile_launch = gi.soundindex("chick/chkatck2.wav"); - sound_melee_swing = gi.soundindex("chick/chkatck3.wav"); - sound_melee_hit = gi.soundindex("chick/chkatck4.wav"); - sound_missile_reload = gi.soundindex("chick/chkatck5.wav"); - sound_death1 = gi.soundindex("chick/chkdeth1.wav"); - sound_death2 = gi.soundindex("chick/chkdeth2.wav"); - sound_fall_down = gi.soundindex("chick/chkfall1.wav"); - sound_idle1 = gi.soundindex("chick/chkidle1.wav"); - sound_idle2 = gi.soundindex("chick/chkidle2.wav"); - sound_pain1 = gi.soundindex("chick/chkpain1.wav"); - sound_pain2 = gi.soundindex("chick/chkpain2.wav"); - sound_pain3 = gi.soundindex("chick/chkpain3.wav"); - sound_sight = gi.soundindex("chick/chksght1.wav"); - sound_search = gi.soundindex("chick/chksrch1.wav"); + sound_missile_prelaunch.assign("chick/chkatck1.wav"); + sound_missile_launch.assign("chick/chkatck2.wav"); + sound_melee_swing.assign("chick/chkatck3.wav"); + sound_melee_hit.assign("chick/chkatck4.wav"); + sound_missile_reload.assign("chick/chkatck5.wav"); + sound_death1.assign("chick/chkdeth1.wav"); + sound_death2.assign("chick/chkdeth2.wav"); + sound_fall_down.assign("chick/chkfall1.wav"); + sound_idle1.assign("chick/chkidle1.wav"); + sound_idle2.assign("chick/chkidle2.wav"); + sound_pain1.assign("chick/chkpain1.wav"); + sound_pain2.assign("chick/chkpain2.wav"); + sound_pain3.assign("chick/chkpain3.wav"); + sound_sight.assign("chick/chksght1.wav"); + sound_search.assign("chick/chksrch1.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; diff --git a/rerelease/m_flipper.cpp b/rerelease/m_flipper.cpp index d0e6ed43..16033cc6 100644 --- a/rerelease/m_flipper.cpp +++ b/rerelease/m_flipper.cpp @@ -11,14 +11,14 @@ FLIPPER #include "g_local.h" #include "m_flipper.h" -static int sound_chomp; -static int sound_attack; -static int sound_pain1; -static int sound_pain2; -static int sound_death; -static int sound_idle; -static int sound_search; -static int sound_sight; +static cached_soundindex sound_chomp; +static cached_soundindex sound_attack; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_death; +static cached_soundindex sound_idle; +static cached_soundindex sound_search; +static cached_soundindex sound_sight; mframe_t flipper_frames_stand[] = { { ai_stand } @@ -343,14 +343,14 @@ void SP_monster_flipper(edict_t *self) return; } - sound_pain1 = gi.soundindex("flipper/flppain1.wav"); - sound_pain2 = gi.soundindex("flipper/flppain2.wav"); - sound_death = gi.soundindex("flipper/flpdeth1.wav"); - sound_chomp = gi.soundindex("flipper/flpatck1.wav"); - sound_attack = gi.soundindex("flipper/flpatck2.wav"); - sound_idle = gi.soundindex("flipper/flpidle1.wav"); - sound_search = gi.soundindex("flipper/flpsrch1.wav"); - sound_sight = gi.soundindex("flipper/flpsght1.wav"); + sound_pain1.assign("flipper/flppain1.wav"); + sound_pain2.assign("flipper/flppain2.wav"); + sound_death.assign("flipper/flpdeth1.wav"); + sound_chomp.assign("flipper/flpatck1.wav"); + sound_attack.assign("flipper/flpatck2.wav"); + sound_idle.assign("flipper/flpidle1.wav"); + sound_search.assign("flipper/flpsrch1.wav"); + sound_sight.assign("flipper/flpsght1.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; diff --git a/rerelease/m_float.cpp b/rerelease/m_float.cpp index a18654a9..ceabb498 100644 --- a/rerelease/m_float.cpp +++ b/rerelease/m_float.cpp @@ -12,13 +12,13 @@ floater #include "m_float.h" #include "m_flash.h" -static int sound_attack2; -static int sound_attack3; -static int sound_death1; -static int sound_idle; -static int sound_pain1; -static int sound_pain2; -static int sound_sight; +static cached_soundindex sound_attack2; +static cached_soundindex sound_attack3; +static cached_soundindex sound_death1; +static cached_soundindex sound_idle; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_sight; MONSTERINFO_SIGHT(floater_sight) (edict_t *self, edict_t *other) -> void { @@ -658,13 +658,13 @@ void SP_monster_floater(edict_t *self) return; } - sound_attack2 = gi.soundindex("floater/fltatck2.wav"); - sound_attack3 = gi.soundindex("floater/fltatck3.wav"); - sound_death1 = gi.soundindex("floater/fltdeth1.wav"); - sound_idle = gi.soundindex("floater/fltidle1.wav"); - sound_pain1 = gi.soundindex("floater/fltpain1.wav"); - sound_pain2 = gi.soundindex("floater/fltpain2.wav"); - sound_sight = gi.soundindex("floater/fltsght1.wav"); + sound_attack2.assign("floater/fltatck2.wav"); + sound_attack3.assign("floater/fltatck3.wav"); + sound_death1.assign("floater/fltdeth1.wav"); + sound_idle.assign("floater/fltidle1.wav"); + sound_pain1.assign("floater/fltpain1.wav"); + sound_pain2.assign("floater/fltpain2.wav"); + sound_sight.assign("floater/fltsght1.wav"); gi.soundindex("floater/fltatck1.wav"); diff --git a/rerelease/m_flyer.cpp b/rerelease/m_flyer.cpp index 10edc8f0..311f5695 100644 --- a/rerelease/m_flyer.cpp +++ b/rerelease/m_flyer.cpp @@ -12,13 +12,13 @@ flyer #include "m_flyer.h" #include "m_flash.h" -static int sound_sight; -static int sound_idle; -static int sound_pain1; -static int sound_pain2; -static int sound_slash; -static int sound_sproing; -static int sound_die; +static cached_soundindex sound_sight; +static cached_soundindex sound_idle; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_slash; +static cached_soundindex sound_sproing; +static cached_soundindex sound_die; void flyer_check_melee(edict_t *self); void flyer_loop_melee(edict_t *self); @@ -704,13 +704,13 @@ void SP_monster_flyer(edict_t *self) return; } - sound_sight = gi.soundindex("flyer/flysght1.wav"); - sound_idle = gi.soundindex("flyer/flysrch1.wav"); - sound_pain1 = gi.soundindex("flyer/flypain1.wav"); - sound_pain2 = gi.soundindex("flyer/flypain2.wav"); - sound_slash = gi.soundindex("flyer/flyatck2.wav"); - sound_sproing = gi.soundindex("flyer/flyatck1.wav"); - sound_die = gi.soundindex("flyer/flydeth1.wav"); + sound_sight.assign("flyer/flysght1.wav"); + sound_idle.assign("flyer/flysrch1.wav"); + sound_pain1.assign("flyer/flypain1.wav"); + sound_pain2.assign("flyer/flypain2.wav"); + sound_slash.assign("flyer/flyatck2.wav"); + sound_sproing.assign("flyer/flyatck1.wav"); + sound_die.assign("flyer/flydeth1.wav"); gi.soundindex("flyer/flyatck3.wav"); diff --git a/rerelease/m_gladiator.cpp b/rerelease/m_gladiator.cpp index fbfca302..05689d0b 100644 --- a/rerelease/m_gladiator.cpp +++ b/rerelease/m_gladiator.cpp @@ -12,18 +12,18 @@ GLADIATOR #include "m_gladiator.h" #include "m_flash.h" -static int sound_pain1; -static int sound_pain2; -static int sound_die; -static int sound_die2; -static int sound_gun; -static int sound_gunb; -static int sound_cleaver_swing; -static int sound_cleaver_hit; -static int sound_cleaver_miss; -static int sound_idle; -static int sound_search; -static int sound_sight; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_die; +static cached_soundindex sound_die2; +static cached_soundindex sound_gun; +static cached_soundindex sound_gunb; +static cached_soundindex sound_cleaver_swing; +static cached_soundindex sound_cleaver_hit; +static cached_soundindex sound_cleaver_miss; +static cached_soundindex sound_idle; +static cached_soundindex sound_search; +static cached_soundindex sound_sight; MONSTERINFO_IDLE(gladiator_idle) (edict_t *self) -> void { @@ -397,16 +397,16 @@ void SP_monster_gladiator(edict_t *self) return; } - sound_pain1 = gi.soundindex("gladiator/pain.wav"); - sound_pain2 = gi.soundindex("gladiator/gldpain2.wav"); - sound_die = gi.soundindex("gladiator/glddeth2.wav"); - sound_die2 = gi.soundindex("gladiator/death.wav"); - sound_cleaver_swing = gi.soundindex("gladiator/melee1.wav"); - sound_cleaver_hit = gi.soundindex("gladiator/melee2.wav"); - sound_cleaver_miss = gi.soundindex("gladiator/melee3.wav"); - sound_idle = gi.soundindex("gladiator/gldidle1.wav"); - sound_search = gi.soundindex("gladiator/gldsrch1.wav"); - sound_sight = gi.soundindex("gladiator/sight.wav"); + sound_pain1.assign("gladiator/pain.wav"); + sound_pain2.assign("gladiator/gldpain2.wav"); + sound_die.assign("gladiator/glddeth2.wav"); + sound_die2.assign("gladiator/death.wav"); + sound_cleaver_swing.assign("gladiator/melee1.wav"); + sound_cleaver_hit.assign("gladiator/melee2.wav"); + sound_cleaver_miss.assign("gladiator/melee3.wav"); + sound_idle.assign("gladiator/gldidle1.wav"); + sound_search.assign("gladiator/gldsrch1.wav"); + sound_sight.assign("gladiator/sight.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; @@ -421,7 +421,7 @@ void SP_monster_gladiator(edict_t *self) // RAFAEL if (strcmp(self->classname, "monster_gladb") == 0) { - sound_gunb = gi.soundindex("weapons/plasshot.wav"); + sound_gunb.assign("weapons/plasshot.wav"); self->health = 250 * st.health_multiplier; self->mass = 350; @@ -440,7 +440,7 @@ void SP_monster_gladiator(edict_t *self) else { // RAFAEL - sound_gun = gi.soundindex("gladiator/railgun.wav"); + sound_gun.assign("gladiator/railgun.wav"); self->health = 400 * st.health_multiplier; self->mass = 400; diff --git a/rerelease/m_guardian.cpp b/rerelease/m_guardian.cpp index f186def1..e9fac5e8 100644 --- a/rerelease/m_guardian.cpp +++ b/rerelease/m_guardian.cpp @@ -81,7 +81,7 @@ MONSTERINFO_STAND(guardian_stand) (edict_t *self) -> void // walk // -static int sound_step; +static cached_soundindex sound_step; void guardian_footstep(edict_t *self) { @@ -213,8 +213,8 @@ void guardian_atk1_finish(edict_t *self) self->monsterinfo.weapon_sound = 0; } -static int sound_charge; -static int sound_spin_loop; +static cached_soundindex sound_charge; +static cached_soundindex sound_spin_loop; void guardian_atk1_charge(edict_t *self) { @@ -292,7 +292,7 @@ void guardian_atk2_out(edict_t *self) M_SetAnimation(self, &guardian_move_atk2_out); } -static int sound_laser; +static cached_soundindex sound_laser; constexpr vec3_t laser_positions[] = { { 125.0f, -70.f, 60.f }, @@ -487,10 +487,10 @@ void SP_monster_guardian(edict_t *self) return; } - sound_step = gi.soundindex("zortemp/step.wav"); - sound_charge = gi.soundindex("weapons/hyprbu1a.wav"); - sound_spin_loop = gi.soundindex("weapons/hyprbl1a.wav"); - sound_laser = gi.soundindex("weapons/laser2.wav"); + sound_step.assign("zortemp/step.wav"); + sound_charge.assign("weapons/hyprbu1a.wav"); + sound_spin_loop.assign("weapons/hyprbl1a.wav"); + sound_laser.assign("weapons/laser2.wav"); for (auto &gib : gibs) gi.modelindex(gib); diff --git a/rerelease/m_guncmdr.cpp b/rerelease/m_guncmdr.cpp index 7f97d773..321af058 100644 --- a/rerelease/m_guncmdr.cpp +++ b/rerelease/m_guncmdr.cpp @@ -14,13 +14,13 @@ GUNNER constexpr spawnflags_t SPAWNFLAG_GUNCMDR_NOJUMPING = 8_spawnflag; -static int sound_pain; -static int sound_pain2; -static int sound_death; -static int sound_idle; -static int sound_open; -static int sound_search; -static int sound_sight; +static cached_soundindex sound_pain; +static cached_soundindex sound_pain2; +static cached_soundindex sound_death; +static cached_soundindex sound_idle; +static cached_soundindex sound_open; +static cached_soundindex sound_search; +static cached_soundindex sound_sight; void guncmdr_idlesound(edict_t *self) { @@ -1326,7 +1326,7 @@ MONSTERINFO_DUCK(guncmdr_duck) (edict_t *self, gtime_t eta) -> bool if ((self->monsterinfo.active_move == &guncmdr_move_fire_chain_dodge_left) || (self->monsterinfo.active_move == &guncmdr_move_fire_chain_dodge_right) || - (self->monsterinfo.active_move == &guncmdr_move_attack_grenade_back_dodge_right) || + (self->monsterinfo.active_move == &guncmdr_move_attack_grenade_back_dodge_left) || (self->monsterinfo.active_move == &guncmdr_move_attack_grenade_back_dodge_right) || (self->monsterinfo.active_move == &guncmdr_move_attack_mortar_dodge)) { @@ -1404,13 +1404,13 @@ void SP_monster_guncmdr(edict_t *self) return; } - sound_death = gi.soundindex("guncmdr/gcdrdeath1.wav"); - sound_pain = gi.soundindex("guncmdr/gcdrpain2.wav"); - sound_pain2 = gi.soundindex("guncmdr/gcdrpain1.wav"); - sound_idle = gi.soundindex("guncmdr/gcdridle1.wav"); - sound_open = gi.soundindex("guncmdr/gcdratck1.wav"); - sound_search = gi.soundindex("guncmdr/gcdrsrch1.wav"); - sound_sight = gi.soundindex("guncmdr/sight1.wav"); + sound_death.assign("guncmdr/gcdrdeath1.wav"); + sound_pain.assign("guncmdr/gcdrpain2.wav"); + sound_pain2.assign("guncmdr/gcdrpain1.wav"); + sound_idle.assign("guncmdr/gcdridle1.wav"); + sound_open.assign("guncmdr/gcdratck1.wav"); + sound_search.assign("guncmdr/gcdrsrch1.wav"); + sound_sight.assign("guncmdr/sight1.wav"); gi.soundindex("guncmdr/gcdratck2.wav"); gi.soundindex("guncmdr/gcdratck3.wav"); diff --git a/rerelease/m_gunner.cpp b/rerelease/m_gunner.cpp index 1bdcc493..c3ad5641 100644 --- a/rerelease/m_gunner.cpp +++ b/rerelease/m_gunner.cpp @@ -12,13 +12,13 @@ GUNNER #include "m_gunner.h" #include "m_flash.h" -static int sound_pain; -static int sound_pain2; -static int sound_death; -static int sound_idle; -static int sound_open; -static int sound_search; -static int sound_sight; +static cached_soundindex sound_pain; +static cached_soundindex sound_pain2; +static cached_soundindex sound_death; +static cached_soundindex sound_idle; +static cached_soundindex sound_open; +static cached_soundindex sound_search; +static cached_soundindex sound_sight; void gunner_idlesound(edict_t *self) { @@ -858,13 +858,13 @@ void SP_monster_gunner(edict_t *self) return; } - sound_death = gi.soundindex("gunner/death1.wav"); - sound_pain = gi.soundindex("gunner/gunpain2.wav"); - sound_pain2 = gi.soundindex("gunner/gunpain1.wav"); - sound_idle = gi.soundindex("gunner/gunidle1.wav"); - sound_open = gi.soundindex("gunner/gunatck1.wav"); - sound_search = gi.soundindex("gunner/gunsrch1.wav"); - sound_sight = gi.soundindex("gunner/sight1.wav"); + sound_death.assign("gunner/death1.wav"); + sound_pain.assign("gunner/gunpain2.wav"); + sound_pain2.assign("gunner/gunpain1.wav"); + sound_idle.assign("gunner/gunidle1.wav"); + sound_open.assign("gunner/gunatck1.wav"); + sound_search.assign("gunner/gunsrch1.wav"); + sound_sight.assign("gunner/sight1.wav"); gi.soundindex("gunner/gunatck2.wav"); gi.soundindex("gunner/gunatck3.wav"); diff --git a/rerelease/m_hover.cpp b/rerelease/m_hover.cpp index 31459bc6..e2a4b6c3 100644 --- a/rerelease/m_hover.cpp +++ b/rerelease/m_hover.cpp @@ -12,23 +12,23 @@ hover #include "m_hover.h" #include "m_flash.h" -static int sound_pain1; -static int sound_pain2; -static int sound_death1; -static int sound_death2; -static int sound_sight; -static int sound_search1; -static int sound_search2; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_death1; +static cached_soundindex sound_death2; +static cached_soundindex sound_sight; +static cached_soundindex sound_search1; +static cached_soundindex sound_search2; // ROGUE // daedalus sounds -static int daed_sound_pain1; -static int daed_sound_pain2; -static int daed_sound_death1; -static int daed_sound_death2; -static int daed_sound_sight; -static int daed_sound_search1; -static int daed_sound_search2; +static cached_soundindex daed_sound_pain1; +static cached_soundindex daed_sound_pain2; +static cached_soundindex daed_sound_death1; +static cached_soundindex daed_sound_death2; +static cached_soundindex daed_sound_sight; +static cached_soundindex daed_sound_search1; +static cached_soundindex daed_sound_search2; // ROGUE MONSTERINFO_SIGHT(hover_sight) (edict_t *self, edict_t *other) -> void @@ -619,26 +619,26 @@ void SP_monster_hover(edict_t *self) self->monsterinfo.power_armor_power = 100; // PMM - daedalus sounds self->monsterinfo.engine_sound = gi.soundindex("daedalus/daedidle1.wav"); - daed_sound_pain1 = gi.soundindex("daedalus/daedpain1.wav"); - daed_sound_pain2 = gi.soundindex("daedalus/daedpain2.wav"); - daed_sound_death1 = gi.soundindex("daedalus/daeddeth1.wav"); - daed_sound_death2 = gi.soundindex("daedalus/daeddeth2.wav"); - daed_sound_sight = gi.soundindex("daedalus/daedsght1.wav"); - daed_sound_search1 = gi.soundindex("daedalus/daedsrch1.wav"); - daed_sound_search2 = gi.soundindex("daedalus/daedsrch2.wav"); + daed_sound_pain1.assign("daedalus/daedpain1.wav"); + daed_sound_pain2.assign("daedalus/daedpain2.wav"); + daed_sound_death1.assign("daedalus/daeddeth1.wav"); + daed_sound_death2.assign("daedalus/daeddeth2.wav"); + daed_sound_sight.assign("daedalus/daedsght1.wav"); + daed_sound_search1.assign("daedalus/daedsrch1.wav"); + daed_sound_search2.assign("daedalus/daedsrch2.wav"); gi.soundindex("tank/tnkatck3.wav"); // pmm } else { self->yaw_speed = 18; - sound_pain1 = gi.soundindex("hover/hovpain1.wav"); - sound_pain2 = gi.soundindex("hover/hovpain2.wav"); - sound_death1 = gi.soundindex("hover/hovdeth1.wav"); - sound_death2 = gi.soundindex("hover/hovdeth2.wav"); - sound_sight = gi.soundindex("hover/hovsght1.wav"); - sound_search1 = gi.soundindex("hover/hovsrch1.wav"); - sound_search2 = gi.soundindex("hover/hovsrch2.wav"); + sound_pain1.assign("hover/hovpain1.wav"); + sound_pain2.assign("hover/hovpain2.wav"); + sound_death1.assign("hover/hovdeth1.wav"); + sound_death2.assign("hover/hovdeth2.wav"); + sound_sight.assign("hover/hovsght1.wav"); + sound_search1.assign("hover/hovsrch1.wav"); + sound_search2.assign("hover/hovsrch2.wav"); gi.soundindex("hover/hovatck1.wav"); self->monsterinfo.engine_sound = gi.soundindex("hover/hovidle1.wav"); diff --git a/rerelease/m_infantry.cpp b/rerelease/m_infantry.cpp index 18f9f200..b832bb1d 100644 --- a/rerelease/m_infantry.cpp +++ b/rerelease/m_infantry.cpp @@ -14,18 +14,18 @@ INFANTRY void InfantryMachineGun(edict_t *self); -static int sound_pain1; -static int sound_pain2; -static int sound_die1; -static int sound_die2; - -static int sound_gunshot; -static int sound_weapon_cock; -static int sound_punch_swing; -static int sound_punch_hit; -static int sound_sight; -static int sound_search; -static int sound_idle; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_die1; +static cached_soundindex sound_die2; + +static cached_soundindex sound_gunshot; +static cached_soundindex sound_weapon_cock; +static cached_soundindex sound_punch_swing; +static cached_soundindex sound_punch_hit; +static cached_soundindex sound_sight; +static cached_soundindex sound_search; +static cached_soundindex sound_idle; // range at which we'll try to initiate a run-attack to close distance constexpr float RANGE_RUN_ATTACK = RANGE_NEAR * 0.75f; @@ -848,19 +848,19 @@ MONSTERINFO_SIDESTEP(infantry_sidestep) (edict_t *self) -> bool void InfantryPrecache() { - sound_pain1 = gi.soundindex("infantry/infpain1.wav"); - sound_pain2 = gi.soundindex("infantry/infpain2.wav"); - sound_die1 = gi.soundindex("infantry/infdeth1.wav"); - sound_die2 = gi.soundindex("infantry/infdeth2.wav"); - - sound_gunshot = gi.soundindex("infantry/infatck1.wav"); - sound_weapon_cock = gi.soundindex("infantry/infatck3.wav"); - sound_punch_swing = gi.soundindex("infantry/infatck2.wav"); - sound_punch_hit = gi.soundindex("infantry/melee2.wav"); - - sound_sight = gi.soundindex("infantry/infsght1.wav"); - sound_search = gi.soundindex("infantry/infsrch1.wav"); - sound_idle = gi.soundindex("infantry/infidle1.wav"); + sound_pain1.assign("infantry/infpain1.wav"); + sound_pain2.assign("infantry/infpain2.wav"); + sound_die1.assign("infantry/infdeth1.wav"); + sound_die2.assign("infantry/infdeth2.wav"); + + sound_gunshot.assign("infantry/infatck1.wav"); + sound_weapon_cock.assign("infantry/infatck3.wav"); + sound_punch_swing.assign("infantry/infatck2.wav"); + sound_punch_hit.assign("infantry/melee2.wav"); + + sound_sight.assign("infantry/infsght1.wav"); + sound_search.assign("infantry/infsrch1.wav"); + sound_idle.assign("infantry/infidle1.wav"); } constexpr spawnflags_t SPAWNFLAG_INFANTRY_NOJUMPING = 8_spawnflag; diff --git a/rerelease/m_insane.cpp b/rerelease/m_insane.cpp index acdcc1ab..ccce5499 100644 --- a/rerelease/m_insane.cpp +++ b/rerelease/m_insane.cpp @@ -17,10 +17,10 @@ constexpr spawnflags_t SPAWNFLAG_INSANE_STAND_GROUND = 16_spawnflag; constexpr spawnflags_t SPAWNFLAG_INSANE_ALWAYS_STAND = 32_spawnflag; constexpr spawnflags_t SPAWNFLAG_INSANE_QUIET = 64_spawnflag; -static int sound_fist; -static int sound_shake; -static int sound_moan; -static int sound_scream[8]; +static cached_soundindex sound_fist; +static cached_soundindex sound_shake; +static cached_soundindex sound_moan; +static cached_soundindex sound_scream[8]; void insane_fist(edict_t *self) { @@ -627,19 +627,19 @@ void SP_misc_insane(edict_t *self) return; } - sound_fist = gi.soundindex("insane/insane11.wav"); + sound_fist.assign("insane/insane11.wav"); if (!self->spawnflags.has(SPAWNFLAG_INSANE_QUIET)) { - sound_shake = gi.soundindex("insane/insane5.wav"); - sound_moan = gi.soundindex("insane/insane7.wav"); - sound_scream[0] = gi.soundindex("insane/insane1.wav"); - sound_scream[1] = gi.soundindex("insane/insane2.wav"); - sound_scream[2] = gi.soundindex("insane/insane3.wav"); - sound_scream[3] = gi.soundindex("insane/insane4.wav"); - sound_scream[4] = gi.soundindex("insane/insane6.wav"); - sound_scream[5] = gi.soundindex("insane/insane8.wav"); - sound_scream[6] = gi.soundindex("insane/insane9.wav"); - sound_scream[7] = gi.soundindex("insane/insane10.wav"); + sound_shake.assign("insane/insane5.wav"); + sound_moan.assign("insane/insane7.wav"); + sound_scream[0].assign("insane/insane1.wav"); + sound_scream[1].assign("insane/insane2.wav"); + sound_scream[2].assign("insane/insane3.wav"); + sound_scream[3].assign("insane/insane4.wav"); + sound_scream[4].assign("insane/insane6.wav"); + sound_scream[5].assign("insane/insane8.wav"); + sound_scream[6].assign("insane/insane9.wav"); + sound_scream[7].assign("insane/insane10.wav"); } self->movetype = MOVETYPE_STEP; diff --git a/rerelease/m_medic.cpp b/rerelease/m_medic.cpp index 85eab253..c1b88cfb 100644 --- a/rerelease/m_medic.cpp +++ b/rerelease/m_medic.cpp @@ -29,29 +29,29 @@ bool FindTarget(edict_t *self); void FoundTarget(edict_t *self); void ED_CallSpawn(edict_t *ent); -static int sound_idle1; -static int sound_pain1; -static int sound_pain2; -static int sound_die; -static int sound_sight; -static int sound_search; -static int sound_hook_launch; -static int sound_hook_hit; -static int sound_hook_heal; -static int sound_hook_retract; +static cached_soundindex sound_idle1; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_die; +static cached_soundindex sound_sight; +static cached_soundindex sound_search; +static cached_soundindex sound_hook_launch; +static cached_soundindex sound_hook_hit; +static cached_soundindex sound_hook_heal; +static cached_soundindex sound_hook_retract; // PMM - commander sounds -static int commander_sound_idle1; -static int commander_sound_pain1; -static int commander_sound_pain2; -static int commander_sound_die; -static int commander_sound_sight; -static int commander_sound_search; -static int commander_sound_hook_launch; -static int commander_sound_hook_hit; -static int commander_sound_hook_heal; -static int commander_sound_hook_retract; -static int commander_sound_spawn; +static cached_soundindex commander_sound_idle1; +static cached_soundindex commander_sound_pain1; +static cached_soundindex commander_sound_pain2; +static cached_soundindex commander_sound_die; +static cached_soundindex commander_sound_sight; +static cached_soundindex commander_sound_search; +static cached_soundindex commander_sound_hook_launch; +static cached_soundindex commander_sound_hook_hit; +static cached_soundindex commander_sound_hook_heal; +static cached_soundindex commander_sound_hook_retract; +static cached_soundindex commander_sound_spawn; constexpr const char *default_reinforcements = "monster_soldier_light 1;monster_soldier 2;monster_soldier_ss 2;monster_infantry 3;monster_gunner 4;monster_medic 5;monster_gladiator 6"; constexpr int32_t default_monster_slots_base = 3; @@ -199,30 +199,34 @@ void abortHeal(edict_t *self, bool change_frame, bool gib, bool mark) int hurt; constexpr vec3_t pain_normal = { 0, 0, 1 }; - cleanupHealTarget(self->enemy); - - // gib em! - if ((mark) && (self->enemy) && (self->enemy->inuse)) + if (self->enemy && self->enemy->inuse) { - // if the first badMedic slot is filled by a medic, skip it and use the second one - if ((self->enemy->monsterinfo.badMedic1) && (self->enemy->monsterinfo.badMedic1->inuse) && (!strncmp(self->enemy->monsterinfo.badMedic1->classname, "monster_medic", 13))) + cleanupHealTarget(self->enemy); + + // gib em! + if (mark) { - self->enemy->monsterinfo.badMedic2 = self; + // if the first badMedic slot is filled by a medic, skip it and use the second one + if ((self->enemy->monsterinfo.badMedic1) && (self->enemy->monsterinfo.badMedic1->inuse) && (!strncmp(self->enemy->monsterinfo.badMedic1->classname, "monster_medic", 13))) + { + self->enemy->monsterinfo.badMedic2 = self; + } + else + { + self->enemy->monsterinfo.badMedic1 = self; + } } - else + + if (gib) { - self->enemy->monsterinfo.badMedic1 = self; - } - } - if ((gib) && (self->enemy) && (self->enemy->inuse)) - { - if (self->enemy->gib_health) - hurt = -self->enemy->gib_health; - else - hurt = 500; + if (self->enemy->gib_health) + hurt = -self->enemy->gib_health; + else + hurt = 500; - T_Damage(self->enemy, self, self, vec3_origin, self->enemy->s.origin, - pain_normal, hurt, 0, DAMAGE_NONE, MOD_UNKNOWN); + T_Damage(self->enemy, self, self, vec3_origin, self->enemy->s.origin, + pain_normal, hurt, 0, DAMAGE_NONE, MOD_UNKNOWN); + } } // clean up self @@ -632,21 +636,6 @@ void medic_fire_blaster(edict_t *self) } else { - static constexpr vec3_t hb_offsets[] = { - { 33.0f, 12.5f, 15.0f }, - { 32.4f, 11.2f, 15.0f }, - { 35.6f, 7.4f, 15.0f }, - { 34.0f, 4.1f, 15.0f }, - { 36.6f, 1.0f, 15.0f }, - { 34.7f, -1.9f, 15.0f }, - { 36.6f, -0.5f, 15.0f }, - { 34.2f, 2.8f, 15.0f }, - { 36.5f, 3.8f, 15.0f }, - { 33.5f, 6.9f, 15.0f }, - { 32.7f, 9.9f, 15.0f }, - { 34.5f, 11.0f, 15.0f } - }; - effect = (self->s.frame % 4) ? EF_NONE : EF_HYPERBLASTER; mz = static_cast(((self->mass > 400) ? MZ2_MEDIC_HYPERBLASTER2_1 : MZ2_MEDIC_HYPERBLASTER1_1) + (self->s.frame - FRAME_attack19)); } @@ -1232,10 +1221,8 @@ void medic_spawngrows(edict_t *self) { offset = reinforcement_position[count]; - if (self->s.scale) - offset *= self->s.scale; - startpoint = M_ProjectFlashSource(self, offset, f, r); + // a little off the ground startpoint[2] += 10 * (self->s.scale ? self->s.scale : 1.0f); @@ -1277,9 +1264,6 @@ void medic_finish_spawn(edict_t *self) auto &reinforcement = self->monsterinfo.reinforcements.reinforcements[self->monsterinfo.chosen_reinforcements[count]]; offset = reinforcement_position[count]; - if (self->s.scale) - offset *= self->s.scale; - startpoint = M_ProjectFlashSource(self, offset, f, r); // a little off the ground @@ -1595,17 +1579,17 @@ void SP_monster_medic(edict_t *self) self->s.skinnum = 2; // commander sounds - commander_sound_idle1 = gi.soundindex("medic_commander/medidle.wav"); - commander_sound_pain1 = gi.soundindex("medic_commander/medpain1.wav"); - commander_sound_pain2 = gi.soundindex("medic_commander/medpain2.wav"); - commander_sound_die = gi.soundindex("medic_commander/meddeth.wav"); - commander_sound_sight = gi.soundindex("medic_commander/medsght.wav"); - commander_sound_search = gi.soundindex("medic_commander/medsrch.wav"); - commander_sound_hook_launch = gi.soundindex("medic_commander/medatck2c.wav"); - commander_sound_hook_hit = gi.soundindex("medic_commander/medatck3a.wav"); - commander_sound_hook_heal = gi.soundindex("medic_commander/medatck4a.wav"); - commander_sound_hook_retract = gi.soundindex("medic_commander/medatck5a.wav"); - commander_sound_spawn = gi.soundindex("medic_commander/monsterspawn1.wav"); + commander_sound_idle1.assign("medic_commander/medidle.wav"); + commander_sound_pain1.assign("medic_commander/medpain1.wav"); + commander_sound_pain2.assign("medic_commander/medpain2.wav"); + commander_sound_die.assign("medic_commander/meddeth.wav"); + commander_sound_sight.assign("medic_commander/medsght.wav"); + commander_sound_search.assign("medic_commander/medsrch.wav"); + commander_sound_hook_launch.assign("medic_commander/medatck2c.wav"); + commander_sound_hook_hit.assign("medic_commander/medatck3a.wav"); + commander_sound_hook_heal.assign("medic_commander/medatck4a.wav"); + commander_sound_hook_retract.assign("medic_commander/medatck5a.wav"); + commander_sound_spawn.assign("medic_commander/monsterspawn1.wav"); gi.soundindex("tank/tnkatck3.wav"); const char *reinforcements = default_reinforcements; @@ -1625,16 +1609,16 @@ void SP_monster_medic(edict_t *self) } else { - sound_idle1 = gi.soundindex("medic/idle.wav"); - sound_pain1 = gi.soundindex("medic/medpain1.wav"); - sound_pain2 = gi.soundindex("medic/medpain2.wav"); - sound_die = gi.soundindex("medic/meddeth1.wav"); - sound_sight = gi.soundindex("medic/medsght1.wav"); - sound_search = gi.soundindex("medic/medsrch1.wav"); - sound_hook_launch = gi.soundindex("medic/medatck2.wav"); - sound_hook_hit = gi.soundindex("medic/medatck3.wav"); - sound_hook_heal = gi.soundindex("medic/medatck4.wav"); - sound_hook_retract = gi.soundindex("medic/medatck5.wav"); + sound_idle1.assign("medic/idle.wav"); + sound_pain1.assign("medic/medpain1.wav"); + sound_pain2.assign("medic/medpain2.wav"); + sound_die.assign("medic/meddeth1.wav"); + sound_sight.assign("medic/medsght1.wav"); + sound_search.assign("medic/medsrch1.wav"); + sound_hook_launch.assign("medic/medatck2.wav"); + sound_hook_hit.assign("medic/medatck3.wav"); + sound_hook_heal.assign("medic/medatck4.wav"); + sound_hook_retract.assign("medic/medatck5.wav"); gi.soundindex("medic/medatck1.wav"); self->s.skinnum = 0; diff --git a/rerelease/m_move.cpp b/rerelease/m_move.cpp index d4313414..bfdb3ef6 100644 --- a/rerelease/m_move.cpp +++ b/rerelease/m_move.cpp @@ -656,7 +656,7 @@ bool SV_movestep(edict_t *ent, vec3_t move, bool relink) float stepsize; // push down from a step height above the wished position - if (ent->spawnflags.has(SPAWNFLAG_MONSTER_SUPER_STEP)) + if (ent->spawnflags.has(SPAWNFLAG_MONSTER_SUPER_STEP) && ent->health > 0) stepsize = 64.f; else if (!(ent->monsterinfo.aiflags & AI_NOSTEP)) stepsize = STEPSIZE; @@ -741,7 +741,8 @@ bool SV_movestep(edict_t *ent, vec3_t move, bool relink) ent->groundentity = nullptr; return true; } - else if (!ent->spawnflags.has(SPAWNFLAG_MONSTER_SUPER_STEP)) + // [Paril-KEX] allow dead monsters to "fall" off of edges in their death animation + else if (!ent->spawnflags.has(SPAWNFLAG_MONSTER_SUPER_STEP) && ent->health > 0) return false; // walked off an edge } @@ -831,7 +832,7 @@ bool SV_movestep(edict_t *ent, vec3_t move, bool relink) return false; } - if (ent->spawnflags.has(SPAWNFLAG_MONSTER_SUPER_STEP)) + if (ent->spawnflags.has(SPAWNFLAG_MONSTER_SUPER_STEP) && ent->health > 0) { if (!ent->groundentity || ent->groundentity->solid == SOLID_BSP) { @@ -867,7 +868,11 @@ bool SV_movestep(edict_t *ent, vec3_t move, bool relink) if (relink) { gi.linkentity(ent); - G_TouchTriggers(ent); + + // [Paril-KEX] this is something N64 does to avoid doors opening + // at the start of a level, which triggers some monsters to spawn. + if (!level.is_n64 || level.time > FRAME_TIME_S) + G_TouchTriggers(ent); } if (stepped) diff --git a/rerelease/m_mutant.cpp b/rerelease/m_mutant.cpp index 7b6294bd..c46b50aa 100644 --- a/rerelease/m_mutant.cpp +++ b/rerelease/m_mutant.cpp @@ -13,19 +13,19 @@ mutant constexpr spawnflags_t SPAWNFLAG_MUTANT_NOJUMPING = 8_spawnflag; -static int sound_swing; -static int sound_hit; -static int sound_hit2; -static int sound_death; -static int sound_idle; -static int sound_pain1; -static int sound_pain2; -static int sound_sight; -static int sound_search; -static int sound_step1; -static int sound_step2; -static int sound_step3; -static int sound_thud; +static cached_soundindex sound_swing; +static cached_soundindex sound_hit; +static cached_soundindex sound_hit2; +static cached_soundindex sound_death; +static cached_soundindex sound_idle; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_sight; +static cached_soundindex sound_search; +static cached_soundindex sound_step1; +static cached_soundindex sound_step2; +static cached_soundindex sound_step3; +static cached_soundindex sound_thud; // // SOUNDS @@ -286,7 +286,8 @@ TOUCH(mutant_jump_touch) (edict_t *self, edict_t *other, const trace_t &tr, bool if (self->style == 1 && other->takedamage) { - if (self->velocity.length() > 400) + // [Paril-KEX] only if we're actually moving fast enough to hurt + if (self->velocity.length() > 30) { vec3_t point; vec3_t normal; @@ -321,8 +322,8 @@ void mutant_jump_takeoff(edict_t *self) gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); AngleVectors(self->s.angles, forward, nullptr, nullptr); self->s.origin[2] += 1; - self->velocity = forward * 400; - self->velocity[2] = 150; + self->velocity = forward * 425; + self->velocity[2] = 160; self->groundentity = nullptr; self->monsterinfo.aiflags |= AI_DUCKED; self->monsterinfo.attack_finished = level.time + 3_sec; @@ -506,15 +507,6 @@ MONSTERINFO_SETSKIN(mutant_setskin) (edict_t *self) -> void // DEATH // -// FIXME: expanded dead box a bit, but rotation of dead body -// means it'll never always fit unless you move the whole box based on angle -void mutant_dead(edict_t *self) -{ - self->mins = { 0, -48, -24 }; - self->maxs = { 64, 16, -8 }; - monster_dead(self); -} - void mutant_shrink(edict_t *self) { self->maxs[2] = 0; @@ -522,32 +514,43 @@ void mutant_shrink(edict_t *self) gi.linkentity(self); } +// [Paril-KEX] +static void ai_move_slide_right(edict_t *self, float dist) +{ + M_walkmove(self, self->s.angles[YAW] + 90, dist); +} + +static void ai_move_slide_left(edict_t *self, float dist) +{ + M_walkmove(self, self->s.angles[YAW] - 90, dist); +} + mframe_t mutant_frames_death1[] = { - { ai_move }, - { ai_move }, - { ai_move }, - { ai_move }, - { ai_move }, - { ai_move, 0, mutant_shrink }, - { ai_move }, - { ai_move }, - { ai_move } + { ai_move_slide_right }, + { ai_move_slide_right }, + { ai_move_slide_right }, + { ai_move_slide_right, 2 }, + { ai_move_slide_right, 5 }, + { ai_move_slide_right, 7, mutant_shrink }, + { ai_move_slide_right, 6 }, + { ai_move_slide_right, 2 }, + { ai_move_slide_right } }; -MMOVE_T(mutant_move_death1) = { FRAME_death101, FRAME_death109, mutant_frames_death1, mutant_dead }; +MMOVE_T(mutant_move_death1) = { FRAME_death101, FRAME_death109, mutant_frames_death1, monster_dead }; mframe_t mutant_frames_death2[] = { - { ai_move }, - { ai_move }, - { ai_move }, - { ai_move }, - { ai_move, 0, mutant_shrink }, - { ai_move }, - { ai_move }, - { ai_move }, - { ai_move }, - { ai_move } + { ai_move_slide_left }, + { ai_move_slide_left }, + { ai_move_slide_left }, + { ai_move_slide_left, 1 }, + { ai_move_slide_left, 3, mutant_shrink }, + { ai_move_slide_left, 6 }, + { ai_move_slide_left, 8 }, + { ai_move_slide_left, 5 }, + { ai_move_slide_left, 2 }, + { ai_move_slide_left } }; -MMOVE_T(mutant_move_death2) = { FRAME_death201, FRAME_death210, mutant_frames_death2, mutant_dead }; +MMOVE_T(mutant_move_death2) = { FRAME_death201, FRAME_death210, mutant_frames_death2, monster_dead }; DIE(mutant_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void { @@ -676,19 +679,19 @@ void SP_monster_mutant(edict_t *self) return; } - sound_swing = gi.soundindex("mutant/mutatck1.wav"); - sound_hit = gi.soundindex("mutant/mutatck2.wav"); - sound_hit2 = gi.soundindex("mutant/mutatck3.wav"); - sound_death = gi.soundindex("mutant/mutdeth1.wav"); - sound_idle = gi.soundindex("mutant/mutidle1.wav"); - sound_pain1 = gi.soundindex("mutant/mutpain1.wav"); - sound_pain2 = gi.soundindex("mutant/mutpain2.wav"); - sound_sight = gi.soundindex("mutant/mutsght1.wav"); - sound_search = gi.soundindex("mutant/mutsrch1.wav"); - sound_step1 = gi.soundindex("mutant/step1.wav"); - sound_step2 = gi.soundindex("mutant/step2.wav"); - sound_step3 = gi.soundindex("mutant/step3.wav"); - sound_thud = gi.soundindex("mutant/thud1.wav"); + sound_swing.assign("mutant/mutatck1.wav"); + sound_hit.assign("mutant/mutatck2.wav"); + sound_hit2.assign("mutant/mutatck3.wav"); + sound_death.assign("mutant/mutdeth1.wav"); + sound_idle.assign("mutant/mutidle1.wav"); + sound_pain1.assign("mutant/mutpain1.wav"); + sound_pain2.assign("mutant/mutpain2.wav"); + sound_sight.assign("mutant/mutsght1.wav"); + sound_search.assign("mutant/mutsrch1.wav"); + sound_step1.assign("mutant/step1.wav"); + sound_step2.assign("mutant/step2.wav"); + sound_step3.assign("mutant/step3.wav"); + sound_thud.assign("mutant/thud1.wav"); self->monsterinfo.aiflags |= AI_STINKY; diff --git a/rerelease/m_parasite.cpp b/rerelease/m_parasite.cpp index 8ec6390b..2d706d13 100644 --- a/rerelease/m_parasite.cpp +++ b/rerelease/m_parasite.cpp @@ -15,17 +15,17 @@ constexpr float g_athena_parasite_miss_chance = 0.1f; constexpr float g_athena_parasite_proboscis_speed = 1250; constexpr float g_athena_parasite_proboscis_retract_modifier = 2.0f; -static int sound_pain1; -static int sound_pain2; -static int sound_die; -static int sound_launch; -static int sound_impact; -static int sound_suck; -static int sound_reelin; -static int sound_sight; -static int sound_tap; -static int sound_scratch; -static int sound_search; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_die; +static cached_soundindex sound_launch; +static cached_soundindex sound_impact; +static cached_soundindex sound_suck; +static cached_soundindex sound_reelin; +static cached_soundindex sound_sight; +static cached_soundindex sound_tap; +static cached_soundindex sound_scratch; +static cached_soundindex sound_search; void parasite_stand(edict_t *self); void parasite_start_run(edict_t *self); @@ -909,17 +909,17 @@ void SP_monster_parasite(edict_t *self) return; } - sound_pain1 = gi.soundindex("parasite/parpain1.wav"); - sound_pain2 = gi.soundindex("parasite/parpain2.wav"); - sound_die = gi.soundindex("parasite/pardeth1.wav"); - sound_launch = gi.soundindex("parasite/paratck1.wav"); - sound_impact = gi.soundindex("parasite/paratck2.wav"); - sound_suck = gi.soundindex("parasite/paratck3.wav"); - sound_reelin = gi.soundindex("parasite/paratck4.wav"); - sound_sight = gi.soundindex("parasite/parsght1.wav"); - sound_tap = gi.soundindex("parasite/paridle1.wav"); - sound_scratch = gi.soundindex("parasite/paridle2.wav"); - sound_search = gi.soundindex("parasite/parsrch1.wav"); + sound_pain1.assign("parasite/parpain1.wav"); + sound_pain2.assign("parasite/parpain2.wav"); + sound_die.assign("parasite/pardeth1.wav"); + sound_launch.assign("parasite/paratck1.wav"); + sound_impact.assign("parasite/paratck2.wav"); + sound_suck.assign("parasite/paratck3.wav"); + sound_reelin.assign("parasite/paratck4.wav"); + sound_sight.assign("parasite/parsght1.wav"); + sound_tap.assign("parasite/paridle1.wav"); + sound_scratch.assign("parasite/paridle2.wav"); + sound_search.assign("parasite/parsrch1.wav"); gi.modelindex("models/monsters/parasite/tip/tris.md2"); gi.modelindex("models/monsters/parasite/segment/tris.md2"); diff --git a/rerelease/m_shambler.cpp b/rerelease/m_shambler.cpp index 56f3d079..e53a013c 100644 --- a/rerelease/m_shambler.cpp +++ b/rerelease/m_shambler.cpp @@ -12,15 +12,15 @@ SHAMBLER #include "m_shambler.h" #include "m_flash.h" -static int sound_pain; -static int sound_idle; -static int sound_die; -static int sound_sight; -static int sound_windup; -static int sound_melee1; -static int sound_melee2; -static int sound_smack; -static int sound_boom; +static cached_soundindex sound_pain; +static cached_soundindex sound_idle; +static cached_soundindex sound_die; +static cached_soundindex sound_sight; +static cached_soundindex sound_windup; +static cached_soundindex sound_melee1; +static cached_soundindex sound_melee2; +static cached_soundindex sound_smack; +static cached_soundindex sound_boom; // // misc @@ -558,15 +558,15 @@ void SP_monster_shambler(edict_t* self) self->solid = SOLID_BBOX; gi.modelindex("models/proj/lightning/tris.md2"); - sound_pain = gi.soundindex("shambler/shurt2.wav"); - sound_idle = gi.soundindex("shambler/sidle.wav"); - sound_die = gi.soundindex("shambler/sdeath.wav"); - sound_windup = gi.soundindex("shambler/sattck1.wav"); - sound_melee1 = gi.soundindex("shambler/melee1.wav"); - sound_melee2 = gi.soundindex("shambler/melee2.wav"); - sound_sight = gi.soundindex("shambler/ssight.wav"); - sound_smack = gi.soundindex("shambler/smack.wav"); - sound_boom = gi.soundindex("shambler/sboom.wav"); + sound_pain.assign("shambler/shurt2.wav"); + sound_idle.assign("shambler/sidle.wav"); + sound_die.assign("shambler/sdeath.wav"); + sound_windup.assign("shambler/sattck1.wav"); + sound_melee1.assign("shambler/melee1.wav"); + sound_melee2.assign("shambler/melee2.wav"); + sound_sight.assign("shambler/ssight.wav"); + sound_smack.assign("shambler/smack.wav"); + sound_boom.assign("shambler/sboom.wav"); self->health = 600 * st.health_multiplier; self->gib_health = -60; diff --git a/rerelease/m_soldier.cpp b/rerelease/m_soldier.cpp index 94ed7f61..97c84584 100644 --- a/rerelease/m_soldier.cpp +++ b/rerelease/m_soldier.cpp @@ -12,16 +12,16 @@ SOLDIER #include "m_soldier.h" #include "m_flash.h" -static int sound_idle; -static int sound_sight1; -static int sound_sight2; -static int sound_pain_light; -static int sound_pain; -static int sound_pain_ss; -static int sound_death_light; -static int sound_death; -static int sound_death_ss; -static int sound_cock; +static cached_soundindex sound_idle; +static cached_soundindex sound_sight1; +static cached_soundindex sound_sight2; +static cached_soundindex sound_pain_light; +static cached_soundindex sound_pain; +static cached_soundindex sound_pain_ss; +static cached_soundindex sound_death_light; +static cached_soundindex sound_death; +static cached_soundindex sound_death_ss; +static cached_soundindex sound_cock; void soldier_start_charge(edict_t *self) { @@ -648,6 +648,13 @@ void soldier_fire_xatrix(edict_t *self, int flash_number, bool angle_limited) } else { + // [Paril-KEX] no enemy = no fire + if ((!self->enemy) || (!self->enemy->inuse)) + { + self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; + return; + } + // PMM if (self->monsterinfo.attack_state == AS_BLIND) end = self->monsterinfo.blind_fire_target; @@ -1838,10 +1845,10 @@ void SP_monster_soldier_x(edict_t *self) self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; - sound_idle = gi.soundindex("soldier/solidle1.wav"); - sound_sight1 = gi.soundindex("soldier/solsght1.wav"); - sound_sight2 = gi.soundindex("soldier/solsrch1.wav"); - sound_cock = gi.soundindex("infantry/infatck3.wav"); + sound_idle.assign("soldier/solidle1.wav"); + sound_sight1.assign("soldier/solsght1.wav"); + sound_sight2.assign("soldier/solsrch1.wav"); + sound_cock.assign("infantry/infatck3.wav"); gi.modelindex("models/monsters/soldier/gibs/head.md2"); gi.modelindex("models/monsters/soldier/gibs/gun.md2"); @@ -1897,8 +1904,8 @@ void SP_monster_soldier_light(edict_t *self) SP_monster_soldier_x(self); - sound_pain_light = gi.soundindex("soldier/solpain2.wav"); - sound_death_light = gi.soundindex("soldier/soldeth2.wav"); + sound_pain_light.assign("soldier/solpain2.wav"); + sound_death_light.assign("soldier/soldeth2.wav"); gi.modelindex("models/objects/laser/tris.md2"); gi.soundindex("misc/lasfly.wav"); gi.soundindex("soldier/solatck2.wav"); @@ -1923,8 +1930,8 @@ void SP_monster_soldier(edict_t *self) SP_monster_soldier_x(self); - sound_pain = gi.soundindex("soldier/solpain1.wav"); - sound_death = gi.soundindex("soldier/soldeth1.wav"); + sound_pain.assign("soldier/solpain1.wav"); + sound_death.assign("soldier/soldeth1.wav"); gi.soundindex("soldier/solatck1.wav"); self->s.skinnum = 2; @@ -1944,8 +1951,8 @@ void SP_monster_soldier_ss(edict_t *self) SP_monster_soldier_x(self); - sound_pain_ss = gi.soundindex("soldier/solpain3.wav"); - sound_death_ss = gi.soundindex("soldier/soldeth3.wav"); + sound_pain_ss.assign("soldier/solpain3.wav"); + sound_death_ss.assign("soldier/soldeth3.wav"); gi.soundindex("soldier/solatck3.wav"); self->s.skinnum = 4; @@ -1975,8 +1982,8 @@ void SP_monster_soldier_ripper(edict_t *self) SP_monster_soldier_h(self); - sound_pain_light = gi.soundindex("soldier/solpain2.wav"); - sound_death_light = gi.soundindex("soldier/soldeth2.wav"); + sound_pain_light.assign("soldier/solpain2.wav"); + sound_death_light.assign("soldier/soldeth2.wav"); gi.modelindex("models/objects/boomrang/tris.md2"); gi.soundindex("misc/lasfly.wav"); @@ -2003,8 +2010,8 @@ void SP_monster_soldier_hypergun(edict_t *self) SP_monster_soldier_h(self); gi.modelindex("models/objects/laser/tris.md2"); - sound_pain = gi.soundindex("soldier/solpain1.wav"); - sound_death = gi.soundindex("soldier/soldeth1.wav"); + sound_pain.assign("soldier/solpain1.wav"); + sound_death.assign("soldier/soldeth1.wav"); gi.soundindex("soldier/solatck1.wav"); gi.soundindex("weapons/hyprbd1a.wav"); gi.soundindex("weapons/hyprbl1a.wav"); @@ -2029,8 +2036,8 @@ void SP_monster_soldier_lasergun(edict_t *self) SP_monster_soldier_h(self); - sound_pain_ss = gi.soundindex("soldier/solpain3.wav"); - sound_death_ss = gi.soundindex("soldier/soldeth3.wav"); + sound_pain_ss.assign("soldier/solpain3.wav"); + sound_death_ss.assign("soldier/soldeth3.wav"); gi.soundindex("soldier/solatck3.wav"); self->s.skinnum = 10; diff --git a/rerelease/m_supertank.cpp b/rerelease/m_supertank.cpp index e01a90a3..d59b3791 100644 --- a/rerelease/m_supertank.cpp +++ b/rerelease/m_supertank.cpp @@ -16,14 +16,14 @@ constexpr spawnflags_t SPAWNFLAG_SUPERTANK_POWERSHIELD = 8_spawnflag; // n64 constexpr spawnflags_t SPAWNFLAG_SUPERTANK_LONG_DEATH = 16_spawnflag; -static int sound_pain1; -static int sound_pain2; -static int sound_pain3; -static int sound_death; -static int sound_search1; -static int sound_search2; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_pain3; +static cached_soundindex sound_death; +static cached_soundindex sound_search1; +static cached_soundindex sound_search2; -static int tread_sound; +static cached_soundindex tread_sound; void TreadSound(edict_t *self) { @@ -640,14 +640,14 @@ void SP_monster_supertank(edict_t *self) return; } - sound_pain1 = gi.soundindex("bosstank/btkpain1.wav"); - sound_pain2 = gi.soundindex("bosstank/btkpain2.wav"); - sound_pain3 = gi.soundindex("bosstank/btkpain3.wav"); - sound_death = gi.soundindex("bosstank/btkdeth1.wav"); - sound_search1 = gi.soundindex("bosstank/btkunqv1.wav"); - sound_search2 = gi.soundindex("bosstank/btkunqv2.wav"); + sound_pain1.assign("bosstank/btkpain1.wav"); + sound_pain2.assign("bosstank/btkpain2.wav"); + sound_pain3.assign("bosstank/btkpain3.wav"); + sound_death.assign("bosstank/btkdeth1.wav"); + sound_search1.assign("bosstank/btkunqv1.wav"); + sound_search2.assign("bosstank/btkunqv2.wav"); - tread_sound = gi.soundindex("bosstank/btkengn1.wav"); + tread_sound.assign("bosstank/btkengn1.wav"); gi.soundindex("gunner/gunatck3.wav"); gi.soundindex("infantry/infatck1.wav"); diff --git a/rerelease/m_tank.cpp b/rerelease/m_tank.cpp index 95507f13..9c6b4e54 100644 --- a/rerelease/m_tank.cpp +++ b/rerelease/m_tank.cpp @@ -16,14 +16,14 @@ void tank_refire_rocket(edict_t *self); void tank_doattack_rocket(edict_t *self); void tank_reattack_blaster(edict_t *self); -static int sound_thud; -static int sound_pain, sound_pain2; -static int sound_idle; -static int sound_die; -static int sound_step; -static int sound_sight; -static int sound_windup; -static int sound_strike; +static cached_soundindex sound_thud; +static cached_soundindex sound_pain, sound_pain2; +static cached_soundindex sound_idle; +static cached_soundindex sound_die; +static cached_soundindex sound_step; +static cached_soundindex sound_sight; +static cached_soundindex sound_windup; +static cached_soundindex sound_strike; constexpr spawnflags_t SPAWNFLAG_TANK_COMMANDER_GUARDIAN = 8_spawnflag; constexpr spawnflags_t SPAWNFLAG_TANK_COMMANDER_HEAT_SEEKING = 16_spawnflag; @@ -612,7 +612,9 @@ void tank_reattack_blaster(edict_t *self) void tank_poststrike(edict_t *self) { self->enemy = nullptr; - tank_run(self); + // [Paril-KEX] + self->monsterinfo.pausetime = HOLD_FOREVER; + self->monsterinfo.stand(self); } mframe_t tank_frames_attack_strike[] = { @@ -1041,13 +1043,13 @@ void SP_monster_tank(edict_t *self) gi.modelindex("models/monsters/tank/gibs/foot.md2"); gi.modelindex("models/monsters/tank/gibs/thigh.md2"); - sound_thud = gi.soundindex("tank/tnkdeth2.wav"); - sound_idle = gi.soundindex("tank/tnkidle1.wav"); - sound_die = gi.soundindex("tank/death.wav"); - sound_step = gi.soundindex("tank/step.wav"); - sound_windup = gi.soundindex("tank/tnkatck4.wav"); - sound_strike = gi.soundindex("tank/tnkatck5.wav"); - sound_sight = gi.soundindex("tank/sight1.wav"); + sound_thud.assign("tank/tnkdeth2.wav"); + sound_idle.assign("tank/tnkidle1.wav"); + sound_die.assign("tank/death.wav"); + sound_step.assign("tank/step.wav"); + sound_windup.assign("tank/tnkatck4.wav"); + sound_strike.assign("tank/tnkatck5.wav"); + sound_sight.assign("tank/sight1.wav"); gi.soundindex("tank/tnkatck1.wav"); gi.soundindex("tank/tnkatk2a.wav"); @@ -1062,13 +1064,13 @@ void SP_monster_tank(edict_t *self) self->health = 1000 * st.health_multiplier; self->gib_health = -225; self->count = 1; - sound_pain2 = gi.soundindex("tank/pain.wav"); + sound_pain2.assign("tank/pain.wav"); } else { self->health = 750 * st.health_multiplier; self->gib_health = -200; - sound_pain = gi.soundindex("tank/tnkpain2.wav"); + sound_pain.assign("tank/tnkpain2.wav"); } self->monsterinfo.scale = MODEL_SCALE; diff --git a/rerelease/p_client.cpp b/rerelease/p_client.cpp index 1f4dcd7c..d187b9b1 100644 --- a/rerelease/p_client.cpp +++ b/rerelease/p_client.cpp @@ -858,14 +858,14 @@ void InitClientPersistant(edict_t *ent, gclient_t *client) client->pers.max_ammo[AMMO_TESLA] = 5; // ROGUE - if (!g_instagib->integer) + if (!deathmatch->integer || !g_instagib->integer) client->pers.inventory[IT_WEAPON_BLASTER] = 1; // [Kex] // start items! if (*g_start_items->string) Player_GiveStartItems(ent, g_start_items->string); - else if (g_instagib->integer) + else if (deathmatch->integer && g_instagib->integer) { client->pers.inventory[IT_WEAPON_RAILGUN] = 1; client->pers.inventory[IT_AMMO_SLUGS] = 99; @@ -924,11 +924,6 @@ void InitClientResp(gclient_t *client) client->resp.entertime = level.time; client->resp.coop_respawn = client->pers; - - // ZOID - if (G_TeamplayEnabled() && client->pers.connected && client->resp.ctf_team < CTF_TEAM1) - CTFAssignTeam(client); - // ZOID } /* @@ -1051,7 +1046,9 @@ select_spawn_result_t SelectDeathmatchSpawnPoint(bool farthest, bool force_spawn if (spawn_points.size() == 0) { spot = G_FindByString<&edict_t::classname>(nullptr, "info_player_start"); - spawn_points.push_back({ spot, PlayersRangeFromSpot(spot) }); + + if (spot) + spawn_points.push_back({ spot, PlayersRangeFromSpot(spot) }); // map is malformed if (spawn_points.size() == 0) @@ -1231,30 +1228,46 @@ static edict_t *SelectSingleSpawnPoint(edict_t *ent) } // [Paril-KEX] -static edict_t *G_UnsafeSpawnPosition(vec3_t spot) +static edict_t *G_UnsafeSpawnPosition(vec3_t spot, bool check_players) { - trace_t tr = gi.trace(spot, PLAYER_MINS, PLAYER_MAXS, spot, nullptr, MASK_PLAYERSOLID); + contents_t mask = MASK_PLAYERSOLID; + + if (!check_players) + mask &= ~CONTENTS_PLAYER; + + trace_t tr = gi.trace(spot, PLAYER_MINS, PLAYER_MAXS, spot, nullptr, mask); // sometimes the spot is too close to the ground, give it a bit of slack if (tr.startsolid && !tr.ent->client) { spot[2] += 1; - tr = gi.trace(spot, PLAYER_MINS, PLAYER_MAXS, spot, nullptr, MASK_PLAYERSOLID); + tr = gi.trace(spot, PLAYER_MINS, PLAYER_MAXS, spot, nullptr, mask); } // no idea why this happens in some maps.. if (tr.startsolid && !tr.ent->client) - return tr.ent; + { + // try a nudge + if (G_FixStuckObject_Generic(spot, PLAYER_MINS, PLAYER_MAXS, [mask] (const vec3_t &start, const vec3_t &mins, const vec3_t &maxs, const vec3_t &end) { + return gi.trace(start, mins, maxs, end, nullptr, mask); + }) == stuck_result_t::NO_GOOD_POSITION) + return tr.ent; // what do we do here...? + + trace_t tr = gi.trace(spot, PLAYER_MINS, PLAYER_MAXS, spot, nullptr, mask); + + if (tr.startsolid && !tr.ent->client) + return tr.ent; // what do we do here...? + } if (tr.fraction == 1.f) return nullptr; - else if (tr.ent->client) + else if (check_players && tr.ent && tr.ent->client) return tr.ent; return nullptr; } -edict_t *SelectCoopSpawnPoint(edict_t *ent, bool force_spawn) +edict_t *SelectCoopSpawnPoint(edict_t *ent, bool force_spawn, bool check_players) { edict_t *spot = nullptr; const char *target; @@ -1268,7 +1281,7 @@ edict_t *SelectCoopSpawnPoint(edict_t *ent, bool force_spawn) // try the main spawn point first spot = SelectSingleSpawnPoint(ent); - if (spot && !G_UnsafeSpawnPosition(spot->s.origin)) + if (spot && !G_UnsafeSpawnPosition(spot->s.origin, check_players)) return spot; spot = nullptr; @@ -1289,7 +1302,7 @@ edict_t *SelectCoopSpawnPoint(edict_t *ent, bool force_spawn) { // this is a coop spawn point for one of the clients here num_valid_spots++; - if (!G_UnsafeSpawnPosition(spot->s.origin)) + if (!G_UnsafeSpawnPosition(spot->s.origin, check_players)) return spot; // this is it } } @@ -1314,7 +1327,7 @@ edict_t *SelectCoopSpawnPoint(edict_t *ent, bool force_spawn) // this is a coop spawn point for one of the clients here num_valid_spots++; - if (!G_UnsafeSpawnPosition(spot->s.origin)) + if (!G_UnsafeSpawnPosition(spot->s.origin, check_players)) return spot; // this is it } } @@ -1392,7 +1405,7 @@ bool TryLandmarkSpawn(edict_t* ent, vec3_t& origin, vec3_t& angles) // sometimes, landmark spawns can cause slight inconsistencies in collision; // we'll do a bit of tracing to make sure the bbox is clear if (G_FixStuckObject_Generic(origin, PLAYER_MINS, PLAYER_MAXS, [ent] (const vec3_t &start, const vec3_t &mins, const vec3_t &maxs, const vec3_t &end) { - return gi.trace(start, mins, maxs, end, ent, MASK_PLAYERSOLID); + return gi.trace(start, mins, maxs, end, ent, MASK_PLAYERSOLID & ~CONTENTS_PLAYER); }) == stuck_result_t::NO_GOOD_POSITION) { origin = old_origin; @@ -1451,11 +1464,26 @@ bool SelectSpawnPoint(edict_t *ent, vec3_t &origin, vec3_t &angles, bool force_s if (coop->integer) { - spot = SelectCoopSpawnPoint(ent, force_spawn); + spot = SelectCoopSpawnPoint(ent, force_spawn, true); + + if (!spot) + spot = SelectCoopSpawnPoint(ent, force_spawn, false); // no open spot yet if (!spot) + { + // in worst case scenario in coop during intermission, just spawn us at intermission + // spot. this only happens for a single frame, and won't break + // anything if they come back. + if (level.intermissiontime) + { + origin = level.intermission_origin; + angles = level.intermission_angle; + return true; + } + return false; + } } else { @@ -2227,15 +2255,20 @@ void PutClientInServer(edict_t *ent) { if (coop->integer) { - if (edict_t *collision = G_UnsafeSpawnPosition(ent->s.origin); collision && collision->client) + edict_t *collision = G_UnsafeSpawnPosition(ent->s.origin, true); + + if (collision) { - // link us early so that the other player sees us there gi.linkentity(ent); - // we spawned in somebody else, so we're going to change their spawn position - bool lm = false; - SelectSpawnPoint(collision, spawn_origin, spawn_angles, true, lm); - PutClientOnSpawnPoint(collision, spawn_origin, spawn_angles); + if (collision->client) + { + // we spawned in somebody else, so we're going to change their spawn position + bool lm = false; + SelectSpawnPoint(collision, spawn_origin, spawn_angles, true, lm); + PutClientOnSpawnPoint(collision, spawn_origin, spawn_angles); + } + // else, no choice but to accept where ever we spawned :( } } @@ -2285,6 +2318,11 @@ void ClientBeginDeathmatch(edict_t *ent) InitClientResp(ent->client); + // ZOID + if (G_TeamplayEnabled() && ent->client->resp.ctf_team < CTF_TEAM1) + CTFAssignTeam(ent->client); + // ZOID + // PGM if (gamerules->integer && DMGame.ClientBegin) { @@ -2433,6 +2471,9 @@ void ClientBegin(edict_t *ent) ent->client->awaiting_respawn = false; ent->client->respawn_timeout = 0_ms; + // [Paril-KEX] we're always connected by this point... + ent->client->pers.connected = true; + if (deathmatch->integer) { ClientBeginDeathmatch(ent); @@ -2732,7 +2773,7 @@ inline edict_t *ClientChooseSlot_Coop(const char *userinfo, const char *social_i // make sure we have a known default matches[i].slot->svflags |= SVF_PLAYER; - matches[i].slot->sv.init = true; + matches[i].slot->sv.init = false; matches[i].slot->classname = "player"; matches[i].slot->client->pers.connected = true; matches[i].slot->client->pers.spawned = true; @@ -2872,6 +2913,9 @@ bool ClientConnect(edict_t *ent, char *userinfo, const char *social_id, bool isB } ent->client->pers.connected = true; + + // [Paril-KEX] force a state update + ent->sv.init = false; return true; } @@ -3190,7 +3234,9 @@ void ClientThink(edict_t *ent, usercmd_t *ucmd) client->ps.pmove.pm_type = PM_NORMAL; // [Paril-KEX] - if (!G_ShouldPlayersCollide(false)) + if (!G_ShouldPlayersCollide(false) || + (coop->integer && !(ent->clipmask & CONTENTS_PLAYER)) // if player collision is on and we're temporarily ghostly... + ) client->ps.pmove.pm_flags |= PMF_IGNORE_PLAYER_COLLISION; else client->ps.pmove.pm_flags &= ~PMF_IGNORE_PLAYER_COLLISION; @@ -3248,7 +3294,8 @@ void ClientThink(edict_t *ent, usercmd_t *ucmd) if (pm.s.pm_flags & PMF_ON_LADDER) { - if (client->last_ladder_sound < level.time) + if (!deathmatch->integer && + client->last_ladder_sound < level.time) { ent->s.event = EV_LADDER_STEP; client->last_ladder_sound = level.time + LADDER_SOUND_TIME; @@ -3407,12 +3454,15 @@ inline bool G_MonstersSearchingFor(edict_t *player) { for (auto ent : active_monsters()) { + // check for *any* player target + if (player == nullptr && ent->enemy && !ent->enemy->client) + continue; // they're not targeting us, so who cares - if (ent->enemy != player) + else if (player != nullptr && ent->enemy != player) continue; // they lost sight of us - if (ent->monsterinfo.aiflags & AI_LOST_SIGHT && level.time > ent->monsterinfo.trail_time + 5_sec) + if ((ent->monsterinfo.aiflags & AI_LOST_SIGHT) && level.time > ent->monsterinfo.trail_time + 5_sec) continue; // no sir @@ -3524,6 +3574,8 @@ inline bool G_FindRespawnSpot(edict_t *player, vec3_t &spot) // respawn target & position inline std::tuple G_FindSquadRespawnTarget() { + bool monsters_searching_for_anybody = G_MonstersSearchingFor(nullptr); + for (auto player : active_players()) { // no dead players @@ -3545,6 +3597,14 @@ inline std::tuple G_FindSquadRespawnTarget() continue; } + // check firing state; if any enemies are mad at any players, + // don't respawn until everybody has cooled down + if (monsters_searching_for_anybody && player->client->last_firing_time >= level.time) + { + player->client->coop_respawn_state = COOP_RESPAWN_IN_COMBAT; + continue; + } + // check positioning; we must be on world ground if (player->groundentity != world) { diff --git a/rerelease/p_hud.cpp b/rerelease/p_hud.cpp index 44cd6e25..682cfd40 100644 --- a/rerelease/p_hud.cpp +++ b/rerelease/p_hud.cpp @@ -279,6 +279,8 @@ void BeginIntermission(edict_t *targ) game.autosaved = false; + level.intermissiontime = level.time; + // respawn any dead clients for (uint32_t i = 0; i < game.maxclients; i++) { @@ -286,10 +288,17 @@ void BeginIntermission(edict_t *targ) if (!client->inuse) continue; if (client->health <= 0) + { + // give us our max health back since it will reset + // to pers.health; in instanced items we'd lose the items + // we touched so we always want to respawn with our max. + if (P_UseCoopInstancedItems()) + client->client->pers.health = client->client->pers.max_health = client->max_health; + respawn(client); + } } - level.intermissiontime = level.time; level.intermission_server_frame = gi.ServerFrame(); level.changemap = targ->map; level.intermission_clear = targ->spawnflags.has(SPAWNFLAG_CHANGELEVEL_CLEAR_INVENTORY); @@ -754,6 +763,7 @@ void G_SetStats(edict_t *ent) ent->client->ps.stats[STAT_ACTIVE_WHEEL_WEAPON] = (ent->client->newweapon ? ent->client->newweapon->weapon_wheel_index : ent->client->pers.weapon ? ent->client->pers.weapon->weapon_wheel_index : -1); + ent->client->ps.stats[STAT_ACTIVE_WEAPON] = ent->client->pers.weapon ? ent->client->pers.weapon->weapon_wheel_index : -1; // // ammo diff --git a/rerelease/p_move.cpp b/rerelease/p_move.cpp index f6308d8b..65072840 100644 --- a/rerelease/p_move.cpp +++ b/rerelease/p_move.cpp @@ -479,10 +479,27 @@ void PM_StepSlideMove() // push down the final amount down = pml.origin; down[2] -= stepSize; + + // [Paril-KEX] jitspoe suggestion for stair clip fix; store + // the old down position, and pick a better spot for downwards + // trace if the start origin's Z position is lower than the down end pt. + vec3_t original_down = down; + + if (start_o[2] < down[2]) + down[2] = start_o[2] - 1.f; + trace = PM_Trace(pml.origin, pm->mins, pm->maxs, down); if (!trace.allsolid) { - pml.origin = trace.endpos; + // [Paril-KEX] from above, do the proper trace now + trace_t real_trace = PM_Trace(pml.origin, pm->mins, pm->maxs, original_down); + pml.origin = real_trace.endpos; + + // only an upwards jump is a stair clip + if (pml.velocity.z > 0.f) + { + pm->step_clip = true; + } } up = pml.origin; @@ -496,7 +513,9 @@ void PM_StepSlideMove() pml.origin = down_o; pml.velocity = down_v; } - else if (pm->s.pm_flags & PMF_ON_GROUND) + // [Paril-KEX] NB: this line being commented is crucial for ramp-jumps to work. + // thanks to Jitspoe for pointing this one out. + else// if (pm->s.pm_flags & PMF_ON_GROUND) //!! Special case // if we were walking along a plane, then we need to copy the Z over pml.velocity[2] = down_v[2]; @@ -1012,7 +1031,12 @@ void PM_CatagorizePosition() pm->s.pm_time = 64; } - PM_ClipVelocity(pml.velocity, pm->groundplane.normal, pml.velocity, 1.01f); + // [Paril-KEX] calculate impact delta; this also fixes triple jumping + vec3_t clipped_velocity; + PM_ClipVelocity(pml.velocity, pm->groundplane.normal, clipped_velocity, 1.01f); + + pm->impact_delta = pml.start_velocity[2] - clipped_velocity[2]; + pm->s.pm_flags |= PMF_ON_GROUND; if (pm_config.n64_physics || (pm->s.pm_flags & PMF_DUCKED)) @@ -1073,15 +1097,6 @@ void PM_CheckJump() float jump_height = 270.f; - // [Paril-KEX] - if (pm->s.pm_flags & PMF_TIME_TRICK) - { - pm->s.pm_flags &= ~PMF_TIME_TRICK; - pm->s.pm_time = 0; - - jump_height *= 1.40f; - } - pml.velocity[2] += jump_height; if (pml.velocity[2] < jump_height) pml.velocity[2] = jump_height; @@ -1125,6 +1140,9 @@ void PM_CheckSpecialMovement() if (pm->waterlevel != WATER_WAIST) return; + // [Paril-KEX] + else if (pm->watertype & CONTENTS_NO_WATERJUMP) + return; // quick check that something is even blocking us forward trace = PM_Trace(pml.origin, pm->mins, pm->maxs, pml.origin + (flatforward * 40), MASK_SOLID); @@ -1561,6 +1579,7 @@ void Pmove(pmove_t *pmove) pm->screen_blend = {}; pm->rdflags = RDF_NONE; pm->jump_sound = false; + pm->step_clip = false; pm->impact_delta = 0; // clear all pmove local vars @@ -1621,8 +1640,6 @@ void Pmove(pmove_t *pmove) if (PM_CheckDuck()) PM_CatagorizePosition(); - bool was_on_ground = !!pm->groundentity; - if (pm->s.pm_type == PM_DEAD) PM_DeadMove(); @@ -1680,10 +1697,6 @@ void Pmove(pmove_t *pmove) // set groundentity, watertype, and waterlevel for final spot PM_CatagorizePosition(); - // impact - if (pm->groundentity && !was_on_ground) - pm->impact_delta = pml.start_velocity[2] - pml.velocity[2]; - // trick jump if (pm->s.pm_flags & PMF_TIME_TRICK) PM_CheckJump(); diff --git a/rerelease/p_view.cpp b/rerelease/p_view.cpp index 5f2e4e72..d3ce0210 100644 --- a/rerelease/p_view.cpp +++ b/rerelease/p_view.cpp @@ -323,7 +323,7 @@ void SV_CalcViewOffset(edict_t *ent) } ent->client->ps.viewangles[YAW] = ent->client->killer_yaw; } - else + else if (!ent->client->pers.bob_skip && !SkipViewModifiers()) { // add angles based on weapon kick angles = P_CurrentKickAngles(ent); @@ -416,29 +416,29 @@ void SV_CalcViewOffset(edict_t *ent) v = {}; // add fall height - - if (ent->client->fall_time > level.time) - { - // [Paril-KEX] 100ms of slack is added to account for - // visual difference in higher tickrates - gtime_t diff = ent->client->fall_time - level.time; - // slack time remaining - if (DAMAGE_TIME_SLACK()) + if (!ent->client->pers.bob_skip && !SkipViewModifiers()) + { + if (ent->client->fall_time > level.time) { - if (diff > FALL_TIME() - DAMAGE_TIME_SLACK()) - ratio = (FALL_TIME() - diff).seconds() / DAMAGE_TIME_SLACK().seconds(); + // [Paril-KEX] 100ms of slack is added to account for + // visual difference in higher tickrates + gtime_t diff = ent->client->fall_time - level.time; + + // slack time remaining + if (DAMAGE_TIME_SLACK()) + { + if (diff > FALL_TIME() - DAMAGE_TIME_SLACK()) + ratio = (FALL_TIME() - diff).seconds() / DAMAGE_TIME_SLACK().seconds(); + else + ratio = diff.seconds() / (FALL_TIME() - DAMAGE_TIME_SLACK()).seconds(); + } else ratio = diff.seconds() / (FALL_TIME() - DAMAGE_TIME_SLACK()).seconds(); + v[2] -= ratio * ent->client->fall_value * 0.4f; } - else - ratio = diff.seconds() / (FALL_TIME() - DAMAGE_TIME_SLACK()).seconds(); - v[2] -= ratio * ent->client->fall_value * 0.4f; - } // add bob height - if (!ent->client->pers.bob_skip && !SkipViewModifiers()) - { bob = bobfracsin * xyspeed * bob_up->value; if (bob > 6) bob = 6; @@ -447,9 +447,9 @@ void SV_CalcViewOffset(edict_t *ent) } // add kick offset - - v += P_CurrentKickOrigin(ent); + if (!ent->client->pers.bob_skip && !SkipViewModifiers()) + v += P_CurrentKickOrigin(ent); // absolutely bound offsets // so the view can never be outside the player box @@ -532,6 +532,9 @@ void SV_CalcGunOffset(edict_t *ent) else if (d < 0) d = min(0.f, d + gi.frame_time_ms * reduction_factor); } + + // [Paril-KEX] cl_rollhack + ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL]; } // ROGUE else @@ -790,7 +793,7 @@ void P_WorldEffects() // play a gurp sound instead of a normal pain sound if (current_player->health <= current_player->dmg) - gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0); + gi.sound(current_player, CHAN_VOICE, gi.soundindex("*drown1.wav"), 1, ATTN_NORM, 0); // [Paril-KEX] else if (brandom()) gi.sound(current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0); else @@ -802,12 +805,11 @@ void P_WorldEffects() } } // Paril: almost-drowning sounds - // FIXME use better sound + precache in worldspawn else if (current_player->air_finished <= level.time + 3_sec) { if (current_player->client->next_drown_time < level.time) { - gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/wade1.wav"), 1, ATTN_NORM, 0); + gi.sound(current_player, CHAN_VOICE, gi.soundindex(fmt::format("player/wade{}.wav", 1 + ((int32_t) level.time.seconds() % 3)).c_str()), 1, ATTN_NORM, 0); current_player->client->next_drown_time = level.time + 1_sec; } } @@ -998,7 +1000,8 @@ void G_SetClientEvent(edict_t *ent) if (ent->client->ps.pmove.pm_flags & PMF_ON_LADDER) { - if (current_client->last_ladder_sound < level.time && + if (!deathmatch->integer && + current_client->last_ladder_sound < level.time && (current_client->last_ladder_pos - ent->s.origin).length() > 48.f) { ent->s.event = EV_LADDER_STEP; @@ -1416,7 +1419,8 @@ void ClientEndServerFrame(edict_t *ent) ent->s.angles[YAW] = ent->client->v_angle[YAW]; ent->s.angles[ROLL] = 0; - ent->s.angles[ROLL] = SV_CalcRoll(ent->s.angles, ent->velocity) * 4; + // [Paril-KEX] cl_rollhack + ent->s.angles[ROLL] = -SV_CalcRoll(ent->s.angles, ent->velocity) * 4; // // calculate speed and cycle to be used for @@ -1524,4 +1528,30 @@ void ClientEndServerFrame(edict_t *ent) G_SaveLagCompensation(ent); Compass_Update(ent, false); + + // [Paril-KEX] in coop, if player collision is enabled and + // we are currently in no-player-collision mode, check if + // it's safe. + if (coop->integer && G_ShouldPlayersCollide(false) && !(ent->clipmask & CONTENTS_PLAYER) && ent->takedamage) + { + bool clipped_player = false; + + for (auto player : active_players()) + { + if (player == ent) + continue; + + trace_t clip = gi.clip(player, ent->s.origin, ent->mins, ent->maxs, ent->s.origin, CONTENTS_MONSTER | CONTENTS_PLAYER); + + if (clip.startsolid || clip.allsolid) + { + clipped_player = true; + break; + } + } + + // safe! + if (!clipped_player) + ent->clipmask |= CONTENTS_PLAYER; + } } \ No newline at end of file diff --git a/rerelease/p_weapon.cpp b/rerelease/p_weapon.cpp index a166df79..d6594a22 100644 --- a/rerelease/p_weapon.cpp +++ b/rerelease/p_weapon.cpp @@ -27,7 +27,7 @@ bool G_CheckInfiniteAmmo(gitem_t *item) if (item->flags & IF_NO_INFINITE_AMMO) return false; - return g_infinite_ammo->integer || g_instagib->integer; + return g_infinite_ammo->integer || (deathmatch->integer && g_instagib->integer); } //======== @@ -456,7 +456,7 @@ void G_RemoveAmmo(edict_t *ent) // [Paril-KEX] get time per animation frame inline gtime_t Weapon_AnimationTime(edict_t *ent) { - if (g_quick_weapon_switch->integer && (gi.tick_rate == 20 || gi.tick_rate == 40) && + if (g_quick_weapon_switch->integer && (gi.tick_rate >= 20) && (ent->client->weaponstate == WEAPON_ACTIVATING || ent->client->weaponstate == WEAPON_DROPPING)) ent->client->ps.gunrate = 20; else @@ -635,6 +635,10 @@ Drop_Weapon */ void Drop_Weapon(edict_t *ent, gitem_t *item) { + // [Paril-KEX] + if (deathmatch->integer && g_dm_weapons_stay->integer) + return; + item_id_t index = item->id; // see if we're already using it if (((item == ent->client->pers.weapon) || (item == ent->client->newweapon)) && (ent->client->pers.inventory[index] == 1)) @@ -817,6 +821,7 @@ inline weapon_ready_state_t Weapon_HandleReady(edict_t *ent, int FRAME_FIRE_FIRS (ent->client->pers.inventory[ent->client->pers.weapon->ammo] >= ent->client->pers.weapon->quantity)) { ent->client->weaponstate = WEAPON_FIRING; + ent->client->last_firing_time = level.time + COOP_DAMAGE_FIRING_TIME; return READY_FIRING; } else @@ -928,6 +933,7 @@ void Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, if (ent->client->weaponstate == WEAPON_FIRING && ent->client->weapon_think_time <= level.time) { + ent->client->last_firing_time = level.time + COOP_DAMAGE_FIRING_TIME; ent->client->ps.gunframe++; Weapon_HandleFiring(ent, FRAME_IDLE_FIRST, [&]() { for (int n = 0; fire_frames[n]; n++) @@ -963,6 +969,7 @@ void Weapon_Repeating(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST if (ent->client->weaponstate == WEAPON_FIRING && ent->client->weapon_think_time <= level.time) { + ent->client->last_firing_time = level.time + COOP_DAMAGE_FIRING_TIME; Weapon_HandleFiring(ent, FRAME_IDLE_FIRST, [&]() { fire(ent); }); if (ent->client->weapon_thunk) @@ -1092,6 +1099,8 @@ void Throw_Generic(edict_t *ent, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int F if (ent->client->weaponstate == WEAPON_FIRING) { + ent->client->last_firing_time = level.time + COOP_DAMAGE_FIRING_TIME; + if (ent->client->weapon_think_time <= level.time) { if (prime_sound && ent->client->ps.gunframe == FRAME_PRIME_SOUND) @@ -1108,12 +1117,10 @@ void Throw_Generic(edict_t *ent, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int F if (ent->client->ps.gunframe == FRAME_THROW_HOLD) { if (!ent->client->grenade_time && !ent->client->grenade_finished_time) - { ent->client->grenade_time = level.time + GRENADE_TIMER + 200_ms; - if (primed_sound) - ent->client->weapon_sound = gi.soundindex(primed_sound); - } + if (primed_sound && !ent->client->grenade_blew_up) + ent->client->weapon_sound = gi.soundindex(primed_sound); // they waited too long, detonate it in their hand if (EXPLODE && !ent->client->grenade_blew_up && level.time >= ent->client->grenade_time) @@ -1775,8 +1782,19 @@ RAILGUN void weapon_railgun_fire(edict_t *ent) { - int damage = 100; - int kick = 200; + int damage, kick; + + // normal damage too extreme for DM + if (deathmatch->integer) + { + damage = 100; + kick = 200; + } + else + { + damage = 125; + kick = 225; + } if (is_quad) { diff --git a/rerelease/q_std.h b/rerelease/q_std.h index 9f976d67..a2e5215e 100644 --- a/rerelease/q_std.h +++ b/rerelease/q_std.h @@ -114,7 +114,7 @@ using std::clamp; template constexpr T lerp(T from, T to, float t) { - return t * from + (1.f - t) * to; + return (to * t) + (from * (1.f - t)); } // angle indexes diff --git a/rerelease/rogue/g_rogue_monster.cpp b/rerelease/rogue/g_rogue_monster.cpp index d333a067..e7e74b1c 100644 --- a/rerelease/rogue/g_rogue_monster.cpp +++ b/rerelease/rogue/g_rogue_monster.cpp @@ -60,7 +60,7 @@ USE(stationarymonster_triggered_spawn_use) (edict_t *self, edict_t *other, edict // we have a one frame delay here so we don't telefrag the guy who activated us self->think = stationarymonster_triggered_spawn; self->nextthink = level.time + FRAME_TIME_S; - if (activator->client) + if (activator && activator->client) self->enemy = activator; self->use = monster_use; } diff --git a/rerelease/rogue/g_rogue_newdm.cpp b/rerelease/rogue/g_rogue_newdm.cpp index 828c0a17..e735318e 100644 --- a/rerelease/rogue/g_rogue_newdm.cpp +++ b/rerelease/rogue/g_rogue_newdm.cpp @@ -91,26 +91,26 @@ inline item_id_t FindSubstituteItem(edict_t *ent) if (!itflags || (itflags & (IF_NOT_GIVEABLE | IF_TECH | IF_NOT_RANDOM)) || !it->pickup || !it->world_model) continue; - itflags = GetSubstituteItemFlags(i); - // don't respawn spheres if they're dmflag disabled. if (g_no_spheres->integer) { - if (ent->item->id == IT_ITEM_SPHERE_VENGEANCE || - ent->item->id == IT_ITEM_SPHERE_HUNTER || - ent->item->id == IT_ITEM_SPHERE_DEFENDER) + if (i == IT_ITEM_SPHERE_VENGEANCE || + i == IT_ITEM_SPHERE_HUNTER || + i == IT_ITEM_SPHERE_DEFENDER) { continue; } } - if (g_no_nukes->integer && ent->item->id == IT_AMMO_NUKE) + if (g_no_nukes->integer && i == IT_AMMO_NUKE) continue; if (g_no_mines->integer && - (ent->item->id == IT_AMMO_PROX || ent->item->id == IT_AMMO_TESLA || ent->item->id == IT_AMMO_TRAP)) + (i == IT_AMMO_PROX || i == IT_AMMO_TESLA || i == IT_AMMO_TRAP || i == IT_WEAPON_PROXLAUNCHER)) continue; + itflags = GetSubstituteItemFlags(i); + if ((itflags & IF_TYPE_MASK) == (myflags & IF_TYPE_MASK)) possible_items[possible_item_count++] = i; } diff --git a/rerelease/rogue/g_rogue_newweap.cpp b/rerelease/rogue/g_rogue_newweap.cpp index b23b3afb..19fab294 100644 --- a/rerelease/rogue/g_rogue_newweap.cpp +++ b/rerelease/rogue/g_rogue_newweap.cpp @@ -567,6 +567,8 @@ bool fire_player_melee(edict_t *self, const vec3_t &start, const vec3_t &aim, in if (!hit->inuse || !hit->takedamage) continue; + else if (!CanDamage(self, hit)) + continue; // do the damage vec3_t closest_point_to_check = closest_point_to_box(start, hit->s.origin + hit->mins, hit->s.origin + hit->maxs); diff --git a/rerelease/rogue/g_rogue_spawn.cpp b/rerelease/rogue/g_rogue_spawn.cpp index 58bdd929..612ca389 100644 --- a/rerelease/rogue/g_rogue_spawn.cpp +++ b/rerelease/rogue/g_rogue_spawn.cpp @@ -169,7 +169,7 @@ THINK(spawngrow_think) (edict_t *self) -> void float t = 1.f - ((level.time - self->teleport_time).seconds() / self->wait); - self->s.scale = clamp(lerp(self->accel, self->decel, t) / 16.f, 0.001f, 16.f); + self->s.scale = clamp(lerp(self->decel, self->accel, t) / 16.f, 0.001f, 16.f); self->s.alpha = t * t; self->nextthink += FRAME_TIME_MS; diff --git a/rerelease/rogue/m_rogue_carrier.cpp b/rerelease/rogue/m_rogue_carrier.cpp index d4d56ab3..b7a1598c 100644 --- a/rerelease/rogue/m_rogue_carrier.cpp +++ b/rerelease/rogue/m_rogue_carrier.cpp @@ -31,15 +31,15 @@ void drawbbox(edict_t *self); void ED_CallSpawn(edict_t *ent); -static int sound_pain1; -static int sound_pain2; -static int sound_pain3; -static int sound_death; -static int sound_sight; -static int sound_rail; -static int sound_spawn; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_pain3; +static cached_soundindex sound_death; +static cached_soundindex sound_sight; +static cached_soundindex sound_rail; +static cached_soundindex sound_spawn; -static int sound_cg_down, sound_cg_loop, sound_cg_up; +static cached_soundindex sound_cg_down, sound_cg_loop, sound_cg_up; float orig_yaw_speed; @@ -1002,48 +1002,9 @@ DIE(carrier_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int dama MONSTERINFO_CHECKATTACK(Carrier_CheckAttack) (edict_t *self) -> bool { - vec3_t spot1, spot2; - vec3_t temp; - float chance; - trace_t tr; - bool enemy_infront, enemy_inback, enemy_below; - float enemy_yaw; - - if (self->enemy->health > 0) - { - // see if any entities are in the way of the shot - spot1 = self->s.origin; - spot1[2] += self->viewheight; - spot2 = self->enemy->s.origin; - spot2[2] += self->enemy->viewheight; - - tr = gi.traceline(spot1, spot2, self, CONTENTS_SOLID | CONTENTS_PLAYER | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA); - - // do we have a clear shot? - if (tr.ent != self->enemy && !(tr.ent->svflags & SVF_PLAYER)) - { - // go ahead and spawn stuff if we're mad a a client - if (self->enemy->client && M_SlotsLeft(self) > 2) - { - self->monsterinfo.attack_state = AS_BLIND; - return true; - } - - // PGM - we want them to go ahead and shoot at info_notnulls if they can. - if (self->enemy->solid != SOLID_NOT || tr.fraction < 1.0f) // PGM - return false; - } - } - - enemy_infront = infront(self, self->enemy); - enemy_inback = inback(self, self->enemy); - enemy_below = below(self, self->enemy); - - float enemy_range = range_to(self, self->enemy); - temp = self->enemy->s.origin - self->s.origin; - enemy_yaw = vectoyaw(temp); - - self->ideal_yaw = enemy_yaw; + bool enemy_infront = infront(self, self->enemy); + bool enemy_inback = inback(self, self->enemy); + bool enemy_below = below(self, self->enemy); // PMM - shoot out the back if appropriate if ((enemy_inback) || (!enemy_infront && enemy_below)) @@ -1061,53 +1022,7 @@ MONSTERINFO_CHECKATTACK(Carrier_CheckAttack) (edict_t *self) -> bool } } - // melee attack - if (enemy_range <= RANGE_MELEE) - { - self->monsterinfo.attack_state = AS_MISSILE; - return true; - } - - // if (level.time < self->monsterinfo.attack_finished) - // return false; - - if (self->monsterinfo.aiflags & AI_STAND_GROUND) - { - chance = 0.4f; - } - else if (enemy_range <= RANGE_MELEE) - { - chance = 0.8f; - } - else if (enemy_range <= RANGE_NEAR) - { - chance = 0.8f; - } - else if (enemy_range <= RANGE_MID) - { - chance = 0.8f; - } - else - { - chance = 0.5f; - } - - // PGM - go ahead and shoot every time if it's a info_notnull - if ((frandom() < chance) || (self->enemy->solid == SOLID_NOT)) - { - self->monsterinfo.attack_state = AS_MISSILE; - return true; - } - - if (self->flags & FL_FLY) - { - if (frandom() < 0.6f) - self->monsterinfo.attack_state = AS_SLIDING; - else - self->monsterinfo.attack_state = AS_STRAIGHT; - } - - return false; + return M_CheckAttack_Base(self, 0.4f, 0.8f, 0.8f, 0.8f, 0.5f, 0.f); } void CarrierPrecache() @@ -1145,17 +1060,17 @@ void SP_monster_carrier(edict_t *self) return; } - sound_pain1 = gi.soundindex("carrier/pain_md.wav"); - sound_pain2 = gi.soundindex("carrier/pain_lg.wav"); - sound_pain3 = gi.soundindex("carrier/pain_sm.wav"); - sound_death = gi.soundindex("carrier/death.wav"); - sound_rail = gi.soundindex("gladiator/railgun.wav"); - sound_sight = gi.soundindex("carrier/sight.wav"); - sound_spawn = gi.soundindex("medic_commander/monsterspawn1.wav"); - - sound_cg_down = gi.soundindex("weapons/chngnd1a.wav"); - sound_cg_loop = gi.soundindex("weapons/chngnl1a.wav"); - sound_cg_up = gi.soundindex("weapons/chngnu1a.wav"); + sound_pain1.assign("carrier/pain_md.wav"); + sound_pain2.assign("carrier/pain_lg.wav"); + sound_pain3.assign("carrier/pain_sm.wav"); + sound_death.assign("carrier/death.wav"); + sound_rail.assign("gladiator/railgun.wav"); + sound_sight.assign("carrier/sight.wav"); + sound_spawn.assign("medic_commander/monsterspawn1.wav"); + + sound_cg_down.assign("weapons/chngnd1a.wav"); + sound_cg_loop.assign("weapons/chngnl1a.wav"); + sound_cg_up.assign("weapons/chngnu1a.wav"); self->monsterinfo.engine_sound = gi.soundindex("bosshovr/bhvengn1.wav"); diff --git a/rerelease/rogue/m_rogue_stalker.cpp b/rerelease/rogue/m_rogue_stalker.cpp index 951c6052..e4f00aa5 100644 --- a/rerelease/rogue/m_rogue_stalker.cpp +++ b/rerelease/rogue/m_rogue_stalker.cpp @@ -12,12 +12,12 @@ stalker #include "m_rogue_stalker.h" #include -static int sound_pain; -static int sound_die; -static int sound_sight; -static int sound_punch_hit1; -static int sound_punch_hit2; -static int sound_idle; +static cached_soundindex sound_pain; +static cached_soundindex sound_die; +static cached_soundindex sound_sight; +static cached_soundindex sound_punch_hit1; +static cached_soundindex sound_punch_hit2; +static cached_soundindex sound_idle; bool stalker_do_pounce(edict_t *self, const vec3_t &dest); void stalker_walk(edict_t *self); @@ -979,12 +979,12 @@ void SP_monster_stalker(edict_t *self) return; } - sound_pain = gi.soundindex("stalker/pain.wav"); - sound_die = gi.soundindex("stalker/death.wav"); - sound_sight = gi.soundindex("stalker/sight.wav"); - sound_punch_hit1 = gi.soundindex("stalker/melee1.wav"); - sound_punch_hit2 = gi.soundindex("stalker/melee2.wav"); - sound_idle = gi.soundindex("stalker/idle.wav"); + sound_pain.assign("stalker/pain.wav"); + sound_die.assign("stalker/death.wav"); + sound_sight.assign("stalker/sight.wav"); + sound_punch_hit1.assign("stalker/melee1.wav"); + sound_punch_hit2.assign("stalker/melee2.wav"); + sound_idle.assign("stalker/idle.wav"); // PMM - precache bolt2 gi.modelindex("models/objects/laser/tris.md2"); diff --git a/rerelease/rogue/m_rogue_turret.cpp b/rerelease/rogue/m_rogue_turret.cpp index b5e06b05..f55c521a 100644 --- a/rerelease/rogue/m_rogue_turret.cpp +++ b/rerelease/rogue/m_rogue_turret.cpp @@ -28,7 +28,7 @@ void turret_run(edict_t *self); extern const mmove_t turret_move_fire; extern const mmove_t turret_move_fire_blind; -static int sound_moved, sound_moving; +static cached_soundindex sound_moved, sound_moving; void TurretAim(edict_t *self) { @@ -399,18 +399,10 @@ void TurretFire(edict_t *self) chance = frandom(); - // rockets fire less often than the others do. if (self->spawnflags.has(SPAWNFLAG_TURRET_ROCKET)) - { - chance = chance * 3; - rocketSpeed = 650; - } else if (self->spawnflags.has(SPAWNFLAG_TURRET_BLASTER)) - { rocketSpeed = 800; - chance = chance * 2; - } else rocketSpeed = 0; @@ -500,16 +492,11 @@ void TurretFireBlind(edict_t *self) return; if (self->spawnflags.has(SPAWNFLAG_TURRET_ROCKET)) - { - if (skill->integer == 2) - { - rocketSpeed += (int) frandom(200); - } - else if (skill->integer == 3) - { - rocketSpeed += (int) frandom(100, 300); - } - } + rocketSpeed = 650; + else if (self->spawnflags.has(SPAWNFLAG_TURRET_BLASTER)) + rocketSpeed = 800; + else + rocketSpeed = 0; start = self->s.origin; end = self->monsterinfo.blind_fire_target; @@ -524,9 +511,9 @@ void TurretFireBlind(edict_t *self) dir.normalize(); if (self->spawnflags.has(SPAWNFLAG_TURRET_BLASTER)) - monster_fire_blaster(self, start, dir, 20, 1000, MZ2_TURRET_BLASTER, EF_BLASTER); + monster_fire_blaster(self, start, dir, TURRET_BLASTER_DAMAGE, rocketSpeed, MZ2_TURRET_BLASTER, EF_BLASTER); else if (self->spawnflags.has(SPAWNFLAG_TURRET_ROCKET)) - monster_fire_rocket(self, start, dir, 50, rocketSpeed, MZ2_TURRET_ROCKET); + monster_fire_rocket(self, start, dir, 40, rocketSpeed, MZ2_TURRET_ROCKET); } // pmm @@ -623,7 +610,7 @@ DIE(turret_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damag if (self->teamchain) { base = self->teamchain; - base->solid = SOLID_BBOX; + base->solid = SOLID_NOT; base->takedamage = false; base->movetype = MOVETYPE_NONE; base->teammaster = base; @@ -937,8 +924,8 @@ void SP_monster_turret(edict_t *self) } // pre-caches - sound_moved = gi.soundindex("turret/moved.wav"); - sound_moving = gi.soundindex("turret/moving.wav"); + sound_moved.assign("turret/moved.wav"); + sound_moving.assign("turret/moving.wav"); gi.modelindex("models/objects/debris1/tris.md2"); self->s.modelindex = gi.modelindex("models/monsters/turret/tris.md2"); diff --git a/rerelease/rogue/m_rogue_widow.cpp b/rerelease/rogue/m_rogue_widow.cpp index 0ed1d464..e13858c3 100644 --- a/rerelease/rogue/m_rogue_widow.cpp +++ b/rerelease/rogue/m_rogue_widow.cpp @@ -22,10 +22,10 @@ constexpr int WIDOW_RAIL_DAMAGE = 50; bool infront(edict_t *self, edict_t *other); -static int sound_pain1; -static int sound_pain2; -static int sound_pain3; -static int sound_rail; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_pain3; +static cached_soundindex sound_rail; static uint32_t shotsfired; @@ -1126,13 +1126,6 @@ void WidowPowerups(edict_t *self) MONSTERINFO_CHECKATTACK(Widow_CheckAttack) (edict_t *self) -> bool { - vec3_t spot1, spot2; - vec3_t temp; - float chance; - trace_t tr; - float enemy_yaw; - float real_enemy_range; - if (!self->enemy) return false; @@ -1165,85 +1158,7 @@ MONSTERINFO_CHECKATTACK(Widow_CheckAttack) (edict_t *self) -> bool return true; } - if (self->enemy->health > 0) - { - // see if any entities are in the way of the shot - spot1 = self->s.origin; - spot1[2] += self->viewheight; - spot2 = self->enemy->s.origin; - spot2[2] += self->enemy->viewheight; - - tr = gi.traceline(spot1, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_PLAYER | CONTENTS_SLIME | CONTENTS_LAVA); - - // do we have a clear shot? - if (tr.ent != self->enemy && !(tr.ent->svflags & SVF_PLAYER)) - { - // go ahead and spawn stuff if we're mad a a client - if (self->enemy->client && M_SlotsLeft(self) >= 2) - { - self->monsterinfo.attack_state = AS_BLIND; - return true; - } - - // PGM - we want them to go ahead and shoot at info_notnulls if they can. - if (self->enemy->solid != SOLID_NOT || tr.fraction < 1.0f) // PGM - return false; - } - } - - float enemy_range = range_to(self, self->enemy); - temp = self->enemy->s.origin - self->s.origin; - enemy_yaw = vectoyaw(temp); - - self->ideal_yaw = enemy_yaw; - - real_enemy_range = realrange(self, self->enemy); - - // melee attack - if (real_enemy_range <= (MELEE_DISTANCE + 20)) - { - // don't always melee in easy mode - if (skill->integer == 0 && irandom(4)) - return false; - if (self->monsterinfo.melee) - self->monsterinfo.attack_state = AS_MELEE; - else - self->monsterinfo.attack_state = AS_MISSILE; - return true; - } - - if (level.time < self->monsterinfo.attack_finished) - return false; - - if (self->monsterinfo.aiflags & AI_STAND_GROUND) - { - chance = 0.4f; - } - else if (enemy_range <= RANGE_MELEE) - { - chance = 0.8f; - } - else if (enemy_range <= RANGE_NEAR) - { - chance = 0.7f; - } - else if (enemy_range <= RANGE_MID) - { - chance = 0.6f; - } - else - { - chance = 0.5f; - } - - // PGM - go ahead and shoot every time if it's a info_notnull - if ((frandom() < chance) || (self->enemy->solid == SOLID_NOT)) - { - self->monsterinfo.attack_state = AS_MISSILE; - return true; - } - - return false; + return M_CheckAttack_Base(self, 0.4f, 0.8f, 0.7f, 0.6f, 0.5f, 0.f); } MONSTERINFO_BLOCKED(widow_blocked) (edict_t *self, float dist) -> bool @@ -1333,10 +1248,10 @@ void SP_monster_widow(edict_t *self) return; } - sound_pain1 = gi.soundindex("widow/bw1pain1.wav"); - sound_pain2 = gi.soundindex("widow/bw1pain2.wav"); - sound_pain3 = gi.soundindex("widow/bw1pain3.wav"); - sound_rail = gi.soundindex("gladiator/railgun.wav"); + sound_pain1.assign("widow/bw1pain1.wav"); + sound_pain2.assign("widow/bw1pain2.wav"); + sound_pain3.assign("widow/bw1pain3.wav"); + sound_rail.assign("gladiator/railgun.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; diff --git a/rerelease/rogue/m_rogue_widow2.cpp b/rerelease/rogue/m_rogue_widow2.cpp index 44175084..b0fba77d 100644 --- a/rerelease/rogue/m_rogue_widow2.cpp +++ b/rerelease/rogue/m_rogue_widow2.cpp @@ -14,12 +14,12 @@ black widow, part 2 #include "m_rogue_widow2.h" #include "../m_flash.h" -static int sound_pain1; -static int sound_pain2; -static int sound_pain3; -static int sound_death; -static int sound_search1; -static int sound_tentacles_retract; +static cached_soundindex sound_pain1; +static cached_soundindex sound_pain2; +static cached_soundindex sound_pain3; +static cached_soundindex sound_death; +static cached_soundindex sound_search1; +static cached_soundindex sound_tentacles_retract; // sqrt(64*64*2) + sqrt(28*28*2) => 130.1 constexpr vec3_t spawnpoints[] = { @@ -685,14 +685,19 @@ MONSTERINFO_WALK(widow2_walk) (edict_t *self) -> void M_SetAnimation(self, &widow2_move_walk); } +void widow2_attack(edict_t *self); + MONSTERINFO_MELEE(widow2_melee) (edict_t *self) -> void { - M_SetAnimation(self, &widow2_move_tongs); + if (self->timestamp >= level.time) + widow2_attack(self); + else + M_SetAnimation(self, &widow2_move_tongs); } MONSTERINFO_ATTACK(widow2_attack) (edict_t *self) -> void { - float range, luck; + float luck; bool blocked = false; if (self->monsterinfo.aiflags & AI_BLOCKED) @@ -704,6 +709,31 @@ MONSTERINFO_ATTACK(widow2_attack) (edict_t *self) -> void if (!self->enemy) return; + float real_enemy_range = realrange(self, self->enemy); + + // melee attack + if (self->timestamp < level.time) + { + if (real_enemy_range < 300) + { + vec3_t f, r, u; + AngleVectors(self->s.angles, f, r, u); + vec3_t spot1 = G_ProjectSource2(self->s.origin, offsets[0], f, r, u); + vec3_t spot2 = self->enemy->s.origin; + if (widow2_tongue_attack_ok(spot1, spot2, 256)) + { + // melee attack ok + + // be nice in easy mode + if (skill->integer != 0 || irandom(4)) + { + M_SetAnimation(self, &widow2_move_tongs); + return; + } + } + } + } + if (self->bad_area) { if ((frandom() < 0.75f) || (level.time < self->monsterinfo.attack_finished)) @@ -731,9 +761,7 @@ MONSTERINFO_ATTACK(widow2_attack) (edict_t *self) -> void return; } - range = realrange(self, self->enemy); - - if (range < 600) + if (real_enemy_range < 600) { luck = frandom(); if (M_SlotsLeft(self) >= 2) @@ -924,14 +952,6 @@ DIE(widow2_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damag MONSTERINFO_CHECKATTACK(Widow2_CheckAttack) (edict_t *self) -> bool { - vec3_t spot1, spot2; - vec3_t temp; - float chance; - trace_t tr; - float enemy_yaw; - float real_enemy_range; - vec3_t f, r, u; - if (!self->enemy) return false; @@ -944,92 +964,7 @@ MONSTERINFO_CHECKATTACK(Widow2_CheckAttack) (edict_t *self) -> bool return true; } - if (self->enemy->health > 0) - { - // see if any entities are in the way of the shot - spot1 = self->s.origin; - spot1[2] += self->viewheight; - spot2 = self->enemy->s.origin; - spot2[2] += self->enemy->viewheight; - - tr = gi.traceline(spot1, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_PLAYER | CONTENTS_SLIME | CONTENTS_LAVA); - - // do we have a clear shot? - if (tr.ent != self->enemy && !(tr.ent->svflags & SVF_PLAYER)) - { - // go ahead and spawn stuff if we're mad a a client - if (self->enemy->client && M_SlotsLeft(self) >= 2) - { - self->monsterinfo.attack_state = AS_BLIND; - return true; - } - - // PGM - we want them to go ahead and shoot at info_notnulls if they can. - if (self->enemy->solid != SOLID_NOT || tr.fraction < 1.0f) // PGM - return false; - } - } - - float enemy_range = range_to(self, self->enemy); - temp = self->enemy->s.origin - self->s.origin; - enemy_yaw = vectoyaw(temp); - - self->ideal_yaw = enemy_yaw; - - // melee attack - if (self->timestamp < level.time) - { - real_enemy_range = realrange(self, self->enemy); - if (real_enemy_range < 300) - { - AngleVectors(self->s.angles, f, r, u); - spot1 = G_ProjectSource2(self->s.origin, offsets[0], f, r, u); - spot2 = self->enemy->s.origin; - if (widow2_tongue_attack_ok(spot1, spot2, 256)) - { - // melee attack ok - - // be nice in easy mode - if (skill->integer == 0 && irandom(4)) - return false; - - if (self->monsterinfo.melee) - self->monsterinfo.attack_state = AS_MELEE; - else - self->monsterinfo.attack_state = AS_MISSILE; - return true; - } - } - } - - if (level.time < self->monsterinfo.attack_finished) - return false; - - if (self->monsterinfo.aiflags & AI_STAND_GROUND) - { - chance = 0.4f; - } - else if (enemy_range <= RANGE_NEAR) - { - chance = 0.8f; - } - else if (enemy_range <= RANGE_MID) - { - chance = 0.8f; - } - else - { - chance = 0.5f; - } - - // PGM - go ahead and shoot every time if it's a info_notnull - if ((frandom() < chance) || (self->enemy->solid == SOLID_NOT)) - { - self->monsterinfo.attack_state = AS_MISSILE; - return true; - } - - return false; + return M_CheckAttack_Base(self, 0.4f, 0.8f, 0.8f, 0.5f, 0.f, 0.f); } void Widow2Precache() @@ -1072,12 +1007,12 @@ void SP_monster_widow2(edict_t *self) return; } - sound_pain1 = gi.soundindex("widow/bw2pain1.wav"); - sound_pain2 = gi.soundindex("widow/bw2pain2.wav"); - sound_pain3 = gi.soundindex("widow/bw2pain3.wav"); - sound_death = gi.soundindex("widow/death.wav"); - sound_search1 = gi.soundindex("bosshovr/bhvunqv1.wav"); - sound_tentacles_retract = gi.soundindex("brain/brnatck3.wav"); + sound_pain1.assign("widow/bw2pain1.wav"); + sound_pain2.assign("widow/bw2pain2.wav"); + sound_pain3.assign("widow/bw2pain3.wav"); + sound_death.assign("widow/death.wav"); + sound_search1.assign("bosshovr/bhvunqv1.wav"); + sound_tentacles_retract.assign("brain/brnatck3.wav"); // self->s.sound = gi.soundindex ("bosshovr/bhvengn1.wav"); diff --git a/rerelease/rogue/rogue_dm_ball.cpp b/rerelease/rogue/rogue_dm_ball.cpp index e20fb09b..895117db 100644 --- a/rerelease/rogue/rogue_dm_ball.cpp +++ b/rerelease/rogue/rogue_dm_ball.cpp @@ -8,7 +8,7 @@ // defines -constexpr spawnflags_t SPAWNFLAG_DBALL_GOAL_TEAM1 = 0x0001_spawnflag; +[[maybe_unused]] constexpr spawnflags_t SPAWNFLAG_DBALL_GOAL_TEAM1 = 0x0001_spawnflag; // unused; assumed by not being team1 // constexpr uint32_t SPAWNFLAG_DBALL_GOAL_TEAM2 = 0x0002; diff --git a/rerelease/xatrix/g_xatrix_misc.cpp b/rerelease/xatrix/g_xatrix_misc.cpp index 10277ce9..eb2cd07d 100644 --- a/rerelease/xatrix/g_xatrix_misc.cpp +++ b/rerelease/xatrix/g_xatrix_misc.cpp @@ -117,7 +117,7 @@ void SP_misc_transport(edict_t *ent) /*QUAKED misc_amb4 (1 0 0) (-16 -16 -16) (16 16 16) Mal's amb4 loop entity */ -static int amb4sound; +static cached_soundindex amb4sound; THINK(amb4_think) (edict_t *ent) -> void { @@ -129,7 +129,7 @@ void SP_misc_amb4(edict_t *ent) { ent->think = amb4_think; ent->nextthink = level.time + 1_sec; - amb4sound = gi.soundindex("world/amb4.wav"); + amb4sound.assign("world/amb4.wav"); gi.linkentity(ent); } diff --git a/rerelease/xatrix/m_xatrix_fixbot.cpp b/rerelease/xatrix/m_xatrix_fixbot.cpp index c446a53c..9eab0fcd 100644 --- a/rerelease/xatrix/m_xatrix_fixbot.cpp +++ b/rerelease/xatrix/m_xatrix_fixbot.cpp @@ -11,11 +11,11 @@ bool infront(edict_t *self, edict_t *other); bool FindTarget(edict_t *self); -static int sound_pain1; -static int sound_die; -static int sound_weld1; -static int sound_weld2; -static int sound_weld3; +static cached_soundindex sound_pain1; +static cached_soundindex sound_die; +static cached_soundindex sound_weld1; +static cached_soundindex sound_weld2; +static cached_soundindex sound_weld3; void fixbot_run(edict_t *self); void fixbot_attack(edict_t *self); @@ -1335,6 +1335,8 @@ PAIN(fixbot_pain) (edict_t *self, edict_t *other, float kick, int damage, const M_SetAnimation(self, &fixbot_move_painb); else M_SetAnimation(self, &fixbot_move_paina); + + abortHeal(self, false, false, false); } void fixbot_dead(edict_t *self) @@ -1364,12 +1366,12 @@ void SP_monster_fixbot(edict_t *self) return; } - sound_pain1 = gi.soundindex("flyer/flypain1.wav"); - sound_die = gi.soundindex("flyer/flydeth1.wav"); + sound_pain1.assign("flyer/flypain1.wav"); + sound_die.assign("flyer/flydeth1.wav"); - sound_weld1 = gi.soundindex("misc/welder1.wav"); - sound_weld2 = gi.soundindex("misc/welder2.wav"); - sound_weld3 = gi.soundindex("misc/welder3.wav"); + sound_weld1.assign("misc/welder1.wav"); + sound_weld2.assign("misc/welder2.wav"); + sound_weld3.assign("misc/welder3.wav"); self->s.modelindex = gi.modelindex("models/monsters/fixbot/tris.md2"); diff --git a/rerelease/xatrix/m_xatrix_gekk.cpp b/rerelease/xatrix/m_xatrix_gekk.cpp index 5e3055b4..f4acd168 100644 --- a/rerelease/xatrix/m_xatrix_gekk.cpp +++ b/rerelease/xatrix/m_xatrix_gekk.cpp @@ -12,22 +12,22 @@ constexpr spawnflags_t SPAWNFLAG_GEKK_CHANT = 8_spawnflag; constexpr spawnflags_t SPAWNFLAG_GEKK_NOJUMPING = 16_spawnflag; constexpr spawnflags_t SPAWNFLAG_GEKK_NOSWIM = 32_spawnflag; -static int sound_swing; -static int sound_hit; -static int sound_hit2; -static int sound_speet; -static int loogie_hit; -static int sound_death; -static int sound_pain1; -static int sound_sight; -static int sound_search; -static int sound_step1; -static int sound_step2; -static int sound_step3; -static int sound_thud; -static int sound_chantlow; -static int sound_chantmid; -static int sound_chanthigh; +static cached_soundindex sound_swing; +static cached_soundindex sound_hit; +static cached_soundindex sound_hit2; +static cached_soundindex sound_speet; +static cached_soundindex loogie_hit; +static cached_soundindex sound_death; +static cached_soundindex sound_pain1; +static cached_soundindex sound_sight; +static cached_soundindex sound_search; +static cached_soundindex sound_step1; +static cached_soundindex sound_step2; +static cached_soundindex sound_step3; +static cached_soundindex sound_thud; +static cached_soundindex sound_chantlow; +static cached_soundindex sound_chantmid; +static cached_soundindex sound_chanthigh; void gekk_swim(edict_t *self); @@ -1589,23 +1589,23 @@ void SP_monster_gekk(edict_t *self) return; } - sound_swing = gi.soundindex("gek/gk_atck1.wav"); - sound_hit = gi.soundindex("gek/gk_atck2.wav"); - sound_hit2 = gi.soundindex("gek/gk_atck3.wav"); - sound_speet = gi.soundindex("gek/gk_atck4.wav"); - loogie_hit = gi.soundindex("gek/loogie_hit.wav"); - sound_death = gi.soundindex("gek/gk_deth1.wav"); - sound_pain1 = gi.soundindex("gek/gk_pain1.wav"); - sound_sight = gi.soundindex("gek/gk_sght1.wav"); - sound_search = gi.soundindex("gek/gk_idle1.wav"); - sound_step1 = gi.soundindex("gek/gk_step1.wav"); - sound_step2 = gi.soundindex("gek/gk_step2.wav"); - sound_step3 = gi.soundindex("gek/gk_step3.wav"); - sound_thud = gi.soundindex("mutant/thud1.wav"); - - sound_chantlow = gi.soundindex("gek/gek_low.wav"); - sound_chantmid = gi.soundindex("gek/gek_mid.wav"); - sound_chanthigh = gi.soundindex("gek/gek_high.wav"); + sound_swing.assign("gek/gk_atck1.wav"); + sound_hit.assign("gek/gk_atck2.wav"); + sound_hit2.assign("gek/gk_atck3.wav"); + sound_speet.assign("gek/gk_atck4.wav"); + loogie_hit.assign("gek/loogie_hit.wav"); + sound_death.assign("gek/gk_deth1.wav"); + sound_pain1.assign("gek/gk_pain1.wav"); + sound_sight.assign("gek/gk_sght1.wav"); + sound_search.assign("gek/gk_idle1.wav"); + sound_step1.assign("gek/gk_step1.wav"); + sound_step2.assign("gek/gk_step2.wav"); + sound_step3.assign("gek/gk_step3.wav"); + sound_thud.assign("mutant/thud1.wav"); + + sound_chantlow.assign("gek/gek_low.wav"); + sound_chantmid.assign("gek/gek_mid.wav"); + sound_chanthigh.assign("gek/gek_high.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX;