Skip to content

Commit

Permalink
Monsters change target strategies
Browse files Browse the repository at this point in the history
second part of
5abc8c5

ranged monsters will change target correctly now  (always picking the
closest one), also all monsters have correct chance to change target and
strategies as they work in real tibia, they can pick the nearest target,
the one with the lowest % health, the one that did the most damage and
also a random one.

updated othire_win32.exe to latest changes
  • Loading branch information
peonso committed Sep 1, 2016
1 parent 5abc8c5 commit 8e56757
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 26 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ OTHire 0.0.3
- Healing and attacking spells shares exhaust
- Players block ranged physical attacks from monsters with shield
- Creature events onDie, onKill, onAdvance available
- Monsters change target strategies implemented
- Fixed monsters casting spells through walls
- Fixed monsters being traped by moveable items
- Fixed bug with beds itemids
Expand Down
Binary file modified othire_win32.exe
Binary file not shown.
112 changes: 90 additions & 22 deletions source/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,40 +418,116 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL
std::cout << "Searching target... " << std::endl;
#endif

if (searchType == TARGETSEARCH_DEFAULT) {
int32_t rnd = random_range(1, 100);

searchType = TARGETSEARCH_NEAREST;

int32_t sum = this->mType->targetStrategiesNearestPercent;
if (rnd > sum) {
searchType = TARGETSEARCH_HP;
sum += this->mType->targetStrategiesLowerHPPercent;

if (rnd > sum) {
searchType = TARGETSEARCH_DAMAGE;
sum += this->mType->targetStrategiesMostDamagePercent;
if (rnd > sum) {
searchType = TARGETSEARCH_RANDOM;
}
}
}
}

std::list<Creature*> resultList;
const Position& myPos = getPosition();
for(CreatureList::iterator it = targetList.begin(); it != targetList.end(); ++it){
if(followCreature != (*it) && isTarget(*it)){
if(searchType == TARGETSEARCH_RANDOM || canUseAttack(myPos, *it)){

This comment has been minimized.

Copy link
@peonso

peonso Mar 18, 2023

Author Collaborator

this line should never be changed

if(isTarget(*it)){
if((this->mType->targetDistance == 1) || canUseAttack(myPos, *it)){
resultList.push_back(*it);
}
}
}

if (resultList.empty()) {
return false;
}

Creature* target = NULL;

switch(searchType){
case TARGETSEARCH_NEAREAST:
{
Creature* target = NULL;
case TARGETSEARCH_NEAREST: {
target = NULL;
int32_t minRange = -1;
for(std::list<Creature*>::iterator it = resultList.begin(); it != resultList.end(); ++it){
if(minRange == -1 || std::max(std::abs(myPos.x - (*it)->getPosition().x), std::abs(myPos.y - (*it)->getPosition().y)) < minRange){
target = *it;
minRange = std::max(std::abs(myPos.x - (*it)->getPosition().x), std::abs(myPos.y - (*it)->getPosition().y));
} else if (std::max(std::abs(myPos.x - (*it)->getPosition().x), std::abs(myPos.y - (*it)->getPosition().y)) == minRange) {
int32_t rnga = random_range(1,2);
int32_t rngb = random_range(1,2);
if (rnga == rngb) {
target = *it;
}
}
}

if (target && selectTarget(target)) {
return true;
}

break;
}
case TARGETSEARCH_HP: {
target = NULL;
if (!resultList.empty()) {
auto it = resultList.begin();
target = *it;

if (++it != resultList.end()) {
int32_t minHp = target->getHealth();
do {
if ((*it)->getHealth() < minHp) {
target = *it;
minHp = target->getHealth();
}
} while (++it != resultList.end());
}
}

if(target && selectTarget(target)){
if (target && selectTarget(target)) {
return true;
}

break;
}
case TARGETSEARCH_DAMAGE: {
target = NULL;
if (!resultList.empty()) {
auto it = resultList.begin();
target = *it;

if (++it != resultList.end()) {
int32_t mostDamage = 0;
do {
const auto& dmg = damageMap.find((*it)->getID());
if (dmg != damageMap.end()) {
if (dmg->second.total > mostDamage) {
mostDamage = dmg->second.total;
target = *it;
}
}
} while (++it != resultList.end());
}
}

if (target && selectTarget(target)) {
return true;
}

case TARGETSEARCH_DEFAULT:
case TARGETSEARCH_ATTACKRANGE:
break;
}
case TARGETSEARCH_RANDOM:
default:
{
default: {
if(!resultList.empty()){
uint32_t index = random_range(0, resultList.size() - 1);
CreatureList::iterator it = resultList.begin();
Expand All @@ -460,18 +536,15 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL
std::cout << "Selecting target " << (*it)->getName() << std::endl;
#endif
return selectTarget(*it);
}

if(searchType == TARGETSEARCH_ATTACKRANGE){
return false;
}
break;
}
}

//lets just pick the first target in the list
for(CreatureList::iterator it = targetList.begin(); it != targetList.end(); ++it){
if(followCreature != (*it) && selectTarget(*it)){
if(selectTarget(*it)){
#ifdef __DEBUG__
std::cout << "Selecting target " << (*it)->getName() << std::endl;
#endif
Expand Down Expand Up @@ -694,7 +767,7 @@ void Monster::onThink(uint32_t interval)
}
else if(isFleeing()){
if(attackedCreature && !canUseAttack(getPosition(), attackedCreature)){
searchTarget(TARGETSEARCH_ATTACKRANGE);
searchTarget(TARGETSEARCH_DEFAULT);
}
}
}
Expand Down Expand Up @@ -829,13 +902,8 @@ void Monster::onThinkTarget(uint32_t interval)
targetChangeCooldown = (uint32_t)mType->changeTargetSpeed;

if(mType->changeTargetChance >= random_range(1, 100)){
if(mType->targetDistance <= 1){
searchTarget(TARGETSEARCH_RANDOM);
}
else{
searchTarget(TARGETSEARCH_NEAREAST);
}
}
searchTarget(TARGETSEARCH_DEFAULT);
}
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions source/monster.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ typedef std::list<Creature*> CreatureList;

enum TargetSearchType_t{
TARGETSEARCH_DEFAULT,
TARGETSEARCH_RANDOM,
TARGETSEARCH_ATTACKRANGE,
TARGETSEARCH_NEAREAST
TARGETSEARCH_NEAREST,
TARGETSEARCH_HP,
TARGETSEARCH_DAMAGE,
TARGETSEARCH_RANDOM
};

class Monster : public Creature
Expand Down
37 changes: 36 additions & 1 deletion source/monsters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ void MonsterType::reset()

changeTargetSpeed = 0;
changeTargetChance = 0;

targetStrategiesNearestPercent = 0;
targetStrategiesLowerHPPercent = 0;
targetStrategiesMostDamagePercent = 0;
targetStrategiesRandom = 100;

scriptList.clear();
}
Expand Down Expand Up @@ -438,7 +443,7 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, MonsterType*
needDirection = true;
}
}

if(readXMLInteger(node, "checkshield", intValue)){
combat->setParam(COMBATPARAM_BLOCKEDBYSHIELD, 1);
}
Expand Down Expand Up @@ -1045,6 +1050,36 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monster_n
SHOW_XML_WARNING("Missing targetchange.chance");
}
}
else if(xmlStrcmp(p->name, (const xmlChar*)"targetstrategies") == 0){

if(readXMLInteger(p, "nearest", intValue)){
mType->targetStrategiesNearestPercent = std::max(1, intValue);
}
else{
SHOW_XML_WARNING("Missing targetStrategiesNearestPercent");
}

if(readXMLInteger(p, "health", intValue)){
mType->targetStrategiesLowerHPPercent = std::max(1, intValue);
}
else{
SHOW_XML_WARNING("Missing targetStrategiesLowerHPPercent");
}

if(readXMLInteger(p, "damage", intValue)){
mType->targetStrategiesMostDamagePercent = std::max(1, intValue);
}
else{
SHOW_XML_WARNING("Missing targetStrategiesMostDamagePercent");
}

if(readXMLInteger(p, "random", intValue)){
mType->targetStrategiesRandom = std::max(1, intValue);
}
else{
SHOW_XML_WARNING("Missing targetStrategiesRandom");
}
}
else if(xmlStrcmp(p->name, (const xmlChar*)"strategy") == 0){

if(readXMLInteger(p, "attack", intValue)){
Expand Down
5 changes: 5 additions & 0 deletions source/monsters.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ class MonsterType{

int32_t changeTargetSpeed;
int32_t changeTargetChance;

int32_t targetStrategiesNearestPercent;
int32_t targetStrategiesLowerHPPercent;
int32_t targetStrategiesMostDamagePercent;
int32_t targetStrategiesRandom;

MonsterScriptList scriptList;

Expand Down

16 comments on commit 8e56757

@diegorodriguesvieira
Copy link
Contributor

@diegorodriguesvieira diegorodriguesvieira commented on 8e56757 Sep 1, 2016

Choose a reason for hiding this comment

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

@peonso
Copy link
Collaborator Author

@peonso peonso commented on 8e56757 Sep 1, 2016

Choose a reason for hiding this comment

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

I can compile it with MSVC 2010, can you help me to fix this?

@diegorodriguesvieira
Copy link
Contributor

Choose a reason for hiding this comment

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

I'll try to fix it here and I talk to you

@diegorodriguesvieira
Copy link
Contributor

@diegorodriguesvieira diegorodriguesvieira commented on 8e56757 Sep 1, 2016

Choose a reason for hiding this comment

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

Compare the original file with my below changes:
Monster.cpp
http://paste.ubuntu.com/23122202/ (I don't know what I'm doing, I'm newbie in C++) 👍

You can use this site to compare: https://www.diffchecker.com/

@diegorodriguesvieira
Copy link
Contributor

@diegorodriguesvieira diegorodriguesvieira commented on 8e56757 Sep 1, 2016

Choose a reason for hiding this comment

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

I do not know if it's a bug but from what I tested here, a monster removed a ladder on their way

before

after

@peonso
Copy link
Collaborator Author

@peonso peonso commented on 8e56757 Sep 2, 2016

Choose a reason for hiding this comment

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

Which item id?

@diegorodriguesvieira
Copy link
Contributor

Choose a reason for hiding this comment

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

@peonso ID: 1386 a ladder

@peonso
Copy link
Collaborator Author

@peonso peonso commented on 8e56757 Sep 2, 2016

Choose a reason for hiding this comment

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

Gonna try to track this, I don't think it's related at all with this commit. I committed your changes, compiled here fine, seems to be working as intended.

@diegorodriguesvieira
Copy link
Contributor

Choose a reason for hiding this comment

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

I fix it!

#72 (comment)

@kito2
Copy link

@kito2 kito2 commented on 8e56757 Oct 3, 2016

Choose a reason for hiding this comment

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

òn RL the can pick any player, not the nearest.

@peonso
Copy link
Collaborator Author

@peonso peonso commented on 8e56757 Oct 3, 2016

Choose a reason for hiding this comment

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

@kito2 Says who?

@kito2
Copy link

@kito2 kito2 commented on 8e56757 Oct 3, 2016

Choose a reason for hiding this comment

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

@peonso I have been playing a lot the last 2 years and leveled manually from 0 to 470. I know how it exact works.

For example:

  • Sometimes hunting on deeper banuta with some friends when I was around 150-18. Mages used to stand over energybombs. Paladin used to lure and run around the knight, while the knight was surrounded of 8 creatures. Sometimes ALL monsters over there had a retarget which wasn't based on the nearest player (which would be the paladin), it happened sometimes that they retargeted the mages (while there were attacking the paladin or the knight).
  • Same happening on roshamuul prison.
  • Same happening on catacombs.

The only difference is about SOME monsters has a lot of chance of retarget.

The only ones with an artificial intelligence based on statistics or formula are bosses.

@peonso
Copy link
Collaborator Author

@peonso peonso commented on 8e56757 Oct 3, 2016

Choose a reason for hiding this comment

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

Don't get me wrong dude, saw a lot of people claiming things about mechanics this whole years I've being messing around with replicating correct Tibia ones, backing up their claims with their memory/experience, just to be contradicted by some fact check, myself included. Unless you give me some resource to actual check what would be wrong here or the correct behavior is, I will just think you are mistaken. This lines of code are based on decompiled version of 7.7 leaked files. What you described don't even contradict the code present here, even imply you actually don't understood it and sounded vague.

@kito2
Copy link

@kito2 kito2 commented on 8e56757 Oct 4, 2016 via email

Choose a reason for hiding this comment

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

@gunzino
Copy link

Choose a reason for hiding this comment

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

Are you sure your target search selecting is correct ? for example if you have lowest hp 60% and most dmg 60% it will always pick only lowest hp strategy.

@peonso
Copy link
Collaborator Author

@peonso peonso commented on 8e56757 Oct 9, 2017

Choose a reason for hiding this comment

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

@gunzino
Total should be 100, if you set one to 60 and the other to 60 the code is not prepared for the 120% chance and will stick to the last picked strategy before the sum goes over 100.

Please sign in to comment.