polserver/pol-core/pol/spelbook.cpp
2018-10-13 00:03:40 +02:00

430 lines
12 KiB
C++

/** @file
*
* @par History
* - 2005/02/14 Shinigami: double_click - simple logical error in layer_is_equipped check
* - 2009/07/26 MuadDib: Packet struct refactoring.
* - 2009/09/17 MuadDib: Spellbook::can_add upgraded to check bitflags instead of contents.
* - 2009/09/18 MuadDib: Spellbook rewrite to deal with only bits, not scrolls inside them.
* - 2009/10/10 Turley: Added spellbook.addspell() & .removespell() methods
*/
#include "spelbook.h"
#include <sstream>
#include <stddef.h>
#include "../bscript/executor.h"
#include "../clib/cfgelem.h"
#include "../clib/logfacility.h"
#include "../clib/streamsaver.h"
#include "../plib/systemstate.h"
#include "baseobject.h"
#include "globals/uvars.h"
#include "item/itemdesc.h"
#include "mobile/charactr.h"
#include "network/client.h"
#include "network/packethelper.h"
#include "network/packets.h"
#include "polclass.h"
#include "syshookscript.h"
#include "ufunc.h"
#include "uobject.h"
namespace Pol
{
namespace Core
{
Spellbook::Spellbook( const Items::SpellbookDesc& descriptor )
: UContainer( descriptor ), spell_school( 0 )
{
if ( descriptor.spelltype == "Magic" )
spell_school = 0;
else if ( descriptor.spelltype == "Necro" )
spell_school = 1;
else if ( descriptor.spelltype == "Paladin" )
spell_school = 2;
// Use spell school 3 for Mysticism with spellid 678+ because on OSI nothing uses this so we can
// use this here.
else if ( descriptor.spelltype == "Mysticism" )
spell_school = 3;
else if ( descriptor.spelltype == "Bushido" )
spell_school = 4;
else if ( descriptor.spelltype == "Ninjitsu" )
spell_school = 5;
else if ( descriptor.spelltype == "SpellWeaving" )
spell_school = 6;
else if ( descriptor.spelltype == "BardMasteries" )
spell_school = 7;
for ( int i = 0; i < 8; ++i )
bitwise_contents[i] = 0;
}
Spellbook::~Spellbook() {}
size_t Spellbook::estimatedSize() const
{
return sizeof( u8 ) * 8 /* bitwise_contents*/
+ sizeof( u8 ) /*spell_school*/
+ base::estimatedSize();
}
void Spellbook::double_click( Network::Client* client )
{
if ( client->chr->is_equipped( this ) )
{
send_put_in_container( client, this );
send_wornitem( client, client->chr, this );
}
else if ( container != nullptr )
send_put_in_container( client, this );
else
{
send_sysmessage( client, "Spellbooks must be equipped or in the top level backpack to use." );
return;
}
if ( bitwise_contents[0] == 0 ) // assume never been clicked using the new bitwise spell scheme
calc_current_bitwise_contents();
if ( !( client->UOExpansionFlag & Network::AOS ) && ( spell_school == 0 ) )
{
send_book_old( client );
}
else if ( !( client->UOExpansionFlag & Network::AOS ) &&
( spell_school == 1 || spell_school == 2 ) )
{
send_sysmessage( client, "This item requires at least the Age of Shadows Expansion." );
return;
}
else if ( !( client->UOExpansionFlag & Network::SE ) &&
( spell_school == 4 || spell_school == 5 ) )
{
send_sysmessage( client, "This item requires at least the Samurai Empire Expansion." );
return;
}
else if ( !( client->UOExpansionFlag & Network::ML ) && spell_school == 6 )
{
send_sysmessage( client, "This item requires at least the Mondain's Legacy Expansion." );
return;
}
else if ( !( client->UOExpansionFlag & Network::SA ) &&
( spell_school == 3 || spell_school == 7 ) )
{
send_sysmessage( client, "This item requires at least the Stygian Abyss Expansion." );
return;
}
else
{
// Ok, now we do a strange check. This is for those people who have no idea that you
// must have AOS Features Enabled on an acct with AOS Expansion to view Magery book.
// All newer spellbooks will bug out if you use this method though.
if ( ( client->UOExpansionFlag & Network::AOS ) && ( spell_school == 0 ) &&
!( settingsManager.ssopt.uo_feature_enable & PKTOUT_A9::FLAG_AOS_FEATURES ) )
{
if ( Plib::systemstate.config.loglevel > 1 )
INFO_PRINT << "Client with AOS Expansion Account using spellbook without UOFeatureEnable "
"0x20 Bitflag.\n";
send_book_old( client );
return;
}
send_open_gump( client, *this );
Network::PktHelper::PacketOut<Network::PktOut_BF_Sub1B> msg;
msg->WriteFlipped<u16>( 23u );
msg->offset += 2; // sub
msg->WriteFlipped<u16>( 1u );
msg->Write<u32>( serial_ext );
msg->WriteFlipped<u16>( graphic );
// Check Mysticism spell here
if ( spell_school == 3 )
msg->WriteFlipped<u16>( 678u );
else
msg->WriteFlipped<u16>( ( spell_school * 100u ) + 1u );
msg->Write( bitwise_contents, 8 );
msg.Send( client );
}
}
bool Spellbook::has_spellid( unsigned int spellid ) const
{
u8 spellschool;
// Check Mysticism here
if ( spellid >= 678 && spellid <= 693 )
spellschool = 3;
else
spellschool = static_cast<u8>( spellid / 100 );
if ( spellschool == this->spell_school )
{
u16 spellnumber;
// Check Mysticism here
if ( spellid >= 678 && spellid <= 693 )
spellnumber = static_cast<u16>( spellid - 677 );
else
spellnumber = static_cast<u16>( spellid % 100 );
// Limits spellnumber to be between 1-64
spellnumber = spellnumber & 63;
if ( spellnumber == 0 )
spellnumber = 64;
u8 spellslot = spellnumber & 7;
if ( spellslot == 0 )
spellslot = 8;
if ( ( ( bitwise_contents[( spellnumber - 1 ) >> 3] ) & ( 1 << ( spellslot - 1 ) ) ) != 0 )
return true;
}
return false;
}
bool Spellbook::remove_spellid( unsigned int spellid )
{
if ( has_spellid( spellid ) )
{
u16 spellnumber;
// Check Mysticism here
if ( spellid >= 678 && spellid <= 693 )
spellnumber = static_cast<u16>( spellid - 677 );
else
spellnumber = static_cast<u16>( spellid % 100 );
// Limits spellnumber to be between 1-64
spellnumber = spellnumber & 63;
if ( spellnumber == 0 )
spellnumber = 64;
u8 spellslot = spellnumber & 7;
if ( spellslot == 0 )
spellslot = 8;
bitwise_contents[( spellnumber - 1 ) >> 3] &= ~( 1 << ( spellslot - 1 ) );
return true;
}
return false;
}
bool Spellbook::add_spellid( unsigned int spellid )
{
if ( !has_spellid( spellid ) )
{
u16 spellnumber;
// Check Mysticism here
if ( spellid >= 678 && spellid <= 693 )
spellnumber = static_cast<u16>( spellid - 677 );
else
spellnumber = static_cast<u16>( spellid % 100 );
// Limits spellnumber to be between 1-64
spellnumber = spellnumber & 63;
if ( spellnumber == 0 )
spellnumber = 64;
u8 spellslot = spellnumber & 7;
if ( spellslot == 0 )
spellslot = 8;
bitwise_contents[( spellnumber - 1 ) >> 3] |= 1 << ( spellslot - 1 );
return true;
}
return false;
}
bool Spellbook::can_add( const Item& item ) const
{
// note: item count maximums are implicitly checked for.
// you can only add scrolls to spellbooks
if ( item.objtype_ < gamestate.spell_scroll_objtype_limits[spell_school][0] ||
item.objtype_ > gamestate.spell_scroll_objtype_limits[spell_school][1] )
{
return false;
}
// you can only add one of each kind of scroll to a spellbook.
u16 spellnum = USpellScroll::convert_objtype_to_spellnum( item.objtype_, spell_school );
u8 spellslot = spellnum & 7;
if ( spellslot == 0 )
spellslot = 8;
if ( bitwise_contents[( spellnum - 1 ) >> 3] & ( 1 << ( spellslot - 1 ) ) )
return false;
return true;
}
void Spellbook::add_bulk( int /* item_count_delta */, int /* weight_delta */ )
{
// spellbooks don't modify their weight, either when adding
// or when removing an item.
}
void Spellbook::add( Item* item )
{
// UContainer::add(item);
u16 spellnum = USpellScroll::convert_objtype_to_spellnum( item->objtype_, spell_school );
u8 spellslot = spellnum & 7;
if ( spellslot == 0 )
spellslot = 8;
bitwise_contents[( spellnum - 1 ) >> 3] |= 1 << ( spellslot - 1 );
item->destroy();
// item->saveonexit(0);
}
void Spellbook::printProperties( Clib::StreamWriter& sw ) const
{
base::printProperties( sw );
for ( int i = 0; i < 8; ++i )
sw() << "\tSpellbits" << i << "\t" << (int)bitwise_contents[i] << pf_endl;
}
void Spellbook::readProperties( Clib::ConfigElem& elem )
{
base::readProperties( elem );
std::ostringstream os;
for ( int i = 0; i < 8; ++i )
{
os << "Spellbits" << i;
bitwise_contents[i] = (u8)elem.remove_ushort( os.str().c_str(), 0 );
os.str( "" );
}
}
void Spellbook::printOn( Clib::StreamWriter& sw ) const
{
base::printOn( sw );
printContents( sw );
}
void Spellbook::printSelfOn( Clib::StreamWriter& sw ) const
{
base::printOn( sw );
}
void Spellbook::calc_current_bitwise_contents()
{
for ( UContainer::const_iterator itr = begin(), itrend = end(); itr != itrend; ++itr )
{
const Item* scroll = GET_ITEM_PTR( itr );
u16 spellnum = USpellScroll::convert_objtype_to_spellnum( scroll->objtype_, spell_school );
u8 spellslot = spellnum & 7;
if ( spellslot == 0 )
spellslot = 8;
bitwise_contents[( spellnum - 1 ) >> 3] |= 1 << ( spellslot - 1 );
}
// ok, it's been upgraded. Destroy everything inside it.
for ( UContainer::iterator itr = begin(), itrend = end(); itr != itrend; ++itr )
{
Item* scroll = GET_ITEM_PTR( itr );
scroll->destroy();
}
}
USpellScroll::USpellScroll( const Items::ItemDesc& itemdesc )
: Item( itemdesc, UOBJ_CLASS::CLASS_ITEM )
{
}
u16 USpellScroll::convert_objtype_to_spellnum( u32 objtype, u8 school )
{
u16 spellnum = static_cast<u16>( objtype - gamestate.spell_scroll_objtype_limits[school][0] + 1 );
if ( school == 0 ) // weirdness in order of original spells
{
if ( spellnum == 1 )
spellnum = 7;
else if ( spellnum <= 7 )
--spellnum;
}
return spellnum;
}
// Spell scrolls send their spell ID in AMOUNT while they're in a spellbook.
// Otherwise, they're stackable I believe.
u16 USpellScroll::get_senditem_amount() const
{
if ( ( container != nullptr ) && ( container->script_isa( POLCLASS_SPELLBOOK ) ) )
{
Spellbook* book = static_cast<Spellbook*>( container );
return convert_objtype_to_spellnum( objtype_, book->spell_school );
}
else // not contained, or not in a spellbook
{
return amount_;
}
}
size_t USpellScroll::estimatedSize() const
{
return base::estimatedSize();
}
void Spellbook::send_book_old( Network::Client* client )
{
client->pause();
if ( !locked() )
{
send_open_gump( client, *this );
send_spellbook_contents( client, *this );
}
else
{
send_sysmessage( client, "That is locked." );
}
client->restart();
}
bool Spellbook::get_method_hook( const char* methodname, Bscript::Executor* ex, ExportScript** hook,
unsigned int* PC ) const
{
if ( gamestate.system_hooks.get_method_hook( gamestate.system_hooks.spellbook_method_script.get(),
methodname, ex, hook, PC ) )
return true;
return base::get_method_hook( methodname, ex, hook, PC );
}
void send_spellbook_contents( Network::Client* client, Spellbook& spellbook )
{
Network::PktHelper::PacketOut<Network::PktOut_3C> msg;
msg->offset += 4; // msglen+count
u16 count = 0;
for ( u16 i = 0; i < 64; ++i )
{
u32 objtype = gamestate.spell_scroll_objtype_limits[0][0] + i;
u16 spellnumber = USpellScroll::convert_objtype_to_spellnum( objtype, spellbook.spell_school );
u8 spellpos = spellnumber & 7; // spellpos is the spell's position it it's circle's array.
if ( spellpos == 0 )
spellpos = 8;
if ( ( ( spellbook.bitwise_contents[( ( spellnumber - 1 ) >> 3 )] ) &
( 1 << ( spellpos - 1 ) ) ) != 0 )
{
msg->Write<u32>( 0x7FFFFFFFu - spellnumber );
msg->WriteFlipped<u16>( objtype );
msg->offset++; // unk6
msg->WriteFlipped<u16>( spellnumber ); // amount
msg->WriteFlipped<u16>( 1u ); // x
msg->WriteFlipped<u16>( 1u ); // y
if ( client->ClientType & Network::CLIENTTYPE_6017 )
msg->Write<u8>( 0u ); // slotindex
msg->Write<u32>( spellbook.serial_ext );
msg->WriteFlipped<u16>( 0u ); // color
++count;
}
}
u16 len = msg->offset;
msg->offset = 1;
msg->WriteFlipped<u16>( len );
msg->WriteFlipped<u16>( count );
msg.Send( client, len );
}
}
}