/*! Calculates the amount of Stamina needed for a move of the passed character. */ bool cMovement::consumeStamina( P_PLAYER pChar, bool running ) { // Dead people and gms don't care about weight if ( pChar->isDead() || pChar->isGMorCounselor() ) { return true; } // Calculate the stones we weight too much int overWeight = ( int ) ( pChar->weight() - pChar->maxWeight() ); bool mounted = pChar->atLayer( cBaseChar::Mount ) != 0; bool update = false; // We carry too much if ( overWeight > 0 ) { // How much stamina we loose int amount = 5 + ( overWeight / 25 ); // Only one third loss if mounted if ( mounted ) { amount = amount / 3; } // Double loss if running if ( running ) { amount = amount * 2; } // Set the new stamina pChar->setStamina( wpMax<Q_INT16>( 0, pChar->stamina() - amount ), false ); update = true; // We are overloaded if ( pChar->stamina() == 0 ) { pChar->socket()->updateStamina(); pChar->socket()->clilocMessage( 500109 ); return false; } } // If we have less than 10% stamina left, we loose // stamina more quickly if ( ( pChar->stamina() * 100 ) / wpMax<ushort>( 1, pChar->maxStamina() ) < 10 ) { pChar->setStamina( wpMax<Q_INT16>( 0, pChar->stamina() - 1 ), false ); update = true; } // We can't move anymore because we are exhausted if ( pChar->stamina() == 0 ) { pChar->socket()->updateStamina(); pChar->socket()->clilocMessage( 500110 ); return false; } // Normally reduce stamina every few steps if ( pChar->stepsTaken() % ( mounted ? 48 : 16 ) == 0 ) { pChar->setStamina( wpMax<Q_INT16>( 0, pChar->stamina() - 1 ) ); update = true; } if ( update ) { pChar->socket()->updateStamina(); } return true; }
/*! This handles if a character actually tries to walk (NPC & Player) */ bool cMovement::Walking( P_CHAR pChar, Q_UINT8 dir, Q_UINT8 sequence ) { if ( !pChar ) return false; // Scripting if ( pChar->onWalk( dir, sequence ) ) return false; P_PLAYER player = dynamic_cast<P_PLAYER>( pChar ); // Is the sequence in order ? if ( player && player->socket() && !verifySequence( player->socket(), sequence ) ) return false; // If checking for weight is more expensive, shouldn't we check for frozen first? if ( pChar->isFrozen() ) { if ( player && player->socket() ) player->socket()->denyMove( sequence ); return false; } // save our original location before we even think about moving const Coord oldpos( pChar->pos() ); // If the Direction we're moving to is different from our current direction // We're turning and NOT moving into a specific direction // Clear the running flag here (!) // If the direction we're moving is already equal to our current direction bool running = dir & 0x80; dir = dir & 0x7; // Remove all unneeded stuff pChar->setRunning( running ); bool turning = dir != pChar->direction(); // This happens if we're moving if ( !turning ) { // Note: Do NOT use the copy constructor as it'll create a reference Coord newCoord = calcCoordFromDir( dir, pChar->pos() ); // Check if the stamina parameters if ( player && !consumeStamina( player, running ) ) { if ( player->socket() ) player->socket()->denyMove( sequence ); return false; } // Check if we're going to collide with characters if ( player ) { // Player vs characters if ( player->socket() && player->pos().map == 0 && !player->account()->isStaff() ) { // Currently hard-limiting collisions to Felucca; this should be a server option! MapCharsIterator charCollisions = MapObjects::instance()->listCharsAtCoord( newCoord ); for ( P_CHAR them = charCollisions.first(); them; them = charCollisions.next() ) { if ( them == player ) continue; P_PLAYER otherplayer = dynamic_cast<P_PLAYER>( them ); if ( otherplayer && otherplayer->account()->isStaff() ) continue; // no collisions against the staff if ( wpAbs<SI08>( newCoord.z - them->pos().z ) < P_M_MAX_Z_CLIMB ) { // to push another char we must have maximum stamina if ( player->stamina() >= player->maxStamina() ) { player->socket()->clilocMessage( them->isHidden() ? 1019043 : 1019042 ); player->setStamina( player->stamina() - 10 ); break; } else { player->socket()->denyMove( sequence ); return false; } } } } } else { // NPC vs characters P_NPC npc = dynamic_cast<P_NPC>( pChar ); if ( npc && CheckForCharacterAtXYZ( pChar, newCoord ) ) { npc->clearPath(); return false; } } // Check if the char can move to those new coordinates // It is going to automatically calculate the new coords (!) if ( !mayWalk( pChar, newCoord ) ) { if ( player && player->socket() ) player->socket()->denyMove( sequence ); return false; } else { if ( player && player->socket() ) player->socket()->allowMove( sequence ); } // We moved so let's update our location pChar->moveTo( newCoord ); pChar->setStepsTaken( pChar->stepsTaken() + 1 ); pChar->setLastMovement( Server::instance()->time() ); checkStealth( pChar ); // Reveals the user if neccesary } else { if ( player && player->socket() ) player->socket()->allowMove( sequence ); } // do all of the following regardless of whether turning or moving i guess // set the player direction to contain only the cardinal direction bits pChar->setDirection( dir ); Coord upperLeft = pChar->pos() - Coord( ( VISRANGE + 1 ), ( VISRANGE + 1 ) ); Coord lowerRight = pChar->pos() + Coord( ( VISRANGE + 1 ), ( VISRANGE + 1 ) ); MapCharsIterator ri = MapObjects::instance()->listCharsInRect( upperLeft, lowerRight ); for ( P_CHAR observer = ri.first(); observer; observer = ri.next() ) { if ( observer == pChar ) continue; bool wasVisible = observer->pos().distance( oldpos ) < VISRANGE; // We were previously in range bool isVisible = pChar->dist( observer ) < VISRANGE; // We are now in range // If we are a player, send us new characters if ( player && player->socket() ) { // Send the observer to us if he was previously not visible and came into range recently if ( !wasVisible && isVisible ) { player->socket()->sendChar( observer ); } } // Send our movement to the observer P_PLAYER otherplayer = dynamic_cast<P_PLAYER>( observer ); if ( !otherplayer || !otherplayer->socket() ) { continue; // Skip this character, it's a player. // TODO: OnSeePlayer, OnLoosePlayer } if ( wasVisible ) { if ( isVisible ) { otherplayer->socket()->updateChar( pChar ); // We walked inside the visible range } else { otherplayer->socket()->removeObject( pChar ); // We walked out of visible range } } else if ( isVisible ) { otherplayer->socket()->sendChar( pChar ); // We walked into visible range } } // If we really moved handle teleporters and new items if ( !turning ) { handleItems( pChar, oldpos ); handleMultis( pChar, oldpos ); handleTeleporters( pChar, oldpos ); } return true; }