/* * psclientchar.cpp * * Copyright (C) 2002 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation (version 2 of the License) * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The client side version of char manager that talks with the server side * version. Used for things like inventory query's and such. * * Client version of character manager that talks to server version about * the details involved about a player. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "net/message.h" #include "net/messages.h" #include "net/msghandler.h" #include "net/charmessages.h" #include "engine/celbase.h" #include "engine/netpersist.h" // CLIENT INCLUDES #include "globals.h" #include "pscelclient.h" #include "psclientchar.h" #include "iclient/isoundmngr.h" #include "paws/pawsmanager.h" #include "paws/pawstextbox.h" #include "paws/pawsobjectview.h" #include "gui/inventorywindow.h" #include "gui/pawssummary.h" #include "gui/pawsspellcancelwindow.h" #include "gui/pawsinfowindow.h" #include "effects/pseffectmanager.h" #include "util/psmeshutil.h" #include "pscamera.h" //------------------------------------------------------------------------------ void Trait::Load( iDocumentNode* node ) { csString genderStr; name = node->GetAttributeValue( "name" ); int cstr_id_mesh = node->GetAttributeValueAsInt( "mesh" ); int cstr_id_texture = node->GetAttributeValueAsInt( "tex" ); int cstr_id_material = node->GetAttributeValueAsInt( "mat" ); genderStr = node->GetAttributeValue( "gender" ); location = ConvertTraitLocationString(node->GetAttributeValue( "loc" )); csString shaderColours = node->GetAttributeValue("shader"); if ( shaderColours.Length() > 0 ) sscanf( shaderColours.GetData(), "%f,%f,%f", &shader.x, &shader.y, &shader.z ); else shader = csVector3(0,0,0); texture = psengine->FindCommonString(cstr_id_texture); material = psengine->FindCommonString(cstr_id_material); mesh = psengine->FindCommonString(cstr_id_mesh); if (genderStr == "") genderStr = "N"; if (genderStr != "F" && genderStr != "M" && genderStr != "N") { Error2("%s isn't a legal gender. Use (F,M,N)",genderStr.GetData()); genderStr = "N"; } if (genderStr == "F") { gender = PSCHARACTER_GENDER_FEMALE; } else if (genderStr == "M") { gender = PSCHARACTER_GENDER_MALE; } else { gender = PSCHARACTER_GENDER_NONE; } csRef idNode = node->GetAttribute("id"); if ( idNode.IsValid()) uid = idNode->GetValueAsInt(); else uid = 0; csRef nextNode = node->GetAttribute("next"); if ( nextNode.IsValid()) next_trait_uid = nextNode->GetValueAsInt(); else next_trait_uid = 0; csRef raceNode = node->GetAttribute("race"); if ( raceNode.IsValid()) raceID = raceNode->GetValueAsInt(); else raceID = 0; next_trait = NULL; prev_trait = NULL; } //------------------------------------------------------------------------------ PSTRAIT_LOCATION Trait::ConvertTraitLocationString(const char *locationstring) { if (locationstring==NULL) return PSTRAIT_LOCATION_NONE; if (!strcasecmp(locationstring,"HAIR_STYLE")) return PSTRAIT_LOCATION_HAIR_STYLE; if (!strcasecmp(locationstring,"HAIR_COLOR")) return PSTRAIT_LOCATION_HAIR_COLOR; if (!strcasecmp(locationstring,"BEARD_STYLE")) return PSTRAIT_LOCATION_BEARD_STYLE; if (!strcasecmp(locationstring,"FACE")) return PSTRAIT_LOCATION_FACE; if (!strcasecmp(locationstring,"SKIN_TONE")) return PSTRAIT_LOCATION_SKIN_TONE; if (!strcasecmp(locationstring,"ITEM")) return PSTRAIT_LOCATION_ITEM; return PSTRAIT_LOCATION_NONE; } //------------------------------------------------------------------------------ psClientCharManager::psClientCharManager(iObjectRegistry *objectreg) { ready = true; objectReg = objectreg; vfs = CS_QUERY_REGISTRY(objectReg, iVFS); charCreation = 0; target = 0; targetEffect = 0; } psClientCharManager::~psClientCharManager() { if (msghandler) { msghandler->Unsubscribe(this, MSGTYPE_CHARREJECT); msghandler->Unsubscribe(this, MSGTYPE_EQUIPMENT); msghandler->Unsubscribe(this, MSGTYPE_EFFECT); msghandler->Unsubscribe(this, MSGTYPE_EFFECT_STOP); msghandler->Unsubscribe(this, MSGTYPE_PLAYSOUND); msghandler->Unsubscribe(this, MSGTYPE_USERACTION); msghandler->Unsubscribe(this, MSGTYPE_GUITARGETUPDATE); } delete charCreation; } bool psClientCharManager::Initialize( MsgHandler* msgHandler, psCelClient* GEMSupervisor) { msghandler = msgHandler; cel = GEMSupervisor; if ( !msghandler->Subscribe(this, MSGTYPE_CHARREJECT) ) return false; if ( !msghandler->Subscribe(this, MSGTYPE_EQUIPMENT) ) return false; if ( !msghandler->Subscribe(this, MSGTYPE_EFFECT) ) return false; if ( !msghandler->Subscribe(this, MSGTYPE_EFFECT_STOP) ) return false; if ( !msghandler->Subscribe(this, MSGTYPE_PLAYSOUND) ) return false; if (!msghandler->Subscribe(this, MSGTYPE_USERACTION) ) return false; if (!msghandler->Subscribe(this, MSGTYPE_GUITARGETUPDATE) ) return false; if (!msghandler->Subscribe(this, MSGTYPE_CHANGE_TRAIT) ) return false; charCreation = new psCreationManager( objectReg ); return true; } void psClientCharManager::HandleMessage ( MsgEntry* me ) { switch ( me->GetType() ) { case MSGTYPE_CHANGE_TRAIT: { ChangeTrait(me); break; } case MSGTYPE_USERACTION: { HandleAction( me ); break; } case MSGTYPE_CHARREJECT: { HandleRejectCharMessage( me ); break; } case MSGTYPE_EQUIPMENT: { HandleEquipment(me); return; } case MSGTYPE_EFFECT: { HandleEffect(me); return; } case MSGTYPE_EFFECT_STOP: { HandleEffectStop(me); return; } case MSGTYPE_PLAYSOUND: { if(psengine->GetSoundStatus()) { HandlePlaySound(me); } return; } case MSGTYPE_GUITARGETUPDATE: { HandleTargetUpdate(me); return; } default: { ready = true; return; } } } void psClientCharManager::ChangeTrait( MsgEntry* me ) { psTraitChangeMessage mesg(me); unsigned int objectID = mesg.target; GEMClientObject* object = (GEMClientObject *)psengine->GetCelClient()->FindObject(objectID); if (!object) { Error2("Got trait change for object %d, but couldn't find it!", objectID); return; } // Update main object psengine->BuildAppearance( object->pcmesh->GetMesh(), mesg.string ); // Update any doll views registered for changes csArray dolls = PawsManager::GetSingleton().ListSubscribers("sigActorUpdate"); for (size_t i=0; i(dolls[i]); if (doll == NULL) continue; if (doll->GetID() == objectID) // This is a doll of the updated object { iMeshWrapper* dollObject = doll->GetObject(); if (dollObject == NULL) { Error2("Cannot update registered doll view with ID %d because it has no object", doll->GetID()); continue; } psengine->BuildAppearance( dollObject, mesg.string ); } } } void psClientCharManager::SetTarget(GEMClientObject * newTarget, char * action, bool notifyServer) { if (target != newTarget) { target = newTarget; PawsManager::GetSingleton().Publish("sTargetName",target?target->GetName():"" ); if (!target) PawsManager::GetSingleton().Publish("fVitalValue0:Target",0); } // delete the old target effect psengine->GetEffectManager()->DeleteEffect(targetEffect); targetEffect = 0; PS_ID mappedID = 0; // Action locations don't have effects if (target && target->GetObjectType() != GEM_ACTION_LOC) { // render the target effect iMeshWrapper * targetMesh = target->pcmesh->GetMesh(); if (targetMesh) targetEffect = psengine->GetEffectManager()->RenderEffect("target", csVector3(0,0,0), targetMesh); // notify the server of selection mappedID = target->GetEntity()->GetID(); } // if it's a message sent by server, there is no need to resend back the same information if (notifyServer) { psUserActionMessage userAction(0, mappedID, action); userAction.SendMessage(); } } void psClientCharManager::HandleAction( MsgEntry* me ) { psUserActionMessage action(me); GEMClientActor* actor = dynamic_cast(cel->FindObject(action.target)); if (actor) { if ( !actor->SetAnimation(action.action) ) Error2("Error playing animation %s!", action.action.GetData() ); } else Error1("Received psUserActionMessage for invalid object."); } void psClientCharManager::HandlePlaySound( MsgEntry* me ) { csRef soundmanager = psengine->GetSoundManager(); if (soundmanager) { soundmanager->StartActionsSound( me->GetStr() ); } } void psClientCharManager::HandleTargetUpdate( MsgEntry* me ) { psGUITargetUpdateMessage targetMsg(me); GEMClientObject* object = cel->FindObject( targetMsg.targetID ); if ( object ) SetTarget( object, "select", false); else Error1("Received TagetUpdateMessage with invalid target."); } void psClientCharManager::Equip( iMeshWrapper* object, csString& slotname, csString& mesh, csString& part, csString& partMesh, csString& texture ) { psMeshUtil * meshutil = psengine->GetMeshUtil(); if ( mesh.Length() ) { meshutil->Attach( object, slotname, mesh ); } // This is a part mesh (ie Mesh) change so change the mesh for that part. if ( partMesh.Length() ) { meshutil->ChangeMesh( object, part, partMesh ); if ( texture.Length() ) { meshutil->ChangeMaterial( object, meshutil->ParseStrings(object,part,partMesh), texture, texture ); } } else { if ( part.Length() ) { meshutil->ChangeMaterial( object, part, texture, texture ); } } } void psClientCharManager::Dequip( iMeshWrapper* object, csString& slotname, csString& mesh, csString& part, csString& partMesh, csString& mat, csString& txt ) { psMeshUtil * meshutil = psengine->GetMeshUtil(); if ( mesh.Length() ) { meshutil->Detach( object, slotname ); } // This is a part mesh (ie Mesh) set default mesh for that part. if ( partMesh.Length() ) { meshutil->DefaultMesh( object, part ); } if ( part.Length() ) { if ( mat.Length() ) { meshutil->ChangeMaterial( object, part, mat, txt); } else { meshutil->DefaultMaterial( object, part ); } } } void psClientCharManager::Equip( iMeshWrapper * object, psEquipmentMessage * equip, csString slotname, csPDelArray& list) { if ( equip->type == psEquipmentMessage::EQUIP ) { Equip(object,slotname,equip->mesh,equip->part,equip->partMesh,equip->texture); } else { csString mat(""); csString txt(""); for (size_t z = 0; z < list.Length(); z++ ) { if ( list[z]->mesh == equip->part ) { mat = list[z]->material; txt = list[z]->texture; } } Dequip(object,slotname,equip->mesh,equip->part,equip->partMesh,mat,txt); } } void psClientCharManager::HandleEquipment( MsgEntry* me ) { psEquipmentMessage equip( me ); unsigned int playerID = equip.player; GEMClientActor* object = (GEMClientActor*)cel->FindObject( playerID ); if (!object) { Error2("Got equipment for actor %d, but couldn't find it!", playerID); return; } const char* slotname = psengine->slotName.GetName(equip.slot); if ( equip.slot == PSCHARACTER_SLOT_HEAD ) { psString result(equip.mesh); result.ReplaceAllSubString("$H",object->helmGroup); equip.mesh = result; } // Update the actor Equip(object->pcmesh->GetMesh(), &equip, slotname, object->traitList); // Update any doll views registered for changes csArray dolls = PawsManager::GetSingleton().ListSubscribers("sigActorUpdate"); for (size_t i=0; i(dolls[i]); if (doll == NULL) continue; if (doll->GetID() == playerID) // This is a doll of the updated object { iMeshWrapper* dollObject = doll->GetObject(); if (dollObject == NULL) { Error2("Cannot update registered doll view with ID %d because it has no object", doll->GetID()); continue; } Equip(dollObject, &equip, slotname, object->traitList); } } } void psClientCharManager::HandleEffectStop(MsgEntry* me) { psStopEffectMessage effect(me); if (psengine->GetEffectManager ()) { // This code work as long as PutUnique is used to enter // new entries to the hash. unsigned int effectID = effectMapper.Get(effect.uid,0); psengine->GetEffectManager()->DeleteEffect(effectID); effectMapper.DeleteAll(effect.uid); } } void psClientCharManager::HandleEffect( MsgEntry* me ) { psEffectMessage effect(me); // get the anchor GEMClientObject* gemAnchor = cel->FindObject(effect.anchorID); iMeshWrapper * anchor = 0; if (!gemAnchor && effect.anchorID != 0) { // The entity isn't here, probably because it's too far away, so don't render the effect. // Not sure if we should do anything here, but meh. } else { if (gemAnchor) anchor = gemAnchor->pcmesh->GetMesh(); // get the target GEMClientObject* gemTarget = cel->FindObject(effect.targetID); iMeshWrapper *target = anchor; if (gemTarget) target = gemTarget->pcmesh->GetMesh(); // render the actual effect if (psengine->GetEffectManager ()) { unsigned int effectID = 0; unsigned int uniqueIDOverride = effectMapper.Get(effect.uid, 0); // Will return 0 if no uid in store or uid is 0. csVector3 up(0,1,0); if (anchor) { effectID = psengine->GetEffectManager()->RenderEffect(effect.name, effect.offset, anchor, target,up,uniqueIDOverride); } else { iSector * sector = psengine->GetPSCamera()->GetICamera()->GetSector(); // Sector should come in the message effectID = psengine->GetEffectManager()->RenderEffect(effect.name, sector, effect.offset, target, up,uniqueIDOverride); } if (effectID == 0) { Error2("Failed to render effect %s",effect.name.GetDataSafe()); } if ( effect.uid != 0 ) { effectMapper.PutUnique( effect.uid, effectID ); } } // if this is a spell effect, if (effect.castDuration > 0) { //and it's anchored to the main actor then the you must be casting the spell if ( effect.anchorID == psengine->GetCelClient()->GetMainActor()->GetID() ) { // show the spell cancel window pawsSpellCancelWindow* widget = (pawsSpellCancelWindow *)PawsManager::GetSingleton().FindWidget( "SpellCancelWindow" ); if (widget) { widget->Start(effect.castDuration); } } // start the spell animation GEMClientActor* actor = (GEMClientActor*)gemAnchor; if ( actor ) { actor->SetAnimation( "cast" ); } } } } void psClientCharManager::HandleRejectCharMessage( MsgEntry* me ) { psCharRejectedMessage reject( me ); switch ( reject.errorType ) { case psCharRejectedMessage::NON_LEGAL_NAME: case psCharRejectedMessage::NON_UNIQUE_NAME: case psCharRejectedMessage::RESERVED_NAME: case psCharRejectedMessage::INVALID_CREATION: { PawsManager::GetSingleton().CreateWarningBox( reject.errorMesg ); PawsManager::GetSingleton().FindWidget("CharCreateMain")->Show(); PawsManager::GetSingleton().FindWidget("Summary")->Hide(); break; } } } //------------------------------------------------------------------------------ psCreationManager::psCreationManager( iObjectRegistry* objReg ) { objectReg = objReg; LoadRaceInformation(); msgHandler = psengine->GetMsgHandler(); msgHandler->Subscribe(this, MSGTYPE_CHAR_CREATE_CP); msgHandler->Subscribe(this, MSGTYPE_CHAR_CREATE_PARENTS); msgHandler->Subscribe(this, MSGTYPE_CHAR_CREATE_CHILDHOOD); msgHandler->Subscribe( this, MSGTYPE_CHAR_CREATE_UPLOAD ); msgHandler->Subscribe( this, MSGTYPE_CHAR_CREATE_VERIFY ); msgHandler->Subscribe( this, MSGTYPE_CHAR_CREATE_LIFEEVENTS ); msgHandler->Subscribe( this, MSGTYPE_CHAR_CREATE_TRAITS ); hasParentData = false; hasChildhoodData = false; hasLifeEventData = false; hasTraitData = false; selectedRace = -1; selectedFace = -1; selectedHairStyle = -1; selectedBeardStyle = -1; selectedHairColour = -1; selectedSkinColour = -1; selectedGender = PSCHARACTER_GENDER_MALE; fatherMod = 1; motherMod = 1; nameGenerator = new NameGenerationSystem(); nameGenerator->LoadDatabase( objReg ); ClearChoices(); } psCreationManager::~psCreationManager() { msgHandler->Unsubscribe( this, MSGTYPE_CHAR_CREATE_UPLOAD ); msgHandler->Unsubscribe( this, MSGTYPE_CHAR_CREATE_LIFEEVENTS ); msgHandler->Unsubscribe( this, MSGTYPE_CHAR_CREATE_VERIFY ); delete nameGenerator; } int psCreationManager::GetRaceCP( int race ) { if ( raceDescriptions[race]->startingCP == -1 ) { // This is a simple message so don't need a seperate class for it. // We can just create the message our selves. MsgEntry *msg = new MsgEntry( 100 ); msg->SetType(MSGTYPE_CHAR_CREATE_CP); msg->Add( (int32_t) race ); msg->ClipToCurrentSize(); msgHandler->SendMessage(msg); return REQUESTING_CP; } else { return raceDescriptions[race]->startingCP; } } void psCreationManager::HandleMessage( MsgEntry* me ) { switch ( me->GetType() ) { case MSGTYPE_CHAR_CREATE_CP: { int race = me->GetInt32(); int points = me->GetInt32(); raceDescriptions[race]->startingCP = points; return; } case MSGTYPE_CHAR_CREATE_PARENTS: { HandleParentsData( me ); hasParentData = true; break; } case MSGTYPE_CHAR_CREATE_CHILDHOOD: { HandleChildhoodData( me ); hasChildhoodData = true; break; } case MSGTYPE_CHAR_CREATE_LIFEEVENTS: { HandleLifeEventData( me ); hasLifeEventData = true; break; } case MSGTYPE_CHAR_CREATE_TRAITS: { HandleTraitData( me ); hasTraitData = true; break; } case MSGTYPE_CHAR_CREATE_VERIFY: { HandleVerify( me ); break; } case MSGTYPE_CHAR_CREATE_UPLOAD: { pawsWidget* wdg = PawsManager::GetSingleton().FindWidget("Summary"); if (wdg) wdg->Hide(); psengine->StartLoad(); break; } } } void psCreationManager::HandleLifeEventData( MsgEntry* me ) { psLifeEventMsg incomming( me ); lifeEventData.SetLength( incomming.choices.Length() ); for ( size_t x = 0; x < incomming.choices.Length(); x++ ) { lifeEventData[x].id = incomming.choices[x]->id; lifeEventData[x].name = incomming.choices[x]->name; lifeEventData[x].description = incomming.choices[x]->description; lifeEventData[x].common = incomming.choices[x]->common; lifeEventData[x].cpCost = incomming.choices[x]->cpCost; incomming.choices[x]->adds.TransferTo( lifeEventData[x].adds ); incomming.choices[x]->removes.TransferTo( lifeEventData[x].removes ); } } void psCreationManager::HandleTraitData( MsgEntry* me ) { psCharCreateTraitsMessage msg(me); iDocumentSystem* xml = psengine->GetXMLParser (); csRef doc = xml->CreateDocument(); const char* error = doc->Parse(msg.GetString().GetData()); if ( error ) { Error2("Error in XML: %s", error ); return; } csRef root = doc->GetRoot(); if(!root) { Error1("No XML root in Trait Data"); return; } csRef iter1 = root->GetNode("traits")->GetNodes("trait"); // Build the traits list while ( iter1->HasNext() ) { csRef node = iter1->Next(); Trait *t = new Trait; t->Load( node ); traits.Push(t); } TraitIterator iter2 = GetTraitIterator(); while ( iter2.HasNext() ) { Trait * t = iter2.Next(); t->next_trait = GetTrait(t->next_trait_uid); if (t->next_trait != NULL) { t->next_trait->prev_trait = t; } } // Insert into the custom location structure all top trait TraitIterator iter3 = GetTraitIterator(); while ( iter3.HasNext() ) { Trait * t = iter3.Next(); // Check for top trait if (t->prev_trait == NULL) { RaceDefinition * race = GetRace(t->raceID); if (race != NULL) { if (t->gender == PSCHARACTER_GENDER_NONE) { race->location[t->location][PSCHARACTER_GENDER_MALE].Push(t); race->location[t->location][PSCHARACTER_GENDER_FEMALE].Push(t); } else { race->location[t->location][t->gender].Push(t); } } else { Error3("Failed to insert trait '%s' into location table for race %d.\n",t->name.GetData(),t->raceID); } } } } Trait* psCreationManager::GetTrait(unsigned int uid) { TraitIterator tIter = GetTraitIterator(); while ( tIter.HasNext() ) { Trait * t = tIter.Next(); if (t->uid == uid) return t; } return NULL; } void psCreationManager::HandleVerify( MsgEntry* me ) { psCharVerificationMesg mesg(me); pawsSummaryWindow * window = (pawsSummaryWindow*)PawsManager::GetSingleton().FindWidget("Summary"); if ( window ) window->SetVerify( mesg.stats, mesg.skills ); } void psCreationManager::HandleChildhoodData( MsgEntry* me ) { psCreationChoiceMsg incomming( me ); // Simple create a copy of the data in the message. incomming.choices.TransferTo( childhoodData ); } void psCreationManager::HandleParentsData( MsgEntry* me ) { psCreationChoiceMsg incomming( me ); // Simple create a copy of the data in the message. incomming.choices.TransferTo( parentData ); } void psCreationManager::LoadRaceInformation() { iDocumentSystem* xml = psengine->GetXMLParser (); iVFS* vfs = psengine->GetVFS (); csRef buff = vfs->ReadFile( "/this/data/races/descriptions.xml" ); if ( !buff || !buff->GetSize() ) { Error2( "Could not load XML: %s", "descriptions.xml" ); return; } csRef doc = xml->CreateDocument(); const char* error = doc->Parse( buff ); if ( error ) { Error3( "Error parsing XML file %s: %s", "descriptions.xml", error ); return; } csRef root = doc->GetRoot(); if(!root) { Error1("No XML root in descriptions.xml"); return; } csRef topNode = root->GetNode("descriptions"); if(!topNode) { Error1("No tag in descriptions.xml"); return; } csRef iter = topNode->GetNodes(); while ( iter->HasNext() ) { csRef node = iter->Next(); csString raceName = node->GetAttributeValue( "name" ); csString description = node->GetAttributeValue( "description" ); csString maleModel = node->GetAttributeValue( "male" ); csString femaleModel = node->GetAttributeValue( "female" ); bool mavailable = node->GetAttributeValueAsBool("male_available"); bool favailable = node->GetAttributeValueAsBool("female_available"); RaceDefinition* race = new RaceDefinition; race->name = raceName; race->description = description; race->startingCP = -1; race->femaleModelName = femaleModel; race->maleModelName = maleModel; race->femaleAvailable = favailable; race->maleAvailable = mavailable; // defaults race->FollowPos.Set(0,3,4); race->LookatPos.Set(0,2,0); race->FirstPos.Set(0,1.5,-.25); csRef fp = node->GetNode("FollowPos"); if (fp) race->FollowPos.Set(fp->GetAttributeValueAsFloat("x"),fp->GetAttributeValueAsFloat("y"),fp->GetAttributeValueAsFloat("z")); fp = node->GetNode("LookatPos"); if (fp) race->LookatPos.Set(fp->GetAttributeValueAsFloat("x"),fp->GetAttributeValueAsFloat("y"),fp->GetAttributeValueAsFloat("z")); fp = node->GetNode("FirstPos"); if (fp) race->FirstPos.Set(fp->GetAttributeValueAsFloat("x"),fp->GetAttributeValueAsFloat("y"),fp->GetAttributeValueAsFloat("z")); // Load the default camera position in the custom carachter generator fp = node->GetNode("ViewerPos"); if (fp) race->ViewerPos.Set(fp->GetAttributeValueAsFloat("x"),fp->GetAttributeValueAsFloat("y"),fp->GetAttributeValueAsFloat("z")); for ( int z = 0; z < PSTRAIT_LOCATION_COUNT; z++ ) { race->zoomLocations[z].Set( 0,0,0 ); } fp = node->GetNode("FACEPOS"); if (fp) race->zoomLocations[PSTRAIT_LOCATION_FACE].Set(fp->GetAttributeValueAsFloat("x"),fp->GetAttributeValueAsFloat("y"),fp->GetAttributeValueAsFloat("z")); raceDescriptions.Push( race ); } } void psCreationManager::LoadPathInfo() { iDocumentSystem* xml = psengine->GetXMLParser (); iVFS* vfs = psengine->GetVFS (); csRef buff = vfs->ReadFile( "/this/data/races/quickpaths.xml" ); if ( !buff || !buff->GetSize() ) { Error2( "Could not load XML: %s", "quickpaths.xml" ); return; } csRef doc = xml->CreateDocument(); const char* error = doc->Parse( buff ); if ( error ) { Error3( "Error parsing XML file %s: %s", "quickpaths.xml", error ); return; } csRef root = doc->GetRoot(); if(!root) { Error1("No XML root in quickpaths.xml"); return; } csRef topNode = root->GetNode("paths"); if(!topNode) { Error1("No tag in quickpaths.xml"); return; } csRef iter = topNode->GetNodes(); while ( iter->HasNext() ) { csRef node = iter->Next(); PathDefinition* path = new PathDefinition(); path->name = node->GetAttributeValue("name"); if ( path->name == "" ) path->name = "None"; path->info = node->GetAttributeValue("info"); if ( path->info == "" ) path->info = "None"; //Load in all of the stat bonuses csRef iter = node->GetNodes("StatBonus"); while (iter->HasNext()) { csRef tmp = iter->Next(); PathDefinition::Bonus* bonus = new PathDefinition::Bonus(); csString name = tmp->GetAttributeValue("name"); float value = tmp->GetAttributeValueAsFloat("value"); bonus->name = name; bonus->value = value; path->statBonuses.Push(bonus); } //Load in all of the bonus skills iter = node->GetNodes("SkillBonus"); while (iter->HasNext()) { csRef tmp = iter->Next(); PathDefinition::Bonus* bonus = new PathDefinition::Bonus(); csString skillName = tmp->GetAttributeValue("name"); float rank = tmp->GetAttributeValueAsFloat("rank"); bonus->name = skillName; bonus->value = rank; path->skillBonuses.Push(bonus); } pathDefinitions.Push(path); } } const char* psCreationManager::GetRaceDescription( int race ) { if ( (size_t)race < raceDescriptions.Length() ) return raceDescriptions[race]->description; else return "None"; } const char* psCreationManager::GetModelName( int race, int gender ) { if ( (size_t)race < raceDescriptions.Length() && race >= 0 ) { if ( gender == PSCHARACTER_GENDER_NONE || gender == PSCHARACTER_GENDER_MALE ) { return raceDescriptions[race]->maleModelName; } else { return raceDescriptions[race]->femaleModelName; } } return NULL; } void psCreationManager::GetParentData() { if ( !hasParentData ) { psCreationChoiceMsg outgoing(MSGTYPE_CHAR_CREATE_PARENTS); outgoing.SendMessage(); } } void psCreationManager::GetChildhoodData() { if ( !hasChildhoodData ) { psCreationChoiceMsg outgoing(MSGTYPE_CHAR_CREATE_CHILDHOOD); outgoing.SendMessage(); } } void psCreationManager::GetLifeEventData() { if ( !hasLifeEventData ) { psLifeEventMsg outgoing; outgoing.SendMessage(); } } void psCreationManager::GetTraitData() { if ( !hasTraitData ) { psCreationChoiceMsg outgoing(MSGTYPE_CHAR_CREATE_TRAITS); outgoing.SendMessage(); } } RaceDefinition* psCreationManager::GetRace( int race ) { return raceDescriptions[race]; } const char* psCreationManager::GetDescription( int id ) { size_t x; for ( x = 0; x < parentData.Length(); x++ ) { if ( parentData[x].id == id ) return parentData[x].description; } for ( x = 0; x < childhoodData.Length(); x++ ) { if ( childhoodData[x].id == id ) return childhoodData[x].description; } return "None"; } CreationChoice* psCreationManager::GetChoice( int id ) { size_t x; for ( x = 0; x < parentData.Length(); x++ ) { if ( parentData[x].id == id ) return &parentData[x]; } for ( x = 0; x < childhoodData.Length(); x++ ) { if ( childhoodData[x].id == id ) return &childhoodData[x]; } return NULL; } void psCreationManager::SetRace( int newRace ) { selectedRace = newRace; } void psCreationManager::UploadChar( bool verify ) { RaceDefinition* race = GetRace( selectedRace ); if ( !race ) return; csString firstname; csString lastname; // Separate names firstname = selectedName.Slice(0,selectedName.FindFirst(' ')); if (selectedName.FindFirst(' ') != SIZET_NOT_FOUND) lastname = selectedName.Slice(selectedName.FindFirst(' ')+1,selectedName.Length()); else lastname = ""; // Make BIO pawsSummaryWindow* summary = (pawsSummaryWindow*)PawsManager::GetSingleton().FindWidget("Summary"); pawsMultiLineTextBox* btext = (pawsMultiLineTextBox*) summary->FindWidget("text_birth"); pawsMultiLineTextBox* ltext = (pawsMultiLineTextBox*) summary->FindWidget("text_life"); csString bio; bio += btext->GetText(); bio += ltext->GetText(); psCharUploadMessage upload( verify, firstname.GetDataSafe(), lastname.GetDataSafe(), selectedRace, selectedGender, choicesMade, motherMod, fatherMod, lifeEventsMade, selectedFace, selectedHairStyle, selectedBeardStyle, selectedHairColour, selectedSkinColour, bio.GetDataSafe(), path.GetDataSafe() ); upload.SendMessage(); } void psCreationManager::SetName( const char* newName ) { selectedName.Replace( newName ); } csString psCreationManager::GetName() { return selectedName; } LifeEventChoice* psCreationManager::FindLifeEvent( int idNumber ) { for ( size_t x = 0; x < lifeEventData.Length(); x++ ) { if ( lifeEventData[x].id == idNumber ) return &lifeEventData[x]; } return NULL; } void psCreationManager::AddChoice( int choice, int modifier ) { if(!GetChoice(choice)) { Error2("Invalid creation choice: %i", choice); return; } choicesMade.Push( choice ); currentCP-=GetCost( choice )*modifier; } void psCreationManager::RemoveChoice( uint32_t choice, int modifier ) { for ( size_t x = 0; x < choicesMade.Length(); x++ ) { if ( choicesMade[x] == choice ) { currentCP += GetCost( choicesMade[x] )*modifier; choicesMade.DeleteIndex(x); return; } } } void psCreationManager::AddLifeEvent( int event ) { lifeEventsMade.Push( event ); currentCP -= GetLifeCost( event ); } void psCreationManager::RemoveLifeEvent( uint32_t event ) { for ( size_t x = 0; x < lifeEventsMade.Length(); x++ ) { if ( lifeEventsMade[x] == event ) { currentCP += GetLifeCost( event ); lifeEventsMade.DeleteIndex(x); return; } } } int psCreationManager::GetCost( int id ) { size_t x; for ( x = 0; x < parentData.Length(); x++ ) { if ( parentData[x].id == id ) return parentData[x].cpCost; } for ( x = 0; x < childhoodData.Length(); x++ ) { if ( childhoodData[x].id == id ) return childhoodData[x].cpCost; } return -1; } int psCreationManager::GetLifeCost( int id ) { LifeEventChoice* life = FindLifeEvent( id ); if ( life != NULL ) return life->cpCost; return 0; } void psCreationManager::SetCustomization( int face, int hairStyle, int beardStyle, int hairColour, int skinColour ) { selectedFace = face; selectedHairStyle = hairStyle; selectedBeardStyle = beardStyle; selectedHairColour = hairColour; selectedSkinColour = skinColour; } RaceDefinition* psCreationManager::GetRace( const char* name ) { for ( size_t i = 0; i < raceDescriptions.Length(); i++ ) { if ( raceDescriptions[i]->name == name ) return raceDescriptions[i]; } return NULL; } csPDelArray::Iterator psCreationManager::GetPathIterator() { if (pathDefinitions.Length() == 0) LoadPathInfo(); return pathDefinitions.GetIterator(); } PathDefinition* psCreationManager::GetPath(int i) { return pathDefinitions[i]; } const char* psCreationManager::GetLifeEventDescription( int id ) { for ( size_t n = 0; n < lifeEventData.Length(); n++ ) { if ( lifeEventData[n].id == id ) { return lifeEventData[n].description; } } return "None"; } bool psCreationManager::IsAvailable(int id, int gender) { RaceDefinition* race = GetRace(id); if (!race) return false; if (gender==PSCHARACTER_GENDER_NONE) return race->maleAvailable; //If neutral gender, check if male gender is available else if (gender==PSCHARACTER_GENDER_FEMALE) return race->femaleAvailable; else if (gender==PSCHARACTER_GENDER_MALE) return race->maleAvailable; return false; } void psCreationManager::ClearChoices() { if (selectedRace != -1) { currentCP = GetRaceCP( selectedRace ); path = "None"; } choicesMade.DeleteAll(); fatherMod = 1; motherMod = 1; lifeEventsMade.DeleteAll(); }