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

Fix some Gothic 1 magic bugs #682

Merged
merged 5 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions game/graphics/effect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,18 +306,27 @@ void Effect::onCollide(World& world, const VisualFx* root, const Vec3& pos, Npc*
if(npc!=nullptr)
vfx = root->emFXCollDyn;

// Gothic 1 uses emFXCollDyn->sendAssessMagic as check for PERC_ASSESSMAGIC
// Gothic 2 instead introduced new vfx emFXCollDynPerc specific for this purpose
bool g2 = world.version().game==2;
if(vfx!=nullptr) {
Effect eff(*vfx,world,pos,SpellFxKey::Collide);
eff.setSpellId(splId,world);
eff.setOrigin(other);
eff.setActive(true);

if(npc!=nullptr)
npc ->runEffect(std::move(eff)); else
if(npc!=nullptr) {
npc ->runEffect(std::move(eff));
if(!g2 && vfx->sendAssessMagic) {
auto oth = other==nullptr ? npc : other;
npc->perceptionProcess(*oth,npc,0,PERC_ASSESSMAGIC);
Try marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
world.runEffect(std::move(eff));
}
}

if(npc!=nullptr && root->emFXCollDynPerc!=nullptr) {
if(g2 && npc!=nullptr && root->emFXCollDynPerc!=nullptr) {
const VisualFx* vfx = root->emFXCollDynPerc;
Effect eff(*vfx,world,pos,SpellFxKey::Collide);
eff.setActive(true);
Expand Down
11 changes: 10 additions & 1 deletion game/world/objects/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "game/serialize.h"
#include "game/gamescript.h"
#include "utils/versioninfo.h"
#include "world/objects/npc.h"
#include "world/world.h"
#include "utils/fileext.h"
Expand Down Expand Up @@ -204,12 +205,20 @@ bool Item::isMulti() const {
}

bool Item::isSpellShoot() const {
// Whether a spell is a projectile is hardcoded in vanilla, see
Try marked this conversation as resolved.
Show resolved Hide resolved
// https://forum.worldofplayers.de/forum/threads/1460092-Stuck-in-a-charge-spell/page2?p=24786462&viewfull=1#post24786462
// Only hardcode Stormfist for now since other spells work with the heuristic based on target_collect_algo
if(!isSpellOrRune())
return false;
bool g1 = world.version().game==1;
const int32_t SPL_STORMFIST = 47;
if(g1 && spellId()==SPL_STORMFIST)
return true;
auto& spl = world.script().spellDesc(spellId());
return spl.target_collect_algo!=TargetCollect::TARGET_COLLECT_NONE &&
spl.target_collect_algo!=TargetCollect::TARGET_COLLECT_CASTER &&
spl.target_collect_algo!=TargetCollect::TARGET_COLLECT_FOCUS;
spl.target_collect_algo!=TargetCollect::TARGET_COLLECT_FOCUS &&
spl.target_collect_algo!=TargetCollect::TARGET_COLLECT_ALL_FALLBACK_NONE;
}

bool Item::isSpellOrRune() const {
Expand Down
35 changes: 20 additions & 15 deletions game/world/world.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,18 +417,20 @@ Focus World::validateFocus(const Focus &def) {
}

Focus World::findFocus(const Npc &pl, const Focus& def) {
auto opt = WorldObjects::NoFlg;
auto coll = TARGET_COLLECT_FOCUS;
auto& policy = searchPolicy(pl,coll,opt);
auto opt = WorldObjects::NoFlg;
auto collAlgo = TARGET_COLLECT_FOCUS;
auto collType = TARGET_TYPE_ALL;
auto& policy = searchPolicy(pl,collAlgo,collType,opt);

WorldObjects::SearchOpt optNpc {policy.npc_range1, policy.npc_range2, policy.npc_azi, coll, opt};
WorldObjects::SearchOpt optMob {policy.mob_range1, policy.mob_range2, policy.mob_azi, coll };
WorldObjects::SearchOpt optItm {policy.item_range1, policy.item_range2, policy.item_azi, coll };
WorldObjects::SearchOpt optNpc {policy.npc_range1, policy.npc_range2, policy.npc_azi, collAlgo, collType, opt};
WorldObjects::SearchOpt optMob {policy.mob_range1, policy.mob_range2, policy.mob_azi, collAlgo};
WorldObjects::SearchOpt optItm {policy.item_range1, policy.item_range2, policy.item_azi, collAlgo, collType};

auto n = policy.npc_prio <0 ? nullptr : wobj.findNpcNear (pl,def.npc, optNpc);
auto it = policy.item_prio<0 ? nullptr : wobj.findItem (pl,def.item, optItm);
auto inter = policy.mob_prio <0 ? nullptr : wobj.findInteractive(pl,def.interactive,optMob);
if(pl.weaponState()!=WeaponState::NoWeapon) {
auto ws = pl.weaponState();
if(ws==WeaponState::Bow || ws==WeaponState::CBow) {
optMob.flags = WorldObjects::SearchFlg(WorldObjects::FcOverride | WorldObjects::NoRay);
inter = wobj.findInteractive(pl,def.interactive,optMob);
}
Expand Down Expand Up @@ -470,11 +472,12 @@ Focus World::findFocus(const Focus &def) {
}

bool World::testFocusNpc(Npc* def) {
auto opt = WorldObjects::NoFlg;
auto coll = TARGET_COLLECT_FOCUS;
auto& policy = searchPolicy(*npcPlayer,coll,opt);
auto opt = WorldObjects::NoFlg;
auto collAlgo = TARGET_COLLECT_FOCUS;
auto collType = TARGET_TYPE_ALL;
auto& policy = searchPolicy(*npcPlayer,collAlgo,collType,opt);

WorldObjects::SearchOpt optNpc{policy.npc_range1, policy.npc_range2, policy.npc_azi, coll, opt};
WorldObjects::SearchOpt optNpc{policy.npc_range1, policy.npc_range2, policy.npc_azi, collAlgo, collType, opt};
return wobj.testFocusNpc(*npcPlayer,def,optNpc);
}

Expand Down Expand Up @@ -815,9 +818,10 @@ void World::invalidateVobIndex() {
wobj.invalidateVobIndex();
}

const zenkit::IFocus& World::searchPolicy(const Npc& pl, TargetCollect& coll, WorldObjects::SearchFlg& opt) const {
opt = WorldObjects::NoFlg;
coll = TARGET_COLLECT_FOCUS;
const zenkit::IFocus& World::searchPolicy(const Npc& pl, TargetCollect& collAlgo, TargetType& collType, WorldObjects::SearchFlg& opt) const {
opt = WorldObjects::NoFlg;
collAlgo = TARGET_COLLECT_FOCUS;
collType = TARGET_TYPE_ALL;

switch(pl.weaponState()) {
case WeaponState::Fist:
Expand All @@ -833,7 +837,8 @@ const zenkit::IFocus& World::searchPolicy(const Npc& pl, TargetCollect& coll, Wo
if(auto weapon = pl.inventory().activeWeapon()) {
int32_t id = weapon->spellId();
auto& spl = script().spellDesc(id);
coll = TargetCollect(spl.target_collect_algo);
collAlgo = TargetCollect(spl.target_collect_algo);
collType = TargetType(spl.target_collect_type);
}
opt = WorldObjects::SearchFlg(WorldObjects::NoDeath | WorldObjects::NoUnconscious);
return game.script()->focusMage();
Expand Down
2 changes: 1 addition & 1 deletion game/world/world.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ class World final {
void invalidateVobIndex();

private:
const zenkit::IFocus& searchPolicy(const Npc& pl, TargetCollect& coll, WorldObjects::SearchFlg& opt) const;
const zenkit::IFocus& searchPolicy(const Npc& pl, TargetCollect& collAlgo, TargetType& collType, WorldObjects::SearchFlg& opt) const;
std::string wname;
GameSession& game;

Expand Down
41 changes: 34 additions & 7 deletions game/world/worldobjects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ void WorldObjects::MobStates::load(Serialize& fin) {
}
}

WorldObjects::SearchOpt::SearchOpt(float rangeMin, float rangeMax, float azi, TargetCollect collectAlgo, WorldObjects::SearchFlg flags)
:rangeMin(rangeMin),rangeMax(rangeMax),azi(azi),collectAlgo(collectAlgo),flags(flags) {
WorldObjects::SearchOpt::SearchOpt(float rangeMin, float rangeMax, float azi, TargetCollect collectAlgo, TargetType collectType, WorldObjects::SearchFlg flags)
:rangeMin(rangeMin),rangeMax(rangeMax),azi(azi),collectAlgo(collectAlgo),collectType(collectType),flags(flags) {
}

WorldObjects::WorldObjects(World& owner):owner(owner){
Expand Down Expand Up @@ -735,6 +735,8 @@ Interactive* WorldObjects::findInteractive(const Npc &pl, Interactive* def, cons
return def;
if(owner.view()==nullptr)
return nullptr;
if(!bool(opt.collectType&TARGET_TYPE_ALL))
return nullptr;

Interactive* ret = nullptr;
float rlen = opt.rangeMax*opt.rangeMax;
Expand Down Expand Up @@ -770,6 +772,8 @@ Item *WorldObjects::findItem(const Npc &pl, Item *def, const SearchOpt& opt) {
return def;
if(owner.view()==nullptr)
return nullptr;
if(!bool(opt.collectType&(TARGET_TYPE_ALL|TARGET_TYPE_ITEMS)))
return nullptr;

Item* ret = nullptr;
float rlen = opt.rangeMax*opt.rangeMax;
Expand Down Expand Up @@ -968,16 +972,16 @@ void WorldObjects::setMobState(std::string_view scheme, int32_t st) {
}

template<class T>
T& deref(std::unique_ptr<T>& x){ return *x; }
static T& deref(std::unique_ptr<T>& x){ return *x; }

template<class T>
T& deref(T* x){ return *x; }
static T& deref(T* x){ return *x; }

template<class T>
T& deref(T& x){ return x; }
static T& deref(T& x){ return x; }

template<class T>
bool checkFlag(T&,WorldObjects::SearchFlg){ return true; }
static bool checkFlag(T&,WorldObjects::SearchFlg){ return true; }

static bool checkFlag(Npc& n,WorldObjects::SearchFlg f){
if(n.handle().no_focus)
Expand All @@ -996,7 +1000,28 @@ static bool checkFlag(Interactive& i,WorldObjects::SearchFlg f){
}

template<class T>
bool canSee(const Npc&,const T&){ return true; }
static bool checkTargetType(T&, TargetType) { return true; }

static bool checkTargetType(Npc& n, TargetType t) {
if(bool(t&(TARGET_TYPE_ALL|TARGET_TYPE_NPCS)))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this code also G1 only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No can be used for G2. SPL_WindFist has TARGET_TYPE_NPCS. If changed to TARGET_TYPE_UNDEAD novices can't be targeted anymore.

Here it's just a leftover because non-npcs can't be targeted anyway. Mostly if G2 does something different G1 stuff is still functional but unused,

return true;
Guild gil = Guild(n.trueGuild());
if(bool(t&TARGET_TYPE_HUMANS) && gil<GIL_SEPERATOR_HUM)
return true;
if(bool(t&TARGET_TYPE_ORCS) && gil>GIL_SEPERATOR_ORC)
return true;
if(bool(t&TARGET_TYPE_UNDEAD)) {
if(gil == GIL_GOBBO_SKELETON || gil == GIL_SUMMONED_GOBBO_SKELETON ||
gil == GIL_SKELETON || gil == GIL_SUMMONED_SKELETON ||
gil == GIL_SKELETON_MAGE || gil == GIL_SHADOWBEAST_SKELETON ||
gil == GIL_ZOMBIE)
return true;
}
return false;
}

template<class T>
static bool canSee(const Npc&,const T&){ return true; }

static bool canSee(const Npc& pl, const Npc& n){
return pl.canSeeNpc(n,true);
Expand Down Expand Up @@ -1049,6 +1074,8 @@ bool WorldObjects::testObj(T &src, const Npc &pl, const WorldObjects::SearchOpt

if(!checkFlag(npc,opt.flags))
return false;
if(!checkTargetType(npc,opt.collectType))
return false;

float l = pl.qDistTo(npc);
if(l>qmax || l<qmin)
Expand Down
3 changes: 2 additions & 1 deletion game/world/worldobjects.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ class WorldObjects final {

struct SearchOpt final {
SearchOpt()=default;
SearchOpt(float rangeMin, float rangeMax, float azi, TargetCollect collectAlgo=TARGET_COLLECT_CASTER, SearchFlg flags=NoFlg);
SearchOpt(float rangeMin, float rangeMax, float azi, TargetCollect collectAlgo=TARGET_COLLECT_CASTER, TargetType collectType=TARGET_TYPE_ALL, SearchFlg flags=NoFlg);
float rangeMin = 0;
float rangeMax = 0;
float azi = 0;
TargetCollect collectAlgo = TARGET_COLLECT_CASTER;
TargetType collectType = TARGET_TYPE_ALL;
SearchFlg flags = NoFlg;
};

Expand Down