Working on hostile NPCs, fixing darklands mode for battle zone
This commit is contained in:
1 parent
2e6b93f3ac
commit
f4bd3b6bed
24 files changed
+23930
-1457
No files matched your search
@@ -66,7 +66,9 @@ void CharacterState::setTarget(
|
||||
|
||||
requestedWalkTarget = normalizedTarget;
|
||||
onArrivedCallbackName = std::move(callbackName);
|
||||
pathWaypoints = location.planPathExceptMyself(position, normalizedTarget, entityId);
|
||||
pathWaypoints = location.navigationMaps.empty()
|
||||
? std::vector<Eigen::Vector3f>{}
|
||||
: location.planPathExceptMyself(position, normalizedTarget, entityId);
|
||||
currentWaypointIndex = 0;
|
||||
if (!pathWaypoints.empty()) {
|
||||
for (Eigen::Vector3f& waypoint : pathWaypoints) waypoint.y() = 0.f;
|
||||
@@ -83,7 +85,9 @@ void CharacterState::forceReplan(
|
||||
{
|
||||
const Eigen::Vector3f normalizedTarget(
|
||||
requestedWalkTarget.x(), 0.f, requestedWalkTarget.z());
|
||||
pathWaypoints = location.planPathExceptMyself(position, normalizedTarget, entityId);
|
||||
pathWaypoints = location.navigationMaps.empty()
|
||||
? std::vector<Eigen::Vector3f>{}
|
||||
: location.planPathExceptMyself(position, normalizedTarget, entityId);
|
||||
currentWaypointIndex = 0;
|
||||
if (!pathWaypoints.empty()) {
|
||||
for (Eigen::Vector3f& waypoint : pathWaypoints) waypoint.y() = 0.f;
|
||||
@@ -146,15 +150,15 @@ CharacterUpdateEvents CharacterState::update(
|
||||
|
||||
const CharacterObservation* attackTarget = context.attackTarget;
|
||||
const CharacterObservation* faceTarget = context.faceTarget;
|
||||
if (!attackTarget) {
|
||||
if (!attackTarget || attackTarget->hp <= 0.f) {
|
||||
battle_state = 0;
|
||||
attack = 0;
|
||||
attackTargetId.clear();
|
||||
} else {
|
||||
const float distance = (attackTarget->position - position).norm();
|
||||
if (distance >= visibleRange) {
|
||||
if (isPlayer &&
|
||||
(attackTarget->position - requestedWalkTarget).norm() > TARGET_REPLAN_THRESHOLD) {
|
||||
if ((attackTarget->position - requestedWalkTarget).norm() >
|
||||
TARGET_REPLAN_THRESHOLD) {
|
||||
events.navigationRequested = true;
|
||||
events.navigationTarget = attackTarget->position;
|
||||
}
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@ int Environment::width = CONST_DEFAULT_WIDTH;
|
||||
int Environment::height = CONST_DEFAULT_HEIGHT;
|
||||
float Environment::zoom = DEFAULT_ZOOM;
|
||||
|
||||
bool Environment::enableLogging = false;
|
||||
//bool Environment::enableLogging = true;
|
||||
//bool Environment::enableLogging = false;
|
||||
bool Environment::enableLogging = true;
|
||||
|
||||
SDL_Window* Environment::window = nullptr;
|
||||
|
||||
|
||||
+73
-11
@@ -263,6 +263,78 @@ LocationSimulationEvents LocationGameState::update(
|
||||
observations.emplace(
|
||||
id, CharacterObservation{npc->position, npc->hp});
|
||||
|
||||
std::vector<std::string> playerIds;
|
||||
playerIds.reserve(players.size());
|
||||
for (const auto& [id, player] : players)
|
||||
if (player) playerIds.push_back(id);
|
||||
std::sort(playerIds.begin(), playerIds.end());
|
||||
|
||||
std::vector<std::string> eligiblePlayerIds;
|
||||
for (const std::string& id : playerIds) {
|
||||
const auto& player = players.at(id);
|
||||
if (player->enabled && player->hp > 0.f)
|
||||
eligiblePlayerIds.push_back(id);
|
||||
}
|
||||
|
||||
std::vector<std::string> npcIds;
|
||||
npcIds.reserve(npcs.size());
|
||||
for (const auto& [id, npc] : npcs)
|
||||
if (npc) npcIds.push_back(id);
|
||||
std::sort(npcIds.begin(), npcIds.end());
|
||||
|
||||
// Target selection is authoritative and runs before all characters observe
|
||||
// the tick. A valid target is a lock: it is retained until the player dies
|
||||
// or becomes disabled, even when the player leaves visibleRange.
|
||||
for (const std::string& id : npcIds) {
|
||||
CharacterState& npc = *npcs.at(id);
|
||||
if (!npc.enabled || !npc.canAttack || npc.hp <= 0.f)
|
||||
continue;
|
||||
|
||||
const std::string previousTarget = npc.attackTargetId;
|
||||
const auto locked = players.find(previousTarget);
|
||||
const bool lockValid =
|
||||
locked != players.end() && locked->second &&
|
||||
locked->second->enabled && locked->second->hp > 0.f;
|
||||
std::string selectedTarget = lockValid ? previousTarget : std::string();
|
||||
|
||||
if (!lockValid) {
|
||||
float nearestDistanceSquared = npc.visibleRange * npc.visibleRange;
|
||||
for (const std::string& playerId : eligiblePlayerIds) {
|
||||
const Eigen::Vector3f delta =
|
||||
players.at(playerId)->position - npc.position;
|
||||
const float distanceSquared =
|
||||
delta.x() * delta.x() + delta.z() * delta.z();
|
||||
if (distanceSquared <= nearestDistanceSquared) {
|
||||
if (selectedTarget.empty() ||
|
||||
distanceSquared < nearestDistanceSquared ||
|
||||
(distanceSquared == nearestDistanceSquared &&
|
||||
playerId < selectedTarget)) {
|
||||
selectedTarget = playerId;
|
||||
nearestDistanceSquared = distanceSquared;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedTarget != previousTarget) {
|
||||
npc.stopInPlace();
|
||||
npc.attackTargetId = selectedTarget;
|
||||
}
|
||||
|
||||
if (selectedTarget.empty()) {
|
||||
npc.battle_state = 0;
|
||||
npc.attack = 0;
|
||||
const Eigen::Vector3f homeTarget(
|
||||
npc.homePosition.x(), 0.f, npc.homePosition.z());
|
||||
const Eigen::Vector3f toHome = homeTarget - npc.position;
|
||||
if (toHome.norm() > CharacterState::kWalkThreshold &&
|
||||
(npc.requestedWalkTarget - homeTarget).norm() >
|
||||
CharacterState::kWalkThreshold) {
|
||||
npc.setTarget(homeTarget, {}, *location, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto advance = [&](const std::string& id, CharacterState& state) {
|
||||
CharacterUpdateContext context;
|
||||
auto attack = observations.find(state.attackTargetId);
|
||||
@@ -276,19 +348,9 @@ LocationSimulationEvents LocationGameState::update(
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::string> playerIds;
|
||||
playerIds.reserve(players.size());
|
||||
for (const auto& [id, player] : players)
|
||||
if (player) playerIds.push_back(id);
|
||||
std::sort(playerIds.begin(), playerIds.end());
|
||||
for (const std::string& id : playerIds)
|
||||
if (players.at(id)->enabled)
|
||||
advance(id, *players.at(id));
|
||||
std::vector<std::string> npcIds;
|
||||
npcIds.reserve(npcs.size());
|
||||
for (const auto& [id, npc] : npcs)
|
||||
if (npc) npcIds.push_back(id);
|
||||
std::sort(npcIds.begin(), npcIds.end());
|
||||
for (const std::string& id : npcIds)
|
||||
if (npcs.at(id)->enabled)
|
||||
advance(id, *npcs.at(id));
|
||||
@@ -296,7 +358,7 @@ LocationSimulationEvents LocationGameState::update(
|
||||
for (const CharacterSimulationEvent& source : result.characters) {
|
||||
if (!source.events.attackImpact) continue;
|
||||
CharacterState* target = findCharacter(source.events.attackTargetId, players);
|
||||
if (!target || !target->enabled) continue;
|
||||
if (!target || !target->enabled || target->hp <= 0.f) continue;
|
||||
constexpr float damage = 10.f;
|
||||
target->setHp(target->hp - damage);
|
||||
result.damage.push_back({
|
||||
|
||||
@@ -182,7 +182,6 @@ CharacterState NpcData::toCharacterState() const
|
||||
state.initialHp = hp;
|
||||
state.visibleRange = visibleRange;
|
||||
state.canAttack = canAttack;
|
||||
if (canAttack) state.attackTargetId = "player";
|
||||
state.enabled = enabled;
|
||||
state.interactionRadius = interactionRadius;
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ void InternalGameServer::initializeCampaign(
|
||||
campaign.id = campaignId;
|
||||
campaign.questsPath = questsPath;
|
||||
startCampaign(campaign, zipFile, true, true);
|
||||
//startCampaign(campaign, zipFile, true, false);
|
||||
}
|
||||
|
||||
void InternalGameServer::startCampaign(
|
||||
@@ -420,8 +421,6 @@ void InternalGameServer::submit(const GameCommand& command)
|
||||
character->stopInPlace();
|
||||
character->faceTargetId.clear();
|
||||
character->attackTargetId.clear();
|
||||
if (value.enabled && !character->isPlayer && character->canAttack)
|
||||
character->attackTargetId = "player";
|
||||
character->battle_state = 0;
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, ClearCharacterAttackTargetCommand>) {
|
||||
|
||||
Reference in new issue
Block a user