void SDLMainLoop::initSDL() { if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) { LOG2(SDL, ERROR, "Can't init SDL: " << SDL_GetError()); } if(TTF_Init() < 0) { LOG2(SDL, ERROR, "Can't init SDL_ttf: " << TTF_GetError()); } SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_WM_SetCaption("HexRacer", NULL); int width = GET_SETTING("display.width", 0); int height = GET_SETTING("display.height", 0); int bpp = GET_SETTING("display.bpp", 0); sdl_init_flags = SDL_HWSURFACE | SDL_OPENGL | SDL_RESIZABLE; if(GET_SETTING("display.fullscreen", 0)) { sdl_init_flags |= SDL_FULLSCREEN; } SDL_SetVideoMode(width, height, bpp, sdl_init_flags); projector.setCurrentDimensions(Point2D(width, height)); }
void PhysicalPlayer::applyAcceleration(double acceleration) { if(!onGround) return; rigidBody->activate(); double constant = GET_SETTING("physics.constant.accel", 1.0); double brakeConstant = GET_SETTING("physics.constant.brake", 5.0); btTransform transform = rigidBody->getWorldTransform(); btMatrix3x3 matrix(transform.getRotation()); Math::Point orientation = Converter::toPoint(matrix * Converter::toVector(Math::Point(0.0, 0.0, 1.0) * constant)); //LOG(PHYSICS, "accel at " << Misc::Sleeper::getTimeMilliseconds()); if(getSliding()) { constant *= GET_SETTING("physics.slipstate.accelfactor", 1.0); } if (acceleration >= 0.0 || getLinearVelocity().dotProduct(orientation) < 0.0) { double paintInfluence = (speedBoost - 1.0) * GET_SETTING("game.paint.boostinfluence", 1.0) + 1.0; applyForce(orientation * constant * acceleration * paintInfluence * traction); } else { applyForce(orientation * brakeConstant * acceleration * traction); } updatePhysicalInfo(); }
void FontManager::loadFonts() { TTF_Font *font = TTF_OpenFont( GET_SETTING("font", "data/gui/DejaVuSans.ttf").c_str(), GET_SETTING("font.size", 16)); if(!font) { LOG2(GUI, ERROR, "Can't load normal font! " << GET_SETTING("font", "data/DejaVuSans.ttf")); } fontList["normal"] = font; currentKind = "normal"; }
void Suspension::calculateSuspensionForPlayer(Object::Player *player, double forceScale) { double front_sep = GET_SETTING("physics.wheel.frontsep", 0.4); double front_z = GET_SETTING("physics.wheel.frontz", 0.68); double back_sep = GET_SETTING("physics.wheel.backsep", 0.4); double back_z = GET_SETTING("physics.wheel.backz", -0.68); static const Math::Point suspensionPoint[] = { Math::Point(front_sep, -0.2 + 0.05,front_z), Math::Point(-front_sep, -0.2 + 0.05, front_z), Math::Point(-back_sep, -0.2 + 0.05, back_z), Math::Point(back_sep, -0.2 + 0.05, back_z), }; // downward axis of suspension springs Math::Point axis = -player->getPhysicalObject()->getUpDirection(); axis.normalize(); double downFactor = calculateDownFactor(axis); player->getPhysicalObject()->setTraction(std::sqrt(downFactor)); for(int wheel = 0; wheel < 4; wheel ++) { Math::Matrix matrix = player->getTransformation(); // suspension attachment point in world space Math::Point point = matrix * suspensionPoint[wheel]; // rotated but not translated suspension attachment point Math::Point forcePoint = suspensionPoint[wheel]; forcePoint.setW(0.0); forcePoint = matrix * forcePoint; // create a Spring object Spring spring(point, axis); spring.setMinLength( GET_SETTING("physics.driving.minlength", 0.0)); spring.setRestLength( GET_SETTING("physics.driving.restlength", 1.0)); spring.setStretchLength( GET_SETTING("physics.driving.stretchlength", 1.0)); // restore last frame's displacement in the Spring object spring.setLastDisplacement( player->getPhysicalObject()->getSpring(wheel)); // calculate and apply actual force Displacement displacement = spring.doRaycast(); double forceFactor = spring.calculateForceFactor(displacement); player->applyForce(axis * forceFactor * downFactor * forceScale, forcePoint); // record displacement for next time player->getPhysicalObject()->setSpring(wheel, displacement); // find the position of the wheel double down = displacement.getDisplacement(); player->setSuspension(wheel, suspensionPoint[wheel] + Math::Point(0.0, -down - GET_SETTING("physics.wheel.diameter", 0.2), 0.0)); } }
void WeightedDriver::detectSittingStill() { Object::Player *player = getPlayer(); // if sitting still for too long, request a warp intention.setReset(false); const Map::PathNode* last_pathnode = nearestPathNode; nearestPathNode = player->getPathTracker()->getCurrentNode(); if (nearestPathNode != NULL && nearestPathNode == last_pathnode && player->getPhysicalObject()->getLinearSpeed() < 4.0) { unsigned long now = Misc::Sleeper::getTimeMilliseconds(); if(!sittingStill) { sittingStill = true; sittingStillSince = now; } else if(sittingStillSince + GET_SETTING("ai.resettimer", 3000) < now) { LOG(PHYSICS, "Player " << player->getID() << " is trying to reset"); intention.setReset(true); sittingStill = false; } } else sittingStill = false; }
void PhysicalPlayer::applyTurning(double amount) { if(!onGround) return; rigidBody->activate(); double constant = GET_SETTING("physics.constant.turn", 1.0); double centripetalConstant = GET_SETTING("physics.constant.centripetal", 1.0); //double leanConstant = GET_SETTING("physics.constant.lean", 1.0); Math::Matrix matrix = getTransformation(); Math::Point forwardAxis = matrix * Math::Point(0.0, 0.0, 1.0, 0.0); Math::Point centripetalAxis = matrix * Math::Point(-1.0, 0.0, 0.0, 0.0); forwardAxis.normalize(); centripetalAxis.normalize(); double speed = getLinearVelocity().length(); #if 0 // turn in the opposite direction when travelling backwards if (getLinearVelocity().dotProduct(forwardAxis) < 0) { speed = -speed; } #endif double speedFactor = GET_SETTING("physics.turning.speedfactor", 0.5); double speedThreshhold = GET_SETTING("physics.turning.speedthreshhold", 15.0); double falloffFactor = GET_SETTING("physics.turning.fallofffactor", 0.25); double turning_factor = GET_SETTING("physics.turning.constant", 1.0); if (speed <= speedThreshhold) { turning_factor += sqrt(speed/speedThreshhold)*(speed*speedFactor); } else { turning_factor += (speedThreshhold*speedFactor)*(1.0/(1.0+(speed-speedThreshhold)*falloffFactor)); } // turn in the opposite direction when travelling backwards if (getLinearVelocity().dotProduct(forwardAxis) < 0) { turning_factor = -turning_factor; } if(getSliding()) { centripetalConstant *= GET_SETTING("physics.slipstate.centripetalfactor", 1.0); constant *= GET_SETTING("physics.slipstate.turnfactor", 1.0); } applyForce(centripetalAxis * centripetalConstant * turning_factor * amount); applyTorque(-getUpDirection() * constant * turning_factor * amount); // twist the car in response to a sharp turn //applyTorque(getFrontDirection() * leanConstant * turning_factor * amount); updatePhysicalInfo(); }
void Suspension::applyDragForce(Object::Player *player, double forceScale) { Physics::PhysicalPlayer *physicalPlayer = player->getPhysicalObject(); Math::Point linearVelocity = physicalPlayer->getLinearVelocity(); Math::Point angularVelocity = physicalPlayer->getAngularVelocity(); double linear; double angular; double sideways; if (player->getOnGround()) { linear = GET_SETTING("physics.driving.lineardrag", 0.1)*player->getPhysicalObject()->getTraction(); angular = GET_SETTING("physics.driving.angulardrag", 0.1)*player->getPhysicalObject()->getTraction(); sideways = GET_SETTING("physics.driving.sidewaysdrag", 0.1)*player->getPhysicalObject()->getTraction(); } else { linear = GET_SETTING("physics.driving.airlineardrag", 0.1); angular = GET_SETTING("physics.driving.airangulardrag", 0.1); sideways = GET_SETTING("physics.driving.airsidewaysdrag", 0.1); } Math::Point linearDrag = -linear * linearVelocity; Math::Point angularDrag = -angular * angularVelocity; physicalPlayer->applyForce(linearDrag * forceScale); physicalPlayer->applyTorque(angularDrag * forceScale); // sideways drag (prevent slipping) Math::Point sidewaysAxis = physicalPlayer->getRightDirection(); double sidewaysSpeed = linearVelocity.dotProduct(sidewaysAxis); Math::Point sidewaysDrag = -sideways * sidewaysSpeed * sidewaysAxis; double enterSlipState = GET_SETTING("physics.slipstate.enter", 1.0); double exitSlipState = GET_SETTING("physics.slipstate.exit", 1.0); if(std::fabs(sidewaysSpeed) >= enterSlipState) { physicalPlayer->setSliding(true); } if(std::fabs(sidewaysSpeed) <= exitSlipState) { physicalPlayer->setSliding(false); } //LOG(PHYSICS, "sideways " << sidewaysSpeed << "\t" << physicalPlayer->getSliding()); if(physicalPlayer->getSliding()) { sidewaysDrag *= GET_SETTING("physics.slipstate.sidewaysfactor", 1.0); } physicalPlayer->applyForce(sidewaysDrag*forceScale); }
double Suspension::Spring::calculateForceFactor( const Displacement &thisDisplacement) { /*LOG(PHYSICS, "spring displacement: " << thisDisplacement.getDisplacement() << " (" << thisDisplacement.isOnGround() << ")");*/ if(!thisDisplacement.isOnGround()) { return 0.0; } double displacement = thisDisplacement.getDisplacement(); double displacementSpeed = lastDisplacement.getDisplacement() - displacement; if(!lastDisplacement.isOnGround()) { displacementSpeed = 0.0; // no damping force when first hit the ground } double GRAVITY = GET_SETTING("physics.constant.gravity", 9.81); static const double VEHICLE_WEIGHT = 2.0; static const double NORMAL_FORCE = (GRAVITY * VEHICLE_WEIGHT) / 4.0; static const double K_FACTOR = 100.0; static const double INITIAL_K = (K_FACTOR / VEHICLE_WEIGHT / 4.0); static const double INITIAL_C = 2 * std::sqrt(INITIAL_K * VEHICLE_WEIGHT); double K = GET_SETTING("physics.driving.constant.k", 1.0) * INITIAL_K; double C = GET_SETTING("physics.driving.constant.c", 1.0) * INITIAL_C; //LOG(PHYSICS, "normal: " << -NORMAL_FORCE << ", -K*disp: " << -K*displacement); //LOG(PHYSICS, "C*disp: " << C*displacementSpeed); double factor = -NORMAL_FORCE + (K * displacement) + (-C * displacementSpeed); // prevent springs from sucking the ground in if(factor > 0.0) factor = 0.0; //LOG(PHYSICS, "factor: " << factor); //LOG(PHYSICS, " " << -K*displacement << ", " << C*displacementSpeed); return factor; }
const std::list<BonusMessageQueue::BonusMessage>& BonusMessageQueue::getQueue() { unsigned long current_time = World::TimeElapsed::getInstance().getGameTime(); int duration = GET_SETTING("hud.bonusmessages.appearduration", 2000); while (!messageQueue.empty() && (*messageQueue.begin()).appearTime+duration < current_time) messageQueue.pop_front(); return messageQueue; }
void PaintSubsystem::playerErase(Object::Player* player) { if (GET_SETTING("game.paint.allowerase", true)) { std::vector<Math::HexHeightMap::LayeredHexIndex> changed_indices; paintManager->colorCellsInRadius(player->getPosition(), PAINTING_RADIUS, -1, false, &changed_indices); if (Bonus::GlobalBonusManager::getInstance()) Bonus::GlobalBonusManager::getInstance()->getPlayerBonuses(player->getID()).playerErase(static_cast<int>(changed_indices.size())); } }
void Suspension::checkForWheelsOnGround() { if(!GET_SETTING("physics.driving.enablesuspension", 0)) { return; } Object::WorldManager::PlayerIteratorType it = worldManager->getPlayerIterator(); while(it.hasNext()) { Object::Player *player = it.next(); int onGround = wheelsOnGround(player->getID()); player->setOnGround(onGround > 2); } }
void Suspension::applySuspension(double forceScale) { if(!GET_SETTING("physics.driving.enablesuspension", 0)) { return; } Object::WorldManager::PlayerIteratorType it = worldManager->getPlayerIterator(); while(it.hasNext()) { Object::Player *player = it.next(); applyDragForce(player, forceScale); calculateSuspensionForPlayer(player, forceScale); } }
void SDLMainLoop::changeScreenModeHandler(Event::ChangeScreenMode *event) { int width = event->getWidth(); int height = event->getHeight(); int bpp = event->getBPP(); bool fullscreen = event->getFullscreen(); #ifndef WIN32 // special case: if just toggling fullscreen, and the operating system // supports it, can just call ToggleFullScreen instead of setting the mode if(width == GET_SETTING("display.width", 0) && height == GET_SETTING("display.height", 0) && bpp == GET_SETTING("display.bpp", 0) && fullscreen != GET_SETTING("display.fullscreen", 0)) { if(SDL_WM_ToggleFullScreen(SDL_GetVideoSurface())) return; } #endif Settings::SettingsManager::getInstance()->set( "display.width", Misc::StreamAsString() << width); Settings::SettingsManager::getInstance()->set( "display.height", Misc::StreamAsString() << height); Settings::SettingsManager::getInstance()->set( "display.bpp", Misc::StreamAsString() << bpp); Settings::SettingsManager::getInstance()->set( "display.fullscreen", Misc::StreamAsString() << fullscreen); if(fullscreen) { sdl_init_flags |= SDL_FULLSCREEN; } else { sdl_init_flags &= ~SDL_FULLSCREEN; } SDL_SetVideoMode(width, height, bpp, this->sdl_init_flags); resizeGL(width, height); }
void PhysicalPlayer::setData(const Math::Matrix &transform, const Math::Point &linearVelocity, const Math::Point &angularVelocity, bool interpolate) { Math::Point originalOrigin = getOrigin(); rigidBody->setWorldTransform( Physics::Converter::toTransform(transform)); rigidBody->setLinearVelocity( Physics::Converter::toVector(linearVelocity)); rigidBody->setAngularVelocity( Physics::Converter::toVector(angularVelocity)); Math::Point newOrigin = getOrigin(); if(interpolate && GET_SETTING("network.interpolation.enable", 1)) { networkError += originalOrigin - newOrigin; } else { networkError = Math::Point(0.0, 0.0, 0.0); } updatePhysicalInfo(); }
void PaintSubsystem::calculateBoostSpeeds() { Object::WorldManager::PlayerIteratorType iterator = worldManager->getPlayerIterator(); while(iterator.hasNext()) { Object::Player *player = iterator.next(); player->setPaintType(getPainting(player->getID())); if(getPainting(player->getID()) == Event::TogglePainting::NOTHING) { double factor = paintManager->weightedCellsInRadius( player->getPosition(), PAINTING_RADIUS, player->getTeamID()); factor *= GET_SETTING("game.paint.boostweightfactor", 2.0); factor += 1.0; factor = Math::bound(factor, GET_SETTING("game.paint.boostmin", 0.5), GET_SETTING("game.paint.boostmax", 1.5) ); double max_boost_increase = GET_SETTING("game.paint.maxboostincrease", 0.01); double current_boost = player->getSpeedBoost(); if (factor < current_boost+max_boost_increase) player->setSpeedBoost(factor); else player->setSpeedBoost(current_boost+max_boost_increase); } else { // if painting or erasing, slow down the player no matter what if (getPainting(player->getID()) == Event::TogglePainting::PAINTING) player->setSpeedBoost(GET_SETTING("game.paint.paintingboost", 0.8)); else player->setSpeedBoost(GET_SETTING("game.paint.erasingboost", 0.8)); } } }
int gt_parse_settings(config_t *config) { config_setting_t *node, *root, *elem; int i, len, ret; struct stat st; const char *filename; if (stat(GT_USER_SETTING_PATH, &st) == 0) filename = GT_USER_SETTING_PATH; else filename = GT_SETTING_PATH; ret = config_read_file(config, filename); if (ret == CONFIG_FALSE) return -1; root = config_root_setting(config); #define GET_SETTING(name, field) do { \ node = config_setting_get_member(root, name); \ if (node) { \ if (config_setting_type(node) != CONFIG_TYPE_STRING) { \ fprintf(stderr, "%s:%d: Expected string\n", \ config_setting_source_file(node), \ config_setting_source_line(node)); \ return -1; \ } \ gt_settings.field = config_setting_get_string(node); \ } \ } while (0) GET_SETTING("default-udc", default_udc); GET_SETTING("configfs-path", configfs_path); GET_SETTING("default-template-path", default_template_path); GET_SETTING("default-gadget", default_gadget); node = config_setting_get_member(root, "lookup-path"); if (node) { if (config_setting_is_aggregate(node) == CONFIG_FALSE) { fprintf(stderr, "%s:%d: Expected list\n", config_setting_source_file(node), config_setting_source_line(node)); return -1; } len = config_setting_length(node); gt_settings.lookup_path = calloc(len + 1, sizeof(*gt_settings.lookup_path)); for (i = 0; i < len; ++i) { elem = config_setting_get_elem(node, i); if (config_setting_type(elem) != CONFIG_TYPE_STRING) { fprintf(stderr, "%s:%d: Expected string\n", config_setting_source_file(elem), config_setting_source_line(elem)); goto out; } gt_settings.lookup_path[i] = config_setting_get_string(elem); } } #undef GET_SETTING return 0; out: free(gt_settings.lookup_path); return -1; }
double Suspension::calculateDownFactor(const Math::Point& axis) { return Math::maximum(GET_SETTING("physics.driving.mindownfactor", 0.1), -axis.normalized().getY()); }
void PathingUpdater::update() { if(!GET_SETTING("game.enablepathing", true)) { return; } Object::WorldManager::PlayerIteratorType iterator = worldManager->getPlayerIterator(); while (iterator.hasNext()) { Object::Player* player = iterator.next(); // Raycast downward to find the update point Math::Point origin_pos = player->getPosition(); Math::Point dir_pos = origin_pos; dir_pos.setY(dir_pos.getY() - VEHICLE_PATH_RAY_MAX_HEIGHT); Math::Point update_pos; Physics::PhysicsWorld *physicsWorld = Physics::PhysicsWorld::getInstance(); // Update if the player is above the track if(physicsWorld->raycastPoint(origin_pos, dir_pos, &update_pos)) { if(!player->getPathTracker()) continue; // no path tracker if(!player->getPathTracker()->atLeastOneNode()) continue; player->getPathTracker()->update(update_pos); // Start a new lap for the player if they have crossed the finish plane if (player->getPathTracker()->readyforNewLap() && raceManager->getBoundingPlane().pointInside(origin_pos)) { player->getPathTracker()->startNewLap(); LOG(WORLD, "Player: " << player->getID() << " has finished lap " << player->getPathTracker()->getNumLaps()); if (!player->getPathTracker()->getFinished() && player->getPathTracker()->getNumLaps() >= raceManager->getNumLapsToWin() && !GET_SETTING("internal.practicemode", false) ) { player->getPathTracker()->setFinished(true); Map::PlayerTimes::getInstance().setFinished( player->getID(), World::TimeElapsed::getInstance().getGameTime()); if (Bonus::GlobalBonusManager::getInstance()) Bonus::GlobalBonusManager::getInstance()->getPlayerBonuses(player->getID()).playerFinish(player->getPathTracker()->getRanking()); } if(playerManager->getPlayer() == player) { EMIT_EVENT(new Event::PlayerProgressEvent( player->getPathTracker()->getNumLaps(), player->getPathTracker()->getLapProgress(), raceManager->getNumLapsToWin() ) ); } } } warpDetector->checkForWarping(player); } raceManager->updatePlayerRankings(worldManager.get()); }
const World::PlayerIntention &WeightedDriver::getAction() { Object::Player *player = getPlayer(); const Map::PathNode *current = player->getPathTracker()->getCurrentNode(); Map::PathNode *next; Map::PathNode *next2; Map::PathNode *previous; // find the next node if (current->getProgress() < player->getPathTracker()->getLapProgress()) { int random = player->getID() % current->getNextNodes().size(); current = current->getNextNodes()[random]; } previous = current->getPreviousNodes()[0]; int random = player->getID() % current->getNextNodes().size(); next = current->getNextNodes()[random]; random = player->getID() % next->getNextNodes().size(); next2 = next->getNextNodes()[random]; // calculate various vectors Math::Point playerFacing = player->getPhysicalObject()->getFrontDirection(); Math::Point playerRight = player->getPhysicalObject()->getRightDirection(); Math::Point playerPos = player->getPosition(); double playerSpeed = player->getPhysicalObject()->getLinearSpeed(); double beeline_u = 1.0-Math::bound( playerPos.distance(current->getPosition())*GET_SETTING("ai.pathlookaheadfactor", 0.1), 0.0, 1.0); Math::Point beelinePoint = next->getPosition()*(1.0-beeline_u)+next2->getPosition()*beeline_u; Math::Point beelineDirection = (beelinePoint-playerPos).normalized(); Math::Point pathDirection = (current->getPosition() - previous->getPosition()).normalized() * 0.5 + (next->getPosition() - current->getPosition()).normalized() * 0.5; pathDirection.normalize(); // compute error due to being far away from this path edge double pathTurnSign = -Math::sign(playerRight.dotProduct(pathDirection)); double distanceOff = player->getPathTracker()->getProgressPosition().distance(playerPos); distanceOff *= GET_SETTING("ai.pathdistancefactor", 1.0); double angleTurnSign = -Math::sign(playerRight.dotProduct(beelineDirection)); double beelineAngle = Math::Geometry::vectorTo2DAngle(beelineDirection, Math::Y_AXIS); double facingAngle = Math::Geometry::vectorTo2DAngle(playerFacing, Math::Y_AXIS); double ccwAngle = std::fabs(beelineAngle-facingAngle); double cwAngle = std::fabs(Math::minimum(beelineAngle, facingAngle)+((PI*2.0)-Math::maximum(beelineAngle, facingAngle))); double offAngle = Math::minimum(ccwAngle, cwAngle); double offAngleFactor = (offAngle/PI) - GET_SETTING("ai.minangleoffset", 0.1); double angleFactor; if (offAngleFactor < 0.0) { offAngleFactor = 0.0; angleFactor = 0.0; } else angleFactor = offAngleFactor*GET_SETTING("ai.pathanglefactor", 1.0); // compute error due to facing in the wrong direction //double beelineAngle = playerRight.dotProduct(beelineDirection); double slowDownFactor = playerSpeed-GET_SETTING("ai.minslowdownspeed", 10.0); if (slowDownFactor < 0.0) slowDownFactor = 0.0; else slowDownFactor *= GET_SETTING("ai.angleslowdownfactor", 1.0); intention.setTurn(Math::bound((pathTurnSign * distanceOff) + (angleTurnSign * angleFactor), -1.0, 1.0)); intention.setAccel(Math::bound(2.0-(offAngleFactor*slowDownFactor), -1.0, 1.0)); detectSittingStill(); detectPaintAhead(); return intention; }
void WeightedDriver::detectPaintAhead() { if (!paintManager) return; double paintLookAhead = GET_SETTING("ai.paintlookahead", 2.0); double minLookAhead = GET_SETTING("ai.minlookahead", 2.0); Math::Point headingDir = getPlayer()->getPhysicalObject()->getLinearVelocity()*paintLookAhead; double dist = getPlayer()->getPhysicalObject()->getLinearSpeed()*headingDir.length(); if (dist < minLookAhead) { headingDir /= dist; headingDir *= minLookAhead; } Math::Point paintQueryPoint = getPlayer()->getPosition()+headingDir; double paintScore = paintManager->weightedCellsInRadius(paintQueryPoint, PAINTING_RADIUS, getPlayer()->getTeamID()); bool considering_change = false; bool change_to_paint = false; bool change_to_erase = false; double paintThresh = GET_SETTING("ai.stoppaintingthreshhold", 1.2); double eraseThresh = GET_SETTING("ai.starterasingthreshhold", 0.9); bool allow_overwrite = GET_SETTING("game.paint.allowoverwrite", false); bool allow_erase = GET_SETTING("game.paint.allowerase", true); if (!intention.getPaint()) { //Not painting: Change to painting if ((allow_overwrite || paintScore > eraseThresh) && paintScore < paintThresh) { considering_change = true; change_to_paint = true; } } if (!considering_change && !intention.getErase()) { //Not erasing: Change to erasing if (allow_erase && paintScore <= eraseThresh) { considering_change = true; change_to_erase = true; } } if (!considering_change && intention.getPaint() ) { //Is painting: Change to not painting considering_change = (paintScore >= paintThresh); } unsigned long now = Misc::Sleeper::getTimeMilliseconds(); if (considering_change) { if (paintSwitchTime + GET_SETTING("ai.paintswitchdelay", 500) < now) { if (change_to_paint) { intention.setPaint(true); intention.setErase(false); } else if (change_to_erase) { intention.setPaint(false); intention.setErase(true); } else { intention.setPaint(false); intention.setErase(false); } paintSwitchTime = now; } } else paintSwitchTime = now; }