CharString GadgetBeamWeapon::useTip( Noun * pTarget, bool shift ) const { if ( shift ) return "Toggle Point Defense."; // usage information CharString tip; float fEnergyMod = calculateModifier( MT_BEAM_ENERGY ); float fChargeScale = damageRatioInv() * fEnergyMod; tip += CharString().format("\nRecharge Time: <X;100>%.1fs", energyCost() / ( ( energyCharge() * fChargeScale ) * TICKS_PER_SECOND ) ); tip += CharString().format("\nEnergy Cost:<X;100>%.1f", energyCost() / 1000.0f ); float fLength = length() * calculateModifier( MT_BEAM_RANGE ); tip += CharString().format("\nMax Range:<X;100>%.1fgu", fLength ); // direct damage float fDamageMod = calculateModifier( MT_BEAM_DAMAGE ); float fDamage = damage() * fDamageMod; if ( damageRandom() > 0 ) { float fMaxDamage = fDamage + (damageRandom() * fDamageMod); tip += CharString().format( "\nDamage:<X;100>%.0f-%.0f", fDamage * duration(), fMaxDamage * duration() ); } else tip += CharString().format( "\nDamage:<X;100>%.1f", fDamage * duration() ); return tip; }
CharString GadgetArmor::useTip( Noun * pTarget, bool shift ) const { CharString tip; // usage information tip += CharString().format("\nCurrent HP:<X;100>%d/%d", m_Armor, maxArmor() ); float fMod = ( calculateModifier( MT_DEFENSE ) * 100 ) - 100; if ( fMod > 0 ) tip += CharString().format("\nDamage Reduction:<X;100>%.0f%%", fMod ); fMod = calculateModifier( MT_DEFENSE_RECHARGE ); return tip; }
int GadgetBeamWeapon::useEnergy( dword nTick, int energy ) { if ( m_Energy < energyCost() ) { int nElapsed = nTick - m_nEnergyTick; m_nEnergyTick = nTick; if ( nElapsed < 0 ) return energy; // currently firing // energy modifier makes weapons charge faster by using more energy.. cooldown gives the same effect float fEnergyMod = calculateModifier( MT_BEAM_ENERGY ); float fChargeScale = damageRatioInv() * fEnergyMod; m_fChargeRate += (fChargeScale * energyCharge()) * nElapsed; int chargeRate = floor( m_fChargeRate ); m_fChargeRate -= chargeRate; // leave fractional amount in the float for next update.. int charge = Min( Min( energyCost() - m_Energy, chargeRate ), energy ); // reduce ship energy energy -= charge; m_Energy += charge; // calculate the signature m_Signature = 0.1f * charge; } else { m_Signature = 0.0f; } return energy; }
bool GadgetBeamWeapon::usable( Noun * pTarget, bool shift ) const { if ( shift && allowPointDefense() ) return true; // allow point defense to be toggled if (! NounGadget::usable( pTarget, shift ) ) return false; if ( pointDefense() && WidgetCast<NounShip>( pTarget ) ) return false; // don't waste beams on ships if ( WidgetCast<NounShip>( parent() ) ) if ( ((NounShip *)parent())->testFlags( NounShip::FLAG_CLOAKED|NounShip::FLAG_IN_SAFE_ZONE ) ) return false; // weapons are disabled if ( pTarget == NULL ) return false; if ( destroyed() ) return false; if ( m_Energy < energyCost() ) return false; Vector3 position( pTarget->worldPosition() ); float fLength = length() * calculateModifier( MT_BEAM_RANGE ); if ( ((position - worldPosition()).magnitude() - pTarget->radius()) > fLength ) return false; // finally check the facing of the weapon return( checkFacing( position ) ); }
void NounShip::update() { NounBody::update(); // reduce signature if any modifiers are attached to do so.. m_LastSignature *= calculateModifier( MT_SIGNATURE, true ); // reset repaired damage m_nRepairedDamage = 0; }
//---------------------------------------------------------------------------- // TODO: add in enhancement data here void NounShip::updateCharacter() { PROFILE_FUNCTION(); float m = mass(); m *= calculateModifier( MT_MASS ); if ( m < 1.0f ) m = 1.0f; m_Sensor = baseSensor(); m_View = baseView(); m_MaxDamage = baseDamage(); m_MaxEnergy = baseEnergy(); m_MaxVelocity = baseVelocity(); m_MaxCargo = baseCargo(); m_MaxThrust = baseThrust(); for(int i=0;i<m_Gadgets.size();i++) { NounGadget * pGadget = m_Gadgets[i]; m += pGadget->addMass(); m_Sensor += pGadget->addSensor(); m_MaxVelocity += pGadget->addMaxVelocity() * velocityModifier(); m_MaxDamage += pGadget->addMaxDamage(); m_MaxEnergy += pGadget->addMaxEnergy(); m_MaxCargo += pGadget->addCargo(); m_MaxThrust += pGadget->addThrust(); } //calculate the acceleration by taking the thrust and dividing by the mass of the ship m_MaxAcceleration = m_MaxThrust / m; //calculate the turn rate (yaw) by taking the baseYaw(), dividing by the mass of the ship and adding a fraction of m_MaxAcceleration m_MaxYaw = (baseYaw() / m) + (m_MaxAcceleration / 10); //apply enhancements to ships, including gadget effects m_MaxVelocity *= calculateModifier( MT_DRIVE_VELOCITY ); m_MaxThrust *= calculateModifier( MT_DRIVE_THRUST ); m_MaxYaw *= calculateModifier( MT_TURNRATE ); m_MaxEnergy *= calculateModifier( MT_SHIPENERGY ); }
//Проверка отсутствия слишком низких характеристик bool MainWindow::checkForLowerAbility(QLabel * labelAbility1, QLabel * labelAbility2, QLabel * labelAbility3, QLabel * labelAbility4, QLabel * labelAbility5, QLabel * labelAbility6) { if ((calculateModifier(labelAbility1->text().toInt()) + calculateModifier(labelAbility2->text().toInt()) + calculateModifier(labelAbility3->text().toInt()) + calculateModifier(labelAbility4->text().toInt()) + calculateModifier(labelAbility5->text().toInt()) + calculateModifier(labelAbility6->text().toInt()) > 0) && ((labelAbility1->text().toInt() >= lowerThresholdAbility) || (labelAbility2->text().toInt() >= lowerThresholdAbility) || (labelAbility3->text().toInt() >= lowerThresholdAbility) || (labelAbility4->text().toInt() >= lowerThresholdAbility) || (labelAbility5->text().toInt() >= lowerThresholdAbility) || (labelAbility6->text().toInt() >= lowerThresholdAbility))) return true; else return false; }
int GadgetArmor::deflect( dword nType, int damage, Facing nFacing ) { if ( !canDeflect( nType ) ) return damage; // doesn't deflect this type of damage if ( (facingMountFlags( nFacing ) & mountFlags()) == 0 ) return damage; // wrong facing float fMod = damageModifier( nType ) * calculateModifier( MT_DEFENSE, true ); if ( fMod <= 0.0f ) return 0; // full damage reflected! int nModifiedDamage = fMod * damage; int nDeflected = Min( nModifiedDamage, m_Armor ); m_Armor -= nDeflected; if ( m_Armor > 0 ) return 0; // full damage is deflected... // return the un-modified damage value.. return damage - (nDeflected / fMod); }
void GadgetArmor::simulate( dword nTick ) { if ( nTick >= m_nArmorTick ) { m_nArmorTick = nTick + TICKS_PER_SECOND; // repair the armor int nMax = maxArmor(); if ( m_Armor < nMax ) { float fMod = calculateModifier( MT_DEFENSE_RECHARGE ); m_Armor = Clamp<int>( m_Armor + (repairRate() * fMod), 0, nMax ); } } if ( nTick >= m_nTipArmorTick ) { m_nTipArmorTick = nTick + ( TICKS_PER_SECOND * 5 ); m_nDamageRepaired = 0; } NounGadget::simulate( nTick ); }
void NounShip::inflictDamage( dword nWhen, Noun * pFrom, int damage, dword type, const Vector3 & direction ) { if ( isDestroyed() ) // don't bother if ship is already destroyed! return; // only increase timer if an enemy is attacking us - we don't want to promote griefing if ( isEnemy( pFrom ) ) setOutOfCombat(); // update combat timer // if we are not the server, then halve the damage applied to avoid over estimatation the damage we are // inflicting on another ship.. if (! isServer() ) damage = damage * CLIENT_SIDE_DAMAGE; Facing eFacing = getFacing( atan2( direction.x, direction.z ), false ); // apply any damage modifiers damage = damage * calculateModifier( MT_DAMAGE_REDUCTION, true ); LOG_DEBUG_LOW( "NounShip", CharString().format("Damaged inflicted, When: %u, From: %s, Damage: %d, Type: 0x%X, Facing: %u", nWhen, pFrom != NULL ? pFrom->name() : "NULL", damage, type, eFacing) ); // callback so AI controlled ships can receive notification that they have been damaged by someone onAttacked( pFrom, damage, type ); // calculate the points we're going to award to the attacker now before shields/armor reduce // the damage applied to the hull. float fDamagePoints = float( damage ) / DAMAGE_POINTS_DIV; // check shields if( damage > 0 ) { for(int i=0;i<m_Shields.size();i++) damage = m_Shields[i]->deflect( type, damage, eFacing, direction ); } // check armor if ( damage > 0 ) { for(int i=0;i<m_Armor.size();i++) damage = m_Armor[i]->deflect( type, damage, eFacing ); } int internalDamage = damage; // inflict internal damage if hull is 50% or less int halfHull = maxDamage() >> 1; if ( damage > 0 && m_Damage > halfHull && (type & DAMAGE_EMP) == 0 ) { // once hull is under 50%, start causing internal damage type |= DAMAGE_EMP; // scale internal damage up based on how much under 50% the hull internalDamage = (damage * (m_Damage - halfHull)) / halfHull; } if ( damage > 0 && (type & (DAMAGE_EMP|DAMAGE_ELF)) ) { // get the radius of this ship float myRadius = radius(); ASSERT( myRadius > 0.0f ); // calculate the hit position in object space of this ship Vector3 hitPosition( direction ); hitPosition.normalize(); // direction is not normalized, it's the delta from the projectile/explosion, to the center of the ship hitPosition *= -(myRadius * 0.5f); // flip the direction so it goes away from the center of the ship if ( isServer() && m_Gadgets.size() > 0 ) { // seed with the time-stamp of the damage so we get the same gadget damaged // on all clients. srand( nWhen ); int nPicked = rand() % m_Gadgets.size(); NounGadget * pDamageGadget = m_Gadgets[ nPicked ]; if ( pDamageGadget != NULL ) { float fDistance = (pDamageGadget->position() - hitPosition).magnitude(); float fScale = (1.0f - (fDistance / myRadius)); if ( fScale > 0.0f ) pDamageGadget->inflictDamage( nWhen, pFrom, fScale * internalDamage, type, direction ); } } } if ( damage > 0 && (type & DAMAGE_ELF) ) { // drain energy from main energy bank int drained = Min( energy(), damage ); setEnergy( energy() - drained ); // give energy to attacking ship if ( WidgetCast<NounShip>( pFrom ) ) ((NounShip *)pFrom)->setEnergy( ((NounShip *)pFrom)->energy() + drained ); } // update the stats if it is another ship - NOTE this needs to happen before the DestroyShip // code below otherwise a player who is damaging this ship may not get credit/loot. if ( WidgetCast<NounShip>( pFrom ) ) { NounShip * pFromShip = (NounShip *)pFrom; ASSERT( pFromShip ); // set combat timer on attacking ship pFromShip->setOutOfCombat(); // non-hull damages gives a lessor percentage of points.. if ( damage <= 0 ) fDamagePoints *= NOHULL_DAMAGE_POINTS_SCALE; if (! isFriend( pFrom ) ) { // if the attacking ship is destroyed, then treat this as kamikaze points if ( pFromShip->isDestroyed() ) gameContext()->gameUser()->onKamikaze( pFrom, 1.0f ); else gameContext()->gameUser()->onDamageShip( pFrom, fDamagePoints, this); } else { gameContext()->gameUser()->onFriendlyFire( pFrom, fDamagePoints ); } } if ( damage > 0 && (type & (DAMAGE_KINETIC|DAMAGE_ENERGY)) ) { ASSERT( context() ); // cap the damage, damage = Min( maxDamage() - m_Damage, damage ); // add the damage to the hull m_Damage = Min( m_Damage + damage, maxDamage() ); // update the damage bits updateDamageBits(); // check if the ship was destroyed if ( isServer() && isDestroyed() ) { // ship is destroyed, send out verb to destroy this ship to all local clients Verb::Ref( new VerbDestroyShip( this, pFrom, false ) ); } } }
//Процедура отображения полученных вычислений пользователю void MainWindow::setupAbilites(QLabel * labelAbility, QLabel * labelAbilityModifier) { labelAbility->setText(QString::number(calculateCandidatToBecomeAbility())); labelAbilityModifier->setText(QString::number(calculateModifier(labelAbility->text().toInt()))); }
void GadgetBeamWeapon::render( RenderContext &context, const Matrix33 & frame, const Vector3 & position ) { NounGadget::render( context, frame, position ); // render the beam if ( useActive() && m_Target.valid() && m_Duration > 0 ) { Vector3 positionVS( context.worldToView( position ) ); float fLength = length() * calculateModifier( MT_BEAM_RANGE ); if (! context.sphereVisible( positionVS, fLength ) ) return; Noun * pTarget = m_Target; Vector3 position2( pTarget->worldPosition() ); if ( ( ( position2 - worldPosition() ).magnitude() - pTarget->radius() ) > fLength ) return; if ( !checkFacing( position2 ) ) return; int tailAlpha = 32; if ( m_Hit.valid() ) tailAlpha = 128; Vector3 direction( pTarget->worldPosition() - position ); Vector3 head( position ); Vector3 tail( head + direction ); // calculate the material wrap float h = 0.05f; float w = 10.0f; Material * pTracerMaterial = tracerMaterial(); if ( pTracerMaterial != NULL ) { h = pTracerMaterial->height(); w = pTracerMaterial->width(); Material::push( context, pTracerMaterial ); } else Material::push( context, Color(255,0,0), true, PrimitiveMaterial::ADDITIVE ); float u = (head - tail).magnitude() / w; const Vector3 N( 0,0, 0); const Vector3 Y( 0, h, 0 ); const Vector3 X( h, 0, 0 ); const Color HC( 255,255,255,255 ); const Color TC( 255,255,255,tailAlpha ); VertexL beamY[4] = { VertexL( head + Y, N, HC, u, 0.0f ), VertexL( tail + Y, N, TC, 0.0f, 0.0f ), VertexL( tail - Y, N, TC, 0.0f, 1.0f ), VertexL( head - Y, N, HC, u, 1.0f ), }; VertexL beamX[4] = { VertexL( head + X, N, HC, u, 0.0f ), VertexL( tail + X, N, TC, 0.0f, 0.0f ), VertexL( tail - X, N, TC, 0.0f, 1.0f ), VertexL( head - X, N, HC, u, 1.0f ), }; DisplayDevice * pDisplay = context.device(); ASSERT( pDisplay ); context.pushIdentity(); PrimitiveTriangleFanDL::push( pDisplay, 4, beamY ); PrimitiveTriangleFanDL::push( pDisplay, 4, beamX ); } }
void GadgetBeamWeapon::simulate( dword nTick ) { if ( m_Duration > 0 ) { // reduce the beam duration m_Duration--; // validate the target bool targetValid = false; if ( WidgetCast<NounTarget>( m_Target.pointer() ) != NULL ) targetValid = true; else if ( m_Target.valid() && m_Target->zone() != NULL ) targetValid = true; if ( targetValid ) { float fLength = length() * calculateModifier( MT_BEAM_RANGE ); Vector3 beamOrigin( worldPosition() ); Vector3 beamEnd( m_Target->worldPosition() ); Vector3 direction( beamEnd - beamOrigin ); direction.normalize(); direction *= fLength; // check for collision Array< GameContext::NounCollision > check; if ( context()->proximityCheck( beamOrigin, fLength, check ) ) { NounGame * pCollide = NULL; float maxDistance = fLength; float collideDistance = maxDistance; Vector3 collidePosition = beamEnd; for(int i=0;i<check.size();i++) { NounGame * pCheck = WidgetCast<NounGame>( check[ i ].pNoun ); if ( pCheck == NULL ) continue; if ( pCheck == (Noun *)parent() ) continue; // don't collide with our own ship! if ( WidgetCast<NounProjectile>( pCheck ) ) if ( pCheck != m_Target ) continue; // don't destroy projectiles that are not our target! if ( WidgetCast<NounNebula>( pCheck ) || WidgetCast<NounField>( pCheck ) ) continue; // don't collide with nebula or fields // object is close, final check Vector3 intersect; if ( pCheck->intersect( beamOrigin, direction, intersect ) ) { float distance = (beamOrigin - intersect).magnitude(); if ( distance < collideDistance ) { pCollide = pCheck; collideDistance = distance; collidePosition = intersect; } } } // does the projectile have a valid collision if ( pCollide != NULL ) { // create the hit effect if ( context()->isClient() ) { if (! m_Hit.valid() ) { // create the hit effect SceneryEffect * pHit = new SceneryEffect; pHit->setNounContext( new NounContext( hitEffect() ) ); pHit->setContext( context() ); pHit->setPosition( collidePosition ); pHit->setFrame( Matrix33( direction ) ); pHit->setLife( m_Duration * TICK_DURATION_S ); context()->attachNoun( pHit ); // track the current hit effect m_Hit = pHit; } else { // update the current hit effect position m_Hit->setWorldPosition( collidePosition ); } } if ( pCollide->canDamage( damageType() ) ) { float fDamageMod = calculateModifier( MT_BEAM_DAMAGE ); int inflict = damage() * fDamageMod; if ( damageFalloff() <= 1 ) { if ( !reverseFalloff() ) { // damage is less with range int falloff = inflict * ( 1.0f - damageFalloff() ); float scale = 1.0f - ( collideDistance / maxDistance ); inflict -= scale * falloff; } else { // damage is more with range int falloff = inflict * damageFalloff(); float scale = collideDistance / maxDistance; inflict += scale * falloff; } } if ( damageRandom() > 0 ) { int nRandomDamage = (int)(damageRandom() * fDamageMod); if ( nRandomDamage > 0 ) inflict += rand() % nRandomDamage; } if ( inflict > 0 ) // make sure the falloff didn't negate the damage { pCollide->inflictDamage( tick(), parentBody(), inflict, damageType(), pCollide->worldFrame() * (collidePosition - pCollide->worldPosition()) ); } } else if ( WidgetCast<NounProjectile>( pCollide ) ) { // explode the projectile ((NounProjectile *)pCollide)->explode(); } } else if ( m_Hit.valid() ) { // stop the hit effect if ( m_bDetachHitEffect ) ((SceneryEffect *)m_Hit.pointer())->setLife( 0.0f ); m_Hit = NULL; } } } // has the beam finished if ( m_Duration <= 0 ) { // stop any hit effect if ( m_Hit.valid() ) { if ( m_bDetachHitEffect ) ((SceneryEffect *)m_Hit.pointer())->setLife( 0.0f ); m_Hit = NULL; } m_Target = NULL; } } NounGadget::simulate( nTick ); }
float GadgetScanner::addSensor() const { return active() ? (damageRatioInv() * sensor() * calculateModifier( MT_SCANNER ) ) : 0.0f; }