diff --git a/src/g_newdm.c b/src/g_newdm.c index 204fd78..5b7fb96 100644 --- a/src/g_newdm.c +++ b/src/g_newdm.c @@ -17,6 +17,7 @@ extern qboolean Pickup_Health(edict_t *ent, edict_t *other); extern qboolean Pickup_Adrenaline(edict_t *ent, edict_t *other); extern qboolean Pickup_Armor(edict_t *ent, edict_t *other); extern qboolean Pickup_PowerArmor(edict_t *ent, edict_t *other); +extern qboolean Pickup_Sphere(edict_t *ent, edict_t *other); extern edict_t *Sphere_Spawn(edict_t *owner, int spawnflags); extern void ED_CallSpawn(edict_t *ent); void fire_doppleganger(edict_t *ent, vec3_t start, vec3_t aimdir); @@ -68,6 +69,39 @@ InitGameRules(void) } } +/* + * These dmflag filters decide which item may be substituted IN, so they test + * the candidate item. The original tested ent->classname, the item being + * replaced, which is loop invariant and the inverse of what the comment on the + * sphere filter says: a disabled sphere respawned as itself, while every other + * item could still be substituted into one. Both passes share this predicate + * so that the counting pass and the picking pass cannot disagree on which + * items are eligible. + */ +static qboolean +SubstituteItemAllowed(const gitem_t *it) +{ + /* don't respawn spheres if they're dmflag disabled. */ + if (((int)dmflags->value & DF_NO_SPHERES) && (it->pickup == Pickup_Sphere)) + { + return false; + } + + if (((int)dmflags->value & DF_NO_NUKES) && + !strcmp(it->classname, "ammo_nuke")) + { + return false; + } + + if (((int)dmflags->value & DF_NO_MINES) && + (!strcmp(it->classname, "ammo_prox") || !strcmp(it->classname, "ammo_tesla"))) + { + return false; + } + + return true; +} + const char * FindSubstituteItem(edict_t *ent) { @@ -168,25 +202,7 @@ FindSubstituteItem(edict_t *ent) itflags = IT_AMMO; } - /* don't respawn spheres if they're dmflag disabled. */ - if ((int)dmflags->value & DF_NO_SPHERES) - { - if (!strcmp(ent->classname, "item_sphere_vengeance") || - !strcmp(ent->classname, "item_sphere_hunter") || - !strcmp(ent->classname, "item_spehre_defender")) - { - continue; - } - } - - if (((int)dmflags->value & DF_NO_NUKES) && - !strcmp(ent->classname, "ammo_nuke")) - { - continue; - } - - if (((int)dmflags->value & DF_NO_MINES) && - (!strcmp(ent->classname, "ammo_prox") || !strcmp(ent->classname, "ammo_tesla"))) + if (!SubstituteItemAllowed(it)) { continue; } @@ -223,14 +239,7 @@ FindSubstituteItem(edict_t *ent) itflags = IT_AMMO; } - if (((int)dmflags->value & DF_NO_NUKES) && - !strcmp(ent->classname, "ammo_nuke")) - { - continue; - } - - if (((int)dmflags->value & DF_NO_MINES) && - (!strcmp(ent->classname, "ammo_prox") || !strcmp(ent->classname, "ammo_tesla"))) + if (!SubstituteItemAllowed(it)) { continue; } diff --git a/src/monster/misc/move.c b/src/monster/misc/move.c index 146a62f..61b2390 100644 --- a/src/monster/misc/move.c +++ b/src/monster/misc/move.c @@ -447,8 +447,14 @@ SV_movestep(edict_t *ent, vec3_t move, qboolean relink) { if (new_bad->owner && !strcmp(new_bad->owner->classname, "tesla")) { + /* A monster that is already angry at a tesla is left alone. + The original guarded that case with a misspelled "telsa", + so the branch never ran and the monster was retargeted at + the blocking tesla instead, which is the behaviour this + condition inherited when the chain was collapsed. */ if (!ent->enemy || !ent->enemy->inuse || - !ent->enemy->client || !visible(ent, ent->enemy)) + (strcmp(ent->enemy->classname, "tesla") && + (!ent->enemy->client || !visible(ent, ent->enemy)))) { TargetTesla(ent, new_bad->owner); ent->monsterinfo.aiflags |= AI_BLOCKED;