Fix css style order when using external css files
[ryzomcore.git] / ryzom / client / src / misc.cpp
blob0432abf1a50a1bd5dda2f514dc0bdabd516c1bb8
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 /////////////
23 // INCLUDE //
24 /////////////
25 #include "stdpch.h"
26 // Client
27 #include "misc.h"
28 #include "entity_animation_manager.h"
29 #include "entities.h"
30 #include "sheet_manager.h"
31 #include "interface_v3/interface_manager.h"
32 #include "continent_manager.h"
33 #include "world_database_manager.h"
34 #include "client_cfg.h"
35 #include "user_entity.h"
36 #include "net_manager.h"
37 #include "sky_render.h"
38 // Client Sheets
39 #include "client_sheets/item_sheet.h"
40 // 3D
41 #include "nel/3d/u_instance.h"
42 #include "nel/3d/u_instance_material.h"
43 #include "nel/3d/u_scene.h"
44 #include "nel/3d/u_driver.h"
45 #include "nel/3d/material.h"
46 // Misc
47 #include "nel/misc/file.h"
48 #include "nel/misc/i_xml.h"
49 #include "nel/misc/o_xml.h"
50 #include "nel/misc/fast_floor.h"
51 #include "nel/misc/noise_value.h"
52 #include "nel/misc/bitmap.h"
53 #include "nel/misc/system_utils.h"
54 // Game Share
55 #include "game_share/player_visual_properties.h"
56 #include "game_share/seeds.h"
57 // Georges
58 #include "nel/georges/u_form.h"
59 #include "nel/georges/u_form_elm.h"
60 #include "nel/georges/u_form_loader.h"
62 ///////////
63 // USING //
64 ///////////
65 using namespace NL3D;
66 using namespace NLMISC;
67 using namespace std;
68 using namespace NLGEORGES;
70 ////////////
71 // EXTERN //
72 ////////////
73 extern CEntityAnimationManager *EAM;
74 extern UDriver *Driver;
75 extern UMaterial GenericMat;
78 ////////////
79 // GLOBAL //
80 ////////////
81 const uint RecordVersion = 1;
82 std::set<std::string> LodCharactersNotFound;
85 //////////////
86 // FUNCTION //
87 //////////////
88 //-----------------------------------------------
89 // keepIn_NegPi_Pi :
90 //-----------------------------------------------
91 double keepIn_NegPi_Pi(double angle)
93 if(angle > Pi)
94 angle = -2*Pi+angle;
95 else if(angle < -Pi)
96 angle = 2*Pi+angle;
98 return angle;
99 }// keepIn_NegPi_Pi //
101 //-----------------------------------------------
102 // angleBetween2Vect :
103 //-----------------------------------------------
104 double angleBetween2Vect(const CVectorD &from, const CVectorD &to)
106 // Get the final local head Yaw.
107 CVector vj = from;
108 vj.z = 0;
109 CVector vk(0,0,1);
110 CVector vi = vj^vk;
112 CMatrix mat;
113 mat.setRot(vi,vj,vk,true);
115 CVector localDir = mat.inverted() * to;
116 return atan2(-localDir.x, localDir.y);
117 }// angleBetween2Vect //
119 //-----------------------------------------------
120 // makeUp :
121 // \param face : pointer on the face to make up (must not be null).
122 // \param idMakeUp : index of the make-up to apply.
123 // \warning This function does not check if 'face' is valid.
124 //-----------------------------------------------
125 void makeUp(NL3D::UInstance face, sint idMakeUp)
127 static const char *tattooStr = "visage_makeup";
129 // look for tattoo texture
130 uint numMat = face.getNumMaterials();
131 std::string texFilename;
132 for(uint k = 0; k < numMat; ++k)
134 UInstanceMaterial im = face.getMaterial(k);
135 sint numTex = im.getLastTextureStage();
136 for(sint l = 0; l <= numTex; ++l)
138 if (im.isTextureFile(l)) // one texture from a file ?
140 // see if it is the texture used for tattoos
141 texFilename = im.getTextureFileName(l);
142 // nlinfo("visage tex = %s", texFilename.c_str());
143 std::string::size_type pos = texFilename.find(tattooStr, 0);
144 if (pos != std::string::npos)
146 uint charIndex = (uint)(pos + strlen(tattooStr));
147 if (texFilename.length() >= charIndex + 2)
149 texFilename[charIndex] = '0' + (unsigned char) (idMakeUp / 10);
150 texFilename[charIndex + 1] = '0' + (unsigned char) (idMakeUp % 10);
151 im.setTextureFileName(texFilename, l);
157 }// makeUp //
159 //-----------------------------------------------
160 // qStart is the quaterion at t=0,
161 // qEnd is the quaterion at t=TAnimEnd,
162 // time is between 0 and 1. (1=> TAnimEnd)
163 //-----------------------------------------------
164 CQuat applyRotationFactor(CQuat in, float rotFactor, CQuat qStart, CQuat qEnd, float time)
166 H_AUTO ( RZ_Client_Apply_Rotation_Factor )
168 CQuat qRotTotal1, qRotTotal2;
169 qStart.invert();
170 qRotTotal1= qEnd*qStart;
171 // qRotTotal2.makeClosest(CQuat::Identity);
172 qRotTotal2= CQuat::slerp(CQuat::Identity, qRotTotal1, rotFactor);
174 // apply animation factor
175 // qRotTotal1.makeClosest(CQuat::Identity);
176 // qRotTotal2.makeClosest(CQuat::Identity);
177 CQuat qRotDelta1= CQuat::slerp(CQuat::Identity, qRotTotal1, time);
178 CQuat qRotDelta2= CQuat::slerp(CQuat::Identity, qRotTotal2, time);
180 // remove normal rotation, and apply rotFactor-ed one.
181 qRotDelta1.invert();
182 return qRotDelta2 * qRotDelta1 * in;
183 }// applyRotationFactor //
186 //-----------------------------------------------
187 // computeShortestAngle :
188 // Compute the angle between a source and a destination using the shortest way.
189 // \param from : angle between -Pi and Pi;
190 // \param to : angle between -Pi and Pi;
191 //-----------------------------------------------
192 double computeShortestAngle(double from, double to)
194 double difAngle = to - from;
195 if(difAngle < -Pi) // Angle in the wrong direction.
196 difAngle += 2.0*Pi;
197 else if(difAngle > Pi) // Angle in the wrong direction.
198 difAngle -= 2.0*Pi;
200 return difAngle;
201 }// computeShortestAngle //
203 //-----------------------------------------------
204 // readStringArray :
205 //-----------------------------------------------
206 void readStringArray(const std::string &filename, NLGEORGES::UFormLoader *formLoader, NLMISC::CSmartPtr<NLGEORGES::UForm> &form, std::map<std::string, std::string> &container)
208 // Read an array of strings
209 if(formLoader)
211 form = formLoader->loadForm(filename.c_str());
212 if(form)
214 // Get the root.
215 const UFormElm& rootElmt = form->getRootNode();
216 // Get animations.
217 const UFormElm *elmt = 0;
218 if(rootElmt.getNodeByName(&elmt, "array") == false)
219 nlwarning("readStringArray: the node 'array' is not referenced.");
220 // If the array is not empty (in fact exist).
221 if(elmt)
223 // Get the array size
224 uint arraySize;
225 elmt->getArraySize(arraySize);
226 // If there is at least 1 animation.
227 if(arraySize > 0)
229 // Get all animation for the State.
230 for(uint i = 0; i<arraySize; ++i)
232 // Get the Name
233 std::string nodeName;
234 std::string stringName;
235 if(elmt->getArrayNodeName(nodeName, i))
237 if(elmt->getArrayValue(stringName, i))
238 container.insert(make_pair(nodeName, stringName));
239 else
240 nlwarning("readStringArray: no string associated to the node '%u(%s)'.", i, nodeName.c_str());
242 else
243 nlwarning("readStringArray: node '%u', index valid.", i);
247 else
248 nlwarning("readStringArray: array node not allocated.");
250 else
251 nlwarning("readStringArray: cannot create the form from file '%s'.", filename.c_str());
253 else
254 nlwarning("readStringArray: the Loader is not allocated.");
255 }// readStringArray //
257 //-----------------------------------------------
258 // mode2Anim :
259 // Return the animset base name corresponding to the mode.
260 // \param mode : the mode to convert.
261 // \param result : this will be filed with the mode animset name.
262 // \return bool : 'true' if 'result' is filled, 'false' if left untouched.
263 //-----------------------------------------------
264 bool mode2Anim(MBEHAV::EMode mode, string &result)
266 static bool init = false;
267 static string mode2AnimArray[MBEHAV::NUMBER_OF_MODES];
269 // Is the mode valid
270 uint index = (uint)mode;
271 if(index >= MBEHAV::NUMBER_OF_MODES)
272 return false;
274 // Initialize
275 if(!init)
277 // Read mode2animset file
278 std::map<std::string, std::string> mode2Animset;
279 const std::string filename = "mode2animset.string_array";
280 NLGEORGES::UFormLoader *formLoader = UFormLoader::createLoader();
281 if(formLoader)
283 NLMISC::CSmartPtr<NLGEORGES::UForm> form;
284 readStringArray(filename, formLoader, form, mode2Animset);
286 else
287 nlwarning("mode2Anim: cannot create de loader");
288 NLGEORGES::UFormLoader::releaseLoader(formLoader);
289 // Initialize the static vector.
290 //-------------------------------
291 for(uint i=0; i<MBEHAV::NUMBER_OF_MODES; ++i)
293 const std::string modeName = MBEHAV::modeToString((MBEHAV::EMode)i);
294 std::map<std::string, std::string>::const_iterator it = mode2Animset.find(modeName);
295 if(it != mode2Animset.end())
297 mode2AnimArray[i] = (*it).second;
298 if(mode2AnimArray[i].empty())
299 nlwarning("mode2Anim: The mode '%d(%s)' has an empty animset associated.", i, modeName.c_str ());
301 // No animset for the mode.
302 else
304 mode2AnimArray[i].clear();
305 nlwarning("mode2Anim: no animset associated to the mode %d'%s'.", i, modeName.c_str());
308 // Init Done now.
309 init = true;
312 // Fill the result.
313 // Check name.
314 if(mode2AnimArray[index].empty())
315 result = mode2AnimArray[MBEHAV::NORMAL];
316 else
317 result = mode2AnimArray[index];
318 // Result filled.
319 return true;
320 }// mode2Anim //
322 //-----------------------------------------------
323 // computeAnimSet :
324 // Compute the animation set to use according to weapons, mode and race.
325 // \param animSet : result pointer.
326 // \param mode : the mode.
327 // \param animSetBaseName : basic name to construc the complet name of the animSet.
328 // \param leftHand : animSet name for the left hand.
329 // \param rightHand : animSet name for the right hand.
330 // \param lookAtItemsInHands : compute animset according to items in hands or not.
331 // \return bool : 'true' if the new animation set is the right one. 'false' if the one choosen is not the right one.
332 //-----------------------------------------------
333 bool computeAnimSet(const CAnimationSet *&animSet, MBEHAV::EMode mode, const string &animSetBaseName, const CItemSheet *itemLeftHand, const CItemSheet *itemRightHand, bool lookAtItemsInHands)
335 static std::set<std::string> UnknownAnimSet;
337 if(EAM == 0)
339 if(!ClientCfg.Light)
341 nlwarning("computeAnimSet: EAM not allocated -> cannot compute the anim set.");
343 return false;
346 string animSetRight, animSetLeft;
347 if(itemLeftHand)
348 animSetLeft = itemLeftHand->getAnimSet();
350 if(itemRightHand)
351 animSetRight = itemRightHand->getAnimSet();
353 // Get the animset name from the mode.
354 string result;
355 if(!mode2Anim(mode, result))
357 nlwarning("computeAnimSet: unknown mode '%d'.", mode);
358 result = "default";
361 // Compute the name.
362 if(lookAtItemsInHands)
363 result = animSetBaseName + "_" + result + "_" + animSetRight + "_" + animSetLeft;
364 else
365 result = animSetBaseName + "_" + result + "__";
367 // Get the animset.
368 const CAnimationSet *animSetTmp = EAM->getAnimSet(result);
369 if(animSetTmp)
371 animSet = animSetTmp;
372 return true;
374 // Bad one try something else.
375 else
377 // Up to 100 missing anim set (security).
378 if(UnknownAnimSet.size() < 100)
380 if(UnknownAnimSet.insert(result).second)
381 nlwarning("computeAnimSet: unknown Anim Set '%s' ('%u' unkowns).", result.c_str(), UnknownAnimSet.size());
383 // Try to compute the default one
384 result = animSetBaseName + "_" + "default" + "__";
385 animSetTmp = EAM->getAnimSet(result);
386 if(animSetTmp)
387 animSet = animSetTmp;
388 else
390 // Up to 100 missing anim set (security).
391 if(UnknownAnimSet.size() < 100)
393 if(UnknownAnimSet.insert(result).second)
394 nlwarning("computeAnimSet: unknown Anim Set '%s' ('%u' unkowns).", result.c_str(), UnknownAnimSet.size());
399 // Not Well done.
400 return false;
401 }// computeAnimSet //
404 //-----------------------------------------------
405 // dump :
406 // Create a file with information to debug.
407 //-----------------------------------------------
408 void dump(const std::string &name)
410 // Write information to start as the version
411 COFile fStart;
412 if(fStart.open(name + "_start.rec", false, false))
414 CVectorD currentPos = UserEntity->pos();
415 fStart.serialVersion(RecordVersion);
416 fStart.serial(currentPos);
417 // Close the File.
418 fStart.close();
420 else
421 nlwarning("dump: cannot open/create the file '%s_start.rec'.", name.c_str());
423 // Write the DB
424 IngameDbMngr.write(name + "_db.rec");
425 // Open the file.
426 COFile f;
427 if(f.open(name + ".rec", false, false))
429 // Dump entities.
430 EntitiesMngr.dump(f);
432 // Dump Client CFG.
433 ClientCfg.serial(f);
435 // Close the File.
436 f.close();
438 else
439 nlwarning("dump: cannot open/create the file '%s.rec'.", name.c_str());
442 // Open the file.
443 if(f.open(name + ".xml", false, true))
445 // Create the XML stream
446 COXml output;
447 // Init
448 if(output.init (&f, "1.0"))
450 // Open the XML Dump.
451 output.xmlPush("XML");
453 // Dump Client CFG.
454 ClientCfg.serial(output);
456 // Dump entities.
457 EntitiesMngr.dumpXML(output);
459 // Close the XML Dump.
460 output.xmlPop();
462 // Flush the stream, write all the output file
463 output.flush();
465 else
466 nlwarning("dump: cannot initialize '%s.xml'.", name.c_str());
467 // Close the File.
468 f.close();
470 else
471 nlwarning("dump: cannot open/create the file '%s.xml'.", name.c_str());
472 }// dump //
475 //-----------------------------------------------
476 // loadDump :
477 // Create a file with the current state of the client (good to report a bug).
478 //-----------------------------------------------
479 void loadDump(const std::string &name)
481 CVectorD currentPos;
483 // Load information to start as the version
484 CIFile fStart;
485 if(fStart.open(name + "_start.rec", false))
487 fStart.serialVersion(RecordVersion);
488 fStart.serial(currentPos);
489 // Close the File.
490 fStart.close();
492 else
493 nlwarning("loadDump: cannot open the file '%s_start.rec'.", name.c_str());
495 // Update the position for the vision.
496 NetMngr.setReferencePosition(currentPos);
498 // Select the closest continent from the new position.
499 class CDummyProgress : public IProgressCallback
501 void progress (float /* value */) {}
503 CDummyProgress dummy;
504 ContinentMngr.select(currentPos, dummy);
506 // Load the DB
507 IngameDbMngr.read(name + "_db.rec");
509 // Open the file.
510 CIFile f;
511 if(f.open(name + ".rec", false))
513 // Dump entities.
514 EntitiesMngr.dump(f);
516 // Close the File.
517 f.close();
519 else
520 nlwarning("loadDump: cannot open '%s.rec'.", name.c_str());
521 }// loadDump //
524 //-----------------------------------------------
525 // getLodCharacterId
526 // Get the lod character id from the scene, according to LodCharacterName. Cached.
527 // -1 if id not found.
528 //-----------------------------------------------
529 sint getLodCharacterId(UScene &scene, const string &lodCharacterName)
531 sint lodCharacterId = scene.getCLodShapeIdByName(lodCharacterName);
532 // display a warning for bad character Id, only if name was setup in the sheet
533 if(lodCharacterId==-1 && !lodCharacterName.empty() )
535 // Limited to 100 missing Lod to avoid memories problems
536 if(LodCharactersNotFound.size() < 100)
538 // Insert and display a waring
539 if(LodCharactersNotFound.insert(lodCharacterName).second)
540 nlwarning("getLodCharacterId: Not found A Character LodCharacter in the Manager: %s", lodCharacterName.c_str());
544 return lodCharacterId;
545 }// getLodCharacterId //
548 //-----------------------------------------------
549 // getItem :
550 //-----------------------------------------------
551 CItemSheet *getItem(const CGenderInfo &genderInfo, SLOTTYPE::EVisualSlot slot)
553 CEntitySheet *faceItem = SheetMngr.get(CSheetId(genderInfo.getItemName(slot)));
554 return (dynamic_cast <CItemSheet *> (faceItem));
555 }// getItem //
558 //-----------------------------------------------
559 // getColorIndex :
560 //-----------------------------------------------
561 sint getColorIndex(const CGenderInfo &genderInfo, SLOTTYPE::EVisualSlot slot)
563 CItemSheet *is = getItem(genderInfo, slot);
564 if(is == 0)
565 return 0;
566 return is->Color;
567 }// getColorIndex //
570 //-----------------------------------------------
571 // buildPropVisualA :
572 //-----------------------------------------------
573 SPropVisualA buildPropVisualA(const CGenderInfo &genderInfo)
575 SPropVisualA dest;
576 dest.PropertyA = 0;
577 // setup items
578 dest.PropertySubData.JacketModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::CHEST_SLOT], SLOTTYPE::CHEST_SLOT);
579 dest.PropertySubData.TrouserModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::LEGS_SLOT], SLOTTYPE::LEGS_SLOT);
580 dest.PropertySubData.ArmModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::ARMS_SLOT], SLOTTYPE::ARMS_SLOT);
581 dest.PropertySubData.HatModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::HEAD_SLOT], SLOTTYPE::HEAD_SLOT);
583 // setup colors (fixed or user)
584 sint col = getColorIndex(genderInfo, SLOTTYPE::CHEST_SLOT);
585 dest.PropertySubData.JacketColor = (col == -1) ? 0 : col;
587 col = getColorIndex(genderInfo, SLOTTYPE::LEGS_SLOT);
588 dest.PropertySubData.TrouserColor = (col == -1) ? 0 : col;
590 col = getColorIndex(genderInfo, SLOTTYPE::ARMS_SLOT);
591 dest.PropertySubData.ArmColor = (col == -1) ? 0 : col;
593 col = getColorIndex(genderInfo, SLOTTYPE::HEAD_SLOT);
594 dest.PropertySubData.HatColor = (col == -1) ? 0 : col;
596 // sheath are not used
597 return dest;
598 }// buildPropVisualA //
601 //-----------------------------------------------
602 // buildPropVisualB :
603 //-----------------------------------------------
604 SPropVisualB buildPropVisualB(const CGenderInfo &genderInfo)
606 // feet & hands
607 SPropVisualB dest;
608 dest.PropertyB = 0;
609 // setup items
610 dest.PropertySubData.HandsModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::HANDS_SLOT], SLOTTYPE::HANDS_SLOT);
611 dest.PropertySubData.FeetModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::FEET_SLOT], SLOTTYPE::FEET_SLOT);
613 // setup colors (fixed or user)
614 sint col = getColorIndex(genderInfo, SLOTTYPE::HANDS_SLOT);
615 dest.PropertySubData.HandsColor = (col == -1) ? 0 : col;
617 col = getColorIndex(genderInfo, SLOTTYPE::FEET_SLOT);
618 dest.PropertySubData.FeetColor = (col == -1) ? 0 : col;
620 return dest;
621 }// buildPropVisualB //
626 //-----------------------------------------------
627 // isUserColorSupported :
628 // Test whether user color is supported for this equipment
629 //-----------------------------------------------
630 bool isUserColorSupported(const CPlayerSheet::CEquipment &equip)
632 NLMISC::CSheetId si;
633 if(!si.buildSheetId(equip.Item))
634 return false;
636 CItemSheet *is = dynamic_cast<CItemSheet *>(SheetMngr.get(si));
637 if(!is)
638 return false;
640 return is->Color == -1; // user color supported by the item
641 }// isUserColorSupported //
643 //-----------------------------------------------
644 // isUserColorSupported :
645 // Test whether user color is supported for a given visual slot
646 //-----------------------------------------------
647 bool isUserColorSupported(const CPlayerSheet &playerSheet, SLOTTYPE::EVisualSlot vs)
649 switch(vs)
651 case SLOTTYPE::CHEST_SLOT: return isUserColorSupported(playerSheet.Body);
652 case SLOTTYPE::LEGS_SLOT: return isUserColorSupported(playerSheet.Legs);
653 case SLOTTYPE::HEAD_SLOT: return isUserColorSupported(playerSheet.Head);
654 case SLOTTYPE::ARMS_SLOT: return isUserColorSupported(playerSheet.Arms);
655 case SLOTTYPE::HANDS_SLOT: return isUserColorSupported(playerSheet.Hands);
656 case SLOTTYPE::FEET_SLOT: return isUserColorSupported(playerSheet.Feet);
657 case SLOTTYPE::RIGHT_HAND_SLOT: return isUserColorSupported(playerSheet.ObjectInRightHand);
658 case SLOTTYPE::LEFT_HAND_SLOT: return isUserColorSupported(playerSheet.ObjectInLeftHand);
659 default: break;
661 return false;
662 }// isUserColorSupported //
665 //-----------------------------------------------
666 // getColor :
667 //-----------------------------------------------
668 sint8 getColor(const CPlayerSheet::CEquipment &equip)
670 NLMISC::CSheetId si;
671 if(!si.buildSheetId(equip.Item))
672 return -2;
674 CItemSheet *is = dynamic_cast<CItemSheet *>(SheetMngr.get(si));
675 if(!is)
676 return -2;
678 if(is->Color == -1) // user color supported by the item
680 return equip.Color;
682 else
684 return is->Color;
686 }// getColor //
688 //-----------------------------------------------
689 // buildPropVisualA :
690 //-----------------------------------------------
691 SPropVisualA buildPropVisualA(const CPlayerSheet &playerSheet)
693 SPropVisualA vp;
694 // setup items
695 vp.PropertySubData.JacketModel = SheetMngr.getVSIndex(playerSheet.Body.Item, SLOTTYPE::CHEST_SLOT);
696 vp.PropertySubData.TrouserModel = SheetMngr.getVSIndex(playerSheet.Legs.Item, SLOTTYPE::LEGS_SLOT);
697 vp.PropertySubData.ArmModel = SheetMngr.getVSIndex(playerSheet.Arms.Item, SLOTTYPE::ARMS_SLOT);
698 vp.PropertySubData.HatModel = SheetMngr.getVSIndex(playerSheet.Head.Item, SLOTTYPE::HEAD_SLOT);
699 vp.PropertySubData.WeaponRightHand = SheetMngr.getVSIndex(playerSheet.ObjectInRightHand.Item, SLOTTYPE::RIGHT_HAND_SLOT);
700 vp.PropertySubData.WeaponLeftHand = SheetMngr.getVSIndex(playerSheet.ObjectInLeftHand.Item, SLOTTYPE::LEFT_HAND_SLOT);
702 // setup colors
703 vp.PropertySubData.JacketColor = getColor(playerSheet.Body);
704 vp.PropertySubData.TrouserColor = getColor(playerSheet.Legs);
705 vp.PropertySubData.ArmColor = getColor(playerSheet.Arms);
706 vp.PropertySubData.HatColor = getColor(playerSheet.Head);
708 return vp;
709 }// buildPropVisualA //
711 //-----------------------------------------------
712 // buildPropVisualB :
713 //-----------------------------------------------
714 SPropVisualB buildPropVisualB(const CPlayerSheet &playerSheet)
716 SPropVisualB vp;
717 // setup items
718 vp.PropertySubData.Name = 0;
719 vp.PropertySubData.HandsModel = SheetMngr.getVSIndex(playerSheet.Hands.Item, SLOTTYPE::HANDS_SLOT);
720 vp.PropertySubData.FeetModel = SheetMngr.getVSIndex(playerSheet.Feet.Item, SLOTTYPE::FEET_SLOT);
722 // setup colors
723 vp.PropertySubData.HandsColor = getColor(playerSheet.Hands);
724 vp.PropertySubData.FeetColor = getColor(playerSheet.Feet);
726 return vp;
727 }// buildPropVisualB //
730 //-----------------------------------------------
731 // drawBox :
732 // Draw a Box from 2 vectors.
733 //-----------------------------------------------
734 void drawBox(const CVector &vMin, const CVector &vMax, const CRGBA &color)
736 CLineColor line;
737 line.Color0 = color;
738 line.Color1 = color;
739 // Bottom quad
740 line = CLine(CVector(vMin.x,vMin.y,vMin.z), CVector(vMax.x,vMin.y,vMin.z));
741 Driver->drawLine(line, GenericMat);
742 line = CLine(CVector(vMax.x,vMin.y,vMin.z), CVector(vMax.x,vMax.y,vMin.z));
743 Driver->drawLine(line, GenericMat);
744 line = CLine(CVector(vMax.x,vMax.y,vMin.z), CVector(vMin.x,vMax.y,vMin.z));
745 Driver->drawLine(line, GenericMat);
746 line = CLine(CVector(vMin.x,vMax.y,vMin.z), CVector(vMin.x,vMin.y,vMin.z));
747 Driver->drawLine(line, GenericMat);
748 // Top quad
749 line = CLine(CVector(vMin.x,vMin.y,vMax.z), CVector(vMax.x,vMin.y,vMax.z));
750 Driver->drawLine(line, GenericMat);
751 line = CLine(CVector(vMax.x,vMin.y,vMax.z), CVector(vMax.x,vMax.y,vMax.z));
752 Driver->drawLine(line, GenericMat);
753 line = CLine(CVector(vMax.x,vMax.y,vMax.z), CVector(vMin.x,vMax.y,vMax.z));
754 Driver->drawLine(line, GenericMat);
755 line = CLine(CVector(vMin.x,vMax.y,vMax.z), CVector(vMin.x,vMin.y,vMax.z));
756 Driver->drawLine(line, GenericMat);
757 // Sides Quad
758 line = CLine(CVector(vMin.x,vMin.y,vMin.z), CVector(vMin.x,vMin.y,vMax.z));
759 Driver->drawLine(line, GenericMat);
760 line = CLine(CVector(vMax.x,vMin.y,vMin.z), CVector(vMax.x,vMin.y,vMax.z));
761 Driver->drawLine(line, GenericMat);
762 line = CLine(CVector(vMax.x,vMax.y,vMin.z), CVector(vMax.x,vMax.y,vMax.z));
763 Driver->drawLine(line, GenericMat);
764 line = CLine(CVector(vMin.x,vMax.y,vMin.z), CVector(vMin.x,vMax.y,vMax.z));
765 Driver->drawLine(line, GenericMat);
766 }// drawBox //
769 //-----------------------------------------------
770 // drawSphere :
771 // Draw a Sphere
772 //-----------------------------------------------
773 void drawSphere(const NLMISC::CVector &center, float radius, const NLMISC::CRGBA &color)
775 const uint numSegs= 12;
776 CLineColor line;
777 line.Color0 = color;
778 line.Color1 = color;
779 // For all faces
780 for(uint face=0;face<3;face++)
782 for(uint i=0;i<numSegs;i++)
784 float angStart= float(i*2*Pi/numSegs);
785 float angEnd= float(((i+1)%numSegs)*2*Pi/numSegs);
786 line.V0= radius*CVector(cosf(angStart), sinf(angStart), 0.f);
787 line.V1= radius*CVector(cosf(angEnd), sinf(angEnd), 0.f);
788 if(face==1)
790 swap(line.V0.x, line.V0.z);
791 swap(line.V1.x, line.V1.z);
793 else if(face==2)
795 swap(line.V0.y, line.V0.z);
796 swap(line.V1.y, line.V1.z);
798 line.V0+= center;
799 line.V1+= center;
800 Driver->drawLine(line, GenericMat);
806 //-----------------------------------------------
807 // getSeedsFromDB :
808 //-----------------------------------------------
809 void getSeedsFromDB(CSeeds &dest)
811 CInterfaceManager *im =CInterfaceManager::getInstance();
812 nlctassert(sizeof(CSeeds::TUInt) == 4); // excpect that the number of each seed type is encoded on 32 bits
813 // if this assert at compile, change the following code
814 string ls = CWidgetManager::getInstance()->getParser()->getDefine("money_1");
815 string ms = CWidgetManager::getInstance()->getParser()->getDefine("money_2");
816 string bs = CWidgetManager::getInstance()->getParser()->getDefine("money_3");
817 string vbs = CWidgetManager::getInstance()->getParser()->getDefine("money_4");
819 dest = CSeeds(NLGUI::CDBManager::getInstance()->getDbProp(ls)->getValue32(),
820 NLGUI::CDBManager::getInstance()->getDbProp(ms)->getValue32(),
821 NLGUI::CDBManager::getInstance()->getDbProp(bs)->getValue32(),
822 NLGUI::CDBManager::getInstance()->getDbProp(vbs)->getValue32());
823 } // getSeedsFromDB //
826 //-----------------------------------------------
827 // setVect :
828 // Change a 'direction' vector.
829 // \param vectToChange : the vector to change.
830 // \param vect : new vector to use.
831 // \param compute : adjust the param 'vect' to be valid or leave the old one unchanged if impossible.
832 // \param check : warning if the param 'vect' is not valid (vector Null) even with compute=true.
833 // \return bool : 'true' if the vectToChange has been filled, else 'false'.
834 //-----------------------------------------------
835 bool setVect(CVector &vectToChange, const CVector &vect, bool compute, bool check)
837 // Compute the vector.
838 if(compute)
840 CVector vectTmp(vect);
841 // No need of the Z component for the vector.
842 vectTmp.z = 0.0f;
843 // Check vector.
844 if(vectTmp != CVector::Null)
846 vectTmp.normalize();
847 vectToChange = vectTmp;
848 return true;
850 // Bad Vector -> vectToChange remains the same
851 else
853 // Warning
854 if(check)
855 nlwarning("setVect: cannot compute the vector, keep the old one.");
856 return false;
860 // Bad Vector -> vectToChange remains the same
861 if(vect == CVector::Null)
863 // Warning
864 if(check)
865 nlwarning("setVect: param is a vector Null, vectToChange remains the same.");
866 return false;
869 // Set the new front vector.
870 vectToChange = vect;
871 return true;
872 }// setVect //
874 NLMISC::CRGBA interpClientCfgColor(const string &src, string &dest)
876 CRGBA color = CRGBA::White;
877 if (src.size() >= 3)
879 if (src[0] == '&')
881 string::size_type nextPos = src.find('&', 1);
882 if (nextPos != string::npos)
884 std::string colorCode;
885 colorCode.resize(nextPos - 1);
886 for(uint k = 0; k < nextPos - 1; ++k)
888 colorCode[k] = tolower((char) src[k + 1]); // TODO: toLowerAscii
890 std::map<std::string, CClientConfig::SSysInfoParam>::const_iterator it = ClientCfg.SystemInfoParams.find(colorCode);
891 if (it != ClientCfg.SystemInfoParams.end())
893 color = it->second.Color;
895 dest = src.substr(nextPos + 1);
897 else
899 dest = src;
902 else
904 dest = src;
907 else
909 dest = src;
911 return color;
914 std::string getStringCategory(const string &src, string &dest, bool alwaysAddSysByDefault)
916 std::string str = getStringCategoryIfAny(src, dest);
917 if (alwaysAddSysByDefault)
918 return str.empty()?"SYS":str;
919 else
920 return str;
924 std::string getStringCategoryIfAny(const string &src, string &dest)
926 std::string colorCode;
927 if (src.size() >= 3)
929 size_t startPos = 0;
931 // Skip <NEW> or <CHG> if present at beginning
932 string preTag;
933 const size_t PreTagSize = 5;
934 static const string newTag = "<NEW>";
935 if ( (src.size() >= PreTagSize) && (src.substr( 0, PreTagSize ) == newTag) )
937 startPos = PreTagSize;
938 preTag = newTag;
940 static const string chgTag = "<CHG>";
941 if ( (src.size() >= PreTagSize) && (src.substr( 0, PreTagSize ) == chgTag) )
943 startPos = PreTagSize;
944 preTag = chgTag;
947 if (src[startPos] == '&')
949 string::size_type nextPos = src.find('&', startPos+1);
950 if (nextPos != string::npos)
952 size_t codeSize = nextPos - startPos - 1;
953 colorCode.resize( codeSize );
954 for(ptrdiff_t k = 0; k < (ptrdiff_t)codeSize; ++k)
956 colorCode[k] = tolower((char) src[k + startPos + 1]); // TODO: toLowerAscii
958 string destTmp;
959 if ( startPos != 0 )
960 destTmp = preTag; // leave <NEW> or <CHG> in the dest string
961 destTmp += src.substr(nextPos + 1);
962 dest = destTmp;
964 else
966 dest = src;
969 else
971 dest = src;
974 else
976 dest = src;
978 return colorCode;
982 // ***************************************************************************
983 sint ucstrnicmp(const ucstring &s0, uint p0, uint n0, const ucstring &s1) // OLD
985 // start
986 const ucchar *start1= s1.c_str();
987 uint lenS1= (uint)s1.size();
988 const ucchar *start0= s0.c_str();
989 uint lenS0= (uint)s0.size();
990 if(p0!=0)
992 if(p0<lenS0)
994 start0+= p0;
995 lenS0-= p0;
997 else
999 start0+= lenS0; // points to '\0'
1000 lenS0= 0;
1003 lenS0= min(lenS0, n0);
1005 // compare character to character
1006 while(lenS0>0 && lenS1>0)
1008 ucchar c0= toLower(*start0++);
1009 ucchar c1= toLower(*start1++);
1010 if(c0!=c1)
1011 return c0<c1?-1:+1;
1012 lenS0--;
1013 lenS1--;
1016 // return -1 if s1>s0, 1 if s0>s1, or 0 if equals
1017 if(lenS1>0)
1018 return -1;
1019 else if(lenS0>0)
1020 return 1;
1021 else
1022 return 0;
1026 // *******************************************************************************************
1027 float computeUniformNoise(const NLMISC::CNoiseValue &nv, const CVector &pos)
1029 NLMISC::OptFastFloorBegin();
1030 float value = nv.eval(pos);
1031 value = 10.f * fmodf(value, 0.1f); // make repartition more uniform
1032 NLMISC::OptFastFloorEnd();
1033 return value;
1037 // ***************************************************************************
1038 void computeCurrentFovAspectRatio(float &fov, float &ar)
1040 // compute the fov
1041 fov = (float)(ClientCfg.FoV*Pi/180.0);
1043 // get the screen aspect ratio from CFG
1044 ar = ClientCfg.ScreenAspectRatio;
1046 // if Driver is not created, we can't get current screen mode
1047 if (!Driver) return;
1049 // if windowed, must modulate aspect ratio by (WindowResolution / ScreenResolution)
1050 if(ClientCfg.Windowed)
1052 uint32 wndW, wndH;
1053 Driver->getWindowSize(wndW, wndH);
1054 UDriver::CMode mode;
1055 Driver->getCurrentScreenMode(mode);
1056 if(wndH)
1058 // compute window aspect ratio
1059 float arWnd= float(wndW) / float(wndH);
1060 if (ar == 0.f)
1062 // auto mode, we are using window aspect ratio
1063 ar = arWnd;
1065 else if (mode.Width && mode.Height)
1067 // compute screen aspect ratio
1068 float arScreen= float(mode.Width) / float(mode.Height);
1069 ar *= arWnd / arScreen;
1073 // if fullscreen, must modulate aspect ratio by ScreenResolution
1074 else
1076 if (ar == 0.f)
1078 UDriver::CMode mode;
1079 Driver->getCurrentScreenMode(mode);
1080 if(mode.Height)
1082 ar = float(mode.Width) / float(mode.Height);
1088 // ***************************************************************************
1089 void drawDisc(CBitmap &dest, float x, float y, float radius, const CRGBA &color, bool additif /*= false*/, uint numSegs /*= 127*/)
1091 CPolygon2D poly;
1092 poly.Vertices.resize(numSegs);
1093 for(uint k = 0; k < numSegs; ++k)
1095 poly.Vertices[k].set(x + radius * (float) cos(k / (float) numSegs * 2 * Pi), y + radius * (float) sin(k / (float) numSegs * 2 * Pi));
1097 CPolygon2D::TRasterVect rasters;
1098 sint minY;
1099 poly.computeOuterBorders(rasters, minY);
1100 sint maxY = std::min((sint) dest.getHeight(), (sint) rasters.size() + minY);
1101 for (sint y = std::max((sint) 0, minY); y < maxY; ++y)
1103 nlassert(y >= 0 && y < (sint) dest.getHeight());
1104 sint minX = std::max((sint) 0, rasters[y - minY].first);
1105 sint maxX = std::min((sint) dest.getWidth(), rasters[y - minY].second);
1106 if (maxX > minX)
1108 CRGBA *pt = (CRGBA *) &dest.getPixels(0)[0];
1109 pt += y * dest.getWidth() + minX;
1110 const CRGBA *endPt = pt + (maxX - minX);
1111 while (pt != endPt)
1113 if (additif)
1115 pt->add(*pt, color);
1117 else
1119 *pt = color;
1121 ++ pt;
1128 // *************************************************************************************************
1129 NL3D::UScene *getSkyScene()
1131 if (ContinentMngr.cur() && !ContinentMngr.cur()->Indoor)
1133 CSky &sky = ContinentMngr.cur()->CurrentSky;
1134 if (sky.getScene())
1136 return sky.getScene();
1138 else
1140 return SkyScene; // old sky rendering
1143 return NULL;
1146 // *************************************************************************************************
1147 void setEmissive(NL3D::UInstance instance, const NLMISC::CRGBA &color)
1149 if (instance.empty()) return;
1150 for(uint k = 0; k < instance.getNumMaterials(); ++k)
1152 NL3D::UInstanceMaterial mat = instance.getMaterial(k);
1153 mat.setEmissive(color);
1157 // *************************************************************************************************
1158 void setDiffuse(NL3D::UInstance instance, bool onOff, const NLMISC::CRGBA &color)
1160 if (instance.empty()) return;
1161 for(uint k = 0; k < instance.getNumMaterials(); ++k)
1163 NL3D::UInstanceMaterial mat = instance.getMaterial(k);
1164 if (mat.isLighted())
1166 CRGBA src;
1167 if (onOff)
1169 src = mat.getDiffuse();
1170 src.R = color.R;
1171 src.G = color.G;
1172 src.B = color.B;
1174 else
1176 NL3D::UMaterial matShape = instance.getShape().getMaterial(k);
1177 src = matShape.getDiffuse();
1178 src.A = mat.getDiffuse().A;
1180 mat.setDiffuse(src);
1182 else
1184 CRGBA src;
1185 if (onOff)
1187 src = mat.getColor();
1188 src.R = color.R;
1189 src.G = color.G;
1190 src.B = color.B;
1192 else
1194 NL3D::UMaterial matShape = instance.getShape().getMaterial(k);
1195 src = matShape.getColor();
1196 src.A = mat.getColor().A;
1198 mat.setColor(src);
1205 // *************************************************************************************************
1206 void makeInstanceTransparent(UInstance &inst, uint8 opacity, bool disableZWrite)
1208 UShape shape= inst.getShape();
1209 if(shape.empty())
1210 return;
1211 uint numMats= shape.getNumMaterials();
1212 if(numMats==0)
1213 return;
1214 if(numMats!=inst.getNumMaterials())
1215 return;
1217 // instance transparent or not?
1218 if (opacity == 255)
1220 // reset default shape opacity / transparency
1221 inst.setOpacity(shape.getDefaultOpacity());
1222 inst.setTransparency(shape.getDefaultTransparency());
1223 inst.setBypassLODOpacityFlag(false);
1225 else
1227 // Will have some blend material => sure not to be rendered in Opaque pass
1228 inst.setOpacity(false);
1229 inst.setTransparency(true);
1230 inst.setBypassLODOpacityFlag(true); // these flags prevails over the current lods flags for multi-lod objects
1233 // set all materials
1234 for (uint32 j = 0; j < numMats; ++j)
1236 NL3D::UInstanceMaterial matInst = inst.getMaterial(j);
1237 NL3D::UMaterial matShape= shape.getMaterial(j);
1239 // disalbe zwrite?
1240 if(disableZWrite)
1241 matInst.setZWrite(false);
1242 else
1243 matInst.setZWrite(matShape.getZWrite());
1245 // if no more transparent
1246 if (opacity == 255)
1248 // reset to default
1249 matInst.setBlend(matShape.getBlend());
1250 matInst.setBlendFunc((NL3D::UInstanceMaterial::TBlend)matShape.getSrcBlend(),
1251 (NL3D::UInstanceMaterial::TBlend)matShape.getDstBlend());
1252 // if orginal material is opaque or additif and has no alpha test, then ensure restore last tex env if needed
1253 CMaterial *destInternalMat = matInst.getObjectPtr();
1254 if (!matShape.getBlend() && !matShape.getAlphaTest())
1256 if (destInternalMat->getShader() == CMaterial::Normal)
1258 CMaterial *srcInternalMat = matShape.getObjectPtr();
1259 uint numTex = 0;
1260 for (;numTex < 4 && srcInternalMat->getTexture(numTex) != NULL; ++numTex) {}
1261 if (numTex > 0)
1263 if (srcInternalMat->getTexEnvMode(numTex - 1) != destInternalMat->getTexEnvMode(numTex - 1))
1265 destInternalMat->setTexEnvMode(numTex - 1, srcInternalMat->getTexEnvMode(numTex - 1));
1270 if (destInternalMat->getShader() == CMaterial::Normal)
1272 // if !lighted, restore color
1273 if (!destInternalMat->isLighted())
1275 CMaterial *srcInternalMat = matShape.getObjectPtr();
1276 // restore alpha in color
1277 CRGBA color = destInternalMat->getColor();
1278 color.A = srcInternalMat->getColor().A;
1279 destInternalMat->setColor(color);
1283 else
1285 // Enable blend
1286 matInst.setBlend(true);
1287 // If default is ???/one or , then use a srcalpha/one (eg: for Diamond-like weapons)
1288 if(matShape.getBlend() && (sint32)matShape.getDstBlend()==(sint32)NL3D::UInstanceMaterial::one)
1289 matInst.setBlendFunc(NL3D::UInstanceMaterial::srcalpha, NL3D::UInstanceMaterial::one);
1290 // else use a standard srcalpha/invsrcalpha
1291 else
1292 matInst.setBlendFunc(NL3D::UInstanceMaterial::srcalpha, NL3D::UInstanceMaterial::invsrcalpha);
1293 // if orginal material is opaque or additif and has no alpha test, then ensure that the alpha output is 'diffuse'
1294 CMaterial *internalMat = matInst.getObjectPtr();
1295 if (!matShape.getBlend() && !matShape.getAlphaTest())
1297 if (internalMat->getShader() == CMaterial::Normal)
1299 uint numTex = 0;
1300 for (;numTex < 4 && internalMat->getTexture(numTex) != NULL; ++numTex) {}
1301 if (numTex > 0)
1303 internalMat->texEnvOpAlpha(numTex - 1, CMaterial::Replace);
1304 // if material is unlighted, then use the constant at this stage to set the alpha
1305 internalMat->texEnvArg0Alpha(numTex - 1, CMaterial::Diffuse, CMaterial::SrcAlpha);
1309 if (internalMat->getShader() == CMaterial::Normal)
1311 if (!internalMat->isLighted())
1313 // replace alpha in color
1314 CRGBA color = internalMat->getColor();
1315 color.A = opacity;
1316 internalMat->setColor(color);
1321 // suppose that default opacity is always 255
1322 if (matInst.isLighted())
1324 matInst.setOpacity(opacity);
1327 matInst.setAlphaTestThreshold(matShape.getAlphaTestThreshold()*((float)opacity)/255.0f);
1331 void setVideoMode(const UDriver::CMode &mode)
1333 UDriver::CMode oldMode, newMode = mode;
1334 oldMode.Windowed = true; // getCurrentScreenMode may fail if first init ...
1335 Driver->getCurrentScreenMode(oldMode);
1336 bool wasMaximized = isWindowMaximized();
1337 if (!Driver->setMode(newMode) && !newMode.Windowed)
1339 // failed to switch to mode, fall back to windowed
1340 newMode.Windowed = true;
1341 ClientCfg.Windowed = true;
1342 ClientCfg.writeInt("FullScreen", 0);
1344 // set the window mode
1345 Driver->setMode(newMode);
1347 bool isMaximized = isWindowMaximized();
1348 if (oldMode.Windowed && !newMode.Windowed) // going to fullscreen ?
1350 /*CInterfaceManager *pIM = CInterfaceManager::getInstance();
1351 pIM->movePointerAbs((sint32) mode.Width / 2, (sint32) mode.Height / 2);
1352 Driver->setMousePos(0.5f, 0.5f);*/
1354 else if ((!oldMode.Windowed || wasMaximized) && (newMode.Windowed && !isMaximized)) // leaving fullscreen ?
1356 UDriver::CMode screenMode;
1358 uint32 posX = 0;
1359 uint32 posY = 0;
1361 if (Driver->getCurrentScreenMode(screenMode))
1363 // position is not saved in config so center the window
1364 posX = (screenMode.Width - Driver->getWindowWidth())/2;
1365 posY = (screenMode.Height - Driver->getWindowHeight())/2;
1368 Driver->setWindowPos(posX, posY);
1372 uint getCurrentColorDepth()
1374 if (Driver && Driver->isActive())
1376 UDriver::CMode videoMode;
1377 Driver->getCurrentScreenMode(videoMode);
1378 if (!videoMode.Windowed)
1380 return videoMode.Depth;
1384 return CSystemUtils::getCurrentColorDepth();
1387 bool isWindowMaximized()
1389 UDriver::CMode screenMode;
1390 uint32 width, height;
1392 Driver->getWindowSize(width, height);
1394 return (Driver->getCurrentScreenMode(screenMode) && screenMode.Windowed &&
1395 screenMode.Width == width && screenMode.Height == height);
1398 bool getRyzomModes(std::vector<NL3D::UDriver::CMode> &videoModes, std::vector<std::string> &stringModeList, std::vector<std::string> &stringFreqList, sint &nFoundStringMode, sint &nFoundStringFreq)
1400 // default values
1401 nFoundStringMode = -1;
1402 nFoundStringFreq = -1;
1404 // mode index in original video modes
1405 sint nFoundMode = -1;
1407 // **** Init Video Modes
1408 Driver->getModes(videoModes);
1410 // Remove modes under 1024x768 (outgame ui limitation) and get the unique strings
1411 sint i, j;
1412 for (i = 0; i < (sint)videoModes.size(); ++i)
1414 if ((videoModes[i].Width < 1024) || (videoModes[i].Height < 768))
1416 videoModes.erase(videoModes.begin()+i);
1417 --i;
1419 else
1421 bool bFound = false;
1423 // create string format with width and height
1424 string res = toString(videoModes[i].Width)+" x "+toString(videoModes[i].Height);
1426 // check if video mode already found in list
1427 for (j = 0; j < (sint)stringModeList.size(); ++j)
1429 if (stringModeList[j] == res)
1431 bFound = true;
1432 break;
1436 // if not found
1437 if (!bFound)
1439 // add it to the list
1440 stringModeList.push_back(res);
1442 // process all screen sizes less or equal to desired one
1443 if ((videoModes[i].Width <= ClientCfg.Width) && (videoModes[i].Height <= ClientCfg.Height))
1445 // take first one by default
1446 if (nFoundStringMode == -1)
1448 nFoundStringMode = j;
1449 nFoundMode = i;
1451 else
1453 // then take the largest one
1454 if ((videoModes[i].Width >= videoModes[nFoundMode].Width) &&
1455 (videoModes[i].Height >= videoModes[nFoundMode].Height))
1457 nFoundStringMode = j;
1458 nFoundMode = i;
1466 // If no modes are available, fallback to windowed mode
1467 if (nFoundStringMode == -1)
1469 nlwarning("Mode %ux%u not found, fall back to windowed", (uint)ClientCfg.Width, (uint)ClientCfg.Height);
1470 ClientCfg.Windowed = true;
1471 ClientCfg.writeInt("FullScreen", 0);
1473 else
1475 // add frequencies to frequencies list
1476 for (i = 0; i < (sint)videoModes.size(); ++i)
1478 // only take exact screen sizes
1479 if (videoModes[i].Width == videoModes[nFoundMode].Width && videoModes[i].Height == videoModes[nFoundMode].Height)
1481 uint freq = videoModes[i].Frequency;
1482 std::string freqStr = toString(freq);
1484 bool bFound = false;
1486 // check if frequency already found in list
1487 for (j = 0; j < (sint)stringFreqList.size(); ++j)
1489 if (stringFreqList[j] == freqStr)
1491 bFound = true;
1492 break;
1496 if (!bFound)
1498 // if frequency is 0, take the first one else use the exact one
1499 if (nFoundStringFreq == -1 && ((ClientCfg.Frequency == 0) || (freq == ClientCfg.Frequency)))
1501 nFoundStringFreq = stringFreqList.size();
1504 stringFreqList.push_back(freqStr);
1510 return nFoundStringMode > -1;
1513 // Get float value from string. Return true if the value is relatif ( src = "+15.5" for example )
1514 bool getRelativeFloatFromString(const std::string src, float &dst)
1516 dst = 0;
1517 if (src.empty())
1518 return false;
1520 if (src[0] == '+')
1521 return fromString(src.substr(1), dst);
1522 else
1523 fromString(src, dst);
1525 return false;
1528 void updateVector(const string part, CVector &dst, float value, bool add /* = false */)
1530 string p = part;
1531 if (part.size() > 1)
1532 p = part.substr(part.size()-1, 1);
1534 if (add)
1536 if (p == "x")
1537 dst.x += value;
1538 else if (p == "y")
1539 dst.y += value;
1540 else if (p == "z")
1541 dst.z += value;
1543 else
1545 if (p == "x")
1546 dst.x = value;
1547 else if (p == "y")
1548 dst.y = value;
1549 else if (p == "z")
1550 dst.z = value;