Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditionally prevent damaging and spell auto-targeting Golem #7010

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
28 changes: 22 additions & 6 deletions Source/missiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ bool MonsterMHit(const Player &player, int monsterId, int mindam, int maxdam, in
{
auto &monster = Monsters[monsterId];

if (!monster.isPossibleToHit() || monster.isImmune(t, damageType))
if (!monster.isPossibleToHit() || monster.isImmune(t, damageType) || !monster.canBeDamagedByPlayer(player))
return false;

int hit = RandomIntLessThan(100);
Expand Down Expand Up @@ -3532,9 +3532,15 @@ void ProcessChainLightning(Missile &missile)
int rad = std::min<int>(missile._mispllvl + 3, MaxCrawlRadius);
Crawl(1, rad, [&](Displacement displacement) {
Point target = position + displacement;
if (InDungeonBounds(target) && dMonster[target.x][target.y] > 0) {
dir = GetDirection(position, target);
AddMissile(position, target, dir, MissileID::LightningControl, TARGET_MONSTERS, id, 1, missile._mispllvl);
const auto &monsterId = dMonster[target.x][target.y];
if (InDungeonBounds(target) && monsterId > 0) {
const auto &player = Players[id];
const auto &monster = Monsters[std::abs(monsterId - 1)];
// Should we also be checking isPossibleToHit() and isImmune()?
if (monster.canBeDamagedByPlayer(player)) {
dir = GetDirection(position, target);
AddMissile(position, target, dir, MissileID::LightningControl, TARGET_MONSTERS, id, 1, missile._mispllvl);
}
}
return false;
});
Expand Down Expand Up @@ -4029,13 +4035,19 @@ void ProcessElemental(Missile &missile)
if (missile.var3 == 1) {
missile.var3 = 2;
missile._mirange = 255;

auto *player = missile.sourcePlayer();
auto *nextMonster = FindClosest(missilePosition, 19);
if (nextMonster != nullptr) {

// Should we also be checking isPossibleToHit() and isImmune()?
if (nextMonster != nullptr && nextMonster->canBeDamagedByPlayer(*player)) {
Direction sd = GetDirection(missilePosition, nextMonster->position.tile);

SetMissDir(missile, sd);
UpdateMissileVelocity(missile, nextMonster->position.tile, 16);
} else {
Direction sd = Players[missile._misource]._pdir;

SetMissDir(missile, sd);
UpdateMissileVelocity(missile, missilePosition + sd, 16);
}
Expand Down Expand Up @@ -4074,13 +4086,17 @@ void ProcessBoneSpirit(Missile &missile)
if (missile.var3 == 1) {
missile.var3 = 2;
missile._mirange = 255;
auto *player = missile.sourcePlayer();
auto *monster = FindClosest(c, 19);
if (monster != nullptr) {

// Should we also be checking isPossibleToHit() and isImmune()?
if (monster != nullptr && monster->canBeDamagedByPlayer(*player)) {
missile._midam = monster->hitPoints >> 7;
SetMissDir(missile, GetDirection(c, monster->position.tile));
UpdateMissileVelocity(missile, monster->position.tile, 16);
} else {
Direction sd = Players[missile._misource]._pdir;

SetMissDir(missile, sd);
UpdateMissileVelocity(missile, c + sd, 16);
}
Expand Down
6 changes: 6 additions & 0 deletions Source/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4829,6 +4829,12 @@ bool Monster::isPlayerMinion() const
return (flags & MFLAG_GOLEM) != 0 && (flags & MFLAG_BERSERK) == 0;
}

bool Monster::canBeDamagedByPlayer(const Player &player) const
{
return !(player.getId() == getId()
|| (isPlayerMinion() && sgGameInitInfo.bFriendlyFire == 0 && player.friendlyMode));
}

bool Monster::isPossibleToHit() const
{
return !(hitPoints >> 6 <= 0
Expand Down
8 changes: 8 additions & 0 deletions Source/monster.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,14 @@ struct Monster { // note: missing field _mAFNum
*/
[[nodiscard]] bool isPlayerMinion() const;

/**
* @brief Can this monster be damaged by the player?
*
* Used for Golems to determine whether or not a player is able to damage it.
* Conditions are set based on Friendly Fire, Player Attack/Friend, and ownership.
*/
[[nodiscard]] bool canBeDamagedByPlayer(const Player &player) const;

bool isPossibleToHit() const;
void tag(const Player &tagger);

Expand Down
7 changes: 6 additions & 1 deletion Source/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ bool PlrHitMonst(Player &player, Monster &monster, bool adjacentDamage = false)
{
int hper = 0;

if (!monster.isPossibleToHit())
if (!monster.isPossibleToHit() || !monster.canBeDamagedByPlayer(player))
return false;

if (adjacentDamage) {
Expand Down Expand Up @@ -2096,6 +2096,11 @@ bool Player::isLevelOwnedByLocalClient() const
return false;
}

Monster &Player::golem() const
{
return Monsters[getId()];
}

Player *PlayerAtPosition(Point position, bool ignoreMovingPlayers /*= false*/)
{
if (!InDungeonBounds(position))
Expand Down
3 changes: 3 additions & 0 deletions Source/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,9 @@ struct Player {

/** @brief Checks if the player level is owned by local client. */
bool isLevelOwnedByLocalClient() const;

/** @brief Returns a reference to the Golem owned by the player. */
Monster &golem() const;
};

extern DVL_API_FOR_TEST uint8_t MyPlayerId;
Expand Down
6 changes: 6 additions & 0 deletions Source/spells.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ int GetManaAmount(const Player &player, SpellID sn)

void ConsumeSpell(Player &player, SpellID sn)
{
if (sn == SpellID::Golem && &player == MyPlayer) {
Monster &golem = player.golem();

if (golem.hitPoints <= 0)
return;
}
switch (player.executedSpell.spellType) {
case SpellType::Skill:
case SpellType::Invalid:
Expand Down
Loading