mirror of
https://github.com/polserver/polserver
synced 2026-08-13 08:23:08 -04:00
* origin/master: (93 commits) Add support for dictionary creation (#277) Add do-while statement (#276) Add foreach loops (#275) New compiler: array initialization (#274) Disambiguator: figure out if label is a case statement dispatch selector, or should apply to a statement within the case dispatch group's block. (#273) New compiler: Add support for case statements (#272) Change "logical" to "bitwise". (#258) Fix divide-by-zero errors, and mask FormatError if it were thrown in a destructor. (#271) Add break and continue statements. (#270) New compiler: assignment to local and global variables. (#269) Add support for while loops (#268) Optimize if statements, and emit module functions in legacy order (#267) Optimizations: binary operators (#266) Add binary operators (#265) Add support for const declarations (#264) Handle default parameter values, pass-by-name, and return statements in functions. (#263) Add limited support for user functions (#262) New compiler: add return and exit statements (#261) Add support for if-then-else statements (#259) OG compiler: set module to Mod_Basic when a token is an identifier (#257) ...
1176 lines
37 KiB
C++
1176 lines
37 KiB
C++
/** @file
|
|
*
|
|
* @par History
|
|
* - 2005/08/19 Shinigami: ZLib functionality linked directly into Core
|
|
* - 2005/09/26 Shinigami: wrong styled break condition in CustomHouseDesign::Compress
|
|
* - 2009/09/03 MuadDib: Relocation of multi related cpp/h
|
|
* - 2009/12/02 Turley: added config.max_tile_id - Tomi
|
|
*
|
|
* @todo Will require a special Character member to check in all kinds of places.
|
|
* Character.EditHouse=0/1
|
|
* The purpose, is to make NPCs IGNORE THIS PERSON, and to NOT send ANY packets about this person
|
|
* to ANYONE. This will also need to be checked when using all sorts of crap. Like when triggering
|
|
* equip/insert/item use/etc. This char should be able to do NOTHING except edit the house when
|
|
* in edit house mode, etc.
|
|
* Enabling this mode can be internal only, simply by linking it with the Custom housing tool.
|
|
* Have a script that can be made to run additional stuff with using the tool, so scripters can
|
|
* check extra stuff if they want, etc, and that script's return decides if to activate the housing
|
|
* tool. Should suffice.
|
|
*/
|
|
|
|
|
|
#include "customhouses.h"
|
|
|
|
#include <cstddef>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <string>
|
|
|
|
#include "../../bscript/bstruct.h"
|
|
#include "../../clib/cfgelem.h"
|
|
#include "../../clib/clib_endian.h"
|
|
#include "../../clib/logfacility.h"
|
|
#include "../../clib/passert.h"
|
|
#include "../../clib/stlutil.h"
|
|
#include "../../clib/streamsaver.h"
|
|
#include "../../plib/clidata.h"
|
|
#include "../../plib/systemstate.h"
|
|
#include "../core.h"
|
|
#include "../globals/uvars.h"
|
|
#include "../item/item.h"
|
|
#include "../item/itemdesc.h"
|
|
#include "../mkscrobj.h"
|
|
#include "../mobile/charactr.h"
|
|
#include "../network/cgdata.h"
|
|
#include "../network/client.h"
|
|
#include "../network/packethelper.h"
|
|
#include "../network/packets.h"
|
|
#include "../network/pktboth.h"
|
|
#include "../network/pktout.h"
|
|
#include "../network/pktoutid.h"
|
|
#include "../realms/realm.h"
|
|
#include "../scrdef.h"
|
|
#include "../scrsched.h"
|
|
#include "../syshook.h"
|
|
#include "../ufunc.h"
|
|
#include "../uoscrobj.h"
|
|
#include "../uworld.h"
|
|
#include "house.h"
|
|
#include "multidef.h"
|
|
|
|
#include <zlib.h>
|
|
|
|
|
|
namespace Pol
|
|
{
|
|
namespace Multi
|
|
{
|
|
// bytes per tile - currently only mode 0 works, meaning we send u16 graphic, s8 x,y,z offsets
|
|
#define BYTES_PER_TILE 5
|
|
|
|
// fixed z offsets for each floor
|
|
const char CustomHouseDesign::custom_house_z_xlate_table[CUSTOM_HOUSE_NUM_PLANES] = {0, 7, 27,
|
|
47, 67, 80};
|
|
|
|
// translate z offset to floor number, use floor below passed-in z value, unless exact match
|
|
char CustomHouseDesign::z_to_custom_house_table( char z )
|
|
{
|
|
unsigned char i;
|
|
for ( i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
{
|
|
if ( z == custom_house_z_xlate_table[i] )
|
|
return i;
|
|
else if ( z < custom_house_z_xlate_table[i] )
|
|
return i - 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
CustomHouseDesign::CustomHouseDesign() : size( 0, 0 ), offset( 0, 0 )
|
|
{
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
floor_sizes[i] = 0;
|
|
}
|
|
|
|
// fixme: need a copy ctor?
|
|
|
|
CustomHouseDesign::CustomHouseDesign( Core::Pos2d width_height, Core::Vec2d xyoffset )
|
|
{
|
|
InitDesign( std::move( width_height ), std::move( xyoffset ) );
|
|
}
|
|
|
|
size_t CustomHouseDesign::estimatedSize() const
|
|
{
|
|
size_t size = sizeof( CustomHouseDesign );
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
size += Elements[i].estimatedSize();
|
|
return size;
|
|
}
|
|
|
|
// init the geometry of the design. the design cannot exist outside the multi foundation boundary
|
|
// (exception: front steps)
|
|
void CustomHouseDesign::InitDesign( Core::Pos2d width_height, Core::Vec2d xyoffset )
|
|
{
|
|
size = std::move( width_height );
|
|
offset = std::move( xyoffset );
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
{
|
|
floor_sizes[i] = 0;
|
|
Elements[i].SetSize( size );
|
|
Elements[i].offset = offset;
|
|
}
|
|
}
|
|
|
|
CustomHouseDesign& CustomHouseDesign::operator=( const CustomHouseDesign& design )
|
|
{
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
{
|
|
Elements[i] = design.Elements[i];
|
|
floor_sizes[i] = design.floor_sizes[i];
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// Adds the element to the design
|
|
void CustomHouseDesign::Add( CUSTOM_HOUSE_ELEMENT& elem )
|
|
{
|
|
int floor_num = z_to_custom_house_table( elem.z );
|
|
if ( floor_num == -1 )
|
|
return;
|
|
Elements[floor_num].AddElement( elem );
|
|
floor_sizes[floor_num]++;
|
|
}
|
|
|
|
// fixme: low walls not being replaced
|
|
// Replaces an existing object depending on the 2 tile heights
|
|
void CustomHouseDesign::AddOrReplace( CUSTOM_HOUSE_ELEMENT& elem )
|
|
{
|
|
int floor_num = z_to_custom_house_table( elem.z );
|
|
if ( floor_num == -1 )
|
|
return;
|
|
char adding_height = Plib::tileheight( elem.graphic );
|
|
|
|
Core::Pos2d idx = Core::Pos2d( 0, 0 ) + ( elem.offset + offset );
|
|
if ( !ValidLocation( idx ) )
|
|
return;
|
|
HouseFloorZColumn* column = &Elements[floor_num].data.at( idx.x() ).at( idx.y() );
|
|
for ( HouseFloorZColumn::iterator itr = column->begin(), itrend = column->end(); itr != itrend;
|
|
++itr )
|
|
{
|
|
char existing_height = Plib::tileheight( itr->graphic );
|
|
|
|
if ( ( ( existing_height == 0 ) && ( adding_height == 0 ) ) || // replace floor with floor
|
|
( ( existing_height != 0 ) && ( adding_height != 0 ) ) ) // or nonfloor with nonfloor
|
|
|
|
{
|
|
column->erase( itr );
|
|
floor_sizes[floor_num]--;
|
|
Add( elem );
|
|
return;
|
|
}
|
|
}
|
|
// no replacement, just add
|
|
Add( elem );
|
|
}
|
|
|
|
bool CustomHouseDesign::Erase( const Core::Vec2d& off, u8 z, int minheight )
|
|
{
|
|
int floor_num = z_to_custom_house_table( z );
|
|
if ( floor_num == -1 )
|
|
return false;
|
|
|
|
Core::Pos2d idx = Core::Pos2d( 0, 0 ) + ( off + offset );
|
|
if ( !ValidLocation( idx ) )
|
|
return false;
|
|
|
|
HouseFloorZColumn* column = &Elements[floor_num].data.at( idx.x() ).at( idx.y() );
|
|
for ( HouseFloorZColumn::iterator itr = column->begin(), itrend = column->end(); itr != itrend;
|
|
++itr )
|
|
{
|
|
char t_height = Plib::tileheight( itr->graphic );
|
|
if ( ( itr->z == z ) && ( t_height >= minheight ) )
|
|
{
|
|
column->erase( itr );
|
|
floor_sizes[floor_num]--;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CustomHouseDesign::EraseGraphicAt( u16 graphic, const Core::Vec2d& off, u8 z )
|
|
{
|
|
int floor_num = z_to_custom_house_table( z );
|
|
if ( floor_num == -1 )
|
|
return false;
|
|
|
|
Core::Pos2d idx = Core::Pos2d( 0, 0 ) + ( off + offset );
|
|
if ( !ValidLocation( idx ) )
|
|
return false;
|
|
HouseFloorZColumn* column = &Elements[floor_num].data.at( idx.x() ).at( idx.y() );
|
|
for ( HouseFloorZColumn::iterator itr = column->begin(), itrend = column->end(); itr != itrend;
|
|
++itr )
|
|
{
|
|
if ( itr->graphic == graphic )
|
|
{
|
|
column->erase( itr );
|
|
floor_sizes[floor_num]--;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CustomHouseDesign::ReplaceDirtFloor( const Core::Vec2d& off )
|
|
{
|
|
int floor_num = 1; // dirt always goes on floor 1 (z=7)
|
|
|
|
Core::Pos2d idx = Core::Pos2d( 0, 0 ) + ( off + offset );
|
|
|
|
if ( idx.x() == 0 || idx.y() == 0 ||
|
|
idx.y() ==
|
|
size.y() ) // don't replace dirt at far-west and far-north sides, check height for y + 1
|
|
return;
|
|
|
|
bool floor_exists = false;
|
|
|
|
if ( !ValidLocation( idx ) )
|
|
return;
|
|
HouseFloorZColumn* column = &Elements[floor_num].data.at( idx.x() ).at( idx.y() );
|
|
for ( HouseFloorZColumn::iterator itr = column->begin(), itrend = column->end(); itr != itrend;
|
|
++itr )
|
|
{
|
|
if ( Plib::tileheight( itr->graphic ) == 0 ) // a floor tile exists
|
|
{
|
|
floor_exists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( floor_exists == false ) // add a dirt tile
|
|
{
|
|
CUSTOM_HOUSE_ELEMENT elem;
|
|
elem.graphic = DIRTY_TILE;
|
|
elem.offset = off;
|
|
elem.z = 7;
|
|
|
|
Add( elem );
|
|
}
|
|
}
|
|
|
|
void CustomHouseDesign::Clear()
|
|
{
|
|
// delete contents of all z column lists
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
{
|
|
for ( HouseFloor::iterator xitr = Elements[i].data.begin(), xitrend = Elements[i].data.end();
|
|
xitr != xitrend; ++xitr )
|
|
{
|
|
for ( HouseFloorRow::iterator yitr = xitr->begin(), yitrend = xitr->end(); yitr != yitrend;
|
|
++yitr )
|
|
{
|
|
yitr->clear();
|
|
}
|
|
}
|
|
floor_sizes[i] = 0;
|
|
}
|
|
}
|
|
|
|
// caller must delete, assume type 0
|
|
unsigned char* CustomHouseDesign::Compress( int floor, u32* uncompr_length, u32* compr_length )
|
|
{
|
|
int numtiles = floor_sizes[floor];
|
|
int nextindex = 0;
|
|
unsigned int ubuflen = numtiles * BYTES_PER_TILE;
|
|
unsigned long cbuflen =
|
|
( ( (unsigned long)( ( (float)( ubuflen ) ) * 1.001f ) ) + 12 ); // as per zlib spec
|
|
unsigned char* uncompressed = new unsigned char[ubuflen];
|
|
memset( uncompressed, 0, ubuflen );
|
|
unsigned char* compressed = new unsigned char[cbuflen];
|
|
memset( compressed, 0, cbuflen );
|
|
|
|
int i = 0;
|
|
for ( HouseFloor::const_iterator xitr = Elements[floor].data.begin(),
|
|
xitrend = Elements[floor].data.end();
|
|
xitr != xitrend; ++xitr )
|
|
{
|
|
i = 0;
|
|
for ( HouseFloorRow::const_iterator yitr = xitr->begin(), yitrend = xitr->end();
|
|
yitr != yitrend; ++yitr )
|
|
{
|
|
for ( HouseFloorZColumn::const_iterator zitr = yitr->begin(), zitrend = yitr->end();
|
|
zitr != zitrend; ++zitr, ++i )
|
|
{
|
|
// assume type 0, I don't know how to deal with stair pieces at odd Z values for mode 1,
|
|
// and mode 2 is just wacky. (position implied from list position, needs alot of null tiles
|
|
// to make that work (but they compress very well)
|
|
if ( i < numtiles )
|
|
{
|
|
uncompressed[nextindex++] = ( u8 )( ( zitr->graphic >> 8 ) & 0xFF );
|
|
uncompressed[nextindex++] = ( u8 )( zitr->graphic & 0xFF );
|
|
|
|
uncompressed[nextindex++] = (u8)zitr->offset.x();
|
|
uncompressed[nextindex++] = (u8)zitr->offset.y();
|
|
uncompressed[nextindex++] = (u8)zitr->z;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*uncompr_length = nextindex;
|
|
|
|
int ret = compress2( compressed, &cbuflen, uncompressed, nextindex, Z_DEFAULT_COMPRESSION );
|
|
if ( ret == Z_OK )
|
|
{
|
|
delete[] uncompressed;
|
|
*compr_length = cbuflen;
|
|
return compressed;
|
|
}
|
|
else
|
|
{
|
|
*uncompr_length = 0;
|
|
*compr_length = 0;
|
|
delete[] compressed;
|
|
delete[] uncompressed;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
bool CustomHouseDesign::IsEmpty() const
|
|
{
|
|
int total = 0;
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
total += floor_sizes[i];
|
|
return total == 0 ? true : false;
|
|
}
|
|
|
|
unsigned int CustomHouseDesign::TotalSize() const
|
|
{
|
|
unsigned int size = 0;
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
{
|
|
size += floor_sizes[i];
|
|
}
|
|
return size;
|
|
}
|
|
|
|
unsigned char CustomHouseDesign::NumUsedPlanes() const
|
|
{
|
|
unsigned char size = 0;
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
{
|
|
if ( floor_sizes[i] > 0 )
|
|
size++;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
// used to add the base foundation to initialize a design, and multi stairs. not tested with
|
|
// anything else.
|
|
// I guess you could add a boat or something inside a house, but deleting would be impossible.
|
|
// Deleting stairs is handled explicitly (CustomHouseDesign::DeleteStairs).
|
|
void CustomHouseDesign::AddMultiAtOffset( u16 multiid, const Core::Vec3d& off )
|
|
{
|
|
const MultiDef* multidef = MultiDefByMultiID( multiid );
|
|
if ( multidef == nullptr )
|
|
{
|
|
ERROR_PRINT << "Trying to add Multi to customhouse, multiid 0x" << fmt::hexu( multiid )
|
|
<< " multi definition doesn't exist!\n";
|
|
return;
|
|
}
|
|
|
|
for ( MultiDef::Components::const_iterator itr = multidef->components.begin(),
|
|
end = multidef->components.end();
|
|
itr != end; ++itr )
|
|
{
|
|
const MULTI_ELEM* m_elem = itr->second;
|
|
if ( ( ( m_elem->objtype ) & Plib::systemstate.config.max_tile_id ) ==
|
|
1 ) // don't add the invisible multi tile
|
|
continue;
|
|
// cout << "0x" << hex << m_elem->graphic
|
|
// << " 0x" << hex << m_elem->flags
|
|
// << ":" << dec << m_elem->x << "," << m_elem->y << "," << m_elem->z << endl;
|
|
CUSTOM_HOUSE_ELEMENT ch_elem;
|
|
ch_elem.graphic = m_elem->objtype;
|
|
ch_elem.offset = m_elem->rel_pos.xy() + off.xy();
|
|
ch_elem.z = static_cast<u8>( m_elem->rel_pos.z() + off.z() );
|
|
Add( ch_elem );
|
|
}
|
|
}
|
|
|
|
void CustomHouseDesign::readProperties( Clib::ConfigElem& elem, const std::string& prefix )
|
|
{
|
|
std::string line;
|
|
|
|
while ( elem.remove_prop( prefix.c_str(), &line ) )
|
|
{
|
|
ISTRINGSTREAM is( line );
|
|
u16 graphic;
|
|
s32 x, y;
|
|
u16 z;
|
|
is >> graphic;
|
|
is >> x;
|
|
is >> y;
|
|
is >> z;
|
|
|
|
CUSTOM_HOUSE_ELEMENT _elem;
|
|
_elem.graphic = graphic;
|
|
_elem.offset = Core::Vec2d( Core::Vec2d::clip( x ), Core::Vec2d::clip( y ) );
|
|
_elem.z = (u8)z;
|
|
Add( _elem );
|
|
}
|
|
}
|
|
|
|
void CustomHouseDesign::printProperties( Clib::StreamWriter& sw, const std::string& prefix ) const
|
|
{
|
|
if ( !IsEmpty() )
|
|
{
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
{
|
|
for ( HouseFloor::const_iterator xitr = Elements[i].data.begin(),
|
|
xitrend = Elements[i].data.end();
|
|
xitr != xitrend; ++xitr )
|
|
{
|
|
for ( HouseFloorRow::const_iterator yitr = xitr->begin(), yitrend = xitr->end();
|
|
yitr != yitrend; ++yitr )
|
|
{
|
|
for ( HouseFloorZColumn::const_iterator zitr = yitr->begin(), zitrend = yitr->end();
|
|
zitr != zitrend; ++zitr )
|
|
{
|
|
sw() << "\t" << prefix << "\t " << zitr->graphic << " " << zitr->offset.x() << " "
|
|
<< zitr->offset.y() << " " << (u16)zitr->z << '\n';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// for testing, prints each floor's x,y,z rows
|
|
void CustomHouseDesign::testprint( std::ostream& os ) const
|
|
{
|
|
if ( !IsEmpty() )
|
|
{
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
{
|
|
int x = 0, y = 0;
|
|
for ( HouseFloor::const_iterator xitr = Elements[i].data.begin(),
|
|
xitrend = Elements[i].data.end();
|
|
xitr != xitrend; ++xitr, x++ )
|
|
{
|
|
os << "X: " << x << std::endl;
|
|
for ( HouseFloorRow::const_iterator yitr = xitr->begin(), yitrend = xitr->end();
|
|
yitr != yitrend; ++yitr, y++ )
|
|
{
|
|
os << "\tY: " << y << std::endl;
|
|
for ( HouseFloorZColumn::const_iterator zitr = yitr->begin(), zitrend = yitr->end();
|
|
zitr != zitrend; ++zitr )
|
|
{
|
|
os << "\t\t" << zitr->graphic << " " << zitr->offset.x() << " " << zitr->offset.y()
|
|
<< " " << (u16)zitr->z << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Tells wether an item should be show in custom house design or not
|
|
bool CustomHouseDesign::isEditableItem( UHouse* house, Items::Item* item )
|
|
{
|
|
// Give scripters the chance to keep an item alive
|
|
if ( item->invisible() )
|
|
return false;
|
|
|
|
// Some items could be part of the house, but not inside the house (e.g. an exterior lamp post)
|
|
// hide them to avoid an exception later, since this is not supported
|
|
// only test foodprint: find_supporting_multis would also work, as long as readshapes includes the
|
|
// teleporter components
|
|
Core::Vec2d shape = item->pos().xy() - house->pos().xy();
|
|
shape += offset;
|
|
// y is +1
|
|
if ( shape >= Core::Vec2d( 0, 0 ) && shape < ( size - Core::Vec2d( 0, -1 ) ).from_origin() )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void CustomHouseDesign::ClearComponents( UHouse* house )
|
|
{
|
|
UHouse::Components* comp = house->get_components();
|
|
UHouse::Components::iterator itr = comp->begin();
|
|
|
|
while ( itr != comp->end() )
|
|
{
|
|
Items::Item* item = ( *itr ).get();
|
|
if ( item != nullptr && !item->orphan() )
|
|
{
|
|
if ( isEditableItem( house, item ) )
|
|
{
|
|
itr = comp->erase( itr );
|
|
destroy_item( item );
|
|
continue;
|
|
}
|
|
}
|
|
++itr;
|
|
}
|
|
}
|
|
|
|
void CustomHouseDesign::AddComponents( UHouse* house )
|
|
{
|
|
UHouse::Components* comp = house->get_components();
|
|
for ( UHouse::Components::const_iterator itr = comp->begin(), end = comp->end(); itr != end;
|
|
++itr )
|
|
{
|
|
Items::Item* item = ( *itr ).get();
|
|
if ( item != nullptr && !item->orphan() )
|
|
{
|
|
if ( isEditableItem( house, item ) ) // give scripters the chance to keep an item alive
|
|
{
|
|
CUSTOM_HOUSE_ELEMENT elem;
|
|
elem.graphic = item->graphic;
|
|
Core::Vec3d offset = item->pos().xyz() - house->pos().xyz();
|
|
elem.offset = offset.xy();
|
|
elem.z = offset.z();
|
|
AddOrReplace( elem ); // A teleporter could replace a floortile
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void CustomHouseDesign::FillComponents( UHouse* house, bool add_as_component )
|
|
{
|
|
UHouse::Components* comp = house->get_components();
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
{
|
|
for ( HouseFloor::iterator xitr = Elements[i].data.begin(), xitrend = Elements[i].data.end();
|
|
xitr != xitrend; ++xitr )
|
|
{
|
|
for ( HouseFloorRow::iterator yitr = xitr->begin(), yitrend = xitr->end(); yitr != yitrend;
|
|
++yitr )
|
|
{
|
|
HouseFloorZColumn::iterator zitr = yitr->begin();
|
|
while ( zitr != yitr->end() )
|
|
{
|
|
const Items::ItemDesc& id = Items::find_itemdesc( zitr->graphic );
|
|
if ( id.type == Items::ItemDesc::DOORDESC )
|
|
{
|
|
if ( add_as_component )
|
|
{
|
|
Items::Item* component = Items::Item::create( id.objtype );
|
|
if ( component != nullptr )
|
|
{
|
|
bool res = house->add_component( component, Core::Vec3d( zitr->offset, zitr->z ) );
|
|
passert_always_r( res,
|
|
"Couldn't add newly created door as house component. Please "
|
|
"report this bug on the forums." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Core::Pos4d cpos = house->pos() + Core::Vec3d( zitr->offset, zitr->z );
|
|
// if component already exists erase from design, otherwise keep it
|
|
bool exists = false;
|
|
for ( const auto& c : *comp )
|
|
{
|
|
Items::Item* item = c.get();
|
|
if ( item == nullptr || item->orphan() )
|
|
continue;
|
|
if ( cpos == item->pos() && zitr->graphic == item->graphic )
|
|
{
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( !exists )
|
|
{
|
|
++zitr;
|
|
continue;
|
|
}
|
|
}
|
|
zitr = yitr->erase( zitr );
|
|
floor_sizes[i]--;
|
|
}
|
|
else if ( zitr->graphic >= TELEPORTER_START &&
|
|
zitr->graphic <= TELEPORTER_END ) // teleporters
|
|
{
|
|
if ( add_as_component )
|
|
{
|
|
Items::Item* component = Items::Item::create( zitr->graphic );
|
|
if ( component != nullptr )
|
|
{
|
|
bool res = house->add_component( component, Core::Vec3d( zitr->offset, zitr->z ) );
|
|
passert_always_r( res,
|
|
"Couldn't add newly created teleporter as house component. "
|
|
"Please report this bug on the forums." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Core::Pos4d cpos = house->pos() + Core::Vec3d( zitr->offset, zitr->z );
|
|
// if component already exists erase from design, otherwise keep it
|
|
bool exists = false;
|
|
for ( const auto& c : *comp )
|
|
{
|
|
Items::Item* item = c.get();
|
|
if ( item == nullptr || item->orphan() )
|
|
continue;
|
|
if ( cpos == item->pos() && zitr->graphic == item->graphic )
|
|
{
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( !exists )
|
|
{
|
|
++zitr;
|
|
continue;
|
|
}
|
|
}
|
|
zitr = yitr->erase( zitr );
|
|
floor_sizes[i]--;
|
|
}
|
|
else
|
|
++zitr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Bscript::ObjArray* CustomHouseDesign::list_parts() const
|
|
{
|
|
std::unique_ptr<Bscript::ObjArray> arr( new Bscript::ObjArray );
|
|
for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
|
|
{
|
|
for ( HouseFloor::const_iterator xitr = Elements[i].data.begin(),
|
|
xitrend = Elements[i].data.end();
|
|
xitr != xitrend; ++xitr )
|
|
{
|
|
for ( HouseFloorRow::const_iterator yitr = xitr->begin(), yitrend = xitr->end();
|
|
yitr != yitrend; ++yitr )
|
|
{
|
|
for ( HouseFloorZColumn::const_iterator zitr = yitr->begin(), zitrend = yitr->end();
|
|
zitr != zitrend; ++zitr )
|
|
{
|
|
std::unique_ptr<Bscript::BStruct> itemstruct( new Bscript::BStruct );
|
|
itemstruct->addMember( "graphic", new Bscript::BLong( zitr->graphic ) );
|
|
itemstruct->addMember( "xoffset", new Bscript::BLong( zitr->offset.x() ) );
|
|
itemstruct->addMember( "yoffset", new Bscript::BLong( zitr->offset.y() ) );
|
|
itemstruct->addMember( "z", new Bscript::BLong( zitr->z ) );
|
|
arr->addElement( itemstruct.release() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return arr.release();
|
|
}
|
|
|
|
void CustomHouseStopEditing( Mobile::Character* chr, UHouse* house, bool send_pkts )
|
|
{
|
|
if ( send_pkts && chr->client )
|
|
{
|
|
Network::PktHelper::PacketOut<Network::PktOut_BF_Sub20> msg;
|
|
msg->WriteFlipped<u16>( 17u );
|
|
msg->offset += 2; // sub
|
|
msg->Write<u32>( house->serial_ext );
|
|
msg->Write<u8>( 0x5u ); // end
|
|
msg->offset += 2; // u16 unk2 FIXME what's the meaning
|
|
msg->Write<u32>( 0xFFFFFFFFu ); // fixme
|
|
msg->Write<u8>( 0xFFu ); // fixme
|
|
msg.Send( chr->client );
|
|
}
|
|
const MultiDef& def = house->multidef();
|
|
Core::Pos4d newpos( house->pos() + Core::Vec2d( def.min_relp.x(), def.max_relp.y() + 1 ) );
|
|
move_character_to( chr, newpos, Core::MOVEITEM_FORCELOCATION );
|
|
if ( chr->client )
|
|
{
|
|
chr->client->gd->custom_house_serial = 0;
|
|
chr->client->gd->custom_house_chrserial = 0;
|
|
}
|
|
house->editing = false;
|
|
if ( send_pkts && chr->client )
|
|
{
|
|
ItemList itemlist;
|
|
MobileList moblist;
|
|
UHouse::list_contents( house, itemlist, moblist );
|
|
while ( !itemlist.empty() )
|
|
{
|
|
Items::Item* item = itemlist.front();
|
|
send_item( chr->client, item );
|
|
itemlist.pop_front();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CustomHousesAdd( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
if ( house == nullptr )
|
|
return;
|
|
|
|
Core::CH_ADD add = msg->ch_add;
|
|
CUSTOM_HOUSE_ELEMENT elem;
|
|
elem.graphic = cfBEu16( add.tileID );
|
|
elem.offset = Core::Vec2d( Core::Vec2d::clip( cfBEu32( add.xoffset ) ),
|
|
Core::Vec2d::clip( cfBEu32( add.yoffset ) ) );
|
|
elem.z = CustomHouseDesign::custom_house_z_xlate_table[house->editing_floor_num];
|
|
|
|
// the south side of the house can have stairs at z=0
|
|
// int ysize = house->multidef().maxry - house->multidef().minry;
|
|
|
|
if ( elem.offset.y() == house->multidef().max_relp.y() + 1 )
|
|
elem.z = 0;
|
|
|
|
house->WorkingDesign.AddOrReplace( elem );
|
|
|
|
// invalidate stored packet
|
|
std::vector<u8> newvec;
|
|
house->WorkingCompressed.swap( newvec );
|
|
|
|
house->revision++;
|
|
}
|
|
|
|
void CustomHousesAddMulti( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
if ( house == nullptr )
|
|
return;
|
|
|
|
u16 itemID = cfBEu16( msg->ch_add_multi.multiID );
|
|
Core::Vec3d off( static_cast<s8>( cfBEu32( msg->ch_add_multi.xoffset ) ),
|
|
static_cast<s8>( cfBEu32( msg->ch_add_multi.yoffset ) ),
|
|
CustomHouseDesign::custom_house_z_xlate_table[house->editing_floor_num] );
|
|
|
|
// only allow stairs IDs
|
|
if ( itemID < STAIR_MULTIID_MIN || itemID > STAIR_MULTIID_MAX )
|
|
{
|
|
Mobile::Character* chr = Core::find_character( serial );
|
|
if ( chr != nullptr && chr->client != nullptr )
|
|
CustomHousesSendFull( house, chr->client, HOUSE_DESIGN_WORKING );
|
|
return;
|
|
}
|
|
|
|
house->WorkingDesign.AddMultiAtOffset( itemID, off );
|
|
|
|
// invalidate stored packet
|
|
std::vector<u8> newvec;
|
|
house->WorkingCompressed.swap( newvec );
|
|
|
|
house->revision++;
|
|
}
|
|
|
|
void CustomHousesErase( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
if ( house == nullptr )
|
|
return;
|
|
|
|
Core::Vec3d off( Core::Vec2d::clip( cfBEu32( msg->ch_erase.xoffset ) ),
|
|
Core::Vec2d::clip( cfBEu32( msg->ch_erase.yoffset ) ),
|
|
static_cast<s8>( cfBEu32( msg->ch_erase.z ) ) );
|
|
u16 graphic = cfBEu16( msg->ch_erase.tileID );
|
|
|
|
Core::Pos3d realpos = Core::Pos3d( 0, 0, 0 ) + off + house->WorkingDesign.offset;
|
|
|
|
// foundation walls should not be deleted
|
|
if ( realpos.z() == 0 && realpos < ( house->WorkingDesign.size - Core::Vec2d( 0, 1 ) ) )
|
|
{
|
|
Mobile::Character* chr = Core::find_character( serial );
|
|
if ( chr && chr->client )
|
|
CustomHousesSendFull( house, chr->client, HOUSE_DESIGN_WORKING );
|
|
return;
|
|
}
|
|
|
|
// check if not deleting a stairs piece (if we are, DeleteStairs will do it)
|
|
// check z == 0 to make sure all exterior stairs are deleted with EraseGraphicAt
|
|
if ( off.z() == 0 || !house->WorkingDesign.DeleteStairs( graphic, off ) )
|
|
{
|
|
house->WorkingDesign.EraseGraphicAt( graphic, off.xy(), off.z() );
|
|
// maybe replace empty ground floor with dirt tile
|
|
if ( off.z() == CustomHouseDesign::custom_house_z_xlate_table[1] )
|
|
house->WorkingDesign.ReplaceDirtFloor( off.xy() );
|
|
}
|
|
|
|
// invalidate stored packet
|
|
std::vector<u8> newvec;
|
|
house->WorkingCompressed.swap( newvec );
|
|
|
|
Mobile::Character* chr = Core::find_character( serial );
|
|
if ( chr && chr->client )
|
|
CustomHousesSendFull( house, chr->client, HOUSE_DESIGN_WORKING );
|
|
|
|
house->revision++;
|
|
}
|
|
|
|
void CustomHousesClear( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
Mobile::Character* chr = Core::find_character( serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
if ( house == nullptr )
|
|
return;
|
|
|
|
house->WorkingDesign.Clear();
|
|
|
|
// invalidate stored packet
|
|
std::vector<u8> newvec;
|
|
house->WorkingCompressed.swap( newvec );
|
|
|
|
// add foundation back to design
|
|
house->WorkingDesign.AddMultiAtOffset( house->multiid, Core::Vec3d( 0, 0, 0 ) );
|
|
if ( chr != nullptr && chr->client != nullptr )
|
|
CustomHousesSendFull( house, chr->client, HOUSE_DESIGN_WORKING );
|
|
|
|
house->revision++;
|
|
}
|
|
|
|
// if the client closed his tool
|
|
void CustomHousesQuit( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
if ( house == nullptr )
|
|
return;
|
|
Mobile::Character* chr = Core::find_character( serial );
|
|
house->CustomHousesQuit( chr, false );
|
|
}
|
|
|
|
void CustomHousesCommit( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
Mobile::Character* chr = Core::find_character( serial );
|
|
if ( house == nullptr || chr == nullptr )
|
|
return;
|
|
|
|
if ( house->waiting_for_accept ) // dont allow multiple commits
|
|
return;
|
|
|
|
// remove dynamic bits (teleporters, doors)
|
|
house->WorkingDesign.FillComponents( house );
|
|
|
|
// call a script to do post processing (calc cost, yes/no confirm, consume cost, link teleporters)
|
|
Core::ScriptDef sd;
|
|
if ( sd.config_nodie( "misc/customhousecommit.ecl", nullptr, nullptr ) )
|
|
{
|
|
if ( sd.exists() )
|
|
{
|
|
house->waiting_for_accept = true;
|
|
// former part list as param is now possible via house member
|
|
if ( Core::start_script( sd, make_mobileref( chr ), new Module::EMultiRefObjImp( house ) ) !=
|
|
nullptr )
|
|
return;
|
|
}
|
|
}
|
|
house->AcceptHouseCommit( chr, true );
|
|
}
|
|
|
|
void CustomHousesSelectFloor( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
Mobile::Character* chr = Core::find_character( serial );
|
|
if ( house == nullptr )
|
|
return;
|
|
u8 floor = msg->ch_select_floor.floornumber;
|
|
|
|
if ( floor < 1 || floor > 4 )
|
|
floor = 1;
|
|
|
|
house->editing_floor_num = floor;
|
|
|
|
if ( chr )
|
|
{
|
|
Core::Pos4d newp( chr->pos().xy(),
|
|
house->z() + CustomHouseDesign::custom_house_z_xlate_table[floor],
|
|
chr->realm() );
|
|
move_character_to( chr, newp, Core::MOVEITEM_FORCELOCATION );
|
|
if ( chr->client )
|
|
CustomHousesSendFull( house, chr->client, HOUSE_DESIGN_WORKING );
|
|
}
|
|
}
|
|
|
|
void CustomHousesBackup( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
if ( house == nullptr )
|
|
return;
|
|
|
|
house->BackupDesign = house->WorkingDesign;
|
|
}
|
|
|
|
void CustomHousesRestore( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
Mobile::Character* chr = Core::find_character( serial );
|
|
if ( house == nullptr )
|
|
return;
|
|
|
|
if ( !house->BackupDesign.IsEmpty() )
|
|
house->WorkingDesign = house->BackupDesign;
|
|
|
|
std::vector<u8> newvec;
|
|
house->WorkingCompressed.swap( newvec );
|
|
if ( chr != nullptr && chr->client != nullptr )
|
|
CustomHousesSendFull( house, chr->client, HOUSE_DESIGN_WORKING );
|
|
}
|
|
|
|
void CustomHousesSynch( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
Mobile::Character* chr = Core::find_character( serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
if ( chr != nullptr && chr->client != nullptr && house != nullptr )
|
|
CustomHousesSendFull( house, chr->client, HOUSE_DESIGN_WORKING );
|
|
}
|
|
|
|
// replace working design with currently committed design
|
|
void CustomHousesRevert( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
|
|
if ( house == nullptr )
|
|
return;
|
|
|
|
house->WorkingDesign = house->CurrentDesign;
|
|
std::vector<u8> newvec;
|
|
house->WorkingCompressed.swap( newvec );
|
|
Mobile::Character* chr = Core::find_character( serial );
|
|
if ( chr != nullptr && chr->client != nullptr )
|
|
CustomHousesSendFull( house, chr->client, HOUSE_DESIGN_WORKING );
|
|
}
|
|
|
|
void CustomHousesRoofSelect( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
if ( house == nullptr )
|
|
return;
|
|
|
|
Core::CH_SELECT_ROOF add = msg->ch_select_roof;
|
|
CUSTOM_HOUSE_ELEMENT elem;
|
|
elem.graphic = cfBEu16( add.tileID );
|
|
elem.offset = Core::Vec2d( Core::Vec2d::clip( cfBEu32( add.xoffset ) ),
|
|
Core::Vec2d::clip( cfBEu32( add.yoffset ) ) );
|
|
s8 z = static_cast<s8>( cfBEu32( add.zoffset ) );
|
|
if ( z < -3 || z > 12 || z % 3 != 0 )
|
|
z = -3;
|
|
elem.z = z + CustomHouseDesign::custom_house_z_xlate_table[house->editing_floor_num];
|
|
|
|
house->WorkingDesign.AddOrReplace( elem );
|
|
|
|
// invalidate stored packet
|
|
std::vector<u8> newvec;
|
|
house->WorkingCompressed.swap( newvec );
|
|
|
|
house->revision++;
|
|
}
|
|
void CustomHousesRoofRemove( Core::PKTBI_D7* msg )
|
|
{
|
|
u32 serial = cfBEu32( msg->serial );
|
|
UHouse* house = UHouse::FindWorkingHouse( serial );
|
|
if ( house == nullptr )
|
|
return;
|
|
|
|
Core::CH_DELETE_ROOF remove = msg->ch_delete_roof;
|
|
Core::Vec2d off( Core::Vec2d::clip( cfBEu32( remove.xoffset ) ),
|
|
Core::Vec2d::clip( cfBEu32( remove.yoffset ) ) );
|
|
u8 z = static_cast<u8>( cfBEu32( remove.zoffset ) );
|
|
u16 graphic = cfBEu16( remove.tileID );
|
|
if ( !house->WorkingDesign.EraseGraphicAt( graphic, off, z ) )
|
|
{
|
|
Mobile::Character* chr = Core::find_character( serial );
|
|
if ( chr != nullptr && chr->client != nullptr )
|
|
CustomHousesSendFull( house, chr->client, HOUSE_DESIGN_WORKING );
|
|
return;
|
|
}
|
|
|
|
// invalidate stored packet
|
|
std::vector<u8> newvec;
|
|
house->WorkingCompressed.swap( newvec );
|
|
|
|
house->revision++;
|
|
}
|
|
|
|
void CustomHousesSendFull( UHouse* house, Network::Client* client, int design )
|
|
{
|
|
u32 clen;
|
|
u32 ulen;
|
|
unsigned char* data;
|
|
// unsigned char** stored_packet;
|
|
std::vector<u8>* stored_packet;
|
|
|
|
u32 planeheader = 0;
|
|
u32 buffer_len = 0;
|
|
CustomHouseDesign* pdesign;
|
|
const unsigned int data_offset = 17;
|
|
const u32 mode = 0; // we only know how to do mode 0 at this point.
|
|
|
|
// choose between sending working or current designs
|
|
switch ( design )
|
|
{
|
|
case HOUSE_DESIGN_CURRENT:
|
|
if ( house->CurrentCompressed.empty() ) // no design stored, create below
|
|
{
|
|
pdesign = &house->CurrentDesign;
|
|
stored_packet = &house->CurrentCompressed;
|
|
}
|
|
else // send stored packet
|
|
{
|
|
Core::networkManager.clientTransmit->AddToQueue(
|
|
client, &house->CurrentCompressed[0],
|
|
ctBEu16( *( reinterpret_cast<u16*>( &house->CurrentCompressed[1] ) ) ) );
|
|
return;
|
|
}
|
|
break;
|
|
case HOUSE_DESIGN_WORKING:
|
|
if ( house->WorkingCompressed.empty() ) // no design stored, create below
|
|
{
|
|
pdesign = &house->WorkingDesign;
|
|
stored_packet = &house->WorkingCompressed;
|
|
}
|
|
else // send stored packet
|
|
{
|
|
Core::networkManager.clientTransmit->AddToQueue(
|
|
client, &house->WorkingCompressed[0],
|
|
ctBEu16( *( reinterpret_cast<u16*>( &house->WorkingCompressed[1] ) ) ) );
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// create compressed house message
|
|
|
|
unsigned char planes = pdesign->NumUsedPlanes();
|
|
unsigned long sbuflen =
|
|
( (unsigned long)( ( (float)( pdesign->TotalSize() * BYTES_PER_TILE ) ) * 1.001f ) + 12 );
|
|
sbuflen += planes * BYTES_PER_TILE; // for plane header dword
|
|
sbuflen += 17; // for packet header
|
|
|
|
std::vector<u8> packet( sbuflen );
|
|
|
|
|
|
Core::PKTOUT_D8* msg = reinterpret_cast<Core::PKTOUT_D8*>( &packet[0] );
|
|
msg->msgtype = Core::PKTOUT_D8_ID;
|
|
msg->compressiontype = 0x3;
|
|
msg->unk = 0;
|
|
msg->serial = house->serial_ext;
|
|
msg->revision = ctBEu32( house->revision );
|
|
msg->numtiles = ctBEu16( static_cast<u16>( pdesign->TotalSize() ) );
|
|
|
|
msg->buffer->planecount = planes;
|
|
buffer_len = 1;
|
|
for ( int i = 0; i < planes; i++ )
|
|
{
|
|
planeheader = 0;
|
|
data = pdesign->Compress( i, &ulen, &clen );
|
|
if ( data == nullptr ) // compression error
|
|
{
|
|
return;
|
|
}
|
|
if ( ulen == 0 )
|
|
clen = 0;
|
|
planeheader |= ( ( mode << 4 ) << 24 );
|
|
planeheader |= ( ( i & 0xF ) << 24 );
|
|
planeheader |= ( ( ulen & 0xFF ) << 16 );
|
|
planeheader |= ( ( clen & 0xFF ) << 8 );
|
|
planeheader |= ( ( ( ulen >> 4 ) & 0xF0 ) | ( ( clen >> 8 ) & 0xF ) );
|
|
u32* p_planeheader = reinterpret_cast<u32*>( &( packet[buffer_len + data_offset] ) );
|
|
*p_planeheader = ctBEu32( planeheader );
|
|
buffer_len += 4;
|
|
memcpy( &( packet[buffer_len + data_offset] ), data, clen );
|
|
buffer_len += clen;
|
|
delete[] data;
|
|
}
|
|
msg->msglen = ctBEu16( static_cast<u16>( buffer_len ) + data_offset );
|
|
msg->planebuffer_len = ctBEu16( static_cast<u16>( buffer_len ) );
|
|
|
|
Core::networkManager.clientTransmit->AddToQueue( client, &packet[0], cfBEu16( msg->msglen ) );
|
|
stored_packet->swap( packet );
|
|
}
|
|
|
|
void CustomHousesSendFullToInRange( UHouse* house, int design, int range )
|
|
{
|
|
Core::WorldIterator<Core::OnlinePlayerFilter>::InRange(
|
|
house->pos(), range,
|
|
[&]( Mobile::Character* chr ) { CustomHousesSendFull( house, chr->client, design ); } );
|
|
}
|
|
|
|
void CustomHousesSendShort( UHouse* house, Network::Client* client )
|
|
{
|
|
Network::PktHelper::PacketOut<Network::PktOut_BF_Sub1D> msg;
|
|
msg->WriteFlipped<u16>( 13u );
|
|
msg->offset += 2;
|
|
msg->Write<u32>( house->serial_ext );
|
|
msg->WriteFlipped<u32>( house->revision );
|
|
msg.Send( client );
|
|
}
|
|
|
|
void UHouse::SetCustom( bool _custom )
|
|
{
|
|
if ( custom == false && _custom == true )
|
|
CustomHouseSetInitialState();
|
|
|
|
custom = _custom;
|
|
}
|
|
void UHouse::CustomHouseSetInitialState()
|
|
{
|
|
const MultiDef& def = multidef();
|
|
auto size = Core::Pos2d( 0, 0 ) + ( def.max_relp - def.min_relp ).xy() + Core::Vec2d( 1, 1 );
|
|
//+1 to include offset 0 in -3..3
|
|
size += Core::Vec2d( 0, 1 ); //+1 for front steps outside multidef footprint
|
|
Core::Vec2d xybase( (short)abs( def.min_relp.x() ), (short)abs( def.min_relp.y() ) );
|
|
CurrentDesign.Clear();
|
|
CurrentDesign.InitDesign( size, xybase );
|
|
WorkingDesign.Clear();
|
|
WorkingDesign.InitDesign( size, xybase );
|
|
BackupDesign.Clear();
|
|
BackupDesign.InitDesign( size, xybase );
|
|
|
|
CurrentDesign.AddMultiAtOffset( multiid, Core::Vec3d( 0, 0, 0 ) );
|
|
WorkingDesign = CurrentDesign;
|
|
BackupDesign = CurrentDesign;
|
|
std::vector<u8> newvec;
|
|
WorkingCompressed.swap( newvec );
|
|
|
|
std::vector<u8> newvec2;
|
|
CurrentCompressed.swap( newvec2 );
|
|
}
|
|
|
|
void UHouse::CustomHousesQuit( Mobile::Character* chr, bool drop_changes, bool send_pkts )
|
|
{
|
|
if ( drop_changes )
|
|
WorkingDesign = CurrentDesign;
|
|
else
|
|
{
|
|
CurrentDesign.FillComponents( this );
|
|
WorkingDesign.FillComponents( this, false ); // keep in sync
|
|
}
|
|
revision++;
|
|
std::vector<u8> newvec;
|
|
WorkingCompressed.swap( newvec );
|
|
|
|
std::vector<u8> newvec2;
|
|
CurrentCompressed.swap( newvec2 );
|
|
if ( chr )
|
|
{
|
|
CustomHouseStopEditing( chr, this, send_pkts );
|
|
if ( chr->client && send_pkts )
|
|
CustomHousesSendFull( this, chr->client, HOUSE_DESIGN_CURRENT );
|
|
if ( Core::gamestate.system_hooks.close_customhouse_hook )
|
|
{
|
|
Core::gamestate.system_hooks.close_customhouse_hook->call(
|
|
send_pkts ? make_mobileref( chr ) : new Module::EOfflineCharacterRefObjImp( chr ),
|
|
new Module::EMultiRefObjImp( this ) );
|
|
}
|
|
}
|
|
}
|
|
} // namespace Multi
|
|
} // namespace Pol
|