Linux multi-monitor fullscreen support
[ryzomcore.git] / ryzom / client / src / misc.cpp
blob7cbeeebc2950e51da02612a17d018af15eb1148a
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2018 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 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 /////////////
24 // INCLUDE //
25 /////////////
26 #include "stdpch.h"
27 // Client
28 #include "misc.h"
29 #include "entity_animation_manager.h"
30 #include "entities.h"
31 #include "sheet_manager.h"
32 #include "interface_v3/interface_manager.h"
33 #include "continent_manager.h"
34 #include "world_database_manager.h"
35 #include "client_cfg.h"
36 #include "user_entity.h"
37 #include "net_manager.h"
38 #include "sky_render.h"
39 // Client Sheets
40 #include "client_sheets/item_sheet.h"
41 // 3D
42 #include "nel/3d/u_instance.h"
43 #include "nel/3d/u_instance_material.h"
44 #include "nel/3d/u_scene.h"
45 #include "nel/3d/u_driver.h"
46 #include "nel/3d/material.h"
47 // Misc
48 #include "nel/misc/file.h"
49 #include "nel/misc/i_xml.h"
50 #include "nel/misc/o_xml.h"
51 #include "nel/misc/fast_floor.h"
52 #include "nel/misc/noise_value.h"
53 #include "nel/misc/bitmap.h"
54 #include "nel/misc/system_utils.h"
55 // Game Share
56 #include "game_share/player_visual_properties.h"
57 #include "game_share/seeds.h"
58 // Georges
59 #include "nel/georges/u_form.h"
60 #include "nel/georges/u_form_elm.h"
61 #include "nel/georges/u_form_loader.h"
63 ///////////
64 // USING //
65 ///////////
66 using namespace NL3D;
67 using namespace NLMISC;
68 using namespace std;
69 using namespace NLGEORGES;
71 ////////////
72 // EXTERN //
73 ////////////
74 extern CEntityAnimationManager *EAM;
75 extern UDriver *Driver;
76 extern UMaterial GenericMat;
79 ////////////
80 // GLOBAL //
81 ////////////
82 const uint RecordVersion = 1;
83 std::set<std::string> LodCharactersNotFound;
86 //////////////
87 // FUNCTION //
88 //////////////
89 //-----------------------------------------------
90 // keepIn_NegPi_Pi :
91 //-----------------------------------------------
92 double keepIn_NegPi_Pi(double angle)
94 if(angle > Pi)
95 angle = -2*Pi+angle;
96 else if(angle < -Pi)
97 angle = 2*Pi+angle;
99 return angle;
100 }// keepIn_NegPi_Pi //
102 //-----------------------------------------------
103 // angleBetween2Vect :
104 //-----------------------------------------------
105 double angleBetween2Vect(const CVectorD &from, const CVectorD &to)
107 // Get the final local head Yaw.
108 CVector vj = from;
109 vj.z = 0;
110 CVector vk(0,0,1);
111 CVector vi = vj^vk;
113 CMatrix mat;
114 mat.setRot(vi,vj,vk,true);
116 CVector localDir = mat.inverted() * to;
117 return atan2(-localDir.x, localDir.y);
118 }// angleBetween2Vect //
120 //-----------------------------------------------
121 // makeUp :
122 // \param face : pointer on the face to make up (must not be null).
123 // \param idMakeUp : index of the make-up to apply.
124 // \warning This function does not check if 'face' is valid.
125 //-----------------------------------------------
126 void makeUp(NL3D::UInstance face, sint idMakeUp)
128 static const char *tattooStr = "visage_makeup";
130 // look for tattoo texture
131 uint numMat = face.getNumMaterials();
132 std::string texFilename;
133 for(uint k = 0; k < numMat; ++k)
135 UInstanceMaterial im = face.getMaterial(k);
136 sint numTex = im.getLastTextureStage();
137 for(sint l = 0; l <= numTex; ++l)
139 if (im.isTextureFile(l)) // one texture from a file ?
141 // see if it is the texture used for tattoos
142 texFilename = im.getTextureFileName(l);
143 // nlinfo("visage tex = %s", texFilename.c_str());
144 std::string::size_type pos = texFilename.find(tattooStr, 0);
145 if (pos != std::string::npos)
147 uint charIndex = (uint)(pos + strlen(tattooStr));
148 if (texFilename.length() >= charIndex + 2)
150 texFilename[charIndex] = '0' + (unsigned char) (idMakeUp / 10);
151 texFilename[charIndex + 1] = '0' + (unsigned char) (idMakeUp % 10);
152 im.setTextureFileName(texFilename, l);
158 }// makeUp //
160 //-----------------------------------------------
161 // qStart is the quaterion at t=0,
162 // qEnd is the quaterion at t=TAnimEnd,
163 // time is between 0 and 1. (1=> TAnimEnd)
164 //-----------------------------------------------
165 CQuat applyRotationFactor(CQuat in, float rotFactor, CQuat qStart, CQuat qEnd, float time)
167 H_AUTO ( RZ_Client_Apply_Rotation_Factor )
169 CQuat qRotTotal1, qRotTotal2;
170 qStart.invert();
171 qRotTotal1= qEnd*qStart;
172 // qRotTotal2.makeClosest(CQuat::Identity);
173 qRotTotal2= CQuat::slerp(CQuat::Identity, qRotTotal1, rotFactor);
175 // apply animation factor
176 // qRotTotal1.makeClosest(CQuat::Identity);
177 // qRotTotal2.makeClosest(CQuat::Identity);
178 CQuat qRotDelta1= CQuat::slerp(CQuat::Identity, qRotTotal1, time);
179 CQuat qRotDelta2= CQuat::slerp(CQuat::Identity, qRotTotal2, time);
181 // remove normal rotation, and apply rotFactor-ed one.
182 qRotDelta1.invert();
183 return qRotDelta2 * qRotDelta1 * in;
184 }// applyRotationFactor //
187 //-----------------------------------------------
188 // computeShortestAngle :
189 // Compute the angle between a source and a destination using the shortest way.
190 // \param from : angle between -Pi and Pi;
191 // \param to : angle between -Pi and Pi;
192 //-----------------------------------------------
193 double computeShortestAngle(double from, double to)
195 double difAngle = to - from;
196 if(difAngle < -Pi) // Angle in the wrong direction.
197 difAngle += 2.0*Pi;
198 else if(difAngle > Pi) // Angle in the wrong direction.
199 difAngle -= 2.0*Pi;
201 return difAngle;
202 }// computeShortestAngle //
204 //-----------------------------------------------
205 // readStringArray :
206 //-----------------------------------------------
207 void readStringArray(const std::string &filename, NLGEORGES::UFormLoader *formLoader, NLMISC::CSmartPtr<NLGEORGES::UForm> &form, std::map<std::string, std::string> &container)
209 // Read an array of strings
210 if(formLoader)
212 form = formLoader->loadForm(filename.c_str());
213 if(form)
215 // Get the root.
216 const UFormElm& rootElmt = form->getRootNode();
217 // Get animations.
218 const UFormElm *elmt = 0;
219 if(rootElmt.getNodeByName(&elmt, "array") == false)
220 nlwarning("readStringArray: the node 'array' is not referenced.");
221 // If the array is not empty (in fact exist).
222 if(elmt)
224 // Get the array size
225 uint arraySize;
226 elmt->getArraySize(arraySize);
227 // If there is at least 1 animation.
228 if(arraySize > 0)
230 // Get all animation for the State.
231 for(uint i = 0; i<arraySize; ++i)
233 // Get the Name
234 std::string nodeName;
235 std::string stringName;
236 if(elmt->getArrayNodeName(nodeName, i))
238 if(elmt->getArrayValue(stringName, i))
239 container.insert(make_pair(nodeName, stringName));
240 else
241 nlwarning("readStringArray: no string associated to the node '%u(%s)'.", i, nodeName.c_str());
243 else
244 nlwarning("readStringArray: node '%u', index valid.", i);
248 else
249 nlwarning("readStringArray: array node not allocated.");
251 else
252 nlwarning("readStringArray: cannot create the form from file '%s'.", filename.c_str());
254 else
255 nlwarning("readStringArray: the Loader is not allocated.");
256 }// readStringArray //
258 //-----------------------------------------------
259 // mode2Anim :
260 // Return the animset base name corresponding to the mode.
261 // \param mode : the mode to convert.
262 // \param result : this will be filed with the mode animset name.
263 // \return bool : 'true' if 'result' is filled, 'false' if left untouched.
264 //-----------------------------------------------
265 bool mode2Anim(MBEHAV::EMode mode, string &result)
267 static bool init = false;
268 static string mode2AnimArray[MBEHAV::NUMBER_OF_MODES];
270 // Is the mode valid
271 uint index = (uint)mode;
272 if(index >= MBEHAV::NUMBER_OF_MODES)
273 return false;
275 // Initialize
276 if(!init)
278 // Read mode2animset file
279 std::map<std::string, std::string> mode2Animset;
280 const std::string filename = "mode2animset.string_array";
281 NLGEORGES::UFormLoader *formLoader = UFormLoader::createLoader();
282 if(formLoader)
284 NLMISC::CSmartPtr<NLGEORGES::UForm> form;
285 readStringArray(filename, formLoader, form, mode2Animset);
287 else
288 nlwarning("mode2Anim: cannot create de loader");
289 NLGEORGES::UFormLoader::releaseLoader(formLoader);
290 // Initialize the static vector.
291 //-------------------------------
292 for(uint i=0; i<MBEHAV::NUMBER_OF_MODES; ++i)
294 const std::string modeName = MBEHAV::modeToString((MBEHAV::EMode)i);
295 std::map<std::string, std::string>::const_iterator it = mode2Animset.find(modeName);
296 if(it != mode2Animset.end())
298 mode2AnimArray[i] = (*it).second;
299 if(mode2AnimArray[i].empty())
300 nlwarning("mode2Anim: The mode '%d(%s)' has an empty animset associated.", i, modeName.c_str ());
302 // No animset for the mode.
303 else
305 mode2AnimArray[i].clear();
306 nlwarning("mode2Anim: no animset associated to the mode %d'%s'.", i, modeName.c_str());
309 // Init Done now.
310 init = true;
313 // Fill the result.
314 // Check name.
315 if(mode2AnimArray[index].empty())
316 result = mode2AnimArray[MBEHAV::NORMAL];
317 else
318 result = mode2AnimArray[index];
319 // Result filled.
320 return true;
321 }// mode2Anim //
323 //-----------------------------------------------
324 // computeAnimSet :
325 // Compute the animation set to use according to weapons, mode and race.
326 // \param animSet : result pointer.
327 // \param mode : the mode.
328 // \param animSetBaseName : basic name to construc the complet name of the animSet.
329 // \param leftHand : animSet name for the left hand.
330 // \param rightHand : animSet name for the right hand.
331 // \param lookAtItemsInHands : compute animset according to items in hands or not.
332 // \return bool : 'true' if the new animation set is the right one. 'false' if the one choosen is not the right one.
333 //-----------------------------------------------
334 bool computeAnimSet(const CAnimationSet *&animSet, MBEHAV::EMode mode, const string &animSetBaseName, const CItemSheet *itemLeftHand, const CItemSheet *itemRightHand, bool lookAtItemsInHands)
336 static std::set<std::string> UnknownAnimSet;
338 if(EAM == 0)
340 if(!ClientCfg.Light)
342 nlwarning("computeAnimSet: EAM not allocated -> cannot compute the anim set.");
344 return false;
347 string animSetRight, animSetLeft;
348 if(itemLeftHand)
349 animSetLeft = itemLeftHand->getAnimSet();
351 if(itemRightHand)
352 animSetRight = itemRightHand->getAnimSet();
354 // Get the animset name from the mode.
355 string result;
356 if(!mode2Anim(mode, result))
358 nlwarning("computeAnimSet: unknown mode '%d'.", mode);
359 result = "default";
362 // Compute the name.
363 if(lookAtItemsInHands)
364 result = animSetBaseName + "_" + result + "_" + animSetRight + "_" + animSetLeft;
365 else
366 result = animSetBaseName + "_" + result + "__";
368 // Get the animset.
369 const CAnimationSet *animSetTmp = EAM->getAnimSet(result);
370 if(animSetTmp)
372 animSet = animSetTmp;
373 return true;
375 // Bad one try something else.
376 else
378 // Up to 100 missing anim set (security).
379 if(UnknownAnimSet.size() < 100)
381 if(UnknownAnimSet.insert(result).second)
382 nlwarning("computeAnimSet: unknown Anim Set '%s' ('%u' unkowns).", result.c_str(), UnknownAnimSet.size());
384 // Try to compute the default one
385 result = animSetBaseName + "_" + "default" + "__";
386 animSetTmp = EAM->getAnimSet(result);
387 if(animSetTmp)
388 animSet = animSetTmp;
389 else
391 // Up to 100 missing anim set (security).
392 if(UnknownAnimSet.size() < 100)
394 if(UnknownAnimSet.insert(result).second)
395 nlwarning("computeAnimSet: unknown Anim Set '%s' ('%u' unkowns).", result.c_str(), UnknownAnimSet.size());
400 // Not Well done.
401 return false;
402 }// computeAnimSet //
405 //-----------------------------------------------
406 // dump :
407 // Create a file with information to debug.
408 //-----------------------------------------------
409 void dump(const std::string &name)
411 // Write information to start as the version
412 COFile fStart;
413 if(fStart.open(name + "_start.rec", false, false))
415 CVectorD currentPos = UserEntity->pos();
416 fStart.serialVersion(RecordVersion);
417 fStart.serial(currentPos);
418 // Close the File.
419 fStart.close();
421 else
422 nlwarning("dump: cannot open/create the file '%s_start.rec'.", name.c_str());
424 // Write the DB
425 IngameDbMngr.write(name + "_db.rec");
426 // Open the file.
427 COFile f;
428 if(f.open(name + ".rec", false, false))
430 // Dump entities.
431 EntitiesMngr.dump(f);
433 // Dump Client CFG.
434 ClientCfg.serial(f);
436 // Close the File.
437 f.close();
439 else
440 nlwarning("dump: cannot open/create the file '%s.rec'.", name.c_str());
443 // Open the file.
444 if(f.open(name + ".xml", false, true))
446 // Create the XML stream
447 COXml output;
448 // Init
449 if(output.init (&f, "1.0"))
451 // Open the XML Dump.
452 output.xmlPush("XML");
454 // Dump Client CFG.
455 ClientCfg.serial(output);
457 // Dump entities.
458 EntitiesMngr.dumpXML(output);
460 // Close the XML Dump.
461 output.xmlPop();
463 // Flush the stream, write all the output file
464 output.flush();
466 else
467 nlwarning("dump: cannot initialize '%s.xml'.", name.c_str());
468 // Close the File.
469 f.close();
471 else
472 nlwarning("dump: cannot open/create the file '%s.xml'.", name.c_str());
473 }// dump //
476 //-----------------------------------------------
477 // loadDump :
478 // Create a file with the current state of the client (good to report a bug).
479 //-----------------------------------------------
480 void loadDump(const std::string &name)
482 CVectorD currentPos;
484 // Load information to start as the version
485 CIFile fStart;
486 if(fStart.open(name + "_start.rec", false))
488 fStart.serialVersion(RecordVersion);
489 fStart.serial(currentPos);
490 // Close the File.
491 fStart.close();
493 else
494 nlwarning("loadDump: cannot open the file '%s_start.rec'.", name.c_str());
496 // Update the position for the vision.
497 NetMngr.setReferencePosition(currentPos);
499 // Select the closest continent from the new position.
500 class CDummyProgress : public IProgressCallback
502 void progress (float /* value */) {}
504 CDummyProgress dummy;
505 ContinentMngr.select(currentPos, dummy);
507 // Load the DB
508 IngameDbMngr.read(name + "_db.rec");
510 // Open the file.
511 CIFile f;
512 if(f.open(name + ".rec", false))
514 // Dump entities.
515 EntitiesMngr.dump(f);
517 // Close the File.
518 f.close();
520 else
521 nlwarning("loadDump: cannot open '%s.rec'.", name.c_str());
522 }// loadDump //
525 //-----------------------------------------------
526 // getLodCharacterId
527 // Get the lod character id from the scene, according to LodCharacterName. Cached.
528 // -1 if id not found.
529 //-----------------------------------------------
530 sint getLodCharacterId(UScene &scene, const string &lodCharacterName)
532 sint lodCharacterId = scene.getCLodShapeIdByName(lodCharacterName);
533 // display a warning for bad character Id, only if name was setup in the sheet
534 if(lodCharacterId==-1 && !lodCharacterName.empty() )
536 // Limited to 100 missing Lod to avoid memories problems
537 if(LodCharactersNotFound.size() < 100)
539 // Insert and display a waring
540 if(LodCharactersNotFound.insert(lodCharacterName).second)
541 nlwarning("getLodCharacterId: Not found A Character LodCharacter in the Manager: %s", lodCharacterName.c_str());
545 return lodCharacterId;
546 }// getLodCharacterId //
549 //-----------------------------------------------
550 // getItem :
551 //-----------------------------------------------
552 CItemSheet *getItem(const CGenderInfo &genderInfo, SLOTTYPE::EVisualSlot slot)
554 CEntitySheet *faceItem = SheetMngr.get(CSheetId(genderInfo.getItemName(slot)));
555 return (dynamic_cast <CItemSheet *> (faceItem));
556 }// getItem //
559 //-----------------------------------------------
560 // getColorIndex :
561 //-----------------------------------------------
562 sint getColorIndex(const CGenderInfo &genderInfo, SLOTTYPE::EVisualSlot slot)
564 CItemSheet *is = getItem(genderInfo, slot);
565 if(is == 0)
566 return 0;
567 return is->Color;
568 }// getColorIndex //
571 //-----------------------------------------------
572 // buildPropVisualA :
573 //-----------------------------------------------
574 SPropVisualA buildPropVisualA(const CGenderInfo &genderInfo)
576 SPropVisualA dest;
577 dest.PropertyA = 0;
578 // setup items
579 dest.PropertySubData.JacketModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::CHEST_SLOT], SLOTTYPE::CHEST_SLOT);
580 dest.PropertySubData.TrouserModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::LEGS_SLOT], SLOTTYPE::LEGS_SLOT);
581 dest.PropertySubData.ArmModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::ARMS_SLOT], SLOTTYPE::ARMS_SLOT);
582 dest.PropertySubData.HatModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::HEAD_SLOT], SLOTTYPE::HEAD_SLOT);
584 // setup colors (fixed or user)
585 sint col = getColorIndex(genderInfo, SLOTTYPE::CHEST_SLOT);
586 dest.PropertySubData.JacketColor = (col == -1) ? 0 : col;
588 col = getColorIndex(genderInfo, SLOTTYPE::LEGS_SLOT);
589 dest.PropertySubData.TrouserColor = (col == -1) ? 0 : col;
591 col = getColorIndex(genderInfo, SLOTTYPE::ARMS_SLOT);
592 dest.PropertySubData.ArmColor = (col == -1) ? 0 : col;
594 col = getColorIndex(genderInfo, SLOTTYPE::HEAD_SLOT);
595 dest.PropertySubData.HatColor = (col == -1) ? 0 : col;
597 // sheath are not used
598 return dest;
599 }// buildPropVisualA //
602 //-----------------------------------------------
603 // buildPropVisualB :
604 //-----------------------------------------------
605 SPropVisualB buildPropVisualB(const CGenderInfo &genderInfo)
607 // feet & hands
608 SPropVisualB dest;
609 dest.PropertyB = 0;
610 // setup items
611 dest.PropertySubData.HandsModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::HANDS_SLOT], SLOTTYPE::HANDS_SLOT);
612 dest.PropertySubData.FeetModel = SheetMngr.getVSIndex(genderInfo.Items[SLOTTYPE::FEET_SLOT], SLOTTYPE::FEET_SLOT);
614 // setup colors (fixed or user)
615 sint col = getColorIndex(genderInfo, SLOTTYPE::HANDS_SLOT);
616 dest.PropertySubData.HandsColor = (col == -1) ? 0 : col;
618 col = getColorIndex(genderInfo, SLOTTYPE::FEET_SLOT);
619 dest.PropertySubData.FeetColor = (col == -1) ? 0 : col;
621 return dest;
622 }// buildPropVisualB //
627 //-----------------------------------------------
628 // isUserColorSupported :
629 // Test whether user color is supported for this equipment
630 //-----------------------------------------------
631 bool isUserColorSupported(const CPlayerSheet::CEquipment &equip)
633 NLMISC::CSheetId si;
634 if(!si.buildSheetId(equip.Item))
635 return false;
637 CItemSheet *is = dynamic_cast<CItemSheet *>(SheetMngr.get(si));
638 if(!is)
639 return false;
641 return is->Color == -1; // user color supported by the item
642 }// isUserColorSupported //
644 //-----------------------------------------------
645 // isUserColorSupported :
646 // Test whether user color is supported for a given visual slot
647 //-----------------------------------------------
648 bool isUserColorSupported(const CPlayerSheet &playerSheet, SLOTTYPE::EVisualSlot vs)
650 switch(vs)
652 case SLOTTYPE::CHEST_SLOT: return isUserColorSupported(playerSheet.Body);
653 case SLOTTYPE::LEGS_SLOT: return isUserColorSupported(playerSheet.Legs);
654 case SLOTTYPE::HEAD_SLOT: return isUserColorSupported(playerSheet.Head);
655 case SLOTTYPE::ARMS_SLOT: return isUserColorSupported(playerSheet.Arms);
656 case SLOTTYPE::HANDS_SLOT: return isUserColorSupported(playerSheet.Hands);
657 case SLOTTYPE::FEET_SLOT: return isUserColorSupported(playerSheet.Feet);
658 case SLOTTYPE::RIGHT_HAND_SLOT: return isUserColorSupported(playerSheet.ObjectInRightHand);
659 case SLOTTYPE::LEFT_HAND_SLOT: return isUserColorSupported(playerSheet.ObjectInLeftHand);
660 default: break;
662 return false;
663 }// isUserColorSupported //
666 //-----------------------------------------------
667 // getColor :
668 //-----------------------------------------------
669 sint8 getColor(const CPlayerSheet::CEquipment &equip)
671 NLMISC::CSheetId si;
672 if(!si.buildSheetId(equip.Item))
673 return -2;
675 CItemSheet *is = dynamic_cast<CItemSheet *>(SheetMngr.get(si));
676 if(!is)
677 return -2;
679 if(is->Color == -1) // user color supported by the item
681 return equip.Color;
683 else
685 return is->Color;
687 }// getColor //
689 //-----------------------------------------------
690 // buildPropVisualA :
691 //-----------------------------------------------
692 SPropVisualA buildPropVisualA(const CPlayerSheet &playerSheet)
694 SPropVisualA vp;
695 // setup items
696 vp.PropertySubData.JacketModel = SheetMngr.getVSIndex(playerSheet.Body.Item, SLOTTYPE::CHEST_SLOT);
697 vp.PropertySubData.TrouserModel = SheetMngr.getVSIndex(playerSheet.Legs.Item, SLOTTYPE::LEGS_SLOT);
698 vp.PropertySubData.ArmModel = SheetMngr.getVSIndex(playerSheet.Arms.Item, SLOTTYPE::ARMS_SLOT);
699 vp.PropertySubData.HatModel = SheetMngr.getVSIndex(playerSheet.Head.Item, SLOTTYPE::HEAD_SLOT);
700 vp.PropertySubData.WeaponRightHand = SheetMngr.getVSIndex(playerSheet.ObjectInRightHand.Item, SLOTTYPE::RIGHT_HAND_SLOT);
701 vp.PropertySubData.WeaponLeftHand = SheetMngr.getVSIndex(playerSheet.ObjectInLeftHand.Item, SLOTTYPE::LEFT_HAND_SLOT);
703 // setup colors
704 vp.PropertySubData.JacketColor = getColor(playerSheet.Body);
705 vp.PropertySubData.TrouserColor = getColor(playerSheet.Legs);
706 vp.PropertySubData.ArmColor = getColor(playerSheet.Arms);
707 vp.PropertySubData.HatColor = getColor(playerSheet.Head);
709 return vp;
710 }// buildPropVisualA //
712 //-----------------------------------------------
713 // buildPropVisualB :
714 //-----------------------------------------------
715 SPropVisualB buildPropVisualB(const CPlayerSheet &playerSheet)
717 SPropVisualB vp;
718 // setup items
719 vp.PropertySubData.Name = 0;
720 vp.PropertySubData.HandsModel = SheetMngr.getVSIndex(playerSheet.Hands.Item, SLOTTYPE::HANDS_SLOT);
721 vp.PropertySubData.FeetModel = SheetMngr.getVSIndex(playerSheet.Feet.Item, SLOTTYPE::FEET_SLOT);
723 // setup colors
724 vp.PropertySubData.HandsColor = getColor(playerSheet.Hands);
725 vp.PropertySubData.FeetColor = getColor(playerSheet.Feet);
727 return vp;
728 }// buildPropVisualB //
731 //-----------------------------------------------
732 // drawBox :
733 // Draw a Box from 2 vectors.
734 //-----------------------------------------------
735 void drawBox(const CVector &vMin, const CVector &vMax, const CRGBA &color)
737 CLineColor line;
738 line.Color0 = color;
739 line.Color1 = color;
740 // Bottom quad
741 line = CLine(CVector(vMin.x,vMin.y,vMin.z), CVector(vMax.x,vMin.y,vMin.z));
742 Driver->drawLine(line, GenericMat);
743 line = CLine(CVector(vMax.x,vMin.y,vMin.z), CVector(vMax.x,vMax.y,vMin.z));
744 Driver->drawLine(line, GenericMat);
745 line = CLine(CVector(vMax.x,vMax.y,vMin.z), CVector(vMin.x,vMax.y,vMin.z));
746 Driver->drawLine(line, GenericMat);
747 line = CLine(CVector(vMin.x,vMax.y,vMin.z), CVector(vMin.x,vMin.y,vMin.z));
748 Driver->drawLine(line, GenericMat);
749 // Top quad
750 line = CLine(CVector(vMin.x,vMin.y,vMax.z), CVector(vMax.x,vMin.y,vMax.z));
751 Driver->drawLine(line, GenericMat);
752 line = CLine(CVector(vMax.x,vMin.y,vMax.z), CVector(vMax.x,vMax.y,vMax.z));
753 Driver->drawLine(line, GenericMat);
754 line = CLine(CVector(vMax.x,vMax.y,vMax.z), CVector(vMin.x,vMax.y,vMax.z));
755 Driver->drawLine(line, GenericMat);
756 line = CLine(CVector(vMin.x,vMax.y,vMax.z), CVector(vMin.x,vMin.y,vMax.z));
757 Driver->drawLine(line, GenericMat);
758 // Sides Quad
759 line = CLine(CVector(vMin.x,vMin.y,vMin.z), CVector(vMin.x,vMin.y,vMax.z));
760 Driver->drawLine(line, GenericMat);
761 line = CLine(CVector(vMax.x,vMin.y,vMin.z), CVector(vMax.x,vMin.y,vMax.z));
762 Driver->drawLine(line, GenericMat);
763 line = CLine(CVector(vMax.x,vMax.y,vMin.z), CVector(vMax.x,vMax.y,vMax.z));
764 Driver->drawLine(line, GenericMat);
765 line = CLine(CVector(vMin.x,vMax.y,vMin.z), CVector(vMin.x,vMax.y,vMax.z));
766 Driver->drawLine(line, GenericMat);
767 }// drawBox //
770 //-----------------------------------------------
771 // drawSphere :
772 // Draw a Sphere
773 //-----------------------------------------------
774 void drawSphere(const NLMISC::CVector &center, float radius, const NLMISC::CRGBA &color)
776 const uint numSegs= 12;
777 CLineColor line;
778 line.Color0 = color;
779 line.Color1 = color;
780 // For all faces
781 for(uint face=0;face<3;face++)
783 for(uint i=0;i<numSegs;i++)
785 float angStart= float(i*2*Pi/numSegs);
786 float angEnd= float(((i+1)%numSegs)*2*Pi/numSegs);
787 line.V0= radius*CVector(cosf(angStart), sinf(angStart), 0.f);
788 line.V1= radius*CVector(cosf(angEnd), sinf(angEnd), 0.f);
789 if(face==1)
791 swap(line.V0.x, line.V0.z);
792 swap(line.V1.x, line.V1.z);
794 else if(face==2)
796 swap(line.V0.y, line.V0.z);
797 swap(line.V1.y, line.V1.z);
799 line.V0+= center;
800 line.V1+= center;
801 Driver->drawLine(line, GenericMat);
807 //-----------------------------------------------
808 // getSeedsFromDB :
809 //-----------------------------------------------
810 void getSeedsFromDB(CSeeds &dest)
812 CInterfaceManager *im =CInterfaceManager::getInstance();
813 nlctassert(sizeof(CSeeds::TUInt) == 4); // excpect that the number of each seed type is encoded on 32 bits
814 // if this assert at compile, change the following code
815 string ls = CWidgetManager::getInstance()->getParser()->getDefine("money_1");
816 string ms = CWidgetManager::getInstance()->getParser()->getDefine("money_2");
817 string bs = CWidgetManager::getInstance()->getParser()->getDefine("money_3");
818 string vbs = CWidgetManager::getInstance()->getParser()->getDefine("money_4");
820 dest = CSeeds(NLGUI::CDBManager::getInstance()->getDbProp(ls)->getValue32(),
821 NLGUI::CDBManager::getInstance()->getDbProp(ms)->getValue32(),
822 NLGUI::CDBManager::getInstance()->getDbProp(bs)->getValue32(),
823 NLGUI::CDBManager::getInstance()->getDbProp(vbs)->getValue32());
824 } // getSeedsFromDB //
827 //-----------------------------------------------
828 // setVect :
829 // Change a 'direction' vector.
830 // \param vectToChange : the vector to change.
831 // \param vect : new vector to use.
832 // \param compute : adjust the param 'vect' to be valid or leave the old one unchanged if impossible.
833 // \param check : warning if the param 'vect' is not valid (vector Null) even with compute=true.
834 // \return bool : 'true' if the vectToChange has been filled, else 'false'.
835 //-----------------------------------------------
836 bool setVect(CVector &vectToChange, const CVector &vect, bool compute, bool check)
838 // Compute the vector.
839 if(compute)
841 CVector vectTmp(vect);
842 // No need of the Z component for the vector.
843 vectTmp.z = 0.0f;
844 // Check vector.
845 if(vectTmp != CVector::Null)
847 vectTmp.normalize();
848 vectToChange = vectTmp;
849 return true;
851 // Bad Vector -> vectToChange remains the same
852 else
854 // Warning
855 if(check)
856 nlwarning("setVect: cannot compute the vector, keep the old one.");
857 return false;
861 // Bad Vector -> vectToChange remains the same
862 if(vect == CVector::Null)
864 // Warning
865 if(check)
866 nlwarning("setVect: param is a vector Null, vectToChange remains the same.");
867 return false;
870 // Set the new front vector.
871 vectToChange = vect;
872 return true;
873 }// setVect //
875 NLMISC::CRGBA interpClientCfgColor(const string &src, string &dest)
877 CRGBA color = CRGBA::White;
878 if (src.size() >= 3)
880 if (src[0] == '&')
882 string::size_type nextPos = src.find('&', 1);
883 if (nextPos != string::npos)
885 std::string colorCode;
886 colorCode.resize(nextPos - 1);
887 for(uint k = 0; k < nextPos - 1; ++k)
889 colorCode[k] = tolower((char) src[k + 1]); // TODO: toLowerAscii
891 std::map<std::string, CClientConfig::SSysInfoParam>::const_iterator it = ClientCfg.SystemInfoParams.find(colorCode);
892 if (it != ClientCfg.SystemInfoParams.end())
894 color = it->second.Color;
896 dest = src.substr(nextPos + 1);
898 else
900 dest = src;
903 else
905 dest = src;
908 else
910 dest = src;
912 return color;
915 std::string getStringCategory(const string &src, string &dest, bool alwaysAddSysByDefault)
917 std::string str = getStringCategoryIfAny(src, dest);
918 if (alwaysAddSysByDefault)
919 return str.empty()?"SYS":str;
920 else
921 return str;
925 std::string getStringCategoryIfAny(const string &src, string &dest)
927 std::string colorCode;
928 if (src.size() >= 3)
930 size_t startPos = 0;
932 // Skip <NEW> or <CHG> if present at beginning
933 string preTag;
934 const size_t PreTagSize = 5;
935 static const string newTag = "<NEW>";
936 if ( (src.size() >= PreTagSize) && (src.substr( 0, PreTagSize ) == newTag) )
938 startPos = PreTagSize;
939 preTag = newTag;
941 static const string chgTag = "<CHG>";
942 if ( (src.size() >= PreTagSize) && (src.substr( 0, PreTagSize ) == chgTag) )
944 startPos = PreTagSize;
945 preTag = chgTag;
948 if (src[startPos] == '&')
950 string::size_type nextPos = src.find('&', startPos+1);
951 if (nextPos != string::npos)
953 size_t codeSize = nextPos - startPos - 1;
954 colorCode.resize( codeSize );
955 for(ptrdiff_t k = 0; k < (ptrdiff_t)codeSize; ++k)
957 colorCode[k] = tolower((char) src[k + startPos + 1]); // TODO: toLowerAscii
959 string destTmp;
960 if ( startPos != 0 )
961 destTmp = preTag; // leave <NEW> or <CHG> in the dest string
962 destTmp += src.substr(nextPos + 1);
963 dest = destTmp;
965 else
967 dest = src;
970 else
972 dest = src;
975 else
977 dest = src;
979 return colorCode;
983 // ***************************************************************************
984 sint ucstrnicmp(const ucstring &s0, uint p0, uint n0, const ucstring &s1) // OLD
986 // start
987 const ucchar *start1= s1.c_str();
988 uint lenS1= (uint)s1.size();
989 const ucchar *start0= s0.c_str();
990 uint lenS0= (uint)s0.size();
991 if(p0!=0)
993 if(p0<lenS0)
995 start0+= p0;
996 lenS0-= p0;
998 else
1000 start0+= lenS0; // points to '\0'
1001 lenS0= 0;
1004 lenS0= min(lenS0, n0);
1006 // compare character to character
1007 while(lenS0>0 && lenS1>0)
1009 ucchar c0= toLower(*start0++);
1010 ucchar c1= toLower(*start1++);
1011 if(c0!=c1)
1012 return c0<c1?-1:+1;
1013 lenS0--;
1014 lenS1--;
1017 // return -1 if s1>s0, 1 if s0>s1, or 0 if equals
1018 if(lenS1>0)
1019 return -1;
1020 else if(lenS0>0)
1021 return 1;
1022 else
1023 return 0;
1027 // *******************************************************************************************
1028 float computeUniformNoise(const NLMISC::CNoiseValue &nv, const CVector &pos)
1030 NLMISC::OptFastFloorBegin();
1031 float value = nv.eval(pos);
1032 value = 10.f * fmodf(value, 0.1f); // make repartition more uniform
1033 NLMISC::OptFastFloorEnd();
1034 return value;
1038 // ***************************************************************************
1039 void computeCurrentFovAspectRatio(float &fov, float &ar)
1041 // compute the fov
1042 fov = (float)(ClientCfg.FoV*Pi/180.0);
1044 // get the screen aspect ratio from CFG
1045 ar = ClientCfg.ScreenAspectRatio;
1047 // if Driver is not created, we can't get current screen mode
1048 if (!Driver) return;
1050 // if windowed, must modulate aspect ratio by (WindowResolution / ScreenResolution)
1051 if(ClientCfg.Windowed)
1053 uint32 wndW, wndH;
1054 Driver->getWindowSize(wndW, wndH);
1055 UDriver::CMode mode;
1056 Driver->getCurrentScreenMode(mode);
1057 if(wndH)
1059 // compute window aspect ratio
1060 float arWnd= float(wndW) / float(wndH);
1061 if (ar == 0.f)
1063 // auto mode, we are using window aspect ratio
1064 ar = arWnd;
1066 else if (mode.Width && mode.Height)
1068 // compute screen aspect ratio
1069 float arScreen= float(mode.Width) / float(mode.Height);
1070 ar *= arWnd / arScreen;
1074 // if fullscreen, must modulate aspect ratio by ScreenResolution
1075 else
1077 if (ar == 0.f)
1079 UDriver::CMode mode;
1080 Driver->getCurrentScreenMode(mode);
1081 if(mode.Height)
1083 ar = float(mode.Width) / float(mode.Height);
1089 // ***************************************************************************
1090 void drawDisc(CBitmap &dest, float x, float y, float radius, const CRGBA &color, bool additif /*= false*/, uint numSegs /*= 127*/)
1092 CPolygon2D poly;
1093 poly.Vertices.resize(numSegs);
1094 for(uint k = 0; k < numSegs; ++k)
1096 poly.Vertices[k].set(x + radius * (float) cos(k / (float) numSegs * 2 * Pi), y + radius * (float) sin(k / (float) numSegs * 2 * Pi));
1098 CPolygon2D::TRasterVect rasters;
1099 sint minY;
1100 poly.computeOuterBorders(rasters, minY);
1101 sint maxY = std::min((sint) dest.getHeight(), (sint) rasters.size() + minY);
1102 for (sint y = std::max((sint) 0, minY); y < maxY; ++y)
1104 nlassert(y >= 0 && y < (sint) dest.getHeight());
1105 sint minX = std::max((sint) 0, rasters[y - minY].first);
1106 sint maxX = std::min((sint) dest.getWidth(), rasters[y - minY].second);
1107 if (maxX > minX)
1109 CRGBA *pt = (CRGBA *) &dest.getPixels(0)[0];
1110 pt += y * dest.getWidth() + minX;
1111 const CRGBA *endPt = pt + (maxX - minX);
1112 while (pt != endPt)
1114 if (additif)
1116 pt->add(*pt, color);
1118 else
1120 *pt = color;
1122 ++ pt;
1129 // *************************************************************************************************
1130 NL3D::UScene *getSkyScene()
1132 if (ContinentMngr.cur() && !ContinentMngr.cur()->Indoor)
1134 CSky &sky = ContinentMngr.cur()->CurrentSky;
1135 if (sky.getScene())
1137 return sky.getScene();
1139 else
1141 return SkyScene; // old sky rendering
1144 return NULL;
1147 // *************************************************************************************************
1148 void setEmissive(NL3D::UInstance instance, const NLMISC::CRGBA &color)
1150 if (instance.empty()) return;
1151 for(uint k = 0; k < instance.getNumMaterials(); ++k)
1153 NL3D::UInstanceMaterial mat = instance.getMaterial(k);
1154 mat.setEmissive(color);
1158 // *************************************************************************************************
1159 void setDiffuse(NL3D::UInstance instance, bool onOff, const NLMISC::CRGBA &color)
1161 if (instance.empty()) return;
1162 for(uint k = 0; k < instance.getNumMaterials(); ++k)
1164 NL3D::UInstanceMaterial mat = instance.getMaterial(k);
1165 if (mat.isLighted())
1167 CRGBA src;
1168 if (onOff)
1170 src = mat.getDiffuse();
1171 src.R = color.R;
1172 src.G = color.G;
1173 src.B = color.B;
1175 else
1177 NL3D::UMaterial matShape = instance.getShape().getMaterial(k);
1178 src = matShape.getDiffuse();
1179 src.A = mat.getDiffuse().A;
1181 mat.setDiffuse(src);
1183 else
1185 CRGBA src;
1186 if (onOff)
1188 src = mat.getColor();
1189 src.R = color.R;
1190 src.G = color.G;
1191 src.B = color.B;
1193 else
1195 NL3D::UMaterial matShape = instance.getShape().getMaterial(k);
1196 src = matShape.getColor();
1197 src.A = mat.getColor().A;
1199 mat.setColor(src);
1206 // *************************************************************************************************
1207 void makeInstanceTransparent(UInstance &inst, uint8 opacity, bool disableZWrite)
1209 UShape shape= inst.getShape();
1210 if(shape.empty())
1211 return;
1212 uint numMats= shape.getNumMaterials();
1213 if(numMats==0)
1214 return;
1215 if(numMats!=inst.getNumMaterials())
1216 return;
1218 // instance transparent or not?
1219 if (opacity == 255)
1221 // reset default shape opacity / transparency
1222 inst.setOpacity(shape.getDefaultOpacity());
1223 inst.setTransparency(shape.getDefaultTransparency());
1224 inst.setBypassLODOpacityFlag(false);
1226 else
1228 // Will have some blend material => sure not to be rendered in Opaque pass
1229 inst.setOpacity(false);
1230 inst.setTransparency(true);
1231 inst.setBypassLODOpacityFlag(true); // these flags prevails over the current lods flags for multi-lod objects
1234 // set all materials
1235 for (uint32 j = 0; j < numMats; ++j)
1237 NL3D::UInstanceMaterial matInst = inst.getMaterial(j);
1238 NL3D::UMaterial matShape= shape.getMaterial(j);
1240 // disalbe zwrite?
1241 if(disableZWrite)
1242 matInst.setZWrite(false);
1243 else
1244 matInst.setZWrite(matShape.getZWrite());
1246 // if no more transparent
1247 if (opacity == 255)
1249 // reset to default
1250 matInst.setBlend(matShape.getBlend());
1251 matInst.setBlendFunc((NL3D::UInstanceMaterial::TBlend)matShape.getSrcBlend(),
1252 (NL3D::UInstanceMaterial::TBlend)matShape.getDstBlend());
1253 // if orginal material is opaque or additif and has no alpha test, then ensure restore last tex env if needed
1254 CMaterial *destInternalMat = matInst.getObjectPtr();
1255 if (!matShape.getBlend() && !matShape.getAlphaTest())
1257 if (destInternalMat->getShader() == CMaterial::Normal)
1259 CMaterial *srcInternalMat = matShape.getObjectPtr();
1260 uint numTex = 0;
1261 for (;numTex < 4 && srcInternalMat->getTexture(numTex) != NULL; ++numTex) {}
1262 if (numTex > 0)
1264 if (srcInternalMat->getTexEnvMode(numTex - 1) != destInternalMat->getTexEnvMode(numTex - 1))
1266 destInternalMat->setTexEnvMode(numTex - 1, srcInternalMat->getTexEnvMode(numTex - 1));
1271 if (destInternalMat->getShader() == CMaterial::Normal)
1273 // if !lighted, restore color
1274 if (!destInternalMat->isLighted())
1276 CMaterial *srcInternalMat = matShape.getObjectPtr();
1277 // restore alpha in color
1278 CRGBA color = destInternalMat->getColor();
1279 color.A = srcInternalMat->getColor().A;
1280 destInternalMat->setColor(color);
1284 else
1286 // Enable blend
1287 matInst.setBlend(true);
1288 // If default is ???/one or , then use a srcalpha/one (eg: for Diamond-like weapons)
1289 if(matShape.getBlend() && (sint32)matShape.getDstBlend()==(sint32)NL3D::UInstanceMaterial::one)
1290 matInst.setBlendFunc(NL3D::UInstanceMaterial::srcalpha, NL3D::UInstanceMaterial::one);
1291 // else use a standard srcalpha/invsrcalpha
1292 else
1293 matInst.setBlendFunc(NL3D::UInstanceMaterial::srcalpha, NL3D::UInstanceMaterial::invsrcalpha);
1294 // if orginal material is opaque or additif and has no alpha test, then ensure that the alpha output is 'diffuse'
1295 CMaterial *internalMat = matInst.getObjectPtr();
1296 if (!matShape.getBlend() && !matShape.getAlphaTest())
1298 if (internalMat->getShader() == CMaterial::Normal)
1300 uint numTex = 0;
1301 for (;numTex < 4 && internalMat->getTexture(numTex) != NULL; ++numTex) {}
1302 if (numTex > 0)
1304 internalMat->texEnvOpAlpha(numTex - 1, CMaterial::Replace);
1305 // if material is unlighted, then use the constant at this stage to set the alpha
1306 internalMat->texEnvArg0Alpha(numTex - 1, CMaterial::Diffuse, CMaterial::SrcAlpha);
1310 if (internalMat->getShader() == CMaterial::Normal)
1312 if (!internalMat->isLighted())
1314 // replace alpha in color
1315 CRGBA color = internalMat->getColor();
1316 color.A = opacity;
1317 internalMat->setColor(color);
1322 // suppose that default opacity is always 255
1323 if (matInst.isLighted())
1325 matInst.setOpacity(opacity);
1328 matInst.setAlphaTestThreshold(matShape.getAlphaTestThreshold()*((float)opacity)/255.0f);
1332 void setVideoMode(const UDriver::CMode &mode)
1334 UDriver::CMode oldMode, newMode = mode;
1335 oldMode.Windowed = true; // getCurrentScreenMode may fail if first init ...
1336 Driver->getCurrentScreenMode(oldMode);
1337 bool wasMaximized = isWindowMaximized();
1338 if (!Driver->setMode(newMode) && !newMode.Windowed)
1340 // failed to switch to mode, fall back to windowed
1341 newMode.Windowed = true;
1342 ClientCfg.Windowed = true;
1343 ClientCfg.writeInt("FullScreen", 0);
1345 // set the window mode
1346 Driver->setMode(newMode);
1348 bool isMaximized = isWindowMaximized();
1349 if (oldMode.Windowed && !newMode.Windowed) // going to fullscreen ?
1351 /*CInterfaceManager *pIM = CInterfaceManager::getInstance();
1352 pIM->movePointerAbs((sint32) mode.Width / 2, (sint32) mode.Height / 2);
1353 Driver->setMousePos(0.5f, 0.5f);*/
1355 else if ((!oldMode.Windowed || wasMaximized) && (newMode.Windowed && !isMaximized)) // leaving fullscreen ?
1357 UDriver::CMode screenMode;
1359 uint32 posX = 0;
1360 uint32 posY = 0;
1362 if (Driver->getCurrentScreenMode(screenMode))
1364 // position is not saved in config so center the window
1365 posX = (screenMode.Width - Driver->getWindowWidth())/2;
1366 posY = (screenMode.Height - Driver->getWindowHeight())/2;
1369 Driver->setWindowPos(posX, posY);
1373 uint getCurrentColorDepth()
1375 if (Driver && Driver->isActive())
1377 UDriver::CMode videoMode;
1378 Driver->getCurrentScreenMode(videoMode);
1379 if (!videoMode.Windowed)
1381 return videoMode.Depth;
1385 return CSystemUtils::getCurrentColorDepth();
1388 bool isWindowMaximized()
1390 UDriver::CMode screenMode;
1391 uint32 width, height;
1393 Driver->getWindowSize(width, height);
1395 return (Driver->getCurrentScreenMode(screenMode) && screenMode.Windowed &&
1396 screenMode.Width == width && screenMode.Height == height);
1399 bool getRyzomModes(std::vector<NL3D::UDriver::CMode> &videoModes, std::vector<std::string> &stringModeList, std::vector<std::string> &stringFreqList, sint &nFoundStringMode, sint &nFoundStringFreq)
1401 // default values
1402 nFoundStringMode = -1;
1403 nFoundStringFreq = -1;
1405 // mode index in original video modes
1406 sint nFoundMode = -1;
1408 // **** Init Video Modes
1409 Driver->getModes(videoModes);
1411 // TODO: for resolutions below 1024x768, could use automatic UI scaling like in login/outgame
1412 // Remove modes under 1024x768 (outgame ui limitation) and get the unique strings
1413 sint i, j;
1414 for (i = 0; i < (sint)videoModes.size(); ++i)
1416 if ((videoModes[i].Width < 1024) || (videoModes[i].Height < 768))
1418 videoModes.erase(videoModes.begin()+i);
1419 --i;
1421 else
1423 bool bFound = false;
1425 // create string format with width and height
1426 string res = toString(videoModes[i].Width)+" x "+toString(videoModes[i].Height);
1428 if (!videoModes[i].DisplayDevice.empty())
1429 res += toString(" (%s)", videoModes[i].DisplayDevice.c_str());
1431 // check if video mode already found in list
1432 for (j = 0; j < (sint)stringModeList.size(); ++j)
1434 if (stringModeList[j] == res)
1436 bFound = true;
1437 break;
1441 // if not found
1442 if (!bFound)
1444 // add it to the list
1445 stringModeList.push_back(res);
1447 // process all screen sizes less or equal to desired one
1448 if ((videoModes[i].Width <= ClientCfg.Width) && (videoModes[i].Height <= ClientCfg.Height))
1450 // take first one by default
1451 if (nFoundStringMode == -1)
1453 nFoundStringMode = j;
1454 nFoundMode = i;
1456 else
1458 // then take the largest one
1459 if ((videoModes[i].Width >= videoModes[nFoundMode].Width) &&
1460 (videoModes[i].Height >= videoModes[nFoundMode].Height))
1462 nFoundStringMode = j;
1463 nFoundMode = i;
1471 // If no modes are available, fallback to windowed mode
1472 if (nFoundStringMode == -1)
1474 nlwarning("Mode %ux%u not found, fall back to windowed", (uint)ClientCfg.Width, (uint)ClientCfg.Height);
1475 ClientCfg.Windowed = true;
1476 ClientCfg.writeInt("FullScreen", 0);
1478 else
1480 // add frequencies to frequencies list
1481 for (i = 0; i < (sint)videoModes.size(); ++i)
1483 // only take exact screen sizes
1484 if (videoModes[i].Width == videoModes[nFoundMode].Width && videoModes[i].Height == videoModes[nFoundMode].Height)
1486 uint freq = videoModes[i].Frequency;
1487 std::string freqStr = toString(freq);
1489 bool bFound = false;
1491 // check if frequency already found in list
1492 for (j = 0; j < (sint)stringFreqList.size(); ++j)
1494 if (stringFreqList[j] == freqStr)
1496 bFound = true;
1497 break;
1501 if (!bFound)
1503 // if frequency is 0, take the first one else use the exact one
1504 if (nFoundStringFreq == -1 && ((ClientCfg.Frequency == 0) || (freq == ClientCfg.Frequency)))
1506 nFoundStringFreq = stringFreqList.size();
1509 stringFreqList.push_back(freqStr);
1515 return nFoundStringMode > -1;
1518 // Get float value from string. Return true if the value is relatif ( src = "+15.5" for example )
1519 bool getRelativeFloatFromString(const std::string src, float &dst)
1521 dst = 0;
1522 if (src.empty())
1523 return false;
1525 if (src[0] == '+')
1526 return fromString(src.substr(1), dst);
1527 else
1528 fromString(src, dst);
1530 return false;
1533 void updateVector(const string part, CVector &dst, float value, bool add /* = false */)
1535 string p = part;
1536 if (part.size() > 1)
1537 p = part.substr(part.size()-1, 1);
1539 if (add)
1541 if (p == "x")
1542 dst.x += value;
1543 else if (p == "y")
1544 dst.y += value;
1545 else if (p == "z")
1546 dst.z += value;
1548 else
1550 if (p == "x")
1551 dst.x = value;
1552 else if (p == "y")
1553 dst.y = value;
1554 else if (p == "z")
1555 dst.z = value;