#include "uox3.h" #include "skills.h" #include "magic.h" #include "cMagic.h" #include "CJSMapping.h" #include "mapstuff.h" #include "cScript.h" #include "cEffects.h" #include "teffect.h" #include "CPacketSend.h" #include "classes.h" #include "regions.h" #include "combat.h" #include "CJSMapping.h" #include "townregion.h" #include "StringUtility.hpp" #include //o-----------------------------------------------------------------------------------------------o //| Function - void deathAction( CChar *s, CItem *x, UI08 fallDirection ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Plays a characters death animation and sends it to nearby players //o-----------------------------------------------------------------------------------------------o void cEffects::deathAction( CChar *s, CItem *x, UI08 fallDirection ) { CPDeathAction toSend( (*s), (*x) ); toSend.FallDirection( fallDirection ); for (auto &mSock : FindNearbyPlayers(s)){ if( mSock->CurrcharObj() != s ) // Death action screws up corpse display for the person who died. mSock->Send( &toSend ); } } //o-----------------------------------------------------------------------------------------------o //| Function - void SpawnBloodEffect( UI16 bloodColour ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Spawns a blood effect item and returns the newly created object to caller //o-----------------------------------------------------------------------------------------------o CItem * cEffects::SpawnBloodEffect( UI08 worldNum, UI16 instanceID, UI16 bloodColour, BloodTypes bloodType ) { // Use default blood decay timer from ini setting R32 bloodDecayTimer = static_cast(cwmWorldState->ServerData()->SystemTimer( tSERVER_BLOODDECAY )); // Blood effects, sorted by size of effect from small to large std::vector bloodIDs{ 0x1645, 0x122C, 0x122E, 0x122B, 0x122D, 0x122A, 0x122F }; UI16 bloodEffectID = 0x122c; switch( bloodType ) { case BLOOD_DEATH: // Use corpse-specific blood decay timer instead of default one bloodDecayTimer = static_cast( cwmWorldState->ServerData()->SystemTimer( tSERVER_BLOODDECAYCORPSE ) ); // Randomize between large blood effects bloodEffectID = bloodIDs[RandomNum( static_cast( 3 ), static_cast( 6 ) )]; break; case BLOOD_BLEED: // Randomize between small blood effects bloodEffectID = bloodIDs[RandomNum( static_cast( 0 ), static_cast( 3 ) )]; break; case BLOOD_CRITICAL: // Randomize between medium to large blood effects bloodEffectID = bloodIDs[RandomNum( static_cast( 2 ), static_cast( 5 ) )]; // More blood, so increase the time it takes to decay bloodDecayTimer *= 1.5; break; default: break; } // Spawn the blood effect item CItem *blood = Items->CreateBaseItem( worldNum, OT_ITEM, instanceID, false ); if( ValidateObject( blood ) ) { blood->SetID( bloodEffectID ); blood->SetColour( bloodColour ); blood->SetDecayable( true ); blood->SetDecayTime( BuildTimeValue( bloodDecayTimer )); return blood; } return nullptr; } //o-----------------------------------------------------------------------------------------------o //| Function - void PlayMovingAnimation( CBaseObject *source, CBaseObject *dest, UI16 effect, //| UI08 speed, UI08 loop, bool explode, UI32 hue, UI32 renderMode ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Sends a message to client to display a moving animation from source object to target object //o-----------------------------------------------------------------------------------------------o void cEffects::PlayMovingAnimation( CBaseObject *source, CBaseObject *dest, UI16 effect, UI08 speed, UI08 loop, bool explode, UI32 hue, UI32 renderMode ) { //0x0f 0x42 = arrow 0x1b 0xfe=bolt if( !ValidateObject( source ) || !ValidateObject( dest ) ) return; CPGraphicalEffect2 toSend( 0, (*source), (*dest) ); if( source == dest ) toSend.Effect( 0x03 ); toSend.Model( effect ); toSend.SourceLocation( (*source) ); toSend.TargetLocation( (*dest) ); toSend.Speed( speed ); toSend.Duration( loop ); toSend.ExplodeOnImpact( explode ); toSend.Hue( hue ); toSend.RenderMode( renderMode );//0x00000004 for(auto &tSock : Network->connClients){ if( objInRange( tSock, source, DIST_SAMESCREEN ) && objInRange( tSock, dest, DIST_SAMESCREEN ) ) tSock->Send( &toSend ); } } //o-----------------------------------------------------------------------------------------------o //| Function - void PlayMovingAnimation( CBaseObject *source, SI16 x, SI16 y, SI08 z, UI16 effect, //| UI08 speed, UI08 loop, bool explode, UI32 hue, UI32 renderMode ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Sends a message to client to display a moving animation from source object to target location //o-----------------------------------------------------------------------------------------------o void cEffects::PlayMovingAnimation( CBaseObject *source, SI16 x, SI16 y, SI08 z, UI16 effect, UI08 speed, UI08 loop, bool explode, UI32 hue, UI32 renderMode ) { //0x0f 0x42 = arrow 0x1b 0xfe=bolt if( !ValidateObject( source ) ) return; CPGraphicalEffect2 toSend( 0, (*source) ); toSend.TargetSerial( INVALIDSERIAL ); toSend.Model( effect ); toSend.SourceLocation( (*source) ); toSend.TargetLocation( x, y, z ); toSend.Speed( speed ); toSend.Duration( loop ); toSend.ExplodeOnImpact( explode ); toSend.Hue( hue ); toSend.RenderMode( renderMode ); for (auto &mSock : FindNearbyPlayers( source, DIST_SAMESCREEN )) { mSock->Send( &toSend ); } } //o-----------------------------------------------------------------------------------------------o //| Function - void PlayMovingAnimation( SI16 srcX, SI16 srcY, SI08 srcZ, SI16 x, SI16 y, SI08 z, UI16 effect, //| UI08 speed, UI08 loop, bool explode, UI32 hue, UI32 renderMode ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Sends a message to client to display a moving animation from source object to target location //o-----------------------------------------------------------------------------------------------o void cEffects::PlayMovingAnimation( SI16 srcX, SI16 srcY, SI08 srcZ, SI16 x, SI16 y, SI08 z, UI16 effect, UI08 speed, UI08 loop, bool explode, UI32 hue, UI32 renderMode ) { //0x0f 0x42 = arrow 0x1b 0xfe=bolt CPGraphicalEffect2 toSend( 0 ); toSend.TargetSerial( INVALIDSERIAL ); toSend.Model( effect ); toSend.SourceLocation( srcX, srcY, srcZ ); toSend.TargetLocation( x, y, z ); toSend.Speed( speed ); toSend.Duration( loop ); toSend.ExplodeOnImpact( explode ); toSend.Hue( hue ); toSend.RenderMode( renderMode ); for (auto &mSock : FindNearbyPlayers( srcX, srcY, srcZ, DIST_SAMESCREEN )) { mSock->Send( &toSend ); } } //o-----------------------------------------------------------------------------------------------o //| Function - void PlayCharacterAnimation( CChar *mChar, UI16 actionID, UI08 frameDelay, bool playBackwards ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Sends message to client to make character perform specified action/anim //o-----------------------------------------------------------------------------------------------o void cEffects::PlayCharacterAnimation( CChar *mChar, UI16 actionID, UI08 frameDelay, UI08 frameCount, bool playBackwards ) { CPCharacterAnimation toSend = (*mChar); toSend.Action( actionID ); toSend.FrameDelay( frameDelay ); toSend.FrameCount( frameCount ); toSend.DoBackwards( playBackwards ); for (auto &mSock : FindNearbyPlayers( mChar )) { mSock->Send( &toSend ); } } //o-----------------------------------------------------------------------------------------------o //| Function - void PlayNewCharacterAnimation( CChar *mChar, UI16 actionID, UI16 subActionID, UI08 subSubActionID ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Sends message to client to make character perform specified action/anim //| in client versions above 7.0.0.0 //o-----------------------------------------------------------------------------------------------o void cEffects::PlayNewCharacterAnimation( CChar *mChar, UI16 actionID, UI16 subActionID, UI08 subSubActionID ) { CPNewCharacterAnimation toSend = (*mChar); toSend.Action( actionID ); toSend.SubAction( subActionID ); toSend.SubSubAction( subSubActionID ); for (auto &mSock : FindNearbyPlayers( mChar )) { mSock->Send( &toSend ); } } //o-----------------------------------------------------------------------------------------------o //| Function - void PlaySpellCastingAnimation( CChar *mChar, UI16 actionID ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Handles spellcasting action //o-----------------------------------------------------------------------------------------------o void cEffects::PlaySpellCastingAnimation( CChar *mChar, UI16 actionID ) { if( mChar->GetBodyType() == BT_GARGOYLE || ( cwmWorldState->ServerData()->ForceNewAnimationPacket() && ( mChar->GetSocket() == nullptr || mChar->GetSocket()->ClientType() >= CV_SA2D ))) { if( mChar->GetBodyType() == BT_GARGOYLE ) { if( actionID == 0x10 ) PlayNewCharacterAnimation( mChar, N_ACT_SPELL, S_ACT_SPELL_AREA ); // Action: 0x0b, SubAction: spell variation 0x01 else if( actionID == 0x11 ) PlayNewCharacterAnimation( mChar, N_ACT_SPELL, S_ACT_SPELL_TARGET ); // Action: 0x0b, SubAction: spell variation 0x00 } else { // Sub actions are in reverse order for humans! if( actionID == 0x10 ) PlayNewCharacterAnimation( mChar, N_ACT_SPELL, S_ACT_SPELL_TARGET ); // Action: 0x0b, SubAction: spell variation 0x00 else if( actionID == 0x11 ) PlayNewCharacterAnimation( mChar, N_ACT_SPELL, S_ACT_SPELL_AREA ); // Action: 0x0b, SubAction: spell variation 0x01 } return; } else if( mChar->IsOnHorse() ) { if( actionID == 0x10 ) PlayCharacterAnimation( mChar, ACT_MOUNT_ATT_1H, 0, 5 ); // 0x1A else if( actionID == 0x11 ) PlayCharacterAnimation( mChar, ACT_MOUNT_ATT_BOW, 0, 5 ); // 0x1B return; } if( !cwmWorldState->creatures[mChar->GetID()].IsHuman() && actionID == 0x22 ) return; PlayCharacterAnimation( mChar, actionID, 0, 7 ); } //o-----------------------------------------------------------------------------------------------o //| Function - void PlayStaticAnimation( CBaseObject *target, UI16 effect, UI08 speed, UI08 loop, bool explode ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Sends message to client to play a static animation effect on an object //o-----------------------------------------------------------------------------------------------o void cEffects::PlayStaticAnimation( CBaseObject *target, UI16 effect, UI08 speed, UI08 loop, bool explode ) { if( !ValidateObject( target ) ) return; CPGraphicalEffect toSend( 3, (*target) ); if( target->GetObjType() != OT_CHAR ) toSend.TargetSerial( (*target) ); else toSend.TargetSerial( INVALIDSERIAL ); toSend.Model( effect ); toSend.SourceLocation( (*target) ); if( target->GetObjType() != OT_CHAR ) toSend.TargetLocation( (*target) ); else toSend.ZTrg( 15 ); toSend.Speed( speed ); toSend.Duration( loop ); toSend.AdjustDir( false ); toSend.ExplodeOnImpact( explode ); for (auto &mSock : FindNearbyPlayers( target, DIST_SAMESCREEN )) { mSock->Send( &toSend ); } } //o-----------------------------------------------------------------------------------------------o //| Function - void cEffects::PlayStaticAnimation( SI16 x, SI16 y, SI08 z, UI16 effect, UI08 speed, UI08 loop, bool explode ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Sends message to client to play a static animation effect at a location //o-----------------------------------------------------------------------------------------------o void cEffects::PlayStaticAnimation( SI16 x, SI16 y, SI08 z, UI16 effect, UI08 speed, UI08 loop, bool explode ) { CPGraphicalEffect toSend( 2 ); toSend.Model( effect ); toSend.SourceLocation( x, y, z ); toSend.TargetLocation( x, y, z ); toSend.Speed( speed ); toSend.Duration( loop ); toSend.AdjustDir( false ); toSend.ExplodeOnImpact( explode ); std::for_each(Network->connClients.begin(),Network->connClients.end(),[&toSend](CSocket *tSock){ tSock->Send( &toSend ); }); } //o-----------------------------------------------------------------------------------------------o //| Function - void bolteffect( CChar *player ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Sends message to client to play a lightning bolt effect on a specific player //o-----------------------------------------------------------------------------------------------o void cEffects::bolteffect( CChar *player ) { if( !ValidateObject( player ) ) return; CPGraphicalEffect toSend( 1, (*player) ); toSend.SourceLocation( (*player) ); toSend.ExplodeOnImpact( false ); toSend.AdjustDir( false ); for (auto &mSock : FindNearbyPlayers( player )) { mSock->Send( &toSend ); } } //o-----------------------------------------------------------------------------------------------o //| Function - void explodeItem( CSocket *mSock, CItem *nItem, UI32 damage, UI08 damageType ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Explode an item, dealing damage to nearby characters and deleting the item //o-----------------------------------------------------------------------------------------------o void explodeItem( CSocket *mSock, CItem *nItem, UI32 damage = 0, UI08 damageType = 0, bool explodeNearby = true ) { CChar *c = mSock->CurrcharObj(); UI32 dx, dy, dz; // - send the effect (visual and sound) if( nItem->GetCont() != nullptr ) { Effects->PlayStaticAnimation( c, 0x36B0, 0x00, 0x09 ); nItem->SetCont( nullptr ); nItem->SetLocation( c ); Effects->PlaySound( c, 0x0207 ); } else { Effects->PlayStaticAnimation( nItem, 0x36B0, 0x00, 0x09, 0x00 ); Effects->PlaySound( nItem, 0x0207 ); } UI32 len = nItem->GetTempVar( CITV_MOREX ) / 250; //4 square max damage at 100 alchemy if( damage == 0 ) { damage = RandomNum( nItem->GetTempVar( CITV_MOREZ ) * 5, nItem->GetTempVar( CITV_MOREZ ) * 10 ); } if( damage < 5 ) damage = RandomNum( 5, 10 ); // 5 points minimum damage if( len < 2 ) len = 2; // 2 square min damage range if( explodeNearby ) { // Explode for characters nearby for (auto &Cell : MapRegion->PopulateList( nItem )){ bool chain = false; GenericList< CChar * > *regChars = Cell->GetCharList(); regChars->Push(); for( CChar *tempChar = regChars->First(); !regChars->Finished(); tempChar = regChars->Next() ) { if( !ValidateObject( tempChar ) || tempChar->GetInstanceID() != nItem->GetInstanceID() ) continue; if( tempChar->GetRegion()->IsSafeZone() ) { // Character is in a safe zone, ignore damage continue; } dx = abs( tempChar->GetX() - nItem->GetX() ); dy = abs( tempChar->GetY() - nItem->GetY() ); dz = abs( tempChar->GetZ() - nItem->GetZ() ); if( dx <= len && dy <= len && dz <= len ) { if( !tempChar->IsGM() && !tempChar->IsInvulnerable() && ( tempChar->IsNpc() || isOnline( (*tempChar) ) ) ) { //UI08 hitLoc = Combat->CalculateHitLoc(); damage = Combat->ApplyDefenseModifiers( HEAT, c, tempChar, ALCHEMY, 0, ( static_cast(damage) + ( 2 - std::min( dx, dy ))), true ); [[maybe_unused]] bool retVal = tempChar->Damage( static_cast(damage), HEAT, c, true ); } } } regChars->Pop(); GenericList< CItem * > *regItems = Cell->GetItemList(); regItems->Push(); for( CItem *tempItem = regItems->First(); !regItems->Finished(); tempItem = regItems->Next() ) { if( tempItem->GetInstanceID() != nItem->GetInstanceID() ) { // Item is in a different instanceID - ignore continue; } if( tempItem->GetID() == 0x0F0D && tempItem->GetType() == IT_POTION ) { dx = abs( nItem->GetX() - tempItem->GetX() ); dy = abs( nItem->GetY() - tempItem->GetY() ); dz = abs( nItem->GetZ() - tempItem->GetZ() ); if( dx <= 2 && dy <= 2 && dz <= 2 && !chain ) // only trigger if in a 2*2*2 cube { if( !( dx == 0 && dy == 0 && dz == 0 ) ) { if( RandomNum( 0, 1 ) == 1 ) chain = true; Effects->tempeffect( c, tempItem, 17, 0, 1, 0 ); } } } } regItems->Pop(); } } else { // Only affect character associated with item if( !c->IsGM() && !c->IsInvulnerable() && ( c->IsNpc() || isOnline( (*c) ))) { damage = Combat->ApplyDefenseModifiers( HEAT, c, c, ALCHEMY, 0, static_cast(damage), true ); [[maybe_unused]] bool retVal = c->Damage( static_cast(damage), HEAT, c, true ); } } nItem->Delete(); } //o-----------------------------------------------------------------------------------------------o //| Function - void HandleMakeItemEffect( CTEffect *tMake ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Applies maker's mark to crafted items and plays sound FX //o-----------------------------------------------------------------------------------------------o void cEffects::HandleMakeItemEffect( CTEffect *tMake ) { if( tMake == nullptr ) return; CChar *src = calcCharObjFromSer( tMake->Source() ); UI16 iMaking = tMake->More2(); createEntry *toMake = Skills->FindItem( iMaking ); if( toMake == nullptr ) return; CSocket *sock = src->GetSocket(); std::string addItem = toMake->addItem; UI16 amount = 1; auto csecs = oldstrutil::sections( addItem, "," ); if( csecs.size() > 1 ) { amount = oldstrutil::value(oldstrutil::extractSection(addItem, ",", 1, 1 )); addItem = oldstrutil::extractSection(addItem, ",", 0, 0 ); } UI16 iColour = 0; if( ValidateObject( src )) { // Get colour of the resource targeted by player (if any), so it can be applied to the item being made TAGMAPOBJECT tempTagObj = src->GetTempTag( "craftItemColor" ); if( tempTagObj.m_ObjectType == TAGMAP_TYPE_INT && tempTagObj.m_IntValue > 0 ) { iColour = static_cast( tempTagObj.m_IntValue ); } } CItem *targItem = Items->CreateScriptItem( sock, src, addItem, amount, OT_ITEM, true, iColour ); for( size_t skCounter = 0; skCounter < toMake->skillReqs.size(); ++skCounter ) src->SkillUsed( false, toMake->skillReqs[skCounter].skillNumber ); if( targItem == nullptr ) { Console.error( oldstrutil::format("cSkills::MakeItem() bad script item # %s, made by player 0x%X", addItem.c_str(), src->GetSerial()) ); return; } else { // targItem->SetName2( targItem->GetName().c_str() ); // Why was this here? SI32 rank = Skills->CalcRankAvg( src, (*toMake) ); SI32 maxrank = toMake->maxRank; Skills->ApplyRank( sock, targItem, static_cast(rank), static_cast(maxrank) ); // if we're not a GM, see if we should store our creator if( !src->IsGM() && !toMake->skillReqs.empty() ) { targItem->SetCreator( src->GetSerial() ); SI32 avgSkill, sumSkill = 0; // Find the average of our player's skills for( size_t resCounter = 0; resCounter < toMake->skillReqs.size(); ++resCounter ) sumSkill += src->GetSkill( toMake->skillReqs[resCounter].skillNumber ); avgSkill = static_cast(sumSkill / toMake->skillReqs.size()); if( avgSkill > 950 ) targItem->SetMadeWith( toMake->skillReqs[0].skillNumber + 1 ); else targItem->SetMadeWith( -(toMake->skillReqs[0].skillNumber + 1) ); } else { targItem->SetCreator( INVALIDSERIAL ); targItem->SetMadeWith( 0 ); } targItem->EntryMadeFrom( iMaking ); } // Make sure it's movable targItem->SetMovable( 1 ); if( toMake->soundPlayed ) PlaySound( sock, toMake->soundPlayed, true ); if( FindItemOwner( targItem ) == src ) sock->sysmessage( 985 ); // You create the item and place it in your backpack. // Trigger onMakeItem() JS event for character who crafted the item std::vector scriptTriggers = src->GetScriptTriggers(); for( auto scriptTrig : scriptTriggers ) { cScript *toExecute = JSMapping->GetScript( scriptTrig ); if( toExecute != nullptr ) { toExecute->OnMakeItem( sock, src, targItem, iMaking ); } } } //o-----------------------------------------------------------------------------------------------o //| Function - void checktempeffects( void ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Checks timer-controlled temporary effects //o-----------------------------------------------------------------------------------------------o void cEffects::checktempeffects( void ) { CItem *i = nullptr; CChar *s = nullptr, *src = nullptr; CSocket *tSock = nullptr; CBaseObject *myObj = nullptr; const UI32 j = cwmWorldState->GetUICurrentTime(); cwmWorldState->tempEffects.Push(); for( CTEffect *Effect = cwmWorldState->tempEffects.First(); !cwmWorldState->tempEffects.Finished(); Effect = cwmWorldState->tempEffects.Next() ) { if( Effect == nullptr ) { cwmWorldState->tempEffects.Remove( Effect ); continue; } else if( Effect->Destination() == INVALIDSERIAL ) { cwmWorldState->tempEffects.Remove( Effect, true ); continue; } if( Effect->ExpireTime() > j ) continue; if( Effect->Destination() < BASEITEMSERIAL ) { s = calcCharObjFromSer( Effect->Destination() ); if( !ValidateObject( s ) ) { cwmWorldState->tempEffects.Remove( Effect, true ); continue; } tSock = s->GetSocket(); } bool equipCheckNeeded = false; bool validChar = ValidateObject( s ); switch( Effect->Number() ) { case 1: // Paralysis / Paralysis Field Spells if( validChar && s->IsFrozen() ) { s->SetFrozen( false ); if( tSock != nullptr ) tSock->sysmessage( 700 ); } break; case 2: // Nightsight Potion (JS) and Spell (code) if( validChar ) { s->SetFixedLight( 255 ); doLight( tSock, cwmWorldState->ServerData()->WorldLightCurrentLevel() ); } break; case 3: // Clumsy spell (JS) if( validChar ) { s->IncDexterity2( Effect->More1() ); equipCheckNeeded = true; } break; case 4: // Feeblemind spell (JS) if( validChar ) { s->IncIntelligence2( Effect->More1() ); equipCheckNeeded = true; } break; case 5: // Weaken Spell if( validChar ) { s->IncStrength2( Effect->More1() ); equipCheckNeeded = true; } break; case 6: // Agility Potion (JS) and Spell (code) if( validChar ) { s->IncDexterity2( -Effect->More1() ); s->SetStamina( std::min(s->GetStamina(), s->GetMaxStam()) ); equipCheckNeeded = true; } break; case 7: // Cunning Spell if( validChar ) { s->IncIntelligence2( -Effect->More1() ); s->SetMana( std::min(s->GetMana(), s->GetMaxMana()) ); equipCheckNeeded = true; } break; case 8: // Strength Potion (JS) and Spell (code) if( validChar ) { s->IncStrength2( -Effect->More1() ); s->SetHP( std::min(s->GetHP(), static_cast(s->GetMaxHP())) ); equipCheckNeeded = true; } break; case 9: // Grind potion (also used for necro stuff) if( validChar ) { switch( Effect->More1() ) { case 0: if( Effect->More2() != 0 ) s->TextMessage( nullptr, 1270, EMOTE, 1, s->GetName().c_str() ); PlaySound( s, 0x0242 ); break; } } break; case 11: // Bless Spell if( validChar ) { s->IncStrength2( -Effect->More1() ); s->SetHP( std::min(s->GetHP(), static_cast(s->GetMaxHP())) ); s->IncDexterity2( -Effect->More2() ); s->SetStamina( std::min(s->GetStamina(), s->GetMaxStam()) ); s->IncIntelligence2( -Effect->More3() ); s->SetMana( std::min(s->GetMana(), s->GetMaxMana()) ); equipCheckNeeded = true; } break; case 12: // Curse Spell if( validChar ) { s->IncStrength2( Effect->More1() ); s->IncDexterity2( Effect->More2() ); s->IncIntelligence2( Effect->More3() ); equipCheckNeeded = true; } else equipCheckNeeded = false; break; case 15: // Reactive Armor Spell if( validChar ) { s->SetReactiveArmour( false ); } break; case 16: // Explosion potion messages (JS) src = calcCharObjFromSer( Effect->Source() ); if( src->GetTimer( tCHAR_ANTISPAM ) < cwmWorldState->GetUICurrentTime() ) { src->SetTimer( tCHAR_ANTISPAM, BuildTimeValue( 1 ) ); std::string mTemp = oldstrutil::number( Effect->More3() ); if( tSock != nullptr ) { tSock->sysmessage( mTemp.c_str() ); } } break; case 17: // Explosion effect (JS and code) src = calcCharObjFromSer( Effect->Source() ); explodeItem( src->GetSocket(), static_cast(Effect->ObjPtr()) ); //explode this item break; case 18: // Polymorph Spell if( validChar ) { s->SetID( s->GetOrgID() ); s->IsPolymorphed( false ); } break; case 19: // Incognito Spell if( validChar ) { s->SetID( s->GetOrgID() ); // ------ NAME ----- s->SetName( s->GetOrgName() ); i = s->GetItemAtLayer( IL_HAIR ); if( ValidateObject( i ) ) { i->SetColour( s->GetHairColour() ); i->SetID( s->GetHairStyle() ); } i = s->GetItemAtLayer( IL_FACIALHAIR ); if( ValidateObject( i ) && s->GetID( 2 ) == 0x90 ) { i->SetColour( s->GetBeardColour() ); i->SetID( s->GetBeardStyle() ); } if( tSock != nullptr ) s->SendWornItems( tSock ); s->IsIncognito( false ); } break; case 21: // Protection Spell if( validChar ) { UI16 toDrop; toDrop = Effect->More1(); if( ( s->GetBaseSkill( PARRYING ) - toDrop ) < 0 ) s->SetBaseSkill( 0, PARRYING ); else s->SetBaseSkill( s->GetBaseSkill( PARRYING ) - toDrop, PARRYING ); equipCheckNeeded = true; } break; case 25: // Temporarily set item as disabled Effect->ObjPtr()->SetDisabled( false ); break; case 26: // Disallow immediately using another potion (JS) if( validChar ) { s->SetUsingPotion( false ); } break; case 27: // Explosion Spell src = calcCharObjFromSer( Effect->Source() ); if( !ValidateObject( src ) || !ValidateObject( s ) ) break; Magic->playSound( src, 43 ); Magic->doMoveEffect( 43, s, src ); Magic->doStaticEffect( s, 43 ); Magic->MagicDamage( s, Effect->More1(), src, HEAT ); equipCheckNeeded = true; break; case 28: // Magic Arrow Spell src = calcCharObjFromSer( Effect->Source() ); if( !ValidateObject( src ) || !ValidateObject( s ) ) break; Magic->playSound( src, 5 ); Magic->doMoveEffect( 5, s, src ); Magic->doStaticEffect( s, 5 ); Magic->MagicDamage( s, Effect->More1(), src, HEAT ); equipCheckNeeded = true; break; case 29: // Harm Spell src = calcCharObjFromSer( Effect->Source() ); if( !ValidateObject( src ) || !ValidateObject( s ) ) break; Magic->playSound( src, 12 ); Magic->doMoveEffect( 12, s, src ); Magic->doStaticEffect( s, 12 ); Magic->MagicDamage( s, Effect->More1(), src, COLD ); equipCheckNeeded = true; break; case 30: // Fireball Spell src = calcCharObjFromSer( Effect->Source() ); if( !ValidateObject( src ) || !ValidateObject( s ) ) break; Magic->playSound( src, 18 ); Magic->doMoveEffect( 18, s, src ); Magic->doStaticEffect( s, 18 ); Magic->MagicDamage( s, Effect->More1(), src, HEAT ); equipCheckNeeded = true; break; case 31: // Lightning Spell src = calcCharObjFromSer( Effect->Source() ); if( !ValidateObject( src ) || !ValidateObject( s ) ) break; Effects->bolteffect( s ); Magic->playSound( src, 30 ); Magic->doMoveEffect( 30, s, src ); Magic->doStaticEffect( s, 30 ); Magic->MagicDamage( s, Effect->More1(), src, LIGHTNING ); equipCheckNeeded = true; break; case 32: // Mind Blast src = calcCharObjFromSer( Effect->Source() ); if( !ValidateObject( src ) || !ValidateObject( s ) ) break; Magic->playSound( src, 37 ); Magic->doMoveEffect( 37, s, src ); Magic->doStaticEffect( s, 37 ); Magic->MagicDamage( s, Effect->More1(), src, COLD ); equipCheckNeeded = true; break; case 33: // Energy Bolt src = calcCharObjFromSer( Effect->Source() ); if( !ValidateObject( src ) || !ValidateObject( s ) ) break; Magic->playSound( s, 42 ); Magic->doMoveEffect( 42, s, src ); Magic->doStaticEffect( s, 42 ); Magic->MagicDamage( s, Effect->More1(), src, LIGHTNING ); equipCheckNeeded = true; break; case 34: // Chain Lightning src = calcCharObjFromSer( Effect->Source() ); if( !ValidateObject( src ) || !ValidateObject( s ) ) break; Effects->bolteffect( s ); Magic->playSound( s, 49 ); Magic->doMoveEffect( 49, s, src ); Magic->doStaticEffect( s, 49 ); Magic->MagicDamage( s, Effect->More1(), src, LIGHTNING ); equipCheckNeeded = true; break; case 35: // Flame Strike src = calcCharObjFromSer( Effect->Source() ); if( !ValidateObject( src ) || !ValidateObject( s ) ) break; Magic->playSound( s, 51 ); Magic->doMoveEffect( 51, s, src ); Magic->doStaticEffect( s, 51 ); Magic->MagicDamage( s, Effect->More1(), src, HEAT ); equipCheckNeeded = true; break; case 36: // Meteor Swarm src = calcCharObjFromSer( Effect->Source() ); if( !ValidateObject( src ) || !ValidateObject( s ) ) break; Magic->playSound( src, 55 ); if( s != src ) Magic->doMoveEffect( 55, s, src ); Magic->doStaticEffect( s, 55 ); //Effects->PlaySound( target, 0x160 ); //Effects->PlayMovingAnimation( caster, target, 0x36D5, 0x07, 0x00, 0x01 ); Magic->MagicDamage( s, Effect->More1(), src, HEAT ); equipCheckNeeded = true; break; case 40: // Used by JS timers { // Default/Global script ID UI16 scpNum = 0xFFFF; // Get script associated with effect, if any cScript *tScript = JSMapping->GetScript( Effect->AssocScript() ); // items have serials of 0x40000000 and above, and we already know it's not INVALIDSERIAL if( Effect->Source() >= BASEITEMSERIAL ) { myObj = calcItemObjFromSer( Effect->Source() ); equipCheckNeeded = false; } else { myObj = calcCharObjFromSer( Effect->Source() ); equipCheckNeeded = true; } // Check that object effect was running on still exists if( !ValidateObject( myObj ) ) break; // No associated script, so it must be another callback variety if( tScript == nullptr ) { if( Effect->More2() != 0xFFFF ) // A scriptID other than default one was found { scpNum = Effect->More2(); tScript = JSMapping->GetScript( scpNum ); } /*else { // No specific script was associated with effect, so let's see if there's a script associated with object instead scpNum = myObj->GetScriptTrigger(); } tScript = JSMapping->GetScript( scpNum );*/ } // Make sure to check for a specific script via envoke system when the previous checks ended in the global script. if( ( tScript == nullptr || scpNum == 0) && Effect->Source() >= BASEITEMSERIAL ) { if( JSMapping->GetEnvokeByType()->Check( static_cast((static_cast(myObj))->GetType()) ) ) { scpNum = JSMapping->GetEnvokeByType()->GetScript( static_cast((static_cast(myObj))->GetType()) ); tScript = JSMapping->GetScript( scpNum ); } else if( JSMapping->GetEnvokeByID()->Check( myObj->GetID() ) ) { scpNum = JSMapping->GetEnvokeByID()->GetScript( myObj->GetID() ); tScript = JSMapping->GetScript( scpNum ); } } // Callback to onTimer event in script if( tScript != nullptr ) { tScript->OnTimer( myObj, static_cast(Effect->More1()) ); } break; } case 41: // Start of item crafting HandleMakeItemEffect( Effect ); break; case 42: // End of item crafting src = calcCharObjFromSer( Effect->Source() ); PlaySound( src, Effect->More2() ); break; case 43: // Turn a wooly sheep into a sheared sheep src = calcCharObjFromSer( Effect->Source() ); if( !ValidateObject( src ) ) // char doesn't exist! break; else if( src->GetID() == 0xCF ) break; src->SetID( 0xCF ); // Thats all we need to do break; default: Console.error( oldstrutil::format(" Fallout of switch statement without default (%i). checktempeffects()", Effect->Number()) ); break; } if( validChar && equipCheckNeeded ) Items->CheckEquipment( s ); // checks equipments for stat requirements cwmWorldState->tempEffects.Remove( Effect, true ); } cwmWorldState->tempEffects.Pop(); } //o-----------------------------------------------------------------------------------------------o //| Function - void reverseEffect( CTEffect *Effect ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Reverse a temp effect //o-----------------------------------------------------------------------------------------------o void reverseEffect( CTEffect *Effect ) { CChar *s = calcCharObjFromSer( Effect->Destination() ); if( ValidateObject( s ) ) { switch( Effect->Number() ) { case 1: // Paralysis Spell s->SetFrozen( false ); break; case 2: // Nightsight Potion (JS) and Spell (code) s->SetFixedLight( 255 ); break; case 3: s->IncDexterity2( Effect->More1() ); break; // Clumsy Spell (JS) case 4: s->IncIntelligence2( Effect->More1() ); break; // Feeblemind Spell (JS) case 5: s->IncStrength2( Effect->More1() ); break; // Weaken Spell case 6: s->IncDexterity2( -Effect->More1() ); break; // Agility Potion (JS) and Spell (code) case 7: s->IncIntelligence2( -Effect->More1() ); break; // Cunning Spell case 8: s->IncStrength2( -Effect->More1() ); break; // Strength Potion (JS) and Spell (code) case 11: // Bless Spell s->IncStrength2( -Effect->More1() ); s->IncDexterity2( -Effect->More2() ); s->IncIntelligence2( -Effect->More3() ); break; case 12: // Curse Spell s->IncStrength2( Effect->More1() ); s->IncDexterity2( Effect->More2() ); s->IncIntelligence2( Effect->More3() ); break; case 18: // Polymorph Spell s->SetID( s->GetOrgID() ); s->IsPolymorphed( false ); break; case 19: // Incognito Spell CItem *j; // ------ SEX ------ s->SetID( s->GetOrgID() ); s->SetName( s->GetOrgName() ); j = s->GetItemAtLayer( IL_HAIR ); if( ValidateObject( j ) ) { j->SetColour( s->GetHairColour() ); j->SetID( s->GetHairStyle() ); } j = s->GetItemAtLayer( IL_FACIALHAIR ); if( ValidateObject( j ) && s->GetID( 2 ) == 0x90 ) { j->SetColour( s->GetBeardColour() ); j->SetID( s->GetBeardStyle() ); } // only refresh once CSocket *tSock; tSock = s->GetSocket(); s->SendWornItems( tSock ); s->IsIncognito( false ); break; case 21: // Protection Spell SI32 toDrop; toDrop = Effect->More1() ; if( ( s->GetBaseSkill( PARRYING ) - toDrop ) < 0 ) s->SetBaseSkill( 0, PARRYING ); else s->SetBaseSkill( s->GetBaseSkill( PARRYING ) - toDrop, PARRYING ); break; case 26: // Re-enable the usage of potions s->SetUsingPotion( false ); break; default: Console.error( " Fallout of switch statement without default. uox3.cpp, reverseEffect()"); break; } } Items->CheckEquipment( s ); } //o-----------------------------------------------------------------------------------------------o //| Function - void tempeffect( CChar *source, CChar *dest, UI08 num, UI16 more1, UI16 more2, UI16 more3, CItem *targItemPtr ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Adds a temp effect to a character //o-----------------------------------------------------------------------------------------------o void cEffects::tempeffect( CChar *source, CChar *dest, UI08 num, UI16 more1, UI16 more2, UI16 more3, CItem *targItemPtr ) { //if( !ValidateObject( source ) || !ValidateObject( dest ) ) if( !ValidateObject( dest ) ) return; bool spellResisted = false; CTEffect *toAdd = new CTEffect; SERIAL sourceSerial = 0; if( source != nullptr ) sourceSerial = source->GetSerial(); SERIAL targSer = dest->GetSerial(); toAdd->Source( sourceSerial ); toAdd->Destination( targSer ); cwmWorldState->tempEffects.Push(); for( CTEffect *Effect = cwmWorldState->tempEffects.First(); !cwmWorldState->tempEffects.Finished(); Effect = cwmWorldState->tempEffects.Next() ) { if( Effect->Destination() == targSer ) { if( Effect->Number() == num ) { switch( Effect->Number() ) { case 3: // Clumsy Spell (JS) case 4: // Feeblemind Spell (JS) case 5: // Weaken Spell case 6: // Agility Potion (JS) and Spell (code) case 7: // Cunning Spell case 8: // Strength Potion (JS) and Spell (code) case 11: // Bless Spell case 12: // Curse Spell case 18: // Polymorph Spell case 19: // Incognito Spell case 21: // Protection Spell reverseEffect( Effect ); cwmWorldState->tempEffects.Remove( Effect, true ); break; default: break; } } } } cwmWorldState->tempEffects.Pop(); CSocket *tSock = dest->GetSocket(); toAdd->Number( num ); switch( num ) { case 1: // Paralyse Spell { dest->SetFrozen( true ); R32 effectDuration = 0; if( source == nullptr ) { effectDuration = static_cast(more2); } else { effectDuration = static_cast(source->GetSkill( MAGERY )) / 100.0f; } toAdd->ExpireTime( BuildTimeValue( effectDuration )); toAdd->Dispellable( true ); break; } case 2: // Nightsight Potion (JS) and Spell (code) { SI16 worldbrightlevel; worldbrightlevel = cwmWorldState->ServerData()->WorldLightBrightLevel(); dest->SetFixedLight( static_cast(worldbrightlevel) ); doLight( tSock, static_cast(worldbrightlevel) ); R32 effectDuration = 0; if( source == nullptr ) { effectDuration = static_cast(more2); } else { effectDuration = static_cast(source->GetSkill( MAGERY )) / 2.0f; } toAdd->ExpireTime( BuildTimeValue( effectDuration )); toAdd->Dispellable( true ); break; } case 3: // Clumsy Spell (JS) { if( dest->GetDexterity() < more1 ) more1 = dest->GetDexterity(); dest->IncDexterity2( -more1 ); dest->SetStamina( std::min( dest->GetStamina(), dest->GetMaxStam() ) ); R32 effectDuration = 0; if( source == nullptr ) { // Use duration provided with effect call effectDuration = static_cast(more2); // Halve effect-duration on resist spellResisted = Magic->CheckResist( more3, dest, 1 ); } else { effectDuration = static_cast(source->GetSkill( MAGERY ) / 10.0f); //Halve effect-timer on resist spellResisted = Magic->CheckResist( source, dest, 1 ); } if( spellResisted ) toAdd->ExpireTime( BuildTimeValue( effectDuration / 2.0f ) ); else toAdd->ExpireTime( BuildTimeValue( effectDuration )); toAdd->More1( more1 ); toAdd->Dispellable( true ); break; } case 4: // Feeblemind Spell (JS) { if( dest->GetIntelligence() < more1 ) more1 = dest->GetIntelligence(); dest->IncIntelligence2( -more1 ); dest->SetMana( std::min(dest->GetMana(), dest->GetMaxMana() )); R32 effectDuration = 0; if( source == nullptr ) { // Use duration provided with effect call effectDuration = static_cast(more2); // Halve effect-duration on resist spellResisted = Magic->CheckResist( more3, dest, 3 ); } else { effectDuration = static_cast(source->GetSkill( MAGERY ) / 10.0f); //Halve effect-timer on resist spellResisted = Magic->CheckResist( source, dest, 1 ); } if( spellResisted ) toAdd->ExpireTime( BuildTimeValue( effectDuration / 2.0f )); else toAdd->ExpireTime( BuildTimeValue( effectDuration )); toAdd->More1( more1 ); toAdd->Dispellable( true ); break; } case 5: // Weaken Spell { if( dest->GetStrength() < more1 ) more1 = dest->GetStrength(); dest->IncStrength2( -more1 ); dest->SetHP( std::min( dest->GetHP(), static_cast(dest->GetMaxHP()) ) ); R32 effectDuration = 0; if( source == nullptr ) { // Use duration provided with effect call effectDuration = static_cast(more2); // Halve effect-duration on resist spellResisted = Magic->CheckResist( more3, dest, 1 ); } else { effectDuration = static_cast(source->GetSkill( MAGERY ) / 10.0f); //Halve effect-timer on resist spellResisted = Magic->CheckResist( source, dest, 1 ); } //Halve effect-timer on resist if( spellResisted ) toAdd->ExpireTime( BuildTimeValue( effectDuration / 2.0f )); else toAdd->ExpireTime( BuildTimeValue( effectDuration )); toAdd->More1( more1 ); toAdd->Dispellable( true ); break; } case 6: // Agility Potion (JS) and Spell (code) { dest->IncDexterity( more1 ); if( source == nullptr ) { // Use duration provided with effect call toAdd->ExpireTime( BuildTimeValue( static_cast(more2) )); } else { toAdd->ExpireTime( BuildTimeValue( (R32)source->GetSkill( MAGERY ) / 10.0f ) ); } toAdd->More1( more1 ); toAdd->Dispellable( true ); break; } case 7: // Cunning Spell dest->IncIntelligence2( more1 ); if( source == nullptr ) { // Use duration provided with effect call toAdd->ExpireTime( BuildTimeValue( static_cast(more2) )); } else { toAdd->ExpireTime( BuildTimeValue( (R32)source->GetSkill( MAGERY ) / 10.0f ) ); } toAdd->More1( more1 ); toAdd->Dispellable( true ); break; case 8: // Strength Potion (JS) and Spell (code) dest->IncStrength2( more1 ); if( source == nullptr ) { // Use duration provided with effect call toAdd->ExpireTime( BuildTimeValue( static_cast(more2) )); } else { toAdd->ExpireTime( BuildTimeValue( (R32)source->GetSkill( MAGERY ) / 10.0f ) ); } toAdd->More1( more1 ); toAdd->Dispellable( true ); break; case 9: // Grind Necro Reagent toAdd->ExpireTime( BuildTimeValue( (R32)more2 ) ); toAdd->More1( more1 ); toAdd->More2( more2 ); toAdd->Dispellable( false ); break; case 10: // ??? toAdd->ExpireTime( BuildTimeValue( 12.0f ) ); toAdd->Dispellable( false ); toAdd->More1( more1 ); toAdd->More2( more2 ); break; case 11: // Bless Spell if( source != nullptr ) { // No source! Effect gotten from equipping an item? // Apply same bonus to all stats dest->IncStrength2( more1 ); dest->IncDexterity2( more1 ); dest->IncIntelligence2( more1 ); toAdd->ExpireTime( BuildTimeValue( more2 )); toAdd->More1( more1 ); toAdd->More2( more1 ); toAdd->More3( more1 ); } else { // Effect gotten from a spell cast by another character dest->IncStrength2( more1 ); dest->IncDexterity2( more2 ); dest->IncIntelligence2( more3 ); toAdd->ExpireTime( BuildTimeValue( (R32)source->GetSkill( MAGERY ) / 10.0f ) ); toAdd->More1( more1 ); toAdd->More2( more2 ); toAdd->More3( more3 ); } toAdd->Dispellable( true ); break; case 12: // Curse Spell { bool spellResisted = false; R32 effectDuration = 0; if( source == nullptr ) { auto effectStrength = more1; if( dest->GetStrength() < more1 ) effectStrength = dest->GetStrength(); dest->IncStrength2( -effectStrength ); if( dest->GetDexterity() < more1 ) effectStrength = dest->GetDexterity(); dest->IncDexterity2( -effectStrength ); if( dest->GetIntelligence() < more1 ) effectStrength = dest->GetIntelligence(); dest->IncIntelligence2( -effectStrength ); toAdd->More1( more1 ); toAdd->More2( more1 ); toAdd->More3( more1 ); // Use duration provided with effect call effectDuration = static_cast(more2); // Halve effect-duration on resist spellResisted = Magic->CheckResist( more3, dest, 4 ); } else { if( dest->GetStrength() < more1 ) more1 = dest->GetStrength(); if( dest->GetDexterity() < more2 ) more2 = dest->GetDexterity(); if( dest->GetIntelligence() < more3 ) more3 = dest->GetIntelligence(); dest->IncStrength2( -more1 ); dest->IncDexterity2( -more2 ); dest->IncIntelligence2( -more3 ); toAdd->More1( more1 ); toAdd->More2( more2 ); toAdd->More3( more3 ); effectDuration = static_cast(source->GetSkill( MAGERY )) / 10.0f; //Halve effect-timer on resist spellResisted = Magic->CheckResist( source, dest, 4 ); } if( spellResisted ) toAdd->ExpireTime( BuildTimeValue( effectDuration / 2.0f )); else toAdd->ExpireTime( BuildTimeValue( effectDuration )); toAdd->Dispellable( true ); break; } case 15: // Reactive Armor Spell if( source != nullptr ) toAdd->ExpireTime( BuildTimeValue( (R32)source->GetSkill( MAGERY ) / 10.0f ) ); else toAdd->ExpireTime( BuildTimeValue( 6.0f )); toAdd->Dispellable( true ); break; case 16: // Explosion potion messages (JS) toAdd->ExpireTime( BuildTimeValue( (R32)more2 ) ); toAdd->More1( more1 ); //item/potion toAdd->More2( more2 ); //seconds toAdd->More3( more3 ); //countdown# toAdd->Dispellable( false ); break; case 18: // Polymorph Spell toAdd->ExpireTime( cwmWorldState->ServerData()->BuildSystemTimeValue( tSERVER_POLYMORPH ) ); toAdd->Dispellable( false ); UI16 k; // Grey flag when polymorphed dest->SetTimer( tCHAR_CRIMFLAG, cwmWorldState->ServerData()->BuildSystemTimeValue( tSERVER_POLYMORPH ) ); if( dest->IsOnHorse() ) DismountCreature( dest ); else if( dest->IsFlying() ) dest->ToggleFlying(); k = ( more1<<8 ) + more2; if( k <= 0x03e2 ) // body-values >0x3e1 crash the client dest->SetID( k ); break; case 19: // Incognito Spell toAdd->ExpireTime( BuildTimeValue( 90.0f ) ); // 90 seconds toAdd->Dispellable( false ); break; case 21: // Protection Spell { R32 effectDuration = 0; if( source == nullptr ) { effectDuration = static_cast(more2); } else { effectDuration = 120.0f; } toAdd->ExpireTime( BuildTimeValue( effectDuration )); toAdd->Dispellable( true ); toAdd->More1( more1 ); dest->SetBaseSkill( dest->GetBaseSkill( PARRYING ) + more1, PARRYING ); break; } case 25: // Temporarily set item as disabled toAdd->ExpireTime( BuildTimeValue( (R32)more1 ) ); toAdd->ObjPtr( dest ); dest->SetDisabled( true ); toAdd->More2( 1 ); break; case 26: // Temporarily disable the usage of potions toAdd->ExpireTime( cwmWorldState->ServerData()->BuildSystemTimeValue( tSERVER_POTION ) ); dest->SetUsingPotion( true ); break; case 27: // Explosion Spell toAdd->ExpireTime( BuildTimeValue( static_cast(Magic->spells[43].DamageDelay() ))); toAdd->More1( more1 ); break; case 28: // Magic Arrow Spell toAdd->ExpireTime( BuildTimeValue( static_cast(Magic->spells[5].DamageDelay() ))); toAdd->More1( more1 ); break; case 29: // Harm Spell toAdd->ExpireTime( BuildTimeValue( static_cast(Magic->spells[12].DamageDelay() ))); toAdd->More1( more1 ); break; case 30: // Fireball Spell toAdd->ExpireTime( BuildTimeValue( static_cast(Magic->spells[18].DamageDelay() ))); toAdd->More1( more1 ); break; case 31: // Lightning Spell toAdd->ExpireTime( BuildTimeValue( static_cast(Magic->spells[30].DamageDelay() ))); toAdd->More1( more1 ); break; case 32: // Mind Blast Spell toAdd->ExpireTime( BuildTimeValue( static_cast(Magic->spells[37].DamageDelay() ))); toAdd->More1( more1 ); break; case 33: // Energy Bolt Spell toAdd->ExpireTime( BuildTimeValue( static_cast(Magic->spells[42].DamageDelay() ))); toAdd->More1( more1 ); break; case 34: // Chain Lightning Spell toAdd->ExpireTime( BuildTimeValue( static_cast(Magic->spells[49].DamageDelay() ))); toAdd->More1( more1 ); break; case 35: // Flamestrike Spell toAdd->ExpireTime( BuildTimeValue( static_cast(Magic->spells[51].DamageDelay() ))); toAdd->More1( more1 ); break; case 36: // Meteor Swarm toAdd->ExpireTime( BuildTimeValue( static_cast(Magic->spells[55].DamageDelay() ))); toAdd->More1( more1 ); break; case 40: // Used by JS timers toAdd->ExpireTime( BuildTimeValue( ( (R32)more1 + (R32)more2 / 1000.0f ) ) ); toAdd->More1( more3 ); break; case 41: // creating item toAdd->ExpireTime( BuildTimeValue( (R32)(more1) / 100.0f ) ); toAdd->More2( more2 ); break; case 42: // delayed sound effect for crafting toAdd->ExpireTime( BuildTimeValue( (R32)(more1) / 100.0f ) ); toAdd->More2( more2 ); break; case 43: // regain wool for sheeps (JS) toAdd->ExpireTime( BuildTimeValue( (R32)(more1) ) ); break; default: Console.error( oldstrutil::format(" Fallout of switch statement (%d) without default. uox3.cpp, tempeffect()", num )); delete toAdd; return; } cwmWorldState->tempEffects.Add( toAdd ); } //o-----------------------------------------------------------------------------------------------o //| Function - void tempeffect( CChar *source, CItem *dest, UI08 num, UI16 more1, UI16 more2, UI16 more3 ) //o-----------------------------------------------------------------------------------------------o //| Purpose - Adds a temp effect to an item //o-----------------------------------------------------------------------------------------------o void cEffects::tempeffect( CChar *source, CItem *dest, UI08 num, UI16 more1, UI16 more2, UI16 more3 ) { if( !ValidateObject( dest ) ) return; CTEffect *toAdd = new CTEffect; if( ValidateObject( source ) ) toAdd->Source( source->GetSerial() ); else toAdd->Source( INVALIDSERIAL ); toAdd->Destination( dest->GetSerial() ); toAdd->Number( num ); switch( num ) { case 10: // Crafting Potion?? toAdd->ExpireTime( BuildTimeValue( 12 ) ); toAdd->Dispellable( false ); toAdd->More1( more1 ); toAdd->More2( more2 ); source->SkillUsed( true, ALCHEMY ); break; case 13: // Timer for door (gangplank on boats?) to autoclose? if( !dest->isDoorOpen() ) dest->SetDoorOpen( true ); toAdd->ExpireTime( BuildTimeValue( 10 ) ); toAdd->Dispellable( false ); break; case 17: // Explosion potion (explosion) Tauriel (explode in 4 seconds) toAdd->ExpireTime( BuildTimeValue( 4 ) ); toAdd->More1( more1 ); toAdd->More2( more2 ); toAdd->ObjPtr( dest ); toAdd->Dispellable( false ); break; case 25: // // Temporarily set item as disabled toAdd->ExpireTime( BuildTimeValue( more1 ) ); toAdd->ObjPtr( dest ); dest->SetDisabled( true ); toAdd->More2( 0 ); break; default: Console.error( " Fallout of switch statement without default. uox3.cpp, tempeffect2()"); delete toAdd; return; } cwmWorldState->tempEffects.Add( toAdd ); } //o-----------------------------------------------------------------------------------------------o //| Function - void SaveEffects( void ) //| Date - 16 March, 2002 //o-----------------------------------------------------------------------------------------------o //| Purpose - Saves out the TempEffects to effects.wsc //o-----------------------------------------------------------------------------------------------o void cEffects::SaveEffects( void ) { std::ofstream effectDestination; const char blockDiscriminator[] = "\n\n---EFFECT---\n\n"; SI32 s_t = getclock(); Console << "Saving Effects... "; Console.TurnYellow(); std::string filename = cwmWorldState->ServerData()->Directory( CSDDP_SHARED ) + "effects.wsc"; effectDestination.open( filename.c_str() ); if( !effectDestination ) { Console.error( oldstrutil::format("Failed to open %s for writing", filename.c_str()) ); return; } cwmWorldState->tempEffects.Push(); for( CTEffect *currEffect = cwmWorldState->tempEffects.First(); !cwmWorldState->tempEffects.Finished(); currEffect = cwmWorldState->tempEffects.Next() ) { if( currEffect == nullptr ) continue; currEffect->Save( effectDestination ); effectDestination << blockDiscriminator; } effectDestination.close(); cwmWorldState->tempEffects.Pop(); Console << "\b\b\b\b"; Console.PrintDone(); SI32 e_t = getclock(); Console.print( oldstrutil::format("Effects saved in %.02fsec\n", ((R32)(e_t-s_t))/1000.0f) ); } void ReadWorldTagData( std::ifstream &inStream, std::string &tag, std::string &data ); //o-----------------------------------------------------------------------------------------------o //| Function - void LoadEffects( void ) //| Date - 16 March, 2002 //o-----------------------------------------------------------------------------------------------o //| Purpose - Loads in Effects from disk //o-----------------------------------------------------------------------------------------------o void cEffects::LoadEffects( void ) { std::ifstream input; std::string filename = cwmWorldState->ServerData()->Directory( CSDDP_SHARED ) + "effects.wsc"; input.open( filename.c_str(), std::ios_base::in ); input.seekg( 0, std::ios::beg ); std::string tag; std::string data; std::string UTag; if( input.is_open() ) { CTEffect *toLoad; char line[1024]; while( !input.eof() && !input.fail() ) { input.getline(line, 1023); line[input.gcount()] = 0; std::string sLine(line); sLine = oldstrutil::trim( oldstrutil::removeTrailing( sLine, "//" )); auto usLine = oldstrutil::upper( sLine ); if( !sLine.empty() ) { if( usLine == "[EFFECT]" ) { toLoad = new CTEffect; while( tag != "o---o" ) { ReadWorldTagData( input, tag, data ); if( tag != "o---o" ) { UTag = oldstrutil::upper( tag ); switch( (UTag.data()[0]) ) { case 'A': if( UTag == "ASSOCSCRIPT" ) toLoad->AssocScript( static_cast(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0)) ); break; case 'D': if( UTag == "DEST" ) toLoad->Destination( static_cast(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0)) ); if( UTag == "DISPEL" ) toLoad->Dispellable((( static_cast(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0)) == 0 ) ? false : true )); break; case 'E': if( UTag == "EXPIRE" ) toLoad->ExpireTime( static_cast(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0)) + cwmWorldState->GetUICurrentTime() ); break; case 'I': if( UTag == "ITEMPTR" ) { SERIAL objSer = static_cast(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0)); if( objSer != INVALIDSERIAL ) { if( objSer < BASEITEMSERIAL ) toLoad->ObjPtr( calcCharObjFromSer( objSer ) ); else toLoad->ObjPtr( calcItemObjFromSer( objSer ) ); } else toLoad->ObjPtr( nullptr ); } break; case 'M': if( UTag == "MORE1" ) toLoad->More1( static_cast(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )),nullptr, 0)) ); if( UTag == "MORE2" ) toLoad->More2( static_cast(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )),nullptr, 0)) ); if( UTag == "MORE3" ) toLoad->More3( static_cast(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )),nullptr, 0)) ); break; case 'N': if( UTag == "NUMBER" ) toLoad->Number( static_cast(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0))); break; case 'O': if( UTag == "OBJPTR" ) { SERIAL objSer = static_cast(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0)); if( objSer != INVALIDSERIAL ) { if( objSer < BASEITEMSERIAL ) toLoad->ObjPtr( calcCharObjFromSer( objSer ) ); else toLoad->ObjPtr( calcItemObjFromSer( objSer ) ); } else toLoad->ObjPtr( nullptr ); } break; case 'S': if( UTag == "SOURCE" ) toLoad->Source( static_cast(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), nullptr, 0)) ); break; default: Console.error( oldstrutil::format("Unknown effects tag %s with contents of %s", tag.c_str(), data.c_str()) ); break; } } } cwmWorldState->tempEffects.Add( toLoad ); tag = ""; } } } input.close(); } } //o-----------------------------------------------------------------------------------------------o //| Function - bool Save( std::ofstream &effectDestination ) const //| Date - March 16, 2002 //o-----------------------------------------------------------------------------------------------o //| Purpose - Output the effect data to the effect destination. Returns true/false //| indicating the success of the write operation //o-----------------------------------------------------------------------------------------------o bool CTEffect::Save( std::ofstream &effectDestination ) const { CBaseObject *getPtr = nullptr; effectDestination << "[EFFECT]" << '\n'; // Hexadecimal Values effectDestination << std::hex; effectDestination << "Source=" << "0x" << Source() << '\n'; effectDestination << "Dest=" << "0x" << Destination() << '\n'; getPtr = ObjPtr(); effectDestination << "ObjPtr=" << "0x"; if( ValidateObject( getPtr ) ) effectDestination << getPtr->GetSerial() << '\n'; else effectDestination << INVALIDSERIAL << '\n'; // Decimal / String Values effectDestination << std::dec; effectDestination << "Expire=" << ( ExpireTime() - cwmWorldState->GetUICurrentTime() ) << '\n'; effectDestination << "Number=" << static_cast(Number()) << '\n'; effectDestination << "More1=" << More1() << '\n'; effectDestination << "More2=" << More2() << '\n'; effectDestination << "More3=" << More3() << '\n'; effectDestination << "Dispel=" << Dispellable() << '\n'; effectDestination << "AssocScript=" << AssocScript() << '\n'; effectDestination << '\n' << "o---o" << '\n' << '\n'; return true; }