From a4c804b49ec872b71bd5a0167c3ad45704a3cc30 Mon Sep 17 00:00:00 2001 From: adiblol Date: Thu, 8 Mar 2012 19:32:05 +0100 Subject: [PATCH] Initial commit, Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch --- src/CBot.dll | Bin 0 -> 434176 bytes src/CBot/CBot.aps | Bin 0 -> 44112 bytes src/CBot/CBot.cpp | 4055 ++++++++++ src/CBot/CBot.dsp | 158 + src/CBot/CBot.dsw | 44 + src/CBot/CBot.h | 1611 ++++ src/CBot/CBot.lib | Bin 0 -> 107676 bytes src/CBot/CBot.opt | Bin 0 -> 53760 bytes src/CBot/CBot.plg | 61 + src/CBot/CBot.rc | 279 + src/CBot/CBotAddExpr.cpp | 128 + src/CBot/CBotClass.cpp | 867 +++ src/CBot/CBotCompExpr.cpp | 117 + src/CBot/CBotDll.h | 1185 +++ src/CBot/CBotFunction.cpp | 1634 ++++ src/CBot/CBotIf.cpp | 145 + src/CBot/CBotProgram.cpp | 1102 +++ src/CBot/CBotStack.cpp | 1460 ++++ src/CBot/CBotString.cpp | 588 ++ src/CBot/CBotToken.cpp | 540 ++ src/CBot/CBotToken.h | 23 + src/CBot/CBotTwoOpExpr ordre inversé.cpp | 302 + src/CBot/CBotTwoOpExpr.cpp | 552 ++ src/CBot/CBotVar.cpp | 2231 ++++++ src/CBot/CBotWhile.cpp | 1413 ++++ src/CBot/ClassFILE.cpp | 412 + src/CBot/Copie de CBot.rc | 184 + src/CBot/Copie de CBotTwoOpExpr.cpp | 295 + src/CBot/StringFunctions.cpp | 420 ++ src/CBot/TestCBot/B.txt | 18 + src/CBot/TestCBot/BUG2.txt | 107 + src/CBot/TestCBot/CBotConsoleDlg.cpp | 205 + src/CBot/TestCBot/CBotConsoleDlg.h | 69 + src/CBot/TestCBot/ChildFrm.cpp | 58 + src/CBot/TestCBot/ChildFrm.h | 50 + src/CBot/TestCBot/Deleted.txt | 23 + src/CBot/TestCBot/MaClass.txt | 16 + src/CBot/TestCBot/MainFrm.cpp | 100 + src/CBot/TestCBot/MainFrm.h | 56 + src/CBot/TestCBot/Mc2.txt | 4 + src/CBot/TestCBot/Mon fichier.txt | 2 + src/CBot/TestCBot/Nop.txt | 4 + src/CBot/TestCBot/POS.txt | 14 + src/CBot/TestCBot/PerformDlg.cpp | 161 + src/CBot/TestCBot/PerformDlg.h | 62 + src/CBot/TestCBot/Routines.cpp | 139 + src/CBot/TestCBot/StdAfx.cpp | 6 + src/CBot/TestCBot/StdAfx.h | 26 + src/CBot/TestCBot/T.txt | 4 + src/CBot/TestCBot/TESTALL.txt | 161 + src/CBot/TestCBot/TestCB1.txt | 18 + src/CBot/TestCBot/TestCBot.clw | 316 + src/CBot/TestCBot/TestCBot.cpp | 253 + src/CBot/TestCBot/TestCBot.dsp | 201 + src/CBot/TestCBot/TestCBot.h | 64 + src/CBot/TestCBot/TestCBot.rc | 564 ++ src/CBot/TestCBot/TestCBot1.txt | 27 + src/CBot/TestCBot/TestCBot3.txt | 24 + src/CBot/TestCBot/TestCBotDoc.cpp | 683 ++ src/CBot/TestCBot/TestCBotDoc.h | 64 + src/CBot/TestCBot/TestCBotView.cpp | 126 + src/CBot/TestCBot/TestCBotView.h | 64 + src/CBot/TestCBot/TestNull.txt | 15 + src/CBot/TestCBot/TestRestoreState.txt | 67 + src/CBot/TestCBot/TestStatic.txt | 31 + src/CBot/TestCBot/TestStr.txt | 17 + src/CBot/TestCBot/Z.txt | 14 + src/CBot/TestCBot/array.txt | 24 + src/CBot/TestCBot/a§1.txt | 96 + src/CBot/TestCBot/bug.txt | 12 + src/CBot/TestCBot/bugmw.txt | 9 + src/CBot/TestCBot/ccc.txt | 8 + src/CBot/TestCBot/enum.txt | 9 + src/CBot/TestCBot/fibo.txt | 25 + src/CBot/TestCBot/file.txt | 70 + src/CBot/TestCBot/h.txt | 5 + src/CBot/TestCBot/include.txt | 27 + src/CBot/TestCBot/intrinsic.txt | 16 + src/CBot/TestCBot/methode1.txt | 57 + src/CBot/TestCBot/methode2.txt | 50 + src/CBot/TestCBot/mp1.txt | 25 + src/CBot/TestCBot/mp2.txt | 28 + src/CBot/TestCBot/mw.txt | 16 + src/CBot/TestCBot/null.txt | 5 + src/CBot/TestCBot/opnew.txt | 20 + src/CBot/TestCBot/plante.txt | 25 + src/CBot/TestCBot/pointer.txt | 41 + src/CBot/TestCBot/postinc.txt | 7 + src/CBot/TestCBot/radar.txt | 39 + src/CBot/TestCBot/res/TestCBot.ico | Bin 0 -> 1078 bytes src/CBot/TestCBot/res/TestCBot.rc2 | 13 + src/CBot/TestCBot/res/TestCBotDoc.ico | Bin 0 -> 1078 bytes src/CBot/TestCBot/res/Toolbar.bmp | Bin 0 -> 1198 bytes src/CBot/TestCBot/resource.h | 30 + src/CBot/TestCBot/solution.txt | 13 + src/CBot/TestCBot/test.txt | 8 + src/CBot/TestCBot/test23.txt | 10 + src/CBot/TestCBot/testmw.txt | 14 + src/CBot/TestCBot/this.txt | 13 + src/CBot/TestCBot/tt.txt | 12 + src/CBot/TestCBot/tt2.txt | 5 + src/CBot/TestCBot/vide.txt | 0 src/CBot/TestCBot/xTestCBot.clw | 245 + src/CBot/TestCBot/zz.txt | 6 + src/CBot/_Copy.bat | 2 + src/CBot/colobot.ini | 49 + src/CBot/idees.txt | 39 + src/CBot/old CBotAddExpr.cpp | 130 + src/CBot/old CBotCompExpr.cpp | 120 + src/CBot/old TstCBot/BotConsoleDlg.cpp | 164 + src/CBot/old TstCBot/BotConsoleDlg.h | 65 + src/CBot/old TstCBot/BotErrorDlg.cpp | 56 + src/CBot/old TstCBot/BotErrorDlg.h | 51 + src/CBot/old TstCBot/CBotTest.txt | 36 + src/CBot/old TstCBot/CMyThread.cpp | 107 + src/CBot/old TstCBot/CMyThread.h | 44 + src/CBot/old TstCBot/MainFrm.cpp | 91 + src/CBot/old TstCBot/MainFrm.h | 55 + src/CBot/old TstCBot/ReadMe.txt | 93 + src/CBot/old TstCBot/Resource.h | 68 + src/CBot/old TstCBot/StdAfx.cpp | 6 + src/CBot/old TstCBot/StdAfx.h | 26 + src/CBot/old TstCBot/TstCBot.clw | 189 + src/CBot/old TstCBot/TstCBot.cpp | 412 + src/CBot/old TstCBot/TstCBot.dsp | 180 + src/CBot/old TstCBot/TstCBot.h | 62 + src/CBot/old TstCBot/TstCBot.rc | 471 ++ src/CBot/old TstCBot/TstCBotDoc.cpp | 83 + src/CBot/old TstCBot/TstCBotDoc.h | 55 + src/CBot/old TstCBot/TstCBotView.cpp | 291 + src/CBot/old TstCBot/TstCBotView.h | 81 + src/CBot/old TstCBot/res/TstCBot.ico | Bin 0 -> 1078 bytes src/CBot/old TstCBot/res/TstCBot.rc2 | 13 + src/CBot/old TstCBot/res/TstCBotDoc.ico | Bin 0 -> 1078 bytes src/CBot/old TstCBot/test complet 1.txt | 213 + src/CBot/old TstCBot/x.txt | 43 + src/CBot/resource.h | 166 + src/ClassFILE.cpp | 413 ++ src/Copie de taskgoto.cpp | 2136 ++++++ src/DirectX.ico | Bin 0 -> 1078 bytes src/accents.txt | 5 + src/auto.cpp | 447 ++ src/auto.h | 95 + src/autobase.cpp | 1446 ++++ src/autobase.h | 102 + src/autoconvert.cpp | 532 ++ src/autoconvert.h | 62 + src/autoderrick.cpp | 591 ++ src/autoderrick.h | 65 + src/autodestroyer.cpp | 383 + src/autodestroyer.h | 57 + src/autoegg.cpp | 359 + src/autoegg.h | 64 + src/autoenergy.cpp | 652 ++ src/autoenergy.h | 63 + src/autofactory.cpp | 947 +++ src/autofactory.h | 66 + src/autoflag.cpp | 164 + src/autoflag.h | 40 + src/autohuston.cpp | 301 + src/autohuston.h | 59 + src/autoinfo.cpp | 523 ++ src/autoinfo.h | 60 + src/autojostle.cpp | 153 + src/autojostle.h | 40 + src/autokid.cpp | 208 + src/autokid.h | 41 + src/autolabo.cpp | 615 ++ src/autolabo.h | 67 + src/automush.cpp | 348 + src/automush.h | 55 + src/autonest.cpp | 277 + src/autonest.h | 55 + src/autonuclear.cpp | 490 ++ src/autonuclear.h | 60 + src/autopara.cpp | 333 + src/autopara.h | 57 + src/autoportico.cpp | 432 ++ src/autoportico.h | 61 + src/autoradar.cpp | 312 + src/autoradar.h | 56 + src/autorepair.cpp | 348 + src/autorepair.h | 55 + src/autoresearch.cpp | 613 ++ src/autoresearch.h | 64 + src/autoroot.cpp | 121 + src/autoroot.h | 38 + src/autosafe.cpp | 622 ++ src/autosafe.h | 66 + src/autostation.cpp | 373 + src/autostation.h | 48 + src/autotower.cpp | 547 ++ src/autotower.h | 68 + src/blitz.cpp | 456 ++ src/blitz.h | 64 + src/brain.cpp | 2985 ++++++++ src/brain.h | 202 + src/bug.txt | 94 + src/bug1.txt | 68 + src/bugs/Colobot_image blèm.psp | Bin 0 -> 42641 bytes src/bugs/Image1.gif | Bin 0 -> 195965 bytes src/bugs/Image2.gif | Bin 0 -> 172230 bytes src/bugs/Image3.gif | Bin 0 -> 123415 bytes src/bugs/Image4.gif | Bin 0 -> 15063 bytes src/bugs/Image9.gif | Bin 0 -> 173575 bytes src/bugs/borne.gif | Bin 0 -> 195790 bytes src/button.cpp | 237 + src/button.h | 42 + src/camera.cpp | 2095 ++++++ src/camera.h | 252 + src/cbottoken.cpp | 507 ++ src/cbottoken.h | 24 + src/ceebot.ini | 66 + src/check.cpp | 156 + src/check.h | 32 + src/cloud.cpp | 317 + src/cloud.h | 71 + src/cmdtoken.cpp | 964 +++ src/cmdtoken.h | 50 + src/colobot.ini | 75 + src/color.cpp | 215 + src/color.h | 41 + src/compass.cpp | 165 + src/compass.h | 36 + src/control.cpp | 862 +++ src/control.h | 118 + src/cur00001.cur | Bin 0 -> 326 bytes src/cur00002.cur | Bin 0 -> 326 bytes src/cur00003.cur | Bin 0 -> 326 bytes src/cursor1.cur | Bin 0 -> 326 bytes src/cursorha.cur | Bin 0 -> 326 bytes src/cursorsc.cur | Bin 0 -> 326 bytes src/d3dapp.cpp | 2433 ++++++ src/d3dapp.h | 153 + src/d3dengine.cpp | 5711 ++++++++++++++ src/d3dengine.h | 669 ++ src/d3denum.cpp | 603 ++ src/d3denum.h | 120 + src/d3dframe.cpp | 607 ++ src/d3dframe.h | 126 + src/d3dmath.cpp | 327 + src/d3dmath.h | 81 + src/d3dres.h | 43 + src/d3dtextr.cpp | 1064 +++ src/d3dtextr.h | 64 + src/d3dutil.cpp | 311 + src/d3dutil.h | 98 + src/dd.cpp | 159 + src/displayinfo.cpp | 1206 +++ src/displayinfo.h | 72 + src/displaytext.cpp | 599 ++ src/displaytext.h | 77 + src/edit.cpp | 3305 +++++++++ src/edit.h | 238 + src/editvalue.cpp | 368 + src/editvalue.h | 69 + src/event.cpp | 75 + src/event.h | 617 ++ src/gauge.cpp | 146 + src/gauge.h | 36 + src/global.h | 48 + src/group.cpp | 632 ++ src/group.h | 32 + src/image.cpp | 145 + src/image.h | 36 + src/iman.cpp | 149 + src/iman.h | 42 + src/interface.cpp | 591 ++ src/interface.h | 77 + src/joystick.cpp | 222 + src/joystick.h | 15 + src/key.cpp | 277 + src/key.h | 38 + src/label.cpp | 81 + src/label.h | 32 + src/language.h | 35 + src/light.cpp | 487 ++ src/light.h | 95 + src/list.cpp | 858 +++ src/list.h | 100 + src/maindialog.cpp | 6922 +++++++++++++++++ src/maindialog.h | 242 + src/mainmap.cpp | 388 + src/mainmap.h | 52 + src/mainmovie.cpp | 233 + src/mainmovie.h | 64 + src/mainshort.cpp | 360 + src/mainshort.h | 45 + src/map.cpp | 1327 ++++ src/map.h | 126 + src/math3d.cpp | 1026 +++ src/math3d.h | 90 + src/metafile.cpp | 403 + src/metafile.h | 60 + src/micent2.txt | 13 + src/misc.cpp | 425 ++ src/misc.h | 223 + src/mixer.txt | 486 ++ src/model.cpp | 3214 ++++++++ src/model.h | 118 + src/modfile.cpp | 681 ++ src/modfile.h | 101 + src/motion.cpp | 241 + src/motion.h | 75 + src/motionant.cpp | 886 +++ src/motionant.h | 61 + src/motionbee.cpp | 647 ++ src/motionbee.h | 54 + src/motionhuman.cpp | 1784 +++++ src/motionhuman.h | 82 + src/motionmother.cpp | 527 ++ src/motionmother.h | 48 + src/motionspider.cpp | 774 ++ src/motionspider.h | 59 + src/motiontoto.cpp | 870 +++ src/motiontoto.h | 61 + src/motionvehicle.cpp | 2075 ++++++ src/motionvehicle.h | 63 + src/motionworm.cpp | 365 + src/motionworm.h | 56 + src/object.cpp | 7599 +++++++++++++++++++ src/object.h | 767 ++ src/particule.cpp | 4357 +++++++++++ src/particule.h | 326 + src/patch16.txt | 10 + src/physics.cpp | 3873 ++++++++++ src/physics.h | 228 + src/planet.cpp | 232 + src/planet.h | 60 + src/profile.cpp | 100 + src/profile.h | 20 + src/projet1.dsp | 608 ++ src/projet1.dsw | 29 + src/projet1.mak | 8659 ++++++++++++++++++++++ src/projet1.opt | Bin 0 -> 58880 bytes src/projet1.plg | 199 + src/pyro.cpp | 2470 ++++++ src/pyro.h | 158 + src/readme.txt | 872 +++ src/resource.h | 39 + src/restext-old.cpp | 2527 +++++++ src/restext.cpp | 3649 +++++++++ src/restext.h | 141 + src/robotmain.cpp | 7018 ++++++++++++++++++ src/robotmain.h | 449 ++ src/script.cpp | 3762 ++++++++++ src/script.h | 99 + src/scroll.cpp | 459 ++ src/scroll.h | 67 + src/shortcut.cpp | 229 + src/shortcut.h | 34 + src/slider.cpp | 570 ++ src/slider.h | 67 + src/sound.cpp | 1640 ++++ src/sound.h | 225 + src/struct.h | 57 + src/studio.cpp | 1649 ++++ src/studio.h | 97 + src/t.txt | 11 + src/target.cpp | 271 + src/target.h | 34 + src/task.cpp | 93 + src/task.h | 70 + src/taskadvance.cpp | 143 + src/taskadvance.h | 39 + src/taskbuild.cpp | 806 ++ src/taskbuild.h | 74 + src/taskfire.cpp | 382 + src/taskfire.h | 42 + src/taskfireant.cpp | 211 + src/taskfireant.h | 53 + src/taskflag.cpp | 305 + src/taskflag.h | 48 + src/taskgoto.cpp | 2340 ++++++ src/taskgoto.h | 147 + src/taskgungoal.cpp | 145 + src/taskgungoal.h | 38 + src/taskinfo.cpp | 217 + src/taskinfo.h | 38 + src/taskmanager.cpp | 275 + src/taskmanager.h | 62 + src/taskmanip.cpp | 1383 ++++ src/taskmanip.h | 88 + src/taskpen.cpp | 288 + src/taskpen.h | 58 + src/taskrecover.cpp | 415 ++ src/taskrecover.h | 56 + src/taskreset.cpp | 329 + src/taskreset.h | 53 + src/tasksearch.cpp | 318 + src/tasksearch.h | 60 + src/taskshield.cpp | 557 ++ src/taskshield.h | 74 + src/taskspiderexplo.cpp | 108 + src/taskspiderexplo.h | 34 + src/tasktake.cpp | 596 ++ src/tasktake.h | 65 + src/taskterraform.cpp | 413 ++ src/taskterraform.h | 52 + src/taskturn.cpp | 131 + src/taskturn.h | 36 + src/taskwait.cpp | 73 + src/taskwait.h | 34 + src/terrain.cpp | 2263 ++++++ src/terrain.h | 195 + src/text.cpp | 1865 +++++ src/text.h | 96 + src/tracks.txt | 17 + src/traduc.txt | 151 + src/version.txt | 108 + src/water.cpp | 819 ++ src/water.h | 118 + src/window.cpp | 1610 ++++ src/window.h | 132 + src/winmain.aps | Bin 0 -> 40696 bytes src/winmain.cpp | 24 + src/winmain.rc | 265 + 417 files changed, 175301 insertions(+) create mode 100644 src/CBot.dll create mode 100644 src/CBot/CBot.aps create mode 100644 src/CBot/CBot.cpp create mode 100644 src/CBot/CBot.dsp create mode 100644 src/CBot/CBot.dsw create mode 100644 src/CBot/CBot.h create mode 100644 src/CBot/CBot.lib create mode 100644 src/CBot/CBot.opt create mode 100644 src/CBot/CBot.plg create mode 100644 src/CBot/CBot.rc create mode 100644 src/CBot/CBotAddExpr.cpp create mode 100644 src/CBot/CBotClass.cpp create mode 100644 src/CBot/CBotCompExpr.cpp create mode 100644 src/CBot/CBotDll.h create mode 100644 src/CBot/CBotFunction.cpp create mode 100644 src/CBot/CBotIf.cpp create mode 100644 src/CBot/CBotProgram.cpp create mode 100644 src/CBot/CBotStack.cpp create mode 100644 src/CBot/CBotString.cpp create mode 100644 src/CBot/CBotToken.cpp create mode 100644 src/CBot/CBotToken.h create mode 100644 src/CBot/CBotTwoOpExpr ordre inversé.cpp create mode 100644 src/CBot/CBotTwoOpExpr.cpp create mode 100644 src/CBot/CBotVar.cpp create mode 100644 src/CBot/CBotWhile.cpp create mode 100644 src/CBot/ClassFILE.cpp create mode 100644 src/CBot/Copie de CBot.rc create mode 100644 src/CBot/Copie de CBotTwoOpExpr.cpp create mode 100644 src/CBot/StringFunctions.cpp create mode 100644 src/CBot/TestCBot/B.txt create mode 100644 src/CBot/TestCBot/BUG2.txt create mode 100644 src/CBot/TestCBot/CBotConsoleDlg.cpp create mode 100644 src/CBot/TestCBot/CBotConsoleDlg.h create mode 100644 src/CBot/TestCBot/ChildFrm.cpp create mode 100644 src/CBot/TestCBot/ChildFrm.h create mode 100644 src/CBot/TestCBot/Deleted.txt create mode 100644 src/CBot/TestCBot/MaClass.txt create mode 100644 src/CBot/TestCBot/MainFrm.cpp create mode 100644 src/CBot/TestCBot/MainFrm.h create mode 100644 src/CBot/TestCBot/Mc2.txt create mode 100644 src/CBot/TestCBot/Mon fichier.txt create mode 100644 src/CBot/TestCBot/Nop.txt create mode 100644 src/CBot/TestCBot/POS.txt create mode 100644 src/CBot/TestCBot/PerformDlg.cpp create mode 100644 src/CBot/TestCBot/PerformDlg.h create mode 100644 src/CBot/TestCBot/Routines.cpp create mode 100644 src/CBot/TestCBot/StdAfx.cpp create mode 100644 src/CBot/TestCBot/StdAfx.h create mode 100644 src/CBot/TestCBot/T.txt create mode 100644 src/CBot/TestCBot/TESTALL.txt create mode 100644 src/CBot/TestCBot/TestCB1.txt create mode 100644 src/CBot/TestCBot/TestCBot.clw create mode 100644 src/CBot/TestCBot/TestCBot.cpp create mode 100644 src/CBot/TestCBot/TestCBot.dsp create mode 100644 src/CBot/TestCBot/TestCBot.h create mode 100644 src/CBot/TestCBot/TestCBot.rc create mode 100644 src/CBot/TestCBot/TestCBot1.txt create mode 100644 src/CBot/TestCBot/TestCBot3.txt create mode 100644 src/CBot/TestCBot/TestCBotDoc.cpp create mode 100644 src/CBot/TestCBot/TestCBotDoc.h create mode 100644 src/CBot/TestCBot/TestCBotView.cpp create mode 100644 src/CBot/TestCBot/TestCBotView.h create mode 100644 src/CBot/TestCBot/TestNull.txt create mode 100644 src/CBot/TestCBot/TestRestoreState.txt create mode 100644 src/CBot/TestCBot/TestStatic.txt create mode 100644 src/CBot/TestCBot/TestStr.txt create mode 100644 src/CBot/TestCBot/Z.txt create mode 100644 src/CBot/TestCBot/array.txt create mode 100644 src/CBot/TestCBot/a§1.txt create mode 100644 src/CBot/TestCBot/bug.txt create mode 100644 src/CBot/TestCBot/bugmw.txt create mode 100644 src/CBot/TestCBot/ccc.txt create mode 100644 src/CBot/TestCBot/enum.txt create mode 100644 src/CBot/TestCBot/fibo.txt create mode 100644 src/CBot/TestCBot/file.txt create mode 100644 src/CBot/TestCBot/h.txt create mode 100644 src/CBot/TestCBot/include.txt create mode 100644 src/CBot/TestCBot/intrinsic.txt create mode 100644 src/CBot/TestCBot/methode1.txt create mode 100644 src/CBot/TestCBot/methode2.txt create mode 100644 src/CBot/TestCBot/mp1.txt create mode 100644 src/CBot/TestCBot/mp2.txt create mode 100644 src/CBot/TestCBot/mw.txt create mode 100644 src/CBot/TestCBot/null.txt create mode 100644 src/CBot/TestCBot/opnew.txt create mode 100644 src/CBot/TestCBot/plante.txt create mode 100644 src/CBot/TestCBot/pointer.txt create mode 100644 src/CBot/TestCBot/postinc.txt create mode 100644 src/CBot/TestCBot/radar.txt create mode 100644 src/CBot/TestCBot/res/TestCBot.ico create mode 100644 src/CBot/TestCBot/res/TestCBot.rc2 create mode 100644 src/CBot/TestCBot/res/TestCBotDoc.ico create mode 100644 src/CBot/TestCBot/res/Toolbar.bmp create mode 100644 src/CBot/TestCBot/resource.h create mode 100644 src/CBot/TestCBot/solution.txt create mode 100644 src/CBot/TestCBot/test.txt create mode 100644 src/CBot/TestCBot/test23.txt create mode 100644 src/CBot/TestCBot/testmw.txt create mode 100644 src/CBot/TestCBot/this.txt create mode 100644 src/CBot/TestCBot/tt.txt create mode 100644 src/CBot/TestCBot/tt2.txt create mode 100644 src/CBot/TestCBot/vide.txt create mode 100644 src/CBot/TestCBot/xTestCBot.clw create mode 100644 src/CBot/TestCBot/zz.txt create mode 100644 src/CBot/_Copy.bat create mode 100644 src/CBot/colobot.ini create mode 100644 src/CBot/idees.txt create mode 100644 src/CBot/old CBotAddExpr.cpp create mode 100644 src/CBot/old CBotCompExpr.cpp create mode 100644 src/CBot/old TstCBot/BotConsoleDlg.cpp create mode 100644 src/CBot/old TstCBot/BotConsoleDlg.h create mode 100644 src/CBot/old TstCBot/BotErrorDlg.cpp create mode 100644 src/CBot/old TstCBot/BotErrorDlg.h create mode 100644 src/CBot/old TstCBot/CBotTest.txt create mode 100644 src/CBot/old TstCBot/CMyThread.cpp create mode 100644 src/CBot/old TstCBot/CMyThread.h create mode 100644 src/CBot/old TstCBot/MainFrm.cpp create mode 100644 src/CBot/old TstCBot/MainFrm.h create mode 100644 src/CBot/old TstCBot/ReadMe.txt create mode 100644 src/CBot/old TstCBot/Resource.h create mode 100644 src/CBot/old TstCBot/StdAfx.cpp create mode 100644 src/CBot/old TstCBot/StdAfx.h create mode 100644 src/CBot/old TstCBot/TstCBot.clw create mode 100644 src/CBot/old TstCBot/TstCBot.cpp create mode 100644 src/CBot/old TstCBot/TstCBot.dsp create mode 100644 src/CBot/old TstCBot/TstCBot.h create mode 100644 src/CBot/old TstCBot/TstCBot.rc create mode 100644 src/CBot/old TstCBot/TstCBotDoc.cpp create mode 100644 src/CBot/old TstCBot/TstCBotDoc.h create mode 100644 src/CBot/old TstCBot/TstCBotView.cpp create mode 100644 src/CBot/old TstCBot/TstCBotView.h create mode 100644 src/CBot/old TstCBot/res/TstCBot.ico create mode 100644 src/CBot/old TstCBot/res/TstCBot.rc2 create mode 100644 src/CBot/old TstCBot/res/TstCBotDoc.ico create mode 100644 src/CBot/old TstCBot/test complet 1.txt create mode 100644 src/CBot/old TstCBot/x.txt create mode 100644 src/CBot/resource.h create mode 100644 src/ClassFILE.cpp create mode 100644 src/Copie de taskgoto.cpp create mode 100644 src/DirectX.ico create mode 100644 src/accents.txt create mode 100644 src/auto.cpp create mode 100644 src/auto.h create mode 100644 src/autobase.cpp create mode 100644 src/autobase.h create mode 100644 src/autoconvert.cpp create mode 100644 src/autoconvert.h create mode 100644 src/autoderrick.cpp create mode 100644 src/autoderrick.h create mode 100644 src/autodestroyer.cpp create mode 100644 src/autodestroyer.h create mode 100644 src/autoegg.cpp create mode 100644 src/autoegg.h create mode 100644 src/autoenergy.cpp create mode 100644 src/autoenergy.h create mode 100644 src/autofactory.cpp create mode 100644 src/autofactory.h create mode 100644 src/autoflag.cpp create mode 100644 src/autoflag.h create mode 100644 src/autohuston.cpp create mode 100644 src/autohuston.h create mode 100644 src/autoinfo.cpp create mode 100644 src/autoinfo.h create mode 100644 src/autojostle.cpp create mode 100644 src/autojostle.h create mode 100644 src/autokid.cpp create mode 100644 src/autokid.h create mode 100644 src/autolabo.cpp create mode 100644 src/autolabo.h create mode 100644 src/automush.cpp create mode 100644 src/automush.h create mode 100644 src/autonest.cpp create mode 100644 src/autonest.h create mode 100644 src/autonuclear.cpp create mode 100644 src/autonuclear.h create mode 100644 src/autopara.cpp create mode 100644 src/autopara.h create mode 100644 src/autoportico.cpp create mode 100644 src/autoportico.h create mode 100644 src/autoradar.cpp create mode 100644 src/autoradar.h create mode 100644 src/autorepair.cpp create mode 100644 src/autorepair.h create mode 100644 src/autoresearch.cpp create mode 100644 src/autoresearch.h create mode 100644 src/autoroot.cpp create mode 100644 src/autoroot.h create mode 100644 src/autosafe.cpp create mode 100644 src/autosafe.h create mode 100644 src/autostation.cpp create mode 100644 src/autostation.h create mode 100644 src/autotower.cpp create mode 100644 src/autotower.h create mode 100644 src/blitz.cpp create mode 100644 src/blitz.h create mode 100644 src/brain.cpp create mode 100644 src/brain.h create mode 100644 src/bug.txt create mode 100644 src/bug1.txt create mode 100644 src/bugs/Colobot_image blèm.psp create mode 100644 src/bugs/Image1.gif create mode 100644 src/bugs/Image2.gif create mode 100644 src/bugs/Image3.gif create mode 100644 src/bugs/Image4.gif create mode 100644 src/bugs/Image9.gif create mode 100644 src/bugs/borne.gif create mode 100644 src/button.cpp create mode 100644 src/button.h create mode 100644 src/camera.cpp create mode 100644 src/camera.h create mode 100644 src/cbottoken.cpp create mode 100644 src/cbottoken.h create mode 100644 src/ceebot.ini create mode 100644 src/check.cpp create mode 100644 src/check.h create mode 100644 src/cloud.cpp create mode 100644 src/cloud.h create mode 100644 src/cmdtoken.cpp create mode 100644 src/cmdtoken.h create mode 100644 src/colobot.ini create mode 100644 src/color.cpp create mode 100644 src/color.h create mode 100644 src/compass.cpp create mode 100644 src/compass.h create mode 100644 src/control.cpp create mode 100644 src/control.h create mode 100644 src/cur00001.cur create mode 100644 src/cur00002.cur create mode 100644 src/cur00003.cur create mode 100644 src/cursor1.cur create mode 100644 src/cursorha.cur create mode 100644 src/cursorsc.cur create mode 100644 src/d3dapp.cpp create mode 100644 src/d3dapp.h create mode 100644 src/d3dengine.cpp create mode 100644 src/d3dengine.h create mode 100644 src/d3denum.cpp create mode 100644 src/d3denum.h create mode 100644 src/d3dframe.cpp create mode 100644 src/d3dframe.h create mode 100644 src/d3dmath.cpp create mode 100644 src/d3dmath.h create mode 100644 src/d3dres.h create mode 100644 src/d3dtextr.cpp create mode 100644 src/d3dtextr.h create mode 100644 src/d3dutil.cpp create mode 100644 src/d3dutil.h create mode 100644 src/dd.cpp create mode 100644 src/displayinfo.cpp create mode 100644 src/displayinfo.h create mode 100644 src/displaytext.cpp create mode 100644 src/displaytext.h create mode 100644 src/edit.cpp create mode 100644 src/edit.h create mode 100644 src/editvalue.cpp create mode 100644 src/editvalue.h create mode 100644 src/event.cpp create mode 100644 src/event.h create mode 100644 src/gauge.cpp create mode 100644 src/gauge.h create mode 100644 src/global.h create mode 100644 src/group.cpp create mode 100644 src/group.h create mode 100644 src/image.cpp create mode 100644 src/image.h create mode 100644 src/iman.cpp create mode 100644 src/iman.h create mode 100644 src/interface.cpp create mode 100644 src/interface.h create mode 100644 src/joystick.cpp create mode 100644 src/joystick.h create mode 100644 src/key.cpp create mode 100644 src/key.h create mode 100644 src/label.cpp create mode 100644 src/label.h create mode 100644 src/language.h create mode 100644 src/light.cpp create mode 100644 src/light.h create mode 100644 src/list.cpp create mode 100644 src/list.h create mode 100644 src/maindialog.cpp create mode 100644 src/maindialog.h create mode 100644 src/mainmap.cpp create mode 100644 src/mainmap.h create mode 100644 src/mainmovie.cpp create mode 100644 src/mainmovie.h create mode 100644 src/mainshort.cpp create mode 100644 src/mainshort.h create mode 100644 src/map.cpp create mode 100644 src/map.h create mode 100644 src/math3d.cpp create mode 100644 src/math3d.h create mode 100644 src/metafile.cpp create mode 100644 src/metafile.h create mode 100644 src/micent2.txt create mode 100644 src/misc.cpp create mode 100644 src/misc.h create mode 100644 src/mixer.txt create mode 100644 src/model.cpp create mode 100644 src/model.h create mode 100644 src/modfile.cpp create mode 100644 src/modfile.h create mode 100644 src/motion.cpp create mode 100644 src/motion.h create mode 100644 src/motionant.cpp create mode 100644 src/motionant.h create mode 100644 src/motionbee.cpp create mode 100644 src/motionbee.h create mode 100644 src/motionhuman.cpp create mode 100644 src/motionhuman.h create mode 100644 src/motionmother.cpp create mode 100644 src/motionmother.h create mode 100644 src/motionspider.cpp create mode 100644 src/motionspider.h create mode 100644 src/motiontoto.cpp create mode 100644 src/motiontoto.h create mode 100644 src/motionvehicle.cpp create mode 100644 src/motionvehicle.h create mode 100644 src/motionworm.cpp create mode 100644 src/motionworm.h create mode 100644 src/object.cpp create mode 100644 src/object.h create mode 100644 src/particule.cpp create mode 100644 src/particule.h create mode 100644 src/patch16.txt create mode 100644 src/physics.cpp create mode 100644 src/physics.h create mode 100644 src/planet.cpp create mode 100644 src/planet.h create mode 100644 src/profile.cpp create mode 100644 src/profile.h create mode 100644 src/projet1.dsp create mode 100644 src/projet1.dsw create mode 100644 src/projet1.mak create mode 100644 src/projet1.opt create mode 100644 src/projet1.plg create mode 100644 src/pyro.cpp create mode 100644 src/pyro.h create mode 100644 src/readme.txt create mode 100644 src/resource.h create mode 100644 src/restext-old.cpp create mode 100644 src/restext.cpp create mode 100644 src/restext.h create mode 100644 src/robotmain.cpp create mode 100644 src/robotmain.h create mode 100644 src/script.cpp create mode 100644 src/script.h create mode 100644 src/scroll.cpp create mode 100644 src/scroll.h create mode 100644 src/shortcut.cpp create mode 100644 src/shortcut.h create mode 100644 src/slider.cpp create mode 100644 src/slider.h create mode 100644 src/sound.cpp create mode 100644 src/sound.h create mode 100644 src/struct.h create mode 100644 src/studio.cpp create mode 100644 src/studio.h create mode 100644 src/t.txt create mode 100644 src/target.cpp create mode 100644 src/target.h create mode 100644 src/task.cpp create mode 100644 src/task.h create mode 100644 src/taskadvance.cpp create mode 100644 src/taskadvance.h create mode 100644 src/taskbuild.cpp create mode 100644 src/taskbuild.h create mode 100644 src/taskfire.cpp create mode 100644 src/taskfire.h create mode 100644 src/taskfireant.cpp create mode 100644 src/taskfireant.h create mode 100644 src/taskflag.cpp create mode 100644 src/taskflag.h create mode 100644 src/taskgoto.cpp create mode 100644 src/taskgoto.h create mode 100644 src/taskgungoal.cpp create mode 100644 src/taskgungoal.h create mode 100644 src/taskinfo.cpp create mode 100644 src/taskinfo.h create mode 100644 src/taskmanager.cpp create mode 100644 src/taskmanager.h create mode 100644 src/taskmanip.cpp create mode 100644 src/taskmanip.h create mode 100644 src/taskpen.cpp create mode 100644 src/taskpen.h create mode 100644 src/taskrecover.cpp create mode 100644 src/taskrecover.h create mode 100644 src/taskreset.cpp create mode 100644 src/taskreset.h create mode 100644 src/tasksearch.cpp create mode 100644 src/tasksearch.h create mode 100644 src/taskshield.cpp create mode 100644 src/taskshield.h create mode 100644 src/taskspiderexplo.cpp create mode 100644 src/taskspiderexplo.h create mode 100644 src/tasktake.cpp create mode 100644 src/tasktake.h create mode 100644 src/taskterraform.cpp create mode 100644 src/taskterraform.h create mode 100644 src/taskturn.cpp create mode 100644 src/taskturn.h create mode 100644 src/taskwait.cpp create mode 100644 src/taskwait.h create mode 100644 src/terrain.cpp create mode 100644 src/terrain.h create mode 100644 src/text.cpp create mode 100644 src/text.h create mode 100644 src/tracks.txt create mode 100644 src/traduc.txt create mode 100644 src/version.txt create mode 100644 src/water.cpp create mode 100644 src/water.h create mode 100644 src/window.cpp create mode 100644 src/window.h create mode 100644 src/winmain.aps create mode 100644 src/winmain.cpp create mode 100644 src/winmain.rc diff --git a/src/CBot.dll b/src/CBot.dll new file mode 100644 index 0000000000000000000000000000000000000000..5b9c1bb7157ffb16d34048ff4619e9e4f66ca76d GIT binary patch literal 434176 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 OVe0>`JNW;*n*Rn+QvIp` literal 0 HcmV?d00001 diff --git a/src/CBot/CBot.aps b/src/CBot/CBot.aps new file mode 100644 index 0000000000000000000000000000000000000000..cd294eca4c6f90241b7792985041a52deca7d712 GIT binary patch literal 44112 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 literal 0 HcmV?d00001 diff --git a/src/CBot/CBot.cpp b/src/CBot/CBot.cpp new file mode 100644 index 00000000..79a0b6f4 --- /dev/null +++ b/src/CBot/CBot.cpp @@ -0,0 +1,4055 @@ +/////////////////////////////////////////////////////////////////////// +// compilation des diverses instructions +// toutes les routines Compile sont statiques +// et retournent un object selon ce qui a été trouvé comme instruction + +// principe de compilation: +// les routines Compile retournent un objet de la classe correspondant à l'opération trouvée +// il s'agit toujours d'une classe fille de CBotInstr. +// ( les objets CBotInstr ne sont jamais utilisés directement ) + +// si la routine Compile retourne NULL, c'est que l'instruction est fausse +// ou incomprise. +// L'erreur se trouve alors sur la pile CBotCStack::IsOk() est FALSE + + +#include "CBot.h" + +// les divers constructeurs / destructeurs +// pour libérer tout selon l'arbre établi +CBotInstr::CBotInstr() +{ + name = "CBotInstr"; + m_next = NULL; + m_next2b = NULL; + m_next3 = NULL; + m_next3b = NULL; +} + +CBotInstr::~CBotInstr() +{ + delete m_next; + delete m_next2b; + delete m_next3; + delete m_next3b; +} + +// compteur de boucles imbriquées, +// pour détermniner les break et continue valides +// et liste des labels utilisables + +int CBotInstr::m_LoopLvl = 0; +CBotStringArray + CBotInstr::m_labelLvl = CBotStringArray(); + +// ajoute un niveau avec un label +void CBotInstr::IncLvl(CBotString& label) +{ + m_labelLvl.SetSize(m_LoopLvl+1); + m_labelLvl[m_LoopLvl] = label; + m_LoopLvl++; +} + +// ajoute un niveau (instruction switch) +void CBotInstr::IncLvl() +{ + m_labelLvl.SetSize(m_LoopLvl+1); + m_labelLvl[m_LoopLvl] = "#SWITCH"; + m_LoopLvl++; +} + +// libère un niveau +void CBotInstr::DecLvl() +{ + m_LoopLvl--; + m_labelLvl[m_LoopLvl].Empty(); +} + +// controle la validité d'un break ou continu +BOOL CBotInstr::ChkLvl(CBotString& label, int type) +{ + int i = m_LoopLvl; + while (--i>=0) + { + if ( type == ID_CONTINUE && m_labelLvl[i] == "#SWITCH") continue; + if ( label.IsEmpty() ) return TRUE; + if ( m_labelLvl[i] == label ) return TRUE; + } + return FALSE; +} + +BOOL CBotInstr::IsOfClass(CBotString n) +{ + return name == n; +} + + +//////////////////////////////////////////////////////////////////////////// +// gestion de base de la classe CBotInstr + +// définie le token correspondant à l'instruction + +void CBotInstr::SetToken(CBotToken* p) +{ + m_token = *p; +} + +void CBotInstr::SetToken(CBotString* name, int start, int end) +{ + SetToken( &CBotToken( *name, CBotString(), start, end)); +} + + +// rend le type du token associé à l'instruction + +int CBotInstr::GivTokenType() +{ + return m_token.GivType(); +} + +// rend le token associé + +CBotToken* CBotInstr::GivToken() +{ + return &m_token; +} + +// ajoute une instruction à la suite des autres + +void CBotInstr::AddNext(CBotInstr* n) +{ + CBotInstr* p = this; + while ( p->m_next != NULL ) p = p->m_next; + p->m_next = n; +} + +void CBotInstr::AddNext3(CBotInstr* n) +{ + CBotInstr* p = this; + while ( p->m_next3 != NULL ) p = p->m_next3; + p->m_next3 = n; +} + +void CBotInstr::AddNext3b(CBotInstr* n) +{ + CBotInstr* p = this; + while ( p->m_next3b != NULL ) p = p->m_next3b; + p->m_next3b = n; +} + +// donne l'instruction suivante + +CBotInstr* CBotInstr::GivNext() +{ + return m_next; +} + +CBotInstr* CBotInstr::GivNext3() +{ + return m_next3; +} + +CBotInstr* CBotInstr::GivNext3b() +{ + return m_next3b; +} + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction, qui peut être +// while, do, try, throw, if, for, switch, break, continu, return +// int, float, boolean, string, +// déclaration d'une instance d'une classe +// expression quelconque + +CBotInstr* CBotInstr::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotToken* pp = p; + + if ( p == NULL ) return NULL; + + int type = p->GivType(); // quel est le prochaine token ? + + // y a-t-il un label ? + if ( IsOfType( pp, TokenTypVar ) && + IsOfType( pp, ID_DOTS ) ) + { + type = pp->GivType(); + // seules ces instructions acceptent un label + if (!IsOfTypeList( pp, ID_WHILE, ID_FOR, ID_DO, ID_REPEAT, 0 )) + { + pStack->SetError(TX_LABEL, pp->GivStart()); + return NULL; + } + } + + // appel la routine de compilation correspondant au token trouvé + switch (type) + { + case ID_WHILE: + return CBotWhile::Compile(p, pStack); + + case ID_FOR: + return CBotFor::Compile(p, pStack); + + case ID_DO: + return CBotDo::Compile(p, pStack); + + case ID_REPEAT: + return CBotRepeat::Compile(p, pStack); + + case ID_BREAK: + case ID_CONTINUE: + return CBotBreak::Compile(p, pStack); + + case ID_SWITCH: + return CBotSwitch::Compile(p, pStack); + + case ID_TRY: + return CBotTry::Compile(p, pStack); + + case ID_THROW: + return CBotThrow::Compile(p, pStack); + + case ID_DEBUGDD: + return CBotStartDebugDD::Compile(p, pStack); + + case ID_INT: + return CBotInt::Compile(p, pStack); + + case ID_FLOAT: + return CBotFloat::Compile(p, pStack); + + case ID_STRING: + return CBotIString::Compile(p, pStack); + + case ID_BOOLEAN: + case ID_BOOL: + return CBotBoolean::Compile(p, pStack); + + case ID_IF: + return CBotIf::Compile(p, pStack); + + case ID_RETURN: + return CBotReturn::Compile(p, pStack); + + case ID_ELSE: + pStack->SetStartError(p->GivStart()); + pStack->SetError(TX_ELSEWITHOUTIF, p->GivEnd()); + return NULL; + + case ID_CASE: + pStack->SetStartError(p->GivStart()); + pStack->SetError(TX_OUTCASE, p->GivEnd()); + return NULL; + } + + pStack->SetStartError(p->GivStart()); + + // ne doit pas être un mot réservé par DefineNum + if ( p->GivType() == TokenTypDef ) + { + pStack->SetError(TX_RESERVED, p); + return NULL; + } + + // ce peut être une définition d'instance de class + CBotToken* ppp = p; + if ( IsOfType( ppp, TokenTypVar ) /* && IsOfType( ppp, TokenTypVar )*/ ) + { + if ( CBotClass::Find(p) != NULL ) + { + // oui, compile la déclaration de l'instance + return CBotClassInst::Compile(p, pStack); + } + } + + // ce peut être une instruction arithmétique + CBotInstr* inst = CBotExpression::Compile(p, pStack); + if (IsOfType(p, ID_SEP)) + { + return inst; + } + pStack->SetError(TX_ENDOF, p->GivStart()); + delete inst; + return NULL; +} + +BOOL CBotInstr::Execute(CBotStack* &pj) +{ + CBotString ClassManquante = name; + __asm int 3; // ne doit jamais passer par cette routine + // mais utiliser les routines des classes filles + return FALSE; +} + +BOOL CBotInstr::Execute(CBotStack* &pj, CBotVar* pVar) +{ + if ( !Execute(pj) ) return FALSE; + pVar->SetVal( pj->GivVar() ); + return TRUE; +} + +void CBotInstr::RestoreState(CBotStack* &pj, BOOL bMain) +{ + CBotString ClassManquante = name; + __asm int 3; // ne doit jamais passer par cette routine + // mais utiliser les routines des classes filles +} + + +BOOL CBotInstr::ExecuteVar(CBotVar* &pVar, CBotCStack* &pile) +{ + __asm int 3; // papa sait pas faire, voir les filles + return FALSE; +} + +BOOL CBotInstr::ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, BOOL bStep, BOOL bExtend) +{ + __asm int 3; // papa sait pas faire, voir les filles + return FALSE; +} + +void CBotInstr::RestoreStateVar(CBotStack* &pile, BOOL bMain) +{ + __asm int 3; // papa sait pas faire, voir les filles +} + +// cette routine n'est définie que pour la classe fille CBotCase +// cela permet de faire l'appel CompCase sur toutes les instructions +// pour savoir s'il s'agit d'un case pour la valeur désirée. + +BOOL CBotInstr::CompCase(CBotStack* &pj, int val) +{ + return FALSE; +} + +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// compile un bloc d'instruction " { i ; i ; } " + +// cette classe n'a pas de constructeur, car il n'y a jamais d'instance de cette classe +// l'objet retourné par Compile est généralement de type CBotListInstr + + +CBotInstr* CBotBlock::Compile(CBotToken* &p, CBotCStack* pStack, BOOL bLocal) +{ + pStack->SetStartError(p->GivStart()); + + if (IsOfType(p, ID_OPBLK)) + { + CBotInstr* inst = CBotListInstr::Compile( p, pStack, bLocal ); + + if (IsOfType(p, ID_CLBLK)) + { + return inst; + } + + pStack->SetError(TX_CLOSEBLK, p->GivStart()); // manque la parenthèse + delete inst; + return NULL; + } + + pStack->SetError(TX_OPENBLK, p->GivStart()); + return NULL; +} + +CBotInstr* CBotBlock::CompileBlkOrInst(CBotToken* &p, CBotCStack* pStack, BOOL bLocal) +{ + // est-ce un nouveau bloc ? + if ( p->GivType() == ID_OPBLK ) return CBotBlock::Compile(p, pStack); + + // sinon, cherche une instruction unique à la place + + // pour gérer les cas avec définition local à l'instructin (*) + CBotCStack* pStk = pStack->TokenStack(p, bLocal); + + return pStack->Return( CBotInstr::Compile(p, pStk), // une instruction unique + pStk); +} + +// (*) c'est le cas dans l'instruction suivante +// if ( 1 == 1 ) int x = 0; +// où la variable x n'est connue que dans le bloc qui suit le if. + + +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// compile une liste d'instruction, séparés par des points-virgules + +CBotListInstr::CBotListInstr() +{ + m_Instr = NULL; + name = "CBotListInstr"; +} + +CBotListInstr::~CBotListInstr() +{ + delete m_Instr; +} + +CBotInstr* CBotListInstr::Compile(CBotToken* &p, CBotCStack* pStack, BOOL bLocal) +{ + CBotCStack* pStk = pStack->TokenStack(p, bLocal); // les variables sont locales + + CBotListInstr* inst = new CBotListInstr(); + + while (TRUE) + { + if ( p == NULL ) break; + + if (IsOfType(p, ID_SEP)) continue; // instruction vide ignorée + if ( p->GivType() == ID_CLBLK ) break; // déja plus d'instruction + + if (IsOfType(p, 0)) + { + pStack->SetError(TX_CLOSEBLK, p->GivStart()); + delete inst; + return pStack->Return(NULL, pStk); + } + + CBotInstr* i = CBotBlock::CompileBlkOrInst( p, pStk ); // compile la suivante + + if (!pStk->IsOk()) + { + delete inst; + return pStack->Return(NULL, pStk); + } + + if ( inst->m_Instr == NULL ) inst->m_Instr = i; + else inst->m_Instr->AddNext(i); // ajoute à la suite + } + return pStack->Return(inst, pStk); +} + +// exécute une liste d'instructions + +BOOL CBotListInstr::Execute(CBotStack* &pj) +{ + + CBotStack* pile = pj->AddStack(this, TRUE);//indispensable pour SetState() + if ( pile->StackOver() ) return pj->Return( pile ); + + + CBotInstr* p = m_Instr; // la première expression + + int state = pile->GivState(); + while (state-->0) p = p->GivNext(); // revient sur l'opération interrompue + + if ( p != NULL ) while (TRUE) + { +// DEBUG( "CBotListInstr", pile->GivState(), pile ); + + if ( !p->Execute(pile) ) return FALSE; + p = p->GivNext(); + if ( p == NULL ) break; + if (!pile->IncState()) ;//return FALSE; // prêt pour la suivante + } + + return pj->Return( pile ); // transmet en dessous +} + +void CBotListInstr::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + CBotInstr* p = m_Instr; // la première expression + + int state = pile->GivState(); + while ( p != NULL && state-- > 0) + { + p->RestoreState(pile, FALSE); + p = p->GivNext(); // revient sur l'opération interrompue + } + + if ( p != NULL ) p->RestoreState(pile, TRUE); +} + +////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////// +// compilation d'un élément se trouvant à gauche d'une assignation + +CBotLeftExprVar::CBotLeftExprVar() +{ + name = "CBotLeftExprVar"; + m_typevar = -1; + m_nIdent = 0; +} + +CBotLeftExprVar::~CBotLeftExprVar() +{ +} + +CBotInstr* CBotLeftExprVar::Compile(CBotToken* &p, CBotCStack* pStack) +{ + // vérifie que le token est un nom de variable + if (p->GivType() != TokenTypVar) + { + pStack->SetError( TX_NOVAR, p->GivStart()); + return NULL; + } + + CBotLeftExprVar* inst = new CBotLeftExprVar(); + inst->SetToken(p); + p = p->GivNext(); + + return inst; +} + +// crée une variable et lui assigne le résultat de la pile +BOOL CBotLeftExprVar::Execute(CBotStack* &pj) +{ + CBotVar* var1; + CBotVar* var2; + + var1 = CBotVar::Create(m_token.GivString(), m_typevar); + var1->SetUniqNum(m_nIdent); // avec cet identificateur unique + pj->AddVar(var1); // la place sur la pile + + var2 = pj->GivVar(); // resultat sur la pile + if ( var2 ) var1->SetVal(var2); // fait l'assignation + + return TRUE; // opération faite +} + +void CBotLeftExprVar::RestoreState(CBotStack* &pj, BOOL bMain) +{ + CBotVar* var1; + + var1 = pj->FindVar(m_token.GivString()); + if ( var1 == NULL ) __asm int 3; + + var1->SetUniqNum(m_nIdent); // avec cet identificateur unique +} + +////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////// +// définition d'un tableau de n'importe quel type +// int a[12]; +// point x[]; + +CBotInstArray::CBotInstArray() +{ + m_var = NULL; + m_listass = NULL; + name = "CBotInstArray"; +} + +CBotInstArray::~CBotInstArray() +{ + delete m_var; + delete m_listass; +} + + +CBotInstr* CBotInstArray::Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResult type) +{ + CBotCStack* pStk = pStack->TokenStack(p); + + CBotInstArray* inst = new CBotInstArray(); // crée l'objet + + CBotToken* vartoken = p; + inst->SetToken(vartoken); + + // détermine l'expression valable pour l'élément gauche + if ( NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk )) ) + { + if (pStk->CheckVarLocal(vartoken)) // redéfinition de la variable ? + { + pStk->SetError(TX_REDEFVAR, vartoken); + goto error; + } + + CBotInstr* i; + while (IsOfType(p, ID_OPBRK)) // avec des indices ? + { + if ( p->GivType() != ID_CLBRK ) + i = CBotExpression::Compile( p, pStk ); // expression pour la valeur + else + i = new CBotEmpty(); // spécial si pas de formule + + inst->AddNext3b(i); // construit une liste + type = CBotTypResult(CBotTypArrayPointer, type); + + if (!pStk->IsOk() || !IsOfType( p, ID_CLBRK ) ) + { + pStk->SetError(TX_CLBRK, p->GivStart()); + goto error; + } + } + + CBotVar* var = CBotVar::Create(vartoken, type); // crée avec une instance + inst->m_typevar = type; + + var->SetUniqNum( + ((CBotLeftExprVar*)inst->m_var)->m_nIdent = CBotVar::NextUniqNum()); + // lui attribut un numéro unique + pStack->AddVar(var); // la place sur la pile + + if ( IsOfType(p, ID_ASS) ) // avec une assignation + { + inst->m_listass = CBotListArray::Compile( p, pStk, type.GivTypElem() ); + } + + if ( pStk->IsOk() ) return pStack->Return(inst, pStk); + } + +error: + delete inst; + return pStack->Return(NULL, pStk); +} + + +// exécute la définition d'un tableau + +BOOL CBotInstArray::Execute(CBotStack* &pj) +{ + CBotStack* pile1 = pj->AddStack(this); +// if ( pile1 == EOX ) return TRUE; + + CBotStack* pile = pile1; + + if ( pile1->GivState() == 0 ) + { + // cherche les dimensions max du tableau + CBotInstr* p = GivNext3b(); // les différentes formules + int nb = 0; + + while (p != NULL) + { + pile = pile->AddStack(); // petite place pour travailler + nb++; + if ( pile->GivState() == 0 ) + { + if ( !p->Execute(pile) ) return FALSE; // calcul de la taille // interrompu? + pile->IncState(); + } + p = p->GivNext3b(); + } + + p = GivNext3b(); + pile = pile1; // revient sur la pile + int n = 0; + int max[100]; + + while (p != NULL) + { + pile = pile->AddStack(); // récupère la même petite place + CBotVar* v = pile->GivVar(); // résultat + max[n] = v->GivValInt(); // valeur + if (max[n]>MAXARRAYSIZE) + { + pile->SetError(TX_OUTARRAY, &m_token); + return pj->Return ( pile ); + } + n++; + p = p->GivNext3b(); + } + while (n<100) max[n++] = 0; + + m_typevar.SetArray( max ); // mémorise les limitations + + // crée simplement un pointeur null + CBotVar* var = CBotVar::Create(m_var->GivToken(), m_typevar); + var->SetPointer(NULL); + var->SetUniqNum(((CBotLeftExprVar*)m_var)->m_nIdent); + pj->AddVar(var); // inscrit le tableau de base sur la pile + +#if STACKMEM + pile1->AddStack()->Delete(); +#else + delete pile1->AddStack(); // plus besoin des indices +#endif + pile1->IncState(); + } + + if ( pile1->GivState() == 1 ) + { + if ( m_listass != NULL ) // il y a des assignation pour ce tableau + { + CBotVar* pVar = pj->FindVar(((CBotLeftExprVar*)m_var)->m_nIdent); + + if ( !m_listass->Execute(pile1, pVar) ) return FALSE; + } + pile1->IncState(); + } + + if ( pile1->IfStep() ) return FALSE; // montre ce pas ? + + if ( m_next2b && + !m_next2b->Execute( pile1 ) ) return FALSE; + + return pj->Return( pile1 ); // transmet en dessous +} + +void CBotInstArray::RestoreState(CBotStack* &pj, BOOL bMain) +{ + CBotStack* pile1 = pj; + + CBotVar* var = pj->FindVar(m_var->GivToken()->GivString()); + if ( var != NULL ) var->SetUniqNum(((CBotLeftExprVar*)m_var)->m_nIdent); + + if ( bMain ) + { + pile1 = pj->RestoreStack(this); + CBotStack* pile = pile1; + if ( pile == NULL ) return; + + if ( pile1->GivState() == 0 ) + { + // cherche les dimensions max du tableau + CBotInstr* p = GivNext3b(); // les différentes formules + + while (p != NULL) + { + pile = pile->RestoreStack(); // petite place pour travailler + if ( pile == NULL ) return; + if ( pile->GivState() == 0 ) + { + p->RestoreState(pile, bMain); // calcul de la taille // interrompu! + return; + } + p = p->GivNext3b(); + } + } + if ( pile1->GivState() == 1 && m_listass != NULL ) + { + m_listass->RestoreState(pile1, bMain); + } + + } + + + if ( m_next2b ) m_next2b->RestoreState( pile1, bMain ); +} + +// cas particulier pour les indices vides +BOOL CBotEmpty :: Execute(CBotStack* &pj) +{ + CBotVar* pVar = CBotVar::Create("", CBotTypInt); + pVar->SetValInt(-1); // met la valeur -1 sur la pile + pj->SetVar(pVar); + return TRUE; +} + +void CBotEmpty :: RestoreState(CBotStack* &pj, BOOL bMain) +{ +} + +////////////////////////////////////////////////////////////////////////////////////// +// définition d'une liste d'initialisation pour un tableau +// int [ ] a [ ] = ( ( 1, 2, 3 ) , ( 3, 2, 1 ) ) ; + + +CBotListArray::CBotListArray() +{ + m_expr = NULL; + name = "CBotListArray"; +} + +CBotListArray::~CBotListArray() +{ + delete m_expr; +} + + +CBotInstr* CBotListArray::Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResult type) +{ + CBotCStack* pStk = pStack->TokenStack(p); + + CBotToken* pp = p; + + if ( IsOfType( p, ID_NULL ) ) + { + CBotInstr* inst = new CBotExprNull (); + inst->SetToken( pp ); +// CBotVar* var = CBotVar::Create("", CBotTypNullPointer); +// pStk->SetVar(var); + return pStack->Return(inst, pStk); // ok avec élément vide + } + + CBotListArray* inst = new CBotListArray(); // crée l'objet + + if ( IsOfType( p, ID_OPENPAR ) ) + { + // prend chaque élément l'un après l'autre + if ( type.Eq( CBotTypArrayPointer ) ) + { + type = type.GivTypElem(); + + pStk->SetStartError(p->GivStart()); + if ( NULL == ( inst->m_expr = CBotListArray::Compile( p, pStk, type ) ) ) + { + goto error; + } + + while ( IsOfType( p, ID_COMMA ) ) // d'autres éléments ? + { + pStk->SetStartError(p->GivStart()); + + CBotInstr* i = CBotListArray::Compile( p, pStk, type ); + if ( NULL == i ) + { + goto error; + } + + inst->m_expr->AddNext3(i); + } + } + else + { + pStk->SetStartError(p->GivStart()); + if ( NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk )) ) + { + goto error; + } + CBotVar* pv = pStk->GivVar(); // le résultat de l'expression + + if ( pv == NULL || !TypesCompatibles( type, pv->GivTypResult() )) // type compatible ? + { + pStk->SetError(TX_BADTYPE, p->GivStart()); + goto error; + } + + while ( IsOfType( p, ID_COMMA ) ) // d'autres éléments ? + { + pStk->SetStartError(p->GivStart()); + + CBotInstr* i = CBotTwoOpExpr::Compile( p, pStk ) ; + if ( NULL == i ) + { + goto error; + } + + CBotVar* pv = pStk->GivVar(); // le résultat de l'expression + + if ( pv == NULL || !TypesCompatibles( type, pv->GivTypResult() )) // type compatible ? + { + pStk->SetError(TX_BADTYPE, p->GivStart()); + goto error; + } + inst->m_expr->AddNext3(i); + } + } + + if (!IsOfType(p, ID_CLOSEPAR) ) + { + pStk->SetError(TX_CLOSEPAR, p->GivStart()); + goto error; + } + + return pStack->Return(inst, pStk); + } + +error: + delete inst; + return pStack->Return(NULL, pStk); +} + + +// exécute la définition d'un tableau + +BOOL CBotListArray::Execute(CBotStack* &pj, CBotVar* pVar) +{ + CBotStack* pile1 = pj->AddStack(); +// if ( pile1 == EOX ) return TRUE; + CBotVar* pVar2; + + CBotInstr* p = m_expr; + + int n = 0; + + for ( ; p != NULL ; n++, p = p->GivNext3() ) + { + if ( pile1->GivState() > n ) continue; + + pVar2 = pVar->GivItem(n, TRUE); + + if ( !p->Execute(pile1, pVar2) ) return FALSE; // évalue l'expression + + pile1->IncState(); + } + + return pj->Return( pile1 ); // transmet en dessous +} + +void CBotListArray::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( bMain ) + { + CBotStack* pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + CBotInstr* p = m_expr; + + int state = pile->GivState(); + + while( state-- > 0 ) p = p->GivNext3() ; + + p->RestoreState(pile, bMain); // calcul de la taille // interrompu! + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////// +// définition d'une variable entière +// int a, b = 12; + +CBotInt::CBotInt() +{ + m_next = NULL; // pour les définitions multiples + m_var = + m_expr = NULL; + name = "CBotInt"; +} + +CBotInt::~CBotInt() +{ + delete m_var; + delete m_expr; +// delete m_next; // fait par le destructeur de la classe de base ~CBotInstr() +} + +CBotInstr* CBotInstr::CompileArray(CBotToken* &p, CBotCStack* pStack, CBotTypResult type, BOOL first) +{ + if ( IsOfType(p, ID_OPBRK) ) + { + if ( !IsOfType(p, ID_CLBRK) ) + { + pStack->SetError(TX_CLBRK, p->GivStart()); + return NULL; + } + + CBotInstr* inst = CompileArray(p, pStack, CBotTypResult( CBotTypArrayPointer, type ), FALSE); + if ( inst != NULL || !pStack->IsOk() ) return inst; + } + + // compile une déclaration de tableau + if (first) return NULL ; + + CBotInstr* inst = CBotInstArray::Compile( p, pStack, type ); + if ( inst == NULL ) return NULL; + + if (IsOfType(p, ID_COMMA)) // plusieurs définitions enchaînées + { + if ( NULL != ( inst->m_next2b = CBotInstArray::CompileArray(p, pStack, type, FALSE) )) // compile la suivante + { + return inst; + } + delete inst; + return NULL; + } + + if (IsOfType(p, ID_SEP)) // instruction terminée + { + return inst; + } + + delete inst; + pStack->SetError(TX_ENDOF, p->GivStart()); + return NULL; +} + +CBotInstr* CBotInt::Compile(CBotToken* &p, CBotCStack* pStack, BOOL cont, BOOL noskip) +{ + CBotToken* pp = cont ? NULL : p; // pas de répétition du token "int" + + if (!cont && !IsOfType(p, ID_INT)) return NULL; + + CBotInt* inst = (CBotInt*)CompileArray(p, pStack, CBotTypInt); + if ( inst != NULL || !pStack->IsOk() ) return inst; + + CBotCStack* pStk = pStack->TokenStack(pp); + + inst = new CBotInt(); // crée l'objet + + inst->m_expr = NULL; + + CBotToken* vartoken = p; + inst->SetToken( vartoken ); + + // détermine l'expression valable pour l'élément gauche + if ( NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk )) ) + { + ((CBotLeftExprVar*)inst->m_var)->m_typevar = CBotTypInt; + if (pStk->CheckVarLocal(vartoken)) // redéfinition de la variable + { + pStk->SetError(TX_REDEFVAR, vartoken); + goto error; + } + + if (IsOfType(p, ID_OPBRK)) // avec des indices ? + { + delete inst; // n'est pas de type CBotInt + p = vartoken; // revient sur le nom de la variable + + // compile une déclaration de tableau + + CBotInstr* inst2 = CBotInstArray::Compile( p, pStk, CBotTypInt ); + + if (!pStk->IsOk() ) + { + pStk->SetError(TX_CLBRK, p->GivStart()); + goto error; + } + + if (IsOfType(p, ID_COMMA)) // plusieurs définitions enchaînées + { + if ( NULL != ( inst2->m_next2b = CBotInt::Compile(p, pStk, TRUE, noskip) )) // compile la suivante + { + return pStack->Return(inst2, pStk); + } + } + inst = (CBotInt*)inst2; + goto suite; // pas d'assignation, variable déjà créée + } + + if (IsOfType(p, ID_ASS)) // avec une assignation ? + { + if ( NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk )) ) + { + goto error; + } + if ( pStk->GivType() >= CBotTypBoolean ) // type compatible ? + { + pStk->SetError(TX_BADTYPE, p->GivStart()); + goto error; + } + } + + { + CBotVar* var = CBotVar::Create(vartoken, CBotTypInt);// crée la variable (après l'assignation évaluée) + var->SetInit(inst->m_expr != NULL); // la marque initialisée si avec assignation + var->SetUniqNum( + ((CBotLeftExprVar*)inst->m_var)->m_nIdent = CBotVar::NextUniqNum()); + // lui attribut un numéro unique + pStack->AddVar(var); // la place sur la pile + } + + if (IsOfType(p, ID_COMMA)) // plusieurs définitions enchaînées + { + if ( NULL != ( inst->m_next2b = CBotInt::Compile(p, pStk, TRUE, noskip) )) // compile la suivante + { + return pStack->Return(inst, pStk); + } + } +suite: + if (noskip || IsOfType(p, ID_SEP)) // instruction terminée + { + return pStack->Return(inst, pStk); + } + + pStk->SetError(TX_ENDOF, p->GivStart()); + } + +error: + delete inst; + return pStack->Return(NULL, pStk); +} + +// exécute la définition de la variable entière + +BOOL CBotInt::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); //indispensable pour SetState() +// if ( pile == EOX ) return TRUE; + + if ( pile->GivState()==0) + { + if (m_expr && !m_expr->Execute(pile)) return FALSE; // valeur initiale // interrompu? + m_var->Execute( pile ); // crée et fait l'assigation du résultat + + if (!pile->SetState(1)) return FALSE; + } + + if ( pile->IfStep() ) return FALSE; + + if ( m_next2b && + !m_next2b->Execute(pile)) return FALSE; // autre(s) définition(s) + + return pj->Return( pile ); // transmet en dessous +} + +void CBotInt::RestoreState(CBotStack* &pj, BOOL bMain) +{ + CBotStack* pile = pj; + if ( bMain ) + { + pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + if ( pile->GivState()==0) + { + if (m_expr) m_expr->RestoreState(pile, bMain); // valeur initiale // interrompu! + return; + } + } + + m_var->RestoreState(pile, bMain); + + if ( m_next2b ) m_next2b->RestoreState(pile, bMain); // autre(s) définition(s) +} + +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// définition d'une variable booléen +// int a, b = false; + +CBotBoolean::CBotBoolean() +{ + m_var = + m_expr = NULL; + name = "CBotBoolean"; +} + +CBotBoolean::~CBotBoolean() +{ + delete m_var; + delete m_expr; +} + +CBotInstr* CBotBoolean::Compile(CBotToken* &p, CBotCStack* pStack, BOOL cont, BOOL noskip) +{ + CBotToken* pp = cont ? NULL : p; + + if (!cont && !IsOfType(p, ID_BOOLEAN, ID_BOOL)) return NULL; + + CBotBoolean* inst = (CBotBoolean*)CompileArray(p, pStack, CBotTypBoolean); + if ( inst != NULL || !pStack->IsOk() ) return inst; + + CBotCStack* pStk = pStack->TokenStack(pp); + + inst = new CBotBoolean(); + + inst->m_expr = NULL; + + CBotToken* vartoken = p; + inst->SetToken( vartoken ); + CBotVar* var = NULL; + + if ( NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk )) ) + { + ((CBotLeftExprVar*)inst->m_var)->m_typevar = CBotTypBoolean; + if (pStk->CheckVarLocal(vartoken)) // redéfinition de la variable + { + pStk->SetError(TX_REDEFVAR, vartoken); + goto error; + } + + if (IsOfType(p, ID_OPBRK)) // avec des indices ? + { + delete inst; // n'est pas de type CBotInt + p = vartoken; // revient sur le nom de la variable + + // compile une déclaration de tableau + + inst = (CBotBoolean*)CBotInstArray::Compile( p, pStk, CBotTypBoolean ); + + if (!pStk->IsOk() ) + { + pStk->SetError(TX_CLBRK, p->GivStart()); + goto error; + } + goto suite; // pas d'assignation, variable déjà créée + } + + if (IsOfType(p, ID_ASS)) // avec une assignation ? + { + if ( NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk )) ) + { + goto error; + } + if ( !pStk->GivTypResult().Eq(CBotTypBoolean) ) // type compatible ? + { + pStk->SetError(TX_BADTYPE, p->GivStart()); + goto error; + } + } + + var = CBotVar::Create(vartoken, CBotTypBoolean);// crée la variable (après l'assignation évaluée) + var->SetInit(inst->m_expr != NULL); // la marque initialisée si avec assignation + var->SetUniqNum( + ((CBotLeftExprVar*)inst->m_var)->m_nIdent = CBotVar::NextUniqNum()); + // lui attribut un numéro unique + pStack->AddVar(var); // la place sur la pile +suite: + if (IsOfType(p, ID_COMMA)) // plusieurs définitions enchaînées + { + if ( NULL != ( inst->m_next2b = CBotBoolean::Compile(p, pStk, TRUE, noskip) )) // compile la suivante + { + return pStack->Return(inst, pStk); + } + } + + if (noskip || IsOfType(p, ID_SEP)) // instruction terminée + { + return pStack->Return(inst, pStk); + } + + pStk->SetError(TX_ENDOF, p->GivStart()); + } + +error: + delete inst; + return pStack->Return(NULL, pStk); +} + +// exécute une définition de variable booléenne + +BOOL CBotBoolean::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this);//indispensable pour SetState() +// if ( pile == EOX ) return TRUE; + + if ( pile->GivState()==0) + { + if (m_expr && !m_expr->Execute(pile)) return FALSE; // valeur initiale // interrompu? + m_var->Execute( pile ); // crée et fait l'assigation du résultat + + if (!pile->SetState(1)) return FALSE; + } + + if ( pile->IfStep() ) return FALSE; + + if ( m_next2b && + !m_next2b->Execute(pile)) return FALSE; // autre(s) définition(s) + + return pj->Return( pile ); // transmet en dessous +} + +void CBotBoolean::RestoreState(CBotStack* &pj, BOOL bMain) +{ + CBotStack* pile = pj; + if ( bMain ) + { + pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + if ( pile->GivState()==0) + { + if (m_expr) m_expr->RestoreState(pile, bMain); // valeur initiale interrompu? + return; + } + } + + m_var->RestoreState( pile, bMain ); // + + if ( m_next2b ) + m_next2b->RestoreState(pile, bMain); // autre(s) définition(s) +} + +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// définition d'une variable réelle +// int a, b = 12.4; + +CBotFloat::CBotFloat() +{ + m_var = + m_expr = NULL; + name = "CBotFloat"; +} + +CBotFloat::~CBotFloat() +{ + delete m_var; + delete m_expr; +} + +CBotInstr* CBotFloat::Compile(CBotToken* &p, CBotCStack* pStack, BOOL cont, BOOL noskip) +{ + CBotToken* pp = cont ? NULL : p; + + if (!cont && !IsOfType(p, ID_FLOAT)) return NULL; + + CBotFloat* inst = (CBotFloat*)CompileArray(p, pStack, CBotTypFloat); + if ( inst != NULL || !pStack->IsOk() ) return inst; + + CBotCStack* pStk = pStack->TokenStack(pp); + + inst = new CBotFloat(); + + inst->m_expr = NULL; + + CBotToken* vartoken = p; + CBotVar* var = NULL; + inst->SetToken(vartoken); + + if ( NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk )) ) + { + ((CBotLeftExprVar*)inst->m_var)->m_typevar = CBotTypFloat; + if (pStk->CheckVarLocal(vartoken)) // redéfinition de la variable + { + pStk->SetStartError(vartoken->GivStart()); + pStk->SetError(TX_REDEFVAR, vartoken->GivEnd()); + goto error; + } + + if (IsOfType(p, ID_OPBRK)) // avec des indices ? + { + delete inst; // n'est pas de type CBotInt + p = vartoken; // revient sur le nom de la variable + + // compile une déclaration de tableau + + inst = (CBotFloat*)CBotInstArray::Compile( p, pStk, CBotTypFloat ); + + if (!pStk->IsOk() ) + { + pStk->SetError(TX_CLBRK, p->GivStart()); + goto error; + } + goto suite; // pas d'assignation, variable déjà créée + } + + if (IsOfType(p, ID_ASS)) // avec une assignation ? + { + if ( NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk )) ) + { + goto error; + } + if ( pStk->GivType() >= CBotTypBoolean ) // type compatible ? + { + pStk->SetError(TX_BADTYPE, p->GivStart()); + goto error; + } + } + + var = CBotVar::Create(vartoken, CBotTypFloat); // crée la variable (après l'assignation évaluée) + var->SetInit(inst->m_expr != NULL); // la marque initialisée si avec assignation + var->SetUniqNum( + ((CBotLeftExprVar*)inst->m_var)->m_nIdent = CBotVar::NextUniqNum()); + // lui attribut un numéro unique + pStack->AddVar(var); // la place sur la pile +suite: + if (IsOfType(p, ID_COMMA)) // plusieurs définitions enchaînées + { + if ( NULL != ( inst->m_next2b = CBotFloat::Compile(p, pStk, TRUE, noskip) )) // compile la suivante + { + return pStack->Return(inst, pStk); + } + } + + if (noskip || IsOfType(p, ID_SEP)) // instruction terminée + { + return pStack->Return(inst, pStk); + } + + pStk->SetError(TX_ENDOF, p->GivStart()); + } + +error: + delete inst; + return pStack->Return(NULL, pStk); +} + +// exécute la défintion de la variable réelle + +BOOL CBotFloat::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this);//indispensable pour SetState() +// if ( pile == EOX ) return TRUE; + + if ( pile->GivState()==0) + { + if (m_expr && !m_expr->Execute(pile)) return FALSE; // valeur initiale // interrompu? + m_var->Execute( pile ); // crée et fait l'assigation du résultat + + if (!pile->SetState(1)) return FALSE; + } + + if ( pile->IfStep() ) return FALSE; + + if ( m_next2b && + !m_next2b->Execute(pile)) return FALSE; // autre(s) définition(s) + + return pj->Return( pile ); // transmet en dessous +} + +void CBotFloat::RestoreState(CBotStack* &pj, BOOL bMain) +{ + CBotStack* pile = pj; + if ( bMain ) + { + pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + if ( pile->GivState()==0) + { + if (m_expr) m_expr->RestoreState(pile, bMain); // valeur initiale interrompu? + return; + } + } + + m_var->RestoreState( pile, bMain ); // + + if ( m_next2b ) + m_next2b->RestoreState(pile, bMain); // autre(s) définition(s) +} + +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// définition d'une variable chaîne de caractères +// int a, b = "salut"; + +CBotIString::CBotIString() +{ + m_var = + m_expr = NULL; + name = "CBotIString"; +} + +CBotIString::~CBotIString() +{ + delete m_var; + delete m_expr; +} + +CBotInstr* CBotIString::Compile(CBotToken* &p, CBotCStack* pStack, BOOL cont, BOOL noskip) +{ + CBotToken* pp = cont ? NULL : p; + + if (!cont && !IsOfType(p, ID_STRING)) return NULL; + + CBotIString* inst = (CBotIString*)CompileArray(p, pStack, CBotTypString); + if ( inst != NULL || !pStack->IsOk() ) return inst; + + CBotCStack* pStk = pStack->TokenStack(pp); + + inst = new CBotIString(); + + inst->m_expr = NULL; + + CBotToken* vartoken = p; + inst->SetToken( vartoken ); + + if ( NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk )) ) + { + ((CBotLeftExprVar*)inst->m_var)->m_typevar = CBotTypString; + if (pStk->CheckVarLocal(vartoken)) // redéfinition de la variable + { + pStk->SetStartError(vartoken->GivStart()); + pStk->SetError(TX_REDEFVAR, vartoken->GivEnd()); + goto error; + } + + if (IsOfType(p, ID_ASS)) // avec une assignation ? + { + if ( NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk )) ) + { + goto error; + } +/* if ( !pStk->GivTypResult().Eq(CBotTypString) ) // type compatible ? + { + pStk->SetError(TX_BADTYPE, p->GivStart()); + goto error; + }*/ + } + + CBotVar* var = CBotVar::Create(vartoken, CBotTypString); // crée la variable (après l'assignation évaluée) + var->SetInit(inst->m_expr != NULL); // la marque initialisée si avec assignation + var->SetUniqNum( + ((CBotLeftExprVar*)inst->m_var)->m_nIdent = CBotVar::NextUniqNum()); + // lui attribut un numéro unique + pStack->AddVar(var); // la place sur la pile + + if (IsOfType(p, ID_COMMA)) // plusieurs définitions enchaînées + { + if ( NULL != ( inst->m_next2b = CBotIString::Compile(p, pStk, TRUE, noskip) )) // compile la suivante + { + return pStack->Return(inst, pStk); + } + } + + if (noskip || IsOfType(p, ID_SEP)) // instruction terminée + { + return pStack->Return(inst, pStk); + } + + pStk->SetError(TX_ENDOF, p->GivStart()); + } + +error: + delete inst; + return pStack->Return(NULL, pStk); +} + +// exécute la définition de la variable string + +BOOL CBotIString::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this);//indispensable pour SetState() +// if ( pile == EOX ) return TRUE; + + if ( pile->GivState()==0) + { + if (m_expr && !m_expr->Execute(pile)) return FALSE; // valeur initiale // interrompu? + m_var->Execute( pile ); // crée et fait l'assigation du résultat + + if (!pile->SetState(1)) return FALSE; + } + + if ( pile->IfStep() ) return FALSE; + + if ( m_next2b && + !m_next2b->Execute(pile)) return FALSE; // autre(s) définition(s) + + return pj->Return( pile ); // transmet en dessous +} + +void CBotIString::RestoreState(CBotStack* &pj, BOOL bMain) +{ + CBotStack* pile = pj; + + if ( bMain ) + { + pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + if ( pile->GivState()==0) + { + if (m_expr) m_expr->RestoreState(pile, bMain); // valeur initiale interrompu? + return; + } + } + + m_var->RestoreState( pile, bMain ); // + + if ( m_next2b ) + m_next2b->RestoreState(pile, bMain); // autre(s) définition(s) +} + +////////////////////////////////////////////////////////////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////////////////// +// compile une instruction de type " x = 123 " ou " z * 5 + 4 " +// avec ou sans assignation donc + +CBotExpression::CBotExpression() +{ + m_leftop = NULL; + m_rightop = NULL; + name = "CBotExpression"; +} + +CBotExpression::~CBotExpression() +{ + delete m_leftop; + delete m_rightop; +} + +CBotInstr* CBotExpression::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotToken* pp = p; + + CBotExpression* inst = new CBotExpression(); + + inst->m_leftop = CBotLeftExpr::Compile(p, pStack); + + inst->SetToken(p); + int OpType = p->GivType(); + + if ( pStack->IsOk() && + IsOfTypeList(p, ID_ASS, ID_ASSADD, ID_ASSSUB, ID_ASSMUL, ID_ASSDIV, ID_ASSMODULO, + ID_ASSAND, ID_ASSXOR, ID_ASSOR, + ID_ASSSL , ID_ASSSR, ID_ASSASR, 0 )) + { + if ( inst->m_leftop == NULL ) + { + pStack->SetError(TX_BADLEFT, p->GivEnd()); + delete inst; + return NULL; + } + + inst->m_rightop = CBotExpression::Compile(p, pStack); + if (inst->m_rightop == NULL) + { + delete inst; + return NULL; + } + + CBotTypResult type1 = pStack->GivTypResult(); + + // récupère la variable pour la marquer assignée + CBotVar* var = NULL; + inst->m_leftop->ExecuteVar(var, pStack); + if ( var == NULL ) + { + delete inst; + return NULL; + } + + if (OpType != ID_ASS && var->GivInit() != IS_DEF) + { + pStack->SetError(TX_NOTINIT, pp); + delete inst; + return NULL; + } + + CBotTypResult type2 = var->GivTypResult(); + + // quels sont les types acceptables ? + switch (OpType) + { + case ID_ASS: + // if (type2 == CBotTypClass) type2 = -1; // pas de classe + if ( (type1.Eq(CBotTypPointer) && type2.Eq(CBotTypPointer) ) || + (type1.Eq(CBotTypClass) && type2.Eq(CBotTypClass) ) ) + { +/* CBotClass* c1 = type1.GivClass(); + CBotClass* c2 = type2.GivClass(); + if ( !c1->IsChildOf(c2) ) type2.SetType(-1); // pas la même classe +//- if ( !type1.Eq(CBotTypClass) ) var->SetPointer(pStack->GivVar()->GivPointer());*/ + var->SetInit(2); + } + else + var->SetInit(TRUE); + + break; + case ID_ASSADD: + if (type2.Eq(CBotTypBoolean) || + type2.Eq(CBotTypPointer) ) type2 = -1; // nombres et chaines + break; + case ID_ASSSUB: + case ID_ASSMUL: + case ID_ASSDIV: + case ID_ASSMODULO: + if (type2.GivType() >= CBotTypBoolean) type2 = -1; // nombres uniquement + break; + } + + if (!TypeCompatible( type1, type2, OpType )) + { + pStack->SetError(TX_BADTYPE, &inst->m_token); + delete inst; + return NULL; + } + + return inst; // types compatibles ? + } + + delete inst; +// p = p->GivNext(); + int start, end, error = pStack->GivError(start, end); + + p = pp; // revient au début + pStack->SetError(0,0); // oublie l'erreur + +// return CBotTwoOpExpr::Compile(p, pStack); // essaie sans assignation + CBotInstr* i = CBotTwoOpExpr::Compile(p, pStack); // essaie sans assignation + if ( i != NULL && error == TX_PRIVATE && p->GivType() == ID_ASS ) + pStack->ResetError( error, start, end ); + return i; +} + +// exécute une expression avec assignation + +BOOL CBotExpression::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + + CBotToken* pToken = m_leftop->GivToken(); + CBotVar* pVar = NULL; + + CBotStack* pile1 = pile; + + BOOL IsInit = TRUE; + CBotVar* result = NULL; + + // doit être fait avant pour les indices éventuels (pile peut être changée) + if ( !m_leftop->ExecuteVar(pVar, pile, NULL, FALSE) ) return FALSE; // variable avant évaluation de la valeur droite + +// DEBUG( "CBotExpression::Execute", -1, pj); + if ( pile1->GivState()==0) + { + pile1->SetCopyVar(pVar); // garde une copie sur la pile (si interrompu) + pile1->IncState(); + } + + CBotStack* pile2 = pile->AddStack(); // attention pile et surtout pas pile1 + + if ( pile2->GivState()==0) + { +// DEBUG( "CBotExpression::Execute", -2, pj); + if (m_rightop && !m_rightop->Execute(pile2)) return FALSE; // valeur initiale // interrompu? + pile2->IncState(); + } + + if ( pile1->GivState() == 1 ) + { +// DEBUG( "CBotExpression::Execute", -3, pj); + if ( m_token.GivType() != ID_ASS ) + { + pVar = pile1->GivVar(); // récupére si interrompu + IsInit = pVar->GivInit(); + if ( IsInit == IS_NAN ) + { + pile2->SetError(TX_OPNAN, m_leftop->GivToken()); + return pj->Return(pile2); + } + result = CBotVar::Create("", pVar->GivTypResult(2)); + } + + switch ( m_token.GivType() ) + { + case ID_ASS: + break; + case ID_ASSADD: + result->Add(pile1->GivVar(), pile2->GivVar()); // additionne + pile2->SetVar(result); // re-place le résultat + break; + case ID_ASSSUB: + result->Sub(pile1->GivVar(), pile2->GivVar()); // soustrait + pile2->SetVar(result); // re-place le résultat + break; + case ID_ASSMUL: + result->Mul(pile1->GivVar(), pile2->GivVar()); // multiplie + pile2->SetVar(result); // re-place le résultat + break; + case ID_ASSDIV: + if (IsInit && + result->Div(pile1->GivVar(), pile2->GivVar())) // divise + pile2->SetError(TX_DIVZERO, &m_token); + pile2->SetVar(result); // re-place le résultat + break; + case ID_ASSMODULO: + if (IsInit && + result->Modulo(pile1->GivVar(), pile2->GivVar())) // reste de la division + pile2->SetError(TX_DIVZERO, &m_token); + pile2->SetVar(result); // re-place le résultat + break; + case ID_ASSAND: + result->And(pile1->GivVar(), pile2->GivVar()); // multiplie + pile2->SetVar(result); // re-place le résultat + break; + case ID_ASSXOR: + result->XOr(pile1->GivVar(), pile2->GivVar()); + pile2->SetVar(result); // re-place le résultat + break; + case ID_ASSOR: + result->Or(pile1->GivVar(), pile2->GivVar()); + pile2->SetVar(result); // re-place le résultat + break; + case ID_ASSSL: + result->SL(pile1->GivVar(), pile2->GivVar()); + pile2->SetVar(result); // re-place le résultat + break; + case ID_ASSSR: + result->SR(pile1->GivVar(), pile2->GivVar()); + pile2->SetVar(result); // re-place le résultat + break; + case ID_ASSASR: + result->ASR(pile1->GivVar(), pile2->GivVar()); + pile2->SetVar(result); // re-place le résultat + break; + default: + __asm int 3; + } + if (!IsInit) + pile2->SetError(TX_NOTINIT, m_leftop->GivToken()); + + pile1->IncState(); + } + +// DEBUG( "CBotExpression::Execute", -4, pj); + if ( !m_leftop->Execute( pile2, pile1 ) ) + return FALSE; // crée et fait l'assigation du résultat + + return pj->Return( pile2 ); // transmet en dessous +} + + +void CBotExpression::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( bMain ) + { + CBotToken* pToken = m_leftop->GivToken(); + CBotVar* pVar = NULL; + + CBotStack* pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + CBotStack* pile1 = pile; + + + if ( pile1->GivState()==0) + { + m_leftop->RestoreStateVar(pile, TRUE); // variable avant évaluation de la valeur droite + return; + } + + m_leftop->RestoreStateVar(pile, FALSE); // variable avant évaluation de la valeur droite + + CBotStack* pile2 = pile->RestoreStack(); // attention pile et surtout pas pile1 + if ( pile2 == NULL ) return; + + if ( pile2->GivState()==0) + { + if (m_rightop) m_rightop->RestoreState(pile2, bMain); // valeur initiale // interrompu? + return; + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// compile une instruction de type " ( condition ) " +// la condition doit être de type booléen + +// cette classe n'a pas de constructeur, car il n'y a jamais d'instance de cette classe +// l'objet retourné par Compile est généralement de type CBotExpression + +CBotInstr* CBotCondition::Compile(CBotToken* &p, CBotCStack* pStack) +{ + pStack->SetStartError(p->GivStart()); + if ( IsOfType(p, ID_OPENPAR )) + { + CBotInstr* inst = CBotBoolExpr::Compile( p, pStack ); + if ( NULL != inst ) + { + if ( IsOfType(p, ID_CLOSEPAR )) + { + return inst; + } + pStack->SetError(TX_CLOSEPAR, p->GivStart()); // manque la parenthèse + } + delete inst; + } + + pStack->SetError(TX_OPENPAR, p->GivStart()); // manque la parenthèse + + return NULL; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// compile une instruction de type " condition " +// la condition doit être de type booléen + +// cette classe n'a pas de constructeur, car il n'y a jamais d'instance de cette classe +// l'objet retourné par Compile est généralement de type CBotExpression + +CBotInstr* CBotBoolExpr::Compile(CBotToken* &p, CBotCStack* pStack) +{ + pStack->SetStartError(p->GivStart()); + + CBotInstr* inst = CBotTwoOpExpr::Compile( p, pStack ); + + if ( NULL != inst ) + { + if ( pStack->GivTypResult().Eq(CBotTypBoolean) ) + { + return inst; + } + pStack->SetError(TX_NOTBOOL, p->GivStart()); // n'est pas un booléan + } + + delete inst; + return NULL; +} + +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// compile soit : +// une instruction entre parenthèses (...) +// une expression unaire (négatif, not) +// nom de variable +// les variables prè et post incrémentées ou décrémentées +// un nombre donné par DefineNum +// une constante +// un appel de procédure +// l'instruction new + +// cette classe n'a pas de constructeur, car il n'y a jamais d'instance de cette classe +// l'objet retourné par Compile est de la classe correspondant à l'instruction + +CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotCStack* pStk = pStack->TokenStack(); + + pStk->SetStartError(p->GivStart()); + + // est-ce une expression entre parenthèse ? + if (IsOfType(p, ID_OPENPAR)) + { + CBotInstr* inst = CBotExpression::Compile( p, pStk ); + + if ( NULL != inst ) + { + if (IsOfType(p, ID_CLOSEPAR)) + { + return pStack->Return(inst, pStk); + } + pStk->SetError(TX_CLOSEPAR, p->GivStart()); + } + delete inst; + return pStack->Return(NULL, pStk); + } + + // est-ce une opération unaire ? + CBotInstr* inst = CBotExprUnaire::Compile(p, pStk); + if (inst != NULL || !pStk->IsOk()) + return pStack->Return(inst, pStk); + + // est-ce un nom de variable ? + if (p->GivType() == TokenTypVar) + { + // c'est peut-être un appel de méthode sans le "this." devant + inst = CBotExprVar::CompileMethode(p, pStk); + if ( inst != NULL ) return pStack->Return(inst, pStk); + + + // est-ce un appel de procédure ? + inst = CBotInstrCall::Compile(p, pStk); + if ( inst != NULL || !pStk->IsOk() ) + return pStack->Return(inst, pStk); + + + CBotToken* pvar = p; + // non, c'est une variable "ordinaire" + inst = CBotExprVar::Compile(p, pStk); + + CBotToken* pp = p; + // post incrémenté ou décrémenté ? + if (IsOfType(p, ID_INC, ID_DEC)) + { + if ( pStk->GivType() >= CBotTypBoolean ) + { + pStk->SetError(TX_BADTYPE, pp); + delete inst; + return pStack->Return(NULL, pStk); + } + + // recompile la variable pour read-only + delete inst; + p = pvar; + inst = CBotExprVar::Compile(p, pStk, PR_READ); + p = p->GivNext(); + + CBotPostIncExpr* i = new CBotPostIncExpr(); + i->SetToken(pp); + i->m_Instr = inst; // instruction associée + return pStack->Return(i, pStk); + } + return pStack->Return(inst, pStk); + } + + // est-ce une variable préincrémentée ou prédécrémentée ? + CBotToken* pp = p; + if (IsOfType(p, ID_INC, ID_DEC)) + { + CBotPreIncExpr* i = new CBotPreIncExpr(); + i->SetToken(pp); + + if (p->GivType() == TokenTypVar) + { + if (NULL != (i->m_Instr = CBotExprVar::Compile(p, pStk, PR_READ))) + { + if ( pStk->GivType() >= CBotTypBoolean ) + { + pStk->SetError(TX_BADTYPE, pp); + delete inst; + return pStack->Return(NULL, pStk); + } + return pStack->Return(i, pStk); + } + delete i; + return pStack->Return(NULL, pStk); + } + } + + // est-ce un nombre ou un DefineNum ? + if (p->GivType() == TokenTypNum || + p->GivType() == TokenTypDef ) + { + CBotInstr* inst = CBotExprNum::Compile( p, pStk ); + return pStack->Return(inst, pStk); + } + + // est-ce une chaine ? + if (p->GivType() == TokenTypString) + { + CBotInstr* inst = CBotExprAlpha::Compile(p, pStk); + return pStack->Return(inst, pStk); + } + + // est un élément "true" ou "false" + if (p->GivType() == ID_TRUE || + p->GivType() == ID_FALSE ) + { + CBotInstr* inst = CBotExprBool::Compile( p, pStk ); + return pStack->Return(inst, pStk); + } + + // est un objet à créer avec new + if (p->GivType() == ID_NEW) + { + CBotInstr* inst = CBotNew::Compile( p, pStk ); + return pStack->Return(inst, pStk); + } + + // est un pointeur nul + if (IsOfType( p, ID_NULL )) + { + CBotInstr* inst = new CBotExprNull (); + inst->SetToken( pp ); + CBotVar* var = CBotVar::Create("", CBotTypNullPointer); + pStk->SetVar(var); + return pStack->Return(inst, pStk); + } + + // est un nombre nan + if (IsOfType( p, ID_NAN )) + { + CBotInstr* inst = new CBotExprNan (); + inst->SetToken( pp ); + CBotVar* var = CBotVar::Create("", CBotTypInt); + var->SetInit(IS_NAN); + pStk->SetVar(var); + return pStack->Return(inst, pStk); + } + + + return pStack->Return(NULL, pStk); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////// +// gestion du post et pré- incrément/décrément + +// il n'y a pas de routine Compile, l'objet est créé directement +// dans CBotParExpr::Compile + +CBotPostIncExpr::CBotPostIncExpr() +{ + m_Instr = NULL; + name = "CBotPostIncExpr"; +} + +CBotPostIncExpr::~CBotPostIncExpr() +{ + delete m_Instr; +} + +CBotPreIncExpr::CBotPreIncExpr() +{ + m_Instr = NULL; + name = "CBotPreIncExpr"; +} + +CBotPreIncExpr::~CBotPreIncExpr() +{ + delete m_Instr; +} + +BOOL CBotPostIncExpr::Execute(CBotStack* &pj) +{ + CBotStack* pile1 = pj->AddStack(this); + CBotStack* pile2 = pile1; + + CBotVar* var1 = NULL; + + if ( !((CBotExprVar*)m_Instr)->ExecuteVar(var1, pile2, NULL, TRUE) ) return FALSE; // récupère la variable selon champs et index + + pile1->SetState(1); + pile1->SetCopyVar(var1); // place le résultat (avant incrémentation); + + CBotStack* pile3 = pile2->AddStack(this); + if ( pile3->IfStep() ) return FALSE; + + if ( var1->GivInit() == IS_NAN ) + { + pile1->SetError( TX_OPNAN, &m_token ); + } + + if ( var1->GivInit() != IS_DEF ) + { + pile1->SetError( TX_NOTINIT, &m_token ); + } + + if (GivTokenType() == ID_INC) var1->Inc(); + else var1->Dec(); + + return pj->Return(pile1); // opération faite, résultat sur pile2 +} + +void CBotPostIncExpr::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile1 = pj->RestoreStack(this); + if ( pile1 == NULL ) return; + + ((CBotExprVar*)m_Instr)->RestoreStateVar(pile1, bMain); + + if ( pile1 != NULL ) pile1->RestoreStack(this); +} + +BOOL CBotPreIncExpr::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + + CBotVar* var1; + + if ( pile->GivState() == 0 ) + { + CBotStack* pile2 = pile; + if ( !((CBotExprVar*)m_Instr)->ExecuteVar(var1, pile2, NULL, TRUE) ) return FALSE; // récupère la variable selon champs et index + // pile2 est modifié en retour + + if ( var1->GivInit() == IS_NAN ) + { + pile->SetError( TX_OPNAN, &m_token ); + return pj->Return(pile); // opération faite + } + + if ( var1->GivInit() != IS_DEF ) + { + pile->SetError( TX_NOTINIT, &m_token ); + return pj->Return(pile); // opération faite + } + + if (GivTokenType() == ID_INC) var1->Inc(); + else var1->Dec(); // ((CBotVarInt*)var1)->m_val + + pile->IncState(); + } + + if ( !m_Instr->Execute(pile) ) return FALSE; + return pj->Return(pile); // opération faite +} + + +void CBotPreIncExpr::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + if ( pile->GivState() == 0 ) + { + return; + } + + m_Instr->RestoreState(pile, bMain); +} + + +////////////////////////////////////////////////////////////////////////////////////// +// compile une expression unaire +// + +// - +// not +// ! +// ~ + +CBotExprUnaire::CBotExprUnaire() +{ + m_Expr = NULL; + name = "CBotExprUnaire"; +} + +CBotExprUnaire::~CBotExprUnaire() +{ + delete m_Expr; +} + +CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack) +{ + int op = p->GivType(); + CBotToken* pp = p; + if ( !IsOfTypeList( p, ID_ADD, ID_SUB, ID_LOG_NOT, ID_TXT_NOT, ID_NOT, 0 ) ) return NULL; + + CBotCStack* pStk = pStack->TokenStack(pp); + + CBotExprUnaire* inst = new CBotExprUnaire(); + inst->SetToken(pp); + + if ( NULL != (inst->m_Expr = CBotParExpr::Compile( p, pStk )) ) + { + if ( op == ID_ADD && pStk->GivType() < CBotTypBoolean ) // seulement avec des nombre + return pStack->Return(inst, pStk); + if ( op == ID_SUB && pStk->GivType() < CBotTypBoolean ) // seulement avec des nombre + return pStack->Return(inst, pStk); + if ( op == ID_NOT && pStk->GivType() < CBotTypFloat ) // seulement avec des entiers + return pStack->Return(inst, pStk); + if ( op == ID_LOG_NOT && pStk->GivTypResult().Eq(CBotTypBoolean) )// seulement avec des booléens + return pStack->Return(inst, pStk); + if ( op == ID_TXT_NOT && pStk->GivTypResult().Eq(CBotTypBoolean) )// seulement avec des booléens + return pStack->Return(inst, pStk); + + pStk->SetError(TX_BADTYPE, &inst->m_token); + } + delete inst; + return pStack->Return(NULL, pStk); +} + +// exécute l'expresson unaire + +BOOL CBotExprUnaire::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + + if ( pile->GivState() == 0 ) + { + if (!m_Expr->Execute( pile )) return FALSE; // interrompu ? + pile->IncState(); + } + + CBotStack* pile2 = pile->AddStack(); + if ( pile2->IfStep() ) return FALSE; + + CBotVar* var = pile->GivVar(); // récupère le résultat sur la pile + + switch (GivTokenType()) + { + case ID_ADD: + break; // ne fait donc rien + case ID_SUB: + var->Neg(); // change le signe + break; + case ID_NOT: + case ID_LOG_NOT: + case ID_TXT_NOT: + var->Not(); + break; + } + return pj->Return(pile); // transmet en dessous +} + +void CBotExprUnaire::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile = pj->RestoreStack(this); + if ( pile == NULL) return; + + if ( pile->GivState() == 0 ) + { + m_Expr->RestoreState( pile, bMain ); // interrompu ici ! + return; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////// +// gestion des index pour les tableaux +// array [ expression ] + + +CBotIndexExpr::CBotIndexExpr() +{ + m_expr = NULL; + name = "CBotIndexExpr"; +} + +CBotIndexExpr::~CBotIndexExpr() +{ + delete m_expr; +} + +// trouve un champ à partir de l'instance à la compilation + +BOOL CBotIndexExpr::ExecuteVar(CBotVar* &pVar, CBotCStack* &pile) +{ + if ( pVar->GivType(1) != CBotTypArrayPointer ) + __asm int 3; + + pVar = ((CBotVarArray*)pVar)->GivItem(0, FALSE); // à la compilation rend l'élément [0] + if ( pVar == NULL ) + { + pile->SetError(TX_OUTARRAY, m_token.GivEnd()); + return FALSE; + } + if ( m_next3 != NULL ) return m_next3->ExecuteVar(pVar, pile); + return TRUE; +} + +// idem à l'exécution +// attention, modifie le pointeur à la pile volontairement +// place les index calculés sur la pile supplémentaire + +BOOL CBotIndexExpr::ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, BOOL bStep, BOOL bExtend) +{ + CBotStack* pj = pile; +// DEBUG( "CBotIndexExpr::ExecuteVar", -1 , pj); + + if ( pVar->GivType(1) != CBotTypArrayPointer ) + __asm int 3; + + pile = pile->AddStack(); +// if ( pile == EOX ) return TRUE; + + if ( pile->GivState() == 0 ) + { + if ( !m_expr->Execute(pile) ) return FALSE; + pile->IncState(); + } + // traite les tableaux + + CBotVar* p = pile->GivVar(); // résultat sur la pile + + if ( p == NULL || p->GivType() > CBotTypDouble ) + { + pile->SetError(TX_BADINDEX, prevToken); + return pj->Return(pile); + } + + int n = p->GivValInt(); // position dans le tableau +// DEBUG( "CBotIndexExpr::ExecuteVar", n , pj); + + pVar = ((CBotVarArray*)pVar)->GivItem(n, bExtend); + if ( pVar == NULL ) + { + pile->SetError(TX_OUTARRAY, prevToken); + return pj->Return(pile); + } + +// DEBUG( "CBotIndexExpr::ExecuteVar", -2 , pj); + //if ( bUpdate ) + pVar->Maj(pile->GivPUser(), TRUE); + +// DEBUG( "CBotIndexExpr::ExecuteVar", -3 , pj); + if ( m_next3 != NULL && + !m_next3->ExecuteVar(pVar, pile, prevToken, bStep, bExtend) ) return FALSE; + +// DEBUG( "CBotIndexExpr::ExecuteVar", -4 , pj); + return TRUE; // ne libère pas la pile + // pour éviter de recalculer les index deux fois le cas échéant +} + +void CBotIndexExpr::RestoreStateVar(CBotStack* &pile, BOOL bMain) +{ + pile = pile->RestoreStack(); + if ( pile == NULL ) return; + + if ( bMain && pile->GivState() == 0 ) + { + m_expr->RestoreState(pile, TRUE); + return; + } + + if ( m_next3 ) + m_next3->RestoreStateVar(pile, bMain); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////// +// gestion des champs dans une instance (opérateur point) +// toto.x + + +CBotFieldExpr::CBotFieldExpr() +{ + name = "CBotFieldExpr"; + m_nIdent = 0; +} + +CBotFieldExpr::~CBotFieldExpr() +{ +} + +void CBotFieldExpr::SetUniqNum(int num) +{ + m_nIdent = num; +} + + +// trouve un champ à partir de l'instance à la compilation + +BOOL CBotFieldExpr::ExecuteVar(CBotVar* &pVar, CBotCStack* &pile) +{ + if ( pVar->GivType(1) != CBotTypPointer ) + __asm int 3; + +// pVar = pVar->GivItem(m_token.GivString()); + pVar = pVar->GivItemRef(m_nIdent); + if ( pVar == NULL ) + { + pile->SetError(TX_NOITEM, &m_token); + return FALSE; + } + + if ( m_next3 != NULL && + !m_next3->ExecuteVar(pVar, pile) ) return FALSE; + + return TRUE; +} + +// idem à l'exécution + +BOOL CBotFieldExpr::ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, BOOL bStep, BOOL bExtend) +{ + CBotStack* pj = pile; + pile = pile->AddStack(this); // modifie pile en sortie + if ( pile == EOX ) return TRUE; + +// DEBUG( "CBotFieldExpre::ExecuteVar "+m_token.GivString(), 0, pj ); + + if ( pVar->GivType(1) != CBotTypPointer ) + __asm int 3; + + CBotVarClass* pItem = pVar->GivPointer(); + if ( pItem == NULL ) + { + pile->SetError(TX_NULLPT, prevToken); + return pj->Return( pile ); + } + if ( pItem->GivUserPtr() == OBJECTDELETED ) + { + pile->SetError(TX_DELETEDPT, prevToken); + return pj->Return( pile ); + } + + if ( bStep && pile->IfStep() ) return FALSE; + +// pVar = pVar->GivItem(m_token.GivString()); + pVar = pVar->GivItemRef(m_nIdent); + if ( pVar == NULL ) + { + pile->SetError(TX_NOITEM, &m_token); + return pj->Return( pile ); + } + + if ( pVar->IsStatic() ) + { +// DEBUG( "IsStatic", 0, pj) ; + // pour une variable statique, la prend dans la classe elle-même + CBotClass* pClass = pItem->GivClass(); + pVar = pClass->GivItem(m_token.GivString()); +// DEBUG( "done "+pVar->GivName(), 0, pj) ; + } + + // demande la mise à jour de l'élément, s'il y a lieu + pVar->Maj(pile->GivPUser(), TRUE); + + if ( m_next3 != NULL && + !m_next3->ExecuteVar(pVar, pile, &m_token, bStep, bExtend) ) return FALSE; + + return TRUE; // ne libère pas la pile + // pour conserver l'état SetState() correspondant à l'étape +} + +void CBotFieldExpr::RestoreStateVar(CBotStack* &pj, BOOL bMain) +{ + pj = pj->RestoreStack(this); // modifie pj en sortie + if ( pj == NULL ) return; + + if ( m_next3 != NULL ) + m_next3->RestoreStateVar(pj, bMain); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////// +// compile un opérande gauche pour une assignation + +CBotLeftExpr::CBotLeftExpr() +{ + name = "CBotLeftExpr"; + m_nIdent = 0; +} + +CBotLeftExpr::~CBotLeftExpr() +{ +} + +// compile une expression pour un left-opérande ( à gauche d'une assignation) +// cela peut être +// toto +// toto[ 3 ] +// toto.x +// toto.pos.x +// toto[2].pos.x +// toto[1].pos[2].x +// toto[1][2][3] + +CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotCStack* pStk = pStack->TokenStack(); + + pStk->SetStartError(p->GivStart()); + + // est-ce un nom de variable ? + if (p->GivType() == TokenTypVar) + { + CBotLeftExpr* inst = new CBotLeftExpr(); // crée l'objet + + inst->SetToken(p); + + CBotVar* var; + + if ( NULL != (var = pStk->FindVar(p)) ) // cherche si variable connue + { + inst->m_nIdent = var->GivUniqNum(); + if (inst->m_nIdent > 0 && inst->m_nIdent < 9000) + { + if ( var->IsPrivate(PR_READ) && + !pStk->GivBotCall()->m_bCompileClass) + { + pStk->SetError( TX_PRIVATE, p ); + goto err; + } + // il s'agit d'un élement de la classe courante + // ajoute l'équivalent d'un this. devant + CBotToken pthis("this"); + inst->SetToken(&pthis); + inst->m_nIdent = -2; // ident pour this + + CBotFieldExpr* i = new CBotFieldExpr(); // nouvel élément + i->SetToken( p ); // garde le nom du token + inst->AddNext3(i); // ajoute à la suite + + var = pStk->FindVar(pthis); + var = var->GivItem(p->GivString()); + i->SetUniqNum(var->GivUniqNum()); + } + p = p->GivNext(); // token suivant + + while (TRUE) + { + if ( var->GivType() == CBotTypArrayPointer ) // s'il sagit d'un tableau + { + if ( IsOfType( p, ID_OPBRK ) ) // regarde s'il y a un index + { + CBotIndexExpr* i = new CBotIndexExpr(); + i->m_expr = CBotExpression::Compile(p, pStk); // compile la formule + inst->AddNext3(i); // ajoute à la chaine + + var = ((CBotVarArray*)var)->GivItem(0,TRUE); // donne le composant [0] + + if ( i->m_expr == NULL ) + { + pStk->SetError( TX_BADINDEX, p->GivStart() ); + goto err; + } + + if ( !pStk->IsOk() || !IsOfType( p, ID_CLBRK ) ) + { + pStk->SetError( TX_CLBRK, p->GivStart() ); + goto err; + } + continue; + } + } + + if ( var->GivType(1) == CBotTypPointer ) // pour les classes + { + if ( IsOfType(p, ID_DOT) ) + { + CBotToken* pp = p; + + CBotFieldExpr* i = new CBotFieldExpr(); // nouvel élément + i->SetToken( pp ); // garde le nom du token + inst->AddNext3(i); // ajoute à la suite + + if ( p->GivType() == TokenTypVar ) // doit être un nom + { + var = var->GivItem(p->GivString()); // récupère l'item correpondant + if ( var != NULL ) + { + if ( var->IsPrivate(PR_READ) && + !pStk->GivBotCall()->m_bCompileClass) + { + pStk->SetError( TX_PRIVATE, pp ); + goto err; + } + + i->SetUniqNum(var->GivUniqNum()); + p = p->GivNext(); // saute le nom + continue; + } + pStk->SetError( TX_NOITEM, p ); + } + pStk->SetError( TX_DOT, p->GivStart() ); + goto err; + } + } + break; + } + + + if ( pStk->IsOk() ) return (CBotLeftExpr*) pStack->Return(inst, pStk); + } + pStk->SetError(TX_UNDEFVAR, p); +err: + delete inst; + return (CBotLeftExpr*) pStack->Return(NULL, pStk); + } + + return (CBotLeftExpr*) pStack->Return(NULL, pStk); +} + +// exécute, trouve une variable et lui assigne le résultat de la pile + +BOOL CBotLeftExpr::Execute(CBotStack* &pj, CBotStack* array) +{ + CBotStack* pile = pj->AddStack(); +// if ( pile == EOX ) return TRUE; + +// if ( pile->IfStep() ) return FALSE; + + CBotVar* var1 = NULL; + CBotVar* var2 = NULL; + +// var1 = pile->FindVar(m_token, FALSE, TRUE); + if (!ExecuteVar( var1, array, NULL, FALSE )) return FALSE; + // retrouve la variable (et pas la copie) + if (pile->IfStep()) return FALSE; + + if ( var1 ) + { + var2 = pj->GivVar(); // resultat sur la pile d'entrée + if ( var2 ) + { + CBotTypResult t1 = var1->GivTypResult(); + CBotTypResult t2 = var2->GivTypResult(); + if ( t2.Eq(CBotTypPointer) ) + { + CBotClass* c1 = t1.GivClass(); + CBotClass* c2 = t2.GivClass(); + if ( !c2->IsChildOf(c1)) + { + CBotToken* pt = &m_token; + pile->SetError(TX_BADTYPE, pt); + return pj->Return(pile); // opération faite + } + } + var1->SetVal(var2); // fait l'assignation + } + pile->SetCopyVar( var1 ); // remplace sur la pile par une copie de la variable elle-même + // (pour avoir le nom) + } + + return pj->Return(pile); // opération faite +} + +// retrouve une variable pendant la compilation + +BOOL CBotLeftExpr::ExecuteVar(CBotVar* &pVar, CBotCStack* &pile) +{ + pVar = pile->FindVar(m_token); + if ( pVar == NULL ) return FALSE; + + if ( m_next3 != NULL && + !m_next3->ExecuteVar(pVar, pile) ) return FALSE; + + return TRUE; +} + +// retrouve une variable à l'exécution + +BOOL CBotLeftExpr::ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, BOOL bStep) +{ + pile = pile->AddStack( this ); // déplace la pile + + pVar = pile->FindVar(m_nIdent); + if ( pVar == NULL ) + { +#ifdef _DEBUG + __asm int 3; +#endif + pile->SetError(2, &m_token); + return FALSE; + } + + if ( bStep && m_next3 == NULL && pile->IfStep() ) return FALSE; + + if ( m_next3 != NULL && + !m_next3->ExecuteVar(pVar, pile, &m_token, bStep, TRUE) ) return FALSE; + + return TRUE; +} + +void CBotLeftExpr::RestoreStateVar(CBotStack* &pile, BOOL bMain) +{ + pile = pile->RestoreStack( this ); // déplace la pile + if ( pile == NULL ) return; + + if ( m_next3 != NULL ) + m_next3->RestoreStateVar(pile, bMain); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +// transforme une chaîne en nombre entier +// peut être de la forme 0xabc123 + +long GivNumInt( const char* p ) +{ + long num = 0; + while (*p >= '0' && *p <= '9') + { + num = num * 10 + *p - '0'; + p++; + } + if ( *p == 'x' || *p == 'X' ) + { + while (*++p != 0) + { + if ( *p >= '0' && *p <= '9' ) + { + num = num * 16 + *p - '0'; + continue; + } + if ( *p >= 'A' && *p <= 'F' ) + { + num = num * 16 + *p - 'A' + 10; + continue; + } + if ( *p >= 'a' && *p <= 'f' ) + { + num = num * 16 + *p - 'a' + 10; + continue; + } + break; + } + } + return num; +} + +// transforme une chaîne en un nombre réel + +extern float GivNumFloat( const char* p ) +{ + double num = 0; + double div = 10; + BOOL bNeg = FALSE; + + if (*p == '-') + { + bNeg = TRUE; + p++; + } + while (*p >= '0' && *p <= '9') + { + num = num * 10. + (*p - '0'); + p++; + } + + if ( *p == '.' ) + { + p++; + while (*p >= '0' && *p <= '9') + { + num = num + (*p - '0') / div; + div = div * 10; + p++; + } + } + + int exp = 0; + if ( *p == 'e' || *p == 'E' ) + { + char neg = 0; + p++; + if ( *p == '-' || *p == '+' ) neg = *p++; + + while (*p >= '0' && *p <= '9') + { + exp = exp * 10 + (*p - '0'); + p++; + } + if ( neg == '-' ) exp = -exp; + } + + while ( exp > 0 ) + { + num *= 10.0; + exp--; + } + + while ( exp < 0 ) + { + num /= 10.0; + exp++; + } + + if ( bNeg ) num = -num; + return (float)num; +} + +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// compile un token représentant un nombre + +CBotExprNum::CBotExprNum() +{ + name = "CBotExprNum"; +} + +CBotExprNum::~CBotExprNum() +{ +} + +CBotInstr* CBotExprNum::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotCStack* pStk = pStack->TokenStack(); + + CBotExprNum* inst = new CBotExprNum(); + + inst->SetToken(p); + CBotString s = p->GivString(); + + inst->m_numtype = CBotTypInt; + if ( p->GivType() == TokenTypDef ) + { + inst->m_valint = p->GivIdKey(); + } + else + { + if ( s.Find('.') >= 0 || ( s.Find('x') < 0 && ( s.Find('e') >= 0 || s.Find('E') >= 0 ) ) ) + { + inst->m_numtype = CBotTypFloat; + inst->m_valfloat = GivNumFloat(s); + } + else + { + inst->m_valint = GivNumInt(s); + } + } + + if (pStk->NextToken(p)) + { + CBotVar* var = CBotVar::Create((CBotToken*)NULL, inst->m_numtype); + pStk->SetVar(var); + + return pStack->Return(inst, pStk); + } + delete inst; + return pStack->Return(NULL, pStk); +} + +// exécute, retourne le nombre correspondant + +BOOL CBotExprNum::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + + CBotVar* var = CBotVar::Create((CBotToken*)NULL, m_numtype); + + CBotString nombre ; + if ( m_token.GivType() == TokenTypDef ) + { + nombre = m_token.GivString(); + } + + switch (m_numtype) + { + case CBotTypShort: + case CBotTypInt: + var->SetValInt( m_valint, nombre ); // valeur du nombre + break; + case CBotTypFloat: + var->SetValFloat( m_valfloat ); // valeur du nombre + break; + } + pile->SetVar( var ); // mis sur la pile + + return pj->Return(pile); // c'est ok +} + +void CBotExprNum::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( bMain ) pj->RestoreStack(this); +} + +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// compile un token représentant une chaine de caractères + +CBotExprAlpha::CBotExprAlpha() +{ + name = "CBotExprAlpha"; +} + +CBotExprAlpha::~CBotExprAlpha() +{ +} + +CBotInstr* CBotExprAlpha::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotCStack* pStk = pStack->TokenStack(); + + CBotExprAlpha* inst = new CBotExprAlpha(); + + inst->SetToken(p); + p = p->GivNext(); + + CBotVar* var = CBotVar::Create((CBotToken*)NULL, CBotTypString); + pStk->SetVar(var); + + return pStack->Return(inst, pStk); +} + +// exécute, retourne la chaîne correspondante + +BOOL CBotExprAlpha::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + + CBotVar* var = CBotVar::Create((CBotToken*)NULL, CBotTypString); + + CBotString chaine = m_token.GivString(); + chaine = chaine.Mid(1, chaine.GivLength()-2); // enlève les guillemets + + var->SetValString( chaine ); // valeur du nombre + + pile->SetVar( var ); // mis sur la pile + + return pj->Return(pile); +} + +void CBotExprAlpha::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( bMain ) pj->RestoreStack(this); +} + +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// compile un token représentant true ou false + +CBotExprBool::CBotExprBool() +{ + name = "CBotExprBool"; +} + +CBotExprBool::~CBotExprBool() +{ +} + +CBotInstr* CBotExprBool::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotCStack* pStk = pStack->TokenStack(); + CBotExprBool* inst = NULL; + + if ( p->GivType() == ID_TRUE || + p->GivType() == ID_FALSE ) + { + inst = new CBotExprBool(); + inst->SetToken(p); // mémorise l'opération false ou true + p = p->GivNext(); + + CBotVar* var = CBotVar::Create((CBotToken*)NULL, CBotTypBoolean); + pStk->SetVar(var); + } + + return pStack->Return(inst, pStk); +} + +// exécute, retourne true ou false + +BOOL CBotExprBool::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + + CBotVar* var = CBotVar::Create((CBotToken*)NULL, CBotTypBoolean); + + if (GivTokenType() == ID_TRUE) var->SetValInt(1); + else var->SetValInt(0); + + pile->SetVar( var ); // mis sur la pile + return pj->Return(pile); // transmet en dessous +} + +void CBotExprBool::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( bMain ) pj->RestoreStack(this); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +// gestion de l'opérande "null" + +CBotExprNull::CBotExprNull() +{ + name = "CBotExprNull"; +} + +CBotExprNull::~CBotExprNull() +{ +} + +// exécute, retourne un pointeur vide + +BOOL CBotExprNull::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + CBotVar* var = CBotVar::Create((CBotToken*)NULL, CBotTypNullPointer); + + var->SetInit(TRUE); // pointeur null valide + pile->SetVar( var ); // mis sur la pile + return pj->Return(pile); // transmet en dessous +} + +void CBotExprNull::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( bMain ) pj->RestoreStack(this); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +// gestion de l'opérande "nan" + +CBotExprNan::CBotExprNan() +{ + name = "CBotExprNan"; +} + +CBotExprNan::~CBotExprNan() +{ +} + +// exécute, retourne un pointeur vide + +BOOL CBotExprNan::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + CBotVar* var = CBotVar::Create((CBotToken*)NULL, CBotTypInt); + + var->SetInit(IS_NAN); // nombre nan + pile->SetVar( var ); // mis sur la pile + return pj->Return(pile); // transmet en dessous +} + +void CBotExprNan::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( bMain ) pj->RestoreStack(this); +} + +////////////////////////////////////////////////////////////////////////////////////// +// compile un nom de variable +// vérifie qu'elle est connue sur la pile +// et qu'elle a été initialisée + +CBotExprVar::CBotExprVar() +{ + name = "CBotExprVar"; + m_nIdent = 0; +} + +CBotExprVar::~CBotExprVar() +{ +} + +CBotInstr* CBotExprVar::Compile(CBotToken* &p, CBotCStack* pStack, int privat) +{ + CBotToken* pDebut = p; + CBotCStack* pStk = pStack->TokenStack(); + + pStk->SetStartError(p->GivStart()); + + // est-ce un nom de variable ? + if (p->GivType() == TokenTypVar) + { + CBotInstr* inst = new CBotExprVar(); // crée l'objet + + inst->SetToken(p); + + CBotVar* var; + + if ( NULL != (var = pStk->FindVar(p)) ) // cherche si variable connue + { + int ident = var->GivUniqNum(); + ((CBotExprVar*)inst)->m_nIdent = ident; // l'identifie par son numéro + + if (ident > 0 && ident < 9000) + { + if ( var->IsPrivate(privat) && + !pStk->GivBotCall()->m_bCompileClass) + { + pStk->SetError( TX_PRIVATE, p ); + goto err; + } + + // il s'agit d'un élement de la classe courante + // ajoute l'équivalent d'un this. devant + inst->SetToken(&CBotToken("this")); + ((CBotExprVar*)inst)->m_nIdent = -2; // ident pour this + + CBotFieldExpr* i = new CBotFieldExpr(); // nouvel élément + i->SetToken( p ); // garde le nom du token + i->SetUniqNum(ident); + inst->AddNext3(i); // ajoute à la suite + } + + p = p->GivNext(); // token suivant + + while (TRUE) + { + if ( var->GivType() == CBotTypArrayPointer ) // s'il sagit d'un tableau + { + if ( IsOfType( p, ID_OPBRK ) ) // regarde s'il y a un index + { + CBotIndexExpr* i = new CBotIndexExpr(); + i->m_expr = CBotExpression::Compile(p, pStk); // compile la formule + inst->AddNext3(i); // ajoute à la chaine + + var = ((CBotVarArray*)var)->GivItem(0,TRUE); // donne le composant [0] + + if ( i->m_expr == NULL ) + { + pStk->SetError( TX_BADINDEX, p->GivStart() ); + goto err; + } + if ( !pStk->IsOk() || !IsOfType( p, ID_CLBRK ) ) + { + pStk->SetError( TX_CLBRK, p->GivStart() ); + goto err; + } + continue; + } + //// pStk->SetError( TX_OPBRK, p->GivStart() ); + } + if ( var->GivType(1) == CBotTypPointer ) // pour les classes + { + if ( IsOfType(p, ID_DOT) ) + { + CBotToken* pp = p; + + if ( p->GivType() == TokenTypVar ) // doit être un nom + { + if ( p->GivNext()->GivType() == ID_OPENPAR )// un appel de méthode ? + { + CBotInstr* i = CBotInstrMethode::Compile(p, pStk, var); + if ( !pStk->IsOk() ) goto err; + inst->AddNext3(i); // ajoute à la suite + return pStack->Return(inst, pStk); + } + else + { + CBotFieldExpr* i = new CBotFieldExpr(); // nouvel élément + i->SetToken( pp ); // garde le nom du token + inst->AddNext3(i); // ajoute à la suite + var = var->GivItem(p->GivString()); // récupère l'item correpondant + if ( var != NULL ) + { + i->SetUniqNum(var->GivUniqNum()); + if ( var->IsPrivate() && + !pStk->GivBotCall()->m_bCompileClass) + { + pStk->SetError( TX_PRIVATE, pp ); + goto err; + } + } + } + + + if ( var != NULL ) + { + p = p->GivNext(); // saute le nom + continue; + } + pStk->SetError( TX_NOITEM, p ); + goto err; + } + pStk->SetError( TX_DOT, p->GivStart() ); + goto err; + } + } + + break; + } + + pStk->SetCopyVar(var); // place une copie de la variable sur la pile (pour le type) + if ( pStk->IsOk() ) return pStack->Return(inst, pStk); + } + pStk->SetError(TX_UNDEFVAR, p); +err: + delete inst; + return pStack->Return(NULL, pStk); + } + + return pStack->Return(NULL, pStk); +} + +CBotInstr* CBotExprVar::CompileMethode(CBotToken* &p, CBotCStack* pStack) +{ + CBotToken* pp = p; + CBotCStack* pStk = pStack->TokenStack(); + + pStk->SetStartError(pp->GivStart()); + + // est-ce un nom de variable ? + if (pp->GivType() == TokenTypVar) + { + CBotToken pthis("this"); + CBotVar* var = pStk->FindVar(pthis); + if ( var == 0 ) return pStack->Return(NULL, pStk); + + CBotInstr* inst = new CBotExprVar(); // crée l'objet + + // il s'agit d'un élement de la classe courante + // ajoute l'équivalent d'un this. devant + inst->SetToken(&pthis); + ((CBotExprVar*)inst)->m_nIdent = -2; // ident pour this + + CBotToken* pp = p; + + if ( pp->GivType() == TokenTypVar ) // doit être un nom + { + if ( pp->GivNext()->GivType() == ID_OPENPAR ) // un appel de méthode ? + { + CBotInstr* i = CBotInstrMethode::Compile(pp, pStk, var); + if ( pStk->IsOk() ) + { + inst->AddNext3(i); // ajoute à la suite + p = pp; // instructions passées + return pStack->Return(inst, pStk); + } + pStk->SetError(0,0); // l'erreur n'est pas traitée ici + } + } + delete inst; + } + return pStack->Return(NULL, pStk); +} + + +// exécute, rend la valeur d'une variable + +BOOL CBotExprVar::Execute(CBotStack* &pj) +{ + CBotVar* pVar = NULL; + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + +// if ( pile->IfStep() ) return FALSE; + + CBotStack* pile1 = pile; + + if ( pile1->GivState() == 0 ) + { + if ( !ExecuteVar(pVar, pile, NULL, TRUE) ) return FALSE; // récupère la variable selon champs et index +// DEBUG("CBotExprVar::Execute", 1 , pj); + + if ( pVar ) pile1->SetCopyVar(pVar); // la place une copie sur la pile + else + { +//-- pile1->SetVar(NULL); // la pile contient déjà le resultat (méthode) + return pj->Return(pile1); + } + pile1->IncState(); + } + + pVar = pile1->GivVar(); // récupère si interrompu + + if ( pVar == NULL ) + { +// pile1->SetError(TX_NULLPT, &m_token); + return pj->Return(pile1); + } + + if ( pVar->GivInit() == IS_UNDEF ) + { + CBotToken* pt = &m_token; + while ( pt->GivNext() != NULL ) pt = pt->GivNext(); + pile1->SetError(TX_NOTINIT, pt); + return pj->Return(pile1); + } + return pj->Return(pile1); // opération faite +} + +void CBotExprVar::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + CBotStack* pile1 = pile; + + if ( pile1->GivState() == 0 ) + { + RestoreStateVar(pile, bMain); // récupère la variable selon champs et index + return; + } +} + +// retrouve une variable à l'exécution + +BOOL CBotExprVar::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* prevToken, BOOL bStep) +{ + CBotStack* pile = pj; + pj = pj->AddStack( this ); + + if ( bStep && m_nIdent>0 && pj->IfStep() ) return FALSE; + + pVar = pj->FindVar(m_nIdent, TRUE); // cherche la variable avec mise à jour si nécessaire + if ( pVar == NULL ) + { +#ifdef _DEBUG + __asm int 3; +#endif + pj->SetError(1, &m_token); + return FALSE; + } + if ( m_next3 != NULL && + !m_next3->ExecuteVar(pVar, pj, &m_token, bStep, FALSE) ) + return FALSE; // Champs d'une instance, tableau, méthode ? + + return pile->ReturnKeep( pj ); // ne rend pas la pile mais récupère le résultat si une méthode a été appelée +} + + +// retrouve une variable à l'exécution + +void CBotExprVar::RestoreStateVar(CBotStack* &pj, BOOL bMain) +{ + pj = pj->RestoreStack( this ); + if ( pj == NULL ) return; + + if ( m_next3 != NULL ) + m_next3->RestoreStateVar(pj, bMain); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +// compile une liste de paramètres + +CBotInstr* CompileParams(CBotToken* &p, CBotCStack* pStack, CBotVar** ppVars) +{ + BOOL first = TRUE; + CBotInstr* ret = NULL; // pour la liste à retourner + +// pStack->SetStartError(p->GivStart()); + CBotCStack* pile = pStack; + int i = 0; + + if ( IsOfType(p, ID_OPENPAR) ) + { + int start, end; + if (!IsOfType(p, ID_CLOSEPAR)) while (TRUE) + { + start = p->GivStart(); + pile = pile->TokenStack(); // garde les résultats sur la pile + + if ( first ) pStack->SetStartError(start); + first = FALSE; + + CBotInstr* param = CBotExpression::Compile(p, pile); + end = p->GivStart(); + + if ( !pile->IsOk() ) + { + return pStack->Return(NULL, pile); + } + + if ( ret == NULL ) ret = param; + else ret->AddNext(param); // construit la liste + + if ( param != NULL ) + { + if ( pile->GivTypResult().Eq(99) ) + { + delete pStack->TokenStack(); + pStack->SetError(TX_VOID, p->GivStart()); + return NULL; + } + ppVars[i] = pile->GivVar(); + ppVars[i]->GivToken()->SetPos(start, end); + i++; + + if (IsOfType(p, ID_COMMA)) continue; // saute la virgule + if (IsOfType(p, ID_CLOSEPAR)) break; + } + + pStack->SetError(TX_CLOSEPAR, p->GivStart()); + delete pStack->TokenStack(); + return NULL; + } + } + ppVars[i] = NULL; + return ret; +} + +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////// +// compile un appel d'une méthode + +CBotInstrMethode::CBotInstrMethode() +{ + m_Parameters = NULL; + m_MethodeIdent = 0; +// m_nThisIdent = 0; + name = "CBotInstrMethode"; +} + +CBotInstrMethode::~CBotInstrMethode() +{ + delete m_Parameters; +} + +CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* var) +{ + CBotInstrMethode* inst = new CBotInstrMethode(); + inst->SetToken(p); // token correspondant + +// inst->m_nThisIdent = CBotVar::NextUniqNum(); + + if ( NULL != var ) + { + CBotToken* pp = p; + p = p->GivNext(); + + if ( p->GivType() == ID_OPENPAR ) + { + inst->m_NomMethod = pp->GivString(); + + // compile la liste des paramètres + CBotVar* ppVars[1000]; + inst->m_Parameters = CompileParams(p, pStack, ppVars); + + if ( pStack->IsOk() ) + { + CBotClass* pClass = var->GivClass(); // pointeur à la classe + inst->m_ClassName = pClass->GivName(); // le nom de la classe + CBotTypResult r = pClass->CompileMethode(inst->m_NomMethod, var, ppVars, + pStack, inst->m_MethodeIdent); + delete pStack->TokenStack(); // libères les paramètres encore sur la pile + inst->m_typRes = r; + + if (inst->m_typRes.GivType() > 20) + { + pStack->SetError(inst->m_typRes.GivType(), pp); + delete inst; + return NULL; + } + // met un résultat sur la pile pour avoir quelque chose + if (inst->m_typRes.GivType() > 0) + { + CBotVar* pResult = CBotVar::Create("", inst->m_typRes); + if (inst->m_typRes.Eq(CBotTypClass)) + { +// CBotClass* pClass = CBotClass::Find(inst->m_RetClassName); + pResult->SetClass(inst->m_typRes.GivClass()); + } + pStack->SetVar(pResult); + } + return inst; + } + delete inst; + return NULL; + } + } + pStack->SetError( 1234, p ); + delete inst; + return NULL; +} + +// exécute l'appel de méthode + +BOOL CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* prevToken, BOOL bStep, BOOL bExtend) +{ + CBotVar* ppVars[1000]; + CBotStack* pile1 = pj->AddStack(this, TRUE); // une place pour la copie de This +// if ( pile1 == EOX ) return TRUE; + +// DEBUG( "CBotInstrMethode::ExecuteVar", 0, pj ); + + if ( pVar->GivPointer() == NULL ) + { + pj->SetError( TX_NULLPT, prevToken ); + } + + if ( pile1->IfStep() ) return FALSE; + + CBotStack* pile2 = pile1->AddStack(); // et pour les paramètres à venir + + if ( pile1->GivState() == 0) + { + CBotVar* pThis = CBotVar::Create(pVar); + pThis->Copy(pVar); + // la valeur de This doit être prise avant l'évaluation des paramètres + // Test.Action( Test = Autre ); + // Action doit agir sur la valeur avant Test = Autre !! + pThis->SetName("this"); +// pThis->SetUniqNum(m_nThisIdent); + pThis->SetUniqNum(-2); + pile1->AddVar(pThis); + pile1->IncState(); + } + int i = 0; + + CBotInstr* p = m_Parameters; + // évalue les paramètres + // et place les valeurs sur la pile + // pour pouvoir être interrompu n'importe quand + if ( p != NULL) while ( TRUE ) + { + if ( pile2->GivState() == 0 ) + { + if (!p->Execute(pile2)) return FALSE; // interrompu ici ? + if (!pile2->SetState(1)) return FALSE; // marque spéciale pour reconnaîre les paramètres + } + ppVars[i++] = pile2->GivVar(); // construit la liste des pointeurs + pile2 = pile2->AddStack(); // de la place sur la pile pour les résultats + p = p->GivNext(); + if ( p == NULL) break; + } + ppVars[i] = NULL; + + CBotClass* pClass = CBotClass::Find(m_ClassName); + CBotVar* pThis = pile1->FindVar(-2); + CBotVar* pResult = NULL; + if (m_typRes.GivType() > 0) pResult = CBotVar::Create("", m_typRes); + if (m_typRes.Eq(CBotTypClass)) + { +// CBotClass* pClass = CBotClass::Find(m_RetClassName); + pResult->SetClass(m_typRes.GivClass()); + } + CBotVar* pRes = pResult; + + if ( !pClass->ExecuteMethode(m_MethodeIdent, m_NomMethod, + pThis, ppVars, + pResult, pile2, GivToken())) return FALSE; // interrompu + if (pRes != pResult) delete pRes; + + pVar = NULL; // ne retourne pas une valeur par cela + return pj->Return(pile2); // libère toute la pile +} + +void CBotInstrMethode::RestoreStateVar(CBotStack* &pile, BOOL bMain) +{ + if ( !bMain ) return; + + CBotVar* ppVars[1000]; + CBotStack* pile1 = pile->RestoreStack(this); // une place pour la copie de This + if ( pile1 == NULL ) return; + + CBotStack* pile2 = pile1->RestoreStack(); // et pour les paramètres à venir + if ( pile2 == NULL ) return; + + CBotVar* pThis = pile1->FindVar("this"); +// pThis->SetUniqNum(m_nThisIdent); + pThis->SetUniqNum(-2); + + int i = 0; + + CBotInstr* p = m_Parameters; + // évalue les paramètres + // et place les valeurs sur la pile + // pour pouvoir être interrompu n'importe quand + if ( p != NULL) while ( TRUE ) + { + if ( pile2->GivState() == 0 ) + { + p->RestoreState(pile2, TRUE); // interrompu ici ! + return; + } + ppVars[i++] = pile2->GivVar(); // construit la liste des pointeurs + pile2 = pile2->RestoreStack(); + if ( pile2 == NULL ) return; + + p = p->GivNext(); + if ( p == NULL) break; + } + ppVars[i] = NULL; + + CBotClass* pClass = CBotClass::Find(m_ClassName); + CBotVar* pResult = NULL; + + CBotVar* pRes = pResult; + + pClass->RestoreMethode(m_MethodeIdent, m_NomMethod, + pThis, ppVars, pile2); +} + + +BOOL CBotInstrMethode::Execute(CBotStack* &pj) +{ + CBotVar* ppVars[1000]; + CBotStack* pile1 = pj->AddStack(this, TRUE); // une place pour la copie de This +// if ( pile1 == EOX ) return TRUE; + + if ( pile1->IfStep() ) return FALSE; + + CBotStack* pile2 = pile1->AddStack(); // et pour les paramètres à venir + + if ( pile1->GivState() == 0) + { + CBotVar* pThis = pile1->CopyVar(m_token); + // la valeur de This doit être prise avant l'évaluation des paramètres + // Test.Action( Test = Autre ); + // Action doit agir sur la valeur avant Test = Autre !! + pThis->SetName("this"); + pile1->AddVar(pThis); + pile1->IncState(); + } + int i = 0; + + CBotInstr* p = m_Parameters; + // évalue les paramètres + // et place les valeurs sur la pile + // pour pouvoir être interrompu n'importe quand + if ( p != NULL) while ( TRUE ) + { + if ( pile2->GivState() == 0 ) + { + if (!p->Execute(pile2)) return FALSE; // interrompu ici ? + if (!pile2->SetState(1)) return FALSE; // marque spéciale pour reconnaîre les paramètres + } + ppVars[i++] = pile2->GivVar(); // construit la liste des pointeurs + pile2 = pile2->AddStack(); // de la place sur la pile pour les résultats + p = p->GivNext(); + if ( p == NULL) break; + } + ppVars[i] = NULL; + + CBotClass* pClass = CBotClass::Find(m_ClassName); + CBotVar* pThis = pile1->FindVar("this"); + CBotVar* pResult = NULL; + if (m_typRes.GivType()>0) pResult = CBotVar::Create("", m_typRes); + if (m_typRes.Eq(CBotTypClass)) + { +// CBotClass* pClass = CBotClass::Find(m_RetClassName); + pResult->SetClass(m_typRes.GivClass()); + } + CBotVar* pRes = pResult; + + if ( !pClass->ExecuteMethode(m_MethodeIdent, m_NomMethod, + pThis, ppVars, + pResult, pile2, GivToken())) return FALSE; // interrompu + + // met la nouvelle valeur de this à la place de l'ancienne variable + CBotVar* old = pile1->FindVar(m_token); + old->Copy(pThis, FALSE); + + if (pRes != pResult) delete pRes; + + return pj->Return(pile2); // libère toute la pile +} + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction "new" + +CBotNew::CBotNew() +{ + name = "CBotNew"; + m_Parameters = NULL; + m_nMethodeIdent = 0; +// m_nThisIdent = 0; +} + +CBotNew::~CBotNew() +{ +} + +CBotInstr* CBotNew::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotToken* pp = p; + if ( !IsOfType(p, ID_NEW) ) return NULL; + + // vérifie que le token est un nom de classe + if (p->GivType() != TokenTypVar) return NULL; + + CBotClass* pClass = CBotClass::Find(p); + if (pClass == NULL) + { + pStack->SetError(TX_BADNEW, p); + return NULL; + } +/* if ( !pClass->m_IsDef ) + { + pStack->SetError(TX_BADNEW, p); + return NULL; + }*/ + + CBotNew* inst = new CBotNew(); + inst->SetToken(pp); + + inst->m_vartoken = p; + p = p->GivNext(); + + // crée l'objet sur le "tas" + // avec un pointeur sur cet objet + CBotVar* pVar = CBotVar::Create("", pClass); +// inst->m_nThisIdent = CBotVar::NextUniqNum(); + + // fait l'appel du créateur + CBotCStack* pStk = pStack->TokenStack(); + { + // regarde s'il y a des paramètres + CBotVar* ppVars[1000]; + inst->m_Parameters = CompileParams(p, pStk, ppVars); + if ( !pStk->IsOk() ) goto error; + + // le constructeur existe-il ? +// CBotString noname; + CBotTypResult r = pClass->CompileMethode(pClass->GivName(), pVar, ppVars, pStk, inst->m_nMethodeIdent); + delete pStk->TokenStack(); // libère le supplément de pile + int typ = r.GivType(); + + // s'il n'y a pas de constructeur, et pas de paramètres non plus, c'est ok + if ( typ == TX_UNDEFCALL && inst->m_Parameters == NULL ) typ = 0; + pVar->SetInit(TRUE); // marque l'instance comme init + + if (typ>20) + { + pStk->SetError(typ, inst->m_vartoken.GivEnd()); + goto error; + } + + // si le constructeur n'existe pas, mais qu'il y a des paramètres + if (typ<0 && inst->m_Parameters != NULL) + { + pStk->SetError(TX_NOCONST, &inst->m_vartoken); + goto error; + } + + // rend le pointeur à l'objet sur la pile + pStk->SetVar(pVar); + return pStack->Return(inst, pStk); + } +error: + delete inst; + return pStack->Return(NULL, pStk); +} + +// exécute une instruction "new" + +BOOL CBotNew::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); //pile principale +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + + CBotStack* pile1 = pj->AddStack2(); //pile secondaire + + CBotVar* pThis = NULL; + + CBotToken* pt = &m_vartoken; + CBotClass* pClass = CBotClass::Find(pt); + + // crée la variable "this" de type pointeur à l'objet + + if ( pile->GivState()==0) + { + // crée une instance de la classe demandée + // et initialise le pointeur à cet objet + + pThis = CBotVar::Create("this", pClass); +// pThis->SetUniqNum( m_nThisIdent ) ; + pThis->SetUniqNum( -2 ) ; + + pile1->SetVar(pThis); // la place sur la pile1 + pile->IncState(); + } + + // retrouve le pointeur this si on a été interrompu + if ( pThis == NULL) + { + pThis = pile1->GivVar(); // retrouve le pointeur + } + + // y a-t-il une assignation ou des paramètres (constructeur) + if ( pile->GivState()==1) + { + // évalue le constructeur de l'instance + + CBotVar* ppVars[1000]; + CBotStack* pile2 = pile; + + int i = 0; + + CBotInstr* p = m_Parameters; + // évalue les paramètres + // et place les valeurs sur la pile + // pour pouvoir être interrompu n'importe quand + + if ( p != NULL) while ( TRUE ) + { + pile2 = pile2->AddStack(); // de la place sur la pile pour les résultats + if ( pile2->GivState() == 0 ) + { + if (!p->Execute(pile2)) return FALSE; // interrompu ici ? + pile2->SetState(1); + } + ppVars[i++] = pile2->GivVar(); + p = p->GivNext(); + if ( p == NULL) break; + } + ppVars[i] = NULL; + + // crée une variable pour le résultat + CBotVar* pResult = NULL; // constructeurs toujours void + + if ( !pClass->ExecuteMethode(m_nMethodeIdent, pClass->GivName(), + pThis, ppVars, + pResult, pile2, GivToken())) return FALSE; // interrompu + + pThis->ConstructorSet(); // signale que le constructeur a été appelé +// pile->Return(pile2); // libère un bout de pile + +// pile->IncState(); + } + + return pj->Return( pile1 ); // transmet en dessous +} + +void CBotNew::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile = pj->RestoreStack(this); //pile principale + if ( pile == NULL ) return; + + CBotStack* pile1 = pj->AddStack2(); //pile secondaire + + CBotToken* pt = &m_vartoken; + CBotClass* pClass = CBotClass::Find(pt); + + // crée la variable "this" de type pointeur à l'objet + + if ( pile->GivState()==0) + { + return; + } + + CBotVar* pThis = pile1->GivVar(); // retrouve le pointeur +// pThis->SetUniqNum( m_nThisIdent ); + pThis->SetUniqNum( -2 ); + + // y a-t-il une assignation ou des paramètres (constructeur) + if ( pile->GivState()==1) + { + // évalue le constructeur de l'instance + + CBotVar* ppVars[1000]; + CBotStack* pile2 = pile; + + int i = 0; + + CBotInstr* p = m_Parameters; + // évalue les paramètres + // et place les valeurs sur la pile + // pour pouvoir être interrompu n'importe quand + + if ( p != NULL) while ( TRUE ) + { + pile2 = pile2->RestoreStack(); // de la place sur la pile pour les résultats + if ( pile2 == NULL ) return; + + if ( pile2->GivState() == 0 ) + { + p->RestoreState(pile2, bMain); // interrompu ici ! + return; + } + ppVars[i++] = pile2->GivVar(); + p = p->GivNext(); + if ( p == NULL) break; + } + ppVars[i] = NULL; + + pClass->RestoreMethode(m_nMethodeIdent, m_vartoken.GivString(), pThis, + ppVars, pile2) ; // interrompu ici ! + } +} + +///////////////////////////////////////////////////////////// +// regarde si deux résultats sont compatibles pour faire une opération + +BOOL TypeCompatible( CBotTypResult& type1, CBotTypResult& type2, int op ) +{ + int t1 = type1.GivType(); + int t2 = type2.GivType(); + + int max = (t1 > t2) ? t1 : t2; + + if ( max == 99 ) return FALSE; // un résultat est void ? + + // cas particulier pour les concaténation de chaînes + if (op == ID_ADD && max >= CBotTypString) return TRUE; + if (op == ID_ASSADD && max >= CBotTypString) return TRUE; + if (op == ID_ASS && t1 == CBotTypString) return TRUE; + + if ( max >= CBotTypBoolean ) + { + if ( (op == ID_EQ || op == ID_NE) && + (t1 == CBotTypPointer && t2 == CBotTypNullPointer)) return TRUE; + if ( (op == ID_EQ || op == ID_NE || op == ID_ASS) && + (t2 == CBotTypPointer && t1 == CBotTypNullPointer)) return TRUE; + if ( (op == ID_EQ || op == ID_NE) && + (t1 == CBotTypArrayPointer && t2 == CBotTypNullPointer)) return TRUE; + if ( (op == ID_EQ || op == ID_NE || op == ID_ASS) && + (t2 == CBotTypArrayPointer && t1 == CBotTypNullPointer)) return TRUE; + if (t2 != t1) return FALSE; + if (t1 == CBotTypArrayPointer) return type1.Compare(type2); + if (t1 == CBotTypPointer || + t1 == CBotTypClass || + t1 == CBotTypIntrinsic ) + { + CBotClass* c1 = type1.GivClass(); + CBotClass* c2 = type2.GivClass(); + + return c1->IsChildOf(c2) || c2->IsChildOf(c1); + // accepte le caste à l'envers, + // l'opération sera refusée à l'exécution si le pointeur n'est pas compatible + } + + return TRUE; + } + + type1.SetType(max); + type2.SetType(max); + return TRUE; +} + +// regarde si deux variables sont compatible pour un passage de paramètre + +BOOL TypesCompatibles( CBotTypResult& type1, CBotTypResult& type2 ) +{ + int t1 = type1.GivType(); + int t2 = type2.GivType(); + + if ( t1 == CBotTypIntrinsic ) t1 = CBotTypClass; + if ( t2 == CBotTypIntrinsic ) t2 = CBotTypClass; + + int max = (t1 > t2) ? t1 : t2; + + if ( max == 99 ) return FALSE; // un résultat est void ? + + if ( max >= CBotTypBoolean ) + { + if ( t2 != t1 ) return FALSE; + + if ( max == CBotTypArrayPointer ) + return TypesCompatibles(type1.GivTypElem(), type2.GivTypElem()); + + if ( max == CBotTypClass || max == CBotTypPointer ) + return type1.GivClass() == type2.GivClass() ; + + return TRUE ; + } + return TRUE; +} + + +///////////////////////////////////////////////////////////////////////////////////// +// Gestion des fichiers + +// nécessaire car il n'est pas possible de faire le fopen dans le programme principal +// et les fwrite ou fread dans une dll en utilisant le FILE* rendu. + +FILE* fOpen(const char* name, const char* mode) +{ + return fopen(name, mode); +} + +int fClose(FILE* filehandle) +{ + return fclose(filehandle); +} + +size_t fWrite(const void *buffer, size_t elemsize, size_t length, FILE* filehandle) +{ + return fwrite(buffer, elemsize, length, filehandle); +} + +size_t fRead(void *buffer, size_t elemsize, size_t length, FILE* filehandle) +{ + return fread(buffer, elemsize, length, filehandle); +} + +size_t fWrite(const void *buffer, size_t length, FILE* filehandle) +{ + return fwrite(buffer, 1, length, filehandle); +} + +size_t fRead(void *buffer, size_t length, FILE* filehandle) +{ + return fread(buffer, 1, length, filehandle); +} + + +//////////////////////////////////////// + + +#if FALSE + +CBotString num(int n) +{ + CBotString s; + if ( n<0 ) {n = -n; s += "-";} + if ( n > 9 ) + { + s += num(n/10); + } + s += '0' + n%10; + return s; +} + +extern void DEBUG( const char* text, int val, CBotStack* pile ) +{ + CBotProgram* p = pile->GivBotCall(TRUE); + if ( !p->m_bDebugDD ) return; + + FILE* pf = fopen("CbotDebug.txt", "a"); + + fputs( text, pf ); + + CBotString v = " " + num(val) + "\n"; + fputs( v, pf ); + + fclose( pf); +} + +#endif diff --git a/src/CBot/CBot.dsp b/src/CBot/CBot.dsp new file mode 100644 index 00000000..062660a2 --- /dev/null +++ b/src/CBot/CBot.dsp @@ -0,0 +1,158 @@ +# Microsoft Developer Studio Project File - Name="CBot" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=CBot - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "CBot.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "CBot.mak" CFG="CBot - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "CBot - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "CBot - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "CBot - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FR /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "CBot - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# Begin Custom Build +InputPath=.\Debug\CBot.dll +SOURCE=$(InputPath) + +".\TestCBot\CBot.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy .\Debug\CBot.dll .\TestCBot\CBot.dll + copy .\Debug\CBot.dll "C:\Program Files\Colobot\CBot.dll" + _Copy.bat + +# End Custom Build + +!ENDIF + +# Begin Target + +# Name "CBot - Win32 Release" +# Name "CBot - Win32 Debug" +# Begin Source File + +SOURCE=.\CBot.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBot.h +# End Source File +# Begin Source File + +SOURCE=.\CBot.rc +# End Source File +# Begin Source File + +SOURCE=.\CBotClass.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBotDll.h +# End Source File +# Begin Source File + +SOURCE=.\CBotFunction.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBotIf.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBotProgram.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBotStack.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBotString.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBotToken.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBotToken.h +# End Source File +# Begin Source File + +SOURCE=.\CBotTwoOpExpr.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBotVar.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBotWhile.cpp +# End Source File +# End Target +# End Project diff --git a/src/CBot/CBot.dsw b/src/CBot/CBot.dsw new file mode 100644 index 00000000..31e21333 --- /dev/null +++ b/src/CBot/CBot.dsw @@ -0,0 +1,44 @@ +Microsoft Developer Studio Workspace File, Format Version 5.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "CBot"=".\CBot.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "TestCBot"=".\TestCBot\TestCBot.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name CBot + End Project Dependency +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/src/CBot/CBot.h b/src/CBot/CBot.h new file mode 100644 index 00000000..bf95c90a --- /dev/null +++ b/src/CBot/CBot.h @@ -0,0 +1,1611 @@ +//////////////////////////////////////////////////////////////////// +// interpréteur pour le language CBot du jeu COLOBOT + +// dernière révision : 03/10/2002 DD + +#define EXTENDS TRUE + + +#include "resource.h" +#include "CBotDll.h" // définitions publiques +#include "CBotToken.h" // gestion des tokens + +#define STACKRUN TRUE // reprise de l'exécution direct sur une routine suspendue +#define STACKMEM TRUE // préréserve la mémoire pour la pile d'exécution +#define MAXSTACK 990 // taille du stack réservé + +#define EOX (CBotStack*)-1 // marqueur condition spéciale + +///////////////////////////////////////////////////////////////////// +// résumé des classes utilisées, définies ci-après + +class CBotCompExpr; // une expression telle que + // () <= () +class CBotAddExpr; // une expression telle que + // () + () +class CBotParExpr; // un élément de base ou entre parenthèse + // Toto.truc + // 12.5 + // "chaine" + // ( expression ) +class CBotExprVar; // un nom de variable tel que + // Toto + // chose.truc.machin +class CBotWhile; // while (...) {...}; +class CBotIf; // if (...) {...} else {...} +class CBotDefParam; // liste de paramètres d'une fonction +class CBotRepeat; // repeat (nb) {...} + + + +//////////////////////////////////////////////////////////////////////// +// Gestion de la pile d'exécution +//////////////////////////////////////////////////////////////////////// + +// en fait, en externe, la seule chose qu'il est possible de faire +// c'est de créer une instance d'une pile +// pour l'utiliser pour la routine CBotProgram::Execute(CBotStack) + +class CBotStack +{ +private: + CBotStack* m_next; + CBotStack* m_next2; + CBotStack* m_prev; + friend class CBotInstArray; + +#ifdef _DEBUG + int m_index; +#endif + int m_state; + int m_step; + static int m_error; + static int m_start; + static int m_end; + static + CBotVar* m_retvar; // résultat d'un return + + CBotVar* m_var; // résultat des opérations + CBotVar* m_listVar; // les variables déclarées à ce niveau + + BOOL m_bBlock; // fait partie d'un bloc (variables sont locales à ce bloc) + BOOL m_bOver; // limites de la pile ? +// BOOL m_bDontDelete; // spécial, ne pas détruire les variables au delete + CBotProgram* m_prog; // les fonctions définies par user + + static + int m_initimer; + static + int m_timer; + static + CBotString m_labelBreak; + static + void* m_pUser; + + CBotInstr* m_instr; // l'instruction correspondante + BOOL m_bFunc; // une entrée d'une fonction ? + CBotCall* m_call; // point de reprise dans un call extern + friend class CBotTry; + +public: +#if STACKMEM + static + CBotStack* FirstStack(); + void Delete(); +#endif + CBotStack(CBotStack* ppapa); + ~CBotStack(); + BOOL StackOver(); + + int GivError(int& start, int& end); + int GivError(); // rend le numéro d'erreur retourné + + void Reset(void* pUser); // plus d'erreur, timer au début + void SetType(CBotTypResult& type); // détermine le type + int GivType(int mode = 0); // donne le type de valeur sur le stack + CBotTypResult GivTypResult(int mode = 0); // donne le type complet de valeur sur le stack + +// void AddVar(CBotVar* p, BOOL bDontDelete=FALSE); // ajoute une variable locale + void AddVar(CBotVar* p); // ajoute une variable locale +// void RestoreVar(CBotVar* pVar); + + CBotVar* FindVar(CBotToken* &p, BOOL bUpdate = FALSE, + BOOL bModif = FALSE); // trouve une variable + CBotVar* FindVar(CBotToken& Token, BOOL bUpdate = FALSE, + BOOL bModif = FALSE); + CBotVar* FindVar(const char* name); + CBotVar* FindVar(long ident, BOOL bUpdate = FALSE, + BOOL bModif = FALSE); + + CBotVar* CopyVar(CBotToken& Token, BOOL bUpdate = FALSE); // trouve et rend une copie + + + CBotStack* AddStack(CBotInstr* instr = NULL, BOOL bBlock = FALSE); // étend le stack + CBotStack* AddStackEOX(CBotCall* instr = NULL, BOOL bBlock = FALSE); // étend le stack + CBotStack* RestoreStack(CBotInstr* instr = NULL); + CBotStack* RestoreStackEOX(CBotCall* instr = NULL); + + CBotStack* AddStack2(BOOL bBlock = FALSE); // étend le stack + BOOL Return(CBotStack* pFils); // transmet le résultat au dessus + BOOL ReturnKeep(CBotStack* pFils); // transmet le résultat sans réduire la pile + BOOL BreakReturn(CBotStack* pfils, const char* name = NULL); + // en cas de break éventuel + BOOL IfContinue(int state, const char* name); + // ou de "continue" + + BOOL IsOk(); + + BOOL SetState(int n, int lim = -10); // sélectionne un état + int GivState(); // dans quel état j'ère ? + BOOL IncState(int lim = -10); // passe à l'état suivant + BOOL IfStep(); // faire du pas à pas ? + BOOL Execute(); + + void SetVar( CBotVar* var ); + void SetCopyVar( CBotVar* var ); + CBotVar* GivVar(); + CBotVar* GivCopyVar(); + CBotVar* GivPtVar(); + BOOL GivRetVar(BOOL bRet); + long GivVal(); + + void SetStartError(int pos); + void SetError(int n, CBotToken* token = NULL); + void SetPosError(CBotToken* token); + void ResetError(int n, int start, int end); + void SetBreak(int val, const char* name); + + void SetBotCall(CBotProgram* p); + CBotProgram* GivBotCall(BOOL bFirst = FALSE); + void* GivPUser(); + BOOL GivBlock(); + + +// BOOL ExecuteCall(CBotToken* token, CBotVar** ppVar, CBotTypResult& rettype); + BOOL ExecuteCall(long& nIdent, CBotToken* token, CBotVar** ppVar, CBotTypResult& rettype); + void RestoreCall(long& nIdent, CBotToken* token, CBotVar** ppVar); + + BOOL SaveState(FILE* pf); + BOOL RestoreState(FILE* pf, CBotStack* &pStack); + + static + void SetTimer(int n); + + void GetRunPos(const char* &FunctionName, int &start, int &end); + CBotVar* GivStackVars(const char* &FunctionName, int level); + + int m_temp; +}; + +// les routines inline doivent être déclarées dans le fichier .h + +inline BOOL CBotStack::IsOk() +{ + return (m_error == 0); +} + +inline int CBotStack::GivState() +{ + return m_state; +} + +inline int CBotStack::GivError() +{ + return m_error; +} + +//////////////////////////////////////////////////////////////////////// +// Gestion de la pile de compilation +//////////////////////////////////////////////////////////////////////// + + +class CBotCStack +{ +private: + CBotCStack* m_next; + CBotCStack* m_prev; + + static + int m_error; + static + int m_end; + int m_start; + + CBotVar* m_var; // résultat des opérations + + BOOL m_bBlock; // fait partie d'un bloc (variables sont locales à ce bloc) + CBotVar* m_listVar; + + static + CBotProgram* m_prog; // liste des fonctions compilées + static + CBotTypResult m_retTyp; +// static +// CBotToken* m_retClass; + +public: + CBotCStack(CBotCStack* ppapa); + ~CBotCStack(); + + BOOL IsOk(); + int GivError(); + int GivError(int& start, int& end); + // rend le numéro d'erreur retourné + + void SetType(CBotTypResult& type);// détermine le type + CBotTypResult GivTypResult(int mode = 0); // donne le type de valeur sur le stack + int GivType(int mode = 0); // donne le type de valeur sur le stack + CBotClass* GivClass(); // donne la classe de la valeur sur le stack + + void AddVar(CBotVar* p); // ajoute une variable locale + CBotVar* FindVar(CBotToken* &p); // trouve une variable + CBotVar* FindVar(CBotToken& Token); + BOOL CheckVarLocal(CBotToken* &pToken); + CBotVar* CopyVar(CBotToken& Token); // trouve et rend une copie + + CBotCStack* TokenStack(CBotToken* pToken = NULL, BOOL bBlock = FALSE); + CBotInstr* Return(CBotInstr* p, CBotCStack* pParent); // transmet le résultat au dessus + CBotFunction* ReturnFunc(CBotFunction* p, CBotCStack* pParent); // transmet le résultat au dessus + + void SetVar( CBotVar* var ); + void SetCopyVar( CBotVar* var ); + CBotVar* GivVar(); + + void SetStartError(int pos); + void SetError(int n, int pos); + void SetError(int n, CBotToken* p); + void ResetError(int n, int start, int end); + + void SetRetType(CBotTypResult& type); + CBotTypResult GivRetType(); + +// void SetBotCall(CBotFunction* &pFunc); + void SetBotCall(CBotProgram* p); + CBotProgram* GivBotCall(); + CBotTypResult CompileCall(CBotToken* &p, CBotVar** ppVars, long& nIdent); + BOOL CheckCall(CBotToken* &pToken, CBotDefParam* pParam); + + BOOL NextToken(CBotToken* &p); +}; + + +extern BOOL SaveVar(FILE* pf, CBotVar* pVar); + + +///////////////////////////////////////////////////////////////////// +// classes définissant une instruction +class CBotInstr +{ +private: + static + CBotStringArray + m_labelLvl; +protected: + CBotToken m_token; // conserve le token + CBotString name; // debug + CBotInstr* m_next; // instructions chaînées + CBotInstr* m_next2b; // seconde liste pour définition en chaîne + CBotInstr* m_next3; // troisième liste pour les indices et champs + CBotInstr* m_next3b; // nécessaire pour la déclaration des tableaux +/* + par exemple, le programme suivant + int x[]; x[1] = 4; + int y[x[1]][10], z; + va généré + CBotInstrArray + m_next3b-> CBotEmpty + m_next-> + CBotExpression .... + m_next-> + CBotInstrArray + m_next3b-> CBotExpression ('x') ( m_next3-> CBotIndexExpr ('1') ) + m_next3b-> CBotExpression ('10') + m_next2-> 'z' + m_next->... + +*/ + + static + int m_LoopLvl; + friend class CBotClassInst; + friend class CBotInt; + friend class CBotListArray; + +public: + CBotInstr(); + virtual + ~CBotInstr(); + + DllExport//debug + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + static + CBotInstr* CompileArray(CBotToken* &p, CBotCStack* pStack, CBotTypResult type, BOOL first = TRUE); + + virtual + BOOL Execute(CBotStack* &pj); + virtual + BOOL Execute(CBotStack* &pj, CBotVar* pVar); + virtual + void RestoreState(CBotStack* &pj, BOOL bMain); + + virtual + BOOL ExecuteVar(CBotVar* &pVar, CBotCStack* &pile); + virtual + BOOL ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, BOOL bStep, BOOL bExtend); + virtual + void RestoreStateVar(CBotStack* &pile, BOOL bMain); + + virtual + BOOL CompCase(CBotStack* &pj, int val); + + void SetToken(CBotToken* p); + void SetToken(CBotString* name, int start=0, int end=0); + int GivTokenType(); + CBotToken* GivToken(); + + void AddNext(CBotInstr* n); + CBotInstr* GivNext(); + void AddNext3(CBotInstr* n); + CBotInstr* GivNext3(); + void AddNext3b(CBotInstr* n); + CBotInstr* GivNext3b(); + + static + void IncLvl(CBotString& label); + static + void IncLvl(); + static + void DecLvl(); + static + BOOL ChkLvl(CBotString& label, int type); + + BOOL IsOfClass(CBotString name); +}; + +class CBotWhile : public CBotInstr +{ +private: + CBotInstr* m_Condition; // la condition + CBotInstr* m_Block; // les instructions + CBotString m_label; // une étiquette s'il y a + +public: + CBotWhile(); + ~CBotWhile(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotRepeat : public CBotInstr +{ +private: + CBotInstr* m_NbIter; // le nombre d'itération + CBotInstr* m_Block; // les instructions + CBotString m_label; // une étiquette s'il y a + +public: + CBotRepeat(); + ~CBotRepeat(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotDo : public CBotInstr +{ +private: + CBotInstr* m_Block; // les instructions + CBotInstr* m_Condition; // la condition + CBotString m_label; // une étiquette s'il y a + +public: + CBotDo(); + ~CBotDo(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotFor : public CBotInstr +{ +private: + CBotInstr* m_Init; // intruction initiale + CBotInstr* m_Test; // la condition de test + CBotInstr* m_Incr; // instruction pour l'incrément + CBotInstr* m_Block; // les instructions + CBotString m_label; // une étiquette s'il y a + +public: + CBotFor(); + ~CBotFor(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotBreak : public CBotInstr +{ +private: + CBotString m_label; // une étiquette s'il y a + +public: + CBotBreak(); + ~CBotBreak(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotReturn : public CBotInstr +{ +private: + CBotInstr* m_Instr; // le paramètre à retourner + +public: + CBotReturn(); + ~CBotReturn(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +class CBotSwitch : public CBotInstr +{ +private: + CBotInstr* m_Value; // value à chercher + CBotInstr* m_Block; // les instructions + +public: + CBotSwitch(); + ~CBotSwitch(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +class CBotCase : public CBotInstr +{ +private: + CBotInstr* m_Value; // valeur à comparer + +public: + CBotCase(); + ~CBotCase(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); + BOOL CompCase(CBotStack* &pj, int val); +}; + +class CBotCatch : public CBotInstr +{ +private: + CBotInstr* m_Block; // les instructions + CBotInstr* m_Cond; // la condition + CBotCatch* m_next; // le catch suivant + friend class CBotTry; + +public: + CBotCatch(); + ~CBotCatch(); + static + CBotCatch* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL TestCatch(CBotStack* &pj, int val); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); + void RestoreCondState(CBotStack* &pj, BOOL bMain); +}; + +class CBotTry : public CBotInstr +{ +private: + CBotInstr* m_Block; // les instructions + CBotCatch* m_ListCatch; // les catches + CBotInstr* m_FinalInst; // instruction finale + +public: + CBotTry(); + ~CBotTry(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotThrow : public CBotInstr +{ +private: + CBotInstr* m_Value; // la valeur à envoyer + +public: + CBotThrow(); + ~CBotThrow(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +class CBotStartDebugDD : public CBotInstr +{ +private: + +public: + CBotStartDebugDD(); + ~CBotStartDebugDD(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); +}; + + +class CBotIf : public CBotInstr +{ +private: + CBotInstr* m_Condition; // la condition + CBotInstr* m_Block; // les instructions + CBotInstr* m_BlockElse; // les instructions + +public: + CBotIf(); + ~CBotIf(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +// définition d'un nombre entier + +class CBotInt : public CBotInstr +{ +private: + CBotInstr* m_var; // la variable à initialiser + CBotInstr* m_expr; // la valeur à mettre, s'il y a +/// CBotInstr* m_next; // plusieurs définitions enchaînées + +public: + CBotInt(); + ~CBotInt(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, BOOL cont = FALSE, BOOL noskip = FALSE); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +// définition d'un tableau + +class CBotInstArray : public CBotInstr +{ +private: + CBotInstr* m_var; // la variable à initialiser + CBotInstr* m_listass; // liste d'assignations pour le tableau + CBotTypResult + m_typevar; // type d'éléments +// CBotString m_ClassName; + +public: + CBotInstArray(); + ~CBotInstArray(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResult type); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +// définition d'une liste d'assignation pour un tableau +// int [ ] a [ ] = ( ( 1, 2, 3 ) , ( 3, 2, 1 ) ) ; + +class CBotListArray : public CBotInstr +{ +private: + CBotInstr* m_expr; // expression pour un élément + // les autres sont chaînés avec CBotInstr::m_next3; +public: + CBotListArray(); + ~CBotListArray(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, CBotTypResult type); + BOOL Execute(CBotStack* &pj, CBotVar* pVar); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +class CBotEmpty : public CBotInstr +{ + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +// définition d'un booléen + +class CBotBoolean : public CBotInstr +{ +private: + CBotInstr* m_var; // la variable à initialiser + CBotInstr* m_expr; // la valeur à mettre, s'il y a + +public: + CBotBoolean(); + ~CBotBoolean(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, BOOL cont = FALSE, BOOL noskip=FALSE); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +// définition d'un nombre réel + +class CBotFloat : public CBotInstr +{ +private: + CBotInstr* m_var; // la variable à initialiser + CBotInstr* m_expr; // la valeur à mettre, s'il y a + +public: + CBotFloat(); + ~CBotFloat(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, BOOL cont = FALSE, BOOL noskip=FALSE); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +// définition d'un elément string + +class CBotIString : public CBotInstr +{ +private: + CBotInstr* m_var; // la variable à initialiser + CBotInstr* m_expr; // la valeur à mettre, s'il y a + +public: + CBotIString(); + ~CBotIString(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, BOOL cont = FALSE, BOOL noskip=FALSE); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +// définition d'un elément dans une classe quelconque + +class CBotClassInst : public CBotInstr +{ +private: + CBotInstr* m_var; // la variable à initialiser + CBotClass* m_pClass; // référence à la classe + CBotInstr* m_Parameters; // les paramètres à évaluer pour le constructeur + CBotInstr* m_expr; // la valeur à mettre, s'il y a + BOOL m_hasParams; // il y a des paramètres ? + long m_nMethodeIdent; + +public: + CBotClassInst(); + ~CBotClassInst(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* pClass = NULL); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotCondition : public CBotInstr +{ +private: + +public: + + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); +}; + + +// left opérande +// n'accepte que les expressions pouvant être à gauche d'une assignation + +class CBotLeftExpr : public CBotInstr +{ +private: + long m_nIdent; + +public: + CBotLeftExpr(); + ~CBotLeftExpr(); + static + CBotLeftExpr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pStack, CBotStack* array); + + BOOL ExecuteVar(CBotVar* &pVar, CBotCStack* &pile); + BOOL ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, BOOL bStep); + void RestoreStateVar(CBotStack* &pile, BOOL bMain); +}; + + +// gestion des champs d'une instance + +class CBotFieldExpr : public CBotInstr +{ +private: + friend class CBotExpression; + int m_nIdent; + +public: + CBotFieldExpr(); + ~CBotFieldExpr(); + void SetUniqNum(int num); +// static +// CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL ExecuteVar(CBotVar* &pVar, CBotCStack* &pile); + BOOL ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, BOOL bStep, BOOL bExtend); + void RestoreStateVar(CBotStack* &pj, BOOL bMain); +}; + +// gestion des index dans les tableaux + +class CBotIndexExpr : public CBotInstr +{ +private: + CBotInstr* m_expr; // expression pour le calcul de l'index + friend class CBotLeftExpr; + friend class CBotExprVar; + +public: + CBotIndexExpr(); + ~CBotIndexExpr(); +// static +// CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL ExecuteVar(CBotVar* &pVar, CBotCStack* &pile); + BOOL ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, BOOL bStep, BOOL bExtend); + void RestoreStateVar(CBotStack* &pj, BOOL bMain); +}; + +// une expression du genre +// x = a; +// x * y + 3; + +class CBotExpression : public CBotInstr +{ +private: + CBotLeftExpr* m_leftop; // élément de gauche + CBotInstr* m_rightop; // élément de droite + +public: + CBotExpression(); + ~CBotExpression(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pStack); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotListExpression : public CBotInstr +{ +private: + CBotInstr* m_Expr; // la 1ère expression à évaluer + +public: + CBotListExpression(); + ~CBotListExpression(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pStack); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotLogicExpr : public CBotInstr +{ +private: + CBotInstr* m_condition; // test à évaluer + CBotInstr* m_op1; // élément de gauche + CBotInstr* m_op2; // élément de droite + friend class CBotTwoOpExpr; + +public: + CBotLogicExpr(); + ~CBotLogicExpr(); +// static +// CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pStack); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + + +class CBotBoolExpr : public CBotInstr +{ +private: + +public: + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); +}; + + + +// une expression éventuellement entre parenthèses ( ... ) +// il n'y a jamais d'instance de cette classe +// l'objet retourné étant le contenu de la parenthése +class CBotParExpr : public CBotInstr +{ +private: + +public: + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); +}; + +// expression unaire +class CBotExprUnaire : public CBotInstr +{ +private: + CBotInstr* m_Expr; // l'expression à évaluer +public: + CBotExprUnaire(); + ~CBotExprUnaire(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pStack); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +// toutes les opérations à 2 opérandes + +class CBotTwoOpExpr : public CBotInstr +{ +private: + CBotInstr* m_leftop; // élément de gauche + CBotInstr* m_rightop; // élément de droite +public: + CBotTwoOpExpr(); + ~CBotTwoOpExpr(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations = NULL); + BOOL Execute(CBotStack* &pStack); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + + + +// un bloc d'instructions { .... } +class CBotBlock : public CBotInstr +{ +private: + +public: + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, BOOL bLocal = TRUE); + static + CBotInstr* CompileBlkOrInst(CBotToken* &p, CBotCStack* pStack, BOOL bLocal = FALSE); +}; + + +// le contenu d'un bloc d'instructions ... ; ... ; ... ; ... ; +class CBotListInstr : public CBotInstr +{ +private: + CBotInstr* m_Instr; // les instructions à faire + +public: + CBotListInstr(); + ~CBotListInstr(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, BOOL bLocal = TRUE); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +class CBotInstrCall : public CBotInstr +{ +private: + CBotInstr* m_Parameters; // les paramètres à évaluer +// int m_typeRes; // type du résultat +// CBotString m_RetClassName; // class du résultat + CBotTypResult + m_typRes; // type complet du résultat + long m_nFuncIdent; // id de la fonction + +public: + CBotInstrCall(); + ~CBotInstrCall(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +// un appel d'une méthode + +class CBotInstrMethode : public CBotInstr +{ +private: + CBotInstr* m_Parameters; // les paramètres à évaluer +// int m_typeRes; // type du résultat +// CBotString m_RetClassName; // class du résultat + CBotTypResult + m_typRes; // type complet du résultat + + CBotString m_NomMethod; // nom de la méthode + long m_MethodeIdent; // identificateur de la méthode +// long m_nThisIdent; // identificateur pour "this" + CBotString m_ClassName; // nom de la classe + +public: + CBotInstrMethode(); + ~CBotInstrMethode(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* pVar); + BOOL Execute(CBotStack* &pj); + BOOL ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* prevToken, BOOL bStep, BOOL bExtend); + void RestoreStateVar(CBotStack* &pj, BOOL bMain); +}; + +// expression donnant un nom de variable + +class CBotExprVar : public CBotInstr +{ +private: + long m_nIdent; + friend class CBotPostIncExpr; + friend class CBotPreIncExpr; + +public: + CBotExprVar(); + ~CBotExprVar(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, int privat=PR_PROTECT); + static + CBotInstr* CompileMethode(CBotToken* &p, CBotCStack* pStack); + + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); + BOOL ExecuteVar(CBotVar* &pVar, CBotStack* &pile, CBotToken* prevToken, BOOL bStep); + BOOL Execute2Var(CBotVar* &pVar, CBotStack* &pj, CBotToken* prevToken, BOOL bStep); + void RestoreStateVar(CBotStack* &pj, BOOL bMain); +}; + +class CBotPostIncExpr : public CBotInstr +{ +private: + CBotInstr* m_Instr; + friend class CBotParExpr; + +public: + CBotPostIncExpr(); + ~CBotPostIncExpr(); +// static +// CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotPreIncExpr : public CBotInstr +{ +private: + CBotInstr* m_Instr; + friend class CBotParExpr; + +public: + CBotPreIncExpr(); + ~CBotPreIncExpr(); +// static +// CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +class CBotLeftExprVar : public CBotInstr +{ +private: +public: + CBotTypResult + m_typevar; // type de variable déclarée + long m_nIdent; // identificateur unique pour cette variable + +public: + CBotLeftExprVar(); + ~CBotLeftExprVar(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +class CBotExprBool : public CBotInstr +{ +private: + +public: + CBotExprBool(); + ~CBotExprBool(); + + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +class CBotExprNull : public CBotInstr +{ +private: + +public: + CBotExprNull(); + ~CBotExprNull(); + + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotExprNan : public CBotInstr +{ +private: + +public: + CBotExprNan(); + ~CBotExprNan(); + + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +class CBotNew : public CBotInstr +{ +private: + CBotInstr* m_Parameters; // les paramètres à évaluer + long m_nMethodeIdent; +// long m_nThisIdent; + CBotToken m_vartoken; + +public: + CBotNew(); + ~CBotNew(); + + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + +// expression représentant un nombre + +class CBotExprNum : public CBotInstr +{ +private: + int m_numtype; // et le type de nombre + long m_valint; // valeur pour un int + float m_valfloat; // valeur pour un float + +public: + CBotExprNum(); + ~CBotExprNum(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + + +// expression représentant une chaine de caractères + +class CBotExprAlpha : public CBotInstr +{ +private: + +public: + CBotExprAlpha(); + ~CBotExprAlpha(); + static + CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); +}; + + +#define MAX(a,b) ((a>b) ? a : b) + + +// classe pour la gestion des nombres entier (int) +class CBotVarInt : public CBotVar +{ +private: + int m_val; // la valeur + CBotString m_defnum; // le nom si donné par DefineNum + friend class CBotVar; + +public: + CBotVarInt( const CBotToken* name ); +// ~CBotVarInt(); + + void SetValInt(int val, const char* s = NULL); + void SetValFloat(float val); + int GivValInt(); + float GivValFloat(); + CBotString GivValString(); + + void Copy(CBotVar* pSrc, BOOL bName=TRUE); + + + void Add(CBotVar* left, CBotVar* right); // addition + void Sub(CBotVar* left, CBotVar* right); // soustraction + void Mul(CBotVar* left, CBotVar* right); // multiplication + int Div(CBotVar* left, CBotVar* right); // division + int Modulo(CBotVar* left, CBotVar* right); // reste de division + void Power(CBotVar* left, CBotVar* right); // puissance + + BOOL Lo(CBotVar* left, CBotVar* right); + BOOL Hi(CBotVar* left, CBotVar* right); + BOOL Ls(CBotVar* left, CBotVar* right); + BOOL Hs(CBotVar* left, CBotVar* right); + BOOL Eq(CBotVar* left, CBotVar* right); + BOOL Ne(CBotVar* left, CBotVar* right); + + void XOr(CBotVar* left, CBotVar* right); + void Or(CBotVar* left, CBotVar* right); + void And(CBotVar* left, CBotVar* right); + + void SL(CBotVar* left, CBotVar* right); + void SR(CBotVar* left, CBotVar* right); + void ASR(CBotVar* left, CBotVar* right); + + void Neg(); + void Not(); + void Inc(); + void Dec(); + + BOOL Save0State(FILE* pf); + BOOL Save1State(FILE* pf); + +}; + +// classe pour la gestion des nombres réels (float) +class CBotVarFloat : CBotVar +{ +private: + float m_val; // la valeur + +public: + CBotVarFloat( const CBotToken* name ); +// ~CBotVarFloat(); + + void SetValInt(int val, const char* s = NULL); + void SetValFloat(float val); + int GivValInt(); + float GivValFloat(); + CBotString GivValString(); + + void Copy(CBotVar* pSrc, BOOL bName=TRUE); + + + void Add(CBotVar* left, CBotVar* right); // addition + void Sub(CBotVar* left, CBotVar* right); // soustraction + void Mul(CBotVar* left, CBotVar* right); // multiplication + int Div(CBotVar* left, CBotVar* right); // division + int Modulo(CBotVar* left, CBotVar* right); // reste de division + void Power(CBotVar* left, CBotVar* right); // puissance + + BOOL Lo(CBotVar* left, CBotVar* right); + BOOL Hi(CBotVar* left, CBotVar* right); + BOOL Ls(CBotVar* left, CBotVar* right); + BOOL Hs(CBotVar* left, CBotVar* right); + BOOL Eq(CBotVar* left, CBotVar* right); + BOOL Ne(CBotVar* left, CBotVar* right); + + void Neg(); + void Inc(); + void Dec(); + + BOOL Save1State(FILE* pf); +}; + + +// classe pour la gestion des chaînes (String) +class CBotVarString : CBotVar +{ +private: + CBotString m_val; // la valeur + +public: + CBotVarString( const CBotToken* name ); +// ~CBotVarString(); + + void SetValString(const char* p); + CBotString GivValString(); + + void Copy(CBotVar* pSrc, BOOL bName=TRUE); + + void Add(CBotVar* left, CBotVar* right); // addition + + BOOL Lo(CBotVar* left, CBotVar* right); + BOOL Hi(CBotVar* left, CBotVar* right); + BOOL Ls(CBotVar* left, CBotVar* right); + BOOL Hs(CBotVar* left, CBotVar* right); + BOOL Eq(CBotVar* left, CBotVar* right); + BOOL Ne(CBotVar* left, CBotVar* right); + + BOOL Save1State(FILE* pf); +}; + +// classe pour la gestion des boolean +class CBotVarBoolean : CBotVar +{ +private: + BOOL m_val; // la valeur + +public: + CBotVarBoolean( const CBotToken* name ); +// ~CBotVarBoolean(); + + void SetValInt(int val, const char* s = NULL); + void SetValFloat(float val); + int GivValInt(); + float GivValFloat(); + CBotString GivValString(); + + void Copy(CBotVar* pSrc, BOOL bName=TRUE); + + void And(CBotVar* left, CBotVar* right); + void Or(CBotVar* left, CBotVar* right); + void XOr(CBotVar* left, CBotVar* right); + void Not(); + BOOL Eq(CBotVar* left, CBotVar* right); + BOOL Ne(CBotVar* left, CBotVar* right); + + BOOL Save1State(FILE* pf); +}; + + +// classe pour la gestion des instances de classe +class CBotVarClass : public CBotVar +{ +private: + static + CBotVarClass* m_ExClass; // liste des instances existantes à un moment donné + CBotVarClass* m_ExNext; // pour cette liste générale + CBotVarClass* m_ExPrev; // pour cette liste générale + +private: + CBotClass* m_pClass; // la définition de la classe + CBotVarClass* m_pParent; // l'instance dans la classe parent + CBotVar* m_pVar; // contenu + friend class CBotVar; // mon papa est un copain + friend class CBotVarPointer; // et le pointeur aussi + int m_CptUse; // compteur d'utilisation + long m_ItemIdent; // identificateur (unique) de l'instance + BOOL m_bConstructor; // set si un constructeur a été appelé + +public: + CBotVarClass( const CBotToken* name, CBotTypResult& type ); +// CBotVarClass( const CBotToken* name, CBotTypResult& type, int &nIdent ); + ~CBotVarClass(); +// void InitCBotVarClass( const CBotToken* name, CBotTypResult& type, int &nIdent ); + + void Copy(CBotVar* pSrc, BOOL bName=TRUE); + void SetClass(CBotClass* pClass); //, int &nIdent); + CBotClass* GivClass(); + CBotVar* GivItem(const char* name); // rend un élément d'une classe selon son nom (*) + CBotVar* GivItemRef(int nIdent); + + CBotVar* GivItem(int n, BOOL bExtend); + CBotVar* GivItemList(); + + CBotString GivValString(); + + BOOL Save1State(FILE* pf); + void Maj(void* pUser, BOOL bContinue); + + void IncrementUse(); // une référence en plus + void DecrementUse(); // une référence en moins + + CBotVarClass* + GivPointer(); + void SetItemList(CBotVar* pVar); + + void SetIdent(long n); + + static + CBotVarClass* + CBotVarClass::Find(long id); + + +// CBotVar* GivMyThis(); + + BOOL Eq(CBotVar* left, CBotVar* right); + BOOL Ne(CBotVar* left, CBotVar* right); + + void ConstructorSet(); +}; + + +// classe pour la gestion des pointeurs à une instances de classe +class CBotVarPointer : public CBotVar +{ +private: + CBotVarClass* + m_pVarClass; // contenu + CBotClass* m_pClass; // la classe prévue pour ce pointeur + friend class CBotVar; // mon papa est un copain + +public: + CBotVarPointer( const CBotToken* name, CBotTypResult& type ); + ~CBotVarPointer(); + + void Copy(CBotVar* pSrc, BOOL bName=TRUE); + void SetClass(CBotClass* pClass); + CBotClass* GivClass(); + CBotVar* GivItem(const char* name); // rend un élément d'une classe selon son nom (*) + CBotVar* GivItemRef(int nIdent); + CBotVar* GivItemList(); + + CBotString GivValString(); + void SetPointer(CBotVar* p); + CBotVarClass* + GivPointer(); + + void SetIdent(long n); // associe un numéro d'identification (unique) + long GivIdent(); // donne le numéro d'identification associé + void ConstructorSet(); + + BOOL Save1State(FILE* pf); + void Maj(void* pUser, BOOL bContinue); + + BOOL Eq(CBotVar* left, CBotVar* right); + BOOL Ne(CBotVar* left, CBotVar* right); +}; + + +// classe pour les tableaux + +#define MAXARRAYSIZE 9999 + +class CBotVarArray : public CBotVar +{ +private: + CBotVarClass* + m_pInstance; // instance gérant le tableau + + friend class CBotVar; // papa est un copain + +public: + CBotVarArray( const CBotToken* name, CBotTypResult& type ); + ~CBotVarArray(); + + void SetPointer(CBotVar* p); + CBotVarClass* + GivPointer(); + + void Copy(CBotVar* pSrc, BOOL bName=TRUE); + CBotVar* GivItem(int n, BOOL bGrow=FALSE); // rend un élément selon son index numérique + // agrandi le tableau si nécessaire si bExtend +// CBotVar* GivItem(const char* name); // rend un élément selon son index litéral + CBotVar* GivItemList(); // donne le premier élément de la liste + + CBotString GivValString(); // donne le contenu du tableau dans une chaîne + + BOOL Save1State(FILE* pf); +}; + + +extern CBotInstr* CompileParams(CBotToken* &p, CBotCStack* pStack, CBotVar** ppVars); + +extern BOOL TypeCompatible( CBotTypResult& type1, CBotTypResult& type2, int op = 0 ); +extern BOOL TypesCompatibles( CBotTypResult& type1, CBotTypResult& type2 ); + +extern BOOL WriteWord(FILE* pf, WORD w); +extern BOOL ReadWord(FILE* pf, WORD& w); +extern BOOL ReadLong(FILE* pf, long& w); +extern BOOL WriteFloat(FILE* pf, float w); +extern BOOL WriteLong(FILE* pf, long w); +extern BOOL ReadFloat(FILE* pf, float& w); +extern BOOL WriteString(FILE* pf, CBotString s); +extern BOOL ReadString(FILE* pf, CBotString& s); +extern BOOL WriteType(FILE* pf, CBotTypResult type); +extern BOOL ReadType(FILE* pf, CBotTypResult& type); + +extern float GivNumFloat( const char* p ); + +#ifdef _DEBUG +extern void DEBUG( const char* text, int val, CBotStack* pile ); +#endif + +/////////////////////////////////////////// +// classe pour les appels de routines (externes) + +class CBotCall +{ +private: + static + CBotCall* m_ListCalls; + static + void* m_pUser; + long m_nFuncIdent; + +private: + CBotString m_name; + BOOL (*m_rExec) (CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser) ; + CBotTypResult + (*m_rComp) (CBotVar* &pVar, void* pUser) ; + CBotCall* m_next; + +public: + CBotCall(const char* name, + BOOL rExec (CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser), + CBotTypResult rCompile (CBotVar* &pVar, void* pUser)); + ~CBotCall(); + + static + BOOL AddFunction(const char* name, + BOOL rExec (CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser), + CBotTypResult rCompile (CBotVar* &pVar, void* pUser)); + + static + CBotTypResult + CompileCall(CBotToken* &p, CBotVar** ppVars, CBotCStack* pStack, long& nIdent); + static + BOOL CheckCall(const char* name); + +// static +// int DoCall(CBotToken* &p, CBotVar** ppVars, CBotStack* pStack, CBotTypResult& rettype); + static + int DoCall(long& nIdent, CBotToken* token, CBotVar** ppVars, CBotStack* pStack, CBotTypResult& rettype); +#if STACKRUN + BOOL Run(CBotStack* pStack); + static + BOOL RestoreCall(long& nIdent, CBotToken* token, CBotVar** ppVar, CBotStack* pStack); +#endif + + CBotString GivName(); + CBotCall* Next(); + + static void SetPUser(void* pUser); + static void Free(); +}; + +// classe gérant les méthodes déclarées par AddFunction sur une classe + +class CBotCallMethode +{ +private: + CBotString m_name; + BOOL (*m_rExec) (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception); + CBotTypResult + (*m_rComp) (CBotVar* pThis, CBotVar* &pVar); + CBotCallMethode* m_next; + friend class CBotClass; + long m_nFuncIdent; + +public: + CBotCallMethode(const char* name, + BOOL rExec (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception), + CBotTypResult rCompile (CBotVar* pThis, CBotVar* &pVar)); + ~CBotCallMethode(); + + CBotTypResult + CompileCall(const char* name, CBotVar* pThis, + CBotVar** ppVars, CBotCStack* pStack, + long& nIdent); + + int DoCall(long& nIdent, const char* name, CBotVar* pThis, CBotVar** ppVars, CBotVar* &pResult, CBotStack* pStack, CBotToken* pFunc); + + CBotString GivName(); + CBotCallMethode* Next(); + void AddNext(CBotCallMethode* p); + +}; + +// une liste de paramètres + +class CBotDefParam +{ +private: + CBotToken m_token; // nom du paramètre + CBotString m_typename; // nom du type + CBotTypResult m_type; // type de paramètre + CBotDefParam* m_next; // paramètre suivant + long m_nIdent; + +public: + CBotDefParam(); + ~CBotDefParam(); + static + CBotDefParam* Compile(CBotToken* &p, CBotCStack* pStack); + BOOL Execute(CBotVar** ppVars, CBotStack* &pj); + void RestoreState(CBotStack* &pj, BOOL bMain); + + void AddNext(CBotDefParam* p); + int GivType(); + CBotTypResult GivTypResult(); + CBotDefParam* GivNext(); + + CBotString GivParamString(); +}; + + +// une déclaration de fonction + +class CBotFunction : CBotInstr +{ +private: + // gestion d'une liste (static) de fonctions publiques + static + CBotFunction* m_listPublic; + CBotFunction* m_nextpublic; + CBotFunction* m_prevpublic; + friend class CBotCStack; +// long m_nThisIdent; + long m_nFuncIdent; + BOOL m_bSynchro; // méthode synchronisée ? + +private: + CBotDefParam* m_Param; // liste des paramètres + CBotInstr* m_Block; // le bloc d'instructions + CBotFunction* m_next; + CBotToken m_retToken; // si retourne un CBotTypClass + CBotTypResult m_retTyp; // type complet du résultat + + BOOL m_bPublic; // fonction publique + BOOL m_bExtern; // fonction extern + CBotString m_MasterClass; // nom de la classe qu'on dérive + CBotProgram* m_pProg; + friend class CBotProgram; + friend class CBotClass; + + CBotToken m_extern; // pour la position du mot "extern" + CBotToken m_openpar; + CBotToken m_closepar; + CBotToken m_openblk; + CBotToken m_closeblk; +public: + CBotFunction::CBotFunction(); + CBotFunction::~CBotFunction(); + static + CBotFunction* Compile(CBotToken* &p, CBotCStack* pStack, CBotFunction* pFunc, BOOL bLocal = TRUE); + static + CBotFunction* Compile1(CBotToken* &p, CBotCStack* pStack, CBotClass* pClass); + BOOL Execute(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInstance = NULL); + void RestoreState(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInstance = NULL); + + void AddNext(CBotFunction* p); + CBotTypResult CompileCall(const char* name, CBotVar** ppVars, long& nIdent); + CBotFunction* FindLocalOrPublic(long& nIdent, const char* name, CBotVar** ppVars, CBotTypResult& TypeOrError, BOOL bPublic = TRUE); + + int DoCall(long& nIdent, const char* name, CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken); + void RestoreCall(long& nIdent, const char* name, CBotVar** ppVars, CBotStack* pStack); + int DoCall(long& nIdent, const char* name, CBotVar* pThis, CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken, CBotClass* pClass); + void RestoreCall(long& nIdent, const char* name, CBotVar* pThis, CBotVar** ppVars, CBotStack* pStack, CBotClass* pClass); + BOOL CheckParam(CBotDefParam* pParam); + + static + void AddPublic(CBotFunction* pfunc); + + CBotString GivName(); + CBotString GivParams(); + BOOL IsPublic(); + BOOL IsExtern(); + CBotFunction* Next(); + + BOOL GetPosition(int& start, int& stop, CBotGet modestart, CBotGet modestop); +}; + + diff --git a/src/CBot/CBot.lib b/src/CBot/CBot.lib new file mode 100644 index 0000000000000000000000000000000000000000..548361f95975da957e499d2a1dc12d64b6176ee7 GIT binary patch literal 107676 zcmeHQ4U8N|m3}=j&M(1)07)=mk~k28(aOo$f~`oe;|LbMMtp zS5s0gT@!K!Bv1(r3U7NSvbknviTeh0N zcZq+yyL&eC?}sk{2rdVh90a)gR{-~X9pFcs7zLmEJ7DzLJrEFmaVMwG{}%*EaPOx$ zoqiF3=%2pN=^w`ckb-rGIbGhyTObX-zzBY`2#kL4901WvcyFZOMes?JVQgHuyoMxvXAi59h zfCNwdE2H2Z%u94|p3}rj5Fo)5J)A!G5CGA4{+`q4Uj`t0@+nT={XGEDC!Xf?S)!%Zu z4*8Ja5BMyIezuF#zaS2ff&*7`y6rRsMBz_4eQGTPM9)le`UE~dqVo|)L~H-RX$@k8 z=rrOD2_F9~r)N+GL<^U3I)(BhdH`jI6!bmB2!1~cjDB4OKngyC_au7j)0{pA5D?w6 zff3BU07Su!BaGk|*8@?|{cT3@^XGsl*!D7~$^-;N520L%F8ML1VZ0a7_4qu9p1^hz zJ@#czqbL`m-9O`W<+BhFUA%@7Ed2}^EpGro3LY6^1V8*SARRh1ynA%-*udrHs+`G zLc}YIXi=r4@{s%oPtG50H5TUef)H~O2#Kki0@Qu zq3L@I&s;*Uj5ZgJHK*pdW>L813MHOs%cDzIJ#0(<7^Bci@mq-gg6pMtqS1}$iG{s+JR58OEbN)8MDe_eTClFWfZCSBWX|6PsLue zaoH=$KHdZdYg>o868&QIP)OTbI!2TbQ#{tLk=4GZwmH@f869taHwbsuXBzYMJtw&} z7b{o;R3=~4>+$|BDv4PK$}ZO+DtgIGe!aiTWETB{*4brdA(ZT|HqdF%zl-LcdoHC(VxbCHM-MSn&low*Oa^s zPqyUi-;U!E_2`p1B?CQ+k^20x)^Vm{wHld^!p#$o)MxmUh%#>{8RlrV0vn(Q8Kb~&SX*_ysUh(wD>dW{t z)vYYr?X%eMt#_AWR3@7-Da(EViL|Go^MoO@qBK!4#2WQTV+nUpov80yTok5*R*v?v zr}D58Mi$J_JsrtmxI62fP6kU}^PY*EvYmUf?&)L*!$gvKPe(HP!UpCRTMs65mqKj{M`+Ef zXhK>7-!H2-I;u3RXIyC4Xac( z8tG`#SM8a^ms!Vy>P$RxgkirbLzQj#Z45_p$Nb^IY<&)g%>BAm*U25c^O2;{&&i*e znDn3#kblFEbj;{6N-%o)G0e*kmqO2d^*(%3&WJ2<;A#ord0f4A|x zoh%~%u$cLfxy?s`xor%j@i0lllFUA7^blEWqMT$KCU>i6!3%Nlh!WzJoQyeVS#@1Z zBng@)8)+<=l1#qQscfqA*Tm`oP7wV8mLsH+w?AlZN;g-`V3CWr17;BE;^x;>|QAFLTM*`80%l8Na?g-B~L zNp5IXUs>L#zm#@8Ze!06ocTRA4LUKJAJZ*0}t>{$S7H_pqn9_t{*vh zOd%OtrWtDXL~2jeF*0akuH>8MWFc(tTdFrlF?@jF>gcT=}N1%f&7mNInD`rd}MlyKA9nHF&#$-}aom+gCU)9kokEpYV70{)hoH|Y* zyyILc?Jxn~Nh=aLL>u^9DZ!(|9kCI;k48G>jQqn>TQoVFY5XiDKA`B}3YkJQCMxknmv6xc+fN63m7 zjADzl%4f0JHJJ$ot0EIDj{KH+1ha~@A0gs`1(Bnn|=iFcb^Mj&u0VJ`NaVK>+=D8>Rx~sPXm1ap8&@G z5#aE;0NR%a@WNmKi@yPQ?gv=LOIXIq06u;!fT1ajVZ9IFybl1p@Doh`bO1-54dBuL z0a*V)0G0p6dtDpASDy@E)Bjr>c{djgn0i0Kmn^gIFZ(B}aD{yP9Ke;(s%pTw9C+!{`p@3u09aJ>DvPM zX&AuTPX#dfOaR~fL;z#w2k?ir0eoQ%%I!4feH`oX48UazJic`10c=ZO0JFabsQwyY z?Pmgb`mF)L#{$@J3qC81Hyyb#fa`yOa_$b`IgG=7d0PMzl>lCSD1aYd5d^pxwmX1luMA+##rS;*V8b%N&?CtILjZ&ToDXk-H$xZP2sgk4oQ6ec z!pGogcmOVdx56ga2lEiZ4!8vtU;yrbAsB{(FbFl+4troPjKW>83+{vw*bV)#6DHw} z@J_f0E`&?qlFay`ad*BW5IE=xQ z@Db>N+hH1RgInQd*aE%qZnzY-!A-Ck*28u1Cb$ab;4ydxo`C)EVR#3eg5%JDV{ipr z4$H6>UI*vFxquiyD;cX~&XFS+F%rNYz=$ptIy#y#XX#o9ASy;13-&|eXED-eVXJaD zXGxMi=Pv4z^gQvBluwVQ!$nE4{vk4y?Y)nxb1a`_AZ9PYsBOFu(K%UjvMO6*aUG#> zhHp6v&@+CG%Oib^tx1+W4E02;b9sCQ%9{Vqc3^6`40?2CwJ}Es! zR^k~)9KVJUTU4|yI9`kw*&mbh39M2hssdQ#DGuL++hml$D z59m3e##Lw#Dv5hlWVQ|*;%xjfjw-<>QTCY75u-9QDT#LZD^+PI&x zM}8osh?8z3gVF6>Vr=f|P9s^C!lWTYmA-BUE1NOJndSf)?{w19H9aq%Cpv(9BGakS zm_@7#5EFmG?;_Gt++v)At-oTf8@}cavl|My#!O?p5xy;1o?p(@xrdeW3r|UOdnpXO zq1B1P`ZYbPeB2T4Qyk`)?@Z<*NmgeP&x@|Pj0IWQj!@!o1+t{M{xHqqP)rYc%?$i= z&K0Kz9kNtsP-A+~WinNZ(sd^?s30EY`X`PjUB%)sEm{Oh-IT`M$$G5Ruv2Egb`Wlw z5gVMkNJg#%%Wj$E?5J7j4YhYkY}E*xN{AMf$i7D<*=G?lJ*=r7bV_ta8!-nh<6sj3 z%qEpuAxd*E#i5)wogpUkDA}qsSbTs;bx!N^%Qpct7= zYZKSHcQ7{*Z(Xd6q>m}>j-5=VWY^_I(*x`yE;W8V-~nLpv)isw%-nE4ao)$#lZ1~b3a=}!M$jHg2{ zp2}q(IK`IA$A?H}s7$1_#K4u*(5k z(nY8-yFGvwCRw8N11gqOZ=lB3+1iL?(3kl*JMj;yn>t6iu|oZPg4B0+JgSTT(qLItyPqZBsD} z^^)JC>FBbvcjC@Hb7VgqXNY(6UXi3ia9A7L8xDgHsE1b1^a9>w%m0yQ17zrxy_E-M%wns7E^E`|fZsWeB7Y47p0KdAg zb(n)02h!kWbUi!m6qmRs-uO-8#2JY`SwV_Ae<5K*B;m0tEMAtb;HRyU?@b$4D z@ec`QS>vPMYfycjKI7L#_yd)5E=wRuoy$bFX!fJ*O0&@wTUgE(Y6wem*3;q6f`XesN2^ev!t_sHL0(wDx{rUOe zvb%SShRcBAaun;s>j{Bq<7Qc3dTzJMf5ay}x7bhY-%jLAvT<%^dx3mroxWw=TX`IQ zwW{7S4AhRdW{a#XVPsnVBR(}0_R1vUJ1+mx_~cKWv=ldPcI?jU+sZAQw`$L94SQx7 zB8^er>;Mc}UD?`3{j32oZM7SS2N|x1MTtPC$2{ zZ~T(qq`D2DignRt!QSqGp1I^z-{@Dmh$qU$Btu%{93xi+qt|g&0oQy?DwO-2po)Lx z#d+wz>*P=PS;5$SdN0_<%RCU-(SE1i@P2o;^lnR_3KvMMI8?n8CDTUIFJDvzGVolt zTjFz^mVRR8V~eCCFNLie+}6vw~|@Q?ycA2j_2zm#BwZYobU+(@+R}A+hvV^l)bD^V3Qt@)w*d z@e~W&l%bzR)J~=zBA)_X8U6xC`<ET?|ote$+9j^G8#3>>_z?xp&}#73h7s9;MP|5>kJBEvNHcXi5b06%kt`A$ciV zDw_Dgqzdj&l7X#+By!YSoBI-aiyXZxZM;`S8MTr0%NJ=EIFYuU!U+ngyQ-Q%6u#bN z{942Rj4@wacgbmNhx+pONtFjv7rHRT*RhlrQ*s?oUYP1Bk6-E{2d0WT2DIB(8Gfml zj!JVWck@3O}^GPLsPQt`C&=y2F;^TxwVbk0uhWC zqp!Yw-(}OQ0$5p07mK`DKWu#2$F1 z(oN3RpVdvMc6=?V%WZfnk#0($DR14>mz;CCXa!p3WMj5vwG%wml(l1}My$#W3-@dho z*s{+Qo8Hh{P+T&$&ffAIk<^>iTdUQRz8;a3zSVCL5oKaWYAZ8Fc!!3lbP42%tYk#g zYDL!D30X8W)@I|Gv0iA4-Ht5x98IbOoWSz-D3oXvcqQV>xubX~JXFrs=ZB{fMFGFV zhNlvZ0uyM;dlXnA-Bd*^B@QDfbbsgTJ$JVLtZqVk(|4SAl?_iN(oG38oz+c|+rSq~ zS0k1Z2c48kH#u8>RyUo2Zo0;XrxNL=1e(t3rZd(}8xTv0LzGITo1CpbtDDY1H(hJP zQ;Bp_0!?}ArV_=PzZ0=!J)Eu3`pMUinJR(&bX19a1@CgWy1V=PaRV>>S)BZ)g>PdL zZokx1tKD7D-OWg%@>1wLB7efq3cMfxDs;XUlFJdveaX4%+gBix??aikk@U+K$=4^5 z9PPpISD$BF$T>-ps=O4M>+&c3tiZ$g*Ma8zq~`>*_Z<0_vJlefBe+%rW^L3UeJuAwPumbtT6t)fK4cYAByJl79K>xf?mc8nYZ>!}Ck6X00|( zqv!4#8$^?3Jh5=1K3|iEi|xQOQ;(jg5%da)y_zO4rSAxB@~XgL{3`SvVed@k;{>Jm zcw<{~Z>K9(ASQ1{xwVn>%NLVfCMKiNpE#En)gDQY2ZD>OwD1h|qN0%`CNG7?e)$uA zR)D-g7BqG)nf!2AqE&d^5e}LDTdHZQP-sYWOEM>9A@!i=Dm7Bq8XPS zL|d~y)#4p7bEznhCC(Bk;yA;!?xd&1Ipg9XjXV*T99^tif&FY3_6Thx{qpT+H>V+v z^|9KFNP-qjrO-h~XZcXhsj7J1}|x)SX;)eGUy`b=ZKzUSl|>ro>shR==c zDk#!Ccbv+UL5}Du(b;KR2wk&v(Tj?4>v;bp8_yOLSyidP@Kfn9=hvQ$_R)34>EMxGA<5FWxTB{ZHOf_={4r!s8^A!6~ zexkSZmPwB2E78e)TM>P8i>(JW!oOa78PRw4Zuo~ugTt#<;-+gn>Ai|Zx`2OIO#3{fI9MENbsVR5Cl-w7&zZ&-=? zyqh&tXCKsIsoieZG+Xdj!Q+18<`m-aO>t1fBP2p+ugs?F@4R!s9AtxM`$Nj@cl5 z-|TI7g2~@nE0Onp2S<~A>{(DmRSn%F?J{S)1R=`V{v1)YHR*4?WCe}^$dYJl5~Q!b zV}P9sRTK47JY*vBYK+VM8l-Q#sL>Lp%_VVW#`BM(^rZbSaMqzjaztK<^w|JI-taVr z>Lg76U8T`y?)K-1suJz$KFCou-{7EP!TG}EDucHld&ps1+nr$Yw%<#%1K=GTORYMO zer8zO*FV4lREG*1gX6gaAR)>vfgEv_^t-Os6LN-9a8=xSZac0_ADDLbk`q_C&vp0q zCZeBL>lxw05wb=aOIi;owN_C*TV}+mwBO&OjeRPx)3D!3XE|k%pVlgoy}pZKYotDZ ztaY3j>~c_8V2@VSK#;^$n*YJwekZ8>y;>#GSa)(%HRc*}A&}viCs%19?RI~SHIgO+ zC%C-rcgyaMtMzQ&5r(ZjQ*%7{OYMdyb|IAttf&~XM^kJzblQl~D|RG{{IFJ{ofmhT zSd)H-Rf06rikJPQbLU00?IWbQC6OP_O5~H+!*M2E^=eX)c_oUf$?RiC=jp11FSi8p z!`0^G&atop=YhzIZ6oQI?>x|66Ib%BEi|gCnn2X*#djIM*6=?y>l2|W-v0bBRU+Gd zlwoQV_e2y^JjwzqG_L$@`&1d^h^^#qh^y6ydKbr5v%buH5vA=1N8J+04_75xe>Ns? z)mS$6_bRZD%X9r1+E$}Yd+VKm^7n9+D8_l5A!@w77*kZSjY!aRFXHM(`^eCYx6xyq z8_S(YB1fc^$ljXZNNb$t{x0s`65Fm8IR?Vt-WpGnffHQbzSM0=duz3>723zKMcYZ^ z*eJPJ0}Xl-9~(EZbsL3^kFc*5qFrv}Re}BZRgD_iCfl7$B|rR?$S&J2@z>JyAr9%-&m?b43Gj{gonv9MRR6 zjKo>3r*cjTbTu3E$1)B7LR$XzJ)*bBP7*&WuqTGIj-=rPoVR^uIg|6e73jaGc#}4g ze);G*rF3_~oBL|!jNWo5^Dd-r}jK`6)kLGH}VHBRS$g<_&1et%IS)$cKcT1Fw z)t64rY5|7WXC{w!EzfEpwfnkmMc5^hBl1eL7v?<@dEB>Qn*W9DKkfGawOS`o0otL$ z&}z2UI|1e0U-p(K2LIj|o{Y|tE$%3y?Qf5xFnzRU3_fZufvKbfoWSz$BTKaE|Gg%% zm`9AfuSM+*J@>=J`J)I^M%!~lQ;DMP?_+4%w^VPA;&Fevb%6(|71W-u8hxC!=S?fW z-{|?=?avcc<%#w>#89<=YIbmTVM;UOah=faqQ(%~QI!_$vw> z+KXv_L{wqdaQIv3h$vg@^TSh#BAur=p6bn|#O8!}{8Ul3l-)v4i*(-amOy^ED$x!R zM<9&0NYHk=Da(G)uD0igr4r2{9z`rI?Ks|;o!&dcjB^#?JjCV@3%YZq=Nw|D44mNd z_au}EUDJdvn$Ougqc*OL#?-~#DZ(3F88XNbT_uXBsWWuhPAe-(L=C=MuGXJL6uD@8 z=b&c@QP$w`qWV7VBS7=41numWl-B2ns1o_oj}f9U>-P!gW(NrQ| z`f&(H>NDKkDffQ3YdZxlbQks58>{q`&HM6-gG=4eX_7?EA0KB^XxKL+w;RxiF&^i zgryVpk%jlw`9!j&rAjiBoZ5aTsQi6YB|_CKN7cT?#Zsfn+5WSrIulRA9HOeh531I* z6gSk1>Pzsn)KlA^C#uR5^)hckl{Wkdb1L>|;8 zA#WMO-0Fi3aaf{e^$+bVs)qCQpk~Ry2`+D~)|)){b?pjV7fti5Z6y8jos(Wl#}*Hw zDWv6Z&O%E~c7rCY3Jl_ZV*Owmx-yPj@yAzkU!>K#4`mDS)i`$CJ*ikoYHRjXzOI?RLFkP(}y7u*NHrwpgU`g@fZ4h{5Usy zxA}jk5OejG{Y>4-T}2&7dRk-Y637o%C0g71U<_AsX=`yYWshvWHhQ0E_cm?!83R*B z>+{1?iQ<}1b3DaQYG$T=5iKu!T$kzpq8)UFFM`lyZMzdp{+3pW)``8Jp~-oEU|~^Z z;z!!^rj8jP;~-D(5%R4X94uvDXg>%Jz-k= zouKmX2}?A4^I<~O0zc1H2USs5QTxu`xZ0j4nvy4ly^7=2i};u|*wm_@YH8!wwfOxz zNZ%K~VK6gP*HsU1YD`bHrZz-}Qmw(?Uqt$$##dX{^}OJVLWPL$=SVNx`Jy8tufX)S z;E^A`5z0?MI9)$-lAjVLclRsoZloj;c~wAq6|Ry$;b#SI#=l}F(B5(+=LAZPgdr<6Q-{n>>B!DXq^F zQRVTWe-sflR-c+CL=B^Va~S{Vi&#rwi>J0fM^u%_EAOixBYor;hwkKd!$T6O$ LS1MfsPGJ2%z`d8i literal 0 HcmV?d00001 diff --git a/src/CBot/CBot.opt b/src/CBot/CBot.opt new file mode 100644 index 0000000000000000000000000000000000000000..938701913a41c6606044e864d793801e187f52ef GIT binary patch literal 53760 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" with flags "" + +Creating command line "rc.exe /l 0x40c /fo"Release/CBot.res" /d "NDEBUG" "D:\Robot\projet1\CBot\CBot.rc"" +Creating temp file "C:\DOCUME~1\ROUXDA~1\LOCALS~1\Temp\RSP9.tmp" with contents +Creating command line "cl.exe @C:\DOCUME~1\ROUXDA~1\LOCALS~1\Temp\RSP9.tmp" +Creating temp file "C:\DOCUME~1\ROUXDA~1\LOCALS~1\Temp\RSPA.tmp" with contents +Creating command line "link.exe @C:\DOCUME~1\ROUXDA~1\LOCALS~1\Temp\RSPA.tmp" +Compiling resources... +Compiling... +CBot.cpp +CBotClass.cpp +CBotFunction.cpp +CBotIf.cpp +CBotProgram.cpp +CBotStack.cpp +CBotString.cpp +CBotToken.cpp +CBotTwoOpExpr.cpp +CBotVar.cpp +CBotWhile.cpp +Linking... + Creating library Release/CBot.lib and object Release/CBot.exp + + + +CBot.dll - 0 error(s), 0 warning(s) diff --git a/src/CBot/CBot.rc b/src/CBot/CBot.rc new file mode 100644 index 00000000..d8b5b744 --- /dev/null +++ b/src/CBot/CBot.rc @@ -0,0 +1,279 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + ID_IF "if" + ID_ELSE "else" + ID_WHILE "while" + ID_DO "do" + ID_FOR "for" + ID_BREAK "break" + ID_CONTINUE "continue" + ID_SWITCH "switch" + ID_CASE "case" + ID_DEFAULT "default" + ID_TRY "try" + ID_THROW "throw" + ID_CATCH "catch" + ID_FINALLY "finally" + ID_TXT_AND "and" + ID_TXT_OR "or" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_DEBUGDD "STARTDEBUGDD" + ID_INT "int" + ID_FLOAT "float" + ID_BOOLEAN "boolean" + ID_STRING "string" + ID_VOID "void" + ID_BOOL "bool" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_TXT_NOT "not" + ID_RETURN "return" + ID_CLASS "class" + ID_EXTENDS "extends" + ID_SYNCHO "synchronized" + ID_NEW "new" + ID_PUBLIC "public" + ID_EXTERN "extern" + ID_FINAL "final" + ID_STATIC "static" + ID_PROTECTED "protected" + ID_PRIVATE "private" + ID_REPEAT "repeat" +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_OPENPAR "Il manque une parenthèse ouvrante." + TX_CLOSEPAR "Il manque une parenthèse fermante." + TX_NOTBOOL "L'expression doit être un boolean." + TX_UNDEFVAR "Variable non déclarée." + TX_BADLEFT "Assignation impossible." + TX_ENDOF "Terminateur point-virgule non trouvé." + TX_OUTCASE "Instruction ""case"" hors d'un bloc ""switch""." + TX_NOTERM "Instructions après la fin." +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_CLOSEBLK "Il manque la fin du bloc." + TX_ELSEWITHOUTIF "Instruction ""else"" sans ""if"" correspondant." + TX_OPENBLK "Début d'un bloc attendu." + TX_BADTYPE "Mauvais type de résultat pour l'assignation." + TX_REDEFVAR "Redéfinition d'une variable." + TX_BAD2TYPE "Les deux opérandes ne sont pas de types compatibles." + TX_UNDEFCALL "Routine inconnue." + TX_MISDOTS "Séparateur "" : "" attendu." + TX_WHILE "Manque le mot ""while""." + TX_BREAK "Instruction ""break"" en dehors d'une boucle." + TX_LABEL "Un label ne peut se placer que devant un ""for"", un ""while"" ou un ""do""." + TX_NOLABEL "Cette étiquette n'existe pas" + TX_NOCASE "Manque une instruction ""case""." + TX_BADNUM "Un nombre est attendu." + TX_VOID "Paramètre void." + TX_NOTYP "Déclaration de type attendu." +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_DIVZERO "Division par zéro." + TX_NOTINIT "Variable non initialisée." + TX_BADTHROW "Valeur négative refusée pour ""throw""." + TX_NORETVAL "La fonction n'a pas retourné de résultat" + TX_NORUN "Pas de fonction en exécution" + TX_NOCALL "Appel d'une fonction inexistante" + TX_NOCLASS "Cette classe n'existe pas" + TX_NULLPT "Pointeur nul." + TX_OPNAN "Opération sur un ""nan""" + TX_OUTARRAY "Accès hors du tableau" + TX_STACKOVER "Dépassement de la pile" + TX_DELETEDPT "Pointeur à un objet détruit" + TX_FILEOPEN "Ouverture du fichier impossible" + TX_NOTOPEN "Fichier pas ouvert" + TX_ERRREAD "Erreur de lecture" + TX_ERRWRITE "Erreur d'écriture" +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_NOVAR "Nom d'une variable attendu." + TX_NOFONC "Nom de la fonction attendu." + TX_OVERPARAM "Trop de paramètres." + TX_REDEF "Cette fonction existe déjà." + TX_LOWPARAM "Pas assez de paramètres." + TX_BADPARAM "Aucune fonction de ce nom n'accepte ce(s) type(s) de paramètre(s)." + TX_NUMPARAM "Aucune fonction de ce nom n'accepte ce nombre de paramètres." + TX_NOITEM "Cet élément n'existe pas dans cette classe." + TX_DOT "L'objet n'est pas une instance d'une classe." + TX_NOCONST "Il n'y a pas de constructeur approprié." + TX_REDEFCLASS "Cette classe existe déjà." + TX_CLBRK """ ] "" attendu." + TX_RESERVED "Ce mot est réservé." + TX_BADNEW "Mauvais argument pour ""new""." + TX_OPBRK """ [ "" attendu." + TX_BADSTRING "Une chaîne de caractère est attendue." +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_BADINDEX "Mauvais type d'index" + TX_PRIVATE "Membre privé de la classe" + TX_NOPUBLIC """public"" manque" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_OPENPAR "(" + ID_CLOSEPAR ")" + ID_OPBLK "{" + ID_CLBLK "}" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_SEP ";" + ID_COMMA "," + ID_DOTS ":" + ID_DOT "." + ID_OPBRK "[" + ID_CLBRK "]" + ID_DBLDOTS "::" + ID_LOGIC "?" + ID_ADD "+" + ID_SUB "-" + ID_MUL "*" + ID_DIV "/" + ID_ASS "=" + ID_ASSADD "+=" + ID_ASSSUB "-=" + ID_ASSMUL "*=" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_TRUE "true" + ID_FALSE "false" + ID_NULL "null" + ID_NAN "nan" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_ASSDIV "/=" + ID_ASSOR "|=" + ID_ASSAND "&=" + ID_ASSXOR "^=" + ID_ASSSL "<<=" + ID_ASSSR ">>>=" + ID_ASSASR ">>=" + ID_SL "<<" + ID_SR ">>>" + ID_ASR ">>" + ID_INC "++" + ID_DEC "--" + ID_LO "<" + ID_HI ">" + ID_LS "<=" + ID_HS ">=" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EQ "==" + ID_NE "!=" + ID_AND "&" + ID_XOR "^" + ID_OR "|" + ID_LOG_AND "&&" + ID_LOG_OR "||" + ID_LOG_NOT "!" + ID_NOT "~" + ID_MODULO "%" + ID_POWER "**" + ID_ASSMODULO "%=" +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_UNDEF "undefined" + TX_NAN "not a number" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_SUPER "super" +END + +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/CBot/CBotAddExpr.cpp b/src/CBot/CBotAddExpr.cpp new file mode 100644 index 00000000..1d2555a5 --- /dev/null +++ b/src/CBot/CBotAddExpr.cpp @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////// +// expression du genre Opérande1 + Opérande2 +// Opérande1 - Opérande2 + +#include "CBot.h" + +// divers constructeurs + +CBotAddExpr::CBotAddExpr() +{ + m_leftop = + m_rightop = NULL; // NULL pour pouvoir faire delete sans autre + name = "CBotAddExpr"; // debug +} + +CBotAddExpr::~CBotAddExpr() +{ + delete m_leftop; + delete m_rightop; +} + + +// compile une instruction de type A + B + +CBotInstr* CBotAddExpr::Compile(CBotToken* &p, CBotStack* pStack) +{ + CBotStack* pStk = pStack->TokenStack(); // un bout de pile svp + + // cherche des instructions qui peuvent convenir à gauche de l'opération + ou - + + CBotInstr* left = CBotMulExpr::Compile( p, pStk ); // expression A * B à gauche + if (left == NULL) return pStack->Return(NULL, pStk); // si erreur, la transmet + + // est-ce qu'on a le token + ou - ensuite ? + + if ( p->GetType() == ID_ADD || + p->GetType() == ID_SUB) // plus ou moins + { + CBotAddExpr* inst = new CBotAddExpr(); // élément pour opération + inst->SetToken(p); // mémorise l'opération + + int type1, type2; + type1 = pStack->GetType(); // de quel type le premier opérande ? + + p = p->Next(); // saute le token de l'opération + + // cherche des instructions qui peuvent convenir à droite + + if ( NULL != (inst->m_rightop = CBotAddExpr::Compile( p, pStk )) ) // expression (...) à droite + { + // il y a un second opérande acceptable + + type2 = pStack->GetType(); // de quel type le résultat ? + + if ( type1 == type2 ) // les résultats sont-ils compatibles + { + // si ok, enregistre l'opérande dans l'objet + inst->m_leftop = left; + // et rend l'object à qui l'a demandé + return pStack->Return(inst, pStk); + } + } + + // en cas d'erreur, libère les éléments + delete left; + delete inst; + // et transmet l'erreur qui se trouve sur la pile + return pStack->Return(NULL, pStk); + } + + // si on n'a pas affaire à une opération + ou - + // rend à qui l'a demandé, l'opérande (de gauche) trouvé + // à la place de l'objet "addition" + return pStack->Return(left, pStk); +} + + + + +// fait l'opération d'addition ou de soustraction + +BOOL CBotAddExpr::Execute(CBotStack* &pStack) +{ + CBotStack* pStk1 = pStack->AddStack(this); // ajoute un élément à la pile + // ou le retrouve en cas de reprise +// if ( pSk1 == EOX ) return TRUE; + + + // selon la reprise, on peut être dans l'un des 2 états + + if ( pStk1->GetState() == 0 && // 1er état, évalue l'opérande de gauche + !m_leftop->Execute(pStk1) ) return FALSE; // interrompu ici ? + + // passe à l'étape suivante + pStk1->SetState(1); // prêt pour la suite + + // demande un peu plus de stack pour ne pas toucher le résultat de gauche + // qui se trouve sur la pile, justement. + + CBotStack* pStk2 = pStk1->AddStack(); // ajoute un élément à la pile + // ou le retrouve en cas de reprise + + // 2e état, évalue l'opérande de droite + if ( !m_rightop->Execute(pStk2) ) return FALSE; // interrompu ici ? + + int type1 = pStk1->GetType(); // de quels types les résultats ? + int type2 = pStk2->GetType(); + + // crée une variable temporaire pour y mettre le résultat + CBotVar* result = new CBotVar( NULL, MAX(type1, type2)); + + // fait l'opération selon la demande + switch (GetTokenType()) + { + case ID_ADD: + result->Add(pStk1->GetVar(), pStk2->GetVar()); // additionne + break; + case ID_SUB: + result->Sub(pStk1->GetVar(), pStk2->GetVar()); // soustrait + break; + } + pStk2->SetVar(result); // met le résultat sur la pile + + pStk1->Return(pStk2); // libère la pile + return pStack->Return(pStk1); // transmet le résultat +} + + diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp new file mode 100644 index 00000000..180faf31 --- /dev/null +++ b/src/CBot/CBotClass.cpp @@ -0,0 +1,867 @@ +/////////////////////////////////////////////////////////////////////// +// Gestion des variables de type classe +// + +#include "CBot.h" + + +CBotClass* CBotClass::m_ExClass = NULL; + +CBotClass::CBotClass(const char* name, CBotClass* pPapa, BOOL bIntrinsic) +{ + m_pParent = pPapa; + m_name = name; + m_pVar = NULL; + m_next = NULL; + m_pCalls = NULL; + m_pMethod = NULL; + m_rMaj = NULL; + m_IsDef = TRUE; + m_bIntrinsic= bIntrinsic; + m_cptLock = 0; + m_cptOne = 0; + m_nbVar = m_pParent == NULL ? 0 : m_pParent->m_nbVar; + + for ( int j= 0; j< 5 ; j++ ) + { + m_ProgInLock[j] = NULL; + } + + + // se place tout seul dans la liste + if (m_ExClass) m_ExClass->m_ExPrev = this; + m_ExNext = m_ExClass; + m_ExPrev = NULL; + m_ExClass = this; + +} + +CBotClass::~CBotClass() +{ + // retire la classe de la liste + if ( m_ExPrev ) m_ExPrev->m_ExNext = m_ExNext; + else m_ExClass = m_ExNext; + + if ( m_ExNext ) m_ExNext->m_ExPrev = m_ExPrev; + m_ExPrev = NULL; + m_ExNext = NULL; + + delete m_pVar; + delete m_pCalls; + delete m_pMethod; + + delete m_next; // libère toutes celle de ce niveau +} + + +void CBotClass::Free() +{ + while ( m_ExClass != NULL ) + { + delete m_ExClass; + } +} + +void CBotClass::Purge() +{ + if ( this == NULL ) return; + + delete m_pVar; + m_pVar = NULL; + delete m_pCalls; + m_pCalls = NULL; + delete m_pMethod; + m_pMethod = NULL; + m_IsDef = FALSE; + + m_nbVar = m_pParent == NULL ? 0 : m_pParent->m_nbVar; + + m_next->Purge(); + m_next = NULL; // n'appartient plus à cette chaîne +} + +BOOL CBotClass::Lock(CBotProgram* p) +{ + int i = m_cptLock++; + + if ( i == 0 ) + { + m_cptOne = 1; + m_ProgInLock[0] = p; + return TRUE; + } + if ( p == m_ProgInLock[0] ) + { + m_cptOne++; + m_cptLock--; // a déjà été compté + return TRUE; + } + + for ( int j = 1 ; j <= i ; j++) + { + if ( p == m_ProgInLock[j] ) + { + m_cptLock--; + return FALSE; // déjà en attente + } + } + + if ( i < 5 ) // maxi 5 en attente + { + m_ProgInLock[i] = p; // se place dans la queue + } + else + m_cptLock--; + + return FALSE; +} + +void CBotClass::Unlock() +{ + if ( --m_cptOne > 0 ) return ; + + int i = --m_cptLock; + if ( i<0 ) + { + m_cptLock = 0; + return; + } + + for ( int j= 0; j< i ; j++ ) + { + m_ProgInLock[j] = m_ProgInLock[j+1]; + } + m_ProgInLock[i] = 0; +} + +void CBotClass::FreeLock(CBotProgram* p) +{ + CBotClass* pClass = m_ExClass; + + while ( pClass != NULL ) + { + if ( p == pClass->m_ProgInLock[0] ) + { + pClass->m_cptLock -= pClass->m_cptOne; + pClass->m_cptOne = 0; + } + + for ( int j = 1; j < 5 ; j++ ) + if ( p == pClass->m_ProgInLock[j] ) + pClass->m_cptLock--; + + pClass = pClass->m_ExNext; + } +} + + + +BOOL CBotClass::AddItem(CBotString name, CBotTypResult type, int mPrivate) +{ + CBotToken token(name, CBotString()); + CBotClass* pClass = type.GivClass(); + + CBotVar* pVar = CBotVar::Create( name, type ); +/// pVar->SetUniqNum(CBotVar::NextUniqNum()); + pVar->SetPrivate( mPrivate ); + + if ( pClass != NULL ) + { +// pVar->SetClass(pClass); + if ( type.Eq(CBotTypClass) ) + { + // ajoute une instruction new pour initialiser l'object + pVar->m_InitExpr = new CBotNew() ; + CBotToken nom( pClass->GivName() ); + pVar->m_InitExpr->SetToken(&nom); + } + } + return AddItem( pVar ); +} + + +BOOL CBotClass::AddItem(CBotVar* pVar) +{ + pVar->SetUniqNum(++m_nbVar); + + if ( m_pVar == NULL ) m_pVar = pVar; + else m_pVar->AddNext(pVar); + + return TRUE; +} + +void CBotClass::AddNext(CBotClass* pClass) +{ + CBotClass* p = this; + while (p->m_next != NULL) p = p->m_next; + + p->m_next = pClass; +} + +CBotString CBotClass::GivName() +{ + return m_name; +} + +CBotClass* CBotClass::GivParent() +{ + if ( this == NULL ) return NULL; + return m_pParent; +} + +BOOL CBotClass::IsChildOf(CBotClass* pClass) +{ + CBotClass* p = this; + while ( p != NULL ) + { + if ( p == pClass ) return TRUE; + p = p->m_pParent; + } + return FALSE; +} + + +CBotVar* CBotClass::GivVar() +{ + return m_pVar; +} + +CBotVar* CBotClass::GivItem(const char* name) +{ + CBotVar* p = m_pVar; + + while ( p != NULL ) + { + if ( p->GivName() == name ) return p; + p = p->GivNext(); + } + if ( m_pParent != NULL ) return m_pParent->GivItem(name); + return NULL; +} + +CBotVar* CBotClass::GivItemRef(int nIdent) +{ + CBotVar* p = m_pVar; + + while ( p != NULL ) + { + if ( p->GivUniqNum() == nIdent ) return p; + p = p->GivNext(); + } + if ( m_pParent != NULL ) return m_pParent->GivItemRef(nIdent); + return NULL; +} + +BOOL CBotClass::IsIntrinsic() +{ + return m_bIntrinsic; +} + +CBotClass* CBotClass::Find(CBotToken* &pToken) +{ + return Find(pToken->GivString()); +} + +CBotClass* CBotClass::Find(const char* name) +{ + CBotClass* p = m_ExClass; + + while ( p != NULL ) + { + if ( p->GivName() == name ) return p; + p = p->m_ExNext; + } + + return NULL; +} + +BOOL CBotClass::AddFunction(const char* name, + BOOL rExec (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception), + CBotTypResult rCompile (CBotVar* pThis, CBotVar* &pVar)) +{ + // mémorise les pointeurs aux deux fonctions + CBotCallMethode* p = m_pCalls; + CBotCallMethode* pp = NULL; + + while ( p != NULL ) + { + if ( name == p->GivName() ) + { + if ( pp == NULL ) m_pCalls = p->m_next; + else pp->m_next = p->m_next; + delete p; + break; + } + pp = p; + p = p->m_next; + } + + p = new CBotCallMethode(name, rExec, rCompile); + + if (m_pCalls == NULL) m_pCalls = p; + else m_pCalls->AddNext(p); // ajoute à la liste + + return TRUE; +} + +BOOL CBotClass::AddUpdateFunc( void rMaj ( CBotVar* pThis, void* pUser ) ) +{ + m_rMaj = rMaj; + return TRUE; +} + +// compile une méthode associée à une instance de classe +// la méthode peut être déclarée par AddFunction ou par l'utilisateur + +CBotTypResult CBotClass::CompileMethode(const char* name, + CBotVar* pThis, CBotVar** ppParams, + CBotCStack* pStack, long& nIdent) +{ + nIdent = 0; // oublie le précédent s'il y a lieu + + // recherche dans les méthodes déclarées par AddFunction + + CBotTypResult r = m_pCalls->CompileCall(name, pThis, ppParams, pStack, nIdent); + if ( r.GivType() >= 0) return r; + + // recherche dans les méthodes déclarées par l'utilisateur + + r = m_pMethod->CompileCall(name, ppParams, nIdent); + if ( r.Eq(TX_UNDEFCALL) && m_pParent != NULL ) + return m_pParent->m_pMethod->CompileCall(name, ppParams, nIdent); + return r; +} + +// exécute une méthode + +BOOL CBotClass::ExecuteMethode(long& nIdent, const char* name, + CBotVar* pThis, CBotVar** ppParams, + CBotVar* &pResult, CBotStack* &pStack, + CBotToken* pToken) +{ + int ret = m_pCalls->DoCall(nIdent, name, pThis, ppParams, pResult, pStack, pToken); + if (ret>=0) return ret; + + ret = m_pMethod->DoCall(nIdent, name, pThis, ppParams, pStack, pToken, this); + return ret; +} + +// rétabli la pile d'exécution + +void CBotClass::RestoreMethode(long& nIdent, const char* name, CBotVar* pThis, + CBotVar** ppParams, CBotStack* &pStack) +{ + m_pMethod->RestoreCall(nIdent, name, pThis, ppParams, pStack, this); +} + + + + +BOOL CBotClass::SaveStaticState(FILE* pf) +{ + if (!WriteWord( pf, CBOTVERSION*2)) return FALSE; + + // sauve l'état des variables statiques dans les classes + CBotClass* p = m_ExClass; + + while ( p != NULL ) + { + if (!WriteWord( pf, 1)) return FALSE; + // enregistre le nom de la classe + if (!WriteString( pf, p->GivName() )) return FALSE; + + CBotVar* pv = p->GivVar(); + while( pv != NULL ) + { + if ( pv->IsStatic() ) + { + if (!WriteWord( pf, 1)) return FALSE; + if (!WriteString( pf, pv->GivName() )) return FALSE; + + if ( !pv->Save0State(pf)) return FALSE; // entête commune + if ( !pv->Save1State(pf) ) return FALSE; // sauve selon la classe fille + if ( !WriteWord( pf, 0)) return FALSE; + } + pv = pv->GivNext(); + } + + if (!WriteWord( pf, 0)) return FALSE; + p = p->m_ExNext; + } + + if (!WriteWord( pf, 0)) return FALSE; + return TRUE; +} + +BOOL CBotClass::RestoreStaticState(FILE* pf) +{ + CBotString ClassName, VarName; + CBotClass* pClass; + WORD w; + + if (!ReadWord( pf, w )) return FALSE; + if ( w != CBOTVERSION*2 ) return FALSE; + + while (TRUE) + { + if (!ReadWord( pf, w )) return FALSE; + if ( w == 0 ) return TRUE; + + if (!ReadString( pf, ClassName )) return FALSE; + pClass = Find(ClassName); + + while (TRUE) + { + if (!ReadWord( pf, w )) return FALSE; + if ( w == 0 ) break; + + CBotVar* pVar = NULL; + CBotVar* pv = NULL; + + if (!ReadString( pf, VarName )) return FALSE; + if ( pClass != NULL ) pVar = pClass->GivItem(VarName); + + if (!CBotVar::RestoreState(pf, pv)) return FALSE; // la variable temp + + if ( pVar != NULL ) pVar->Copy(pv); + delete pv; + } + } + return TRUE; +} + + +///////////////////////////////////////////////////////////////////// + +CBotClassInst::CBotClassInst() +{ + m_next = NULL; + m_var = NULL; + m_Parameters = NULL; + m_expr = NULL; + m_hasParams = FALSE; + m_nMethodeIdent = 0; + name = "CBotClassInst"; +} + +CBotClassInst::~CBotClassInst() +{ + delete m_var; +// delete m_next; // fait par le destructeur de la classe de base ~CBotInstr() +} + +// définition de pointeur(s) à un objet +// du style +// CPoint A, B ; + +CBotInstr* CBotClassInst::Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* pClass) +{ + // cherche la classe correspondante + if ( pClass == NULL ) + { + pStack->SetStartError(p->GivStart()); + pClass = CBotClass::Find(p); + if ( pClass == NULL ) + { + // pas trouvé ? c'est bizare + pStack->SetError(TX_NOCLASS, p); + return NULL; + } + p = p->GivNext(); + } + + BOOL bIntrinsic = pClass->IsIntrinsic(); + CBotTypResult + type = CBotTypResult( bIntrinsic ? CBotTypIntrinsic : CBotTypPointer, + pClass ); + CBotClassInst* inst = (CBotClassInst*)CompileArray(p, pStack, type); + if ( inst != NULL || !pStack->IsOk() ) return inst; + + CBotCStack* pStk = pStack->TokenStack(); + + inst = new CBotClassInst(); + + inst->SetToken(&pClass->GivName(), p->GivStart(), p->GivEnd()); + CBotToken* vartoken = p; + + if ( NULL != (inst->m_var = CBotLeftExprVar::Compile( p, pStk )) ) + { + ((CBotLeftExprVar*)inst->m_var)->m_typevar = type; + if (pStk->CheckVarLocal(vartoken)) // redéfinition de la variable + { + pStk->SetStartError(vartoken->GivStart()); + pStk->SetError(TX_REDEFVAR, vartoken->GivEnd()); + goto error; + } + + if (IsOfType(p, ID_OPBRK)) // avec des indices ? + { + delete inst; // n'est pas de type CBotInt + p = vartoken; // revient sur le nom de la variable + + // compile une déclaration de tableau + + inst = (CBotClassInst*)CBotInstArray::Compile( p, pStk, type ); + + if (!pStk->IsOk() ) + { + pStk->SetError(TX_CLBRK, p->GivStart()); + goto error; + } + goto suite; // pas d'assignation, variable déjà créée + } + + + CBotVar* var; + var = CBotVar::Create(vartoken->GivString(), type); // crée l'instance +// var->SetClass(pClass); + var->SetUniqNum( + ((CBotLeftExprVar*)inst->m_var)->m_nIdent = CBotVar::NextUniqNum()); + // lui attribut un numéro unique + pStack->AddVar(var); // la place sur la pile + + // regarde s'il y a des paramètres + inst->m_hasParams = (p->GivType() == ID_OPENPAR); + + CBotVar* ppVars[1000]; + inst->m_Parameters = CompileParams(p, pStk, ppVars); + if ( !pStk->IsOk() ) goto error; + + // s'il y a des paramètres, fait l'équivalent de l'instruction new + // CPoint A ( 0, 0 ) est équivalent à + // CPoint A = new CPoint( 0, 0 ) + +// if ( NULL != inst->m_Parameters ) + if ( inst->m_hasParams ) + { + // le constructeur existe-il ? +// CBotString noname; + CBotTypResult r = pClass->CompileMethode(pClass->GivName(), var, ppVars, pStk, inst->m_nMethodeIdent); + delete pStk->TokenStack(); // libère le supplément de pile + int typ = r.GivType(); + + if (typ == TX_UNDEFCALL) + { + // si le constructeur n'existe pas + if (inst->m_Parameters != NULL) // avec des paramètres + { + pStk->SetError(TX_NOCONST, vartoken); + goto error; + } + typ = 0; + } + + if (typ>20) + { + pStk->SetError(typ, vartoken->GivEnd()); + goto error; + } + + } + + if (IsOfType(p, ID_ASS)) // avec une assignation ? + { + if (inst->m_hasParams) + { + pStk->SetError(TX_ENDOF, p->GivStart()); + goto error; + } + + if ( NULL == ( inst->m_expr = CBotTwoOpExpr::Compile( p, pStk )) ) + { + goto error; + } + CBotClass* result = pStk->GivClass(); + if ( !pStk->GivTypResult(1).Eq(CBotTypNullPointer) && + ( !pStk->GivTypResult(1).Eq(CBotTypPointer) || + ( result != NULL && !pClass->IsChildOf(result) ))) // type compatible ? + { + pStk->SetError(TX_BADTYPE, p->GivStart()); + goto error; + } +// if ( !bIntrinsic ) var->SetPointer(pStk->GivVar()->GivPointer()); + if ( !bIntrinsic ) + { + // n'utilise pas le résultat sur la pile, pour imposer la classe + CBotVar* pvar = CBotVar::Create("", pClass); + var->SetPointer( pvar ); // var déjà déclarée pointe l'instance + delete pvar; // supprime le second pointeur + } + var->SetInit(TRUE); // marque le pointeur comme init + } + else if (inst->m_hasParams) + { + // crée l'objet sur le "tas" + // avec un pointeur sur cet objet + if ( !bIntrinsic ) + { + CBotVar* pvar = CBotVar::Create("", pClass); + var->SetPointer( pvar ); // var déjà déclarée pointe l'instance + delete pvar; // supprime le second pointeur + } + var->SetInit(2); // marque le pointeur comme init + } +suite: + if (IsOfType(p, ID_COMMA)) // plusieurs définitions enchaînées + { + if ( NULL != ( inst->m_next = CBotClassInst::Compile(p, pStk, pClass) )) // compile la suivante + { + return pStack->Return(inst, pStk); + } + } + + if (IsOfType(p, ID_SEP)) // instruction terminée + { + return pStack->Return(inst, pStk); + } + + pStk->SetError(TX_ENDOF, p->GivStart()); + } + +error: + delete inst; + return pStack->Return(NULL, pStk); +} + +// déclaration de l'instance d'une classe, par exemple: +// CPoint A, B; + +BOOL CBotClassInst::Execute(CBotStack* &pj) +{ + CBotVar* pThis = NULL; + + CBotStack* pile = pj->AddStack(this);//indispensable pour SetState() +// if ( pile == EOX ) return TRUE; + + CBotToken* pt = &m_token; + CBotClass* pClass = CBotClass::Find(pt); + + BOOL bIntrincic = pClass->IsIntrinsic(); + + // crée la variable de type pointeur à l'objet + + if ( pile->GivState()==0) + { + CBotString name = m_var->m_token.GivString(); + if ( bIntrincic ) + { + pThis = CBotVar::Create(name, CBotTypResult( CBotTypIntrinsic, pClass )); + } + else + { + pThis = CBotVar::Create(name, CBotTypResult( CBotTypPointer, pClass )); + } + + pThis->SetUniqNum(((CBotLeftExprVar*)m_var)->m_nIdent); // lui attribut un numéro unique + pile->AddVar(pThis); // la place sur la pile + pile->IncState(); + } + + if ( pThis == NULL ) pThis = pile->FindVar(((CBotLeftExprVar*)m_var)->m_nIdent); + + if ( pile->GivState()<3) + { + // y a-t-il une assignation ou des paramètres (constructeur) + +// CBotVarClass* pInstance = NULL; + + if ( m_expr != NULL ) + { + // évalue l'expression pour l'assignation + if (!m_expr->Execute(pile)) return FALSE; + + if ( bIntrincic ) + { + CBotVar* pv = pile->GivVar(); + if ( pv == NULL || pv->GivPointer() == NULL ) + { + pile->SetError(TX_NULLPT, &m_token); + return pj->Return(pile); + } + pThis->Copy(pile->GivVar(), FALSE); + } + else + { + CBotVarClass* pInstance; + pInstance = ((CBotVarPointer*)pile->GivVar())->GivPointer(); // valeur pour l'assignation + pThis->SetPointer(pInstance); + } + pThis->SetInit(TRUE); + } + + else if ( m_hasParams ) + { + // évalue le constructeur d'une instance + + if ( !bIntrincic && pile->GivState() == 1) + { + CBotToken* pt = &m_token; + CBotClass* pClass = CBotClass::Find(pt); + + // crée une instance de la classe demandée + + CBotVarClass* pInstance; + pInstance = (CBotVarClass*)CBotVar::Create("", pClass); + pThis->SetPointer(pInstance); + delete pInstance; + + pile->IncState(); + } + + CBotVar* ppVars[1000]; + CBotStack* pile2 = pile; + + int i = 0; + + CBotInstr* p = m_Parameters; + // évalue les paramètres + // et place les valeurs sur la pile + // pour pouvoir être interrompu n'importe quand + + if ( p != NULL) while ( TRUE ) + { + pile2 = pile2->AddStack(); // de la place sur la pile pour les résultats + if ( pile2->GivState() == 0 ) + { + if (!p->Execute(pile2)) return FALSE; // interrompu ici ? + pile2->SetState(1); + } + ppVars[i++] = pile2->GivVar(); + p = p->GivNext(); + if ( p == NULL) break; + } + ppVars[i] = NULL; + + // crée une variable pour le résultat + CBotVar* pResult = NULL; // constructeurs toujours void + + if ( !pClass->ExecuteMethode(m_nMethodeIdent, pClass->GivName(), + pThis, ppVars, + pResult, pile2, GivToken())) return FALSE; // interrompu + + pThis->SetInit(TRUE); + pThis->ConstructorSet(); // signale que le constructeur a été appelé + pile->Return(pile2); // libère un bout de pile + +// pInstance = pThis->GivPointer(); + + } + +// if ( !bIntrincic ) pThis->SetPointer(pInstance); // le fait pointer l'instance + + pile->SetState(3); // fini cette partie + } + + if ( pile->IfStep() ) return FALSE; + + if ( m_next2b != NULL && + !m_next2b->Execute(pile)) return FALSE; // autre(s) définition(s) + + return pj->Return( pile ); // transmet en dessous +} + + + +void CBotClassInst::RestoreState(CBotStack* &pj, BOOL bMain) +{ + CBotVar* pThis = NULL; + + CBotStack* pile = pj; + if ( bMain ) pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + // crée la variable de type pointeur à l'objet + { + CBotString name = m_var->m_token.GivString(); + pThis = pile->FindVar(name); + pThis->SetUniqNum(((CBotLeftExprVar*)m_var)->m_nIdent); // lui attribut un numéro unique + } + + CBotToken* pt = &m_token; + CBotClass* pClass = CBotClass::Find(pt); + BOOL bIntrincic = pClass->IsIntrinsic(); + + if ( bMain && pile->GivState()<3) + { + // y a-t-il une assignation ou des paramètres (constructeur) + +// CBotVarClass* pInstance = NULL; + + if ( m_expr != NULL ) + { + // évalue l'expression pour l'assignation + m_expr->RestoreState(pile, bMain); + return; + } + + else if ( m_hasParams ) + { + // évalue le constructeur d'une instance + + if ( !bIntrincic && pile->GivState() == 1) + { + return; + } + + CBotVar* ppVars[1000]; + CBotStack* pile2 = pile; + + int i = 0; + + CBotInstr* p = m_Parameters; + // évalue les paramètres + // et place les valeurs sur la pile + // pour pouvoir être interrompu n'importe quand + + if ( p != NULL) while ( TRUE ) + { + pile2 = pile2->RestoreStack(); // de la place sur la pile pour les résultats + if ( pile2 == NULL ) return; + + if ( pile2->GivState() == 0 ) + { + p->RestoreState(pile2, bMain); // interrompu ici ? + return; + } + ppVars[i++] = pile2->GivVar(); + p = p->GivNext(); + if ( p == NULL) break; + } + ppVars[i] = NULL; + + // crée une variable pour le résultat + CBotVar* pResult = NULL; // constructeurs toujours void + + pClass->RestoreMethode(m_nMethodeIdent, pClass->GivName(), pThis, ppVars, pile2); + return; + } + } + + if ( m_next2b != NULL ) + m_next2b->RestoreState(pile, bMain); // autre(s) définition(s) +} + + +// test si un nom de procédure est déjà défini quelque part + +BOOL CBotClass::CheckCall(CBotToken* &pToken, CBotDefParam* pParam) +{ + CBotString name = pToken->GivString(); + + if ( CBotCall::CheckCall(name) ) return TRUE; + + CBotFunction* pp = m_pMethod; + while ( pp != NULL ) + { + if ( pToken->GivString() == pp->GivName() ) + { + // les paramètres sont-ils exactement les mêmes ? + if ( pp->CheckParam( pParam ) ) + return TRUE; + } + pp = pp->Next(); + } + + return FALSE; +} + diff --git a/src/CBot/CBotCompExpr.cpp b/src/CBot/CBotCompExpr.cpp new file mode 100644 index 00000000..99abfb9d --- /dev/null +++ b/src/CBot/CBotCompExpr.cpp @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////// +// expression du genre Opérande1 > Opérande2 +// Opérande1 != Opérande2 +// etc. + +#include "CBot.h" + +// divers constructeurs + +CBotCompExpr::CBotCompExpr() +{ + m_leftop = + m_rightop = NULL; + name = "CBotCompExpr"; +} + +CBotCompExpr::~CBotCompExpr() +{ + delete m_leftop; + delete m_rightop; +} + +fichier plus utilise; + +// compile une instruction de type A < B + +CBotInstr* CBotCompExpr::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotCStack* pStk = pStack->AddStack(); + + CBotInstr* left = CBotAddExpr::Compile( p, pStk ); // expression A + B à gauche + if (left == NULL) return pStack->Return(NULL, pStk); // erreur + + if ( p->GetType() == ID_HI || + p->GetType() == ID_LO || + p->GetType() == ID_HS || + p->GetType() == ID_LS || + p->GetType() == ID_EQ || + p->GetType() == ID_NE) // les diverses comparaisons + { + CBotCompExpr* inst = new CBotCompExpr(); // élément pour opération + inst->SetToken(p); // mémorise l'opération + + int type1, type2; + type1 = pStack->GetType(); + + p = p->Next(); + if ( NULL != (inst->m_rightop = CBotAddExpr::Compile( p, pStk )) ) // expression A + B à droite + { + type2 = pStack->GetType(); + // les résultats sont-ils compatibles + if ( type1 == type2 ) + { + inst->m_leftop = left; + pStk->SetVar(new CBotVar(NULL, CBotTypBoolean)); + // le résultat est un boolean + return pStack->Return(inst, pStk); + } + } + + delete left; + delete inst; + return pStack->Return(NULL, pStk); + } + + return pStack->Return(left, pStk); +} + + +// fait l'opération + +BOOL CBotCompExpr::Execute(CBotStack* &pStack) +{ + CBotStack* pStk1 = pStack->AddStack(this); +// if ( pStk1 == EOX ) return TRUE; + + if ( pStk1->GetState() == 0 && !m_leftop->Execute(pStk1) ) return FALSE; // interrompu ici ? + + pStk1->SetState(1); // opération terminée + + // demande un peu plus de stack pour ne pas toucher le résultat de gauche + CBotStack* pStk2 = pStk1->AddStack(); + + if ( !m_rightop->Execute(pStk2) ) return FALSE; // interrompu ici ? + + int type1 = pStk1->GetType(); + int type2 = pStk2->GetType(); + + CBotVar* result = new CBotVar( NULL, CBotTypBoolean ); + + switch (GetTokenType()) + { + case ID_LO: + result->Lo(pStk1->GetVar(), pStk2->GetVar()); // inférieur + break; + case ID_HI: + result->Hi(pStk1->GetVar(), pStk2->GetVar()); // supérieur + break; + case ID_LS: + result->Ls(pStk1->GetVar(), pStk2->GetVar()); // inférieur ou égal + break; + case ID_HS: + result->Hs(pStk1->GetVar(), pStk2->GetVar()); // supérieur ou égal + break; + case ID_EQ: + result->Eq(pStk1->GetVar(), pStk2->GetVar()); // égal + break; + case ID_NE: + result->Ne(pStk1->GetVar(), pStk2->GetVar()); // différent + break; + } + pStk2->SetVar(result); // met le résultat sur la pile + + pStk1->Return(pStk2); // libère la pile + return pStack->Return(pStk1); // transmet le résultat +} + diff --git a/src/CBot/CBotDll.h b/src/CBot/CBotDll.h new file mode 100644 index 00000000..4e0c5b14 --- /dev/null +++ b/src/CBot/CBotDll.h @@ -0,0 +1,1185 @@ +//////////////////////////////////////////////////////////////////////// +// Librairie pour l'interprétation du language CBOT +// pour le jeu COLOBOT +// + +//#include "stdafx.h" + +#include +#include + +#define DllExport __declspec( dllexport ) + +#define CBOTVERSION 104 + +//////////////////////////////////////////////////////////////////////// +// quelques classes définies par ailleurs + +class CBotToken; // programme transformé en "jetons" +class CBotStack; // pile pour l'exécution +class CBotClass; // classe d'object +class CBotInstr; // instruction à exécuter +class CBotFunction; // les fonctions user +class CBotVar; // les variables +class CBotVarClass; // une instance de classe +class CBotVarPointer; // pointeur à une instance de classe +class CBotCall; // les fonctions +class CBotCallMethode; // les méthodes +class CBotDefParam; // liste de paramètres + + +//////////////////////////////////////////////////////////////////////// +// Gestion des variables +//////////////////////////////////////////////////////////////////////// + +// ces types sont calqués sur les types Java +// ne pas changer l'ordre de ces types + +enum CBotType +{ + CBotTypVoid = 0, // fonction retournant void + CBotTypByte = 1, //n // nombre entier ( 8 bits) + CBotTypShort = 2, //n // nombre entier (16 bits) + CBotTypChar = 3, //n // caractère "unicode" (16 bits) + CBotTypInt = 4, // nombre entier (32 bits) + CBotTypLong = 5, //n // nombre entier (64 bits) + CBotTypFloat = 6, // nombre décimal (32 bits) + CBotTypDouble = 7, //n // nombre décimal (64 bits) + CBotTypBoolean = 8, // true ou false exclusivement + CBotTypString = 9, // chaine de caractère + + CBotTypArrayPointer = 10, // un tableau de variables + CBotTypArrayBody = 11, // idem mais crée l'instance + + CBotTypPointer = 12, // pointeur à une instance + CBotTypNullPointer = 13, // pointeur null est spécial + + CBotTypClass = 15, // instance d'une classe + CBotTypIntrinsic = 16 // instance d'une classe intrinsèque +}; + //n = non encore implémenté + +// pour SetUserPtr lors de la suppression d'un objet +#define OBJECTDELETED ((void*)-1) +// valeur mise avant initialisation +#define OBJECTCREATED ((void*)-2) + + +// classe permettant de définir le type complet d'un résultat +class CBotTypResult +{ +private: + int m_type; + CBotTypResult* m_pNext; // pour les types de types + CBotClass* m_pClass; // pour les dérivés de classe + int m_limite; // limitation des tableaux + friend class CBotVarClass; + friend class CBotVarPointer; + +public: + // divers constructeurs selon les besoins + DllExport + CBotTypResult(int type); + // pour les types simples (CBotTypInt à CBotTypString) + DllExport + CBotTypResult(int type, const char* name); + // pour les types pointeur et classe intrinsic + DllExport + CBotTypResult(int type, CBotClass* pClass); + // idem à partir de l'instance d'une classe + DllExport + CBotTypResult(int type, CBotTypResult elem); + // pour les tableaux de variables + + DllExport + CBotTypResult(CBotTypResult& typ); + // pour les assignations + DllExport + CBotTypResult(); + // pour par défaut + DllExport + ~CBotTypResult(); + + DllExport + int GivType(int mode = 0); + // rend le type CBotTyp* du résultat + + void SetType(int n); + // modifie le type + + DllExport + CBotClass* GivClass(); + // rend le pointeur à la classe (pour les CBotTypClass, CBotTypPointer) + + DllExport + int GivLimite(); + // rend la taille limite du tableau (CBotTypArray) + + DllExport + void SetLimite(int n); + // fixe une limite au tableau + + void SetArray(int* max ); + // idem avec une liste de dimension (tableaux de tableaux) + + DllExport + CBotTypResult& GivTypElem(); + // rend le type des éléments du tableau (CBotTypArray) + + DllExport + BOOL Compare(CBotTypResult& typ); + // compare si les types sont compatibles + DllExport + BOOL Eq(int type); + // compare le type + + DllExport + CBotTypResult& + operator=(const CBotTypResult& src); + // copie un type complet dans un autre +}; + +/* +// pour définir un résultat en sortie, utiliser par exemple + + // pour rendre un simple Float + return CBotTypResult( CBotTypFloat ); + + + // pour rendre un tableau de string + return CBotTypResult( CBotTypArray, CBotTypResult( CBotTypString ) ); + + // pour rendre un tableau de tableau de "point" + CBotTypResult typPoint( CBotTypIntrinsic, "point" ); + CBotTypResult arrPoint( CBotTypArray, typPoint ); + return CBotTypResult( CBotTypArray, arrPoint ); +*/ + + +//////////////////////////////////////////////////////////////////////// +// Gestion des erreurs compilation et exécution +//////////////////////////////////////////////////////////////////////// + +// voici la liste des erreurs pouvant être retournées par le module +// pour la compilation + +#define CBotErrOpenPar 5000 // manque la parenthèse ouvrante +#define CBotErrClosePar 5001 // manque la parenthèse fermante +#define CBotErrNotBoolean 5002 // l'expression doit être un boolean +#define CBotErrUndefVar 5003 // variable non déclarée +#define CBotErrBadLeft 5004 // assignation impossible ( 5 = ... ) +#define CBotErrNoTerminator 5005 // point-virgule attendu +#define CBotErrCaseOut 5006 // case en dehors d'un switch +// CBotErrNoTerm 5007, plus utile +#define CBotErrCloseBlock 5008 // manque " } " +#define CBotErrElseWhitoutIf 5009 // else sans if correspondant +#define CBotErrOpenBlock 5010 // manque " { " +#define CBotErrBadType1 5011 // mauvais type pour l'assignation +#define CBotErrRedefVar 5012 // redéfinition de la variable +#define CBotErrBadType2 5013 // 2 opérandes de type incompatibles +#define CBotErrUndefCall 5014 // routine inconnue +#define CBotErrNoDoubleDots 5015 // " : " attendu +// CBotErrWhile 5016, plus utile +#define CBotErrBreakOutside 5017 // break en dehors d'une boucle +#define CBotErrUndefLabel 5019 // label inconnu +#define CBotErrLabel 5018 // label ne peut se mettre ici +#define CBotErrNoCase 5020 // manque " case " +#define CBotErrBadNum 5021 // nombre attendu +#define CBotErrVoid 5022 // " void " pas possible ici +#define CBotErrNoType 5023 // déclaration de type attendue +#define CBotErrNoVar 5024 // nom de variable attendu +#define CBotErrNoFunc 5025 // nom de fonction attendu +#define CBotErrOverParam 5026 // trop de paramètres +#define CBotErrRedefFunc 5027 // cette fonction existe déjà +#define CBotErrLowParam 5028 // pas assez de paramètres +#define CBotErrBadParam 5029 // mauvais types de paramètres +#define CBotErrNbParam 5030 // mauvais nombre de paramètres +#define CBotErrUndefItem 5031 // élément n'existe pas dans la classe +#define CBotErrUndefClass 5032 // variable n'est pas une classe +#define CBotErrNoConstruct 5033 // pas de constructeur approprié +#define CBotErrRedefClass 5034 // classe existe déjà +#define CBotErrCloseIndex 5035 // " ] " attendu +#define CBotErrReserved 5036 // mot réservé (par un DefineNum) +#define CBotErrBadNew 5037 // mauvais paramètre pour new +#define CBotErrOpenIndex 5038 // " [ " attendu +#define CBotErrBadString 5039 // chaîne de caractère attendue +#define CBotErrBadIndex 5040 // mauvais type d'index "[ false ]" +#define CBotErrPrivate 5041 // élément protégé +#define CBotErrNoPublic 5042 // manque le mot "public" + +// voici la liste des erreurs pouvant être retournées par le module +// pour l'exécution + +#define CBotErrZeroDiv 6000 // division par zéro +#define CBotErrNotInit 6001 // variable non initialisée +#define CBotErrBadThrow 6002 // throw d'une valeur négative +#define CBotErrNoRetVal 6003 // fonction n'a pas retourné de résultat +#define CBotErrNoRun 6004 // Run() sans fonction active +#define CBotErrUndefFunc 6005 // appel d'une fonction qui n'existe plus +#define CBotErrNotClass 6006 // cette classe n'existe pas +#define CBotErrNull 6007 // pointeur null +#define CBotErrNan 6008 // calcul avec un NAN +#define CBotErrOutArray 6009 // index hors du tableau +#define CBotErrStackOver 6010 // dépassement de la pile +#define CBotErrDeletedPtr 6011 // pointeur à un objet détruit + +#define CBotErrFileOpen 6012 // ouverture du fichier impossible +#define CBotErrNotOpen 6013 // canal pas ouvert +#define CBotErrRead 6014 // erreur à la lecture +#define CBotErrWrite 6015 // erreur à l'écriture + +// d'autres valeurs peuvent être rendues +// par exemple les exceptions rendues par les routines externes +// et les " throw " avec un nombre quelconque. + + +//////////////////////////////////////////////////////////////////////// +// définie une classe pour l'utilisation des strings +// car CString fait partie de MFC pas utilisé ici. +// +// ( toutes les fonctions ne sont pas encore implémentées ) + +class CBotString +{ +private: + char* m_ptr; // pointeur à la chaine + int m_lg; // longueur de la chaine + static + HINSTANCE m_hInstance; + +public: + DllExport + CBotString(); + DllExport + CBotString(const char* p); + DllExport + CBotString(const CBotString& p); + DllExport + ~CBotString(); + + DllExport + void Empty(); + DllExport + BOOL IsEmpty(); + DllExport + int GivLength(); + DllExport + int Find(const char c); + DllExport + int Find(LPCTSTR lpsz); + DllExport + int ReverseFind(const char c); + DllExport + int ReverseFind(LPCTSTR lpsz); + DllExport + BOOL LoadString(UINT id); + DllExport + CBotString Mid(int nFirst, int nCount) const; + DllExport + CBotString Mid(int nFirst) const; + DllExport + CBotString Left(int nCount) const; + DllExport + CBotString Right(int nCount) const; + + DllExport + const CBotString& + operator=(const CBotString& stringSrc); + DllExport + const CBotString& + operator=(const char ch); + DllExport + const CBotString& + operator=(const char* pString); + DllExport + const CBotString& + operator+(const CBotString& str); + DllExport + friend CBotString + operator+(const CBotString& string, LPCTSTR lpsz); + + DllExport + const CBotString& + operator+=(const char ch); + DllExport + const CBotString& + operator+=(const CBotString& str); + DllExport + BOOL operator==(const CBotString& str); + DllExport + BOOL operator==(const char* p); + DllExport + BOOL operator!=(const CBotString& str); + DllExport + BOOL operator!=(const char* p); + DllExport + BOOL operator>(const CBotString& str); + DllExport + BOOL operator>(const char* p); + DllExport + BOOL operator>=(const CBotString& str); + DllExport + BOOL operator>=(const char* p); + DllExport + BOOL operator<(const CBotString& str); + DllExport + BOOL operator<(const char* p); + DllExport + BOOL operator<=(const CBotString& str); + DllExport + BOOL operator<=(const char* p); + + DllExport + operator LPCTSTR() const; // as a C string + + int Compare(LPCTSTR lpsz) const; + + DllExport + CBotString Mid(int start, int lg=-1); + + DllExport + void MakeUpper(); + DllExport + void MakeLower(); +}; + + +// idem avec la gestion en tableau + +class CBotStringArray : public CBotString +{ +private: + int m_nSize; // nombre d'éléments + int m_nMaxSize; // taille réservée + CBotString* m_pData; // ^aux données + +public: + DllExport + CBotStringArray(); + DllExport + ~CBotStringArray(); + DllExport + void SetSize(int nb); + DllExport + int GivSize(); + DllExport + void Add(const CBotString& str); + DllExport + CBotString& operator[](int nIndex); + + DllExport + CBotString& ElementAt(int nIndex); +}; + +// différents mode pour GetPosition +enum CBotGet +{ + GetPosExtern = 1, + GetPosNom = 2, + GetPosParam = 3, + GetPosBloc = 4 +}; + +//////////////////////////////////////////////////////////////////// +// classe principale gérant un programme CBot +// + +class CBotProgram +{ +private: + CBotFunction* m_Prog; // les fonctions définies par l'utilisateur + CBotFunction* m_pRun; // la fonction de base pour l'exécution + CBotClass* m_pClass; // les classes définies dans cette partie + CBotStack* m_pStack; // la pile d'exécution + CBotVar* m_pInstance; // instance de la classe parent + friend class CBotFunction; + + int m_ErrorCode; + int m_ErrorStart; + int m_ErrorEnd; + + long m_Ident; // identificateur associé + +public: + static + CBotString m_DebugVarStr; // a fin de debug + BOOL m_bDebugDD; // idem déclanchable par robot + + BOOL m_bCompileClass; + +public: + DllExport + static + void Init(); + // initialise le module (défini les mots clefs pour les erreurs) + // doit être fait une fois (et une seule) au tout début + DllExport + static + void Free(); + // libère les zones mémoires statiques + + DllExport + static + int GivVersion(); + // donne la version de la librairie CBOT + + + DllExport + CBotProgram(); + DllExport + CBotProgram(CBotVar* pInstance); + DllExport + ~CBotProgram(); + + DllExport + BOOL Compile( const char* program, CBotStringArray& ListFonctions, void* pUser = NULL); + // compile le programme donné en texte + // retourne FALSE s'il y a une erreur à la compilation + // voir GetCompileError() pour récupérer l'erreur + // ListFonctions retourne le nom des fonctions déclarées extern + // pUser permet de passer un pointeur pour les routines définies par AddFunction + + DllExport + void SetIdent(long n); + // associe un identificateur avec l'instance CBotProgram + + DllExport + long GivIdent(); + // redonne l'identificateur + + DllExport + int GivError(); + DllExport + BOOL GetError(int& code, int& start, int& end); + DllExport + BOOL GetError(int& code, int& start, int& end, CBotProgram* &pProg); + // si TRUE + // donne l'erreur trouvée à la compilation + // ou à l'exécution + // start et end délimite le bloc où se trouve l'erreur + // pProg permet de savoir dans quel "module" s'est produite l'erreur d'exécution + DllExport + static + CBotString GivErrorText(int code); + + + DllExport + BOOL Start(const char* name); + // définie quelle fonction doit être exécutée + // retourne FALSE si la fontion name n'est pas trouvée + // le programme ne fait rien, il faut appeller Run() pour cela + + DllExport + BOOL Run(void* pUser = NULL, int timer = -1); + // exécute le programme + // retourne FALSE si le programme a été suspendu + // retourne TRUE si le programme s'est terminé avec ou sans erreur + // timer = 0 permet de faire une avance pas à pas + + DllExport + BOOL GetRunPos(const char* &FunctionName, int &start, int &end); + // donne la position dans le programme en exécution + // retourne FALSE si on n'est pas en exécution (programme terminé) + // FunctionName est un pointeur rendu sur le nom de la fonction + // start et end la position dans le texte du token en traitement + + DllExport + CBotVar* GivStackVars(const char* &FunctionName, int level); + // permet d'obtenir le pointeur aux variables sur la pile d'exécution + // level est un paramètre d'entrée, 0 pour le dernier niveau, -1, -2, etc pour les autres niveau + // la valeur retournée (CBotVar*) est une liste de variable (ou NULL) + // qui peut être traité que la liste des paramètres reçu par une routine + // FunctionName donne le nom de la fonction où se trouvent ces variables + // FunctionName == NULL signifiant qu'on est plus dans le programme (selon level) + + DllExport + void Stop(); + // arrête l'exécution du programme + // quitte donc le mode "suspendu" + + DllExport + static + void SetTimer(int n); + // défini le nombre de pas (parties d'instructions) à faire + // dans Run() avant de rendre la main "FALSE" + + DllExport + static + BOOL AddFunction(const char* name, + BOOL rExec (CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser), + CBotTypResult rCompile (CBotVar* &pVar, void* pUser)); + // cet appel permet d'ajouter de manière externe (**) + // une nouvelle fonction utilisable par le programme CBot + + DllExport + static + BOOL DefineNum(const char* name, long val); + + DllExport + BOOL SaveState(FILE* pf); + // sauvegarde l'état d'exécution dans le fichier + // le fichier doit avoir été ouvert avec l'appel fopen de cette dll + // sinon le système plante + DllExport + BOOL RestoreState(FILE* pf); + // rétablie l'état de l'exécution depuis le fichier + // le programme compilé doit évidemment être identique + + DllExport + BOOL GetPosition(const char* name, int& start, int& stop, + CBotGet modestart = GetPosExtern, + CBotGet modestop = GetPosBloc); + // donne la position d'une routine dans le texte d'origine + // le mode permet de choisir l'élément à trouver pour le début et la fin + // voir les modes ci-dessus dans CBotGet + + + CBotFunction* GivFunctions(); +}; + + +/////////////////////////////////////////////////////////////////////////////// +// routines pour la gestion d'un fichier (FILE*) + DllExport + FILE* fOpen(const char* name, const char* mode); + DllExport + int fClose(FILE* filehandle); + DllExport + size_t fWrite(const void *buffer, size_t elemsize, size_t length, FILE* filehandle); + DllExport + size_t fRead(void *buffer, size_t elemsize, size_t length, FILE* filehandle); + + +#if 0 +/* +(**) Note: + Pour définir une fonction externe, il faut procéder ainsi: + + a) définir une routine pour la compilation + cette routine reçois la liste des paramètres (sans valeurs) + et retourne soit un type de résultat (CBotTyp... ou 0 = void) + soit un numéro d'erreur + b) définir une routine pour l'exécution + cette rourine reCoit la liste des paramètres (avec valeurs), + une variable pour stocker le résultat (selon le type donné à la compilation) + + Par exemple, une routine qui calcule la moyenne d'une liste de paramètres */ + +int cMoyenne(CBotVar* &pVar, CBotString& ClassName) +{ + if ( pVar == NULL ) return 6001; // il n'y a aucun paramètre ! + + while ( pVar != NULL ) + { + if ( pVar->GivType() > CBotTypDouble ) return 6002; // ce n'est pas un nombre + pVar = pVar -> GivNext(); + } + + return CBotTypFloat; // le type du résultat pourrait dépendre des paramètres ! +} + + +BOOL rMoyenne(CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + float total = 0; + int nb = 0; + while (pVar != NULL) + { + total += pVar->GivValFloat(); + pVar = pVar->GivNext(); + nb++; + } + pResult->SetValFloat(total/nb); // retourne la valeur moyenne + + return TRUE; // opération totalement terminée +} + +#endif + +///////////////////////////////////////////////////////////////////////////////// +// Classe pour la gestion des variables + +// les méthodes marquées DllExport +// peuvent être utile à l'exterieur du module +// ( il n'est pour l'instant pas prévu de pouvoir créer ces objets en externe ) + +// résultats pour GivInit() +#define IS_UNDEF 0 // variable indéfinie +#define IS_DEF 1 // variable définie +#define IS_NAN 999 // variable définie comme étant not a number + +// type de variable SetPrivate / IsPrivate +#define PR_PUBLIC 0 // variable publique +#define PR_READ 1 // read only +#define PR_PROTECT 2 // protected (héritage) +#define PR_PRIVATE 3 // strictement privée + +class CBotVar +{ +protected: + CBotToken* m_token; // le token correspondant + + CBotVar* m_next; // liste de variables + friend class CBotStack; + friend class CBotCStack; + friend class CBotInstrCall; + friend class CBotProgram; + + CBotTypResult m_type; // type de valeur + + int m_binit; // pas initialisée ? + CBotVarClass* m_pMyThis; // ^élément this correspondant + void* m_pUserPtr; // ^données user s'il y a lieu + BOOL m_bStatic; // élément static (dans une classe) + int m_mPrivate; // élément public, protected ou private ? + + CBotInstr* m_InitExpr; // expression pour le contenu initial + CBotInstr* m_LimExpr; // liste des limites pour un tableau + friend class CBotClass; + friend class CBotVarClass; + friend class CBotVarPointer; + friend class CBotVarArray; + + long m_ident; // identificateur unique + static long m_identcpt; // compteur + +public: + CBotVar(); +virtual ~CBotVar( ); // destructeur + + +/* DllExport + static + CBotVar* Create( const char* name, int type, const char* ClassName = NULL); + // crée une variable selon son type,*/ + + DllExport + static + CBotVar* Create( const char* name, CBotTypResult type); + // idem à partir du type complet + + DllExport + static + CBotVar* Create( const char* name, CBotClass* pClass); + // idem pour une instance d'une classe connue + + static + CBotVar* Create( const CBotToken* name, int type ); + static + CBotVar* Create( const CBotToken* name, CBotTypResult type ); + + static + CBotVar* Create( const char* name, int type, CBotClass* pClass); + + static + CBotVar* Create( CBotVar* pVar ); + + + DllExport + void SetUserPtr(void* pUser); + // associe un pointeur utilisateur à une instance + + DllExport + virtual void SetIdent(long UniqId); + // associe un identificateur unique à une instance + // ( c'est à l'utilisateur de s'assurer que l'id est unique) + + DllExport + void* GivUserPtr(); + // rend le pointeur associé à la variable + + DllExport + CBotString GivName(); // le nom de la variable, s'il est connu + //////////////////////////////////////////////////////////////////////////////////// + void SetName(const char* name); // change le nom de la variable + + DllExport + int GivType(int mode = 0); // rend le type de base (int) de la variable + //////////////////////////////////////////////////////////////////////////////////////// + + DllExport + CBotTypResult GivTypResult(int mode = 0); // rend le type complet de la variable + + + CBotToken* GivToken(); + void SetType(CBotTypResult& type); + + DllExport + void SetInit(int bInit); // met la variable dans l'état IS_UNDEF, IS_DEF, IS_NAN + + DllExport + int GivInit(); // donne l'état de la variable + + DllExport + void SetStatic(BOOL bStatic); + DllExport + BOOL IsStatic(); + + DllExport + void SetPrivate(int mPrivate); + DllExport + BOOL IsPrivate(int mode = PR_PROTECT); + DllExport + int GivPrivate(); + + virtual + void ConstructorSet(); + + void SetVal(CBotVar* var); // remprend une valeur + + DllExport + virtual + CBotVar* GivItem(const char* name); // rend un élément d'une classe selon son nom (*) + virtual + CBotVar* GivItemRef(int nIdent); // idem à partir du n° ref + + DllExport + virtual + CBotVar* GivItem(int row, BOOL bGrow = FALSE); + + DllExport + virtual + CBotVar* GivItemList(); // donne la liste des éléments + + DllExport + CBotVar* GivStaticVar(); // rend le pointeur à la variable si elle est statique + + DllExport + BOOL IsElemOfClass(const char* name); + // dit si l'élément appartient à la classe "name" + // rend TRUE si l'objet est d'une classe fille + + DllExport + CBotVar* GivNext(); // prochaine variable dans la liste (paramètres) + //////////////////////////////////////////////////////////////////////////////////////////// + + void AddNext(CBotVar* pVar); // ajoute dans une liste + + virtual + void Copy(CBotVar* pSrc, BOOL bName = TRUE); // fait une copie de la variable + + DllExport + virtual void SetValInt(int val, const char* name = NULL); + // initialise avec une valeur entière (#) + ///////////////////////////////////////////////////////////////////////////////// + + DllExport + virtual void SetValFloat(float val); // initialise avec une valeur réelle (#) + //////////////////////////////////////////////////////////////////////////////// + + DllExport + virtual void SetValString(const char* p);// initialise avec une valeur chaîne (#) + //////////////////////////////////////////////////////////////////////////////// + + DllExport + virtual int GivValInt(); // demande la valeur entière (#) + //////////////////////////////////////////////////////////////////////// + + DllExport + virtual float GivValFloat(); // demande la valeur réelle (#) + /////////////////////////////////////////////////////////////////////// + + virtual + CBotString GivValString(); // demande la valeur chaîne (#) + /////////////////////////////////////////////////////////////////////// + + virtual void SetClass(CBotClass* pClass); + virtual + CBotClass* GivClass(); + + virtual void SetPointer(CBotVar* p); + virtual + CBotVarClass* GivPointer(); +// virtual void SetIndirection(CBotVar* pVar); + + virtual void Add(CBotVar* left, CBotVar* right); // addition + virtual void Sub(CBotVar* left, CBotVar* right); // soustraction + virtual void Mul(CBotVar* left, CBotVar* right); // multiplication + virtual int Div(CBotVar* left, CBotVar* right); // division + virtual int Modulo(CBotVar* left, CBotVar* right); // reste de division + virtual void Power(CBotVar* left, CBotVar* right); // puissance + + virtual BOOL Lo(CBotVar* left, CBotVar* right); + virtual BOOL Hi(CBotVar* left, CBotVar* right); + virtual BOOL Ls(CBotVar* left, CBotVar* right); + virtual BOOL Hs(CBotVar* left, CBotVar* right); + virtual BOOL Eq(CBotVar* left, CBotVar* right); + virtual BOOL Ne(CBotVar* left, CBotVar* right); + + virtual void And(CBotVar* left, CBotVar* right); + virtual void Or(CBotVar* left, CBotVar* right); + virtual void XOr(CBotVar* left, CBotVar* right); + virtual void ASR(CBotVar* left, CBotVar* right); + virtual void SR(CBotVar* left, CBotVar* right); + virtual void SL(CBotVar* left, CBotVar* right); + + virtual void Neg(); + virtual void Not(); + virtual void Inc(); + virtual void Dec(); + + + virtual BOOL Save0State(FILE* pf); + virtual BOOL Save1State(FILE* pf); + static BOOL RestoreState(FILE* pf, CBotVar* &pVar); + + DllExport + void debug(); + +// virtual +// CBotVar* GivMyThis(); + + DllExport + virtual + void Maj(void* pUser = NULL, BOOL bContinue = TRUE); + + void SetUniqNum(long n); + long GivUniqNum(); + static long NextUniqNum(); +}; + +/* NOTE (#) + les méthodes SetValInt() SetValFloat() et SetValString() + ne peuvent êtes appellées qu'avec des objets respectivement entier, réelle ou chaîne + toujours s'assurer du type de la variable avant d'appeller ces méthodes + + if ( pVar->GivType() == CBotInt() ) pVar->SetValFloat( 3.3 ); // plante !! + + les méthodes GivValInt(), GivValFloat() et GivValString() + font des conversions de valeur, + GivValString() fonctionne sur des nombres (rend la chaîne correspondante) + par contre il ne faut pas faire de GivValInt() avec une variable de type chaîne ! +*/ + + + +//////////////////////////////////////////////////////////////////////// +// Gestion des classes +//////////////////////////////////////////////////////////////////////// + +// classe pour définir de nouvelle classes dans le language CBOT +// par exemple pour définir la classe CPoint (x,y) + +class CBotClass +{ +private: + static + CBotClass* m_ExClass; // liste des classes existante à un moment donné + CBotClass* m_ExNext; // pour cette liste générale + CBotClass* m_ExPrev; // pour cette liste générale + +private: + CBotClass* m_pParent; // classe parent + CBotString m_name; // nom de cette classe-ci + int m_nbVar; // nombre de variables dans la chaîne + CBotVar* m_pVar; // contenu de la classe + BOOL m_bIntrinsic; // classe intrinsèque + CBotClass* m_next; // chaine les classe + CBotCallMethode* m_pCalls; // liste des méthodes définie en externe + CBotFunction* m_pMethod; // liste des méthodes compilées + void (*m_rMaj) ( CBotVar* pThis, void* pUser ); + friend class CBotVarClass; + int m_cptLock; // pour Lock / UnLock + int m_cptOne; // pour réentrance Lock + CBotProgram* m_ProgInLock[5];// processus en attente pour synchro + +public: + BOOL m_IsDef; // marque si est définie ou pas encore + + DllExport + CBotClass( const char* name, + CBotClass* pParent, BOOL bIntrinsic = FALSE ); // constructeur + // Dès qu'une classe est créée, elle est connue + // partout dans CBot + // le mode intrinsic donne une classe qui n'est pas gérée par des pointeurs + + DllExport + ~CBotClass( ); // destructeur + + DllExport + BOOL AddFunction(const char* name, + BOOL rExec (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception), + CBotTypResult rCompile (CBotVar* pThis, CBotVar* &pVar)); + // cet appel permet d'ajouter de manière externe (**) + // une nouvelle méthode utilisable par les objets de cette classe + + DllExport + BOOL AddUpdateFunc( void rMaj ( CBotVar* pThis, void* pUser ) ); + // défini la routine qui sera appellée pour mettre à jour les élements de la classe + + DllExport + BOOL AddItem(CBotString name, CBotTypResult type, int mPrivate = PR_PUBLIC); + // ajoute un élément à la classe +// DllExport +// BOOL AddItem(CBotString name, CBotClass* pClass); + // idem pour des éléments appartenant à pClass + DllExport + BOOL AddItem(CBotVar* pVar); + // idem en passant le pointeur à une instance d'une variable + // l'objet est pris tel quel, il ne faut donc pas le détruire + + + + // idem en donnant un élément de type CBotVar + void AddNext(CBotClass* pClass); + + DllExport + CBotString GivName(); // rend le nom de la classe + DllExport + CBotClass* GivParent(); // donne la classe père (ou NULL) + + // dit si une classe est dérivée (Extends) d'une autre + // rend TRUE aussi si les classes sont identiques + DllExport + BOOL IsChildOf(CBotClass* pClass); + + static + CBotClass* Find(CBotToken* &pToken); // trouve une classe d'après son nom + + DllExport + static + CBotClass* Find(const char* name); + + CBotVar* GivVar(); // rend la liste des variables + CBotVar* GivItem(const char* name); // l'une des variables selon son nom + CBotVar* GivItemRef(int nIdent); + + CBotTypResult CompileMethode(const char* name, CBotVar* pThis, CBotVar** ppParams, + CBotCStack* pStack, long& nIdent); + + BOOL ExecuteMethode(long& nIdent, const char* name, CBotVar* pThis, CBotVar** ppParams, CBotVar* &pResult, CBotStack* &pStack, CBotToken* pToken); + void RestoreMethode(long& nIdent, const char* name, CBotVar* pThis, CBotVar** ppParams, CBotStack* &pStack); + + // compile une classe déclarée par l'utilisateur + static + CBotClass* Compile(CBotToken* &p, CBotCStack* pStack); + static + CBotClass* Compile1(CBotToken* &p, CBotCStack* pStack); + + BOOL CompileDefItem(CBotToken* &p, CBotCStack* pStack, BOOL bSecond); + + BOOL IsIntrinsic(); + void Purge(); + static + void Free(); + + DllExport + static + BOOL SaveStaticState(FILE* pf); + + DllExport + static + BOOL RestoreStaticState(FILE* pf); + + BOOL Lock(CBotProgram* p); + void Unlock(); + static + void FreeLock(CBotProgram* p); + + BOOL CheckCall(CBotToken* &pToken, CBotDefParam* pParam); + +}; + +#define MAXDEFNUM 1000 // nombre limite des DefineNum + +///////////////////////////////////////////////////////////////////////////////////// +// gestion des jetons (tokens) + +#define TokenTypKeyWord 1 // un mot clef du language (voir TokenKeyWord) +#define TokenTypNum 2 // un nombre +#define TokenTypString 3 // une chaine +#define TokenTypVar 4 // un nom de variable +#define TokenTypDef 5 // une valeur selon DefineNum + +#define TokenKeyWord 2000 // les mots clefs du langage +#define TokenKeyDeclare 2100 // mots clefs pour déclarations (int, float,..) +#define TokenKeyVal 2200 // les mots représentant une "valeur" (true, false, null, nan) +#define TokenKeyOp 2300 // les opérateurs + + +class CBotToken +{ +private: + static + CBotStringArray m_ListKeyWords; // liste des mots clefs du language + static + int m_ListIdKeyWords[200]; // les codes correspondants + + static + CBotStringArray m_ListKeyDefine; // les noms définis par un DefineNum + static + long m_ListKeyNums[MAXDEFNUM]; // les valeurs associées + +private: + CBotToken* m_next; // suivant dans la liste + CBotToken* m_prev; + int m_type; // type de Token + long m_IdKeyWord; // numéro du mot clef si c'en est un + // ou valeur du "define" + + CBotString m_Text; // mot trouvé comme token + CBotString m_Sep; // séparateurs qui suivent + + int m_start; // position dans le texte d'origine (programme) + int m_end; // itou pour la fin du token + + static + int GivKeyWords(const char* w); // est-ce un mot clef ? + static + BOOL GivKeyDefNum(const char* w, CBotToken* &token); + + static + void LoadKeyWords(); // fait la liste des mots clefs + +public: + CBotToken(); + CBotToken(const CBotToken* pSrc); + CBotToken(CBotString& mot, CBotString& sep, int start=0, int end=0); + CBotToken(const char* mot, const char* sep = NULL); + // constructeur + ~CBotToken(); // destructeur + + DllExport + int GivType(); // rend le type du token + + DllExport + CBotString& GivString(); // rend la chaine correspondant à ce token + + DllExport + CBotString& GivSep(); // rend le séparateur suivant le token + + DllExport + int GivStart(); // position du début dans le texte + DllExport + int GivEnd(); // position de fin dans le texte + + DllExport + CBotToken* GivNext(); // rend le suivant dans la liste + DllExport + CBotToken* GivPrev(); // rend le Précédent dans la liste + + DllExport + static + CBotToken* CompileTokens(const char* p, int& error); + // transforme tout le programme + DllExport + static + void Delete(CBotToken* pToken); // libère la liste + + + // fonctions non utiles en export + static + BOOL DefineNum(const char* name, long val); + void SetString(const char* name); + + void SetPos(int start, int end); + long GivIdKey(); + void AddNext(CBotToken* p); // ajoute un token (une copie) + + static + CBotToken* NextToken(char* &program, int& error, BOOL first = FALSE); + // trouve le prochain token + const CBotToken& + operator=(const CBotToken& src); + + static + void Free(); +}; + + + +#if 0 +//////////////////////////////////////////////////////////////////////// +// Exemples d'utilisation +// Définition de classes et de fonctions + + +// définie la classe globale CPoint +// -------------------------------- + m_pClassPoint = new CBotClass("CPoint", NULL); + // ajoute le composant ".x" + m_pClassPoint->AddItem("x", CBotTypResult(CBotTypFloat)); + // ajoute le composant ".y" + m_pClassPoint->AddItem("y", CBotTypResult(CBotTypFloat)); + // le joueur peut alors utiliser les instructions + // CPoint position; position.x = 12; position.y = -13.6 + +// définie la classe CColobotObject +// -------------------------------- +// cette classe gère tous les objets dans le monde de COLOBOT +// le programme utilisateur "main" appartient à cette classe + m_pClassObject = new CBotClass("CColobotObject", m_pClassBase); + // ajoute le composant ".position" + m_pClassObject->AddItem("position", m_pClassPoint); + // ajoute le composant ".type" + m_pClassObject->AddItem("type", CBotTypResult(CBotTypShort)); + // ajoute une définition de constante + m_pClassObject->AddConst("ROBOT", CBotTypShort, 1); // ROBOT équivalent à la valeur 1 + // ajoute la routine FIND + m_pClassObject->AddFunction( rCompFind, rDoFind ); + // le joueur peut maintenant utiliser les instructions + // CColobotObject chose; chose = FIND( ROBOT ) + + + +// définie la classe CColobotRobot dérivée de CColobotObject +// --------------------------------------------------------- +// les programmes "main" associés aux robots font partie de cette classe + m_pClassRobot = new CBotClass("CColobotRobot", m_pClassObject); + // ajoute la routine GOTO + m_pClassRobot->AddFunction( rCompGoto, rDoGoto ); + // le joueur peut maintenant faire + // GOTO( FIND ( ROBOT ) ); + + +// crée une instance de la classe Robot +// ------------------------------------ +// par exemple un nouveau robot qui vient d'être fabriqué + CBotVar* m_pMonRobot = new CBotVar("MonRobot", m_pClassRobot); + +// compile le programme main pour ce robot-là +// ------------------------------------------ + CString LeProgramme( "void main() {GOTO(0, 0); return 0;}" ); + if ( !m_pMonRobot->Compile( LeProgramme ) ) {gestion d'erreur...}; + +// construit une pile pour l'interpréteur +// -------------------------------------- + CBotStack* pStack = new CBotStack(NULL); + +// exécute le programme main +// ------------------------- + while( FALSE = m_pMonRobot->Execute( "main", pStack )) + { + // programme suspendu + // on pourrait passer la main à un autre (en sauvegardant pStack pour ce robot-là) + }; + // programme "main" terminé ! + + + + +// routine implémentant l'instruction GOTO( CPoint pos ) +BOOL rDoGoto( CBotVar* pVar, CBotVar* pResult, int& exception ) +{ + if (pVar->GivType() != CBotTypeClass || + pVar->IsElemOfClas("CPoint") ) { exception = 6522; return FALSE; ) + // le paramètre n'est pas de la bonne classe ? + // NB en fait ce contrôle est déjà fait par la routine pour la compilation + + m_PosToGo.Copy( pVar ); // garde la position à atteindre (object type CBotVar) + + // ou alors + CBotVar* temp; + temp = pVar->GivItem("x"); // trouve forcément pour un object de type "CPoint" + ASSERT (temp != NULL && temp->GivType() == CBotTypFloat); + m_PosToGo.x = temp->GivValFloat(); + + temp = pVar->GivItem("y"); // trouve forcément pour un object de type "CPoint" + ASSERT (temp != NULL && temp->GivType() == CBotTypFloat); + m_PosToGo.y = temp->GivValFloat(); + + return (m_CurentPos == m_PosToGo); // rend TRUE si la position est atteinte + // rend FALSE s'il faut patienter encore +} + +#endif \ No newline at end of file diff --git a/src/CBot/CBotFunction.cpp b/src/CBot/CBotFunction.cpp new file mode 100644 index 00000000..43dbc83b --- /dev/null +++ b/src/CBot/CBotFunction.cpp @@ -0,0 +1,1634 @@ +/////////////////////////////////////////////////////////////////////// +// compilation des diverses fonctions déclarées par l'utilisateur +// + +#include "CBot.h" + +// les divers constructeurs / destructeurs +// pour libérer tout selon l'arbre établi +CBotFunction::CBotFunction() +{ + m_Param = NULL; // liste des paramètres vide + m_Block = NULL; // le bloc d'instructions + m_next = NULL; // les fonctions peuvent être chaînées + m_bPublic = FALSE; // fonction non publique + m_bExtern = FALSE; // fonction non externe + m_nextpublic = NULL; + m_prevpublic = NULL; + m_pProg = NULL; +// m_nThisIdent = 0; + m_nFuncIdent = 0; + m_bSynchro = FALSE; +} + +CBotFunction* CBotFunction::m_listPublic = NULL; + +CBotFunction::~CBotFunction() +{ + delete m_Param; // liste des paramètres vide + delete m_Block; // le bloc d'instructions + delete m_next; + + // enlève de la liste publique s'il y a lieu + if ( m_bPublic ) + { + if ( m_nextpublic != NULL ) + { + m_nextpublic->m_prevpublic = m_prevpublic; + } + if ( m_prevpublic != NULL) + { + m_prevpublic->m_nextpublic = m_nextpublic; + } + else + { + // si prev = next = null peut ne pas être dans la liste ! + if ( m_listPublic == this ) m_listPublic = m_nextpublic; + } + } +} + +BOOL CBotFunction::IsPublic() +{ + return m_bPublic; +} + +BOOL CBotFunction::IsExtern() +{ + return m_bExtern; +} + +BOOL CBotFunction::GetPosition(int& start, int& stop, CBotGet modestart, CBotGet modestop) +{ + start = m_extern.GivStart(); + stop = m_closeblk.GivEnd(); + + if (modestart == GetPosExtern) + { + start = m_extern.GivStart(); + } + if (modestop == GetPosExtern) + { + stop = m_extern.GivEnd(); + } + if (modestart == GetPosNom) + { + start = m_token.GivStart(); + } + if (modestop == GetPosNom) + { + stop = m_token.GivEnd(); + } + if (modestart == GetPosParam) + { + start = m_openpar.GivStart(); + } + if (modestop == GetPosParam) + { + stop = m_closepar.GivEnd(); + } + if (modestart == GetPosBloc) + { + start = m_openblk.GivStart(); + } + if (modestop == GetPosBloc) + { + stop = m_closeblk.GivEnd(); + } + + return TRUE; +} + + +CBotTypResult ArrayType(CBotToken* &p, CBotCStack* pile, CBotTypResult type) +{ + while ( IsOfType( p, ID_OPBRK ) ) + { + if ( !IsOfType( p, ID_CLBRK ) ) + { + pile->SetError(TX_CLBRK, p->GivStart()); + return CBotTypResult( -1 ); + } + type = CBotTypResult( CBotTypArrayPointer, type ); + } + return type; +} + +CBotTypResult TypeParam(CBotToken* &p, CBotCStack* pile) +{ + CBotClass* pClass = NULL; + + switch (p->GivType()) + { + case ID_INT: + p = p->GivNext(); + return ArrayType(p, pile, CBotTypResult( CBotTypInt )); + case ID_FLOAT: + p = p->GivNext(); + return ArrayType(p, pile, CBotTypResult( CBotTypFloat )); + case ID_BOOLEAN: + case ID_BOOL: + p = p->GivNext(); + return ArrayType(p, pile, CBotTypResult( CBotTypBoolean )); + case ID_STRING: + p = p->GivNext(); + return ArrayType(p, pile, CBotTypResult( CBotTypString )); + case ID_VOID: + p = p->GivNext(); + return CBotTypResult( 0 ); + + case TokenTypVar: + pClass = CBotClass::Find(p); + if ( pClass != NULL) + { + p = p->GivNext(); + return ArrayType(p, pile, + pClass->IsIntrinsic() ? + CBotTypResult( CBotTypIntrinsic, pClass ) : + CBotTypResult( CBotTypPointer, pClass ) ); + } + } + return CBotTypResult( -1 ); +} + +// compile une nouvelle fonction +// bLocal permet de mettre la déclaration des paramètres au même niveau +// que le éléments appartenant à la classe pour les méthodes +CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunction* finput, BOOL bLocal) +{ + CBotToken* pp; + CBotFunction* func = finput; + if ( func == NULL ) func = new CBotFunction(); + + CBotCStack* pStk = pStack->TokenStack(p, bLocal); + +// func->m_nFuncIdent = CBotVar::NextUniqNum(); + + while (TRUE) + { + if ( IsOfType(p, ID_PUBLIC) ) + { + func->m_bPublic = TRUE; + continue; + } + pp = p; + if ( IsOfType(p, ID_EXTERN) ) + { + func->m_extern = pp; // pour la position du mot "extern" + func->m_bExtern = TRUE; +// func->m_bPublic = TRUE; // donc aussi publique! + continue; + } + break; + } + + func->m_retToken = *p; +// CBotClass* pClass; + func->m_retTyp = TypeParam(p, pStk); // type du résultat + + if (func->m_retTyp.GivType() >= 0) + { + CBotToken* pp = p; + func->m_token = *p; + + if ( IsOfType(p, ID_NOT) ) + { + CBotToken d("~" + p->GivString()); + func->m_token = d; + } + + // un nom de fonction est-il là ? + if (IsOfType(p, TokenTypVar)) + { + if ( IsOfType( p, ID_DBLDOTS ) ) // méthode pour une classe + { + func->m_MasterClass = pp->GivString(); + CBotClass* pClass = CBotClass::Find(pp); + if ( pClass == NULL ) goto bad; + +// pp = p; + func->m_token = *p; + if (!IsOfType(p, TokenTypVar)) goto bad; + + } + func->m_openpar = p; + func->m_Param = CBotDefParam::Compile( p, pStk ); + func->m_closepar = p->GivPrev(); + if (pStk->IsOk()) + { + pStk->SetRetType(func->m_retTyp); // pour savoir de quel type les return + + if (!func->m_MasterClass.IsEmpty()) + { + // rend "this" connu + CBotVar* pThis = CBotVar::Create("this", CBotTypResult( CBotTypClass, func->m_MasterClass )); + pThis->SetInit(2); +// pThis->SetUniqNum(func->m_nThisIdent = -2); //CBotVar::NextUniqNum() va pas + pThis->SetUniqNum(-2); + pStk->AddVar(pThis); + + // initialise les variables selon This + // n'enregistre que le pointeur à la première, + // le reste est chainé + CBotVar* pv = pThis->GivItemList(); +// int num = 1; + while (pv != NULL) + { + CBotVar* pcopy = CBotVar::Create(pv); +// pcopy->SetInit(2); + pcopy->Copy(pv); + pcopy->SetPrivate(pv->GivPrivate()); +// pcopy->SetUniqNum(pv->GivUniqNum()); //num++); + pStk->AddVar(pcopy); + pv = pv->GivNext(); + } + } + + // et compile le bloc d'instruction qui suit + func->m_openblk = p; + func->m_Block = CBotBlock::Compile(p, pStk, FALSE); + func->m_closeblk = p->GivPrev(); + if ( pStk->IsOk() ) + { + if ( func->m_bPublic ) // fonction publique, la rend connue pour tous + { + CBotFunction::AddPublic(func); + } + return pStack->ReturnFunc(func, pStk); + } + } + } +bad: + pStk->SetError(TX_NOFONC, p); + } + pStk->SetError(TX_NOTYP, p); + if ( finput == NULL ) delete func; + return pStack->ReturnFunc(NULL, pStk); +} + +// pré-compile une nouvelle fonction +CBotFunction* CBotFunction::Compile1(CBotToken* &p, CBotCStack* pStack, CBotClass* pClass) +{ + CBotFunction* func = new CBotFunction(); + func->m_nFuncIdent = CBotVar::NextUniqNum(); + + CBotCStack* pStk = pStack->TokenStack(p, TRUE); + + while (TRUE) + { + if ( IsOfType(p, ID_PUBLIC) ) + { + // func->m_bPublic = TRUE; // sera fait en passe 2 + continue; + } + if ( IsOfType(p, ID_EXTERN) ) + { + func->m_bExtern = TRUE; + continue; + } + break; + } + + func->m_retToken = *p; + func->m_retTyp = TypeParam(p, pStack); // type du résultat + + if (func->m_retTyp.GivType() >= 0) + { + CBotToken* pp = p; + func->m_token = *p; + // un nom de fonction est-il là ? + if (IsOfType(p, TokenTypVar)) + { + if ( IsOfType( p, ID_DBLDOTS ) ) // méthode pour une classe + { + func->m_MasterClass = pp->GivString(); + CBotClass* pClass = CBotClass::Find(pp); + if ( pClass == NULL ) + { + pStk->SetError(TX_NOCLASS, pp); + goto bad; + } + + pp = p; + func->m_token = *p; + if (!IsOfType(p, TokenTypVar)) goto bad; + + } + func->m_Param = CBotDefParam::Compile( p, pStk ); + if (pStk->IsOk()) + { + // regarde si la fonction existe ailleurs + if (( pClass != NULL || !pStack->CheckCall(pp, func->m_Param)) && + ( pClass == NULL || !pClass->CheckCall(pp, func->m_Param)) ) + { + if (IsOfType(p, ID_OPBLK)) + { + int level = 1; + // et saute le bloc d'instructions qui suit + do + { + int type = p->GivType(); + p = p->GivNext(); + if (type == ID_OPBLK) level++; + if (type == ID_CLBLK) level--; + } + while (level > 0 && p != NULL); + + return pStack->ReturnFunc(func, pStk); + } + pStk->SetError(TX_OPENBLK, p); + } + } + pStk->SetError(TX_REDEF, pp); + } +bad: + pStk->SetError(TX_NOFONC, p); + } + pStk->SetError(TX_NOTYP, p); + delete func; + return pStack->ReturnFunc(NULL, pStk); +} + +#ifdef _DEBUG +static int xx = 0; +#endif + +BOOL CBotFunction::Execute(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInstance) +{ + CBotStack* pile = pj->AddStack(this, 2); // un bout de pile local à cette fonction +// if ( pile == EOX ) return TRUE; + + pile->SetBotCall(m_pProg); // bases pour les routines + + if ( pile->GivState() == 0 ) + { + if ( !m_Param->Execute(ppVars, pile) ) return FALSE; // défini les paramètres + pile->IncState(); + } + + if ( pile->GivState() == 1 && !m_MasterClass.IsEmpty() ) + { + // rend "this" connu + CBotVar* pThis ; + if ( pInstance == NULL ) + { + pThis = CBotVar::Create("this", CBotTypResult( CBotTypClass, m_MasterClass )); + pThis->SetInit(2); + } + else + { + pThis = CBotVar::Create("this", CBotTypResult( CBotTypPointer, m_MasterClass )); + pThis->SetPointer(pInstance); + pThis->SetInit(2); + } + +// pThis->SetUniqNum(m_nThisIdent); + pThis->SetUniqNum(-2); + pile->AddVar(pThis); + + pile->IncState(); + } + + if ( pile->IfStep() ) return FALSE; + + if ( !m_Block->Execute(pile) ) + { + if ( pile->GivError() < 0 ) + pile->SetError( 0 ); + else + return FALSE; + } + + return pj->Return(pile); +} + + +void CBotFunction::RestoreState(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInstance) +{ + CBotStack* pile = pj->RestoreStack(this); // un bout de pile local à cette fonction + if ( pile == NULL ) return; + CBotStack* pile2 = pile; + + pile->SetBotCall(m_pProg); // bases pour les routines + + if ( pile->GivBlock() < 2 ) + { + CBotStack* pile2 = pile->RestoreStack(NULL); // un bout de pile local à cette fonction + if ( pile2 == NULL ) return; + pile->SetState(pile->GivState() + pile2->GivState()); + pile2->Delete(); + } + + m_Param->RestoreState(pile2, TRUE); // les paramètres + + if ( !m_MasterClass.IsEmpty() ) + { + CBotVar* pThis = pile->FindVar("this"); + pThis->SetInit(2); + pThis->SetUniqNum(-2); + } + + m_Block->RestoreState(pile2, TRUE); +} + +void CBotFunction::AddNext(CBotFunction* p) +{ + CBotFunction* pp = this; + while (pp->m_next != NULL) pp = pp->m_next; + + pp->m_next = p; +} + + +CBotTypResult CBotFunction::CompileCall(const char* name, CBotVar** ppVars, long& nIdent) +{ + nIdent = 0; + CBotTypResult type; + + CBotFunction* pt = FindLocalOrPublic(nIdent, name, ppVars, type); + return type; +} + + +// trouve une fonction selon son identificateur unique +// si l'identificateur n'est pas trouvé, cherche selon le nom et les paramètres + +CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const char* name, CBotVar** ppVars, CBotTypResult& TypeOrError, BOOL bPublic) +{ + TypeOrError.SetType(TX_UNDEFCALL); // pas de routine de ce nom + CBotFunction* pt; + + if ( nIdent ) + { + if ( this != NULL ) for ( pt = this ; pt != NULL ; pt = pt->m_next ) + { + if ( pt->m_nFuncIdent == nIdent ) + { + TypeOrError = pt->m_retTyp; + return pt; + } + } + + // recherche dans la liste des fonctions publiques + + for ( pt = m_listPublic ; pt != NULL ; pt = pt->m_nextpublic ) + { + if ( pt->m_nFuncIdent == nIdent ) + { + TypeOrError = pt->m_retTyp; + return pt; + } + } + } + + if ( name == NULL ) return NULL; + + int delta = 99999; // cherche la signature la plus faible + CBotFunction* pFunc = NULL; // la meilleure fonction trouvée + + if ( this != NULL ) + { + for ( pt = this ; pt != NULL ; pt = pt->m_next ) + { + if ( pt->m_token.GivString() == name ) + { + int i = 0; + int alpha = 0; // signature des paramètres + // les paramètres sont-ils compatibles ? + CBotDefParam* pv = pt->m_Param; // liste des paramètres attendus + CBotVar* pw = ppVars[i++]; // liste des paramètres fournis + while ( pv != NULL && pw != NULL) + { + if (!TypesCompatibles(pv->GivTypResult(), pw->GivTypResult())) + { + if ( pFunc == NULL ) TypeOrError = TX_BADPARAM; + break; + } + int d = pv->GivType() - pw->GivType(2); + alpha += d>0 ? d : -10*d; // perte de qualité, 10 fois plus cher !! + + pv = pv->GivNext(); + pw = ppVars[i++]; + } + if ( pw != NULL ) + { + if ( pFunc != NULL ) continue; + if ( TypeOrError.Eq(TX_LOWPARAM) ) TypeOrError.SetType(TX_NUMPARAM); + if ( TypeOrError.Eq(TX_UNDEFCALL)) TypeOrError.SetType(TX_OVERPARAM); + continue; // trop de paramètres + } + if ( pv != NULL ) + { + if ( pFunc != NULL ) continue; + if ( TypeOrError.Eq(TX_OVERPARAM) ) TypeOrError.SetType(TX_NUMPARAM); + if ( TypeOrError.Eq(TX_UNDEFCALL) ) TypeOrError.SetType(TX_LOWPARAM); + continue; // pas assez de paramètres + } + + if (alpha == 0) // signature parfaite + { + nIdent = pt->m_nFuncIdent; + TypeOrError = pt->m_retTyp; + return pt; + } + + if ( alpha < delta ) // une meilleur signature ? + { + pFunc = pt; + delta = alpha; + } + } + } + } + + if ( bPublic ) + { + for ( pt = m_listPublic ; pt != NULL ; pt = pt->m_nextpublic ) + { + if ( pt->m_token.GivString() == name ) + { + int i = 0; + int alpha = 0; // signature des paramètres + // les paramètres sont-ils compatibles ? + CBotDefParam* pv = pt->m_Param; // liste des paramètres attendus + CBotVar* pw = ppVars[i++]; // liste des paramètres fournis + while ( pv != NULL && pw != NULL) + { + if (!TypesCompatibles(pv->GivTypResult(), pw->GivTypResult())) + { + if ( pFunc == NULL ) TypeOrError = TX_BADPARAM; + break; + } + int d = pv->GivType() - pw->GivType(2); + alpha += d>0 ? d : -10*d; // perte de qualité, 10 fois plus cher !! + + pv = pv->GivNext(); + pw = ppVars[i++]; + } + if ( pw != NULL ) + { + if ( pFunc != NULL ) continue; + if ( TypeOrError.Eq(TX_LOWPARAM) ) TypeOrError.SetType(TX_NUMPARAM); + if ( TypeOrError.Eq(TX_UNDEFCALL)) TypeOrError.SetType(TX_OVERPARAM); + continue; // trop de paramètres + } + if ( pv != NULL ) + { + if ( pFunc != NULL ) continue; + if ( TypeOrError.Eq(TX_OVERPARAM) ) TypeOrError.SetType(TX_NUMPARAM); + if ( TypeOrError.Eq(TX_UNDEFCALL) ) TypeOrError.SetType(TX_LOWPARAM); + continue; // pas assez de paramètres + } + + if (alpha == 0) // signature parfaite + { + nIdent = pt->m_nFuncIdent; + TypeOrError = pt->m_retTyp; + return pt; + } + + if ( alpha < delta ) // une meilleur signature ? + { + pFunc = pt; + delta = alpha; + } + } + } + } + + if ( pFunc != NULL ) + { + nIdent = pFunc->m_nFuncIdent; + TypeOrError = pFunc->m_retTyp; + return pFunc; + } + return NULL; +} + + +// fait un appel à une fonction + +int CBotFunction::DoCall(long& nIdent, const char* name, CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken) +{ + CBotTypResult type; + CBotFunction* pt = NULL; + + pt = FindLocalOrPublic(nIdent, name, ppVars, type); + + if ( pt != NULL ) + { + CBotStack* pStk1 = pStack->AddStack(pt, 2); // pour mettre "this" +// if ( pStk1 == EOX ) return TRUE; + + pStk1->SetBotCall(pt->m_pProg); // on a peut-être changé de module + + if ( pStk1->IfStep() ) return FALSE; + + CBotStack* pStk3 = pStk1->AddStack(NULL, TRUE); // paramètres + + // prépare les paramètres sur la pile + + if ( pStk1->GivState() == 0 ) + { + if ( !pt->m_MasterClass.IsEmpty() ) + { + CBotVar* pInstance = m_pProg->m_pInstance; + // rend "this" connu + CBotVar* pThis ; + if ( pInstance == NULL ) + { + pThis = CBotVar::Create("this", CBotTypResult( CBotTypClass, pt->m_MasterClass )); + pThis->SetInit(2); + } + else + { + pThis = CBotVar::Create("this", CBotTypResult( CBotTypPointer, pt->m_MasterClass )); + pThis->SetPointer(pInstance); + pThis->SetInit(2); + } + + pThis->SetUniqNum(-2); + pStk1->AddVar(pThis); + + } + + // initialise les variables selon paramètres + pt->m_Param->Execute(ppVars, pStk3); // ne peut pas être interrompu + + pStk1->IncState(); + } + + // finalement exécute la fonction trouvée + + if ( !pStk3->GivRetVar( // remet le résultat sur la pile + pt->m_Block->Execute(pStk3) )) // GivRetVar dit si c'est interrompu + { + if ( !pStk3->IsOk() && pt->m_pProg != m_pProg ) + { +#ifdef _DEBUG + if ( m_pProg->GivFunctions()->GivName() == "LaCommande" ) return FALSE; +#endif + pStk3->SetPosError(pToken); // indique l'erreur sur l'appel de procédure + } + return FALSE; // interrompu ! + } + + return pStack->Return( pStk3 ); + } + return -1; +} + +void CBotFunction::RestoreCall(long& nIdent, const char* name, CBotVar** ppVars, CBotStack* pStack) +{ + CBotTypResult type; + CBotFunction* pt = NULL; + CBotStack* pStk1; + CBotStack* pStk3; + + // recherche la fonction pour remettre l'identificateur ok + + pt = FindLocalOrPublic(nIdent, name, ppVars, type); + + if ( pt != NULL ) + { + pStk1 = pStack->RestoreStack(pt); + if ( pStk1 == NULL ) return; + + pStk1->SetBotCall(pt->m_pProg); // on a peut-être changé de module + + if ( pStk1->GivBlock() < 2 ) + { + CBotStack* pStk2 = pStk1->RestoreStack(NULL); // plus utilisé + if ( pStk2 == NULL ) return; + pStk3 = pStk2->RestoreStack(NULL); + if ( pStk3 == NULL ) return; + } + else + { + pStk3 = pStk1->RestoreStack(NULL); + if ( pStk3 == NULL ) return; + } + + // prépare les paramètres sur la pile + + { + if ( !pt->m_MasterClass.IsEmpty() ) + { + CBotVar* pInstance = m_pProg->m_pInstance; + // rend "this" connu + CBotVar* pThis = pStk1->FindVar("this"); + pThis->SetInit(2); + pThis->SetUniqNum(-2); + } + } + + if ( pStk1->GivState() == 0 ) + { + pt->m_Param->RestoreState(pStk3, TRUE); + return; + } + + // initialise les variables selon paramètres + pt->m_Param->RestoreState(pStk3, FALSE); + pt->m_Block->RestoreState(pStk3, TRUE); + } +} + + + +// fait un appel d'une méthode +// note : this est déjà sur la pile, le pointeur pThis est juste là pour simplifier + +int CBotFunction::DoCall(long& nIdent, const char* name, CBotVar* pThis, CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken, CBotClass* pClass) +{ + CBotTypResult type; + CBotProgram* pProgCurrent = pStack->GivBotCall(); + + CBotFunction* pt = FindLocalOrPublic(nIdent, name, ppVars, type, FALSE); + + if ( pt != NULL ) + { +// DEBUG( "CBotFunction::DoCall" + pt->GivName(), 0, pStack); + + CBotStack* pStk = pStack->AddStack(pt, 2); +// if ( pStk == EOX ) return TRUE; + + pStk->SetBotCall(pt->m_pProg); // on a peut-être changé de module + CBotStack* pStk3 = pStk->AddStack(NULL, TRUE); // pour mettre les paramètres passés + + // prépare les paramètres sur la pile + + if ( pStk->GivState() == 0 ) + { + // met la variable "this" sur la pile + CBotVar* pthis = CBotVar::Create("this", CBotTypNullPointer); + pthis->Copy(pThis, FALSE); + pthis->SetUniqNum(-2); // valeur spéciale + pStk->AddVar(pthis); + + CBotClass* pClass = pThis->GivClass()->GivParent(); + if ( pClass ) + { + // met la variable "super" sur la pile + CBotVar* psuper = CBotVar::Create("super", CBotTypNullPointer); + psuper->Copy(pThis, FALSE); // en fait identique à "this" + psuper->SetUniqNum(-3); // valeur spéciale + pStk->AddVar(psuper); + } + // initialise les variables selon paramètres + pt->m_Param->Execute(ppVars, pStk3); // ne peut pas être interrompu + pStk->IncState(); + } + + if ( pStk->GivState() == 1 ) + { + if ( pt->m_bSynchro ) + { + CBotProgram* pProgBase = pStk->GivBotCall(TRUE); + if ( !pClass->Lock(pProgBase) ) return FALSE; // attend de pouvoir + } + pStk->IncState(); + } + // finalement appelle la fonction trouvée + + if ( !pStk3->GivRetVar( // remet le résultat sur la pile + pt->m_Block->Execute(pStk3) )) // GivRetVar dit si c'est interrompu + { + if ( !pStk3->IsOk() ) + { + if ( pt->m_bSynchro ) + { + pClass->Unlock(); // libère la fonction + } + + if ( pt->m_pProg != pProgCurrent ) + { + pStk3->SetPosError(pToken); // indique l'erreur sur l'appel de procédure + } + } + return FALSE; // interrompu ! + } + + if ( pt->m_bSynchro ) + { + pClass->Unlock(); // libère la fonction + } + + return pStack->Return( pStk3 ); + } + return -1; +} + +void CBotFunction::RestoreCall(long& nIdent, const char* name, CBotVar* pThis, CBotVar** ppVars, CBotStack* pStack, CBotClass* pClass) +{ + CBotTypResult type; + CBotFunction* pt = FindLocalOrPublic(nIdent, name, ppVars, type); + + if ( pt != NULL ) + { + CBotStack* pStk = pStack->RestoreStack(pt); + if ( pStk == NULL ) return; + pStk->SetBotCall(pt->m_pProg); // on a peut-être changé de module + + CBotVar* pthis = pStk->FindVar("this"); + pthis->SetUniqNum(-2); + + CBotStack* pStk3 = pStk->RestoreStack(NULL); // pour mettre les paramètres passés + if ( pStk3 == NULL ) return; + + pt->m_Param->RestoreState(pStk3, TRUE); // les paramètres + + if ( pStk->GivState() > 1 && // vérouillage est effectif ? + pt->m_bSynchro ) + { + CBotProgram* pProgBase = pStk->GivBotCall(TRUE); + pClass->Lock(pProgBase); // vérouille la classe + } + + // finalement appelle la fonction trouvée + + pt->m_Block->RestoreState(pStk3, TRUE); // interrompu ! + } +} + +// regarde si la "signature" des paramètres est identique +BOOL CBotFunction::CheckParam(CBotDefParam* pParam) +{ + CBotDefParam* pp = m_Param; + while ( pp != NULL && pParam != NULL ) + { + CBotTypResult type1 = pp->GivType(); + CBotTypResult type2 = pParam->GivType(); + if ( !type1.Compare(type2) ) return FALSE; + pp = pp->GivNext(); + pParam = pParam->GivNext(); + } + return ( pp == NULL && pParam == NULL ); +} + +CBotString CBotFunction::GivName() +{ + return m_token.GivString(); +} + +CBotString CBotFunction::GivParams() +{ + if ( m_Param == NULL ) return CBotString("()"); + + CBotString params = "( "; + CBotDefParam* p = m_Param; // liste des paramètres + + while (p != NULL) + { + params += p->GivParamString(); + p = p->GivNext(); + if ( p != NULL ) params += ", "; + } + + params += " )"; + return params; +} + +CBotFunction* CBotFunction::Next() +{ + return m_next; +} + +void CBotFunction::AddPublic(CBotFunction* func) +{ + if ( m_listPublic != NULL ) + { + func->m_nextpublic = m_listPublic; + m_listPublic->m_prevpublic = func; + } + m_listPublic = func; +} + + + +///////////////////////////////////////////////////////////////////////// +// gestion des paramètres + + +CBotDefParam::CBotDefParam() +{ + m_next = NULL; + m_nIdent = 0; +} + +CBotDefParam::~CBotDefParam() +{ + delete m_next; +} + + +// compile une liste de paramètres +CBotDefParam* CBotDefParam::Compile(CBotToken* &p, CBotCStack* pStack) +{ + // surtout pas de pStack->TokenStack ici + // les variables déclarées doivent rester visibles par la suite + + pStack->SetStartError(p->GivStart()); + + if (IsOfType(p, ID_OPENPAR)) + { + CBotDefParam* list = NULL; + + while (!IsOfType(p, ID_CLOSEPAR)) + { + CBotDefParam* param = new CBotDefParam(); + if (list == NULL) list = param; + else list->AddNext(param); // ajoute à la liste + + CBotClass* pClass = NULL;//= CBotClass::Find(p); + param->m_typename = p->GivString(); + CBotTypResult type = param->m_type = TypeParam(p, pStack); +// if ( type == CBotTypPointer ) type = CBotTypClass; // il faut créer un nouvel objet + + if (param->m_type.GivType() > 0) + { + CBotToken* pp = p; + param->m_token = *p; + if (pStack->IsOk() && IsOfType(p, TokenTypVar) ) + { + + // variable déjà déclarée ? + if (pStack->CheckVarLocal(pp)) + { + pStack->SetError(TX_REDEFVAR, pp); + break; + } + + if ( type.Eq(CBotTypArrayPointer) ) type.SetType(CBotTypArrayBody); + CBotVar* var = CBotVar::Create(pp->GivString(), type); // crée la variable +// if ( pClass ) var->SetClass(pClass); + var->SetInit(2); // la marque initialisée + param->m_nIdent = CBotVar::NextUniqNum(); + var->SetUniqNum(param->m_nIdent); + pStack->AddVar(var); // la place sur la pile + + if (IsOfType(p, ID_COMMA) || p->GivType() == ID_CLOSEPAR) + continue; + } + pStack->SetError(TX_CLOSEPAR, p->GivStart()); + } + pStack->SetError(TX_NOTYP, p); + delete list; + return NULL; + } + return list; + } + pStack->SetError(TX_OPENPAR, p->GivStart()); + return NULL; +} + +void CBotDefParam::AddNext(CBotDefParam* p) +{ + CBotDefParam* pp = this; + while (pp->m_next != NULL) pp = pp->m_next; + + pp->m_next = p; +} + + +BOOL CBotDefParam::Execute(CBotVar** ppVars, CBotStack* &pj) +{ + int i = 0; + CBotDefParam* p = this; + + while ( p != NULL ) + { + // crée une variable locale sur la pile + CBotVar* newvar = CBotVar::Create(p->m_token.GivString(), p->m_type); + + // procède ainsi pour faire la transformation des types : + if ( ppVars != NULL && ppVars[i] != NULL ) + { + switch (p->m_type.GivType()) + { + case CBotTypInt: + newvar->SetValInt(ppVars[i]->GivValInt()); + break; + case CBotTypFloat: + newvar->SetValFloat(ppVars[i]->GivValFloat()); + break; + case CBotTypString: + newvar->SetValString(ppVars[i]->GivValString()); + break; + case CBotTypBoolean: + newvar->SetValInt(ppVars[i]->GivValInt()); + break; + case CBotTypIntrinsic: + ((CBotVarClass*)newvar)->Copy(ppVars[i], FALSE); + break; + case CBotTypPointer: + case CBotTypArrayPointer: + { + newvar->SetPointer(ppVars[i]->GivPointer()); + } + break; + default: + __asm int 3; + } + } + newvar->SetUniqNum(p->m_nIdent); + pj->AddVar(newvar); // place la variable + p = p->m_next; + i++; + } + + return TRUE; +} + +void CBotDefParam::RestoreState(CBotStack* &pj, BOOL bMain) +{ + int i = 0; + CBotDefParam* p = this; + + while ( p != NULL ) + { + // crée une variable locale sur la pile + CBotVar* var = pj->FindVar(p->m_token.GivString()); + var->SetUniqNum(p->m_nIdent); + p = p->m_next; + } +} + +int CBotDefParam::GivType() +{ + return m_type.GivType(); +} + +CBotTypResult CBotDefParam::GivTypResult() +{ + return m_type; +} + +CBotDefParam* CBotDefParam::GivNext() +{ + return m_next; +} + +CBotString CBotDefParam::GivParamString() +{ + CBotString param; + + param = m_typename; + param += ' '; + + param += m_token.GivString(); + return param; +} + + + +////////////////////////////////////////////////////////////////////////// +// retour des paramètres + +CBotReturn::CBotReturn() +{ + m_Instr = NULL; + name = "CBotReturn"; // debug +} + +CBotReturn::~CBotReturn() +{ + delete m_Instr; +} + +CBotInstr* CBotReturn::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotToken* pp = p; + + if (!IsOfType(p, ID_RETURN)) return NULL; // ne devrait jamais arriver + + CBotReturn* inst = new CBotReturn(); // crée l'objet + inst->SetToken( pp ); + + CBotTypResult type = pStack->GivRetType(); + + if ( type.GivType() == 0 ) // retourne void ? + { + if ( IsOfType( p, ID_SEP ) ) return inst; + pStack->SetError( TX_BADTYPE, pp ); + return NULL; + } + + inst->m_Instr = CBotExpression::Compile(p, pStack); + if ( pStack->IsOk() ) + { + CBotTypResult retType = pStack->GivTypResult(2); + if (TypeCompatible(retType, type, ID_ASS)) + { + if ( IsOfType( p, ID_SEP ) ) + return inst; + + pStack->SetError(TX_ENDOF, p->GivStart()); + } + pStack->SetError(TX_BADTYPE, p->GivStart()); + } + + delete inst; + return NULL; // pas d'objet, l'erreur est sur la pile +} + +BOOL CBotReturn::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + + if ( pile->GivState() == 0 ) + { + if ( m_Instr != NULL && !m_Instr->Execute(pile) ) return FALSE; // évalue le résultat + // le résultat est sur la pile + pile->IncState(); + } + + if ( pile->IfStep() ) return FALSE; + + pile->SetBreak(3, CBotString()); + return pj->Return(pile); +} + +void CBotReturn::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + CBotStack* pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + if ( pile->GivState() == 0 ) + { + if ( m_Instr != NULL ) m_Instr->RestoreState(pile, bMain); // évalue le résultat + return; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Les appels à ces fonctions + +CBotInstrCall::CBotInstrCall() +{ + m_Parameters = NULL; + m_nFuncIdent = 0; + name = "CBotInstrCall"; +} + +CBotInstrCall::~CBotInstrCall() +{ + delete m_Parameters; +} + +CBotInstr* CBotInstrCall::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotVar* ppVars[1000]; + + int i = 0; + + CBotToken* pp = p; + p = p->GivNext(); + + pStack->SetStartError(p->GivStart()); + CBotCStack* pile = pStack; + + if ( IsOfType(p, ID_OPENPAR) ) + { + int start, end; + CBotInstrCall* inst = new CBotInstrCall(); + inst->SetToken(pp); + + // compile la liste des paramètres + if (!IsOfType(p, ID_CLOSEPAR)) while (TRUE) + { + start = p->GivStart(); + pile = pile->TokenStack(); // garde les résultats sur la pile + + CBotInstr* param = CBotExpression::Compile(p, pile); + end = p->GivStart(); + if ( inst->m_Parameters == NULL ) inst->m_Parameters = param; + else inst->m_Parameters->AddNext(param); // construit la liste + + if ( !pile->IsOk() ) + { + delete inst; + return pStack->Return(NULL, pile); + } + + if ( param != NULL ) + { + if ( pile->GivTypResult().Eq(99) ) + { + delete pStack->TokenStack(); + pStack->SetError(TX_VOID, p->GivStart()); + delete inst; + return NULL; + } + ppVars[i] = pile->GivVar(); + ppVars[i]->GivToken()->SetPos(start, end); + i++; + + if (IsOfType(p, ID_COMMA)) continue; // saute la virgule + if (IsOfType(p, ID_CLOSEPAR)) break; + } + + pStack->SetError(TX_CLOSEPAR, p->GivStart()); + delete pStack->TokenStack(); + delete inst; + return NULL; + } + ppVars[i] = NULL; + + // la routine est-elle connue ? +// CBotClass* pClass = NULL; + inst->m_typRes = pStack->CompileCall(pp, ppVars, inst->m_nFuncIdent); + if ( inst->m_typRes.GivType() >= 20 ) + { +// if (pVar2!=NULL) pp = pVar2->RetToken(); + pStack->SetError( inst->m_typRes.GivType(), pp ); + delete pStack->TokenStack(); + delete inst; + return NULL; + } + + delete pStack->TokenStack(); + if ( inst->m_typRes.GivType() > 0 ) + { + CBotVar* pRes = CBotVar::Create("", inst->m_typRes); + pStack->SetVar(pRes); // pour connaître le type du résultat + } + else pStack->SetVar(NULL); // routine retourne void + + return inst; + } + p = pp; + delete pStack->TokenStack(); + return NULL; +} + +BOOL CBotInstrCall::Execute(CBotStack* &pj) +{ + CBotVar* ppVars[1000]; + CBotStack* pile = pj->AddStack(this); + if ( pile->StackOver() ) return pj->Return( pile ); + + CBotStack* pile1 = pile; + + int i = 0; + + CBotInstr* p = m_Parameters; + // évalue les paramètres + // et place les valeurs sur la pile + // pour pouvoir être interrompu n'importe quand + if ( p != NULL) while ( TRUE ) + { + pile = pile->AddStack(); // de la place sur la pile pour les résultats + if ( pile->GivState() == 0 ) + { + if (!p->Execute(pile)) return FALSE; // interrompu ici ? + pile->SetState(1); // marque spéciale pour reconnaîre les paramètres + } + ppVars[i++] = pile->GivVar(); + p = p->GivNext(); + if ( p == NULL) break; + } + ppVars[i] = NULL; + + CBotStack* pile2 = pile->AddStack(); + if ( pile2->IfStep() ) return FALSE; + + if ( !pile2->ExecuteCall(m_nFuncIdent, GivToken(), ppVars, m_typRes)) return FALSE; // interrompu + + return pj->Return(pile2); // libère toute la pile +} + +void CBotInstrCall::RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + CBotStack* pile1 = pile; + + int i = 0; + CBotVar* ppVars[1000]; + CBotInstr* p = m_Parameters; + // évalue les paramètres + // et place les valeurs sur la pile + // pour pouvoir être interrompu n'importe quand + if ( p != NULL) while ( TRUE ) + { + pile = pile->RestoreStack(); // de la place sur la pile pour les résultats + if ( pile == NULL ) return; + if ( pile->GivState() == 0 ) + { + p->RestoreState(pile, bMain); // interrompu ici ! + return; + } + ppVars[i++] = pile->GivVar(); // construit la liste des paramètres + p = p->GivNext(); + if ( p == NULL) break; + } + ppVars[i] = NULL; + + CBotStack* pile2 = pile->RestoreStack(); + if ( pile2 == NULL ) return; + + pile2->RestoreCall(m_nFuncIdent, GivToken(), ppVars); +} + +////////////////////////////////////////////////////////////////////////////// +// déclaration des classes par l'utilisateur + +// pré-compile une nouvelle class +// l'analyse est complète à l'execption du corps des routines + +CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack) +{ + if ( !IsOfType(p, ID_PUBLIC) ) + { + pStack->SetError(TX_NOPUBLIC, p); + return NULL; + } + + if ( !IsOfType(p, ID_CLASS) ) return NULL; + + CBotString name = p->GivString(); + + CBotClass* pOld = CBotClass::Find(name); + if ( pOld != NULL && pOld->m_IsDef ) + { + pStack->SetError( TX_REDEFCLASS, p ); + return NULL; + } + + // un nom pour la classe est-il là ? + if (IsOfType(p, TokenTypVar)) + { + CBotClass* pPapa = NULL; +#if EXTENDS + if ( IsOfType( p, ID_EXTENDS ) ) + { + CBotString name = p->GivString(); + pPapa = CBotClass::Find(name); + + if (!IsOfType(p, TokenTypVar) || pPapa == NULL ) + { + pStack->SetError( TX_NOCLASS, p ); + return NULL; + } + } +#endif + CBotClass* classe = (pOld == NULL) ? new CBotClass(name, pPapa) : pOld; + classe->Purge(); // vide les anciennes définitions + classe->m_IsDef = FALSE; // définition en cours + + if ( !IsOfType( p, ID_OPBLK) ) + { + pStack->SetError(TX_OPENBLK, p); + return NULL; + } + + while ( pStack->IsOk() && !IsOfType( p, ID_CLBLK ) ) + { + classe->CompileDefItem(p, pStack, FALSE); + } + + if (pStack->IsOk()) return classe; + } + pStack->SetError(TX_ENDOF, p); + return NULL; +} + +BOOL CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, BOOL bSecond) +{ + BOOL bStatic = FALSE; + int mProtect = PR_PUBLIC; + BOOL bSynchro = FALSE; + + while (IsOfType(p, ID_SEP)) ; + + CBotTypResult type( -1 ); + + if ( IsOfType(p, ID_SYNCHO) ) bSynchro = TRUE; + CBotToken* pBase = p; + + if ( IsOfType(p, ID_STATIC) ) bStatic = TRUE; + if ( IsOfType(p, ID_PUBLIC) ) mProtect = PR_PUBLIC; + if ( IsOfType(p, ID_PRIVATE) ) mProtect = PR_PRIVATE; + if ( IsOfType(p, ID_PROTECTED) ) mProtect = PR_PROTECT; + if ( IsOfType(p, ID_STATIC) ) bStatic = TRUE; + +// CBotClass* pClass = NULL; + type = TypeParam(p, pStack); // type du résultat + + if ( type.Eq(-1) ) + { + pStack->SetError(TX_NOTYP, p); + return FALSE; + } + + while (pStack->IsOk()) + { + CBotToken* pp = p; + IsOfType(p, ID_NOT); // saute le ~ éventuel (destructeur) + + if (IsOfType(p, TokenTypVar)) + { + CBotInstr* limites = NULL; + while ( IsOfType( p, ID_OPBRK ) ) // un tableau ? + { + CBotInstr* i = NULL; + + if ( p->GivType() != ID_CLBRK ) + i = CBotExpression::Compile( p, pStack ); // expression pour la valeur + else + i = new CBotEmpty(); // spécial si pas de formule + + type = CBotTypResult(CBotTypArrayPointer, type); + + if (!pStack->IsOk() || !IsOfType( p, ID_CLBRK ) ) + { + pStack->SetError(TX_CLBRK, p->GivStart()); + return FALSE; + } + +/* CBotVar* pv = pStack->GivVar(); + if ( pv->GivType()>= CBotTypBoolean ) + { + pStack->SetError(TX_BADTYPE, p->GivStart()); + return FALSE; + }*/ + + if (limites == NULL) limites = i; + else limites->AddNext3(i); + } + + if ( p->GivType() == ID_OPENPAR ) + { + if ( !bSecond ) + { + p = pBase; + CBotFunction* f = + CBotFunction::Compile1(p, pStack, this); + + if ( f == NULL ) return FALSE; + + if (m_pMethod == NULL) m_pMethod = f; + else m_pMethod->AddNext(f); + } + else + { + // retrouve la méthode précompilée en passe 1 + CBotFunction* pf = m_pMethod; + CBotFunction* prev = NULL; + while ( pf != NULL ) + { + if (pf->GivName() == pp->GivString()) break; + prev = pf; + pf = pf->Next(); + } + + BOOL bConstructor = (pp->GivString() == GivName()); + CBotCStack* pile = pStack->TokenStack(NULL, TRUE); + + // rend "this" connu + CBotToken TokenThis(CBotString("this"), CBotString()); + CBotVar* pThis = CBotVar::Create(&TokenThis, CBotTypResult( CBotTypClass, this ) ); + pThis->SetUniqNum(-2); + pile->AddVar(pThis); + + if ( m_pParent ) + { + // rend "super" connu + CBotToken TokenSuper(CBotString("super"), CBotString()); + CBotVar* pThis = CBotVar::Create(&TokenSuper, CBotTypResult( CBotTypClass, m_pParent ) ); + pThis->SetUniqNum(-3); + pile->AddVar(pThis); + } + +// int num = 1; + CBotClass* my = this; + while (my != NULL) + { + // place une copie des varibles de la classe (this) sur la pile + CBotVar* pv = my->m_pVar; + while (pv != NULL) + { + CBotVar* pcopy = CBotVar::Create(pv); + pcopy->SetInit(!bConstructor || pv->IsStatic()); + pcopy->SetUniqNum(pv->GivUniqNum()); + pile->AddVar(pcopy); + pv = pv->GivNext(); + } + my = my->m_pParent; + } + + // compile une méthode + p = pBase; + CBotFunction* f = + CBotFunction::Compile(p, pile, NULL/*, FALSE*/); + + if ( f != NULL ) + { + f->m_pProg = pStack->GivBotCall(); + f->m_bSynchro = bSynchro; + // remplace l'élément dans la chaîne + f->m_next = pf->m_next; + pf->m_next = NULL; + delete pf; + if (prev == NULL) m_pMethod = f; + else prev->m_next = f; + } + pStack->Return(NULL, pile); + } + + return pStack->IsOk(); + } + + // définition d'un élément + if (type.Eq(0)) + { + pStack->SetError(TX_ENDOF, p); + return FALSE; + } + + CBotInstr* i = NULL; + if ( IsOfType(p, ID_ASS ) ) + { + if ( type.Eq(CBotTypArrayPointer) ) + { + i = CBotListArray::Compile(p, pStack, type.GivTypElem()); + } + else + { + // il y a une assignation à calculer + i = CBotTwoOpExpr::Compile(p, pStack); + } + if ( !pStack->IsOk() ) return FALSE; + } + + + if ( !bSecond ) + { + CBotVar* pv = CBotVar::Create(pp->GivString(), type); + pv -> SetStatic( bStatic ); + pv -> SetPrivate( mProtect ); + + AddItem( pv ); + + pv->m_InitExpr = i; + pv->m_LimExpr = limites; + + + if ( pv->IsStatic() && pv->m_InitExpr != NULL ) + { + CBotStack* pile = CBotStack::FirstStack(); // une pile indépendante + while(pile->IsOk() && !pv->m_InitExpr->Execute(pile)); // évalue l'expression sans timer + pv->SetVal( pile->GivVar() ) ; + pile->Delete(); + } + } + else + delete i; + + if ( IsOfType(p, ID_COMMA) ) continue; + if ( IsOfType(p, ID_SEP) ) break; + } + pStack->SetError(TX_ENDOF, p); + } + return pStack->IsOk(); +} + + +CBotClass* CBotClass::Compile(CBotToken* &p, CBotCStack* pStack) +{ + if ( !IsOfType(p, ID_PUBLIC) ) return NULL; + if ( !IsOfType(p, ID_CLASS) ) return NULL; + + CBotString name = p->GivString(); + + // un nom pour la classe est-il là ? + if (IsOfType(p, TokenTypVar)) + { + // la classe à été créée par Compile1 + CBotClass* pOld = CBotClass::Find(name); + +#if EXTENDS + if ( IsOfType( p, ID_EXTENDS ) ) + { + IsOfType(p, TokenTypVar); // forcément + } +#endif + IsOfType( p, ID_OPBLK); // forcément + + while ( pStack->IsOk() && !IsOfType( p, ID_CLBLK ) ) + { + pOld->CompileDefItem(p, pStack, TRUE); + } + + pOld->m_IsDef = TRUE; // définition terminée + if (pStack->IsOk()) return pOld; + } + pStack->SetError(TX_ENDOF, p); + return NULL; +} diff --git a/src/CBot/CBotIf.cpp b/src/CBot/CBotIf.cpp new file mode 100644 index 00000000..c0f561a7 --- /dev/null +++ b/src/CBot/CBotIf.cpp @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////// +// instruction if (condition) opération1 else opération2; + +#include "CBot.h" + +// les divers constructeurs / destructeurs +CBotIf::CBotIf() +{ + m_Condition = + m_Block = + m_BlockElse = NULL; // NULL pour pouvoir faire delete directement + name = "CBotIf"; // debug +} + +CBotIf::~CBotIf() +{ + delete m_Condition; // libère la condition + delete m_Block; // libère le bloc d'instruction1 + delete m_BlockElse; // libère le bloc d'instruction2 +} + +// compilation (routine statique) +// appelé lorsque le token "if" a été trouvé + +CBotInstr* CBotIf::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotToken* pp = p; // conserve le ^au token (début instruction) + + if (!IsOfType(p, ID_IF)) return NULL; // ne doit jamais arriver + + CBotCStack* pStk = pStack->TokenStack(pp); // un petit bout de pile svp + + CBotIf* inst = new CBotIf(); // crée l'object + inst->SetToken( pp ); + + if ( NULL != (inst->m_Condition = CBotCondition::Compile( p, pStk )) ) + { + // la condition existe bel et bien + + inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStk, TRUE ); + if ( pStk->IsOk() ) + { + // le bloc d'instruction est ok (peut être vide) + + // regarde si l'instruction suivante est le token "else" + if (IsOfType(p, ID_ELSE)) + { + // si oui, compile le bloc d'instruction qui suit + inst->m_BlockElse = CBotBlock::CompileBlkOrInst( p, pStk, TRUE ); + if (!pStk->IsOk()) + { + // il n'y a pas de bloc correct après le else + // libère l'objet, et transmet l'erreur qui est sur la pile + delete inst; + return pStack->Return(NULL, pStk); + } + } + + // rend l'object correct à qui le demande. + return pStack->Return(inst, pStk); + } + } + + // erreur, libère l'objet + delete inst; + // et transmet l'erreur qui se trouve sur la pile. + return pStack->Return(NULL, pStk); +} + + +// exécution de l'instruction + +BOOL CBotIf :: Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); // ajoute un élément à la pile + // ou le retrouve en cas de reprise +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + + // selon la reprise, on peut être dans l'un des 2 états + if( pile->GivState() == 0 ) + { + // évalue la condition + if ( !m_Condition->Execute(pile) ) return FALSE; // interrompu ici ? + + // termine s'il y a une erreur + if ( !pile->IsOk() ) + { + return pj->Return(pile); // transmet le résultat et libère la pile + } + + // passe dans le second état + if (!pile->SetState(1)) return FALSE; // prêt pour la suite + } + + // second état, évalue les instructions associées + // le résultat de la condition est sur la pile + + if ( pile->GivVal() == TRUE ) // condition était vraie ? + { + if ( m_Block != NULL && // bloc peut être absent + !m_Block->Execute(pile) ) return FALSE; // interrompu ici ? + } + else + { + if ( m_BlockElse != NULL && // s'il existe un bloc alternatif + !m_BlockElse->Execute(pile) ) return FALSE; // interrompu ici + } + + // transmet le résultat et libère la pile + return pj->Return(pile); +} + + +void CBotIf :: RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile = pj->RestoreStack(this); // ajoute un élément à la pile + if ( pile == NULL ) return; + + // selon la reprise, on peut être dans l'un des 2 états + if( pile->GivState() == 0 ) + { + // évalue la condition + m_Condition->RestoreState(pile, bMain); // interrompu ici ! + return; + } + + // second état, évalue les instructions associées + // le résultat de la condition est sur la pile + + if ( pile->GivVal() == TRUE ) // condition était vraie ? + { + if ( m_Block != NULL ) // bloc peut être absent + m_Block->RestoreState(pile, bMain); // interrompu ici ! + } + else + { + if ( m_BlockElse != NULL ) // s'il existe un bloc alternatif + m_BlockElse->RestoreState(pile, bMain); // interrompu ici ! + } +} + diff --git a/src/CBot/CBotProgram.cpp b/src/CBot/CBotProgram.cpp new file mode 100644 index 00000000..ba14f899 --- /dev/null +++ b/src/CBot/CBotProgram.cpp @@ -0,0 +1,1102 @@ +////////////////////////////////////////////////////////////////////// +// gestion de base d'un programme CBot + +#include "CBot.h" +#include + +CBotProgram::CBotProgram() +{ + m_Prog = NULL; + m_pRun = NULL; + m_pClass = NULL; + m_pStack = NULL; + m_pInstance = NULL; + + m_ErrorCode = 0; + m_Ident = 0; + m_bDebugDD = 0; +} + +CBotProgram::CBotProgram(CBotVar* pInstance) +{ + m_Prog = NULL; + m_pRun = NULL; + m_pClass = NULL; + m_pStack = NULL; + m_pInstance = pInstance; + + m_ErrorCode = 0; + m_Ident = 0; + m_bDebugDD = 0; +} + + +CBotProgram::~CBotProgram() +{ +// delete m_pClass; + m_pClass->Purge(); + m_pClass = NULL; + + CBotClass::FreeLock(this); + + delete m_Prog; +#if STACKMEM + m_pStack->Delete(); +#else + delete m_pStack; +#endif +} + + +BOOL CBotProgram::Compile( const char* program, CBotStringArray& ListFonctions, void* pUser ) +{ + int error = 0; + Stop(); + +// delete m_pClass; + m_pClass->Purge(); // purge les anciennes définitions des classes + // mais sans détruire l'object + m_pClass = NULL; + delete m_Prog; m_Prog= NULL; + + ListFonctions.SetSize(0); + m_ErrorCode = 0; + + if (m_pInstance != NULL && m_pInstance->m_pUserPtr != NULL) + pUser = m_pInstance->m_pUserPtr; + + // transforme le programme en Tokens + CBotToken* pBaseToken = CBotToken::CompileTokens(program, error); + if ( pBaseToken == NULL ) return FALSE; + + + CBotCStack* pStack = new CBotCStack(NULL); + CBotToken* p = pBaseToken->GivNext(); // saute le 1er token (séparateur) + + pStack->SetBotCall(this); // défini les routines utilisables + CBotCall::SetPUser(pUser); + + // fait une première passe rapide juste pour prendre les entêtes de routines et de classes + while ( pStack->IsOk() && p != NULL && p->GivType() != 0) + { + if ( IsOfType(p, ID_SEP) ) continue; // des point-virgules qui trainent + + if ( p->GivType() == ID_CLASS || + ( p->GivType() == ID_PUBLIC && p->GivNext()->GivType() == ID_CLASS )) + { + CBotClass* nxt = CBotClass::Compile1(p, pStack); + if (m_pClass == NULL ) m_pClass = nxt; + else m_pClass->AddNext(nxt); + } + else + { + CBotFunction* next = CBotFunction::Compile1(p, pStack, NULL); + if (m_Prog == NULL ) m_Prog = next; + else m_Prog->AddNext(next); + } + } + if ( !pStack->IsOk() ) + { + m_ErrorCode = pStack->GivError(m_ErrorStart, m_ErrorEnd); + delete m_Prog; + m_Prog = NULL; + delete pBaseToken; + return FALSE; + } + +// CBotFunction* temp = NULL; + CBotFunction* next = m_Prog; // reprend la liste + + p = pBaseToken->GivNext(); // revient au début + + while ( pStack->IsOk() && p != NULL && p->GivType() != 0 ) + { + if ( IsOfType(p, ID_SEP) ) continue; // des point-virgules qui trainent + + if ( p->GivType() == ID_CLASS || + ( p->GivType() == ID_PUBLIC && p->GivNext()->GivType() == ID_CLASS )) + { + m_bCompileClass = TRUE; + CBotClass::Compile(p, pStack); // complète la définition de la classe + } + else + { + m_bCompileClass = FALSE; + CBotFunction::Compile(p, pStack, next); + if (next->IsExtern()) ListFonctions.Add(next->GivName()/* + next->GivParams()*/); + next->m_pProg = this; // garde le pointeur au module + next = next->Next(); + } + } + +// delete m_Prog; // la liste de la 1ère passe +// m_Prog = temp; // la liste de la seconde passe + + if ( !pStack->IsOk() ) + { + m_ErrorCode = pStack->GivError(m_ErrorStart, m_ErrorEnd); + delete m_Prog; + m_Prog = NULL; + } + + delete pBaseToken; + delete pStack; + + return (m_Prog != NULL); +} + + +BOOL CBotProgram::Start(const char* name) +{ +#if STACKMEM + m_pStack->Delete(); +#else + delete m_pStack; +#endif + m_pStack = NULL; + + m_pRun = m_Prog; + while (m_pRun != NULL) + { + if ( m_pRun->GivName() == name ) break; + m_pRun = m_pRun->m_next; + } + + if ( m_pRun == NULL ) + { + m_ErrorCode = TX_NORUN; + return FALSE; + } + +#if STACKMEM + m_pStack = CBotStack::FirstStack(); +#else + m_pStack = new CBotStack(NULL); // crée une pile d'exécution +#endif + + m_pStack->SetBotCall(this); // bases pour les routines + + return TRUE; // on est prêt pour un Run() +} + +BOOL CBotProgram::GetPosition(const char* name, int& start, int& stop, CBotGet modestart, CBotGet modestop) +{ + CBotFunction* p = m_Prog; + while (p != NULL) + { + if ( p->GivName() == name ) break; + p = p->m_next; + } + + if ( p == NULL ) return FALSE; + + p->GetPosition(start, stop, modestart, modestop); + return TRUE; +} + +BOOL CBotProgram::Run(void* pUser, int timer) +{ + BOOL ok; + + if (m_pStack == NULL || m_pRun == NULL) goto error; + + m_ErrorCode = 0; + if (m_pInstance != NULL && m_pInstance->m_pUserPtr != NULL) + pUser = m_pInstance->m_pUserPtr; + + m_pStack->Reset(pUser); // vide l'éventuelle erreur précédente, et remet le timer + if ( timer >= 0 ) m_pStack->SetTimer(timer); + + m_pStack->SetBotCall(this); // bases pour les routines + +#if STACKRUN + // reprend l'exécution sur le haut de la pile + ok = m_pStack->Execute(); + if ( ok ) + { +#ifdef _DEBUG + CBotVar* ppVar[3]; + ppVar[0] = CBotVar::Create("aa", CBotTypInt); + ppVar[1] = CBotVar::Create("bb", CBotTypInt); + ppVar[2] = NULL; + ok = m_pRun->Execute(ppVar, m_pStack, m_pInstance); +#else + // revient sur l'exécution normale + ok = m_pRun->Execute(NULL, m_pStack, m_pInstance); +#endif + } +#else + ok = m_pRun->Execute(NULL, m_pStack, m_pInstance); +#endif + + // terminé sur une erreur ? + if (!ok && !m_pStack->IsOk()) + { + m_ErrorCode = m_pStack->GivError(m_ErrorStart, m_ErrorEnd); +#if STACKMEM + m_pStack->Delete(); +#else + delete m_pStack; +#endif + m_pStack = NULL; + return TRUE; // exécution terminée !! + } + + if ( ok ) m_pRun = NULL; // plus de fonction en exécution + return ok; + +error: + m_ErrorCode = TX_NORUN; + return TRUE; +} + +void CBotProgram::Stop() +{ +#if STACKMEM + m_pStack->Delete(); +#else + delete m_pStack; +#endif + m_pStack = NULL; + m_pRun = NULL; +} + + + +BOOL CBotProgram::GetRunPos(const char* &FunctionName, int &start, int &end) +{ + FunctionName = NULL; + start = end = 0; + if (m_pStack == NULL) return FALSE; + + m_pStack->GetRunPos(FunctionName, start, end); + return TRUE; +} + +CBotVar* CBotProgram::GivStackVars(const char* &FunctionName, int level) +{ + FunctionName = NULL; + if (m_pStack == NULL) return NULL; + + return m_pStack->GivStackVars(FunctionName, level); +} + + + + + + + +void CBotProgram::SetTimer(int n) +{ + CBotStack::SetTimer( n ); +} + +int CBotProgram::GivError() +{ + return m_ErrorCode; +} + +void CBotProgram::SetIdent(long n) +{ + m_Ident = n; +} + +long CBotProgram::GivIdent() +{ + return m_Ident; +} + +BOOL CBotProgram::GetError(int& code, int& start, int& end) +{ + code = m_ErrorCode; + start = m_ErrorStart; + end = m_ErrorEnd; + return code > 0; +} + +BOOL CBotProgram::GetError(int& code, int& start, int& end, CBotProgram* &pProg) +{ + code = m_ErrorCode; + start = m_ErrorStart; + end = m_ErrorEnd; + pProg = this; + return code > 0; +} + +CBotString CBotProgram::GivErrorText(int code) +{ + CBotString TextError; + + TextError.LoadString( code ); + if (TextError.IsEmpty()) + { + char buf[100]; + sprintf(buf, "Exception numéro %d.", code); + TextError = buf; + } + return TextError; +} + + +CBotFunction* CBotProgram::GivFunctions() +{ + return m_Prog; +} + +BOOL CBotProgram::AddFunction(const char* name, + BOOL rExec (CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser), + CBotTypResult rCompile (CBotVar* &pVar, void* pUser)) +{ + // mémorise les pointeurs aux deux fonctions + return CBotCall::AddFunction(name, rExec, rCompile); +} + + +BOOL WriteWord(FILE* pf, WORD w) +{ + size_t lg; + + lg = fwrite(&w, sizeof( WORD ), 1, pf ); + + return (lg == 1); +} + +BOOL ReadWord(FILE* pf, WORD& w) +{ + size_t lg; + + lg = fread(&w, sizeof( WORD ), 1, pf ); + + return (lg == 1); +} + +BOOL WriteFloat(FILE* pf, float w) +{ + size_t lg; + + lg = fwrite(&w, sizeof( float ), 1, pf ); + + return (lg == 1); +} + +BOOL ReadFloat(FILE* pf, float& w) +{ + size_t lg; + + lg = fread(&w, sizeof( float ), 1, pf ); + + return (lg == 1); +} + +BOOL WriteLong(FILE* pf, long w) +{ + size_t lg; + + lg = fwrite(&w, sizeof( long ), 1, pf ); + + return (lg == 1); +} + +BOOL ReadLong(FILE* pf, long& w) +{ + size_t lg; + + lg = fread(&w, sizeof( long ), 1, pf ); + + return (lg == 1); +} + +BOOL WriteString(FILE* pf, CBotString s) +{ + size_t lg1, lg2; + + lg1 = s.GivLength(); + if (!WriteWord(pf, lg1)) return FALSE; + + lg2 = fwrite(s, 1, lg1, pf ); + return (lg1 == lg2); +} + +BOOL ReadString(FILE* pf, CBotString& s) +{ + WORD w; + char buf[1000]; + size_t lg1, lg2; + + if (!ReadWord(pf, w)) return FALSE; + lg1 = w; + lg2 = fread(buf, 1, lg1, pf ); + buf[lg2] = 0; + + s = buf; + return (lg1 == lg2); +} + +BOOL WriteType(FILE* pf, CBotTypResult type) +{ + int typ = type.GivType(); + if ( typ == CBotTypIntrinsic ) typ = CBotTypClass; + if ( !WriteWord(pf, typ) ) return FALSE; + if ( typ == CBotTypClass ) + { + CBotClass* p = type.GivClass(); + if ( !WriteString(pf, p->GivName()) ) return FALSE; + } + if ( type.Eq( CBotTypArrayBody ) || + type.Eq( CBotTypArrayPointer ) ) + { + if ( !WriteWord(pf, type.GivLimite()) ) return FALSE; + if ( !WriteType(pf, type.GivTypElem()) ) return FALSE; + } + return TRUE; +} + +BOOL ReadType(FILE* pf, CBotTypResult& type) +{ + WORD w, ww; + if ( !ReadWord(pf, w) ) return FALSE; + type.SetType(w); + + if ( type.Eq( CBotTypIntrinsic ) ) + { + type = CBotTypResult( w, "point" ); + } + + if ( type.Eq( CBotTypClass ) ) + { + CBotString s; + if ( !ReadString(pf, s) ) return FALSE; + type = CBotTypResult( w, s ); + } + + if ( type.Eq( CBotTypArrayPointer ) || + type.Eq( CBotTypArrayBody ) ) + { + CBotTypResult r; + if ( !ReadWord(pf, ww) ) return FALSE; + if ( !ReadType(pf, r) ) return FALSE; + type = CBotTypResult( w, r ); + type.SetLimite((short)ww); + } + return TRUE; +} + + +BOOL CBotProgram::DefineNum(const char* name, long val) +{ + return CBotToken::DefineNum(name, val); +} + + +BOOL CBotProgram::SaveState(FILE* pf) +{ + if (!WriteWord( pf, CBOTVERSION)) return FALSE; + + + if ( m_pStack != NULL ) + { + if (!WriteWord( pf, 1)) return FALSE; + if (!WriteString( pf, m_pRun->GivName() )) return FALSE; + if (!m_pStack->SaveState(pf)) return FALSE; + } + else + { + if (!WriteWord( pf, 0)) return FALSE; + } + return TRUE; +} + + +BOOL CBotProgram::RestoreState(FILE* pf) +{ + WORD w; + CBotString s; + + Stop(); + + if (!ReadWord( pf, w )) return FALSE; + if ( w != CBOTVERSION ) return FALSE; + + if (!ReadWord( pf, w )) return FALSE; + if ( w == 0 ) return TRUE; + + if (!ReadString( pf, s )) return FALSE; + Start(s); // point de reprise + +#if STACKMEM + m_pStack->Delete(); +#else + delete m_pStack; +#endif + m_pStack = NULL; + + // récupère la pile depuis l'enregistrement + // utilise un pointeur NULL (m_pStack) mais c'est ok comme ça + if (!m_pStack->RestoreState(pf, m_pStack)) return FALSE; + m_pStack->SetBotCall(this); // bases pour les routines + + // rétabli certains états dans la pile selon la structure + m_pRun->RestoreState(NULL, m_pStack, m_pInstance); + return TRUE; +} + +int CBotProgram::GivVersion() +{ + return CBOTVERSION; +} + + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +CBotCall* CBotCall::m_ListCalls = NULL; + +CBotCall::CBotCall(const char* name, + BOOL rExec (CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser), + CBotTypResult rCompile (CBotVar* &pVar, void* pUser)) +{ + m_name = name; + m_rExec = rExec; + m_rComp = rCompile; + m_next = NULL; + m_nFuncIdent = CBotVar::NextUniqNum(); +} + +CBotCall::~CBotCall() +{ + if (m_next) delete m_next; + m_next = NULL; +} + +void CBotCall::Free() +{ + delete CBotCall::m_ListCalls; +} + +BOOL CBotCall::AddFunction(const char* name, + BOOL rExec (CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser), + CBotTypResult rCompile (CBotVar* &pVar, void* pUser)) +{ + CBotCall* p = m_ListCalls; + CBotCall* pp = NULL; + + if ( p != NULL ) while ( p->m_next != NULL ) + { + if ( p->GivName() == name ) + { + // libère une fonction qu'on redéfini + if ( pp ) pp->m_next = p->m_next; + else m_ListCalls = p->m_next; + pp = p; + p = p->m_next; + pp->m_next = NULL; // ne pas détruire la suite de la liste + delete pp; + continue; + } + pp = p; // pointeur précédent + p = p->m_next; + } + + pp = new CBotCall(name, rExec, rCompile); + + if (p) p->m_next = pp; + else m_ListCalls = pp; + + return TRUE; +} + + +// transforme le tableau de pointeurs aux variables +// en une liste de variables chaînées +CBotVar* MakeListVars(CBotVar** ppVars, BOOL bSetVal=FALSE) +{ + int i = 0; + CBotVar* pVar = NULL; + + while( TRUE ) + { + ppVars[i]; + if ( ppVars[i] == NULL ) break; + + CBotVar* pp = CBotVar::Create(ppVars[i]); + if (bSetVal) pp->Copy(ppVars[i]); + else + if ( ppVars[i]->GivType() == CBotTypPointer ) + pp->SetClass( ppVars[i]->GivClass()); +// copier le pointeur selon indirection + if (pVar == NULL) pVar = pp; + else pVar->AddNext(pp); + i++; + } + return pVar; +} + +// trouve un appel acceptable selon le nom de la procédure +// et les paramètres donnés + +CBotTypResult CBotCall::CompileCall(CBotToken* &p, CBotVar** ppVar, CBotCStack* pStack, long& nIdent) +{ + nIdent = 0; + CBotCall* pt = m_ListCalls; + CBotString name = p->GivString(); + + while ( pt != NULL ) + { + if ( pt->m_name == name ) + { + CBotVar* pVar = MakeListVars(ppVar); + CBotVar* pVar2 = pVar; + CBotTypResult r = pt->m_rComp(pVar2, m_pUser); + int ret = r.GivType(); + + // si une classe est retournée, c'est en fait un pointeur + if ( ret == CBotTypClass ) r.SetType( ret = CBotTypPointer ); + + if ( ret > 20 ) + { + if (pVar2) pStack->SetError(ret, p /*pVar2->GivToken()*/ ); + } + delete pVar; + nIdent = pt->m_nFuncIdent; + return r; + } + pt = pt->m_next; + } + return -1; +} + +void* CBotCall::m_pUser = NULL; + +void CBotCall::SetPUser(void* pUser) +{ + m_pUser = pUser; +} + +int CBotCall::CheckCall(const char* name) +{ + CBotCall* p = m_ListCalls; + + while ( p != NULL ) + { + if ( name == p->GivName() ) return TRUE; + p = p->m_next; + } + return FALSE; +} + + + +CBotString CBotCall::GivName() +{ + return m_name; +} + +CBotCall* CBotCall::Next() +{ + return m_next; +} + + +int CBotCall::DoCall(long& nIdent, CBotToken* token, CBotVar** ppVar, CBotStack* pStack, CBotTypResult& rettype) +{ + CBotCall* pt = m_ListCalls; + + if ( nIdent ) while ( pt != NULL ) + { + if ( pt->m_nFuncIdent == nIdent ) + { + goto fund; + } + pt = pt->m_next; + } + + pt = m_ListCalls; + + if ( token != NULL ) + { + CBotString name = token->GivString(); + while ( pt != NULL ) + { + if ( pt->m_name == name ) + { + nIdent = pt->m_nFuncIdent; + goto fund; + } + pt = pt->m_next; + } + } + + return -1; + +fund: +#if !STACKRUN + // fait la liste des paramètres selon le contenu de la pile (pStackVar) + + CBotVar* pVar = MakeListVars(ppVar, TRUE); + CBotVar* pVarToDelete = pVar; + + // crée une variable pour le résultat + CBotVar* pResult = rettype.Eq(0) ? NULL : CBotVar::Create("", rettype); + + CBotVar* pRes = pResult; + int Exception = 0; + int res = pt->m_rExec(pVar, pResult, Exception, pStack->GivPUser()); + + if ( pResult != pRes ) delete pRes; // si résultat différent rendu + delete pVarToDelete; + + if (res == FALSE) + { + if (Exception!=0) + { + pStack->SetError(Exception, token); + } + delete pResult; + return FALSE; + } + pStack->SetVar(pResult); + + if ( rettype.GivType() > 0 && pResult == NULL ) + { + pStack->SetError(TX_NORETVAL, token); + } + nIdent = pt->m_nFuncIdent; + return TRUE; + +#else + + CBotStack* pile = pStack->AddStackEOX(pt); + if ( pile == EOX ) return TRUE; + + // fait la liste des paramètres selon le contenu de la pile (pStackVar) + + CBotVar* pVar = MakeListVars(ppVar, TRUE); + CBotVar* pVarToDelete = pVar; + + // crée une variable pour le résultat + CBotVar* pResult = rettype.Eq(0) ? NULL : CBotVar::Create("", rettype); + + pile->SetVar( pVar ); + + CBotStack* pile2 = pile->AddStack(); + pile2->SetVar( pResult ); + + pile->SetError(0, token); // pour la position en cas d'erreur + loin + return pt->Run( pStack ); + +#endif + +} + +#if STACKRUN + +BOOL CBotCall::RestoreCall(long& nIdent, CBotToken* token, CBotVar** ppVar, CBotStack* pStack) +{ + CBotCall* pt = m_ListCalls; + + { + CBotString name = token->GivString(); + while ( pt != NULL ) + { + if ( pt->m_name == name ) + { + nIdent = pt->m_nFuncIdent; + + CBotStack* pile = pStack->RestoreStackEOX(pt); + if ( pile == NULL ) return TRUE; + + CBotStack* pile2 = pile->RestoreStack(); + return TRUE; + } + pt = pt->m_next; + } + } + + return FALSE; +} + +BOOL CBotCall::Run(CBotStack* pStack) +{ + CBotStack* pile = pStack->AddStackEOX(this); + if ( pile == EOX ) return TRUE; + CBotVar* pVar = pile->GivVar(); + + CBotStack* pile2 = pile->AddStack(); + CBotVar* pResult = pile2->GivVar(); + CBotVar* pRes = pResult; + + int Exception = 0; + int res = m_rExec(pVar, pResult, Exception, pStack->GivPUser()); + + if (res == FALSE) + { + if (Exception!=0) + { + pStack->SetError(Exception); + } + if ( pResult != pRes ) delete pResult; // si résultat différent rendu + return FALSE; + } + + if ( pResult != NULL ) pStack->SetCopyVar( pResult ); + if ( pResult != pRes ) delete pResult; // si résultat différent rendu + + return TRUE; +} + +#endif + +/////////////////////////////////////////////////////////////////////////////////////// + +CBotCallMethode::CBotCallMethode(const char* name, + BOOL rExec (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception), + CBotTypResult rCompile (CBotVar* pThis, CBotVar* &pVar)) +{ + m_name = name; + m_rExec = rExec; + m_rComp = rCompile; + m_next = NULL; + m_nFuncIdent = CBotVar::NextUniqNum(); +} + +CBotCallMethode::~CBotCallMethode() +{ + delete m_next; + m_next = NULL; +} + +// trouve un appel acceptable selon le nom de la procédure +// et les paramètres donnés + +CBotTypResult CBotCallMethode::CompileCall(const char* name, CBotVar* pThis, + CBotVar** ppVar, CBotCStack* pStack, + long& nIdent) +{ + CBotCallMethode* pt = this; + nIdent = 0; + + while ( pt != NULL ) + { + if ( pt->m_name == name ) + { + CBotVar* pVar = MakeListVars(ppVar, TRUE); + CBotVar* pVar2 = pVar; + CBotTypResult r = pt->m_rComp(pThis, pVar2); + int ret = r.GivType(); + if ( ret > 20 ) + { + if (pVar2) pStack->SetError(ret, pVar2->GivToken()); + } + delete pVar; + nIdent = pt->m_nFuncIdent; + return r; + } + pt = pt->m_next; + } + return CBotTypResult(-1); +} + + +CBotString CBotCallMethode::GivName() +{ + return m_name; +} + +CBotCallMethode* CBotCallMethode::Next() +{ + return m_next; +} + +void CBotCallMethode::AddNext(CBotCallMethode* pt) +{ + CBotCallMethode* p = this; + while ( p->m_next != NULL ) p = p->m_next; + + p->m_next = pt; +} + + +int CBotCallMethode::DoCall(long& nIdent, const char* name, CBotVar* pThis, CBotVar** ppVars, CBotVar* &pResult, CBotStack* pStack, CBotToken* pToken) +{ + CBotCallMethode* pt = this; + + // recherche selon l'identificateur + + if ( nIdent ) while ( pt != NULL ) + { + if ( pt->m_nFuncIdent == nIdent ) + { + // fait la liste des paramètres selon le contenu de la pile (pStackVar) + + CBotVar* pVar = MakeListVars(ppVars, TRUE); + CBotVar* pVarToDelete = pVar; + + // puis appelle la routine externe au module + + int Exception = 0; + int res = pt->m_rExec(pThis, pVar, pResult, Exception); + pStack->SetVar(pResult); + + if (res == FALSE) + { + if (Exception!=0) + { +// pStack->SetError(Exception, pVar->GivToken()); + pStack->SetError(Exception, pToken); + } + delete pVarToDelete; + return FALSE; + } + delete pVarToDelete; + return TRUE; + } + pt = pt->m_next; + } + + // recherche selon le nom + + while ( pt != NULL ) + { + if ( pt->m_name == name ) + { + // fait la liste des paramètres selon le contenu de la pile (pStackVar) + + CBotVar* pVar = MakeListVars(ppVars, TRUE); + CBotVar* pVarToDelete = pVar; + + int Exception = 0; + int res = pt->m_rExec(pThis, pVar, pResult, Exception); + pStack->SetVar(pResult); + + if (res == FALSE) + { + if (Exception!=0) + { +// pStack->SetError(Exception, pVar->GivToken()); + pStack->SetError(Exception, pToken); + } + delete pVarToDelete; + return FALSE; + } + delete pVarToDelete; + nIdent = pt->m_nFuncIdent; + return TRUE; + } + pt = pt->m_next; + } + + return -1; +} + +BOOL rSizeOf( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + if ( pVar == NULL ) return TX_LOWPARAM; + + int i = 0; + pVar = pVar->GivItemList(); + + while ( pVar != NULL ) + { + i++; + pVar = pVar->GivNext(); + } + + pResult->SetValInt(i); + return TRUE; +} + +CBotTypResult cSizeOf( CBotVar* &pVar, void* pUser ) +{ + if ( pVar == NULL ) return CBotTypResult( TX_LOWPARAM ); + if ( pVar->GivType() != CBotTypArrayPointer ) + return CBotTypResult( TX_BADPARAM ); + return CBotTypResult( CBotTypInt ); +} + + +CBotString CBotProgram::m_DebugVarStr = ""; + +BOOL rCBotDebug( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + pResult->SetValString( CBotProgram::m_DebugVarStr ); + + return TRUE; +} + +CBotTypResult cCBotDebug( CBotVar* &pVar, void* pUser ) +{ + // pas de paramètre + if ( pVar != NULL ) return CBotTypResult( TX_OVERPARAM ); + + // la fonction retourne un résultat "string" + return CBotTypResult( CBotTypString ); +} + + +#include "StringFunctions.cpp" + +void CBotProgram::Init() +{ + CBotToken::DefineNum( "CBotErrOpenPar", 5000) ; // manque la parenthèse ouvrante + CBotToken::DefineNum( "CBotErrClosePar", 5001) ; // manque la parenthèse fermante + CBotToken::DefineNum( "CBotErrNotBoolean", 5002) ; // l'expression doit être un boolean + CBotToken::DefineNum( "CBotErrUndefVar", 5003) ; // variable non déclarée + CBotToken::DefineNum( "CBotErrBadLeft", 5004) ; // assignation impossible ( 5 = ... ) + CBotToken::DefineNum( "CBotErrNoTerminator", 5005) ;// point-virgule attendu + CBotToken::DefineNum( "CBotErrCaseOut", 5006) ; // case en dehors d'un switch + CBotToken::DefineNum( "CBotErrCloseBlock", 5008) ; // manque " } " + CBotToken::DefineNum( "CBotErrElseWhitoutIf", 5009) ;// else sans if correspondant + CBotToken::DefineNum( "CBotErrOpenBlock", 5010) ; // manque " { " + CBotToken::DefineNum( "CBotErrBadType1", 5011) ; // mauvais type pour l'assignation + CBotToken::DefineNum( "CBotErrRedefVar", 5012) ; // redéfinition de la variable + CBotToken::DefineNum( "CBotErrBadType2", 5013) ; // 2 opérandes de type incompatibles + CBotToken::DefineNum( "CBotErrUndefCall", 5014) ; // routine inconnue + CBotToken::DefineNum( "CBotErrNoDoubleDots", 5015) ;// " : " attendu + CBotToken::DefineNum( "CBotErrBreakOutside", 5017) ;// break en dehors d'une boucle + CBotToken::DefineNum( "CBotErrUndefLabel", 5019) ; // label inconnu + CBotToken::DefineNum( "CBotErrLabel", 5018) ; // label ne peut se mettre ici + CBotToken::DefineNum( "CBotErrNoCase", 5020) ; // manque " case " + CBotToken::DefineNum( "CBotErrBadNum", 5021) ; // nombre attendu + CBotToken::DefineNum( "CBotErrVoid", 5022) ; // " void " pas possible ici + CBotToken::DefineNum( "CBotErrNoType", 5023) ; // déclaration de type attendue + CBotToken::DefineNum( "CBotErrNoVar", 5024) ; // nom de variable attendu + CBotToken::DefineNum( "CBotErrNoFunc", 5025) ; // nom de fonction attendu + CBotToken::DefineNum( "CBotErrOverParam", 5026) ; // trop de paramètres + CBotToken::DefineNum( "CBotErrRedefFunc", 5027) ; // cette fonction existe déjà + CBotToken::DefineNum( "CBotErrLowParam", 5028) ; // pas assez de paramètres + CBotToken::DefineNum( "CBotErrBadParam", 5029) ; // mauvais types de paramètres + CBotToken::DefineNum( "CBotErrNbParam", 5030) ; // mauvais nombre de paramètres + CBotToken::DefineNum( "CBotErrUndefItem", 5031) ; // élément n'existe pas dans la classe + CBotToken::DefineNum( "CBotErrUndefClass", 5032) ; // variable n'est pas une classe + CBotToken::DefineNum( "CBotErrNoConstruct", 5033) ; // pas de constructeur approprié + CBotToken::DefineNum( "CBotErrRedefClass", 5034) ; // classe existe déjà + CBotToken::DefineNum( "CBotErrCloseIndex", 5035) ; // " ] " attendu + CBotToken::DefineNum( "CBotErrReserved", 5036) ; // mot réservé (par un DefineNum) + +// voici la liste des erreurs pouvant être retournées par le module +// pour l'exécution + + CBotToken::DefineNum( "CBotErrZeroDiv", 6000) ; // division par zéro + CBotToken::DefineNum( "CBotErrNotInit", 6001) ; // variable non initialisée + CBotToken::DefineNum( "CBotErrBadThrow", 6002) ; // throw d'une valeur négative + CBotToken::DefineNum( "CBotErrNoRetVal", 6003) ; // fonction n'a pas retourné de résultat + CBotToken::DefineNum( "CBotErrNoRun", 6004) ; // Run() sans fonction active + CBotToken::DefineNum( "CBotErrUndefFunc", 6005) ; // appel d'une fonction qui n'existe plus + + CBotProgram::AddFunction("sizeof", rSizeOf, cSizeOf ); + + InitStringFunctions(); + + // une fonction juste pour les debug divers + CBotProgram::AddFunction("CBOTDEBUGDD", rCBotDebug, cCBotDebug); + DeleteFile("CbotDebug.txt"); + +} + +void CBotProgram::Free() +{ + CBotToken::Free() ; + CBotCall ::Free() ; + CBotClass::Free() ; +} + diff --git a/src/CBot/CBotStack.cpp b/src/CBot/CBotStack.cpp new file mode 100644 index 00000000..c313a8b6 --- /dev/null +++ b/src/CBot/CBotStack.cpp @@ -0,0 +1,1460 @@ +////////////////////////////////////////////////////////////////////// +// gestion de la pile (stack) + +#include "CBot.h" + + +#define ITIMER 100 + +//////////////////////////////////////////////////////////////////////////// +// gestion de la pile d'exécution +//////////////////////////////////////////////////////////////////////////// + +int CBotStack::m_initimer = ITIMER; // init la variable statique +int CBotStack::m_timer = 0; // init la variable statique +CBotVar* CBotStack::m_retvar = NULL; // init la variable statique +int CBotStack::m_error = 0; // init la variable statique +int CBotStack::m_start = 0; // init la variable statique +int CBotStack::m_end = 0; // init la variable statique +CBotString CBotStack::m_labelBreak=""; // init la variable statique +void* CBotStack::m_pUser = NULL; + +#if STACKMEM + +CBotStack* CBotStack::FirstStack() +{ + CBotStack* p; + + long size = sizeof(CBotStack); + size *= (MAXSTACK+10); + + // demande une tranche mémoire pour la pile + p = (CBotStack*)malloc(size); + + // la vide totalement + memset(p, 0, size); + + p-> m_bBlock = TRUE; + m_timer = m_initimer; // met le timer au début + + CBotStack* pp = p; + pp += MAXSTACK; + for ( int i = 0 ; i< 10 ; i++ ) + { + pp->m_bOver = TRUE; + pp ++; + } +#ifdef _DEBUG + int n = 1; + pp = p; + for ( i = 0 ; i< MAXSTACK+10 ; i++ ) + { + pp->m_index = n++; + pp ++; + } +#endif + + m_error = 0; // évite des blocages car m_error est static + return p; +} + +CBotStack::CBotStack(CBotStack* ppapa) +{ + // constructeur doit exister, sinon le destructeur n'est jamais appelé ! + __asm int 3; +} + +CBotStack::~CBotStack() +{ + __asm int 3; // utiliser Delete() à la place +} + +void CBotStack::Delete() +{ + if ( this == NULL || this == EOX ) return; + + m_next->Delete(); + m_next2->Delete(); + + if (m_prev != NULL) + { + if ( m_prev->m_next == this ) + m_prev->m_next = NULL; // enlève de la chaîne + + if ( m_prev->m_next2 == this ) + m_prev->m_next2 = NULL; // enlève de la chaîne + } + + delete m_var; + delete m_listVar; + + CBotStack* p = m_prev; + BOOL bOver = m_bOver; +#ifdef _DEBUG + int n = m_index; +#endif + + // efface le bloc libéré + memset(this, 0, sizeof(CBotStack)); + m_bOver = bOver; +#ifdef _DEBUG + m_index = n; +#endif + + if ( p == NULL ) + free( this ); +} + + +// routine optimisée +CBotStack* CBotStack::AddStack(CBotInstr* instr, BOOL bBlock) +{ + if (m_next != NULL) + { + return m_next; // reprise dans une pile existante + } + +#ifdef _DEBUG + int n = 0; +#endif + CBotStack* p = this; + do + { + p ++; +#ifdef _DEBUG + n ++; +#endif + } + while ( p->m_prev != NULL ); + + m_next = p; // chaîne l'élément + p->m_bBlock = bBlock; + p->m_instr = instr; + p->m_prog = m_prog; + p->m_step = 0; + p->m_prev = this; + p->m_state = 0; + p->m_call = NULL; + p->m_bFunc = FALSE; + return p; +} + +CBotStack* CBotStack::AddStackEOX(CBotCall* instr, BOOL bBlock) +{ + if (m_next != NULL) + { + if ( m_next == EOX ) + { + m_next = NULL; + return EOX; + } + return m_next; // reprise dans une pile existante + } + CBotStack* p = AddStack(NULL, bBlock); + p->m_call = instr; + p->m_bFunc = 2; // spécial + return p; +} + +CBotStack* CBotStack::AddStack2(BOOL bBlock) +{ + if (m_next2 != NULL) + { + m_next2->m_prog = m_prog; // spécial évite un RestoreStack2 + return m_next2; // reprise dans une pile existante + } + + CBotStack* p = this; + do + { + p ++; + } + while ( p->m_prev != NULL ); + + m_next2 = p; // chaîne l'élément + p->m_prev = this; + p->m_bBlock = bBlock; + p->m_prog = m_prog; + p->m_step = 0; + return p; +} + +BOOL CBotStack::GivBlock() +{ + return m_bBlock; +} + +BOOL CBotStack::Return(CBotStack* pfils) +{ + if ( pfils == this ) return TRUE; // spécial + + if (m_var != NULL) delete m_var; // valeur remplacée ? + m_var = pfils->m_var; // résultat transmis + pfils->m_var = NULL; // ne pas détruire la variable + + m_next->Delete();m_next = NULL; // libère la pile au dessus + m_next2->Delete();m_next2 = NULL; // aussi la seconde pile (catch) + + return (m_error == 0); // interrompu si erreur +} + +BOOL CBotStack::ReturnKeep(CBotStack* pfils) +{ + if ( pfils == this ) return TRUE; // spécial + + if (m_var != NULL) delete m_var; // valeur remplacée ? + m_var = pfils->m_var; // résultat transmis + pfils->m_var = NULL; // ne pas détruire la variable + + return (m_error == 0); // interrompu si erreur +} + +BOOL CBotStack::StackOver() +{ + if (!m_bOver) return FALSE; + m_error = TX_STACKOVER; + return TRUE; +} + +#else + +CBotStack::CBotStack(CBotStack* ppapa) +{ + m_next = NULL; + m_next2 = NULL; + m_prev = ppapa; + + m_bBlock = (ppapa == NULL) ? TRUE : FALSE; + + m_state = 0; + m_step = 1; + + if (ppapa == NULL) m_timer = m_initimer; // met le timer au début + + m_listVar = NULL; + m_bDontDelete = FALSE; + + m_var = NULL; + m_prog = NULL; + m_instr = NULL; + m_call = NULL; + m_bFunc = FALSE; +} + +// destructeur +CBotStack::~CBotStack() +{ + if ( m_next != EOX) delete m_next; + delete m_next2; + if (m_prev != NULL && m_prev->m_next == this ) + m_prev->m_next = NULL; // enlève de la chaîne + + delete m_var; + if ( !m_bDontDelete ) delete m_listVar; +} + +// routine à optimiser +CBotStack* CBotStack::AddStack(CBotInstr* instr, BOOL bBlock) +{ + if (m_next != NULL) + { + return m_next; // reprise dans une pile existante + } + CBotStack* p = new CBotStack(this); + m_next = p; // chaîne l'élément + p->m_bBlock = bBlock; + p->m_instr = instr; + p->m_prog = m_prog; + p->m_step = 0; + return p; +} + +CBotStack* CBotStack::AddStackEOX(CBotCall* instr, BOOL bBlock) +{ + if (m_next != NULL) + { + if ( m_next == EOX ) + { + m_next = NULL; + return EOX; + } + return m_next; // reprise dans une pile existante + } + CBotStack* p = new CBotStack(this); + m_next = p; // chaîne l'élément + p->m_bBlock = bBlock; + p->m_call = instr; + p->m_prog = m_prog; + p->m_step = 0; + p->m_bFunc = 2; // spécial + return p; +} + +CBotStack* CBotStack::AddStack2(BOOL bBlock) +{ + if (m_next2 != NULL) + { + m_next2->m_prog = m_prog; // spécial évite un RestoreStack2 + return m_next2; // reprise dans une pile existante + } + + CBotStack* p = new CBotStack(this); + m_next2 = p; // chaîne l'élément + p->m_bBlock = bBlock; + p->m_prog = m_prog; + p->m_step = 0; + + return p; +} + +BOOL CBotStack::Return(CBotStack* pfils) +{ + if ( pfils == this ) return TRUE; // spécial + + if (m_var != NULL) delete m_var; // valeur remplacée ? + m_var = pfils->m_var; // résultat transmis + pfils->m_var = NULL; // ne pas détruite la variable + + if ( m_next != EOX ) delete m_next; // libère la pile au dessus + delete m_next2;m_next2 = NULL; // aussi la seconde pile (catch) + + return (m_error == 0); // interrompu si erreur +} + +BOOL CBotStack::StackOver() +{ + return FALSE; // pas de test de débordement dans cette version +} + +#endif + +void CBotStack::Reset(void* pUser) +{ + m_timer = m_initimer; // remet le timer + m_error = 0; +// m_start = 0; +// m_end = 0; + m_labelBreak.Empty(); + m_pUser = pUser; +} + + + + +CBotStack* CBotStack::RestoreStack(CBotInstr* instr) +{ + if (m_next != NULL) + { + m_next->m_instr = instr; // réinit (si reprise après restitution) + m_next->m_prog = m_prog; + return m_next; // reprise dans une pile existante + } + return NULL; +} + +CBotStack* CBotStack::RestoreStackEOX(CBotCall* instr) +{ + CBotStack* p = RestoreStack(); + p->m_call = instr; + return p; +} + + + +// routine pour l'exécution pas à pas +BOOL CBotStack::IfStep() +{ + if ( m_initimer > 0 || m_step++ > 0 ) return FALSE; + return TRUE; +} + + +BOOL CBotStack::BreakReturn(CBotStack* pfils, const char* name) +{ + if ( m_error>=0 ) return FALSE; // sortie normale + if ( m_error==-3 ) return FALSE; // sortie normale (return en cours) + + if (!m_labelBreak.IsEmpty() && (name[0] == 0 || m_labelBreak != name)) + return FALSE; // c'est pas pour moi + + m_error = 0; + m_labelBreak.Empty(); + return Return(pfils); +} + +BOOL CBotStack::IfContinue(int state, const char* name) +{ + if ( m_error != -2 ) return FALSE; + + if (!m_labelBreak.IsEmpty() && (name == NULL || m_labelBreak != name)) + return FALSE; // c'est pas pour moi + + m_state = state; // où reprendre ? + m_error = 0; + m_labelBreak.Empty(); + if ( m_next != EOX ) m_next->Delete(); // purge la pile au dessus + return TRUE; +} + +void CBotStack::SetBreak(int val, const char* name) +{ + m_error = -val; // réagit comme une Exception + m_labelBreak = name; + if (val == 3) // pour un return + { + m_retvar = m_var; + m_var = NULL; + } +} + +// remet sur la pile la valeur calculée par le dernier CBotReturn + +BOOL CBotStack::GivRetVar(BOOL bRet) +{ + if (m_error == -3) + { + if ( m_var ) delete m_var; + m_var = m_retvar; + m_retvar = NULL; + m_error = 0; + return TRUE; + } + return bRet; // interrompu par autre chose que return +} + +int CBotStack::GivError(int& start, int& end) +{ + start = m_start; + end = m_end; + return m_error; +} + + +// type d'instruction sur la pile +int CBotStack::GivType(int mode) +{ + if (m_var == NULL) return -1; + return m_var->GivType(mode); +} + +// type d'instruction sur la pile +CBotTypResult CBotStack::GivTypResult(int mode) +{ + if (m_var == NULL) return -1; + return m_var->GivTypResult(mode); +} + +// type d'instruction sur la pile +void CBotStack::SetType(CBotTypResult& type) +{ + if (m_var == NULL) return; + m_var->SetType( type ); +} + + +// trouve une variable par son token +// ce peut être une variable composée avec un point +CBotVar* CBotStack::FindVar(CBotToken* &pToken, BOOL bUpdate, BOOL bModif) +{ + CBotStack* p = this; + CBotString name = pToken->GivString(); + + while (p != NULL) + { + CBotVar* pp = p->m_listVar; + while ( pp != NULL) + { + if (pp->GivName() == name) + { + if ( bUpdate ) + pp->Maj(m_pUser, FALSE); + + return pp; + } + pp = pp->m_next; + } + p = p->m_prev; + } + return NULL; +} + +CBotVar* CBotStack::FindVar(const char* name) +{ + CBotStack* p = this; + while (p != NULL) + { + CBotVar* pp = p->m_listVar; + while ( pp != NULL) + { + if (pp->GivName() == name) + { + return pp; + } + pp = pp->m_next; + } + p = p->m_prev; + } + return NULL; +} + +// retrouve une variable sur la pile selon son numéro d'identification +// ce qui va plus vite que de comparer les noms. + +CBotVar* CBotStack::FindVar(long ident, BOOL bUpdate, BOOL bModif) +{ + CBotStack* p = this; + while (p != NULL) + { + CBotVar* pp = p->m_listVar; + while ( pp != NULL) + { + if (pp->GivUniqNum() == ident) + { + if ( bUpdate ) + pp->Maj(m_pUser, FALSE); + + return pp; + } + pp = pp->m_next; + } + p = p->m_prev; + } + return NULL; +} + + +CBotVar* CBotStack::FindVar(CBotToken& Token, BOOL bUpdate, BOOL bModif) +{ + CBotToken* pt = &Token; + return FindVar(pt, bUpdate, bModif); +} + + +CBotVar* CBotStack::CopyVar(CBotToken& Token, BOOL bUpdate) +{ + CBotVar* pVar = FindVar( Token, bUpdate ); + + if ( pVar == NULL) return NULL; + + CBotVar* pCopy = CBotVar::Create(pVar); + pCopy->Copy(pVar); + return pCopy; +} + + +BOOL CBotStack::SetState(int n, int limite) +{ + m_state = n; + + m_timer--; // décompte les opérations + return ( m_timer > limite ); // interrompu si timer passé +} + +BOOL CBotStack::IncState(int limite) +{ + m_state++; + + m_timer--; // décompte les opérations + return ( m_timer > limite ); // interrompu si timer passé +} + + +void CBotStack::SetError(int n, CBotToken* token) +{ + if ( n!= 0 && m_error != 0) return; // ne change pas une erreur déjà existante + m_error = n; + if (token != NULL) + { + m_start = token->GivStart(); + m_end = token->GivEnd(); + } +} + +void CBotStack::ResetError(int n, int start, int end) +{ + m_error = n; + m_start = start; + m_end = end; +} + +void CBotStack::SetPosError(CBotToken* token) +{ + m_start = token->GivStart(); + m_end = token->GivEnd(); +} + +void CBotStack::SetTimer(int n) +{ + m_initimer = n; +} + +BOOL CBotStack::Execute() +{ + CBotCall* instr = NULL; // instruction la plus élevée + CBotStack* pile; + + CBotStack* p = this; + + while (p != NULL) + { + if ( p->m_next2 != NULL ) break; + if ( p->m_call != NULL ) + { + instr = p->m_call; + pile = p->m_prev ; + } + p = p->m_next; + } + + if ( instr == NULL ) return TRUE; // exécution normale demandée + + if (!instr->Run(pile)) return FALSE; // exécution à partir de là + +#if STACKMEM + pile->m_next->Delete(); +#else + delete pile->m_next; +#endif + + pile->m_next = EOX; // spécial pour reprise + return TRUE; +} + +// met sur le stack le pointeur à une variable +void CBotStack::SetVar( CBotVar* var ) +{ + if (m_var) delete m_var; // remplacement d'une variable + m_var = var; +} + +// met sur le stack une copie d'une variable +void CBotStack::SetCopyVar( CBotVar* var ) +{ + if (m_var) delete m_var; // remplacement d'une variable + + m_var = CBotVar::Create("", var->GivTypResult(2)); + m_var->Copy( var ); +} + +CBotVar* CBotStack::GivVar() +{ + return m_var; +} + +CBotVar* CBotStack::GivPtVar() +{ + CBotVar* p = m_var; + m_var = NULL; // ne sera pas détruit donc + return p; +} + +CBotVar* CBotStack::GivCopyVar() +{ + if (m_var == NULL) return NULL; + CBotVar* v = CBotVar::Create("", m_var->GivType()); + v->Copy( m_var ); + return v; +} + +long CBotStack::GivVal() +{ + if (m_var == NULL) return 0; + return m_var->GivValInt(); +} + + + + +void CBotStack::AddVar(CBotVar* pVar) +{ + CBotStack* p = this; + + // revient sur l'élement père + while (p != NULL && p->m_bBlock == 0) p = p->m_prev; + + if ( p == NULL ) return; + +/// p->m_bDontDelete = bDontDelete; + + CBotVar** pp = &p->m_listVar; + while ( *pp != NULL ) pp = &(*pp)->m_next; + + *pp = pVar; // ajoute à la suite + +#ifdef _DEBUG + if ( pVar->GivUniqNum() == 0 ) __asm int 3; +#endif +} + +/*void CBotStack::RestoreVar(CBotVar* pVar) +{ + if ( !m_bDontDelete ) __asm int 3; + delete m_listVar; + m_listVar = pVar; // remplace directement +}*/ + +void CBotStack::SetBotCall(CBotProgram* p) +{ + m_prog = p; + m_bFunc = TRUE; +} + +CBotProgram* CBotStack::GivBotCall(BOOL bFirst) +{ + if ( ! bFirst ) return m_prog; + CBotStack* p = this; + while ( p->m_prev != NULL ) p = p->m_prev; + return p->m_prog; +} + +void* CBotStack::GivPUser() +{ + return m_pUser; +} + + +BOOL CBotStack::ExecuteCall(long& nIdent, CBotToken* token, CBotVar** ppVar, CBotTypResult& rettype) +{ + CBotTypResult res; + + // cherche d'abord selon l'identificateur + + res = CBotCall::DoCall(nIdent, NULL, ppVar, this, rettype ); + if (res.GivType() >= 0) return res.GivType(); + + res = m_prog->GivFunctions()->DoCall(nIdent, NULL, ppVar, this, token ); + if (res.GivType() >= 0) return res.GivType(); + + // si pas trouvé (recompilé ?) cherche selon le nom + + nIdent = 0; + res = CBotCall::DoCall(nIdent, token, ppVar, this, rettype ); + if (res.GivType() >= 0) return res.GivType(); + + res = m_prog->GivFunctions()->DoCall(nIdent, token->GivString(), ppVar, this, token ); + if (res.GivType() >= 0) return res.GivType(); + + SetError(TX_NOCALL, token); + return TRUE; +} + +void CBotStack::RestoreCall(long& nIdent, CBotToken* token, CBotVar** ppVar) +{ + if ( m_next == NULL ) return; + + if ( !CBotCall::RestoreCall(nIdent, token, ppVar, this) ) + m_prog->GivFunctions()->RestoreCall(nIdent, token->GivString(), ppVar, this ); +} + + +BOOL SaveVar(FILE* pf, CBotVar* pVar) +{ + while ( TRUE ) + { + if ( pVar == NULL ) + { + return WriteWord(pf, 0); // met un terminateur + } + + if ( !pVar->Save0State(pf)) return FALSE; // entête commune + if ( !pVar->Save1State(pf) ) return FALSE; // sauve selon la classe fille + + pVar = pVar->GivNext(); + } +} + +void CBotStack::GetRunPos(const char* &FunctionName, int &start, int &end) +{ + CBotProgram* prog = m_prog; // programme courrant + + CBotInstr* funct = NULL; // fonction trouvée + CBotInstr* instr = NULL; // instruction la plus élevée + + CBotStack* p = this; + + while (p->m_next != NULL) + { + if ( p->m_instr != NULL ) instr = p->m_instr; + if ( p->m_bFunc == 1 ) funct = p->m_instr; + if ( p->m_next->m_prog != prog ) break ; + + if (p->m_next2 && p->m_next2->m_state != 0) p = p->m_next2 ; + else p = p->m_next; + } + + if ( p->m_instr != NULL ) instr = p->m_instr; + if ( p->m_bFunc == 1 ) funct = p->m_instr; + + if ( funct == NULL ) return; + + CBotToken* t = funct->GivToken(); + FunctionName = t->GivString(); + +// if ( p->m_instr != NULL ) instr = p->m_instr; + + t = instr->GivToken(); + start = t->GivStart(); + end = t->GivEnd(); +} + +CBotVar* CBotStack::GivStackVars(const char* &FunctionName, int level) +{ + CBotProgram* prog = m_prog; // programme courrant + FunctionName = NULL; + + // remonte la pile dans le module courant + CBotStack* p = this; + + while (p->m_next != NULL) + { + if ( p->m_next->m_prog != prog ) break ; + + if (p->m_next2 && p->m_next2->m_state != 0) p = p->m_next2 ; + else p = p->m_next; + } + + + // descend sur les éléments de block + while ( p != NULL && !p->m_bBlock ) p = p->m_prev; + + while ( p != NULL && level++ < 0 ) + { + p = p->m_prev; + while ( p != NULL && !p->m_bBlock ) p = p->m_prev; + } + + if ( p == NULL ) return NULL; + + // recherche le nom de la fonction courante + CBotStack* pp = p; + while ( pp != NULL ) + { + if ( pp->m_bFunc == 1 ) break; + pp = pp->m_prev; + } + + if ( pp == NULL || pp->m_instr == NULL ) return NULL; + + CBotToken* t = pp->m_instr->GivToken(); + FunctionName = t->GivString(); + + return p->m_listVar; +} + +BOOL CBotStack::SaveState(FILE* pf) +{ + if ( this == NULL ) // fin de l'arbre ? + { + return WriteWord(pf, 0); // met un terminateur + } + + if ( m_next2 != NULL ) + { + if (!WriteWord(pf, 2)) return FALSE; // une marque de poursuite + if (!m_next2->SaveState(pf)) return FALSE; + } + else + { + if (!WriteWord(pf, 1)) return FALSE; // une marque de poursuite + } + if (!WriteWord(pf, m_bBlock)) return FALSE; // est-ce un bloc local + if (!WriteWord(pf, m_state)) return FALSE; // dans quel état + if (!WriteWord(pf, 0)) return FALSE; // par compatibilité m_bDontDelete + if (!WriteWord(pf, m_step)) return FALSE; // dans quel état + + + if (!SaveVar(pf, m_var)) return FALSE; // le résultat courant + if (!SaveVar(pf, m_listVar)) return FALSE; // les variables locales + + return m_next->SaveState(pf); // enregistre la suite +} + + +BOOL CBotStack::RestoreState(FILE* pf, CBotStack* &pStack) +{ + WORD w; + + pStack = NULL; + if (!ReadWord(pf, w)) return FALSE; + if ( w == 0 ) return TRUE; + +#if STACKMEM + if ( this == NULL ) pStack = FirstStack(); + else pStack = AddStack(); +#else + pStack = new CBotStack(this); +#endif + + if ( w == 2 ) + { + if (!pStack->RestoreState(pf, pStack->m_next2)) return FALSE; + } + + if (!ReadWord(pf, w)) return FALSE; // est-ce un bloc local + pStack->m_bBlock = w; + + if (!ReadWord(pf, w)) return FALSE; // dans quel état j'ère ? + pStack->SetState((short)w); // dans le bon état + + if (!ReadWord(pf, w)) return FALSE; // dont delete ? + // plus utilisé + + if (!ReadWord(pf, w)) return FALSE; // pas à pas + pStack->m_step = w; + + if (!CBotVar::RestoreState(pf, pStack->m_var)) return FALSE; // la variable temp + if (!CBotVar::RestoreState(pf, pStack->m_listVar)) return FALSE;// les variables locales + + return pStack->RestoreState(pf, pStack->m_next); +} + + +BOOL CBotVar::Save0State(FILE* pf) +{ + if (!WriteWord(pf, 100+m_mPrivate))return FALSE; // variable privée ? + if (!WriteWord(pf, m_bStatic))return FALSE; // variable static ? + if (!WriteWord(pf, m_type.GivType()))return FALSE; // enregiste le type (toujours non nul) + if (!WriteWord(pf, m_binit))return FALSE; // variable définie ? + return WriteString(pf, m_token->GivString()); // et le nom de la variable +} + +BOOL CBotVarInt::Save0State(FILE* pf) +{ + if ( !m_defnum.IsEmpty() ) + { + if(!WriteWord(pf, 200 )) return FALSE; // marqueur spécial + if(!WriteString(pf, m_defnum)) return FALSE; // nom de la valeur + } + + return CBotVar::Save0State(pf); +} + +BOOL CBotVarInt::Save1State(FILE* pf) +{ + return WriteWord(pf, m_val); // la valeur de la variable +} + +BOOL CBotVarBoolean::Save1State(FILE* pf) +{ + return WriteWord(pf, m_val); // la valeur de la variable +} + +BOOL CBotVarFloat::Save1State(FILE* pf) +{ + return WriteFloat(pf, m_val); // la valeur de la variable +} + +BOOL CBotVarString::Save1State(FILE* pf) +{ + return WriteString(pf, m_val); // la valeur de la variable +} + + + +BOOL CBotVarClass::Save1State(FILE* pf) +{ + if ( !WriteType(pf, m_type) ) return FALSE; + if ( !WriteLong(pf, m_ItemIdent) ) return FALSE; + + return SaveVar(pf, m_pVar); // contenu de l'objet +} + +BOOL CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) +{ + WORD w, wi, prv, st; + float ww; + CBotString name, s; + + delete pVar; + + pVar = NULL; + CBotVar* pNew = NULL; + CBotVar* pPrev = NULL; + + while ( TRUE ) // recupère toute une liste + { + if (!ReadWord(pf, w)) return FALSE; // privé ou type ? + if ( w == 0 ) return TRUE; + + CBotString defnum; + if ( w == 200 ) + { + if (!ReadString(pf, defnum)) return FALSE; // nombre avec un identifiant + if (!ReadWord(pf, w)) return FALSE; // type + } + + prv = 100; st = 0; + if ( w >= 100 ) + { + prv = w; + if (!ReadWord(pf, st)) return FALSE; // statique + if (!ReadWord(pf, w)) return FALSE; // type + } + + if ( w == CBotTypClass ) w = CBotTypIntrinsic; // forcément intrinsèque + + if (!ReadWord(pf, wi)) return FALSE; // init ? + + if (!ReadString(pf, name)) return FALSE; // nom de la variable + + CBotToken token(name, CBotString()); + + switch (w) + { + case CBotTypInt: + case CBotTypBoolean: + pNew = CBotVar::Create(&token, w); // crée une variable + if (!ReadWord(pf, w)) return FALSE; + pNew->SetValInt((short)w, defnum); + break; + case CBotTypFloat: + pNew = CBotVar::Create(&token, w); // crée une variable + if (!ReadFloat(pf, ww)) return FALSE; + pNew->SetValFloat(ww); + break; + case CBotTypString: + pNew = CBotVar::Create(&token, w); // crée une variable + if (!ReadString(pf, s)) return FALSE; + pNew->SetValString(s); + break; + + // restitue un objet intrinsic ou un élément d'un array + case CBotTypIntrinsic: + case CBotTypArrayBody: + { + CBotTypResult r; + long id; + if (!ReadType(pf, r)) return FALSE; // type complet + if (!ReadLong(pf, id) ) return FALSE; + +// if (!ReadString(pf, s)) return FALSE; + { + CBotVar* p = NULL; + if ( id ) p = CBotVarClass::Find(id) ; + + pNew = new CBotVarClass(&token, r); // crée directement une instance + // attention cptuse = 0 + if ( !RestoreState(pf, ((CBotVarClass*)pNew)->m_pVar)) return FALSE; + pNew->SetIdent(id); + + if ( p != NULL ) + { + delete pNew; + pNew = p; // reprend l'élément connu + } + } + } + break; + + case CBotTypPointer: + case CBotTypNullPointer: + if (!ReadString(pf, s)) return FALSE; + { + pNew = CBotVar::Create(&token, CBotTypResult(w, s));// crée une variable + CBotVarClass* p = NULL; + long id; + ReadLong(pf, id); +// if ( id ) p = CBotVarClass::Find(id); // retrouve l'instance ( fait par RestoreInstance ) + + // restitue une copie de l'instance d'origine + CBotVar* pInstance = NULL; + if ( !CBotVar::RestoreState( pf, pInstance ) ) return FALSE; + ((CBotVarPointer*)pNew)->SetPointer( pInstance ); // et pointe dessus + +// if ( p != NULL ) ((CBotVarPointer*)pNew)->SetPointer( p ); // plutôt celui-ci ! + + } + break; + + case CBotTypArrayPointer: + { + CBotTypResult r; + if (!ReadType(pf, r)) return FALSE; + + pNew = CBotVar::Create(&token, r); // crée une variable + + // restitue une copie de l'instance d'origine + CBotVar* pInstance = NULL; + if ( !CBotVar::RestoreState( pf, pInstance ) ) return FALSE; + ((CBotVarPointer*)pNew)->SetPointer( pInstance ); // et pointe dessus + } + break; + default: + __asm int 3; + } + + if ( pPrev != NULL ) pPrev->m_next = pNew; + if ( pVar == NULL ) pVar = pNew; + + pNew->m_binit = wi; // pNew->SetInit(wi); + pNew->SetStatic(st); + pNew->SetPrivate(prv-100); + pPrev = pNew; + } + return TRUE; +} + + + + +//////////////////////////////////////////////////////////////////////////// +// gestion de la pile à la compilation +//////////////////////////////////////////////////////////////////////////// + +CBotProgram* CBotCStack::m_prog = NULL; // init la variable statique +int CBotCStack::m_error = 0; +int CBotCStack::m_end = 0; +CBotTypResult CBotCStack::m_retTyp = CBotTypResult(0); +//CBotToken* CBotCStack::m_retClass= NULL; + + +CBotCStack::CBotCStack(CBotCStack* ppapa) +{ + m_next = NULL; + m_prev = ppapa; + + if (ppapa == NULL) + { + m_error = 0; + m_start = 0; + m_end = 0; + m_bBlock = TRUE; + } + else + { + m_start = ppapa->m_start; + m_bBlock = FALSE; + } + + m_listVar = NULL; + m_var = NULL; +} + +// destructeur +CBotCStack::~CBotCStack() +{ + if (m_next != NULL) delete m_next; + if (m_prev != NULL) m_prev->m_next = NULL; // enlève de la chaîne + + delete m_var; + delete m_listVar; +} + +// utilisé uniquement à la compilation +CBotCStack* CBotCStack::TokenStack(CBotToken* pToken, BOOL bBlock) +{ + if (m_next != NULL) return m_next; // reprise dans une pile existante + + CBotCStack* p = new CBotCStack(this); + m_next = p; // chaîne l'élément + p->m_bBlock = bBlock; + + if (pToken != NULL) p->SetStartError(pToken->GivStart()); + + return p; +} + + +CBotInstr* CBotCStack::Return(CBotInstr* inst, CBotCStack* pfils) +{ + if ( pfils == this ) return inst; + + if (m_var != NULL) delete m_var; // valeur remplacée ? + m_var = pfils->m_var; // résultat transmis + pfils->m_var = NULL; // ne pas détruire la variable + + if (m_error) + { + m_start = pfils->m_start; // récupère la position de l'erreur + m_end = pfils->m_end; + } + + delete pfils; + return inst; +} + +CBotFunction* CBotCStack::ReturnFunc(CBotFunction* inst, CBotCStack* pfils) +{ + if (m_var != NULL) delete m_var; // valeur remplacée ? + m_var = pfils->m_var; // résultat transmis + pfils->m_var = NULL; // ne pas détruire la variable + + if (m_error) + { + m_start = pfils->m_start; // récupère la position de l'erreur + m_end = pfils->m_end; + } + + delete pfils; + return inst; +} + +int CBotCStack::GivError(int& start, int& end) +{ + start = m_start; + end = m_end; + return m_error; +} + +int CBotCStack::GivError() +{ + return m_error; +} + +// type d'instruction sur la pile +CBotTypResult CBotCStack::GivTypResult(int mode) +{ + if (m_var == NULL) + return CBotTypResult(99); + return m_var->GivTypResult(mode); +} + +// type d'instruction sur la pile +int CBotCStack::GivType(int mode) +{ + if (m_var == NULL) + return 99; + return m_var->GivType(mode); +} + +// pointeur sur la pile est de quelle classe ? +CBotClass* CBotCStack::GivClass() +{ + if ( m_var == NULL ) + return NULL; + if ( m_var->GivType(1) != CBotTypPointer ) return NULL; + + return m_var->GivClass(); +} + +// type d'instruction sur la pile +void CBotCStack::SetType(CBotTypResult& type) +{ + if (m_var == NULL) return; + m_var->SetType( type ); +} + +// cherche une variable sur la pile +// le token peut être une suite de TokenTypVar (objet d'une classe) +// ou un pointeur dans le source + +CBotVar* CBotCStack::FindVar(CBotToken* &pToken) +{ + CBotCStack* p = this; + CBotString name = pToken->GivString(); + + while (p != NULL) + { + CBotVar* pp = p->m_listVar; + while ( pp != NULL) + { + if (name == pp->GivName()) + { + return pp; + } + pp = pp->m_next; + } + p = p->m_prev; + } + return NULL; +} + +CBotVar* CBotCStack::FindVar(CBotToken& Token) +{ + CBotToken* pt = &Token; + return FindVar(pt); +} + +CBotVar* CBotCStack::CopyVar(CBotToken& Token) +{ + CBotVar* pVar = FindVar( Token ); + + if ( pVar == NULL) return NULL; + + CBotVar* pCopy = CBotVar::Create( "", pVar->GivType() ); + pCopy->Copy(pVar); + return pCopy; +} + +BOOL CBotCStack::IsOk() +{ + return (m_error == 0); +} + + +void CBotCStack::SetStartError( int pos ) +{ + if ( m_error != 0) return; // ne change pas une erreur déjà existante + m_start = pos; +} + +void CBotCStack::SetError(int n, int pos) +{ + if ( n!= 0 && m_error != 0) return; // ne change pas une erreur déjà existante + m_error = n; + m_end = pos; +} + +void CBotCStack::SetError(int n, CBotToken* p) +{ + if (m_error) return; // ne change pas une erreur déjà existante + m_error = n; + m_start = p->GivStart(); + m_end = p->GivEnd(); +} + +void CBotCStack::ResetError(int n, int start, int end) +{ + m_error = n; + m_start = start; + m_end = end; +} + +BOOL CBotCStack::NextToken(CBotToken* &p) +{ + CBotToken* pp = p; + + p = p->GivNext(); + if (p!=NULL) return TRUE; + + SetError(TX_ENDOF, pp->GivEnd()); + return FALSE; +} + +void CBotCStack::SetBotCall(CBotProgram* p) +{ + m_prog = p; +} + +CBotProgram* CBotCStack::GivBotCall() +{ + return m_prog; +} + +void CBotCStack::SetRetType(CBotTypResult& type) +{ + m_retTyp = type; +} + +CBotTypResult CBotCStack::GivRetType() +{ + return m_retTyp; +} + +void CBotCStack::SetVar( CBotVar* var ) +{ + if (m_var) delete m_var; // remplacement d'une variable + m_var = var; +} + +// met sur le stack une copie d'une variable +void CBotCStack::SetCopyVar( CBotVar* var ) +{ + if (m_var) delete m_var; // remplacement d'une variable + + if ( var == NULL ) return; + m_var = CBotVar::Create("", var->GivTypResult(2)); + m_var->Copy( var ); +} + +CBotVar* CBotCStack::GivVar() +{ + return m_var; +} + +void CBotCStack::AddVar(CBotVar* pVar) +{ + CBotCStack* p = this; + + // revient sur l'élement père + while (p != NULL && p->m_bBlock == 0) p = p->m_prev; + + if ( p == NULL ) return; + + CBotVar** pp = &p->m_listVar; + while ( *pp != NULL ) pp = &(*pp)->m_next; + + *pp = pVar; // ajoute à la suite + +#ifdef _DEBUG + if ( pVar->GivUniqNum() == 0 ) __asm int 3; +#endif +} + +// test si une variable est déjà définie localement + +BOOL CBotCStack::CheckVarLocal(CBotToken* &pToken) +{ + CBotCStack* p = this; + CBotString name = pToken->GivString(); + + while (p != NULL) + { + CBotVar* pp = p->m_listVar; + while ( pp != NULL) + { + if (name == pp->GivName()) + return TRUE; + pp = pp->m_next; + } + if ( p->m_bBlock ) return FALSE; + p = p->m_prev; + } + return FALSE; +} + +CBotTypResult CBotCStack::CompileCall(CBotToken* &p, CBotVar** ppVars, long& nIdent) +{ + nIdent = 0; + CBotTypResult val(-1); + + val = CBotCall::CompileCall(p, ppVars, this, nIdent); + if (val.GivType() < 0) + { + val = m_prog->GivFunctions()->CompileCall(p->GivString(), ppVars, nIdent); + if ( val.GivType() < 0 ) + { + // pVar = NULL; // l'erreur n'est pas sur un paramètre en particulier + SetError( -val.GivType(), p ); + val.SetType(-val.GivType()); + return val; + } + } + return val; +} + +// test si un nom de procédure est déjà défini quelque part + +BOOL CBotCStack::CheckCall(CBotToken* &pToken, CBotDefParam* pParam) +{ + CBotString name = pToken->GivString(); + + if ( CBotCall::CheckCall(name) ) return TRUE; + + CBotFunction* pp = m_prog->GivFunctions(); + while ( pp != NULL ) + { + if ( pToken->GivString() == pp->GivName() ) + { + // les paramètres sont-ils exactement les mêmes ? + if ( pp->CheckParam( pParam ) ) + return TRUE; + } + pp = pp->Next(); + } + + pp = CBotFunction::m_listPublic; + while ( pp != NULL ) + { + if ( pToken->GivString() == pp->GivName() ) + { + // les paramètres sont-ils exactement les mêmes ? + if ( pp->CheckParam( pParam ) ) + return TRUE; + } + pp = pp->m_nextpublic; + } + + return FALSE; +} + diff --git a/src/CBot/CBotString.cpp b/src/CBot/CBotString.cpp new file mode 100644 index 00000000..bcbe3995 --- /dev/null +++ b/src/CBot/CBotString.cpp @@ -0,0 +1,588 @@ +///////////////////////////////////////////////////// +// gestion de chaine +// basé sur le CString de MFC +// mais moins complet + +#include "CBot.h" + +#include + +HINSTANCE CBotString::m_hInstance = (HINSTANCE)LoadLibrary("Cbot.dll"); // comment le récupérer autrement ?? + + +CBotString::CBotString() +{ + m_ptr = NULL; // chaine vide + m_lg = 0; +} + +CBotString::~CBotString() +{ + if (m_ptr != NULL) free(m_ptr); +} + + +CBotString::CBotString(const char* p) +{ + m_lg = lstrlen( p ); + + m_ptr = NULL; + if (m_lg>0) + { + m_ptr = (char*)malloc(m_lg+1); + lstrcpy(m_ptr, p); + } +} + +CBotString::CBotString(const CBotString& srcString) +{ + m_lg = srcString.m_lg; + + m_ptr = NULL; + if (m_lg>0) + { + m_ptr = (char*)malloc(m_lg+1); + lstrcpy(m_ptr, srcString.m_ptr); + } +} + + + + +int CBotString::GivLength() +{ + if ( m_ptr == NULL ) return 0; + return lstrlen( m_ptr ); +} + + + +CBotString CBotString::Left(int nCount) const +{ + char chaine[2000]; + + int i; + for (i = 0; i < m_lg && i < nCount && i < 1999; i++) + { + chaine[i] = m_ptr[i]; + } + chaine[i] = 0 ; + + return CBotString( chaine ); +} + +CBotString CBotString::Right(int nCount) const +{ + char chaine[2000]; + + int i = m_lg - nCount; + if ( i < 0 ) i = 0; + + for ( int j = 0; i < m_lg && i < 1999; i++) + { + chaine[j++] = m_ptr[i]; + } + chaine[j] = 0 ; + + return CBotString( chaine ); +} + +CBotString CBotString::Mid(int nFirst, int nCount) const +{ + char chaine[2000]; + + int i; + + for ( i = nFirst; i < m_lg && i < 1999 && i <= nFirst + nCount; i++) + { + chaine[i] = m_ptr[i]; + } + chaine[i] = 0 ; + + return CBotString( chaine ); +} + +CBotString CBotString::Mid(int nFirst) const +{ + char chaine[2000]; + + int i; + + for ( i = nFirst; i < m_lg && i < 1999 ; i++) + { + chaine[i] = m_ptr[i]; + } + chaine[i] = 0 ; + + return CBotString( chaine ); +} + + +int CBotString::Find(const char c) +{ + int i; + for (i = 0; i < m_lg; i++) + { + if (m_ptr[i] == c) return i; + } + return -1; +} + +int CBotString::Find(LPCTSTR lpsz) +{ + int i, j; + int l = lstrlen(lpsz); + + for (i = 0; i <= m_lg-l; i++) + { + for (j = 0; j < l; j++) + { + if (m_ptr[i+j] != lpsz[j]) goto bad; + } + return i; +bad:; + } + return -1; +} + +int CBotString::ReverseFind(const char c) +{ + int i; + for (i = m_lg-1; i >= 0; i--) + { + if (m_ptr[i] == c) return i; + } + return -1; +} + +int CBotString::ReverseFind(LPCTSTR lpsz) +{ + int i, j; + int l = lstrlen(lpsz); + + for (i = m_lg-l; i >= 0; i--) + { + for (j = 0; j < l; j++) + { + if (m_ptr[i+j] != lpsz[j]) goto bad; + } + return i; +bad:; + } + return -1; +} + +CBotString CBotString::Mid(int start, int lg) +{ + CBotString res; + if (start >= m_lg) return res; + + if ( lg < 0 ) lg = m_lg - start; + + char* p = (char*)malloc(m_lg+1); + lstrcpy(p, m_ptr+start); + p[lg] = 0; + + res = p; + free(p); + return res; +} + +void CBotString::MakeUpper() +{ + int i; + + for ( i = 0; i < m_lg && i < 1999 ; i++) + { + char c = m_ptr[i]; + if ( c >= 'a' && c <= 'z' ) m_ptr[i] = c - 'a' + 'A'; + } +} + +void CBotString::MakeLower() +{ + int i; + + for ( i = 0; i < m_lg && i < 1999 ; i++) + { + char c = m_ptr[i]; + if ( c >= 'A' && c <= 'Z' ) m_ptr[i] = c - 'A' + 'a'; + } +} + + + +#define MAXSTRING 256 + +BOOL CBotString::LoadString(UINT id) +{ + char buffer[MAXSTRING]; + + m_lg = ::LoadString( m_hInstance, id, buffer, MAXSTRING ); + + if (m_ptr != NULL) free(m_ptr); + + m_ptr = NULL; + if (m_lg > 0) + { + m_ptr = (char*)malloc(m_lg+1); + lstrcpy(m_ptr, buffer); + return TRUE; + } + return FALSE; +} + + +const CBotString& CBotString::operator=(const CBotString& stringSrc) +{ + if (m_ptr != NULL) free(m_ptr); + + m_lg = stringSrc.m_lg; + m_ptr = NULL; + + if (m_lg > 0) + { + m_ptr = (char*)malloc(m_lg+1); + lstrcpy(m_ptr, stringSrc.m_ptr); + } + + return *this; +} + +CBotString operator+(const CBotString& string, LPCTSTR lpsz) +{ + CBotString s ( string ); + s += lpsz; + return s; +} + +const CBotString& CBotString::operator+(const CBotString& stringSrc) +{ + char* p = (char*)malloc(m_lg+stringSrc.m_lg+1); + + lstrcpy(p, m_ptr); + char* pp = p + m_lg; + lstrcpy(pp, stringSrc.m_ptr); + + if (m_ptr != NULL) free(m_ptr); + m_ptr = p; + m_lg += stringSrc.m_lg; + + return *this; +} + +const CBotString& CBotString::operator=(const char ch) +{ + if (m_ptr != NULL) free(m_ptr); + + m_lg = 1; + + m_ptr = (char*)malloc(2); + m_ptr[0] = ch; + m_ptr[1] = 0; + + return *this; +} + +const CBotString& CBotString::operator=(const char* pString) +{ + if (m_ptr != NULL) free(m_ptr); + m_ptr = NULL; + + if ( pString != NULL ) + { + m_lg = lstrlen(pString); + + if (m_lg != 0) + { + m_ptr = (char*)malloc(m_lg+1); + lstrcpy(m_ptr, pString); + } + } + + return *this; +} + + +const CBotString& CBotString::operator+=(const char ch) +{ + char* p = (char*)malloc(m_lg+2); + + if (m_ptr!=NULL) lstrcpy(p, m_ptr); + p[m_lg++] = ch; + p[m_lg] = 0; + + if (m_ptr != NULL) free(m_ptr); + + m_ptr = p; + + return *this; +} + +const CBotString& CBotString::operator+=(const CBotString& str) +{ + char* p = (char*)malloc(m_lg+str.m_lg+1); + + lstrcpy(p, m_ptr); + char* pp = p + m_lg; + lstrcpy(pp, str.m_ptr); + + m_lg = m_lg + str.m_lg; + + if (m_ptr != NULL) free(m_ptr); + + m_ptr = p; + + return *this; +} + +BOOL CBotString::operator==(const CBotString& str) +{ + return Compare(str) == 0; +} + +BOOL CBotString::operator==(const char* p) +{ + return Compare(p) == 0; +} + +BOOL CBotString::operator!=(const CBotString& str) +{ + return Compare(str) != 0; +} + +BOOL CBotString::operator!=(const char* p) +{ + return Compare(p) != 0; +} + +BOOL CBotString::operator>(const CBotString& str) +{ + return Compare(str) > 0; +} + +BOOL CBotString::operator>(const char* p) +{ + return Compare(p) > 0; +} + +BOOL CBotString::operator>=(const CBotString& str) +{ + return Compare(str) >= 0; +} + +BOOL CBotString::operator>=(const char* p) +{ + return Compare(p) >= 0; +} + +BOOL CBotString::operator<(const CBotString& str) +{ + return Compare(str) < 0; +} + +BOOL CBotString::operator<(const char* p) +{ + return Compare(p) < 0; +} + +BOOL CBotString::operator<=(const CBotString& str) +{ + return Compare(str) <= 0; +} + +BOOL CBotString::operator<=(const char* p) +{ + return Compare(p) <= 0; +} + +BOOL CBotString::IsEmpty() +{ + return (m_lg == 0); +} + +void CBotString::Empty() +{ + if (m_ptr != NULL) free(m_ptr); + m_ptr = NULL; + m_lg = 0; +} + +static char nilstring[] = {0}; + +CBotString::operator LPCTSTR() const +{ + if (this == NULL || m_ptr == NULL) return nilstring; + return m_ptr; +} + + +int CBotString::Compare(LPCTSTR lpsz) const +{ + char* p = m_ptr; + if (lpsz == NULL) lpsz = nilstring; + if (m_ptr == NULL) p = nilstring; + return strcmp(p, lpsz); // wcscmp +} + + + +/////////////////////////////////////////////////////////////////////////////////////////// +// tableaux de chaines + +CBotStringArray::CBotStringArray() +{ + m_pData = NULL; + m_nSize = m_nMaxSize = 0; +} + +CBotStringArray::~CBotStringArray() +{ + SetSize(0); // détruit les données ! +} + + +int CBotStringArray::GivSize() +{ + return m_nSize; +} + +void CBotStringArray::Add(const CBotString& str) +{ + SetSize(m_nSize+1); + + m_pData[m_nSize-1] = str; +} + + +/////////////////////////////////////////////////////////////////////// +// routines utilitaires + +static inline void ConstructElement(CBotString* pNewData) +{ + memset(pNewData, 0, sizeof(CBotString)); +} + +static inline void DestructElement(CBotString* pOldData) +{ + pOldData->~CBotString(); +} + +static inline void CopyElement(CBotString* pSrc, CBotString* pDest) +{ + *pSrc = *pDest; +} + +static void ConstructElements(CBotString* pNewData, int nCount) +{ + while (nCount--) + { + ConstructElement(pNewData); + pNewData++; + } +} + +static void DestructElements(CBotString* pOldData, int nCount) +{ + while (nCount--) + { + DestructElement(pOldData); + pOldData++; + } +} + +static void CopyElements(CBotString* pDest, CBotString* pSrc, int nCount) +{ + while (nCount--) + { + *pDest = *pSrc; + ++pDest; + ++pSrc; + } +} + + + +// sélect la taille du tableau + +void CBotStringArray::SetSize(int nNewSize) +{ + if (nNewSize == 0) + { + // shrink to nothing + + DestructElements(m_pData, m_nSize); + delete[] (BYTE*)m_pData; + m_pData = NULL; + m_nSize = m_nMaxSize = 0; + } + else if (m_pData == NULL) + { + // create one with exact size + m_pData = (CBotString*) new BYTE[nNewSize * sizeof(CBotString)]; + + ConstructElements(m_pData, nNewSize); + + m_nSize = m_nMaxSize = nNewSize; + } + else if (nNewSize <= m_nMaxSize) + { + // it fits + if (nNewSize > m_nSize) + { + // initialize the new elements + + ConstructElements(&m_pData[m_nSize], nNewSize-m_nSize); + + } + + else if (m_nSize > nNewSize) // destroy the old elements + DestructElements(&m_pData[nNewSize], m_nSize-nNewSize); + + m_nSize = nNewSize; + } + else + { + // otherwise, grow array + int nGrowBy; + { + // heuristically determine growth when nGrowBy == 0 + // (this avoids heap fragmentation in many situations) + nGrowBy = min(1024, max(4, m_nSize / 8)); + } + int nNewMax; + if (nNewSize < m_nMaxSize + nGrowBy) + nNewMax = m_nMaxSize + nGrowBy; // granularity + else + nNewMax = nNewSize; // no slush + + CBotString* pNewData = (CBotString*) new BYTE[nNewMax * sizeof(CBotString)]; + + // copy new data from old + memcpy(pNewData, m_pData, m_nSize * sizeof(CBotString)); + + // construct remaining elements + ConstructElements(&pNewData[m_nSize], nNewSize-m_nSize); + + + // Ret rid of old stuff (note: no destructors called) + delete[] (BYTE*)m_pData; + m_pData = pNewData; + m_nSize = nNewSize; + m_nMaxSize = nNewMax; + } +} + + +CBotString& CBotStringArray::operator[](int nIndex) +{ + return ElementAt(nIndex); +} + +CBotString& CBotStringArray::ElementAt(int nIndex) +{ + return m_pData[nIndex]; +} + + + diff --git a/src/CBot/CBotToken.cpp b/src/CBot/CBotToken.cpp new file mode 100644 index 00000000..4e99cf94 --- /dev/null +++ b/src/CBot/CBotToken.cpp @@ -0,0 +1,540 @@ +////////////////////////////////////////////////////////////////// +// Gestion des Tokens +// le texte d'un programme est d'abord transformé +// en une suite de tokens pour facilité l'interprétation + +// il faudra traiter le seul cas d'erreur possible +// qui est un caractère illégal dans une string + + +#include "CBot.h" + +CBotStringArray CBotToken::m_ListKeyWords; +int CBotToken::m_ListIdKeyWords[200]; +CBotStringArray CBotToken::m_ListKeyDefine; +long CBotToken::m_ListKeyNums[MAXDEFNUM]; + +// constructeurs +CBotToken::CBotToken() +{ + m_next = NULL; + m_prev = NULL; + + m_type = TokenTypVar; // à priori un nom d'une variable + m_IdKeyWord = -1; +} + +CBotToken::CBotToken(const CBotToken* pSrc) +{ + m_next = NULL; + m_prev = NULL; + + m_Text.Empty(); + m_Sep.Empty(); + + m_type = 0; + m_IdKeyWord = 0; + + m_start = 0; + m_end = 0; + + if ( pSrc != NULL ) + { + m_Text = pSrc->m_Text; + m_Sep = pSrc->m_Sep; + + m_type = pSrc->m_type; + m_IdKeyWord = pSrc->m_IdKeyWord; + + m_start = pSrc->m_start; + m_end = pSrc->m_end; + } +} + +CBotToken::CBotToken(CBotString& mot, CBotString& sep, int start, int end) +{ + m_Text = mot; // mot trouvé comme token + m_Sep = sep; // séparateurs qui suivent + m_next = NULL; + m_prev = NULL; + m_start = start; + m_end = end; + + m_type = TokenTypVar; // à priori un nom d'une variable + m_IdKeyWord = -1; +} + +CBotToken::CBotToken(const char* mot, const char* sep) +{ + m_Text = mot; + if ( sep != NULL ) m_Sep = sep; + m_next = NULL; + m_prev = NULL; + + m_type = TokenTypVar; // à priori un nom d'une variable + m_IdKeyWord = -1; +} + +CBotToken::~CBotToken() +{ + delete m_next; // récursif + m_next = NULL; +} + +void CBotToken::Free() +{ + m_ListKeyDefine.SetSize(0); +} + +const CBotToken& CBotToken::operator=(const CBotToken& src) +{ + if (m_next != NULL) delete(m_next); + m_next = NULL; + m_prev = NULL; + + m_Text = src.m_Text; + m_Sep = src.m_Sep; + + m_type = src.m_type; + m_IdKeyWord = src.m_IdKeyWord; + + m_start = src.m_start; + m_end = src.m_end; + return *this; +} + + +int CBotToken::GivType() +{ + if (this == NULL) return 0; + if (m_type == TokenTypKeyWord) return m_IdKeyWord; + return m_type; +} + +long CBotToken::GivIdKey() +{ + return m_IdKeyWord; +} + +CBotToken* CBotToken::GivNext() +{ + if (this == NULL) return NULL; + return m_next; +} + +CBotToken* CBotToken::GivPrev() +{ + if (this == NULL) return NULL; + return m_prev; +} + +void CBotToken::AddNext(CBotToken* p) +{ + CBotToken* n = new CBotToken(p); + CBotToken* pt = this; + + while ( pt->m_next != NULL ) pt = pt->m_next; + + pt->m_next = n; + n->m_prev = pt; +} + + +CBotString& CBotToken::GivString() +{ + return m_Text; +} + +CBotString& CBotToken::GivSep() +{ + return m_Sep; +} + +void CBotToken::SetString(const char* name) +{ + m_Text = name; +} + + +int CBotToken::GivStart() +{ + if (this == NULL) return -1; + return m_start; +} + +int CBotToken::GivEnd() +{ + if (this == NULL) return -1; + return m_end; +} + +void CBotToken::SetPos(int start, int end) +{ + m_start = start; + m_end = end; +} + +BOOL CharInList(const char c, const char* list) +{ + int i = 0; + + while (TRUE) + { + if (c == list[i++]) return TRUE; + if (list[i] == 0) return FALSE; + } +} + +BOOL Char2InList(const char c, const char cc, const char* list) +{ + int i = 0; + + while (TRUE) + { + if (c == list[i++] && + cc == list[i++]) return TRUE; + + if (list[i] == 0) return FALSE; + } +} + +static char* sep1 = " \r\n\t,:()[]{}-+*/=;>!~^|&%."; // séparateurs opérationnels +static char* num = "0123456789"; // le point (unique) est testé séparément +static char* hexnum = "0123456789ABCDEFabcdef"; +static char* nch = "\"\r\n\t"; // refusé dans les chaines + +//static char* duo = "+=-=*=/===!=<=>=++--///**/||&&";// les opérateurs doubles + +// cherche le prochain token dans une phrase +// ne doit pas commencer par des séparateurs +// qui sont pris avec le token précédent +CBotToken* CBotToken::NextToken(char* &program, int& error, BOOL first) +{ + CBotString mot; // le mot trouvé + CBotString sep; // les séparateurs qui le suivent + char c; + BOOL stop = first; + + if (*program == 0) return NULL; + + c = *(program++); // prochain caractère + + if (!first) + { + mot = c; // construit le mot + c = *(program++); // prochain caractère + + // cas particulier pour les chaînes de caractères + if ( mot[0] == '\"' ) + { + while (c != 0 && !CharInList(c, nch)) + { + mot += c; + c = *(program++); // prochain caractère + if ( c == '\\' ) + { + c = *(program++); // prochain caractère + if ( c == 'n' ) c = '\n'; + if ( c == 'r' ) c = '\r'; + if ( c == 't' ) c = '\t'; + mot += c; + c = *(program++); // prochain caractère + } + } + if ( c == '\"' ) + { + mot += c; // chaîne complète + c = *(program++); // prochain caractère + } + stop = TRUE; + } + + // cas particulier pour les nombres + if ( CharInList(mot[0], num )) + { + BOOL bdot = FALSE; // trouvé un point ? + BOOL bexp = FALSE; // trouvé un exposant ? + + char* liste = num; + if (mot[0] == '0' && c == 'x') // valeur hexadécimale ? + { + mot += c; + c = *(program++); // prochain caractère + liste = hexnum; + } +cw: + while (c != 0 && CharInList(c, liste)) + { +cc: mot += c; + c = *(program++); // prochain caractère + } + if ( liste == num ) // pas pour les exadécimaux + { + if ( !bdot && c == '.' ) { bdot = TRUE; goto cc; } + if ( !bexp && ( c == 'e' || c == 'E' ) ) + { + bexp = TRUE; + mot += c; + c = *(program++); // prochain caractère + if ( c == '-' || + c == '+' ) goto cc; + goto cw; + } + + } + stop = TRUE; + } + + if (CharInList(mot[0], sep3)) // un séparateur opérationnel ? + { + CBotString motc = mot; + while (motc += c, c != 0 && GivKeyWords(motc)>0) // cherche l'opérande le plus long possible + { + mot += c; // construit le mot + c = *(program++); // prochain caractère + } + + stop = TRUE; + } + } + + + + while (TRUE) + { + if (stop || c == 0 || CharInList(c, sep1)) + { + if (!first && mot.IsEmpty()) return NULL; // fin de l'analyse +bis: + while (CharInList(c, sep2)) + { + sep += c; // tous les séparateurs qui suivent + c = *(program++); + } + if (c == '/' && *program == '/') // un commentaire dans le tas ? + { + while( c != '\n' && c != 0 ) + { + sep += c; + c = *(program++); // prochain caractère + } + goto bis; + } + + if (c == '/' && *program == '*') // un commentaire dans le tas ? + { + while( c != 0 && (c != '*' || *program != '/')) + { + sep += c; + c = *(program++); // prochain caractère + } + if ( c != 0 ) + { + sep += c; + c = *(program++); // prochain caractère + sep += c; + c = *(program++); // prochain caractère + } + goto bis; + } + + program--; + + CBotToken* token = new CBotToken(mot, sep); + + if (CharInList( mot[0], num )) token->m_type = TokenTypNum; + if (mot[0] == '\"') token->m_type = TokenTypString; + if (first) token->m_type = 0; + + token->m_IdKeyWord = GivKeyWords(mot); + if (token->m_IdKeyWord > 0) token->m_type = TokenTypKeyWord; + else GivKeyDefNum(mot, token) ; // traite les DefineNum + + return token; + } + + mot += c; // construit le mot + c = *(program++); // prochain caractère + } +} + +CBotToken* CBotToken::CompileTokens(const char* program, int& error) +{ + CBotToken *nxt, *prv, *tokenbase; + char* p = (char*) program; + int pos = 0; + + error = 0; + prv = tokenbase = NextToken(p, error, TRUE); + + if (tokenbase == NULL) return NULL; + + tokenbase->m_start = pos; + pos += tokenbase->m_Text.GivLength(); + tokenbase->m_end = pos; + pos += tokenbase->m_Sep.GivLength(); + + char* pp = p; + while (NULL != (nxt = NextToken(p, error))) + { + prv->m_next = nxt; // ajoute à la suite + nxt->m_prev = prv; + prv = nxt; // avance + + nxt->m_start = pos; +/* pos += nxt->m_Text.GivLength(); // la chaîne peut être plus courte (BOA supprimés) + nxt->m_end = pos; + pos += nxt->m_Sep.GivLength();*/ + pos += (p - pp); // taille totale + nxt->m_end = pos - nxt->m_Sep.GivLength(); + pp = p; + } + + // ajoute un token comme terminateur + // ( utile pour avoir le précédent ) + nxt = new CBotToken(); + nxt->m_type = 0; + prv->m_next = nxt; // ajoute à la suite + nxt->m_prev = prv; + + return tokenbase; +} + +void CBotToken::Delete(CBotToken* pToken) +{ + delete pToken; +} + + +// recherche si un mot fait parti des mots clefs + +int CBotToken::GivKeyWords(const char* w) +{ + int i; + int l = m_ListKeyWords.GivSize(); + + if (l == 0) + { + LoadKeyWords(); // prend la liste la première fois + l = m_ListKeyWords.GivSize(); + } + + for (i = 0; i < l; i++) + { + if (m_ListKeyWords[i] == w) return m_ListIdKeyWords[ i ]; + } + + return -1; +} + +BOOL CBotToken::GivKeyDefNum(const char* w, CBotToken* &token) +{ + int i; + int l = m_ListKeyDefine.GivSize(); + + for (i = 0; i < l; i++) + { + if (m_ListKeyDefine[i] == w) + { + token->m_IdKeyWord = m_ListKeyNums[i]; + token->m_type = TokenTypDef; + return TRUE; + } + } + + return FALSE; +} + +// reprend la liste des mots clefs dans les ressources + +void CBotToken::LoadKeyWords() +{ + CBotString s; + int i, n = 0; + + i = TokenKeyWord; + while (s.LoadString(i)) + { + m_ListKeyWords.Add(s); + m_ListIdKeyWords[n++] = i++; + } + + i = TokenKeyDeclare; + while (s.LoadString(i)) + { + m_ListKeyWords.Add(s); + m_ListIdKeyWords[n++] = i++; + } + + + i = TokenKeyVal; + while (s.LoadString(i)) + { + m_ListKeyWords.Add(s); + m_ListIdKeyWords[n++] = i++; + } + + i = TokenKeyOp; + while (s.LoadString(i)) + { + m_ListKeyWords.Add(s); + m_ListIdKeyWords[n++] = i++; + } +} + +BOOL CBotToken::DefineNum(const char* name, long val) +{ + int i; + int l = m_ListKeyDefine.GivSize(); + + for (i = 0; i < l; i++) + { + if (m_ListKeyDefine[i] == name) return FALSE; + } + if ( i == MAXDEFNUM ) return FALSE; + + m_ListKeyDefine.Add( name ); + m_ListKeyNums[i] = val; + return TRUE; +} + +BOOL IsOfType(CBotToken* &p, int type1, int type2) +{ + if (p->GivType() == type1 || + p->GivType() == type2 ) + { + p = p->GivNext(); + return TRUE; + } + return FALSE; +} + +// idem avec un nombre indéfini d'arguments +// il faut mettre un zéro comme dernier argument +BOOL IsOfTypeList(CBotToken* &p, int type1, ...) +{ + int i = type1; + int max = 20; + int type = p->GivType(); + + va_list marker; + va_start( marker, type1 ); /* Initialize variable arguments. */ + + while (TRUE) + { + if (type == i) + { + p = p->GivNext(); + va_end( marker ); /* Reset variable arguments. */ + return TRUE; + } + if (--max == 0 || 0 == (i = va_arg( marker, int))) + { + va_end( marker ); /* Reset variable arguments. */ + return FALSE; + } + } +} + diff --git a/src/CBot/CBotToken.h b/src/CBot/CBotToken.h new file mode 100644 index 00000000..2715dd58 --- /dev/null +++ b/src/CBot/CBotToken.h @@ -0,0 +1,23 @@ +//////////////////////////////////////////////////////////////////// +// interpréteur pour le language CBot du jeu COLOBOT + + +// un programme écrit est tout d'abord transformé en une liste de tokens +// avant d'aborder le compilateur proprement dit +// par exemple +// int var = 3 * ( pos.y + x ) +// est décomposé en (chaque ligne est un token) +// int +// var +// = +// 3 +// * +// ( +// pos.y +// + +// x +// ) + + +extern BOOL IsOfType(CBotToken* &p, int type1, int type2 = -1); +extern BOOL IsOfTypeList(CBotToken* &p, int type1, ...); diff --git a/src/CBot/CBotTwoOpExpr ordre inversé.cpp b/src/CBot/CBotTwoOpExpr ordre inversé.cpp new file mode 100644 index 00000000..65adcbad --- /dev/null +++ b/src/CBot/CBotTwoOpExpr ordre inversé.cpp @@ -0,0 +1,302 @@ +/////////////////////////////////////////////////// +// expression du genre Opérande1 + Opérande2 +// Opérande1 > Opérande2 + +#include "CBot.h" + +// divers constructeurs + +CBotTwoOpExpr::CBotTwoOpExpr() +{ + m_leftop = + m_rightop = NULL; // NULL pour pouvoir faire delete sans autre + name = "CBotTwoOpExpr"; // debug +} + +CBotTwoOpExpr::~CBotTwoOpExpr() +{ + delete m_leftop; + delete m_rightop; +} + +// type d'opérandes acceptés par les opérations +#define ENTIER ((1<TokenStack(); // un bout de pile svp + + // cherche des instructions qui peuvent convenir à gauche de l'opération + CBotInstr* left = CBotParExpr::Compile( p, pStk ) ; // expression (...) à gauche + + if (left == NULL) return pStack->Return(NULL, pStk); // si erreur, la transmet + + + CBotToken* pp = p; + int TypeOp = pp->GetType(); // type d'opération + p = p->Next(); // saute le token de l'opération + + // cherche des instructions qui peuvent convenir à droite + + CBotInstr* right = (*pOp == 0) ? + CBotParExpr::Compile( p, pStk ) : // expression (...) à droite + CBotTwoOpExpr::Compile( p, pStk, pOp ); // expression A op B à droite + + if (right == NULL) return pStack->Return(left, pStk); // pas d'opérande à droite ? + + // est-ce qu'on a l'opération prévue entre les deux ? + if ( IsInList( TypeOp, pOperations, typemasque ) ) + { + CBotTwoOpExpr* inst = new CBotTwoOpExpr(); // élément pour opération + inst->SetToken(pp); // mémorise l'opération + + int type1, type2; + type1 = pStk->GetType(); // de quel type le premier opérande ? + + inst->m_rightop = right; + { + // il y a un second opérande acceptable + + type2 = pStk->GetType(); // de quel type le résultat ? + + // quel est le type du résultat ? + int TypeRes = MAX( type1, type2 ); + if (!TypeOk( TypeRes, typemasque )) type1 = 99; // erreur de type + + switch ( TypeOp ) + { + case ID_LOG_OR: + case ID_LOG_AND: + case ID_EQ: + case ID_NE: + case ID_HI: + case ID_LO: + case ID_HS: + case ID_LS: + TypeRes = CBotTypBoolean; + } + if ( TypeCompatible (type1, type2) || // les résultats sont-ils compatibles + // cas particulier pour les concaténation de chaînes + (TypeOp == ID_ADD && (type1 == CBotTypString || type2 == CBotTypString))) + { + // si ok, enregistre l'opérande dans l'objet + inst->m_leftop = left; + // met une variable sur la pile pour avoir le type de résultat + pStk->SetVar(new CBotVar(NULL, TypeRes)); + // et rend l'object à qui l'a demandé + return pStack->Return(inst, pStk); + } + pStk->SetError(TX_BAD2TYPE, &inst->m_token); + } + + // en cas d'erreur, libère les éléments + delete left; + delete inst; + // et transmet l'erreur qui se trouve sur la pile + return pStack->Return(NULL, pStk); + } + + // si on n'a pas affaire à une opération + ou - + // rend à qui l'a demandé, l'opérande (de gauche) trouvé + // à la place de l'objet "addition" + return pStack->Return(left, pStk); +} + + + + +// fait l'opération d'addition ou de soustraction + +BOOL CBotTwoOpExpr::Execute(CBotStack* &pStack) +{ + CBotStack* pStk1 = pStack->AddStack(); // ajoute un élément à la pile + // ou le retrouve en cas de reprise +// if ( pStk1 == EOX ) return TRUE; + + + // selon la reprise, on peut être dans l'un des 2 états + + if ( pStk1->GetState() == 0 && // 1er état, évalue l'opérande de gauche + !m_leftop->Execute(pStk1) ) return FALSE; // interrompu ici ? + + // passe à l'étape suivante + pStk1->SetState(1); // prêt pour la suite + + // pour les OU et ET logique, n'évalue pas la seconde expression si pas nécessaire + if ( GetTokenType() == ID_LOG_AND && pStk1->GetVal() == FALSE ) + { + CBotVar* res = CBotVar::Create( NULL, CBotTypBoolean); + res->SetValInt(FALSE); + pStk1->SetVar(res); + return pStack->Return(pStk1); // transmet le résultat + } + if ( GetTokenType() == ID_LOG_OR && pStk1->GetVal() == TRUE ) + { + CBotVar* res = CBotVar::Create( NULL, CBotTypBoolean); + res->SetValInt(TRUE); + pStk1->SetVar(res); + return pStack->Return(pStk1); // transmet le résultat + } + + // demande un peu plus de stack pour ne pas toucher le résultat de gauche + // qui se trouve sur la pile, justement. + + CBotStack* pStk2 = pStk1->AddStack(); // ajoute un élément à la pile + // ou le retrouve en cas de reprise + + // 2e état, évalue l'opérande de droite + if ( !m_rightop->Execute(pStk2) ) return FALSE; // interrompu ici ? + + int type1 = pStk1->GetType(); // de quels types les résultats ? + int type2 = pStk2->GetType(); + + // crée une variable temporaire pour y mettre le résultat + // quel est le type du résultat ? + int TypeRes = MAX(type1, type2); + switch ( GetTokenType() ) + { + case ID_LOG_OR: + case ID_LOG_AND: + case ID_EQ: + case ID_NE: + case ID_HI: + case ID_LO: + case ID_HS: + case ID_LS: + TypeRes = CBotTypBoolean; + } + CBotVar* result = CBotVar::Create( NULL, TypeRes); + CBotVar* temp = CBotVar::Create( NULL, MAX(type1, type2) ); + + int err = 0; + // fait l'opération selon la demande + switch (GetTokenType()) + { + case ID_ADD: + result->Add(pStk1->GetVar(), pStk2->GetVar()); // additionne + break; + case ID_SUB: + result->Sub(pStk1->GetVar(), pStk2->GetVar()); // soustrait + break; + case ID_MUL: + result->Mul(pStk1->GetVar(), pStk2->GetVar()); // multiplie + break; + case ID_DIV: + err = result->Div(pStk1->GetVar(), pStk2->GetVar());// divise + break; + case ID_MODULO: + err = result->Modulo(pStk1->GetVar(), pStk2->GetVar());// reste de division + break; + case ID_LO: + temp->Lo(pStk1->GetVar(), pStk2->GetVar()); // inférieur + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_HI: + temp->Hi(pStk1->GetVar(), pStk2->GetVar()); // supérieur + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_LS: + temp->Ls(pStk1->GetVar(), pStk2->GetVar()); // inférieur ou égal + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_HS: + temp->Hs(pStk1->GetVar(), pStk2->GetVar()); // supérieur ou égal + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_EQ: + temp->Eq(pStk1->GetVar(), pStk2->GetVar()); // égal + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_NE: + temp->Ne(pStk1->GetVar(), pStk2->GetVar()); // différent + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_LOG_AND: + case ID_AND: + result->And(pStk1->GetVar(), pStk2->GetVar()); // ET + break; + case ID_LOG_OR: + case ID_OR: + result->Or(pStk1->GetVar(), pStk2->GetVar()); // OU + break; + case ID_XOR: + result->XOr(pStk1->GetVar(), pStk2->GetVar()); // OU exclusif + break; + case ID_ASR: + result->ASR(pStk1->GetVar(), pStk2->GetVar()); + break; + case ID_SR: + result->SR(pStk1->GetVar(), pStk2->GetVar()); + break; + case ID_SL: + result->SL(pStk1->GetVar(), pStk2->GetVar()); + break; + default: + __asm int 3; + } + delete temp; + + pStk2->SetVar(result); // met le résultat sur la pile + if ( err ) pStk2->SetError(err, &m_token); // et l'erreur éventuelle (division par zéro) + + pStk1->Return(pStk2); // libère la pile + return pStack->Return(pStk1); // transmet le résultat +} + + diff --git a/src/CBot/CBotTwoOpExpr.cpp b/src/CBot/CBotTwoOpExpr.cpp new file mode 100644 index 00000000..bc06873f --- /dev/null +++ b/src/CBot/CBotTwoOpExpr.cpp @@ -0,0 +1,552 @@ +/////////////////////////////////////////////////// +// expression du genre Opérande1 + Opérande2 +// Opérande1 > Opérande2 + +#include "CBot.h" + +// divers constructeurs + +CBotTwoOpExpr::CBotTwoOpExpr() +{ + m_leftop = + m_rightop = NULL; // NULL pour pouvoir faire delete sans autre + name = "CBotTwoOpExpr"; // debug +} + +CBotTwoOpExpr::~CBotTwoOpExpr() +{ + delete m_leftop; + delete m_rightop; +} + +CBotLogicExpr::CBotLogicExpr() +{ + m_condition = + m_op1 = + m_op2 = NULL; // NULL pour pouvoir faire delete sans autre + name = "CBotLogicExpr"; // debug +} + +CBotLogicExpr::~CBotLogicExpr() +{ + delete m_condition; + delete m_op1; + delete m_op2; +} + + +// type d'opérandes acceptés par les opérations +#define ENTIER ((1<TokenStack(); // un bout de pile svp + + // cherche des instructions qui peuvent convenir à gauche de l'opération + CBotInstr* left = (*pOp == 0) ? + CBotParExpr::Compile( p, pStk ) : // expression (...) à gauche + CBotTwoOpExpr::Compile( p, pStk, pOp ); // expression A * B à gauche + + if (left == NULL) return pStack->Return(NULL, pStk); // si erreur, la transmet + + // est-ce qu'on a l'opérande prévu ensuite ? + int TypeOp = p->GivType(); + if ( IsInList( TypeOp, pOperations, typemasque ) ) + { + CBotTypResult type1, type2; + type1 = pStk->GivTypResult(); // de quel type le premier opérande ? + + if ( TypeOp == ID_LOGIC ) // cas spécial pour condition ? op1 : op2 ; + { + if ( !type1.Eq(CBotTypBoolean) ) + { + pStk->SetError( TX_BADTYPE, p); + return pStack->Return(NULL, pStk); + } + CBotLogicExpr* inst = new CBotLogicExpr(); + inst->m_condition = left; + + p = p->GivNext(); // saute le token de l'opération + inst->m_op1 = CBotExpression::Compile(p, pStk); + CBotToken* pp = p; + if ( inst->m_op1 == NULL || !IsOfType( p, ID_DOTS ) ) + { + pStk->SetError( TX_MISDOTS, p->GivStart()); + delete inst; + return pStack->Return(NULL, pStk); + } + type1 = pStk->GivTypResult(); + + inst->m_op2 = CBotExpression::Compile(p, pStk); + if ( inst->m_op2 == NULL ) + { + pStk->SetError( TX_ENDOF, p->GivStart() ); + delete inst; + return pStack->Return(NULL, pStk); + } + type2 = pStk->GivTypResult(); + if (!TypeCompatible(type1, type2)) + { + pStk->SetError( TX_BAD2TYPE, pp ); + delete inst; + return pStack->Return(NULL, pStk); + } + + pStk->SetType(type1); // le plus grand des 2 types + + return pStack->Return(inst, pStk); + } + + CBotTwoOpExpr* inst = new CBotTwoOpExpr(); // élément pour opération + inst->SetToken(p); // mémorise l'opération + + + p = p->GivNext(); // saute le token de l'opération + + // cherche des instructions qui peuvent convenir à droite + + if ( NULL != (inst->m_rightop = CBotTwoOpExpr::Compile( p, pStk, pOp )) ) + // expression (...) à droite + { + // il y a un second opérande acceptable + + type2 = pStk->GivTypResult(); // de quel type le résultat ? + + // quel est le type du résultat ? + int TypeRes = MAX( type1.GivType(3), type2.GivType(3) ); + if ( TypeOp == ID_ADD && type1.Eq(CBotTypString) ) + { + TypeRes = CBotTypString; + type2 = type1; // tout type convertible en chaîne + } + else if ( TypeOp == ID_ADD && type2.Eq(CBotTypString) ) + { + TypeRes = CBotTypString; + type1 = type2; // tout type convertible en chaîne + } + else if (!TypeOk( TypeRes, typemasque )) type1.SetType(99);// erreur de type + + switch ( TypeOp ) + { + case ID_LOG_OR: + case ID_LOG_AND: + case ID_TXT_OR: + case ID_TXT_AND: + case ID_EQ: + case ID_NE: + case ID_HI: + case ID_LO: + case ID_HS: + case ID_LS: + TypeRes = CBotTypBoolean; + } + if ( TypeCompatible (type1, type2, TypeOp ) ) // les résultats sont-ils compatibles + { + // si ok, enregistre l'opérande dans l'objet + inst->m_leftop = left; + + // spécial pour évaluer les opérations de même niveau de gauche à droite + while ( IsInList( p->GivType(), pOperations, typemasque ) ) // même(s) opération(s) suit ? + { + TypeOp = p->GivType(); + CBotTwoOpExpr* i = new CBotTwoOpExpr(); // élément pour opération + i->SetToken(p); // mémorise l'opération + i->m_leftop = inst; // opérande de gauche + type1 = TypeRes; + + p = p->GivNext(); // avance à la suite + i->m_rightop = CBotTwoOpExpr::Compile( p, pStk, pOp ); + type2 = pStk->GivTypResult(); + + if ( !TypeCompatible (type1, type2, TypeOp) ) // les résultats sont-ils compatibles + { + pStk->SetError(TX_BAD2TYPE, &i->m_token); + delete i; + return pStack->Return(NULL, pStk); + } + + if ( TypeRes != CBotTypString ) + TypeRes = MAX(type1.GivType(), type2.GivType()); + inst = i; + } + + CBotTypResult t(type1); + t.SetType(TypeRes); + // met une variable sur la pile pour avoir le type de résultat + pStk->SetVar(CBotVar::Create((CBotToken*)NULL, t)); + + // et rend l'object à qui l'a demandé + return pStack->Return(inst, pStk); + } + pStk->SetError(TX_BAD2TYPE, &inst->m_token); + } + + // en cas d'erreur, libère les éléments + delete left; + delete inst; + // et transmet l'erreur qui se trouve sur la pile + return pStack->Return(NULL, pStk); + } + + // si on n'a pas affaire à une opération + ou - + // rend à qui l'a demandé, l'opérande (de gauche) trouvé + // à la place de l'objet "addition" + return pStack->Return(left, pStk); +} + + +BOOL IsNan(CBotVar* left, CBotVar* right, int* err = NULL) +{ + if ( left ->GivInit() > IS_DEF || right->GivInit() > IS_DEF ) + { + if ( err != NULL ) *err = TX_OPNAN ; + return TRUE; + } + return FALSE; +} + + +// fait l'opération sur 2 opérandes + +BOOL CBotTwoOpExpr::Execute(CBotStack* &pStack) +{ + CBotStack* pStk1 = pStack->AddStack(this); // ajoute un élément à la pile + // ou le retrouve en cas de reprise +// if ( pStk1 == EOX ) return TRUE; + + // selon la reprise, on peut être dans l'un des 2 états + + if ( pStk1->GivState() == 0 ) // 1er état, évalue l'opérande de gauche + { + if (!m_leftop->Execute(pStk1) ) return FALSE; // interrompu ici ? + + // pour les OU et ET logique, n'évalue pas la seconde expression si pas nécessaire + if ( (GivTokenType() == ID_LOG_AND || GivTokenType() == ID_TXT_AND ) && pStk1->GivVal() == FALSE ) + { + CBotVar* res = CBotVar::Create( (CBotToken*)NULL, CBotTypBoolean); + res->SetValInt(FALSE); + pStk1->SetVar(res); + return pStack->Return(pStk1); // transmet le résultat + } + if ( (GivTokenType() == ID_LOG_OR||GivTokenType() == ID_TXT_OR) && pStk1->GivVal() == TRUE ) + { + CBotVar* res = CBotVar::Create( (CBotToken*)NULL, CBotTypBoolean); + res->SetValInt(TRUE); + pStk1->SetVar(res); + return pStack->Return(pStk1); // transmet le résultat + } + + // passe à l'étape suivante + pStk1->SetState(1); // prêt pour la suite + } + + + // demande un peu plus de stack pour ne pas toucher le résultat de gauche + // qui se trouve sur la pile, justement. + + CBotStack* pStk2 = pStk1->AddStack(); // ajoute un élément à la pile + // ou le retrouve en cas de reprise + + // 2e état, évalue l'opérande de droite + if ( pStk2->GivState() == 0 ) + { + if ( !m_rightop->Execute(pStk2) ) return FALSE; // interrompu ici ? + pStk2->IncState(); + } + + CBotTypResult type1 = pStk1->GivTypResult(); // de quels types les résultats ? + CBotTypResult type2 = pStk2->GivTypResult(); + + CBotStack* pStk3 = pStk2->AddStack(this); // ajoute un élément à la pile + if ( pStk3->IfStep() ) return FALSE; // montre l'opération si step by step + + // crée une variable temporaire pour y mettre le résultat + // quel est le type du résultat ? + int TypeRes = MAX(type1.GivType(), type2.GivType()); + + if ( GivTokenType() == ID_ADD && type1.Eq(CBotTypString) ) + { + TypeRes = CBotTypString; + } + + switch ( GivTokenType() ) + { + case ID_LOG_OR: + case ID_LOG_AND: + case ID_TXT_OR: + case ID_TXT_AND: + case ID_EQ: + case ID_NE: + case ID_HI: + case ID_LO: + case ID_HS: + case ID_LS: + TypeRes = CBotTypBoolean; + break; + case ID_DIV: + TypeRes = MAX(TypeRes, CBotTypFloat); + } + + // crée une variable pour le résultat + CBotVar* result = CBotVar::Create( (CBotToken*)NULL, TypeRes); + + // crée une variable pour effectuer le calcul dans le type adapté + TypeRes = MAX(type1.GivType(), type2.GivType()); + + if ( GivTokenType() == ID_ADD && type1.Eq(CBotTypString) ) + { + TypeRes = CBotTypString; + } + + CBotVar* temp; + + if ( TypeRes == CBotTypPointer ) TypeRes = CBotTypNullPointer; + if ( TypeRes == CBotTypClass ) temp = CBotVar::Create( (CBotToken*)NULL, CBotTypResult(CBotTypIntrinsic, type1.GivClass() ) ); + else temp = CBotVar::Create( (CBotToken*)NULL, TypeRes ); + + int err = 0; + // fait l'opération selon la demande + CBotVar* left = pStk1->GivVar(); + CBotVar* right = pStk2->GivVar(); + + switch (GivTokenType()) + { + case ID_ADD: + if ( !IsNan(left, right, &err) ) result->Add(left , right); // additionne + break; + case ID_SUB: + if ( !IsNan(left, right, &err) ) result->Sub(left , right); // soustrait + break; + case ID_MUL: + if ( !IsNan(left, right, &err) ) result->Mul(left , right); // multiplie + break; + case ID_POWER: + if ( !IsNan(left, right, &err) ) result->Power(left , right); // puissance + break; + case ID_DIV: + if ( !IsNan(left, right, &err) ) err = result->Div(left , right);// divise + break; + case ID_MODULO: + if ( !IsNan(left, right, &err) ) err = result->Modulo(left , right);// reste de division + break; + case ID_LO: + if ( !IsNan(left, right, &err) ) + result->SetValInt(temp->Lo(left , right)); // inférieur + break; + case ID_HI: + if ( !IsNan(left, right, &err) ) + result->SetValInt(temp->Hi(left , right)); // supérieur + break; + case ID_LS: + if ( !IsNan(left, right, &err) ) + result->SetValInt(temp->Ls(left , right)); // inférieur ou égal + break; + case ID_HS: + if ( !IsNan(left, right, &err) ) + result->SetValInt(temp->Hs(left , right)); // supérieur ou égal + break; + case ID_EQ: + if ( IsNan(left, right) ) + result->SetValInt(left->GivInit() == right->GivInit()) ; + else + result->SetValInt(temp->Eq(left , right)); // égal + break; + case ID_NE: + if ( IsNan(left, right) ) + result->SetValInt(left ->GivInit() != right->GivInit()) ; + else + result->SetValInt(temp->Ne(left , right)); // différent + break; + case ID_TXT_AND: + case ID_LOG_AND: + case ID_AND: + if ( !IsNan(left, right, &err) ) result->And(left , right); // ET + break; + case ID_TXT_OR: + case ID_LOG_OR: + case ID_OR: + if ( !IsNan(left, right, &err) ) result->Or(left , right); // OU + break; + case ID_XOR: + if ( !IsNan(left, right, &err) ) result->XOr(left , right); // OU exclusif + break; + case ID_ASR: + if ( !IsNan(left, right, &err) ) result->ASR(left , right); + break; + case ID_SR: + if ( !IsNan(left, right, &err) ) result->SR(left , right); + break; + case ID_SL: + if ( !IsNan(left, right, &err) ) result->SL(left , right); + break; + default: + __asm int 3; + } + delete temp; + + pStk2->SetVar(result); // met le résultat sur la pile + if ( err ) pStk2->SetError(err, &m_token); // et l'erreur éventuelle (division par zéro) + +// pStk1->Return(pStk2); // libère la pile + return pStack->Return(pStk2); // transmet le résultat +} + +void CBotTwoOpExpr::RestoreState(CBotStack* &pStack, BOOL bMain) +{ + if ( !bMain ) return; + CBotStack* pStk1 = pStack->RestoreStack(this); // ajoute un élément à la pile + if ( pStk1 == NULL ) return; + + // selon la reprise, on peut être dans l'un des 2 états + + if ( pStk1->GivState() == 0 ) // 1er état, évalue l'opérande de gauche + { + m_leftop->RestoreState(pStk1, bMain); // interrompu ici ! + return; + } + + CBotStack* pStk2 = pStk1->RestoreStack(); // ajoute un élément à la pile + if ( pStk2 == NULL ) return; + + // 2e état, évalue l'opérande de droite + if ( pStk2->GivState() == 0 ) + { + m_rightop->RestoreState(pStk2, bMain); // interrompu ici ! + return; + } +} + + +BOOL CBotLogicExpr::Execute(CBotStack* &pStack) +{ + CBotStack* pStk1 = pStack->AddStack(this); // ajoute un élément à la pile + // ou le retrouve en cas de reprise +// if ( pStk1 == EOX ) return TRUE; + + if ( pStk1->GivState() == 0 ) + { + if ( !m_condition->Execute(pStk1) ) return FALSE; + if (!pStk1->SetState(1)) return FALSE; + } + + if ( pStk1->GivVal() == TRUE ) + { + if ( !m_op1->Execute(pStk1) ) return FALSE; + } + else + { + if ( !m_op2->Execute(pStk1) ) return FALSE; + } + + return pStack->Return(pStk1); // transmet le résultat +} + +void CBotLogicExpr::RestoreState(CBotStack* &pStack, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pStk1 = pStack->RestoreStack(this); // ajoute un élément à la pile + if ( pStk1 == NULL ) return; + + if ( pStk1->GivState() == 0 ) + { + m_condition->RestoreState(pStk1, bMain); + return; + } + + if ( pStk1->GivVal() == TRUE ) + { + m_op1->RestoreState(pStk1, bMain); + } + else + { + m_op2->RestoreState(pStk1, bMain); + } +} + +#if 0 +void t() +{ + int x,y; + 1>0 ? x = 0 : y = 0; +} +#endif + +#if 01 +void t(BOOL t) +{ + int x; + x = 1 + t ? 1 : 3 + 4 * 2 ; + t ? 0 : "test"; +} +#endif \ No newline at end of file diff --git a/src/CBot/CBotVar.cpp b/src/CBot/CBotVar.cpp new file mode 100644 index 00000000..bd5e889c --- /dev/null +++ b/src/CBot/CBotVar.cpp @@ -0,0 +1,2231 @@ +//////////////////////////////////////////////////////////////////// +// Définition pour la classe CBotVar +// gestion des variables du langage CBot + +// on ne crée jamais d'instance de la class mère CBotVar + + +#include "CBot.h" +#include +#include + +long CBotVar::m_identcpt = 0; + +CBotVar::CBotVar( ) +{ + m_next = NULL; + m_pMyThis = NULL; + m_pUserPtr = NULL; + m_InitExpr = NULL; + m_LimExpr = NULL; + m_type = -1; + m_binit = FALSE; + m_ident = 0; + m_bStatic = FALSE; + m_mPrivate = 0; +} + +CBotVarInt::CBotVarInt( const CBotToken* name ) +{ + m_token = new CBotToken(name); + m_next = NULL; + m_pMyThis = NULL; + m_pUserPtr = NULL; + m_InitExpr = NULL; + m_LimExpr = NULL; + m_type = CBotTypInt; + m_binit = FALSE; + m_bStatic = FALSE; + m_mPrivate = 0; + + m_val = 0; +} + +CBotVarFloat::CBotVarFloat( const CBotToken* name ) +{ + m_token = new CBotToken(name); + m_next = NULL; + m_pMyThis = NULL; + m_pUserPtr = NULL; + m_InitExpr = NULL; + m_LimExpr = NULL; + m_type = CBotTypFloat; + m_binit = FALSE; + m_bStatic = FALSE; + m_mPrivate = 0; + + m_val = 0; +} + +CBotVarString::CBotVarString( const CBotToken* name ) +{ + m_token = new CBotToken(name); + m_next = NULL; + m_pMyThis = NULL; + m_pUserPtr = NULL; + m_InitExpr = NULL; + m_LimExpr = NULL; + m_type = CBotTypString; + m_binit = FALSE; + m_bStatic = FALSE; + m_mPrivate = 0; + + m_val.Empty(); +} + +CBotVarBoolean::CBotVarBoolean( const CBotToken* name ) +{ + m_token = new CBotToken(name); + m_next = NULL; + m_pMyThis = NULL; + m_pUserPtr = NULL; + m_InitExpr = NULL; + m_LimExpr = NULL; + m_type = CBotTypBoolean; + m_binit = FALSE; + m_bStatic = FALSE; + m_mPrivate = 0; + + m_val = 0; +} + +CBotVarClass* CBotVarClass::m_ExClass = NULL; + +CBotVarClass::CBotVarClass( const CBotToken* name, CBotTypResult& type) +{ +/* +// int nIdent = 0; + InitCBotVarClass( name, type ) //, nIdent ); +} + +CBotVarClass::CBotVarClass( const CBotToken* name, CBotTypResult& type) //, int &nIdent ) +{ + InitCBotVarClass( name, type ); //, nIdent ); +} + +void CBotVarClass::InitCBotVarClass( const CBotToken* name, CBotTypResult& type ) //, int &nIdent ) +{*/ + if ( !type.Eq(CBotTypClass) && + !type.Eq(CBotTypIntrinsic) && // par comodité accepte ces types + !type.Eq(CBotTypPointer) && + !type.Eq(CBotTypArrayPointer) && + !type.Eq(CBotTypArrayBody)) __asm int 3; + + m_token = new CBotToken(name); + m_next = NULL; + m_pMyThis = NULL; + m_pUserPtr = OBJECTCREATED;//NULL; + m_InitExpr = NULL; + m_LimExpr = NULL; + m_pVar = NULL; + m_type = type; + if ( type.Eq(CBotTypArrayPointer) ) m_type.SetType( CBotTypArrayBody ); + else if ( !type.Eq(CBotTypArrayBody) ) m_type.SetType( CBotTypClass ); + // type officel pour cet object + + m_pClass = NULL; + m_pParent = NULL; + m_binit = FALSE; + m_bStatic = FALSE; + m_mPrivate = 0; + m_bConstructor = FALSE; + m_CptUse = 0; + m_ItemIdent = type.Eq(CBotTypIntrinsic) ? 0 : CBotVar::NextUniqNum(); + + // se place tout seul dans la liste + if (m_ExClass) m_ExClass->m_ExPrev = this; + m_ExNext = m_ExClass; + m_ExPrev = NULL; + m_ExClass = this; + + CBotClass* pClass = type.GivClass(); + CBotClass* pClass2 = pClass->GivParent(); + if ( pClass2 != NULL ) + { + // crée également une instance dans la classe père + m_pParent = new CBotVarClass(name, CBotTypResult(type.GivType(),pClass2) ); //, nIdent); + } + + SetClass( pClass ); //, nIdent ); + +} + +CBotVarClass::~CBotVarClass( ) +{ + if ( m_CptUse != 0 ) + __asm int 3; + + if ( m_pParent ) delete m_pParent; + m_pParent = NULL; + + // libère l'objet indirect s'il y a lieu +// if ( m_Indirect != NULL ) +// m_Indirect->DecrementUse(); + + // retire la classe de la liste + if ( m_ExPrev ) m_ExPrev->m_ExNext = m_ExNext; + else m_ExClass = m_ExNext; + + if ( m_ExNext ) m_ExNext->m_ExPrev = m_ExPrev; + m_ExPrev = NULL; + m_ExNext = NULL; + + delete m_pVar; +} + +void CBotVarClass::ConstructorSet() +{ + m_bConstructor = TRUE; +} + + +CBotVar::~CBotVar( ) +{ + delete m_token; + delete m_next; +} + +void CBotVar::debug() +{ + const char* p = (LPCTSTR) m_token->GivString(); + CBotString s = (LPCTSTR) GivValString(); + const char* v = (LPCTSTR) s; + + if ( m_type.Eq(CBotTypClass) ) + { + CBotVar* pv = ((CBotVarClass*)this)->m_pVar; + while (pv != NULL) + { + pv->debug(); + pv = pv->GivNext(); + } + } +} + +void CBotVar::ConstructorSet() +{ + // nop +} + +void CBotVar::SetUserPtr(void* pUser) +{ + m_pUserPtr = pUser; + if (m_type.Eq(CBotTypPointer) && + ((CBotVarPointer*)this)->m_pVarClass != NULL ) + ((CBotVarPointer*)this)->m_pVarClass->SetUserPtr(pUser); +} + +void CBotVar::SetIdent(long n) +{ + if (m_type.Eq(CBotTypPointer) && + ((CBotVarPointer*)this)->m_pVarClass != NULL ) + ((CBotVarPointer*)this)->m_pVarClass->SetIdent(n); +} + +void CBotVar::SetUniqNum(long n) +{ + m_ident = n; + + if ( n == 0 ) __asm int 3; +} + +long CBotVar::NextUniqNum() +{ + if (++m_identcpt < 10000) m_identcpt = 10000; + return m_identcpt; +} + +long CBotVar::GivUniqNum() +{ + return m_ident; +} + + +void* CBotVar::GivUserPtr() +{ + return m_pUserPtr; +} + +BOOL CBotVar::Save1State(FILE* pf) +{ + // cette routine "virtual" ne doit jamais être appellée, + // il doit y avoir une routine pour chaque classe fille (CBotVarInt, CBotVarFloat, etc) + // ( voir le type dans m_type ) + __asm int 3; + return FALSE; +} + +void CBotVar::Maj(void* pUser, BOOL bContinu) +{ +/* if (!bContinu && m_pMyThis != NULL) + m_pMyThis->Maj(pUser, TRUE);*/ +} + + +// crée une variable selon son type + +CBotVar* CBotVar::Create(const CBotToken* name, int type ) +{ + CBotTypResult t(type); + return Create(name, t); +} + +CBotVar* CBotVar::Create(const CBotToken* name, CBotTypResult type) +{ + switch (type.GivType()) + { + case CBotTypShort: + case CBotTypInt: + return new CBotVarInt(name); + case CBotTypFloat: + return new CBotVarFloat(name); + case CBotTypBoolean: + return new CBotVarBoolean(name); + case CBotTypString: + return new CBotVarString(name); + case CBotTypPointer: + case CBotTypNullPointer: + return new CBotVarPointer(name, type); + case CBotTypIntrinsic: + return new CBotVarClass(name, type); + + case CBotTypClass: + // crée une nouvelle instance d'une classe + // et retourne le POINTER sur cette instance + { + CBotVarClass* instance = new CBotVarClass(name, type); + CBotVarPointer* pointer = new CBotVarPointer(name, type); + pointer->SetPointer( instance ); + return pointer; + } + + case CBotTypArrayPointer: + return new CBotVarArray(name, type); + + case CBotTypArrayBody: + { + CBotVarClass* instance = new CBotVarClass(name, type); + CBotVarArray* array = new CBotVarArray(name, type); + array->SetPointer( instance ); + + CBotVar* pv = array; + while (type.Eq(CBotTypArrayBody)) + { + type = type.GivTypElem(); + pv = ((CBotVarArray*)pv)->GivItem(0, TRUE); // crée au moins l'élément [0] + } + + return array; + } + } + + __asm int 3; + return NULL; +} + +CBotVar* CBotVar::Create( CBotVar* pVar ) +{ + CBotVar* p = Create(pVar->m_token->GivString(), pVar->GivTypResult(2)); + return p; +} + + +CBotVar* CBotVar::Create( const char* n, CBotTypResult type) +{ + CBotToken name(n); + + switch (type.GivType()) + { + case CBotTypShort: + case CBotTypInt: + return new CBotVarInt(&name); + case CBotTypFloat: + return new CBotVarFloat(&name); + case CBotTypBoolean: + return new CBotVarBoolean(&name); + case CBotTypString: + return new CBotVarString(&name); + case CBotTypPointer: + case CBotTypNullPointer: + { + CBotVarPointer* p = new CBotVarPointer(&name, type); +// p->SetClass(type.GivClass()); + return p; + } + case CBotTypIntrinsic: + { + CBotVarClass* p = new CBotVarClass(&name, type); +// p->SetClass(type.GivClass()); + return p; + } + + case CBotTypClass: + // crée une nouvelle instance d'une classe + // et retourne le POINTER sur cette instance + { + CBotVarClass* instance = new CBotVarClass(&name, type); + CBotVarPointer* pointer = new CBotVarPointer(&name, type); + pointer->SetPointer( instance ); +// pointer->SetClass( type.GivClass() ); + return pointer; + } + + case CBotTypArrayPointer: + return new CBotVarArray(&name, type); + + case CBotTypArrayBody: + { + CBotVarClass* instance = new CBotVarClass(&name, type); + CBotVarArray* array = new CBotVarArray(&name, type); + array->SetPointer( instance ); + + CBotVar* pv = array; + while (type.Eq(CBotTypArrayBody)) + { + type = type.GivTypElem(); + pv = ((CBotVarArray*)pv)->GivItem(0, TRUE); // crée au moins l'élément [0] + } + + return array; + } + } + + __asm int 3; + return NULL; +} + +CBotVar* CBotVar::Create( const char* name, int type, CBotClass* pClass) +{ + CBotToken token( name, "" ); + CBotVar* pVar = Create( &token, type ); + + if ( type == CBotTypPointer && pClass == NULL ) // pointeur "null" ? + return pVar; + + if ( type == CBotTypClass || type == CBotTypPointer || + type == CBotTypIntrinsic ) + { + if (pClass == NULL) + { + delete pVar; + return NULL; + } + pVar->SetClass( pClass ); + } + return pVar; +} + +CBotVar* CBotVar::Create( const char* name, CBotClass* pClass) +{ + CBotToken token( name, "" ); + CBotVar* pVar = Create( &token, CBotTypResult( CBotTypClass, pClass ) ); +// pVar->SetClass( pClass ); + return pVar; +} + +CBotTypResult CBotVar::GivTypResult(int mode) +{ + CBotTypResult r = m_type; + + if ( mode == 1 && m_type.Eq(CBotTypClass) ) + r.SetType(CBotTypPointer); + if ( mode == 2 && m_type.Eq(CBotTypClass) ) + r.SetType(CBotTypIntrinsic); + + return r; +} + +int CBotVar::GivType(int mode) +{ + if ( mode == 1 && m_type.Eq(CBotTypClass) ) + return CBotTypPointer; + if ( mode == 2 && m_type.Eq(CBotTypClass) ) + return CBotTypIntrinsic; + return m_type.GivType(); +} + +void CBotVar::SetType(CBotTypResult& type) +{ + m_type = type; +} + + +int CBotVar::GivInit() +{ + if ( m_type.Eq(CBotTypClass) ) return IS_DEF; // toujours défini ! + + return m_binit; +} + +void CBotVar::SetInit(BOOL bInit) +{ + m_binit = bInit; + if ( bInit == 2 ) m_binit = IS_DEF; // cas spécial + + if ( m_type.Eq(CBotTypPointer) && bInit == 2 ) + { + CBotVarClass* instance = GivPointer(); + if ( instance == NULL ) + { + instance = new CBotVarClass(NULL, m_type); +// instance->SetClass(((CBotVarPointer*)this)->m_pClass); + SetPointer(instance); + } + instance->SetInit(1); + } + + if ( m_type.Eq(CBotTypClass) || m_type.Eq(CBotTypIntrinsic) ) + { + CBotVar* p = ((CBotVarClass*)this)->m_pVar; + while( p != NULL ) + { + p->SetInit( bInit ); + p->m_pMyThis = (CBotVarClass*)this; + p = p->GivNext(); + } + } +} + +CBotString CBotVar::GivName() +{ + return m_token->GivString(); +} + +void CBotVar::SetName(const char* name) +{ + m_token->SetString(name); +} + +CBotToken* CBotVar::GivToken() +{ + return m_token; +} + +CBotVar* CBotVar::GivItem(const char* name) +{ + __asm int 3; + return NULL; +} + +CBotVar* CBotVar::GivItemRef(int nIdent) +{ + __asm int 3; + return NULL; +} + +CBotVar* CBotVar::GivItemList() +{ + __asm int 3; + return NULL; +} + +CBotVar* CBotVar::GivItem(int row, BOOL bGrow) +{ + __asm int 3; + return NULL; +} + +// dit si une variable appartient à une classe donnée +BOOL CBotVar::IsElemOfClass(const char* name) +{ + CBotClass* pc = NULL; + + if ( m_type.Eq(CBotTypPointer) ) + { + pc = ((CBotVarPointer*)this)->m_pClass; + } + if ( m_type.Eq(CBotTypClass) ) + { + pc = ((CBotVarClass*)this)->m_pClass; + } + + while ( pc != NULL ) + { + if ( pc->GivName() == name ) return TRUE; + pc = pc->GivParent(); + } + + return FALSE; +} + + +CBotVar* CBotVar::GivStaticVar() +{ + // rend le pointeur à la variable si elle est statique + if ( m_bStatic == 0 || m_pMyThis == NULL ) return this; + + CBotClass* pClass = m_pMyThis->GivClass(); + return pClass->GivItem( m_token->GivString() ); +} + + +CBotVar* CBotVar::GivNext() +{ + return m_next; +} + +void CBotVar::AddNext(CBotVar* pVar) +{ + CBotVar* p = this; + while (p->m_next != NULL) p = p->m_next; + + p->m_next = pVar; +} + +void CBotVar::SetVal(CBotVar* var) +{ + switch (/*var->*/GivType()) + { + case CBotTypBoolean: + SetValInt(var->GivValInt()); + break; + case CBotTypInt: + SetValInt(var->GivValInt(), ((CBotVarInt*)var)->m_defnum); + break; + case CBotTypFloat: + SetValFloat(var->GivValFloat()); + break; + case CBotTypString: + SetValString(var->GivValString()); + break; + case CBotTypPointer: + case CBotTypNullPointer: + case CBotTypArrayPointer: + SetPointer(var->GivPointer()); + break; + case CBotTypClass: + { + delete ((CBotVarClass*)this)->m_pVar; + ((CBotVarClass*)this)->m_pVar = NULL; + Copy(var, FALSE); + } + break; + default: + __asm int 3; + } + + m_binit = var->m_binit; // copie l'état nan s'il y a +} + +void CBotVar::SetStatic(BOOL bStatic) +{ + m_bStatic = bStatic; +} + +void CBotVar::SetPrivate(int mPrivate) +{ + m_mPrivate = mPrivate; +} + +BOOL CBotVar::IsStatic() +{ + return m_bStatic; +} + +BOOL CBotVar::IsPrivate(int mode) +{ + return m_mPrivate >= mode; +} + +int CBotVar::GivPrivate() +{ + return m_mPrivate; +} + + +void CBotVar::SetPointer(CBotVar* pVarClass) +{ + __asm int 3; +} + +CBotVarClass* CBotVar::GivPointer() +{ + __asm int 3; + return NULL; +} + +// toutes ces fonctions doivent être définies dans les classes filles +// dérivées de la classe CBotVar + +int CBotVar::GivValInt() +{ + __asm int 3; + return 0; +} + +float CBotVar::GivValFloat() +{ + __asm int 3; + return 0; +} + +void CBotVar::SetValInt(int c, const char* s) +{ + __asm int 3; +} + +void CBotVar::SetValFloat(float c) +{ + __asm int 3; +} + +void CBotVar::Mul(CBotVar* left, CBotVar* right) +{ + __asm int 3; +} + +void CBotVar::Power(CBotVar* left, CBotVar* right) +{ + __asm int 3; +} + +int CBotVar::Div(CBotVar* left, CBotVar* right) +{ + __asm int 3; + return 0; +} + +int CBotVar::Modulo(CBotVar* left, CBotVar* right) +{ + __asm int 3; + return 0; +} + +void CBotVar::Add(CBotVar* left, CBotVar* right) +{ + __asm int 3; +} + +void CBotVar::Sub(CBotVar* left, CBotVar* right) +{ + __asm int 3; +} + +BOOL CBotVar::Lo(CBotVar* left, CBotVar* right) +{ + __asm int 3; + return FALSE; +} + +BOOL CBotVar::Hi(CBotVar* left, CBotVar* right) +{ + __asm int 3; + return FALSE; +} + +BOOL CBotVar::Ls(CBotVar* left, CBotVar* right) +{ + __asm int 3; + return FALSE; +} + +BOOL CBotVar::Hs(CBotVar* left, CBotVar* right) +{ + __asm int 3; + return FALSE; +} + +BOOL CBotVar::Eq(CBotVar* left, CBotVar* right) +{ + __asm int 3; + return FALSE; +} + +BOOL CBotVar::Ne(CBotVar* left, CBotVar* right) +{ + __asm int 3; + return FALSE; +} + +void CBotVar::And(CBotVar* left, CBotVar* right) +{ + __asm int 3; +} + +void CBotVar::Or(CBotVar* left, CBotVar* right) +{ + __asm int 3; +} + +void CBotVar::XOr(CBotVar* left, CBotVar* right) +{ + __asm int 3; +} + +void CBotVar::ASR(CBotVar* left, CBotVar* right) +{ + __asm int 3; +} + +void CBotVar::SR(CBotVar* left, CBotVar* right) +{ + __asm int 3; +} + +void CBotVar::SL(CBotVar* left, CBotVar* right) +{ + __asm int 3; +} + +void CBotVar::Neg() +{ + __asm int 3; +} + +void CBotVar::Not() +{ + __asm int 3; +} + +void CBotVar::Inc() +{ + __asm int 3; +} +void CBotVar::Dec() +{ + __asm int 3; +} + +void CBotVar::Copy(CBotVar* pSrc, BOOL bName) +{ + __asm int 3; +} + +void CBotVar::SetValString(const char* p) +{ + __asm int 3; +} + +CBotString CBotVar::GivValString() +{ + __asm int 3; + return CBotString(); +} + +void CBotVar::SetClass(CBotClass* pClass) +{ + __asm int 3; +} + +CBotClass* CBotVar::GivClass() +{ + __asm int 3; + return NULL; +} + +/* +void CBotVar::SetIndirection(CBotVar* pVar) +{ + // nop, uniquement pour CBotVarPointer::SetIndirection +} +*/ + +////////////////////////////////////////////////////////////////////////////////////// + +// copie une variable dans une autre +void CBotVarInt::Copy(CBotVar* pSrc, BOOL bName) +{ + CBotVarInt* p = (CBotVarInt*)pSrc; + + if ( bName) *m_token = *p->m_token; + m_type = p->m_type; + m_val = p->m_val; + m_binit = p->m_binit; + m_pMyThis = NULL; + m_pUserPtr = p->m_pUserPtr; + + // garde le même idendificateur (par défaut) + if (m_ident == 0 ) m_ident = p->m_ident; + + m_defnum = p->m_defnum; +} + + + + +void CBotVarInt::SetValInt(int val, const char* defnum) +{ + m_val = val; + m_binit = TRUE; + m_defnum = defnum; +} + + + +void CBotVarInt::SetValFloat(float val) +{ + m_val = (int)val; + m_binit = TRUE; +} + +int CBotVarInt::GivValInt() +{ + return m_val; +} + +float CBotVarInt::GivValFloat() +{ + return (float)m_val; +} + +CBotString CBotVarInt::GivValString() +{ + if ( !m_defnum.IsEmpty() ) return m_defnum; + + CBotString res; + + if ( !m_binit ) + { + res.LoadString(TX_UNDEF); + return res; + } + if ( m_binit == IS_NAN ) + { + res.LoadString(TX_NAN); + return res; + } + + char buffer[300]; + sprintf(buffer, "%d", m_val); + res = buffer; + + return res; +} + + +void CBotVarInt::Mul(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValInt() * right->GivValInt(); + m_binit = TRUE; +} + +void CBotVarInt::Power(CBotVar* left, CBotVar* right) +{ + m_val = (int) pow( left->GivValInt() , right->GivValInt() ); + m_binit = TRUE; +} + +int CBotVarInt::Div(CBotVar* left, CBotVar* right) +{ + int r = right->GivValInt(); + if ( r != 0 ) + { + m_val = left->GivValInt() / r; + m_binit = TRUE; + } + return ( r == 0 ? TX_DIVZERO : 0 ); +} + +int CBotVarInt::Modulo(CBotVar* left, CBotVar* right) +{ + int r = right->GivValInt(); + if ( r != 0 ) + { + m_val = left->GivValInt() % r; + m_binit = TRUE; + } + return ( r == 0 ? TX_DIVZERO : 0 ); +} + +void CBotVarInt::Add(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValInt() + right->GivValInt(); + m_binit = TRUE; +} + +void CBotVarInt::Sub(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValInt() - right->GivValInt(); + m_binit = TRUE; +} + +void CBotVarInt::XOr(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValInt() ^ right->GivValInt(); + m_binit = TRUE; +} + +void CBotVarInt::And(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValInt() & right->GivValInt(); + m_binit = TRUE; +} + +void CBotVarInt::Or(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValInt() | right->GivValInt(); + m_binit = TRUE; +} + +void CBotVarInt::SL(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValInt() << right->GivValInt(); + m_binit = TRUE; +} + +void CBotVarInt::ASR(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValInt() >> right->GivValInt(); + m_binit = TRUE; +} + +void CBotVarInt::SR(CBotVar* left, CBotVar* right) +{ + int source = left->GivValInt(); + int shift = right->GivValInt(); + if (shift>=1) source &= 0x7fffffff; + m_val = source >> shift; + m_binit = TRUE; +} + +void CBotVarInt::Neg() +{ + m_val = -m_val; +} + +void CBotVarInt::Not() +{ + m_val = ~m_val; +} + +void CBotVarInt::Inc() +{ + m_val++; + m_defnum.Empty(); +} + +void CBotVarInt::Dec() +{ + m_val--; + m_defnum.Empty(); +} + +BOOL CBotVarInt::Lo(CBotVar* left, CBotVar* right) +{ + return left->GivValInt() < right->GivValInt(); +} + +BOOL CBotVarInt::Hi(CBotVar* left, CBotVar* right) +{ + return left->GivValInt() > right->GivValInt(); +} + +BOOL CBotVarInt::Ls(CBotVar* left, CBotVar* right) +{ + return left->GivValInt() <= right->GivValInt(); +} + +BOOL CBotVarInt::Hs(CBotVar* left, CBotVar* right) +{ + return left->GivValInt() >= right->GivValInt(); +} + +BOOL CBotVarInt::Eq(CBotVar* left, CBotVar* right) +{ + return left->GivValInt() == right->GivValInt(); +} + +BOOL CBotVarInt::Ne(CBotVar* left, CBotVar* right) +{ + return left->GivValInt() != right->GivValInt(); +} + + +////////////////////////////////////////////////////////////////////////////////////// + +// copie une variable dans une autre +void CBotVarFloat::Copy(CBotVar* pSrc, BOOL bName) +{ + CBotVarFloat* p = (CBotVarFloat*)pSrc; + + if (bName) *m_token = *p->m_token; + m_type = p->m_type; + m_val = p->m_val; + m_binit = p->m_binit; +//- m_bStatic = p->m_bStatic; + m_next = NULL; + m_pMyThis = NULL;//p->m_pMyThis; + m_pUserPtr = p->m_pUserPtr; + + // garde le même idendificateur (par défaut) + if (m_ident == 0 ) m_ident = p->m_ident; +} + + + + +void CBotVarFloat::SetValInt(int val, const char* s) +{ + m_val = (float)val; + m_binit = TRUE; +} + +void CBotVarFloat::SetValFloat(float val) +{ + m_val = val; + m_binit = TRUE; +} + +int CBotVarFloat::GivValInt() +{ + return (int)m_val; +} + +float CBotVarFloat::GivValFloat() +{ + return m_val; +} + +CBotString CBotVarFloat::GivValString() +{ + CBotString res; + + if ( !m_binit ) + { + res.LoadString(TX_UNDEF); + return res; + } + if ( m_binit == IS_NAN ) + { + res.LoadString(TX_NAN); + return res; + } + + char buffer[300]; + sprintf(buffer, "%.2f", m_val); + res = buffer; + + return res; +} + + +void CBotVarFloat::Mul(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValFloat() * right->GivValFloat(); + m_binit = TRUE; +} + +void CBotVarFloat::Power(CBotVar* left, CBotVar* right) +{ + m_val = (float)pow( left->GivValFloat() , right->GivValFloat() ); + m_binit = TRUE; +} + +int CBotVarFloat::Div(CBotVar* left, CBotVar* right) +{ + float r = right->GivValFloat(); + if ( r != 0 ) + { + m_val = left->GivValFloat() / r; + m_binit = TRUE; + } + return ( r == 0 ? TX_DIVZERO : 0 ); +} + +int CBotVarFloat::Modulo(CBotVar* left, CBotVar* right) +{ + float r = right->GivValFloat(); + if ( r != 0 ) + { + m_val = (float)fmod( left->GivValFloat() , r ); + m_binit = TRUE; + } + return ( r == 0 ? TX_DIVZERO : 0 ); +} + +void CBotVarFloat::Add(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValFloat() + right->GivValFloat(); + m_binit = TRUE; +} + +void CBotVarFloat::Sub(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValFloat() - right->GivValFloat(); + m_binit = TRUE; +} + +void CBotVarFloat::Neg() +{ + m_val = -m_val; +} + +void CBotVarFloat::Inc() +{ + m_val++; +} + +void CBotVarFloat::Dec() +{ + m_val--; +} + + +BOOL CBotVarFloat::Lo(CBotVar* left, CBotVar* right) +{ + return left->GivValFloat() < right->GivValFloat(); +} + +BOOL CBotVarFloat::Hi(CBotVar* left, CBotVar* right) +{ + return left->GivValFloat() > right->GivValFloat(); +} + +BOOL CBotVarFloat::Ls(CBotVar* left, CBotVar* right) +{ + return left->GivValFloat() <= right->GivValFloat(); +} + +BOOL CBotVarFloat::Hs(CBotVar* left, CBotVar* right) +{ + return left->GivValFloat() >= right->GivValFloat(); +} + +BOOL CBotVarFloat::Eq(CBotVar* left, CBotVar* right) +{ + return left->GivValFloat() == right->GivValFloat(); +} + +BOOL CBotVarFloat::Ne(CBotVar* left, CBotVar* right) +{ + return left->GivValFloat() != right->GivValFloat(); +} + + +////////////////////////////////////////////////////////////////////////////////////// + +// copie une variable dans une autre +void CBotVarBoolean::Copy(CBotVar* pSrc, BOOL bName) +{ + CBotVarBoolean* p = (CBotVarBoolean*)pSrc; + + if (bName) *m_token = *p->m_token; + m_type = p->m_type; + m_val = p->m_val; + m_binit = p->m_binit; +//- m_bStatic = p->m_bStatic; + m_next = NULL; + m_pMyThis = NULL;//p->m_pMyThis; + m_pUserPtr = p->m_pUserPtr; + + // garde le même idendificateur (par défaut) + if (m_ident == 0 ) m_ident = p->m_ident; +} + + + + +void CBotVarBoolean::SetValInt(int val, const char* s) +{ + m_val = (BOOL)val; + m_binit = TRUE; +} + +void CBotVarBoolean::SetValFloat(float val) +{ + m_val = (BOOL)val; + m_binit = TRUE; +} + +int CBotVarBoolean::GivValInt() +{ + return m_val; +} + +float CBotVarBoolean::GivValFloat() +{ + return (float)m_val; +} + +CBotString CBotVarBoolean::GivValString() +{ + CBotString ret; + + CBotString res; + + if ( !m_binit ) + { + res.LoadString(TX_UNDEF); + return res; + } + if ( m_binit == IS_NAN ) + { + res.LoadString(TX_NAN); + return res; + } + + ret.LoadString( m_val > 0 ? ID_TRUE : ID_FALSE ); + return ret; +} + +void CBotVarBoolean::And(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValInt() && right->GivValInt(); + m_binit = TRUE; +} +void CBotVarBoolean::Or(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValInt() || right->GivValInt(); + m_binit = TRUE; +} + +void CBotVarBoolean::XOr(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValInt() ^ right->GivValInt(); + m_binit = TRUE; +} + +void CBotVarBoolean::Not() +{ + m_val = m_val ? FALSE : TRUE ; +} + +BOOL CBotVarBoolean::Eq(CBotVar* left, CBotVar* right) +{ + return left->GivValInt() == right->GivValInt(); +} + +BOOL CBotVarBoolean::Ne(CBotVar* left, CBotVar* right) +{ + return left->GivValInt() != right->GivValInt(); +} + +////////////////////////////////////////////////////////////////////////////////////// + +// copie une variable dans une autre +void CBotVarString::Copy(CBotVar* pSrc, BOOL bName) +{ + CBotVarString* p = (CBotVarString*)pSrc; + + if (bName) *m_token = *p->m_token; + m_type = p->m_type; + m_val = p->m_val; + m_binit = p->m_binit; +//- m_bStatic = p->m_bStatic; + m_next = NULL; + m_pMyThis = NULL;//p->m_pMyThis; + m_pUserPtr = p->m_pUserPtr; + + // garde le même idendificateur (par défaut) + if (m_ident == 0 ) m_ident = p->m_ident; +} + + +void CBotVarString::SetValString(const char* p) +{ + m_val = p; + m_binit = TRUE; +} + +CBotString CBotVarString::GivValString() +{ + if ( !m_binit ) + { + CBotString res; + res.LoadString(TX_UNDEF); + return res; + } + if ( m_binit == IS_NAN ) + { + CBotString res; + res.LoadString(TX_NAN); + return res; + } + + return m_val; +} + + +void CBotVarString::Add(CBotVar* left, CBotVar* right) +{ + m_val = left->GivValString() + right->GivValString(); + m_binit = TRUE; +} + +BOOL CBotVarString::Eq(CBotVar* left, CBotVar* right) +{ + return (left->GivValString() == right->GivValString()); +} + +BOOL CBotVarString::Ne(CBotVar* left, CBotVar* right) +{ + return (left->GivValString() != right->GivValString()); +} + + +BOOL CBotVarString::Lo(CBotVar* left, CBotVar* right) +{ + return (left->GivValString() == right->GivValString()); +} + +BOOL CBotVarString::Hi(CBotVar* left, CBotVar* right) +{ + return (left->GivValString() == right->GivValString()); +} + +BOOL CBotVarString::Ls(CBotVar* left, CBotVar* right) +{ + return (left->GivValString() == right->GivValString()); +} + +BOOL CBotVarString::Hs(CBotVar* left, CBotVar* right) +{ + return (left->GivValString() == right->GivValString()); +} + + +//////////////////////////////////////////////////////////////// + +// copie une variable dans une autre +void CBotVarClass::Copy(CBotVar* pSrc, BOOL bName) +{ + pSrc = pSrc->GivPointer(); // si source donné par un pointeur + + if ( pSrc->GivType() != CBotTypClass ) + __asm int 3; + + CBotVarClass* p = (CBotVarClass*)pSrc; + + if (bName) *m_token = *p->m_token; + + m_type = p->m_type; + m_binit = p->m_binit; +//- m_bStatic = p->m_bStatic; + m_pClass = p->m_pClass; + if ( p->m_pParent ) + { +__asm int 3; "que faire du pParent"; + } + +// m_next = NULL; + m_pUserPtr = p->m_pUserPtr; + m_pMyThis = NULL;//p->m_pMyThis; + m_ItemIdent = p->m_ItemIdent; + + // garde le même idendificateur (par défaut) + if (m_ident == 0 ) m_ident = p->m_ident; + + delete m_pVar; + m_pVar = NULL; + + CBotVar* pv = p->m_pVar; + while( pv != NULL ) + { + CBotVar* pn = CBotVar::Create(pv); + pn->Copy( pv ); + if ( m_pVar == NULL ) m_pVar = pn; + else m_pVar->AddNext(pn); + + pv = pv->GivNext(); + } +} + +void CBotVarClass::SetItemList(CBotVar* pVar) +{ + delete m_pVar; + m_pVar = pVar; // remplace le pointeur existant +} + +void CBotVarClass::SetIdent(long n) +{ + m_ItemIdent = n; +} + +void CBotVarClass::SetClass(CBotClass* pClass)//, int &nIdent) +{ + m_type.m_pClass = pClass; + + if ( m_pClass == pClass ) return; + + m_pClass = pClass; + + // initialise les variables associées à cette classe + delete m_pVar; + m_pVar = NULL; + + if (pClass == NULL) return; + + CBotVar* pv = pClass->GivVar(); // premier de la liste + while ( pv != NULL ) + { + // cherche les dimensions max du tableau + CBotInstr* p = pv->m_LimExpr; // les différentes formules + if ( p != NULL ) + { + CBotStack* pile = CBotStack::FirstStack(); // une pile indépendante + int n = 0; + int max[100]; + + while (p != NULL) + { + while( pile->IsOk() && !p->Execute(pile) ) ; // calcul de la taille sans interruptions + CBotVar* v = pile->GivVar(); // résultat + max[n] = v->GivValInt(); // valeur + n++; + p = p->GivNext3(); + } + while (n<100) max[n++] = 0; + + pv->m_type.SetArray( max ); // mémorise les limitations + pile->Delete(); + } + + CBotVar* pn = CBotVar::Create( pv ); // une copie + pn->SetStatic(pv->IsStatic()); + pn->SetPrivate(pv->GivPrivate()); + + if ( pv->m_InitExpr != NULL ) // expression pour l'initialisation ? + { +#if STACKMEM + CBotStack* pile = CBotStack::FirstStack(); // une pile indépendante + + while(pile->IsOk() && !pv->m_InitExpr->Execute(pile, pn)); // évalue l'expression sans timer + + pile->Delete(); +#else + CBotStack* pile = new CBotStack(NULL); // une pile indépendante + while(!pv->m_InitExpr->Execute(pile)); // évalue l'expression sans timer + pn->SetVal( pile->GivVar() ) ; + delete pile; +#endif + } + +// pn->SetUniqNum(CBotVar::NextUniqNum()); // numérote les éléments + pn->SetUniqNum(pv->GivUniqNum()); //++nIdent + pn->m_pMyThis = this; + + if ( m_pVar == NULL) m_pVar = pn; + else m_pVar->AddNext( pn ); + pv = pv->GivNext(); + } +} + +CBotClass* CBotVarClass::GivClass() +{ + return m_pClass; +} + + +void CBotVarClass::Maj(void* pUser, BOOL bContinu) +{ +/* if (!bContinu && m_pMyThis != NULL) + m_pMyThis->Maj(pUser, TRUE);*/ + + // une routine de mise à jour existe-elle ? + + if ( m_pClass->m_rMaj == NULL ) return; + + // récupère le pointeur user selon la classe + // ou selon le paramètre passé au CBotProgram::Run() + + if ( m_pUserPtr != NULL) pUser = m_pUserPtr; + if ( pUser == OBJECTDELETED || + pUser == OBJECTCREATED ) return; + m_pClass->m_rMaj( this, pUser ); +} + +CBotVar* CBotVarClass::GivItem(const char* name) +{ + CBotVar* p = m_pVar; + + while ( p != NULL ) + { + if ( p->GivName() == name ) return p; + p = p->GivNext(); + } + + if ( m_pParent != NULL ) return m_pParent->GivItem(name); + return NULL; +} + +CBotVar* CBotVarClass::GivItemRef(int nIdent) +{ + CBotVar* p = m_pVar; + + while ( p != NULL ) + { + if ( p->GivUniqNum() == nIdent ) return p; + p = p->GivNext(); + } + + if ( m_pParent != NULL ) return m_pParent->GivItemRef(nIdent); + return NULL; +} + +// pour la gestion d'un tableau +// bExtend permet d'agrandir le tableau, mais pas au dela de la taille fixée par SetArray() + +CBotVar* CBotVarClass::GivItem(int n, BOOL bExtend) +{ + CBotVar* p = m_pVar; + + if ( n < 0 ) return NULL; + if ( n > MAXARRAYSIZE ) return NULL; + + if ( m_type.GivLimite() >= 0 && n >= m_type.GivLimite() ) return NULL; + + if ( p == NULL && bExtend ) + { + p = CBotVar::Create("", m_type.GivTypElem()); + m_pVar = p; + } + + if ( n == 0 ) return p; + + while ( n-- > 0 ) + { + if ( p->m_next == NULL ) + { + if ( bExtend ) p->m_next = CBotVar::Create("", m_type.GivTypElem()); + if ( p->m_next == NULL ) return NULL; + } + p = p->m_next; + } + + return p; +} + +CBotVar* CBotVarClass::GivItemList() +{ + return m_pVar; +} + + +CBotString CBotVarClass::GivValString() +{ +// if ( m_Indirect != NULL) return m_Indirect->GivValString(); + + CBotString res; + + if ( m_pClass != NULL ) // pas utilisé pour un array + { + res = m_pClass->GivName() + "( "; + + CBotVarClass* my = this; + while ( my != NULL ) + { + CBotVar* pv = my->m_pVar; + while ( pv != NULL ) + { + res += pv->GivName() + "="; + + if ( pv->IsStatic() ) + { + CBotVar* pvv = my->m_pClass->GivItem(pv->GivName()); + res += pvv->GivValString(); + } + else + { + res += pv->GivValString(); + } + pv = pv->GivNext(); + if ( pv != NULL ) res += ", "; + } + my = my->m_pParent; + if ( my != NULL ) + { + res += ") extends "; + res += my->m_pClass->GivName(); + res += " ("; + } + } + } + else + { + res = "( "; + + CBotVar* pv = m_pVar; + while ( pv != NULL ) + { + res += pv->GivValString(); + if ( pv->GivNext() != NULL ) res += ", "; + pv = pv->GivNext(); + } + } + + res += " )"; + return res; +} + +void CBotVarClass::IncrementUse() +{ + m_CptUse++; +} + +void CBotVarClass::DecrementUse() +{ + m_CptUse--; + if ( m_CptUse == 0 ) + { + // s'il y en a un, appel le destructeur + // mais seulement si un constructeur avait été appelé. + if ( m_bConstructor ) + { + m_CptUse++; // ne revient pas dans le destructeur + + // m_error est static dans le stack + // sauve la valeur pour la remettre ensuite + int err, start, end; + CBotStack* pile = NULL; + err = pile->GivError(start,end); // pile == NULL ça ne derange pas !! + + pile = CBotStack::FirstStack(); // efface l'erreur + CBotVar* ppVars[1]; + ppVars[0] = NULL; + + CBotVar* pThis = CBotVar::Create("this", CBotTypNullPointer); + pThis->SetPointer(this); + CBotVar* pResult = NULL; + + CBotString nom = "~" + m_pClass->GivName(); + long ident = 0; + + while ( pile->IsOk() && !m_pClass->ExecuteMethode(ident, nom, pThis, ppVars, pResult, pile, NULL)) ; // attend la fin + + pile->ResetError(err, start,end); + + pile->Delete(); + delete pThis; + m_CptUse--; + } + + delete this; // s'auto-détruit !! + } +} + +CBotVarClass* CBotVarClass::GivPointer() +{ + return this; +} + + +// trouve une instance selon son numéro unique + +CBotVarClass* CBotVarClass::Find(long id) +{ + CBotVarClass* p = m_ExClass; + + while ( p != NULL ) + { + if ( p->m_ItemIdent == id ) return p; + p = p->m_ExNext; + } + + return NULL; +} + +BOOL CBotVarClass::Eq(CBotVar* left, CBotVar* right) +{ + CBotVar* l = left->GivItemList(); + CBotVar* r = right->GivItemList(); + + while ( l != NULL && r != NULL ) + { + if ( l->Ne(l, r) ) return FALSE; + l = l->GivNext(); + r = r->GivNext(); + } + + // devrait toujours arrivé simultanément au bout (mêmes classes) + return l == r; +} + +BOOL CBotVarClass::Ne(CBotVar* left, CBotVar* right) +{ + CBotVar* l = left->GivItemList(); + CBotVar* r = right->GivItemList(); + + while ( l != NULL && r != NULL ) + { + if ( l->Ne(l, r) ) return TRUE; + l = l->GivNext(); + r = r->GivNext(); + } + + // devrait toujours arrivé simultanément au bout (mêmes classes) + return l != r; +} + +///////////////////////////////////////////////////////////////////////////// +// gestion des tableaux de variables + +CBotVarArray::CBotVarArray(const CBotToken* name, CBotTypResult& type ) +{ + if ( !type.Eq(CBotTypArrayPointer) && + !type.Eq(CBotTypArrayBody)) __asm int 3; + + m_token = new CBotToken(name); + m_next = NULL; + m_pMyThis = NULL; + m_pUserPtr = NULL; + + m_type = type; + m_type.SetType(CBotTypArrayPointer); + m_binit = FALSE; + + m_pInstance = NULL; // la liste des éléments du tableau +} + +CBotVarArray::~CBotVarArray() +{ + if ( m_pInstance != NULL ) m_pInstance->DecrementUse(); // une référence en moins +} + +// copie une variable dans une autre +void CBotVarArray::Copy(CBotVar* pSrc, BOOL bName) +{ + if ( pSrc->GivType() != CBotTypArrayPointer ) + __asm int 3; + + CBotVarArray* p = (CBotVarArray*)pSrc; + + if ( bName) *m_token = *p->m_token; + m_type = p->m_type; + m_pInstance = p->GivPointer(); + + if ( m_pInstance != NULL ) + m_pInstance->IncrementUse(); // une référence en plus + + m_binit = p->m_binit; +//- m_bStatic = p->m_bStatic; + m_pMyThis = NULL;//p->m_pMyThis; + m_pUserPtr = p->m_pUserPtr; + + // garde le même idendificateur (par défaut) + if (m_ident == 0 ) m_ident = p->m_ident; +} + +void CBotVarArray::SetPointer(CBotVar* pVarClass) +{ + m_binit = TRUE; // init, même sur un pointeur null + + if ( m_pInstance == pVarClass) return; // spécial, ne pas décrémenter et réincrémenter + // car le décrément peut détruire l'object + + if ( pVarClass != NULL ) + { + if ( pVarClass->GivType() == CBotTypArrayPointer ) + pVarClass = pVarClass->GivPointer(); // le vrai pointeur à l'objet + + if ( !pVarClass->m_type.Eq(CBotTypClass) && + !pVarClass->m_type.Eq(CBotTypArrayBody)) + __asm int 3; + + ((CBotVarClass*)pVarClass)->IncrementUse(); // une référence en plus + } + + if ( m_pInstance != NULL ) m_pInstance->DecrementUse(); + m_pInstance = (CBotVarClass*)pVarClass; +} + + +CBotVarClass* CBotVarArray::GivPointer() +{ + if ( m_pInstance == NULL ) return NULL; + return m_pInstance->GivPointer(); +} + +CBotVar* CBotVarArray::GivItem(int n, BOOL bExtend) +{ + if ( m_pInstance == NULL ) + { + if ( !bExtend ) return NULL; + // crée une instance pour le tableau + + CBotVarClass* instance = new CBotVarClass(NULL, m_type); + SetPointer( instance ); + } + return m_pInstance->GivItem(n, bExtend); +} + +CBotVar* CBotVarArray::GivItemList() +{ + if ( m_pInstance == NULL) return NULL; + return m_pInstance->GivItemList(); +} + +CBotString CBotVarArray::GivValString() +{ + if ( m_pInstance == NULL ) return ( CBotString( "Null pointer" ) ) ; + return m_pInstance->GivValString(); +} + +BOOL CBotVarArray::Save1State(FILE* pf) +{ + if ( !WriteType(pf, m_type) ) return FALSE; + return SaveVar(pf, m_pInstance); // sauve l'instance qui gère le tableau +} + + +///////////////////////////////////////////////////////////////////////////// +// gestion des pointeurs à une instance donnée + +CBotVarPointer::CBotVarPointer(const CBotToken* name, CBotTypResult& type ) +{ + if ( !type.Eq(CBotTypPointer) && + !type.Eq(CBotTypNullPointer) && + !type.Eq(CBotTypClass) && // par commodité accepte Class et Intrinsic + !type.Eq(CBotTypIntrinsic) ) __asm int 3; + + m_token = new CBotToken(name); + m_next = NULL; + m_pMyThis = NULL; + m_pUserPtr = NULL; + + m_type = type; + if ( !type.Eq(CBotTypNullPointer) ) + m_type.SetType(CBotTypPointer); // quoi qu'il en soit, c'est un pointeur + m_binit = FALSE; + m_pClass = NULL; + m_pVarClass = NULL; // sera défini par un SetPointer() + + SetClass(type.GivClass() ); +} + +CBotVarPointer::~CBotVarPointer() +{ + if ( m_pVarClass != NULL ) m_pVarClass->DecrementUse(); // une référence en moins +} + + +void CBotVarPointer::Maj(void* pUser, BOOL bContinu) +{ +/* if ( !bContinu && m_pMyThis != NULL ) + m_pMyThis->Maj(pUser, FALSE);*/ + + if ( m_pVarClass != NULL) m_pVarClass->Maj(pUser, FALSE); +} + +CBotVar* CBotVarPointer::GivItem(const char* name) +{ + if ( m_pVarClass == NULL) // pas d'instance existant ? + return m_pClass->GivItem(name); // rend le pointeur dans la classe elle-même + + return m_pVarClass->GivItem(name); +} + +CBotVar* CBotVarPointer::GivItemRef(int nIdent) +{ + if ( m_pVarClass == NULL) // pas d'instance existant ? + return m_pClass->GivItemRef(nIdent);// rend le pointeur dans la classe elle-même + + return m_pVarClass->GivItemRef(nIdent); +} + +CBotVar* CBotVarPointer::GivItemList() +{ + if ( m_pVarClass == NULL) return NULL; + return m_pVarClass->GivItemList(); +} + +CBotString CBotVarPointer::GivValString() +{ + CBotString s = "Pointer to "; + if ( m_pVarClass == NULL ) s = "Null pointer" ; + else s += m_pVarClass->GivValString(); + return s; +} + + +void CBotVarPointer::ConstructorSet() +{ + if ( m_pVarClass != NULL) m_pVarClass->ConstructorSet(); +} + +// initialise le pointeur vers l'instance d'une classe + +void CBotVarPointer::SetPointer(CBotVar* pVarClass) +{ + m_binit = TRUE; // init, même sur un pointeur null + + if ( m_pVarClass == pVarClass) return; // spécial, ne pas décrémenter et réincrémenter + // car le décrément peut détruire l'object + + if ( pVarClass != NULL ) + { + if ( pVarClass->GivType() == CBotTypPointer ) + pVarClass = pVarClass->GivPointer(); // le vrai pointeur à l'objet + +// if ( pVarClass->GivType() != CBotTypClass ) + if ( !pVarClass->m_type.Eq(CBotTypClass) ) + __asm int 3; + + ((CBotVarClass*)pVarClass)->IncrementUse(); // une référence en plus + m_pClass = ((CBotVarClass*)pVarClass)->m_pClass; + m_pUserPtr = pVarClass->m_pUserPtr; // pas vraiment indispensable + m_type = CBotTypResult(CBotTypPointer, m_pClass); // un pointeur de quel genre + } + + if ( m_pVarClass != NULL ) m_pVarClass->DecrementUse(); + m_pVarClass = (CBotVarClass*)pVarClass; + +} + +CBotVarClass* CBotVarPointer::GivPointer() +{ + if ( m_pVarClass == NULL ) return NULL; + return m_pVarClass->GivPointer(); +} + +void CBotVarPointer::SetIdent(long n) +{ + if ( m_pVarClass == NULL ) return; + m_pVarClass->SetIdent( n ); +} + +long CBotVarPointer::GivIdent() +{ + if ( m_pVarClass == NULL ) return 0; + return m_pVarClass->m_ItemIdent; +} + + +void CBotVarPointer::SetClass(CBotClass* pClass) +{ +// int nIdent = 0; + m_type.m_pClass = m_pClass = pClass; + if ( m_pVarClass != NULL ) m_pVarClass->SetClass(pClass); //, nIdent); +} + +CBotClass* CBotVarPointer::GivClass() +{ + if ( m_pVarClass != NULL ) return m_pVarClass->GivClass(); + + return m_pClass; +} + + +BOOL CBotVarPointer::Save1State(FILE* pf) +{ + if ( m_pClass ) + { + if (!WriteString(pf, m_pClass->GivName())) return FALSE; // nom de la classe + } + else + { + if (!WriteString(pf, "")) return FALSE; + } + + if (!WriteLong(pf, GivIdent())) return FALSE; // la référence unique + + // sauve aussi une copie de l'instance + return SaveVar(pf, GivPointer()); +} + +// copie une variable dans une autre +void CBotVarPointer::Copy(CBotVar* pSrc, BOOL bName) +{ + if ( pSrc->GivType() != CBotTypPointer && + pSrc->GivType() != CBotTypNullPointer) + __asm int 3; + + CBotVarPointer* p = (CBotVarPointer*)pSrc; + + if ( bName) *m_token = *p->m_token; + m_type = p->m_type; +// m_pVarClass = p->m_pVarClass; + m_pVarClass = p->GivPointer(); + + if ( m_pVarClass != NULL ) + m_pVarClass->IncrementUse(); // une référence en plus + + m_pClass = p->m_pClass; + m_binit = p->m_binit; +//- m_bStatic = p->m_bStatic; + m_next = NULL; + m_pMyThis = NULL;//p->m_pMyThis; + m_pUserPtr = p->m_pUserPtr; + + // garde le même idendificateur (par défaut) + if (m_ident == 0 ) m_ident = p->m_ident; +} + +BOOL CBotVarPointer::Eq(CBotVar* left, CBotVar* right) +{ + CBotVarClass* l = left->GivPointer(); + CBotVarClass* r = right->GivPointer(); + + if ( l == r ) return TRUE; + if ( l == NULL && r->GivUserPtr() == OBJECTDELETED ) return TRUE; + if ( r == NULL && l->GivUserPtr() == OBJECTDELETED ) return TRUE; + return FALSE; +} + +BOOL CBotVarPointer::Ne(CBotVar* left, CBotVar* right) +{ + CBotVarClass* l = left->GivPointer(); + CBotVarClass* r = right->GivPointer(); + + if ( l == r ) return FALSE; + if ( l == NULL && r->GivUserPtr() == OBJECTDELETED ) return FALSE; + if ( r == NULL && l->GivUserPtr() == OBJECTDELETED ) return FALSE; + return TRUE; +} + + + +/////////////////////////////////////////////////////// +// gestion des types de résultats + + +CBotTypResult::CBotTypResult(int type) +{ + m_type = type; + m_pNext = NULL; + m_pClass = NULL; + m_limite = -1; +} + +CBotTypResult::CBotTypResult(int type, const char* name) +{ + m_type = type; + m_pNext = NULL; + m_pClass = NULL; + m_limite = -1; + + if ( type == CBotTypPointer || + type == CBotTypClass || + type == CBotTypIntrinsic ) + { + m_pClass = CBotClass::Find(name); + if ( m_pClass && m_pClass->IsIntrinsic() ) m_type = CBotTypIntrinsic; + } +} + +CBotTypResult::CBotTypResult(int type, CBotClass* pClass) +{ + m_type = type; + m_pNext = NULL; + m_pClass = pClass; + m_limite = -1; + + if ( m_pClass && m_pClass->IsIntrinsic() ) m_type = CBotTypIntrinsic; +} + +CBotTypResult::CBotTypResult(int type, CBotTypResult elem) +{ + m_type = type; + m_pNext = NULL; + m_pClass = NULL; + m_limite = -1; + + if ( type == CBotTypArrayPointer || + type == CBotTypArrayBody ) + m_pNext = new CBotTypResult( elem ); +} + +CBotTypResult::CBotTypResult(CBotTypResult& typ) +{ + m_type = typ.m_type; + m_pClass = typ.m_pClass; + m_pNext = NULL; + m_limite = typ.m_limite; + + if ( typ.m_pNext ) + m_pNext = new CBotTypResult( *typ.m_pNext ); +} + +CBotTypResult::CBotTypResult() +{ + m_type = 0; + m_limite = -1; + m_pNext = NULL; + m_pClass = NULL; +} + +CBotTypResult::~CBotTypResult() +{ + delete m_pNext; +} + +int CBotTypResult::GivType(int mode) +{ +#ifdef _DEBUG + if ( m_type == CBotTypPointer || + m_type == CBotTypClass || + m_type == CBotTypIntrinsic ) + + if ( m_pClass == NULL ) __asm int 3; + + + if ( m_type == CBotTypArrayPointer ) + if ( m_pNext == NULL ) __asm int 3; +#endif + if ( mode == 3 && m_type == CBotTypNullPointer ) return CBotTypPointer; + return m_type; +} + +void CBotTypResult::SetType(int n) +{ + m_type = n; +} + +CBotClass* CBotTypResult::GivClass() +{ + return m_pClass; +} + +CBotTypResult& CBotTypResult::GivTypElem() +{ + return *m_pNext; +} + +int CBotTypResult::GivLimite() +{ + return m_limite; +} + +void CBotTypResult::SetLimite(int n) +{ + m_limite = n; +} + +void CBotTypResult::SetArray( int* max ) +{ + m_limite = *max; + if (m_limite < 1) m_limite = -1; + + if ( m_pNext != NULL ) // dernière dimension ? + { + m_pNext->SetArray( max+1 ); + } +} + + + +BOOL CBotTypResult::Compare(CBotTypResult& typ) +{ + if ( m_type != typ.m_type ) return FALSE; + + if ( m_type == CBotTypArrayPointer ) return m_pNext->Compare(*typ.m_pNext); + + if ( m_type == CBotTypPointer || + m_type == CBotTypClass || + m_type == CBotTypIntrinsic ) + { + return m_pClass == typ.m_pClass; + } + + return TRUE; +} + +BOOL CBotTypResult::Eq(int type) +{ + return m_type == type; +} + +CBotTypResult& + CBotTypResult::operator=(const CBotTypResult& src) +{ + m_type = src.m_type; + m_limite = src.m_limite; + m_pClass = src.m_pClass; + m_pNext = NULL; + if ( src.m_pNext != NULL ) + { + m_pNext = new CBotTypResult(*src.m_pNext); + } + return *this; +} + + diff --git a/src/CBot/CBotWhile.cpp b/src/CBot/CBotWhile.cpp new file mode 100644 index 00000000..7a1ca1fa --- /dev/null +++ b/src/CBot/CBotWhile.cpp @@ -0,0 +1,1413 @@ +/////////////////////////////////////////////////////////////////////// +// ce fichier défini les instructions suivantes: +// CBotWhile "while (condition) {instructions}" +// CBotDo "do {instructions} while (condition)" +// CBotFor "for (init, condition, incr) {instructions}" +// CBotSwitch "switch (val) {instructions}" +// CBotCase "case val:" +// CBotBreak "break", "break label", "continu", "continu label" +// CBotTry "try {instructions}" +// CBotCatch "catch (condition) {instructions}" ou "finally" +// CBotThrow "throw execption" + + +#include "CBot.h" + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction "while" + +CBotWhile::CBotWhile() +{ + m_Condition = + m_Block = NULL; // NULL pour que delete soit possible sans autre + name = "CBotWhile"; // debug +} + +CBotWhile::~CBotWhile() +{ + delete m_Condition; // libère la condition + delete m_Block; // libère le bloc d'instruction +} + +CBotInstr* CBotWhile::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotWhile* inst = new CBotWhile(); // crée l'objet + CBotToken* pp = p; // conserve le ^au token (position début) + + if ( IsOfType( p, TokenTypVar ) && + IsOfType( p, ID_DOTS ) ) + { + inst->m_label = pp->GivString(); // enregistre le nom du label + } + + inst->SetToken(p); + if (!IsOfType(p, ID_WHILE)) return NULL; // ne devrait jamais arriver + + CBotCStack* pStk = pStack->TokenStack(pp); // un petit bout de pile svp + + if ( NULL != (inst->m_Condition = CBotCondition::Compile( p, pStk )) ) + { + // la condition existe + + IncLvl(inst->m_label); + inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStk, TRUE ); + DecLvl(); + + if ( pStk->IsOk() ) + { + // le bloc d'instruction est ok (il peut être vide ! + + return pStack->Return(inst, pStk); // rend l'objet à qui le demande + } + } + + delete inst; // erreur, libère la place + return pStack->Return(NULL, pStk); // pas d'objet, l'erreur est sur la pile +} + +// exécute une instruction "while" + +BOOL CBotWhile :: Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); // ajoute un élément à la pile + // ou le retrouve en cas de reprise +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + + while( TRUE ) switch( pile->GivState() ) // exécute la boucle + { // il y a 2 états possibles (selon reprise) + case 0: + // évalue la condition + if ( !m_Condition->Execute(pile) ) return FALSE; // interrompu ici ? + + // le résultat de la condition est sur la pile + + // termine s'il y a une erreur ou si la condition est fausse + if ( !pile->IsOk() || pile->GivVal() != TRUE ) + { + return pj->Return(pile); // transmet le résultat et libère la pile + } + + // la condition est vrai, passe dans le second mode + + if (!pile->SetState(1)) return FALSE; // prêt pour la suite + + case 1: + // évalue le bloc d'instruction associé + if ( m_Block != NULL && + !m_Block->Execute(pile) ) + { + if (pile->IfContinue(0, m_label)) continue; // si continue, repasse au test + return pj->BreakReturn(pile, m_label); // transmet le résultat et libère la pile + } + + // termine s'il y a une erreur + if ( !pile->IsOk() ) + { + return pj->Return(pile); // transmet le résultat et libère la pile + } + + // repasse au test pour recommencer + if (!pile->SetState(0, 0)) return FALSE; + continue; + } +} + +void CBotWhile :: RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + CBotStack* pile = pj->RestoreStack(this); // ajoute un élément à la pile + if ( pile == NULL ) return; + + switch( pile->GivState() ) + { // il y a 2 états possibles (selon reprise) + case 0: + // évalue la condition + m_Condition->RestoreState(pile, bMain); + return; + + case 1: + // évalue le bloc d'instruction associé + if ( m_Block != NULL ) m_Block->RestoreState(pile, bMain); + return; + } +} + + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction "repeat" + +CBotRepeat::CBotRepeat() +{ + m_NbIter = + m_Block = NULL; // NULL pour que delete soit possible sans autre + name = "CBotRepeat"; // debug +} + +CBotRepeat::~CBotRepeat() +{ + delete m_NbIter; // libère la condition + delete m_Block; // libère le bloc d'instruction +} + +CBotInstr* CBotRepeat::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotRepeat* inst = new CBotRepeat(); // crée l'objet + CBotToken* pp = p; // conserve le ^au token (position début) + + if ( IsOfType( p, TokenTypVar ) && + IsOfType( p, ID_DOTS ) ) + { + inst->m_label = pp->GivString(); // enregistre le nom du label + } + + inst->SetToken(p); + if (!IsOfType(p, ID_REPEAT)) return NULL; // ne devrait jamais arriver + + CBotCStack* pStk = pStack->TokenStack(pp); // un petit bout de pile svp + + if ( IsOfType(p, ID_OPENPAR ) ) + { + CBotToken* ppp = p; // conserve le ^au token (position début) + if ( NULL != (inst->m_NbIter = CBotExpression::Compile( p, pStk )) ) + { + if ( pStk->GivType() < CBotTypLong ) + { + if ( IsOfType(p, ID_CLOSEPAR ) ) + { + + IncLvl(inst->m_label); + inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStk, TRUE ); + DecLvl(); + + if ( pStk->IsOk() ) + { + // le bloc d'instruction est ok (il peut être vide ! + + return pStack->Return(inst, pStk); // rend l'objet à qui le demande + } + } + pStack->SetError(TX_CLOSEPAR, p->GivStart()); + } + pStk->SetStartError(ppp->GivStart()); + pStk->SetError( TX_BADTYPE, p->GivStart() ); + } + pStack->SetError(TX_ENDOF, p); + } + pStack->SetError(TX_OPENPAR, p->GivStart()); // manque la parenthèse + + delete inst; // erreur, libère la place + return pStack->Return(NULL, pStk); // pas d'objet, l'erreur est sur la pile +} + +// exécute une instruction "repeat" + +BOOL CBotRepeat :: Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); // ajoute un élément à la pile + // ou le retrouve en cas de reprise +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + + while( TRUE ) switch( pile->GivState() ) // exécute la boucle + { // il y a 2 états possibles (selon reprise) + case 0: + // évalue le nombre d'itération + if ( !m_NbIter->Execute(pile) ) return FALSE; // interrompu ici ? + + // le résultat de la condition est sur la pile + + // termine s'il y a une erreur ou si la condition est fausse + int n; + if ( !pile->IsOk() || ( n = pile->GivVal() ) < 1 ) + { + return pj->Return(pile); // transmet le résultat et libère la pile + } + + // met le nombre d'itération +1 dans le "state" + + if (!pile->SetState(n+1)) return FALSE; // prêt pour la suite + continue; // passe à la suite + + case 1: + // fin normale de la boucle + return pj->Return(pile); // transmet le résultat et libère la pile + + default: + // évalue le bloc d'instruction associé + if ( m_Block != NULL && + !m_Block->Execute(pile) ) + { + if (pile->IfContinue(pile->GivState()-1, m_label)) continue; // si continue, repasse au test + return pj->BreakReturn(pile, m_label); // transmet le résultat et libère la pile + } + + // termine s'il y a une erreur + if ( !pile->IsOk() ) + { + return pj->Return(pile); // transmet le résultat et libère la pile + } + + // repasse au test pour recommencer + if (!pile->SetState(pile->GivState()-1, 0)) return FALSE; + continue; + } +} + +void CBotRepeat :: RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + CBotStack* pile = pj->RestoreStack(this); // ajoute un élément à la pile + if ( pile == NULL ) return; + + switch( pile->GivState() ) + { // il y a 2 états possibles (selon reprise) + case 0: + // évalue la condition + m_NbIter->RestoreState(pile, bMain); + return; + + case 1: + // évalue le bloc d'instruction associé + if ( m_Block != NULL ) m_Block->RestoreState(pile, bMain); + return; + } +} + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction "do" + +CBotDo::CBotDo() +{ + m_Condition = + m_Block = NULL; // NULL pour que delete soit possible sans autre + name = "CBotDo"; // debug +} + +CBotDo::~CBotDo() +{ + delete m_Condition; // libère la condition + delete m_Block; // libère le bloc d'instruction +} + +CBotInstr* CBotDo::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotDo* inst = new CBotDo(); // crée l'objet + + CBotToken* pp = p; // conserve le ^au token (position début) + + if ( IsOfType( p, TokenTypVar ) && + IsOfType( p, ID_DOTS ) ) + { + inst->m_label = pp->GivString(); // enregistre le nom du label + } + + inst->SetToken(p); + if (!IsOfType(p, ID_DO)) return NULL; // ne devrait jamais arriver + + CBotCStack* pStk = pStack->TokenStack(pp); // un petit bout de pile svp + + + // cherche un bloc d'instruction après le do + IncLvl(inst->m_label); + inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStk, TRUE ); + DecLvl(); + + if ( pStk->IsOk() ) + { + if (IsOfType(p, ID_WHILE)) + { + if ( NULL != (inst->m_Condition = CBotCondition::Compile( p, pStk )) ) + { + // la condition existe + if (IsOfType(p, ID_SEP)) + { + return pStack->Return(inst, pStk); // rend l'objet à qui le demande + } + pStk->SetError(TX_ENDOF, p->GivStart()); + } + } + pStk->SetError(TX_WHILE, p->GivStart()); + } + + delete inst; // erreur, libère la place + return pStack->Return(NULL, pStk); // pas d'objet, l'erreur est sur la pile +} + +// exécute une instruction "do" + +BOOL CBotDo :: Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); // ajoute un élément à la pile + // ou le retrouve en cas de reprise +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + + while( TRUE ) switch( pile->GivState() ) // exécute la boucle + { // il y a 2 états possibles (selon reprise) + case 0: + // évalue le bloc d'instruction associé + if ( m_Block != NULL && + !m_Block->Execute(pile) ) + { + if (pile->IfContinue(1, m_label)) continue; // si continue, repasse au test + return pj->BreakReturn(pile, m_label); // transmet le résultat et libère la pile + } + + // termine s'il y a une erreur + if ( !pile->IsOk() ) + { + return pj->Return(pile); // transmet le résultat et libère la pile + } + + if (!pile->SetState(1)) return FALSE; // prêt pour la suite + + case 1: + // évalue la condition + if ( !m_Condition->Execute(pile) ) return FALSE; // interrompu ici ? + + // le résultat de la condition est sur la pile + + // termine s'il y a une erreur ou si la condition est fausse + if ( !pile->IsOk() || pile->GivVal() != TRUE ) + { + return pj->Return(pile); // transmet le résultat et libère la pile + } + + // repasse au bloc d'instruction pour recommencer + if (!pile->SetState(0, 0)) return FALSE; + continue; + } +} + +void CBotDo :: RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile = pj->RestoreStack(this); // ajoute un élément à la pile + if ( pile == NULL ) return; + + switch( pile->GivState() ) + { // il y a 2 états possibles (selon reprise) + case 0: + // restitue le bloc d'instruction associé + if ( m_Block != NULL ) m_Block->RestoreState(pile, bMain); + return; + + case 1: + // restitue la condition + m_Condition->RestoreState(pile, bMain); + return; + } +} + + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction "for" + +CBotFor::CBotFor() +{ + m_Init = + m_Test = + m_Incr = + m_Block = NULL; // NULL pour que delete soit possible sans autre + name = "CBotFor"; // debug +} + +CBotFor::~CBotFor() +{ + delete m_Init; + delete m_Test; + delete m_Incr; + delete m_Block; // libère le bloc d'instruction +} + +CBotInstr* CBotFor::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotFor* inst = new CBotFor(); // crée l'objet + CBotToken* pp = p; // conserve le ^au token (position début) + + if ( IsOfType( p, TokenTypVar ) && + IsOfType( p, ID_DOTS ) ) + { + inst->m_label = pp->GivString(); // enregistre le nom du label + } + + inst->SetToken(p); + if (!IsOfType(p, ID_FOR)) return NULL; // ne devrait jamais arriver + + if ( !IsOfType(p, ID_OPENPAR)) // manque la parenthèse ? + { + pStack->SetError(TX_OPENPAR, p->GivStart()); + return NULL; + } + + CBotCStack* pStk = pStack->TokenStack(pp, TRUE); // un petit bout de pile svp + + // compile les instructions pour initialisation + inst->m_Init = CBotListExpression::Compile( p, pStk ); + if ( pStk->IsOk() ) + { + if ( !IsOfType(p, ID_SEP)) // manque le point-virgule ? + { + pStack->SetError(TX_OPENPAR, p->GivStart()); + delete inst; + return pStack->Return(NULL, pStk); // pas d'objet, l'erreur est sur la pile + } + inst->m_Test = CBotBoolExpr::Compile( p, pStk ); + if ( pStk->IsOk() ) + { + if ( !IsOfType(p, ID_SEP)) // manque le point-virgule ? + { + pStack->SetError(TX_OPENPAR, p->GivStart()); + delete inst; + return pStack->Return(NULL, pStk); // pas d'objet, l'erreur est sur la pile + } + inst->m_Incr = CBotListExpression::Compile( p, pStk ); + if ( pStk->IsOk() ) + { + if ( IsOfType(p, ID_CLOSEPAR)) // manque la parenthèse ? + { + IncLvl(inst->m_label); + inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStk, TRUE ); + DecLvl(); + if ( pStk->IsOk() ) + return pStack->Return(inst, pStk);; + } + pStack->SetError(TX_CLOSEPAR, p->GivStart()); + } + } + } + + delete inst; // erreur, libère la place + return pStack->Return(NULL, pStk); // pas d'objet, l'erreur est sur la pile +} + +// exécute l'instruction "for" + +BOOL CBotFor :: Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this, TRUE); // ajoute un élément à la pile (variables locales) + // ou le retrouve en cas de reprise +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + + while( TRUE ) switch( pile->GivState() ) // exécute la boucle + { // il y a 4 états possibles (selon reprise) + case 0: + // évalue l'initialisation + if ( m_Init != NULL && + !m_Init->Execute(pile) ) return FALSE; // interrompu ici ? + if (!pile->SetState(1)) return FALSE; // prêt pour la suite + + case 1: + // évalue la condition + if ( m_Test != NULL ) // pas de condition ? -> vrai ! + { + if (!m_Test->Execute(pile) ) return FALSE; // interrompu ici ? + + // le résultat de la condition est sur la pile + + // termine s'il y a une erreur ou si la condition est fausse + if ( !pile->IsOk() || pile->GivVal() != TRUE ) + { + return pj->Return(pile); // transmet le résultat et libère la pile + } + } + + // la condition est vrai, passe à la suite + if (!pile->SetState(2)) return FALSE; // prêt pour la suite + + case 2: + // évalue le bloc d'instruction associé + if ( m_Block != NULL && + !m_Block->Execute(pile) ) + { + if (pile->IfContinue(3, m_label)) continue; // si continue, passe à l'incrémentation + return pj->BreakReturn(pile, m_label); // transmet le résultat et libère la pile + } + + // termine s'il y a une erreur + if ( !pile->IsOk() ) + { + return pj->Return(pile); // transmet le résultat et libère la pile + } + + if (!pile->SetState(3)) return FALSE; // prêt pour la suite + + case 3: + // évalue l'incrémentation + if ( m_Incr != NULL && + !m_Incr->Execute(pile) ) return FALSE; // interrompu ici ? + + // repasse au test pour recommencer + if (!pile->SetState(1, 0)) return FALSE; // revient au test + continue; + } +} + +void CBotFor :: RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile = pj->RestoreStack(this); // ajoute un élément à la pile (variables locales) + if ( pile == NULL ) return; + + switch( pile->GivState() ) + { // il y a 4 états possibles (selon reprise) + case 0: + // évalue l'initialisation + if ( m_Init != NULL ) m_Init->RestoreState(pile, TRUE); // interrompu ici ! + return; + + case 1: + if ( m_Init != NULL ) m_Init->RestoreState(pile, FALSE); // définitions variables + + // évalue la condition + if ( m_Test != NULL ) m_Test->RestoreState(pile, TRUE); // interrompu ici ! + return; + + case 2: + if ( m_Init != NULL ) m_Init->RestoreState(pile, FALSE); // définitions variables + + // évalue le bloc d'instruction associé + if ( m_Block != NULL ) m_Block->RestoreState(pile, TRUE); + return; + + case 3: + if ( m_Init != NULL ) m_Init->RestoreState(pile, FALSE); // définitions variables + + // évalue l'incrémentation + if ( m_Incr != NULL ) m_Incr->RestoreState(pile, TRUE); // interrompu ici ! + return; + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// compile une liste d'expression +// n'est utilisé que pour l'instruction for +// dans l'intitialisation et dans l'incrémentation + +CBotListExpression::CBotListExpression() +{ + m_Expr = NULL; + name = "CBotListExpression"; +} + +CBotListExpression::~CBotListExpression() +{ + delete m_Expr; +} + +// cherche une déclaration de variable ou une expression + +static CBotInstr* CompileInstrOrDefVar(CBotToken* &p, CBotCStack* pStack) +{ + CBotInstr* i = CBotInt::Compile( p, pStack, FALSE, TRUE ); // est-ce une déclaration d'un entier ? + if ( i== NULL ) i = CBotFloat::Compile( p, pStack, FALSE, TRUE ); // ou d'un nombre réel ? + if ( i== NULL ) i = CBotBoolean::Compile( p, pStack, FALSE, TRUE ); // ou d'un booléen ? + if ( i== NULL ) i = CBotIString::Compile( p, pStack, FALSE, TRUE ); // ou d'une chaîne ? + if ( i== NULL ) i = CBotExpression::Compile( p, pStack ); // compile une expression + return i; +} + +CBotInstr* CBotListExpression::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotListExpression* inst = new CBotListExpression(); + + inst->m_Expr = CompileInstrOrDefVar( p, pStack ); // compile la première expression de la liste + if (pStack->IsOk()) + { + while ( IsOfType(p, ID_COMMA) ) // plusieurs instructions ? + { + CBotInstr* i = CompileInstrOrDefVar( p, pStack ); // est-ce une déclaration d'un entier ? + inst->m_Expr->AddNext(i); // ajoute à la suite + if ( !pStack->IsOk() ) + { + delete inst; + return NULL; // pas d'objet, l'erreur est sur la pile + } + } + return inst; + } + delete inst; + return NULL; +} + +BOOL CBotListExpression::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack();// indispensable + CBotInstr* p = m_Expr; // la première expression + + int state = pile->GivState(); + while (state-->0) p = p->GivNext(); // revient sur l'opération interrompue + + if ( p != NULL ) while (TRUE) + { + if ( !p->Execute(pile) ) return FALSE; + p = p->GivNext(); + if ( p == NULL ) break; + if (!pile->IncState()) return FALSE; // prêt pour la suivante + } + return pj->Return(pile); +} + +void CBotListExpression::RestoreState(CBotStack* &pj, BOOL bMain) +{ + CBotStack* pile = pj; + int state = 0x7000; + + if ( bMain ) + { + pile = pj->RestoreStack(); + if ( pile == NULL ) return; + state = pile->GivState(); + } + + CBotInstr* p = m_Expr; // la première expression + + while (p != NULL && state-->0) + { + p->RestoreState(pile, FALSE); + p = p->GivNext(); // revient sur l'opération interrompue + } + + if ( p != NULL ) + { + p->RestoreState(pile, bMain); + } +} + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction "switch" + +CBotSwitch::CBotSwitch() +{ + m_Value = + m_Block = NULL; // NULL pour que delete soit possible sans autre + name = "CBotSwitch"; // debug +} + +CBotSwitch::~CBotSwitch() +{ + delete m_Value; // libère la valeur + delete m_Block; // libère le bloc d'instruction +} + + +CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotSwitch* inst = new CBotSwitch(); // crée l'objet + CBotToken* pp = p; // conserve le ^au token (position début) + + inst->SetToken(p); + if (!IsOfType(p, ID_SWITCH)) return NULL; // ne devrait jamais arriver + + CBotCStack* pStk = pStack->TokenStack(pp); // un petit bout de pile svp + + if ( IsOfType(p, ID_OPENPAR ) ) + { + if ( NULL != (inst->m_Value = CBotExpression::Compile( p, pStk )) ) + { + if ( pStk->GivType() < CBotTypLong ) + { + if ( IsOfType(p, ID_CLOSEPAR ) ) + { + if ( IsOfType(p, ID_OPBLK ) ) + { + IncLvl(); + + while( !IsOfType( p, ID_CLBLK ) ) + { + if ( p->GivType() == ID_CASE || p->GivType() == ID_DEFAULT) + { + CBotCStack* pStk2 = pStk->TokenStack(p); // un petit bout de pile svp + + CBotInstr* i = CBotCase::Compile( p, pStk2 ); + if (i == NULL) + { + delete inst; + return pStack->Return(NULL, pStk2); + } + delete pStk2; + if ( inst->m_Block == NULL ) inst->m_Block = i; + else inst->m_Block->AddNext(i); + continue; + } + + if ( inst->m_Block == NULL ) + { + pStk->SetError(TX_NOCASE, p->GivStart()); + delete inst; + return pStack->Return(NULL, pStk); + } + + CBotInstr* i = CBotBlock::CompileBlkOrInst( p, pStk, TRUE ); + if ( !pStk->IsOk() ) + { + delete inst; + return pStack->Return(NULL, pStk); + } + inst->m_Block->AddNext(i); + + if ( p == NULL ) + { + pStk->SetError(TX_CLOSEBLK, -1); + delete inst; + return pStack->Return(NULL, pStk); + } + } + DecLvl(); + + if ( inst->m_Block == NULL ) + { + pStk->SetError(TX_NOCASE, p->GivStart()); + delete inst; + return pStack->Return(NULL, pStk); + } + // le bloc d'instruction est ok + return pStack->Return(inst, pStk); // rend l'objet à qui le demande + } + pStk->SetError( TX_OPENBLK, p->GivStart() ); + } + pStk->SetError( TX_CLOSEPAR, p->GivStart() ); + } + pStk->SetError( TX_BADTYPE, p->GivStart() ); + } + } + pStk->SetError( TX_OPENPAR, p->GivStart()); + + delete inst; // erreur, libère la place + return pStack->Return(NULL, pStk); // pas d'objet, l'erreur est sur la pile +} + +// exécute une instruction "switch" + +BOOL CBotSwitch :: Execute(CBotStack* &pj) +{ + CBotStack* pile1 = pj->AddStack(this); // ajoute un élément à la pile +// if ( pile1 == EOX ) return TRUE; + + CBotInstr* p = m_Block; // la première expression + + int state = pile1->GivState(); + if (state == 0) + { + if ( !m_Value->Execute(pile1) ) return FALSE; + pile1->SetState(state = -1); + } + + if ( pile1->IfStep() ) return FALSE; + + if ( state == -1 ) + { + state = 0; + int val = pile1->GivVal(); // résultat de la valeur + + CBotStack* pile2 = pile1->AddStack(); + while ( p != NULL ) // recherche le case correspondant dans la liste + { + state++; + if ( p->CompCase( pile2, val ) ) break; // trouvé le case + p = p->GivNext(); + } + pile2->Delete(); + + if ( p == NULL ) return pj->Return(pile1); // terminé si plus rien + + if ( !pile1->SetState(state) ) return FALSE; + } + + p = m_Block; // revient au début + while (state-->0) p = p->GivNext(); // avance dans la liste + + while( p != NULL ) + { + if ( !p->Execute(pile1) ) return pj->BreakReturn(pile1); + if ( !pile1->IncState() ) return FALSE; + p = p->GivNext(); + } + return pj->Return(pile1); +} + +void CBotSwitch :: RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile1 = pj->RestoreStack(this); // ajoute un élément à la pile + if ( pile1 == NULL ) return; + + CBotInstr* p = m_Block; // la première expression + + int state = pile1->GivState(); + if (state == 0) + { + m_Value->RestoreState(pile1, bMain); + return; + } + + if ( state == -1 ) + { + return; + } + +// p = m_Block; // revient au début + while ( p != NULL && state-- > 0 ) + { + p->RestoreState(pile1, FALSE); + p = p->GivNext(); // avance dans la liste + } + + if( p != NULL ) + { + p->RestoreState(pile1, TRUE); + return; + } +} + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction "case" +// on est forcément dans un bloc d'instruction "switch" + +CBotCase::CBotCase() +{ + m_Value = NULL; // NULL pour que delete soit possible sans autre + name = "CBotCase"; // debug +} + +CBotCase::~CBotCase() +{ + delete m_Value; // libère la valeur +} + + +CBotInstr* CBotCase::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotCase* inst = new CBotCase(); // crée l'objet + CBotToken* pp = p; // conserve le ^au token (position début) + + inst->SetToken(p); + if (!IsOfType(p, ID_CASE, ID_DEFAULT)) return NULL; // ne devrait jamais arriver + + if ( pp->GivType() == ID_CASE ) + { + pp = p; + inst->m_Value = CBotExprNum::Compile(p, pStack); + if ( inst->m_Value == NULL ) + { + pStack->SetError( TX_BADNUM, pp ); + delete inst; + return NULL; + } + } + if ( !IsOfType( p, ID_DOTS )) + { + pStack->SetError( TX_MISDOTS, p->GivStart() ); + delete inst; + return NULL; + } + + return inst; +} + +// exécution de l'instruction "case" + +BOOL CBotCase::Execute(CBotStack* &pj) +{ + return TRUE; // l'instruction "case" ne fait rien ! +} + +void CBotCase::RestoreState(CBotStack* &pj, BOOL bMain) +{ +} + +// routine permettant de trouver le point d'entrée "case" +// correspondant à la valeur cherchée + +BOOL CBotCase::CompCase(CBotStack* &pile, int val) +{ + if ( m_Value == NULL ) return TRUE; // cas pour "default" + + while (!m_Value->Execute(pile)); // met sur la pile la valeur correpondant (sans interruption) + return (pile->GivVal() == val); // compare avec la valeur cherchée +} + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction "break" ou "continu" + +CBotBreak::CBotBreak() +{ + name = "CBotBreak"; // debug +} + +CBotBreak::~CBotBreak() +{ +} + +CBotInstr* CBotBreak::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotToken* pp = p; // conserve le ^au token (position début) + int type = p->GivType(); + + if (!IsOfType(p, ID_BREAK, ID_CONTINUE)) return NULL; // ne devrait jamais arriver + + if ( !ChkLvl(CBotString(), type ) ) + { + pStack->SetError(TX_BREAK, pp); + return NULL; // pas d'objet, l'erreur est sur la pile + } + + CBotBreak* inst = new CBotBreak(); // crée l'objet + inst->SetToken(pp); // garde l'opération + + pp = p; + if ( IsOfType( p, TokenTypVar ) ) + { + inst->m_label = pp->GivString(); // enregistre le nom du label + if ( !ChkLvl(inst->m_label, type ) ) + { + delete inst; + pStack->SetError(TX_NOLABEL, pp); + return NULL; // pas d'objet, l'erreur est sur la pile + } + } + + if (IsOfType(p, ID_SEP)) + { + return inst; // et le donne à qui veux + } + delete inst; + + pStack->SetError(TX_ENDOF, p->GivStart()); + return NULL; // pas d'objet, l'erreur est sur la pile +} + +// exécution l'instructino "break" ou "continu" + +BOOL CBotBreak :: Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + + if ( pile->IfStep() ) return FALSE; + + pile->SetBreak(m_token.GivType()==ID_BREAK ? 1 : 2, m_label); + return pj->Return(pile); +} + +void CBotBreak :: RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( bMain ) pj->RestoreStack(this); +} + + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction "try" + +CBotTry::CBotTry() +{ + m_ListCatch = NULL; + m_FinalInst = + m_Block = NULL; // NULL pour que delete soit possible sans autre + name = "CBotTry"; // debug +} + +CBotTry::~CBotTry() +{ + delete m_ListCatch; // libère la liste + delete m_Block; // libère le bloc d'instruction + delete m_FinalInst; +} + +CBotInstr* CBotTry::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotTry* inst = new CBotTry(); // crée l'objet + CBotToken* pp = p; // conserve le ^au token (position début) + + inst->SetToken(p); + if (!IsOfType(p, ID_TRY)) return NULL; // ne devrait jamais arriver + + CBotCStack* pStk = pStack->TokenStack(pp); // un petit bout de pile svp + + inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStk ); + CBotCatch** pn = &inst->m_ListCatch; + + while (pStk->IsOk() && p->GivType() == ID_CATCH) + { + CBotCatch* i = CBotCatch::Compile(p, pStk); + *pn = i; + pn = &i->m_next; + } + + if (pStk->IsOk() && IsOfType( p, ID_FINALLY) ) + { + inst->m_FinalInst = CBotBlock::CompileBlkOrInst( p, pStk ); + } + + if (pStk->IsOk()) + { + return pStack->Return(inst, pStk); // rend l'objet à qui le demande + } + + delete inst; // erreur, libère la place + return pStack->Return(NULL, pStk); // pas d'objet, l'erreur est sur la pile +} + +// exécute l'instruction Try +// gère le retour d'exceptions +// les arrêts par suspension +// et les "finaly" + +BOOL CBotTry :: Execute(CBotStack* &pj) +{ + int val; + + CBotStack* pile1 = pj->AddStack(this); // ajoute un élément à la pile +// if ( pile1 == EOX ) return TRUE; + + if ( pile1->IfStep() ) return FALSE; + // ou le retrouve en cas de reprise + CBotStack* pile0 = pj->AddStack2(); // ajoute un élément à la pile secondaire + CBotStack* pile2 = pile0->AddStack(); + + if ( pile1->GivState() == 0 ) + { + if ( m_Block->Execute(pile1) ) + { + if ( m_FinalInst == NULL ) return pj->Return(pile1); + pile1->SetState(-2); // passe au final + } + + val = pile1->GivError(); + if ( val == 0 && CBotStack::m_initimer == 0 ) // en mode de step ? + return FALSE; // ne fait pas le catch + + pile1->IncState(); + pile2->SetState(val); // mémorise le numéro de l'erreur + pile1->SetError(0); // pour l'instant il n'y a plus d'erreur ! + + if ( val == 0 && CBotStack::m_initimer < 0 ) // en mode de step ? + return FALSE; // ne fait pas le catch + } + + // il y a eu une interruption + // voir de quoi il en retourne + + CBotCatch* pc = m_ListCatch; + int state = (short)pile1->GivState(); // où en étions-nous ? + val = pile2->GivState(); // pour quelle erreur ? + pile0->SetState(1); // marquage pour GetRunPos + + if ( val >= 0 && state > 0 ) while ( pc != NULL ) + { + if ( --state <= 0 ) + { + // demande au bloc catch s'il se sent concerné + if ( !pc->TestCatch(pile2, val) ) return FALSE; // suspendu ! + pile1->IncState(); + } + if ( --state <= 0 ) + { + if ( pile2->GivVal() == TRUE ) + { +// pile0->SetState(1); + + if ( !pc->Execute(pile2) ) return FALSE; // exécute l'opération + if ( m_FinalInst == NULL ) + return pj->Return(pile2); // termine le try + + pile1->SetState(-2); // passe au final + break; + } + pile1->IncState(); + } + pc = pc->m_next; + } + if ( m_FinalInst != NULL && + pile1->GivState() > 0 && val != 0 ) pile1->SetState(-1);// si arret alors fait le final + + if (pile1->GivState() <= -1) + { +// pile0->SetState(1); + + if (!m_FinalInst->Execute(pile2) && pile2->IsOk()) return FALSE; + if (!pile2->IsOk()) return pj->Return(pile2); // garde cette exception + pile2->SetError(pile1->GivState()==-1 ? val : 0); // remet l'erreur initiale + return pj->Return(pile2); + } + + pile1->SetState(0); // revient à l'évaluation + pile0->SetState(0); // revient à l'évaluation + if ( val != 0 && m_ListCatch == NULL && m_FinalInst == NULL ) + return pj->Return(pile2); // termine le try sans exception aucune + + pile1->SetError(val); // remet l'erreur + return FALSE; // ce n'est pas pour nous +} + + +void CBotTry :: RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + int val; + CBotStack* pile1 = pj->RestoreStack(this); // ajoute un élément à la pile + if ( pile1 == NULL ) return; + // ou le retrouve en cas de reprise + CBotStack* pile0 = pj->AddStack2(); // ajoute un élément à la pile secondaire + if ( pile0 == NULL ) return; + + CBotStack* pile2 = pile0->RestoreStack(); + if ( pile2 == NULL ) return; + + m_Block->RestoreState(pile1, bMain); + if ( pile0->GivState() == 0 ) + { + return; + } + + // il y a eu une interruption + // voir de quoi il en retourne + + CBotCatch* pc = m_ListCatch; + int state = pile1->GivState(); // où en étions-nous ? + val = pile2->GivState(); // pour quelle erreur ? + + if ( val >= 0 && state > 0 ) while ( pc != NULL ) + { + if ( --state <= 0 ) + { + // demande au bloc catch s'il se sent concerné + pc->RestoreCondState(pile2, bMain); // suspendu ! + return; + } + if ( --state <= 0 ) + { + if ( pile2->GivVal() == TRUE ) + { + pc->RestoreState(pile2, bMain); // exécute l'opération + return; + } + } + pc = pc->m_next; + } + + if (pile1->GivState() <= -1) + { + m_FinalInst->RestoreState(pile2, bMain); + return; + } +} + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction "catch" + +CBotCatch::CBotCatch() +{ + m_Cond = + m_Block = NULL; // NULL pour que delete soit possible sans autre + m_next = NULL; + + name = "CBotCatch"; // debug +} + +CBotCatch::~CBotCatch() +{ + delete m_Cond; // libère la liste + delete m_Block; // libère le bloc d'instruction + delete m_next; // et la suite +} + +CBotCatch* CBotCatch::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotCatch* inst = new CBotCatch(); // crée l'objet + pStack->SetStartError(p->GivStart()); + + inst->SetToken(p); + if (!IsOfType(p, ID_CATCH)) return NULL; // ne devrait jamais arriver + + if (IsOfType(p, ID_OPENPAR)) + { + inst->m_Cond = CBotExpression::Compile(p, pStack); + if (( pStack->GivType() < CBotTypLong || + pStack->GivTypResult().Eq(CBotTypBoolean) )&& pStack->IsOk() ) + { + if (IsOfType(p, ID_CLOSEPAR)) + { + inst->m_Block = CBotBlock::CompileBlkOrInst( p, pStack ); + if ( pStack->IsOk() ) + return inst; // rend l'objet à qui le demande + } + pStack->SetError(TX_CLOSEPAR, p->GivStart()); + } + pStack->SetError(TX_BADTYPE, p->GivStart()); + } + pStack->SetError(TX_OPENPAR, p->GivStart()); + delete inst; // erreur, libère la place + return NULL; // pas d'objet, l'erreur est sur la pile +} + +// exécution de "catch" + +BOOL CBotCatch :: Execute(CBotStack* &pj) +{ + if ( m_Block == NULL ) return TRUE; + return m_Block->Execute(pj); // exécute le bloc associé +} + +void CBotCatch :: RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( bMain && m_Block != NULL ) m_Block->RestoreState(pj, bMain); +} + +void CBotCatch :: RestoreCondState(CBotStack* &pj, BOOL bMain) +{ + m_Cond->RestoreState(pj, bMain); +} + +// routine pour savoir si le catch est à faire ou non + +BOOL CBotCatch :: TestCatch(CBotStack* &pile, int val) +{ + if ( !m_Cond->Execute(pile) ) return FALSE; + + if ( val > 0 || pile->GivType() != CBotTypBoolean ) + { + CBotVar* var = CBotVar::Create((CBotToken*)NULL, CBotTypBoolean); + var->SetValInt( pile->GivVal() == val ); + pile->SetVar(var); // remet sur la pile + } + + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// compile une instruction "throw" + +CBotThrow::CBotThrow() +{ + m_Value = NULL; // NULL pour que delete soit possible sans autre + + name = "CBotThrow"; // debug +} + +CBotThrow::~CBotThrow() +{ + delete m_Value; +} + +CBotInstr* CBotThrow::Compile(CBotToken* &p, CBotCStack* pStack) +{ + pStack->SetStartError(p->GivStart()); + + CBotThrow* inst = new CBotThrow(); // crée l'objet + inst->SetToken(p); + + CBotToken* pp = p; // conserve le ^au token (position début) + + if (!IsOfType(p, ID_THROW)) return NULL; // ne devrait jamais arriver + + inst->m_Value = CBotExpression::Compile( p, pStack ); + + if (pStack->GivType() < CBotTypLong && pStack->IsOk()) + { + return inst; // rend l'objet à qui le demande + } + pStack->SetError(TX_BADTYPE, pp); + + delete inst; // erreur, libère la place + return NULL; // pas d'objet, l'erreur est sur la pile +} + +// exécute l'instruction "throw" + +BOOL CBotThrow :: Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); +// if ( pile == EOX ) return TRUE; + + if ( pile->GivState() == 0 ) + { + if ( !m_Value->Execute(pile) ) return FALSE; + pile->IncState(); + } + + if ( pile->IfStep() ) return FALSE; + + int val = pile->GivVal(); + if ( val < 0 ) val = TX_BADTHROW; + pile->SetError( val, &m_token ); + return pj->Return( pile ); +} + +void CBotThrow :: RestoreState(CBotStack* &pj, BOOL bMain) +{ + if ( !bMain ) return; + + CBotStack* pile = pj->RestoreStack(this); + if ( pile == NULL ) return; + + if ( pile->GivState() == 0 ) + { + m_Value->RestoreState(pile, bMain); + return; + } +} + + + +//////////////////////////////////////////////////////////// + + +CBotStartDebugDD::CBotStartDebugDD() +{ + name = "CBotStartDebugDD"; // debug +} + +CBotStartDebugDD::~CBotStartDebugDD() +{ +} + +CBotInstr* CBotStartDebugDD::Compile(CBotToken* &p, CBotCStack* pStack) +{ + + if (!IsOfType(p, ID_DEBUGDD)) return NULL; // ne devrait jamais arriver + + return new CBotStartDebugDD(); // crée l'objet + +} + +// exécute l'instruction "throw" + +BOOL CBotStartDebugDD :: Execute(CBotStack* &pj) +{ + CBotProgram* p = pj->GivBotCall(); + p->m_bDebugDD = TRUE; + + return TRUE; +} + diff --git a/src/CBot/ClassFILE.cpp b/src/CBot/ClassFILE.cpp new file mode 100644 index 00000000..21bd39ea --- /dev/null +++ b/src/CBot/ClassFILE.cpp @@ -0,0 +1,412 @@ +// ClassFile.cpp +// +// définition des méthodes pour la classe FILE + + + +// Variables statiques + +static CBotClass* m_pClassFILE; +static CBotProgram* m_pFuncFile; +static int m_CompteurFileOpen = 0; + + + +// Prépare un nom de fichier. + +void PrepareFilename(CBotString &filename) //DD! +{ + int pos; + + pos = filename.ReverseFind('\\'); + if ( pos > 0 ) + { + filename = filename.Mid(pos+1); // enlève les dossiers + } + + pos = filename.ReverseFind('/'); + if ( pos > 0 ) + { + filename = filename.Mid(pos+1); // aussi ceux avec / + } + + pos = filename.ReverseFind(':'); + if ( pos > 0 ) + { + filename = filename.Mid(pos+1); // enlève aussi la lettre d'unité C: + } + + filename = CBotString("files\\") + filename; +} + + +// constructeur de la classe +// reçois le nom du fichier en paramètre + +// exécution +BOOL rfconstruct (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + CBotString mode; + + // accepte sans paramètre + if ( pVar == NULL ) return TRUE; + + // qui doit être une chaîne de caractères + if ( pVar->GivType() != CBotTypString ) { Exception = CBotErrBadString; return FALSE; } + + CBotString filename = pVar->GivValString(); + PrepareFilename(filename); //DR + + // il peut y avoir un second paramètre + pVar = pVar->GivNext(); + if ( pVar != NULL ) + { + // récupère le mode + mode = pVar->GivValString(); + if ( mode != "r" && mode != "w" ) { Exception = CBotErrBadParam; return FALSE; } + + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) { Exception = CBotErrOverParam; return FALSE; } + } + + // enregistre le nom du fichier + pVar = pThis->GivItem("filename"); + pVar->SetValString(filename); + + if ( ! mode.IsEmpty() ) + { + // ouvre le ficher demandé + FILE* pFile = fopen( filename, mode ); + if ( pFile == NULL ) { Exception = CBotErrFileOpen; return FALSE; } + + m_CompteurFileOpen ++; + + // enregiste le canal du fichier + pVar = pThis->GivItem("handle"); + pVar->SetValInt((long)pFile); + } + + return TRUE; +} + +// compilation +CBotTypResult cfconstruct (CBotVar* pThis, CBotVar* &pVar) +{ + // accepte sans paramètre + if ( pVar == NULL ) return CBotTypResult( 0 ); + + // qui doit être une chaine + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( CBotErrBadString ); + + // il peut y avoir un second paramètre + pVar = pVar->GivNext(); + if ( pVar != NULL ) + { + // qui doit être une chaine + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( CBotErrBadString ); + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( CBotErrOverParam ); + } + + // le résultat est de type void (constructeur) + return CBotTypResult( 0 ); +} + + +// destructeur de la classe + +// exécution +BOOL rfdestruct (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + // pas ouvert ? pas de problème + if ( pVar->GivInit() != IS_DEF) return TRUE; + + FILE* pFile= (FILE*)pVar->GivValInt(); + fclose(pFile); + m_CompteurFileOpen --; + + pVar->SetInit(IS_NAN); + + return TRUE; +} + + +// méthode FILE :: open +// reçois le mode r/w en paramètre + +// exécution +BOOL rfopen (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // il doit y avoir un paramètre + if ( pVar == NULL ) { Exception = CBotErrLowParam; return FALSE; } + + // qui doit être une chaîne de caractères + if ( pVar->GivType() != CBotTypString ) { Exception = CBotErrBadString; return FALSE; } + + // il peut y avoir un second paramètre + if ( pVar->GivNext() != NULL ) + { + // dans ce cas le premier paramètre est le nom du fichier + CBotString filename = pVar->GivValString(); + PrepareFilename(filename); //DR + + // enregistre le nom du fichier + CBotVar* pVar2 = pThis->GivItem("filename"); + pVar2->SetValString(filename); + + // paramètre suivant est le mode + pVar = pVar -> GivNext(); + } + + CBotString mode = pVar->GivValString(); + if ( mode != "r" && mode != "w" ) { Exception = CBotErrBadParam; return FALSE; } + + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) { Exception = CBotErrOverParam; return FALSE; } + + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + // qui doit pas être initialisé + if ( pVar->GivInit() == IS_DEF) { Exception = CBotErrFileOpen; return FALSE; } + + // reprend le nom du fichier + pVar = pThis->GivItem("filename"); + CBotString filename = pVar->GivValString(); + + PrepareFilename(filename); //DD! (si le nom a été attribué par h.filename = "..."; + + // ouvre le ficher demandé + FILE* pFile = fopen( filename, mode ); + if ( pFile == NULL ) //DR + { + pResult->SetValInt(FALSE); //DR + return TRUE; //DR + } + + m_CompteurFileOpen ++; + + // enregiste le canal du fichier + pVar = pThis->GivItem("handle"); + pVar->SetValInt((long)pFile); + + pResult->SetValInt(TRUE); //DR + return TRUE; +} + +// compilation +CBotTypResult cfopen (CBotVar* pThis, CBotVar* &pVar) +{ + // il doit y avoir un paramètre + if ( pVar == NULL ) return CBotTypResult( CBotErrLowParam ); + + // qui doit être une chaine + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( CBotErrBadString ); + + // il peut y avoir un second paramètre + pVar = pVar->GivNext(); + if ( pVar != NULL ) + { + // qui doit être une chaine + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( CBotErrBadString ); + + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( CBotErrOverParam ); + } + + // le résultat est de type bool + return CBotTypResult(CBotTypBoolean); //DR +} + + +// méthode FILE :: close + +// exécution +BOOL rfclose (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) return CBotErrOverParam; + + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + if ( pVar->GivInit() != IS_DEF) { Exception = CBotErrNotOpen; return FALSE; } + + FILE* pFile= (FILE*)pVar->GivValInt(); + fclose(pFile); + m_CompteurFileOpen --; + + pVar->SetInit(IS_NAN); + + return TRUE; +} + +// compilation +CBotTypResult cfclose (CBotVar* pThis, CBotVar* &pVar) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) return CBotTypResult( CBotErrOverParam ); + + // la fonction retourne un résultat "void" + return CBotTypResult( 0 ); +} + +// méthode FILE :: writeln + +// exécution +BOOL rfwrite (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // il doit y avoir un paramètre + if ( pVar == NULL ) { Exception = CBotErrLowParam; return FALSE; } + + // qui doit être une chaîne de caractères + if ( pVar->GivType() != CBotTypString ) { Exception = CBotErrBadString; return FALSE; } + + CBotString param = pVar->GivValString(); + + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + if ( pVar->GivInit() != IS_DEF) { Exception = CBotErrNotOpen; return FALSE; } + + FILE* pFile= (FILE*)pVar->GivValInt(); + + int res = fputs(param+"\n", pFile); + + // en cas d'erreur génère une exception + if ( res < 0 ) { Exception = CBotErrWrite; return FALSE; } + + return TRUE; +} + +// compilation +CBotTypResult cfwrite (CBotVar* pThis, CBotVar* &pVar) +{ + // il doit y avoir un paramètre + if ( pVar == NULL ) return CBotTypResult( CBotErrLowParam ); + + // qui doit être une chaîne de caractères + if ( pVar->GivType() != CBotTypString ) return CBotTypResult( CBotErrBadString ); + + // pas d'autre paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( CBotErrOverParam ); + + // la fonction retourne un résultat void + return CBotTypResult( 0 ); +} + +// méthode FILE :: readln + +// exécution +BOOL rfread (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) { Exception = CBotErrOverParam; return FALSE; } + + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + if ( pVar->GivInit() != IS_DEF) { Exception = CBotErrNotOpen; return FALSE; } + + FILE* pFile= (FILE*)pVar->GivValInt(); + + char chaine[2000]; + int i; + for ( i = 0 ; i < 2000 ; i++ ) chaine[i] = 0; + + fgets(chaine, 1999, pFile); + + for ( i = 0 ; i < 2000 ; i++ ) if (chaine[i] == '\n') chaine[i] = 0; + + // en cas d'erreur génère une exception + if ( ferror(pFile) ) { Exception = CBotErrRead; return FALSE; } + + pResult->SetValString( chaine ); + + return TRUE; +} + +// compilation +CBotTypResult cfread (CBotVar* pThis, CBotVar* &pVar) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) return CBotTypResult( CBotErrOverParam ); + + // la fonction retourne un résultat "string" + return CBotTypResult( CBotTypString ); +} +// méthode FILE :: readln + + +// exécution +BOOL rfeof (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) { Exception = CBotErrOverParam; return FALSE; } + + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + if ( pVar->GivInit() != IS_DEF) { Exception = CBotErrNotOpen; return FALSE; } + + FILE* pFile= (FILE*)pVar->GivValInt(); + + pResult->SetValInt( feof( pFile ) ); + + return TRUE; +} + +// compilation +CBotTypResult cfeof (CBotVar* pThis, CBotVar* &pVar) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) return CBotTypResult( CBotErrOverParam ); + + // la fonction retourne un résultat booleen + return CBotTypResult( CBotTypBoolean ); +} + + + + + +void InitClassFILE() +{ +// crée une classe pour la gestion des fichiers +// l'utilisation en est la suivante: +// file canal( "NomFichier.txt" ) +// canal.open( "r" ); // ouvre en lecture +// s = canal.readln( ); // lit une ligne +// canal.close(); // referme le fichier + + // crée la classe FILE + m_pClassFILE = new CBotClass("file", NULL); + // ajoute le composant ".filename" + m_pClassFILE->AddItem("filename", CBotTypString); + // ajoute le composant ".handle" + m_pClassFILE->AddItem("handle", CBotTypInt, PR_PRIVATE); + + // défini un constructeur et un destructeur + m_pClassFILE->AddFunction("file", rfconstruct, cfconstruct ); + m_pClassFILE->AddFunction("~file", rfdestruct, NULL ); + + // défini les méthodes associées + m_pClassFILE->AddFunction("open", rfopen, cfopen ); + m_pClassFILE->AddFunction("close", rfclose, cfclose ); + m_pClassFILE->AddFunction("writeln", rfwrite, cfwrite ); + m_pClassFILE->AddFunction("readln", rfread, cfread ); + m_pClassFILE->AddFunction("eof", rfeof, cfeof ); + + m_pFuncFile = new CBotProgram( ); + CBotStringArray ListFonctions; + m_pFuncFile->Compile( "public file openfile(string name, string mode) {return new file(name, mode);}", ListFonctions); + m_pFuncFile->SetIdent(-2); // identificateur spécial pour RestoreState dans cette fonction +} + diff --git a/src/CBot/Copie de CBot.rc b/src/CBot/Copie de CBot.rc new file mode 100644 index 00000000..52ef23ad --- /dev/null +++ b/src/CBot/Copie de CBot.rc @@ -0,0 +1,184 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + ID_IF "if" + ID_ELSE "else" + ID_WHILE "while" + ID_DO "do" + ID_ADD "+" + ID_SUB "-" + ID_MUL "*" + ID_DIV "/" + ID_OPENPAR "(" + ID_CLOSEPAR ")" + ID_SEP ";" + ID_INT "int" + ID_ASS "=" + ID_TRUE "true" + ID_FALSE "false" + ID_OPBLK "{" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_CLBLK "}" + ID_FOR "for" + ID_COMMA "," + ID_LO "<" + ID_HI ">" + ID_LS "<=" + ID_HS ">=" + ID_EQ "==" + ID_NE "!=" + ID_FLOAT "float" + ID_STRING "String" + ID_BOOLEAN "boolean" + ID_AND "&" + ID_XOR "^" + ID_OR "|" + ID_LOG_AND "&&" +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_OPENPAR "Il manque une parenthèse ouvrante." + TX_CLOSEPAR "Il manque une parenthèse fermante." + TX_NOTBOOL "L'expression doit être un boolean." + TX_UNDEFVAR "Variable non déclarée." + TX_BADLEFT "Assignation impossible." + TX_ENDOF "Instruction non terminée." + TX_OUTCASE "Instruction ""case"" hors d'un bloc ""switch""." + TX_NOTERM "Instructions après la fin." +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_CLOSEBLK "Il manque la fin du bloc." + TX_ELSEWITHOUTIF "Instruction ""else"" sans ""if"" correspondant." + TX_OPENBLK "Début d'un bloc attendu." + TX_BADTYPE "Mauvais type de résultat pour l'assignation." + TX_REDEFVAR "Redéfinition d'une variable." + TX_BAD2TYPE "Les deux opérandes ne sont pas de types compatibles." + TX_UNDEFCALL "Routine inconnue." + TX_MISDOTS "Séparateur "" : "" attendu." + TX_WHILE "Manque le mot ""while""." + TX_BREAK "Instruction ""break"" en dehors d'une boucle." + TX_LABEL "Un label ne peut se placer que devant un ""for"", un ""while"", un ""do"" ou un ""switch""." + TX_NOLABEL "Cette étiquette n'existe pas" + TX_NOCASE "Manque une instruction ""case""." + TX_BADNUM "Un nombre est attendu." +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_DIVZERO "Division par zéro." + TX_NOTINIT "Variable non initialisée." +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_LOG_OR "||" + ID_LOG_NOT "!" + ID_NOT "~" + ID_ASSADD "+=" + ID_ASSSUB "-=" + ID_ASSMUL "*=" + ID_ASSDIV "/=" + ID_ASSOR "|=" + ID_ASSAND "&=" + ID_ASSXOR "^=" + ID_ASSSL "<<=" + ID_ASSSR ">>>=" + ID_ASSASR ">>=" + ID_SL "<<" + ID_SR ">>>" + ID_ASR ">>" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_INC "++" + ID_DEC "--" + ID_MODULO "%" + ID_ASSMODULO "%=" + ID_LOGIC "?" + ID_DOTS ":" + ID_BREAK "break" + ID_SWITCH "switch" + ID_CASE "case" + ID_CONTINUE "continue" + ID_TRY "try" + ID_CATCH "catch" + ID_THROW "throw" + ID_FINALLY "finally" + ID_DEFAULT "default" +END + +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/CBot/Copie de CBotTwoOpExpr.cpp b/src/CBot/Copie de CBotTwoOpExpr.cpp new file mode 100644 index 00000000..7a95b557 --- /dev/null +++ b/src/CBot/Copie de CBotTwoOpExpr.cpp @@ -0,0 +1,295 @@ +/////////////////////////////////////////////////// +// expression du genre Opérande1 + Opérande2 +// Opérande1 > Opérande2 + +#include "CBot.h" + +// divers constructeurs + +CBotTwoOpExpr::CBotTwoOpExpr() +{ + m_leftop = + m_rightop = NULL; // NULL pour pouvoir faire delete sans autre + name = "CBotTwoOpExpr"; // debug +} + +CBotTwoOpExpr::~CBotTwoOpExpr() +{ + delete m_leftop; + delete m_rightop; +} + +// type d'opérandes acceptés par les opérations +#define ENTIER ((1<TokenStack(); // un bout de pile svp + + // cherche des instructions qui peuvent convenir à gauche de l'opération + CBotInstr* left = (*pOp == 0) ? + CBotParExpr::Compile( p, pStk ) : // expression (...) à gauche + CBotTwoOpExpr::Compile( p, pStk, pOp ); // expression A * B à gauche + + if (left == NULL) return pStack->Return(NULL, pStk); // si erreur, la transmet + + // est-ce qu'on a l'opérande prévu ensuite ? + int TypeOp = p->GetType(); + if ( IsInList( TypeOp, pOperations, typemasque ) ) + { + CBotTwoOpExpr* inst = new CBotTwoOpExpr(); // élément pour opération + inst->SetToken(p); // mémorise l'opération + + int type1, type2; + type1 = pStk->GetType(); // de quel type le premier opérande ? + + p = p->Next(); // saute le token de l'opération + + // cherche des instructions qui peuvent convenir à droite + + if ( NULL != (inst->m_rightop = CBotTwoOpExpr::Compile( p, pStk, pOperations )) ) + // expression (...) à droite + { + // il y a un second opérande acceptable + + type2 = pStk->GetType(); // de quel type le résultat ? + + // quel est le type du résultat ? + int TypeRes = MAX( type1, type2 ); + if (!TypeOk( TypeRes, typemasque )) type1 = 99; // erreur de type + + switch ( TypeOp ) + { + case ID_LOG_OR: + case ID_LOG_AND: + case ID_EQ: + case ID_NE: + case ID_HI: + case ID_LO: + case ID_HS: + case ID_LS: + TypeRes = CBotTypBoolean; + } + if ( TypeCompatible (type1, type2) || // les résultats sont-ils compatibles + // cas particulier pour les concaténation de chaînes + (TypeOp == ID_ADD && (type1 == CBotTypString || type2 == CBotTypString))) + { + // si ok, enregistre l'opérande dans l'objet + inst->m_leftop = left; + // met une variable sur la pile pour avoir le type de résultat + pStk->SetVar(new CBotVar(NULL, TypeRes)); + // et rend l'object à qui l'a demandé + return pStack->Return(inst, pStk); + } + pStk->SetError(TX_BAD2TYPE, &inst->m_token); + } + + // en cas d'erreur, libère les éléments + delete left; + delete inst; + // et transmet l'erreur qui se trouve sur la pile + return pStack->Return(NULL, pStk); + } + + // si on n'a pas affaire à une opération + ou - + // rend à qui l'a demandé, l'opérande (de gauche) trouvé + // à la place de l'objet "addition" + return pStack->Return(left, pStk); +} + + + + +// fait l'opération d'addition ou de soustraction + +BOOL CBotTwoOpExpr::Execute(CBotStack* &pStack) +{ + CBotStack* pStk1 = pStack->AddStack(); // ajoute un élément à la pile + // ou le retrouve en cas de reprise + + // selon la reprise, on peut être dans l'un des 2 états + + if ( pStk1->GetState() == 0 && // 1er état, évalue l'opérande de gauche + !m_leftop->Execute(pStk1) ) return FALSE; // interrompu ici ? + + // passe à l'étape suivante + pStk1->SetState(1); // prêt pour la suite + + // pour les OU et ET logique, n'évalue pas la seconde expression si pas nécessaire + if ( GetTokenType() == ID_LOG_AND && pStk1->GetVal() == FALSE ) + { + CBotVar* res = CBotVar::Create( NULL, CBotTypBoolean); + res->SetValInt(FALSE); + pStk1->SetVar(res); + return pStack->Return(pStk1); // transmet le résultat + } + if ( GetTokenType() == ID_LOG_OR && pStk1->GetVal() == TRUE ) + { + CBotVar* res = CBotVar::Create( NULL, CBotTypBoolean); + res->SetValInt(TRUE); + pStk1->SetVar(res); + return pStack->Return(pStk1); // transmet le résultat + } + + // demande un peu plus de stack pour ne pas toucher le résultat de gauche + // qui se trouve sur la pile, justement. + + CBotStack* pStk2 = pStk1->AddStack(); // ajoute un élément à la pile + // ou le retrouve en cas de reprise + + // 2e état, évalue l'opérande de droite + if ( !m_rightop->Execute(pStk2) ) return FALSE; // interrompu ici ? + + int type1 = pStk1->GetType(); // de quels types les résultats ? + int type2 = pStk2->GetType(); + + // crée une variable temporaire pour y mettre le résultat + // quel est le type du résultat ? + int TypeRes = MAX(type1, type2); + switch ( GetTokenType() ) + { + case ID_LOG_OR: + case ID_LOG_AND: + case ID_EQ: + case ID_NE: + case ID_HI: + case ID_LO: + case ID_HS: + case ID_LS: + TypeRes = CBotTypBoolean; + } + CBotVar* result = CBotVar::Create( NULL, TypeRes); + CBotVar* temp = CBotVar::Create( NULL, MAX(type1, type2) ); + + int err = 0; + // fait l'opération selon la demande + switch (GetTokenType()) + { + case ID_ADD: + result->Add(pStk1->GetVar(), pStk2->GetVar()); // additionne + break; + case ID_SUB: + result->Sub(pStk1->GetVar(), pStk2->GetVar()); // soustrait + break; + case ID_MUL: + result->Mul(pStk1->GetVar(), pStk2->GetVar()); // multiplie + break; + case ID_DIV: + err = result->Div(pStk1->GetVar(), pStk2->GetVar());// divise + break; + case ID_MODULO: + err = result->Modulo(pStk1->GetVar(), pStk2->GetVar());// reste de division + break; + case ID_LO: + temp->Lo(pStk1->GetVar(), pStk2->GetVar()); // inférieur + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_HI: + temp->Hi(pStk1->GetVar(), pStk2->GetVar()); // supérieur + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_LS: + temp->Ls(pStk1->GetVar(), pStk2->GetVar()); // inférieur ou égal + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_HS: + temp->Hs(pStk1->GetVar(), pStk2->GetVar()); // supérieur ou égal + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_EQ: + temp->Eq(pStk1->GetVar(), pStk2->GetVar()); // égal + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_NE: + temp->Ne(pStk1->GetVar(), pStk2->GetVar()); // différent + result->SetValInt(temp->GetValInt()); // converti le résultat + break; + case ID_LOG_AND: + case ID_AND: + result->And(pStk1->GetVar(), pStk2->GetVar()); // ET + break; + case ID_LOG_OR: + case ID_OR: + result->Or(pStk1->GetVar(), pStk2->GetVar()); // OU + break; + case ID_XOR: + result->XOr(pStk1->GetVar(), pStk2->GetVar()); // OU exclusif + break; + case ID_ASR: + result->ASR(pStk1->GetVar(), pStk2->GetVar()); + break; + case ID_SR: + result->SR(pStk1->GetVar(), pStk2->GetVar()); + break; + case ID_SL: + result->SL(pStk1->GetVar(), pStk2->GetVar()); + break; + default: + __asm int 3; + } + delete temp; + + pStk2->SetVar(result); // met le résultat sur la pile + if ( err ) pStk2->SetError(err, &m_token); // et l'erreur éventuelle (division par zéro) + + pStk1->Return(pStk2); // libère la pile + return pStack->Return(pStk1); // transmet le résultat +} + + diff --git a/src/CBot/StringFunctions.cpp b/src/CBot/StringFunctions.cpp new file mode 100644 index 00000000..803ffd97 --- /dev/null +++ b/src/CBot/StringFunctions.cpp @@ -0,0 +1,420 @@ +// définition des fonctions sur les chaînes + + +// donne la longueur d'une chaîne +// exécution + +BOOL rStrLen( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) { ex = TX_BADSTRING ; return TRUE; } + + // pas de second paramètre + if ( pVar->GivNext() != NULL ) { ex = TX_OVERPARAM ; return TRUE; } + + // recupére le contenu de la string + CBotString s = pVar->GivValString(); + + // met la longueur sur la pile + pResult->SetValInt( s.GivLength() ); + return TRUE; +} + +// int xxx ( string ) +// compilation + +CBotTypResult cIntStr( CBotVar* &pVar, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) return CBotTypResult( TX_LOWPARAM ); + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( TX_BADPARAM ); + + // pas de second paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( TX_OVERPARAM ); + + // le résultat final est un nombre entier + return CBotTypResult( CBotTypInt ); +} + + +// donne la partie gauche d'une chaîne +// exécution + +BOOL rStrLeft( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) { ex = TX_BADSTRING ; return TRUE; } + + // recupére le contenu de la string + CBotString s = pVar->GivValString(); + + // il faut un second paramètre + pVar = pVar->GivNext(); + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être un nombre + if ( pVar->GivType() > CBotTypDouble ) { ex = TX_BADNUM ; return TRUE; } + + // récupère ce nombre + int n = pVar->GivValInt(); + + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) { ex = TX_OVERPARAM ; return TRUE; } + + // prend la partie intéressante + s = s.Left( n ); + + // la met sur la pile + pResult->SetValString( s ); + return TRUE; +} + +// string xxx ( string, int ) +// compilation + +CBotTypResult cStrStrInt( CBotVar* &pVar, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) return CBotTypResult( TX_LOWPARAM ); + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( TX_BADSTRING ); + + // il faut un second paramètre + pVar = pVar->GivNext(); + if ( pVar == NULL ) return CBotTypResult( TX_LOWPARAM ); + + // qui doit être un nombre + if ( pVar->GivType() > CBotTypDouble ) + return CBotTypResult( TX_BADNUM ); + + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( TX_OVERPARAM ); + + // le résultat final est une string + return CBotTypResult( CBotTypString ); +} + +// donne la partie droite d'une chaîne +// exécution + +BOOL rStrRight( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) { ex = TX_BADSTRING ; return TRUE; } + + // recupére le contenu de la string + CBotString s = pVar->GivValString(); + + // il faut un second paramètre + pVar = pVar->GivNext(); + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être un nombre + if ( pVar->GivType() > CBotTypDouble ) { ex = TX_BADNUM ; return TRUE; } + + // récupère ce nombre + int n = pVar->GivValInt(); + + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) { ex = TX_OVERPARAM ; return TRUE; } + + // prend la partie intéressante + s = s.Right( n ); + + // la met sur la pile + pResult->SetValString( s ); + return TRUE; +} + +// donne la partie centrale d'une chaîne +// exécution + +BOOL rStrMid( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) { ex = TX_BADSTRING ; return TRUE; } + + // recupére le contenu de la string + CBotString s = pVar->GivValString(); + + // il faut un second paramètre + pVar = pVar->GivNext(); + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être un nombre + if ( pVar->GivType() > CBotTypDouble ) { ex = TX_BADNUM ; return TRUE; } + + // récupère ce nombre + int n = pVar->GivValInt(); + + // 3e paramètre optionnel + if ( pVar->GivNext() != NULL ) + { + pVar = pVar->GivNext(); + + // qui doit être un nombre + if ( pVar->GivType() > CBotTypDouble ) { ex = TX_BADNUM ; return TRUE; } + + // récupère ce nombre + int l = pVar->GivValInt(); + + // mais pas de 4e paramètre + if ( pVar->GivNext() != NULL ){ ex = TX_OVERPARAM ; return TRUE; } + + // prend la partie intéressante + s = s.Mid( n, l ); + } + else + { + // prend la partie intéressante + s = s.Mid( n ); + } + + // la met sur la pile + pResult->SetValString( s ); + return TRUE; +} + +// donne la partie centrale d'une chaîne +// compilation + +CBotTypResult cStrStrIntInt( CBotVar* &pVar, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) return CBotTypResult( TX_LOWPARAM ); + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( TX_BADSTRING ); + + // il faut un second paramètre + pVar = pVar->GivNext(); + if ( pVar == NULL ) return CBotTypResult( TX_LOWPARAM ); + + // qui doit être un nombre + if ( pVar->GivType() > CBotTypDouble ) + return CBotTypResult( TX_BADNUM ); + + // 3e paramètre optionnel + if ( pVar->GivNext() != NULL ) + { + + pVar = pVar->GivNext(); + // qui doit être un nombre + if ( pVar->GivType() > CBotTypDouble ) + return CBotTypResult( TX_BADNUM ); + + // pas de 4e paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( TX_OVERPARAM ); + } + + // le résultat final est une string + return CBotTypResult( CBotTypString ); +} + + +// donne le nombre contenu dans une chaîne +// exécution + +BOOL rStrVal( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) { ex = TX_BADSTRING ; return TRUE; } + + // recupére le contenu de la string + CBotString s = pVar->GivValString(); + + // mais pas de 2e paramètre + if ( pVar->GivNext() != NULL ){ ex = TX_OVERPARAM ; return TRUE; } + + float val = GivNumFloat(s); + + // la met la valeur sur la pile + pResult->SetValFloat( val ); + return TRUE; +} + +// float xxx ( string ) +// compilation + +CBotTypResult cFloatStr( CBotVar* &pVar, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) return CBotTypResult( TX_LOWPARAM ); + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( TX_BADSTRING ); + + // pas de 2e paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( TX_OVERPARAM ); + + // le résultat final est un nombre + return CBotTypResult( CBotTypFloat ); +} + + +// trouve une chaine dans une autre +// exécution + +BOOL rStrFind( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) { ex = TX_BADSTRING ; return TRUE; } + + // recupére le contenu de la string + CBotString s = pVar->GivValString(); + + // il faut un second paramètre + pVar = pVar->GivNext(); + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) { ex = TX_BADSTRING ; return TRUE; } + + // récupère ce nombre + CBotString s2 = pVar->GivValString(); + + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) { ex = TX_OVERPARAM ; return TRUE; } + + // met le résultat sur la pile + int res = s.Find(s2); + pResult->SetValInt( res ); + if ( res < 0 ) pResult->SetInit( IS_NAN ); + return TRUE; +} + +// int xxx ( string, string ) +// compilation + +CBotTypResult cIntStrStr( CBotVar* &pVar, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) return CBotTypResult( TX_LOWPARAM ); + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( TX_BADSTRING ); + + // il faut un second paramètre + pVar = pVar->GivNext(); + if ( pVar == NULL ) return CBotTypResult( TX_LOWPARAM ); + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( TX_BADSTRING ); + + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( TX_OVERPARAM ); + + // le résultat final est un nombre + return CBotTypResult( CBotTypInt ); +} + +// donne une chaine en majuscule +// exécution + +BOOL rStrUpper( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) { ex = TX_BADSTRING ; return TRUE; } + + // recupére le contenu de la string + CBotString s = pVar->GivValString(); + + // mais pas de 2e paramètre + if ( pVar->GivNext() != NULL ){ ex = TX_OVERPARAM ; return TRUE; } + + + s.MakeUpper(); + + // la met la valeur sur la pile + pResult->SetValString( s ); + return TRUE; +} + +// donne une chaine en minuscules +// exécution + +BOOL rStrLower( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) { ex = TX_LOWPARAM ; return TRUE; } + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) { ex = TX_BADSTRING ; return TRUE; } + + // recupére le contenu de la string + CBotString s = pVar->GivValString(); + + // mais pas de 2e paramètre + if ( pVar->GivNext() != NULL ){ ex = TX_OVERPARAM ; return TRUE; } + + + s.MakeLower(); + + // la met la valeur sur la pile + pResult->SetValString( s ); + return TRUE; +} + +// string xxx ( string ) +// compilation + +CBotTypResult cStrStr( CBotVar* &pVar, void* pUser ) +{ + // il faut un paramètre + if ( pVar == NULL ) return CBotTypResult( TX_LOWPARAM ); + + // qui doit être une string + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( TX_BADSTRING ); + + // pas de 2e paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( TX_OVERPARAM ); + + // le résultat final est une string + return CBotTypResult( CBotTypString ); +} + + +void InitStringFunctions() +{ + CBotProgram::AddFunction("strlen", rStrLen, cIntStr ); + CBotProgram::AddFunction("strleft", rStrLeft, cStrStrInt ); + CBotProgram::AddFunction("strright", rStrRight, cStrStrInt ); + CBotProgram::AddFunction("strmid", rStrMid, cStrStrIntInt ); + + CBotProgram::AddFunction("strval", rStrVal, cFloatStr ); + CBotProgram::AddFunction("strfind", rStrFind, cIntStrStr ); + + CBotProgram::AddFunction("strupper", rStrUpper, cStrStr ); + CBotProgram::AddFunction("strlower", rStrLower, cStrStr ); +} diff --git a/src/CBot/TestCBot/B.txt b/src/CBot/TestCBot/B.txt new file mode 100644 index 00000000..53715f8a --- /dev/null +++ b/src/CBot/TestCBot/B.txt @@ -0,0 +1,18 @@ + + float [ ] TEST2 ( int [ ] param ) + { + float [ ] z; + for ( int i = 0 ; i < sizeof( param ) ; i++ ) try { z [i] = param [i] / 3; } + return z; + } + +extern public void T() +{ + int a [4]; + for ( int i = 0 ; i < 3 ; i++ ) a[i] = 4*i; + a [2] = 22; + + float [] b ; + b = TEST2 ( a ) ; + show ( a, b ); +} diff --git a/src/CBot/TestCBot/BUG2.txt b/src/CBot/TestCBot/BUG2.txt new file mode 100644 index 00000000..44de05a3 --- /dev/null +++ b/src/CBot/TestCBot/BUG2.txt @@ -0,0 +1,107 @@ +object object :: TT ( int n ) +{ + object XX = radar(); + if ( n == 0 ) return null; + + while ( null == XX ) XX = radar(); + return XX; +} + +extern void object::Attack( ) +{ + show ( TT ( 0 ) ) ; + show ( TT ( 1 ) ) ; + return; + + int list[]; + int i; + object p; + float dist, prox; + point dest; + boolean advance = true; + + TEST(0); // ne stoppe pas si erreur +// while ( F () != 0 ) F(1); + + i = 0; + list[i++] = WingedGrabber; + list[i++] = TrackedGrabber; + list[i++] = WheeledGrabber; + list[i++] = LeggedGrabber; + list[i++] = WingedShooter; + list[i++] = TrackedShooter; + list[i++] = WheeledShooter; + list[i++] = LeggedShooter; + list[i++] = WingedOrgaShooter; + list[i++] = TrackedOrgaShooter; + list[i++] = WheeledOrgaShooter; + list[i++] = LeggedOrgaShooter; + list[i++] = WingedSniffer; + list[i++] = TrackedSniffer; + list[i++] = WheeledSniffer; + list[i++] = LeggedSniffer; + list[i++] = Thumper; + list[i++] = PhazerShooter; + list[i++] = Recycler; + list[i++] = Shielder; + list[i++] = Subber; + list[i++] = Me; + list[i++] = 3333; + list[i++] = 3334; + list[i++] = 3335; + list[i++] = 3336; + list[i++] = 3337; + list[i++] = 3338; + list[i++] = 3339; + list[i++] = 3331; + list[i++] = 3332; + list[i++] = 3330; + list[i++] = 1111; + list[i++] = 1112; + + F(F(0)); + + while ( true ) + { + p = radar(list, 0, 360, 0, 1000); + if ( p == null ) + { + F(2); + } + else + { + dist = F(p.position, position); + if ( dist <= 40 && !advance ) + { + fire(p.position); + advance = true; + } + else + { +//? if ( RetBaseDistance() > 20 ) + { + prox = dist-(5+F()*5); + if ( prox < 5 ) prox = 5; + dest.x = (position.x-p.position.x)*prox/dist + p.position.x; + dest.y = (position.y-p.position.y)*prox/dist + p.position.y; + dest.z = (position.z-p.position.z)*prox/dist + p.position.z; + goto(dest); + advance = false; + } + } + } + } +} + +// Calcule la distance jusqu'à la base. + +float object::RetBaseDistance() +{ + object p; + float dist; + + p = radar(4444, 0, 360, 0, 1000); + if ( p == null ) return 1000; + dist = F(p.position, position); + return dist; +} \ No newline at end of file diff --git a/src/CBot/TestCBot/CBotConsoleDlg.cpp b/src/CBot/TestCBot/CBotConsoleDlg.cpp new file mode 100644 index 00000000..5f29e86d --- /dev/null +++ b/src/CBot/TestCBot/CBotConsoleDlg.cpp @@ -0,0 +1,205 @@ +// CBotConsoleDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "TestCBot.h" +#include "CBotConsoleDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CBotConsoleDlg dialog + + +CBotConsoleDlg::CBotConsoleDlg(CWnd* pParent /*=NULL*/) + : CDialog(CBotConsoleDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CBotConsoleDlg) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + m_pProg = NULL; + m_threadinfo.m_bRun = FALSE; + m_code = 0; +} + + +void CBotConsoleDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CBotConsoleDlg) + DDX_Control(pDX, IDOK, m_cOK); + DDX_Control(pDX, IDC_EDIT2, m_Edit2); + DDX_Control(pDX, IDC_EDIT1, m_Edit1); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CBotConsoleDlg, CDialog) + //{{AFX_MSG_MAP(CBotConsoleDlg) + ON_MESSAGE(WM_ENDPROG, EndProg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CBotConsoleDlg message handlers + +UINT ThreadProc(ThreadInfo *info) +{ + CTime t0 = CTime::GetCurrentTime(); + int Cpt = 0; + + info->m_pProg->Start("LaCommande"); + while ( !info->m_bStop && !info->m_pProg->Run() ) + { +#if 0 + const char* FunctionName; + const char* FN; + int start, end; + + info->m_pProg->GetRunPos(FunctionName, start, end); + + if ( FunctionName != NULL ) + { + info->m_pEditx->SetSel(start, end); + + char buffer[200]; + sprintf( buffer, "step %s, %d, %d",FunctionName, start, end); + AfxMessageBox( buffer ); + + int level = 0; + do + { + CBotVar* t = info->m_pProg->GivStackVars(FN, level--); + if ( FN != FunctionName ) break; + if ( t != NULL ) + { + CString s ; + while ( t != NULL ) + { + if (s.IsEmpty()) s+= "Stack -> "; + else s+= " , "; + s += t->GivValString(); + t = t->GivNext(); + } + AfxMessageBox(s); + } + } while (TRUE); + } +#endif + Cpt++; + if ( Cpt%50 == 0 ) info->m_pEdit1->ReplaceSel("."); + } + + if ( info->m_bStop ) + { + info->m_pEdit1->ReplaceSel("\r\nInterrompu\r\n"); + } + else if (info->m_pProg->GivError() == 0) + { + CTime t = CTime::GetCurrentTime(); + CTimeSpan ts = t - t0; + + char buffer[200]; + sprintf( buffer, "\r\nExécution terminée en %d secondes.\r\nInterrompue %d fois.\r\n", + ts.GetTotalSeconds(), Cpt); + + info->m_pEdit1->ReplaceSel(buffer); + } + + info->m_pWndMessage->SendMessage(WM_ENDPROG, 0, 0) ; + return 0 ; +} + +LONG CBotConsoleDlg::EndProg(UINT wparam, LONG lparam) +{ + m_threadinfo.m_bRun = FALSE; + + if (m_pProg->GetError(m_code, m_start, m_end)) + { + CBotString TextError; + TextError = CBotProgram::GivErrorText(m_code); + AfxMessageBox(TextError); + CDialog::OnCancel(); + return 1; + } + delete m_pProg; + m_pProg = NULL; + + m_Edit2.EnableWindow(TRUE); + m_cOK.EnableWindow(TRUE); + + m_Edit2.SetWindowText(""); + m_Edit2.SetFocus(); + return 0 ; +} + +void CBotConsoleDlg::OnOK() +{ + CTestCBotApp* pApp = (CTestCBotApp*)AfxGetApp(); + pApp->m_pConsole = &m_Edit1; + m_code = 0; + + CString Commande; + m_Edit2.GetWindowText(Commande); + + CString s = "void LaCommande() { " + Commande + " ;}"; + m_pProg = new CBotProgram(); + CBotStringArray liste; + m_pProg->Compile(s, liste); + + int err, start, end; + if ( m_pProg->GetError(err, start, end) ) + { + CBotString TextError; + TextError = CBotProgram::GivErrorText(err); + AfxMessageBox(TextError); + m_Edit2.SetSel(start-20, end-20); + return; + } + + m_Edit1.ReplaceSel("\r\n" + Commande + " ->\r\n"); + + m_Edit2.SetWindowText(""); + m_Edit1.SetFocus(); + m_Edit2.EnableWindow(FALSE); + m_cOK.EnableWindow(FALSE); + + // lance un processus paralèle pour l'exécution + m_threadinfo.m_pWndMessage = this ; + + m_threadinfo.m_pEdit1 = &m_Edit1; + m_threadinfo.m_pEditx = m_pEditx; + m_threadinfo.m_pProg = m_pProg; + m_threadinfo.m_bStop = FALSE; + m_threadinfo.m_bRun = TRUE; + + AfxBeginThread((AFX_THREADPROC)ThreadProc, &m_threadinfo) ; +} + +void CBotConsoleDlg::OnCancel() +{ + if (!m_threadinfo.m_bRun) CDialog::OnCancel(); + m_threadinfo.m_bStop = TRUE ; +} + + +BOOL CBotConsoleDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_Edit1.ReplaceSel("Les fonctions suivantes sont disponibles:\r\n"); + for ( int i = 0; i < m_pListe->GivSize(); i++ ) + { + CBotString x = (*m_pListe)[i] + "\r\n"; + m_Edit1.ReplaceSel(x); + } + m_Edit1.ReplaceSel("Entrez une commande ci-dessous.\r\n\r\n"); + + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/CBot/TestCBot/CBotConsoleDlg.h b/src/CBot/TestCBot/CBotConsoleDlg.h new file mode 100644 index 00000000..bae97939 --- /dev/null +++ b/src/CBot/TestCBot/CBotConsoleDlg.h @@ -0,0 +1,69 @@ +#if !defined(AFX_BOTCONSOLEDLG_H__A11450A2_8E09_11D4_A439_00D059085115__INCLUDED_) +#define AFX_BOTCONSOLEDLG_H__A11450A2_8E09_11D4_A439_00D059085115__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// CBotConsoleDlg.h : header file +// + +struct ThreadInfo +{ + CEdit* m_pEdit1 ; + CEdit* m_pEditx ; + CBotProgram* m_pProg; + CWnd* m_pWndMessage; + BOOL m_bStop; + BOOL m_bRun; +}; + + +///////////////////////////////////////////////////////////////////////////// +// CBotConsoleDlg dialog + +class CBotConsoleDlg : public CDialog +{ +// Construction +public: + CBotConsoleDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CBotConsoleDlg) + enum { IDD = IDD_CONSOLE }; + CButton m_cOK; + CEdit m_Edit2; + CEdit m_Edit1; + //}}AFX_DATA + + CBotProgram* m_pProg; + ThreadInfo m_threadinfo; + + CBotStringArray* + m_pListe; + int m_code, m_start, m_end; + CEdit* m_pEditx; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CBotConsoleDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CBotConsoleDlg) + virtual void OnOK(); + virtual void OnCancel(); + virtual BOOL OnInitDialog(); + afx_msg LONG EndProg(UINT wparam, LONG lparam) ; + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_BOTCONSOLEDLG_H__A11450A2_8E09_11D4_A439_00D059085115__INCLUDED_) diff --git a/src/CBot/TestCBot/ChildFrm.cpp b/src/CBot/TestCBot/ChildFrm.cpp new file mode 100644 index 00000000..9005c728 --- /dev/null +++ b/src/CBot/TestCBot/ChildFrm.cpp @@ -0,0 +1,58 @@ +// ChildFrm.cpp : implementation of the CChildFrame class +// + +#include "stdafx.h" +#include "TestCBot.h" + +#include "ChildFrm.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CChildFrame + +IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWnd) + +BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd) + //{{AFX_MSG_MAP(CChildFrame) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CChildFrame construction/destruction + +CChildFrame::CChildFrame() +{ +} + +CChildFrame::~CChildFrame() +{ +} + +BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs) +{ + return CMDIChildWnd::PreCreateWindow(cs); +} + +///////////////////////////////////////////////////////////////////////////// +// CChildFrame diagnostics + +#ifdef _DEBUG +void CChildFrame::AssertValid() const +{ + CMDIChildWnd::AssertValid(); +} + +void CChildFrame::Dump(CDumpContext& dc) const +{ + CMDIChildWnd::Dump(dc); +} + +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CChildFrame message handlers diff --git a/src/CBot/TestCBot/ChildFrm.h b/src/CBot/TestCBot/ChildFrm.h new file mode 100644 index 00000000..ebcbeb29 --- /dev/null +++ b/src/CBot/TestCBot/ChildFrm.h @@ -0,0 +1,50 @@ +// ChildFrm.h : interface of the CChildFrame class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_CHILDFRM_H__4D1BB909_8E74_11D4_A439_00D059085115__INCLUDED_) +#define AFX_CHILDFRM_H__4D1BB909_8E74_11D4_A439_00D059085115__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +class CChildFrame : public CMDIChildWnd +{ + DECLARE_DYNCREATE(CChildFrame) +public: + CChildFrame(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CChildFrame) + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CChildFrame(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +// Generated message map functions +protected: + //{{AFX_MSG(CChildFrame) + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_CHILDFRM_H__4D1BB909_8E74_11D4_A439_00D059085115__INCLUDED_) diff --git a/src/CBot/TestCBot/Deleted.txt b/src/CBot/TestCBot/Deleted.txt new file mode 100644 index 00000000..469a624c --- /dev/null +++ b/src/CBot/TestCBot/Deleted.txt @@ -0,0 +1,23 @@ +public extern void object :: ESSAI() +{ + while(true) + { + if ( true ) + { + goto(12); + break; + } + } + object x = null ; + + while ( x == null ) x = radar(); + + show ( x.position ) ; + + TEST(5, x); + + if ( x == null ) show ( "DELETED" ); + + show ( x.position ) ; + +} \ No newline at end of file diff --git a/src/CBot/TestCBot/MaClass.txt b/src/CBot/TestCBot/MaClass.txt new file mode 100644 index 00000000..ac472b49 --- /dev/null +++ b/src/CBot/TestCBot/MaClass.txt @@ -0,0 +1,16 @@ + +class MaClass +{ + int a = 1 ; + MaClass pointeur ; + MaClass next = null ; + CPoint autre = new CPoint( 1 , 1 ) ; +} + +extern public void Test ( ) +{ + MaClass x () ; + x.next = new MaClass ( ) ; + println ( x ) ; +} + diff --git a/src/CBot/TestCBot/MainFrm.cpp b/src/CBot/TestCBot/MainFrm.cpp new file mode 100644 index 00000000..e151ccec --- /dev/null +++ b/src/CBot/TestCBot/MainFrm.cpp @@ -0,0 +1,100 @@ +// MainFrm.cpp : implementation of the CMainFrame class +// + +#include "stdafx.h" +#include "TestCBot.h" + +#include "MainFrm.h" +#include "TestCBotDoc.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame + +IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd) + +BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd) + //{{AFX_MSG_MAP(CMainFrame) + ON_WM_CREATE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +static UINT indicators[] = +{ + ID_SEPARATOR, // status line indicator + ID_INDICATOR_CAPS, + ID_INDICATOR_NUM, + ID_INDICATOR_SCRL, +}; + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame construction/destruction + +CMainFrame::CMainFrame() +{ +} + +CMainFrame::~CMainFrame() +{ +} + +int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + if (!m_wndToolBar.Create(this) || + !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) + { + TRACE0("Failed to create toolbar\n"); + return -1; // fail to create + } + + if (!m_wndStatusBar.Create(this) || + !m_wndStatusBar.SetIndicators(indicators, + sizeof(indicators)/sizeof(UINT))) + { + TRACE0("Failed to create status bar\n"); + return -1; // fail to create + } + + m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | + CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); + + m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); + EnableDocking(CBRS_ALIGN_ANY); + DockControlBar(&m_wndToolBar); + + return 0; +} + +BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) +{ + return CMDIFrameWnd::PreCreateWindow(cs); +} + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame diagnostics + +#ifdef _DEBUG +void CMainFrame::AssertValid() const +{ + CMDIFrameWnd::AssertValid(); +} + +void CMainFrame::Dump(CDumpContext& dc) const +{ + CMDIFrameWnd::Dump(dc); +} + +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame message handlers + + diff --git a/src/CBot/TestCBot/MainFrm.h b/src/CBot/TestCBot/MainFrm.h new file mode 100644 index 00000000..b9d68dba --- /dev/null +++ b/src/CBot/TestCBot/MainFrm.h @@ -0,0 +1,56 @@ +// MainFrm.h : interface of the CMainFrame class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_MAINFRM_H__4D1BB907_8E74_11D4_A439_00D059085115__INCLUDED_) +#define AFX_MAINFRM_H__4D1BB907_8E74_11D4_A439_00D059085115__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +class CMainFrame : public CMDIFrameWnd +{ + DECLARE_DYNAMIC(CMainFrame) +public: + CMainFrame(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMainFrame) + public: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CMainFrame(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: // control bar embedded members + CStatusBar m_wndStatusBar; + CToolBar m_wndToolBar; + +// Generated message map functions +protected: + //{{AFX_MSG(CMainFrame) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAINFRM_H__4D1BB907_8E74_11D4_A439_00D059085115__INCLUDED_) diff --git a/src/CBot/TestCBot/Mc2.txt b/src/CBot/TestCBot/Mc2.txt new file mode 100644 index 00000000..172c259f --- /dev/null +++ b/src/CBot/TestCBot/Mc2.txt @@ -0,0 +1,4 @@ +class MaClass +{ + int t = 12; +} diff --git a/src/CBot/TestCBot/Mon fichier.txt b/src/CBot/TestCBot/Mon fichier.txt new file mode 100644 index 00000000..6b35bf83 --- /dev/null +++ b/src/CBot/TestCBot/Mon fichier.txt @@ -0,0 +1,2 @@ +Voici encore du texte +et une seconde ligne diff --git a/src/CBot/TestCBot/Nop.txt b/src/CBot/TestCBot/Nop.txt new file mode 100644 index 00000000..6a66f6f1 --- /dev/null +++ b/src/CBot/TestCBot/Nop.txt @@ -0,0 +1,4 @@ +public extern void Nop() +{ + while ( true ) {} +} \ No newline at end of file diff --git a/src/CBot/TestCBot/POS.txt b/src/CBot/TestCBot/POS.txt new file mode 100644 index 00000000..688e4fbc --- /dev/null +++ b/src/CBot/TestCBot/POS.txt @@ -0,0 +1,14 @@ +void object :: T ( ) +{ + show ( position ) ; +} + +public extern void object :: POS() +{ + for ( int i = 0; i < 10 ; i++ ) + { + if ( i == 2 ) TEST ( 12 ) ; +// show ( position ); + T ( ) ; + } +} \ No newline at end of file diff --git a/src/CBot/TestCBot/PerformDlg.cpp b/src/CBot/TestCBot/PerformDlg.cpp new file mode 100644 index 00000000..a541f0b7 --- /dev/null +++ b/src/CBot/TestCBot/PerformDlg.cpp @@ -0,0 +1,161 @@ +// PerformDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "testcbot.h" +#include "PerformDlg.h" + +//#include +#include +//#include + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPerformDlg dialog + + +CPerformDlg::CPerformDlg(CWnd* pParent /*=NULL*/) + : CDialog(CPerformDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CPerformDlg) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void CPerformDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CPerformDlg) + DDX_Control(pDX, IDC_EDIT3, m_Edit3); + DDX_Control(pDX, IDC_EDIT1, m_Edit1); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CPerformDlg, CDialog) + //{{AFX_MSG_MAP(CPerformDlg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPerformDlg message handlers + +/* Pauses for a specified number of milliseconds. */ + +/*void sleep( double waitseconds ) +{ + clock_t wait = (clock_t)(waitseconds * CLOCKS_PER_SEC); + clock_t goal; + goal = wait + clock(); + while( goal > clock() ) + ; +}*/ + +void sleep( clock_t wait ) +{ + clock_t goal; + goal = wait + clock(); + while( goal > clock() ) + TRACE("%d \n", clock() ); +} + +void sleep2( clock_t wait ) +{ + struct _timeb timebuffer; + char *timeline; + + _ftime( &timebuffer ); + timeline = ctime( & ( timebuffer.time ) ); + long x = timebuffer.millitm; + while( x == timebuffer.millitm ) _ftime( &timebuffer ); +} + +#define NBLP 20 + +UINT ThreadProc2(ThreadInfo2 *info) +{ + int lp = NBLP; + int i; + clock_t start = clock(); + + while ( !info->m_bStop ) + { + for ( i = 0; i< info->m_nbscripts; i++ ) + { + info->m_pProg[i]->Run(); + } + +#ifdef _DEBUG + sleep2( 1 ); +#else + CString s ( "xx" ); + for ( long z = 0x5000; z>0; z-- ) s = s.Left(1); +#endif + if ( --lp == 0 ) + { + clock_t finish = clock(); + double n = (double)NBLP / (double)(finish-start) * CLOCKS_PER_SEC; + char b[30]; + sprintf( b, "%f", n); + info->m_pEdit->SetWindowText(b); + + n = n * 1100 / 200; // performances + sprintf( b, "%f", n); + info->m_pEdit3->SetWindowText(b); + start = finish; + lp = NBLP; + } + } + + return 0 ; +} + +BOOL CPerformDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + + CBotStringArray liste; + // crée les scripts pour les tests + for ( int i = 0; i < 100; i++ ) + { + m_pProg[i] = new CBotProgram(); + m_pProg[i]->Compile(m_Script, liste); + m_pProg[i]->Start(liste[0]); + } + + // lance un processus paralèle pour l'exécution +// m_threadinfo2.m_pWndMessage = this ; + + m_threadinfo2.m_pEdit = &m_Edit1; + m_threadinfo2.m_pEdit3 = &m_Edit3; + m_threadinfo2.m_pProg = m_pProg; + m_threadinfo2.m_bStop = FALSE; + m_threadinfo2.m_nbscripts = 30; + + + AfxBeginThread((AFX_THREADPROC)ThreadProc2, &m_threadinfo2) ; + // TODO: Add extra initialization here + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CPerformDlg::OnCancel() +{ + m_threadinfo2.m_bStop = TRUE; + sleep ( 2000 ); + + CDialog::OnCancel(); + + for ( int i = 0; i < 100; i++ ) + { + delete m_pProg[i]; + } +} diff --git a/src/CBot/TestCBot/PerformDlg.h b/src/CBot/TestCBot/PerformDlg.h new file mode 100644 index 00000000..77ca71a2 --- /dev/null +++ b/src/CBot/TestCBot/PerformDlg.h @@ -0,0 +1,62 @@ +#if !defined(AFX_PERFORMDLG_H__EAF2D560_97D8_11D4_A439_00D059085115__INCLUDED_) +#define AFX_PERFORMDLG_H__EAF2D560_97D8_11D4_A439_00D059085115__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// PerformDlg.h : header file +// + +struct ThreadInfo2 +{ + CEdit* m_pEdit ; + CEdit* m_pEdit3 ; + + CBotProgram** m_pProg; + BOOL m_bStop; + int m_nbscripts; +}; + + +///////////////////////////////////////////////////////////////////////////// +// CPerformDlg dialog + +class CPerformDlg : public CDialog +{ +// Construction +public: + CPerformDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CPerformDlg) + enum { IDD = IDD_DIALOG1 }; + CEdit m_Edit3; + CEdit m_Edit1; + //}}AFX_DATA + + CBotProgram* m_pProg[100]; + ThreadInfo2 m_threadinfo2; + CString m_Script; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPerformDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CPerformDlg) + virtual BOOL OnInitDialog(); + virtual void OnCancel(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PERFORMDLG_H__EAF2D560_97D8_11D4_A439_00D059085115__INCLUDED_) diff --git a/src/CBot/TestCBot/Routines.cpp b/src/CBot/TestCBot/Routines.cpp new file mode 100644 index 00000000..cd3a32ba --- /dev/null +++ b/src/CBot/TestCBot/Routines.cpp @@ -0,0 +1,139 @@ + + +//////////////////////////////////////////////////////////////////// +// routine show() +// utilisable depuis le programme écrit en CBot + +// exécution +BOOL rShow( CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser ) +{ + CString s; + + while ( pVar != NULL ) + { + CString ss; + ss.LoadString( TX_TYPENAMES + pVar->GivType() ); + s += ss + " "; + + ss = pVar->GivName(); + if (ss.IsEmpty()) ss = ""; + s += ss + " = "; + + s += pVar->GivValString(); + s += "\n"; + pVar = pVar->GivNext(); + } + + AfxMessageBox(s, MB_OK|MB_ICONINFORMATION); + + return TRUE; // pas d'interruption +} + +CBotTypResult cShow( CBotVar* &pVar, void* pUser) +{ + if ( pVar == NULL ) return CBotTypResult(5028); + return CBotTypResult(0); // tous paramètres acceptés, void en retour +} + + +//////////////////////////////////////////////////////////////////// +// routine print() +// utilisable depuis le programme écrit en CBot + +// exécution +BOOL rPrintLn( CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser ) +{ + CString s; + + CTestCBotApp* pApp = (CTestCBotApp*)AfxGetApp(); + CEdit* pEdit = pApp->m_pConsole; + + if (pEdit == NULL) return TRUE; + pEdit->GetWindowText(s); + + while ( pVar != NULL ) + { + if ( !s.IsEmpty() ) s += " "; + s += pVar->GivValString(); + pVar = pVar->GivNext(); + } + s += "\r\n"; + + pEdit->SetWindowText(s); + pEdit->SetSel(s.GetLength(), s.GetLength()); + pEdit->SetFocus(); + return TRUE; // pas d'interruption +} + +BOOL rPrint( CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser ) +{ + CString s; + + CTestCBotApp* pApp = (CTestCBotApp*)AfxGetApp(); + CEdit* pEdit = pApp->m_pConsole; + + if (pEdit == NULL) return TRUE; + pEdit->GetWindowText(s); + + while ( pVar != NULL ) + { + if ( !s.IsEmpty() ) s += " "; + s += pVar->GivValString(); + pVar = pVar->GivNext(); + } + + pEdit->SetWindowText(s); + pEdit->SetSel(s.GetLength(), s.GetLength()); + pEdit->SetFocus(); + return TRUE; // pas d'interruption +} + +CBotTypResult cPrint( CBotVar* &pVar, void* pUser) +{ + return CBotTypResult(0); // tous paramètres acceptés, un entier en retour +} + + +////////////////////////////////////////////////////////////////// +// class CPoint pour essayer + +// exécution +BOOL rCPoint( CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception ) +{ + CString s; + + if ( pVar == NULL )return TRUE; // constructeur sans paramètres est ok + + CBotVar* pX = pThis->GivItem("x"); + pX->SetValFloat( pVar->GivValFloat() ); + pVar = pVar->GivNext(); + + CBotVar* pY = pThis->GivItem("y"); + pY->SetValFloat( pVar->GivValFloat() ); + pVar = pVar->GivNext(); + + return TRUE; // pas d'interruption +} + +CBotTypResult cCPoint( CBotVar* pThis, CBotVar* &pVar) +{ + // ok si aucun paramètres ! + if ( pVar == NULL ) return CBotTypResult(0); + + // paramètre de type numérique svp + if ( pVar->GivType() > CBotTypDouble ) return CBotTypResult(5011); + pVar = pVar->GivNext(); + + // il doit y avoir un second paramètre + if ( pVar == NULL ) return 5028; + // également de type numérique + if ( pVar->GivType() > CBotTypDouble )return CBotTypResult(5011); + pVar = pVar->GivNext(); + + // et pas plus de 2 paramètres svp + if ( pVar != NULL ) return CBotTypResult(5026); + + return CBotTypResult(0); // cette fonction retourne void +} + + diff --git a/src/CBot/TestCBot/StdAfx.cpp b/src/CBot/TestCBot/StdAfx.cpp new file mode 100644 index 00000000..b1dde8d0 --- /dev/null +++ b/src/CBot/TestCBot/StdAfx.cpp @@ -0,0 +1,6 @@ +// stdafx.cpp : source file that includes just the standard includes +// TestCBot.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + diff --git a/src/CBot/TestCBot/StdAfx.h b/src/CBot/TestCBot/StdAfx.h new file mode 100644 index 00000000..7be8c409 --- /dev/null +++ b/src/CBot/TestCBot/StdAfx.h @@ -0,0 +1,26 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__4D1BB905_8E74_11D4_A439_00D059085115__INCLUDED_) +#define AFX_STDAFX_H__4D1BB905_8E74_11D4_A439_00D059085115__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC OLE automation classes +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__4D1BB905_8E74_11D4_A439_00D059085115__INCLUDED_) diff --git a/src/CBot/TestCBot/T.txt b/src/CBot/TestCBot/T.txt new file mode 100644 index 00000000..50a792b4 --- /dev/null +++ b/src/CBot/TestCBot/T.txt @@ -0,0 +1,4 @@ +public extern int T ( float n ) +{ + return n * 1.1; +} \ No newline at end of file diff --git a/src/CBot/TestCBot/TESTALL.txt b/src/CBot/TestCBot/TESTALL.txt new file mode 100644 index 00000000..82247a0f --- /dev/null +++ b/src/CBot/TestCBot/TESTALL.txt @@ -0,0 +1,161 @@ +int T ( int z ) +{ + return 45 + z ; +} + +class toto +{ + int val = 3 ; + int x = 3 * 3 ; + void toto( int n ) + { val = n + 3 ; } + int retval ( int param ) + { int r = val + param + x ; + val = param ; + return r ; } +} + +public extern void object :: Chose( ) +{ + int z [ 6 ]; + for ( int i = 0 ; i < 6 ; ) z [ i++ ] = 3 - i ; + show ( z ) ; + return; + + // test des tableaux + int [ ] a [ 3 ] ; +// a = null; + if ( a == null ) show ( "NULL" ); + + a [ 2 / 2 ] [ 2 ]= 5 ; + int [ ] b ; b = a [1] ; + b [ 0 ] = -4; + a [ 4 / 2 ] [ 1 ]= 1 ; + show ( a , b ) ; + return ; + { + toto chose = new toto (5 ) ; + toto truc = chose ; + show ( chose, chose.retval( 100 ) , + truc, truc.retval (40 ) ) ; + + return; + } + { + point A = new + point ( 4 * 4 , 2 ) ; + show ( A ) ; + return; + } + { + show ( T ( 1 ) , T ( 3.7 ) ) ; + return; + } + + { + point A ( 3, 4 ) , + B = A ; + + int n = -4; + show ( n ); + + show ( A, B ) ; + + boolean a = false; + boolean b = a or true; + if ( not a and b ) ; + return; + } + { + // test try + float x = nan ; int z = 0 ; + try { +// throw ( 3 * 4 + 33 ) ; + int zz ; goto ( 12 ) ; z = 1 ; z = 0 / 0 ; z = 2 ; + } + catch ( 45 + 0 * 6000 ) + { + show( "Exception 6000", z ) ; + } + catch ( x == 0 ) { show( "x nul" ) ; } + finally { show ( "fini" ) ; } + show ( "continue" ); + return; + } + { + // test des if + int a = 3; + if ( a == 3 ) show ( "33"); + else show ( "44"); + if ( a != 3 ) show ( "333"); + else show ( "444"); + return; + } + { + int a = 0; + // test break +un: + while ( true ) + { +deux: + while ( true ) + { + a++; + if ( a == 2 ) continue; + if ( a == 3 ) break deux; + show ( a ) ; + if ( a == 5 ) break un; + } + show ( "DEUX" ); + } + return; + } + { + // test switch + int a = 0; + + switch ( a ) + { + case 1 : show( "un" ) ; break; + case 2 : show( "deux" ) ; // break; + case 3 : show( "trois" ) ; break; + case 4 : show( "quatre" ) ; // break; + default : show( "par défaut" ) ; + } + return; + } + { + // test boucle while + float z = 3.3; + while ( z > 0 ) + { show ( z-- ) ; } + return; + } + + { + // test boucle do + float y = 3.3; + do { int x = 0; show(y); y++; } while ( y < 7 ) ; + return; + } + // test boucle for + int j = -7; show ( j ); + for ( int ii = 3, j = 31; ii < 6 ; ++ii, j = j -3 ) + { + j = 10 * j; + show ( ii, j ); + } + return; +{ + // déclarations de variables + int a; int b = 3; int c = 4*b, d = 1, e; + float x; float y = 3.3; float z = y / 2, u = 1, v; + boolean t; boolean tt = true or false; boolean ttt = false, tttt = true, t5; + string s; string ss = "hello"; string s2 = ss + " plus", s3 = "s3", s4; + + show( b, c, d ); + show( y, z, u ); + show( tt, ttt, tttt ); + show( ss, s2, s3 ); +} +} \ No newline at end of file diff --git a/src/CBot/TestCBot/TestCB1.txt b/src/CBot/TestCBot/TestCB1.txt new file mode 100644 index 00000000..516db47d --- /dev/null +++ b/src/CBot/TestCBot/TestCB1.txt @@ -0,0 +1,18 @@ +extern public void toto() +{ + print( "hello" ) ; + print( fac(5) ); + print( t() ) ; +} + +public int fac(int n) +{ + if ( n<2 ) return 1; + return n * fac(n-1); +} + +point t() +{ + point a(1,2); + return a; +} diff --git a/src/CBot/TestCBot/TestCBot.clw b/src/CBot/TestCBot/TestCBot.clw new file mode 100644 index 00000000..13f20f46 --- /dev/null +++ b/src/CBot/TestCBot/TestCBot.clw @@ -0,0 +1,316 @@ +; CLW file contains information for the MFC ClassWizard + +[General Info] +Version=1 +LastClass=CPerformDlg +LastTemplate=CDialog +NewFileInclude1=#include "stdafx.h" +NewFileInclude2=#include "testcbot.h" +LastPage=0 + +ClassCount=8 +Class1=CBotConsoleDlg +Class2=CChildFrame +Class3=CMainFrame +Class4=CTestCBotApp +Class5=CAboutDlg +Class6=CTestCBotDoc +Class7=CTestCBotView + +ResourceCount=12 +Resource1=IDD_CONSOLE +Resource2=IDR_TESTCBTYPE (French (France)) +Resource3=IDD_ABOUTBOX (French (France)) +Resource4=IDR_MAINFRAME (French (France)) +Resource5=IDR_MAINFRAME +Resource6=IDR_TESTCBTYPE +Resource7=IDD_ABOUTBOX +Resource8=IDD_CONSOLE (French (Switzerland)) +Class8=CPerformDlg +Resource9=IDD_DIALOG1 +Resource10=IDD_DIALOG2 +Resource11=IDD_DIALOG1 (French (Switzerland)) +Resource12=IDD_DIALOG2 (French (France)) + +[CLS:CBotConsoleDlg] +Type=0 +BaseClass=CDialog +HeaderFile=CBotConsoleDlg.h +ImplementationFile=CBotConsoleDlg.cpp +LastObject=IDC_EDIT1 + +[CLS:CChildFrame] +Type=0 +BaseClass=CMDIChildWnd +HeaderFile=ChildFrm.h +ImplementationFile=ChildFrm.cpp + +[CLS:CMainFrame] +Type=0 +BaseClass=CMDIFrameWnd +HeaderFile=MainFrm.h +ImplementationFile=MainFrm.cpp +Filter=T +VirtualFilter=fWC +LastObject=CMainFrame + +[CLS:CTestCBotApp] +Type=0 +BaseClass=CWinApp +HeaderFile=TestCBot.h +ImplementationFile=TestCBot.cpp +Filter=N +VirtualFilter=AC +LastObject=ID_TEST + +[CLS:CAboutDlg] +Type=0 +BaseClass=CDialog +HeaderFile=TestCBot.cpp +ImplementationFile=TestCBot.cpp +LastObject=CAboutDlg + +[CLS:CTestCBotDoc] +Type=0 +BaseClass=CDocument +HeaderFile=TestCBotDoc.h +ImplementationFile=TestCBotDoc.cpp +LastObject=CTestCBotDoc +Filter=N +VirtualFilter=DC + +[CLS:CTestCBotView] +Type=0 +BaseClass=CView +HeaderFile=TestCBotView.h +ImplementationFile=TestCBotView.cpp +LastObject=CTestCBotView +Filter=C +VirtualFilter=VWC + +[DLG:IDD_CONSOLE] +Type=1 +Class=CBotConsoleDlg +ControlCount=4 +Control1=IDC_STATIC,static,1342308352 +Control2=IDC_EDIT2,edit,1350631552 +Control3=IDOK,button,1342242817 +Control4=IDC_EDIT1,edit,1352734724 + +[DLG:IDD_ABOUTBOX] +Type=1 +Class=CAboutDlg +ControlCount=7 +Control1=IDC_STATIC,static,1342177283 +Control2=IDC_STATIC,static,1342308480 +Control3=IDC_STATIC,static,1342308352 +Control4=IDOK,button,1342373889 +Control5=IDC_STATIC,static,1342308352 +Control6=IDC_STATIC,static,1342308352 +Control7=IDC_STATIC,static,1342308352 + +[TB:IDR_MAINFRAME (French (France))] +Type=1 +Class=? +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_SAVE +Command4=ID_EDIT_CUT +Command5=ID_EDIT_COPY +Command6=ID_EDIT_PASTE +Command7=ID_FILE_PRINT +Command8=ID_RUN +Command9=ID_APP_ABOUT +CommandCount=9 + +[MNU:IDR_MAINFRAME (French (France))] +Type=1 +Class=? +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_MRU_FILE1 +Command4=ID_APP_EXIT +Command5=ID_VIEW_TOOLBAR +Command6=ID_VIEW_STATUS_BAR +Command7=ID_APP_ABOUT +CommandCount=7 + +[MNU:IDR_TESTCBTYPE (French (France))] +Type=1 +Class=? +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_CLOSE +Command4=ID_FILE_SAVE +Command5=ID_FILE_SAVE_AS +Command6=ID_FILE_MRU_FILE1 +Command7=ID_APP_EXIT +Command8=ID_EDIT_UNDO +Command9=ID_EDIT_CUT +Command10=ID_EDIT_COPY +Command11=ID_EDIT_PASTE +Command12=ID_VIEW_TOOLBAR +Command13=ID_VIEW_STATUS_BAR +Command14=ID_WINDOW_NEW +Command15=ID_WINDOW_CASCADE +Command16=ID_WINDOW_TILE_HORZ +Command17=ID_WINDOW_ARRANGE +Command18=ID_APP_ABOUT +CommandCount=18 + +[ACL:IDR_MAINFRAME (French (France))] +Type=1 +Class=? +Command1=ID_EDIT_COPY +Command2=ID_FILE_NEW +Command3=ID_FILE_OPEN +Command4=ID_FILE_SAVE +Command5=ID_EDIT_PASTE +Command6=ID_EDIT_UNDO +Command7=ID_EDIT_CUT +Command8=ID_RUN +Command9=ID_NEXT_PANE +Command10=ID_PREV_PANE +Command11=ID_RUN +Command12=ID_TEST +Command13=ID_EDIT_COPY +Command14=ID_EDIT_PASTE +Command15=ID_EDIT_CUT +Command16=ID_EDIT_UNDO +CommandCount=16 + +[DLG:IDD_ABOUTBOX (French (France))] +Type=1 +Class=CAboutDlg +ControlCount=7 +Control1=IDC_STATIC,static,1342177283 +Control2=IDC_STATIC,static,1342308480 +Control3=IDC_STATIC,static,1342308352 +Control4=IDOK,button,1342373889 +Control5=IDC_STATIC,static,1342308352 +Control6=IDC_STATIC,static,1342308352 +Control7=IDC_STATIC,static,1342308352 + +[ACL:IDR_MAINFRAME] +Type=1 +Command1=ID_EDIT_COPY +Command2=ID_FILE_NEW +Command3=ID_FILE_OPEN +Command4=ID_FILE_SAVE +Command5=ID_EDIT_PASTE +Command6=ID_EDIT_UNDO +Command7=ID_EDIT_CUT +Command8=ID_RUN +Command9=ID_NEXT_PANE +Command10=ID_PREV_PANE +Command11=ID_RUN +Command12=ID_TEST +Command13=ID_EDIT_COPY +Command14=ID_EDIT_PASTE +Command15=ID_EDIT_CUT +Command16=ID_EDIT_UNDO +CommandCount=16 + +[TB:IDR_MAINFRAME] +Type=1 +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_SAVE +Command4=ID_EDIT_CUT +Command5=ID_EDIT_COPY +Command6=ID_EDIT_PASTE +Command7=ID_FILE_PRINT +Command8=ID_RUN +Command9=ID_APP_ABOUT +CommandCount=9 + +[MNU:IDR_MAINFRAME] +Type=1 +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_MRU_FILE1 +Command4=ID_APP_EXIT +Command5=ID_VIEW_TOOLBAR +Command6=ID_VIEW_STATUS_BAR +Command7=ID_APP_ABOUT +CommandCount=7 + +[MNU:IDR_TESTCBTYPE] +Type=1 +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_CLOSE +Command4=ID_FILE_SAVE +Command5=ID_FILE_SAVE_AS +Command6=ID_FILE_MRU_FILE1 +Command7=ID_APP_EXIT +Command8=ID_EDIT_UNDO +Command9=ID_EDIT_CUT +Command10=ID_EDIT_COPY +Command11=ID_EDIT_PASTE +Command12=ID_VIEW_TOOLBAR +Command13=ID_VIEW_STATUS_BAR +Command14=ID_WINDOW_NEW +Command15=ID_WINDOW_CASCADE +Command16=ID_WINDOW_TILE_HORZ +Command17=ID_WINDOW_ARRANGE +Command18=ID_APP_ABOUT +CommandCount=18 + +[DLG:IDD_CONSOLE (French (Switzerland))] +Type=1 +Class=CBotConsoleDlg +ControlCount=4 +Control1=IDC_STATIC,static,1342308352 +Control2=IDC_EDIT2,edit,1350631552 +Control3=IDOK,button,1342242817 +Control4=IDC_EDIT1,edit,1352734724 + +[DLG:IDD_DIALOG1] +Type=1 +Class=CPerformDlg +ControlCount=9 +Control1=IDC_STATIC,static,1342308352 +Control2=IDC_EDIT1,edit,1350633600 +Control3=IDC_STATIC,static,1342308352 +Control4=IDC_EDIT2,edit,1350631552 +Control5=IDC_SPIN1,msctls_updown32,1342177312 +Control6=IDC_COMBO1,combobox,1344339971 +Control7=IDC_STATIC,static,1342308352 +Control8=IDC_STATIC,static,1342308352 +Control9=IDC_EDIT3,edit,1350633600 + +[CLS:CPerformDlg] +Type=0 +HeaderFile=PerformDlg.h +ImplementationFile=PerformDlg.cpp +BaseClass=CDialog +Filter=D +VirtualFilter=dWC +LastObject=IDC_EDIT3 + +[DLG:IDD_DIALOG2] +Type=1 +ControlCount=2 +Control1=IDOK,button,1342242817 +Control2=IDCANCEL,button,1342242816 + +[DLG:IDD_DIALOG1 (French (Switzerland))] +Type=1 +ControlCount=9 +Control1=IDC_STATIC,static,1342308352 +Control2=IDC_EDIT1,edit,1350633600 +Control3=IDC_STATIC,static,1342308352 +Control4=IDC_EDIT2,edit,1350631552 +Control5=IDC_SPIN1,msctls_updown32,1342177312 +Control6=IDC_COMBO1,combobox,1344339971 +Control7=IDC_STATIC,static,1342308352 +Control8=IDC_STATIC,static,1342308352 +Control9=IDC_EDIT3,edit,1350633600 + +[DLG:IDD_DIALOG2 (French (France))] +Type=1 +ControlCount=2 +Control1=IDOK,button,1342242817 +Control2=IDCANCEL,button,1342242816 + diff --git a/src/CBot/TestCBot/TestCBot.cpp b/src/CBot/TestCBot/TestCBot.cpp new file mode 100644 index 00000000..7e2aabb0 --- /dev/null +++ b/src/CBot/TestCBot/TestCBot.cpp @@ -0,0 +1,253 @@ +// TestCBot.cpp : Defines the class behaviors for the application. +// + +#include "stdafx.h" +#include "TestCBot.h" + +#include "MainFrm.h" +#include "ChildFrm.h" +#include "TestCBotDoc.h" +#include "TestCBotView.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotApp + +BEGIN_MESSAGE_MAP(CTestCBotApp, CWinApp) + //{{AFX_MSG_MAP(CTestCBotApp) + ON_COMMAND(ID_APP_ABOUT, OnAppAbout) + //}}AFX_MSG_MAP + // Standard file based document commands + ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) + ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotApp construction + +CTestCBotApp::CTestCBotApp() +{ + m_pConsole = NULL; + m_LastActive = NULL; + m_pClassPoint= NULL; +} + + +///////////////////////////////////////////////////////////////////////////// +// The one and only CTestCBotApp object + +CTestCBotApp theApp; + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotApp initialization + +#include "Routines.cpp" + + +static char BASED_CODE szSection[] = "Recent File List"; +static char BASED_CODE szFilename[] = "File1"; + + +#include "../ClassFILE.cpp" + +// routine pour mettre à jour l'instance de la classe Bot courante +void rMajObject( CBotVar* pThis, void* pUser ) +{ + if (!pThis->IsElemOfClass("object")) + return ; + CBotVar* pPos = pThis->GivItem("position"); + CBotVar* pX = pPos->GivItem("x"); + CBotVar* pY = pPos->GivItem("y"); + CBotVar* pZ = pPos->GivItem("z"); +// CBotVar* pPt = pThis->GivItem("transport"); + + CBotString p = pX->GivValString(); + +// pX->SetValFloat( pUser == (void*)1 ? (float)12.5 : (float)44.4 ); + pZ->SetValFloat( (float)0 ); + pY->SetValFloat( (float)-3.33 ); + pX->SetValFloat( pX->GivValFloat() + 10 ) ; + +// pX = pThis->GivItem( "xx" ); +// pX->SetValFloat( (float)22 ); + + // crée une instance sur une classe object +// CBotVar* pAutre = CBotVar::Create("autre", CBotTypClass, "object"); +// pAutre->SetUserPtr( (void*)3 ); +// pPt->SetPointer( pAutre ); +// pPt->SetPointer( NULL ); +// delete pAutre; +} + + +BOOL CTestCBotApp::InitInstance() +{ +////////////////////////////////////////////// +// défini les mots clefs supplémentaires +// ------------------------------------------- + + CBotProgram::Init(); + +////////////////////////////////////////////// +// défini les fonctions "show()" et "print()" +// ------------------------------------------- + + CBotProgram::AddFunction("show", rShow, cShow); + CBotProgram::AddFunction("print", rPrint, cPrint); + CBotProgram::AddFunction("println", rPrintLn, cPrint); + + +/////////////////////////////////// +// définie la classe globale CPoint +// -------------------------------- + + m_pClassPoint = new CBotClass("CPoint", NULL); + // ajoute le composant ".x" + m_pClassPoint->AddItem("x", CBotTypFloat); + // ajoute le composant ".y" + m_pClassPoint->AddItem("y", CBotTypFloat); + + // ajoute le constructeur pour cette classe + m_pClassPoint->AddFunction("CPoint", rCPoint, cCPoint); + + m_pClassPointIntr = new CBotClass("point", NULL, TRUE); + // ajoute le composant ".x" + m_pClassPointIntr->AddItem("x", CBotTypFloat); + // ajoute le composant ".y" + m_pClassPointIntr->AddItem("y", CBotTypFloat); + // ajoute le composant ".z" + m_pClassPointIntr->AddItem("z", CBotTypFloat); + + // ajoute le constructeur pour cette classe + m_pClassPointIntr->AddFunction("point", rCPoint, cCPoint); + + // défini la classe "object" + CBotClass* pClassObject = new CBotClass( "object", NULL ) ; + pClassObject->AddItem( "xx", CBotTypFloat ); + pClassObject->AddItem( "position", CBotTypResult( CBotTypIntrinsic, "point" ) ); + pClassObject->AddItem( "transport", CBotTypResult( CBotTypPointer, "object" ) ); + pClassObject->AddUpdateFunc( rMajObject ); + + InitClassFILE(); + + AfxEnableControlContainer(); + + // Standard initialization + +#ifdef _AFXDLL + Enable3dControls(); // Call this when using MFC in a shared DLL +#else + Enable3dControlsStatic(); // Call this when linking to MFC statically +#endif + + // Change the registry key under which our settings are stored. + SetRegistryKey(_T("Local AppWizard-Generated Applications")); + + LoadStdProfileSettings(); // Load standard INI file options (including MRU) + + // Register document templates + + CMultiDocTemplate* pDocTemplate; + pDocTemplate = new CMultiDocTemplate( + IDR_TESTCBTYPE, + RUNTIME_CLASS(CTestCBotDoc), + RUNTIME_CLASS(CChildFrame), // custom MDI child frame + RUNTIME_CLASS(CTestCBotView)); + AddDocTemplate(pDocTemplate); + + // create main MDI Frame window + CMainFrame* pMainFrame = new CMainFrame; + if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) + return FALSE; + m_pMainWnd = pMainFrame; + + // Parse command line for standard shell commands, DDE, file open + CCommandLineInfo cmdInfo; + ParseCommandLine(cmdInfo); + + if (m_lpCmdLine[0] == 0) + { + CString Filename = GetProfileString(szSection, szFilename); + if (Filename.IsEmpty()) Filename = "TstCbot.txt"; + else OpenDocumentFile(Filename); + } + else + // Dispatch commands specified on the command line + if (!ProcessShellCommand(cmdInfo)) + return FALSE; + pMainFrame->ShowWindow(m_nCmdShow); + pMainFrame->UpdateWindow(); + + + return TRUE; +} + + +///////////////////////////////////////////////////////////////////////////// +// CAboutDlg dialog used for App About + +class CAboutDlg : public CDialog +{ +public: + CAboutDlg(); + +// Dialog Data + //{{AFX_DATA(CAboutDlg) + enum { IDD = IDD_ABOUTBOX }; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAboutDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + //{{AFX_MSG(CAboutDlg) + // No message handlers + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) +{ + //{{AFX_DATA_INIT(CAboutDlg) + //}}AFX_DATA_INIT +} + +void CAboutDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAboutDlg) + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) + //{{AFX_MSG_MAP(CAboutDlg) + // No message handlers + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +// App command to run the dialog +void CTestCBotApp::OnAppAbout() +{ + CAboutDlg aboutDlg; + aboutDlg.DoModal(); +} + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotApp commands + +int CTestCBotApp::ExitInstance() +{ + delete m_pFuncFile; + + CBotProgram::Free(); + return CWinApp::ExitInstance(); +} diff --git a/src/CBot/TestCBot/TestCBot.dsp b/src/CBot/TestCBot/TestCBot.dsp new file mode 100644 index 00000000..8ed9b116 --- /dev/null +++ b/src/CBot/TestCBot/TestCBot.dsp @@ -0,0 +1,201 @@ +# Microsoft Developer Studio Project File - Name="TestCBot" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=TestCBot - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "TestCBot.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "TestCBot.mak" CFG="TestCBot - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "TestCBot - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "TestCBot - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "TestCBot - Win32 Release" + +# PROP BASE Use_MFC 5 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 5 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FR /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x100c /d "NDEBUG" +# ADD RSC /l 0x100c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386 +# ADD LINK32 /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "TestCBot - Win32 Debug" + +# PROP BASE Use_MFC 5 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 5 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x100c /d "_DEBUG" +# ADD RSC /l 0x100c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /stack:0x7010 /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "TestCBot - Win32 Release" +# Name "TestCBot - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CBotConsoleDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\ChildFrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\MainFrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\PerformDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"stdafx.h" +# End Source File +# Begin Source File + +SOURCE=.\TestCBot.cpp +# End Source File +# Begin Source File + +SOURCE=.\TestCBot.rc + +!IF "$(CFG)" == "TestCBot - Win32 Release" + +!ELSEIF "$(CFG)" == "TestCBot - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\TestCBotDoc.cpp +# End Source File +# Begin Source File + +SOURCE=.\TestCBotView.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CBotConsoleDlg.h +# End Source File +# Begin Source File + +SOURCE=.\ChildFrm.h +# End Source File +# Begin Source File + +SOURCE=.\MainFrm.h +# End Source File +# Begin Source File + +SOURCE=.\PerformDlg.h +# End Source File +# Begin Source File + +SOURCE=.\Resource.h +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# Begin Source File + +SOURCE=.\TestCBot.h +# End Source File +# Begin Source File + +SOURCE=.\TestCBotDoc.h +# End Source File +# Begin Source File + +SOURCE=.\TestCBotView.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\res\TestCBot.ico +# End Source File +# Begin Source File + +SOURCE=.\res\TestCBot.rc2 +# End Source File +# Begin Source File + +SOURCE=.\res\TestCBotDoc.ico +# End Source File +# Begin Source File + +SOURCE=.\res\Toolbar.bmp +# End Source File +# End Group +# Begin Source File + +SOURCE=..\Debug\CBot.lib +# End Source File +# End Target +# End Project diff --git a/src/CBot/TestCBot/TestCBot.h b/src/CBot/TestCBot/TestCBot.h new file mode 100644 index 00000000..f101fd10 --- /dev/null +++ b/src/CBot/TestCBot/TestCBot.h @@ -0,0 +1,64 @@ +// TestCBot.h : main header file for the TESTCBOT application +// + +#if !defined(AFX_TESTCBOT_H__4D1BB903_8E74_11D4_A439_00D059085115__INCLUDED_) +#define AFX_TESTCBOT_H__4D1BB903_8E74_11D4_A439_00D059085115__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // main symbols +//#include "../CbotDll.h" // librairie CBot +#include "../Cbot.h" // complet pour Browse + +class CTestCBotView; + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotApp: +// See TestCBot.cpp for the implementation of this class +// + +class CTestCBotApp : public CWinApp +{ +public: + CTestCBotApp(); + + CEdit* m_pConsole; + CTestCBotView* m_LastActive; + CBotClass* m_pClassPoint; + CBotClass* m_pClassPointIntr; + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTestCBotApp) + public: + virtual BOOL InitInstance(); + virtual int ExitInstance(); + //}}AFX_VIRTUAL + +// Implementation + + //{{AFX_MSG(CTestCBotApp) + afx_msg void OnAppAbout(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TESTCBOT_H__4D1BB903_8E74_11D4_A439_00D059085115__INCLUDED_) + + +#define WM_STARTPROG WM_APP + 0 +#define WM_ENDPROG WM_APP + 1 +#define WM_ACTWINDOW WM_APP + 2 diff --git a/src/CBot/TestCBot/TestCBot.rc b/src/CBot/TestCBot/TestCBot.rc new file mode 100644 index 00000000..137458cd --- /dev/null +++ b/src/CBot/TestCBot/TestCBot.rc @@ -0,0 +1,564 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "res\\TestCBot.ico" +IDR_TESTCBTYPE ICON DISCARDABLE "res\\TestCBotDoc.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON ID_FILE_NEW + BUTTON ID_FILE_OPEN + BUTTON ID_FILE_SAVE + SEPARATOR + BUTTON ID_EDIT_CUT + BUTTON ID_EDIT_COPY + BUTTON ID_EDIT_PASTE + SEPARATOR + BUTTON ID_FILE_PRINT + BUTTON ID_RUN + SEPARATOR + BUTTON ID_APP_ABOUT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINFRAME MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&Fichier" + BEGIN + MENUITEM "&Nouveau\tCtrl+N", ID_FILE_NEW + MENUITEM "&Ouvrir...\tCtrl+O", ID_FILE_OPEN + MENUITEM SEPARATOR + MENUITEM "Fichier récent", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "&Quitter", ID_APP_EXIT + END + POPUP "&Affichage" + BEGIN + MENUITEM "&Barre d'outils", ID_VIEW_TOOLBAR + MENUITEM "Barre d'é&tat", ID_VIEW_STATUS_BAR + END + POPUP "&?" + BEGIN + MENUITEM "&A propos de TestCBot...", ID_APP_ABOUT + END +END + +IDR_TESTCBTYPE MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&Fichier" + BEGIN + MENUITEM "&Nouveau\tCtrl+N", ID_FILE_NEW + MENUITEM "&Ouvrir...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Fermer", ID_FILE_CLOSE + MENUITEM "&Enregistrer\tCtrl+S", ID_FILE_SAVE + MENUITEM "En®istrer sous...", ID_FILE_SAVE_AS + MENUITEM SEPARATOR + MENUITEM "Fichier récent", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "&Quitter", ID_APP_EXIT + END + POPUP "&Edition" + BEGIN + MENUITEM "&Annuler\tCtrl+Z", ID_EDIT_UNDO + MENUITEM SEPARATOR + MENUITEM "&Couper\tCtrl+X", ID_EDIT_CUT + MENUITEM "&Copier\tCtrl+C", ID_EDIT_COPY + MENUITEM "C&oller\tCtrl+V", ID_EDIT_PASTE + END + POPUP "&Affichage" + BEGIN + MENUITEM "&Barre d'outils", ID_VIEW_TOOLBAR + MENUITEM "Barre d'é&tat", ID_VIEW_STATUS_BAR + END + POPUP "Fe&nêtre" + BEGIN + MENUITEM "&Nouvelle fenêtre", ID_WINDOW_NEW + MENUITEM "&Cascade", ID_WINDOW_CASCADE + MENUITEM "&Mosaïque", ID_WINDOW_TILE_HORZ + MENUITEM "&Réorganiser les icônes", ID_WINDOW_ARRANGE + END + POPUP "&?" + BEGIN + MENUITEM "&A propos de TestCBot...", ID_APP_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "C", ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + "N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + "V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT + VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT, NOINVERT + VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT + VK_F5, ID_RUN, VIRTKEY, NOINVERT + VK_F6, ID_NEXT_PANE, VIRTKEY, NOINVERT + VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT, NOINVERT + VK_F7, ID_RUN, VIRTKEY, NOINVERT + VK_F9, ID_TEST, VIRTKEY, NOINVERT + VK_INSERT, ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + VK_INSERT, ID_EDIT_PASTE, VIRTKEY, SHIFT, NOINVERT + "X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT + "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 265, 206 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "A propos de TestCBot" +FONT 8, "MS Sans Serif" +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,11,17,21,20 + LTEXT "TestCBot version 1.0",IDC_STATIC,40,10,119,8, + SS_NOPREFIX + LTEXT "Copyright D. Dumoulin (C) 2000",IDC_STATIC,40,25,119,8 + DEFPUSHBUTTON "OK",IDOK,226,7,32,14,WS_GROUP + LTEXT "Programme de test pour la librairie CBot\n\nLes fonctions doivent être déclarées comme ""extern"" pour apparaître dans la liste lors de l'exécution.\n\n", + IDC_STATIC,39,43,191,41 + LTEXT "Mais en fait, on peut accèder à toutes les fonctions marquées ""public"" quelles soient dans la fenêtre active ou non.", + IDC_STATIC,39,89,187,36 + LTEXT "Les fonctions print( ... ) et println( ...) permettent d'afficher des résultats dans la console.\n\nLa fonction show( ... ) affiche les paramètres dans un dialogue, et suspend donc l'exécution.", + IDC_STATIC,39,130,187,54 +END + +IDD_DIALOG2 DIALOG DISCARDABLE 0, 0, 186, 95 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Dialog" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,129,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,129,24,50,14 +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040C04B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "Application MFC TestCBot\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "TestCBot\0" + VALUE "LegalCopyright", "Copyright (C) 1900\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "TestCBot.EXE\0" + VALUE "ProductName", "Application TestCBot\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Traduction", 0x40c, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 258 + TOPMARGIN, 7 + BOTTOMMARGIN, 199 + END + + IDD_DIALOG2, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDR_MAINFRAME "TestCBot" + IDR_TESTCBTYPE "\nTestCBot\nTestCBot\nCBot (*.txt)\n.txt\nTestCBot.Document\nTestCB Document" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + AFX_IDS_APP_TITLE "TestCBot" + AFX_IDS_IDLEMESSAGE "Prêt" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_INDICATOR_EXT "EXT" + ID_INDICATOR_CAPS "MAJ" + ID_INDICATOR_NUM "NUM" + ID_INDICATOR_SCRL "DEF" + ID_INDICATOR_OVR "ECR" + ID_INDICATOR_REC "ENR" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_NEW "Crée un nouveau document\nNouveau" + ID_FILE_OPEN "Ouvre un document existant\nOuvrir" + ID_FILE_CLOSE "Ferme le document actif\nFermer" + ID_FILE_SAVE "Enregistre le document actif\nEnregistrer" + ID_FILE_SAVE_AS "Enregistre le document actif sous un nouveau nom\nEnregistrer sous" + ID_FILE_PRINT "Imprime le document\nImprime" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_APP_ABOUT "Affiche des informations sur le programme\nA propos de" + ID_APP_EXIT "Ferme l'application ; propose d'enregistrer les documents\nQuitter" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_MRU_FILE1 "Ouvre ce document" + ID_FILE_MRU_FILE2 "Ouvre ce document" + ID_FILE_MRU_FILE3 "Ouvre ce document" + ID_FILE_MRU_FILE4 "Ouvre ce document" + ID_FILE_MRU_FILE5 "Ouvre ce document" + ID_FILE_MRU_FILE6 "Ouvre ce document" + ID_FILE_MRU_FILE7 "Ouvre ce document" + ID_FILE_MRU_FILE8 "Ouvre ce document" + ID_FILE_MRU_FILE9 "Ouvre ce document" + ID_FILE_MRU_FILE10 "Ouvre ce document" + ID_FILE_MRU_FILE11 "Ouvre ce document" + ID_FILE_MRU_FILE12 "Ouvre ce document" + ID_FILE_MRU_FILE13 "Ouvre ce document" + ID_FILE_MRU_FILE14 "Ouvre ce document" + ID_FILE_MRU_FILE15 "Ouvre ce document" + ID_FILE_MRU_FILE16 "Ouvre ce document" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_NEXT_PANE "Passe au volet de fenêtre suivant\nVolet suivant" + ID_PREV_PANE "Revient au volet précédent\nVolet précédent" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_WINDOW_NEW "Ouvre une nouvelle fenêtre pour le document actif\nNouvelle fenêtre" + ID_WINDOW_ARRANGE "Réorganise les icônes en bas de la fenêtre\nRéorganise les icônes" + ID_WINDOW_CASCADE "Réorganise les fenêtres en cascade\nCascade" + ID_WINDOW_TILE_HORZ "Réorganise les fenêtres en une mosaïque\nMosaïque" + ID_WINDOW_TILE_VERT "Réorganise les fenêtres en une mosaïque\nMosaïque" + ID_WINDOW_SPLIT "Fractionne la fenêtre active en deux volets\nFractionner" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDIT_CLEAR "Efface la sélection\nEffacer" + ID_EDIT_CLEAR_ALL "Efface tout\nEffacer tout" + ID_EDIT_COPY "Copie la sélection et la place dans le Presse-papiers\nCopier" + ID_EDIT_CUT "Supprime la sélection et la place dans le Presse-papiers\nCopier" + ID_EDIT_FIND "Recherche le texte spécifié\nRechercher" + ID_EDIT_PASTE "Insère le contenu du Presse-papiers\nColler" + ID_EDIT_REPEAT "Répète la dernière action\nRépéter" + ID_EDIT_REPLACE "Remplace le texte spécifique par un texte différent\nRemplacer" + ID_EDIT_SELECT_ALL "Sélectionne le document entier\nSélectionner tout" + ID_EDIT_UNDO "Annule la dernière action\nAnnuler" + ID_EDIT_REDO "Rétablit l'action précédemment annulée\nRétablir" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_TOOLBAR "Affiche ou masque la barre d'outils\nBarre d'outils" + ID_VIEW_STATUS_BAR "Affiche ou masque la barre d'état\nBarre d'état" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCSIZE "Change la taille de la fenêtre" + AFX_IDS_SCMOVE "Change la position de la fenêtre" + AFX_IDS_SCMINIMIZE "Réduit la fenêtre en icône" + AFX_IDS_SCMAXIMIZE "Agrandit la fenêtre au format de l'écran" + AFX_IDS_SCNEXTWINDOW "Passe à la fenêtre de document suivante" + AFX_IDS_SCPREVWINDOW "Passe à la fenêtre de document précédente" + AFX_IDS_SCCLOSE "Ferme la fenêtre active et propose l'enregistrement des documents" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCRESTORE "Restaure la fenêtre à sa taille d'origine" + AFX_IDS_SCTASKLIST "Active la liste des tâches" + AFX_IDS_MDICHILD "Active cette fenêtre" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_RUN "Execute le programme CBot\nExecute (F5)" +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_TYPENAMES "les différents types" + 1001 "Byte" + 1002 "Short" + 1003 "Char" + 1004 "Int" + 1005 "Long" + 1006 "Real" + 1007 "Double" +END + +STRINGTABLE DISCARDABLE +BEGIN + 1008 "Boolean" + 1009 "String" + 1010 "Array" + 1011 "Arraybody" + 1012 "Pointer" + 1013 "Nullpointer" + 1014 "nop" + 1015 "Class" + 1016 "Intrinsic" +END + +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// French (Switzerland) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRS) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH_SWISS +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONSOLE DIALOG DISCARDABLE 0, 0, 401, 210 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "CBot Console" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Commande :",IDC_STATIC,7,177,40,8 + EDITTEXT IDC_EDIT2,7,189,329,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "Exécute",IDOK,344,189,50,14 + EDITTEXT IDC_EDIT1,7,7,387,167,ES_MULTILINE | ES_READONLY | + ES_WANTRETURN | WS_VSCROLL +END + +IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 177, 100 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Test performances" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Boucles par seconde",IDC_STATIC,7,9,68,8 + EDITTEXT IDC_EDIT1,111,7,51,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Nombre de scripts",IDC_STATIC,7,55,58,8 + EDITTEXT IDC_EDIT2,111,52,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN1,"msctls_updown32",UDS_ARROWKEYS,152,52, + 10,14 + COMBOBOX IDC_COMBO1,111,74,52,111,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Timer",IDC_STATIC,7,77,18,8 + LTEXT "Performance %",IDC_STATIC,7,28,48,8 + EDITTEXT IDC_EDIT3,111,25,51,14,ES_AUTOHSCROLL | ES_READONLY +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_CONSOLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 394 + TOPMARGIN, 7 + BOTTOMMARGIN, 203 + END + + IDD_DIALOG1, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 170 + TOPMARGIN, 7 + BOTTOMMARGIN, 93 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 12, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif\r\n" + "#include ""res\\TestCBot.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""l.fra\\afxres.rc"" // Standard components\r\n" + "#endif\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_DIALOG1 DLGINIT +BEGIN + IDC_COMBO1, 0x403, 2, 0 +0x0031, + IDC_COMBO1, 0x403, 3, 0 +0x3031, "\000" + IDC_COMBO1, 0x403, 4, 0 +0x3031, 0x0030, + IDC_COMBO1, 0x403, 5, 0 +0x3031, 0x3030, "\000" + 0 +END + +#endif // French (Switzerland) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE 12, 1 +#pragma code_page(1252) +#endif +#include "res\TestCBot.rc2" // non-Microsoft Visual C++ edited resources +#include "l.fra\afxres.rc" // Standard components +#endif +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/CBot/TestCBot/TestCBot1.txt b/src/CBot/TestCBot/TestCBot1.txt new file mode 100644 index 00000000..d27b4f8a --- /dev/null +++ b/src/CBot/TestCBot/TestCBot1.txt @@ -0,0 +1,27 @@ + +class CPoint2 +{ + float x, y; + void CPoint2(float x, float y) + { + this.x = x; + this.y = y; + } +} + +public extern void T ( ) +{ + CPoint2 X( 12, 33 ), Y ( -4, 4/3 ); + print ( X, Y ) ; +} + +public extern void Hello ( ) + +{ + println ( "Hello" ); +} + +public extern void test ( int n ) +{ + for ( int i = n; i>0 ; i--) print (i); +} \ No newline at end of file diff --git a/src/CBot/TestCBot/TestCBot3.txt b/src/CBot/TestCBot/TestCBot3.txt new file mode 100644 index 00000000..b915f967 --- /dev/null +++ b/src/CBot/TestCBot/TestCBot3.txt @@ -0,0 +1,24 @@ +public extern void Test () +{ + for ( int x = 100000; x>0 ; x-- ) { } +} + +float MaRoutine( CPoint A, CPoint B ) +{ + A.x -= B.x ; // distance en x + A.y -= B.y ; // distance en y + A.x *= A.x; // carré de la distance + A.y += A.y; // carré de la distance + println ( A, B ) ; + return ( A.x + A.y ) ; +} + +public extern void TestAB ( ) +{ + CPoint A(3, 5) ; + CPoint B(4, -2); + println ( A, B ) ; + MaRoutine( A, B ) ; + println ( A, B ) ; +} + diff --git a/src/CBot/TestCBot/TestCBotDoc.cpp b/src/CBot/TestCBot/TestCBotDoc.cpp new file mode 100644 index 00000000..e033dd50 --- /dev/null +++ b/src/CBot/TestCBot/TestCBotDoc.cpp @@ -0,0 +1,683 @@ +// TestCBotDoc.cpp : implementation of the CTestCBotDoc class +// + +#include "stdafx.h" +#include "TestCBot.h" + +#include "TestCBotDoc.h" +#include "TestCBotView.h" +#include "CBotConsoleDlg.h" +#include "PerformDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotDoc + +IMPLEMENT_DYNCREATE(CTestCBotDoc, CDocument) + +BEGIN_MESSAGE_MAP(CTestCBotDoc, CDocument) + //{{AFX_MSG_MAP(CTestCBotDoc) + ON_COMMAND(ID_RUN, OnRun) + ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit1) + ON_COMMAND(ID_TEST, OnTest) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotDoc construction/destruction + +static BOOL test = FALSE; + + +CTestCBotDoc::CTestCBotDoc() +{ + m_pEdit = NULL; + m_pProg = NULL; + m_bModified = FALSE; +} + +CTestCBotDoc::~CTestCBotDoc() +{ + delete m_pEdit; + delete m_pProg; +} + +BOOL CTestCBotDoc::OnNewDocument() +{ + if (!CDocument::OnNewDocument()) + return FALSE; + + return TRUE; +} + + + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotDoc serialization + +void CTestCBotDoc::Serialize(CArchive& ar) +{ + if (ar.IsStoring()) + { + m_pEdit->GetWindowText(m_DocText); + int w = m_DocText.GetLength(); + ar.Write((LPCTSTR)m_DocText, w); + } + else + { + int r; + char buf[10001]; + + r = ar.Read(buf, 10000); + buf[r] = 0; + m_DocText = buf; + + if ( m_pProg == NULL ) m_pProg = new CBotProgram(); + + if (!m_pProg->Compile(m_DocText, m_Liste, NULL)) + { + delete m_pProg; + m_pProg = NULL; + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotDoc diagnostics + +#ifdef _DEBUG +void CTestCBotDoc::AssertValid() const +{ + CDocument::AssertValid(); +} + +void CTestCBotDoc::Dump(CDumpContext& dc) const +{ + CDocument::Dump(dc); +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotDoc commands + +void CTestCBotDoc::OnRun() +{ + OnFileSave(); + + m_pEdit->GetWindowText(m_DocText); + + CString TextError; + int code, start, end; + + if ( m_pProg == NULL ) m_pProg = new CBotProgram(); + + CTestCBotApp* pApp = (CTestCBotApp*)AfxGetApp(); + + if (!m_pProg->Compile(m_DocText, m_Liste, NULL)) + { + m_pProg->GetError(code, start, end); + delete m_pProg; + m_pProg = NULL; + + m_pEdit->SetSel( start, end ); + m_pEdit->SetFocus(); // met en évidence la partie avec problème + + TextError = CBotProgram::GivErrorText( code ); + AfxMessageBox( TextError ); + + m_pEdit->SetFocus(); + return; + } + + if( m_Liste.GivSize() == 0 ) + { + AfxMessageBox("Aucune fonction marquée \"extern\" !"); + return; + } + + for ( int i = 0; i < m_Liste.GivSize(); i++ ) + { + int start, stop; + m_pProg->GetPosition(m_Liste[i], start, stop, GetPosNom, GetPosParam); + m_Liste[i] = m_DocText.Mid( start, stop-start ); + } + + CBotConsoleDlg dlg; + dlg.m_pListe = &m_Liste; + dlg.m_pEditx = m_pEdit; + + dlg.DoModal(); // dialogue pour faire la console + + if ( dlg.m_code>0 ) + { + CString TextError; + + TextError = m_pProg->GivErrorText( dlg.m_code ); + + m_pEdit->SetSel( dlg.m_start, dlg.m_end ); + m_pEdit->SetFocus(); // met en évidence la partie avec problème + + AfxMessageBox(TextError); + } + + m_pEdit->SetFocus(); + + return; +} + + +void CTestCBotDoc::OnChangeEdit1() +{ + SetModifiedFlag(); + m_bModified = TRUE; +} + +BOOL CTestCBotDoc::Compile() +{ + m_pEdit->GetWindowText(m_DocText); + + CString TextError; + int code, start, end; + + if ( m_pProg == NULL ) m_pProg = new CBotProgram(); + + char buffer[100]; + strcpy(buffer, "le pointeur à passer pour voir"); + + if (m_bModified && !m_pProg->Compile(m_DocText, m_Liste, (void*)buffer)) + { + m_pProg->GetError(code, start, end); + delete m_pProg; + m_pProg = NULL; + + m_pEdit->SetSel( start, end ); + m_pEdit->SetFocus(); // met en évidence la partie avec problème + + TextError = CBotProgram::GivErrorText( code ); + AfxMessageBox( TextError ); + + m_pEdit->SetFocus(); + m_bModified = FALSE; + return FALSE; + } + + if ( m_pProg->GetPosition( "TheTest", start, end) ) + { + m_pEdit->SetSel( start, end ); + m_pEdit->SetFocus(); // met en évidence la partie avec problème + } + + m_bModified = FALSE; + return TRUE; +} + + + +static int compt = 0; +// routine retournant le "pointeur" à un autre object +BOOL rRetObject( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + pResult->SetPointer( NULL ); + compt+=45671; + if (compt&0x11) return TRUE; + + CBotVar* pAutre = CBotVar::Create("autre", CBotTypResult( CBotTypClass, "object" )); + pAutre->SetUserPtr( (void*)2 ); + pResult->SetPointer( pAutre ); + + if (!pResult->IsElemOfClass("object")) + return TRUE; + + delete pAutre; + return TRUE; +} + +CBotTypResult cRetObject( CBotVar* &pVar, void* pUser ) +{ + return CBotTypResult( CBotTypPointer, "object"); +} + +BOOL roRadar( CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception ) +{ + pResult->SetPointer( NULL ); + compt+=45671; + if (compt&0x11) return TRUE; + + CBotVar* pAutre = CBotVar::Create("autre", CBotTypResult( CBotTypClass, "object" )); + pAutre->SetUserPtr( (void*)2 ); + pResult->SetPointer( pAutre ); + + if (!pResult->IsElemOfClass("object")) + return TRUE; + + delete pAutre; + return TRUE; +} + +CBotTypResult coRadar( CBotVar* pThis, CBotVar* &pVar ) +{ + void* pUser = pThis->GivUserPtr(); + return CBotTypResult( CBotTypPointer, "object"); +} + +BOOL rMove( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + if ( test < 12 ) + { + test++; + return FALSE; + } + return TRUE; +} + +CBotTypResult cMove( CBotVar* &pVar, void* pUser ) +{ + return CBotTypResult( 0 ); +} + +BOOL rTurn( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + return TRUE; +} + +CBotTypResult cTurn( CBotVar* &pVar, void* pUser ) +{ + return CBotTypResult( 0 ); +} + +BOOL rRadar( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + pResult->SetPointer( NULL ); + + if ( pVar ) pVar->debug(); + + compt+=45671; + if (compt&0x11) + { + return FALSE; // TRUE; + } + + CBotVar* pAutre = CBotVar::Create("autre", CBotTypResult( CBotTypClass, "object" )); + pAutre->SetUserPtr( (void*)2 ); + pResult->SetPointer( pAutre ); + + if (!pResult->IsElemOfClass("object")) + return TRUE; + + delete pAutre; + return TRUE; +} + +CBotTypResult cRadar( CBotVar* &pVar, void* pUser ) +{ + return CBotTypResult( CBotTypPointer, "object"); +} + +// routine retournant le "pointeur" à un autre object +BOOL rTEST( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + test = 1 ; + if ( pVar == NULL ) return TRUE; + + test = pVar->GivValInt(); + if ( test == 5 ) + { + pVar = pVar->GivNext(); + pVar->SetUserPtr( OBJECTDELETED ); + } + return TRUE; +} + +CBotTypResult cTEST( CBotVar* &pVar, void* pUser ) +{ + return CBotTypResult( 0 ); +} + +// routine retournant le "pointeur" à un autre object +BOOL rF( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) +{ + if ( pResult == NULL ) return TRUE; + pResult->SetValInt(3); + return TRUE; +} + +CBotTypResult cF( CBotVar* &pVar, void* pUser ) +{ + return CBotTypResult( CBotTypFloat ); +} + +///////////////////////////////////////////////////////////////// + +// Compilation d'une procédure avec un "point". + +CBotTypResult cPoint(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult( CBotErrLowParam ); + + if ( var->GivType() <= CBotTypDouble ) + { + var = var->GivNext(); + if ( var == 0 ) return CBotTypResult( CBotErrLowParam ); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult( CBotErrBadNum ); + var = var->GivNext(); + if ( var == 0 ) return CBotTypResult( CBotErrLowParam ); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult( CBotErrBadNum ); + var = var->GivNext(); + return CBotTypResult( 0 ); + } + + if ( var->GivType() == CBotTypClass ) + { + if ( !var->IsElemOfClass("point") ) return CBotTypResult( CBotErrBadParam ); + var = var->GivNext(); + return CBotTypResult( 0 ); + } + + return CBotTypResult( CBotErrBadParam ); +} + +// Donne un paramètre de type "point". +#define UNIT 1 + + +CBotTypResult cSpace(CBotVar* &var, void* user) +{ + CBotTypResult ret; + + if ( var == 0 ) return CBotTypResult( CBotTypIntrinsic, "point" ); + ret = cPoint(var, user); + if ( !ret.Eq(0) ) return ret; + + if ( var == 0 ) return CBotTypIntrinsic; + if ( var->GivType() > CBotTypDouble ) return CBotTypResult( CBotErrBadNum ); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypIntrinsic; + if ( var->GivType() > CBotTypDouble ) return CBotTypResult( CBotErrBadNum ); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypIntrinsic; + if ( var->GivType() > CBotTypDouble ) return CBotTypResult( CBotErrBadNum ); + var = var->GivNext(); + + if ( var != 0 ) return CBotErrOverParam; + return CBotTypResult( CBotTypIntrinsic, "point" ); +} + +// Instruction "space(center, rMin, rMax, dist)". + +BOOL rSpace(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CBotVar* pSub; + float rMin, rMax, dist; + + rMin = 5.0f*UNIT; + rMax = 50.0f*UNIT; + dist = 4.0f*UNIT; + + if ( var == 0 ) + { +// center = pThis->RetPosition(0); + } + else + { + if ( var != 0 ) + { + rMin = var->GivValFloat()*UNIT; + var = var->GivNext(); + + if ( var != 0 ) + { + rMax = var->GivValFloat()*UNIT; + var = var->GivNext(); + + if ( var != 0 ) + { + dist = var->GivValFloat()*UNIT; + var = var->GivNext(); + } + } + } + } + + if ( result != 0 ) + { + pSub = result->GivItemList(); + if ( pSub != 0 ) + { + pSub->SetValFloat(1); + pSub = pSub->GivNext(); // "y" + pSub->SetValFloat(2); + pSub = pSub->GivNext(); // "z" +// pSub->SetValFloat(3); + } + } + return TRUE; +} +////////////////////////////////////////////////////////////// + + +void CTestCBotDoc::OnTest() +{ + CBotProgram::DefineNum("WingedGrabber", 1); + CBotProgram::DefineNum("TrackedGrabber", 2); + CBotProgram::DefineNum("WheeledGrabber", 3); + CBotProgram::DefineNum("LeggedGrabber", 4); + CBotProgram::DefineNum("WingedShooter", 5); + CBotProgram::DefineNum("TrackedShooter", 6); + CBotProgram::DefineNum("WheeledShooter", 7); + CBotProgram::DefineNum("LeggedShooter", 8); + CBotProgram::DefineNum("WingedOrgaShooter", 9); + CBotProgram::DefineNum("TrackedOrgaShooter", 10); + CBotProgram::DefineNum("WheeledOrgaShooter", 11); + CBotProgram::DefineNum("LeggedOrgaShooter", 12); + CBotProgram::DefineNum("WingedSniffer", 13); + CBotProgram::DefineNum("TrackedSniffer", 14); + CBotProgram::DefineNum("WheeledSniffer", 14); + CBotProgram::DefineNum("LeggedSniffer", 15); + CBotProgram::DefineNum("Thumper", 16); + CBotProgram::DefineNum("PhazerShooter", 17); + CBotProgram::DefineNum("Recycler", 18); + CBotProgram::DefineNum("Shielder", 19); + CBotProgram::DefineNum("Subber", 20); + CBotProgram::DefineNum("Me", 21); + + CBotProgram::DefineNum("TypeMarkPath", 111); + + OnFileSave(); + +// CPerformDlg dlg; +// dlg.m_Script = m_DocText; +// dlg.DoModal(); + + // défini la routine RetObject + CBotProgram::AddFunction( "Radar", rRetObject, cRetObject ); + + // ajoute une routine pour cette classe + CBotProgram::AddFunction("Space", rSpace, cSpace); + + // défini la routine Test + CBotProgram::AddFunction( "TEST", rTEST, cTEST ); + CBotProgram::AddFunction( "F", rF, cF ); + + CBotProgram::AddFunction( "goto", rMove, cMove ); + CBotProgram::AddFunction( "fire", rTurn, cTurn ); + CBotProgram::AddFunction( "radar", rRadar, cRadar ); + + // crée une instance de la classe "Bot" pour ce robot + CBotVar* pThisRobot = CBotVar::Create( "", CBotTypResult(CBotTypClass, "object") ); + pThisRobot->SetUserPtr( (void*)1 ); + pThisRobot->SetIdent( 1234 ); + + delete m_pProg; + // crée un objet programme associé à cette instance + m_pProg = new CBotProgram(pThisRobot); + + // compile le programme + CString TextError; + int code, start, end; + + m_pEdit->GetWindowText(m_DocText); + if (!m_pProg->Compile(m_DocText, m_Liste, (void*) 44)) + { + m_pProg->GetError(code, start, end); + delete m_pProg; + m_pProg = NULL; + + delete pThisRobot; + + m_pEdit->SetSel( start, end ); + m_pEdit->SetFocus(); // met en évidence la partie avec problème + + TextError = CBotProgram::GivErrorText( code ); + AfxMessageBox( TextError ); + + m_pEdit->SetFocus(); + return; + } + + // exécute pour voir + m_pProg->Start(m_Liste[0]); + + int mode = -1; + + if ( mode >= 0 ) { + + // sauve et restore à chaque pas possible + while (!m_pProg->Run(NULL, 1)) + { + const char* FunctionName; + int start1, end1; + m_pProg->GetRunPos(FunctionName, start1, end1); + if ( end1 <= 0 ) + m_pProg->GetRunPos(FunctionName, start1, end1); + m_pEdit->SetSel(start1, end1); + +if ( mode == 0 ) continue; + + FILE* pf; + pf = fOpen( "TEST.CBO", "wb" ); + CBotClass::SaveStaticState(pf); + m_pProg->SaveState(pf); + fClose(pf); + +if ( mode == 2 ) if (!m_pProg->Compile(m_DocText, m_Liste, (void*) 44)) + { + m_pProg->GetError(code, start, end); + delete m_pProg; + m_pProg = NULL; + + delete pThisRobot; + + m_pEdit->SetSel( start, end ); + m_pEdit->SetFocus(); // met en évidence la partie avec problème + + TextError = CBotProgram::GivErrorText( code ); + AfxMessageBox( TextError ); + + m_pEdit->SetFocus(); + return; + } + + pf = fOpen( "TEST.CBO", "rb" ); + CBotClass::RestoreStaticState(pf); + m_pProg->RestoreState(pf); + fClose(pf); + + int start2, end2; + m_pProg->GetRunPos(FunctionName, start2, end2); + if ( end2 <= 0 ) + m_pProg->GetRunPos(FunctionName, start2, end2); + + if ( start1 != start2 || end1 != end2 ) + m_pProg->GetRunPos(FunctionName, start2, end2); + m_pEdit->SetSel(start2, end2); + } + + if (m_pProg->GetError(code, start, end)) + { + m_pEdit->SetSel(start, end); + TextError = CBotProgram::GivErrorText(code); + AfxMessageBox(TextError); + } + return;} + + while (!m_pProg->Run(NULL, 0)) + { + const char* FunctionName; + int start, end; + m_pProg->GetRunPos(FunctionName, start, end); + m_pEdit->SetSel(start, end); + + if ( FunctionName == NULL ) continue; + CString info (FunctionName); + CString sep (":\n"); + + int level = 0; + const char* Name; + while ( TRUE ) + { + CBotVar* pVar = m_pProg->GivStackVars(Name, level--); + if ( Name != FunctionName ) break; + if ( pVar == NULL ) continue; +// pVar->Maj(NULL, FALSE); + while ( pVar != NULL ) + { + info += sep; + info += pVar->GivName() + " = " + pVar->GivValString(); + sep = ", "; + pVar = pVar->GivNext(); + } + sep = "\n"; + } + if ( IDOK != AfxMessageBox(info, MB_OKCANCEL) ) break; + + if ( test == 1 ) + { + test = 0; + FILE* pf; + pf = fOpen( "TEST.CBO", "wb" ); + m_pProg->SaveState(pf); + fClose(pf); + } + + if ( test == 2 ) + { + test = 0; + FILE* pf; + pf = fOpen( "TEST.CBO", "rb" ); + m_pProg->RestoreState(pf); + fClose(pf); + } + + if ( test == 12 ) + { + test = 0; + FILE* pf; + pf = fOpen( "TEST.CBO", "wb" ); + m_pProg->SaveState(pf); + fClose(pf); + + pf = fOpen( "TEST.CBO", "rb" ); + m_pProg->RestoreState(pf); + fClose(pf); + + test = 13; + } + } + + if (m_pProg->GetError(code, start, end)) + { + m_pEdit->SetSel(start, end); + TextError = CBotProgram::GivErrorText(code); + AfxMessageBox(TextError); + } + + delete pThisRobot; +} + diff --git a/src/CBot/TestCBot/TestCBotDoc.h b/src/CBot/TestCBot/TestCBotDoc.h new file mode 100644 index 00000000..d8275b52 --- /dev/null +++ b/src/CBot/TestCBot/TestCBotDoc.h @@ -0,0 +1,64 @@ +// TestCBotDoc.h : interface of the CTestCBotDoc class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_TESTCBOTDOC_H__4D1BB90B_8E74_11D4_A439_00D059085115__INCLUDED_) +#define AFX_TESTCBOTDOC_H__4D1BB90B_8E74_11D4_A439_00D059085115__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + + +class CTestCBotDoc : public CDocument +{ +protected: // create from serialization only + CTestCBotDoc(); + DECLARE_DYNCREATE(CTestCBotDoc) + +// Attributes +public: + CEdit* m_pEdit; // pour mémoriser le texte, et l'afficher + CBotProgram* m_pProg; // le programme compilé + CString m_DocText; + CBotStringArray m_Liste; + BOOL m_bModified; + +// Operations +public: + BOOL Compile(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTestCBotDoc) + public: + virtual BOOL OnNewDocument(); + virtual void Serialize(CArchive& ar); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CTestCBotDoc(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + +// Generated message map functions +protected: + //{{AFX_MSG(CTestCBotDoc) + afx_msg void OnRun(); + afx_msg void OnChangeEdit1(); + afx_msg void OnTest(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TESTCBOTDOC_H__4D1BB90B_8E74_11D4_A439_00D059085115__INCLUDED_) diff --git a/src/CBot/TestCBot/TestCBotView.cpp b/src/CBot/TestCBot/TestCBotView.cpp new file mode 100644 index 00000000..052e7563 --- /dev/null +++ b/src/CBot/TestCBot/TestCBotView.cpp @@ -0,0 +1,126 @@ +// TestCBotView.cpp : implementation of the CTestCBotView class +// + +#include "stdafx.h" +#include "TestCBot.h" + +#include "TestCBotDoc.h" +#include "TestCBotView.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotView + +IMPLEMENT_DYNCREATE(CTestCBotView, CView) + +BEGIN_MESSAGE_MAP(CTestCBotView, CView) + //{{AFX_MSG_MAP(CTestCBotView) + ON_WM_SIZE() + ON_MESSAGE(WM_ACTWINDOW, ActWindow) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotView construction/destruction + +CTestCBotView::CTestCBotView() +{ +} + +CTestCBotView::~CTestCBotView() +{ +} + +BOOL CTestCBotView::PreCreateWindow(CREATESTRUCT& cs) +{ + return CView::PreCreateWindow(cs); +} + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotView drawing + +void CTestCBotView::OnDraw(CDC* pDC) +{ + CTestCBotDoc* pDoc = GetDocument(); + ASSERT_VALID(pDoc); +} + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotView diagnostics + +#ifdef _DEBUG +void CTestCBotView::AssertValid() const +{ + CView::AssertValid(); +} + +void CTestCBotView::Dump(CDumpContext& dc) const +{ + CView::Dump(dc); +} + +CTestCBotDoc* CTestCBotView::GetDocument() // non-debug version is inline +{ + ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTestCBotDoc))); + return (CTestCBotDoc*)m_pDocument; +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CTestCBotView message handlers + +void CTestCBotView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView) +{ + CTestCBotDoc* pDoc = GetDocument(); +// CTestCBotApp* pApp = (CTestCBotApp*)AfxGetApp(); + + if ( pDoc->m_pEdit == NULL) + { + pDoc->m_pEdit = new CEdit(); + CRect rect; + GetClientRect( rect ); + + pDoc->m_pEdit->Create( WS_VISIBLE|WS_BORDER|WS_TABSTOP|ES_MULTILINE|ES_WANTRETURN|ES_NOHIDESEL|ES_AUTOVSCROLL, + rect, this, IDC_EDIT1 ); + pDoc->m_pEdit->SetTabStops(12); + pDoc->m_pEdit->SetWindowText(pDoc->m_DocText); + } + + if ( !bActivate && !pDoc->Compile() ) + { +// comment faire pour réactiver l'ancien document + } + + CView::OnActivateView(bActivate, pActivateView, pDeactiveView); + + if ( bActivate ) pDoc->m_pEdit->SetFocus(); +} + + +void CTestCBotView::OnSize(UINT nType, int cx, int cy) +{ + CView::OnSize(nType, cx, cy); + + CTestCBotDoc* pDoc = GetDocument(); + if ( pDoc->m_pEdit != NULL ) + { + CRect rect; + GetClientRect( rect ); + pDoc->m_pEdit->MoveWindow( rect ); + pDoc->m_pEdit->SetFocus(); + } +} + + + +LONG CTestCBotView::ActWindow(UINT wparam, LONG lparam) +{ +// GetParentFrame()->SetActiveView( this, TRUE ); +// CMDIChildWnd::OnMDIActivate(1, this, this) + return 0; +} diff --git a/src/CBot/TestCBot/TestCBotView.h b/src/CBot/TestCBot/TestCBotView.h new file mode 100644 index 00000000..bca156f8 --- /dev/null +++ b/src/CBot/TestCBot/TestCBotView.h @@ -0,0 +1,64 @@ +// TestCBotView.h : interface of the CTestCBotView class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_TESTCBOTVIEW_H__4D1BB90D_8E74_11D4_A439_00D059085115__INCLUDED_) +#define AFX_TESTCBOTVIEW_H__4D1BB90D_8E74_11D4_A439_00D059085115__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +class CTestCBotView : public CView +{ +protected: // create from serialization only + CTestCBotView(); + DECLARE_DYNCREATE(CTestCBotView) + +// Attributes +public: + CTestCBotDoc* GetDocument(); + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTestCBotView) + public: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + protected: + virtual void OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CTestCBotView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + +// Generated message map functions +protected: + //{{AFX_MSG(CTestCBotView) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg LONG ActWindow(UINT wparam, LONG lparam) ; + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#ifndef _DEBUG // debug version in TestCBotView.cpp +inline CTestCBotDoc* CTestCBotView::GetDocument() + { return (CTestCBotDoc*)m_pDocument; } +#endif + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TESTCBOTVIEW_H__4D1BB90D_8E74_11D4_A439_00D059085115__INCLUDED_) diff --git a/src/CBot/TestCBot/TestNull.txt b/src/CBot/TestCBot/TestNull.txt new file mode 100644 index 00000000..f447245f --- /dev/null +++ b/src/CBot/TestCBot/TestNull.txt @@ -0,0 +1,15 @@ +extern public void TestNull () +{ + CPoint pointeur = null; + + try { + pointeur.x = 4; } + catch ( 6007 ) {} + + pointeur = new CPoint(1,2); + + print ( pointeur.x, pointeur.y, + pointeur ); + + pointeur.x = 5; +} \ No newline at end of file diff --git a/src/CBot/TestCBot/TestRestoreState.txt b/src/CBot/TestCBot/TestRestoreState.txt new file mode 100644 index 00000000..1e49e378 --- /dev/null +++ b/src/CBot/TestCBot/TestRestoreState.txt @@ -0,0 +1,67 @@ +// routine de Daniel qui plante après RestoreState + +extern void object::Attack( ) +{ + int list[], i; + object p; + float dist, prox; + point nav1, nav2, dest; + boolean advance = true; + + i = 0; + list[i++] = WingedGrabber; + list[i++] = TrackedGrabber; + list[i++] = WheeledGrabber; + list[i++] = LeggedGrabber; + list[i++] = WingedShooter; + list[i++] = TrackedShooter; + list[i++] = WheeledShooter; + list[i++] = LeggedShooter; + list[i++] = WingedOrgaShooter; + list[i++] = TrackedOrgaShooter; + list[i++] = WheeledOrgaShooter; + list[i++] = LeggedOrgaShooter; + list[i++] = WingedSniffer; + list[i++] = TrackedSniffer; + list[i++] = WheeledSniffer; + list[i++] = LeggedSniffer; + list[i++] = Thumper; + list[i++] = PhazerShooter; + list[i++] = Recycler; + list[i++] = Shielder; + list[i++] = Subber; + list[i++] = Me; + + nav1.x = 1;//cmdline(0); + nav1.y = 1;//cmdline(1); + nav2.x = 2;//cmdline(2); + nav2.y = 2;//cmdline(3); + + while ( true ) + { + while ( true ) + { + // ennemi à proximité ? + p = radar(list, 0, 360, 0, 40); + if ( p == null ) break; + // lui tire dessus + fire(p.position); + } + + // se promène vers le point A + goto(nav1); + + while ( true ) + { + // ennemi à proximité ? + p = radar(list, 0, 360, 0, 40); + if ( p == null ) break; + // lui tire dessus + fire(p.position); + } + + // se promène vers le point B + goto(nav2); + } +} + diff --git a/src/CBot/TestCBot/TestStatic.txt b/src/CBot/TestCBot/TestStatic.txt new file mode 100644 index 00000000..f501aa55 --- /dev/null +++ b/src/CBot/TestCBot/TestStatic.txt @@ -0,0 +1,31 @@ +class ESSAI +{ + int x = 0; + static int nb = 3; + static int [ ] array ; + + void Put( int val) + { +show(nb); + array[ nb ] = val; +// this.nb++; + this.nb = this.nb + 1; +show(nb, array); + } + int Get( ) + { + nb--; +show("out", nb, array); + return array[ nb ] ; + } +} + +extern public void T() +{ + ESSAI t1 ( ) ; + ESSAI t2 ( ) ; + t1.nb++; + t1.Put( 11 ); t1.Put( 12 ); t2.Put( 13 ); + + show ( t1.Get(), t2.Get(), t2.Get() ) ; +} \ No newline at end of file diff --git a/src/CBot/TestCBot/TestStr.txt b/src/CBot/TestCBot/TestStr.txt new file mode 100644 index 00000000..683ec1bd --- /dev/null +++ b/src/CBot/TestCBot/TestStr.txt @@ -0,0 +1,17 @@ +extern public void TSTR() +{ + string s = "C'est un essai"; + + print ( s, strlen(s), strleft(s, 3), strright(s,3), strmid(s, 2), strmid(s,2,3), strfind(s, "un"), strfind(s, "sdgfld") ); + + show ( strupper(s), strlower(s) ); + + s = "123.45" ; + print ( strval(s) ); + + + string sub = strright("abcdef", 2); // sub vaut "ef###", # étant un caractère bizarre quelconque + show (sub); + int pos = strfind("abcdef", "xy"); // pos vaut -1. Pourquoi pas nan ? + show(pos); +} diff --git a/src/CBot/TestCBot/Z.txt b/src/CBot/TestCBot/Z.txt new file mode 100644 index 00000000..714119b9 --- /dev/null +++ b/src/CBot/TestCBot/Z.txt @@ -0,0 +1,14 @@ +public extern void tp() +{ + int a [4], b[]; + a [ 0 ] = 8 ; + + b = T ( a ) ; + show ( a, b ); +} + +int[] T ( int[] Z ) +{ + for ( int i = 0; i < 4 ; i++ ) Z[ i ] = i * i ; + return Z; +} \ No newline at end of file diff --git a/src/CBot/TestCBot/array.txt b/src/CBot/TestCBot/array.txt new file mode 100644 index 00000000..081b60ef --- /dev/null +++ b/src/CBot/TestCBot/array.txt @@ -0,0 +1,24 @@ + +public extern void TestTableau () +{ + int tableau [ 12 ] ; + + point array[ 12 ] [ 14 ] ; + + point zéro ( 1, 2 ) ; + point a = zéro ; + + for ( int i = 0 ; i < 10 ; i++ ) array[ i ] [ i ]= zéro ; + + array[ 5 ] [3 ] . x =1.5 ; + + array[ 2 ] [ 2 ] . y = array[ 5 ] [ 5 ] . x ; + + array[ 4 ] = array [ 2 ] ; + + for ( int i = 0 ; i < 10 ; i++ ) for ( int j = 0 ; j < 4 ; j++ ) println ( i, j, array [ i ] [ j ] ) ; + + show( zéro, a, array ); + +} + diff --git a/src/CBot/TestCBot/a§1.txt b/src/CBot/TestCBot/a§1.txt new file mode 100644 index 00000000..0c579508 --- /dev/null +++ b/src/CBot/TestCBot/a§1.txt @@ -0,0 +1,96 @@ +object radarGuepe(point orig, float dist) +{ + int i; + object pr, r; + float mindist; + + i = 0; + mindist = 1000; + while (i<30) + { + pr = radar(i); + if (pr != null) + { + + if (F(orig, pr.position) < mindist and pr.category == AlienWasp and pr.altitude > 3) + { + mindist = distance(orig, pr.position); + r = pr; + } + } + i = i+1; + } + if (mindist < dist) return(r); else return(null); +} + + +class Guepe +{ + + point pos; + + + void cherche(point orig, float dist) + { + object p; + point o; + + p = radarGuepe(orig, dist); + while (p == null) + { + wait(0.1); + p = radarGuepe(orig, dist); + } + + pos.x = p.position.x; + pos.y = p.position.y; + pos.z = p.position.z; + + //o = p.position; + //wait(0.1); + + //vitessex = (p.position.x - o.x)/0.1; + //vitessey = (p.position.y - o.y)/0.1; + //vitessez = (p.position.z - o.z)/0.1; + + } + + + void tire(point orig, float orient) + { + //float t = 3; //temps d'anticipation + float angle; + point cible; + + cible.x = pos.x;// + t*vitessex; + cible.y = pos.y;// + t*vitessey; + cible.z = pos.z;// + t*vitessez; + + if (cible.x == 0) angle = 90; else + angle = atan(cible.y / cible.x); + if (cible.x < 0) angle = angle + 180; + angle = angle - orient; + if (angle > 180) angle = angle - 360; + if (angle < -180) angle = angle + 360; + turn(angle); + + angle = atan((cible.z-orig.z) / distance2d(orig, cible)); + aim(angle); + + fire(0.1); + + } +} + +extern void object::Fourmi6() +{ + //fps(1000); + Guepe guepe = new Guepe(); + + while (true) + { + guepe.cherche(position, 50); + + guepe.tire(position, orientation); + } +} diff --git a/src/CBot/TestCBot/bug.txt b/src/CBot/TestCBot/bug.txt new file mode 100644 index 00000000..4ec6eb38 --- /dev/null +++ b/src/CBot/TestCBot/bug.txt @@ -0,0 +1,12 @@ +public extern void object::Bug() +{ + point a; + a = position; + TEST(); + float d=dist(a, position); +} + +float dist(point a, point b) +{ + return a.x-b.x; +} diff --git a/src/CBot/TestCBot/bugmw.txt b/src/CBot/TestCBot/bugmw.txt new file mode 100644 index 00000000..284ee43f --- /dev/null +++ b/src/CBot/TestCBot/bugmw.txt @@ -0,0 +1,9 @@ +extern public void main() +{ + show(fact(30)) ; +} + +public int fact(int n) +{ + return (fact(n-1)*n) ; +} diff --git a/src/CBot/TestCBot/ccc.txt b/src/CBot/TestCBot/ccc.txt new file mode 100644 index 00000000..dbcd1d5d --- /dev/null +++ b/src/CBot/TestCBot/ccc.txt @@ -0,0 +1,8 @@ +public extern void ccc() +{ + int a; + a = 0 ; + + if ( a == 0 ); + +} \ No newline at end of file diff --git a/src/CBot/TestCBot/enum.txt b/src/CBot/TestCBot/enum.txt new file mode 100644 index 00000000..a592a7fb --- /dev/null +++ b/src/CBot/TestCBot/enum.txt @@ -0,0 +1,9 @@ + +enum JourDeLaSemaine { + lundi = 1, + mardi, + mercredi, + jeudi, + vendredi, + samedi, + dimanche = 0 } \ No newline at end of file diff --git a/src/CBot/TestCBot/fibo.txt b/src/CBot/TestCBot/fibo.txt new file mode 100644 index 00000000..88f53579 --- /dev/null +++ b/src/CBot/TestCBot/fibo.txt @@ -0,0 +1,25 @@ + +extern public int Fibo( int n, boolean b ) +{ + if ( n < 2 ) return n; + int a = Fibo(n-1, b) + Fibo(n-2, false); + if ( b ) print (n + "=" + a); + return a; +} + +extern public void t() +{ + Fibo( 23, true); +} + +extern public void tt() +{ + t(); +} + +// cette routine n'est évidemment pas du tout obtimisée +// c'est même un très mauvais exemple de programmation récursive + +// pour un test de durée, Fibo(23, true) prend +// en mode Debug 67 secondes +// en mode Release 8 secondes diff --git a/src/CBot/TestCBot/file.txt b/src/CBot/TestCBot/file.txt new file mode 100644 index 00000000..2a22dd96 --- /dev/null +++ b/src/CBot/TestCBot/file.txt @@ -0,0 +1,70 @@ +class CLASS22 +{ + static int nb = 2; + void T22 ( ) { nb = nb / 0 ; } +} + +public extern void object :: TEST() +{ + switch ( 1 ) + { + case 1: + { + file h(); + h.open("Mon Fichier.txt", "r"); +show ( h.filename, h.handle ); +h.filename = "xx"; +h.handle = 1 ; + h.readln(); + h.close(); + } + case 2: + { + file h("Mon Fichier.txt"); + h.open("r"); + h.readln(); + h.close(); + } + case 3: + { + file h("Mon Fichier.txt", "r"); + h.readln(); + h.close(); + } + case 4: + { + file h(); + h.filename = "Mon Fichier.txt"; + h.open("r"); + h.readln(); + h.close(); + } + case 5: + { + file h = fileopen( "Mon 2Fichier.txt", "r" ); + h.readln(); + h.close(); + } + } +{ + file h( ) ; + h.filename = "Test.h"; + h.open ( "r" ); + + + file pf ( "Mon Fichier.txt" ) ; + pf . open ( "w" ) ; + pf . writeln ( "Voici encore du texte" ) ; + pf . writeln ( "et une seconde ligne" ) ; + pf . close( ); + + pf . open ( "r" ) ; + + while ( not pf . eof( ) ) + { + string s = pf . readln ( ); + show ( s ); + } + pf.close( ); +} +} diff --git a/src/CBot/TestCBot/h.txt b/src/CBot/TestCBot/h.txt new file mode 100644 index 00000000..c3953199 --- /dev/null +++ b/src/CBot/TestCBot/h.txt @@ -0,0 +1,5 @@ +void tf() +{ + file h; + h.handle += 1 ; +} \ No newline at end of file diff --git a/src/CBot/TestCBot/include.txt b/src/CBot/TestCBot/include.txt new file mode 100644 index 00000000..e8f8cc97 --- /dev/null +++ b/src/CBot/TestCBot/include.txt @@ -0,0 +1,27 @@ +class Z +{ + static int x = 0; + private int y; + + void T( ) + { + // autorisé ici + y = x ; + this.y = this.x ; + x = y ; + this.x = this.y ; + } +} + +extern public void test() +{ + Z a(); + 3 * a.x; // autorisé +//vu 3 * a.y; // interdit +//vu a.y = 3; // interdit ici + a.x = 1; // autorisé + + show ( a ); + a.T(); + show ( a ); +} diff --git a/src/CBot/TestCBot/intrinsic.txt b/src/CBot/TestCBot/intrinsic.txt new file mode 100644 index 00000000..f2157914 --- /dev/null +++ b/src/CBot/TestCBot/intrinsic.txt @@ -0,0 +1,16 @@ +public extern void TestIntrinsic() +{ + point a ( 1, 2 ); + print (a); + + a.x = 3; + a.y = 4; + + point b = a; + + println ( b.x, b.y, b ) ; + if ( b == a ) b.y = 0; + println (a,b); + if ( b != a ) b.y = a.y; + println(a,b); +} \ No newline at end of file diff --git a/src/CBot/TestCBot/methode1.txt b/src/CBot/TestCBot/methode1.txt new file mode 100644 index 00000000..080bba29 --- /dev/null +++ b/src/CBot/TestCBot/methode1.txt @@ -0,0 +1,57 @@ +class t { + point p; +} + +void object :: toto() +{ + show ( Position ) ; +} + +extern public void object :: XX() +{ + int test []; + test [ 9999 ] = 3; + + toto () ; +/* + Radar(); + + object test ; + test = this. Radar(); + + do { + test = this.Radar(); + } while ( test == null ); + +/* + t test [ 4 ]; + for ( int i = 0 ; i < 4 ; i++ ) test [ i ] = new t(); + test [ 3 ] .p.x = 2; + show ( test ); +/* + int a = nan; + show ( a ) ; + + a = TypeMarkPath; + show ( a, a++, --a ) ; + + if ( a != nan ) a += 1 ; + + a = TypeMarkPath; + float q = a ; + show ( a, q ) ; + +return; + + a += ++a; + show ( a ) ; + + boolean i = false; + + if ( i == true ) {} + + object p; + if ( p == null) { p = p ; } +*/ +} + diff --git a/src/CBot/TestCBot/methode2.txt b/src/CBot/TestCBot/methode2.txt new file mode 100644 index 00000000..76ce7f48 --- /dev/null +++ b/src/CBot/TestCBot/methode2.txt @@ -0,0 +1,50 @@ + +extern void Toto() +{ + TEST(12); + + for ( int i = 0 ; i<1000; i++) + { + int j = 1; + if (i==55) TEST(12); + } + + TEST(2); + + +// Nouveau(); + int toto[4]; + point Z[3]; + + Z[1].x = 11; Z[1].y = 12; + + toto[2] = 12; + toto[1] = nan; + +// point test, autre(2,3) ; +// object titi = Radar(); + + TEST ( 1 ) ; + + toto[0] = 11; + + TEST ( 2 ) ; + + toto[6] = 0; +} + +extern void object::Nouveau() +{ + point a; + a = np(Position); +} + +point np(point b) +{ + point c; + c.x = b.y; + c.y = b.x; + return c ; +} + + diff --git a/src/CBot/TestCBot/mp1.txt b/src/CBot/TestCBot/mp1.txt new file mode 100644 index 00000000..599cfc46 --- /dev/null +++ b/src/CBot/TestCBot/mp1.txt @@ -0,0 +1,25 @@ +class Guepet +{ + + float a; + float b; + + void init() + { + a = 12.34; + b = 56.78; + } + + +} + +extern void object::Fourmi6() +{ + Guepet guepe =new Guepet(); + + guepe.init(); + + + show("test "+guepe.a+" "+guepe.b); + +} diff --git a/src/CBot/TestCBot/mp2.txt b/src/CBot/TestCBot/mp2.txt new file mode 100644 index 00000000..1c2972c9 --- /dev/null +++ b/src/CBot/TestCBot/mp2.txt @@ -0,0 +1,28 @@ +class Guepet +{ + + float a; + float b; + + void init() + { + a = 12.34; + b = 56.78; + + object x = radar(123); + show("radar "+x.position.x); + show("C'est fait"); + } + + +} + +extern void object::Fourmi6() +{ + Guepet guepe=new Guepet(); + + guepe.init(); + + show("test "+guepe.a+" "+guepe.b); + +} diff --git a/src/CBot/TestCBot/mw.txt b/src/CBot/TestCBot/mw.txt new file mode 100644 index 00000000..c2376707 --- /dev/null +++ b/src/CBot/TestCBot/mw.txt @@ -0,0 +1,16 @@ +extern public void main() +{ +// goto( 3, 4 ); + + while( true ) + { + try { goto (12) ; } + catch( FF( ) ) + { show( "ko"); } + } +} + +boolean FF() +{ + return false; +} diff --git a/src/CBot/TestCBot/null.txt b/src/CBot/TestCBot/null.txt new file mode 100644 index 00000000..ae76b742 --- /dev/null +++ b/src/CBot/TestCBot/null.txt @@ -0,0 +1,5 @@ +extern public void xxx () +{ + CPoint test = null ; + if ( test == null ) show ( "NULL" ); +} \ No newline at end of file diff --git a/src/CBot/TestCBot/opnew.txt b/src/CBot/TestCBot/opnew.txt new file mode 100644 index 00000000..7d6838c2 --- /dev/null +++ b/src/CBot/TestCBot/opnew.txt @@ -0,0 +1,20 @@ +extern public void xx () +{ + CPoint pointeur, test = null ; + pointeur = new CPoint ( 3, 4 ); + + if ( test == null ) show ( "NULL" ); + + CPoint pp = pointeur; + +show( pointeur , pp ); + + pp.x = 33.3; + if ( pointeur.x != pp.x ) 0/0; + + pp = new CPoint(); +// pointeur = pp; + +show( pointeur , pp ); + +} \ No newline at end of file diff --git a/src/CBot/TestCBot/plante.txt b/src/CBot/TestCBot/plante.txt new file mode 100644 index 00000000..363461b1 --- /dev/null +++ b/src/CBot/TestCBot/plante.txt @@ -0,0 +1,25 @@ +class Guepet +{ + + point pos; + float t = 0.1; + + void init() + { + pos.x = 12.123; + pos.y = 34.345; + + F(t); + } + + +} + +extern void object::Fourmi6() +{ + Guepet guepe=new Guepet(); + + guepe.init(); + + show ( guepe ); +} diff --git a/src/CBot/TestCBot/pointer.txt b/src/CBot/TestCBot/pointer.txt new file mode 100644 index 00000000..2d4d907a --- /dev/null +++ b/src/CBot/TestCBot/pointer.txt @@ -0,0 +1,41 @@ +extern public void x () +{ + show ( 3 ** 4 ); + float z = 1e-3; + show ( z ); + + CPoint b ( 4,5 ); + show ( b ); + + CPoint a ( ) ; + a.x = 21; a.y = 12; + show ( a ) ; + + CPoint test = new CPoint ( 1,1 ); + test = new CPoint ( 2, 2 ); + show ( test ); +} + +// crée un objet et retourne son pointeur +CPoint newcpoint() +{ + CPoint p = new CPoint ( 3, 3 ); + return p; +} + +extern public void y () +{ + CPoint test = newcpoint(); + println ( test ); + dontmodif( test ); + println ( test ); +} + +// ne doit pas modifier l'objet en paramètre +void dontmodif ( CPoint pp ) +{ + pp.x = 5; + pp.y = 2; + println ( pp, pp.x, pp.y ); +} + diff --git a/src/CBot/TestCBot/postinc.txt b/src/CBot/TestCBot/postinc.txt new file mode 100644 index 00000000..cdf6ab5b --- /dev/null +++ b/src/CBot/TestCBot/postinc.txt @@ -0,0 +1,7 @@ +extern public void X() +{ + point A [ ] ; + A[5] = new point (2,3); + int val = A[5].x++ + --A[5].y; + show ( A, val ); +} diff --git a/src/CBot/TestCBot/radar.txt b/src/CBot/TestCBot/radar.txt new file mode 100644 index 00000000..09d84a27 --- /dev/null +++ b/src/CBot/TestCBot/radar.txt @@ -0,0 +1,39 @@ +extern void object::Bug( ) +{ + try{ int a = 44 ; a = 12 / 0 ; } + catch(6000) { int b = 4 ; } + finally { int z = 1 ; } + +// tp ( A, B ); + +/* int a = 4, b = 2, c = nan; + float x, y = 3/2, z = nan; + boolean i, j = false, k = true; + + string s, ss = "xyz"; + + while ( false ) + { + object left, right; + + left = Radar(TypeMarkPath, -45, 120, 100); + right = Radar(TypeMarkPath, 45, 120, 100); + + if ( left == null && right == null ) + { + } + } + int t = fact ( 4 ) ;*/ +} + +void tp( point a , point b ) +{ + a.x += b.x; +} + + +int fact( int n ) +{ + if ( n < 2 ) return n; + return n * fact ( n - 1 ) ; +} \ No newline at end of file diff --git a/src/CBot/TestCBot/res/TestCBot.ico b/src/CBot/TestCBot/res/TestCBot.ico new file mode 100644 index 0000000000000000000000000000000000000000..06a649d7442de012830934b6f77fe4d85b2ae6e2 GIT binary patch literal 1078 zcmeHGF$%&!5S*yBmnWo>ltM7XN*fJ=*oY7C4K`YNFNt5d2L$qlbd~`hu+An%n;-~w zI(M@(w|56em;fGn2m&99YY!MveZN_U9x&_!A$tKiCp`>U37+kS1vp`CBdoM=&QWzk z>Io5bB!{-N{PELYfEnmS>5iwoh@y@lg6_3$_F>xdeAQcstWuH9QY77pEY{=}i1iN3Bxy9B NK6K1qGZu-TdIyMX)@%R( literal 0 HcmV?d00001 diff --git a/src/CBot/TestCBot/res/TestCBot.rc2 b/src/CBot/TestCBot/res/TestCBot.rc2 new file mode 100644 index 00000000..b55f0d91 --- /dev/null +++ b/src/CBot/TestCBot/res/TestCBot.rc2 @@ -0,0 +1,13 @@ +// +// TESTCBOT.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/src/CBot/TestCBot/res/TestCBotDoc.ico b/src/CBot/TestCBot/res/TestCBotDoc.ico new file mode 100644 index 0000000000000000000000000000000000000000..3545614c5abebb6942bf04a65b9040fec65b8445 GIT binary patch literal 1078 zcmcJNJyOFk5QSHSL9S3*DqJzSMN;G-IYK(OqBBQu<*Q@{N)AG)4qS;(t3 zm;M;&Zzq_k3>oWsj58e0aXar1)i_UwCOnn@z{mStpV!5%k6j!%NScrDhXEaN2bkhn zou#@Sqw8@v3d%!?qJr;>5BZs^lXS=Yy& n7fF7OQ@!g;zs|(G#m^5}GxK@7m8XppgE!la`cu<2&p-bQe^%Ie literal 0 HcmV?d00001 diff --git a/src/CBot/TestCBot/res/Toolbar.bmp b/src/CBot/TestCBot/res/Toolbar.bmp new file mode 100644 index 0000000000000000000000000000000000000000..04a71af5e5f1bfb3d300e43871df4b0d1c9785f7 GIT binary patch literal 1198 zcmcIjyH3ME5Ihu1SETT7P(})cYjjd7#II~&u|zbMOhHM9T=54eeG2E3Tv$qx!p!X2 z#74Aie9zsRo7uhGKYV4u>Yd#K?#_bR1N+SnCQnyHb+{PUkc5?}b}p{r=N& z)(1qZz&ms*t1T=pa7r~wrJXN|yFR7fZVizqo91jlOPr24n?G^75PGTWSKm=Qkyv24;lTo}W?I#R&;%Q6PXy^=Nq0VulaX$BA=XaKpcCC&xbA!Nfq>QZHAeDai-ACx^>UC$9x zt~iT&wXnki$KHkWdE7~17By#i=X`=3nV_le7i0nJ86SHkGCWk)UDtGo!!gWQ5SIV!3Uh2`(`7Kfkq|>Zu3fA9o8%GQT*d= zXTmv#D<20D7Q*su?{&hVdhkU!d3%B|0_OvRu8*D{$e!q6V)gJH$C-H@&>{#h8rC@f E4Mtz#rT_o{ literal 0 HcmV?d00001 diff --git a/src/CBot/TestCBot/resource.h b/src/CBot/TestCBot/resource.h new file mode 100644 index 00000000..bed36ca5 --- /dev/null +++ b/src/CBot/TestCBot/resource.h @@ -0,0 +1,30 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by TestCBot.rc +// +#define IDD_ABOUTBOX 100 +#define IDR_MAINFRAME 128 +#define IDR_TESTCBTYPE 129 +#define IDD_DIALOG1 130 +#define IDD_CONSOLE 131 +#define IDD_DIALOG2 133 +#define IDC_EDIT1 1000 +#define TX_TYPENAMES 1000 +#define IDC_SPIN1 1001 +#define IDC_EDIT2 1002 +#define IDC_COMBO1 1003 +#define IDC_EDIT3 1004 +#define ID_RUN 32771 +#define ID_TEST 32772 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 135 +#define _APS_NEXT_COMMAND_VALUE 32773 +#define _APS_NEXT_CONTROL_VALUE 1004 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/CBot/TestCBot/solution.txt b/src/CBot/TestCBot/solution.txt new file mode 100644 index 00000000..f78cf12e --- /dev/null +++ b/src/CBot/TestCBot/solution.txt @@ -0,0 +1,13 @@ +extern void object::Solution( ) +{ +show ( "Solution " + Position ); + Carré(15); + Carré(25); +} + +void object::Carré(float côté) +{ +show ( "Carré " + Position ); + Move(côté); + Turn(-90); +} \ No newline at end of file diff --git a/src/CBot/TestCBot/test.txt b/src/CBot/TestCBot/test.txt new file mode 100644 index 00000000..a912415d --- /dev/null +++ b/src/CBot/TestCBot/test.txt @@ -0,0 +1,8 @@ +extern public void x() +{ + float a= 1, b = 2; + a = b * ( 2 + 2 ); +// print (a); + a += 4; +// print (a); +} \ No newline at end of file diff --git a/src/CBot/TestCBot/test23.txt b/src/CBot/TestCBot/test23.txt new file mode 100644 index 00000000..d6e1dddb --- /dev/null +++ b/src/CBot/TestCBot/test23.txt @@ -0,0 +1,10 @@ +extern public void object::TEST23() +{ + CLASS22 T; + T.T22( ) ; + + show( position ); + show( this.position ); + +// T22(); +} \ No newline at end of file diff --git a/src/CBot/TestCBot/testmw.txt b/src/CBot/TestCBot/testmw.txt new file mode 100644 index 00000000..6570f6d1 --- /dev/null +++ b/src/CBot/TestCBot/testmw.txt @@ -0,0 +1,14 @@ +extern public int testmw( int a) +{ + boolean b = true ; + + if (b) + return 1 ; + else + return a ; 0 * testmw(a-1) ; +} + +public int Fibo2 ( int n ) +{ + print ( " bof " ); +} \ No newline at end of file diff --git a/src/CBot/TestCBot/this.txt b/src/CBot/TestCBot/this.txt new file mode 100644 index 00000000..b8a9e044 --- /dev/null +++ b/src/CBot/TestCBot/this.txt @@ -0,0 +1,13 @@ +extern void object :: TEST22 ( ) +{ + show( position ); + show( this.position ); + + T(); +} + +public void object :: T22() +{ + show( position ); + show( this.position ); +} \ No newline at end of file diff --git a/src/CBot/TestCBot/tt.txt b/src/CBot/TestCBot/tt.txt new file mode 100644 index 00000000..cd13c9db --- /dev/null +++ b/src/CBot/TestCBot/tt.txt @@ -0,0 +1,12 @@ +extern public void T() { T1(); } + +public void T1() +{ + show( "T1" ); + T2(); +} + +public void T2() +{ + show( "T2" ); +} \ No newline at end of file diff --git a/src/CBot/TestCBot/tt2.txt b/src/CBot/TestCBot/tt2.txt new file mode 100644 index 00000000..ad9dc1df --- /dev/null +++ b/src/CBot/TestCBot/tt2.txt @@ -0,0 +1,5 @@ +extern public void TT() +{ + T1(); + T2(); +} \ No newline at end of file diff --git a/src/CBot/TestCBot/vide.txt b/src/CBot/TestCBot/vide.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/CBot/TestCBot/xTestCBot.clw b/src/CBot/TestCBot/xTestCBot.clw new file mode 100644 index 00000000..5b84c163 --- /dev/null +++ b/src/CBot/TestCBot/xTestCBot.clw @@ -0,0 +1,245 @@ +; CLW file contains information for the MFC ClassWizard + +[General Info] +Version=1 +LastClass=CBotConsoleDlg +LastTemplate=CDialog +NewFileInclude1=#include "stdafx.h" +NewFileInclude2=#include "TestCBot.h" +LastPage=0 + +ClassCount=7 +Class1=CTestCBotApp +Class2=CTestCBotDoc +Class3=CTestCBotView +Class4=CMainFrame + +ResourceCount=7 +Resource1=IDD_ABOUTBOX +Resource2=IDR_MAINFRAME +Resource3=IDR_TESTCBTYPE +Class5=CAboutDlg +Class6=CChildFrame +Resource4=IDD_ABOUTBOX (French (France)) +Resource5=IDR_TESTCBTYPE (French (France)) +Resource6=IDD_CONSOLE +Class7=CBotConsoleDlg +Resource7=IDR_MAINFRAME (French (France)) + +[CLS:CTestCBotApp] +Type=0 +HeaderFile=TestCBot.h +ImplementationFile=TestCBot.cpp +Filter=N + +[CLS:CTestCBotDoc] +Type=0 +HeaderFile=TestCBotDoc.h +ImplementationFile=TestCBotDoc.cpp +Filter=N +BaseClass=CDocument +VirtualFilter=DC +LastObject=IDC_EDIT2 + +[CLS:CTestCBotView] +Type=0 +HeaderFile=TestCBotView.h +ImplementationFile=TestCBotView.cpp +Filter=C +BaseClass=CView +VirtualFilter=VWC +LastObject=CTestCBotView + +[CLS:CMainFrame] +Type=0 +HeaderFile=MainFrm.h +ImplementationFile=MainFrm.cpp +Filter=T +BaseClass=CMDIFrameWnd +VirtualFilter=fWC +LastObject=CMainFrame + + +[CLS:CChildFrame] +Type=0 +HeaderFile=ChildFrm.h +ImplementationFile=ChildFrm.cpp +Filter=M + +[CLS:CAboutDlg] +Type=0 +HeaderFile=TestCBot.cpp +ImplementationFile=TestCBot.cpp +Filter=D + +[DLG:IDD_ABOUTBOX] +Type=1 +ControlCount=4 +Control1=IDC_STATIC,static,1342177283 +Control2=IDC_STATIC,static,1342308352 +Control3=IDC_STATIC,static,1342308352 +Control4=IDOK,button,1342373889 +Class=CAboutDlg + +[MNU:IDR_MAINFRAME] +Type=1 +Class=CMainFrame +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command4=ID_APP_EXIT +Command5=ID_VIEW_TOOLBAR +Command6=ID_VIEW_STATUS_BAR +Command7=ID_APP_ABOUT +CommandCount=7 +Command3=ID_FILE_MRU_FILE1 + +[TB:IDR_MAINFRAME] +Type=1 +Class=CMainFrame +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_SAVE +Command4=ID_EDIT_CUT +Command5=ID_EDIT_COPY +Command6=ID_EDIT_PASTE +Command7=ID_FILE_PRINT +CommandCount=8 +Command8=ID_APP_ABOUT + +[MNU:IDR_TESTCBTYPE] +Type=1 +Class=CTestCBotView +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_CLOSE +Command4=ID_FILE_SAVE +Command5=ID_FILE_SAVE_AS +Command9=ID_EDIT_CUT +Command10=ID_EDIT_COPY +Command11=ID_EDIT_PASTE +Command12=ID_VIEW_TOOLBAR +Command13=ID_VIEW_STATUS_BAR +Command14=ID_WINDOW_NEW +CommandCount=18 +Command6=ID_FILE_MRU_FILE1 +Command7=ID_APP_EXIT +Command8=ID_EDIT_UNDO +Command15=ID_WINDOW_CASCADE +Command16=ID_WINDOW_TILE_HORZ +Command17=ID_WINDOW_ARRANGE +Command18=ID_APP_ABOUT + +[ACL:IDR_MAINFRAME] +Type=1 +Class=CMainFrame +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_SAVE +Command5=ID_EDIT_CUT +Command6=ID_EDIT_COPY +Command7=ID_EDIT_PASTE +Command8=ID_EDIT_UNDO +Command9=ID_EDIT_CUT +Command10=ID_EDIT_COPY +Command11=ID_EDIT_PASTE +Command12=ID_NEXT_PANE +CommandCount=13 +Command4=ID_EDIT_UNDO +Command13=ID_PREV_PANE + + +[TB:IDR_MAINFRAME (French (France))] +Type=1 +Class=? +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_SAVE +Command4=ID_EDIT_CUT +Command5=ID_EDIT_COPY +Command6=ID_EDIT_PASTE +Command7=ID_FILE_PRINT +Command8=ID_RUN +Command9=ID_APP_ABOUT +CommandCount=9 + +[MNU:IDR_MAINFRAME (French (France))] +Type=1 +Class=? +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_MRU_FILE1 +Command4=ID_APP_EXIT +Command5=ID_VIEW_TOOLBAR +Command6=ID_VIEW_STATUS_BAR +Command7=ID_APP_ABOUT +CommandCount=7 + +[MNU:IDR_TESTCBTYPE (French (France))] +Type=1 +Class=? +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_CLOSE +Command4=ID_FILE_SAVE +Command5=ID_FILE_SAVE_AS +Command6=ID_FILE_MRU_FILE1 +Command7=ID_APP_EXIT +Command8=ID_EDIT_UNDO +Command9=ID_EDIT_CUT +Command10=ID_EDIT_COPY +Command11=ID_EDIT_PASTE +Command12=ID_VIEW_TOOLBAR +Command13=ID_VIEW_STATUS_BAR +Command14=ID_WINDOW_NEW +Command15=ID_WINDOW_CASCADE +Command16=ID_WINDOW_TILE_HORZ +Command17=ID_WINDOW_ARRANGE +Command18=ID_APP_ABOUT +CommandCount=18 + +[ACL:IDR_MAINFRAME (French (France))] +Type=1 +Class=? +Command1=ID_EDIT_COPY +Command2=ID_FILE_NEW +Command3=ID_FILE_OPEN +Command4=ID_FILE_SAVE +Command5=ID_EDIT_PASTE +Command6=ID_EDIT_UNDO +Command7=ID_EDIT_CUT +Command8=ID_RUN +Command9=ID_NEXT_PANE +Command10=ID_PREV_PANE +Command11=ID_RUN +Command12=ID_EDIT_COPY +Command13=ID_EDIT_PASTE +Command14=ID_EDIT_CUT +Command15=ID_EDIT_UNDO +CommandCount=15 + +[DLG:IDD_ABOUTBOX (French (France))] +Type=1 +Class=CAboutDlg +ControlCount=4 +Control1=IDC_STATIC,static,1342177283 +Control2=IDC_STATIC,static,1342308480 +Control3=IDC_STATIC,static,1342308352 +Control4=IDOK,button,1342373889 + +[DLG:IDD_CONSOLE] +Type=1 +Class=CBotConsoleDlg +ControlCount=4 +Control1=IDC_STATIC,static,1342308352 +Control2=IDC_EDIT2,edit,1350631552 +Control3=IDOK,button,1342242817 +Control4=IDC_EDIT1,edit,1352734724 + +[CLS:CBotConsoleDlg] +Type=0 +HeaderFile=CBotConsoleDlg.h +ImplementationFile=CBotConsoleDlg.cpp +BaseClass=CDialog +Filter=D +VirtualFilter=dWC + diff --git a/src/CBot/TestCBot/zz.txt b/src/CBot/TestCBot/zz.txt new file mode 100644 index 00000000..da764ac7 --- /dev/null +++ b/src/CBot/TestCBot/zz.txt @@ -0,0 +1,6 @@ +extern public void zz() +{ + MaClass TOTO (); + + show (TOTO); +} \ No newline at end of file diff --git a/src/CBot/_Copy.bat b/src/CBot/_Copy.bat new file mode 100644 index 00000000..510dc5a1 --- /dev/null +++ b/src/CBot/_Copy.bat @@ -0,0 +1,2 @@ +copy debug\cbot.dll "F:\Program Files\Ceebot\cbot.dll" +cls \ No newline at end of file diff --git a/src/CBot/colobot.ini b/src/CBot/colobot.ini new file mode 100644 index 00000000..32163dba --- /dev/null +++ b/src/CBot/colobot.ini @@ -0,0 +1,49 @@ +[Directory] +scene=scene +savegame=savegame +public=program +user=user +[Setup] +TotoMode=1 +Tooltips=1 +InterfaceGlint=1 +NiceMouse=0 +Movies=1 +NiceReset=1 +HimselfDamage=1 +CameraScroll=1 +CameraInvertX=0 +InterfaceEffect=1 +GroundShadow=1 +GroundSpot=1 +ObjectDirty=1 +FogMode=1 +LensMode=1 +SkyMode=1 +PlanetMode=1 +LightMode=1 +UseJoystick=0 +ParticuleDensity=1.00 +ClippingDistance=1.00 +ObjectDetail=2.00 +GadgetQuantity=1.00 +TextureQuality=1 +AudioVolume=20 +MidiVolume=15 +Sound3D=0 +EditIndentMode=1 +EditIndentValue=4 +KeyMap=37+0 39+0 38+0 40+0 16+0 17+0 32+258 96+262 13+257 107+261 109+260 9+259 36+263 27+0 112+0 113+0 110+0 115+0 116+0 117+0 +[Engine] +AlphaMode=1 +StateColor=-1 +BlackSrcBlend=0 +BlackDestBlend=0 +WhiteSrcBlend=0 +WhiteDestBlend=0 +DiffuseSrcBlend=0 +DiffuseDestBlend=0 +AlphaSrcBlend=0 +AlphaDestBlend=0 +[Gamer] +LastName=Player diff --git a/src/CBot/idees.txt b/src/CBot/idees.txt new file mode 100644 index 00000000..71537895 --- /dev/null +++ b/src/CBot/idees.txt @@ -0,0 +1,39 @@ +pour la gestion des instances d'une classe. + +l'objet créé actuellement avec CBotVar::Create(nom, pClasse) +est a conserver tel quel, en dehors des vars sur la pile + +il faut un autre type de variable pour garder les pointeurs +CBotTypPtClass par exemple + +L'instance de la classe doit avoir un compteur d'utilisation +qui est le nombre d'objet de classe CBotTypPtClass qui y réfèrent. +Le compteur est décrémenté lorsque le pointeur est détruit, +l'objet supprimé lorsqu'il n'y a plus de pointeurs. + + +Dans le cas des robots, Daniel crée une instance de sa classe "Object" +et peut retourner des pointeurs à cette instance par des routines genre FindRobot() + +Object FindRobot(int n) { } + +pResult dans ce cas est un pointeur CBotTypPtClass +lorsqu'il a trouvé le robot concerné, il lui faudra faire + +pResult->SetPointeur(InstanceDeLaClassObject); + +cette opération incrémente le compteur des références + +-- + +lorsque le robot est détruit, l'instance de la classe Object correspondant +est détruit également. +s'il reste des pointeurs à cet objet, et l'on risque la planté + +solution 1: + garder non pas le pointeur à l'objet directement, mais + un index dans une tables de pointeurs + +solution 2: + ne pas détruire l'objet imédiatement lorsqu'il reste des pointeurs + mais le marqué comme virtuellement détruit \ No newline at end of file diff --git a/src/CBot/old CBotAddExpr.cpp b/src/CBot/old CBotAddExpr.cpp new file mode 100644 index 00000000..85b75881 --- /dev/null +++ b/src/CBot/old CBotAddExpr.cpp @@ -0,0 +1,130 @@ +/////////////////////////////////////////////////// +// expression du genre Opérande1 + Opérande2 +// Opérande1 - Opérande2 + +#include "CBot.h" + +// divers constructeurs + +CBotAddExpr::CBotAddExpr() +{ + m_leftop = + m_rightop = NULL; // NULL pour pouvoir faire delete sans autre + name = "CBotAddExpr"; // debug +} + +CBotAddExpr::~CBotAddExpr() +{ + delete m_leftop; + delete m_rightop; +} + + +// compile une instruction de type A + B + +CBotInstr* CBotAddExpr::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotCStack* pStk = pStack->TokenStack(); // un bout de pile svp + + // cherche des instructions qui peuvent convenir à gauche de l'opération + ou - + + CBotInstr* left = CBotMulExpr::Compile( p, pStk ); // expression A * B à gauche + if (left == NULL) return pStack->Return(NULL, pStk); // si erreur, la transmet + + // est-ce qu'on a le token + ou - ensuite ? + + if ( p->GetType() == ID_ADD || + p->GetType() == ID_SUB) // plus ou moins + { + CBotAddExpr* inst = new CBotAddExpr(); // élément pour opération + inst->SetToken(p); // mémorise l'opération + + int type1, type2; + type1 = pStk->GetType(CBotTypChar); // de quel type le premier opérande ? + + p = p->Next(); // saute le token de l'opération + + // cherche des instructions qui peuvent convenir à droite + + if ( NULL != (inst->m_rightop = CBotAddExpr::Compile( p, pStk )) ) // expression (...) à droite + { + // il y a un second opérande acceptable + + type2 = pStk->GetType(CBotTypChar); // de quel type le résultat ? + + if ( type1 == type2 && // les résultats sont-ils compatibles + type1 != CBotTypBoolean && + (inst->m_token.GetType() != ID_SUB || + type1 < CBotTypBoolean )) // pas de soustraction de chaînes ! + { + // si ok, enregistre l'opérande dans l'objet + inst->m_leftop = left; + // et rend l'object à qui l'a demandé + return pStack->Return(inst, pStk); + } + pStk->SetError(TX_BAD2TYPE, &inst->m_token); + } + + // en cas d'erreur, libère les éléments + delete left; + delete inst; + // et transmet l'erreur qui se trouve sur la pile + return pStack->Return(NULL, pStk); + } + + // si on n'a pas affaire à une opération + ou - + // rend à qui l'a demandé, l'opérande (de gauche) trouvé + // à la place de l'objet "addition" + return pStack->Return(left, pStk); +} + + + + +// fait l'opération d'addition ou de soustraction + +BOOL CBotAddExpr::Execute(CBotStack* &pStack) +{ + CBotStack* pStk1 = pStack->AddStack(); // ajoute un élément à la pile + // ou le retrouve en cas de reprise + + // selon la reprise, on peut être dans l'un des 2 états + + if ( pStk1->GetState() == 0 && // 1er état, évalue l'opérande de gauche + !m_leftop->Execute(pStk1) ) return FALSE; // interrompu ici ? + + // passe à l'étape suivante + pStk1->SetState(1); // prêt pour la suite + + // demande un peu plus de stack pour ne pas toucher le résultat de gauche + // qui se trouve sur la pile, justement. + + CBotStack* pStk2 = pStk1->AddStack(); // ajoute un élément à la pile + // ou le retrouve en cas de reprise + + // 2e état, évalue l'opérande de droite + if ( !m_rightop->Execute(pStk2) ) return FALSE; // interrompu ici ? + + int type1 = pStk1->GetType(); // de quels types les résultats ? + int type2 = pStk2->GetType(); + + // crée une variable temporaire pour y mettre le résultat + CBotVar* result = CBotVar::Create( NULL, MAX(type1, type2)); + + // fait l'opération selon la demande + switch (GetTokenType()) + { + case ID_ADD: + result->Add(pStk1->GetVar(), pStk2->GetVar()); // additionne + break; + case ID_SUB: + result->Sub(pStk1->GetVar(), pStk2->GetVar()); // soustrait + break; + } + pStk2->SetVar(result); // met le résultat sur la pile + + pStk1->Return(pStk2); // libère la pile + return pStack->Return(pStk1); // transmet le résultat +} + + diff --git a/src/CBot/old CBotCompExpr.cpp b/src/CBot/old CBotCompExpr.cpp new file mode 100644 index 00000000..e7439b76 --- /dev/null +++ b/src/CBot/old CBotCompExpr.cpp @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////// +// expression du genre Opérande1 > Opérande2 +// Opérande1 != Opérande2 +// etc. + +#include "CBot.h" + +// divers constructeurs + +CBotCompExpr::CBotCompExpr() +{ + m_leftop = + m_rightop = NULL; + name = "CBotCompExpr"; +} + +CBotCompExpr::~CBotCompExpr() +{ + delete m_leftop; + delete m_rightop; +} + + +// compile une instruction de type A < B + +CBotInstr* CBotCompExpr::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotCStack* pStk = pStack->TokenStack(); + + CBotInstr* left = CBotAddExpr::Compile( p, pStk ); // expression A + B à gauche + if (left == NULL) return pStack->Return(NULL, pStk); // erreur + + if ( p->GetType() == ID_HI || + p->GetType() == ID_LO || + p->GetType() == ID_HS || + p->GetType() == ID_LS || + p->GetType() == ID_EQ || + p->GetType() == ID_NE) // les diverses comparaisons + { + CBotCompExpr* inst = new CBotCompExpr(); // élément pour opération + inst->SetToken(p); // mémorise l'opération + + int type1, type2; + type1 = pStk->GetType(CBotTypChar); + + p = p->Next(); + if ( NULL != (inst->m_rightop = CBotAddExpr::Compile( p, pStk )) ) // expression A + B à droite + { + type2 = pStk->GetType(CBotTypChar); + // les résultats sont-ils compatibles + if ( type1 == type2 && type1 != CBotTypBoolean && type1 != CBotTypClass) + { + inst->m_leftop = left; + pStk->SetVar(new CBotVar(NULL, CBotTypBoolean)); + // le résultat est un boolean + return pStack->Return(inst, pStk); + } + pStk->SetError(TX_BAD2TYPE, &inst->m_token); + } + + delete left; + delete inst; + return pStack->Return(NULL, pStk); + } + + return pStack->Return(left, pStk); +} + + +// fait l'opération + +BOOL CBotCompExpr::Execute(CBotStack* &pStack) +{ + CBotStack* pStk1 = pStack->AddStack(); + + if ( pStk1->GetState() == 0 && !m_leftop->Execute(pStk1) ) return FALSE; // interrompu ici ? + + pStk1->SetState(1); // opération terminée + + // demande un peu plus de stack pour ne pas toucher le résultat de gauche + CBotStack* pStk2 = pStk1->AddStack(); + + if ( !m_rightop->Execute(pStk2) ) return FALSE; // interrompu ici ? + + int type1 = pStk1->GetType(); + int type2 = pStk2->GetType(); + + CBotVar* temp = CBotVar::Create( NULL, MAX(type1, type2) ); + CBotVar* result = CBotVar::Create( NULL, CBotTypBoolean ); + + switch (GetTokenType()) + { + case ID_LO: + temp->Lo(pStk1->GetVar(), pStk2->GetVar()); // inférieur + break; + case ID_HI: + temp->Hi(pStk1->GetVar(), pStk2->GetVar()); // supérieur + break; + case ID_LS: + temp->Ls(pStk1->GetVar(), pStk2->GetVar()); // inférieur ou égal + break; + case ID_HS: + temp->Hs(pStk1->GetVar(), pStk2->GetVar()); // supérieur ou égal + break; + case ID_EQ: + temp->Eq(pStk1->GetVar(), pStk2->GetVar()); // égal + break; + case ID_NE: + temp->Ne(pStk1->GetVar(), pStk2->GetVar()); // différent + break; + } + result->SetValInt(temp->GetValInt()); // converti le résultat + delete temp; + + pStk2->SetVar(result); // met le résultat sur la pile + + pStk1->Return(pStk2); // libère la pile + return pStack->Return(pStk1); // transmet le résultat +} + diff --git a/src/CBot/old TstCBot/BotConsoleDlg.cpp b/src/CBot/old TstCBot/BotConsoleDlg.cpp new file mode 100644 index 00000000..077f080a --- /dev/null +++ b/src/CBot/old TstCBot/BotConsoleDlg.cpp @@ -0,0 +1,164 @@ +// BotConsoleDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "TstCBot.h" +#include "BotConsoleDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CBotConsoleDlg dialog + + +CBotConsoleDlg::CBotConsoleDlg(CWnd* pParent /*=NULL*/) + : CDialog(CBotConsoleDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CBotConsoleDlg) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + m_pProg = NULL; + m_threadinfo.m_bRun = FALSE; +} + + +void CBotConsoleDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CBotConsoleDlg) + DDX_Control(pDX, IDOK, m_cOK); + DDX_Control(pDX, IDC_EDIT2, m_Edit2); + DDX_Control(pDX, IDC_EDIT1, m_Edit1); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CBotConsoleDlg, CDialog) + //{{AFX_MSG_MAP(CBotConsoleDlg) + ON_MESSAGE(WM_ENDPROG, EndProg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CBotConsoleDlg message handlers + +UINT ThreadProc(ThreadInfo *info) +{ + CTime t0 = CTime::GetCurrentTime(); + int Cpt = 0; + + info->m_pProg->Start("LaCommande"); + while ( !info->m_bStop && !info->m_pProg->Run() ) + { + Cpt++; + if ( Cpt%20 == 0 ) info->m_pEdit1->ReplaceSel("."); + } + + if ( info->m_bStop ) + { + info->m_pEdit1->ReplaceSel("\r\nInterrompu\r\n"); + } + else if (info->m_pProg->GivError() == 0) + { + CTime t = CTime::GetCurrentTime(); + CTimeSpan ts = t - t0; + + char buffer[200]; + sprintf( buffer, "\r\nExécution terminée en %d secondes.\r\nInterrompue %d fois.\r\n", + ts.GetTotalSeconds(), Cpt); + + info->m_pEdit1->ReplaceSel(buffer); + } + + info->m_pWndMessage->SendMessage(WM_ENDPROG, 0, 0) ; + return 0 ; +} + +LONG CBotConsoleDlg::EndProg(UINT wparam, LONG lparam) +{ + m_threadinfo.m_bRun = FALSE; + + if (m_pProg->GetError(m_code, m_start, m_end)) + { + AfxMessageBox(m_code); + CDialog::OnCancel(); + return 1; + } + delete m_pProg; + m_pProg = NULL; + + m_Edit2.EnableWindow(TRUE); + m_cOK.EnableWindow(TRUE); + + m_Edit2.SetWindowText(""); + m_Edit2.SetFocus(); + return 0 ; +} + +void CBotConsoleDlg::OnOK() +{ + CTstCBotApp* pApp = (CTstCBotApp*)AfxGetApp(); + pApp->m_pConsole = &m_Edit1; + + CString Commande; + m_Edit2.GetWindowText(Commande); + + CString s = "void LaCommande() { " + Commande + " ;}"; + m_pProg = new CBotProgram(); + CBotStringArray liste; + m_pProg->Compile(s, liste); + int err, start, end; + if ( m_pProg->GetError(err, start, end) ) + { + AfxMessageBox(err); + m_Edit2.SetSel(start-20, end-20); + return; + } + + m_Edit1.ReplaceSel(Commande + " ->\r\n"); + + m_Edit2.SetWindowText(""); + m_Edit1.SetFocus(); + m_Edit2.EnableWindow(FALSE); + m_cOK.EnableWindow(FALSE); + + // lance un processus paralèle pour l'exécution + m_threadinfo.m_pWndMessage = this ; + + m_threadinfo.m_pEdit1 = &m_Edit1; + m_threadinfo.m_pProg = m_pProg; + m_threadinfo.m_bStop = FALSE; + m_threadinfo.m_bRun = TRUE; + + AfxBeginThread((AFX_THREADPROC)ThreadProc, &m_threadinfo) ; +} + +void CBotConsoleDlg::OnCancel() +{ + if (!m_threadinfo.m_bRun) CDialog::OnCancel(); + m_threadinfo.m_bStop = TRUE ; +} + + +BOOL CBotConsoleDlg::OnInitDialog() +{ + CTstCBotApp* pApp = (CTstCBotApp*)AfxGetApp(); + + CDialog::OnInitDialog(); + + m_Edit1.ReplaceSel("Les fonctions suivantes sont disponibles:\r\n"); + for ( int i = 0; i < pApp->m_Liste.RetSize(); i++ ) + { + CBotString x = CString(pApp->m_Liste[i]) + "\r\n"; + m_Edit1.ReplaceSel(x); + } + m_Edit1.ReplaceSel("Entrez une commande ci-dessous.\r\n\r\n"); + + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/CBot/old TstCBot/BotConsoleDlg.h b/src/CBot/old TstCBot/BotConsoleDlg.h new file mode 100644 index 00000000..9b54ff2b --- /dev/null +++ b/src/CBot/old TstCBot/BotConsoleDlg.h @@ -0,0 +1,65 @@ +#if !defined(AFX_BOTCONSOLEDLG_H__A11450A2_8E09_11D4_A439_00D059085115__INCLUDED_) +#define AFX_BOTCONSOLEDLG_H__A11450A2_8E09_11D4_A439_00D059085115__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// BotConsoleDlg.h : header file +// + +struct ThreadInfo +{ + CEdit* m_pEdit1 ; + CBotProgram* m_pProg; + CWnd* m_pWndMessage; + BOOL m_bStop; + BOOL m_bRun; +}; + + +///////////////////////////////////////////////////////////////////////////// +// CBotConsoleDlg dialog + +class CBotConsoleDlg : public CDialog +{ +// Construction +public: + CBotConsoleDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CBotConsoleDlg) + enum { IDD = IDD_CONSOLE }; + CButton m_cOK; + CEdit m_Edit2; + CEdit m_Edit1; + //}}AFX_DATA + + CBotProgram* m_pProg; + ThreadInfo m_threadinfo; + + int m_code, m_start, m_end; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CBotConsoleDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CBotConsoleDlg) + virtual void OnOK(); + virtual void OnCancel(); + virtual BOOL OnInitDialog(); + afx_msg LONG EndProg(UINT wparam, LONG lparam) ; + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_BOTCONSOLEDLG_H__A11450A2_8E09_11D4_A439_00D059085115__INCLUDED_) diff --git a/src/CBot/old TstCBot/BotErrorDlg.cpp b/src/CBot/old TstCBot/BotErrorDlg.cpp new file mode 100644 index 00000000..87d56f04 --- /dev/null +++ b/src/CBot/old TstCBot/BotErrorDlg.cpp @@ -0,0 +1,56 @@ +// BotErrorDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "TstCBot.h" +#include "BotErrorDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CBotErrorDlg dialog + + +CBotErrorDlg::CBotErrorDlg(CWnd* pParent /*=NULL*/) + : CDialog(CBotErrorDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CBotErrorDlg) + m_TextProgram = _T(""); + //}}AFX_DATA_INIT +} + + +void CBotErrorDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CBotErrorDlg) + DDX_Control(pDX, IDC_EDIT1, m_eProgram); + DDX_Control(pDX, IDC_STATIC1, m_sMessage); + DDX_Text(pDX, IDC_EDIT1, m_TextProgram); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CBotErrorDlg, CDialog) + //{{AFX_MSG_MAP(CBotErrorDlg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CBotErrorDlg message handlers + +BOOL CBotErrorDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_sMessage.SetWindowText(m_TextError); + m_eProgram.SetFocus(); + m_eProgram.SetSel(m_start, m_end); + + return FALSE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/CBot/old TstCBot/BotErrorDlg.h b/src/CBot/old TstCBot/BotErrorDlg.h new file mode 100644 index 00000000..522afad5 --- /dev/null +++ b/src/CBot/old TstCBot/BotErrorDlg.h @@ -0,0 +1,51 @@ +#if !defined(AFX_BOTERRORDLG_H__80E73D20_7454_11D4_A439_00D059085115__INCLUDED_) +#define AFX_BOTERRORDLG_H__80E73D20_7454_11D4_A439_00D059085115__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// BotErrorDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CBotErrorDlg dialog + +class CBotErrorDlg : public CDialog +{ +// Construction +public: + CBotErrorDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CBotErrorDlg) + enum { IDD = IDD_DIALOG1 }; + CEdit m_eProgram; + CStatic m_sMessage; + CString m_TextProgram; + //}}AFX_DATA + + + CString m_TextError; + int m_start, m_end; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CBotErrorDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CBotErrorDlg) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_BOTERRORDLG_H__80E73D20_7454_11D4_A439_00D059085115__INCLUDED_) diff --git a/src/CBot/old TstCBot/CBotTest.txt b/src/CBot/old TstCBot/CBotTest.txt new file mode 100644 index 00000000..ce20e26f --- /dev/null +++ b/src/CBot/old TstCBot/CBotTest.txt @@ -0,0 +1,36 @@ + +extern void TheTest() +{ + for (int x = 130; x>0; x--) print (x); +} + +extern void Test() +{ + int var = 10000 ; + while (var > 0) var = var -1; +} +// exécuté en 30 secondes + +extern void Autre() +{ + int var = 10000 ; + while (var > 0) if ( var > 0 ) var = var -1; +} +// exécuté en 45 secondes + +int Y ( int n ) +{ + if ( n < 2 ) return n; + int a = Y(n-1) + Y(n-2); + return a; +} + +extern int X ( int n ) +{ + if ( n < 2 ) { print(n); return n; } + int a = X(n-1) + Y(n-2); + print (a); + return a; +} + + diff --git a/src/CBot/old TstCBot/CMyThread.cpp b/src/CBot/old TstCBot/CMyThread.cpp new file mode 100644 index 00000000..ca92c770 --- /dev/null +++ b/src/CBot/old TstCBot/CMyThread.cpp @@ -0,0 +1,107 @@ +// CMyThread.cpp : pour créer un processus pour la console +// + +#include "stdafx.h" +#include "TstCBot.h" +#include "CMyThread.h" +#include "BotConsoleDlg.h" + + +//IMPLEMENT_DYNAMIC (CMyThread, CWinThread) +IMPLEMENT_DYNCREATE (CMyThread, CWinThread) + +///////////////////////////////////////////////////////////////////////////// +// CMyThread + +BEGIN_MESSAGE_MAP(CMyThread, CWinThread) + //{{AFX_MSG_MAP(CMyThread) + //}}AFX_MSG_MAP + // Standard file based document commands +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CMyThread construction + +CMyThread::CMyThread() +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + + +///////////////////////////////////////////////////////////////////////////// +// CMyThread initialization +/* +BOOL CMyThread::InitInstance() +{ + AfxEnableControlContainer(); + + CTstCBotApp* pApp = (CTstCBotApp*)AfxGetApp(); + + // ouvre une fenêtre pour afficher les sorties + CRect rect; + AfxGetMainWnd()->GetClientRect( rect ); + rect += CPoint(30,30); + + CWnd* pWnd = new CWnd(); + pWnd->CreateEx( 0, + AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW)), + "CBot console", WS_POPUPWINDOW|WS_CAPTION|WS_VISIBLE, + rect, + AfxGetMainWnd()->GetParent(), NULL, NULL); + m_pMainWnd = pWnd; + + pApp->m_pEdit2 = new CEdit(); + + m_pMainWnd->GetClientRect( rect ); + rect.bottom -= 40; + pApp->m_pEdit2->Create( WS_VISIBLE|WS_BORDER|WS_TABSTOP|ES_MULTILINE|ES_WANTRETURN| + ES_AUTOVSCROLL|ES_READONLY, + rect, m_pMainWnd, IDC_EDIT2 ); + + pApp->m_pEdit2->ReplaceSel("Les fonctions suivantes sont disponibles:\n\r"); + for ( int i = 0; i < pApp->m_Liste.RetSize(); i++ ) + { + pApp->m_pEdit2->ReplaceSel(pApp->m_Liste[i] + "\r\n"); + } + pApp->m_pEdit2->ReplaceSel("Entrez une commande ci-dessous.\r\r"); + + +// pApp->m_pEdit2->SetFocus(); + + pApp->m_pEdit3 = new CEdit(); + m_pMainWnd->GetClientRect( rect ); + rect.top = rect.bottom-40; + pApp->m_pEdit3->Create( WS_VISIBLE|WS_BORDER|WS_TABSTOP, + rect, m_pMainWnd, IDC_EDIT1 ); + pApp->m_pEdit3->SetFocus(); + + return TRUE; +}*/ + +BOOL CMyThread::InitInstance() +{ + CBotConsoleDlg dlg; + m_pMainWnd = &dlg; // cela ferme l'application avec la DBOX ! + + int nResponse = dlg.DoModal(); + + return TRUE; +} + + +int CMyThread::ExitInstance() +{ + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// +// CMyThread message handlers + + +void CMyThread::OnReturn() +{ + // TODO: Add your command handler code here + __asm int 3; +} diff --git a/src/CBot/old TstCBot/CMyThread.h b/src/CBot/old TstCBot/CMyThread.h new file mode 100644 index 00000000..1134077d --- /dev/null +++ b/src/CBot/old TstCBot/CMyThread.h @@ -0,0 +1,44 @@ +// CMyThread.h : pour créer un processus pour la console +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_MAINFRM_H__20B3756C_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) +#define AFX_MAINFRM_H__20B3756C_5DFD_11D4_A15E_00E0189013DF__INCLUDED_ + + +#include "stdafx.h" +#include "TstCBot.h" + +class CMyThread : public CWinThread +{ +// DECLARE_DYNAMIC(CMyThread) + DECLARE_DYNCREATE(CMyThread) + +public: + + +// Constructor + CMyThread(); + virtual BOOL InitInstance(); + virtual int ExitInstance(); // return app exit code + +// Implementation + + //{{AFX_MSG(CTstCBotApp) + afx_msg void OnAppAbout(); + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + +// Generated message map functions +protected: + //{{AFX_MSG(CMainFrame) + afx_msg void OnReturn(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAINFRM_H__20B3756C_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) diff --git a/src/CBot/old TstCBot/MainFrm.cpp b/src/CBot/old TstCBot/MainFrm.cpp new file mode 100644 index 00000000..6c0962cb --- /dev/null +++ b/src/CBot/old TstCBot/MainFrm.cpp @@ -0,0 +1,91 @@ +// MainFrm.cpp : implementation of the CMainFrame class +// + +#include "stdafx.h" +#include "TstCBot.h" + +#include "MainFrm.h" +#include "BotErrorDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame + +IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd) + +BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) + //{{AFX_MSG_MAP(CMainFrame) + ON_WM_CREATE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +static UINT indicators[] = +{ + ID_SEPARATOR, // status line indicator + ID_INDICATOR_CAPS, + ID_INDICATOR_NUM, + ID_INDICATOR_SCRL, +}; + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame construction/destruction + +CMainFrame::CMainFrame() +{ + // TODO: add member initialization code here + +} + +CMainFrame::~CMainFrame() +{ +} + +int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CFrameWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + if (!m_wndStatusBar.Create(this) || + !m_wndStatusBar.SetIndicators(indicators, + sizeof(indicators)/sizeof(UINT))) + { + TRACE0("Failed to create status bar\n"); + return -1; // fail to create + } + + return 0; +} + +BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) +{ + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + return CFrameWnd::PreCreateWindow(cs); +} + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame diagnostics + +#ifdef _DEBUG +void CMainFrame::AssertValid() const +{ + CFrameWnd::AssertValid(); +} + +void CMainFrame::Dump(CDumpContext& dc) const +{ + CFrameWnd::Dump(dc); +} + +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame message handlers + + diff --git a/src/CBot/old TstCBot/MainFrm.h b/src/CBot/old TstCBot/MainFrm.h new file mode 100644 index 00000000..56b9c411 --- /dev/null +++ b/src/CBot/old TstCBot/MainFrm.h @@ -0,0 +1,55 @@ +// MainFrm.h : interface of the CMainFrame class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_MAINFRM_H__70B3756C_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) +#define AFX_MAINFRM_H__70B3756C_5DFD_11D4_A15E_00E0189013DF__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +class CMainFrame : public CFrameWnd +{ +protected: // create from serialization only + CMainFrame(); + DECLARE_DYNCREATE(CMainFrame) + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMainFrame) + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CMainFrame(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: // control bar embedded members + CStatusBar m_wndStatusBar; + +// Generated message map functions +protected: + //{{AFX_MSG(CMainFrame) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnCp1(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAINFRM_H__70B3756C_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) diff --git a/src/CBot/old TstCBot/ReadMe.txt b/src/CBot/old TstCBot/ReadMe.txt new file mode 100644 index 00000000..67dc05b9 --- /dev/null +++ b/src/CBot/old TstCBot/ReadMe.txt @@ -0,0 +1,93 @@ +======================================================================== + MICROSOFT FOUNDATION CLASS LIBRARY : TstCBot +======================================================================== + + +AppWizard has created this TstCBot application for you. This application +not only demonstrates the basics of using the Microsoft Foundation classes +but is also a starting point for writing your application. + +This file contains a summary of what you will find in each of the files that +make up your TstCBot application. + +TstCBot.h + This is the main header file for the application. It includes other + project specific headers (including Resource.h) and declares the + CTstCBotApp application class. + +TstCBot.cpp + This is the main application source file that contains the application + class CTstCBotApp. + +TstCBot.rc + This is a listing of all of the Microsoft Windows resources that the + program uses. It includes the icons, bitmaps, and cursors that are stored + in the RES subdirectory. This file can be directly edited in Microsoft + Developer Studio. + +res\TstCBot.ico + This is an icon file, which is used as the application's icon. This + icon is included by the main resource file TstCBot.rc. + +res\TstCBot.rc2 + This file contains resources that are not edited by Microsoft + Developer Studio. You should place all resources not + editable by the resource editor in this file. + +TstCBot.clw + This file contains information used by ClassWizard to edit existing + classes or add new classes. ClassWizard also uses this file to store + information needed to create and edit message maps and dialog data + maps and to create prototype member functions. + +///////////////////////////////////////////////////////////////////////////// + +For the main frame window: + +MainFrm.h, MainFrm.cpp + These files contain the frame class CMainFrame, which is derived from + CFrameWnd and controls all SDI frame features. + + +///////////////////////////////////////////////////////////////////////////// + +AppWizard creates one document type and one view: + +TstCBotDoc.h, TstCBotDoc.cpp - the document + These files contain your CTstCBotDoc class. Edit these files to + add your special document data and to implement file saving and loading + (via CTstCBotDoc::Serialize). + +TstCBotView.h, TstCBotView.cpp - the view of the document + These files contain your CTstCBotView class. + CTstCBotView objects are used to view CTstCBotDoc objects. + + + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named TstCBot.pch and a precompiled types file named StdAfx.obj. + +Resource.h + This is the standard header file, which defines new resource IDs. + Microsoft Developer Studio reads and updates this file. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" to indicate parts of the source code you +should add to or customize. + +If your application uses MFC in a shared DLL, and your application is +in a language other than the operating system's current language, you +will need to copy the corresponding localized resources MFC40XXX.DLL +from the Microsoft Visual C++ CD-ROM onto the system or system32 directory, +and rename it to be MFCLOC.DLL. ("XXX" stands for the language abbreviation. +For example, MFC40DEU.DLL contains resources translated to German.) If you +don't do this, some of the UI elements of your application will remain in the +language of the operating system. + +///////////////////////////////////////////////////////////////////////////// diff --git a/src/CBot/old TstCBot/Resource.h b/src/CBot/old TstCBot/Resource.h new file mode 100644 index 00000000..6863fd8d --- /dev/null +++ b/src/CBot/old TstCBot/Resource.h @@ -0,0 +1,68 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by TstCBot.rc +// +#define IDD_ABOUTBOX 100 +#define IDR_MAINFRAME 128 +#define IDR_TSTCBOTYPE 129 +#define IDD_DIALOG1 130 +#define IDD_CONSOLE 131 +#define IDC_EDIT1 1000 +#define TX_TYPENAMES 1000 +#define IDC_STATIC1 1001 +#define IDC_EDIT2 1002 +#define TX_OPENPAR 5000 +#define TX_CLOSEPAR 5001 +#define TX_NOTBOOL 5002 +#define TX_UNDEFVAR 5003 +#define TX_BADLEFT 5004 +#define TX_ENDOF 5005 +#define TX_OUTCASE 5006 +#define TX_NOTERM 5007 +#define TX_CLOSEBLK 5008 +#define TX_ELSEWITHOUTIF 5009 +#define TX_OPENBLK 5010 +#define TX_BADTYPE 5011 +#define TX_REDEFVAR 5012 +#define TX_BAD2TYPE 5013 +#define TX_UNDEFCALL 5014 +#define TX_MISDOTS 5015 +#define TX_WHILE 5016 +#define TX_BREAK 5017 +#define TX_LABEL 5018 +#define TX_NOLABEL 5019 +#define TX_NOCASE 5020 +#define TX_BADNUM 5021 +#define TX_VOID 5022 +#define TX_NOTYP 5023 +#define TX_NOVAR 5024 +#define TX_NOFONC 5025 +#define TX_OVERPARAM 5026 +#define TX_REDEF 5027 +#define TX_LOWPARAM 5028 +#define TX_BADPARAM 5029 +#define TX_NUMPARAM 5030 +#define TX_NOITEM 5031 +#define TX_DOT 5032 +#define TX_NOCONST 5033 +#define TX_REDEFCLASS 5034 +#define TX_DIVZERO 6000 +#define TX_NOTINIT 6001 +#define TX_BADTHROW 6002 +#define TX_NORETVAL 6003 +#define TX_NORUN 6004 +#define TX_NOCALL 6005 +#define ID_CP1 32771 +#define ID_EXE 32772 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 132 +#define _APS_NEXT_COMMAND_VALUE 32775 +#define _APS_NEXT_CONTROL_VALUE 1002 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/CBot/old TstCBot/StdAfx.cpp b/src/CBot/old TstCBot/StdAfx.cpp new file mode 100644 index 00000000..ae0ec933 --- /dev/null +++ b/src/CBot/old TstCBot/StdAfx.cpp @@ -0,0 +1,6 @@ +// stdafx.cpp : source file that includes just the standard includes +// TstCBot.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + diff --git a/src/CBot/old TstCBot/StdAfx.h b/src/CBot/old TstCBot/StdAfx.h new file mode 100644 index 00000000..7d46aceb --- /dev/null +++ b/src/CBot/old TstCBot/StdAfx.h @@ -0,0 +1,26 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__70B3756A_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) +#define AFX_STDAFX_H__70B3756A_5DFD_11D4_A15E_00E0189013DF__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC OLE automation classes +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__70B3756A_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) diff --git a/src/CBot/old TstCBot/TstCBot.clw b/src/CBot/old TstCBot/TstCBot.clw new file mode 100644 index 00000000..4c541688 --- /dev/null +++ b/src/CBot/old TstCBot/TstCBot.clw @@ -0,0 +1,189 @@ +; CLW file contains information for the MFC ClassWizard + +[General Info] +Version=1 +LastClass=CTstCBotView +LastTemplate=CDialog +NewFileInclude1=#include "stdafx.h" +NewFileInclude2=#include "TstCBot.h" +LastPage=0 + +ClassCount=7 +Class1=CTstCBotApp +Class2=CTstCBotDoc +Class3=CTstCBotView +Class4=CMainFrame + +ResourceCount=6 +Resource1=IDD_ABOUTBOX +Resource2=IDR_MAINFRAME +Class5=CAboutDlg +Resource3=IDD_ABOUTBOX (French (France)) +Resource4=IDD_CONSOLE +Class6=CBotErrorDlg +Resource5=IDD_DIALOG1 (French (Switzerland)) +Class7=CBotConsoleDlg +Resource6=IDR_MAINFRAME (French (France)) + +[CLS:CTstCBotApp] +Type=0 +HeaderFile=TstCBot.h +ImplementationFile=TstCBot.cpp +Filter=N + +[CLS:CTstCBotDoc] +Type=0 +HeaderFile=TstCBotDoc.h +ImplementationFile=TstCBotDoc.cpp +Filter=N +BaseClass=CDocument +VirtualFilter=DC +LastObject=CTstCBotDoc + +[CLS:CTstCBotView] +Type=0 +HeaderFile=TstCBotView.h +ImplementationFile=TstCBotView.cpp +Filter=C +BaseClass=CView +VirtualFilter=VWC +LastObject=CTstCBotView + +[CLS:CMainFrame] +Type=0 +HeaderFile=MainFrm.h +ImplementationFile=MainFrm.cpp +Filter=T +BaseClass=CFrameWnd +VirtualFilter=fWC +LastObject=CMainFrame + + + +[CLS:CAboutDlg] +Type=0 +HeaderFile=TstCBot.cpp +ImplementationFile=TstCBot.cpp +Filter=D + +[DLG:IDD_ABOUTBOX] +Type=1 +Class=CAboutDlg +ControlCount=4 +Control1=IDC_STATIC,static,1342177283 +Control2=IDC_STATIC,static,1342308480 +Control3=IDC_STATIC,static,1342308352 +Control4=IDOK,button,1342373889 + +[MNU:IDR_MAINFRAME] +Type=1 +Class=CMainFrame +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_SAVE +Command4=ID_FILE_SAVE_AS +Command5=ID_FILE_MRU_FILE1 +Command6=ID_APP_EXIT +Command7=ID_EDIT_UNDO +Command8=ID_EDIT_CUT +Command9=ID_EDIT_COPY +Command10=ID_EDIT_PASTE +Command11=ID_VIEW_STATUS_BAR +Command12=ID_CP1 +Command13=ID_EXE +Command14=ID_APP_ABOUT +CommandCount=14 + +[ACL:IDR_MAINFRAME] +Type=1 +Class=CMainFrame +Command1=ID_CP1 +Command2=ID_FILE_NEW +Command3=ID_FILE_OPEN +Command4=ID_FILE_SAVE +Command5=ID_EXE +Command6=ID_EDIT_UNDO +Command7=ID_EDIT_CUT +Command8=ID_EXE +Command9=ID_CP1 +Command10=ID_EXE +CommandCount=10 + +[MNU:IDR_MAINFRAME (French (France))] +Type=1 +Class=? +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_SAVE +Command4=ID_FILE_SAVE_AS +Command5=ID_FILE_MRU_FILE1 +Command6=ID_APP_EXIT +Command7=ID_EDIT_UNDO +Command8=ID_EDIT_CUT +Command9=ID_EDIT_COPY +Command10=ID_EDIT_PASTE +Command11=ID_VIEW_STATUS_BAR +Command12=ID_CP1 +Command13=ID_EXE +Command14=ID_APP_ABOUT +CommandCount=14 + +[ACL:IDR_MAINFRAME (French (France))] +Type=1 +Class=? +Command1=ID_CP1 +Command2=ID_FILE_NEW +Command3=ID_FILE_OPEN +Command4=ID_FILE_SAVE +Command5=ID_EXE +Command6=ID_EDIT_UNDO +Command7=ID_EDIT_CUT +Command8=ID_EXE +Command9=ID_CP1 +Command10=ID_EXE +CommandCount=10 + +[DLG:IDD_ABOUTBOX (French (France))] +Type=1 +Class=CAboutDlg +ControlCount=4 +Control1=IDC_STATIC,static,1342177283 +Control2=IDC_STATIC,static,1342308480 +Control3=IDC_STATIC,static,1342308352 +Control4=IDOK,button,1342373889 + +[CLS:CBotErrorDlg] +Type=0 +HeaderFile=BotErrorDlg.h +ImplementationFile=BotErrorDlg.cpp +BaseClass=CDialog +Filter=D +VirtualFilter=dWC +LastObject=CBotErrorDlg + +[DLG:IDD_DIALOG1 (French (Switzerland))] +Type=1 +ControlCount=4 +Control1=IDOK,button,1342242817 +Control2=IDC_EDIT1,edit,1352728708 +Control3=IDC_STATIC,static,1342308352 +Control4=IDC_STATIC1,static,1342308352 + +[DLG:IDD_CONSOLE] +Type=1 +Class=CBotConsoleDlg +ControlCount=4 +Control1=IDC_STATIC,static,1342308352 +Control2=IDC_EDIT2,edit,1350631552 +Control3=IDOK,button,1342242817 +Control4=IDC_EDIT1,edit,1352734724 + +[CLS:CBotConsoleDlg] +Type=0 +HeaderFile=BotConsoleDlg.h +ImplementationFile=BotConsoleDlg.cpp +BaseClass=CDialog +Filter=D +VirtualFilter=dWC +LastObject=IDOK + diff --git a/src/CBot/old TstCBot/TstCBot.cpp b/src/CBot/old TstCBot/TstCBot.cpp new file mode 100644 index 00000000..8ac45572 --- /dev/null +++ b/src/CBot/old TstCBot/TstCBot.cpp @@ -0,0 +1,412 @@ +// TstCBot.cpp : Defines the class behaviors for the application. +// + +#include "stdafx.h" +#include "TstCBot.h" + +#include "MainFrm.h" +#include "TstCBotDoc.h" +#include "TstCBotView.h" +#include "CMyThread.h" + + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + + +//////////////////////////////////////////////////////////////////// +// routine show() +// utilisable depuis le programme écrit en CBot + +// exécution +BOOL rShow( CBotVar* pVar, CBotVar* pResult, int& Exception ) +{ + CString s; + + if ( pVar == NULL ) + { + Exception = 22; return FALSE; + } + + while ( pVar != NULL ) + { + CString ss; + ss.LoadString( TX_TYPENAMES + pVar->RetType() ); + s += ss + " "; + + ss = pVar->RetName(); + if (ss.IsEmpty()) ss = ""; + s += ss + " = "; + + s += pVar->RetValString(); + s += "\n"; + pVar = pVar->RetNext(); + } + + AfxMessageBox(s, MB_OK|MB_ICONINFORMATION); + +// if ( pResult && pResult->RetType() == CBotTypInt) pResult->SetValInt(123); + + return TRUE; // pas d'interruption +} + +int cShow( CBotVar* &pVar, CBotString& RetClass) +{ + if ( pVar == NULL ) return 22; + return CBotTypInt; // tous paramètres acceptés, un entier en retour +} + +int cErr( CBotVar* &pVar, CBotString& RetClass) +{ + pVar = pVar->RetNext(); // avance le pointeur sur l'erreur + return 6666; +} + +//////////////////////////////////////////////////////////////////// +// routine print() +// utilisable depuis le programme écrit en CBot + +// exécution +BOOL rPrintLn( CBotVar* pVar, CBotVar* pResult, int& Exception ) +{ + CString s; + + CTstCBotApp* pApp = (CTstCBotApp*)AfxGetApp(); + CEdit* pEdit = pApp->m_pConsole; + + if (pEdit == NULL) return TRUE; + pEdit->GetWindowText(s); + + while ( pVar != NULL ) + { + if ( !s.IsEmpty() ) s += "\r\n"; + s += pVar->RetValString(); + pVar = pVar->RetNext(); + } + + pEdit->SetWindowText(s); + pEdit->SetSel(s.GetLength(), s.GetLength()); + pEdit->SetFocus(); + return TRUE; // pas d'interruption +} + +BOOL rPrint( CBotVar* pVar, CBotVar* pResult, int& Exception ) +{ + CString s; + + CTstCBotApp* pApp = (CTstCBotApp*)AfxGetApp(); + CEdit* pEdit = pApp->m_pConsole; + + if (pEdit == NULL) return TRUE; + pEdit->GetWindowText(s); + + while ( pVar != NULL ) + { + if ( !s.IsEmpty() ) s += " "; + s += pVar->RetValString(); + pVar = pVar->RetNext(); + } + + pEdit->SetWindowText(s); + pEdit->SetSel(s.GetLength(), s.GetLength()); + pEdit->SetFocus(); + return TRUE; // pas d'interruption +} + +int cPrint( CBotVar* &pVar, CBotString& RetClass) +{ + return 0; // tous paramètres acceptés, un entier en retour +} + + +////////////////////////////////////////////////////////////////// +// class CPoint pour essayer + +// exécution +BOOL rCPoint( CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception ) +{ + CString s; + + if ( pVar == NULL )return TRUE; // constructeur sans paramètres est ok + + if ( pVar->RetType() > CBotTypDouble ) + { + Exception = 6023; return FALSE; + } + + CBotVar* pX = pThis->RetItem("x"); + if ( pX == NULL ) + { + Exception = 6024; return FALSE; + } + + pX->SetValFloat( pVar->RetValFloat() ); + pVar = pVar->RetNext(); + + if ( pVar == NULL ) + { + Exception = 6022; return FALSE; + } + + if ( pVar->RetType() > CBotTypDouble ) + { + Exception = 6023; return FALSE; + } + + CBotVar* pY = pThis->RetItem("y"); + if ( pY == NULL ) + { + Exception = 6024; return FALSE; + } + + pY->SetValFloat( pVar->RetValFloat() ); + pVar = pVar->RetNext(); + + if ( pVar != NULL ) + { + Exception = 6025; return FALSE; + } + + return TRUE; // pas d'interruption +} + +int cCPoint( CBotVar* pThis, CBotVar* &pVar, CBotString& RetClass) +{ + // l'objet doit être de la classe CPoint + if ( !pThis->IsElemOfClass("CPoint") ) return 6021; + + // ok si aucun paramètres ! + if ( pVar == NULL ) return 0; + + // paramètre de type numérique svp + if ( pVar->RetType() > CBotTypDouble ) return 6023; + pVar = pVar->RetNext(); + + // il doit y avoir un second paramètre + if ( pVar == NULL ) return 6022; + // également de type numérique + if ( pVar->RetType() > CBotTypDouble )return 6023; + pVar = pVar->RetNext(); + + // et pas plus de 2 paramètres svp + if ( pVar != NULL ) return 6025; + + return 0; // cette fonction retourne void +} + +// méthode déterminant l'opposé +BOOL rOppose( CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception ) +{ + CString s; + + if ( pVar != NULL ) // pas de paramètre + { + Exception = 6025; return FALSE; + } + + CBotVar* pvar = pThis->RetItemList(); // demande la chaîne des items + + // tous les paramètres sont des nombres + while (pvar != NULL) + { + pvar->SetValFloat( -pvar->RetValFloat() ); + pvar = pvar->RetNext(); + } + + pResult->Copy(pThis); + return TRUE; // pas d'interruption +} + +int cOppose( CBotVar* pThis, CBotVar* &pVar, CBotString& RetClass) +{ + // l'objet doit être de la classe CPoint + if ( !pThis->IsElemOfClass("CPoint") ) return 6021; + + RetClass = "CPoint"; // l'objet rendu est de cette class + + // ok si aucun paramètres ! + if ( pVar == NULL ) return CBotTypClass; // le paramètre retourné est une instance de la classe + + return TX_OVERPARAM; // ça va pas +} + + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotApp + +BEGIN_MESSAGE_MAP(CTstCBotApp, CWinApp) + //{{AFX_MSG_MAP(CTstCBotApp) + ON_COMMAND(ID_APP_ABOUT, OnAppAbout) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG_MAP + // Standard file based document commands + ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) + ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotApp construction + +CTstCBotApp::CTstCBotApp() +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only CTstCBotApp object + +CTstCBotApp theApp; + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotApp initialization + +BOOL CTstCBotApp::InitInstance() +{ + AfxEnableControlContainer(); + + // Standard initialization + // If you are not using these features and wish to reduce the size + // of your final executable, you should remove from the following + // the specific initialization routines you do not need. + +#ifdef _AFXDLL + Enable3dControls(); // Call this when using MFC in a shared DLL +#else + Enable3dControlsStatic(); // Call this when linking to MFC statically +#endif + + // Change the registry key under which our settings are stored. + // You should modify this string to be something appropriate + // such as the name of your company or organization. + SetRegistryKey(_T("Local AppWizard-Generated Applications")); + + LoadStdProfileSettings(); // Load standard INI file options (including MRU) + + // Register the application's document templates. Document templates + // serve as the connection between documents, frame windows and views. + + CSingleDocTemplate* pDocTemplate; + pDocTemplate = new CSingleDocTemplate( + IDR_MAINFRAME, + RUNTIME_CLASS(CTstCBotDoc), + RUNTIME_CLASS(CMainFrame), // main SDI frame window + RUNTIME_CLASS(CTstCBotView)); + AddDocTemplate(pDocTemplate); + + // Parse command line for standard shell commands, DDE, file open + CCommandLineInfo cmdInfo; + ParseCommandLine(cmdInfo); + + // Dispatch commands specified on the command line + if (!ProcessShellCommand(cmdInfo)) + return FALSE; + + // The one and only window has been initialized, so show and update it. + m_pMainWnd->ShowWindow(SW_SHOW); + m_pMainWnd->UpdateWindow(); + + + +/////////////////////////////////// +// défini la fonction "show()" +// -------------------------------- + + CBotProgram::AddFunction("show", rShow, cShow); + CBotProgram::AddFunction("err", rShow, cErr); + CBotProgram::AddFunction("print", rPrint, cPrint); + CBotProgram::AddFunction("println", rPrintLn, cPrint); + + +/////////////////////////////////// +// définie la classe globale CPoint +// -------------------------------- + + CBotClass* m_pClassPoint; + + m_pClassPoint = new CBotClass("CPoint", NULL); + // ajoute le composant ".x" + m_pClassPoint->AddItem("x", CBotTypFloat); + // ajoute le composant ".y" + m_pClassPoint->AddItem("y", CBotTypFloat); + + // ajoute le constructeur pour cette classe + m_pClassPoint->AddFunction("CPoint", rCPoint, cCPoint); + // ajoute la méthode Opposé + m_pClassPoint->AddFunction("Opposé", rOppose, cOppose); + + +////////////////////////////////////////////////////////////////// +// compile un bout de programme pour voir s'il est bien accessible +// depuis un autre "module" + + CBotProgram* p = new CBotProgram; + CBotStringArray Liste; + p->Compile(" public void MonProgram( ) { show (\"mon programme\") ;}", Liste ); + + // l'objet n'est pas détruit et plus référencé + // je sais c'est pas bien + + + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// CAboutDlg dialog used for App About + +class CAboutDlg : public CDialog +{ +public: + CAboutDlg(); + +// Dialog Data + //{{AFX_DATA(CAboutDlg) + enum { IDD = IDD_ABOUTBOX }; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAboutDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + //{{AFX_MSG(CAboutDlg) + // No message handlers + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) +{ + //{{AFX_DATA_INIT(CAboutDlg) + //}}AFX_DATA_INIT +} + +void CAboutDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAboutDlg) + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) + //{{AFX_MSG_MAP(CAboutDlg) + // No message handlers + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +// App command to run the dialog +void CTstCBotApp::OnAppAbout() +{ + CAboutDlg aboutDlg; + aboutDlg.DoModal(); +} + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotApp commands diff --git a/src/CBot/old TstCBot/TstCBot.dsp b/src/CBot/old TstCBot/TstCBot.dsp new file mode 100644 index 00000000..35e5c0b1 --- /dev/null +++ b/src/CBot/old TstCBot/TstCBot.dsp @@ -0,0 +1,180 @@ +# Microsoft Developer Studio Project File - Name="TstCBot" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=TstCBot - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "TstCBot.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "TstCBot.mak" CFG="TstCBot - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "TstCBot - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "TstCBot - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "TstCBot - Win32 Release" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /FR /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" /d "_AFXDLL" +# ADD RSC /l 0x40c /d "NDEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386 +# ADD LINK32 /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "TstCBot - Win32 Debug" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /FR /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" /d "_AFXDLL" +# ADD RSC /l 0x40c /d "_DEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "TstCBot - Win32 Release" +# Name "TstCBot - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\BotConsoleDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\MainFrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"stdafx.h" +# End Source File +# Begin Source File + +SOURCE=.\TstCBot.cpp +# End Source File +# Begin Source File + +SOURCE=.\TstCBot.rc + +!IF "$(CFG)" == "TstCBot - Win32 Release" + +!ELSEIF "$(CFG)" == "TstCBot - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\TstCBotDoc.cpp +# End Source File +# Begin Source File + +SOURCE=.\TstCBotView.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\BotConsoleDlg.h +# End Source File +# Begin Source File + +SOURCE=.\MainFrm.h +# End Source File +# Begin Source File + +SOURCE=.\Resource.h +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# Begin Source File + +SOURCE=.\TstCBot.h +# End Source File +# Begin Source File + +SOURCE=.\TstCBotDoc.h +# End Source File +# Begin Source File + +SOURCE=.\TstCBotView.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\res\TstCBot.ico +# End Source File +# Begin Source File + +SOURCE=.\res\TstCBot.rc2 +# End Source File +# Begin Source File + +SOURCE=.\res\TstCBotDoc.ico +# End Source File +# End Group +# Begin Source File + +SOURCE=.\ReadMe.txt +# End Source File +# End Target +# End Project diff --git a/src/CBot/old TstCBot/TstCBot.h b/src/CBot/old TstCBot/TstCBot.h new file mode 100644 index 00000000..616db43d --- /dev/null +++ b/src/CBot/old TstCBot/TstCBot.h @@ -0,0 +1,62 @@ +// TstCBot.h : main header file for the TSTCBOT application +// + +#if !defined(AFX_TSTCBOT_H__70B37568_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) +#define AFX_TSTCBOT_H__70B37568_5DFD_11D4_A15E_00E0189013DF__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // main symbols +#include "..\CBotDll.h" + + +class CMyThread; + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotApp: +// See TstCBot.cpp for the implementation of this class +// + +class CTstCBotApp : public CWinApp +{ +public: + CTstCBotApp(); + + CMyThread* m_pThread; + CWnd* m_pView; + CEdit* m_pConsole; + CBotStringArray m_Liste; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTstCBotApp) + public: + virtual BOOL InitInstance(); + //}}AFX_VIRTUAL + +// Implementation + + //{{AFX_MSG(CTstCBotApp) + afx_msg void OnAppAbout(); + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TSTCBOT_H__70B37568_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) + +#define WM_STARTPROG WM_APP + 0 +#define WM_ENDPROG WM_APP + 1 diff --git a/src/CBot/old TstCBot/TstCBot.rc b/src/CBot/old TstCBot/TstCBot.rc new file mode 100644 index 00000000..9e91c761 --- /dev/null +++ b/src/CBot/old TstCBot/TstCBot.rc @@ -0,0 +1,471 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 12, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif\r\n" + "#include ""res\\TstCBot.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""l.fra\\afxres.rc"" // Standard components\r\n" + "#endif\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "res\\TstCBot.ico" +IDR_TSTCBOTYPE ICON DISCARDABLE "res\\TstCBotDoc.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINFRAME MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&Fichier" + BEGIN + MENUITEM "&Nouveau\tCtrl+N", ID_FILE_NEW + MENUITEM "&Ouvrir...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Enregistrer\tCtrl+S", ID_FILE_SAVE + MENUITEM "En®istrer sous...", ID_FILE_SAVE_AS + MENUITEM SEPARATOR + MENUITEM "Fichier récent", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "&Quitter", ID_APP_EXIT + END + POPUP "&Edition" + BEGIN + MENUITEM "&Annuler\tCtrl+Z", ID_EDIT_UNDO + MENUITEM SEPARATOR + MENUITEM "&Couper\tCtrl+X", ID_EDIT_CUT + MENUITEM "&Copier\tCtrl+C", ID_EDIT_COPY + MENUITEM "C&oller\tCtrl+V", ID_EDIT_PASTE + END + POPUP "&Affichage" + BEGIN + MENUITEM "Barre d'é&tat", ID_VIEW_STATUS_BAR + END + POPUP "&Tests" + BEGIN + MENUITEM "&Compile\tAlt+C", ID_CP1 + MENUITEM "&Execute\tAlt+V", ID_EXE + END + POPUP "&?" + BEGIN + MENUITEM "&A propos de TstCBot...", ID_APP_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "C", ID_CP1, VIRTKEY, ALT, NOINVERT + "N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + "V", ID_EXE, VIRTKEY, ALT, NOINVERT + VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT, NOINVERT + VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT + VK_F5, ID_EXE, VIRTKEY, NOINVERT + VK_F7, ID_CP1, VIRTKEY, NOINVERT + "X", ID_EXE, VIRTKEY, ALT, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 217, 55 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "A propos de TstCBot" +FONT 8, "MS Sans Serif" +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20 + LTEXT "TstCBot version 1.0",IDC_STATIC,40,10,119,8,SS_NOPREFIX + LTEXT "Copyright (C) 1900",IDC_STATIC,40,25,119,8 + DEFPUSHBUTTON "OK",IDOK,178,7,32,14,WS_GROUP +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040C04B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "Application MFC TstCBot\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "TstCBot\0" + VALUE "LegalCopyright", "Copyright (C) 1900\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "TstCBot.EXE\0" + VALUE "ProductName", "Application TstCBot\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Traduction", 0x40c, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 210 + TOPMARGIN, 7 + BOTTOMMARGIN, 48 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDR_MAINFRAME "TstCBot\n\nTstCBo\n\n\nTstCBot.Document\nTstCBo Document" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + AFX_IDS_APP_TITLE "TstCBot" + AFX_IDS_IDLEMESSAGE "Prêt" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_INDICATOR_EXT "EXT" + ID_INDICATOR_CAPS "MAJ" + ID_INDICATOR_NUM "NUM" + ID_INDICATOR_SCRL "DEF" + ID_INDICATOR_OVR "ECR" + ID_INDICATOR_REC "ENR" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_NEW "Crée un nouveau document\nNouveau" + ID_FILE_OPEN "Ouvre un document existant\nOuvrir" + ID_FILE_CLOSE "Ferme le document actif\nFermer" + ID_FILE_SAVE "Enregistre le document actif\nEnregistrer" + ID_FILE_SAVE_AS "Enregistre le document actif sous un nouveau nom\nEnregistrer sous" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_APP_ABOUT "Affiche des informations sur le programme, le numéro de version et le copyright\nA propos de" + ID_APP_EXIT "Ferme l'application ; propose d'enregistrer les documents\nQuitter" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_MRU_FILE1 "Ouvre ce document" + ID_FILE_MRU_FILE2 "Ouvre ce document" + ID_FILE_MRU_FILE3 "Ouvre ce document" + ID_FILE_MRU_FILE4 "Ouvre ce document" + ID_FILE_MRU_FILE5 "Ouvre ce document" + ID_FILE_MRU_FILE6 "Ouvre ce document" + ID_FILE_MRU_FILE7 "Ouvre ce document" + ID_FILE_MRU_FILE8 "Ouvre ce document" + ID_FILE_MRU_FILE9 "Ouvre ce document" + ID_FILE_MRU_FILE10 "Ouvre ce document" + ID_FILE_MRU_FILE11 "Ouvre ce document" + ID_FILE_MRU_FILE12 "Ouvre ce document" + ID_FILE_MRU_FILE13 "Ouvre ce document" + ID_FILE_MRU_FILE14 "Ouvre ce document" + ID_FILE_MRU_FILE15 "Ouvre ce document" + ID_FILE_MRU_FILE16 "Ouvre ce document" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_NEXT_PANE "Passe au volet de fenêtre suivant\nVolet suivant" + ID_PREV_PANE "Revient au volet précédent\nVolet précédent" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_WINDOW_SPLIT "Fractionne la fenêtre active en deux volets\nFractionner" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDIT_CLEAR "Efface la sélection\nEffacer" + ID_EDIT_CLEAR_ALL "Efface tout\nEffacer tout" + ID_EDIT_COPY "Copie la sélection et la place dans le Presse-papiers\nCopier" + ID_EDIT_CUT "Supprime la sélection et la place dans le Presse-papiers\nCopier" + ID_EDIT_FIND "Recherche le texte spécifié\nRechercher" + ID_EDIT_PASTE "Insère le contenu du Presse-papiers\nColler" + ID_EDIT_REPEAT "Répète la dernière action\nRépéter" + ID_EDIT_REPLACE "Remplace le texte spécifique par un texte différent\nRemplacer" + ID_EDIT_SELECT_ALL "Sélectionne le document entier\nSélectionner tout" + ID_EDIT_UNDO "Annule la dernière action\nAnnuler" + ID_EDIT_REDO "Rétablit l'action précédemment annulée\nRétablir" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_STATUS_BAR "Affiche ou masque la barre d'état\nBarre d'état" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCSIZE "Change la taille de la fenêtre" + AFX_IDS_SCMOVE "Change la position de la fenêtre" + AFX_IDS_SCMINIMIZE "Réduit la fenêtre en icône" + AFX_IDS_SCMAXIMIZE "Agrandit la fenêtre au format de l'écran" + AFX_IDS_SCNEXTWINDOW "Passe à la fenêtre de document suivante" + AFX_IDS_SCPREVWINDOW "Passe à la fenêtre de document précédente" + AFX_IDS_SCCLOSE "Ferme la fenêtre active et propose l'enregistrement des documents" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCRESTORE "Restaure la fenêtre à sa taille d'origine" + AFX_IDS_SCTASKLIST "Active la liste des tâches" +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_TYPENAMES "les différents types" + 1001 "Byte" + 1002 "Short" + 1003 "Char" + 1004 "Int" + 1005 "Long" + 1006 "Real" + 1007 "Double" +END + +STRINGTABLE DISCARDABLE +BEGIN + 1008 "Boolean" + 1009 "Class" + 1010 "String" +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_OPENPAR "Il manque une parenthèse ouvrante." + TX_CLOSEPAR "Il manque une parenthèse fermante." + TX_NOTBOOL "L'expression doit être un boolean." + TX_UNDEFVAR "Variable non déclarée." + TX_BADLEFT "Assignation impossible." + TX_ENDOF "Instruction non terminée." + TX_OUTCASE "Instruction ""case"" hors d'un bloc ""switch""." + TX_NOTERM "Instructions après la fin." +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_CLOSEBLK "Il manque la fin du bloc." + TX_ELSEWITHOUTIF "Instruction ""else"" sans ""if"" correspondant." + TX_OPENBLK "Début d'un bloc attendu." + TX_BADTYPE "Mauvais type de résultat pour l'assignation." + TX_REDEFVAR "Redéfinition d'une variable." + TX_BAD2TYPE "Les deux opérandes ne sont pas de types compatibles." + TX_UNDEFCALL "Routine inconnue." + TX_MISDOTS "Séparateur "" : "" attendu." + TX_WHILE "Manque le mot ""while""." + TX_BREAK "Instruction ""break"" en dehors d'une boucle." + TX_LABEL "Un label ne peut se placer que devant un ""for"", un ""while"", un ""do"" ou un ""switch""." + TX_NOLABEL "Cette étiquette n'existe pas" + TX_NOCASE "Manque une instruction ""case""." + TX_BADNUM "Un nombre est attendu." + TX_VOID "Paramètre void." + TX_NOTYP "Déclaration de type attendu" +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_DIVZERO "Division par zéro." + TX_NOTINIT "Variable non initialisée." + TX_BADTHROW "Valeur négative refusée pour ""throw""." + TX_NORETVAL "La fonction n'a pas retourné de résultat" + TX_NORUN "Pas de fonction en exécution" + TX_NOCALL "Appel d'une fonction inexistante" +END + +STRINGTABLE DISCARDABLE +BEGIN + TX_NOVAR "Nom d'une variable attendu" + TX_NOFONC "Nom de la fonction attendu." + TX_OVERPARAM "Trop de paramètres" + TX_REDEF "Cette fonction existe déjà." + TX_LOWPARAM "Pas assez de paramètres" + TX_BADPARAM "Aucune fonction de ce nom n'accepte ce(s) type(s) de paramètre(s)" + TX_NUMPARAM "Aucune fonction de ce nom n'accepte ce nombre de paramètres" + TX_NOITEM "Cet élément n'exite pas dans cette classe." + TX_DOT "L'objet n'est pas une instance d'une classe." + TX_NOCONST "Il n'y a pas de constructeur approprié." + TX_REDEFCLASS "Cette classe existe déjà." +END + +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// French (Switzerland) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRS) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH_SWISS +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONSOLE DIALOG DISCARDABLE 0, 0, 401, 210 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "CBot Console" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Commande :",IDC_STATIC,7,177,40,8 + EDITTEXT IDC_EDIT2,7,189,329,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "Exécute",IDOK,344,189,50,14 + EDITTEXT IDC_EDIT1,7,7,387,167,ES_MULTILINE | ES_READONLY | + ES_WANTRETURN | WS_VSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_CONSOLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 394 + TOPMARGIN, 7 + BOTTOMMARGIN, 203 + END +END +#endif // APSTUDIO_INVOKED + +#endif // French (Switzerland) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE 12, 1 +#pragma code_page(1252) +#endif +#include "res\TstCBot.rc2" // non-Microsoft Visual C++ edited resources +#include "l.fra\afxres.rc" // Standard components +#endif +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/CBot/old TstCBot/TstCBotDoc.cpp b/src/CBot/old TstCBot/TstCBotDoc.cpp new file mode 100644 index 00000000..7d7e2ef3 --- /dev/null +++ b/src/CBot/old TstCBot/TstCBotDoc.cpp @@ -0,0 +1,83 @@ +// TstCBotDoc.cpp : implementation of the CTstCBotDoc class +// + +#include "stdafx.h" +#include "TstCBot.h" + +#include "TstCBotDoc.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotDoc + +IMPLEMENT_DYNCREATE(CTstCBotDoc, CDocument) + +BEGIN_MESSAGE_MAP(CTstCBotDoc, CDocument) + //{{AFX_MSG_MAP(CTstCBotDoc) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotDoc construction/destruction + +CTstCBotDoc::CTstCBotDoc() +{ + // TODO: add one-time construction code here + +} + +CTstCBotDoc::~CTstCBotDoc() +{ +} + +BOOL CTstCBotDoc::OnNewDocument() +{ + if (!CDocument::OnNewDocument()) + return FALSE; + + // TODO: add reinitialization code here + // (SDI documents will reuse this document) + + return TRUE; +} + + + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotDoc serialization + +void CTstCBotDoc::Serialize(CArchive& ar) +{ + if (ar.IsStoring()) + { + // TODO: add storing code here + } + else + { + // TODO: add loading code here + } +} + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotDoc diagnostics + +#ifdef _DEBUG +void CTstCBotDoc::AssertValid() const +{ + CDocument::AssertValid(); +} + +void CTstCBotDoc::Dump(CDumpContext& dc) const +{ + CDocument::Dump(dc); +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotDoc commands + diff --git a/src/CBot/old TstCBot/TstCBotDoc.h b/src/CBot/old TstCBot/TstCBotDoc.h new file mode 100644 index 00000000..ae1d0f78 --- /dev/null +++ b/src/CBot/old TstCBot/TstCBotDoc.h @@ -0,0 +1,55 @@ +// TstCBotDoc.h : interface of the CTstCBotDoc class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_TSTCBOTDOC_H__70B3756E_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) +#define AFX_TSTCBOTDOC_H__70B3756E_5DFD_11D4_A15E_00E0189013DF__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + + +class CTstCBotDoc : public CDocument +{ +protected: // create from serialization only + CTstCBotDoc(); + DECLARE_DYNCREATE(CTstCBotDoc) + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTstCBotDoc) + public: + virtual BOOL OnNewDocument(); + virtual void Serialize(CArchive& ar); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CTstCBotDoc(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + +// Generated message map functions +protected: + //{{AFX_MSG(CTstCBotDoc) + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TSTCBOTDOC_H__70B3756E_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) diff --git a/src/CBot/old TstCBot/TstCBotView.cpp b/src/CBot/old TstCBot/TstCBotView.cpp new file mode 100644 index 00000000..3ee9094b --- /dev/null +++ b/src/CBot/old TstCBot/TstCBotView.cpp @@ -0,0 +1,291 @@ +// TstCBotView.cpp : implementation of the CTstCBotView class +// + +#include "stdafx.h" +#include "TstCBot.h" + +#include "TstCBotDoc.h" +#include "TstCBotView.h" +#include "BotConsoleDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotView + +IMPLEMENT_DYNCREATE(CTstCBotView, CView) + +BEGIN_MESSAGE_MAP(CTstCBotView, CView) + //{{AFX_MSG_MAP(CTstCBotView) + ON_WM_SIZE() + ON_COMMAND(ID_CP1, OnCp1) + ON_COMMAND(ID_EXE, OnExe) + ON_COMMAND(ID_FILE_SAVE, OnFileSave) + ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotView construction/destruction + +CTstCBotView::CTstCBotView() +{ + // TODO: add construction code here + m_pEdit = NULL; + m_pProg = NULL; +} + +CTstCBotView::~CTstCBotView() +{ +} + +BOOL CTstCBotView::PreCreateWindow(CREATESTRUCT& cs) +{ + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + return CView::PreCreateWindow(cs); +} + +void CTstCBotView::OnActivateView( BOOL bActivate, CView* pActivateView, CView* pDeactiveView ) +{ + if ( m_pEdit == NULL) + { + m_pEdit = new CEdit(); + CRect rect; + GetClientRect( rect ); + + m_pEdit->Create( WS_VISIBLE|WS_BORDER|WS_TABSTOP|ES_MULTILINE|ES_WANTRETURN|ES_NOHIDESEL|ES_AUTOVSCROLL, + rect, this, IDC_EDIT1 ); + m_pEdit->SetTabStops(12); + LoadEdition("CBotTest.txt"); + m_pEdit->SetFocus(); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotView drawing + +void CTstCBotView::OnDraw(CDC* pDC) +{ + CTstCBotDoc* pDoc = GetDocument(); + ASSERT_VALID(pDoc); + + // TODO: add draw code for native data here +} + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotView diagnostics + +#ifdef _DEBUG +void CTstCBotView::AssertValid() const +{ + CView::AssertValid(); +} + +void CTstCBotView::Dump(CDumpContext& dc) const +{ + CView::Dump(dc); +} + +CTstCBotDoc* CTstCBotView::GetDocument() // non-debug version is inline +{ + ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTstCBotDoc))); + return (CTstCBotDoc*)m_pDocument; +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CTstCBotView message handlers + +void CTstCBotView::OnSize(UINT nType, int cx, int cy) +{ + CView::OnSize(nType, cx, cy); + + if ( m_pEdit != NULL ) + { + CRect rect; + GetClientRect( rect ); + m_pEdit->MoveWindow( rect ); + m_pEdit->SetFocus(); + } +} + +void CTstCBotView::SaveEdition(const char* filename) +{ + CString program; + + m_pEdit->GetWindowText(program); + + FILE* pf = fopen(filename, "wb"); + if (pf==NULL) return; + + fputs (program, pf); + fclose(pf); +} + +void CTstCBotView::LoadEdition(const char* filename) +{ + CString program("{ int x = 10000; while (x > 0) x = x-1; }"); + + FILE* pf = fopen(filename, "r"); + if (pf!=NULL) + { + char buffer[10000]; + program.Empty(); + + while (NULL != fgets (buffer, 100000, pf)) + { + program += buffer; + program = program.Left(program.GetLength()-1) + "\r\n"; + } + + fclose(pf); + } + + m_pEdit->SetWindowText(program); +} + + + +// compile le programme +#include + +void CTstCBotView::OnCp1() +{ + CString program; + + SaveEdition("CBotTest.txt"); + + m_pEdit->GetWindowText(program); + + CString TextError; + int code, start, end; + + if ( m_pProg == NULL ) m_pProg = new CBotProgram(); + + CTstCBotApp* pApp = (CTstCBotApp*)AfxGetApp(); + + if (m_pProg->Compile(program, pApp->m_Liste)) + { + CString done = "Compilation sans erreur.\nLes fonctions suivantes sont externes:\n"; + + for ( int i = 0; i < pApp->m_Liste.RetSize(); i++) + { + done += CString(pApp->m_Liste[i]) + "\n"; + } + + AfxMessageBox( done ); + } + else + { + m_pProg->GetError(code, start, end); + delete m_pProg; + m_pProg = NULL; + + m_pEdit->SetSel( start, end ); + m_pEdit->SetFocus(); // met en évidence la partie avec problème + + TextError.LoadString( code ); + if (TextError.IsEmpty()) + { + char buf[100]; + sprintf(buf, "Erreur numéro %d.", code); + TextError = buf; + } + AfxMessageBox( TextError ); + } + + m_pEdit->SetFocus(); +} + + +////////////////////////////////////////////////////// + + +void CTstCBotView::OnExe() +{ + CTstCBotApp* pApp = (CTstCBotApp*)AfxGetApp(); + + if( m_pProg == NULL) + { + AfxMessageBox("Pas de programme compilé !"); + return; + } + + if( pApp->m_Liste.RetSize() == 0 ) + { + AfxMessageBox("Aucune fonction marquée \"extern\" !"); + return; + } + + + + CBotConsoleDlg dlg; + dlg.DoModal(); // dialogue pour faire la console + + if ( dlg.m_code>0 ) + { + CString TextError; + + m_pEdit->SetSel( dlg.m_start, dlg.m_end ); + m_pEdit->SetFocus(); // met en évidence la partie avec problème + + TextError.LoadString( dlg.m_code ); + if (TextError.IsEmpty()) + { + char buf[100]; + sprintf(buf, "Erreur numéro %d.", dlg.m_code); + TextError = buf; + } +// AfxMessageBox( TextError ); + } + + m_pEdit->SetFocus(); + + return; +} + + + +void CTstCBotView::OnFileSave() +{ + // TODO: Add your command handler code here + SaveEdition("CBotTest.txt"); +} + +void CTstCBotView::OnFileSaveAs() +{ + CFileDialog *pDlg; + CString s; + + pDlg = new CFileDialog(FALSE, "TXT", NULL, + OFN_OVERWRITEPROMPT|OFN_HIDEREADONLY, + "cboxtest|*.txt", this); + if ( pDlg == NULL ) return; + + if ( pDlg->DoModal() == IDOK ) // choix du fichier ... + { + SaveEdition(pDlg->GetPathName()); + } + + delete pDlg; +} + +#if 0 +void test() +{ + int y,z; + + for (;;); + for (x = 0; y = 1; z = 3) int q = 6; + for (int x = 0; int y = 1; int z = 3) int q = 6; + // pour voir +} +#endif + diff --git a/src/CBot/old TstCBot/TstCBotView.h b/src/CBot/old TstCBot/TstCBotView.h new file mode 100644 index 00000000..d5aede58 --- /dev/null +++ b/src/CBot/old TstCBot/TstCBotView.h @@ -0,0 +1,81 @@ +// TstCBotView.h : interface of the CTstCBotView class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_TSTCBOTVIEW_H__70B37570_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) +#define AFX_TSTCBOTVIEW_H__70B37570_5DFD_11D4_A15E_00E0189013DF__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + + +class CBotProgram; +class CBotClass; + + +class CTstCBotView : public CView +{ +protected: // create from serialization only + CTstCBotView(); + DECLARE_DYNCREATE(CTstCBotView) + + CEdit* m_pEdit; // texte en édition + CWnd* m_pWnd; + CBotProgram* m_pProg; // programme compilé + +// Attributes +public: + CTstCBotDoc* GetDocument(); + +// Operations +public: + void LoadEdition(const char* name); + void SaveEdition(const char* name); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTstCBotView) + public: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + virtual void OnActivateView( BOOL bActivate, CView* pActivateView, CView* pDeactiveView ); + protected: + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CTstCBotView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + +// Generated message map functions +protected: + //{{AFX_MSG(CTstCBotView) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnCp1(); + afx_msg void OnExe(); + afx_msg void OnFileSave(); + afx_msg void OnFileSaveAs(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#ifndef _DEBUG // debug version in TstCBotView.cpp +inline CTstCBotDoc* CTstCBotView::GetDocument() + { return (CTstCBotDoc*)m_pDocument; } +#endif + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TSTCBOTVIEW_H__70B37570_5DFD_11D4_A15E_00E0189013DF__INCLUDED_) + + + diff --git a/src/CBot/old TstCBot/res/TstCBot.ico b/src/CBot/old TstCBot/res/TstCBot.ico new file mode 100644 index 0000000000000000000000000000000000000000..7eef0bcbe6580a6f464d688906172c2d9de44262 GIT binary patch literal 1078 zcmc&zF>b>!3}jLb9s)T}@Kod(893@u8ajANzT`op9^o+)S?=nU(FD@%0s)Sg^oyC8{H z9myetc;MEP)59v(LMa~xK8Yu^jIR*H22uCFiq5%C{s7(PJi>o15i^bmX4(vPxWAio z9ryY#AU_jfnd047-@`)XzL?%iS$gQyFP{44kS9X)fN{{QoL~hO-&=q&20Zr*cxFAt PkaNE{wR~2C$NfnjhSXWT literal 0 HcmV?d00001 diff --git a/src/CBot/old TstCBot/res/TstCBot.rc2 b/src/CBot/old TstCBot/res/TstCBot.rc2 new file mode 100644 index 00000000..21862724 --- /dev/null +++ b/src/CBot/old TstCBot/res/TstCBot.rc2 @@ -0,0 +1,13 @@ +// +// TSTCBOT.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/src/CBot/old TstCBot/res/TstCBotDoc.ico b/src/CBot/old TstCBot/res/TstCBotDoc.ico new file mode 100644 index 0000000000000000000000000000000000000000..2a1f1ae6ef15e51df8c39bc028bbfb2171822ba5 GIT binary patch literal 1078 zcmcJNF;c@Y5JlGsgIpoERJdY%i*S@2!JS&si6f-)RXoFGDAfg5;z_dQBoi_)1DpI^ z{oS?KlD%25H@>BZ{KJV|_dD9_G1MV<{5a&-}7^W%4AM)k- zx&P*V(j}a@*Y~UhksXTAK!NRyiYN-8NMyKz<)2v9@tUao7A!g+SzkAcsHvdq6!0vQ z#-rA6>0BAr)4*u6Y57EdkamnXf Uh-a7VEuQ2KJb_2>o71HC3-%7w@Bjb+ literal 0 HcmV?d00001 diff --git a/src/CBot/old TstCBot/test complet 1.txt b/src/CBot/old TstCBot/test complet 1.txt new file mode 100644 index 00000000..0fd4fa53 --- /dev/null +++ b/src/CBot/old TstCBot/test complet 1.txt @@ -0,0 +1,213 @@ +// test de l'interpréteur CBot, (c) D. Dumoulin 2000 + +Int Somme ( Int x, Int y ) +{ + return x + y; +} + +Real Somme ( Real x, Real y ) +{ + return x + y; +} + +void A_Faire() +{ + CPoint position; // utilise une classe externe + position.x = 123.5; + position.y = -45.1; + + show ( position ); +} + +/* Les nouveautés sont les suivantes + __________________________________________________ + + On peut définir des fonctions, avec la syntaxe habituelle au C + void MaFonction( Int x, Real y ) { ... } + + Les caractéristiques sont pour l'instant les suivantes: + + - ce programme TstCBot exécute la dernière fonction définie + + - on peut définir deux fonctions du même nom, + si la liste de paramètres est différente. + Par exemple + Int Somme( Int x, Int y ) + Real Somme( Real x, Real y ); + Note: si la seconde n'existait pas, Somme ( 1.3, 4.8 ) + serait fait sur les nombres entier 1 + 4 + La priorité est donnée à la routine qui ne pert pas + de bits dans la conversion des paramètres. + + - il n'y a pas d'erreur de compilation si une routine + ne retourne pas de valeur alors qu'elle devrait, + par contre il y a une erreur "correcte" à l'exécution + + - il est possible d'utiliser une fonction qui est définie + plus bas dans le programme. + __________________________________________________ + + Tous les blocs d'instructions existent maintenant, à savoir + + label : + while (condition) { instructions; break label; continue label; } + + label : + do { instructions; break label; continue label; } while (condition) + + label: + for (initial; condition; incrément) { instructions; break; continue } + + switch ( valeur ) { case 1: instructions; case 2: break ; } + + try {instructions; throw exception; } catch (exception) {instructions;} + catch (testlogique) {instructions;} + finally {instructions;} + // le bloc finally est exécuter dans tous les cas + // qu'il y ait eu exception ou non, et aussi en cas de break, continue ou return + __________________________________________________ + + Les "exceptions" sont juste des numéros (31 bits) + 6000 = division par zéro + 6001 = variable non initialisée + 6002 = valeur négative pour un throw + 6003 = la fonction n'a pas retourné de valeur + + les autres numéros sont à disposition + (COLOBOT aura surement des numéros d'exception propre) + l'association d'un mot clef pour ces exceptions est à venir. + __________________________________________________ + + L'interpréteur a été un peu optimiser, une boucle de un millon de décrément + ne prend plus que +*/ + +void Test () +{ // début du bloc d'instructions + + Int z = 1000000; + while ( z>0 ) z--; + + return; + { + // test la préséance pour les assignations + Int a = 9; + a += (a = 3); + if ( a != 12 ) 1/0; // le résultat correct est 12 + + Int b = 9; + b = b + (b = 3); + if (b != 12) 1/0; // même chose + + // la fonction show est une fonction externe + // définie par TstCBot + // elle peut prendre un nombre quelconque de paramètres + show ( a, b ); + } + + { + // petit test sur les chaînes + String x = "ch." ; + String y ; + x += y = x + " de la brume."; + + // concaténation de chaînes, accepte des autres types + String s = 1 + 2 + " test " + 3 + 4 ; + + show( x, y, s ); + + // les tests sur les chaînes ne sont pas standard en Java + // mais c'est si pratique : + + if ( s != "3 test 34" ) 1/0; // le résultat correct est "3 test 34" + // car 1+2 est évalué en premier entre 2 nombres + // et ensuite on additionne des chaînes "3" "4" + } + + { + // teste toutes les opérations avec les entiers (32 bits) + Int a = 4; + Int b = 4; + + Int c = a++ * --b; // post incrément, pré décrément + if ( c != 12 ) 1/0; + + c = ++a * b--; // pré incrément, post décrément + if ( c!=18 ) 1/0; + + a = a+b-a*b/a%3; // 6 + 2 - ( 6 * 2 / 6 % 3 ) -> 6 + if ( a != 6 ) 1/0; + + a += 2; a-=1; a*=3; a/=4; a%=3; // (6+2 -1) *3 /4 modulo 3 = 21 / 4 modulo 3 = 2 + if ( a!= 2) 0/0; + + if (-5 << 3 != -40) 0/0; // shift à gauche + if ( -5 >> 1 != -3) 0/0; // shift arithmétique à droite 11111011 -> 11111101 = -3 + if ( -5 >>> 1 != 0x3ffffffd) 0/0; // shift non signé à droite + + a = -10; // fait la même chose en assignation + a <<= 1; // -20 + a >>= 2; // -5 + a >>>= 1; // pert le signe + if ( a != 0x3ffffffd) 0/0; // + + Int x = 5/3; // division d'entiers + if ( x != 1 ) 0/0; + Int xx = 5.0/3.0; // division de réels, assigné à un entier + if ( xx != 1 ) 0/0; + + Int y = 0xF0035678; + if ( ~y != 0x0FFCA987 ) 0/0; // NOT bit à bit + if ( (0x3456 ^ 0x54f0) != 0x60A6) // XOR bit à bit + 0/0; + if ( (0x23 | 0x83) != 0xA3 ) 0/0; // OR bit à bit + if ( (0x23 & 0x83) != 0x03 ) 0/0; // AND bit à bit + + Int z = 0x0123; + z |= 0x8010; if ( z != 0x8133) 0/0; + z &= 0xF018; if ( z != 0x8010) 0/0; + z ^= 0xFF17; if ( z != 0x7F07) 0/0; + } + + { + // test pour les booléens + Boolean a, b= true, c = false; + a = b | c & b; + if ( a != b ) 0/0; + if ( !a ) 0/0; + if ( b ^ a ) 0/0; // XOR + if ( true || 0/0<1 ) {}; + if ( false && 0/0<1) {}; + // a ? "vrai" : "faux"; + } + + { + // petit test sur les nombres réels + Real x = 1. / 3, y = 0; + + if ( 3 * x != 1 ) x = x / y; // provoque une division par zéro + else y = 1.123; + } + + + // test de durée + // attention, le programme de test ne stoppe qu'à la fin d'exécution + // bien que la boucle est interrompue plusieures fois + + // la boucle est plus rapide si elle est au début du programme ! + { + Int z = 10000; + while ( z > 0 ) z = z - 1; + } + +} + +void t() +{ + A_Faire(); + + show ( Somme ( 1, 2 ) ); + show ( Somme ( 1., 2 ) ); + show ( Somme ( 4.5, 2.7 ) ); +} + diff --git a/src/CBot/old TstCBot/x.txt b/src/CBot/old TstCBot/x.txt new file mode 100644 index 00000000..95856e03 --- /dev/null +++ b/src/CBot/old TstCBot/x.txt @@ -0,0 +1,43 @@ +// test de l'interpréteur CBot, (c) D. Dumoulin 2000 + +// pour l'instant, seule les primitives suivantes sont implémentées + +// { ... ; ... ; ... } un bloc d'instructions +// int x, y = 12, z; // déclaration de nombre entier +// float a, b= 2/3, c=b+1; // déclaration de nombres réels +// boolean tst = true; // déclaration d'un booléen +// String x = "hello"; // déclaration d'une chaînes + +// z = x = x * y / ( z + 1 - x ); // assignation en chaîne et les 4 opérations + +// while ( x >= 0 ) x = x - 1; // boucle while, et test > >= < <= == != +// if ( x < y ) x = x + 1; // test si +// else y = y + 1; // sinon + +/* et les opérations suivantes: + + plus unaire x = +y; + - moins unaire x = -y; + + || OU logique + && ET logique + ! NOT logique + | OU bit à bit + & ET bit à bit + ^ XOR bit à bit + ~ NON bit à bit + +// les commentaires sont acceptés +/* y compris les commentaires + sur plusieures lignes */ + + +{ +String str ; + +str = "abc" ; + +show (str) ; + +show( str = str + "+++" , ) ; + +} diff --git a/src/CBot/resource.h b/src/CBot/resource.h new file mode 100644 index 00000000..659ee695 --- /dev/null +++ b/src/CBot/resource.h @@ -0,0 +1,166 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by CBot.rc +// +#define ID_KEYWORDS 2000 +#define ID_IF 2000 +#define ID_ELSE 2001 +#define ID_WHILE 2002 +#define ID_DO 2003 +#define ID_FOR 2004 +#define ID_BREAK 2005 +#define ID_CONTINUE 2006 +#define ID_SWITCH 2007 +#define ID_CASE 2008 +#define ID_DEFAULT 2009 +#define ID_TRY 2010 +#define ID_THROW 2011 +#define ID_CATCH 2012 +#define ID_FINALLY 2013 +#define ID_TXT_AND 2014 +#define ID_TXT_OR 2015 +#define ID_TXT_NOT 2016 +#define ID_RETURN 2017 +#define ID_CLASS 2018 +#define ID_EXTENDS 2019 +#define ID_SYNCHO 2020 +#define ID_NEW 2021 +#define ID_PUBLIC 2022 +#define ID_EXTERN 2023 +#define ID_FINAL 2024 +#define ID_STATIC 2025 +#define ID_PROTECTED 2026 +#define ID_PRIVATE 2027 +#define ID_REPEAT 2028 +#define ID_DEBUGDD 2099 +#define ID_INT 2100 +#define ID_FLOAT 2101 +#define ID_BOOLEAN 2102 +#define ID_STRING 2103 +#define ID_VOID 2104 +#define ID_BOOL 2105 +#define ID_TRUE 2200 +#define ID_FALSE 2201 +#define ID_NULL 2202 +#define ID_NAN 2203 +#define ID_OPENPAR 2300 +#define ID_CLOSEPAR 2301 +#define ID_OPBLK 2302 +#define ID_CLBLK 2303 +#define ID_SEP 2304 +#define ID_COMMA 2305 +#define ID_DOTS 2306 +#define ID_DOT 2307 +#define ID_OPBRK 2308 +#define ID_CLBRK 2309 +#define ID_DBLDOTS 2310 +#define ID_LOGIC 2311 +#define ID_ADD 2312 +#define ID_SUB 2313 +#define ID_MUL 2314 +#define ID_DIV 2315 +#define ID_ASS 2316 +#define ID_ASSADD 2317 +#define ID_ASSSUB 2318 +#define ID_ASSMUL 2319 +#define ID_ASSDIV 2320 +#define ID_ASSOR 2321 +#define ID_ASSAND 2322 +#define ID_ASSXOR 2323 +#define ID_ASSSL 2324 +#define ID_ASSSR 2325 +#define ID_ASSASR 2326 +#define ID_SL 2327 +#define ID_SR 2328 +#define ID_ASR 2329 +#define ID_INC 2330 +#define ID_DEC 2331 +#define ID_LO 2332 +#define ID_HI 2333 +#define ID_LS 2334 +#define ID_HS 2335 +#define ID_EQ 2336 +#define ID_NE 2337 +#define ID_AND 2338 +#define ID_XOR 2339 +#define ID_OR 2340 +#define ID_LOG_AND 2341 +#define ID_LOG_OR 2342 +#define ID_LOG_NOT 2343 +#define ID_NOT 2344 +#define ID_MODULO 2345 +#define ID_POWER 2346 +#define ID_ASSMODULO 2347 +#define TX_UNDEF 4000 +#define TX_NAN 4001 +#define TX_OPENPAR 5000 +#define TX_CLOSEPAR 5001 +#define TX_NOTBOOL 5002 +#define TX_UNDEFVAR 5003 +#define TX_BADLEFT 5004 +#define TX_ENDOF 5005 +#define TX_OUTCASE 5006 +#define TX_NOTERM 5007 +#define TX_CLOSEBLK 5008 +#define TX_ELSEWITHOUTIF 5009 +#define TX_OPENBLK 5010 +#define TX_BADTYPE 5011 +#define TX_REDEFVAR 5012 +#define TX_BAD2TYPE 5013 +#define TX_UNDEFCALL 5014 +#define TX_MISDOTS 5015 +#define TX_WHILE 5016 +#define TX_BREAK 5017 +#define TX_LABEL 5018 +#define TX_NOLABEL 5019 +#define TX_NOCASE 5020 +#define TX_BADNUM 5021 +#define TX_VOID 5022 +#define TX_NOTYP 5023 +#define TX_NOVAR 5024 +#define TX_NOFONC 5025 +#define TX_OVERPARAM 5026 +#define TX_REDEF 5027 +#define TX_LOWPARAM 5028 +#define TX_BADPARAM 5029 +#define TX_NUMPARAM 5030 +#define TX_NOITEM 5031 +#define TX_DOT 5032 +#define TX_NOCONST 5033 +#define TX_REDEFCLASS 5034 +#define TX_CLBRK 5035 +#define TX_RESERVED 5036 +#define TX_BADNEW 5037 +#define TX_OPBRK 5038 +#define TX_BADSTRING 5039 +#define TX_BADINDEX 5040 +#define TX_PRIVATE 5041 +#define TX_NOPUBLIC 5042 +#define TX_DIVZERO 6000 +#define TX_NOTINIT 6001 +#define TX_BADTHROW 6002 +#define TX_NORETVAL 6003 +#define TX_NORUN 6004 +#define TX_NOCALL 6005 +#define TX_NOCLASS 6006 +#define TX_NULLPT 6007 +#define TX_OPNAN 6008 +#define TX_OUTARRAY 6009 +#define TX_STACKOVER 6010 +#define TX_DELETEDPT 6011 +#define TX_FILEOPEN 6012 +#define TX_NOTOPEN 6013 +#define TX_ERRREAD 6014 +#define TX_ERRWRITE 6015 +#define ID_SUPER 62020 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/ClassFILE.cpp b/src/ClassFILE.cpp new file mode 100644 index 00000000..cdfe26b3 --- /dev/null +++ b/src/ClassFILE.cpp @@ -0,0 +1,413 @@ +// ClassFile.cpp +// +// définition des méthodes pour la classe FILE + + + +// Variables statiques + +static CBotClass* m_pClassFILE; +static CBotProgram* m_pFuncFile; +static int m_CompteurFileOpen = 0; +static char* m_filesDir; + + + +// Prépare un nom de fichier. + +void PrepareFilename(CBotString &filename) +{ + int pos; + + pos = filename.ReverseFind('\\'); + if ( pos > 0 ) + { + filename = filename.Mid(pos+1); // enlève les dossiers + } + + pos = filename.ReverseFind('/'); + if ( pos > 0 ) + { + filename = filename.Mid(pos+1); // aussi ceux avec / + } + + pos = filename.ReverseFind(':'); + if ( pos > 0 ) + { + filename = filename.Mid(pos+1); // enlève aussi la lettre d'unité C: + } + + filename = CBotString(m_filesDir) + CBotString("\\") + filename; +} + + +// constructeur de la classe +// reçois le nom du fichier en paramètre + +// exécution +BOOL rfconstruct (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + CBotString mode; + + // accepte sans paramètre + if ( pVar == NULL ) return TRUE; + + // qui doit être une chaîne de caractères + if ( pVar->GivType() != CBotTypString ) { Exception = CBotErrBadString; return FALSE; } + + CBotString filename = pVar->GivValString(); + PrepareFilename(filename); + + // il peut y avoir un second paramètre + pVar = pVar->GivNext(); + if ( pVar != NULL ) + { + // récupère le mode + mode = pVar->GivValString(); + if ( mode != "r" && mode != "w" ) { Exception = CBotErrBadParam; return FALSE; } + + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) { Exception = CBotErrOverParam; return FALSE; } + } + + // enregistre le nom du fichier + pVar = pThis->GivItem("filename"); + pVar->SetValString(filename); + + if ( ! mode.IsEmpty() ) + { + // ouvre le ficher demandé + FILE* pFile = fopen( filename, mode ); + if ( pFile == NULL ) { Exception = CBotErrFileOpen; return FALSE; } + + m_CompteurFileOpen ++; + + // enregiste le canal du fichier + pVar = pThis->GivItem("handle"); + pVar->SetValInt((long)pFile); + } + + return TRUE; +} + +// compilation +CBotTypResult cfconstruct (CBotVar* pThis, CBotVar* &pVar) +{ + // accepte sans paramètre + if ( pVar == NULL ) return CBotTypResult( 0 ); + + // qui doit être une chaine + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( CBotErrBadString ); + + // il peut y avoir un second paramètre + pVar = pVar->GivNext(); + if ( pVar != NULL ) + { + // qui doit être une chaine + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( CBotErrBadString ); + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( CBotErrOverParam ); + } + + // le résultat est de type void (constructeur) + return CBotTypResult( 0 ); +} + + +// destructeur de la classe + +// exécution +BOOL rfdestruct (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + // pas ouvert ? pas de problème + if ( pVar->GivInit() != IS_DEF) return TRUE; + + FILE* pFile= (FILE*)pVar->GivValInt(); + fclose(pFile); + m_CompteurFileOpen --; + + pVar->SetInit(IS_NAN); + + return TRUE; +} + + +// méthode FILE :: open +// reçois le mode r/w en paramètre + +// exécution +BOOL rfopen (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // il doit y avoir un paramètre + if ( pVar == NULL ) { Exception = CBotErrLowParam; return FALSE; } + + // qui doit être une chaîne de caractères + if ( pVar->GivType() != CBotTypString ) { Exception = CBotErrBadString; return FALSE; } + + // il peut y avoir un second paramètre + if ( pVar->GivNext() != NULL ) + { + // dans ce cas le premier paramètre est le nom du fichier + CBotString filename = pVar->GivValString(); + PrepareFilename(filename); + + // enregistre le nom du fichier + CBotVar* pVar2 = pThis->GivItem("filename"); + pVar2->SetValString(filename); + + // paramètre suivant est le mode + pVar = pVar -> GivNext(); + } + + CBotString mode = pVar->GivValString(); + if ( mode != "r" && mode != "w" ) { Exception = CBotErrBadParam; return FALSE; } + + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) { Exception = CBotErrOverParam; return FALSE; } + + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + // qui doit pas être initialisé + if ( pVar->GivInit() == IS_DEF) { Exception = CBotErrFileOpen; return FALSE; } + + // reprend le nom du fichier + pVar = pThis->GivItem("filename"); + CBotString filename = pVar->GivValString(); + + PrepareFilename(filename); // si le nom a été attribué par h.filename = "..."; + + // ouvre le ficher demandé + FILE* pFile = fopen( filename, mode ); + if ( pFile == NULL ) + { + pResult->SetValInt(FALSE); + return TRUE; + } + + m_CompteurFileOpen ++; + + // enregiste le canal du fichier + pVar = pThis->GivItem("handle"); + pVar->SetValInt((long)pFile); + + pResult->SetValInt(TRUE); + return TRUE; +} + +// compilation +CBotTypResult cfopen (CBotVar* pThis, CBotVar* &pVar) +{ + // il doit y avoir un paramètre + if ( pVar == NULL ) return CBotTypResult( CBotErrLowParam ); + + // qui doit être une chaine + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( CBotErrBadString ); + + // il peut y avoir un second paramètre + pVar = pVar->GivNext(); + if ( pVar != NULL ) + { + // qui doit être une chaine + if ( pVar->GivType() != CBotTypString ) + return CBotTypResult( CBotErrBadString ); + + // pas de 3e paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( CBotErrOverParam ); + } + + // le résultat est de type bool + return CBotTypResult(CBotTypBoolean); +} + + +// méthode FILE :: close + +// exécution +BOOL rfclose (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) return CBotErrOverParam; + + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + if ( pVar->GivInit() != IS_DEF) { Exception = CBotErrNotOpen; return FALSE; } + + FILE* pFile= (FILE*)pVar->GivValInt(); + fclose(pFile); + m_CompteurFileOpen --; + + pVar->SetInit(IS_NAN); + + return TRUE; +} + +// compilation +CBotTypResult cfclose (CBotVar* pThis, CBotVar* &pVar) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) return CBotTypResult( CBotErrOverParam ); + + // la fonction retourne un résultat "void" + return CBotTypResult( 0 ); +} + +// méthode FILE :: writeln + +// exécution +BOOL rfwrite (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // il doit y avoir un paramètre + if ( pVar == NULL ) { Exception = CBotErrLowParam; return FALSE; } + + // qui doit être une chaîne de caractères + if ( pVar->GivType() != CBotTypString ) { Exception = CBotErrBadString; return FALSE; } + + CBotString param = pVar->GivValString(); + + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + if ( pVar->GivInit() != IS_DEF) { Exception = CBotErrNotOpen; return FALSE; } + + FILE* pFile= (FILE*)pVar->GivValInt(); + + int res = fputs(param+"\n", pFile); + + // en cas d'erreur génère une exception + if ( res < 0 ) { Exception = CBotErrWrite; return FALSE; } + + return TRUE; +} + +// compilation +CBotTypResult cfwrite (CBotVar* pThis, CBotVar* &pVar) +{ + // il doit y avoir un paramètre + if ( pVar == NULL ) return CBotTypResult( CBotErrLowParam ); + + // qui doit être une chaîne de caractères + if ( pVar->GivType() != CBotTypString ) return CBotTypResult( CBotErrBadString ); + + // pas d'autre paramètre + if ( pVar->GivNext() != NULL ) return CBotTypResult( CBotErrOverParam ); + + // la fonction retourne un résultat void + return CBotTypResult( 0 ); +} + +// méthode FILE :: readln + +// exécution +BOOL rfread (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) { Exception = CBotErrOverParam; return FALSE; } + + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + if ( pVar->GivInit() != IS_DEF) { Exception = CBotErrNotOpen; return FALSE; } + + FILE* pFile= (FILE*)pVar->GivValInt(); + + char chaine[2000]; + int i; + for ( i = 0 ; i < 2000 ; i++ ) chaine[i] = 0; + + fgets(chaine, 1999, pFile); + + for ( i = 0 ; i < 2000 ; i++ ) if (chaine[i] == '\n') chaine[i] = 0; + + // en cas d'erreur génère une exception + if ( ferror(pFile) ) { Exception = CBotErrRead; return FALSE; } + + pResult->SetValString( chaine ); + + return TRUE; +} + +// compilation +CBotTypResult cfread (CBotVar* pThis, CBotVar* &pVar) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) return CBotTypResult( CBotErrOverParam ); + + // la fonction retourne un résultat "string" + return CBotTypResult( CBotTypString ); +} +// méthode FILE :: readln + + +// exécution +BOOL rfeof (CBotVar* pThis, CBotVar* pVar, CBotVar* pResult, int& Exception) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) { Exception = CBotErrOverParam; return FALSE; } + + // récupère l'élément "handle" + pVar = pThis->GivItem("handle"); + + if ( pVar->GivInit() != IS_DEF) { Exception = CBotErrNotOpen; return FALSE; } + + FILE* pFile= (FILE*)pVar->GivValInt(); + + pResult->SetValInt( feof( pFile ) ); + + return TRUE; +} + +// compilation +CBotTypResult cfeof (CBotVar* pThis, CBotVar* &pVar) +{ + // il ne doit pas y avoir de paramètre + if ( pVar != NULL ) return CBotTypResult( CBotErrOverParam ); + + // la fonction retourne un résultat booleen + return CBotTypResult( CBotTypBoolean ); +} + + + + + +void InitClassFILE() +{ +// crée une classe pour la gestion des fichiers +// l'utilisation en est la suivante: +// file canal( "NomFichier.txt" ) +// canal.open( "r" ); // ouvre en lecture +// s = canal.readln( ); // lit une ligne +// canal.close(); // referme le fichier + + // crée la classe FILE + m_pClassFILE = new CBotClass("file", NULL); + // ajoute le composant ".filename" + m_pClassFILE->AddItem("filename", CBotTypString); + // ajoute le composant ".handle" + m_pClassFILE->AddItem("handle", CBotTypInt, PR_PRIVATE); + + // défini un constructeur et un destructeur + m_pClassFILE->AddFunction("file", rfconstruct, cfconstruct ); + m_pClassFILE->AddFunction("~file", rfdestruct, NULL ); + + // défini les méthodes associées + m_pClassFILE->AddFunction("open", rfopen, cfopen ); + m_pClassFILE->AddFunction("close", rfclose, cfclose ); + m_pClassFILE->AddFunction("writeln", rfwrite, cfwrite ); + m_pClassFILE->AddFunction("readln", rfread, cfread ); + m_pClassFILE->AddFunction("eof", rfeof, cfeof ); + + m_pFuncFile = new CBotProgram( ); + CBotStringArray ListFonctions; + m_pFuncFile->Compile( "public file openfile(string name, string mode) {return new file(name, mode);}", ListFonctions); + m_pFuncFile->SetIdent(-2); // identificateur spécial pour RestoreState dans cette fonction +} + diff --git a/src/Copie de taskgoto.cpp b/src/Copie de taskgoto.cpp new file mode 100644 index 00000000..ddb9a405 --- /dev/null +++ b/src/Copie de taskgoto.cpp @@ -0,0 +1,2136 @@ +// taskgoto.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "terrain.h" +#include "water.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "task.h" +#include "taskgoto.h" + + + +#define BM_DIM_STEP 5.0f + + + + +// Constructeur de l'objet. + +CTaskGoto::CTaskGoto(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); + + m_bmArray = 0; +} + +// Destructeur de l'objet. + +CTaskGoto::~CTaskGoto() +{ + BitmapClose(); +} + + +// Gestion d'un événement. + +BOOL CTaskGoto::EventProcess(const Event &event) +{ + D3DVECTOR pos, goal; + FPOINT rot, repulse; + float a, g, dist, linSpeed, cirSpeed, h, w, factor, dir, floor; + Error ret; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + // Objet momentanément immobile (fourmi sur le dos) ? + if ( m_object->RetFixed() ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + return TRUE; + } + + if ( m_error != ERR_OK ) return FALSE; + + if ( m_bWorm ) + { + WormFrame(event.rTime); + } + + if ( m_phase == TGP_BEAMLEAK ) // fuite ? + { + m_leakTime += event.rTime; + + pos = m_object->RetPosition(0); + + rot.x = m_leakPos.x-pos.x; + rot.y = m_leakPos.z-pos.z; + dist = Length(rot.x, rot.y); + rot.x /= dist; + rot.y /= dist; + + a = m_object->RetAngleY(0); + g = RotateAngle(rot.x, -rot.y); // CW ! + a = Direction(a, g)*1.0f; + cirSpeed = a; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + a = NormAngle(a); + if ( a > PI*0.5f && a < PI*1.5f ) + { + linSpeed = 1.0f; // obstacle derrière -> avance + cirSpeed = -cirSpeed; + } + else + { + linSpeed = -1.0f; // obstacle devant -> recule + } + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + m_physics->SetMotorSpeedX(linSpeed); // avance + return TRUE; + } + + if ( m_phase == TGP_BEAMSEARCH ) // recherche chemin ? + { +//? OutputDebugString("CTaskGoto::EventProcess TGP_BEAMSEARCH\n"); + + pos = m_object->RetPosition(0); + + if ( m_bmFretObject == 0 ) + { + goal = m_goal; + dist = 0.0f; + } + else + { + goal = m_goalObject; + dist = TAKE_DIST; + } + + ret = BeamSearch(pos, goal, dist); + if ( ret == ERR_OK ) + { +#if 1 + D3DVECTOR min, max; + min = pos; + max = m_goal; + if ( min.x > max.x ) Swap(min.x, max.x); + if ( min.z > max.z ) Swap(min.z, max.z); + min.x -= 50.0f; + min.z -= 50.0f; + max.x += 50.0f; + max.z += 50.0f; + BitmapDebug(min, max, m_object->RetPosition(0), m_goal); +#endif + m_phase = TGP_BEAMUP; + m_bmIndex = 0; + m_bmWatchDogPos = m_object->RetPosition(0); + m_bmWatchDogTime = 0.0f; + } + if ( ret == ERR_GOTO_IMPOSSIBLE || ret == ERR_GOTO_ITER ) + { +#if 1 + D3DVECTOR min, max; + min = pos; + max = m_goal; + if ( min.x > max.x ) Swap(min.x, max.x); + if ( min.z > max.z ) Swap(min.z, max.z); + min.x -= 50.0f; + min.z -= 50.0f; + max.x += 50.0f; + max.z += 50.0f; + BitmapDebug(min, max, m_object->RetPosition(0), m_goal); +#endif + m_error = ret; + return FALSE; + } + return TRUE; + } + + if ( m_phase == TGP_BEAMUP ) // décolle ? + { + m_physics->SetMotorSpeedY(1.0f); // monte + return TRUE; + } + + if ( m_phase == TGP_BEAMGOTO ) // goto dot list ? + { + if ( m_physics->RetCollision() ) // collision ? + { + m_physics->SetCollision(FALSE); // y'a plus + } + + pos = m_object->RetPosition(0); + + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) + { + goal = m_bmPoints[m_bmIndex]; + dist = Length2d(pos, goal); + if ( dist != 0.0f ) // anticipe ? + { + linSpeed = m_physics->RetLinMotionX(MO_REASPEED); + linSpeed /= m_physics->RetLinMotionX(MO_ADVSPEED); + goal.x = pos.x + (goal.x-pos.x)*linSpeed*20.0f/dist; + goal.z = pos.z + (goal.z-pos.z)*linSpeed*20.0f/dist; + } + floor = m_terrain->RetFloorLevel(goal, TRUE); + w = m_water->RetLevel(); + if ( floor < w ) floor = w; // jamais en-dessous de l'eau + h = pos.y-floor; // h <- hauteur jusqu'au sol + + linSpeed = 0.0f; + if ( h < m_altitude-1.0f ) + { + linSpeed = 0.2f+((m_altitude-1.0f)-h)*0.1f; // monte + if ( linSpeed > 1.0f ) linSpeed = 1.0f; + } + if ( h > m_altitude+1.0f ) + { + linSpeed = -0.2f; // descend + } + m_physics->SetMotorSpeedY(linSpeed); + } + + dist = Length2d(pos, m_bmWatchDogPos); + if ( dist < 1.0f ) + { + m_bmWatchDogTime += event.rTime; + } + else + { + m_bmWatchDogTime = 0.0f; + m_bmWatchDogPos = pos; + } + + if ( m_bmWatchDogTime >= 1.0f ) // immobile depuis longtemps ? + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + BeamStart(); + return TRUE; + } + + rot.x = m_bmPoints[m_bmIndex].x-pos.x; + rot.y = m_bmPoints[m_bmIndex].z-pos.z; + dist = Length(rot.x, rot.y); + rot.x /= dist; + rot.y /= dist; + + a = m_object->RetAngleY(0); + g = RotateAngle(rot.x, -rot.y); // CW ! + cirSpeed = Direction(a, g)*1.0f; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + if ( m_bmIndex == m_bmTotal ) // dernier point ? + { + dist = Length2d(m_bmPoints[m_bmIndex], pos); + linSpeed = dist/(m_physics->RetLinStopLength()*1.5f); + if ( linSpeed > 1.0f ) linSpeed = 1.0f; + } + else + { + linSpeed = 1.0f; // fonce sans s'arrêter + } + + linSpeed *= 1.0f-(1.0f-0.3f)*Abs(cirSpeed); + + if ( dist < 20.0f && Abs(cirSpeed) >= 0.5f ) + { + linSpeed = 0.0f; // tourne d'abord, puis avance + } + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + m_physics->SetMotorSpeedX(linSpeed); // avance + return TRUE; + } + + if ( m_phase == TGP_BEAMDOWN ) // atterri ? + { + m_physics->SetMotorSpeedY(-0.5f); // tombe + return TRUE; + } + + if ( m_phase == TGP_LAND ) // atterri ? + { + m_physics->SetMotorSpeedY(-0.5f); // tombe + return TRUE; + } + + if ( m_goalMode == TGG_EXPRESS ) + { + if ( m_crashMode == TGC_HALT ) + { + if ( m_physics->RetCollision() ) // collision ? + { + m_physics->SetCollision(FALSE); // y'a plus + m_error = ERR_STOP; + return TRUE; + } + } + + pos = m_object->RetPosition(0); + + if ( m_altitude > 0.0f ) + { + h = m_terrain->RetFloorHeight(pos, TRUE); + linSpeed = 0.0f; + if ( h < m_altitude ) + { + linSpeed = 0.1f; // monte + } + if ( h > m_altitude ) + { + linSpeed = -0.2f; // descend + } + m_physics->SetMotorSpeedY(linSpeed); + } + + rot.x = m_goal.x-pos.x; + rot.y = m_goal.z-pos.z; + a = m_object->RetAngleY(0); + g = RotateAngle(rot.x, -rot.y); // CW ! + cirSpeed = Direction(a, g)*1.0f; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + m_physics->SetMotorSpeedX(1.0f); // avance + return TRUE; + } + + if ( m_phase != TGP_TURN && + m_physics->RetType() == TYPE_FLYING && + m_altitude > 0.0f ) + { + pos = m_object->RetPosition(0); + dist = Length2d(m_goal, pos); + factor = (dist-20.0f)/20.0f; + if ( factor < 0.0f ) factor = 0.0f; + if ( factor > 1.0f ) factor = 1.0f; + + h = m_terrain->RetFloorHeight(m_object->RetPosition(0), TRUE); + linSpeed = 0.0f; + if ( h < (m_altitude-0.5f)*factor && factor == 1.0f ) + { + linSpeed = 0.1f; // monte + } + if ( h > m_altitude*factor ) + { + linSpeed = -0.2f; // descend + } + ComputeFlyingRepulse(dir); + linSpeed += dir*0.2f; + + m_physics->SetMotorSpeedY(linSpeed); + } + + if ( m_phase == TGP_ADVANCE ) // va vers l'objectif ? + { + if ( m_physics->RetCollision() ) // collision ? + { + m_physics->SetCollision(FALSE); // y'a plus + m_time = 0.0f; + m_phase = TGP_CRWAIT; + return TRUE; + } + +#if 0 + pos = m_object->RetPosition(0); + a = m_object->RetAngleY(0); + g = RotateAngle(m_goal.x-pos.x, pos.z-m_goal.z); // CW ! + cirSpeed = Direction(a, g)*1.0f; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + dist = Length2d(m_goal, pos); + linSpeed = dist/(m_physics->RetLinStopLength()*1.5f); + if ( linSpeed > 1.0f ) linSpeed = 1.0f; + + if ( dist < 20.0f && Abs(cirSpeed) >= 0.5f ) + { + linSpeed = 0.0f; // tourne d'abord, puis avance + } +#else + pos = m_object->RetPosition(0); + + rot.x = m_goal.x-pos.x; + rot.y = m_goal.z-pos.z; + dist = Length(rot.x, rot.y); + rot.x /= dist; + rot.y /= dist; + + ComputeRepulse(repulse); + rot.x += repulse.x*2.0f; + rot.y += repulse.y*2.0f; + + a = m_object->RetAngleY(0); + g = RotateAngle(rot.x, -rot.y); // CW ! + cirSpeed = Direction(a, g)*1.0f; +//? if ( m_physics->RetType() == TYPE_FLYING && +//? m_physics->RetLand() ) // volant au sol ? +//? { +//? cirSpeed *= 4.0f; // plus de pèche +//? } + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + dist = Length2d(m_goal, pos); + linSpeed = dist/(m_physics->RetLinStopLength()*1.5f); +//? if ( m_physics->RetType() == TYPE_FLYING && +//? m_physics->RetLand() ) // volant au sol ? +//? { +//? linSpeed *= 8.0f; // plus de pèche +//? } + if ( linSpeed > 1.0f ) linSpeed = 1.0f; + + linSpeed *= 1.0f-(1.0f-0.3f)*Abs(cirSpeed); + + if ( dist < 20.0f && Abs(cirSpeed) >= 0.5f ) + { + linSpeed = 0.0f; // tourne d'abord, puis avance + } +#endif + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + m_physics->SetMotorSpeedX(linSpeed); // avance + } + + if ( m_phase == TGP_TURN || // tourne vers l'objet ? + m_phase == TGP_CRTURN || // tourne après collision ? + m_phase == TGP_CLTURN ) // tourne après collision ? + { + a = m_object->RetAngleY(0); + g = m_angle; + cirSpeed = Direction(a, g)*1.0f; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + } + + if ( m_phase == TGP_CRWAIT || // attend après collision ? + m_phase == TGP_CLWAIT ) // attend après collision ? + { + m_time += event.rTime; + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + } + + if ( m_phase == TGP_CRADVANCE ) // avance après collision ? + { + if ( m_physics->RetCollision() ) // collision ? + { + m_physics->SetCollision(FALSE); // y'a plus + m_time = 0.0f; + m_phase = TGP_CLWAIT; + return TRUE; + } + m_physics->SetMotorSpeedX(0.5f); // avance mollo + } + + if ( m_phase == TGP_CLADVANCE ) // avance après collision ? + { + if ( m_physics->RetCollision() ) // collision ? + { + m_physics->SetCollision(FALSE); // y'a plus + m_time = 0.0f; + m_phase = TGP_CRWAIT; + return TRUE; + } + m_physics->SetMotorSpeedX(0.5f); // avance mollo + } + + if ( m_phase == TGP_MOVE ) // avance finale ? + { + m_bmTimeLimit -= event.rTime; + m_physics->SetMotorSpeedX(1.0f); + } + + return TRUE; +} + + +// Cherche une cible pour le ver. + +CObject* CTaskGoto::WormSearch(D3DVECTOR &impact) +{ + CObject* pObj; + CObject* pBest = 0; + D3DVECTOR iPos, oPos; + ObjectType oType; + float distance, min, radius; + int i; + + iPos = m_object->RetPosition(0); + min = 1000000.0f; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + oType = pObj->RetType(); + if ( oType != OBJECT_MOBILEfa && + oType != OBJECT_MOBILEta && + oType != OBJECT_MOBILEwa && + oType != OBJECT_MOBILEia && + oType != OBJECT_MOBILEfc && + oType != OBJECT_MOBILEtc && + oType != OBJECT_MOBILEwc && + oType != OBJECT_MOBILEic && + oType != OBJECT_MOBILEfi && + oType != OBJECT_MOBILEti && + oType != OBJECT_MOBILEwi && + oType != OBJECT_MOBILEii && + oType != OBJECT_MOBILEfs && + oType != OBJECT_MOBILEts && + oType != OBJECT_MOBILEws && + oType != OBJECT_MOBILEis && + oType != OBJECT_MOBILErt && + oType != OBJECT_MOBILErc && + oType != OBJECT_MOBILErr && + oType != OBJECT_MOBILErs && + oType != OBJECT_MOBILEsa && + oType != OBJECT_MOBILEtg && + oType != OBJECT_MOBILEft && + oType != OBJECT_MOBILEtt && + oType != OBJECT_MOBILEwt && + oType != OBJECT_MOBILEit && + oType != OBJECT_DERRICK && + oType != OBJECT_STATION && + oType != OBJECT_FACTORY && + oType != OBJECT_REPAIR && + oType != OBJECT_CONVERT && + oType != OBJECT_TOWER && + oType != OBJECT_RESEARCH && + oType != OBJECT_RADAR && + oType != OBJECT_INFO && + oType != OBJECT_ENERGY && + oType != OBJECT_LABO && + oType != OBJECT_NUCLEAR && + oType != OBJECT_PARA && + oType != OBJECT_SAFE && + oType != OBJECT_HUSTON ) continue; + + if ( pObj->RetVirusMode() ) continue; // objet infecté ? + + if ( !pObj->GetCrashSphere(0, oPos, radius) ) continue; + distance = Length2d(oPos, iPos); + if ( distance < min ) + { + min = distance; + pBest = pObj; + } + } + if ( pBest == 0 ) return 0; + + impact = pBest->RetPosition(0); + return pBest; +} + +// Contamine les objets proches du ver. + +void CTaskGoto::WormFrame(float rTime) +{ + CObject* pObj; + D3DVECTOR impact, pos; + float dist; + + m_wormLastTime += rTime; + + if ( m_wormLastTime >= 0.5f ) + { + m_wormLastTime = 0.0f; + + pObj = WormSearch(impact); + if ( pObj != 0 ) + { + pos = m_object->RetPosition(0); + dist = Length(pos, impact); + if ( dist <= 15.0f ) + { + pObj->SetVirusMode(TRUE); // paf, infecté ! + } + } + } +} + + + +// Assigne le but à atteindre. +// "dist" est la distance de laquelle il faut s'éloigner pour +// prendre ou déposer un objet. + +Error CTaskGoto::Start(D3DVECTOR goal, float altitude, + TaskGotoGoal goalMode, TaskGotoCrash crashMode) +{ + D3DVECTOR pos; + CObject* target; + ObjectType type; + float dist; + int x, y; + + type = m_object->RetType(); + + if ( goalMode == TGG_DEFAULT ) + { + goalMode = TGG_STOP; + if ( type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_WORM ) + { + goalMode = TGG_EXPRESS; + } + } + + if ( crashMode == TGC_DEFAULT ) + { +//? crashMode = TGC_RIGHTLEFT; + crashMode = TGC_BEAM; + if ( type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_WORM || + type == OBJECT_BEE ) + { + crashMode = TGC_HALT; + } + } + + m_altitude = altitude; + m_goalMode = goalMode; + m_crashMode = crashMode; + m_goalObject = goal; + m_goal = goal; + + m_bTake = FALSE; + m_phase = TGP_ADVANCE; + m_error = ERR_OK; + m_try = 0; + m_bmFretObject = 0; + m_bmFinalMove = 0.0f; + + pos = m_object->RetPosition(0); + dist = Length2d(pos, m_goal); + if ( dist < 10.0f && m_crashMode == TGC_BEAM ) + { + m_crashMode = TGC_RIGHTLEFT; + } + + m_bWorm = FALSE; + if ( type == OBJECT_WORM ) + { + m_bWorm = TRUE; + m_wormLastTime = 0.0f; + } + + m_bApprox = FALSE; + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH || + type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) + { + m_bApprox = TRUE; + } + + if ( !m_bApprox && m_crashMode != TGC_BEAM ) + { + target = SearchTarget(goal, 1.0f); + if ( target != 0 ) + { + m_goal = target->RetPosition(0); + dist = 0.0f; + if ( !AdjustBuilding(m_goal, 1.0f, dist) ) + { + dist = 0.0f; + AdjustTarget(target, m_goal, dist); + } + m_bTake = TRUE; // objet à prendre à l'arrivée (rotation finale) + } + } + + m_lastDistance = 1000.0f; + m_physics->SetCollision(FALSE); + + if ( m_crashMode == TGC_BEAM ) + { + target = SearchTarget(goal, 1.0f); + if ( target != 0 ) + { + m_goal = target->RetPosition(0); + dist = 4.0f; + if ( AdjustBuilding(m_goal, 1.0f, dist) ) + { + m_bmFinalMove = dist; + } + else + { + dist = 4.0f; + if ( AdjustTarget(target, m_goal, dist) ) + { + m_bmFretObject = target; // fret posé au sol + } + else + { + m_bmFinalMove = dist; + } + } + m_bTake = TRUE; // objet à prendre à l'arrivée (rotation finale) + } + + if ( m_physics->RetType() == TYPE_FLYING && m_altitude == 0.0f ) + { + pos = m_object->RetPosition(0); + dist = Length2d(pos, m_goal); + if ( dist > 40.0f ) // plus de 10 mètres ? + { + m_altitude = 20.0f; // altitude par défaut + } + } + + BeamStart(); + + if ( m_bmFretObject == 0 ) + { + x = (int)((m_goal.x+1600.0f)/BM_DIM_STEP); + y = (int)((m_goal.z+1600.0f)/BM_DIM_STEP); + if ( BitmapTestDot(0, x, y) ) // arrivée occupée ? + { + m_error = ERR_GOTO_IMPOSSIBLE; + return m_error; + } + } + } + + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskGoto::IsEnded() +{ + D3DVECTOR pos; + float limit, angle, dist, h; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_error != ERR_OK ) return m_error; + + pos = m_object->RetPosition(0); + + if ( m_phase == TGP_BEAMLEAK ) // fuite ? + { + if ( m_leakTime >= m_leakDelay ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + BeamInit(); + m_phase = TGP_BEAMSEARCH; // faudra chercher le chemin + } + return ERR_CONTINUE; + } + + if ( m_phase == TGP_BEAMSEARCH ) // recherche du chemin ? + { + return ERR_CONTINUE; + } + + if ( m_phase == TGP_BEAMUP ) // décolle ? + { + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) + { + h = m_terrain->RetFloorHeight(pos, TRUE); + if ( h < m_altitude-20.0f ) return ERR_CONTINUE; + m_physics->SetMotorSpeedY(0.0f); // stoppe la montée + } + m_phase = TGP_BEAMGOTO; + } + + if ( m_phase == TGP_BEAMGOTO ) // goto dot list ? + { + if ( m_physics->RetLand() ) // au sol ? + { + limit = 1.0f; + } + else // en vol ? + { + limit = 2.0f; + if ( m_bmIndex < m_bmTotal ) limit *= 2.0f; // point intermédiaire + } + if ( m_bApprox ) limit = 2.0f; + + if ( Abs(pos.x - m_bmPoints[m_bmIndex].x) < limit && + Abs(pos.z - m_bmPoints[m_bmIndex].z) < limit ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + + m_bmIndex = BeamShortcut(); + + if ( m_bmIndex > m_bmTotal ) + { + m_phase = TGP_BEAMDOWN; + } + } + } + + if ( m_phase == TGP_BEAMDOWN ) // atteri ? + { + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) + { + if ( !m_physics->RetLand() ) return ERR_CONTINUE; + m_physics->SetMotorSpeedY(0.0f); // stoppe la descente + } + + if ( m_bTake ) + { + m_angle = RotateAngle(m_goalObject.x-pos.x, pos.z-m_goalObject.z); + m_phase = TGP_TURN; + } + else + { + return ERR_STOP; + } + } + + if ( m_goalMode == TGG_EXPRESS ) + { + dist = Length2d(m_goal, pos); + if ( dist < 10.0f && dist > m_lastDistance ) + { + return ERR_STOP; + } + m_lastDistance = dist; + } + + if ( m_phase == TGP_ADVANCE ) // va vers l'objectif ? + { + if ( m_physics->RetLand() ) limit = 0.1f; // au sol + else limit = 1.0f; // en vol + if ( m_bApprox ) limit = 2.0f; + + if ( Abs(pos.x - m_goal.x) < limit && + Abs(pos.z - m_goal.z) < limit ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + m_phase = TGP_LAND; + } + } + + if ( m_phase == TGP_LAND ) // atterri ? + { + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) + { + if ( !m_physics->RetLand() ) return ERR_CONTINUE; + m_physics->SetMotorSpeedY(0.0f); + } + + if ( m_bTake ) + { + m_angle = RotateAngle(m_goalObject.x-pos.x, pos.z-m_goalObject.z); + m_phase = TGP_TURN; + } + else + { + return ERR_STOP; + } + } + + if ( m_phase == TGP_TURN ) // tourne vers l'objet ? + { + angle = NormAngle(m_object->RetAngleY(0)); + limit = 0.02f; + if ( m_bApprox ) limit = 0.10f; + if ( Abs(angle-m_angle) < limit ) + { + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + if ( m_bmFinalMove == 0.0f ) return ERR_STOP; + + m_bmFinalPos = m_object->RetPosition(0); + m_bmFinalDist = m_physics->RetLinLength(m_bmFinalMove); + m_bmTimeLimit = m_physics->RetLinTimeLength(Abs(m_bmFinalMove))*1.5f; + if ( m_bmTimeLimit < 0.5f ) m_bmTimeLimit = 0.5f; + m_phase = TGP_MOVE; + } + } + + if ( m_phase == TGP_CRWAIT ) // attend après collision ? + { + if ( m_crashMode == TGC_HALT ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + m_error = ERR_GENERIC; + return m_error; + } + if ( m_time >= 1.0f ) + { + if ( m_crashMode == TGC_RIGHTLEFT || + m_crashMode == TGC_RIGHT ) angle = PI/2.0f; // 90 à droite + else angle = -PI/2.0f; // 90 à gauche + m_angle = NormAngle(m_object->RetAngleY(0)+angle); + m_phase = TGP_CRTURN; +//? m_phase = TGP_ADVANCE; + } + } + + if ( m_phase == TGP_CRTURN ) // tourne après collision ? + { + angle = NormAngle(m_object->RetAngleY(0)); + limit = 0.1f; + if ( Abs(angle-m_angle) < limit ) + { + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + m_pos = pos; + m_phase = TGP_CRADVANCE; + } + } + + if ( m_phase == TGP_CRADVANCE ) // avance après collision ? + { + if ( Length(pos, m_pos) >= 5.0f ) + { + m_phase = TGP_ADVANCE; + } + } + + if ( m_phase == TGP_CLWAIT ) // attend après collision ? + { + if ( m_time >= 1.0f ) + { + if ( m_crashMode == TGC_RIGHTLEFT ) angle = -PI; + if ( m_crashMode == TGC_LEFTRIGHT ) angle = PI; + if ( m_crashMode == TGC_RIGHT ) angle = PI/2.0f; + if ( m_crashMode == TGC_LEFT ) angle = -PI/2.0f; + m_angle = NormAngle(m_object->RetAngleY(0)+angle); + m_phase = TGP_CLTURN; + } + } + + if ( m_phase == TGP_CLTURN ) // tourne après collision ? + { + angle = NormAngle(m_object->RetAngleY(0)); + limit = 0.1f; + if ( Abs(angle-m_angle) < limit ) + { + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + m_pos = pos; + m_phase = TGP_CLADVANCE; + } + } + + if ( m_phase == TGP_CLADVANCE ) // avance après collision ? + { + if ( Length(pos, m_pos) >= 10.0f ) + { + m_phase = TGP_ADVANCE; + m_try ++; + } + } + + if ( m_phase == TGP_MOVE ) // avance finale ? + { + if ( m_bmTimeLimit <= 0.0f ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe + Abort(); + return ERR_STOP; + } + + dist = Length(m_bmFinalPos, m_object->RetPosition(0)); + if ( dist < m_bmFinalDist ) return ERR_CONTINUE; + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + return ERR_STOP; + } + + return ERR_CONTINUE; +} + + +// Cherche l'objet à la position cible. + +CObject* CTaskGoto::SearchTarget(D3DVECTOR pos, float margin) +{ + CObject* pObj; + D3DVECTOR oPos; + float dist; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; + if ( pObj->RetTruck() != 0 ) continue; // objet porté ? + + oPos = pObj->RetPosition(0); + dist = Length2d(pos, oPos); + + if ( dist <= margin ) return pObj; + } + + return 0; +} + +// Ajuste la cible en fonction de l'objet. +// Retourne TRUE s'il s'agit de fret posé au sol. + +BOOL CTaskGoto::AdjustTarget(CObject* pObj, D3DVECTOR &pos, float &distance) +{ + ObjectType type; + Character* character; + D3DMATRIX* mat; + D3DVECTOR goal; + float dist, suppl; + + type = m_object->RetType(); + if ( type == OBJECT_BEE || + type == OBJECT_WORM ) + { + pos = pObj->RetPosition(0); + return FALSE; + } + + type = pObj->RetType(); + + if ( type == OBJECT_FRET || + type == OBJECT_STONE || + type == OBJECT_URANIUM || + type == OBJECT_METAL || + type == OBJECT_POWER || + type == OBJECT_ATOMIC || + type == OBJECT_BULLET || + type == OBJECT_BBOX || + type == OBJECT_KEYa || + type == OBJECT_KEYb || + type == OBJECT_KEYc || + type == OBJECT_KEYd || + type == OBJECT_TNT || + type == OBJECT_BOMB ) + { + pos = m_object->RetPosition(0); + goal = pObj->RetPosition(0); + dist = Length(goal, pos); + pos = (pos-goal)*(TAKE_DIST+distance)/dist + goal; + return TRUE; + } + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEtg || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit ) + { + character = pObj->RetCharacter(); + pos = character->posPower; + pos.x -= TAKE_DIST+TAKE_DIST_OTHER+distance; + mat = pObj->RetWorldMatrix(0); + pos = Transform(*mat, pos); + return FALSE; + } + + if ( GetHotPoint(pObj, goal, TRUE, distance, suppl) ) + { + pos = goal; + distance += suppl; + return FALSE; + } + + pos = pObj->RetPosition(0); + return FALSE; +} + +// S'il on est sur un objet produit par un bâtiment (minerai produit +// par derrick), modifie la position par-rapport au bâtiment. + +BOOL CTaskGoto::AdjustBuilding(D3DVECTOR &pos, float margin, float &distance) +{ + CObject* pObj; + D3DVECTOR oPos; + float dist, suppl; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; + if ( pObj->RetTruck() != 0 ) continue; // objet porté ? + + if ( !GetHotPoint(pObj, oPos, FALSE, 0.0f, suppl) ) continue; + dist = Length2d(pos, oPos); + if ( dist <= margin ) + { + GetHotPoint(pObj, pos, TRUE, distance, suppl); + distance += suppl; + return TRUE; + } + } + return FALSE; +} + +// Retourne le point où est produit ou posé qq chose sur un bâtiment. + +BOOL CTaskGoto::GetHotPoint(CObject *pObj, D3DVECTOR &pos, + BOOL bTake, float distance, float &suppl) +{ + ObjectType type; + D3DMATRIX* mat; + + pos = D3DVECTOR(0.0f, 0.0f, 0.0f); + suppl = 0.0f; + type = pObj->RetType(); + + if ( type == OBJECT_DERRICK ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 7.0f; + if ( bTake && distance != 0.0f ) suppl = 4.0f; + if ( bTake ) pos.x += TAKE_DIST+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_CONVERT ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 0.0f; + if ( bTake && distance != 0.0f ) suppl = 4.0f; + if ( bTake ) pos.x += TAKE_DIST+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_RESEARCH ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 7.5f; + if ( bTake ) pos.x += TAKE_DIST+TAKE_DIST_OTHER+distance; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_ENERGY ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 0.0f; + if ( bTake ) pos.x += TAKE_DIST+TAKE_DIST_OTHER+distance; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_TOWER ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 5.0f; + if ( bTake && distance != 0.0f ) suppl = 4.0f; + if ( bTake ) pos.x += TAKE_DIST+TAKE_DIST_OTHER+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_LABO ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 0.0f; + if ( bTake ) pos.x += TAKE_DIST+TAKE_DIST_OTHER+distance; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_NUCLEAR ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 22.0f; + if ( bTake && distance != 0.0f ) suppl = 4.0f; + if ( bTake ) pos.x += TAKE_DIST+TAKE_DIST_OTHER+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_FACTORY ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 4.0f; + if ( bTake && distance != 0.0f ) suppl = 3.0f; + if ( bTake ) pos.x += TAKE_DIST+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_STATION ) + { + mat = pObj->RetWorldMatrix(0); + if ( bTake ) pos.x += distance; + pos = Transform(*mat, pos); + return TRUE; + } + + suppl = 0.0f; + return FALSE; +} + + +// Cherche un objet trop proche qu'il faut fuire. + +BOOL CTaskGoto::LeakSearch(D3DVECTOR &pos, float &delay) +{ + CObject* pObj; + D3DVECTOR iPos, oPos, bPos; + float iRadius, oRadius, bRadius, dist, min; + int i, j; + + m_object->GetCrashSphere(0, iPos, iRadius); + + min = 100000.0f; + bRadius = 0.0f; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + if ( !pObj->RetActif() ) continue; + if ( pObj->RetTruck() != 0 ) continue; // objet porté ? + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + dist = Length2d(oPos, iPos); + if ( dist < min ) + { + min = dist; + bPos = oPos; + bRadius = oRadius; + } + } + } + if ( min > iRadius+bRadius+4.0f ) return FALSE; + + pos = bPos; + delay = m_physics->RetLinTimeLength(4.0f); + return TRUE; +} + + +// Calcule la force de répulsion en fonction des obstacles. +// La longueur du vecteur rendu est comprise entre 0 et 1. + +void CTaskGoto::ComputeRepulse(FPOINT &dir) +{ +#if 0 + D3DVECTOR iPos, oPos; + FPOINT repulse; + CObject *pObj; + float dist, iRadius, oRadius; + int i; + + dir.x = 0.0f; + dir.y = 0.0f; + + m_object->GetCrashSphere(0, iPos, iRadius); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, m_goalObject); + if ( dist <= 1.0f ) continue; + + pObj->GetGlobalSphere(oPos, oRadius); + oRadius += iRadius+m_physics->RetLinStopLength()*1.1f; + dist = Length2d(oPos, iPos); + if ( dist <= oRadius ) + { + repulse.x = iPos.x-oPos.x; + repulse.y = iPos.z-oPos.z; + +//? dist = 0.2f-(0.2f*dist/oRadius); + dist = powf(dist/oRadius, 2.0f); + dist = 0.2f-0.2f*dist; + repulse.x *= dist; + repulse.y *= dist; +//? repulse.x /= dist; +//? repulse.y /= dist; + + dir.x += repulse.x; + dir.y += repulse.y; + } + } +#else + ObjectType iType, oType; + D3DVECTOR iPos, oPos; + FPOINT repulse; + CObject *pObj; + float gDist, add, addi, fac, dist, iRadius, oRadius; + int i, j; + BOOL bAlien; + + dir.x = 0.0f; + dir.y = 0.0f; + + // Le ver passe partout et à travers tout ! + iType = m_object->RetType(); + if ( iType == OBJECT_WORM ) return; + + m_object->GetCrashSphere(0, iPos, iRadius); + gDist = Length(iPos, m_goal); + + add = m_physics->RetLinStopLength()*1.1f; // distance de freinage + fac = 2.0f; + + if ( iType == OBJECT_MOBILEwa || + iType == OBJECT_MOBILEwc || + iType == OBJECT_MOBILEwi || + iType == OBJECT_MOBILEws || + iType == OBJECT_MOBILEwt ) // roues ? + { + add = 5.0f; + fac = 1.5f; + } + if ( iType == OBJECT_MOBILEta || + iType == OBJECT_MOBILEtc || + iType == OBJECT_MOBILEti || + iType == OBJECT_MOBILEts || + iType == OBJECT_MOBILEtt ) // chenilles ? + { + add = 4.0f; + fac = 1.5f; + } + if ( iType == OBJECT_MOBILEfa || + iType == OBJECT_MOBILEfc || + iType == OBJECT_MOBILEfi || + iType == OBJECT_MOBILEfs || + iType == OBJECT_MOBILEft ) // volant ? + { + if ( m_physics->RetLand() ) + { + add = 5.0f; + fac = 1.5f; + } + else + { + add = 10.0f; + fac = 1.5f; + } + } + if ( iType == OBJECT_MOBILEia || + iType == OBJECT_MOBILEic || + iType == OBJECT_MOBILEii || + iType == OBJECT_MOBILEis || + iType == OBJECT_MOBILEit ) // pattes ? + { + add = 4.0f; + fac = 1.5f; + } + if ( iType == OBJECT_BEE ) // guêpe ? + { + if ( m_physics->RetLand() ) + { + add = 3.0f; + fac = 1.5f; + } + else + { + add = 5.0f; + fac = 1.5f; + } + } + + bAlien = FALSE; + if ( iType == OBJECT_MOTHER || + iType == OBJECT_ANT || + iType == OBJECT_SPIDER || + iType == OBJECT_BEE || + iType == OBJECT_WORM ) + { + bAlien = TRUE; + } + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + oType = pObj->RetType(); + + if ( oType == OBJECT_WORM ) continue; + + if ( bAlien ) + { + if ( oType == OBJECT_STONE || + oType == OBJECT_URANIUM || + oType == OBJECT_METAL || + oType == OBJECT_POWER || + oType == OBJECT_ATOMIC || + oType == OBJECT_BULLET || + oType == OBJECT_BBOX || + oType == OBJECT_KEYa || + oType == OBJECT_KEYb || + oType == OBJECT_KEYc || + oType == OBJECT_KEYd || + oType == OBJECT_TNT || + oType == OBJECT_BOMB || + (oType >= OBJECT_PLANT0 && + oType <= OBJECT_PLANT19 ) || + (oType >= OBJECT_MUSHROOM0 && + oType <= OBJECT_MUSHROOM9 ) ) continue; + } + + addi = add; + if ( iType == OBJECT_BEE && + oType == OBJECT_BEE ) + { + addi = 2.0f; // entre guèpes, faut pas trop s'embêter + } + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + if ( oPos.y-oRadius > iPos.y+iRadius ) continue; + if ( oPos.y+oRadius < iPos.y-iRadius ) continue; + + dist = Length(oPos, m_goal); + if ( dist <= 1.0f ) continue; // sur le but ? + + oRadius += iRadius+addi; + dist = Length2d(oPos, iPos); + if ( dist > gDist ) continue; // plus loin que le but ? + if ( dist <= oRadius ) + { + repulse.x = iPos.x-oPos.x; + repulse.y = iPos.z-oPos.z; + + dist = powf(dist/oRadius, fac); + dist = 0.2f-0.2f*dist; + repulse.x *= dist; + repulse.y *= dist; + + dir.x += repulse.x; + dir.y += repulse.y; + } + } + } +#endif +} + +// Calcule la force de répulsion verticale en fonction des obstacles. +// La longueur du vecteur rendu est comprise entre -1 et 1. + +void CTaskGoto::ComputeFlyingRepulse(float &dir) +{ + ObjectType oType; + D3DVECTOR iPos, oPos; + CObject *pObj; + float add, fac, dist, iRadius, oRadius, repulse; + int i, j; + + m_object->GetCrashSphere(0, iPos, iRadius); + + add = 0.0f; + fac = 1.5f; + dir = 0.0f; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + oType = pObj->RetType(); + + if ( oType == OBJECT_WORM ) continue; + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + oRadius += iRadius+add; + dist = Length2d(oPos, iPos); + if ( dist <= oRadius ) + { + repulse = iPos.y-oPos.y; + + dist = powf(dist/oRadius, fac); + dist = 0.2f-0.2f*dist; + repulse *= dist; + + dir += repulse; + } + } + } + + if ( dir < -1.0f ) dir = -1.0f; + if ( dir > 1.0f ) dir = 1.0f; +} + + + +// Parmi tous les points suivants, cherche s'il en existe un qui +// permet d'y aller directement à vol d'oiseau. Si oui, saute tous +// les points intermédiaires inutiles. + +int CTaskGoto::BeamShortcut() +{ + int i; + + for ( i=m_bmTotal ; i>=m_bmIndex+2 ; i-- ) // cherche depuis le dernier + { + if ( BitmapTestLine(m_bmPoints[m_bmIndex], m_bmPoints[i], 0.0f, FALSE) ) + { + return i; // bingo, trouvé + } + } + + return m_bmIndex+1; // va simplement au point suivant +} + +// C'est le grand départ. + +void CTaskGoto::BeamStart() +{ + D3DVECTOR min, max; + +//? OutputDebugString("CTaskGoto::Start TGC_BEAM\n"); + BitmapOpen(); + BitmapObject(); + + min = m_object->RetPosition(0); + max = m_goal; + if ( min.x > max.x ) Swap(min.x, max.x); + if ( min.z > max.z ) Swap(min.z, max.z); + min.x -= 10.0f*BM_DIM_STEP; + min.z -= 10.0f*BM_DIM_STEP; + max.x += 10.0f*BM_DIM_STEP; + max.z += 10.0f*BM_DIM_STEP; + BitmapTerrain(min, max); + + if ( LeakSearch(m_leakPos, m_leakDelay) ) + { + m_phase = TGP_BEAMLEAK; // il faut d'abord fuire + m_leakTime = 0.0f; + } + else + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + BeamInit(); + m_phase = TGP_BEAMSEARCH; // faudra chercher le chemin + } +} + +// Initialisation avant le premier BeamSearch. + +void CTaskGoto::BeamInit() +{ + int i; + + for ( i=0 ; i 20.0f ) step = 20.0f; + nbIter = 200; // pour ne pas trop baisser le framerate + m_bmIterCounter = 0; + return BeamExplore(start, start, goal, goalRadius, 165.0f*PI/180.0f, 22, step, 0, nbIter); +} + +// prevPos: position précédente +// curPos: position courante +// goalPos: position qu'on cherche à atteindre +// angle: angle par rapport au but qu'on explore +// nbDiv: nombre du sous-divisions qu'on fait avec angle +// step longuer d'un pas +// i nombre de récursions effectuées +// nbIter nombre max. d'iterations qu'on a le droit de faire avant d'interrompre provisoirement + +Error CTaskGoto::BeamExplore(const D3DVECTOR &prevPos, const D3DVECTOR &curPos, + const D3DVECTOR &goalPos, float goalRadius, + float angle, int nbDiv, float step, + int i, int nbIter) +{ + D3DVECTOR newPos; + Error ret; + int iDiv, iClear, iLar; + + iLar = 0; + if ( i >= MAXPOINTS ) return ERR_GOTO_ITER; // trop de récursion + + if ( m_bmIter[i] == -1 ) + { + m_bmIter[i] = 0; + + if ( i == 0 ) + { + m_bmPoints[i] = curPos; + } + else + { + if ( !BitmapTestLine(prevPos, curPos, angle/nbDiv, TRUE) ) return ERR_GOTO_IMPOSSIBLE; + + m_bmPoints[i] = curPos; + + if ( Length2d(curPos, goalPos)-goalRadius <= step ) + { + if ( goalRadius == 0.0f ) + { + newPos = goalPos; + } + else + { + newPos = BeamPoint(curPos, goalPos, 0, Length2d(curPos, curPos)-goalRadius); + } + if ( BitmapTestLine(curPos, newPos, angle/nbDiv, FALSE) ) + { + m_bmPoints[i+1] = newPos; + m_bmTotal = i+1; + return ERR_OK; + } + } + } + } + + if ( iLar >= m_bmIter[i] ) + { + newPos = BeamPoint(curPos, goalPos, 0, step); + ret = BeamExplore(curPos, newPos, goalPos, goalRadius, angle, nbDiv, step, i+1, nbIter); + if ( ret != ERR_GOTO_IMPOSSIBLE ) return ret; + m_bmIter[i] = iLar+1; + for ( iClear=i+1 ; iClear<=MAXPOINTS ; iClear++ ) m_bmIter[iClear] = -1; + m_bmIterCounter ++; + if ( m_bmIterCounter >= nbIter ) return ERR_CONTINUE; + } + iLar ++; + + for ( iDiv=1 ; iDiv<=nbDiv ; iDiv++ ) + { + if ( iLar >= m_bmIter[i] ) + { + newPos = BeamPoint(curPos, goalPos, angle*iDiv/nbDiv, step); + ret = BeamExplore(curPos, newPos, goalPos, goalRadius, angle, nbDiv, step, i+1, nbIter); + if ( ret != ERR_GOTO_IMPOSSIBLE ) return ret; + m_bmIter[i] = iLar+1; + for ( iClear=i+1 ; iClear<=MAXPOINTS ; iClear++ ) m_bmIter[iClear] = -1; + m_bmIterCounter ++; + if ( m_bmIterCounter >= nbIter ) return ERR_CONTINUE; + } + iLar ++; + + if ( iLar >= m_bmIter[i] ) + { + newPos = BeamPoint(curPos, goalPos, -angle*iDiv/nbDiv, step); + ret = BeamExplore(curPos, newPos, goalPos, goalRadius, angle, nbDiv, step, i+1, nbIter); + if ( ret != ERR_GOTO_IMPOSSIBLE ) return ret; + m_bmIter[i] = iLar+1; + for ( iClear=i+1 ; iClear<=MAXPOINTS ; iClear++ ) m_bmIter[iClear] = -1; + m_bmIterCounter ++; + if ( m_bmIterCounter >= nbIter ) return ERR_CONTINUE; + } + iLar ++; + } + + return ERR_GOTO_IMPOSSIBLE; +} + +// Soit une droite "start-goal". Calcule le point situé à la distance +// "step" du point "start" et faisant un angle "angle" avec la droite. + +D3DVECTOR CTaskGoto::BeamPoint(const D3DVECTOR &startPoint, + const D3DVECTOR &goalPoint, + float angle, float step) +{ + D3DVECTOR resPoint; + float goalAngle; + + goalAngle = RotateAngle(goalPoint.x-startPoint.x, goalPoint.z-startPoint.z); + + resPoint.x = startPoint.x + cosf(goalAngle+angle)*step; + resPoint.z = startPoint.z + sinf(goalAngle+angle)*step; + resPoint.y = 0.0f; + + return resPoint; +} + +// Affiche une partion de bitmap. + +void CTaskGoto::BitmapDebug(const D3DVECTOR &min, const D3DVECTOR &max, + const D3DVECTOR &start, const D3DVECTOR &goal) +{ + int minx, miny, maxx, maxy, x, y, i ,n; + char s[2000]; + + minx = (int)((min.x+1600.0f)/BM_DIM_STEP); + miny = (int)((min.z+1600.0f)/BM_DIM_STEP); + maxx = (int)((max.x+1600.0f)/BM_DIM_STEP); + maxy = (int)((max.z+1600.0f)/BM_DIM_STEP); + + if ( minx > maxx ) Swap(minx, maxx); + if ( miny > maxy ) Swap(miny, maxy); + + OutputDebugString("Bitmap :\n"); + for ( y=miny ; y<=maxy ; y++ ) + { + s[0] = 0; + for ( x=minx ; x<=maxx ; x++ ) + { + n = -1; + for ( i=0 ; i<=m_bmTotal ; i++ ) + { + if ( x == (int)((m_bmPoints[i].x+1600.0f)/BM_DIM_STEP) && + y == (int)((m_bmPoints[i].z+1600.0f)/BM_DIM_STEP) ) + { + n = i; + break; + } + } + + if ( BitmapTestDot(0, x,y) ) + { + strcat(s, "o"); + } + else + { + if ( BitmapTestDot(1, x,y) ) + { + strcat(s, "-"); + } + else + { + strcat(s, "."); + } + } + + if ( x == (int)((start.x+1600.0f)/BM_DIM_STEP) && + y == (int)((start.z+1600.0f)/BM_DIM_STEP) ) + { + strcat(s, "s"); + } + else + if ( x == (int)((goal.x+1600.0f)/BM_DIM_STEP) && + y == (int)((goal.z+1600.0f)/BM_DIM_STEP) ) + { + strcat(s, "g"); + } + else + if ( n != -1 ) + { + char ss[2]; + ss[0] = 'A'+n; + ss[1] = 0; + strcat(s, ss); + } + else + { + strcat(s, " "); + } + } + strcat(s, "\n"); + OutputDebugString(s); + } +} + +// Teste si un chemin le long d'une droite est possible. + +BOOL CTaskGoto::BitmapTestLine(const D3DVECTOR &start, const D3DVECTOR &goal, + float stepAngle, BOOL bSecond) +{ + D3DVECTOR pos, inc; + float dist, step; + float distNoB2; + int i, max, x, y; + + if ( m_bmArray == 0 ) return TRUE; + + dist = Length2d(start, goal); + if ( dist == 0.0f ) return TRUE; + step = BM_DIM_STEP*0.5f; + + inc.x = (goal.x-start.x)*step/dist; + inc.z = (goal.z-start.z)*step/dist; + + pos = start; + + if ( bSecond ) + { + x = (int)((pos.x+1600.0f)/BM_DIM_STEP); + y = (int)((pos.z+1600.0f)/BM_DIM_STEP); + BitmapSetDot(1, x, y); // met le flag du point de départ + } + + max = (int)(dist/step); + if ( max == 0 ) max = 1; + distNoB2 = BM_DIM_STEP*sqrtf(2.0f)/sinf(stepAngle); + for ( i=0 ; i 2 && BitmapTestDot(1, x, y) ) return FALSE; + + if ( step*(i+1) > distNoB2 && i < max-2 ) + { + BitmapSetDot(1, x, y); + } + } + + if ( BitmapTestDot(0, x, y) ) return FALSE; + } + return TRUE; +} + +// Ajoute les objets dans le bitmap. + +void CTaskGoto::BitmapObject() +{ + CObject *pObj; + D3DVECTOR iPos, oPos; + float iRadius, oRadius, h; + int i, j; + + m_object->GetCrashSphere(0, iPos, iRadius); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + if ( pObj == m_bmFretObject ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + h = m_terrain->RetFloorLevel(pObj->RetPosition(0), TRUE); + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) + { + h += m_altitude; + } + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) // volant ? + { + if ( oPos.y-oRadius > h+8.0f || + oPos.y+oRadius < h-8.0f ) continue; + } + else // rampant ? + { + if ( oPos.y-oRadius > h+8.0f ) continue; + } + + BitmapSetCircle(oPos, oRadius+iRadius+4.0f); + } + } +} + +// Ajoute une portion de terrain dans le bitmap. + +void CTaskGoto::BitmapTerrain(const D3DVECTOR &min, const D3DVECTOR &max) +{ + int minx, miny, maxx, maxy; + + minx = (int)((min.x+1600.0f)/BM_DIM_STEP); + miny = (int)((min.z+1600.0f)/BM_DIM_STEP); + maxx = (int)((max.x+1600.0f)/BM_DIM_STEP); + maxy = (int)((max.z+1600.0f)/BM_DIM_STEP); + + BitmapTerrain(minx, miny, maxx, maxy); +} + +// Ajoute une portion de terrain dans le bitmap. + +void CTaskGoto::BitmapTerrain(int minx, int miny, int maxx, int maxy) +{ + ObjectType type; + D3DVECTOR p; + float aLimit, angle, h; + int x, y; + BOOL bAcceptWater, bFly; + + if ( minx > maxx ) Swap(minx, maxx); + if ( miny > maxy ) Swap(miny, maxy); + + if ( minx < 0 ) minx = 0; + if ( miny < 0 ) miny = 0; + if ( maxx > m_bmSize-1 ) maxx = m_bmSize-1; + if ( maxy > m_bmSize-1 ) maxy = m_bmSize-1; + + if ( minx > m_bmMinX ) minx = m_bmMinX; + if ( miny > m_bmMinY ) miny = m_bmMinY; + if ( maxx < m_bmMaxX ) maxx = m_bmMaxX; + if ( maxy < m_bmMaxY ) maxy = m_bmMaxY; + + if ( minx >= m_bmMinX && maxx <= m_bmMaxX && + miny >= m_bmMinY && maxy <= m_bmMaxY ) return; + + aLimit = 20.0f*PI/180.0f; + bAcceptWater = FALSE; + bFly = FALSE; + + type = m_object->RetType(); + + if ( type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEtg ) // roues ? + { + aLimit = 20.0f*PI/180.0f; + } + + if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts ) // chenilles ? + { + aLimit = 35.0f*PI/180.0f; + } + + if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) // grosses chenilles ? + { + aLimit = 35.0f*PI/180.0f; + } + + if ( type == OBJECT_MOBILEsa ) // chenilles sous-marin ? + { + aLimit = 35.0f*PI/180.0f; + bAcceptWater = TRUE; + } + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEft ) // volant ? + { + aLimit = 15.0f*PI/180.0f; + bFly = TRUE; + } + + if ( type == OBJECT_MOBILEia || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILEii ) // pattes d'insecte ? + { + aLimit = 60.0f*PI/180.0f; + } + + for ( y=miny ; y<=maxy ; y++ ) + { + for ( x=minx ; x<=maxx ; x++ ) + { + if ( x >= m_bmMinX && x <= m_bmMaxX && + y >= m_bmMinY && y <= m_bmMaxY ) continue; + + p.x = x*BM_DIM_STEP-1600.0f; + p.z = y*BM_DIM_STEP-1600.0f; + + if ( bFly ) // robot volant ? + { + h = m_terrain->RetFloorLevel(p, TRUE); + if ( h >= m_terrain->RetFlyingMaxHeight()-5.0f ) + { + BitmapSetDot(0, x, y); + } + continue; + } + + if ( !bAcceptWater ) // ne va pas sous l'eau ? + { + h = m_terrain->RetFloorLevel(p, TRUE); +//? if ( h <= m_water->RetLevel()+1.0f ) // sous l'eau ? + if ( h < m_water->RetLevel()-2.0f ) // sous l'eau ? + { +//? BitmapSetDot(0, x, y); + BitmapSetCircle(p, BM_DIM_STEP*1.0f); + continue; + } + } + + angle = m_terrain->RetFineSlope(p); + if ( angle > aLimit ) + { + BitmapSetDot(0, x, y); + } + } + } + + m_bmMinX = minx; + m_bmMinY = miny; + m_bmMaxX = maxx; + m_bmMaxY = maxy; +} + +// Ouvre un bitmap vide. + +BOOL CTaskGoto::BitmapOpen() +{ + BitmapClose(); + + m_bmSize = (int)(3200.0f/BM_DIM_STEP); + m_bmArray = (unsigned char*)malloc(m_bmSize*m_bmSize/8*2); + ZeroMemory(m_bmArray, m_bmSize*m_bmSize/8*2); + + m_bmOffset = m_bmSize/2; + m_bmLine = m_bmSize/8; + + m_bmMinX = m_bmSize; + m_bmMinY = m_bmSize; + m_bmMaxX = 0; + m_bmMaxY = 0; + + return TRUE; +} + +// Ferme le bitmap. + +BOOL CTaskGoto::BitmapClose() +{ + free(m_bmArray); + m_bmArray = 0; + return TRUE; +} + +// Met un cercle dans le bitmap. + +void CTaskGoto::BitmapSetCircle(const D3DVECTOR &pos, float radius) +{ + float d, r; + int cx, cy, ix, iy; + + cx = (int)((pos.x+1600.0f)/BM_DIM_STEP); + cy = (int)((pos.z+1600.0f)/BM_DIM_STEP); + r = radius/BM_DIM_STEP; + + for ( iy=cy-(int)r ; iy<=cy+(int)r ; iy++ ) + { + for ( ix=cx-(int)r ; ix<=cx+(int)r ; ix++ ) + { + d = Length((float)(ix-cx), (float)(iy-cy)); + if ( d > r ) continue; + BitmapSetDot(0, ix, iy); + } + } +} + +// Met un point dans le bitmap. +// x:y: 0..m_bmSize-1 + +void CTaskGoto::BitmapSetDot(int rank, int x, int y) +{ + if ( x < 0 || x >= m_bmSize || + y < 0 || y >= m_bmSize ) return; + + m_bmArray[rank*m_bmLine*m_bmSize + m_bmLine*y + x/8] |= (1<= m_bmSize || + y < 0 || y >= m_bmSize ) return FALSE; + + if ( x < m_bmMinX || x > m_bmMaxX || + y < m_bmMinY || y > m_bmMaxY ) + { + BitmapTerrain(x-10,y-10, x+10,y+10); // refait une couche + } + + return m_bmArray[rank*m_bmLine*m_bmSize + m_bmLine*y + x/8] & (1<(#6~Ws%VdqqhW24YdLbU*GQBmB_-YDy)kA%VWp(VEYI`*fMJDxfL#68St@l{(Q??s%2W;-=e(LA&-p`Mz zpW9?skaL@2Y!k2tZ87KAnk6Bc#Wknq9&y%I5A{+{BWce&Vj$|CC(6e(a>wc6PCMgC z@ppS3wD9^iy4AqVlrBkAh)=? +@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^ +_abcdefghijklmnopqrstuvwxyz{|}~ +ÁÀÂÄÃÇÉÈÊËÍÌÎÏÑÓÒÔÖÕÚÙÛÜ +áàâäãçéèêëíìîïñóòôöõúùûü diff --git a/src/auto.cpp b/src/auto.cpp new file mode 100644 index 00000000..3f0d5495 --- /dev/null +++ b/src/auto.cpp @@ -0,0 +1,447 @@ +// auto.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "water.h" +#include "cloud.h" +#include "planet.h" +#include "blitz.h" +#include "camera.h" +#include "object.h" +#include "modfile.h" +#include "interface.h" +#include "button.h" +#include "list.h" +#include "label.h" +#include "gauge.h" +#include "window.h" +#include "robotmain.h" +#include "displaytext.h" +#include "sound.h" +#include "cmdtoken.h" +#include "auto.h" + + + + +// Constructeur de l'objet. + +CAuto::CAuto(CInstanceManager* iMan, CObject* object) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_AUTO, this, 100); + + m_object = object; + m_event = (CEvent*)m_iMan->SearchInstance(CLASS_EVENT); + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + m_light = (CLight*)m_iMan->SearchInstance(CLASS_LIGHT); + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + m_cloud = (CCloud*)m_iMan->SearchInstance(CLASS_CLOUD); + m_planet = (CPlanet*)m_iMan->SearchInstance(CLASS_PLANET); + m_blitz = (CBlitz*)m_iMan->SearchInstance(CLASS_BLITZ); + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + m_interface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_displayText = (CDisplayText*)m_iMan->SearchInstance(CLASS_DISPLAYTEXT); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + + m_type = m_object->RetType(); + m_time = 0.0f; + m_lastUpdateTime = 0.0f; + m_bMotor = FALSE; + m_progressTime = 0.0f; + m_progressTotal = 1.0f; + + Init(); +} + +// Destructeur de l'objet. + +CAuto::~CAuto() +{ + m_iMan->DeleteInstance(CLASS_AUTO, this); +} + + +// Détruit l'objet. + +void CAuto::DeleteObject(BOOL bAll) +{ +} + + +// Initialise l'objet. + +void CAuto::Init() +{ + m_bBusy = FALSE; +} + +// Démarre l'objet. + +void CAuto::Start(int param) +{ +} + + +// Donne un type. + +BOOL CAuto::SetType(ObjectType type) +{ + return FALSE; +} + +// Donne une valeur. + +BOOL CAuto::SetValue(int rank, float value) +{ + return FALSE; +} + +// Donne la string. + +BOOL CAuto::SetString(char *string) +{ + return FALSE; +} + + +// Gestion d'un événement. + +BOOL CAuto::EventProcess(const Event &event) +{ + if ( event.event == EVENT_FRAME && + !m_engine->RetPause() ) + { + m_time += event.rTime; + UpdateInterface(event.rTime); + } + + if ( !m_object->RetSelect() ) // robot pas sélectionné ? + { + return TRUE; + } + + return TRUE; +} + +// Indique si l'automate a terminé son activité. + +Error CAuto::IsEnded() +{ + return ERR_CONTINUE; +} + +// Stoppe l'automate. + +BOOL CAuto::Abort() +{ + return FALSE; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAuto::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, dim, ddim; + float ox, oy, sx, sy; + char name[100]; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw != 0 ) + { + pw->Flush(); // détruit les boutons de la fenêtre + m_interface->DeleteControl(EVENT_WINDOW0); // détruit la fenêtre + } + + if ( !bSelect ) return TRUE; + + pos.x = 0.0f; + pos.y = 0.0f; + dim.x = 540.0f/640.0f; +//? dim.y = 70.0f/480.0f; + dim.y = 86.0f/480.0f; + m_interface->CreateWindows(pos, dim, 3, EVENT_WINDOW0); + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + m_object->GetTooltipName(name); + pos.x = 0.0f; + pos.y = 64.0f/480.0f; + ddim.x = 540.0f/640.0f; + ddim.y = 16.0f/480.0f; + pw->CreateLabel(pos, ddim, 0, EVENT_LABEL0, name); + + dim.x = 33.0f/640.0f; + dim.y = 33.0f/480.0f; + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*7.0f; + pos.y = oy+sy*0.6f; + ddim.x = 160.0f/640.0f; + ddim.y = 26.0f/480.0f; + pw->CreateGauge(pos, ddim, 0, EVENT_OBJECT_GPROGRESS); + + if ( m_type != OBJECT_BASE && + m_type != OBJECT_SAFE && + m_type != OBJECT_HUSTON ) + { + pos.x = ox+sx*2.1f; + pos.y = oy+sy*0; + ddim.x = dim.x*0.6f; + ddim.y = dim.y*0.6f; + pw->CreateButton(pos, ddim, 12, EVENT_OBJECT_DELETE); + } + +#if 0 + pos.x = ox+sx*12.4f; + pos.y = oy+sy*1; + pw->CreateButton(pos, dim, 63, EVENT_OBJECT_BHELP); + + pos.x = ox+sx*12.4f; + pos.y = oy+sy*0; + pw->CreateButton(pos, dim, 19, EVENT_OBJECT_HELP); + + if ( m_main->RetSceneSoluce() ) + { + pos.x = ox+sx*13.4f; + pos.y = oy+sy*1; + pw->CreateButton(pos, dim, 20, EVENT_OBJECT_SOLUCE); + } + + pos.x = ox+sx*13.4f; + pos.y = oy+sy*0; + pw->CreateButton(pos, dim, 10, EVENT_OBJECT_DESELECT); +#else + pos.x = ox+sx*12.3f; + pos.y = oy+sy*-0.1f; + ddim.x = dim.x*1.0f; + ddim.y = dim.y*2.1f; + pw->CreateGroup(pos, ddim, 20, EVENT_NULL); // fond bleu uni + + pos.x = ox+sx*12.3f; + pos.y = oy+sy*1; + pw->CreateGroup(pos, dim, 19, EVENT_NULL); // signe SatCom + + pos.x = ox+sx*12.4f; + pos.y = oy+sy*0.5f; + ddim.x = dim.x*0.8f; + ddim.y = dim.y*0.5f; + pw->CreateButton(pos, ddim, 18, EVENT_OBJECT_BHELP); + pos.y = oy+sy*0.0f; + pw->CreateButton(pos, ddim, 19, EVENT_OBJECT_HELP); + + pos.x = ox+sx*13.4f; + pos.y = oy+sy*0; + pw->CreateButton(pos, dim, 10, EVENT_OBJECT_DESELECT); +#endif + + pos.x = ox+sx*14.9f; + pos.y = oy+sy*0; + ddim.x = 14.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGauge(pos, ddim, 3, EVENT_OBJECT_GSHIELD); + + UpdateInterface(); + m_lastUpdateTime = 0.0f; + UpdateInterface(0.0f); + + return TRUE; +} + +// Modifie l'état d'un bouton de l'interface. + +void CAuto::CheckInterface(CWindow *pw, EventMsg event, BOOL bState) +{ + CControl* control; + + control = pw->SearchControl(event); + if ( control == 0 ) return; + + control->SetState(STATE_CHECK, bState); +} + +// Modifie l'état d'un bouton de l'interface. + +void CAuto::EnableInterface(CWindow *pw, EventMsg event, BOOL bState) +{ + CControl* control; + + control = pw->SearchControl(event); + if ( control == 0 ) return; + + control->SetState(STATE_ENABLE, bState); +} + +// Modifie l'état d'un bouton de l'interface. + +void CAuto::VisibleInterface(CWindow *pw, EventMsg event, BOOL bState) +{ + CControl* control; + + control = pw->SearchControl(event); + if ( control == 0 ) return; + + control->SetState(STATE_VISIBLE, bState); +} + +// Modifie l'état d'un bouton de l'interface. + +void CAuto::DeadInterface(CWindow *pw, EventMsg event, BOOL bState) +{ + CControl* control; + + control = pw->SearchControl(event); + if ( control == 0 ) return; + + control->SetState(STATE_DEAD, !bState); +} + +// Met à jour l'état de tous les boutons de l'interface. + +void CAuto::UpdateInterface() +{ + CWindow* pw; + + if ( !m_object->RetSelect() ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + VisibleInterface(pw, EVENT_OBJECT_GPROGRESS, m_bBusy); +} + +// Met à jour l'état de tous les boutons de l'interface, +// suite au temps qui s'écoule ... + +void CAuto::UpdateInterface(float rTime) +{ + CWindow* pw; + CGauge* pg; + + if ( m_time < m_lastUpdateTime+0.1f ) return; + m_lastUpdateTime = m_time; + + if ( !m_object->RetSelect() ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + pg = (CGauge*)pw->SearchControl(EVENT_OBJECT_GSHIELD); + if ( pg != 0 ) + { + pg->SetLevel(m_object->RetShield()); + } + + pg = (CGauge*)pw->SearchControl(EVENT_OBJECT_GPROGRESS); + if ( pg != 0 ) + { + pg->SetLevel(m_progressTime); + } +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAuto::RetError() +{ + return ERR_OK; +} + + +// Gestion de l'occupation. + +BOOL CAuto::RetBusy() +{ + return m_bBusy; +} + +void CAuto::SetBusy(BOOL bBusy) +{ + m_bBusy = bBusy; +} + +void CAuto::InitProgressTotal(float total) +{ + m_progressTime = 0.0f; + m_progressTotal = total; +} + +void CAuto::EventProgress(float rTime) +{ + m_progressTime += rTime/m_progressTotal; +} + + +// Gestion du moteur. + +BOOL CAuto::RetMotor() +{ + return m_bMotor; +} + +void CAuto::SetMotor(BOOL bMotor) +{ + m_bMotor = bMotor; +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAuto::Write(char *line) +{ + char name[100]; + + sprintf(name, " aType=%d", m_type); + strcat(line, name); + + sprintf(name, " aBusy=%d", m_bBusy); + strcat(line, name); + + sprintf(name, " aTime=%.2f", m_time); + strcat(line, name); + + sprintf(name, " aProgressTime=%.2f", m_progressTime); + strcat(line, name); + + sprintf(name, " aProgressTotal=%.2f", m_progressTotal); + strcat(line, name); + + return FALSE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAuto::Read(char *line) +{ + m_type = (ObjectType)OpInt(line, "aType", OBJECT_NULL); + m_bBusy = OpInt(line, "aBusy", 0); + m_time = OpFloat(line, "aTime", 0.0f); + m_progressTime = OpFloat(line, "aProgressTime", 0.0f); + m_progressTotal = OpFloat(line, "aProgressTotal", 0.0f); + + return FALSE; +} + diff --git a/src/auto.h b/src/auto.h new file mode 100644 index 00000000..5b0f331f --- /dev/null +++ b/src/auto.h @@ -0,0 +1,95 @@ +// auto.h + +#ifndef _AUTO_H_ +#define _AUTO_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CLight; +class CTerrain; +class CWater; +class CCloud; +class CPlanet; +class CBlitz; +class CCamera; +class CObject; +class CInterface; +class CRobotMain; +class CDisplayText; +class CWindow; +class CSound; + + + + +class CAuto +{ +public: + CAuto(CInstanceManager* iMan, CObject* object); + ~CAuto(); + + virtual void DeleteObject(BOOL bAll=FALSE); + + virtual void Init(); + virtual void Start(int param); + virtual BOOL EventProcess(const Event &event); + virtual Error IsEnded(); + virtual BOOL Abort(); + + virtual BOOL SetType(ObjectType type); + virtual BOOL SetValue(int rank, float value); + virtual BOOL SetString(char *string); + + virtual BOOL CreateInterface(BOOL bSelect); + virtual Error RetError(); + + virtual BOOL RetBusy(); + virtual void SetBusy(BOOL bBuse); + virtual void InitProgressTotal(float total); + virtual void EventProgress(float rTime); + + virtual BOOL RetMotor(); + virtual void SetMotor(BOOL bMotor); + + virtual BOOL Write(char *line); + virtual BOOL Read(char *line); + +protected: + void CheckInterface(CWindow *pw, EventMsg event, BOOL bState); + void EnableInterface(CWindow *pw, EventMsg event, BOOL bState); + void VisibleInterface(CWindow *pw, EventMsg event, BOOL bState); + void DeadInterface(CWindow *pw, EventMsg event, BOOL bState); + void UpdateInterface(); + void UpdateInterface(float rTime); + +protected: + CInstanceManager* m_iMan; + CEvent* m_event; + CD3DEngine* m_engine; + CParticule* m_particule; + CLight* m_light; + CTerrain* m_terrain; + CWater* m_water; + CCloud * m_cloud; + CPlanet * m_planet; + CBlitz* m_blitz; + CCamera* m_camera; + CInterface* m_interface; + CRobotMain* m_main; + CDisplayText* m_displayText; + CObject* m_object; + CSound* m_sound; + + ObjectType m_type; + BOOL m_bBusy; + BOOL m_bMotor; + float m_time; + float m_lastUpdateTime; + float m_progressTime; + float m_progressTotal; +}; + + +#endif //_AUTO_H_ diff --git a/src/autobase.cpp b/src/autobase.cpp new file mode 100644 index 00000000..db046ef9 --- /dev/null +++ b/src/autobase.cpp @@ -0,0 +1,1446 @@ +// autobase.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "language.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "cloud.h" +#include "planet.h" +#include "blitz.h" +#include "camera.h" +#include "object.h" +#include "physics.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "displaytext.h" +#include "robotmain.h" +#include "sound.h" +#include "auto.h" +#include "autobase.h" + + + +#define BASE_LAND_TIME 7.5f // durée atterrissage +#define BASE_TAKO_TIME 10.0f // durée atterrissage +#define BASE_DOOR_TIME 6.0f // durée ouverture/fermeture +#define BASE_DOOR_TIME2 2.0f // durée ouverture/fermeture suppl. +#define BASE_PORTICO_TIME_MOVE 16.0f // durée avance portique +#define BASE_PORTICO_TIME_DOWN 4.0f // durée descente portique +#define BASE_PORTICO_TIME_OPEN 4.0f // durée ouverture portique +#define BASE_TRANSIT_TIME 15.0f // durée transit + + + + +// Constructeur de l'objet. + +CAutoBase::CAutoBase(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + m_fogStart = m_engine->RetFogStart(); + m_deepView = m_engine->RetDeepView(); + Init(); + m_phase = ABP_WAIT; + m_soundChannel = -1; +} + +// Destructeur de l'objet. + +CAutoBase::~CAutoBase() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoBase::DeleteObject(BOOL bAll) +{ + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoBase::Init() +{ + m_bOpen = FALSE; + m_time = 0.0f; + m_lastParticule = 0.0f; + m_lastMotorParticule = 0.0f; + + m_pos = m_object->RetPosition(0); + m_lastPos = m_pos; + + m_phase = ABP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; +} + + +// Démarre l'objet. + +void CAutoBase::Start(int param) +{ + m_phase = ABP_START; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + + m_param = param; +} + + +// Gestion d'un événement. + +BOOL CAutoBase::EventProcess(const Event &event) +{ + D3DMATRIX* mat; + Event newEvent; + CObject* pObj; + D3DVECTOR pos, speed, vibCir, iPos; + FPOINT dim, p; + Error err; + float angle, dist, time, h, len, vSpeed; + int i, max; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + +begin: + iPos = m_object->RetPosition(0); + + if ( m_phase == ABP_START ) + { + if ( m_param != PARAM_STOP && // pas posé au sol ? + m_param != PARAM_FIXSCENE ) + { + FreezeCargo(TRUE); // gèle toute la cargaison + } + + if ( m_param == PARAM_STOP ) // posé au sol ? + { + m_phase = ABP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetAngleZ(1+i, PI/2.0f-124.0f*PI/180.0f); + m_object->SetAngleX(10+i, -10.0f*PI/180.0f); + m_object->SetAngleX(18+i, 10.0f*PI/180.0f); + m_object->SetPosition(10+i, D3DVECTOR(23.5f, 0.0f, -11.5f)); + m_object->SetPosition(18+i, D3DVECTOR(23.5f, 0.0f, 11.5f)); + } + + pObj = m_main->RetSelectObject(); + m_main->SelectObject(pObj); + m_camera->SetObject(pObj); + if ( pObj == 0 ) + { + m_camera->SetType(CAMERA_BACK); + } + else + { + m_camera->SetType(pObj->RetCameraType()); + m_camera->SetDist(pObj->RetCameraDist()); + } + + m_main->StartMusic(); + } + + if ( m_param == PARAM_FIXSCENE ) // posé au sol ? + { + m_phase = ABP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetAngleZ(1+i, PI/2.0f-124.0f*PI/180.0f); + m_object->SetAngleX(10+i, -10.0f*PI/180.0f); + m_object->SetAngleX(18+i, 10.0f*PI/180.0f); + m_object->SetPosition(10+i, D3DVECTOR(23.5f, 0.0f, -11.5f)); + m_object->SetPosition(18+i, D3DVECTOR(23.5f, 0.0f, 11.5f)); + } + } + + if ( m_param == PARAM_LANDING ) // atterrissage ? + { + m_phase = ABP_LAND; + m_progress = 0.0f; + m_speed = 1.0f/BASE_LAND_TIME; + + m_main->SetMovieLock(TRUE); // bloque tout jusqu'à la fin de l'atterrissage + m_bMotor = TRUE; // allume le réacteur + + m_camera->SetType(CAMERA_SCRIPT); + + pos = m_pos; + pos.x -= 150.0f; + m_terrain->MoveOnFloor(pos); + pos.y += 10.0f; + m_camera->SetScriptEye(pos); + m_posSound = pos; + + pos = m_object->RetPosition(0); + pos.y += 300.0f+50.0f; + m_camera->SetScriptLookat(pos); + + m_camera->FixCamera(); + m_engine->SetFocus(2.0f); + + m_engine->SetFogStart(0.9f); + + if ( m_soundChannel == -1 ) + { + m_soundChannel = m_sound->Play(SOUND_FLY, m_posSound, 0.3f, 2.0f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 0.5f, BASE_LAND_TIME, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.5f, 2.0f, SOPER_STOP); + } + + m_main->StartMusic(); + } + + if ( m_param == PARAM_PORTICO ) // porté par le portique ? + { + pos = m_object->RetPosition(0); + m_finalPos = pos; + pos.z += BASE_PORTICO_TIME_MOVE*5.0f; // recule + pos.y += 10.0f; // monte (porté par le portique) + m_object->SetPosition(0, pos); + MoveCargo(); // déplace toute la cargaison + + m_phase = ABP_PORTICO_MOVE; + m_progress = 0.0f; + m_speed = 1.0f/BASE_PORTICO_TIME_MOVE; + + m_main->StartMusic(); + } + + if ( m_param == PARAM_TRANSIT1 || + m_param == PARAM_TRANSIT2 || + m_param == PARAM_TRANSIT3 ) // transit dans l'espace ? + { + m_phase = ABP_TRANSIT_MOVE; + m_progress = 0.0f; + m_speed = 1.0f/BASE_TRANSIT_TIME; + + m_object->SetAngleZ(0, -PI/2.0f); + pos = m_object->RetPosition(0); + pos.y += 10000.0f; // dans l'espace + m_finalPos = pos; + m_object->SetPosition(0, pos); + + m_main->SetMovieLock(TRUE); // bloque tout jusqu'à la fin de l'atterrissage + m_bMotor = TRUE; // allume le réacteur + + m_camera->SetType(CAMERA_SCRIPT); + pos.x += 1000.0f; + pos.z -= 60.0f; + pos.y += 80.0f; + m_camera->SetScriptEye(pos); + m_posSound = pos; + m_camera->FixCamera(); + m_engine->SetFocus(1.0f); + + BeginTransit(); + + mat = m_object->RetWorldMatrix(0); + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 10.0f; + dim.y = dim.x; + pos = D3DVECTOR(42.0f, -2.0f, 17.0f); + pos = Transform(*mat, pos); + m_partiChannel[0] = m_particule->CreateParticule(pos, speed, dim, PARTILENS1, BASE_TRANSIT_TIME+1.0f, 0.0f, 0.0f); + pos = D3DVECTOR(17.0f, -2.0f, 42.0f); + pos = Transform(*mat, pos); + m_partiChannel[1] = m_particule->CreateParticule(pos, speed, dim, PARTILENS1, BASE_TRANSIT_TIME+1.0f, 0.0f, 0.0f); + pos = D3DVECTOR(42.0f, -2.0f, -17.0f); + pos = Transform(*mat, pos); + m_partiChannel[2] = m_particule->CreateParticule(pos, speed, dim, PARTILENS1, BASE_TRANSIT_TIME+1.0f, 0.0f, 0.0f); + pos = D3DVECTOR(17.0f, -2.0f, -42.0f); + pos = Transform(*mat, pos); + m_partiChannel[3] = m_particule->CreateParticule(pos, speed, dim, PARTILENS1, BASE_TRANSIT_TIME+1.0f, 0.0f, 0.0f); + pos = D3DVECTOR(-42.0f, -2.0f, 17.0f); + pos = Transform(*mat, pos); + m_partiChannel[4] = m_particule->CreateParticule(pos, speed, dim, PARTILENS1, BASE_TRANSIT_TIME+1.0f, 0.0f, 0.0f); + pos = D3DVECTOR(-17.0f, -2.0f, 42.0f); + pos = Transform(*mat, pos); + m_partiChannel[5] = m_particule->CreateParticule(pos, speed, dim, PARTILENS1, BASE_TRANSIT_TIME+1.0f, 0.0f, 0.0f); + pos = D3DVECTOR(-42.0f, -2.0f, -17.0f); + pos = Transform(*mat, pos); + m_partiChannel[6] = m_particule->CreateParticule(pos, speed, dim, PARTILENS1, BASE_TRANSIT_TIME+1.0f, 0.0f, 0.0f); + pos = D3DVECTOR(-17.0f, -2.0f, -42.0f); + pos = Transform(*mat, pos); + m_partiChannel[7] = m_particule->CreateParticule(pos, speed, dim, PARTILENS1, BASE_TRANSIT_TIME+1.0f, 0.0f, 0.0f); + + if ( m_soundChannel == -1 ) + { + m_soundChannel = m_sound->Play(SOUND_FLY, m_posSound, 0.0f, 1.2f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, BASE_TRANSIT_TIME*0.55f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.3f, 0.8f, BASE_TRANSIT_TIME*0.45f, SOPER_STOP); + } + } + } + + if ( event.event == EVENT_UPDINTERFACE ) + { + if ( m_object->RetSelect() ) CreateInterface(TRUE); + } + + if ( event.event == EVENT_OBJECT_BTAKEOFF ) + { + err = CheckCloseDoor(); + if ( err != ERR_OK ) + { + m_displayText->DisplayError(err, m_object); + return FALSE; + } + + err = m_main->CheckEndMission(FALSE); + if ( err != ERR_OK ) + { + m_displayText->DisplayError(err, m_object); + return FALSE; + } + + FreezeCargo(TRUE); // gèle toute la cargaison + m_main->SetMovieLock(TRUE); // bloque tout jusqu'à la fin + m_main->DeselectAll(); + + m_event->MakeEvent(newEvent, EVENT_UPDINTERFACE); + m_event->AddEvent(newEvent); + + m_camera->SetType(CAMERA_SCRIPT); + + pos = m_pos; + pos.x -= 110.0f; + m_terrain->MoveOnFloor(pos); + pos.y += 10.0f; + m_camera->SetScriptEye(pos); + m_posSound = pos; + + pos = m_object->RetPosition(0); + pos.y += 50.0f; + m_camera->SetScriptLookat(pos); + + m_engine->SetFocus(1.0f); + + m_soundChannel = m_sound->Play(SOUND_MANIP, m_posSound, 0.3f, 1.5f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 0.3f, 1.5f, BASE_DOOR_TIME2, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.5f, 0.5f, SOPER_STOP); + + m_phase = ABP_CLOSE2; + m_progress = 0.0f; + m_speed = 1.0f/BASE_DOOR_TIME2; + return TRUE; + } + + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_phase == ABP_WAIT ) return TRUE; + + m_progress += event.rTime*m_speed; + + if ( m_phase == ABP_LAND ) + { + if ( m_progress < 1.0f ) + { + pos = m_pos; + pos.y += powf(1.0f-m_progress, 2.0f)*300.0f; + m_object->SetPosition(0, pos); + MoveCargo(); // déplace toute la cargaison + + vibCir.z = sinf(m_time*PI* 2.01f)*(PI/150.0f)+ + sinf(m_time*PI* 2.51f)*(PI/200.0f)+ + sinf(m_time*PI*19.01f)*(PI/400.0f); + vibCir.x = sinf(m_time*PI* 2.03f)*(PI/150.0f)+ + sinf(m_time*PI* 2.52f)*(PI/200.0f)+ + sinf(m_time*PI*19.53f)*(PI/400.0f); + vibCir.y = 0.0f; + vibCir *= Min(1.0f, (1.0f-m_progress)*3.0f); + m_object->SetCirVibration(vibCir); + + pos = m_pos; + pos.x -= 150.0f; + m_terrain->MoveOnFloor(pos); + pos.y += 10.0f; + m_camera->SetScriptEye(pos); + + pos = m_object->RetPosition(0); + pos.y += 50.0f; + m_camera->SetScriptLookat(pos); + + m_engine->SetFocus(1.0f+(1.0f-m_progress)); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticule = m_time; + + // Poussière éjectée au sol. + pos = m_pos; + pos.x += (Rand()-0.5f)*10.0f; + pos.z += (Rand()-0.5f)*10.0f; + angle = Rand()*(PI*2.0f); + dist = m_progress*50.0f; + p = RotatePoint(angle, dist); + speed.x = p.x; + speed.z = p.y; + speed.y = 0.0f; + dim.x = (Rand()*15.0f+15.0f)*m_progress; + dim.y = dim.x; + if ( dim.x >= 1.0f ) + { + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f, 0.0f, 2.0f); + } + + // Particules éjectées du réacteur. + pos = m_object->RetPosition(0); + pos.y += 6.0f; + h = m_terrain->RetFloorHeight(pos)/300.0f; + speed.x = (Rand()-0.5f)*(80.0f-50.0f*h); + speed.z = (Rand()-0.5f)*(80.0f-50.0f*h); + speed.y = -(Rand()*(h+1.0f)*40.0f+(h+1.0f)*40.0f); + dim.x = Rand()*2.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 2.0f, 10.0f, 2.0f); + + // Fumée noire du réacteur. + if ( m_progress > 0.8f ) + { + pos = m_pos; + pos.x += (Rand()-0.5f)*8.0f; + pos.z += (Rand()-0.5f)*8.0f; + pos.y += 3.0f; + speed.x = (Rand()-0.5f)*8.0f; + speed.z = (Rand()-0.5f)*8.0f; + speed.y = 0.0f; + dim.x = Rand()*4.0f+4.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE3, 4.0f, 0.0f, 2.0f); + } + } + } + else + { + m_bMotor = FALSE; // éteint le réacteur + + m_object->SetPosition(0, m_pos); // posé au sol + m_object->SetCirVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + MoveCargo(); // déplace toute la cargaison + + // Choc avec le sol. + max = (int)(50.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iCreateParticule(pos, speed, dim, PARTICRASH, time, 0.0f, 2.0f); + } + +//? m_camera->StartEffect(CE_CRASH, m_pos, 1.0f); + m_camera->StartEffect(CE_EXPLO, m_pos, 2.0f); + m_engine->SetFocus(1.0f); + m_sound->Play(SOUND_BOUM, m_posSound, 0.6f, 0.5f); + + m_phase = ABP_OPENWAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_phase == ABP_OPENWAIT ) + { + if ( m_progress < 1.0f ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticule = m_time; + + // Fumée noire du réacteur. + pos = m_pos; + pos.x += (Rand()-0.5f)*8.0f; + pos.z += (Rand()-0.5f)*8.0f; + pos.y += 3.0f; + speed.x = (Rand()-0.5f)*8.0f; + speed.z = (Rand()-0.5f)*8.0f; + speed.y = 0.0f; + dim.x = Rand()*4.0f+4.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE3, 4.0f, 0.0f, 2.0f); + } + } + else + { + m_soundChannel = m_sound->Play(SOUND_MANIP, m_posSound, 0.0f, 0.3f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 0.3f, 0.3f, 1.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.3f, 1.0f, BASE_DOOR_TIME-1.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.3f, 1.0f, SOPER_STOP); + + m_phase = ABP_OPEN; + m_progress = 0.0f; + m_speed = 1.0f/BASE_DOOR_TIME; + } + } + + if ( m_phase == ABP_OPEN ) + { + if ( m_progress < 1.0f ) + { + angle = -m_progress*124.0f*PI/180.0f; + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetAngleZ(1+i, PI/2.0f+angle); + } + + if ( m_param != PARAM_PORTICO ) + { + angle = m_progress*PI*2.0f; + p = RotatePoint(angle, -150.0f); + pos = m_pos; + pos.x += p.x; + pos.z += p.y; + m_terrain->MoveOnFloor(pos); + pos.y += 10.0f; + pos.y += m_progress*40.0f; + m_camera->SetScriptEye(pos); + + m_engine->SetFogStart(0.9f-(0.9f-m_fogStart)*m_progress); + } + } + else + { + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetAngleZ(1+i, PI/2.0f-124.0f*PI/180.0f); + } + + // Choc des portes avec le sol. + max = (int)(20.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iCreateParticule(pos, speed, dim, PARTICRASH, time, 0.0f, 2.0f); + } + + m_soundChannel = m_sound->Play(SOUND_MANIP, m_posSound, 0.3f, 1.5f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 0.3f, 1.5f, BASE_DOOR_TIME2, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.5f, 0.5f, SOPER_STOP); + + m_phase = ABP_OPEN2; + m_progress = 0.0f; + m_speed = 1.0f/BASE_DOOR_TIME2; + } + } + + if ( m_phase == ABP_OPEN2 ) + { + if ( m_progress < 1.0f ) + { + len = 7.0f-m_progress*(7.0f+11.5f); + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetPosition(10+i, D3DVECTOR(23.5f, 0.0f, len)); + m_object->SetPosition(18+i, D3DVECTOR(23.5f, 0.0f, -len)); + m_object->SetAngleX(10+i, -10.0f*PI/180.0f*m_progress); + m_object->SetAngleX(18+i, 10.0f*PI/180.0f*m_progress); + } + + if ( m_param != PARAM_PORTICO ) + { + angle = m_progress*PI/2.0f; + p = RotatePoint(angle, -150.0f); + pos = m_pos; + pos.x += p.x; + pos.z += p.y; + m_terrain->MoveOnFloor(pos); + pos.y += 10.0f; + pos.y += m_progress*40.0f; + m_camera->SetScriptEye(pos); + + m_engine->SetFogStart(0.9f-(0.9f-m_fogStart)*m_progress); + } + } + else + { + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetPosition(10+i, D3DVECTOR(23.5f, 0.0f, -11.5f)); + m_object->SetPosition(18+i, D3DVECTOR(23.5f, 0.0f, 11.5f)); + m_object->SetAngleX(10+i, -10.0f*PI/180.0f); + m_object->SetAngleX(18+i, 10.0f*PI/180.0f); + } + + m_phase = ABP_LDWAIT; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == ABP_LDWAIT ) + { + if ( m_progress >= 1.0f ) + { + FreezeCargo(FALSE); // libère toute la cargaison + + if ( m_param != PARAM_PORTICO ) + { + m_main->SetMovieLock(FALSE); // on peut jouer ! + + pObj = m_main->RetSelectObject(); + m_main->SelectObject(pObj); + m_camera->SetObject(pObj); + if ( pObj == 0 ) + { + m_camera->SetType(CAMERA_BACK); + } + else + { + m_camera->SetType(pObj->RetCameraType()); + m_camera->SetDist(pObj->RetCameraDist()); + } + m_sound->Play(SOUND_BOUM, m_object->RetPosition(0)); + m_soundChannel = -1; + + m_engine->SetFogStart(m_fogStart); + } + + m_bOpen = TRUE; + m_phase = ABP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == ABP_CLOSE2 ) + { + if ( m_progress < 1.0f ) + { + len = 7.0f-(1.0f-m_progress)*(7.0f+11.5f); + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetPosition(10+i, D3DVECTOR(23.5f, 0.0f, len)); + m_object->SetPosition(18+i, D3DVECTOR(23.5f, 0.0f, -len)); + m_object->SetAngleX(10+i, -10.0f*PI/180.0f*(1.0f-m_progress)); + m_object->SetAngleX(18+i, 10.0f*PI/180.0f*(1.0f-m_progress)); + } + } + else + { + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetPosition(10+i, D3DVECTOR(23.5f, 0.0f, 7.0f)); + m_object->SetPosition(18+i, D3DVECTOR(23.5f, 0.0f, -7.0f)); + m_object->SetAngleX(10+i, 0.0f); + m_object->SetAngleX(18+i, 0.0f); + } + + m_soundChannel = m_sound->Play(SOUND_MANIP, m_posSound, 0.0f, 0.3f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 0.3f, 0.3f, 1.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.3f, 1.0f, BASE_DOOR_TIME-1.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.3f, 1.0f, SOPER_STOP); + + m_phase = ABP_CLOSE; + m_progress = 0.0f; + m_speed = 1.0f/BASE_DOOR_TIME; + } + } + + if ( m_phase == ABP_CLOSE ) + { + if ( m_progress < 1.0f ) + { + angle = -(1.0f-m_progress)*124.0f*PI/180.0f; + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetAngleZ(1+i, PI/2.0f+angle); + } + } + else + { + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetAngleZ(1+i, PI/2.0f); + } + m_bMotor = TRUE; // allume le réacteur + + // Choc de la fermeture des portes. + max = (int)(20.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iCreateParticule(pos, speed, dim, PARTICRASH, time); + } + m_sound->Play(SOUND_BOUM, m_object->RetPosition(0)); + + m_soundChannel = -1; + m_bOpen = FALSE; + m_phase = ABP_TOWAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_phase == ABP_TOWAIT ) + { + if ( m_progress < 1.0f ) + { + if ( m_soundChannel == -1 ) + { + m_soundChannel = m_sound->Play(SOUND_FLY, m_posSound, 0.0f, 0.5f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 0.5f, 2.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.3f, 2.0f, BASE_TAKO_TIME, SOPER_STOP); + } + + vibCir.z = sinf(m_time*PI*19.01f)*(PI/400.0f); + vibCir.x = sinf(m_time*PI*19.53f)*(PI/400.0f); + vibCir.y = 0.0f; + vibCir *= m_progress*1.0f; + m_object->SetCirVibration(vibCir); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + // Particules éjectées du réacteur. + pos = m_object->RetPosition(0); + pos.y += 6.0f; + speed.x = (Rand()-0.5f)*160.0f; + speed.z = (Rand()-0.5f)*160.0f; + speed.y = -(Rand()*10.0f+10.0f); + dim.x = Rand()*2.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 2.0f, 10.0f, 2.0f); + } + + m_engine->SetFogStart(m_fogStart+(0.9f-m_fogStart)*m_progress); + } + else + { + m_engine->SetFogStart(0.9f); + + m_phase = ABP_TAKEOFF; + m_progress = 0.0f; + m_speed = 1.0f/BASE_TAKO_TIME; + } + } + + if ( m_phase == ABP_TAKEOFF ) + { + if ( m_progress < 1.0f ) + { + pos = m_pos; + pos.y += powf(m_progress, 2.0f)*600.0f; + m_object->SetPosition(0, pos); + MoveCargo(); // déplace toute la cargaison + + vibCir.z = sinf(m_time*PI*19.01f)*(PI/400.0f); + vibCir.x = sinf(m_time*PI*19.53f)*(PI/400.0f); + vibCir.y = 0.0f; + m_object->SetCirVibration(vibCir); + + pos = m_pos; + pos.x -= 110.0f+m_progress*250.0f; + m_terrain->MoveOnFloor(pos); + pos.y += 10.0f; + m_camera->SetScriptEye(pos); + + pos = m_object->RetPosition(0); + pos.y += 50.0f; + m_camera->SetScriptLookat(pos); + + m_engine->SetFocus(1.0f+m_progress); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticule = m_time; + + // Poussière éjectée au sol. + pos = m_pos; + pos.x += (Rand()-0.5f)*10.0f; + pos.z += (Rand()-0.5f)*10.0f; + angle = Rand()*(PI*2.0f); + dist = (1.0f-m_progress)*50.0f; + p = RotatePoint(angle, dist); + speed.x = p.x; + speed.z = p.y; + speed.y = 0.0f; + dim.x = (Rand()*10.0f+10.0f)*(1.0f-m_progress); + dim.y = dim.x; + if ( dim.x >= 1.0f ) + { + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f, 0.0f, 2.0f); + } + + // Particules éjectées du réacteur. + pos = m_object->RetPosition(0); + pos.y += 6.0f; + speed.x = (Rand()-0.5f)*40.0f; + speed.z = (Rand()-0.5f)*40.0f; + time = 5.0f+150.0f*m_progress; + speed.y = -(Rand()*time+time); + time = 2.0f+m_progress*12.0f; + dim.x = Rand()*time+time; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 2.0f, 10.0f, 2.0f); + + // Fumée noire du réacteur. + pos = m_object->RetPosition(0); + pos.y += 3.0f; + speed.x = (Rand()-0.5f)*10.0f*(4.0f-m_progress*3.0f); + speed.z = (Rand()-0.5f)*10.0f*(4.0f-m_progress*3.0f); + speed.y = 0.0f; + dim.x = Rand()*20.0f+20.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE3, 10.0f, 0.0f, 2.0f); + } + } + else + { + m_soundChannel = -1; + m_event->MakeEvent(newEvent, EVENT_WIN); + m_event->AddEvent(newEvent); + + m_phase = ABP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_phase == ABP_PORTICO_MOVE ) // avance du portique ? + { + if ( m_progress < 1.0f ) + { + pos = m_object->RetPosition(0); + pos.z -= event.rTime*5.0f; + m_object->SetPosition(0, pos); + MoveCargo(); // déplace toute la cargaison + } + else + { + m_phase = ABP_PORTICO_WAIT1; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == ABP_PORTICO_WAIT1 ) // attente du portique ? + { + if ( m_progress >= 1.0f ) + { + m_phase = ABP_PORTICO_DOWN; + m_progress = 0.0f; + m_speed = 1.0f/BASE_PORTICO_TIME_DOWN; + } + } + + if ( m_phase == ABP_PORTICO_DOWN ) // descente du portique ? + { + if ( m_progress < 1.0f ) + { + pos = m_object->RetPosition(0); + pos.y -= event.rTime*(10.0f/BASE_PORTICO_TIME_DOWN); + m_object->SetPosition(0, pos); + MoveCargo(); // déplace toute la cargaison + } + else + { + // Choc avec le sol. + max = (int)(50.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iCreateParticule(pos, speed, dim, PARTICRASH, time, 0.0f, 2.0f); + } + + m_phase = ABP_PORTICO_WAIT2; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == ABP_PORTICO_WAIT2 ) // attente du portique ? + { + if ( m_progress >= 1.0f ) + { + m_phase = ABP_PORTICO_OPEN; + m_progress = 0.0f; + m_speed = 1.0f/BASE_PORTICO_TIME_OPEN; + } + } + + if ( m_phase == ABP_PORTICO_OPEN ) // ouverture du portique ? + { + if ( m_progress < 1.0f ) + { + } + else + { + m_phase = ABP_OPEN; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_phase == ABP_TRANSIT_MOVE ) // transit dans l'espace ? + { + if ( m_progress < 1.0f ) + { + pos = m_object->RetPosition(0); + pos.x += event.rTime*(2000.0f/BASE_TRANSIT_TIME); + m_object->SetPosition(0, pos); + pos.x += 60.0f; + m_camera->SetScriptLookat(pos); + } + else + { + m_object->SetAngleZ(0, 0.0f); + + m_param = PARAM_LANDING; + m_phase = ABP_START; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + + EndTransit(); + + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.8f, 0.01f, SOPER_STOP); + m_soundChannel = -1; + } + goto begin; + } + } + + if ( m_bMotor ) + { + if ( m_lastMotorParticule+m_engine->ParticuleAdapt(0.02f) <= m_time ) + { + m_lastMotorParticule = m_time; + + mat = m_object->RetWorldMatrix(0); + + if ( event.rTime == 0.0f ) + { + vSpeed = 0.0f; + } + else + { + pos = m_object->RetPosition(0); + if ( m_phase == ABP_TRANSIT_MOVE ) + { + vSpeed = (pos.x-iPos.x)/event.rTime; + } + else + { + vSpeed = (pos.y-iPos.y)/event.rTime; + } + if ( vSpeed < 0.0f ) vSpeed *= 1.5f; + } + + pos = D3DVECTOR(0.0f, 6.0f, 0.0f); + speed.x = (Rand()-0.5f)*4.0f; + speed.z = (Rand()-0.5f)*4.0f; + speed.y = vSpeed*0.8f-(8.0f+Rand()*6.0f); + speed += pos; + pos = Transform(*mat, pos); + speed = Transform(*mat, speed); + speed -= pos; + + dim.x = 4.0f+Rand()*4.0f; + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, PARTIBASE, 3.0f, 0.0f, 0.0f); + + if ( m_phase == ABP_TRANSIT_MOVE ) + { + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 12.0f; + dim.y = dim.x; + pos = D3DVECTOR(0.0f, 7.0f, 0.0f); + pos.x += (Rand()-0.5f)*2.0f; pos.z += (Rand()-0.5f)*2.0f; + pos = Transform(*mat, pos); + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 1.0f, 0.0f, 0.0f); + + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 4.0f; + dim.y = dim.x; + pos = D3DVECTOR(42.0f, 0.0f, 17.0f); + pos.x += (Rand()-0.5f)*2.0f; pos.z += (Rand()-0.5f)*2.0f; + pos = Transform(*mat, pos); + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 0.5f, 0.0f, 0.0f); + pos = D3DVECTOR(17.0f, 0.0f, 42.0f); + pos.x += (Rand()-0.5f)*2.0f; pos.z += (Rand()-0.5f)*2.0f; + pos = Transform(*mat, pos); + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 0.5f, 0.0f, 0.0f); + pos = D3DVECTOR(42.0f, 0.0f, -17.0f); + pos.x += (Rand()-0.5f)*2.0f; pos.z += (Rand()-0.5f)*2.0f; + pos = Transform(*mat, pos); + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 0.5f, 0.0f, 0.0f); + pos = D3DVECTOR(17.0f, 0.0f, -42.0f); + pos.x += (Rand()-0.5f)*2.0f; pos.z += (Rand()-0.5f)*2.0f; + pos = Transform(*mat, pos); + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 0.5f, 0.0f, 0.0f); + pos = D3DVECTOR(-42.0f, 0.0f, 17.0f); + pos.x += (Rand()-0.5f)*2.0f; pos.z += (Rand()-0.5f)*2.0f; + pos = Transform(*mat, pos); + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 0.5f, 0.0f, 0.0f); + pos = D3DVECTOR(-17.0f, 0.0f, 42.0f); + pos.x += (Rand()-0.5f)*2.0f; pos.z += (Rand()-0.5f)*2.0f; + pos = Transform(*mat, pos); + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 0.5f, 0.0f, 0.0f); + pos = D3DVECTOR(-42.0f, 0.0f, -17.0f); + pos.x += (Rand()-0.5f)*2.0f; pos.z += (Rand()-0.5f)*2.0f; + pos = Transform(*mat, pos); + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 0.5f, 0.0f, 0.0f); + pos = D3DVECTOR(-17.0f, 0.0f, -42.0f); + pos.x += (Rand()-0.5f)*2.0f; pos.z += (Rand()-0.5f)*2.0f; + pos = Transform(*mat, pos); + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 0.5f, 0.0f, 0.0f); + + pos = D3DVECTOR(42.0f, -2.0f, 17.0f); + pos = Transform(*mat, pos); + m_particule->SetPosition(m_partiChannel[0], pos); + pos = D3DVECTOR(17.0f, -2.0f, 42.0f); + pos = Transform(*mat, pos); + m_particule->SetPosition(m_partiChannel[1], pos); + pos = D3DVECTOR(42.0f, -2.0f, -17.0f); + pos = Transform(*mat, pos); + m_particule->SetPosition(m_partiChannel[2], pos); + pos = D3DVECTOR(17.0f, -2.0f, -42.0f); + pos = Transform(*mat, pos); + m_particule->SetPosition(m_partiChannel[3], pos); + pos = D3DVECTOR(-42.0f, -2.0f, 17.0f); + pos = Transform(*mat, pos); + m_particule->SetPosition(m_partiChannel[4], pos); + pos = D3DVECTOR(-17.0f, -2.0f, 42.0f); + pos = Transform(*mat, pos); + m_particule->SetPosition(m_partiChannel[5], pos); + pos = D3DVECTOR(-42.0f, -2.0f, -17.0f); + pos = Transform(*mat, pos); + m_particule->SetPosition(m_partiChannel[6], pos); + pos = D3DVECTOR(-17.0f, -2.0f, -42.0f); + pos = Transform(*mat, pos); + m_particule->SetPosition(m_partiChannel[7], pos); + } + } + } + + if ( m_soundChannel != -1 ) + { + pos = m_engine->RetEyePt(); + m_sound->Position(m_soundChannel, pos); + } + + return TRUE; +} + +// Stoppe l'automate. + +BOOL CAutoBase::Abort() +{ + Event newEvent; + CObject* pObj; + int i; + + if ( m_phase == ABP_TRANSIT_MOVE ) // transit ? + { + m_object->SetAngleZ(0, 0.0f); + + m_param = PARAM_LANDING; + m_phase = ABP_START; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + + EndTransit(); + + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.8f, 0.01f, SOPER_STOP); + m_soundChannel = -1; + } + return TRUE; + } + + if ( m_param == PARAM_PORTICO ) // porté par le portique ? + { + m_object->SetPosition(0, m_finalPos); + MoveCargo(); // déplace toute la cargaison + + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetAngleZ(1+i, PI/2.0f-124.0f*PI/180.0f); + m_object->SetAngleX(10+i, -10.0f*PI/180.0f); + m_object->SetAngleX(18+i, 10.0f*PI/180.0f); + m_object->SetPosition(10+i, D3DVECTOR(23.5f, 0.0f, -11.5f)); + m_object->SetPosition(18+i, D3DVECTOR(23.5f, 0.0f, 11.5f)); + } + } + else + { + if ( m_phase == ABP_LAND || + m_phase == ABP_OPENWAIT || + m_phase == ABP_OPEN || + m_phase == ABP_OPEN2 ) // atterrissage ? + { + m_bMotor = FALSE; // éteint le réacteur + m_bOpen = TRUE; + + m_object->SetPosition(0, m_pos); // posé au sol + m_object->SetCirVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + MoveCargo(); // déplace toute la cargaison + + for ( i=0 ; i<8 ; i++ ) + { + m_object->SetAngleZ(1+i, PI/2.0f-124.0f*PI/180.0f); + m_object->SetAngleX(10+i, -10.0f*PI/180.0f); + m_object->SetAngleX(18+i, 10.0f*PI/180.0f); + m_object->SetPosition(10+i, D3DVECTOR(23.5f, 0.0f, -11.5f)); + m_object->SetPosition(18+i, D3DVECTOR(23.5f, 0.0f, 11.5f)); + } + + m_main->SetMovieLock(FALSE); // on peut jouer ! + + pObj = m_main->RetSelectObject(); + m_main->SelectObject(pObj); + m_camera->SetObject(pObj); + if ( pObj == 0 ) + { + m_camera->SetType(CAMERA_BACK); + } + else + { + m_camera->SetType(pObj->RetCameraType()); + m_camera->SetDist(pObj->RetCameraDist()); + } + + m_engine->SetFogStart(m_fogStart); + } + + if ( m_phase == ABP_CLOSE2 || + m_phase == ABP_CLOSE || + m_phase == ABP_TOWAIT || + m_phase == ABP_TAKEOFF ) // décollage ? + { + m_event->MakeEvent(newEvent, EVENT_WIN); + m_event->AddEvent(newEvent); + } + } + + m_object->SetAngleZ(0, 0.0f); + FreezeCargo(FALSE); // libère toute la cargaison + + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + m_phase = ABP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoBase::RetError() +{ + return ERR_OK; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoBase::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, dim, ddim; + float ox, oy, sx, sy; + float sleep, delay, magnetic, progress; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + dim.x = 33.0f/640.0f; + dim.y = 33.0f/480.0f; + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + ddim.x = dim.x*1.5f; + ddim.y = dim.y*1.5f; + +//? pos.x = ox+sx*7.25f; +//? pos.y = oy+sy*0.25f; +//? pw->CreateButton(pos, ddim, 63, EVENT_OBJECT_BHELP); + + pos.x = ox+sx*8.00f; + pos.y = oy+sy*0.25f; + pw->CreateButton(pos, ddim, 28, EVENT_OBJECT_BTAKEOFF); + + if ( m_blitz->GetStatus(sleep, delay, magnetic, progress) ) + { + pos.x = ox+sx*10.2f; + pos.y = oy+sy*0.5f; + ddim.x = dim.x*1.0f; + ddim.y = dim.y*1.0f; + pw->CreateButton(pos, ddim, 41, EVENT_OBJECT_LIMIT); + } + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 100, EVENT_OBJECT_TYPE); + + UpdateInterface(); + + return TRUE; +} + +// Met à jour l'état de tous les boutons de l'interface. + +void CAutoBase::UpdateInterface() +{ + CWindow* pw; + + if ( !m_object->RetSelect() ) return; + + CAuto::UpdateInterface(); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); +} + + +// Gèle ou libère toute la cargaison. + +void CAutoBase::FreezeCargo(BOOL bFreeze) +{ + CObject* pObj; + CPhysics* physics; + D3DVECTOR oPos; + float dist; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + pObj->SetCargo(FALSE); + + if ( pObj == m_object ) continue; // soi-même ? + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + + oPos = pObj->RetPosition(0); + dist = Length2d(m_pos, oPos); + if ( dist < 32.0f ) + { + if ( bFreeze ) + { + pObj->SetCargo(TRUE); + } + + physics = pObj->RetPhysics(); + if ( physics != 0 ) + { + physics->SetFreeze(bFreeze); + } + } + } +} + +// Déplace verticalement toute la cargaison avec le vaisseau. + +void CAutoBase::MoveCargo() +{ + CObject* pObj; + D3DVECTOR oPos, sPos; + int i; + + sPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetCargo() ) continue; + + oPos = pObj->RetPosition(0); + oPos.y = sPos.y+30.0f; + oPos.y += pObj->RetCharacter()->height; + oPos.x += sPos.x-m_lastPos.x; + oPos.z += sPos.z-m_lastPos.z; + pObj->SetPosition(0, oPos); + } + + m_lastPos = sPos; +} + + +// Vérifie s'il est possible de fermer les portes. + +Error CAutoBase::CheckCloseDoor() +{ + CObject* pObj; + D3DVECTOR oPos; + ObjectType type; + float oRad, dist; + int i, j; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; // soi-même ? + if ( !pObj->RetActif() ) continue; // inactif ? + + type = pObj->RetType(); + if ( type == OBJECT_PORTICO ) continue; + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRad) ) + { + dist = Length2d(m_pos, oPos); + if ( dist+oRad > 32.0f && + dist-oRad < 72.0f ) + { + return ERR_BASE_DLOCK; + } + + if ( type == OBJECT_HUMAN && + dist+oRad > 32.0f ) + { + return ERR_BASE_DHUMAN; + } + } + } + return ERR_OK; +} + + +// Début d'un transit. + +void CAutoBase::BeginTransit() +{ + BOOL bFull, bQuarter; + + if ( m_param == PARAM_TRANSIT2 ) + { + strcpy(m_bgBack, "back01.tga"); // nuages oranges/bleus + } + else if ( m_param == PARAM_TRANSIT3 ) + { + strcpy(m_bgBack, "back22.tga"); // nuages bleutés + } + else + { +#if _DEMO + strcpy(m_bgBack, "back46b.tga"); // étoiles +#else + strcpy(m_bgBack, "back46.tga"); // étoiles +#endif + } + + m_engine->SetFogStart(0.9f); // presque pas de brouillard + m_engine->SetDeepView(2000.0f); // on voit très loin + m_engine->ApplyChange(); + + m_engine->RetBackground(m_bgName, m_bgUp, m_bgDown, m_bgCloudUp, m_bgCloudDown, bFull, bQuarter); + m_engine->FreeTexture(m_bgName); + + m_engine->SetBackground(m_bgBack, 0x00000000, 0x00000000, 0x00000000, 0x00000000); + m_engine->LoadTexture(m_bgBack); + + m_cloud->SetEnable(FALSE); // cache les nuages + m_planet->SetMode(1); +} + +// Fin d'un transit. + +void CAutoBase::EndTransit() +{ + m_engine->SetFogStart(m_fogStart); // remet brouillard initial + m_engine->SetDeepView(m_deepView); // remet profondeur initiale + m_engine->ApplyChange(); + + m_engine->FreeTexture(m_bgBack); + + m_engine->SetBackground(m_bgName, m_bgUp, m_bgDown, m_bgCloudUp, m_bgCloudDown); + m_engine->LoadTexture(m_bgName); + + m_cloud->SetEnable(TRUE); // remet les nuages + m_planet->SetMode(0); + + m_main->StartMusic(); +} + diff --git a/src/autobase.h b/src/autobase.h new file mode 100644 index 00000000..bfc02208 --- /dev/null +++ b/src/autobase.h @@ -0,0 +1,102 @@ +// autobase.h + +#ifndef _AUTOBASE_H_ +#define _AUTOBASE_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +#define PARAM_STOP 0 // run=0 -> stoppé et ouvert +#define PARAM_LANDING 1 // run=1 -> atterrissage +#define PARAM_PORTICO 2 // run=2 -> porté par le portique +#define PARAM_FIXSCENE 3 // run=3 -> stoppé et ouvert pour win/lost +#define PARAM_TRANSIT1 11 // run=11 -> transit dans l'espace +#define PARAM_TRANSIT2 12 // run=12 -> transit dans l'espace +#define PARAM_TRANSIT3 13 // run=13 -> transit dans l'espace + + +enum AutoBasePhase +{ + ABP_WAIT = 1, // attend + ABP_START = 2, // démarrage + + ABP_LAND = 3, // atterissage + ABP_OPENWAIT = 4, // attente avant ouverture + ABP_OPEN = 5, // ouvre les portes + ABP_OPEN2 = 6, // ouvre les suppléments + ABP_LDWAIT = 7, // attend + + ABP_CLOSE2 = 8, // ferme les suppléments + ABP_CLOSE = 9, // ferme les portes + ABP_TOWAIT = 10, // attente avant décollage + ABP_TAKEOFF = 11, // décollage + + ABP_PORTICO_MOVE = 12, // portique avance + ABP_PORTICO_WAIT1= 13, // portique attend + ABP_PORTICO_DOWN = 14, // portique descend + ABP_PORTICO_WAIT2= 15, // portique attend + ABP_PORTICO_OPEN = 16, // portique s'ouvre + + ABP_TRANSIT_MOVE = 17, // transit - déplacement +}; + + + +class CAutoBase : public CAuto +{ +public: + CAutoBase(CInstanceManager* iMan, CObject* object); + ~CAutoBase(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + void Start(int param); + BOOL EventProcess(const Event &event); + BOOL Abort(); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + +protected: + void UpdateInterface(); + void FreezeCargo(BOOL bFreeze); + void MoveCargo(); + Error CheckCloseDoor(); + void BeginTransit(); + void EndTransit(); + +protected: + AutoBasePhase m_phase; + BOOL m_bOpen; + float m_progress; + float m_speed; + float m_lastParticule; + float m_lastMotorParticule; + float m_fogStart; + float m_deepView; + D3DVECTOR m_pos; + D3DVECTOR m_posSound; + D3DVECTOR m_finalPos; + D3DVECTOR m_lastPos; + int m_param; + int m_soundChannel; + int m_partiChannel[8]; + + char m_bgBack[100]; + char m_bgName[100]; + D3DCOLOR m_bgUp; + D3DCOLOR m_bgDown; + D3DCOLOR m_bgCloudUp; + D3DCOLOR m_bgCloudDown; +}; + + +#endif //_AUTOBASE_H_ diff --git a/src/autoconvert.cpp b/src/autoconvert.cpp new file mode 100644 index 00000000..7a11ac32 --- /dev/null +++ b/src/autoconvert.cpp @@ -0,0 +1,532 @@ +// autoconvert.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "sound.h" +#include "displaytext.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autoconvert.h" + + + + +// Constructeur de l'objet. + +CAutoConvert::CAutoConvert(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); + m_phase = ACP_STOP; + m_bResetDelete = FALSE; + m_soundChannel = -1; +} + +// Destructeur de l'objet. + +CAutoConvert::~CAutoConvert() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoConvert::DeleteObject(BOOL bAll) +{ + CObject* fret; + + if ( !bAll ) + { + fret = SearchStone(OBJECT_STONE); + if ( fret != 0 ) + { + fret->DeleteObject(); // détruit la pierre + delete fret; + } + } + + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoConvert::Init() +{ + m_phase = ACP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastParticule = 0.0f; + + CAuto::Init(); +} + + +// Gestion d'un événement. + +BOOL CAutoConvert::EventProcess(const Event &event) +{ + CObject* fret; + D3DVECTOR pos, speed; + FPOINT dim, c, p; + float angle; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + + angle = (Rand()-0.5f)*0.3f; + m_object->SetAngleY(1, angle); + m_object->SetAngleY(2, angle); + m_object->SetAngleY(3, angle+PI); + + m_object->SetAngleX(2, -PI*0.35f*(0.8f+Rand()*0.2f)); + m_object->SetAngleX(3, -PI*0.35f*(0.8f+Rand()*0.2f)); + } + return TRUE; + } + + EventProgress(event.rTime); + + if ( m_phase == ACP_STOP ) return TRUE; + + if ( m_phase == ACP_WAIT ) + { + if ( m_progress >= 1.0f ) + { + fret = SearchStone(OBJECT_STONE); // pierre à transformer ? + if ( fret == 0 || SearchVehicle() ) + { + m_phase = ACP_WAIT; // attend encore ... + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + else + { + fret->SetLock(TRUE); // pierre plus utilisable + + SetBusy(TRUE); + InitProgressTotal(3.0f+10.0f+1.5f); + UpdateInterface(); + + m_sound->Play(SOUND_OPEN, m_object->RetPosition(0), 1.0f, 1.0f); + m_bSoundClose = FALSE; + + m_phase = ACP_CLOSE; + m_progress = 0.0f; + m_speed = 1.0f/3.0f; + } + } + } + + if ( m_phase == ACP_CLOSE ) + { + if ( m_progress < 1.0f ) + { + if ( m_progress >= 0.8f && !m_bSoundClose ) + { + m_bSoundClose = TRUE; + m_sound->Play(SOUND_CLOSE, m_object->RetPosition(0), 1.0f, 0.8f); + } + angle = -PI*0.35f*(1.0f-Bounce(m_progress, 0.85f, 0.05f)); + m_object->SetAngleX(2, angle); + m_object->SetAngleX(3, angle); + } + else + { + m_object->SetAngleX(2, 0.0f); + m_object->SetAngleX(3, 0.0f); + + m_soundChannel = m_sound->Play(SOUND_CONVERT, m_object->RetPosition(0), 0.0f, 0.25f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 0.25f, 0.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.00f, 4.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 0.25f, 4.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.25f, 0.5f, SOPER_STOP); + + m_phase = ACP_ROTATE; + m_progress = 0.0f; + m_speed = 1.0f/10.0f; + } + } + + if ( m_phase == ACP_ROTATE ) + { + if ( m_progress < 1.0f ) + { + if ( m_progress < 0.5f ) + { + angle = powf((m_progress*2.0f)*5.0f, 2.0f); // accélère + } + else + { + angle = -powf((2.0f-m_progress*2.0f)*5.0f, 2.0f); // freine + } + m_object->SetAngleY(1, angle); + m_object->SetAngleY(2, angle); + m_object->SetAngleY(3, angle+PI); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + c.x = pos.x; + c.y = pos.z; + p.x = c.x; + p.y = c.y+6.0f; + p = RotatePoint(c, Rand()*PI*2.0f, p); + pos.x = p.x; + pos.z = p.y; + pos.y += 1.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = Rand()*2.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGAS, 1.0f, 0.0f, 0.0f); + } + } + else + { + m_object->SetAngleY(1, 0.0f); + m_object->SetAngleY(2, 0.0f); + m_object->SetAngleY(3, PI); + + fret = SearchStone(OBJECT_STONE); + if ( fret != 0 ) + { + m_bResetDelete = ( fret->RetResetCap() != RESET_NONE ); + fret->DeleteObject(); // détruit la pierre + delete fret; + } + + CreateMetal(); // c'est du métal + m_sound->Play(SOUND_OPEN, m_object->RetPosition(0), 1.0f, 1.5f); + + m_phase = ACP_OPEN; + m_progress = 0.0f; + m_speed = 1.0f/1.5f; + } + } + + if ( m_phase == ACP_OPEN ) + { + if ( m_progress < 1.0f ) + { + angle = -PI*0.35f*Bounce(m_progress, 0.7f, 0.2f); + m_object->SetAngleX(2, angle); + m_object->SetAngleX(3, angle); + + if ( m_progress < 0.9f && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + pos.x += (Rand()-0.5f)*6.0f; + pos.z += (Rand()-0.5f)*6.0f; + pos.y += Rand()*4.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = Rand()*4.0f+3.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLUE, 1.0f, 0.0f, 0.0f); + } + } + else + { + m_soundChannel = -1; + m_object->SetAngleX(2, -PI*0.35f); + m_object->SetAngleX(3, -PI*0.35f); + + SetBusy(FALSE); + UpdateInterface(); + + m_phase = ACP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + return TRUE; +} + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoConvert::RetError() +{ + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + + if ( m_phase == ACP_WAIT ) return ERR_CONVERT_EMPTY; + return ERR_OK; +} + +// Annule la transformation en cours. + +BOOL CAutoConvert::Abort() +{ + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + m_object->SetAngleY(1, 0.0f); + m_object->SetAngleY(2, 0.0f); + m_object->SetAngleY(3, PI); + m_object->SetAngleX(2, -PI*0.35f); + m_object->SetAngleX(3, -PI*0.35f); + + m_phase = ACP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + + SetBusy(FALSE); + UpdateInterface(); + + return TRUE; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoConvert::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 103, EVENT_OBJECT_TYPE); + + return TRUE; +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoConvert::Write(char *line) +{ + char name[100]; + + if ( m_phase == ACP_STOP || + m_phase == ACP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoConvert::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoConvertPhase)OpInt(line, "aPhase", ACP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastParticule = 0.0f; + + return TRUE; +} + + +// Cherche l'objet avant ou pendant transformation. + +CObject* CAutoConvert::SearchStone(ObjectType type) +{ + CObject* pObj; + D3DVECTOR cPos, oPos; + ObjectType oType; + float dist; + int i; + + cPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + oType = pObj->RetType(); + if ( oType != type ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, cPos); + + if ( dist <= 5.0f ) return pObj; + } + + return 0; +} + +// Cherche si un véhicule est trop proche. + +BOOL CAutoConvert::SearchVehicle() +{ + CObject* pObj; + D3DVECTOR cPos, oPos; + ObjectType type; + float oRadius, dist; + int i; + + cPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_HUMAN && + type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis && + type != OBJECT_MOBILErt && + type != OBJECT_MOBILErc && + type != OBJECT_MOBILErr && + type != OBJECT_MOBILErs && + type != OBJECT_MOBILEsa && + type != OBJECT_MOBILEtg && + type != OBJECT_MOBILEft && + type != OBJECT_MOBILEtt && + type != OBJECT_MOBILEwt && + type != OBJECT_MOBILEit && + type != OBJECT_MOBILEdr && + type != OBJECT_METAL && + type != OBJECT_URANIUM && + type != OBJECT_POWER && + type != OBJECT_ATOMIC && + type != OBJECT_BULLET && + type != OBJECT_BBOX && + type != OBJECT_TNT && + type != OBJECT_MOTHER && + type != OBJECT_ANT && + type != OBJECT_SPIDER && + type != OBJECT_BEE && + type != OBJECT_WORM ) continue; + + if ( !pObj->GetCrashSphere(0, oPos, oRadius) ) continue; + dist = Length(oPos, cPos)-oRadius; + + if ( dist < 8.0f ) return TRUE; + } + + return FALSE; +} + +// Crée un objet métal. + +void CAutoConvert::CreateMetal() +{ + D3DVECTOR pos; + float angle; + CObject* fret; + + pos = m_object->RetPosition(0); + angle = m_object->RetAngleY(0); + + fret = new CObject(m_iMan); + if ( !fret->CreateResource(pos, angle, OBJECT_METAL) ) + { + delete fret; + m_displayText->DisplayError(ERR_TOOMANY, m_object); + return; + } + + if ( m_bResetDelete ) + { + fret->SetResetCap(RESET_DELETE); + } + + m_displayText->DisplayError(INFO_CONVERT, m_object); +} + diff --git a/src/autoconvert.h b/src/autoconvert.h new file mode 100644 index 00000000..3cdbf57b --- /dev/null +++ b/src/autoconvert.h @@ -0,0 +1,62 @@ +// autoconvert.h + +#ifndef _AUTOCONVERT_H_ +#define _AUTOCONVERT_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoConvertPhase +{ + ACP_STOP = 1, + ACP_WAIT = 2, + ACP_CLOSE = 3, // ferme le couvervle + ACP_ROTATE = 4, // tourne le couvercle + ACP_OPEN = 5, // ouvre le couvercle +}; + + + +class CAutoConvert : public CAuto +{ +public: + CAutoConvert(CInstanceManager* iMan, CObject* object); + ~CAutoConvert(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + BOOL Abort(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + CObject* SearchStone(ObjectType type); + BOOL SearchVehicle(); + void CreateMetal(); + +protected: + AutoConvertPhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastParticule; + BOOL m_bResetDelete; + BOOL m_bSoundClose; + int m_soundChannel; +}; + + +#endif //_AUTOCONVERT_H_ diff --git a/src/autoderrick.cpp b/src/autoderrick.cpp new file mode 100644 index 00000000..077dc357 --- /dev/null +++ b/src/autoderrick.cpp @@ -0,0 +1,591 @@ +// autoderrick.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "sound.h" +#include "displaytext.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autoderrick.h" + + + +#define DERRICK_DELAY 10.0f // durée de l'extraction +#define DERRICK_DELAYu 30.0f // idem, mais pour l'uranium + + + + +// Constructeur de l'objet. + +CAutoDerrick::CAutoDerrick(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); + m_phase = ADP_WAIT; // en pause jusqu'au premier Init() + m_soundChannel = -1; +} + +// Destructeur de l'objet. + +CAutoDerrick::~CAutoDerrick() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoDerrick::DeleteObject(BOOL bAll) +{ + CObject* fret; + + if ( !bAll ) + { + fret = SearchFret(); + if ( fret != 0 && fret->RetLock() ) + { + fret->DeleteObject(); + delete fret; + } + } + + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoDerrick::Init() +{ + D3DMATRIX* mat; + D3DVECTOR pos; + TerrainRes res; + + pos = m_object->RetPosition(0); + res = m_terrain->RetResource(pos); + + if ( res == TR_STONE || + res == TR_URANIUM || + res == TR_KEYa || + res == TR_KEYb || + res == TR_KEYc || + res == TR_KEYd ) + { + m_type = OBJECT_FRET; + if ( res == TR_STONE ) m_type = OBJECT_STONE; + if ( res == TR_URANIUM ) m_type = OBJECT_URANIUM; + if ( res == TR_KEYa ) m_type = OBJECT_KEYa; + if ( res == TR_KEYb ) m_type = OBJECT_KEYb; + if ( res == TR_KEYc ) m_type = OBJECT_KEYc; + if ( res == TR_KEYd ) m_type = OBJECT_KEYd; + + m_phase = ADP_EXCAVATE; + m_progress = 0.0f; + m_speed = 1.0f/(m_type==OBJECT_URANIUM?DERRICK_DELAYu:DERRICK_DELAY); + } + else + { + m_phase = ADP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f; + } + + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastParticule = 0.0f; + m_lastTrack = 0.0f; + + pos = D3DVECTOR(7.0f, 0.0f, 0.0f); + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + m_terrain->MoveOnFloor(pos); + m_fretPos = pos; +} + + +// Gestion d'un événement. + +BOOL CAutoDerrick::EventProcess(const Event &event) +{ + CObject* fret; + D3DVECTOR pos, speed; + FPOINT dim; + float angle, duration, factor; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_phase == ADP_WAIT ) return TRUE; + + m_progress += event.rTime*m_speed; + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + + pos.x = 0.0f; + pos.z = 0.0f; + pos.y = -2.0f*Rand(); + m_object->SetPosition(1, pos); // monte/descend le foret + + m_object->SetAngleY(1, Rand()*0.5f); // tourne le foret + } + return TRUE; + } + + if ( m_phase == ADP_EXCAVATE ) + { + if ( m_soundChannel == -1 ) + { + if ( m_type == OBJECT_URANIUM ) + { + factor = DERRICK_DELAYu/DERRICK_DELAY; + } + else + { + factor = 1.0f; + } + m_soundChannel = m_sound->Play(SOUND_DERRICK, m_object->RetPosition(0), 1.0f, 0.5f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 0.5f, 4.0f*factor, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 0.3f, 6.0f*factor, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 0.5f, 1.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 0.5f, 4.0f, SOPER_STOP); + } + + if ( m_progress >= 6.0f/16.0f && // pénètre dans le sol ? + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + speed.y = Rand()*5.0f; + dim.x = Rand()*3.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f); + } + + if ( m_progress >= 6.0f/16.0f && // pénètre dans le sol ? + m_lastTrack+m_engine->ParticuleAdapt(0.5f) <= m_time ) + { + m_lastTrack = m_time; + + pos = m_object->RetPosition(0); + speed.x = (Rand()-0.5f)*12.0f; + speed.z = (Rand()-0.5f)*12.0f; + speed.y = Rand()*10.0f+10.0f; + dim.x = 0.6f; + dim.y = dim.x; + pos.y += dim.y; + duration = Rand()*2.0f+2.0f; + m_particule->CreateTrack(pos, speed, dim, PARTITRACK5, + duration, Rand()*10.0f+15.0f, + duration*0.2f, 1.0f); + } + + if ( m_progress < 1.0f ) + { + pos.x = 0.0f; + pos.z = 0.0f; + pos.y = -m_progress*16.0f; + m_object->SetPosition(1, pos); // descend le foret + + angle = m_object->RetAngleY(1); + angle += event.rTime*8.0f; + m_object->SetAngleY(1, angle); // tourne le foret + } + else + { + m_phase = ADP_ASCEND; + m_progress = 0.0f; + m_speed = 1.0f/5.0f; + } + } + + if ( m_phase == ADP_ASCEND ) + { + if ( m_progress <= 7.0f/16.0f && + m_lastParticule+m_engine->ParticuleAdapt(0.1f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + speed.y = Rand()*5.0f; + dim.x = Rand()*3.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f); + } + + if ( m_progress <= 4.0f/16.0f && + m_lastTrack+m_engine->ParticuleAdapt(1.0f) <= m_time ) + { + m_lastTrack = m_time; + + pos = m_object->RetPosition(0); + speed.x = (Rand()-0.5f)*12.0f; + speed.z = (Rand()-0.5f)*12.0f; + speed.y = Rand()*10.0f+10.0f; + dim.x = 0.6f; + dim.y = dim.x; + pos.y += dim.y; + duration = Rand()*2.0f+2.0f; + m_particule->CreateTrack(pos, speed, dim, PARTITRACK5, + duration, Rand()*10.0f+15.0f, + duration*0.2f, 1.0f); + } + + if ( m_progress < 1.0f ) + { + pos.x = 0.0f; + pos.z = 0.0f; + pos.y = -(1.0f-m_progress)*16.0f; + m_object->SetPosition(1, pos); // remonte le foret + + angle = m_object->RetAngleY(1); + angle -= event.rTime*2.0f; + m_object->SetAngleY(1, angle); // tourne le foret + } + else + { + m_soundChannel = -1; + m_bSoundFall = FALSE; + + m_phase = ADP_EXPORT; + m_progress = 0.0f; + m_speed = 1.0f/5.0f; + } + } + + if ( m_phase == ADP_ISFREE ) + { + if ( m_progress >= 1.0f ) + { + m_bSoundFall = FALSE; + + m_phase = ADP_EXPORT; + m_progress = 0.0f; + m_speed = 1.0f/5.0f; + } + } + + if ( m_phase == ADP_EXPORT ) + { + if ( m_progress == 0.0f ) + { + if ( SearchFree(m_fretPos) ) + { + angle = m_object->RetAngleY(0); + CreateFret(m_fretPos, angle, m_type, 16.0f); + } + else + { + m_phase = ADP_ISFREE; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + return TRUE; + } + } + + fret = SearchFret(); + + if ( fret != 0 && + m_progress <= 0.5f && + m_lastParticule+m_engine->ParticuleAdapt(0.1f) <= m_time ) + { + m_lastParticule = m_time; + + if ( m_progress < 0.3f ) + { + pos = fret->RetPosition(0); + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + pos.y += (Rand()-0.5f)*5.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 3.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFIRE, 1.0f, 0.0f, 0.0f); + } + else + { + pos = fret->RetPosition(0); + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + pos.y += Rand()*2.5f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINT, 2.0f, 0.0f, 0.0f); + } + } + + if ( m_progress < 1.0f ) + { + if ( fret != 0 ) + { + pos = fret->RetPosition(0); + pos.y -= event.rTime*20.0f; // tombe + if ( !m_bSoundFall && pos.y < m_fretPos.y ) + { + m_sound->Play(SOUND_BOUM, m_fretPos); + m_bSoundFall = TRUE; + } + if ( pos.y < m_fretPos.y ) + { + pos.y = m_fretPos.y; + fret->SetLock(FALSE); // objet utilisable + } + fret->SetPosition(0, pos); + } + } + else + { + if ( ExistKey() ) // clé existe déjà ? + { + m_phase = ADP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/10.0f; + } + else + { + m_phase = ADP_EXCAVATE; + m_progress = 0.0f; + m_speed = 1.0f/(m_type==OBJECT_URANIUM?DERRICK_DELAYu:DERRICK_DELAY); + } + } + } + + return TRUE; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoDerrick::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 109, EVENT_OBJECT_TYPE); + + return TRUE; +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoDerrick::Write(char *line) +{ + char name[100]; + + if ( m_phase == ADP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoDerrick::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoDerrickPhase)OpInt(line, "aPhase", ADP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastParticule = 0.0f; + + return TRUE; +} + + +// Cherche l'objet fret. + +CObject* CAutoDerrick::SearchFret() +{ + CObject* pObj; + D3DVECTOR oPos; + ObjectType type; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type == OBJECT_DERRICK ) continue; + + oPos = pObj->RetPosition(0); + + if ( oPos.x == m_fretPos.x && + oPos.z == m_fretPos.z ) return pObj; + } + + return 0; +} + +// Cherche si un emplacement est libre. + +BOOL CAutoDerrick::SearchFree(D3DVECTOR pos) +{ + CObject* pObj; + D3DVECTOR sPos; + ObjectType type; + float sRadius, distance; + int i, j; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type == OBJECT_DERRICK ) continue; + + j = 0; + while ( pObj->GetCrashSphere(j++, sPos, sRadius) ) + { + distance = Length(sPos, pos); + distance -= sRadius; + if ( distance < 2.0f ) return FALSE; // emplacement occupé + } + } + + return TRUE; // emplacement libre +} + +// Crée un objet transportable. + +void CAutoDerrick::CreateFret(D3DVECTOR pos, float angle, ObjectType type, + float height) +{ + CObject* fret; + + fret = new CObject(m_iMan); + if ( !fret->CreateResource(pos, angle, type) ) + { + delete fret; + m_displayText->DisplayError(ERR_TOOMANY, m_object); + return; + } + fret->SetLock(TRUE); // objet pas encore utilisable + + if ( m_object->RetResetCap() == RESET_MOVE ) + { + fret->SetResetCap(RESET_DELETE); + } + + pos = fret->RetPosition(0); + pos.y += height; + fret->SetPosition(0, pos); +} + +// Cherche s'il existe déjà une clé. + +BOOL CAutoDerrick::ExistKey() +{ + CObject* pObj; + ObjectType type; + int i; + + if ( m_type != OBJECT_KEYa && + m_type != OBJECT_KEYb && + m_type != OBJECT_KEYc && + m_type != OBJECT_KEYd ) return FALSE; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type == m_type ) return TRUE; + } + + return FALSE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoDerrick::RetError() +{ + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + + if ( m_phase == ADP_WAIT ) return ERR_DERRICK_NULL; + return ERR_OK; +} + + diff --git a/src/autoderrick.h b/src/autoderrick.h new file mode 100644 index 00000000..cb5ccb48 --- /dev/null +++ b/src/autoderrick.h @@ -0,0 +1,65 @@ +// autoderrick.h + +#ifndef _AUTODERRICK_H_ +#define _AUTODERRICK_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + +enum ObjectType; + + + +enum AutoDerrickPhase +{ + ADP_WAIT = 1, + ADP_EXCAVATE = 2, // descend le foret + ADP_ASCEND = 3, // remonte le foret + ADP_EXPORT = 4, // exporte la matière + ADP_ISFREE = 5, // attend disparition matière +}; + + + +class CAutoDerrick : public CAuto +{ +public: + CAutoDerrick(CInstanceManager* iMan, CObject* object); + ~CAutoDerrick(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + CObject* SearchFret(); + BOOL SearchFree(D3DVECTOR pos); + void CreateFret(D3DVECTOR pos, float angle, ObjectType type, float height); + BOOL ExistKey(); + +protected: + AutoDerrickPhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastParticule; + float m_lastTrack; + D3DVECTOR m_fretPos; + int m_soundChannel; + BOOL m_bSoundFall; +}; + + +#endif //_AUTODERRICK_H_ diff --git a/src/autodestroyer.cpp b/src/autodestroyer.cpp new file mode 100644 index 00000000..28f548f1 --- /dev/null +++ b/src/autodestroyer.cpp @@ -0,0 +1,383 @@ +// autodestroyer.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "physics.h" +#include "pyro.h" +#include "sound.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "robotmain.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autodestroyer.h" + + + + +// Constructeur de l'objet. + +CAutoDestroyer::CAutoDestroyer(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); + m_phase = ADEP_WAIT; // en pause jusqu'au premier Init() +} + +// Destructeur de l'objet. + +CAutoDestroyer::~CAutoDestroyer() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoDestroyer::DeleteObject(BOOL bAll) +{ + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoDestroyer::Init() +{ + m_phase = ADEP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/0.5f; + + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastParticule = 0.0f; + + CAuto::Init(); +} + + +// Gestion d'un événement. + +BOOL CAutoDestroyer::EventProcess(const Event &event) +{ + CObject* scrap; + CPyro* pyro; + D3DVECTOR pos, speed; + FPOINT dim; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + } + return TRUE; + } + + if ( m_phase == ADEP_WAIT ) + { + if ( m_progress >= 1.0f ) + { + scrap = SearchPlastic(); + if ( scrap == 0 ) + { + m_phase = ADEP_WAIT; // attend encore ... + m_progress = 0.0f; + m_speed = 1.0f/0.5f; + } + else + { + scrap->SetLock(TRUE); // déchet plus utilisable +//? scrap->SetTruck(m_object); // déchet plus utilisable + + if ( SearchVehicle() ) + { + m_phase = ADEP_WAIT; // attend encore ... + m_progress = 0.0f; + m_speed = 1.0f/0.5f; + } + else + { + m_sound->Play(SOUND_PSHHH2, m_object->RetPosition(0), 1.0f, 1.0f); + + m_phase = ADEP_DOWN; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + m_bExplo = FALSE; + } + } + } + } + + if ( m_phase == ADEP_DOWN ) + { + if ( m_progress >= 0.3f-0.05f && !m_bExplo ) + { + scrap = SearchPlastic(); + if ( scrap != 0 ) + { + pyro = new CPyro(m_iMan); + pyro->Create(PT_FRAGT, scrap); + } + m_bExplo = TRUE; + } + + if ( m_progress < 1.0f ) + { + pos = D3DVECTOR(0.0f, -10.0f, 0.0f); + pos.y = -Bounce(m_progress, 0.3f)*10.0f; + m_object->SetPosition(1, pos); + } + else + { + m_object->SetPosition(1, D3DVECTOR(0.0f, -10.0f, 0.0f)); + m_sound->Play(SOUND_REPAIR, m_object->RetPosition(0)); + + m_phase = ADEP_REPAIR; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == ADEP_REPAIR ) + { + if ( m_progress < 1.0f ) + { + } + else + { + m_sound->Play(SOUND_OPEN, m_object->RetPosition(0), 1.0f, 0.8f); + + m_phase = ADEP_UP; + m_progress = 0.0f; + m_speed = 1.0f/3.0f; + } + } + + if ( m_phase == ADEP_UP ) + { + if ( m_progress < 1.0f ) + { + pos = D3DVECTOR(0.0f, -10.0f, 0.0f); + pos.y = -(1.0f-m_progress)*10.0f; + m_object->SetPosition(1, pos); + } + else + { + m_object->SetPosition(1, D3DVECTOR(0.0f, 0.0f, 0.0f)); + + m_phase = ADEP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/0.5f; + } + } + + return TRUE; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoDestroyer::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 106, EVENT_OBJECT_TYPE); + + return TRUE; +} + + +// Cherche le déchet placé sous le destructeur. + +CObject* CAutoDestroyer::SearchPlastic() +{ + CObject* pObj; + D3DVECTOR sPos, oPos; + ObjectType type; + float dist; + int i; + + sPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_SCRAP4 && + type != OBJECT_SCRAP5 ) continue; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, sPos); + if ( dist <= 5.0f ) return pObj; + } + + return 0; +} + +// Cherche si un véhicule est trop proche. + +BOOL CAutoDestroyer::SearchVehicle() +{ + CObject* pObj; + D3DVECTOR cPos, oPos; + ObjectType type; + float oRadius, dist; + int i; + + cPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_HUMAN && + type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis && + type != OBJECT_MOBILErt && + type != OBJECT_MOBILErc && + type != OBJECT_MOBILErr && + type != OBJECT_MOBILErs && + type != OBJECT_MOBILEsa && + type != OBJECT_MOBILEtg && + type != OBJECT_MOBILEft && + type != OBJECT_MOBILEtt && + type != OBJECT_MOBILEwt && + type != OBJECT_MOBILEit && + type != OBJECT_MOBILEdr && + type != OBJECT_MOTHER && + type != OBJECT_ANT && + type != OBJECT_SPIDER && + type != OBJECT_BEE && + type != OBJECT_WORM ) continue; + + if ( !pObj->GetCrashSphere(0, oPos, oRadius) ) continue; + dist = Length(oPos, cPos)-oRadius; + + if ( dist < 20.0f ) return TRUE; + } + + return FALSE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoDestroyer::RetError() +{ + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + + return ERR_OK; +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoDestroyer::Write(char *line) +{ + char name[100]; + + if ( m_phase == ADEP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoDestroyer::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoDestroyerPhase)OpInt(line, "aPhase", ADEP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastParticule = 0.0f; + + return TRUE; +} + + diff --git a/src/autodestroyer.h b/src/autodestroyer.h new file mode 100644 index 00000000..3b48f666 --- /dev/null +++ b/src/autodestroyer.h @@ -0,0 +1,57 @@ +// autodestroyer.h + +#ifndef _AUTODESTROYER_H_ +#define _AUTODESTROYER_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoDestroyerPhase +{ + ADEP_WAIT = 1, // attend métal + ADEP_DOWN = 2, // descend le couvercle + ADEP_REPAIR = 3, // construit le véhicule + ADEP_UP = 4, // remonte le couvercle +}; + + + +class CAutoDestroyer : public CAuto +{ +public: + CAutoDestroyer(CInstanceManager* iMan, CObject* object); + ~CAutoDestroyer(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + CObject* SearchPlastic(); + BOOL SearchVehicle(); + +protected: + AutoDestroyerPhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastParticule; + BOOL m_bExplo; +}; + + +#endif //_AUTODESTROYER_H_ diff --git a/src/autoegg.cpp b/src/autoegg.cpp new file mode 100644 index 00000000..a8eec72b --- /dev/null +++ b/src/autoegg.cpp @@ -0,0 +1,359 @@ +// autoegg.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "pyro.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autoegg.h" + + + +// Constructeur de l'objet. + +CAutoEgg::CAutoEgg(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + m_type = OBJECT_NULL; + m_value = 0.0f; + m_string[0] = 0; + + m_param = 0; + m_phase = AEP_NULL; + Init(); +} + +// Destructeur de l'objet. + +CAutoEgg::~CAutoEgg() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoEgg::DeleteObject(BOOL bAll) +{ + CObject* alien; + + CAuto::DeleteObject(bAll); + + if ( !bAll ) + { + alien = SearchAlien(); + if ( alien != 0 ) + { + if ( alien->RetZoom(0) == 1.0f ) + { + alien->SetLock(FALSE); + alien->SetActivity(TRUE); // l'insect né est actif + } + else + { + alien->DeleteObject(); + delete alien; + } + } + } +} + + +// Initialise l'objet. + +void CAutoEgg::Init() +{ + CObject* alien; + + alien = SearchAlien(); + if ( alien == 0 ) + { + m_phase = AEP_NULL; + m_progress = 0.0f; + m_speed = 1.0f/5.0f; + m_time = 0.0f; + return; + } + + m_phase = AEP_INCUB; + m_progress = 0.0f; + m_speed = 1.0f/5.0f; + m_time = 0.0f; + + m_type = alien->RetType(); + + if ( m_type == OBJECT_ANT || + m_type == OBJECT_SPIDER || + m_type == OBJECT_BEE ) + { + alien->SetZoom(0, 0.2f); + } + if ( m_type == OBJECT_WORM ) + { + alien->SetZoom(0, 0.01f); // invisible ! + } + alien->SetLock(TRUE); + alien->SetActivity(FALSE); +} + + +// Donne une valeur. + +BOOL CAutoEgg::SetType(ObjectType type) +{ + m_type = type; + return TRUE; +} + +// Donne une valeur. + +BOOL CAutoEgg::SetValue(int rank, float value) +{ + if ( rank != 0 ) return FALSE; + m_value = value; + return TRUE; +} + +// Donne la string. + +BOOL CAutoEgg::SetString(char *string) +{ + strcpy(m_string, string); + return TRUE; +} + + +// Démarre l'objet. + +void CAutoEgg::Start(int param) +{ + if ( m_type == OBJECT_NULL ) return; + if ( m_value == 0.0f ) return; + + m_phase = AEP_DELAY; + m_progress = 0.0f; + m_speed = 1.0f/m_value; + + m_param = param; +} + + +// Gestion d'un événement. + +BOOL CAutoEgg::EventProcess(const Event &event) +{ + CObject* alien; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_phase == AEP_NULL ) return TRUE; + + if ( m_phase == AEP_DELAY ) + { + m_progress += event.rTime*m_speed; + if ( m_progress < 1.0f ) return TRUE; + + alien = new CObject(m_iMan); + if ( !alien->CreateInsect(m_object->RetPosition(0), m_object->RetAngleY(0), m_type) ) + { + delete alien; + m_phase = AEP_DELAY; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + return TRUE; + } + alien->SetActivity(FALSE); + alien->ReadProgram(0, m_string); + alien->RunProgram(0); + Init(); + } + + alien = SearchAlien(); + if ( alien == 0 ) return TRUE; + alien->SetActivity(FALSE); + + m_progress += event.rTime*m_speed; + + if ( m_phase == AEP_ZOOM ) + { + if ( m_type == OBJECT_ANT || + m_type == OBJECT_SPIDER || + m_type == OBJECT_BEE ) + { + alien->SetZoom(0, 0.2f+m_progress*0.8f); // ça pousse + } + } + + return TRUE; +} + +// Indique si l'automate a terminé son activité. + +Error CAutoEgg::IsEnded() +{ + CObject* alien; + CPyro* pyro; + + if ( m_phase == AEP_DELAY ) + { + return ERR_CONTINUE; + } + + alien = SearchAlien(); + if ( alien == 0 ) return ERR_STOP; + + if ( m_phase == AEP_INCUB ) + { + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + m_phase = AEP_ZOOM; + m_progress = 0.0f; + m_speed = 1.0f/5.0f; + } + + if ( m_phase == AEP_ZOOM ) + { + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + pyro = new CPyro(m_iMan); + pyro->Create(PT_EGG, m_object); // explosion de l'oeuf + + alien->SetZoom(0, 1.0f); // c'est un grand garçon, maintenant + + m_phase = AEP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/3.0f; + } + + if ( m_phase == AEP_WAIT ) + { + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + alien->SetLock(FALSE); + alien->SetActivity(TRUE); // l'insect né est actif + } + + return ERR_STOP; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoEgg::RetError() +{ + return ERR_OK; +} + + +// Cherche l'insect qui prend naissance dans l'oeuf. + +CObject* CAutoEgg::SearchAlien() +{ + CObject* pObj; + CObject* pBest; + D3DVECTOR cPos, oPos; + ObjectType type; + float dist, min; + int i; + + cPos = m_object->RetPosition(0); + min = 100000.0f; + pBest = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetTruck() != 0 ) continue; + + type = pObj->RetType(); + if ( type != OBJECT_ANT && + type != OBJECT_BEE && + type != OBJECT_SPIDER && + type != OBJECT_WORM ) continue; + + oPos = pObj->RetPosition(0); + dist = Length2d(oPos, cPos); + if ( dist < 8.0f && dist < min ) + { + min = dist; + pBest = pObj; + } + } + return pBest; +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoEgg::Write(char *line) +{ + char name[100]; + + if ( m_phase == AEP_NULL ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.5f", m_speed); + strcat(line, name); + + sprintf(name, " aParamType=%s", GetTypeObject(m_type)); + strcat(line, name); + + sprintf(name, " aParamValue1=%.2f", m_value); + strcat(line, name); + + sprintf(name, " aParamString=\"%s\"", m_string); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoEgg::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoEggPhase)OpInt(line, "aPhase", AEP_NULL); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + m_type = OpTypeObject(line, "aParamType", OBJECT_NULL); + m_value = OpFloat(line, "aParamValue1", 0.0f); + OpString(line, "aParamString", m_string); + + return TRUE; +} + diff --git a/src/autoegg.h b/src/autoegg.h new file mode 100644 index 00000000..bc4be7b7 --- /dev/null +++ b/src/autoegg.h @@ -0,0 +1,64 @@ +// autoegg.h + +#ifndef _AUTOEGG_H_ +#define _AUTOEGG_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + +enum ObjectType; + + + +enum AutoEggPhase +{ + AEP_NULL = 0, + AEP_DELAY = 1, + AEP_INCUB = 3, + AEP_ZOOM = 4, + AEP_WAIT = 5, +}; + + + +class CAutoEgg : public CAuto +{ +public: + CAutoEgg(CInstanceManager* iMan, CObject* object); + ~CAutoEgg(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + void Start(int param); + BOOL EventProcess(const Event &event); + Error IsEnded(); + Error RetError(); + + BOOL SetType(ObjectType type); + BOOL SetValue(int rank, float value); + BOOL SetString(char *string); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + CObject* SearchAlien(); + +protected: + ObjectType m_type; + float m_value; + char m_string[100]; + int m_param; + AutoEggPhase m_phase; + float m_progress; + float m_speed; +}; + + +#endif //_AUTOEGG_H_ diff --git a/src/autoenergy.cpp b/src/autoenergy.cpp new file mode 100644 index 00000000..1456f017 --- /dev/null +++ b/src/autoenergy.cpp @@ -0,0 +1,652 @@ +// autoenergy.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "gauge.h" +#include "window.h" +#include "displaytext.h" +#include "sound.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autoenergy.h" + + + +#define ENERGY_POWER 0.4f // énergie nécessaire pour une pile +#define ENERGY_DELAY 12.0f // durée de la transformation + + + + +// Constructeur de l'objet. + +CAutoEnergy::CAutoEnergy(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + m_partiSphere = -1; + Init(); +} + +// Destructeur de l'objet. + +CAutoEnergy::~CAutoEnergy() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoEnergy::DeleteObject(BOOL bAll) +{ + CObject* fret; + + if ( m_partiSphere != -1 ) + { + m_particule->DeleteParticule(m_partiSphere); + m_partiSphere = -1; + } + + if ( !bAll ) + { + fret = SearchMetal(); + if ( fret != 0 ) + { + fret->DeleteObject(); // détruit le métal + delete fret; + } + + fret = SearchPower(); + if ( fret != 0 ) + { + fret->DeleteObject(); // détruit la pile + delete fret; + } + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoEnergy::Init() +{ + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastUpdateTime = 0.0f; + m_lastParticule = 0.0f; + + m_phase = AENP_WAIT; // attend ... + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + + CAuto::Init(); +} + + +// Gestion d'un événement. + +BOOL CAutoEnergy::EventProcess(const Event &event) +{ + CObject* fret; + D3DVECTOR pos, ppos, speed; + FPOINT dim, c, p; + TerrainRes res; + float big; + BOOL bGO; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + pos = m_object->RetPosition(0); + pos.y += 10.0f; + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + speed.y = -7.0f; + dim.x = Rand()*0.5f+0.5f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFIREZ, 1.0f, 0.0f, 0.0f); + } + } + return TRUE; + } + + UpdateInterface(event.rTime); + EventProgress(event.rTime); + + big = m_object->RetEnergy(); + + res = m_terrain->RetResource(m_object->RetPosition(0)); + if ( res == TR_POWER ) + { + big += event.rTime*0.01f; // recharge la grosse pile + } + + if ( m_phase == AENP_WAIT ) + { + if ( m_progress >= 1.0f ) + { + bGO = FALSE; + fret = SearchMetal(); // métal à transformer ? + if ( fret != 0 ) + { + if ( fret->RetType() == OBJECT_METAL ) + { + if ( big > ENERGY_POWER ) bGO = TRUE; + } + else + { + if ( !SearchVehicle() ) bGO = TRUE; + } + } + + if ( bGO ) + { + if ( fret->RetType() == OBJECT_METAL ) + { + fret->SetLock(TRUE); // métal plus utilisable + CreatePower(); // crée la pile + } + + SetBusy(TRUE); + InitProgressTotal(ENERGY_DELAY); + CAuto::UpdateInterface(); + + pos = m_object->RetPosition(0); + pos.y += 4.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 3.0f; + dim.y = dim.x; + m_partiSphere = m_particule->CreateParticule(pos, speed, dim, PARTISPHERE1, ENERGY_DELAY, 0.0f, 0.0f); + + m_phase = AENP_CREATE; + m_progress = 0.0f; + m_speed = 1.0f/ENERGY_DELAY; + } + else + { + if ( rand()%3 == 0 && big > 0.01f ) + { + m_phase = AENP_BLITZ; + m_progress = 0.0f; + m_speed = 1.0f/Rand()*1.0f+1.0f; + } + else + { + m_phase = AENP_WAIT; // attend encore ... + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + } + } + + if ( m_phase == AENP_BLITZ ) + { + if ( m_progress < 1.0f && big > 0.01f ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + pos = m_object->RetPosition(0); + pos.y += 10.0f; + speed.x = (Rand()-0.5f)*1.0f; + speed.z = (Rand()-0.5f)*1.0f; + speed.y = -7.0f; + dim.x = Rand()*0.5f+0.5f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFIREZ, 1.0f, 0.0f, 0.0f); + } + } + else + { + m_phase = AENP_WAIT; // attend encore ... + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_phase == AENP_CREATE ) + { + if ( m_progress < 1.0f ) + { + fret = SearchMetal(); + if ( fret != 0 ) + { + if ( fret->RetType() == OBJECT_METAL ) + { + big -= event.rTime/ENERGY_DELAY*ENERGY_POWER; + } + else + { + big += event.rTime/ENERGY_DELAY*0.25f; + } + fret->SetZoom(0, 1.0f-m_progress); + } + + fret = SearchPower(); + if ( fret != 0 ) + { + fret->SetZoom(0, m_progress); + } + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + c.x = pos.x; + c.y = pos.z; + p.x = c.x; + p.y = c.y+2.0f; + p = RotatePoint(c, Rand()*PI*2.0f, p); + pos.x = p.x; + pos.z = p.y; + pos.y += 2.5f+Rand()*3.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = Rand()*2.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINT, 1.0f, 0.0f, 0.0f); + + pos = m_object->RetPosition(0); + pos.y += 3.0f; + speed.x = (Rand()-0.5f)*30.0f; + speed.z = (Rand()-0.5f)*30.0f; + speed.y = Rand()*20.0f+10.0f; + dim.x = Rand()*0.4f+0.4f; + dim.y = dim.x; + m_particule->CreateTrack(pos, speed, dim, PARTITRACK2, 2.0f, 50.0f, 1.2f, 1.2f); + + pos = m_object->RetPosition(0); + pos.y += 10.0f; + speed.x = (Rand()-0.5f)*1.5f; + speed.z = (Rand()-0.5f)*1.5f; + speed.y = -6.0f; + dim.x = Rand()*1.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFIREZ, 1.0f, 0.0f, 0.0f); + + m_sound->Play(SOUND_ENERGY, m_object->RetPosition(0), + 1.0f, 1.0f+Rand()*1.5f); + } + } + else + { + fret = SearchMetal(); + if ( fret != 0 ) + { + m_object->SetPower(0); + fret->DeleteObject(); // détruit le métal + delete fret; + } + + fret = SearchPower(); + if ( fret != 0 ) + { + fret->SetZoom(0, 1.0f); + fret->SetLock(FALSE); // pile utilisable + fret->SetTruck(m_object); + fret->SetPosition(0, D3DVECTOR(0.0f, 3.0f, 0.0f)); + m_object->SetPower(fret); + + m_displayText->DisplayError(INFO_ENERGY, m_object); + } + + SetBusy(FALSE); + CAuto::UpdateInterface(); + + m_phase = AENP_SMOKE; + m_progress = 0.0f; + m_speed = 1.0f/5.0f; + } + } + + if ( m_phase == AENP_SMOKE ) + { + if ( m_progress < 1.0f ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + pos.y += 17.0f; + pos.x += (Rand()-0.5f)*3.0f; + pos.z += (Rand()-0.5f)*3.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 6.0f+Rand()*6.0f; + dim.x = Rand()*1.5f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE3, 4.0f); + } + } + else + { + m_phase = AENP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( big < 0.0f ) big = 0.0f; + if ( big > 1.0f ) big = 1.0f; + m_object->SetEnergy(big); // màj la grosse pile + + return TRUE; +} + + +// Cherche l'objet métal. + +CObject* CAutoEnergy::SearchMetal() +{ + CObject* pObj; + ObjectType type; + + pObj = m_object->RetPower(); + if ( pObj == 0 ) return 0; + + type = pObj->RetType(); + if ( type == OBJECT_METAL || + type == OBJECT_SCRAP1 || + type == OBJECT_SCRAP2 || + type == OBJECT_SCRAP3 ) return pObj; + + return 0; +} + +// Cherche si un véhicule est trop proche. + +BOOL CAutoEnergy::SearchVehicle() +{ + CObject* pObj; + D3DVECTOR cPos, oPos; + ObjectType type; + float oRadius, dist; + int i; + + cPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_HUMAN && + type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis && + type != OBJECT_MOBILErt && + type != OBJECT_MOBILErc && + type != OBJECT_MOBILErr && + type != OBJECT_MOBILErs && + type != OBJECT_MOBILEsa && + type != OBJECT_MOBILEtg && + type != OBJECT_MOBILEft && + type != OBJECT_MOBILEtt && + type != OBJECT_MOBILEwt && + type != OBJECT_MOBILEit && + type != OBJECT_MOBILEdr && + type != OBJECT_MOTHER && + type != OBJECT_ANT && + type != OBJECT_SPIDER && + type != OBJECT_BEE && + type != OBJECT_WORM ) continue; + + if ( !pObj->GetCrashSphere(0, oPos, oRadius) ) continue; + dist = Length(oPos, cPos)-oRadius; + + if ( dist < 10.0f ) return TRUE; + } + + return FALSE; +} + +// Crée un objet pile. + +void CAutoEnergy::CreatePower() +{ + CObject* power; + D3DVECTOR pos; + float angle; + + pos = m_object->RetPosition(0); + angle = m_object->RetAngleY(0); + + power = new CObject(m_iMan); + if ( !power->CreateResource(pos, angle, OBJECT_POWER) ) + { + delete power; + m_displayText->DisplayError(ERR_TOOMANY, m_object); + return; + } + power->SetLock(TRUE); // pile pas encore utilisable + + pos = power->RetPosition(0); + pos.y += 3.0f; + power->SetPosition(0, pos); +} + +// Cherche la pile en cours de fabrication. + +CObject* CAutoEnergy::SearchPower() +{ + CObject* pObj; + D3DVECTOR cPos, oPos; + ObjectType type; + int i; + + cPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetLock() ) continue; + + type = pObj->RetType(); + if ( type != OBJECT_POWER ) continue; + + oPos = pObj->RetPosition(0); + if ( oPos.x == cPos.x && + oPos.z == cPos.z ) + { + return pObj; + } + } + + return 0; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoEnergy::RetError() +{ + CObject* pObj; + ObjectType type; + TerrainRes res; + + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + + if ( m_phase != AENP_WAIT && + m_phase != AENP_BLITZ ) return ERR_OK; + + res = m_terrain->RetResource(m_object->RetPosition(0)); + if ( res != TR_POWER ) return ERR_ENERGY_NULL; + + if ( m_object->RetEnergy() < ENERGY_POWER ) return ERR_ENERGY_LOW; + + pObj = m_object->RetPower(); + if ( pObj == 0 ) return ERR_ENERGY_EMPTY; + type = pObj->RetType(); + if ( type == OBJECT_POWER ) return ERR_OK; + if ( type != OBJECT_METAL && + type != OBJECT_SCRAP1 && + type != OBJECT_SCRAP2 && + type != OBJECT_SCRAP3 ) return ERR_ENERGY_BAD; + + return ERR_OK; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoEnergy::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*14.5f; + pos.y = oy+sy*0; + ddim.x = 14.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGauge(pos, ddim, 0, EVENT_OBJECT_GENERGY); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 108, EVENT_OBJECT_TYPE); + + return TRUE; +} + +// Met à jour l'état de tous les boutons de l'interface, +// suite au temps qui s'écoule ... + +void CAutoEnergy::UpdateInterface(float rTime) +{ + CWindow* pw; + CGauge* pg; + + CAuto::UpdateInterface(rTime); + + if ( m_time < m_lastUpdateTime+0.1f ) return; + m_lastUpdateTime = m_time; + + if ( !m_object->RetSelect() ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + pg = (CGauge*)pw->SearchControl(EVENT_OBJECT_GENERGY); + if ( pg != 0 ) + { + pg->SetLevel(m_object->RetEnergy()); + } +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoEnergy::Write(char *line) +{ + char name[100]; + + if ( m_phase == AENP_STOP || + m_phase == AENP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoEnergy::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoEnergyPhase)OpInt(line, "aPhase", AENP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastUpdateTime = 0.0f; + m_lastParticule = 0.0f; + + return TRUE; +} + diff --git a/src/autoenergy.h b/src/autoenergy.h new file mode 100644 index 00000000..5aa4631a --- /dev/null +++ b/src/autoenergy.h @@ -0,0 +1,63 @@ +// autoenergy.h + +#ifndef _AUTOENERGY_H_ +#define _AUTOENERGY_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoEnergyPhase +{ + AENP_STOP = 1, + AENP_WAIT = 2, + AENP_BLITZ = 3, + AENP_CREATE = 4, + AENP_SMOKE = 5, +}; + + + +class CAutoEnergy : public CAuto +{ +public: + CAutoEnergy(CInstanceManager* iMan, CObject* object); + ~CAutoEnergy(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + void UpdateInterface(float rTime); + + CObject* SearchMetal(); + BOOL SearchVehicle(); + void CreatePower(); + CObject* SearchPower(); + +protected: + AutoEnergyPhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastUpdateTime; + float m_lastParticule; + int m_partiSphere; +}; + + +#endif //_AUTOENERGY_H_ diff --git a/src/autofactory.cpp b/src/autofactory.cpp new file mode 100644 index 00000000..4d913f66 --- /dev/null +++ b/src/autofactory.cpp @@ -0,0 +1,947 @@ +// autofactory.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "restext.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "displaytext.h" +#include "robotmain.h" +#include "sound.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autofactory.h" + + + + +// Constructeur de l'objet. + +CAutoFactory::CAutoFactory(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); + m_type = OBJECT_MOBILEws; + m_phase = AFP_WAIT; // en pause jusqu'au premier Init() + m_channelSound = -1; +} + +// Destructeur de l'objet. + +CAutoFactory::~CAutoFactory() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoFactory::DeleteObject(BOOL bAll) +{ + CObject* fret; + CObject* vehicle; + + if ( !bAll ) + { + fret = SearchFret(); // métal à transformer ? + if ( fret != 0 ) + { + fret->DeleteObject(); // détruit le métal + delete fret; + } + + vehicle = SearchVehicle(); + if ( vehicle != 0 ) + { + vehicle->DeleteObject(); // détruit le véhicule + delete vehicle; + } + } + + if ( m_channelSound != -1 ) + { + m_sound->FlushEnvelope(m_channelSound); + m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_channelSound = -1; + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoFactory::Init() +{ + m_phase = AFP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + + m_time = 0.0f; + m_lastParticule = 0.0f; + + m_fretPos = m_object->RetPosition(0); + + CAuto::Init(); +} + + +// Gestion d'un événement. + +BOOL CAutoFactory::EventProcess(const Event &event) +{ + CObject* fret; + CObject* vehicle; + D3DMATRIX* mat; + CPhysics* physics; + D3DVECTOR pos, speed; + FPOINT dim; + ObjectType type; + float zoom, angle, prog; + int i; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + + if ( m_object->RetSelect() ) // usine sélectionnée ? + { + if ( event.event == EVENT_UPDINTERFACE ) + { + CreateInterface(TRUE); + } + + type = OBJECT_NULL; + if ( event.event == EVENT_OBJECT_FACTORYwa ) type = OBJECT_MOBILEwa; + if ( event.event == EVENT_OBJECT_FACTORYta ) type = OBJECT_MOBILEta; + if ( event.event == EVENT_OBJECT_FACTORYfa ) type = OBJECT_MOBILEfa; + if ( event.event == EVENT_OBJECT_FACTORYia ) type = OBJECT_MOBILEia; + if ( event.event == EVENT_OBJECT_FACTORYws ) type = OBJECT_MOBILEws; + if ( event.event == EVENT_OBJECT_FACTORYts ) type = OBJECT_MOBILEts; + if ( event.event == EVENT_OBJECT_FACTORYfs ) type = OBJECT_MOBILEfs; + if ( event.event == EVENT_OBJECT_FACTORYis ) type = OBJECT_MOBILEis; + if ( event.event == EVENT_OBJECT_FACTORYwc ) type = OBJECT_MOBILEwc; + if ( event.event == EVENT_OBJECT_FACTORYtc ) type = OBJECT_MOBILEtc; + if ( event.event == EVENT_OBJECT_FACTORYfc ) type = OBJECT_MOBILEfc; + if ( event.event == EVENT_OBJECT_FACTORYic ) type = OBJECT_MOBILEic; + if ( event.event == EVENT_OBJECT_FACTORYwi ) type = OBJECT_MOBILEwi; + if ( event.event == EVENT_OBJECT_FACTORYti ) type = OBJECT_MOBILEti; + if ( event.event == EVENT_OBJECT_FACTORYfi ) type = OBJECT_MOBILEfi; + if ( event.event == EVENT_OBJECT_FACTORYii ) type = OBJECT_MOBILEii; + if ( event.event == EVENT_OBJECT_FACTORYrt ) type = OBJECT_MOBILErt; + if ( event.event == EVENT_OBJECT_FACTORYrc ) type = OBJECT_MOBILErc; + if ( event.event == EVENT_OBJECT_FACTORYrr ) type = OBJECT_MOBILErr; + if ( event.event == EVENT_OBJECT_FACTORYrs ) type = OBJECT_MOBILErs; + if ( event.event == EVENT_OBJECT_FACTORYsa ) type = OBJECT_MOBILEsa; + + if ( type != OBJECT_NULL ) + { + m_type = type; + + if ( m_phase != AFP_WAIT ) + { + return FALSE; + } + + fret = SearchFret(); // métal à transformer ? + if ( fret == 0 ) + { + m_displayText->DisplayError(ERR_FACTORY_NULL, m_object); + return FALSE; + } + if ( NearestVehicle() ) + { + m_displayText->DisplayError(ERR_FACTORY_NEAR, m_object); + return FALSE; + } + + SetBusy(TRUE); + InitProgressTotal(3.0f+2.0f+15.0f+2.0f+3.0f); + UpdateInterface(); + + fret->SetLock(TRUE); // métal plus utilisable + SoundManip(3.0f, 1.0f, 0.5f); + + m_phase = AFP_CLOSE_S; + m_progress = 0.0f; + m_speed = 1.0f/3.0f; + return TRUE; + } + } + + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + EventProgress(event.rTime); + + if ( m_phase == AFP_WAIT ) + { + if ( m_progress >= 1.0f ) + { + m_phase = AFP_WAIT; // attend encore ... + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_phase == AFP_CLOSE_S ) + { + if ( m_progress < 1.0f ) + { + for ( i=0 ; i<9 ; i++ ) + { + zoom = 0.30f+(m_progress-0.5f+i/16.0f)*2.0f*0.70f; + if ( zoom < 0.30f ) zoom = 0.30f; + if ( zoom > 1.00f ) zoom = 1.00f; + m_object->SetZoomZ( 1+i, zoom); + m_object->SetZoomZ(10+i, zoom); + } + } + else + { + for ( i=0 ; i<9 ; i++ ) + { + m_object->SetZoomZ( 1+i, 1.0f); + m_object->SetZoomZ(10+i, 1.0f); + } + + SoundManip(2.0f, 1.0f, 1.2f); + + m_phase = AFP_CLOSE_T; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_phase == AFP_CLOSE_T ) + { + if ( m_progress < 1.0f ) + { + for ( i=0 ; i<9 ; i++ ) + { + angle = -m_progress*(PI/2.0f)+PI/2.0f; + m_object->SetAngleZ( 1+i, angle); + m_object->SetAngleZ(10+i, -angle); + } + } + else + { + for ( i=0 ; i<9 ; i++ ) + { + m_object->SetAngleZ( 1+i, 0.0f); + m_object->SetAngleZ(10+i, 0.0f); + } + + m_channelSound = m_sound->Play(SOUND_FACTORY, m_object->RetPosition(0), 0.0f, 1.0f, TRUE); + m_sound->AddEnvelope(m_channelSound, 1.0f, 1.0f, 2.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_channelSound, 1.0f, 1.0f, 11.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f, 2.0f, SOPER_STOP); + + m_phase = AFP_BUILD; + m_progress = 0.0f; + m_speed = 1.0f/15.0f; + } + } + + if ( m_phase == AFP_BUILD ) + { + if ( m_progress == 0.0f ) + { + if ( !CreateVehicle() ) + { + fret = SearchFret(); // métal à transformer ? + if ( fret != 0 ) + { + fret->SetLock(FALSE); // métal de nouveau utilisable + } + + if ( m_channelSound != -1 ) + { + m_sound->FlushEnvelope(m_channelSound); + m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_channelSound = -1; + } + + m_phase = AFP_OPEN_T; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + return TRUE; + } + } + + if ( m_progress < 1.0f ) + { + if ( m_type == OBJECT_MOBILErt || + m_type == OBJECT_MOBILErc || + m_type == OBJECT_MOBILErr || + m_type == OBJECT_MOBILErs ) + { + prog = 1.0f-m_progress*1.5f; + if ( prog < 0.0f ) prog = 0.0f; + } + else + { + prog = 1.0f-m_progress; + } + angle = powf(prog*10.0f, 2.0f)+m_object->RetAngleY(0); + + vehicle = SearchVehicle(); + if ( vehicle != 0 ) + { + vehicle->SetAngleY(0, angle+PI); + vehicle->SetZoom(0, m_progress); + } + + fret = SearchFret(); // métal à transformer ? + if ( fret != 0 ) + { + fret->SetZoom(0, 1.0f-m_progress); + } + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + +#if 0 + pos = m_fretPos; + pos.x += (Rand()-0.5f)*20.0f; + pos.z += (Rand()-0.5f)*20.0f; + pos.y += 1.0f; + speed.x = (Rand()-0.5f)*12.0f; + speed.z = (Rand()-0.5f)*12.0f; + speed.y = Rand()*12.0f; + dim.x = Rand()*12.0f+10.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLUE, 1.0f, 0.0f, 0.0f); +#else + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(-12.0f, 20.0f, -4.0f); // position cheminée + pos = Transform(*mat, pos); + pos.y += 2.0f; + pos.x += (Rand()-0.5f)*2.0f; + pos.z += (Rand()-0.5f)*2.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 6.0f+Rand()*6.0f; + dim.x = Rand()*1.5f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE3, 4.0f); +#endif + } + } + else + { + m_displayText->DisplayError(INFO_FACTORY, m_object); + SoundManip(2.0f, 1.0f, 1.2f); + + fret = SearchFret(); // métal à transformer ? + if ( fret != 0 ) + { + fret->DeleteObject(); // supprime le métal + delete fret; + } + + vehicle = SearchVehicle(); + if ( vehicle != 0 ) + { + physics = vehicle->RetPhysics(); + if ( physics != 0 ) + { + physics->SetFreeze(FALSE); // on peut bouger + } + + vehicle->SetLock(FALSE); // véhicule utilisable +//? vehicle->RetPhysics()->RetBrain()->StartTaskAdvance(16.0f); + vehicle->SetAngleY(0, m_object->RetAngleY(0)+PI); + vehicle->SetZoom(0, 1.0f); + } + + m_main->CreateShortcuts(); + + m_phase = AFP_OPEN_T; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_phase == AFP_OPEN_T ) + { + if ( m_progress < 1.0f ) + { + for ( i=0 ; i<9 ; i++ ) + { + angle = -(1.0f-m_progress)*(PI/2.0f)+PI/2.0f; + m_object->SetAngleZ( 1+i, angle); + m_object->SetAngleZ(10+i, -angle); + } + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.1f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_fretPos; + pos.x += (Rand()-0.5f)*10.0f; + pos.z += (Rand()-0.5f)*10.0f; + pos.y += Rand()*10.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINT, 2.0f, 0.0f, 0.0f); + } + } + else + { + for ( i=0 ; i<9 ; i++ ) + { + m_object->SetAngleZ( 1+i, PI/2.0f); + m_object->SetAngleZ(10+i, -PI/2.0f); + } + + SoundManip(3.0f, 1.0f, 0.5f); + + m_phase = AFP_OPEN_S; + m_progress = 0.0f; + m_speed = 1.0f/3.0f; + } + } + + if ( m_phase == AFP_OPEN_S ) + { + if ( m_progress < 1.0f ) + { + for ( i=0 ; i<9 ; i++ ) + { + zoom = 0.30f+((1.0f-m_progress)-0.5f+i/16.0f)*2.0f*0.70f; + if ( zoom < 0.30f ) zoom = 0.30f; + if ( zoom > 1.00f ) zoom = 1.00f; + m_object->SetZoomZ( 1+i, zoom); + m_object->SetZoomZ(10+i, zoom); + } + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.1f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_fretPos; + pos.x += (Rand()-0.5f)*10.0f; + pos.z += (Rand()-0.5f)*10.0f; + pos.y += Rand()*10.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINT, 2.0f, 0.0f, 0.0f); + } + } + else + { + for ( i=0 ; i<9 ; i++ ) + { + m_object->SetZoomZ( 1+i, 0.30f); + m_object->SetZoomZ(10+i, 0.30f); + } + + SetBusy(FALSE); + UpdateInterface(); + + m_phase = AFP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + return TRUE; +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoFactory::Write(char *line) +{ + char name[100]; + + if ( m_phase == AFP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoFactory::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoFactoryPhase)OpInt(line, "aPhase", AFP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastParticule = 0.0f; + m_fretPos = m_object->RetPosition(0); + + return TRUE; +} + + +// Cherche l'objet fret. + +CObject* CAutoFactory::SearchFret() +{ + CObject* pObj; + D3DVECTOR oPos; + ObjectType type; + float dist; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_METAL ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, m_fretPos); + + if ( dist < 8.0f ) return pObj; + } + + return 0; +} + +// Cherche si un véhicule est trop proche. + +BOOL CAutoFactory::NearestVehicle() +{ + CObject* pObj; + D3DVECTOR cPos, oPos; + ObjectType type; + float oRadius, dist; + int i; + + cPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_HUMAN && + type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis && + type != OBJECT_MOBILErt && + type != OBJECT_MOBILErc && + type != OBJECT_MOBILErr && + type != OBJECT_MOBILErs && + type != OBJECT_MOBILEsa && + type != OBJECT_MOBILEtg && + type != OBJECT_MOBILEft && + type != OBJECT_MOBILEtt && + type != OBJECT_MOBILEwt && + type != OBJECT_MOBILEit && + type != OBJECT_MOBILEdr && + type != OBJECT_MOTHER && + type != OBJECT_ANT && + type != OBJECT_SPIDER && + type != OBJECT_BEE && + type != OBJECT_WORM ) continue; + + if ( !pObj->GetCrashSphere(0, oPos, oRadius) ) continue; + dist = Length(oPos, cPos)-oRadius; + + if ( dist < 10.0f ) return TRUE; + } + + return FALSE; +} + + +// Crée un véhicule pas utilisable tout de suite. + +BOOL CAutoFactory::CreateVehicle() +{ + CObject* vehicle; + D3DMATRIX* mat; + CPhysics* physics; + D3DVECTOR pos; + float angle; + char* name; + int i; + + angle = m_object->RetAngleY(0); + + mat = m_object->RetWorldMatrix(0); + if ( m_type == OBJECT_MOBILErt || + m_type == OBJECT_MOBILErc || + m_type == OBJECT_MOBILErr || + m_type == OBJECT_MOBILErs ) + { + pos = D3DVECTOR(2.0f, 0.0f, 0.0f); + } + else + { + pos = D3DVECTOR(4.0f, 0.0f, 0.0f); + } + pos = Transform(*mat, pos); + + vehicle = new CObject(m_iMan); + if ( !vehicle->CreateVehicle(pos, angle, m_type, -1.0f, FALSE, FALSE) ) + { + delete vehicle; + m_displayText->DisplayError(ERR_TOOMANY, m_object); + return FALSE; + } + vehicle->UpdateMapping(); + vehicle->SetLock(TRUE); // pas utilisable + vehicle->SetRange(30.0f); + + physics = vehicle->RetPhysics(); + if ( physics != 0 ) + { + physics->SetFreeze(TRUE); // on ne bouge plus + } + + for ( i=0 ; i<10 ; i++ ) + { + name = m_main->RetNewScriptName(m_type, i); + if ( name == 0 ) break; + vehicle->ReadProgram(i, name); + } + + return TRUE; +} + +// Cherche le véhicule en cours de fabrication. + +CObject* CAutoFactory::SearchVehicle() +{ + CObject* pObj; + D3DVECTOR oPos; + ObjectType type; + float dist; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetLock() ) continue; + + type = pObj->RetType(); + if ( type != m_type ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, m_fretPos); + + if ( dist < 8.0f ) return pObj; + } + + return 0; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoFactory::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, dim, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + dim.x = 33.0f/640.0f; + dim.y = 33.0f/480.0f; + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = 0.0f; + pos.y = oy+sy*2.6f; + ddim.x = 138.0f/640.0f; + ddim.y = 222.0f/480.0f; + pw->CreateGroup(pos, ddim, 6, EVENT_WINDOW3); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*8.2f; + pw->CreateButton(pos, dim, 128+9, EVENT_OBJECT_FACTORYwa); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+10, EVENT_OBJECT_FACTORYta); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+11, EVENT_OBJECT_FACTORYfa); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+22, EVENT_OBJECT_FACTORYia); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*7.1f; + pw->CreateButton(pos, dim, 128+12, EVENT_OBJECT_FACTORYws); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+13, EVENT_OBJECT_FACTORYts); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+14, EVENT_OBJECT_FACTORYfs); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+24, EVENT_OBJECT_FACTORYis); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*6.0f; + pw->CreateButton(pos, dim, 128+15, EVENT_OBJECT_FACTORYwc); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+16, EVENT_OBJECT_FACTORYtc); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+17, EVENT_OBJECT_FACTORYfc); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+23, EVENT_OBJECT_FACTORYic); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*4.9f; + pw->CreateButton(pos, dim, 128+25, EVENT_OBJECT_FACTORYwi); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+26, EVENT_OBJECT_FACTORYti); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+27, EVENT_OBJECT_FACTORYfi); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+28, EVENT_OBJECT_FACTORYii); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*3.8f; + pw->CreateButton(pos, dim, 128+18, EVENT_OBJECT_FACTORYrt); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+19, EVENT_OBJECT_FACTORYrc); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+20, EVENT_OBJECT_FACTORYrr); + pos.x += dim.x; + pw->CreateButton(pos, dim, 128+29, EVENT_OBJECT_FACTORYrs); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*2.7f; + pw->CreateButton(pos, dim, 128+21, EVENT_OBJECT_FACTORYsa); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 101, EVENT_OBJECT_TYPE); + + UpdateInterface(); + return TRUE; +} + +// Met à jour l'état de tous les boutons de l'interface. + +void CAutoFactory::UpdateInterface() +{ + CWindow* pw; + + if ( !m_object->RetSelect() ) return; + + CAuto::UpdateInterface(); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + + UpdateButton(pw, EVENT_OBJECT_FACTORYwa, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYta, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYfa, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYia, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYws, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYts, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYfs, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYis, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYwc, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYtc, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYfc, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYic, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYwi, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYti, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYfi, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYii, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYrt, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYrc, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYrr, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYrs, m_bBusy); + UpdateButton(pw, EVENT_OBJECT_FACTORYsa, m_bBusy); +} + +// Met à jour un bouton de l'interface. + +void CAutoFactory::UpdateButton(CWindow *pw, EventMsg event, BOOL bBusy) +{ + BOOL bEnable = TRUE; + + EnableInterface(pw, event, !bBusy); + + if ( event == EVENT_OBJECT_FACTORYta ) + { + bEnable = g_researchDone&RESEARCH_TANK; + } + if ( event == EVENT_OBJECT_FACTORYfa ) + { + bEnable = g_researchDone&RESEARCH_FLY; + } + if ( event == EVENT_OBJECT_FACTORYia ) + { + bEnable = g_researchDone&RESEARCH_iPAW; + } + + if ( event == EVENT_OBJECT_FACTORYws ) + { + bEnable = g_researchDone&RESEARCH_SNIFFER; + } + if ( event == EVENT_OBJECT_FACTORYts ) + { + bEnable = ( (g_researchDone&RESEARCH_SNIFFER) && + (g_researchDone&RESEARCH_TANK) ); + } + if ( event == EVENT_OBJECT_FACTORYfs ) + { + bEnable = ( (g_researchDone&RESEARCH_SNIFFER) && + (g_researchDone&RESEARCH_FLY) ); + } + if ( event == EVENT_OBJECT_FACTORYis ) + { + bEnable = ( (g_researchDone&RESEARCH_SNIFFER) && + (g_researchDone&RESEARCH_iPAW) ); + } + + if ( event == EVENT_OBJECT_FACTORYwc ) + { + bEnable = g_researchDone&RESEARCH_CANON; + } + if ( event == EVENT_OBJECT_FACTORYtc ) + { + bEnable = ( (g_researchDone&RESEARCH_CANON) && + (g_researchDone&RESEARCH_TANK) ); + } + if ( event == EVENT_OBJECT_FACTORYfc ) + { + bEnable = ( (g_researchDone&RESEARCH_CANON) && + (g_researchDone&RESEARCH_FLY) ); + } + if ( event == EVENT_OBJECT_FACTORYic ) + { + bEnable = ( (g_researchDone&RESEARCH_CANON) && + (g_researchDone&RESEARCH_iPAW) ); + } + + if ( event == EVENT_OBJECT_FACTORYwi ) + { + bEnable = g_researchDone&RESEARCH_iGUN; + } + if ( event == EVENT_OBJECT_FACTORYti ) + { + bEnable = ( (g_researchDone&RESEARCH_iGUN) && + (g_researchDone&RESEARCH_TANK) ); + } + if ( event == EVENT_OBJECT_FACTORYfi ) + { + bEnable = ( (g_researchDone&RESEARCH_iGUN) && + (g_researchDone&RESEARCH_FLY) ); + } + if ( event == EVENT_OBJECT_FACTORYii ) + { + bEnable = ( (g_researchDone&RESEARCH_iGUN) && + (g_researchDone&RESEARCH_iPAW) ); + } + + if ( event == EVENT_OBJECT_FACTORYrt ) + { + bEnable = ( (g_researchDone&RESEARCH_THUMP) && + (g_researchDone&RESEARCH_TANK) ); + } + if ( event == EVENT_OBJECT_FACTORYrc ) + { + bEnable = ( (g_researchDone&RESEARCH_PHAZER) && + (g_researchDone&RESEARCH_TANK) ); + } + if ( event == EVENT_OBJECT_FACTORYrr ) + { + bEnable = ( (g_researchDone&RESEARCH_RECYCLER) && + (g_researchDone&RESEARCH_TANK) ); + } + if ( event == EVENT_OBJECT_FACTORYrs ) + { + bEnable = ( (g_researchDone&RESEARCH_SHIELD) && + (g_researchDone&RESEARCH_TANK) ); + } + + if ( event == EVENT_OBJECT_FACTORYsa ) + { + bEnable = g_researchDone&RESEARCH_SUBM; + } + + DeadInterface(pw, event, bEnable); +} + +// Fait entendre le son du bras manipulateur. + +void CAutoFactory::SoundManip(float time, float amplitude, float frequency) +{ + int i; + + i = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 0.0f, 0.3f*frequency, TRUE); + m_sound->AddEnvelope(i, 0.5f*amplitude, 1.0f*frequency, 0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.5f*amplitude, 1.0f*frequency, time-0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.0f, 0.3f*frequency, 0.1f, SOPER_STOP); +} + diff --git a/src/autofactory.h b/src/autofactory.h new file mode 100644 index 00000000..c635608f --- /dev/null +++ b/src/autofactory.h @@ -0,0 +1,66 @@ +// autofactory.h + +#ifndef _AUTOFACTORY_H_ +#define _AUTOFACTORY_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoFactoryPhase +{ + AFP_WAIT = 1, // attend métal + AFP_CLOSE_S = 2, // ferme les portes (shift) + AFP_CLOSE_T = 3, // ferme les portes (turn) + AFP_BUILD = 4, // construit le véhicule + AFP_OPEN_T = 5, // ouvre les portes (turn) + AFP_OPEN_S = 6, // ouvre les portes (shift) + AFP_ADVANCE = 7, // avance devant la porte +}; + + + +class CAutoFactory : public CAuto +{ +public: + CAutoFactory(CInstanceManager* iMan, CObject* object); + ~CAutoFactory(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + void UpdateInterface(); + void UpdateButton(CWindow *pw, EventMsg event, BOOL bBusy); + + CObject* SearchFret(); + BOOL NearestVehicle(); + BOOL CreateVehicle(); + CObject* SearchVehicle(); + + void SoundManip(float time, float amplitude, float frequency); + +protected: + AutoFactoryPhase m_phase; + float m_progress; + float m_speed; + float m_lastParticule; + D3DVECTOR m_fretPos; + int m_channelSound; +}; + + +#endif //_AUTOFACTORY_H_ diff --git a/src/autoflag.cpp b/src/autoflag.cpp new file mode 100644 index 00000000..46b529c8 --- /dev/null +++ b/src/autoflag.cpp @@ -0,0 +1,164 @@ +// autoflag.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "auto.h" +#include "autoflag.h" + + + +#define ADJUST_ANGLE FALSE // TRUE -> ajuste les angles des membres + + +#if ADJUST_ANGLE +static float g_flag1 = 6.00f; +static float g_flag2 = 0.10f; +static float g_flag3 = 2.00f; +#endif + + +// Constructeur de l'objet. + +CAutoFlag::CAutoFlag(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); +} + +// Destructeur de l'objet. + +CAutoFlag::~CAutoFlag() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoFlag::DeleteObject(BOOL bAll) +{ + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoFlag::Init() +{ + D3DVECTOR wind; + float angle; + + m_time = 0.0f; + m_param = 0; + m_progress = 0.0f; + + wind = m_terrain->RetWind(); + angle = RotateAngle(wind.x, -wind.z); + m_object->SetAngleY(0, angle); // oriente le drapeau dans le vent + + m_strong = Length(wind); +} + + +// Début d'une action (1 = secoue). + +void CAutoFlag::Start(int param) +{ + if ( m_param == 0 ) + { + m_param = param; + m_progress = 0.0f; + } +} + + +// Gestion d'un événement. + +BOOL CAutoFlag::EventProcess(const Event &event) +{ + float angle; + int i; + + CAuto::EventProcess(event); + +#if ADJUST_ANGLE + if ( event.event == EVENT_KEYDOWN ) + { + if ( event.param == 'E' ) g_flag1 += 0.1f; + if ( event.param == 'D' ) g_flag1 -= 0.1f; + if ( event.param == 'R' ) g_flag2 += 0.1f; + if ( event.param == 'F' ) g_flag2 -= 0.1f; + if ( event.param == 'T' ) g_flag3 += 0.1f; + if ( event.param == 'G' ) g_flag3 -= 0.1f; + } +#endif + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + if ( m_param == 1 ) // secoue ? + { + m_progress += event.rTime*(1.0f/2.0f); + if ( m_progress < 1.0f ) + { + angle = sinf(m_progress*PI*8.0f)*0.3f*(1.0f-m_progress); + m_object->SetAngleX(0, angle); + angle = sinf(m_progress*PI*4.0f)*0.3f*(1.0f-m_progress); + m_object->SetAngleZ(0, angle); + } + else + { + m_object->SetAngleX(0, 0.0f); + m_object->SetAngleZ(0, 0.0f); + m_param = 0; + m_progress = 0.0f; + } + } + + if ( m_strong == 0.0f ) return TRUE; // pas de vent ? + + for ( i=0 ; i<4 ; i++ ) + { +#if ADJUST_ANGLE + angle = sinf(m_time*g_flag1+i*2.0f)*((i+g_flag3)*g_flag2); +#else + angle = sinf(m_time*6.0f+i*2.0f)*((i+2.0f)*0.1f); +#endif + m_object->SetAngleY(1+i, angle); + } + +#if ADJUST_ANGLE + char s[100]; + sprintf(s, "a=%.2f b=%.2f c=%.2f", g_flag1, g_flag2, g_flag3); + m_engine->SetInfoText(4, s); +#endif + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoFlag::RetError() +{ + return ERR_OK; +} + + diff --git a/src/autoflag.h b/src/autoflag.h new file mode 100644 index 00000000..9d928c59 --- /dev/null +++ b/src/autoflag.h @@ -0,0 +1,40 @@ +// autoflag.h + +#ifndef _AUTOFLAG_H_ +#define _AUTOFLAG_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + +enum ObjectType; + + + +class CAutoFlag : public CAuto +{ +public: + CAutoFlag(CInstanceManager* iMan, CObject* object); + ~CAutoFlag(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + void Start(int param); + BOOL EventProcess(const Event &event); + Error RetError(); + +protected: + +protected: + float m_strong; + int m_param; + float m_progress; +}; + + +#endif //_AUTOFLAG_H_ diff --git a/src/autohuston.cpp b/src/autohuston.cpp new file mode 100644 index 00000000..5e5c2823 --- /dev/null +++ b/src/autohuston.cpp @@ -0,0 +1,301 @@ +// autohuston.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "auto.h" +#include "autohuston.h" + + + + +// Constructeur de l'objet. + +CAutoHuston::CAutoHuston(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + D3DVECTOR pos; + int i; + + CAuto::CAuto(iMan, object); + + for ( i=0 ; iRetPosition(0); + m_lens[0].type = PARTISELR; + m_lens[1].type = PARTISELR; + m_lens[2].type = PARTISELR; + m_lens[3].type = PARTISELR; + m_lens[0].pos = pos+D3DVECTOR(0.0f+13.0f, 34.0f, 30.0f ); + m_lens[1].pos = pos+D3DVECTOR(0.0f-13.0f, 34.0f, 30.0f ); + m_lens[2].pos = pos+D3DVECTOR(0.0f , 34.0f, 30.0f+13.0f); + m_lens[3].pos = pos+D3DVECTOR(0.0f , 34.0f, 30.0f-13.0f); + m_lens[0].dim = 4.0f; + m_lens[1].dim = 4.0f; + m_lens[2].dim = 4.0f; + m_lens[3].dim = 4.0f; + m_lens[0].total = 1.0f; + m_lens[1].total = 1.0f; + m_lens[2].total = 1.0f; + m_lens[3].total = 1.0f; + m_lens[0].off = 0.4f; + m_lens[1].off = 0.4f; + m_lens[2].off = 0.4f; + m_lens[3].off = 0.4f; + + // Pièce sous radar. + i = 4; + m_lens[i].type = PARTISELR; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 9.9f, 40.1f); + m_lens[i].dim = 1.8f; + m_lens[i].total = 0.4f; + m_lens[i].off = 0.2f; + i ++; + + m_lens[i].type = PARTISELY; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 7.2f, 34.8f); + m_lens[i].dim = 0.4f; + m_lens[i].total = 0.7f; + m_lens[i].off = 0.3f; + i ++; + m_lens[i].type = PARTISELY; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 6.5f, 34.3f); + m_lens[i].dim = 0.4f; + m_lens[i].total = 0.7f; + m_lens[i].off = 0.3f; + i ++; + m_lens[i].type = PARTISELR; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 6.5f, 33.4f); + m_lens[i].dim = 0.4f; + m_lens[i].total = 0.0f; + m_lens[i].off = 0.0f; + i ++; + m_lens[i].type = PARTISELR; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 6.5f, 33.0f); + m_lens[i].dim = 0.4f; + m_lens[i].total = 1.0f; + m_lens[i].off = 0.5f; + i ++; + + m_lens[i].type = PARTISELY; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 8.5f, 14.0f); + m_lens[i].dim = 1.2f; + m_lens[i].total = 0.8f; + m_lens[i].off = 0.2f; + i ++; + + m_lens[i].type = PARTISELR; + m_lens[i].pos = pos+D3DVECTOR(4.0f, 6.0f, 8.6f); + m_lens[i].dim = 1.0f; + m_lens[i].total = 0.9f; + m_lens[i].off = 0.7f; + i ++; + + // Pièce avec 3 fenêtres. + m_lens[i].type = PARTISELR; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 9.9f, -19.9f); + m_lens[i].dim = 1.0f; + m_lens[i].total = 0.6f; + m_lens[i].off = 0.3f; + i ++; + + m_lens[i].type = PARTISELY; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 7.2f, 34.8f-60.0f); + m_lens[i].dim = 0.4f; + m_lens[i].total = 0.7f; + m_lens[i].off = 0.3f; + i ++; + m_lens[i].type = PARTISELY; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 6.5f, 34.3f-60.0f); + m_lens[i].dim = 0.4f; + m_lens[i].total = 0.0f; + m_lens[i].off = 0.0f; + i ++; + m_lens[i].type = PARTISELR; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 6.5f, 33.4f-60.0f); + m_lens[i].dim = 0.4f; + m_lens[i].total = 0.6f; + m_lens[i].off = 0.4f; + i ++; + m_lens[i].type = PARTISELR; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 6.5f, 33.0f-60.0f); + m_lens[i].dim = 0.4f; + m_lens[i].total = 0.8f; + m_lens[i].off = 0.2f; + i ++; + + m_lens[i].type = PARTISELY; + m_lens[i].pos = pos+D3DVECTOR(-6.5f, 13.5f, -37.0f); + m_lens[i].dim = 1.0f; + m_lens[i].total = 0.0f; + m_lens[i].off = 0.0f; + i ++; + + m_lens[i].type = PARTISELY; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 12.2f, -39.8f); + m_lens[i].dim = 1.8f; + m_lens[i].total = 1.5f; + m_lens[i].off = 0.5f; + i ++; + + m_lens[i].type = PARTISELY; + m_lens[i].pos = pos+D3DVECTOR(-7.0f, 8.5f, -47.0f); + m_lens[i].dim = 0.6f; + m_lens[i].total = 0.7f; + m_lens[i].off = 0.5f; + i ++; + + m_lensTotal = i; + + Init(); +} + +// Destructeur de l'objet. + +CAutoHuston::~CAutoHuston() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoHuston::DeleteObject(BOOL bAll) +{ + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoHuston::Init() +{ + m_time = 0.0f; + + m_progress = 0.0f; + m_speed = 1.0f/2.0f; +} + + +// Démarre l'objet. + +void CAutoHuston::Start(int param) +{ +} + + +// Gestion d'un événement. + +BOOL CAutoHuston::EventProcess(const Event &event) +{ + D3DVECTOR speed; + FPOINT dim; + float angle; + int i; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + + angle = -m_time*1.0f; + m_object->SetAngleY(1, angle); // fait tourner le radar + angle = sinf(m_time*4.0f)*0.3f; + m_object->SetAngleX(2, angle); + + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + + // Fait clignotter les clés. + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + for ( i=0 ; iDeleteParticule(m_lens[i].parti); + m_lens[i].parti = -1; + } + } + else + { + if ( m_lens[i].parti == -1 ) + { + dim.x = m_lens[i].dim; + dim.y = dim.x; + m_lens[i].parti = m_particule->CreateParticule(m_lens[i].pos, speed, dim, m_lens[i].type, 1.0f, 0.0f, 0.0f); + } + } + } + + return TRUE; +} + +// Stoppe l'automate. + +BOOL CAutoHuston::Abort() +{ + return TRUE; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoHuston::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 115, EVENT_OBJECT_TYPE); + + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoHuston::RetError() +{ + return ERR_OK; +} + diff --git a/src/autohuston.h b/src/autohuston.h new file mode 100644 index 00000000..150edd56 --- /dev/null +++ b/src/autohuston.h @@ -0,0 +1,59 @@ +// autohuston.h + +#ifndef _AUTOHUSTON_H_ +#define _AUTOHUSTON_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + +enum ParticuleType; + + + +typedef struct +{ + int parti; + ParticuleType type; + D3DVECTOR pos; + float dim; + float total; + float off; +} +HustonLens; + + +#define HUSTONMAXLENS 20 + + +class CAutoHuston : public CAuto +{ +public: + CAutoHuston(CInstanceManager* iMan, CObject* object); + ~CAutoHuston(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + void Start(int param); + BOOL EventProcess(const Event &event); + BOOL Abort(); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + +protected: + +protected: + float m_progress; + float m_speed; + HustonLens m_lens[HUSTONMAXLENS]; + int m_lensTotal; +}; + + +#endif //_AUTOHUSTON_H_ diff --git a/src/autoinfo.cpp b/src/autoinfo.cpp new file mode 100644 index 00000000..f6de5a6c --- /dev/null +++ b/src/autoinfo.cpp @@ -0,0 +1,523 @@ +// autoinfo.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "list.h" +#include "window.h" +#include "sound.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autoinfo.h" + + + + +// Constructeur de l'objet. + +CAutoInfo::CAutoInfo(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); +} + +// Destructeur de l'objet. + +CAutoInfo::~CAutoInfo() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoInfo::DeleteObject(BOOL bAll) +{ + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoInfo::Init() +{ + m_phase = AIP_WAIT; + m_time = 0.0f; + m_timeVirus = 0.0f; + m_bLastVirus = FALSE; + + CAuto::Init(); +} + + +// Démarre une émission. + +void CAutoInfo::Start(int param) +{ + D3DVECTOR pos, speed; + FPOINT dim; + + if ( param == 0 ) // instruction "receive" ? + { + m_phase = AIP_EMETTE; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + else if ( param == 2 ) // instruction "send" ? + { + m_phase = AIP_RECEIVE; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + else + { + m_phase = AIP_ERROR; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + + m_lastParticule = 0; + m_goal = m_object->RetPosition(0); + + if ( m_phase == AIP_EMETTE ) + { + pos = m_goal; + pos.y += 9.5f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 30.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISPHERE4, 1.5f, 0.0f, 0.0f); + + m_sound->Play(SOUND_LABO, pos, 1.0f, 2.0f); + } + if ( m_phase == AIP_RECEIVE ) + { + pos = m_goal; + pos.y += 9.5f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 50.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISPHERE6, 1.5f, 0.0f, 0.0f); + + m_sound->Play(SOUND_LABO, pos, 1.0f, 2.0f); + } + if ( m_phase == AIP_ERROR ) + { + m_sound->Play(SOUND_GGG, pos, 1.0f, 0.5f); + } +} + + +// Gestion d'un événement. + +BOOL CAutoInfo::EventProcess(const Event &event) +{ + D3DVECTOR pos, speed; + FPOINT dim; + float duration, angle, rTime; + int i; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + + angle = m_object->RetAngleY(1); + angle += Rand()*0.3f; + m_object->SetAngleY(1, angle); + + m_object->SetAngleX(2, (Rand()-0.5f)*0.3f); + m_object->SetAngleX(4, (Rand()-0.5f)*0.3f); + m_object->SetAngleX(6, (Rand()-0.5f)*0.3f); + + m_object->SetAngleZ(2, (Rand()-0.5f)*0.3f); + m_object->SetAngleZ(4, (Rand()-0.5f)*0.3f); + m_object->SetAngleZ(6, (Rand()-0.5f)*0.3f); + + UpdateListVirus(); + } + m_bLastVirus = TRUE; + return TRUE; + } + else + { + if ( m_bLastVirus ) + { + m_bLastVirus = FALSE; + UpdateList(); // remet la liste normalement + } + else + { + if ( m_object->RetInfoUpdate() ) + { + UpdateList(); // actualise la liste + } + } + } + + UpdateInterface(event.rTime); + + rTime = event.rTime; + + if ( m_phase == AIP_EMETTE ) // instruction "receive" ? + { + if ( m_progress < 0.5f && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + for ( i=0 ; i<4 ; i++ ) + { + pos = m_goal; + pos.y += 9.5f; + speed.x = (Rand()-0.5f)*50.0f; + speed.z = (Rand()-0.5f)*50.0f; + speed.y = (Rand()-0.5f)*50.0f; + speed *= 0.5f+m_progress*0.5f; + dim.x = 0.6f; + dim.y = dim.x; + duration = Rand()*0.5f+0.5f; + m_particule->CreateTrack(pos, speed, dim, PARTITRACK6, + duration, 0.0f, + duration*0.9f, 0.7f); + } + } + + if ( m_progress < 1.0f ) + { + m_progress += rTime*m_speed; + + m_object->SetAngleZ(2, m_progress*2.0f*PI); + m_object->SetAngleZ(4, m_progress*2.0f*PI); + m_object->SetAngleZ(6, m_progress*2.0f*PI); + } + else + { + m_phase = AIP_WAIT; + + m_object->SetAngleX(2, 0.0f); + m_object->SetAngleX(4, 0.0f); + m_object->SetAngleX(6, 0.0f); + + m_object->SetAngleZ(2, 0.0f); + m_object->SetAngleZ(4, 0.0f); + m_object->SetAngleZ(6, 0.0f); + } + } + + if ( m_phase == AIP_RECEIVE ) // instruction "send" ? + { + if ( m_progress < 0.5f && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + for ( i=0 ; i<4 ; i++ ) + { + pos = m_goal; + pos.y += 9.5f; + speed = pos; + pos.x += (Rand()-0.5f)*40.0f; + pos.y += (Rand()-0.5f)*40.0f; + pos.z += (Rand()-0.5f)*40.0f; + speed = (speed-pos)*1.0f; +//? speed *= 0.5f+m_progress*0.5f; + dim.x = 0.6f; + dim.y = dim.x; + duration = Rand()*0.5f+0.5f; + m_particule->CreateTrack(pos, speed, dim, PARTITRACK6, + duration, 0.0f, + duration*0.9f, 0.7f); + } + } + + if ( m_progress < 1.0f ) + { + m_progress += rTime*m_speed; + + m_object->SetAngleZ(2, m_progress*2.0f*PI); + m_object->SetAngleZ(4, m_progress*2.0f*PI); + m_object->SetAngleZ(6, m_progress*2.0f*PI); + } + else + { + m_phase = AIP_WAIT; + + m_object->SetAngleX(2, 0.0f); + m_object->SetAngleX(4, 0.0f); + m_object->SetAngleX(6, 0.0f); + + m_object->SetAngleZ(2, 0.0f); + m_object->SetAngleZ(4, 0.0f); + m_object->SetAngleZ(6, 0.0f); + } + } + + if ( m_phase == AIP_ERROR ) + { + if ( m_progress < 0.5f && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_goal; + speed.x = (Rand()-0.5f)*5.0f; + speed.z = (Rand()-0.5f)*5.0f; + speed.y = 5.0f+Rand()*5.0f; + dim.x = 5.0f+Rand()*5.0f; + dim.y = dim.x; + duration = Rand()*0.5f+0.5f; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE1, 4.0f); + } + + if ( m_progress < 1.0f ) + { + m_progress += rTime*m_speed; + rTime = 0.0f; // stoppe la rotation + + if ( m_progress < 0.5f ) + { + angle = m_progress/0.5f; + } + else + { + angle = 1.0f-(m_progress-0.5f)/0.5f; + } + m_object->SetAngleX(2, angle*0.5f); + m_object->SetAngleX(4, angle*0.5f); + m_object->SetAngleX(6, angle*0.5f); + + m_object->SetAngleZ(2, (Rand()-0.5f)*0.2f); + m_object->SetAngleZ(4, (Rand()-0.5f)*0.2f); + m_object->SetAngleZ(6, (Rand()-0.5f)*0.2f); + } + else + { + m_phase = AIP_WAIT; + + m_object->SetAngleX(2, 0.0f); + m_object->SetAngleX(4, 0.0f); + m_object->SetAngleX(6, 0.0f); + + m_object->SetAngleZ(2, 0.0f); + m_object->SetAngleZ(4, 0.0f); + m_object->SetAngleZ(6, 0.0f); + } + } + + angle = m_object->RetAngleY(1); + angle += rTime*0.5f; + m_object->SetAngleY(1, angle); + + m_object->SetAngleX(3, sinf(m_time*6.0f+PI*0.0f/3.0f)*0.3f); + m_object->SetAngleX(5, sinf(m_time*6.0f+PI*2.0f/3.0f)*0.3f); + m_object->SetAngleX(7, sinf(m_time*6.0f+PI*4.0f/3.0f)*0.3f); + + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoInfo::RetError() +{ + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + + return ERR_OK; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoInfo::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + CList* pl; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*7.0f; + pos.y = oy+sy*0.0f; + ddim.x = 160.0f/640.0f; + ddim.y = 66.0f/480.0f; + pl = pw->CreateList(pos, ddim, 1, EVENT_OBJECT_GINFO, 1.10f); + pl->SetSelectCap(FALSE); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 112, EVENT_OBJECT_TYPE); + + UpdateList(); + return TRUE; +} + +// Met à jour l'état de tous les boutons de l'interface, +// suite au temps qui s'écoule ... + +void CAutoInfo::UpdateInterface(float rTime) +{ + CAuto::UpdateInterface(rTime); +} + + +// Met à jour le contenu de la liste. + +void CAutoInfo::UpdateList() +{ + CWindow* pw; + CList* pl; + Info info; + int total, i; + char text[100]; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + pl = (CList*)pw->SearchControl(EVENT_OBJECT_GINFO); + if ( pl == 0 ) return; + + pl->Flush(); + total = m_object->RetInfoTotal(); + if ( total == 0 ) + { + pl->ClearState(STATE_ENABLE); + } + else + { + pl->SetState(STATE_ENABLE); + + for ( i=0 ; iRetInfo(i); + sprintf(text, "%s = %.2f", info.name, info.value); + pl->SetName(i, text); + } + } + + m_object->SetInfoUpdate(FALSE); +} + +// Met à jour le contenu contaminé de la liste. + +void CAutoInfo::UpdateListVirus() +{ + CWindow* pw; + CList* pl; + int i, j, max; + char text[100]; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + pl = (CList*)pw->SearchControl(EVENT_OBJECT_GINFO); + if ( pl == 0 ) return; + + pl->SetState(STATE_ENABLE); + + pl->Flush(); + for ( i=0 ; i<4 ; i++ ) + { + max = (int)(2.0f+Rand()*10.0f); + for ( j=0 ; jSetName(i, text); + } +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoInfo::Write(char *line) +{ + char name[100]; + + if ( m_phase == AIP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoInfo::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoInfoPhase)OpInt(line, "aPhase", AIP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastParticule = 0.0f; + + return TRUE; +} + + diff --git a/src/autoinfo.h b/src/autoinfo.h new file mode 100644 index 00000000..f7ce998c --- /dev/null +++ b/src/autoinfo.h @@ -0,0 +1,60 @@ +// autoinfo.h + +#ifndef _AUTOINFO_H_ +#define _AUTOINFO_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoInfoPhase +{ + AIP_WAIT = 1, + AIP_EMETTE = 2, + AIP_RECEIVE = 3, + AIP_ERROR = 4, +}; + + + +class CAutoInfo : public CAuto +{ +public: + CAutoInfo(CInstanceManager* iMan, CObject* object); + ~CAutoInfo(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + void Start(int param); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + void UpdateInterface(float rTime); + void UpdateList(); + void UpdateListVirus(); + +protected: + AutoInfoPhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastParticule; + D3DVECTOR m_goal; + BOOL m_bLastVirus; +}; + + +#endif //_AUTOINFO_H_ diff --git a/src/autojostle.cpp b/src/autojostle.cpp new file mode 100644 index 00000000..057ca905 --- /dev/null +++ b/src/autojostle.cpp @@ -0,0 +1,153 @@ +// autojostle.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "list.h" +#include "window.h" +#include "sound.h" +#include "auto.h" +#include "autojostle.h" + + + + +// Constructeur de l'objet. + +CAutoJostle::CAutoJostle(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); +} + +// Destructeur de l'objet. + +CAutoJostle::~CAutoJostle() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoJostle::DeleteObject(BOOL bAll) +{ + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoJostle::Init() +{ + m_time = 0.0f; + m_error = ERR_CONTINUE; + + CAuto::Init(); +} + + +// Démarre une émission. + +void CAutoJostle::Start(int param, float force) +{ + ObjectType type; + + if ( force < 0.0f ) force = 0.0f; + if ( force > 1.0f ) force = 1.0f; + + m_force = force; + m_progress = 0.0f; + m_speed = 1.0f/(0.5f+force*1.0f); // 0.5 .. 1.5 + m_time = 0.0f; + m_error = ERR_CONTINUE; + + type = m_object->RetType(); + if ( type >= OBJECT_PLANT5 && + type <= OBJECT_PLANT7 ) // trèfle ? + { + m_force *= 3.0f; + } +} + + +// Gestion d'un événement. + +BOOL CAutoJostle::EventProcess(const Event &event) +{ + D3DVECTOR dir; + float factor, angle, zoom; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + if ( m_progress < 1.0f ) + { + m_progress += event.rTime*m_speed; + + if ( m_progress < 0.5f ) + { + factor = m_progress/0.5f; + } + else + { + factor = 2.0f-m_progress/0.5f; + } + factor *= m_force; + + dir.x = sinf(m_progress*PI*4.0f); + dir.z = cosf(m_progress*PI*4.0f); + + angle = sinf(m_time*10.0f)*factor*0.04f; + m_object->SetAngleX(0, angle*dir.z); + m_object->SetAngleZ(0, angle*dir.x); + + zoom = 1.0f+sinf(m_time*8.0f)*factor*0.06f; + m_object->SetZoomX(0, zoom); + zoom = 1.0f+sinf(m_time*5.0f)*factor*0.06f; + m_object->SetZoomY(0, zoom); + zoom = 1.0f+sinf(m_time*7.0f)*factor*0.06f; + m_object->SetZoomZ(0, zoom); + } + else + { + m_object->SetAngleX(0, 0.0f); + m_object->SetAngleZ(0, 0.0f); + m_object->SetZoom(0, D3DVECTOR(1.0f, 1.0f, 1.0f)); + m_error = ERR_STOP; + } + + return TRUE; +} + + +// Indique si l'automate a terminé son activité. + +Error CAutoJostle::IsEnded() +{ + return m_error; +} + + diff --git a/src/autojostle.h b/src/autojostle.h new file mode 100644 index 00000000..3527c938 --- /dev/null +++ b/src/autojostle.h @@ -0,0 +1,40 @@ +// autojostle.h + +#ifndef _AUTOJOSTLE_H_ +#define _AUTOJOSTLE_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +class CAutoJostle : public CAuto +{ +public: + CAutoJostle(CInstanceManager* iMan, CObject* object); + ~CAutoJostle(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + void Start(int param, float force); + BOOL EventProcess(const Event &event); + Error IsEnded(); + +protected: + +protected: + float m_force; + float m_progress; + float m_speed; + float m_lastParticule; + Error m_error; +}; + + +#endif //_AUTOJOSTLE_H_ diff --git a/src/autokid.cpp b/src/autokid.cpp new file mode 100644 index 00000000..46853190 --- /dev/null +++ b/src/autokid.cpp @@ -0,0 +1,208 @@ +// autokid.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "water.h" +#include "camera.h" +#include "object.h" +#include "sound.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autokid.h" + + + + +// Constructeur de l'objet. + +CAutoKid::CAutoKid(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + m_soundChannel = -1; + Init(); +} + +// Destructeur de l'objet. + +CAutoKid::~CAutoKid() +{ + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoKid::DeleteObject(BOOL bAll) +{ + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoKid::Init() +{ + D3DVECTOR pos; + + m_speed = 1.0f/1.0f; + m_progress = 0.0f; + m_lastParticule = 0.0f; + + if ( m_type == OBJECT_TEEN36 ) // tronc ? + { + pos = m_object->RetPosition(0); + m_speed = 1.0f/(1.0f+(Mod(pos.x/10.0f-0.5f, 1.0f)*0.2f)); + m_progress = Mod(pos.x/10.0f, 1.0f); + } + + if ( m_type == OBJECT_TEEN37 ) // bateau ? + { + pos = m_object->RetPosition(0); + m_speed = 1.0f/(1.0f+(Mod(pos.x/10.0f-0.5f, 1.0f)*0.2f))*2.5f; + m_progress = Mod(pos.x/10.0f, 1.0f); + } + + if ( m_type == OBJECT_TEEN38 ) // ventillateur ? + { + if ( m_soundChannel == -1 ) + { +//? m_soundChannel = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 1.0f, 0.5f, TRUE); + m_bSilent = FALSE; + } + } +} + + +// Gestion d'un événement. + +BOOL CAutoKid::EventProcess(const Event &event) +{ + D3DVECTOR vib, pos, speed; + FPOINT dim; + + CAuto::EventProcess(event); + + if ( m_soundChannel != -1 ) + { + if ( m_engine->RetPause() ) + { + if ( !m_bSilent ) + { + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.5f, 0.1f, SOPER_CONTINUE); + m_bSilent = TRUE; + } + } + else + { + if ( m_bSilent ) + { + m_sound->AddEnvelope(m_soundChannel, 1.0f, 0.5f, 0.1f, SOPER_CONTINUE); + m_bSilent = FALSE; + } + } + } + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + + if ( m_type == OBJECT_TEEN36 ) // tronc ? + { + vib.x = 0.0f; + vib.y = sinf(m_progress)*1.0f; + vib.z = 0.0f; + m_object->SetLinVibration(vib); + + vib.x = 0.0f; + vib.y = 0.0f; + vib.z = sinf(m_progress*0.5f)*0.05f; + m_object->SetCirVibration(vib); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.15f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + pos.y = m_water->RetLevel()+1.0f; + pos.x += (Rand()-0.5f)*50.0f; + pos.z += (Rand()-0.5f)*50.0f; + speed.y = 0.0f; + speed.x = 0.0f; + speed.z = 0.0f; + dim.x = 50.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFLIC, 3.0f, 0.0f, 0.0f); + } + } + + if ( m_type == OBJECT_TEEN37 ) // bateau ? + { + vib.x = 0.0f; + vib.y = sinf(m_progress)*1.0f; + vib.z = 0.0f; + m_object->SetLinVibration(vib); + + vib.x = 0.0f; + vib.y = 0.0f; + vib.z = sinf(m_progress*0.5f)*0.15f; + m_object->SetCirVibration(vib); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.15f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + pos.y = m_water->RetLevel()+1.0f; + pos.x += (Rand()-0.5f)*20.0f; + pos.z += (Rand()-0.5f)*20.0f; + speed.y = 0.0f; + speed.x = 0.0f; + speed.z = 0.0f; + dim.x = 20.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFLIC, 3.0f, 0.0f, 0.0f); + } + } + + if ( m_type == OBJECT_TEEN38 ) // ventillateur ? + { + m_object->SetAngleY(1, sinf(m_progress*0.6f)*0.4f); + m_object->SetAngleX(2, m_progress*5.0f); + } + + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoKid::RetError() +{ + return ERR_OK; +} + + diff --git a/src/autokid.h b/src/autokid.h new file mode 100644 index 00000000..a51effd0 --- /dev/null +++ b/src/autokid.h @@ -0,0 +1,41 @@ +// autokid.h + +#ifndef _AUTOKID_H_ +#define _AUTOKID_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + +enum ObjectType; + + + +class CAutoKid : public CAuto +{ +public: + CAutoKid(CInstanceManager* iMan, CObject* object); + ~CAutoKid(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + +protected: + +protected: + float m_speed; + float m_progress; + float m_lastParticule; + int m_soundChannel; + BOOL m_bSilent; +}; + + +#endif //_AUTOKID_H_ diff --git a/src/autolabo.cpp b/src/autolabo.cpp new file mode 100644 index 00000000..8fc3da2f --- /dev/null +++ b/src/autolabo.cpp @@ -0,0 +1,615 @@ +// autolabo.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "displaytext.h" +#include "sound.h" +#include "robotmain.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autolabo.h" + + + +#define LABO_DELAY 20.0f // durée de l'analyse + + + + +// Constructeur de l'objet. + +CAutoLabo::CAutoLabo(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + int i; + + CAuto::CAuto(iMan, object); + + for ( i=0 ; i<3 ; i++ ) + { + m_partiRank[i] = -1; + } + m_partiSphere = -1; + + m_soundChannel = -1; + Init(); +} + +// Destructeur de l'objet. + +CAutoLabo::~CAutoLabo() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoLabo::DeleteObject(BOOL bAll) +{ + int i; + + for ( i=0 ; i<3 ; i++ ) + { + if ( m_partiRank[i] != -1 ) + { + m_particule->DeleteParticule(m_partiRank[i]); + m_partiRank[i] = -1; + } + } + + if ( m_partiSphere != -1 ) + { + m_particule->DeleteParticule(m_partiSphere); + m_partiSphere = -1; + } + + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoLabo::Init() +{ + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastParticule = 0.0f; + + m_phase = ALAP_WAIT; // attend ... + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + + CAuto::Init(); +} + + +// Gestion d'un événement. + +BOOL CAutoLabo::EventProcess(const Event &event) +{ + CObject* power; + D3DVECTOR pos, goal, speed; + FPOINT dim, rot; + float angle; + int i; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + + if ( event.event == EVENT_UPDINTERFACE ) + { + if ( m_object->RetSelect() ) CreateInterface(TRUE); + } + + if ( m_object->RetSelect() && // centre sélectionné ? + (event.event == EVENT_OBJECT_RiPAW || + event.event == EVENT_OBJECT_RiGUN) ) + { + if ( m_phase != ALAP_WAIT ) + { + return FALSE; + } + + m_research = event.event; + + if ( TestResearch(m_research) ) + { + m_displayText->DisplayError(ERR_LABO_ALREADY, m_object); + return FALSE; + } + + power = m_object->RetPower(); + if ( power == 0 ) + { + m_displayText->DisplayError(ERR_LABO_NULL, m_object); + return FALSE; + } + if ( power->RetType() != OBJECT_BULLET ) + { + m_displayText->DisplayError(ERR_LABO_BAD, m_object); + return FALSE; + } + + SetBusy(TRUE); + InitProgressTotal(1.0f+1.5f+1.5f+LABO_DELAY+1.5f+1.5f+1.0f); + UpdateInterface(); + + power->SetLock(TRUE); // boulet plus utilisable + + SoundManip(1.0f, 1.0f, 1.0f); + m_phase = ALAP_OPEN1; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + return TRUE; + } + + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + } + return TRUE; + } + + EventProgress(event.rTime); + + if ( m_phase == ALAP_WAIT ) + { + if ( m_progress >= 1.0f ) + { + m_phase = ALAP_WAIT; // attend encore ... + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_phase == ALAP_OPEN1 ) + { + if ( m_progress < 1.0f ) + { + angle = 80.0f-(35.0f*m_progress); + m_object->SetAngleZ(3, angle*PI/180.0f); + m_object->SetAngleZ(4, angle*PI/180.0f); + m_object->SetAngleZ(5, angle*PI/180.0f); + } + else + { + m_object->SetAngleZ(3, 45.0f*PI/180.0f); + m_object->SetAngleZ(4, 45.0f*PI/180.0f); + m_object->SetAngleZ(5, 45.0f*PI/180.0f); + + SoundManip(1.5f, 1.0f, 0.7f); + m_phase = ALAP_OPEN2; + m_progress = 0.0f; + m_speed = 1.0f/1.5f; + } + } + + if ( m_phase == ALAP_OPEN2 ) + { + if ( m_progress < 1.0f ) + { + pos.x = -9.0f; + pos.y = 3.0f+m_progress*10.0f; + pos.z = 0.0f; + m_object->SetPosition(1, pos); + } + else + { + m_object->SetPosition(1, D3DVECTOR(-9.0f, 13.0f, 0.0f)); + + SoundManip(1.5f, 1.0f, 0.5f); + m_phase = ALAP_OPEN3; + m_progress = 0.0f; + m_speed = 1.0f/1.5f; + } + } + + if ( m_phase == ALAP_OPEN3 ) + { + if ( m_progress < 1.0f ) + { + angle = (1.0f-m_progress)*PI/2.0f; + m_object->SetAngleZ(1, angle); + } + else + { + m_object->SetAngleZ(1, 0.0f); + + goal = m_object->RetPosition(0); + goal.y += 3.0f; + pos = goal; + pos.x -= 4.0f; + pos.y += 4.0f; + for ( i=0 ; i<3 ; i++ ) + { + m_partiRank[i] = m_particule->CreateRay(pos, goal, + PARTIRAY2, + FPOINT(2.9f, 2.9f), + LABO_DELAY); + } + + m_soundChannel = m_sound->Play(SOUND_LABO, m_object->RetPosition(0), 0.0f, 0.25f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 0.60f, 2.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 2.00f, 8.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 0.60f, 8.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.25f, 2.0f, SOPER_STOP); + + pos = m_object->RetPosition(0); + pos.y += 4.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 4.0f; + dim.y = dim.x; + m_partiSphere = m_particule->CreateParticule(pos, speed, dim, PARTISPHERE2, LABO_DELAY, 0.0f, 0.0f); + + m_phase = ALAP_ANALYSE; + m_progress = 0.0f; + m_speed = 1.0f/LABO_DELAY; + } + } + + if ( m_phase == ALAP_ANALYSE ) + { + if ( m_progress < 1.0f ) + { + power = m_object->RetPower(); + if ( power != 0 ) + { + power->SetZoom(0, 1.0f-m_progress); + } + + angle = m_object->RetAngleY(2); + if ( m_progress < 0.5f ) + { + angle -= event.rTime*m_progress*20.0f; + } + else + { + angle -= event.rTime*(20.0f-m_progress*20.0f); + } + m_object->SetAngleY(2, angle); // tourne l'analyseur + + angle += m_object->RetAngleY(0); + for ( i=0 ; i<3 ; i++ ) + { + rot = RotatePoint(-angle, -4.0f); + pos = m_object->RetPosition(0); + pos.x += rot.x; + pos.z += rot.y; + pos.y += 3.0f+4.0f;; + m_particule->SetPosition(m_partiRank[i], pos); // ajuste rayon + + angle += PI*2.0f/3.0f; + } + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + if ( m_progress > 0.25f && + m_progress < 0.80f ) + { + pos = m_object->RetPosition(0); + pos.y += 3.0f; + pos.x += (Rand()-0.5f)*2.0f; + pos.z += (Rand()-0.5f)*2.0f; + speed.y = Rand()*5.0f+5.0f; + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + dim.x = Rand()*0.4f*m_progress+1.0f; + dim.y = dim.x; + m_particule->CreateTrack(pos, speed, dim, PARTITRACK2, + 2.0f+2.0f*m_progress, 10.0f, 1.5f, 1.4f); + } + } + } + else + { + SetResearch(m_research); // recherche effectuée + + power = m_object->RetPower(); + if ( power != 0 ) + { + m_object->SetPower(0); + power->DeleteObject(); // détruit le boulet + delete power; + } + + m_displayText->DisplayError(INFO_LABO, m_object); + + SoundManip(1.5f, 1.0f, 0.5f); + m_phase = ALAP_CLOSE1; + m_progress = 0.0f; + m_speed = 1.0f/1.5f; + } + } + + if ( m_phase == ALAP_CLOSE1 ) + { + if ( m_progress < 1.0f ) + { + angle = m_progress*PI/2.0f; + m_object->SetAngleZ(1, angle); + } + else + { + m_object->SetAngleZ(1, PI/2.0f); + + SoundManip(1.5f, 1.0f, 0.7f); + m_phase = ALAP_CLOSE2; + m_progress = 0.0f; + m_speed = 1.0f/1.5f; + } + } + + if ( m_phase == ALAP_CLOSE2 ) + { + if ( m_progress < 1.0f ) + { + pos.x = -9.0f; + pos.y = 3.0f+(1.0f-m_progress)*10.0f;; + pos.z = 0.0f; + m_object->SetPosition(1, pos); + } + else + { + m_object->SetPosition(1, D3DVECTOR(-9.0f, 3.0f, 0.0f)); + + SoundManip(1.0f, 1.0f, 1.0f); + m_phase = ALAP_CLOSE3; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == ALAP_CLOSE3 ) + { + if ( m_progress < 1.0f ) + { + angle = 45.0f+(35.0f*m_progress); + m_object->SetAngleZ(3, angle*PI/180.0f); + m_object->SetAngleZ(4, angle*PI/180.0f); + m_object->SetAngleZ(5, angle*PI/180.0f); + } + else + { + m_object->SetAngleZ(3, 80.0f*PI/180.0f); + m_object->SetAngleZ(4, 80.0f*PI/180.0f); + m_object->SetAngleZ(5, 80.0f*PI/180.0f); + + SetBusy(FALSE); + UpdateInterface(); + + m_phase = ALAP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoLabo::RetError() +{ + CObject* pObj; + ObjectType type; + + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + + pObj = m_object->RetPower(); + if ( pObj == 0 ) return ERR_LABO_NULL; + type = pObj->RetType(); + if ( type != OBJECT_BULLET ) return ERR_LABO_BAD; + + return ERR_OK; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoLabo::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, dim, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + dim.x = 33.0f/640.0f; + dim.y = 33.0f/480.0f; + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*7.0f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 64+45, EVENT_OBJECT_RiPAW); + + pos.x = ox+sx*8.0f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 64+46, EVENT_OBJECT_RiGUN); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 111, EVENT_OBJECT_TYPE); + + UpdateInterface(); + + return TRUE; +} + +// Met à jour l'état de tous les boutons de l'interface. + +void CAutoLabo::UpdateInterface() +{ + CWindow* pw; + + if ( !m_object->RetSelect() ) return; + + CAuto::UpdateInterface(); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + DeadInterface(pw, EVENT_OBJECT_RiPAW, g_researchEnable&RESEARCH_iPAW); + DeadInterface(pw, EVENT_OBJECT_RiGUN, g_researchEnable&RESEARCH_iGUN); + + OkayButton(pw, EVENT_OBJECT_RiPAW); + OkayButton(pw, EVENT_OBJECT_RiGUN); + + VisibleInterface(pw, EVENT_OBJECT_RiPAW, !m_bBusy); + VisibleInterface(pw, EVENT_OBJECT_RiGUN, !m_bBusy); +} + +// Indique les recherches déjà effectuées pour un bouton. + +void CAutoLabo::OkayButton(CWindow *pw, EventMsg event) +{ + CControl* control; + + control = pw->SearchControl(event); + if ( control == 0 ) return; + + control->SetState(STATE_OKAY, TestResearch(event)); +} + + +// Teste si une recherche a déjà été effectuée. + +BOOL CAutoLabo::TestResearch(EventMsg event) +{ + if ( event == EVENT_OBJECT_RiPAW ) return (g_researchDone & RESEARCH_iPAW); + if ( event == EVENT_OBJECT_RiGUN ) return (g_researchDone & RESEARCH_iGUN); + + return FALSE; +} + +// Indique une recherche comme effectuée. + +void CAutoLabo::SetResearch(EventMsg event) +{ + Event newEvent; + + if ( event == EVENT_OBJECT_RiPAW ) g_researchDone |= RESEARCH_iPAW; + if ( event == EVENT_OBJECT_RiGUN ) g_researchDone |= RESEARCH_iGUN; + + m_main->WriteFreeParam(); + + m_event->MakeEvent(newEvent, EVENT_UPDINTERFACE); + m_event->AddEvent(newEvent); + UpdateInterface(); +} + +// Fait entendre le son du bras manipulateur. + +void CAutoLabo::SoundManip(float time, float amplitude, float frequency) +{ + int i; + + i = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 0.0f, 0.3f*frequency, TRUE); + m_sound->AddEnvelope(i, 0.5f*amplitude, 1.0f*frequency, 0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.5f*amplitude, 1.0f*frequency, time-0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.0f, 0.3f*frequency, 0.1f, SOPER_STOP); +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoLabo::Write(char *line) +{ + D3DVECTOR pos; + char name[100]; + + if ( m_phase == ALAP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + sprintf(name, " aResearch=%d", m_research); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoLabo::Read(char *line) +{ + D3DVECTOR pos; + + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoLaboPhase)OpInt(line, "aPhase", ALAP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + m_research = (EventMsg)OpInt(line, "aResearch", 0); + + m_lastParticule = 0.0f; + + return TRUE; +} + + diff --git a/src/autolabo.h b/src/autolabo.h new file mode 100644 index 00000000..71a68bce --- /dev/null +++ b/src/autolabo.h @@ -0,0 +1,67 @@ +// autolabo.h + +#ifndef _AUTOLABO_H_ +#define _AUTOLABO_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoLaboPhase +{ + ALAP_WAIT = 1, + ALAP_OPEN1 = 2, + ALAP_OPEN2 = 3, + ALAP_OPEN3 = 4, + ALAP_ANALYSE = 5, + ALAP_CLOSE1 = 6, + ALAP_CLOSE2 = 7, + ALAP_CLOSE3 = 8, +}; + + + +class CAutoLabo : public CAuto +{ +public: + CAutoLabo(CInstanceManager* iMan, CObject* object); + ~CAutoLabo(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + void UpdateInterface(); + void OkayButton(CWindow *pw, EventMsg event); + BOOL TestResearch(EventMsg event); + void SetResearch(EventMsg event); + void SoundManip(float time, float amplitude, float frequency); + +protected: + AutoLaboPhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastParticule; + EventMsg m_research; + int m_partiRank[3]; + int m_partiSphere; + int m_soundChannel; +}; + + +#endif //_AUTOLABO_H_ diff --git a/src/automush.cpp b/src/automush.cpp new file mode 100644 index 00000000..e9a1a3ab --- /dev/null +++ b/src/automush.cpp @@ -0,0 +1,348 @@ +// automush.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "cmdtoken.h" +#include "sound.h" +#include "auto.h" +#include "automush.h" + + + + +// Constructeur de l'objet. + +CAutoMush::CAutoMush(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); +} + +// Destructeur de l'objet. + +CAutoMush::~CAutoMush() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoMush::DeleteObject(BOOL bAll) +{ + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoMush::Init() +{ + m_phase = AMP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/4.0f; + + m_time = 0.0f; + m_lastParticule = 0.0f; +} + + +// Gestion d'un événement. + +BOOL CAutoMush::EventProcess(const Event &event) +{ + D3DVECTOR pos, speed, dir; + FPOINT dim; + float factor, zoom, size, angle; + int i, channel; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + + factor = 0.0f; + size = 1.0f; + + if ( m_phase == AMP_WAIT ) + { + if ( m_progress >= 1.0f ) + { + if ( !SearchTarget() ) + { + m_phase = AMP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/(2.0f+Rand()*2.0f); + } + else + { + m_phase = AMP_SNIF; + m_progress = 0.0f; + m_speed = 1.0f/1.5f; + } + } + } + + if ( m_phase == AMP_SNIF ) + { + if ( m_progress < 1.0f ) + { + factor = m_progress; + } + else + { + m_phase = AMP_ZOOM; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == AMP_ZOOM ) + { + if ( m_progress < 1.0f ) + { + factor = 1.0f; + size = 1.0f+m_progress*0.3f; + } + else + { + m_sound->Play(SOUND_MUSHROOM, m_object->RetPosition(0)); + + m_phase = AMP_FIRE; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == AMP_FIRE ) + { + if ( m_progress < 1.0f ) + { + factor = 1.0f-m_progress; + size = 1.0f+(1.0f-m_progress)*0.3f; + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + for ( i=0 ; i<10 ; i++ ) + { + pos = m_object->RetPosition(0); + pos.y += 5.0f; + speed.x = (Rand()-0.5f)*200.0f; + speed.z = (Rand()-0.5f)*200.0f; + speed.y = -(20.0f+Rand()*20.0f); + dim.x = 1.0f; + dim.y = dim.x; + channel = m_particule->CreateParticule(pos, speed, dim, PARTIGUN2, 2.0f, 100.0f, 0.0f); + m_particule->SetObjectFather(channel, m_object); + } + } + } + else + { + m_phase = AMP_SMOKE; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_phase == AMP_SMOKE ) + { + if ( m_progress < 1.0f ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + pos.y += 5.0f; + speed.x = (Rand()-0.5f)*4.0f; + speed.z = (Rand()-0.5f)*4.0f; + speed.y = -(0.5f+Rand()*0.5f); + dim.x = Rand()*2.5f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE3, 4.0f, 0.0f, 0.0f); + } + } + else + { + m_phase = AMP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/(2.0f+Rand()*2.0f); + } + } + + if ( factor != 0.0f || size != 1.0f ) + { + dir.x = sinf(m_time*PI*4.0f); + dir.z = cosf(m_time*PI*4.0f); + + angle = sinf(m_time*10.0f)*factor*0.04f; + m_object->SetAngleX(0, angle*dir.z); + m_object->SetAngleZ(0, angle*dir.x); + + zoom = 1.0f+sinf(m_time*8.0f)*factor*0.06f; + m_object->SetZoomX(0, zoom*size); + zoom = 1.0f+sinf(m_time*5.0f)*factor*0.06f; + m_object->SetZoomY(0, zoom*size); + zoom = 1.0f+sinf(m_time*7.0f)*factor*0.06f; + m_object->SetZoomZ(0, zoom*size); + } + else + { + m_object->SetAngleX(0, 0.0f); + m_object->SetAngleZ(0, 0.0f); + m_object->SetZoom(0, D3DVECTOR(1.0f, 1.0f, 1.0f)); + } + + return TRUE; +} + + +// Cherche une cible proche. + +BOOL CAutoMush::SearchTarget() +{ + CObject* pObj; + D3DVECTOR iPos, oPos; + ObjectType type; + float dist; + int i; + + iPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetLock() ) continue; + + type = pObj->RetType(); + if ( type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis && + type != OBJECT_MOBILErt && + type != OBJECT_MOBILErc && + type != OBJECT_MOBILErr && + type != OBJECT_MOBILErs && + type != OBJECT_MOBILEsa && + type != OBJECT_MOBILEtg && + type != OBJECT_MOBILEft && + type != OBJECT_MOBILEtt && + type != OBJECT_MOBILEwt && + type != OBJECT_MOBILEit && + type != OBJECT_MOBILEdr && + type != OBJECT_DERRICK && + type != OBJECT_STATION && + type != OBJECT_FACTORY && + type != OBJECT_REPAIR && + type != OBJECT_DESTROYER&& + type != OBJECT_CONVERT && + type != OBJECT_TOWER && + type != OBJECT_RESEARCH && + type != OBJECT_RADAR && + type != OBJECT_INFO && + type != OBJECT_ENERGY && + type != OBJECT_LABO && + type != OBJECT_NUCLEAR && + type != OBJECT_PARA && + type != OBJECT_HUMAN ) continue; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, iPos); + if ( dist < 50.0f ) return TRUE; + } + + return FALSE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoMush::RetError() +{ + return ERR_OK; +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoMush::Write(char *line) +{ + D3DVECTOR pos; + char name[100]; + + if ( m_phase == AMP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoMush::Read(char *line) +{ + D3DVECTOR pos; + + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoMushPhase)OpInt(line, "aPhase", AMP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastParticule = 0.0f; + + return TRUE; +} + + diff --git a/src/automush.h b/src/automush.h new file mode 100644 index 00000000..03f2d284 --- /dev/null +++ b/src/automush.h @@ -0,0 +1,55 @@ +// automush.h + +#ifndef _AUTOMUSH_H_ +#define _AUTOMUSH_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + +enum ObjectType; + + + +enum AutoMushPhase +{ + AMP_WAIT = 1, + AMP_SNIF = 2, + AMP_ZOOM = 3, + AMP_FIRE = 4, + AMP_SMOKE = 5, +}; + + + +class CAutoMush : public CAuto +{ +public: + CAutoMush(CInstanceManager* iMan, CObject* object); + ~CAutoMush(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + BOOL SearchTarget(); + +protected: + AutoMushPhase m_phase; + float m_progress; + float m_speed; + float m_lastParticule; +}; + + +#endif //_AUTOMUSH_H_ diff --git a/src/autonest.cpp b/src/autonest.cpp new file mode 100644 index 00000000..1885ce80 --- /dev/null +++ b/src/autonest.cpp @@ -0,0 +1,277 @@ +// autonest.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autonest.h" + + + + +// Constructeur de l'objet. + +CAutoNest::CAutoNest(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); +} + +// Destructeur de l'objet. + +CAutoNest::~CAutoNest() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoNest::DeleteObject(BOOL bAll) +{ + CObject* fret; + + if ( !bAll ) + { + fret = SearchFret(); + if ( fret != 0 ) + { + fret->DeleteObject(); + delete fret; + } + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoNest::Init() +{ + D3DVECTOR pos; + + m_phase = ANP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/4.0f; + + m_time = 0.0f; + m_lastParticule = 0.0f; + + pos = m_object->RetPosition(0); + m_terrain->MoveOnFloor(pos); + m_fretPos = pos; +} + + +// Gestion d'un événement. + +BOOL CAutoNest::EventProcess(const Event &event) +{ + CObject* fret; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + + if ( m_phase == ANP_WAIT ) + { + if ( m_progress >= 1.0f ) + { + if ( !SearchFree(m_fretPos) ) + { + m_phase = ANP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/4.0f; + } + else + { + CreateFret(m_fretPos, 0.0f, OBJECT_BULLET); + m_phase = ANP_BIRTH; + m_progress = 0.0f; + m_speed = 1.0f/5.0f; + } + } + } + + if ( m_phase == ANP_BIRTH ) + { + fret = SearchFret(); + + if ( m_progress < 1.0f ) + { + if ( fret != 0 ) + { + fret->SetZoom(0, m_progress); + } + } + else + { + if ( fret != 0 ) + { + fret->SetZoom(0, 1.0f); + fret->SetLock(FALSE); + } + + m_phase = ANP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/5.0f; + } + } + + return TRUE; +} + + +// Cherche si un emplacement est libre. + +BOOL CAutoNest::SearchFree(D3DVECTOR pos) +{ + CObject* pObj; + D3DVECTOR sPos; + ObjectType type; + float sRadius, distance; + int i, j; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type == OBJECT_NEST ) continue; + + j = 0; + while ( pObj->GetCrashSphere(j++, sPos, sRadius) ) + { + distance = Length(sPos, pos); + distance -= sRadius; + if ( distance < 2.0f ) return FALSE; // emplacement occupé + } + } + + return TRUE; // emplacement libre +} + +// Crée un objet transportable. + +void CAutoNest::CreateFret(D3DVECTOR pos, float angle, ObjectType type) +{ + CObject* fret; + + fret = new CObject(m_iMan); + if ( !fret->CreateResource(pos, angle, type) ) + { + delete fret; + return; + } + fret->SetLock(TRUE); // pas utilisable + fret->SetZoom(0, 0.0f); +} + +// Cherche le boullet en cours de fabrication. + +CObject* CAutoNest::SearchFret() +{ + CObject* pObj; + D3DVECTOR oPos; + ObjectType type; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetLock() ) continue; + + type = pObj->RetType(); + if ( type != OBJECT_BULLET ) continue; + + oPos = pObj->RetPosition(0); + if ( oPos.x == m_fretPos.x && + oPos.z == m_fretPos.z ) + { + return pObj; + } + } + + return 0; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoNest::RetError() +{ + return ERR_OK; +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoNest::Write(char *line) +{ + D3DVECTOR pos; + char name[100]; + + if ( m_phase == ANP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoNest::Read(char *line) +{ + D3DVECTOR pos; + + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoNestPhase)OpInt(line, "aPhase", ANP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastParticule = 0.0f; + + return TRUE; +} + + diff --git a/src/autonest.h b/src/autonest.h new file mode 100644 index 00000000..50a36876 --- /dev/null +++ b/src/autonest.h @@ -0,0 +1,55 @@ +// autonest.h + +#ifndef _AUTONEST_H_ +#define _AUTONEST_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + +enum ObjectType; + + + +enum AutoNestPhase +{ + ANP_WAIT = 1, + ANP_BIRTH = 2, // apparition d'un boulet +}; + + + +class CAutoNest : public CAuto +{ +public: + CAutoNest(CInstanceManager* iMan, CObject* object); + ~CAutoNest(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + BOOL SearchFree(D3DVECTOR pos); + void CreateFret(D3DVECTOR pos, float angle, ObjectType type); + CObject* SearchFret(); + +protected: + AutoNestPhase m_phase; + float m_progress; + float m_speed; + float m_lastParticule; + D3DVECTOR m_fretPos; +}; + + +#endif //_AUTONEST_H_ diff --git a/src/autonuclear.cpp b/src/autonuclear.cpp new file mode 100644 index 00000000..b33dd4ca --- /dev/null +++ b/src/autonuclear.cpp @@ -0,0 +1,490 @@ +// autonuclear.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "sound.h" +#include "displaytext.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autonuclear.h" + + + +#define NUCLEAR_DELAY 30.0f // durée de la génération + + + + +// Constructeur de l'objet. + +CAutoNuclear::CAutoNuclear(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + m_channelSound = -1; + Init(); +} + +// Destructeur de l'objet. + +CAutoNuclear::~CAutoNuclear() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoNuclear::DeleteObject(BOOL bAll) +{ + CObject* fret; + + if ( !bAll ) + { + fret = SearchUranium(); + if ( fret != 0 ) + { + fret->DeleteObject(); // détruit le métal + delete fret; + } + } + + if ( m_channelSound != -1 ) + { + m_sound->FlushEnvelope(m_channelSound); + m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_channelSound = -1; + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoNuclear::Init() +{ + D3DMATRIX* mat; + + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastParticule = 0.0f; + + mat = m_object->RetWorldMatrix(0); + m_pos = Transform(*mat, D3DVECTOR(22.0f, 4.0f, 0.0f)); + + m_phase = ANUP_WAIT; // attend ... + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + + CAuto::Init(); +} + + +// Gestion d'un événement. + +BOOL CAutoNuclear::EventProcess(const Event &event) +{ + CObject* fret; + D3DMATRIX* mat; + D3DVECTOR pos, goal, speed; + FPOINT dim, rot; + float angle; + int i, max; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + } + return TRUE; + } + + EventProgress(event.rTime); + + if ( m_phase == ANUP_WAIT ) + { + if ( m_progress >= 1.0f ) + { + fret = SearchUranium(); // uranium à transformer ? + if ( fret == 0 || SearchVehicle() ) + { + m_phase = ANUP_WAIT; // attend encore ... + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + else + { + fret->SetLock(TRUE); // uranium plus utilisable + + SetBusy(TRUE); + InitProgressTotal(1.5f+NUCLEAR_DELAY+1.5f); + UpdateInterface(); + + m_sound->Play(SOUND_OPEN, m_object->RetPosition(0), 1.0f, 1.4f); + + m_phase = ANUP_CLOSE; + m_progress = 0.0f; + m_speed = 1.0f/1.5f; + } + } + } + + if ( m_phase == ANUP_CLOSE ) + { + if ( m_progress < 1.0f ) + { + angle = (1.0f-m_progress)*(135.0f*PI/180.0f); + m_object->SetAngleZ(1, angle); + } + else + { + m_object->SetAngleZ(1, 0.0f); + + mat = m_object->RetWorldMatrix(0); + max = (int)(10.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iCreateParticule(pos, speed, dim, PARTICRASH); + } + + m_sound->Play(SOUND_CLOSE, m_object->RetPosition(0), 1.0f, 1.0f); + + m_channelSound = m_sound->Play(SOUND_NUCLEAR, m_object->RetPosition(0), 1.0f, 0.1f, TRUE); + m_sound->AddEnvelope(m_channelSound, 1.0f, 1.0f, NUCLEAR_DELAY-1.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f, 2.0f, SOPER_STOP); + + m_phase = ANUP_GENERATE; + m_progress = 0.0f; + m_speed = 1.0f/NUCLEAR_DELAY; + } + } + + if ( m_phase == ANUP_GENERATE ) + { + if ( m_progress < 1.0f ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + pos.y += 30.0f; + pos.x += (Rand()-0.5f)*6.0f; + pos.z += (Rand()-0.5f)*6.0f; + speed.y = Rand()*15.0f+15.0f; + speed.x = 0.0f; + speed.z = 0.0f; + dim.x = Rand()*8.0f+8.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH); + + pos = m_pos; + speed.x = (Rand()-0.5f)*20.0f; + speed.y = (Rand()-0.5f)*20.0f; + speed.z = (Rand()-0.5f)*20.0f; + dim.x = 2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLITZ, 1.0f, 0.0f, 0.0f); + } + } + else + { + fret = SearchUranium(); + if ( fret != 0 ) + { + fret->DeleteObject(); // détruit l'uranium + delete fret; + m_object->SetPower(0); + } + + CreatePower(); // crée la pile atomique + + max = (int)(20.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iCreateParticule(pos, speed, dim, PARTIBLUE, Rand()*5.0f+5.0f, 0.0f, 0.0f); + } + + m_sound->Play(SOUND_OPEN, m_object->RetPosition(0), 1.0f, 1.4f); + + m_phase = ANUP_OPEN; + m_progress = 0.0f; + m_speed = 1.0f/1.5f; + } + } + + if ( m_phase == ANUP_OPEN ) + { + if ( m_progress < 1.0f ) + { + angle = m_progress*(135.0f*PI/180.0f); + m_object->SetAngleZ(1, angle); + } + else + { + m_object->SetAngleZ(1, 135.0f*PI/180.0f); + + SetBusy(FALSE); + UpdateInterface(); + + m_displayText->DisplayError(INFO_NUCLEAR, m_object); + + m_phase = ANUP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + return TRUE; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoNuclear::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 110, EVENT_OBJECT_TYPE); + + return TRUE; +} + + +// Cherche l'objet uranium. + +CObject* CAutoNuclear::SearchUranium() +{ + CObject* pObj; + + pObj = m_object->RetPower(); + if ( pObj == 0 ) return 0; + if ( pObj->RetType() == OBJECT_URANIUM ) return pObj; + return 0; +} + +// Cherche si un véhicule est trop proche. + +BOOL CAutoNuclear::SearchVehicle() +{ + CObject* pObj; + D3DVECTOR oPos; + ObjectType type; + float oRadius, dist; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_HUMAN && + type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis && + type != OBJECT_MOBILErt && + type != OBJECT_MOBILErc && + type != OBJECT_MOBILErr && + type != OBJECT_MOBILErs && + type != OBJECT_MOBILEsa && + type != OBJECT_MOBILEtg && + type != OBJECT_MOBILEft && + type != OBJECT_MOBILEtt && + type != OBJECT_MOBILEwt && + type != OBJECT_MOBILEit && + type != OBJECT_MOBILEdr && + type != OBJECT_MOTHER && + type != OBJECT_ANT && + type != OBJECT_SPIDER && + type != OBJECT_BEE && + type != OBJECT_WORM ) continue; + + if ( !pObj->GetCrashSphere(0, oPos, oRadius) ) continue; + dist = Length(oPos, m_pos)-oRadius; + + if ( dist < 10.0f ) return TRUE; + } + + return FALSE; +} + +// Crée un objet pile. + +void CAutoNuclear::CreatePower() +{ + CObject* power; + D3DVECTOR pos; + float angle; + + pos = m_object->RetPosition(0); + angle = m_object->RetAngleY(0); + + power = new CObject(m_iMan); + if ( !power->CreateResource(pos, angle, OBJECT_ATOMIC) ) + { + delete power; + m_displayText->DisplayError(ERR_TOOMANY, m_object); + return; + } + + power->SetTruck(m_object); + power->SetPosition(0, D3DVECTOR(22.0f, 3.0f, 0.0f)); + m_object->SetPower(power); +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoNuclear::RetError() +{ + CObject* pObj; + ObjectType type; +//? TerrainRes res; + + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + +//? res = m_terrain->RetResource(m_object->RetPosition(0)); +//? if ( res != TR_POWER ) return ERR_NUCLEAR_NULL; + +//? if ( m_object->RetEnergy() < ENERGY_POWER ) return ERR_NUCLEAR_LOW; + + pObj = m_object->RetPower(); + if ( pObj == 0 ) return ERR_NUCLEAR_EMPTY; + if ( pObj->RetLock() ) return ERR_OK; + type = pObj->RetType(); + if ( type == OBJECT_ATOMIC ) return ERR_OK; + if ( type != OBJECT_URANIUM ) return ERR_NUCLEAR_BAD; + + return ERR_OK; +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoNuclear::Write(char *line) +{ + char name[100]; + + if ( m_phase == ANUP_STOP || + m_phase == ANUP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoNuclear::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoNuclearPhase)OpInt(line, "aPhase", ANUP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastParticule = 0.0f; + + return TRUE; +} + + diff --git a/src/autonuclear.h b/src/autonuclear.h new file mode 100644 index 00000000..f58921e7 --- /dev/null +++ b/src/autonuclear.h @@ -0,0 +1,60 @@ +// autonuclear.h + +#ifndef _AUTONUCLEAR_H_ +#define _AUTONUCLEAR_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoNuclearPhase +{ + ANUP_STOP = 1, + ANUP_WAIT = 2, + ANUP_CLOSE = 3, + ANUP_GENERATE = 4, + ANUP_OPEN = 5, +}; + + + +class CAutoNuclear : public CAuto +{ +public: + CAutoNuclear(CInstanceManager* iMan, CObject* object); + ~CAutoNuclear(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + CObject* SearchUranium(); + BOOL SearchVehicle(); + void CreatePower(); + +protected: + AutoNuclearPhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastParticule; + D3DVECTOR m_pos; + int m_channelSound; +}; + + +#endif //_AUTONUCLEAR_H_ diff --git a/src/autopara.cpp b/src/autopara.cpp new file mode 100644 index 00000000..15177af8 --- /dev/null +++ b/src/autopara.cpp @@ -0,0 +1,333 @@ +// autopara.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "sound.h" +#include "displaytext.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autopara.h" + + + + +// Constructeur de l'objet. + +CAutoPara::CAutoPara(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + m_channelSound = -1; + Init(); +} + +// Destructeur de l'objet. + +CAutoPara::~CAutoPara() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoPara::DeleteObject(BOOL bAll) +{ + if ( m_channelSound != -1 ) + { + m_sound->FlushEnvelope(m_channelSound); + m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_channelSound = -1; + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoPara::Init() +{ + D3DMATRIX* mat; + + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastParticule = 0.0f; + + mat = m_object->RetWorldMatrix(0); + m_pos = Transform(*mat, D3DVECTOR(22.0f, 4.0f, 0.0f)); + + m_phase = APAP_WAIT; // attend ... + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + + CAuto::Init(); +} + + +// Réception de l'éclair. + +void CAutoPara::StartBlitz() +{ + m_phase = APAP_BLITZ; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; +} + + +// Gestion d'un événement. + +BOOL CAutoPara::EventProcess(const Event &event) +{ + D3DVECTOR pos, speed; + FPOINT dim; + int i; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + } + return TRUE; + } + + EventProgress(event.rTime); + + if ( m_phase == APAP_BLITZ ) + { + if ( m_progress < 1.0f ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + for ( i=0 ; i<10 ; i++ ) + { + pos = m_object->RetPosition(0); + pos.x += (Rand()-0.5f)*m_progress*40.0f; + pos.z += (Rand()-0.5f)*m_progress*40.0f; + pos.y += 50.0f-m_progress*50.0f; + speed.x = (Rand()-0.5f)*20.0f; + speed.z = (Rand()-0.5f)*20.0f; + speed.y = 5.0f+Rand()*5.0f; + dim.x = 2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLITZ, 1.0f, 20.0f, 0.5f); + } + } + } + else + { + m_phase = APAP_CHARGE; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_phase == APAP_CHARGE ) + { + if ( m_progress < 1.0f ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + for ( i=0 ; i<2 ; i++ ) + { + pos = m_object->RetPosition(0); + pos.y += 16.0f; + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + speed.y = -Rand()*30.0f; + dim.x = 1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLITZ, 1.0f, 0.0f, 0.0f); + } + } + + ChargeObject(event.rTime); + } + else + { + m_phase = APAP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + return TRUE; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoPara::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 113, EVENT_OBJECT_TYPE); + + pos.x = ox+sx*10.2f; + pos.y = oy+sy*0.5f; + ddim.x = 33.0f/640.0f; + ddim.y = 33.0f/480.0f; + pw->CreateButton(pos, ddim, 41, EVENT_OBJECT_LIMIT); + + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoPara::RetError() +{ + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + return ERR_OK; +} + + +// Charge tous les objets placés sous le paratonnerre. + +void CAutoPara::ChargeObject(float rTime) +{ + CObject* pObj; + CObject* power; + D3DVECTOR sPos, oPos; + float dist, energy; + int i; + + sPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, sPos); + if ( dist > 20.0f ) continue; + + if ( pObj->RetTruck() == 0 && pObj->RetType() == OBJECT_POWER ) + { + energy = pObj->RetEnergy(); + energy += rTime/2.0f; + if ( energy > 1.0f ) energy = 1.0f; + pObj->SetEnergy(energy); + } + + power = pObj->RetPower(); + if ( power != 0 && power->RetType() == OBJECT_POWER ) + { + energy = power->RetEnergy(); + energy += rTime/2.0f; + if ( energy > 1.0f ) energy = 1.0f; + power->SetEnergy(energy); + } + + power = pObj->RetFret(); + if ( power != 0 && power->RetType() == OBJECT_POWER ) + { + energy = power->RetEnergy(); + energy += rTime/2.0f; + if ( energy > 1.0f ) energy = 1.0f; + power->SetEnergy(energy); + } + } +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoPara::Write(char *line) +{ + char name[100]; + + if ( m_phase == APAP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoPara::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoParaPhase)OpInt(line, "aPhase", APAP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastParticule = 0.0f; + + return TRUE; +} + + diff --git a/src/autopara.h b/src/autopara.h new file mode 100644 index 00000000..691c5088 --- /dev/null +++ b/src/autopara.h @@ -0,0 +1,57 @@ +// autopara.h + +#ifndef _AUTOPARA_H_ +#define _AUTOPARA_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoParaPhase +{ + APAP_WAIT = 1, + APAP_BLITZ = 2, + APAP_CHARGE = 3, +}; + + + +class CAutoPara : public CAuto +{ +public: + CAutoPara(CInstanceManager* iMan, CObject* object); + ~CAutoPara(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + void StartBlitz(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + void ChargeObject(float rTime); + +protected: + AutoParaPhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastParticule; + D3DVECTOR m_pos; + int m_channelSound; +}; + + +#endif //_AUTOPARA_H_ diff --git a/src/autoportico.cpp b/src/autoportico.cpp new file mode 100644 index 00000000..12dcbcac --- /dev/null +++ b/src/autoportico.cpp @@ -0,0 +1,432 @@ +// autoportico.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "displaytext.h" +#include "robotmain.h" +#include "sound.h" +#include "auto.h" +#include "autoportico.h" + + + +#define PARAM_DEPOSE 2 // run=2 -> dépose le vaisseau + +#define PORTICO_POSa 75.0f +#define PORTICO_POSb 65.0f +#define PORTICO_ANGLE1a ( 25.0f*PI/180.0f) +#define PORTICO_ANGLE1b ( 70.0f*PI/180.0f) +#define PORTICO_ANGLE2a (-37.5f*PI/180.0f) +#define PORTICO_ANGLE2b (-62.5f*PI/180.0f) +#define PORTICO_ANGLE3a (-77.5f*PI/180.0f) +#define PORTICO_ANGLE3b (-30.0f*PI/180.0f) + +#define PORTICO_TIME_MOVE 16.0f +#define PORTICO_TIME_DOWN 4.0f +#define PORTICO_TIME_OPEN 12.0f + + + + +// Si progress=0, return a. +// Si progress=1, return b. + +float Progress(float a, float b, float progress) +{ + return a+(b-a)*progress; +} + + + +// Constructeur de l'objet. + +CAutoPortico::CAutoPortico(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); + m_phase = APOP_WAIT; + m_soundChannel = -1; +} + +// Destructeur de l'objet. + +CAutoPortico::~CAutoPortico() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoPortico::DeleteObject(BOOL bAll) +{ + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoPortico::Init() +{ + m_time = 0.0f; + m_lastParticule = 0.0f; + m_posTrack = 0.0f; + + m_phase = APOP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + + m_cameraProgress = 0.0f; + m_cameraSpeed = 1.0f/(PORTICO_TIME_MOVE-2.0f); +} + + +// Démarre l'objet. + +void CAutoPortico::Start(int param) +{ + D3DVECTOR pos; + + pos = m_object->RetPosition(0); + m_finalPos = pos; + pos.z += PORTICO_TIME_MOVE*5.0f; // recule au départ + m_object->SetPosition(0, pos); + m_finalPos.z += PORTICO_TIME_OPEN*5.3f; + + m_object->SetPosition(1, D3DVECTOR(0.0f, PORTICO_POSa, 0.0f)); + m_object->SetAngleY(2, PORTICO_ANGLE1a); + m_object->SetAngleY(3, PORTICO_ANGLE2a); + m_object->SetAngleY(4, PORTICO_ANGLE3a); + m_object->SetAngleY(5, -PORTICO_ANGLE1a); + m_object->SetAngleY(6, -PORTICO_ANGLE2a); + m_object->SetAngleY(7, -PORTICO_ANGLE3a); + + m_phase = APOP_START; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + + m_param = param; +} + + +// Gestion d'un événement. + +BOOL CAutoPortico::EventProcess(const Event &event) +{ + CObject* pObj; + D3DVECTOR pos; + float angle; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + + if ( m_phase == APOP_START ) + { + if ( m_param == PARAM_DEPOSE ) // dépose le vaisseau ? + { + m_startPos = m_object->RetPosition(0); + + m_soundChannel = m_sound->Play(SOUND_MOTORr, m_object->RetPosition(0), 0.0f, 0.3f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 0.5f, 0.6f, 0.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.5f, 0.6f, PORTICO_TIME_MOVE-0.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.3f, 0.5f, SOPER_STOP); + + m_phase = APOP_MOVE; + m_progress = 0.0f; + m_speed = 1.0f/PORTICO_TIME_MOVE; + + m_main->SetMovieLock(TRUE); // bloque tout jusqu'à la fin de l'atterrissage + + m_camera->SetType(CAMERA_SCRIPT); + + pos = m_startPos; + pos.x += -100.0f; + pos.y += 9.0f; + pos.z += -200.0f; + m_camera->SetScriptEye(pos); + + pos = m_object->RetPosition(0); + pos.x += 0.0f; + pos.y += 10.0f; + pos.z += -40.0f; + m_camera->SetScriptLookat(pos); + + m_camera->FixCamera(); + } + } + + angle = -m_time*1.0f; + m_object->SetAngleY(8, angle); // fait tourner le radar droite + angle = sinf(m_time*4.0f)*0.3f; + m_object->SetAngleX(9, angle); + + angle = -m_time*1.0f+PI/2.3f; + m_object->SetAngleY(10, angle); // fait tourner le radar gauche + angle = sinf(m_time*4.0f)*0.3f; + m_object->SetAngleX(11, angle); + + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_phase == APOP_WAIT ) return TRUE; + + m_progress += event.rTime*m_speed; + m_cameraProgress += event.rTime*m_cameraSpeed; + + if ( m_phase == APOP_MOVE ) + { + if ( m_progress < 1.0f ) + { + pos = m_object->RetPosition(0); + pos.z -= event.rTime*5.0f; // avance + m_object->SetPosition(0, pos); + + m_posTrack += event.rTime*0.5f; + UpdateTrackMapping(m_posTrack, m_posTrack); + } + else + { + m_phase = APOP_WAIT1; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == APOP_WAIT1 ) + { + if ( m_progress >= 1.0f ) + { + m_soundChannel = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 0.0f, 0.3f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 0.3f, 0.5f, 1.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.3f, 0.6f, PORTICO_TIME_DOWN-1.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.3f, 1.0f, SOPER_STOP); + + m_phase = APOP_DOWN; + m_progress = 0.0f; + m_speed = 1.0f/PORTICO_TIME_DOWN; + } + } + + if ( m_phase == APOP_DOWN ) + { + if ( m_progress < 1.0f ) + { + pos.x = 0.0f; + pos.y = Progress(PORTICO_POSa, PORTICO_POSb, m_progress); + pos.z = 0.0f; + m_object->SetPosition(1, pos); + } + else + { + pos.x = 0.0f; + pos.y = PORTICO_POSb; + pos.z = 0.0f; + m_object->SetPosition(1, pos); + + m_phase = APOP_WAIT2; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == APOP_WAIT2 ) + { + if ( m_progress >= 1.0f ) + { + m_soundChannel = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 0.0f, 0.5f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 0.5f, 1.0f, 0.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.5f, 1.0f, PORTICO_TIME_OPEN/2.0f-0.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.5f, 0.5f, SOPER_STOP); + + m_soundChannel = m_sound->Play(SOUND_MOTORr, m_object->RetPosition(0), 0.0f, 0.3f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 0.5f, 0.6f, 0.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.5f, 0.6f, PORTICO_TIME_OPEN-0.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.3f, 0.5f, SOPER_STOP); + + m_phase = APOP_OPEN; + m_progress = 0.0f; + m_speed = 1.0f/PORTICO_TIME_OPEN; + } + } + + if ( m_phase == APOP_OPEN ) + { + if ( m_progress < 1.0f ) + { + pos = m_object->RetPosition(0); + pos.z += event.rTime*5.3f; // recule + m_object->SetPosition(0, pos); + + m_posTrack -= event.rTime*1.0f; + UpdateTrackMapping(m_posTrack, m_posTrack); + + if ( m_progress < 0.5f ) + { + angle = Progress(PORTICO_ANGLE1a, PORTICO_ANGLE1b, m_progress/0.5f); + m_object->SetAngleY(2, angle); + m_object->SetAngleY(5, -angle); + angle = Progress(PORTICO_ANGLE2a, PORTICO_ANGLE2b, m_progress/0.5f); + m_object->SetAngleY(3, angle); + m_object->SetAngleY(6, -angle); + angle = Progress(PORTICO_ANGLE3a, PORTICO_ANGLE3b, m_progress/0.5f); + m_object->SetAngleY(4, angle); + m_object->SetAngleY(7, -angle); + } + else + { + m_object->SetAngleY(2, PORTICO_ANGLE1b); + m_object->SetAngleY(3, PORTICO_ANGLE2b); + m_object->SetAngleY(4, PORTICO_ANGLE3b); + m_object->SetAngleY(5, -PORTICO_ANGLE1b); + m_object->SetAngleY(6, -PORTICO_ANGLE2b); + m_object->SetAngleY(7, -PORTICO_ANGLE3b); + } + } + else + { + m_main->SetMovieLock(FALSE); // on peut jouer ! + + pObj = m_main->SearchHuman(); + m_main->SelectObject(pObj); + m_camera->SetObject(pObj); + m_camera->SetType(CAMERA_BACK); + + m_phase = APOP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + } + } + + if ( m_soundChannel != -1 ) + { +//? m_sound->Position(m_soundChannel, m_object->RetPosition(0)); + pos = m_engine->RetEyePt(); + m_sound->Position(m_soundChannel, pos); + } + + if ( m_cameraProgress < 1.0f ) + { + if ( m_cameraProgress < 0.5f ) + { + } + else + { + pos = m_startPos; + pos.x += -100.0f-(m_cameraProgress-0.5f)*1.0f*120.0f; + pos.y += 9.0f; + pos.z += -200.0f+(m_cameraProgress-0.5f)*1.0f*210.0f; + m_camera->SetScriptEye(pos); + } + + pos = m_object->RetPosition(0); + pos.x += 0.0f; + pos.y += 10.0f; + pos.z += -40.0f; + m_camera->SetScriptLookat(pos); + } + + return TRUE; +} + +// Stoppe l'automate. + +BOOL CAutoPortico::Abort() +{ + CObject* pObj; + + m_object->SetPosition(0, m_finalPos); + m_object->SetPosition(1, D3DVECTOR(0.0f, PORTICO_POSb, 0.0f)); + m_object->SetAngleY(2, PORTICO_ANGLE1b); + m_object->SetAngleY(3, PORTICO_ANGLE2b); + m_object->SetAngleY(4, PORTICO_ANGLE3b); + m_object->SetAngleY(5, -PORTICO_ANGLE1b); + m_object->SetAngleY(6, -PORTICO_ANGLE2b); + m_object->SetAngleY(7, -PORTICO_ANGLE3b); + + m_main->SetMovieLock(FALSE); // on peut jouer ! + + pObj = m_main->SearchHuman(); + m_main->SelectObject(pObj); + m_camera->SetObject(pObj); + m_camera->SetType(CAMERA_BACK); + + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + m_phase = APOP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/2.0f; + + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoPortico::RetError() +{ + return ERR_OK; +} + + +// Met à jour le mapping de la texture des chenilles. + +void CAutoPortico::UpdateTrackMapping(float left, float right) +{ + D3DMATERIAL7 mat; + float limit[2]; + int rank; + + ZeroMemory( &mat, sizeof(D3DMATERIAL7) ); + mat.diffuse.r = 1.0f; + mat.diffuse.g = 1.0f; + mat.diffuse.b = 1.0f; // blanc + mat.ambient.r = 0.5f; + mat.ambient.g = 0.5f; + mat.ambient.b = 0.5f; + + rank = m_object->RetObjectRank(0); + + limit[0] = 0.0f; + limit[1] = 1000000.0f; + + m_engine->TrackTextureMapping(rank, mat, D3DSTATEPART1, "lemt.tga", "", + limit[0], limit[1], D3DMAPPINGX, + right, 8.0f, 8.0f, 192.0f, 256.0f); + + m_engine->TrackTextureMapping(rank, mat, D3DSTATEPART2, "lemt.tga", "", + limit[0], limit[1], D3DMAPPINGX, + left, 8.0f, 8.0f, 192.0f, 256.0f); +} + diff --git a/src/autoportico.h b/src/autoportico.h new file mode 100644 index 00000000..611a4b2c --- /dev/null +++ b/src/autoportico.h @@ -0,0 +1,61 @@ +// autoportico.h + +#ifndef _AUTOPORTICO_H_ +#define _AUTOPORTICO_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoPorticoPhase +{ + APOP_WAIT = 1, // attend + APOP_START = 2, // départ de l'action + APOP_MOVE = 3, // avance + APOP_WAIT1 = 4, // attend + APOP_DOWN = 5, // descend + APOP_WAIT2 = 6, // attend + APOP_OPEN = 7, // ouvre +}; + + + +class CAutoPortico : public CAuto +{ +public: + CAutoPortico(CInstanceManager* iMan, CObject* object); + ~CAutoPortico(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + void Start(int param); + BOOL EventProcess(const Event &event); + BOOL Abort(); + Error RetError(); + +protected: + void UpdateTrackMapping(float left, float right); + +protected: + AutoPorticoPhase m_phase; + float m_progress; + float m_speed; + float m_cameraProgress; + float m_cameraSpeed; + float m_lastParticule; + D3DVECTOR m_finalPos; + D3DVECTOR m_startPos; + float m_posTrack; + int m_param; + int m_soundChannel; +}; + + +#endif //_AUTOPORTICO_H_ diff --git a/src/autoradar.cpp b/src/autoradar.cpp new file mode 100644 index 00000000..3c10e596 --- /dev/null +++ b/src/autoradar.cpp @@ -0,0 +1,312 @@ +// autoradar.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "gauge.h" +#include "sound.h" +#include "auto.h" +#include "autoradar.h" + + + + +// Constructeur de l'objet. + +CAutoRadar::CAutoRadar(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); + m_phase = ARAP_WAIT; + m_totalDetect = 0; +} + +// Destructeur de l'objet. + +CAutoRadar::~CAutoRadar() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoRadar::DeleteObject(BOOL bAll) +{ + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoRadar::Init() +{ + m_phase = ARAP_SEARCH; + m_progress = 0.0f; + m_speed = 1.0f/3.0f; + + m_aTime = 0.0f; + m_time = 0.0f; + m_timeVirus = 0.0f; +} + + +// Gestion d'un événement. + +BOOL CAutoRadar::EventProcess(const Event &event) +{ + D3DVECTOR pos, ePos; + float speed, angle, prog, freq, ampl; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_phase == ARAP_WAIT ) return TRUE; + + m_progress += event.rTime*m_speed; + m_aTime += event.rTime; + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + + angle = m_object->RetAngleY(1); + angle += (Rand()-0.2f)*0.5f; + m_object->SetAngleY(1, angle); + + angle = m_object->RetAngleY(2); + angle += (Rand()-0.8f)*1.0f; + m_object->SetAngleY(2, angle); + + m_object->SetAngleX(3, (Rand()-0.5f)*0.3f); + + m_totalDetect = (int)(Rand()*10.0f); + UpdateInterface(); + } + return TRUE; + } + + if ( m_phase == ARAP_SEARCH ) + { + if ( m_progress < 1.0f ) + { + speed = Min(10.0f, m_progress*50.0f); + angle = m_object->RetAngleY(1); + angle += event.rTime*speed; + m_object->SetAngleY(1, angle); + } + else + { + if ( !SearchEnemy(ePos) ) + { + m_phase = ARAP_SEARCH; + m_progress = 10.0f/50.0f; // plein régime tout de suite + m_speed = 1.0f/3.0f; + } + else + { + pos = m_object->RetPosition(0); + m_start = m_object->RetAngleY(1); + m_angle = m_start-NormAngle(m_start)+PI*2.0f; + m_angle += RotateAngle(pos.x-ePos.x, ePos.z-pos.z); + m_angle += PI-m_object->RetAngleY(0); + + m_phase = ARAP_SHOW; + m_progress = 0.0f; + m_speed = 1.0f/(Abs(m_angle-m_start)/10.0f); + } + } + } + + if ( m_phase == ARAP_SHOW ) + { + if ( m_progress < 1.0f ) + { + angle = m_start + (m_angle-m_start)*m_progress; + m_object->SetAngleY(1, angle); + } + else + { + m_sound->Play(SOUND_RADAR, m_object->RetPosition(0)); + + m_phase = ARAP_SINUS; + m_progress = 0.0f; + m_speed = 1.0f/4.0f; + m_time = 0.0f; + } + } + + if ( m_phase == ARAP_SINUS ) + { + if ( m_progress < 1.0f ) + { + prog = Min(1.0f, m_progress*2.0f); + freq = 16.0f*(prog+1.0f); + ampl = 0.2f-prog*0.2f; + angle = m_angle + sinf(m_time*freq)*ampl; + m_object->SetAngleY(1, angle); + } + else + { + m_phase = ARAP_SEARCH; + m_progress = 0.0f; + m_speed = 1.0f/3.0f; + } + } + + angle = -m_aTime*2.0f; + m_object->SetAngleY(2, angle); + + angle = sinf(m_aTime*4.0f)*0.3f; + m_object->SetAngleX(3, angle); + + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoRadar::RetError() +{ + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + + return ERR_OK; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoRadar::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, dim, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*7.0f; + pos.y = oy+sy*0.6f; + dim.x = 160.0f/640.0f; + dim.y = 26.0f/480.0f; + pw->CreateGauge(pos, dim, 1, EVENT_OBJECT_GRADAR); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 105, EVENT_OBJECT_TYPE); + + UpdateInterface(); + return TRUE; +} + +// Met à jour l'état de tous les boutons de l'interface. + +void CAutoRadar::UpdateInterface() +{ + CWindow* pw; + CGauge* pg; + float level; + + if ( !m_object->RetSelect() ) return; + + CAuto::UpdateInterface(); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + pg = (CGauge*)pw->SearchControl(EVENT_OBJECT_GRADAR); + if ( pg != 0 ) + { + level = (float)m_totalDetect*(1.0f/8.0f); + if ( level > 1.0f ) level = 1.0f; + pg->SetLevel(level); + } +} + + +// Cherche la position d'un ennemi. + +BOOL CAutoRadar::SearchEnemy(D3DVECTOR &pos) +{ + CObject* pObj; + CObject* pBest = 0; + D3DVECTOR iPos, oPos; + ObjectType oType; + float distance, min; + int i; + + iPos = m_object->RetPosition(0); + min = 1000000.0f; + m_totalDetect = 0; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; + + oType = pObj->RetType(); + if ( oType != OBJECT_ANT && + oType != OBJECT_SPIDER && + oType != OBJECT_BEE && + oType != OBJECT_WORM && + oType != OBJECT_MOTHER ) continue; + + m_totalDetect ++; + + oPos = pObj->RetPosition(0); + distance = Length(oPos, iPos); + if ( distance < min ) + { + min = distance; + pBest = pObj; + } + } + + UpdateInterface(); + + if ( pBest == 0 ) return FALSE; + pos = pBest->RetPosition(0); + return TRUE; +} + + diff --git a/src/autoradar.h b/src/autoradar.h new file mode 100644 index 00000000..29c6bf66 --- /dev/null +++ b/src/autoradar.h @@ -0,0 +1,56 @@ +// autoradar.h + +#ifndef _AUTORADAR_H_ +#define _AUTORADAR_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoRadarPhase +{ + ARAP_WAIT = 1, // attend + ARAP_SEARCH = 2, // cherche + ARAP_SHOW = 3, // montre + ARAP_SINUS = 4, // oscille +}; + + + +class CAutoRadar : public CAuto +{ +public: + CAutoRadar(CInstanceManager* iMan, CObject* object); + ~CAutoRadar(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + BOOL CreateInterface(BOOL bSelect); + Error RetError(); + +protected: + void UpdateInterface(); + BOOL SearchEnemy(D3DVECTOR &pos); + +protected: + AutoRadarPhase m_phase; + float m_progress; + float m_speed; + float m_aTime; + float m_timeVirus; + float m_lastParticule; + float m_angle; + float m_start; + int m_totalDetect; +}; + + +#endif //_AUTORADAR_H_ diff --git a/src/autorepair.cpp b/src/autorepair.cpp new file mode 100644 index 00000000..84335ee1 --- /dev/null +++ b/src/autorepair.cpp @@ -0,0 +1,348 @@ +// autorepair.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "physics.h" +#include "sound.h" +#include "interface.h" +#include "button.h" +#include "window.h" +#include "robotmain.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autorepair.h" + + + + +// Constructeur de l'objet. + +CAutoRepair::CAutoRepair(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); + m_phase = ARP_WAIT; // en pause jusqu'au premier Init() +} + +// Destructeur de l'objet. + +CAutoRepair::~CAutoRepair() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoRepair::DeleteObject(BOOL bAll) +{ + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoRepair::Init() +{ + m_phase = ARP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastParticule = 0.0f; + + CAuto::Init(); +} + + +// Gestion d'un événement. + +BOOL CAutoRepair::EventProcess(const Event &event) +{ + CObject* vehicule; + D3DVECTOR pos, speed; + FPOINT dim; + float angle, shield; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + } + return TRUE; + } + + if ( m_phase == ARP_WAIT ) + { + if ( m_progress >= 1.0f ) + { + if ( SearchVehicle() == 0 ) + { + m_phase = ARP_WAIT; // attend encore ... + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + else + { + m_sound->Play(SOUND_OPEN, m_object->RetPosition(0), 1.0f, 0.8f); + + m_phase = ARP_DOWN; + m_progress = 0.0f; + m_speed = 1.0f/3.0f; + } + } + } + + if ( m_phase == ARP_DOWN ) + { + if ( m_progress < 1.0f ) + { + angle = -m_progress*(PI/2.0f)+PI/2.0f; + m_object->SetAngleZ(1, angle); + } + else + { + m_object->SetAngleZ(1, 0.0f); + m_sound->Play(SOUND_REPAIR, m_object->RetPosition(0)); + + m_phase = ARP_REPAIR; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == ARP_REPAIR ) + { + vehicule = SearchVehicle(); + if ( m_progress < 1.0f || + (vehicule != 0 && vehicule->RetShield() < 1.0f) ) + { + if ( vehicule != 0 ) + { + shield = vehicule->RetShield(); + shield += event.rTime*0.2f; + if ( shield > 1.0f ) shield = 1.0f; + vehicule->SetShield(shield); + } + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + pos.y += 1.0f; + speed.x = (Rand()-0.5f)*12.0f; + speed.z = (Rand()-0.5f)*12.0f; + speed.y = Rand()*15.0f; + dim.x = Rand()*6.0f+4.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLUE, 1.0f, 0.0f, 0.0f); + } + } + else + { + m_sound->Play(SOUND_OPEN, m_object->RetPosition(0), 1.0f, 0.8f); + + m_phase = ARP_UP; + m_progress = 0.0f; + m_speed = 1.0f/3.0f; + } + } + + if ( m_phase == ARP_UP ) + { + if ( m_progress < 1.0f ) + { + angle = -(1.0f-m_progress)*(PI/2.0f)+PI/2.0f; + m_object->SetAngleZ(1, angle); + } + else + { + m_object->SetAngleZ(1, PI/2.0f); + + m_phase = ARP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + return TRUE; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoRepair::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 106, EVENT_OBJECT_TYPE); + + return TRUE; +} + + +// Cherche le véhicule placé sur la station. + +CObject* CAutoRepair::SearchVehicle() +{ + CObject* pObj; + CPhysics* physics; + D3DVECTOR sPos, oPos; + ObjectType type; + float dist; + int i; + + sPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis && + type != OBJECT_MOBILErt && + type != OBJECT_MOBILErc && + type != OBJECT_MOBILErr && + type != OBJECT_MOBILErs && + type != OBJECT_MOBILEsa && + type != OBJECT_MOBILEtg && + type != OBJECT_MOBILEft && + type != OBJECT_MOBILEtt && + type != OBJECT_MOBILEwt && + type != OBJECT_MOBILEit && + type != OBJECT_MOBILEdr ) continue; + + physics = pObj->RetPhysics(); + if ( physics != 0 && !physics->RetLand() ) continue; // en vol ? + + oPos = pObj->RetPosition(0); + dist = Length(oPos, sPos); + if ( dist <= 5.0f ) return pObj; + } + + return 0; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoRepair::RetError() +{ + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + + return ERR_OK; +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoRepair::Write(char *line) +{ + char name[100]; + + if ( m_phase == ARP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoRepair::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoRepairPhase)OpInt(line, "aPhase", ARP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastParticule = 0.0f; + + return TRUE; +} + + diff --git a/src/autorepair.h b/src/autorepair.h new file mode 100644 index 00000000..c29b21fb --- /dev/null +++ b/src/autorepair.h @@ -0,0 +1,55 @@ +// autorepair.h + +#ifndef _AUTOREPAIR_H_ +#define _AUTOREPAIR_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoRepairPhase +{ + ARP_WAIT = 1, // attend métal + ARP_DOWN = 2, // descend le couvercle + ARP_REPAIR = 3, // construit le véhicule + ARP_UP = 4, // remonte le couvercle +}; + + + +class CAutoRepair : public CAuto +{ +public: + CAutoRepair(CInstanceManager* iMan, CObject* object); + ~CAutoRepair(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + CObject* SearchVehicle(); + +protected: + AutoRepairPhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastParticule; +}; + + +#endif //_AUTOREPAIR_H_ diff --git a/src/autoresearch.cpp b/src/autoresearch.cpp new file mode 100644 index 00000000..fcaa29e4 --- /dev/null +++ b/src/autoresearch.cpp @@ -0,0 +1,613 @@ +// autoresearch.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "gauge.h" +#include "window.h" +#include "displaytext.h" +#include "sound.h" +#include "robotmain.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autoresearch.h" + + + +#define SEARCH_TIME 30.0f // durée d'une recherche + + + +// Constructeur de l'objet. + +CAutoResearch::CAutoResearch(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + int i; + + CAuto::CAuto(iMan, object); + + for ( i=0 ; i<6 ; i++ ) + { + m_partiStop[i] = -1; + } + m_channelSound = -1; + + Init(); +} + +// Destructeur de l'objet. + +CAutoResearch::~CAutoResearch() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoResearch::DeleteObject(BOOL bAll) +{ + if ( m_channelSound != -1 ) + { + m_sound->FlushEnvelope(m_channelSound); + m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_channelSound = -1; + } + + FireStopUpdate(0.0f, FALSE); + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoResearch::Init() +{ + m_phase = ALP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastUpdateTime = 0.0f; + m_lastParticule = 0.0f; +} + + +// Gestion d'un événement. + +BOOL CAutoResearch::EventProcess(const Event &event) +{ + CObject* power; + D3DVECTOR pos, speed; + Error message; + FPOINT dim; + float angle, time; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + + if ( event.event == EVENT_UPDINTERFACE ) + { + if ( m_object->RetSelect() ) CreateInterface(TRUE); + } + + if ( m_object->RetSelect() && // centre sélectionné ? + (event.event == EVENT_OBJECT_RTANK || + event.event == EVENT_OBJECT_RFLY || + event.event == EVENT_OBJECT_RTHUMP || + event.event == EVENT_OBJECT_RCANON || + event.event == EVENT_OBJECT_RTOWER || + event.event == EVENT_OBJECT_RPHAZER || + event.event == EVENT_OBJECT_RSHIELD || + event.event == EVENT_OBJECT_RATOMIC ) ) + { + if ( m_phase != ALP_WAIT ) + { + return FALSE; + } + + m_research = event.event; + + if ( TestResearch(m_research) ) + { + m_displayText->DisplayError(ERR_RESEARCH_ALREADY, m_object); + return FALSE; + } + + power = m_object->RetPower(); + if ( power == 0 ) + { + m_displayText->DisplayError(ERR_RESEARCH_POWER, m_object); + return FALSE; + } + if ( power->RetCapacity() > 1.0f ) + { + m_displayText->DisplayError(ERR_RESEARCH_TYPE, m_object); + return FALSE; + } + if ( power->RetEnergy() < 1.0f ) + { + m_displayText->DisplayError(ERR_RESEARCH_ENERGY, m_object); + return FALSE; + } + + time = SEARCH_TIME; + if ( event.event == EVENT_OBJECT_RTANK ) time *= 0.3f; + if ( event.event == EVENT_OBJECT_RFLY ) time *= 0.3f; + if ( event.event == EVENT_OBJECT_RATOMIC ) time *= 2.0f; + + SetBusy(TRUE); + InitProgressTotal(time); + UpdateInterface(); + + m_channelSound = m_sound->Play(SOUND_RESEARCH, m_object->RetPosition(0), 0.0f, 1.0f, TRUE); + m_sound->AddEnvelope(m_channelSound, 1.0f, 1.0f, 2.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_channelSound, 1.0f, 1.0f, time-4.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f, 2.0f, SOPER_STOP); + + m_phase = ALP_SEARCH; + m_progress = 0.0f; + m_speed = 1.0f/time; + return TRUE; + } + + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + } + return TRUE; + } + + UpdateInterface(event.rTime); + EventProgress(event.rTime); + + angle = m_time*0.1f; + m_object->SetAngleY(1, angle); // tourne l'antenne + + angle = (30.0f+sinf(m_time*0.3f)*20.0f)*PI/180.0f; + m_object->SetAngleZ(2, angle); // oriente l'antenne + + if ( m_phase == ALP_WAIT ) + { + FireStopUpdate(m_progress, FALSE); // éteint + return TRUE; + } + + if ( m_phase == ALP_SEARCH ) + { + FireStopUpdate(m_progress, TRUE); // clignotte + if ( m_progress < 1.0f ) + { + power = m_object->RetPower(); + if ( power == 0 ) // plus de pile ? + { + SetBusy(FALSE); + UpdateInterface(); + + m_phase = ALP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + return TRUE; + } + power->SetEnergy(1.0f-m_progress); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + pos.x += (Rand()-0.5f)*6.0f; + pos.z += (Rand()-0.5f)*6.0f; + pos.y += 11.0f; + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = Rand()*20.0f; + dim.x = Rand()*1.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIVAPOR); + } + } + else + { + SetResearch(m_research); // recherche effectuée + m_displayText->DisplayError(INFO_RESEARCH, m_object); + + message = ERR_OK; + if ( m_research == EVENT_OBJECT_RTANK ) message = INFO_RESEARCHTANK; + if ( m_research == EVENT_OBJECT_RFLY ) message = INFO_RESEARCHFLY; + if ( m_research == EVENT_OBJECT_RTHUMP ) message = INFO_RESEARCHTHUMP; + if ( m_research == EVENT_OBJECT_RCANON ) message = INFO_RESEARCHCANON; + if ( m_research == EVENT_OBJECT_RTOWER ) message = INFO_RESEARCHTOWER; + if ( m_research == EVENT_OBJECT_RPHAZER ) message = INFO_RESEARCHPHAZER; + if ( m_research == EVENT_OBJECT_RSHIELD ) message = INFO_RESEARCHSHIELD; + if ( m_research == EVENT_OBJECT_RATOMIC ) message = INFO_RESEARCHATOMIC; + if ( message != ERR_OK ) + { + m_displayText->DisplayError(message, m_object); + } + + SetBusy(FALSE); + UpdateInterface(); + + m_phase = ALP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoResearch::RetError() +{ + CObject* power; + + if ( m_phase == ALP_SEARCH ) + { + return ERR_OK; + } + + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + + power = m_object->RetPower(); + if ( power == 0 ) + { + return ERR_RESEARCH_POWER; + } + if ( power != 0 && power->RetCapacity() > 1.0f ) + { + return ERR_RESEARCH_TYPE; + } + if ( power != 0 && power->RetEnergy() < 1.0f ) + { + return ERR_RESEARCH_ENERGY; + } + + return ERR_OK; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoResearch::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, dim, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + dim.x = 33.0f/640.0f; + dim.y = 33.0f/480.0f; + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*7.0f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, dim, 64+0, EVENT_OBJECT_RTANK); + + pos.x = ox+sx*8.0f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, dim, 64+1, EVENT_OBJECT_RFLY); + + pos.x = ox+sx*9.0f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, dim, 64+3, EVENT_OBJECT_RCANON); + + pos.x = ox+sx*10.0f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, dim, 64+4, EVENT_OBJECT_RTOWER); + + pos.x = ox+sx*7.0f; + pos.y = oy+sy*0.0f; + pw->CreateButton(pos, dim, 64+7, EVENT_OBJECT_RATOMIC); + + pos.x = ox+sx*8.0f; + pos.y = oy+sy*0.0f; + pw->CreateButton(pos, dim, 64+2, EVENT_OBJECT_RTHUMP); + + pos.x = ox+sx*9.0f; + pos.y = oy+sy*0.0f; + pw->CreateButton(pos, dim, 64+6, EVENT_OBJECT_RSHIELD); + + pos.x = ox+sx*10.0f; + pos.y = oy+sy*0.0f; + pw->CreateButton(pos, dim, 64+5, EVENT_OBJECT_RPHAZER); + + pos.x = ox+sx*14.5f; + pos.y = oy+sy*0; + ddim.x = 14.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGauge(pos, ddim, 0, EVENT_OBJECT_GENERGY); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 102, EVENT_OBJECT_TYPE); + + UpdateInterface(); + + return TRUE; +} + +// Met à jour l'état de tous les boutons de l'interface. + +void CAutoResearch::UpdateInterface() +{ + CWindow* pw; + + if ( !m_object->RetSelect() ) return; + + CAuto::UpdateInterface(); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + DeadInterface(pw, EVENT_OBJECT_RTANK, g_researchEnable&RESEARCH_TANK); + DeadInterface(pw, EVENT_OBJECT_RFLY, g_researchEnable&RESEARCH_FLY); + DeadInterface(pw, EVENT_OBJECT_RTHUMP, g_researchEnable&RESEARCH_THUMP); + DeadInterface(pw, EVENT_OBJECT_RCANON, g_researchEnable&RESEARCH_CANON); + DeadInterface(pw, EVENT_OBJECT_RTOWER, g_researchEnable&RESEARCH_TOWER); + DeadInterface(pw, EVENT_OBJECT_RPHAZER, g_researchEnable&RESEARCH_PHAZER); + DeadInterface(pw, EVENT_OBJECT_RSHIELD, g_researchEnable&RESEARCH_SHIELD); + DeadInterface(pw, EVENT_OBJECT_RATOMIC, g_researchEnable&RESEARCH_ATOMIC); + + OkayButton(pw, EVENT_OBJECT_RTANK); + OkayButton(pw, EVENT_OBJECT_RFLY); + OkayButton(pw, EVENT_OBJECT_RTHUMP); + OkayButton(pw, EVENT_OBJECT_RCANON); + OkayButton(pw, EVENT_OBJECT_RTOWER); + OkayButton(pw, EVENT_OBJECT_RPHAZER); + OkayButton(pw, EVENT_OBJECT_RSHIELD); + OkayButton(pw, EVENT_OBJECT_RATOMIC); + + VisibleInterface(pw, EVENT_OBJECT_RTANK, !m_bBusy); + VisibleInterface(pw, EVENT_OBJECT_RFLY, !m_bBusy); + VisibleInterface(pw, EVENT_OBJECT_RTHUMP, !m_bBusy); + VisibleInterface(pw, EVENT_OBJECT_RCANON, !m_bBusy); + VisibleInterface(pw, EVENT_OBJECT_RTOWER, !m_bBusy); + VisibleInterface(pw, EVENT_OBJECT_RPHAZER, !m_bBusy); + VisibleInterface(pw, EVENT_OBJECT_RSHIELD, !m_bBusy); + VisibleInterface(pw, EVENT_OBJECT_RATOMIC, !m_bBusy); +} + +// Met à jour l'état de tous les boutons de l'interface, +// suite au temps qui s'écoule ... + +void CAutoResearch::UpdateInterface(float rTime) +{ + CWindow* pw; + CGauge* pg; + CObject* power; + float energy; + + CAuto::UpdateInterface(rTime); + + if ( m_time < m_lastUpdateTime+0.1f ) return; + m_lastUpdateTime = m_time; + + if ( !m_object->RetSelect() ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + pg = (CGauge*)pw->SearchControl(EVENT_OBJECT_GENERGY); + if ( pg != 0 ) + { + energy = 0.0f; + power = m_object->RetPower(); + if ( power != 0 ) + { + energy = power->RetEnergy(); + } + pg->SetLevel(energy); + } +} + +// Indique les recherches déjà effectuées pour un bouton. + +void CAutoResearch::OkayButton(CWindow *pw, EventMsg event) +{ + CControl* control; + + control = pw->SearchControl(event); + if ( control == 0 ) return; + + control->SetState(STATE_OKAY, TestResearch(event)); +} + + +// Teste si une recherche a déjà été effectuée. + +BOOL CAutoResearch::TestResearch(EventMsg event) +{ + if ( event == EVENT_OBJECT_RTANK ) return (g_researchDone & RESEARCH_TANK ); + if ( event == EVENT_OBJECT_RFLY ) return (g_researchDone & RESEARCH_FLY ); + if ( event == EVENT_OBJECT_RTHUMP ) return (g_researchDone & RESEARCH_THUMP ); + if ( event == EVENT_OBJECT_RCANON ) return (g_researchDone & RESEARCH_CANON ); + if ( event == EVENT_OBJECT_RTOWER ) return (g_researchDone & RESEARCH_TOWER ); + if ( event == EVENT_OBJECT_RPHAZER ) return (g_researchDone & RESEARCH_PHAZER ); + if ( event == EVENT_OBJECT_RSHIELD ) return (g_researchDone & RESEARCH_SHIELD); + if ( event == EVENT_OBJECT_RATOMIC ) return (g_researchDone & RESEARCH_ATOMIC); + + return FALSE; +} + +// Indique une recherche comme effectuée. + +void CAutoResearch::SetResearch(EventMsg event) +{ + Event newEvent; + + if ( event == EVENT_OBJECT_RTANK ) g_researchDone |= RESEARCH_TANK; + if ( event == EVENT_OBJECT_RFLY ) g_researchDone |= RESEARCH_FLY; + if ( event == EVENT_OBJECT_RTHUMP ) g_researchDone |= RESEARCH_THUMP; + if ( event == EVENT_OBJECT_RCANON ) g_researchDone |= RESEARCH_CANON; + if ( event == EVENT_OBJECT_RTOWER ) g_researchDone |= RESEARCH_TOWER; + if ( event == EVENT_OBJECT_RPHAZER ) g_researchDone |= RESEARCH_PHAZER; + if ( event == EVENT_OBJECT_RSHIELD ) g_researchDone |= RESEARCH_SHIELD; + if ( event == EVENT_OBJECT_RATOMIC ) g_researchDone |= RESEARCH_ATOMIC; + + m_main->WriteFreeParam(); + + m_event->MakeEvent(newEvent, EVENT_UPDINTERFACE); + m_event->AddEvent(newEvent); + UpdateInterface(); +} + + +// Met à jour les feux de stop. + +void CAutoResearch::FireStopUpdate(float progress, BOOL bLightOn) +{ + D3DMATRIX* mat; + D3DVECTOR pos, speed; + FPOINT dim; + int i; + + static float listpos[12] = + { + 9.5f, 0.0f, + 4.7f, 8.2f, + -4.7f, 8.2f, + -9.5f, 0.0f, + -4.7f, -8.2f, + 4.7f, -8.2f, + }; + + if ( !bLightOn ) // éteint ? + { + for ( i=0 ; i<6 ; i++ ) + { + if ( m_partiStop[i] != -1 ) + { + m_particule->DeleteParticule(m_partiStop[i]); + m_partiStop[i] = -1; + } + } + return; + } + + mat = m_object->RetWorldMatrix(0); + + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 2.0f; + dim.y = dim.x; + + for ( i=0 ; i<6 ; i++ ) + { + if ( Mod(progress, 0.025f) < 0.005f ) + { + if ( m_partiStop[i] != -1 ) + { + m_particule->DeleteParticule(m_partiStop[i]); + m_partiStop[i] = -1; + } + } + else + { + if ( m_partiStop[i] == -1 ) + { + pos.x = listpos[i*2+0]; + pos.y = 11.5f; + pos.z = listpos[i*2+1]; + pos = Transform(*mat, pos); + m_partiStop[i] = m_particule->CreateParticule(pos, speed, + dim, PARTISELY, + 1.0f, 0.0f, 0.0f); + } + } + } +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoResearch::Write(char *line) +{ + char name[100]; + + if ( m_phase == ALP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + sprintf(name, " aResearch=%d", m_research); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoResearch::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoResearchPhase)OpInt(line, "aPhase", ALP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + m_research = (EventMsg)OpInt(line, "aResearch", 0); + + m_lastUpdateTime = 0.0f; + m_lastParticule = 0.0f; + + return TRUE; +} + + diff --git a/src/autoresearch.h b/src/autoresearch.h new file mode 100644 index 00000000..ec5f09d0 --- /dev/null +++ b/src/autoresearch.h @@ -0,0 +1,64 @@ +// autoresearch.h + +#ifndef _AUTORESEARCH_H_ +#define _AUTORESEARCH_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + +enum ObjectType; + + + +enum AutoResearchPhase +{ + ALP_WAIT = 1, + ALP_SEARCH = 2, // recherche en cours +}; + + + +class CAutoResearch : public CAuto +{ +public: + CAutoResearch(CInstanceManager* iMan, CObject* object); + ~CAutoResearch(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + void UpdateInterface(); + void UpdateInterface(float rTime); + void OkayButton(CWindow *pw, EventMsg event); + BOOL TestResearch(EventMsg event); + void SetResearch(EventMsg event); + void FireStopUpdate(float progress, BOOL bLightOn); + +protected: + AutoResearchPhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastUpdateTime; + float m_lastParticule; + EventMsg m_research; + int m_partiStop[6]; + int m_channelSound; +}; + + +#endif //_AUTORESEARCH_H_ diff --git a/src/autoroot.cpp b/src/autoroot.cpp new file mode 100644 index 00000000..5d654485 --- /dev/null +++ b/src/autoroot.cpp @@ -0,0 +1,121 @@ +// autoroot.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "auto.h" +#include "autoroot.h" + + + + +// Constructeur de l'objet. + +CAutoRoot::CAutoRoot(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); +} + +// Destructeur de l'objet. + +CAutoRoot::~CAutoRoot() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoRoot::DeleteObject(BOOL bAll) +{ + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoRoot::Init() +{ + D3DMATRIX* mat; + D3DVECTOR pos, speed; + FPOINT dim; + + m_time = 0.0f; + m_lastParticule = 0.0f; + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(-5.0f, 28.0f, -4.0f); // position pointe + pos = Transform(*mat, pos); + m_center = pos; + + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 100.0f; + dim.y = dim.x; + m_particule->CreateParticule(m_center, speed, dim, PARTISPHERE5, 0.5f, 0.0f, 0.0f); + + m_terrain->AddFlyingLimit(pos, 100.0f, 80.0f, pos.y-60.0f); +} + + +// Gestion d'un événement. + +BOOL CAutoRoot::EventProcess(const Event &event) +{ + D3DVECTOR pos, speed; + FPOINT dim; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_object->SetZoomX(1, 1.0f+sinf(m_time*2.0f)*0.2f); + m_object->SetZoomY(1, 1.0f+sinf(m_time*2.3f)*0.2f); + m_object->SetZoomZ(1, 1.0f+sinf(m_time*2.7f)*0.2f); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_center; + pos.x += (Rand()-0.5f)*8.0f; + pos.z += (Rand()-0.5f)*8.0f; + pos.y += 0.0f; + speed.x = (Rand()-0.5f)*12.0f; + speed.z = (Rand()-0.5f)*12.0f; + speed.y = Rand()*12.0f; + dim.x = Rand()*6.0f+4.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIROOT, 1.0f, 0.0f, 0.0f); + } + + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoRoot::RetError() +{ + return ERR_OK; +} + + diff --git a/src/autoroot.h b/src/autoroot.h new file mode 100644 index 00000000..50ca0345 --- /dev/null +++ b/src/autoroot.h @@ -0,0 +1,38 @@ +// autoroot.h + +#ifndef _AUTOROOT_H_ +#define _AUTOROOT_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + +enum ObjectType; + + + +class CAutoRoot : public CAuto +{ +public: + CAutoRoot(CInstanceManager* iMan, CObject* object); + ~CAutoRoot(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + +protected: + +protected: + float m_lastParticule; + D3DVECTOR m_center; +}; + + +#endif //_AUTOROOT_H_ diff --git a/src/autosafe.cpp b/src/autosafe.cpp new file mode 100644 index 00000000..e9c19595 --- /dev/null +++ b/src/autosafe.cpp @@ -0,0 +1,622 @@ +// autosafe.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "robotmain.h" +#include "window.h" +#include "sound.h" +#include "displaytext.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autosafe.h" + + + +#define OPEN_DELAY 8.0f // durée de l'ouverture + + + + +// Constructeur de l'objet. + +CAutoSafe::CAutoSafe(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + int i; + + CAuto::CAuto(iMan, object); + + for ( i=0 ; i<4 ; i++ ) + { + m_bKey[i] = FALSE; + m_keyParti[i] = -1; + } + + m_bLock = FALSE; + m_lastParticule = 0.0f; + m_channelSound = -1; + Init(); +} + +// Destructeur de l'objet. + +CAutoSafe::~CAutoSafe() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoSafe::DeleteObject(BOOL bAll) +{ + CObject* pObj; + + pObj = SearchVehicle(); + if ( pObj != 0 ) + { + pObj->DeleteObject(); + delete pObj; + } + + if ( m_channelSound != -1 ) + { + m_sound->FlushEnvelope(m_channelSound); + m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_channelSound = -1; + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoSafe::Init() +{ + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastParticule = 0.0f; + + m_countKeys = 0; + m_actualAngle = 0.0f; + m_finalAngle = 0.0f; + + m_phase = ASAP_WAIT; // attend ... + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + + CAuto::Init(); +} + + +// Gestion d'un événement. + +BOOL CAutoSafe::EventProcess(const Event &event) +{ + CObject* pObj; + D3DVECTOR pos, speed; + FPOINT dim; + int i, count; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + } + return TRUE; + } + + EventProgress(event.rTime); + + if ( !m_bLock ) + { + pObj = SearchVehicle(); + if ( pObj != 0 ) + { + pObj->SetLock(TRUE); // objet pas encore utilisable + m_main->CreateShortcuts(); + m_bLock = TRUE; + } + } + + if ( m_phase == ASAP_WAIT ) + { + if ( m_progress >= 1.0f ) + { + count = CountKeys(); // compte les clés présentes + if ( count != m_countKeys ) + { + m_countKeys = count; + + if ( count == 0 ) m_finalAngle = 0.0f*PI/180.0f; + if ( count == 1 ) m_finalAngle = 5.0f*PI/180.0f; + if ( count == 2 ) m_finalAngle = 10.0f*PI/180.0f; + if ( count == 3 ) m_finalAngle = 15.0f*PI/180.0f; + if ( count == 4 ) m_finalAngle = 120.0f*PI/180.0f; + + if ( count == 4 ) // toutes les clés ? + { + LockKeys(); + + m_channelSound = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 1.0f, 0.25f, TRUE); + m_sound->AddEnvelope(m_channelSound, 1.0f, 2.00f, OPEN_DELAY, SOPER_STOP); + + m_phase = ASAP_OPEN; + m_progress = 0.0f; + m_speed = 1.0f/OPEN_DELAY; + return TRUE; + } + else + { + m_channelSound = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 1.0f, 0.25f, TRUE); + m_sound->AddEnvelope(m_channelSound, 1.0f, 0.35f, 0.5f, SOPER_STOP); + } + } + + m_phase = ASAP_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == ASAP_OPEN ) + { + if ( m_progress < 1.0f ) + { + DownKeys(m_progress); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + for ( i=0 ; i<10 ; i++ ) + { + pos = m_object->RetPosition(0); + pos.x += (Rand()-0.5f)*10.0f; + pos.z += (Rand()-0.5f)*10.0f; + speed.x = (Rand()-0.5f)*4.0f; + speed.z = (Rand()-0.5f)*4.0f; + speed.y = Rand()*15.0f; + dim.x = Rand()*6.0f+4.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLUE, 1.0f, 0.0f, 0.0f); + } + + pos = m_object->RetPosition(0); + pos.x += (Rand()-0.5f)*10.0f; + pos.z += (Rand()-0.5f)*10.0f; + speed.x = (Rand()-0.5f)*4.0f; + speed.z = (Rand()-0.5f)*4.0f; + speed.y = Rand()*10.0f; + dim.x = Rand()*3.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINT, 1.0f, 0.0f, 0.0f); + + for ( i=0 ; i<4 ; i++ ) + { + pos = m_keyPos[i]; + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = 1.0f+Rand()*1.0f; + dim.x = Rand()*1.5f+1.5f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE3, 4.0f, 0.0f, 0.0f); + } + } + } + else + { + DeleteKeys(); + + pObj = SearchVehicle(); + if ( pObj != 0 ) + { + pObj->SetLock(FALSE); // objet utilisable + m_main->CreateShortcuts(); + } + + m_object->FlushCrashShere(); + m_object->SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 0.0f); + + m_sound->Play(SOUND_FINDING, m_object->RetPosition(0)); + + m_phase = ASAP_FINISH; + m_progress = 0.0f; + m_speed = 1.0f/100.0f; + } + } + + if ( m_phase == ASAP_FINISH ) + { + if ( m_progress >= 1.0f ) + { + m_phase = ASAP_FINISH; + m_progress = 0.0f; + m_speed = 1.0f/100.0f; + } + } + + // Ouvre ou ferme les portes. + if ( m_actualAngle != m_finalAngle ) + { + if ( m_actualAngle < m_finalAngle ) + { + m_actualAngle += (105.0f*PI/180.0f)*event.rTime/OPEN_DELAY; + if ( m_actualAngle > m_finalAngle ) m_actualAngle = m_finalAngle; + } + else + { + m_actualAngle -= (105.0f*PI/180.0f)*event.rTime/OPEN_DELAY; + if ( m_actualAngle < m_finalAngle ) m_actualAngle = m_finalAngle; + } + m_object->SetAngleZ(1, m_actualAngle); + m_object->SetAngleZ(2, -m_actualAngle); + } + + // Fait clignotter les clés. + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 2.0f; + dim.y = dim.x; + for ( i=0 ; i<4 ; i++ ) + { + if ( m_phase != ASAP_WAIT || !m_bKey[i] || Mod(m_time, 1.0f) < 0.4f ) + { + if ( m_keyParti[i] != -1 ) + { + m_particule->DeleteParticule(m_keyParti[i]); + m_keyParti[i] = -1; + } + } + else + { + if ( m_keyParti[i] == -1 ) + { + pos = m_keyPos[i]; + pos.y += 2.2f; + m_keyParti[i] = m_particule->CreateParticule(pos, speed, dim, PARTISELY, 1.0f, 0.0f, 0.0f); + } + } + } + + return TRUE; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoSafe::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 114, EVENT_OBJECT_TYPE); + + return TRUE; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoSafe::RetError() +{ + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + return ERR_OK; +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoSafe::Write(char *line) +{ + char name[100]; + + if ( m_phase == ASAP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoSafe::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoSafePhase)OpInt(line, "aPhase", ASAP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + + m_lastParticule = 0.0f; + + return TRUE; +} + + +// Compte le nombre de clés présentes. + +int CAutoSafe::CountKeys() +{ + CObject* pObj; + D3DVECTOR cPos, oPos; + FPOINT rot; + ObjectType oType; + float dist, angle, limit, cAngle, oAngle; + int i, index; + + cPos = m_object->RetPosition(0); + cAngle = m_object->RetAngleY(0); + + for ( index=0 ; index<4 ; index++ ) + { + m_bKey[index] = FALSE; + m_keyPos[index] = cPos; + } + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + oType = pObj->RetType(); + if ( pObj->RetTruck() != 0 ) continue; + + if ( oType != OBJECT_KEYa && + oType != OBJECT_KEYb && + oType != OBJECT_KEYc && + oType != OBJECT_KEYd ) continue; + + oPos = pObj->RetPosition(0); + dist = Length2d(oPos, cPos); + if ( dist > 20.0f ) continue; + + if ( oType == OBJECT_KEYa ) + { + limit = PI*1.0f; + oAngle = PI*0.0f; + index = 0; + } + if ( oType == OBJECT_KEYb ) + { + limit = PI*0.0f; + oAngle = PI*1.0f; + index = 1; + } + if ( oType == OBJECT_KEYc ) + { + limit = PI*1.5f; + oAngle = PI*0.5f; + index = 2; + } + if ( oType == OBJECT_KEYd ) + { + limit = PI*0.5f; + oAngle = PI*0.0f; + index = 3; + } + + angle = RotateAngle(oPos.x-cPos.x, oPos.z-cPos.z)+cAngle; + if ( !TestAngle(angle, limit-8.0f*PI/180.0f, limit+8.0f*PI/180.0f) ) continue; + + // Déplace la clé sur la forme du socle. + rot = RotatePoint(FPOINT(cPos.x, cPos.z), limit-cAngle, FPOINT(cPos.x+16.0f, cPos.z)); + oPos.x = rot.x; + oPos.z = rot.y; + oPos.y = cPos.y+1.0f; + pObj->SetPosition(0, oPos); + pObj->SetAngleY(0, oAngle+cAngle); + m_keyPos[index] = oPos; + + m_bKey[index] = TRUE; + } + + i = 0; + for ( index=0 ; index<4 ; index++ ) + { + if ( m_bKey[index] ) i++; + } + return i; +} + +// Bloque toutes les clés présentes. + +void CAutoSafe::LockKeys() +{ + CObject* pObj; + D3DVECTOR cPos, oPos; + ObjectType oType; + float dist; + int i; + + cPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + oType = pObj->RetType(); + if ( pObj->RetTruck() != 0 ) continue; + + if ( oType != OBJECT_KEYa && + oType != OBJECT_KEYb && + oType != OBJECT_KEYc && + oType != OBJECT_KEYd ) continue; + + oPos = pObj->RetPosition(0); + dist = Length2d(oPos, cPos); + if ( dist > 20.0f ) continue; + + pObj->SetLock(TRUE); + } +} + +// Fait descendre toutes les clés présentes. + +void CAutoSafe::DownKeys(float progress) +{ + CObject* pObj; + D3DVECTOR cPos, oPos; + ObjectType oType; + float dist; + int i; + + cPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + oType = pObj->RetType(); + if ( pObj->RetTruck() != 0 ) continue; + + if ( oType != OBJECT_KEYa && + oType != OBJECT_KEYb && + oType != OBJECT_KEYc && + oType != OBJECT_KEYd ) continue; + + oPos = pObj->RetPosition(0); + dist = Length2d(oPos, cPos); + if ( dist > 20.0f ) continue; + + oPos.y = cPos.y+1.0f-progress*2.2f; + pObj->SetPosition(0, oPos); + } +} + +// Supprime toutes les clés présentes. + +void CAutoSafe::DeleteKeys() +{ + CObject* pObj; + D3DVECTOR cPos, oPos; + ObjectType oType; + float dist; + int i; + BOOL bDelete; + + cPos = m_object->RetPosition(0); + + do + { + bDelete = FALSE; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + oType = pObj->RetType(); + if ( pObj->RetTruck() != 0 ) continue; + + if ( oType != OBJECT_KEYa && + oType != OBJECT_KEYb && + oType != OBJECT_KEYc && + oType != OBJECT_KEYd ) continue; + + oPos = pObj->RetPosition(0); + dist = Length2d(oPos, cPos); + if ( dist > 20.0f ) continue; + + pObj->DeleteObject(); + delete pObj; + bDelete = TRUE; + } + } + while ( bDelete ); +} + +// Cherche le véhicule dans le coffre-fort. + +CObject* CAutoSafe::SearchVehicle() +{ + CObject* pObj; + D3DVECTOR cPos, oPos; + ObjectType oType; + float dist; + int i; + + cPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + oType = pObj->RetType(); + if ( pObj == m_object ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + oPos = pObj->RetPosition(0); + dist = Length2d(oPos, cPos); + if ( dist <= 4.0f ) return pObj; + } + return 0; +} + + + diff --git a/src/autosafe.h b/src/autosafe.h new file mode 100644 index 00000000..66d55732 --- /dev/null +++ b/src/autosafe.h @@ -0,0 +1,66 @@ +// autosafe.h + +#ifndef _AUTOSAFE_H_ +#define _AUTOSAFE_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +enum AutoSafePhase +{ + ASAP_WAIT = 1, + ASAP_OPEN = 2, + ASAP_FINISH = 3, +}; + + + +class CAutoSafe : public CAuto +{ +public: + CAutoSafe(CInstanceManager* iMan, CObject* object); + ~CAutoSafe(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + int CountKeys(); + void LockKeys(); + void DownKeys(float progress); + void DeleteKeys(); + CObject* SearchVehicle(); + +protected: + AutoSafePhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastParticule; + int m_channelSound; + BOOL m_bLock; + int m_countKeys; + float m_actualAngle; + float m_finalAngle; + BOOL m_bKey[4]; + D3DVECTOR m_keyPos[4]; + int m_keyParti[4]; +}; + + +#endif //_AUTOSAFE_H_ diff --git a/src/autostation.cpp b/src/autostation.cpp new file mode 100644 index 00000000..3594eafa --- /dev/null +++ b/src/autostation.cpp @@ -0,0 +1,373 @@ +// autostation.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "light.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "interface.h" +#include "button.h" +#include "gauge.h" +#include "window.h" +#include "sound.h" +#include "auto.h" +#include "autostation.h" + + + + +// Constructeur de l'objet. + +CAutoStation::CAutoStation(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + CAuto::CAuto(iMan, object); + + Init(); +} + +// Destructeur de l'objet. + +CAutoStation::~CAutoStation() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoStation::DeleteObject(BOOL bAll) +{ + if ( m_soundChannel != -1 ) + { + m_sound->Stop(m_soundChannel); + m_soundChannel = -1; + } + + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoStation::Init() +{ + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastUpdateTime = 0.0f; + m_lastParticule = 0.0f; + m_soundChannel = -1; + m_bLastVirus = FALSE; + + CAuto::Init(); +} + + +// Gestion d'un événement. + +BOOL CAutoStation::EventProcess(const Event &event) +{ + D3DMATRIX* mat; + D3DVECTOR pos, ppos, speed; + FPOINT dim; + CObject* vehicule; + CObject* power; + TerrainRes res; + float big, energy, used, add, freq; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( !m_bLastVirus ) + { + m_bLastVirus = TRUE; + m_energyVirus = m_object->RetEnergy(); + } + + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + + m_object->SetEnergy(Rand()); + } + return TRUE; + } + else + { + if ( m_bLastVirus ) + { + m_bLastVirus = FALSE; + m_object->SetEnergy(m_energyVirus); + } + } + + UpdateInterface(event.rTime); + + big = m_object->RetEnergy(); + + res = m_terrain->RetResource(m_object->RetPosition(0)); + if ( res == TR_POWER ) + { + big += event.rTime*0.01f; // recharge la grosse pile + } + + used = big; + freq = 1.0f; + if ( big > 0.0f ) + { + vehicule = SearchVehicle(); + if ( vehicule != 0 ) + { + power = vehicule->RetPower(); + if ( power != 0 && power->RetCapacity() == 1.0f ) + { + energy = power->RetEnergy(); + add = event.rTime*0.2f; + if ( add > big*4.0f ) add = big*4.0f; + if ( add > 1.0f-energy ) add = 1.0f-energy; + energy += add; // recharge la pile + power->SetEnergy(energy); + if ( energy < freq ) freq = energy; + big -= add/4.0f; // décharge la grosse pile + } + + power = vehicule->RetFret(); + if ( power != 0 && power->RetType() == OBJECT_POWER ) + { + energy = power->RetEnergy(); + add = event.rTime*0.2f; + if ( add > big*4.0f ) add = big*4.0f; + if ( add > 1.0f-energy ) add = 1.0f-energy; + energy += add; // recharge la pile + power->SetEnergy(energy); + if ( energy < freq ) freq = energy; + big -= add/4.0f; // décharge la grosse pile + } + } + } + used -= big; // énergie utilisée + + if ( freq < 1.0f ) // charge en cours ? + { + freq = 1.0f+3.0f*freq; + if ( m_soundChannel == -1 ) + { + m_soundChannel = m_sound->Play(SOUND_STATION, m_object->RetPosition(0), + 0.3f, freq, TRUE); + } + m_sound->Frequency(m_soundChannel, freq); + } + else + { + if ( m_soundChannel != -1 ) + { + m_sound->Stop(m_soundChannel); + m_soundChannel = -1; + } + } + + if ( used != 0.0f && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(-15.0f, 7.0f, 0.0f); // position batterie + pos = Transform(*mat, pos); + speed.x = (Rand()-0.5f)*20.0f; + speed.y = (Rand()-0.5f)*20.0f; + speed.z = (Rand()-0.5f)*20.0f; + ppos.x = pos.x; + ppos.y = pos.y+(Rand()-0.5f)*4.0f; + ppos.z = pos.z; + dim.x = 1.5f; + dim.y = 1.5f; + m_particule->CreateParticule(ppos, speed, dim, PARTIBLITZ, 1.0f, 0.0f, 0.0f); + +#if 0 + ppos = pos; + ppos.y += 1.0f; + ppos.x += (Rand()-0.5f)*3.0f; + ppos.z += (Rand()-0.5f)*3.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 2.5f+Rand()*6.0f; + dim.x = Rand()*1.5f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(ppos, speed, dim, PARTISMOKE3, 4.0f); +#else + ppos = pos; + ppos.y += 1.0f; + ppos.x += (Rand()-0.5f)*3.0f; + ppos.z += (Rand()-0.5f)*3.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 2.5f+Rand()*5.0f; + dim.x = Rand()*1.0f+0.6f; + dim.y = dim.x; + m_particule->CreateParticule(ppos, speed, dim, PARTIVAPOR, 3.0f); +#endif + } + + if ( big < 0.0f ) big = 0.0f; + if ( big > 1.0f ) big = 1.0f; + m_object->SetEnergy(big); // màj la grosse pile + + return TRUE; +} + + +// Cherche le véhicule placé sur la station. + +CObject* CAutoStation::SearchVehicle() +{ + CObject* pObj; + D3DVECTOR sPos, oPos; + ObjectType type; + float dist; + int i; + + sPos = m_object->RetPosition(0); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_HUMAN && + type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis && + type != OBJECT_MOBILErt && + type != OBJECT_MOBILErc && + type != OBJECT_MOBILErr && + type != OBJECT_MOBILErs && + type != OBJECT_MOBILEsa && + type != OBJECT_MOBILEft && + type != OBJECT_MOBILEtt && + type != OBJECT_MOBILEwt && + type != OBJECT_MOBILEit && + type != OBJECT_MOBILEdr ) continue; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, sPos); + if ( dist <= 5.0f ) return pObj; + } + + return 0; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoStation::RetError() +{ + TerrainRes res; + + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + + res = m_terrain->RetResource(m_object->RetPosition(0)); + if ( res != TR_POWER ) return ERR_STATION_NULL; + + return ERR_OK; +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoStation::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*14.5f; + pos.y = oy+sy*0; + ddim.x = 14.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGauge(pos, ddim, 0, EVENT_OBJECT_GENERGY); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 104, EVENT_OBJECT_TYPE); + + return TRUE; +} + +// Met à jour l'état de tous les boutons de l'interface, +// suite au temps qui s'écoule ... + +void CAutoStation::UpdateInterface(float rTime) +{ + CWindow* pw; + CGauge* pg; + + CAuto::UpdateInterface(rTime); + + if ( m_time < m_lastUpdateTime+0.1f ) return; + m_lastUpdateTime = m_time; + + if ( !m_object->RetSelect() ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + pg = (CGauge*)pw->SearchControl(EVENT_OBJECT_GENERGY); + if ( pg != 0 ) + { + pg->SetLevel(m_object->RetEnergy()); + } +} + + diff --git a/src/autostation.h b/src/autostation.h new file mode 100644 index 00000000..9fd02b01 --- /dev/null +++ b/src/autostation.h @@ -0,0 +1,48 @@ +// autostation.h + +#ifndef _AUTOSTATION_H_ +#define _AUTOSTATION_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + + + +class CAutoStation : public CAuto +{ +public: + CAutoStation(CInstanceManager* iMan, CObject* object); + ~CAutoStation(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + +protected: + void UpdateInterface(float rTime); + + CObject* SearchVehicle(); + +protected: + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastUpdateTime; + float m_lastParticule; + int m_soundChannel; + D3DVECTOR m_fretPos; + BOOL m_bLastVirus; + float m_energyVirus; +}; + + +#endif //_AUTOSTATION_H_ diff --git a/src/autotower.cpp b/src/autotower.cpp new file mode 100644 index 00000000..68a6e9e5 --- /dev/null +++ b/src/autotower.cpp @@ -0,0 +1,547 @@ +// autotower.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "camera.h" +#include "object.h" +#include "physics.h" +#include "interface.h" +#include "button.h" +#include "gauge.h" +#include "window.h" +#include "sound.h" +#include "displaytext.h" +#include "cmdtoken.h" +#include "auto.h" +#include "autotower.h" + + + +#define TOWER_SCOPE 200.0f // portée du rayon +#define ENERGY_FIRE 0.125f // énergie consommée par tir + + +// Constructeur de l'objet. + +CAutoTower::CAutoTower(CInstanceManager* iMan, CObject* object) + : CAuto(iMan, object) +{ + int i; + + CAuto::CAuto(iMan, object); + + for ( i=0 ; i<4 ; i++ ) + { + m_partiStop[i] = -1; + } + + Init(); + m_phase = ATP_WAIT; // en pause jusqu'au premier Init() + m_time = 0.0f; + m_lastUpdateTime = 0.0f; +} + +// Destructeur de l'objet. + +CAutoTower::~CAutoTower() +{ + CAuto::~CAuto(); +} + + +// Détruit l'objet. + +void CAutoTower::DeleteObject(BOOL bAll) +{ + FireStopUpdate(0.0f, FALSE); + CAuto::DeleteObject(bAll); +} + + +// Initialise l'objet. + +void CAutoTower::Init() +{ + m_phase = ATP_ZERO; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + + m_time = 0.0f; + m_timeVirus = 0.0f; + m_lastUpdateTime = 0.0f; + m_lastParticule = 0.0f; +} + + +// Gestion d'un événement. + +BOOL CAutoTower::EventProcess(const Event &event) +{ + CObject* power; + CObject* target; + D3DVECTOR pos; + float angle, energy, quick; + + CAuto::EventProcess(event); + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_timeVirus -= event.rTime; + + if ( m_object->RetVirusMode() ) // contaminé par un virus ? + { + if ( m_timeVirus <= 0.0f ) + { + m_timeVirus = 0.1f+Rand()*0.3f; + + angle = m_object->RetAngleY(1); + angle += Rand()*0.5f; + m_object->SetAngleY(1, angle); + + m_object->SetAngleZ(2, Rand()*0.5f); + } + return TRUE; + } + + UpdateInterface(event.rTime); + + if ( m_phase == ATP_WAIT ) return TRUE; + + m_progress += event.rTime*m_speed; + + if ( m_phase == ATP_ZERO ) + { + FireStopUpdate(m_progress, TRUE); // clignotte + if ( m_progress < 1.0f ) + { + energy = 0.0f; + power = m_object->RetPower(); + if ( power != 0 ) + { + energy = power->RetEnergy(); + } + if ( energy >= ENERGY_FIRE ) + { + m_phase = ATP_SEARCH; + m_progress = 0.0f; + m_speed = 1.0f/3.0f; + } + } + else + { + m_phase = ATP_ZERO; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + if ( m_phase == ATP_SEARCH ) + { + FireStopUpdate(m_progress, FALSE); // éteint + if ( m_progress < 1.0f ) + { + quick = 1.0f; +//? if ( g_researchDone & RESEARCH_QUICK ) quick = 3.0f; + + angle = m_object->RetAngleY(1); + angle -= event.rTime*quick*2.0f; + m_object->SetAngleY(1, angle); + + angle = m_object->RetAngleZ(2); + angle += event.rTime*quick*0.5f; + if ( angle > 0.0f ) angle = 0.0f; + m_object->SetAngleZ(2, angle); + } + else + { + energy = 0.0f; + power = m_object->RetPower(); + if ( power != 0 ) + { + energy = power->RetEnergy(); + } + + target = SearchTarget(m_targetPos); + if ( energy < ENERGY_FIRE ) + { + m_displayText->DisplayError(ERR_TOWER_ENERGY, m_object); + } + if ( target == 0 || energy < ENERGY_FIRE ) + { + m_phase = ATP_ZERO; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + else + { + pos = m_object->RetPosition(0); + pos.y += 24.5f; + m_angleYfinal = RotateAngle(m_targetPos.x-pos.x, pos.z-m_targetPos.z); // CW ! + m_angleYfinal += PI*2.0f; + m_angleYfinal -= m_object->RetAngleY(0); + m_angleYactual = NormAngle(m_object->RetAngleY(1)); + + m_angleZfinal = -PI/2.0f; + m_angleZfinal -= RotateAngle(Length2d(m_targetPos, pos), pos.y-m_targetPos.y); // CW ! + m_angleZactual = m_object->RetAngleZ(2); + + m_phase = ATP_TURN; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; +//? if ( g_researchDone & RESEARCH_QUICK ) m_speed = 1.0f/0.2f; + } + } + } + + if ( m_phase == ATP_TURN ) + { + if ( m_progress < 1.0f ) + { + angle = m_angleYactual+(m_angleYfinal-m_angleYactual)*m_progress; + m_object->SetAngleY(1, angle); + + angle = m_angleZactual+(m_angleZfinal-m_angleZactual)*m_progress; + m_object->SetAngleZ(2, angle); + } + else + { + m_object->SetAngleY(1, m_angleYfinal); + m_object->SetAngleZ(2, m_angleZfinal); + + power = m_object->RetPower(); + if ( power != 0 ) + { + energy = power->RetEnergy(); + energy -= ENERGY_FIRE/power->RetCapacity(); + power->SetEnergy(energy); + } + + m_sound->Play(SOUND_GGG, m_object->RetPosition(0)); + + m_phase = ATP_FIRE; + m_progress = 0.0f; + m_speed = 1.0f/1.5f; + } + } + + if ( m_phase == ATP_FIRE ) + { + if ( m_progress == 0.0f ) + { + pos = m_object->RetPosition(0); + pos.y += 24.5f; + m_particule->CreateRay(pos, m_targetPos, PARTIRAY1, + FPOINT(5.0f, 5.0f), 1.5f); + } + if ( m_progress >= 1.0f ) + { + m_phase = ATP_ZERO; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + + return TRUE; +} + + +// Cherche l'objet cible le plus proche. + +CObject* CAutoTower::SearchTarget(D3DVECTOR &impact) +{ + CObject* pObj; + CObject* pBest = 0; + CPhysics* physics; + D3DVECTOR iPos, oPos; + ObjectType oType; + float distance, min, radius, speed; + int i; + + iPos = m_object->RetPosition(0); + min = 1000000.0f; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + oType = pObj->RetType(); + if ( oType != OBJECT_MOTHER && + oType != OBJECT_ANT && + oType != OBJECT_SPIDER && + oType != OBJECT_BEE && + oType != OBJECT_WORM ) continue; + + if ( !pObj->RetActif() ) continue; // inactif ? + +//? if ( g_researchDone & RESEARCH_QUICK ) + if ( FALSE ) + { + physics = pObj->RetPhysics(); + if ( physics != 0 ) + { + speed = Abs(physics->RetLinMotionX(MO_REASPEED)); + if ( speed > 20.0f ) continue; // avance trop vite ? + } + } + + if ( !pObj->GetCrashSphere(0, oPos, radius) ) continue; + distance = Length(oPos, iPos); + if ( distance > TOWER_SCOPE ) continue; // trop loin + if ( distance < min ) + { + min = distance; + pBest = pObj; + } + } + if ( pBest == 0 ) return 0; + + impact = pBest->RetPosition(0); + return pBest; +} + + +// Retourne une erreur liée à l'état de l'automate. + +Error CAutoTower::RetError() +{ + CObject* power; + + if ( m_object->RetVirusMode() ) + { + return ERR_BAT_VIRUS; + } + + power = m_object->RetPower(); + if ( power == 0 ) + { + return ERR_TOWER_POWER; // pas de pile + } + else + { + if ( power->RetEnergy() < ENERGY_FIRE ) + { + return ERR_TOWER_ENERGY; // plus assez d'énergie + } + } + return ERR_OK; +} + + +// Met à jour les feux de stop. + +void CAutoTower::FireStopUpdate(float progress, BOOL bLightOn) +{ + D3DMATRIX* mat; + D3DVECTOR pos, speed; + FPOINT dim; + int i; + + static float listpos[8] = + { + 4.5f, 0.0f, + 0.0f, 4.5f, + -4.5f, 0.0f, + 0.0f, -4.5f, + }; + + if ( !bLightOn ) // éteint ? + { + for ( i=0 ; i<4 ; i++ ) + { + if ( m_partiStop[i] != -1 ) + { + m_particule->DeleteParticule(m_partiStop[i]); + m_partiStop[i] = -1; + } + } + return; + } + + mat = m_object->RetWorldMatrix(0); + + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 2.0f; + dim.y = dim.x; + + for ( i=0 ; i<4 ; i++ ) + { + if ( Mod(progress+i*0.125f, 0.5f) < 0.2f ) + { + if ( m_partiStop[i] != -1 ) + { + m_particule->DeleteParticule(m_partiStop[i]); + m_partiStop[i] = -1; + } + } + else + { + if ( m_partiStop[i] == -1 ) + { + pos.x = listpos[i*2+0]; + pos.y = 18.0f; + pos.z = listpos[i*2+1]; + pos = Transform(*mat, pos); + m_partiStop[i] = m_particule->CreateParticule(pos, speed, + dim, PARTISELR, + 1.0f, 0.0f, 0.0f); + } + } + } +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CAutoTower::CreateInterface(BOOL bSelect) +{ + CWindow* pw; + FPOINT pos, ddim; + float ox, oy, sx, sy; + + CAuto::CreateInterface(bSelect); + + if ( !bSelect ) return TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + pos.x = ox+sx*14.5f; + pos.y = oy+sy*0; + ddim.x = 14.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGauge(pos, ddim, 0, EVENT_OBJECT_GENERGY); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0; + ddim.x = 66.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGroup(pos, ddim, 107, EVENT_OBJECT_TYPE); + + pos.x = ox+sx*10.2f; + pos.y = oy+sy*0.5f; + ddim.x = 33.0f/640.0f; + ddim.y = 33.0f/480.0f; + pw->CreateButton(pos, ddim, 41, EVENT_OBJECT_LIMIT); + + return TRUE; +} + +// Met à jour l'état de tous les boutons de l'interface, +// suite au temps qui s'écoule ... + +void CAutoTower::UpdateInterface(float rTime) +{ + CWindow* pw; + CGauge* pg; + CObject* power; + float energy; + + CAuto::UpdateInterface(rTime); + + if ( m_time < m_lastUpdateTime+0.1f ) return; + m_lastUpdateTime = m_time; + + if ( !m_object->RetSelect() ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + pg = (CGauge*)pw->SearchControl(EVENT_OBJECT_GENERGY); + if ( pg != 0 ) + { + energy = 0.0f; + power = m_object->RetPower(); + if ( power != 0 ) + { + energy = power->RetEnergy(); + } + pg->SetLevel(energy); + } +} + + +// Sauve tous les paramètres de l'automate. + +BOOL CAutoTower::Write(char *line) +{ + char name[100]; + + if ( m_phase == ATP_WAIT ) return FALSE; + + sprintf(name, " aExist=%d", 1); + strcat(line, name); + + CAuto::Write(line); + + sprintf(name, " aPhase=%d", m_phase); + strcat(line, name); + + sprintf(name, " aProgress=%.2f", m_progress); + strcat(line, name); + + sprintf(name, " aSpeed=%.2f", m_speed); + strcat(line, name); + + sprintf(name, " aTargetPos=%.2f;%.2f;%.2f", m_targetPos.x, m_targetPos.y, m_targetPos.z); + strcat(line, name); + + sprintf(name, " aAngleYactual=%.2f", m_angleYactual); + strcat(line, name); + + sprintf(name, " aAngleZactual=%.2f", m_angleZactual); + strcat(line, name); + + sprintf(name, " aAngleYfinal=%.2f", m_angleYfinal); + strcat(line, name); + + sprintf(name, " aAngleZfinal=%.2f", m_angleZfinal); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'automate. + +BOOL CAutoTower::Read(char *line) +{ + if ( OpInt(line, "aExist", 0) == 0 ) return FALSE; + + CAuto::Read(line); + + m_phase = (AutoTowerPhase)OpInt(line, "aPhase", ATP_WAIT); + m_progress = OpFloat(line, "aProgress", 0.0f); + m_speed = OpFloat(line, "aSpeed", 1.0f); + m_targetPos = OpDir(line, "aTargetPos"); + m_angleYactual = OpFloat(line, "aAngleYactual", 0.0f); + m_angleZactual = OpFloat(line, "aAngleZactual", 0.0f); + m_angleYfinal = OpFloat(line, "aAngleYfinal", 0.0f); + m_angleZfinal = OpFloat(line, "aAngleZfinal", 0.0f); + + m_lastUpdateTime = 0.0f; + + return TRUE; +} + + diff --git a/src/autotower.h b/src/autotower.h new file mode 100644 index 00000000..6fe5032f --- /dev/null +++ b/src/autotower.h @@ -0,0 +1,68 @@ +// autotower.h + +#ifndef _AUTOTOWER_H_ +#define _AUTOTOWER_H_ + + +class CInstanceManager; +class CD3DEngine; +class CParticule; +class CTerrain; +class CCamera; +class CObject; + +enum ObjectType; + + + +enum AutoTowerPhase +{ + ATP_WAIT = 1, + ATP_ZERO = 2, // plus d'énergie + ATP_SEARCH = 3, // cherche une cible + ATP_TURN = 4, // tourne vers la cible + ATP_FIRE = 5, // tire sur la cible +}; + + + +class CAutoTower : public CAuto +{ +public: + CAutoTower(CInstanceManager* iMan, CObject* object); + ~CAutoTower(); + + void DeleteObject(BOOL bAll=FALSE); + + void Init(); + BOOL EventProcess(const Event &event); + Error RetError(); + + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + +protected: + void UpdateInterface(float rTime); + + CObject* SearchTarget(D3DVECTOR &impact); + void FireStopUpdate(float progress, BOOL bLightOn); + +protected: + AutoTowerPhase m_phase; + float m_progress; + float m_speed; + float m_timeVirus; + float m_lastUpdateTime; + float m_lastParticule; + D3DVECTOR m_targetPos; + float m_angleYactual; + float m_angleZactual; + float m_angleYfinal; + float m_angleZfinal; + int m_partiStop[4]; +}; + + +#endif //_AUTOTOWER_H_ diff --git a/src/blitz.cpp b/src/blitz.cpp new file mode 100644 index 00000000..cef567ef --- /dev/null +++ b/src/blitz.cpp @@ -0,0 +1,456 @@ +// blitz.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "terrain.h" +#include "math3d.h" +#include "object.h" +#include "camera.h" +#include "auto.h" +#include "autopara.h" +#include "sound.h" +#include "blitz.h" + + + + +// Constructeur du terrain. + +CBlitz::CBlitz(CInstanceManager* iMan, CD3DEngine* engine) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_BLITZ, this); + + m_engine = engine; + m_terrain = 0; + m_camera = 0; + m_sound = 0; + Flush(); +} + +// Destructeur du terrain. + +CBlitz::~CBlitz() +{ +} + + +// Supprime les éclairs. + +void CBlitz::Flush() +{ + int i; + + m_bBlitzExist = FALSE; + m_time = 0.0f; + m_phase = BPH_WAIT; + m_speed = 0.0f; + m_progress = 0.0f; + + for ( i=0 ; iRetPause() ) return TRUE; + if ( m_engine->RetMovieLock() ) return TRUE; + + m_time += event.rTime; + m_progress += event.rTime*m_speed; + + if ( m_phase == BPH_WAIT ) + { + if ( m_progress >= 1.0f ) + { +#if 1 + m_pos.x = (Rand()-0.5f)*(3200.0f-200.0f); + m_pos.z = (Rand()-0.5f)*(3200.0f-200.0f); +#else + m_pos.x = (Rand()-0.5f)*(3200.0f-2800.0f); + m_pos.z = (Rand()-0.5f)*(3200.0f-2800.0f); +#endif + m_pos.y = 0.0f; + + pObj = SearchObject(m_pos); + if ( pObj == 0 ) + { + m_terrain->MoveOnFloor(m_pos, TRUE); + } + else + { + m_pos = pObj->RetPosition(0); + m_terrain->MoveOnFloor(m_pos, TRUE); + + type = pObj->RetType(); + if ( type == OBJECT_BASE ) + { + m_pos.y += 120.0f; // sommet de la fusée + } + else if ( type == OBJECT_PARA ) + { + automat = (CAutoPara*)pObj->RetAuto(); + if ( automat != 0 ) + { + automat->StartBlitz(); + } + m_pos.y += 67.0f; // sommet du paratonnerre + } + else + { + pObj->ExploObject(EXPLO_BOUM, 1.0f); + } + } + + eye = m_engine->RetEyePt(); + dist = Length(m_pos, eye); + deep = m_engine->RetDeepView(); + + if ( dist < deep ) + { + pos = eye+((m_pos-eye)*0.2f); // comme si proche ! + m_sound->Play(SOUND_BLITZ, pos); + + m_camera->StartOver(OE_BLITZ, m_pos, 1.0f); + + m_phase = BPH_BLITZ; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + } + } + } + + if ( m_phase == BPH_BLITZ ) + { + if ( m_progress < 1.0f ) + { + max = 5.0f; + for ( i=0 ; i max ) m_shift[i].x = max; + + m_shift[i].y += (Rand()-0.5f)*max*2.0f; + if ( m_shift[i].y < -max ) m_shift[i].y = -max; + if ( m_shift[i].y > max ) m_shift[i].y = max; + + m_width[i] += (Rand()-0.5f)*2.0f; + if ( m_width[i] < 1.0f ) m_width[i] = 1.0f; + if ( m_width[i] > 6.0f ) m_width[i] = 6.0f; + } + m_shift[0].x = 0.0f; + m_shift[0].y = 0.0f; + m_width[0] = 0.0f; + } + else + { + m_phase = BPH_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/(1.0f+Rand()*m_delay); + } + } + + return TRUE; +} + + +// Dessine les éclairs. + +void CBlitz::Draw() +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2 vertex[4]; // 2 triangles + D3DVECTOR corner[4], eye, n, p, p1, p2; + D3DMATRIX matrix; + FPOINT texInf, texSup, rot; + float a; + int i; + + if ( !m_bBlitzExist ) return; + if ( m_phase != BPH_BLITZ ) return; + + device = m_engine->RetD3DDevice(); + device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + + D3DUtil_SetIdentityMatrix(matrix); + device->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + m_engine->SetTexture("effect00.tga"); + m_engine->SetState(D3DSTATETTb); + texInf.x = 64.5f/256.0f; + texInf.y = 33.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 34.0f/256.0f; // blanc + + p1 = m_pos; + eye = m_engine->RetEyePt(); + a = RotateAngle(eye.x-p1.x, eye.z-p1.z); + n = Normalize(p1-eye); + + for ( i=0 ; iDrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + + p1 = p2; + } + + device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, TRUE); +} + + +// Enclanche les éclairs. + +BOOL CBlitz::Create(float sleep, float delay, float magnetic) +{ + m_bBlitzExist = TRUE; + if ( sleep < 1.0f ) sleep = 1.0f; + m_sleep = sleep; + m_delay = delay; + m_magnetic = magnetic; + + m_phase = BPH_WAIT; + m_progress = 0.0f; + m_speed = 1.0f/m_sleep; + + if ( m_terrain == 0 ) + { + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + } + + if ( m_camera == 0 ) + { + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + } + + if ( m_sound == 0 ) + { + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + } + + return FALSE; +} + + +// Donne l'état des éclairs. + +BOOL CBlitz::GetStatus(float &sleep, float &delay, float &magnetic, float &progress) +{ + if ( !m_bBlitzExist ) return FALSE; + + sleep = m_sleep; + delay = m_delay; + magnetic = m_magnetic; + progress = m_progress; + + return TRUE; +} + +// Spécifie l'état des éclairs. + +BOOL CBlitz::SetStatus(float sleep, float delay, float magnetic, float progress) +{ + m_bBlitzExist = TRUE; + + m_sleep = sleep; + m_delay = delay; + m_magnetic = magnetic; + m_progress = progress; + m_phase = BPH_WAIT; + m_speed = 1.0f/m_sleep; + + return TRUE; +} + + +// Cherche l'objet le plus proche de l'éclair. + +CObject* CBlitz::SearchObject(D3DVECTOR pos) +{ + CObject *pObj, *pBest, *pObjPara[100]; + D3DVECTOR oPos, pPos[100]; + ObjectType type; + float min, dist, detect; + int i, nbPara; + + // Cherche l'objet le plus proche du point d'impact de la foudre. + pBest = 0; + min = 100000.0f; + nbPara = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; // objet inactif ? + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + + type = pObj->RetType(); + if ( type == OBJECT_BASE || + type == OBJECT_PARA ) // bâtiment à effet paratonnerre ? + { + pObjPara[nbPara] = pObj; + pPos[nbPara] = pObj->RetPosition(0); + nbPara ++; + } + + detect = 0.0f; + if ( type == OBJECT_BASE || + type == OBJECT_DERRICK || + type == OBJECT_FACTORY || + type == OBJECT_REPAIR || + type == OBJECT_DESTROYER|| + type == OBJECT_STATION || + type == OBJECT_CONVERT || + type == OBJECT_TOWER || + type == OBJECT_RESEARCH || + type == OBJECT_RADAR || + type == OBJECT_INFO || + type == OBJECT_ENERGY || + type == OBJECT_LABO || + type == OBJECT_NUCLEAR || + type == OBJECT_PARA || + type == OBJECT_SAFE || + type == OBJECT_HUSTON ) + { + detect = m_magnetic; + } + if ( type == OBJECT_METAL || + type == OBJECT_POWER || + type == OBJECT_ATOMIC ) + { + detect = m_magnetic*0.3f; + } + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr ) + { + detect = m_magnetic*0.5f; + } + if ( detect == 0.0f ) continue; + + oPos = pObj->RetPosition(0); + dist = Length2d(oPos, pos); + if ( dist > detect ) continue; + if ( dist < min ) + { + min = dist; + pBest = pObj; + } + } + if ( pBest == 0 ) return 0; // rien trouvé + + // Sous la protection d'un paratonnerre ? + oPos = pBest->RetPosition(0); + for ( i=nbPara-1 ; i>=0 ; i-- ) + { + dist = Length2d(oPos, pPos[i]); + if ( dist <= BLITZPARA ) + { + return pObjPara[i]; + } + } + return pBest; +} + diff --git a/src/blitz.h b/src/blitz.h new file mode 100644 index 00000000..df6e70ec --- /dev/null +++ b/src/blitz.h @@ -0,0 +1,64 @@ +// blitz.h + +#ifndef _BLITZ_H_ +#define _BLITZ_H_ + + +class CInstanceManager; +class CD3DEngine; +class CTerrain; +class CCamera; +class CSound; + + + +#define BLITZPARA 200.0f // rayon de protection du paratonnerre +#define BLITZMAX 50 + +enum BlitzPhase +{ + BPH_WAIT, + BPH_BLITZ, +}; + + + +class CBlitz +{ +public: + CBlitz(CInstanceManager* iMan, CD3DEngine* engine); + ~CBlitz(); + + void Flush(); + BOOL EventProcess(const Event &event); + BOOL Create(float sleep, float delay, float magnetic); + BOOL GetStatus(float &sleep, float &delay, float &magnetic, float &progress); + BOOL SetStatus(float sleep, float delay, float magnetic, float progress); + void Draw(); + +protected: + BOOL EventFrame(const Event &event); + CObject* SearchObject(D3DVECTOR pos); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CTerrain* m_terrain; + CCamera* m_camera; + CSound* m_sound; + + BOOL m_bBlitzExist; + float m_sleep; + float m_delay; + float m_magnetic; + BlitzPhase m_phase; + float m_time; + float m_speed; + float m_progress; + D3DVECTOR m_pos; + FPOINT m_shift[BLITZMAX]; + float m_width[BLITZMAX]; +}; + + +#endif //_BLITZ_H_ diff --git a/src/brain.cpp b/src/brain.cpp new file mode 100644 index 00000000..5155eb24 --- /dev/null +++ b/src/brain.cpp @@ -0,0 +1,2985 @@ +// brain.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "cbot/cbotdll.h" +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "language.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "math3d.h" +#include "robotmain.h" +#include "terrain.h" +#include "water.h" +#include "camera.h" +#include "object.h" +#include "physics.h" +#include "motion.h" +#include "motionspider.h" +#include "pyro.h" +#include "taskmanager.h" +#include "task.h" +#include "taskmanip.h" +#include "taskflag.h" +#include "taskshield.h" +#include "script.h" +#include "studio.h" +#include "interface.h" +#include "button.h" +#include "color.h" +#include "edit.h" +#include "list.h" +#include "label.h" +#include "group.h" +#include "gauge.h" +#include "slider.h" +#include "compass.h" +#include "target.h" +#include "window.h" +#include "displaytext.h" +#include "text.h" +#include "sound.h" +#include "particule.h" +#include "cmdtoken.h" +#include "brain.h" + + + +#define MAXTRACERECORD 1000 + + + +// Constructeur de l'objet. + +CBrain::CBrain(CInstanceManager* iMan, CObject* object) +{ + int i; + + m_iMan = iMan; + m_iMan->AddInstance(CLASS_BRAIN, this, 100); + + m_object = object; + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + m_interface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + m_displayText = (CDisplayText*)m_iMan->SearchInstance(CLASS_DISPLAYTEXT); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + m_physics = 0; + m_motion = 0; + m_primaryTask = 0; + m_secondaryTask = 0; + m_studio = 0; + + m_program = -1; + m_bActivity = TRUE; + m_bBurn = FALSE; + m_bActiveVirus = FALSE; + m_time = 0.0f; + m_burnTime = 0.0f; + m_lastUpdateTime = 0.0f; + m_lastHumanTime = 0.0f; + m_lastWormTime = 0.0f; + m_antTarget = 0; + m_beeBullet = 0; + m_lastAlarmTime = 0.0f; + m_soundChannelAlarm = -1; + m_flagColor = 0; + + m_buttonAxe = EVENT_NULL; + m_defaultEnter = EVENT_NULL; + m_manipStyle = EVENT_OBJECT_MFRONT; + + for ( i=0 ; iDeleteInstance(CLASS_BRAIN, this); +} + + +// Détruit l'objet. + +void CBrain::DeleteObject(BOOL bAll) +{ + if ( m_soundChannelAlarm != -1 ) + { + m_sound->FlushEnvelope(m_soundChannelAlarm); + m_sound->AddEnvelope(m_soundChannelAlarm, 0.0f, 0.5f, 0.5f, SOPER_STOP); + m_soundChannelAlarm = -1; + } + + if ( !bAll ) + { + if ( m_beeBullet != 0 ) + { + m_beeBullet->DeleteObject(); + delete m_beeBullet; + m_beeBullet = 0; + } + } + + if ( m_studio != 0 ) // édition en cours ? + { + StopEditScript(TRUE); + } +} + + +void CBrain::SetPhysics(CPhysics* physics) +{ + m_physics = physics; +} + +void CBrain::SetMotion(CMotion* motion) +{ + m_motion = motion; +} + + +// Sauve tous les paramètres de l'objet. + +BOOL CBrain::Write(char *line) +{ + char name[100]; + + sprintf(name, " bVirusActive=%d", m_bActiveVirus); + strcat(line, name); + + return TRUE; +} + +// Restitue tous les paramètres de l'objet. + +BOOL CBrain::Read(char *line) +{ + m_bActiveVirus = OpInt(line, "bVirusActive", 0); + + return TRUE; +} + + +// Gestion d'un événement. + +BOOL CBrain::EventProcess(const Event &event) +{ + CWindow* pw; + CControl* pc; + CSlider* ps; + EventMsg action; + ObjectType type; + Error err; + float axeX, axeY, axeZ, factor; + + type = m_object->RetType(); + + if ( m_primaryTask != 0 ) // tâche en cours ? + { + m_primaryTask->EventProcess(event); + } + + if ( m_secondaryTask != 0 ) // tâche en cours ? + { + m_secondaryTask->EventProcess(event); + } + + action = EVENT_NULL; + + if ( event.event == EVENT_KEYDOWN && + (event.param == m_engine->RetKey(KEYRANK_ACTION, 0) || + event.param == m_engine->RetKey(KEYRANK_ACTION, 1) ) && + !m_main->RetEditLock() ) + { + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw != 0 ) + { + pc = pw->SearchControl(m_defaultEnter); + if ( pc != 0 ) + { + if ( pc->TestState(STATE_ENABLE) ) + { + action = m_defaultEnter; + } + } + } + } + else + { + action = event.event; + } + + if ( action == EVENT_NULL ) return TRUE; + + if ( action == EVENT_UPDINTERFACE ) + { + if ( m_object->RetSelect() ) CreateInterface(TRUE); + } + + if ( action == EVENT_FRAME ) + { + EventFrame(event); + } + + if ( m_object->RetSelect() && // robot sélectionné ? + m_studio != 0 ) // édition en cours ? + { + m_studio->EventProcess(event); + + if ( action == EVENT_OBJECT_PROGRUN ) + { + if ( m_program == -1 ) + { + RunProgram(m_selScript); + } + else + { + StopProgram(); + } + } + if ( action == EVENT_OBJECT_PROGSTART ) + { + m_main->SaveOneScript(m_object); + RunProgram(m_selScript); + } + if ( action == EVENT_OBJECT_PROGSTOP ) + { + StopProgram(); + } + if ( action == EVENT_STUDIO_OK ) + { + StopEditScript(FALSE); + m_main->SaveOneScript(m_object); + } + if ( action == EVENT_STUDIO_CANCEL ) + { + StopEditScript(TRUE); + m_main->SaveOneScript(m_object); + } + return TRUE; + } + + if ( !m_object->RetSelect() && // robot pas sélectionné ? + m_program == -1 && + m_primaryTask == 0 ) + { + axeX = 0.0f; + axeY = 0.0f; + axeZ = 0.0f; + if ( m_object->RetBurn() ) // brûle ? + { + if ( !m_bBurn ) // début ? + { + m_bBurn = TRUE; + m_burnTime = 0.0f; + } + + axeZ = -1.0f; // tombe + + if ( !m_object->RetFixed() && + (type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_WORM ) ) + { + axeY = 2.0f; // zig-zag désordonné rapide + if ( type == OBJECT_WORM ) axeY = 5.0f; + axeX = 0.5f+sinf(m_time* 1.0f)*0.5f+ + sinf(m_time* 6.0f)*2.0f+ + sinf(m_time*21.0f)*0.2f; + factor = 1.0f-m_burnTime/15.0f; // ralenti + if ( factor < 0.0f ) factor = 0.0f; + axeY *= factor; + axeX *= factor; + } + } + m_physics->SetMotorSpeedX(axeY); // avancer/reculer + m_physics->SetMotorSpeedY(axeZ); // monter/descendre + m_physics->SetMotorSpeedZ(axeX); // tourner + return TRUE; + } + + if ( m_program != -1 && + m_object->RetRuin() ) + { + StopProgram(); + return TRUE; + } + + if ( !m_object->RetSelect() ) // robot pas sélectionné ? + { + return TRUE; + } + + if ( m_secondaryTask != 0 ) // tâche en cours ? + { + if ( action == EVENT_OBJECT_ENDSHIELD ) + { + m_secondaryTask->StartTaskShield(TSM_DOWN, 0.0f); + } + } + if ( m_primaryTask != 0 || // tâche en cours ? + m_program != -1 ) + { + if ( action == EVENT_OBJECT_PROGRUN ) + { + StopProgram(); + } + if ( action == EVENT_OBJECT_PROGEDIT ) + { + StartEditScript(m_selScript, m_main->RetScriptName()); + } + if ( m_primaryTask == 0 || !m_primaryTask->IsPilot() ) return TRUE; + } + + if ( action == EVENT_OBJECT_LEFT || + action == EVENT_OBJECT_RIGHT || + action == EVENT_OBJECT_UP || + action == EVENT_OBJECT_DOWN || + action == EVENT_OBJECT_GASUP || + action == EVENT_OBJECT_GASDOWN ) + { + m_buttonAxe = action; + } + if ( action == EVENT_LBUTTONUP || + action == EVENT_RBUTTONUP ) + { + m_buttonAxe = EVENT_NULL; + } + + axeX = event.axeX; + axeY = event.axeY; + axeZ = event.axeZ; + + if ( !m_main->RetTrainerPilot() && + m_object->RetTrainer() ) // véhicule d'entraînement ? + { + axeX = 0.0f; + axeY = 0.0f; + axeZ = 0.0f; // télécommande impossible ! + } + + if ( m_buttonAxe == EVENT_OBJECT_LEFT ) axeX = -1.0f; + if ( m_buttonAxe == EVENT_OBJECT_RIGHT ) axeX = 1.0f; + if ( m_buttonAxe == EVENT_OBJECT_UP ) axeY = 1.0f; + if ( m_buttonAxe == EVENT_OBJECT_DOWN ) axeY = -1.0f; + if ( m_buttonAxe == EVENT_OBJECT_GASUP ) axeZ = 1.0f; + if ( m_buttonAxe == EVENT_OBJECT_GASDOWN ) axeZ = -1.0f; + + if ( m_object->RetManual() ) // scribbler en mode manuel ? + { + if ( axeX != 0.0f ) axeY = 0.0f; // si tourne -> n'avance pas ! + axeX *= 0.5f; + axeY *= 0.5f; + } + + if ( (g_researchDone&RESEARCH_FLY) == 0 ) + { + axeZ = -1.0f; // tombe + } + + axeX += m_camera->RetMotorTurn(); // force additionnelle selon caméra + if ( axeX > 1.0f ) axeX = 1.0f; + if ( axeX < -1.0f ) axeX = -1.0f; + + m_physics->SetMotorSpeedX(axeY); // avancer/reculer + m_physics->SetMotorSpeedY(axeZ); // monter/descendre + m_physics->SetMotorSpeedZ(axeX); // tourner + + if ( action == EVENT_OBJECT_PROGLIST ) + { + m_selScript = RetSelScript(); + UpdateInterface(); + } + + if ( action == EVENT_OBJECT_PROGEDIT ) + { + StartEditScript(m_selScript, m_main->RetScriptName()); + } + + if ( action == EVENT_OBJECT_PROGRUN ) + { + StopProgram(); // stoppe programme en cours + RunProgram(m_selScript); + UpdateInterface(); + } + + err = ERR_OK; + + if ( m_program == -1 ) + { + if ( action == EVENT_OBJECT_HTAKE ) + { + err = StartTaskTake(); + } + + if ( action == EVENT_OBJECT_MFRONT || + action == EVENT_OBJECT_MBACK || + action == EVENT_OBJECT_MPOWER ) + { + m_manipStyle = action; + UpdateInterface(); + } + + if ( action == EVENT_OBJECT_MTAKE ) + { + if ( m_manipStyle == EVENT_OBJECT_MFRONT ) + { + err = StartTaskManip(TMO_AUTO, TMA_FFRONT); + } + if ( m_manipStyle == EVENT_OBJECT_MBACK ) + { + err = StartTaskManip(TMO_AUTO, TMA_FBACK); + if ( err == ERR_OK ) + { + m_manipStyle = EVENT_OBJECT_MFRONT; + UpdateInterface(); + } + } + if ( m_manipStyle == EVENT_OBJECT_MPOWER ) + { + err = StartTaskManip(TMO_AUTO, TMA_POWER); + if ( err == ERR_OK ) + { + m_manipStyle = EVENT_OBJECT_MFRONT; + UpdateInterface(); + } + } + } + + if ( action == EVENT_OBJECT_BDERRICK ) + { + err = StartTaskBuild(OBJECT_DERRICK); + } + if ( action == EVENT_OBJECT_BSTATION ) + { + err = StartTaskBuild(OBJECT_STATION); + } + if ( action == EVENT_OBJECT_BFACTORY ) + { + err = StartTaskBuild(OBJECT_FACTORY); + } + if ( action == EVENT_OBJECT_BREPAIR ) + { + err = StartTaskBuild(OBJECT_REPAIR); + } + if ( action == EVENT_OBJECT_BCONVERT ) + { + err = StartTaskBuild(OBJECT_CONVERT); + } + if ( action == EVENT_OBJECT_BTOWER ) + { + err = StartTaskBuild(OBJECT_TOWER); + } + if ( action == EVENT_OBJECT_BRESEARCH ) + { + err = StartTaskBuild(OBJECT_RESEARCH); + } + if ( action == EVENT_OBJECT_BRADAR ) + { + err = StartTaskBuild(OBJECT_RADAR); + } + if ( action == EVENT_OBJECT_BENERGY ) + { + err = StartTaskBuild(OBJECT_ENERGY); + } + if ( action == EVENT_OBJECT_BLABO ) + { + err = StartTaskBuild(OBJECT_LABO); + } + if ( action == EVENT_OBJECT_BNUCLEAR ) + { + err = StartTaskBuild(OBJECT_NUCLEAR); + } + if ( action == EVENT_OBJECT_BPARA ) + { + err = StartTaskBuild(OBJECT_PARA); + } + if ( action == EVENT_OBJECT_BINFO ) + { + err = StartTaskBuild(OBJECT_INFO); + } + + if ( action == EVENT_OBJECT_GFLAT ) + { + GroundFlat(); + } + if ( action == EVENT_OBJECT_FCREATE ) + { + err = StartTaskFlag(TFL_CREATE, m_flagColor); + } + if ( action == EVENT_OBJECT_FDELETE ) + { + err = StartTaskFlag(TFL_DELETE, m_flagColor); + } + if ( action == EVENT_OBJECT_FCOLORb || + action == EVENT_OBJECT_FCOLORr || + action == EVENT_OBJECT_FCOLORg || + action == EVENT_OBJECT_FCOLORy || + action == EVENT_OBJECT_FCOLORv ) + { + ColorFlag(action-EVENT_OBJECT_FCOLORb); + } + + if ( action == EVENT_OBJECT_SEARCH ) + { + err = StartTaskSearch(); + } + + if ( action == EVENT_OBJECT_TERRAFORM ) + { + err = StartTaskTerraform(); + } + + if ( action == EVENT_OBJECT_RECOVER ) + { + err = StartTaskRecover(); + } + + if ( action == EVENT_OBJECT_BEGSHIELD ) + { + err = StartTaskShield(TSM_UP); + } + + if ( action == EVENT_OBJECT_DIMSHIELD ) + { + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw != 0 ) + { + ps = (CSlider*)pw->SearchControl(EVENT_OBJECT_DIMSHIELD); + if ( ps != 0 ) + { + m_object->SetParam((ps->RetVisibleValue()-(RADIUS_SHIELD_MIN/g_unit))/((RADIUS_SHIELD_MAX-RADIUS_SHIELD_MIN)/g_unit)); + } + } + } + + if ( action == EVENT_OBJECT_FIRE && m_primaryTask == 0 && !m_object->RetTrainer()) + { + if ( m_camera->RetType() != CAMERA_ONBOARD ) + { + m_camera->SetType(CAMERA_ONBOARD); + } + err = StartTaskFire(0.0f); + } + if ( action == EVENT_OBJECT_TARGET && !m_object->RetTrainer() ) + { + err = StartTaskGunGoal((event.pos.y-0.50f)*1.3f, (event.pos.x-0.50f)*2.0f); + } + + if ( action == EVENT_OBJECT_FIREANT ) + { +//? err = StartTaskFireAnt(); + } + + if ( action == EVENT_OBJECT_PEN0 ) // up + { + err = StartTaskPen(FALSE, m_object->RetTraceColor()); + m_object->SetTraceDown(FALSE); + } + if ( action == EVENT_OBJECT_PEN1 ) // noir + { + err = StartTaskPen(TRUE, 1); + m_object->SetTraceDown(TRUE); + m_object->SetTraceColor(1); + } + if ( action == EVENT_OBJECT_PEN2 ) // jaune + { + err = StartTaskPen(TRUE, 8); + m_object->SetTraceDown(TRUE); + m_object->SetTraceColor(8); + } + if ( action == EVENT_OBJECT_PEN3 ) // orange + { + err = StartTaskPen(TRUE, 7); + m_object->SetTraceDown(TRUE); + m_object->SetTraceColor(7); + } + if ( action == EVENT_OBJECT_PEN4 ) // rouge + { + err = StartTaskPen(TRUE, 4); + m_object->SetTraceDown(TRUE); + m_object->SetTraceColor(4); + } + if ( action == EVENT_OBJECT_PEN5 ) // violet + { + err = StartTaskPen(TRUE, 6); + m_object->SetTraceDown(TRUE); + m_object->SetTraceColor(6); + } + if ( action == EVENT_OBJECT_PEN6 ) // bleu + { + err = StartTaskPen(TRUE, 14); + m_object->SetTraceDown(TRUE); + m_object->SetTraceColor(14); + } + if ( action == EVENT_OBJECT_PEN7 ) // vert + { + err = StartTaskPen(TRUE, 12); + m_object->SetTraceDown(TRUE); + m_object->SetTraceColor(12); + } + if ( action == EVENT_OBJECT_PEN8 ) // brun + { + err = StartTaskPen(TRUE, 10); + m_object->SetTraceDown(TRUE); + m_object->SetTraceColor(10); + } + + if ( action == EVENT_OBJECT_REC ) // enregistre ? + { + if ( m_bTraceRecord ) + { + m_bTraceRecord = FALSE; + TraceRecordStop(); + } + else + { + m_bTraceRecord = TRUE; + TraceRecordStart(); + } + UpdateInterface(); + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw != 0 ) + { + UpdateScript(pw); + } + } + if ( action == EVENT_OBJECT_STOP ) // stoppe ? + { + if ( m_bTraceRecord ) + { + m_bTraceRecord = FALSE; + TraceRecordStop(); + } + UpdateInterface(); + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw != 0 ) + { + UpdateScript(pw); + } + } + + if ( action == EVENT_OBJECT_RESET ) + { + m_main->ResetObject(); // reset tous les objets + UpdateInterface(); + } + +#if 0 + if ( event.param == 'T' ) + { + D3DVECTOR p1, p2; + float h; + p1 = m_object->RetPosition(0); + h = m_terrain->RetFloorLevel(p1); + p2 = p1; + p1.x -= 20.0f; + p1.z -= 20.0f; + p2.x += 20.0f; + p2.z += 20.0f; + m_terrain->Terraform(p1, p2, h+1.0f); + } + if ( event.param == 'R' ) + { + D3DVECTOR p1, p2; + float h; + p1 = m_object->RetPosition(0); + h = m_terrain->RetFloorLevel(p1); + p2 = p1; + p1.x -= 20.0f; + p1.z -= 20.0f; + p2.x += 20.0f; + p2.z += 20.0f; + m_terrain->Terraform(p1, p2, h-1.0f); + } +#endif + } + + if ( err != ERR_OK ) + { + m_displayText->DisplayError(err, m_object); + } + + return TRUE; +} + + +// Fait évoluer le cerveau selon le temps écoulé. + +BOOL CBrain::EventFrame(const Event &event) +{ + m_time += event.rTime; + if ( m_bBurn ) m_burnTime += event.rTime; + + if ( m_soundChannelAlarm != -1 ) + { + m_sound->Position(m_soundChannelAlarm, m_object->RetPosition(0)); + } + + if ( m_studio != 0 ) // édition en cours ? + { + m_studio->EventProcess(event); + } + + UpdateInterface(event.rTime); + + if ( m_engine->RetPause() ) return TRUE; + if ( !m_bActivity ) return TRUE; // attend si inactif + if ( EndedTask() == ERR_CONTINUE ) return TRUE; // attend si pas fini ... + + if ( m_program != -1 ) // programme en cours ? + { + if ( m_script[m_program]->Continue(event) ) + { + StopProgram(); + } + } + + if ( m_bTraceRecord ) // enregistrement du dessin en cours ? + { + TraceRecordFrame(); + } + + return TRUE; +} + + +// Stoppe le programme en cours. + +void CBrain::StopProgram() +{ + StopTask(); + + if ( m_object->RetType() == OBJECT_HUMAN || + m_object->RetType() == OBJECT_TECH ) return; + + if ( m_program != -1 && + m_script[m_program] != 0 ) + { + m_script[m_program]->Stop(); + } + + BlinkScript(FALSE); // ne clignotte plus + + m_program = -1; + + m_physics->SetMotorSpeedX(0.0f); + m_physics->SetMotorSpeedY(0.0f); + m_physics->SetMotorSpeedZ(0.0f); + + m_motion->SetAction(-1); + + UpdateInterface(); + m_main->UpdateShortcuts(); + m_object->CreateSelectParticule(); +} + +// Stoppe la tâche en cours. + +void CBrain::StopTask() +{ + if ( m_primaryTask != 0 ) + { + m_primaryTask->Abort(); + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } +} + + +// Introduit un virus dans un programme. +// Retourne TRUE s'il a été introduit. + +BOOL CBrain::IntroduceVirus() +{ + int i, j; + + for ( i=0 ; i<50 ; i++ ) + { + j = rand()%BRAINMAXSCRIPT; + if ( m_script[j] != 0 ) + { + if ( m_script[j]->IntroduceVirus() ) // essaye d'introduire + { + m_bActiveVirus = TRUE; // virus actif + return TRUE; + } + } + } + return FALSE; +} + +// ActiveVirus indique que l'objet est contaminé. Contrairement aux +// chtites lettres qui disparaissent automatiquement après un certain +// temps, ActiveVirus ne disparaît qu'après avoir édité le programme +// (même si le virus n'est pas corrigé). + +void CBrain::SetActiveVirus(BOOL bActive) +{ + m_bActiveVirus = bActive; + + if ( !m_bActiveVirus ) // virus désactivé ? + { + m_object->SetVirusMode(FALSE); // chtites lettres aussi + } +} + +BOOL CBrain::RetActiveVirus() +{ + return m_bActiveVirus; +} + + +// Début de l'édition d'un programme. + +void CBrain::StartEditScript(int rank, char* name) +{ + CreateInterface(FALSE); // supprime les boutons de commande + + if ( m_script[rank] == 0 ) + { + m_script[rank] = new CScript(m_iMan, m_object, &m_secondaryTask); + } + + m_studio = new CStudio(m_iMan); + m_studio->StartEditScript(m_script[rank], name, rank); +} + +// Fin de l'édition d'un programme. + +void CBrain::StopEditScript(BOOL bCancel) +{ + if ( !bCancel ) SetActiveVirus(FALSE); + + if ( !m_studio->StopEditScript(bCancel) ) return; + + delete m_studio; + m_studio = 0; + + CreateInterface(TRUE); // remet les boutons de commande +} + + + +// Bouge le bras manipulateur. + +Error CBrain::StartTaskTake() +{ + Error err; + + if ( m_primaryTask != 0 ) + { + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } + + m_primaryTask = new CTaskManager(m_iMan, m_object); + err = m_primaryTask->StartTaskTake(); + UpdateInterface(); + return err; +} + +// Bouge le bras manipulateur. + +Error CBrain::StartTaskManip(TaskManipOrder order, TaskManipArm arm) +{ + Error err; + + if ( m_primaryTask != 0 ) + { + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } + + m_primaryTask = new CTaskManager(m_iMan, m_object); + err = m_primaryTask->StartTaskManip(order, arm); + UpdateInterface(); + return err; +} + +// Met ou enlève un drapeau. + +Error CBrain::StartTaskFlag(TaskFlagOrder order, int rank) +{ + Error err; + + if ( m_primaryTask != 0 ) + { + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } + + m_primaryTask = new CTaskManager(m_iMan, m_object); + err = m_primaryTask->StartTaskFlag(order, rank); + UpdateInterface(); + return err; +} + +// Construit un batiment. + +Error CBrain::StartTaskBuild(ObjectType type) +{ + Error err; + + if ( m_primaryTask != 0 ) + { + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } + + m_primaryTask = new CTaskManager(m_iMan, m_object); + err = m_primaryTask->StartTaskBuild(type); + UpdateInterface(); + return err; +} + +// Sonde le sol. + +Error CBrain::StartTaskSearch() +{ + Error err; + + if ( m_primaryTask != 0 ) + { + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } + + m_primaryTask = new CTaskManager(m_iMan, m_object); + err = m_primaryTask->StartTaskSearch(); + UpdateInterface(); + return err; +} + +// Terraforme le sol. + +Error CBrain::StartTaskTerraform() +{ + Error err; + + if ( m_primaryTask != 0 ) + { + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } + + m_primaryTask = new CTaskManager(m_iMan, m_object); + err = m_primaryTask->StartTaskTerraform(); + UpdateInterface(); + return err; +} + +// Change de crayon. + +Error CBrain::StartTaskPen(BOOL bDown, int color) +{ + Error err; + + m_physics->SetMotorSpeedX(0.0f); + m_physics->SetMotorSpeedY(0.0f); + m_physics->SetMotorSpeedZ(0.0f); + + if ( m_primaryTask != 0 ) + { + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } + + m_primaryTask = new CTaskManager(m_iMan, m_object); + err = m_primaryTask->StartTaskPen(bDown, color); + UpdateInterface(); + return err; +} + +// Récupère une ruine. + +Error CBrain::StartTaskRecover() +{ + Error err; + + if ( m_primaryTask != 0 ) + { + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } + + m_primaryTask = new CTaskManager(m_iMan, m_object); + err = m_primaryTask->StartTaskRecover(); + UpdateInterface(); + return err; +} + +// Déploie le bouclier. + +Error CBrain::StartTaskShield(TaskShieldMode mode) +{ + Error err; + + if ( m_secondaryTask != 0 ) + { + delete m_secondaryTask; // stoppe la tâche en cours + m_secondaryTask = 0; + } + + m_secondaryTask = new CTaskManager(m_iMan, m_object); + err = m_secondaryTask->StartTaskShield(mode, 1000.0f); + UpdateInterface(); + return err; +} + +// Tire. + +Error CBrain::StartTaskFire(float delay) +{ + Error err; + + if ( m_primaryTask != 0 ) + { + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } + + m_primaryTask = new CTaskManager(m_iMan, m_object); + err = m_primaryTask->StartTaskFire(delay); + UpdateInterface(); + return err; +} + +// Tire avec la fourmi. + +Error CBrain::StartTaskFireAnt(D3DVECTOR impact) +{ + Error err; + + if ( m_primaryTask != 0 ) + { + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } + + m_primaryTask = new CTaskManager(m_iMan, m_object); + err = m_primaryTask->StartTaskFireAnt(impact); + UpdateInterface(); + return err; +} + +// Ajuste la hausse. + +Error CBrain::StartTaskGunGoal(float dirV, float dirH) +{ + Error err; + + if ( m_secondaryTask != 0 ) + { + delete m_secondaryTask; // stoppe la tâche en cours + m_secondaryTask = 0; + } + + m_secondaryTask = new CTaskManager(m_iMan, m_object); + err = m_secondaryTask->StartTaskGunGoal(dirV, dirH); + UpdateInterface(); + return err; +} + +// Reset. + +Error CBrain::StartTaskReset(D3DVECTOR goal, D3DVECTOR angle) +{ + Error err; + + if ( m_primaryTask != 0 ) + { + delete m_primaryTask; // stoppe la tâche en cours + m_primaryTask = 0; + } + + m_primaryTask = new CTaskManager(m_iMan, m_object); + err = m_primaryTask->StartTaskReset(goal, angle); + UpdateInterface(); + return err; +} + +// Termine la tâche lorsque le moment est venu. + +Error CBrain::EndedTask() +{ + Error err; + + if ( m_secondaryTask != 0 ) // tâche en cours ? + { + err = m_secondaryTask->IsEnded(); + if ( err != ERR_CONTINUE ) // tâche terminée ? + { + delete m_secondaryTask; + m_secondaryTask = 0; + UpdateInterface(); + } + } + + if ( m_primaryTask != 0 ) // tâche en cours ? + { + err = m_primaryTask->IsEnded(); + if ( err != ERR_CONTINUE ) // tâche terminée ? + { + delete m_primaryTask; + m_primaryTask = 0; + UpdateInterface(); + } + return err; + } + return ERR_STOP; +} + + + +// Montre les zones plates dans le terrain. + +void CBrain::GroundFlat() +{ + D3DVECTOR pos, speed; + FPOINT dim; + Error err; + float level; + + if ( !m_physics->RetLand() ) + { + err = ERR_FLAG_FLY; + pos = m_object->RetPosition(0); + if ( pos.y < m_water->RetLevel() ) err = ERR_FLAG_WATER; + m_displayText->DisplayError(err, m_object); + return; + } + + pos = m_object->RetPosition(0); + m_terrain->GroundFlat(pos); + m_sound->Play(SOUND_GFLAT, pos); + + level = m_terrain->RetFloorLevel(pos)+2.0f; + if ( pos.y < level ) pos.y = level; // pas en-dessous du sol + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 40.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGFLAT, 1.0f); +} + + +// Choix de la couleur pour un indicateur de couleur. + +void CBrain::ColorFlag(int color) +{ + m_flagColor = color; + UpdateInterface(); +} + + +// Crée toute l'interface lorsque l'objet est sélectionné. + +BOOL CBrain::CreateInterface(BOOL bSelect) +{ + ObjectType type; + CWindow* pw; + CButton* pb; + CColor* pc; + CSlider* ps; + CTarget* pt; + CLabel* pl; + FPOINT pos, dim, ddim; + float ox, oy, sx, sy; + char name[100]; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw != 0 ) + { + pw->Flush(); // détruit les boutons de la fenêtre + m_interface->DeleteControl(EVENT_WINDOW0); // détruit la fenêtre + } + m_defaultEnter = EVENT_NULL; + + if ( !bSelect ) return TRUE; + + pos.x = 0.0f; + pos.y = 0.0f; + dim.x = 540.0f/640.0f; + if ( !m_main->RetShowMap() ) dim.x = 640.0f/640.0f; + dim.y = 86.0f/480.0f; + m_interface->CreateWindows(pos, dim, 3, EVENT_WINDOW0); + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return FALSE; + + m_object->GetTooltipName(name); + pos.x = 0.0f; + pos.y = 64.0f/480.0f; + ddim.x = 540.0f/640.0f; + if ( !m_main->RetShowMap() ) ddim.x = 640.0f/640.0f; + ddim.y = 16.0f/480.0f; + pw->CreateLabel(pos, ddim, 0, EVENT_LABEL0, name); + + dim.x = 33.0f/640.0f; + dim.y = 33.0f/480.0f; + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = 33.0f/640.0f; + sy = 33.0f/480.0f; + + type = m_object->RetType(); + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEtg || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr || + type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM ) // véhicule ? + { + ddim.x = dim.x*5.1f; + ddim.y = dim.y*2.0f; + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0.0f; + pw->CreateList(pos, ddim, -1, EVENT_OBJECT_PROGLIST, 1.10f); + UpdateScript(pw); + + pos.x = ox+sx*5.2f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, dim, 8, EVENT_OBJECT_PROGRUN); + pos.y = oy+sy*0.0f; + pw->CreateButton(pos, dim, 22, EVENT_OBJECT_PROGEDIT); + } + + if ( type == OBJECT_HUMAN || + type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEft || + type == OBJECT_BEE ) // volant ? + { + pos.x = ox+sx*6.4f; + pos.y = oy+sy*0; + pb = pw->CreateButton(pos, dim, 29, EVENT_OBJECT_GASDOWN); + pb->SetImmediat(TRUE); + + pos.x = ox+sx*6.4f; + pos.y = oy+sy*1; + pb = pw->CreateButton(pos, dim, 28, EVENT_OBJECT_GASUP); + pb->SetImmediat(TRUE); + + if ( type != OBJECT_HUMAN || + m_object->RetOption() != 2 ) + { + pos.x = ox+sx*15.3f; + pos.y = oy+sy*0; + ddim.x = 14.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGauge(pos, ddim, 2, EVENT_OBJECT_GRANGE); + } + } + + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH ) + { + pos.x = ox+sx*7.7f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 31, EVENT_OBJECT_HTAKE); + DefaultEnter(pw, EVENT_OBJECT_HTAKE); + } + + if ( (type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia ) && // bras ? + !m_object->RetTrainer() ) + { + pos.x = ox+sx*7.7f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 32, EVENT_OBJECT_MTAKE); + DefaultEnter(pw, EVENT_OBJECT_MTAKE); + + pos.x = ox+sx*8.9f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 34, EVENT_OBJECT_MBACK); + + pos.x = ox+sx*9.9f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 35, EVENT_OBJECT_MPOWER); + + pos.x = ox+sx*10.9f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 33, EVENT_OBJECT_MFRONT); + } + + if ( type == OBJECT_MOBILEsa && // sous-marin ? + !m_object->RetTrainer() ) + { + pos.x = ox+sx*7.7f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 32, EVENT_OBJECT_MTAKE); + DefaultEnter(pw, EVENT_OBJECT_MTAKE); + } + + if ( type == OBJECT_HUMAN ) // constructeur ? + { + pos.x = 1.0f/640.0f; + pos.y = 4.0f/480.0f; + ddim.x = 212.0f/640.0f; + ddim.y = 64.0f/480.0f; + pw->CreateGroup(pos, ddim, 27, EVENT_NULL); + + ddim.x = dim.x*0.9f; + ddim.y = dim.y*0.9f; + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, ddim, 128+35, EVENT_OBJECT_BRESEARCH); + DeadInterface(pw, EVENT_OBJECT_BRESEARCH, g_build&BUILD_RESEARCH); + + pos.x = ox+sx*0.9f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, ddim, 128+32, EVENT_OBJECT_BFACTORY); + DeadInterface(pw, EVENT_OBJECT_BFACTORY, g_build&BUILD_FACTORY); + + pos.x = ox+sx*1.8f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, ddim, 128+34, EVENT_OBJECT_BCONVERT); + DeadInterface(pw, EVENT_OBJECT_BCONVERT, g_build&BUILD_CONVERT); + + pos.x = ox+sx*2.7f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, ddim, 128+36, EVENT_OBJECT_BSTATION); + DeadInterface(pw, EVENT_OBJECT_BSTATION, g_build&BUILD_STATION); + + pos.x = ox+sx*3.6f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, ddim, 128+40, EVENT_OBJECT_BRADAR); + DeadInterface(pw, EVENT_OBJECT_BRADAR, g_build&BUILD_RADAR); + + pos.x = ox+sx*4.5f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, ddim, 128+41, EVENT_OBJECT_BREPAIR); + DeadInterface(pw, EVENT_OBJECT_BREPAIR, g_build&BUILD_REPAIR); + + pos.x = ox+sx*5.4f; + pos.y = oy+sy*1.0f; + pw->CreateButton(pos, ddim, 128+44, EVENT_OBJECT_BINFO); + DeadInterface(pw, EVENT_OBJECT_BINFO, g_build&BUILD_INFO); + + pos.x = ox+sx*0.0f; + pos.y = oy+sy*0.1f; + pw->CreateButton(pos, ddim, 128+37, EVENT_OBJECT_BTOWER); + DeadInterface(pw, EVENT_OBJECT_BTOWER, + (g_build&BUILD_TOWER) && + (g_researchDone & RESEARCH_TOWER)); + + pos.x = ox+sx*0.9f; + pos.y = oy+sy*0.1f; + pw->CreateButton(pos, ddim, 128+39, EVENT_OBJECT_BENERGY); + DeadInterface(pw, EVENT_OBJECT_BENERGY, g_build&BUILD_ENERGY); + + pos.x = ox+sx*1.8f; + pos.y = oy+sy*0.1f; + pw->CreateButton(pos, ddim, 128+33, EVENT_OBJECT_BDERRICK); + DeadInterface(pw, EVENT_OBJECT_BDERRICK, g_build&BUILD_DERRICK); + + pos.x = ox+sx*2.7f; + pos.y = oy+sy*0.1f; + pw->CreateButton(pos, ddim, 128+42, EVENT_OBJECT_BNUCLEAR); + DeadInterface(pw, EVENT_OBJECT_BNUCLEAR, + (g_build&BUILD_NUCLEAR) && + (g_researchDone & RESEARCH_ATOMIC)); + + pos.x = ox+sx*3.6f; + pos.y = oy+sy*0.1f; + pw->CreateButton(pos, ddim, 128+38, EVENT_OBJECT_BLABO); + DeadInterface(pw, EVENT_OBJECT_BLABO, g_build&BUILD_LABO); + + pos.x = ox+sx*4.5f; + pos.y = oy+sy*0.1f; + pw->CreateButton(pos, ddim, 128+46, EVENT_OBJECT_BPARA); + DeadInterface(pw, EVENT_OBJECT_BPARA, g_build&BUILD_PARA); + + pos.x = ox+sx*5.4f; + pos.y = oy+sy*0.1f; + pw->CreateButton(pos, ddim, 128+56, EVENT_OBJECT_BXXXX); + DeadInterface(pw, EVENT_OBJECT_BXXXX, FALSE); + + if ( g_build&BUILD_GFLAT ) + { + pos.x = ox+sx*9.0f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 64+47, EVENT_OBJECT_GFLAT); + } + + if ( g_build&BUILD_FLAG ) + { + pos.x = ox+sx*10.1f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 64+54, EVENT_OBJECT_FCREATE); + + pos.x = ox+sx*11.1f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 64+55, EVENT_OBJECT_FDELETE); + + ddim.x = dim.x*0.4f; + ddim.y = dim.y*0.4f; + pos.x = ox+sx*10.1f; + pos.y = oy+sy*2.0f-ddim.y; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_FCOLORb); + pc->SetColor(RetColor((D3DCOLOR)0x004890ff)); + pos.x += ddim.x; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_FCOLORr); + pc->SetColor(RetColor((D3DCOLOR)0x00ff0000)); + pos.x += ddim.x; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_FCOLORg); + pc->SetColor(RetColor((D3DCOLOR)0x0000ce00)); + pos.x += ddim.x; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_FCOLORy); + pc->SetColor(RetColor((D3DCOLOR)0x00ffec00)); + pos.x += ddim.x; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_FCOLORv); + pc->SetColor(RetColor((D3DCOLOR)0x00d101fe)); + } + } + + if ( (type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis ) && // chercheur ? + !m_object->RetTrainer() ) + { + pos.x = ox+sx*7.7f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 40, EVENT_OBJECT_SEARCH); + DefaultEnter(pw, EVENT_OBJECT_SEARCH); + } + + if ( type == OBJECT_MOBILErt && // terraformeur ? + !m_object->RetTrainer() ) + { + pos.x = ox+sx*7.7f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 128+18, EVENT_OBJECT_TERRAFORM); + DefaultEnter(pw, EVENT_OBJECT_TERRAFORM); + + pos.x = ox+sx*10.2f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 41, EVENT_OBJECT_LIMIT); + } + + if ( type == OBJECT_MOBILErr && // récupérateur ? + !m_object->RetTrainer() ) + { + pos.x = ox+sx*7.7f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 128+20, EVENT_OBJECT_RECOVER); + DefaultEnter(pw, EVENT_OBJECT_RECOVER); + } + + if ( type == OBJECT_MOBILErs && // bouclier ? + !m_object->RetTrainer() ) + { + pos.x = ox+sx*7.7f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 39, EVENT_OBJECT_BEGSHIELD); + DefaultEnter(pw, EVENT_OBJECT_BEGSHIELD); + + pos.x = ox+sx*9.0f; + pos.y = oy+sy*0.5f; + pw->CreateButton(pos, dim, 47, EVENT_OBJECT_ENDSHIELD); + +//? pos.x = ox+sx*10.2f; +//? pos.y = oy+sy*0.5f; +//? pw->CreateButton(pos, dim, 41, EVENT_OBJECT_LIMIT); + + pos.x = ox+sx*10.5f; + pos.y = oy+sy*0.0f; + ddim.x = dim.x*0.5f; + ddim.y = dim.y*2.0f; + ps = pw->CreateSlider(pos, ddim, 0, EVENT_OBJECT_DIMSHIELD); + ps->SetState(STATE_VALUE); + ps->SetLimit((RADIUS_SHIELD_MIN/g_unit), (RADIUS_SHIELD_MAX/g_unit)); + ps->SetArrowStep(1.0f); + } + + if ( (type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILErc ) && // canon ? + !m_object->RetTrainer() ) + { + pos.x = ox+sx*7.7f; + pos.y = oy+sy*0.5f; + pb = pw->CreateButton(pos, dim, 42, EVENT_OBJECT_FIRE); + pb->SetImmediat(TRUE); + DefaultEnter(pw, EVENT_OBJECT_FIRE); + +//? pos.x = ox+sx*10.2f; +//? pos.y = oy+sy*0.5f; +//? pw->CreateButton(pos, dim, 41, EVENT_OBJECT_LIMIT); + } + + if ( type == OBJECT_MOBILEdr && + m_object->RetManual() ) // scribbler en mode manuel ? + { + pos.x = ox+sx*6.9f; + pos.y = oy+sy*0.0f; + ddim.x = dim.x*2.2f; + ddim.y = dim.y*2.0f; + pw->CreateGroup(pos, ddim, 20, EVENT_NULL); // fond bleu uni + + pos.x = ox+sx*9.3f; + pos.y = oy+sy*0.0f; + ddim.x = dim.x*2.2f; + ddim.y = dim.y*2.0f; + pw->CreateGroup(pos, ddim, 20, EVENT_NULL); // fond bleu uni + + pos.x = ox+sx*9.90f; + pos.y = oy+sy*0.50f; + pw->CreateButton(pos, dim, 43, EVENT_OBJECT_PEN0); + + ddim.x = dim.x*0.5f; + ddim.y = dim.y*0.5f; + pos.x = ox+sx*10.15f; + pos.y = oy+sy*1.50f; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_PEN1); // noir + pc->SetColor(RetColor((D3DCOLOR)0x00000000)); + pos.x = ox+sx*10.65f; + pos.y = oy+sy*1.25f; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_PEN2); // jaune + pc->SetColor(RetColor((D3DCOLOR)0x00ffff00)); + pos.x = ox+sx*10.90f; + pos.y = oy+sy*0.75f; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_PEN3); // orange + pc->SetColor(RetColor((D3DCOLOR)0x00ff8800)); + pos.x = ox+sx*10.65f; + pos.y = oy+sy*0.25f; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_PEN4); // rouge + pc->SetColor(RetColor((D3DCOLOR)0x00ff0000)); + pos.x = ox+sx*10.15f; + pos.y = oy+sy*0.00f; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_PEN5); // violet + pc->SetColor(RetColor((D3DCOLOR)0x00ff00ff)); + pos.x = ox+sx*9.65f; + pos.y = oy+sy*0.25f; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_PEN6); // bleu + pc->SetColor(RetColor((D3DCOLOR)0x000066ff)); + pos.x = ox+sx*9.40f; + pos.y = oy+sy*0.75f; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_PEN7); // vert + pc->SetColor(RetColor((D3DCOLOR)0x0000cc00)); + pos.x = ox+sx*9.65f; + pos.y = oy+sy*1.25f; + pc = pw->CreateColor(pos, ddim, -1, EVENT_OBJECT_PEN8); // brun + pc->SetColor(RetColor((D3DCOLOR)0x00884400)); + + pos.x = ox+sx*6.9f; + pos.y = oy+sy*1.2f; + ddim.x = dim.x*2.2f; + ddim.y = dim.y*0.4f; + GetResource(RES_TEXT, RT_INTERFACE_REC, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL1, name); + pl->SetFontSize(9.0f); + + pos.x = ox+sx*7.0f; + pos.y = oy+sy*0.3f; + pw->CreateButton(pos, dim, 44, EVENT_OBJECT_REC); + pos.x = ox+sx*8.0f; + pos.y = oy+sy*0.3f; + pw->CreateButton(pos, dim, 45, EVENT_OBJECT_STOP); + } + + if ( m_object->RetToy() ) + { + pos.x = ox+sx*12.1f; + pos.y = oy+sy*-0.1f; + ddim.x = dim.x*1.2f; + ddim.y = dim.y*2.1f; + pw->CreateGroup(pos, ddim, 20, EVENT_NULL); // fond bleu uni + + pos.x = ox+sx*12.2f; + pos.y = oy+sy*1; + pw->CreateGroup(pos, dim, 19, EVENT_NULL); // signe SatCom + + pos.x = ox+sx*12.2f; + pos.y = oy+sy*0.0f; + pw->CreateButton(pos, dim, 128+57, EVENT_OBJECT_BHELP); + } + else + { + pos.x = ox+sx*12.3f; + pos.y = oy+sy*-0.1f; + ddim.x = dim.x*1.0f; + ddim.y = dim.y*2.1f; + pw->CreateGroup(pos, ddim, 20, EVENT_NULL); // fond bleu uni + + pos.x = ox+sx*12.3f; + pos.y = oy+sy*1; + pw->CreateGroup(pos, dim, 19, EVENT_NULL); // signe SatCom + + pos.x = ox+sx*12.4f; + pos.y = oy+sy*0.5f; + ddim.x = dim.x*0.8f; + ddim.y = dim.y*0.5f; + pw->CreateButton(pos, ddim, 18, EVENT_OBJECT_BHELP); + pos.y = oy+sy*0.0f; + pw->CreateButton(pos, ddim, 19, EVENT_OBJECT_HELP); + } + + if ( type != OBJECT_HUMAN && + type != OBJECT_TECH && + !m_object->RetCameraLock() ) + { +//? if ( m_main->RetShowMap() ) + if ( TRUE ) + { + pos.x = ox+sx*13.4f; + pos.y = oy+sy*1; + pw->CreateButton(pos, dim, 13, EVENT_OBJECT_CAMERA); + } + else + { + ddim.x = dim.x*0.66f; + ddim.y = dim.y*0.66f; + pos.x = ox+sx*(17.0f+0.66f); + pos.y = oy+sy*0.66f; + pw->CreateButton(pos, ddim, 13, EVENT_OBJECT_CAMERA); + } + } + + if ( m_object->RetToy() && !m_object->RetManual() ) + { +#if 0 + ddim.x = dim.x*0.66f; + ddim.y = dim.y*0.66f; + pos.x = ox+sx*10.0f; + pos.y = oy+sy*0.66f; + pb = pw->CreateButton(pos, ddim, 55, EVENT_OBJECT_CAMERAleft); + pb->SetImmediat(TRUE); + pos.x = ox+sx*(10.0f+0.66f*2.0f); + pos.y = oy+sy*0.66f; + pb = pw->CreateButton(pos, ddim, 48, EVENT_OBJECT_CAMERAright); + pb->SetImmediat(TRUE); + pos.x = ox+sx*(10.0f+0.66f); + pos.y = oy+sy*(0.66f*2.0f); + pb = pw->CreateButton(pos, ddim, 49, EVENT_OBJECT_CAMERAnear); + pb->SetImmediat(TRUE); + pos.x = ox+sx*(10.0f+0.66f); + pos.y = oy+sy*0.0f; + pb = pw->CreateButton(pos, ddim, 50, EVENT_OBJECT_CAMERAaway); + pb->SetImmediat(TRUE); +#else + pos.x = ox+sx*9.0f; + pos.y = oy+sy*0; + pb = pw->CreateButton(pos, dim, 55, EVENT_OBJECT_CAMERAleft); + pb->SetImmediat(TRUE); + pos.x = ox+sx*11.0f; + pos.y = oy+sy*0; + pb = pw->CreateButton(pos, dim, 48, EVENT_OBJECT_CAMERAright); + pb->SetImmediat(TRUE); + pos.x = ox+sx*10.0f; + pos.y = oy+sy*1; + pb = pw->CreateButton(pos, dim, 49, EVENT_OBJECT_CAMERAnear); + pb->SetImmediat(TRUE); + pos.x = ox+sx*10.0f; + pos.y = oy+sy*0; + pb = pw->CreateButton(pos, dim, 50, EVENT_OBJECT_CAMERAaway); + pb->SetImmediat(TRUE); +#endif + } + + pos.x = ox+sx*13.4f; + pos.y = oy+sy*0; +#if _TEEN + pw->CreateButton(pos, dim, 9, EVENT_OBJECT_RESET); +#else + if ( m_object->RetTrainer() ) // entraînement ? + { + pw->CreateButton(pos, dim, 9, EVENT_OBJECT_RESET); + } + else + { + pw->CreateButton(pos, dim, 10, EVENT_OBJECT_DESELECT); + } +#endif + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit ) // véhicule ? + { + pos.x = ox+sx*14.5f; + pos.y = oy+sy*0; + ddim.x = 14.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGauge(pos, ddim, 0, EVENT_OBJECT_GENERGY); + } + + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH || + type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEtg || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit ) // véhicule ? + { + pos.x = ox+sx*14.9f; + pos.y = oy+sy*0; + ddim.x = 14.0f/640.0f; + ddim.y = 66.0f/480.0f; + pw->CreateGauge(pos, ddim, 3, EVENT_OBJECT_GSHIELD); + } + +#if 0 + if ( FALSE ) + { + pos.x = 505.0f/640.0f; + pos.y = 3.0f/480.0f; + ddim.x = 33.0f/640.0f; + ddim.y = 33.0f/480.0f; + pw->CreateCompass(pos, ddim, 0, EVENT_OBJECT_COMPASS); + + pc = (CCompass*)pw->SearchControl(EVENT_OBJECT_COMPASS); + if ( pc != 0 ) + { + pc->SetState(STATE_VISIBLE, m_main->RetShowMap()); + } + } +#endif + + if ( type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILErc ) // canon ? + { + ddim.x = 64.0f/640.0f; + ddim.y = 64.0f/480.0f; + pos.x = 0.5f-ddim.x/2.0f; + pos.y = 0.5f-ddim.y/2.0f; + pw->CreateGroup(pos, ddim, 12, EVENT_OBJECT_CROSSHAIR); + + pos.x = 20.0f/640.0f; + pos.y = 100.0f/480.0f; + ddim.x = 600.0f/640.0f; + ddim.y = 340.0f/480.0f; + pt = pw->CreateTarget(pos, ddim, 0, EVENT_OBJECT_TARGET); + pt->ClearState(STATE_GLINT); + } + + ddim.x = 64.0f/640.0f; + ddim.y = 64.0f/480.0f; + pos.x = 30.0f/640.0f; + pos.y = 430.0f/480.0f-ddim.y; + pw->CreateGroup(pos, ddim, 13, EVENT_OBJECT_CORNERul); + + ddim.x = 64.0f/640.0f; + ddim.y = 64.0f/480.0f; + pos.x = 610.0f/640.0f-ddim.x; + pos.y = 430.0f/480.0f-ddim.y; + pw->CreateGroup(pos, ddim, 14, EVENT_OBJECT_CORNERur); + + ddim.x = 64.0f/640.0f; + ddim.y = 64.0f/480.0f; + pos.x = 30.0f/640.0f; + pos.y = 110.0f/480.0f; + pw->CreateGroup(pos, ddim, 15, EVENT_OBJECT_CORNERdl); + + ddim.x = 64.0f/640.0f; + ddim.y = 64.0f/480.0f; + pos.x = 610.0f/640.0f-ddim.x; + pos.y = 110.0f/480.0f; + pw->CreateGroup(pos, ddim, 16, EVENT_OBJECT_CORNERdr); + + UpdateInterface(); + m_lastUpdateTime = 0.0f; + UpdateInterface(0.0f); + + return TRUE; +} + +// Met à jour l'état de tous les boutons de l'interface, +// suite au temps qui s'écoule ... + +void CBrain::UpdateInterface(float rTime) +{ + CWindow* pw; +#if _TEEN + CButton* pb; +#endif + CGauge* pg; + CCompass* pc; + CGroup* pgr; + CTarget* ptg; + CObject* power; + D3DVECTOR pos, hPos; + FPOINT ppos; + float energy, limit, angle, range; + int icon; + BOOL bOnBoard; + + m_lastAlarmTime += rTime; + if ( m_time < m_lastUpdateTime+0.1f ) return; + m_lastUpdateTime = m_time; + + if ( !m_object->RetSelect() ) + { + if ( m_soundChannelAlarm != -1 ) + { + m_sound->FlushEnvelope(m_soundChannelAlarm); + m_sound->AddEnvelope(m_soundChannelAlarm, 0.0f, 1.0f, 0.1f, SOPER_STOP); + m_soundChannelAlarm = -1; + } + return; + } + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + pg = (CGauge*)pw->SearchControl(EVENT_OBJECT_GENERGY); + if ( pg != 0 ) + { + power = m_object->RetPower(); + if ( power == 0 ) + { + energy = 0.0f; + } + else + { + energy = power->RetEnergy(); + limit = energy*power->RetCapacity(); + } + icon = 0; // rouge/vert + + if ( limit < 0.2f && energy != 0.0f ) // niveau bas mais non nul ? + { + if ( m_lastAlarmTime >= 0.8f ) // clignotte ? + { + energy = 1.0f; + icon = 1; // brun + } + if ( m_lastAlarmTime >= 1.0f ) + { + m_sound->Play(SOUND_ALARM, 0.5f); // bip-bip-bip + m_lastAlarmTime = 0.0f; + } + } + pg->SetLevel(energy); + pg->SetIcon(icon); + } + + pg = (CGauge*)pw->SearchControl(EVENT_OBJECT_GSHIELD); + if ( pg != 0 ) + { + pg->SetLevel(m_object->RetShield()); + } + + pg = (CGauge*)pw->SearchControl(EVENT_OBJECT_GRANGE); + if ( pg != 0 ) + { + icon = 2; // bleu/rouge + range = m_physics->RetReactorRange(); + + if ( range < 0.2f && range != 0.0f && !m_physics->RetLand() ) + { + if ( Mod(m_time, 0.5f) >= 0.2f ) // clignotte ? + { + range = 1.0f; + icon = 1; // jaune + } + if ( m_soundChannelAlarm == -1 ) + { + m_soundChannelAlarm = m_sound->Play(SOUND_ALARMt, m_object->RetPosition(0), 0.0f, 0.1f, TRUE); + m_sound->AddEnvelope(m_soundChannelAlarm, 1.0f, 1.0f, 1.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannelAlarm, 1.0f, 1.0f, 1.0f, SOPER_LOOP); + } + } + else + { + if ( m_soundChannelAlarm != -1 ) + { + m_sound->FlushEnvelope(m_soundChannelAlarm); + m_sound->AddEnvelope(m_soundChannelAlarm, 0.0f, 0.1f, 1.0f, SOPER_STOP); + m_soundChannelAlarm = -1; + } + } + + pg->SetLevel(1.0f-range); + pg->SetIcon(icon); + } + + pc = (CCompass*)pw->SearchControl(EVENT_OBJECT_COMPASS); + if ( pc != 0 ) + { + angle = -(m_object->RetAngleY(0)+PI/2.0f); + pc->SetDirection(angle); + + pc->SetState(STATE_VISIBLE, m_main->RetShowMap()); + } + +#if _TEEN + pb = (CButton*)pw->SearchControl(EVENT_OBJECT_REC); + if ( pb != 0 ) + { + if ( m_bTraceRecord && Mod(m_time, 0.4f) >= 0.2f ) + { + pb->SetState(STATE_CHECK); + } + else + { + pb->ClearState(STATE_CHECK); + } + } +#endif + + bOnBoard = m_camera->RetType() == CAMERA_ONBOARD; + + pgr = (CGroup*)pw->SearchControl(EVENT_OBJECT_CROSSHAIR); + if ( pgr != 0 ) + { + if ( bOnBoard ) + { +#if 0 + angle = m_object->RetGunGoalV(); + if ( m_object->RetType() != OBJECT_MOBILErc ) + { + angle += 10.0f*PI/360.0f; + } + ppos.x = 0.5f-(64.0f/640.0f)/2.0f; + ppos.y = 0.5f-(64.0f/480.0f)/2.0f; + ppos.y += sinf(angle)*0.6f; + pgr->SetPos(ppos); +#else + ppos.x = 0.50f-(64.0f/640.0f)/2.0f; + ppos.y = 0.50f-(64.0f/480.0f)/2.0f; + ppos.x += m_object->RetGunGoalH()/2.0f; + ppos.y += m_object->RetGunGoalV()/1.3f; + pgr->SetPos(ppos); +#endif + pgr->SetState(STATE_VISIBLE, !m_main->RetFriendAim()); + } + else + { + pgr->ClearState(STATE_VISIBLE); + } + } + + ptg = (CTarget*)pw->SearchControl(EVENT_OBJECT_TARGET); + if ( ptg != 0 ) + { + if ( bOnBoard ) + { + ptg->SetState(STATE_VISIBLE); + } + else + { + ptg->ClearState(STATE_VISIBLE); + } + } + + pgr = (CGroup*)pw->SearchControl(EVENT_OBJECT_CORNERul); + if ( pgr != 0 ) + { + pgr->SetState(STATE_VISIBLE, bOnBoard); + } + + pgr = (CGroup*)pw->SearchControl(EVENT_OBJECT_CORNERur); + if ( pgr != 0 ) + { + pgr->SetState(STATE_VISIBLE, bOnBoard); + } + + pgr = (CGroup*)pw->SearchControl(EVENT_OBJECT_CORNERdl); + if ( pgr != 0 ) + { + pgr->SetState(STATE_VISIBLE, bOnBoard); + } + + pgr = (CGroup*)pw->SearchControl(EVENT_OBJECT_CORNERdr); + if ( pgr != 0 ) + { + pgr->SetState(STATE_VISIBLE, bOnBoard); + } +} + +// Met à jour l'état de tous les boutons de l'interface. + +void CBrain::UpdateInterface() +{ + ObjectType type; + CWindow* pw; + CButton* pb; + CSlider* ps; +#if _TEEN + CColor* pc; + int color; +#endif + BOOL bEnable, bFly, bRun; + char title[100]; + + if ( !m_object->RetSelect() ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + type = m_object->RetType(); + + bEnable = ( m_secondaryTask == 0 && m_program == -1 ); + + bEnable = ( m_primaryTask == 0 && m_program == -1 ); + + EnableInterface(pw, EVENT_OBJECT_PROGEDIT, (m_primaryTask == 0 && !m_bTraceRecord)); + EnableInterface(pw, EVENT_OBJECT_PROGLIST, bEnable && !m_bTraceRecord); + EnableInterface(pw, EVENT_OBJECT_LEFT, bEnable); + EnableInterface(pw, EVENT_OBJECT_RIGHT, bEnable); + EnableInterface(pw, EVENT_OBJECT_UP, bEnable); + EnableInterface(pw, EVENT_OBJECT_DOWN, bEnable); + EnableInterface(pw, EVENT_OBJECT_HTAKE, bEnable); + EnableInterface(pw, EVENT_OBJECT_MTAKE, bEnable); + EnableInterface(pw, EVENT_OBJECT_MBACK, bEnable); + EnableInterface(pw, EVENT_OBJECT_MPOWER, bEnable); + EnableInterface(pw, EVENT_OBJECT_MFRONT, bEnable); + EnableInterface(pw, EVENT_OBJECT_GFLAT, bEnable); + EnableInterface(pw, EVENT_OBJECT_FCREATE, bEnable); + EnableInterface(pw, EVENT_OBJECT_FDELETE, bEnable); + EnableInterface(pw, EVENT_OBJECT_SEARCH, bEnable); + EnableInterface(pw, EVENT_OBJECT_TERRAFORM, bEnable); + EnableInterface(pw, EVENT_OBJECT_RECOVER, bEnable); + EnableInterface(pw, EVENT_OBJECT_FIRE, bEnable); + EnableInterface(pw, EVENT_OBJECT_RESET, bEnable); +#if _TEEN + EnableInterface(pw, EVENT_OBJECT_PEN0, bEnable); + EnableInterface(pw, EVENT_OBJECT_PEN1, bEnable); + EnableInterface(pw, EVENT_OBJECT_PEN2, bEnable); + EnableInterface(pw, EVENT_OBJECT_PEN3, bEnable); + EnableInterface(pw, EVENT_OBJECT_PEN4, bEnable); + EnableInterface(pw, EVENT_OBJECT_PEN5, bEnable); + EnableInterface(pw, EVENT_OBJECT_PEN6, bEnable); + EnableInterface(pw, EVENT_OBJECT_PEN7, bEnable); + EnableInterface(pw, EVENT_OBJECT_PEN8, bEnable); + EnableInterface(pw, EVENT_OBJECT_REC, bEnable); + EnableInterface(pw, EVENT_OBJECT_STOP, bEnable); +#endif + + if ( type == OBJECT_HUMAN ) // constructeur ? + { + EnableInterface(pw, EVENT_OBJECT_BFACTORY, bEnable); + EnableInterface(pw, EVENT_OBJECT_BDERRICK, bEnable); + EnableInterface(pw, EVENT_OBJECT_BCONVERT, bEnable); + EnableInterface(pw, EVENT_OBJECT_BSTATION, bEnable); + EnableInterface(pw, EVENT_OBJECT_BREPAIR, bEnable); + EnableInterface(pw, EVENT_OBJECT_BTOWER, bEnable); + EnableInterface(pw, EVENT_OBJECT_BRESEARCH, bEnable); + EnableInterface(pw, EVENT_OBJECT_BRADAR, bEnable); + EnableInterface(pw, EVENT_OBJECT_BENERGY, bEnable); + EnableInterface(pw, EVENT_OBJECT_BLABO, bEnable); + EnableInterface(pw, EVENT_OBJECT_BNUCLEAR, bEnable); + EnableInterface(pw, EVENT_OBJECT_BPARA, bEnable); + EnableInterface(pw, EVENT_OBJECT_BINFO, bEnable); + EnableInterface(pw, EVENT_OBJECT_BXXXX, bEnable); + } + + pb = (CButton*)pw->SearchControl(EVENT_OBJECT_GFLAT); + if ( pb != 0 ) + { + pb->SetState(STATE_VISIBLE, m_engine->RetGroundSpot()); + } + + if ( type == OBJECT_HUMAN || // constructeur ? + type == OBJECT_TECH ) + { + CheckInterface(pw, EVENT_OBJECT_FCOLORb, m_flagColor==0); + CheckInterface(pw, EVENT_OBJECT_FCOLORr, m_flagColor==1); + CheckInterface(pw, EVENT_OBJECT_FCOLORg, m_flagColor==2); + CheckInterface(pw, EVENT_OBJECT_FCOLORy, m_flagColor==3); + CheckInterface(pw, EVENT_OBJECT_FCOLORv, m_flagColor==4); + } + + if ( type == OBJECT_MOBILErs ) // bouclier ? + { + if ( (m_secondaryTask == 0 || !m_secondaryTask->IsBusy()) && m_program == -1 ) + { + EnableInterface(pw, EVENT_OBJECT_BEGSHIELD, (m_secondaryTask == 0)); + EnableInterface(pw, EVENT_OBJECT_ENDSHIELD, (m_secondaryTask != 0)); + DefaultEnter (pw, EVENT_OBJECT_BEGSHIELD, (m_secondaryTask == 0)); + DefaultEnter (pw, EVENT_OBJECT_ENDSHIELD, (m_secondaryTask != 0)); + } + else + { + EnableInterface(pw, EVENT_OBJECT_BEGSHIELD, FALSE); + EnableInterface(pw, EVENT_OBJECT_ENDSHIELD, FALSE); + DefaultEnter (pw, EVENT_OBJECT_BEGSHIELD, FALSE); + DefaultEnter (pw, EVENT_OBJECT_ENDSHIELD, FALSE); + } + + ps = (CSlider*)pw->SearchControl(EVENT_OBJECT_DIMSHIELD); + if ( ps != 0 ) + { + ps->SetVisibleValue((RADIUS_SHIELD_MIN/g_unit)+m_object->RetParam()*((RADIUS_SHIELD_MAX-RADIUS_SHIELD_MIN)/g_unit)); + } + } + + bFly = bEnable; + if ( bFly && (type == OBJECT_HUMAN || type == OBJECT_TECH) ) + { + if ( m_object->RetFret() != 0 ) bFly = FALSE; // si porte -> ne vole pas + } + EnableInterface(pw, EVENT_OBJECT_GASUP, bFly); + EnableInterface(pw, EVENT_OBJECT_GASDOWN, bFly); + if ( m_object->RetTrainer() ) // entraînement ? + { + DeadInterface(pw, EVENT_OBJECT_GASUP, FALSE); + DeadInterface(pw, EVENT_OBJECT_GASDOWN, FALSE); + } + else + { + DeadInterface(pw, EVENT_OBJECT_GASUP, g_researchDone&RESEARCH_FLY); + DeadInterface(pw, EVENT_OBJECT_GASDOWN, g_researchDone&RESEARCH_FLY); + } + + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH || + type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEtg || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr || + type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM ) // véhicule ? + { + bRun = FALSE; + if ( m_script[m_selScript] != 0 ) + { + m_script[m_selScript]->GetTitle(title); + if ( title[0] != 0 ) + { + bRun = TRUE; + } + } + if ( !bEnable && m_program == -1 ) bRun = FALSE; + if ( m_bTraceRecord ) bRun = FALSE; + EnableInterface(pw, EVENT_OBJECT_PROGRUN, bRun); + + pb = (CButton*)pw->SearchControl(EVENT_OBJECT_PROGRUN); + if ( pb != 0 ) + { + pb->SetIcon(m_program==-1?21:8); // run/stop + } + +//? pb = (CButton*)pw->SearchControl(EVENT_OBJECT_PROGEDIT); +//? if ( pb != 0 ) +//? { +//? pb->SetIcon(m_program==-1?22:40); // edit/debug +//? } + + BlinkScript(m_program != -1); // clignotte si script en exécution + } + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia ) // bras ? + { + CheckInterface(pw, EVENT_OBJECT_MPOWER, m_manipStyle==EVENT_OBJECT_MPOWER); + CheckInterface(pw, EVENT_OBJECT_MBACK, m_manipStyle==EVENT_OBJECT_MBACK); + CheckInterface(pw, EVENT_OBJECT_MFRONT, m_manipStyle==EVENT_OBJECT_MFRONT); + } + +#if _TEEN + if ( m_object->RetTraceDown() ) + { + pb = (CButton*)pw->SearchControl(EVENT_OBJECT_PEN0); + if ( pb != 0 ) + { + pb->ClearState(STATE_CHECK); + } + + color = m_object->RetTraceColor(); + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN1); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, color==1); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN2); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, color==8); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN3); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, color==7); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN4); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, color==4); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN5); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, color==6); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN6); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, color==14); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN7); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, color==12); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN8); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, color==10); + } + } + else + { + pb = (CButton*)pw->SearchControl(EVENT_OBJECT_PEN0); + if ( pb != 0 ) + { + pb->SetState(STATE_CHECK); + } + + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN1); + if ( pc != 0 ) + { + pc->ClearState(STATE_CHECK); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN2); + if ( pc != 0 ) + { + pc->ClearState(STATE_CHECK); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN3); + if ( pc != 0 ) + { + pc->ClearState(STATE_CHECK); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN4); + if ( pc != 0 ) + { + pc->ClearState(STATE_CHECK); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN5); + if ( pc != 0 ) + { + pc->ClearState(STATE_CHECK); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN6); + if ( pc != 0 ) + { + pc->ClearState(STATE_CHECK); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN7); + if ( pc != 0 ) + { + pc->ClearState(STATE_CHECK); + } + pc = (CColor*)pw->SearchControl(EVENT_OBJECT_PEN8); + if ( pc != 0 ) + { + pc->ClearState(STATE_CHECK); + } + } +#endif +} + +// Met à jour la liste des programmes. + +void CBrain::UpdateScript(CWindow *pw) +{ + CList* pl; + char name[100]; + char title[100]; + int i; + BOOL bSoluce; + + pl = (CList*)pw->SearchControl(EVENT_OBJECT_PROGLIST); + if ( pl == 0 ) return; + +#if _SCHOOL + bSoluce = m_main->RetSoluce4(); +#else + bSoluce = TRUE; +#endif + + for ( i=0 ; iGetTitle(title); + if ( !bSoluce && i == 3 ) + { + title[0] = 0; + } + if ( title[0] != 0 ) + { + sprintf(name, "%d: %s", i+1, title); + } + } + + pl->SetName(i, name); + } + + if ( !bSoluce ) + { + pl->SetEnable(3, FALSE); + } + + pl->SetSelect(m_selScript); + pl->ShowSelect(TRUE); +} + +// Retourne le rang du script sélectionné. + +int CBrain::RetSelScript() +{ + CWindow* pw; + CList* pl; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return -1; + + pl = (CList*)pw->SearchControl(EVENT_OBJECT_PROGLIST); + if ( pl == 0 ) return -1; + + return pl->RetSelect(); +} + +// Fait clignotter le programme en exécution. + +void CBrain::BlinkScript(BOOL bEnable) +{ + CWindow* pw; + CList* pl; + + if ( !m_object->RetSelect() ) return; // robot pas sélectionné ? + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw == 0 ) return; + + pl = (CList*)pw->SearchControl(EVENT_OBJECT_PROGLIST); + if ( pl == 0 ) return; + + pl->SetBlink(bEnable); +} + +// Modifie l'état d'un bouton de l'interface. + +void CBrain::CheckInterface(CWindow *pw, EventMsg event, BOOL bState) +{ + CControl* control; + + control = pw->SearchControl(event); + if ( control == 0 ) return; + + control->SetState(STATE_CHECK, bState); +} + +// Modifie l'état d'un bouton de l'interface. + +void CBrain::EnableInterface(CWindow *pw, EventMsg event, BOOL bState) +{ + CControl* control; + + control = pw->SearchControl(event); + if ( control == 0 ) return; + + control->SetState(STATE_ENABLE, bState); +} + +// Modifie l'état d'un bouton de l'interface. + +void CBrain::DeadInterface(CWindow *pw, EventMsg event, BOOL bState) +{ + CControl* control; + + control = pw->SearchControl(event); + if ( control == 0 ) return; + + control->SetState(STATE_DEAD, !bState); +} + +// Modifie l'état d'entrée par défaut d'un bouton de l'onterface. + +void CBrain::DefaultEnter(CWindow *pw, EventMsg event, BOOL bState) +{ + CControl* control; + + control = pw->SearchControl(event); + if ( control == 0 ) return; + + if ( bState ) + { + control->SetState(STATE_DEFAULT); + m_defaultEnter = event; + } + else + { + control->ClearState(STATE_DEFAULT); + } +} + + +// Indique si l'objet est occupé dans une tâche. + +BOOL CBrain::IsBusy() +{ + return (m_primaryTask != 0); +} + +// Gestion de l'activité d'un objet. + +void CBrain::SetActivity(BOOL bMode) +{ + m_bActivity = bMode; +} + +BOOL CBrain::RetActivity() +{ + return m_bActivity; +} + +// Indique si un programme est en cours d'exécution. + +BOOL CBrain::IsProgram() +{ + return ( m_program != -1 ); +} + +// Indique si un programme existe. + +BOOL CBrain::ProgramExist(int rank) +{ + return ( m_script[rank] != 0 ); +} + +// Démarre un programme donné. + +void CBrain::RunProgram(int rank) +{ + if ( rank < 0 ) return; + + if ( m_script[rank] != 0 && + m_script[rank]->Run() ) + { + m_program = rank; // démarre nouveau programme + BlinkScript(TRUE); // clignotte + m_object->CreateSelectParticule(); + m_main->UpdateShortcuts(); + } +} + +// Retourne le premier programme libre. + +int CBrain::FreeProgram() +{ + int i; + + for ( i=0 ; iCompare(m_script[rank]) ) // déjà un même ? + { + delete m_script[rank]; + m_script[rank] = 0; + return FALSE; + } + } + + return TRUE; +} + +// Charge un script avec un fichier texte. + +BOOL CBrain::ReadProgram(int rank, char* filename) +{ + if ( m_script[rank] == 0 ) + { + m_script[rank] = new CScript(m_iMan, m_object, &m_secondaryTask); + } + + if ( m_script[rank]->ReadScript(filename) ) return TRUE; + + delete m_script[rank]; + m_script[rank] = 0; + + return FALSE; +} + +// Indique si un programme est correctement compilé. + +BOOL CBrain::RetCompile(int rank) +{ + if ( m_script[rank] == 0 ) return FALSE; + return m_script[rank]->RetCompile(); +} + +// Sauve un script dans un fichier texte. + +BOOL CBrain::WriteProgram(int rank, char* filename) +{ + if ( m_script[rank] == 0 ) + { + m_script[rank] = new CScript(m_iMan, m_object, &m_secondaryTask); + } + + if ( m_script[rank]->WriteScript(filename) ) return TRUE; + + delete m_script[rank]; + m_script[rank] = 0; + + return FALSE; +} + + +// Charge un stack de script en exécution avec un fichier. + +BOOL CBrain::ReadStack(FILE *file) +{ + short op; + + fRead(&op, sizeof(short), 1, file); + if ( op == 1 ) // run ? + { + fRead(&op, sizeof(short), 1, file); // program rank + if ( op >= 0 && op < BRAINMAXSCRIPT ) + { + m_program = op; // redémarre programme + m_selScript = op; + BlinkScript(TRUE); // clignotte + + if ( m_script[op] == 0 ) + { + m_script[op] = new CScript(m_iMan, m_object, &m_secondaryTask); + } + if ( !m_script[op]->ReadStack(file) ) return FALSE; + } + } + + return TRUE; +} + +// Sauve le stack du script en exécution dans un fichier. + +BOOL CBrain::WriteStack(FILE *file) +{ + short op; + + if ( m_program != -1 && // programme en cours ? + m_script[m_program]->IsRunning() ) + { + op = 1; // run + fWrite(&op, sizeof(short), 1, file); + + op = m_program; + fWrite(&op, sizeof(short), 1, file); + + return m_script[m_program]->WriteStack(file); + } + + op = 0; // stop + fWrite(&op, sizeof(short), 1, file); + return TRUE; +} + + + +// Début de l'enregistrement du dessin. + +void CBrain::TraceRecordStart() +{ + m_traceOper = TO_STOP; + + m_tracePos = m_object->RetPosition(0); + m_traceAngle = m_object->RetAngleY(0); + + if ( m_object->RetTraceDown() ) // crayon baissé ? + { + m_traceColor = m_object->RetTraceColor(); + } + else // crayon levé ? + { + m_traceColor = -1; + } + + delete m_traceRecordBuffer; + m_traceRecordBuffer = (TraceRecord*)malloc(sizeof(TraceRecord)*MAXTRACERECORD); + m_traceRecordIndex = 0; +} + +// Enregistrement du dessin en cours. + +void CBrain::TraceRecordFrame() +{ + TraceOper oper = TO_STOP; + D3DVECTOR pos; + float angle, len, speed; + int color; + + speed = m_physics->RetLinMotionX(MO_REASPEED); + if ( speed > 0.0f ) oper = TO_ADVANCE; + if ( speed < 0.0f ) oper = TO_RECEDE; + + speed = m_physics->RetCirMotionY(MO_REASPEED); + if ( speed != 0.0f ) oper = TO_TURN; + + if ( m_object->RetTraceDown() ) // crayon baissé ? + { + color = m_object->RetTraceColor(); + } + else // crayon levé ? + { + color = -1; + } + + if ( oper != m_traceOper || + color != m_traceColor ) + { + if ( m_traceOper == TO_ADVANCE || + m_traceOper == TO_RECEDE ) + { + pos = m_object->RetPosition(0); + len = Length2d(pos, m_tracePos); + TraceRecordOper(m_traceOper, len); + } + if ( m_traceOper == TO_TURN ) + { + angle = m_object->RetAngleY(0)-m_traceAngle; + TraceRecordOper(m_traceOper, angle); + } + + if ( color != m_traceColor ) + { + TraceRecordOper(TO_PEN, (float)color); + } + + m_traceOper = oper; + m_tracePos = m_object->RetPosition(0); + m_traceAngle = m_object->RetAngleY(0); + m_traceColor = color; + } +} + +// Fin de l'enregistrement du dessin. Génère le programme CBOT. + +void CBrain::TraceRecordStop() +{ + TraceOper lastOper, curOper; + float lastParam, curParam; + int max, i; + char* buffer; + + if ( m_traceRecordBuffer == 0 ) return; + + max = 10000; + buffer = (char*)malloc(max); + *buffer = 0; + strncat(buffer, "extern void object::AutoDraw()\n{\n", max-1); + + lastOper = TO_STOP; + lastParam = 0.0f; + for ( i=0 ; iSendScript(buffer); + delete buffer; +} + +// Enregistre une instruction CBOT. + +BOOL CBrain::TraceRecordOper(TraceOper oper, float param) +{ + int i; + + i = m_traceRecordIndex; + if ( i >= MAXTRACERECORD ) return FALSE; + + m_traceRecordBuffer[i].oper = oper; + m_traceRecordBuffer[i].param = param; + + m_traceRecordIndex = i+1; + return TRUE; +} + +// Génère une instruction CBOT. + +BOOL CBrain::TraceRecordPut(char *buffer, int max, TraceOper oper, float param) +{ + char line[100]; + int color; + + if ( oper == TO_ADVANCE ) + { + param /= g_unit; + sprintf(line, "\tmove(%.1f);\n", param); + strncat(buffer, line, max-1); + } + + if ( oper == TO_RECEDE ) + { + param /= g_unit; + sprintf(line, "\tmove(-%.1f);\n", param); + strncat(buffer, line, max-1); + } + + if ( oper == TO_TURN ) + { + param = -param*180.0f/PI; + sprintf(line, "\tturn(%d);\n", (int)param); +//? sprintf(line, "\tturn(%.1f);\n", param); + strncat(buffer, line, max-1); + } + + if ( oper == TO_PEN ) + { + color = (int)param; + if ( color == -1 ) strncat(buffer, "\tpenup();\n", max-1); + if ( color == 1 ) strncat(buffer, "\tpendown(Black);\n", max-1); + if ( color == 8 ) strncat(buffer, "\tpendown(Yellow);\n", max-1); + if ( color == 7 ) strncat(buffer, "\tpendown(Orange);\n", max-1); + if ( color == 4 ) strncat(buffer, "\tpendown(Red);\n", max-1); + if ( color == 6 ) strncat(buffer, "\tpendown(Purple);\n", max-1); + if ( color == 14 ) strncat(buffer, "\tpendown(Blue);\n", max-1); + if ( color == 12 ) strncat(buffer, "\tpendown(Green);\n", max-1); + if ( color == 10 ) strncat(buffer, "\tpendown(Brown);\n", max-1); + } + + return TRUE; +} + diff --git a/src/brain.h b/src/brain.h new file mode 100644 index 00000000..071c4b14 --- /dev/null +++ b/src/brain.h @@ -0,0 +1,202 @@ +// brain.h + +#ifndef _BRAIN_H_ +#define _BRAIN_H_ + + +class CInstanceManager; +class CD3DEngine; +class CTerrain; +class CWater; +class CCamera; +class CObject; +class CPhysics; +class CMotion; +class CTaskManager; +class CInterface; +class CWindow; +class CDisplayText; +class CScript; +class CRobotMain; +class CStudio; +class CSound; +class CParticule; + +enum ObjectType; +enum TaskManipOrder; +enum TaskManipArm; +enum TaskFlagOrder; +enum TaskShieldMode; + + +#define BRAINMAXSCRIPT 10 + + + +enum TraceOper +{ + TO_STOP = 0, // arrêt + TO_ADVANCE = 1, // avance + TO_RECEDE = 2, // recule + TO_TURN = 3, // tourne + TO_PEN = 4, // changement de couleur +}; + +typedef struct +{ + TraceOper oper; + float param; +} +TraceRecord; + + + +class CBrain +{ +public: + CBrain(CInstanceManager* iMan, CObject* object); + ~CBrain(); + + void DeleteObject(BOOL bAll=FALSE); + + void SetPhysics(CPhysics* physics); + void SetMotion(CMotion* motion); + + BOOL EventProcess(const Event &event); + BOOL CreateInterface(BOOL bSelect); + + BOOL Write(char *line); + BOOL Read(char *line); + + BOOL IsBusy(); + void SetActivity(BOOL bMode); + BOOL RetActivity(); + BOOL IsProgram(); + BOOL ProgramExist(int rank); + void RunProgram(int rank); + int FreeProgram(); + int RetProgram(); + void StopProgram(); + void StopTask(); + + BOOL IntroduceVirus(); + void SetActiveVirus(BOOL bActive); + BOOL RetActiveVirus(); + + void SetScriptRun(int rank); + int RetScriptRun(); + void SetScriptName(int rank, char *name); + char* RetScriptName(int rank); + void SetSoluceName(char *name); + char* RetSoluceName(); + + BOOL ReadSoluce(char* filename); + BOOL ReadProgram(int rank, char* filename); + BOOL RetCompile(int rank); + BOOL WriteProgram(int rank, char* filename); + BOOL ReadStack(FILE *file); + BOOL WriteStack(FILE *file); + + Error StartTaskTake(); + Error StartTaskManip(TaskManipOrder order, TaskManipArm arm); + Error StartTaskFlag(TaskFlagOrder order, int rank); + Error StartTaskBuild(ObjectType type); + Error StartTaskSearch(); + Error StartTaskTerraform(); + Error StartTaskPen(BOOL bDown, int color); + Error StartTaskRecover(); + Error StartTaskShield(TaskShieldMode mode); + Error StartTaskFire(float delay); + Error StartTaskFireAnt(D3DVECTOR impact); + Error StartTaskGunGoal(float dirV, float dirH); + Error StartTaskReset(D3DVECTOR goal, D3DVECTOR angle); + + void UpdateInterface(float rTime); + void UpdateInterface(); + +protected: + BOOL EventFrame(const Event &event); + + void StartEditScript(int rank, char* name); + void StopEditScript(BOOL bCancel); + + Error EndedTask(); + + void GroundFlat(); + void ColorFlag(int color); + + void UpdateScript(CWindow *pw); + int RetSelScript(); + void BlinkScript(BOOL bEnable); + + void CheckInterface(CWindow *pw, EventMsg event, BOOL bState); + void EnableInterface(CWindow *pw, EventMsg event, BOOL bState); + void DeadInterface(CWindow *pw, EventMsg event, BOOL bState); + void DefaultEnter(CWindow *pw, EventMsg event, BOOL bState=TRUE); + + void TraceRecordStart(); + void TraceRecordFrame(); + void TraceRecordStop(); + BOOL TraceRecordOper(TraceOper oper, float param); + BOOL TraceRecordPut(char *buffer, int max, TraceOper oper, float param); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CTerrain* m_terrain; + CWater* m_water; + CCamera* m_camera; + CObject* m_object; + CPhysics* m_physics; + CMotion* m_motion; + CInterface* m_interface; + CDisplayText* m_displayText; + CRobotMain* m_main; + CStudio* m_studio; + CSound* m_sound; + CParticule* m_particule; + CTaskManager* m_primaryTask; + CTaskManager* m_secondaryTask; + + CScript* m_script[BRAINMAXSCRIPT]; + int m_selScript; // rang du script sélectionné + int m_program; // rang du programme exécuté / -1 + BOOL m_bActivity; + BOOL m_bBurn; + BOOL m_bActiveVirus; + + int m_scriptRun; + char m_scriptName[BRAINMAXSCRIPT][50]; + char m_soluceName[50]; + + EventMsg m_buttonAxe; + EventMsg m_manipStyle; + EventMsg m_defaultEnter; + EventMsg m_interfaceEvent[100]; + + CObject* m_antTarget; + CObject* m_beeBullet; + float m_beeBulletSpeed; + D3DVECTOR m_startPos; + float m_time; + float m_burnTime; + float m_lastUpdateTime; + float m_lastHumanTime; + float m_lastSpiderTime; + float m_lastWormTime; + float m_lastBulletTime; + float m_lastAlarmTime; + int m_soundChannelAlarm; + int m_flagColor; + + BOOL m_bTraceRecord; + TraceOper m_traceOper; + D3DVECTOR m_tracePos; + float m_traceAngle; + int m_traceColor; + int m_traceRecordIndex; + TraceRecord* m_traceRecordBuffer; +}; + + +#endif //_BRAIN_H_ diff --git a/src/bug.txt b/src/bug.txt new file mode 100644 index 00000000..b598ad05 --- /dev/null +++ b/src/bug.txt @@ -0,0 +1,94 @@ +void CD3DApplication::StepSimul(float rTime) +{ + Event event; + + if ( m_pRobotMain == 0 ) return; + + if ( rTime > 0.5f ) rTime = 0.5f; // jamais plus de 0.5s ! + + ZeroMemory(&event, sizeof(Event)); + event.event = EVENT_FRAME; + event.rTime = rTime; + event.axeX = AxeLimit(m_axeKey.x + m_axeJoy.x); + event.axeY = AxeLimit(m_axeKey.y + m_axeJoy.y); + event.axeZ = AxeLimit(m_axeKey.z + m_axeJoy.z); + event.keyState = m_keyState; + +//?char s[100]; +//?sprintf(s, "StepSimul %.3f\n", event.rTime); +//?OutputDebugString(s); + m_pRobotMain->EventProcess(event); +} + + + +1347: void CD3DApplication::StepSimul(float rTime) +1348: { +0041E950 sub esp,24h +0041E953 push esi +0041E954 mov esi,ecx +1349: Event event; +1350: +1351: if ( m_pRobotMain == 0 ) return; +0041E956 mov eax,dword ptr [esi+0ECh] +0041E95C test eax,eax +0041E95E je CD3DApplication::StepSimul(0x0041ea08)+0B8h +1352: +1353: if ( rTime > 0.5f ) rTime = 0.5f; // jamais plus de 0.5s ! +0041E964 fld dword ptr [esp+2Ch] +0041E968 fcomp dword ptr [??_7CControl@@6B@(0x004b9ed4)+94h] +0041E96E push edi +0041E96F fnstsw ax +0041E971 test ah,41h +0041E974 jne CD3DApplication::StepSimul(0x0041e97e)+2Eh +0041E976 mov dword ptr [esp+30h],3F000000h +1354: +1355: ZeroMemory(&event, sizeof(Event)); +1356: event.event = EVENT_FRAME; +1357: event.rTime = rTime; +1358: event.axeX = AxeLimit(m_axeKey.x + m_axeJoy.x); +0041E97E fld dword ptr [esi+0F8h] +0041E984 fadd dword ptr [esi+104h] +0041E98A mov eax,dword ptr [esp+30h] +0041E98E mov ecx,9 +0041E993 mov dword ptr [esp+28h],eax +0041E997 push ecx +0041E998 xor eax,eax +0041E99A lea edi,dword ptr [esp+0Ch] +0041E99E fstp dword ptr [esp] +0041E9A1 rep stos dword ptr es:[edi] +0041E9A3 mov dword ptr [esp+0Ch],2 +0041E9AB call AxeLimit(0x0041cae0) +1359: event.axeY = AxeLimit(m_axeKey.y + m_axeJoy.y); +0041E9B0 fld dword ptr [esi+0FCh] +0041E9B6 fadd dword ptr [esi+108h] +0041E9BC fxch st(1) +0041E9BE fstp dword ptr [esp+1Ch] +0041E9C2 fstp dword ptr [esp] +0041E9C5 call AxeLimit(0x0041cae0) +1360: event.axeZ = AxeLimit(m_axeKey.z + m_axeJoy.z); +0041E9CA fld dword ptr [esi+100h] +0041E9D0 fadd dword ptr [esi+10Ch] +0041E9D6 fxch st(1) +0041E9D8 fstp dword ptr [esp+20h] +0041E9DC fstp dword ptr [esp] +0041E9DF call AxeLimit(0x0041cae0) +1361: event.keyState = m_keyState; +1362: +1363: //?char s[100]; +1364: //?sprintf(s, "StepSimul %.3f\n", event.rTime); +1365: //?OutputDebugString(s); +1366: m_pRobotMain->EventProcess(event); +0041E9E4 mov dx,word ptr [esi+0F4h] +0041E9EB mov ecx,dword ptr [esi+0ECh] +0041E9F1 fstp dword ptr [esp+24h] +0041E9F5 add esp,4 +0041E9F8 lea eax,dword ptr [esp+8] +0041E9FC mov word ptr [esp+24h],dx +0041EA01 push eax +0041EA02 call CRobotMain::EventProcess(0x0047fba0) +0041EA07 pop edi +1367: } +0041EA08 pop esi +0041EA09 add esp,24h +0041EA0C ret 4 diff --git a/src/bug1.txt b/src/bug1.txt new file mode 100644 index 00000000..59b0ca40 --- /dev/null +++ b/src/bug1.txt @@ -0,0 +1,68 @@ +extern void object::Solution( ) +{ + while ( true ) + { + object left, right; + + left = Radar(TypeMarkPath, -45, 120, 100); + right = Radar(TypeMarkPath, 45, 120, 100); + + if ( left == null && right == null ) + { + } + } +} + +CBotString::CBotString(const CBotString &) +CBotVar::GivName() +CBotStack::FindVar(CBotToken * &, int, int) +CBotStack::FindVar(CBotToken &, int, int) +CBotStack::CopyVar(CBotToken &, int) +CBotExpression::Execute(CBotStack * &) +CBotListInstr::Execute(CBotStack * &) +CBotWhile::Execute(CBotStack * &) +CBotListInstr::Execute(CBotStack * &) +CBotFunction::Execute(CBotVar * *, CBotStack * &) +CBotProgram::Run(void *) +CScript::Continue(const Event &) + + + +CBotString::CBotString(const CBotString &) : + m_token = 0xdddddddd + +CBotVar::GivName() : + return m_token->GivString(); + +CBotStack::FindVar(CBotToken * &, int, int) : + CBotStack* p = this; + CBotString name = pToken->GivString(); + + while (p != NULL) + { + CBotVar* pp = p->m_listVar; + while ( pp != NULL) + { + if (pp->GivName() == name) <- paf + + avec : + pp->__vfprt = 0xdddddddd + pp->m_token = 0xdddddddd + pp->m_next = 0xdddddddd + pp->m_type = -572662307 + pp->m_binit = -572662307 + pp->m_pMyThis = 0xdddddddd + pp->m_pUserPtr = 0xdddddddd + pp->m_InitExpr = 0xdddddddd + +CBotStack::FindVar(CBotToken &, int, int) : + CBotToken* pt = &Token; + pt->m_next = 0 + pt->m_prev = 0 + pt->m_type = 4 + pt->m_IdKeyWord = -1 + pt->m_Text = "right" + pt->m_Sep = " " + pt->m_start = 124 + pt->m_end = 129 + diff --git a/src/bugs/Colobot_image blèm.psp b/src/bugs/Colobot_image blèm.psp new file mode 100644 index 0000000000000000000000000000000000000000..905ae5ce55e711864adb4865300e3ba31f5cd5a2 GIT binary patch literal 42641 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 literal 0 HcmV?d00001 diff --git a/src/bugs/Image1.gif b/src/bugs/Image1.gif new file mode 100644 index 0000000000000000000000000000000000000000..309c3fb447cbfc66bd727a7267ad7df62ba6f72e GIT binary patch literal 195965 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

x$52UhCFX8XtfXA8)J9LQuF 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%0A_b({-2HY^H-q?ArqpQDj>N;mFzv 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 literal 0 HcmV?d00001 diff --git a/src/bugs/Image4.gif b/src/bugs/Image4.gif new file mode 100644 index 0000000000000000000000000000000000000000..88cb30a3440b9106cf196776f8180932a9258f3e GIT binary patch literal 15063 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

q!)!u{3Q;@3SOEF~k{erjwe+@ZVOh+Lsu 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

5rc? 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 R1@p0A5f&lgF9QMq06Wl(N^t-H literal 0 HcmV?d00001 diff --git a/src/bugs/borne.gif b/src/bugs/borne.gif new file mode 100644 index 0000000000000000000000000000000000000000..3046e6c9381feb14fca298210a0b19a50c0de2e0 GIT binary patch literal 195790 zcmV)FK)=67Nk%w1VITuo0`~v_0000C2m%`y3M?QIEH4%@DJd~ADKRlIKO77}F%m&J z2|zI+JvkgoIT$%PDML9YB}@`ESQbA@6GB2ENJbGxMkPX8DM~^qN=hk0IWa;)F-k%) zIXO8(IXOl~HAg}}VHgTiKNnU<5LZGjS4tjZN*q#3DO^e|T2c{RQYKDPF=kRON=i9O zN@rgWm*ntS|(mvF>hKiQc^iuQaNB(IcrimQc^-vQc6-%Qd&|%T2e|{T0&z| zLTy+(PGcTpV=Xyr97umHYhws&V;o~^F=JynYij^)X%1^+DQjyfYhy8MYcXqMIcsY< zb87%|WeRh10CRH)b7L5DVb5d_>V_KX-ib5ZQYd?!~0)cWW zdviu}b6SXVN{Dk>ihB!(dpUo5T9SKIb8};Ob7+BhW0-qdii8iDgbIv=DUF0YgoIjz zgky|^QkR5SdwXkpdvk<)YlM4qiGFB|gkzL@W1NIzm5m0KkQ9}VBax0gkc?WCjAN9P zTAYkygoJa9glmk1bBv5@jEr-Xgm09LYnP92q?#S1oJg{zLffK4#lS1e#6;`jF7Mzx zoRn*vlyjhzYpa)QwWw*oyk^#?W!J}NgoJyDgnx{Tdy$NRl$3j%kb9h*cbJrfoScNE zjB}-wbETAfrJQr6oO`95gr%i(t(14IoO7+EbE=yy{*Q^snW`&#K5@N z)3fH_%JcB*EC2ui03ZWc0ssj90M7^@h_xkYFqb_TlE;d41QC}M;%@>ZjU8sfO1jw1G0VtiCq zY2}qSW%gMC01Ob!Vf^6J0ssIM0FFNS=;I6}sntl_n+n#};)@~fR^F6t`lcbAw6(b7 zgGi3}!Wpgo2AgaL%6VLff#$}-kEOpKrL$rBC~T3cc;I$^ zir65ggJS9GtNyUYYSDZ|aR~qi@WiEw000D#gFN}{BaeUu5r{)dR8^&HvQ#y@?6XwS zFyle1<)G}d*gDHBv^(VXoweI;3+=ehO3T}(*E*Z+y4rS&!?)?iOYXepo`G(-$i}O# zwpF<+>%Ra846sU?@xutO0r-KBDz@ejPMZ15>{=<3Xw&J1?vm1JBL;8jTQvF5BD>D|yTY2QVmeZ1$T(lU>!% zk78GM{>*s)nD*W__uwsafREhw(ugOnI9~I`#g8zylG)`jqeU!_nrv3g!wz0o)oebD zG$QuK&6fU;3NPSUv24p~JB}E)i2;u3R^1Ksp{@7aPbYHFAy2u|63RO5{g|VyDOTa4 ztT+33?tIRJhD@&AwO4=o?LyNYGwsa^-sAOe6?ACl z&cc%iBbf{{HniHSpxi-p+t@g6^P<@mEqK`(nf9zwi95-|a~>;^fsVzmdpJW`il9m# zl=Y0}@e6gM%AWI_wU0i4!3I4zLLd5(yXw^-c%3R-^|W=k@!9Z(IIKz2Jo7ZkbqN^$ z@yJ@%;<2XG?0|Fpv!5R@k-uTJPJaq9}T2U7{YKMMa1n{Tl5>W^l^k{j8I$K=)nU` zcg7q#@{y1P3bMe|k9_!nS3Vp-CgdTHdBo#=@gvBayr!*i;K2`#$OJG(w?LoKAc_V# z75`WPr5K^YdQ<`783Ncs{#At;IKW3rT2RJZB5;cQ>W3o=L63iA(3$?YhaDWjLerh@ z1!&}7J9@Aqm4KrRI5>y4a*544LJJJUn4YqXQ3H*2?3Iy>r#$ER5S37}D46~nzz6b) z2~K{p85AQ3bdWgCaO44%OhkqLaH&P09fVRXT!khkstHWYMiY*}gd;FI!@I!~qPQHH zG;)&(et@F|j({Kw9wnPJ_G2F&JV!jB>BfK1!x~lT$36B@O@G*c1tn6^xPE3Nk)?zm z%R$FD>DbcN`NNK0q)IAiP|dpmm7ZA5YF2kRjL2yc9%l#unFfG~PZBY6q8twWlx0vq zgf0`A2;10fR)duygqJt#K^o_!0=vqRdR38zgnHBhMpV^*))`(^&fyGWIKm&PAjUfM zu#R=uVI9Os1q&%r3}jqGS%GY-_4*+p+pq+wIPjp)T97xzQiT;gVE$o0+A%|aw4<8+ z5Cbrr6-cf6_P04~8a_HerT`Qm6Zp`_ek3P=O{_y6&NzfYW<$_J7Q(Hm!~`&avCuxU8VJJ~RrN<*v$?N3gd+@M$nPHa8-`SfBM;+zWn!UP zl{^-;CHyEuBLE3c(dHBfz^Do(QZdd|_^~Rq&!hz6CU;-7Y?CuBQ`A}q%4GaYCs8t%tpkDF|7lU;XF~)hikD`}$ z^@s;Cz(E(efP)9ZkViadCpZZ{Sy}t|6M9<`j?_A@uU>{~{)VgK3=b}=A9*MSRr=8a zPJoujAI`I$|FuJYAOjrv2=qQIhYAvNfEfP_VokCUWc+&o%3nuq+5| zcy;Fa7s{RYTQ{s%n8zSt5orEGuWV z9(7gvfCG}1L5#Q@h8R3ZgE3s%u6vHQw5=54(O7#iX}a7x!hwu%d~zMgK+1r9wjMHcLW;HG-p$J7d!VhB5@Jje0 z1~(YI;H3bqU4=~*ZeDTBEhz&HcsucTH|q`*4D$v4{XvO7G;16zXr_FNdAfE_`{gjF zj7kK$lMl`!MfA1l+tVqYM~ z(-qU+O^>&cbuV|3-2(?B zS40c$uvixKT?NkmuFDBe{p!o7DsmZg`xO3fR(UiY9?J;mqM`GSe-C|$=ne)jR1r1} zv-^9Ru6n+0;9Y*fUDP=ce(Q{3gFav)$tlG>Z`W~9RSE^C2!&t=vnO_L&<75v2%W%m zu;(ni7ZffMA=ngZ&T@at@p=9*cT{nF&d@A+&@ATA0xH*R5(j_Q*MctC6jd<{olps# z&qdu0`U;0&n1M`EA@Vz6I1zzNO(VC+V9 zFF5D)g>m;RsIUXLWs{|I zmHp5Io3Ml|2$FI+mq!7OPBnB)v|V9ijZUSEy^)jJvJaTx4AfB}*i@Lgfef1PJ94;c z1zA1&(04X91`$$Qz2t?DnNy1qQ!`Zv8uSnAU=3QanP%5@PS`Bx(3LB3CqSi=rKy$A zLT9NUbk9->EZ~09w~TbTo4jcgB3W*!v{*`ygjj(ME4%6}jMMwKI>)0hk-p4~>umVo(NU z;D#*F2xDoY%E>SAfCGjkWK=;7FMxUgI-pG2q#*H|+5-=mFloXtJ2nGw9?%;>Dxnnm z5BVS{GbxiR^#a1wp;&pJC3<`;(4A3bJzj{0&9V>3fI%A6EYt9#XG(RaNpJc92Q%3M zWq_Lj#sdBu;Fv}hiXruEh3ce?+Nc>Jk{lxsQz{JMpm3Zs28tO_w3G*Eu^)(^pO3`@sQr6a6bx^e!%3RQIus!&bMPzcvm z2&mQ%s<3(e&6JFilQu+iub9Xlc@ zJN~l9>P03{vKl*ul1MszwWtr9vkt2pI6$pTmNHyOg|3Pa5(1~xlW#Fwt2whh(_?=_ z3!N4lvh}C1gt{C{d$3n(uO)l6HQP$G)3G`mw$ECaHBh!PKm%ucwkqI1Ia32F;I?nO zws5<)NsF_Z22?L(wkwmiDxkJ->$ZPO18cjs8!)salDBD#wtcGtHjp!M8@FZaKZBdJ z1>3eVurf6u1AVKwjr+E4O9Ox_xN}RWwHhHWl(u`TxPB|Qa{IS~OQ??moi)${nG3p& zo46|jxvk5(YfHLf`@4;5YEqJm(V3yfOR6iCsRXO3f)XKah-n%My~dlN%Im7~{^}?f zSiICRz1*9;@M^uwfxXWAO4~b=-D|v=TDVHLs& z7Yv=O#aa%G!QM--;F~1Ud6@1y!WVp;t_s2G8@>vhIy@R7AHc%6_yR8+!3^uaKJ2h? z&=*0>5kvgLMtsCboWx4J#7x}8PW;4B9K}*R#Z+9yR(!=+oW)wa#a!IQUi`&i9L8ci z#$;T^W_-qIoW^Rr#%$ciZv4h@9LI7z$8=oBc6`TpoX2{+$9&w!e*DM&fE>tzJjjGx z$cB8#h@8lZyvU5)$d3HTkQ~X9Jjs+?$(DS{n4HO)yvdy0$)5blpd8AgJj$e8%BFnE zsGQ2GyvnTH%C7v%upG;>Jj=9P%eH*WxSY$nyvw}Y%f9@}z#PoNJj}#g%*K4o$ehf| zyv)qp%+CDG&>YRuJk8Wx&DMO)*qqJUyv^L)&EEXY;2h54JkI1?&gOj1=$y{#yw2?0 z&hGrq@Ep(bJkRu8&-Q%J_?*xBywCjH&;I<+03FZ*JoJ<=py(k6YR^eI=$08-P1n((?A{6LOs+(UDQT>)JUDwO1;!f-PBI~)KDGOQa#mF zUDZ~7)mWX?TD{d=-PK9_PztUP*H{t;Jun77fY@Vj*=bs@(~x z@CvMu3XD()WH1|Ha0iOO3AJ5=olpvpO&OQX24eu-nf{&FVE_hWklAc-2j+@^+N}qy zUDtY02zqb_cHjnx-2-A^-LjqC#{F=75e95f*N!lQt6d3#?Fe+O2saqo%%BaTtqXrm z3HUwVoIr#4UEX70-fnOQ-c1RhtqaP43%RfgjxgYzunMnG46oqZdO#awpa+7D+<;x% z628~W(A;=|--l3ejl4BiKa5a?09;`&X4 zT3!mNAmBCFM5+cQD_&;N{XF+RR`Kr~L|rtp{|V2)Us039;ON&GiL0?AdJxY>?PI zF9z9d_rU)2ckl+`ecy%9*0BEL0Uz)JJdmTyAL9bw=WhVxfPH|$j^C(0-a}ppsUG$Z zclvq--!xC&G`>fU@7+QV_o>kKmhRfDKIBAB_6S)2K91^3UxV=v5UxtOB3SCxse`X@ zEzG4$*Q-vQ`sjJXri`0Ag%ovKDf=9z`8rt;*Fil_N)t?2rk=V~m(GWW;O@gM$SQF?RA4f||-_ zPex;KjLG9D6+v7db1n4wir_0|)1Eb(Chg2$z<~u1CS2I?VZ@0QFJ|1>@ngu5B~PYY z+45z~nKf^gY$s!(jQ)4}+|g6V%}7N#OVM1d=nkDRKE|ZE+T-eusywV+!o?wnc*3QU z0!b@`CRa{+Kp;ejWWs*V~i-J#NkRD$Y8H5pIBioj2*_HuBe$@X@$R3{NqTWrckQu zF3#eVb51(z{<$4B*=V4eBH|DZHhKut4K~uCA&ojT0~oJs2ga zrWRe%v8IJ|Q3gX@R^#s|M>=^$US(Y2kiFyt+>a8OKqQF5P6R}XQ8x;~iNDnZ+IK~} zN;yfHR~)eej6yy#W*JvFG0qWA(&PjaCzFFCzI1bV0f!yR7|Y2ew@s4~Fo`XMlWzr_ zv67koirThCpZvXJRiOYS%Mm}DcKT_kqZX`GHpUnUwjQifOHZl0=Ch13z=Upx9@t>R zv(ZF*uMf7B7Pwn&T^m^uPADU$Z79Io8KzAIVyfYugv!0IB5L`zl=E3c#hW>PUCYrsCUY!36Y>vEL@1i>2XWMK;jgQJL) z!V$b-gCp*MqBInu4Lw*34>ZsLS^?1mV`Ef!mL)vz9IaEpdI1l-0<)`_O+2*A6jHw9 z9nT2IB3s-dqh_<4s+mnRsTmCmGQa{KEY3vXxk{$eAO<>wriOJ8gV~nHo6w{!ZFi`{ z9$`|uYXOL0Hrh{4d~>h&u%SLZ$Q4YMWRy3I0S$mUq`f*(8pWM0bgpsDXj0?1d@*T8 zXi~%?%@;Ci$wv`+2;o(@@;Nf#AXpm^Rt`$_8PG7xJL8#y)ZlVFV8gBgpr6p+gSyp^s+)zhAg{y#IEmv;?HlJmUbs`f9PZ~JtIN*h+dHX4afBd5@ zyuu6&Bsrgdo>?v#)l6Rs;jUd;#Gjntf*JEF#oZ!urW`4Q85gQvL$pPZPP8JURjVU$ z#ut&dc%&5JjED`XI3t4`L>Jhr*iB|+K_BHWe?Cc5gHz$9nPsp^g=mCZR#6OM_waeViEE z`K%N9a?%)BFhpUIO$g2O7B{Sq!WPBRfC?FR)D-z=WIS?Phtjk)n9a z%+=V8K_i{WNii=s1{2+Oy63Cw8+@r`+mKc5 zG%0wOJTUGiKgt^pS7R4g^!#-|z=5mi*!D@=;lvS{#ct1>lv!jYs}OLoZyuPHMd4H{ zIW!}S9%Q{VvkWdcdElyf!sUi5^}xP!8=g#z0+m!Tt`gr>gdKdqWXtg;UzQZ@w)j&w zxV5Mw5W5R52$em%i0nPuREYH!!VF!cL>kRt#%lid+uR}B;UdOb<|wr(koWp1Btq?9 zCm7-ox)jZR_!8PS6~fQEC^RZ8C5uFRwX~8s*erEf1-Kr`BzfPGZgx>9zIcV~Wv|9J zjze}CZq+bts+ZjkaiyE`#ah4c0 zg1r3llWz?g?QagT=+VwZpyyLW8)%RpI;7!pG~hCoLMNl2rAkns3FB8sIduMrQg$8! z)VBn8Eu-jxl~S7G=!glPu`25`1*#}ip8iUPYO%Tu20RLVf)yRlNqb58V}$^piL9tL zSvX+Z3T`fEn>!o=Cn{#_d(VfwZq*1x(bEOKz1wl)s4ZV?VmZsT2cnuR<&vu+4(M`z zNaSs^sW2elC=le^F6*jrNK53DVppI{u z6atZyN74_!QUpYq5)dMTw@MbM5Cb=mLHc?XzDbT45tmBZJD+(b!ZH?S`HpGn9|c*h zP1>6!As7yMCOZh5XY;J~FdrLP{t~{aoBgN}80s&^LlNeBGK$N;%{0I$k1vBJ7e2b8Qu!~gq7f$$y zV#tMNxW-&?g;i*f$ zBOm=K1s9XRX!IVQP_GOAOo@YN7yeKR9dQMM8YdB4NQS(OG>M90qJb9Z0Xsl8;i)i} zk`G2unRX-{F_^^d2!micCMUTA9M~LT5y^U@j@khaS$To4;0{}Zrq80h-FvcCA(GiR zJl9YqnLw)S7?$P{5K73b2HCW|Ne}cuidz)2{}{5Hpp>KFzO@>o7$Ov#z(u@?Mn@2; zunB`S+6~yT78NV6gyAFe2$WHy8>u8c8F8H7d7QA>iu&-1FtL{X=!jK7uT)_vX2}Ds z8i{h@l|twgP<+HGLCi9+NGu`^`N#t@h!Ww*1Im1<+cF(d!4jAmiqv2Y>q$532^rTg z8=Tt+yWuvwNWcD55GZyrup(TW(89KC36Y&p24w(7T!==wxfXltHa6J_e`%|V+Li^A zi(EL94V$>0P$)*Q2v&j;MVYI7@d%!13I@A24cf+SxhO{n8$A#uw;%|vPya*=IQY0iK%f?&0UoHxO4!B{ zTotb%ytU|#&~P;vyGgN|AkC?juXx0(2oAF7&_a+3St-!3;IjH!oj6;vDe7PS*#ofW1E1Z#|xj{2~mQ*OtjJbmyI2{J+!vxW)F2zHh=?MB6K(PuQ zXMqx}=!y|@1L5lgPv8VY`#r=7lbxwgep-cXe2@lXlH{NdHTyE2CAAt&|fw+)#!!g^$)VeYe`Ot{K(I#y=rwf@Ea|^eG;gCMl zPlJG$hOmnv^UEaq5M^ivWtawHRg$nl5mk7Fiz>m4Tq`hjziVW-o~R!WLoQ^~kB@MY z3X_z#sJ+5;gYR<-|8j)<*|&^{ul>3PX1Wn)wZP88^3?I;&HI3Lv(ikqb zqaFF^E%+EKV~IlJf{*@5y9R?o;ur%Su#d^PoXR;JF7u4k(7S_l%`w;jT6qD_fwSB3 zo1~Nq z`-qM)_?h=?FyJYVQ;{gw@Kn>YH8_AV!vY?GA9w94lN*C zlfjV$6PV=L4QPQDfSDa=o>BlvmXn6{@)wR6oC(^1mywNzfjidhmUAjqb7KhpQf-J} zY(d?@8+eJucCnWs(VJJ@CyTk5meUw7y$F)(1Y_U}X}BqNWCK#XgOYiLYJ|pU?r}}uw7T{U4t+yxD zotz*AcdOrsSOieDiO2(*RGA2D$*jOy721fpR9O|-K#_>>gzO{1FB$%#qr0n4;0b2n zu5DbUGN6H@gR_P+ibHTUK;qJ{ItpYV4Y3dl_z;W7au5pna1gN-B|%UK{tKnS~7F!%y6{H@|PiJJg<1(j>L z9x)R)^)54s5Su|0AHfS6%L_%|Uw5WCjvMCx2p>Jf&u}sm{QfeCc6rE>oM1bcTPOjO z8W0220GHbuu?`-w%6S_3Vv=WcyA&P-WA3*V*_KTk$agyDW z#5a4Eq|iowZaoA#GK_w@hBg(}^WPz%;{sx-;HWgM`dp5X+MZd}WC@!@z7jyTpp@~v z+mg}8#eqLYkyenGrm#MH5s=}al2Qp9&V?B^c)M5CPv*lOT!cP?+80$7cyC$@~QH_co8bp_IMF*~r+ zU^SYc$E0>X8@pn;yazFMzw@ zC6?M=1d^VRA-JN1RPaTCFsn|$Uv`^@Xb@JAAQ!5*9D;V9@nW~;1qq|zS}MWnK3p1W zGYTuSy}UfO4sDJ)nr5HaS{{uOBn56VFO`{Dh28}jZOq1^aD`@Aw+$g2U#iHs7%*_= z5XQrZ>5N_=8Qdv$^ze>`zUTz=j&`pBSb)8)F<>M*x0r+&CpKvl?yAoXG81wC2)-mY zZEUW@win+3F#-;sYyU*Iz7o_MokafKHL$4)CG)I4m1bJ`AZYh`4s4QIvZTo>a{jhdFZ03j^*gkzIKIZ z5C?5|g;HwuB8iI7eu{LFnf~zUD9IC<$|f=|iXuhWm-X7_)~lLjO1X7b1`ZjC(0+z% zcx7q0v1up;Y2SUR!GR5+C7+!xdsF+%OY5<6H7+UY8cBCG37V8+H)AlcwLFT6V3t+! zCB(^*PL4Dd+L`jsotLhZF>96!*Ue5!mv*XD$&t>NI~}E} zTF4C=cs zOc*hJQsrt!GuNwBPaQD|t0(z%o^HmlL79i;{4i_^xf)#fuq!w@;u2K@DKm{W+`UqX z9(^d1i#F3p6QMQ}Ml+2rXK1(~haGzOA&4Q0I3kH9ns_3LDXO?4iz0f~Ng?#TR|!*V zedLOB61P$_M}q*Em@EjJTqixozY zE2rG4lp}Y5;R73YD91^holv6GC6GbZ%5rzwI1qDDF>(hSc8sw@3p)Mzy`f5qeE!v{5R!G&9XK%oLQIE4gTL zRYg$M=2J=<35t_J1AcX^avnwYW+k2=HkdM;c^aTieHKCn3mG^t23>(6hM2m8>VoK^ zF=_tFDWj!SrdeVB`Da+7o&oqTp*X^ni!!gQl8YlIq12upES1rdm9^<)GH$J~G7u|z zgn{Y?8+bq@bb*e07gbmGXi-W#0jA8f(=;=*D~_KkrZGk#o^Y0!VYz75ub$Ihqm4P}pmPl_O)+E6Gt*?V_Sy+bqfCo^`~5fI zfeSu(-<%Ppj4_8Paz`h)TqaH81g&ycA#aqCh8s=-)!?(@np{a&p-<(eQk3lU#^#F5 z#bz2*Ki5MIVYs1)TBUr_yjriE0-5few1VeNUD=u(RAVOh)KYDc)@Xh`^dQC^ zsZ9Hd(b%AoB~=7B1Mv(9RFa|MKv|=@@{&)p5v||)R z!SNNuaD^sR*Aq-Ur4V`e+Sc5_hWQi=EsD}p*>quycmeB()RKmFA}NidT!tAFS{f{u zrj2bV!xe=4B`||2%wYzueTzeh3eTdEr36ncOMzt5nAi-|gi&^7;2M5HIZ7)H#377*~w1Mtt&!GL&&4X^3NO z)5w@3mV_3>;39DiBb(s{Lzn-gMo-HKY8V-)mkh7~2V$@T*LF0zv|x-VLnDUKCIYgH zV8UChK;pm%bwr>|t7$r!-$f+##m=mCaxLrxL7*8boZMzLjmpDuk`uB9_MjvY=|LJK z6|2S65Q)qXEN6-<%Vrd%xxG9^E+!NqStf{Wg&`((v#Z_h4(@qN@kym>5+F}#)u=|z zL#}9W$1<$snt8+{1Y=`|HxMW$tpFCE(o()s=wSytD3<;k($E7RgdvS5*^*Ptq$LNr z-c~|hk9PdhM}3vVK8_n(THzdXU(2cU_v7+C1-Xx?Li(Ra*~~xN>}@F9*q=n z2=fXmP%VUsm9jTuMZ9n-OvxdqTCo;qQT07&F-S+B=S&|buwR4>7fLv>3VCtjNIg*> zsMIs2*vN)Uva1MJ-lJoY0%g9fl1a7He4k_SczNDsxjgKa=Ev}iEb7~0W>IR0{t*+Ot47cg=XY>`;Dy0oHJj%d|; z1|*$8F6!8mSVtAt`CYpD%%Xd{Y1 z+h;V)p^eILx8C-?H@?qs=kpMXF10+HhRd@I-NZ=6`1y22?hvO_97sYEs;)LFq)$C( z215m@1S7@(92W2(3}GOHqZG7|@VbRC>DrK=iV@^es!OyzuWsLpvO zV2)U8#5FgGh|0w>riq^N1;`>AS&vZ({-lxEjKvWO?unn^(kQBG?wmjgUG7Mit$z!< zGNV0iXRdgswrYwpW{Wc{tL2#c-ZwWyS%v}!CK#en5kOTIuo@W0;X4<~FljQ-v-MEsw1l`o4{qcgA=wU+Ad8p17NW@1TBO2N zq|u8FMJ}{LQi)l%5JsF`26L%Z7gY|r_>rS&!WhVmV}Y5L=mC9ggq679Rxt(WphRql zQs!KQAz=gzA_h_H6LVdWVjLb(>;e)UjxpF1GeE?2EK_1~iZ(WlZfPq77Npk4kTJ#Vb&_D)kzz&=Q zlxRe(poT6KO&@q)7^H!II7xjK(F{sj0p5uLPDUqqLQ-tRA$*R>yalC{izWd@R*(T3 z;1(V603Owc=!nbBafwBYgsK!v&mad>M3o}E0e!f^55-lwB;&sjxvO=z`?+ z9+Ujg^D&WT{Qg@mEK2N12kXSfbdUvj_!kZIz)74%5iv$H)XiNT#`O3R3fTc07#vD) zkbdoAJS__GD2y%bQ!OnmNrV*Q9>7U_vBuEuw4%^SEvT}?w6b(L^x1oQpd(B#4) z%olP5idyIdB3(nNamUl#RW2CAUUn1~x*8ckL`&pMxva--(Etn7=L_IKfsw%v+0rhV z!#1$PHlTwv7zK`e$xi6v0bNEmXhX$4!!@wqQ%K7$CCE;&!$DpshTa=!F@|NOn^C~X zOMrw>{vnJXVU(cY38De{(k|L;6WjX?Rtq=Uw-os%Adh|(Nkp+(l+114s z%k|!K$k@ED+)C`!T9qTU%BOmqnr(nu;a(tXubZ`ct!iUDpl!ch$fAYKH1 zhzv+V1m@TQ$tcB5Jjd#+5l5^=DKwHlwWfxx&wYW_dB#;Pu!4GS1Y#^BVbqWUVURYC z93k=qmPl4tgiIe;iIBBs!(GTTL=j|w&O`>{82AA2nFW$81xC?V44DokfR4d9j=Uj{ zW;jhGE$B9ogEx>vs@9U@#1$1~WBFv*49Q|{u+(#?Q`ZF)F3xZe!Cm7582w5rS!Zet}i*k`C z-h?w{PyF;j_0@-n-Nk8Pi8oqX3#`}oFyCpKLpO}3+h{}NRKgt;9V*5Iq8P?yaEG2v z%QKiqDv)X=sOy4O!Ye2OI%KHFerzypm7s`~TPR1pJOx>#0UJ0-Ct!>R7S~CjE4n)e8of7Xfh8B$?XdQ(pX3&g{NlX;Vp_Gkb7-0C^(4&0|iPg`X z;>oXY#w!#F8)z7DnnAN#Z0t9mHCExtxB?y z4o1BlQ*c5H#l`LrMhx0g-jt>{oMtAX;bl0@tqx2qMxrcg?MJKvT!aN1cw8tQM@K;x z7_2O8v0Z+J7;bb5X)w|hvPFo6*!55@DltZ-HKe(`1#aNboCfM%4hUcj-!zEBG~k6q zTp4U>Ei?@ebq*h*tWR4wTXN*V*6hdUz}<69o5$e*5A4rL3q)P;<$n*kcpAP zRsy-g;snKTPO0s1j+MAc`52&5s0qf5)?Xb3YU~1^rsqJc!cp;9S3%d&p;<;;N^91XCLs%K zS*{&qg&xpIlk(MIfpSkeZZ7{2P9#v{- z&O3DqWiW$yN&^5Pg#8v69Ka8kkVjB0QVz+hR&Guu;KHC`BCRpeSN$7k`UkL#VolV< zqvg&7=$?;Y14siq3_l{^Np*OaP6Z?qw+qNRlMW z`mBYux+3Me<{%ZqWQJpQJZNZPBT!vK+gyWc6vM!+Ldg+BBZ!P0luS$YL~G!RL|hX^ zq=Lg~LpF@2yhVdhr3ZtML6=s9`Js~IpdV)Z+feZYk9ou{_$M2*n>p;j1y3|ZhsX{2 z@FT|-x)EN+1jw_VA7;G5pGnXwU|#PDMS1vew1pbno)t`#f)#lptNLved4}*L7r70T zrxEzpHQhwA?VTKa(XaA8Suadu)qhjzzeiMzv8Ew+6GzOi2(&_c?!)c z+yQPSn^7N7a@7R@-N6p9Kn>_h8*Bu6GzOt!26I{d#>1r_cPInGT3hCRPfQ%|M`*1! zS_Fv!Nqt?ZfXD`rILT}*lUg(eQP7n+6yq!8g$8&44y+h+SRqW1SFR3(8;wbAxQm^N zRVf(Coz9#H=FXF-Bf8!(r(H^W?_Z@Mif8hAX~SWIQWzCNhq7jv;f&pzLBY;Ib+h&6IdR zzrq(q6o{NgDy3OgG*p*@bOjuo^$YX>UqGP0fE~a@4DX4I*WQ4pTJ_Ma#`YT&*)f^x zS%e^u_+mz-ubO)BW0n9{{ z8#o&yumKNPg_!=IxKAE;=6iN*dis80i^ayqA=oXo>#0DaZt#n0l zkVYdN8?q%wDyYIoyu!*fhwRXX4l>hfb#(p>rAF_~hgmF#y51E%0%|T?2)p7sfTRb@#=2+Fuj8jt^( zgOo`pP*=BFa~271CBgPc4$YnqyksL)?~nOpa#fD>0T0-%x&C8(Cx%# z5z8IiSFs-o^zwR5p%)cHHGY;sJ44T|rxG1cNU4=3yQZ2RL`r$1hawcK!8~zM%+*}! z88a{@GZfo8ZCVfPKydNcVozW$>He9}&^RkNLKr|4aWKwWxcD+kNHg#;aOosQ?2fTF zib*KFr#|X(L{dEZCpp|Vbu$BqGPzSzwi&es_x=4DGSQ0wHw$t40Sy;s=#yMm+;N#a zsfQL#F~?ZNgrDSu{N9IF5Q|GD2h%ZYP2fZ!n2L(^#8X6Lr#yuyq0W_Yx=fT!I1&GO zj^W~CC!Cw$9AyIG`J>RG=uk|uosy)b7f;vR2?U{9wUSSaV&j_Ld)dv z;LZqkZW@DhO_vsB<1v}VIr#<-XxmTCabgffe@0w90>m|Kxhg4A#HmxlQ_Ps@spRO@ zt1}ZRRe^0;v`W(S*2 zKsj@c*^F7UT$&1%X+h(~jxlcL*1h?PZ$w-SSDMpiTbtTxp_?g-`1=)5Ww}~WE!4?X zv$WE7hYqc5Xr)4K#Js>^odpXTJKP{=s9c=3+|!;h?3^oAp)oYJyHQjr)lFQ=rrpjR zbQLE~jvQ4&PM@XzwO(-~3^Bq;qYOP9S&9=~nz_vzbG%_^o7|Y04JcP2y8{l(+`vHt z7PwQy5l-r2CYt4-BT5g;l3L1a-cZDq`fO{a%{L0+1eXV=$%dt~ZbNIDXn>l=9C4Vbqz^}2N#-S+ z1bZmX_xPJj4+G^w?k1h$y9p;y6&dp}??RI%PoN?yg+-+7n9Gqdt{dZyN`4aSMijM? z=2(@kVNU)$W?WgYKfy3ma5Gm*x)$4PwcVE6ZYN?6S#g>RsuWIKHR_H#vV@b#RZ?+8 zT{pT?swhVed2B8c4I;`fCVA=uuR}_q?=0q~(NI@C?8xJeJnZPovO9QP^_5fTG$t2} zBXXr&Q1Nv{r&sb@P>(m*NY@QJ*kD7Ex`cx%muA#z#+U)~D1(e0aM(Evx?DNZFXUdq zb5ViBQ;VTIaoJeOzE))n%ogD)<50qYBkZ+Q9$HE=HsI=`6tc|9EjQ*E8jj56%p9s@Qu!vYmMG-e% z2>y|~{$4RA>0E*Ys*^j|*a5CF3dKZBB* z48HsrvBSVH>>yE-r}~SNRE6Jg*s?NQoJ&}yJMoFh{%%?&xbDXy7PQceQD&@U=Bsa6 z-O!sWxz~?AOB~zC%cT_lDtU!Bnw3e#M0a^Ih*yqrgdP|%ChFWlMvenhEBxm%63J*j zWO>Qb`f?YEJ&;Ly-~kJ8;0BPrVmLrL$u+WJ78IcfTr9bUv#gR35I*BwonS^zyj4RR z-Vld5w8$9JFoxCi0Y>$ji)1F_A*2|@KaT0cW!$zd$r!~b_%ethpprzM1Z_d-{?f}> zUO_oG^uUKrj2M(8gO|YtW6 zEI6Q9#`j?3xny__C=6Lk-8{9J)it9TK6(js!p4$qEGJSfSz0MTSf90|A~KY*gNIm? zid<+Te&&!+;-u-UIBil!Fd5Ff?BD|&pr!}xE0%dwXDh93h(x9M#L$w~&UWqsPw|Wm z1((F6dRk?92f1EEijj+DJj)GX*x3%mK)^V$@iC0hLx=K^2g;;LGSKq=4`G0!F>{5g zjqtIgY+|_#^1%d8w@gdfbhAHUh2|Oc5s%TxvkZ}?kv~6COD-Hihq<^*lQG&!L11(h zgb2kaLeZN25VAaM`a~H)8cUFBB*`g;Wp%@GL=QMP22G9SCAvU~cxZJJ6q-dK0ufEB zPJzE^6ykmZv6c?m+E%x|)gn`p(rWDRFgWqbC~$Dg9XRodWuQ+|^&^o}^uSs-mAOI&Nt|S%xuIJF{lA zf@DiM$RF2NIyF2peq(oAM~6w3Y;A7U=&?x!*Z+N=-m zunL)_A0(mVwKlFybvO7-Sg8z#3lp2fUnUQ5nV{y~z~t#@AcSPk_=U zI3*FhSwS#-mO7j~%A|9$1eZX>ftF^xVm||8Np4QUobnWAKcITjyLP~W9_YcjSK$v& z1~`{6(*+lU;R&cnTcT*0c*w6<%+ipvPQExOZb-`oC;rNi9UshzNVH>QBVE#HMwTsn z!&xpam{W*L(oG_8)%2!0eXTG(WrUd5vGVU;is-|OpaiX2 z&vrqW=>ZN%2Szhx2@ZN~XovoG9U0@`KvOzYk~N{Qlo9c;4@6EX$|@F^uy(saaRiUe zL{y~mL^@e*ix&AsiUwv9-0AF;ae(^D=KjrG1#F$ z`kC!bic^W)fp;10EsVk=>$9hw9rbs_g&v_PO=+rdrjDRN4Kza&RA0mOuCI_eDkQhp z=SJ0nuJAQDAsT7sqJYUSU?Uj8eCIdc`HhhN^rw#rMpz#c($D_(8-WS$e;@qe7ytOZ zuLS`*yxIcW-NjNCV0a)DnVgff4Q{!k|o-!iOPL=*q$bvV`VmGHfRXXbgD30;!}wS^*bY zY$XKc8pLH9ilI&F3J4EnGxYxC643!2+F=w$(Kap-Qc@~MI)x~D3cS(_E#ha(&_W=P zMHQ1qyXqt&UP%LSan;nK%60>p$UvY5X^;Sk4VL1&u4iqO#43{KRiZJbueCgtz_;&1-uum0LE z{(#c`c#{5rvi;x>DE{G3DRmMRDuMo-GX9`473gm&_0KA=5-YP36_)GMe2NiZZU@qU z4f+7Soa}69XPEGbuz-j%+LFh>D8GD2dgdZ*053;+1IzB9%(!Dr?10C_zy?!hzcdAe z2IL3b?%~X3ia0@%&}((@CXa9l%Z!2snW+Y)hpwon;Km1+#z)VF@F%V-7{|&nI$>UV&4<;}V-e@s6Sp@`4e33gVoLfA-^J5=%_@!(H+NP?+HwOtBQzVHA;5 zn&!t9jKh9xPSVn5!HR=6`(qV?N{_-39gwpf#N`zvV>JjUALY&!ST7vO0UaVlJhn?E zngefUVkQnQ{y0>};?%-X7%x)JW~s=cGk(Kr7R)(xBS5AhC}Pk+#4fjT>+zQc#-@Q5$tooRsgHR953daD#HhLVEBJnmu#XaJ0S z=nD5O4|)nY`ZLa)Ge+{P2v-M0lXV&lGntaX8^ShC`bH8EB@T zg2NaFb{U8Pe~ObH%CQ_1wjS((9uig^(1LS<0zN3J8M;-PKw~Cw4;|Lw94VAS-EkU_ zp`wTrQZ^2}wg!kCVHJp>8Js~J;)g;-k!EX_96Poh)`1kyu~J+wWPMg0ywzKAmS+AT z(jBoYeyl+puoYX8YYk(uXQjzq&%qjw;awccp`hqJUI7_m(HgL;U1t_&AJ!a-AwBT0 z5K@5{0CzT8%tD&!e1S4TWlEpWjcDuFngp%bL39TL@1;nYpN?sC$r_{qTam#YSAiAwRc4g-YnwJSly+|2 zF=odh>u4qyu0dxL_F)&c9q1u$TaG-g^=owl_s-#A>!BWkcpWMwiECyVQ;O0UpaO=*8w@tL1)w09CP+tEv#*a){H&YX7?6{QL$3Sp~{-}Un@9n zba))37L7Z$W7EMJkRcTitPr*YyjYTAjfdh{0}| zmS&tGTZMKV9+n=S7#+}Jiqj6fT=*4IxuX8cg1fbh7w2sSd2wiwo6&-pb0JBr!Hshd zEwllfT`w4e%SKJs^-^IPxKu4(fnyIfP?L}Oln58le|Dp$Gb(86giKTB7}+5Gb0W$K;|@RigPYqAPk#9QdR8S)}!O%T`L5M5Yvc z!rgxH8ET?A)y3n7GIsZxgA`29mWAYI7v@QL>*sI zm9O}Sn_=}#H*Q$sXl;}h?>Osd>JCVBMmbbBIMigb6_3q9hpUWRJvL_(_8dhqXx~*i z+|g&h)ms}|XGQTGMsr{@hP5E79qu8S>7iqJc89T5levc& zG?YW}R%(A3iZK|7<92WV*swA7uyq)->G+(p8J*)N7V{dCan@sdm>4QWG-y^UhqC=L z!To$ZCxe{*s&{&a*G}J*QImY6m;9j_x}g2Q5Ap!Yr98@~yvn5<%db4jvAm(W+z--o zail>+6~PXANGvraW|~7N_+v8Of<3~HL08Zbpr_x!;xG1MX{@cuk_IITCa$CNe`17t z7&2pq!*OupNpqnX?8a6^hDj=cG}L7b>i#Na7RK$K2;E4eF0idrLIY~w*0w2p9E?E~ zG+|7X!hW1`aqG2OpV}J!PC4w_zN_pVkroxtAstSc695LSlEWE3l#g{|X;Bmv$GV#< zMaE+`BF7qzJJwx!TU=-cV>Z+oIz?cUTWBNl9Jm{q7iZeBJDHVvoVRlnMHxAR7CGTI zj&t^m;g}TNk(GNpxD9*Ob9rf-)*4>c7|L@Ra9xfo`&-8WzVVT*HNk}s9Fjp6gU=z9 z@4>slTf&)}vOV_ShaJ1uVZRw$mOplBe_8P)uPC;*87OqUGk(PBp}*zMx~!Svb(U&1 zbQ$s;n3v&Sl{UDg+LtT1XxTB0{&Qg!`e||^n8UyL#J!cpY%?kjx7w_K%w%04&1<@ z{IC^{WHEGtn8;#2^bF5|Vz{>JAGy*gT!=F{w2LDBsmWWv1n7*2alqz`mJ0AHe<-b7*fQJ)7x!nJ<#S2Y!hCZ@OW=hiWZN! zcC{Z;uq&`dczgzS(Z87f*ikfUhjz40m^|K-U&%ir^hjpc-@?!VncJZr@YldmSR5K# z905YyHFN0J(Njn7V8Vmw(3!h7@EkgI>)J^~ClMjKYtyDd948JW#E0X|g}f%tTE}L_ zn&rynDwVN<6ER|3hx6PyXRIm}QiQ2erB}wrm6V8d;yQcv?%k_rZy`OL7{#44cU7lO zYc7di)48sp!>i`7UbB|#6)RV)R&lDN2-nwk^q@kmOVJ|EYq=ElQS|C7R?^Zo1C3onr3#hw>C~%Rzm7e-_U+m+P1j`IyL9p5 zQI+?O-n@EDn$Wj5{~i;2^z!T5zmGq^en*P<_y1o=ABQNyNhz=B^3p4=bdm}uj_}8b z8*c1?0}D8W!G;@m6!McIowQPlGPx|rj4{hFgW@ZlRMJT&uDnuNF1jdV3^Om`@=7VL zEZCwWm2l!&F3nhDOe>Czk;WJq+M!1wj&PESE334UiX(+E(uW>;*pUa9Zj9ju9Ck=p z2AFhcct<6!yo4f(&oF}vGt+3(T0;*>(~OGzamb+_cPM#>BB{jq(o4!TgQ7Ijj6{%F zb~^MZr2YbF!_hU`ctn~&q-9DPrrI=GmO#@y(+o?6Rt94(#59I!N27WKs-py*!%;44 ztwofpEHPIWq@ng&mvI{v%2+egjE1Q|v`!-qLA@D=5M8Z36cJ5jeVQ6a&(PNFS=Lx1 zPDstBL}W9_sH)Z~ofs3=I+ z(-@Nqi>U}{&Aw(mWR5ij3#5`Uux3PDJ#@9>+E_X@vx*{$tda|(O3`(WF)vEim_R8l zEEzGc*!3W@2#{7&>SY8KhK8_(xFOWZotY8tD+3AIy? z{yDH#M4UPxgS<{IUQf+fv#ywf3M*~3y$UOBzb&`itF(>E-FMrKciz;cA|2p?PbYYG zf(u^Bbcsv1I4X@V{`hy&fj2qhs89|bd5*8gdFJYQ?zwlNb1t8G`pM^6i>-9RiOG8O zQOF@cF6c@tJGSx)E(x+yWg&Nb`NmJ2eD+E)BTgfYHk4|EXfCVx=cFSp=1!=LX;H~w zDh&R}y~8fz?n>*wYp91JjPQrPm3okwM;_-t$Ot1^j!FMduZYNuGRq;)^R?zs8VwP} z#AJE+U6bUuB|A7NN(wrV?nINyHL8fvJYpA6X{Heq$j3Kk4ajYHpFpg`q$S;Eh5{+tXhBJ&o47hN}WSaQnu|k5d zX4!@&WSJFV4q1`l)CMg<+Soa`Mh|zGC= zg7PqYt;AF`Y~-kBV-7>UCS+le2v%m5myQu9I4Y^sGzdZ$yD$YEr853UJH!SXv9(4W z=U|MpjFGZ)R01X3Y3JYucem;kE}rz1PCebpox)kCahp>db@*AietIWyi5qC=4jL5F zd9D(oV`z&!u_8tI?hSSbRDU`_2_SVsN4gtPS{B5gH)Mzn8)8vq+B6LmHA5L{^kE^~ zAciz(D0-cs9xghX3tTLybq-3OBKY_t27TtEG3f+<^x&fhv50kiS?hribr6Ni%Iz{eKG^bBlkWvs?o<}YPk!X}k zuO%Xuh^(qRUEK76DxF3&;z}ugIZ+p`*wcSFfvQ|60~a-7hW=J9g-wv{uqKdsQ8Tn5 z#OIl;rqd{hMVN&Wapa;!pCO_vaA7RO2!(-+k(1HnbXZ1_!yynUQE9t~4%MQ}Bco!* zVe_>cjx3BZX@TXpT(S!7WiPs&Fxy}VGZlK|F&7QfghZY6if^ezjn62H*qX)=-PDyf zCYhOXRRZ70w5||-I%_JLA(1;$q#)#cCRs4~E8Do{AQiJpK@P(fjS-|Sh1BjfoPn-l z`PXQ~XiHl{daC>jxpyt)iTE8|ct)I=fjFa|LQ8%cMN-YRxvpaa2mtIBIO zMDRpHIibiHB1#a9QiUT0)h>tu5TB|iR?6EiF?y$tWC-TpH^YK8_6lL@QzJ8 z$|8;B&_*lEYGkQef^=e_x)R=9#a9ZG8nSHggS%o0ad{PU6{CcU4l&j_>X8tS4NNCz zDV2t&VzpwdHHt!0`8Co}<9}(l8IGuE%AGOGmTXcY3M&bcc#+Nutu* z=qYe9Bg-NWQ8t+?CaM4mbLPxKduazF;q@Oqm zNI30y~btK>9;L4M7glpbR|H37_y7k`}Lk zb(*q49E48j1cTRBBV1v2-lGkmxQkfFnmr2~)yX^{5O9`TWo^;sUHnl zAMFv5@*$B3$p*pZ{sU!T2Y(?wpXLdtAX&|jJeZ|I%A;KQ^H+uTCOtSJapEQ=R2D8_ zRD9GTp_U+35`E+IJAr003#U=+W;-vkBdlXGd=hAPz?1f}AlgGEsCHHxLLeqcZTUlO zG?XCphal*L3nHcmV(!~M=I08Z@NF7{TwX&&Hn?+4m^-;} zP1 z_c7+c84ltGWzYw&fDGZI2y0?ns;EY;m=>OKMDSJ@Oo5@_v_!sQ68;q`t0){wBpbP4 zR)O(sn8l8w1SyG=3bIZ5x+BAjptJ_01Gpd$?8r*A=XTk>mT5-8G>nKz{;zf(~f z;vufkgIhWcxuzhvGm1Xs2wGAGWFQ7%@RcIipD;pbBQkE$R+(`kBPwE>$EIykGL)@) zmyuQqeG> zop3~yq6?ki300*cb$M-{AW1*Lg@0Ek%3x_x^(5-XA1+dw4I`Z7N+6WjQ4!=SG6NZ$ z(Isw>2dXwE67q;mvT8<%e}6$@_hVVgqe9aOGXK+rrI~YSL0t|~38ip6Um*@c*8U)z z5M^t{J8LIYQL=Umm}&okK5tQgLs1#JV_CMS8_XbnnY9k=;586uh7JK?tZ=DKA}BEO zpREyj_b@h3a~LpEcVgfMjxZ%w(xPjGS4W2`MdS+qDK^{)W7Ua5JM=%50uhg~ijBk( zOT#Zh#viWW5nGyAvcU`zk+aQWD^0tkKPr(6>9zn#AErZmH<}4+JCQ+Z9}o$V^ubVf zYdRGPxcQ-b1-Z6=E2Q&Lk#M`C`q3UF^E+VTBsKY3EKwq&c70u{JB_6XbwCGfKnH&T zBpih!g0+Lp#UsG^1_xROX&?q=FtnXebrY(X=<}KZ$URB*e=)H-dB#T>{`Gu$@Kj|Y z1`1ROt)L4Z#0>0b2V>9!Niwv9l9r|NmoZU0VWmD}G6tDC24ql}QCU?8QX z+Y$W(K-Um-h$2|!iwnr}r9Z`(U&>mqc7QLE47tz>W96;NAXg4C4W-~a?i%PBZrbN zCfRRH=oW@*gU8ls1^6LI(_XA}sZ7$Zw8;q6=Odb+!q3+vPSPL#j9|u&fC�MY({7 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

QAjoAPT-dfpfUyI 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%0AsH?Wd6ga2WUaFa!K-5Q!R6eIO(zqA>^41 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 zibww +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "language.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "button.h" + + + +#define DELAY1 0.4f +#define DELAY2 0.1f + + + +// Constructeur de l'objet. + +CButton::CButton(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); + + m_bCapture = FALSE; + m_bImmediat = FALSE; + m_bRepeat = FALSE; + m_repeat = 0.0f; +} + +// Destructeur de l'objet. + +CButton::~CButton() +{ + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CButton::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + + if ( icon == -1 ) + { + char name[100]; + char* p; + + GetResource(RES_EVENT, eventMsg, name); + p = strchr(name, '\\'); + if ( p != 0 ) *p = 0; + SetName(name); + } + + return TRUE; +} + + +// Gestion d'un événement. + +BOOL CButton::EventProcess(const Event &event) +{ + if ( (m_state & STATE_VISIBLE) == 0 ) return TRUE; + if ( m_state & STATE_DEAD ) return TRUE; + + CControl::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + if ( m_bRepeat && m_repeat != 0.0f ) + { + m_repeat -= event.rTime; + if ( m_repeat <= 0.0f ) + { + m_repeat = DELAY2; + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + return FALSE; + } + } + } + + if ( event.event == EVENT_LBUTTONDOWN && + (m_state & STATE_VISIBLE) && + (m_state & STATE_ENABLE) ) + { + if ( CControl::Detect(event.pos) ) + { + m_bCapture = TRUE; + m_repeat = DELAY1; + + if ( m_bImmediat || m_bRepeat ) + { + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + } + return FALSE; + } + } + + if ( event.event == EVENT_MOUSEMOVE && m_bCapture ) + { + } + + if ( event.event == EVENT_LBUTTONUP && m_bCapture ) + { + if ( CControl::Detect(event.pos) ) + { + if ( !m_bImmediat && !m_bRepeat ) + { + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + } + } + + m_bCapture = FALSE; + m_repeat = 0.0f; + } + + return TRUE; +} + + +// Dessine le bouton. + +void CButton::Draw() +{ + FPOINT pos, dim, uv1, uv2; +#if !_NEWLOOK + float dp; +#endif + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + if ( m_state & STATE_WARNING ) // hachures jaunes-noires ? + { + pos.x = m_pos.x-( 8.0f/640.0f); + pos.y = m_pos.y-( 4.0f/480.0f); + dim.x = m_dim.x+(16.0f/640.0f); + dim.y = m_dim.y+( 8.0f/480.0f); + if ( m_state & STATE_SHADOW ) + { + DrawShadow(pos, dim); + } + DrawWarning(pos, dim); + } + + if ( m_state & STATE_SHADOW ) + { + DrawShadow(m_pos, m_dim); + } + + CControl::Draw(); + +#if !_NEWLOOK + if ( m_name[0] != 0 && // bouton avec nom ? + (m_state & STATE_CARD ) == 0 && + (m_state & STATE_SIMPLY) == 0 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + + dp = 0.5f/256.0f; + + uv1.x = 128.0f/256.0f; + uv1.y = 96.0f/256.0f; + uv2.x = 136.0f/256.0f; + uv2.y = 128.0f/256.0f; + + if ( (m_state & STATE_ENABLE) == 0 ) + { + uv1.x += 16.0f/256.0f; + uv2.x += 16.0f/256.0f; + } + + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + pos.y = m_pos.y+5.0f/480.0f; + dim.y = m_dim.y-10.0f/480.0f; + pos.x = m_pos.x+5.0f/640.0f; + dim.x = 3.0f/640.0f; + DrawIcon(pos, dim, uv1, uv2, 0.0f); + + uv1.x += 8.0f/256.0f; + uv2.x += 8.0f/256.0f; + pos.x = m_pos.x+m_dim.x-5.0f/640.0f-3.0f/640.0f; + DrawIcon(pos, dim, uv1, uv2, 0.0f); + } +#endif +} + + +// Gestion du mode immédiat, qui envoie l'événement "pressé" +// avant que le bouton de la souris soit relâché. + +void CButton::SetImmediat(BOOL bImmediat) +{ + m_bImmediat = bImmediat; +} + +BOOL CButton::RetImmediat() +{ + return m_bImmediat; +} + + +// Gestion du mode "répétition automatique", lorsque le bouton +// de la souris est maintenu pressé. + +void CButton::SetRepeat(BOOL bRepeat) +{ + m_bRepeat = bRepeat; +} + +BOOL CButton::RetRepeat() +{ + return m_bRepeat; +} + diff --git a/src/button.h b/src/button.h new file mode 100644 index 00000000..1e6ef90f --- /dev/null +++ b/src/button.h @@ -0,0 +1,42 @@ +// button.h + +#ifndef _BUTTON_H_ +#define _BUTTON_H_ + + +#include "control.h" + + +class CD3DEngine; + + + +class CButton : public CControl +{ +public: + CButton(CInstanceManager* iMan); + ~CButton(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + BOOL EventProcess(const Event &event); + + void Draw(); + + void SetImmediat(BOOL bRepeat); + BOOL RetImmediat(); + + void SetRepeat(BOOL bRepeat); + BOOL RetRepeat(); + +protected: + +protected: + BOOL m_bCapture; + BOOL m_bImmediat; + BOOL m_bRepeat; + float m_repeat; +}; + + +#endif //_BUTTON_H_ diff --git a/src/camera.cpp b/src/camera.cpp new file mode 100644 index 00000000..21371dc7 --- /dev/null +++ b/src/camera.cpp @@ -0,0 +1,2095 @@ +// camera.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "language.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "terrain.h" +#include "water.h" +#include "object.h" +#include "physics.h" +#include "camera.h" + + + + +// Constructeur de l'objet. + +CCamera::CCamera(CInstanceManager* iMan) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_CAMERA, this); + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + + m_type = CAMERA_FREE; + m_smooth = CS_NORM; + m_cameraObj = 0; + + m_eyeDistance = 10.0f; + m_initDelay = 0.0f; + + m_actualEye = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_actualLookat = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_finalEye = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_finalLookat = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_normEye = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_normLookat = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_focus = 1.0f; + + m_bRightDown = FALSE; + m_rightPosInit = FPOINT(0.5f, 0.5f); + m_rightPosCenter = FPOINT(0.5f, 0.5f); + m_rightPosMove = FPOINT(0.5f, 0.5f); + + m_eyePt = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_directionH = 0.0f; + m_directionV = 0.0f; + m_heightEye = 20.0f; + m_heightLookat = 0.0f; + m_speed = 2.0f; + + m_backDist = 0.0f; + m_backMin = 0.0f; + m_addDirectionH = 0.0f; + m_addDirectionV = 0.0f; + m_bTransparency = FALSE; + + m_fixDist = 0.0f; + m_fixDirectionH = 0.0f; + m_fixDirectionV = 0.0f; + + m_visitGoal = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_visitDist = 0.0f; + m_visitTime = 0.0f; + m_visitType = CAMERA_NULL; + m_visitDirectionH = 0.0f; + m_visitDirectionV = 0.0f; + + m_editHeight = 40.0f; + + m_remotePan = 0.0f; + m_remoteZoom = 0.0f; + + m_mouseDirH = 0.0f; + m_mouseDirV = 0.0f; + m_mouseMarging = 0.01f; + + m_motorTurn = 0.0f; + + m_centeringPhase = CP_NULL; + m_centeringAngleH = 0.0f; + m_centeringAngleV = 0.0f; + m_centeringDist = 0.0f; + m_centeringCurrentH = 0.0f; + m_centeringCurrentV = 0.0f; + m_centeringTime = 0.0f; + m_centeringProgress = 0.0f; + + m_effectType = CE_NULL; + m_effectPos = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_effectForce = 0.0f; + m_effectProgress = 0.0f; + m_effectOffset = D3DVECTOR(0.0f, 0.0f, 0.0f); + + m_scriptEye = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_scriptLookat = D3DVECTOR(0.0f, 0.0f, 0.0f); + + m_bEffect = TRUE; + m_bCameraScroll = TRUE; + m_bCameraInvertX = FALSE; + m_bCameraInvertY = FALSE; +} + +// Destructeur de l'objet. + +CCamera::~CCamera() +{ +} + + +void CCamera::SetEffect(BOOL bEnable) +{ + m_bEffect = bEnable; +} + +void CCamera::SetCameraScroll(BOOL bScroll) +{ + m_bCameraScroll = bScroll; +} + +void CCamera::SetCameraInvertX(BOOL bInvert) +{ + m_bCameraInvertX = bInvert; +} + +void CCamera::SetCameraInvertY(BOOL bInvert) +{ + m_bCameraInvertY = bInvert; +} + + +// Retourne une force additionnelle pour tourner. + +float CCamera::RetMotorTurn() +{ + if ( m_type == CAMERA_BACK ) return m_motorTurn; + return 0.0f; +} + + + +// Initialise la caméra. + +void CCamera::Init(D3DVECTOR eye, D3DVECTOR lookat, float delay) +{ + D3DVECTOR vUpVec; + + m_initDelay = delay; + + eye.y += m_terrain->RetFloorLevel(eye, TRUE); + lookat.y += m_terrain->RetFloorLevel(lookat, TRUE); + + m_type = CAMERA_FREE; + m_eyePt = eye; + + m_directionH = RotateAngle(eye.x-lookat.x, eye.z-lookat.z)+PI/2.0f; + m_directionV = -RotateAngle(Length2d(eye, lookat), eye.y-lookat.y); + + m_eyeDistance = 10.0f; + m_heightLookat = 10.0f; + m_backDist = 30.0f; + m_backMin = 10.0f; + m_addDirectionH = 0.0f; + m_addDirectionV = -PI*0.05f; + m_fixDist = 50.0f; + m_fixDirectionH = PI*0.25f; + m_fixDirectionV = -PI*0.10f; + m_centeringPhase = CP_NULL; + m_actualEye = m_eyePt; + m_actualLookat = LookatPoint(m_eyePt, m_directionH, m_directionV, 50.0f); + m_finalEye = m_actualEye; + m_finalLookat = m_actualLookat; + m_scriptEye = m_actualEye; + m_scriptLookat = m_actualLookat; + m_focus = 1.00f; + m_remotePan = 0.0f; + m_remoteZoom = 0.0f; + + FlushEffect(); + FlushOver(); + SetType(CAMERA_FREE); +} + + +// Donne l'objet pilotant la caméra. + +void CCamera::SetObject(CObject* object) +{ + m_cameraObj = object; +} + +CObject* CCamera::RetObject() +{ + return m_cameraObj; +} + + +// Modifie le niveau de transparence d'un objet et des objets +// transportés (pile & fret). + +void SetTransparency(CObject* pObj, float value) +{ + CObject* pFret; + + pObj->SetTransparency(value); + + pFret = pObj->RetFret(); + if ( pFret != 0 ) + { + pFret->SetTransparency(value); + } + + pFret = pObj->RetPower(); + if ( pFret != 0 ) + { + pFret->SetTransparency(value); + } +} + +// Modifie le type de la caméra. + +void CCamera::SetType(CameraType type) +{ + CObject* pObj; + ObjectType oType; + D3DVECTOR vUpVec; + int i; + + m_remotePan = 0.0f; + m_remoteZoom = 0.0f; + + if ( m_type == CAMERA_BACK && m_bTransparency ) + { + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetTruck() ) continue; // pile ou fret? + + SetTransparency(pObj, 0.0f); // objet opaque + } + } + m_bTransparency = FALSE; + + if ( type == CAMERA_INFO || + type == CAMERA_VISIT ) // xx -> info ? + { + m_normEye = m_engine->RetEyePt(); + m_normLookat = m_engine->RetLookatPt(); + + m_engine->SetFocus(1.00f); // normal + m_type = type; + return; + } + + if ( m_type == CAMERA_INFO || + m_type == CAMERA_VISIT ) // info -> xx ? + { + m_engine->SetFocus(m_focus); // remet focus initial + m_type = type; + + vUpVec = D3DVECTOR(0.0f, 1.0f, 0.0f); + SetViewParams(m_normEye, m_normLookat, vUpVec); + return; + } + + if ( m_type == CAMERA_BACK && type == CAMERA_FREE ) // back -> free ? + { + m_eyePt = LookatPoint(m_eyePt, m_directionH, m_directionV, -50.0f); + } + + if ( m_type == CAMERA_BACK && type == CAMERA_EDIT ) // back -> edit ? + { + m_eyePt = LookatPoint(m_eyePt, m_directionH, m_directionV, -1.0f); + } + + if ( m_type == CAMERA_ONBOARD && type == CAMERA_FREE ) // onboard -> free ? + { + m_eyePt = LookatPoint(m_eyePt, m_directionH, m_directionV, -30.0f); + } + + if ( m_type == CAMERA_ONBOARD && type == CAMERA_EDIT ) // onboard -> edit ? + { + m_eyePt = LookatPoint(m_eyePt, m_directionH, m_directionV, -30.0f); + } + + if ( m_type == CAMERA_ONBOARD && type == CAMERA_EXPLO ) // onboard -> explo ? + { + m_eyePt = LookatPoint(m_eyePt, m_directionH, m_directionV, -50.0f); + } + + if ( m_type == CAMERA_BACK && type == CAMERA_EXPLO ) // back -> explo ? + { + m_eyePt = LookatPoint(m_eyePt, m_directionH, m_directionV, -20.0f); + } + + if ( type == CAMERA_FIX || + type == CAMERA_PLANE ) + { + AbortCentering(); // stoppe cadrage spécial + } + + m_fixDist = 50.0f; + if ( type == CAMERA_PLANE ) + { + m_fixDist = 60.0f; + } + + if ( type == CAMERA_BACK ) + { + AbortCentering(); // stoppe cadrage spécial + m_addDirectionH = 0.0f; + m_addDirectionV = -PI*0.05f; + + if ( m_cameraObj == 0 ) oType = OBJECT_NULL; + else oType = m_cameraObj->RetType(); + + m_backDist = 30.0f; + if ( oType == OBJECT_BASE ) m_backDist = 200.0f; + if ( oType == OBJECT_HUMAN ) m_backDist = 20.0f; + if ( oType == OBJECT_TECH ) m_backDist = 20.0f; + if ( oType == OBJECT_FACTORY ) m_backDist = 50.0f; + if ( oType == OBJECT_RESEARCH ) m_backDist = 40.0f; + if ( oType == OBJECT_DERRICK ) m_backDist = 40.0f; + if ( oType == OBJECT_REPAIR ) m_backDist = 35.0f; + if ( oType == OBJECT_DESTROYER) m_backDist = 35.0f; + if ( oType == OBJECT_TOWER ) m_backDist = 45.0f; + if ( oType == OBJECT_NUCLEAR ) m_backDist = 70.0f; + if ( oType == OBJECT_PARA ) m_backDist = 180.0f; + if ( oType == OBJECT_SAFE ) m_backDist = 50.0f; + if ( oType == OBJECT_HUSTON ) m_backDist = 120.0f; + + m_backMin = m_backDist/3.0f; + if ( oType == OBJECT_HUMAN ) m_backMin = 10.0f; + if ( oType == OBJECT_TECH ) m_backMin = 10.0f; + if ( oType == OBJECT_FACTORY ) m_backMin = 30.0f; + if ( oType == OBJECT_RESEARCH ) m_backMin = 20.0f; + if ( oType == OBJECT_NUCLEAR ) m_backMin = 32.0f; + if ( oType == OBJECT_PARA ) m_backMin = 40.0f; + if ( oType == OBJECT_SAFE ) m_backMin = 25.0f; + if ( oType == OBJECT_HUSTON ) m_backMin = 80.0f; + } + + if ( type != CAMERA_ONBOARD && m_cameraObj != 0 ) + { + m_cameraObj->SetGunGoalH(0.0f); // met le canon droit + } + + if ( type == CAMERA_ONBOARD ) + { + m_focus = 1.50f; // grand-angle + } + else + { + m_focus = 1.00f; // normal + } + m_engine->SetFocus(m_focus); + + m_type = type; + + SetSmooth(CS_NORM); +} + +CameraType CCamera::RetType() +{ + return m_type; +} + + +// Gestion du mode de lissage. + +void CCamera::SetSmooth(CameraSmooth type) +{ + m_smooth = type; +} + +CameraSmooth CCamera::RetSmoth() +{ + return m_smooth; +} + + +// Gestion de la distance de recul. + +void CCamera::SetDist(float dist) +{ + m_fixDist = dist; +} + +float CCamera::RetDist() +{ + return m_fixDist; +} + + +// Gestion de l'angle en mode CAMERA_FIX. + +void CCamera::SetFixDirection(float angle) +{ + m_fixDirectionH = angle; +} + +float CCamera::RetFixDirection() +{ + return m_fixDirectionH; +} + + +// Gestion de la télécommande panoramique de la caméra. + +void CCamera::SetRemotePan(float value) +{ + m_remotePan = value; +} + +float CCamera::RetRemotePan() +{ + return m_remotePan; +} + +// Gestion de la télécommande zoom (0..1) de la caméra. + +void CCamera::SetRemoteZoom(float value) +{ + value = Norm(value); + + if ( m_type == CAMERA_BACK ) + { + m_backDist = m_backMin+(200.0f-m_backMin)*value; + } + + if ( m_type == CAMERA_FIX || + m_type == CAMERA_PLANE ) + { + m_fixDist = 10.0f+(200.0f-10.0f)*value; + } +} + +float CCamera::RetRemoteZoom() +{ + if ( m_type == CAMERA_BACK ) + { + return (m_backDist-m_backMin)/(200.0f-m_backMin); + } + + if ( m_type == CAMERA_FIX || + m_type == CAMERA_PLANE ) + { + return (m_fixDist-10.0f)/(200.0f-10.0f); + } + return 0.0f; +} + + + +// Début d'une visite circulaire avec la caméra. + +void CCamera::StartVisit(D3DVECTOR goal, float dist) +{ + m_visitType = m_type; + SetType(CAMERA_VISIT); + m_visitGoal = goal; + m_visitDist = dist; + m_visitTime = 0.0f; + m_visitDirectionH = 0.0f; + m_visitDirectionV = -PI*0.10f; +} + +// Fin d'une visite circulaire avec la caméra. + +void CCamera::StopVisit() +{ + SetType(m_visitType); // remet le type initial +} + + +// Retourne le point de vue de la caméra. + +void CCamera::RetCamera(D3DVECTOR &eye, D3DVECTOR &lookat) +{ + eye = m_eyePt; + lookat = LookatPoint(m_eyePt, m_directionH, m_directionV, 50.0f); +} + + +// Spécifie un mouvement spécial de caméra pour cadrer une action. + +BOOL CCamera::StartCentering(CObject *object, float angleH, float angleV, + float dist, float time) +{ + if ( m_type != CAMERA_BACK ) return FALSE; + if ( object != m_cameraObj ) return FALSE; + + if ( m_centeringPhase != CP_NULL ) return FALSE; + + if ( m_addDirectionH > PI ) + { + angleH = PI*2.0f-angleH; + } + + m_centeringPhase = CP_START; + m_centeringAngleH = angleH; + m_centeringAngleV = angleV; + m_centeringDist = dist; + m_centeringCurrentH = 0.0f; + m_centeringCurrentV = 0.0f; + m_centeringTime = time; + m_centeringProgress = 0.0f; + + return TRUE; +} + +// Termine un mouvement spécial de caméra pour cadrer une action. + +BOOL CCamera::StopCentering(CObject *object, float time) +{ + if ( m_type != CAMERA_BACK ) return FALSE; + if ( object != m_cameraObj ) return FALSE; + + if ( m_centeringPhase != CP_START && + m_centeringPhase != CP_WAIT ) return FALSE; + + m_centeringPhase = CP_STOP; + + if ( m_centeringAngleH != 99.9f ) + { + m_centeringAngleH = m_centeringCurrentH; + } + if ( m_centeringAngleV != 99.9f ) + { + m_centeringAngleV = m_centeringCurrentV; + } + + m_centeringTime = time; + m_centeringProgress = 0.0f; + + return TRUE; +} + +// Stoppe le cadrage spécial dans la position actuelle. + +void CCamera::AbortCentering() +{ + if ( m_type == CAMERA_INFO || + m_type == CAMERA_VISIT ) return; + + if ( m_centeringPhase == CP_NULL ) return; + + m_centeringPhase = CP_NULL; + + if ( m_centeringAngleH != 99.9f ) + { + m_addDirectionH = m_centeringCurrentH; + } + if ( m_centeringAngleV != 99.9f ) + { + m_addDirectionV = m_centeringCurrentV; + } +} + + + +// Supprime l'effet spécial avec la caméra. + +void CCamera::FlushEffect() +{ + m_effectType = CE_NULL; + m_effectForce = 0.0f; + m_effectProgress = 0.0f; + m_effectOffset = D3DVECTOR(0.0f, 0.0f, 0.0f); +} + +// Démarre un effet spécial avec la caméra. + +void CCamera::StartEffect(CameraEffect effect, D3DVECTOR pos, float force) +{ + if ( !m_bEffect ) return; + + m_effectType = effect; + m_effectPos = pos; + m_effectForce = force; + m_effectProgress = 0.0f; +} + +// Fait progresser l'effet de la caméra. + +void CCamera::EffectFrame(const Event &event) +{ + float dist, force; + + if ( m_type == CAMERA_INFO || + m_type == CAMERA_VISIT ) return; + + if ( m_effectType == CE_NULL ) return; + + m_effectOffset = D3DVECTOR(0.0f, 0.0f, 0.0f); + force = m_effectForce; + + if ( m_effectType == CE_TERRAFORM ) + { + m_effectProgress += event.rTime*0.7f; + m_effectOffset.x = (Rand()-0.5f)*10.0f; + m_effectOffset.y = (Rand()-0.5f)*10.0f; + m_effectOffset.z = (Rand()-0.5f)*10.0f; + + force *= 1.0f-m_effectProgress; + } + + if ( m_effectType == CE_EXPLO ) + { + m_effectProgress += event.rTime*1.0f; + m_effectOffset.x = (Rand()-0.5f)*5.0f; + m_effectOffset.y = (Rand()-0.5f)*5.0f; + m_effectOffset.z = (Rand()-0.5f)*5.0f; + + force *= 1.0f-m_effectProgress; + } + + if ( m_effectType == CE_SHOT ) + { + m_effectProgress += event.rTime*1.0f; + m_effectOffset.x = (Rand()-0.5f)*2.0f; + m_effectOffset.y = (Rand()-0.5f)*2.0f; + m_effectOffset.z = (Rand()-0.5f)*2.0f; + + force *= 1.0f-m_effectProgress; + } + + if ( m_effectType == CE_CRASH ) + { + m_effectProgress += event.rTime*5.0f; + m_effectOffset.y = sinf(m_effectProgress*PI)*1.5f; + m_effectOffset.x = (Rand()-0.5f)*1.0f*(1.0f-m_effectProgress); + m_effectOffset.z = (Rand()-0.5f)*1.0f*(1.0f-m_effectProgress); + } + + if ( m_effectType == CE_VIBRATION ) + { + m_effectProgress += event.rTime*0.1f; + m_effectOffset.y = (Rand()-0.5f)*1.0f*(1.0f-m_effectProgress); + m_effectOffset.x = (Rand()-0.5f)*1.0f*(1.0f-m_effectProgress); + m_effectOffset.z = (Rand()-0.5f)*1.0f*(1.0f-m_effectProgress); + } + + if ( m_effectType == CE_PET ) + { + m_effectProgress += event.rTime*5.0f; + m_effectOffset.x = (Rand()-0.5f)*0.2f; + m_effectOffset.y = (Rand()-0.5f)*2.0f; + m_effectOffset.z = (Rand()-0.5f)*0.2f; + } + + dist = Length(m_eyePt, m_effectPos); + dist = Norm((dist-100.f)/100.0f); + + force *= 1.0f-dist; +#if _TEEN + force *= 2.0f; +#endif + m_effectOffset *= force; + + if ( m_effectProgress >= 1.0f ) + { + FlushEffect(); + } +} + + +// Supprime l'effet de superposition au premier plan. + +void CCamera::FlushOver() +{ + m_overType = OE_NULL; + m_overColorBase.r = 0.0f; // noir + m_overColorBase.g = 0.0f; + m_overColorBase.b = 0.0f; + m_overColorBase.a = 0.0f; + m_engine->SetOverColor(); // rien +} + +// Spécifie la couleur de base. + +void CCamera::SetOverBaseColor(D3DCOLORVALUE color) +{ + m_overColorBase = color; +} + +// Démarre un effet de superposition au premier plan. + +void CCamera::StartOver(OverEffect effect, D3DVECTOR pos, float force) +{ + D3DCOLOR color; + float dist, decay; + + m_overType = effect; + m_overTime = 0.0f; + + if ( m_overType == OE_BLITZ ) decay = 400.0f; + else decay = 100.0f; + dist = Length(m_eyePt, pos); + dist = (dist-decay)/decay; + if ( dist < 0.0f ) dist = 0.0f; + if ( dist > 1.0f ) dist = 1.0f; + + m_overForce = force * (1.0f-dist); + + if ( m_overType == OE_BLOOD ) + { + m_overColor.r = 0.8f; + m_overColor.g = 0.1f; + m_overColor.b = 0.1f; // rouge + m_overMode = D3DSTATETCb; + + m_overFadeIn = 0.4f; + m_overFadeOut = 0.8f; + m_overForce = 1.0f; + } + + if ( m_overType == OE_FADEINw ) + { + m_overColor.r = 1.0f; + m_overColor.g = 1.0f; + m_overColor.b = 1.0f; // blanc + m_overMode = D3DSTATETCb; + + m_overFadeIn = 0.0f; + m_overFadeOut =20.0f; + m_overForce = 1.0f; + } + + if ( m_overType == OE_FADEOUTw ) + { + m_overColor.r = 1.0f; + m_overColor.g = 1.0f; + m_overColor.b = 1.0f; // blanc + m_overMode = D3DSTATETCb; + + m_overFadeIn = 6.0f; + m_overFadeOut = 100000.0f; + m_overForce = 1.0f; + } + + if ( m_overType == OE_FADEOUTb ) + { + color = m_engine->RetFogColor(1); // couleur brouillard sous-marin + m_overColor = RetColor(color); + m_overMode = D3DSTATETCw; + + m_overFadeIn = 4.0f; + m_overFadeOut = 100000.0f; + m_overForce = 1.0f; + } + + if ( m_overType == OE_BLITZ ) + { + m_overColor.r = 0.9f; + m_overColor.g = 1.0f; + m_overColor.b = 1.0f; // blanc-cyan + m_overMode = D3DSTATETCb; + + m_overFadeIn = 0.0f; + m_overFadeOut = 1.0f; + } +} + +// Fait progresser l'effet de superposition au premier plan. + +void CCamera::OverFrame(const Event &event) +{ + D3DCOLORVALUE color; + float intensity; + + if ( m_type == CAMERA_INFO || + m_type == CAMERA_VISIT ) return; + + if ( m_overType == OE_NULL ) + { + return; + } + + m_overTime += event.rTime; + + if ( m_overType == OE_BLITZ ) + { + if ( rand()%2 == 0 ) + { + color.r = m_overColor.r*m_overForce; + color.g = m_overColor.g*m_overForce; + color.b = m_overColor.b*m_overForce; + } + else + { + color.r = 0.0f; + color.g = 0.0f; + color.b = 0.0f; + } + color.a = 0.0f; + m_engine->SetOverColor(RetColor(color), m_overMode); + } + else + { + if ( m_overFadeIn > 0.0f && m_overTime < m_overFadeIn ) + { + intensity = m_overTime/m_overFadeIn; + intensity *= m_overForce; + + if ( m_overMode == D3DSTATETCw ) + { + color.r = 1.0f-(1.0f-m_overColor.r)*intensity; + color.g = 1.0f-(1.0f-m_overColor.g)*intensity; + color.b = 1.0f-(1.0f-m_overColor.b)*intensity; + } + else + { + color.r = m_overColor.r*intensity; + color.g = m_overColor.g*intensity; + color.b = m_overColor.b*intensity; + + color.r = 1.0f-(1.0f-color.r)*(1.0f-m_overColorBase.r); + color.g = 1.0f-(1.0f-color.g)*(1.0f-m_overColorBase.g); + color.b = 1.0f-(1.0f-color.b)*(1.0f-m_overColorBase.b); + } + color.a = 0.0f; + m_engine->SetOverColor(RetColor(color), m_overMode); + } + else if ( m_overFadeOut > 0.0f && m_overTime-m_overFadeIn < m_overFadeOut ) + { + intensity = 1.0f-(m_overTime-m_overFadeIn)/m_overFadeOut; + intensity *= m_overForce; + + if ( m_overMode == D3DSTATETCw ) + { + color.r = 1.0f-(1.0f-m_overColor.r)*intensity; + color.g = 1.0f-(1.0f-m_overColor.g)*intensity; + color.b = 1.0f-(1.0f-m_overColor.b)*intensity; + } + else + { + color.r = m_overColor.r*intensity; + color.g = m_overColor.g*intensity; + color.b = m_overColor.b*intensity; + + color.r = 1.0f-(1.0f-color.r)*(1.0f-m_overColorBase.r); + color.g = 1.0f-(1.0f-color.g)*(1.0f-m_overColorBase.g); + color.b = 1.0f-(1.0f-color.b)*(1.0f-m_overColorBase.b); + } + color.a = 0.0f; + m_engine->SetOverColor(RetColor(color), m_overMode); + } + } + + if ( m_overTime >= m_overFadeIn+m_overFadeOut ) + { + FlushOver(); + return; + } +} + + + +// Fixe le mouvement mou de la caméra. + +void CCamera::FixCamera() +{ + m_initDelay = 0.0f; + m_actualEye = m_finalEye = m_scriptEye; + m_actualLookat = m_finalLookat = m_scriptLookat; + SetViewTime(m_scriptEye, m_scriptLookat, 0.0f); +} + +// Spécifie l'emplacement et la direction du point de vue au moteur 3D. + +void CCamera::SetViewTime(const D3DVECTOR &vEyePt, + const D3DVECTOR &vLookatPt, + float rTime) +{ + D3DVECTOR vUpVec, eye, lookat; + float prog, dist, h; + + if ( m_type == CAMERA_INFO ) + { + eye = vEyePt; + lookat = vLookatPt; + } + else + { + if ( m_initDelay > 0.0f ) + { + m_initDelay -= rTime; + if ( m_initDelay < 0.0f ) m_initDelay = 0.0f; + rTime /= 1.0f+m_initDelay; + } + + eye = vEyePt; + lookat = vLookatPt; + if ( !IsCollision(eye, lookat) ) + { + m_finalEye = eye; + m_finalLookat = lookat; + } + + dist = Length(m_finalEye, m_actualEye); + if ( m_smooth == CS_NONE ) prog = dist; + if ( m_smooth == CS_NORM ) prog = powf(dist, 1.5f)*rTime*0.5f; + if ( m_smooth == CS_HARD ) prog = powf(dist, 1.0f)*rTime*4.0f; + if ( m_smooth == CS_SPEC ) prog = powf(dist, 1.0f)*rTime*0.05f; + if ( dist == 0.0f ) + { + m_actualEye = m_finalEye; + } + else + { + if ( prog > dist ) prog = dist; + m_actualEye = (m_finalEye-m_actualEye)/dist*prog + m_actualEye; + } + + dist = Length(m_finalLookat, m_actualLookat); + if ( m_smooth == CS_NONE ) prog = dist; + if ( m_smooth == CS_NORM ) prog = powf(dist, 1.5f)*rTime*2.0f; + if ( m_smooth == CS_HARD ) prog = powf(dist, 1.0f)*rTime*4.0f; + if ( m_smooth == CS_SPEC ) prog = powf(dist, 1.0f)*rTime*4.0f; + if ( dist == 0.0f ) + { + m_actualLookat = m_finalLookat; + } + else + { + if ( prog > dist ) prog = dist; + m_actualLookat = (m_finalLookat-m_actualLookat)/dist*prog + m_actualLookat; + } + + eye = m_effectOffset+m_actualEye; + m_water->AdjustEye(eye); + + h = m_terrain->RetFloorLevel(eye); + if ( eye.y < h+4.0f ) + { + eye.y = h+4.0f; + } + + lookat = m_effectOffset+m_actualLookat; + } + + vUpVec = D3DVECTOR(0.0f, 1.0f, 0.0f); + SetViewParams(eye, lookat, vUpVec); +} + + +// Evite les obstacles. + +BOOL CCamera::IsCollision(D3DVECTOR &eye, D3DVECTOR lookat) +{ + if ( m_type == CAMERA_BACK ) return IsCollisionBack(eye, lookat); + if ( m_type == CAMERA_FIX ) return IsCollisionFix(eye, lookat); + if ( m_type == CAMERA_PLANE ) return IsCollisionFix(eye, lookat); + return FALSE; +} + +// Evite les obstacles. + +BOOL CCamera::IsCollisionBack(D3DVECTOR &eye, D3DVECTOR lookat) +{ +#if 0 + CObject *pObj; + D3DVECTOR oPos, min, max, proj; + ObjectType oType, iType; + float oRadius, dpp, dpl, del, dist, len, prox; + int i; + + if ( m_cameraObj == 0 ) + { + iType = OBJECT_NULL; + } + else + { + iType = m_cameraObj->RetType(); + } + + min.x = Min(eye.x, lookat.x); + min.y = Min(eye.y, lookat.y); + min.z = Min(eye.z, lookat.z); + + max.x = Max(eye.x, lookat.x); + max.y = Max(eye.y, lookat.y); + max.z = Max(eye.z, lookat.z); + + prox = 8.0f; // proximité maximale du véhicule + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_cameraObj ) continue; + + oType = pObj->RetType(); + if ( oType == OBJECT_TOTO || + oType == OBJECT_FIX || + oType == OBJECT_FRET || + oType == OBJECT_STONE || + oType == OBJECT_URANIUM || + oType == OBJECT_METAL || + oType == OBJECT_POWER || + oType == OBJECT_ATOMIC || + oType == OBJECT_BULLET || + oType == OBJECT_BBOX || + oType == OBJECT_TNT || + oType == OBJECT_BOMB || + oType == OBJECT_WAYPOINTb || + oType == OBJECT_WAYPOINTr || + oType == OBJECT_WAYPOINTg || + oType == OBJECT_WAYPOINTy || + oType == OBJECT_WAYPOINTv || + oType == OBJECT_FLAGb || + oType == OBJECT_FLAGr || + oType == OBJECT_FLAGg || + oType == OBJECT_FLAGy || + oType == OBJECT_FLAGv || + oType == OBJECT_ANT || + oType == OBJECT_SPIDER || + oType == OBJECT_BEE || + oType == OBJECT_WORM ) continue; + + pObj->GetGlobalSphere(oPos, oRadius); + if ( oRadius <= 0.0f ) continue; + + if ( oPos.x+oRadius < min.x || + oPos.y+oRadius < min.y || + oPos.z+oRadius < min.z || + oPos.x-oRadius > max.x || + oPos.y-oRadius > max.y || + oPos.z-oRadius > max.z ) continue; + + if ( iType == OBJECT_FACTORY ) + { + dpl = Length(oPos, lookat); + if ( dpl < oRadius ) continue; + } + + proj = Projection(eye, lookat, oPos); + dpp = Length(proj, oPos); + if ( dpp > oRadius ) continue; + + del = Length(eye, lookat); + len = Length(eye, proj); + if ( len > del ) continue; + + dist = sqrtf(oRadius*oRadius + dpp*dpp)-3.0f; + if ( dist < 0.0f ) dist = 0.0f; + proj = (lookat-eye)*dist/del + proj; + len = Length(eye, proj); + + if ( len < del-prox ) + { + eye = proj; + eye.y += len/5.0f; + return FALSE; + } + else + { + eye = (eye-lookat)*prox/del + lookat; + eye.y += (del-prox)/5.0f; + return FALSE; + } + } + return FALSE; +#else + CObject *pObj; + D3DVECTOR oPos, min, max, proj; + ObjectType oType, iType; + float oRadius, dpp, del, len, angle; + int i; + + if ( m_cameraObj == 0 ) + { + iType = OBJECT_NULL; + } + else + { + iType = m_cameraObj->RetType(); + } + + min.x = Min(m_actualEye.x, m_actualLookat.x); + min.y = Min(m_actualEye.y, m_actualLookat.y); + min.z = Min(m_actualEye.z, m_actualLookat.z); + + max.x = Max(m_actualEye.x, m_actualLookat.x); + max.y = Max(m_actualEye.y, m_actualLookat.y); + max.z = Max(m_actualEye.z, m_actualLookat.z); + + m_bTransparency = FALSE; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetTruck() ) continue; // pile ou fret? + + SetTransparency(pObj, 0.0f); // objet opaque + + if ( pObj == m_cameraObj ) continue; + + if ( iType == OBJECT_BASE || // bâtiment ? + iType == OBJECT_DERRICK || + iType == OBJECT_FACTORY || + iType == OBJECT_STATION || + iType == OBJECT_CONVERT || + iType == OBJECT_REPAIR || + iType == OBJECT_DESTROYER|| + iType == OBJECT_TOWER || + iType == OBJECT_RESEARCH || + iType == OBJECT_RADAR || + iType == OBJECT_ENERGY || + iType == OBJECT_LABO || + iType == OBJECT_NUCLEAR || + iType == OBJECT_PARA || + iType == OBJECT_SAFE || + iType == OBJECT_HUSTON ) continue; + + oType = pObj->RetType(); + if ( oType == OBJECT_HUMAN || + oType == OBJECT_TECH || + oType == OBJECT_TOTO || + oType == OBJECT_FIX || + oType == OBJECT_FRET || + oType == OBJECT_ANT || + oType == OBJECT_SPIDER || + oType == OBJECT_BEE || + oType == OBJECT_WORM ) continue; + + pObj->GetGlobalSphere(oPos, oRadius); + if ( oRadius <= 2.0f ) continue; // ignore les petits objets + + if ( oPos.x+oRadius < min.x || + oPos.y+oRadius < min.y || + oPos.z+oRadius < min.z || + oPos.x-oRadius > max.x || + oPos.y-oRadius > max.y || + oPos.z-oRadius > max.z ) continue; + + proj = Projection(m_actualEye, m_actualLookat, oPos); + dpp = Length(proj, oPos); + if ( dpp > oRadius ) continue; + + if ( oType == OBJECT_FACTORY ) + { + angle = RotateAngle(m_actualEye.x-oPos.x, oPos.z-m_actualEye.z); // CW ! + angle = Direction(angle, pObj->RetAngleY(0)); + if ( Abs(angle) < 30.0f*PI/180.0f ) continue; // dans la porte ? + } + + del = Length(m_actualEye, m_actualLookat); + if ( oType == OBJECT_FACTORY ) + { + del += oRadius; + } + + len = Length(m_actualEye, proj); + if ( len > del ) continue; + + SetTransparency(pObj, 1.0f); // objet transparent + m_bTransparency = TRUE; + } + return FALSE; +#endif +} + +// Evite les obstacles. + +BOOL CCamera::IsCollisionFix(D3DVECTOR &eye, D3DVECTOR lookat) +{ + CObject *pObj; + D3DVECTOR oPos, proj; + ObjectType type; + float oRadius, dist; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_cameraObj ) continue; + + type = pObj->RetType(); + if ( type == OBJECT_TOTO || + type == OBJECT_FRET || + type == OBJECT_STONE || + type == OBJECT_URANIUM || + type == OBJECT_METAL || + type == OBJECT_POWER || + type == OBJECT_ATOMIC || + type == OBJECT_BULLET || + type == OBJECT_BBOX || + type == OBJECT_KEYa || + type == OBJECT_KEYb || + type == OBJECT_KEYc || + type == OBJECT_KEYd || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM ) continue; + + pObj->GetGlobalSphere(oPos, oRadius); + if ( oRadius == 0.0f ) continue; + + dist = Length(eye, oPos); + if ( dist < oRadius ) + { + dist = Length(eye, lookat); + proj = Projection(eye, lookat, oPos); + eye = (lookat-eye)*oRadius/dist + proj; + return FALSE; + } + } + return FALSE; +} + + +// Gestion d'un événement. + +BOOL CCamera::EventProcess(const Event &event) +{ + switch( event.event ) + { + case EVENT_FRAME: + EventFrame(event); + break; + +#if 0 + case EVENT_RBUTTONDOWN: + m_bRightDown = TRUE; + m_rightPosInit = event.pos; + m_rightPosCenter = FPOINT(0.5f, 0.5f); + m_engine->MoveMousePos(m_rightPosCenter); +//? m_engine->SetMouseHide(TRUE); // cache la souris + break; + + case EVENT_RBUTTONUP: + m_bRightDown = FALSE; + m_engine->MoveMousePos(m_rightPosInit); +//? m_engine->SetMouseHide(FALSE); // remontre la souris + m_addDirectionH = 0.0f; + m_addDirectionV = -PI*0.05f; + break; +#endif + + case EVENT_MOUSEMOVE: + EventMouseMove(event); + break; + + case EVENT_KEYDOWN: + if ( event.param == VK_WHEELUP ) EventMouseWheel(+1); + if ( event.param == VK_WHEELDOWN ) EventMouseWheel(-1); + break; + } + return TRUE; +} + +// Fait évoluer la caméra selon la souris déplacée. + +BOOL CCamera::EventMouseMove(const Event &event) +{ + m_mousePos = event.pos; + return TRUE; +} + +// Molette souris actionnée. + +void CCamera::EventMouseWheel(int dir) +{ + if ( m_type == CAMERA_BACK ) + { + if ( dir > 0 ) + { + m_backDist -= 8.0f; + if ( m_backDist < m_backMin ) m_backDist = m_backMin; + } + if ( dir < 0 ) + { + m_backDist += 8.0f; + if ( m_backDist > 200.0f ) m_backDist = 200.0f; + } + } + + if ( m_type == CAMERA_FIX || + m_type == CAMERA_PLANE ) + { + if ( dir > 0 ) + { + m_fixDist -= 8.0f; + if ( m_fixDist < 10.0f ) m_fixDist = 10.0f; + } + if ( dir < 0 ) + { + m_fixDist += 8.0f; + if ( m_fixDist > 200.0f ) m_fixDist = 200.0f; + } + } + + if ( m_type == CAMERA_VISIT ) + { + if ( dir > 0 ) + { + m_visitDist -= 8.0f; + if ( m_visitDist < 20.0f ) m_visitDist = 20.0f; + } + if ( dir < 0 ) + { + m_visitDist += 8.0f; + if ( m_visitDist > 200.0f ) m_visitDist = 200.0f; + } + } +} + +// Fait évoluer la caméra selon le temps écoulé. + +BOOL CCamera::EventFrame(const Event &event) +{ + EffectFrame(event); + OverFrame(event); + + if ( m_type == CAMERA_FREE ) + { + return EventFrameFree(event); + } + if ( m_type == CAMERA_EDIT ) + { + return EventFrameEdit(event); + } + if ( m_type == CAMERA_DIALOG ) + { + return EventFrameDialog(event); + } + if ( m_type == CAMERA_BACK ) + { + return EventFrameBack(event); + } + if ( m_type == CAMERA_FIX || + m_type == CAMERA_PLANE ) + { + return EventFrameFix(event); + } + if ( m_type == CAMERA_EXPLO ) + { + return EventFrameExplo(event); + } + if ( m_type == CAMERA_ONBOARD ) + { + return EventFrameOnBoard(event); + } + if ( m_type == CAMERA_SCRIPT ) + { + return EventFrameScript(event); + } + if ( m_type == CAMERA_INFO ) + { + return EventFrameInfo(event); + } + if ( m_type == CAMERA_VISIT ) + { + return EventFrameVisit(event); + } + + return TRUE; +} + + +// Retourne le sprite par défaut à utiliser pour la souris. + +D3DMouse CCamera::RetMouseDef(FPOINT pos) +{ + D3DMouse type; + + type = D3DMOUSENORM; + m_mousePos = pos; + + if ( m_type == CAMERA_INFO ) return type; + + if ( m_bRightDown ) // bouton droite pressé ? + { + m_rightPosMove.x = pos.x - m_rightPosCenter.x; + m_rightPosMove.y = pos.y - m_rightPosCenter.y; + type = D3DMOUSEMOVE; + } + else + { + if ( !m_bCameraScroll ) return type; + + m_mouseDirH = 0.0f; + m_mouseDirV = 0.0f; + + if ( pos.x < m_mouseMarging ) + { + m_mouseDirH = pos.x/m_mouseMarging - 1.0f; + } + + if ( pos.x > 1.0f-m_mouseMarging ) + { + m_mouseDirH = 1.0f - (1.0f-pos.x)/m_mouseMarging; + } + + if ( pos.y < m_mouseMarging ) + { + m_mouseDirV = pos.y/m_mouseMarging - 1.0f; + } + + if ( pos.y > 1.0f-m_mouseMarging ) + { + m_mouseDirV = 1.0f - (1.0f-pos.y)/m_mouseMarging; + } + + if ( m_type == CAMERA_FREE || + m_type == CAMERA_EDIT || + m_type == CAMERA_BACK || + m_type == CAMERA_FIX || + m_type == CAMERA_PLANE || + m_type == CAMERA_EXPLO ) + { + if ( m_mouseDirH > 0.0f ) + { + type = D3DMOUSESCROLLR; + } + if ( m_mouseDirH < 0.0f ) + { + type = D3DMOUSESCROLLL; + } + } + + if ( m_type == CAMERA_FREE || + m_type == CAMERA_EDIT ) + { + if ( m_mouseDirV > 0.0f ) + { + type = D3DMOUSESCROLLU; + } + if ( m_mouseDirV < 0.0f ) + { + type = D3DMOUSESCROLLD; + } + } + + if ( m_bCameraInvertX ) + { + m_mouseDirH = -m_mouseDirH; + } + } + + return type; +} + + + +// Déplace le point de vue. + +BOOL CCamera::EventFrameFree(const Event &event) +{ + D3DVECTOR pos, vLookatPt; + float factor; + + factor = m_heightEye*0.5f+30.0f; + + if ( m_mouseDirH != 0.0f ) + { + m_directionH -= m_mouseDirH*event.rTime*0.7f*m_speed; + } + if ( m_mouseDirV != 0.0f ) + { + m_eyePt = LookatPoint(m_eyePt, m_directionH, m_directionV, m_mouseDirV*event.rTime*factor*m_speed); + } + + // Up/Down. + m_eyePt = LookatPoint(m_eyePt, m_directionH, m_directionV, event.axeY*event.rTime*factor*m_speed); + + // Left/Right. + if ( event.keyState & KS_CONTROL ) + { + if ( event.axeX < 0.0f ) + { + m_eyePt = LookatPoint(m_eyePt, m_directionH+PI/2.0f, m_directionV, -event.axeX*event.rTime*factor*m_speed); + } + if ( event.axeX > 0.0f ) + { + m_eyePt = LookatPoint(m_eyePt, m_directionH-PI/2.0f, m_directionV, event.axeX*event.rTime*factor*m_speed); + } + } + else + { + m_directionH -= event.axeX*event.rTime*0.7f*m_speed; + } + + // PageUp/PageDown. + if ( event.keyState & KS_NUMMINUS ) + { + if ( m_heightEye < 500.0f ) + { + m_heightEye += event.rTime*factor*m_speed; + } + } + if ( event.keyState & KS_NUMPLUS ) + { + if ( m_heightEye > -2.0f ) + { + m_heightEye -= event.rTime*factor*m_speed; + } + } + + m_terrain->ValidPosition(m_eyePt, 10.0f); + + if ( m_terrain->MoveOnFloor(m_eyePt, TRUE) ) + { + m_eyePt.y += m_heightEye; + + pos = m_eyePt; + if ( m_terrain->MoveOnFloor(pos, TRUE) ) + { + pos.y -= 2.0f; + if ( m_eyePt.y < pos.y ) + { + m_eyePt.y = pos.y; + } + } + + } + + vLookatPt = LookatPoint( m_eyePt, m_directionH, m_directionV, 50.0f ); + + if ( m_terrain->MoveOnFloor(vLookatPt, TRUE) ) + { + vLookatPt.y += m_heightLookat; + } + + SetViewTime(m_eyePt, vLookatPt, event.rTime); + + return TRUE; +} + +// Déplace le point de vue. + +BOOL CCamera::EventFrameEdit(const Event &event) +{ + D3DVECTOR pos, vLookatPt; + float factor; + + factor = m_editHeight*0.5f+30.0f; + + if ( m_mouseDirH != 0.0f ) + { + m_directionH -= m_mouseDirH*event.rTime*0.7f*m_speed; + } + if ( m_mouseDirV != 0.0f ) + { + m_eyePt = LookatPoint(m_eyePt, m_directionH, m_directionV, m_mouseDirV*event.rTime*factor*m_speed); + } + + if ( m_bCameraScroll ) + { + // Left/Right. + m_fixDirectionH += m_mouseDirH*event.rTime*1.0f*m_speed; + m_fixDirectionH = NormAngle(m_fixDirectionH); + + // Up/Down. +//? m_fixDirectionV -= m_mouseDirV*event.rTime*0.5f*m_speed; +//? if ( m_fixDirectionV < -PI*0.40f ) m_fixDirectionV = -PI*0.40f; +//? if ( m_fixDirectionV > PI*0.20f ) m_fixDirectionV = PI*0.20f; + } + + m_terrain->ValidPosition(m_eyePt, 10.0f); + + if ( m_terrain->MoveOnFloor(m_eyePt, FALSE) ) + { + m_eyePt.y += m_editHeight; + + pos = m_eyePt; + if ( m_terrain->MoveOnFloor(pos, FALSE) ) + { + pos.y += 2.0f; + if ( m_eyePt.y < pos.y ) + { + m_eyePt.y = pos.y; + } + } + + } + + vLookatPt = LookatPoint( m_eyePt, m_directionH, m_directionV, 50.0f ); + + if ( m_terrain->MoveOnFloor(vLookatPt, TRUE) ) + { + vLookatPt.y += m_heightLookat; + } + + SetViewTime(m_eyePt, vLookatPt, event.rTime); + + return TRUE; +} + +// Déplace le point de vue. + +BOOL CCamera::EventFrameDialog(const Event &event) +{ + return TRUE; +} + +// Déplace le point de vue. + +BOOL CCamera::EventFrameBack(const Event &event) +{ + CPhysics* physics; + ObjectType type; + D3DVECTOR pos, vLookatPt; + FPOINT mouse; + float centeringH, centeringV, centeringD, h, v, d, floor; + + if ( m_cameraObj == 0 ) + { + type = OBJECT_NULL; + } + else + { + type = m_cameraObj->RetType(); + } + + // +/-. + if ( event.keyState & KS_NUMPLUS ) + { + m_backDist -= event.rTime*30.0f*m_speed; + if ( m_backDist < m_backMin ) m_backDist = m_backMin; + } + if ( event.keyState & KS_NUMMINUS ) + { + m_backDist += event.rTime*30.0f*m_speed; + if ( m_backDist > 200.0f ) m_backDist = 200.0f; + } + + m_motorTurn = 0.0f; + + if ( m_bRightDown ) + { + m_addDirectionH = m_rightPosMove.x*6.0f; + m_addDirectionV = -m_rightPosMove.y*2.0f; + } + else + { + if ( m_bCameraScroll ) + { +#if 1 + // Left/Right. + m_addDirectionH += m_mouseDirH*event.rTime*1.0f*m_speed; + m_addDirectionH = NormAngle(m_addDirectionH); + + // Up/Down. +//? m_backDist -= m_mouseDirV*event.rTime*30.0f*m_speed; +//? if ( m_backDist < 10.0f ) m_backDist = 10.0f; +//? if ( m_backDist > 200.0f ) m_backDist = 200.0f; +#else + if ( m_mousePos.y >= 0.18f && m_mousePos.y <= 0.93f ) + { +//? m_addDirectionH = -(m_mousePos.x-0.5f)*4.0f; + m_addDirectionV = (m_mousePos.y-0.5f)*2.0f; +//? if ( m_bCameraInvertX ) m_addDirectionH = -m_addDirectionH; + if ( m_bCameraInvertY ) m_addDirectionV = -m_addDirectionV; + + if ( m_mousePos.x < 0.5f ) m_motorTurn = -1.0f; + if ( m_mousePos.x > 0.5f ) m_motorTurn = 1.0f; + + mouse = m_mousePos; + mouse.x = 0.5f; + m_engine->MoveMousePos(mouse); + } + else + { + m_addDirectionH = 0.0f; + m_addDirectionV = 0.0f; + } +#endif + } + } + + if ( m_mouseDirH != 0 || m_mouseDirV != 0 ) + { + AbortCentering(); // stoppe cadrage spécial + } + + // Progression du cadrage spécial. + centeringH = 0.0f; + centeringV = 0.0f; + centeringD = 0.0f; + + if ( m_centeringPhase == CP_START ) + { + m_centeringProgress += event.rTime/m_centeringTime; + if ( m_centeringProgress > 1.0f ) m_centeringProgress = 1.0f; + centeringH = m_centeringProgress; + centeringV = m_centeringProgress; + centeringD = m_centeringProgress; + if ( m_centeringProgress >= 1.0f ) + { + m_centeringPhase = CP_WAIT; + } + } + + if ( m_centeringPhase == CP_WAIT ) + { + centeringH = 1.0f; + centeringV = 1.0f; + centeringD = 1.0f; + } + + if ( m_centeringPhase == CP_STOP ) + { + m_centeringProgress += event.rTime/m_centeringTime; + if ( m_centeringProgress > 1.0f ) m_centeringProgress = 1.0f; + centeringH = 1.0f-m_centeringProgress; + centeringV = 1.0f-m_centeringProgress; + centeringD = 1.0f-m_centeringProgress; + if ( m_centeringProgress >= 1.0f ) + { + m_centeringPhase = CP_NULL; + } + } + + if ( m_centeringAngleH == 99.9f ) centeringH = 0.0f; + if ( m_centeringAngleV == 99.9f ) centeringV = 0.0f; + if ( m_centeringDist == 0.0f ) centeringD = 0.0f; + + if ( m_cameraObj != 0 ) + { + vLookatPt = m_cameraObj->RetPosition(0); + if ( type == OBJECT_BASE ) vLookatPt.y += 40.0f; + else if ( type == OBJECT_HUMAN ) vLookatPt.y += 1.0f; + else if ( type == OBJECT_TECH ) vLookatPt.y += 1.0f; + else vLookatPt.y += 4.0f; + + h = -m_cameraObj->RetAngleY(0); // angle véhicule/batiment + + if ( type == OBJECT_DERRICK || + type == OBJECT_FACTORY || + type == OBJECT_REPAIR || + type == OBJECT_DESTROYER|| + type == OBJECT_STATION || + type == OBJECT_CONVERT || + type == OBJECT_TOWER || + type == OBJECT_RESEARCH || + type == OBJECT_RADAR || + type == OBJECT_INFO || + type == OBJECT_ENERGY || + type == OBJECT_LABO || + type == OBJECT_NUCLEAR || + type == OBJECT_PARA || + type == OBJECT_SAFE || + type == OBJECT_HUSTON || + type == OBJECT_START || + type == OBJECT_END ) // batiment ? + { + h += PI*0.20f; // presque de face + } + else // véhicule ? + { + h += PI; // de dos + } + h = NormAngle(h)+m_remotePan; + v = 0.0f; //? + + h += m_centeringCurrentH; + h += m_addDirectionH*(1.0f-centeringH); + h = NormAngle(h); + + if ( type == OBJECT_MOBILEdr ) // dessinateur ? + { + v -= 0.3f; // caméra plus haute + } + + v += m_centeringCurrentV; + v += m_addDirectionV*(1.0f-centeringV); + + d = m_backDist; + d += m_centeringDist*centeringD; + + m_centeringCurrentH = m_centeringAngleH*centeringH; + m_centeringCurrentV = m_centeringAngleV*centeringV; + + m_eyePt = RotateView(vLookatPt, h, v, d); + + physics = m_cameraObj->RetPhysics(); + if ( physics != 0 && physics->RetLand() ) // au sol ? + { + pos = vLookatPt+(vLookatPt-m_eyePt); + floor = m_terrain->RetFloorHeight(pos)-4.0f; + if ( floor > 0.0f ) + { + m_eyePt.y += floor; // montre la descente devant + } + } + + m_eyePt = ExcludeTerrain(m_eyePt, vLookatPt, h, v); + m_eyePt = ExcludeObject(m_eyePt, vLookatPt, h, v); + + SetViewTime(m_eyePt, vLookatPt, event.rTime); + + m_directionH = h+PI/2.0f; + m_directionV = v; + } + + return TRUE; +} + +// Déplace le point de vue. + +BOOL CCamera::EventFrameFix(const Event &event) +{ + D3DVECTOR pos, vLookatPt; + float h, v, d; + + // +/-. + if ( event.keyState & KS_NUMPLUS ) + { + m_fixDist -= event.rTime*30.0f*m_speed; + if ( m_fixDist < 10.0f ) m_fixDist = 10.0f; + } + if ( event.keyState & KS_NUMMINUS ) + { + m_fixDist += event.rTime*30.0f*m_speed; + if ( m_fixDist > 200.0f ) m_fixDist = 200.0f; + } + + if ( m_bCameraScroll ) + { + // Left/Right. + m_fixDirectionH += m_mouseDirH*event.rTime*1.0f*m_speed; + m_fixDirectionH = NormAngle(m_fixDirectionH); + + // Up/Down. +//? m_fixDist -= m_mouseDirV*event.rTime*30.0f*m_speed; +//? if ( m_fixDist < 10.0f ) m_fixDist = 10.0f; +//? if ( m_fixDist > 200.0f ) m_fixDist = 200.0f; + } + + if ( m_mouseDirH != 0 || m_mouseDirV != 0 ) + { + AbortCentering(); // stoppe cadrage spécial + } + + if ( m_cameraObj != 0 ) + { + vLookatPt = m_cameraObj->RetPosition(0); + + h = m_fixDirectionH+m_remotePan; + v = m_fixDirectionV; + + d = m_fixDist; +//- if ( m_type == CAMERA_PLANE ) d += 20.0f; + m_eyePt = RotateView(vLookatPt, h, v, d); +//- if ( m_type == CAMERA_PLANE ) m_eyePt.y += 50.0f; + if ( m_type == CAMERA_PLANE ) m_eyePt.y += m_fixDist/2.0f; + m_eyePt = ExcludeTerrain(m_eyePt, vLookatPt, h, v); + m_eyePt = ExcludeObject(m_eyePt, vLookatPt, h, v); + + SetViewTime(m_eyePt, vLookatPt, event.rTime); + + m_directionH = h+PI/2.0f; + m_directionV = v; + } + + return TRUE; +} + +// Déplace le point de vue. + +BOOL CCamera::EventFrameExplo(const Event &event) +{ + D3DVECTOR pos, vLookatPt; + float factor; + + factor = m_heightEye*0.5f+30.0f; + + if ( m_mouseDirH != 0.0f ) + { + m_directionH -= m_mouseDirH*event.rTime*0.7f*m_speed; + } + + m_terrain->ValidPosition(m_eyePt, 10.0f); + + if ( m_terrain->MoveOnFloor(m_eyePt, FALSE) ) + { + m_eyePt.y += m_heightEye; + + pos = m_eyePt; + if ( m_terrain->MoveOnFloor(pos, FALSE) ) + { + pos.y += 2.0f; + if ( m_eyePt.y < pos.y ) + { + m_eyePt.y = pos.y; + } + } + + } + + vLookatPt = LookatPoint( m_eyePt, m_directionH, m_directionV, 50.0f ); + + if ( m_terrain->MoveOnFloor(vLookatPt, TRUE) ) + { + vLookatPt.y += m_heightLookat; + } + + SetViewTime(m_eyePt, vLookatPt, event.rTime); + + return TRUE; +} + +// Déplace le point de vue. + +BOOL CCamera::EventFrameOnBoard(const Event &event) +{ + D3DVECTOR vLookatPt, vUpVec, eye, lookat, pos; + + if ( m_cameraObj != 0 ) + { + m_cameraObj->SetViewFromHere(m_eyePt, m_directionH, m_directionV, + vLookatPt, vUpVec, m_type); + eye = m_effectOffset*0.3f+m_eyePt; + lookat = m_effectOffset*0.3f+vLookatPt; + + SetViewParams(eye, lookat, vUpVec); + m_actualEye = eye; + m_actualLookat = lookat; + } + return TRUE; +} + +// Déplace le point de vue. + +BOOL CCamera::EventFrameInfo(const Event &event) +{ + SetViewTime(D3DVECTOR(0.0f, 0.0f, 0.0f), + D3DVECTOR(0.0f, 0.0f, 1.0f), + event.rTime); + return TRUE; +} + +// Déplace le point de vue. + +BOOL CCamera::EventFrameVisit(const Event &event) +{ + D3DVECTOR eye; + float angleH, angleV; + + m_visitTime += event.rTime; + + // +/-. + if ( event.keyState & KS_NUMPLUS ) + { + m_visitDist -= event.rTime*50.0f*m_speed; + if ( m_visitDist < 20.0f ) m_visitDist = 20.0f; + } + if ( event.keyState & KS_NUMMINUS ) + { + m_visitDist += event.rTime*50.0f*m_speed; + if ( m_visitDist > 200.0f ) m_visitDist = 200.0f; + } + + // PageUp/Down. + if ( event.keyState & KS_PAGEUP ) + { + m_visitDirectionV -= event.rTime*1.0f*m_speed; + if ( m_visitDirectionV < -PI*0.40f ) m_visitDirectionV = -PI*0.40f; + } + if ( event.keyState & KS_PAGEDOWN ) + { + m_visitDirectionV += event.rTime*1.0f*m_speed; + if ( m_visitDirectionV > 0.0f ) m_visitDirectionV = 0.0f; + } + + if ( m_bCameraScroll ) + { + m_visitDist -= m_mouseDirV*event.rTime*30.0f*m_speed; + if ( m_visitDist < 20.0f ) m_visitDist = 20.0f; + if ( m_visitDist > 200.0f ) m_visitDist = 200.0f; + } + + angleH = (m_visitTime/10.0f)*(PI*2.0f); + angleV = m_visitDirectionV; + eye = RotateView(m_visitGoal, angleH, angleV, m_visitDist); + eye = ExcludeTerrain(eye, m_visitGoal, angleH, angleV); + eye = ExcludeObject(eye, m_visitGoal, angleH, angleV); + SetViewTime(eye, m_visitGoal, event.rTime); + + return TRUE; +} + +// Déplace le point de vue. + +BOOL CCamera::EventFrameScript(const Event &event) +{ + SetViewTime(m_scriptEye+m_effectOffset, + m_scriptLookat+m_effectOffset, event.rTime); + return TRUE; +} + +void CCamera::SetScriptEye(D3DVECTOR eye) +{ + m_scriptEye = eye; +} + +void CCamera::SetScriptLookat(D3DVECTOR lookat) +{ + m_scriptLookat = lookat; +} + + +// Spécifie l'emplacement et la direction du point de vue. + +void CCamera::SetViewParams(const D3DVECTOR &eye, const D3DVECTOR &lookat, + const D3DVECTOR &up) +{ + BOOL bUnder; + + m_engine->SetViewParams(eye, lookat, up, m_eyeDistance); + + bUnder = (eye.y < m_water->RetLevel()); // est-on sous l'eau ? + if ( m_type == CAMERA_INFO ) bUnder = FALSE; + m_engine->SetRankView(bUnder?1:0); +} + + +// Adapte la caméra pour ne pas entrer dans le terrain. + +D3DVECTOR CCamera::ExcludeTerrain(D3DVECTOR eye, D3DVECTOR lookat, + float &angleH, float &angleV) +{ + D3DVECTOR pos; + float dist; + + pos = eye; + if ( m_terrain->MoveOnFloor(pos) ) + { + dist = Length2d(lookat, pos); + pos.y += 2.0f+dist*0.1f; + if ( pos.y > eye.y ) + { + angleV = -RotateAngle(dist, pos.y-lookat.y); + eye = RotateView(lookat, angleH, angleV, dist); + } + } + return eye; +} + +// Adapte la caméra pour ne pas pénétrer dans un objet. + +D3DVECTOR CCamera::ExcludeObject(D3DVECTOR eye, D3DVECTOR lookat, + float &angleH, float &angleV) +{ + CObject* pObj; + D3DVECTOR oPos; + float oRad, dist; + int i, j; + +return eye; +//? + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRad) ) + { + dist = Length(oPos, eye); + if ( dist < oRad+2.0f ) + { + eye.y = oPos.y+oRad+2.0f; + } + } + } + + return eye; +} + + diff --git a/src/camera.h b/src/camera.h new file mode 100644 index 00000000..1f2c41c8 --- /dev/null +++ b/src/camera.h @@ -0,0 +1,252 @@ +// camera.h + +#ifndef _CAMERA_H_ +#define _CAMERA_H_ + + +class CInstanceManager; +class CD3DEngine; +class CTerrain; +class CWater; +class CObject; +enum D3DMouse; + + +enum CameraType +{ + CAMERA_NULL = 0, // caméra indéfinie + CAMERA_FREE = 1, // caméra libre (jamais en principe) + CAMERA_EDIT = 2, // caméra pendant l'édition d'un programme + CAMERA_ONBOARD = 3, // caméra à bord d'un robot + CAMERA_BACK = 4, // caméra derrière un robot + CAMERA_FIX = 5, // caméra fixe après robot + CAMERA_EXPLO = 6, // caméra immobile après explosion + CAMERA_SCRIPT = 7, // caméra pendant un film scripté + CAMERA_INFO = 8, // caméra pendant l'affichage des informations + CAMERA_VISIT = 9, // visite du lieu d'une erreur + CAMERA_DIALOG = 10, // caméra pendant dialogue + CAMERA_PLANE = 11, // caméra fixe en hauteur +}; + +enum CameraSmooth +{ + CS_NONE = 0, // brusque + CS_NORM = 1, // normal + CS_HARD = 2, // dur + CS_SPEC = 3, // spécial +}; + +enum CenteringPhase +{ + CP_NULL = 0, + CP_START = 1, + CP_WAIT = 2, + CP_STOP = 3, +}; + +enum CameraEffect +{ + CE_NULL = 0, // pas d'effet + CE_TERRAFORM = 1, // terrassement + CE_CRASH = 2, // véhicule volant posé violemment + CE_EXPLO = 3, // explosion + CE_SHOT = 4, // coup non mortel + CE_VIBRATION = 5, // vibration pendant construction + CE_PET = 6, // raté du réacteur +}; + +enum OverEffect +{ + OE_NULL = 0, // pas d'effet + OE_BLOOD = 1, // flash rouge + OE_FADEINw = 2, // blanc -> rien + OE_FADEOUTw = 3, // rien -> blanc + OE_FADEOUTb = 4, // rien -> bleu + OE_BLITZ = 5, // éclair +}; + + + +class CCamera +{ +public: + CCamera(CInstanceManager* iMan); + ~CCamera(); + + BOOL EventProcess(const Event &event); + + void Init(D3DVECTOR eye, D3DVECTOR lookat, float delay); + + void SetObject(CObject* object); + CObject* RetObject(); + + void SetType(CameraType type); + CameraType RetType(); + + void SetSmooth(CameraSmooth type); + CameraSmooth RetSmoth(); + + void SetDist(float dist); + float RetDist(); + + void SetFixDirection(float angle); + float RetFixDirection(); + + void SetRemotePan(float value); + float RetRemotePan(); + + void SetRemoteZoom(float value); + float RetRemoteZoom(); + + void StartVisit(D3DVECTOR goal, float dist); + void StopVisit(); + + void RetCamera(D3DVECTOR &eye, D3DVECTOR &lookat); + + BOOL StartCentering(CObject *object, float angleH, float angleV, float dist, float time); + BOOL StopCentering(CObject *object, float time); + void AbortCentering(); + + void FlushEffect(); + void StartEffect(CameraEffect effect, D3DVECTOR pos, float force); + + void FlushOver(); + void SetOverBaseColor(D3DCOLORVALUE color); + void StartOver(OverEffect effect, D3DVECTOR pos, float force); + + void FixCamera(); + void SetScriptEye(D3DVECTOR eye); + void SetScriptLookat(D3DVECTOR lookat); + + void SetEffect(BOOL bEnable); + void SetCameraScroll(BOOL bScroll); + void SetCameraInvertX(BOOL bInvert); + void SetCameraInvertY(BOOL bInvert); + + float RetMotorTurn(); + D3DMouse RetMouseDef(FPOINT pos); + +protected: + BOOL EventMouseMove(const Event &event); + void EventMouseWheel(int dir); + BOOL EventFrame(const Event &event); + BOOL EventFrameFree(const Event &event); + BOOL EventFrameEdit(const Event &event); + BOOL EventFrameDialog(const Event &event); + BOOL EventFrameBack(const Event &event); + BOOL EventFrameFix(const Event &event); + BOOL EventFrameExplo(const Event &event); + BOOL EventFrameOnBoard(const Event &event); + BOOL EventFrameInfo(const Event &event); + BOOL EventFrameVisit(const Event &event); + BOOL EventFrameScript(const Event &event); + + void SetViewTime(const D3DVECTOR &vEyePt, const D3DVECTOR &vLookatPt, float rTime); + BOOL IsCollision(D3DVECTOR &eye, D3DVECTOR lookat); + BOOL IsCollisionBack(D3DVECTOR &eye, D3DVECTOR lookat); + BOOL IsCollisionFix(D3DVECTOR &eye, D3DVECTOR lookat); + + D3DVECTOR ExcludeTerrain(D3DVECTOR eye, D3DVECTOR lookat, float &angleH, float &angleV); + D3DVECTOR ExcludeObject(D3DVECTOR eye, D3DVECTOR lookat, float &angleH, float &angleV); + + void SetViewParams(const D3DVECTOR &eye, const D3DVECTOR &lookat, const D3DVECTOR &up); + void EffectFrame(const Event &event); + void OverFrame(const Event &event); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CTerrain* m_terrain; + CWater* m_water; + + CameraType m_type; // type de la caméra (CAMERA_*) + CameraSmooth m_smooth; // type de lissage + CObject* m_cameraObj; // objet lié à la caméra + + float m_eyeDistance; // distance entre les yeux + float m_initDelay; // délai du centrage initial + + D3DVECTOR m_actualEye; // oeil actuel + D3DVECTOR m_actualLookat; // visée actuelle + D3DVECTOR m_finalEye; // oeil final + D3DVECTOR m_finalLookat; // visée finale + D3DVECTOR m_normEye; // oeil normal + D3DVECTOR m_normLookat; // visée normale + float m_focus; + + BOOL m_bRightDown; + FPOINT m_rightPosInit; + FPOINT m_rightPosCenter; + FPOINT m_rightPosMove; + + D3DVECTOR m_eyePt; // CAMERA_FREE: oeil + float m_directionH; // CAMERA_FREE: direction horizontale + float m_directionV; // CAMERA_FREE: direction verticale + float m_heightEye; // CAMERA_FREE: hauteur au-dessus du sol + float m_heightLookat; // CAMERA_FREE: hauteur au-dessus du sol + float m_speed; // CAMERA_FREE: vitesse de déplacement + + float m_backDist; // CAMERA_BACK: éloignement + float m_backMin; // CAMERA_BACK: éloignement minimal + float m_addDirectionH; // CAMERA_BACK: direction supplémentaire + float m_addDirectionV; // CAMERA_BACK: direction supplémentaire + BOOL m_bTransparency; + + float m_fixDist; // CAMERA_FIX: éloignement + float m_fixDirectionH; // CAMERA_FIX: direction + float m_fixDirectionV; // CAMERA_FIX: direction + + D3DVECTOR m_visitGoal; // CAMERA_VISIT: position visée + float m_visitDist; // CAMERA_VISIT: éloignement + float m_visitTime; // CAMERA_VISIT: temps relatif + CameraType m_visitType; // CAMERA_VISIT: type initial + float m_visitDirectionH; // CAMERA_VISIT: direction + float m_visitDirectionV; // CAMERA_VISIT: direction + + float m_editHeight; // CAMERA_EDIT: hauteur + + float m_remotePan; + float m_remoteZoom; + + FPOINT m_mousePos; + float m_mouseDirH; + float m_mouseDirV; + float m_mouseMarging; + + float m_motorTurn; + + CenteringPhase m_centeringPhase; + float m_centeringAngleH; + float m_centeringAngleV; + float m_centeringDist; + float m_centeringCurrentH; + float m_centeringCurrentV; + float m_centeringTime; + float m_centeringProgress; + + CameraEffect m_effectType; + D3DVECTOR m_effectPos; + float m_effectForce; + float m_effectProgress; + D3DVECTOR m_effectOffset; + + OverEffect m_overType; + float m_overForce; + float m_overTime; + D3DCOLORVALUE m_overColorBase; + D3DCOLORVALUE m_overColor; + int m_overMode; + float m_overFadeIn; + float m_overFadeOut; + + D3DVECTOR m_scriptEye; + D3DVECTOR m_scriptLookat; + + BOOL m_bEffect; // secousses si explosion ? + BOOL m_bCameraScroll; // scroll dans les bords ? + BOOL m_bCameraInvertX; // inversion X dans les bords ? + BOOL m_bCameraInvertY; // inversion Y dans les bords ? +}; + + +#endif //_CAMERA_H_ diff --git a/src/cbottoken.cpp b/src/cbottoken.cpp new file mode 100644 index 00000000..db07edfa --- /dev/null +++ b/src/cbottoken.cpp @@ -0,0 +1,507 @@ +// cbottoken.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "language.h" +#include "global.h" +#include "event.h" +#include "object.h" +#include "cbottoken.h" + + + + +// Cherche le nom d'un objet. + +char* RetObjectName(ObjectType type) +{ + if ( type == OBJECT_PORTICO ) return "Portico"; + if ( type == OBJECT_BASE ) return "SpaceShip"; + if ( type == OBJECT_DERRICK ) return "Derrick"; + if ( type == OBJECT_FACTORY ) return "BotFactory"; + if ( type == OBJECT_STATION ) return "PowerStation"; + if ( type == OBJECT_CONVERT ) return "Converter"; + if ( type == OBJECT_REPAIR ) return "RepairCenter"; + if ( type == OBJECT_DESTROYER ) return "Destroyer"; + if ( type == OBJECT_TOWER ) return "DefenseTower"; + if ( type == OBJECT_NEST ) return "AlienNest"; + if ( type == OBJECT_RESEARCH ) return "ResearchCenter"; + if ( type == OBJECT_RADAR ) return "RadarStation"; + if ( type == OBJECT_INFO ) return "ExchangePost"; + if ( type == OBJECT_ENERGY ) return "PowerPlant"; + if ( type == OBJECT_LABO ) return "AutoLab"; + if ( type == OBJECT_NUCLEAR ) return "NuclearPlant"; + if ( type == OBJECT_PARA ) return "PowerCaptor"; + if ( type == OBJECT_SAFE ) return "Vault"; + if ( type == OBJECT_HUSTON ) return "Houston"; + if ( type == OBJECT_TARGET1 ) return "Target1"; + if ( type == OBJECT_TARGET2 ) return "Target2"; + if ( type == OBJECT_START ) return "StartArea"; + if ( type == OBJECT_END ) return "GoalArea"; + if ( type == OBJECT_TEEN34 ) return "Stone"; + if ( type == OBJECT_STONE ) return "TitaniumOre"; + if ( type == OBJECT_URANIUM ) return "UraniumOre"; + if ( type == OBJECT_METAL ) return "Titanium"; + if ( type == OBJECT_POWER ) return "PowerCell"; + if ( type == OBJECT_ATOMIC ) return "NuclearCell"; + if ( type == OBJECT_BULLET ) return "OrgaMatter"; + if ( type == OBJECT_BBOX ) return "BlackBox"; + if ( type == OBJECT_KEYa ) return "KeyA"; + if ( type == OBJECT_KEYb ) return "KeyB"; + if ( type == OBJECT_KEYc ) return "KeyC"; + if ( type == OBJECT_KEYd ) return "KeyD"; + if ( type == OBJECT_TNT ) return "TNT"; + if ( type == OBJECT_SCRAP1 ) return "Scrap"; + if ( type == OBJECT_BOMB ) return "Mine"; + if ( type == OBJECT_BARRIER1 ) return "Barrier"; + if ( type == OBJECT_WAYPOINT ) return "WayPoint"; + if ( type == OBJECT_FLAGb ) return "BlueFlag"; + if ( type == OBJECT_FLAGr ) return "RedFlag"; + if ( type == OBJECT_FLAGg ) return "GreenFlag"; + if ( type == OBJECT_FLAGy ) return "YellowFlag"; + if ( type == OBJECT_FLAGv ) return "VioletFlag"; + if ( type == OBJECT_MARKPOWER ) return "PowerSpot"; + if ( type == OBJECT_MARKSTONE ) return "TitaniumSpot"; + if ( type == OBJECT_MARKURANIUM ) return "UraniumSpot"; + if ( type == OBJECT_MARKKEYa ) return "KeyASpot"; + if ( type == OBJECT_MARKKEYb ) return "KeyBSpot"; + if ( type == OBJECT_MARKKEYc ) return "KeyCSpot"; + if ( type == OBJECT_MARKKEYd ) return "KeyDSpot"; + if ( type == OBJECT_MOBILEwt ) return "PracticeBot"; + if ( type == OBJECT_MOBILEwa ) return "WheeledGrabber"; + if ( type == OBJECT_MOBILEta ) return "TrackedGrabber"; + if ( type == OBJECT_MOBILEfa ) return "WingedGrabber"; + if ( type == OBJECT_MOBILEia ) return "LeggedGrabber"; + if ( type == OBJECT_MOBILEwc ) return "WheeledShooter"; + if ( type == OBJECT_MOBILEtc ) return "TrackedShooter"; + if ( type == OBJECT_MOBILEfc ) return "WingedShooter"; + if ( type == OBJECT_MOBILEic ) return "LeggedShooter"; + if ( type == OBJECT_MOBILEwi ) return "WheeledOrgaShooter"; + if ( type == OBJECT_MOBILEti ) return "TrackedOrgaShooter"; + if ( type == OBJECT_MOBILEfi ) return "WingedOrgaShooter"; + if ( type == OBJECT_MOBILEii ) return "LeggedOrgaShooter"; + if ( type == OBJECT_MOBILEws ) return "WheeledSniffer"; + if ( type == OBJECT_MOBILEts ) return "TrackedSniffer"; + if ( type == OBJECT_MOBILEfs ) return "WingedSniffer"; + if ( type == OBJECT_MOBILEis ) return "LeggedSniffer"; + if ( type == OBJECT_MOBILErt ) return "Thumper"; + if ( type == OBJECT_MOBILErc ) return "PhazerShooter"; + if ( type == OBJECT_MOBILErr ) return "Recycler"; + if ( type == OBJECT_MOBILErs ) return "Shielder"; + if ( type == OBJECT_MOBILEsa ) return "Subber"; + if ( type == OBJECT_MOBILEtg ) return "TargetBot"; + if ( type == OBJECT_MOBILEdr ) return "Scribbler"; + if ( type == OBJECT_HUMAN ) return "Me"; + if ( type == OBJECT_TECH ) return "Tech"; + if ( type == OBJECT_MOTHER ) return "AlienQueen"; + if ( type == OBJECT_EGG ) return "AlienEgg"; + if ( type == OBJECT_ANT ) return "AlienAnt"; + if ( type == OBJECT_SPIDER ) return "AlienSpider"; + if ( type == OBJECT_BEE ) return "AlienWasp"; + if ( type == OBJECT_WORM ) return "AlienWorm"; + if ( type == OBJECT_RUINmobilew1) return "Wreck"; + return ""; +} + +// Cherche le nom secondaire d'un objet. +// (à cause d'Otto qui pense que les allemands n'aiment pas le nucléaire) + +char* RetObjectAlias(ObjectType type) +{ + if ( type == OBJECT_NUCLEAR ) return "FuelCellPlant"; + if ( type == OBJECT_URANIUM ) return "PlatinumOre"; + if ( type == OBJECT_ATOMIC ) return "FuelCell"; + if ( type == OBJECT_MARKURANIUM ) return "PlatinumSpot"; + if ( type == OBJECT_ENERGY ) return "Disintegrator"; // pour CeeBot-K + return ""; +} + + +// Retourne le fichier d'aide à utiliser pour l'objet. + +char* RetHelpFilename(ObjectType type) +{ + if ( type == OBJECT_BASE ) return "help\\object\\base.txt"; + if ( type == OBJECT_DERRICK ) return "help\\object\\derrick.txt"; + if ( type == OBJECT_FACTORY ) return "help\\object\\factory.txt"; + if ( type == OBJECT_STATION ) return "help\\object\\station.txt"; + if ( type == OBJECT_CONVERT ) return "help\\object\\convert.txt"; + if ( type == OBJECT_REPAIR ) return "help\\object\\repair.txt"; + if ( type == OBJECT_DESTROYER ) return "help\\object\\destroy.txt"; + if ( type == OBJECT_TOWER ) return "help\\object\\tower.txt"; + if ( type == OBJECT_NEST ) return "help\\object\\nest.txt"; + if ( type == OBJECT_RESEARCH ) return "help\\object\\research.txt"; + if ( type == OBJECT_RADAR ) return "help\\object\\radar.txt"; + if ( type == OBJECT_INFO ) return "help\\object\\exchange.txt"; + if ( type == OBJECT_ENERGY ) return "help\\object\\energy.txt"; + if ( type == OBJECT_LABO ) return "help\\object\\labo.txt"; + if ( type == OBJECT_NUCLEAR ) return "help\\object\\nuclear.txt"; + if ( type == OBJECT_PARA ) return "help\\object\\captor.txt"; + if ( type == OBJECT_SAFE ) return "help\\object\\safe.txt"; + if ( type == OBJECT_HUSTON ) return "help\\object\\huston.txt"; + if ( type == OBJECT_START ) return "help\\object\\start.txt"; + if ( type == OBJECT_END ) return "help\\object\\goal.txt"; + if ( type == OBJECT_STONE ) return "help\\object\\titanore.txt"; + if ( type == OBJECT_URANIUM ) return "help\\object\\uranore.txt"; + if ( type == OBJECT_METAL ) return "help\\object\\titan.txt"; + if ( type == OBJECT_POWER ) return "help\\object\\power.txt"; + if ( type == OBJECT_ATOMIC ) return "help\\object\\atomic.txt"; + if ( type == OBJECT_BULLET ) return "help\\object\\bullet.txt"; + if ( type == OBJECT_BBOX ) return "help\\object\\bbox.txt"; + if ( type == OBJECT_KEYa ) return "help\\object\\key.txt"; + if ( type == OBJECT_KEYb ) return "help\\object\\key.txt"; + if ( type == OBJECT_KEYc ) return "help\\object\\key.txt"; + if ( type == OBJECT_KEYd ) return "help\\object\\key.txt"; + if ( type == OBJECT_TNT ) return "help\\object\\tnt.txt"; + if ( type == OBJECT_SCRAP1 ) return "help\\object\\scrap.txt"; + if ( type == OBJECT_BOMB ) return "help\\object\\mine.txt"; + if ( type == OBJECT_BARRIER1 ) return "help\\object\\barrier.txt"; + if ( type == OBJECT_WAYPOINT ) return "help\\object\\waypoint.txt"; + if ( type == OBJECT_FLAGb ) return "help\\object\\flag.txt"; + if ( type == OBJECT_FLAGr ) return "help\\object\\flag.txt"; + if ( type == OBJECT_FLAGg ) return "help\\object\\flag.txt"; + if ( type == OBJECT_FLAGy ) return "help\\object\\flag.txt"; + if ( type == OBJECT_FLAGv ) return "help\\object\\flag.txt"; + if ( type == OBJECT_MARKPOWER ) return "help\\object\\enerspot.txt"; + if ( type == OBJECT_MARKSTONE ) return "help\\object\\stonspot.txt"; + if ( type == OBJECT_MARKURANIUM ) return "help\\object\\uranspot.txt"; + if ( type == OBJECT_MOBILEwa ) return "help\\object\\botgr.txt"; + if ( type == OBJECT_MOBILEta ) return "help\\object\\botgc.txt"; + if ( type == OBJECT_MOBILEfa ) return "help\\object\\botgj.txt"; + if ( type == OBJECT_MOBILEia ) return "help\\object\\botgs.txt"; + if ( type == OBJECT_MOBILEws ) return "help\\object\\botsr.txt"; + if ( type == OBJECT_MOBILEts ) return "help\\object\\botsc.txt"; + if ( type == OBJECT_MOBILEfs ) return "help\\object\\botsj.txt"; + if ( type == OBJECT_MOBILEis ) return "help\\object\\botss.txt"; + if ( type == OBJECT_MOBILEwi ) return "help\\object\\botor.txt"; + if ( type == OBJECT_MOBILEti ) return "help\\object\\botoc.txt"; + if ( type == OBJECT_MOBILEfi ) return "help\\object\\botoj.txt"; + if ( type == OBJECT_MOBILEii ) return "help\\object\\botos.txt"; + if ( type == OBJECT_MOBILEwc ) return "help\\object\\botfr.txt"; + if ( type == OBJECT_MOBILEtc ) return "help\\object\\botfc.txt"; + if ( type == OBJECT_MOBILEfc ) return "help\\object\\botfj.txt"; + if ( type == OBJECT_MOBILEic ) return "help\\object\\botfs.txt"; + if ( type == OBJECT_MOBILErt ) return "help\\object\\bottump.txt"; + if ( type == OBJECT_MOBILErc ) return "help\\object\\botphaz.txt"; + if ( type == OBJECT_MOBILErr ) return "help\\object\\botrecy.txt"; + if ( type == OBJECT_MOBILErs ) return "help\\object\\botshld.txt"; + if ( type == OBJECT_MOBILEsa ) return "help\\object\\botsub.txt"; + if ( type == OBJECT_MOBILEwt ) return "help\\object\\bottr.txt"; + if ( type == OBJECT_MOBILEtg ) return "help\\object\\bottarg.txt"; + if ( type == OBJECT_MOBILEdr ) return "help\\object\\botdraw.txt"; + if ( type == OBJECT_APOLLO2 ) return "help\\object\\lrv.txt"; + if ( type == OBJECT_HUMAN ) return "help\\object\\human.txt"; + if ( type == OBJECT_MOTHER ) return "help\\object\\mother.txt"; + if ( type == OBJECT_EGG ) return "help\\object\\egg.txt"; + if ( type == OBJECT_ANT ) return "help\\object\\ant.txt"; + if ( type == OBJECT_SPIDER ) return "help\\object\\spider.txt"; + if ( type == OBJECT_BEE ) return "help\\object\\wasp.txt"; + if ( type == OBJECT_WORM ) return "help\\object\\worm.txt"; + if ( type == OBJECT_RUINmobilew1) return "help\\object\\wreck.txt"; + return ""; +} + + +// Retourne le fichier d'aide à utiliser pour une instruction. + +char* RetHelpFilename(const char *token) +{ + if ( strcmp(token, "if" ) == 0 ) return "help\\cbot\\if.txt"; + if ( strcmp(token, "else" ) == 0 ) return "help\\cbot\\if.txt"; + if ( strcmp(token, "repeat" ) == 0 ) return "help\\cbot\\repeat.txt"; + if ( strcmp(token, "for" ) == 0 ) return "help\\cbot\\for.txt"; + if ( strcmp(token, "while" ) == 0 ) return "help\\cbot\\while.txt"; + if ( strcmp(token, "do" ) == 0 ) return "help\\cbot\\do.txt"; + if ( strcmp(token, "break" ) == 0 ) return "help\\cbot\\break.txt"; + if ( strcmp(token, "continue" ) == 0 ) return "help\\cbot\\continue.txt"; + if ( strcmp(token, "return" ) == 0 ) return "help\\cbot\\return.txt"; + if ( strcmp(token, "sizeof" ) == 0 ) return "help\\cbot\\sizeof.txt"; + if ( strcmp(token, "int" ) == 0 ) return "help\\cbot\\int.txt"; + if ( strcmp(token, "float" ) == 0 ) return "help\\cbot\\float.txt"; + if ( strcmp(token, "bool" ) == 0 ) return "help\\cbot\\bool.txt"; + if ( strcmp(token, "string" ) == 0 ) return "help\\cbot\\string.txt"; + if ( strcmp(token, "point" ) == 0 ) return "help\\cbot\\point.txt"; + if ( strcmp(token, "object" ) == 0 ) return "help\\cbot\\object.txt"; + if ( strcmp(token, "file" ) == 0 ) return "help\\cbot\\file.txt"; + if ( strcmp(token, "void" ) == 0 ) return "help\\cbot\\void.txt"; + if ( strcmp(token, "null" ) == 0 ) return "help\\cbot\\null.txt"; + if ( strcmp(token, "nan" ) == 0 ) return "help\\cbot\\nan.txt"; + if ( strcmp(token, "true" ) == 0 ) return "help\\cbot\\true.txt"; + if ( strcmp(token, "false" ) == 0 ) return "help\\cbot\\false.txt"; + if ( strcmp(token, "sin" ) == 0 ) return "help\\cbot\\expr.txt"; + if ( strcmp(token, "cos" ) == 0 ) return "help\\cbot\\expr.txt"; + if ( strcmp(token, "tan" ) == 0 ) return "help\\cbot\\expr.txt"; + if ( strcmp(token, "asin" ) == 0 ) return "help\\cbot\\expr.txt"; + if ( strcmp(token, "acos" ) == 0 ) return "help\\cbot\\expr.txt"; + if ( strcmp(token, "atan" ) == 0 ) return "help\\cbot\\expr.txt"; + if ( strcmp(token, "sqrt" ) == 0 ) return "help\\cbot\\expr.txt"; + if ( strcmp(token, "pow" ) == 0 ) return "help\\cbot\\expr.txt"; + if ( strcmp(token, "rand" ) == 0 ) return "help\\cbot\\expr.txt"; + if ( strcmp(token, "abs" ) == 0 ) return "help\\cbot\\expr.txt"; + if ( strcmp(token, "retobject" ) == 0 ) return "help\\cbot\\retobj.txt"; + if ( strcmp(token, "search" ) == 0 ) return "help\\cbot\\search.txt"; + if ( strcmp(token, "radar" ) == 0 ) return "help\\cbot\\radar.txt"; + if ( strcmp(token, "direction" ) == 0 ) return "help\\cbot\\direct.txt"; + if ( strcmp(token, "distance" ) == 0 ) return "help\\cbot\\dist.txt"; + if ( strcmp(token, "distance2d" ) == 0 ) return "help\\cbot\\dist2d.txt"; + if ( strcmp(token, "space" ) == 0 ) return "help\\cbot\\space.txt"; + if ( strcmp(token, "flatground" ) == 0 ) return "help\\cbot\\flatgrnd.txt"; + if ( strcmp(token, "wait" ) == 0 ) return "help\\cbot\\wait.txt"; + if ( strcmp(token, "move" ) == 0 ) return "help\\cbot\\move.txt"; + if ( strcmp(token, "turn" ) == 0 ) return "help\\cbot\\turn.txt"; + if ( strcmp(token, "goto" ) == 0 ) return "help\\cbot\\goto.txt"; + if ( strcmp(token, "find" ) == 0 ) return "help\\cbot\\find.txt"; + if ( strcmp(token, "grab" ) == 0 ) return "help\\cbot\\grab.txt"; + if ( strcmp(token, "drop" ) == 0 ) return "help\\cbot\\drop.txt"; + if ( strcmp(token, "sniff" ) == 0 ) return "help\\cbot\\sniff.txt"; + if ( strcmp(token, "receive" ) == 0 ) return "help\\cbot\\receive.txt"; + if ( strcmp(token, "send" ) == 0 ) return "help\\cbot\\send.txt"; + if ( strcmp(token, "deleteinfo" ) == 0 ) return "help\\cbot\\delinfo.txt"; + if ( strcmp(token, "testinfo" ) == 0 ) return "help\\cbot\\testinfo.txt"; + if ( strcmp(token, "thump" ) == 0 ) return "help\\cbot\\thump.txt"; + if ( strcmp(token, "recycle" ) == 0 ) return "help\\cbot\\recycle.txt"; + if ( strcmp(token, "shield" ) == 0 ) return "help\\cbot\\shield.txt"; + if ( strcmp(token, "fire" ) == 0 ) return "help\\cbot\\fire.txt"; + if ( strcmp(token, "antfire" ) == 0 ) return "help\\cbot\\antfire.txt"; + if ( strcmp(token, "aim" ) == 0 ) return "help\\cbot\\aim.txt"; + if ( strcmp(token, "motor" ) == 0 ) return "help\\cbot\\motor.txt"; + if ( strcmp(token, "jet" ) == 0 ) return "help\\cbot\\jet.txt"; + if ( strcmp(token, "topo" ) == 0 ) return "help\\cbot\\topo.txt"; + if ( strcmp(token, "message" ) == 0 ) return "help\\cbot\\message.txt"; + if ( strcmp(token, "abstime" ) == 0 ) return "help\\cbot\\abstime.txt"; + if ( strcmp(token, "BlackArrow" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "RedArrow" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "White" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Black" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Gray" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "LightGray" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Red" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Pink" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Purple" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Orange" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Yellow" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Beige" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Brown" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Skin" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Green" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "LightGreen" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "Blue" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "LightBlue" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "InFront" ) == 0 ) return "help\\cbot\\grab.txt"; + if ( strcmp(token, "Behind" ) == 0 ) return "help\\cbot\\grab.txt"; + if ( strcmp(token, "EnergyCell" ) == 0 ) return "help\\cbot\\grab.txt"; + if ( strcmp(token, "DisplayError" ) == 0 ) return "help\\cbot\\message.txt"; + if ( strcmp(token, "DisplayWarning") == 0 ) return "help\\cbot\\message.txt"; + if ( strcmp(token, "DisplayInfo" ) == 0 ) return "help\\cbot\\message.txt"; + if ( strcmp(token, "DisplayMessage") == 0 ) return "help\\cbot\\message.txt"; + if ( strcmp(token, "strlen" ) == 0 ) return "help\\cbot\\string.txt"; + if ( strcmp(token, "strleft" ) == 0 ) return "help\\cbot\\string.txt"; + if ( strcmp(token, "strright" ) == 0 ) return "help\\cbot\\string.txt"; + if ( strcmp(token, "strmid" ) == 0 ) return "help\\cbot\\string.txt"; + if ( strcmp(token, "strval" ) == 0 ) return "help\\cbot\\string.txt"; + if ( strcmp(token, "strfind" ) == 0 ) return "help\\cbot\\string.txt"; + if ( strcmp(token, "strlower" ) == 0 ) return "help\\cbot\\string.txt"; + if ( strcmp(token, "strupper" ) == 0 ) return "help\\cbot\\string.txt"; + if ( strcmp(token, "open" ) == 0 ) return "help\\cbot\\open.txt"; + if ( strcmp(token, "close" ) == 0 ) return "help\\cbot\\close.txt"; + if ( strcmp(token, "writeln" ) == 0 ) return "help\\cbot\\writeln.txt"; + if ( strcmp(token, "readln " ) == 0 ) return "help\\cbot\\readln.txt"; + if ( strcmp(token, "eof" ) == 0 ) return "help\\cbot\\eof.txt"; + if ( strcmp(token, "deletefile" ) == 0 ) return "help\\cbot\\deletef.txt"; + if ( strcmp(token, "openfile" ) == 0 ) return "help\\cbot\\openfile.txt"; + if ( strcmp(token, "pendown" ) == 0 ) return "help\\cbot\\pendown.txt"; + if ( strcmp(token, "penup" ) == 0 ) return "help\\cbot\\penup.txt"; + if ( strcmp(token, "pencolor" ) == 0 ) return "help\\cbot\\pencolor.txt"; + if ( strcmp(token, "penwidth" ) == 0 ) return "help\\cbot\\penwidth.txt"; + if ( strcmp(token, "extern" ) == 0 ) return "help\\cbot\\extern.txt"; + if ( strcmp(token, "class" ) == 0 ) return "help\\cbot\\class.txt"; + if ( strcmp(token, "static" ) == 0 ) return "help\\cbot\\static.txt"; + if ( strcmp(token, "public" ) == 0 ) return "help\\cbot\\public.txt"; + if ( strcmp(token, "private" ) == 0 ) return "help\\cbot\\private.txt"; + if ( strcmp(token, "synchronized" ) == 0 ) return "help\\cbot\\synchro.txt"; + if ( strcmp(token, "new" ) == 0 ) return "help\\cbot\\new.txt"; + if ( strcmp(token, "this" ) == 0 ) return "help\\cbot\\this.txt"; + return ""; +} + + +// Teste si un mot clé est un type de variable. + +BOOL IsType(const char *token) +{ + if ( strcmp(token, "void" ) == 0 ) return TRUE; + if ( strcmp(token, "int" ) == 0 ) return TRUE; + if ( strcmp(token, "float" ) == 0 ) return TRUE; + if ( strcmp(token, "bool" ) == 0 ) return TRUE; + if ( strcmp(token, "string" ) == 0 ) return TRUE; + if ( strcmp(token, "point" ) == 0 ) return TRUE; + if ( strcmp(token, "object" ) == 0 ) return TRUE; + if ( strcmp(token, "file" ) == 0 ) return TRUE; + if ( strcmp(token, "this" ) == 0 ) return TRUE; + return FALSE; +} + +// Teste si un mot clé est une fonction. + +BOOL IsFunction(const char *token) +{ + if ( strcmp(token, "sin" ) == 0 ) return TRUE; + if ( strcmp(token, "cos" ) == 0 ) return TRUE; + if ( strcmp(token, "tan" ) == 0 ) return TRUE; + if ( strcmp(token, "asin" ) == 0 ) return TRUE; + if ( strcmp(token, "acos" ) == 0 ) return TRUE; + if ( strcmp(token, "atan" ) == 0 ) return TRUE; + if ( strcmp(token, "sqrt" ) == 0 ) return TRUE; + if ( strcmp(token, "pow" ) == 0 ) return TRUE; + if ( strcmp(token, "rand" ) == 0 ) return TRUE; + if ( strcmp(token, "abs" ) == 0 ) return TRUE; + if ( strcmp(token, "retobject" ) == 0 ) return TRUE; + if ( strcmp(token, "search" ) == 0 ) return TRUE; + if ( strcmp(token, "radar" ) == 0 ) return TRUE; + if ( strcmp(token, "detect" ) == 0 ) return TRUE; + if ( strcmp(token, "direction" ) == 0 ) return TRUE; + if ( strcmp(token, "distance" ) == 0 ) return TRUE; + if ( strcmp(token, "distance2d" ) == 0 ) return TRUE; + if ( strcmp(token, "space" ) == 0 ) return TRUE; + if ( strcmp(token, "flatground" ) == 0 ) return TRUE; + if ( strcmp(token, "wait" ) == 0 ) return TRUE; + if ( strcmp(token, "move" ) == 0 ) return TRUE; + if ( strcmp(token, "turn" ) == 0 ) return TRUE; + if ( strcmp(token, "goto" ) == 0 ) return TRUE; + if ( strcmp(token, "find" ) == 0 ) return TRUE; + if ( strcmp(token, "grab" ) == 0 ) return TRUE; + if ( strcmp(token, "drop" ) == 0 ) return TRUE; + if ( strcmp(token, "sniff" ) == 0 ) return TRUE; + if ( strcmp(token, "receive" ) == 0 ) return TRUE; + if ( strcmp(token, "send" ) == 0 ) return TRUE; + if ( strcmp(token, "deleteinfo" ) == 0 ) return TRUE; + if ( strcmp(token, "testinfo" ) == 0 ) return TRUE; + if ( strcmp(token, "thump" ) == 0 ) return TRUE; + if ( strcmp(token, "recycle" ) == 0 ) return TRUE; + if ( strcmp(token, "shield" ) == 0 ) return TRUE; + if ( strcmp(token, "fire" ) == 0 ) return TRUE; + if ( strcmp(token, "antfire" ) == 0 ) return TRUE; + if ( strcmp(token, "aim" ) == 0 ) return TRUE; + if ( strcmp(token, "motor" ) == 0 ) return TRUE; + if ( strcmp(token, "jet" ) == 0 ) return TRUE; + if ( strcmp(token, "topo" ) == 0 ) return TRUE; + if ( strcmp(token, "message" ) == 0 ) return TRUE; + if ( strcmp(token, "abstime" ) == 0 ) return TRUE; + if ( strcmp(token, "ismovie" ) == 0 ) return TRUE; + if ( strcmp(token, "errmode" ) == 0 ) return TRUE; + if ( strcmp(token, "ipf" ) == 0 ) return TRUE; + if ( strcmp(token, "strlen" ) == 0 ) return TRUE; + if ( strcmp(token, "strleft" ) == 0 ) return TRUE; + if ( strcmp(token, "strright" ) == 0 ) return TRUE; + if ( strcmp(token, "strmid" ) == 0 ) return TRUE; + if ( strcmp(token, "strval" ) == 0 ) return TRUE; + if ( strcmp(token, "strfind" ) == 0 ) return TRUE; + if ( strcmp(token, "strlower" ) == 0 ) return TRUE; + if ( strcmp(token, "strupper" ) == 0 ) return TRUE; + if ( strcmp(token, "open" ) == 0 ) return TRUE; + if ( strcmp(token, "close" ) == 0 ) return TRUE; + if ( strcmp(token, "writeln" ) == 0 ) return TRUE; + if ( strcmp(token, "readln" ) == 0 ) return TRUE; + if ( strcmp(token, "eof" ) == 0 ) return TRUE; + if ( strcmp(token, "deletefile" ) == 0 ) return TRUE; + if ( strcmp(token, "openfile" ) == 0 ) return TRUE; + if ( strcmp(token, "pendown" ) == 0 ) return TRUE; + if ( strcmp(token, "penup" ) == 0 ) return TRUE; + if ( strcmp(token, "pencolor" ) == 0 ) return TRUE; + if ( strcmp(token, "penwidth" ) == 0 ) return TRUE; + if ( strcmp(token, "sizeof" ) == 0 ) return TRUE; + return FALSE; +} + + +// Retourne l'aide compacte pour une instruction. + +char* RetHelpText(const char *token) +{ + if ( strcmp(token, "if" ) == 0 ) return "if ( condition ) { bloc }"; + if ( strcmp(token, "else" ) == 0 ) return "else { bloc }"; + if ( strcmp(token, "repeat" ) == 0 ) return "repeat ( number )"; + if ( strcmp(token, "for" ) == 0 ) return "for ( before ; condition ; end )"; + if ( strcmp(token, "while" ) == 0 ) return "while ( condition ) { bloc }"; + if ( strcmp(token, "do" ) == 0 ) return "do { bloc } while ( condition );"; + if ( strcmp(token, "break" ) == 0 ) return "break;"; + if ( strcmp(token, "continue" ) == 0 ) return "continue;"; + if ( strcmp(token, "return" ) == 0 ) return "return;"; + if ( strcmp(token, "sizeof" ) == 0 ) return "sizeof( array );"; + if ( strcmp(token, "int" ) == 0 ) return "int"; + if ( strcmp(token, "sin" ) == 0 ) return "sin ( angle );"; + if ( strcmp(token, "cos" ) == 0 ) return "cos ( angle );"; + if ( strcmp(token, "tan" ) == 0 ) return "tan ( angle );"; + if ( strcmp(token, "asin" ) == 0 ) return "asin ( value );"; + if ( strcmp(token, "acos" ) == 0 ) return "acos ( value );"; + if ( strcmp(token, "atan" ) == 0 ) return "atan ( value );"; + if ( strcmp(token, "sqrt" ) == 0 ) return "sqrt ( value );"; + if ( strcmp(token, "pow" ) == 0 ) return "pow ( x, y );"; + if ( strcmp(token, "rand" ) == 0 ) return "rand ( );"; + if ( strcmp(token, "abs" ) == 0 ) return "abs ( value );"; + if ( strcmp(token, "retobject" ) == 0 ) return "retobjet ( );"; + if ( strcmp(token, "search" ) == 0 ) return "search ( );"; + if ( strcmp(token, "radar" ) == 0 ) return "radar ( cat, angle, focus, min, max, sens );"; + if ( strcmp(token, "detect" ) == 0 ) return "detect ( cat );"; + if ( strcmp(token, "direction" ) == 0 ) return "direction ( position );"; + if ( strcmp(token, "distance2d") == 0 ) return "distance2d ( p1, p2 );"; + if ( strcmp(token, "distance" ) == 0 ) return "distance ( p1, p2 );"; + if ( strcmp(token, "space" ) == 0 ) return "space ( center, rmin, rmax, dist );"; + if ( strcmp(token, "flatground") == 0 ) return "flatground ( center, rmax );"; + if ( strcmp(token, "wait" ) == 0 ) return "wait ( time );"; + if ( strcmp(token, "move" ) == 0 ) return "move ( distance );"; + if ( strcmp(token, "turn" ) == 0 ) return "turn ( angle );"; + if ( strcmp(token, "goto" ) == 0 ) return "goto ( position, altitude );"; + if ( strcmp(token, "find" ) == 0 ) return "find ( cat );"; + if ( strcmp(token, "grab" ) == 0 ) return "grab ( order );"; + if ( strcmp(token, "drop" ) == 0 ) return "drop ( order );"; + if ( strcmp(token, "sniff" ) == 0 ) return "sniff ( );"; + if ( strcmp(token, "receive" ) == 0 ) return "receive ( name, power );"; + if ( strcmp(token, "send" ) == 0 ) return "send ( name, value, power );"; + if ( strcmp(token, "deleteinfo") == 0 ) return "deleteinfo ( name, power );"; + if ( strcmp(token, "testinfo" ) == 0 ) return "testinfo ( name, power );"; + if ( strcmp(token, "thump" ) == 0 ) return "thump ( );"; + if ( strcmp(token, "recycle" ) == 0 ) return "recycle ( );"; + if ( strcmp(token, "shield" ) == 0 ) return "shield ( oper, radius );"; + if ( strcmp(token, "fire" ) == 0 ) return "fire ( time );"; + if ( strcmp(token, "antfire" ) == 0 ) return "antfire ( );"; + if ( strcmp(token, "aim" ) == 0 ) return "aim ( angle );"; + if ( strcmp(token, "motor" ) == 0 ) return "motor ( left, right );"; + if ( strcmp(token, "jet" ) == 0 ) return "jet ( power );"; + if ( strcmp(token, "topo" ) == 0 ) return "topo ( position );"; + if ( strcmp(token, "message" ) == 0 ) return "message ( string, type );"; + if ( strcmp(token, "abstime" ) == 0 ) return "abstime ( );"; + if ( strcmp(token, "ismovie" ) == 0 ) return "ismovie ( );"; + if ( strcmp(token, "errmode" ) == 0 ) return "errmode ( mdoe );"; + if ( strcmp(token, "ipf" ) == 0 ) return "ipf ( number );"; + if ( strcmp(token, "strlen" ) == 0 ) return "strlen ( string );"; + if ( strcmp(token, "strleft" ) == 0 ) return "strleft ( string, len );"; + if ( strcmp(token, "strright" ) == 0 ) return "strright ( string, len );"; + if ( strcmp(token, "strmid" ) == 0 ) return "strmid ( string, pos, len );"; + if ( strcmp(token, "strval" ) == 0 ) return "strval ( string );"; + if ( strcmp(token, "strfind" ) == 0 ) return "strfind ( string, substring );"; + if ( strcmp(token, "strlower" ) == 0 ) return "strlower ( string );"; + if ( strcmp(token, "strupper" ) == 0 ) return "strupper ( string );"; + if ( strcmp(token, "open" ) == 0 ) return "open ( filename, mode );"; + if ( strcmp(token, "close" ) == 0 ) return "close ( );"; + if ( strcmp(token, "writeln" ) == 0 ) return "writeln ( string );"; + if ( strcmp(token, "readln" ) == 0 ) return "readln ( );"; + if ( strcmp(token, "eof" ) == 0 ) return "eof ( );"; + if ( strcmp(token, "deletefile") == 0 ) return "deletefile ( filename );"; + if ( strcmp(token, "openfile" ) == 0 ) return "openfile ( filename, mode );"; + if ( strcmp(token, "pendown" ) == 0 ) return "pendown ( color, width );"; + if ( strcmp(token, "penup" ) == 0 ) return "penup ( );"; + if ( strcmp(token, "pencolor" ) == 0 ) return "pencolor ( color );"; + if ( strcmp(token, "penwidth" ) == 0 ) return "penwidth ( width );"; + return ""; +} + + diff --git a/src/cbottoken.h b/src/cbottoken.h new file mode 100644 index 00000000..34c391c1 --- /dev/null +++ b/src/cbottoken.h @@ -0,0 +1,24 @@ +// cbottoken.h + +#ifndef _CBOTTOKEN_H_ +#define _CBOTTOKEN_H_ + + + +enum ObjectType; + + + +// Procédures. + +extern char* RetObjectName(ObjectType type); +extern char* RetObjectAlias(ObjectType type); +extern char* RetHelpFilename(ObjectType type); +extern char* RetHelpFilename(const char *token); +extern BOOL IsType(const char *token); +extern BOOL IsFunction(const char *token); +extern char* RetHelpText(const char *token); + + + +#endif //_CBOTTOKEN_H_ diff --git a/src/ceebot.ini b/src/ceebot.ini new file mode 100644 index 00000000..0de7a320 --- /dev/null +++ b/src/ceebot.ini @@ -0,0 +1,66 @@ +[Directory] +scene=scene +savegame=savegame +public=program +user=user +files=files +[Setup] +TotoMode=1 +Tooltips=1 +InterfaceGlint=1 +NiceMouse=0 +Movies=1 +NiceReset=1 +HimselfDamage=1 +CameraScroll=0 +CameraInvertX=0 +InterfaceEffect=1 +GroundShadow=1 +GroundSpot=1 +ObjectDirty=1 +FogMode=1 +LensMode=1 +SkyMode=1 +PlanetMode=1 +LightMode=1 +UseJoystick=0 +ParticuleDensity=1.00 +ClippingDistance=1.00 +ObjectDetail=2.00 +GadgetQuantity=1.00 +TextureQuality=1 +AudioVolume=4 +MidiVolume=15 +Sound3D=0 +EditIndentMode=1 +EditIndentValue=4 +KeyMap=37+0 39+0 38+0 40+0 16+0 17+0 32+258 96+262 13+257 107+261 109+260 9+259 36+263 27+0 112+0 113+0 110+0 115+0 116+0 117+0 +DeleteGamer=1 +Soluce4=1 +[Engine] +AlphaMode=1 +StateColor=-1 +BlackSrcBlend=0 +BlackDestBlend=0 +WhiteSrcBlend=0 +WhiteDestBlend=0 +DiffuseSrcBlend=0 +DiffuseDestBlend=0 +AlphaSrcBlend=0 +AlphaDestBlend=0 +[Gamer] +LastName=Linda +[Edit] +FontSize=9.00 +WindowPos.x=0.09 +WindowPos.y=0.08 +WindowDim.x=0.69 +WindowDim.y=0.84 +IOPos.x=0.36 +IOPos.y=0.15 +IODim.x=0.50 +IODim.y=0.55 +[Device] +Name=Direct3D HAL +Mode=1600 x 1200 x 32 +FullScreen=0 diff --git a/src/check.cpp b/src/check.cpp new file mode 100644 index 00000000..4c8637b4 --- /dev/null +++ b/src/check.cpp @@ -0,0 +1,156 @@ +// check.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "text.h" +#include "check.h" + + + + +// Constructeur de l'objet. + +CCheck::CCheck(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); +} + +// Destructeur de l'objet. + +CCheck::~CCheck() +{ + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CCheck::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + char name[100]; + char* p; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + + GetResource(RES_EVENT, eventMsg, name); + p = strchr(name, '\\'); + if ( p != 0 ) *p = 0; + SetName(name); + + return TRUE; +} + + +// Gestion d'un événement. + +BOOL CCheck::EventProcess(const Event &event) +{ + if ( m_state & STATE_DEAD ) return TRUE; + + CControl::EventProcess(event); + + if ( event.event == EVENT_LBUTTONDOWN && + (m_state & STATE_VISIBLE) && + (m_state & STATE_ENABLE) ) + { + if ( CControl::Detect(event.pos) ) + { + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + return FALSE; + } + } + + return TRUE; +} + + +// Dessine le bouton. + +void CCheck::Draw() +{ + FPOINT iDim, pos; + float zoomExt, zoomInt; + int icon; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + iDim = m_dim; + m_dim.x = m_dim.y*0.75f; // carré + + if ( m_state & STATE_SHADOW ) + { + DrawShadow(m_pos, m_dim); + } + + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + + zoomExt = 1.00f; + zoomInt = 0.95f; + + icon = 2; + if ( m_state & STATE_DEFAULT ) + { + DrawPart(23, 1.3f, 0.0f); + + zoomExt *= 1.15f; + zoomInt *= 1.15f; + } + if ( m_state & STATE_HILIGHT ) + { + icon = 1; + } + if ( m_state & STATE_PRESS ) + { + icon = 3; + zoomInt *= 0.9f; + } + if ( (m_state & STATE_ENABLE) == 0 ) + { + icon = 7; + } + if ( m_state & STATE_DEAD ) + { + icon = 17; + } + DrawPart(icon, zoomExt, 0.0f); // dessine le bouton + + if ( (m_state & STATE_DEAD) == 0 ) + { + m_engine->SetState(D3DSTATETTw); + + if ( m_state & STATE_CHECK ) + { + icon = 16; // vu + DrawPart(icon, zoomInt, 0.0f); // dessine l'icône + } + } + + m_dim = iDim; + + if ( m_state & STATE_DEAD ) return; + + // Dessine le nom. + pos.x = m_pos.x+m_dim.y/0.9f; + pos.y = m_pos.y+m_dim.y*0.50f; + pos.y -= m_engine->RetText()->RetHeight(m_fontSize, m_fontType)/2.0f; + m_engine->RetText()->DrawText(m_name, pos, m_dim.x, 1, m_fontSize, m_fontStretch, m_fontType, 0); +} + + diff --git a/src/check.h b/src/check.h new file mode 100644 index 00000000..35aa543a --- /dev/null +++ b/src/check.h @@ -0,0 +1,32 @@ +// check.h + +#ifndef _CHECK_H_ +#define _CHECK_H_ + + +#include "control.h" + + +class CD3DEngine; + + + +class CCheck : public CControl +{ +public: + CCheck(CInstanceManager* iMan); + ~CCheck(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + BOOL EventProcess(const Event &event); + + void Draw(); + +protected: + +protected: +}; + + +#endif //_CHECK_H_ diff --git a/src/cloud.cpp b/src/cloud.cpp new file mode 100644 index 00000000..ee41c48f --- /dev/null +++ b/src/cloud.cpp @@ -0,0 +1,317 @@ +// cloud.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "terrain.h" +#include "object.h" +#include "cloud.h" + + + +#define DIMEXPAND 4 // extension des dimensions + + + +// Constructeur des nuages. + +CCloud::CCloud(CInstanceManager* iMan, CD3DEngine* engine) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_CLOUD, this); + + m_engine = engine; + m_terrain = 0; + + m_level = 0.0f; + m_wind = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_subdiv = 8; + m_filename[0] = 0; + m_bEnable = TRUE; +} + +// Destructeur des nuages. + +CCloud::~CCloud() +{ +} + + +BOOL CCloud::EventProcess(const Event &event) +{ + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + + return TRUE; +} + +// Fait évoluer les nuages. + +BOOL CCloud::EventFrame(const Event &event) +{ + if ( m_engine->RetPause() ) return TRUE; + + m_time += event.rTime; + + if ( m_level == 0.0f ) return TRUE; + + if ( m_time-m_lastTest < 0.2f ) return TRUE; + m_lastTest = m_time; + + return TRUE; +} + + +// Ajuste la position et la normale, pour imiter des nuages +// en mouvement. + +void CCloud::AdjustLevel(D3DVECTOR &pos, D3DVECTOR &eye, float deep, + FPOINT &uv1, FPOINT &uv2) +{ + float dist, factor; + + uv1.x = (pos.x+20000.0f)/1280.0f; + uv1.y = (pos.z+20000.0f)/1280.0f; + uv1.x -= m_time*(m_wind.x/100.0f); + uv1.y -= m_time*(m_wind.z/100.0f); + + uv2.x = 0.0f; + uv2.y = 0.0f; + + dist = Length2d(pos, eye); + factor = powf(dist/deep, 2.0f); + pos.y -= m_level*factor*10.0f; +} + +inline DWORD F2DW( FLOAT f ) +{ + return *((DWORD*)&f); +} + +// Dessine les nuages. + +void CCloud::Draw() +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2* vertex; + D3DMATRIX* matView; + D3DMATERIAL7 material; + D3DMATRIX matrix; + D3DVECTOR n, pos, p, eye; + FPOINT uv1, uv2; + float iDeep, deep, size, fogStart, fogEnd; + int i, j, u; + + if ( !m_bEnable ) return; + if ( m_level == 0.0f ) return; + if ( m_lineUsed == 0 ) return; + + vertex = (D3DVERTEX2*)malloc(sizeof(D3DVERTEX2)*(m_brick+2)*2); + + iDeep = m_engine->RetDeepView(); + deep = (m_brick*m_size)/2.0f; + m_engine->SetDeepView(deep); + m_engine->SetFocus(m_engine->RetFocus()); + m_engine->UpdateMatProj(); // augmente la profondeur de vue + +//? fogStart = deep*0.10f; +//? fogEnd = deep*0.16f; + fogStart = deep*0.15f; + fogEnd = deep*0.24f; + + device = m_engine->RetD3DDevice(); + device->SetRenderState(D3DRENDERSTATE_AMBIENT, 0x00000000); + device->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE); + device->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE); +//? device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, TRUE); + device->SetRenderState(D3DRENDERSTATE_FOGENABLE, TRUE); + device->SetRenderState(D3DRENDERSTATE_FOGSTART, F2DW(fogStart)); + device->SetRenderState(D3DRENDERSTATE_FOGEND, F2DW(fogEnd)); + + matView = m_engine->RetMatView(); + device->SetTransform(D3DTRANSFORMSTATE_VIEW, matView); + + ZeroMemory( &material, sizeof(D3DMATERIAL7) ); + material.diffuse = m_diffuse; + material.ambient = m_ambient; + m_engine->SetMaterial(material); + + m_engine->SetTexture(m_filename, 0); + m_engine->SetTexture(m_filename, 1); + +//? m_engine->SetState(D3DSTATETTb|D3DSTATEDUALw|D3DSTATEWRAP); + m_engine->SetState(D3DSTATETTb|D3DSTATEFOG|D3DSTATEWRAP); +//? m_engine->SetState(D3DSTATEWRAP); + + D3DUtil_SetIdentityMatrix(matrix); + device->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + size = m_size/2.0f; + eye = m_engine->RetEyePt(); + n = D3DVECTOR(0.0f, -1.0f, 0.0f); + + // Dessine toutes les lignes. + for ( i=0 ; iDrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, u, NULL); + m_engine->AddStatisticTriangle(u-2); + } + + m_engine->SetDeepView(iDeep); + m_engine->SetFocus(m_engine->RetFocus()); + m_engine->UpdateMatProj(); // remet profondeur de vue initiale + + free(vertex); +} + + +// Met à jour les positions par-rapport au terrain. + +BOOL CCloud::CreateLine(int x, int y, int len) +{ + float offset; + + m_line[m_lineUsed].x = x; + m_line[m_lineUsed].y = y; + m_line[m_lineUsed].len = len; + + offset = m_brick*m_size/2.0f - m_size/2.0f; + + m_line[m_lineUsed].px1 = m_size* m_line[m_lineUsed].x - offset; + m_line[m_lineUsed].px2 = m_size*(m_line[m_lineUsed].x+m_line[m_lineUsed].len) - offset; + m_line[m_lineUsed].pz = m_size* m_line[m_lineUsed].y - offset; + + m_lineUsed ++; + + return ( m_lineUsed < MAXCLOUDLINE ); +} + +// Crée toutes les étendues de nuages. + +BOOL CCloud::Create(const char *filename, + D3DCOLORVALUE diffuse, D3DCOLORVALUE ambient, + float level) +{ + int y; + + m_diffuse = diffuse; + m_ambient = ambient; + m_level = level; + m_time = 0.0f; + m_lastTest = 0.0f; + strcpy(m_filename, filename); + + if ( m_filename[0] != 0 ) + { + m_engine->LoadTexture(m_filename, 0); + m_engine->LoadTexture(m_filename, 1); + } + + if ( m_terrain == 0 ) + { + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + } + + m_wind = m_terrain->RetWind(); + + m_brick = m_terrain->RetBrick()*m_terrain->RetMosaic()*DIMEXPAND; + m_size = m_terrain->RetSize(); + + m_brick /= m_subdiv*DIMEXPAND; + m_size *= m_subdiv*DIMEXPAND; + + if ( m_level == 0.0f ) return TRUE; + + m_lineUsed = 0; + for ( y=0 ; y +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "language.h" +#include "global.h" +#include "event.h" +#include "water.h" +#include "pyro.h" +#include "camera.h" +#include "object.h" +#include "cmdtoken.h" + + + + + +// Saute les espaces. + +char* SkipSpace(char *line) +{ + while ( *line == ' ' ) + { + line ++; + } + return line; +} + +// Vérifie si une ligne contient une commande. + +BOOL Cmd(char *line, char *token) +{ + char* p; + + line = SkipSpace(line); + p = strstr(line, token); + return ( p == line ); // commande au début ? +} + +// Cherche un opérateur. + +char* SearchOp(char *line, char *op) +{ + char opeq[50]; + char* p; + + strcpy(opeq, " "); + strcat(opeq, op); + strcat(opeq, "="); + + p = strstr(line, opeq); + if ( p == 0 ) // pas trouvé ? + { + return line+strlen(line); // pointe le zéro terminateur + } + return p+strlen(opeq); // pointe après le "=" +} + +// Cherche le nième argument. + +char* SearchArg(char *line, int rank) +{ + int i; + char c; + + for ( i=0 ; i= '0' && *p <= '9' ) + { + n *= 16; + n += (*p++)-'0'; + continue; + } + if ( *p >= 'a' && *p <= 'f' ) + { + n *= 16; + n += (*p++)-'a'+10; + continue; + } + break; + } + } + else // nombre entier ? + { + sscanf(p, "%d", &n); + } + return n; +} + +// Lit un nombre réel. + +float GetFloat(char *line, int rank, float def) +{ + char* p; + float n = 0.0f; + + p = SearchArg(line, rank); + if ( *p == 0 ) return def; + + sscanf(p, "%f", &n); + return n; +} + +// Lit une chaîne. + +void GetString(char *line, int rank, char *buffer) +{ + char* p; + + p = SearchArg(line, rank); + *buffer = 0; + if ( *p++ != '"' ) return; + + while ( p[0] != 0 ) + { + if ( p[0] == '"' && + p[1] == '"' ) + { + *buffer++ = *p++; + p++; + continue; + } + if ( p[0] == '"' ) break; + + *buffer++ = *p++; + } + *buffer = 0; +} + +// Retourne le type d'un objet. + +ObjectType GetTypeObject(char *line, int rank, ObjectType def) +{ + char* p; + + p = SearchArg(line, rank); + if ( *p == 0 ) return def; + + if ( Cmd(p, "All" ) ) return OBJECT_NULL; + if ( Cmd(p, "Portico" ) ) return OBJECT_PORTICO; + if ( Cmd(p, "SpaceShip" ) ) return OBJECT_BASE; + if ( Cmd(p, "PracticeBot" ) ) return OBJECT_MOBILEwt; + if ( Cmd(p, "WingedGrabber" ) ) return OBJECT_MOBILEfa; + if ( Cmd(p, "TrackedGrabber" ) ) return OBJECT_MOBILEta; + if ( Cmd(p, "WheeledGrabber" ) ) return OBJECT_MOBILEwa; + if ( Cmd(p, "LeggedGrabber" ) ) return OBJECT_MOBILEia; + if ( Cmd(p, "WingedShooter" ) ) return OBJECT_MOBILEfc; + if ( Cmd(p, "TrackedShooter" ) ) return OBJECT_MOBILEtc; + if ( Cmd(p, "WheeledShooter" ) ) return OBJECT_MOBILEwc; + if ( Cmd(p, "LeggedShooter" ) ) return OBJECT_MOBILEic; + if ( Cmd(p, "WingedOrgaShooter" ) ) return OBJECT_MOBILEfi; + if ( Cmd(p, "TrackedOrgaShooter") ) return OBJECT_MOBILEti; + if ( Cmd(p, "WheeledOrgaShooter") ) return OBJECT_MOBILEwi; + if ( Cmd(p, "LeggedOrgaShooter" ) ) return OBJECT_MOBILEii; + if ( Cmd(p, "WingedSniffer" ) ) return OBJECT_MOBILEfs; + if ( Cmd(p, "TrackedSniffer" ) ) return OBJECT_MOBILEts; + if ( Cmd(p, "WheeledSniffer" ) ) return OBJECT_MOBILEws; + if ( Cmd(p, "LeggedSniffer" ) ) return OBJECT_MOBILEis; + if ( Cmd(p, "Thumper" ) ) return OBJECT_MOBILErt; + if ( Cmd(p, "PhazerShooter" ) ) return OBJECT_MOBILErc; + if ( Cmd(p, "Recycler" ) ) return OBJECT_MOBILErr; + if ( Cmd(p, "Shielder" ) ) return OBJECT_MOBILErs; + if ( Cmd(p, "Subber" ) ) return OBJECT_MOBILEsa; + if ( Cmd(p, "TargetBot" ) ) return OBJECT_MOBILEtg; + if ( Cmd(p, "Scribbler" ) ) return OBJECT_MOBILEdr; + if ( Cmd(p, "PowerSpot" ) ) return OBJECT_MARKPOWER; + if ( Cmd(p, "TitaniumSpot" ) ) return OBJECT_MARKSTONE; + if ( Cmd(p, "UraniumSpot" ) ) return OBJECT_MARKURANIUM; + if ( Cmd(p, "PlatinumSpot" ) ) return OBJECT_MARKURANIUM; + if ( Cmd(p, "KeyASpot" ) ) return OBJECT_MARKKEYa; + if ( Cmd(p, "KeyBSpot" ) ) return OBJECT_MARKKEYb; + if ( Cmd(p, "KeyCSpot" ) ) return OBJECT_MARKKEYc; + if ( Cmd(p, "KeyDSpot" ) ) return OBJECT_MARKKEYd; + if ( Cmd(p, "WayPoint" ) ) return OBJECT_WAYPOINT; + if ( Cmd(p, "BlueFlag" ) ) return OBJECT_FLAGb; + if ( Cmd(p, "RedFlag" ) ) return OBJECT_FLAGr; + if ( Cmd(p, "GreenFlag" ) ) return OBJECT_FLAGg; + if ( Cmd(p, "YellowFlag" ) ) return OBJECT_FLAGy; + if ( Cmd(p, "VioletFlag" ) ) return OBJECT_FLAGv; + if ( Cmd(p, "PowerCell" ) ) return OBJECT_POWER; + if ( Cmd(p, "FuelCellPlant" ) ) return OBJECT_NUCLEAR; + if ( Cmd(p, "FuelCell" ) ) return OBJECT_ATOMIC; + if ( Cmd(p, "NuclearCell" ) ) return OBJECT_ATOMIC; + if ( Cmd(p, "TitaniumOre" ) ) return OBJECT_STONE; + if ( Cmd(p, "UraniumOre" ) ) return OBJECT_URANIUM; + if ( Cmd(p, "PlatinumOre" ) ) return OBJECT_URANIUM; + if ( Cmd(p, "Titanium" ) ) return OBJECT_METAL; + if ( Cmd(p, "OrgaMatter" ) ) return OBJECT_BULLET; + if ( Cmd(p, "BlackBox" ) ) return OBJECT_BBOX; + if ( Cmd(p, "KeyA" ) ) return OBJECT_KEYa; + if ( Cmd(p, "KeyB" ) ) return OBJECT_KEYb; + if ( Cmd(p, "KeyC" ) ) return OBJECT_KEYc; + if ( Cmd(p, "KeyD" ) ) return OBJECT_KEYd; + if ( Cmd(p, "TNT" ) ) return OBJECT_TNT; + if ( Cmd(p, "Scrap1" ) ) return OBJECT_SCRAP1; + if ( Cmd(p, "Scrap2" ) ) return OBJECT_SCRAP2; + if ( Cmd(p, "Scrap3" ) ) return OBJECT_SCRAP3; + if ( Cmd(p, "Scrap4" ) ) return OBJECT_SCRAP4; + if ( Cmd(p, "Scrap5" ) ) return OBJECT_SCRAP5; + if ( Cmd(p, "Mine" ) ) return OBJECT_BOMB; + if ( Cmd(p, "Firework" ) ) return OBJECT_WINFIRE; + if ( Cmd(p, "Bag" ) ) return OBJECT_BAG; + if ( Cmd(p, "Greenery10" ) ) return OBJECT_PLANT10; + if ( Cmd(p, "Greenery11" ) ) return OBJECT_PLANT11; + if ( Cmd(p, "Greenery12" ) ) return OBJECT_PLANT12; + if ( Cmd(p, "Greenery13" ) ) return OBJECT_PLANT13; + if ( Cmd(p, "Greenery14" ) ) return OBJECT_PLANT14; + if ( Cmd(p, "Greenery15" ) ) return OBJECT_PLANT15; + if ( Cmd(p, "Greenery16" ) ) return OBJECT_PLANT16; + if ( Cmd(p, "Greenery17" ) ) return OBJECT_PLANT17; + if ( Cmd(p, "Greenery18" ) ) return OBJECT_PLANT18; + if ( Cmd(p, "Greenery19" ) ) return OBJECT_PLANT19; + if ( Cmd(p, "Greenery0" ) ) return OBJECT_PLANT0; + if ( Cmd(p, "Greenery1" ) ) return OBJECT_PLANT1; + if ( Cmd(p, "Greenery2" ) ) return OBJECT_PLANT2; + if ( Cmd(p, "Greenery3" ) ) return OBJECT_PLANT3; + if ( Cmd(p, "Greenery4" ) ) return OBJECT_PLANT4; + if ( Cmd(p, "Greenery5" ) ) return OBJECT_PLANT5; + if ( Cmd(p, "Greenery6" ) ) return OBJECT_PLANT6; + if ( Cmd(p, "Greenery7" ) ) return OBJECT_PLANT7; + if ( Cmd(p, "Greenery8" ) ) return OBJECT_PLANT8; + if ( Cmd(p, "Greenery9" ) ) return OBJECT_PLANT9; + if ( Cmd(p, "Tree0" ) ) return OBJECT_TREE0; + if ( Cmd(p, "Tree1" ) ) return OBJECT_TREE1; + if ( Cmd(p, "Tree2" ) ) return OBJECT_TREE2; + if ( Cmd(p, "Tree3" ) ) return OBJECT_TREE3; + if ( Cmd(p, "Tree4" ) ) return OBJECT_TREE4; + if ( Cmd(p, "Tree5" ) ) return OBJECT_TREE5; + if ( Cmd(p, "Tree6" ) ) return OBJECT_TREE6; + if ( Cmd(p, "Tree7" ) ) return OBJECT_TREE7; + if ( Cmd(p, "Tree8" ) ) return OBJECT_TREE8; + if ( Cmd(p, "Tree9" ) ) return OBJECT_TREE9; + if ( Cmd(p, "Mushroom0" ) ) return OBJECT_MUSHROOM0; + if ( Cmd(p, "Mushroom1" ) ) return OBJECT_MUSHROOM1; + if ( Cmd(p, "Mushroom2" ) ) return OBJECT_MUSHROOM2; + if ( Cmd(p, "Mushroom3" ) ) return OBJECT_MUSHROOM3; + if ( Cmd(p, "Mushroom4" ) ) return OBJECT_MUSHROOM4; + if ( Cmd(p, "Mushroom5" ) ) return OBJECT_MUSHROOM5; + if ( Cmd(p, "Mushroom6" ) ) return OBJECT_MUSHROOM6; + if ( Cmd(p, "Mushroom7" ) ) return OBJECT_MUSHROOM7; + if ( Cmd(p, "Mushroom8" ) ) return OBJECT_MUSHROOM8; + if ( Cmd(p, "Mushroom9" ) ) return OBJECT_MUSHROOM9; + if ( Cmd(p, "Home" ) ) return OBJECT_HOME1; + if ( Cmd(p, "Derrick" ) ) return OBJECT_DERRICK; + if ( Cmd(p, "BotFactory" ) ) return OBJECT_FACTORY; + if ( Cmd(p, "PowerStation" ) ) return OBJECT_STATION; + if ( Cmd(p, "Converter" ) ) return OBJECT_CONVERT; + if ( Cmd(p, "RepairCenter" ) ) return OBJECT_REPAIR; + if ( Cmd(p, "Destroyer" ) ) return OBJECT_DESTROYER; + if ( Cmd(p, "DefenseTower" ) ) return OBJECT_TOWER; + if ( Cmd(p, "AlienNest" ) ) return OBJECT_NEST; + if ( Cmd(p, "ResearchCenter" ) ) return OBJECT_RESEARCH; + if ( Cmd(p, "RadarStation" ) ) return OBJECT_RADAR; + if ( Cmd(p, "ExchangePost" ) ) return OBJECT_INFO; + if ( Cmd(p, "PowerPlant" ) ) return OBJECT_ENERGY; + if ( Cmd(p, "AutoLab" ) ) return OBJECT_LABO; + if ( Cmd(p, "NuclearPlant" ) ) return OBJECT_NUCLEAR; + if ( Cmd(p, "PowerCaptor" ) ) return OBJECT_PARA; + if ( Cmd(p, "Vault" ) ) return OBJECT_SAFE; + if ( Cmd(p, "Houston" ) ) return OBJECT_HUSTON; + if ( Cmd(p, "Target1" ) ) return OBJECT_TARGET1; + if ( Cmd(p, "Target2" ) ) return OBJECT_TARGET2; + if ( Cmd(p, "StartArea" ) ) return OBJECT_START; + if ( Cmd(p, "GoalArea" ) ) return OBJECT_END; + if ( Cmd(p, "AlienQueen" ) ) return OBJECT_MOTHER; + if ( Cmd(p, "AlienEgg" ) ) return OBJECT_EGG; + if ( Cmd(p, "AlienAnt" ) ) return OBJECT_ANT; + if ( Cmd(p, "AlienSpider" ) ) return OBJECT_SPIDER; + if ( Cmd(p, "AlienWasp" ) ) return OBJECT_BEE; + if ( Cmd(p, "AlienWorm" ) ) return OBJECT_WORM; + if ( Cmd(p, "WreckBotw1" ) ) return OBJECT_RUINmobilew1; + if ( Cmd(p, "WreckBotw2" ) ) return OBJECT_RUINmobilew2; + if ( Cmd(p, "WreckBott1" ) ) return OBJECT_RUINmobilet1; + if ( Cmd(p, "WreckBott2" ) ) return OBJECT_RUINmobilet2; + if ( Cmd(p, "WreckBotr1" ) ) return OBJECT_RUINmobiler1; + if ( Cmd(p, "WreckBotr2" ) ) return OBJECT_RUINmobiler2; + if ( Cmd(p, "RuinBotFactory" ) ) return OBJECT_RUINfactory; + if ( Cmd(p, "RuinDoor" ) ) return OBJECT_RUINdoor; + if ( Cmd(p, "RuinSupport" ) ) return OBJECT_RUINsupport; + if ( Cmd(p, "RuinRadar" ) ) return OBJECT_RUINradar; + if ( Cmd(p, "RuinConvert" ) ) return OBJECT_RUINconvert; + if ( Cmd(p, "RuinBaseCamp" ) ) return OBJECT_RUINbase; + if ( Cmd(p, "RuinHeadCamp" ) ) return OBJECT_RUINhead; + if ( Cmd(p, "Barrier0" ) ) return OBJECT_BARRIER0; + if ( Cmd(p, "Barrier1" ) ) return OBJECT_BARRIER1; + if ( Cmd(p, "Barrier2" ) ) return OBJECT_BARRIER2; + if ( Cmd(p, "Barrier3" ) ) return OBJECT_BARRIER3; + if ( Cmd(p, "Barrier4" ) ) return OBJECT_BARRIER4; + if ( Cmd(p, "Teen40" ) ) return OBJECT_TEEN40; + if ( Cmd(p, "Teen41" ) ) return OBJECT_TEEN41; + if ( Cmd(p, "Teen42" ) ) return OBJECT_TEEN42; + if ( Cmd(p, "Teen43" ) ) return OBJECT_TEEN43; + if ( Cmd(p, "Teen44" ) ) return OBJECT_TEEN44; + if ( Cmd(p, "Teen45" ) ) return OBJECT_TEEN45; + if ( Cmd(p, "Teen46" ) ) return OBJECT_TEEN46; + if ( Cmd(p, "Teen47" ) ) return OBJECT_TEEN47; + if ( Cmd(p, "Teen48" ) ) return OBJECT_TEEN48; + if ( Cmd(p, "Teen49" ) ) return OBJECT_TEEN49; + if ( Cmd(p, "Teen30" ) ) return OBJECT_TEEN30; + if ( Cmd(p, "Teen31" ) ) return OBJECT_TEEN31; + if ( Cmd(p, "Teen32" ) ) return OBJECT_TEEN32; + if ( Cmd(p, "Teen33" ) ) return OBJECT_TEEN33; + if ( Cmd(p, "Stone" ) ) return OBJECT_TEEN34; + if ( Cmd(p, "Teen35" ) ) return OBJECT_TEEN35; + if ( Cmd(p, "Teen36" ) ) return OBJECT_TEEN36; + if ( Cmd(p, "Teen37" ) ) return OBJECT_TEEN37; + if ( Cmd(p, "Teen38" ) ) return OBJECT_TEEN38; + if ( Cmd(p, "Teen39" ) ) return OBJECT_TEEN39; + if ( Cmd(p, "Teen20" ) ) return OBJECT_TEEN20; + if ( Cmd(p, "Teen21" ) ) return OBJECT_TEEN21; + if ( Cmd(p, "Teen22" ) ) return OBJECT_TEEN22; + if ( Cmd(p, "Teen23" ) ) return OBJECT_TEEN23; + if ( Cmd(p, "Teen24" ) ) return OBJECT_TEEN24; + if ( Cmd(p, "Teen25" ) ) return OBJECT_TEEN25; + if ( Cmd(p, "Teen26" ) ) return OBJECT_TEEN26; + if ( Cmd(p, "Teen27" ) ) return OBJECT_TEEN27; + if ( Cmd(p, "Teen28" ) ) return OBJECT_TEEN28; + if ( Cmd(p, "Teen29" ) ) return OBJECT_TEEN29; + if ( Cmd(p, "Teen10" ) ) return OBJECT_TEEN10; + if ( Cmd(p, "Teen11" ) ) return OBJECT_TEEN11; + if ( Cmd(p, "Teen12" ) ) return OBJECT_TEEN12; + if ( Cmd(p, "Teen13" ) ) return OBJECT_TEEN13; + if ( Cmd(p, "Teen14" ) ) return OBJECT_TEEN14; + if ( Cmd(p, "Teen15" ) ) return OBJECT_TEEN15; + if ( Cmd(p, "Teen16" ) ) return OBJECT_TEEN16; + if ( Cmd(p, "Teen17" ) ) return OBJECT_TEEN17; + if ( Cmd(p, "Teen18" ) ) return OBJECT_TEEN18; + if ( Cmd(p, "Teen19" ) ) return OBJECT_TEEN19; + if ( Cmd(p, "Teen0" ) ) return OBJECT_TEEN0; + if ( Cmd(p, "Teen1" ) ) return OBJECT_TEEN1; + if ( Cmd(p, "Teen2" ) ) return OBJECT_TEEN2; + if ( Cmd(p, "Teen3" ) ) return OBJECT_TEEN3; + if ( Cmd(p, "Teen4" ) ) return OBJECT_TEEN4; + if ( Cmd(p, "Teen5" ) ) return OBJECT_TEEN5; + if ( Cmd(p, "Teen6" ) ) return OBJECT_TEEN6; + if ( Cmd(p, "Teen7" ) ) return OBJECT_TEEN7; + if ( Cmd(p, "Teen8" ) ) return OBJECT_TEEN8; + if ( Cmd(p, "Teen9" ) ) return OBJECT_TEEN9; + if ( Cmd(p, "Quartz0" ) ) return OBJECT_QUARTZ0; + if ( Cmd(p, "Quartz1" ) ) return OBJECT_QUARTZ1; + if ( Cmd(p, "Quartz2" ) ) return OBJECT_QUARTZ2; + if ( Cmd(p, "Quartz3" ) ) return OBJECT_QUARTZ3; + if ( Cmd(p, "Quartz4" ) ) return OBJECT_QUARTZ4; + if ( Cmd(p, "Quartz5" ) ) return OBJECT_QUARTZ5; + if ( Cmd(p, "Quartz6" ) ) return OBJECT_QUARTZ6; + if ( Cmd(p, "Quartz7" ) ) return OBJECT_QUARTZ7; + if ( Cmd(p, "Quartz8" ) ) return OBJECT_QUARTZ8; + if ( Cmd(p, "Quartz9" ) ) return OBJECT_QUARTZ9; + if ( Cmd(p, "MegaStalk0" ) ) return OBJECT_ROOT0; + if ( Cmd(p, "MegaStalk1" ) ) return OBJECT_ROOT1; + if ( Cmd(p, "MegaStalk2" ) ) return OBJECT_ROOT2; + if ( Cmd(p, "MegaStalk3" ) ) return OBJECT_ROOT3; + if ( Cmd(p, "MegaStalk4" ) ) return OBJECT_ROOT4; + if ( Cmd(p, "MegaStalk5" ) ) return OBJECT_ROOT5; + if ( Cmd(p, "MegaStalk6" ) ) return OBJECT_ROOT6; + if ( Cmd(p, "MegaStalk7" ) ) return OBJECT_ROOT7; + if ( Cmd(p, "MegaStalk8" ) ) return OBJECT_ROOT8; + if ( Cmd(p, "MegaStalk9" ) ) return OBJECT_ROOT9; + if ( Cmd(p, "ApolloLEM" ) ) return OBJECT_APOLLO1; + if ( Cmd(p, "ApolloJeep" ) ) return OBJECT_APOLLO2; + if ( Cmd(p, "ApolloFlag" ) ) return OBJECT_APOLLO3; + if ( Cmd(p, "ApolloModule" ) ) return OBJECT_APOLLO4; + if ( Cmd(p, "ApolloAntenna" ) ) return OBJECT_APOLLO5; + if ( Cmd(p, "Me" ) ) return OBJECT_HUMAN; + if ( Cmd(p, "Tech" ) ) return OBJECT_TECH; + + return def; +} + +// Retourne le nom d'un type d'objet. + +char* GetTypeObject(ObjectType type) +{ + if ( type == OBJECT_PORTICO ) return "Portico"; + if ( type == OBJECT_BASE ) return "SpaceShip"; + if ( type == OBJECT_MOBILEwt ) return "PracticeBot"; + if ( type == OBJECT_MOBILEfa ) return "WingedGrabber"; + if ( type == OBJECT_MOBILEta ) return "TrackedGrabber"; + if ( type == OBJECT_MOBILEwa ) return "WheeledGrabber"; + if ( type == OBJECT_MOBILEia ) return "LeggedGrabber"; + if ( type == OBJECT_MOBILEfc ) return "WingedShooter"; + if ( type == OBJECT_MOBILEtc ) return "TrackedShooter"; + if ( type == OBJECT_MOBILEwc ) return "WheeledShooter"; + if ( type == OBJECT_MOBILEic ) return "LeggedShooter"; + if ( type == OBJECT_MOBILEfi ) return "WingedOrgaShooter"; + if ( type == OBJECT_MOBILEti ) return "TrackedOrgaShooter"; + if ( type == OBJECT_MOBILEwi ) return "WheeledOrgaShooter"; + if ( type == OBJECT_MOBILEii ) return "LeggedOrgaShooter"; + if ( type == OBJECT_MOBILEfs ) return "WingedSniffer"; + if ( type == OBJECT_MOBILEts ) return "TrackedSniffer"; + if ( type == OBJECT_MOBILEws ) return "WheeledSniffer"; + if ( type == OBJECT_MOBILEis ) return "LeggedSniffer"; + if ( type == OBJECT_MOBILErt ) return "Thumper"; + if ( type == OBJECT_MOBILErc ) return "PhazerShooter"; + if ( type == OBJECT_MOBILErr ) return "Recycler"; + if ( type == OBJECT_MOBILErs ) return "Shielder"; + if ( type == OBJECT_MOBILEsa ) return "Subber"; + if ( type == OBJECT_MOBILEtg ) return "TargetBot"; + if ( type == OBJECT_MOBILEdr ) return "Scribbler"; + if ( type == OBJECT_MARKPOWER ) return "PowerSpot"; + if ( type == OBJECT_MARKSTONE ) return "TitaniumSpot"; +#if _GERMAN | _WG + if ( type == OBJECT_MARKURANIUM ) return "PlatinumSpot"; +#else + if ( type == OBJECT_MARKURANIUM ) return "UraniumSpot"; +#endif + if ( type == OBJECT_MARKKEYa ) return "KeyASpot"; + if ( type == OBJECT_MARKKEYb ) return "KeyBSpot"; + if ( type == OBJECT_MARKKEYc ) return "KeyCSpot"; + if ( type == OBJECT_MARKKEYd ) return "KeyDSpot"; + if ( type == OBJECT_WAYPOINT ) return "WayPoint"; + if ( type == OBJECT_FLAGb ) return "BlueFlag"; + if ( type == OBJECT_FLAGr ) return "RedFlag"; + if ( type == OBJECT_FLAGg ) return "GreenFlag"; + if ( type == OBJECT_FLAGy ) return "YellowFlag"; + if ( type == OBJECT_FLAGv ) return "VioletFlag"; + if ( type == OBJECT_POWER ) return "PowerCell"; +#if _GERMAN | _WG + if ( type == OBJECT_ATOMIC ) return "FuelCell"; +#else + if ( type == OBJECT_ATOMIC ) return "NuclearCell"; +#endif + if ( type == OBJECT_STONE ) return "TitaniumOre"; +#if _GERMAN | _WG + if ( type == OBJECT_URANIUM ) return "PlatinumOre"; +#else + if ( type == OBJECT_URANIUM ) return "UraniumOre"; +#endif + if ( type == OBJECT_METAL ) return "Titanium"; + if ( type == OBJECT_BULLET ) return "OrgaMatter"; + if ( type == OBJECT_BBOX ) return "BlackBox"; + if ( type == OBJECT_KEYa ) return "KeyA"; + if ( type == OBJECT_KEYb ) return "KeyB"; + if ( type == OBJECT_KEYc ) return "KeyC"; + if ( type == OBJECT_KEYd ) return "KeyD"; + if ( type == OBJECT_TNT ) return "TNT"; + if ( type == OBJECT_SCRAP1 ) return "Scrap1"; + if ( type == OBJECT_SCRAP2 ) return "Scrap2"; + if ( type == OBJECT_SCRAP3 ) return "Scrap3"; + if ( type == OBJECT_SCRAP4 ) return "Scrap4"; + if ( type == OBJECT_SCRAP5 ) return "Scrap5"; + if ( type == OBJECT_BOMB ) return "Mine"; + if ( type == OBJECT_WINFIRE ) return "Firework"; + if ( type == OBJECT_BAG ) return "Bag"; + if ( type == OBJECT_PLANT0 ) return "Greenery0"; + if ( type == OBJECT_PLANT1 ) return "Greenery1"; + if ( type == OBJECT_PLANT2 ) return "Greenery2"; + if ( type == OBJECT_PLANT3 ) return "Greenery3"; + if ( type == OBJECT_PLANT4 ) return "Greenery4"; + if ( type == OBJECT_PLANT5 ) return "Greenery5"; + if ( type == OBJECT_PLANT6 ) return "Greenery6"; + if ( type == OBJECT_PLANT7 ) return "Greenery7"; + if ( type == OBJECT_PLANT8 ) return "Greenery8"; + if ( type == OBJECT_PLANT9 ) return "Greenery9"; + if ( type == OBJECT_PLANT10 ) return "Greenery10"; + if ( type == OBJECT_PLANT11 ) return "Greenery11"; + if ( type == OBJECT_PLANT12 ) return "Greenery12"; + if ( type == OBJECT_PLANT13 ) return "Greenery13"; + if ( type == OBJECT_PLANT14 ) return "Greenery14"; + if ( type == OBJECT_PLANT15 ) return "Greenery15"; + if ( type == OBJECT_PLANT16 ) return "Greenery16"; + if ( type == OBJECT_PLANT17 ) return "Greenery17"; + if ( type == OBJECT_PLANT18 ) return "Greenery18"; + if ( type == OBJECT_PLANT19 ) return "Greenery19"; + if ( type == OBJECT_TREE0 ) return "Tree0"; + if ( type == OBJECT_TREE1 ) return "Tree1"; + if ( type == OBJECT_TREE2 ) return "Tree2"; + if ( type == OBJECT_TREE3 ) return "Tree3"; + if ( type == OBJECT_TREE4 ) return "Tree4"; + if ( type == OBJECT_TREE5 ) return "Tree5"; + if ( type == OBJECT_TREE6 ) return "Tree6"; + if ( type == OBJECT_TREE7 ) return "Tree7"; + if ( type == OBJECT_TREE8 ) return "Tree8"; + if ( type == OBJECT_TREE9 ) return "Tree9"; + if ( type == OBJECT_MUSHROOM0 ) return "Mushroom0"; + if ( type == OBJECT_MUSHROOM1 ) return "Mushroom1"; + if ( type == OBJECT_MUSHROOM2 ) return "Mushroom2"; + if ( type == OBJECT_MUSHROOM3 ) return "Mushroom3"; + if ( type == OBJECT_MUSHROOM4 ) return "Mushroom4"; + if ( type == OBJECT_MUSHROOM5 ) return "Mushroom5"; + if ( type == OBJECT_MUSHROOM6 ) return "Mushroom6"; + if ( type == OBJECT_MUSHROOM7 ) return "Mushroom7"; + if ( type == OBJECT_MUSHROOM8 ) return "Mushroom8"; + if ( type == OBJECT_MUSHROOM9 ) return "Mushroom9"; + if ( type == OBJECT_HOME1 ) return "Home"; + if ( type == OBJECT_DERRICK ) return "Derrick"; + if ( type == OBJECT_FACTORY ) return "BotFactory"; + if ( type == OBJECT_STATION ) return "PowerStation"; + if ( type == OBJECT_CONVERT ) return "Converter"; + if ( type == OBJECT_REPAIR ) return "RepairCenter"; + if ( type == OBJECT_DESTROYER ) return "Destroyer"; + if ( type == OBJECT_TOWER ) return "DefenseTower"; + if ( type == OBJECT_NEST ) return "AlienNest"; + if ( type == OBJECT_RESEARCH ) return "ResearchCenter"; + if ( type == OBJECT_RADAR ) return "RadarStation"; + if ( type == OBJECT_INFO ) return "ExchangePost"; + if ( type == OBJECT_ENERGY ) return "PowerPlant"; + if ( type == OBJECT_LABO ) return "AutoLab"; +#if _GERMAN | _WG + if ( type == OBJECT_NUCLEAR ) return "FuelCellPlant"; +#else + if ( type == OBJECT_NUCLEAR ) return "NuclearPlant"; +#endif + if ( type == OBJECT_PARA ) return "PowerCaptor"; + if ( type == OBJECT_SAFE ) return "Vault"; + if ( type == OBJECT_HUSTON ) return "Houston"; + if ( type == OBJECT_TARGET1 ) return "Target1"; + if ( type == OBJECT_TARGET2 ) return "Target2"; + if ( type == OBJECT_START ) return "StartArea"; + if ( type == OBJECT_END ) return "GoalArea"; + if ( type == OBJECT_MOTHER ) return "AlienQueen"; + if ( type == OBJECT_EGG ) return "AlienEgg"; + if ( type == OBJECT_ANT ) return "AlienAnt"; + if ( type == OBJECT_SPIDER ) return "AlienSpider"; + if ( type == OBJECT_BEE ) return "AlienWasp"; + if ( type == OBJECT_WORM ) return "AlienWorm"; + if ( type == OBJECT_RUINmobilew1 ) return "WreckBotw1"; + if ( type == OBJECT_RUINmobilew2 ) return "WreckBotw2"; + if ( type == OBJECT_RUINmobilet1 ) return "WreckBott1"; + if ( type == OBJECT_RUINmobilet2 ) return "WreckBott2"; + if ( type == OBJECT_RUINmobiler1 ) return "WreckBotr1"; + if ( type == OBJECT_RUINmobiler2 ) return "WreckBotr2"; + if ( type == OBJECT_RUINfactory ) return "RuinBotFactory"; + if ( type == OBJECT_RUINdoor ) return "RuinDoor"; + if ( type == OBJECT_RUINsupport ) return "RuinSupport"; + if ( type == OBJECT_RUINradar ) return "RuinRadar"; + if ( type == OBJECT_RUINconvert ) return "RuinConvert"; + if ( type == OBJECT_RUINbase ) return "RuinBaseCamp"; + if ( type == OBJECT_RUINhead ) return "RuinHeadCamp"; + if ( type == OBJECT_BARRIER0 ) return "Barrier0"; + if ( type == OBJECT_BARRIER1 ) return "Barrier1"; + if ( type == OBJECT_BARRIER2 ) return "Barrier2"; + if ( type == OBJECT_BARRIER3 ) return "Barrier3"; + if ( type == OBJECT_BARRIER4 ) return "Barrier4"; + if ( type == OBJECT_TEEN0 ) return "Teen0"; + if ( type == OBJECT_TEEN1 ) return "Teen1"; + if ( type == OBJECT_TEEN2 ) return "Teen2"; + if ( type == OBJECT_TEEN3 ) return "Teen3"; + if ( type == OBJECT_TEEN4 ) return "Teen4"; + if ( type == OBJECT_TEEN5 ) return "Teen5"; + if ( type == OBJECT_TEEN6 ) return "Teen6"; + if ( type == OBJECT_TEEN7 ) return "Teen7"; + if ( type == OBJECT_TEEN8 ) return "Teen8"; + if ( type == OBJECT_TEEN9 ) return "Teen9"; + if ( type == OBJECT_TEEN10 ) return "Teen10"; + if ( type == OBJECT_TEEN11 ) return "Teen11"; + if ( type == OBJECT_TEEN12 ) return "Teen12"; + if ( type == OBJECT_TEEN13 ) return "Teen13"; + if ( type == OBJECT_TEEN14 ) return "Teen14"; + if ( type == OBJECT_TEEN15 ) return "Teen15"; + if ( type == OBJECT_TEEN16 ) return "Teen16"; + if ( type == OBJECT_TEEN17 ) return "Teen17"; + if ( type == OBJECT_TEEN18 ) return "Teen18"; + if ( type == OBJECT_TEEN19 ) return "Teen19"; + if ( type == OBJECT_TEEN20 ) return "Teen20"; + if ( type == OBJECT_TEEN21 ) return "Teen21"; + if ( type == OBJECT_TEEN22 ) return "Teen22"; + if ( type == OBJECT_TEEN23 ) return "Teen23"; + if ( type == OBJECT_TEEN24 ) return "Teen24"; + if ( type == OBJECT_TEEN25 ) return "Teen25"; + if ( type == OBJECT_TEEN26 ) return "Teen26"; + if ( type == OBJECT_TEEN27 ) return "Teen27"; + if ( type == OBJECT_TEEN28 ) return "Teen28"; + if ( type == OBJECT_TEEN29 ) return "Teen29"; + if ( type == OBJECT_TEEN30 ) return "Teen30"; + if ( type == OBJECT_TEEN31 ) return "Teen31"; + if ( type == OBJECT_TEEN32 ) return "Teen32"; + if ( type == OBJECT_TEEN33 ) return "Teen33"; + if ( type == OBJECT_TEEN34 ) return "Stone"; + if ( type == OBJECT_TEEN35 ) return "Teen35"; + if ( type == OBJECT_TEEN36 ) return "Teen36"; + if ( type == OBJECT_TEEN37 ) return "Teen37"; + if ( type == OBJECT_TEEN38 ) return "Teen38"; + if ( type == OBJECT_TEEN39 ) return "Teen39"; + if ( type == OBJECT_TEEN40 ) return "Teen40"; + if ( type == OBJECT_TEEN41 ) return "Teen41"; + if ( type == OBJECT_TEEN42 ) return "Teen42"; + if ( type == OBJECT_TEEN43 ) return "Teen43"; + if ( type == OBJECT_TEEN44 ) return "Teen44"; + if ( type == OBJECT_TEEN45 ) return "Teen45"; + if ( type == OBJECT_TEEN46 ) return "Teen46"; + if ( type == OBJECT_TEEN47 ) return "Teen47"; + if ( type == OBJECT_TEEN48 ) return "Teen48"; + if ( type == OBJECT_TEEN49 ) return "Teen49"; + if ( type == OBJECT_QUARTZ0 ) return "Quartz0"; + if ( type == OBJECT_QUARTZ1 ) return "Quartz1"; + if ( type == OBJECT_QUARTZ2 ) return "Quartz2"; + if ( type == OBJECT_QUARTZ3 ) return "Quartz3"; + if ( type == OBJECT_QUARTZ4 ) return "Quartz4"; + if ( type == OBJECT_QUARTZ5 ) return "Quartz5"; + if ( type == OBJECT_QUARTZ6 ) return "Quartz6"; + if ( type == OBJECT_QUARTZ7 ) return "Quartz7"; + if ( type == OBJECT_QUARTZ8 ) return "Quartz8"; + if ( type == OBJECT_QUARTZ9 ) return "Quartz9"; + if ( type == OBJECT_ROOT0 ) return "MegaStalk0"; + if ( type == OBJECT_ROOT1 ) return "MegaStalk1"; + if ( type == OBJECT_ROOT2 ) return "MegaStalk2"; + if ( type == OBJECT_ROOT3 ) return "MegaStalk3"; + if ( type == OBJECT_ROOT4 ) return "MegaStalk4"; + if ( type == OBJECT_ROOT5 ) return "MegaStalk5"; + if ( type == OBJECT_ROOT6 ) return "MegaStalk6"; + if ( type == OBJECT_ROOT7 ) return "MegaStalk7"; + if ( type == OBJECT_ROOT8 ) return "MegaStalk8"; + if ( type == OBJECT_ROOT9 ) return "MegaStalk9"; + if ( type == OBJECT_APOLLO1 ) return "ApolloLEM"; + if ( type == OBJECT_APOLLO2 ) return "ApolloJeep"; + if ( type == OBJECT_APOLLO3 ) return "ApolloFlag"; + if ( type == OBJECT_APOLLO4 ) return "ApolloModule"; + if ( type == OBJECT_APOLLO5 ) return "ApolloAntenna"; + if ( type == OBJECT_HUMAN ) return "Me"; + if ( type == OBJECT_TECH ) return "Tech"; + return ""; +} + +// Retourne le type de l'eau. + +WaterType GetTypeWater(char *line, int rank, WaterType def) +{ + char* p; + + p = SearchArg(line, rank); + if ( *p == 0 ) return def; + + if ( Cmd(p, "NULL" ) ) return WATER_NULL; + if ( Cmd(p, "TT" ) ) return WATER_TT; + if ( Cmd(p, "TO" ) ) return WATER_TO; + if ( Cmd(p, "CT" ) ) return WATER_CT; + if ( Cmd(p, "CO" ) ) return WATER_CO; + + return def; +} + +// Retourne le type de terrain. + +D3DTypeObj GetTypeTerrain(char *line, int rank, D3DTypeObj def) +{ + char* p; + + p = SearchArg(line, rank); + if ( *p == 0 ) return def; + + if ( Cmd(p, "Terrain" ) ) return TYPETERRAIN; + if ( Cmd(p, "Object" ) ) return TYPEFIX; + if ( Cmd(p, "Quartz" ) ) return TYPEQUARTZ; + if ( Cmd(p, "Metal" ) ) return TYPEMETAL; + + return def; +} + +// Retourne le numéro d'un bâtiment. + +int GetBuild(char *line, int rank) +{ + char* p; + + p = SearchArg(line, rank); + if ( *p == 0 ) return 0; + + if ( Cmd(p, "BotFactory" ) ) return BUILD_FACTORY; + if ( Cmd(p, "Derrick" ) ) return BUILD_DERRICK; + if ( Cmd(p, "Converter" ) ) return BUILD_CONVERT; + if ( Cmd(p, "RadarStation" ) ) return BUILD_RADAR; + if ( Cmd(p, "PowerPlant" ) ) return BUILD_ENERGY; + if ( Cmd(p, "NuclearPlant" ) ) return BUILD_NUCLEAR; + if ( Cmd(p, "FuelCellPlant" ) ) return BUILD_NUCLEAR; + if ( Cmd(p, "PowerStation" ) ) return BUILD_STATION; + if ( Cmd(p, "RepairCenter" ) ) return BUILD_REPAIR; + if ( Cmd(p, "DefenseTower" ) ) return BUILD_TOWER; + if ( Cmd(p, "ResearchCenter") ) return BUILD_RESEARCH; + if ( Cmd(p, "AutoLab" ) ) return BUILD_LABO; + if ( Cmd(p, "PowerCaptor" ) ) return BUILD_PARA; + if ( Cmd(p, "ExchangePost" ) ) return BUILD_INFO; + if ( Cmd(p, "FlatGround" ) ) return BUILD_GFLAT; + if ( Cmd(p, "Flag" ) ) return BUILD_FLAG; + + return 0; +} + +// Retourne le numéro d'une recherche. + +int GetResearch(char *line, int rank) +{ + char* p; + + p = SearchArg(line, rank); + if ( *p == 0 ) return 0; + + if ( Cmd(p, "TRACKER" ) ) return RESEARCH_TANK; + if ( Cmd(p, "WINGER" ) ) return RESEARCH_FLY; + if ( Cmd(p, "THUMPER" ) ) return RESEARCH_THUMP; + if ( Cmd(p, "SHOOTER" ) ) return RESEARCH_CANON; + if ( Cmd(p, "TOWER" ) ) return RESEARCH_TOWER; + if ( Cmd(p, "PHAZER" ) ) return RESEARCH_PHAZER; + if ( Cmd(p, "SHIELDER") ) return RESEARCH_SHIELD; + if ( Cmd(p, "ATOMIC" ) ) return RESEARCH_ATOMIC; + if ( Cmd(p, "iPAW" ) ) return RESEARCH_iPAW; + if ( Cmd(p, "iGUN" ) ) return RESEARCH_iGUN; + if ( Cmd(p, "RECYCLER") ) return RESEARCH_RECYCLER; + if ( Cmd(p, "SUBBER" ) ) return RESEARCH_SUBM; + if ( Cmd(p, "SNIFFER" ) ) return RESEARCH_SNIFFER; + + return 0; +} + +// Retourne le type d'un effet pyrotechnique. + +PyroType GetPyro(char *line, int rank) +{ + char* p; + + p = SearchArg(line, rank); + if ( *p == 0 ) return PT_NULL; + + if ( Cmd(p, "FRAGt" ) ) return PT_FRAGT; + if ( Cmd(p, "FRAGo" ) ) return PT_FRAGO; + if ( Cmd(p, "FRAGw" ) ) return PT_FRAGW; + if ( Cmd(p, "EXPLOt" ) ) return PT_EXPLOT; + if ( Cmd(p, "EXPLOo" ) ) return PT_EXPLOO; + if ( Cmd(p, "EXPLOw" ) ) return PT_EXPLOW; + if ( Cmd(p, "SHOTt" ) ) return PT_SHOTT; + if ( Cmd(p, "SHOTh" ) ) return PT_SHOTH; + if ( Cmd(p, "SHOTm" ) ) return PT_SHOTM; + if ( Cmd(p, "SHOTw" ) ) return PT_SHOTW; + if ( Cmd(p, "EGG" ) ) return PT_EGG; + if ( Cmd(p, "BURNt" ) ) return PT_BURNT; + if ( Cmd(p, "BURNo" ) ) return PT_BURNO; + if ( Cmd(p, "SPIDER" ) ) return PT_SPIDER; + if ( Cmd(p, "FALL" ) ) return PT_FALL; + if ( Cmd(p, "RESET" ) ) return PT_RESET; + if ( Cmd(p, "WIN" ) ) return PT_WIN; + if ( Cmd(p, "LOST" ) ) return PT_LOST; + + return PT_NULL; +} + +// Retourne le type d'une caméra. + +CameraType GetCamera(char *line, int rank) +{ + char* p; + + p = SearchArg(line, rank); + if ( *p == 0 ) return CAMERA_NULL; + + if ( Cmd(p, "BACK" ) ) return CAMERA_BACK; + if ( Cmd(p, "PLANE" ) ) return CAMERA_PLANE; + if ( Cmd(p, "ONBOARD" ) ) return CAMERA_ONBOARD; + if ( Cmd(p, "FIX" ) ) return CAMERA_FIX; + + return CAMERA_NULL; +} + +// Retourne le nom d'une camera. + +char* GetCamera(CameraType type) +{ + if ( type == CAMERA_ONBOARD ) return "ONBOARD"; + if ( type == CAMERA_FIX ) return "FIX"; + return "BACK"; +} + +// Retourne un nombre entier. + +int OpInt(char *line, char *op, int def) +{ + line = SearchOp(line, op); + if ( *line == 0 ) return def; + return GetInt(line, 0, def); +} + +// Retourne un nombre réel. + +float OpFloat(char *line, char *op, float def) +{ + line = SearchOp(line, op); + if ( *line == 0 ) return def; + return GetFloat(line, 0, def); +} + +// Retourne une chaîne de caractères. + +void OpString(char *line, char *op, char *buffer) +{ + line = SearchOp(line, op); + if ( *line == 0 ) + { + buffer[0] = 0; + } + else + { + GetString(line, 0, buffer); + } +} + +// Retourne le type d'un objet. + +ObjectType OpTypeObject(char *line, char *op, ObjectType def) +{ + line = SearchOp(line, op); + if ( *line == 0 ) return def; + return GetTypeObject(line, 0, def); +} + +// Retourne le type d'un objet. + +WaterType OpTypeWater(char *line, char *op, WaterType def) +{ + line = SearchOp(line, op); + if ( *line == 0 ) return def; + return GetTypeWater(line, 0, def); +} + +// Retourne le type d'un objet. + +D3DTypeObj OpTypeTerrain(char *line, char *op, D3DTypeObj def) +{ + line = SearchOp(line, op); + if ( *line == 0 ) return def; + return GetTypeTerrain(line, 0, def); +} + +// Retourne le numéro d'une recherche. + +int OpResearch(char *line, char *op) +{ + line = SearchOp(line, op); + if ( *line == 0 ) return 0; + return GetResearch(line, 0); +} + +// Retourne le type d'un effet pyrotechnique. + +PyroType OpPyro(char *line, char *op) +{ + line = SearchOp(line, op); + if ( *line == 0 ) return PT_NULL; + return GetPyro(line, 0); +} + +// Retourne le type d'une caméra. + +CameraType OpCamera(char *line, char *op) +{ + line = SearchOp(line, op); + if ( *line == 0 ) return CAMERA_NULL; + return GetCamera(line, 0); +} + +// Retourne le numéro d'un bâtiment. + +int OpBuild(char *line, char *op) +{ + line = SearchOp(line, op); + if ( *line == 0 ) return 0; + return GetBuild(line, 0); +} + +// Retourne une position dans le plan XZ (vue d'en haut). + +D3DVECTOR OpPos(char *line, char *op) +{ + D3DVECTOR pos; + + line = SearchOp(line, op); + if ( *line == 0 ) + { + pos = D3DVECTOR(0.0f, 0.0f, 0.0f); + return pos; + } + pos.x = GetFloat(line, 0, 0.0f); + pos.y = 0.0f; + pos.z = GetFloat(line, 1, 0.0f); + return pos; +} + +// Retourne une direction. + +D3DVECTOR OpDir(char *line, char *op) +{ + D3DVECTOR dir; + + line = SearchOp(line, op); + if ( *line == 0 ) + { + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + return dir; + } + dir.x = GetFloat(line, 0, 0.0f); + dir.y = GetFloat(line, 1, 0.0f); + dir.z = GetFloat(line, 2, 0.0f); + return dir; +} + +// Lit une couleur (0..255). + +D3DCOLOR OpColor(char *line, char *op, D3DCOLOR def) +{ + D3DCOLOR color; + + line = SearchOp(line, op); + if ( *line == 0 ) return def; + + color = 0; + color |= (GetInt(line, 0, 0)&0xff)<<16; // r + color |= (GetInt(line, 1, 0)&0xff)<<8; // g + color |= (GetInt(line, 2, 0)&0xff)<<0; // b + color |= (GetInt(line, 3, 0)&0xff)<<24; // a + return color; +} + +// Lit une couleur (-1..1). + +D3DCOLORVALUE OpColorValue(char *line, char *op, D3DCOLORVALUE def) +{ + D3DCOLORVALUE color; + + line = SearchOp(line, op); + if ( *line == 0 ) return def; + + color.r = GetFloat(line, 0, 0.0f); + color.g = GetFloat(line, 1, 0.0f); + color.b = GetFloat(line, 2, 0.0f); + color.a = GetFloat(line, 3, 0.0f); + return color; +} + + diff --git a/src/cmdtoken.h b/src/cmdtoken.h new file mode 100644 index 00000000..67456899 --- /dev/null +++ b/src/cmdtoken.h @@ -0,0 +1,50 @@ +// cmdtoken.h + +#ifndef _CMDTOKEN_H_ +#define _CMDTOKEN_H_ + + + +enum ObjectType; +enum WaterType; +enum PyroType; +enum CameraType; + + + +// Procédures. + +extern BOOL Cmd(char *line, char *token); +extern char* SearchOp(char *line, char *op); + +extern int GetInt(char *line, int rank, int def); +extern float GetFloat(char *line, int rank, float def); +extern void GetString(char *line, int rank, char *buffer); +extern ObjectType GetTypeObject(char *line, int rank, ObjectType def); +extern char* GetTypeObject(ObjectType type); +extern WaterType GetTypeWater(char *line, int rank, WaterType def); +extern D3DTypeObj GetTypeTerrain(char *line, int rank, D3DTypeObj def); +extern int GetBuild(char *line, int rank); +extern int GetResearch(char *line, int rank); +extern PyroType GetPyro(char *line, int rank); +extern CameraType GetCamera(char *line, int rank); +extern char* GetCamera(CameraType type); + +extern int OpInt(char *line, char *op, int def); +extern float OpFloat(char *line, char *op, float def); +extern void OpString(char *line, char *op, char *buffer); +extern ObjectType OpTypeObject(char *line, char *op, ObjectType def); +extern WaterType OpTypeWater(char *line, char *op, WaterType def); +extern D3DTypeObj OpTypeTerrain(char *line, char *op, D3DTypeObj def); +extern int OpResearch(char *line, char *op); +extern PyroType OpPyro(char *line, char *op); +extern CameraType OpCamera(char *line, char *op); +extern int OpBuild(char *line, char *op); +extern D3DVECTOR OpPos(char *line, char *op); +extern D3DVECTOR OpDir(char *line, char *op); +extern D3DCOLOR OpColor(char *line, char *op, D3DCOLOR def); +extern D3DCOLORVALUE OpColorValue(char *line, char *op, D3DCOLORVALUE def); + + + +#endif //_CMDTOKEN_H_ diff --git a/src/colobot.ini b/src/colobot.ini new file mode 100644 index 00000000..de547ec4 --- /dev/null +++ b/src/colobot.ini @@ -0,0 +1,75 @@ +[Gamer] +LastName=Joueur + +[Setup] +TotoMode=1 +Tooltips=1 +InterfaceGlint=1 +NiceMouse=0 +Movies=1 +NiceReset=1 +HimselfDamage=1 +CameraScroll=1 +CameraInvertX=0 +InterfaceEffect=1 +GroundShadow=1 +GroundSpot=1 +ObjectDirty=1 +FogMode=1 +LensMode=1 +SkyMode=1 +PlanetMode=1 +LightMode=1 +UseJoystick=0 +ParticuleDensity=1.00 +ClippingDistance=1.00 +ObjectDetail=2.00 +GadgetQuantity=1.00 +TextureQuality=1 +AudioVolume=15 +MidiVolume=20 +Sound3D=0 +EditIndentMode=1 +EditIndentValue=4 +KeyMap=37+0 39+0 38+0 40+0 16+0 17+0 32+258 96+262 13+257 107+261 109+260 9+259 36+263 27+0 112+0 113+0 110+0 115+0 116+0 117+0 +FullScreenActivateEnable=0 +AccessMission=1 +AccessUser=1 +DeleteGamer=1 +Soluce4=1 + +[Engine] +StateColor=-1 +BlackSrcBlend=0 +BlackDestBlend=0 +WhiteSrcBlend=0 +WhiteDestBlend=0 +DiffuseSrcBlend=0 +DiffuseDestBlend=0 +AlphaSrcBlend=0 +AlphaDestBlend=0 +AlphaMode=1 + +[Device] +Name=Direct3D HAL +Mode=1600 x 1200 x 32 +FullScreen=0 + +[Edit] +WindowPos.x=0.19 +WindowPos.y=0.15 +WindowDim.x=0.66 +WindowDim.y=0.69 +FontSize=9.00 +IOPos.x=0.29 +IOPos.y=0.34 +IODim.x=0.50 +IODim.y=0.55 +IOPublic=0 + +[Directory] +scene=scene +savegame=savegame +public=program +user=user +files=files diff --git a/src/color.cpp b/src/color.cpp new file mode 100644 index 00000000..25036eba --- /dev/null +++ b/src/color.cpp @@ -0,0 +1,215 @@ +// color.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "language.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "color.h" + + + +#define DELAY1 0.4f +#define DELAY2 0.1f + + + +// Constructeur de l'objet. + +CColor::CColor(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); + + m_bRepeat = FALSE; + m_repeat = 0.0f; + + m_color.r = 0.0f; + m_color.g = 0.0f; + m_color.b = 0.0f; + m_color.a = 0.0f; +} + +// Destructeur de l'objet. + +CColor::~CColor() +{ + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CColor::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + + if ( icon == -1 ) + { + char name[100]; + char* p; + + GetResource(RES_EVENT, eventMsg, name); + p = strchr(name, '\\'); + if ( p != 0 ) *p = 0; + SetName(name); + } + + return TRUE; +} + + +// Gestion d'un événement. + +BOOL CColor::EventProcess(const Event &event) +{ + if ( m_state & STATE_DEAD ) return TRUE; + + CControl::EventProcess(event); + + if ( event.event == EVENT_FRAME && m_bRepeat ) + { + if ( m_repeat != 0.0f ) + { + m_repeat -= event.rTime; + if ( m_repeat <= 0.0f ) + { + m_repeat = DELAY2; + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + return FALSE; + } + } + } + + if ( event.event == EVENT_LBUTTONDOWN && + (m_state & STATE_VISIBLE) && + (m_state & STATE_ENABLE) ) + { + if ( CControl::Detect(event.pos) ) + { + m_repeat = DELAY1; + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + return FALSE; + } + } + + if ( event.event == EVENT_LBUTTONUP ) + { + m_repeat = 0.0f; + } + + return TRUE; +} + + +// Dessine le bouton. + +void CColor::Draw() +{ + LPDIRECT3DDEVICE7 device; + D3DLVERTEX vertex[4]; // 2 triangles + D3DCOLOR color; + FPOINT p1, p2; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + if ( m_state & STATE_SHADOW ) + { + DrawShadow(m_pos, m_dim); + } + + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + CControl::Draw(); + +#if _TEEN + color = ::RetColor(m_color); + + m_engine->SetTexture("xxx.tga"); // pas de texture + m_engine->SetState(D3DSTATENORMAL); + + device = m_engine->RetD3DDevice(); + + p1.x = m_pos.x+(4.0f/640.0f); + p1.y = m_pos.y+(4.0f/480.0f); + p2.x = m_pos.x+m_dim.x-(4.0f/640.0f); + p2.y = m_pos.y+m_dim.y-(4.0f/480.0f); + vertex[0] = D3DLVERTEX(D3DVECTOR(p1.x, p1.y, 0.0f), 0x00000000,0x00000000, 0.0f,0.0f); + vertex[1] = D3DLVERTEX(D3DVECTOR(p1.x, p2.y, 0.0f), 0x00000000,0x00000000, 0.0f,0.0f); + vertex[2] = D3DLVERTEX(D3DVECTOR(p2.x, p1.y, 0.0f), 0x00000000,0x00000000, 0.0f,0.0f); + vertex[3] = D3DLVERTEX(D3DVECTOR(p2.x, p2.y, 0.0f), 0x00000000,0x00000000, 0.0f,0.0f); + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_LVERTEX, vertex, 4, NULL); + + p1.x = m_pos.x+(5.0f/640.0f); + p1.y = m_pos.y+(5.0f/480.0f); + p2.x = m_pos.x+m_dim.x-(5.0f/640.0f); + p2.y = m_pos.y+m_dim.y-(5.0f/480.0f); + vertex[0] = D3DLVERTEX(D3DVECTOR(p1.x, p1.y, 0.0f), color,0x00000000, 0.0f,0.0f); + vertex[1] = D3DLVERTEX(D3DVECTOR(p1.x, p2.y, 0.0f), color,0x00000000, 0.0f,0.0f); + vertex[2] = D3DLVERTEX(D3DVECTOR(p2.x, p1.y, 0.0f), color,0x00000000, 0.0f,0.0f); + vertex[3] = D3DLVERTEX(D3DVECTOR(p2.x, p2.y, 0.0f), color,0x00000000, 0.0f,0.0f); + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_LVERTEX, vertex, 4, NULL); + + m_engine->AddStatisticTriangle(4); +#else + p1.x = m_pos.x+(3.0f/640.0f); + p1.y = m_pos.y+(3.0f/480.0f); + p2.x = m_pos.x+m_dim.x-(3.0f/640.0f); + p2.y = m_pos.y+m_dim.y-(3.0f/480.0f); + + color = ::RetColor(m_color); + + m_engine->SetTexture("xxx.tga"); // pas de texture + m_engine->SetState(D3DSTATENORMAL); + + vertex[0] = D3DLVERTEX(D3DVECTOR(p1.x, p1.y, 0.0f), color,0x00000000, 0.0f,0.0f); + vertex[1] = D3DLVERTEX(D3DVECTOR(p1.x, p2.y, 0.0f), color,0x00000000, 0.0f,0.0f); + vertex[2] = D3DLVERTEX(D3DVECTOR(p2.x, p1.y, 0.0f), color,0x00000000, 0.0f,0.0f); + vertex[3] = D3DLVERTEX(D3DVECTOR(p2.x, p2.y, 0.0f), color,0x00000000, 0.0f,0.0f); + + device = m_engine->RetD3DDevice(); + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_LVERTEX, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); +#endif +} + + +void CColor::SetRepeat(BOOL bRepeat) +{ + m_bRepeat = bRepeat; +} + +BOOL CColor::RetRepeat() +{ + return m_bRepeat; +} + + +void CColor::SetColor(D3DCOLORVALUE color) +{ + m_color = color; +} + +D3DCOLORVALUE CColor::RetColor() +{ + return m_color; +} + + diff --git a/src/color.h b/src/color.h new file mode 100644 index 00000000..9189b41e --- /dev/null +++ b/src/color.h @@ -0,0 +1,41 @@ +// color.h + +#ifndef _COLOR_H_ +#define _COLOR_H_ + + +#include "control.h" + + +class CD3DEngine; + + + +class CColor : public CControl +{ +public: + CColor(CInstanceManager* iMan); + ~CColor(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + BOOL EventProcess(const Event &event); + + void Draw(); + + void SetRepeat(BOOL bRepeat); + BOOL RetRepeat(); + + void SetColor(D3DCOLORVALUE color); + D3DCOLORVALUE RetColor(); + +protected: + +protected: + BOOL m_bRepeat; + float m_repeat; + D3DCOLORVALUE m_color; +}; + + +#endif //_COLOR_H_ diff --git a/src/compass.cpp b/src/compass.cpp new file mode 100644 index 00000000..55c8f9ec --- /dev/null +++ b/src/compass.cpp @@ -0,0 +1,165 @@ +// compass.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "compass.h" + + + + +// Constructeur de l'objet. + +CCompass::CCompass(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); + + m_dir = 0.0f; +} + +// Destructeur de l'objet. + +CCompass::~CCompass() +{ + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CCompass::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + return TRUE; +} + + +// Gestion d'un événement. + +BOOL CCompass::EventProcess(const Event &event) +{ + CControl::EventProcess(event); + + if ( event.event == EVENT_LBUTTONDOWN ) + { + if ( CControl::Detect(event.pos) ) + { + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + return FALSE; + } + } + + return TRUE; +} + + +// Dessine le bouton. + +void CCompass::Draw() +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2 vertex[4]; // 2 triangles + FPOINT p1, p2, p3, c, uv1, uv2; + D3DVECTOR n; + float dp; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + device = m_engine->RetD3DDevice(); + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + + p1.x = m_pos.x; + p1.y = m_pos.y; + p2.x = m_pos.x + m_dim.x; + p2.y = m_pos.y + m_dim.y; + + c.x = (p1.x+p2.x)/2.0f; + c.y = (p1.y+p2.y)/2.0f; // centre + + uv1.x = 64.0f/256.0f; + uv1.y = 32.0f/256.0f; + uv2.x = 96.0f/256.0f; + uv2.y = 64.0f/256.0f; + + dp = 0.5f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, uv1.x,uv2.y); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, uv1.x,uv1.y); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, uv2.x,uv2.y); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, uv2.x,uv1.y); + + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + + if ( m_state & STATE_ENABLE ) + { + p1.x = c.x; + p1.y = c.y+m_dim.x*0.40f; + p1 = RotatePoint(c, m_dir, p1); + p1.x = c.x+(p1.x-c.x)*(m_dim.x/m_dim.y); + + p2.x = c.x+m_dim.x*0.20f; + p2.y = c.y-m_dim.x*0.40f; + p2 = RotatePoint(c, m_dir, p2); + p2.x = c.x+(p2.x-c.x)*(m_dim.x/m_dim.y); + + p3.x = c.x-m_dim.x*0.20f; + p3.y = c.y-m_dim.x*0.40f; + p3 = RotatePoint(c, m_dir, p3); + p3.x = c.x+(p3.x-c.x)*(m_dim.x/m_dim.y); + + uv1.x = 96.0f/256.0f; + uv1.y = 32.0f/256.0f; + uv2.x = 104.0f/256.0f; + uv2.y = 64.0f/256.0f; + + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, uv1.x,uv1.y); + vertex[1] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, uv1.x,uv2.y); + vertex[2] = D3DVERTEX2(D3DVECTOR(p3.x, p3.y, 0.0f), n, uv2.x,uv2.y); + + device->DrawPrimitive(D3DPT_TRIANGLELIST, D3DFVF_VERTEX2, vertex, 3, NULL); + m_engine->AddStatisticTriangle(1); + } +} + + +// Gestion des directions de la boussole. + +void CCompass::SetDirection(float dir) +{ + m_dir = dir; +} + +float CCompass::RetDirection() +{ + return m_dir; +} + + diff --git a/src/compass.h b/src/compass.h new file mode 100644 index 00000000..46ed00dd --- /dev/null +++ b/src/compass.h @@ -0,0 +1,36 @@ +// compass.h + +#ifndef _COMPASS_H_ +#define _COMPASS_H_ + + +#include "control.h" + + +class CD3DEngine; + + + +class CCompass : public CControl +{ +public: + CCompass(CInstanceManager* iMan); + ~CCompass(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + BOOL EventProcess(const Event &event); + + void Draw(); + + void SetDirection(float dir); + float RetDirection(); + +protected: + +protected: + float m_dir; +}; + + +#endif //_COMPASS_H_ diff --git a/src/control.cpp b/src/control.cpp new file mode 100644 index 00000000..5e786ccb --- /dev/null +++ b/src/control.cpp @@ -0,0 +1,862 @@ +// control.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "language.h" +#include "restext.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "robotmain.h" +#include "particule.h" +#include "misc.h" +#include "iman.h" +#include "text.h" +#include "sound.h" +#include "control.h" + + + + +// Constructeur de l'objet. + +CControl::CControl(CInstanceManager* iMan) +{ + m_iMan = iMan; + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_event = (CEvent*)m_iMan->SearchInstance(CLASS_EVENT); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + m_eventMsg = EVENT_NULL; + m_state = STATE_ENABLE|STATE_VISIBLE|STATE_GLINT; + m_fontSize = SMALLFONT; + m_fontStretch = NORMSTRETCH; + m_fontType = FONT_COLOBOT; + m_justif = 0; + m_name[0] = 0; + m_tooltip[0] = 0; + m_bFocus = FALSE; + m_bCapture = FALSE; + + m_bGlint = FALSE; + m_glintCorner1 = FPOINT(0.0f, 0.0f); + m_glintCorner2 = FPOINT(0.0f, 0.0f); + m_glintProgress = 999.0f; + m_glintMouse = FPOINT(0.0f, 0.0f); +} + +// Destructeur de l'objet. + +CControl::~CControl() +{ +} + + +// Crée un nouveau bouton. +// pos: [0..1] + +BOOL CControl::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + char text[100]; + char* p; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + m_pos = pos; + m_dim = dim; + m_icon = icon; + m_eventMsg = eventMsg; + + pos.x = m_pos.x; + pos.y = m_pos.y+m_dim.y; + GlintCreate(pos); + + GetResource(RES_EVENT, m_eventMsg, text); + p = strchr(text, '\\'); + if ( p == 0 ) + { + if ( icon != -1 ) + { + strcpy(m_tooltip, text); + } + } + else + { + strcpy(m_tooltip, p+1); // texte après "\\" + } + + return TRUE; +} + + +void CControl::SetPos(FPOINT pos) +{ + m_pos = pos; + + pos.x = m_pos.x; + pos.y = m_pos.y+m_dim.y; + GlintCreate(pos); +} + +FPOINT CControl::RetPos() +{ + return m_pos; +} + +void CControl::SetDim(FPOINT dim) +{ + FPOINT pos; + + m_dim = dim; + + pos.x = m_pos.x; + pos.y = m_pos.y+m_dim.y; + GlintCreate(pos); +} + +FPOINT CControl::RetDim() +{ + return m_dim; +} + + +// Modifie un attribut d'état. + +BOOL CControl::SetState(int state, BOOL bState) +{ + if ( bState ) m_state |= state; + else m_state &= ~state; + return TRUE; +} + +// Met un attribut d'état. + +BOOL CControl::SetState(int state) +{ + m_state |= state; + return TRUE; +} + +// Enlève un attribut d'état. + +BOOL CControl::ClearState(int state) +{ + m_state &= ~state; + return TRUE; +} + +// Teste un attribut d'état. + +BOOL CControl::TestState(int state) +{ + return (m_state & state) ? TRUE:FALSE; +} + +// Retourne tous les attributs d'état. + +int CControl::RetState() +{ + return m_state; +} + + +// Gestion de l'icône. + +void CControl::SetIcon(int icon) +{ + m_icon = icon; +} + +int CControl::RetIcon() +{ + return m_icon; +} + + +// Gestion du nom du bouton. + +void CControl::SetName(char* name, BOOL bTooltip) +{ + char* p; + + if ( bTooltip ) + { + p = strchr(name, '\\'); + if ( p == 0 ) + { + strncpy(m_name, name, 100); + m_name[100-1] = 0; + } + else + { + char buffer[100]; + + strncpy(m_tooltip, p+1, 100); // texte après "\\" + m_tooltip[100-1] = 0; + + strncpy(buffer, name, 100); + buffer[100-1] = 0; + p = strchr(buffer, '\\'); + if ( p != 0 ) *p = 0; + strncpy(m_name, buffer, 100); + m_name[100-1] = 0; + } + } + else + { + strncpy(m_name, name, 100); + m_name[100-1] = 0; + } +} + +char* CControl::RetName() +{ + return m_name; +} + + +// Gestion du mode de justification (-1,0,1). + +void CControl::SetJustif(int mode) +{ + m_justif = mode; +} + +int CControl::RetJustif() +{ + return m_justif; +} + + +// Gestion de la taille de la fonte. + +void CControl::SetFontSize(float size) +{ + m_fontSize = size; +} + +float CControl::RetFontSize() +{ + return m_fontSize; +} + + +// Gestion du stretch de la fonte. + +void CControl::SetFontStretch(float stretch) +{ + m_fontStretch = stretch; +} + +float CControl::RetFontStretch() +{ + return m_fontStretch; +} + + +// Choix de la fonte. + +void CControl::SetFontType(FontType font) +{ + m_fontType = font; +} + +FontType CControl::RetFontType() +{ + return m_fontType; +} + + +// Spécifie le tooltip. + +BOOL CControl::SetTooltip(char* name) +{ + strcpy(m_tooltip, name); + return TRUE; +} + +BOOL CControl::GetTooltip(FPOINT pos, char* name) +{ + if ( m_tooltip[0] == 0 ) return FALSE; + if ( (m_state & STATE_VISIBLE) == 0 ) return FALSE; + if ( (m_state & STATE_ENABLE) == 0 ) return FALSE; + if ( m_state & STATE_DEAD ) return FALSE; + if ( !Detect(pos) ) return FALSE; + + strcpy(name, m_tooltip); + return TRUE; +} + + +// Gestion de qui a la focus. + +void CControl::SetFocus(BOOL bFocus) +{ + m_bFocus = bFocus; +} + +BOOL CControl::RetFocus() +{ + return m_bFocus; +} + + +// Retourne l'événement associé au contrôle. + +EventMsg CControl::RetEventMsg() +{ + return m_eventMsg; +} + + +// Gestion d'un événement. + +BOOL CControl::EventProcess(const Event &event) +{ + if ( m_state & STATE_DEAD ) return TRUE; + + if ( event.event == EVENT_FRAME && m_bGlint ) + { + GlintFrame(event); + } + + if ( event.event == EVENT_MOUSEMOVE ) + { + m_glintMouse = event.pos; + + if ( Detect(event.pos) ) + { + if ( (m_state & STATE_VISIBLE) && + (m_state & STATE_ENABLE ) ) + { + m_engine->SetMouseType(D3DMOUSEHAND); + } + SetState(STATE_HILIGHT); + } + else + { + ClearState(STATE_HILIGHT); + } + } + + if ( event.event == EVENT_LBUTTONDOWN ) + { + if ( Detect(event.pos) ) + { + m_bCapture = TRUE; + SetState(STATE_PRESS); + } + } + + if ( event.event == EVENT_MOUSEMOVE && m_bCapture ) + { + if ( Detect(event.pos) ) + { + SetState(STATE_PRESS); + } + else + { + ClearState(STATE_PRESS); + } + } + + if ( event.event == EVENT_LBUTTONUP && m_bCapture ) + { + m_bCapture = FALSE; + ClearState(STATE_PRESS); + } + + return TRUE; +} + + +// Supprime le reflet. + +void CControl::GlintDelete() +{ + m_bGlint = FALSE; +} + +// Crée un reflet pour ce bouton. + +void CControl::GlintCreate(FPOINT ref, BOOL bLeft, BOOL bUp) +{ + float offset; + + offset = 8.0f/640.0f; + if ( offset > m_dim.x/4.0f) offset = m_dim.x/4.0f; + + if ( bLeft ) + { + m_glintCorner1.x = ref.x; + m_glintCorner2.x = ref.x+offset; + } + else + { + m_glintCorner1.x = ref.x-offset; + m_glintCorner2.x = ref.x; + } + + offset = 8.0f/480.0f; + if ( offset > m_dim.y/4.0f) offset = m_dim.y/4.0f; + + if ( bUp ) + { + m_glintCorner1.y = ref.y-offset; + m_glintCorner2.y = ref.y; + } + else + { + m_glintCorner1.y = ref.y; + m_glintCorner2.y = ref.y+offset; + } + + m_bGlint = TRUE; +} + +// Gestion du reflet. + +void CControl::GlintFrame(const Event &event) +{ + D3DVECTOR pos, speed; + FPOINT dim; + + if ( (m_state & STATE_GLINT ) == 0 || + (m_state & STATE_ENABLE ) == 0 || + (m_state & STATE_VISIBLE) == 0 ) return; + + if ( !m_main->RetGlint() ) return; + + m_glintProgress += event.rTime; + + if ( m_glintProgress >= 2.0f && Detect(m_glintMouse) ) + { + pos.x = m_glintCorner1.x + (m_glintCorner2.x-m_glintCorner1.x)*Rand(); + pos.y = m_glintCorner1.y + (m_glintCorner2.y-m_glintCorner1.y)*Rand(); + pos.z = 0.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = ((15.0f+Rand()*15.0f)/640.0f); + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, PARTICONTROL, + 1.0f, 0.0f, 0.0f, SH_INTERFACE); + + m_glintProgress = 0.0f; + } +} + + +// Dessine le bouton. + +void CControl::Draw() +{ + FPOINT pos; + float zoomExt, zoomInt; + int icon; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + + zoomExt = 1.00f; + zoomInt = 0.95f; + + if ( m_icon >= 128 ) + { + zoomInt = 0.80f; + } + + icon = 2; + if ( m_state & STATE_CARD ) + { + icon = 26; + } + if ( m_state & STATE_DEFAULT ) + { + DrawPart(23, 1.3f, 0.0f); + + zoomExt *= 1.15f; + zoomInt *= 1.15f; + } + if ( m_state & STATE_HILIGHT ) + { + icon = 1; + } + if ( m_state & STATE_CHECK ) + { + if ( m_state & STATE_CARD ) + { + icon = 27; + } + else + { + icon = 0; + } + } + if ( m_state & STATE_PRESS ) + { + icon = 3; + zoomInt *= 0.9f; + } + if ( (m_state & STATE_ENABLE) == 0 ) + { + icon = 7; + } + if ( m_state & STATE_DEAD ) + { + icon = 17; + } + + if ( m_state & STATE_OKAY ) + { + m_engine->SetTexture("button3.tga"); + icon = 3; // jaune pressé avec point vert + } + + if ( m_name[0] == 0 ) // bouton sans nom ? + { +//? DrawPart(icon, zoomExt, 0.0f); + DrawPart(icon, zoomExt, 8.0f/256.0f); + + if ( m_state & STATE_DEAD ) return; + + icon = m_icon; + if ( icon >= 192 ) + { + icon -= 192; +#if _POLISH + m_engine->SetTexture("textp.tga"); +#else + m_engine->SetTexture("text.tga"); +#endif + m_engine->SetState(D3DSTATETTw); + } + else if ( icon >= 128 ) + { + icon -= 128; + m_engine->SetTexture("button3.tga"); + m_engine->SetState(D3DSTATETTw); + } + else if ( icon >= 64 ) + { + icon -= 64; + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + } + else + { + m_engine->SetState(D3DSTATETTw); + } + if ( icon != -1 ) + { + DrawPart(icon, zoomInt, 0.0f); + } + } + else // bouton avec nom ? + { + DrawPart(icon, 1.0f, 8.0f/256.0f); + + if ( m_state & STATE_DEAD ) return; + + if ( m_justif < 0 ) + { + pos.x = m_pos.x+m_dim.x-m_dim.y*0.5f; + pos.y = m_pos.y+m_dim.y*0.5f; + pos.y -= m_engine->RetText()->RetHeight(m_fontSize, m_fontType)/2; + m_engine->RetText()->DrawText(m_name, pos, m_dim.x, m_justif, m_fontSize, m_fontStretch, m_fontType, 0); + } + else if ( m_justif > 0 ) + { + pos.x = m_pos.x+m_dim.y*0.5f; + pos.y = m_pos.y+m_dim.y*0.5f; + pos.y -= m_engine->RetText()->RetHeight(m_fontSize, m_fontType)/2.0f; + m_engine->RetText()->DrawText(m_name, pos, m_dim.x, m_justif, m_fontSize, m_fontStretch, m_fontType, 0); + } + else + { + pos.x = m_pos.x+m_dim.x*0.5f; + pos.y = m_pos.y+m_dim.y*0.5f; + pos.y -= m_engine->RetText()->RetHeight(m_fontSize, m_fontType)/2.0f; + m_engine->RetText()->DrawText(m_name, pos, m_dim.x, m_justif, m_fontSize, m_fontStretch, m_fontType, 0); + } + } +} + +// Dessine le tableau des vertex. + +void CControl::DrawPart(int icon, float zoom, float ex) +{ + FPOINT p1, p2, c, uv1, uv2; + float dp; + + p1.x = m_pos.x; + p1.y = m_pos.y; + p2.x = m_pos.x + m_dim.x; + p2.y = m_pos.y + m_dim.y; + + if ( (m_state & STATE_CARD ) && + (m_state & STATE_CHECK) ) + { + p2.y += (2.0f/480.0f); // un poil plus haut + } + + c.x = (p1.x+p2.x)/2.0f; + c.y = (p1.y+p2.y)/2.0f; // centre + + p1.x = (p1.x-c.x)*zoom + c.x; + p1.y = (p1.y-c.y)*zoom + c.y; + p2.x = (p2.x-c.x)*zoom + c.x; + p2.y = (p2.y-c.y)*zoom + c.y; + + p2.x -= p1.x; + p2.y -= p1.y; + + uv1.x = (32.0f/256.0f)*(icon%8); + uv1.y = (32.0f/256.0f)*(icon/8); // uv texture + uv2.x = (32.0f/256.0f)+uv1.x; + uv2.y = (32.0f/256.0f)+uv1.y; + + dp = 0.5f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + DrawIcon(p1, p2, uv1, uv2, ex); +} + +// Dessine une icône rectangulaire composée de 1 (si ex=0) +// ou 3 morceaux. + +void CControl::DrawIcon(FPOINT pos, FPOINT dim, FPOINT uv1, FPOINT uv2, + float ex) +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2 vertex[8]; // 6 triangles + FPOINT p1, p2, p3, p4; + D3DVECTOR n; + + device = m_engine->RetD3DDevice(); + + p1.x = pos.x; + p1.y = pos.y; + p2.x = pos.x + dim.x; + p2.y = pos.y + dim.y; + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + + if ( ex == 0.0f ) // un seul morceau ? + { + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, uv1.x,uv2.y); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, uv1.x,uv1.y); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, uv2.x,uv2.y); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, uv2.x,uv1.y); + + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + } + else // 3 morceaux ? + { + if ( dim.x >= dim.y ) + { + p3.x = p1.x + ex*dim.y/(uv2.y-uv1.y); + p4.x = p2.x - ex*dim.y/(uv2.y-uv1.y); + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, uv1.x, uv2.y); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, uv1.x, uv1.y); + vertex[2] = D3DVERTEX2(D3DVECTOR(p3.x, p1.y, 0.0f), n, uv1.x+ex,uv2.y); + vertex[3] = D3DVERTEX2(D3DVECTOR(p3.x, p2.y, 0.0f), n, uv1.x+ex,uv1.y); + vertex[4] = D3DVERTEX2(D3DVECTOR(p4.x, p1.y, 0.0f), n, uv2.x-ex,uv2.y); + vertex[5] = D3DVERTEX2(D3DVECTOR(p4.x, p2.y, 0.0f), n, uv2.x-ex,uv1.y); + vertex[6] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, uv2.x, uv2.y); + vertex[7] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, uv2.x, uv1.y); + + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 8, NULL); + m_engine->AddStatisticTriangle(6); + } + else + { + p3.y = p1.y + ex*dim.x/(uv2.x-uv1.x); + p4.y = p2.y - ex*dim.x/(uv2.x-uv1.x); + + vertex[0] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, uv2.x,uv2.y ); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, uv1.x,uv2.y ); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p3.y, 0.0f), n, uv2.x,uv2.y-ex); + vertex[3] = D3DVERTEX2(D3DVECTOR(p1.x, p3.y, 0.0f), n, uv1.x,uv2.y-ex); + vertex[4] = D3DVERTEX2(D3DVECTOR(p2.x, p4.y, 0.0f), n, uv2.x,uv1.y+ex); + vertex[5] = D3DVERTEX2(D3DVECTOR(p1.x, p4.y, 0.0f), n, uv1.x,uv1.y+ex); + vertex[6] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, uv2.x,uv1.y ); + vertex[7] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, uv1.x,uv1.y ); + + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 8, NULL); + m_engine->AddStatisticTriangle(6); + } + } +} + +// Dessine une icône rectangulaire composée de 9 morceaux. + +void CControl::DrawIcon(FPOINT pos, FPOINT dim, FPOINT uv1, FPOINT uv2, + FPOINT corner, float ex) +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2 vertex[8]; // 6 triangles + FPOINT p1, p2, p3, p4; + D3DVECTOR n; + + device = m_engine->RetD3DDevice(); + + p1.x = pos.x; + p1.y = pos.y; + p2.x = pos.x + dim.x; + p2.y = pos.y + dim.y; + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + + if ( corner.x > dim.x/2.0f ) corner.x = dim.x/2.0f; + if ( corner.y > dim.y/2.0f ) corner.y = dim.y/2.0f; + + p1.x = pos.x; + p1.y = pos.y; + p2.x = pos.x + dim.x; + p2.y = pos.y + dim.y; + p3.x = p1.x + corner.x; + p3.y = p1.y + corner.y; + p4.x = p2.x - corner.x; + p4.y = p2.y - corner.y; + + // Bande horizontale inférieure. + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, uv1.x, uv2.y ); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p3.y, 0.0f), n, uv1.x, uv2.y-ex); + vertex[2] = D3DVERTEX2(D3DVECTOR(p3.x, p1.y, 0.0f), n, uv1.x+ex,uv2.y ); + vertex[3] = D3DVERTEX2(D3DVECTOR(p3.x, p3.y, 0.0f), n, uv1.x+ex,uv2.y-ex); + vertex[4] = D3DVERTEX2(D3DVECTOR(p4.x, p1.y, 0.0f), n, uv2.x-ex,uv2.y ); + vertex[5] = D3DVERTEX2(D3DVECTOR(p4.x, p3.y, 0.0f), n, uv2.x-ex,uv2.y-ex); + vertex[6] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, uv2.x, uv2.y ); + vertex[7] = D3DVERTEX2(D3DVECTOR(p2.x, p3.y, 0.0f), n, uv2.x, uv2.y-ex); + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 8, NULL); + m_engine->AddStatisticTriangle(6); + + // Bande horizontale médiane. + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p3.y, 0.0f), n, uv1.x, uv2.y-ex); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p4.y, 0.0f), n, uv1.x, uv1.y+ex); + vertex[2] = D3DVERTEX2(D3DVECTOR(p3.x, p3.y, 0.0f), n, uv1.x+ex,uv2.y-ex); + vertex[3] = D3DVERTEX2(D3DVECTOR(p3.x, p4.y, 0.0f), n, uv1.x+ex,uv1.y+ex); + vertex[4] = D3DVERTEX2(D3DVECTOR(p4.x, p3.y, 0.0f), n, uv2.x-ex,uv2.y-ex); + vertex[5] = D3DVERTEX2(D3DVECTOR(p4.x, p4.y, 0.0f), n, uv2.x-ex,uv1.y+ex); + vertex[6] = D3DVERTEX2(D3DVECTOR(p2.x, p3.y, 0.0f), n, uv2.x, uv2.y-ex); + vertex[7] = D3DVERTEX2(D3DVECTOR(p2.x, p4.y, 0.0f), n, uv2.x, uv1.y+ex); + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 8, NULL); + m_engine->AddStatisticTriangle(6); + + // Bande horizontale supérieure. + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p4.y, 0.0f), n, uv1.x, uv1.y+ex); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, uv1.x, uv1.y ); + vertex[2] = D3DVERTEX2(D3DVECTOR(p3.x, p4.y, 0.0f), n, uv1.x+ex,uv1.y+ex); + vertex[3] = D3DVERTEX2(D3DVECTOR(p3.x, p2.y, 0.0f), n, uv1.x+ex,uv1.y ); + vertex[4] = D3DVERTEX2(D3DVECTOR(p4.x, p4.y, 0.0f), n, uv2.x-ex,uv1.y+ex); + vertex[5] = D3DVERTEX2(D3DVECTOR(p4.x, p2.y, 0.0f), n, uv2.x-ex,uv1.y ); + vertex[6] = D3DVERTEX2(D3DVECTOR(p2.x, p4.y, 0.0f), n, uv2.x, uv1.y+ex); + vertex[7] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, uv2.x, uv1.y ); + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 8, NULL); + m_engine->AddStatisticTriangle(6); +} + +// Dessine les hachures autour d'un bouton. + +void CControl::DrawWarning(FPOINT pos, FPOINT dim) +{ + FPOINT uv1, uv2; + float dp; + + dp = 0.5f/256.0f; + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + + uv1.x = 64.0f/256.0f; + uv1.y = 208.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 224.0f/256.0f; + + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + if ( dim.x < dim.y*4.0f ) + { + dim.y /= 2.0f; + DrawIcon(pos, dim, uv1, uv2); + pos.y += dim.y; + DrawIcon(pos, dim, uv1, uv2); + } + else + { + dim.x /= 2.0f; + dim.y /= 2.0f; + DrawIcon(pos, dim, uv1, uv2); + pos.x += dim.x; + DrawIcon(pos, dim, uv1, uv2); + pos.x -= dim.x; + pos.y += dim.y; + DrawIcon(pos, dim, uv1, uv2); + pos.x += dim.x; + DrawIcon(pos, dim, uv1, uv2); + } +} + +// Dessine l'ombre sous un bouton. + +void CControl::DrawShadow(FPOINT pos, FPOINT dim, float deep) +{ + FPOINT uv1, uv2, corner; + float dp; + + dp = 0.5f/256.0f; + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + + pos.x += deep*0.010f*0.75f; + pos.y -= deep*0.015f; + dim.x += deep*0.005f*0.75f; + dim.y += deep*0.005f; + + uv1.x = 192.0f/256.0f; + uv1.y = 32.0f/256.0f; + uv2.x = 224.0f/256.0f; + uv2.y = 64.0f/256.0f; + + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + corner.x = 10.0f/640.0f; + corner.y = 10.0f/480.0f; + + DrawIcon(pos, dim, uv1, uv2, corner, 6.0f/256.0f); +} + + +// Détecte si une position est dans le bouton. + +BOOL CControl::Detect(FPOINT pos) +{ + return ( pos.x >= m_pos.x && + pos.x <= m_pos.x+m_dim.x && + pos.y >= m_pos.y && + pos.y <= m_pos.y+m_dim.y ); +} + + diff --git a/src/control.h b/src/control.h new file mode 100644 index 00000000..d40b3a14 --- /dev/null +++ b/src/control.h @@ -0,0 +1,118 @@ +// control.h + +#ifndef _CONTROL_H_ +#define _CONTROL_H_ + + +class CInstanceManager; +class CEvent; +class CD3DEngine; +class CRobotMain; +class CParticule; +class CSound; + +enum FontType; + + +#define STATE_ENABLE (1<<0) // actif +#define STATE_CHECK (1<<1) // enfoncé +#define STATE_HILIGHT (1<<2) // survolé par la souris +#define STATE_PRESS (1<<3) // pressé par la souris +#define STATE_VISIBLE (1<<4) // visible +#define STATE_DEAD (1<<5) // inaccessible (x) +#define STATE_DEFAULT (1<<6) // actionné par RETURN +#define STATE_OKAY (1<<7) // point vert en bas à droite +#define STATE_SHADOW (1<<8) // ombre +#define STATE_GLINT (1<<9) // reflet dynamique +#define STATE_CARD (1<<10) // onglet +#define STATE_EXTEND (1<<11) // mode étendu +#define STATE_SIMPLY (1<<12) // sans ornements +#define STATE_FRAME (1<<13) // cadre de mise en évidence +#define STATE_WARNING (1<<14) // cadre hachuré jaune/noir +#define STATE_VALUE (1<<15) // affiche la valeur +#define STATE_RUN (1<<16) // programme en cours + + + +class CControl +{ +public: + CControl(CInstanceManager* iMan); + virtual ~CControl(); + + virtual BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + virtual BOOL EventProcess(const Event &event); + + virtual void SetPos(FPOINT pos); + virtual FPOINT RetPos(); + virtual void SetDim(FPOINT dim); + virtual FPOINT RetDim(); + virtual BOOL SetState(int state, BOOL bState); + virtual BOOL SetState(int state); + virtual BOOL ClearState(int state); + virtual BOOL TestState(int state); + virtual int RetState(); + virtual void SetIcon(int icon); + virtual int RetIcon(); + virtual void SetName(char* name, BOOL bTooltip=TRUE); + virtual char* RetName(); + virtual void SetJustif(int mode); + virtual int RetJustif(); + virtual void SetFontSize(float size); + virtual float RetFontSize(); + virtual void SetFontStretch(float stretch); + virtual float RetFontStretch(); + virtual void SetFontType(FontType font); + virtual FontType RetFontType(); + virtual BOOL SetTooltip(char* name); + virtual BOOL GetTooltip(FPOINT pos, char* name); + virtual void SetFocus(BOOL bFocus); + virtual BOOL RetFocus(); + + virtual EventMsg RetEventMsg(); + + virtual void Draw(); + +protected: + void GlintDelete(); + void GlintCreate(FPOINT ref, BOOL bLeft=TRUE, BOOL bUp=TRUE); + void GlintFrame(const Event &event); + void DrawPart(int icon, float zoom, float ex); + void DrawIcon(FPOINT pos, FPOINT dim, FPOINT uv1, FPOINT uv2, float ex=0.0f); + void DrawIcon(FPOINT pos, FPOINT dim, FPOINT uv1, FPOINT uv2, FPOINT corner, float ex); + void DrawWarning(FPOINT pos, FPOINT dim); + void DrawShadow(FPOINT pos, FPOINT dim, float deep=1.0f); + virtual BOOL Detect(FPOINT pos); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CEvent* m_event; + CRobotMain* m_main; + CParticule* m_particule; + CSound* m_sound; + + FPOINT m_pos; // coin sup/gauche + FPOINT m_dim; // dimensions + int m_icon; + EventMsg m_eventMsg; // message à envoyer si clic + int m_state; // états (STATE_*) + float m_fontSize; // taille du nom du bouton + float m_fontStretch; // stretch de la fonte + FontType m_fontType; // type de la fonte + int m_justif; // type de justification (-1,0,1) + char m_name[100]; // nom du bouton + char m_tooltip[100]; // nom du tooltip + BOOL m_bFocus; + BOOL m_bCapture; + + BOOL m_bGlint; + FPOINT m_glintCorner1; + FPOINT m_glintCorner2; + float m_glintProgress; + FPOINT m_glintMouse; +}; + + +#endif //_CONTROL_H_ diff --git a/src/cur00001.cur b/src/cur00001.cur new file mode 100644 index 0000000000000000000000000000000000000000..e19873efff3544b427011003c252ce76cf874ab8 GIT binary patch literal 326 zcmbV{F%E-337~tmq z2+#p{mo1VjIQPhho<-8x{LzsnGm=@E)|fg%SwgJMZ9(JpoVP*Adw&M>$~^Vq3# z1-JlLrzMIr7@rt6-jk%g_^VRfINBpYY@x29jgV}a?N|&=^Ko4>zTv-^Jl09Cv-E7F TwUU;gZR3Nx26{i&eXYL%kCAjc literal 0 HcmV?d00001 diff --git a/src/cur00003.cur b/src/cur00003.cur new file mode 100644 index 0000000000000000000000000000000000000000..8a4745ad15920fa08a7c234aaf18244ee7e417d5 GIT binary patch literal 326 zcmZXOO%4G;6okJX!o+68!q&vf(hW>pY=&4^I)bBc6K=qc1m%^#jp}rF-h0(wHDn~p z5{g*U1VdmFJ6cq+Q!J}_o&SfVceB35qa6Bu$V+HfkTnMU6jio^&K7zb7_4Eug834b zzFoWTxqVIe`N|yg%r)OR&WqgmgVeO7T~|63(rF}JX40*e9x?l#N~V&U<(1pynvYrdIe(oQOk z6x1}*n7$6uT+#}Rv`66Nq*8~)_Hg)P5+3YB?EJ>4 hgg^XEFZ-=i>+DwSd{uR!r qUIyrf|7#e?2)+-&*FN~%1s{FzUV|foO^xsLZr-|m_B-c1_0I(P`Eou0 literal 0 HcmV?d00001 diff --git a/src/cursorsc.cur b/src/cursorsc.cur new file mode 100644 index 0000000000000000000000000000000000000000..52004ac078efd7297e2d0a77e75e4e79c220cc06 GIT binary patch literal 326 zcmb7@E9050!QIys?~O5q0W>2B1?_~7ZSq&H?BMr z+<`|RC|-dS$5B7|m?Ry^6HKkJ@33vKuCOdH&oE6ej`3ayYHZ-@yLtDmv)(@YqfPAa jca$RKEcLzAbyC|(O(Uh`4;dwpfA!0|Z=Ln_+3z`T*E)77 literal 0 HcmV?d00001 diff --git a/src/d3dapp.cpp b/src/d3dapp.cpp new file mode 100644 index 00000000..7ca6d5a7 --- /dev/null +++ b/src/d3dapp.cpp @@ -0,0 +1,2433 @@ +// D3DApp.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "struct.h" +#include "D3DTextr.h" +#include "D3DEngine.h" +#include "language.h" +#include "event.h" +#include "profile.h" +#include "iman.h" +#include "restext.h" +#include "math3d.h" +#include "joystick.h" +#include "robotmain.h" +#include "sound.h" +#include "D3DApp.h" + + + + +#define AUDIO_TRACK 13 // nb total de pistes audio sur le CD +#define MAX_STEP 0.2f // temps maximum pour un step + +#define WINDOW_DX (640+6) // dimensions en mode fenêtré +#define WINDOW_DY (480+25) + +#define USE_THREAD FALSE // TRUE ne fonctionne pas ! +#define TIME_THREAD 0.02f + + + + +// Limite le débattement des commandes clavier & joystick. + +float AxeLimit(float value) +{ + if ( value < -1.0f ) value = -1.0f; + if ( value > 1.0f ) value = 1.0f; + return value; +} + + +// Entry point to the program. Initializes everything, and goes into a +// message-processing loop. Idle time is used to render the scene. + +INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT ) +{ + Error err; + char string[100]; + + CD3DApplication d3dApp; // unique instance de l'application + + err = d3dApp.CheckMistery(strCmdLine); + if ( err != ERR_OK ) + { + GetResource(RES_ERR, err, string); +#if _NEWLOOK + MessageBox( NULL, string, _T("CeeBot"), MB_ICONERROR|MB_OK ); +#else + MessageBox( NULL, string, _T("COLOBOT"), MB_ICONERROR|MB_OK ); +#endif + return 0; + } + + if ( FAILED(d3dApp.Create(hInst, strCmdLine)) ) + { + return 0; + } + + return d3dApp.Run(); // exécution du tout +} + + +// Internal function prototypes and variables. + +enum APPMSGTYPE { MSG_NONE, MSGERR_APPMUSTEXIT, MSGWARN_SWITCHEDTOSOFTWARE }; + +static INT CALLBACK AboutProc( HWND, UINT, WPARAM, LPARAM ); +static LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); + +static CD3DApplication* g_pD3DApp; + + + +// Constructor. + +CD3DApplication::CD3DApplication() +{ + int i; + + m_iMan = new(CInstanceManager); + m_event = new CEvent(m_iMan); + + m_pD3DEngine = 0; + m_pRobotMain = 0; + m_pSound = 0; + m_pFramework = 0; + m_instance = 0; + m_hWnd = 0; + m_pDD = 0; + m_pD3D = 0; + m_pD3DDevice = 0; + + m_CDpath[0] = 0; + + m_pddsRenderTarget = 0; + m_pddsDepthBuffer = 0; + + m_keyState = 0; + m_axeKey = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_axeJoy = D3DVECTOR(0.0f, 0.0f, 0.0f); + + m_vidMemTotal = 0; + m_bActive = FALSE; + m_bActivateApp = FALSE; + m_bReady = FALSE; + m_bJoystick = FALSE; + m_aTime = 0.0f; + + for ( i=0 ; i<32 ; i++ ) + { + m_bJoyButton[i] = FALSE; + } + +#if _NEWLOOK + m_strWindowTitle = _T("CeeBot"); +#else + m_strWindowTitle = _T("COLOBOT"); +#endif + m_bAppUseZBuffer = TRUE; + m_bAppUseStereo = TRUE; + m_bShowStats = FALSE; + m_bDebugMode = FALSE; + m_bAudioState = TRUE; + m_bAudioTrack = TRUE; + m_bNiceMouse = FALSE; + m_bSetupMode = TRUE; + m_fnConfirmDevice = 0; + + ResetKey(); + + g_pD3DApp = this; + + // Demande l'événement envoyé par les souris Logitech. + m_mshMouseWheel = RegisterWindowMessage(MSH_MOUSEWHEEL); + + _mkdir("files\\"); +} + + +// Destructor. + +CD3DApplication::~CD3DApplication() +{ + delete m_iMan; +} + + + +// Retourne le chemin d'accès du CD. + +char* CD3DApplication::RetCDpath() +{ + return m_CDpath; +} + +// Lit les informations dans la base de registre. + +Error CD3DApplication::RegQuery() +{ + FILE* file = NULL; + HKEY key; + LONG i; + DWORD type, len; + char filename[100]; + +#if _NEWLOOK + #if _TEEN + i = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Epsitec\\CeeBot-Teen\\Setup", + #else + i = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Epsitec\\CeeBot-A\\Setup", + #endif +#else + i = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Epsitec\\Colobot\\Setup", +#endif + 0, KEY_READ, &key); + if ( i != ERROR_SUCCESS ) return ERR_INSTALL; + + type = REG_SZ; + len = sizeof(m_CDpath); + i = RegQueryValueEx(key, "CDpath", NULL, &type, (LPBYTE)m_CDpath, &len); + if ( i != ERROR_SUCCESS || type != REG_SZ ) return ERR_INSTALL; + + filename[0] = m_CDpath[0]; + filename[1] = ':'; + filename[2] = '\\'; + filename[3] = 0; + i = GetDriveType(filename); + if ( i != DRIVE_CDROM ) return ERR_NOCD; + + strcat(filename, "install.ini"); + file = fopen(filename, "rb"); // fichier install.ini inexistant ? + if ( file == NULL ) return ERR_NOCD; + fclose(file); + + return ERR_OK; +} + +// Vérifie la présence des pistes audio sur le CD. + +Error CD3DApplication::AudioQuery() +{ + MCI_OPEN_PARMS mciOpenParms; + MCI_STATUS_PARMS mciStatusParms; + DWORD dwReturn; + UINT deviceID; + char device[10]; + + // Open the device by specifying the device and filename. + // MCI will attempt to choose the MIDI mapper as the output port. + memset(&mciOpenParms, 0, sizeof(MCI_OPEN_PARMS)); + mciOpenParms.lpstrDeviceType = (LPCTSTR)MCI_DEVTYPE_CD_AUDIO; + if ( m_CDpath[0] == 0 ) + { + dwReturn = mciSendCommand(NULL, + MCI_OPEN, + MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID, + (DWORD)(LPVOID)&mciOpenParms); + } + else + { + device[0] = m_CDpath[0]; + device[1] = ':'; + device[2] = 0; + mciOpenParms.lpstrElementName = device; + dwReturn = mciSendCommand(NULL, + MCI_OPEN, + MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID|MCI_OPEN_ELEMENT, + (DWORD)(LPVOID)&mciOpenParms); + } + if ( dwReturn != 0 ) + { + return ERR_NOCD; + } + + // The device opened successfully; get the device ID. + deviceID = mciOpenParms.wDeviceID; + + memset(&mciStatusParms, 0, sizeof(MCI_STATUS_PARMS)); + mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + dwReturn = mciSendCommand(deviceID, + MCI_STATUS, + MCI_WAIT|MCI_STATUS_ITEM, + (DWORD)&mciStatusParms); + if ( dwReturn != 0 ) + { + mciSendCommand(deviceID, MCI_CLOSE, 0, NULL); + return ERR_NOCD; + } + + if ( mciStatusParms.dwReturn != AUDIO_TRACK ) + { + mciSendCommand(deviceID, MCI_CLOSE, 0, NULL); + return ERR_NOCD; + } + + mciSendCommand(deviceID, MCI_CLOSE, 0, NULL); + return ERR_OK; +} + +// Vérifie la présence de la clé. + +Error CD3DApplication::CheckMistery(char *strCmdLine) +{ + if ( strstr(strCmdLine, "-debug") != 0 ) + { + m_bShowStats = TRUE; + SetDebugMode(TRUE); + } + + if ( strstr(strCmdLine, "-audiostate") != 0 ) + { + m_bAudioState = FALSE; + } + + if ( strstr(strCmdLine, "-audiotrack") != 0 ) + { + m_bAudioTrack = FALSE; + } + + m_CDpath[0] = 0; +#if _FULL + if ( strstr(strCmdLine, "-nocd") == 0 && !m_bDebugMode ) + { + Error err; + + err = RegQuery(); + if ( err != ERR_OK ) return err; + + //?err = AudioQuery(); + //?if ( err != ERR_OK ) return err; + } +#endif +#if _SCHOOL & _EDU + if ( strstr(strCmdLine, "-nosetup") != 0 ) + { + m_bSetupMode = FALSE; + } + m_bAudioTrack = FALSE; +#endif +#if _SCHOOL & _PERSO + Error err = RegQuery(); + if ( err != ERR_OK ) return err; + m_bAudioTrack = FALSE; +#endif +#if _SCHOOL & _CEEBOTDEMO + m_bAudioTrack = FALSE; +#endif +#if _NET + m_bAudioTrack = FALSE; +#endif +#if _DEMO + m_bAudioTrack = FALSE; +#endif + + return ERR_OK; +} + + +// Retourne la quantité totale de mémoire vidéo pour les textures. + +int CD3DApplication::GetVidMemTotal() +{ + return m_vidMemTotal; +} + +BOOL CD3DApplication::IsVideo8MB() +{ + if ( m_vidMemTotal == 0 ) return FALSE; + return (m_vidMemTotal <= 8388608L); // 8 Mb ou moins (2^23) ? +} + +BOOL CD3DApplication::IsVideo32MB() +{ + if ( m_vidMemTotal == 0 ) return FALSE; + return (m_vidMemTotal > 16777216L); // plus de 16 Mb (2^24) ? +} + + +void CD3DApplication::SetShowStat(BOOL bShow) +{ + m_bShowStats = bShow; +} + +BOOL CD3DApplication::RetShowStat() +{ + return m_bShowStats; +} + + +void CD3DApplication::SetDebugMode(BOOL bMode) +{ + m_bDebugMode = bMode; + D3DTextr_SetDebugMode(m_bDebugMode); +} + +BOOL CD3DApplication::RetDebugMode() +{ + return m_bDebugMode; +} + +BOOL CD3DApplication::RetSetupMode() +{ + return m_bSetupMode; +} + + + + +// Processus fils de gestion du temps. + +DWORD WINAPI ThreadRoutine(LPVOID) +{ + Event event; + float time; + int ms, start, end, delay; + + ms = (int)(TIME_THREAD*1000.0f); + time = 0.0f; + while ( TRUE ) + { + start = timeGetTime(); + + g_pD3DApp->m_pD3DEngine->FrameMove(TIME_THREAD); + + ZeroMemory(&event, sizeof(Event)); + event.event = EVENT_FRAME; + event.rTime = TIME_THREAD; + event.axeX = AxeLimit(g_pD3DApp->m_axeKey.x + g_pD3DApp->m_axeJoy.x); + event.axeY = AxeLimit(g_pD3DApp->m_axeKey.y + g_pD3DApp->m_axeJoy.y); + event.axeZ = AxeLimit(g_pD3DApp->m_axeKey.z + g_pD3DApp->m_axeJoy.z); + event.keyState = g_pD3DApp->m_keyState; + + if ( g_pD3DApp->m_pRobotMain != 0 ) + { + g_pD3DApp->m_pRobotMain->EventProcess(event); + } + + end = timeGetTime(); + + delay = ms-(end-start); + if ( delay > 0 ) + { + Sleep(delay); // attend 20ms-used + } + time += TIME_THREAD; + } + return 0; +} + + +// Called during device intialization, this code checks the device +// for some minimum set of capabilities. + +HRESULT CD3DApplication::ConfirmDevice( DDCAPS* pddDriverCaps, + D3DDEVICEDESC7* pd3dDeviceDesc ) +{ +//? if( pd3dDeviceDesc->wMaxVertexBlendMatrices < 2 ) +//? return E_FAIL; + + return S_OK; +} + +// Create the application. + +HRESULT CD3DApplication::Create( HINSTANCE hInst, TCHAR* strCmdLine ) +{ + HRESULT hr; + char deviceName[100]; + char modeName[100]; + int iValue; + DWORD style; + BOOL bFull, b3D; + + m_instance = hInst; + + InitCurrentDirectory(); + + // Enumerate available D3D devices. The callback is used so the app can + // confirm/reject each enumerated device depending on its capabilities. + if( FAILED( hr = D3DEnum_EnumerateDevices( m_fnConfirmDevice ) ) ) + { + DisplayFrameworkError( hr, MSGERR_APPMUSTEXIT ); + return hr; + } + + if( FAILED( hr = D3DEnum_SelectDefaultDevice( &m_pDeviceInfo ) ) ) + { + DisplayFrameworkError( hr, MSGERR_APPMUSTEXIT ); + return hr; + } + + if ( !m_bDebugMode ) + { + m_pDeviceInfo->bWindowed = FALSE; // plein écran + } + if ( GetProfileInt("Device", "FullScreen", bFull) ) + { + m_pDeviceInfo->bWindowed = !bFull; + } + + // Create the 3D engine. + if( (m_pD3DEngine = new CD3DEngine(m_iMan, this)) == NULL ) + { + DisplayFrameworkError( D3DENUMERR_ENGINE, MSGERR_APPMUSTEXIT ); + return E_OUTOFMEMORY; + } + SetEngine(m_pD3DEngine); + + // Initialize the app's custom scene stuff + if( FAILED( hr = m_pD3DEngine->OneTimeSceneInit() ) ) + { + DisplayFrameworkError( hr, MSGERR_APPMUSTEXIT ); + return hr; + } + + // Create a new CD3DFramework class. This class does all of our D3D + // initialization and manages the common D3D objects. + if( (m_pFramework = new CD3DFramework7()) == NULL ) + { + DisplayFrameworkError( E_OUTOFMEMORY, MSGERR_APPMUSTEXIT ); + return E_OUTOFMEMORY; + } + + // Create the sound instance. + if( (m_pSound = new CSound(m_iMan)) == NULL ) + { + DisplayFrameworkError( D3DENUMERR_SOUND, MSGERR_APPMUSTEXIT ); + return E_OUTOFMEMORY; + } + + // Create the robot application. + if( (m_pRobotMain = new CRobotMain(m_iMan)) == NULL ) + { + DisplayFrameworkError( D3DENUMERR_ROBOT, MSGERR_APPMUSTEXIT ); + return E_OUTOFMEMORY; + } + + // Register the window class + WNDCLASS wndClass = { 0, WndProc, 0, 0, hInst, + LoadIcon( hInst, MAKEINTRESOURCE(IDI_MAIN_ICON) ), + LoadCursor( NULL, IDC_ARROW ), + (HBRUSH)GetStockObject(WHITE_BRUSH), + NULL, _T("D3D Window") }; + RegisterClass( &wndClass ); + + // Create the render window + style = WS_CAPTION|WS_VISIBLE; + if ( m_bDebugMode ) style |= WS_SYSMENU; // case de fermeture + m_hWnd = CreateWindow( _T("D3D Window"), m_strWindowTitle, +//? WS_OVERLAPPEDWINDOW|WS_VISIBLE, + style, CW_USEDEFAULT, CW_USEDEFAULT, + WINDOW_DX, WINDOW_DY, 0L, +//? LoadMenu( hInst, MAKEINTRESOURCE(IDR_MENU) ), + NULL, + hInst, 0L ); + UpdateWindow( m_hWnd ); + + if ( !GetProfileInt("Setup", "Sound3D", b3D) ) + { + b3D = TRUE; + } + m_pSound->SetDebugMode(m_bDebugMode); + m_pSound->Create(m_hWnd, b3D); + m_pSound->CacheAll(); + m_pSound->SetState(m_bAudioState); + m_pSound->SetAudioTrack(m_bAudioTrack); + m_pSound->SetCDpath(m_CDpath); + + // Initialize the 3D environment for the app + if( FAILED( hr = Initialize3DEnvironment() ) ) + { + DisplayFrameworkError( hr, MSGERR_APPMUSTEXIT ); + Cleanup3DEnvironment(); + return E_FAIL; + } + + // Change the display device driver. + GetProfileString("Device", "Name", deviceName, 100); + GetProfileString("Device", "Mode", modeName, 100); + GetProfileInt("Device", "FullScreen", bFull); + if ( deviceName[0] != 0 && modeName[0] != 0 && bFull ) + { + ChangeDevice(deviceName, modeName, bFull); + } + + // Première exécution ? + if ( !GetProfileInt("Setup", "ObjectDirty", iValue) ) + { + m_pD3DEngine->FirstExecuteAdapt(TRUE); + } + + // Crée le fichier colobot.ini à la première exécution. + m_pRobotMain->CreateIni(); + +#if _DEMO + m_pRobotMain->ChangePhase(PHASE_NAME); +#else +#if _NET | _SCHOOL + m_pRobotMain->ChangePhase(PHASE_WELCOME2); +#else +#if _FRENCH + m_pRobotMain->ChangePhase(PHASE_WELCOME2); +#endif +#if _ENGLISH + m_pRobotMain->ChangePhase(PHASE_WELCOME2); +#endif +#if _GERMAN + m_pRobotMain->ChangePhase(PHASE_WELCOME2); +#endif +#if _WG + m_pRobotMain->ChangePhase(PHASE_WELCOME1); +#endif +#if _POLISH + m_pRobotMain->ChangePhase(PHASE_WELCOME1); +#endif +#endif +#endif + m_pD3DEngine->TimeInit(); + +#if USE_THREAD + m_thread = CreateThread(NULL, 0, ThreadRoutine, this, 0, &m_threadId); + SetThreadPriority(m_thread, THREAD_PRIORITY_ABOVE_NORMAL); +#endif + + // The app is ready to go + m_bReady = TRUE; + + return S_OK; +} + + +// Message-processing loop. Idle time is used to render the scene. + +INT CD3DApplication::Run() +{ + // Load keyboard accelerators + HACCEL hAccel = LoadAccelerators( NULL, MAKEINTRESOURCE(IDR_MAIN_ACCEL) ); + + // Now we're ready to recieve and process Windows messages. + BOOL bGotMsg; + MSG msg; + PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE ); + + while( WM_QUIT != msg.message ) + { + // Use PeekMessage() if the app is active, so we can use idle time to + // render the scene. Else, use GetMessage() to avoid eating CPU time. + if( m_bActive ) + bGotMsg = PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ); + else + bGotMsg = GetMessage( &msg, NULL, 0U, 0U ); + + if( bGotMsg ) + { + // Translate and dispatch the message + if( TranslateAccelerator( m_hWnd, hAccel, &msg ) == 0 ) + { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + } + else + { + // Render a frame during idle time (no messages are waiting) + if( m_bActive && m_bReady ) + { + Event event; + + while ( m_event->GetEvent(event) ) + { + if ( event.event == EVENT_QUIT ) + { +//? SendMessage( m_hWnd, WM_CLOSE, 0, 0 ); + m_pSound->StopMusic(); + Cleanup3DEnvironment(); + PostQuitMessage(0); + return msg.wParam; + } + m_pRobotMain->EventProcess(event); + } + + if ( !RetNiceMouse() ) + { + SetMouseType(m_pD3DEngine->RetMouseType()); + } + + if( FAILED( Render3DEnvironment() ) ) + DestroyWindow( m_hWnd ); + } + } + } + + return msg.wParam; +} + + + +// Conversion de la position de la souris. +// x: 0=gauche, 1=droite +// y: 0=bas, 1=haut + +FPOINT CD3DApplication::ConvPosToInterface(HWND hWnd, LPARAM lParam) +{ + POINT cpos; + FPOINT pos; + float px, py, w, h; + + cpos.x = (short)LOWORD(lParam); + cpos.y = (short)HIWORD(lParam); + + if ( !m_pDeviceInfo->bWindowed ) + { + ClientToScreen(hWnd, &cpos); + } + + px = (float)cpos.x; + py = (float)cpos.y; + w = (float)m_ddsdRenderTarget.dwWidth; + h = (float)m_ddsdRenderTarget.dwHeight; + + pos.x = px/w; + pos.y = 1.0f-py/h; + + return pos; +} + +// Déplace physiquement la souris. + +void CD3DApplication::SetMousePos(FPOINT pos) +{ + POINT p; + + pos.y = 1.0f-pos.y; + + pos.x *= m_ddsdRenderTarget.dwWidth; + pos.y *= m_ddsdRenderTarget.dwHeight; + + p.x = (int)pos.x; + p.y = (int)pos.y; + ClientToScreen(m_hWnd, &p); + + SetCursorPos(p.x, p.y); +} + +// Choix du type de curseur pour la souris. + +void CD3DApplication::SetMouseType(D3DMouse type) +{ + HCURSOR hc; + + if ( type == D3DMOUSEHAND ) + { + hc = LoadCursor(m_instance, MAKEINTRESOURCE(IDC_CURSORHAND)); + } + else if ( type == D3DMOUSECROSS ) + { + hc = LoadCursor(NULL, IDC_CROSS); + } + else if ( type == D3DMOUSEEDIT ) + { + hc = LoadCursor(NULL, IDC_IBEAM); + } + else if ( type == D3DMOUSENO ) + { + hc = LoadCursor(NULL, IDC_NO); + } + else if ( type == D3DMOUSEMOVE ) + { + hc = LoadCursor(NULL, IDC_SIZEALL); + } + else if ( type == D3DMOUSEMOVEH ) + { + hc = LoadCursor(NULL, IDC_SIZEWE); + } + else if ( type == D3DMOUSEMOVEV ) + { + hc = LoadCursor(NULL, IDC_SIZENS); + } + else if ( type == D3DMOUSEMOVED ) + { + hc = LoadCursor(NULL, IDC_SIZENESW); + } + else if ( type == D3DMOUSEMOVEI ) + { + hc = LoadCursor(NULL, IDC_SIZENWSE); + } + else if ( type == D3DMOUSEWAIT ) + { + hc = LoadCursor(NULL, IDC_WAIT); + } + else if ( type == D3DMOUSESCROLLL ) + { + hc = LoadCursor(m_instance, MAKEINTRESOURCE(IDC_CURSORSCROLLL)); + } + else if ( type == D3DMOUSESCROLLR ) + { + hc = LoadCursor(m_instance, MAKEINTRESOURCE(IDC_CURSORSCROLLR)); + } + else if ( type == D3DMOUSESCROLLU ) + { + hc = LoadCursor(m_instance, MAKEINTRESOURCE(IDC_CURSORSCROLLU)); + } + else if ( type == D3DMOUSESCROLLD ) + { + hc = LoadCursor(m_instance, MAKEINTRESOURCE(IDC_CURSORSCROLLD)); + } + else if ( type == D3DMOUSETARGET ) + { + hc = LoadCursor(m_instance, MAKEINTRESOURCE(IDC_CURSORTARGET)); + } + else + { + hc = LoadCursor(NULL, IDC_ARROW); + } + + if ( hc != NULL ) + { + SetCursor(hc); + } +} + +// Choix du mode pour la souris. + +void CD3DApplication::SetNiceMouse(BOOL bNice) +{ + if ( bNice == m_bNiceMouse ) return; + m_bNiceMouse = bNice; + + if ( m_bNiceMouse ) + { + ShowCursor(FALSE); // cache la vilaine souris windows + SetCursor(NULL); + } + else + { + ShowCursor(TRUE); // montre la vilaine souris windows + SetCursor(LoadCursor(NULL, IDC_ARROW)); + } +} + +// Indique s'il faut utiliser la jolie souris ombrée. + +BOOL CD3DApplication::RetNiceMouse() +{ + if ( m_pDeviceInfo->bWindowed ) return FALSE; + if ( !m_pDeviceInfo->bHardware ) return FALSE; + + return m_bNiceMouse; +} + +// Indique s'il est possible d'utiliser la jolie souris ombrée. + +BOOL CD3DApplication::RetNiceMouseCap() +{ + if ( m_pDeviceInfo->bWindowed ) return FALSE; + if ( !m_pDeviceInfo->bHardware ) return FALSE; + + return TRUE; +} + + +// Static msg handler which passes messages to the application class. + +LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + if ( g_pD3DApp != 0 ) + { + Event event; + short move; + + ZeroMemory(&event, sizeof(Event)); + +#if 0 + if ( uMsg == WM_KEYDOWN || + uMsg == WM_CHAR || + uMsg == WM_XBUTTONDOWN || + uMsg == WM_XBUTTONUP ) + { + char s[100]; + sprintf(s, "event: %d %d %d\n", uMsg, wParam, lParam); + OutputDebugString(s); + } +#endif + + if ( uMsg == WM_LBUTTONDOWN ) event.event = EVENT_LBUTTONDOWN; + if ( uMsg == WM_RBUTTONDOWN ) event.event = EVENT_RBUTTONDOWN; + if ( uMsg == WM_LBUTTONUP ) event.event = EVENT_LBUTTONUP; + if ( uMsg == WM_RBUTTONUP ) event.event = EVENT_RBUTTONUP; + if ( uMsg == WM_MOUSEMOVE ) event.event = EVENT_MOUSEMOVE; + if ( uMsg == WM_KEYDOWN ) event.event = EVENT_KEYDOWN; + if ( uMsg == WM_KEYUP ) event.event = EVENT_KEYUP; + if ( uMsg == WM_CHAR ) event.event = EVENT_CHAR; + + if ( uMsg == WM_XBUTTONUP ) + { + if ( (wParam>>16) == XBUTTON1 ) event.event = EVENT_HYPER_PREV; + if ( (wParam>>16) == XBUTTON2 ) event.event = EVENT_HYPER_NEXT; + } + + event.param = wParam; + event.axeX = AxeLimit(g_pD3DApp->m_axeKey.x + g_pD3DApp->m_axeJoy.x); + event.axeY = AxeLimit(g_pD3DApp->m_axeKey.y + g_pD3DApp->m_axeJoy.y); + event.axeZ = AxeLimit(g_pD3DApp->m_axeKey.z + g_pD3DApp->m_axeJoy.z); + event.keyState = g_pD3DApp->m_keyState; + + if ( uMsg == WM_LBUTTONDOWN || + uMsg == WM_RBUTTONDOWN || + uMsg == WM_LBUTTONUP || + uMsg == WM_RBUTTONUP || + uMsg == WM_MOUSEMOVE ) // événement souris ? + { + event.pos = g_pD3DApp->ConvPosToInterface(hWnd, lParam); + g_pD3DApp->m_mousePos = event.pos; + g_pD3DApp->m_pD3DEngine->SetMousePos(event.pos); + } + + if ( uMsg == WM_MOUSEWHEEL ) // molette souris ? + { + event.event = EVENT_KEYDOWN; + event.pos = g_pD3DApp->m_mousePos; + move = HIWORD(wParam); + if ( move/WHEEL_DELTA > 0 ) event.param = VK_WHEELUP; + if ( move/WHEEL_DELTA < 0 ) event.param = VK_WHEELDOWN; + } + if ( g_pD3DApp->m_mshMouseWheel != 0 && + uMsg == g_pD3DApp->m_mshMouseWheel ) // molette souris Logitech ? + { + event.event = EVENT_KEYDOWN; + event.pos = g_pD3DApp->m_mousePos; + move = LOWORD(wParam); + if ( move/WHEEL_DELTA > 0 ) event.param = VK_WHEELUP; + if ( move/WHEEL_DELTA < 0 ) event.param = VK_WHEELDOWN; + } + + if ( event.event == EVENT_KEYDOWN || + event.event == EVENT_KEYUP || + event.event == EVENT_CHAR ) + { + if ( event.param == 0 ) + { + event.event = EVENT_NULL; + } + } + + if ( g_pD3DApp->m_pRobotMain != 0 && event.event != 0 ) + { + g_pD3DApp->m_pRobotMain->EventProcess(event); +//? if ( !g_pD3DApp->RetNiceMouse() ) +//? { +//? g_pD3DApp->SetMouseType(g_pD3DApp->m_pD3DEngine->RetMouseType()); +//? } + } + if ( g_pD3DApp->m_pD3DEngine != 0 ) + { + g_pD3DApp->m_pD3DEngine->MsgProc( hWnd, uMsg, wParam, lParam ); + } + return g_pD3DApp->MsgProc( hWnd, uMsg, wParam, lParam ); + } + + return DefWindowProc( hWnd, uMsg, wParam, lParam ); +} + + +// Minimal message proc function for the about box. + +BOOL CALLBACK AboutProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM ) +{ + if( WM_COMMAND == uMsg ) + if( IDOK == LOWORD(wParam) || IDCANCEL == LOWORD(wParam) ) + EndDialog( hWnd, TRUE ); + + return WM_INITDIALOG == uMsg ? TRUE : FALSE; +} + + + +// Ignore les touches pressées. + +void CD3DApplication::FlushPressKey() +{ + m_keyState = 0; + m_axeKey = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_axeJoy = D3DVECTOR(0.0f, 0.0f, 0.0f); +} + +// Remet les touches par défaut. + +void CD3DApplication::ResetKey() +{ + int i; + + for ( i=0 ; i<50 ; i++ ) + { + m_key[i][0] = 0; + m_key[i][1] = 0; + } + m_key[KEYRANK_LEFT ][0] = VK_LEFT; + m_key[KEYRANK_RIGHT ][0] = VK_RIGHT; + m_key[KEYRANK_UP ][0] = VK_UP; + m_key[KEYRANK_DOWN ][0] = VK_DOWN; + m_key[KEYRANK_GUP ][0] = VK_SHIFT; + m_key[KEYRANK_GDOWN ][0] = VK_CONTROL; + m_key[KEYRANK_CAMERA ][0] = VK_SPACE; + m_key[KEYRANK_CAMERA ][1] = VK_BUTTON2; + m_key[KEYRANK_DESEL ][0] = VK_NUMPAD0; + m_key[KEYRANK_DESEL ][1] = VK_BUTTON6; + m_key[KEYRANK_ACTION ][0] = VK_RETURN; + m_key[KEYRANK_ACTION ][1] = VK_BUTTON1; + m_key[KEYRANK_NEAR ][0] = VK_ADD; + m_key[KEYRANK_NEAR ][1] = VK_BUTTON5; + m_key[KEYRANK_AWAY ][0] = VK_SUBTRACT; + m_key[KEYRANK_AWAY ][1] = VK_BUTTON4; + m_key[KEYRANK_NEXT ][0] = VK_TAB; + m_key[KEYRANK_NEXT ][1] = VK_BUTTON3; + m_key[KEYRANK_HUMAN ][0] = VK_HOME; + m_key[KEYRANK_HUMAN ][1] = VK_BUTTON7; + m_key[KEYRANK_QUIT ][0] = VK_ESCAPE; + m_key[KEYRANK_HELP ][0] = VK_F1; + m_key[KEYRANK_PROG ][0] = VK_F2; + m_key[KEYRANK_CBOT ][0] = VK_F3; + m_key[KEYRANK_VISIT ][0] = VK_DECIMAL; + m_key[KEYRANK_SPEED10][0] = VK_F4; + m_key[KEYRANK_SPEED15][0] = VK_F5; + m_key[KEYRANK_SPEED20][0] = VK_F6; +// m_key[KEYRANK_SPEED30][0] = VK_F7; +} + +// Modifie une touche. + +void CD3DApplication::SetKey(int keyRank, int option, int key) +{ + if ( keyRank < 0 || + keyRank >= 50 ) return; + + if ( option < 0 || + option >= 2 ) return; + + m_key[keyRank][option] = key; +} + +// Donne une touche. + +int CD3DApplication::RetKey(int keyRank, int option) +{ + if ( keyRank < 0 || + keyRank >= 50 ) return 0; + + if ( option < 0 || + option >= 2 ) return 0; + + return m_key[keyRank][option]; +} + + + +// Utilise le joystick ou le clavier. + +void CD3DApplication::SetJoystick(BOOL bEnable) +{ + m_bJoystick = bEnable; + + if ( m_bJoystick ) // joystick ? + { + if ( !InitDirectInput(m_instance, m_hWnd) ) // initialise joystick + { + m_bJoystick = FALSE; + } + else + { + SetAcquire(TRUE); + SetTimer(m_hWnd, 0, 1000/30, NULL); + } + } + else // clavier ? + { + KillTimer(m_hWnd, 0); + SetAcquire(FALSE); + FreeDirectInput(); + } +} + +BOOL CD3DApplication::RetJoystick() +{ + return m_bJoystick; +} + + +// Message handling function. + +LRESULT CD3DApplication::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, + LPARAM lParam ) +{ + HRESULT hr; + DIJOYSTATE js; + int i; + + // La touche F10 envoie un autre message pour activer + // le menu dans les applications Windows standard ! + if ( uMsg == WM_SYSKEYDOWN && wParam == VK_F10 ) + { + uMsg = WM_KEYDOWN; + } + if ( uMsg == WM_SYSKEYUP && wParam == VK_F10 ) + { + uMsg = WM_KEYUP; + } + + // Mange l'événement "menu" envoyé par Alt ou F10. + if ( uMsg == WM_SYSCOMMAND && wParam == SC_KEYMENU ) + { + return 0; + } + + if ( uMsg == WM_KEYDOWN || uMsg == WM_KEYUP ) + { + if ( GetKeyState(VK_SHIFT) & 0x8000 ) + { + m_keyState |= KS_SHIFT; + } + else + { + m_keyState &= ~KS_SHIFT; + } + + if ( GetKeyState(VK_CONTROL) & 0x8000 ) + { + m_keyState |= KS_CONTROL; + } + else + { + m_keyState &= ~KS_CONTROL; + } + } + + switch( uMsg ) + { + case WM_KEYDOWN: + if ( wParam == m_key[KEYRANK_UP ][0] ) m_axeKey.y = 1.0f; + if ( wParam == m_key[KEYRANK_UP ][1] ) m_axeKey.y = 1.0f; + if ( wParam == m_key[KEYRANK_DOWN ][0] ) m_axeKey.y = -1.0f; + if ( wParam == m_key[KEYRANK_DOWN ][1] ) m_axeKey.y = -1.0f; + if ( wParam == m_key[KEYRANK_LEFT ][0] ) m_axeKey.x = -1.0f; + if ( wParam == m_key[KEYRANK_LEFT ][1] ) m_axeKey.x = -1.0f; + if ( wParam == m_key[KEYRANK_RIGHT][0] ) m_axeKey.x = 1.0f; + if ( wParam == m_key[KEYRANK_RIGHT][1] ) m_axeKey.x = 1.0f; + if ( wParam == m_key[KEYRANK_GUP ][0] ) m_axeKey.z = 1.0f; + if ( wParam == m_key[KEYRANK_GUP ][1] ) m_axeKey.z = 1.0f; + if ( wParam == m_key[KEYRANK_GDOWN][0] ) m_axeKey.z = -1.0f; + if ( wParam == m_key[KEYRANK_GDOWN][1] ) m_axeKey.z = -1.0f; + if ( wParam == m_key[KEYRANK_NEAR ][0] ) m_keyState |= KS_NUMPLUS; + if ( wParam == m_key[KEYRANK_NEAR ][1] ) m_keyState |= KS_NUMPLUS; + if ( wParam == m_key[KEYRANK_AWAY ][0] ) m_keyState |= KS_NUMMINUS; + if ( wParam == m_key[KEYRANK_AWAY ][1] ) m_keyState |= KS_NUMMINUS; + if ( wParam == VK_PRIOR ) m_keyState |= KS_PAGEUP; + if ( wParam == VK_NEXT ) m_keyState |= KS_PAGEDOWN; +//? if ( wParam == VK_SHIFT ) m_keyState |= KS_SHIFT; +//? if ( wParam == VK_CONTROL ) m_keyState |= KS_CONTROL; + if ( wParam == VK_NUMPAD8 ) m_keyState |= KS_NUMUP; + if ( wParam == VK_NUMPAD2 ) m_keyState |= KS_NUMDOWN; + if ( wParam == VK_NUMPAD4 ) m_keyState |= KS_NUMLEFT; + if ( wParam == VK_NUMPAD6 ) m_keyState |= KS_NUMRIGHT; + break; + + case WM_KEYUP: + if ( wParam == m_key[KEYRANK_UP ][0] ) m_axeKey.y = 0.0f; + if ( wParam == m_key[KEYRANK_UP ][1] ) m_axeKey.y = 0.0f; + if ( wParam == m_key[KEYRANK_DOWN ][0] ) m_axeKey.y = 0.0f; + if ( wParam == m_key[KEYRANK_DOWN ][1] ) m_axeKey.y = 0.0f; + if ( wParam == m_key[KEYRANK_LEFT ][0] ) m_axeKey.x = 0.0f; + if ( wParam == m_key[KEYRANK_LEFT ][1] ) m_axeKey.x = 0.0f; + if ( wParam == m_key[KEYRANK_RIGHT][0] ) m_axeKey.x = 0.0f; + if ( wParam == m_key[KEYRANK_RIGHT][1] ) m_axeKey.x = 0.0f; + if ( wParam == m_key[KEYRANK_GUP ][0] ) m_axeKey.z = 0.0f; + if ( wParam == m_key[KEYRANK_GUP ][1] ) m_axeKey.z = 0.0f; + if ( wParam == m_key[KEYRANK_GDOWN][0] ) m_axeKey.z = 0.0f; + if ( wParam == m_key[KEYRANK_GDOWN][1] ) m_axeKey.z = 0.0f; + if ( wParam == m_key[KEYRANK_NEAR ][0] ) m_keyState &= ~KS_NUMPLUS; + if ( wParam == m_key[KEYRANK_NEAR ][1] ) m_keyState &= ~KS_NUMPLUS; + if ( wParam == m_key[KEYRANK_AWAY ][0] ) m_keyState &= ~KS_NUMMINUS; + if ( wParam == m_key[KEYRANK_AWAY ][1] ) m_keyState &= ~KS_NUMMINUS; + if ( wParam == VK_PRIOR ) m_keyState &= ~KS_PAGEUP; + if ( wParam == VK_NEXT ) m_keyState &= ~KS_PAGEDOWN; +//? if ( wParam == VK_SHIFT ) m_keyState &= ~KS_SHIFT; +//? if ( wParam == VK_CONTROL ) m_keyState &= ~KS_CONTROL; + if ( wParam == VK_NUMPAD8 ) m_keyState &= ~KS_NUMUP; + if ( wParam == VK_NUMPAD2 ) m_keyState &= ~KS_NUMDOWN; + if ( wParam == VK_NUMPAD4 ) m_keyState &= ~KS_NUMLEFT; + if ( wParam == VK_NUMPAD6 ) m_keyState &= ~KS_NUMRIGHT; + break; + + case WM_LBUTTONDOWN: + m_keyState |= KS_MLEFT; + break; + + case WM_RBUTTONDOWN: + m_keyState |= KS_MRIGHT; + break; + + case WM_LBUTTONUP: + m_keyState &= ~KS_MLEFT; + break; + + case WM_RBUTTONUP: + m_keyState &= ~KS_MRIGHT; + break; + + case WM_PAINT: + // Handle paint messages when the app is not ready + if( m_pFramework && !m_bReady ) + { + if( m_pDeviceInfo->bWindowed ) + m_pFramework->ShowFrame(); + else + m_pFramework->FlipToGDISurface( TRUE ); + } + break; + + case WM_MOVE: + // If in windowed mode, move the Framework's window + if( m_pFramework && m_bActive && m_bReady && m_pDeviceInfo->bWindowed ) + m_pFramework->Move( (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam) ); + break; + + case WM_SIZE: + // Check to see if we are losing our window... + if( SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam ) + { + m_bActive = FALSE; + } + else + { + m_bActive = TRUE; + } +//? char s[100]; +//? sprintf(s, "WM_SIZE %d %d %d\n", m_bActive, m_bReady, m_pDeviceInfo->bWindowed); +//? OutputDebugString(s); + + // A new window size will require a new backbuffer + // size, so the 3D structures must be changed accordingly. + if( m_bActive && m_bReady && m_pDeviceInfo->bWindowed ) + { + m_bReady = FALSE; + +//? OutputDebugString("WM_SIZE Change3DEnvironment\n"); + if( FAILED( hr = Change3DEnvironment() ) ) + return 0; + + m_bReady = TRUE; + } + break; + + case WM_TIMER: + if ( m_bActivateApp && m_bJoystick ) + { + if ( UpdateInputState(js) ) + { + m_axeJoy.x = js.lX/1000.0f+js.lRz/1000.0f; // tourner + m_axeJoy.y = -js.lY/1000.0f; // avancer + m_axeJoy.z = -js.rglSlider[0]/1000.0f; // monter + + m_axeJoy.x = Neutral(m_axeJoy.x, 0.2f); + m_axeJoy.y = Neutral(m_axeJoy.y, 0.2f); + m_axeJoy.z = Neutral(m_axeJoy.z, 0.2f); + +//? char s[100]; +//? sprintf(s, "x=%d y=%d z=% x=%d y=%d z=%d\n", js.lX,js.lY,js.lZ,js.lRx,js.lRy,js.lRz); +//? OutputDebugString(s); + + for ( i=0 ; i<32 ; i++ ) + { + if ( js.rgbButtons[i] != 0 && !m_bJoyButton[i] ) + { + m_bJoyButton[i] = TRUE; + PostMessage(m_hWnd, WM_KEYDOWN, VK_BUTTON1+i, 0); + } + if ( js.rgbButtons[i] == 0 && m_bJoyButton[i] ) + { + m_bJoyButton[i] = FALSE; + PostMessage(m_hWnd, WM_KEYUP, VK_BUTTON1+i, 0); + } + } + } + else + { + OutputDebugString("UpdateInputState error\n"); + } + } + break; + + case WM_ACTIVATE: + if( LOWORD(wParam) == WA_INACTIVE ) + { + m_bActivateApp = FALSE; + } + else + { + m_bActivateApp = TRUE; + } + + if ( m_bActivateApp && m_bJoystick ) + { + SetAcquire(TRUE); // ré-active le joystick + } + break; + + case MM_MCINOTIFY: + if ( wParam == MCI_NOTIFY_SUCCESSFUL ) + { + OutputDebugString("Event MM_MCINOTIFY\n"); + m_pSound->SuspendMusic(); + m_pSound->RestartMusic(); + } + break; + + case WM_SETCURSOR: + // Prevent a cursor in fullscreen mode + if( m_bActive && m_bReady && !m_pDeviceInfo->bWindowed ) + { +//? SetCursor(NULL); + return 1; + } + break; + + case WM_ENTERMENULOOP: + // Pause the app when menus are displayed + Pause(TRUE); + break; + case WM_EXITMENULOOP: + Pause(FALSE); + break; + + case WM_ENTERSIZEMOVE: + // Halt frame movement while the app is sizing or moving + m_pD3DEngine->TimeEnterGel(); + break; + case WM_EXITSIZEMOVE: + m_pD3DEngine->TimeExitGel(); + break; + + case WM_NCHITTEST: + // Prevent the user from selecting the menu in fullscreen mode + if( !m_pDeviceInfo->bWindowed ) + return HTCLIENT; + + break; + + case WM_POWERBROADCAST: + switch( wParam ) + { + case PBT_APMQUERYSUSPEND: + // At this point, the app should save any data for open + // network connections, files, etc.., and prepare to go into + // a suspended mode. + return OnQuerySuspend( (DWORD)lParam ); + + case PBT_APMRESUMESUSPEND: + // At this point, the app should recover any data, network + // connections, files, etc.., and resume running from when + // the app was suspended. + return OnResumeSuspend( (DWORD)lParam ); + } + break; + + case WM_SYSCOMMAND: + // Prevent moving/sizing and power loss in fullscreen mode + switch( wParam ) + { + case SC_MOVE: + case SC_SIZE: + case SC_MAXIMIZE: + case SC_MONITORPOWER: + if( FALSE == m_pDeviceInfo->bWindowed ) + return 1; + break; + } + break; + + case WM_COMMAND: + switch( LOWORD(wParam) ) + { + case IDM_CHANGEDEVICE: + // Display the device-selection dialog box. + if( m_bActive && m_bReady ) + { + Pause(TRUE); + + if( SUCCEEDED( D3DEnum_UserChangeDevice( &m_pDeviceInfo ) ) ) + { + if( FAILED( hr = Change3DEnvironment() ) ) + return 0; + } + Pause(FALSE); + } + return 0; + + case IDM_ABOUT: + // Display the About box + Pause(TRUE); + DialogBox( (HINSTANCE)GetWindowLong( hWnd, GWL_HINSTANCE ), + MAKEINTRESOURCE(IDD_ABOUT), hWnd, AboutProc ); + Pause(FALSE); + return 0; + + case IDM_EXIT: + // Recieved key/menu command to exit app + SendMessage( hWnd, WM_CLOSE, 0, 0 ); + return 0; + } + break; + + case WM_GETMINMAXINFO: + ((MINMAXINFO*)lParam)->ptMinTrackSize.x = 100; + ((MINMAXINFO*)lParam)->ptMinTrackSize.y = 100; + break; + + case WM_CLOSE: + DestroyWindow( hWnd ); + return 0; + + case WM_DESTROY: + Cleanup3DEnvironment(); + PostQuitMessage(0); + return 0; + } + + return DefWindowProc( hWnd, uMsg, wParam, lParam ); +} + + +// Enumeration function to report valid pixel formats for z-buffers. + +HRESULT WINAPI EnumZBufferFormatsCallback(DDPIXELFORMAT* pddpf, + VOID* pContext) +{ + DDPIXELFORMAT* pddpfOut = (DDPIXELFORMAT*)pContext; + + char s[100]; + sprintf(s, "EnumZBufferFormatsCallback %d\n", pddpf->dwRGBBitCount); + OutputDebugString(s); + + if( pddpfOut->dwRGBBitCount == pddpf->dwRGBBitCount ) + { + (*pddpfOut) = (*pddpf); + return D3DENUMRET_CANCEL; + } + + return D3DENUMRET_OK; +} + +// Internal function called by Create() to make and attach a zbuffer +// to the renderer. + +HRESULT CD3DApplication::CreateZBuffer(GUID* pDeviceGUID) +{ + HRESULT hr; + + // Check if the device supports z-bufferless hidden surface removal. If so, + // we don't really need a z-buffer + D3DDEVICEDESC7 ddDesc; + m_pD3DDevice->GetCaps( &ddDesc ); + if( ddDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ZBUFFERLESSHSR ) + return S_OK; + + // Get z-buffer dimensions from the render target + DDSURFACEDESC2 ddsd; + ddsd.dwSize = sizeof(ddsd); + m_pddsRenderTarget->GetSurfaceDesc( &ddsd ); + + // Setup the surface desc for the z-buffer. + ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT; + ddsd.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | DDSCAPS_VIDEOMEMORY; + ddsd.ddpfPixelFormat.dwSize = 0; // Tag the pixel format as unitialized + + // Get an appropiate pixel format from enumeration of the formats. On the + // first pass, we look for a zbuffer dpeth which is equal to the frame + // buffer depth (as some cards unfornately require this). + m_pD3D->EnumZBufferFormats( *pDeviceGUID, EnumZBufferFormatsCallback, + (VOID*)&ddsd.ddpfPixelFormat ); + if( 0 == ddsd.ddpfPixelFormat.dwSize ) + { + // Try again, just accepting any 16-bit zbuffer + ddsd.ddpfPixelFormat.dwRGBBitCount = 16; + m_pD3D->EnumZBufferFormats( *pDeviceGUID, EnumZBufferFormatsCallback, + (VOID*)&ddsd.ddpfPixelFormat ); + + if( 0 == ddsd.ddpfPixelFormat.dwSize ) + { + DEBUG_MSG( _T("Device doesn't support requested zbuffer format") ); + return D3DFWERR_NOZBUFFER; + } + } + + // Create and attach a z-buffer + if( FAILED( hr = m_pDD->CreateSurface( &ddsd, &m_pddsDepthBuffer, NULL ) ) ) + { + DEBUG_MSG( _T("Error: Couldn't create a ZBuffer surface") ); + if( hr != DDERR_OUTOFVIDEOMEMORY ) + return D3DFWERR_NOZBUFFER; + DEBUG_MSG( _T("Error: Out of video memory") ); + return DDERR_OUTOFVIDEOMEMORY; + } + + if( FAILED( m_pddsRenderTarget->AddAttachedSurface( m_pddsDepthBuffer ) ) ) + { + DEBUG_MSG( _T("Error: Couldn't attach zbuffer to render surface") ); + return D3DFWERR_NOZBUFFER; + } + + // Finally, this call rebuilds internal structures + if( FAILED( m_pD3DDevice->SetRenderTarget( m_pddsRenderTarget, 0L ) ) ) + { + DEBUG_MSG( _T("Error: SetRenderTarget() failed after attaching zbuffer!") ); + return D3DFWERR_NOZBUFFER; + } + + return S_OK; +} + +// Initializes the sample framework, then calls the app-specific function +// to initialize device specific objects. This code is structured to +// handled any errors that may occur duing initialization. + +HRESULT CD3DApplication::Initialize3DEnvironment() +{ + HRESULT hr; + DDSCAPS2 ddsCaps2; + DWORD dwFrameworkFlags = 0L; + DWORD dwTotal; + DWORD dwFree; + + dwFrameworkFlags |= ( !m_pDeviceInfo->bWindowed ? D3DFW_FULLSCREEN : 0L ); + dwFrameworkFlags |= ( m_pDeviceInfo->bStereo ? D3DFW_STEREO : 0L ); + dwFrameworkFlags |= ( m_bAppUseZBuffer ? D3DFW_ZBUFFER : 0L ); + + // Initialize the D3D framework + if( SUCCEEDED( hr = m_pFramework->Initialize( m_hWnd, + m_pDeviceInfo->pDriverGUID, m_pDeviceInfo->pDeviceGUID, + &m_pDeviceInfo->ddsdFullscreenMode, dwFrameworkFlags ) ) ) + { + m_pDD = m_pFramework->GetDirectDraw(); + m_pD3D = m_pFramework->GetDirect3D(); + m_pD3DDevice = m_pFramework->GetD3DDevice(); + + m_pD3DEngine->SetD3DDevice(m_pD3DDevice); + + m_pddsRenderTarget = m_pFramework->GetRenderSurface(); + + m_ddsdRenderTarget.dwSize = sizeof(m_ddsdRenderTarget); + m_pddsRenderTarget->GetSurfaceDesc( &m_ddsdRenderTarget ); + + // Demande la quantité de mémoire vidéo. + ZeroMemory(&ddsCaps2, sizeof(ddsCaps2)); + ddsCaps2.dwCaps = DDSCAPS_TEXTURE; + dwTotal = 0; + hr = m_pDD->GetAvailableVidMem(&ddsCaps2, &dwTotal, &dwFree); + m_vidMemTotal = dwTotal; + + // Let the app run its startup code which creates the 3d scene. + if( SUCCEEDED( hr = m_pD3DEngine->InitDeviceObjects() ) ) + { +//? CreateZBuffer(m_pDeviceInfo->pDeviceGUID); + return S_OK; + } + else + { + DeleteDeviceObjects(); + m_pFramework->DestroyObjects(); + } + } + + // If we get here, the first initialization passed failed. If that was with a + // hardware device, try again using a software rasterizer instead. + if( m_pDeviceInfo->bHardware ) + { + // Try again with a software rasterizer + DisplayFrameworkError( hr, MSGWARN_SWITCHEDTOSOFTWARE ); + D3DEnum_SelectDefaultDevice( &m_pDeviceInfo, D3DENUM_SOFTWAREONLY ); + return Initialize3DEnvironment(); + } + + return hr; +} + + +// Handles driver, device, and/or mode changes for the app. + +HRESULT CD3DApplication::Change3DEnvironment() +{ +#if 0 + HRESULT hr; + static BOOL bOldWindowedState = TRUE; + static DWORD dwSavedStyle; + static RECT rcSaved; + + // Release all scene objects that will be re-created for the new device + DeleteDeviceObjects(); + + // Release framework objects, so a new device can be created + if( FAILED( hr = m_pFramework->DestroyObjects() ) ) + { + DisplayFrameworkError( hr, MSGERR_APPMUSTEXIT ); + SendMessage( m_hWnd, WM_CLOSE, 0, 0 ); + return hr; + } + + // Check if going from fullscreen to windowed mode, or vice versa. + if( bOldWindowedState != m_pDeviceInfo->bWindowed ) + { + if( m_pDeviceInfo->bWindowed ) + { + // Coming from fullscreen mode, so restore window properties + SetWindowLong( m_hWnd, GWL_STYLE, dwSavedStyle ); + SetWindowPos( m_hWnd, HWND_NOTOPMOST, rcSaved.left, rcSaved.top, + ( rcSaved.right - rcSaved.left ), + ( rcSaved.bottom - rcSaved.top ), SWP_SHOWWINDOW ); + } + else + { + // Going to fullscreen mode, save/set window properties as needed + dwSavedStyle = GetWindowLong( m_hWnd, GWL_STYLE ); + GetWindowRect( m_hWnd, &rcSaved ); + SetWindowLong( m_hWnd, GWL_STYLE, WS_POPUP|WS_SYSMENU|WS_VISIBLE ); + } + + bOldWindowedState = m_pDeviceInfo->bWindowed; + } + + // Inform the framework class of the driver change. It will internally + // re-create valid surfaces, a d3ddevice, etc. + if( FAILED( hr = Initialize3DEnvironment() ) ) + { + DisplayFrameworkError( hr, MSGERR_APPMUSTEXIT ); + SendMessage( m_hWnd, WM_CLOSE, 0, 0 ); + return hr; + } + + return S_OK; +#else + HRESULT hr; + + // Release all scene objects that will be re-created for the new device + DeleteDeviceObjects(); + + // Release framework objects, so a new device can be created + if( FAILED( hr = m_pFramework->DestroyObjects() ) ) + { + DisplayFrameworkError( hr, MSGERR_APPMUSTEXIT ); + SendMessage( m_hWnd, WM_CLOSE, 0, 0 ); + return hr; + } + + if( m_pDeviceInfo->bWindowed ) + { + SetWindowPos(m_hWnd, HWND_NOTOPMOST, 10, 10, WINDOW_DX, WINDOW_DY, SWP_SHOWWINDOW); + } + + // Inform the framework class of the driver change. It will internally + // re-create valid surfaces, a d3ddevice, etc. + if( FAILED( hr = Initialize3DEnvironment() ) ) + { + DisplayFrameworkError( hr, MSGERR_APPMUSTEXIT ); + SendMessage( m_hWnd, WM_CLOSE, 0, 0 ); + return hr; + } + + m_pD3DEngine->ChangeLOD(); + + if( m_pDeviceInfo->bWindowed ) + { + SetNiceMouse(FALSE); // cache la vilaine souris windows + } + + return S_OK; +#endif +} + + + +// Fait évoluer tout le jeu. + +void CD3DApplication::StepSimul(float rTime) +{ + Event event; + + if ( m_pRobotMain == 0 ) return; + + ZeroMemory(&event, sizeof(Event)); + event.event = EVENT_FRAME; // drôle de bug en release "Maximize speed" !!! + event.rTime = rTime; + event.axeX = AxeLimit(m_axeKey.x + m_axeJoy.x); + event.axeY = AxeLimit(m_axeKey.y + m_axeJoy.y); + event.axeZ = AxeLimit(m_axeKey.z + m_axeJoy.z); + event.keyState = m_keyState; + +//?char s[100]; +//?sprintf(s, "StepSimul %.3f\n", event.rTime); +//?OutputDebugString(s); + m_pRobotMain->EventProcess(event); +} + + +// Draws the scene. + +HRESULT CD3DApplication::Render3DEnvironment() +{ + HRESULT hr; + float rTime; + + // Check the cooperative level before rendering + if( FAILED( hr = m_pDD->TestCooperativeLevel() ) ) + { + switch( hr ) + { + case DDERR_EXCLUSIVEMODEALREADYSET: + case DDERR_NOEXCLUSIVEMODE: + OutputDebugString("DDERR_EXCLUSIVEMODEALREADYSET\n"); + // Do nothing because some other app has exclusive mode + return S_OK; + + case DDERR_WRONGMODE: + OutputDebugString("DDERR_WRONGMODE\n"); + // The display mode changed on us. Resize accordingly + if( m_pDeviceInfo->bWindowed ) + return Change3DEnvironment(); + break; + } + return hr; + } + + // Get the relative time, in seconds + rTime = m_pD3DEngine->TimeGet(); + if ( rTime > MAX_STEP ) rTime = MAX_STEP; // jamais plus de 0.5s ! + m_aTime += rTime; + +#if !USE_THREAD + if( FAILED( hr = m_pD3DEngine->FrameMove(rTime) ) ) + return hr; + + // FrameMove (animate) the scene + StepSimul(rTime); +#endif + + // Render the scene. + if( FAILED( hr = m_pD3DEngine->Render() ) ) + return hr; + + DrawSuppl(); + + // Show the frame rate, etc. + if( m_bShowStats ) + ShowStats(); + + // Show the frame on the primary surface. + if( FAILED( hr = m_pFramework->ShowFrame() ) ) + { + if( DDERR_SURFACELOST != hr ) + return hr; + + m_pFramework->RestoreSurfaces(); + m_pD3DEngine->RestoreSurfaces(); + } + + return S_OK; +} + + +// Cleanup scene objects + +VOID CD3DApplication::Cleanup3DEnvironment() +{ + m_bActive = FALSE; + m_bReady = FALSE; + + if( m_pFramework ) + { + DeleteDeviceObjects(); + SAFE_DELETE( m_pFramework ); + + m_pD3DEngine->FinalCleanup(); + } + + D3DEnum_FreeResources(); +//? FreeDirectInput(); +} + +// Called when the app is exitting, or the device is being changed, +// this function deletes any device dependant objects. + +VOID CD3DApplication::DeleteDeviceObjects() +{ + if( m_pFramework ) + { + m_pD3DEngine->DeleteDeviceObjects(); + SAFE_RELEASE( m_pddsDepthBuffer ); + } +} + + + +// Called in to toggle the pause state of the app. This function +// brings the GDI surface to the front of the display, so drawing +// output like message boxes and menus may be displayed. + +VOID CD3DApplication::Pause( BOOL bPause ) +{ + static DWORD dwAppPausedCount = 0L; + + dwAppPausedCount += ( bPause ? +1 : -1 ); + m_bReady = ( dwAppPausedCount ? FALSE : TRUE ); + + // Handle the first pause request (of many, nestable pause requests) + if( bPause && ( 1 == dwAppPausedCount ) ) + { + // Get a surface for the GDI + if( m_pFramework ) + m_pFramework->FlipToGDISurface( TRUE ); + + // Stop the scene from animating + m_pD3DEngine->TimeEnterGel(); + } + + if( 0 == dwAppPausedCount ) + { + // Restart the scene + m_pD3DEngine->TimeExitGel(); + } +} + + +// Called when the app receives a PBT_APMQUERYSUSPEND message, meaning +// the computer is about to be suspended. At this point, the app should +// save any data for open network connections, files, etc.., and prepare +// to go into a suspended mode. + +LRESULT CD3DApplication::OnQuerySuspend( DWORD dwFlags ) +{ + OutputDebugString("OnQuerySuspend\n"); + Pause(TRUE); + return TRUE; +} + + +// Called when the app receives a PBT_APMRESUMESUSPEND message, meaning +// the computer has just resumed from a suspended state. At this point, +// the app should recover any data, network connections, files, etc.., +// and resume running from when the app was suspended. + +LRESULT CD3DApplication::OnResumeSuspend( DWORD dwData ) +{ + OutputDebugString("OnResumeSuspend\n"); + Pause(FALSE); + return TRUE; +} + + +// Dessine tous les éléments graphiques supplémentaires. + +void CD3DApplication::DrawSuppl() +{ + HDC hDC; + FPOINT p1, p2; + POINT list[3]; + RECT rect; + HPEN hPen; + HGDIOBJ old; + FPOINT pos; + float d; + int nbOut; + + if ( FAILED(m_pddsRenderTarget->GetDC(&hDC)) ) return; + + // Affiche le rectangle de sélection. + if ( m_pD3DEngine->GetHilite(p1, p2) ) + { + nbOut = 0; + if ( p1.x < 0.0f || p1.x > 1.0f ) nbOut ++; + if ( p1.y < 0.0f || p1.y > 1.0f ) nbOut ++; + if ( p2.x < 0.0f || p2.x > 1.0f ) nbOut ++; + if ( p2.y < 0.0f || p2.y > 1.0f ) nbOut ++; + if ( nbOut <= 2 ) + { +#if 0 + time = Mod(m_aTime, 0.5f); + if ( time < 0.25f ) d = time*4.0f; + else d = (2.0f-time*4.0f); +#endif +#if 0 + time = Mod(m_aTime, 0.5f); + if ( time < 0.4f ) d = time/0.4f; + else d = 1.0f-(time-0.4f)/0.1f; +#endif +#if 1 + d = 0.5f+sinf(m_aTime*6.0f)*0.5f; +#endif + d *= (p2.x-p1.x)*0.1f; + p1.x += d; + p1.y += d; + p2.x -= d; + p2.y -= d; + + hPen = CreatePen(PS_SOLID, 1, RGB(255,255,0)); // jaune + old = SelectObject(hDC, hPen); + + rect.left = (int)(p1.x*m_ddsdRenderTarget.dwWidth); + rect.right = (int)(p2.x*m_ddsdRenderTarget.dwWidth); + rect.top = (int)((1.0f-p2.y)*m_ddsdRenderTarget.dwHeight); + rect.bottom = (int)((1.0f-p1.y)*m_ddsdRenderTarget.dwHeight); + + list[0].x = rect.left; + list[0].y = rect.top+(rect.bottom-rect.top)/5; + list[1].x = rect.left; + list[1].y = rect.top; + list[2].x = rect.left+(rect.right-rect.left)/5; + list[2].y = rect.top; + Polyline(hDC, list, 3); + + list[0].x = rect.right; + list[0].y = rect.top+(rect.bottom-rect.top)/5; + list[1].x = rect.right; + list[1].y = rect.top; + list[2].x = rect.right+(rect.left-rect.right)/5; + list[2].y = rect.top; + Polyline(hDC, list, 3); + + list[0].x = rect.left; + list[0].y = rect.bottom+(rect.top-rect.bottom)/5; + list[1].x = rect.left; + list[1].y = rect.bottom; + list[2].x = rect.left+(rect.right-rect.left)/5; + list[2].y = rect.bottom; + Polyline(hDC, list, 3); + + list[0].x = rect.right; + list[0].y = rect.bottom+(rect.top-rect.bottom)/5; + list[1].x = rect.right; + list[1].y = rect.bottom; + list[2].x = rect.right+(rect.left-rect.right)/5; + list[2].y = rect.bottom; + Polyline(hDC, list, 3); + + if ( old != 0 ) SelectObject(hDC, old); + DeleteObject(hPen); + } + } + + m_pddsRenderTarget->ReleaseDC(hDC); +} + +// Shows frame rate and dimensions of the rendering device. + +VOID CD3DApplication::ShowStats() +{ + static FLOAT fFPS = 0.0f; + static FLOAT fLastTime = 0.0f; + static DWORD dwFrames = 0L; + + // Keep track of the time lapse and frame count + FLOAT fTime = timeGetTime() * 0.001f; // Get current time in seconds + ++dwFrames; + + // Update the frame rate once per second + if( fTime - fLastTime > 1.0f ) + { + fFPS = dwFrames / (fTime - fLastTime); + fLastTime = fTime; + dwFrames = 0L; + } + + int t = m_pD3DEngine->RetStatisticTriangle(); + + // Setup the text buffer to write out dimensions + TCHAR buffer[100]; + sprintf( buffer, _T("%7.02f fps T=%d (%dx%dx%d)"), fFPS, t, + m_ddsdRenderTarget.dwWidth, m_ddsdRenderTarget.dwHeight, + m_ddsdRenderTarget.ddpfPixelFormat.dwRGBBitCount ); + OutputText( 400, 2, buffer ); + + int x, y, i; + if ( m_pD3DEngine->GetSpriteCoord(x, y) ) + { + OutputText( x, y, "+" ); + } + + for ( i=0 ; i<10 ; i++ ) + { + char* info = m_pD3DEngine->RetInfoText(i); + x = 50; + y = m_ddsdRenderTarget.dwHeight-20-i*20; + OutputText( x, y, info ); + } +} + + +// Draws text on the window. + +VOID CD3DApplication::OutputText( DWORD x, DWORD y, TCHAR* str ) +{ + HDC hDC; + + // Get a DC for the surface. Then, write out the buffer + if( m_pddsRenderTarget ) + { + if( SUCCEEDED( m_pddsRenderTarget->GetDC(&hDC) ) ) + { + SetTextColor( hDC, RGB(255,255,0) ); + SetBkMode( hDC, TRANSPARENT ); + ExtTextOut( hDC, x, y, 0, NULL, str, lstrlen(str), NULL ); + m_pddsRenderTarget->ReleaseDC(hDC); + } + } +} + + + + +// Defines a function that allocates memory for and initializes +// members within a BITMAPINFOHEADER structure + +PBITMAPINFO CD3DApplication::CreateBitmapInfoStruct(HBITMAP hBmp) +{ + BITMAP bmp; + PBITMAPINFO pbmi; + WORD cClrBits; + + // Retrieve the bitmap's color format, width, and height. + if ( !GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp) ) + return 0; + + // Convert the color format to a count of bits. + cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); + + if ( cClrBits == 1 ) cClrBits = 1; + else if ( cClrBits <= 4 ) cClrBits = 4; + else if ( cClrBits <= 8 ) cClrBits = 8; + else if ( cClrBits <= 16 ) cClrBits = 16; + else if ( cClrBits <= 24 ) cClrBits = 24; + else cClrBits = 32; + + // Allocate memory for the BITMAPINFO structure. (This structure + // contains a BITMAPINFOHEADER structure and an array of RGBQUAD data + // structures.) + if ( cClrBits != 24 ) + { + pbmi = (PBITMAPINFO)LocalAlloc(LPTR, + sizeof(BITMAPINFOHEADER) + + sizeof(RGBQUAD) * (2^cClrBits)); + } + // There is no RGBQUAD array for the 24-bit-per-pixel format. + else + { + pbmi = (PBITMAPINFO)LocalAlloc(LPTR, + sizeof(BITMAPINFOHEADER)); + } + + // Initialize the fields in the BITMAPINFO structure. + pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmi->bmiHeader.biWidth = bmp.bmWidth; + pbmi->bmiHeader.biHeight = bmp.bmHeight; + pbmi->bmiHeader.biPlanes = bmp.bmPlanes; + pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; + if ( cClrBits < 24 ) + pbmi->bmiHeader.biClrUsed = 2^cClrBits; + + // If the bitmap is not compressed, set the BI_RGB flag. + pbmi->bmiHeader.biCompression = BI_RGB; + + // Compute the number of bytes in the array of color + // indices and store the result in biSizeImage. + pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8 + * pbmi->bmiHeader.biHeight + * cClrBits; + + // Set biClrImportant to 0, indicating that all of the + // device colors are important. + pbmi->bmiHeader.biClrImportant = 0; + + return pbmi; +} + +// Defines a function that initializes the remaining structures, +// retrieves the array of palette indices, opens the file, copies +// the data, and closes the file. + +BOOL CD3DApplication::CreateBMPFile(LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC) +{ + FILE* file; // file handle + BITMAPFILEHEADER hdr; // bitmap file-header + PBITMAPINFOHEADER pbih; // bitmap info-header + LPBYTE lpBits; // memory pointer + DWORD dwTotal; // total count of bytes + + pbih = (PBITMAPINFOHEADER)pbi; + lpBits = (LPBYTE)GlobalAlloc(GMEM_FIXED, pbih->biSizeImage); + if ( !lpBits ) return FALSE; + + // Retrieve the color table (RGBQUAD array) and the bits + // (array of palette indices) from the DIB. + if ( !GetDIBits(hDC, hBMP, 0, (WORD)pbih->biHeight, + lpBits, pbi, DIB_RGB_COLORS) ) + return FALSE; + + // Create the .BMP file. + file = fopen(pszFile, "wb"); + if ( file == NULL ) return FALSE; + + hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M" + + // Compute the size of the entire file. + hdr.bfSize = (DWORD)(sizeof(BITMAPFILEHEADER) + + pbih->biSize + pbih->biClrUsed + * sizeof(RGBQUAD) + pbih->biSizeImage); + + hdr.bfReserved1 = 0; + hdr.bfReserved2 = 0; + + // Compute the offset to the array of color indices. + hdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + + pbih->biSize + pbih->biClrUsed + * sizeof (RGBQUAD); + + // Copy the BITMAPFILEHEADER into the .BMP file. + fwrite(&hdr, sizeof(BITMAPFILEHEADER), 1, file); + + // Copy the BITMAPINFOHEADER and RGBQUAD array into the file. + fwrite(pbih, sizeof(BITMAPINFOHEADER)+pbih->biClrUsed*sizeof(RGBQUAD), 1, file); + + // Copy the array of color indices into the .BMP file. + dwTotal = pbih->biSizeImage; + fwrite(lpBits, dwTotal, 1, file); + + // Close the .BMP file. + fclose(file); + + // Free memory. + GlobalFree((HGLOBAL)lpBits); + return TRUE; +} + +// Ecrit un fichier .BMP copie d'écran. + +BOOL CD3DApplication::WriteScreenShot(char *filename, int width, int height) +{ + D3DVIEWPORT7 vp; + HDC hDC; + HDC hDCImage; + HBITMAP hb; + PBITMAPINFO info; + int dx, dy; + + m_pD3DDevice->GetViewport(&vp); + dx = vp.dwWidth; + dy = vp.dwHeight; + + if ( FAILED(m_pddsRenderTarget->GetDC(&hDC)) ) return FALSE; + + hDCImage = CreateCompatibleDC(hDC); + if ( hDCImage == 0 ) + { + m_pddsRenderTarget->ReleaseDC(hDC); + return FALSE; + } + + hb = CreateCompatibleBitmap(hDC, width, height); + if ( hb == 0 ) + { + DeleteDC(hDCImage); + m_pddsRenderTarget->ReleaseDC(hDC); + return FALSE; + } + + SelectObject(hDCImage, hb); + StretchBlt(hDCImage, 0, 0, width, height, hDC, 0, 0, dx, dy, SRCCOPY); + + info = CreateBitmapInfoStruct(hb); + if ( info == 0 ) + { + DeleteObject(hb); + DeleteDC(hDCImage); + m_pddsRenderTarget->ReleaseDC(hDC); + return FALSE; + } + + CreateBMPFile(filename, info, hb, hDCImage); + + DeleteObject(hb); + DeleteDC(hDCImage); + m_pddsRenderTarget->ReleaseDC(hDC); + return TRUE; +} + + +// Initialise un hDC sur la surface de rendu. + +BOOL CD3DApplication::GetRenderDC(HDC &hDC) +{ + if ( FAILED(m_pddsRenderTarget->GetDC(&hDC)) ) return FALSE; + return TRUE; +} + +// Libère le hDC sur la surface de rendu. + +BOOL CD3DApplication::ReleaseRenderDC(HDC &hDC) +{ + m_pddsRenderTarget->ReleaseDC(hDC); + return TRUE; +} + + + + +// Effectue la liste de tous les devices graphiques disponibles. +// Pour le device sélectionné, donne la liste des modes plein écran +// possibles. +// buf* --> nom1<0> nom2<0> <0> + +BOOL CD3DApplication::EnumDevices(char *bufDevices, int lenDevices, + char *bufModes, int lenModes, + int &totalDevices, int &selectDevices, + int &totalModes, int &selectModes) +{ + D3DEnum_DeviceInfo* pDeviceList; + D3DEnum_DeviceInfo* pDevice; + DDSURFACEDESC2* pddsdMode; + DWORD numDevices, device, mode; + int len; + char text[100]; + + D3DEnum_GetDevices(&pDeviceList, &numDevices); + + selectDevices = -1; + selectModes = -1; + totalModes = 0; + for( device=0 ; devicestrDesc)+1; + if ( len >= lenDevices ) break; // bufDevices plein ! + strcpy(bufDevices, pDevice->strDesc); + bufDevices += len; + lenDevices -= len; + + if ( pDevice == m_pDeviceInfo ) // select device ? + { + selectDevices = device; + + for( mode=0 ; modedwNumModes ; mode++ ) + { + pddsdMode = &pDevice->pddsdModes[mode]; + + sprintf(text, "%ld x %ld x %ld", + pddsdMode->dwWidth, + pddsdMode->dwHeight, + pddsdMode->ddpfPixelFormat.dwRGBBitCount); + + len = strlen(text)+1; + if ( len >= lenModes ) break; // bufModes plein ! + strcpy(bufModes, text); + bufModes += len; + lenModes -= len; + + if ( mode == m_pDeviceInfo->dwCurrentMode ) // select mode ? + { + selectModes = mode; + } + } + bufModes[0] = 0; + totalModes = pDevice->dwNumModes; + } + } + bufDevices[0] = 0; + totalDevices = numDevices; + + return TRUE; +} + +// Indique si on est en mode plein écran. + +BOOL CD3DApplication::RetFullScreen() +{ + return !m_pDeviceInfo->bWindowed; +} + +// Change le mode graphique. + +BOOL CD3DApplication::ChangeDevice(char *deviceName, char *modeName, + BOOL bFull) +{ + D3DEnum_DeviceInfo* pDeviceList; + D3DEnum_DeviceInfo* pDevice; + DDSURFACEDESC2* pddsdMode; + DWORD numDevices, device, mode; + HRESULT hr; + char text[100]; + + D3DEnum_GetDevices(&pDeviceList, &numDevices); + + for( device=0 ; devicestrDesc, deviceName) == 0 ) // device found ? + { + for( mode=0 ; modedwNumModes ; mode++ ) + { + pddsdMode = &pDevice->pddsdModes[mode]; + + sprintf(text, "%ld x %ld x %ld", + pddsdMode->dwWidth, + pddsdMode->dwHeight, + pddsdMode->ddpfPixelFormat.dwRGBBitCount); + + if ( strcmp(text, modeName) == 0 ) // mode found ? + { + m_pDeviceInfo = pDevice; + pDevice->bWindowed = !bFull; + pDevice->dwCurrentMode = mode; + pDevice->ddsdFullscreenMode = pDevice->pddsdModes[mode]; + + m_bReady = FALSE; + + if ( FAILED( hr = Change3DEnvironment() ) ) + { + return FALSE; + } + + SetProfileString("Device", "Name", deviceName); + SetProfileString("Device", "Mode", modeName); + SetProfileInt("Device", "FullScreen", bFull); + m_bReady = TRUE; + return TRUE; + } + } + } + } + + return FALSE; +} + + + +// Displays error messages in a message box. + +VOID CD3DApplication::DisplayFrameworkError( HRESULT hr, DWORD dwType ) +{ + TCHAR strMsg[512]; + + switch( hr ) + { + case D3DENUMERR_ENGINE: + lstrcpy( strMsg, _T("Could not create 3D Engine application!") ); + break; + case D3DENUMERR_ROBOT: + lstrcpy( strMsg, _T("Could not create Robot application!") ); + break; + case D3DENUMERR_NODIRECTDRAW: + lstrcpy( strMsg, _T("Could not create DirectDraw!") ); + break; + case D3DENUMERR_NOCOMPATIBLEDEVICES: + lstrcpy( strMsg, _T("Could not find any compatible Direct3D\n" + "devices.") ); + break; + case D3DENUMERR_SUGGESTREFRAST: + lstrcpy( strMsg, _T("Could not find any compatible devices.\n\n" + "Try enabling the reference rasterizer using\n" + "EnableRefRast.reg.") ); + break; + case D3DENUMERR_ENUMERATIONFAILED: + lstrcpy( strMsg, _T("Enumeration failed. Your system may be in an\n" + "unstable state and need to be rebooted") ); + break; + case D3DFWERR_INITIALIZATIONFAILED: + lstrcpy( strMsg, _T("Generic initialization error.\n\nEnable " + "debug output for detailed information.") ); + break; + case D3DFWERR_NODIRECTDRAW: + lstrcpy( strMsg, _T("No DirectDraw") ); + break; + case D3DFWERR_NODIRECT3D: + lstrcpy( strMsg, _T("No Direct3D") ); + break; + case D3DFWERR_INVALIDMODE: + lstrcpy( strMsg, _T("COLOBOT requires a 16-bit (or higher) " + "display mode\nto run in a window.\n\nPlease " + "switch your desktop settings accordingly.") ); + break; + case D3DFWERR_COULDNTSETCOOPLEVEL: + lstrcpy( strMsg, _T("Could not set Cooperative Level") ); + break; + case D3DFWERR_NO3DDEVICE: + lstrcpy( strMsg, _T("Could not create the Direct3DDevice object.") ); + + if( MSGWARN_SWITCHEDTOSOFTWARE == dwType ) + lstrcat( strMsg, _T("\nThe 3D hardware chipset may not support" + "\nrendering in the current display mode.") ); + break; + case D3DFWERR_NOZBUFFER: + lstrcpy( strMsg, _T("No ZBuffer") ); + break; + case D3DFWERR_INVALIDZBUFFERDEPTH: + lstrcpy( strMsg, _T("Invalid Z-buffer depth. Try switching modes\n" + "from 16- to 32-bit (or vice versa)") ); + break; + case D3DFWERR_NOVIEWPORT: + lstrcpy( strMsg, _T("No Viewport") ); + break; + case D3DFWERR_NOPRIMARY: + lstrcpy( strMsg, _T("No primary") ); + break; + case D3DFWERR_NOCLIPPER: + lstrcpy( strMsg, _T("No Clipper") ); + break; + case D3DFWERR_BADDISPLAYMODE: + lstrcpy( strMsg, _T("Bad display mode") ); + break; + case D3DFWERR_NOBACKBUFFER: + lstrcpy( strMsg, _T("No backbuffer") ); + break; + case D3DFWERR_NONZEROREFCOUNT: + lstrcpy( strMsg, _T("A DDraw object has a non-zero reference\n" + "count (meaning it was not properly cleaned up)." ) ); + break; + case D3DFWERR_NORENDERTARGET: + lstrcpy( strMsg, _T("No render target") ); + break; + case E_OUTOFMEMORY: + lstrcpy( strMsg, _T("Not enough memory!") ); + break; + case DDERR_OUTOFVIDEOMEMORY: + lstrcpy( strMsg, _T("There was insufficient video memory " + "to use the\nhardware device.") ); + break; + default: + lstrcpy( strMsg, _T("Generic application error.\n\nEnable " + "debug output for detailed information.") ); + } + + if( MSGERR_APPMUSTEXIT == dwType ) + { + lstrcat( strMsg, _T("\n\nCOLOBOT will now exit.") ); + MessageBox( NULL, strMsg, m_strWindowTitle, MB_ICONERROR|MB_OK ); + } + else + { + if( MSGWARN_SWITCHEDTOSOFTWARE == dwType ) + lstrcat( strMsg, _T("\n\nSwitching to software rasterizer.") ); + MessageBox( NULL, strMsg, m_strWindowTitle, MB_ICONWARNING|MB_OK ); + } +} + + diff --git a/src/d3dapp.h b/src/d3dapp.h new file mode 100644 index 00000000..4808e683 --- /dev/null +++ b/src/d3dapp.h @@ -0,0 +1,153 @@ +// D3DApp.h + + +#ifndef _D3DAPP_H +#define _D3DAPP_H + +#define D3D_OVERLOADS + +#include +#include "D3DFrame.h" +#include "D3DEnum.h" +#include "D3DUtil.h" +#include "D3DRes.h" + + + +class CInstanceManager; +class CEvent; +class CD3DEngine; +class CRobotMain; +class CSound; + +enum D3DMouse; +enum Error; + + + +class CD3DApplication +{ +public: + CD3DApplication(); + ~CD3DApplication(); + +protected: + LRESULT OnQuerySuspend( DWORD dwFlags ); + LRESULT OnResumeSuspend( DWORD dwData ); + +public: + Error RegQuery(); + Error AudioQuery(); + Error CheckMistery(char *strCmdLine); + int GetVidMemTotal(); + BOOL IsVideo8MB(); + BOOL IsVideo32MB(); + HRESULT Create( HINSTANCE, TCHAR* ); + INT Run(); + LRESULT MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + VOID Pause( BOOL bPause ); + FPOINT ConvPosToInterface(HWND hWnd, LPARAM lParam); + void SetMousePos(FPOINT pos); + void StepSimul(float rTime); + char* RetCDpath(); + + void SetShowStat(BOOL bShow); + BOOL RetShowStat(); + void SetDebugMode(BOOL bMode); + BOOL RetDebugMode(); + BOOL RetSetupMode(); + + BOOL EnumDevices(char *bufDevices, int lenDevices, char *bufModes, int lenModes, int &totalDevices, int &selectDevices, int &totalModes, int &selectModes); + BOOL RetFullScreen(); + BOOL ChangeDevice(char *device, char *mode, BOOL bFull); + + void FlushPressKey(); + void ResetKey(); + void SetKey(int keyRank, int option, int key); + int RetKey(int keyRank, int option); + + void SetJoystick(BOOL bEnable); + BOOL RetJoystick(); + + void SetMouseType(D3DMouse type); + void SetNiceMouse(BOOL bNice); + BOOL RetNiceMouse(); + BOOL RetNiceMouseCap(); + + BOOL WriteScreenShot(char *filename, int width, int height); + + BOOL GetRenderDC(HDC &hDC); + BOOL ReleaseRenderDC(HDC &hDC); + PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp); + BOOL CreateBMPFile(LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC); + +protected: + HRESULT ConfirmDevice( DDCAPS* pddDriverCaps, D3DDEVICEDESC7* pd3dDeviceDesc ); + HRESULT Initialize3DEnvironment(); + HRESULT Change3DEnvironment(); + HRESULT CreateZBuffer(GUID* pDeviceGUID); + HRESULT Render3DEnvironment(); + VOID Cleanup3DEnvironment(); + VOID DeleteDeviceObjects(); + VOID DisplayFrameworkError( HRESULT, DWORD ); + + void InitText(); + void DrawSuppl(); + VOID ShowStats(); + VOID OutputText( DWORD x, DWORD y, TCHAR* str ); + +protected: + CInstanceManager* m_iMan; + CEvent* m_event; + + HINSTANCE m_instance; + HWND m_hWnd; + D3DEnum_DeviceInfo* m_pDeviceInfo; + LPDIRECTDRAW7 m_pDD; + LPDIRECT3D7 m_pD3D; + LPDIRECT3DDEVICE7 m_pD3DDevice; + LPDIRECTDRAWSURFACE7 m_pddsRenderTarget; + DDSURFACEDESC2 m_ddsdRenderTarget; + LPDIRECTDRAWSURFACE7 m_pddsDepthBuffer; + + HANDLE m_thread; + DWORD m_threadId; + + char m_CDpath[100]; + + CD3DFramework7* m_pFramework; + BOOL m_bActive; + BOOL m_bActivateApp; + BOOL m_bReady; + BOOL m_bJoystick; + + DWORD m_vidMemTotal; + TCHAR* m_strWindowTitle; + BOOL m_bAppUseZBuffer; + BOOL m_bAppUseStereo; + BOOL m_bShowStats; + BOOL m_bDebugMode; + BOOL m_bAudioState; + BOOL m_bAudioTrack; + BOOL m_bNiceMouse; + BOOL m_bSetupMode; + HRESULT (*m_fnConfirmDevice)(DDCAPS*, D3DDEVICEDESC7*); + +public: + CD3DEngine* m_pD3DEngine; + CRobotMain* m_pRobotMain; + CSound* m_pSound; + + int m_keyState; + D3DVECTOR m_axeKey; + D3DVECTOR m_axeJoy; + BOOL m_bJoyButton[32]; + FPOINT m_mousePos; + DWORD m_mshMouseWheel; + + float m_aTime; + DWORD m_key[50][2]; +}; + + +#endif // _D3DAPP_H diff --git a/src/d3dengine.cpp b/src/d3dengine.cpp new file mode 100644 index 00000000..20bff340 --- /dev/null +++ b/src/d3dengine.cpp @@ -0,0 +1,5711 @@ +// D3DEngine.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include + +#include "struct.h" +#include "D3DApp.h" +#include "D3DTextr.h" +#include "D3DUtil.h" +#include "D3DMath.h" +#include "D3DEngine.h" +#include "language.h" +#include "iman.h" +#include "event.h" +#include "profile.h" +#include "math3d.h" +#include "object.h" +#include "interface.h" +#include "light.h" +#include "text.h" +#include "particule.h" +#include "terrain.h" +#include "water.h" +#include "cloud.h" +#include "blitz.h" +#include "planet.h" +#include "sound.h" + + + +#define SIZEBLOC_TEXTURE 50 +#define SIZEBLOC_TRANSFORM 100 +#define SIZEBLOC_MINMAX 5 +#define SIZEBLOC_LIGHT 10 +#define SIZEBLOC_MATERIAL 100 +#define SIZEBLOC_TRIANGLE 200 + + + +#if 0 +static int debug_blend1 = 1; +static int debug_blend2 = 3; +static int debug_blend3 = 8; +static int debug_blend4 = 0; + +static int table_blend[13] = +{ + D3DBLEND_ZERO, // 0 + D3DBLEND_ONE, // 1 + D3DBLEND_SRCCOLOR, // 2 + D3DBLEND_INVSRCCOLOR, // 3 + D3DBLEND_SRCALPHA, // 4 + D3DBLEND_INVSRCALPHA, // 5 + D3DBLEND_DESTALPHA, // 6 + D3DBLEND_INVDESTALPHA, // 7 + D3DBLEND_DESTCOLOR, // 8 + D3DBLEND_INVDESTCOLOR, // 9 + D3DBLEND_SRCALPHASAT, // 10 + D3DBLEND_BOTHSRCALPHA, // 11 + D3DBLEND_BOTHINVSRCALPHA, // 12 +}; +#endif + +static int s_resol = 0; + + + +// Converts a FLOAT to a DWORD for use in SetRenderState() calls. + +inline DWORD F2DW( FLOAT f ) +{ + return *((DWORD*)&f); +} + + + + +// Application constructor. Sets attributes for the app. + +CD3DEngine::CD3DEngine(CInstanceManager *iMan, CD3DApplication *app) +{ + int i; + + m_iMan = iMan; + m_iMan->AddInstance(CLASS_ENGINE, this); + m_app = app; + + m_light = new CLight(m_iMan, this); + m_text = new CText(m_iMan, this); + m_particule = new CParticule(m_iMan, this); + m_water = new CWater(m_iMan, this); + m_cloud = new CCloud(m_iMan, this); + m_blitz = new CBlitz(m_iMan, this); + m_planet = new CPlanet(m_iMan, this); + m_pD3DDevice = 0; + m_sound = 0; + m_terrain = 0; + + m_dim.x = 640; + m_dim.y = 480; + m_lastDim = m_dim; + m_focus = 0.75f; + m_baseTime = 0; + m_lastTime = 0; + m_absTime = 0.0f; + m_rankView = 0; + m_ambiantColor[0] = 0x80808080; + m_ambiantColor[1] = 0x80808080; + m_fogColor[0] = 0xffffffff; // white + m_fogColor[1] = 0xffffffff; // white + m_deepView[0] = 1000.0f; + m_deepView[1] = 1000.0f; + m_fogStart[0] = 0.75f; + m_fogStart[1] = 0.75f; + m_waterAddColor.r = 0.0f; + m_waterAddColor.g = 0.0f; + m_waterAddColor.b = 0.0f; + m_waterAddColor.a = 0.0f; + m_bPause = FALSE; + m_bRender = TRUE; + m_bMovieLock = FALSE; + m_bShadow = TRUE; + m_bGroundSpot = TRUE; + m_bDirty = TRUE; + m_bFog = TRUE; + m_speed = 1.0f; + m_secondTexNum = 0; + m_eyeDirH = 0.0f; + m_eyeDirV = 0.0f; + m_backgroundName[0] = 0; // pas d'image de fond + m_backgroundColorUp = 0; + m_backgroundColorDown = 0; + m_backgroundCloudUp = 0; + m_backgroundCloudDown = 0; + m_bBackgroundFull = FALSE; + m_bBackgroundQuarter = FALSE; + m_bOverFront = TRUE; + m_overColor = 0; + m_overMode = D3DSTATETCb; + m_frontsizeName[0] = 0; // pas d'image de devant + m_hiliteRank[0] = -1; // liste vide + m_mousePos = FPOINT(0.5f, 0.5f); + m_mouseType = D3DMOUSENORM; + m_bMouseHide = FALSE; + m_imageSurface = 0; + m_imageCopy = 0; + m_eyePt = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_lookatPt = D3DVECTOR(0.0f, 0.0f, 1.0f); + m_bDrawWorld = TRUE; + m_bDrawFront = FALSE; + m_limitLOD[0] = 100.0f; + m_limitLOD[1] = 200.0f; + m_particuleDensity = 1.0f; + m_clippingDistance = 1.0f; + m_lastClippingDistance = m_clippingDistance; + m_objectDetail = 1.0f; + m_lastObjectDetail = m_objectDetail; + m_terrainVision = 1000.0f; + m_gadgetQuantity = 1.0f; + m_textureQuality = 1; + m_bTotoMode = TRUE; + m_bLensMode = TRUE; + m_bWaterMode = TRUE; + m_bSkyMode = TRUE; + m_bBackForce = TRUE; + m_bPlanetMode = TRUE; + m_bLightMode = TRUE; + m_bEditIndentMode = TRUE; + m_editIndentValue = 4; + m_tracePrecision = 1.0f; + + m_alphaMode = 1; + if ( GetProfileInt("Engine", "AlphaMode", i) ) + { + m_alphaMode = i; + } + + if ( GetProfileInt("Engine", "StateColor", i) && i != -1 ) + { + m_bForceStateColor = TRUE; + m_bStateColor = i; + } + else + { + m_bForceStateColor = FALSE; + m_bStateColor = FALSE; + } + + m_blackSrcBlend[0] = 0; + m_blackDestBlend[0] = 0; + m_whiteSrcBlend[0] = 0; + m_whiteDestBlend[0] = 0; + m_diffuseSrcBlend[0] = 0; + m_diffuseDestBlend[0] = 0; + m_alphaSrcBlend[0] = 0; + m_alphaDestBlend[0] = 0; + + if ( GetProfileInt("Engine", "BlackSrcBlend", i) ) m_blackSrcBlend[0] = i; + if ( GetProfileInt("Engine", "BlackDestBlend", i) ) m_blackDestBlend[0] = i; + if ( GetProfileInt("Engine", "WhiteSrcBlend", i) ) m_whiteSrcBlend[0] = i; + if ( GetProfileInt("Engine", "WhiteDestBlend", i) ) m_whiteDestBlend[0] = i; + if ( GetProfileInt("Engine", "DiffuseSrcBlend", i) ) m_diffuseSrcBlend[0] = i; + if ( GetProfileInt("Engine", "DiffuseDestBlend", i) ) m_diffuseDestBlend[0] = i; + if ( GetProfileInt("Engine", "AlphaSrcBlend", i) ) m_alphaSrcBlend[0] = i; + if ( GetProfileInt("Engine", "AlphaDestBlend", i) ) m_alphaDestBlend[0] = i; + + m_bUpdateGeometry = FALSE; + + for ( i=0 ; i<10 ; i++ ) + { + m_infoText[i][0] = 0; + } + + m_objectPointer = 0; + MemSpace1(m_objectPointer, 0); + + m_objectParam = (D3DObject*)malloc(sizeof(D3DObject)*D3DMAXOBJECT); + ZeroMemory(m_objectParam, sizeof(D3DObject)*D3DMAXOBJECT); + m_objectParamTotal = 0; + + m_shadow = (D3DShadow*)malloc(sizeof(D3DShadow)*D3DMAXSHADOW); + ZeroMemory(m_shadow, sizeof(D3DShadow)*D3DMAXSHADOW); + m_shadowTotal = 0; + + m_groundSpot = (D3DGroundSpot*)malloc(sizeof(D3DGroundSpot)*D3DMAXGROUNDSPOT); + ZeroMemory(m_groundSpot, sizeof(D3DGroundSpot)*D3DMAXGROUNDSPOT); + + ZeroMemory(&m_groundMark, sizeof(D3DGroundMark)); + + D3DTextr_SetTexturePath("textures\\"); +} + +// Application destructor. Free memory. + +CD3DEngine::~CD3DEngine() +{ + D3DObjLevel1* p1; + D3DObjLevel2* p2; + D3DObjLevel3* p3; + D3DObjLevel4* p4; + D3DObjLevel5* p5; + D3DObjLevel6* p6; + int l1, l2, l3, l4, l5; + + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; + free(p6); + } + free(p5); + } + free(p4); + } + free(p3); + } + free(p2); + } + free(p1); + + delete m_light; + delete m_particule; + delete m_water; + delete m_cloud; + delete m_blitz; + delete m_planet; +} + + + +void CD3DEngine::SetD3DDevice(LPDIRECT3DDEVICE7 device) +{ + D3DDEVICEDESC7 ddDesc; + + m_pD3DDevice = device; + m_light->SetD3DDevice(device); + m_text->SetD3DDevice(device); + m_particule->SetD3DDevice(device); + + if ( !m_bForceStateColor ) + { + m_pD3DDevice->GetCaps(&ddDesc); + if( ddDesc.dpcTriCaps.dwTextureBlendCaps & D3DPTBLENDCAPS_ADD ) + { + m_bStateColor = TRUE; + } + else + { + m_bStateColor = FALSE; + } + } + + m_blackSrcBlend[1] = D3DBLEND_ONE; // = 2 + m_blackDestBlend[1] = D3DBLEND_INVSRCCOLOR; // = 4 + m_whiteSrcBlend[1] = D3DBLEND_DESTCOLOR; // = 9 + m_whiteDestBlend[1] = D3DBLEND_ZERO; // = 1 + m_diffuseSrcBlend[1] = D3DBLEND_SRCALPHA; // = 5 + m_diffuseDestBlend[1] = D3DBLEND_DESTALPHA; // = 7 + m_alphaSrcBlend[1] = D3DBLEND_ONE; // = 2 + m_alphaDestBlend[1] = D3DBLEND_INVSRCCOLOR; // = 4 + +//? if ( !m_bStateColor ) m_whiteDestBlend[1] = D3DBLEND_INVSRCALPHA; // = 6 + + if ( m_blackSrcBlend[0] ) m_blackSrcBlend[1] = m_blackSrcBlend[0]; + if ( m_blackDestBlend[0] ) m_blackDestBlend[1] = m_blackDestBlend[0]; + if ( m_whiteSrcBlend[0] ) m_whiteSrcBlend[1] = m_whiteSrcBlend[0]; + if ( m_whiteDestBlend[0] ) m_whiteDestBlend[1] = m_whiteDestBlend[0]; + if ( m_diffuseSrcBlend[0] ) m_diffuseSrcBlend[1] = m_diffuseSrcBlend[0]; + if ( m_diffuseDestBlend[0] ) m_diffuseDestBlend[1] = m_diffuseDestBlend[0]; + if ( m_alphaSrcBlend[0] ) m_alphaSrcBlend[1] = m_alphaSrcBlend[0]; + if ( m_alphaDestBlend[0] ) m_alphaDestBlend[1] = m_alphaDestBlend[0]; + +#if 0 + DWORD pass; + m_pD3DDevice->ValidateDevice(&pass); + char s[100]; + sprintf(s, "NbPass=%d", pass); + SetInfoText(3, s); +#endif +} + +LPDIRECT3DDEVICE7 CD3DEngine::RetD3DDevice() +{ + return m_pD3DDevice; +} + + +// Donne le pointeur au terrain existant. + +void CD3DEngine::SetTerrain(CTerrain* terrain) +{ + m_terrain = terrain; +} + + +// Sauvegarde l'état du moteur graphique dans COLOBOT.INI. + +BOOL CD3DEngine::WriteProfile() +{ + SetProfileInt("Engine", "AlphaMode", m_alphaMode); + + if ( m_bForceStateColor ) + { + SetProfileInt("Engine", "StateColor", m_bStateColor); + } + else + { + SetProfileInt("Engine", "StateColor", -1); + } + + SetProfileInt("Engine", "BlackSrcBlend", m_blackSrcBlend[0]); + SetProfileInt("Engine", "BlackDestBlend", m_blackDestBlend[0]); + SetProfileInt("Engine", "WhiteSrcBlend", m_whiteSrcBlend[0]); + SetProfileInt("Engine", "WhiteDestBlend", m_whiteDestBlend[0]); + SetProfileInt("Engine", "DiffuseSrcBlend", m_diffuseSrcBlend[0]); + SetProfileInt("Engine", "DiffuseDestBlend", m_diffuseDestBlend[0]); + SetProfileInt("Engine", "AlphaSrcBlend", m_alphaSrcBlend[0]); + SetProfileInt("Engine", "AlphaDestBlend", m_alphaDestBlend[0]); + + return TRUE; +} + + +// Setup the app so it can support single-stepping. + +void CD3DEngine::TimeInit() +{ + m_baseTime = timeGetTime(); + m_lastTime = 0; + m_absTime = 0.0f; +} + +void CD3DEngine::TimeEnterGel() +{ + m_stopTime = timeGetTime(); +} + +void CD3DEngine::TimeExitGel() +{ + m_baseTime += timeGetTime() - m_stopTime; +} + +float CD3DEngine::TimeGet() +{ + float aTime, rTime; + + aTime = (timeGetTime()-m_baseTime)*0.001f; // en ms + rTime = (aTime - m_lastTime)*m_speed; + m_absTime += rTime; + m_lastTime = aTime; + + return rTime; +} + + +void CD3DEngine::SetPause(BOOL bPause) +{ + m_bPause = bPause; +} + +BOOL CD3DEngine::RetPause() +{ + return m_bPause; +} + + +void CD3DEngine::SetMovieLock(BOOL bLock) +{ + m_bMovieLock = bLock; +} + +BOOL CD3DEngine::RetMovieLock() +{ + return m_bMovieLock; +} + + +void CD3DEngine::SetShowStat(BOOL bShow) +{ + m_app->SetShowStat(bShow); +} + +BOOL CD3DEngine::RetShowStat() +{ + return m_app->RetShowStat(); +} + + +void CD3DEngine::SetRenderEnable(BOOL bEnable) +{ + m_bRender = bEnable; +} + + +// Prépare une structure D3DObjLevel6 pour pouvoir ajouter +// qq éléments D3DVERTEX2. + +void CD3DEngine::MemSpace6(D3DObjLevel6 *&p, int nb) +{ + D3DObjLevel6* pp; + int total, size; + + if ( p == 0 ) + { + total = SIZEBLOC_TRIANGLE+nb; + size = sizeof(D3DObjLevel6)+sizeof(D3DVERTEX2)*(total-1); + p = (D3DObjLevel6*)malloc(size); + ZeroMemory(p, size); + p->totalPossible = total; + return; + } + + if ( p->totalUsed+nb > p->totalPossible ) + { + total = p->totalPossible+SIZEBLOC_TRIANGLE+nb; + size = sizeof(D3DObjLevel6)+sizeof(D3DVERTEX2)*(total-1); + pp = (D3DObjLevel6*)malloc(size); + ZeroMemory(pp, size); + CopyMemory(pp, p, sizeof(D3DObjLevel6)+sizeof(D3DVERTEX2)*(p->totalPossible-1)); + pp->totalPossible = total; + free(p); + p = pp; + } +} + +// Prépare une structure D3DObjLevel5 pour pouvoir ajouter +// qq éléments D3DObjLevel6. + +void CD3DEngine::MemSpace5(D3DObjLevel5 *&p, int nb) +{ + D3DObjLevel5* pp; + int total, size; + + if ( p == 0 ) + { + total = SIZEBLOC_MATERIAL+nb; + size = sizeof(D3DObjLevel5)+sizeof(D3DObjLevel6*)*(total-1); + p = (D3DObjLevel5*)malloc(size); + ZeroMemory(p, size); + p->totalPossible = total; + return; + } + + if ( p->totalUsed+nb > p->totalPossible ) + { + total = p->totalPossible+SIZEBLOC_MATERIAL+nb; + size = sizeof(D3DObjLevel5)+sizeof(D3DObjLevel6*)*(total-1); + pp = (D3DObjLevel5*)malloc(size); + ZeroMemory(pp, size); + CopyMemory(pp, p, sizeof(D3DObjLevel5)+sizeof(D3DObjLevel6*)*(p->totalPossible-1)); + pp->totalPossible = total; + free(p); + p = pp; + } +} + +// Prépare une structure D3DObjLevel4 pour pouvoir ajouter +// qq éléments D3DObjLevel5. + +void CD3DEngine::MemSpace4(D3DObjLevel4 *&p, int nb) +{ + D3DObjLevel4* pp; + int total, size; + + if ( p == 0 ) + { + total = SIZEBLOC_LIGHT+nb; + size = sizeof(D3DObjLevel4)+sizeof(D3DObjLevel5*)*(total-1); + p = (D3DObjLevel4*)malloc(size); + ZeroMemory(p, size); + p->totalPossible = total; + return; + } + + if ( p->totalUsed+nb > p->totalPossible ) + { + total = p->totalPossible+SIZEBLOC_LIGHT+nb; + size = sizeof(D3DObjLevel4)+sizeof(D3DObjLevel5*)*(total-1); + pp = (D3DObjLevel4*)malloc(size); + ZeroMemory(pp, size); + CopyMemory(pp, p, sizeof(D3DObjLevel4)+sizeof(D3DObjLevel5*)*(p->totalPossible-1)); + pp->totalPossible = total; + free(p); + p = pp; + } +} + +// Prépare une structure D3DObjLevel3 pour pouvoir ajouter +// qq éléments D3DObjLevel4. + +void CD3DEngine::MemSpace3(D3DObjLevel3 *&p, int nb) +{ + D3DObjLevel3* pp; + int total, size; + + if ( p == 0 ) + { + total = SIZEBLOC_MINMAX+nb; + size = sizeof(D3DObjLevel3)+sizeof(D3DObjLevel4*)*(total-1); + p = (D3DObjLevel3*)malloc(size); + ZeroMemory(p, size); + p->totalPossible = total; + return; + } + + if ( p->totalUsed+nb > p->totalPossible ) + { + total = p->totalPossible+SIZEBLOC_MINMAX+nb; + size = sizeof(D3DObjLevel3)+sizeof(D3DObjLevel4*)*(total-1); + pp = (D3DObjLevel3*)malloc(size); + ZeroMemory(pp, size); + CopyMemory(pp, p, sizeof(D3DObjLevel3)+sizeof(D3DObjLevel4*)*(p->totalPossible-1)); + pp->totalPossible = total; + free(p); + p = pp; + } +} + +// Prépare une structure D3DObjLevel2 pour pouvoir ajouter +// qq éléments D3DObjLevel3. + +void CD3DEngine::MemSpace2(D3DObjLevel2 *&p, int nb) +{ + D3DObjLevel2* pp; + int total, size; + + if ( p == 0 ) + { + total = SIZEBLOC_TRANSFORM+nb; + size = sizeof(D3DObjLevel2)+sizeof(D3DObjLevel3*)*(total-1); + p = (D3DObjLevel2*)malloc(size); + ZeroMemory(p, size); + p->totalPossible = total; + return; + } + + if ( p->totalUsed+nb > p->totalPossible ) + { + total = p->totalPossible+SIZEBLOC_TRANSFORM+nb; + size = sizeof(D3DObjLevel2)+sizeof(D3DObjLevel3*)*(total-1); + pp = (D3DObjLevel2*)malloc(size); + ZeroMemory(pp, size); + CopyMemory(pp, p, sizeof(D3DObjLevel2)+sizeof(D3DObjLevel3*)*(p->totalPossible-1)); + pp->totalPossible = total; + free(p); + p = pp; + } +} + +// Prépare une structure D3DObjLevel1 pour pouvoir ajouter +// qq éléments D3DObjLevel2. + +void CD3DEngine::MemSpace1(D3DObjLevel1 *&p, int nb) +{ + D3DObjLevel1* pp; + int total, size; + + if ( p == 0 ) + { + total = SIZEBLOC_TEXTURE+nb; + size = sizeof(D3DObjLevel1)+sizeof(D3DObjLevel2*)*(total-1); + p = (D3DObjLevel1*)malloc(size); + ZeroMemory(p, size); + p->totalPossible = total; + return; + } + + if ( p->totalUsed+nb > p->totalPossible ) + { + total = p->totalPossible+SIZEBLOC_TEXTURE+nb; + size = sizeof(D3DObjLevel1)+sizeof(D3DObjLevel2*)*(total-1); + pp = (D3DObjLevel1*)malloc(size); + ZeroMemory(pp, size); + CopyMemory(pp, p, sizeof(D3DObjLevel1)+sizeof(D3DObjLevel2*)*(p->totalPossible-1)); + pp->totalPossible = total; + free(p); + p = pp; + } +} + + +// Retourne le nombre d'objets qu'il est encore possible de créer. + +int CD3DEngine::RetRestCreate() +{ + return D3DMAXOBJECT-m_objectParamTotal-2; +} + +// Crée un nouvel objet. Retourne son rang ou -1 en cas d'erreur. + +int CD3DEngine::CreateObject() +{ + D3DMATRIX mat; + int i; + + for ( i=0 ; i= m_objectParamTotal ) + { + m_objectParamTotal = i+1; + } + return i; + } + } + OutputDebugString("CD3DEngine::CreateObject() -> Too many object\n"); + return -1; +} + + +// Supprime tous les objets. + +void CD3DEngine::FlushObject() +{ + D3DObjLevel1* p1; + D3DObjLevel2* p2; + D3DObjLevel3* p3; + D3DObjLevel4* p4; + D3DObjLevel5* p5; + D3DObjLevel6* p6; + int l1, l2, l3, l4, l5, i; + + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; + free(p6); + } + free(p5); + } + free(p4); + } + free(p3); + } + free(p2); + p1->table[l1] = 0; + } + p1->totalUsed = 0; + + for ( i=0 ; itotalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + if ( p3->objRank != objRank ) continue; + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; + free(p6); + } + free(p5); + } + free(p4); + } + free(p3); + p2->table[l2] = 0; + } + } + + ShadowDelete(objRank); // supprime l'ombre + + m_objectParam[objRank].bUsed = FALSE; + + m_objectParamTotal = 0; + for ( i=0 ; i= D3DMAXOBJECT ) return FALSE; + + m_objectParam[objRank].bDrawWorld = bDraw; + return TRUE; +} + +// Indique si un objet doit être dessiné par dessus l'interface. + +BOOL CD3DEngine::SetDrawFront(int objRank, BOOL bDraw) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + m_objectParam[objRank].bDrawFront = bDraw; + return TRUE; +} + + +// Prépare le niveau 1 pour ajouter un triangle. + +D3DObjLevel2* CD3DEngine::AddLevel1(D3DObjLevel1 *&p1, char* texName1, char* texName2) +{ + D3DObjLevel2* p2; + int l1; + + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + if ( strcmp(p2->texName1, texName1) == 0 && + strcmp(p2->texName2, texName2) == 0 ) + { + MemSpace2(p1->table[l1], 1); + return p1->table[l1]; + } + } + + MemSpace1(p1, 1); + l1 = p1->totalUsed++; + p1->table[l1] = 0; + + MemSpace2(p1->table[l1], 1); + strcpy(p1->table[l1]->texName1, texName1); + strcpy(p1->table[l1]->texName2, texName2); + return p1->table[l1]; +} + +// Prépare le niveau 2 pour ajouter un triangle. + +D3DObjLevel3* CD3DEngine::AddLevel2(D3DObjLevel2 *&p2, int objRank) +{ + D3DObjLevel3* p3; + int l2; + + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + if ( p3->objRank == objRank ) + { + MemSpace3(p2->table[l2], 1); + return p2->table[l2]; + } + } + + MemSpace2(p2, 1); + l2 = p2->totalUsed++; + p2->table[l2] = 0; + + MemSpace3(p2->table[l2], 1); + p2->table[l2]->objRank = objRank; + return p2->table[l2]; +} + +// Prépare le niveau 3 pour ajouter un triangle. + +D3DObjLevel4* CD3DEngine::AddLevel3(D3DObjLevel3 *&p3, float min, float max) +{ + D3DObjLevel4* p4; + int l3; + + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + if ( p4->min == min && p4->max == max ) + { + MemSpace4(p3->table[l3], 1); + return p3->table[l3]; + } + } + + MemSpace3(p3, 1); + l3 = p3->totalUsed++; + p3->table[l3] = 0; + + MemSpace4(p3->table[l3], 1); + p3->table[l3]->min = min; + p3->table[l3]->max = max; + return p3->table[l3]; +} + +// Prépare le niveau 4 pour ajouter un triangle. + +D3DObjLevel5* CD3DEngine::AddLevel4(D3DObjLevel4 *&p4, int reserve) +{ + D3DObjLevel5* p5; + int l4; + + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + if ( p5->reserve == reserve ) + { + MemSpace5(p4->table[l4], 1); + return p4->table[l4]; + } + } + + MemSpace4(p4, 1); + l4 = p4->totalUsed++; + p4->table[l4] = 0; + + MemSpace5(p4->table[l4], 1); + p4->table[l4]->reserve = reserve; + return p4->table[l4]; +} + +// Prépare le niveau 5 pour ajouter des vertex. + +D3DObjLevel6* CD3DEngine::AddLevel5(D3DObjLevel5 *&p5, D3DTypeTri type, + const D3DMATERIAL7 &mat, int state, + int nb) +{ + D3DObjLevel6* p6; + int l5; + + if ( type == D3DTYPE6T ) + { + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; + if ( p6->type == type && + memcmp(&p6->material, &mat, sizeof(D3DMATERIAL7)) == 0 && + p6->state == state ) + { + MemSpace6(p5->table[l5], nb); + return p5->table[l5]; + } + } + } + + MemSpace5(p5, 1); + l5 = p5->totalUsed++; + p5->table[l5] = 0; + + MemSpace6(p5->table[l5], nb); + p5->table[l5]->type = type; + p5->table[l5]->material = mat; + p5->table[l5]->state = state; + return p5->table[l5]; +} + +// Ajoute un ou plusieurs triangles à un objet existant. +// Le nombre doit être multiple de 3. + +BOOL CD3DEngine::AddTriangle(int objRank, D3DVERTEX2* vertex, int nb, + const D3DMATERIAL7 &mat, int state, + char* texName1, char* texName2, + float min, float max, BOOL bGlobalUpdate) +{ + D3DObjLevel2* p2; + D3DObjLevel3* p3; + D3DObjLevel4* p4; + D3DObjLevel5* p5; + D3DObjLevel6* p6; + int i; + + m_lastDim = m_dim; + m_lastObjectDetail = m_objectDetail; + m_lastClippingDistance = m_clippingDistance; + + p2 = AddLevel1(m_objectPointer, texName1, texName2); + p3 = AddLevel2(p2, objRank); + p4 = AddLevel3(p3, min, max); + p5 = AddLevel4(p4, 0); + p6 = AddLevel5(p5, D3DTYPE6T, mat, state, nb); // place pour nb vertex + + CopyMemory(&p6->vertex[p6->totalUsed], vertex, sizeof(D3DVERTEX2)*nb); + p6->totalUsed += nb; + + if ( bGlobalUpdate ) + { + m_bUpdateGeometry = TRUE; + } + else + { + for ( i=0 ; ivertex[p6->totalUsed], vertex, sizeof(D3DVERTEX2)*nb); + p6->totalUsed += nb; + + if ( bGlobalUpdate ) + { + m_bUpdateGeometry = TRUE; + } + else + { + for ( i=0 ; itotalUsed++; + p5->table[l5] = buffer; + + if ( bGlobalUpdate ) + { + m_bUpdateGeometry = TRUE; + } + else + { + for ( i=0 ; itotalUsed ; i++ ) + { + m_objectParam[objRank].bboxMin.x = Min(buffer->vertex[i].x, m_objectParam[objRank].bboxMin.x); + m_objectParam[objRank].bboxMin.y = Min(buffer->vertex[i].y, m_objectParam[objRank].bboxMin.y); + m_objectParam[objRank].bboxMin.z = Min(buffer->vertex[i].z, m_objectParam[objRank].bboxMin.z); + m_objectParam[objRank].bboxMax.x = Max(buffer->vertex[i].x, m_objectParam[objRank].bboxMax.x); + m_objectParam[objRank].bboxMax.y = Max(buffer->vertex[i].y, m_objectParam[objRank].bboxMax.y); + m_objectParam[objRank].bboxMax.z = Max(buffer->vertex[i].z, m_objectParam[objRank].bboxMax.z); + } + + m_objectParam[objRank].radius = Max(Length(m_objectParam[objRank].bboxMin), + Length(m_objectParam[objRank].bboxMax)); + } + m_objectParam[objRank].totalTriangle += buffer->totalUsed-2; + + return TRUE; +} + + +// Cherche une liste de triangles. + +void CD3DEngine::ChangeLOD() +{ + D3DObjLevel1* p1; + D3DObjLevel2* p2; + D3DObjLevel3* p3; + D3DObjLevel4* p4; + int l1, l2, l3; + float oldLimit[2], newLimit[2]; + float oldTerrain, newTerrain; + + oldLimit[0] = RetLimitLOD(0, TRUE); + oldLimit[1] = RetLimitLOD(1, TRUE); + + newLimit[0] = RetLimitLOD(0, FALSE); + newLimit[1] = RetLimitLOD(1, FALSE); + + oldTerrain = m_terrainVision*m_lastClippingDistance; + newTerrain = m_terrainVision*m_clippingDistance; + + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + + if ( IsEqual(p4->min, 0.0f ) && + IsEqual(p4->max, oldLimit[0]) ) + { + p4->max = newLimit[0]; + } + else if ( IsEqual(p4->min, oldLimit[0]) && + IsEqual(p4->max, oldLimit[1]) ) + { + p4->min = newLimit[0]; + p4->max = newLimit[1]; + } + else if ( IsEqual(p4->min, oldLimit[1]) && + IsEqual(p4->max, 1000000.0f ) ) + { + p4->min = newLimit[1]; + } + else if ( IsEqual(p4->min, 0.0f ) && + IsEqual(p4->max, oldTerrain) ) + { + p4->max = newTerrain; + } + } + } + } + + m_lastDim = m_dim; + m_lastObjectDetail = m_objectDetail; + m_lastClippingDistance = m_clippingDistance; +} + +// Cherche une liste de triangles. + +D3DObjLevel6* CD3DEngine::SearchTriangle(int objRank, + const D3DMATERIAL7 &mat, int state, + char* texName1, char* texName2, + float min, float max) +{ + D3DObjLevel1* p1; + D3DObjLevel2* p2; + D3DObjLevel3* p3; + D3DObjLevel4* p4; + D3DObjLevel5* p5; + D3DObjLevel6* p6; + int l1, l2, l3, l4, l5; + + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; +//? if ( strcmp(p2->texName1, texName1) != 0 || +//? strcmp(p2->texName2, texName2) != 0 ) continue; + if ( strcmp(p2->texName1, texName1) != 0 ) continue; + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + if ( p3->objRank != objRank ) continue; + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + if ( p4->min != min || + p4->max != max ) continue; + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; +//? if ( p6->state != state || + if ( (p6->state&(~(D3DSTATEDUALb|D3DSTATEDUALw))) != state || + memcmp(&p6->material, &mat, sizeof(D3DMATERIAL7)) != 0 ) continue; + return p6; + } + } + } + } + } + return 0; +} + +// Change la texture secondaire d'un objet. + +BOOL CD3DEngine::ChangeSecondTexture(int objRank, char* texName2) +{ + D3DObjLevel2* newp2; + D3DObjLevel1* p1; + D3DObjLevel2* p2; + D3DObjLevel3* p3; + int l1, l2; + + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + if ( strcmp(p2->texName2, texName2) == 0 ) continue; // déjà nouvelle + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + if ( p3->objRank != objRank ) continue; + + newp2 = AddLevel1(m_objectPointer, p2->texName1, texName2); + + if ( newp2->totalUsed >= newp2->totalPossible ) continue; // faire mieux !!! + newp2->table[newp2->totalUsed++] = p3; + + p2->table[l2] = 0; + } + } + return TRUE; +} + + +// Retourne le nombre de triangles de l'objet. + +int CD3DEngine::RetTotalTriangles(int objRank) +{ + return m_objectParam[objRank].totalTriangle; +} + +// Retourne qq triangles d'un objet. Utilisé pour extraire qq +// triangles d'un objet qui explose. +// "percent" est compris entre 0 et 1. + +int CD3DEngine::GetTriangles(int objRank, float min, float max, + D3DTriangle* buffer, int size, float percent) +{ + D3DObjLevel1* p1; + D3DObjLevel2* p2; + D3DObjLevel3* p3; + D3DObjLevel4* p4; + D3DObjLevel5* p5; + D3DObjLevel6* p6; + D3DVERTEX2* pv; + int l1, l2, l3, l4, l5, l6, i, rank; + + rank = 0; + i = 0; + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; +//? if ( p2->texName[0] == 0 ) continue; + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + if ( p3->objRank != objRank ) continue; + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + if ( p4->min != min || + p4->max != max ) continue; + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; + if ( p6->type == D3DTYPE6T ) + { + pv = &p6->vertex[0]; + for ( l6=0 ; l6totalUsed/3 ; l6++ ) + { + if ( (float)i/rank <= percent ) + { + if ( i >= size ) break; + buffer[i].triangle[0] = pv[0]; + buffer[i].triangle[1] = pv[1]; + buffer[i].triangle[2] = pv[2]; + buffer[i].material = p6->material; + buffer[i].state = p6->state; + strcpy(buffer[i].texName1, p2->texName1); + strcpy(buffer[i].texName2, p2->texName2); + i ++; + } + rank ++; + pv += 3; + } + } + if ( p6->type == D3DTYPE6S ) + { + pv = &p6->vertex[0]; + for ( l6=0 ; l6totalUsed-2 ; l6++ ) + { + if ( (float)i/rank <= percent ) + { + if ( i >= size ) break; + buffer[i].triangle[0] = pv[0]; + buffer[i].triangle[1] = pv[1]; + buffer[i].triangle[2] = pv[2]; + buffer[i].material = p6->material; + buffer[i].state = p6->state; + strcpy(buffer[i].texName1, p2->texName1); + strcpy(buffer[i].texName2, p2->texName2); + i ++; + } + rank ++; + pv += 1; + } + } + } + } + } + } + } + return i; +} + +// Donne la bbox d'un objet. + +BOOL CD3DEngine::GetBBox(int objRank, D3DVECTOR &min, D3DVECTOR &max) +{ + min = m_objectParam[objRank].bboxMin; + max = m_objectParam[objRank].bboxMax; + return TRUE; +} + + +// Change le mapping de texture pour toute une liste de triangles. + +BOOL CD3DEngine::ChangeTextureMapping(int objRank, + const D3DMATERIAL7 &mat, int state, + char* texName1, char* texName2, + float min, float max, + D3DMaping mode, + float au, float bu, + float av, float bv) +{ + D3DObjLevel6* p6; + D3DVERTEX2* pv; + int l6, nb; + + p6 = SearchTriangle(objRank, mat, state, texName1, texName2, min, max); + if ( p6 == 0 ) return FALSE; + + pv = &p6->vertex[0]; + nb = p6->totalUsed; + + if ( mode == D3DMAPPINGX ) + { + for ( l6=0 ; l6tu = pv->z*au+bu; + pv->tv = pv->y*av+bv; + pv ++; + } + } + + if ( mode == D3DMAPPINGY ) + { + for ( l6=0 ; l6tu = pv->x*au+bu; + pv->tv = pv->z*av+bv; + pv ++; + } + } + + if ( mode == D3DMAPPINGZ ) + { + for ( l6=0 ; l6tu = pv->x*au+bu; + pv->tv = pv->y*av+bv; + pv ++; + } + } + + if ( mode == D3DMAPPING1X ) + { + for ( l6=0 ; l6tu = pv->x*au+bu; + pv ++; + } + } + + if ( mode == D3DMAPPING1Y ) + { + for ( l6=0 ; l6tv = pv->y*au+bu; + pv ++; + } + } + + if ( mode == D3DMAPPING1Z ) + { + for ( l6=0 ; l6tu = pv->z*au+bu; + pv ++; + } + } + + return TRUE; +} + +// Change le mapping de texture pour toute une liste de triangles, +// afin de simuler une chenille qui tourne. +// Seul le mapping selon "u" est changé. +// +// pos: position sur le pourtour [p] +// tl: longeur élément répétitif de la texture [t] +// ts: début de la texture [t] +// tt: largeur totale de la texture [t] +// +// [p] = distance dans l'univers 3D +// [t] = position dans la texture (pixels) + +// ^ y 5 +// | 6 o---------o 4 +// | / \ +// | o o +// | 7 | | 3 +// | o current o +// | \ | / +// | 0 o---------o 2 +// | 1 +// -o-----------------------> x +// | +// +// Quand l6=1 : +// 0 1 2 3 4 ... 7 +// o--o---------o--o--o--o-//-o--o développement chenille +// |ps| | +// <--> pe | +// <------------> +// +// Texture : +// o---------------o +// | | +// | o-o-o-o-o | +// | | | | | |<--- texture de la chenille +// | o-o-o-o-o | +// | | | tl | +// | ->|-|<--- | +// | | | +// o-----|---------o--> u +// | ts | | +// <-----> tt | +// <---------------> + +BOOL CD3DEngine::TrackTextureMapping(int objRank, + const D3DMATERIAL7 &mat, int state, + char* texName1, char* texName2, + float min, float max, + D3DMaping mode, float pos, float factor, + float tl, float ts, float tt) +{ + D3DObjLevel6* p6; + D3DVERTEX2* pv; + D3DVECTOR current; + float ps, pe, pps, ppe, offset; + int l6, nb, i, j, s, e; + int is[6], ie[6]; + + p6 = SearchTriangle(objRank, mat, state, texName1, texName2, min, max); + if ( p6 == 0 ) return FALSE; + + pv = &p6->vertex[0]; + nb = p6->totalUsed; + + if ( nb < 12 || nb%6 != 0 ) return FALSE; + + while ( pos < 0.0f ) + { + pos += 1000000.0f; // jamais négatif ! + } + + for ( i=0 ; i<6 ; i++ ) + { + for ( j=0 ; j<6 ; j++ ) + { + if ( pv[i].x == pv[j+6].x && + pv[i].y == pv[j+6].y ) + { + current.x = pv[i].x; // position fin maillon + current.y = pv[i].y; + break; + } + } + } + + ps = 0.0f; // position de début sur le pourtour + for ( l6=0 ; l6= (nb/6)-1 ) break; + for ( i=0 ; i<6 ; i++ ) + { + if ( Abs(pv[i+6].x-current.x) > 0.0001f || + Abs(pv[i+6].y-current.y) > 0.0001f ) + { + current.x = pv[i+6].x; // fin maillon suivant + current.y = pv[i+6].y; + break; + } + } + ps = pe; // position de début suivante sur le pourtour + pv += 6; + } + + return TRUE; +} + + +// Met à jour tous les paramètres géométriques des objets. + +void CD3DEngine::UpdateGeometry() +{ + D3DObjLevel1* p1; + D3DObjLevel2* p2; + D3DObjLevel3* p3; + D3DObjLevel4* p4; + D3DObjLevel5* p5; + D3DObjLevel6* p6; + int l1, l2, l3, l4, l5, objRank, i; + + if ( !m_bUpdateGeometry ) return; + + for ( i=0 ; itotalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + objRank = p3->objRank; + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; + + for ( i=0 ; itotalUsed ; i++ ) + { + m_objectParam[objRank].bboxMin.x = Min(p6->vertex[i].x, m_objectParam[objRank].bboxMin.x); + m_objectParam[objRank].bboxMin.y = Min(p6->vertex[i].y, m_objectParam[objRank].bboxMin.y); + m_objectParam[objRank].bboxMin.z = Min(p6->vertex[i].z, m_objectParam[objRank].bboxMin.z); + m_objectParam[objRank].bboxMax.x = Max(p6->vertex[i].x, m_objectParam[objRank].bboxMax.x); + m_objectParam[objRank].bboxMax.y = Max(p6->vertex[i].y, m_objectParam[objRank].bboxMax.y); + m_objectParam[objRank].bboxMax.z = Max(p6->vertex[i].z, m_objectParam[objRank].bboxMax.z); + } + + m_objectParam[objRank].radius = Max(Length(m_objectParam[objRank].bboxMin), + Length(m_objectParam[objRank].bboxMax)); + } + } + } + } + } + + m_bUpdateGeometry = FALSE; +} + + +// Détermine si un objet est visible, même partiellement. +// La transformation "world" doit être faite ! + +BOOL CD3DEngine::IsVisible(int objRank) +{ + D3DVECTOR center; + DWORD flags; + float radius; + + radius = m_objectParam[objRank].radius; + center = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_pD3DDevice->ComputeSphereVisibility(¢er, &radius, 1, 0, &flags); + + if ( flags & D3DSTATUS_CLIPINTERSECTIONALL ) + { + m_objectParam[objRank].bVisible = FALSE; + return FALSE; + } + m_objectParam[objRank].bVisible = TRUE; + return TRUE; +} + + +// Détecte l'objet visé par la souris. +// Retourne le rang de l'objet ou -1. + +int CD3DEngine::DetectObject(FPOINT mouse) +{ + D3DObjLevel1* p1; + D3DObjLevel2* p2; + D3DObjLevel3* p3; + D3DObjLevel4* p4; + D3DObjLevel5* p5; + D3DObjLevel6* p6; + D3DVERTEX2* pv; + int l1, l2, l3, l4, l5, i, objRank, nearest; + float dist, min; + + min = 1000000.0f; + nearest = -1; + + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + objRank = p3->objRank; + if ( m_objectParam[objRank].type == TYPETERRAIN ) continue; + if ( !DetectBBox(objRank, mouse) ) continue; + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + if ( p4->min != 0.0f ) continue; // LOD B ou C ? + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; + + if ( p6->type == D3DTYPE6T ) + { + pv = &p6->vertex[0]; + for ( i=0 ; itotalUsed/3 ; i++ ) + { + if ( DetectTriangle(mouse, pv, objRank, dist) && + dist < min ) + { + min = dist; + nearest = objRank; + } + pv += 3; + } + } + if ( p6->type == D3DTYPE6S ) + { + pv = &p6->vertex[0]; + for ( i=0 ; itotalUsed-2 ; i++ ) + { + if ( DetectTriangle(mouse, pv, objRank, dist) && + dist < min ) + { + min = dist; + nearest = objRank; + } + pv += 1; + } + } + } + } + } + } + } + return nearest; +} + +// Détecte si la souris est dans un triangle. + +BOOL CD3DEngine::DetectTriangle(FPOINT mouse, D3DVERTEX2 *triangle, + int objRank, float &dist) +{ + D3DVECTOR p2D[3], p3D; + FPOINT a, b, c; + int i; + + for ( i=0 ; i<3 ; i++ ) + { + p3D.x = triangle[i].x; + p3D.y = triangle[i].y; + p3D.z = triangle[i].z; + if ( !TransformPoint(p2D[i], objRank, p3D) ) return FALSE; + } + + if ( mouse.x < p2D[0].x && + mouse.x < p2D[1].x && + mouse.x < p2D[2].x ) return FALSE; + if ( mouse.x > p2D[0].x && + mouse.x > p2D[1].x && + mouse.x > p2D[2].x ) return FALSE; + if ( mouse.y < p2D[0].y && + mouse.y < p2D[1].y && + mouse.y < p2D[2].y ) return FALSE; + if ( mouse.y > p2D[0].y && + mouse.y > p2D[1].y && + mouse.y > p2D[2].y ) return FALSE; + + a.x = p2D[0].x; + a.y = p2D[0].y; + b.x = p2D[1].x; + b.y = p2D[1].y; + c.x = p2D[2].x; + c.y = p2D[2].y; + if ( !IsInsideTriangle(a, b, c, mouse) ) return FALSE; + + dist = (p2D[0].z+p2D[1].z+p2D[2].z)/3.0f; + return TRUE; +} + +// Détecte si un objet est visé par la souris. + +BOOL CD3DEngine::DetectBBox(int objRank, FPOINT mouse) +{ + D3DVECTOR p, pp; + FPOINT min, max; + int i; + + min.x = 1000000.0f; + min.y = 1000000.0f; + max.x = -1000000.0f; + max.y = -1000000.0f; + + for ( i=0 ; i<8 ; i++ ) + { + if ( i & (1<<0) ) p.x = m_objectParam[objRank].bboxMin.x; + else p.x = m_objectParam[objRank].bboxMax.x; + if ( i & (1<<1) ) p.y = m_objectParam[objRank].bboxMin.y; + else p.y = m_objectParam[objRank].bboxMax.y; + if ( i & (1<<2) ) p.z = m_objectParam[objRank].bboxMin.z; + else p.z = m_objectParam[objRank].bboxMax.z; + if ( TransformPoint(pp, objRank, p) ) + { + if ( pp.x < min.x ) min.x = pp.x; + if ( pp.x > max.x ) max.x = pp.x; + if ( pp.y < min.y ) min.y = pp.y; + if ( pp.y > max.y ) max.y = pp.y; + } + } + + return ( mouse.x >= min.x && + mouse.x <= max.x && + mouse.y >= min.y && + mouse.y <= max.y ); +} + +// Transforme un point 3D (x,y,z) dans l'espace 2D (x,y,-) de la fenêtre. +// La coordonnée p2D.z donne l'éloignement. + +BOOL CD3DEngine::TransformPoint(D3DVECTOR &p2D, int objRank, D3DVECTOR p3D) +{ + p3D = Transform(m_objectParam[objRank].transform, p3D); + p3D = Transform(m_matView, p3D); + + if ( p3D.z < 2.0f ) return FALSE; // derrière ? + + p2D.x = (p3D.x/p3D.z)*m_matProj._11; + p2D.y = (p3D.y/p3D.z)*m_matProj._22; + p2D.z = p3D.z; + + p2D.x = (p2D.x+1.0f)/2.0f; // [-1..1] -> [0..1] + p2D.y = (p2D.y+1.0f)/2.0f; + + return TRUE; +} + + +// Calcul les distances entre le point de vue et l'origine +// des différents objets. + +void CD3DEngine::ComputeDistance() +{ + D3DVECTOR v; + int i; + float distance; + + if ( s_resol == 0 ) + { + for ( i=0 ; iIsVideo8MB() ) + { + SetGroundSpot(FALSE); + SetSkyMode(FALSE); + } + + if ( m_app->IsVideo32MB() && bFirst ) + { + SetObjectDetail(2.0f); + } +} + +// Retourne la quantité totale de mémoire vidéo pour les textures. + +int CD3DEngine::GetVidMemTotal() +{ + return m_app->GetVidMemTotal(); +} + +BOOL CD3DEngine::IsVideo8MB() +{ + return m_app->IsVideo8MB(); +} + +BOOL CD3DEngine::IsVideo32MB() +{ + return m_app->IsVideo32MB(); +} + + +// Effectue la liste de tous les devices graphiques disponibles. + +BOOL CD3DEngine::EnumDevices(char *bufDevices, int lenDevices, + char *bufModes, int lenModes, + int &totalDevices, int &selectDevices, + int &totalModes, int &selectModes) +{ + return m_app->EnumDevices(bufDevices, lenDevices, + bufModes, lenModes, + totalDevices, selectDevices, + totalModes, selectModes); +} + +BOOL CD3DEngine::RetFullScreen() +{ + return m_app->RetFullScreen(); +} + +BOOL CD3DEngine::ChangeDevice(char *device, char *mode, BOOL bFull) +{ + return m_app->ChangeDevice(device, mode, bFull); +} + + + +D3DMATRIX* CD3DEngine::RetMatView() +{ + return &m_matView; +} + +D3DMATRIX* CD3DEngine::RetMatLeftView() +{ + return &m_matLeftView; +} + +D3DMATRIX* CD3DEngine::RetMatRightView() +{ + return &m_matRightView; +} + + +// Spécifie l'emplacement et la direction du point de vue. + +void CD3DEngine::SetViewParams(const D3DVECTOR &vEyePt, + const D3DVECTOR &vLookatPt, + const D3DVECTOR &vUpVec, + FLOAT fEyeDistance) +{ +#if 0 + m_eyePt = vEyePt; + + // Adjust camera position for left or right eye along the axis + // perpendicular to the view direction vector and the up vector. + D3DVECTOR vView = (vLookatPt) - (vEyePt); + vView = CrossProduct( vView, (vUpVec) ); + vView = Normalize( vView ) * fEyeDistance; + + D3DVECTOR vLeftEyePt = (vEyePt) + vView; + D3DVECTOR vRightEyePt = (vEyePt) - vView; + + // Set the view matrices + D3DUtil_SetViewMatrix( m_matLeftView, (D3DVECTOR)vLeftEyePt, (D3DVECTOR)vLookatPt, (D3DVECTOR)vUpVec ); + D3DUtil_SetViewMatrix( m_matRightView, (D3DVECTOR)vRightEyePt, (D3DVECTOR)vLookatPt, (D3DVECTOR)vUpVec ); + D3DUtil_SetViewMatrix( m_matView, (D3DVECTOR)vEyePt, (D3DVECTOR)vLookatPt, (D3DVECTOR)vUpVec ); +#else + m_eyePt = vEyePt; + m_lookatPt = vLookatPt; + m_eyeDirH = RotateAngle(vEyePt.x-vLookatPt.x, vEyePt.z-vLookatPt.z); + m_eyeDirV = RotateAngle(Length2d(vEyePt, vLookatPt), vEyePt.y-vLookatPt.y); + + D3DUtil_SetViewMatrix(m_matView, (D3DVECTOR)vEyePt, (D3DVECTOR)vLookatPt, (D3DVECTOR)vUpVec); + + if ( m_sound == 0 ) + { + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + } + m_sound->SetListener(vEyePt, vLookatPt); +#endif +} + + +// Spécifie la matrice de transformation d'un objet. + +BOOL CD3DEngine::SetObjectTransform(int objRank, const D3DMATRIX &transform) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + m_objectParam[objRank].transform = transform; + return TRUE; +} + +// Donne la matrice de transformation d'un objet. + +BOOL CD3DEngine::GetObjectTransform(int objRank, D3DMATRIX &transform) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + transform = m_objectParam[objRank].transform; + return TRUE; +} + +// Spécifie le type d'un objet. + +BOOL CD3DEngine::SetObjectType(int objRank, D3DTypeObj type) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + m_objectParam[objRank].type = type; + return TRUE; +} + +// Retourne le type d'un objet. + +D3DTypeObj CD3DEngine::RetObjectType(int objRank) +{ + return m_objectParam[objRank].type; +} + +// Spécifie la transparence d'un objet. + +BOOL CD3DEngine::SetObjectTransparency(int objRank, float value) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + m_objectParam[objRank].transparency = value; + return TRUE; +} + + +// Alloue une table pour l'ombre, si nécessaire. + +BOOL CD3DEngine::ShadowCreate(int objRank) +{ + int i; + + // Déjà alloué ? + if ( m_objectParam[objRank].shadowRank != -1 ) return TRUE; + + for ( i=0 ; i= D3DMAXOBJECT ) return FALSE; + + int i = m_objectParam[objRank].shadowRank; + if ( i == -1 ) return FALSE; + + m_shadow[i].bHide = bHide; + return TRUE; +} + +// Spécifie le type de l'ombre de l'objet. + +BOOL CD3DEngine::SetObjectShadowType(int objRank, D3DShadowType type) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + int i = m_objectParam[objRank].shadowRank; + if ( i == -1 ) return FALSE; + + m_shadow[i].type = type; + return TRUE; +} + +// Spécifie la position de l'ombre de l'objet. + +BOOL CD3DEngine::SetObjectShadowPos(int objRank, const D3DVECTOR &pos) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + int i = m_objectParam[objRank].shadowRank; + if ( i == -1 ) return FALSE; + + m_shadow[i].pos = pos; + return TRUE; +} + +// Spécifie la normale au terrain de l'ombre de l'objet. + +BOOL CD3DEngine::SetObjectShadowNormal(int objRank, const D3DVECTOR &n) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + int i = m_objectParam[objRank].shadowRank; + if ( i == -1 ) return FALSE; + + m_shadow[i].normal = n; + return TRUE; +} + +// Spécifie l'angle de l'ombre de l'objet. + +BOOL CD3DEngine::SetObjectShadowAngle(int objRank, float angle) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + int i = m_objectParam[objRank].shadowRank; + if ( i == -1 ) return FALSE; + + m_shadow[i].angle = angle; + return TRUE; +} + +// Spécifie le rayon de l'ombre de l'objet. + +BOOL CD3DEngine::SetObjectShadowRadius(int objRank, float radius) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + int i = m_objectParam[objRank].shadowRank; + if ( i == -1 ) return FALSE; + + m_shadow[i].radius = radius; + return TRUE; +} + +// Retourne le rayon de l'ombre de l'objet. + +float CD3DEngine::RetObjectShadowRadius(int objRank) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return 0.0f; + + int i = m_objectParam[objRank].shadowRank; + if ( i == -1 ) return FALSE; + + return m_shadow[i].radius; +} + +// Spécifie l'intensité de l'ombre de l'objet. + +BOOL CD3DEngine::SetObjectShadowIntensity(int objRank, float intensity) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + int i = m_objectParam[objRank].shadowRank; + if ( i == -1 ) return FALSE; + + m_shadow[i].intensity = intensity; + return TRUE; +} + +// Spécifie la hauteur de l'ombre de l'objet. + +BOOL CD3DEngine::SetObjectShadowHeight(int objRank, float h) +{ + if ( objRank < 0 || objRank >= D3DMAXOBJECT ) return FALSE; + + int i = m_objectParam[objRank].shadowRank; + if ( i == -1 ) return FALSE; + + m_shadow[i].height = h; + return TRUE; +} + + +// Efface toutes les marques au sol. + +void CD3DEngine::GroundSpotFlush() +{ + LPDIRECTDRAWSURFACE7 surface; + DDSURFACEDESC2 ddsd; + WORD* pbSurf; + char texName[20]; + int s, y; + + ZeroMemory(m_groundSpot, sizeof(D3DGroundSpot)*D3DMAXGROUNDSPOT); + m_bFirstGroundSpot = TRUE; // force le dessin la première fois + + for ( s=0 ; s<16 ; s++ ) + { + sprintf(texName, "shadow%.2d.tga", s); + surface = D3DTextr_GetSurface(texName); + if ( surface == 0 ) continue; + + ZeroMemory(&ddsd, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + if ( surface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL) != DD_OK ) continue; + + if ( ddsd.ddpfPixelFormat.dwRGBBitCount != 16 ) continue; + + for ( y=0 ; y<(int)ddsd.dwHeight ; y++ ) + { + pbSurf = (WORD*)ddsd.lpSurface; + pbSurf += ddsd.lPitch*y/2; + memset(pbSurf, -1, ddsd.lPitch); // tout blanc + } + + surface->Unlock(NULL); + } +} + +// Alloue une table pour une marque au sol, si nécessaire. + +int CD3DEngine::GroundSpotCreate() +{ + int i; + + for ( i=0 ; i 1 ) rank = 1; + + if ( m_rankView == 0 && rank == 1 ) // entre dans l'eau ? + { + m_light->AdaptLightColor(m_waterAddColor, +1.0f); + } + + if ( m_rankView == 1 && rank == 0 ) // sort de l'eau ? + { + m_light->AdaptLightColor(m_waterAddColor, -1.0f); + } + + m_rankView = rank; +} + +int CD3DEngine::RetRankView() +{ + return m_rankView; +} + +// Indique s'il faut dessiner le monde sous l'interface. + +void CD3DEngine::SetDrawWorld(BOOL bDraw) +{ + m_bDrawWorld = bDraw; +} + +// Indique s'il faut dessiner le monde sur l'interface. + +void CD3DEngine::SetDrawFront(BOOL bDraw) +{ + m_bDrawFront = bDraw; +} + +// Gestion de la couleur ambiante. +// color = 0x00rrggbb +// rr: rouge +// gg: vert +// bb: bleu + +void CD3DEngine::SetAmbiantColor(D3DCOLOR color, int rank) +{ + m_ambiantColor[rank] = color; +} + +D3DCOLOR CD3DEngine::RetAmbiantColor(int rank) +{ + return m_ambiantColor[rank]; +} + + +// Gestion de la couleur sous l'eau. + +void CD3DEngine::SetWaterAddColor(D3DCOLORVALUE color) +{ + m_waterAddColor = color; +} + +D3DCOLORVALUE CD3DEngine::RetWaterAddColor() +{ + return m_waterAddColor; +} + + +// Gestion de la couleur du brouillard. + +void CD3DEngine::SetFogColor(D3DCOLOR color, int rank) +{ + m_fogColor[rank] = color; +} + +D3DCOLOR CD3DEngine::RetFogColor(int rank) +{ + return m_fogColor[rank]; +} + + +// Gestion de la profondeur de champ. +// Au-delà de cette distance, plus rien n'est visible. +// Un peu avant (selon SetFogStart), on entre dans le brouillard. + +void CD3DEngine::SetDeepView(float length, int rank, BOOL bRef) +{ + if ( bRef ) + { + length *= m_clippingDistance; + } + + m_deepView[rank] = length; +} + +float CD3DEngine::RetDeepView(int rank) +{ + return m_deepView[rank]; +} + + +// Gestion du départ de brouillard. +// Avec 0.0, le brouillard part du point de vue (brouillard max). +// Avec 1.0, le brouillard part de la profondeur de champ (pas de brouillard). + +void CD3DEngine::SetFogStart(float start, int rank) +{ + m_fogStart[rank] = start; +} + +float CD3DEngine::RetFogStart(int rank) +{ + return m_fogStart[rank]; +} + + +// Donne l'image d'arrière-plan à utiliser. + +void CD3DEngine::SetBackground(char *name, D3DCOLOR up, D3DCOLOR down, + D3DCOLOR cloudUp, D3DCOLOR cloudDown, + BOOL bFull, BOOL bQuarter) +{ + strcpy(m_backgroundName, name); + m_backgroundColorUp = up; + m_backgroundColorDown = down; + m_backgroundCloudUp = cloudUp; + m_backgroundCloudDown = cloudDown; + m_bBackgroundFull = bFull; + m_bBackgroundQuarter = bQuarter; +} + +// Donne l'image d'arrière-plan utilisée. + +void CD3DEngine::RetBackground(char *name, D3DCOLOR &up, D3DCOLOR &down, + D3DCOLOR &cloudUp, D3DCOLOR &cloudDown, + BOOL &bFull, BOOL &bQuarter) +{ + strcpy(name, m_backgroundName); + up = m_backgroundColorUp; + down = m_backgroundColorDown; + cloudUp = m_backgroundCloudUp; + cloudDown = m_backgroundCloudDown; + bFull = m_bBackgroundFull; + bQuarter = m_bBackgroundQuarter; +} + +// Donne l'image d'avant-plan à utiliser. + +void CD3DEngine::SetFrontsizeName(char *name) +{ + if ( m_frontsizeName[0] != 0 ) + { + FreeTexture(m_frontsizeName); + } + + strcpy(m_frontsizeName, name); +} + +// Indique s'il faut dessiner d'avant-plan. + +void CD3DEngine::SetOverFront(BOOL bFront) +{ + m_bOverFront = bFront; +} + +// Donne la couleur d'avant-plan. + +void CD3DEngine::SetOverColor(D3DCOLOR color, int mode) +{ + m_overColor = color; + m_overMode = mode; +} + + + +// Gestion de la densité des particules. + +void CD3DEngine::SetParticuleDensity(float value) +{ + if ( value < 0.0f ) value = 0.0f; + if ( value > 2.0f ) value = 2.0f; + m_particuleDensity = value; +} + +float CD3DEngine::RetParticuleDensity() +{ + return m_particuleDensity; +} + +float CD3DEngine::ParticuleAdapt(float factor) +{ + if ( m_particuleDensity == 0.0f ) + { + return 1000000.0f; + } + return factor/m_particuleDensity; +} + +// Gestion de la distance de clipping. + +void CD3DEngine::SetClippingDistance(float value) +{ + if ( value < 0.5f ) value = 0.5f; + if ( value > 2.0f ) value = 2.0f; + m_clippingDistance = value; +} + +float CD3DEngine::RetClippingDistance() +{ + return m_clippingDistance; +} + +// Gestion du détail des objets. + +void CD3DEngine::SetObjectDetail(float value) +{ + if ( value < 0.0f ) value = 0.0f; + if ( value > 2.0f ) value = 2.0f; + m_objectDetail = value; +} + +float CD3DEngine::RetObjectDetail() +{ + return m_objectDetail; +} + +// Gestion de la quantité d'objets gadgets. + +void CD3DEngine::SetGadgetQuantity(float value) +{ + if ( value < 0.0f ) value = 0.0f; + if ( value > 1.0f ) value = 1.0f; + + m_gadgetQuantity = value; +} + +float CD3DEngine::RetGadgetQuantity() +{ + return m_gadgetQuantity; +} + +// Gestion de la qualité des textures. + +void CD3DEngine::SetTextureQuality(int value) +{ + if ( value < 0 ) value = 0; + if ( value > 2 ) value = 2; + + if ( value != m_textureQuality ) + { + m_textureQuality = value; + LoadAllTexture(); + } +} + +int CD3DEngine::RetTextureQuality() +{ + return m_textureQuality; +} + + +// Gestion du mode de toto. + +void CD3DEngine::SetTotoMode(BOOL bPresent) +{ + m_bTotoMode = bPresent; +} + +BOOL CD3DEngine::RetTotoMode() +{ + return m_bTotoMode; +} + + +// Gestion du mode d'avant-plan. + +void CD3DEngine::SetLensMode(BOOL bPresent) +{ + m_bLensMode = bPresent; +} + +BOOL CD3DEngine::RetLensMode() +{ + return m_bLensMode; +} + + +// Gestion du mode de l'eau. + +void CD3DEngine::SetWaterMode(BOOL bPresent) +{ + m_bWaterMode = bPresent; +} + +BOOL CD3DEngine::RetWaterMode() +{ + return m_bWaterMode; +} + + +// Gestion du mode de ciel. + +void CD3DEngine::SetSkyMode(BOOL bPresent) +{ + m_bSkyMode = bPresent; +} + +BOOL CD3DEngine::RetSkyMode() +{ + return m_bSkyMode; +} + + +// Gestion du mode d'arrière-plan. + +void CD3DEngine::SetBackForce(BOOL bPresent) +{ + m_bBackForce = bPresent; +} + +BOOL CD3DEngine::RetBackForce() +{ + return m_bBackForce; +} + + +// Gestion du mode des planètes. + +void CD3DEngine::SetPlanetMode(BOOL bPresent) +{ + m_bPlanetMode = bPresent; +} + +BOOL CD3DEngine::RetPlanetMode() +{ + return m_bPlanetMode; +} + + +// Gestion du mode des lumières dynamiques. + +void CD3DEngine::SetLightMode(BOOL bPresent) +{ + m_bLightMode = bPresent; +} + +BOOL CD3DEngine::RetLightMode() +{ + return m_bLightMode; +} + + +// Gestion du mode d'indentation pendant l'édition (CEdit). + +void CD3DEngine::SetEditIndentMode(BOOL bAuto) +{ + m_bEditIndentMode = bAuto; +} + +BOOL CD3DEngine::RetEditIndentMode() +{ + return m_bEditIndentMode; +} + + +// Gestion de l'avance d'un tabulateur pendant l'édition (CEdit). + +void CD3DEngine::SetEditIndentValue(int value) +{ + m_editIndentValue = value; +} + +int CD3DEngine::RetEditIndentValue() +{ + return m_editIndentValue; +} + + +void CD3DEngine::SetSpeed(float speed) +{ + m_speed = speed; +} + +float CD3DEngine::RetSpeed() +{ + return m_speed; +} + + +void CD3DEngine::SetTracePrecision(float factor) +{ + m_tracePrecision = factor; +} + +float CD3DEngine::RetTracePrecision() +{ + return m_tracePrecision; +} + + +// Met à jour la scène après un changement de paramètres. + +void CD3DEngine::ApplyChange() +{ + m_deepView[0] /= m_lastClippingDistance; + m_deepView[1] /= m_lastClippingDistance; + + SetFocus(m_focus); + ChangeLOD(); + + m_deepView[0] *= m_clippingDistance; + m_deepView[1] *= m_clippingDistance; +} + + + +// Retourne le point de vue de l'utilisateur. + +D3DVECTOR CD3DEngine::RetEyePt() +{ + return m_eyePt; +} + +D3DVECTOR CD3DEngine::RetLookatPt() +{ + return m_lookatPt; +} + +float CD3DEngine::RetEyeDirH() +{ + return m_eyeDirH; +} + +float CD3DEngine::RetEyeDirV() +{ + return m_eyeDirV; +} + +POINT CD3DEngine::RetDim() +{ + return m_dim; +} + + +// Génère un nom de quart d'image. + +void QuarterName(char *buffer, char *name, int quarter) +{ + while ( *name != 0 ) + { + if ( *name == '.' ) + { + *buffer++ = 'a'+quarter; + } + *buffer++ = *name++; + } + *buffer++ = 0; +} + +// Libère une texture. + +BOOL CD3DEngine::FreeTexture(char* name) +{ + if ( name[0] == 0 ) return TRUE; + + if ( D3DTextr_DestroyTexture(name) != S_OK ) + { + return FALSE; + } + return TRUE; +} + +// Charge une texture. + +BOOL CD3DEngine::LoadTexture(char* name, int stage) +{ + DWORD mode; + + if ( name[0] == 0 ) return TRUE; + + if ( D3DTextr_GetSurface(name) == NULL ) + { + if ( strstr(name, ".tga") == 0 ) + { + mode = 0; + } + else + { + mode = D3DTEXTR_CREATEWITHALPHA; + } + + if ( D3DTextr_CreateTextureFromFile(name, stage, mode) != S_OK ) + { + return FALSE; + } + + if ( D3DTextr_Restore(name, m_pD3DDevice) != S_OK ) + { + return FALSE; + } + } + return TRUE; +} + +// Charge toutes les textures de la scène. + +BOOL CD3DEngine::LoadAllTexture() +{ + D3DObjLevel1* p1; + D3DObjLevel2* p2; + int l1, i; + char name[50]; + BOOL bOK = TRUE; + +#if _POLISH + LoadTexture("textp.tga"); +#else + LoadTexture("text.tga"); +#endif + LoadTexture("mouse.tga"); + LoadTexture("button1.tga"); + LoadTexture("button2.tga"); + LoadTexture("button3.tga"); + LoadTexture("effect00.tga"); + LoadTexture("effect01.tga"); + LoadTexture("effect02.tga"); + LoadTexture("map.tga"); + + if ( m_backgroundName[0] != 0 ) + { + if ( m_bBackgroundQuarter ) // image en 4 morceaux ? + { + for ( i=0 ; i<4 ; i++ ) + { + QuarterName(name, m_backgroundName, i); + LoadTexture(name); + } + } + else + { + LoadTexture(m_backgroundName); + } + } + if ( m_frontsizeName[0] != 0 ) + { + LoadTexture(m_frontsizeName); + } + + m_planet->LoadTexture(); + + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + + if ( p2 == 0 || p2->texName1[0] != 0 ) + { + if ( !LoadTexture(p2->texName1, 0) ) bOK = FALSE; + } + + if ( p2 == 0 || p2->texName2[0] != 0 ) + { + if ( !LoadTexture(p2->texName2, 1) ) bOK = FALSE; + } + } + return bOK; +} + + +// Called during initial app startup, this function performs all the +// permanent initialization. + +HRESULT CD3DEngine::OneTimeSceneInit() +{ + return S_OK; +} + + +// Mise à jour après avoir créé des objets. + +void CD3DEngine::Update() +{ + ComputeDistance(); + UpdateGeometry(); +} + +// Called once per frame, the call is the entry point for animating +// the scene. + +HRESULT CD3DEngine::FrameMove(float rTime) +{ + m_light->FrameLight(rTime); + m_particule->FrameParticule(rTime); + ComputeDistance(); + UpdateGeometry(); + + if ( m_groundMark.bUsed ) + { + if ( m_groundMark.phase == 1 ) // croissance ? + { + m_groundMark.intensity += rTime*(1.0f/m_groundMark.delay[0]); + if ( m_groundMark.intensity >= 1.0f ) + { + m_groundMark.intensity = 1.0f; + m_groundMark.fix = 0.0f; + m_groundMark.phase = 2; + } + } + else if ( m_groundMark.phase == 2 ) // fixe ? + { + m_groundMark.fix += rTime*(1.0f/m_groundMark.delay[1]); + if ( m_groundMark.fix >= 1.0f ) + { + m_groundMark.phase = 3; + } + } + else if ( m_groundMark.phase == 3 ) // décroissance ? + { + m_groundMark.intensity -= rTime*(1.0f/m_groundMark.delay[2]); + if ( m_groundMark.intensity < 0.0f ) + { + m_groundMark.intensity = 0.0f; + m_groundMark.phase = 0; + m_groundMark.bUsed = FALSE; + } + } + } + + if ( m_sound == 0 ) + { + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + } + m_sound->FrameMove(rTime); + + return S_OK; +} + +// Fait évoluer tout le jeu. + +void CD3DEngine::StepSimul(float rTime) +{ + m_app->StepSimul(rTime); +} + + + +// Modifie l'état associé à un matériaux. +// (*) Ne fonctionne pas sans cette instruction, mystère ! + +void CD3DEngine::SetState(int state, D3DCOLOR color) +{ + BOOL bSecond; + + if ( state == m_lastState && + color == m_lastColor ) return; + m_lastState = state; + m_lastColor = color; + + if ( m_alphaMode != 1 && (state & D3DSTATEALPHA) ) + { + state &= ~D3DSTATEALPHA; + + if ( m_alphaMode == 2 ) + { + state |= D3DSTATETTb; + } + } + + if ( state & D3DSTATETTb ) // transparent selon noir de la texture ? + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, m_blackSrcBlend[1]); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, m_blackDestBlend[1]); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, table_blend[debug_blend1]); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, table_blend[debug_blend2]); + + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREFACTOR, color); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); // (*) + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + } + else if ( state & D3DSTATETTw ) // transparent selon blanc de la texture ? + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, m_whiteSrcBlend[1]); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, m_whiteDestBlend[1]); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, table_blend[debug_blend3]); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, table_blend[debug_blend4]); + + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREFACTOR, ~color); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADD); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); // (*) + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + } + else if ( state & D3DSTATETCb ) // transparent selon noir de la couleur ? + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, m_blackSrcBlend[1]); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, m_blackDestBlend[1]); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, table_blend[debug_blend1]); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, table_blend[debug_blend2]); + + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREFACTOR, color); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + } + else if ( state & D3DSTATETCw ) // transparent selon blanc de la couleur ? + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, m_whiteSrcBlend[1]); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, m_whiteDestBlend[1]); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, table_blend[debug_blend3]); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, table_blend[debug_blend4]); + + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREFACTOR, ~color); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + } + else if ( state & D3DSTATETD ) // transparent selon couleur diffuse ? + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, m_diffuseSrcBlend[1]); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, m_diffuseDestBlend[1]); + + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + } + else if ( state & D3DSTATEALPHA ) // image avec canal alpha ? + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHAFUNC, D3DCMP_GREATER); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHAREF, (DWORD)(128)); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, m_alphaSrcBlend[1]); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, m_alphaSrcBlend[1]); + + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREFACTOR, color); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + } + else // normal ? + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, FALSE); + + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + } + + if ( state & D3DSTATEFOG ) + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, TRUE); + } + + bSecond = m_bGroundSpot|m_bDirty; + if ( !m_bGroundSpot && (state & D3DSTATESECOND) != 0 ) bSecond = FALSE; + if ( !m_bDirty && (state & D3DSTATESECOND) == 0 ) bSecond = FALSE; + + if ( (state & D3DSTATEDUALb) && bSecond ) + { + m_pD3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1); + } + else if ( (state & D3DSTATEDUALw) && bSecond ) + { + m_pD3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1); + } + else + { + m_pD3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + } + + if ( state & D3DSTATEWRAP ) + { +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_WRAP0, D3DWRAP_U|D3DWRAP_V); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_ADDRESS, D3DTADDRESS_WRAP); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_ADDRESS, D3DTADDRESS_WRAP); + } + else if ( state & D3DSTATECLAMP ) + { +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_WRAP0, 0); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP); + } + else + { +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_WRAP0, 0); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP); + } + + if ( state & D3DSTATE2FACE ) + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE); + } + else + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_CCW); + } + + if ( state & D3DSTATELIGHT ) + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_AMBIENT, 0xffffffff); + } + else + { + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_AMBIENT, m_ambiantColor[m_rankView]); + } +} + +// Spécifie une texture à utiliser. + +void CD3DEngine::SetTexture(char *name, int stage) +{ +//? if ( stage == 1 && !m_bDirty ) return; +//? if ( stage == 1 && !m_bShadow ) return; + + if ( strcmp(name, m_lastTexture[stage]) == 0 ) return; + strcpy(m_lastTexture[stage], name); + + m_pD3DDevice->SetTexture(stage, D3DTextr_GetSurface(name)); +} + +// Spécifie le matérial à utiliser. + +void CD3DEngine::SetMaterial(const D3DMATERIAL7 &mat) +{ + if ( memcmp(&mat, &m_lastMaterial, sizeof(D3DMATERIAL7)) == 0 ) return; + m_lastMaterial = mat; + + m_pD3DDevice->SetMaterial(&m_lastMaterial); +} + + +// Efface un point dans une surface (dessine en blanc). + +inline void ClearDot(DDSURFACEDESC2* ddsd, int x, int y) +{ + WORD* pbSurf; + + if ( ddsd->ddpfPixelFormat.dwRGBBitCount != 16 ) return; + + pbSurf = (WORD*)ddsd->lpSurface; + pbSurf += ddsd->lPitch*y/2; + pbSurf += x; + + *pbSurf = 0xffff; // blanc +} + +// Affiche un point dans une surface. + +void AddDot(DDSURFACEDESC2* ddsd, int x, int y, D3DCOLORVALUE color) +{ + WORD* pbSurf; + WORD r,g,b, w; + + if ( ddsd->ddpfPixelFormat.dwRGBBitCount != 16 ) return; + + if ( color.r < 0.0f ) color.r = 0.0f; + if ( color.r > 1.0f ) color.r = 1.0f; + r = (int)(color.r*32.0f); + if ( r >= 32 ) r = 31; // 5 bits + + if ( color.g < 0.0f ) color.g = 0.0f; + if ( color.g > 1.0f ) color.g = 1.0f; + g = (int)(color.g*32.0f); + if ( g >= 32 ) g = 31; // 5 bits + + if ( color.b < 0.0f ) color.b = 0.0f; + if ( color.b > 1.0f ) color.b = 1.0f; + b = (int)(color.b*32.0f); + if ( b >= 32 ) b = 31; // 5 bits + + if ( ddsd->ddpfPixelFormat.dwRBitMask == 0xf800 ) // 565 ? + { + w = (r<<11)|(g<<6)|b; + } + else if ( ddsd->ddpfPixelFormat.dwRBitMask == 0x7c00 ) // 555 ? + { + w = (r<<10)|(g<<5)|b; + } + else + { + w = -1; // blanc + } + + pbSurf = (WORD*)ddsd->lpSurface; + pbSurf += ddsd->lPitch*y/2; + pbSurf += x; + + *pbSurf &= w; +} + +// Affiche un point dans une surface. + +void SetDot(DDSURFACEDESC2* ddsd, int x, int y, D3DCOLORVALUE color) +{ + if ( ddsd->ddpfPixelFormat.dwRGBBitCount == 16 ) + { + WORD* pbSurf; + WORD r,g,b, w; + + if ( color.r < 0.0f ) color.r = 0.0f; + if ( color.r > 1.0f ) color.r = 1.0f; + if ( color.g < 0.0f ) color.g = 0.0f; + if ( color.g > 1.0f ) color.g = 1.0f; + if ( color.b < 0.0f ) color.b = 0.0f; + if ( color.b > 1.0f ) color.b = 1.0f; + + r = (int)(color.r*32.0f); + g = (int)(color.g*32.0f); + b = (int)(color.b*32.0f); + + if ( r >= 32 ) r = 31; // 5 bits + if ( g >= 32 ) g = 31; // 5 bits + if ( b >= 32 ) b = 31; // 5 bits + + if ( ddsd->ddpfPixelFormat.dwRBitMask == 0xf800 ) // 565 ? + { + w = (r<<11)|(g<<6)|b; + } + else if ( ddsd->ddpfPixelFormat.dwRBitMask == 0x7c00 ) // 555 ? + { + w = (r<<10)|(g<<5)|b; + } + else + { + w = -1; // blanc + } + + pbSurf = (WORD*)ddsd->lpSurface; + pbSurf += ddsd->lPitch*y/2; + pbSurf += x; + + *pbSurf = w; + } + + if ( ddsd->ddpfPixelFormat.dwRGBBitCount == 32 ) // image .tga ? + { + LONG* pbSurf; + LONG r,g,b, w; + + if ( color.r < 0.0f ) color.r = 0.0f; + if ( color.r > 1.0f ) color.r = 1.0f; + if ( color.g < 0.0f ) color.g = 0.0f; + if ( color.g > 1.0f ) color.g = 1.0f; + if ( color.b < 0.0f ) color.b = 0.0f; + if ( color.b > 1.0f ) color.b = 1.0f; + + r = (int)(color.r*256.0f); + g = (int)(color.g*256.0f); + b = (int)(color.b*256.0f); + + if ( r >= 256 ) r = 255; // 8 bits + if ( g >= 256 ) g = 255; // 8 bits + if ( b >= 256 ) b = 255; // 8 bits + + if ( ddsd->ddpfPixelFormat.dwRBitMask == 0xff0000 ) + { + w = (r<<16)|(g<<8)|b; + + pbSurf = (LONG*)ddsd->lpSurface; + pbSurf += ddsd->lPitch*y/4; + pbSurf += x; + + *pbSurf &= 0xff000000; // garde canal alpha + *pbSurf |= w; + } + } +} + +// Donne un point dans une surface. + +D3DCOLORVALUE GetDot(DDSURFACEDESC2* ddsd, int x, int y) +{ + D3DCOLORVALUE color; + + if ( ddsd->ddpfPixelFormat.dwRGBBitCount == 16 ) + { + WORD* pbSurf; + WORD r,g,b, w; + + pbSurf = (WORD*)ddsd->lpSurface; + pbSurf += ddsd->lPitch*y/2; + pbSurf += x; + + w = *pbSurf; + + if ( ddsd->ddpfPixelFormat.dwRBitMask == 0xf800 ) // 565 ? + { + r = (w>>10)&0x003e; + g = (w>> 5)&0x003f; + b = (w<< 1)&0x003e; + } + else if ( ddsd->ddpfPixelFormat.dwRBitMask == 0x7c00 ) // 555 ? + { + r = (w>> 9)&0x003e; + g = (w>> 4)&0x003e; + b = (w<< 1)&0x003e; + } + else + { + r = 0; + g = 0; + b = 0; // noir + } + + color.r = (float)r/63.0f; + color.g = (float)g/63.0f; + color.b = (float)b/63.0f; + color.a = 0.0f; + return color; + } + + if ( ddsd->ddpfPixelFormat.dwRGBBitCount == 32 ) // image .tga ? + { + LONG* pbSurf; + LONG r,g,b, w; + + pbSurf = (LONG*)ddsd->lpSurface; + pbSurf += ddsd->lPitch*y/4; + pbSurf += x; + + w = *pbSurf; + + if ( ddsd->ddpfPixelFormat.dwRBitMask == 0xff0000 ) + { + r = (w>>16)&0x00ff; + g = (w>> 8)&0x00ff; + b = (w<< 0)&0x00ff; + } + else + { + r = 0; + g = 0; + b = 0; // noir + } + + color.r = (float)r/255.0f; + color.g = (float)g/255.0f; + color.b = (float)b/255.0f; + color.a = 0.0f; + return color; + } + + color.r = 0.0f; + color.g = 0.0f; + color.b = 0.0f; + color.a = 0.0f; // noir + return color; +} + +// Dessine toutes les ombres. + +// Il y a 1 pixel de recouvrement autour de chacune des 16 surfaces : +// +// |<----------------------->|<----------------------->|<---- ... +// 0 | 1 2 253 254|255 | +// |---|---|---|-- ... --|---|---|---| | +// 0 | 1 2 253 254|255 +// |---|---|---|-- ... --|---|---|---| +// +// On dessine donc dans des surfaces de 254x254 pixels. Le pixel de +// marge tout autour est dessiné à double (dans 2 surfaces adjacentes), +// pour que le filtrage produise des résultats identiques ! + +void CD3DEngine::RenderGroundSpot() +{ + LPDIRECTDRAWSURFACE7 surface; + DDSURFACEDESC2 ddsd; + WORD* pbSurf; + D3DCOLORVALUE color; + D3DVECTOR pos; + FPOINT min, max; + int s, i, j, dot, ix, iy, y; + float tu, tv, cx, cy, px, py, ppx, ppy; + float intensity, level; + char texName[20]; + BOOL bClear, bSet; + + if ( !m_bFirstGroundSpot && + m_groundMark.drawPos.x == m_groundMark.pos.x && + m_groundMark.drawPos.z == m_groundMark.pos.z && + m_groundMark.drawRadius == m_groundMark.radius && + m_groundMark.drawIntensity == m_groundMark.intensity ) return; + + for ( s=0 ; s<16 ; s++ ) + { + min.x = (s%4)*254.0f-1.0f; // 1 pixel de recouvrement + min.y = (s/4)*254.0f-1.0f; + max.x = min.x+254.0f+2.0f; + max.y = min.y+254.0f+2.0f; + + bClear = FALSE; + bSet = FALSE; + + // Calcule la zone à effacer. + dot = (int)(m_groundMark.drawRadius/2.0f); + + tu = (m_groundMark.drawPos.x+1600.0f)/3200.0f; + tv = (m_groundMark.drawPos.z+1600.0f)/3200.0f; // 0..1 + + cx = (tu*254.0f*4.0f)-0.5f; + cy = (tv*254.0f*4.0f)-0.5f; + + if ( dot == 0 ) + { + cx += 0.5f; + cy += 0.5f; + } + + px = cx-Mod(cx, 1.0f); + py = cy-Mod(cy, 1.0f); // multiple de 1 + + if ( m_bFirstGroundSpot || + ( m_groundMark.drawRadius != 0.0f && + px+dot >= min.x && py+dot >= min.y && + px-dot <= max.x && py-dot <= max.y ) ) + { + bClear = TRUE; + } + + // Calcule la zone à dessiner. + dot = (int)(m_groundMark.radius/2.0f); + + tu = (m_groundMark.pos.x+1600.0f)/3200.0f; + tv = (m_groundMark.pos.z+1600.0f)/3200.0f; // 0..1 + + cx = (tu*254.0f*4.0f)-0.5f; + cy = (tv*254.0f*4.0f)-0.5f; + + if ( dot == 0 ) + { + cx += 0.5f; + cy += 0.5f; + } + + px = cx-Mod(cx, 1.0f); + py = cy-Mod(cy, 1.0f); // multiple de 1 + + if ( m_groundMark.bUsed && + px+dot >= min.x && py+dot >= min.y && + px-dot <= max.x && py-dot <= max.y ) + { + bSet = TRUE; + } + + if ( bClear || bSet ) + { + // Charge le morceau. + sprintf(texName, "shadow%.2d.tga", s); + surface = D3DTextr_GetSurface(texName); + if ( surface == 0 ) continue; + + ZeroMemory(&ddsd, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + if ( surface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL) != DD_OK ) continue; + + // Efface en blanc tout le morceau. + if ( ddsd.ddpfPixelFormat.dwRGBBitCount == 16 ) + { + for ( y=0 ; y<(int)ddsd.dwHeight ; y++ ) + { + pbSurf = (WORD*)ddsd.lpSurface; + pbSurf += ddsd.lPitch*y/2; + memset(pbSurf, -1, ddsd.lPitch); // tout blanc + } + } + + // Dessine les nouvelles ombres. + for ( i=0 ; i max.x || py-dot > max.y ) continue; + + for ( iy=-dot ; iy<=dot ; iy++ ) + { + for ( ix=-dot ; ix<=dot ; ix++ ) + { + ppx = px+ix; + ppy = py+iy; + + if ( ppx < min.x || ppy < min.y || + ppx >= max.x || ppy >= max.y ) continue; + + if ( dot == 0 ) + { + intensity = 0.0f; + } + else + { + intensity = Length(ppx-cx, ppy-cy)/dot; + //? intensity = powf(intensity, m_groundSpot[i].smooth); + } + + color.r = m_groundSpot[i].color.r+intensity; + color.g = m_groundSpot[i].color.g+intensity; + color.b = m_groundSpot[i].color.b+intensity; + + ppx -= min.x; // relatif à la texture + ppy -= min.y; + AddDot(&ddsd, (int)ppx, (int)ppy, color); + } + } + } + else + { + for ( iy=0 ; iy<256 ; iy++ ) + { + for ( ix=0 ; ix<256 ; ix++ ) + { + pos.x = (256.0f*(s%4)+ix)*3200.0f/1024.0f - 1600.0f; + pos.z = (256.0f*(s/4)+iy)*3200.0f/1024.0f - 1600.0f; + pos.y = 0.0f; + level = m_terrain->RetFloorLevel(pos, TRUE); + if ( level < m_groundSpot[i].min || + level > m_groundSpot[i].max ) continue; + + if ( level > (m_groundSpot[i].max+m_groundSpot[i].min)/2.0f ) + { + intensity = 1.0f-(m_groundSpot[i].max-level)/m_groundSpot[i].smooth; + } + else + { + intensity = 1.0f-(level-m_groundSpot[i].min)/m_groundSpot[i].smooth; + } + if ( intensity < 0.0f ) intensity = 0.0f; + + color.r = m_groundSpot[i].color.r+intensity; + color.g = m_groundSpot[i].color.g+intensity; + color.b = m_groundSpot[i].color.b+intensity; + + AddDot(&ddsd, ix, iy, color); + } + } + } + } + + if ( bSet ) + { + dot = (int)(m_groundMark.radius/2.0f); + + tu = (m_groundMark.pos.x+1600.0f)/3200.0f; + tv = (m_groundMark.pos.z+1600.0f)/3200.0f; // 0..1 + + cx = (tu*254.0f*4.0f)-0.5f; + cy = (tv*254.0f*4.0f)-0.5f; + + if ( dot == 0 ) + { + cx += 0.5f; + cy += 0.5f; + } + + px = cx-Mod(cx, 1.0f); + py = cy-Mod(cy, 1.0f); // multiple de 1 + + for ( iy=-dot ; iy<=dot ; iy++ ) + { + for ( ix=-dot ; ix<=dot ; ix++ ) + { + ppx = px+ix; + ppy = py+iy; + + if ( ppx < min.x || ppy < min.y || + ppx >= max.x || ppy >= max.y ) continue; + + ppx -= min.x; // relatif à la texture + ppy -= min.y; + + intensity = 1.0f-Length((float)ix, (float)iy)/dot; + if ( intensity <= 0.0f ) continue; + intensity *= m_groundMark.intensity; + + j = (ix+dot) + (iy+dot)*m_groundMark.dx; + if ( m_groundMark.table[j] == 1 ) // vert ? + { + color.r = 1.0f-intensity; + color.g = 1.0f; + color.b = 1.0f-intensity; + AddDot(&ddsd, (int)ppx, (int)ppy, color); + } + if ( m_groundMark.table[j] == 2 ) // rouge ? + { + color.r = 1.0f; + color.g = 1.0f-intensity; + color.b = 1.0f-intensity; + AddDot(&ddsd, (int)ppx, (int)ppy, color); + } + } + } + } + + surface->Unlock(NULL); + } + } + + for ( i=0 ; iSetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE); + + D3DUtil_SetIdentityMatrix(matrix); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + ZeroMemory( &material, sizeof(D3DMATERIAL7) ); + material.diffuse.r = 1.0f; + material.diffuse.g = 1.0f; + material.diffuse.b = 1.0f; // blanc + material.ambient.r = 0.5f; + material.ambient.g = 0.5f; + material.ambient.b = 0.5f; + SetMaterial(material); + +#if _POLISH + SetTexture("textp.tga"); +#else + SetTexture("text.tga"); +#endif + + dp = 0.5f/256.0f; + ts.y = 192.0f/256.0f; + ti.y = 224.0f/256.0f; + ts.y += dp; + ti.y -= dp; + + n = D3DVECTOR(0.0f, 1.0f, 0.0f); + + startDeepView = m_deepView[m_rankView]*m_fogStart[m_rankView]; + endDeepView = m_deepView[m_rankView]; + + lastIntensity = -1.0f; + for ( i=0 ; i pos.y ) // caméra en dessus ? + { + height = m_eyePt.y-pos.y; + h = m_shadow[i].radius; + max = height*0.5f; + if ( h > max ) h = max; + if ( h > 4.0f ) h = 4.0f; + + D = Length(m_eyePt, pos); + if ( D >= endDeepView ) continue; + d = D*h/height; + + pos.x += (m_eyePt.x-pos.x)*d/D; + pos.z += (m_eyePt.z-pos.z)*d/D; + pos.y += h; + } + else // caméra en dessous ? + { + height = pos.y-m_eyePt.y; + h = m_shadow[i].radius; + max = height*0.1f; + if ( h > max ) h = max; + if ( h > 4.0f ) h = 4.0f; + + D = Length(m_eyePt, pos); + if ( D >= endDeepView ) continue; + d = D*h/height; + + pos.x += (m_eyePt.x-pos.x)*d/D; + pos.z += (m_eyePt.z-pos.z)*d/D; + pos.y -= h; + } + + // Le hFactor diminue l'intensité et augmente la taille plus + // l'objet est haut par rapport au sol. + hFactor = m_shadow[i].height/20.0f; + if ( hFactor < 0.0f ) hFactor = 0.0f; + if ( hFactor > 1.0f ) hFactor = 1.0f; + hFactor = powf(1.0f-hFactor, 2.0f); + if ( hFactor < 0.2f ) hFactor = 0.2f; + + radius = m_shadow[i].radius*1.5f; + radius *= 2.0f-hFactor; // plus grand si haut + radius *= 1.0f-d/D; // plus petit si proche + + if ( m_shadow[i].type == D3DSHADOWNORM ) + { + corner[0].x = +radius; + corner[0].z = +radius; + corner[0].y = 0.0f; + + corner[1].x = -radius; + corner[1].z = +radius; + corner[1].y = 0.0f; + + corner[2].x = +radius; + corner[2].z = -radius; + corner[2].y = 0.0f; + + corner[3].x = -radius; + corner[3].z = -radius; + corner[3].y = 0.0f; + + ts.x = 64.0f/256.0f; + ti.x = 96.0f/256.0f; + } + else + { + rot = RotatePoint(-m_shadow[i].angle, FPOINT(radius, radius)); + corner[0].x = rot.x; + corner[0].z = rot.y; + corner[0].y = 0.0f; + + rot = RotatePoint(-m_shadow[i].angle, FPOINT(-radius, radius)); + corner[1].x = rot.x; + corner[1].z = rot.y; + corner[1].y = 0.0f; + + rot = RotatePoint(-m_shadow[i].angle, FPOINT(radius, -radius)); + corner[2].x = rot.x; + corner[2].z = rot.y; + corner[2].y = 0.0f; + + rot = RotatePoint(-m_shadow[i].angle, FPOINT(-radius, -radius)); + corner[3].x = rot.x; + corner[3].z = rot.y; + corner[3].y = 0.0f; + + if ( m_shadow[i].type == D3DSHADOWWORM ) + { + ts.x = 96.0f/256.0f; + ti.x = 128.0f/256.0f; + } + else + { + ts.x = 64.0f/256.0f; + ti.x = 96.0f/256.0f; + } + } + + corner[0] = Cross(corner[0], m_shadow[i].normal); + corner[1] = Cross(corner[1], m_shadow[i].normal); + corner[2] = Cross(corner[2], m_shadow[i].normal); + corner[3] = Cross(corner[3], m_shadow[i].normal); + + corner[0] += pos; + corner[1] += pos; + corner[2] += pos; + corner[3] += pos; + + ts.x += dp; + ti.x -= dp; + + vertex[0] = D3DVERTEX2(corner[1], n, ts.x, ts.y); + vertex[1] = D3DVERTEX2(corner[0], n, ti.x, ts.y); + vertex[2] = D3DVERTEX2(corner[3], n, ts.x, ti.y); + vertex[3] = D3DVERTEX2(corner[2], n, ti.x, ti.y); + + intensity = (0.5f+m_shadow[i].intensity*0.5f)*hFactor; + + // Diminue l'intensité de l'ombre si on est dans la zone + // comprise entre le début et la fin du brouillard. + if ( D > startDeepView ) + { + intensity *= 1.0f-(D-startDeepView)/(endDeepView-startDeepView); + } + + // Diminue l'intensité si on est presque à l'horizontale + // avec l'ombre (ombre très platte). +//? if ( height < 4.0f ) intensity *= height/4.0f; + + if ( intensity == 0.0f ) continue; + + if ( lastIntensity != intensity ) // intensité changée ? + { + lastIntensity = intensity; + SetState(D3DSTATETTw, RetColor(intensity)); + } + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + AddStatisticTriangle(2); + } + + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, TRUE); +} + + +// Called once per frame, the call is the entry point for 3d +// rendering. This function sets up render states, clears the +// viewport, and renders the scene. + +HRESULT CD3DEngine::Render() +{ + D3DObjLevel1* p1; + D3DObjLevel2* p2; + D3DObjLevel3* p3; + D3DObjLevel4* p4; + D3DObjLevel5* p5; + D3DObjLevel6* p6; + D3DVERTEX2* pv; + int l1, l2, l3, l4, l5, objRank, tState; + CInterface* pInterface; + BOOL bTransparent; + D3DCOLOR color, tColor; + + if ( !m_bRender ) return S_OK; + + m_statisticTriangle = 0; + m_lastState = -1; + m_lastColor = 999; + m_lastTexture[0][0] = 0; + m_lastTexture[1][0] = 0; + ZeroMemory(&m_lastMaterial, sizeof(D3DMATERIAL7)); + + if ( m_bGroundSpot ) + { + RenderGroundSpot(); + } + + // Clear the viewport + if ( m_bSkyMode && m_cloud->RetLevel() != 0.0f ) // nuages ? + { + color = m_backgroundCloudDown; + } + else + { + color = m_backgroundColorDown; + } + m_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, + color, 1.0f, 0L ); + + m_light->LightUpdate(); + + // Begin the scene + if( FAILED( m_pD3DDevice->BeginScene() ) ) return S_OK; + + if ( m_bDrawWorld ) + { + DrawBackground(); // dessine l'arrière-plan + if ( m_bPlanetMode ) DrawPlanet(); // dessine les planètes + if ( m_bSkyMode ) m_cloud->Draw(); // dessine les nuages + + // Display the objects + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, TRUE); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZBIAS, F2DW(16)); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZFUNC, D3DCMP_LESSEQUAL); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, &m_matProj); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, TRUE); + + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGCOLOR, m_fogColor[m_rankView]); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGVERTEXMODE, D3DFOG_LINEAR); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGSTART, F2DW(m_deepView[m_rankView]*m_fogStart[m_rankView])); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGEND, F2DW(m_deepView[m_rankView])); + + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &m_matView); + + if ( m_bWaterMode ) m_water->DrawBack(); // dessine l'eau + + if ( m_bShadow ) + { + // Dessine le terrain. + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + SetTexture(p2->texName1, 0); + SetTexture(p2->texName2, 1); + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + objRank = p3->objRank; + if ( m_objectParam[objRank].type != TYPETERRAIN ) continue; + if ( !m_objectParam[objRank].bDrawWorld ) continue; + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, + &m_objectParam[objRank].transform); + if ( !IsVisible(objRank) ) continue; + m_light->LightUpdate(m_objectParam[objRank].type); + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + if ( m_objectParam[objRank].distance < p4->min || + m_objectParam[objRank].distance >= p4->max ) continue; + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; + SetMaterial(p6->material); + SetState(p6->state); + if ( p6->type == D3DTYPE6T ) + { + pv = &p6->vertex[0]; + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, + D3DFVF_VERTEX2, + pv, p6->totalUsed, + NULL); + m_statisticTriangle += p6->totalUsed/3; + } + if ( p6->type == D3DTYPE6S ) + { + pv = &p6->vertex[0]; + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, + D3DFVF_VERTEX2, + pv, p6->totalUsed, + NULL); + m_statisticTriangle += p6->totalUsed-2; + } + } + } + } + } + } + + DrawShadow(); // dessine les ombres + } + + // Dessine les objets. + bTransparent = FALSE; + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + SetTexture(p2->texName1, 0); + SetTexture(p2->texName2, 1); + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + objRank = p3->objRank; + if ( m_bShadow && m_objectParam[objRank].type == TYPETERRAIN ) continue; + if ( !m_objectParam[objRank].bDrawWorld ) continue; + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, + &m_objectParam[objRank].transform); + if ( !IsVisible(objRank) ) continue; + m_light->LightUpdate(m_objectParam[objRank].type); + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + if ( m_objectParam[objRank].distance < p4->min || + m_objectParam[objRank].distance >= p4->max ) continue; + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; + SetMaterial(p6->material); + if ( m_objectParam[objRank].transparency != 0.0f ) // transparent ? + { + bTransparent = TRUE; + continue; + } + SetState(p6->state); + if ( p6->type == D3DTYPE6T ) + { + pv = &p6->vertex[0]; + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, + D3DFVF_VERTEX2, + pv, p6->totalUsed, + NULL); + m_statisticTriangle += p6->totalUsed/3; + } + if ( p6->type == D3DTYPE6S ) + { + pv = &p6->vertex[0]; + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, + D3DFVF_VERTEX2, + pv, p6->totalUsed, + NULL); + m_statisticTriangle += p6->totalUsed-2; + } + } + } + } + } + } + + if ( bTransparent ) + { + if ( m_bStateColor ) + { + tState = D3DSTATETTb|D3DSTATE2FACE; + tColor = 0x44444444; + } + else + { + tState = D3DSTATETTb; + tColor = 0x88888888; + } + + // Dessine les objets transparents. + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + SetTexture(p2->texName1, 0); + SetTexture(p2->texName2, 1); + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + objRank = p3->objRank; + if ( m_bShadow && m_objectParam[objRank].type == TYPETERRAIN ) continue; + if ( !m_objectParam[objRank].bDrawWorld ) continue; + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, + &m_objectParam[objRank].transform); + if ( !IsVisible(objRank) ) continue; + m_light->LightUpdate(m_objectParam[objRank].type); + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + if ( m_objectParam[objRank].distance < p4->min || + m_objectParam[objRank].distance >= p4->max ) continue; + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; + SetMaterial(p6->material); + if ( m_objectParam[objRank].transparency == 0.0f ) continue; + SetState(tState, tColor); + if ( p6->type == D3DTYPE6T ) + { + pv = &p6->vertex[0]; + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, + D3DFVF_VERTEX2, + pv, p6->totalUsed, + NULL); + m_statisticTriangle += p6->totalUsed/3; + } + if ( p6->type == D3DTYPE6S ) + { + pv = &p6->vertex[0]; + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, + D3DFVF_VERTEX2, + pv, p6->totalUsed, + NULL); + m_statisticTriangle += p6->totalUsed-2; + } + } + } + } + } + } + } + + m_light->LightUpdate(TYPETERRAIN); + if ( m_bWaterMode ) m_water->DrawSurf(); // dessine l'eau +//? m_cloud->Draw(); // dessine les nuages + + m_particule->DrawParticule(SH_WORLD); // dessine les particules du monde 3D + m_blitz->Draw(); // dessine les éclairs + if ( m_bLensMode ) DrawFrontsize(); // dessine l'avant-plan + if ( !m_bOverFront ) DrawOverColor(); // dessine la couleur d'avant-plan + } + + // Dessine l'interface utilisateur par-dessus la scène. + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_AMBIENT, 0xffffffff); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &m_matViewInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, &m_matProjInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &m_matWorldInterface); + + pInterface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + if ( pInterface != 0 ) + { + pInterface->Draw(); // dessine toute l'interface + } + m_particule->DrawParticule(SH_INTERFACE); // dessine les particules de l'interface + + if ( m_bDrawFront ) + { + // Display the objects + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, TRUE); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZBIAS, F2DW(16)); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZFUNC, D3DCMP_LESSEQUAL); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, &m_matProj); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_AMBIENT, m_ambiantColor[m_rankView]); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, TRUE); + + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGCOLOR, m_fogColor[m_rankView]); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGVERTEXMODE, D3DFOG_LINEAR); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGSTART, F2DW(m_deepView[m_rankView]*m_fogStart[m_rankView])); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGEND, F2DW(m_deepView[m_rankView])); + + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &m_matView); + + p1 = m_objectPointer; + for ( l1=0 ; l1totalUsed ; l1++ ) + { + p2 = p1->table[l1]; + if ( p2 == 0 ) continue; + SetTexture(p2->texName1, 0); + SetTexture(p2->texName2, 1); + for ( l2=0 ; l2totalUsed ; l2++ ) + { + p3 = p2->table[l2]; + if ( p3 == 0 ) continue; + objRank = p3->objRank; + if ( !m_objectParam[objRank].bDrawFront ) continue; + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, + &m_objectParam[objRank].transform); + if ( !IsVisible(objRank) ) continue; + m_light->LightUpdate(m_objectParam[objRank].type); + for ( l3=0 ; l3totalUsed ; l3++ ) + { + p4 = p3->table[l3]; + if ( p4 == 0 ) continue; + if ( m_objectParam[objRank].distance < p4->min || + m_objectParam[objRank].distance >= p4->max ) continue; + for ( l4=0 ; l4totalUsed ; l4++ ) + { + p5 = p4->table[l4]; + if ( p5 == 0 ) continue; + for ( l5=0 ; l5totalUsed ; l5++ ) + { + p6 = p5->table[l5]; + if ( p6 == 0 ) continue; + SetMaterial(p6->material); + SetState(p6->state); + if ( p6->type == D3DTYPE6T ) + { + pv = &p6->vertex[0]; + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, + D3DFVF_VERTEX2, + pv, p6->totalUsed, + NULL); + m_statisticTriangle += p6->totalUsed/3; + } + if ( p6->type == D3DTYPE6S ) + { + pv = &p6->vertex[0]; + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, + D3DFVF_VERTEX2, + pv, p6->totalUsed, + NULL); + m_statisticTriangle += p6->totalUsed-2; + } + } + } + } + } + } + + m_particule->DrawParticule(SH_FRONT); // dessine les particules du monde 3D + + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_AMBIENT, 0xffffffff); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &m_matViewInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, &m_matProjInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &m_matWorldInterface); + } + + if ( m_bOverFront ) DrawOverColor(); // dessine la couleur d'avant-plan + + if ( m_mouseType != D3DMOUSEHIDE ) + { + DrawMouse(); + } + + // End the scene. + m_pD3DDevice->EndScene(); + + DrawHilite(); + return S_OK; +} + + +// Dessine le dégradé d'arrière-plan. + +void CD3DEngine::DrawBackground() +{ + if ( m_bSkyMode && m_cloud->RetLevel() != 0.0f ) // nuages ? + { + if ( m_backgroundCloudUp != m_backgroundCloudDown ) // dégradé ? + { + DrawBackgroundGradient(m_backgroundCloudUp, m_backgroundCloudDown); + } + } + else + { + if ( m_backgroundColorUp != m_backgroundColorDown ) // dégradé ? + { + DrawBackgroundGradient(m_backgroundColorUp, m_backgroundColorDown); + } + } + + if ( m_bBackForce || (m_bSkyMode && m_backgroundName[0] != 0) ) + { + DrawBackgroundImage(); // image + } +} + +// Dessine le dégradé d'arrière-plan. + +void CD3DEngine::DrawBackgroundGradient(D3DCOLOR up, D3DCOLOR down) +{ + D3DLVERTEX vertex[4]; // 2 triangles + D3DCOLOR color[3]; + FPOINT p1, p2; + + p1.x = 0.0f; + p1.y = 0.5f; + p2.x = 1.0f; + p2.y = 1.0f; + + color[0] = up; + color[1] = down; + color[2] = 0x00000000; + +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE ); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_AMBIENT, 0xffffffff); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE ); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + + SetTexture("xxx.tga"); // pas de texture + SetState(D3DSTATENORMAL); + + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &m_matViewInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, &m_matProjInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &m_matWorldInterface); + + vertex[0] = D3DLVERTEX(D3DVECTOR(p1.x, p1.y, 0.0f), color[1],color[2], 0.0f,0.0f); + vertex[1] = D3DLVERTEX(D3DVECTOR(p1.x, p2.y, 0.0f), color[0],color[2], 0.0f,0.0f); + vertex[2] = D3DLVERTEX(D3DVECTOR(p2.x, p1.y, 0.0f), color[1],color[2], 0.0f,0.0f); + vertex[3] = D3DLVERTEX(D3DVECTOR(p2.x, p2.y, 0.0f), color[0],color[2], 0.0f,0.0f); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_LVERTEX, vertex, 4, NULL); + AddStatisticTriangle(2); +} + +// Dessine une partie de l'image d'arrière-plan. + +void CD3DEngine::DrawBackgroundImageQuarter(FPOINT p1, FPOINT p2, char *name) +{ + D3DVERTEX2 vertex[4]; // 2 triangles + D3DVECTOR n; + float u1, u2, v1, v2, h, a; + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + + if ( m_bBackgroundFull ) + { + u1 = 0.0f; + v1 = 0.0f; + u2 = 1.0f; + v2 = 1.0f; + + if ( m_bBackgroundQuarter ) + { + u1 += 0.5f/512.0f; + v1 += 0.5f/384.0f; + u2 -= 0.5f/512.0f; + v2 -= 0.5f/384.0f; + } + } + else + { + h = 0.5f; // partie visible verticalement (1=tout) + a = m_eyeDirV-PI*0.15f; + if ( a > PI ) a -= PI*2.0f; // a = -PI..PI + if ( a > PI/4.0f ) a = PI/4.0f; + if ( a < -PI/4.0f ) a = -PI/4.0f; + + u1 = -m_eyeDirH/PI; + u2 = u1+1.0f/PI; +//? u1 = -m_eyeDirH/(PI*2.0f); +//? u2 = u1+1.0f/(PI*2.0f); + + v1 = (1.0f-h)*(0.5f+a/(2.0f*PI/4.0f))+0.1f; + v2 = v1+h; + } + +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE ); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_AMBIENT, 0xffffffff); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE ); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + + SetTexture(name); + SetState(D3DSTATEWRAP); + + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &m_matViewInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, &m_matProjInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &m_matWorldInterface); + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, u1,v2); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, u1,v1); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, u2,v2); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, u2,v1); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + AddStatisticTriangle(2); +} + +// Dessine l'image d'arrière-plan. + +void CD3DEngine::DrawBackgroundImage() +{ + FPOINT p1, p2; + char name[50]; + + if ( m_bBackgroundQuarter ) + { + p1.x = 0.0f; + p1.y = 0.5f; + p2.x = 0.5f; + p2.y = 1.0f; + QuarterName(name, m_backgroundName, 0); + DrawBackgroundImageQuarter(p1, p2, name); + + p1.x = 0.5f; + p1.y = 0.5f; + p2.x = 1.0f; + p2.y = 1.0f; + QuarterName(name, m_backgroundName, 1); + DrawBackgroundImageQuarter(p1, p2, name); + + p1.x = 0.0f; + p1.y = 0.0f; + p2.x = 0.5f; + p2.y = 0.5f; + QuarterName(name, m_backgroundName, 2); + DrawBackgroundImageQuarter(p1, p2, name); + + p1.x = 0.5f; + p1.y = 0.0f; + p2.x = 1.0f; + p2.y = 0.5f; + QuarterName(name, m_backgroundName, 3); + DrawBackgroundImageQuarter(p1, p2, name); + } + else + { + p1.x = 0.0f; + p1.y = 0.0f; + p2.x = 1.0f; + p2.y = 1.0f; + DrawBackgroundImageQuarter(p1, p2, m_backgroundName); + } +} + +// Dessine toutes les planètes. + +void CD3DEngine::DrawPlanet() +{ + if ( !m_planet->PlanetExist() ) return; + +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE ); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_AMBIENT, 0xffffffff); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE ); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &m_matViewInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, &m_matProjInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &m_matWorldInterface); + + m_planet->Draw(); // dessine les planètes +} + +// Dessine l'image d'avant-plan. + +void CD3DEngine::DrawFrontsize() +{ + D3DVERTEX2 vertex[4]; // 2 triangles + D3DVECTOR n; + FPOINT p1, p2; + float u1, u2, v1, v2; + + if ( m_frontsizeName[0] == 0 ) return; + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + + p1.x = 0.0f; + p1.y = 0.0f; + p2.x = 1.0f; + p2.y = 1.0f; + + u1 = -m_eyeDirH/(PI*0.6f)+PI*0.5f; + u2 = u1+0.50f; + + v1 = 0.2f; + v2 = 1.0f; + +#if 0 + char s[100]; + sprintf(s, "h=%.2f v=%.2f u=%.2f;%.2f v=%.2f;%.2f", m_eyeDirH, m_eyeDirV, u1, u2, v1, v2); + SetInfoText(3, s); +#endif + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, u1,v2); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, u1,v1); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, u2,v2); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, u2,v1); + +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_AMBIENT, 0xffffffff); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE ); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + + SetTexture(m_frontsizeName); + SetState(D3DSTATECLAMP|D3DSTATETTb); + + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &m_matViewInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, &m_matProjInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &m_matWorldInterface); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + AddStatisticTriangle(2); +} + +// Dessine la couleur d'avant-plan. + +void CD3DEngine::DrawOverColor() +{ + D3DLVERTEX vertex[4]; // 2 triangles + D3DCOLOR color[3]; + FPOINT p1, p2; + + if ( !m_bStateColor ) return; + if ( (m_overColor == 0x00000000 && m_overMode == D3DSTATETCb) || + (m_overColor == 0xffffffff && m_overMode == D3DSTATETCw) ) return; + + p1.x = 0.0f; + p1.y = 0.0f; + p2.x = 1.0f; + p2.y = 1.0f; + + color[0] = m_overColor; + color[1] = m_overColor; + color[2] = 0x00000000; + +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE ); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_AMBIENT, 0xffffffff); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE ); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE); + + SetTexture("xxx.tga"); // pas de texture + SetState(m_overMode); + + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &m_matViewInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, &m_matProjInterface); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &m_matWorldInterface); + + vertex[0] = D3DLVERTEX(D3DVECTOR(p1.x, p1.y, 0.0f), color[1],color[2], 0.0f,0.0f); + vertex[1] = D3DLVERTEX(D3DVECTOR(p1.x, p2.y, 0.0f), color[0],color[2], 0.0f,0.0f); + vertex[2] = D3DLVERTEX(D3DVECTOR(p2.x, p1.y, 0.0f), color[1],color[2], 0.0f,0.0f); + vertex[3] = D3DLVERTEX(D3DVECTOR(p2.x, p2.y, 0.0f), color[0],color[2], 0.0f,0.0f); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_LVERTEX, vertex, 4, NULL); + AddStatisticTriangle(2); +} + + +// Donne la liste des rangs des objets et sous-objets sélectionnés. + +void CD3DEngine::SetHiliteRank(int *rankList) +{ + int i; + + i = 0; + while ( *rankList != -1 ) + { + m_hiliteRank[i++] = *rankList++; + } + m_hiliteRank[i] = -1; // terminateur +} + +// Donne la bbox dans l'écran 2D d'un objet quelconque. + +BOOL CD3DEngine::GetBBox2D(int objRank, FPOINT &min, FPOINT &max) +{ + D3DVECTOR p, pp; + int i; + + min.x = 1000000.0f; + min.y = 1000000.0f; + max.x = -1000000.0f; + max.y = -1000000.0f; + + for ( i=0 ; i<8 ; i++ ) + { + if ( i & (1<<0) ) p.x = m_objectParam[objRank].bboxMin.x; + else p.x = m_objectParam[objRank].bboxMax.x; + if ( i & (1<<1) ) p.y = m_objectParam[objRank].bboxMin.y; + else p.y = m_objectParam[objRank].bboxMax.y; + if ( i & (1<<2) ) p.z = m_objectParam[objRank].bboxMin.z; + else p.z = m_objectParam[objRank].bboxMax.z; + if ( TransformPoint(pp, objRank, p) ) + { + if ( pp.x < min.x ) min.x = pp.x; + if ( pp.x > max.x ) max.x = pp.x; + if ( pp.y < min.y ) min.y = pp.y; + if ( pp.y > max.y ) max.y = pp.y; + } + } + + if ( min.x == 1000000.0f || + min.y == 1000000.0f || + max.x == -1000000.0f || + max.y == -1000000.0f ) return FALSE; + + return TRUE; +} + +// Détermine le rectangle de l'objet mis en évidence, qui sera +// dessiné par CD3DApplication. + +void CD3DEngine::DrawHilite() +{ + FPOINT min, max, omin, omax; + int i; + + min.x = 1000000.0f; + min.y = 1000000.0f; + max.x = -1000000.0f; + max.y = -1000000.0f; + + i = 0; + while ( m_hiliteRank[i] != -1 ) + { + if ( GetBBox2D(m_hiliteRank[i++], omin, omax) ) + { + min.x = Min(min.x, omin.x); + min.y = Min(min.y, omin.y); + max.x = Max(max.x, omax.x); + max.y = Max(max.y, omax.y); + } + } + + if ( min.x == 1000000.0f || + min.y == 1000000.0f || + max.x == -1000000.0f || + max.y == -1000000.0f ) + { + m_bHilite = FALSE; // pas de mise en évidence + } + else + { + m_hiliteP1 = min; + m_hiliteP2 = max; + m_bHilite = TRUE; + } +} + +// Donne le rectangle de mise en évidence à dessiner +// par CD3DApplication. + +BOOL CD3DEngine::GetHilite(FPOINT &p1, FPOINT &p2) +{ + p1 = m_hiliteP1; + p2 = m_hiliteP2; + return m_bHilite; +} + + +// Ajoute qq triangles rendus pour les statistiques. + +void CD3DEngine::AddStatisticTriangle(int nb) +{ + m_statisticTriangle += nb; +} + +// Retourne le nombre de triangles rendus. + +int CD3DEngine::RetStatisticTriangle() +{ + return m_statisticTriangle; +} + +BOOL CD3DEngine::GetSpriteCoord(int &x, int &y) +{ + D3DVIEWPORT7 vp; + D3DVECTOR v, vv; + + return FALSE; + //? + vv = D3DVECTOR(0.0f, 0.0f, 0.0f); + if ( !TransformPoint(v, 20*20+1, vv) ) return FALSE; + + m_pD3DDevice->GetViewport(&vp); + v.x *= vp.dwWidth/2; + v.y *= vp.dwHeight/2; + v.x = v.x+vp.dwWidth/2; + v.y = vp.dwHeight-(v.y+vp.dwHeight/2); + + x = (int)v.x; + y = (int)v.y; + return TRUE; +} + + +// Teste s'il faut exclure un point. + +BOOL IsExcludeColor(FPOINT *pExclu, int x, int y) +{ + int i; + + i = 0; + while ( pExclu[i+0].x != 0.0f || pExclu[i+0].y != 0.0f || + pExclu[i+1].y != 0.0f || pExclu[i+1].y != 0.0f ) + { + if ( x >= (int)(pExclu[i+0].x*256.0f) && + x < (int)(pExclu[i+1].x*256.0f) && + y >= (int)(pExclu[i+0].y*256.0f) && + y < (int)(pExclu[i+1].y*256.0f) ) return TRUE; // exclure + + i += 2; + } + + return FALSE; // point à inclure +} + +// Change la couleur d'une texture. + +BOOL CD3DEngine::ChangeColor(char *name, + D3DCOLORVALUE colorRef1, D3DCOLORVALUE colorNew1, + D3DCOLORVALUE colorRef2, D3DCOLORVALUE colorNew2, + float tolerance1, float tolerance2, + FPOINT ts, FPOINT ti, + FPOINT *pExclu, float shift, BOOL bHSV) +{ + LPDIRECTDRAWSURFACE7 surface; + DDSURFACEDESC2 ddsd; + D3DCOLORVALUE color; + ColorHSV cr1, cn1, cr2, cn2, c; + int dx, dy, x, y, sx, sy, ex, ey; + + D3DTextr_Invalidate(name); + LoadTexture(name); // recharge la texture initiale + + if ( colorRef1.r == colorNew1.r && + colorRef1.g == colorNew1.g && + colorRef1.b == colorNew1.b && + colorRef2.r == colorNew2.r && + colorRef2.g == colorNew2.g && + colorRef2.b == colorNew2.b ) return TRUE; + + surface = D3DTextr_GetSurface(name); + if ( surface == 0 ) return FALSE; + + ZeroMemory(&ddsd, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + if ( surface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL) != DD_OK ) return FALSE; + + dx = ddsd.dwWidth; + dy = ddsd.dwHeight; + + sx = (int)(ts.x*dx); + sy = (int)(ts.y*dy); + ex = (int)(ti.x*dx); + ey = (int)(ti.y*dy); + + RGB2HSV(colorRef1, cr1); + RGB2HSV(colorNew1, cn1); + RGB2HSV(colorRef2, cr2); + RGB2HSV(colorNew2, cn2); + + for ( y=sy ; y 0.01f && Abs(c.h-cr1.h) < tolerance1 ) + { + c.h += cn1.h-cr1.h; + c.s += cn1.s-cr1.s; + c.v += cn1.v-cr1.v; + if ( c.h < 0.0f ) c.h -= 1.0f; + if ( c.h > 1.0f ) c.h += 1.0f; + HSV2RGB(c, color); + color.r += shift; + color.g += shift; + color.b += shift; + ::SetDot(&ddsd, x, y, color); + } + else + if ( tolerance2 != -1.0f && + c.s > 0.01f && Abs(c.h-cr2.h) < tolerance2 ) + { + c.h += cn2.h-cr2.h; + c.s += cn2.s-cr2.s; + c.v += cn2.v-cr2.v; + if ( c.h < 0.0f ) c.h -= 1.0f; + if ( c.h > 1.0f ) c.h += 1.0f; + HSV2RGB(c, color); + color.r += shift; + color.g += shift; + color.b += shift; + ::SetDot(&ddsd, x, y, color); + } + } + else + { + if ( Abs(color.r-colorRef1.r)+ + Abs(color.g-colorRef1.g)+ + Abs(color.b-colorRef1.b) < tolerance1*3.0f ) + { + color.r = colorNew1.r+color.r-colorRef1.r+shift; + color.g = colorNew1.g+color.g-colorRef1.g+shift; + color.b = colorNew1.b+color.b-colorRef1.b+shift; + ::SetDot(&ddsd, x, y, color); + } + else + if ( tolerance2 != -1 && + Abs(color.r-colorRef2.r)+ + Abs(color.g-colorRef2.g)+ + Abs(color.b-colorRef2.b) < tolerance2*3.0f ) + { + color.r = colorNew2.r+color.r-colorRef2.r+shift; + color.g = colorNew2.g+color.g-colorRef2.g+shift; + color.b = colorNew2.b+color.b-colorRef2.b+shift; + ::SetDot(&ddsd, x, y, color); + } + } + } + } + + surface->Unlock(NULL); + return TRUE; +} + + +// Ouvre une image pour travailler directement dedans. + +BOOL CD3DEngine::OpenImage(char *name) +{ +//? D3DTextr_Invalidate(name); +//? LoadTexture(name); + + m_imageSurface = D3DTextr_GetSurface(name); + if ( m_imageSurface == 0 ) return FALSE; + + ZeroMemory(&m_imageDDSD, sizeof(DDSURFACEDESC2)); + m_imageDDSD.dwSize = sizeof(DDSURFACEDESC2); + if ( m_imageSurface->Lock(NULL, &m_imageDDSD, DDLOCK_WAIT, NULL) != DD_OK ) + { + return FALSE; + } + + if ( m_imageDDSD.ddpfPixelFormat.dwRGBBitCount != 16 ) + { + m_imageSurface->Unlock(NULL); + return FALSE; + } + + m_imageDX = m_imageDDSD.dwWidth; + m_imageDY = m_imageDDSD.dwHeight; + + return TRUE; +} + +// Copie l'image de travail. + +BOOL CD3DEngine::CopyImage() +{ + WORD* pbSurf; + int y; + + if ( m_imageCopy == 0 ) + { + m_imageCopy = (WORD*)malloc(m_imageDX*m_imageDY*sizeof(WORD)); + } + + for ( y=0 ; y 0 ) + { + for ( y=0 ; y=-dx ; x-- ) + { + m_imageCopy[x+y*m_imageDX] = m_imageCopy[x+dx+y*m_imageDX]; + } + } + } + + if ( dy > 0 ) + { + for ( y=0 ; y=-dy ; y-- ) + { + memcpy(m_imageCopy+y*m_imageDX, m_imageCopy+(y+dy)*m_imageDX, m_imageDX*sizeof(WORD)); + } + } + + return TRUE; +} + +// Dessine un point dans l'image de travail. + +BOOL CD3DEngine::SetDot(int x, int y, D3DCOLORVALUE color) +{ + WORD* pbSurf; + WORD r,g,b, w; + + if ( x < 0 || x >= m_imageDX || + y < 0 || y >= m_imageDY ) return FALSE; + +#if 1 + if ( color.r < 0.0f ) color.r = 0.0f; + if ( color.r > 1.0f ) color.r = 1.0f; + if ( color.g < 0.0f ) color.g = 0.0f; + if ( color.g > 1.0f ) color.g = 1.0f; + if ( color.b < 0.0f ) color.b = 0.0f; + if ( color.b > 1.0f ) color.b = 1.0f; + + r = (int)(color.r*32.0f); + g = (int)(color.g*32.0f); + b = (int)(color.b*32.0f); + + if ( r >= 32 ) r = 31; // 5 bits + if ( g >= 32 ) g = 31; // 5 bits + if ( b >= 32 ) b = 31; // 5 bits +#else + r = (int)(color.r*31.0f); + g = (int)(color.g*31.0f); + b = (int)(color.b*31.0f); +#endif + + if ( m_imageDDSD.ddpfPixelFormat.dwRBitMask == 0xf800 ) // 565 ? + { + w = (r<<11)|(g<<6)|b; + } + else if ( m_imageDDSD.ddpfPixelFormat.dwRBitMask == 0x7c00 ) // 555 ? + { + w = (r<<10)|(g<<5)|b; + } + else + { + w = -1; // blanc + } + + pbSurf = (WORD*)m_imageDDSD.lpSurface; + pbSurf += m_imageDDSD.lPitch*y/2; + pbSurf += x; + + *pbSurf = w; + return TRUE; +} + +// Ferme l'image de travail. + +BOOL CD3DEngine::CloseImage() +{ + m_imageSurface->Unlock(NULL); + return TRUE; +} + + +// Ecrit un fichier .BMP copie d'écran. + +BOOL CD3DEngine::WriteScreenShot(char *filename, int width, int height) +{ + return m_app->WriteScreenShot(filename, width, height); +} + +// Initialise un hDC sur la surface de rendu. + +BOOL CD3DEngine::GetRenderDC(HDC &hDC) +{ + return m_app->GetRenderDC(hDC); +} + +// Libère le hDC sur la surface de rendu. + +BOOL CD3DEngine::ReleaseRenderDC(HDC &hDC) +{ + return m_app->ReleaseRenderDC(hDC); +} + +PBITMAPINFO CD3DEngine::CreateBitmapInfoStruct(HBITMAP hBmp) +{ + return m_app->CreateBitmapInfoStruct(hBmp); +} + +BOOL CD3DEngine::CreateBMPFile(LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC) +{ + return m_app->CreateBMPFile(pszFile, pbi, hBMP, hDC); +} + +// Retourne le pointeur à la classe CText. + +CText* CD3DEngine::RetText() +{ + return m_text; +} + + +// Gestion du texte d'informations affiché dans la fenêtre. + +void CD3DEngine::SetInfoText(int line, char* text) +{ + strcpy(m_infoText[line], text); +} + +char* CD3DEngine::RetInfoText(int line) +{ + return m_infoText[line]; +} + + + +// Spécifie la focale de la caméra. +// 0.75 = normal +// 1.50 = grand-angle + +void CD3DEngine::SetFocus(float focus) +{ + D3DVIEWPORT7 vp; + float fAspect; + + m_focus = focus; + + if ( m_pD3DDevice != 0 ) + { + m_pD3DDevice->GetViewport(&vp); + m_dim.x = vp.dwWidth; + m_dim.y = vp.dwHeight; + } + + fAspect = ((float)m_dim.y) / m_dim.x; +//? D3DUtil_SetProjectionMatrix(m_matProj, m_focus, fAspect, 0.5f, m_deepView[m_rankView]); + D3DUtil_SetProjectionMatrix(m_matProj, m_focus, fAspect, 0.5f, m_deepView[0]); +} + +float CD3DEngine::RetFocus() +{ + return m_focus; +} + +// + +void CD3DEngine::UpdateMatProj() +{ + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION, &m_matProj); +} + + + +// Ignore les touches pressées. + +void CD3DEngine::FlushPressKey() +{ + m_app->FlushPressKey(); +} + +// Remet les touches par défaut. + +void CD3DEngine::ResetKey() +{ + m_app->ResetKey(); +} + +// Modifie une touche. + +void CD3DEngine::SetKey(int keyRank, int option, int key) +{ + m_app->SetKey(keyRank, option, key); +} + +// Donne une touche. + +int CD3DEngine::RetKey(int keyRank, int option) +{ + return m_app->RetKey(keyRank, option); +} + + +// Utilise le joystick ou le clavier. + +void CD3DEngine::SetJoystick(BOOL bEnable) +{ + m_app->SetJoystick(bEnable); +} + +BOOL CD3DEngine::RetJoystick() +{ + return m_app->RetJoystick(); +} + + +void CD3DEngine::SetDebugMode(BOOL bMode) +{ + m_app->SetDebugMode(bMode); +} + +BOOL CD3DEngine::RetDebugMode() +{ + return m_app->RetDebugMode(); +} + +BOOL CD3DEngine::RetSetupMode() +{ + return m_app->RetSetupMode(); +} + + +// Indique si un point est visible. + +BOOL CD3DEngine::IsVisiblePoint(const D3DVECTOR &pos) +{ + return ( Length(m_eyePt, pos) <= m_deepView[0] ); +} + + +// Initialize scene objects. + +HRESULT CD3DEngine::InitDeviceObjects() +{ + // Set miscellaneous render states. + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_DITHERENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SPECULARENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_SHADEMODE, D3DSHADE_GOURAUD); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_FILLMODE, D3DFILL_SOLID); + + // Set up the textures. + D3DTextr_RestoreAllTextures(m_pD3DDevice); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFN_LINEAR); + m_pD3DDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_LINEAR); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_MINFILTER, D3DTFN_LINEAR); + m_pD3DDevice->SetTextureStageState(1, D3DTSS_MAGFILTER, D3DTFG_LINEAR); + + SetFocus(m_focus); + + // Définitions des matrices pour l'interface. + D3DUtil_SetIdentityMatrix(m_matWorldInterface); + + D3DUtil_SetIdentityMatrix(m_matViewInterface); + m_matViewInterface._41 = -0.5f; + m_matViewInterface._42 = -0.5f; + m_matViewInterface._43 = 1.0f; + + D3DUtil_SetIdentityMatrix(m_matProjInterface); + m_matProjInterface._11 = 2.0f; + m_matProjInterface._22 = 2.0f; + m_matProjInterface._34 = 1.0f; + m_matProjInterface._43 = -1.0f; + m_matProjInterface._44 = 0.0f; + + return S_OK; +} + + +// Restore all surfaces. + +HRESULT CD3DEngine::RestoreSurfaces() +{ + return S_OK; +} + + +// Called when the app is exitting, or the device is being changed, +// this function deletes any device dependant objects. + +HRESULT CD3DEngine::DeleteDeviceObjects() +{ + D3DTextr_InvalidateAllTextures(); + return S_OK; +} + + +// Called before the app exits, this function gives the app the chance +// to cleanup after itself. + +HRESULT CD3DEngine::FinalCleanup() +{ + return S_OK; +} + + +// Overrrides the main WndProc, so the sample can do custom message +// handling (e.g. processing mouse, keyboard, or menu commands). + +LRESULT CD3DEngine::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ +#if 0 + if ( uMsg == WM_KEYDOWN ) // Alt+key ? + { + if ( wParam == 'Q' ) + { + debug_blend1 ++; + if ( debug_blend1 > 13 ) debug_blend1 = 0; + } + if ( wParam == 'W' ) + { + debug_blend2 ++; + if ( debug_blend2 > 13 ) debug_blend2 = 0; + } + if ( wParam == 'E' ) + { + debug_blend3 ++; + if ( debug_blend3 > 13 ) debug_blend3 = 0; + } + if ( wParam == 'R' ) + { + debug_blend4 ++; + if ( debug_blend4 > 13 ) debug_blend4 = 0; + } + char s[100]; + sprintf(s, "src=%d, dest=%d, src=%d, dest=%d", debug_blend1, debug_blend2, debug_blend3, debug_blend4); + SetInfoText(4, s); + } +#endif + +#if 1 + if ( uMsg == WM_SYSKEYDOWN ) // Alt+key ? + { + if ( wParam == VK_F7 ) // Alt+F7 ? + { + s_resol = 0; + } + if ( wParam == VK_F8 ) // Alt+F8 ? + { + s_resol = 1; + } + if ( wParam == VK_F9 ) // Alt+F9 ? + { + s_resol = 2; + } + if ( wParam == VK_F10 ) // Alt+F10 ? + { + s_resol = 3; + } + } +#endif + + return 0; +} + + +// Gestion de la souris. + +void CD3DEngine::MoveMousePos(FPOINT pos) +{ + m_mousePos = pos; + m_app->SetMousePos(pos); +} + +void CD3DEngine::SetMousePos(FPOINT pos) +{ + m_mousePos = pos; +} + +FPOINT CD3DEngine::RetMousePos() +{ + return m_mousePos; +} + +void CD3DEngine::SetMouseType(D3DMouse type) +{ + m_mouseType = type; +} + +D3DMouse CD3DEngine::RetMouseType() +{ + return m_mouseType; +} + +void CD3DEngine::SetMouseHide(BOOL bHide) +{ + if ( m_bMouseHide == bHide ) return; + + if ( bHide ) // cache la souris ? + { + m_bNiceMouse = m_app->RetNiceMouse(); + if ( !m_bNiceMouse ) + { + m_app->SetNiceMouse(TRUE); + } + m_bMouseHide = TRUE; + } + else // montre la souris ? + { + if ( !m_bNiceMouse ) + { + m_app->SetNiceMouse(FALSE); + } + m_bMouseHide = FALSE; + } +} + +BOOL CD3DEngine::RetMouseHide() +{ + return m_bMouseHide; +} + +void CD3DEngine::SetNiceMouse(BOOL bNice) +{ + m_app->SetNiceMouse(bNice); +} + +BOOL CD3DEngine::RetNiceMouse() +{ + return m_app->RetNiceMouse(); +} + +BOOL CD3DEngine::RetNiceMouseCap() +{ + return m_app->RetNiceMouseCap(); +} + +// Dessine le sprite de la souris. + +void CD3DEngine::DrawMouse() +{ + D3DMATERIAL7 material; + FPOINT pos, ppos, dim; + int i; + + typedef struct + { + D3DMouse type; + int icon1, icon2, iconShadow; + int mode1, mode2; + float hotx, hoty; + } + Mouse; + + static Mouse table[] = + { + { D3DMOUSENORM, 0, 1,32, D3DSTATETTw, D3DSTATETTb, 1.0f, 1.0f}, + { D3DMOUSEWAIT, 2, 3,33, D3DSTATETTw, D3DSTATETTb, 8.0f, 12.0f}, + { D3DMOUSEHAND, 4, 5,34, D3DSTATETTw, D3DSTATETTb, 7.0f, 2.0f}, + { D3DMOUSENO, 6, 7,35, D3DSTATETTw, D3DSTATETTb, 10.0f, 10.0f}, + { D3DMOUSEEDIT, 8, 9,-1, D3DSTATETTb, D3DSTATETTw, 6.0f, 10.0f}, + { D3DMOUSECROSS, 10,11,-1, D3DSTATETTb, D3DSTATETTw, 10.0f, 10.0f}, + { D3DMOUSEMOVEV, 12,13,-1, D3DSTATETTb, D3DSTATETTw, 5.0f, 11.0f}, + { D3DMOUSEMOVEH, 14,15,-1, D3DSTATETTb, D3DSTATETTw, 11.0f, 5.0f}, + { D3DMOUSEMOVED, 16,17,-1, D3DSTATETTb, D3DSTATETTw, 9.0f, 9.0f}, + { D3DMOUSEMOVEI, 18,19,-1, D3DSTATETTb, D3DSTATETTw, 9.0f, 9.0f}, + { D3DMOUSEMOVE, 20,21,-1, D3DSTATETTb, D3DSTATETTw, 11.0f, 11.0f}, + { D3DMOUSETARGET, 22,23,-1, D3DSTATETTb, D3DSTATETTw, 15.0f, 15.0f}, + { D3DMOUSESCROLLL, 24,25,43, D3DSTATETTb, D3DSTATETTw, 2.0f, 9.0f}, + { D3DMOUSESCROLLR, 26,27,44, D3DSTATETTb, D3DSTATETTw, 17.0f, 9.0f}, + { D3DMOUSESCROLLU, 28,29,45, D3DSTATETTb, D3DSTATETTw, 9.0f, 2.0f}, + { D3DMOUSESCROLLD, 30,31,46, D3DSTATETTb, D3DSTATETTw, 9.0f, 17.0f}, + { D3DMOUSEHIDE }, + }; + + if ( m_bMouseHide ) return; + if ( !m_app->RetNiceMouse() ) return; // souris windows ? + + ZeroMemory( &material, sizeof(D3DMATERIAL7) ); + material.diffuse.r = 1.0f; + material.diffuse.g = 1.0f; + material.diffuse.b = 1.0f; + material.ambient.r = 0.5f; + material.ambient.g = 0.5f; + material.ambient.b = 0.5f; + SetMaterial(material); + + SetTexture("mouse.tga"); + + i = 0; + while ( table[i].type != D3DMOUSEHIDE ) + { + if ( m_mouseType == table[i].type ) + { + dim.x = 0.05f*0.75f; + dim.y = 0.05f; + + pos.x = m_mousePos.x - (table[i].hotx*dim.x)/32.0f; + pos.y = m_mousePos.y - ((32.0f-table[i].hoty)*dim.y)/32.0f; + + ppos.x = pos.x+(4.0f/640.0f); + ppos.y = pos.y-(3.0f/480.0f); + SetState(D3DSTATETTw); + DrawSprite(ppos, dim, table[i].iconShadow); + + SetState(table[i].mode1); + DrawSprite(pos, dim, table[i].icon1); + + SetState(table[i].mode2); + DrawSprite(pos, dim, table[i].icon2); + break; + } + i ++; + } +} + +// Dessine le sprite de la souris. + +void CD3DEngine::DrawSprite(FPOINT pos, FPOINT dim, int icon) +{ + D3DVERTEX2 vertex[4]; // 2 triangles + FPOINT p1, p2; + D3DVECTOR n; + float u1, u2, v1, v2, dp; + + if ( icon == -1 ) return; + + p1.x = pos.x; + p1.y = pos.y; + p2.x = pos.x + dim.x; + p2.y = pos.y + dim.y; + + u1 = (32.0f/256.0f)*(icon%8); + v1 = (32.0f/256.0f)*(icon/8); // u-v texture + u2 = (32.0f/256.0f)+u1; + v2 = (32.0f/256.0f)+v1; + + dp = 0.5f/256.0f; + u1 += dp; + v1 += dp; + u2 -= dp; + v2 -= dp; + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, u1,v2); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, u1,v1); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, u2,v2); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, u2,v1); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + AddStatisticTriangle(2); +} + diff --git a/src/d3dengine.h b/src/d3dengine.h new file mode 100644 index 00000000..8bc2c4e6 --- /dev/null +++ b/src/d3dengine.h @@ -0,0 +1,669 @@ +// D3DEngine.h + +#ifndef _D3DENGINE_H_ +#define _D3DENGINE_H_ + +#include "D3DApp.h" + + + +class CInstanceManager; +class CObject; +class CLight; +class CText; +class CParticule; +class CWater; +class CCloud; +class CBlitz; +class CPlanet; +class CSound; +class CTerrain; + + +#define D3DMAXOBJECT 1200 +#define D3DMAXSHADOW 500 +#define D3DMAXGROUNDSPOT 100 + + +enum D3DTypeObj +{ + TYPENULL = 0, // object inexistant + TYPETERRAIN = 1, // terrain + TYPEFIX = 2, // objet fixe + TYPEVEHICULE = 3, // objet mobile + TYPEDESCENDANT = 4, // partie d'un objet mobile + TYPEQUARTZ = 5, // objet fixe de type quartz + TYPEMETAL = 6, // objet fixe de type métalique +}; + +enum D3DTypeTri +{ + D3DTYPE6T = 1, // triangles + D3DTYPE6S = 2, // surfaces +}; + +enum D3DMaping +{ + D3DMAPPINGX = 1, + D3DMAPPINGY = 2, + D3DMAPPINGZ = 3, + D3DMAPPING1X = 4, + D3DMAPPING1Y = 5, + D3DMAPPING1Z = 6, +}; + +enum D3DMouse +{ + D3DMOUSEHIDE = 0, // pas de souris + D3DMOUSENORM = 1, + D3DMOUSEWAIT = 2, + D3DMOUSEEDIT = 3, + D3DMOUSEHAND = 4, + D3DMOUSECROSS = 5, + D3DMOUSESHOW = 6, + D3DMOUSENO = 7, + D3DMOUSEMOVE = 8, // + + D3DMOUSEMOVEH = 9, // - + D3DMOUSEMOVEV = 10, // | + D3DMOUSEMOVED = 11, // / + D3DMOUSEMOVEI = 12, // \ + D3DMOUSESCROLLL = 13, // << + D3DMOUSESCROLLR = 14, // >> + D3DMOUSESCROLLU = 15, // ^ + D3DMOUSESCROLLD = 16, // v + D3DMOUSETARGET = 17, +}; + +enum D3DShadowType +{ + D3DSHADOWNORM = 0, + D3DSHADOWWORM = 1, +}; + + +#define D3DSTATENORMAL 0 // matériaux opaque normal +#define D3DSTATETTb (1<<0) // transparent selon texture (noir=rien) +#define D3DSTATETTw (1<<1) // transparent selon texture (blanc=rien) +#define D3DSTATETD (1<<2) // transparent selon couleur diffuse +#define D3DSTATEWRAP (1<<3) // texture wrappée +#define D3DSTATECLAMP (1<<4) // texture bordée d'une couleur unie +#define D3DSTATELIGHT (1<<5) // texture lumineuse (ambiance max) +#define D3DSTATEDUALb (1<<6) // double texturage noir +#define D3DSTATEDUALw (1<<7) // double texturage blanc +#define D3DSTATEPART1 (1<<8) // partie 1 (ne pas changer, dans .MOD !) +#define D3DSTATEPART2 (1<<9) // partie 2 +#define D3DSTATEPART3 (1<<10) // partie 3 +#define D3DSTATEPART4 (1<<11) // partie 4 +#define D3DSTATE2FACE (1<<12) // mode double-face +#define D3DSTATEALPHA (1<<13) // image avec canal alpha +#define D3DSTATESECOND (1<<14) // utilise tjrs 2ème étage de texturage +#define D3DSTATEFOG (1<<15) // force le brouillard +#define D3DSTATETCb (1<<16) // transparent selon couleur (noir=rien) +#define D3DSTATETCw (1<<17) // transparent selon couleur (blanc=rien) + + +typedef struct +{ + D3DVERTEX2 triangle[3]; + D3DMATERIAL7 material; + int state; + char texName1[20]; + char texName2[20]; +} +D3DTriangle; + + +typedef struct +{ + int totalPossible; + int totalUsed; + D3DMATERIAL7 material; + int state; + D3DTypeTri type; // D3DTYPE6x + D3DVERTEX2 vertex[1]; +} +D3DObjLevel6; + +typedef struct +{ + int totalPossible; + int totalUsed; + int reserve; + D3DObjLevel6* table[1]; +} +D3DObjLevel5; + +typedef struct +{ + int totalPossible; + int totalUsed; + float min, max; + D3DObjLevel5* table[1]; +} +D3DObjLevel4; + +typedef struct +{ + int totalPossible; + int totalUsed; + int objRank; + D3DObjLevel4* table[1]; +} +D3DObjLevel3; + +typedef struct +{ + int totalPossible; + int totalUsed; + char texName1[20]; + char texName2[20]; + D3DObjLevel3* table[1]; +} +D3DObjLevel2; + +typedef struct +{ + int totalPossible; + int totalUsed; + D3DObjLevel2* table[1]; +} +D3DObjLevel1; + + +typedef struct +{ + char bUsed; // TRUE -> objet existe + char bVisible; // TRUE -> objet visible + char bDrawWorld; // TRUE -> dessine derrière l'interface + char bDrawFront; // TRUE -> dessine devant l'interface + int totalTriangle; // nb de triangles utilisés + D3DTypeObj type; // type de l'objet (TYPE*) + D3DMATRIX transform; // matrice de transformation + float distance; // distance point de vue - origine + D3DVECTOR bboxMin; // bounding box de l'objet + D3DVECTOR bboxMax; // (l'origine 0;0;0 est tjrs incluse) + float radius; // rayon de la sphère à l'origine + int shadowRank; // rang de l'ombre associée + float transparency; // transparence de l'objet (0..1) +} +D3DObject; + +typedef struct +{ + char bUsed; // TRUE -> objet existe + char bHide; // TRUE -> ombre invisible (objet porté par ex.) + int objRank; // rang de l'objet + D3DShadowType type; // type de l'ombre + D3DVECTOR pos; // position pour l'ombre + D3DVECTOR normal; // normale au terrain + float angle; // angle de l'ombre + float radius; // rayon de l'ombre + float intensity; // intensité de l'ombre + float height; // hauteur depuis le sol +} +D3DShadow; + +typedef struct +{ + char bUsed; // TRUE -> objet existe + D3DCOLORVALUE color; // couleur de l'ombre + float min, max; // altitudes min/max + float smooth; // zone de transition + D3DVECTOR pos; // position pour l'ombre + float radius; // rayon de l'ombre + D3DVECTOR drawPos; // position pour l'ombre dessinée + float drawRadius; // rayon de l'ombre dessinée +} +D3DGroundSpot; + +typedef struct +{ + char bUsed; // TRUE -> objet existe + char bDraw; // TRUE -> marque dessinée + int phase; // 1=croissance, 2=fixe, 3=décroissance + float delay[3]; // délais pour les 3 phases + float fix; // temps fixe + D3DVECTOR pos; // position pour marques + float radius; // rayon des marques + float intensity; // intensité couleur + D3DVECTOR drawPos; // position pour marques dessinées + float drawRadius; // rayon des marques dessinées + float drawIntensity; // intensité dessinée + int dx, dy; // dimensions table + char* table; // pointeur à la table +} +D3DGroundMark; + + + +class CD3DEngine +{ +public: + CD3DEngine(CInstanceManager *iMan, CD3DApplication *app); + ~CD3DEngine(); + + void SetD3DDevice(LPDIRECT3DDEVICE7 device); + LPDIRECT3DDEVICE7 RetD3DDevice(); + + void SetTerrain(CTerrain* terrain); + + BOOL WriteProfile(); + + void SetPause(BOOL bPause); + BOOL RetPause(); + + void SetMovieLock(BOOL bLock); + BOOL RetMovieLock(); + + void SetShowStat(BOOL bShow); + BOOL RetShowStat(); + + void SetRenderEnable(BOOL bEnable); + + HRESULT OneTimeSceneInit(); + HRESULT InitDeviceObjects(); + HRESULT DeleteDeviceObjects(); + HRESULT RestoreSurfaces(); + HRESULT Render(); + HRESULT FrameMove(float rTime); + void StepSimul(float rTime); + HRESULT FinalCleanup(); + void AddStatisticTriangle(int nb); + int RetStatisticTriangle(); + void SetHiliteRank(int *rankList); + BOOL GetHilite(FPOINT &p1, FPOINT &p2); + BOOL GetSpriteCoord(int &x, int &y); + void SetInfoText(int line, char* text); + char* RetInfoText(int line); + LRESULT MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + void FirstExecuteAdapt(BOOL bFirst); + int GetVidMemTotal(); + BOOL IsVideo8MB(); + BOOL IsVideo32MB(); + + BOOL EnumDevices(char *bufDevices, int lenDevices, char *bufModes, int lenModes, int &totalDevices, int &selectDevices, int &totalModes, int &selectModes); + BOOL RetFullScreen(); + BOOL ChangeDevice(char *device, char *mode, BOOL bFull); + + D3DMATRIX* RetMatView(); + D3DMATRIX* RetMatLeftView(); + D3DMATRIX* RetMatRightView(); + + void TimeInit(); + void TimeEnterGel(); + void TimeExitGel(); + float TimeGet(); + + int RetRestCreate(); + int CreateObject(); + void FlushObject(); + BOOL DeleteObject(int objRank); + BOOL SetDrawWorld(int objRank, BOOL bDraw); + BOOL SetDrawFront(int objRank, BOOL bDraw); + BOOL AddTriangle(int objRank, D3DVERTEX2* vertex, int nb, const D3DMATERIAL7 &mat, int state, char* texName1, char* texName2, float min, float max, BOOL bGlobalUpdate); + BOOL AddSurface(int objRank, D3DVERTEX2* vertex, int nb, const D3DMATERIAL7 &mat, int state, char* texName1, char* texName2, float min, float max, BOOL bGlobalUpdate); + BOOL AddQuick(int objRank, D3DObjLevel6* buffer, char* texName1, char* texName2, float min, float max, BOOL bGlobalUpdate); + D3DObjLevel6* SearchTriangle(int objRank, const D3DMATERIAL7 &mat, int state, char* texName1, char* texName2, float min, float max); + void ChangeLOD(); + BOOL ChangeSecondTexture(int objRank, char* texName2); + int RetTotalTriangles(int objRank); + int GetTriangles(int objRank, float min, float max, D3DTriangle* buffer, int size, float percent); + BOOL GetBBox(int objRank, D3DVECTOR &min, D3DVECTOR &max); + BOOL ChangeTextureMapping(int objRank, const D3DMATERIAL7 &mat, int state, char* texName1, char* texName2, float min, float max, D3DMaping mode, float au, float bu, float av, float bv); + BOOL TrackTextureMapping(int objRank, const D3DMATERIAL7 &mat, int state, char* texName1, char* texName2, float min, float max, D3DMaping mode, float pos, float factor, float tl, float ts, float tt); + BOOL SetObjectTransform(int objRank, const D3DMATRIX &transform); + BOOL GetObjectTransform(int objRank, D3DMATRIX &transform); + BOOL SetObjectType(int objRank, D3DTypeObj type); + D3DTypeObj RetObjectType(int objRank); + BOOL SetObjectTransparency(int objRank, float value); + + BOOL ShadowCreate(int objRank); + void ShadowDelete(int objRank); + BOOL SetObjectShadowHide(int objRank, BOOL bHide); + BOOL SetObjectShadowType(int objRank, D3DShadowType type); + BOOL SetObjectShadowPos(int objRank, const D3DVECTOR &pos); + BOOL SetObjectShadowNormal(int objRank, const D3DVECTOR &n); + BOOL SetObjectShadowAngle(int objRank, float angle); + BOOL SetObjectShadowRadius(int objRank, float radius); + BOOL SetObjectShadowIntensity(int objRank, float intensity); + BOOL SetObjectShadowHeight(int objRank, float h); + float RetObjectShadowRadius(int objRank); + + void GroundSpotFlush(); + int GroundSpotCreate(); + void GroundSpotDelete(int rank); + BOOL SetObjectGroundSpotPos(int rank, const D3DVECTOR &pos); + BOOL SetObjectGroundSpotRadius(int rank, float radius); + BOOL SetObjectGroundSpotColor(int rank, D3DCOLORVALUE color); + BOOL SetObjectGroundSpotMinMax(int rank, float min, float max); + BOOL SetObjectGroundSpotSmooth(int rank, float smooth); + + int GroundMarkCreate(D3DVECTOR pos, float radius, float delay1, float delay2, float delay3, int dx, int dy, char* table); + BOOL GroundMarkDelete(int rank); + + void Update(); + + void SetViewParams(const D3DVECTOR &vEyePt, const D3DVECTOR &vLookatPt, const D3DVECTOR &vUpVec, FLOAT fEyeDistance); + + BOOL FreeTexture(char* name); + BOOL LoadTexture(char* name, int stage=0); + BOOL LoadAllTexture(); + + void SetLimitLOD(int rank, float limit); + float RetLimitLOD(int rank, BOOL bLast=FALSE); + + void SetTerrainVision(float vision); + + void SetGroundSpot(BOOL bMode); + BOOL RetGroundSpot(); + void SetShadow(BOOL bMode); + BOOL RetShadow(); + void SetDirty(BOOL bMode); + BOOL RetDirty(); + void SetFog(BOOL bMode); + BOOL RetFog(); + BOOL RetStateColor(); + + void SetSecondTexture(int texNum); + int RetSecondTexture(); + + void SetRankView(int rank); + int RetRankView(); + + void SetDrawWorld(BOOL bDraw); + void SetDrawFront(BOOL bDraw); + + void SetAmbiantColor(D3DCOLOR color, int rank=0); + D3DCOLOR RetAmbiantColor(int rank=0); + + void SetWaterAddColor(D3DCOLORVALUE color); + D3DCOLORVALUE RetWaterAddColor(); + + void SetFogColor(D3DCOLOR color, int rank=0); + D3DCOLOR RetFogColor(int rank=0); + + void SetDeepView(float length, int rank=0, BOOL bRef=FALSE); + float RetDeepView(int rank=0); + + void SetFogStart(float start, int rank=0); + float RetFogStart(int rank=0); + + void SetBackground(char *name, D3DCOLOR up=0, D3DCOLOR down=0, D3DCOLOR cloudUp=0, D3DCOLOR cloudDown=0, BOOL bFull=FALSE, BOOL bQuarter=FALSE); + void RetBackground(char *name, D3DCOLOR &up, D3DCOLOR &down, D3DCOLOR &cloudUp, D3DCOLOR &cloudDown, BOOL &bFull, BOOL &bQuarter); + void SetFrontsizeName(char *name); + void SetOverFront(BOOL bFront); + void SetOverColor(D3DCOLOR color=0, int mode=D3DSTATETCb); + + void SetParticuleDensity(float value); + float RetParticuleDensity(); + float ParticuleAdapt(float factor); + + void SetClippingDistance(float value); + float RetClippingDistance(); + + void SetObjectDetail(float value); + float RetObjectDetail(); + + void SetGadgetQuantity(float value); + float RetGadgetQuantity(); + + void SetTextureQuality(int value); + int RetTextureQuality(); + + void SetTotoMode(BOOL bPresent); + BOOL RetTotoMode(); + + void SetLensMode(BOOL bPresent); + BOOL RetLensMode(); + + void SetWaterMode(BOOL bPresent); + BOOL RetWaterMode(); + + void SetBlitzMode(BOOL bPresent); + BOOL RetBlitzMode(); + + void SetSkyMode(BOOL bPresent); + BOOL RetSkyMode(); + + void SetBackForce(BOOL bPresent); + BOOL RetBackForce(); + + void SetPlanetMode(BOOL bPresent); + BOOL RetPlanetMode(); + + void SetLightMode(BOOL bPresent); + BOOL RetLightMode(); + + void SetEditIndentMode(BOOL bAuto); + BOOL RetEditIndentMode(); + + void SetEditIndentValue(int value); + int RetEditIndentValue(); + + void SetSpeed(float speed); + float RetSpeed(); + + void SetTracePrecision(float factor); + float RetTracePrecision(); + + void SetFocus(float focus); + float RetFocus(); + D3DVECTOR RetEyePt(); + D3DVECTOR RetLookatPt(); + float RetEyeDirH(); + float RetEyeDirV(); + POINT RetDim(); + void UpdateMatProj(); + + void ApplyChange(); + + void FlushPressKey(); + void ResetKey(); + void SetKey(int keyRank, int option, int key); + int RetKey(int keyRank, int option); + + void SetJoystick(BOOL bEnable); + BOOL RetJoystick(); + + void SetDebugMode(BOOL bMode); + BOOL RetDebugMode(); + BOOL RetSetupMode(); + + BOOL IsVisiblePoint(const D3DVECTOR &pos); + + int DetectObject(FPOINT mouse); + void SetState(int state, D3DCOLOR color=0xffffffff); + void SetTexture(char *name, int stage=0); + void SetMaterial(const D3DMATERIAL7 &mat); + + void MoveMousePos(FPOINT pos); + void SetMousePos(FPOINT pos); + FPOINT RetMousePos(); + void SetMouseType(D3DMouse type); + D3DMouse RetMouseType(); + void SetMouseHide(BOOL bHide); + BOOL RetMouseHide(); + void SetNiceMouse(BOOL bNice); + BOOL RetNiceMouse(); + BOOL RetNiceMouseCap(); + + CText* RetText(); + + BOOL ChangeColor(char *name, D3DCOLORVALUE colorRef1, D3DCOLORVALUE colorNew1, D3DCOLORVALUE colorRef2, D3DCOLORVALUE colorNew2, float tolerance1, float tolerance2, FPOINT ts, FPOINT ti, FPOINT *pExclu=0, float shift=0.0f, BOOL bHSV=FALSE); + BOOL OpenImage(char *name); + BOOL CopyImage(); + BOOL LoadImage(); + BOOL ScrollImage(int dx, int dy); + BOOL SetDot(int x, int y, D3DCOLORVALUE color); + BOOL CloseImage(); + BOOL WriteScreenShot(char *filename, int width, int height); + BOOL GetRenderDC(HDC &hDC); + BOOL ReleaseRenderDC(HDC &hDC); + PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp); + BOOL CreateBMPFile(LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC); + +protected: + void MemSpace1(D3DObjLevel1 *&p, int nb); + void MemSpace2(D3DObjLevel2 *&p, int nb); + void MemSpace3(D3DObjLevel3 *&p, int nb); + void MemSpace4(D3DObjLevel4 *&p, int nb); + void MemSpace5(D3DObjLevel5 *&p, int nb); + void MemSpace6(D3DObjLevel6 *&p, int nb); + + D3DObjLevel2* AddLevel1(D3DObjLevel1 *&p1, char* texName1, char* texName2); + D3DObjLevel3* AddLevel2(D3DObjLevel2 *&p2, int objRank); + D3DObjLevel4* AddLevel3(D3DObjLevel3 *&p3, float min, float max); + D3DObjLevel5* AddLevel4(D3DObjLevel4 *&p4, int reserve); + D3DObjLevel6* AddLevel5(D3DObjLevel5 *&p5, D3DTypeTri type, const D3DMATERIAL7 &mat, int state, int nb); + + BOOL IsVisible(int objRank); + BOOL DetectBBox(int objRank, FPOINT mouse); + BOOL DetectTriangle(FPOINT mouse, D3DVERTEX2 *triangle, int objRank, float &dist); + BOOL TransformPoint(D3DVECTOR &p2D, int objRank, D3DVECTOR p3D); + void ComputeDistance(); + void UpdateGeometry(); + void RenderGroundSpot(); + void DrawShadow(); + void DrawBackground(); + void DrawBackgroundGradient(D3DCOLOR up, D3DCOLOR down); + void DrawBackgroundImageQuarter(FPOINT p1, FPOINT p2, char *name); + void DrawBackgroundImage(); + void DrawPlanet(); + void DrawFrontsize(); + void DrawOverColor(); + BOOL GetBBox2D(int objRank, FPOINT &min, FPOINT &max); + void DrawHilite(); + void DrawMouse(); + void DrawSprite(FPOINT pos, FPOINT dim, int icon); + +protected: + CInstanceManager* m_iMan; + CD3DApplication* m_app; + LPDIRECT3DDEVICE7 m_pD3DDevice; + CText* m_text; + CLight* m_light; + CParticule* m_particule; + CWater* m_water; + CCloud* m_cloud; + CBlitz* m_blitz; + CPlanet* m_planet; + CSound* m_sound; + CTerrain* m_terrain; + + int m_blackSrcBlend[2]; + int m_blackDestBlend[2]; + int m_whiteSrcBlend[2]; + int m_whiteDestBlend[2]; + int m_diffuseSrcBlend[2]; + int m_diffuseDestBlend[2]; + int m_alphaSrcBlend[2]; + int m_alphaDestBlend[2]; + + D3DMATRIX m_matProj; + D3DMATRIX m_matLeftView; + D3DMATRIX m_matRightView; + D3DMATRIX m_matView; + float m_focus; + + D3DMATRIX m_matWorldInterface; + D3DMATRIX m_matProjInterface; + D3DMATRIX m_matViewInterface; + + DWORD m_baseTime; + DWORD m_stopTime; + float m_absTime; + float m_lastTime; + float m_speed; + BOOL m_bPause; + BOOL m_bRender; + BOOL m_bMovieLock; + + POINT m_dim; + POINT m_lastDim; + D3DObjLevel1* m_objectPointer; + int m_objectParamTotal; + D3DObject* m_objectParam; + int m_shadowTotal; + D3DShadow* m_shadow; + D3DGroundSpot* m_groundSpot; + D3DGroundMark m_groundMark; + D3DVECTOR m_eyePt; + D3DVECTOR m_lookatPt; + float m_eyeDirH; + float m_eyeDirV; + int m_rankView; + D3DCOLOR m_ambiantColor[2]; + D3DCOLOR m_backColor[2]; + D3DCOLOR m_fogColor[2]; + float m_deepView[2]; + float m_fogStart[2]; + D3DCOLORVALUE m_waterAddColor; + int m_statisticTriangle; + BOOL m_bUpdateGeometry; + char m_infoText[10][200]; + int m_alphaMode; + BOOL m_bStateColor; + BOOL m_bForceStateColor; + BOOL m_bGroundSpot; + BOOL m_bShadow; + BOOL m_bDirty; + BOOL m_bFog; + BOOL m_bFirstGroundSpot; + int m_secondTexNum; + char m_backgroundName[50]; + D3DCOLOR m_backgroundColorUp; + D3DCOLOR m_backgroundColorDown; + D3DCOLOR m_backgroundCloudUp; + D3DCOLOR m_backgroundCloudDown; + BOOL m_bBackgroundFull; + BOOL m_bBackgroundQuarter; + BOOL m_bOverFront; + D3DCOLOR m_overColor; + int m_overMode; + char m_frontsizeName[50]; + BOOL m_bDrawWorld; + BOOL m_bDrawFront; + float m_limitLOD[2]; + float m_particuleDensity; + float m_clippingDistance; + float m_lastClippingDistance; + float m_objectDetail; + float m_lastObjectDetail; + float m_terrainVision; + float m_gadgetQuantity; + int m_textureQuality; + BOOL m_bTotoMode; + BOOL m_bLensMode; + BOOL m_bWaterMode; + BOOL m_bSkyMode; + BOOL m_bBackForce; + BOOL m_bPlanetMode; + BOOL m_bLightMode; + BOOL m_bEditIndentMode; + int m_editIndentValue; + float m_tracePrecision; + + int m_hiliteRank[100]; + BOOL m_bHilite; + FPOINT m_hiliteP1; + FPOINT m_hiliteP2; + + int m_lastState; + D3DCOLOR m_lastColor; + char m_lastTexture[2][50]; + D3DMATERIAL7 m_lastMaterial; + + FPOINT m_mousePos; + D3DMouse m_mouseType; + BOOL m_bMouseHide; + BOOL m_bNiceMouse; + + LPDIRECTDRAWSURFACE7 m_imageSurface; + DDSURFACEDESC2 m_imageDDSD; + WORD* m_imageCopy; + int m_imageDX; + int m_imageDY; +}; + + +#endif //_D3DENGINE_H_ diff --git a/src/d3denum.cpp b/src/d3denum.cpp new file mode 100644 index 00000000..7f24c98b --- /dev/null +++ b/src/d3denum.cpp @@ -0,0 +1,603 @@ +//----------------------------------------------------------------------------- +// File: D3DEnum.cpp +// +// Desc: Functions to enumerate DDraw/D3D drivers, devices, and modes. +// +// Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved +//----------------------------------------------------------------------------- +#define STRICT +#include +#include +#include +#include "D3DEnum.h" +#include "D3DUtil.h" // For DEBUG_MSG +#include "D3DRes.h" // For dialog controls + + + + +//----------------------------------------------------------------------------- +// Global data for the enumerator functions +//----------------------------------------------------------------------------- +static HRESULT (*g_fnAppConfirmFn)(DDCAPS*, D3DDEVICEDESC7*) = NULL; + +static D3DEnum_DeviceInfo g_pDeviceList[20]; +static DWORD g_dwNumDevicesEnumerated = 0L; +static DWORD g_dwNumDevices = 0L; + + + + +//----------------------------------------------------------------------------- +// Name: SortModesCallback() +// Desc: Callback function for sorting display modes. +//----------------------------------------------------------------------------- +int SortModesCallback( const VOID* arg1, const VOID* arg2 ) +{ + DDSURFACEDESC2* p1 = (DDSURFACEDESC2*)arg1; + DDSURFACEDESC2* p2 = (DDSURFACEDESC2*)arg2; + + if( p1->dwWidth < p2->dwWidth ) + return -1; + if( p1->dwWidth > p2->dwWidth ) + return +1; + + if( p1->dwHeight < p2->dwHeight ) + return -1; + if( p1->dwHeight > p2->dwHeight ) + return +1; + + if( p1->ddpfPixelFormat.dwRGBBitCount < p2->ddpfPixelFormat.dwRGBBitCount ) + return -1; + if( p1->ddpfPixelFormat.dwRGBBitCount > p2->ddpfPixelFormat.dwRGBBitCount ) + return +1; + + return 0; +} + + + + +//----------------------------------------------------------------------------- +// Name: ModeEnumCallback() +// Desc: Callback function for enumerating display modes. +//----------------------------------------------------------------------------- +static HRESULT WINAPI ModeEnumCallback( DDSURFACEDESC2* pddsd, + VOID* pParentInfo ) +{ + D3DEnum_DeviceInfo* pDevice = (D3DEnum_DeviceInfo*)pParentInfo; + + // Reallocate storage for the modes + DDSURFACEDESC2* pddsdNewModes = new DDSURFACEDESC2[pDevice->dwNumModes+1]; + memcpy( pddsdNewModes, pDevice->pddsdModes, + pDevice->dwNumModes * sizeof(DDSURFACEDESC2) ); + delete pDevice->pddsdModes; + pDevice->pddsdModes = pddsdNewModes; + + // Add the new mode + pDevice->pddsdModes[pDevice->dwNumModes++] = (*pddsd); + + return DDENUMRET_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: DeviceEnumCallback() +// Desc: Callback function for enumerating devices +//----------------------------------------------------------------------------- +static HRESULT WINAPI DeviceEnumCallback( TCHAR* strDesc, TCHAR* strName, + D3DDEVICEDESC7* pDesc, + VOID* pParentInfo ) +{ + // Keep track of # of devices that were enumerated + g_dwNumDevicesEnumerated++; + + D3DEnum_DeviceInfo* pDriverInfo = (D3DEnum_DeviceInfo*)pParentInfo; + D3DEnum_DeviceInfo* pDeviceInfo = &g_pDeviceList[g_dwNumDevices]; + ZeroMemory( pDeviceInfo, sizeof(D3DEnum_DeviceInfo) ); + + // Select either the HAL or HEL device desc: + pDeviceInfo->bHardware = pDesc->dwDevCaps & D3DDEVCAPS_HWRASTERIZATION; + memcpy( &pDeviceInfo->ddDeviceDesc, pDesc, sizeof(D3DDEVICEDESC7) ); + + // Set up device info for this device + pDeviceInfo->bDesktopCompatible = pDriverInfo->bDesktopCompatible; + pDeviceInfo->ddDriverCaps = pDriverInfo->ddDriverCaps; + pDeviceInfo->ddHELCaps = pDriverInfo->ddHELCaps; + pDeviceInfo->guidDevice = pDesc->deviceGUID; + pDeviceInfo->pDeviceGUID = &pDeviceInfo->guidDevice; + pDeviceInfo->pddsdModes = new DDSURFACEDESC2[pDriverInfo->dwNumModes]; + + // Copy the driver GUID and description for the device + if( pDriverInfo->pDriverGUID ) + { + pDeviceInfo->guidDriver = pDriverInfo->guidDriver; + pDeviceInfo->pDriverGUID = &pDeviceInfo->guidDriver; + lstrcpyn( pDeviceInfo->strDesc, pDriverInfo->strDesc, 39 ); + } + else + { + pDeviceInfo->pDriverGUID = NULL; + lstrcpyn( pDeviceInfo->strDesc, strName, 39 ); + } + +//? if( strstr(strName, "T&L") != 0 ) return D3DENUMRET_OK; + + // Avoid duplicates: only enum HW devices for secondary DDraw drivers. + if( NULL != pDeviceInfo->pDriverGUID && FALSE == pDeviceInfo->bHardware ) + return D3DENUMRET_OK; + + // Give the app a chance to accept or reject this device. + if( g_fnAppConfirmFn ) + if( FAILED( g_fnAppConfirmFn( &pDeviceInfo->ddDriverCaps, + &pDeviceInfo->ddDeviceDesc ) ) ) + return D3DENUMRET_OK; + + // Build list of supported modes for the device + for( DWORD i=0; idwNumModes; i++ ) + { + DDSURFACEDESC2 ddsdMode = pDriverInfo->pddsdModes[i]; + DWORD dwRenderDepths = pDeviceInfo->ddDeviceDesc.dwDeviceRenderBitDepth; + DWORD dwDepth = ddsdMode.ddpfPixelFormat.dwRGBBitCount; + + // Accept modes that are compatable with the device + if( ( ( dwDepth == 32 ) && ( dwRenderDepths & DDBD_32 ) ) || + ( ( dwDepth == 24 ) && ( dwRenderDepths & DDBD_24 ) ) || + ( ( dwDepth == 16 ) && ( dwRenderDepths & DDBD_16 ) ) ) + { + // Copy compatible modes to the list of device-supported modes + pDeviceInfo->pddsdModes[pDeviceInfo->dwNumModes++] = ddsdMode; + + // Record whether the device has any stereo modes + if( ddsdMode.ddsCaps.dwCaps2 & DDSCAPS2_STEREOSURFACELEFT ) + pDeviceInfo->bStereoCompatible = TRUE; + } + } + + // Bail if the device has no supported modes + if( 0 == pDeviceInfo->dwNumModes ) + return D3DENUMRET_OK; + + // Find a 640x480x16 mode for the default fullscreen mode + for( i=0; idwNumModes; i++ ) + { + if( ( pDeviceInfo->pddsdModes[i].dwWidth == 640 ) && + ( pDeviceInfo->pddsdModes[i].dwHeight == 480 ) && + ( pDeviceInfo->pddsdModes[i].ddpfPixelFormat.dwRGBBitCount == 16 ) ) + { + pDeviceInfo->ddsdFullscreenMode = pDeviceInfo->pddsdModes[i]; + pDeviceInfo->dwCurrentMode = i; + } + } + + // Select whether the device is initially windowed + pDeviceInfo->bWindowed = pDeviceInfo->bDesktopCompatible; + + // Accept the device and return + g_dwNumDevices++; + + return D3DENUMRET_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: DriverEnumCallback() +// Desc: Callback function for enumerating drivers. +//----------------------------------------------------------------------------- +static BOOL WINAPI DriverEnumCallback( GUID* pGUID, TCHAR* strDesc, + TCHAR* strName, VOID*, HMONITOR ) +{ + D3DEnum_DeviceInfo d3dDeviceInfo; + LPDIRECTDRAW7 pDD; + LPDIRECT3D7 pD3D; + HRESULT hr; + + // Use the GUID to create the DirectDraw object + hr = DirectDrawCreateEx( pGUID, (VOID**)&pDD, IID_IDirectDraw7, NULL ); + if( FAILED(hr) ) + { + DEBUG_MSG( _T("Can't create DDraw during enumeration!") ); + return D3DENUMRET_OK; + } + + // Create a D3D object, to enumerate the d3d devices + hr = pDD->QueryInterface( IID_IDirect3D7, (VOID**)&pD3D ); + if( FAILED(hr) ) + { + pDD->Release(); + DEBUG_MSG( _T("Can't query IDirect3D7 during enumeration!") ); + return D3DENUMRET_OK; + } + + // Copy data to a device info structure + ZeroMemory( &d3dDeviceInfo, sizeof(d3dDeviceInfo) ); + lstrcpyn( d3dDeviceInfo.strDesc, strDesc, 39 ); + d3dDeviceInfo.ddDriverCaps.dwSize = sizeof(DDCAPS); + d3dDeviceInfo.ddHELCaps.dwSize = sizeof(DDCAPS); + pDD->GetCaps( &d3dDeviceInfo.ddDriverCaps, &d3dDeviceInfo.ddHELCaps ); + if( pGUID ) + { + d3dDeviceInfo.guidDriver = (*pGUID); + d3dDeviceInfo.pDriverGUID = &d3dDeviceInfo.guidDriver; + } + + // Record whether the device can render into a desktop window + if( d3dDeviceInfo.ddDriverCaps.dwCaps2 & DDCAPS2_CANRENDERWINDOWED ) + if( NULL == d3dDeviceInfo.pDriverGUID ) + d3dDeviceInfo.bDesktopCompatible = TRUE; + + // Enumerate the fullscreen display modes. + pDD->EnumDisplayModes( 0, NULL, &d3dDeviceInfo, ModeEnumCallback ); + + // Sort list of display modes + qsort( d3dDeviceInfo.pddsdModes, d3dDeviceInfo.dwNumModes, + sizeof(DDSURFACEDESC2), SortModesCallback ); + + // Now, enumerate all the 3D devices + pD3D->EnumDevices( DeviceEnumCallback, &d3dDeviceInfo ); + + // Clean up and return + SAFE_DELETE( d3dDeviceInfo.pddsdModes ); + pD3D->Release(); + pDD->Release(); + + return DDENUMRET_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DEnum_EnumerateDevices() +// Desc: Enumerates all drivers, devices, and modes. The callback function is +// called each device, to confirm that the device supports the feature +// set required by the app. +//----------------------------------------------------------------------------- +HRESULT D3DEnum_EnumerateDevices( HRESULT (*AppConfirmFn)(DDCAPS*, D3DDEVICEDESC7*) ) +{ + // Store the device enumeration callback function + g_fnAppConfirmFn = AppConfirmFn; + + // Enumerate drivers, devices, and modes + DirectDrawEnumerateEx( DriverEnumCallback, NULL, + DDENUM_ATTACHEDSECONDARYDEVICES | + DDENUM_DETACHEDSECONDARYDEVICES | + DDENUM_NONDISPLAYDEVICES ); + + // Make sure devices were actually enumerated + if( 0 == g_dwNumDevicesEnumerated ) + { + DEBUG_MSG( _T("No devices and/or modes were enumerated!") ); + return D3DENUMERR_ENUMERATIONFAILED; + } + if( 0 == g_dwNumDevices ) + { + DEBUG_MSG( _T("No enumerated devices were accepted!") ); + DEBUG_MSG( _T("Try enabling the D3D Reference Rasterizer.") ); + return D3DENUMERR_SUGGESTREFRAST; + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DEnum_FreeResources() +// Desc: Cleans up any memory allocated during device enumeration +//----------------------------------------------------------------------------- +VOID D3DEnum_FreeResources() +{ + for( DWORD i=0; ibDesktopCompatible ) + bWindowed = FALSE; + + // Add a list of devices to the device combo box + for( DWORD device = 0; device < dwNumDevices; device++ ) + { + D3DEnum_DeviceInfo* pDevice = &pDeviceList[device]; + + // Add device name to the combo box + DWORD dwItem = ComboBox_AddString( hwndDevice, pDevice->strDesc ); + + // Set the remaining UI states for the current device + if( pDevice == pCurrentDevice ) + { + // Set the combobox selection on the current device + ComboBox_SetCurSel( hwndDevice, dwItem ); + + // Enable/set the fullscreen checkbox, as appropriate + if( hwndWindowed ) + { + EnableWindow( hwndWindowed, pDevice->bDesktopCompatible ); + Button_SetCheck( hwndWindowed, bWindowed ); + } + + // Enable/set the stereo checkbox, as appropriate + if( hwndStereo ) + { + EnableWindow( hwndStereo, pDevice->bStereoCompatible && !bWindowed ); + Button_SetCheck( hwndStereo, bStereo ); + } + + // Enable/set the fullscreen modes combo, as appropriate + EnableWindow( hwndMode, !bWindowed ); + EnableWindow( hwndFullscreenText, !bWindowed ); + + // Build the list of fullscreen modes + for( DWORD mode = 0; mode < pDevice->dwNumModes; mode++ ) + { + DDSURFACEDESC2* pddsdMode = &pDevice->pddsdModes[mode]; + + // Skip non-stereo modes, if the device is in stereo mode + if( 0 == (pddsdMode->ddsCaps.dwCaps2&DDSCAPS2_STEREOSURFACELEFT) ) + if( bStereo ) + continue; + + TCHAR strMode[80]; + wsprintf( strMode, _T("%ld x %ld x %ld"), + pddsdMode->dwWidth, pddsdMode->dwHeight, + pddsdMode->ddpfPixelFormat.dwRGBBitCount ); + + // Add mode desc to the combo box + DWORD dwItem = ComboBox_AddString( hwndMode, strMode ); + + // Set the item data to identify this mode + ComboBox_SetItemData( hwndMode, dwItem, mode ); + + // Set the combobox selection on the current mode + if( mode == dwCurrentMode ) + ComboBox_SetCurSel( hwndMode, dwItem ); + + // Since not all modes support stereo, select a default mode in + // case none was chosen yet. + if( bStereo && ( CB_ERR == ComboBox_GetCurSel( hwndMode ) ) ) + ComboBox_SetCurSel( hwndMode, dwItem ); + } + } + } +} + + + + +//----------------------------------------------------------------------------- +// Name: ChangeDeviceProc() +// Desc: Windows message handling function for the device select dialog +//----------------------------------------------------------------------------- +static BOOL CALLBACK ChangeDeviceProc( HWND hDlg, UINT uiMsg, WPARAM wParam, + LPARAM lParam ) +{ + static D3DEnum_DeviceInfo** ppDeviceArg; + static D3DEnum_DeviceInfo* pCurrentDevice; + static DWORD dwCurrentMode; + static BOOL bCurrentWindowed; + static BOOL bCurrentStereo; + + // Get access to the enumerated device list + D3DEnum_DeviceInfo* pDeviceList; + DWORD dwNumDevices; + D3DEnum_GetDevices( &pDeviceList, &dwNumDevices ); + + // Handle the initialization message + if( WM_INITDIALOG == uiMsg ) + { + // Get the app's current device, passed in as an lParam argument + ppDeviceArg = (D3DEnum_DeviceInfo**)lParam; + if( NULL == ppDeviceArg ) + return FALSE; + + // Setup temp storage pointers for dialog + pCurrentDevice = (*ppDeviceArg); + dwCurrentMode = pCurrentDevice->dwCurrentMode; + bCurrentWindowed = pCurrentDevice->bWindowed; + bCurrentStereo = pCurrentDevice->bStereo; + + UpdateDialogControls( hDlg, pCurrentDevice, dwCurrentMode, + bCurrentWindowed, bCurrentStereo ); + + return TRUE; + } + else if( WM_COMMAND == uiMsg ) + { + HWND hwndDevice = GetDlgItem( hDlg, IDC_DEVICE_COMBO ); + HWND hwndMode = GetDlgItem( hDlg, IDC_MODE_COMBO ); + HWND hwndWindowed = GetDlgItem( hDlg, IDC_WINDOWED_CHECKBOX ); + HWND hwndStereo = GetDlgItem( hDlg, IDC_STEREO_CHECKBOX ); + + // Get current UI state + DWORD dwDevice = ComboBox_GetCurSel( hwndDevice ); + DWORD dwModeItem = ComboBox_GetCurSel( hwndMode ); + DWORD dwMode = ComboBox_GetItemData( hwndMode, dwModeItem ); + BOOL bWindowed = hwndWindowed ? Button_GetCheck( hwndWindowed ) : 0; + BOOL bStereo = hwndStereo ? Button_GetCheck( hwndStereo ) : 0; + + D3DEnum_DeviceInfo* pDevice = &pDeviceList[dwDevice]; + + if( IDOK == LOWORD(wParam) ) + { + // Handle the case when the user hits the OK button. Check if any + // of the options were changed + if( pDevice != pCurrentDevice || dwMode != dwCurrentMode || + bWindowed != bCurrentWindowed || bStereo != bCurrentStereo ) + { + // Return the newly selected device and its new properties + (*ppDeviceArg) = pDevice; + pDevice->bWindowed = bWindowed; + pDevice->bStereo = bStereo; + pDevice->dwCurrentMode = dwMode; + pDevice->ddsdFullscreenMode = pDevice->pddsdModes[dwMode]; + + EndDialog( hDlg, IDOK ); + } + else + EndDialog( hDlg, IDCANCEL ); + + return TRUE; + } + else if( IDCANCEL == LOWORD(wParam) ) + { + // Handle the case when the user hits the Cancel button + EndDialog( hDlg, IDCANCEL ); + return TRUE; + } + else if( CBN_SELENDOK == HIWORD(wParam) ) + { + if( LOWORD(wParam) == IDC_DEVICE_COMBO ) + { + // Handle the case when the user chooses the device combo + dwMode = pDeviceList[dwDevice].dwCurrentMode; + bWindowed = pDeviceList[dwDevice].bWindowed; + bStereo = pDeviceList[dwDevice].bStereo; + } + } + + // Keep the UI current + UpdateDialogControls( hDlg, &pDeviceList[dwDevice], dwMode, bWindowed, bStereo ); + return TRUE; + } + + return FALSE; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DEnum_UserChangeDevice() +// Desc: Pops up a dialog which allows the user to select a new device. +//----------------------------------------------------------------------------- +HRESULT D3DEnum_UserChangeDevice( D3DEnum_DeviceInfo** ppDevice ) +{ + if( IDOK == DialogBoxParam( (HINSTANCE)GetModuleHandle(NULL), + MAKEINTRESOURCE(IDD_CHANGEDEVICE), + GetForegroundWindow(), + ChangeDeviceProc, (LPARAM)ppDevice ) ) + return S_OK; + + return E_FAIL; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DEnum_SelectDefaultDevice() +// Desc: Pick a default device, preferably hardware and desktop compatible. +//----------------------------------------------------------------------------- +HRESULT D3DEnum_SelectDefaultDevice( D3DEnum_DeviceInfo** ppDevice, + DWORD dwFlags ) +{ + // Check arguments + if( NULL == ppDevice ) + return E_INVALIDARG; + + // Get access to the enumerated device list + D3DEnum_DeviceInfo* pDeviceList; + DWORD dwNumDevices; + D3DEnum_GetDevices( &pDeviceList, &dwNumDevices ); + + // Look for windowable software, hardware, and hardware TnL devices + D3DEnum_DeviceInfo* pRefRastDevice = NULL; + D3DEnum_DeviceInfo* pSoftwareDevice = NULL; + D3DEnum_DeviceInfo* pHardwareDevice = NULL; + D3DEnum_DeviceInfo* pHardwareTnLDevice = NULL; + + for( DWORD i=0; ibWindowed = TRUE; + + return S_OK; +} + + + + + diff --git a/src/d3denum.h b/src/d3denum.h new file mode 100644 index 00000000..9b75611a --- /dev/null +++ b/src/d3denum.h @@ -0,0 +1,120 @@ +//----------------------------------------------------------------------------- +// File: D3DEnum.h +// +// Desc: Functions to enumerate DDraw/D3D drivers, devices, and modes. +// +// Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved +//----------------------------------------------------------------------------- +#ifndef D3DENUM_H +#define D3DENUM_H +#include + + +//----------------------------------------------------------------------------- +// Flag and error definitions +//----------------------------------------------------------------------------- +#define D3DENUM_SOFTWAREONLY 0x00000001 // Software-devices only flag + +#define D3DENUMERR_NODIRECTDRAW 0x81000001 // Could not create DDraw +#define D3DENUMERR_ENUMERATIONFAILED 0x81000002 // Enumeration failed +#define D3DENUMERR_SUGGESTREFRAST 0x81000003 // Suggest using the RefRast +#define D3DENUMERR_NOCOMPATIBLEDEVICES 0x81000004 // No devices were found that + // meet the app's desired + // capabilities +#define D3DENUMERR_ENGINE 0x81000005 // 3D engine error +#define D3DENUMERR_ROBOT 0x81000006 // robot error +#define D3DENUMERR_SOUND 0x81000007 // sound error + + +//----------------------------------------------------------------------------- +// Name: struct D3DEnum_DeviceInfo +// Desc: Structure to hold info about the enumerated Direct3D devices. +//----------------------------------------------------------------------------- +struct D3DEnum_DeviceInfo +{ + // D3D Device info + TCHAR strDesc[40]; + GUID* pDeviceGUID; + D3DDEVICEDESC7 ddDeviceDesc; + BOOL bHardware; + + // DDraw Driver info + GUID* pDriverGUID; + DDCAPS ddDriverCaps; + DDCAPS ddHELCaps; + + // DDraw Mode Info + DDSURFACEDESC2 ddsdFullscreenMode; + BOOL bWindowed; + BOOL bStereo; + + // For internal use (apps should not need to use these) + GUID guidDevice; + GUID guidDriver; + DDSURFACEDESC2* pddsdModes; + DWORD dwNumModes; + DWORD dwCurrentMode; + BOOL bDesktopCompatible; + BOOL bStereoCompatible; +}; + + +// For code not yet switched to new struct name +typedef D3DEnum_DeviceInfo D3DDEVICEINFO; + + + + +//----------------------------------------------------------------------------- +// Name: D3DEnum_EnumerateDevices() +// Desc: Enumerates all drivers, devices, and modes. The callback function is +// called each device, to confirm that the device supports the feature +// set required by the app. +//----------------------------------------------------------------------------- +HRESULT D3DEnum_EnumerateDevices( HRESULT (*fn)(DDCAPS*, D3DDEVICEDESC7*) ); + + + + +//----------------------------------------------------------------------------- +// Name: D3DEnum_FreeResources() +// Desc: Cleans up any memory allocated during device enumeration +//----------------------------------------------------------------------------- +VOID D3DEnum_FreeResources(); + + + + +//----------------------------------------------------------------------------- +// Name: D3DEnum_GetDevices() +// Desc: Returns a ptr to the array of enumerated D3DDEVICEINFO structures. +//----------------------------------------------------------------------------- +VOID D3DEnum_GetDevices( D3DEnum_DeviceInfo** ppDevices, DWORD* pdwCount ); + + + + +//----------------------------------------------------------------------------- +// Name: D3DEnum_SelectDefaultDevice() +// Desc: Picks a driver based on a set of passed in criteria. The +// D3DENUM_SOFTWAREONLY flag can be used to pick a software device. +//----------------------------------------------------------------------------- +HRESULT D3DEnum_SelectDefaultDevice( D3DEnum_DeviceInfo** pDevice, + DWORD dwFlags = 0L ); + + + + +//----------------------------------------------------------------------------- +// Name: D3DEnum_UserChangeDevice() +// Desc: Pops up a dialog which allows the user to select a new device. +//----------------------------------------------------------------------------- +HRESULT D3DEnum_UserChangeDevice( D3DEnum_DeviceInfo** ppDevice ); + + + + +#endif // D3DENUM_H + + + diff --git a/src/d3dframe.cpp b/src/d3dframe.cpp new file mode 100644 index 00000000..a1189d1b --- /dev/null +++ b/src/d3dframe.cpp @@ -0,0 +1,607 @@ +//----------------------------------------------------------------------------- +// File: D3DFrame.cpp +// +// Desc: Class functions to implement a Direct3D app framework. +// +// Copyright (c) 1995-1999 by Microsoft, all rights reserved +//----------------------------------------------------------------------------- +#define STRICT +#include +#include +#include +#include "D3DFrame.h" +#include "D3DUtil.h" + + + + +//----------------------------------------------------------------------------- +// Name: CD3DFramework7() +// Desc: The constructor. Clears static variables +//----------------------------------------------------------------------------- +CD3DFramework7::CD3DFramework7() +{ + m_hWnd = NULL; + m_bIsFullscreen = FALSE; + m_bIsStereo = FALSE; + m_dwRenderWidth = 0L; + m_dwRenderHeight = 0L; + + m_pddsFrontBuffer = NULL; + m_pddsBackBuffer = NULL; + m_pddsBackBufferLeft = NULL; + + m_pddsZBuffer = NULL; + m_pd3dDevice = NULL; + m_pDD = NULL; + m_pD3D = NULL; + m_dwDeviceMemType = NULL; +} + + + + +//----------------------------------------------------------------------------- +// Name: ~CD3DFramework7() +// Desc: The destructor. Deletes all objects +//----------------------------------------------------------------------------- +CD3DFramework7::~CD3DFramework7() +{ + DestroyObjects(); +} + + + + +//----------------------------------------------------------------------------- +// Name: DestroyObjects() +// Desc: Cleans everything up upon deletion. This code returns an error +// if any of the objects have remaining reference counts. +//----------------------------------------------------------------------------- +HRESULT CD3DFramework7::DestroyObjects() +{ + LONG nDD = 0L; // Number of outstanding DDraw references + LONG nD3D = 0L; // Number of outstanding D3DDevice references + + if( m_pDD ) + { + HRESULT err = m_pDD->SetCooperativeLevel( m_hWnd, DDSCL_NORMAL ); + char s[100]; + sprintf(s, "SetCooperativeLevel error=%d\n", err); + OutputDebugString(s); + } + + // Do a safe check for releasing the D3DDEVICE. RefCount must be zero. + if( m_pd3dDevice ) + if( 0 < ( nD3D = m_pd3dDevice->Release() ) ) + DEBUG_MSG( _T("Error: D3DDevice object is still referenced!") ); + m_pd3dDevice = NULL; + + SAFE_RELEASE( m_pddsBackBuffer ); + SAFE_RELEASE( m_pddsBackBufferLeft ); + SAFE_RELEASE( m_pddsZBuffer ); + SAFE_RELEASE( m_pddsFrontBuffer ); + SAFE_RELEASE( m_pD3D ); + + if( m_pDD ) + { + // Do a safe check for releasing DDRAW. RefCount must be zero. + if( 0 < ( nDD = m_pDD->Release() ) ) + DEBUG_MSG( _T("Error: DDraw object is still referenced!") ); + } + m_pDD = NULL; + + // Return successful, unless there are outstanding DD or D3DDevice refs. + return ( nDD==0 && nD3D==0 ) ? S_OK : D3DFWERR_NONZEROREFCOUNT; +} + + + + +//----------------------------------------------------------------------------- +// Name: Initialize() +// Desc: Creates the internal objects for the framework +//----------------------------------------------------------------------------- +HRESULT CD3DFramework7::Initialize( HWND hWnd, GUID* pDriverGUID, + GUID* pDeviceGUID, DDSURFACEDESC2* pMode, + DWORD dwFlags ) +{ + HRESULT hr; + + // Check params. Note: A NULL mode is valid for windowed modes only. + if( ( NULL==hWnd ) || ( NULL==pDeviceGUID ) || + ( NULL==pMode && (dwFlags&D3DFW_FULLSCREEN) ) ) + return E_INVALIDARG; + + // Setup state for windowed/fullscreen mode + m_hWnd = hWnd; + m_bIsStereo = FALSE; + m_bIsFullscreen = ( dwFlags & D3DFW_FULLSCREEN ) ? TRUE : FALSE; + + // Support stereoscopic viewing for fullscreen modes which support it + if( ( dwFlags & D3DFW_STEREO ) && ( dwFlags & D3DFW_FULLSCREEN ) ) + if( pMode->ddsCaps.dwCaps2 & DDSCAPS2_STEREOSURFACELEFT ) + m_bIsStereo = TRUE; + + // Create the D3D rendering environment (surfaces, device, viewport, etc.) + if( FAILED( hr = CreateEnvironment( pDriverGUID, pDeviceGUID, pMode, + dwFlags ) ) ) + { + DestroyObjects(); + return hr; + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: CreateEnvironment() +// Desc: Creates the internal objects for the framework +//----------------------------------------------------------------------------- +HRESULT CD3DFramework7::CreateEnvironment( GUID* pDriverGUID, GUID* pDeviceGUID, + DDSURFACEDESC2* pMode, DWORD dwFlags ) +{ + HRESULT hr; + + // Select the default memory type, for whether the device is HW or SW + if( IsEqualIID( *pDeviceGUID, IID_IDirect3DHALDevice) ) + m_dwDeviceMemType = DDSCAPS_VIDEOMEMORY; + else if( IsEqualIID( *pDeviceGUID, IID_IDirect3DTnLHalDevice) ) + m_dwDeviceMemType = DDSCAPS_VIDEOMEMORY; + else + m_dwDeviceMemType = DDSCAPS_SYSTEMMEMORY; + + // Create the DDraw object + hr = CreateDirectDraw( pDriverGUID, dwFlags ); + if( FAILED( hr ) ) + return hr; + + // Create the front and back buffers, and attach a clipper + if( dwFlags & D3DFW_FULLSCREEN ) + hr = CreateFullscreenBuffers( pMode ); + else + hr = CreateWindowedBuffers(); + if( FAILED( hr ) ) + return hr; + + // Create the Direct3D object and the Direct3DDevice object + hr = CreateDirect3D( pDeviceGUID ); + if( FAILED( hr ) ) + return hr; + + // Create and attach the zbuffer + if( dwFlags & D3DFW_ZBUFFER ) + hr = CreateZBuffer( pDeviceGUID ); + if( FAILED( hr ) ) + return hr; + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: EnumZBufferFormatsCallback() +// Desc: Simply returns the first matching enumerated z-buffer format +//----------------------------------------------------------------------------- +static HRESULT WINAPI EnumZBufferFormatsCallback( DDPIXELFORMAT* pddpf, + VOID* pContext ) +{ + DDPIXELFORMAT* pddpfOut = (DDPIXELFORMAT*)pContext; + + if( pddpfOut->dwRGBBitCount == pddpf->dwRGBBitCount ) + { + (*pddpfOut) = (*pddpf); + return D3DENUMRET_CANCEL; + } + + return D3DENUMRET_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: CreateDirectDraw() +// Desc: Create the DirectDraw interface +//----------------------------------------------------------------------------- +HRESULT CD3DFramework7::CreateDirectDraw( GUID* pDriverGUID, DWORD dwFlags ) +{ + // Create the DirectDraw interface, and query for the DD7 interface + if( FAILED( DirectDrawCreateEx( pDriverGUID, (VOID**)&m_pDD, + IID_IDirectDraw7, NULL ) ) ) + { + DEBUG_MSG( _T("Could not create DirectDraw") ); + return D3DFWERR_NODIRECTDRAW; + } + + // Set the Windows cooperative level + DWORD dwCoopFlags = DDSCL_NORMAL; + if( m_bIsFullscreen ) + dwCoopFlags = DDSCL_ALLOWREBOOT|DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN; + + // By defualt, set the flag to allow D3D to optimize floating point calcs + if( 0L == ( dwFlags & D3DFW_NO_FPUSETUP ) ) + dwCoopFlags |= DDSCL_FPUSETUP; + + if( FAILED( m_pDD->SetCooperativeLevel( m_hWnd, dwCoopFlags ) ) ) + { + DEBUG_MSG( _T("Couldn't set coop level") ); + return D3DFWERR_COULDNTSETCOOPLEVEL; + } + + // Check that we are NOT in a palettized display. That case will fail, + // since the framework doesn't use palettes. + DDSURFACEDESC2 ddsd; + ddsd.dwSize = sizeof(ddsd); + m_pDD->GetDisplayMode( &ddsd ); + if( ddsd.ddpfPixelFormat.dwRGBBitCount <= 8 ) + return D3DFWERR_INVALIDMODE; + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: CreateFullscreenBuffers() +// Desc: Creates the primary and (optional) backbuffer for rendering. +// Windowed mode and fullscreen mode are handled differently. +//----------------------------------------------------------------------------- +HRESULT CD3DFramework7::CreateFullscreenBuffers( DDSURFACEDESC2* pddsd ) +{ + HRESULT hr; + + // Get the dimensions of the screen bounds + // Store the rectangle which contains the renderer + SetRect( &m_rcScreenRect, 0, 0, pddsd->dwWidth, pddsd->dwHeight ); + m_dwRenderWidth = m_rcScreenRect.right - m_rcScreenRect.left; + m_dwRenderHeight = m_rcScreenRect.bottom - m_rcScreenRect.top; + + // Set the display mode to the requested dimensions. Check for + // 320x200x8 modes, and set flag to avoid using ModeX + DWORD dwModeFlags = 0; + + if( (320==m_dwRenderWidth) && (200==m_dwRenderHeight) && + (8==pddsd->ddpfPixelFormat.dwRGBBitCount) ) + dwModeFlags |= DDSDM_STANDARDVGAMODE; + + if( FAILED( m_pDD->SetDisplayMode( m_dwRenderWidth, m_dwRenderHeight, + pddsd->ddpfPixelFormat.dwRGBBitCount, + pddsd->dwRefreshRate, dwModeFlags ) ) ) + { + DEBUG_MSG( _T("Can't set display mode") ); + return D3DFWERR_BADDISPLAYMODE; + } + + // Setup to create the primary surface w/backbuffer + DDSURFACEDESC2 ddsd; + ZeroMemory( &ddsd, sizeof(ddsd) ); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS|DDSD_BACKBUFFERCOUNT; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE | + DDSCAPS_FLIP | DDSCAPS_COMPLEX; + ddsd.dwBackBufferCount = 1; + + // Support for stereoscopic viewing + if( m_bIsStereo ) + { + ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; + ddsd.ddsCaps.dwCaps2 |= DDSCAPS2_STEREOSURFACELEFT; + } + + // Create the primary surface + if( FAILED( hr = m_pDD->CreateSurface( &ddsd, &m_pddsFrontBuffer, NULL ) ) ) + { + DEBUG_MSG( _T("Error: Can't create primary surface") ); + if( hr != DDERR_OUTOFVIDEOMEMORY ) + return D3DFWERR_NOPRIMARY; + DEBUG_MSG( _T("Error: Out of video memory") ); + return DDERR_OUTOFVIDEOMEMORY; + } + + // Get the backbuffer, which was created along with the primary. + DDSCAPS2 ddscaps = { DDSCAPS_BACKBUFFER, 0, 0, 0 }; + if( FAILED( hr = m_pddsFrontBuffer->GetAttachedSurface( &ddscaps, + &m_pddsBackBuffer ) ) ) + { + DEBUG_ERR( hr, _T("Error: Can't get the backbuffer") ); + return D3DFWERR_NOBACKBUFFER; + } + + // Increment the backbuffer count (for consistency with windowed mode) + m_pddsBackBuffer->AddRef(); + + // Support for stereoscopic viewing + if( m_bIsStereo ) + { + // Get the left backbuffer, which was created along with the primary. + DDSCAPS2 ddscaps = { 0, DDSCAPS2_STEREOSURFACELEFT, 0, 0 }; + if( FAILED( hr = m_pddsBackBuffer->GetAttachedSurface( &ddscaps, + &m_pddsBackBufferLeft ) ) ) + { + DEBUG_ERR( hr, _T("Error: Can't get the left backbuffer") ); + return D3DFWERR_NOBACKBUFFER; + } + m_pddsBackBufferLeft->AddRef(); + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: CreateWindowedBuffers() +// Desc: Creates the primary and (optional) backbuffer for rendering. +// Windowed mode and fullscreen mode are handled differently. +//----------------------------------------------------------------------------- +HRESULT CD3DFramework7::CreateWindowedBuffers() +{ + HRESULT hr; + + // Get the dimensions of the viewport and screen bounds + GetClientRect( m_hWnd, &m_rcScreenRect ); + ClientToScreen( m_hWnd, (POINT*)&m_rcScreenRect.left ); + ClientToScreen( m_hWnd, (POINT*)&m_rcScreenRect.right ); + m_dwRenderWidth = m_rcScreenRect.right - m_rcScreenRect.left; + m_dwRenderHeight = m_rcScreenRect.bottom - m_rcScreenRect.top; + + // Create the primary surface + DDSURFACEDESC2 ddsd; + ZeroMemory( &ddsd, sizeof(ddsd) ); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + if( FAILED( hr = m_pDD->CreateSurface( &ddsd, &m_pddsFrontBuffer, NULL ) ) ) + { + DEBUG_MSG( _T("Error: Can't create primary surface") ); + if( hr != DDERR_OUTOFVIDEOMEMORY ) + return D3DFWERR_NOPRIMARY; + DEBUG_MSG( _T("Error: Out of video memory") ); + return DDERR_OUTOFVIDEOMEMORY; + } + + // If in windowed-mode, create a clipper object + LPDIRECTDRAWCLIPPER pcClipper; + if( FAILED( hr = m_pDD->CreateClipper( 0, &pcClipper, NULL ) ) ) + { + DEBUG_MSG( _T("Error: Couldn't create clipper") ); + return D3DFWERR_NOCLIPPER; + } + + // Associate the clipper with the window + pcClipper->SetHWnd( 0, m_hWnd ); + m_pddsFrontBuffer->SetClipper( pcClipper ); + SAFE_RELEASE( pcClipper ); + + // Create a backbuffer + ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; + ddsd.dwWidth = m_dwRenderWidth; + ddsd.dwHeight = m_dwRenderHeight; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE; + + if( FAILED( hr = m_pDD->CreateSurface( &ddsd, &m_pddsBackBuffer, NULL ) ) ) + { + DEBUG_ERR( hr, _T("Error: Couldn't create the backbuffer") ); + if( hr != DDERR_OUTOFVIDEOMEMORY ) + return D3DFWERR_NOBACKBUFFER; + DEBUG_MSG( _T("Error: Out of video memory") ); + return DDERR_OUTOFVIDEOMEMORY; + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: CreateDirect3D() +// Desc: Create the Direct3D interface +//----------------------------------------------------------------------------- +HRESULT CD3DFramework7::CreateDirect3D( GUID* pDeviceGUID ) +{ + // Query DirectDraw for access to Direct3D + if( FAILED( m_pDD->QueryInterface( IID_IDirect3D7, (VOID**)&m_pD3D ) ) ) + { + DEBUG_MSG( _T("Couldn't get the Direct3D interface") ); + return D3DFWERR_NODIRECT3D; + } + + // Create the device + if( FAILED( m_pD3D->CreateDevice( *pDeviceGUID, m_pddsBackBuffer, + &m_pd3dDevice) ) ) + { + DEBUG_MSG( _T("Couldn't create the D3DDevice") ); + return D3DFWERR_NO3DDEVICE; + } + + // Finally, set the viewport for the newly created device + D3DVIEWPORT7 vp = { 0, 0, m_dwRenderWidth, m_dwRenderHeight, 0.0f, 1.0f }; + + if( FAILED( m_pd3dDevice->SetViewport( &vp ) ) ) + { + DEBUG_MSG( _T("Error: Couldn't set current viewport to device") ); + return D3DFWERR_NOVIEWPORT; + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: CreateZBuffer() +// Desc: Internal function called by Create() to make and attach a zbuffer +// to the renderer +//----------------------------------------------------------------------------- +HRESULT CD3DFramework7::CreateZBuffer( GUID* pDeviceGUID ) +{ + HRESULT hr; + + // Check if the device supports z-bufferless hidden surface removal. If so, + // we don't really need a z-buffer + D3DDEVICEDESC7 ddDesc; + m_pd3dDevice->GetCaps( &ddDesc ); + if( ddDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ZBUFFERLESSHSR ) + return S_OK; + + // Get z-buffer dimensions from the render target + DDSURFACEDESC2 ddsd; + ddsd.dwSize = sizeof(ddsd); + m_pddsBackBuffer->GetSurfaceDesc( &ddsd ); + + // Setup the surface desc for the z-buffer. + ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT; + ddsd.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | m_dwDeviceMemType; + ddsd.ddpfPixelFormat.dwSize = 0; // Tag the pixel format as unitialized + + // Get an appropiate pixel format from enumeration of the formats. On the + // first pass, we look for a zbuffer dpeth which is equal to the frame + // buffer depth (as some cards unfornately require this). + m_pD3D->EnumZBufferFormats( *pDeviceGUID, EnumZBufferFormatsCallback, + (VOID*)&ddsd.ddpfPixelFormat ); + if( 0 == ddsd.ddpfPixelFormat.dwSize ) + { + // Try again, just accepting any 16-bit zbuffer + ddsd.ddpfPixelFormat.dwRGBBitCount = 16; + m_pD3D->EnumZBufferFormats( *pDeviceGUID, EnumZBufferFormatsCallback, + (VOID*)&ddsd.ddpfPixelFormat ); + + if( 0 == ddsd.ddpfPixelFormat.dwSize ) + { + DEBUG_MSG( _T("Device doesn't support requested zbuffer format") ); + return D3DFWERR_NOZBUFFER; + } + } + + // Create and attach a z-buffer + if( FAILED( hr = m_pDD->CreateSurface( &ddsd, &m_pddsZBuffer, NULL ) ) ) + { + DEBUG_MSG( _T("Error: Couldn't create a ZBuffer surface") ); + if( hr != DDERR_OUTOFVIDEOMEMORY ) + return D3DFWERR_NOZBUFFER; + DEBUG_MSG( _T("Error: Out of video memory") ); + return DDERR_OUTOFVIDEOMEMORY; + } + + if( FAILED( m_pddsBackBuffer->AddAttachedSurface( m_pddsZBuffer ) ) ) + { + DEBUG_MSG( _T("Error: Couldn't attach zbuffer to render surface") ); + return D3DFWERR_NOZBUFFER; + } + + // For stereoscopic viewing, attach zbuffer to left surface as well + if( m_bIsStereo ) + { + if( FAILED( m_pddsBackBufferLeft->AddAttachedSurface( m_pddsZBuffer ) ) ) + { + DEBUG_MSG( _T("Error: Couldn't attach zbuffer to left render surface") ); + return D3DFWERR_NOZBUFFER; + } + } + + // Finally, this call rebuilds internal structures + if( FAILED( m_pd3dDevice->SetRenderTarget( m_pddsBackBuffer, 0L ) ) ) + { + DEBUG_MSG( _T("Error: SetRenderTarget() failed after attaching zbuffer!") ); + return D3DFWERR_NOZBUFFER; + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: RestoreSurfaces() +// Desc: Checks for lost surfaces and restores them if lost. Note: Don't +// restore render surface, since it's just a duplicate ptr. +//----------------------------------------------------------------------------- +HRESULT CD3DFramework7::RestoreSurfaces() +{ + // Restore all surfaces (including video memory vertex buffers) + m_pDD->RestoreAllSurfaces(); + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: Move() +// Desc: Moves the screen rect for windowed renderers +//----------------------------------------------------------------------------- +VOID CD3DFramework7::Move( INT x, INT y ) +{ + if( TRUE == m_bIsFullscreen ) + return; + + SetRect( &m_rcScreenRect, x, y, x + m_dwRenderWidth, y + m_dwRenderHeight ); +} + + + + +//----------------------------------------------------------------------------- +// Name: FlipToGDISurface() +// Desc: Puts the GDI surface in front of the primary, so that dialog +// boxes and other windows drawing funcs may happen. +//----------------------------------------------------------------------------- +HRESULT CD3DFramework7::FlipToGDISurface( BOOL bDrawFrame ) +{ + if( m_pDD && m_bIsFullscreen ) + { + m_pDD->FlipToGDISurface(); + + if( bDrawFrame ) + { + DrawMenuBar( m_hWnd ); + RedrawWindow( m_hWnd, NULL, NULL, RDW_FRAME ); + } + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: ShowFrame() +// Desc: Show the frame on the primary surface, via a blt or a flip. +//----------------------------------------------------------------------------- +HRESULT CD3DFramework7::ShowFrame() +{ + if( NULL == m_pddsFrontBuffer ) + return D3DFWERR_NOTINITIALIZED; + + if( m_bIsFullscreen ) + { + // We are in fullscreen mode, so perform a flip. + if( m_bIsStereo ) + return m_pddsFrontBuffer->Flip( NULL, DDFLIP_WAIT | DDFLIP_STEREO ); + else + return m_pddsFrontBuffer->Flip( NULL, DDFLIP_WAIT ); + } + else + { + // We are in windowed mode, so perform a blit. + return m_pddsFrontBuffer->Blt( &m_rcScreenRect, m_pddsBackBuffer, + NULL, DDBLT_WAIT, NULL ); + } +} + + + diff --git a/src/d3dframe.h b/src/d3dframe.h new file mode 100644 index 00000000..2ac913e5 --- /dev/null +++ b/src/d3dframe.h @@ -0,0 +1,126 @@ +//----------------------------------------------------------------------------- +// File: D3DFrame.h +// +// Desc: Class to manage the Direct3D environment objects such as buffers, +// viewports, and 3D devices. +// +// The class is initialized with the Initialize() function, after which +// the Get????() functions can be used to access the objects needed for +// rendering. If the device or display needs to be changed, the +// ChangeDevice() function can be called. If the display window is moved +// the changes need to be reported with the Move() function. +// +// After rendering a frame, the ShowFrame() function filps or blits the +// backbuffer contents to the primary. If surfaces are lost, they can be +// restored with the RestoreSurfaces() function. Finally, if normal +// Windows output is needed, the FlipToGDISurface() provides a GDI +// surface to draw on. +// +// Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved +//----------------------------------------------------------------------------- +#ifndef D3DFRAME_H +#define D3DFRAME_H +#include +#include + + + + +//----------------------------------------------------------------------------- +// Name: CD3DFramework7 +// Desc: The Direct3D sample framework class for DX7. Maintains the D3D +// surfaces and device used for 3D rendering. +//----------------------------------------------------------------------------- +class CD3DFramework7 +{ + // Internal variables for the framework class + HWND m_hWnd; // The window object + BOOL m_bIsFullscreen; // Fullscreen vs. windowed + BOOL m_bIsStereo; // Stereo view mode + DWORD m_dwRenderWidth; // Dimensions of the render target + DWORD m_dwRenderHeight; + RECT m_rcScreenRect; // Screen rect for window + LPDIRECTDRAW7 m_pDD; // The DirectDraw object + LPDIRECT3D7 m_pD3D; // The Direct3D object + LPDIRECT3DDEVICE7 m_pd3dDevice; // The D3D device + LPDIRECTDRAWSURFACE7 m_pddsFrontBuffer; // The primary surface + LPDIRECTDRAWSURFACE7 m_pddsBackBuffer; // The backbuffer surface + LPDIRECTDRAWSURFACE7 m_pddsBackBufferLeft; // For stereo modes + LPDIRECTDRAWSURFACE7 m_pddsZBuffer; // The zbuffer surface + DWORD m_dwDeviceMemType; + + // Internal functions for the framework class + HRESULT CreateZBuffer( GUID* ); + HRESULT CreateFullscreenBuffers( DDSURFACEDESC2* ); + HRESULT CreateWindowedBuffers(); + HRESULT CreateDirectDraw( GUID*, DWORD ); + HRESULT CreateDirect3D( GUID* ); + HRESULT CreateEnvironment( GUID*, GUID*, DDSURFACEDESC2*, DWORD ); + +public: + // Access functions for DirectX objects + LPDIRECTDRAW7 GetDirectDraw() { return m_pDD; } + LPDIRECT3D7 GetDirect3D() { return m_pD3D; } + LPDIRECT3DDEVICE7 GetD3DDevice() { return m_pd3dDevice; } + LPDIRECTDRAWSURFACE7 GetFrontBuffer() { return m_pddsFrontBuffer; } + LPDIRECTDRAWSURFACE7 GetBackBuffer() { return m_pddsBackBuffer; } + LPDIRECTDRAWSURFACE7 GetRenderSurface() { return m_pddsBackBuffer; } + LPDIRECTDRAWSURFACE7 GetRenderSurfaceLeft() { return m_pddsBackBufferLeft; } + + // Functions to aid rendering + HRESULT RestoreSurfaces(); + HRESULT ShowFrame(); + HRESULT FlipToGDISurface( BOOL bDrawFrame = FALSE ); + + // Functions for managing screen and viewport bounds + BOOL IsFullscreen() { return m_bIsFullscreen; } + BOOL IsStereo() { return m_bIsStereo; } + VOID Move( INT x, INT y ); + + // Creates the Framework + HRESULT Initialize( HWND hWnd, GUID* pDriverGUID, GUID* pDeviceGUID, + DDSURFACEDESC2* pddsd, DWORD dwFlags ); + HRESULT DestroyObjects(); + + CD3DFramework7(); + ~CD3DFramework7(); +}; + + + + +//----------------------------------------------------------------------------- +// Flags used for the Initialize() method of a CD3DFramework object +//----------------------------------------------------------------------------- +#define D3DFW_FULLSCREEN 0x00000001 // Use fullscreen mode +#define D3DFW_STEREO 0x00000002 // Use stereo-scopic viewing +#define D3DFW_ZBUFFER 0x00000004 // Create and use a zbuffer +#define D3DFW_NO_FPUSETUP 0x00000008 // Don't use default DDSCL_FPUSETUP flag + + + + +//----------------------------------------------------------------------------- +// Errors that the Initialize() and ChangeDriver() calls may return +//----------------------------------------------------------------------------- +#define D3DFWERR_INITIALIZATIONFAILED 0x82000000 +#define D3DFWERR_NODIRECTDRAW 0x82000001 +#define D3DFWERR_COULDNTSETCOOPLEVEL 0x82000002 +#define D3DFWERR_NODIRECT3D 0x82000003 +#define D3DFWERR_NO3DDEVICE 0x82000004 +#define D3DFWERR_NOZBUFFER 0x82000005 +#define D3DFWERR_INVALIDZBUFFERDEPTH 0x82000006 +#define D3DFWERR_NOVIEWPORT 0x82000007 +#define D3DFWERR_NOPRIMARY 0x82000008 +#define D3DFWERR_NOCLIPPER 0x82000009 +#define D3DFWERR_BADDISPLAYMODE 0x8200000a +#define D3DFWERR_NOBACKBUFFER 0x8200000b +#define D3DFWERR_NONZEROREFCOUNT 0x8200000c +#define D3DFWERR_NORENDERTARGET 0x8200000d +#define D3DFWERR_INVALIDMODE 0x8200000e +#define D3DFWERR_NOTINITIALIZED 0x8200000f + + +#endif // D3DFRAME_H + + diff --git a/src/d3dmath.cpp b/src/d3dmath.cpp new file mode 100644 index 00000000..79ea56fb --- /dev/null +++ b/src/d3dmath.cpp @@ -0,0 +1,327 @@ +//----------------------------------------------------------------------------- +// File: D3DMath.cpp +// +// Desc: Shortcut macros and functions for using DX objects +// +// Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved +//----------------------------------------------------------------------------- +#define D3D_OVERLOADS +#define STRICT +#include +#include +#include "D3DMath.h" + + + + +//----------------------------------------------------------------------------- +// Name: D3DMath_MatrixMultiply() +// Desc: Does the matrix operation: [Q] = [A] * [B]. Note that the order of +// this operation was changed from the previous version of the DXSDK. +//----------------------------------------------------------------------------- +VOID D3DMath_MatrixMultiply( D3DMATRIX& q, D3DMATRIX& a, D3DMATRIX& b ) +{ + FLOAT* pA = (FLOAT*)&a; + FLOAT* pB = (FLOAT*)&b; + FLOAT pM[16]; + + ZeroMemory( pM, sizeof(D3DMATRIX) ); + + for( WORD i=0; i<4; i++ ) + for( WORD j=0; j<4; j++ ) + for( WORD k=0; k<4; k++ ) + pM[4*i+j] += pA[4*i+k] * pB[4*k+j]; + + memcpy( &q, pM, sizeof(D3DMATRIX) ); +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DMath_MatrixInvert() +// Desc: Does the matrix operation: [Q] = inv[A]. Note: this function only +// works for matrices with [0 0 0 1] for the 4th column. +//----------------------------------------------------------------------------- +HRESULT D3DMath_MatrixInvert( D3DMATRIX& q, D3DMATRIX& a ) +{ + if( fabs(a._44 - 1.0f) > .001f) + return E_INVALIDARG; + if( fabs(a._14) > .001f || fabs(a._24) > .001f || fabs(a._34) > .001f ) + return E_INVALIDARG; + + FLOAT fDetInv = 1.0f / ( a._11 * ( a._22 * a._33 - a._23 * a._32 ) - + a._12 * ( a._21 * a._33 - a._23 * a._31 ) + + a._13 * ( a._21 * a._32 - a._22 * a._31 ) ); + + q._11 = fDetInv * ( a._22 * a._33 - a._23 * a._32 ); + q._12 = -fDetInv * ( a._12 * a._33 - a._13 * a._32 ); + q._13 = fDetInv * ( a._12 * a._23 - a._13 * a._22 ); + q._14 = 0.0f; + + q._21 = -fDetInv * ( a._21 * a._33 - a._23 * a._31 ); + q._22 = fDetInv * ( a._11 * a._33 - a._13 * a._31 ); + q._23 = -fDetInv * ( a._11 * a._23 - a._13 * a._21 ); + q._24 = 0.0f; + + q._31 = fDetInv * ( a._21 * a._32 - a._22 * a._31 ); + q._32 = -fDetInv * ( a._11 * a._32 - a._12 * a._31 ); + q._33 = fDetInv * ( a._11 * a._22 - a._12 * a._21 ); + q._34 = 0.0f; + + q._41 = -( a._41 * q._11 + a._42 * q._21 + a._43 * q._31 ); + q._42 = -( a._41 * q._12 + a._42 * q._22 + a._43 * q._32 ); + q._43 = -( a._41 * q._13 + a._42 * q._23 + a._43 * q._33 ); + q._44 = 1.0f; + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DMath_VectorMatrixMultiply() +// Desc: Multiplies a vector by a matrix +//----------------------------------------------------------------------------- +HRESULT D3DMath_VectorMatrixMultiply( D3DVECTOR& vDest, D3DVECTOR& vSrc, + D3DMATRIX& mat) +{ + FLOAT x = vSrc.x*mat._11 + vSrc.y*mat._21 + vSrc.z* mat._31 + mat._41; + FLOAT y = vSrc.x*mat._12 + vSrc.y*mat._22 + vSrc.z* mat._32 + mat._42; + FLOAT z = vSrc.x*mat._13 + vSrc.y*mat._23 + vSrc.z* mat._33 + mat._43; + FLOAT w = vSrc.x*mat._14 + vSrc.y*mat._24 + vSrc.z* mat._34 + mat._44; + + if( fabs( w ) < g_EPSILON ) + return E_INVALIDARG; + + vDest.x = x/w; + vDest.y = y/w; + vDest.z = z/w; + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DMath_VertexMatrixMultiply() +// Desc: Multiplies a vertex by a matrix +//----------------------------------------------------------------------------- +HRESULT D3DMath_VertexMatrixMultiply( D3DVERTEX& vDest, D3DVERTEX& vSrc, + D3DMATRIX& mat ) +{ + HRESULT hr; + D3DVECTOR* pSrcVec = (D3DVECTOR*)&vSrc.x; + D3DVECTOR* pDestVec = (D3DVECTOR*)&vDest.x; + + if( SUCCEEDED( hr = D3DMath_VectorMatrixMultiply( *pDestVec, *pSrcVec, + mat ) ) ) + { + pSrcVec = (D3DVECTOR*)&vSrc.nx; + pDestVec = (D3DVECTOR*)&vDest.nx; + hr = D3DMath_VectorMatrixMultiply( *pDestVec, *pSrcVec, mat ); + } + return hr; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DMath_QuaternionFromRotation() +// Desc: Converts a normalized axis and angle to a unit quaternion. +//----------------------------------------------------------------------------- +VOID D3DMath_QuaternionFromRotation( FLOAT& x, FLOAT& y, FLOAT& z, FLOAT& w, + D3DVECTOR& v, FLOAT fTheta ) +{ + x = sinf( fTheta/2.0f ) * v.x; + y = sinf( fTheta/2.0f ) * v.y; + z = sinf( fTheta/2.0f ) * v.z; + w = cosf( fTheta/2.0f ); +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DMath_RotationFromQuaternion() +// Desc: Converts a normalized axis and angle to a unit quaternion. +//----------------------------------------------------------------------------- +VOID D3DMath_RotationFromQuaternion( D3DVECTOR& v, FLOAT& fTheta, + FLOAT x, FLOAT y, FLOAT z, FLOAT w ) + +{ + fTheta = acosf(w) * 2.0f; + v.x = x / sinf( fTheta/2.0f ); + v.y = y / sinf( fTheta/2.0f ); + v.z = z / sinf( fTheta/2.0f ); +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DMath_QuaternionFromAngles() +// Desc: Converts euler angles to a unit quaternion. +//----------------------------------------------------------------------------- +VOID D3DMath_QuaternionFromAngles( FLOAT& x, FLOAT& y, FLOAT& z, FLOAT& w, + FLOAT fYaw, FLOAT fPitch, FLOAT fRoll ) + +{ + FLOAT fSinYaw = sinf( fYaw/2.0f ); + FLOAT fSinPitch = sinf( fPitch/2.0f ); + FLOAT fSinRoll = sinf( fRoll/2.0f ); + FLOAT fCosYaw = cosf( fYaw/2.0f ); + FLOAT fCosPitch = cosf( fPitch/2.0f ); + FLOAT fCosRoll = cosf( fRoll/2.0f ); + + x = fSinRoll * fCosPitch * fCosYaw - fCosRoll * fSinPitch * fSinYaw; + y = fCosRoll * fSinPitch * fCosYaw + fSinRoll * fCosPitch * fSinYaw; + z = fCosRoll * fCosPitch * fSinYaw - fSinRoll * fSinPitch * fCosYaw; + w = fCosRoll * fCosPitch * fCosYaw + fSinRoll * fSinPitch * fSinYaw; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DMath_MatrixFromQuaternion() +// Desc: Converts a unit quaternion into a rotation matrix. +//----------------------------------------------------------------------------- +VOID D3DMath_MatrixFromQuaternion( D3DMATRIX& mat, FLOAT x, FLOAT y, FLOAT z, + FLOAT w ) +{ + FLOAT xx = x*x; FLOAT yy = y*y; FLOAT zz = z*z; + FLOAT xy = x*y; FLOAT xz = x*z; FLOAT yz = y*z; + FLOAT wx = w*x; FLOAT wy = w*y; FLOAT wz = w*z; + + mat._11 = 1 - 2 * ( yy + zz ); + mat._12 = 2 * ( xy - wz ); + mat._13 = 2 * ( xz + wy ); + + mat._21 = 2 * ( xy + wz ); + mat._22 = 1 - 2 * ( xx + zz ); + mat._23 = 2 * ( yz - wx ); + + mat._31 = 2 * ( xz - wy ); + mat._32 = 2 * ( yz + wx ); + mat._33 = 1 - 2 * ( xx + yy ); + + mat._14 = mat._24 = mat._34 = 0.0f; + mat._41 = mat._42 = mat._43 = 0.0f; + mat._44 = 1.0f; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DMath_QuaternionFromMatrix() +// Desc: Converts a rotation matrix into a unit quaternion. +//----------------------------------------------------------------------------- +VOID D3DMath_QuaternionFromMatrix( FLOAT& x, FLOAT& y, FLOAT& z, FLOAT& w, + D3DMATRIX& mat ) +{ + if( mat._11 + mat._22 + mat._33 > 0.0f ) + { + FLOAT s = sqrtf( mat._11 + mat._22 + mat._33 + mat._44 ); + + x = (mat._23-mat._32) / (2*s); + y = (mat._31-mat._13) / (2*s); + z = (mat._12-mat._21) / (2*s); + w = 0.5f * s; + } + else + { + + + } + FLOAT xx = x*x; FLOAT yy = y*y; FLOAT zz = z*z; + FLOAT xy = x*y; FLOAT xz = x*z; FLOAT yz = y*z; + FLOAT wx = w*x; FLOAT wy = w*y; FLOAT wz = w*z; + + mat._11 = 1 - 2 * ( yy + zz ); + mat._12 = 2 * ( xy - wz ); + mat._13 = 2 * ( xz + wy ); + + mat._21 = 2 * ( xy + wz ); + mat._22 = 1 - 2 * ( xx + zz ); + mat._23 = 2 * ( yz - wx ); + + mat._31 = 2 * ( xz - wy ); + mat._32 = 2 * ( yz + wx ); + mat._33 = 1 - 2 * ( xx + yy ); + + mat._14 = mat._24 = mat._34 = 0.0f; + mat._41 = mat._42 = mat._43 = 0.0f; + mat._44 = 1.0f; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DMath_QuaternionMultiply() +// Desc: Mulitples two quaternions together as in {Q} = {A} * {B}. +//----------------------------------------------------------------------------- +VOID D3DMath_QuaternionMultiply( FLOAT& Qx, FLOAT& Qy, FLOAT& Qz, FLOAT& Qw, + FLOAT Ax, FLOAT Ay, FLOAT Az, FLOAT Aw, + FLOAT Bx, FLOAT By, FLOAT Bz, FLOAT Bw ) +{ + FLOAT Dx = Ax*Bw + Ay*Bz - Az*By + Aw*Bx; + FLOAT Dy = -Ax*Bz + Ay*Bw + Az*Bx + Aw*By; + FLOAT Dz = Ax*By - Ay*Bx + Az*Bw + Aw*Bz; + FLOAT Dw = -Ax*Bx - Ay*By - Az*Bz + Aw*Bw; + + Qx = Dx; Qy = Dy; Qz = Dz; Qw = Dw; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DMath_SlerpQuaternions() +// Desc: Compute a quaternion which is the spherical linear interpolation +// between two other quaternions by dvFraction. +//----------------------------------------------------------------------------- +VOID D3DMath_QuaternionSlerp( FLOAT& Qx, FLOAT& Qy, FLOAT& Qz, FLOAT& Qw, + FLOAT Ax, FLOAT Ay, FLOAT Az, FLOAT Aw, + FLOAT Bx, FLOAT By, FLOAT Bz, FLOAT Bw, + FLOAT fAlpha ) +{ + // Compute dot product (equal to cosine of the angle between quaternions) + FLOAT fCosTheta = Ax*Bx + Ay*By + Az*Bz + Aw*Bw; + + // Check angle to see if quaternions are in opposite hemispheres + if( fCosTheta < 0.0f ) + { + // If so, flip one of the quaterions + fCosTheta = -fCosTheta; + Bx = -Bx; By = -By; Bz = -Bz; Bw = -Bw; + } + + // Set factors to do linear interpolation, as a special case where the + // quaternions are close together. + FLOAT fBeta = 1.0f - fAlpha; + + // If the quaternions aren't close, proceed with spherical interpolation + if( 1.0f - fCosTheta > 0.001f ) + { + FLOAT fTheta = acosf( fCosTheta ); + + fBeta = sinf( fTheta*fBeta ) / sinf( fTheta); + fAlpha = sinf( fTheta*fAlpha ) / sinf( fTheta); + } + + // Do the interpolation + Qx = fBeta*Ax + fAlpha*Bx; + Qy = fBeta*Ay + fAlpha*By; + Qz = fBeta*Az + fAlpha*Bz; + Qw = fBeta*Aw + fAlpha*Bw; +} + + + + diff --git a/src/d3dmath.h b/src/d3dmath.h new file mode 100644 index 00000000..8315d88b --- /dev/null +++ b/src/d3dmath.h @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// File: D3DMath.h +// +// Desc: Math functions and shortcuts for Direct3D programming. +// +// Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved +//----------------------------------------------------------------------------- +#ifndef D3DMATH_H +#define D3DMATH_H +#include +#include + + +//----------------------------------------------------------------------------- +// Useful Math constants +//----------------------------------------------------------------------------- +const FLOAT g_PI = 3.14159265358979323846f; // Pi +const FLOAT g_2_PI = 6.28318530717958623200f; // 2 * Pi +const FLOAT g_PI_DIV_2 = 1.57079632679489655800f; // Pi / 2 +const FLOAT g_PI_DIV_4 = 0.78539816339744827900f; // Pi / 4 +const FLOAT g_INV_PI = 0.31830988618379069122f; // 1 / Pi +const FLOAT g_DEGTORAD = 0.01745329251994329547f; // Degrees to Radians +const FLOAT g_RADTODEG = 57.29577951308232286465f; // Radians to Degrees +const FLOAT g_HUGE = 1.0e+38f; // Huge number for FLOAT +const FLOAT g_EPSILON = 1.0e-5f; // Tolerance for FLOATs + + + + +//----------------------------------------------------------------------------- +// Fuzzy compares (within tolerance) +//----------------------------------------------------------------------------- +inline BOOL D3DMath_IsZero( FLOAT a, FLOAT fTol = g_EPSILON ) +{ return ( a <= 0.0f ) ? ( a >= -fTol ) : ( a <= fTol ); } + + + + +//----------------------------------------------------------------------------- +// Matrix functions +//----------------------------------------------------------------------------- +VOID D3DMath_MatrixMultiply( D3DMATRIX& q, D3DMATRIX& a, D3DMATRIX& b ); +HRESULT D3DMath_MatrixInvert( D3DMATRIX& q, D3DMATRIX& a ); + + + + +//----------------------------------------------------------------------------- +// Vector functions +//----------------------------------------------------------------------------- +HRESULT D3DMath_VectorMatrixMultiply( D3DVECTOR& vDest, D3DVECTOR& vSrc, + D3DMATRIX& mat); +HRESULT D3DMath_VertexMatrixMultiply( D3DVERTEX& vDest, D3DVERTEX& vSrc, + D3DMATRIX& mat ); + + + + +//----------------------------------------------------------------------------- +// Quaternion functions +//----------------------------------------------------------------------------- +VOID D3DMath_QuaternionFromRotation( FLOAT& x, FLOAT& y, FLOAT& z, FLOAT& w, + D3DVECTOR& v, FLOAT fTheta ); +VOID D3DMath_RotationFromQuaternion( D3DVECTOR& v, FLOAT& fTheta, + FLOAT x, FLOAT y, FLOAT z, FLOAT w ); +VOID D3DMath_QuaternionFromAngles( FLOAT& x, FLOAT& y, FLOAT& z, FLOAT& w, + FLOAT fYaw, FLOAT fPitch, FLOAT fRoll ); +VOID D3DMath_MatrixFromQuaternion( D3DMATRIX& mat, FLOAT x, FLOAT y, FLOAT z, + FLOAT w ); +VOID D3DMath_QuaternionFromMatrix( FLOAT &x, FLOAT &y, FLOAT &z, FLOAT &w, + D3DMATRIX& mat ); +VOID D3DMath_QuaternionMultiply( FLOAT& Qx, FLOAT& Qy, FLOAT& Qz, FLOAT& Qw, + FLOAT Ax, FLOAT Ay, FLOAT Az, FLOAT Aw, + FLOAT Bx, FLOAT By, FLOAT Bz, FLOAT Bw ); +VOID D3DMath_QuaternionSlerp( FLOAT& Qx, FLOAT& Qy, FLOAT& Qz, FLOAT& Qw, + FLOAT Ax, FLOAT Ay, FLOAT Az, FLOAT Aw, + FLOAT Bx, FLOAT By, FLOAT Bz, FLOAT Bw, + FLOAT fAlpha ); + + +#endif // D3DMATH_H diff --git a/src/d3dres.h b/src/d3dres.h new file mode 100644 index 00000000..c9d1df94 --- /dev/null +++ b/src/d3dres.h @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------------- +// File: D3DRes.h +// +// Desc: Resource definitions required by the CD3DApplication class. +// Any application using the CD3DApplication class must include resources +// with the following identifiers. +// +// Copyright (c) 1999 Microsoft Corporation. All rights reserved. +//----------------------------------------------------------------------------- +#ifndef D3DRES_H +#define D3DRES_H + + +#define IDI_MAIN_ICON 101 // Application icon +#define IDR_MAIN_ACCEL 113 // Keyboard accelerator +#define IDR_MENU 141 // Application menu +#define IDR_POPUP 142 // Popup menu +#define IDD_ABOUT 143 // About dialog box +#define IDD_CHANGEDEVICE 144 // "Change Device" dialog box +#define IDC_CURSORHAND 149 +#define IDC_CURSORSCROLLL 150 +#define IDC_CURSORSCROLLR 151 +#define IDC_CURSORSCROLLU 152 +#define IDC_CURSORSCROLLD 153 +#define IDC_CURSORTARGET 154 + +#define IDC_DEVICE_COMBO 1000 // Device combobox for "Change Device" dlg +#define IDC_MODE_COMBO 1001 // Mode combobox for "Change Device" dlg +#define IDC_WINDOWED_CHECKBOX 1012 // Checkbox for windowed-mode +#define IDC_STEREO_CHECKBOX 1013 // Checkbox for stereo modes +#define IDC_FULLSCREEN_TEXT 1014 // Group box text label + +#define IDM_ABOUT 40001 // Command to invoke About dlg +#define IDM_CHANGEDEVICE 40002 // Command to invoke "Change Device" dlg +#define IDM_TOGGLEFULLSCREEN 40003 // Command to toggle fullscreen mode +#define IDM_TOGGLESTART 40004 // Command to toggle frame animation +#define IDM_SINGLESTEP 40005 // Command to single step frame animation +#define IDM_EXIT 40006 // Command to exit the application + + + + +#endif // D3DRES_H diff --git a/src/d3dtextr.cpp b/src/d3dtextr.cpp new file mode 100644 index 00000000..f3364f69 --- /dev/null +++ b/src/d3dtextr.cpp @@ -0,0 +1,1064 @@ +//----------------------------------------------------------------------------- +// File: D3DTextr.cpp +// +// Desc: Functions to manage textures, including creating (loading from a +// file), restoring lost surfaces, invalidating, and destroying. +// +// Note: the implementation of these fucntions maintain an internal list +// of loaded textures. After creation, individual textures are referenced +// via their ASCII names. +// +// Copyright (c) 1996-1999 Microsoft Corporation. All rights reserved +//----------------------------------------------------------------------------- +#define STRICT +#include +#include +#include "D3DTextr.h" +#include "D3DUtil.h" +#include "language.h" +#include "misc.h" + + + + +//----------------------------------------------------------------------------- +// Macros, function prototypes and static variable +//----------------------------------------------------------------------------- +static TCHAR g_strTexturePath[512] = _T(""); // Path for files +static BOOL g_bDebugMode = FALSE; + + + +void D3DTextr_SetDebugMode(BOOL bDebug) +{ + g_bDebugMode = bDebug; +} + + + +//----------------------------------------------------------------------------- +// Name: TextureContainer +// Desc: Linked list structure to hold info per texture +//----------------------------------------------------------------------------- +struct TextureContainer +{ + TextureContainer* m_pNext; // Linked list ptr + + TCHAR m_strName[80]; // Name of texture (doubles as image filename) + DWORD m_dwWidth; + DWORD m_dwHeight; + DWORD m_dwStage; // Texture stage (for multitexture devices) + DWORD m_dwBPP; + DWORD m_dwFlags; + BOOL m_bHasAlpha; + + LPDIRECTDRAWSURFACE7 m_pddsSurface; // Surface of the texture + HBITMAP m_hbmBitmap; // Bitmap containing texture image + DWORD* m_pRGBAData; + +public: + HRESULT LoadImageData(); + HRESULT LoadBitmapFile( TCHAR* strPathname ); + HRESULT LoadTargaFile( TCHAR* strPathname, TCHAR* strFilename ); + HRESULT Restore( LPDIRECT3DDEVICE7 pd3dDevice ); + HRESULT CopyBitmapToSurface(); + HRESULT CopyRGBADataToSurface(); + + TextureContainer( TCHAR* strName, DWORD dwStage, DWORD dwFlags ); + ~TextureContainer(); +}; + +// Local list of textures +static TextureContainer* g_ptcTextureList = NULL; + + + + +//----------------------------------------------------------------------------- +// Name: CD3DTextureManager +// Desc: Class used to automatically construct and destruct the static +// texture engine class. +//----------------------------------------------------------------------------- +class CD3DTextureManager +{ +public: + CD3DTextureManager() {} + ~CD3DTextureManager() { if( g_ptcTextureList ) delete g_ptcTextureList; } +}; + +// Global instance +CD3DTextureManager g_StaticTextureEngine; + + + + +//----------------------------------------------------------------------------- +// Name: struct TEXTURESEARCHINFO +// Desc: Structure used to search for texture formats +//----------------------------------------------------------------------------- +struct TEXTURESEARCHINFO +{ + DWORD dwDesiredBPP; // Input for texture format search + BOOL bUseAlpha; + BOOL bUsePalette; + BOOL bFoundGoodFormat; + + DDPIXELFORMAT* pddpf; // Output of texture format search +}; + + + + +//----------------------------------------------------------------------------- +// Name: TextureSearchCallback() +// Desc: Enumeration callback routine to find a best-matching texture format. +// The param data is the DDPIXELFORMAT of the best-so-far matching +// texture. Note: the desired BPP is passed in the dwSize field, and the +// default BPP is passed in the dwFlags field. +//----------------------------------------------------------------------------- +static HRESULT CALLBACK TextureSearchCallback( DDPIXELFORMAT* pddpf, + VOID* param ) +{ + if( NULL==pddpf || NULL==param ) + return DDENUMRET_OK; + + TEXTURESEARCHINFO* ptsi = (TEXTURESEARCHINFO*)param; + + // Skip any funky modes + if( pddpf->dwFlags & (DDPF_LUMINANCE|DDPF_BUMPLUMINANCE|DDPF_BUMPDUDV) ) + return DDENUMRET_OK; + + // Check for palettized formats + if( ptsi->bUsePalette ) + { + if( !( pddpf->dwFlags & DDPF_PALETTEINDEXED8 ) ) + return DDENUMRET_OK; + + // Accept the first 8-bit palettized format we get + memcpy( ptsi->pddpf, pddpf, sizeof(DDPIXELFORMAT) ); + ptsi->bFoundGoodFormat = TRUE; + return DDENUMRET_CANCEL; + } + + // Else, skip any paletized formats (all modes under 16bpp) + if( pddpf->dwRGBBitCount < 16 ) + return DDENUMRET_OK; + + // Skip any FourCC formats + if( pddpf->dwFourCC != 0 ) + return DDENUMRET_OK; + + // Skip any ARGB 4444 formats (which are best used for pre-authored + // content designed speciafically for an ARGB 4444 format). + if( pddpf->dwRGBAlphaBitMask == 0x0000f000 ) + return DDENUMRET_OK; + + // Make sure current alpha format agrees with requested format type + if( (ptsi->bUseAlpha==TRUE) && !(pddpf->dwFlags&DDPF_ALPHAPIXELS) ) + return DDENUMRET_OK; + if( (ptsi->bUseAlpha==FALSE) && (pddpf->dwFlags&DDPF_ALPHAPIXELS) ) + return DDENUMRET_OK; + + // Check if we found a good match + if( pddpf->dwRGBBitCount == ptsi->dwDesiredBPP ) + { + memcpy( ptsi->pddpf, pddpf, sizeof(DDPIXELFORMAT) ); + ptsi->bFoundGoodFormat = TRUE; + return DDENUMRET_CANCEL; + } + + return DDENUMRET_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: FindTexture() +// Desc: Searches the internal list of textures for a texture specified by +// its name. Returns the structure associated with that texture. +//----------------------------------------------------------------------------- +static TextureContainer* FindTexture( TCHAR* strTextureName ) +{ + TextureContainer* ptcTexture = g_ptcTextureList; + + while( ptcTexture ) + { + if( !lstrcmpi( strTextureName, ptcTexture->m_strName ) ) + return ptcTexture; + ptcTexture = ptcTexture->m_pNext; + } + + return NULL; +} + + + + +//----------------------------------------------------------------------------- +// Name: TextureContainer() +// Desc: Constructor for a texture object +//----------------------------------------------------------------------------- +TextureContainer::TextureContainer( TCHAR* strName, DWORD dwStage, + DWORD dwFlags ) +{ + lstrcpy( m_strName, strName ); + m_dwWidth = 0; + m_dwHeight = 0; + m_dwStage = dwStage; + m_dwBPP = 0; + m_dwFlags = dwFlags; + m_bHasAlpha = 0; + + m_pddsSurface = NULL; + m_hbmBitmap = NULL; + m_pRGBAData = NULL; + + // Add the texture to the head of the global texture list + m_pNext = g_ptcTextureList; + g_ptcTextureList = this; + +} + + + + +//----------------------------------------------------------------------------- +// Name: ~TextureContainer() +// Desc: Destructs the contents of the texture container +//----------------------------------------------------------------------------- +TextureContainer::~TextureContainer() +{ + SAFE_RELEASE( m_pddsSurface ); + SAFE_DELETE( m_pRGBAData ); + DeleteObject( m_hbmBitmap ); + + // Remove the texture container from the global list + if( g_ptcTextureList == this ) + g_ptcTextureList = m_pNext; + else + { + for( TextureContainer* ptc=g_ptcTextureList; ptc; ptc=ptc->m_pNext ) + if( ptc->m_pNext == this ) + ptc->m_pNext = m_pNext; + } +} + + + + +//----------------------------------------------------------------------------- +// Name: LoadImageData() +// Desc: Loads the texture map's image data +//----------------------------------------------------------------------------- +HRESULT TextureContainer::LoadImageData() +{ + TCHAR* strExtension; + TCHAR strMetaname[256]; + TCHAR strFilename[256]; + + if ( g_bDebugMode ) + { + if ( _tcsrchr( m_strName, _T('\\') ) == 0 ) + { + lstrcpy( strMetaname, "" ); + lstrcpy( strFilename, g_strTexturePath ); + lstrcat( strFilename, m_strName ); + } + else + { + lstrcpy( strMetaname, "" ); + lstrcpy( strFilename, m_strName ); + } + } + else + { + if ( _tcsrchr( m_strName, _T('\\') ) == 0 ) + { +#if _SCHOOL + lstrcpy( strMetaname, "ceebot1.dat" ); +#else + lstrcpy( strMetaname, "colobot1.dat" ); +#endif + lstrcpy( strFilename, m_strName ); + } + else + { + lstrcpy( strMetaname, "" ); + lstrcpy( strFilename, m_strName ); + } + } + + if ( !g_metafile.IsExist(strMetaname, strFilename) ) + { + return DDERR_NOTFOUND; + } + + // Get the filename extension + if ( NULL == ( strExtension = _tcsrchr( m_strName, _T('.') ) ) ) + { + return DDERR_UNSUPPORTED; + } + + // Load bitmap files + if ( strMetaname[0] == 0 && !lstrcmpi( strExtension, _T(".bmp") ) ) + { + return LoadBitmapFile( strFilename ); + } + + // Load targa files + if ( !lstrcmpi( strExtension, _T(".tga") ) ) + { + return LoadTargaFile( strMetaname, strFilename ); + } + + // Can add code here to check for other file formats before failing + return DDERR_UNSUPPORTED; +} + + + + +//----------------------------------------------------------------------------- +// Name: LoadBitmapFile() +// Desc: Loads data from a .bmp file, and stores it in a bitmap structure. +//----------------------------------------------------------------------------- +HRESULT TextureContainer::LoadBitmapFile( TCHAR* strPathname ) +{ + // Try to load the bitmap as a file + m_hbmBitmap = (HBITMAP)LoadImage( NULL, strPathname, IMAGE_BITMAP, 0, 0, + LR_LOADFROMFILE|LR_CREATEDIBSECTION ); + if( m_hbmBitmap ) + return S_OK; + + return DDERR_NOTFOUND; +} + + + + +//----------------------------------------------------------------------------- +// Name: LoadTargaFile() +// Desc: Loads RGBA data from a .tga file, and stores it in allocated memory +// for the specified texture container +//----------------------------------------------------------------------------- +HRESULT TextureContainer::LoadTargaFile( TCHAR* strMetaname, TCHAR* strFilename ) +{ + if( g_metafile.Open(strMetaname, strFilename) != 0 ) + return E_FAIL; + + struct TargaHeader + { + BYTE IDLength; + BYTE ColormapType; + BYTE ImageType; + BYTE ColormapSpecification[5]; + WORD XOrigin; + WORD YOrigin; + WORD ImageWidth; + WORD ImageHeight; + BYTE PixelDepth; + BYTE ImageDescriptor; + } tga; + + g_metafile.Read(&tga, sizeof(TargaHeader)); + + // Only true color, non-mapped images are supported + if( ( 0 != tga.ColormapType ) || + ( tga.ImageType != 10 && tga.ImageType != 2 ) ) + { + g_metafile.Close(); + return E_FAIL; + } + + // Skip the ID field. The first byte of the header is the length of this field + if( tga.IDLength ) + { + g_metafile.Seek(tga.IDLength); + } + + m_dwWidth = tga.ImageWidth; + m_dwHeight = tga.ImageHeight; + m_dwBPP = tga.PixelDepth; + m_pRGBAData = new DWORD[m_dwWidth*m_dwHeight]; + + if( m_pRGBAData == NULL ) + { + g_metafile.Close(); + return E_FAIL; + } + + for( DWORD y=0; yGetCaps( &ddDesc) ) ) + return E_FAIL; + + // Setup the new surface desc + DDSURFACEDESC2 ddsd; + D3DUtil_InitSurfaceDesc( ddsd ); + ddsd.dwFlags = DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH| + DDSD_PIXELFORMAT|DDSD_TEXTURESTAGE; + ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + ddsd.dwTextureStage = m_dwStage; + ddsd.dwWidth = m_dwWidth; + ddsd.dwHeight = m_dwHeight; + + // Turn on texture management for hardware devices + if( ddDesc.deviceGUID == IID_IDirect3DHALDevice ) + ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE; + else if( ddDesc.deviceGUID == IID_IDirect3DTnLHalDevice ) + ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE; + else + ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + + // Adjust width and height to be powers of 2, if the device requires it + if( ddDesc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_POW2 ) + { + for( ddsd.dwWidth=1; m_dwWidth>ddsd.dwWidth; ddsd.dwWidth<<=1 ); + for( ddsd.dwHeight=1; m_dwHeight>ddsd.dwHeight; ddsd.dwHeight<<=1 ); + } + + // Limit max texture sizes, if the driver can't handle large textures + DWORD dwMaxWidth = ddDesc.dwMaxTextureWidth; + DWORD dwMaxHeight = ddDesc.dwMaxTextureHeight; + ddsd.dwWidth = min( ddsd.dwWidth, ( dwMaxWidth ? dwMaxWidth : 256 ) ); + ddsd.dwHeight = min( ddsd.dwHeight, ( dwMaxHeight ? dwMaxHeight : 256 ) ); + + // Make the texture square, if the driver requires it + if( ddDesc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_SQUAREONLY ) + { + if( ddsd.dwWidth > ddsd.dwHeight ) ddsd.dwHeight = ddsd.dwWidth; + else ddsd.dwWidth = ddsd.dwHeight; + } + + // Setup the structure to be used for texture enumration. + TEXTURESEARCHINFO tsi; + tsi.bFoundGoodFormat = FALSE; + tsi.pddpf = &ddsd.ddpfPixelFormat; + tsi.dwDesiredBPP = m_dwBPP; + tsi.bUsePalette = ( m_dwBPP <= 8 ); + tsi.bUseAlpha = m_bHasAlpha; + if( m_dwFlags & D3DTEXTR_16BITSPERPIXEL ) + tsi.dwDesiredBPP = 16; + else if( m_dwFlags & D3DTEXTR_32BITSPERPIXEL ) + tsi.dwDesiredBPP = 32; + + if( m_dwFlags & (D3DTEXTR_TRANSPARENTWHITE|D3DTEXTR_TRANSPARENTBLACK) ) + { + if( tsi.bUsePalette ) + { + if( ddDesc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_ALPHAPALETTE ) + { + tsi.bUseAlpha = TRUE; + tsi.bUsePalette = TRUE; + } + else + { + tsi.bUseAlpha = TRUE; + tsi.bUsePalette = FALSE; + } + } + } + + // Enumerate the texture formats, and find the closest device-supported + // texture pixel format + pd3dDevice->EnumTextureFormats( TextureSearchCallback, &tsi ); + + // If we couldn't find a format, let's try a default format + if( FALSE == tsi.bFoundGoodFormat ) + { + tsi.bUsePalette = FALSE; + tsi.dwDesiredBPP = 16; + pd3dDevice->EnumTextureFormats( TextureSearchCallback, &tsi ); + + // If we still fail, we cannot create this texture + if( FALSE == tsi.bFoundGoodFormat ) + return E_FAIL; + } + + // Get the DirectDraw interface for creating surfaces + LPDIRECTDRAW7 pDD; + LPDIRECTDRAWSURFACE7 pddsRender; + pd3dDevice->GetRenderTarget( &pddsRender ); + pddsRender->GetDDInterface( (VOID**)&pDD ); + pddsRender->Release(); + + // Create a new surface for the texture + HRESULT hr = pDD->CreateSurface( &ddsd, &m_pddsSurface, NULL ); + + // Done with DDraw + pDD->Release(); + + if( FAILED(hr) ) + return hr; + + // For bitmap-based textures, copy the bitmap image. + if( m_hbmBitmap ) + return CopyBitmapToSurface(); + + if( m_pRGBAData ) + return CopyRGBADataToSurface(); + + // At this point, code can be added to handle other file formats (such as + // .dds files, .jpg files, etc.). + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: CopyBitmapToSurface() +// Desc: Copies the image of a bitmap into a surface +//----------------------------------------------------------------------------- +HRESULT TextureContainer::CopyBitmapToSurface() +{ + // Get a DDraw object to create a temporary surface + LPDIRECTDRAW7 pDD; + m_pddsSurface->GetDDInterface( (VOID**)&pDD ); + + // Get the bitmap structure (to extract width, height, and bpp) + BITMAP bm; + GetObject( m_hbmBitmap, sizeof(BITMAP), &bm ); + + // Setup the new surface desc + DDSURFACEDESC2 ddsd; + ddsd.dwSize = sizeof(ddsd); + m_pddsSurface->GetSurfaceDesc( &ddsd ); + ddsd.dwFlags = DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT| + DDSD_TEXTURESTAGE; + ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE|DDSCAPS_SYSTEMMEMORY; + ddsd.ddsCaps.dwCaps2 = 0L; + ddsd.dwWidth = bm.bmWidth; + ddsd.dwHeight = bm.bmHeight; + + // Create a new surface for the texture + LPDIRECTDRAWSURFACE7 pddsTempSurface; + HRESULT hr; + if( FAILED( hr = pDD->CreateSurface( &ddsd, &pddsTempSurface, NULL ) ) ) + { + pDD->Release(); + return hr; + } + + // Get a DC for the bitmap + HDC hdcBitmap = CreateCompatibleDC( NULL ); + if( NULL == hdcBitmap ) + { + pddsTempSurface->Release(); + pDD->Release(); + return hr; + } + SelectObject( hdcBitmap, m_hbmBitmap ); + + // Handle palettized textures. Need to attach a palette + if( ddsd.ddpfPixelFormat.dwRGBBitCount == 8 ) + { + LPDIRECTDRAWPALETTE pPalette; + DWORD dwPaletteFlags = DDPCAPS_8BIT|DDPCAPS_ALLOW256; + DWORD pe[256]; + WORD wNumColors = GetDIBColorTable( hdcBitmap, 0, 256, (RGBQUAD*)pe ); + + // Create the color table + for( WORD i=0; iCreatePalette( dwPaletteFlags, (PALETTEENTRY*)pe, &pPalette, NULL ); + pddsTempSurface->SetPalette( pPalette ); + m_pddsSurface->SetPalette( pPalette ); + SAFE_RELEASE( pPalette ); + } + + // Copy the bitmap image to the surface. + HDC hdcSurface; + if( SUCCEEDED( pddsTempSurface->GetDC( &hdcSurface ) ) ) + { + BitBlt( hdcSurface, 0, 0, bm.bmWidth, bm.bmHeight, hdcBitmap, 0, 0, + SRCCOPY ); + pddsTempSurface->ReleaseDC( hdcSurface ); + } + DeleteDC( hdcBitmap ); + + // Copy the temp surface to the real texture surface + m_pddsSurface->Blt( NULL, pddsTempSurface, NULL, DDBLT_WAIT, NULL ); + + // Done with the temp surface + pddsTempSurface->Release(); + + // For textures with real alpha (not palettized), set transparent bits + if( ddsd.ddpfPixelFormat.dwRGBAlphaBitMask ) + { + if( m_dwFlags & (D3DTEXTR_TRANSPARENTWHITE|D3DTEXTR_TRANSPARENTBLACK) ) + { + // Lock the texture surface + DDSURFACEDESC2 ddsd; + ddsd.dwSize = sizeof(ddsd); + while( m_pddsSurface->Lock( NULL, &ddsd, 0, NULL ) == + DDERR_WASSTILLDRAWING ); + + DWORD dwAlphaMask = ddsd.ddpfPixelFormat.dwRGBAlphaBitMask; + DWORD dwRGBMask = ( ddsd.ddpfPixelFormat.dwRBitMask | + ddsd.ddpfPixelFormat.dwGBitMask | + ddsd.ddpfPixelFormat.dwBBitMask ); + DWORD dwColorkey = 0x00000000; // Colorkey on black + if( m_dwFlags & D3DTEXTR_TRANSPARENTWHITE ) + dwColorkey = dwRGBMask; // Colorkey on white + + // Add an opaque alpha value to each non-colorkeyed pixel + for( DWORD y=0; yUnlock( NULL ); + } + } + + pDD->Release(); + + return S_OK;; +} + + + + +//----------------------------------------------------------------------------- +// Name: CopyRGBADataToSurface() +// Desc: Invalidates the current texture objects and rebuilds new ones +// using the new device. +//----------------------------------------------------------------------------- +HRESULT TextureContainer::CopyRGBADataToSurface() +{ + // Get a DDraw object to create a temporary surface + LPDIRECTDRAW7 pDD; + m_pddsSurface->GetDDInterface( (VOID**)&pDD ); + + // Setup the new surface desc + DDSURFACEDESC2 ddsd; + ddsd.dwSize = sizeof(ddsd); + m_pddsSurface->GetSurfaceDesc( &ddsd ); + ddsd.dwFlags = DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT| + DDSD_TEXTURESTAGE; + ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE|DDSCAPS_SYSTEMMEMORY; + ddsd.ddsCaps.dwCaps2 = 0L; + ddsd.dwWidth = m_dwWidth; + ddsd.dwHeight = m_dwHeight; + + // Create a new surface for the texture + LPDIRECTDRAWSURFACE7 pddsTempSurface; + HRESULT hr; + if( FAILED( hr = pDD->CreateSurface( &ddsd, &pddsTempSurface, NULL ) ) ) + { + pDD->Release(); + return NULL; + } + + while( pddsTempSurface->Lock( NULL, &ddsd, 0, 0 ) == DDERR_WASSTILLDRAWING ); + DWORD lPitch = ddsd.lPitch; + BYTE* pBytes = (BYTE*)ddsd.lpSurface; + + DWORD dwRMask = ddsd.ddpfPixelFormat.dwRBitMask; + DWORD dwGMask = ddsd.ddpfPixelFormat.dwGBitMask; + DWORD dwBMask = ddsd.ddpfPixelFormat.dwBBitMask; + DWORD dwAMask = ddsd.ddpfPixelFormat.dwRGBAlphaBitMask; + + DWORD dwRShiftL = 8, dwRShiftR = 0; + DWORD dwGShiftL = 8, dwGShiftR = 0; + DWORD dwBShiftL = 8, dwBShiftR = 0; + DWORD dwAShiftL = 8, dwAShiftR = 0; + + DWORD dwMask; + for( dwMask=dwRMask; dwMask && !(dwMask&0x1); dwMask>>=1 ) dwRShiftR++; + for( ; dwMask; dwMask>>=1 ) dwRShiftL--; + + for( dwMask=dwGMask; dwMask && !(dwMask&0x1); dwMask>>=1 ) dwGShiftR++; + for( ; dwMask; dwMask>>=1 ) dwGShiftL--; + + for( dwMask=dwBMask; dwMask && !(dwMask&0x1); dwMask>>=1 ) dwBShiftR++; + for( ; dwMask; dwMask>>=1 ) dwBShiftL--; + + for( dwMask=dwAMask; dwMask && !(dwMask&0x1); dwMask>>=1 ) dwAShiftR++; + for( ; dwMask; dwMask>>=1 ) dwAShiftL--; + + for( DWORD y=0; y>24)&0x000000ff); + BYTE g = (BYTE)((dwPixel>>16)&0x000000ff); + BYTE b = (BYTE)((dwPixel>> 8)&0x000000ff); + BYTE a = (BYTE)((dwPixel>> 0)&0x000000ff); + + DWORD dr = ((r>>(dwRShiftL))<>(dwGShiftL))<>(dwBShiftL))<>(dwAShiftL))<Unlock(0); + + // Copy the temp surface to the real texture surface + m_pddsSurface->Blt( NULL, pddsTempSurface, NULL, DDBLT_WAIT, NULL ); + + // Done with the temp objects + pddsTempSurface->Release(); + pDD->Release(); + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DTextr_SetTexturePath() +// Desc: Enumeration callback routine to find a best-matching texture format. +//----------------------------------------------------------------------------- +VOID D3DTextr_SetTexturePath( TCHAR* strTexturePath ) +{ + if( NULL == strTexturePath ) + strTexturePath = _T(""); + lstrcpy( g_strTexturePath, strTexturePath ); +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DTextr_CreateTextureFromFile() +// Desc: Is passed a filename and creates a local Bitmap from that file. +// The texture can not be used until it is restored, however. +//----------------------------------------------------------------------------- +HRESULT D3DTextr_CreateTextureFromFile( TCHAR* strName, DWORD dwStage, + DWORD dwFlags ) +{ + // Check parameters + if( NULL == strName ) + return E_INVALIDARG; + + // Check first to see if the texture is already loaded + if( NULL != FindTexture( strName ) ) + return S_OK; + + // Allocate and add the texture to the linked list of textures; + TextureContainer* ptcTexture = new TextureContainer( strName, dwStage, + dwFlags ); + if( NULL == ptcTexture ) + return E_OUTOFMEMORY; + + // Create a bitmap and load the texture file into it, + if( FAILED( ptcTexture->LoadImageData() ) ) + { + delete ptcTexture; + return E_FAIL; + } + + // Save the image's dimensions + if( ptcTexture->m_hbmBitmap ) + { + BITMAP bm; + GetObject( ptcTexture->m_hbmBitmap, sizeof(BITMAP), &bm ); + ptcTexture->m_dwWidth = (DWORD)bm.bmWidth; + ptcTexture->m_dwHeight = (DWORD)bm.bmHeight; + ptcTexture->m_dwBPP = (DWORD)bm.bmBitsPixel; + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DTextr_CreateEmptyTexture() +// Desc: Creates an empty texture. +//----------------------------------------------------------------------------- +HRESULT D3DTextr_CreateEmptyTexture( TCHAR* strName, DWORD dwWidth, + DWORD dwHeight, DWORD dwStage, + DWORD dwFlags ) +{ + // Check parameters + if( NULL == strName ) + return E_INVALIDARG; + + // Check first to see if the texture is already loaded + if( NULL != FindTexture( strName ) ) + return E_FAIL; + + // Allocate and add the texture to the linked list of textures; + TextureContainer* ptcTexture = new TextureContainer( strName, dwStage, + dwFlags ); + if( NULL == ptcTexture ) + return E_OUTOFMEMORY; + + // Save dimensions + ptcTexture->m_dwWidth = dwWidth; + ptcTexture->m_dwHeight = dwHeight; + ptcTexture->m_dwBPP = 16; + if( ptcTexture->m_dwFlags & D3DTEXTR_32BITSPERPIXEL ) + ptcTexture->m_dwBPP = 32; + + // Save alpha usage flag + if( dwFlags & D3DTEXTR_CREATEWITHALPHA ) + ptcTexture->m_bHasAlpha = TRUE; + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DTextr_Restore() +// Desc: Invalidates the current texture objects and rebuilds new ones +// using the new device. +//----------------------------------------------------------------------------- +HRESULT D3DTextr_Restore( TCHAR* strName, LPDIRECT3DDEVICE7 pd3dDevice ) +{ + TextureContainer* ptcTexture = FindTexture( strName ); + if( NULL == ptcTexture ) + return DDERR_NOTFOUND; + + // Restore the texture (this recreates the new surface for this device). + return ptcTexture->Restore( pd3dDevice ); +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DTextr_RestoreAllTextures() +// Desc: This function is called when a mode is changed. It updates all +// texture objects to be valid with the new device. +//----------------------------------------------------------------------------- +HRESULT D3DTextr_RestoreAllTextures( LPDIRECT3DDEVICE7 pd3dDevice ) +{ + TextureContainer* ptcTexture = g_ptcTextureList; + + while( ptcTexture ) + { + D3DTextr_Restore( ptcTexture->m_strName, pd3dDevice ); + ptcTexture = ptcTexture->m_pNext; + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DTextr_Invalidate() +// Desc: Used to bump a texture out of (video) memory, this function +// actually destroys the d3dtexture and ddsurface of the texture +//----------------------------------------------------------------------------- +HRESULT D3DTextr_Invalidate( TCHAR* strName ) +{ + TextureContainer* ptcTexture = FindTexture( strName ); + if( NULL == ptcTexture ) + return DDERR_NOTFOUND; + + SAFE_RELEASE( ptcTexture->m_pddsSurface ); + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DTextr_InvalidateAllTextures() +// Desc: This function is called when a mode is changed. It invalidates +// all texture objects so their device can be safely released. +//----------------------------------------------------------------------------- +HRESULT D3DTextr_InvalidateAllTextures() +{ + TextureContainer* ptcTexture = g_ptcTextureList; + + while( ptcTexture ) + { + SAFE_RELEASE( ptcTexture->m_pddsSurface ); + ptcTexture = ptcTexture->m_pNext; + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DTextr_DestroyTexture() +// Desc: Frees the resources for the specified texture container +//----------------------------------------------------------------------------- +HRESULT D3DTextr_DestroyTexture( TCHAR* strName ) +{ + TextureContainer* ptcTexture = FindTexture( strName ); + + SAFE_DELETE( ptcTexture ); + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DTextr_GetSurface() +// Desc: Returns a pointer to a d3dSurface from the name of the texture +//----------------------------------------------------------------------------- +LPDIRECTDRAWSURFACE7 D3DTextr_GetSurface( TCHAR* strName ) +{ + TextureContainer* ptcTexture = FindTexture( strName ); + + return ptcTexture ? ptcTexture->m_pddsSurface : NULL; +} + + + + + diff --git a/src/d3dtextr.h b/src/d3dtextr.h new file mode 100644 index 00000000..72d44301 --- /dev/null +++ b/src/d3dtextr.h @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// File: D3DTextr.h +// +// Desc: Functions to manage textures, including creating (loading from a +// file), restoring lost surfaces, invalidating, and destroying. +// +// Note: the implementation of these fucntions maintain an internal list +// of loaded textures. After creation, individual textures are referenced +// via their ASCII names. +// +// Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved +//----------------------------------------------------------------------------- +#ifndef D3DTEXTR_H +#define D3DTEXTR_H +#include +#include + + + + +//----------------------------------------------------------------------------- +// Access functions for loaded textures. Note: these functions search +// an internal list of the textures, and use the texture associated with the +// ASCII name. +//----------------------------------------------------------------------------- +LPDIRECTDRAWSURFACE7 D3DTextr_GetSurface( TCHAR* strName ); + + + + +//----------------------------------------------------------------------------- +// Texture invalidation and restoration functions +//----------------------------------------------------------------------------- +HRESULT D3DTextr_Invalidate( TCHAR* strName ); +HRESULT D3DTextr_Restore( TCHAR* strName, LPDIRECT3DDEVICE7 pd3dDevice ); +HRESULT D3DTextr_InvalidateAllTextures(); +HRESULT D3DTextr_RestoreAllTextures( LPDIRECT3DDEVICE7 pd3dDevice ); + + + + +//----------------------------------------------------------------------------- +// Texture creation and deletion functions +//----------------------------------------------------------------------------- +#define D3DTEXTR_TRANSPARENTWHITE 0x00000001 +#define D3DTEXTR_TRANSPARENTBLACK 0x00000002 +#define D3DTEXTR_32BITSPERPIXEL 0x00000004 +#define D3DTEXTR_16BITSPERPIXEL 0x00000008 +#define D3DTEXTR_CREATEWITHALPHA 0x00000010 + + +HRESULT D3DTextr_CreateTextureFromFile( TCHAR* strName, DWORD dwStage=0L, + DWORD dwFlags=0L ); +HRESULT D3DTextr_CreateEmptyTexture( TCHAR* strName, DWORD dwWidth, + DWORD dwHeight, DWORD dwStage, + DWORD dwFlags ); +HRESULT D3DTextr_DestroyTexture( TCHAR* strName ); +VOID D3DTextr_SetTexturePath( TCHAR* strTexturePath ); + +void D3DTextr_SetDebugMode(BOOL bDebug); + + + +#endif // D3DTEXTR_H diff --git a/src/d3dutil.cpp b/src/d3dutil.cpp new file mode 100644 index 00000000..8362f9f6 --- /dev/null +++ b/src/d3dutil.cpp @@ -0,0 +1,311 @@ +//----------------------------------------------------------------------------- +// File: D3DUtil.cpp +// +// Desc: Shortcut macros and functions for using DX objects +// +// +// Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved +//----------------------------------------------------------------------------- +#define D3D_OVERLOADS +#define STRICT +#include +#include +#include +#include "D3DUtil.h" + + + + +//----------------------------------------------------------------------------- +// Name: D3DUtil_GetDXSDKMediaPath() +// Desc: Returns the DirectX SDK media path, as stored in the system registry +// during the SDK install. +//----------------------------------------------------------------------------- +const TCHAR* D3DUtil_GetDXSDKMediaPath() +{ + static TCHAR strNull[2] = _T(""); + static TCHAR strPath[512]; + HKEY key; + DWORD type, size = 512; + + // Open the appropriate registry key + LONG result = RegOpenKeyEx( HKEY_LOCAL_MACHINE, + _T("Software\\Microsoft\\DirectX"), + 0, KEY_READ, &key ); + if( ERROR_SUCCESS != result ) + return strNull; + + result = RegQueryValueEx( key, _T("DXSDK Samples Path"), NULL, + &type, (BYTE*)strPath, &size ); + RegCloseKey( key ); + + if( ERROR_SUCCESS != result ) + return strNull; + + lstrcat( strPath, _T("\\D3DIM\\Media\\") ); + + return strPath; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DUtil_InitSurfaceDesc() +// Desc: Helper function called to build a DDSURFACEDESC2 structure, +// typically before calling CreateSurface() or GetSurfaceDesc() +//----------------------------------------------------------------------------- +VOID D3DUtil_InitSurfaceDesc( DDSURFACEDESC2& ddsd, DWORD dwFlags, + DWORD dwCaps ) +{ + ZeroMemory( &ddsd, sizeof(ddsd) ); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = dwFlags; + ddsd.ddsCaps.dwCaps = dwCaps; + ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DUtil_InitMaterial() +// Desc: Helper function called to build a D3DMATERIAL7 structure +//----------------------------------------------------------------------------- +VOID D3DUtil_InitMaterial( D3DMATERIAL7& mtrl, FLOAT r, FLOAT g, FLOAT b, + FLOAT a ) +{ + ZeroMemory( &mtrl, sizeof(D3DMATERIAL7) ); + mtrl.dcvDiffuse.r = mtrl.dcvAmbient.r = r; + mtrl.dcvDiffuse.g = mtrl.dcvAmbient.g = g; + mtrl.dcvDiffuse.b = mtrl.dcvAmbient.b = b; + mtrl.dcvDiffuse.a = mtrl.dcvAmbient.a = a; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DUtil_InitLight() +// Desc: Initializes a D3DLIGHT7 structure +//----------------------------------------------------------------------------- +VOID D3DUtil_InitLight( D3DLIGHT7& light, D3DLIGHTTYPE ltType, + FLOAT x, FLOAT y, FLOAT z ) +{ + ZeroMemory( &light, sizeof(D3DLIGHT7) ); + light.dltType = ltType; + light.dcvDiffuse.r = 1.0f; + light.dcvDiffuse.g = 1.0f; + light.dcvDiffuse.b = 1.0f; + light.dcvSpecular = light.dcvDiffuse; + light.dvPosition.x = light.dvDirection.x = x; + light.dvPosition.y = light.dvDirection.y = y; + light.dvPosition.z = light.dvDirection.z = z; + light.dvAttenuation0 = 1.0f; + light.dvRange = D3DLIGHT_RANGE_MAX; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DUtil_SetViewMatrix() +// Desc: Given an eye point, a lookat point, and an up vector, this +// function builds a 4x4 view matrix. +//----------------------------------------------------------------------------- +HRESULT D3DUtil_SetViewMatrix( D3DMATRIX& mat, D3DVECTOR& vFrom, + D3DVECTOR& vAt, D3DVECTOR& vWorldUp ) +{ + // Get the z basis vector, which points straight ahead. This is the + // difference from the eyepoint to the lookat point. + D3DVECTOR vView = vAt - vFrom; + + FLOAT fLength = Magnitude( vView ); + if( fLength < 1e-6f ) + return E_INVALIDARG; + + // Normalize the z basis vector + vView /= fLength; + + // Get the dot product, and calculate the projection of the z basis + // vector onto the up vector. The projection is the y basis vector. + FLOAT fDotProduct = DotProduct( vWorldUp, vView ); + + D3DVECTOR vUp = vWorldUp - fDotProduct * vView; + + // If this vector has near-zero length because the input specified a + // bogus up vector, let's try a default up vector + if( 1e-6f > ( fLength = Magnitude( vUp ) ) ) + { + vUp = D3DVECTOR( 0.0f, 1.0f, 0.0f ) - vView.y * vView; + + // If we still have near-zero length, resort to a different axis. + if( 1e-6f > ( fLength = Magnitude( vUp ) ) ) + { + vUp = D3DVECTOR( 0.0f, 0.0f, 1.0f ) - vView.z * vView; + + if( 1e-6f > ( fLength = Magnitude( vUp ) ) ) + return E_INVALIDARG; + } + } + + // Normalize the y basis vector + vUp /= fLength; + + // The x basis vector is found simply with the cross product of the y + // and z basis vectors + D3DVECTOR vRight = CrossProduct( vUp, vView ); + + // Start building the matrix. The first three rows contains the basis + // vectors used to rotate the view to point at the lookat point + D3DUtil_SetIdentityMatrix( mat ); + mat._11 = vRight.x; mat._12 = vUp.x; mat._13 = vView.x; + mat._21 = vRight.y; mat._22 = vUp.y; mat._23 = vView.y; + mat._31 = vRight.z; mat._32 = vUp.z; mat._33 = vView.z; + + // Do the translation values (rotations are still about the eyepoint) + mat._41 = - DotProduct( vFrom, vRight ); + mat._42 = - DotProduct( vFrom, vUp ); + mat._43 = - DotProduct( vFrom, vView ); + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DUtil_SetProjectionMatrix() +// Desc: Sets the passed in 4x4 matrix to a perpsective projection matrix built +// from the field-of-view (fov, in y), aspect ratio, near plane (D), +// and far plane (F). Note that the projection matrix is normalized for +// element [3][4] to be 1.0. This is performed so that W-based range fog +// will work correctly. +//----------------------------------------------------------------------------- +HRESULT D3DUtil_SetProjectionMatrix( D3DMATRIX& mat, FLOAT fFOV, FLOAT fAspect, + FLOAT fNearPlane, FLOAT fFarPlane ) +{ + if( fabs(fFarPlane-fNearPlane) < 0.01f ) + return E_INVALIDARG; + if( fabs(sin(fFOV/2)) < 0.01f ) + return E_INVALIDARG; + + FLOAT w = fAspect * ( cosf(fFOV/2)/sinf(fFOV/2) ); + FLOAT h = 1.0f * ( cosf(fFOV/2)/sinf(fFOV/2) ); + FLOAT Q = fFarPlane / ( fFarPlane - fNearPlane ); + + ZeroMemory( &mat, sizeof(D3DMATRIX) ); + mat._11 = w; + mat._22 = h; + mat._33 = Q; + mat._34 = 1.0f; + mat._43 = -Q*fNearPlane; + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DUtil_SetRotateXMatrix() +// Desc: Create Rotation matrix about X axis +//----------------------------------------------------------------------------- +VOID D3DUtil_SetRotateXMatrix( D3DMATRIX& mat, FLOAT fRads ) +{ + D3DUtil_SetIdentityMatrix( mat ); + mat._22 = cosf( fRads ); + mat._23 = sinf( fRads ); + mat._32 = -sinf( fRads ); + mat._33 = cosf( fRads ); +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DUtil_SetRotateYMatrix() +// Desc: Create Rotation matrix about Y axis +//----------------------------------------------------------------------------- +VOID D3DUtil_SetRotateYMatrix( D3DMATRIX& mat, FLOAT fRads ) +{ + D3DUtil_SetIdentityMatrix( mat ); + mat._11 = cosf( fRads ); + mat._13 = -sinf( fRads ); + mat._31 = sinf( fRads ); + mat._33 = cosf( fRads ); +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DUtil_SetRotateZMatrix() +// Desc: Create Rotation matrix about Z axis +//----------------------------------------------------------------------------- +VOID D3DUtil_SetRotateZMatrix( D3DMATRIX& mat, FLOAT fRads ) +{ + D3DUtil_SetIdentityMatrix( mat ); + mat._11 = cosf( fRads ); + mat._12 = sinf( fRads ); + mat._21 = -sinf( fRads ); + mat._22 = cosf( fRads ); +} + + + + +//----------------------------------------------------------------------------- +// Name: D3DUtil_SetRotationMatrix +// Desc: Create a Rotation matrix about vector direction +//----------------------------------------------------------------------------- +VOID D3DUtil_SetRotationMatrix( D3DMATRIX& mat, D3DVECTOR& vDir, FLOAT fRads ) +{ + FLOAT fCos = cosf( fRads ); + FLOAT fSin = sinf( fRads ); + D3DVECTOR v = Normalize( vDir ); + + mat._11 = ( v.x * v.x ) * ( 1.0f - fCos ) + fCos; + mat._12 = ( v.x * v.y ) * ( 1.0f - fCos ) - (v.z * fSin); + mat._13 = ( v.x * v.z ) * ( 1.0f - fCos ) + (v.y * fSin); + + mat._21 = ( v.y * v.x ) * ( 1.0f - fCos ) + (v.z * fSin); + mat._22 = ( v.y * v.y ) * ( 1.0f - fCos ) + fCos ; + mat._23 = ( v.y * v.z ) * ( 1.0f - fCos ) - (v.x * fSin); + + mat._31 = ( v.z * v.x ) * ( 1.0f - fCos ) - (v.y * fSin); + mat._32 = ( v.z * v.y ) * ( 1.0f - fCos ) + (v.x * fSin); + mat._33 = ( v.z * v.z ) * ( 1.0f - fCos ) + fCos; + + mat._14 = mat._24 = mat._34 = 0.0f; + mat._41 = mat._42 = mat._43 = 0.0f; + mat._44 = 1.0f; +} + + + + +//----------------------------------------------------------------------------- +// Name: _DbgOut() +// Desc: Outputs a message to the debug stream +//----------------------------------------------------------------------------- +HRESULT _DbgOut( TCHAR* strFile, DWORD dwLine, HRESULT hr, TCHAR* strMsg ) +{ + TCHAR buffer[256]; + wsprintf( buffer, _T("%s(%ld): "), strFile, dwLine ); + OutputDebugString( buffer ); + OutputDebugString( strMsg ); + + if( hr ) + { + wsprintf( buffer, _T("(hr=%08lx)\n"), hr ); + OutputDebugString( buffer ); + } + + OutputDebugString( _T("\n") ); + + return hr; +} + + + diff --git a/src/d3dutil.h b/src/d3dutil.h new file mode 100644 index 00000000..476f142c --- /dev/null +++ b/src/d3dutil.h @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------------- +// File: D3DUtil.h +// +// Desc: Helper functions and typing shortcuts for Direct3D programming. +// +// Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved +//----------------------------------------------------------------------------- +#ifndef D3DUTIL_H +#define D3DUTIL_H +#include +#include + + + + +//----------------------------------------------------------------------------- +// Miscellaneous helper functions +//----------------------------------------------------------------------------- +const TCHAR* D3DUtil_GetDXSDKMediaPath(); + +#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } } +#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } + + + + +//----------------------------------------------------------------------------- +// Short cut functions for creating and using DX structures +//----------------------------------------------------------------------------- +VOID D3DUtil_InitDeviceDesc( D3DDEVICEDESC7& ddDevDesc ); +VOID D3DUtil_InitSurfaceDesc( DDSURFACEDESC2& ddsd, DWORD dwFlags=0, + DWORD dwCaps=0 ); +VOID D3DUtil_InitMaterial( D3DMATERIAL7& mtrl, FLOAT r=0.0f, FLOAT g=0.0f, + FLOAT b=0.0f, FLOAT a=1.0f ); +VOID D3DUtil_InitLight( D3DLIGHT7& light, D3DLIGHTTYPE ltType, + FLOAT x=0.0f, FLOAT y=0.0f, FLOAT z=0.0f ); + + + + +//----------------------------------------------------------------------------- +// D3D Matrix functions. For performance reasons, some functions are inline. +//----------------------------------------------------------------------------- +HRESULT D3DUtil_SetViewMatrix( D3DMATRIX& mat, D3DVECTOR& vFrom, + D3DVECTOR& vAt, D3DVECTOR& vUp ); +HRESULT D3DUtil_SetProjectionMatrix( D3DMATRIX& mat, FLOAT fFOV = 1.570795f, + FLOAT fAspect = 1.0f, + FLOAT fNearPlane = 1.0f, + FLOAT fFarPlane = 1000.0f ); + +inline VOID D3DUtil_SetIdentityMatrix( D3DMATRIX& m ) +{ + m._12 = m._13 = m._14 = m._21 = m._23 = m._24 = 0.0f; + m._31 = m._32 = m._34 = m._41 = m._42 = m._43 = 0.0f; + m._11 = m._22 = m._33 = m._44 = 1.0f; +} + +inline VOID D3DUtil_SetTranslateMatrix( D3DMATRIX& m, FLOAT tx, FLOAT ty, + FLOAT tz ) +{ D3DUtil_SetIdentityMatrix( m ); m._41 = tx; m._42 = ty; m._43 = tz; } + +inline VOID D3DUtil_SetTranslateMatrix( D3DMATRIX& m, D3DVECTOR& v ) +{ D3DUtil_SetTranslateMatrix( m, v.x, v.y, v.z ); } + +inline VOID D3DUtil_SetScaleMatrix( D3DMATRIX& m, FLOAT sx, FLOAT sy, + FLOAT sz ) +{ D3DUtil_SetIdentityMatrix( m ); m._11 = sx; m._22 = sy; m._33 = sz; } + +inline VOID SetScaleMatrix( D3DMATRIX& m, D3DVECTOR& v ) +{ D3DUtil_SetScaleMatrix( m, v.x, v.y, v.z ); } + +VOID D3DUtil_SetRotateXMatrix( D3DMATRIX& mat, FLOAT fRads ); +VOID D3DUtil_SetRotateYMatrix( D3DMATRIX& mat, FLOAT fRads ); +VOID D3DUtil_SetRotateZMatrix( D3DMATRIX& mat, FLOAT fRads ); +VOID D3DUtil_SetRotationMatrix( D3DMATRIX& mat, D3DVECTOR& vDir, + FLOAT fRads ); + + + + +//----------------------------------------------------------------------------- +// Debug printing support +//----------------------------------------------------------------------------- + +HRESULT _DbgOut( TCHAR*, DWORD, HRESULT, TCHAR* ); + +#if defined(DEBUG) | defined(_DEBUG) + #define DEBUG_MSG(str) _DbgOut( __FILE__, (DWORD)__LINE__, 0, str ) + #define DEBUG_ERR(hr,str) _DbgOut( __FILE__, (DWORD)__LINE__, hr, str ) +#else + #define DEBUG_MSG(str) (0L) + #define DEBUG_ERR(hr,str) (hr) +#endif + + + + +#endif // D3DUTIL_H diff --git a/src/dd.cpp b/src/dd.cpp new file mode 100644 index 00000000..6e3dc101 --- /dev/null +++ b/src/dd.cpp @@ -0,0 +1,159 @@ +// Compilation d'une procédure avec un "point". + +int cPoint(CBotVar* &var, CBotString& retClass, void* user) +{ + if ( var == 0 ) return CBotErrLowParam; + + if ( var->GivType() <= CBotTypDouble ) + { + var = var->GivNext(); + if ( var == 0 ) return CBotErrLowParam; + if ( var->GivType() > CBotTypDouble ) return CBotErrBadNum; + var = var->GivNext(); + if ( var == 0 ) return CBotErrLowParam; + if ( var->GivType() > CBotTypDouble ) return CBotErrBadNum; + var = var->GivNext(); + return 0; + } + + if ( var->GivType() == CBotTypClass ) + { + if ( !var->IsElemOfClass("point") ) return CBotErrBadParam; + var = var->GivNext(); + return 0; + } + + return CBotErrBadParam; +} + +// Donne un paramètre de type "point". + +BOOL GetPoint(CBotVar* &var, int& exception, D3DVECTOR& pos) +{ + CBotVar *pX, *pY, *pZ; + + if ( var->GivType() <= CBotTypDouble ) + { + pos.x = var->GivValFloat()*UNIT; + var = var->GivNext(); + + pos.z = var->GivValFloat()*UNIT; + var = var->GivNext(); + + pos.y = var->GivValFloat()*UNIT; + var = var->GivNext(); + } + else + { + pX = var->GivItem("x"); + if ( pX == NULL ) + { + exception = CBotErrUndefItem; return TRUE; + } + pos.x = pX->GivValFloat()*UNIT; + + pY = var->GivItem("y"); + if ( pY == NULL ) + { + exception = CBotErrUndefItem; return TRUE; + } + pos.z = pY->GivValFloat()*UNIT; // attention y -> z ! + + pZ = var->GivItem("z"); + if ( pZ == NULL ) + { + exception = CBotErrUndefItem; return TRUE; + } + pos.y = pZ->GivValFloat()*UNIT; // attention z -> y ! + + var = var->GivNext(); + } + return TRUE; +} + + + +// Compilation de l'instruction "space(center, rMin, rMax, dist)". + +int cSpace(CBotVar* &var, CBotString& retClass, void* user) +{ + int ret; + + retClass = "point"; + + if ( var == 0 ) return CBotTypIntrinsic; + ret = cPoint(var, retClass, user); + if ( ret != 0 ) return ret; + + if ( var == 0 ) return CBotTypIntrinsic; + if ( var->GivType() > CBotTypDouble ) return CBotErrBadNum; + var = var->GivNext(); + + if ( var == 0 ) return CBotTypIntrinsic; + if ( var->GivType() > CBotTypDouble ) return CBotErrBadNum; + var = var->GivNext(); + + if ( var == 0 ) return CBotTypIntrinsic; + if ( var->GivType() > CBotTypDouble ) return CBotErrBadNum; + var = var->GivNext(); + + if ( var != 0 ) return CBotErrOverParam; + return CBotTypIntrinsic; +} + +// Instruction "space(center, rMin, rMax, dist)". + +BOOL rSpace(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + CBotVar* pSub; + D3DVECTOR center; + float rMin, rMax, dist; + + rMin = 5.0f*UNIT; + rMax = 50.0f*UNIT; + dist = 4.0f*UNIT; + + if ( var == 0 ) + { + center = pThis->RetPosition(0); + } + else + { + if ( !GetPoint(var, exception, center) ) return TRUE; + + if ( var != 0 ) + { + rMin = var->GivValFloat()*UNIT; + var = var->GivNext(); + + if ( var != 0 ) + { + rMax = var->GivValFloat()*UNIT; + var = var->GivNext(); + + if ( var != 0 ) + { + dist = var->GivValFloat()*UNIT; + var = var->GivNext(); + } + } + } + } + script->m_main->FreeSpace(center, rMin, rMax, dist, pThis); + + if ( result != 0 ) + { + pSub = result->GivItemList(); + if ( pSub != 0 ) + { + pSub->SetValFloat(center.x/UNIT); + pSub = pSub->GivNext(); // "y" + pSub->SetValFloat(center.z/UNIT); + pSub = pSub->GivNext(); // "z" + pSub->SetValFloat(center.y/UNIT); + } + } + return TRUE; +} diff --git a/src/displayinfo.cpp b/src/displayinfo.cpp new file mode 100644 index 00000000..3adc5b8f --- /dev/null +++ b/src/displayinfo.cpp @@ -0,0 +1,1206 @@ +// displayinfo.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "language.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "math3d.h" +#include "robotmain.h" +#include "camera.h" +#include "object.h" +#include "motion.h" +#include "motiontoto.h" +#include "interface.h" +#include "button.h" +#include "slider.h" +#include "edit.h" +#include "group.h" +#include "window.h" +#include "particule.h" +#include "light.h" +#include "text.h" +#include "cbottoken.h" +#include "displayinfo.h" + + + + +// Constructeur de l'objet. + +CDisplayInfo::CDisplayInfo(CInstanceManager* iMan) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_STUDIO, this); + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_event = (CEvent*)m_iMan->SearchInstance(CLASS_EVENT); + m_interface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + m_light = (CLight*)m_iMan->SearchInstance(CLASS_LIGHT); + + m_bInfoMaximized = TRUE; + m_bInfoMinimized = FALSE; + + m_infoFinalPos = m_infoActualPos = m_infoNormalPos = FPOINT(0.00f, 0.00f); + m_infoFinalDim = m_infoActualPos = m_infoNormalDim = FPOINT(1.00f, 1.00f); + + m_lightSuppl = -1; + m_toto = 0; +} + +// Destructeur de l'objet. + +CDisplayInfo::~CDisplayInfo() +{ + m_iMan->DeleteInstance(CLASS_STUDIO, this); +} + + +// Gestion d'un événement. + +BOOL CDisplayInfo::EventProcess(const Event &event) +{ + CWindow* pw; + CEdit* edit; + CSlider* slider; + CMotionToto* toto; + + if ( event.event == EVENT_FRAME ) + { + EventFrame(event); + HyperUpdate(); + } + + if ( event.event == EVENT_MOUSEMOVE ) + { + if ( m_toto != 0 ) + { + toto = (CMotionToto*)m_toto->RetMotion(); + if ( toto != 0 ) + { + toto->SetMousePos(event.pos); + } + } + } + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw != 0 ) + { + if ( event.event == pw->RetEventMsgClose() ) + { + Event newEvent = event; + newEvent.event = EVENT_OBJECT_INFOOK; + m_event->AddEvent(newEvent); + } + + if ( event.event == EVENT_SATCOM_HUSTON ) + { + ChangeIndexButton(SATCOM_HUSTON); + } + if ( event.event == EVENT_SATCOM_SAT ) + { + ChangeIndexButton(SATCOM_SAT); + } +//? if ( event.event == EVENT_SATCOM_OBJECT ) +//? { +//? ChangeIndexButton(SATCOM_OBJECT); +//? } + if ( event.event == EVENT_SATCOM_LOADING ) + { + ChangeIndexButton(SATCOM_LOADING); + } + if ( event.event == EVENT_SATCOM_PROG ) + { + ChangeIndexButton(SATCOM_PROG); + } + if ( event.event == EVENT_SATCOM_SOLUCE ) + { + ChangeIndexButton(SATCOM_SOLUCE); + } + + if ( event.event == EVENT_HYPER_HOME || + event.event == EVENT_HYPER_PREV || + event.event == EVENT_HYPER_NEXT ) + { + edit = (CEdit*)pw->SearchControl(EVENT_EDIT1); + if ( edit != 0 ) + { + edit->HyperGo(event.event); + HyperUpdate(); + } + } + + if ( event.event == EVENT_HYPER_SIZE1 ) // taille 1 ? + { + m_main->SetFontSize(9.0f); + slider = (CSlider*)pw->SearchControl(EVENT_STUDIO_SIZE); + if ( slider != 0 ) slider->SetVisibleValue((m_main->RetFontSize()-9.0f)/6.0f); + ViewDisplayInfo(); + } + if ( event.event == EVENT_HYPER_SIZE2 ) // taille 2 ? + { + m_main->SetFontSize(10.0f); + slider = (CSlider*)pw->SearchControl(EVENT_STUDIO_SIZE); + if ( slider != 0 ) slider->SetVisibleValue((m_main->RetFontSize()-9.0f)/6.0f); + ViewDisplayInfo(); + } + if ( event.event == EVENT_HYPER_SIZE3 ) // taille 3 ? + { + m_main->SetFontSize(12.0f); + slider = (CSlider*)pw->SearchControl(EVENT_STUDIO_SIZE); + if ( slider != 0 ) slider->SetVisibleValue((m_main->RetFontSize()-9.0f)/6.0f); + ViewDisplayInfo(); + } + if ( event.event == EVENT_HYPER_SIZE4 ) // taille 4 ? + { + m_main->SetFontSize(15.0f); + slider = (CSlider*)pw->SearchControl(EVENT_STUDIO_SIZE); + if ( slider != 0 ) slider->SetVisibleValue((m_main->RetFontSize()-9.0f)/6.0f); + ViewDisplayInfo(); + } + + if ( event.event == EVENT_STUDIO_SIZE ) // taille ? + { + slider = (CSlider*)pw->SearchControl(EVENT_STUDIO_SIZE); + if ( slider == 0 ) return FALSE; + m_main->SetFontSize(9.0f+slider->RetVisibleValue()*6.0f); + ViewDisplayInfo(); + } + + if ( event.event == EVENT_HYPER_COPY ) // copie ? + { + edit = (CEdit*)pw->SearchControl(EVENT_EDIT1); + if ( edit != 0 ) + { + edit->Copy(); + } + } + + if ( event.event == EVENT_LBUTTONDOWN || + event.event == EVENT_LBUTTONUP ) + { + UpdateCopyButton(); + } + + if ( event.event == EVENT_WINDOW4 ) // fenêtre déplacée ? + { + m_infoNormalPos = m_infoActualPos = m_infoFinalPos = pw->RetPos(); + m_infoNormalDim = m_infoActualDim = m_infoFinalDim = pw->RetDim(); + AdjustDisplayInfo(m_infoActualPos, m_infoActualDim); + } + if ( event.event == pw->RetEventMsgReduce() ) + { + if ( m_bInfoMinimized ) + { + m_infoFinalPos = m_infoNormalPos; + m_infoFinalDim = m_infoNormalDim; + m_bInfoMinimized = FALSE; + m_bInfoMaximized = FALSE; + } + else + { + m_infoFinalPos.x = 0.00f; + m_infoFinalPos.y = -0.34f; + m_infoFinalDim.x = 1.00f; + m_infoFinalDim.y = 0.40f; + m_bInfoMinimized = TRUE; + m_bInfoMaximized = FALSE; + } +//? m_main->SetEditFull(m_bInfoMaximized); + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw != 0 ) + { + pw->SetMaximized(m_bInfoMaximized); + pw->SetMinimized(m_bInfoMinimized); + } + } + if ( event.event == pw->RetEventMsgFull() ) + { + if ( m_bInfoMaximized ) + { + m_infoFinalPos = m_infoNormalPos; + m_infoFinalDim = m_infoNormalDim; + m_bInfoMinimized = FALSE; + m_bInfoMaximized = FALSE; + } + else + { + m_infoFinalPos.x = 0.00f; + m_infoFinalPos.y = 0.00f; + m_infoFinalDim.x = 1.00f; + m_infoFinalDim.y = 1.00f; + m_bInfoMinimized = FALSE; + m_bInfoMaximized = TRUE; + } +//? m_main->SetEditFull(m_bInfoMaximized); + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw != 0 ) + { + pw->SetMaximized(m_bInfoMaximized); + pw->SetMinimized(m_bInfoMinimized); + } + } + } + return TRUE; +} + + +// Fait évoluer le cerveau selon le temps écoulé. + +BOOL CDisplayInfo::EventFrame(const Event &event) +{ + float time; + + if ( m_infoFinalPos.x != m_infoActualPos.x || + m_infoFinalPos.y != m_infoActualPos.y || + m_infoFinalDim.x != m_infoActualDim.x || + m_infoFinalDim.y != m_infoActualDim.y ) + { + time = event.rTime*20.0f; + m_infoActualPos.x += (m_infoFinalPos.x-m_infoActualPos.x)*time; + m_infoActualPos.y += (m_infoFinalPos.y-m_infoActualPos.y)*time; + m_infoActualDim.x += (m_infoFinalDim.x-m_infoActualDim.x)*time; + m_infoActualDim.y += (m_infoFinalDim.y-m_infoActualDim.y)*time; + AdjustDisplayInfo(m_infoActualPos, m_infoActualDim); + } + + return TRUE; +} + + +// Met à jour les boutons pour les liens hyper-texte. + +void CDisplayInfo::HyperUpdate() +{ + CWindow* pw; + CEdit* edit; + CButton* button; + BOOL bEnable; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw == 0 ) return; + edit = (CEdit*)pw->SearchControl(EVENT_EDIT1); + if ( edit == 0 ) return; + + button = (CButton*)pw->SearchControl(EVENT_HYPER_HOME); + if ( button != 0 ) + { + bEnable = edit->HyperTest(EVENT_HYPER_HOME); + button->SetState(STATE_ENABLE, bEnable); + } + + button = (CButton*)pw->SearchControl(EVENT_HYPER_PREV); + if ( button != 0 ) + { + bEnable = edit->HyperTest(EVENT_HYPER_PREV); + button->SetState(STATE_ENABLE, bEnable); + } + + button = (CButton*)pw->SearchControl(EVENT_HYPER_NEXT); + if ( button != 0 ) + { + bEnable = edit->HyperTest(EVENT_HYPER_NEXT); + button->SetState(STATE_ENABLE, bEnable); + } +} + + +// Début de l'affichage des informations. + +void CDisplayInfo::StartDisplayInfo(char *filename, int index, BOOL bSoluce) +{ + D3DLIGHT7 light; + FPOINT pos, dim; + CWindow* pw; + CEdit* edit; + CButton* button; + CSlider* slider; + CMotionToto* toto; + + m_index = index; + m_bSoluce = bSoluce; + +//? CreateObjectsFile(); + + m_bEditLock = m_main->RetEditLock(); + if ( m_bEditLock ) // édition programme en cours ? + { + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw != 0 ) + { + pw->ClearState(STATE_ENABLE); // CStudio inactif + } + } + + m_main->SetEditLock(TRUE, FALSE); + m_main->SetEditFull(FALSE); + m_bInitPause = m_engine->RetPause(); + m_engine->SetPause(TRUE); + m_infoCamera = m_camera->RetType(); + m_camera->SetType(CAMERA_INFO); + + pos = m_infoActualPos = m_infoFinalPos; + dim = m_infoActualDim = m_infoFinalDim; + pw = m_interface->CreateWindows(pos, dim, 4, EVENT_WINDOW4); + if ( pw == 0 ) return; +//? pw->SetClosable(TRUE); +//? GetResource(RES_TEXT, RT_DISINFO_TITLE, res); +//? pw->SetName(res); +//? pw->SetMinDim(FPOINT(0.56f, 0.40f)); +//? pw->SetMaximized(m_bInfoMaximized); +//? pw->SetMinimized(m_bInfoMinimized); +//? m_main->SetEditFull(m_bInfoMaximized); + + edit = pw->CreateEdit(pos, dim, 0, EVENT_EDIT1); + if ( edit == 0 ) return; + edit->SetState(STATE_SHADOW); + edit->SetMultiFont(TRUE); + edit->SetMaxChar(10000); + edit->SetFontType(FONT_COLOBOT); + edit->SetSoluceMode(bSoluce); + edit->ReadText(filename); + edit->HyperHome(filename); + edit->SetEditCap(FALSE); // juste pour voir ! + edit->SetHiliteCap(FALSE); + edit->SetFocus(TRUE); + + ViewDisplayInfo(); + + button = pw->CreateButton(pos, dim, 128+57, EVENT_SATCOM_HUSTON); + button->SetState(STATE_SHADOW); +#if _TEEN +#if !_ENGLISH + button = pw->CreateButton(pos, dim, 46, EVENT_SATCOM_SAT); +#endif +#else + button = pw->CreateButton(pos, dim, 128+58, EVENT_SATCOM_SAT); +#endif + button->SetState(STATE_SHADOW); +//? button = pw->CreateButton(pos, dim, 128+59, EVENT_SATCOM_OBJECT); +//? button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 53, EVENT_SATCOM_LOADING); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 128+60, EVENT_SATCOM_PROG); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 20, EVENT_SATCOM_SOLUCE); + button->SetState(STATE_SHADOW); + + pw->CreateGroup(pos, dim, 18, EVENT_LABEL1); // flèche > + pw->CreateGroup(pos, dim, 19, EVENT_LABEL2); // sigle SatCom + + button = pw->CreateButton(pos, dim, 55, EVENT_HYPER_PREV); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 48, EVENT_HYPER_NEXT); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 30, EVENT_HYPER_HOME); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 82, EVENT_HYPER_SIZE1); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 83, EVENT_HYPER_SIZE2); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 90, EVENT_HYPER_SIZE3); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 91, EVENT_HYPER_SIZE4); + button->SetState(STATE_SHADOW); + slider = pw->CreateSlider(pos, dim, 0, EVENT_STUDIO_SIZE); + slider->SetState(STATE_SHADOW); + slider->SetVisibleValue((m_main->RetFontSize()-9.0f)/6.0f); + button = pw->CreateButton(pos, dim, 61, EVENT_HYPER_COPY); + button->SetState(STATE_SHADOW); + HyperUpdate(); + + button = pw->CreateButton(pos, dim, -1, EVENT_OBJECT_INFOOK); + button->SetState(STATE_SHADOW); + button->SetState(STATE_SIMPLY); + button->SetState(STATE_DEFAULT); + pw->CreateGroup(pos, dim, 21, EVENT_LABEL3); // sigle stand-by + + AdjustDisplayInfo(m_infoActualPos, m_infoActualDim); + UpdateIndexButton(); + + m_engine->SetDrawWorld(FALSE); // ne dessine rien sous l'interface + m_engine->SetDrawFront(TRUE); // dessine toto sur l'interface + m_particule->SetFrameUpdate(SH_WORLD, FALSE); // pause pour particules dans monde + + m_toto = SearchToto(); + if ( m_toto != 0 ) + { + m_toto->SetDrawFront(TRUE); + + toto = (CMotionToto*)m_toto->RetMotion(); + if ( toto != 0 ) + { + toto->StartDisplayInfo(); + } + } + + ZeroMemory(&light, sizeof(light)); + light.dltType = D3DLIGHT_DIRECTIONAL; + light.dcvDiffuse.r = 1.0f; + light.dcvDiffuse.g = 1.0f; + light.dcvDiffuse.b = 1.0f; + light.dvDirection = D3DVECTOR(1.0f, 0.0f, 1.0f); + m_lightSuppl = m_light->CreateLight(); + m_light->SetLight(m_lightSuppl, light); + m_light->SetLightExcluType(m_lightSuppl, TYPETERRAIN); +} + +// Repositionne tous les contrôles d'édition. + +void CDisplayInfo::AdjustDisplayInfo(FPOINT wpos, FPOINT wdim) +{ + CWindow* pw; + CEdit* edit; + CButton* button; + CSlider* slider; + CGroup* group; + FPOINT pos, dim; + + wpos.x = 50.0f/640.0f; + wpos.y = 30.0f/480.0f; + wdim.x = 540.0f/640.0f; + wdim.y = 420.0f/480.0f; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw != 0 ) + { + pw->SetPos(wpos); + pw->SetDim(wdim); + wdim = pw->RetDim(); + } + + pos.x = (50.0f+10.0f)/640.0f; + pos.y = (30.0f+10.0f+24.0f+10.0f+324.0f-48.0f)/480.0f; + dim.x = 48.0f/640.0f; + dim.y = 48.0f/480.0f; + button = (CButton*)pw->SearchControl(EVENT_SATCOM_HUSTON); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.y -= (48.0f+4.0f)/480.0f; + button = (CButton*)pw->SearchControl(EVENT_SATCOM_SAT); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } +//? pos.y -= (48.0f+4.0f)/480.0f; +//? button = (CButton*)pw->SearchControl(EVENT_SATCOM_OBJECT); +//? if ( button != 0 ) +//? { +//? button->SetPos(pos); +//? button->SetDim(dim); +//? } + pos.y -= (48.0f+4.0f)/480.0f; + button = (CButton*)pw->SearchControl(EVENT_SATCOM_LOADING); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.y -= (48.0f+4.0f)/480.0f; + button = (CButton*)pw->SearchControl(EVENT_SATCOM_PROG); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.y -= (48.0f+4.0f)/480.0f; + button = (CButton*)pw->SearchControl(EVENT_SATCOM_SOLUCE); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + + pos.x = (50.0f+10.0f+5.0f)/640.0f; + pos.y = (30.0f+10.0f+4.0f)/480.0f; + dim.x = (48.0f-10.0f)/640.0f; + dim.y = 24.0f/480.0f; + button = (CButton*)pw->SearchControl(EVENT_OBJECT_INFOOK); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + + pos.x = (50.0f+10.0f+48.0f+10.0f)/640.0f; + pos.y = (30.0f+10.0f)/480.0f; + dim.x = 462.0f/640.0f; + dim.y = 358.0f/480.0f; + edit = (CEdit*)pw->SearchControl(EVENT_EDIT1); + if ( edit != 0 ) + { + edit->SetPos(pos); + edit->SetDim(dim); + } + + pos.x = (50.0f+10.0f+48.0f+10.0f)/640.0f; + pos.y = (30.0f+10.0f+358.0f+10.0f)/480.0f; + dim.x = 32.0f/640.0f; + dim.y = 32.0f/480.0f; + button = (CButton*)pw->SearchControl(EVENT_HYPER_PREV); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x += 35.0f/640.0f; + button = (CButton*)pw->SearchControl(EVENT_HYPER_NEXT); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x += 35.0f/640.0f; + button = (CButton*)pw->SearchControl(EVENT_HYPER_HOME); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + + pos.x += 50.0f/640.0f; + button = (CButton*)pw->SearchControl(EVENT_HYPER_SIZE1); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x += 35.0f/640.0f; + button = (CButton*)pw->SearchControl(EVENT_HYPER_SIZE2); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x += 35.0f/640.0f; + button = (CButton*)pw->SearchControl(EVENT_HYPER_SIZE3); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x += 35.0f/640.0f; + button = (CButton*)pw->SearchControl(EVENT_HYPER_SIZE4); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x += 35.0f/640.0f; + dim.x = 18.0f/640.0f; + slider = (CSlider*)pw->SearchControl(EVENT_STUDIO_SIZE); + if ( slider != 0 ) + { + slider->SetPos(pos); + slider->SetDim(dim); + } + pos.x += 50.0f/640.0f; + dim.x = 32.0f/640.0f; + button = (CButton*)pw->SearchControl(EVENT_HYPER_COPY); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + + pos.x = (50.0f+10.0f)/640.0f; + pos.y = (30.0f+10.0f+24.0f+10.0f+324.0f+6.0f)/480.0f; + dim.x = 48.0f/640.0f; + dim.y = 40.0f/480.0f; + group = (CGroup*)pw->SearchControl(EVENT_LABEL2); // sigle SatCom + if ( group != 0 ) + { + group->SetPos(pos); + group->SetDim(dim); + } + + pos.x = (50.0f+10.0f+14.0f)/640.0f; + pos.y = (30.0f+10.0f+6.0f)/480.0f; + dim.x = 20.0f/640.0f; + dim.y = 20.0f/480.0f; + group = (CGroup*)pw->SearchControl(EVENT_LABEL3); // sigle stand-by + if ( group != 0 ) + { + group->SetPos(pos); + group->SetDim(dim); + } +} + +// Change le bouton d'index. + +void CDisplayInfo::ChangeIndexButton(int index) +{ + CWindow* pw; + CEdit* edit; + char* filename; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw == 0 ) return; + + if ( m_index != -1 ) + { + m_main->SetDisplayInfoPosition(m_index, RetPosition()); + } + m_index = index; + + edit = (CEdit*)pw->SearchControl(EVENT_EDIT1); + if ( edit != 0 ) + { + filename = m_main->RetDisplayInfoName(m_index); + edit->ReadText(filename); + edit->HyperHome(filename); + SetPosition(m_main->RetDisplayInfoPosition(m_index)); + } + + UpdateIndexButton(); +} + +// Adapte les boutons d'index. + +void CDisplayInfo::UpdateIndexButton() +{ + CWindow* pw; + CButton* button; + CGroup* group; + CEdit* edit; + FPOINT pos, dim; + char* filename; + char* loading; + + static int table[SATCOM_MAX] = + { + 0, // SATCOM_HUSTON + 1, // SATCOM_SAT + -1, // SATCOM_OBJECT + 2, // SATCOM_LOADING + 3, // SATCOM_PROG + 4, // SATCOM_SOLUCE + }; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw == 0 ) return; + + button = (CButton*)pw->SearchControl(EVENT_SATCOM_HUSTON); + if ( button != 0 ) + { + button->SetState(STATE_CHECK, m_index==SATCOM_HUSTON); + filename = m_main->RetDisplayInfoName(SATCOM_HUSTON); + button->SetState(STATE_VISIBLE, filename[0]!=0); + } + + button = (CButton*)pw->SearchControl(EVENT_SATCOM_SAT); + if ( button != 0 ) + { + button->SetState(STATE_CHECK, m_index==SATCOM_SAT); + filename = m_main->RetDisplayInfoName(SATCOM_SAT); + button->SetState(STATE_VISIBLE, filename[0]!=0); + } + +//? button = (CButton*)pw->SearchControl(EVENT_SATCOM_OBJECT); +//? if ( button != 0 ) +//? { +//? button->SetState(STATE_CHECK, m_index==SATCOM_OBJECT); +//? filename = m_main->RetDisplayInfoName(SATCOM_OBJECT); +//? button->SetState(STATE_VISIBLE, filename[0]!=0); +//? } + + loading = 0; + button = (CButton*)pw->SearchControl(EVENT_SATCOM_LOADING); + if ( button != 0 ) + { + button->SetState(STATE_CHECK, m_index==SATCOM_LOADING); + loading = m_main->RetDisplayInfoName(SATCOM_LOADING); + button->SetState(STATE_VISIBLE, loading[0]!=0); + } + + button = (CButton*)pw->SearchControl(EVENT_SATCOM_PROG); + if ( button != 0 ) + { + button->SetState(STATE_CHECK, m_index==SATCOM_PROG); + filename = m_main->RetDisplayInfoName(SATCOM_PROG); + button->SetState(STATE_VISIBLE, filename[0]!=0 && (m_index==SATCOM_LOADING||m_index==SATCOM_PROG||(loading!=0&&loading[0]==0))); + } + + button = (CButton*)pw->SearchControl(EVENT_SATCOM_SOLUCE); + if ( button != 0 ) + { + button->SetState(STATE_CHECK, m_index==SATCOM_SOLUCE); + filename = m_main->RetDisplayInfoName(SATCOM_SOLUCE); + button->SetState(STATE_VISIBLE, filename[0]!=0 && m_bSoluce); + } + + group = (CGroup*)pw->SearchControl(EVENT_LABEL1); + if ( group != 0 ) + { + if ( m_index == -1 ) + { + group->ClearState(STATE_VISIBLE); + } + else + { + group->SetState(STATE_VISIBLE); + + pos.x = (50.0f+10.0f+48.0f-3.0f)/640.0f; + pos.y = (30.0f+10.0f+24.0f+10.0f+324.0f-48.0f-1.0f)/480.0f; + pos.y -= (48.0f+4.0f)/480.0f*table[m_index]; + dim.x = 15.0f/640.0f; + dim.y = 48.0f/480.0f; + group->SetPos(pos); + group->SetDim(dim); + } + } + +#if 0 + button = (CButton*)pw->SearchControl(EVENT_HYPER_COPY); + if ( button != 0 ) + { + button->SetState(STATE_VISIBLE, m_index==SATCOM_LOADING); + } +#endif + + edit = (CEdit*)pw->SearchControl(EVENT_EDIT1); + if ( edit != 0 ) + { +//? edit->SetHiliteCap(m_index==SATCOM_LOADING); + edit->SetHiliteCap(TRUE); + } + + UpdateCopyButton(); +} + +// Adapte le bouton de copie. + +void CDisplayInfo::UpdateCopyButton() +{ + CWindow* pw; + CButton* button; + CEdit* edit; + int c1, c2; + +//? if ( m_index != SATCOM_LOADING ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw == 0 ) return; + + button = (CButton*)pw->SearchControl(EVENT_HYPER_COPY); + if ( button == 0 ) return; + + edit = (CEdit*)pw->SearchControl(EVENT_EDIT1); + if ( edit == 0 ) return; + + edit->GetCursor(c1, c2); + button->SetState(STATE_ENABLE, c1!=c2); + +} + +// Fin de l'affichage des informations. + +void CDisplayInfo::StopDisplayInfo() +{ + CWindow* pw; + CMotionToto* toto; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw == 0 ) return; + + m_interface->DeleteControl(EVENT_WINDOW4); + + if ( m_bEditLock ) // édition programme en cours ? + { + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw != 0 ) + { + pw->SetState(STATE_ENABLE); // CStudio opérationnel + } + } + else + { + if ( !m_bInitPause ) m_engine->SetPause(FALSE); + m_main->SetEditLock(FALSE, FALSE); + } + m_camera->SetType(m_infoCamera); + + m_engine->SetDrawWorld(TRUE); // dessine tout sous l'interface + m_engine->SetDrawFront(FALSE); // ne dessine rien sur l'interface + m_particule->SetFrameUpdate(SH_WORLD, TRUE); + m_particule->FlushParticule(SH_FRONT); + m_particule->FlushParticule(SH_INTERFACE); + + if ( m_toto != 0 ) + { + toto = (CMotionToto*)m_toto->RetMotion(); + if ( toto != 0 ) + { + toto->StopDisplayInfo(); + } + } + + m_light->DeleteLight(m_lightSuppl); + m_lightSuppl = -1; +} + + +// Spécifie la position. + +void CDisplayInfo::SetPosition(int pos) +{ + CWindow* pw; + CEdit* edit; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw == 0 ) return; + + edit = (CEdit*)pw->SearchControl(EVENT_EDIT1); + if ( edit == 0 ) return; + + edit->SetFirstLine(pos); +} + +// Retourne la position. + +int CDisplayInfo::RetPosition() +{ + CWindow* pw; + CEdit* edit; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw == 0 ) return 0; + + edit = (CEdit*)pw->SearchControl(EVENT_EDIT1); + if ( edit == 0 ) return 0; + + return edit->RetFirstLine(); +} + + + +// Changement de la taille de l'affichage des informations. + +void CDisplayInfo::ViewDisplayInfo() +{ + CWindow* pw; + CEdit* edit; + POINT dim; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw == 0 ) return; + + edit = (CEdit*)pw->SearchControl(EVENT_EDIT1); + if ( edit == 0 ) return; + + dim = m_engine->RetDim(); + edit->SetFontSize(m_main->RetFontSize()/(dim.x/640.0f)); +} + +// Retourne l'objet de l'homme. + +CObject* CDisplayInfo::SearchToto() +{ + ObjectType type; + CObject* pObj; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type == OBJECT_TOTO ) + { + return pObj; + } + } + return 0; +} + + +// Création de la liste des objets. + +typedef struct +{ + int total; + ObjectType type; +} +ObjectList; + +void ObjectAdd(ObjectList list[], ObjectType type) +{ + int i; + + for ( i=0 ; i<200 ; i++ ) + { + if ( list[i].total == 0 ) + { + list[i].total ++; + list[i].type = type; + list[i+1].total = 0; + return; + } + if ( list[i].type == type ) + { + list[i].total ++; + return; + } + } +} + +void ObjectWrite(FILE* file, ObjectList list[], int i) +{ + char line[100]; + char res[100]; + char* p; + + if ( list[i].total < 10 ) + { + sprintf(line, "\\c; %dx \\n;\\l;", list[i].total); + } + else + { + sprintf(line, "\\c;%dx \\n;\\l;", list[i].total); + } + + GetResource(RES_OBJECT, list[i].type, res); + if ( res[0] == 0 ) return; + strcat(line, res); + + strcat(line, "\\u "); + p = RetHelpFilename(list[i].type); + if ( p[0] == 0 ) return; + strcat(line, p+5); // skip "help\" + p = strstr(line, ".txt"); + if ( p != 0 ) *p = 0; + strcat(line, ";\n"); + fputs(line, file); +} + +// Crée le fichier contenant la liste des objets. + +void CDisplayInfo::CreateObjectsFile() +{ + FILE* file; + CObject* pObj; + ObjectType type; + ObjectList list[200]; + char line[100]; + int i; + BOOL bRadar, bAtLeast; + + file = fopen("help\\objects.txt", "w"); + if ( file == 0 ) return; + + list[0].total = 0; // vide la liste + bRadar = FALSE; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; + if ( !pObj->RetSelectable() ) continue; + if ( pObj->RetProxyActivate() ) continue; + + type = pObj->RetType(); + if ( type == OBJECT_NULL ) continue; + if ( type == OBJECT_FIX ) continue; + + ObjectAdd(list, type); + + if ( type == OBJECT_RADAR ) bRadar = TRUE; + } + + if ( bRadar ) + { + GetResource(RES_TEXT, RT_SATCOM_LIST, line); + fputs(line, file); + bAtLeast = FALSE; + for ( i=0 ; i<200 ; i++ ) + { + if ( list[i].total == 0 ) break; // fin de la liste ? + + if ( list[i].type == OBJECT_BASE || + list[i].type == OBJECT_HUMAN ) + { + ObjectWrite(file, list, i); + bAtLeast = TRUE; + } + } + if ( !bAtLeast ) + { + GetResource(RES_TEXT, RT_SATCOM_NULL, line); + fputs(line, file); + } + + strcpy(line, "\n"); + fputs(line, file); + GetResource(RES_TEXT, RT_SATCOM_BOT, line); + fputs(line, file); + bAtLeast = FALSE; + for ( i=0 ; i<200 ; i++ ) + { + if ( list[i].total == 0 ) break; // fin de la liste ? + + if ( list[i].type == OBJECT_MOBILEwt || + list[i].type == OBJECT_MOBILEtt || + list[i].type == OBJECT_MOBILEft || + list[i].type == OBJECT_MOBILEit || + list[i].type == OBJECT_MOBILEwa || + list[i].type == OBJECT_MOBILEta || + list[i].type == OBJECT_MOBILEfa || + list[i].type == OBJECT_MOBILEia || + list[i].type == OBJECT_MOBILEwc || + list[i].type == OBJECT_MOBILEtc || + list[i].type == OBJECT_MOBILEfc || + list[i].type == OBJECT_MOBILEic || + list[i].type == OBJECT_MOBILEwi || + list[i].type == OBJECT_MOBILEti || + list[i].type == OBJECT_MOBILEfi || + list[i].type == OBJECT_MOBILEii || + list[i].type == OBJECT_MOBILEws || + list[i].type == OBJECT_MOBILEts || + list[i].type == OBJECT_MOBILEfs || + list[i].type == OBJECT_MOBILEis || + list[i].type == OBJECT_MOBILErt || + list[i].type == OBJECT_MOBILErc || + list[i].type == OBJECT_MOBILErr || + list[i].type == OBJECT_MOBILErs || + list[i].type == OBJECT_MOBILEsa || + list[i].type == OBJECT_MOBILEtg || + list[i].type == OBJECT_MOBILEdr ) + { + ObjectWrite(file, list, i); + bAtLeast = TRUE; + } + } + if ( !bAtLeast ) + { + GetResource(RES_TEXT, RT_SATCOM_NULL, line); + fputs(line, file); + } + + strcpy(line, "\n"); + fputs(line, file); + GetResource(RES_TEXT, RT_SATCOM_BUILDING, line); + fputs(line, file); + bAtLeast = FALSE; + for ( i=0 ; i<200 ; i++ ) + { + if ( list[i].total == 0 ) break; // fin de la liste ? + + if ( list[i].type == OBJECT_DERRICK || + list[i].type == OBJECT_FACTORY || + list[i].type == OBJECT_STATION || + list[i].type == OBJECT_CONVERT || + list[i].type == OBJECT_REPAIR || + list[i].type == OBJECT_DESTROYER|| + list[i].type == OBJECT_TOWER || + list[i].type == OBJECT_NEST || + list[i].type == OBJECT_RESEARCH || + list[i].type == OBJECT_RADAR || + list[i].type == OBJECT_ENERGY || + list[i].type == OBJECT_LABO || + list[i].type == OBJECT_NUCLEAR || + list[i].type == OBJECT_START || + list[i].type == OBJECT_END || + list[i].type == OBJECT_INFO || + list[i].type == OBJECT_PARA || + list[i].type == OBJECT_TARGET1 || + list[i].type == OBJECT_TARGET2 || + list[i].type == OBJECT_SAFE || + list[i].type == OBJECT_HUSTON ) + { + ObjectWrite(file, list, i); + bAtLeast = TRUE; + } + } + if ( !bAtLeast ) + { + GetResource(RES_TEXT, RT_SATCOM_NULL, line); + fputs(line, file); + } + + strcpy(line, "\n"); + fputs(line, file); + GetResource(RES_TEXT, RT_SATCOM_FRET, line); + fputs(line, file); + bAtLeast = FALSE; + for ( i=0 ; i<200 ; i++ ) + { + if ( list[i].total == 0 ) break; // fin de la liste ? + + if ( list[i].type == OBJECT_STONE || + list[i].type == OBJECT_URANIUM || + list[i].type == OBJECT_METAL || + list[i].type == OBJECT_POWER || + list[i].type == OBJECT_ATOMIC || + list[i].type == OBJECT_BULLET || + list[i].type == OBJECT_BBOX || + list[i].type == OBJECT_TNT ) + { + ObjectWrite(file, list, i); + bAtLeast = TRUE; + } + } + if ( !bAtLeast ) + { + GetResource(RES_TEXT, RT_SATCOM_NULL, line); + fputs(line, file); + } + + strcpy(line, "\n"); + fputs(line, file); + GetResource(RES_TEXT, RT_SATCOM_ALIEN, line); + fputs(line, file); + bAtLeast = FALSE; + for ( i=0 ; i<200 ; i++ ) + { + if ( list[i].total == 0 ) break; // fin de la liste ? + + if ( list[i].type == OBJECT_MOTHER || + list[i].type == OBJECT_ANT || + list[i].type == OBJECT_BEE || + list[i].type == OBJECT_WORM || + list[i].type == OBJECT_SPIDER ) + { + ObjectWrite(file, list, i); + bAtLeast = TRUE; + } + } + if ( !bAtLeast ) + { + GetResource(RES_TEXT, RT_SATCOM_NULL, line); + fputs(line, file); + } + } + else + { + GetResource(RES_TEXT, RT_SATCOM_ERROR1, line); + fputs(line, file); + GetResource(RES_TEXT, RT_SATCOM_ERROR2, line); + fputs(line, file); + } + + strcpy(line, "\n"); + fputs(line, file); + + fclose(file); +} + + diff --git a/src/displayinfo.h b/src/displayinfo.h new file mode 100644 index 00000000..88f3721b --- /dev/null +++ b/src/displayinfo.h @@ -0,0 +1,72 @@ +// displayinfo.h + +#ifndef _DISPLAYINFO_H_ +#define _DISPLAYINFO_H_ + + +class CInstanceManager; +class CD3DEngine; +class CEvent; +class CRobotMain; +class CCamera; +class CInterface; +class CObject; +class CParticule; +class CLight; + + +class CDisplayInfo +{ +public: + CDisplayInfo(CInstanceManager* iMan); + ~CDisplayInfo(); + + BOOL EventProcess(const Event &event); + + void StartDisplayInfo(char *filename, int index, BOOL bSoluce); + void StopDisplayInfo(); + + void SetPosition(int pos); + int RetPosition(); + +protected: + BOOL EventFrame(const Event &event); + void HyperUpdate(); + void AdjustDisplayInfo(FPOINT wpos, FPOINT wdim); + void ChangeIndexButton(int index); + void UpdateIndexButton(); + void UpdateCopyButton(); + void ViewDisplayInfo(); + CObject* SearchToto(); + void CreateObjectsFile(); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CEvent* m_event; + CRobotMain* m_main; + CCamera* m_camera; + CInterface* m_interface; + CParticule* m_particule; + CLight* m_light; + + BOOL m_bInfoMaximized; + BOOL m_bInfoMinimized; + + int m_index; + CameraType m_infoCamera; + FPOINT m_infoNormalPos; + FPOINT m_infoNormalDim; + FPOINT m_infoActualPos; + FPOINT m_infoActualDim; + FPOINT m_infoFinalPos; + FPOINT m_infoFinalDim; + int m_lightSuppl; + BOOL m_bEditLock; + BOOL m_bInitPause; + BOOL m_bSoluce; + CObject* m_toto; +}; + + +#endif //_DISPLAYINFO_H_ diff --git a/src/displaytext.cpp b/src/displaytext.cpp new file mode 100644 index 00000000..f9c63124 --- /dev/null +++ b/src/displaytext.cpp @@ -0,0 +1,599 @@ +// displaytext.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "event.h" +#include "misc.h" +#include "restext.h" +#include "iman.h" +#include "object.h" +#include "motion.h" +#include "motiontoto.h" +#include "interface.h" +#include "button.h" +#include "label.h" +#include "window.h" +#include "group.h" +#include "text.h" +#include "sound.h" +#include "displaytext.h" + + + +#define FONTSIZE 12.0f + + + +// Constructeur de l'objet. + +CDisplayText::CDisplayText(CInstanceManager* iMan) +{ + int i; + + m_iMan = iMan; + m_iMan->AddInstance(CLASS_DISPLAYTEXT, this); + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_interface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + + for ( i=0 ; iDeleteInstance(CLASS_DISPLAYTEXT, this); +} + + +// Détruit l'objet. + +void CDisplayText::DeleteObject() +{ + m_interface->DeleteControl(EVENT_WINDOW2); +} + + +// Gestion d'un événement. + +BOOL CDisplayText::EventProcess(const Event &event) +{ + int i; + + if ( m_engine->RetPause() ) return TRUE; + + if ( event.event == EVENT_FRAME ) + { + for ( i=0 ; i 0.0f ) break; + if ( !ClearLastText() ) break; + } + } + + return TRUE; +} + + +// Affiche une erreur. + +void CDisplayText::DisplayError(Error err, CObject* pObj, float time) +{ + D3DVECTOR pos; + float h, d; + + if ( pObj == 0 ) return; + + pos = pObj->RetPosition(0); + h = RetIdealHeight(pObj); + d = RetIdealDist(pObj); + DisplayError(err, pos, h, d, time); +} + +// Affiche une erreur. + +void CDisplayText::DisplayError(Error err, D3DVECTOR goal, float height, + float dist, float time) +{ + TextType type; + char text[100]; + + if ( err == ERR_OK ) return; + +#if 0 + type = TT_INFO; + if ( err < INFO_FIRST ) + { + type = TT_ERROR; + } + if ( err == ERR_TOWER_POWER || + err == ERR_RESEARCH_POWER || + err == ERR_ENERGY_EMPTY || + err == ERR_LABO_NULL || + err == ERR_NUCLEAR_EMPTY || + err == ERR_CONVERT_EMPTY ) + { + type = TT_WARNING; + } +#else + type = TT_WARNING; + if ( err >= INFO_FIRST ) + { + type = TT_INFO; + } + if ( err == ERR_BAT_VIRUS || + err == ERR_VEH_VIRUS || + err == ERR_DELETEMOBILE || + err == ERR_DELETEBUILDING || + err == ERR_TOOMANY || + err == INFO_LOST ) + { + type = TT_ERROR; + } +#endif + + GetResource(RES_ERR, err, text); + DisplayText(text, goal, height, dist, time, type); +} + +// Affiche le texte. + +void CDisplayText::DisplayText(char *text, CObject* pObj, + float time, TextType type) +{ + D3DVECTOR pos; + float h, d; + + if ( pObj == 0 ) return; + + pos = pObj->RetPosition(0); + h = RetIdealHeight(pObj); + d = RetIdealDist(pObj); + DisplayText(text, pos, h, d, time, type); +} + +// Affiche le texte. + +void CDisplayText::DisplayText(char *text, D3DVECTOR goal, float height, + float dist, float time, TextType type) +{ + CObject* toto; + CMotion* motion; + CWindow* pw; + CButton* button; + CGroup* group; + CLabel* label; + FPOINT pos, ppos, dim; + Sound sound; + float hLine, hBox; + int nLine, icon, i; + + if ( !m_bEnable ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW2); + if ( pw == 0 ) + { + pos.x = 0.0f; + pos.y = 0.0f; + dim.x = 0.0f; + dim.y = 0.0f; + pw = m_interface->CreateWindows(pos, dim, 10, EVENT_WINDOW2); + } + + hBox = 0.045f; + hLine = m_engine->RetText()->RetHeight(FONTSIZE, FONT_COLOBOT); + + nLine = 0; + for ( i=0 ; iSearchControl(EventMsg(EVENT_DT_GROUP0+i)); + if ( group == 0 ) break; + nLine ++; + } + + if ( nLine == MAXDTLINE ) + { + ClearLastText(); + nLine --; + } + + pos.x = 0.10f; + pos.y = 0.92f-hBox-hBox*nLine; + dim.x = 0.80f; + dim.y = hBox; + + icon = 1; // jaune + if ( type == TT_ERROR ) icon = 9; // rouge + if ( type == TT_WARNING ) icon = 10; // bleu + if ( type == TT_INFO ) icon = 8; // vert + if ( type == TT_MESSAGE ) icon = 11; // jaune + pw->CreateGroup(pos, dim, icon, EventMsg(EVENT_DT_GROUP0+nLine)); + + pw->SetTrashEvent(FALSE); + + ppos = pos; + ppos.y -= hLine/2.0f; + label = pw->CreateLabel(ppos, dim, -1, EventMsg(EVENT_DT_LABEL0+nLine), text); + if ( label != 0 ) + { + label->SetFontSize(FONTSIZE); + } + + dim.x = dim.y*0.75f; + pos.x -= dim.x; + button = pw->CreateButton(pos, dim, 14, EventMsg(EVENT_DT_VISIT0+nLine)); + + if ( goal.x == 0.0f && + goal.y == 0.0f && + goal.z == 0.0f ) + { + button->ClearState(STATE_ENABLE); + } + + m_bExist[nLine] = TRUE; + m_visitGoal[nLine] = goal; + m_visitDist[nLine] = dist; + m_visitHeight[nLine] = height; + m_time[nLine] = time*m_delayFactor; + + toto = SearchToto(); + if ( toto != 0 ) + { + motion = toto->RetMotion(); + if ( motion != 0 ) + { + if ( type == TT_ERROR ) + { + motion->SetAction(MT_ERROR, 4.0f); + } + if ( type == TT_WARNING ) + { + motion->SetAction(MT_WARNING, 4.0f); + } + if ( type == TT_INFO ) + { + motion->SetAction(MT_INFO, 4.0f); + } + if ( type == TT_MESSAGE ) + { + motion->SetAction(MT_MESSAGE, 4.0f); + } + } + } + + if ( m_bHide ) + { + HideText(m_bHide); // cache tout + } + else + { + sound = SOUND_CLICK; + if ( type == TT_ERROR ) sound = SOUND_ERROR; + if ( type == TT_WARNING ) sound = SOUND_WARNING; + if ( type == TT_INFO ) sound = SOUND_INFO; + if ( type == TT_MESSAGE ) sound = SOUND_MESSAGE; + + if ( sound != SOUND_CLICK ) + { + m_sound->Play(sound); + } + } +} + +// Efface tous les textes. + +void CDisplayText::ClearText() +{ + CWindow* pw; + int i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW2); + + for ( i=0 ; iDeleteControl(EventMsg(EVENT_DT_GROUP0+i)); + pw->DeleteControl(EventMsg(EVENT_DT_LABEL0+i)); + pw->DeleteControl(EventMsg(EVENT_DT_VISIT0+i)); + } + m_bExist[i] = FALSE; + m_visitGoal[i] = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_visitDist[i] = 0.0f; + m_visitHeight[i] = 0.0f; + m_time[i] = 0.0f; + } +} + +// Cache ou montre tous les textes. + +void CDisplayText::HideText(BOOL bHide) +{ + CWindow* pw; + CGroup* pg; + CLabel* pl; + CButton* pb; + int i; + + m_bHide = bHide; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW2); + if ( pw == 0 ) return; + + for ( i=0 ; iSearchControl(EventMsg(EVENT_DT_GROUP0+i)); + if ( pg != 0 ) + { + pg->SetState(STATE_VISIBLE, !bHide); + } + + pl = (CLabel* )pw->SearchControl(EventMsg(EVENT_DT_LABEL0+i)); + if ( pl != 0 ) + { + pl->SetState(STATE_VISIBLE, !bHide); + } + + pb = (CButton*)pw->SearchControl(EventMsg(EVENT_DT_VISIT0+i)); + if ( pb != 0 ) + { + pb->SetState(STATE_VISIBLE, !bHide); + } + } +} + +// Efface le dernier texte (en haut de la liste). + +BOOL CDisplayText::ClearLastText() +{ + CWindow *pw; + CButton *pb1, *pb2; + CGroup *pg1, *pg2; + CLabel *pl1, *pl2; + int i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW2); + if ( pw == 0 ) return FALSE; + + pb2 = (CButton*)pw->SearchControl(EVENT_DT_VISIT0); + if ( pb2 == 0 ) return FALSE; // même pas de première ligne + pg2 = (CGroup*)pw->SearchControl(EVENT_DT_GROUP0); + if ( pg2 == 0 ) return FALSE; + pl2 = (CLabel*)pw->SearchControl(EVENT_DT_LABEL0); + if ( pl2 == 0 ) return FALSE; + + for ( i=0 ; iSearchControl(EventMsg(EVENT_DT_VISIT0+i+1)); + if ( pb2 == 0 ) break; + + pg2 = (CGroup*)pw->SearchControl(EventMsg(EVENT_DT_GROUP0+i+1)); + if ( pg2 == 0 ) break; + + pl2 = (CLabel*)pw->SearchControl(EventMsg(EVENT_DT_LABEL0+i+1)); + if ( pl2 == 0 ) break; + + pb1->SetState(STATE_ENABLE, pb2->TestState(STATE_ENABLE)); + pg1->SetIcon(pg2->RetIcon()); + pl1->SetName(pl2->RetName()); + + m_time[i] = m_time[i+1]; + m_visitGoal[i] = m_visitGoal[i+1]; + m_visitDist[i] = m_visitDist[i+1]; + m_visitHeight[i] = m_visitHeight[i+1]; // shift + } + + pw->DeleteControl(EventMsg(EVENT_DT_VISIT0+i)); + pw->DeleteControl(EventMsg(EVENT_DT_GROUP0+i)); + pw->DeleteControl(EventMsg(EVENT_DT_LABEL0+i)); + m_bExist[i] = FALSE; + return TRUE; +} + + +// Spécifie le facteur du délai. + +void CDisplayText::SetDelay(float factor) +{ + m_delayFactor = factor; +} + + +// Active l'affichage des textes. + +void CDisplayText::SetEnable(BOOL bEnable) +{ + m_bEnable = bEnable; +} + + +// Retourne le goal lors d'une visite. + +D3DVECTOR CDisplayText::RetVisitGoal(EventMsg event) +{ + int i; + + i = event-EVENT_DT_VISIT0; + if ( i < 0 || i >= MAXDTLINE ) return D3DVECTOR(0.0f, 0.0f, 0.0f); + return m_visitGoal[i]; +} + +// Retourne la distance lors d'une visite. + +float CDisplayText::RetVisitDist(EventMsg event) +{ + int i; + + i = event-EVENT_DT_VISIT0; + if ( i < 0 || i >= MAXDTLINE ) return 0.0f; + return m_visitDist[i]; +} + +// Retourne la hauteur lors d'une visite. + +float CDisplayText::RetVisitHeight(EventMsg event) +{ + int i; + + i = event-EVENT_DT_VISIT0; + if ( i < 0 || i >= MAXDTLINE ) return 0.0f; + return m_visitHeight[i]; +} + + +// Retourne la distance de visite idéale pour un objet donné. + +float CDisplayText::RetIdealDist(CObject* pObj) +{ + ObjectType type; + + if ( pObj == 0 ) return 40.0f; + + type = pObj->RetType(); + if ( type == OBJECT_PORTICO ) return 200.0f; + if ( type == OBJECT_BASE ) return 200.0f; + if ( type == OBJECT_NUCLEAR ) return 100.0f; + if ( type == OBJECT_PARA ) return 100.0f; + if ( type == OBJECT_SAFE ) return 100.0f; + if ( type == OBJECT_TOWER ) return 80.0f; + + return 60.0f; +} + +// Retourne la hauteur de visite idéale pour un objet donné. + +float CDisplayText::RetIdealHeight(CObject* pObj) +{ + ObjectType type; + + if ( pObj == 0 ) return 5.0f; + + type = pObj->RetType(); + if ( type == OBJECT_DERRICK ) return 35.0f; + if ( type == OBJECT_FACTORY ) return 22.0f; + if ( type == OBJECT_REPAIR ) return 30.0f; + if ( type == OBJECT_DESTROYER) return 30.0f; + if ( type == OBJECT_STATION ) return 13.0f; + if ( type == OBJECT_CONVERT ) return 20.0f; + if ( type == OBJECT_TOWER ) return 30.0f; + if ( type == OBJECT_RESEARCH ) return 22.0f; + if ( type == OBJECT_RADAR ) return 19.0f; + if ( type == OBJECT_INFO ) return 19.0f; + if ( type == OBJECT_ENERGY ) return 20.0f; + if ( type == OBJECT_LABO ) return 16.0f; + if ( type == OBJECT_NUCLEAR ) return 40.0f; + if ( type == OBJECT_PARA ) return 40.0f; + if ( type == OBJECT_SAFE ) return 20.0f; + + return 15.0f; +} + + +// Supprime toutes les visites. + +void CDisplayText::ClearVisit() +{ + CWindow* pw; + CButton* pb; + int i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW2); + if ( pw == 0 ) return; + + for ( i=0 ; iSearchControl(EventMsg(EVENT_DT_VISIT0+i)); + if ( pb == 0 ) break; + pb->SetIcon(14); // yeux + } +} + +// Met un bouton en mode "visite". + +void CDisplayText::SetVisit(EventMsg event) +{ + CWindow* pw; + CButton* pb; + int i; + + i = event-EVENT_DT_VISIT0; + if ( i < 0 || i >= MAXDTLINE ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW2); + if ( pw == 0 ) return; + pb = (CButton*)pw->SearchControl(EventMsg(EVENT_DT_VISIT0+i)); + if ( pb == 0 ) return; + pb->SetIcon(48); // > +} + +// Indique si un bouton est en mode "visite". + +BOOL CDisplayText::IsVisit(EventMsg event) +{ + CWindow* pw; + CButton* pb; + int i; + + i = event-EVENT_DT_VISIT0; + if ( i < 0 || i >= MAXDTLINE ) return FALSE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW2); + if ( pw == 0 ) return FALSE; + pb = (CButton*)pw->SearchControl(EventMsg(EVENT_DT_VISIT0+i)); + if ( pb == 0 ) return FALSE; + return (pb->RetIcon() == 48); // > ? +} + + +// Retourne l'objet de toto. + +CObject* CDisplayText::SearchToto() +{ + ObjectType type; + CObject* pObj; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type == OBJECT_TOTO ) + { + return pObj; + } + } + return 0; +} + diff --git a/src/displaytext.h b/src/displaytext.h new file mode 100644 index 00000000..3f138f27 --- /dev/null +++ b/src/displaytext.h @@ -0,0 +1,77 @@ +// displaytext.h + +#ifndef _DISPLAYTEXT_H_ +#define _DISPLAYTEXT_H_ + + +class CInstanceManager; +class CD3DEngine; +class CInterface; +class CObject; +class CSound; + + +enum TextType +{ + TT_ERROR = 1, + TT_WARNING = 2, + TT_INFO = 3, + TT_MESSAGE = 4, +}; + +#define MAXDTLINE 4 + + +class CDisplayText +{ +public: + CDisplayText(CInstanceManager* iMan); + ~CDisplayText(); + + void DeleteObject(); + + BOOL EventProcess(const Event &event); + + void DisplayError(Error err, CObject* pObj, float time=10.0f); + void DisplayError(Error err, D3DVECTOR goal, float height=15.0f, float dist=60.0f, float time=10.0f); + void DisplayText(char *text, CObject* pObj, float time=10.0f, TextType type=TT_INFO); + void DisplayText(char *text, D3DVECTOR goal, float height=15.0f, float dist=60.0f, float time=10.0f, TextType type=TT_INFO); + void HideText(BOOL bHide); + void ClearText(); + BOOL ClearLastText(); + void SetDelay(float factor); + void SetEnable(BOOL bEnable); + + D3DVECTOR RetVisitGoal(EventMsg event); + float RetVisitDist(EventMsg event); + float RetVisitHeight(EventMsg event); + + float RetIdealDist(CObject* pObj); + float RetIdealHeight(CObject* pObj); + + void ClearVisit(); + void SetVisit(EventMsg event); + BOOL IsVisit(EventMsg event); + +protected: + CObject* SearchToto(); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CInterface* m_interface; + CSound* m_sound; + + BOOL m_bExist[MAXDTLINE]; + float m_time[MAXDTLINE]; + D3DVECTOR m_visitGoal[MAXDTLINE]; + float m_visitDist[MAXDTLINE]; + float m_visitHeight[MAXDTLINE]; + + BOOL m_bHide; + BOOL m_bEnable; + float m_delayFactor; +}; + + +#endif //_DISPLAYTEXT_H_ diff --git a/src/edit.cpp b/src/edit.cpp new file mode 100644 index 00000000..6d4beac2 --- /dev/null +++ b/src/edit.cpp @@ -0,0 +1,3305 @@ +// edit.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "language.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "scroll.h" +#include "text.h" +#include "edit.h" + + +#define MARGX (5.0f/640.0f) +#define MARGY (5.0f/480.0f) +#define MARGYS (4.0f/480.0f) +#define MARGY1 (1.0f/480.0f) +#define DELAY_DBCLICK 0.3f // délai pour double-clic +#define DELAY_SCROLL 0.1f // délai pour défilement +#define BIG_FONT 1.6f // agrandissement pour \b; + + + + +// Indique si un caractère est un espace. + +BOOL IsSpace(int character) +{ + return ( character == ' ' || + character == '\t' || + character == '\n' ); +} + +// Indique si un caractère fait partie d'un mot. + +BOOL IsWord(int character) +{ + char c; + + c = tolower(RetNoAccent(character)); + + return ( (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + c == '_' ); +} + +// Indique si un caractère est un séparateur de mot. + +BOOL IsSep(int character) +{ + if ( IsSpace(character) ) return FALSE; + return !IsWord(character); +} + + + +// Constructeur de l'objet. + +CEdit::CEdit(CInstanceManager* iMan) : CControl(iMan) +{ + FPOINT pos; + int i; + + CControl::CControl(iMan); + + m_maxChar = 100; + m_text = (char*)malloc(sizeof(char)*(m_maxChar+1)); + m_format = 0; + m_len = 0; + + m_fontType = FONT_COURIER; + m_scroll = 0; + m_bEdit = TRUE; + m_bHilite = TRUE; + m_bInsideScroll = TRUE; + m_bCapture = FALSE; + m_bDisplaySpec = FALSE; + m_bSoluce = FALSE; + m_bGeneric = FALSE; + m_bAutoIndent = FALSE; + m_cursor1 = 0; + m_cursor2 = 0; + m_column = 0; + m_imageTotal = 0; + + HyperFlush(); + + for ( i=0 ; iCreate(pos, dim, -1, EVENT_NULL); + MoveAdjust(); + } + + return TRUE; +} + + +void CEdit::SetPos(FPOINT pos) +{ + CControl::SetPos(pos); + MoveAdjust(); +} + +void CEdit::SetDim(FPOINT dim) +{ + CControl::SetDim(dim); + MoveAdjust(); +} + +void CEdit::MoveAdjust() +{ + FPOINT pos, dim; + float height; + + m_lineDescent = m_engine->RetText()->RetDescent(m_fontSize, m_fontType); + m_lineAscent = m_engine->RetText()->RetAscent(m_fontSize, m_fontType); + m_lineHeight = m_engine->RetText()->RetHeight(m_fontSize, m_fontType); + + height = m_dim.y-(m_bMulti?MARGY*2.0f:MARGY1); + m_lineVisible = (int)(height/m_lineHeight); + + if ( m_scroll != 0 ) + { + if ( m_bInsideScroll ) + { + pos.x = m_pos.x+m_dim.x-MARGX-SCROLL_WIDTH; + pos.y = m_pos.y+MARGYS; + dim.x = SCROLL_WIDTH; + dim.y = m_dim.y-MARGYS*2.0f; + } + else + { + pos.x = m_pos.x+m_dim.x-SCROLL_WIDTH; + pos.y = m_pos.y; + dim.x = SCROLL_WIDTH; + dim.y = m_dim.y; + } + m_scroll->SetPos(pos); + m_scroll->SetDim(dim); + } + + Justif(); + + if ( m_lineFirst > m_lineTotal-m_lineVisible ) + { + m_lineFirst = m_lineTotal-m_lineVisible; + if ( m_lineFirst < 0 ) m_lineFirst = 0; + } + + pos.x = m_pos.x+m_dim.x-(m_bMulti?SCROLL_WIDTH:0.0f); + pos.y = m_pos.y; + GlintCreate(pos, FALSE, FALSE); +} + + +// Gestion d'un événement. + +BOOL CEdit::EventProcess(const Event &event) +{ + BOOL bShift, bControl; + + if ( (m_state & STATE_VISIBLE) == 0 ) return TRUE; + + if ( event.event == EVENT_KEYDOWN && + event.param == VK_WHEELUP && + Detect(event.pos) ) + { + Scroll(m_lineFirst-3, TRUE); + return TRUE; + } + if ( event.event == EVENT_KEYDOWN && + event.param == VK_WHEELDOWN && + Detect(event.pos) ) + { + Scroll(m_lineFirst+3, TRUE); + return TRUE; + } + + CControl::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + m_time += event.rTime; + m_timeBlink += event.rTime; + } + + if ( event.event == EVENT_MOUSEMOVE ) + { + if ( Detect(event.pos) && + event.pos.x < m_pos.x+m_dim.x-(m_bMulti?MARGX+SCROLL_WIDTH:0.0f) ) + { + if ( m_bEdit ) + { + m_engine->SetMouseType(D3DMOUSEEDIT); + } + else + { + if ( IsLinkPos(event.pos) ) + { + m_engine->SetMouseType(D3DMOUSEHAND); + } + else + { + m_engine->SetMouseType(D3DMOUSENORM); + } + } + } + } + + if ( m_scroll != 0 && !m_bGeneric ) + { + m_scroll->EventProcess(event); + + if ( event.event == m_scroll->RetEventMsg() ) + { + Scroll(); + return TRUE; + } + } + + if ( event.event == EVENT_KEYDOWN && m_bFocus ) + { + bShift = (event.keyState&KS_SHIFT); + bControl = (event.keyState&KS_CONTROL); + + if ( (event.param == 'X' && !bShift && bControl) || + (event.param == VK_DELETE && bShift && !bControl) ) + { + Cut(); + return TRUE; + } + if ( (event.param == 'C' && !bShift && bControl) || + (event.param == VK_INSERT && !bShift && bControl) ) + { + Copy(); + return TRUE; + } + if ( (event.param == 'V' && !bShift && bControl) || + (event.param == VK_INSERT && bShift && !bControl) ) + { + Paste(); + return TRUE; + } + + if ( event.param == 'A' && !bShift && bControl ) + { + SetCursor(999999, 0); + return TRUE; + } + + if ( event.param == 'O' && !bShift && bControl ) + { + Event newEvent; + m_event->MakeEvent(newEvent, EVENT_STUDIO_OPEN); + m_event->AddEvent(newEvent); + } + if ( event.param == 'S' && !bShift && bControl ) + { + Event newEvent; + m_event->MakeEvent(newEvent, EVENT_STUDIO_SAVE); + m_event->AddEvent(newEvent); + } + + if ( event.param == 'Z' && !bShift && bControl ) + { + Undo(); + return TRUE; + } + + if ( event.param == 'U' && !bShift && bControl ) + { + if ( MinMaj(FALSE) ) return TRUE; + } + if ( event.param == 'U' && bShift && bControl ) + { + if ( MinMaj(TRUE) ) return TRUE; + } + + if ( event.param == VK_TAB && !bShift && !bControl && !m_bAutoIndent ) + { + if ( Shift(FALSE) ) return TRUE; + } + if ( event.param == VK_TAB && bShift && !bControl && !m_bAutoIndent ) + { + if ( Shift(TRUE) ) return TRUE; + } + + if ( m_bEdit ) + { + if ( event.param == VK_LEFT ) + { + MoveChar(-1, bControl, bShift); + return TRUE; + } + if ( event.param == VK_RIGHT ) + { + MoveChar(1, bControl, bShift); + return TRUE; + } + if ( event.param == VK_UP ) + { + MoveLine(-1, bControl, bShift); + return TRUE; + } + if ( event.param == VK_DOWN ) + { + MoveLine(1, bControl, bShift); + return TRUE; + } + + if ( event.param == VK_PRIOR ) // PageUp ? + { + MoveLine(-(m_lineVisible-1), bControl, bShift); + return TRUE; + } + if ( event.param == VK_NEXT ) // PageDown ? + { + MoveLine(m_lineVisible-1, bControl, bShift); + return TRUE; + } + } + else + { + if ( event.param == VK_LEFT || + event.param == VK_UP ) + { + Scroll(m_lineFirst-1, TRUE); + return TRUE; + } + if ( event.param == VK_RIGHT || + event.param == VK_DOWN ) + { + Scroll(m_lineFirst+1, TRUE); + return TRUE; + } + + if ( event.param == VK_PRIOR ) // PageUp ? + { + Scroll(m_lineFirst-(m_lineVisible-1), TRUE); + return TRUE; + } + if ( event.param == VK_NEXT ) // PageDown ? + { + Scroll(m_lineFirst+(m_lineVisible-1), TRUE); + return TRUE; + } + } + + if ( event.param == VK_HOME ) + { + MoveHome(bControl, bShift); + return TRUE; + } + if ( event.param == VK_END ) + { + MoveEnd(bControl, bShift); + return TRUE; + } + + if ( event.param == VK_BACK ) // backspace ( <- ) ? + { + Delete(-1); + SendModifEvent(); + return TRUE; + } + if ( event.param == VK_DELETE ) + { + Delete(1); + SendModifEvent(); + return TRUE; + } + + if ( event.param == VK_RETURN ) + { + Insert('\n'); + SendModifEvent(); + return TRUE; + } + if ( event.param == VK_TAB ) + { + Insert('\t'); + SendModifEvent(); + return TRUE; + } + } + + if ( event.event == EVENT_CHAR && m_bFocus ) + { + if ( event.param >= ' ' && event.param <= 255 ) + { + Insert((char)event.param); + SendModifEvent(); + return TRUE; + } + } + + if ( event.event == EVENT_FOCUS ) + { + if ( event.param == m_eventMsg ) + { + m_bFocus = TRUE; + } + else + { + m_bFocus = FALSE; + } + } + + if ( event.event == EVENT_LBUTTONDOWN ) + { + m_mouseFirstPos = event.pos; + m_mouseLastPos = event.pos; + if ( Detect(event.pos) ) + { + if ( event.pos.x < m_pos.x+m_dim.x-(m_bMulti?MARGX+SCROLL_WIDTH:0.0f) ) + { + MouseClick(event.pos); + if ( m_bEdit || m_bHilite ) m_bCapture = TRUE; + } + m_bFocus = TRUE; + } + else + { + m_bFocus = FALSE; + } + } + + if ( event.event == EVENT_MOUSEMOVE && m_bCapture ) + { + m_mouseLastPos = event.pos; + MouseMove(event.pos); + } + + if ( event.event == EVENT_FRAME && m_bCapture ) + { + MouseMove(m_mouseLastPos); + } + + if ( event.event == EVENT_LBUTTONUP ) + { + if ( Detect(event.pos) ) + { + if ( event.pos.x < m_pos.x+m_dim.x-(m_bMulti?MARGX+SCROLL_WIDTH:0.0f) ) + { + MouseRelease(m_mouseFirstPos); + } + } + if ( m_bCapture ) + { + if ( m_timeLastClick+DELAY_DBCLICK > m_time ) // double-clic ? + { + MouseDoubleClick(event.pos); + } + m_timeLastClick = m_time; + m_bCapture = FALSE; + } + } + + return TRUE; +} + + +// Envoie un événement pour indiquer que le texte a été modifié. + +void CEdit::SendModifEvent() +{ + Event newEvent; + + m_event->MakeEvent(newEvent, m_eventMsg); + m_event->AddEvent(newEvent); +} + + +// Détecte si la souris est sur un caractère lien hypertexte. + +BOOL CEdit::IsLinkPos(FPOINT pos) +{ + int i; + + if ( m_format == 0 ) return FALSE; + + i = MouseDetect(pos); + if ( i == -1 ) return FALSE; + if ( i >= m_len ) return FALSE; + + if ( (m_format[i]&COLOR_MASK) == COLOR_LINK ) return TRUE; + return FALSE; +} + + +// Positionne le curseur suite à un double-clic. + +void CEdit::MouseDoubleClick(FPOINT mouse) +{ + int i, character; + + if ( m_bMulti ) // multi-lignes ? + { + i = MouseDetect(mouse); + if ( i == -1 ) return; + + while ( i > 0 ) + { + character = (unsigned char)m_text[i-1]; + if ( !IsWord(character) ) break; + i --; + } + m_cursor2 = i; + + while ( i < m_len ) + { + character = (unsigned char)m_text[i]; + if ( !IsWord(character) ) break; + i ++; + } + m_cursor1 = i; + } + else // mono-ligne ? + { + m_cursor2 = 0; + m_cursor1 = m_len; // sélectionne tout + } + + m_bUndoForce = TRUE; + + Justif(); + ColumnFix(); +} + +// Positionne le curseur suite à un clic. + +void CEdit::MouseClick(FPOINT mouse) +{ + int i; + + i = MouseDetect(mouse); + if ( i == -1 ) return; + + if ( m_bEdit || m_bHilite ) + { + m_cursor1 = i; + m_cursor2 = i; + m_bUndoForce = TRUE; + m_timeBlink = 0.0f; // allume le curseur immédiatement + ColumnFix(); + } +} + +// Positionne le curseur suite à un clic relâché. + +void CEdit::MouseRelease(FPOINT mouse) +{ + int i, j, rank; + + i = MouseDetect(mouse); + if ( i == -1 ) return; + + if ( !m_bEdit ) + { + if ( m_format != 0 && i < m_len && m_cursor1 == m_cursor2 && + (m_format[i]&COLOR_MASK) == COLOR_LINK ) + { + rank = -1; + for ( j=0 ; j<=i ; j++ ) + { + if ( (j == 0 || (m_format[j-1]&COLOR_MASK) != COLOR_LINK) && + (m_format[j+0]&COLOR_MASK) == COLOR_LINK ) + { + rank ++; + } + } + HyperJump(m_link[rank].name, m_link[rank].marker); + } + } +} + +// Positionne le curseur suite à un déplacement. + +void CEdit::MouseMove(FPOINT mouse) +{ + int i; + + if ( m_bMulti && + m_timeLastScroll+DELAY_SCROLL <= m_time ) + { + if ( mouse.y > m_pos.y+m_dim.y ) // plus haut ? + { + Scroll(m_lineFirst-1, FALSE); + mouse.y = m_pos.y+m_dim.y-MARGY-m_lineHeight/2.0f; + } + if ( mouse.y < m_pos.y ) // plus bas ? + { + Scroll(m_lineFirst+1, FALSE); + mouse.y = m_pos.y+m_dim.y-MARGY-m_lineVisible*m_lineHeight+m_lineHeight/2.0f; + } + m_timeLastScroll = m_time; + } + + i = MouseDetect(mouse); + if ( i != -1 ) + { + m_cursor1 = i; + m_bUndoForce = TRUE; + m_timeBlink = 0.0f; // allume le curseur immédiatement + ColumnFix(); + } +} + +// Positionne le curseur suite à un clic. + +int CEdit::MouseDetect(FPOINT mouse) +{ + FPOINT pos; + float indentLength, offset, size; + int i, len, c; + BOOL bTitle; + + if ( m_bAutoIndent ) + { + indentLength = m_engine->RetText()->RetCharWidth(' ', 0.0f, m_fontSize, m_fontStretch, m_fontType) + * m_engine->RetEditIndentValue(); + } + + pos.y = m_pos.y+m_dim.y-m_lineHeight-(m_bMulti?MARGY:MARGY1); + for ( i=m_lineFirst ; i= m_lineFirst+m_lineVisible ) break; + + pos.x = m_pos.x+(10.0f/640.0f); + if ( m_bAutoIndent ) + { + pos.x += indentLength*m_lineIndent[i]; + } + offset = mouse.x-pos.x; + + if ( bTitle ) pos.y -= m_lineHeight; + + if ( mouse.y > pos.y ) + { + len = m_lineOffset[i+1] - m_lineOffset[i]; + + if ( m_format == 0 ) + { + c = m_engine->RetText()->Detect(m_text+m_lineOffset[i], + len, offset, m_fontSize, + m_fontStretch, m_fontType); + } + else + { + size = m_fontSize; + if ( bTitle ) size *= BIG_FONT; + + c = m_engine->RetText()->Detect(m_text+m_lineOffset[i], + m_format+m_lineOffset[i], + len, offset, size, + m_fontStretch); + } + return m_lineOffset[i]+c; + } + + if ( bTitle ) i ++; + pos.y -= m_lineHeight; + } + return -1; +} + + +// Efface tout l'historique. + +void CEdit::HyperFlush() +{ + m_historyTotal = 0; + m_historyCurrent = -1; +} + +// Indique quelle est la page home. + +void CEdit::HyperHome(char *filename) +{ + HyperFlush(); + HyperAdd(filename, 0); +} + +// Effectue un hyper saut à travers un lien. + +void CEdit::HyperJump(char *name, char *marker) +{ + char filename[100]; + char sMarker[100]; + int i, line, pos; + + if ( m_historyCurrent >= 0 ) + { + m_history[m_historyCurrent].firstLine = m_lineFirst; + } + + strcpy(sMarker, marker); + +//? sprintf(filename, "help\\%s.txt", name); + if ( name[0] == '%' ) + { + UserDir(filename, name, ""); + strcat(filename, ".txt"); + } + else + { + sprintf(filename, "help\\%s.txt", name); + } + if ( ReadText(filename) ) + { + Justif(); + + line = 0; + for ( i=0 ; i= m_lineOffset[i] ) + { + line = i; + } + } + break; + } + } + + SetFirstLine(line); + HyperAdd(filename, line); + } +} + +// Ajoute un texte visité à l'historique. + +BOOL CEdit::HyperAdd(char *filename, int firstLine) +{ + if ( m_historyCurrent >= EDITHISTORYMAX-1 ) return FALSE; + + m_historyCurrent ++; + strcpy(m_history[m_historyCurrent].filename, filename); + m_history[m_historyCurrent].firstLine = firstLine; + + m_historyTotal = m_historyCurrent+1; + return TRUE; +} + +// Indique si un bouton EVENT_HYPER_* est actif ou non. + +BOOL CEdit::HyperTest(EventMsg event) +{ + if ( event == EVENT_HYPER_HOME ) + { + return ( m_historyCurrent > 0 ); + } + + if ( event == EVENT_HYPER_PREV ) + { + return ( m_historyCurrent > 0 ); + } + + if ( event == EVENT_HYPER_NEXT ) + { + return ( m_historyCurrent < m_historyTotal-1 ); + } + + return FALSE; +} + +// Effectue l'action correspondant à un bouton EVENT_HYPER_*. + +BOOL CEdit::HyperGo(EventMsg event) +{ + if ( !HyperTest(event) ) return FALSE; + + m_history[m_historyCurrent].firstLine = m_lineFirst; + + if ( event == EVENT_HYPER_HOME ) + { + m_historyCurrent = 0; + } + + if ( event == EVENT_HYPER_PREV ) + { + m_historyCurrent --; + } + + if ( event == EVENT_HYPER_NEXT ) + { + m_historyCurrent ++; + } + + ReadText(m_history[m_historyCurrent].filename); + Justif(); + SetFirstLine(m_history[m_historyCurrent].firstLine); + return TRUE; +} + + +// Dessine la ligne éditable. + +void CEdit::Draw() +{ + FPOINT pos, ppos, dim, start, end; + float size, indentLength; + int i, j, beg, len, c1, c2, o1, o2, eol, iIndex, line; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + if ( m_state & STATE_SHADOW ) + { + DrawShadow(m_pos, m_dim); + } + + pos.x = m_pos.x; + pos.y = m_pos.y; + dim.x = m_dim.x; + if ( !m_bInsideScroll ) dim.x -= m_bMulti?SCROLL_WIDTH:0.0f; + dim.y = m_dim.y; + DrawBack(pos, dim); // fond + + // Affiche toutes les lignes. + c1 = m_cursor1; + c2 = m_cursor2; + if ( c1 > c2 ) Swap(c1, c2); // toujours c1 <= c2 + + if ( m_bInsideScroll ) + { + dim.x -= m_bMulti?SCROLL_WIDTH:0.0f + (1.0f/640.0f); + } + + if ( m_bAutoIndent ) + { + indentLength = m_engine->RetText()->RetCharWidth(' ', 0.0f, m_fontSize, m_fontStretch, m_fontType) + * m_engine->RetEditIndentValue(); + } + + pos.y = m_pos.y+m_dim.y-m_lineHeight-(m_bMulti?MARGY:MARGY1); + for ( i=m_lineFirst ; i= m_lineFirst+m_lineVisible ) break; + + pos.x = m_pos.x+(10.0f/640.0f); + if ( m_bAutoIndent ) + { + for ( j=0 ; jRetText()->DrawText(&s, 1, pos, 1.0f, 1, m_fontSize, m_fontStretch, m_fontType, 0); + pos.x += indentLength; + } + } + + beg = m_lineOffset[i]; + len = m_lineOffset[i+1] - m_lineOffset[i]; + + ppos = pos; + size = m_fontSize; + + // Grand titre \b; ? + if ( beg+len < m_len && m_format != 0 && + (m_format[beg]&TITLE_MASK) == TITLE_BIG ) + { + start.x = ppos.x-MARGX; + end.x = dim.x-MARGX*2.0f; + start.y = ppos.y-(m_bMulti?0.0f:MARGY1)-m_lineHeight*(BIG_FONT-1.0f); + end.y = m_lineHeight*BIG_FONT; + DrawPart(start, end, 2); // fond bleu dégradé -> + + size *= BIG_FONT; + ppos.y -= m_lineHeight*(BIG_FONT-1.0f); + } + + // Titre \t; ? + if ( beg+len < m_len && m_format != 0 && + (m_format[beg]&TITLE_MASK) == TITLE_NORM ) + { + start.x = ppos.x-MARGX; + end.x = dim.x-MARGX*2.0f; + start.y = ppos.y-(m_bMulti?0.0f:MARGY1); + end.y = m_lineHeight; + DrawPart(start, end, 2); // fond bleu dégradé -> + } + + // Sous-titre \s; ? + if ( beg+len < m_len && m_format != 0 && + (m_format[beg]&TITLE_MASK) == TITLE_LITTLE ) + { + start.x = ppos.x-MARGX; + end.x = dim.x-MARGX*2.0f; + start.y = ppos.y-(m_bMulti?0.0f:MARGY1); + end.y = m_lineHeight; + DrawPart(start, end, 3); // fond jaune dégradé -> + } + + // Tableau \tab; ? + if ( beg+len < m_len && m_format != 0 && + (m_format[beg]&COLOR_MASK) == COLOR_TABLE ) + { + start.x = ppos.x-MARGX; + end.x = dim.x-MARGX*2.0f; + start.y = ppos.y-(m_bMulti?0.0f:MARGY1); + end.y = m_lineHeight; + DrawPart(start, end, 11); // fond orange dégradé -> + } + + // Image \image; ? + if ( beg+len < m_len && m_format != 0 && + (m_format[beg]&IMAGE_MASK) != 0 ) + { + line = 1; + while ( true ) // regroupe les tranches d'image + { + if ( i+line >= m_lineTotal || + i+line >= m_lineFirst+m_lineVisible || + (m_format[beg+line]&IMAGE_MASK) == 0 ) break; + line ++; + } + + iIndex = m_text[beg]; // caractère = index dans m_image + pos.y -= m_lineHeight*(line-1); + DrawImage(pos, m_image[iIndex].name, + m_image[iIndex].width*(m_fontSize/SMALLFONT), + m_image[iIndex].offset, m_image[iIndex].height*line, line); + pos.y -= m_lineHeight; + i += line-1; + continue; + } + + if ( ((m_bEdit && m_bFocus && m_bHilite) || + (!m_bEdit && m_bHilite) ) && + c1 != c2 && beg <= c2 && beg+len >= c1 ) // zone sélectionnée ? + { + o1 = c1; if ( o1 < beg ) o1 = beg; + o2 = c2; if ( o2 > beg+len ) o2 = beg+len; + + if ( m_format == 0 ) + { + start.x = ppos.x+m_engine->RetText()->RetStringWidth(m_text+beg, o1-beg, size, m_fontStretch, m_fontType); + end.x = m_engine->RetText()->RetStringWidth(m_text+o1, o2-o1, size, m_fontStretch, m_fontType); + } + else + { + start.x = ppos.x+m_engine->RetText()->RetStringWidth(m_text+beg, m_format+beg, o1-beg, size, m_fontStretch); + end.x = m_engine->RetText()->RetStringWidth(m_text+o1, m_format+o1, o2-o1, size, m_fontStretch); + } + + start.y = ppos.y-(m_bMulti?0.0f:MARGY1); + end.y = m_lineHeight; + if ( m_format != 0 && (m_format[beg]&TITLE_MASK) == TITLE_BIG ) end.y *= BIG_FONT; + DrawPart(start, end, 1); // fond jaune uni + } + + eol = 16; // > + if ( len > 0 && m_text[beg+len-1] == '\n' ) + { + len --; // n'affiche pas le '\n' + eol = 0; // rien + } + if ( beg+len >= m_len ) + { + eol = 2; // carré (eot) + } + if ( !m_bMulti || !m_bDisplaySpec ) eol = 0; + if ( m_format == 0 ) + { + m_engine->RetText()->DrawText(m_text+beg, len, ppos, m_dim.x, 1, size, m_fontStretch, m_fontType, eol); + } + else + { + m_engine->RetText()->DrawText(m_text+beg, m_format+beg, len, ppos, m_dim.x, 1, size, m_fontStretch, eol); + } + + pos.y -= m_lineHeight; + + if ( i < m_lineTotal-2 && m_lineOffset[i+1] == m_lineOffset[i+2] ) + { + pos.y -= m_lineHeight; // saute double ligne \b; + i ++; + } + } + + // Affiche le curseur. + if ( (m_bEdit && m_bFocus && m_bHilite && Mod(m_timeBlink, 1.0f) <= 0.5f) ) // ça clignotte + { + pos.y = m_pos.y+m_dim.y-m_lineHeight-(m_bMulti?MARGY:MARGY1*2.0f); + for ( i=m_lineFirst ; iRetText()->DimText(m_text+m_lineOffset[i], len, + pos, 1, size, + m_fontStretch, m_fontType, + start, end); + } + else + { + m_engine->RetText()->DimText(m_text+m_lineOffset[i], + m_format+m_lineOffset[i], + len, pos, 1, size, + m_fontStretch, + start, end); + } + + pos.x = end.x; + break; + } + pos.y -= m_lineHeight; + } + pos.x -= 1.0f/640.0f; + dim.x = 2.0f/640.0f; + dim.y = m_lineHeight; + DrawPart(pos, dim, 0); // rouge + } + + if ( m_scroll != 0 && !m_bGeneric ) + { + m_scroll->Draw(); + } +} + +// Dessine une partie d'image. + +void CEdit::DrawImage(FPOINT pos, char *name, float width, + float offset, float height, int nbLine) +{ + FPOINT uv1, uv2, dim; + float dp; + char filename[100]; + +//? sprintf(filename, "diagram\\%s.bmp", name); + UserDir(filename, name, "diagram"); + strcat(filename, ".bmp"); + + m_engine->SetTexture(filename); + m_engine->SetState(D3DSTATENORMAL); + + uv1.x = 0.0f; + uv2.x = 1.0f; + uv1.y = offset; + uv2.y = offset+height; + + dp = 0.5f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + dim.x = width; + dim.y = m_lineHeight*nbLine; + DrawIcon(pos, dim, uv1, uv2); +} + +// Dessine le fond. + +void CEdit::DrawBack(FPOINT pos, FPOINT dim) +{ + FPOINT uv1,uv2, corner; + float dp; + + if ( m_bGeneric ) return; + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + + if ( m_bMulti ) + { + uv1.x = 128.0f/256.0f; // bleu clair + uv1.y = 64.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 96.0f/256.0f; + } + else + { + uv1.x = 160.0f/256.0f; // bleu moyen + uv1.y = 192.0f/256.0f; + uv2.x = 192.0f/256.0f; + uv2.y = 224.0f/256.0f; + } + if ( m_icon == 1 ) + { + uv1.x = 192.0f/256.0f; // orange + uv1.y = 96.0f/256.0f; + uv2.x = 224.0f/256.0f; + uv2.y = 128.0f/256.0f; + } + + dp = 0.5f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + if ( m_bMulti ) + { + corner.x = 10.0f/640.0f; + corner.y = 10.0f/480.0f; + DrawIcon(pos, dim, uv1, uv2, corner, 8.0f/256.0f); + } + else + { + DrawIcon(pos, dim, uv1, uv2, 8.0f/256.0f); + } +} + +// Dessine une icône de fond. + +void CEdit::DrawPart(FPOINT pos, FPOINT dim, int icon) +{ + FPOINT uv1, uv2; + float dp; + +#if _POLISH + m_engine->SetTexture("textp.tga"); +#else + m_engine->SetTexture("text.tga"); +#endif + m_engine->SetState(D3DSTATENORMAL); + + uv1.x = (16.0f/256.0f)*(icon%16); + uv1.y = (240.0f/256.0f); + uv2.x = (16.0f/256.0f)+uv1.x; + uv2.y = (16.0f/256.0f)+uv1.y; + + dp = 0.5f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + DrawIcon(pos, dim, uv1, uv2); +} + + +// Donne le texte à éditer. + +void CEdit::SetText(char *text, BOOL bNew) +{ + int i, j, font; + BOOL bBOL; + + if ( !bNew ) UndoMemorize(OPERUNDO_SPEC); + + m_len = strlen(text); + if ( m_len > m_maxChar ) m_len = m_maxChar; + + if ( m_format == 0 ) + { + if ( m_bAutoIndent ) + { + j = 0; + bBOL = TRUE; + for ( i=0 ; i max ) max = max-1; + + strncpy(buffer, m_text, max); + buffer[max] = 0; +} + +// Retourne la longueur du texte. + +int CEdit::RetTextLength() +{ + return m_len; +} + + + +// Retourne un nom dans une commande. +// \x nom1 nom2 nom3; + +void GetNameParam(char *cmd, int rank, char *buffer) +{ + int i; + + for ( i=0 ; iFreeTexture(filename); + } +} + +// Lit la texture d'une image. + +void CEdit::LoadImage(char *name) +{ + char filename[100]; + +//? sprintf(filename, "diagram\\%s.bmp", name); + UserDir(filename, name, "diagram"); + strcat(filename, ".bmp"); + m_engine->LoadTexture(filename); +} + +// Lit le contenu d'un fichier texte. + +BOOL CEdit::ReadText(char *filename, int addSize) +{ + FILE *file = NULL; + char *buffer; + int len, i, j, n, font, iIndex, iLines, iCount, iLink, res; + char iName[50]; + char text[50]; + float iWidth; + KeyRank key; + BOOL bInSoluce, bBOL; + + if ( filename[0] == 0 ) return FALSE; + file = fopen(filename, "rb"); + if ( file == NULL ) return FALSE; + + fseek(file, 0, SEEK_END); + len = ftell(file); + fseek(file, 0, SEEK_SET); + + m_maxChar = len+addSize+100; + m_len = len; + m_cursor1 = 0; + m_cursor2 = 0; + + FreeImage(); + delete m_text; + m_text = (char*)malloc(sizeof(char)*(m_maxChar+1)); + buffer = (char*)malloc(sizeof(char)*(m_maxChar+1)); + fread(buffer, 1, len, file); + + if ( m_format != 0 ) + { + delete m_format; + m_format = (char*)malloc(sizeof(char)*m_maxChar); + } + + fclose(file); + + bInSoluce = FALSE; + font = m_fontType; + iIndex = 0; + iLink = 0; + m_imageTotal = 0; + m_markerTotal = 0; + i = j = 0; + bBOL = TRUE; + while ( i < m_len ) + { + if ( m_bAutoIndent ) + { + if ( buffer[i] == '\t' ) + { + if ( !bBOL ) + { + m_text[j] = buffer[i]; + if ( m_format != 0 ) m_format[j] = font; + j ++; + } + i ++; + continue; // enlève les tabulateurs + } + bBOL = ( buffer[i] == '\n' || buffer[i] == '\r' ); + } + + if ( buffer[i] == '\r' ) // supprime les \r + { + i ++; + } + else if ( m_format != 0 && buffer[i] == '\\' && buffer[i+2] == ';' ) + { + if ( buffer[i+1] == 'n' ) // normal ? + { + if ( m_bSoluce || !bInSoluce ) + { + font &= ~FONT_MASK; + font |= FONT_COLOBOT; + } + i += 3; + } + else if ( buffer[i+1] == 'c' ) // cbot ? + { + if ( m_bSoluce || !bInSoluce ) + { + font &= ~FONT_MASK; + font |= FONT_COURIER; + } + i += 3; + } + else if ( buffer[i+1] == 'b' ) // big title ? + { + if ( m_bSoluce || !bInSoluce ) + { + font &= ~TITLE_MASK; + font |= TITLE_BIG; + } + i += 3; + } + else if ( buffer[i+1] == 't' ) // title ? + { + if ( m_bSoluce || !bInSoluce ) + { + font &= ~TITLE_MASK; + font |= TITLE_NORM; + } + i += 3; + } + else if ( buffer[i+1] == 's' ) // sbttl ? + { + if ( m_bSoluce || !bInSoluce ) + { + font &= ~TITLE_MASK; + font |= TITLE_LITTLE; + } + i += 3; + } + else if ( buffer[i+1] == 'l' ) // link ? + { + if ( m_bSoluce || !bInSoluce ) + { + font &= ~COLOR_MASK; + font |= COLOR_LINK; + } + i += 3; + } + else + { + i += 3; + } + } + else if ( m_format != 0 && + buffer[i+0] == '\\' && // \u nom marker; ? + buffer[i+1] == 'u' && + buffer[i+2] == ' ' ) + { + if ( m_bSoluce || !bInSoluce ) + { + if ( iLink < EDITLINKMAX ) + { + GetNameParam(buffer+i+3, 0, m_link[iLink].name); + GetNameParam(buffer+i+3, 1, m_link[iLink].marker); + iLink ++; + } + font &= ~COLOR_MASK; + } + i += strchr(buffer+i, ';')-(buffer+i)+1; + } + else if ( m_format != 0 && + buffer[i+0] == '\\' && // \m marker; ? + buffer[i+1] == 'm' && + buffer[i+2] == ' ' ) + { + if ( m_bSoluce || !bInSoluce ) + { + if ( m_markerTotal < EDITLINKMAX ) + { + GetNameParam(buffer+i+3, 0, m_marker[m_markerTotal].name); + m_marker[m_markerTotal].pos = j; + m_markerTotal ++; + } + } + i += strchr(buffer+i, ';')-(buffer+i)+1; + } + else if ( m_format != 0 && + buffer[i+0] == '\\' && // \image nom lx ly; ? + buffer[i+1] == 'i' && + buffer[i+2] == 'm' && + buffer[i+3] == 'a' && + buffer[i+4] == 'g' && + buffer[i+5] == 'e' && + buffer[i+6] == ' ' ) + { + if ( m_bSoluce || !bInSoluce ) + { +#if _DEMO + strcpy(iName, "demo"); +#else + GetNameParam(buffer+i+7, 0, iName); +#endif +//? iWidth = m_lineHeight*RetValueParam(buffer+i+7, 1); + iWidth = (float)RetValueParam(buffer+i+7, 1); + iWidth *= m_engine->RetText()->RetHeight(SMALLFONT, FONT_COLOBOT); + iLines = RetValueParam(buffer+i+7, 2); + LoadImage(iName); + + // Une tranche d'image par ligne de texte. + for ( iCount=0 ; iCountRetKey(key, 0); + if ( res != 0 ) + { + if ( GetResource(RES_KEY, res, iName) ) + { + m_text[j] = ' '; + m_format[j] = font; + j ++; + n = 0; + while ( iName[n] != 0 ) + { + m_text[j] = iName[n++]; + m_format[j] = font; + j ++; + } + m_text[j] = ' '; + m_format[j] = font; + j ++; + + res = m_engine->RetKey(key, 1); + if ( res != 0 ) + { + if ( GetResource(RES_KEY, res, iName) ) + { + GetResource(RES_TEXT, RT_KEY_OR, text); + n = 0; + while ( text[n] != 0 ) + { + m_text[j] = text[n++]; + m_format[j] = font&~COLOR_MASK; + j ++; + } + n = 0; + while ( iName[n] != 0 ) + { + m_text[j] = iName[n++]; + m_format[j] = font; + j ++; + } + m_text[j] = ' '; + m_format[j] = font; + j ++; + } + } + while ( buffer[i++] != ';' ); + continue; + } + } + } + m_text[j] = '?'; + m_format[j] = font; + j ++; + } + while ( buffer[i++] != ';' ); + } + else + { + if ( m_bSoluce || !bInSoluce ) + { + m_text[j] = buffer[i]; + if ( m_format != 0 ) m_format[j] = font; + j ++; + } + i ++; + + font &= ~TITLE_MASK; // reset title + + if ( (font&COLOR_MASK) == COLOR_TABLE ) + { + font &= ~COLOR_TABLE; + } + } + } + m_len = j; + m_imageTotal = iIndex; + + delete buffer; + + Justif(); + ColumnFix(); + return TRUE; +} + +// Ecrit tout le texte dans un fichier. + +BOOL CEdit::WriteText(char *filename) +{ + FILE* file; + char buffer[1000+20]; + int i, j, k, n; + float iDim; + + if ( filename[0] == 0 ) return FALSE; + file = fopen(filename, "wb"); + if ( file == NULL ) return FALSE; + + if ( m_bAutoIndent ) + { + iDim = m_dim.x; + m_dim.x = 1000.0f; // met une largeur infinie ! + Justif(); + } + + i = j = k = 0; + while ( m_text[i] != 0 && i < m_len ) + { + if ( m_bAutoIndent && i == m_lineOffset[k] ) + { + for ( n=0 ; n= 1000-1 ) + { + fwrite(buffer, 1, j, file); + j = 0; + } + + i ++; + } + if ( j > 0 ) + { + fwrite(buffer, 1, j, file); + } + + fclose(file); + + if ( m_bAutoIndent ) + { + m_dim.x = iDim; // remet la largeur initiale + Justif(); + } + + return TRUE; +} + + +// Gestion du nombre max de caractères éditables. + +void CEdit::SetMaxChar(int max) +{ + m_maxChar = max; + FreeImage(); + delete m_text; + m_text = (char*)malloc(sizeof(char)*(m_maxChar+1)); + + if ( m_format != 0 ) + { + delete m_format; + m_format = (char*)malloc(sizeof(char)*m_maxChar); + } + + m_len = 0; + m_cursor1 = 0; + m_cursor2 = 0; + Justif(); + UndoFlush(); +} + +int CEdit::RetMaxChar() +{ + return m_maxChar; +} + + +// Gestion du mode "éditable". + +void CEdit::SetEditCap(BOOL bMode) +{ + m_bEdit = bMode; +} + +BOOL CEdit::RetEditCap() +{ + return m_bEdit; +} + +// Gestion du mode "hilitable" (ça c'est du franch). + +void CEdit::SetHiliteCap(BOOL bEnable) +{ + m_bHilite = bEnable; +} + +BOOL CEdit::RetHiliteCap() +{ + return m_bHilite; +} + +// Ascenseur dans/hors cadre. + +void CEdit::SetInsideScroll(BOOL bInside) +{ + m_bInsideScroll = bInside; +} + +BOOL CEdit::RetInsideScroll() +{ + return m_bInsideScroll; +} + +// Indique s'il faut afficher les liens montrant la solution. + +void CEdit::SetSoluceMode(BOOL bSoluce) +{ + m_bSoluce = bSoluce; +} + +BOOL CEdit::RetSoluceMode() +{ + return m_bSoluce; +} + +// Indique si le texte est un générique qui défile. + +void CEdit::SetGenericMode(BOOL bGeneric) +{ + m_bGeneric = bGeneric; +} + +BOOL CEdit::RetGenericMode() +{ + return m_bGeneric; +} + + +// Gestion du mode d'indentation automatique avec { }. + +void CEdit::SetAutoIndent(BOOL bMode) +{ + m_bAutoIndent = bMode; +} + +BOOL CEdit::RetAutoIndent() +{ + return m_bAutoIndent; +} + + + +// Déplace les curseurs. + +void CEdit::SetCursor(int cursor1, int cursor2) +{ + if ( cursor1 > m_len ) cursor1 = m_len; + if ( cursor2 > m_len ) cursor2 = m_len; + + m_cursor1 = cursor1; + m_cursor2 = cursor2; + m_bUndoForce = TRUE; + ColumnFix(); +} + +// Retourne les curseurs. + +void CEdit::GetCursor(int &cursor1, int &cursor2) +{ + cursor1 = m_cursor1; + cursor2 = m_cursor2; +} + + +// Modifie la première ligne affichée. + +void CEdit::SetFirstLine(int rank) +{ + Scroll(rank, TRUE); +} + +// Retourne la première ligne affichée. + +int CEdit::RetFirstLine() +{ + if ( m_historyTotal > 0 ) + { + if ( m_historyCurrent == 0 ) + { + return m_lineFirst; + } + else + { + return m_history[0].firstLine; + } + } + return m_lineFirst; +} + + +// Montre la zone sélectionnée. + +void CEdit::ShowSelect() +{ + int cursor1, cursor2, line; + + if ( m_cursor1 < m_cursor2 ) + { + cursor1 = m_cursor1; + cursor2 = m_cursor2; + } + else + { + cursor1 = m_cursor2; + cursor2 = m_cursor1; + } + + line = RetCursorLine(cursor2); + if ( line >= m_lineFirst+m_lineVisible ) + { + line -= m_lineVisible-1; + if ( line < 0 ) line = 0; + Scroll(line, FALSE); + } + + line = RetCursorLine(cursor1); + if ( line < m_lineFirst ) + { + Scroll(line, FALSE); + } +} + + +// Gestion du mode d'affichage des caractères spéciaux. + +void CEdit::SetDisplaySpec(BOOL bDisplay) +{ + m_bDisplaySpec = bDisplay; +} + +BOOL CEdit::RetDisplaySpec() +{ + return m_bDisplaySpec; +} + + +// Gestion du mode multi-fontes. + +void CEdit::SetMultiFont(BOOL bMulti) +{ + if ( bMulti ) + { + delete m_format; + m_format = (char*)malloc(sizeof(char)*m_maxChar); + memset(m_format, 0, m_maxChar); + } + else + { + delete m_format; + m_format = 0; + } +} + +BOOL CEdit::RetMultiFont() +{ + return ( m_format != 0 ); +} + + +// Gestion de la taille des caractères. + +void CEdit::SetFontSize(float size) +{ + CControl::SetFontSize(size); + + MoveAdjust(); +} + + +// Déplace la partie visible selon l'ascenseur. + +void CEdit::Scroll() +{ + float value; + + if ( m_scroll != 0 ) + { + value = m_scroll->RetVisibleValue(); + value *= m_lineTotal-m_lineVisible; + Scroll((int)(value+0.5f), TRUE); + } +} + +// Déplace la partie visible selon l'ascenseur. + +void CEdit::Scroll(int pos, BOOL bAdjustCursor) +{ + int max, line; + + m_lineFirst = pos; + + if ( m_lineFirst < 0 ) m_lineFirst = 0; + + max = m_lineTotal-m_lineVisible; + if ( max < 0 ) max = 0; + if ( m_lineFirst > max ) m_lineFirst = max; + + line = RetCursorLine(m_cursor1); + + if ( bAdjustCursor && m_bEdit ) + { + // Curseur trop haut ? + if ( line < m_lineFirst ) + { + MoveLine(m_lineFirst-line, FALSE, FALSE); + return; + } + + // Curseur trop bas ? + if ( line >= m_lineFirst+m_lineVisible ) + { + MoveLine(m_lineFirst+m_lineVisible-line-1, FALSE, FALSE); + return; + } + } + + Justif(); +} + +// Déplace le curseur au début de la ligne. + +void CEdit::MoveHome(BOOL bWord, BOOL bSelect) +{ + int begin, tab; + + if ( bWord ) + { + m_cursor1 = 0; + } + else + { + begin = m_cursor1; + while ( begin > 0 && m_text[begin-1] != '\n' ) + { + begin --; + } + + tab = begin; + while ( tab < m_len && (m_text[tab] == '\t' || m_text[tab] == ' ') ) + { + tab ++; + } + + if ( m_cursor1 == tab ) + { + m_cursor1 = begin; + } + else + { + m_cursor1 = tab; + } + } + if ( !bSelect ) m_cursor2 = m_cursor1; + + m_bUndoForce = TRUE; + Justif(); + ColumnFix(); +} + +// Déplace le curseur à la fin de la ligne. + +void CEdit::MoveEnd(BOOL bWord, BOOL bSelect) +{ + if ( bWord ) + { + m_cursor1 = m_len; + } + else + { + while ( m_cursor1 < m_len && m_text[m_cursor1] != '\n' ) + { + m_cursor1 ++; + } + } + if ( !bSelect ) m_cursor2 = m_cursor1; + + m_bUndoForce = TRUE; + Justif(); + ColumnFix(); +} + +// Déplace le curseur par caractères. + +void CEdit::MoveChar(int move, BOOL bWord, BOOL bSelect) +{ + int character; + + if ( move == -1 ) // recule ? + { + if ( bWord ) + { + while ( m_cursor1 > 0 ) + { + character = (unsigned char)m_text[m_cursor1-1]; + if ( !IsSpace(character) ) break; + m_cursor1 --; + } + + if ( m_cursor1 > 0 ) + { + character = (unsigned char)m_text[m_cursor1-1]; + if ( IsSpace(character) ) + { + while ( m_cursor1 > 0 ) + { + character = (unsigned char)m_text[m_cursor1-1]; + if ( !IsSpace(character) ) break; + m_cursor1 --; + } + } + else if ( IsWord(character) ) + { + while ( m_cursor1 > 0 ) + { + character = (unsigned char)m_text[m_cursor1-1]; + if ( !IsWord(character) ) break; + m_cursor1 --; + } + } + else if ( IsSep(character) ) + { + while ( m_cursor1 > 0 ) + { + character = (unsigned char)m_text[m_cursor1-1]; + if ( !IsSep(character) ) break; + m_cursor1 --; + } + } + } + } + else + { + m_cursor1 --; + if ( m_cursor1 < 0 ) m_cursor1 = 0; + } + } + + if ( move == 1 ) // avance ? + { + if ( bWord ) + { + if ( m_cursor1 < m_len ) + { + character = (unsigned char)m_text[m_cursor1]; + if ( IsSpace(character) ) + { + while ( m_cursor1 < m_len ) + { + character = (unsigned char)m_text[m_cursor1]; + if ( !IsSpace(character) ) break; + m_cursor1 ++; + } + } + else if ( IsWord(character) ) + { + while ( m_cursor1 < m_len ) + { + character = (unsigned char)m_text[m_cursor1]; + if ( !IsWord(character) ) break; + m_cursor1 ++; + } + } + else if ( IsSep(character) ) + { + while ( m_cursor1 < m_len ) + { + character = (unsigned char)m_text[m_cursor1]; + if ( !IsSep(character) ) break; + m_cursor1 ++; + } + } + } + + while ( m_cursor1 < m_len ) + { + character = (unsigned char)m_text[m_cursor1]; + if ( !IsSpace(character) ) break; + m_cursor1 ++; + } + } + else + { + m_cursor1 ++; + if ( m_cursor1 > m_len ) m_cursor1 = m_len; + } + } + + if ( !bSelect ) m_cursor2 = m_cursor1; + + m_bUndoForce = TRUE; + Justif(); + ColumnFix(); +} + +// Déplace le curseur par lignes. + +void CEdit::MoveLine(int move, BOOL bWord, BOOL bSelect) +{ + float column, indentLength; + int i, line, c; + + if ( move == 0 ) return; + + for ( i=0 ; i>move ; i-- ) // recule ? + { + while ( m_cursor1 > 0 && m_text[m_cursor1-1] != '\n' ) + { + m_cursor1 --; + } + if ( m_cursor1 != 0 ) + { + m_cursor1 --; + while ( m_cursor1 > 0 ) + { + if ( m_text[--m_cursor1] == '\n' ) + { + m_cursor1 ++; + break; + } + } + } + } + + for ( i=0 ; iRetText()->RetCharWidth(' ', 0.0f, m_fontSize, m_fontStretch, m_fontType) + * m_engine->RetEditIndentValue(); + column -= indentLength*m_lineIndent[line]; + } + + if ( m_format == 0 ) + { + c = m_engine->RetText()->Detect(m_text+m_lineOffset[line], + m_lineOffset[line+1]-m_lineOffset[line], + column, m_fontSize, + m_fontStretch, m_fontType); + } + else + { + c = m_engine->RetText()->Detect(m_text+m_lineOffset[line], + m_format+m_lineOffset[line], + m_lineOffset[line+1]-m_lineOffset[line], + column, m_fontSize, m_fontStretch); + } + + m_cursor1 = m_lineOffset[line]+c; + if ( !bSelect ) m_cursor2 = m_cursor1; + + m_bUndoForce = TRUE; + Justif(); +} + +// Fixe la position horizontale. + +void CEdit::ColumnFix() +{ + float indentLength; + int line; + + line = RetCursorLine(m_cursor1); + + if ( m_format == 0 ) + { + m_column = m_engine->RetText()->RetStringWidth + ( + m_text+m_lineOffset[line], + m_cursor1-m_lineOffset[line], + m_fontSize, m_fontStretch, m_fontType + ); + } + else + { + m_column = m_engine->RetText()->RetStringWidth + ( + m_text+m_lineOffset[line], + m_format+m_lineOffset[line], + m_cursor1-m_lineOffset[line], + m_fontSize, m_fontStretch + ); + } + + if ( m_bAutoIndent ) + { + indentLength = m_engine->RetText()->RetCharWidth(' ', 0.0f, m_fontSize, m_fontStretch, m_fontType) + * m_engine->RetEditIndentValue(); + m_column += indentLength*m_lineIndent[line]; + } +} + + +// Coupe les caractères sélectionnés, ou toute la ligne. + +BOOL CEdit::Cut() +{ + HGLOBAL hg; + char* text; + char c; + int c1, c2, start, len, i, j; + + if ( !m_bEdit ) return FALSE; + + c1 = m_cursor1; + c2 = m_cursor2; + if ( c1 > c2 ) Swap(c1, c2); // toujours c1 <= c2 + + if ( c1 == c2 ) + { + while ( c1 > 0 ) + { + if ( m_text[c1-1] == '\n' ) break; + c1 --; + } + while ( c2 < m_len ) + { + c2 ++; + if ( m_text[c2-1] == '\n' ) break; + } + } + + if ( c1 == c2 ) return FALSE; + + start = c1; + len = c2-c1; + + if ( !(hg = GlobalAlloc(GMEM_DDESHARE, len*2+1)) ) + { + return FALSE; + } + if ( !(text = (char*)GlobalLock(hg)) ) + { + GlobalFree(hg); + return FALSE; + } + + j = 0; + for ( i=start ; i c2 ) Swap(c1, c2); // toujours c1 <= c2 + + if ( c1 == c2 ) + { + while ( c1 > 0 ) + { + if ( m_text[c1-1] == '\n' ) break; + c1 --; + } + while ( c2 < m_len ) + { + c2 ++; + if ( m_text[c2-1] == '\n' ) break; + } + } + + if ( c1 == c2 ) return FALSE; + + start = c1; + len = c2-c1; + + if ( !(hg = GlobalAlloc(GMEM_DDESHARE, len*2+1)) ) + { + return FALSE; + } + if ( !(text = (char*)GlobalLock(hg)) ) + { + GlobalFree(hg); + return FALSE; + } + + j = 0; + for ( i=start ; iRetEditIndentValue() ; i++ ) + { + InsertOne(' '); + } + } + else + { + InsertOne(character); + } + } + else + { + InsertOne(character); + } + + Justif(); + ColumnFix(); +} + +// Insère un caractère brut. + +void CEdit::InsertOne(char character) +{ + int i; + + if ( !m_bEdit ) return; + if ( !m_bMulti && character == '\n' ) return; + + if ( m_cursor1 != m_cursor2 ) + { + DeleteOne(0); // supprime les caractères sélectionnés + } + + if ( m_len >= m_maxChar ) return; + + for ( i=m_len ; i>=m_cursor1 ; i-- ) + { + m_text[i] = m_text[i-1]; // pousse + + if ( m_format != 0 ) + { + m_format[i] = m_format[i-1]; // pousse + } + } + + m_len ++; + + m_text[m_cursor1] = character; + + if ( m_format != 0 ) + { + m_format[m_cursor1] = 0; + } + + m_cursor1++; + m_cursor2 = m_cursor1; +} + +// Supprime le caractère à gauche du curseur ou tous les +// caractères sélectionnés. + +void CEdit::Delete(int dir) +{ + if ( !m_bEdit ) return; + + UndoMemorize(OPERUNDO_DELETE); + DeleteOne(dir); + + Justif(); + ColumnFix(); +} + +// Supprime le caractère à gauche du curseur ou tous les +// caractères sélectionnés brut. + +void CEdit::DeleteOne(int dir) +{ + int i, end, hole; + + if ( !m_bEdit ) return; + + if ( m_cursor1 == m_cursor2 ) + { + if ( dir < 0 ) + { + if ( m_cursor1 == 0 ) return; + m_cursor1 --; + } + else + { + if ( m_cursor2 == m_len ) return; + m_cursor2 ++; + } + } + + if ( m_cursor1 > m_cursor2 ) Swap(m_cursor1, m_cursor2); + hole = m_cursor2-m_cursor1; + end = m_len-hole; + for ( i=m_cursor1 ; i 0 ) + { + if ( m_text[i-1] == '\n' ) return nb; + if ( m_text[i-1] != '\t' ) return -1; + nb ++; + i --; + } + return nb; +} + +// Ajoute ou supprime qq tabulateurs. + +void CEdit::IndentTabAdjust(int number) +{ + int i; + + for ( i=0 ; inumber ; i-- ) // supprime ? + { + DeleteOne(-1); + } +} + + +// Indente à gauche ou à droite toute la sélection. + +BOOL CEdit::Shift(BOOL bLeft) +{ + BOOL bInvert = FALSE; + int c1, c2, i; + + if ( m_cursor1 == m_cursor2 ) return FALSE; + + UndoMemorize(OPERUNDO_SPEC); + + c1 = m_cursor1; + c2 = m_cursor2; + if ( c1 > c2 ) + { + Swap(c1, c2); // toujours c1 <= c2 + bInvert = TRUE; + } + + if ( c1 > 0 ) + { + if ( m_text[c1-1] != '\n' ) return FALSE; + } + if ( c2 < m_len ) + { + if ( m_text[c2-1] != '\n' ) return FALSE; + } + + if ( bLeft ) // décale à gauche ? + { + i = c1; + while ( i < c2 ) + { + if ( m_text[i] == '\t' ) + { + m_cursor1 = i; + m_cursor2 = i+1; + DeleteOne(0); + c2 --; + } + while ( i < c2 && m_text[i++] != '\n' ); + } + } + else // décale à droite ? + { + i = c1; + while ( i < c2 ) + { + m_cursor1 = m_cursor2 = i; + InsertOne('\t'); + c2 ++; + while ( i < c2 && m_text[i++] != '\n' ); + } + } + + if ( bInvert ) Swap(c1, c2); + m_cursor1 = c1; + m_cursor2 = c2; + + Justif(); + ColumnFix(); + SendModifEvent(); + return TRUE; +} + +// Conversion min <-> maj de la sélection. + +BOOL CEdit::MinMaj(BOOL bMaj) +{ + int c1, c2, i, character; + + if ( m_cursor1 == m_cursor2 ) return FALSE; + + UndoMemorize(OPERUNDO_SPEC); + + c1 = m_cursor1; + c2 = m_cursor2; + if ( c1 > c2 ) Swap(c1, c2); // toujours c1 <= c2 + + for ( i=c1 ; iRetText()->RetCharWidth(' ', 0.0f, m_fontSize, m_fontStretch, m_fontType) + * m_engine->RetEditIndentValue(); + } + + bString = bRem = FALSE; + i = 0; + while ( TRUE ) + { + bDual = FALSE; + + width = m_dim.x-(10.0f/640.0f)*2.0f-(m_bMulti?MARGX*2.0f+SCROLL_WIDTH:0.0f); + if ( m_bAutoIndent ) + { + width -= indentLength*m_lineIndent[m_lineTotal-1]; + } + + if ( m_format == 0 ) + { + i += m_engine->RetText()->Justif(m_text+i, m_len-i, width, + m_fontSize, m_fontStretch, + m_fontType); + } + else + { + size = m_fontSize; + + if ( (m_format[i]&TITLE_MASK) == TITLE_BIG ) // grand titre ? + { + size *= BIG_FONT; + bDual = TRUE; + } + + if ( (m_format[i]&IMAGE_MASK) != 0 ) // tranche d'image ? + { + i ++; // saute juste un caractère (index dans m_image) + } + else + { + i += m_engine->RetText()->Justif(m_text+i, m_format+i, + m_len-i, width, + size, m_fontStretch); + } + } + + if ( i >= m_len ) break; + + if ( m_bAutoIndent ) + { + for ( j=m_lineOffset[m_lineTotal-1] ; j= EDITLINEMAX-2 ) break; + } + if ( m_len > 0 && m_text[m_len-1] == '\n' ) + { + m_lineOffset[m_lineTotal] = m_len; + m_lineIndent[m_lineTotal] = 0; + m_lineTotal ++; + } + m_lineOffset[m_lineTotal] = m_len; + m_lineIndent[m_lineTotal] = 0; + + if ( m_bAutoIndent ) + { + for ( i=0 ; i<=m_lineTotal ; i++ ) + { + if ( m_text[m_lineOffset[i]] == '}' ) + { + if ( m_lineIndent[i] > 0 ) m_lineIndent[i] --; + } + } + } + + if ( m_bMulti ) + { + if ( m_bEdit ) + { + line = RetCursorLine(m_cursor1); + if ( line < m_lineFirst ) + { + m_lineFirst = line; + } + if ( line >= m_lineFirst+m_lineVisible ) + { + m_lineFirst = line-m_lineVisible+1; + } + } + } + else + { + m_lineFirst = 0; + } + + if ( m_scroll != 0 ) + { + if ( m_lineTotal <= m_lineVisible ) + { + m_scroll->SetVisibleRatio(1.0f); + m_scroll->SetVisibleValue(0.0f); + m_scroll->SetArrowStep(0.0f); + } + else + { + value = (float)m_lineVisible/m_lineTotal; + m_scroll->SetVisibleRatio(value); + + value = (float)m_lineFirst/(m_lineTotal-m_lineVisible); + m_scroll->SetVisibleValue(value); + + value = (float)1.0f/(m_lineTotal-m_lineVisible); + m_scroll->SetArrowStep(value); + } + } + + m_timeBlink = 0.0f; // allume le curseur immédiatement +} + +// Retourne le rang de la ligne dans laquelle se trouve le curseur. + +int CEdit::RetCursorLine(int cursor) +{ + int line, i; + + line = 0; + for ( i=0 ; i= m_lineOffset[i] ) + { + line = i; + } + } + return line; +} + + +// Vide le buffer undo. + +void CEdit::UndoFlush() +{ + int i; + + for ( i=0 ; i=1 ; i-- ) + { + m_undo[i] = m_undo[i-1]; + } + + len = m_len; + if ( len == 0 ) len ++; + m_undo[0].text = (char*)malloc(sizeof(char)*(len+1)); + memcpy(m_undo[0].text, m_text, m_len); + m_undo[0].len = m_len; + + m_undo[0].cursor1 = m_cursor1; + m_undo[0].cursor2 = m_cursor2; + m_undo[0].lineFirst = m_lineFirst; +} + +// Revient à l'état précédent. + +BOOL CEdit::UndoRecall() +{ + int i; + + if ( m_undo[0].text == 0 ) return FALSE; + + m_len = m_undo[0].len; + memcpy(m_text, m_undo[0].text, m_len); + + m_cursor1 = m_undo[0].cursor1; + m_cursor2 = m_undo[0].cursor2; + m_lineFirst = m_undo[0].lineFirst; + + for ( i=0 ; i multi lignes + BOOL m_bEdit; // TRUE -> éditable + BOOL m_bHilite; // TRUE -> hilitable + BOOL m_bInsideScroll; // TRUE -> ascenseur dans le cadre + BOOL m_bDisplaySpec; // TRUE -> affiche les caractères spéciaux + BOOL m_bMultiFont; // TRUE -> plusieurs fontes possible + BOOL m_bSoluce; // TRUE -> montre les liens-solution + BOOL m_bGeneric; // TRUE -> générique qui défile + BOOL m_bAutoIndent; // TRUE -> indentation automatique + float m_lineHeight; // hauteur d'une ligne + float m_lineAscent; // hauteur au-dessus de la ligne de base + float m_lineDescent; // hauteur au-dessous de la ligne de base + int m_lineVisible; // nb total de ligne affichables + int m_lineFirst; // première ligne affichée + int m_lineTotal; // nb lignes utilisées (ds m_lineOffset) + int m_lineOffset[EDITLINEMAX]; + char m_lineIndent[EDITLINEMAX]; + int m_imageTotal; + ImageLine m_image[EDITIMAGEMAX]; + HyperLink m_link[EDITLINKMAX]; + int m_markerTotal; + HyperMarker m_marker[EDITLINKMAX]; + int m_historyTotal; + int m_historyCurrent; + HyperHistory m_history[EDITHISTORYMAX]; + float m_time; // temps absolu + float m_timeBlink; + float m_timeLastClick; + float m_timeLastScroll; + FPOINT m_mouseFirstPos; + FPOINT m_mouseLastPos; + float m_column; + + BOOL m_bCapture; + + BOOL m_bUndoForce; + OperUndo m_undoOper; + EditUndo m_undo[EDITUNDOMAX]; +}; + + +#endif //_EDIT_H_ diff --git a/src/editvalue.cpp b/src/editvalue.cpp new file mode 100644 index 00000000..2a881ed2 --- /dev/null +++ b/src/editvalue.cpp @@ -0,0 +1,368 @@ +// editvalue.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "edit.h" +#include "button.h" +#include "editvalue.h" + + + + +// Constructeur de l'objet. + +CEditValue::CEditValue(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); + + m_edit = 0; + m_buttonUp = 0; + m_buttonDown = 0; + + m_type = EVT_100; // % + m_stepValue = 0.1f; // 10% + m_minValue = 0.0f; // 0% + m_maxValue = 1.0f; // 100% +} + +// Destructeur de l'objet. + +CEditValue::~CEditValue() +{ + delete m_edit; + delete m_buttonUp; + delete m_buttonDown; + + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CEditValue::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CEdit* pe; + CButton* pc; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + CControl::Create(pos, dim, icon, eventMsg); + + GlintDelete(); + + m_edit = new CEdit(m_iMan); + pe = (CEdit*)m_edit; + pe->Create(pos, dim, 0, EVENT_NULL); + pe->SetMaxChar(4); + + m_buttonUp = new CButton(m_iMan); + pc = (CButton*)m_buttonUp; + pc->Create(pos, dim, 49, EVENT_NULL); // ^ + pc->SetRepeat(TRUE); + + m_buttonDown = new CButton(m_iMan); + pc = (CButton*)m_buttonDown; + pc->Create(pos, dim, 50, EVENT_NULL); // v + pc->SetRepeat(TRUE); + + MoveAdjust(); + return TRUE; +} + + +void CEditValue::SetPos(FPOINT pos) +{ + CControl::SetPos(pos); + MoveAdjust(); +} + +void CEditValue::SetDim(FPOINT dim) +{ + CControl::SetDim(dim); + MoveAdjust(); +} + +void CEditValue::MoveAdjust() +{ + FPOINT pos, dim; + + if ( m_edit != 0 ) + { + pos.x = m_pos.x; + pos.y = m_pos.y; + dim.x = m_dim.x-m_dim.y*0.6f; + dim.y = m_dim.y; + m_edit->SetPos(pos); + m_edit->SetDim(dim); + } + + if ( m_buttonUp != 0 ) + { + pos.x = m_pos.x+m_dim.x-m_dim.y*0.6f; + pos.y = m_pos.y+m_dim.y*0.5f; + dim.x = m_dim.y*0.6f; + dim.y = m_dim.y*0.5f; + m_buttonUp->SetPos(pos); + m_buttonUp->SetDim(dim); + } + + if ( m_buttonDown != 0 ) + { + pos.x = m_pos.x+m_dim.x-m_dim.y*0.6f; + pos.y = m_pos.y; + dim.x = m_dim.y*0.6f; + dim.y = m_dim.y*0.5f; + m_buttonDown->SetPos(pos); + m_buttonDown->SetDim(dim); + } +} + + +// Gestion d'un événement. + +BOOL CEditValue::EventProcess(const Event &event) +{ + float value; + + CControl::EventProcess(event); + + if ( (m_state & STATE_VISIBLE) == 0 ) return TRUE; + if ( (m_state & STATE_ENABLE) == 0 ) return TRUE; + + if ( m_edit != 0 ) + { + if ( m_edit->RetFocus() && + event.event == EVENT_KEYDOWN && + event.param == VK_RETURN ) + { + value = RetValue(); + if ( value > m_maxValue ) value = m_maxValue; + if ( value < m_minValue ) value = m_minValue; + SetValue(value, TRUE); + HiliteValue(event); + } + if ( !m_edit->EventProcess(event) ) return FALSE; + + if ( event.event == m_edit->RetEventMsg() ) + { + Event newEvent; + m_event->MakeEvent(newEvent, m_eventMsg); + m_event->AddEvent(newEvent); + } + } + + if ( m_buttonUp != 0 ) + { + if ( event.event == m_buttonUp->RetEventMsg() ) + { + value = RetValue()+m_stepValue; + if ( value > m_maxValue ) value = m_maxValue; + SetValue(value, TRUE); + HiliteValue(event); + } + if ( !m_buttonUp->EventProcess(event) ) return FALSE; + } + + if ( m_buttonDown != 0 ) + { + if ( event.event == m_buttonDown->RetEventMsg() ) + { + value = RetValue()-m_stepValue; + if ( value < m_minValue ) value = m_minValue; + SetValue(value, TRUE); + HiliteValue(event); + } + if ( !m_buttonDown->EventProcess(event) ) return FALSE; + } + + if ( event.event == EVENT_KEYDOWN && + event.param == VK_WHEELUP && + Detect(event.pos) ) + { + value = RetValue()+m_stepValue; + if ( value > m_maxValue ) value = m_maxValue; + SetValue(value, TRUE); + HiliteValue(event); + } + if ( event.event == EVENT_KEYDOWN && + event.param == VK_WHEELDOWN && + Detect(event.pos) ) + { + value = RetValue()-m_stepValue; + if ( value < m_minValue ) value = m_minValue; + SetValue(value, TRUE); + HiliteValue(event); + } + + return TRUE; +} + + +// Met en évidence la valeur éditée. + +void CEditValue::HiliteValue(const Event &event) +{ + int pos; + + if ( m_edit == 0 ) return; + + pos = m_edit->RetTextLength(); + if ( m_type == EVT_100 && pos > 0 ) + { + pos --; // ne sélectionne pas le "%" + } + + m_edit->SetCursor(pos, 0); + m_edit->SetFocus(TRUE); + + Event newEvent = event; + newEvent.event = EVENT_FOCUS; + newEvent.param = m_edit->RetEventMsg(); + m_event->AddEvent(newEvent); // défocus les autres objets +} + + +// Dessine le bouton. + +void CEditValue::Draw() +{ + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + if ( m_state & STATE_SHADOW ) + { + DrawShadow(m_pos, m_dim); + } + + if ( m_edit != 0 ) + { + m_edit->Draw(); + } + if ( m_buttonUp != 0 ) + { + m_buttonUp->Draw(); + } + if ( m_buttonDown != 0 ) + { + m_buttonDown->Draw(); + } +} + + +// Choix du type de la valeur. + +void CEditValue::SetType(EditValueType type) +{ + m_type = type; +} + +EditValueType CEditValue::RetType() +{ + return m_type; +} + + +// Modifie la valeur. + +void CEditValue::SetValue(float value, BOOL bSendMessage) +{ + char text[100]; + + if ( m_edit == 0 ) return; + + text[0] = 0; + + if ( m_type == EVT_INT ) + { + sprintf(text, "%d", (int)value); + } + + if ( m_type == EVT_FLOAT ) + { + sprintf(text, "%.2f", value); + } + + if ( m_type == EVT_100 ) + { + sprintf(text, "%d%%", (int)(value*100.0f)); + } + + m_edit->SetText(text); + + if ( bSendMessage ) + { + Event newEvent; + m_event->MakeEvent(newEvent, m_eventMsg); + m_event->AddEvent(newEvent); + } +} + +// Retourne la valeur éditée. + +float CEditValue::RetValue() +{ + char text[100]; + float value; + + if ( m_edit == 0 ) 0.0f; + + m_edit->GetText(text, 100); + sscanf(text, "%f", &value); + + if ( m_type == EVT_100 ) + { + value = (value+0.5f)/100.0f; + if ( value < 0.01f ) value = 0.0f; // moins que 1% ? + } + + return value; +} + + +// Gestion du pas pour les boutons. + +void CEditValue::SetStepValue(float value) +{ + m_stepValue = value; +} + +float CEditValue::RetStepValue() +{ + return m_stepValue; +} + + +// Gestion de la valeur minimale. + +void CEditValue::SetMinValue(float value) +{ + m_minValue = value; +} + +float CEditValue::RetMinValue() +{ + return m_minValue; +} + + +// Gestion de la valeur maximale. + +void CEditValue::SetMaxValue(float value) +{ + m_maxValue = value; +} + +float CEditValue::RetMaxValue() +{ + return m_maxValue; +} + diff --git a/src/editvalue.h b/src/editvalue.h new file mode 100644 index 00000000..bef6f001 --- /dev/null +++ b/src/editvalue.h @@ -0,0 +1,69 @@ +// editvalue.h + +#ifndef _EDITVALUE_H_ +#define _EDITVALUE_H_ + + +#include "control.h" + + +enum EditValueType +{ + EVT_INT = 1, // valeur entière + EVT_FLOAT = 2, // valeur réelle + EVT_100 = 3, // pour-cent (0..1) +}; + + +class CD3DEngine; +class CEdit; +class CButton; + + + +class CEditValue : public CControl +{ +public: + CEditValue(CInstanceManager* iMan); + ~CEditValue(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + void SetPos(FPOINT pos); + void SetDim(FPOINT dim); + + BOOL EventProcess(const Event &event); + void Draw(); + + void SetType(EditValueType type); + EditValueType RetType(); + + void SetValue(float value, BOOL bSendMessage=FALSE); + float RetValue(); + + void SetStepValue(float value); + float RetStepValue(); + + void SetMinValue(float value); + float RetMinValue(); + + void SetMaxValue(float value); + float RetMaxValue(); + +protected: + void MoveAdjust(); + void HiliteValue(const Event &event); + +protected: + CEdit* m_edit; + CButton* m_buttonUp; + CButton* m_buttonDown; + + EditValueType m_type; + float m_stepValue; + float m_minValue; + float m_maxValue; +}; + + +#endif //_EDITVALUE_H_ diff --git a/src/event.cpp b/src/event.cpp new file mode 100644 index 00000000..b3480e05 --- /dev/null +++ b/src/event.cpp @@ -0,0 +1,75 @@ +// event.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include + +#include "struct.h" +#include "iman.h" +#include "event.h" + + + + +// Constructeur de l'objet. + +CEvent::CEvent(CInstanceManager* iMan) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_EVENT, this); + + Flush(); +} + +// Destructeur de l'objet. + +CEvent::~CEvent() +{ +} + + +// Vide le fifo des événements. + +void CEvent::Flush() +{ + m_head = 0; + m_tail = 0; + m_total = 0; +} + +// Fabrique un événement. + +void CEvent::MakeEvent(Event &event, EventMsg msg) +{ + ZeroMemory(&event, sizeof(Event)); + event.event = msg; +} + +// Ajoute un événement dans le fifo. + +BOOL CEvent::AddEvent(const Event &event) +{ + if ( m_total >= MAXEVENT ) return FALSE; + + m_fifo[m_head++] = event; + if ( m_head >= MAXEVENT ) m_head = 0; + m_total ++; + + return TRUE; +} + +// Enlève un événement du fifo. + +BOOL CEvent::GetEvent(Event &event) +{ + if ( m_head == m_tail ) return FALSE; + + event = m_fifo[m_tail++]; + if ( m_tail >= MAXEVENT ) m_tail = 0; + m_total --; + + return TRUE; +} + diff --git a/src/event.h b/src/event.h new file mode 100644 index 00000000..c9877db2 --- /dev/null +++ b/src/event.h @@ -0,0 +1,617 @@ +// event.h + +#ifndef _EVENT_H_ +#define _EVENT_H_ + + +#if !defined (WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + + + +class CInstanceManager; + + +#define MAXEVENT 100 + +// Evénements. + +enum EventMsg +{ + EVENT_NULL = 0, + + EVENT_QUIT = 1, + EVENT_FRAME = 2, + EVENT_LBUTTONDOWN = 3, + EVENT_RBUTTONDOWN = 4, + EVENT_LBUTTONUP = 5, + EVENT_RBUTTONUP = 6, + EVENT_MOUSEMOVE = 7, + EVENT_KEYDOWN = 8, + EVENT_KEYUP = 9, + EVENT_CHAR = 10, + EVENT_FOCUS = 11, + + EVENT_UPDINTERFACE = 20, + EVENT_WIN = 30, + EVENT_LOST = 31, + + EVENT_BUTTON_OK = 40, + EVENT_BUTTON_CANCEL = 41, + EVENT_BUTTON_NEXT = 42, + EVENT_BUTTON_PREV = 43, + EVENT_BUTTON_QUIT = 44, + + EVENT_BUTTON0 = 50, + EVENT_BUTTON1 = 51, + EVENT_BUTTON2 = 52, + EVENT_BUTTON3 = 53, + EVENT_BUTTON4 = 54, + EVENT_BUTTON5 = 55, + EVENT_BUTTON6 = 56, + EVENT_BUTTON7 = 57, + EVENT_BUTTON8 = 58, + EVENT_BUTTON9 = 59, + EVENT_BUTTON10 = 60, + EVENT_BUTTON11 = 61, + EVENT_BUTTON12 = 62, + EVENT_BUTTON13 = 63, + EVENT_BUTTON14 = 64, + EVENT_BUTTON15 = 65, + EVENT_BUTTON16 = 66, + EVENT_BUTTON17 = 67, + EVENT_BUTTON18 = 68, + EVENT_BUTTON19 = 69, + + EVENT_EDIT0 = 70, + EVENT_EDIT1 = 71, + EVENT_EDIT2 = 72, + EVENT_EDIT3 = 73, + EVENT_EDIT4 = 74, + EVENT_EDIT5 = 75, + EVENT_EDIT6 = 76, + EVENT_EDIT7 = 77, + EVENT_EDIT8 = 78, + EVENT_EDIT9 = 79, + + EVENT_WINDOW0 = 80, // tableau de bord en bas + EVENT_WINDOW1 = 81, // map + EVENT_WINDOW2 = 82, // CDisplayText + EVENT_WINDOW3 = 83, // CStudio + EVENT_WINDOW4 = 84, // DisplayInfo + EVENT_WINDOW5 = 85, // setup + EVENT_WINDOW6 = 86, + EVENT_WINDOW7 = 87, + EVENT_WINDOW8 = 88, + EVENT_WINDOW9 = 89, // dialogue + + EVENT_LABEL0 = 90, + EVENT_LABEL1 = 91, + EVENT_LABEL2 = 92, + EVENT_LABEL3 = 93, + EVENT_LABEL4 = 94, + EVENT_LABEL5 = 95, + EVENT_LABEL6 = 96, + EVENT_LABEL7 = 97, + EVENT_LABEL8 = 98, + EVENT_LABEL9 = 99, + EVENT_LABEL10 = 100, + EVENT_LABEL11 = 101, + EVENT_LABEL12 = 102, + EVENT_LABEL13 = 103, + EVENT_LABEL14 = 104, + EVENT_LABEL15 = 105, + EVENT_LABEL16 = 106, + EVENT_LABEL17 = 107, + EVENT_LABEL18 = 108, + EVENT_LABEL19 = 109, + + EVENT_LIST0 = 110, + EVENT_LIST1 = 111, + EVENT_LIST2 = 112, + EVENT_LIST3 = 113, + EVENT_LIST4 = 114, + EVENT_LIST5 = 115, + EVENT_LIST6 = 116, + EVENT_LIST7 = 117, + EVENT_LIST8 = 118, + EVENT_LIST9 = 119, + + EVENT_TOOLTIP = 200, + + EVENT_DIALOG_OK = 300, + EVENT_DIALOG_CANCEL = 301, + EVENT_DIALOG_LABEL = 302, + EVENT_DIALOG_LABEL1 = 303, + EVENT_DIALOG_LABEL2 = 304, + EVENT_DIALOG_LABEL3 = 305, + EVENT_DIALOG_LIST = 306, + EVENT_DIALOG_EDIT = 307, + EVENT_DIALOG_CHECK1 = 308, + EVENT_DIALOG_CHECK2 = 309, + + EVENT_INTERFACE_TRAINER = 400, + EVENT_INTERFACE_DEFI = 401, + EVENT_INTERFACE_MISSION = 402, + EVENT_INTERFACE_FREE = 403, + EVENT_INTERFACE_PROTO = 404, + EVENT_INTERFACE_NAME = 405, + EVENT_INTERFACE_SETUP = 406, + EVENT_INTERFACE_QUIT = 407, + EVENT_INTERFACE_BACK = 408, + EVENT_INTERFACE_AGAIN = 409, + EVENT_INTERFACE_WRITE = 410, + EVENT_INTERFACE_READ = 411, + EVENT_INTERFACE_ABORT = 412, + EVENT_INTERFACE_USER = 413, + EVENT_INTERFACE_TEEN = 414, + + EVENT_INTERFACE_CHAP = 420, + EVENT_INTERFACE_LIST = 421, + EVENT_INTERFACE_RESUME = 422, + EVENT_INTERFACE_PLAY = 423, + + EVENT_INTERFACE_SETUPd = 430, + EVENT_INTERFACE_SETUPg = 431, + EVENT_INTERFACE_SETUPp = 432, + EVENT_INTERFACE_SETUPc = 433, + EVENT_INTERFACE_SETUPs = 434, + + EVENT_INTERFACE_DEVICE = 440, + EVENT_INTERFACE_RESOL = 441, + EVENT_INTERFACE_FULL = 442, + EVENT_INTERFACE_APPLY = 443, + + EVENT_INTERFACE_TOTO = 450, + EVENT_INTERFACE_SHADOW = 451, + EVENT_INTERFACE_DIRTY = 452, + EVENT_INTERFACE_LENS = 453, + EVENT_INTERFACE_SKY = 454, + EVENT_INTERFACE_PLANET = 456, + EVENT_INTERFACE_LIGHT = 457, + EVENT_INTERFACE_PARTI = 458, + EVENT_INTERFACE_CLIP = 459, + EVENT_INTERFACE_DETAIL = 460, + EVENT_INTERFACE_TEXTURE = 461, + EVENT_INTERFACE_RAIN = 462, + EVENT_INTERFACE_GLINT = 463, + EVENT_INTERFACE_TOOLTIP = 464, + EVENT_INTERFACE_MOVIES = 465, + EVENT_INTERFACE_NICERST = 466, + EVENT_INTERFACE_SCROLL = 467, + EVENT_INTERFACE_INVERTX = 468, + EVENT_INTERFACE_INVERTY = 469, + EVENT_INTERFACE_EFFECT = 470, + EVENT_INTERFACE_MOUSE = 471, + EVENT_INTERFACE_GROUND = 472, + EVENT_INTERFACE_GADGET = 473, + EVENT_INTERFACE_FOG = 474, + EVENT_INTERFACE_HIMSELF = 475, + EVENT_INTERFACE_EDITMODE= 476, + EVENT_INTERFACE_EDITVALUE= 477, + EVENT_INTERFACE_SOLUCE4 = 478, + + EVENT_INTERFACE_KINFO1 = 500, + EVENT_INTERFACE_KINFO2 = 501, + EVENT_INTERFACE_KGROUP = 502, + EVENT_INTERFACE_KSCROLL = 503, + EVENT_INTERFACE_KDEF = 504, + EVENT_INTERFACE_KLEFT = 505, + EVENT_INTERFACE_KRIGHT = 506, + EVENT_INTERFACE_KUP = 507, + EVENT_INTERFACE_KDOWN = 508, + EVENT_INTERFACE_KGUP = 509, + EVENT_INTERFACE_KGDOWN = 510, + EVENT_INTERFACE_KCAMERA = 511, + EVENT_INTERFACE_KDESEL = 512, + EVENT_INTERFACE_KACTION = 513, + EVENT_INTERFACE_KNEAR = 514, + EVENT_INTERFACE_KAWAY = 515, + EVENT_INTERFACE_KNEXT = 516, + EVENT_INTERFACE_KHUMAN = 517, + EVENT_INTERFACE_KQUIT = 518, + EVENT_INTERFACE_KHELP = 519, + EVENT_INTERFACE_KPROG = 520, + EVENT_INTERFACE_KCBOT = 521, + EVENT_INTERFACE_KVISIT = 522, + EVENT_INTERFACE_KSPEED10= 523, + EVENT_INTERFACE_KSPEED15= 524, + EVENT_INTERFACE_KSPEED20= 525, + EVENT_INTERFACE_KSPEED30= 526, + + EVENT_INTERFACE_VOLSOUND= 530, + EVENT_INTERFACE_VOLMUSIC= 531, + EVENT_INTERFACE_SOUND3D = 532, + + EVENT_INTERFACE_MIN = 540, + EVENT_INTERFACE_NORM = 541, + EVENT_INTERFACE_MAX = 542, + + EVENT_INTERFACE_SILENT = 550, + EVENT_INTERFACE_NOISY = 551, + + EVENT_INTERFACE_JOYSTICK= 560, + EVENT_INTERFACE_SOLUCE = 561, + + EVENT_INTERFACE_GLINTl = 570, + EVENT_INTERFACE_GLINTr = 571, + EVENT_INTERFACE_GLINTu = 572, + EVENT_INTERFACE_GLINTb = 573, + + EVENT_INTERFACE_NEDIT = 580, + EVENT_INTERFACE_NLIST = 581, + EVENT_INTERFACE_NOK = 582, + EVENT_INTERFACE_NCANCEL = 583, + EVENT_INTERFACE_NDELETE = 584, + EVENT_INTERFACE_NLABEL = 585, + + EVENT_INTERFACE_IOWRITE = 600, + EVENT_INTERFACE_IOREAD = 601, + EVENT_INTERFACE_IOLIST = 602, + EVENT_INTERFACE_IONAME = 603, + EVENT_INTERFACE_IOLABEL = 604, + EVENT_INTERFACE_IOIMAGE = 605, + EVENT_INTERFACE_IODELETE= 606, + + EVENT_INTERFACE_PERSO = 620, + EVENT_INTERFACE_POK = 621, + EVENT_INTERFACE_PCANCEL = 622, + EVENT_INTERFACE_PDEF = 623, + EVENT_INTERFACE_PHEAD = 624, + EVENT_INTERFACE_PBODY = 625, + EVENT_INTERFACE_PLROT = 626, + EVENT_INTERFACE_PRROT = 627, + EVENT_INTERFACE_PC0a = 640, + EVENT_INTERFACE_PC1a = 641, + EVENT_INTERFACE_PC2a = 642, + EVENT_INTERFACE_PC3a = 643, + EVENT_INTERFACE_PC4a = 644, + EVENT_INTERFACE_PC5a = 645, + EVENT_INTERFACE_PC6a = 646, + EVENT_INTERFACE_PC7a = 647, + EVENT_INTERFACE_PC8a = 648, + EVENT_INTERFACE_PC9a = 649, + EVENT_INTERFACE_PCRa = 650, + EVENT_INTERFACE_PCGa = 651, + EVENT_INTERFACE_PCBa = 652, + EVENT_INTERFACE_PC0b = 660, + EVENT_INTERFACE_PC1b = 661, + EVENT_INTERFACE_PC2b = 662, + EVENT_INTERFACE_PC3b = 663, + EVENT_INTERFACE_PC4b = 664, + EVENT_INTERFACE_PC5b = 665, + EVENT_INTERFACE_PC6b = 666, + EVENT_INTERFACE_PC7b = 667, + EVENT_INTERFACE_PC8b = 668, + EVENT_INTERFACE_PC9b = 669, + EVENT_INTERFACE_PCRb = 670, + EVENT_INTERFACE_PCGb = 671, + EVENT_INTERFACE_PCBb = 672, + EVENT_INTERFACE_PFACE1 = 680, + EVENT_INTERFACE_PFACE2 = 681, + EVENT_INTERFACE_PFACE3 = 682, + EVENT_INTERFACE_PFACE4 = 683, + EVENT_INTERFACE_PGLASS0 = 690, + EVENT_INTERFACE_PGLASS1 = 691, + EVENT_INTERFACE_PGLASS2 = 692, + EVENT_INTERFACE_PGLASS3 = 693, + EVENT_INTERFACE_PGLASS4 = 694, + EVENT_INTERFACE_PGLASS5 = 695, + EVENT_INTERFACE_PGLASS6 = 696, + EVENT_INTERFACE_PGLASS7 = 697, + EVENT_INTERFACE_PGLASS8 = 698, + EVENT_INTERFACE_PGLASS9 = 699, + + EVENT_DT_GROUP0 = 700, + EVENT_DT_GROUP1 = 701, + EVENT_DT_GROUP2 = 702, + EVENT_DT_GROUP3 = 703, + EVENT_DT_GROUP4 = 704, + EVENT_DT_LABEL0 = 710, + EVENT_DT_LABEL1 = 711, + EVENT_DT_LABEL2 = 712, + EVENT_DT_LABEL3 = 713, + EVENT_DT_LABEL4 = 714, + EVENT_DT_VISIT0 = 720, + EVENT_DT_VISIT1 = 721, + EVENT_DT_VISIT2 = 722, + EVENT_DT_VISIT3 = 723, + EVENT_DT_VISIT4 = 724, + EVENT_DT_END = 725, + + EVENT_CMD = 800, + EVENT_SPEED = 801, + + EVENT_HYPER_PREV = 900, + EVENT_HYPER_NEXT = 901, + EVENT_HYPER_HOME = 902, + EVENT_HYPER_COPY = 903, + EVENT_HYPER_SIZE1 = 904, + EVENT_HYPER_SIZE2 = 905, + EVENT_HYPER_SIZE3 = 906, + EVENT_HYPER_SIZE4 = 907, + EVENT_HYPER_SIZE5 = 908, + + EVENT_SATCOM_HUSTON = 920, + EVENT_SATCOM_SAT = 921, + EVENT_SATCOM_LOADING = 922, + EVENT_SATCOM_OBJECT = 923, + EVENT_SATCOM_PROG = 924, + EVENT_SATCOM_SOLUCE = 925, + + EVENT_OBJECT_DESELECT = 1000, + EVENT_OBJECT_LEFT = 1001, + EVENT_OBJECT_RIGHT = 1002, + EVENT_OBJECT_UP = 1003, + EVENT_OBJECT_DOWN = 1004, + EVENT_OBJECT_GASUP = 1005, + EVENT_OBJECT_GASDOWN = 1006, + EVENT_OBJECT_HTAKE = 1020, + EVENT_OBJECT_MTAKE = 1021, + EVENT_OBJECT_MFRONT = 1022, + EVENT_OBJECT_MBACK = 1023, + EVENT_OBJECT_MPOWER = 1024, + EVENT_OBJECT_BHELP = 1040, + EVENT_OBJECT_BTAKEOFF = 1041, + EVENT_OBJECT_BDERRICK = 1050, + EVENT_OBJECT_BSTATION = 1051, + EVENT_OBJECT_BFACTORY = 1052, + EVENT_OBJECT_BCONVERT = 1053, + EVENT_OBJECT_BTOWER = 1054, + EVENT_OBJECT_BREPAIR = 1055, + EVENT_OBJECT_BRESEARCH = 1056, + EVENT_OBJECT_BRADAR = 1057, + EVENT_OBJECT_BENERGY = 1058, + EVENT_OBJECT_BLABO = 1059, + EVENT_OBJECT_BNUCLEAR = 1060, + EVENT_OBJECT_BPARA = 1061, + EVENT_OBJECT_BINFO = 1062, + EVENT_OBJECT_BXXXX = 1063, + EVENT_OBJECT_GFLAT = 1070, + EVENT_OBJECT_FCREATE = 1071, + EVENT_OBJECT_FDELETE = 1072, + EVENT_OBJECT_FCOLORb = 1073, + EVENT_OBJECT_FCOLORr = 1074, + EVENT_OBJECT_FCOLORg = 1075, + EVENT_OBJECT_FCOLORy = 1076, + EVENT_OBJECT_FCOLORv = 1077, + EVENT_OBJECT_FACTORYwa = 1080, + EVENT_OBJECT_FACTORYta = 1081, + EVENT_OBJECT_FACTORYfa = 1082, + EVENT_OBJECT_FACTORYia = 1083, + EVENT_OBJECT_FACTORYwc = 1084, + EVENT_OBJECT_FACTORYtc = 1085, + EVENT_OBJECT_FACTORYfc = 1086, + EVENT_OBJECT_FACTORYic = 1087, + EVENT_OBJECT_FACTORYwi = 1088, + EVENT_OBJECT_FACTORYti = 1089, + EVENT_OBJECT_FACTORYfi = 1090, + EVENT_OBJECT_FACTORYii = 1091, + EVENT_OBJECT_FACTORYws = 1092, + EVENT_OBJECT_FACTORYts = 1093, + EVENT_OBJECT_FACTORYfs = 1094, + EVENT_OBJECT_FACTORYis = 1095, + EVENT_OBJECT_FACTORYrt = 1096, + EVENT_OBJECT_FACTORYrc = 1097, + EVENT_OBJECT_FACTORYrr = 1098, + EVENT_OBJECT_FACTORYrs = 1099, + EVENT_OBJECT_FACTORYsa = 1100, + EVENT_OBJECT_SEARCH = 1200, + EVENT_OBJECT_TERRAFORM = 1201, + EVENT_OBJECT_FIRE = 1202, + EVENT_OBJECT_FIREANT = 1203, + EVENT_OBJECT_RECOVER = 1220, + EVENT_OBJECT_BEGSHIELD = 1221, + EVENT_OBJECT_ENDSHIELD = 1222, + EVENT_OBJECT_RTANK = 1223, + EVENT_OBJECT_RFLY = 1224, + EVENT_OBJECT_RTHUMP = 1225, + EVENT_OBJECT_RCANON = 1226, + EVENT_OBJECT_RTOWER = 1227, + EVENT_OBJECT_RPHAZER = 1228, + EVENT_OBJECT_RSHIELD = 1229, + EVENT_OBJECT_RATOMIC = 1230, + EVENT_OBJECT_RiPAW = 1231, + EVENT_OBJECT_RiGUN = 1232, + EVENT_OBJECT_RESET = 1233, + EVENT_OBJECT_DIMSHIELD = 1234, + EVENT_OBJECT_TARGET = 1235, + EVENT_OBJECT_PROGLIST = 1310, + EVENT_OBJECT_PROGRUN = 1311, + EVENT_OBJECT_PROGEDIT = 1312, + EVENT_OBJECT_PROGSTART = 1313, + EVENT_OBJECT_PROGSTOP = 1314, + EVENT_OBJECT_INFOOK = 1340, + EVENT_OBJECT_DELETE = 1350, + EVENT_OBJECT_GENERGY = 1360, + EVENT_OBJECT_GSHIELD = 1361, + EVENT_OBJECT_GRANGE = 1362, + EVENT_OBJECT_COMPASS = 1363, + EVENT_OBJECT_MAP = 1364, + EVENT_OBJECT_MAPZOOM = 1365, + EVENT_OBJECT_GPROGRESS = 1366, + EVENT_OBJECT_GRADAR = 1367, + EVENT_OBJECT_GINFO = 1368, + EVENT_OBJECT_TYPE = 1369, + EVENT_OBJECT_CROSSHAIR = 1370, + EVENT_OBJECT_CORNERul = 1371, + EVENT_OBJECT_CORNERur = 1372, + EVENT_OBJECT_CORNERdl = 1373, + EVENT_OBJECT_CORNERdr = 1374, + EVENT_OBJECT_MAPi = 1375, + EVENT_OBJECT_MAPg = 1376, + EVENT_OBJECT_CAMERA = 1400, + EVENT_OBJECT_HELP = 1401, + EVENT_OBJECT_SOLUCE = 1402, + EVENT_OBJECT_CAMERAleft = 1403, + EVENT_OBJECT_CAMERAright= 1404, + EVENT_OBJECT_CAMERAnear = 1405, + EVENT_OBJECT_CAMERAaway = 1406, + EVENT_OBJECT_SHORTCUT00 = 1500, + EVENT_OBJECT_SHORTCUT01 = 1501, + EVENT_OBJECT_SHORTCUT02 = 1502, + EVENT_OBJECT_SHORTCUT03 = 1503, + EVENT_OBJECT_SHORTCUT04 = 1504, + EVENT_OBJECT_SHORTCUT05 = 1505, + EVENT_OBJECT_SHORTCUT06 = 1506, + EVENT_OBJECT_SHORTCUT07 = 1507, + EVENT_OBJECT_SHORTCUT08 = 1508, + EVENT_OBJECT_SHORTCUT09 = 1509, + EVENT_OBJECT_SHORTCUT10 = 1510, + EVENT_OBJECT_SHORTCUT11 = 1511, + EVENT_OBJECT_SHORTCUT12 = 1512, + EVENT_OBJECT_SHORTCUT13 = 1513, + EVENT_OBJECT_SHORTCUT14 = 1514, + EVENT_OBJECT_SHORTCUT15 = 1515, + EVENT_OBJECT_SHORTCUT16 = 1516, + EVENT_OBJECT_SHORTCUT17 = 1517, + EVENT_OBJECT_SHORTCUT18 = 1518, + EVENT_OBJECT_SHORTCUT19 = 1519, + EVENT_OBJECT_MOVIELOCK = 1550, + EVENT_OBJECT_EDITLOCK = 1551, + EVENT_OBJECT_LIMIT = 1560, + + EVENT_OBJECT_PEN0 = 1570, + EVENT_OBJECT_PEN1 = 1571, + EVENT_OBJECT_PEN2 = 1572, + EVENT_OBJECT_PEN3 = 1573, + EVENT_OBJECT_PEN4 = 1574, + EVENT_OBJECT_PEN5 = 1575, + EVENT_OBJECT_PEN6 = 1576, + EVENT_OBJECT_PEN7 = 1577, + EVENT_OBJECT_PEN8 = 1578, + EVENT_OBJECT_REC = 1580, + EVENT_OBJECT_STOP = 1581, + + EVENT_STUDIO_OK = 2000, + EVENT_STUDIO_CANCEL = 2001, + EVENT_STUDIO_EDIT = 2002, + EVENT_STUDIO_LIST = 2003, + EVENT_STUDIO_NEW = 2010, + EVENT_STUDIO_OPEN = 2011, + EVENT_STUDIO_SAVE = 2012, + EVENT_STUDIO_UNDO = 2013, + EVENT_STUDIO_CUT = 2014, + EVENT_STUDIO_COPY = 2015, + EVENT_STUDIO_PASTE = 2016, + EVENT_STUDIO_SIZE = 2017, + EVENT_STUDIO_TOOL = 2018, + EVENT_STUDIO_HELP = 2019, + EVENT_STUDIO_COMPILE = 2050, + EVENT_STUDIO_RUN = 2051, + EVENT_STUDIO_REALTIME = 2052, + EVENT_STUDIO_STEP = 2053, + + EVENT_USER = 10000, + EVENT_FORCE_DWORD = 0x7fffffff +}; + +typedef struct +{ + EventMsg event; // événement (EVENT_*) + long param; // paramètre + FPOINT pos; // position de la souris (0..1) + float axeX; // commande de l'axe X (-1..1) + float axeY; // commande de l'axe Y (-1..1) + float axeZ; // commande de l'axe Z (-1..1) + short keyState; // état du clavier (KS_*) + float rTime; // temps relatif +} +Event; + + +#define VK_BUTTON1 (0x100+1) // joystick button 1 +#define VK_BUTTON2 (0x100+2) // joystick button 2 +#define VK_BUTTON3 (0x100+3) // joystick button 3 +#define VK_BUTTON4 (0x100+4) // joystick button 4 +#define VK_BUTTON5 (0x100+5) // joystick button 5 +#define VK_BUTTON6 (0x100+6) // joystick button 6 +#define VK_BUTTON7 (0x100+7) // joystick button 7 +#define VK_BUTTON8 (0x100+8) // joystick button 8 +#define VK_BUTTON9 (0x100+9) // joystick button 9 +#define VK_BUTTON10 (0x100+10) // joystick button 10 +#define VK_BUTTON11 (0x100+11) // joystick button 11 +#define VK_BUTTON12 (0x100+12) // joystick button 12 +#define VK_BUTTON13 (0x100+13) // joystick button 13 +#define VK_BUTTON14 (0x100+14) // joystick button 14 +#define VK_BUTTON15 (0x100+15) // joystick button 15 +#define VK_BUTTON16 (0x100+16) // joystick button 16 +#define VK_BUTTON17 (0x100+17) // joystick button 17 +#define VK_BUTTON18 (0x100+18) // joystick button 18 +#define VK_BUTTON19 (0x100+19) // joystick button 19 +#define VK_BUTTON20 (0x100+20) // joystick button 20 +#define VK_BUTTON21 (0x100+21) // joystick button 21 +#define VK_BUTTON22 (0x100+22) // joystick button 22 +#define VK_BUTTON23 (0x100+23) // joystick button 23 +#define VK_BUTTON24 (0x100+24) // joystick button 24 +#define VK_BUTTON25 (0x100+25) // joystick button 25 +#define VK_BUTTON26 (0x100+26) // joystick button 26 +#define VK_BUTTON27 (0x100+27) // joystick button 27 +#define VK_BUTTON28 (0x100+28) // joystick button 28 +#define VK_BUTTON29 (0x100+29) // joystick button 29 +#define VK_BUTTON30 (0x100+30) // joystick button 30 +#define VK_BUTTON31 (0x100+31) // joystick button 31 +#define VK_BUTTON32 (0x100+32) // joystick button 32 + +#define VK_WHEELUP (0x200+1) // molette souris up +#define VK_WHEELDOWN (0x200+2) // molette souris down + + +enum KeyRank +{ + KEYRANK_LEFT = 0, + KEYRANK_RIGHT = 1, + KEYRANK_UP = 2, + KEYRANK_DOWN = 3, + KEYRANK_GUP = 4, + KEYRANK_GDOWN = 5, + KEYRANK_CAMERA = 6, + KEYRANK_DESEL = 7, + KEYRANK_ACTION = 8, + KEYRANK_NEAR = 9, + KEYRANK_AWAY = 10, + KEYRANK_NEXT = 11, + KEYRANK_HUMAN = 12, + KEYRANK_QUIT = 13, + KEYRANK_HELP = 14, + KEYRANK_PROG = 15, + KEYRANK_VISIT = 16, + KEYRANK_SPEED10 = 17, + KEYRANK_SPEED15 = 18, + KEYRANK_SPEED20 = 19, + KEYRANK_SPEED30 = 20, + KEYRANK_AIMUP = 21, + KEYRANK_AIMDOWN = 22, + KEYRANK_CBOT = 23, +}; + + + +class CEvent +{ +public: + CEvent(CInstanceManager* iMan); + ~CEvent(); + + void Flush(); + void MakeEvent(Event &event, EventMsg msg); + BOOL AddEvent(const Event &event); + BOOL GetEvent(Event &event); + +protected: + +protected: + CInstanceManager* m_iMan; + + Event m_fifo[MAXEVENT]; + int m_head; + int m_tail; + int m_total; +}; + + +#endif //_EVENT_H_ diff --git a/src/gauge.cpp b/src/gauge.cpp new file mode 100644 index 00000000..8f3d5bde --- /dev/null +++ b/src/gauge.cpp @@ -0,0 +1,146 @@ +// gauge.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "gauge.h" + + + + +// Constructeur de l'objet. + +CGauge::CGauge(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); + + m_level = 0.0f; +} + +// Destructeur de l'objet. + +CGauge::~CGauge() +{ + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CGauge::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + return TRUE; +} + + +// Gestion d'un événement. + +BOOL CGauge::EventProcess(const Event &event) +{ + CControl::EventProcess(event); + + if ( event.event == EVENT_LBUTTONDOWN ) + { + if ( CControl::Detect(event.pos) ) + { + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + return FALSE; + } + } + + return TRUE; +} + + +// Dessine la jauge. + +void CGauge::Draw() +{ + FPOINT pos, dim, ddim, uv1, uv2, corner; + float dp; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + + dp = 0.5f/256.0f; + + pos = m_pos; + dim = m_dim; + + uv1.x = 32.0f/256.0f; + uv1.y = 32.0f/256.0f; + uv2.x = 64.0f/256.0f; + uv2.y = 64.0f/256.0f; + + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + corner.x = 10.0f/640.0f; + corner.y = 10.0f/480.0f; + + DrawIcon(pos, dim, uv1, uv2, corner, 8.0f/256.0f); + + + pos.x += 3.0f/640.0f; + pos.y += 3.0f/480.0f; + dim.x -= 6.0f/640.0f; + dim.y -= 6.0f/480.0f; + + if ( m_dim.x < m_dim.y ) // jauge verticale ? + { + uv1.x = (0.0f+m_icon*16.0f)/256.0f; + uv2.x = uv1.x+16.0f/256.0f; + uv1.y = 128.0f/256.0f+m_level*(64.0f/256.0f); + uv2.y = uv1.y+64.0f/256.0f; + } + else // jauge horizontale ? + { + uv1.x = 64.0f/256.0f+(1.0f-m_level)*(64.0f/256.0f); + uv2.x = uv1.x+64.0f/256.0f; + uv1.y = (128.0f+m_icon*16.0f)/256.0f; + uv2.y = uv1.y+16.0f/256.0f; + } + + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + DrawIcon(pos, dim, uv1, uv2); +} + + +// Gestion du niveau de la jauge. + +void CGauge::SetLevel(float level) +{ + if ( level < 0.0f ) level = 0.0f; + if ( level > 1.0f ) level = 1.0f; + m_level = level; +} + +float CGauge::RetLevel() +{ + return m_level; +} + + diff --git a/src/gauge.h b/src/gauge.h new file mode 100644 index 00000000..5813268a --- /dev/null +++ b/src/gauge.h @@ -0,0 +1,36 @@ +// gauge.h + +#ifndef _GAUGE_H_ +#define _GAUGE_H_ + + +#include "control.h" + + +class CD3DEngine; + + + +class CGauge : public CControl +{ +public: + CGauge(CInstanceManager* iMan); + ~CGauge(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + BOOL EventProcess(const Event &event); + + void Draw(); + + void SetLevel(float level); + float RetLevel(); + +protected: + +protected: + float m_level; +}; + + +#endif //_GAUGE_H_ diff --git a/src/global.h b/src/global.h new file mode 100644 index 00000000..5ed51286 --- /dev/null +++ b/src/global.h @@ -0,0 +1,48 @@ +// global.h + +#ifndef _GLOBAL_H_ +#define _GLOBAL_H_ + + +#define BUILD_FACTORY (1<<0) // usine +#define BUILD_DERRICK (1<<1) // derrick +#define BUILD_CONVERT (1<<2) // convertisseur +#define BUILD_RADAR (1<<3) // radar +#define BUILD_ENERGY (1<<4) // fabrique à pile +#define BUILD_NUCLEAR (1<<5) // centrale nucléaire +#define BUILD_STATION (1<<6) // station de recharge +#define BUILD_REPAIR (1<<7) // centre de réparation +#define BUILD_TOWER (1<<8) // tour de défense +#define BUILD_RESEARCH (1<<9) // centre de recherche +#define BUILD_LABO (1<<10) // laboratoire +#define BUILD_PARA (1<<11) // paratonnerre +#define BUILD_INFO (1<<12) // borne d'information +#define BUILD_GFLAT (1<<16) // montre le sol plat +#define BUILD_FLAG (1<<17) // met/enlève drapeau de couleur + + +// Ne pas changer les valeurs à cause des sauvegardes (bits=...). + +#define RESEARCH_TANK (1<<0) // chenilles +#define RESEARCH_FLY (1<<1) // ailes +#define RESEARCH_CANON (1<<2) // canon +#define RESEARCH_TOWER (1<<3) // tour de défense +#define RESEARCH_ATOMIC (1<<4) // nucléaire +#define RESEARCH_THUMP (1<<5) // thumper +#define RESEARCH_SHIELD (1<<6) // bouclier +#define RESEARCH_PHAZER (1<<7) // canon phazer +#define RESEARCH_iPAW (1<<8) // pattes des insectes +#define RESEARCH_iGUN (1<<9) // canon des insectes +#define RESEARCH_RECYCLER (1<<10) // recycleur +#define RESEARCH_SUBM (1<<11) // sous-marin +#define RESEARCH_SNIFFER (1<<12) // sniffeur + +extern long g_id; // identificateur unique +extern long g_build; // bâtiments constructibles +extern long g_researchDone; // recherches effectuées +extern long g_researchEnable; // recherches accessbles +extern float g_unit; // facteur de conversion + + + +#endif //_GLOBAL_H_ diff --git a/src/group.cpp b/src/group.cpp new file mode 100644 index 00000000..82a0e0d1 --- /dev/null +++ b/src/group.cpp @@ -0,0 +1,632 @@ +// group.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "group.h" + + + + +// Constructeur de l'objet. + +CGroup::CGroup(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); +} + +// Destructeur de l'objet. + +CGroup::~CGroup() +{ + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CGroup::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + + if ( icon == -1 ) + { + char name[100]; + char* p; + + GetResource(RES_EVENT, eventMsg, name); + p = strchr(name, '\\'); + if ( p != 0 ) *p = 0; + SetName(name); + } + + return TRUE; +} + + +// Gestion d'un événement. + +BOOL CGroup::EventProcess(const Event &event) +{ + return TRUE; +} + + +// Dessine le bouton. + +void CGroup::Draw() +{ + FPOINT uv1,uv2, corner, pos, dim; + float dp; + int icon; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + if ( m_state & STATE_SHADOW ) + { + DrawShadow(m_pos, m_dim); + } + + dp = 0.5f/256.0f; + + if ( m_icon == 0 ) // cadre en creux ? + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 160.0f/256.0f; + uv1.y = 192.0f/256.0f; // u-v texture + uv2.x = 192.0f/256.0f; + uv2.y = 224.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 10.0f/640.0f; + corner.y = 10.0f/480.0f; + DrawIcon(m_pos, m_dim, uv1, uv2, corner, 8.0f/256.0f); + } + if ( m_icon == 1 ) // orange uni opaque ? + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 104.0f/256.0f; + uv1.y = 48.0f/256.0f; + uv2.x = 112.0f/256.0f; + uv2.y = 64.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2); + } + if ( m_icon == 2 ) // dégradé orange -> transparent ? + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 112.0f/256.0f; + uv1.y = 48.0f/256.0f; + uv2.x = 120.0f/256.0f; + uv2.y = 64.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2); + } + if ( m_icon == 3 ) // dégradé transparent -> gris ? + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 120.0f/256.0f; + uv1.y = 48.0f/256.0f; + uv2.x = 128.0f/256.0f; + uv2.y = 64.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2); + } + if ( m_icon == 4 ) // coin bleu dégradé ? + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 192.0f/256.0f; + uv1.y = 128.0f/256.0f; + uv2.x = 224.0f/256.0f; + uv2.y = 160.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2); + } + if ( m_icon == 5 ) // coin orange dégradé ? + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 224.0f/256.0f; + uv1.y = 128.0f/256.0f; + uv2.x = 256.0f/256.0f; + uv2.y = 160.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2); + } + if ( m_icon == 6 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 0.0f/256.0f; // brun transparent + uv1.y = 75.0f/256.0f; + uv2.x = 64.0f/256.0f; + uv2.y = 128.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 4.0f/640.0f; + corner.y = 4.0f/480.0f; + DrawIcon(m_pos, m_dim, uv1, uv2, corner, 8.0f/256.0f); + } + if ( m_icon == 7 ) + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 64.0f/256.0f; + uv1.y = 0.0f/256.0f; + uv2.x = 96.0f/256.0f; + uv2.y = 32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2, 8.0f/256.0f); + } + if ( m_icon == 8 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 64.0f/256.0f; // vert transparent + uv1.y = 160.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 176.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2, 8.0f/256.0f); + } + if ( m_icon == 9 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 64.0f/256.0f; // rouge transparent + uv1.y = 176.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 192.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2, 8.0f/256.0f); + } + if ( m_icon == 10 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 64.0f/256.0f; // bleu transparent + uv1.y = 192.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 208.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2, 8.0f/256.0f); + } + if ( m_icon == 11 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 64.0f/256.0f; // jaune transparent + uv1.y = 224.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 240.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2, 8.0f/256.0f); + } + if ( m_icon == 12 ) // viseur en croix ? + { + dim.x = m_dim.x/2.0f; + dim.y = m_dim.y/2.0f; + + m_engine->SetTexture("mouse.tga"); + m_engine->SetState(D3DSTATETTb); + pos.x = m_pos.x-m_dim.x/300.0f; + pos.y = m_pos.y+m_dim.y/300.0f+dim.y; + uv1.x = 0.5f/256.0f; + uv1.y = 192.5f/256.0f; + uv2.x = 63.5f/256.0f; + uv2.y = 255.5f/256.0f; + DrawIcon(pos, dim, uv1, uv2); // ul + pos.x += dim.x; + Swap(uv1.x, uv2.x); + DrawIcon(pos, dim, uv1, uv2); // ur + pos.y -= dim.y; + Swap(uv1.y, uv2.y); + DrawIcon(pos, dim, uv1, uv2); // dr + pos.x -= dim.x; + Swap(uv1.x, uv2.x); + DrawIcon(pos, dim, uv1, uv2); // dl + + m_engine->SetState(D3DSTATETTw); + pos.x = m_pos.x+m_dim.x/300.0f; + pos.y = m_pos.y-m_dim.y/300.0f+dim.y; + uv1.x = 64.5f/256.0f; + uv1.y = 192.5f/256.0f; + uv2.x = 127.5f/256.0f; + uv2.y = 255.5f/256.0f; + DrawIcon(pos, dim, uv1, uv2); // ul + pos.x += dim.x; + Swap(uv1.x, uv2.x); + DrawIcon(pos, dim, uv1, uv2); // ur + pos.y -= dim.y; + Swap(uv1.y, uv2.y); + DrawIcon(pos, dim, uv1, uv2); // dr + pos.x -= dim.x; + Swap(uv1.x, uv2.x); + DrawIcon(pos, dim, uv1, uv2); // dl + } + if ( m_icon == 13 ) // coin sup/gauche ? + { + m_engine->SetTexture("mouse.tga"); + m_engine->SetState(D3DSTATETTb); + pos.x = m_pos.x-m_dim.x/150.0f; + pos.y = m_pos.y+m_dim.y/150.0f; + uv1.x = 128.5f/256.0f; + uv1.y = 192.5f/256.0f; + uv2.x = 191.5f/256.0f; + uv2.y = 255.5f/256.0f; + DrawIcon(pos, m_dim, uv1, uv2); + + m_engine->SetState(D3DSTATETTw); + pos.x = m_pos.x+m_dim.x/150.0f; + pos.y = m_pos.y-m_dim.y/150.0f; + uv1.x = 192.5f/256.0f; + uv1.y = 192.5f/256.0f; + uv2.x = 255.5f/256.0f; + uv2.y = 255.5f/256.0f; + DrawIcon(pos, m_dim, uv1, uv2); + } + if ( m_icon == 14 ) // coin sup/droite ? + { + m_engine->SetTexture("mouse.tga"); + m_engine->SetState(D3DSTATETTb); + pos.x = m_pos.x-m_dim.x/150.0f; + pos.y = m_pos.y+m_dim.y/150.0f; + uv2.x = 128.5f/256.0f; + uv1.y = 192.5f/256.0f; + uv1.x = 191.5f/256.0f; + uv2.y = 255.5f/256.0f; + DrawIcon(pos, m_dim, uv1, uv2); + + m_engine->SetState(D3DSTATETTw); + pos.x = m_pos.x+m_dim.x/150.0f; + pos.y = m_pos.y-m_dim.y/150.0f; + uv2.x = 192.5f/256.0f; + uv1.y = 192.5f/256.0f; + uv1.x = 255.5f/256.0f; + uv2.y = 255.5f/256.0f; + DrawIcon(pos, m_dim, uv1, uv2); + } + if ( m_icon == 15 ) // coin inf/gauche ? + { + m_engine->SetTexture("mouse.tga"); + m_engine->SetState(D3DSTATETTb); + pos.x = m_pos.x-m_dim.x/150.0f; + pos.y = m_pos.y+m_dim.y/150.0f; + uv1.x = 128.5f/256.0f; + uv2.y = 192.5f/256.0f; + uv2.x = 191.5f/256.0f; + uv1.y = 255.5f/256.0f; + DrawIcon(pos, m_dim, uv1, uv2); + + m_engine->SetState(D3DSTATETTw); + pos.x = m_pos.x+m_dim.x/150.0f; + pos.y = m_pos.y-m_dim.y/150.0f; + uv1.x = 192.5f/256.0f; + uv2.y = 192.5f/256.0f; + uv2.x = 255.5f/256.0f; + uv1.y = 255.5f/256.0f; + DrawIcon(pos, m_dim, uv1, uv2); + } + if ( m_icon == 16 ) // coin inf/droite ? + { + m_engine->SetTexture("mouse.tga"); + m_engine->SetState(D3DSTATETTb); + pos.x = m_pos.x-m_dim.x/150.0f; + pos.y = m_pos.y+m_dim.y/150.0f; + uv2.x = 128.5f/256.0f; + uv2.y = 192.5f/256.0f; + uv1.x = 191.5f/256.0f; + uv1.y = 255.5f/256.0f; + DrawIcon(pos, m_dim, uv1, uv2); + + m_engine->SetState(D3DSTATETTw); + pos.x = m_pos.x+m_dim.x/150.0f; + pos.y = m_pos.y-m_dim.y/150.0f; + uv2.x = 192.5f/256.0f; + uv2.y = 192.5f/256.0f; + uv1.x = 255.5f/256.0f; + uv1.y = 255.5f/256.0f; + DrawIcon(pos, m_dim, uv1, uv2); + } + if ( m_icon == 17 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 0.0f/256.0f; // cadre bleu + uv1.y = 75.0f/256.0f; + uv2.x = 64.0f/256.0f; + uv2.y = 128.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 6.0f/640.0f; + corner.y = 6.0f/480.0f; + DrawIcon(m_pos, m_dim, uv1, uv2, corner, 2.0f/256.0f); + } + if ( m_icon == 18 ) // flèche > pour SatCom ? + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 0.0f/256.0f; // > + uv1.y = 192.0f/256.0f; + uv2.x = 32.0f/256.0f; + uv2.y = 224.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2); + } + if ( m_icon == 19 ) // sigle SatCom ? + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 224.0f/256.0f; // sigle SatCom + uv1.y = 224.0f/256.0f; + uv2.x = 256.0f/256.0f; + uv2.y = 256.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2); + } + if ( m_icon == 20 ) // fond fleu uni ? + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 224.0f/256.0f; + uv1.y = 32.0f/256.0f; + uv2.x = 256.0f/256.0f; + uv2.y = 64.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2); + } + if ( m_icon == 21 ) // sigle stand-by ? + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 160.0f/256.0f; + uv1.y = 32.0f/256.0f; + uv2.x = 192.0f/256.0f; + uv2.y = 64.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2); + } + if ( m_icon == 22 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 64.0f/256.0f; // jaune opaque + uv1.y = 224.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 240.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 5.0f/640.0f; + corner.y = 5.0f/480.0f; + DrawIcon(m_pos, m_dim, uv1, uv2, corner, 3.0f/256.0f); + } + + if ( m_icon == 23 ) + { + m_engine->SetTexture("button3.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 64.0f/256.0f; // jaune + uv1.y = 192.0f/256.0f; + uv2.x = 80.0f/256.0f; + uv2.y = 208.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 4.0f/640.0f; + corner.y = 4.0f/480.0f; + DrawIcon(m_pos, m_dim, uv1, uv2, corner, 2.0f/256.0f); + } + if ( m_icon == 24 ) + { + m_engine->SetTexture("button3.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 80.0f/256.0f; // orangé + uv1.y = 192.0f/256.0f; + uv2.x = 96.0f/256.0f; + uv2.y = 208.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 4.0f/640.0f; + corner.y = 4.0f/480.0f; + DrawIcon(m_pos, m_dim, uv1, uv2, corner, 2.0f/256.0f); + } + if ( m_icon == 25 ) + { + m_engine->SetTexture("button3.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 64.0f/256.0f; // orange + uv1.y = 208.0f/256.0f; + uv2.x = 80.0f/256.0f; + uv2.y = 224.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 4.0f/640.0f; + corner.y = 4.0f/480.0f; + DrawIcon(m_pos, m_dim, uv1, uv2, corner, 2.0f/256.0f); + } + if ( m_icon == 26 ) + { + m_engine->SetTexture("button3.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 80.0f/256.0f; // rouge + uv1.y = 208.0f/256.0f; + uv2.x = 96.0f/256.0f; + uv2.y = 224.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 4.0f/640.0f; + corner.y = 4.0f/480.0f; + DrawIcon(m_pos, m_dim, uv1, uv2, corner, 2.0f/256.0f); + } + if ( m_icon == 27 ) + { + m_engine->SetTexture("button3.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 32.0f/256.0f; + uv1.y = 0.0f/256.0f; + uv2.x = 64.0f/256.0f; + uv2.y = 32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(m_pos, m_dim, uv1, uv2); + } + + if ( m_icon >= 100 && m_icon <= 120 ) // bâtiment ? + { + pos = m_pos; + dim = m_dim; + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 32.0f/256.0f; + uv1.y = 32.0f/256.0f; + uv2.x = uv1.x+32.0f/256.0f; + uv2.y = uv1.y+32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2); + + m_engine->SetTexture("button3.tga"); + m_engine->SetState(D3DSTATENORMAL); + pos.x += 8.0f/640.0f; + pos.y += 8.0f/480.0f; + dim.x -= 16.0f/640.0f; + dim.y -= 16.0f/480.0f; + uv1.x = 32.0f/256.0f; + uv1.y = 0.0f/256.0f; + uv2.x = uv1.x+32.0f/256.0f; + uv2.y = uv1.y+32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2); + + m_engine->SetState(D3DSTATENORMAL); + pos.x += 2.0f/640.0f; + pos.y += 2.0f/480.0f; + dim.x -= 4.0f/640.0f; + dim.y -= 4.0f/480.0f; + uv1.x = 0.0f/256.0f; + uv1.y = 0.0f/256.0f; + uv2.x = uv1.x+32.0f/256.0f; + uv2.y = uv1.y+32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2); + + m_engine->SetState(D3DSTATETTb); + pos.x += 8.0f/640.0f; + pos.y += 8.0f/480.0f; + dim.x -= 16.0f/640.0f; + dim.y -= 16.0f/480.0f; + if ( m_icon == 100 ) icon = 43; // base ? + if ( m_icon == 101 ) icon = 32; // factory ? + if ( m_icon == 102 ) icon = 35; // research ? + if ( m_icon == 103 ) icon = 34; // convert ? + if ( m_icon == 104 ) icon = 36; // station ? + if ( m_icon == 105 ) icon = 40; // radar ? + if ( m_icon == 106 ) icon = 41; // repair ? + if ( m_icon == 107 ) icon = 37; // tower ? + if ( m_icon == 108 ) icon = 39; // energy ? + if ( m_icon == 109 ) icon = 33; // derrick ? + if ( m_icon == 110 ) icon = 42; // nuclear ? + if ( m_icon == 111 ) icon = 38; // labo ? + if ( m_icon == 112 ) icon = 44; // info ? + if ( m_icon == 113 ) icon = 46; // paratonnerre ? + if ( m_icon == 114 ) icon = 47; // coffre-fort ? + if ( m_icon == 115 ) icon = 48; // centre de contrôle ? + uv1.x = (32.0f/256.0f)*(icon%8); + uv1.y = (32.0f/256.0f)*(icon/8); // uv texture + uv2.x = uv1.x+32.0f/256.0f; + uv2.y = uv1.y+32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2); + } +} + + diff --git a/src/group.h b/src/group.h new file mode 100644 index 00000000..4bd6a9ce --- /dev/null +++ b/src/group.h @@ -0,0 +1,32 @@ +// group.h + +#ifndef _GROUP_H_ +#define _GROUP_H_ + + +#include "control.h" + + +class CD3DEngine; + + + +class CGroup : public CControl +{ +public: + CGroup(CInstanceManager* iMan); + ~CGroup(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + BOOL EventProcess(const Event &event); + + void Draw(); + +protected: + +protected: +}; + + +#endif //_GROUP_H_ diff --git a/src/image.cpp b/src/image.cpp new file mode 100644 index 00000000..ce560eff --- /dev/null +++ b/src/image.cpp @@ -0,0 +1,145 @@ +// image.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "image.h" + + + + +// Constructeur de l'objet. + +CImage::CImage(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); + + m_filename[0] = 0; +} + +// Destructeur de l'objet. + +CImage::~CImage() +{ + if ( m_filename[0] != 0 ) + { + m_engine->FreeTexture(m_filename); + } + + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CImage::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + + if ( icon == -1 ) + { + char name[100]; + char* p; + + GetResource(RES_EVENT, eventMsg, name); + p = strchr(name, '\\'); + if ( p != 0 ) *p = 0; + SetName(name); + } + + return TRUE; +} + + +// Spécifie le nom de l'image à afficher. + +void CImage::SetFilenameImage(char *name) +{ + if ( m_filename[0] != 0 ) + { + m_engine->FreeTexture(m_filename); + } + + strcpy(m_filename, name); +} + +char* CImage::RetFilenameImage() +{ + return m_filename; +} + + +// Gestion d'un événement. + +BOOL CImage::EventProcess(const Event &event) +{ + return TRUE; +} + + +// Dessine le bouton. + +void CImage::Draw() +{ + FPOINT uv1,uv2, corner, pos, dim; + float dp; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + if ( m_state & STATE_SHADOW ) + { + DrawShadow(m_pos, m_dim); + } + + dp = 0.5f/256.0f; + + if ( m_icon == 0 ) // cadre en creux ? + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 160.0f/256.0f; + uv1.y = 192.0f/256.0f; // u-v texture + uv2.x = 192.0f/256.0f; + uv2.y = 224.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 10.0f/640.0f; + corner.y = 10.0f/480.0f; + DrawIcon(m_pos, m_dim, uv1, uv2, corner, 8.0f/256.0f); + } + + if ( m_filename[0] != 0 ) // affiche une image ? + { + m_engine->LoadTexture(m_filename); + m_engine->SetTexture(m_filename); + m_engine->SetState(D3DSTATENORMAL); + pos = m_pos; + dim = m_dim; + pos.x += 5.0f/640.0f; + pos.y += 5.0f/480.0f; + dim.x -= 10.0f/640.0f; + dim.y -= 10.0f/480.0f; + uv1.x = 0.0f; + uv1.y = 0.0f; + uv2.x = 1.0f; + uv2.y = 1.0f; + DrawIcon(pos, dim, uv1, uv2); + } +} + + diff --git a/src/image.h b/src/image.h new file mode 100644 index 00000000..acbfa10d --- /dev/null +++ b/src/image.h @@ -0,0 +1,36 @@ +// image.h + +#ifndef _IMAGE_H_ +#define _IMAGE_H_ + + +#include "control.h" + + +class CD3DEngine; + + + +class CImage : public CControl +{ +public: + CImage(CInstanceManager* iMan); + ~CImage(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + BOOL EventProcess(const Event &event); + + void Draw(); + + void SetFilenameImage(char *name); + char* RetFilenameImage(); + +protected: + +protected: + char m_filename[100]; +}; + + +#endif //_IMAGE_H_ diff --git a/src/iman.cpp b/src/iman.cpp new file mode 100644 index 00000000..9b64b930 --- /dev/null +++ b/src/iman.cpp @@ -0,0 +1,149 @@ +// iman.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include + +#include "struct.h" +#include "iman.h" + + + + +// Constructeur de l'objet. + +CInstanceManager::CInstanceManager() +{ + int i; + + for ( i=0 ; i= CLASS_MAX ) return; + if ( m_table[classType].classPointer == 0 ) return; + + free(m_table[classType].classPointer); + m_table[classType].classPointer = 0; +} + + +// Ajoute une nouvelle instance d'une classe. + +BOOL CInstanceManager::AddInstance(ClassType classType, void* pointer, int max) +{ + int i; + + if ( classType < 0 || classType >= CLASS_MAX ) return FALSE; + + if ( m_table[classType].classPointer == 0 ) + { + m_table[classType].classPointer = (void**)malloc(max*sizeof(void*)); + m_table[classType].totalPossible = max; + m_table[classType].totalUsed = 0; + } + + if ( m_table[classType].totalUsed >= m_table[classType].totalPossible ) return FALSE; + + i = m_table[classType].totalUsed++; + m_table[classType].classPointer[i] = pointer; + return TRUE; +} + +// Supprime une instance d'une classe. + +BOOL CInstanceManager::DeleteInstance(ClassType classType, void* pointer) +{ + int i; + + if ( classType < 0 || classType >= CLASS_MAX ) return FALSE; + + for ( i=0 ; i= CLASS_MAX ) return 0; + if ( m_table[classType].classPointer == 0 ) return 0; +#endif + if ( rank >= m_table[classType].totalUsed ) return 0; + + return m_table[classType].classPointer[rank]; +} + + +// Bouche les trous dans une table. + +void CInstanceManager::Compress(ClassType classType) +{ + int i, j; + + if ( classType < 0 || classType >= CLASS_MAX ) return; + + j = 0; + for ( i=0 ; i +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "control.h" +#include "button.h" +#include "color.h" +#include "check.h" +#include "key.h" +#include "group.h" +#include "image.h" +#include "label.h" +#include "edit.h" +#include "editvalue.h" +#include "scroll.h" +#include "slider.h" +#include "list.h" +#include "shortcut.h" +#include "compass.h" +#include "target.h" +#include "map.h" +#include "window.h" +#include "camera.h" +#include "interface.h" + + + + +// Constructeur de l'objet. + +CInterface::CInterface(CInstanceManager* iMan) +{ + int i; + + m_iMan = iMan; + m_iMan->AddInstance(CLASS_INTERFACE, this); + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_camera = 0; + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau bouton. + +CButton* CInterface::CreateButton(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CButton* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau bouton. + +CColor* CInterface::CreateColor(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CColor* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau bouton. + +CCheck* CInterface::CreateCheck(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CCheck* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau bouton. + +CKey* CInterface::CreateKey(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CKey* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau bouton. + +CGroup* CInterface::CreateGroup(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CGroup* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau bouton. + +CImage* CInterface::CreateImage(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CImage* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau label. + +CLabel* CInterface::CreateLabel(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg, + char *name) +{ + CLabel* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + pc->SetName(name); + return pc; + } + } + return 0; +} + +// Crée un nouveau pavé éditable. + +CEdit* CInterface::CreateEdit(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CEdit* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau pavé éditable. + +CEditValue* CInterface::CreateEditValue(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CEditValue* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouvel ascenseur. + +CScroll* CInterface::CreateScroll(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CScroll* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau curseur. + +CSlider* CInterface::CreateSlider(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CSlider* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée une nouvelle liste. + +CList* CInterface::CreateList(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg, + float expand) +{ + CList* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg, expand); + return pc; + } + } + return 0; +} + +// Crée un nouveau raccourci. + +CShortcut* CInterface::CreateShortcut(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CShortcut* ps; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return ps; + } + } + return 0; +} + +// Crée un nouvelle boussole. + +CCompass* CInterface::CreateCompass(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CCompass* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouvelle cible. + +CTarget* CInterface::CreateTarget(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CTarget* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée une nouvelle carte. + +CMap* CInterface::CreateMap(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CMap* pm; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=10 ; iCreate(pos, dim, icon, eventMsg); + return pm; + } + } + return 0; +} + +// Supprime un contrôle. + +BOOL CInterface::DeleteControl(EventMsg eventMsg) +{ + int i; + + for ( i=0 ; iRetEventMsg() ) + { + delete m_table[i]; + m_table[i] = 0; + return TRUE; + } + } + } + return FALSE; +} + +// Donne un contrôle. + +CControl* CInterface::SearchControl(EventMsg eventMsg) +{ + int i; + + for ( i=0 ; iRetEventMsg() ) + { + return m_table[i]; + } + } + } + return 0; +} + +// Gestion d'un événement. + +BOOL CInterface::EventProcess(const Event &event) +{ + int i; + + if ( event.event == EVENT_MOUSEMOVE ) + { + if ( m_camera == 0 ) + { + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + } + m_engine->SetMouseType(m_camera->RetMouseDef(event.pos)); + } + + for ( i=MAXCONTROL-1 ; i>=0 ; i-- ) + { + if ( m_table[i] != 0 && + m_table[i]->TestState(STATE_ENABLE) ) + { + if ( !m_table[i]->EventProcess(event) ) + { + return FALSE; + } + } + } + + return TRUE; +} + + +// Donne le tooltip lié à la fenêtre. + +BOOL CInterface::GetTooltip(FPOINT pos, char* name) +{ + int i; + + for ( i=MAXCONTROL-1 ; i>=0 ; i-- ) + { + if ( m_table[i] != 0 ) + { + if ( m_table[i]->GetTooltip(pos, name) ) + { + return TRUE; + } + } + } + return FALSE; +} + + +// Dessine tous les boutons. + +void CInterface::Draw() +{ + D3DMATERIAL7 material; + int i; + + ZeroMemory( &material, sizeof(D3DMATERIAL7) ); + material.diffuse.r = 1.0f; + material.diffuse.g = 1.0f; + material.diffuse.b = 1.0f; + material.ambient.r = 0.5f; + material.ambient.g = 0.5f; + material.ambient.b = 0.5f; + m_engine->SetMaterial(material); + + for ( i=0 ; i=0 ; i-- ) + { + if ( m_table[i] != 0 ) + { + m_table[i]->Draw(); + } + } +} + + diff --git a/src/interface.h b/src/interface.h new file mode 100644 index 00000000..4feb86de --- /dev/null +++ b/src/interface.h @@ -0,0 +1,77 @@ +// interface.h + +#ifndef _INTERFACE_H_ +#define _INTERFACE_H_ + + +class CInstanceManager; +class CD3DEngine; +class CControl; +class CWindow; +class CButton; +class CColor; +class CCheck; +class CKey; +class CGroup; +class CImage; +class CLabel; +class CEdit; +class CEditValue; +class CScroll; +class CSlider; +class CList; +class CShortcut; +class CMap; +class CGauge; +class CCompass; +class CTarget; +class CCamera; + + +#define MAXCONTROL 100 + + +class CInterface +{ +public: + CInterface(CInstanceManager* iMan); + ~CInterface(); + + BOOL EventProcess(const Event &event); + BOOL GetTooltip(FPOINT pos, char* name); + + void Flush(); + CWindow* CreateWindows(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CButton* CreateButton(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CColor* CreateColor(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CCheck* CreateCheck(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CKey* CreateKey(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CGroup* CreateGroup(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CImage* CreateImage(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CLabel* CreateLabel(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg, char *name); + CEdit* CreateEdit(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CEditValue* CreateEditValue(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CScroll* CreateScroll(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CSlider* CreateSlider(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CList* CreateList(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg, float expand=1.2f); + CShortcut* CreateShortcut(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CCompass* CreateCompass(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CTarget* CreateTarget(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CMap* CreateMap(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + BOOL DeleteControl(EventMsg eventMsg); + CControl* SearchControl(EventMsg eventMsg); + + void Draw(); + +protected: + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CCamera* m_camera; + + CControl* m_table[MAXCONTROL]; +}; + + +#endif //_INTERFACE_H_ diff --git a/src/joystick.cpp b/src/joystick.cpp new file mode 100644 index 00000000..41d9f567 --- /dev/null +++ b/src/joystick.cpp @@ -0,0 +1,222 @@ +// joystick.cpp + +#define STRICT + +#include +#include +#include + +#include "joystick.h" + + + + +// Global variables. + +LPDIRECTINPUT7 g_pDI = NULL; +LPDIRECTINPUTDEVICE2 g_pJoystick = NULL; +DIDEVCAPS g_diDevCaps; + + + + + +// Called once for each enumerated joystick. If we find one, create a +// device interface on it so we can play with it. + +BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, + VOID* pContext ) +{ + HRESULT hr; + + // Obtain an interface to the enumerated joystick. + hr = g_pDI->CreateDeviceEx( pdidInstance->guidInstance, IID_IDirectInputDevice2, + (VOID**)&g_pJoystick, NULL ); + + // If it failed, then we can't use this joystick. (Maybe the user unplugged + // it while we were in the middle of enumerating it.) + if( FAILED(hr) ) + return DIENUM_CONTINUE; + + + // Stop enumeration. Note: we're just taking the first joystick we get. You + // could store all the enumerated joysticks and let the user pick. + return DIENUM_STOP; +} + + +// Callback function for enumerating the axes on a joystick. + +BOOL CALLBACK EnumAxesCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, + VOID* pContext ) +{ + DIPROPRANGE diprg; + diprg.diph.dwSize = sizeof(DIPROPRANGE); + diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); + diprg.diph.dwHow = DIPH_BYOFFSET; + diprg.diph.dwObj = pdidoi->dwOfs; // Specify the enumerated axis + diprg.lMin = -1000; + diprg.lMax = +1000; + + // Set the range for the axis + if( FAILED( g_pJoystick->SetProperty( DIPROP_RANGE, &diprg.diph ) ) ) + return DIENUM_STOP; + + // Set the UI to reflect what axes the joystick supports + switch( pdidoi->dwOfs ) + { + case DIJOFS_X: + OutputDebugString("EnumAxesCallback -x\n"); + break; + case DIJOFS_Y: + OutputDebugString("EnumAxesCallback -y\n"); + break; + case DIJOFS_Z: + OutputDebugString("EnumAxesCallback -z\n"); + break; + case DIJOFS_RX: + OutputDebugString("EnumAxesCallback -rx\n"); + break; + case DIJOFS_RY: + OutputDebugString("EnumAxesCallback -ry\n"); + break; + case DIJOFS_RZ: + OutputDebugString("EnumAxesCallback -rz\n"); + break; + case DIJOFS_SLIDER(0): + OutputDebugString("EnumAxesCallback -s0\n"); + break; + case DIJOFS_SLIDER(1): + OutputDebugString("EnumAxesCallback -s1\n"); + break; + } + + return DIENUM_CONTINUE; +} + + +// Initialize the DirectInput variables. + +BOOL InitDirectInput(HINSTANCE hInst, HWND hWnd) +{ + HRESULT hr; + + // Register with the DirectInput subsystem and get a pointer + // to a IDirectInput interface we can use. + hr = DirectInputCreateEx( hInst, DIRECTINPUT_VERSION,IID_IDirectInput7, (LPVOID*)&g_pDI, NULL ); + if( FAILED(hr) ) return FALSE;; + + // Look for a simple joystick we can use for this sample program. + hr = g_pDI->EnumDevices( DIDEVTYPE_JOYSTICK, EnumJoysticksCallback, + NULL, DIEDFL_ATTACHEDONLY ); + if( FAILED(hr) ) return FALSE; + + // Make sure we got a joystick + if( NULL == g_pJoystick ) + { +//? MessageBox( NULL, "Joystick not found", "DInput Sample", +//? MB_ICONERROR | MB_OK ); + return FALSE; + } + + // Set the data format to "simple joystick" - a predefined data format + // + // A data format specifies which controls on a device we are interested in, + // and how they should be reported. This tells DInput that we will be + // passing a DIJOYSTATE structure to IDirectInputDevice::GetDeviceState(). + hr = g_pJoystick->SetDataFormat( &c_dfDIJoystick ); + if( FAILED(hr) ) return FALSE; + + // Set the cooperative level to let DInput know how this device should + // interact with the system and with other DInput applications. + hr = g_pJoystick->SetCooperativeLevel( hWnd, DISCL_EXCLUSIVE|DISCL_FOREGROUND ); + if( FAILED(hr) ) return FALSE; + + // Determine how many axis the joystick has (so we don't error out setting + // properties for unavailable axis) + g_diDevCaps.dwSize = sizeof(DIDEVCAPS); + hr = g_pJoystick->GetCapabilities(&g_diDevCaps); + if( FAILED(hr) ) return FALSE; + + + // Enumerate the axes of the joyctick and set the range of each axis. Note: + // we could just use the defaults, but we're just trying to show an example + // of enumerating device objects (axes, buttons, etc.). + g_pJoystick->EnumObjects( EnumAxesCallback, (VOID*)g_pJoystick, DIDFT_AXIS ); + + return TRUE; +} + +// Acquire or unacquire the keyboard, depending on if the app is active +// Input device must be acquired before the GetDeviceState is called. + +BOOL SetAcquire(BOOL bActive) +{ + if ( g_pJoystick ) + { + if( bActive ) g_pJoystick->Acquire(); + else g_pJoystick->Unacquire(); + } + return TRUE; +} + + +// Get the input device's state and display it. + +BOOL UpdateInputState( DIJOYSTATE &js ) +{ + HRESULT hr; + + if ( g_pJoystick ) + { + do + { + // Poll the device to read the current state + hr = g_pJoystick->Poll(); + if ( FAILED(hr) ) return FALSE; + + // Get the input's device state + hr = g_pJoystick->GetDeviceState( sizeof(DIJOYSTATE), &js ); + + if( hr == DIERR_INPUTLOST ) + { + // DInput is telling us that the input stream has been + // interrupted. We aren't tracking any state between polls, so + // we don't have any special reset that needs to be done. We + // just re-acquire and try again. + hr = g_pJoystick->Acquire(); + if ( FAILED(hr) ) return FALSE; + } + } + while ( DIERR_INPUTLOST == hr ); + if ( FAILED(hr) ) return FALSE; + } + return TRUE; +} + + +// Initialize the DirectInput variables. + +BOOL FreeDirectInput() +{ + // Unacquire and release any DirectInputDevice objects. + if( NULL != g_pJoystick ) + { + // Unacquire the device one last time just in case + // the app tried to exit while the device is still acquired. + g_pJoystick->Unacquire(); + g_pJoystick->Release(); + g_pJoystick = NULL; + } + + + // Release any DirectInput objects. + if( g_pDI ) + { + g_pDI->Release(); + g_pDI = NULL; + } + + return TRUE; +} + diff --git a/src/joystick.h b/src/joystick.h new file mode 100644 index 00000000..99aff580 --- /dev/null +++ b/src/joystick.h @@ -0,0 +1,15 @@ +// joystick.h + +#ifndef _JOYSTICK_H_ +#define _JOYSTICK_H_ + + + +extern BOOL InitDirectInput(HINSTANCE hInst, HWND hWnd); +extern BOOL SetAcquire(BOOL bActive); +extern BOOL UpdateInputState(DIJOYSTATE &js); +extern BOOL FreeDirectInput(); + + + +#endif //_JOYSTICK_H_ diff --git a/src/key.cpp b/src/key.cpp new file mode 100644 index 00000000..3319f920 --- /dev/null +++ b/src/key.cpp @@ -0,0 +1,277 @@ +// key.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "sound.h" +#include "text.h" +#include "key.h" + + + + +// COnstruit le nom d'une touche. + +void GetKeyName(char *name, int key) +{ + if ( !GetResource(RES_KEY, key, name) ) + { + if ( (key >= '0' && key <= '9') || + (key >= 'A' && key <= 'Z') || + (key >= 'a' && key <= 'z') ) + { + name[0] = key; + name[1] = 0; + } + else + { + sprintf(name, "Code %d", key); + } + } +} + + + + +// Constructeur de l'objet. + +CKey::CKey(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); + + m_key[0] = 0; + m_key[1] = 0; + m_bCatch = FALSE; +} + +// Destructeur de l'objet. + +CKey::~CKey() +{ + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CKey::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + char name[100]; + char* p; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + + GetResource(RES_EVENT, eventMsg, name); + p = strchr(name, '\\'); + if ( p != 0 ) *p = 0; + SetName(name); + + return TRUE; +} + + +// Gestion d'un événement. + +BOOL CKey::EventProcess(const Event &event) +{ + if ( m_state & STATE_DEAD ) return TRUE; + + CControl::EventProcess(event); + + if ( event.event == EVENT_LBUTTONDOWN ) + { + if ( Detect(event.pos) ) + { + m_bCatch = TRUE; + } + else + { + m_bCatch = FALSE; + } + } + + if ( event.event == EVENT_KEYDOWN && m_bCatch ) + { + m_bCatch = FALSE; + + if ( TestKey(event.param) ) // impossible ? + { + m_sound->Play(SOUND_TZOING); + } + else + { + if ( event.param == m_key[0] || + event.param == m_key[1] ) + { + m_key[0] = event.param; + m_key[1] = 0; + } + else + { + m_key[1] = m_key[0]; + m_key[0] = event.param; + } + m_sound->Play(SOUND_CLICK); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + } + return FALSE; + } + + return TRUE; +} + + +// Cherche si une touche est déjà utilisée. + +BOOL CKey::TestKey(int key) +{ + int i, j; + + if ( key == VK_PAUSE || + key == VK_SNAPSHOT ) return TRUE; // touche bloquée + + for ( i=0 ; i<20 ; i++ ) + { + for ( j=0 ; j<2 ; j++ ) + { + if ( key == m_engine->RetKey(i, j) ) // touche utilisée ? + { + m_engine->SetKey(i, j, 0); // plus rien ! + } + } + + if ( m_engine->RetKey(i, 0) == 0 ) // première option libre ? + { + m_engine->SetKey(i, 0, m_engine->RetKey(i, 1)); // shift + m_engine->SetKey(i, 1, 0); + } + } + + return FALSE; // pas utilisée +} + + +// Dessine le bouton. + +void CKey::Draw() +{ + FPOINT iDim, pos; + float zoomExt, zoomInt, h; + int icon; + char text[100]; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + iDim = m_dim; + m_dim.x = 200.0f/640.0f; + + if ( m_state & STATE_SHADOW ) + { + DrawShadow(m_pos, m_dim); + } + + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + + zoomExt = 1.00f; + zoomInt = 0.95f; + + icon = 2; + if ( m_key[0] == 0 && + m_key[1] == 0 ) // pas de raccourci ? + { + icon = 3; + } + if ( m_state & STATE_DEFAULT ) + { + DrawPart(23, 1.3f, 0.0f); + + zoomExt *= 1.15f; + zoomInt *= 1.15f; + } + if ( m_state & STATE_HILIGHT ) + { + icon = 1; + } + if ( m_state & STATE_CHECK ) + { + icon = 0; + } + if ( m_state & STATE_PRESS ) + { + icon = 3; + zoomInt *= 0.9f; + } + if ( (m_state & STATE_ENABLE) == 0 ) + { + icon = 7; + } + if ( m_state & STATE_DEAD ) + { + icon = 17; + } + if ( m_bCatch ) + { + icon = 23; + } + DrawPart(icon, zoomExt, 8.0f/256.0f); // dessine le bouton + + h = m_engine->RetText()->RetHeight(m_fontSize, m_fontType)/2.0f; + + GetKeyName(text, m_key[0]); + if ( m_key[1] != 0 ) + { + GetResource(RES_TEXT, RT_KEY_OR, text+strlen(text)); + GetKeyName(text+strlen(text), m_key[1]); + } + + pos.x = m_pos.x+m_dim.x*0.5f; + pos.y = m_pos.y+m_dim.y*0.5f; + pos.y -= h; + m_engine->RetText()->DrawText(text, pos, m_dim.x, 0, m_fontSize, m_fontStretch, m_fontType, 0); + + m_dim = iDim; + + if ( m_state & STATE_DEAD ) return; + + // Dessine le nom. + pos.x = m_pos.x+(214.0f/640.0f); + pos.y = m_pos.y+m_dim.y*0.5f; + pos.y -= h; + m_engine->RetText()->DrawText(m_name, pos, m_dim.x, 1, m_fontSize, m_fontStretch, m_fontType, 0); +} + + + +void CKey::SetKey(int option, int key) +{ + if ( option < 0 || + option > 1 ) return; + + m_key[option] = key; +} + +int CKey::RetKey(int option) +{ + if ( option < 0 || + option > 1 ) return 0; + + return m_key[option]; +} + diff --git a/src/key.h b/src/key.h new file mode 100644 index 00000000..c26bfb3a --- /dev/null +++ b/src/key.h @@ -0,0 +1,38 @@ +// key.h + +#ifndef _KEY_H_ +#define _KEY_H_ + + +#include "control.h" + + +class CD3DEngine; + + + +class CKey : public CControl +{ +public: + CKey(CInstanceManager* iMan); + ~CKey(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + BOOL EventProcess(const Event &event); + + void Draw(); + + void SetKey(int option, int key); + int RetKey(int option); + +protected: + BOOL TestKey(int key); + +protected: + int m_key[2]; + BOOL m_bCatch; +}; + + +#endif //_KEY_H_ diff --git a/src/label.cpp b/src/label.cpp new file mode 100644 index 00000000..4bb95fae --- /dev/null +++ b/src/label.cpp @@ -0,0 +1,81 @@ +// label.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "text.h" +#include "label.h" + + + + +// Constructeur de l'objet. + +CLabel::CLabel(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); +} + +// Destructeur de l'objet. + +CLabel::~CLabel() +{ + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CLabel::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + return TRUE; +} + + +// Gestion d'un événement. + +BOOL CLabel::EventProcess(const Event &event) +{ +//? CControl::EventProcess(event); + return TRUE; +} + + +// Dessine le bouton. + +void CLabel::Draw() +{ + FPOINT pos; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + pos.y = m_pos.y+m_dim.y/2.0f; + + if ( m_justif > 0 ) + { + pos.x = m_pos.x; + } + if ( m_justif == 0 ) + { + pos.x = m_pos.x+m_dim.x/2.0f; + } + if ( m_justif < 0 ) + { + pos.x = m_pos.x+m_dim.x; + } + m_engine->RetText()->DrawText(m_name, pos, m_dim.x, m_justif, m_fontSize, m_fontStretch, m_fontType, 0); +} + diff --git a/src/label.h b/src/label.h new file mode 100644 index 00000000..c2120a88 --- /dev/null +++ b/src/label.h @@ -0,0 +1,32 @@ +// label.h + +#ifndef _LABEL_H_ +#define _LABEL_H_ + + +#include "control.h" + + +class CD3DEngine; + + + +class CLabel : public CControl +{ +public: + CLabel(CInstanceManager* iMan); + ~CLabel(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + BOOL EventProcess(const Event &event); + + void Draw(); + +protected: + +protected: +}; + + +#endif //_LABEL_H_ diff --git a/src/language.h b/src/language.h new file mode 100644 index 00000000..f77e6da5 --- /dev/null +++ b/src/language.h @@ -0,0 +1,35 @@ +// language.h + +#define _FULL TRUE // CoLoBoT +#define _SCHOOL FALSE // CeeBot-A ou Teen + #define _TEEN FALSE // FALSE si CeeBot-A, TRUE si CeeBot-Teen + #define _EDU FALSE + #define _PERSO FALSE + #define _CEEBOTDEMO FALSE +#define _NET FALSE +#define _DEMO FALSE // DEMO uniquement de CoLoBoT (avec _FULL = FALSE) ! + +#define _FRENCH TRUE +#define _ENGLISH FALSE +#define _GERMAN FALSE +#define _WG FALSE +#define _POLISH FALSE + +#define _NEWLOOK FALSE // FALSE pour CoLoBoT, TRUE pour tous les CeeBot +#define _SOUNDTRACKS FALSE // toujours FALSE depuis que InitAudioTrackVolume plante sous Vista + + +// Vérifications + +#if !_FULL & !_SCHOOL & !_NET & !_DEMO +-> aucune version choisie !!! +#endif + +#if _SCHOOL +#if !_EDU & !_PERSO & !_CEEBOTDEMO +-> EDU ou PERSO ou CEEBOTDEMO ? +#endif +#if _EDU & _PERSO & _CEEBOTDEMO +-> pas EDU et PERSO et CEEBOTDEMO en même temps !!! +#endif +#endif diff --git a/src/light.cpp b/src/light.cpp new file mode 100644 index 00000000..e925fead --- /dev/null +++ b/src/light.cpp @@ -0,0 +1,487 @@ +// light.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "light.h" + + + + + +// Initialise une progression. + +void ProgInit(LightProg &p, float value) +{ + p.starting = value; + p.ending = value; + p.current = value; + p.progress = 0.0f; + p.speed = 100.0f; +} + +// Fait évoluer une progression. + +void ProgFrame(LightProg &p, float rTime) +{ + if ( p.speed < 100.0f ) + { + if ( p.progress < 1.0f ) + { + p.progress += p.speed*rTime; + if ( p.progress > 1.0f ) + { + p.progress = 1.0f; + } + } + + p.current = (p.ending-p.starting)*p.progress + p.starting; + } + else + { + p.current = p.ending; + } +} + +// Change la valeur courante. + +void ProgSet(LightProg &p, float value) +{ + p.starting = p.current; + p.ending = value; + p.progress = 0.0f; +} + + + + + +// Constructeur de l'objet. + +CLight::CLight(CInstanceManager* iMan, CD3DEngine* engine) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_LIGHT, this); + + m_pD3DDevice = 0; + m_engine = engine; + + m_lightUsed = 0; + m_lightTable = (Light*)malloc(sizeof(Light)*D3DMAXLIGHT); + ZeroMemory(m_lightTable, sizeof(Light)*D3DMAXLIGHT); + + m_time = 0.0f; +} + +// Destructeur de l'objet. + +CLight::~CLight() +{ + free(m_lightTable); + m_iMan->DeleteInstance(CLASS_LIGHT, this); +} + + +void CLight::SetD3DDevice(LPDIRECT3DDEVICE7 device) +{ + m_pD3DDevice = device; +} + + +// Supprime toutes les lumières. + +void CLight::FlushLight() +{ + int i; + + for ( i=0 ; iLightEnable(i, FALSE); + } + m_lightUsed = 0; +} + + +// Crée une nouvelle lumière. Retourne son rang ou -1 en cas d'erreur. + +int CLight::CreateLight() +{ + int i; + + for ( i=0 ; i= D3DMAXLIGHT ) return FALSE; + + m_lightTable[lightRank].bUsed = FALSE; + m_pD3DDevice->LightEnable(lightRank, FALSE); + + m_lightUsed = 0; + for ( i=0 ; i= D3DMAXLIGHT ) return FALSE; + + m_lightTable[lightRank].light = light; + + ProgInit(m_lightTable[lightRank].colorRed, m_lightTable[lightRank].light.dcvDiffuse.r); + ProgInit(m_lightTable[lightRank].colorGreen, m_lightTable[lightRank].light.dcvDiffuse.g); + ProgInit(m_lightTable[lightRank].colorBlue, m_lightTable[lightRank].light.dcvDiffuse.b); + + return TRUE; +} + +// Donne les spécifications d'une lumière. + +BOOL CLight::GetLight(int lightRank, D3DLIGHT7 &light) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return FALSE; + + light = m_lightTable[lightRank].light; + return TRUE; +} + + +// Allume ou éteint une lumière. + +BOOL CLight::LightEnable(int lightRank, BOOL bEnable) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return FALSE; + + m_lightTable[lightRank].bEnable = bEnable; + return TRUE; +} + + +// Spécifie le type (TYPE*) des objets inclus par cette lumière. +// Cette lumière n'éclairera donc que ce type d'objets. + +BOOL CLight::SetLightIncluType(int lightRank, D3DTypeObj type) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return FALSE; + + m_lightTable[lightRank].incluType = type; + return TRUE; +} + +// Spécifie le type (TYPE*) des objets exclus par cette lumière. +// Cette lumière n'éclairera donc jamais ce type d'objets. + +BOOL CLight::SetLightExcluType(int lightRank, D3DTypeObj type) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return FALSE; + + m_lightTable[lightRank].excluType = type; + return TRUE; +} + + +// Gestion de la position de la lunière. + +BOOL CLight::SetLightPos(int lightRank, D3DVECTOR pos) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return FALSE; + + m_lightTable[lightRank].light.dvPosition = pos; + return TRUE; +} + +D3DVECTOR CLight::RetLightPos(int lightRank) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return D3DVECTOR(0.0f, 0.0f, 0.0f); + + return m_lightTable[lightRank].light.dvPosition; +} + + +// Gestion de la direction de la lumière. + +BOOL CLight::SetLightDir(int lightRank, D3DVECTOR dir) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return FALSE; + + m_lightTable[lightRank].light.dvDirection = dir; + return TRUE; +} + +D3DVECTOR CLight::RetLightDir(int lightRank) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return D3DVECTOR(0.0f, 0.0f, 0.0f); + + return m_lightTable[lightRank].light.dvDirection; +} + + +// Spécifie la vitesse de changement. + +BOOL CLight::SetLightIntensitySpeed(int lightRank, float speed) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return FALSE; + + m_lightTable[lightRank].intensity.speed = speed; + return TRUE; +} + +// Gestion de l'intensité de la lumière. + +BOOL CLight::SetLightIntensity(int lightRank, float value) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return FALSE; + + ProgSet(m_lightTable[lightRank].intensity, value); + return TRUE; +} + +float CLight::RetLightIntensity(int lightRank) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return 0.0f; + + return m_lightTable[lightRank].intensity.current; +} + + +// Spécifie la vitesse de changement. + +BOOL CLight::SetLightColorSpeed(int lightRank, float speed) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return FALSE; + + m_lightTable[lightRank].colorRed.speed = speed; + m_lightTable[lightRank].colorGreen.speed = speed; + m_lightTable[lightRank].colorBlue.speed = speed; + return TRUE; +} + +// Gestion de la couleur de la lumière. + +BOOL CLight::SetLightColor(int lightRank, D3DCOLORVALUE color) +{ + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) return FALSE; + + ProgSet(m_lightTable[lightRank].colorRed, color.r); + ProgSet(m_lightTable[lightRank].colorGreen, color.g); + ProgSet(m_lightTable[lightRank].colorBlue, color.b); + return TRUE; +} + +D3DCOLORVALUE CLight::RetLightColor(int lightRank) +{ + D3DCOLORVALUE color; + + if ( lightRank < 0 || lightRank >= D3DMAXLIGHT ) + { + color.r = 0.5f; + color.g = 0.5f; + color.b = 0.5f; + color.a = 0.5f; + return color; + } + + color.r = m_lightTable[lightRank].colorRed.current; + color.g = m_lightTable[lightRank].colorGreen.current; + color.b = m_lightTable[lightRank].colorBlue.current; + return color; +} + + +// Adapte la couleur de toutes les lumières. + +void CLight::AdaptLightColor(D3DCOLORVALUE color, float factor) +{ + D3DCOLORVALUE value; + int i; + + for ( i=0 ; iRetPause() ) return; + + m_time += rTime; + + for ( i=0 ; iRetEyePt()-m_engine->RetLookatPt(); + angle = RotateAngle(dir.x, dir.z); + angle += PI*0.5f*i; + m_lightTable[i].light.dvDirection.x = sinf(angle*2.0f); + m_lightTable[i].light.dvDirection.z = cosf(angle*2.0f); + } + } +} + + +// Met à jour toutes les lumières. + +void CLight::LightUpdate() +{ + BOOL bEnable; + float value; + int i; + + for ( i=0 ; iSetLight(i, &m_lightTable[i].light); + m_pD3DDevice->LightEnable(i, bEnable); + } + else + { + m_lightTable[i].light.dcvDiffuse.r = 0.0f; + m_lightTable[i].light.dcvDiffuse.g = 0.0f; + m_lightTable[i].light.dcvDiffuse.b = 0.0f; + + m_pD3DDevice->LightEnable(i, bEnable); + } + } +} + +// Met à jour les lumières pour un type donné. + +void CLight::LightUpdate(D3DTypeObj type) +{ + BOOL bEnable; + int i; + + for ( i=0 ; iLightEnable(i, bEnable); + } + + if ( m_lightTable[i].excluType != TYPENULL ) + { + bEnable = (m_lightTable[i].excluType != type); + m_pD3DDevice->LightEnable(i, bEnable); + } + } +} + + diff --git a/src/light.h b/src/light.h new file mode 100644 index 00000000..c67f2197 --- /dev/null +++ b/src/light.h @@ -0,0 +1,95 @@ +// light.h + +#ifndef _LIGHT_H_ +#define _LIGHT_H_ + + + +class CInstanceManager; +class CD3DEngine; + + +#define D3DMAXLIGHT 100 + + +typedef struct +{ + float starting; + float ending; + float current; + float progress; + float speed; +} +LightProg; + + +typedef struct +{ + char bUsed; // TRUE -> lumière existe + char bEnable; // TRUE -> lumière allumée + + D3DTypeObj incluType; // type de tous les objets inclus + D3DTypeObj excluType; // type de tous les objets exclus + + D3DLIGHT7 light; // configuration de la lumière + + LightProg intensity; // intensité (0..1) + LightProg colorRed; + LightProg colorGreen; + LightProg colorBlue; +} +Light; + + + +class CLight +{ +public: + CLight(CInstanceManager *iMan, CD3DEngine* engine); + ~CLight(); + + void SetD3DDevice(LPDIRECT3DDEVICE7 device); + + void FlushLight(); + int CreateLight(); + BOOL DeleteLight(int lightRank); + BOOL SetLight(int lightRank, const D3DLIGHT7 &light); + BOOL GetLight(int lightRank, D3DLIGHT7 &light); + BOOL LightEnable(int lightRank, BOOL bEnable); + + BOOL SetLightIncluType(int lightRank, D3DTypeObj type); + BOOL SetLightExcluType(int lightRank, D3DTypeObj type); + + BOOL SetLightPos(int lightRank, D3DVECTOR pos); + D3DVECTOR RetLightPos(int lightRank); + + BOOL SetLightDir(int lightRank, D3DVECTOR dir); + D3DVECTOR RetLightDir(int lightRank); + + BOOL SetLightIntensitySpeed(int lightRank, float speed); + BOOL SetLightIntensity(int lightRank, float value); + float RetLightIntensity(int lightRank); + void AdaptLightColor(D3DCOLORVALUE color, float factor); + + BOOL SetLightColorSpeed(int lightRank, float speed); + BOOL SetLightColor(int lightRank, D3DCOLORVALUE color); + D3DCOLORVALUE RetLightColor(int lightRank); + + void FrameLight(float rTime); + void LightUpdate(); + void LightUpdate(D3DTypeObj type); + +protected: + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + LPDIRECT3DDEVICE7 m_pD3DDevice; + + float m_time; + int m_lightUsed; + Light* m_lightTable; +}; + + +#endif //_LIGHT_H_ diff --git a/src/list.cpp b/src/list.cpp new file mode 100644 index 00000000..79a3dd6e --- /dev/null +++ b/src/list.cpp @@ -0,0 +1,858 @@ +// list.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "button.h" +#include "scroll.h" +#include "text.h" +#include "list.h" + + + +#define MARGING 4.0f + + + +// Constructeur de l'objet. + +CList::CList(CInstanceManager* iMan) : CControl(iMan) +{ + int i; + + CControl::CControl(iMan); + + for ( i=0 ; iCreate(pos, dim, 0, EVENT_NULL); + m_eventScroll = m_scroll->RetEventMsg(); + + return MoveAdjust(); +} + +// Ajuste après un changement de dimensions. + +BOOL CList::MoveAdjust() +{ + FPOINT ipos, idim, ppos, ddim; + float marging, h; + int i; + + for ( i=0 ; iRetText()->RetHeight(m_fontSize, m_fontType)*m_expand; + + m_displayLine = (int)(idim.y/h); + if ( m_displayLine == 0 ) return FALSE; + if ( m_displayLine > LISTMAXDISPLAY ) m_displayLine = LISTMAXDISPLAY; + idim.y = h*m_displayLine; + m_dim.y = idim.y+marging*2.0f/480.f; + + ppos.x = ipos.x; + ppos.y = ipos.y+idim.y-h; + ddim.x = idim.x-SCROLL_WIDTH; + ddim.y = h; + for ( i=0 ; iCreate(ppos, ddim, -1, EVENT_NULL); + m_button[i]->SetJustif(1); + m_button[i]->SetState(STATE_SIMPLY); + m_button[i]->SetFontType(m_fontType); + m_button[i]->SetFontSize(m_fontSize); + ppos.y -= h; + + m_eventButton[i] = m_button[i]->RetEventMsg(); + } + + if ( m_scroll != 0 ) + { + ppos.x = ipos.x+idim.x-SCROLL_WIDTH; + ppos.y = ipos.y; + ddim.x = SCROLL_WIDTH; + ddim.y = idim.y; + m_scroll->SetPos(ppos); + m_scroll->SetDim(ddim); + } + + UpdateScroll(); + UpdateButton(); + return TRUE; +} + + +// Retourne le message d'un bouton. + +EventMsg CList::RetEventMsgButton(int i) +{ + if ( i < 0 || i >= m_displayLine ) return EVENT_NULL; + if ( m_button[i] == 0 ) return EVENT_NULL; + return m_button[i]->RetEventMsg(); +} + +// Retourne le message de l'ascenseur. + +EventMsg CList::RetEventMsgScroll() +{ + if ( m_scroll == 0 ) return EVENT_NULL; + return m_scroll->RetEventMsg(); +} + + +void CList::SetPos(FPOINT pos) +{ + CControl::SetPos(pos); +} + +void CList::SetDim(FPOINT dim) +{ + m_dim = dim; + MoveAdjust(); + CControl::SetDim(dim); +} + + +BOOL CList::SetState(int state, BOOL bState) +{ + int i; + + if ( state & STATE_ENABLE ) + { + for ( i=0 ; iSetState(state, bState); + } + if ( m_scroll != 0 ) m_scroll->SetState(state, bState); + } + + return CControl::SetState(state, bState); +} + +BOOL CList::SetState(int state) +{ + int i; + + if ( state & STATE_ENABLE ) + { + for ( i=0 ; iSetState(state); + } + if ( m_scroll != 0 ) m_scroll->SetState(state); + } + + return CControl::SetState(state); +} + +BOOL CList::ClearState(int state) +{ + int i; + + if ( state & STATE_ENABLE ) + { + for ( i=0 ; iClearState(state); + } + if ( m_scroll != 0 ) m_scroll->ClearState(state); + } + + return CControl::ClearState(state); +} + + +// Gestion d'un événement. + +BOOL CList::EventProcess(const Event &event) +{ + int i; + + if ( m_bBlink && // clignotte ? + event.event == EVENT_FRAME ) + { + i = m_selectLine-m_firstLine; + + if ( i >= 0 && i < 4 && + m_button[i] != 0 ) + { + m_blinkTime += event.rTime; + if ( Mod(m_blinkTime, 0.7f) < 0.3f ) + { + m_button[i]->ClearState(STATE_ENABLE); + m_button[i]->ClearState(STATE_CHECK); + } + else + { + m_button[i]->SetState(STATE_ENABLE); + m_button[i]->SetState(STATE_CHECK); + } + } + } + + if ( (m_state & STATE_VISIBLE) == 0 ) return TRUE; + if ( (m_state & STATE_ENABLE) == 0 ) return TRUE; + + if ( event.event == EVENT_KEYDOWN && + event.param == VK_WHEELUP && + Detect(event.pos) ) + { + if ( m_firstLine > 0 ) m_firstLine --; + UpdateScroll(); + UpdateButton(); + return TRUE; + } + if ( event.event == EVENT_KEYDOWN && + event.param == VK_WHEELDOWN && + Detect(event.pos) ) + { + if ( m_firstLine < m_totalLine-m_displayLine ) m_firstLine ++; + UpdateScroll(); + UpdateButton(); + return TRUE; + } + + CControl::EventProcess(event); + + if ( event.event == EVENT_MOUSEMOVE && Detect(event.pos) ) + { + m_engine->SetMouseType(D3DMOUSENORM); + for ( i=0 ; i= m_totalLine ) break; + if ( m_button[i] != 0 ) + { + m_button[i]->EventProcess(event); + } + } + } + + if ( m_bSelectCap ) + { + for ( i=0 ; i= m_totalLine ) break; + if ( m_button[i] != 0 ) + { + if ( !m_button[i]->EventProcess(event) ) return FALSE; + + if ( event.event == m_eventButton[i] ) + { + SetSelect(m_firstLine+i); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); // ligne sélectionnée changée + } + } + } + } + + if ( m_scroll != 0 ) + { + if ( !m_scroll->EventProcess(event) ) return FALSE; + + if ( event.event == m_eventScroll ) + { + MoveScroll(); + UpdateButton(); + } + } + + return TRUE; +} + + +// Dessine la liste. + +void CList::Draw() +{ + FPOINT uv1, uv2, corner, pos, dim, ppos, ddim; + float dp; + int i, j; + char text[100]; + char *pb, *pe; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + if ( m_state & STATE_SHADOW ) + { + DrawShadow(m_pos, m_dim); + } + + dp = 0.5f/256.0f; + + if ( m_icon != -1 ) + { + dim = m_dim; + + if ( m_icon == 0 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + + uv1.x = 128.0f/256.0f; + uv1.y = 64.0f/256.0f; // u-v texture + uv2.x = 160.0f/256.0f; + uv2.y = 96.0f/256.0f; + } + else + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + + uv1.x = 132.0f/256.0f; + uv1.y = 68.0f/256.0f; // u-v texture + uv2.x = 156.0f/256.0f; + uv2.y = 92.0f/256.0f; + + if ( m_button[0] != 0 ) + { + dim = m_button[0]->RetDim(); + dim.y *= m_displayLine; // fond pile-poil derrière + } + } + + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + corner.x = 10.0f/640.0f; + corner.y = 10.0f/480.0f; + DrawIcon(m_pos, dim, uv1, uv2, corner, 8.0f/256.0f); + } + + if ( m_totalLine < m_displayLine ) // boutons pas jusqu'en bas ? + { + i = m_totalLine; + if ( m_button[i] != 0 ) + { + pos = m_button[i]->RetPos(); + dim = m_button[i]->RetDim(); + pos.y += dim.y*1.1f; + dim.y *= 0.4f; + pos.y -= dim.y; + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 120.0f/256.0f; + uv1.y = 64.0f/256.0f; + uv2.x = 128.0f/256.0f; + uv2.y = 48.0f/256.0f; + uv1.x += dp; + uv1.y -= dp; + uv2.x -= dp; + uv2.y += dp; + DrawIcon(pos, dim, uv1, uv2); // ch'tite ombre mignonne + } + } + + for ( i=0 ; i= m_totalLine ) break; + + if ( m_button[i] != 0 ) + { + if ( !m_bBlink && i+m_firstLine < m_totalLine ) + { + m_button[i]->SetState(STATE_ENABLE, m_enable[i+m_firstLine] && (m_state & STATE_ENABLE) ); + } + m_button[i]->Draw(); // dessine une case sans texte + + // dessine le texte dans la case + pos = m_button[i]->RetPos(); + dim = m_button[i]->RetDim(); + if ( m_tabs[0] == 0.0f ) + { + ppos.x = pos.x+dim.y*0.5f; + ppos.y = pos.y+dim.y*0.5f; + ppos.y -= m_engine->RetText()->RetHeight(m_fontSize, m_fontType)/2.0f; + ddim.x = dim.x-dim.y; + DrawCase(m_text[i+m_firstLine], ppos, ddim.x, 1); + } + else + { + ppos.x = pos.x+dim.y*0.5f; + ppos.y = pos.y+dim.y*0.5f; + ppos.y -= m_engine->RetText()->RetHeight(m_fontSize, m_fontType)/2.0f; + pb = m_text[i+m_firstLine]; + for ( j=0 ; j<10 ; j++ ) + { + pe = strchr(pb, '\t'); + if ( pe == 0 ) + { + strcpy(text, pb); + } + else + { + strncpy(text, pb, pe-pb); + text[pe-pb] = 0; + } + DrawCase(text, ppos, m_tabs[j], m_justifs[j]); + + if ( pe == 0 ) break; + ppos.x += m_tabs[j]; + pb = pe+1; + } + } + + if ( (m_state & STATE_EXTEND) && i < m_totalLine ) + { + pos = m_button[i]->RetPos(); + dim = m_button[i]->RetDim(); + pos.x += dim.x-dim.y*0.75f; + dim.x = dim.y*0.75f; + pos.x += 2.0f/640.0f; + pos.y += 2.0f/480.0f; + dim.x -= 4.0f/640.0f; + dim.y -= 4.0f/480.0f; + + if ( m_check[i+m_firstLine] ) + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 64.0f/256.0f; + uv1.y = 0.0f/256.0f; + uv2.x = 96.0f/256.0f; + uv2.y = 32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2); // dessine carré + + m_engine->SetState(D3DSTATETTw); + uv1.x = 0.0f/256.0f; // v + uv1.y = 64.0f/256.0f; + uv2.x = 32.0f/256.0f; + uv2.y = 96.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2); // dessine v + } + else + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATETTw); + if ( i+m_firstLine == m_selectLine ) + { + uv1.x =224.0f/256.0f; // < + uv1.y =192.0f/256.0f; + uv2.x =256.0f/256.0f; + uv2.y =224.0f/256.0f; + } + else + { + uv1.x = 96.0f/256.0f; // x + uv1.y = 32.0f/256.0f; + uv2.x =128.0f/256.0f; + uv2.y = 64.0f/256.0f; + } + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2); // dessine x + } + } + } + } + + if ( m_scroll != 0 ) + { + m_scroll->Draw(); // dessine l'ascenseur + } +} + +// Affiche un texte dans une case. + +void CList::DrawCase(char *text, FPOINT pos, float width, int justif) +{ + if ( justif == 1 ) + { + m_engine->RetText()->DrawText(text, pos, width, 1, m_fontSize, m_fontStretch, m_fontType, 0); + } + else if ( justif == 0 ) + { + pos.x += width/2.0f; + m_engine->RetText()->DrawText(text, pos, width, 0, m_fontSize, m_fontStretch, m_fontType, 0); + } + else + { + pos.x += width; + m_engine->RetText()->DrawText(text, pos, width, -1, m_fontSize, m_fontStretch, m_fontType, 0); + } +} + + +// Vide complètement la liste. + +void CList::Flush() +{ + m_totalLine = 0; + m_selectLine = -1; + m_firstLine = 0; + UpdateButton(); + UpdateScroll(); +} + + +// Spécifie le nombre total de lignes. + +void CList::SetTotal(int i) +{ + m_totalLine = i; +} + +// Retourne le nombre total de lignes. + +int CList::RetTotal() +{ + return m_totalLine; +} + + +// Sélectionne une ligne. + +void CList::SetSelect(int i) +{ + if ( m_bSelectCap ) + { + m_selectLine = i; + } + else + { + m_firstLine = i; + UpdateScroll(); + } + + UpdateButton(); +} + +// Retourne la ligne sélectionnée. + +int CList::RetSelect() +{ + if ( m_bSelectCap ) + { + return m_selectLine; + } + else + { + return m_firstLine; + } +} + + +// Gestion de la capacité à sélectionner une case. + +void CList::SetSelectCap(BOOL bEnable) +{ + m_bSelectCap = bEnable; +} + +BOOL CList::RetSelectCap() +{ + return m_bSelectCap; +} + + +// Fait cligontter une ligne. + +void CList::SetBlink(BOOL bEnable) +{ + int i; + + m_bBlink = bEnable; + m_blinkTime = 0.0f; + + i = m_selectLine-m_firstLine; + + if ( i >= 0 && i < 4 && + m_button[i] != 0 ) + { + if ( !bEnable ) + { + m_button[i]->SetState(STATE_CHECK); + m_button[i]->ClearState(STATE_ENABLE); + } + } +} + +BOOL CList::RetBlink() +{ + return m_bBlink; +} + + +// Spécifie le texte d'une ligne. + +void CList::SetName(int i, char* name) +{ + if ( i < 0 || i >= LISTMAXTOTAL ) return; + + if ( i >= m_totalLine ) + { + m_totalLine = i+1; // allonge la liste + } + + if ( name[0] == 0 ) + { + strcpy(m_text[i], " "); + } + else + { + strcpy(m_text[i], name); + } + UpdateButton(); + UpdateScroll(); +} + +// Retourne le texte d'une ligne. + +char* CList::RetName(int i) +{ + if ( i < 0 || i >= m_totalLine ) return 0; + + return m_text[i]; +} + + +// Spécifie le bit "check" pour une case. + +void CList::SetCheck(int i, BOOL bMode) +{ + if ( i < 0 || i >= m_totalLine ) return; + + m_check[i] = bMode; +} + +// Retourne le bit "check" pour une case. + +BOOL CList::RetCheck(int i) +{ + if ( i < 0 || i >= m_totalLine ) return FALSE; + + return m_check[i]; +} + + +// Spécifie le bit "enable" pour une case. + +void CList::SetEnable(int i, BOOL bMode) +{ + if ( i < 0 || i >= m_totalLine ) return; + + m_enable[i] = bMode; +} + +// Retourne le bit "enable" pour une case. + +BOOL CList::RetEnable(int i) +{ + if ( i < 0 || i >= m_totalLine ) return FALSE; + + return m_enable[i]; +} + + +// Gestion de la position des tabulateurs. + +void CList::SetTabs(int i, float pos, int justif) +{ + if ( i < 0 || i >= 10 ) return; + m_tabs[i] = pos; + m_justifs[i] = justif; +} + +float CList::RetTabs(int i) +{ + if ( i < 0 || i >= 10 ) return 0.0f; + return m_tabs[i]; +} + + +// Déplace l'ascenseur de la liste pour voir la ligne sélectionnée. + +void CList::ShowSelect(BOOL bFixed) +{ + int sel; + + if ( bFixed && + m_selectLine >= m_firstLine && + m_selectLine < m_firstLine+m_displayLine ) return; // tout bon + + sel = m_selectLine; + + // Descend de 1/2*h. + sel += m_displayLine/2; + if ( sel > m_totalLine-1 ) sel = m_totalLine-1; + + // Remonte de h-1. + sel -= m_displayLine-1; + if ( sel < 0 ) sel = 0; + + m_firstLine = sel; + + UpdateButton(); + UpdateScroll(); +} + + +// Met à jour tous les noms des boutons. + +void CList::UpdateButton() +{ + int state, i, j; + + state = CControl::RetState(); + + j = m_firstLine; + for ( i=0 ; iSetState(STATE_CHECK, (j == m_selectLine)); + + if ( j < m_totalLine ) + { +//? m_button[i]->SetName(m_text[j]); + m_button[i]->SetName(" "); // bouton vide + m_button[i]->SetState(STATE_ENABLE, (state & STATE_ENABLE)); + } + else + { + m_button[i]->SetName(" "); // bouton vide + m_button[i]->ClearState(STATE_ENABLE); + } + j ++; + } +} + +// Met à jour l'ascenseur. + +void CList::UpdateScroll() +{ + float ratio, value, step; + + if ( m_scroll == 0 ) return; + + if ( m_totalLine <= m_displayLine ) + { + ratio = 1.0f; + value = 0.0f; + step = 0.0f; + } + else + { + ratio = (float)m_displayLine/m_totalLine; + if ( ratio > 1.0f ) ratio = 1.0f; + + value = (float)m_firstLine/(m_totalLine-m_displayLine); + if ( value < 0.0f ) value = 0.0f; + if ( value > 1.0f ) value = 1.0f; + + step = (float)1.0f/(m_totalLine-m_displayLine); + if ( step < 0.0f ) step = 0.0f; + } + + m_scroll->SetVisibleRatio(ratio); + m_scroll->SetVisibleValue(value); + m_scroll->SetArrowStep(step); +} + +// Mise à jour lorsque l'ascenseur a été bougé. + +void CList::MoveScroll() +{ + float pos; + int n; + + if ( m_scroll == 0 ) return; + + n = m_totalLine-m_displayLine; + pos = m_scroll->RetVisibleValue(); + pos += m_scroll->RetArrowStep()/2.0f; // c'est magique ! + m_firstLine = (int)(pos*n); + if ( m_firstLine < 0 ) m_firstLine = 0; + if ( m_firstLine > n ) m_firstLine = n; +} + + diff --git a/src/list.h b/src/list.h new file mode 100644 index 00000000..f3ff1d5f --- /dev/null +++ b/src/list.h @@ -0,0 +1,100 @@ +// list.h + +#ifndef _LIST_H_ +#define _LIST_H_ + + +#include "control.h" + + +class CD3DEngine; +class CButton; +class CScroll; + + +#define LISTMAXDISPLAY 20 // nb max de lignes visibles +#define LISTMAXTOTAL 100 // nb max de lignes total + + + +class CList : public CControl +{ +public: + CList(CInstanceManager* iMan); + ~CList(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg, float expand); + + void SetPos(FPOINT pos); + void SetDim(FPOINT dim); + + BOOL SetState(int state, BOOL bState); + BOOL SetState(int state); + BOOL ClearState(int state); + + BOOL EventProcess(const Event &event); + void Draw(); + + void Flush(); + + void SetTotal(int i); + int RetTotal(); + + void SetSelect(int i); + int RetSelect(); + + void SetSelectCap(BOOL bEnable); + BOOL RetSelectCap(); + + void SetBlink(BOOL bEnable); + BOOL RetBlink(); + + void SetName(int i, char* name); + char* RetName(int i); + + void SetCheck(int i, BOOL bMode); + BOOL RetCheck(int i); + + void SetEnable(int i, BOOL bEnable); + BOOL RetEnable(int i); + + void SetTabs(int i, float pos, int justif=1); + float RetTabs(int i); + + void ShowSelect(BOOL bFixed); + + EventMsg RetEventMsgButton(int i); + EventMsg RetEventMsgScroll(); + +protected: + BOOL MoveAdjust(); + void UpdateButton(); + void UpdateScroll(); + void MoveScroll(); + void DrawCase(char *text, FPOINT pos, float width, int justif); + +protected: + CButton* m_button[LISTMAXDISPLAY]; + CScroll* m_scroll; + + EventMsg m_eventButton[LISTMAXDISPLAY]; + EventMsg m_eventScroll; + + float m_expand; + int m_totalLine; // nb total de lignes + int m_displayLine; // nb de lignes visibles + int m_selectLine; // ligne sélectionnée + int m_firstLine; // première ligne visible + BOOL m_bBlink; + BOOL m_bSelectCap; + float m_blinkTime; + float m_tabs[10]; + int m_justifs[10]; + + char m_text[LISTMAXTOTAL][100]; + char m_check[LISTMAXTOTAL]; + char m_enable[LISTMAXTOTAL]; +}; + + +#endif //_LIST_H_ diff --git a/src/maindialog.cpp b/src/maindialog.cpp new file mode 100644 index 00000000..b4e67f1f --- /dev/null +++ b/src/maindialog.cpp @@ -0,0 +1,6922 @@ +// maindialog.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "global.h" +#include "language.h" +#include "event.h" +#include "misc.h" +#include "profile.h" +#include "iman.h" +#include "restext.h" +#include "math3d.h" +#include "particule.h" +#include "interface.h" +#include "button.h" +#include "color.h" +#include "check.h" +#include "key.h" +#include "group.h" +#include "image.h" +#include "scroll.h" +#include "slider.h" +#include "list.h" +#include "label.h" +#include "window.h" +#include "edit.h" +#include "editvalue.h" +#include "text.h" +#include "camera.h" +#include "sound.h" +#include "cmdtoken.h" +#include "robotmain.h" +#include "maindialog.h" + + + +#define KEY_VISIBLE 6 // nb de touches redéfinissables visibles + +#if _SCHOOL & _TEEN +#define KEY_TOTAL 13 // nb total de touches redéfinissables +#else +#define KEY_TOTAL 21 // nb total de touches redéfinissables +#endif + +#define WELCOME_LENGTH 6.0f + + + +static int perso_color[3*10*3] = +{ + // cheveux : + 193, 221, 226, // blanc + 255, 255, 181, // jaune + 204, 155, 84, // châtin + 165, 48, 10, // roux + 140, 75, 84, // brun + 83, 64, 51, // brun + 90, 95, 85, // noir + 85, 48, 9, // brun + 60, 0, 23, // noir + 0, 0, 0, // + // combinaison : + 203, 206, 204, // blanc sale + 0, 205, 203, // bleuté + 108, 176, 0, // verdatre + 207, 207, 32, // jaune + 170, 141, 0, // orangé + 108, 84, 0, // brun + 0, 84, 136, // bleuté + 56, 61, 146, // bleuté + 56, 56, 56, // noir + 0, 0, 0, // + // bandes : + 255, 255, 255, // blanc + 255, 255, 0, // jaune + 255, 132, 1, // orange + 255, 0, 255, // magenta + 255, 0, 0, // rouge + 0, 255, 0, // vert + 0, 255, 255, // cyan + 0, 0, 255, // bleu + 70, 51, 84, // foncé + 0, 0, 0, // +}; + + +#if _NET +// Vérifie si la clé "school" est présente dans la base de registres. + +BOOL SchoolCheck() +{ + HKEY key; + char buffer[100]; + LONG i; + DWORD type, len; + + i = RegOpenKeyEx(HKEY_LOCAL_MACHINE, +#if _NEWLOOK + "Software\\Epsitec\\CeeBot\\Setup", +#else + "Software\\Epsitec\\Colobot\\Setup", +#endif + 0, KEY_READ, &key); + if ( i != ERROR_SUCCESS ) return FALSE; + + type = REG_SZ; + len = sizeof(buffer); + i = RegQueryValueEx(key, "School", NULL, &type, (LPBYTE)buffer, &len); + if ( i != ERROR_SUCCESS || type != REG_SZ ) return FALSE; + + if ( strcmp(buffer, "ToBoLoC") != 0 ) return FALSE; + + return TRUE; +} +#endif + + +// Constructeur de l'application robot. + +CMainDialog::CMainDialog(CInstanceManager* iMan) +{ + int i; + + m_iMan = iMan; + m_iMan->AddInstance(CLASS_DIALOG, this); + + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_interface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + m_event = (CEvent*)m_iMan->SearchInstance(CLASS_EVENT); + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + + m_phase = PHASE_NAME; + m_phaseSetup = PHASE_SETUPg; + m_phaseTerm = PHASE_TRAINER; + m_sceneRead[0] = 0; + m_stackRead[0] = 0; + m_sceneName[0] = 0; + m_sceneRank = 0; + m_bSceneSoluce = FALSE; + m_bSimulSetup = FALSE; +#if _NET + m_accessEnable = SchoolCheck(); + m_accessMission= FALSE; + m_accessUser = FALSE; +#else + m_accessEnable = TRUE; + m_accessMission= TRUE; + m_accessUser = TRUE; +#endif + m_bDeleteGamer = TRUE; + + for ( i=0 ; i<10 ; i++ ) + { + m_chap[i] = 0; + m_sel[i] = 0; + } + m_index = 0; + m_maxList = 0; + + ZeroMemory(&m_perso, sizeof(GamerPerso)); + DefPerso(); + + m_bTooltip = TRUE; + m_bGlint = TRUE; + m_bRain = TRUE; + m_bSoluce4 = TRUE; + m_bMovies = TRUE; + m_bNiceReset = TRUE; + m_bHimselfDamage = TRUE; +#if _TEEN + m_bCameraScroll = FALSE; +#else + m_bCameraScroll = TRUE; +#endif + m_bCameraInvertX = FALSE; + m_bCameraInvertY = FALSE; + m_bEffect = TRUE; + m_shotDelay = 0; + + m_glintMouse = FPOINT(0.0f, 0.0f); + m_glintTime = 1000.0f; + + for ( i=0 ; i<10 ; i++ ) + { + m_partiPhase[i] = 0; + m_partiTime[i] = 0.0f; + } + + strcpy(m_sceneDir, "scene"); + strcpy(m_savegameDir, "savegame"); + strcpy(m_publicDir, "program"); + strcpy(m_userDir, "user"); + strcpy(m_filesDir, "files"); + + m_bDialog = FALSE; +} + +// Destructeur de l'application robot. + +CMainDialog::~CMainDialog() +{ +} + + +// Change de phase. + +void CMainDialog::ChangePhase(Phase phase) +{ + CWindow* pw; + CEdit* pe; + CEditValue* pv; + CLabel* pl; + CList* pli; + CCheck* pc; + CScroll* ps; + CSlider* psl; + CButton* pb; + CColor* pco; + CGroup* pg; + CImage* pi; + FPOINT pos, dim, ddim; + float ox, oy, sx, sy; + char name[100]; + char* gamer; + int res, i, j; + + m_camera->SetType(CAMERA_DIALOG); + m_engine->SetOverFront(FALSE); + m_engine->SetOverColor(RetColor(0.0f), D3DSTATETCb); + + if ( phase == PHASE_TERM ) + { + phase = m_phaseTerm; + } + m_phase = phase; // copie l'info de CRobotMain + m_phaseTime = 0.0f; + + dim.x = 32.0f/640.0f; + dim.y = 32.0f/480.0f; + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = (32.0f+2.0f)/640.0f; + sy = (32.0f+2.0f)/480.0f; + + if ( m_phase == PHASE_INIT ) + { + pos.x = 0.35f; + pos.y = 0.10f; + ddim.x = 0.30f; + ddim.y = 0.80f; +#if _TEEN + pw = m_interface->CreateWindows(pos, ddim, 12, EVENT_WINDOW5); +#else + pw = m_interface->CreateWindows(pos, ddim, 10, EVENT_WINDOW5); +#endif + GetResource(RES_TEXT, RT_TITLE_INIT, name); + pw->SetName(name); + + pos.x = 0.35f; + pos.y = 0.60f; + ddim.x = 0.30f; + ddim.y = 0.30f; + pw->CreateGroup(pos, ddim, 5, EVENT_INTERFACE_GLINTl); // coin orange + pos.x = 0.35f; + pos.y = 0.10f; + ddim.x = 0.30f; + ddim.y = 0.30f; + pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // coin bleu + +#if _SCHOOL + ddim.x = 0.20f; + ddim.y = dim.y*2.4f; + pos.x = 0.40f; + pos.y = oy+sy*7.9f; + pg = pw->CreateGroup(pos, ddim, 24, EVENT_LABEL1); // orangé + pg->SetState(STATE_SHADOW); + pos.y = oy+sy*3.9f; + pg = pw->CreateGroup(pos, ddim, 25, EVENT_LABEL1); // orange + pg->SetState(STATE_SHADOW); + ddim.y = dim.y*1.2f; + pos.y = oy+sy*1.9f; + pg = pw->CreateGroup(pos, ddim, 26, EVENT_LABEL1); // rouge + pg->SetState(STATE_SHADOW); +#else + ddim.x = 0.20f; + ddim.y = dim.y*2.4f; + pos.x = 0.40f; + if ( m_accessEnable && m_accessMission ) + { + pos.y = oy+sy*9.1f; + pg = pw->CreateGroup(pos, ddim, 23, EVENT_LABEL1); // jaune + pg->SetState(STATE_SHADOW); + } + pos.y = oy+sy*6.8f; + pg = pw->CreateGroup(pos, ddim, 24, EVENT_LABEL1); // orangé + pg->SetState(STATE_SHADOW); + pos.y = oy+sy*3.9f; + pg = pw->CreateGroup(pos, ddim, 25, EVENT_LABEL1); // orange + pg->SetState(STATE_SHADOW); + ddim.y = dim.y*1.2f; + pos.y = oy+sy*1.9f; + pg = pw->CreateGroup(pos, ddim, 26, EVENT_LABEL1); // rouge + pg->SetState(STATE_SHADOW); +#endif + +#if _SCHOOL + ddim.x = 0.18f; + ddim.y = dim.y*1; + pos.x = 0.41f; + pos.y = oy+sy*9.1f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_TRAINER); + pb->SetState(STATE_SHADOW); + + pos.y = oy+sy*8.0f; +#if _TEEN + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_TEEN); +#else + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_DEFI); +#endif +#if _CEEBOTDEMO + pb->ClearState(STATE_ENABLE); +#endif + pb->SetState(STATE_SHADOW); +#else + ddim.x = 0.18f; + ddim.y = dim.y*1; + pos.x = 0.41f; + + if ( m_accessEnable && m_accessMission ) + { + pos.y = oy+sy*10.3f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MISSION); + pb->SetState(STATE_SHADOW); + + pos.y = oy+sy*9.2f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_FREE); + pb->SetState(STATE_SHADOW); + } + + pos.y = oy+sy*8.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_TRAINER); + pb->SetState(STATE_SHADOW); + + pos.y = oy+sy*6.9f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_DEFI); + pb->SetState(STATE_SHADOW); +#endif + + if ( m_engine->RetSetupMode() ) + { + pos.y = oy+sy*5.1f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_SETUP); + pb->SetState(STATE_SHADOW); + } + + pos.y = oy+sy*4.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_NAME); + pb->SetState(STATE_SHADOW); + + pos.y = oy+sy*2.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_QUIT); + pb->SetState(STATE_SHADOW); + +#if !_DEMO & !_SCHOOL + if ( m_accessEnable && m_accessUser ) + { + pos.x = 447.0f/640.0f; + pos.y = 313.0f/480.0f; + ddim.x = 0.09f; +#if _POLISH + pos.x -= 5.0f/640.0f; + ddim.x += 10.0f/640.0f; +#endif + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_USER); + pb->SetState(STATE_SHADOW); + } +#endif + + if ( m_engine->RetDebugMode() ) + { + pos.x = 139.0f/640.0f; + pos.y = 313.0f/480.0f; + ddim.x = 0.09f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PROTO); + pb->SetState(STATE_SHADOW); + } + + pos.x = 0.40f; + ddim.x = 0.20f; + pos.y = 26.0f/480.0f; + ddim.y = 12.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 1, EVENT_LABEL1); + pg->SetState(STATE_SHADOW); + pos.y -= 5.0f/480.0f; +#if _WG + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL1, " "); +#else +#if _NEWLOOK + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL1, "www.epsitec.ch"); +#else + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL1, "www.ceebot.com"); +#endif +#endif + pl->SetFontType(FONT_COURIER); + pl->SetFontSize(8.0f); + + m_engine->SetBackground("inter01.tga", 0,0, 0,0, TRUE, TRUE); + m_engine->SetBackForce(TRUE); + } + + if ( m_phase == PHASE_NAME ) + { + pos.x = 0.10f; + pos.y = 0.10f; + ddim.x = 0.80f; + ddim.y = 0.80f; + pw = m_interface->CreateWindows(pos, ddim, 12, EVENT_WINDOW5); + GetResource(RES_TEXT, RT_TITLE_NAME, name); + pw->SetName(name); + +#if _NEWLOOK + pos.x = 80.0f/640.0f; + pos.y = 93.0f/480.0f; + ddim.x = 285.0f/640.0f; + ddim.y = 266.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 23, EVENT_LABEL1); // bleu + pg->SetState(STATE_SHADOW); + pos.x = 372.0f/640.0f; + ddim.x = 188.0f/640.0f; + pg = pw->CreateGroup(pos, ddim, 26, EVENT_LABEL1); // violet + pg->SetState(STATE_SHADOW); +#endif + + pos.x = 0.10f; + pos.y = 0.40f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 5, EVENT_INTERFACE_GLINTl); // coin orange + pos.x = 0.40f; + pos.y = 0.10f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // coin bleu + + pos.x = 60.0f/640.0f; + pos.y = 313.0f/480.0f; + ddim.x = 120.0f/640.0f; + ddim.y = 32.0f/480.0f; + GetResource(RES_EVENT, EVENT_INTERFACE_NLABEL, name); + pl = pw->CreateLabel(pos, ddim, -1, EVENT_INTERFACE_NLABEL, name); + pl->SetJustif(-1); + + pos.x = 200.0f/640.0f; + pos.y = 320.0f/480.0f; + ddim.x = 160.0f/640.0f; + ddim.y = 32.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 7, EVENT_LABEL1); + pg->SetState(STATE_SHADOW); + + pos.x = 207.0f/640.0f; + pos.y = 328.0f/480.0f; + ddim.x = 144.0f/640.0f; + ddim.y = 18.0f/480.0f; + pe = pw->CreateEdit(pos, ddim, 0, EVENT_INTERFACE_NEDIT); + pe->SetMaxChar(15); + gamer = m_main->RetGamerName(); + if ( gamer[0] == 0 ) + { + GetResource(RES_TEXT, RT_NAME_DEFAULT, name); + } + else + { + strcpy(name, gamer); + } + pe->SetText(name); + pe->SetCursor(strlen(name), 0); + pe->SetFocus(TRUE); + + pos.x = 380.0f/640.0f; + pos.y = 320.0f/480.0f; + ddim.x =100.0f/640.0f; + ddim.y = 32.0f/480.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_NOK); + pb->SetState(STATE_SHADOW); + +#if !_TEEN + pos.x = 380.0f/640.0f; + pos.y = 250.0f/480.0f; + ddim.x =100.0f/640.0f; + ddim.y = 52.0f/480.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PERSO); + pb->SetState(STATE_SHADOW); +#endif + + pos.x = 200.0f/640.0f; + pos.y = 150.0f/480.0f; + ddim.x = 160.0f/640.0f; + ddim.y = 160.0f/480.0f; + pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_NLIST); + pli->SetState(STATE_SHADOW); + + if ( m_bDeleteGamer ) + { + pos.x = 200.0f/640.0f; + pos.y = 100.0f/480.0f; + ddim.x = 160.0f/640.0f; + ddim.y = 32.0f/480.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_NDELETE); + pb->SetState(STATE_SHADOW); + } + + pos.x = 380.0f/640.0f; + pos.y = 100.0f/480.0f; + ddim.x =100.0f/640.0f; + ddim.y = 32.0f/480.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_NCANCEL); + pb->SetState(STATE_SHADOW); + + ReadNameList(); + UpdateNameList(); + UpdateNameControl(); + UpdateNameFace(); + + m_engine->SetBackground("inter01.tga", 0,0, 0,0, TRUE, TRUE); + m_engine->SetBackForce(TRUE); + } + + if ( m_phase == PHASE_PERSO ) + { + pos.x = 0.10f; + pos.y = 0.10f; + ddim.x = 0.80f; + ddim.y = 0.80f; + pw = m_interface->CreateWindows(pos, ddim, 12, EVENT_WINDOW5); + GetResource(RES_TEXT, RT_TITLE_PERSO, name); + pw->SetName(name); + +#if _NEWLOOK + pos.x = 95.0f/640.0f; + pos.y = 66.0f/480.0f; + ddim.x = 443.0f/640.0f; + ddim.y = 42.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 26, EVENT_LABEL1); // violet + pg->SetState(STATE_SHADOW); +#endif + + pos.x = 0.10f; + pos.y = 0.40f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 5, EVENT_INTERFACE_GLINTl); // coin orange + pos.x = 0.40f; + pos.y = 0.10f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // coin bleu + + pos.x = 95.0f/640.0f; + pos.y = 108.0f/480.0f; + ddim.x = 220.0f/640.0f; + ddim.y = 274.0f/480.0f; + pw->CreateGroup(pos, ddim, 17, EVENT_NULL); // cadre + + pos.x = 100.0f/640.0f; + pos.y = 364.0f/480.0f; + ddim.x = 210.0f/640.0f; + ddim.y = 14.0f/480.0f; + pw->CreateGroup(pos, ddim, 3, EVENT_NULL); // transparent -> gris + + pos.x = 120.0f/640.0f; + pos.y = 364.0f/480.0f; + ddim.x = 80.0f/640.0f; + ddim.y = 28.0f/480.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PHEAD); + pb->SetState(STATE_SHADOW); + pb->SetState(STATE_CARD); + + pos.x = 210.0f/640.0f; + pos.y = 364.0f/480.0f; + ddim.x = 80.0f/640.0f; + ddim.y = 28.0f/480.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PBODY); + pb->SetState(STATE_SHADOW); + pb->SetState(STATE_CARD); + + pos.x = 100.0f/640.0f; + pos.y = 354.0f/480.0f; + ddim.x = 210.0f/640.0f; + ddim.y = 10.0f/480.0f; + pw->CreateGroup(pos, ddim, 1, EVENT_INTERFACE_GLINTb); // barre orange + pos.x = 100.0f/640.0f; + pos.y = 154.0f/480.0f; + ddim.x = 210.0f/640.0f; + ddim.y = 200.0f/480.0f; + pw->CreateGroup(pos, ddim, 2, EVENT_INTERFACE_GLINTu); // orange -> transparent + + // Visage + pos.x = 340.0f/640.0f; + pos.y = 356.0f/480.0f; + ddim.x = 200.0f/640.0f; + ddim.y = 16.0f/480.0f; + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL11, ""); + pl->SetJustif(1); + + pos.x = 340.0f/640.0f; + pos.y = 312.0f/480.0f; + ddim.x = 44.0f/640.0f; + ddim.y = 44.0f/480.0f; + pb = pw->CreateButton(pos, ddim, 43, EVENT_INTERFACE_PFACE1); + pb->SetState(STATE_SHADOW); + pos.x += 50.0f/640.0f; + pb = pw->CreateButton(pos, ddim, 46, EVENT_INTERFACE_PFACE4); + pb->SetState(STATE_SHADOW); + pos.x += 50.0f/640.0f; + pb = pw->CreateButton(pos, ddim, 45, EVENT_INTERFACE_PFACE3); + pb->SetState(STATE_SHADOW); + pos.x += 50.0f/640.0f; + pb = pw->CreateButton(pos, ddim, 44, EVENT_INTERFACE_PFACE2); + pb->SetState(STATE_SHADOW); + + // Lunettes + pos.x = 340.0f/640.0f; + pos.y = 270.0f/480.0f; + ddim.x = 200.0f/640.0f; + ddim.y = 16.0f/480.0f; + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL12, ""); + pl->SetJustif(1); + + pos.x = 340.0f/640.0f; + pos.y = 240.0f/480.0f; + ddim.x = 30.0f/640.0f; + ddim.y = 30.0f/480.0f; + for ( i=0 ; i<6 ; i++ ) + { + int ti[6] = {11, 179, 180, 181, 182, 183}; + pb = pw->CreateButton(pos, ddim, ti[i], (EventMsg)(EVENT_INTERFACE_PGLASS0+i)); + pb->SetState(STATE_SHADOW); + pos.x += (30.0f+2.8f)/640.0f; + } + + // Couleur a + pos.x = 340.0f/640.0f; + pos.y = 300.0f/480.0f; + ddim.x = 200.0f/640.0f; + ddim.y = 16.0f/480.0f; + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL14, ""); + pl->SetJustif(1); + + pos.y = 282.0f/480.0f; + ddim.x = 18.0f/640.0f; + ddim.y = 18.0f/480.0f; + for ( j=0 ; j<3 ; j++ ) + { + pos.x = 340.0f/640.0f; + for ( i=0 ; i<3 ; i++ ) + { + pco = pw->CreateColor(pos, ddim, -1, (EventMsg)(EVENT_INTERFACE_PC0a+j*3+i)); + pco->SetState(STATE_SHADOW); + pos.x += 20.0f/640.0f; + } + pos.y -= 20.0f/480.0f; + } + + pos.x = 420.0f/640.0f; + pos.y = 282.0f/480.0f; + ddim.x = 100.0f/640.0f; + ddim.y = 18.0f/480.0f; + for ( i=0 ; i<3 ; i++ ) + { + psl = pw->CreateSlider(pos, ddim, 0, (EventMsg)(EVENT_INTERFACE_PCRa+i)); + psl->SetState(STATE_SHADOW); + psl->SetLimit(0.0f, 255.0f); + psl->SetArrowStep(16.0f); + pos.y -= 20.0f/480.0f; + } + + // Couleur b + pos.x = 340.0f/640.0f; + pos.y = 192.0f/480.0f; + ddim.x = 200.0f/640.0f; + ddim.y = 16.0f/480.0f; + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL13, ""); + pl->SetJustif(1); + + pos.y = 174.0f/480.0f; + ddim.x = 18.0f/640.0f; + ddim.y = 18.0f/480.0f; + for ( j=0 ; j<3 ; j++ ) + { + pos.x = 340.0f/640.0f; + for ( i=0 ; i<3 ; i++ ) + { + pco = pw->CreateColor(pos, ddim, -1, (EventMsg)(EVENT_INTERFACE_PC0b+j*3+i)); + pco->SetState(STATE_SHADOW); + pos.x += 20.0f/640.0f; + } + pos.y -= 20.0f/480.0f; + } + + pos.x = 420.0f/640.0f; + pos.y = 174.0f/480.0f; + ddim.x = 100.0f/640.0f; + ddim.y = 18.0f/480.0f; + for ( i=0 ; i<3 ; i++ ) + { + psl = pw->CreateSlider(pos, ddim, 0, (EventMsg)(EVENT_INTERFACE_PCRb+i)); + psl->SetState(STATE_SHADOW); + psl->SetLimit(0.0f, 255.0f); + psl->SetArrowStep(16.0f); + pos.y -= 20.0f/480.0f; + } + + // Rotation + pos.x = 100.0f/640.0f; + pos.y = 113.0f/480.0f; + ddim.x = 20.0f/640.0f; + ddim.y = 20.0f/480.0f; + pb = pw->CreateButton(pos, ddim, 55, EVENT_INTERFACE_PLROT); // < + pb->SetState(STATE_SHADOW); + pb->SetRepeat(TRUE); + + pos.x = 290.0f/640.0f; + pos.y = 113.0f/480.0f; + ddim.x = 20.0f/640.0f; + ddim.y = 20.0f/480.0f; + pb = pw->CreateButton(pos, ddim, 48, EVENT_INTERFACE_PRROT); // > + pb->SetState(STATE_SHADOW); + pb->SetRepeat(TRUE); + + pos.x = 100.0f/640.0f; + pos.y = 70.0f/480.0f; + ddim.x = 100.0f/640.0f; + ddim.y = 32.0f/480.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_POK); + pb->SetState(STATE_SHADOW); + + pos.x = 210.0f/640.0f; + pos.y = 70.0f/480.0f; + ddim.x =100.0f/640.0f; + ddim.y = 32.0f/480.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PCANCEL); + pb->SetState(STATE_SHADOW); + + pos.x = 340.0f/640.0f; + pos.y = 70.0f/480.0f; + ddim.x =194.0f/640.0f; + ddim.y = 32.0f/480.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PDEF); + pb->SetState(STATE_SHADOW); + + m_persoCopy = m_perso; + m_persoTab = 0; + m_persoAngle = -0.6f; + UpdatePerso(); + m_main->ScenePerso(); + CameraPerso(); + } + + if ( m_phase == PHASE_TRAINER || + m_phase == PHASE_DEFI || + m_phase == PHASE_MISSION || + m_phase == PHASE_FREE || + m_phase == PHASE_TEEN || + m_phase == PHASE_USER || + m_phase == PHASE_PROTO ) + { + if ( m_phase == PHASE_TRAINER ) m_index = 0; + if ( m_phase == PHASE_DEFI ) m_index = 1; + if ( m_phase == PHASE_MISSION ) m_index = 2; + if ( m_phase == PHASE_FREE ) m_index = 3; + if ( m_phase == PHASE_USER ) m_index = 4; + if ( m_phase == PHASE_PROTO ) m_index = 5; + if ( m_phase == PHASE_TEEN ) m_index = 6; + + if ( m_phase == PHASE_FREE ) + { + strcpy(m_sceneName, "scene"); + ReadGamerInfo(); + m_accessChap = RetChapPassed(); + } + + if ( m_phase == PHASE_TRAINER ) strcpy(m_sceneName, "train"); + if ( m_phase == PHASE_DEFI ) strcpy(m_sceneName, "defi" ); + if ( m_phase == PHASE_MISSION ) strcpy(m_sceneName, "scene"); + if ( m_phase == PHASE_FREE ) strcpy(m_sceneName, "free"); + if ( m_phase == PHASE_TEEN ) strcpy(m_sceneName, "teen"); + if ( m_phase == PHASE_USER ) strcpy(m_sceneName, "user"); + if ( m_phase == PHASE_PROTO ) strcpy(m_sceneName, "proto"); + + ReadGamerInfo(); + + pos.x = 0.10f; + pos.y = 0.10f; + ddim.x = 0.80f; + ddim.y = 0.80f; + pw = m_interface->CreateWindows(pos, ddim, 12, EVENT_WINDOW5); + pw->SetClosable(TRUE); + if ( m_phase == PHASE_TRAINER ) res = RT_TITLE_TRAINER; + if ( m_phase == PHASE_DEFI ) res = RT_TITLE_DEFI; + if ( m_phase == PHASE_MISSION ) res = RT_TITLE_MISSION; + if ( m_phase == PHASE_FREE ) res = RT_TITLE_FREE; + if ( m_phase == PHASE_TEEN ) res = RT_TITLE_TEEN; + if ( m_phase == PHASE_USER ) res = RT_TITLE_USER; + if ( m_phase == PHASE_PROTO ) res = RT_TITLE_PROTO; + GetResource(RES_TEXT, res, name); + pw->SetName(name); + +#if _NEWLOOK + pos.x = 100.0f/640.0f; + pos.y = 226.0f/480.0f; + ddim.x = 216.0f/640.0f; + ddim.y = 160.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 23, EVENT_LABEL1); // bleu + pg->SetState(STATE_SHADOW); + pos.x = 322.0f/640.0f; + pg = pw->CreateGroup(pos, ddim, 24, EVENT_LABEL1); // cyan + pg->SetState(STATE_SHADOW); + + pos.x = 100.0f/640.0f; + pos.y = 122.0f/480.0f; + ddim.x = 438.0f/640.0f; + ddim.y = 98.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 25, EVENT_LABEL1); // vert + pg->SetState(STATE_SHADOW); + pos.y = 66.0f/480.0f; + ddim.y = 42.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 26, EVENT_LABEL1); // violet + pg->SetState(STATE_SHADOW); +#endif + + pos.x = 0.10f; + pos.y = 0.40f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 5, EVENT_INTERFACE_GLINTl); // coin orange + pos.x = 0.40f; + pos.y = 0.10f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // coin bleu + + // Affiche la liste des chapitres : + pos.x = ox+sx*3; + pos.y = oy+sy*10.5f; + ddim.x = dim.x*7.5f; + ddim.y = dim.y*0.6f; + if ( m_phase == PHASE_TRAINER ) res = RT_PLAY_CHAPt; + if ( m_phase == PHASE_DEFI ) res = RT_PLAY_CHAPd; + if ( m_phase == PHASE_MISSION ) res = RT_PLAY_CHAPm; + if ( m_phase == PHASE_FREE ) res = RT_PLAY_CHAPf; + if ( m_phase == PHASE_TEEN ) res = RT_PLAY_CHAPte; + if ( m_phase == PHASE_USER ) res = RT_PLAY_CHAPu; + if ( m_phase == PHASE_PROTO ) res = RT_PLAY_CHAPp; + GetResource(RES_TEXT, res, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL11, name); + pl->SetJustif(1); + + pos.y = oy+sy*6.7f; + ddim.y = dim.y*4.5f; + ddim.x = dim.x*6.5f; + pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_CHAP); + pli->SetState(STATE_SHADOW); + UpdateSceneChap(m_chap[m_index]); + if ( m_phase != PHASE_USER ) pli->SetState(STATE_EXTEND); + + // Affiche la liste des missions : + pos.x = ox+sx*9.5f; + pos.y = oy+sy*10.5f; + ddim.x = dim.x*7.5f; + ddim.y = dim.y*0.6f; + if ( m_phase == PHASE_TRAINER ) res = RT_PLAY_LISTt; + if ( m_phase == PHASE_DEFI ) res = RT_PLAY_LISTd; + if ( m_phase == PHASE_MISSION ) res = RT_PLAY_LISTm; + if ( m_phase == PHASE_FREE ) res = RT_PLAY_LISTf; + if ( m_phase == PHASE_TEEN ) res = RT_PLAY_LISTk; + if ( m_phase == PHASE_USER ) res = RT_PLAY_LISTu; + if ( m_phase == PHASE_PROTO ) res = RT_PLAY_LISTp; + GetResource(RES_TEXT, res, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL12, name); + pl->SetJustif(1); + + pos.y = oy+sy*6.7f; + ddim.y = dim.y*4.5f; + ddim.x = dim.x*6.5f; + pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_LIST); + pli->SetState(STATE_SHADOW); + UpdateSceneList(m_chap[m_index], m_sel[m_index]); + if ( m_phase != PHASE_USER ) pli->SetState(STATE_EXTEND); + pos = pli->RetPos(); + ddim = pli->RetDim(); + + // Affiche le résumé : + pos.x = ox+sx*3; + pos.y = oy+sy*5.4f; + ddim.x = dim.x*6.5f; + ddim.y = dim.y*0.6f; + GetResource(RES_TEXT, RT_PLAY_RESUME, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL13, name); + pl->SetJustif(1); + + pos.x = ox+sx*3; + pos.y = oy+sy*3.6f; + ddim.x = dim.x*13.4f; + ddim.y = dim.y*1.9f; + pe = pw->CreateEdit(pos, ddim, 0, EVENT_INTERFACE_RESUME); + pe->SetState(STATE_SHADOW); + pe->SetMaxChar(500); + pe->SetEditCap(FALSE); // juste pour voir + pe->SetHiliteCap(FALSE); + + // Affiche le bouton "soluce" : + if ( m_phase != PHASE_TRAINER && + m_phase != PHASE_FREE && + m_phase != PHASE_TEEN ) + { + pos.x = ox+sx*9.5f; + pos.y = oy+sy*5.8f; + ddim.x = dim.x*6.5f; + ddim.y = dim.y*0.5f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_SOLUCE); + pc->SetState(STATE_SHADOW); + pc->ClearState(STATE_CHECK); + } + m_bSceneSoluce = FALSE; + + UpdateSceneResume((m_chap[m_index]+1)*100+(m_sel[m_index]+1)); + + if ( m_phase == PHASE_MISSION || + m_phase == PHASE_FREE || + m_phase == PHASE_USER ) + { + pos.x = ox+sx*9.5f; + pos.y = oy+sy*2; + ddim.x = dim.x*3.7f; + ddim.y = dim.y*1; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PLAY); + pb->SetState(STATE_SHADOW); + if ( m_maxList == 0 ) + { + pb->ClearState(STATE_ENABLE); + } + + pos.x += dim.x*4.0f; + ddim.x = dim.x*2.5f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_READ); + pb->SetState(STATE_SHADOW); + if ( !IsIOReadScene() ) // aucun fichier à lire ? + { + pb->ClearState(STATE_ENABLE); + } + } + else + { + pos.x = ox+sx*9.5f; + pos.y = oy+sy*2; + ddim.x = dim.x*6.5f; + ddim.y = dim.y*1; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PLAY); + pb->SetState(STATE_SHADOW); + if ( m_maxList == 0 ) + { + pb->ClearState(STATE_ENABLE); + } + } + + pos.x = ox+sx*3; + ddim.x = dim.x*4; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_BACK); + pb->SetState(STATE_SHADOW); + + m_engine->SetBackground("inter01.tga", 0,0, 0,0, TRUE, TRUE); + m_engine->SetBackForce(TRUE); + } + + if ( m_phase == PHASE_SETUPd || + m_phase == PHASE_SETUPg || + m_phase == PHASE_SETUPp || + m_phase == PHASE_SETUPc || + m_phase == PHASE_SETUPs || + m_phase == PHASE_SETUPds || + m_phase == PHASE_SETUPgs || + m_phase == PHASE_SETUPps || + m_phase == PHASE_SETUPcs || + m_phase == PHASE_SETUPss ) + { + if ( m_phase == PHASE_SETUPds ) + { + m_phaseSetup = PHASE_SETUPd; + m_bSimulSetup = TRUE; + } + else if ( m_phase == PHASE_SETUPgs ) + { + m_phaseSetup = PHASE_SETUPg; + m_bSimulSetup = TRUE; + } + else if ( m_phase == PHASE_SETUPps ) + { + m_phaseSetup = PHASE_SETUPp; + m_bSimulSetup = TRUE; + } + else if ( m_phase == PHASE_SETUPcs ) + { + m_phaseSetup = PHASE_SETUPc; + m_bSimulSetup = TRUE; + } + else if ( m_phase == PHASE_SETUPss ) + { + m_phaseSetup = PHASE_SETUPs; + m_bSimulSetup = TRUE; + } + else + { + m_phaseSetup = m_phase; + m_bSimulSetup = FALSE; + } + + pos.x = 0.10f; + pos.y = 0.10f; + ddim.x = 0.80f; + ddim.y = 0.80f; + pw = m_interface->CreateWindows(pos, ddim, 12, EVENT_WINDOW5); + pw->SetClosable(TRUE); + GetResource(RES_TEXT, RT_TITLE_SETUP, name); + pw->SetName(name); + + pos.x = 0.70f; + pos.y = 0.10f; + ddim.x = 0.20f; + ddim.y = 0.20f; + pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // coin bleu + + pos.x = 0.10f; + ddim.x = 0.80f; + pos.y = 0.76f; + ddim.y = 0.05f; + pw->CreateGroup(pos, ddim, 3, EVENT_NULL); // transparent -> gris + +#if _NEWLOOK + if ( m_phase == PHASE_SETUPd || // setup/display ? + m_phase == PHASE_SETUPds ) + { + pos.x = 100.0f/640.0f; + pos.y = 130.0f/480.0f; + ddim.x = 216.0f/640.0f; + ddim.y = 212.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 23, EVENT_LABEL1); // bleu + pg->SetState(STATE_SHADOW); + pos.x = 324.0f/640.0f; + pg = pw->CreateGroup(pos, ddim, 24, EVENT_LABEL1); // cyan + pg->SetState(STATE_SHADOW); + } + if ( m_phase == PHASE_SETUPg || // setup/graphic ? + m_phase == PHASE_SETUPgs ) + { + pos.x = 100.0f/640.0f; + pos.y = 130.0f/480.0f; + ddim.x = 174.0f/640.0f; + ddim.y = 212.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 23, EVENT_LABEL1); // bleu + pg->SetState(STATE_SHADOW); + pos.x = 282.0f/640.0f; + ddim.x = 258.0f/640.0f; + pg = pw->CreateGroup(pos, ddim, 24, EVENT_LABEL1); // cyan + pg->SetState(STATE_SHADOW); + } + if ( m_phase == PHASE_SETUPp || // setup/jeu ? + m_phase == PHASE_SETUPps ) + { + pos.x = 100.0f/640.0f; + pos.y = 130.0f/480.0f; + ddim.x = 226.0f/640.0f; + ddim.y = 212.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 23, EVENT_LABEL1); // bleu + pg->SetState(STATE_SHADOW); + pos.x = 334.0f/640.0f; + ddim.x = 206.0f/640.0f; + pg = pw->CreateGroup(pos, ddim, 24, EVENT_LABEL1); // cyan + pg->SetState(STATE_SHADOW); + } + if ( m_phase == PHASE_SETUPc || // setup/commande ? + m_phase == PHASE_SETUPcs ) + { + pos.x = 100.0f/640.0f; + pos.y = 125.0f/480.0f; + ddim.x = 440.0f/640.0f; + ddim.y = 222.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 23, EVENT_LABEL1); // bleu + pg->SetState(STATE_SHADOW); + } + if ( m_phase == PHASE_SETUPs || // setup/sound ? + m_phase == PHASE_SETUPss ) + { + pos.x = 100.0f/640.0f; + pos.y = 130.0f/480.0f; + ddim.x = 216.0f/640.0f; + ddim.y = 212.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 23, EVENT_LABEL1); // bleu + pg->SetState(STATE_SHADOW); + pos.x = 324.0f/640.0f; + pg = pw->CreateGroup(pos, ddim, 24, EVENT_LABEL1); // cyan + pg->SetState(STATE_SHADOW); + } + + pos.x = 100.0f/640.0f; + pos.y = 66.0f/480.0f; + ddim.x = 440.0f/640.0f; + ddim.y = 42.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 26, EVENT_LABEL1); // violet + pg->SetState(STATE_SHADOW); +#endif + + ddim.x = 0.78f/5-0.01f; + ddim.y = 0.06f; + pos.x = 0.115f; + pos.y = 0.76f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_SETUPd); + pb->SetState(STATE_SHADOW); + pb->SetState(STATE_CARD); + pb->SetState(STATE_CHECK, (m_phase == PHASE_SETUPd || m_phase == PHASE_SETUPds)); + + pos.x += ddim.x+0.01f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_SETUPg); + pb->SetState(STATE_SHADOW); + pb->SetState(STATE_CARD); + pb->SetState(STATE_CHECK, (m_phase == PHASE_SETUPg || m_phase == PHASE_SETUPgs)); + + pos.x += ddim.x+0.01f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_SETUPp); + pb->SetState(STATE_SHADOW); + pb->SetState(STATE_CARD); + pb->SetState(STATE_CHECK, (m_phase == PHASE_SETUPp || m_phase == PHASE_SETUPps)); + + pos.x += ddim.x+0.01f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_SETUPc); + pb->SetState(STATE_SHADOW); + pb->SetState(STATE_CARD); + pb->SetState(STATE_CHECK, (m_phase == PHASE_SETUPc || m_phase == PHASE_SETUPcs)); + + pos.x += ddim.x+0.01f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_SETUPs); + pb->SetState(STATE_SHADOW); + pb->SetState(STATE_CARD); + pb->SetState(STATE_CHECK, (m_phase == PHASE_SETUPs || m_phase == PHASE_SETUPss)); + + pos.x = 0.10f; + ddim.x = 0.80f; + pos.y = 0.34f; + ddim.y = 0.42f; + pw->CreateGroup(pos, ddim, 2, EVENT_INTERFACE_GLINTu); // orange -> transparent + pos.x = 0.10f+(6.0f/640.0f); + ddim.x = 0.80f-(11.0f/640.0f); + pos.y = 0.74f; + ddim.y = 0.02f; + pw->CreateGroup(pos, ddim, 1, EVENT_INTERFACE_GLINTb); // barre orange + + ddim.x = dim.x*4; + ddim.y = dim.y*1; + pos.x = ox+sx*3; + pos.y = oy+sy*2; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_BACK); + pb->SetState(STATE_SHADOW); + + if ( !m_bSimulSetup ) + { + m_engine->SetBackground("inter01.tga", 0,0, 0,0, TRUE, TRUE); + m_engine->SetBackForce(TRUE); + } + } + + if ( m_phase == PHASE_SETUPd || // setup/display ? + m_phase == PHASE_SETUPds ) + { + pos.x = ox+sx*3; + pos.y = oy+sy*9; + ddim.x = dim.x*6; + ddim.y = dim.y*1; + GetResource(RES_TEXT, RT_SETUP_DEVICE, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL1, name); + pl->SetJustif(1); + + pos.x = ox+sx*3; + pos.y = oy+sy*5.2f; + ddim.x = dim.x*6; + ddim.y = dim.y*4.5f; + pli = pw->CreateList(pos, ddim, 0, EVENT_LIST1); + pli->SetState(STATE_SHADOW); + UpdateDisplayDevice(); + + pos.x = ox+sx*10; + pos.y = oy+sy*9; + ddim.x = dim.x*6; + ddim.y = dim.y*1; + GetResource(RES_TEXT, RT_SETUP_MODE, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL2, name); + pl->SetJustif(1); + + m_setupFull = m_engine->RetFullScreen(); + pos.x = ox+sx*10; + pos.y = oy+sy*5.2f; + ddim.x = dim.x*6; + ddim.y = dim.y*4.5f; + pli = pw->CreateList(pos, ddim, 0, EVENT_LIST2); + pli->SetState(STATE_SHADOW); + UpdateDisplayMode(); + pli->SetState(STATE_ENABLE, m_setupFull); + + ddim.x = dim.x*4; + ddim.y = dim.y*0.5f; + pos.x = ox+sx*3; + pos.y = oy+sy*4.1f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_FULL); + pc->SetState(STATE_SHADOW); + pc->SetState(STATE_CHECK, m_setupFull); + + ddim.x = dim.x*6; + ddim.y = dim.y*1; + pos.x = ox+sx*10; + pos.y = oy+sy*2; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_APPLY); + pb->SetState(STATE_SHADOW); + UpdateApply(); + } + + if ( m_phase == PHASE_SETUPg || // setup/graphic ? + m_phase == PHASE_SETUPgs ) + { + ddim.x = dim.x*6; + ddim.y = dim.y*0.5f; + pos.x = ox+sx*3; + pos.y = 0.65f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_SHADOW); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + if ( !m_bSimulSetup ) + { + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_GROUND); + pc->SetState(STATE_SHADOW); + if ( m_engine->IsVideo8MB() ) pc->ClearState(STATE_ENABLE); + } + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_DIRTY); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_SKY); + pc->SetState(STATE_SHADOW); + if ( m_engine->IsVideo8MB() ) pc->ClearState(STATE_ENABLE); + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_LENS); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_PLANET); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_FOG); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + if ( !m_bSimulSetup ) + { + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_LIGHT); + pc->SetState(STATE_SHADOW); + } + + pos.x = ox+sx*8.5f; + pos.y = 0.65f; + ddim.x = dim.x*2.2f; + ddim.y = 18.0f/480.0f; + pv = pw->CreateEditValue(pos, ddim, 0, EVENT_INTERFACE_PARTI); + pv->SetState(STATE_SHADOW); + pv->SetMinValue(0.0f); + pv->SetMaxValue(2.0f); + pos.x += 0.13f; + pos.y -= 0.015f; + ddim.x = 0.40f; + GetResource(RES_EVENT, EVENT_INTERFACE_PARTI, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL10, name); + pl->SetJustif(1); + + pos.x = ox+sx*8.5f; + pos.y = 0.59f; + ddim.x = dim.x*2.2f; + ddim.y = 18.0f/480.0f; + pv = pw->CreateEditValue(pos, ddim, 0, EVENT_INTERFACE_CLIP); + pv->SetState(STATE_SHADOW); + pv->SetMinValue(0.5f); + pv->SetMaxValue(2.0f); + pos.x += 0.13f; + pos.y -= 0.015f; + ddim.x = 0.40f; + GetResource(RES_EVENT, EVENT_INTERFACE_CLIP, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL11, name); + pl->SetJustif(1); + + pos.x = ox+sx*8.5f; + pos.y = 0.53f; + ddim.x = dim.x*2.2f; + ddim.y = 18.0f/480.0f; + pv = pw->CreateEditValue(pos, ddim, 0, EVENT_INTERFACE_DETAIL); + pv->SetState(STATE_SHADOW); + pv->SetMinValue(0.0f); + pv->SetMaxValue(2.0f); + pos.x += 0.13f; + pos.y -= 0.015f; + ddim.x = 0.40f; + GetResource(RES_EVENT, EVENT_INTERFACE_DETAIL, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL12, name); + pl->SetJustif(1); + + if ( !m_bSimulSetup ) + { + pos.x = ox+sx*8.5f; + pos.y = 0.47f; + ddim.x = dim.x*2.2f; + ddim.y = 18.0f/480.0f; + pv = pw->CreateEditValue(pos, ddim, 0, EVENT_INTERFACE_GADGET); + pv->SetState(STATE_SHADOW); + pv->SetMinValue(0.0f); + pv->SetMaxValue(1.0f); + pos.x += 0.13f; + pos.y -= 0.015f; + ddim.x = 0.40f; + GetResource(RES_EVENT, EVENT_INTERFACE_GADGET, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL13, name); + pl->SetJustif(1); + } + +#if 0 + if ( !m_bSimulSetup ) + { + pos.x = ox+sx*8.5f; + pos.y = 0.41f; + ddim.x = dim.x*2.2f; + ddim.y = 18.0f/480.0f; + pv = pw->CreateEditValue(pos, ddim, 0, EVENT_INTERFACE_TEXTURE); + pv->SetState(STATE_SHADOW); + pv->SetType(EVT_INT); + pv->SetMinValue(0.0f); + pv->SetMaxValue(2.0f); + pv->SetStepValue(1.0f); + pos.x += 0.13f; + pos.y -= 0.015f; + ddim.x = 0.40f; + GetResource(RES_EVENT, EVENT_INTERFACE_TEXTURE, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL14, name); + pl->SetJustif(1); + } +#endif + + ddim.x = dim.x*2; + ddim.y = dim.y*1; + pos.x = ox+sx*10; + pos.y = oy+sy*2; +#if _POLISH + ddim.x += 20.0f/640.0f; + pos.x -= 20.0f/640.0f*3.0f; +#endif + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MIN); + pb->SetState(STATE_SHADOW); + pos.x += ddim.x; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_NORM); + pb->SetState(STATE_SHADOW); + pos.x += ddim.x; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MAX); + pb->SetState(STATE_SHADOW); + + UpdateSetupButtons(); + } + + if ( m_phase == PHASE_SETUPp || // setup/jeu ? + m_phase == PHASE_SETUPps ) + { + ddim.x = dim.x*6; + ddim.y = dim.y*0.5f; + pos.x = ox+sx*3; + pos.y = 0.65f; +//? pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_TOTO); +//? pc->SetState(STATE_SHADOW); +//? pos.y -= 0.048f; +#if _SCHOOL + #if _EDU + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_SOLUCE4); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + #endif +#else + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_MOVIES); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; +#endif + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_SCROLL); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_INVERTX); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_INVERTY); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_EFFECT); + pc->SetState(STATE_SHADOW); +//? pos.y -= 0.048f; +//? pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_NICERST); +//? pc->SetState(STATE_SHADOW); +//? pos.y -= 0.048f; +//? pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_HIMSELF); +//? pc->SetState(STATE_SHADOW); + + ddim.x = dim.x*6; + ddim.y = dim.y*0.5f; + pos.x = ox+sx*10; + pos.y = 0.65f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_TOOLTIP); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_GLINT); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_RAIN); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_MOUSE); + pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + pos.y -= 0.048f; + if ( !m_bSimulSetup ) + { + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_EDITMODE); + pc->SetState(STATE_SHADOW); + } + pos.y -= 0.048f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_EDITVALUE); + pc->SetState(STATE_SHADOW); + + UpdateSetupButtons(); + } + + if ( m_phase == PHASE_SETUPc || // setup/commandes ? + m_phase == PHASE_SETUPcs ) + { + pos.x = ox+sx*3; + pos.y = 320.0f/480.0f; + ddim.x = dim.x*15.0f; + ddim.y = 18.0f/480.0f; + GetResource(RES_TEXT, RT_SETUP_KEY1, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_INTERFACE_KINFO1, name); + pl->SetJustif(1); + + pos.x = ox+sx*3; + pos.y = 302.0f/480.0f; + ddim.x = dim.x*15.0f; + ddim.y = 18.0f/480.0f; + GetResource(RES_TEXT, RT_SETUP_KEY2, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_INTERFACE_KINFO2, name); + pl->SetJustif(1); + + ddim.x = 428.0f/640.0f; + ddim.y = 128.0f/480.0f; + pos.x = 105.0f/640.0f; + pos.y = 164.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 0, EVENT_INTERFACE_KGROUP); + pg->ClearState(STATE_ENABLE); + pg->SetState(STATE_DEAD); + pg->SetState(STATE_SHADOW); + + ddim.x = 18.0f/640.0f; + ddim.y = (20.0f/480.0f)*KEY_VISIBLE; + pos.x = 510.0f/640.0f; + pos.y = 168.0f/480.0f; + ps = pw->CreateScroll(pos, ddim, -1, EVENT_INTERFACE_KSCROLL); + ps->SetVisibleRatio((float)KEY_VISIBLE/KEY_TOTAL); + ps->SetArrowStep(1.0f/((float)KEY_TOTAL-KEY_VISIBLE)); + UpdateKey(); + + ddim.x = dim.x*6; + ddim.y = dim.y*0.5f; + pos.x = ox+sx*3; + pos.y = 130.0f/480.0f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_JOYSTICK); + pc->SetState(STATE_SHADOW); + + ddim.x = dim.x*6; + ddim.y = dim.y*1; + pos.x = ox+sx*10; + pos.y = oy+sy*2; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_KDEF); + pb->SetState(STATE_SHADOW); + + UpdateSetupButtons(); + } + + if ( m_phase == PHASE_SETUPs || // setup/sound ? + m_phase == PHASE_SETUPss ) + { + pos.x = ox+sx*3; + pos.y = 0.55f; + ddim.x = dim.x*4.0f; + ddim.y = 18.0f/480.0f; + psl = pw->CreateSlider(pos, ddim, 0, EVENT_INTERFACE_VOLSOUND); + psl->SetState(STATE_SHADOW); + psl->SetLimit(0.0f, MAXVOLUME); + psl->SetArrowStep(1.0f); + pos.y += ddim.y; + GetResource(RES_EVENT, EVENT_INTERFACE_VOLSOUND, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL1, name); + pl->SetJustif(1); + +#if (_FULL | _NET) & _SOUNDTRACKS + pos.x = ox+sx*3; + pos.y = 0.40f; + ddim.x = dim.x*4.0f; + ddim.y = 18.0f/480.0f; + psl = pw->CreateSlider(pos, ddim, 0, EVENT_INTERFACE_VOLMUSIC); + psl->SetState(STATE_SHADOW); + psl->SetLimit(0.0f, MAXVOLUME); + psl->SetArrowStep(1.0f); + pos.y += ddim.y; + GetResource(RES_EVENT, EVENT_INTERFACE_VOLMUSIC, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL2, name); + pl->SetJustif(1); +#endif + + ddim.x = dim.x*6; + ddim.y = dim.y*0.5f; + pos.x = ox+sx*10; + pos.y = 0.55f; + pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_SOUND3D); + pc->SetState(STATE_SHADOW); + + ddim.x = dim.x*3; + ddim.y = dim.y*1; + pos.x = ox+sx*10; + pos.y = oy+sy*2; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_SILENT); + pb->SetState(STATE_SHADOW); + pos.x += ddim.x; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_NOISY); + pb->SetState(STATE_SHADOW); + + UpdateSetupButtons(); + } + + if ( m_phase == PHASE_WRITE || + m_phase == PHASE_WRITEs ) + { + pos.x = 0.10f; + pos.y = 0.10f; + ddim.x = 0.80f; + ddim.y = 0.80f; + pw = m_interface->CreateWindows(pos, ddim, 13, EVENT_WINDOW5); + pw->SetClosable(TRUE); + GetResource(RES_TEXT, RT_TITLE_WRITE, name); + pw->SetName(name); + + pos.x = 0.10f; + pos.y = 0.40f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 5, EVENT_INTERFACE_GLINTl); // coin orange + pos.x = 0.40f; + pos.y = 0.10f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // coin bleu + +#if _NEWLOOK + pos.x = 100.0f/640.0f; + pos.y = 66.0f/480.0f; + ddim.x = 438.0f/640.0f; + ddim.y = 42.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 26, EVENT_LABEL1); // violet + pg->SetState(STATE_SHADOW); +#endif + + pos.x = 290.0f/640.0f; + ddim.x = 245.0f/640.0f; + + pos.y = 146.0f/480.0f; + ddim.y = 18.0f/480.0f; + GetResource(RES_EVENT, EVENT_INTERFACE_IOLABEL, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_INTERFACE_IOLABEL, name); + pl->SetJustif(1); + + pos.y = 130.0f/480.0f; + ddim.y = 18.0f/480.0f; + pe = pw->CreateEdit(pos, ddim, 0, EVENT_INTERFACE_IONAME); + pe->SetState(STATE_SHADOW); + pe->SetFontType(FONT_COLOBOT); + pe->SetMaxChar(35); + IOReadName(); + + pos.y = 190.0f/480.0f; + ddim.y = 190.0f/480.0f; + pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_IOLIST); + pli->SetState(STATE_SHADOW); + + pos.y = oy+sy*2; + ddim.y = dim.y*1; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_IOWRITE); + pb->SetState(STATE_SHADOW); + + pos.x = 105.0f/640.0f; + pos.y = 190.0f/480.0f; + ddim.x = 170.0f/640.0f; + ddim.y = dim.y*1; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_IODELETE); + pb->SetState(STATE_SHADOW); + + pos.x = 105.0f/640.0f; + pos.y = 250.0f/480.0f; + ddim.x = 170.0f/640.0f; + ddim.y = 128.0f/480.0f; + pi = pw->CreateImage(pos, ddim, 0, EVENT_INTERFACE_IOIMAGE); + pi->SetState(STATE_SHADOW); + + ddim.x = dim.x*4; + ddim.y = dim.y*1; + pos.x = ox+sx*3; + pos.y = oy+sy*2; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_BACK); + pb->SetState(STATE_SHADOW); + + IOReadList(); + IOUpdateList(); + } + + if ( m_phase == PHASE_READ || + m_phase == PHASE_READs ) + { + pos.x = 0.10f; + pos.y = 0.10f; + ddim.x = 0.80f; + ddim.y = 0.80f; + pw = m_interface->CreateWindows(pos, ddim, 14, EVENT_WINDOW5); + pw->SetClosable(TRUE); + GetResource(RES_TEXT, RT_TITLE_READ, name); + pw->SetName(name); + + pos.x = 0.10f; + pos.y = 0.40f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 5, EVENT_INTERFACE_GLINTl); // coin orange + pos.x = 0.40f; + pos.y = 0.10f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // coin bleu + +#if _NEWLOOK + pos.x = 100.0f/640.0f; + pos.y = 66.0f/480.0f; + ddim.x = 438.0f/640.0f; + ddim.y = 42.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 26, EVENT_LABEL1); // violet + pg->SetState(STATE_SHADOW); +#endif + + pos.x = 290.0f/640.0f; + ddim.x = 245.0f/640.0f; + + pos.y = 160.0f/480.0f; + ddim.y = 190.0f/480.0f; + pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_IOLIST); + pli->SetState(STATE_SHADOW); + + pos.y = oy+sy*2; + ddim.y = dim.y*1; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_IOREAD); + pb->SetState(STATE_SHADOW); + if ( m_phase == PHASE_READs ) + { + pb->SetState(STATE_WARNING); + } + + pos.x = 105.0f/640.0f; + pos.y = 160.0f/480.0f; + ddim.x = 170.0f/640.0f; + ddim.y = dim.y*1; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_IODELETE); + pb->SetState(STATE_SHADOW); + + pos.x = 105.0f/640.0f; + pos.y = 220.0f/480.0f; + ddim.x = 170.0f/640.0f; + ddim.y = 128.0f/480.0f; + pi = pw->CreateImage(pos, ddim, 0, EVENT_INTERFACE_IOIMAGE); + pi->SetState(STATE_SHADOW); + + ddim.x = dim.x*4; + ddim.y = dim.y*1; + pos.x = ox+sx*3; + pos.y = oy+sy*2; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_BACK); + pb->SetState(STATE_SHADOW); + + IOReadList(); + IOUpdateList(); + + if ( m_phase == PHASE_READ ) + { + m_engine->SetBackground("inter01.tga", 0,0, 0,0, TRUE, TRUE); + m_engine->SetBackForce(TRUE); + } + } + + if ( m_phase == PHASE_LOADING ) + { + pos.x = 0.35f; + pos.y = 0.10f; + ddim.x = 0.30f; + ddim.y = 0.80f; +#if _TEEN + pw = m_interface->CreateWindows(pos, ddim, 12, EVENT_WINDOW5); +#else + pw = m_interface->CreateWindows(pos, ddim, 10, EVENT_WINDOW5); +#endif + pw->SetName(" "); + + pos.x = 0.35f; + pos.y = 0.60f; + ddim.x = 0.30f; + ddim.y = 0.30f; + pw->CreateGroup(pos, ddim, 5, EVENT_INTERFACE_GLINTl); // coin orange + pos.x = 0.35f; + pos.y = 0.10f; + ddim.x = 0.30f; + ddim.y = 0.30f; + pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // coin bleu + + pos.x = 254.0f/640.0f; + pos.y = 208.0f/480.0f; + ddim.x = 132.0f/640.0f; + ddim.y = 42.0f/480.0f; + pg = pw->CreateGroup(pos, ddim, 22, EVENT_NULL); + pg->SetState(STATE_SHADOW); + + pos.x = 220.0f/640.0f; + pos.y = 210.0f/480.0f; + ddim.x = 200.0f/640.0f; + ddim.y = 20.0f/480.0f; + GetResource(RES_TEXT, RT_DIALOG_LOADING, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL1, name); + pl->SetFontSize(12.0f); + pl->SetJustif(0); + + m_engine->SetBackground("inter01.tga", 0,0, 0,0, TRUE, TRUE); + m_engine->SetBackForce(TRUE); + + m_loadingCounter = 1; // laisse le temps de s'afficher ! + } + + if ( m_phase == PHASE_WELCOME1 ) + { + m_sound->StopMusic(); + m_sound->PlayMusic(11, FALSE); + + pos.x = 0.0f; + pos.y = 0.0f; + ddim.x = 0.0f; + ddim.y = 0.0f; + pw = m_interface->CreateWindows(pos, ddim, -1, EVENT_WINDOW5); + + m_engine->SetOverColor(RetColor(1.0f), D3DSTATETCb); + m_engine->SetOverFront(TRUE); + +#if _FRENCH + m_engine->SetBackground("alsyd.tga", 0,0, 0,0, TRUE, FALSE); +#endif +#if _POLISH + m_engine->SetBackground("manta.tga", 0,0, 0,0, TRUE, FALSE); +#endif +#if _WG + m_engine->SetBackground("wg.tga", 0,0, 0,0, TRUE, FALSE); +#endif + m_engine->SetBackForce(TRUE); + } + if ( m_phase == PHASE_WELCOME2 ) + { +#if _ENGLISH + m_sound->StopMusic(); + m_sound->PlayMusic(11, FALSE); +#endif +#if _POLISH + m_sound->StopMusic(); + m_sound->PlayMusic(11, FALSE); +#endif + + pos.x = 0.0f; + pos.y = 0.0f; + ddim.x = 0.0f; + ddim.y = 0.0f; + pw = m_interface->CreateWindows(pos, ddim, -1, EVENT_WINDOW5); + + m_engine->SetOverColor(RetColor(1.0f), D3DSTATETCb); + m_engine->SetOverFront(TRUE); + + m_engine->SetBackground("colobot.tga", 0,0, 0,0, TRUE, FALSE); + m_engine->SetBackForce(TRUE); + } + if ( m_phase == PHASE_WELCOME3 ) + { + pos.x = 0.0f; + pos.y = 0.0f; + ddim.x = 0.0f; + ddim.y = 0.0f; + pw = m_interface->CreateWindows(pos, ddim, -1, EVENT_WINDOW5); + + m_engine->SetOverColor(RetColor(0.0f), D3DSTATETCw); + m_engine->SetOverFront(TRUE); + +#if _FRENCH + m_engine->SetBackground("epsitecf.tga", 0,0, 0,0, TRUE, FALSE); +#endif +#if _ENGLISH + m_engine->SetBackground("epsitece.tga", 0,0, 0,0, TRUE, FALSE); +#endif +#if _GERMAN | _WG + m_engine->SetBackground("epsitecd.tga", 0,0, 0,0, TRUE, FALSE); +#endif +#if _POLISH + m_engine->SetBackground("epsitecp.tga", 0,0, 0,0, TRUE, FALSE); +#endif + m_engine->SetBackForce(TRUE); + } + + if ( m_phase == PHASE_GENERIC ) + { + pos.x = 0.0f; + pos.y = 0.0f; + ddim.x = 0.0f; + ddim.y = 0.0f; + pw = m_interface->CreateWindows(pos, ddim, -1, EVENT_WINDOW5); + +#if _FULL | _NET + pos.x = 80.0f/640.0f; + pos.y = 240.0f/480.0f; + ddim.x = 490.0f/640.0f; + ddim.y = 110.0f/480.0f; + pe = pw->CreateEdit(pos, ddim, 0, EVENT_EDIT1); + pe->SetGenericMode(TRUE); + pe->SetEditCap(FALSE); + pe->SetHiliteCap(FALSE); + pe->SetFontType(FONT_COURIER); + pe->SetFontSize(8.0f); + pe->ReadText("help\\authors.txt"); + + pos.x = 80.0f/640.0f; + pos.y = 140.0f/480.0f; + ddim.x = 490.0f/640.0f; + ddim.y = 100.0f/480.0f; + pe = pw->CreateEdit(pos, ddim, 0, EVENT_EDIT2); + pe->SetGenericMode(TRUE); + pe->SetEditCap(FALSE); + pe->SetHiliteCap(FALSE); + pe->SetFontType(FONT_COURIER); + pe->SetFontSize(6.5f); + pe->ReadText("help\\licences.txt"); +#endif +#if _SCHOOL +#if _CEEBOTDEMO + pos.x = 80.0f/640.0f; + pos.y = 210.0f/480.0f; + ddim.x = 490.0f/640.0f; + ddim.y = 150.0f/480.0f; +#else + pos.x = 80.0f/640.0f; + pos.y = 200.0f/480.0f; + ddim.x = 490.0f/640.0f; + ddim.y = 150.0f/480.0f; +#endif + pe = pw->CreateEdit(pos, ddim, 0, EVENT_EDIT1); + pe->SetGenericMode(TRUE); + pe->SetEditCap(FALSE); + pe->SetHiliteCap(FALSE); + pe->SetFontType(FONT_COURIER); + pe->SetFontSize(8.0f); + pe->ReadText("help\\authors.txt"); +#endif +#if _DEMO +//? pos.x = 80.0f/640.0f; +//? pos.y = 240.0f/480.0f; +//? ddim.x = 490.0f/640.0f; +//? ddim.y = 110.0f/480.0f; +//? pe = pw->CreateEdit(pos, ddim, 0, EVENT_EDIT1); +//? pe->SetGenericMode(TRUE); +//? pe->SetEditCap(FALSE); +//? pe->SetHiliteCap(FALSE); +//? pe->SetFontType(FONT_COURIER); +//? pe->SetFontSize(8.0f); +//? pe->ReadText("help\\demo.txt"); + +//? pos.x = 80.0f/640.0f; +//? pos.y = 140.0f/480.0f; +//? ddim.x = 490.0f/640.0f; +//? ddim.y = 100.0f/480.0f; +//? pe = pw->CreateEdit(pos, ddim, 0, EVENT_EDIT2); +//? pe->SetGenericMode(TRUE); +//? pe->SetEditCap(FALSE); +//? pe->SetHiliteCap(FALSE); +//? pe->SetFontType(FONT_COURIER); +//? pe->SetFontSize(8.0f); +//? pe->ReadText("help\\authors.txt"); +#endif + +#if !_DEMO + pos.x = 40.0f/640.0f; + pos.y = 83.0f/480.0f; + ddim.x = 246.0f/640.0f; + ddim.y = 16.0f/480.0f; + GetResource(RES_TEXT, RT_GENERIC_DEV1, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL1, name); + pl->SetFontType(FONT_COURIER); + pl->SetFontSize(8.0f); + + pos.y = 13.0f/480.0f; + GetResource(RES_TEXT, RT_GENERIC_DEV2, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL2, name); + pl->SetFontType(FONT_COURIER); + pl->SetFontSize(8.0f); + + pos.x = 355.0f/640.0f; + pos.y = 83.0f/480.0f; + ddim.x = 246.0f/640.0f; + ddim.y = 16.0f/480.0f; + GetResource(RES_TEXT, RT_GENERIC_EDIT1, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL3, name); + pl->SetFontType(FONT_COURIER); + pl->SetFontSize(8.0f); + + pos.y = 13.0f/480.0f; + GetResource(RES_TEXT, RT_GENERIC_EDIT2, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL4, name); + pl->SetFontType(FONT_COURIER); + pl->SetFontSize(8.0f); +#endif + +#if _DEMO + pos.x = 481.0f/640.0f; + pos.y = 51.0f/480.0f; + ddim.x = 30.0f/640.0f; + ddim.y = 30.0f/480.0f; + pb = pw->CreateButton(pos, ddim, 49, EVENT_INTERFACE_ABORT); + pb->SetState(STATE_SHADOW); +#else + pos.x = 306.0f/640.0f; + pos.y = 17.0f/480.0f; + ddim.x = 30.0f/640.0f; + ddim.y = 30.0f/480.0f; + pb = pw->CreateButton(pos, ddim, 49, EVENT_INTERFACE_ABORT); + pb->SetState(STATE_SHADOW); +#endif + +#if _NEWLOOK +#if _CEEBOTDEMO +#if _TEEN + m_engine->SetBackground("genedt.tga", 0,0, 0,0, TRUE, TRUE); +#else + m_engine->SetBackground("geneda.tga", 0,0, 0,0, TRUE, TRUE); +#endif +#else + m_engine->SetBackground("genern.tga", 0,0, 0,0, TRUE, TRUE); +#endif +#else +#if _FRENCH +#if _DEMO + m_engine->SetBackground("genedf.tga", 0,0, 0,0, TRUE, TRUE); +#else + m_engine->SetBackground("generf.tga", 0,0, 0,0, TRUE, TRUE); +#endif +#endif +#if _ENGLISH +#if _DEMO + m_engine->SetBackground("genede.tga", 0,0, 0,0, TRUE, TRUE); +#else + m_engine->SetBackground("genere.tga", 0,0, 0,0, TRUE, TRUE); +#endif +#endif +#if _GERMAN +#if _DEMO + m_engine->SetBackground("genedd.tga", 0,0, 0,0, TRUE, TRUE); +#else + m_engine->SetBackground("genere.tga", 0,0, 0,0, TRUE, TRUE); +#endif +#endif +#if _WG +#if _DEMO + m_engine->SetBackground("genedd.tga", 0,0, 0,0, TRUE, TRUE); +#else + m_engine->SetBackground("generd.tga", 0,0, 0,0, TRUE, TRUE); +#endif +#endif +#if _POLISH +#if _DEMO + m_engine->SetBackground("genedp.tga", 0,0, 0,0, TRUE, TRUE); +#else + m_engine->SetBackground("generp.tga", 0,0, 0,0, TRUE, TRUE); +#endif +#endif +#endif + m_engine->SetBackForce(TRUE); + } + + if ( m_phase == PHASE_INIT || + m_phase == PHASE_NAME || + m_phase == PHASE_TRAINER || + m_phase == PHASE_DEFI || + m_phase == PHASE_MISSION || + m_phase == PHASE_FREE || + m_phase == PHASE_TEEN || + m_phase == PHASE_USER || + m_phase == PHASE_PROTO || + m_phase == PHASE_SETUPd || + m_phase == PHASE_SETUPg || + m_phase == PHASE_SETUPp || + m_phase == PHASE_SETUPc || + m_phase == PHASE_SETUPs || + m_phase == PHASE_READ || + m_phase == PHASE_LOADING ) + { +#if _SCHOOL +#if _TEEN + pos.x = 50.0f/640.0f; + pos.y = 430.0f/480.0f; + ddim.x = 200.0f/640.0f; + ddim.y = 10.0f/480.0f; +#else + pos.x = 450.0f/640.0f; + pos.y = 0.0f/480.0f; + ddim.x = 170.0f/640.0f; + ddim.y = 9.0f/480.0f; +#endif +#else + pos.x = 540.0f/640.0f; + pos.y = 9.0f/480.0f; + ddim.x = 90.0f/640.0f; + ddim.y = 10.0f/480.0f; +#endif + GetResource(RES_TEXT, RT_VERSION_ID, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL1, name); + pl->SetFontType(FONT_COURIER); + pl->SetFontSize(9.0f); + } + + m_engine->LoadAllTexture(); +} + + +// Traite un événement. +// Retourne FALSE si l'événement a été traîté complètement. + +BOOL CMainDialog::EventProcess(const Event &event) +{ + CWindow* pw; + CList* pl; + CButton* pb; + CCheck* pc; + Event newEvent; + float welcomeLength; + + if ( event.event == EVENT_FRAME ) + { + m_phaseTime += event.rTime; + +//? if ( m_phase == PHASE_WELCOME1 ) welcomeLength = WELCOME_LENGTH+2.0f; +//? else welcomeLength = WELCOME_LENGTH; + welcomeLength = WELCOME_LENGTH; + + if ( m_phase == PHASE_WELCOME1 || + m_phase == PHASE_WELCOME2 || + m_phase == PHASE_WELCOME3 ) + { + float intensity; + int mode = D3DSTATETCb; + + if ( m_phaseTime < 1.5f ) + { + intensity = 1.0f-(m_phaseTime-0.5f); + } + else if ( m_phaseTime < welcomeLength-1.0f ) + { + intensity = 0.0f; + } + else + { + intensity = m_phaseTime-(welcomeLength-1.0f); + } + if ( intensity < 0.0f ) intensity = 0.0f; + if ( intensity > 1.0f ) intensity = 1.0f; + + if ( (m_phase == PHASE_WELCOME2 && m_phaseTime > welcomeLength/2.0f) || + m_phase == PHASE_WELCOME3 ) + { + intensity = 1.0f-intensity; + mode = D3DSTATETCw; + } + + m_engine->SetOverColor(RetColor(intensity), mode); + } + + if ( m_phase == PHASE_WELCOME1 && m_phaseTime >= welcomeLength ) + { + ChangePhase(PHASE_WELCOME2); + return TRUE; + } + if ( m_phase == PHASE_WELCOME2 && m_phaseTime >= welcomeLength ) + { + ChangePhase(PHASE_WELCOME3); + return TRUE; + } + if ( m_phase == PHASE_WELCOME3 && m_phaseTime >= welcomeLength ) + { + ChangePhase(PHASE_NAME); + return TRUE; + } + + if ( m_shotDelay > 0 && !m_bDialog ) // copie d'écran à faire ? + { + m_shotDelay --; + if ( m_shotDelay == 0 ) + { + m_engine->WriteScreenShot(m_shotName, 320, 240); +//? m_engine->WriteScreenShot(m_shotName, 160, 120); + } + } + + if ( m_phase == PHASE_LOADING ) + { + if ( m_loadingCounter == 0 ) + { + m_main->ChangePhase(PHASE_SIMUL); + } + m_loadingCounter --; + return FALSE; + } + + m_glintTime += event.rTime; + GlintMove(); // bouge les reflets + + FrameParticule(event.rTime); + + if ( m_bDialog ) // dialogue présent ? + { + FrameDialog(event.rTime); + } + + return TRUE; + } + + if ( event.event == EVENT_MOUSEMOVE ) + { + m_glintMouse = event.pos; + NiceParticule(event.pos, event.keyState&KS_MLEFT); + } + + if ( m_bDialog ) // dialogue présent ? + { + m_interface->EventProcess(event); + + if ( event.event == EVENT_DIALOG_OK || + (event.event == EVENT_KEYDOWN && event.param == VK_RETURN ) ) + { + StopDialog(); + if ( m_phase == PHASE_NAME ) + { + NameDelete(); + } + if ( m_phase == PHASE_INIT ) + { +//? m_event->MakeEvent(newEvent, EVENT_QUIT); +//? m_event->AddEvent(newEvent); + m_main->ChangePhase(PHASE_GENERIC); + } + if ( m_phase == PHASE_SIMUL ) + { + if ( m_bDialogDelete ) + { + m_main->DeleteObject(); + } + else + { + m_main->ChangePhase(PHASE_TERM); + } + } + } + if ( event.event == EVENT_DIALOG_CANCEL || + (event.event == EVENT_KEYDOWN && event.param == VK_ESCAPE ) ) + { + StopDialog(); + } + if ( event.event == EVENT_INTERFACE_SETUP ) + { + StopDialog(); + StartSuspend(); + if ( m_phaseSetup == PHASE_SETUPd ) ChangePhase(PHASE_SETUPds); + if ( m_phaseSetup == PHASE_SETUPg ) ChangePhase(PHASE_SETUPgs); + if ( m_phaseSetup == PHASE_SETUPp ) ChangePhase(PHASE_SETUPps); + if ( m_phaseSetup == PHASE_SETUPc ) ChangePhase(PHASE_SETUPcs); + if ( m_phaseSetup == PHASE_SETUPs ) ChangePhase(PHASE_SETUPss); + } + if ( event.event == EVENT_INTERFACE_AGAIN ) + { + StopDialog(); + m_main->ChangePhase(PHASE_LOADING); + } + if ( event.event == EVENT_INTERFACE_WRITE ) + { + StopDialog(); + StartSuspend(); + ChangePhase(PHASE_WRITEs); + } + if ( event.event == EVENT_INTERFACE_READ ) + { + StopDialog(); + StartSuspend(); + ChangePhase(PHASE_READs); + } + + return FALSE; + } + + if ( !m_engine->RetMouseHide() && + !m_interface->EventProcess(event) ) + { + return FALSE; + } + + if ( m_phase == PHASE_INIT ) + { + switch( event.event ) + { + case EVENT_KEYDOWN: + if ( event.param == VK_ESCAPE ) + { +//? StartQuit(); // voulez-vous quitter ? + m_sound->Play(SOUND_TZOING); + m_main->ChangePhase(PHASE_GENERIC); + } + break; + + case EVENT_INTERFACE_QUIT: +//? StartQuit(); // voulez-vous quitter ? + m_sound->Play(SOUND_TZOING); + m_main->ChangePhase(PHASE_GENERIC); + break; + + case EVENT_INTERFACE_TRAINER: + m_main->ChangePhase(PHASE_TRAINER); + break; + + case EVENT_INTERFACE_DEFI: + m_main->ChangePhase(PHASE_DEFI); + break; + + case EVENT_INTERFACE_MISSION: + m_main->ChangePhase(PHASE_MISSION); + break; + + case EVENT_INTERFACE_FREE: + m_main->ChangePhase(PHASE_FREE); + break; + + case EVENT_INTERFACE_TEEN: + m_main->ChangePhase(PHASE_TEEN); + break; + + case EVENT_INTERFACE_USER: + m_main->ChangePhase(PHASE_USER); + break; + + case EVENT_INTERFACE_PROTO: + m_main->ChangePhase(PHASE_PROTO); + break; + + case EVENT_INTERFACE_SETUP: + m_main->ChangePhase(m_phaseSetup); + break; + + case EVENT_INTERFACE_NAME: + m_main->ChangePhase(PHASE_NAME); + break; + } + return FALSE; + } + + if ( m_phase == PHASE_NAME ) + { + switch( event.event ) + { + case EVENT_KEYDOWN: + if ( event.param == VK_RETURN ) + { + NameSelect(); + } + if ( event.param == VK_ESCAPE ) + { + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) break; + pb = (CButton*)pw->SearchControl(EVENT_INTERFACE_NCANCEL); + if ( pb == 0 ) break; + if ( pb->TestState(STATE_ENABLE) ) + { + m_main->ChangePhase(PHASE_INIT); + } + } + break; + + case EVENT_INTERFACE_NEDIT: + UpdateNameList(); + UpdateNameControl(); + break; + + case EVENT_INTERFACE_NLIST: + UpdateNameEdit(); + break; + + case EVENT_INTERFACE_NOK: + NameSelect(); + break; + + case EVENT_INTERFACE_PERSO: + NameSelect(); + m_main->ChangePhase(PHASE_PERSO); + break; + + case EVENT_INTERFACE_NCANCEL: + m_main->ChangePhase(PHASE_INIT); + break; + + case EVENT_INTERFACE_NDELETE: + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) break; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_NLIST); + if ( pl == 0 ) break; + StartDeleteGame(pl->RetName(pl->RetSelect())); + break; + } + } + + if ( m_phase == PHASE_PERSO ) + { + switch( event.event ) + { + case EVENT_KEYDOWN: + if ( event.param == VK_RETURN ) + { + m_main->ChangePhase(PHASE_INIT); + } + if ( event.param == VK_ESCAPE ) + { + m_main->ChangePhase(PHASE_NAME); + } + break; + + case EVENT_INTERFACE_PHEAD: + m_persoTab = 0; + UpdatePerso(); + m_main->ScenePerso(); + CameraPerso(); + break; + case EVENT_INTERFACE_PBODY: + m_persoTab = 1; + UpdatePerso(); + m_main->ScenePerso(); + CameraPerso(); + break; + + case EVENT_INTERFACE_PFACE1: + case EVENT_INTERFACE_PFACE2: + case EVENT_INTERFACE_PFACE3: + case EVENT_INTERFACE_PFACE4: + m_perso.face = event.event-EVENT_INTERFACE_PFACE1; + WriteGamerPerso(m_main->RetGamerName()); + UpdatePerso(); + m_main->ScenePerso(); + break; + + case EVENT_INTERFACE_PGLASS0: + case EVENT_INTERFACE_PGLASS1: + case EVENT_INTERFACE_PGLASS2: + case EVENT_INTERFACE_PGLASS3: + case EVENT_INTERFACE_PGLASS4: + case EVENT_INTERFACE_PGLASS5: + case EVENT_INTERFACE_PGLASS6: + case EVENT_INTERFACE_PGLASS7: + case EVENT_INTERFACE_PGLASS8: + case EVENT_INTERFACE_PGLASS9: + m_perso.glasses = event.event-EVENT_INTERFACE_PGLASS0; + WriteGamerPerso(m_main->RetGamerName()); + UpdatePerso(); + m_main->ScenePerso(); + break; + + case EVENT_INTERFACE_PC0a: + case EVENT_INTERFACE_PC1a: + case EVENT_INTERFACE_PC2a: + case EVENT_INTERFACE_PC3a: + case EVENT_INTERFACE_PC4a: + case EVENT_INTERFACE_PC5a: + case EVENT_INTERFACE_PC6a: + case EVENT_INTERFACE_PC7a: + case EVENT_INTERFACE_PC8a: + case EVENT_INTERFACE_PC9a: + FixPerso(event.event-EVENT_INTERFACE_PC0a, 0); + WriteGamerPerso(m_main->RetGamerName()); + UpdatePerso(); + m_main->ScenePerso(); + break; + + case EVENT_INTERFACE_PC0b: + case EVENT_INTERFACE_PC1b: + case EVENT_INTERFACE_PC2b: + case EVENT_INTERFACE_PC3b: + case EVENT_INTERFACE_PC4b: + case EVENT_INTERFACE_PC5b: + case EVENT_INTERFACE_PC6b: + case EVENT_INTERFACE_PC7b: + case EVENT_INTERFACE_PC8b: + case EVENT_INTERFACE_PC9b: + FixPerso(event.event-EVENT_INTERFACE_PC0b, 1); + WriteGamerPerso(m_main->RetGamerName()); + UpdatePerso(); + m_main->ScenePerso(); + break; + + case EVENT_INTERFACE_PCRa: + case EVENT_INTERFACE_PCGa: + case EVENT_INTERFACE_PCBa: + case EVENT_INTERFACE_PCRb: + case EVENT_INTERFACE_PCGb: + case EVENT_INTERFACE_PCBb: + ColorPerso(); + WriteGamerPerso(m_main->RetGamerName()); + UpdatePerso(); + m_main->ScenePerso(); + break; + + case EVENT_INTERFACE_PDEF: + DefPerso(); + WriteGamerPerso(m_main->RetGamerName()); + UpdatePerso(); + m_main->ScenePerso(); + break; + + case EVENT_INTERFACE_PLROT: + m_persoAngle += 0.2f; + break; + case EVENT_INTERFACE_PRROT: + m_persoAngle -= 0.2f; + break; + + case EVENT_INTERFACE_POK: + m_main->ChangePhase(PHASE_INIT); + break; + + case EVENT_INTERFACE_PCANCEL: + m_perso = m_persoCopy; + WriteGamerPerso(m_main->RetGamerName()); + m_main->ChangePhase(PHASE_NAME); + break; + } + } + + if ( m_phase == PHASE_TRAINER || + m_phase == PHASE_DEFI || + m_phase == PHASE_MISSION || + m_phase == PHASE_FREE || + m_phase == PHASE_TEEN || + m_phase == PHASE_USER || + m_phase == PHASE_PROTO ) + { + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return FALSE; + + if ( event.event == pw->RetEventMsgClose() || + event.event == EVENT_INTERFACE_BACK || + (event.event == EVENT_KEYDOWN && event.param == VK_ESCAPE) ) + { + m_main->ChangePhase(PHASE_INIT); + return FALSE; + } + } + + if ( m_phase == PHASE_TRAINER || + m_phase == PHASE_DEFI || + m_phase == PHASE_MISSION || + m_phase == PHASE_FREE || + m_phase == PHASE_TEEN || + m_phase == PHASE_USER || + m_phase == PHASE_PROTO ) + { + switch( event.event ) + { + case EVENT_INTERFACE_CHAP: + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_CHAP); + if ( pl == 0 ) break; + m_chap[m_index] = pl->RetSelect(); + UpdateSceneList(m_chap[m_index], m_sel[m_index]); + UpdateSceneResume((m_chap[m_index]+1)*100+(m_sel[m_index]+1)); + break; + + case EVENT_INTERFACE_LIST: + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_LIST); + if ( pl == 0 ) break; + m_sel[m_index] = pl->RetSelect(); + UpdateSceneResume((m_chap[m_index]+1)*100+(m_sel[m_index]+1)); + break; + + case EVENT_INTERFACE_SOLUCE: + pb = (CButton*)pw->SearchControl(EVENT_INTERFACE_SOLUCE); + if ( pb == 0 ) break; + m_bSceneSoluce = !m_bSceneSoluce; + pb->SetState(STATE_CHECK, m_bSceneSoluce); + break; + + case EVENT_INTERFACE_PLAY: + if ( m_phase == PHASE_PROTO && m_chap[m_index] == 0 && m_sel[m_index] == 0 ) + { + m_main->ChangePhase(PHASE_MODEL); + break; + } + m_sceneRank = (m_chap[m_index]+1)*100+(m_sel[m_index]+1); + m_phaseTerm = m_phase; + m_main->ChangePhase(PHASE_LOADING); + break; + + case EVENT_INTERFACE_READ: + m_phaseTerm = m_phase; + m_main->ChangePhase(PHASE_READ); + break; + } + return FALSE; + } + + if ( m_phase == PHASE_SETUPd || + m_phase == PHASE_SETUPg || + m_phase == PHASE_SETUPp || + m_phase == PHASE_SETUPc || + m_phase == PHASE_SETUPs ) + { + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return FALSE; + + if ( event.event == pw->RetEventMsgClose() || + event.event == EVENT_INTERFACE_BACK || + (event.event == EVENT_KEYDOWN && event.param == VK_ESCAPE) ) + { + SetupMemorize(); + m_engine->ApplyChange(); + m_main->ChangePhase(PHASE_INIT); + return FALSE; + } + + switch( event.event ) + { + case EVENT_INTERFACE_SETUPd: + m_main->ChangePhase(PHASE_SETUPd); + break; + + case EVENT_INTERFACE_SETUPg: + m_main->ChangePhase(PHASE_SETUPg); + break; + + case EVENT_INTERFACE_SETUPp: + m_main->ChangePhase(PHASE_SETUPp); + break; + + case EVENT_INTERFACE_SETUPc: + m_main->ChangePhase(PHASE_SETUPc); + break; + + case EVENT_INTERFACE_SETUPs: + m_main->ChangePhase(PHASE_SETUPs); + break; + } + } + + if ( m_phase == PHASE_SETUPds || + m_phase == PHASE_SETUPgs || + m_phase == PHASE_SETUPps || + m_phase == PHASE_SETUPcs || + m_phase == PHASE_SETUPss ) + { + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return FALSE; + + if ( event.event == pw->RetEventMsgClose() || + event.event == EVENT_INTERFACE_BACK || + (event.event == EVENT_KEYDOWN && event.param == VK_ESCAPE) ) + { + SetupMemorize(); + m_engine->ApplyChange(); + m_interface->DeleteControl(EVENT_WINDOW5); + ChangePhase(PHASE_SIMUL); + StopSuspend(); + return FALSE; + } + + switch( event.event ) + { + case EVENT_INTERFACE_SETUPd: + ChangePhase(PHASE_SETUPds); + break; + + case EVENT_INTERFACE_SETUPg: + ChangePhase(PHASE_SETUPgs); + break; + + case EVENT_INTERFACE_SETUPp: + ChangePhase(PHASE_SETUPps); + break; + + case EVENT_INTERFACE_SETUPc: + ChangePhase(PHASE_SETUPcs); + break; + + case EVENT_INTERFACE_SETUPs: + ChangePhase(PHASE_SETUPss); + break; + } + } + + if ( m_phase == PHASE_SETUPd || // setup/display ? + m_phase == PHASE_SETUPds ) + { + switch( event.event ) + { + case EVENT_LIST1: + case EVENT_LIST2: + UpdateApply(); + break; + + case EVENT_INTERFACE_FULL: + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) break; + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_FULL); + if ( pc == 0 ) break; + pl = (CList*)pw->SearchControl(EVENT_LIST2); + if ( pl == 0 ) break; + if ( pc->TestState(STATE_CHECK) ) + { + pc->ClearState(STATE_CHECK); // fenêtré + pl->ClearState(STATE_ENABLE); + } + else + { + pc->SetState(STATE_CHECK); // plein écran + pl->SetState(STATE_ENABLE); + } + UpdateApply(); + break; + + case EVENT_INTERFACE_APPLY: + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) break; + pb = (CButton*)pw->SearchControl(EVENT_INTERFACE_APPLY); + if ( pb == 0 ) break; + pb->ClearState(STATE_PRESS); + pb->ClearState(STATE_HILIGHT); + ChangeDisplay(); + UpdateApply(); + break; + } + return FALSE; + } + + if ( m_phase == PHASE_SETUPg || // setup/graphic ? + m_phase == PHASE_SETUPgs ) + { + switch( event.event ) + { + case EVENT_INTERFACE_SHADOW: + m_engine->SetShadow(!m_engine->RetShadow()); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_GROUND: + m_engine->SetGroundSpot(!m_engine->RetGroundSpot()); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_DIRTY: + m_engine->SetDirty(!m_engine->RetDirty()); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_FOG: + m_engine->SetFog(!m_engine->RetFog()); + m_camera->SetOverBaseColor(RetColor(RetColor(0.0f))); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_LENS: + m_engine->SetLensMode(!m_engine->RetLensMode()); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_SKY: + m_engine->SetSkyMode(!m_engine->RetSkyMode()); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_PLANET: + m_engine->SetPlanetMode(!m_engine->RetPlanetMode()); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_LIGHT: + m_engine->SetLightMode(!m_engine->RetLightMode()); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_PARTI: + case EVENT_INTERFACE_CLIP: + case EVENT_INTERFACE_DETAIL: + case EVENT_INTERFACE_GADGET: + case EVENT_INTERFACE_TEXTURE: + ChangeSetupButtons(); + break; + + case EVENT_INTERFACE_MIN: + ChangeSetupQuality(-1); + UpdateSetupButtons(); + break; + case EVENT_INTERFACE_NORM: + ChangeSetupQuality(0); + UpdateSetupButtons(); + break; + case EVENT_INTERFACE_MAX: + ChangeSetupQuality(1); + UpdateSetupButtons(); + break; + } + return FALSE; + } + + if ( m_phase == PHASE_SETUPp || // setup/jeu ? + m_phase == PHASE_SETUPps ) + { + switch( event.event ) + { + case EVENT_INTERFACE_TOTO: + m_engine->SetTotoMode(!m_engine->RetTotoMode()); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_TOOLTIP: + m_bTooltip = !m_bTooltip; + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_GLINT: + m_bGlint = !m_bGlint; + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_RAIN: + m_bRain = !m_bRain; + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_MOUSE: + m_engine->SetNiceMouse(!m_engine->RetNiceMouse()); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_EDITMODE: + m_engine->SetEditIndentMode(!m_engine->RetEditIndentMode()); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_EDITVALUE: + if ( m_engine->RetEditIndentValue() == 2 ) + { + m_engine->SetEditIndentValue(4); + } + else + { + m_engine->SetEditIndentValue(2); + } + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_SOLUCE4: + m_bSoluce4 = !m_bSoluce4; + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_MOVIES: + m_bMovies = !m_bMovies; + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_NICERST: + m_bNiceReset = !m_bNiceReset; + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_HIMSELF: + m_bHimselfDamage = !m_bHimselfDamage; + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_SCROLL: + m_bCameraScroll = !m_bCameraScroll; + m_camera->SetCameraScroll(m_bCameraScroll); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_INVERTX: + m_bCameraInvertX = !m_bCameraInvertX; + m_camera->SetCameraInvertX(m_bCameraInvertX); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_INVERTY: + m_bCameraInvertY = !m_bCameraInvertY; + m_camera->SetCameraInvertY(m_bCameraInvertY); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_EFFECT: + m_bEffect = !m_bEffect; + m_camera->SetEffect(m_bEffect); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + } + return FALSE; + } + + if ( m_phase == PHASE_SETUPc || // setup/commandes ? + m_phase == PHASE_SETUPcs ) + { + switch( event.event ) + { + case EVENT_INTERFACE_KSCROLL: + UpdateKey(); + break; + + case EVENT_INTERFACE_KLEFT: + case EVENT_INTERFACE_KRIGHT: + case EVENT_INTERFACE_KUP: + case EVENT_INTERFACE_KDOWN: + case EVENT_INTERFACE_KGUP: + case EVENT_INTERFACE_KGDOWN: + case EVENT_INTERFACE_KCAMERA: + case EVENT_INTERFACE_KDESEL: + case EVENT_INTERFACE_KACTION: + case EVENT_INTERFACE_KNEAR: + case EVENT_INTERFACE_KAWAY: + case EVENT_INTERFACE_KNEXT: + case EVENT_INTERFACE_KHUMAN: + case EVENT_INTERFACE_KQUIT: + case EVENT_INTERFACE_KHELP: + case EVENT_INTERFACE_KPROG: + case EVENT_INTERFACE_KCBOT: + case EVENT_INTERFACE_KSPEED10: + case EVENT_INTERFACE_KSPEED15: + case EVENT_INTERFACE_KSPEED20: + case EVENT_INTERFACE_KSPEED30: + case EVENT_INTERFACE_KVISIT: + ChangeKey(event.event); + UpdateKey(); + break; + + case EVENT_INTERFACE_KDEF: + m_engine->ResetKey(); + UpdateKey(); + break; + + case EVENT_INTERFACE_JOYSTICK: + m_engine->SetJoystick(!m_engine->RetJoystick()); + UpdateSetupButtons(); + break; + } + return FALSE; + } + + if ( m_phase == PHASE_SETUPs || // setup/sound ? + m_phase == PHASE_SETUPss ) + { + switch( event.event ) + { + case EVENT_INTERFACE_VOLSOUND: + case EVENT_INTERFACE_VOLMUSIC: + ChangeSetupButtons(); + break; + + case EVENT_INTERFACE_SOUND3D: + m_sound->SetSound3D(!m_sound->RetSound3D()); + ChangeSetupButtons(); + UpdateSetupButtons(); + break; + + case EVENT_INTERFACE_SILENT: + m_sound->SetAudioVolume(0); + m_sound->SetMidiVolume(0); + UpdateSetupButtons(); + break; + case EVENT_INTERFACE_NOISY: + m_sound->SetAudioVolume(MAXVOLUME); + m_sound->SetMidiVolume(MAXVOLUME*3/4); + UpdateSetupButtons(); + break; + } + return FALSE; + } + + if ( m_phase == PHASE_READ ) + { + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return FALSE; + + if ( event.event == pw->RetEventMsgClose() || + event.event == EVENT_INTERFACE_BACK || + (event.event == EVENT_KEYDOWN && event.param == VK_ESCAPE) ) + { + ChangePhase(m_phaseTerm); + } + + if ( event.event == EVENT_INTERFACE_IOLIST ) + { + IOUpdateList(); + } + if ( event.event == EVENT_INTERFACE_IODELETE ) + { + IODeleteScene(); + IOUpdateList(); + } + if ( event.event == EVENT_INTERFACE_IOREAD ) + { + if ( IOReadScene() ) + { + m_main->ChangePhase(PHASE_LOADING); + } + } + + return FALSE; + } + + if ( m_phase == PHASE_WRITEs || + m_phase == PHASE_READs ) + { + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return FALSE; + + if ( event.event == pw->RetEventMsgClose() || + event.event == EVENT_INTERFACE_BACK || + (event.event == EVENT_KEYDOWN && event.param == VK_ESCAPE) ) + { + m_interface->DeleteControl(EVENT_WINDOW5); + ChangePhase(PHASE_SIMUL); + StopSuspend(); + } + + if ( event.event == EVENT_INTERFACE_IOLIST ) + { + IOUpdateList(); + } + if ( event.event == EVENT_INTERFACE_IODELETE ) + { + IODeleteScene(); + IOUpdateList(); + } + if ( event.event == EVENT_INTERFACE_IOWRITE ) + { + IOWriteScene(); + m_interface->DeleteControl(EVENT_WINDOW5); + ChangePhase(PHASE_SIMUL); + StopSuspend(); + } + if ( event.event == EVENT_INTERFACE_IOREAD ) + { + if ( IOReadScene() ) + { + m_interface->DeleteControl(EVENT_WINDOW5); + ChangePhase(PHASE_SIMUL); + StopSuspend(); + m_main->ChangePhase(PHASE_LOADING); + } + } + + return FALSE; + } + + if ( m_phase == PHASE_WELCOME1 ) + { + if ( event.event == EVENT_KEYDOWN || + event.event == EVENT_LBUTTONDOWN || + event.event == EVENT_RBUTTONDOWN ) + { + ChangePhase(PHASE_WELCOME2); + return TRUE; + } + } + if ( m_phase == PHASE_WELCOME2 ) + { + if ( event.event == EVENT_KEYDOWN || + event.event == EVENT_LBUTTONDOWN || + event.event == EVENT_RBUTTONDOWN ) + { + ChangePhase(PHASE_WELCOME3); + return TRUE; + } + } + if ( m_phase == PHASE_WELCOME3 ) + { + if ( event.event == EVENT_KEYDOWN || + event.event == EVENT_LBUTTONDOWN || + event.event == EVENT_RBUTTONDOWN ) + { + ChangePhase(PHASE_NAME); + return TRUE; + } + } + + if ( m_phase == PHASE_GENERIC ) + { + if ( event.event == EVENT_INTERFACE_ABORT ) + { + ChangePhase(PHASE_INIT); + } + + if ( event.event == EVENT_KEYDOWN ) + { + if ( event.param == VK_ESCAPE ) + { + ChangePhase(PHASE_INIT); + } + else + { + m_event->MakeEvent(newEvent, EVENT_QUIT); + m_event->AddEvent(newEvent); + } + } + + if ( event.event == EVENT_LBUTTONDOWN || + event.event == EVENT_RBUTTONDOWN ) + { + m_event->MakeEvent(newEvent, EVENT_QUIT); + m_event->AddEvent(newEvent); + } + } + + return TRUE; +} + + +// Fait bouger les reflets. + +void CMainDialog::GlintMove() +{ + CWindow* pw; + CGroup* pg; + FPOINT pos, dim, zoom; + + if ( m_phase == PHASE_SIMUL ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + + if ( m_phase == PHASE_INIT ) + { + pg = (CGroup*)pw->SearchControl(EVENT_INTERFACE_GLINTl); + if ( pg != 0 ) + { + zoom.x = sinf(m_glintTime*0.23f); + zoom.y = sinf(m_glintTime*0.37f); + pos.x = 0.35f; + pos.y = 0.90f; + dim.x = 0.30f-0.10f*(zoom.x+1.0f)/2.0f; + dim.y = 0.50f-0.30f*(zoom.y+1.0f)/2.0f; + pos.y -= dim.y; + pg->SetPos(pos); + pg->SetDim(dim); + } + + pg = (CGroup*)pw->SearchControl(EVENT_INTERFACE_GLINTr); + if ( pg != 0 ) + { + zoom.x = sinf(m_glintTime*0.21f); + zoom.y = sinf(m_glintTime*0.26f); + pos.x = 0.65f; + pos.y = 0.10f; + dim.x = 0.30f-0.10f*(zoom.x+1.0f)/2.0f; + dim.y = 0.50f-0.30f*(zoom.y+1.0f)/2.0f; + pos.x -= dim.x; + pg->SetPos(pos); + pg->SetDim(dim); + } + } + + if ( m_phase == PHASE_NAME || + m_phase == PHASE_TRAINER || + m_phase == PHASE_MISSION || + m_phase == PHASE_FREE || + m_phase == PHASE_TEEN || + m_phase == PHASE_USER || + m_phase == PHASE_PROTO ) + { + pg = (CGroup*)pw->SearchControl(EVENT_INTERFACE_GLINTl); + if ( pg != 0 ) + { + zoom.x = sinf(m_glintTime*0.22f); + zoom.y = sinf(m_glintTime*0.37f); + pos.x = 0.10f; + pos.y = 0.90f; + dim.x = 0.60f+0.30f*zoom.x; + dim.y = 0.60f+0.30f*zoom.y; + pos.y -= dim.y; + pg->SetPos(pos); + pg->SetDim(dim); + } + + pg = (CGroup*)pw->SearchControl(EVENT_INTERFACE_GLINTr); + if ( pg != 0 ) + { + zoom.x = sinf(m_glintTime*0.19f); + zoom.y = sinf(m_glintTime*0.28f); + pos.x = 0.90f; + pos.y = 0.10f; + dim.x = 0.60f+0.30f*zoom.x; + dim.y = 0.60f+0.30f*zoom.y; + pos.x -= dim.x; + pg->SetPos(pos); + pg->SetDim(dim); + } + } + + if ( m_phase == PHASE_SETUPd || + m_phase == PHASE_SETUPg || + m_phase == PHASE_SETUPp || + m_phase == PHASE_SETUPc || + m_phase == PHASE_SETUPs || + m_phase == PHASE_SETUPds || + m_phase == PHASE_SETUPgs || + m_phase == PHASE_SETUPps || + m_phase == PHASE_SETUPcs || + m_phase == PHASE_SETUPss ) + { + pg = (CGroup*)pw->SearchControl(EVENT_INTERFACE_GLINTu); + if ( pg != 0 ) + { + zoom.y = sinf(m_glintTime*0.27f); + pos.x = 0.10f; + pos.y = 0.76f; + dim.x = 0.80f; + dim.y = 0.32f+0.20f*zoom.y; + pos.y -= dim.y; + pg->SetPos(pos); + pg->SetDim(dim); + } + + pg = (CGroup*)pw->SearchControl(EVENT_INTERFACE_GLINTr); + if ( pg != 0 ) + { + zoom.x = sinf(m_glintTime*0.29f); + zoom.y = sinf(m_glintTime*0.14f); + pos.x = 0.90f; + pos.y = 0.10f; + dim.x = 0.40f+0.20f*zoom.x; + dim.y = 0.40f+0.20f*zoom.y; + pos.x -= dim.x; + pg->SetPos(pos); + pg->SetDim(dim); + } + } + + if ( m_phase == PHASE_WRITE || + m_phase == PHASE_READ || + m_phase == PHASE_WRITEs || + m_phase == PHASE_READs ) + { + pg = (CGroup*)pw->SearchControl(EVENT_INTERFACE_GLINTl); + if ( pg != 0 ) + { + zoom.x = sinf(m_glintTime*0.22f); + zoom.y = sinf(m_glintTime*0.37f); + pos.x = 0.10f; + pos.y = 0.90f; + dim.x = 0.60f+0.30f*zoom.x; + dim.y = 0.60f+0.30f*zoom.y; + pos.y -= dim.y; + pg->SetPos(pos); + pg->SetDim(dim); + } + + pg = (CGroup*)pw->SearchControl(EVENT_INTERFACE_GLINTr); + if ( pg != 0 ) + { + zoom.x = sinf(m_glintTime*0.19f); + zoom.y = sinf(m_glintTime*0.28f); + pos.x = 0.90f; + pos.y = 0.10f; + dim.x = 0.60f+0.30f*zoom.x; + dim.y = 0.60f+0.30f*zoom.y; + pos.x -= dim.x; + pg->SetPos(pos); + pg->SetDim(dim); + } + } +} + + +// Retourne la position pour un son. + +D3DVECTOR SoundPos(FPOINT pos) +{ + D3DVECTOR s; + + s.x = (pos.x-0.5f)*2.0f; + s.y = (pos.y-0.5f)*2.0f; + s.z = 0.0f; + + return s; +} + +// Retourne une position aléatoire pour un son. + +D3DVECTOR SoundRand() +{ + D3DVECTOR s; + + s.x = (Rand()-0.5f)*2.0f; + s.y = (Rand()-0.5f)*2.0f; + s.z = 0.0f; + + return s; +} + +// Fait évoluer qq joiles particules. + +void CMainDialog::FrameParticule(float rTime) +{ +#if _NEWLOOK +#else + D3DVECTOR pos, speed; + FPOINT dim; + float *pParti, *pGlint; + int nParti, nGlint; + int i, r, ii; + + static float partiPosInit[1+5*12] = + { // x x t2 t2 type + 12.0f, + 607.0f, 164.0f, 0.2f, 0.8f, 1.0f, // câble sup. + 604.0f, 205.0f, 0.1f, 0.3f, 1.0f, // câble mid. + 603.0f, 247.0f, 0.1f, 0.3f, 1.0f, // câble inf. + 119.0f, 155.0f, 0.2f, 0.4f, 2.0f, // tuyau gauche + 366.0f, 23.0f, 0.5f, 1.5f, 4.0f, // tuyau sup. + 560.0f, 414.0f, 0.1f, 0.1f, 1.0f, // bouton inf/droite + 20.0f, 413.0f, 0.1f, 0.1f, 2.0f, // bouton inf/gauche + 39.0f, 78.0f, 0.1f, 0.2f, 1.0f, // pot gauche + 39.0f, 78.0f, 0.5f, 0.9f, 1.0f, // pot gauche + 170.0f, 229.0f, 0.5f, 0.5f, 3.0f, // fumée gauche + 170.0f, 229.0f, 0.5f, 0.5f, 3.0f, // fumée gauche + 474.0f, 229.0f, 0.5f, 0.5f, 3.0f, // fumée droite + }; + + static float glintPosInit[1+2*14] = + { + 14.0f, + 15.0f, 407.0f, + 68.0f, 417.0f, + 548.0f, 36.0f, + 611.0f, 37.0f, + 611.0f, 100.0f, + 611.0f, 395.0f, + 36.0f, 35.0f, + 166.0f, 55.0f, + 166.0f, 94.0f, + 477.0f, 56.0f, + 31.0f, 190.0f, + 32.0f, 220.0f, + 65.0f, 221.0f, + 65.0f, 250.0f, + }; + + static float partiPosBig[1+5*12] = + { // x x t2 t2 type + 12.0f, + 607.0f, 164.0f, 0.2f, 0.8f, 1.0f, // câble sup. + 604.0f, 205.0f, 0.1f, 0.3f, 1.0f, // câble mid. + 603.0f, 247.0f, 0.1f, 0.3f, 1.0f, // câble inf. + 64.0f, 444.0f, 0.2f, 0.8f, 1.0f, // câble bas gauche + 113.0f, 449.0f, 0.1f, 0.3f, 1.0f, // câble bas gauche + 340.0f, 463.0f, 0.2f, 0.8f, 1.0f, // câble bas milieu + 36.0f, 155.0f, 0.2f, 0.4f, 2.0f, // tuyau gauche + 366.0f, 23.0f, 0.5f, 1.5f, 4.0f, // tuyau sup. + 612.0f, 414.0f, 0.1f, 0.1f, 1.0f, // bouton inf/droite + 20.0f, 413.0f, 0.1f, 0.1f, 2.0f, // bouton inf/gauche + 39.0f, 78.0f, 0.1f, 0.2f, 1.0f, // pot gauche + 39.0f, 78.0f, 0.5f, 0.9f, 1.0f, // pot gauche + }; + + static float glintPosBig[1+2*12] = + { + 12.0f, + 15.0f, 407.0f, + 48.0f, 399.0f, + 611.0f, 37.0f, + 611.0f, 100.0f, + 611.0f, 395.0f, + 36.0f, 35.0f, + 31.0f, 190.0f, + 32.0f, 220.0f, + 31.0f, 221.0f, + 31.0f, 189.0f, + 255.0f, 18.0f, + 279.0f, 18.0f, + }; + + if ( m_bDialog || !m_bRain ) return; + + if ( m_phase == PHASE_INIT ) + { + pParti = partiPosInit; + pGlint = glintPosInit; + } + else if ( m_phase == PHASE_NAME || + m_phase == PHASE_TRAINER || + m_phase == PHASE_DEFI || + m_phase == PHASE_MISSION || + m_phase == PHASE_FREE || + m_phase == PHASE_TEEN || + m_phase == PHASE_USER || + m_phase == PHASE_PROTO || + m_phase == PHASE_SETUPd || + m_phase == PHASE_SETUPg || + m_phase == PHASE_SETUPp || + m_phase == PHASE_SETUPc || + m_phase == PHASE_SETUPs || + m_phase == PHASE_WRITE || + m_phase == PHASE_READ ) + { + pParti = partiPosBig; + pGlint = glintPosBig; + } + else + { + return; + } + + nParti = (int)(*pParti++); + nGlint = (int)(*pGlint++); + + for ( i=0 ; i<10 ; i++ ) + { + if ( m_partiPhase[i] == 0 ) // attente ? + { + m_partiTime[i] -= rTime; + if ( m_partiTime[i] <= 0.0f ) + { + r = rand()%3; + + if ( r == 0 ) + { + ii = rand()%nParti; + m_partiPos[i].x = pParti[ii*5+0]/640.0f; + m_partiPos[i].y = (480.0f-pParti[ii*5+1])/480.0f; + m_partiTime[i] = pParti[ii*5+2]+Rand()*pParti[ii*5+3]; + m_partiPhase[i] = (int)pParti[ii*5+4]; + if ( m_partiPhase[i] == 3 ) + { + m_sound->Play(SOUND_PSHHH, SoundPos(m_partiPos[i]), 0.3f+Rand()*0.3f); + } + else + { + m_sound->Play(SOUND_GGG, SoundPos(m_partiPos[i]), 0.1f+Rand()*0.4f); + } + } + + if ( r == 1 ) + { + ii = rand()%nGlint; + pos.x = pGlint[ii*2+0]/640.0f; + pos.y = (480.0f-pGlint[ii*2+1])/480.0f; + pos.z = 0.0f; + speed.x = 0.0f; + speed.y = 0.0f; + speed.z = 0.0f; + dim.x = 0.04f+Rand()*0.04f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, + rand()%2?PARTIGLINT:PARTICONTROL, + Rand()*0.4f+0.4f, 0.0f, 0.0f, + SH_INTERFACE); + m_partiTime[i] = 0.5f+Rand()*0.5f; + } + + if ( r == 2 ) + { + ii = rand()%7; + if ( ii == 0 ) + { + m_sound->Play(SOUND_ENERGY, SoundRand(), 0.2f+Rand()*0.2f); + m_partiTime[i] = 1.0f+Rand()*1.0f; + } + if ( ii == 1 ) + { + m_sound->Play(SOUND_STATION, SoundRand(), 0.2f+Rand()*0.2f); + m_partiTime[i] = 1.0f+Rand()*2.0f; + } + if ( ii == 2 ) + { + m_sound->Play(SOUND_ALARM, SoundRand(), 0.1f+Rand()*0.1f); + m_partiTime[i] = 2.0f+Rand()*4.0f; + } + if ( ii == 3 ) + { + m_sound->Play(SOUND_INFO, SoundRand(), 0.1f+Rand()*0.1f); + m_partiTime[i] = 2.0f+Rand()*4.0f; + } + if ( ii == 4 ) + { + m_sound->Play(SOUND_RADAR, SoundRand(), 0.2f+Rand()*0.2f); + m_partiTime[i] = 0.5f+Rand()*1.0f; + } + if ( ii == 5 ) + { + m_sound->Play(SOUND_GFLAT, SoundRand(), 0.3f+Rand()*0.3f); + m_partiTime[i] = 2.0f+Rand()*4.0f; + } + if ( ii == 6 ) + { + m_sound->Play(SOUND_ALARMt, SoundRand(), 0.1f+Rand()*0.1f); + m_partiTime[i] = 2.0f+Rand()*4.0f; + } + } + } + } + + if ( m_partiPhase[i] != 0 ) // génère ? + { + m_partiTime[i] -= rTime; + if ( m_partiTime[i] > 0.0f ) + { + if ( m_partiPhase[i] == 1 ) // étincelles ? + { + pos.x = m_partiPos[i].x; + pos.y = m_partiPos[i].y; + pos.z = 0.0f; + pos.x += (Rand()-0.5f)*0.01f; + pos.y += (Rand()-0.5f)*0.01f; + speed.x = (Rand()-0.5f)*0.2f; + speed.y = (Rand()-0.5f)*0.2f; + speed.z = 0.0f; + dim.x = 0.005f+Rand()*0.005f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, PARTIBLITZ, + Rand()*0.2f+0.2f, 0.0f, 0.0f, + SH_INTERFACE); + pos.x = m_partiPos[i].x; + pos.y = m_partiPos[i].y; + pos.z = 0.0f; + speed.x = (Rand()-0.5f)*0.5f; + speed.y = (0.3f+Rand()*0.3f); + speed.z = 0.0f; + dim.x = 0.01f+Rand()*0.01f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, + (ParticuleType)(PARTILENS1+rand()%3), + Rand()*0.5f+0.5f, 2.0f, 0.0f, + SH_INTERFACE); + } + if ( m_partiPhase[i] == 2 ) // étincelles ? + { + pos.x = m_partiPos[i].x; + pos.y = m_partiPos[i].y; + pos.z = 0.0f; + pos.x += (Rand()-0.5f)*0.01f; + pos.y += (Rand()-0.5f)*0.01f; + speed.x = (Rand()-0.5f)*0.2f; + speed.y = (Rand()-0.5f)*0.2f; + speed.z = 0.0f; + dim.x = 0.005f+Rand()*0.005f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, PARTIBLITZ, + Rand()*0.2f+0.2f, 0.0f, 0.0f, + SH_INTERFACE); + pos.x = m_partiPos[i].x; + pos.y = m_partiPos[i].y; + pos.z = 0.0f; + speed.x = (Rand()-0.5f)*0.5f; + speed.y = (0.3f+Rand()*0.3f); + speed.z = 0.0f; + dim.x = 0.005f+Rand()*0.005f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, PARTISCRAPS, + Rand()*0.5f+0.5f, 2.0f, 0.0f, + SH_INTERFACE); + } + if ( m_partiPhase[i] == 3 ) // fumée ? + { + pos.x = m_partiPos[i].x; + pos.y = m_partiPos[i].y; + pos.z = 0.0f; + pos.x += (Rand()-0.5f)*0.03f; + pos.y += (Rand()-0.5f)*0.03f; + speed.x = (Rand()-0.5f)*0.2f; + speed.y = Rand()*0.5f; + speed.z = 0.0f; + dim.x = 0.03f+Rand()*0.07f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, + Rand()*0.4f+0.4f, 0.0f, 0.0f, + SH_INTERFACE); + } + } + else + { + m_partiPhase[i] = 0; + m_partiTime[i] = 2.0f+Rand()*4.0f; + } + } + } +#endif +} + +// Quelques jolies particules pour suivre la souris. + +void CMainDialog::NiceParticule(FPOINT mouse, BOOL bPress) +{ + D3DVECTOR pos, speed; + FPOINT dim; + + if ( !m_bRain ) return; + if ( (m_phase == PHASE_SIMUL || + m_phase == PHASE_WIN || + m_phase == PHASE_LOST || + m_phase == PHASE_MODEL ) && + !m_bDialog ) return; + + if ( bPress ) + { + pos.x = mouse.x; + pos.y = mouse.y; + pos.z = 0.0f; + speed.x = (Rand()-0.5f)*0.5f; + speed.y = (0.3f+Rand()*0.3f); + speed.z = 0.0f; + dim.x = 0.005f+Rand()*0.005f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, PARTISCRAPS, + Rand()*0.5f+0.5f, 2.0f, 0.0f, + SH_INTERFACE); + } + else + { + pos.x = mouse.x; + pos.y = mouse.y; + pos.z = 0.0f; + speed.x = (Rand()-0.5f)*0.5f; + speed.y = (0.3f+Rand()*0.3f); + speed.z = 0.0f; + dim.x = 0.01f+Rand()*0.01f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, + (ParticuleType)(PARTILENS1+rand()%3), + Rand()*0.5f+0.5f, 2.0f, 0.0f, + SH_INTERFACE); + } +} + + + +// Spécifie le dossier spécial utilisateur si nécessaire. + +void CMainDialog::SetUserDir(char *base, int rank) +{ + char dir[100]; + + if ( strcmp(base, "user") == 0 && rank >= 100 ) + { + sprintf(dir, "%s\\%s", m_userDir, m_userList[rank/100-1]); + UserDir(TRUE, dir); + } + else + { + UserDir(FALSE, ""); + } +} + +// Construit le nom de fichier d'une mission. + +void CMainDialog::BuildSceneName(char *filename, char *base, int rank) +{ + if ( strcmp(base, "user") == 0 ) + { + sprintf(filename, "%s\\%s\\scene%.2d.txt", m_userDir, m_userList[rank/100-1], rank%100); + } + else + { + sprintf(filename, "%s\\%s%.3d.txt", m_sceneDir, base, rank); + } +} + +// Construit le nom descriptif par défaut d'une mission. + +void CMainDialog::BuildResumeName(char *filename, char *base, int rank) +{ + sprintf(filename, "Scene %s %d", base, rank); +} + +// Retourne le nom du dossier où mettre les fichiers. + +char* CMainDialog::RetFilesDir() +{ + return m_filesDir; +} + + +// Met à jour la liste des joueurs d'après les dossiers sur disque. + +void CMainDialog::ReadNameList() +{ + CWindow* pw; + CList* pl; + long hFile; + struct _finddata_t fBuffer; + BOOL bDo; + char dir[_MAX_FNAME]; + char temp[_MAX_FNAME]; + char filenames[_MAX_FNAME][100]; + int nbFilenames, i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_NLIST); + if ( pl == 0 ) return; + pl->Flush(); + + nbFilenames = 0; + sprintf(dir, "%s\\*", m_savegameDir); + hFile = _findfirst(dir, &fBuffer); + if ( hFile != -1 ) + { + do + { + if ( (fBuffer.attrib & _A_SUBDIR) && fBuffer.name[0] != '.' ) + { + strcpy(filenames[nbFilenames++], fBuffer.name); + } + } + while ( _findnext(hFile, &fBuffer) == 0 && nbFilenames < 100 ); + } + do // trie tous les noms : + { + bDo = FALSE; + for ( i=0 ; i 0 ) + { + strcpy(temp, filenames[i]); + strcpy(filenames[i], filenames[i+1]); + strcpy(filenames[i+1], temp); + bDo = TRUE; + } + } + } + while ( bDo ); + + for ( i=0 ; iSetName(i, filenames[i]); + } +} + +// Met à jour les contrôles des joueurs. + +void CMainDialog::UpdateNameControl() +{ + CWindow* pw; + CList* pl; + CButton* pb; + CEdit* pe; + char name[100]; + char* gamer; + int total, sel; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_NLIST); + if ( pl == 0 ) return; + pe = (CEdit*)pw->SearchControl(EVENT_INTERFACE_NEDIT); + if ( pe == 0 ) return; + + gamer = m_main->RetGamerName(); + total = pl->RetTotal(); + sel = pl->RetSelect(); + pe->GetText(name, 100); + + pb = (CButton*)pw->SearchControl(EVENT_INTERFACE_NCANCEL); + if ( pb != 0 ) + { + pb->SetState(STATE_ENABLE, gamer[0]!=0); + } + + pb = (CButton*)pw->SearchControl(EVENT_INTERFACE_NDELETE); + if ( pb != 0 ) + { + pb->SetState(STATE_ENABLE, total>0 && sel!=-1); + } + + pb = (CButton*)pw->SearchControl(EVENT_INTERFACE_NOK); + if ( pb != 0 ) + { + pb->SetState(STATE_ENABLE, name[0]!=0 || sel!=-1); + } + + pb = (CButton*)pw->SearchControl(EVENT_INTERFACE_PERSO); + if ( pb != 0 ) + { + pb->SetState(STATE_ENABLE, name[0]!=0 || sel!=-1); + } +} + +// Met à jour la liste des joueurs en fonction du nom frapé. + +void CMainDialog::UpdateNameList() +{ + CWindow* pw; + CList* pl; + CEdit* pe; + char name[100]; + int total, sel, i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_NLIST); + if ( pl == 0 ) return; + pe = (CEdit*)pw->SearchControl(EVENT_INTERFACE_NEDIT); + if ( pe == 0 ) return; + + pe->GetText(name, 100); + total = pl->RetTotal(); + sel = pl->RetSelect(); + + for ( i=0 ; iRetName(i)) == 0 ) + { + pl->SetSelect(i); + pl->ShowSelect(FALSE); + return; + } + } + + pl->SetSelect(-1); +} + +// Met à jour le nom du joueur et fonction de la liste sélectionnée. + +void CMainDialog::UpdateNameEdit() +{ + CWindow* pw; + CList* pl; + CEdit* pe; + char* name; + int sel; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_NLIST); + if ( pl == 0 ) return; + pe = (CEdit*)pw->SearchControl(EVENT_INTERFACE_NEDIT); + if ( pe == 0 ) return; + + sel = pl->RetSelect(); + if ( sel == -1 ) + { + pe->SetText(""); + pe->SetCursor(0, 0); + } + else + { + name = pl->RetName(sel); + pe->SetText(name); + pe->SetCursor(strlen(name), 0); + } + + UpdateNameControl(); +} + +// Met à jour la représentation du joueur en fonction de la liste sélectionnée. + +void CMainDialog::UpdateNameFace() +{ + CWindow* pw; + CList* pl; + char* name; + int sel; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_NLIST); + if ( pl == 0 ) return; + + sel = pl->RetSelect(); + if ( sel == -1 ) return; + name = pl->RetName(sel); + + ReadGamerPerso(name); +} + +// Sélectionne un joueur. + +void CMainDialog::NameSelect() +{ + CWindow* pw; + CList* pl; + CEdit* pe; + char name[100]; + int sel; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_NLIST); + if ( pl == 0 ) return; + pe = (CEdit*)pw->SearchControl(EVENT_INTERFACE_NEDIT); + if ( pe == 0 ) return; + + pe->GetText(name, 100); + sel = pl->RetSelect(); + + if ( sel == -1 ) + { + NameCreate(); + } + else + { + m_main->SetGamerName(pl->RetName(sel)); + m_main->ChangePhase(PHASE_INIT); + } + + RetGamerFace(m_main->RetGamerName()); + + SetProfileString("Gamer", "LastName", m_main->RetGamerName()); +} + +// Crée un nouveau joueur. + +void CMainDialog::NameCreate() +{ + CWindow* pw; + CEdit* pe; + char name[100]; + char dir[100]; + char c; + int len, i, j; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pe = (CEdit*)pw->SearchControl(EVENT_INTERFACE_NEDIT); + if ( pe == 0 ) return; + + pe->GetText(name, 100); + if ( name[0] == 0 ) + { + m_sound->Play(SOUND_TZOING); + return; + } + + len = strlen(name); + j = 0; + for ( i=0 ; i= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + c == ' ' || + c == '-' || + c == '_' || + c == '.' || + c == ',' || + c == '\'' ) + { + name[j++] = name[i]; + } + } + name[j] = 0; + if ( j == 0 ) + { + m_sound->Play(SOUND_TZOING); + return; + } + + _mkdir(m_savegameDir); // si n'existe pas encore ! + + sprintf(dir, "%s\\%s", m_savegameDir, name); + if ( _mkdir(dir) != 0 ) + { + m_sound->Play(SOUND_TZOING); + pe->SetText(name); + pe->SetCursor(strlen(name), 0); + pe->SetFocus(TRUE); + return; + } + + SetGamerFace(name, 0); + + m_main->SetGamerName(name); + m_main->ChangePhase(PHASE_INIT); +} + +// Supprime un dossier et toute sa descendance. + +BOOL RemoveDir(char *dirname) +{ + long hFile; + struct _finddata_t fBuffer; + char filename[100]; + + sprintf(filename, "%s\\*", dirname); + hFile = _findfirst(filename, &fBuffer); + if ( hFile != -1 ) + { + do + { + if ( fBuffer.name[0] != '.' ) + { + if ( fBuffer.attrib & _A_SUBDIR ) + { + sprintf(filename, "%s\\%s", dirname, fBuffer.name); + RemoveDir(filename); + } + else + { + sprintf(filename, "%s\\%s", dirname, fBuffer.name); + remove(filename); + } + } + } + while ( _findnext(hFile, &fBuffer) == 0 ); + } + + if ( _rmdir(dirname) != 0 ) + { + return FALSE; + } + return TRUE; +} + +// Supprime un joueur. + +void CMainDialog::NameDelete() +{ + CWindow* pw; + CList* pl; + int sel; + char* gamer; + char dir[100]; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_NLIST); + if ( pl == 0 ) return; + + sel = pl->RetSelect(); + if ( sel == -1 ) + { + m_sound->Play(SOUND_TZOING); + return; + } + gamer = pl->RetName(sel); + + // Supprime tout le contenu du dossier. + sprintf(dir, "%s\\%s", m_savegameDir, gamer); + if ( !RemoveDir(dir) ) + { + m_sound->Play(SOUND_TZOING); + return; + } + + m_main->SetGamerName(""); + pl->SetSelect(-1); + + ReadNameList(); + UpdateNameList(); + UpdateNameControl(); +} + + + +// Teste si deux couleurs sont égales ou presque. + +BOOL EqColor(const D3DCOLORVALUE &c1, const D3DCOLORVALUE &c2) +{ + return (Abs(c1.r-c2.r) < 0.01f && + Abs(c1.g-c2.g) < 0.01f && + Abs(c1.b-c2.b) < 0.01f ); +} + +// Met à jour tous les boutons pour le personnage. + +void CMainDialog::UpdatePerso() +{ + CWindow* pw; + CLabel* pl; + CButton* pb; + CColor* pc; + CSlider* ps; + D3DCOLORVALUE color; + char name[100]; + int i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + + pb = (CButton*)pw->SearchControl(EVENT_INTERFACE_PHEAD); + if ( pb != 0 ) + { + pb->SetState(STATE_CHECK, m_persoTab==0); + } + pb = (CButton*)pw->SearchControl(EVENT_INTERFACE_PBODY); + if ( pb != 0 ) + { + pb->SetState(STATE_CHECK, m_persoTab==1); + } + + pl = (CLabel*)pw->SearchControl(EVENT_LABEL11); + if ( pl != 0 ) + { + if ( m_persoTab == 0 ) + { + pl->SetState(STATE_VISIBLE); + GetResource(RES_TEXT, RT_PERSO_FACE, name); + pl->SetName(name); + } + else + { + pl->ClearState(STATE_VISIBLE); + } + } + + pl = (CLabel*)pw->SearchControl(EVENT_LABEL12); + if ( pl != 0 ) + { + if ( m_persoTab == 0 ) + { + pl->SetState(STATE_VISIBLE); + GetResource(RES_TEXT, RT_PERSO_GLASSES, name); + pl->SetName(name); + } + else + { + pl->ClearState(STATE_VISIBLE); + } + } + + pl = (CLabel*)pw->SearchControl(EVENT_LABEL13); + if ( pl != 0 ) + { + if ( m_persoTab == 0 ) GetResource(RES_TEXT, RT_PERSO_HAIR, name); + else GetResource(RES_TEXT, RT_PERSO_BAND, name); + pl->SetName(name); + } + + pl = (CLabel*)pw->SearchControl(EVENT_LABEL14); + if ( pl != 0 ) + { + if ( m_persoTab == 0 ) + { + pl->ClearState(STATE_VISIBLE); + } + else + { + pl->SetState(STATE_VISIBLE); + GetResource(RES_TEXT, RT_PERSO_COMBI, name); + pl->SetName(name); + } + } + + for ( i=0 ; i<4 ; i++ ) + { + pb = (CButton*)pw->SearchControl((EventMsg)(EVENT_INTERFACE_PFACE1+i)); + if ( pb == 0 ) break; + pb->SetState(STATE_VISIBLE, m_persoTab==0); + pb->SetState(STATE_CHECK, i==m_perso.face); + } + + for ( i=0 ; i<10 ; i++ ) + { + pb = (CButton*)pw->SearchControl((EventMsg)(EVENT_INTERFACE_PGLASS0+i)); + if ( pb == 0 ) break; + pb->SetState(STATE_VISIBLE, m_persoTab==0); + pb->SetState(STATE_CHECK, i==m_perso.glasses); + } + + for ( i=0 ; i<3*3 ; i++ ) + { + pc = (CColor*)pw->SearchControl((EventMsg)(EVENT_INTERFACE_PC0a+i)); + if ( pc == 0 ) break; + if ( m_persoTab == 0 ) + { + pc->ClearState(STATE_VISIBLE); + } + else + { + pc->SetState(STATE_VISIBLE); + color.r = perso_color[3*10*1+3*i+0]/255.0f; + color.g = perso_color[3*10*1+3*i+1]/255.0f; + color.b = perso_color[3*10*1+3*i+2]/255.0f; + color.a = 0.0f; + pc->SetColor(color); + pc->SetState(STATE_CHECK, EqColor(color, m_perso.colorCombi)); + } + + pc = (CColor*)pw->SearchControl((EventMsg)(EVENT_INTERFACE_PC0b+i)); + if ( pc == 0 ) break; + color.r = perso_color[3*10*2*m_persoTab+3*i+0]/255.0f; + color.g = perso_color[3*10*2*m_persoTab+3*i+1]/255.0f; + color.b = perso_color[3*10*2*m_persoTab+3*i+2]/255.0f; + color.a = 0.0f; + pc->SetColor(color); + pc->SetState(STATE_CHECK, EqColor(color, m_persoTab?m_perso.colorBand:m_perso.colorHair)); + } + + for ( i=0 ; i<3 ; i++ ) + { + ps = (CSlider*)pw->SearchControl((EventMsg)(EVENT_INTERFACE_PCRa+i)); + if ( ps == 0 ) break; + ps->SetState(STATE_VISIBLE, m_persoTab==1); + } + + if ( m_persoTab == 1 ) + { + color = m_perso.colorCombi; + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCRa); + if ( ps != 0 ) ps->SetVisibleValue(color.r*255.0f); + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCGa); + if ( ps != 0 ) ps->SetVisibleValue(color.g*255.0f); + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCBa); + if ( ps != 0 ) ps->SetVisibleValue(color.b*255.0f); + } + + if ( m_persoTab == 0 ) color = m_perso.colorHair; + else color = m_perso.colorBand; + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCRb); + if ( ps != 0 ) ps->SetVisibleValue(color.r*255.0f); + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCGb); + if ( ps != 0 ) ps->SetVisibleValue(color.g*255.0f); + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCBb); + if ( ps != 0 ) ps->SetVisibleValue(color.b*255.0f); +} + +// Met à jour la caméra pour le personnage. + +void CMainDialog::CameraPerso() +{ + if ( m_persoTab == 0 ) + { +//? m_camera->Init(D3DVECTOR(4.0f, 0.0f, 0.0f), +//? D3DVECTOR(0.0f, 0.0f, 1.0f), 0.0f); + m_camera->Init(D3DVECTOR(6.0f, 0.0f, 0.0f), + D3DVECTOR(0.0f, 0.2f, 1.5f), 0.0f); + } + else + { + m_camera->Init(D3DVECTOR(18.0f, 0.0f, 4.5f), + D3DVECTOR(0.0f, 1.6f, 4.5f), 0.0f); + } + + m_camera->SetType(CAMERA_SCRIPT); + m_camera->FixCamera(); +} + +// Met une couleur fixe. + +void CMainDialog::FixPerso(int rank, int index) +{ + if ( m_persoTab == 0 ) + { + if ( index == 1 ) + { + m_perso.colorHair.r = perso_color[3*10*0+rank*3+0]/255.0f; + m_perso.colorHair.g = perso_color[3*10*0+rank*3+1]/255.0f; + m_perso.colorHair.b = perso_color[3*10*0+rank*3+2]/255.0f; + } + } + if ( m_persoTab == 1 ) + { + if ( index == 0 ) + { + m_perso.colorCombi.r = perso_color[3*10*1+rank*3+0]/255.0f; + m_perso.colorCombi.g = perso_color[3*10*1+rank*3+1]/255.0f; + m_perso.colorCombi.b = perso_color[3*10*1+rank*3+2]/255.0f; + } + if ( index == 1 ) + { + m_perso.colorBand.r = perso_color[3*10*2+rank*3+0]/255.0f; + m_perso.colorBand.g = perso_color[3*10*2+rank*3+1]/255.0f; + m_perso.colorBand.b = perso_color[3*10*2+rank*3+2]/255.0f; + } + } +} + +// Met à jour les couleurs du personnage. + +void CMainDialog::ColorPerso() +{ + CWindow* pw; + CSlider* ps; + D3DCOLORVALUE color; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + + color.a = 0.0f; + + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCRa); + if ( ps != 0 ) color.r = ps->RetVisibleValue()/255.0f; + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCGa); + if ( ps != 0 ) color.g = ps->RetVisibleValue()/255.0f; + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCBa); + if ( ps != 0 ) color.b = ps->RetVisibleValue()/255.0f; + if ( m_persoTab == 1 ) m_perso.colorCombi = color; + + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCRb); + if ( ps != 0 ) color.r = ps->RetVisibleValue()/255.0f; + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCGb); + if ( ps != 0 ) color.g = ps->RetVisibleValue()/255.0f; + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_PCBb); + if ( ps != 0 ) color.b = ps->RetVisibleValue()/255.0f; + if ( m_persoTab == 0 ) m_perso.colorHair = color; + else m_perso.colorBand = color; +} + +// Met à jour les paramètres par défaut du personnage. + +void CMainDialog::DefPerso() +{ + m_perso.colorCombi.r = 206.0f/256.0f; + m_perso.colorCombi.g = 206.0f/256.0f; + m_perso.colorCombi.b = 204.0f/256.0f; // ~blanc + m_perso.colorBand.r = 255.0f/256.0f; + m_perso.colorBand.g = 132.0f/256.0f; + m_perso.colorBand.b = 1.0f/256.0f; // orange + + if ( m_perso.face == 0 ) // normal ? + { + m_perso.glasses = 0; + m_perso.colorHair.r = 90.0f/256.0f; + m_perso.colorHair.g = 95.0f/256.0f; + m_perso.colorHair.b = 85.0f/256.0f; // noir + } + if ( m_perso.face == 1 ) // chauve ? + { + m_perso.glasses = 0; + m_perso.colorHair.r = 83.0f/256.0f; + m_perso.colorHair.g = 64.0f/256.0f; + m_perso.colorHair.b = 51.0f/256.0f; // brun + } + if ( m_perso.face == 2 ) // carlos ? + { + m_perso.glasses = 1; + m_perso.colorHair.r = 85.0f/256.0f; + m_perso.colorHair.g = 48.0f/256.0f; + m_perso.colorHair.b = 9.0f/256.0f; // brun + } + if ( m_perso.face == 3 ) // blond ? + { + m_perso.glasses = 4; + m_perso.colorHair.r = 255.0f/256.0f; + m_perso.colorHair.g = 255.0f/256.0f; + m_perso.colorHair.b = 181.0f/256.0f; // jaune + } + + m_perso.colorHair.a = 0.0f; + m_perso.colorCombi.a = 0.0f; + m_perso.colorBand.a = 0.0f; +} + + +// Indique s'il existe au moins une sauvegarde. + +BOOL CMainDialog::IsIOReadScene() +{ + FILE* file; + char filename[100]; + + sprintf(filename, "%s\\%s\\save%c%.3d\\data.sav", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], 0); + file = fopen(filename, "r"); + if ( file == NULL ) return FALSE; + fclose(file); + return TRUE; +} + +// Construit le nom du fichier par défaut. + +void CMainDialog::IOReadName() +{ + FILE* file; + CWindow* pw; + CEdit* pe; + char filename[_MAX_FNAME]; + char op[100]; + char line[500]; + char resume[100]; + char name[100]; + time_t now; + int i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pe = (CEdit*)pw->SearchControl(EVENT_INTERFACE_IONAME); + if ( pe == 0 ) return; + + sprintf(resume, "%s %d", m_sceneName, m_chap[m_index]+1); + BuildSceneName(filename, m_sceneName, (m_chap[m_index]+1)*100); + file = fopen(filename, "r"); + if ( file != NULL ) + { + while ( fgets(line, 500, file) != NULL ) + { + for ( i=0 ; i<500 ; i++ ) + { + if ( line[i] == '\t' ) line[i] = ' '; // remplace tab par space + if ( line[i] == '/' && line[i+1] == '/' ) + { + line[i] = 0; + break; + } + } + + sprintf(op, "Title.%c", RetLanguageLetter()); + if ( Cmd(line, op) ) + { + OpString(line, "resume", resume); + break; + } + } + fclose(file); + } + + time(&now); + TimeToAscii(now, line); + sprintf(name, "%s %d - %s", resume, m_sel[m_index]+1, line); + pe->SetText(name); + pe->SetCursor(strlen(name), 0); + pe->SetFocus(TRUE); +} + +// Met à jour la liste des parties enregistrées sur disque. + +void CMainDialog::IOReadList() +{ + FILE* file = NULL; + CWindow* pw; + CList* pl; + char filename[100]; + char line[500]; + char name[100]; + int i, j; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_IOLIST); + if ( pl == 0 ) return; + + pl->Flush(); + + for ( j=0 ; j<999 ; j++ ) + { + sprintf(filename, "%s\\%s\\save%c%.3d\\data.sav", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], j); + file = fopen(filename, "r"); + if ( file == NULL ) break; + + strcmp(name, filename); // nom par défaut + while ( fgets(line, 500, file) != NULL ) + { + for ( i=0 ; i<500 ; i++ ) + { + if ( line[i] == '\t' ) line[i] = ' '; // remplace tab par space + if ( line[i] == '/' && line[i+1] == '/' ) + { + line[i] = 0; + break; + } + } + + if ( Cmd(line, "Title") ) + { + OpString(line, "text", name); + break; + } + } + fclose(file); + + pl->SetName(j, name); + } + + if ( m_phase == PHASE_WRITE || + m_phase == PHASE_WRITEs ) + { + GetResource(RES_TEXT, RT_IO_NEW, name); + pl->SetName(j, name); + j ++; + } + + pl->SetSelect(j-1); + pl->ShowSelect(FALSE); // montre la ligne sélectionnée +} + +// Met à jour les boutons en fonction de la partie sélectionnée +// dans la liste. + +void CMainDialog::IOUpdateList() +{ + FILE* file = NULL; + CWindow* pw; + CList* pl; + CButton* pb; + CImage* pi; + char filename[100]; + int sel, max; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_IOLIST); + if ( pl == 0 ) return; + pi = (CImage*)pw->SearchControl(EVENT_INTERFACE_IOIMAGE); + if ( pi == 0 ) return; + + sel = pl->RetSelect(); + max = pl->RetTotal(); + + sprintf(filename, "%s\\%s\\save%c%.3d\\screen.bmp", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], sel); + + if ( m_phase == PHASE_WRITE || + m_phase == PHASE_WRITEs ) + { + if ( sel < max-1 ) + { + pi->SetFilenameImage(filename); + } + else + { + pi->SetFilenameImage(""); + } + + pb = (CButton*)pw->SearchControl(EVENT_INTERFACE_IODELETE); + if ( pb != 0 ) + { + pb->SetState(STATE_ENABLE, sel < max-1); + } + } + else + { + pi->SetFilenameImage(filename); + } +} + +// Supprime la scène sélectionnée. + +void CMainDialog::IODeleteScene() +{ + CWindow* pw; + CList* pl; + char dir[100]; + char old[100]; + long hFile; + struct _finddata_t fBuffer; + int sel, max, i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_IOLIST); + if ( pl == 0 ) return; + + sel = pl->RetSelect(); + if ( sel == -1 ) + { + m_sound->Play(SOUND_TZOING); + return; + } + + // Supprime tout le contenu du dossier. + sprintf(dir, "%s\\%s\\save%c%.3d\\*", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], sel); + hFile = _findfirst(dir, &fBuffer); + if ( hFile != -1 ) + { + do + { + if ( fBuffer.name[0] != '.' ) + { + sprintf(dir, "%s\\%s\\save%c%.3d\\%s", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], sel, fBuffer.name); + remove(dir); + } + } + while ( _findnext(hFile, &fBuffer) == 0 ); + } + + sprintf(dir, "%s\\%s\\save%c%.3d", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], sel); + if ( _rmdir(dir) != 0 ) + { + m_sound->Play(SOUND_TZOING); + return; + } + + max = pl->RetTotal(); + for ( i=sel+1 ; iRetGamerName(), m_sceneName[0], i); + sprintf(dir, "%s\\%s\\save%c%.3d", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], i-1); + rename(old, dir); + } + IOReadList(); +} + +// Ecrit la scène. + +BOOL CMainDialog::IOWriteScene() +{ + CWindow* pw; + CList* pl; + CEdit* pe; + char filename[100]; + char filecbot[100]; + char info[100]; + int sel; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return FALSE; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_IOLIST); + if ( pl == 0 ) return FALSE; + pe = (CEdit*)pw->SearchControl(EVENT_INTERFACE_IONAME); + if ( pe == 0 ) return FALSE; + + sel = pl->RetSelect(); + if ( sel == -1 ) return FALSE; + + _mkdir("Savegame"); // si n'existe pas encore ! + sprintf(filename, "%s\\%s", m_savegameDir, m_main->RetGamerName()); + _mkdir(filename); + sprintf(filename, "%s\\%s\\save%c%.3d", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], sel); + _mkdir(filename); + + sprintf(filename, "%s\\%s\\save%c%.3d\\data.sav", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], sel); + sprintf(filecbot, "%s\\%s\\save%c%.3d\\cbot.run", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], sel); + pe->GetText(info, 100); + m_main->IOWriteScene(filename, filecbot, info); + + m_shotDelay = 3; + sprintf(m_shotName, "%s\\%s\\save%c%.3d\\screen.bmp", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], sel); + + return TRUE; +} + +// Lit la scène. + +BOOL CMainDialog::IOReadScene() +{ + FILE* file; + CWindow* pw; + CList* pl; + char filename[100]; + char filecbot[100]; + char line[500]; + char dir[100]; + int sel, i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return FALSE; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_IOLIST); + if ( pl == 0 ) return FALSE; + + sel = pl->RetSelect(); + if ( sel == -1 ) return FALSE; + + sprintf(filename, "%s\\%s\\save%c%.3d\\data.sav", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], sel); + sprintf(filecbot, "%s\\%s\\save%c%.3d\\cbot.run", m_savegameDir, m_main->RetGamerName(), m_sceneName[0], sel); + + file = fopen(filename, "r"); + if ( file == NULL ) return FALSE; + + while ( fgets(line, 500, file) != NULL ) + { + for ( i=0 ; i<500 ; i++ ) + { + if ( line[i] == '\t' ) line[i] = ' '; // remplace tab par space + if ( line[i] == '/' && line[i+1] == '/' ) + { + line[i] = 0; + break; + } + } + + if ( Cmd(line, "Mission") ) + { + OpString(line, "base", m_sceneName); + m_sceneRank = OpInt(line, "rank", 0); + + if ( strcmp(m_sceneName, "user") == 0 ) + { + m_sceneRank = m_sceneRank%100; + OpString(line, "dir", dir); + for ( i=0 ; iRetShowAll() ) return 9; + + for ( j=0 ; j<9 ; j++ ) + { + if ( !RetGamerInfoPassed((j+1)*100) ) + { + return j; + } + } + return 9; +} + +// Met à jour les listes selon le cheat code. + +void CMainDialog::AllMissionUpdate() +{ + if ( m_phase == PHASE_TRAINER || + m_phase == PHASE_DEFI || + m_phase == PHASE_MISSION || + m_phase == PHASE_FREE || + m_phase == PHASE_TEEN || + m_phase == PHASE_USER || + m_phase == PHASE_PROTO ) + { + UpdateSceneChap(m_chap[m_index]); + UpdateSceneList(m_chap[m_index], m_sel[m_index]); + } +} + +// Met à jour les chapitres des exercices ou missions. + +void CMainDialog::UpdateSceneChap(int &chap) +{ + FILE* file = NULL; + CWindow* pw; + CList* pl; + long hFile; + struct _finddata_t fileBuffer; + char filename[_MAX_FNAME]; + char op[100]; + char line[500]; + char name[100]; + int i, j; + BOOL bPassed, bDo; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_CHAP); + if ( pl == 0 ) return; + + pl->Flush(); + + if ( m_phase == PHASE_USER ) + { + j = 0; + hFile = _findfirst("user\\*", &fileBuffer); + if ( hFile != -1 ) + { + do + { + if ( (fileBuffer.attrib & _A_SUBDIR) != 0 && + fileBuffer.name[0] != '.' ) + { + strcpy(m_userList[j++], fileBuffer.name); + } + } + while ( _findnext(hFile, &fileBuffer) == 0 && j < 100 ); + } + m_userTotal = j; + + do // trie tous les noms : + { + bDo = FALSE; + for ( i=0 ; i 0 ) + { + strcpy(name, m_userList[i]); + strcpy(m_userList[i], m_userList[i+1]); + strcpy(m_userList[i+1], name); + bDo = TRUE; + } + } + } + while ( bDo ); + + for ( j=0 ; jSetName(j, name); + pl->SetEnable(j, TRUE); + } + } + else + { + for ( j=0 ; j<9 ; j++ ) + { +#if _SCHOOL + if ( m_phase == PHASE_MISSION ) break; + if ( m_phase == PHASE_FREE ) break; +#if _CEEBOTDEMO + if ( m_phase == PHASE_TRAINER && j >= 2 ) break; +#endif +#endif +#if _DEMO + if ( m_phase == PHASE_MISSION && j >= 4 ) break; + if ( m_phase == PHASE_TRAINER && j >= 1 ) break; +#endif + BuildSceneName(filename, m_sceneName, (j+1)*100); + file = fopen(filename, "r"); + if ( file == NULL ) break; + + BuildResumeName(name, m_sceneName, j+1); // nom par défaut + while ( fgets(line, 500, file) != NULL ) + { + for ( i=0 ; i<500 ; i++ ) + { + if ( line[i] == '\t' ) line[i] = ' '; // remplace tab par space + if ( line[i] == '/' && line[i+1] == '/' ) + { + line[i] = 0; + break; + } + } + + sprintf(op, "Title.%c", RetLanguageLetter()); + if ( Cmd(line, op) ) + { + OpString(line, "text", name); + break; + } + } + fclose(file); + + bPassed = RetGamerInfoPassed((j+1)*100); + sprintf(line, "%d: %s", j+1, name); + pl->SetName(j, line); + pl->SetCheck(j, bPassed); + pl->SetEnable(j, TRUE); + + if ( m_phase == PHASE_MISSION && !m_main->RetShowAll() && !bPassed ) + { + j ++; + break; + } + +#if _TEEN + if ( m_phase == PHASE_TRAINER && !m_main->RetShowAll() && !bPassed ) + { + j ++; + break; + } +#endif + + if ( m_phase == PHASE_FREE && j == m_accessChap ) + { + j ++; + break; + } + } + } + + if ( chap > j-1 ) chap = j-1; + + pl->SetSelect(chap); + pl->ShowSelect(FALSE); // montre la ligne sélectionnée +} + +// Met à jour la liste des exercices ou missions. + +void CMainDialog::UpdateSceneList(int chap, int &sel) +{ + FILE* file = NULL; + CWindow* pw; + CList* pl; + char filename[_MAX_FNAME]; + char op[100]; + char line[500]; + char name[100]; + int i, j; + BOOL bPassed; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_INTERFACE_LIST); + if ( pl == 0 ) return; + + pl->Flush(); + + for ( j=0 ; j<99 ; j++ ) + { +#if _SCHOOL + if ( m_phase == PHASE_MISSION ) break; + if ( m_phase == PHASE_FREE ) break; +#if _CEEBOTDEMO +#if _TEEN + if ( m_phase == PHASE_TRAINER && j >= 5 ) break; +#else + if ( m_phase == PHASE_TRAINER && j >= 3 ) break; +#endif +#endif +#endif +#if _DEMO + if ( m_phase == PHASE_MISSION && j >= 3 ) break; + if ( m_phase == PHASE_TRAINER && j >= 5 ) break; +#endif + BuildSceneName(filename, m_sceneName, (chap+1)*100+(j+1)); + file = fopen(filename, "r"); + if ( file == NULL ) break; + + BuildResumeName(name, m_sceneName, j+1); // nom par défaut + while ( fgets(line, 500, file) != NULL ) + { + for ( i=0 ; i<500 ; i++ ) + { + if ( line[i] == '\t' ) line[i] = ' '; // remplace tab par space + if ( line[i] == '/' && line[i+1] == '/' ) + { + line[i] = 0; + break; + } + } + + sprintf(op, "Title.%c", RetLanguageLetter()); + if ( Cmd(line, op) ) + { + OpString(line, "text", name); + break; + } + } + fclose(file); + + bPassed = RetGamerInfoPassed((chap+1)*100+(j+1)); + sprintf(line, "%d: %s", j+1, name); + pl->SetName(j, line); + pl->SetCheck(j, bPassed); + pl->SetEnable(j, TRUE); + + if ( m_phase == PHASE_MISSION && !m_main->RetShowAll() && !bPassed ) + { + j ++; + break; + } + +#if _TEEN + if ( m_phase == PHASE_TRAINER && !m_main->RetShowAll() && !bPassed ) + { + j ++; + break; + } +#endif + } + + BuildSceneName(filename, m_sceneName, (chap+1)*100+(j+1)); + file = fopen(filename, "r"); + if ( file == NULL ) + { + m_maxList = j; + } + else + { + m_maxList = j+1; // ce n'est pas le dernier ! + fclose(file); + } + + if ( sel > j-1 ) sel = j-1; + + pl->SetSelect(sel); + pl->ShowSelect(FALSE); // montre la ligne sélectionnée +} + +// Met à jour le bouton "solution" selon le cheat code. + +void CMainDialog::ShowSoluceUpdate() +{ + CWindow* pw; + CEdit* pe; + CCheck* pc; + + if ( m_phase == PHASE_TRAINER || + m_phase == PHASE_DEFI || + m_phase == PHASE_MISSION || + m_phase == PHASE_FREE || + m_phase == PHASE_TEEN || + m_phase == PHASE_USER || + m_phase == PHASE_PROTO ) + { + m_bSceneSoluce = FALSE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pe = (CEdit*)pw->SearchControl(EVENT_INTERFACE_RESUME); + if ( pe == 0 ) return; + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_SOLUCE); + if ( pc == 0 ) return; + + if ( m_main->RetShowSoluce() ) + { + pc->SetState(STATE_VISIBLE); + pc->SetState(STATE_CHECK); + m_bSceneSoluce = TRUE; + } + else + { + pc->ClearState(STATE_VISIBLE); + pc->ClearState(STATE_CHECK); + m_bSceneSoluce = FALSE; + } + } +} + +// Met à jour un résumé d'exercice ou de mission. + +void CMainDialog::UpdateSceneResume(int rank) +{ + FILE* file = NULL; + CWindow* pw; + CEdit* pe; + CCheck* pc; + char filename[_MAX_FNAME]; + char op[100]; + char line[500]; + char name[500]; + int i, numTry; + BOOL bPassed, bVisible; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pe = (CEdit*)pw->SearchControl(EVENT_INTERFACE_RESUME); + if ( pe == 0 ) return; + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_SOLUCE); + + if ( pc == 0 ) + { + m_bSceneSoluce = FALSE; + } + else + { + numTry = RetGamerInfoTry(rank); + bPassed = RetGamerInfoPassed(rank); + bVisible = ( numTry > 2 || bPassed || m_main->RetShowSoluce() ); + if ( !RetSoluce4() ) bVisible = FALSE; + pc->SetState(STATE_VISIBLE, bVisible); + if ( !bVisible ) + { + pc->ClearState(STATE_CHECK); + m_bSceneSoluce = FALSE; + } + } + + BuildSceneName(filename, m_sceneName, rank); + file = fopen(filename, "r"); + if ( file == NULL ) return; + + name[0] = 0; + while ( fgets(line, 500, file) != NULL ) + { + for ( i=0 ; i<500 ; i++ ) + { + if ( line[i] == '\t' ) line[i] = ' '; // remplace tab par space + if ( line[i] == '/' && line[i+1] == '/' ) + { + line[i] = 0; + break; + } + } + + sprintf(op, "Resume.%c", RetLanguageLetter()); + if ( Cmd(line, op) ) + { + OpString(line, "text", name); + break; + } + } + fclose(file); + + pe->SetText(name); +} + +// Met à jour la liste des devices. + +void CMainDialog::UpdateDisplayDevice() +{ + CWindow* pw; + CList* pl; + char bufDevices[1000]; + char bufModes[5000]; + int i, j, totalDevices, selectDevices, totalModes, selectModes; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_LIST1); + if ( pl == 0 ) return; + pl->Flush(); + + m_engine->EnumDevices(bufDevices, 1000, + bufModes, 5000, + totalDevices, selectDevices, + totalModes, selectModes); + + i = 0; + j = 0; + while ( bufDevices[i] != 0 ) + { + pl->SetName(j++, bufDevices+i); + while ( bufDevices[i++] != 0 ); + } + + pl->SetSelect(selectDevices); + pl->ShowSelect(FALSE); + + m_setupSelDevice = selectDevices; +} + +// Met à jour la liste des modes. + +void CMainDialog::UpdateDisplayMode() +{ + CWindow* pw; + CList* pl; + char bufDevices[1000]; + char bufModes[5000]; + int i, j, totalDevices, selectDevices, totalModes, selectModes; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_LIST2); + if ( pl == 0 ) return; + pl->Flush(); + + m_engine->EnumDevices(bufDevices, 1000, + bufModes, 5000, + totalDevices, selectDevices, + totalModes, selectModes); + + i = 0; + j = 0; + while ( bufModes[i] != 0 ) + { + pl->SetName(j++, bufModes+i); + while ( bufModes[i++] != 0 ); + } + + pl->SetSelect(selectModes); + pl->ShowSelect(FALSE); + + m_setupSelMode = selectModes; +} + +// Change le mode graphique. + +void CMainDialog::ChangeDisplay() +{ + CWindow* pw; + CList* pl; + CCheck* pc; + char* device; + char* mode; + BOOL bFull; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + + pl = (CList*)pw->SearchControl(EVENT_LIST1); + if ( pl == 0 ) return; + m_setupSelDevice = pl->RetSelect(); + device = pl->RetName(m_setupSelDevice); + + pl = (CList*)pw->SearchControl(EVENT_LIST2); + if ( pl == 0 ) return; + m_setupSelMode = pl->RetSelect(); + mode = pl->RetName(m_setupSelMode); + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_FULL); + if ( pc == 0 ) return; + bFull = pc->TestState(STATE_CHECK); + m_setupFull = bFull; + + m_engine->ChangeDevice(device, mode, bFull); + + if ( m_bSimulSetup ) + { + m_main->ChangeColor(); + m_main->UpdateMap(); + } +} + + + +// Met à jour le bouton "appliquer". + +void CMainDialog::UpdateApply() +{ + CWindow* pw; + CButton* pb; + CList* pl; + CCheck* pc; + int sel1, sel2; + BOOL bFull; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + + pb = (CButton*)pw->SearchControl(EVENT_INTERFACE_APPLY); + if ( pb == 0 ) return; + + pl = (CList*)pw->SearchControl(EVENT_LIST1); + if ( pl == 0 ) return; + sel1 = pl->RetSelect(); + + pl = (CList*)pw->SearchControl(EVENT_LIST2); + if ( pl == 0 ) return; + sel2 = pl->RetSelect(); + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_FULL); + bFull = pc->TestState(STATE_CHECK); + + if ( sel1 == m_setupSelDevice && + sel2 == m_setupSelMode && + bFull == m_setupFull ) + { + pb->ClearState(STATE_ENABLE); + } + else + { + pb->SetState(STATE_ENABLE); + } +} + +// Met à jour les boutons pendant la phase de setup. + +void CMainDialog::UpdateSetupButtons() +{ + CWindow* pw; + CCheck* pc; + CEditValue* pv; + CSlider* ps; + float value; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_TOTO); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetTotoMode()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_TOOLTIP); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_bTooltip); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_GLINT); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_bGlint); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_RAIN); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_bRain); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_MOUSE); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetNiceMouse()); + pc->SetState(STATE_ENABLE, m_engine->RetNiceMouseCap()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_EDITMODE); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetEditIndentMode()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_EDITVALUE); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetEditIndentValue()>2); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_SOLUCE4); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_bSoluce4); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_MOVIES); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_bMovies); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_NICERST); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_bNiceReset); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_HIMSELF); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_bHimselfDamage); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_SCROLL); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_bCameraScroll); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_INVERTX); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_bCameraInvertX); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_INVERTY); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_bCameraInvertY); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_EFFECT); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_bEffect); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_SHADOW); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetShadow()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_GROUND); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetGroundSpot()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_DIRTY); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetDirty()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_FOG); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetFog()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_LENS); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetLensMode()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_SKY); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetSkyMode()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_PLANET); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetPlanetMode()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_LIGHT); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetLightMode()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_JOYSTICK); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_engine->RetJoystick()); + } + + pv = (CEditValue*)pw->SearchControl(EVENT_INTERFACE_PARTI); + if ( pv != 0 ) + { + value = m_engine->RetParticuleDensity(); + pv->SetValue(value); + } + + pv = (CEditValue*)pw->SearchControl(EVENT_INTERFACE_CLIP); + if ( pv != 0 ) + { + value = m_engine->RetClippingDistance(); + pv->SetValue(value); + } + + pv = (CEditValue*)pw->SearchControl(EVENT_INTERFACE_DETAIL); + if ( pv != 0 ) + { + value = m_engine->RetObjectDetail(); + pv->SetValue(value); + } + + pv = (CEditValue*)pw->SearchControl(EVENT_INTERFACE_GADGET); + if ( pv != 0 ) + { + value = m_engine->RetGadgetQuantity(); + pv->SetValue(value); + } + + pv = (CEditValue*)pw->SearchControl(EVENT_INTERFACE_TEXTURE); + if ( pv != 0 ) + { + value = (float)m_engine->RetTextureQuality(); + pv->SetValue(value); + } + + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_VOLSOUND); + if ( ps != 0 ) + { + value = (float)m_sound->RetAudioVolume(); + ps->SetVisibleValue(value); + } + + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_VOLMUSIC); + if ( ps != 0 ) + { + value = (float)m_sound->RetMidiVolume(); + ps->SetVisibleValue(value); + } + + pc = (CCheck*)pw->SearchControl(EVENT_INTERFACE_SOUND3D); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_sound->RetSound3D()); + pc->SetState(STATE_ENABLE, m_sound->RetSound3DCap()); + } +} + +// Met à jour le moteur en fonction des boutons après la phase de setup. + +void CMainDialog::ChangeSetupButtons() +{ + CWindow* pw; + CEditValue* pv; + CSlider* ps; + float value; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + + pv = (CEditValue*)pw->SearchControl(EVENT_INTERFACE_PARTI); + if ( pv != 0 ) + { + value = pv->RetValue(); + m_engine->SetParticuleDensity(value); + } + + pv = (CEditValue*)pw->SearchControl(EVENT_INTERFACE_CLIP); + if ( pv != 0 ) + { + value = pv->RetValue(); + m_engine->SetClippingDistance(value); + } + + pv = (CEditValue*)pw->SearchControl(EVENT_INTERFACE_DETAIL); + if ( pv != 0 ) + { + value = pv->RetValue(); + m_engine->SetObjectDetail(value); + } + + pv = (CEditValue*)pw->SearchControl(EVENT_INTERFACE_GADGET); + if ( pv != 0 ) + { + value = pv->RetValue(); + m_engine->SetGadgetQuantity(value); + } + + pv = (CEditValue*)pw->SearchControl(EVENT_INTERFACE_TEXTURE); + if ( pv != 0 ) + { + value = pv->RetValue(); + m_engine->SetTextureQuality((int)value); + } + + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_VOLSOUND); + if ( ps != 0 ) + { + value = ps->RetVisibleValue(); + m_sound->SetAudioVolume((int)value); + } + + ps = (CSlider*)pw->SearchControl(EVENT_INTERFACE_VOLMUSIC); + if ( ps != 0 ) + { + value = ps->RetVisibleValue(); + m_sound->SetMidiVolume((int)value); + } +} + + +// Mémorise tous les réglages. + +void CMainDialog::SetupMemorize() +{ + float fValue; + int iValue, i, j; + char key[500]; + char num[10]; + + SetProfileString("Directory", "scene", m_sceneDir); + SetProfileString("Directory", "savegame", m_savegameDir); + SetProfileString("Directory", "public", m_publicDir); + SetProfileString("Directory", "user", m_userDir); + SetProfileString("Directory", "files", m_filesDir); + + iValue = m_engine->RetTotoMode(); + SetProfileInt("Setup", "TotoMode", iValue); + + iValue = m_bTooltip; + SetProfileInt("Setup", "Tooltips", iValue); + + iValue = m_bGlint; + SetProfileInt("Setup", "InterfaceGlint", iValue); + + iValue = m_bRain; + SetProfileInt("Setup", "InterfaceGlint", iValue); + + iValue = m_engine->RetNiceMouse(); + SetProfileInt("Setup", "NiceMouse", iValue); + + iValue = m_bSoluce4; + SetProfileInt("Setup", "Soluce4", iValue); + + iValue = m_bMovies; + SetProfileInt("Setup", "Movies", iValue); + + iValue = m_bNiceReset; + SetProfileInt("Setup", "NiceReset", iValue); + + iValue = m_bHimselfDamage; + SetProfileInt("Setup", "HimselfDamage", iValue); + + iValue = m_bCameraScroll; + SetProfileInt("Setup", "CameraScroll", iValue); + + iValue = m_bCameraInvertX; + SetProfileInt("Setup", "CameraInvertX", iValue); + + iValue = m_bEffect; + SetProfileInt("Setup", "InterfaceEffect", iValue); + + iValue = m_engine->RetShadow(); + SetProfileInt("Setup", "GroundShadow", iValue); + + iValue = m_engine->RetGroundSpot(); + SetProfileInt("Setup", "GroundSpot", iValue); + + iValue = m_engine->RetDirty(); + SetProfileInt("Setup", "ObjectDirty", iValue); + + iValue = m_engine->RetFog(); + SetProfileInt("Setup", "FogMode", iValue); + + iValue = m_engine->RetLensMode(); + SetProfileInt("Setup", "LensMode", iValue); + + iValue = m_engine->RetSkyMode(); + SetProfileInt("Setup", "SkyMode", iValue); + + iValue = m_engine->RetPlanetMode(); + SetProfileInt("Setup", "PlanetMode", iValue); + + iValue = m_engine->RetLightMode(); + SetProfileInt("Setup", "LightMode", iValue); + + iValue = m_engine->RetJoystick(); + SetProfileInt("Setup", "UseJoystick", iValue); + + fValue = m_engine->RetParticuleDensity(); + SetProfileFloat("Setup", "ParticuleDensity", fValue); + + fValue = m_engine->RetClippingDistance(); + SetProfileFloat("Setup", "ClippingDistance", fValue); + + fValue = m_engine->RetObjectDetail(); + SetProfileFloat("Setup", "ObjectDetail", fValue); + + fValue = m_engine->RetGadgetQuantity(); + SetProfileFloat("Setup", "GadgetQuantity", fValue); + + iValue = m_engine->RetTextureQuality(); + SetProfileInt("Setup", "TextureQuality", iValue); + + iValue = m_sound->RetAudioVolume(); + SetProfileInt("Setup", "AudioVolume", iValue); + + iValue = m_sound->RetMidiVolume(); + SetProfileInt("Setup", "MidiVolume", iValue); + + iValue = m_sound->RetSound3D(); + SetProfileInt("Setup", "Sound3D", iValue); + + iValue = m_engine->RetEditIndentMode(); + SetProfileInt("Setup", "EditIndentMode", iValue); + + iValue = m_engine->RetEditIndentValue(); + SetProfileInt("Setup", "EditIndentValue", iValue); + + key[0] = 0; + for ( i=0 ; i<100 ; i++ ) + { + if ( m_engine->RetKey(i, 0) == 0 ) break; + + for ( j=0 ; j<2 ; j++ ) + { + iValue = m_engine->RetKey(i, j); + sprintf(num, "%d%c", iValue, j==0?'+':' '); + strcat(key, num); + } + } + SetProfileString("Setup", "KeyMap", key); + +#if _NET + if ( m_accessEnable ) + { + iValue = m_accessMission; + SetProfileInt("Setup", "AccessMission", iValue); + + iValue = m_accessUser; + SetProfileInt("Setup", "AccessUser", iValue); + } +#endif + + iValue = m_bDeleteGamer; + SetProfileInt("Setup", "DeleteGamer", iValue); + + m_engine->WriteProfile(); +} + +// Rappelle tous les réglages. + +void CMainDialog::SetupRecall() +{ + float fValue; + int iValue, i, j; + char key[500]; + char* p; + + if ( GetProfileString("Directory", "scene", key, _MAX_FNAME) ) + { + strcpy(m_sceneDir, key); + } + + if ( GetProfileString("Directory", "savegame", key, _MAX_FNAME) ) + { + strcpy(m_savegameDir, key); + } + + if ( GetProfileString("Directory", "public", key, _MAX_FNAME) ) + { + strcpy(m_publicDir, key); + } + + if ( GetProfileString("Directory", "user", key, _MAX_FNAME) ) + { + strcpy(m_userDir, key); + } + + if ( GetProfileString("Directory", "files", key, _MAX_FNAME) ) + { + strcpy(m_filesDir, key); + } + + + if ( GetProfileInt("Setup", "TotoMode", iValue) ) + { + m_engine->SetTotoMode(iValue); + } + + if ( GetProfileInt("Setup", "Tooltips", iValue) ) + { + m_bTooltip = iValue; + } + + if ( GetProfileInt("Setup", "InterfaceGlint", iValue) ) + { + m_bGlint = iValue; + } + + if ( GetProfileInt("Setup", "InterfaceGlint", iValue) ) + { + m_bRain = iValue; + } + + if ( GetProfileInt("Setup", "NiceMouse", iValue) ) + { + m_engine->SetNiceMouse(iValue); + } + + if ( GetProfileInt("Setup", "Soluce4", iValue) ) + { + m_bSoluce4 = iValue; + } + + if ( GetProfileInt("Setup", "Movies", iValue) ) + { + m_bMovies = iValue; + } + + if ( GetProfileInt("Setup", "NiceReset", iValue) ) + { + m_bNiceReset = iValue; + } + + if ( GetProfileInt("Setup", "HimselfDamage", iValue) ) + { + m_bHimselfDamage = iValue; + } + + if ( GetProfileInt("Setup", "CameraScroll", iValue) ) + { + m_bCameraScroll = iValue; + m_camera->SetCameraScroll(m_bCameraScroll); + } + + if ( GetProfileInt("Setup", "CameraInvertX", iValue) ) + { + m_bCameraInvertX = iValue; + m_camera->SetCameraInvertX(m_bCameraInvertX); + } + + if ( GetProfileInt("Setup", "CameraInvertY", iValue) ) + { + m_bCameraInvertY = iValue; + m_camera->SetCameraInvertY(m_bCameraInvertY); + } + + if ( GetProfileInt("Setup", "InterfaceEffect", iValue) ) + { + m_bEffect = iValue; + } + + if ( GetProfileInt("Setup", "GroundShadow", iValue) ) + { + m_engine->SetShadow(iValue); + } + + if ( GetProfileInt("Setup", "GroundSpot", iValue) ) + { + m_engine->SetGroundSpot(iValue); + } + + if ( GetProfileInt("Setup", "ObjectDirty", iValue) ) + { + m_engine->SetDirty(iValue); + } + + if ( GetProfileInt("Setup", "FogMode", iValue) ) + { + m_engine->SetFog(iValue); + m_camera->SetOverBaseColor(RetColor(RetColor(0.0f))); + } + + if ( GetProfileInt("Setup", "LensMode", iValue) ) + { + m_engine->SetLensMode(iValue); + } + + if ( GetProfileInt("Setup", "SkyMode", iValue) ) + { + m_engine->SetSkyMode(iValue); + } + + if ( GetProfileInt("Setup", "PlanetMode", iValue) ) + { + m_engine->SetPlanetMode(iValue); + } + + if ( GetProfileInt("Setup", "LightMode", iValue) ) + { + m_engine->SetLightMode(iValue); + } + + if ( GetProfileInt("Setup", "UseJoystick", iValue) ) + { + m_engine->SetJoystick(iValue); + } + + if ( GetProfileFloat("Setup", "ParticuleDensity", fValue) ) + { + m_engine->SetParticuleDensity(fValue); + } + + if ( GetProfileFloat("Setup", "ClippingDistance", fValue) ) + { + m_engine->SetClippingDistance(fValue); + } + + if ( GetProfileFloat("Setup", "ObjectDetail", fValue) ) + { + m_engine->SetObjectDetail(fValue); + } + + if ( GetProfileFloat("Setup", "GadgetQuantity", fValue) ) + { + m_engine->SetGadgetQuantity(fValue); + } + + if ( GetProfileInt("Setup", "TextureQuality", iValue) ) + { + m_engine->SetTextureQuality(iValue); + } + + if ( GetProfileInt("Setup", "AudioVolume", iValue) ) + { + m_sound->SetAudioVolume(iValue); + } + + if ( GetProfileInt("Setup", "MidiVolume", iValue) ) + { + m_sound->SetMidiVolume(iValue); + } + + if ( GetProfileInt("Setup", "EditIndentMode", iValue) ) + { + m_engine->SetEditIndentMode(iValue); + } + + if ( GetProfileInt("Setup", "EditIndentValue", iValue) ) + { + m_engine->SetEditIndentValue(iValue); + } + + if ( GetProfileString("Setup", "KeyMap", key, 500) ) + { + p = key; + for ( i=0 ; i<100 ; i++ ) + { + if ( p[0] == 0 ) break; + + for ( j=0 ; j<2 ; j++ ) + { + sscanf(p, "%d", &iValue); + m_engine->SetKey(i, j, iValue); + while ( *p >= '0' && *p <= '9' ) p++; + while ( *p == ' ' || *p == '+' ) p++; + } + } + } + +#if _NET + if ( m_accessEnable ) + { + if ( GetProfileInt("Setup", "AccessMission", iValue) ) + { + m_accessMission = iValue; + } + + if ( GetProfileInt("Setup", "AccessUser", iValue) ) + { + m_accessUser = iValue; + } + } +#endif + + if ( GetProfileInt("Setup", "DeleteGamer", iValue) ) + { + m_bDeleteGamer = iValue; + } +} + + +// Change le niveau général de qualité. + +void CMainDialog::ChangeSetupQuality(int quality) +{ + BOOL bEnable; + float value; + int iValue; + + bEnable = (quality >= 0); + m_engine->SetShadow(bEnable); + m_engine->SetGroundSpot(bEnable); + m_engine->SetDirty(bEnable); + m_engine->SetFog(bEnable); + m_engine->SetLensMode(bEnable); + m_engine->SetSkyMode(bEnable); + m_engine->SetPlanetMode(bEnable); + m_engine->SetLightMode(bEnable); + m_camera->SetOverBaseColor(RetColor(RetColor(0.0f))); + + if ( quality < 0 ) value = 0.0f; + if ( quality == 0 ) value = 1.0f; + if ( quality > 0 ) value = 2.0f; + m_engine->SetParticuleDensity(value); + + if ( quality < 0 ) value = 0.5f; + if ( quality == 0 ) value = 1.0f; + if ( quality > 0 ) value = 2.0f; + m_engine->SetClippingDistance(value); + + if ( quality < 0 ) value = 0.0f; + if ( quality == 0 ) value = 1.0f; + if ( quality > 0 ) value = 2.0f; + m_engine->SetObjectDetail(value); + + if ( quality < 0 ) value = 0.5f; + if ( quality == 0 ) value = 1.0f; + if ( quality > 0 ) value = 1.0f; + m_engine->SetGadgetQuantity(value); + + if ( quality < 0 ) iValue = 0; + if ( quality == 0 ) iValue = 1; + if ( quality > 0 ) iValue = 2; + m_engine->SetTextureQuality(iValue); + + m_engine->FirstExecuteAdapt(FALSE); +} + + +// Touches redéfinissables : + +static int key_table[KEY_TOTAL] = +{ +#if _SCHOOL & _TEEN + KEYRANK_LEFT, + KEYRANK_RIGHT, + KEYRANK_UP, + KEYRANK_DOWN, + KEYRANK_CAMERA, + KEYRANK_NEAR, + KEYRANK_AWAY, + KEYRANK_HELP, + KEYRANK_PROG, + KEYRANK_SPEED10, + KEYRANK_SPEED15, + KEYRANK_SPEED20, + KEYRANK_QUIT, +#else + KEYRANK_LEFT, + KEYRANK_RIGHT, + KEYRANK_UP, + KEYRANK_DOWN, + KEYRANK_GUP, + KEYRANK_GDOWN, + KEYRANK_ACTION, + KEYRANK_CAMERA, + KEYRANK_VISIT, + KEYRANK_NEXT, + KEYRANK_HUMAN, + KEYRANK_DESEL, + KEYRANK_NEAR, + KEYRANK_AWAY, + KEYRANK_HELP, + KEYRANK_PROG, + KEYRANK_CBOT, + KEYRANK_SPEED10, + KEYRANK_SPEED15, + KEYRANK_SPEED20, + KEYRANK_QUIT, +#endif +}; + +static EventMsg key_event[KEY_TOTAL] = +{ +#if _SCHOOL & _TEEN + EVENT_INTERFACE_KLEFT, + EVENT_INTERFACE_KRIGHT, + EVENT_INTERFACE_KUP, + EVENT_INTERFACE_KDOWN, + EVENT_INTERFACE_KCAMERA, + EVENT_INTERFACE_KNEAR, + EVENT_INTERFACE_KAWAY, + EVENT_INTERFACE_KHELP, + EVENT_INTERFACE_KPROG, + EVENT_INTERFACE_KSPEED10, + EVENT_INTERFACE_KSPEED15, + EVENT_INTERFACE_KSPEED20, + EVENT_INTERFACE_KQUIT, +#else + EVENT_INTERFACE_KLEFT, + EVENT_INTERFACE_KRIGHT, + EVENT_INTERFACE_KUP, + EVENT_INTERFACE_KDOWN, + EVENT_INTERFACE_KGUP, + EVENT_INTERFACE_KGDOWN, + EVENT_INTERFACE_KACTION, + EVENT_INTERFACE_KCAMERA, + EVENT_INTERFACE_KVISIT, + EVENT_INTERFACE_KNEXT, + EVENT_INTERFACE_KHUMAN, + EVENT_INTERFACE_KDESEL, + EVENT_INTERFACE_KNEAR, + EVENT_INTERFACE_KAWAY, + EVENT_INTERFACE_KHELP, + EVENT_INTERFACE_KPROG, + EVENT_INTERFACE_KCBOT, + EVENT_INTERFACE_KSPEED10, + EVENT_INTERFACE_KSPEED15, + EVENT_INTERFACE_KSPEED20, + EVENT_INTERFACE_KQUIT, +#endif +}; + +// Met à jour la liste des touches. + +void CMainDialog::UpdateKey() +{ + CWindow* pw; + CScroll* ps; + CKey* pk; + FPOINT pos, dim; + int first, i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + + ps = (CScroll*)pw->SearchControl(EVENT_INTERFACE_KSCROLL); + if ( ps == 0 ) return; + + first = (int)(ps->RetVisibleValue()*(KEY_TOTAL-KEY_VISIBLE)); + + for ( i=0 ; iDeleteControl(key_event[i]); + } + + dim.x = 400.0f/640.0f; + dim.y = 20.0f/480.0f; + pos.x = 110.0f/640.0f; + pos.y = 168.0f/480.0f + dim.y*(KEY_VISIBLE-1); + for ( i=0 ; iCreateKey(pos, dim, -1, key_event[first+i]); + pk = (CKey*)pw->SearchControl(key_event[first+i]); + if ( pk == 0 ) break; + pk->SetKey(0, m_engine->RetKey(key_table[first+i], 0)); + pk->SetKey(1, m_engine->RetKey(key_table[first+i], 1)); + pos.y -= dim.y; + } +} + +// Change une touche. + +void CMainDialog::ChangeKey(EventMsg event) +{ + CWindow* pw; + CScroll* ps; + CKey* pk; + int i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw == 0 ) return; + + ps = (CScroll*)pw->SearchControl(EVENT_INTERFACE_KSCROLL); + if ( ps == 0 ) return; + + for ( i=0 ; iSearchControl(key_event[i]); + if ( pk == 0 ) break; + m_engine->SetKey(key_table[i], 0, pk->RetKey(0)); + m_engine->SetKey(key_table[i], 1, pk->RetKey(1)); + } + } +} + + + +// Voulez-vous quitter la mission en cours ? + +void CMainDialog::StartAbort() +{ + CWindow* pw; + CButton* pb; + FPOINT pos, dim; + char name[100]; + + StartDialog(FPOINT(0.3f, 0.8f), TRUE, FALSE, FALSE); + m_bDialogDelete = FALSE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return; + + pos.x = 0.35f; + pos.y = 0.60f; + dim.x = 0.30f; + dim.y = 0.30f; + pw->CreateGroup(pos, dim, 5, EVENT_INTERFACE_GLINTl); // coin orange + pos.x = 0.35f; + pos.y = 0.10f; + dim.x = 0.30f; + dim.y = 0.30f; + pw->CreateGroup(pos, dim, 4, EVENT_INTERFACE_GLINTr); // coin bleu + + pos.x = 0.40f; + dim.x = 0.20f; +#if _POLISH + pos.x -= 7.0f/640.0f; + dim.x += 14.0f/640.0f; +#endif + dim.y = 32.0f/480.0f; + + pos.y = 0.74f; + pb = pw->CreateButton(pos, dim, -1, EVENT_DIALOG_CANCEL); + pb->SetState(STATE_SHADOW); + GetResource(RES_TEXT, RT_DIALOG_NO, name); + pb->SetName(name); + + if ( m_index == 2 || // missions ? + m_index == 3 || // jeux libres ? + m_index == 4 ) // user ? + { + pos.y = 0.62f; + pb = pw->CreateButton(pos, dim, -1, EVENT_INTERFACE_WRITE); + pb->SetState(STATE_SHADOW); + if ( m_main->IsBusy() ) // tâche en cours ? + { + pb->ClearState(STATE_ENABLE); + } + + pos.y = 0.53f; + pb = pw->CreateButton(pos, dim, -1, EVENT_INTERFACE_READ); + pb->SetState(STATE_SHADOW); + if ( !IsIOReadScene() ) // aucun fichier à lire ? + { + pb->ClearState(STATE_ENABLE); + } + pb->SetState(STATE_WARNING); + } + + if ( m_engine->RetSetupMode() ) + { + pos.y = 0.39f; + pb = pw->CreateButton(pos, dim, -1, EVENT_INTERFACE_SETUP); + pb->SetState(STATE_SHADOW); + } + + pos.y = 0.25f; + pb = pw->CreateButton(pos, dim, -1, EVENT_INTERFACE_AGAIN); + pb->SetState(STATE_SHADOW); + pb->SetState(STATE_WARNING); + + pos.y = 0.16f; + pb = pw->CreateButton(pos, dim, -1, EVENT_DIALOG_OK); + pb->SetState(STATE_SHADOW); + pb->SetState(STATE_WARNING); + GetResource(RES_TEXT, RT_DIALOG_YES, name); + pb->SetName(name); +} + +// Voulez-vous détruire le bâtiment ? + +void CMainDialog::StartDeleteObject() +{ + CWindow* pw; + CButton* pb; + FPOINT pos, dim; + char name[100]; + + StartDialog(FPOINT(0.7f, 0.3f), FALSE, TRUE, TRUE); + m_bDialogDelete = TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return; + + pos.x = 0.00f; + pos.y = 0.50f; + dim.x = 1.00f; + dim.y = 0.05f; + GetResource(RES_TEXT, RT_DIALOG_DELOBJ, name); + pw->CreateLabel(pos, dim, -1, EVENT_DIALOG_LABEL, name); + + pb = (CButton*)pw->SearchControl(EVENT_DIALOG_OK); + if ( pb == 0 ) return; + GetResource(RES_TEXT, RT_DIALOG_YESDEL, name); + pb->SetName(name); + pb->SetState(STATE_WARNING); + + pb = (CButton*)pw->SearchControl(EVENT_DIALOG_CANCEL); + if ( pb == 0 ) return; + GetResource(RES_TEXT, RT_DIALOG_NODEL, name); + pb->SetName(name); +} + +// Voulez-vous détruire le joueur ? + +void CMainDialog::StartDeleteGame(char *gamer) +{ + CWindow* pw; + CButton* pb; + FPOINT pos, dim; + char name[100]; + char text[100]; + + StartDialog(FPOINT(0.7f, 0.3f), FALSE, TRUE, TRUE); + m_bDialogDelete = TRUE; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return; + + pos.x = 0.00f; + pos.y = 0.50f; + dim.x = 1.00f; + dim.y = 0.05f; + GetResource(RES_TEXT, RT_DIALOG_DELGAME, name); + sprintf(text, name, gamer); + pw->CreateLabel(pos, dim, -1, EVENT_DIALOG_LABEL, text); + + pb = (CButton*)pw->SearchControl(EVENT_DIALOG_OK); + if ( pb == 0 ) return; + GetResource(RES_TEXT, RT_DIALOG_YESDEL, name); + pb->SetName(name); + pb->SetState(STATE_WARNING); + + pb = (CButton*)pw->SearchControl(EVENT_DIALOG_CANCEL); + if ( pb == 0 ) return; + GetResource(RES_TEXT, RT_DIALOG_NODEL, name); + pb->SetName(name); +} + +// Voulez-vous quitter le jeu ? + +void CMainDialog::StartQuit() +{ + CWindow* pw; + CButton* pb; + FPOINT pos, dim; + char name[100]; + + StartDialog(FPOINT(0.6f, 0.3f), FALSE, TRUE, TRUE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return; + + pos.x = 0.00f; + pos.y = 0.50f; + dim.x = 1.00f; + dim.y = 0.05f; + GetResource(RES_TEXT, RT_DIALOG_QUIT, name); + pw->CreateLabel(pos, dim, -1, EVENT_DIALOG_LABEL, name); + + pb = (CButton*)pw->SearchControl(EVENT_DIALOG_OK); + if ( pb == 0 ) return; + GetResource(RES_TEXT, RT_DIALOG_YESQUIT, name); + pb->SetName(name); + pb->SetState(STATE_WARNING); + + pb = (CButton*)pw->SearchControl(EVENT_DIALOG_CANCEL); + if ( pb == 0 ) return; + GetResource(RES_TEXT, RT_DIALOG_NOQUIT, name); + pb->SetName(name); +} + +// Début de l'affichage d'un dialogue. + +void CMainDialog::StartDialog(FPOINT dim, BOOL bFire, BOOL bOK, BOOL bCancel) +{ + CWindow* pw; + CButton* pb; + FPOINT pos, ddim; + char name[100]; + + StartSuspend(); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW2); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW6); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW7); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW8); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pb = (CButton*)m_interface->SearchControl(EVENT_BUTTON_QUIT); + if ( pb != 0 ) + { + pb->ClearState(STATE_VISIBLE); + } + + m_bDialogFire = bFire; + + pos.x = (1.0f-dim.x)/2.0f; + pos.y = (1.0f-dim.y)/2.0f; + pw = m_interface->CreateWindows(pos, dim, bFire?12:8, EVENT_WINDOW9); + pw->SetState(STATE_SHADOW); + GetResource(RES_TEXT, RT_TITLE_BASE, name); + pw->SetName(name); + + m_dialogPos = pos; + m_dialogDim = dim; + m_dialogTime = 0.0f; + m_dialogParti = 999.0f; + + if ( bOK ) + { + pos.x = 0.50f-0.15f-0.02f; + pos.y = 0.50f-dim.y/2.0f+0.03f; + ddim.x = 0.15f; + ddim.y = 0.06f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_DIALOG_OK); + pb->SetState(STATE_SHADOW); + GetResource(RES_EVENT, EVENT_DIALOG_OK, name); + pb->SetName(name); + } + + if ( bCancel ) + { + pos.x = 0.50f+0.02f; + pos.y = 0.50f-dim.y/2.0f+0.03f; + ddim.x = 0.15f; + ddim.y = 0.06f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_DIALOG_CANCEL); + pb->SetState(STATE_SHADOW); + GetResource(RES_EVENT, EVENT_DIALOG_CANCEL, name); + pb->SetName(name); + } + + m_sound->Play(SOUND_TZOING); + m_bDialog = TRUE; +} + +// Animation d'un dialogue. + +void CMainDialog::FrameDialog(float rTime) +{ + CWindow* pw; + D3DVECTOR pos, speed; + FPOINT dim, dpos, ddim; + float zoom; + int i; + + dpos = m_dialogPos; + ddim = m_dialogDim; + + m_dialogTime += rTime; + if ( m_dialogTime < 1.0f ) + { + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw != 0 ) + { + if ( m_dialogTime < 0.50f ) + { + zoom = Bounce(m_dialogTime/0.50f); + } + else + { + zoom = 1.0f; + } + + dpos.x += ddim.x/2.0f; + dpos.y += ddim.y/2.0f; + + ddim.x *= zoom; +//? ddim.y *= zoom; + + dpos.x -= ddim.x/2.0f; + dpos.y -= ddim.y/2.0f; + + pw->SetPos(dpos); + pw->SetDim(ddim); + } + } + + if ( !m_bGlint ) return; + + m_dialogParti += rTime; + if ( m_dialogParti < m_engine->ParticuleAdapt(0.05f) ) return; + m_dialogParti = 0.0f; + + if ( !m_bDialogFire ) return; + + dpos = m_dialogPos; + ddim = m_dialogDim; + + pos.z = 0.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + + for ( i=0 ; i<2 ; i++ ) + { + // En bas. + pos.x = dpos.x + ddim.x*Rand(); + pos.y = dpos.y; + pos.x += (Rand()-0.5f)*(6.0f/640.0f); + pos.y += Rand()*(16.0f/480.0f)-(10.0f/480.0f); + dim.x = 0.01f+Rand()*0.01f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, + (ParticuleType)(PARTILENS1+rand()%3), + 1.0f, 0.0f, 0.0f, SH_INTERFACE); + + // En haut. + pos.x = dpos.x + ddim.x*Rand(); + pos.y = dpos.y + ddim.y; + pos.x += (Rand()-0.5f)*(6.0f/640.0f); + pos.y -= Rand()*(16.0f/480.0f)-(10.0f/480.0f); + dim.x = 0.01f+Rand()*0.01f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, + (ParticuleType)(PARTILENS1+rand()%3), + 1.0f, 0.0f, 0.0f, SH_INTERFACE); + + // A gauche. + pos.y = dpos.y + ddim.y*Rand(); + pos.x = dpos.x; + pos.x += Rand()*(16.0f/640.0f)-(10.0f/640.0f); + pos.y += (Rand()-0.5f)*(6.0f/480.0f); + dim.x = 0.01f+Rand()*0.01f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, + (ParticuleType)(PARTILENS1+rand()%3), + 1.0f, 0.0f, 0.0f, SH_INTERFACE); + + // A droite. + pos.y = dpos.y + ddim.y*Rand(); + pos.x = dpos.x + ddim.x; + pos.x -= Rand()*(16.0f/640.0f)-(10.0f/640.0f); + pos.y += (Rand()-0.5f)*(6.0f/480.0f); + dim.x = 0.01f+Rand()*0.01f; + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, + (ParticuleType)(PARTILENS1+rand()%3), + 1.0f, 0.0f, 0.0f, SH_INTERFACE); + } +} + +// Fin de l'affichage d'un dialogue. + +void CMainDialog::StopDialog() +{ + CWindow* pw; + CButton* pb; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW2); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW6); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW7); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW8); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pb = (CButton*)m_interface->SearchControl(EVENT_BUTTON_QUIT); + if ( pb != 0 ) + { + pb->SetState(STATE_VISIBLE); + } + + StopSuspend(); + m_interface->DeleteControl(EVENT_WINDOW9); + m_bDialog = FALSE; +} + +// Suspend la simulation pour une phase de dialogue. + +void CMainDialog::StartSuspend() +{ + m_sound->MuteAll(TRUE); + m_main->ClearInterface(); + m_bInitPause = m_engine->RetPause(); + m_engine->SetPause(TRUE); + m_engine->SetOverFront(FALSE); // over plane derrière + m_main->CreateShortcuts(); + m_main->StartSuspend(); + m_initCamera = m_camera->RetType(); + m_camera->SetType(CAMERA_DIALOG); +} + +// Reprend la simulation après une phase de dialogue. + +void CMainDialog::StopSuspend() +{ + m_sound->MuteAll(FALSE); + m_main->ClearInterface(); + if ( !m_bInitPause ) m_engine->SetPause(FALSE); + m_engine->SetOverFront(TRUE); // over plane devant + m_main->CreateShortcuts(); + m_main->StopSuspend(); + m_camera->SetType(m_initCamera); +} + + +// Indique s'il faut utiliser les tooltips. + +BOOL CMainDialog::RetTooltip() +{ + return m_bTooltip; +} + +// Indique si un dialogue est affiché. + +BOOL CMainDialog::IsDialog() +{ + return m_bDialog; +} + + + + +// Spécifie le nom de la scène à lire. + +void CMainDialog::SetSceneRead(char* name) +{ + strcpy(m_sceneRead, name); +} + +// Retourne le nom de la scène à lire. + +char* CMainDialog::RetSceneRead() +{ + return m_sceneRead; +} + +// Spécifie le nom de la scène à lire. + +void CMainDialog::SetStackRead(char* name) +{ + strcpy(m_stackRead, name); +} + +// Retourne le nom de la scène à lire. + +char* CMainDialog::RetStackRead() +{ + return m_stackRead; +} + +// Spécifie le nom de la scène choisie pour jouer. + +void CMainDialog::SetSceneName(char* name) +{ + strcpy(m_sceneName, name); +} + +// Retourne le nom de la scène choisie pour jouer. + +char* CMainDialog::RetSceneName() +{ + return m_sceneName; +} + +// Spécifie le rang de la scène choisie pour jouer. + +void CMainDialog::SetSceneRank(int rank) +{ + m_sceneRank = rank; +} + +// Retourne le rang de la scène choisie pour jouer. + +int CMainDialog::RetSceneRank() +{ + return m_sceneRank; +} + +// Retourne nom de dossier de la scène utilisateur choisie pour jouer. + +char* CMainDialog::RetSceneDir() +{ + int i; + + i = (m_sceneRank/100)-1; + + if ( i < 0 || i >= m_userTotal ) return 0; + return m_userList[i]; +} + +// Indique s'il faut montrer la solution. + +BOOL CMainDialog::RetSceneSoluce() +{ + return m_bSceneSoluce; +} + +// Retourne le nom du dossier pour sauvegarder. + +char* CMainDialog::RetSavegameDir() +{ + return m_savegameDir; +} + +// Retourne le nom du dossier public. + +char* CMainDialog::RetPublicDir() +{ + return m_publicDir; +} + + +// Indique s'il y a des reflets sur les boutons. + +BOOL CMainDialog::RetGlint() +{ + return m_bGlint; +} + +// Indique s'il faut montrer les 4:solutions. + +BOOL CMainDialog::RetSoluce4() +{ + return m_bSoluce4; +} + +// Indique s'il faut montrer les cinématiques. + +BOOL CMainDialog::RetMovies() +{ + return m_bMovies; +} + +// Indique s'il faut faire une animation dans CTaskReset. + +BOOL CMainDialog::RetNiceReset() +{ + return m_bNiceReset; +} + +// Indique si les tirs provoquent des dommages à ses propres unités. + +BOOL CMainDialog::RetHimselfDamage() +{ + return m_bHimselfDamage; +} + + + +// Enregistre la représentation personnalisée du joueur. + +void CMainDialog::WriteGamerPerso(char *gamer) +{ + FILE* file; + char filename[100]; + char line[100]; + + sprintf(filename, "%s\\%s\\face.gam", m_savegameDir, gamer); + file = fopen(filename, "w"); + if ( file == NULL ) return; + + sprintf(line, "Head face=%d glasses=%d hair=%.2f;%.2f;%.2f;%.2f\n", + m_perso.face, m_perso.glasses, + m_perso.colorHair.r, m_perso.colorHair.g, m_perso.colorHair.b, m_perso.colorHair.a); + fputs(line, file); + + sprintf(line, "Body combi=%.2f;%.2f;%.2f;%.2f band=%.2f;%.2f;%.2f;%.2f\n", + m_perso.colorCombi.r, m_perso.colorCombi.g, m_perso.colorCombi.b, m_perso.colorCombi.a, + m_perso.colorBand.r, m_perso.colorBand.g, m_perso.colorBand.b, m_perso.colorBand.a); + fputs(line, file); + + fclose(file); +} + +// Lit la représentation personnalisée du joueur. + +void CMainDialog::ReadGamerPerso(char *gamer) +{ + FILE* file; + char filename[100]; + char line[100]; + D3DCOLORVALUE color; + + m_perso.face = 0; + DefPerso(); + + sprintf(filename, "%s\\%s\\face.gam", m_savegameDir, gamer); + file = fopen(filename, "r"); + if ( file == NULL ) return; + + while ( fgets(line, 100, file) != NULL ) + { + if ( Cmd(line, "Head") ) + { + m_perso.face = OpInt(line, "face", 0); + m_perso.glasses = OpInt(line, "glasses", 0); + + color.r = 0.0f; + color.g = 0.0f; + color.b = 0.0f; + color.a = 0.0f; + m_perso.colorHair = OpColorValue(line, "hair", color); + } + + if ( Cmd(line, "Body") ) + { + color.r = 0.0f; + color.g = 0.0f; + color.b = 0.0f; + color.a = 0.0f; + m_perso.colorCombi = OpColorValue(line, "combi", color); + + color.r = 0.0f; + color.g = 0.0f; + color.b = 0.0f; + color.a = 0.0f; + m_perso.colorBand = OpColorValue(line, "band", color); + } + } + + fclose(file); +} + +// Spécifie la représentation du joueur. + +void CMainDialog::SetGamerFace(char *gamer, int face) +{ + m_perso.face = face; + WriteGamerPerso(gamer); +} + +// Donne la représentation du joueur. + +int CMainDialog::RetGamerFace(char *gamer) +{ + ReadGamerPerso(gamer); + return m_perso.face; +} + +// Donne la représentation du joueur. + +int CMainDialog::RetGamerFace() +{ + return m_perso.face; +} + +int CMainDialog::RetGamerGlasses() +{ + return m_perso.glasses; +} + +BOOL CMainDialog::RetGamerOnlyHead() +{ + return (m_phase == PHASE_PERSO && m_persoTab == 0); +} + +float CMainDialog::RetPersoAngle() +{ + return m_persoAngle; +} + +D3DCOLORVALUE CMainDialog::RetGamerColorHair() +{ + return m_perso.colorHair; +} + +D3DCOLORVALUE CMainDialog::RetGamerColorCombi() +{ + return m_perso.colorCombi; +} + +D3DCOLORVALUE CMainDialog::RetGamerColorBand() +{ + return m_perso.colorBand; +} + + +// Lit le fichier du joueur. + +BOOL CMainDialog::ReadGamerInfo() +{ + FILE* file; + char line[100]; + int chap, i, numTry, passed; + + for ( i=0 ; iRetGamerName(), m_sceneName); + file = fopen(line, "r"); + if ( file == NULL ) return FALSE; + + if ( fgets(line, 100, file) != NULL ) + { + sscanf(line, "CurrentChapter=%d CurrentSel=%d\n", &chap, &i); + m_chap[m_index] = chap-1; + m_sel[m_index] = i-1; + } + + while ( fgets(line, 100, file) != NULL ) + { + sscanf(line, "Chapter %d: Scene %d: numTry=%d passed=%d\n", + &chap, &i, &numTry, &passed); + + i += chap*100; + if ( i >= 0 && i < MAXSCENE ) + { + m_sceneInfo[i].numTry = numTry; + m_sceneInfo[i].bPassed = passed; + } + } + + fclose(file); + return TRUE; +} + +// Ecrit le fichier du joueur. + +BOOL CMainDialog::WriteGamerInfo() +{ + FILE* file; + char line[100]; + int i; + + sprintf(line, "%s\\%s\\%s.gam", m_savegameDir, m_main->RetGamerName(), m_sceneName); + file = fopen(line, "w"); + if ( file == NULL ) return FALSE; + + sprintf(line, "CurrentChapter=%d CurrentSel=%d\n", + m_chap[m_index]+1, m_sel[m_index]+1); + fputs(line, file); + + for ( i=0 ; i= MAXSCENE ) return; + if ( numTry > 100 ) numTry = 100; + m_sceneInfo[rank].numTry = numTry; +} + +int CMainDialog::RetGamerInfoTry(int rank) +{ + if ( rank < 0 || rank >= MAXSCENE ) return 0; + return m_sceneInfo[rank].numTry; +} + +void CMainDialog::SetGamerInfoPassed(int rank, BOOL bPassed) +{ + int chap, i; + BOOL bAll; + + if ( rank < 0 || rank >= MAXSCENE ) return; + m_sceneInfo[rank].bPassed = bPassed; + + if ( bPassed ) + { + bAll = TRUE; + chap = rank/100; + for ( i=0 ; i= MAXSCENE ) return FALSE; + return m_sceneInfo[rank].bPassed; +} + + +// Passe à la misison suivante, et éventuellement au chapitre suivant. + +BOOL CMainDialog::NextMission() +{ + m_sel[m_index] ++; // mission suivante + + if ( m_sel[m_index] >= m_maxList ) // dernière mission du chapitre ? + { + m_chap[m_index] ++; // chapitre suivant + m_sel[m_index] = 0; // première mission + } + + return TRUE; +} + + diff --git a/src/maindialog.h b/src/maindialog.h new file mode 100644 index 00000000..24719642 --- /dev/null +++ b/src/maindialog.h @@ -0,0 +1,242 @@ +// maindialog.h + +#ifndef _MAINDIALOG_H_ +#define _MAINDIALOG_H_ + + + +class CInstanceManager; +class CRobotMain; +class CEvent; +class CD3DEngine; +class CInterface; +class CWindow; +class CControl; +class CParticule; +class CCamera; +class CSound; + +enum Phase; +enum CameraType; + + +#define USERLISTMAX 100 +#define MAXSCENE 1000 + +typedef struct +{ + char numTry; + char bPassed; +} +SceneInfo; + +typedef struct +{ + int face; // visage + int glasses; // lunettes + D3DCOLORVALUE colorHair; // couleur cheveux + D3DCOLORVALUE colorCombi; // couleur combinaison + D3DCOLORVALUE colorBand; // couleur bandes +} +GamerPerso; + + + +class CMainDialog +{ +public: + CMainDialog(CInstanceManager* iMan); + ~CMainDialog(); + + BOOL EventProcess(const Event &event); + void ChangePhase(Phase phase); + + void SetSceneRead(char* name); + void SetStackRead(char* name); + void SetSceneName(char* name); + void SetSceneRank(int rank); + char* RetSceneRead(); + char* RetStackRead(); + char* RetSceneName(); + int RetSceneRank(); + char* RetSceneDir(); + BOOL RetSceneSoluce(); + char* RetSavegameDir(); + char* RetPublicDir(); + + BOOL RetTooltip(); + BOOL RetGlint(); + BOOL RetSoluce4(); + BOOL RetMovies(); + BOOL RetNiceReset(); + BOOL RetHimselfDamage(); + + void SetUserDir(char *base, int rank); + void BuildSceneName(char *filename, char *base, int rank); + void BuildResumeName(char *filename, char *base, int rank); + char* RetFilesDir(); + + void StartAbort(); + void StartDeleteObject(); + void StartDeleteGame(char *gamer); + void StartQuit(); + void StartDialog(FPOINT dim, BOOL bFire, BOOL bOK, BOOL bCancel); + void FrameDialog(float rTime); + void StopDialog(); + BOOL IsDialog(); + + void StartSuspend(); + void StopSuspend(); + + void SetupMemorize(); + void SetupRecall(); + + BOOL ReadGamerInfo(); + BOOL WriteGamerInfo(); + void SetGamerInfoTry(int rank, int numTry); + int RetGamerInfoTry(int rank); + void SetGamerInfoPassed(int rank, BOOL bPassed); + BOOL RetGamerInfoPassed(int rank); + BOOL NextMission(); + + void WriteGamerPerso(char *gamer); + void ReadGamerPerso(char *gamer); + void SetGamerFace(char *gamer, int face); + int RetGamerFace(char *gamer); + int RetGamerFace(); + int RetGamerGlasses(); + BOOL RetGamerOnlyHead(); + float RetPersoAngle(); + D3DCOLORVALUE RetGamerColorHair(); + D3DCOLORVALUE RetGamerColorCombi(); + D3DCOLORVALUE RetGamerColorBand(); + + void AllMissionUpdate(); + void ShowSoluceUpdate(); + +protected: + void GlintMove(); + void FrameParticule(float rTime); + void NiceParticule(FPOINT mouse, BOOL bPress); + void ReadNameList(); + void UpdateNameList(); + void UpdateNameEdit(); + void UpdateNameControl(); + void UpdateNameFace(); + void NameSelect(); + void NameCreate(); + void NameDelete(); + void UpdatePerso(); + void CameraPerso(); + void FixPerso(int rank, int index); + void ColorPerso(); + void DefPerso(); + BOOL IsIOReadScene(); + void IOReadName(); + void IOReadList(); + void IOUpdateList(); + void IODeleteScene(); + BOOL IOWriteScene(); + BOOL IOReadScene(); + int RetChapPassed(); + void UpdateSceneChap(int &chap); + void UpdateSceneList(int chap, int &sel); + void UpdateSceneResume(int rank); + void UpdateDisplayDevice(); + void UpdateDisplayMode(); + void ChangeDisplay(); + void UpdateApply(); + void UpdateSetupButtons(); + void ChangeSetupButtons(); + void ChangeSetupQuality(int quality); + void UpdateKey(); + void ChangeKey(EventMsg event); + +protected: + CInstanceManager* m_iMan; + CRobotMain* m_main; + CEvent* m_event; + CD3DEngine* m_engine; + CInterface* m_interface; + CParticule* m_particule; + CCamera* m_camera; + CSound* m_sound; + + Phase m_phase; // copie de CRobotMain + Phase m_phaseSetup; // onglet choisi + Phase m_phaseTerm; // phase trainer/scene/proto + float m_phaseTime; + + GamerPerso m_perso; // perso: description + GamerPerso m_persoCopy; // perso: copie pour annulation + int m_persoTab; // perso: onglet choisi + float m_persoAngle; // perso: angle de présentation + + char m_sceneDir[_MAX_FNAME]; // dossier scene\ + char m_savegameDir[_MAX_FNAME]; // dossier savegame\ + char m_publicDir[_MAX_FNAME]; // dossier program\ + char m_userDir[_MAX_FNAME]; // dossier user\ + char m_filesDir[_MAX_FNAME]; // dossier files\ + + int m_index; // 0..4 + int m_chap[10]; // chapitre choisi (0..8) + int m_sel[10]; // mission choisie (0..98) + int m_maxList; + int m_accessChap; + char m_sceneRead[100]; // nom de la scène à lire + char m_stackRead[100]; // nom de la scène à lire + char m_sceneName[20]; // nom de la scène à jouer + int m_sceneRank; // rang de la scène à jouer + BOOL m_bSceneSoluce; // montre la solution + BOOL m_bSimulSetup; // réglages pendant le jeu + BOOL m_accessEnable; + BOOL m_accessMission; + BOOL m_accessUser; + BOOL m_bDeleteGamer; + + int m_userTotal; + char m_userList[USERLISTMAX][100]; + + int m_shotDelay; // nb de frames avant copie + char m_shotName[100]; // nom du fichier à générer + + int m_setupSelDevice; + int m_setupSelMode; + BOOL m_setupFull; + + BOOL m_bTooltip; // info-bulles à afficher ? + BOOL m_bGlint; // reflets sur boutons ? + BOOL m_bRain; // pluie dans l'interface ? + BOOL m_bSoluce4; // solutions dans programme 4 ? + BOOL m_bMovies; // cinématiques ? + BOOL m_bNiceReset; // pour CTaskReset + BOOL m_bHimselfDamage; // pour les tirs + BOOL m_bCameraScroll; // pour CCamera + BOOL m_bCameraInvertX; // pour CCamera + BOOL m_bCameraInvertY; // pour CCamera + BOOL m_bEffect; // pour CCamera + + FPOINT m_glintMouse; + float m_glintTime; + + int m_loadingCounter; + + BOOL m_bDialog; // dialogue présent ? + BOOL m_bDialogFire; // cadre en feu ? + BOOL m_bDialogDelete; + FPOINT m_dialogPos; + FPOINT m_dialogDim; + float m_dialogParti; + float m_dialogTime; + BOOL m_bInitPause; + CameraType m_initCamera; + + int m_partiPhase[10]; + float m_partiTime[10]; + FPOINT m_partiPos[10]; + + SceneInfo m_sceneInfo[MAXSCENE]; +}; + + +#endif //_MAINDIALOG_H_ diff --git a/src/mainmap.cpp b/src/mainmap.cpp new file mode 100644 index 00000000..eed4c97c --- /dev/null +++ b/src/mainmap.cpp @@ -0,0 +1,388 @@ +// mainmap.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "global.h" +#include "event.h" +#include "iman.h" +#include "interface.h" +#include "map.h" +#include "image.h" +#include "group.h" +#include "slider.h" +#include "scroll.h" +#include "window.h" +#include "mainmap.h" + + + +#define ZOOM_MIN 1.0f +#define ZOOM_MAX 16.0f + + + +// Constructeur de l'application carte. + +CMainMap::CMainMap(CInstanceManager* iMan) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_MAP, this); + + m_interface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + m_event = (CEvent*)m_iMan->SearchInstance(CLASS_EVENT); + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + + m_mapMode = 1; + m_bFixImage = FALSE; +} + +// Destructeur de l'application robot. + +CMainMap::~CMainMap() +{ +} + + +// Crée la mini-carte et les boutons correspondants. + +void CMainMap::CreateMap() +{ + CWindow* pw; + FPOINT pos, dim; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) + { + pos.x = 0.0f; + pos.y = 0.0f; + dim.x = 0.0f; + dim.y = 0.0f; + pw = m_interface->CreateWindows(pos, dim, 10, EVENT_WINDOW1); + } + + dim.x = 10.0f/640.0f; + dim.y = 10.0f/480.0f; + pos.x = 10.0f/640.0f; + pos.y = 10.0f/480.0f; + pw->CreateMap (pos, dim, 2, EVENT_OBJECT_MAP); + pw->CreateSlider(pos, dim, 0, EVENT_OBJECT_MAPZOOM); + + DimMap(); +} + +// Indique si la mini-carte doit afficher une image fixe. + +void CMainMap::SetFixImage(char *filename) +{ + CWindow* pw; + CMap* pm; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return; + + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm == 0 ) return; + + pw->DeleteControl(EVENT_OBJECT_MAPZOOM); + m_bFixImage = TRUE; + + pm->SetFixImage(filename); +} + +// Choix des couleurs du sol et de l'eau pour la mini-carte. + +void CMainMap::FloorColorMap(D3DCOLORVALUE floor, D3DCOLORVALUE water) +{ + CWindow* pw; + CMap* pm; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return; + + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm != 0 ) + { + pm->SetFloorColor(floor); + pm->SetWaterColor(water); + } +} + +// Montre ou cache la mini-carte. + +void CMainMap::ShowMap(BOOL bShow) +{ + CWindow* pw; + CMap* pm; + CSlider* ps; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return; + + if ( bShow ) + { + DimMap(); + } + else + { + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm != 0 ) + { + pm->ClearState(STATE_VISIBLE); + } + + ps = (CSlider*)pw->SearchControl(EVENT_OBJECT_MAPZOOM); + if ( ps != 0 ) + { + ps->ClearState(STATE_VISIBLE); + } + } +} + +// Dimensions de la mini-carte. + +void CMainMap::DimMap() +{ + CWindow* pw; + CMap* pm; + CSlider* ps; + FPOINT pos, dim; + float value; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return; + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm == 0 ) return; + + pm->SetState(STATE_VISIBLE, (m_mapMode != 0)); + + dim.x = 100.0f/640.0f; + dim.y = 100.0f/480.0f; + pos.x = 540.0f/640.0f; + pos.y = 0.0f/480.0f; + pm->SetPos(pos); + pm->SetDim(dim); + + ps = (CSlider*)pw->SearchControl(EVENT_OBJECT_MAPZOOM); + if ( ps != 0 ) + { + ps->SetState(STATE_VISIBLE, (m_mapMode != 0)); + + dim.x = SCROLL_WIDTH; + dim.y = 66.0f/480.0f; + pos.x = 523.0f/640.0f; + pos.y = 3.0f/480.0f; + ps->SetPos(pos); + ps->SetDim(dim); + + value = pm->RetZoom(); + value = (value-ZOOM_MIN)/(ZOOM_MAX-ZOOM_MIN); + value = powf(value, 0.5f); + ps->SetVisibleValue(value); + ps->SetArrowStep(0.2f); + } +} + +// Retourne le zoom actuel de la mini-carte. + +float CMainMap::RetZoomMap() +{ + CWindow* pw; + CMap* pm; + CSlider* ps; + + pw = (CWindow*)pw->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return ZOOM_MIN; + + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm == 0 ) return ZOOM_MIN; + + ps = (CSlider*)pw->SearchControl(EVENT_OBJECT_MAPZOOM); + if ( ps == 0 ) return ZOOM_MIN; + + return pm->RetZoom(); +} + +// Zoom la mini-carte d'un facteur quelconque. + +void CMainMap::ZoomMap(float zoom) +{ + CWindow* pw; + CMap* pm; + CSlider* ps; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return; + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm == 0 ) return; + + ps = (CSlider*)pw->SearchControl(EVENT_OBJECT_MAPZOOM); + if ( ps == 0 ) return; + + if ( zoom < ZOOM_MIN ) zoom = ZOOM_MIN; + if ( zoom > ZOOM_MAX ) zoom = ZOOM_MAX; + pm->SetZoom(zoom); + + DimMap(); +} + +// Zoom la mini-carte selon le slider. + +void CMainMap::ZoomMap() +{ + CWindow* pw; + CMap* pm; + CSlider* ps; + float zoom; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return; + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm == 0 ) return; + + ps = (CSlider*)pw->SearchControl(EVENT_OBJECT_MAPZOOM); + if ( ps == 0 ) return; + + zoom = ps->RetVisibleValue(); + zoom = powf(zoom, 2.0f); + zoom = ZOOM_MIN+zoom*(ZOOM_MAX-ZOOM_MIN); + pm->SetZoom(zoom); + + DimMap(); +} + +// Active ou désactive la carte. + +void CMainMap::MapEnable(BOOL bEnable) +{ + CWindow* pw; + CMap* pm; + CSlider* ps; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return; + + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm != 0 ) + { + pm->SetEnable(bEnable); + } + + ps = (CSlider*)pw->SearchControl(EVENT_OBJECT_MAPZOOM); + if ( ps != 0 ) + { + ps->SetState(STATE_ENABLE, bEnable); + } +} + +// Spécifie le type de l'icône pour l'objet sélectionné. + +void CMainMap::SetToy(BOOL bToy) +{ + CWindow* pw; + CMap* pm; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return; + + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm == 0 ) return; + + pm->SetToy(bToy); +} + +// Spécifie les paramètres lors de l'usage d'une image fixe. + +void CMainMap::SetFixParam(float zoom, float ox, float oy, float angle, + int mode, BOOL bDebug) +{ + CWindow* pw; + CMap* pm; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return; + + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm == 0 ) return; + + pm->SetZoom(zoom); + pm->SetOffset(ox, oy); + pm->SetAngle(angle); + pm->SetMode(mode); + pm->SetDebug(bDebug); +} + +// Met à jour la mini-carte suite à un changement du terrain. + +void CMainMap::UpdateMap() +{ + CWindow* pw; + CMap* pm; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return; + + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm != 0 ) + { + pm->UpdateTerrain(); + } +} + +// Indique si la mini-carte est visible. + +BOOL CMainMap::RetShowMap() +{ + return ( m_mapMode != 0 ); +} + +// Indique si la mini-carte affiche une image fixe. + +BOOL CMainMap::RetFixImage() +{ + return m_bFixImage; +} + + +// Détecte l'objet visé dans la mini-carte. + +CObject* CMainMap::DetectMap(FPOINT pos, BOOL &bInMap) +{ + CWindow* pw; + CMap* pm; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return 0; + + bInMap = FALSE; + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm == 0 ) return 0; + return pm->DetectObject(pos, bInMap); +} + + +// Indique l'objet survolé par la souris. + +void CMainMap::SetHilite(CObject* pObj) +{ + CWindow* pw; + CMap* pm; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) return; + + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm != 0 ) + { + pm->SetHilite(pObj); + } +} + + diff --git a/src/mainmap.h b/src/mainmap.h new file mode 100644 index 00000000..68befa1b --- /dev/null +++ b/src/mainmap.h @@ -0,0 +1,52 @@ +// mainmap.h + +#ifndef _MAINMAP_H_ +#define _MAINMAP_H_ + + +class CInstanceManager; +class CEvent; +class CD3DEngine; +class CInterface; +class CObject; + + + +class CMainMap +{ +public: + CMainMap(CInstanceManager* iMan); + ~CMainMap(); + + void UpdateMap(); + void CreateMap(); + void SetFixImage(char *filename); + void FloorColorMap(D3DCOLORVALUE floor, D3DCOLORVALUE water); + void ShowMap(BOOL bShow); + void DimMap(); + float RetZoomMap(); + void ZoomMap(float zoom); + void ZoomMap(); + void MapEnable(BOOL bEnable); + BOOL RetShowMap(); + BOOL RetFixImage(); + CObject* DetectMap(FPOINT pos, BOOL &bInMap); + void SetHilite(CObject* pObj); + void SetToy(BOOL bToy); + void SetFixParam(float zoom, float ox, float oy, float angle, int mode, BOOL bDebug); + +protected: + void CenterMap(); + +protected: + CInstanceManager* m_iMan; + CEvent* m_event; + CD3DEngine* m_engine; + CInterface* m_interface; + + int m_mapMode; + BOOL m_bFixImage; +}; + + +#endif //_MAINMAP_H_ diff --git a/src/mainmovie.cpp b/src/mainmovie.cpp new file mode 100644 index 00000000..b8d0fb56 --- /dev/null +++ b/src/mainmovie.cpp @@ -0,0 +1,233 @@ +// mainmovie.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "global.h" +#include "event.h" +#include "iman.h" +#include "math3d.h" +#include "camera.h" +#include "object.h" +#include "motion.h" +#include "motionhuman.h" +#include "interface.h" +#include "robotmain.h" +#include "sound.h" +#include "mainmovie.h" + + + + +// Constructeur de l'application carte. + +CMainMovie::CMainMovie(CInstanceManager* iMan) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_SHORT, this); + + m_interface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + m_event = (CEvent*)m_iMan->SearchInstance(CLASS_EVENT); + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + + Flush(); +} + +// Destructeur de l'application robot. + +CMainMovie::~CMainMovie() +{ +} + + +// Stoppe le film en cours. + +void CMainMovie::Flush() +{ + m_type = MM_NONE; +} + + +// Début d'un film. + +BOOL CMainMovie::Start(MainMovieType type, float time) +{ + D3DMATRIX* mat; + D3DVECTOR pos; + CObject* pObj; + CMotion* motion; + + m_type = type; + m_speed = 1.0f/time; + m_progress = 0.0f; + + if ( m_type == MM_SATCOMopen ) + { + pObj = m_main->SearchHuman(); + if ( pObj == 0 ) + { + m_type = MM_NONE; // c'est déjà fini ! + return TRUE; + } + + motion = pObj->RetMotion(); + if ( motion != 0 ) + { + motion->SetAction(MHS_SATCOM, 0.5f); // lit le SatCom + } + + m_camera->RetCamera(m_initialEye, m_initialLookat); + m_camera->SetType(CAMERA_SCRIPT); + m_camera->SetSmooth(CS_HARD); + m_camera->SetScriptEye(m_initialEye); + m_camera->SetScriptLookat(m_initialLookat); + m_camera->FixCamera(); + + mat = pObj->RetWorldMatrix(0); + m_finalLookat[0] = Transform(*mat, D3DVECTOR( 1.6f, 1.0f, 1.2f)); + m_finalEye[0] = Transform(*mat, D3DVECTOR(-1.5f, 5.0f, 3.0f)); + m_finalLookat[1] = Transform(*mat, D3DVECTOR( 1.6f, 1.0f, 1.2f)); + m_finalEye[1] = Transform(*mat, D3DVECTOR( 0.8f, 3.0f, 0.8f)); + } + + if ( m_type == MM_SATCOMclose ) + { + pObj = m_main->SearchHuman(); + if ( pObj != 0 ) + { + motion = pObj->RetMotion(); + if ( motion != 0 ) + { + motion->SetAction(-1); // termine lecture du SatCom + } + } + + m_camera->SetType(CAMERA_BACK); + m_type = MM_NONE; // c'est déjà fini ! + } + + return TRUE; +} + +// Stoppe un film en cours. + +BOOL CMainMovie::Stop() +{ + CObject* pObj; + CMotion* motion; + + if ( m_type == MM_SATCOMopen ) + { + pObj = m_main->SearchHuman(); + if ( pObj != 0 ) + { + motion = pObj->RetMotion(); + if ( motion != 0 ) + { + motion->SetAction(-1); // termine lecture du SatCom + } + } + } + + m_type = MM_NONE; + return TRUE; +} + +// Indique si un film est en cours. + +BOOL CMainMovie::IsExist() +{ + return (m_type != MM_NONE); +} + + +// Traite un événement. + +BOOL CMainMovie::EventProcess(const Event &event) +{ + D3DVECTOR initialEye, initialLookat, finalEye, finalLookat, eye, lookat; + float progress; + + if ( m_type == MM_NONE ) return TRUE; + + m_progress += event.rTime*m_speed; + + if ( m_type == MM_SATCOMopen ) + { + if ( m_progress < 1.0f ) + { + progress = 1.0f-powf(1.0f-m_progress, 3.0f); + + if ( progress < 0.6f ) + { + progress = progress/0.6f; + initialEye = m_initialEye; + initialLookat = m_initialLookat; + finalEye = m_finalEye[0]; + finalLookat = m_finalLookat[0]; + } + else + { + progress = (progress-0.6f)/0.3f; + initialEye = m_finalEye[0]; + initialLookat = m_finalLookat[0]; + finalEye = m_finalEye[1]; + finalLookat = m_finalLookat[1]; + } + if ( progress > 1.0f ) progress = 1.0f; + + eye = (finalEye-initialEye)*progress+initialEye; + lookat = (finalLookat-initialLookat)*progress+initialLookat; + m_camera->SetScriptEye(eye); + m_camera->SetScriptLookat(lookat); +// m_camera->FixCamera(); + } + else + { + m_stopType = m_type; + Flush(); + return FALSE; + } + } + + if ( m_type == MM_SATCOMclose ) + { + if ( m_progress < 1.0f ) + { + } + else + { + m_stopType = m_type; + Flush(); + return FALSE; + } + } + + return TRUE; +} + + +// Retourne le type du film en cours. + +MainMovieType CMainMovie::RetType() +{ + return m_type; +} + +// Retourne le type du film stoppé. + +MainMovieType CMainMovie::RetStopType() +{ + return m_stopType; +} + + diff --git a/src/mainmovie.h b/src/mainmovie.h new file mode 100644 index 00000000..9932e1dd --- /dev/null +++ b/src/mainmovie.h @@ -0,0 +1,64 @@ +// mainmovie.h + +#ifndef _MAINMOVIE_H_ +#define _MAINMOVIE_H_ + + +class CInstanceManager; +class CEvent; +class CD3DEngine; +class CInterface; +class CRobotMain; +class CCamera; +class CSound; +class CObject; + + + + +enum MainMovieType +{ + MM_NONE, + MM_SATCOMopen, + MM_SATCOMclose, +}; + + + +class CMainMovie +{ +public: + CMainMovie(CInstanceManager* iMan); + ~CMainMovie(); + + void Flush(); + BOOL Start(MainMovieType type, float time); + BOOL Stop(); + BOOL IsExist(); + BOOL EventProcess(const Event &event); + MainMovieType RetType(); + MainMovieType RetStopType(); + +protected: + +protected: + CInstanceManager* m_iMan; + CEvent* m_event; + CD3DEngine* m_engine; + CInterface* m_interface; + CRobotMain* m_main; + CCamera* m_camera; + CSound* m_sound; + + MainMovieType m_type; + MainMovieType m_stopType; + float m_speed; + float m_progress; + D3DVECTOR m_initialEye; + D3DVECTOR m_initialLookat; + D3DVECTOR m_finalEye[2]; + D3DVECTOR m_finalLookat[2]; +}; + + +#endif //_MAINMOVIE_H_ diff --git a/src/mainshort.cpp b/src/mainshort.cpp new file mode 100644 index 00000000..15affa36 --- /dev/null +++ b/src/mainshort.cpp @@ -0,0 +1,360 @@ +// mainshort.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "global.h" +#include "event.h" +#include "iman.h" +#include "object.h" +#include "interface.h" +#include "map.h" +#include "button.h" +#include "robotmain.h" +#include "mainshort.h" + + + + +// Constructeur de l'application carte. + +CMainShort::CMainShort(CInstanceManager* iMan) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_SHORT, this); + + m_interface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + m_event = (CEvent*)m_iMan->SearchInstance(CLASS_EVENT); + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + + FlushShortcuts(); +} + +// Destructeur de l'application robot. + +CMainShort::~CMainShort() +{ +} + + + +void CMainShort::SetMode(BOOL bBuilding) +{ + m_bBuilding = bBuilding; +} + + + +// Remise à zéro de tous les raccourcis. + +void CMainShort::FlushShortcuts() +{ + int i; + + for ( i=0 ; i<20 ; i++ ) + { + m_shortcuts[i] = 0; + } +} + +static EventMsg table_sc_em[20] = +{ + EVENT_OBJECT_SHORTCUT00, + EVENT_OBJECT_SHORTCUT01, + EVENT_OBJECT_SHORTCUT02, + EVENT_OBJECT_SHORTCUT03, + EVENT_OBJECT_SHORTCUT04, + EVENT_OBJECT_SHORTCUT05, + EVENT_OBJECT_SHORTCUT06, + EVENT_OBJECT_SHORTCUT07, + EVENT_OBJECT_SHORTCUT08, + EVENT_OBJECT_SHORTCUT09, + EVENT_OBJECT_SHORTCUT10, + EVENT_OBJECT_SHORTCUT11, + EVENT_OBJECT_SHORTCUT12, + EVENT_OBJECT_SHORTCUT13, + EVENT_OBJECT_SHORTCUT14, + EVENT_OBJECT_SHORTCUT15, + EVENT_OBJECT_SHORTCUT16, + EVENT_OBJECT_SHORTCUT17, + EVENT_OBJECT_SHORTCUT18, + EVENT_OBJECT_SHORTCUT19, +}; + +// Crée l'interface des raccourcis aux unités. + +BOOL CMainShort::CreateShortcuts() +{ + CObject* pObj; + CControl* pc; + ObjectType type; + FPOINT pos, dim; + int i, rank, icon; + char name[100]; + + if ( m_main->RetFixScene() ) return FALSE; + + m_interface->DeleteControl(EVENT_OBJECT_MOVIELOCK); + m_interface->DeleteControl(EVENT_OBJECT_EDITLOCK); + for ( i=0 ; i<20 ; i++ ) + { + if ( i != 0 && m_shortcuts[i] == 0 ) continue; + + m_interface->DeleteControl(table_sc_em[i]); + m_shortcuts[i] = 0; + } + + dim.x = 28.0f/640.0f; + dim.y = 28.0f/480.0f; + pos.x = 4.0f/640.0f; + pos.y = (480.0f-32.0f)/480.0f; + + if ( m_main->RetMovieLock() && + !m_main->RetEditLock() ) // bloqué pendant film ? + { + m_interface->CreateShortcut(pos, dim, 7, EVENT_OBJECT_MOVIELOCK); + return TRUE; + } + if ( !m_main->RetFreePhoto() && + (m_main->RetEditLock() || + m_engine->RetPause()) ) // bloqué pendant édition ? + { + m_interface->CreateShortcut(pos, dim, 6, EVENT_OBJECT_EDITLOCK); + return TRUE; + } + + rank = 0; + + m_interface->CreateShortcut(pos, dim, 2, table_sc_em[rank]); + pos.x += dim.x*1.2f; + m_shortcuts[rank] = 0; + rank ++; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; + if ( !pObj->RetSelectable() ) continue; + if ( pObj->RetProxyActivate() ) continue; + + type = pObj->RetType(); + icon = -1; + if ( m_bBuilding ) + { + if ( type == OBJECT_FACTORY ) icon = 32; + if ( type == OBJECT_DERRICK ) icon = 33; + if ( type == OBJECT_CONVERT ) icon = 34; + if ( type == OBJECT_RESEARCH ) icon = 35; + if ( type == OBJECT_STATION ) icon = 36; + if ( type == OBJECT_TOWER ) icon = 37; + if ( type == OBJECT_LABO ) icon = 38; + if ( type == OBJECT_ENERGY ) icon = 39; + if ( type == OBJECT_RADAR ) icon = 40; + if ( type == OBJECT_INFO ) icon = 44; + if ( type == OBJECT_REPAIR ) icon = 41; + if ( type == OBJECT_DESTROYER) icon = 41; + if ( type == OBJECT_NUCLEAR ) icon = 42; + if ( type == OBJECT_PARA ) icon = 46; + if ( type == OBJECT_SAFE ) icon = 47; + if ( type == OBJECT_HUSTON ) icon = 48; + if ( type == OBJECT_BASE ) icon = 43; + } + else + { + if ( type == OBJECT_HUMAN ) icon = 8; + if ( type == OBJECT_MOBILEfa ) icon = 11; + if ( type == OBJECT_MOBILEta ) icon = 10; + if ( type == OBJECT_MOBILEwa ) icon = 9; + if ( type == OBJECT_MOBILEia ) icon = 22; + if ( type == OBJECT_MOBILEfc ) icon = 17; + if ( type == OBJECT_MOBILEtc ) icon = 16; + if ( type == OBJECT_MOBILEwc ) icon = 15; + if ( type == OBJECT_MOBILEic ) icon = 23; + if ( type == OBJECT_MOBILEfi ) icon = 27; + if ( type == OBJECT_MOBILEti ) icon = 26; + if ( type == OBJECT_MOBILEwi ) icon = 25; + if ( type == OBJECT_MOBILEii ) icon = 28; + if ( type == OBJECT_MOBILEfs ) icon = 14; + if ( type == OBJECT_MOBILEts ) icon = 13; + if ( type == OBJECT_MOBILEws ) icon = 12; + if ( type == OBJECT_MOBILEis ) icon = 24; + if ( type == OBJECT_MOBILErt ) icon = 18; + if ( type == OBJECT_MOBILErc ) icon = 19; + if ( type == OBJECT_MOBILErr ) icon = 20; + if ( type == OBJECT_MOBILErs ) icon = 29; + if ( type == OBJECT_MOBILEsa ) icon = 21; + if ( type == OBJECT_MOBILEft ) icon = 30; + if ( type == OBJECT_MOBILEtt ) icon = 30; + if ( type == OBJECT_MOBILEwt ) icon = 30; + if ( type == OBJECT_MOBILEit ) icon = 30; + if ( type == OBJECT_MOBILEdr ) icon = 48; + if ( type == OBJECT_APOLLO2 ) icon = 49; + } + if ( icon == -1 ) continue; + + m_interface->CreateShortcut(pos, dim, icon, table_sc_em[rank]); + pos.x += dim.x; + m_shortcuts[rank] = pObj; + + pc = m_interface->SearchControl(table_sc_em[rank]); + if ( pc != 0 ) + { + pObj->GetTooltipName(name); + pc->SetTooltip(name); + } + rank ++; + + if ( rank >= 20 ) break; + } + + UpdateShortcuts(); + return TRUE; +} + +// Met à jour l'interface des raccourcis aux unités. + +BOOL CMainShort::UpdateShortcuts() +{ + CControl* pc; + int i; + + for ( i=0 ; i<20 ; i++ ) + { + if ( m_shortcuts[i] == 0 ) continue; + + pc = m_interface->SearchControl(table_sc_em[i]); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_shortcuts[i]->RetSelect()); + pc->SetState(STATE_RUN, m_shortcuts[i]->IsProgram()); + } + } + return TRUE; +} + +// Sélectionne un objet à travers un raccourci. + +void CMainShort::SelectShortcut(EventMsg event) +{ + int i; + + for ( i=0 ; i<20 ; i++ ) + { + if ( event == table_sc_em[i] ) + { + if ( i != 0 && m_shortcuts[i] == 0 ) continue; + + if ( i == 0 ) // bâtiments <-> véhicules ? + { + m_bBuilding = !m_bBuilding; + CreateShortcuts(); + } + else + { + m_main->SelectObject(m_shortcuts[i]); + } + return; + } + } +} + + +// Sélectionne l'objet suivant. + +void CMainShort::SelectNext() +{ + CObject* pPrev; + int i; + + if ( m_main->RetMovieLock() || + m_main->RetEditLock() || + m_engine->RetPause() ) return; + + pPrev = m_main->DeselectAll(); + + for ( i=1 ; i<20 ; i++ ) + { + if ( m_shortcuts[i] == pPrev ) + { + if ( m_shortcuts[++i] == 0 ) i = 1; + break; + } + } + + if ( i == 20 || m_shortcuts[i] == 0 ) + { + m_main->SelectHuman(); + } + else + { + m_main->SelectObject(m_shortcuts[i]); + } +} + + +// Détecte l'objet survolé par la souris. + +CObject* CMainShort::DetectShort(FPOINT pos) +{ + CControl* pc; + FPOINT cpos, cdim; + int i; + + for ( i=0 ; i<20 ; i++ ) + { + if ( m_shortcuts[i] == 0 ) continue; + + pc = m_interface->SearchControl(table_sc_em[i]); + if ( pc != 0 ) + { + cpos = pc->RetPos(); + cdim = pc->RetDim(); + + if ( pos.x >= cpos.x && + pos.x <= cpos.x+cdim.x && + pos.y >= cpos.y && + pos.y <= cpos.y+cdim.y ) + { + return m_shortcuts[i]; + } + } + } + return 0; +} + +// Signale l'objet survolé par la souris. + +void CMainShort::SetHilite(CObject* pObj) +{ + CControl* pc; + int i; + + for ( i=0 ; i<20 ; i++ ) + { + if ( m_shortcuts[i] == 0 ) continue; + + pc = m_interface->SearchControl(table_sc_em[i]); + if ( pc == 0 ) continue; + + if ( m_shortcuts[i] == pObj ) + { + pc->SetState(STATE_HILIGHT); + pc->SetState(STATE_FRAME); + } + else + { + pc->ClearState(STATE_HILIGHT); + pc->ClearState(STATE_FRAME); + } + } +} + diff --git a/src/mainshort.h b/src/mainshort.h new file mode 100644 index 00000000..3ad3cd7f --- /dev/null +++ b/src/mainshort.h @@ -0,0 +1,45 @@ +// mainshort.h + +#ifndef _MAINSHORT_H_ +#define _MAINSHORT_H_ + + +class CInstanceManager; +class CEvent; +class CD3DEngine; +class CInterface; +class CRobotMain; +class CObject; + + + +class CMainShort +{ +public: + CMainShort(CInstanceManager* iMan); + ~CMainShort(); + + void SetMode(BOOL bBuilding); + void FlushShortcuts(); + BOOL CreateShortcuts(); + BOOL UpdateShortcuts(); + void SelectShortcut(EventMsg event); + void SelectNext(); + CObject* DetectShort(FPOINT pos); + void SetHilite(CObject* pObj); + +protected: + +protected: + CInstanceManager* m_iMan; + CEvent* m_event; + CD3DEngine* m_engine; + CInterface* m_interface; + CRobotMain* m_main; + + CObject* m_shortcuts[20]; + BOOL m_bBuilding; +}; + + +#endif //_MAINSHORT_H_ diff --git a/src/map.cpp b/src/map.cpp new file mode 100644 index 00000000..bf5fd1c5 --- /dev/null +++ b/src/map.cpp @@ -0,0 +1,1327 @@ +// map.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "event.h" +#include "math3d.h" +#include "terrain.h" +#include "water.h" +#include "object.h" +#include "event.h" +#include "misc.h" +#include "robotmain.h" +#include "iman.h" +#include "map.h" + + + + +// Constructeur de l'objet. + +CMap::CMap(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); + + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + + m_bEnable = TRUE; + m_time = 0.0f; + m_zoom = 2.0f; + m_offset.x = 0.0f; + m_offset.y = 0.0f; + m_angle = 0.0f; + + m_floorColor.r = 1.00f; + m_floorColor.g = 0.50f; + m_floorColor.b = 0.00f; // orange + + m_waterColor.r = 0.00f; + m_waterColor.g = 0.80f; + m_waterColor.b = 1.00f; // bleu + + m_half = m_terrain->RetMosaic()*m_terrain->RetBrick()*m_terrain->RetSize()/2.0f; + + m_hiliteRank = -1; + FlushObject(); + + m_fixImage[0] = 0; + m_mode = 0; + m_bToy = FALSE; + m_bDebug = FALSE; +} + +// Destructeur de l'objet. + +CMap::~CMap() +{ + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CMap::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + return TRUE; +} + + +// Choix de l'offset, lorsqu'une image fixe est affichée. + +void CMap::SetOffset(float ox, float oy) +{ + m_offset.x = ox; + m_offset.y = oy; + m_half = m_terrain->RetMosaic()*m_terrain->RetBrick()*m_terrain->RetSize()/2.0f; +} + +// Choix de l'angle global de rotation. + +void CMap::SetAngle(float angle) +{ + m_angle = angle; +} + +// Spécifie le mode spécial. + +void CMap::SetMode(int mode) +{ + m_mode = mode; +} + +// Spécifie le type de l'icône pour l'objet sélectionné. + +void CMap::SetToy(BOOL bToy) +{ + m_bToy = bToy; +} + +void CMap::SetDebug(BOOL bDebug) +{ + m_bDebug = bDebug; +} + + +// Choix du facteur de zoom de la carte. + +void CMap::SetZoom(float value) +{ + m_zoom = value; + m_half = m_terrain->RetMosaic()*m_terrain->RetBrick()*m_terrain->RetSize()/2.0f; +} + +float CMap::RetZoom() +{ + return m_zoom; +} + +// Choix d'un offset fixe. + +// Active ou désactive la carte. + +void CMap::SetEnable(BOOL bEnable) +{ + m_bEnable = bEnable; + SetState(STATE_DEAD, !bEnable); +} + +BOOL CMap::RetEnable() +{ + return m_bEnable; +} + + +// Choix de la couleur du sol. + +void CMap::SetFloorColor(D3DCOLORVALUE color) +{ + m_floorColor = color; +} + +// Choix de la couleur de l'eau. + +void CMap::SetWaterColor(D3DCOLORVALUE color) +{ + m_waterColor = color; +} + + +// Spécifie une image fixe à la place du dessin du relief. + +void CMap::SetFixImage(char *filename) +{ + strcpy(m_fixImage, filename); +} + +// Indique si on utilise une image fixe. + +BOOL CMap::RetFixImage() +{ + return (m_fixImage[0] != 0); +} + + +// Gestion d'un événement. + +BOOL CMap::EventProcess(const Event &event) +{ + BOOL bInMap; + + if ( (m_state & STATE_VISIBLE) == 0 ) return TRUE; + + CControl::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + m_time += event.rTime; + } + + if ( event.event == EVENT_MOUSEMOVE && Detect(event.pos) ) + { + m_engine->SetMouseType(D3DMOUSENORM); + if ( DetectObject(event.pos, bInMap) != 0 ) + { + m_engine->SetMouseType(D3DMOUSEHAND); + } + } + + if ( event.event == EVENT_LBUTTONDOWN ) + { + if ( CControl::Detect(event.pos) ) + { + SelectObject(event.pos); + return FALSE; + } + } + + return TRUE; +} + +// Ajuste l'offset pour ne pas dépasser la carte. + +FPOINT CMap::AdjustOffset(FPOINT offset) +{ + float limit; + + limit = m_half - m_half/m_zoom; + if ( offset.x < -limit ) offset.x = -limit; + if ( offset.x > limit ) offset.x = limit; + if ( offset.y < -limit ) offset.y = -limit; + if ( offset.y > limit ) offset.y = limit; + + return offset; +} + +// Indique l'objet survolé par la souris. + +void CMap::SetHilite(CObject* pObj) +{ + int i; + + m_hiliteRank = -1; + if ( m_bToy || m_fixImage[0] != 0 ) return; // carte avec image fixe ? + if ( pObj == 0 ) return; + + for ( i=0 ; i m_pos.x+m_dim.x || + pos.y > m_pos.y+m_dim.y ) return 0; + + bInMap = TRUE; + + pos.x = (pos.x-m_pos.x)/m_dim.x*256.0f; + pos.y = (pos.y-m_pos.y)/m_dim.y*256.0f; // 0..256 + pos.x = (pos.x-128.0f)*m_half/(m_zoom*128.0f)+m_offset.x; + pos.y = (pos.y-128.0f)*m_half/(m_zoom*128.0f)+m_offset.y; + + min = 10000.0f; + best = -1; + for ( i=MAPMAXOBJECT-1 ; i>=0 ; i-- ) + { + if ( !m_map[i].bUsed ) continue; + if ( m_map[i].color == MAPCOLOR_BBOX && !m_bRadar ) continue; + if ( m_map[i].color == MAPCOLOR_ALIEN && !m_bRadar ) continue; + + dist = Length(m_map[i].pos.x-pos.x, m_map[i].pos.y-pos.y); + if ( dist > m_half/m_zoom*8.0f/100.0f ) continue; // trop loin ? + if ( dist < min ) + { + min = dist; + best = i; + } + } + if ( best == -1 ) return 0; + return m_map[best].object; +} + +// Sélectionne un objet. + +void CMap::SelectObject(FPOINT pos) +{ + CObject *pObj; + BOOL bInMap; + + pObj = DetectObject(pos, bInMap); + if ( pObj != 0 ) + { + m_main->SelectObject(pObj); + } +} + + +// Dessine la carte. + +void CMap::Draw() +{ + FPOINT uv1, uv2; + int i; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + CControl::Draw(); // dessine le fond (bouton) + + if ( !m_bEnable ) return; + + if ( m_fixImage[0] == 0 && m_map[MAPMAXOBJECT-1].bUsed ) + { + m_offset = AdjustOffset(m_map[MAPMAXOBJECT-1].pos); + } + + if ( m_fixImage[0] == 0 ) // dessin du relief ? + { + m_engine->SetTexture("map.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 0.5f+(m_offset.x-(m_half/m_zoom))/(m_half*2.0f); + uv1.y = 0.5f-(m_offset.y+(m_half/m_zoom))/(m_half*2.0f); + uv2.x = 0.5f+(m_offset.x+(m_half/m_zoom))/(m_half*2.0f); + uv2.y = 0.5f-(m_offset.y-(m_half/m_zoom))/(m_half*2.0f); + DrawVertex(uv1, uv2, 0.97f); // dessine la carte + } + else // image fixe ? + { + m_engine->LoadTexture(m_fixImage); + m_engine->SetTexture(m_fixImage); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 0.0f; + uv1.y = 0.0f; + uv2.x = 1.0f; + uv2.y = 1.0f; + DrawVertex(uv1, uv2, 0.97f); // dessine la carte + } + + i = MAPMAXOBJECT-1; + if ( m_map[i].bUsed ) // sélection : + { + DrawFocus(m_map[i].pos, m_map[i].dir, m_map[i].type, m_map[i].color); + } + + for ( i=0 ; im_totalMove ; i-- ) // objets mobiles : + { + if ( i == m_hiliteRank ) continue; + DrawObject(m_map[i].pos, m_map[i].dir, m_map[i].type, m_map[i].color, FALSE, FALSE); + } + + i = MAPMAXOBJECT-1; + if ( m_map[i].bUsed && i != m_hiliteRank ) // sélection : + { + DrawObject(m_map[i].pos, m_map[i].dir, m_map[i].type, m_map[i].color, TRUE, FALSE); + } + + if ( m_hiliteRank != -1 && m_map[m_hiliteRank].bUsed ) + { + i = m_hiliteRank; + DrawObject(m_map[i].pos, m_map[i].dir, m_map[i].type, m_map[i].color, FALSE, TRUE); + DrawHilite(m_map[i].pos); + } +} + +// Calcul un point pour DrawFocus. + +FPOINT CMap::MapInter(FPOINT pos, float dir) +{ + FPOINT p1; + float limit; + + p1.x = pos.x+1.0f; + p1.y = pos.y; + p1 = RotatePoint(pos, dir, p1); + + p1.x -= pos.x; + p1.y -= pos.y; + + limit = m_mapPos.x+m_mapDim.x-pos.x; + if ( p1.x > limit ) // dépasse à droite ? + { + p1.y = limit*p1.y/p1.x; + p1.x = limit; + } + limit = m_mapPos.y*0.75f+m_mapDim.y*0.75f-pos.y; + if ( p1.y > limit ) // dépasse en haut ? + { + p1.x = limit*p1.x/p1.y; + p1.y = limit; + } + limit = m_mapPos.x-pos.x; + if ( p1.x < limit ) // dépasse à gauche ? + { + p1.y = limit*p1.y/p1.x; + p1.x = limit; + } + limit = m_mapPos.y*0.75f-pos.y; + if ( p1.y < limit ) // dépasse en bas ? + { + p1.x = limit*p1.x/p1.y; + p1.y = limit; + } + + p1.x += pos.x; + p1.y += pos.y; + return p1; +} + +// Dessine le champ de vision de l'objet sélectionné. + +void CMap::DrawFocus(FPOINT pos, float dir, ObjectType type, MapColor color) +{ + FPOINT p0, p1, p2, uv1, uv2, rel; + float aMin, aMax, aOct, focus, a; + float limit[5]; + BOOL bEnding; + int quart; + + if ( m_bToy || m_fixImage[0] != 0 ) return; // carte avec image fixe ? + if ( color != MAPCOLOR_MOVE ) return; + + pos.x = (pos.x-m_offset.x)*(m_zoom*0.5f)/m_half+0.5f; + pos.y = (pos.y-m_offset.y)*(m_zoom*0.5f)/m_half+0.5f; + + if ( pos.x < 0.0f || pos.x > 1.0f || + pos.y < 0.0f || pos.y > 1.0f ) return; + + rel.x = pos.x*2.0f-1.0f; + rel.y = pos.y*2.0f-1.0f; // rel [-1..1] + + pos.x = m_mapPos.x+m_mapDim.x*pos.x; + pos.y = m_mapPos.y*0.75f+m_mapDim.y*pos.y*0.75f; + + focus = m_engine->RetFocus(); + dir += PI/2.0f; + aMin = NormAngle(dir-PI/4.0f*focus); + aMax = NormAngle(dir+PI/4.0f*focus); + + if ( aMin > aMax ) + { + aMax += PI*2.0f; // aMax toujours après aMin + } + + limit[0] = RotateAngle( 1.0f-rel.x, 1.0f-rel.y); // sup/droite + limit[1] = RotateAngle(-1.0f-rel.x, 1.0f-rel.y); // sup/gauche + limit[2] = RotateAngle(-1.0f-rel.x, -1.0f-rel.y); // inf/gauche + limit[3] = RotateAngle( 1.0f-rel.x, -1.0f-rel.y); // inf/droite + limit[4] = limit[0]+PI*2.0f; + + a = NormAngle(aMin); + for ( quart=0 ; quart<4 ; quart++ ) + { + if ( a >= limit[quart+0] && + a <= limit[quart+1] ) break; + } + if ( quart == 4 ) quart = -1; + + uv1.x = 113.0f/256.0f; // dégradé vert + uv1.y = 240.5f/256.0f; + uv2.x = 126.0f/256.0f; + uv2.y = 255.0f/256.0f; + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + + bEnding = FALSE; + do + { + quart ++; + aOct = limit[quart%4]; + if ( quart >= 4 ) aOct += PI*2.0f; + if ( aOct >= aMax-CHOUIA ) + { + aOct = aMax; + bEnding = TRUE; + } + + p0 = pos; + p1 = MapInter(pos, aMin); + p2 = MapInter(pos, aOct); + p0.y /= 0.75f; + p1.y /= 0.75f; + p2.y /= 0.75f; + DrawTriangle(p0, p2, p1, uv1, uv2); + + aMin = aOct; + } + while ( !bEnding ); +} + +// Dessine un objet. + +void CMap::DrawObject(FPOINT pos, float dir, ObjectType type, MapColor color, + BOOL bSelect, BOOL bHilite) +{ + FPOINT p1, p2, p3, p4, p5, dim, uv1, uv2; + BOOL bOut, bUp, bDown, bLeft, bRight; + + pos.x = (pos.x-m_offset.x)*(m_zoom*0.5f)/m_half+0.5f; + pos.y = (pos.y-m_offset.y)*(m_zoom*0.5f)/m_half+0.5f; + + bOut = bUp = bDown = bLeft = bRight = FALSE; + if ( pos.x < 0.06f ) { pos.x = 0.02f; bOut = bLeft = TRUE; } + if ( pos.y < 0.06f ) { pos.y = 0.02f; bOut = bDown = TRUE; } + if ( pos.x > 0.94f ) { pos.x = 0.98f; bOut = bRight = TRUE; } + if ( pos.y > 0.94f ) { pos.y = 0.98f; bOut = bUp = TRUE; } + + pos.x = m_mapPos.x+m_mapDim.x*pos.x; + pos.y = m_mapPos.y+m_mapDim.y*pos.y; + dim.x = 2.0f/128.0f*0.75f; + dim.y = 2.0f/128.0f; + + if ( bOut ) // hors de la carte ? + { + if ( color == MAPCOLOR_BBOX && !m_bRadar ) return; + if ( color == MAPCOLOR_ALIEN && !m_bRadar ) return; + + if ( Mod(m_time+(pos.x+pos.y)*4.0f, 0.6f) > 0.2f ) + { + return; // clignotte + } + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + if ( bUp ) + { + uv1.x = 160.5f/256.0f; // triangle jaune ^ + uv1.y = 240.5f/256.0f; + uv2.x = 175.0f/256.0f; + uv2.y = 255.0f/256.0f; + } + if ( bDown ) + { + uv1.x = 160.5f/256.0f; // triangle jaune v + uv1.y = 255.0f/256.0f; + uv2.x = 175.0f/256.0f; + uv2.y = 240.5f/256.0f; + } + if ( bRight ) + { + uv1.x = 176.5f/256.0f; // triangle jaune > + uv1.y = 240.5f/256.0f; + uv2.x = 191.0f/256.0f; + uv2.y = 255.0f/256.0f; + } + if ( bLeft ) + { + uv1.x = 191.0f/256.0f; // triangle jaune < + uv1.y = 240.5f/256.0f; + uv2.x = 176.5f/256.0f; + uv2.y = 255.0f/256.0f; + } + pos.x -= dim.x/2.0f; + pos.y -= dim.y/2.0f; + DrawIcon(pos, dim, uv1, uv2); + return; + } + + if ( bSelect ) + { + if ( m_bToy ) + { + dim.x *= 1.2f+sinf(m_time*8.0f)*0.1f; + dim.y *= 1.2f+sinf(m_time*8.0f)*0.1f; + } + else + { + dim.x *= 1.2f+sinf(m_time*8.0f)*0.3f; + dim.y *= 1.2f+sinf(m_time*8.0f)*0.3f; + } + } + if ( color == MAPCOLOR_BASE || + color == MAPCOLOR_FIX || + color == MAPCOLOR_MOVE ) + { + if ( bHilite ) + { + dim.x *= 2.2f; + dim.y *= 2.2f; + } + else + { + dim.x *= 0.6f; + dim.y *= 0.6f; + } + } + if ( color == MAPCOLOR_ALIEN ) + { + dim.x *= 1.4f; + dim.y *= 1.4f; + } + if ( type == OBJECT_TEEN28 ) // bouteille ? + { + dim.x *= 3.0f; + dim.y *= 3.0f; + bHilite = TRUE; + } + if ( type == OBJECT_TEEN34 ) // caillou ? + { + dim.x *= 2.0f; + dim.y *= 2.0f; + bHilite = TRUE; + } + + if ( color == MAPCOLOR_MOVE && bSelect ) + { + if ( m_bToy ) + { + p1.x = pos.x; + p1.y = pos.y+dim.y*1.4f; + p1 = RotatePoint(pos, dir, p1); + p1.x = pos.x+(p1.x-pos.x)*0.75f; + + p2.x = pos.x+dim.x*1.2f; + p2.y = pos.y+dim.y*0.8f; + p2 = RotatePoint(pos, dir, p2); + p2.x = pos.x+(p2.x-pos.x)*0.75f; + + p3.x = pos.x+dim.x*1.2f; + p3.y = pos.y-dim.y*1.0f; + p3 = RotatePoint(pos, dir, p3); + p3.x = pos.x+(p3.x-pos.x)*0.75f; + + p4.x = pos.x-dim.x*1.2f; + p4.y = pos.y-dim.y*1.0f; + p4 = RotatePoint(pos, dir, p4); + p4.x = pos.x+(p4.x-pos.x)*0.75f; + + p5.x = pos.x-dim.x*1.2f; + p5.y = pos.y+dim.y*0.8f; + p5 = RotatePoint(pos, dir, p5); + p5.x = pos.x+(p5.x-pos.x)*0.75f; + } + else + { + p1.x = pos.x; + p1.y = pos.y+dim.y*2.4f; + p1 = RotatePoint(pos, dir, p1); + p1.x = pos.x+(p1.x-pos.x)*0.75f; + + p2.x = pos.x+dim.x*1.0f; + p2.y = pos.y-dim.y*1.6f; + p2 = RotatePoint(pos, dir, p2); + p2.x = pos.x+(p2.x-pos.x)*0.75f; + + p3.x = pos.x-dim.x*1.0f; + p3.y = pos.y-dim.y*1.6f; + p3 = RotatePoint(pos, dir, p3); + p3.x = pos.x+(p3.x-pos.x)*0.75f; + } + } + + pos.x -= dim.x/2.0f; + pos.y -= dim.y/2.0f; + + if ( color == MAPCOLOR_BASE || + color == MAPCOLOR_FIX ) + { + DrawObjectIcon(pos, dim, color, type, bHilite); + } + + if ( color == MAPCOLOR_MOVE ) + { + if ( bSelect ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + if ( m_bToy ) + { + uv1.x = 164.5f/256.0f; // pentagone noir + uv1.y = 228.5f/256.0f; + uv2.x = 172.0f/256.0f; + uv2.y = 236.0f/256.0f; + DrawPenta(p1, p2, p3, p4, p5, uv1, uv2); + } + else + { + uv1.x = 144.5f/256.0f; // triangle rouge + uv1.y = 240.5f/256.0f; + uv2.x = 159.0f/256.0f; + uv2.y = 255.0f/256.0f; + DrawTriangle(p1, p2, p3, uv1, uv2); + } + } + DrawObjectIcon(pos, dim, color, type, bHilite); + } + + if ( color == MAPCOLOR_BBOX ) + { + if ( m_bRadar ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 64.5f/256.0f; // triangle bleu + uv1.y = 240.5f/256.0f; + uv2.x = 79.0f/256.0f; + uv2.y = 255.0f/256.0f; + DrawIcon(pos, dim, uv1, uv2); + } + } + + if ( color == MAPCOLOR_ALIEN ) + { + if ( m_bRadar ) + { + DrawObjectIcon(pos, dim, color, type, TRUE); + } + } + + if ( color == MAPCOLOR_WAYPOINTb ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 192.5f/256.0f; // croix bleue + uv1.y = 240.5f/256.0f; + uv2.x = 207.0f/256.0f; + uv2.y = 255.0f/256.0f; + DrawIcon(pos, dim, uv1, uv2); + } + if ( color == MAPCOLOR_WAYPOINTr ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 208.5f/256.0f; // croix rouge + uv1.y = 240.5f/256.0f; + uv2.x = 223.0f/256.0f; + uv2.y = 255.0f/256.0f; + DrawIcon(pos, dim, uv1, uv2); + } + if ( color == MAPCOLOR_WAYPOINTg ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 224.5f/256.0f; // croix verte + uv1.y = 240.5f/256.0f; + uv2.x = 239.0f/256.0f; + uv2.y = 255.0f/256.0f; + DrawIcon(pos, dim, uv1, uv2); + } + if ( color == MAPCOLOR_WAYPOINTy ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 240.5f/256.0f; // croix jaune + uv1.y = 240.5f/256.0f; + uv2.x = 255.0f/256.0f; + uv2.y = 255.0f/256.0f; + DrawIcon(pos, dim, uv1, uv2); + } + if ( color == MAPCOLOR_WAYPOINTv ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 192.5f/256.0f; // croix violette + uv1.y = 224.5f/256.0f; + uv2.x = 207.0f/256.0f; + uv2.y = 239.0f/256.0f; + DrawIcon(pos, dim, uv1, uv2); + } +} + +// Dessine l'icône d'un objet. + +void CMap::DrawObjectIcon(FPOINT pos, FPOINT dim, MapColor color, + ObjectType type, BOOL bHilite) +{ + FPOINT ppos, ddim, uv1, uv2; + float dp; + int icon; + + dp = 0.5f/256.0f; + + m_engine->SetTexture("button3.tga"); + m_engine->SetState(D3DSTATENORMAL); + if ( color == MAPCOLOR_MOVE ) + { + uv1.x = 160.0f/256.0f; // bleu + uv1.y = 224.0f/256.0f; + } + else if ( color == MAPCOLOR_ALIEN ) + { + uv1.x = 224.0f/256.0f; // vert + uv1.y = 224.0f/256.0f; + } + else + { + uv1.x = 192.0f/256.0f; // jaune + uv1.y = 224.0f/256.0f; + } + uv2.x = uv1.x+32.0f/256.0f; + uv2.y = uv1.y+32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2); // fond coloré + + if ( bHilite ) + { + icon = -1; + if ( type == OBJECT_FACTORY ) icon = 32; + if ( type == OBJECT_DERRICK ) icon = 33; + if ( type == OBJECT_CONVERT ) icon = 34; + if ( type == OBJECT_RESEARCH ) icon = 35; + if ( type == OBJECT_STATION ) icon = 36; + if ( type == OBJECT_TOWER ) icon = 37; + if ( type == OBJECT_LABO ) icon = 38; + if ( type == OBJECT_ENERGY ) icon = 39; + if ( type == OBJECT_RADAR ) icon = 40; + if ( type == OBJECT_INFO ) icon = 44; + if ( type == OBJECT_REPAIR ) icon = 41; + if ( type == OBJECT_DESTROYER) icon = 41; + if ( type == OBJECT_NUCLEAR ) icon = 42; + if ( type == OBJECT_PARA ) icon = 46; + if ( type == OBJECT_SAFE ) icon = 47; + if ( type == OBJECT_HUSTON ) icon = 48; + if ( type == OBJECT_TARGET1 ) icon = 45; + if ( type == OBJECT_BASE ) icon = 43; + if ( type == OBJECT_HUMAN ) icon = 8; + if ( type == OBJECT_MOBILEfa ) icon = 11; + if ( type == OBJECT_MOBILEta ) icon = 10; + if ( type == OBJECT_MOBILEwa ) icon = 9; + if ( type == OBJECT_MOBILEia ) icon = 22; + if ( type == OBJECT_MOBILEfc ) icon = 17; + if ( type == OBJECT_MOBILEtc ) icon = 16; + if ( type == OBJECT_MOBILEwc ) icon = 15; + if ( type == OBJECT_MOBILEic ) icon = 23; + if ( type == OBJECT_MOBILEfi ) icon = 27; + if ( type == OBJECT_MOBILEti ) icon = 26; + if ( type == OBJECT_MOBILEwi ) icon = 25; + if ( type == OBJECT_MOBILEii ) icon = 28; + if ( type == OBJECT_MOBILEfs ) icon = 14; + if ( type == OBJECT_MOBILEts ) icon = 13; + if ( type == OBJECT_MOBILEws ) icon = 12; + if ( type == OBJECT_MOBILEis ) icon = 24; + if ( type == OBJECT_MOBILErt ) icon = 18; + if ( type == OBJECT_MOBILErc ) icon = 19; + if ( type == OBJECT_MOBILErr ) icon = 20; + if ( type == OBJECT_MOBILErs ) icon = 29; + if ( type == OBJECT_MOBILEsa ) icon = 21; + if ( type == OBJECT_MOBILEft ) icon = 30; + if ( type == OBJECT_MOBILEtt ) icon = 30; + if ( type == OBJECT_MOBILEwt ) icon = 30; + if ( type == OBJECT_MOBILEit ) icon = 30; + if ( type == OBJECT_MOBILEtg ) icon = 45; + if ( type == OBJECT_MOBILEdr ) icon = 48; + if ( type == OBJECT_APOLLO2 ) icon = 49; + if ( type == OBJECT_MOTHER ) icon = 31; + if ( type == OBJECT_ANT ) icon = 31; + if ( type == OBJECT_SPIDER ) icon = 31; + if ( type == OBJECT_BEE ) icon = 31; + if ( type == OBJECT_WORM ) icon = 31; + if ( type == OBJECT_TEEN28 ) icon = 48; // bouteille + if ( type == OBJECT_TEEN34 ) icon = 48; // caillou + if ( icon == -1 ) return; + + m_engine->SetState(D3DSTATETTw); + uv1.x = (32.0f/256.0f)*(icon%8); + uv1.y = (32.0f/256.0f)*(icon/8); + uv2.x = uv1.x+32.0f/256.0f; + uv2.y = uv1.y+32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2); // icône + } +} + +// Dessine l'objet survolé par la souris. + +void CMap::DrawHilite(FPOINT pos) +{ + FPOINT dim, uv1, uv2; + BOOL bOut, bUp, bDown, bLeft, bRight; + + if ( m_bToy || m_fixImage[0] != 0 ) return; // carte avec image fixe ? + + pos.x = (pos.x-m_offset.x)*(m_zoom*0.5f)/m_half+0.5f; + pos.y = (pos.y-m_offset.y)*(m_zoom*0.5f)/m_half+0.5f; + + bOut = bUp = bDown = bLeft = bRight = FALSE; + if ( pos.x < 0.06f ) { pos.x = 0.02f; bOut = bLeft = TRUE; } + if ( pos.y < 0.06f ) { pos.y = 0.02f; bOut = bDown = TRUE; } + if ( pos.x > 0.94f ) { pos.x = 0.98f; bOut = bRight = TRUE; } + if ( pos.y > 0.94f ) { pos.y = 0.98f; bOut = bUp = TRUE; } + + pos.x = m_mapPos.x+m_mapDim.x*pos.x; + pos.y = m_mapPos.y+m_mapDim.y*pos.y; + dim.x = 2.0f/128.0f*0.75f; + dim.y = 2.0f/128.0f; + dim.x *= 2.0f+cosf(m_time*8.0f)*0.5f; + dim.y *= 2.0f+cosf(m_time*8.0f)*0.5f; + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 160.5f/256.0f; // hilite + uv1.y = 224.5f/256.0f; + uv2.x = 175.0f/256.0f; + uv2.y = 239.0f/256.0f; + pos.x -= dim.x/2.0f; + pos.y -= dim.y/2.0f; + DrawIcon(pos, dim, uv1, uv2); +} + +// Dessine une icône triangulaire. + +void CMap::DrawTriangle(FPOINT p1, FPOINT p2, FPOINT p3, FPOINT uv1, FPOINT uv2) +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2 vertex[3]; // 1 triangle + D3DVECTOR n; + + device = m_engine->RetD3DDevice(); + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, uv1.x,uv1.y); + vertex[1] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, uv1.x,uv2.y); + vertex[2] = D3DVERTEX2(D3DVECTOR(p3.x, p3.y, 0.0f), n, uv2.x,uv2.y); + + device->DrawPrimitive(D3DPT_TRIANGLELIST, D3DFVF_VERTEX2, vertex, 3, NULL); + m_engine->AddStatisticTriangle(1); +} + +// Dessine une icône pentagonulaire (à 5 côtés, quoi !). + +void CMap::DrawPenta(FPOINT p1, FPOINT p2, FPOINT p3, FPOINT p4, FPOINT p5, FPOINT uv1, FPOINT uv2) +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2 vertex[5]; // 1 pentagone + D3DVECTOR n; + + device = m_engine->RetD3DDevice(); + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + +#if 1 + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, uv1.x,uv1.y); + vertex[1] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, uv1.x,uv2.y); + vertex[2] = D3DVERTEX2(D3DVECTOR(p5.x, p5.y, 0.0f), n, uv2.x,uv2.y); + vertex[3] = D3DVERTEX2(D3DVECTOR(p3.x, p3.y, 0.0f), n, uv2.x,uv2.y); + vertex[4] = D3DVERTEX2(D3DVECTOR(p4.x, p4.y, 0.0f), n, uv2.x,uv2.y); + + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 5, NULL); +#else + vertex[0] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, uv1.x,uv1.y); + vertex[1] = D3DVERTEX2(D3DVECTOR(p3.x, p3.y, 0.0f), n, uv1.x,uv2.y); + vertex[2] = D3DVERTEX2(D3DVECTOR(p4.x, p4.y, 0.0f), n, uv2.x,uv2.y); + + device->DrawPrimitive(D3DPT_TRIANGLELIST, D3DFVF_VERTEX2, vertex, 3, NULL); +#endif + m_engine->AddStatisticTriangle(3); +} + +// Dessine le tableau des vertex. + +void CMap::DrawVertex(FPOINT uv1, FPOINT uv2, float zoom) +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2 vertex[4]; // 2 triangles + FPOINT p1, p2, c; + D3DVECTOR n; + + device = m_engine->RetD3DDevice(); + + p1.x = m_pos.x; + p1.y = m_pos.y; + p2.x = m_pos.x + m_dim.x; + p2.y = m_pos.y + m_dim.y; + + c.x = (p1.x+p2.x)/2.0f; + c.y = (p1.y+p2.y)/2.0f; // centre + + p1.x = (p1.x-c.x)*zoom + c.x; + p1.y = (p1.y-c.y)*zoom + c.y; + + p2.x = (p2.x-c.x)*zoom + c.x; + p2.y = (p2.y-c.y)*zoom + c.y; + + m_mapPos = p1; + m_mapDim.x = p2.x-p1.x; + m_mapDim.y = p2.y-p1.y; + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, uv1.x,uv2.y); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, uv1.x,uv1.y); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, uv2.x,uv2.y); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, uv2.x,uv1.y); + + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); +} + + +// Met à jour le terrain dans la carte. + +void CMap::UpdateTerrain() +{ + D3DCOLORVALUE color; + D3DVECTOR pos; + float scale, water, level, intensity; + int x, y; + + if ( m_fixImage[0] != 0 ) return; // image fixe ? + if ( !m_engine->OpenImage("map.tga") ) return; + + scale = m_terrain->RetScaleRelief(); + water = m_water->RetLevel(); + color.a = 0.0f; + + for ( y=0 ; y<256 ; y++ ) + { + for ( x=0 ; x<256 ; x++ ) + { + pos.x = ((float)x-128.0f)*m_half/128.0f; + pos.z = -((float)y-128.0f)*m_half/128.0f; + pos.y = 0.0f; + + if ( pos.x >= -m_half && pos.x <= m_half && + pos.z >= -m_half && pos.z <= m_half ) + { + level = m_terrain->RetFloorLevel(pos, TRUE)/scale; + } + else + { + level = 1000.0f; + } + + intensity = level/256.0f; + if ( intensity < 0.0f ) intensity = 0.0f; + if ( intensity > 1.0f ) intensity = 1.0f; + + if ( level >= water ) // sur l'eau ? + { + color.r = m_floorColor.r + (intensity-0.5f); + color.g = m_floorColor.g + (intensity-0.5f); + color.b = m_floorColor.b + (intensity-0.5f); + } + else // sous l'eau ? + { + color.r = m_waterColor.r + (intensity-0.5f); + color.g = m_waterColor.g + (intensity-0.5f); + color.b = m_waterColor.b + (intensity-0.5f); + } + + m_engine->SetDot(x, y, color); + } + } + + m_engine->CopyImage(); // copie avec juste le terrain dessiné + m_engine->CloseImage(); +} + +// Met à jour le terrain dans la carte. + +void CMap::UpdateTerrain(int bx, int by, int ex, int ey) +{ + D3DCOLORVALUE color; + D3DVECTOR pos; + float scale, water, level, intensity; + int x, y; + + if ( m_fixImage[0] != 0 ) return; // image fixe ? + if ( !m_engine->OpenImage("map.tga") ) return; + m_engine->LoadImage(); + + scale = m_terrain->RetScaleRelief(); + water = m_water->RetLevel(); + color.a = 0.0f; + + for ( y=by ; y= -m_half && pos.x <= m_half && + pos.z >= -m_half && pos.z <= m_half ) + { + level = m_terrain->RetFloorLevel(pos, TRUE)/scale; + } + else + { + level = 1000.0f; + } + + intensity = level/256.0f; + if ( intensity < 0.0f ) intensity = 0.0f; + if ( intensity > 1.0f ) intensity = 1.0f; + + if ( level > water ) // sur l'eau ? + { + color.r = m_floorColor.r + (intensity-0.5f); + color.g = m_floorColor.g + (intensity-0.5f); + color.b = m_floorColor.b + (intensity-0.5f); + } + else // sous l'eau ? + { + color.r = m_waterColor.r + (intensity-0.5f); + color.g = m_waterColor.g + (intensity-0.5f); + color.b = m_waterColor.b + (intensity-0.5f); + } + + m_engine->SetDot(x, y, color); + } + } + + m_engine->CopyImage(); // copie avec juste le terrain dessiné + m_engine->CloseImage(); +} + + +// Vide tous les objets. + +void CMap::FlushObject() +{ + int i; + + m_totalFix = 0; // index objet fixes + m_totalMove = MAPMAXOBJECT-2; // index véhicules mobiles + m_bRadar = m_main->RetCheatRadar(); // pas de radar + + for ( i=0 ; i= m_totalMove ) return; // table pleine ? + + if ( !pObj->RetActif() ) return; + if ( !pObj->RetSelectable() ) return; + if ( pObj->RetProxyActivate() ) return; + if ( pObj->RetTruck() != 0 ) return; + + type = pObj->RetType(); + pos = pObj->RetPosition(0); + dir = -(pObj->RetAngleY(0)+PI/2.0f); + + if ( m_angle != 0.0f ) + { + ppos = RotatePoint(m_angle, FPOINT(pos.x, pos.z)); + pos.x = ppos.x; + pos.z = ppos.y; + dir += m_angle; + } + + if ( type == OBJECT_RADAR ) + { + m_bRadar = TRUE; // radar existe + } + + color = MAPCOLOR_NULL; + if ( type == OBJECT_BASE ) + { + color = MAPCOLOR_BASE; + } + if ( type == OBJECT_DERRICK || + type == OBJECT_FACTORY || + type == OBJECT_STATION || + type == OBJECT_CONVERT || + type == OBJECT_REPAIR || + type == OBJECT_DESTROYER|| + type == OBJECT_TOWER || + type == OBJECT_RESEARCH || + type == OBJECT_RADAR || + type == OBJECT_INFO || + type == OBJECT_ENERGY || + type == OBJECT_LABO || + type == OBJECT_NUCLEAR || + type == OBJECT_PARA || + type == OBJECT_SAFE || + type == OBJECT_HUSTON || + type == OBJECT_TARGET1 || + type == OBJECT_START || + type == OBJECT_END || // objet fixe ? + type == OBJECT_TEEN28 || // bouteille ? + type == OBJECT_TEEN34 ) // caillou ? + { + color = MAPCOLOR_FIX; + } + if ( type == OBJECT_BBOX || + type == OBJECT_KEYa || + type == OBJECT_KEYb || + type == OBJECT_KEYc || + type == OBJECT_KEYd ) + { + color = MAPCOLOR_BBOX; + } + if ( type == OBJECT_HUMAN || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEtg || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr || + type == OBJECT_APOLLO2 ) // véhicule mobile ? + { + color = MAPCOLOR_MOVE; + } + if ( type == OBJECT_ANT || + type == OBJECT_BEE || + type == OBJECT_WORM || + type == OBJECT_SPIDER ) // ennemi mobile ? + { + color = MAPCOLOR_ALIEN; + } + if ( type == OBJECT_WAYPOINT || + type == OBJECT_FLAGb ) + { + color = MAPCOLOR_WAYPOINTb; + } + if ( type == OBJECT_FLAGr ) + { + color = MAPCOLOR_WAYPOINTr; + } + if ( type == OBJECT_FLAGg ) + { + color = MAPCOLOR_WAYPOINTg; + } + if ( type == OBJECT_FLAGy ) + { + color = MAPCOLOR_WAYPOINTy; + } + if ( type == OBJECT_FLAGv ) + { + color = MAPCOLOR_WAYPOINTv; + } + + if ( color == MAPCOLOR_NULL ) return; + + if ( m_fixImage[0] != 0 && !m_bDebug ) // carte avec image fixe ? + { + if ( (type == OBJECT_TEEN28 || + type == OBJECT_TEEN34 ) && + m_mode == 0 ) return; + + if ( type != OBJECT_TEEN28 && + type != OBJECT_TEEN34 && + color != MAPCOLOR_MOVE ) return; + } + + if ( pObj->RetSelect() ) + { + m_map[MAPMAXOBJECT-1].type = type; + m_map[MAPMAXOBJECT-1].object = pObj; + m_map[MAPMAXOBJECT-1].color = color; + m_map[MAPMAXOBJECT-1].pos.x = pos.x; + m_map[MAPMAXOBJECT-1].pos.y = pos.z; + m_map[MAPMAXOBJECT-1].dir = dir; + m_map[MAPMAXOBJECT-1].bUsed = TRUE; + } + else + { + if ( color == MAPCOLOR_BASE || + color == MAPCOLOR_FIX ) + { + m_map[m_totalFix].type = type; + m_map[m_totalFix].object = pObj; + m_map[m_totalFix].color = color; + m_map[m_totalFix].pos.x = pos.x; + m_map[m_totalFix].pos.y = pos.z; + m_map[m_totalFix].dir = dir; + m_map[m_totalFix].bUsed = TRUE; + m_totalFix ++; + } + else + { + m_map[m_totalMove].type = type; + m_map[m_totalMove].object = pObj; + m_map[m_totalMove].color = color; + m_map[m_totalMove].pos.x = pos.x; + m_map[m_totalMove].pos.y = pos.z; + m_map[m_totalMove].dir = dir; + m_map[m_totalMove].bUsed = TRUE; + m_totalMove --; + } + } +} + diff --git a/src/map.h b/src/map.h new file mode 100644 index 00000000..f5c9b31d --- /dev/null +++ b/src/map.h @@ -0,0 +1,126 @@ +// map.h + +#ifndef _MAP_H_ +#define _MAP_H_ + + +#include "control.h" + + +class CD3DEngine; +class CTerrain; +class CWater; +class CObject; +class CRobotMain; + +enum ObjectType; + + + +#define MAPMAXOBJECT 100 + +enum MapColor +{ + MAPCOLOR_NULL, + MAPCOLOR_BASE, + MAPCOLOR_FIX, + MAPCOLOR_MOVE, + MAPCOLOR_ALIEN, + MAPCOLOR_WAYPOINTb, + MAPCOLOR_WAYPOINTr, + MAPCOLOR_WAYPOINTg, + MAPCOLOR_WAYPOINTy, + MAPCOLOR_WAYPOINTv, + MAPCOLOR_BBOX, +}; + +typedef struct +{ + char bUsed; + CObject* object; + MapColor color; + ObjectType type; + FPOINT pos; + float dir; +} +MapObject; + + + +class CMap : public CControl +{ +public: + CMap(CInstanceManager* iMan); + ~CMap(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + BOOL EventProcess(const Event &event); + void Draw(); + + void UpdateTerrain(); + void UpdateTerrain(int bx, int by, int ex, int ey); + + void SetFixImage(char *filename); + BOOL RetFixImage(); + + void SetOffset(float ox, float oy); + void SetAngle(float angle); + void SetMode(int mode); + void SetToy(BOOL bToy); + void SetDebug(BOOL bDebug); + + void SetZoom(float value); + float RetZoom(); + + void SetEnable(BOOL bEnable); + BOOL RetEnable(); + + void SetFloorColor(D3DCOLORVALUE color); + void SetWaterColor(D3DCOLORVALUE color); + + void FlushObject(); + void UpdateObject(CObject* pObj); + + CObject* DetectObject(FPOINT pos, BOOL &bInMap); + void SetHilite(CObject* pObj); + +protected: + FPOINT AdjustOffset(FPOINT offset); + void SelectObject(FPOINT pos); + FPOINT MapInter(FPOINT pos, float dir); + void DrawFocus(FPOINT pos, float dir, ObjectType type, MapColor color); + void DrawObject(FPOINT pos, float dir, ObjectType type, MapColor color, BOOL bSelect, BOOL bHilite); + void DrawObjectIcon(FPOINT pos, FPOINT dim, MapColor color, ObjectType type, BOOL bHilite); + void DrawHilite(FPOINT pos); + void DrawTriangle(FPOINT p1, FPOINT p2, FPOINT p3, FPOINT uv1, FPOINT uv2); + void DrawPenta(FPOINT p1, FPOINT p2, FPOINT p3, FPOINT p4, FPOINT p5, FPOINT uv1, FPOINT uv2); + void DrawVertex(FPOINT uv1, FPOINT uv2, float zoom); + +protected: + CTerrain* m_terrain; + CWater* m_water; + CRobotMain* m_main; + + BOOL m_bEnable; + float m_time; + float m_half; + float m_zoom; + FPOINT m_offset; + float m_angle; + D3DCOLORVALUE m_floorColor; + D3DCOLORVALUE m_waterColor; + MapObject m_map[MAPMAXOBJECT]; + int m_totalFix; + int m_totalMove; + int m_hiliteRank; + FPOINT m_mapPos; + FPOINT m_mapDim; + BOOL m_bRadar; + char m_fixImage[100]; + int m_mode; + BOOL m_bToy; + BOOL m_bDebug; +}; + + +#endif //_MAP_H_ diff --git a/src/math3d.cpp b/src/math3d.cpp new file mode 100644 index 00000000..75e3951f --- /dev/null +++ b/src/math3d.cpp @@ -0,0 +1,1026 @@ +// math3d.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "D3DUtil.h" +#include "math3d.h" + + + +// Retourne TRUE si 2 nombres sont presques égaux. + +BOOL IsEqual(float a, float b) +{ + return Abs(a-b) < CHOUIA; +} + + +// Retourne la valeur minimale. + +inline float Min(float a, float b) +{ + if ( a <= b ) return a; + else return b; +} + +inline float Min(float a, float b, float c) +{ + return Min( Min(a,b), c ); +} + +inline float Min(float a, float b, float c, float d) +{ + return Min( Min(a,b), Min(c,d) ); +} + +inline float Min(float a, float b, float c, float d, float e) +{ + return Min( Min(a,b), Min(c,d), e ); +} + + +// Retourne la valeur maximale. + +inline float Max(float a, float b) +{ + if ( a >= b ) return a; + else return b; +} + +inline float Max(float a, float b, float c) +{ + return Max( Max(a,b), c ); +} + +inline float Max(float a, float b, float c, float d) +{ + return Max( Max(a,b), Max(c,d) ); +} + +inline float Max(float a, float b, float c, float d, float e) +{ + return Max( Max(a,b), Max(c,d), e ); +} + + +// Retourne la valeur normalisée (0..1). + +inline float Norm(float a) +{ + if ( a < 0.0f ) return 0.0f; + if ( a > 1.0f ) return 1.0f; + return a; +} + + +// Retourne la valeur absolue d'un nombre. + +inline float Abs(float a) +{ + return (float)fabs(a); +} + + +// Permute deux entiers. + +inline void Swap(int &a, int &b) +{ + int c; + + c = a; + a = b; + b = c; +} + +// Permute deux réels. + +inline void Swap(float &a, float &b) +{ + float c; + + c = a; + a = b; + b = c; +} + +// Permute deux points. + +inline void Swap(FPOINT &a, FPOINT &b) +{ + FPOINT c; + + c = a; + a = b; + b = c; +} + +// Retourne le modulo d'un nombre flottant. +// Mod(8.1, 4) = 0.1 +// Mod(n, 1) = partie fractionnaire de n + +inline float Mod(float a, float m) +{ + return a - ((int)(a/m))*m; +} + +// Retourne un angle normalisé, c'est-à-dire compris entre +// 0 et 2*PI. + +inline float NormAngle(float angle) +{ + angle = Mod(angle, PI*2.0f); + if ( angle < 0.0f ) + { + return PI*2.0f + angle; + } + else + { + return angle; + } +} + +// Teste si un angle est compris entre 2 bornes. + +BOOL TestAngle(float angle, float min, float max) +{ + angle = NormAngle(angle); + min = NormAngle(min); + max = NormAngle(max); + + if ( min > max ) + { + return ( angle <= max || angle >= min ); + } + else + { + return ( angle >= min && angle <= max ); + } +} + + +// Calcule l'angle permettant de tourner de l'angle a vers l'angle g. +// Un angle positif est anti-horaire (CCW). + +float Direction(float a, float g) +{ + a = NormAngle(a); + g = NormAngle(g); + + if ( a < g ) + { + if ( a+PI*2.0f-g < g-a ) a += PI*2.0f; + } + else + { + if ( g+PI*2.0f-a < a-g ) g += PI*2.0f; + } + return (g-a); +} + + +// Fait tourner un point autour d'un centre. +// L'angle est exprimé en radians. +// Un angle positif est anti-horaire (CCW). + +FPOINT RotatePoint(FPOINT center, float angle, FPOINT p) +{ + FPOINT a, b; + + a.x = p.x-center.x; + a.y = p.y-center.y; + + b.x = a.x*cosf(angle) - a.y*sinf(angle); + b.y = a.x*sinf(angle) + a.y*cosf(angle); + + b.x += center.x; + b.y += center.y; + return b; +} + +// Fait tourner un point autour de l'origine. +// L'angle est exprimé en radians. +// Un angle positif est anti-horaire (CCW). + +FPOINT RotatePoint(float angle, FPOINT p) +{ + FPOINT a; + + a.x = p.x*cosf(angle) - p.y*sinf(angle); + a.y = p.x*sinf(angle) + p.y*cosf(angle); + + return a; +} + +// Fait tourner un vecteur (dist;0). +// L'angle est exprimé en radians. +// Un angle positif est anti-horaire (CCW). + +FPOINT RotatePoint(float angle, float dist) +{ + FPOINT a; + + a.x = dist*cosf(angle); + a.y = dist*sinf(angle); + + return a; +} + +// Calcule l'angle d'un triangle rectangle. +// L'angle est anti-horaire (CCW), compris entre 0 et 2*PI. +// Pour obtenir un angle horaire (CW), il suffit de passer -y. +// +// ^ +// | +// y o----o +// | / | +// |/)a | +// ----o----o--> +// | x +// | + +float RotateAngle(float x, float y) +{ +#if 1 + if ( x == 0.0f && y == 0.0f ) return 0.0f; + + if ( x >= 0.0f ) + { + if ( y >= 0.0f ) + { + if ( x > y ) return atanf(y/x); + else return PI*0.5f - atanf(x/y); + } + else + { + if ( x > -y ) return PI*2.0f + atanf(y/x); + else return PI*1.5f - atanf(x/y); + } + } + else + { + if ( y >= 0.0f ) + { + if ( -x > y ) return PI*1.0f + atanf(y/x); + else return PI*0.5f - atanf(x/y); + } + else + { + if ( -x > -y ) return PI*1.0f + atanf(y/x); + else return PI*1.5f - atanf(x/y); + } + } +#else + float angle; + + if ( x == 0.0f ) + { + if ( y > 0.0f ) + { + return 90.0f*PI/180.0f; + } + else + { + return 270.0f*PI/180.0f; + } + } + else + { + angle = atanf(y/x); + if ( x < 0.0f ) + { + angle += PI; + } + return angle; + } +#endif +} + +// Calcule l'angle entre deux points et un centre. +// L'angle est exprimé en radians. +// Un angle positif est anti-horaire (CCW). + +float RotateAngle(FPOINT center, FPOINT p1, FPOINT p2) +{ + float a1, a2, a; + + if ( p1.x == center.x && + p1.y == center.y ) return 0; + + if ( p2.x == center.x && + p2.y == center.y ) return 0; + + a1 = asinf((p1.y-center.y)/Length(p1,center)); + a2 = asinf((p2.y-center.y)/Length(p2,center)); + + if ( p1.x < center.x ) a1 = PI-a1; + if ( p2.x < center.x ) a2 = PI-a2; + + a = a2-a1; + if ( a < 0 ) a += PI*2; + return a; +} + +// Retourne py placé sur la droite ab. + +float MidPoint(FPOINT a, FPOINT b, float px) +{ + if ( Abs(a.x-b.x) < CHOUIA ) + { + if ( a.y < b.y ) return BEAUCOUP; + else return -BEAUCOUP; + } + return (b.y-a.y)*(px-a.x)/(b.x-a.x)+a.y; +} + +// Avance de "dist" le long du segment p1-p2. + +D3DVECTOR SegmentDist(const D3DVECTOR &p1, const D3DVECTOR &p2, float dist) +{ + return p1+Normalize(p2-p1)*dist; +} + +// Vérifie si un point est dans un triangle. + +BOOL IsInsideTriangle(FPOINT a, FPOINT b, FPOINT c, FPOINT p) +{ + float n, m; + + if ( p.x < a.x && p.x < b.x && p.x < c.x ) return FALSE; + if ( p.x > a.x && p.x > b.x && p.x > c.x ) return FALSE; + if ( p.y < a.y && p.y < b.y && p.y < c.y ) return FALSE; + if ( p.y > a.y && p.y > b.y && p.y > c.y ) return FALSE; + + if ( a.x > b.x ) Swap(a,b); + if ( a.x > c.x ) Swap(a,c); + if ( c.x < a.x ) Swap(c,a); + if ( c.x < b.x ) Swap(c,b); + + n = MidPoint(a, b, p.x); + m = MidPoint(a, c, p.x); + if ( (n>p.y||p.y>m) && (np.y||p.y>m) && (n= 100 ) break; + } + } + + sum.x = 0; + sum.y = 0; + sum.z = 0; + for ( j=0 ; j 0.1f || + Abs(n1.y-n2.y) > 0.1f || + Abs(n1.z-n2.z) > 0.1f ) return FALSE; + + dist = DistancePlanPoint(plan1[0], plan1[1], plan1[2], plan2[0]); + if ( dist > 0.1f ) return FALSE; + + return TRUE; +} + + +// Calcule la matrice permettant de faire 3 rotations +// dans l'ordre X, Z et Y. +// >>>>>> A OPTIMISER !!! + +void MatRotateXZY(D3DMATRIX &mat, D3DVECTOR angle) +{ + D3DMATRIX temp; + + D3DUtil_SetRotateXMatrix(temp, angle.x); + D3DUtil_SetRotateZMatrix(mat, angle.z); + D3DMath_MatrixMultiply(mat, mat, temp); + D3DUtil_SetRotateYMatrix(temp, angle.y); + D3DMath_MatrixMultiply(mat, mat, temp); // X-Z-Y +} + +// Calcule la matrice permettant de faire 3 rotations +// dans l'ordre Z, X et Y. +// >>>>>> A OPTIMISER !!! + +void MatRotateZXY(D3DMATRIX &mat, D3DVECTOR angle) +{ + D3DMATRIX temp; + + D3DUtil_SetRotateZMatrix(temp, angle.z); + D3DUtil_SetRotateXMatrix(mat, angle.x); + D3DMath_MatrixMultiply(mat, mat, temp); + D3DUtil_SetRotateYMatrix(temp, angle.y); + D3DMath_MatrixMultiply(mat, mat, temp); // Z-X-Y +} + + +// Retourne une valeur aléatoire comprise entre 0 et 1. + +float Rand() +{ + return (float)rand()/RAND_MAX; +} + + +// Gestion de la zone neutre d'un joystick. + +// in: -1 0 1 +// --|-------|----o----|-------|--> +// <----> +// dead +// out: -1 0 0 1 + +float Neutral(float value, float dead) +{ + if ( Abs(value) <= dead ) + { + return 0.0f; + } + else + { + if ( value > 0.0f ) return (value-dead)/(1.0f-dead); + else return (value+dead)/(1.0f-dead); + } +} + + +// Calcule une valeur (radians) proportionnelle comprise +// entre a et b (degrés). + +inline float Prop(int a, int b, float p) +{ + float aa, bb; + + aa = (float)a*PI/180.0f; + bb = (float)b*PI/180.0f; + + return aa+p*(bb-aa); +} + +// Fait progresser mollement une valeur souhaitée à partir de +// sa valeur actuelle. Plus le temps est grand et plus la +// progression est rapide. + +float Smooth(float actual, float hope, float time) +{ + float futur; + + futur = actual + (hope-actual)*time; + + if ( hope > actual ) + { + if ( futur > hope ) futur = hope; + } + if ( hope < actual ) + { + if ( futur < hope ) futur = hope; + } + + return futur; +} + + +// Fait reboudir un mouvement quelconque. + +// out +// | +// 1+------o-------o--- +// | o | o o | | bounce +// | o | o---|--- +// | o | | +// | o | | +// -o------|-------+----> progress +// 0| | 1 +// |<---->|middle + +float Bounce(float progress, float middle, float bounce) +{ + if ( progress < middle ) + { + progress = progress/middle; // 0..1 + return 0.5f+sinf(progress*PI-PI/2.0f)/2.0f; + } + else + { + progress = (progress-middle)/(1.0f-middle); // 0..1 + return (1.0f-bounce/2.0f)+sinf((0.5f+progress*2.0f)*PI)*(bounce/2.0f); + } +} + + +// Retourne la couleur D3DCOLOR correspondante. + +D3DCOLOR RetColor(float intensity) +{ + D3DCOLOR color; + + if ( intensity <= 0.0f ) return 0x00000000; + if ( intensity >= 1.0f ) return 0xffffffff; + + color = (int)(intensity*255.0f)<<24; + color |= (int)(intensity*255.0f)<<16; + color |= (int)(intensity*255.0f)<<8; + color |= (int)(intensity*255.0f); + + return color; +} + +// Retourne la couleur D3DCOLOR correspondante. + +D3DCOLOR RetColor(D3DCOLORVALUE intensity) +{ + D3DCOLOR color; + + color = (int)(intensity.a*255.0f)<<24; + color |= (int)(intensity.r*255.0f)<<16; + color |= (int)(intensity.g*255.0f)<<8; + color |= (int)(intensity.b*255.0f); + + return color; +} + +// Retourne la couleur D3DCOLORVALUE correspondante. + +D3DCOLORVALUE RetColor(D3DCOLOR intensity) +{ + D3DCOLORVALUE color; + + color.r = (float)((intensity>>16)&0xff)/256.0f; + color.g = (float)((intensity>>8 )&0xff)/256.0f; + color.b = (float)((intensity>>0 )&0xff)/256.0f; + color.a = (float)((intensity>>24)&0xff)/256.0f; + + return color; +} + + +// Conversion RGB vers HSV. + +void RGB2HSV(D3DCOLORVALUE src, ColorHSV &dest) +{ + float min, max, delta; + + min = Min(src.r, src.g, src.b); + max = Max(src.r, src.g, src.b); + + dest.v = max; // intensité + + if ( max == 0.0f ) + { + dest.s = 0.0f; // saturation + dest.h = 0.0f; // teinte indéfinie ! + } + else + { + delta = max-min; + dest.s = delta/max; // saturation + + if ( src.r == max ) // between yellow & magenta + { + dest.h = (src.g-src.b)/delta; + } + else if ( src.g == max ) // between cyan & yellow + { + dest.h = 2.0f+(src.b-src.r)/delta; + } + else // between magenta & cyan + { + dest.h = 4.0f+(src.r-src.g)/delta; + } + + dest.h *= 60.0f; // en degrés + if ( dest.h < 0.0f ) dest.h += 360.0f; + dest.h /= 360.0f; // 0..1 + } +} + +// Conversion HSV vers RGB. + +void HSV2RGB(ColorHSV src, D3DCOLORVALUE &dest) +{ + int i; + float f,v,p,q,t; + + src.h = Norm(src.h)*360.0f; + src.s = Norm(src.s); + src.v = Norm(src.v); + + if ( src.s == 0.0f ) // saturation nulle ? + { + dest.r = src.v; + dest.g = src.v; + dest.b = src.v; // gris + } + else + { + if ( src.h == 360.0f ) src.h = 0.0f; + src.h /= 60.0f; + i = (int)src.h; // partie entière (0..5) + f = src.h-i; // partie fractionnaire + + v = src.v; + p = src.v*(1.0f-src.s); + q = src.v*(1.0f-(src.s*f)); + t = src.v*(1.0f-(src.s*(1.0f-f))); + + switch (i) + { + case 0: dest.r=v; dest.g=t; dest.b=p; break; + case 1: dest.r=q; dest.g=v; dest.b=p; break; + case 2: dest.r=p; dest.g=v; dest.b=t; break; + case 3: dest.r=p; dest.g=q; dest.b=v; break; + case 4: dest.r=t; dest.g=p; dest.b=v; break; + case 5: dest.r=v; dest.g=p; dest.b=q; break; + } + } +} + diff --git a/src/math3d.h b/src/math3d.h new file mode 100644 index 00000000..083e4184 --- /dev/null +++ b/src/math3d.h @@ -0,0 +1,90 @@ +// math3d.h + +#ifndef _MATH3D_H_ +#define _MATH3D_H_ + + +#define STRICT +#define D3D_OVERLOADS +#include + + +#define PI 3.14159265358979323846f +#define CHOUIA 1e-6f +#define BEAUCOUP 1e6f + + + +extern BOOL IsEqual(float a, float b); + +extern float Min(float a, float b); +extern float Min(float a, float b, float c); +extern float Min(float a, float b, float c, float d); +extern float Min(float a, float b, float c, float d, float e); + +extern float Max(float a, float b); +extern float Max(float a, float b, float c); +extern float Max(float a, float b, float c, float d); +extern float Max(float a, float b, float c, float d, float e); + +extern float Norm(float a); +extern float Abs(float a); + +extern void Swap(int &a, int &b); +extern void Swap(float &a, float &b); +extern void Swap(FPOINT &a, FPOINT &b); + +extern float Mod(float a, float m); +extern float NormAngle(float angle); +extern BOOL TestAngle(float angle, float min, float max); + +extern float Direction(float a, float g); +extern FPOINT RotatePoint(FPOINT center, float angle, FPOINT p); +extern FPOINT RotatePoint(float angle, FPOINT p); +extern FPOINT RotatePoint(float angle, float dist); +extern float RotateAngle(float x, float y); +extern float RotateAngle(FPOINT center, FPOINT p1, FPOINT p2); +extern float MidPoint(FPOINT a, FPOINT b, float px); +extern D3DVECTOR SegmentDist(const D3DVECTOR &p1, const D3DVECTOR &p2, float dist); +extern BOOL IsInsideTriangle(FPOINT a, FPOINT b, FPOINT c, FPOINT p); +extern BOOL Intersect(D3DVECTOR a, D3DVECTOR b, D3DVECTOR c, D3DVECTOR d, D3DVECTOR e, D3DVECTOR &i); +extern BOOL IntersectY(D3DVECTOR a, D3DVECTOR b, D3DVECTOR c, D3DVECTOR &p); +extern void RotatePoint(float cx, float cy, float angle, float &px, float &py); +extern void RotatePoint(D3DVECTOR center, float angleH, float angleV, D3DVECTOR &p); +extern void RotatePoint2(D3DVECTOR center, float angleH, float angleV, D3DVECTOR &p); +extern D3DVECTOR RotateView(D3DVECTOR center, float angleH, float angleV, float dist); +extern D3DVECTOR LookatPoint( D3DVECTOR eye, float angleH, float angleV, float length ); +extern float Length(FPOINT a, FPOINT b); +extern float Length(float x, float y); +extern float Length(const D3DVECTOR &u); +extern float Length(const D3DVECTOR &a, const D3DVECTOR &b); +extern float Length2d(const D3DVECTOR &a, const D3DVECTOR &b); +extern float Angle( D3DVECTOR u, D3DVECTOR v ); +extern D3DVECTOR Cross( D3DVECTOR u, D3DVECTOR v ); +extern D3DVECTOR ComputeNormal( D3DVECTOR p1, D3DVECTOR p2, D3DVECTOR p3 ); +extern D3DVECTOR Transform(const D3DMATRIX &m, D3DVECTOR p); +extern D3DVECTOR Projection(const D3DVECTOR &a, const D3DVECTOR &b, const D3DVECTOR &p); + +extern void MappingObject( D3DVERTEX2* pVertices, int nb, float scale ); +extern void SmoothObject( D3DVERTEX2* pVertices, int nb ); +extern BOOL LineFunction(FPOINT p1, FPOINT p2, float &a, float &b); +extern float DistancePlanPoint(const D3DVECTOR &a, const D3DVECTOR &b, const D3DVECTOR &c, const D3DVECTOR &p); +extern BOOL IsSamePlane(D3DVECTOR *plan1, D3DVECTOR *plan2); +extern void MatRotateXZY(D3DMATRIX &mat, D3DVECTOR angle); +extern void MatRotateZXY(D3DMATRIX &mat, D3DVECTOR angle); + +extern float Rand(); +extern float Neutral(float value, float dead); + +extern float Prop(int a, int b, float p); +extern float Smooth(float actual, float hope, float time); +extern float Bounce(float progress, float middle=0.3f, float bounce=0.4f); + +extern D3DCOLOR RetColor(float intensity); +extern D3DCOLOR RetColor(D3DCOLORVALUE intensity); +extern D3DCOLORVALUE RetColor(D3DCOLOR intensity); + +extern void RGB2HSV(D3DCOLORVALUE src, ColorHSV &dest); +extern void HSV2RGB(ColorHSV src, D3DCOLORVALUE &dest); + +#endif //_MATH3D_H_ diff --git a/src/metafile.cpp b/src/metafile.cpp new file mode 100644 index 00000000..7cc9cfda --- /dev/null +++ b/src/metafile.cpp @@ -0,0 +1,403 @@ +// metafile.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include + +#include "language.h" +#include "metafile.h" + + + + +#if _FULL | _NET +static unsigned char table_codec[23] = +{ + 0x85, 0x91, 0x73, 0xcf, 0xa2, 0xbb, 0xf4, 0x77, + 0x58, 0x39, 0x37, 0xfd, 0x2a, 0xcc, 0x5f, 0x55, + 0x96, 0x90, 0x07, 0xcd, 0x11, 0x88, 0x21, +}; + +void Codec(void* buffer, int len, int start) +{ + unsigned char *b = (unsigned char*)buffer; + int i; + + for ( i=0 ; i SURFACE\c; +\tab;Température: 25.4 degrés +\tab;Atmosphère: oxygène, azote, ammoniaque +\tab;Vent: 0.7 m/s +\tab;Minerai titanium: aucun +\tab;Minerai uranium: aucun + +\s;-> SOUS-SOL\c; +\tab;Energie: aucune +\tab;Minerai titanium: aucun +\tab;Minerai uranium: aucun diff --git a/src/misc.cpp b/src/misc.cpp new file mode 100644 index 00000000..d9151f03 --- /dev/null +++ b/src/misc.cpp @@ -0,0 +1,425 @@ +// misc.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "D3DUtil.h" +#include "language.h" +#include "event.h" +#include "misc.h" + + + +CMetaFile g_metafile; + +static EventMsg g_uniqueEventMsg = EVENT_USER; +static BOOL g_bUserDir = FALSE; +static char g_userDir[100] = ""; + + + +// Donne un événement utilisateur unique. + +EventMsg GetUniqueEventMsg() +{ + int i; + + i = (int)g_uniqueEventMsg+1; + g_uniqueEventMsg = (EventMsg)i; + return g_uniqueEventMsg; +} + + + +// Retourne une lettre non accentuée. + +char RetNoAccent(char letter) +{ + if ( letter < 0 ) + { + if ( letter == 'á' || + letter == 'à' || + letter == 'â' || + letter == 'ä' || + letter == 'ã' ) return 'a'; + + if ( letter == 'é' || + letter == 'è' || + letter == 'ê' || + letter == 'ë' ) return 'e'; + + if ( letter == 'í' || + letter == 'ì' || + letter == 'î' || + letter == 'ï' ) return 'i'; + + if ( letter == 'ó' || + letter == 'ò' || + letter == 'ô' || + letter == 'ö' || + letter == 'õ' ) return 'o'; + + if ( letter == 'ú' || + letter == 'ù' || + letter == 'û' || + letter == 'ü' ) return 'u'; + + if ( letter == 'ç' ) return 'c'; + + if ( letter == 'ñ' ) return 'n'; + + if ( letter == 'Á' || + letter == 'À' || + letter == 'Â' || + letter == 'Ä' || + letter == 'Ã' ) return 'A'; + + if ( letter == 'É' || + letter == 'È' || + letter == 'Ê' || + letter == 'Ë' ) return 'E'; + + if ( letter == 'Í' || + letter == 'Ì' || + letter == 'Î' || + letter == 'Ï' ) return 'I'; + + if ( letter == 'Ó' || + letter == 'Ò' || + letter == 'Ô' || + letter == 'Ö' || + letter == 'Õ' ) return 'O'; + + if ( letter == 'Ú' || + letter == 'Ù' || + letter == 'Û' || + letter == 'Ü' ) return 'U'; + + if ( letter == 'Ç' ) return 'C'; + + if ( letter == 'Ñ' ) return 'N'; + } + + return letter; +} + +// Retourne une lettre majuscule. + +char RetToUpper(char letter) +{ + if ( letter < 0 ) + { + if ( letter == 'á' ) return 'Á'; + if ( letter == 'à' ) return 'À'; + if ( letter == 'â' ) return 'Â'; + if ( letter == 'ä' ) return 'Ä'; + if ( letter == 'ã' ) return 'Ã'; + + if ( letter == 'é' ) return 'É'; + if ( letter == 'è' ) return 'È'; + if ( letter == 'ê' ) return 'Ê'; + if ( letter == 'ë' ) return 'Ë'; + + if ( letter == 'í' ) return 'Í'; + if ( letter == 'ì' ) return 'Ì'; + if ( letter == 'î' ) return 'Î'; + if ( letter == 'ï' ) return 'Ï'; + + if ( letter == 'ó' ) return 'Ó'; + if ( letter == 'ò' ) return 'Ò'; + if ( letter == 'ô' ) return 'Ô'; + if ( letter == 'ö' ) return 'Ö'; + if ( letter == 'õ' ) return 'Õ'; + + if ( letter == 'ú' ) return 'Ú'; + if ( letter == 'ù' ) return 'Ù'; + if ( letter == 'û' ) return 'Û'; + if ( letter == 'ü' ) return 'Ü'; + + if ( letter == 'ç' ) return 'Ç'; + + if ( letter == 'ñ' ) return 'Ñ'; + } + + return toupper(letter); +} + +// Retourne une lettre minuscule. + +char RetToLower(char letter) +{ + if ( letter < 0 ) + { + if ( letter == 'Á' ) return 'á'; + if ( letter == 'À' ) return 'à'; + if ( letter == 'Â' ) return 'â'; + if ( letter == 'Ä' ) return 'ä'; + if ( letter == 'Ã' ) return 'ã'; + + if ( letter == 'É' ) return 'é'; + if ( letter == 'È' ) return 'è'; + if ( letter == 'Ê' ) return 'ê'; + if ( letter == 'Ë' ) return 'ë'; + + if ( letter == 'Í' ) return 'í'; + if ( letter == 'Ì' ) return 'ì'; + if ( letter == 'Î' ) return 'î'; + if ( letter == 'Ï' ) return 'ï'; + + if ( letter == 'Ó' ) return 'ó'; + if ( letter == 'Ò' ) return 'ò'; + if ( letter == 'Ô' ) return 'ô'; + if ( letter == 'Ö' ) return 'ö'; + if ( letter == 'Õ' ) return 'õ'; + + if ( letter == 'Ú' ) return 'ú'; + if ( letter == 'Ù' ) return 'ù'; + if ( letter == 'Û' ) return 'û'; + if ( letter == 'Ü' ) return 'ü'; + + if ( letter == 'Ç' ) return 'ç'; + + if ( letter == 'Ñ' ) return 'ñ'; + } + + return tolower(letter); +} + + +// Conversion du temps en chaîne. + +void TimeToAscii(time_t time, char *buffer) +{ + struct tm when; + int year; + + when = *localtime(&time); + year = when.tm_year+1900; + if ( year < 2000 ) year -= 1900; + else year -= 2000; +#if _FRENCH + sprintf(buffer, "%.2d.%.2d.%.2d %.2d:%.2d", + when.tm_mday, when.tm_mon+1, year, + when.tm_hour, when.tm_min); +#endif +#if _GERMAN | _WG + sprintf(buffer, "%.2d.%.2d.%.2d %.2d:%.2d", + when.tm_mday, when.tm_mon+1, year, + when.tm_hour, when.tm_min); +#endif +#if _ENGLISH + char format[10]; + int hour; + + hour = when.tm_hour; // 0..23 + if ( hour < 12 ) // matin ? + { + strcpy(format, "am"); + } + else // après-midi ? + { + strcpy(format, "pm"); + hour -= 12; // 0..11 + } + if ( hour == 0 ) hour = 12; + + sprintf(buffer, "%.2d.%.2d.%.2d %.2d:%.2d %s", + when.tm_mon+1, when.tm_mday, year, + hour, when.tm_min, format); +#endif +#if _POLISH + sprintf(buffer, "%.2d.%.2d.%.2d %.2d:%.2d", + when.tm_mday, when.tm_mon+1, year, + when.tm_hour, when.tm_min); +#endif +} + + +// Effectue une copie d'un fichier. + +BOOL Xfer(char* src, char* dst) +{ + FILE *fs, *fd; + char *buffer; + int len; + + fs = fopen(src, "rb"); + if ( fs == 0 ) + { + return FALSE; + } + + fd = fopen(dst, "wb"); + if ( fd == 0 ) + { + fclose(fs); + return FALSE; + } + + buffer = (char*)malloc(10000); + + while ( TRUE ) + { + len = fread(buffer, 1, 10000, fs); + if ( len == 0 ) break; + fwrite(buffer, 1, len, fd); + } + + free(buffer); + fclose(fs); + fclose(fd); + return TRUE; +} + +// Copie un fichier dans le dossier temporaire. + +BOOL CopyFileToTemp(char* filename) +{ + char src[100]; + char dst[100]; + char save[100]; + + UserDir(src, filename, "textures"); + + strcpy(save, g_userDir); + strcpy(g_userDir, "temp"); + UserDir(dst, filename, "textures"); + strcpy(g_userDir, save); + + _mkdir("temp"); + if ( !Xfer(src, dst) ) return FALSE; + + strcpy(filename, dst); + return TRUE; +} + +// Copie une liste de fichiers numérotés dans le dossier temporaire. + +BOOL CopyFileListToTemp(char* filename, int* list, int total) +{ + char name[100]; + char ext[10]; + char file[100]; + char save[100]; + char* p; + int i; + + strcpy(name, filename); + p = strchr(name, '.'); + if ( p == 0 ) + { + strcpy(ext, ".tga"); + } + else + { + strcpy(ext, p); + *p = 0; + } + + for ( i=0 ; i +#include "metafile.h" + + +extern CMetaFile g_metafile; + + + +// Classes existantes. + +enum ClassType +{ + CLASS_EVENT = 1, + CLASS_INTERFACE = 2, + CLASS_MAIN = 3, + CLASS_ENGINE = 4, + CLASS_TERRAIN = 5, + CLASS_OBJECT = 6, + CLASS_PHYSICS = 7, + CLASS_BRAIN = 8, + CLASS_CAMERA = 9, + CLASS_LIGHT = 10, + CLASS_PARTICULE = 11, + CLASS_AUTO = 12, + CLASS_DISPLAYTEXT = 13, + CLASS_PYRO = 14, + CLASS_SCRIPT = 15, + CLASS_TEXT = 16, + CLASS_STUDIO = 17, + CLASS_WATER = 18, + CLASS_CLOUD = 19, + CLASS_MOTION = 20, + CLASS_SOUND = 21, + CLASS_PLANET = 22, + CLASS_TASKMANAGER = 23, + CLASS_DIALOG = 24, + CLASS_MAP = 25, + CLASS_SHORT = 26, + CLASS_BLITZ = 27, +}; + +#define CLASS_MAX 30 + + + +enum Error +{ + ERR_OK = 0, // ok + ERR_GENERIC = 1, // erreur quelconque + ERR_CONTINUE = 2, // continue + ERR_STOP = 3, // stoppe + ERR_CMD = 4, // commande inconnue + ERR_INSTALL = 20, // programme mal installé + ERR_NOCD = 21, // CD pas trouvé + ERR_MANIP_VEH = 100, // véhicule inadapté + ERR_MANIP_FLY = 101, // impossible en vol + ERR_MANIP_BUSY = 102, // prend: porte déjà qq chose + ERR_MANIP_NIL = 103, // prend: rien à prendre + ERR_MANIP_MOTOR = 105, // dépose: impossible en mouvement + ERR_MANIP_OCC = 106, // dépose: emplacement déjà occupé + ERR_MANIP_FRIEND = 107, // pas d'autre véhicule + ERR_MANIP_RADIO = 108, // impossible car radioactif + ERR_MANIP_WATER = 109, // impossible sous l'eau + ERR_MANIP_EMPTY = 110, // rien à déposer + ERR_BUILD_FLY = 120, // impossible en vol + ERR_BUILD_WATER = 121, // impossible sous l'eau + ERR_BUILD_ENERGY = 122, // pas assez d'énergie + ERR_BUILD_METALAWAY = 123, // pas de métal (trop loin) + ERR_BUILD_METALNEAR = 124, // pas de métal (trop proche) + ERR_BUILD_METALINEX = 125, // métal inexistant + ERR_BUILD_FLAT = 126, // sol pas assez plat + ERR_BUILD_FLATLIT = 127, // sol plat pas assez grand + ERR_BUILD_BUSY = 128, // enplacement occupé + ERR_BUILD_BASE = 129, // trop proche de la fusée + ERR_BUILD_NARROW = 130, // bâtiments trop serrés + ERR_BUILD_MOTOR = 131, // construit: impossible en mouvement + ERR_SEARCH_FLY = 140, // impossible en vol + ERR_SEARCH_VEH = 141, // véhicule inadapté + ERR_SEARCH_MOTOR = 142, // impossible en mouvement + ERR_TERRA_VEH = 150, // véhicule inadapté + ERR_TERRA_ENERGY = 151, // pas assez d'énergie + ERR_TERRA_FLOOR = 152, // terrain inadapté + ERR_TERRA_BUILDING = 153, // batiment trop proche + ERR_TERRA_OBJECT = 154, // object trop proche + ERR_FIRE_VEH = 160, // véhicule inadapté + ERR_FIRE_ENERGY = 161, // pas assez d'énergie + ERR_FIRE_FLY = 162, // impossible en vol + ERR_RECOVER_VEH = 170, // véhicule inadapté + ERR_RECOVER_ENERGY = 171, // pas assez d'énergie + ERR_RECOVER_NULL = 172, // pas de ruine + ERR_CONVERT_EMPTY = 180, // pas de pierre à transformer + ERR_SHIELD_VEH = 190, // véhicule inadapté + ERR_SHIELD_ENERGY = 191, // pas assez d'énergie + ERR_MOVE_IMPOSSIBLE = 200, // move impossible + ERR_FIND_IMPOSSIBLE = 201, // find impossible + ERR_GOTO_IMPOSSIBLE = 210, // goto impossible + ERR_GOTO_ITER = 211, // goto trop compliqué + ERR_GOTO_BUSY = 212, // destination goto occupée + ERR_DERRICK_NULL = 300, // pas de minerai en sous-sol + ERR_STATION_NULL = 301, // pas d'énergie en sous-sol + ERR_TOWER_POWER = 310, // pas de pile + ERR_TOWER_ENERGY = 311, // plus d'énergie + ERR_RESEARCH_POWER = 320, // pas de pile + ERR_RESEARCH_ENERGY = 321, // plus d'énergie + ERR_RESEARCH_TYPE = 322, // pas le bon type de pile + ERR_RESEARCH_ALREADY= 323, // recherche déjà faîte + ERR_ENERGY_NULL = 330, // pas d'énergie en sous-sol + ERR_ENERGY_LOW = 331, // pas encore assez d'énergie + ERR_ENERGY_EMPTY = 332, // pas de métal à transformer + ERR_ENERGY_BAD = 333, // ne transforme que le métal + ERR_BASE_DLOCK = 340, // portes bloquées + ERR_BASE_DHUMAN = 341, // vous devez embarquer + ERR_LABO_NULL = 350, // rien à analyser + ERR_LABO_BAD = 351, // pas de boulet à analyser + ERR_LABO_ALREADY = 352, // analyse déjà faîte + ERR_NUCLEAR_NULL = 360, // pas d'énergie en sous-sol + ERR_NUCLEAR_LOW = 361, // pas encore assez d'énergie + ERR_NUCLEAR_EMPTY = 362, // pas d'uranium à transformer + ERR_NUCLEAR_BAD = 363, // ne transforme que l'uranium + ERR_FACTORY_NULL = 370, // pas de métal + ERR_FACTORY_NEAR = 371, // véhicule trop proche + ERR_RESET_NEAR = 380, // véhicule trop proche + ERR_INFO_NULL = 390, // pas de borne d'information + ERR_VEH_VIRUS = 400, // véhicule infecté par un virus + ERR_BAT_VIRUS = 401, // bâtiment infecté par un virus + ERR_VEH_POWER = 500, // pas de pile + ERR_VEH_ENERGY = 501, // plus d'énergie + ERR_FLAG_FLY = 510, // impossible en vol + ERR_FLAG_WATER = 511, // impossible en nageant + ERR_FLAG_MOTOR = 512, // impossible en mouvement + ERR_FLAG_BUSY = 513, // prend: porte déjà qq chose + ERR_FLAG_CREATE = 514, // trop d'indicateurs + ERR_FLAG_PROXY = 515, // trop proche + ERR_FLAG_DELETE = 516, // rien à supprimer + ERR_MISSION_NOTERM = 600, // mission pas terminée + ERR_DELETEMOBILE = 700, // véhicule détruit + ERR_DELETEBUILDING = 701, // bâtiment détruit + ERR_TOOMANY = 702, // trop d'objets + ERR_OBLIGATORYTOKEN = 800, // instruction obligatoire manquante + ERR_PROHIBITEDTOKEN = 801, // instruction interdite + + INFO_FIRST = 10000, // première information + INFO_BUILD = 10001, // construction terminée + INFO_CONVERT = 10002, // métal disponible + INFO_RESEARCH = 10003, // recherche terminée + INFO_FACTORY = 10004, // véhicule fabriqué + INFO_LABO = 10005, // analyse terminée + INFO_ENERGY = 10006, // pile disponible + INFO_NUCLEAR = 10007, // pile nucléaire disponible + INFO_FINDING = 10008, // pile nucléaire disponible + INFO_MARKPOWER = 10020, // emplacement pour station trouvé + INFO_MARKURANIUM = 10021, // emplacement pour derrick trouvé + INFO_MARKSTONE = 10022, // emplacement pour derrick trouvé + INFO_MARKKEYa = 10023, // emplacement pour derrick trouvé + INFO_MARKKEYb = 10024, // emplacement pour derrick trouvé + INFO_MARKKEYc = 10025, // emplacement pour derrick trouvé + INFO_MARKKEYd = 10026, // emplacement pour derrick trouvé + INFO_RESEARCHTANK = 10030, // recherche terminée + INFO_RESEARCHFLY = 10031, // recherche terminée + INFO_RESEARCHTHUMP = 10032, // recherche terminée + INFO_RESEARCHCANON = 10033, // recherche terminée + INFO_RESEARCHTOWER = 10034, // recherche terminée + INFO_RESEARCHPHAZER = 10035, // recherche terminée + INFO_RESEARCHSHIELD = 10036, // recherche terminée + INFO_RESEARCHATOMIC = 10037, // recherche terminée + INFO_WIN = 10040, // gagné + INFO_LOST = 10041, // perdu + INFO_LOSTq = 10042, // perdu immédiatement + INFO_WRITEOK = 10043, // enregistrement effectué + INFO_DELETEPATH = 10050, // marque chemin supprimée + INFO_DELETEMOTHER = 10100, // insecte tué + INFO_DELETEANT = 10101, // insecte tué + INFO_DELETEBEE = 10102, // insecte tué + INFO_DELETEWORM = 10103, // insecte tué + INFO_DELETESPIDER = 10104, // insecte tué + INFO_BEGINSATCOM = 10105, // utilisez votre SatCom +}; + + +// Etat du clavier. + +#define KS_PAGEUP (1<<4) +#define KS_PAGEDOWN (1<<5) +#define KS_SHIFT (1<<6) +#define KS_CONTROL (1<<7) +#define KS_MLEFT (1<<8) +#define KS_MRIGHT (1<<9) +#define KS_NUMUP (1<<10) +#define KS_NUMDOWN (1<<11) +#define KS_NUMLEFT (1<<12) +#define KS_NUMRIGHT (1<<13) +#define KS_NUMPLUS (1<<14) +#define KS_NUMMINUS (1<<15) + + +// Procédures. + +enum EventMsg; + +extern EventMsg GetUniqueEventMsg(); + +extern char RetNoAccent(char letter); +extern char RetToUpper(char letter); +extern char RetToLower(char letter); + +extern void TimeToAscii(time_t time, char *buffer); + +extern BOOL CopyFileToTemp(char* filename); +extern BOOL CopyFileListToTemp(char* filename, int* list, int total); +extern void AddExt(char* filename, char* ext); +extern void UserDir(BOOL bUser, char* dir); +extern void UserDir(char* buffer, char* dir, char* def); + +extern char RetLanguageLetter(); + + + +#endif //_MISC_H_ diff --git a/src/mixer.txt b/src/mixer.txt new file mode 100644 index 00000000..2009c2a0 --- /dev/null +++ b/src/mixer.txt @@ -0,0 +1,486 @@ +Several people have asked me for the sample code I use to program +the mixer in Windows. Since it's a fairly short sample and there's +clearly some interest, I thought I'd post it directly to the newsgroup. +Here it is ... enjoy! (?) + +Julian + +// Example routine that manipulates the mixer controls for Win32 +// This code is not a stand-alone application ... +// +// It's also not very pretty ... +// +// But then, neither is the API ... +// +// Julian Bunn, 1998, julianb@altavista.net + + +#include +#include + + +MIXERCONTROLDETAILS mixDetailsMic,mixDetailsSpk,mixDetailsLin; +LONG +lMaximumSpk,lMaximumMic,lMaximumLin,lMinimumMic,lMinimumSpk,lMinimumLin; + +int nMixerDevs; +int nMicMixID; + +LPHMIXER hMixer; +UINT IdMixer; + + +/**************************************************************************** + + Function: ProgramInitMixer() + + PURPOSE : Initialises the mixer +*****************************************************************************/ + +LONG WINAPI ProgramInitMixer() +{ + UINT iS,iD,iDC,iC, itype; + UINT volume; + MMRESULT mmres; + MIXERCAPS mixCaps; + MIXERLINE mixLine; + MIXERLINECONTROLS mixControls; + MIXERCONTROL mixClist[50]; + MIXERCONTROLDETAILS mixDetails; + MIXERCONTROLDETAILS_UNSIGNED mixValue; + MIXERCONTROLDETAILS_BOOLEAN mixMute; + MIXERCONTROLDETAILS_BOOLEAN mixBoolean[50]; + MIXERCONTROLDETAILS_LISTTEXT mixList[50]; + BOOL bDoneMike = FALSE; + BOOL bDoneSpkr = FALSE; + UINT LineID = 0; + + if(!bMixerOpened) { +// check first if we have a mixer + if((nMixerDevs = mixerGetNumDevs()) < 1) { + return (Program_ERROR); + } + +// really need to pop up a chooser for which mixer device, in +// cases where there is more than one. +// In the meantime, I select the last one listed + + IdMixer = nMixerDevs-1; + + mmres = mixerOpen((LPHMIXER) &hMixer, IdMixer, (DWORD) 0, (DWORD) +NULL, + MIXER_OBJECTF_MIXER); + if(mmres != MMSYSERR_NOERROR) { + return (Program_ERROR); + } + + bMixerOpened = TRUE; + } + + mmres = mixerGetDevCaps(IdMixer, (LPMIXERCAPS) &mixCaps, +sizeof(MIXERCAPS)); + if(mmres != MMSYSERR_NOERROR) { + return (Program_ERROR); + } +// Set the manufacturer's name for the mixer ... + SetDlgItemText(hWndDialogBox,IDC_MIXERNAME,mixCaps.szPname); + if(nMixerDevs>1) { + DialogBox(g_hInstance,MAKEINTRESOURCE(IDD_INFOMESSAGE1), +hWndDialogBox, DialogBoxCallback); + } + + +// Loop over the destination mixer lines + for (iD=0;iD +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "water.h" +#include "robotmain.h" +#include "interface.h" +#include "edit.h" +#include "button.h" +#include "cmdtoken.h" +#include "modfile.h" +#include "model.h" + + + +#define MAX_COLORS 9 + +static float table_color[MAX_COLORS*3] = +{ + 1.0f, 1.0f, 1.0f, // blanc + 1.0f, 0.0f, 0.0f, // rouge + 0.0f, 1.0f, 0.0f, // vert + 0.0f, 0.6f, 1.0f, // bleu + 1.0f, 1.0f, 0.0f, // jaune + 0.0f, 1.0f, 1.0f, // cyan + 1.0f, 0.0f, 1.0f, // magenta + 0.3f, 0.3f, 0.3f, // gris + 0.0f, 0.0f, 0.0f, // noir +}; + + +#define MAX_STATES 10 + +static int table_state[MAX_STATES] = +{ + D3DSTATENORMAL, + D3DSTATEPART1, + D3DSTATEPART2, + D3DSTATEPART3, + D3DSTATEPART4, + D3DSTATE2FACE, // #5 + D3DSTATETTw, + D3DSTATETTb, + D3DSTATETTw|D3DSTATE2FACE, // #8 + D3DSTATETTb|D3DSTATE2FACE, // #9 +}; + + +#define MAX_NAMES 23 + + + + +// Constructeur de l'objet. + +CModel::CModel(CInstanceManager* iMan) +{ + m_iMan = iMan; + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_interface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + + m_modFile = new CModFile(m_iMan); + m_triangleTable = m_modFile->RetTriangleList(); + + m_textureRank = 0; + strcpy(m_textureName, "lemt.tga"); + m_color = 0; + m_state = 0; + m_textureMode = 0; + m_textureRotate = 0; + m_bTextureMirrorX = FALSE; + m_bTextureMirrorY = FALSE; + m_texturePart = 0; + TexturePartUpdate(); + + m_bDisplayTransparent = FALSE; + m_bDisplayOnlySelection = FALSE; + InitView(); + + m_triangleSel1 = 0; + m_triangleSel2 = 0; + + m_mode = 1; + m_oper = 'P'; + + m_secondTexNum = 0; + m_secondSubdiv = 1; + m_secondOffsetU = 0; + m_secondOffsetV = 0; + + m_min = 0.0f; + m_max = 1000000.0f; +} + +// Destructeur de l'objet. + +CModel::~CModel() +{ + delete m_modFile; +} + + +// Il faut appeler cette procédure avant de modifier interactivement +// le modèle. + +void CModel::StartUserAction() +{ + Event event; + FPOINT pos, dim; + CButton* pb; + + dim.x = 105.0f/640.0f; + dim.y = 18.0f/480.0f; + pos.x = 10.0f/640.0f; + pos.y = 450.0f/480.0f; + m_interface->CreateEdit(pos, dim, 0, EVENT_EDIT1); + + dim.x = 50.0f/640.0f; + pos.x = 125.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON1); + pb->SetState(STATE_SIMPLY); + pb->SetName("Load"); + pos.x = 185.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON2); + pb->SetState(STATE_SIMPLY); + pb->SetName("Script"); + pos.x = 245.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON3); + pb->SetState(STATE_SIMPLY); + pb->SetName("Read"); + pos.x = 305.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON4); + pb->SetState(STATE_SIMPLY); + pb->SetName("Add"); + pos.x = 365.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON5); + pb->SetState(STATE_SIMPLY); + pb->SetName("Write"); + + dim.x = 50.0f/640.0f; + dim.y = 18.0f/480.0f; + pos.x = 10.0f/640.0f; + pos.y = 425.0f/480.0f; + m_interface->CreateEdit(pos, dim, 0, EVENT_EDIT2); + pos.x = 65.0f/640.0f; + pos.y = 425.0f/480.0f; + m_interface->CreateEdit(pos, dim, 0, EVENT_EDIT3); + pos.x = 10.0f/640.0f; + pos.y = 400.0f/480.0f; + m_interface->CreateEdit(pos, dim, 0, EVENT_EDIT4); + pos.x = 65.0f/640.0f; + pos.y = 400.0f/480.0f; + m_interface->CreateEdit(pos, dim, 0, EVENT_EDIT5); + + dim.x = 20.0f/640.0f; + dim.y = 20.0f/480.0f; + pos.y = 370.0f/480.0f; + pos.x = 10.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON10); + pb->SetState(STATE_SIMPLY); + pb->SetName("P"); + pos.x = 30.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON11); + pb->SetState(STATE_SIMPLY); + pb->SetName("R"); + pos.x = 50.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON12); + pb->SetState(STATE_SIMPLY); + pb->SetName("Z"); + pos.y = 350.0f/480.0f; + pos.x = 10.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON13); + pb->SetState(STATE_SIMPLY); + pb->SetName("+X"); + pos.x = 30.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON14); + pb->SetState(STATE_SIMPLY); + pb->SetName("+Y"); + pos.x = 50.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON15); + pb->SetState(STATE_SIMPLY); + pb->SetName("+Z"); + pos.y = 330.0f/480.0f; + pos.x = 10.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON16); + pb->SetState(STATE_SIMPLY); + pb->SetName("-X"); + pos.x = 30.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON17); + pb->SetState(STATE_SIMPLY); + pb->SetName("-Y"); + pos.x = 50.0f/640.0f; + pb = m_interface->CreateButton(pos, dim, 0, EVENT_BUTTON18); + pb->SetState(STATE_SIMPLY); + pb->SetName("-Z"); + +//? m_modFile->ReadModel("objects\\io.mod"); + DeselectAll(); + CurrentInit(); + + ZeroMemory(&event, sizeof(Event)); + EventFrame(event); + + m_engine->LoadAllTexture(); + UpdateInfoText(); +} + +// Il faut appeler cette procédure après avoir modifié interactivement +// le modèle. + +void CModel::StopUserAction() +{ + m_interface->DeleteControl(EVENT_EDIT1); + m_interface->DeleteControl(EVENT_EDIT2); + m_interface->DeleteControl(EVENT_EDIT3); + m_interface->DeleteControl(EVENT_EDIT4); + m_interface->DeleteControl(EVENT_EDIT5); + m_interface->DeleteControl(EVENT_BUTTON1); + m_interface->DeleteControl(EVENT_BUTTON2); + m_interface->DeleteControl(EVENT_BUTTON3); + m_interface->DeleteControl(EVENT_BUTTON4); + m_interface->DeleteControl(EVENT_BUTTON5); + m_interface->DeleteControl(EVENT_BUTTON10); + m_interface->DeleteControl(EVENT_BUTTON11); + m_interface->DeleteControl(EVENT_BUTTON12); + m_interface->DeleteControl(EVENT_BUTTON13); + m_interface->DeleteControl(EVENT_BUTTON14); + m_interface->DeleteControl(EVENT_BUTTON15); + m_interface->DeleteControl(EVENT_BUTTON16); + m_interface->DeleteControl(EVENT_BUTTON17); + m_interface->DeleteControl(EVENT_BUTTON18); + + m_engine->SetInfoText(0, ""); + m_engine->SetInfoText(1, ""); +} + + +// Met à jour les valeurs éditables pour le mapping des textures. + +void CModel::PutTextureValues() +{ + CEdit* pe; + char s[100]; + int value; + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT2); + if ( pe != 0 ) + { + value = (int)(m_textureSup.x*256.0f+0.5f); + sprintf(s, "%d", value); + pe->SetText(s); + } + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT3); + if ( pe != 0 ) + { + value = (int)(m_textureSup.y*256.0f+0.5f); + sprintf(s, "%d", value); + pe->SetText(s); + } + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT4); + if ( pe != 0 ) + { + value = (int)(m_textureInf.x*256.0f-0.5f); + sprintf(s, "%d", value); + pe->SetText(s); + } + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT5); + if ( pe != 0 ) + { + value = (int)(m_textureInf.y*256.0f-0.5f); + sprintf(s, "%d", value); + pe->SetText(s); + } +} + +// Prend les valeurs éditables pour le mapping des textures. + +void CModel::GetTextureValues() +{ + CEdit* pe; + char s[100]; + int value; + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT2); + if ( pe != 0 ) + { + pe->GetText(s, 100); + sscanf(s, "%d", &value); + m_textureSup.x = ((float)value-0.5f)/256.0f; + } + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT3); + if ( pe != 0 ) + { + pe->GetText(s, 100); + sscanf(s, "%d", &value); + m_textureSup.y = ((float)value-0.5f)/256.0f; + } + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT4); + if ( pe != 0 ) + { + pe->GetText(s, 100); + sscanf(s, "%d", &value); + m_textureInf.x = ((float)value+0.5f)/256.0f; + } + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT5); + if ( pe != 0 ) + { + pe->GetText(s, 100); + sscanf(s, "%d", &value); + m_textureInf.y = ((float)value+0.5f)/256.0f; + } +} + + +// Donne le nom du modèle. + +void CModel::GetModelName(char *buffer) +{ + CEdit* pe; + char s[100]; + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT1); + if ( pe == 0 ) + { + strcpy(buffer, "objects\\io.mod"); + } + else + { + pe->GetText(s, 100); + sprintf(buffer, "objects\\%s.mod", s); + } +} + +// Donne le nom du modèle. + +void CModel::GetDXFName(char *buffer) +{ + CEdit* pe; + char s[100]; + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT1); + if ( pe == 0 ) + { + strcpy(buffer, "models\\import.dxf"); + } + else + { + pe->GetText(s, 100); + sprintf(buffer, "models\\%s.dxf", s); + } +} + +// Donne le nom du modèle. + +void CModel::GetScriptName(char *buffer) +{ + CEdit* pe; + char s[100]; + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT1); + if ( pe == 0 ) + { + strcpy(buffer, "objects\\script.txt"); + } + else + { + pe->GetText(s, 100); + sprintf(buffer, "objects\\%s.txt", s); + } +} + +// Indique si l'édition du nom a le focus. + +BOOL CModel::IsEditFocus() +{ + CEdit* pe; + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT1); + if ( pe != 0 ) + { + if ( pe->RetFocus() ) return TRUE; + } + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT2); + if ( pe != 0 ) + { + if ( pe->RetFocus() ) return TRUE; + } + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT3); + if ( pe != 0 ) + { + if ( pe->RetFocus() ) return TRUE; + } + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT4); + if ( pe != 0 ) + { + if ( pe->RetFocus() ) return TRUE; + } + + pe = (CEdit*)m_interface->SearchControl(EVENT_EDIT5); + if ( pe != 0 ) + { + if ( pe->RetFocus() ) return TRUE; + } + + return FALSE; +} + + +// Gestion d'un événement. + +BOOL CModel::EventProcess(const Event &event) +{ + char s[100]; + int first, last; + + switch( event.event ) + { + case EVENT_FRAME: + EventFrame(event); + break; + + case EVENT_KEYDOWN: + if ( IsEditFocus() ) + break; + + if ( event.param == '1' ) + { + m_mode = 1; + UpdateInfoText(); + } + if ( event.param == '2' ) + { + m_mode = 2; + UpdateInfoText(); + } + if ( event.param == '3' ) + { + m_mode = 3; + UpdateInfoText(); + } + if ( event.param == VK_ADD ) // plus pavé num ? + { + if ( event.keyState & KS_SHIFT ) CurrentSelect(TRUE); + CurrentSearchNext(+1, (event.keyState & KS_CONTROL)); + } + if ( event.param == VK_SUBTRACT ) // moins pavé num ? + { + if ( event.keyState & KS_SHIFT ) CurrentSelect(TRUE); + CurrentSearchNext(-1, (event.keyState & KS_CONTROL)); + } + if ( event.param == VK_NUMPAD0 ) + { + CurrentSelect(FALSE); + } + if ( event.param == VK_DECIMAL ) + { + CurrentSelect(TRUE); + } + if ( event.param == VK_END ) + { + DeselectAll(); + } + if ( event.param == VK_INSERT ) + { + SelectAll(); + } + if ( event.param == VK_BACK ) // Delete normal ? + { + SelectDelete(); + } + if ( event.param == VK_SPACE ) + { + m_bDisplayTransparent = !m_bDisplayTransparent; + m_bDisplayOnlySelection = FALSE; + } + if ( event.param == 'H' ) + { + m_bDisplayOnlySelection = !m_bDisplayOnlySelection; + m_bDisplayTransparent = FALSE; + } + if ( m_mode == 1 ) + { + if ( event.param == 'S' ) + { + SmoothSelect(); + } + if ( event.param == 'N' ) + { + PlaneSelect(); + } + if ( event.param == 'C' ) + { + ColorSelect(); + } + if ( event.param == 'V' ) + { + m_color ++; + if ( m_color >= MAX_COLORS ) m_color = 0; + UpdateInfoText(); + ColorSelect(); + } + if ( event.param == 'J' ) + { + StateSelect(); + } + if ( event.param == 'K' ) + { + m_state ++; + if ( m_state >= MAX_STATES ) m_state = 0; + UpdateInfoText(); + StateSelect(); + } + if ( event.param == 'M' ) + { + m_textureMode ++; + if ( m_textureMode > 3 ) m_textureMode = 0; + UpdateInfoText(); + GetTextureValues(); + MappingSelect(m_textureMode, m_textureRotate, + m_bTextureMirrorX, m_bTextureMirrorY, + m_textureInf, m_textureSup, m_textureName); + } + if ( event.param == 'Z' ) + { + m_textureRotate ++; + if ( m_textureRotate > 2 ) m_textureRotate = 0; + UpdateInfoText(); + GetTextureValues(); + MappingSelect(m_textureMode, m_textureRotate, + m_bTextureMirrorX, m_bTextureMirrorY, + m_textureInf, m_textureSup, m_textureName); + } + if ( event.param == 'X' ) + { + m_bTextureMirrorX = !m_bTextureMirrorX; + UpdateInfoText(); + GetTextureValues(); + MappingSelect(m_textureMode, m_textureRotate, + m_bTextureMirrorX, m_bTextureMirrorY, + m_textureInf, m_textureSup, m_textureName); + } + if ( event.param == 'Y' ) + { + m_bTextureMirrorY = !m_bTextureMirrorY; + UpdateInfoText(); + GetTextureValues(); + MappingSelect(m_textureMode, m_textureRotate, + m_bTextureMirrorX, m_bTextureMirrorY, + m_textureInf, m_textureSup, m_textureName); + } + if ( event.param == 'O' ) + { + TextureRankChange(+1); + UpdateInfoText(); + } + if ( event.param == 'P' ) + { + TexturePartChange(+1); + UpdateInfoText(); + GetTextureValues(); + MappingSelect(m_textureMode, m_textureRotate, + m_bTextureMirrorX, m_bTextureMirrorY, + m_textureInf, m_textureSup, m_textureName); + } + if ( event.param == 'T' ) + { + GetTextureValues(); + MappingSelect(m_textureMode, m_textureRotate, + m_bTextureMirrorX, m_bTextureMirrorY, + m_textureInf, m_textureSup, m_textureName); + } + if ( event.param == 'E' ) + { + FPOINT ti, ts; + ti.x = 0.00f; + ti.y = 0.00f; + ts.x = 0.00f; + ts.y = 0.00f; + MappingSelect(m_textureMode, m_textureRotate, + m_bTextureMirrorX, m_bTextureMirrorY, + ti, ts, ""); + } + } + if ( m_mode == 2 ) + { + if ( event.param == 'E' ) + { + m_secondTexNum = 0; + UpdateInfoText(); + MappingSelect2(m_secondTexNum, m_secondSubdiv, m_secondOffsetU, m_secondOffsetV, m_bTextureMirrorX, m_bTextureMirrorY); + } + if ( event.param == 'O' ) + { + m_secondTexNum ++; + if ( m_secondTexNum > 10 ) m_secondTexNum = 1; + UpdateInfoText(); + } + if ( event.param == 'T' ) + { + MappingSelect2(m_secondTexNum, m_secondSubdiv, m_secondOffsetU, m_secondOffsetV, m_bTextureMirrorX, m_bTextureMirrorY); + m_engine->LoadAllTexture(); + } + if ( event.param == 'U' ) + { + m_secondOffsetU += 45; + if ( m_secondOffsetU >= 360 ) m_secondOffsetU = 0; + MappingSelect2(m_secondTexNum, m_secondSubdiv, m_secondOffsetU, m_secondOffsetV, m_bTextureMirrorX, m_bTextureMirrorY); + UpdateInfoText(); + } + if ( event.param == 'V' ) + { + m_secondOffsetV += 45; + if ( m_secondOffsetV >= 360 ) m_secondOffsetV = 0; + MappingSelect2(m_secondTexNum, m_secondSubdiv, m_secondOffsetU, m_secondOffsetV, m_bTextureMirrorX, m_bTextureMirrorY); + UpdateInfoText(); + } + if ( event.param == 'X' ) + { + m_bTextureMirrorX = !m_bTextureMirrorX; + MappingSelect2(m_secondTexNum, m_secondSubdiv, m_secondOffsetU, m_secondOffsetV, m_bTextureMirrorX, m_bTextureMirrorY); + UpdateInfoText(); + } + if ( event.param == 'Y' ) + { + m_bTextureMirrorY = !m_bTextureMirrorY; + MappingSelect2(m_secondTexNum, m_secondSubdiv, m_secondOffsetU, m_secondOffsetV, m_bTextureMirrorX, m_bTextureMirrorY); + UpdateInfoText(); + } + if ( event.param == 'S' ) + { + m_secondSubdiv ++; + if ( m_secondSubdiv > 7 ) m_secondSubdiv = 1; + MappingSelect2(m_secondTexNum, m_secondSubdiv, m_secondOffsetU, m_secondOffsetV, m_bTextureMirrorX, m_bTextureMirrorY); + UpdateInfoText(); + } + } + if ( m_mode == 3 ) + { + if ( event.param == 'M' ) + { + if ( m_min == 0.0f && m_max == 1000000.0f ) + { + m_min = 0.0f; m_max = 100.0f; + } + else if ( m_min == 0.0f && m_max == 100.0f ) + { + m_min = 100.0f; m_max = 200.0f; + } + else if ( m_min == 100.0f && m_max == 200.0f ) + { + m_min = 200.0f; m_max = 1000000.0f; + } + else if ( m_min == 200.0f && m_max == 1000000.0f ) + { + m_min = 0.0f; m_max = 1000000.0f; + } + UpdateInfoText(); + } + if ( event.param == 'C' ) + { + MinMaxChange(); + } + } + break; + + case EVENT_BUTTON1: // import ? + GetDXFName(s); + m_modFile->ReadDXF(s, m_min, m_max); + DeselectAll(); + CurrentInit(); + EventFrame(event); + m_engine->LoadAllTexture(); + break; + + case EVENT_BUTTON2: // script ? + GetScriptName(s); + ReadScript(s); + DeselectAll(); + CurrentInit(); + EventFrame(event); + m_engine->LoadAllTexture(); + break; + + case EVENT_BUTTON3: // read ? + GetModelName(s); + m_modFile->ReadModel(s, TRUE, FALSE); // lit avec frontières standard + DeselectAll(); + CurrentInit(); + EventFrame(event); + m_engine->LoadAllTexture(); + break; + + case EVENT_BUTTON4: // add ? + GetModelName(s); + first = m_modFile->RetTriangleUsed(); + m_modFile->AddModel(s, first, TRUE, FALSE); // lit avec frontières standard + last = m_modFile->RetTriangleUsed(); + SelectZone(first, last); + EventFrame(event); + break; + + case EVENT_BUTTON5: // write ? + GetModelName(s); + DeselectAll(); + m_modFile->WriteModel(s); + break; + + case EVENT_BUTTON10: // pos ? + m_oper = 'P'; + break; + case EVENT_BUTTON11: // rotate ? + m_oper = 'R'; + break; + case EVENT_BUTTON12: // zoom ? + m_oper = 'Z'; + break; + + case EVENT_BUTTON13: // +X ? + MoveSelect(D3DVECTOR(1.0f, 0.0f, 0.0f)); + break; + case EVENT_BUTTON16: // -X ? + MoveSelect(D3DVECTOR(-1.0f, 0.0f, 0.0f)); + break; + case EVENT_BUTTON14: // +Y ? + MoveSelect(D3DVECTOR(0.0f, 1.0f, 0.0f)); + break; + case EVENT_BUTTON17: // -Y ? + MoveSelect(D3DVECTOR(0.0f, -1.0f, 0.0f)); + break; + case EVENT_BUTTON15: // +Z ? + MoveSelect(D3DVECTOR(0.0f, 0.0f, 1.0f)); + break; + case EVENT_BUTTON18: // -Z ? + MoveSelect(D3DVECTOR(0.0f, 0.0f, -1.0f)); + break; + } + + return 0; +} + + +// Anime le modèle. + +BOOL CModel::EventFrame(const Event &event) +{ + D3DMATERIAL7 matCurrent, matCurrenti, matCurrents, matTrans; + D3DMATERIAL7* pMat; + D3DVERTEX2 vertex[3]; + char texName2[20]; + int i, used, objRank, state; + + m_time += event.rTime; + + m_engine->FlushObject(); + objRank = m_engine->CreateObject(); + + ZeroMemory(&matCurrent, sizeof(D3DMATERIAL7)); + matCurrent.diffuse.r = 1.0f; + matCurrent.diffuse.g = 0.0f; + matCurrent.diffuse.b = 0.0f; // rouge + matCurrent.ambient.r = 0.5f; + matCurrent.ambient.g = 0.5f; + matCurrent.ambient.b = 0.5f; + + ZeroMemory(&matCurrents, sizeof(D3DMATERIAL7)); + matCurrents.diffuse.r = 1.0f; + matCurrents.diffuse.g = 1.0f; + matCurrents.diffuse.b = 0.0f; // jaune + matCurrents.ambient.r = 0.5f; + matCurrents.ambient.g = 0.5f; + matCurrents.ambient.b = 0.5f; + + ZeroMemory(&matCurrenti, sizeof(D3DMATERIAL7)); + matCurrenti.diffuse.r = 0.0f; + matCurrenti.diffuse.g = 0.0f; + matCurrenti.diffuse.b = 1.0f; // bleu + matCurrenti.ambient.r = 0.5f; + matCurrenti.ambient.g = 0.5f; + matCurrenti.ambient.b = 0.5f; + + used = m_modFile->RetTriangleUsed(); + for ( i=0 ; i= m_triangleSel1 && + i <= m_triangleSel2 && + (int)(m_time*10.0f)%2 == 0 ) + { + pMat = &matCurrent; + } + else if ( m_triangleTable[i].bSelect && + (int)(m_time*10.0f)%2 == 0 ) + { + pMat = &matCurrents; + } + else + { + if ( m_bDisplayOnlySelection ) continue; + if ( m_bDisplayTransparent ) + { + matTrans = m_triangleTable[i].material; + matTrans.diffuse.a = 0.1f; // très transparent + pMat = &matTrans; + state = D3DSTATETD; + } + } + + if ( m_triangleTable[i].texNum2 == 0 ) + { + m_engine->AddTriangle(objRank, &m_triangleTable[i].p1, 3, + *pMat, state, + m_triangleTable[i].texName, "", + 0.0f, 1000000.0f, FALSE); + } + else + { + sprintf(texName2, "dirty%.2d.tga", m_triangleTable[i].texNum2); + m_engine->AddTriangle(objRank, &m_triangleTable[i].p1, 3, + *pMat, state|D3DSTATEDUALb, + m_triangleTable[i].texName, texName2, + 0.0f, 1000000.0f, FALSE); + } + + if ( m_bDisplayTransparent && // dessine l'intérieur ? + i >= m_triangleSel1 && + i <= m_triangleSel2 ) + { + vertex[0] = m_triangleTable[i].p3; + vertex[1] = m_triangleTable[i].p2; + vertex[2] = m_triangleTable[i].p1; + + m_engine->AddTriangle(objRank, vertex, 3, + matCurrenti, D3DSTATENORMAL, + m_triangleTable[i].texName, "", + 0.0f, 1000000.0f, FALSE); + } + } + + return TRUE; +} + + +// Donne un vertex. + +BOOL CModel::GetVertex(int rank, D3DVERTEX2 &vertex) +{ + if ( rank < 0 || rank/3 >= m_modFile->RetTriangleUsed() ) return FALSE; + if ( !m_triangleTable[rank/3].bUsed ) return FALSE; + + if ( !m_triangleTable[rank/3].bSelect ) return FALSE; + + if ( rank%3 == 0 ) + { + vertex = m_triangleTable[rank/3].p1; + return TRUE; + } + if ( rank%3 == 1 ) + { + vertex = m_triangleTable[rank/3].p2; + return TRUE; + } + if ( rank%3 == 2 ) + { + vertex = m_triangleTable[rank/3].p3; + return TRUE; + } + return FALSE; +} + +// Modifie un vertex. + +BOOL CModel::SetVertex(int rank, D3DVERTEX2 &vertex) +{ + if ( rank < 0 || rank/3 >= m_modFile->RetTriangleUsed() ) return FALSE; + if ( !m_triangleTable[rank/3].bUsed ) return FALSE; + + if ( !m_triangleTable[rank/3].bSelect ) return FALSE; + + if ( rank%3 == 0 ) + { + m_triangleTable[rank/3].p1 = vertex; + return TRUE; + } + if ( rank%3 == 1 ) + { + m_triangleTable[rank/3].p2 = vertex; + return TRUE; + } + if ( rank%3 == 2 ) + { + m_triangleTable[rank/3].p3 = vertex; + return TRUE; + } + return FALSE; +} + +// Adouci les normales des triangles sélectionés. + +void CModel::SmoothSelect() +{ + char* bDone; + int index[100]; + int used, i, j, rank; + D3DVERTEX2 vi, vj; + D3DVECTOR sum; + + used = m_modFile->RetTriangleUsed(); + + bDone = (char*)malloc(used*3*sizeof(char)); + for ( i=0 ; i= 100 ) break; + } + } + + sum.x = 0; + sum.y = 0; + sum.z = 0; + for ( j=0 ; jRetTriangleUsed(); + + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; iReadModel(buffer, TRUE, TRUE); + last = m_modFile->RetTriangleUsed(); + SelectZone(0, last); + } + else + { + first = m_modFile->RetTriangleUsed(); + m_modFile->AddModel(buffer, first, TRUE, FALSE); + last = m_modFile->RetTriangleUsed(); + SelectZone(first, last); + } + bFirst = FALSE; + + move = OpDir(line, "zoom"); + OperSelect(move, 'Z'); + + move = OpDir(line, "rot"); + move *= PI/180.0f; // degrés -> radians + OperSelect(move, 'R'); + + move = OpDir(line, "pos"); + OperSelect(move, 'P'); + } + } + + fclose(file); +} + + + +// Calcule la bbox des triangles sélectionnés. + +void CModel::BBoxCompute(D3DVECTOR &min, D3DVECTOR &max) +{ + D3DVERTEX2 vertex; + int used, i; + + min.x = 1000000.0f; + min.y = 1000000.0f; + min.z = 1000000.0f; + max.x = -1000000.0f; + max.y = -1000000.0f; + max.z = -1000000.0f; + + used = m_modFile->RetTriangleUsed(); + + for ( i=0 ; i max.x ) max.x = vertex.x; + if ( vertex.y > max.y ) max.y = vertex.y; + if ( vertex.z > max.z ) max.z = vertex.z; + } +} + +// Retourne le centre de gravité de la sélection. + +D3DVECTOR CModel::RetSelectCDG() +{ + D3DVECTOR min, max, cdg; + + BBoxCompute(min, max); + + cdg.x = (min.x+max.x)/2.0f; + cdg.y = (min.y+max.y)/2.0f; + cdg.z = (min.z+max.z)/2.0f; + + return cdg; +} + +// Retourne le vecteur normal de la sélection. + +D3DVECTOR CModel::RetSelectNormal() +{ + D3DVECTOR p1, p2, p3, n; + + p1.x = m_triangleTable[m_triangleSel1].p1.nx; + p1.y = m_triangleTable[m_triangleSel1].p1.ny; + p1.z = m_triangleTable[m_triangleSel1].p1.nz; + + p2.x = m_triangleTable[m_triangleSel1].p2.nx; + p2.y = m_triangleTable[m_triangleSel1].p2.ny; + p2.z = m_triangleTable[m_triangleSel1].p2.nz; + + p3.x = m_triangleTable[m_triangleSel1].p3.nx; + p3.y = m_triangleTable[m_triangleSel1].p3.ny; + p3.z = m_triangleTable[m_triangleSel1].p3.nz; + + n = Normalize(p1+p2+p3); + + return n; +} + +// Mappe une texture sur les triangles sélectionnés. + +BOOL CModel::IsMappingSelectPlausible(D3DMaping D3Dmode) +{ + D3DVERTEX2 vertex[3]; + D3DVECTOR min, max; + FPOINT a, b, ti, ts; + float au, bu, av, bv; + int used, i, j; + + ti.x = 0.0f; + ti.y = 0.0f; + ts.x = 1.0f; + ts.y = 1.0f; + + BBoxCompute(min, max); + + if ( D3Dmode == D3DMAPPINGX ) + { + a.x = min.z; + a.y = min.y; + b.x = max.z; + b.y = max.y; + } + if ( D3Dmode == D3DMAPPINGY ) + { + a.x = min.x; + a.y = min.z; + b.x = max.x; + b.y = max.z; + } + if ( D3Dmode == D3DMAPPINGZ ) + { + a.x = min.x; + a.y = min.y; + b.x = max.x; + b.y = max.y; + } + + au = (ts.x-ti.x)/(b.x-a.x); + bu = ts.x-b.x*(ts.x-ti.x)/(b.x-a.x); + + av = (ts.y-ti.y)/(b.y-a.y); + bv = ts.y-b.y*(ts.y-ti.y)/(b.y-a.y); + + used = m_modFile->RetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; i 1.0f ) u[j] -= 1.0f; +#else + u[j] = RotateAngle(p.x, p.z)/PI; +//? if ( u[j] > 1.0f ) u[j] = 2.0f-u[j]; + if ( u[j] > 1.0f ) u[j] -= 1.0f; +#endif + + v[j] = p.y/dim.y/2.0f + 0.5f; + + if ( u[j] < 0.5f ) m[j] = u[j]; + else m[j] = u[j]-1.0f; + } + + avg = (m[0]+m[1]+m[2])/3.0f; + + for ( j=0 ; j<3 ; j++ ) + { + if ( u[j] < 0.05f || u[j] > 0.95f ) + { + if ( avg > 0.0f ) u[j] = 0.0f; + else u[j] = 1.0f; + } + + vertex[j].tu = ti.x+(ts.x-ti.x)*u[j]; + vertex[j].tv = ti.y+(ts.y-ti.y)*v[j]; + + SetVertex(i*3+j, vertex[j]); + } + } + + SelectTerm(); +} + + +// Mappe une texture secondaire sur les triangles sélectionnés. + +void CModel::MappingSelect2(int texNum2, int subdiv, + int offsetU, int offsetV, + BOOL bMirrorX, BOOL bMirrorY) +{ + D3DVERTEX2 vertex; + D3DVECTOR min, max, center, p; + float u ,v; + int used, i; + + DefaultSelect(); + + used = m_modFile->RetTriangleUsed(); + for ( i=0 ; i 2 ) + { + MappingSelectPlane2(subdiv-3, bMirrorX, bMirrorY); + return; + } + + BBoxCompute(min, max); + center = (min+max)/2.0f; + + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; i= Max(n.y, n.z) ) mode = 0; + if ( n.y >= Max(n.x, n.z) ) mode = 1; + if ( n.z >= Max(n.x, n.y) ) mode = 2; + } + + if ( !GetVertex(i, vertex) ) continue; + + if ( mode == 0 ) + { + vertex.tu2 = vertex.z*au.z+bu.z; + vertex.tv2 = vertex.y*av.y+bv.y; + } + if ( mode == 1 ) + { + vertex.tu2 = vertex.x*au.x+bu.x; + vertex.tv2 = vertex.z*av.z+bv.z; + } + if ( mode == 2 ) + { + vertex.tu2 = vertex.x*au.x+bu.x; + vertex.tv2 = vertex.y*av.y+bv.y; + } + + SetVertex(i, vertex); + } + + SelectTerm(); +} + + +// Cherche le triangle suivant. + +int CModel::SearchNext(int rank, int step) +{ + int max, i; + + max = m_modFile->RetTriangleUsed(); + + for ( i=0 ; i= max ) rank = 0; + + if ( m_triangleTable[rank].min != m_min || + m_triangleTable[rank].max != m_max ) continue; + + if ( m_triangleTable[rank].bUsed ) break; + } + return rank; +} + +// Cherche tous les triangles faisant partie du même plan. + +int CModel::SearchSamePlane(int first, int step) +{ + D3DVECTOR vFirst[3], vNext[3]; + int last, i; + + vFirst[0].x = m_triangleTable[first].p1.x; + vFirst[0].y = m_triangleTable[first].p1.y; + vFirst[0].z = m_triangleTable[first].p1.z; + vFirst[1].x = m_triangleTable[first].p2.x; + vFirst[1].y = m_triangleTable[first].p2.y; + vFirst[1].z = m_triangleTable[first].p2.z; + vFirst[2].x = m_triangleTable[first].p3.x; + vFirst[2].y = m_triangleTable[first].p3.y; + vFirst[2].z = m_triangleTable[first].p3.z; + + for ( i=0 ; i<1000 ; i++ ) + { + last = first; + first = SearchNext(first, step); + + vNext[0].x = m_triangleTable[first].p1.x; + vNext[0].y = m_triangleTable[first].p1.y; + vNext[0].z = m_triangleTable[first].p1.z; + vNext[1].x = m_triangleTable[first].p2.x; + vNext[1].y = m_triangleTable[first].p2.y; + vNext[1].z = m_triangleTable[first].p2.z; + vNext[2].x = m_triangleTable[first].p3.x; + vNext[2].y = m_triangleTable[first].p3.y; + vNext[2].z = m_triangleTable[first].p3.z; + + if ( !IsSamePlane(vFirst, vNext) ) // autre plan ? + { + return last; + } + } + return first; +} + +// Cherche le triangle suivant. + +void CModel::CurrentSearchNext(int step, BOOL bControl) +{ + if ( step > 0 ) // en avant ? + { + m_triangleSel1 = SearchNext(m_triangleSel2, step); + if ( bControl ) + { + m_triangleSel2 = m_triangleSel1; + } + else + { + m_triangleSel2 = SearchSamePlane(m_triangleSel1, step); + } + } + if ( step < 0 ) // en arrière ? + { + m_triangleSel2 = SearchNext(m_triangleSel1, step); + if ( bControl ) + { + m_triangleSel1 = m_triangleSel2; + } + else + { + m_triangleSel1 = SearchSamePlane(m_triangleSel2, step); + } + } + +#if 0 + char s[100]; + sprintf(s, "(%.2f;%.2f;%.2f) (%.2f;%.2f;%.2f) (%.2f;%.2f;%.2f)", + m_triangleTable[m_triangleSel1].p1.x, + m_triangleTable[m_triangleSel1].p1.y, + m_triangleTable[m_triangleSel1].p1.z, + m_triangleTable[m_triangleSel1].p2.x, + m_triangleTable[m_triangleSel1].p2.y, + m_triangleTable[m_triangleSel1].p2.z, + m_triangleTable[m_triangleSel1].p3.x, + m_triangleTable[m_triangleSel1].p3.y, + m_triangleTable[m_triangleSel1].p3.z); + m_engine->SetInfoText(2, s); + sprintf(s, "(%.2f;%.2f) (%.2f;%.2f) (%.2f;%.2f)", + m_triangleTable[m_triangleSel1].p1.tu2, + m_triangleTable[m_triangleSel1].p1.tv2, + m_triangleTable[m_triangleSel1].p2.tu2, + m_triangleTable[m_triangleSel1].p2.tv2, + m_triangleTable[m_triangleSel1].p3.tu2, + m_triangleTable[m_triangleSel1].p3.tv2); + m_engine->SetInfoText(3, s); +#endif + + InitViewFromSelect(); + UpdateInfoText(); +} + +// Initialise les triangles courants initiaux. + +void CModel::CurrentInit() +{ + m_triangleSel1 = 0; + m_triangleSel2 = SearchSamePlane(m_triangleSel1, +1); + + InitViewFromSelect(); + UpdateInfoText(); +} + +// Sélectionne les triangles courants. + +void CModel::CurrentSelect(BOOL bSelect) +{ + int i; + + for ( i=m_triangleSel1 ; i<=m_triangleSel2 ; i++ ) + { + m_triangleTable[i].bSelect = bSelect; + } +} + + +// Désélectionne tous les triangles. + +void CModel::DeselectAll() +{ + int used, i; + + used = m_modFile->RetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; i= first && i < last ) + { + m_triangleTable[i].bSelect = TRUE; + } + } + m_triangleSel1 = first; + m_triangleSel2 = last-1; +} + +// Sélectionne tous les triangles. + +void CModel::SelectAll() +{ + int used, i; + + used = m_modFile->RetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; i= m_triangleSel1 && i <= m_triangleSel2 ) + { + if ( !m_triangleTable[i].bSelect ) return; + } + else + { + if ( m_triangleTable[i].bSelect ) return; + } + } + + DeselectAll(); +} + +// Sélectionne les triangles courants. + +void CModel::DefaultSelect() +{ + int used, i; + + used = m_modFile->RetTriangleUsed(); + for ( i=m_triangleSel1 ; i<=m_triangleSel2 ; i++ ) + { + m_triangleTable[i].bSelect = TRUE; + } +} + + + +// Supprime tous les triangles sélectionnés. + +void CModel::SelectDelete() +{ + int used ,i; + + DefaultSelect(); + + used = m_modFile->RetTriangleUsed(); + for ( i=0 ; iRetTriangleUsed(); + for ( i=0 ; iSetTriangleUsed(j); + CurrentInit(); +} + + +// Change le min/max de tous les triangles sélectionnés. + +void CModel::MinMaxChange() +{ + int used, i; + + DefaultSelect(); + + used = m_modFile->RetTriangleUsed(); + for ( i=0 ; i= PI ) + { + m_viewAngleV -= PI; + m_viewAngleH -= PI; + } + m_viewAngleV *= 0.75f; + + char s[100]; + sprintf(s, "angle=%f %f -> %f %f\n", h,v, m_viewAngleH, m_viewAngleV); + OutputDebugString(s); +#endif +} + +// Met à jour les paramètres pour le point de vue. + +void CModel::UpdateView() +{ + D3DVECTOR eye, lookat, vUpVec; + +//? lookat = RetSelectCDG(); + lookat = D3DVECTOR(0.0f, m_viewHeight, 0.0f); + eye = RotateView(lookat, m_viewAngleH, m_viewAngleV, m_viewDist); + + vUpVec = D3DVECTOR(0.0f, 1.0f, 0.0f); + m_engine->SetViewParams(eye, lookat, vUpVec, 10.0f); + m_engine->SetRankView(0); +} + +// Déplace le point de vue. + +void CModel::ViewMove(const Event &event, float speed) +{ + if ( IsEditFocus() ) return; + + // Up/Down. + if ( event.axeY > 0.5f ) + { + if ( event.keyState & KS_CONTROL ) + { + m_viewHeight += event.rTime*10.0f*speed; + if ( m_viewHeight > 100.0f ) m_viewHeight = 100.0f; + } + else + { + m_viewAngleV -= event.rTime*1.0f*speed; + if ( m_viewAngleV < -PI*0.49f ) m_viewAngleV = -PI*0.49f; + } + } + if ( event.axeY < -0.5f ) + { + if ( event.keyState & KS_CONTROL ) + { + m_viewHeight -= event.rTime*10.0f*speed; + if ( m_viewHeight < -100.0f ) m_viewHeight = -100.0f; + } + else + { + m_viewAngleV += event.rTime*1.0f*speed; + if ( m_viewAngleV > PI*0.49f ) m_viewAngleV = PI*0.49f; + } + } + + // Left/Right. + if ( event.axeX < -0.5f ) + { + m_viewAngleH -= event.rTime*1.0f*speed; + } + if ( event.axeX > 0.5f ) + { + m_viewAngleH += event.rTime*1.0f*speed; + } + + // PageUp/PageDown. + if ( event.keyState & KS_PAGEUP ) + { + m_viewDist -= event.rTime*30.0f*speed; + if ( m_viewDist < 1.0f ) m_viewDist = 1.0f; + } + if ( event.keyState & KS_PAGEDOWN ) + { + m_viewDist += event.rTime*30.0f*speed; + if ( m_viewDist > 300.0f ) m_viewDist = 300.0f; + } +} + + + +// Met à jour le texte d'informations. + +void CModel::UpdateInfoText() +{ + char info[100]; + + if ( m_mode == 1 ) + { + sprintf(info, "[1] V:color=%d K:state=%d Sel=%d..%d (T=%d)", + m_color, m_state, + m_triangleSel1, m_triangleSel2, + m_triangleSel2-m_triangleSel1+1); + m_engine->SetInfoText(0, info); + + sprintf(info, "M:mode=%d Z:rot=%d XY:mir=%d;%d P:part=%d O:name=%s", + m_textureMode, m_textureRotate, + m_bTextureMirrorX, m_bTextureMirrorY, + m_texturePart, m_textureName); + m_engine->SetInfoText(1, info); + } + + if ( m_mode == 2 ) + { + sprintf(info, "[2] Sel=%d..%d (T=%d)", + m_triangleSel1, m_triangleSel2, + m_triangleSel2-m_triangleSel1+1); + m_engine->SetInfoText(0, info); + + sprintf(info, "O:dirty=%d UV:offset=%d;%d XY:mir=%d;%d S:subdiv=%d", + m_secondTexNum, + m_secondOffsetU, m_secondOffsetV, + m_bTextureMirrorX, m_bTextureMirrorY, + m_secondSubdiv); + m_engine->SetInfoText(1, info); + } + + if ( m_mode == 3 ) + { + sprintf(info, "[3] LOD Min/max=%d..%d Sel=%d..%d (T=%d)", + (int)m_min, (int)m_max, + m_triangleSel1, m_triangleSel2, + m_triangleSel2-m_triangleSel1+1); + m_engine->SetInfoText(0, info); + + sprintf(info, "[Change]"); + m_engine->SetInfoText(1, info); + } +} + + + +static int tablePartT[] = // lemt.tga +{ + 192, 0, 256, 32, // profil chenille + 0, 64, 128, 128, // roues pour chenille + 0, 0, 128, 64, // profil + 90, 0, 128, 28, // pivot trainer + 128, 0, 192, 44, // coffre avant + 128, 44, 192, 58, // callandre + 128, 58, 192, 87, // coffre arrière + 128, 87, 192, 128, // callandre arrière + 128, 128, 192, 144, // sous-callandre arrière + 0, 128, 32, 152, // garde boue arrière + 0, 152, 32, 182, // garde boue milieu + 0, 182, 32, 256, // garde boue avant + 32, 128, 112, 176, // aile + 224, 48, 232, 64, // tuyère + 192, 32, 224, 64, // feu sous réacteur + 224, 32, 256, 48, // pied + 192, 64, 256, 128, // capteur + 192, 128, 224, 176, // support pile + 192, 216, 248, 248, // côté canon + 220, 216, 222, 245, // côté canon + 64, 176, 128, 224, // dessus canon + 128, 152, 192, 160, // extérieur canon + 128, 144, 192, 152, // intérieur canon + 192, 176, 224, 192, // petit canon + 128, 236, 192, 256, // canon organique + 214, 192, 224, 216, // réticule de visée + 224, 128, 248, 152, // articulation + 128, 192, 192, 214, // piston côté + 128, 214, 192, 236, // piston face + 192, 192, 214, 214, // piston tranche + 128, 192, 161, 214, // piston petit côté + 32, 176, 64, 198, // piston pour radar + 128, 160, 160, 192, // roue + 232, 48, 255, 56, // profil pneu + 240, 152, 248, 216, // hachures verticales + 248, 192, 256, 256, // batterie + 224, 152, 240, 168, // roche + 144, 80, 176, 112, // nucléaire + 140, 76, 180, 116, // nucléaire grand + 144, 80, 152, 88, // jaune nucléaire + 224, 168, 240, 192, // capot résolution C + 224, 192, 240, 210, // arrière résolution C + 32, 224, 96, 235, // bras résolution C + 32, 235, 96, 246, // bras résolution C + 161, 1, 164, 4, // blanc + 168, 1, 171, 4, // gris moyen + 154, 1, 157, 4, // gris foncé uni + 147, 1, 150, 4, // bleu uni + 114, 130, 118, 134, // rouge uni + 121, 130, 125, 134, // vert uni + 114, 137, 118, 141, // jaune uni + 121, 137, 125, 141, // violet uni + -1 +}; + +static int tablePartR[] = // roller.tga +{ + 0, 0, 128, 52, // roues pour chenille + 48, 137, 128, 201, // côté radiateur + 0, 52, 32, 84, // avant radiateur + 32, 52, 43, 84, // arrière radiateur + 0, 84, 96, 137, // grand côté + 128, 0, 192, 85, // avant + 128, 173, 192, 256, // arrière + 192, 0, 256, 42, // dessus + 128, 85, 192, 109, // côté pillon + 128, 109, 192, 173, // dessus pillon + 192, 85, 240, 109, // côté porte pillon + 0, 137, 24, 256, // côté verrin + 24, 137, 48, 256, // côté verrin + 48, 201, 128, 233, // support canon + 192, 109, 256, 173, // fond canon + 192, 173, 240, 205, // canon 1 + 192, 173, 240, 177, // canon 2 + 43, 52, 75, 84, // avant canon + 48, 233, 128, 247, // piston + 96, 105, 128, 137, // avant phazer + 96, 97, 128, 105, // canon phazer + 75, 52, 107, 84, // échappement + 192, 205, 243, 256, // instruction centrale nucléaire + 192, 42, 256, 85, // reflet vitres + -1 +}; + +static int tablePartW[] = // subm.tga +{ + 0, 0, 128, 26, // chenilles + 0, 26, 22, 114, // portique 1 + 0, 114, 22, 202, // portique 2 + 22, 26, 82, 56, // côté hublot + 22, 56, 82, 86, // côté ligne rouge + 22, 86, 82, 116, // côté simple + 22, 116, 82, 146, // avant/arrière + 22, 146, 82, 176, // avant/arrière + phare + 132, 82, 196, 166, // capot trainer + 132, 166, 196, 177, // capot trainer + 132, 177, 196, 188, // capot trainer + 0, 224, 96, 256, // côté trainer + 30, 224, 48, 256, // arrière trainer + 136, 240, 216, 256, // barrière courte + 96, 240, 256, 256, // barrière longue + 128, 0, 160, 32, // black-box 1 + 160, 0, 192, 32, // black-box 2 + 192, 0, 224, 32, // black-box 3 + 224, 105, 256, 137, // TNT 1 + 224, 137, 256, 169, // TNT 2 + 82, 32, 146, 82, // factory résolution C + 146, 32, 210, 82, // factory résolution C + 224, 0, 256, 105, // tower résolution C + 82, 82, 132, 150, // research résolution C + 199, 169, 256, 233, // sac résolution C + 106, 150, 130, 214, // clé A + 82, 150, 106, 214, // clé B + 132, 188, 196, 212, // clé C + 132, 212, 196, 236, // clé D + 210, 32, 224, 46, // gris + 56, 176, 82, 224, // sol coffre-fort + -1 +}; + +static int tablePartDr[] = // drawer.tga +{ + 128, 0, 134, 6, // bleu + 128, 6, 134, 12, // gris foncé + 128, 12, 134, 18, // gris clair + 0, 0, 128, 32, // roues chenille + 192, 0, 256, 32, // profil chenille + 140, 0, 160, 8, // profil phare + 160, 0, 192, 32, // face phare + 0, 32, 160, 48, // hachure + 160, 32, 192, 48, // côté + 0, 48, 96, 96, // tableau de bord + 96, 48, 192, 112, // radiateur + 192, 32, 256, 112, // grille latérale + 192, 112, 256, 128, // capot + 0, 96, 8, 160, // chassis + 8, 96, 96, 104, // axe chenilles + 8, 104, 16, 160, // axe carrousel + 16, 128, 24, 160, // flan support + 224, 128, 256, 160, // rotule + 24, 104, 32, 160, // bocal (18) + 32, 104, 40, 160, // bocal + 40, 104, 48, 160, // bocal + 24, 152, 48, 160, // bocal fond + 0, 240, 32, 256, // crayon 1: couleur (22) + 0, 160, 32, 192, // crayon 1: dessus + 0, 192, 32, 256, // crayon 1: pointe + 32, 240, 64, 256, // crayon 2: couleur + 32, 160, 64, 192, // crayon 2: dessus + 32, 192, 64, 256, // crayon 2: pointe + 64, 240, 96, 256, // crayon 3: couleur + 64, 160, 96, 192, // crayon 3: dessus + 64, 192, 96, 256, // crayon 3: pointe + 96, 240, 128, 256, // crayon 4: couleur + 96, 160, 128, 192, // crayon 4: dessus + 96, 192, 128, 256, // crayon 4: pointe + 128, 240, 160, 256, // crayon 5: couleur + 128, 160, 160, 192, // crayon 5: dessus + 128, 192, 160, 256, // crayon 5: pointe + 160, 240, 192, 256, // crayon 6: couleur + 160, 160, 192, 192, // crayon 6: dessus + 160, 192, 192, 256, // crayon 6: pointe + 192, 240, 224, 256, // crayon 7: couleur + 192, 160, 224, 192, // crayon 7: dessus + 192, 192, 224, 256, // crayon 7: pointe + 224, 240, 256, 256, // crayon 8: couleur + 224, 160, 256, 192, // crayon 8: dessus + 224, 192, 256, 256, // crayon 8: pointe + -1 +}; + +static int tablePartKi[] = // kid.tga +{ + 0, 0, 128, 53, // ciseaux + 128, 0, 256, 128, // CD + 0, 0, 8, 8, // livre 1: fond + 8, 0, 16, 8, // livre 2: fond + 16, 0, 24, 8, // livre: fond + 24, 0, 32, 8, // livre: fond + 32, 0, 40, 8, // livre: fond + 40, 0, 48, 8, // livre: fond + 0, 53, 22, 138, // livre 1: tranche + 22, 53, 86, 138, // livre 1: face + 0, 138, 22, 224, // livre 2: tranche + 22, 138, 86, 224, // livre 2: face + 86, 53, 94, 85, // livre: pages + 94, 53, 110, 139, // livre: tranche + 110, 53, 126, 139, // livre: tranche + 86, 139, 102, 225, // livre: tranche + 102, 139, 118, 225, // livre: tranche + 118, 139, 134, 225, // livre: tranche + 64, 0, 72, 8, // fauille: fond + 155, 155, 256, 256, // feuille: carreaux + 72, 0, 80, 8, // lampe + 80, 0, 88, 8, // lampe + 80, 8, 88, 16, // ampoule + 72, 8, 80, 16, // rayons (23) + 86, 85, 94, 139, // lampe + 0, 224, 32, 256, // lampe rotule + 64, 8, 72, 16, // arrosoir: fond + 134, 128, 142, 256, // arrosoir: corps + 142, 128, 150, 256, // arrosoir: tuyau + 128, 225, 134, 256, // arrosoir: intérieur + 32, 224, 64, 256, // arrosoir: ponneau + 56, 8, 64, 16, // skate: roues (31) + 48, 8, 56, 16, // skate: axes + 40, 8, 48, 16, // skate: grip + 32, 8, 40, 16, // skate: tranche + 24, 8, 32, 16, // skate: dessous + 150, 128, 200, 256, // skate: motif 1 + 200, 128, 250, 256, // skate: motif 2 + 64, 224, 96, 256, // skate: roue (38) + 96, 225, 104, 256, // skate: amortisseur + -1 +}; + +static int tablePartKi2[] = // kid2.tga +{ + 2, 2, 62, 62, // coca: dessus + 0, 64, 8, 192, // coca: flan + 8, 64, 96, 192, // coca: logo + 128, 0, 256, 85, // carton + 128, 85, 256, 91, // carton tranche + 128, 128, 256, 256, // roue + 192, 96, 256, 128, // pneu + 184, 96, 192, 128, // jante + 128, 96, 160, 128, // intérieur + 160, 96, 168, 104, // porte bois + 160, 104, 168, 112, // porte métal + 160, 112, 184, 128, // vitre + 96, 0, 128, 256, // bouteille: corps (12) + 64, 0, 96, 32, // bouteille: bouchon + 168, 96, 176, 104, // bouteille: vert + 0, 192, 96, 224, // bois clair + 0, 224, 96, 256, // bois foncé + 64, 32, 96, 64, // bateau + 168, 104, 176, 112, // ballon (18) + 176, 104, 184, 112, // ballon + 176, 96, 184, 104, // intérieur caisse + -1 +}; + +static int tablePartKi3[] = // kid3.tga +{ + 0, 0, 32, 28, // écrou: flan + 0, 28, 32, 44, // écrou: profil + 0, 44, 32, 60, // écrou: pas de vis + 0, 60, 32, 64, // tuyau + 0, 64, 32, 68, // tuyau + 0, 68, 8, 76, // tuyau + 0, 76, 32, 108, // plastic + 8, 68, 16, 76, // saut: gris clair (7) + 16, 68, 24, 76, // saut: gris foncé + 24, 68, 32, 76, // saut: gris bois + 0, 108, 32, 140, // saut: rotule + 0, 140, 32, 144, // saut: axe + 128, 0, 256, 128, // saut: flan + 0, 144, 8, 152, // basket: gris foncé (13) + 8, 144, 16, 152, // basket: gris clair + 16, 144, 24, 152, // basket: gris lacets + 24, 144, 32, 152, // basket: gris semelle + 0, 152, 8, 181, // basket: intérieur + 0, 181, 192, 256, // basket: côté + 192, 181, 226, 256, // basket: arrière + 32, 135, 96, 181, // basket: dessus (20) + 96, 168, 128, 181, // basket: avant + 8, 152, 16, 160, // chaise: plastique + 16, 152, 24, 160, // chaise: métal + 32, 0, 64, 32, // chaise: roue + 8, 177, 24, 181, // chaise: roue + 226, 181, 234, 256, // chaise: piston (26) + 64, 96, 128, 128, // chaise: relief + 96, 135, 128, 167, // chaise: dessous + 32, 128, 250, 135, // paille 1 + 38, 128, 256, 135, // paille 2 + 234, 181, 242, 256, // allumette + 8, 160, 16, 168, // allumette (dessus) + 128, 135, 224, 181, // panneau + 242, 135, 256, 256, // poteau (34) + 24, 152, 32, 160, // clou + 16, 160, 24, 168, // tuyau métalique + 112, 181, 192, 185, // tuyau intérieur + 32, 32, 48, 80, // pas de vis + 24, 160, 32, 168, // ventillateur: plastique (39) + 40, 80, 56, 96, // ventillateur: plastique dégradé + 8, 168, 16, 176, // ventillateur: métal + 32, 80, 40, 112, // ventillateur: socle 1 + 64, 0, 96, 16, // ventillateur: socle 2 + 48, 32, 56, 80, // ventillateur: socle 3 + 64, 16, 96, 32, // ventillateur: moteur flan + 96, 0, 128, 32, // ventillateur: moteur face + 102, 6, 122, 26, // ventillateur: socle dessus + 16, 168, 24, 176, // pot: uni (48) + 56, 32, 64, 64, // pot: haut + 56, 64, 64, 96, // pot: bas + 64, 32, 128, 96, // pot: terre + -1 +}; + +static int tablePartF[] = // factory.tga +{ + 0, 0, 152, 152, // plancher octogonal fabrique + 50, 50, 102, 102, // dessus pile + 0, 152, 128, 252, // avant + 128, 152, 256, 252, // arrière + 152, 28, 225, 128, // côté + 152, 28, 176, 128, // côté partiel + 152, 0, 216, 16, // hachures + 236, 0, 256, 40, // axe + 152, 128, 224, 152, // support cible + -1 +}; + +static int tablePartD[] = // derrick.tga +{ + 0, 0, 64, 32, // grand côté + 64, 0, 96, 24, // petit côté + 96, 0, 136, 24, // attention + 0, 32, 8, 160, // tube 1 + 8, 32, 16, 96, // tube 2 + 16, 32, 24, 160, // pilier + 24, 32, 32, 160, // tige foret + 32, 32, 40, 160, // tige destructeur + 8, 96, 16, 128, // foret + 136, 0, 256, 120, // plancher octogonal station de recharge + 40, 32, 64, 56, // cube métal + 64, 24, 128, 48, // côté tour haut + 64, 48, 128, 229, // côté tour bas + 136, 120, 256, 240, // intérieur usine + 0, 160, 64, 224, // toît usine + -1 +}; + +static int tablePartC[] = // convert.tga +{ + 0, 0, 120, 120, // plancher octogonal convertisseur + 0, 120, 128, 176, // grand côté + 128, 120, 192, 176, // petit côté + 192, 120, 256, 184, // couvercle convertisseur + 120, 0, 216, 64, // face trianble + 216, 0, 248, 64, // côté triangle + 120, 64, 160, 84, // axe + 0, 141, 128, 176, // recherche: base + 0, 176, 128, 214, // recherche: haut + 0, 214, 128, 252, // recherche: haut (!) + 174, 64, 190, 120, // recherche: montant + 190, 64, 206, 120, // recherche: montant + 206, 64, 254, 85, // radar + 192, 168, 256, 232, // hachures carrées + 248, 0, 256, 64, // cône fabrique de piles + 128, 176, 192, 240, // dessus centrale nucléaire + 120, 85, 174, 120, // technicien, visage + 206, 106, 256, 120, // technicien, casquette + 160, 64, 174, 78, // technicien, visière + -1 +}; + +static int tablePartS[] = // search.tga +{ + 0, 0, 128, 128, // usine 1 + 128, 0, 256, 128, // usine 2 + 0, 128, 128, 256, // pile + 128, 128, 228, 240, // support pile + 228, 128, 256, 184, // antenne + 128, 128, 192, 160, // contrôle 1 + 128, 160, 192, 192, // contrôle 2 + 128, 192, 192, 224, // contrôle 3 + 128, 224, 192, 256, // contrôle 4 + -1 +}; + +static int tablePartP[] = // plant.tga +{ + 0, 160, 48, 256, // feuille 1 + 0, 0, 94, 100, // feuille 2 + 48, 156, 108, 256, // feuille 3 + 94, 0, 104, 100, // tige 1 + 185, 0, 195, 100, // tige 2 + 108, 100, 182, 256, // fougère + 104, 0, 144, 100, // courge + 203, 0, 256, 83, // armature derrick résolution C + -1 +}; + +static int tablePartV[] = // vegetal.tga +{ + 0, 0, 94, 100, // racine + 186, 0, 256, 256, // tronc + 162, 0, 168, 128, // mat drapeau bleu + 168, 0, 174, 128, // mat drapeau rouge + 174, 0, 180, 128, // mat drapeau vert + 180, 0, 186, 128, // mat drapeau jaune + 180, 128, 186, 256, // mat drapeau violet + 94, 0, 107, 32, // drapeau bleu + 107, 0, 120, 32, // drapeau rouge + 120, 0, 133, 32, // drapeau vert + 133, 0, 146, 32, // drapeau jaune + 146, 0, 159, 32, // drapeau violet + 94, 64, 126, 96, // verre 1 + 126, 64, 158, 86, // verre 2 + 128, 128, 180, 144, // verre 3a + 128, 144, 180, 160, // verre 3b + 128, 94, 162, 128, // verre 4 + 0, 100, 32, 228, // champignon 1 + 32, 100, 48, 228, // champignon 1 + 48, 100, 112, 228, // champignon 2 + 112, 100, 128, 228, // champignon 2 + 128, 160, 180, 212, // tronc (21) + -1 +}; + +static int tablePartM[] = // mother.tga +{ + 0, 0, 128, 128, // corps arrière + 128, 0, 192, 128, // corps avant + 0, 128, 64, 192, // tête + 64, 128, 192, 160, // pince ext. + 64, 160, 192, 192, // pince int. + 0, 192, 64, 256, // mire + -1 +}; + +static int tablePartA[] = // ant.tga +{ + 0, 0, 64, 64, // queue + 0, 96, 128, 160, // queue abeille + 64, 0, 128, 64, // corps + 128, 0, 192, 64, // tête + 0, 64, 64, 72, // patte + 0, 72, 64, 80, // antenne + 64, 64, 150, 96, // queue ver + 150, 64, 182, 96, // corps ver + 182, 64, 256, 96, // tête ver + 224, 32, 256, 64, // articulation ver + 128, 96, 220, 160, // aile + 0, 80, 16, 96, // oeil + 200, 0, 208, 8, // vert clair + 200, 8, 208, 16, // vert foncé + 0, 160, 64, 224, // corps araignée + 64, 160, 128, 192, // tête araignée + 208, 0, 216, 64, // patte araignée + 216, 0, 224, 32, // patte araignée + 224, 0, 256, 8, // antenne araignée + 192, 0, 200, 8, // brun clair + 192, 8, 200, 16, // brun foncé + 128, 160, 256, 256, // SatCom + -1 +}; + +static int tablePartH[] = // human.tga +{ + 0, 0, 64, 64, // vissière + 64, 0, 96, 64, // cuisse + 96, 0, 128, 64, // jambe + 128, 0, 192, 32, // bras + 128, 32, 192, 64, // avant-bras + 0, 64, 128, 224, // ventre + 128, 64, 256, 224, // dos + 64, 224, 112, 256, // dessus pied + 144, 224, 168, 240, // dessous pied + 112, 224, 144, 240, // côté pied + 112, 224, 128, 240, // côté pied + 0, 224, 64, 256, // gant + 168, 224, 200, 256, // oreille + 112, 240, 144, 256, // ligne casque + 200, 224, 208, 256, // intérieur coup + 240, 0, 244, 64, // bombone orange + 244, 0, 248, 64, // bombone orange (reflet) + 248, 0, 252, 64, // bombone bleu + 252, 0, 256, 64, // bombone bleu (reflet) + 144, 240, 156, 256, // gris habit + 156, 240, 168, 256, // gris articulation +//? 208, 224, 256, 256, // SatCom + 192, 0, 240, 64, // quartz + -1 +}; + +static int tablePartG[] = // apollo.tga +{ + 0, 0, 64, 64, // revètement LEM + 64, 0, 128, 64, // revètement LEM + 128, 8, 136, 128, // pied + 0, 64, 64, 128, // roue + 136, 24, 152, 44, // profil pneu + 136, 8, 160, 24, // garde boue + 64, 64, 128, 128, // siège + 64, 128, 128, 192, // siège + 64, 192, 128, 212, // siège + 128, 128, 240, 192, // moteur + 0, 192, 28, 256, // moteur + 32, 128, 60, 256, // moteur + 224, 0, 256, 128, // avant + 206, 0, 224, 128, // avant + 136, 44, 168, 62, // avant + 64, 212, 108, 256, // panneau de commande + 198, 0, 206, 128, // mat + 190, 64, 198, 128, // mat + 160, 8, 176, 24, // caméra + 176, 8, 192, 24, // moyeu + 136, 64, 168, 96, // module + 168, 64, 190, 96, // module + 136, 96, 168, 128, // module + 128, 192, 230, 252, // drapeau + 0, 128, 32, 192, // antenne + 128, 0, 136, 8, // jaune + 136, 0, 144, 8, // beige + 144, 0, 152, 8, // brun + 168, 0, 176, 8, // gris très clair + 152, 0, 160, 8, // gris clair + 160, 0, 168, 8, // gris foncé + -1 +}; + +static int tablePartB[] = // base1.tga +{ + 0, 0, 80, 256, // intérieur porte + 80, 0, 88, 256, // tranche porte + 116, 0, 180, 64, // coiffe 1 + 116, 64, 180, 102, // coiffe 2 + 180, 0, 244, 37, // base + 180, 37, 196, 101, // support + 88, 0, 116, 256, // colonne + 212, 37, 256, 128, // supplément + 128, 128, 256, 256, // 1/4 du sol + 196, 37, 212, 53, // gris foncé + 196, 53, 212, 69, // gris clair + -1 +}; + +static int tablePartCe[] = // cellar01.tga +{ + 0, 128, 64, 192, // briques + 64, 128, 128, 192, // briques + 128, 128, 192, 192, // briques + 192, 128, 256, 192, // briques + -1 +}; + +static int tablePartFa[] = // face01.tga +{ + 0, 0, 256, 256, // visage + -1 +}; + +// Retourne le pointeur la table. + +int* CModel::RetTextureTable() +{ + if ( m_textureRank == 0 ) return tablePartT; + if ( m_textureRank == 1 ) return tablePartR; + if ( m_textureRank == 2 ) return tablePartW; + if ( m_textureRank == 3 ) return tablePartDr; + if ( m_textureRank == 4 ) return tablePartKi; + if ( m_textureRank == 5 ) return tablePartKi2; + if ( m_textureRank == 6 ) return tablePartKi3; + if ( m_textureRank == 7 ) return tablePartF; + if ( m_textureRank == 8 ) return tablePartD; + if ( m_textureRank == 9 ) return tablePartC; + if ( m_textureRank == 10 ) return tablePartS; + if ( m_textureRank == 11 ) return tablePartP; + if ( m_textureRank == 12 ) return tablePartV; + if ( m_textureRank == 13 ) return tablePartM; + if ( m_textureRank == 14 ) return tablePartA; + if ( m_textureRank == 15 ) return tablePartH; + if ( m_textureRank == 16 ) return tablePartG; + if ( m_textureRank == 17 ) return tablePartB; + if ( m_textureRank == 18 ) return tablePartCe; + if ( m_textureRank == 19 ) return tablePartFa; + if ( m_textureRank == 20 ) return tablePartFa; + if ( m_textureRank == 21 ) return tablePartFa; + if ( m_textureRank == 22 ) return tablePartFa; + return 0; +} + +// Met à jour la partie de texture. + +void CModel::TexturePartUpdate() +{ + int *table; + + table = RetTextureTable(); + if ( table == 0 ) return; + + m_textureInf.x = (table[m_texturePart*4+0]+0.5f)/256.0f; + m_textureInf.y = (table[m_texturePart*4+1]+0.5f)/256.0f; + m_textureSup.x = (table[m_texturePart*4+2]-0.5f)/256.0f; + m_textureSup.y = (table[m_texturePart*4+3]-0.5f)/256.0f; + + PutTextureValues(); +} + +// Change la texture. + +void CModel::TextureRankChange(int step) +{ + m_textureRank += step; + + if ( m_textureRank >= MAX_NAMES ) m_textureRank = 0; + if ( m_textureRank < 0 ) m_textureRank = MAX_NAMES-1; + + if ( m_textureRank == 0 ) strcpy(m_textureName, "lemt.tga"); + if ( m_textureRank == 1 ) strcpy(m_textureName, "roller.tga"); + if ( m_textureRank == 2 ) strcpy(m_textureName, "subm.tga"); + if ( m_textureRank == 3 ) strcpy(m_textureName, "drawer.tga"); + if ( m_textureRank == 4 ) strcpy(m_textureName, "kid.tga"); + if ( m_textureRank == 5 ) strcpy(m_textureName, "kid2.tga"); + if ( m_textureRank == 6 ) strcpy(m_textureName, "kid3.tga"); + if ( m_textureRank == 7 ) strcpy(m_textureName, "factory.tga"); + if ( m_textureRank == 8 ) strcpy(m_textureName, "derrick.tga"); + if ( m_textureRank == 9 ) strcpy(m_textureName, "convert.tga"); + if ( m_textureRank == 10 ) strcpy(m_textureName, "search.tga"); + if ( m_textureRank == 11 ) strcpy(m_textureName, "plant.tga"); + if ( m_textureRank == 12 ) strcpy(m_textureName, "vegetal.tga"); + if ( m_textureRank == 13 ) strcpy(m_textureName, "mother.tga"); + if ( m_textureRank == 14 ) strcpy(m_textureName, "ant.tga"); + if ( m_textureRank == 15 ) strcpy(m_textureName, "human.tga"); + if ( m_textureRank == 16 ) strcpy(m_textureName, "apollo.tga"); + if ( m_textureRank == 17 ) strcpy(m_textureName, "base1.tga"); + if ( m_textureRank == 18 ) strcpy(m_textureName, "cellar01.tga"); + if ( m_textureRank == 19 ) strcpy(m_textureName, "face01.tga"); + if ( m_textureRank == 20 ) strcpy(m_textureName, "face02.tga"); + if ( m_textureRank == 21 ) strcpy(m_textureName, "face03.tga"); + if ( m_textureRank == 22 ) strcpy(m_textureName, "face04.tga"); + + m_texturePart = 0; +} + +// Change la partie de texture. + +void CModel::TexturePartChange(int step) +{ + int *table; + + table = RetTextureTable(); + if ( table == 0 ) return; + + m_texturePart ++; + + if ( table[m_texturePart*4] == -1 ) + { + m_texturePart = 0; + } + + TexturePartUpdate(); +} + + diff --git a/src/model.h b/src/model.h new file mode 100644 index 00000000..2f686ee6 --- /dev/null +++ b/src/model.h @@ -0,0 +1,118 @@ +// model.h + +#ifndef _MODEL_H_ +#define _MODEL_H_ + + +class CInstanceManager; +class CD3DEngine; +class CModFile; +class CInterface; + + + +class CModel +{ +public: + CModel(CInstanceManager* iMan); + ~CModel(); + + void StartUserAction(); + void StopUserAction(); + + BOOL EventProcess(const Event &event); + + void InitView(); + void InitViewFromSelect(); + void UpdateView(); + void ViewMove(const Event &event, float speed); + +protected: + BOOL EventFrame(const Event &event); + BOOL GetVertex(int rank, D3DVERTEX2 &vertex); + BOOL SetVertex(int rank, D3DVERTEX2 &vertex); + D3DVECTOR RetSelectCDG(); + D3DVECTOR RetSelectNormal(); + void SmoothSelect(); + void PlaneSelect(); + void ColorSelect(); + void StateSelect(); + void MoveSelect(D3DVECTOR move); + void OperSelect(D3DVECTOR move, char oper); + void ReadScript(char *filename); + void BBoxCompute(D3DVECTOR &min, D3DVECTOR &max); + BOOL IsMappingSelectPlausible(D3DMaping D3Dmode); + void MappingSelect(int mode, int rotate, BOOL bMirrorX, BOOL bMirrorY, FPOINT ti, FPOINT ts, char *texName); + void MappingSelectSpherical(int mode, int rotate, BOOL bMirrorX, BOOL bMirrorY, FPOINT ti, FPOINT ts, char *texName); + D3DVECTOR RetMappingCenter(D3DVECTOR pos, D3DVECTOR min); + void MappingSelectCylindrical(int mode, int rotate, BOOL bMirrorX, BOOL bMirrorY, FPOINT ti, FPOINT ts, char *texName); + void MappingSelectFace(int mode, int rotate, BOOL bMirrorX, BOOL bMirrorY, FPOINT ti, FPOINT ts, char *texName); + void MappingSelect2(int texNum2, int subdiv, int offsetU, int offsetV, BOOL bMirrorX, BOOL bMirrorY); + void MappingSelectPlane2(int mode, BOOL bMirrorX, BOOL bMirrorY); + void MappingSelectSpherical2(BOOL bMirrorX, BOOL bMirrorY); + void MappingSelectMagic2(BOOL bMirrorX, BOOL bMirrorY); + int SearchNext(int rank, int step); + int SearchSamePlane(int first, int step); + void CurrentSearchNext(int step, BOOL bControl); + void CurrentInit(); + void CurrentSelect(BOOL bSelect); + void DeselectAll(); + void SelectAll(); + void SelectZone(int first, int last); + void SelectTerm(); + void DefaultSelect(); + void SelectDelete(); + void Compress(); + void MinMaxSelect(); + void MinMaxChange(); + void UpdateInfoText(); + int* RetTextureTable(); + void TexturePartUpdate(); + void TextureRankChange(int step); + void TexturePartChange(int step); + void PutTextureValues(); + void GetTextureValues(); + void GetModelName(char *buffer); + void GetDXFName(char *buffer); + void GetScriptName(char *buffer); + BOOL IsEditFocus(); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CModFile* m_modFile; + CInterface* m_interface; + + float m_time; + ModelTriangle* m_triangleTable; + int m_triangleSel1; + int m_triangleSel2; + int m_mode; + int m_textureMode; + int m_textureRotate; + BOOL m_bTextureMirrorX; + BOOL m_bTextureMirrorY; + FPOINT m_textureInf; + FPOINT m_textureSup; + int m_texturePart; + int m_textureRank; + char m_textureName[20]; + BOOL m_bDisplayTransparent; + BOOL m_bDisplayOnlySelection; + float m_viewHeight; + float m_viewDist; + float m_viewAngleH; + float m_viewAngleV; + int m_color; + int m_state; + int m_secondTexNum; + int m_secondSubdiv; + int m_secondOffsetU; + int m_secondOffsetV; + char m_oper; + float m_min; + float m_max; +}; + + +#endif //_MODEL_H_ diff --git a/src/modfile.cpp b/src/modfile.cpp new file mode 100644 index 00000000..f54f3c99 --- /dev/null +++ b/src/modfile.cpp @@ -0,0 +1,681 @@ +// modfile.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "language.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "modfile.h" + + + +#define MAX_VERTICES 2000 + + + +// Constructeur de l'objet. + +CModFile::CModFile(CInstanceManager* iMan) +{ + m_iMan = iMan; + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + + m_triangleUsed = 0; + m_triangleTable = (ModelTriangle*)malloc(sizeof(ModelTriangle)*MAX_VERTICES); + ZeroMemory(m_triangleTable, sizeof(ModelTriangle)*MAX_VERTICES); +} + +// Destructeur de l'objet. + +CModFile::~CModFile() +{ + free(m_triangleTable); +} + + + + +// Crée un triangle dans la structure interne. + +BOOL CModFile::CreateTriangle(D3DVECTOR p1, D3DVECTOR p2, D3DVECTOR p3, + float min, float max) +{ + D3DVECTOR n; + int i; + + if ( m_triangleUsed >= MAX_VERTICES ) + { + OutputDebugString("ERROR: CreateTriangle::Too many triangles\n"); + return FALSE; + } + + i = m_triangleUsed++; + + ZeroMemory(&m_triangleTable[i], sizeof(ModelTriangle)); + + m_triangleTable[i].bUsed = TRUE; + m_triangleTable[i].bSelect = FALSE; + + n = ComputeNormal(p3, p2, p1); + m_triangleTable[i].p1 = D3DVERTEX2( p1, n); + m_triangleTable[i].p2 = D3DVERTEX2( p2, n); + m_triangleTable[i].p3 = D3DVERTEX2( p3, n); + + m_triangleTable[i].material.diffuse.r = 1.0f; + m_triangleTable[i].material.diffuse.g = 1.0f; + m_triangleTable[i].material.diffuse.b = 1.0f; // blanc + m_triangleTable[i].material.ambient.r = 0.5f; + m_triangleTable[i].material.ambient.g = 0.5f; + m_triangleTable[i].material.ambient.b = 0.5f; + + m_triangleTable[i].min = min; + m_triangleTable[i].max = max; + + return TRUE; +} + +// Lit un fichier DXF. + +BOOL CModFile::ReadDXF(char *filename, float min, float max) +{ + FILE* file = NULL; + char line[100]; + int command, rankSommet, nbSommet, nbFace; + D3DVECTOR table[MAX_VERTICES]; + BOOL bWaitNbSommet; + BOOL bWaitNbFace; + BOOL bWaitSommetX; + BOOL bWaitSommetY; + BOOL bWaitSommetZ; + BOOL bWaitFaceX; + BOOL bWaitFaceY; + BOOL bWaitFaceZ; + float x,y,z; + int p1,p2,p3; + + file = fopen(filename, "r"); + if ( file == NULL ) return FALSE; + + m_triangleUsed = 0; + + rankSommet = 0; + bWaitNbSommet = FALSE; + bWaitNbFace = FALSE; + bWaitSommetX = FALSE; + bWaitSommetY = FALSE; + bWaitSommetZ = FALSE; + bWaitFaceX = FALSE; + bWaitFaceY = FALSE; + bWaitFaceZ = FALSE; + + while ( fgets(line, 100, file) != NULL ) + { + sscanf(line, "%d", &command); + if ( fgets(line, 100, file) == NULL ) break; + + if ( command == 66 ) + { + bWaitNbSommet = TRUE; + } + + if ( command == 71 && bWaitNbSommet ) + { + bWaitNbSommet = FALSE; + sscanf(line, "%d", &nbSommet); + if ( nbSommet > MAX_VERTICES ) nbSommet = MAX_VERTICES; + rankSommet = 0; + bWaitNbFace = TRUE; + +//? sprintf(s, "Waiting for %d sommets\n", nbSommet); +//? OutputDebugString(s); + } + + if ( command == 72 && bWaitNbFace ) + { + bWaitNbFace = FALSE; + sscanf(line, "%d", &nbFace); + bWaitSommetX = TRUE; + +//? sprintf(s, "Waiting for %d faces\n", nbFace); +//? OutputDebugString(s); + } + + if ( command == 10 && bWaitSommetX ) + { + bWaitSommetX = FALSE; + sscanf(line, "%f", &x); + bWaitSommetY = TRUE; + } + + if ( command == 20 && bWaitSommetY ) + { + bWaitSommetY = FALSE; + sscanf(line, "%f", &y); + bWaitSommetZ = TRUE; + } + + if ( command == 30 && bWaitSommetZ ) + { + bWaitSommetZ = FALSE; + sscanf(line, "%f", &z); + + nbSommet --; + if ( nbSommet >= 0 ) + { + D3DVECTOR p(x,z,y); // permutation de Y et Z ! + table[rankSommet++] = p; + bWaitSommetX = TRUE; + +//? sprintf(s, "Sommet[%d]=%f;%f;%f\n", rankSommet, p.x,p.y,p.z); +//? OutputDebugString(s); + } + else + { + bWaitFaceX = TRUE; + } + } + + if ( command == 71 && bWaitFaceX ) + { + bWaitFaceX = FALSE; + sscanf(line, "%d", &p1); + if ( p1 < 0 ) p1 = -p1; + bWaitFaceY = TRUE; + } + + if ( command == 72 && bWaitFaceY ) + { + bWaitFaceY = FALSE; + sscanf(line, "%d", &p2); + if ( p2 < 0 ) p2 = -p2; + bWaitFaceZ = TRUE; + } + + if ( command == 73 && bWaitFaceZ ) + { + bWaitFaceZ = FALSE; + sscanf(line, "%d", &p3); + if ( p3 < 0 ) p3 = -p3; + + nbFace --; + if ( nbFace >= 0 ) + { + CreateTriangle( table[p3-1], table[p2-1], table[p1-1], min,max ); + bWaitFaceX = TRUE; + +//? sprintf(s, "Face=%d;%d;%d\n", p1,p2,p3); +//? OutputDebugString(s); + } + } + + } + + fclose(file); + return TRUE; +} + + + +typedef struct +{ + int rev; + int vers; + int total; + int reserve[10]; +} +InfoMOD; + + +// Change un nom.bmp en nom.tga + +void ChangeBMPtoTGA(char *filename) +{ + char* p; + + p = strstr(filename, ".bmp"); + if ( p != 0 ) strcpy(p, ".tga"); +} + + +// Lit un fichier MOD. + +BOOL CModFile::AddModel(char *filename, int first, BOOL bEdit, BOOL bMeta) +{ + FILE* file; + InfoMOD info; + float limit[2]; + int i, nb, err; + char* p; + + if ( m_engine->RetDebugMode() ) + { + bMeta = FALSE; + } + + if ( bMeta ) + { + p = strchr(filename, '\\'); + if ( p == 0 ) + { +#if _SCHOOL + err = g_metafile.Open("ceebot2.dat", filename); +#else + err = g_metafile.Open("colobot2.dat", filename); +#endif + } + else + { +#if _SCHOOL + err = g_metafile.Open("ceebot2.dat", p+1); +#else + err = g_metafile.Open("colobot2.dat", p+1); +#endif + } + if ( err != 0 ) bMeta = FALSE; + } + if ( !bMeta ) + { + file = fopen(filename, "rb"); + if ( file == NULL ) return FALSE; + } + + if ( bMeta ) + { + g_metafile.Read(&info, sizeof(InfoMOD)); + } + else + { + fread(&info, sizeof(InfoMOD), 1, file); + } + nb = info.total; + m_triangleUsed += nb; + + if ( info.rev == 1 && info.vers == 0 ) + { + OldModelTriangle1 old; + + for ( i=first ; iRetLimitLOD(0); // frontière AB selon config + limit[1] = m_engine->RetLimitLOD(1); // frontière BC selon config + + // Frontières standard -> config. + for ( i=first ; iRetSecondTexture(); + } + else + { + texNum = m_triangleTable[i].texNum2; + } + + if ( texNum >= 1 && texNum <= 10 ) + { + state = m_triangleTable[i].state|D3DSTATEDUALb; + } + if ( texNum >= 11 && texNum <= 20 ) + { + state = m_triangleTable[i].state|D3DSTATEDUALw; + } + sprintf(texName2, "dirty%.2d.bmp", texNum); + } + + m_engine->AddTriangle(objRank, &m_triangleTable[i].p1, 3, + m_triangleTable[i].material, + state+addState, + m_triangleTable[i].texName, texName2, + m_triangleTable[i].min, + m_triangleTable[i].max, FALSE); + } + return TRUE; +#else + char texName1[20]; + char texName2[20]; + int texNum, i, state; + + for ( i=0 ; iRetSecondTexture(); + } + else + { + texNum = m_triangleTable[i].texNum2; + } + + if ( texNum >= 1 && texNum <= 10 ) + { + state |= D3DSTATEDUALb; + } + if ( texNum >= 11 && texNum <= 20 ) + { + state |= D3DSTATEDUALw; + } + sprintf(texName2, "dirty%.2d.tga", texNum); + } + + m_engine->AddTriangle(objRank, &m_triangleTable[i].p1, 3, + m_triangleTable[i].material, + state+addState, + texName1, texName2, + m_triangleTable[i].min, + m_triangleTable[i].max, FALSE); + } + return TRUE; +#endif +} + + +// Effectue un miroir selon Z. + +void CModFile::Mirror() +{ + D3DVERTEX2 t; + int i; + + for ( i=0 ; i utilisé + char bSelect; // TRUE -> sélectionné + D3DVERTEX p1; + D3DVERTEX p2; + D3DVERTEX p3; + D3DMATERIAL7 material; + char texName[20]; + float min; + float max; +} +OldModelTriangle1; // longueur = 196 bytes + +typedef struct +{ + char bUsed; // TRUE -> utilisé + char bSelect; // TRUE -> sélectionné + D3DVERTEX p1; + D3DVERTEX p2; + D3DVERTEX p3; + D3DMATERIAL7 material; + char texName[20]; + float min; + float max; + long state; + short reserve1; + short reserve2; + short reserve3; + short reserve4; +} +OldModelTriangle2; + +typedef struct +{ + char bUsed; // TRUE -> utilisé + char bSelect; // TRUE -> sélectionné + D3DVERTEX2 p1; + D3DVERTEX2 p2; + D3DVERTEX2 p3; + D3DMATERIAL7 material; + char texName[20]; + float min; + float max; + long state; + short texNum2; + short reserve2; + short reserve3; + short reserve4; +} +ModelTriangle; // longueur = 208 bytes + + + + +class CModFile +{ +public: + CModFile(CInstanceManager* iMan); + ~CModFile(); + + BOOL ReadDXF(char *filename, float min, float max); + BOOL AddModel(char *filename, int first, BOOL bEdit=FALSE, BOOL bMeta=TRUE); + BOOL ReadModel(char *filename, BOOL bEdit=FALSE, BOOL bMeta=TRUE); + BOOL WriteModel(char *filename); + + BOOL CreateEngineObject(int objRank, int addState=0); + void Mirror(); + + void SetTriangleUsed(int total); + int RetTriangleUsed(); + int RetTriangleMax(); + ModelTriangle* RetTriangleList(); + + float RetHeight(D3DVECTOR pos); + +protected: + BOOL CreateTriangle(D3DVECTOR p1, D3DVECTOR p2, D3DVECTOR p3, float min, float max); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + + ModelTriangle* m_triangleTable; + int m_triangleUsed; +}; + + +#endif //_MODFILE_H_ diff --git a/src/motion.cpp b/src/motion.cpp new file mode 100644 index 00000000..5553f69d --- /dev/null +++ b/src/motion.cpp @@ -0,0 +1,241 @@ +// motion.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "water.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "robotmain.h" +#include "sound.h" +#include "cmdtoken.h" +#include "motion.h" + + + + +// Constructeur de l'objet. + +CMotion::CMotion(CInstanceManager* iMan, CObject* object) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_MOTION, this, 100); + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_light = (CLight*)m_iMan->SearchInstance(CLASS_LIGHT); + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + + m_object = object; + m_physics = 0; + m_brain = 0; + + m_actionType = -1; + m_actionTime = 0.0f; + m_progress = 0.0f; + + m_linVibration = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_cirVibration = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_inclinaison = D3DVECTOR(0.0f, 0.0f, 0.0f); +} + +// Destructeur de l'objet. + +CMotion::~CMotion() +{ + m_iMan->DeleteInstance(CLASS_MOTION, this); +} + +// Supprime l'objet. + +void CMotion::DeleteObject(BOOL bAll) +{ +} + + +void CMotion::SetPhysics(CPhysics* physics) +{ + m_physics = physics; +} + +void CMotion::SetBrain(CBrain* brain) +{ + m_brain = brain; +} + + +// Crée. + +BOOL CMotion::Create(D3DVECTOR pos, float angle, ObjectType type, float power) +{ + return TRUE; +} + +// Gestion d'un événement. + +BOOL CMotion::EventProcess(const Event &event) +{ + D3DVECTOR pos, dir; + float time; + + if ( m_object->RetType() != OBJECT_TOTO && + m_engine->RetPause() ) return TRUE; + + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_actionTime; + if ( m_progress > 1.0f ) m_progress = 1.0f; // (*) + + pos = m_object->RetPosition(0); + if ( pos.y < m_water->RetLevel(m_object) ) // sous l'eau ? + { + time = event.rTime*3.0f; // tout est plus lent + } + else + { + time = event.rTime*10.0f; + } + + dir = m_object->RetLinVibration(); + dir.x = Smooth(dir.x, m_linVibration.x, time); + dir.y = Smooth(dir.y, m_linVibration.y, time); + dir.z = Smooth(dir.z, m_linVibration.z, time); + m_object->SetLinVibration(dir); + + dir = m_object->RetCirVibration(); + dir.x = Smooth(dir.x, m_cirVibration.x, time); + dir.y = Smooth(dir.y, m_cirVibration.y, time); + dir.z = Smooth(dir.z, m_cirVibration.z, time); + m_object->SetCirVibration(dir); + + dir = m_object->RetInclinaison(); + dir.x = Smooth(dir.x, m_inclinaison.x, time); + dir.y = Smooth(dir.y, m_inclinaison.y, time); + dir.z = Smooth(dir.z, m_inclinaison.z, time); + m_object->SetInclinaison(dir); + + return TRUE; +} + +// (*) Evite le bug des fourmis retournées par le thumper et dont +// l'abdomen grossi à l'infini ! + + +// Démarre une action. + +Error CMotion::SetAction(int action, float time) +{ + m_actionType = action; + m_actionTime = 1.0f/time; + m_progress = 0.0f; + return ERR_OK; +} + +// Retourne l'action en cours. + +int CMotion::RetAction() +{ + return m_actionType; +} + + +// Spécifie un paramètre spécial. + +BOOL CMotion::SetParam(int rank, float value) +{ + return FALSE; +} + +float CMotion::RetParam(int rank) +{ + return 0.0f; +} + + +// Sauve tous les paramètres de l'objet. + +BOOL CMotion::Write(char *line) +{ + char name[100]; + + if ( m_actionType == -1 ) return FALSE; + + sprintf(name, " mType=%d", m_actionType); + strcat(line, name); + + sprintf(name, " mTime=%.2f", m_actionTime); + strcat(line, name); + + sprintf(name, " mProgress=%.2f", m_progress); + strcat(line, name); + + return FALSE; +} + +// Restitue tous les paramètres de l'objet. + +BOOL CMotion::Read(char *line) +{ + m_actionType = OpInt(line, "mType", -1); + m_actionTime = OpFloat(line, "mTime", 0.0f); + m_progress = OpFloat(line, "mProgress", 0.0f); + + return FALSE; +} + + +// Donne la vibration linéaire. + +void CMotion::SetLinVibration(D3DVECTOR dir) +{ + m_linVibration = dir; +} + +D3DVECTOR CMotion::RetLinVibration() +{ + return m_linVibration; +} + +// Donne la vibration circulaire. + +void CMotion::SetCirVibration(D3DVECTOR dir) +{ + m_cirVibration = dir; +} + +D3DVECTOR CMotion::RetCirVibration() +{ + return m_cirVibration; +} + +// Donne l'inclinaison. + +void CMotion::SetInclinaison(D3DVECTOR dir) +{ + m_inclinaison = dir; +} + +D3DVECTOR CMotion::RetInclinaison() +{ + return m_inclinaison; +} + diff --git a/src/motion.h b/src/motion.h new file mode 100644 index 00000000..dcbd5950 --- /dev/null +++ b/src/motion.h @@ -0,0 +1,75 @@ +// motion.h + +#ifndef _MOTION_H_ +#define _MOTION_H_ + + +class CInstanceManager; +class CEngine; +class CLight; +class CParticule; +class CTerrain; +class CWater; +class CCamera; +class CBrain; +class CPhysics; +class CObject; +class CRobotMain; +class CSound; + + +class CMotion +{ +public: + CMotion(CInstanceManager* iMan, CObject* object); + virtual ~CMotion(); + + void SetPhysics(CPhysics* physics); + void SetBrain(CBrain* brain); + + virtual void DeleteObject(BOOL bAll=FALSE); + virtual BOOL Create(D3DVECTOR pos, float angle, ObjectType type, float power); + virtual BOOL EventProcess(const Event &event); + virtual Error SetAction(int action, float time=0.2f); + virtual int RetAction(); + + virtual BOOL SetParam(int rank, float value); + virtual float RetParam(int rank); + + virtual BOOL Write(char *line); + virtual BOOL Read(char *line); + + virtual void SetLinVibration(D3DVECTOR dir); + virtual D3DVECTOR RetLinVibration(); + virtual void SetCirVibration(D3DVECTOR dir); + virtual D3DVECTOR RetCirVibration(); + virtual void SetInclinaison(D3DVECTOR dir); + virtual D3DVECTOR RetInclinaison(); + +protected: + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CLight* m_light; + CParticule* m_particule; + CTerrain* m_terrain; + CWater* m_water; + CCamera* m_camera; + CObject* m_object; + CBrain* m_brain; + CPhysics* m_physics; + CRobotMain* m_main; + CSound* m_sound; + + int m_actionType; + float m_actionTime; + float m_progress; + + D3DVECTOR m_linVibration; // vibration linéaire + D3DVECTOR m_cirVibration; // vibration circulaire + D3DVECTOR m_inclinaison; // inclinaison +}; + + +#endif //_MOTION_H_ diff --git a/src/motionant.cpp b/src/motionant.cpp new file mode 100644 index 00000000..4fd76f6f --- /dev/null +++ b/src/motionant.cpp @@ -0,0 +1,886 @@ +// motionant.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "modfile.h" +#include "sound.h" +#include "motion.h" +#include "motionant.h" + + + +#define ADJUST_ANGLE FALSE // TRUE -> ajuste les angles des membres +#define START_TIME 1000.0f // début du temps relatif + + + +// Constructeur de l'objet. + +CMotionAnt::CMotionAnt(CInstanceManager* iMan, CObject* object) + : CMotion(iMan, object) +{ + CMotion::CMotion(iMan, object); + + m_armMember = START_TIME; + m_armTimeAbs = START_TIME; + m_armTimeMarch = START_TIME; + m_armTimeAction = START_TIME; + m_armTimeIndex = 0; + m_armPartIndex = 0; + m_armMemberIndex = 0; + m_armLastAction = -1; + m_bArmStop = FALSE; + m_lastParticule = 0.0f; +} + +// Destructeur de l'objet. + +CMotionAnt::~CMotionAnt() +{ +} + + +// Supprime un objet. + +void CMotionAnt::DeleteObject(BOOL bAll) +{ +} + + +// Crée un véhicule roulant quelconque posé sur le sol. + +BOOL CMotionAnt::Create(D3DVECTOR pos, float angle, ObjectType type, + float power) +{ + CModFile* pModFile; + int rank; + + if ( m_engine->RetRestCreate() < 3+18 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + m_object->SetType(type); + + // Crée la base principale. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEVEHICULE); // c'est un objet mobile + m_object->SetObjectRank(0, rank); + + pModFile->ReadModel("objects\\ant1.mod"); + pModFile->CreateEngineObject(rank); + + m_object->SetPosition(0, pos); + m_object->SetAngleY(0, angle); + + // Un véhicule doit avoir obligatoirement une sphère de + // collision avec un centre (0;y;0) (voir GetCrashSphere). + m_object->CreateCrashSphere(D3DVECTOR(0.0f, -2.0f, 0.0f), 4.0f, SOUND_BOUM, 0.20f); + m_object->SetGlobalSphere(D3DVECTOR(-0.5f, 1.0f, 0.0f), 4.0f); + + // Crée la tête. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\ant2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(2.0f, 0.0f, 0.0f)); + + // Crée la queue. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 0); + pModFile->ReadModel("objects\\ant3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(-1.0f, 0.0f, 0.0f)); + + // Crée la cuisse 1 arrière-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(3, rank); + m_object->SetObjectParent(3, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(3, D3DVECTOR(-0.4f, -0.1f, -0.3f)); + + // Crée la jambe 1 arrière-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(4, rank); + m_object->SetObjectParent(4, 3); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(4, D3DVECTOR(0.0f, 0.0f, -1.0f)); + + // Crée le pied 1 arrière-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(5, rank); + m_object->SetObjectParent(5, 4); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(5, D3DVECTOR(0.0f, 0.0f, -2.0f)); + + // Crée la cuisse 2 milieu-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(0.1f, -0.1f, -0.4f)); + + // Crée la jambe 2 milieu-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 6); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(0.0f, 0.0f, -1.0f)); + + // Crée le pied 2 milieu-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(8, rank); + m_object->SetObjectParent(8, 7); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(8, D3DVECTOR(0.0f, 0.0f, -2.0f)); + + // Crée la cuisse 3 avant-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(9, rank); + m_object->SetObjectParent(9, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(9, D3DVECTOR(1.4f, -0.1f, -0.6f)); + + // Crée la jambe 3 avant-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(10, rank); + m_object->SetObjectParent(10, 9); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(10, D3DVECTOR(0.0f, 0.0f, -1.0f)); + + // Crée le pied 3 avant-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(11, rank); + m_object->SetObjectParent(11, 10); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(11, D3DVECTOR(0.0f, 0.0f, -2.0f)); + + // Crée la cuisse 1 arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(12, rank); + m_object->SetObjectParent(12, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(12, D3DVECTOR(-0.4f, -0.1f, 0.3f)); + + // Crée la jambe 1 arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(13, rank); + m_object->SetObjectParent(13, 12); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(13, D3DVECTOR(0.0f, 0.0f, 1.0f)); + + // Crée le pied 1 arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(14, rank); + m_object->SetObjectParent(14, 13); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(14, D3DVECTOR(0.0f, 0.0f, 2.0f)); + + // Crée la cuisse 2 milieu-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(15, rank); + m_object->SetObjectParent(15, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(15, D3DVECTOR(0.1f, -0.1f, 0.4f)); + + // Crée la jambe 2 milieu-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(16, rank); + m_object->SetObjectParent(16, 15); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(16, D3DVECTOR(0.0f, 0.0f, 1.0f)); + + // Crée le pied 2 milieu-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(17, rank); + m_object->SetObjectParent(17, 16); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(17, D3DVECTOR(0.0f, 0.0f, 2.0f)); + + // Crée la cuisse 3 avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(18, rank); + m_object->SetObjectParent(18, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(18, D3DVECTOR(1.4f, -0.1f, 0.6f)); + + // Crée la jambe 3 avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(19, rank); + m_object->SetObjectParent(19, 18); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(19, D3DVECTOR(0.0f, 0.0f, 1.0f)); + + // Crée le pied 3 avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(20, rank); + m_object->SetObjectParent(20, 19); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(20, D3DVECTOR(0.0f, 0.0f, 2.0f)); + + m_object->CreateShadowCircle(4.0f, 0.5f); + + CreatePhysics(); + m_object->SetFloorHeight(0.0f); + + pos = m_object->RetPosition(0); + m_object->SetPosition(0, pos); // pour afficher les ombres tout de suite + + m_engine->LoadAllTexture(); + + delete pModFile; + return TRUE; +} + +// Crée la physique de l'objet. + +void CMotionAnt::CreatePhysics() +{ + Character* character; + int i; + + int member_march[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, // en l'air : + 0,45,0, 0,45,0, 0,50,0, // t0: cuisses 1..3 + 30,-70,0, 20,-105,20, 25,-100,0, // t0: jambes 1..3 + -70,75,0, -30,80,0, -80,80,0, // t0: pieds 1..3 + // au sol devant : + 0,30,0, 0,20,0, 0,15,0, // t1: cuisses 1..3 + -15,-50,0, -20,-60,0, -10,-75,0, // t1: jambes 1..3 + -40,50,0, -25,15,0, -50,35,0, // t1: pieds 1..3 + // au sol derrière : + 0,35,0, 0,30,0, 0,20,0, // t2: cuisses 1..3 + -20,-15,0, -30,-55,0, -25,-70,15, // t2: jambes 1..3 + -25,25,0, -20,60,0, -30,95,0, // t2: pieds 1..3 + }; + + int member_stop[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, // en l'air : + 0,30,0, 0,20,0, 0,15,0, // t0: cuisses 1..3 + -15,-35,0, -20,-60,0, -15,-75,0, // t0: jambes 1..3 + -35,35,0, -25,40,0, -40,65,0, // t0: pieds 1..3 + // au sol devant : + 0,30,0, 0,20,0, 0,15,0, // t1: cuisses 1..3 + -15,-35,0, -20,-60,0, -15,-75,0, // t1: jambes 1..3 + -35,35,0, -25,40,0, -40,65,0, // t1: pieds 1..3 + // au sol derrière : + 0,30,0, 0,20,0, 0,15,0, // t2: cuisses 1..3 + -15,-35,0, -20,-60,0, -15,-75,0, // t2: jambes 1..3 + -35,35,0, -25,40,0, -40,65,0, // t2: pieds 1..3 + }; + + int member_spec[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, // prépare le tir : + 0,20,0, 0,10,0, 0,50,0, // s0: cuisses 1..3 + -50,-30,0, -20,-15,0, 35,-65,0, // s0: jambes 1..3 + -5,-40,0, 20,-70,0, -10,-40,0, // s0: pieds 1..3 + // tir : + 0,20,0, 0,10,0, 0,50,0, // s1: cuisses 1..3 + -50,-30,0, -20,-15,0, 35,-65,0, // s1: jambes 1..3 + -5,-40,0, 20,-70,0, -10,-40,0, // s1: pieds 1..3 + // termine le tir : + 0,30,0, 0,20,0, 0,15,0, // s2: cuisses 1..3 + -15,-50,0, -20,-60,0, -10,-75,0, // s2: jambes 1..3 + -40,50,0, -25,15,0, -50,35,0, // s2: pieds 1..3 + // brûle : + 0,30,0, 0,20,0, 0,15,0, // s3: cuisses 1..3 + -15,-35,0, -20,-60,0, -15,-75,0, // s3: jambes 1..3 + -35,35,0, -25,40,0, -40,65,0, // s3: pieds 1..3 + // ruine : + 0,30,0, 0,20,0, 0,15,0, // s4: cuisses 1..3 + -15,-35,0, -20,-60,0, -15,-75,0, // s4: jambes 1..3 + -35,35,0, -25,40,0, -40,65,0, // s4: pieds 1..3 + // back1 : + 0,30,0, 0,20,0, 0,15,0, // s5: cuisses 1..3 + -15,-35,0, -20,-60,0, -15,-75,0, // s5: jambes 1..3 + -35,35,0, -25,40,0, -40,65,0, // s5: pieds 1..3 + // back2 : + 0,45,0, 0,45,0, 0,50,0, // s6: cuisses 1..3 + -35,-70,0, -20,-85,-25,-25,-100,0, // s6: jambes 1..3 + -110,75,-15,-130,80,-25,-125,40,0, // s6: pieds 1..3 + // back3 : + 0,30,0, 0,20,0, 0,15,0, // s7: cuisses 1..3 + -15,-35,0, -20,-60,0, -15,-75,0, // s7: jambes 1..3 + -35,35,0, -25,40,0, -40,65,0, // s7: pieds 1..3 + }; + + m_physics->SetType(TYPE_ROLLING); + + character = m_object->RetCharacter(); + character->wheelFront = 3.0f; + character->wheelBack = 3.0f; + character->wheelLeft = 5.0f; + character->wheelRight = 5.0f; + character->height = 1.2f; + + m_physics->SetLinMotionX(MO_ADVSPEED, 12.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 12.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 15.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 15.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 5.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 5.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 10.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 1.0f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 1.0f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 20.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 20.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 40.0f); + + for ( i=0 ; i<3*3*3*3 ; i++ ) + { + m_armAngles[3*3*3*3*MA_MARCH+i] = member_march[i]; + } + for ( i=0 ; i<3*3*3*3 ; i++ ) + { + m_armAngles[3*3*3*3*MA_STOP+i] = member_stop[i]; + } + for ( i=0 ; i<3*3*3*8 ; i++ ) + { + m_armAngles[3*3*3*3*MA_SPEC+i] = member_spec[i]; + } +} + + +// Gestion d'un événement. + +BOOL CMotionAnt::EventProcess(const Event &event) +{ + CMotion::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + + if ( event.event == EVENT_KEYDOWN ) + { +#if ADJUST_ANGLE + int i; + + if ( event.param == 'A' ) m_armTimeIndex++; + if ( m_armTimeIndex >= 3 ) m_armTimeIndex = 0; + + if ( event.param == 'Q' ) m_armPartIndex++; + if ( m_armPartIndex >= 3 ) m_armPartIndex = 0; + + if ( event.param == 'W' ) m_armMemberIndex++; + if ( m_armMemberIndex >= 3 ) m_armMemberIndex = 0; + + i = m_armMemberIndex*3; + i += m_armPartIndex*3*3; + i += m_armTimeIndex*3*3*3; +//? i += 3*3*3*3; + + if ( event.param == 'E' ) m_armAngles[i+0] += 5; + if ( event.param == 'D' ) m_armAngles[i+0] -= 5; + if ( event.param == 'R' ) m_armAngles[i+1] += 5; + if ( event.param == 'F' ) m_armAngles[i+1] -= 5; + if ( event.param == 'T' ) m_armAngles[i+2] += 5; + if ( event.param == 'G' ) m_armAngles[i+2] -= 5; + + if ( event.param == 'Y' ) m_bArmStop = !m_bArmStop; +#endif + } + + return TRUE; +} + +// Calcule une valeur (radians) proportionnelle comprise +// entre a et b (degrés). + +inline float Propf(float a, float b, float p) +{ + float aa, bb; + + aa = a*PI/180.0f; + bb = b*PI/180.0f; + + return aa+p*(bb-aa); +} + +// Gestion d'un événement. + +BOOL CMotionAnt::EventFrame(const Event &event) +{ + D3DVECTOR dir, pos, speed; + FPOINT dim; + float s, a, prog, time; + float tSt[9], tNd[9]; + int i, ii, st, nd, action; + BOOL bStop; + + if ( m_engine->RetPause() ) return TRUE; + if ( !m_engine->IsVisiblePoint(m_object->RetPosition(0)) ) return TRUE; + + s = m_physics->RetLinMotionX(MO_MOTSPEED)*1.5f; + a = Abs(m_physics->RetCirMotionY(MO_MOTSPEED)*2.0f); + + if ( s == 0.0f && a != 0.0f ) a *= 1.5f; + + m_armTimeAbs += event.rTime; + m_armTimeMarch += (s)*event.rTime*0.15f; + m_armMember += (s+a)*event.rTime*0.15f; + + bStop = ( a == 0.0f && s == 0.0f ); // a l'arrêt ? + + action = MA_MARCH; // marche + if ( s == 0.0f && a == 0.0f ) + { + action = MA_STOP; // stop + } + + if ( bStop ) + { + prog = Mod(m_armTimeAbs, 2.0f)/10.0f; + a = Mod(m_armMember, 1.0f); + a = (prog-a)*event.rTime*2.0f; // vient gentiment à position stop + m_armMember += a; + } + + if ( m_object->RetRuin() ) // ruine ? + { + m_actionType = MAS_RUIN; + } + if ( m_object->RetBurn() ) // brûle ? + { + if ( m_object->RetFixed() ) + { + m_actionType = MAS_BURN; + } + else + { + m_actionType = -1; + } + } + + for ( i=0 ; i<6 ; i++ ) // les 6 pattes + { + if ( m_actionType != -1 ) // action spéciale en cours ? + { + st = 3*3*3*3*MA_SPEC + 3*3*3*m_actionType + (i%3)*3; + nd = st; + time = event.rTime*m_actionTime; + m_armTimeAction = 0.0f; + } + else + { + if ( i < 3 ) prog = Mod(m_armMember+(2.0f-(i%3))*0.33f+0.0f, 1.0f); + else prog = Mod(m_armMember+(2.0f-(i%3))*0.33f+0.3f, 1.0f); + if ( m_bArmStop ) + { + prog = (float)m_armTimeIndex/3.0f; + } + if ( prog < 0.33f ) // t0..t1 ? + { + prog = prog/0.33f; // 0..1 + st = 0; // index start + nd = 1; // index end + } + else if ( prog < 0.67f ) // t1..t2 ? + { + prog = (prog-0.33f)/0.33f; // 0..1 + st = 1; // index start + nd = 2; // index end + } + else // t2..t0 ? + { + prog = (prog-0.67f)/0.33f; // 0..1 + st = 2; // index start + nd = 0; // index end + } + st = 3*3*3*3*action + st*3*3*3 + (i%3)*3; + nd = 3*3*3*3*action + nd*3*3*3 + (i%3)*3; + + // De moins en moins mou ... + time = event.rTime*(10.0f+Min(m_armTimeAction*100.0f, 200.0f)); + } + + tSt[0] = m_armAngles[st+ 0]; // x + tSt[1] = m_armAngles[st+ 1]; // y + tSt[2] = m_armAngles[st+ 2]; // z + tSt[3] = m_armAngles[st+ 9]; // x + tSt[4] = m_armAngles[st+10]; // y + tSt[5] = m_armAngles[st+11]; // z + tSt[6] = m_armAngles[st+18]; // x + tSt[7] = m_armAngles[st+19]; // y + tSt[8] = m_armAngles[st+20]; // z + + tNd[0] = m_armAngles[nd+ 0]; // x + tNd[1] = m_armAngles[nd+ 1]; // y + tNd[2] = m_armAngles[nd+ 2]; // z + tNd[3] = m_armAngles[nd+ 9]; // x + tNd[4] = m_armAngles[nd+10]; // y + tNd[5] = m_armAngles[nd+11]; // z + tNd[6] = m_armAngles[nd+18]; // x + tNd[7] = m_armAngles[nd+19]; // y + tNd[8] = m_armAngles[nd+20]; // z + + if ( m_actionType == MAS_BACK2 ) // sur le dos ? + { + for ( ii=0 ; ii<9 ; ii++ ) + { + tSt[ii] += Rand()*50.0f; + tNd[ii] = tSt[ii]; + } +//? time = 100.0f; + time = event.rTime*10.0f; + } + + if ( i < 3 ) // patte droite (1..3) ? + { + m_object->SetAngleX(3+3*i+0, Smooth(m_object->RetAngleX(3+3*i+0), Propf(tSt[0], tNd[0], prog), time)); + m_object->SetAngleY(3+3*i+0, Smooth(m_object->RetAngleY(3+3*i+0), Propf(tSt[1], tNd[1], prog), time)); + m_object->SetAngleZ(3+3*i+0, Smooth(m_object->RetAngleZ(3+3*i+0), Propf(tSt[2], tNd[2], prog), time)); + m_object->SetAngleX(3+3*i+1, Smooth(m_object->RetAngleX(3+3*i+1), Propf(tSt[3], tNd[3], prog), time)); + m_object->SetAngleY(3+3*i+1, Smooth(m_object->RetAngleY(3+3*i+1), Propf(tSt[4], tNd[4], prog), time)); + m_object->SetAngleZ(3+3*i+1, Smooth(m_object->RetAngleZ(3+3*i+1), Propf(tSt[5], tNd[5], prog), time)); + m_object->SetAngleX(3+3*i+2, Smooth(m_object->RetAngleX(3+3*i+2), Propf(tSt[6], tNd[6], prog), time)); + m_object->SetAngleY(3+3*i+2, Smooth(m_object->RetAngleY(3+3*i+2), Propf(tSt[7], tNd[7], prog), time)); + m_object->SetAngleZ(3+3*i+2, Smooth(m_object->RetAngleZ(3+3*i+2), Propf(tSt[8], tNd[8], prog), time)); + } + else // patte gauche (4..6) ? + { + m_object->SetAngleX(3+3*i+0, Smooth(m_object->RetAngleX(3+3*i+0), Propf(-tSt[0], -tNd[0], prog), time)); + m_object->SetAngleY(3+3*i+0, Smooth(m_object->RetAngleY(3+3*i+0), Propf(-tSt[1], -tNd[1], prog), time)); + m_object->SetAngleZ(3+3*i+0, Smooth(m_object->RetAngleZ(3+3*i+0), Propf( tSt[2], tNd[2], prog), time)); + m_object->SetAngleX(3+3*i+1, Smooth(m_object->RetAngleX(3+3*i+1), Propf(-tSt[3], -tNd[3], prog), time)); + m_object->SetAngleY(3+3*i+1, Smooth(m_object->RetAngleY(3+3*i+1), Propf(-tSt[4], -tNd[4], prog), time)); + m_object->SetAngleZ(3+3*i+1, Smooth(m_object->RetAngleZ(3+3*i+1), Propf( tSt[5], tNd[5], prog), time)); + m_object->SetAngleX(3+3*i+2, Smooth(m_object->RetAngleX(3+3*i+2), Propf(-tSt[6], -tNd[6], prog), time)); + m_object->SetAngleY(3+3*i+2, Smooth(m_object->RetAngleY(3+3*i+2), Propf(-tSt[7], -tNd[7], prog), time)); + m_object->SetAngleZ(3+3*i+2, Smooth(m_object->RetAngleZ(3+3*i+2), Propf( tSt[8], tNd[8], prog), time)); + } + } + +#if ADJUST_ANGLE + if ( m_object->RetSelect() ) + { + char s[100]; + sprintf(s, "A:time=%d Q:part=%d W:member=%d", m_armTimeIndex, m_armPartIndex, m_armMemberIndex); + m_engine->SetInfoText(4, s); + } +#endif + + if ( m_actionType == MAS_PREPARE ) // prépare le tir ? + { + prog = m_progress; + + dir.x = 0.0f; + dir.y = 0.0f; + dir.z = Prop(0, -50, prog); + SetInclinaison(dir); + m_object->SetAngleZ(1, Prop(0, 65, prog)); // tête + m_object->SetAngleZ(2, Prop(0, -95, prog)); // queue + } + else if ( m_actionType == MAS_FIRE ) // tir ? + { + if ( m_progress < 0.75f ) a = m_progress/0.75f; + else a = (1.0f-m_progress)/0.25f; + m_object->SetZoom(2, (a*0.5f)+1.0f); // queue + m_object->SetAngleX(2, (Rand()-0.5f)*0.3f*a); + m_object->SetAngleY(2, (Rand()-0.5f)*0.3f*a); + + dir.x = (Rand()-0.5f)*0.02f*a; + dir.y = (Rand()-0.5f)*0.05f*a; + dir.z = (Rand()-0.5f)*0.03f*a; + SetCirVibration(dir); + } + else if ( m_actionType == MAS_TERMINATE ) // termine le tir ? + { + prog = 1.0f-m_progress; + + dir.x = 0.0f; + dir.y = 0.0f; + dir.z = Prop(0, -50, prog); + SetInclinaison(dir); + m_object->SetAngleZ(1, Prop(0, 65, prog)); // tête + m_object->SetAngleZ(2, Prop(0, -95, prog)); // queue + } + else if ( m_actionType == MAS_BURN ) // brûle ? + { + dir = D3DVECTOR(PI, 0.0f, 0.0f); + SetCirVibration(dir); + dir = D3DVECTOR(0.0f, -1.5f, 0.0f); + SetLinVibration(dir); + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetInclinaison(dir); + + time = event.rTime*1.0f; + m_object->SetAngleZ(1, Smooth(m_object->RetAngleZ(1), 0.0f, time)); // tête + m_object->SetAngleZ(2, Smooth(m_object->RetAngleZ(2), 0.0f, time)); // queue + } + else if ( m_actionType == MAS_RUIN ) // ruine ? + { + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetLinVibration(dir); + SetCirVibration(dir); + SetInclinaison(dir); + } + else if ( m_actionType == MAS_BACK1 ) // se met sur le dos ? + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_armTimeAbs ) + { + m_lastParticule = m_armTimeAbs; + + pos = m_object->RetPosition(0); + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + speed.y = Rand()*5.0f; + dim.x = Rand()*3.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f); + } + + if ( m_progress < 0.5f ) + { + dir.x = 0.0f; + dir.y = powf(m_progress/0.5f, 2.0f)*12.0f; + dir.z = 0.0f; + SetLinVibration(dir); + } + else + { + dir.x = 0.0f; + dir.y = powf(2.0f-m_progress/0.5f, 2.0f)*12.0f; + dir.z = 0.0f; + SetLinVibration(dir); + } + dir.x = m_progress*PI; + dir.y = 0.0f; + dir.z = 0.0f; + SetCirVibration(dir); + + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetInclinaison(dir); + + if ( m_progress >= 1.0f ) + { + SetAction(MAS_BACK2, 55.0f+Rand()*10.0f); + } + } + else if ( m_actionType == MAS_BACK2 ) // bouge sur le dos ? + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_armTimeAbs ) + { + m_lastParticule = m_armTimeAbs; + + if ( rand()%10 == 0 ) + { + pos = m_object->RetPosition(0); + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + pos.y -= 1.0f; + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = Rand()*2.0f; + dim.x = Rand()*1.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f); + } + } + + dir = D3DVECTOR(0.0f, -1.0f, 0.0f); + SetLinVibration(dir); + dir.x = sinf(m_armTimeAbs* 4.0f)*0.10f+ + sinf(m_armTimeAbs* 7.0f)*0.20f+ + sinf(m_armTimeAbs*10.0f)*0.40f+ + sinf(m_armTimeAbs*21.0f)*0.50f+PI; + dir.y = sinf(m_armTimeAbs* 3.0f)*0.01f+ + sinf(m_armTimeAbs* 6.0f)*0.02f+ + sinf(m_armTimeAbs*11.0f)*0.04f+ + sinf(m_armTimeAbs*20.0f)*0.02f; + dir.z = sinf(m_armTimeAbs* 5.0f)*0.01f+ + sinf(m_armTimeAbs* 8.0f)*0.02f+ + sinf(m_armTimeAbs* 9.0f)*0.04f+ + sinf(m_armTimeAbs*23.0f)*0.03f; + SetCirVibration(dir); + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetInclinaison(dir); + + m_object->SetAngleY(1, sinf(m_armTimeAbs*8.0f)*0.7f); // tête + m_object->SetAngleY(2, cosf(m_armTimeAbs*8.0f)*0.7f); // queue + m_object->SetAngleZ(1, 0.0f); // tête + m_object->SetAngleZ(2, 0.0f); // queue + + if ( m_progress >= 1.0f ) + { + SetAction(MAS_BACK3, 0.4f); + } + } + else if ( m_actionType == MAS_BACK3 ) // se remet sur les pattes ? + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_armTimeAbs ) + { + m_lastParticule = m_armTimeAbs; + + pos = m_object->RetPosition(0); + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + speed.y = Rand()*5.0f; + dim.x = Rand()*3.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f); + } + + if ( m_progress < 0.5f ) + { + dir.x = 0.0f; + dir.y = powf(m_progress/0.5f, 2.0f)*5.0f; + dir.z = 0.0f; + SetLinVibration(dir); + } + else + { + dir.x = 0.0f; + dir.y = powf(2.0f-m_progress/0.5f, 2.0f)*5.0f; + dir.z = 0.0f; + SetLinVibration(dir); + } + dir.x = (1.0f-m_progress)*PI; + dir.y = 0.0f; + dir.z = 0.0f; + SetCirVibration(dir); + + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetInclinaison(dir); + + if ( m_progress >= 1.0f ) + { + SetAction(-1); + m_object->SetFixed(FALSE); // bouge de nouveau + } + } + else + { + m_object->SetZoom(2, 1.0f); // queue + m_object->SetAngleX(2, 0.0f); + m_object->SetAngleY(2, 0.0f); + + if ( bStop ) + { + m_object->SetAngleZ(2, sinf(m_armTimeAbs*1.7f)*0.15f); // queue + + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetLinVibration(dir); + SetInclinaison(dir); + } + else + { + a = Mod(m_armTimeMarch, 1.0f); + if ( a < 0.5f ) a = -1.0f+4.0f*a; // -1..1 + else a = 3.0f-4.0f*a; // 1..-1 + dir.x = sinf(a)*0.05f; + + s = Mod(m_armTimeMarch/2.0f, 1.0f); + if ( s < 0.5f ) s = -1.0f+4.0f*s; // -1..1 + else s = 3.0f-4.0f*s; // 1..-1 + dir.z = sinf(s)*0.1f; + + dir.y = 0.0f; + SetInclinaison(dir); + + m_object->SetAngleZ(2, -sinf(a)*0.3f); // queue + + a = Mod(m_armMember-0.1f, 1.0f); + if ( a < 0.33f ) + { + dir.y = -(1.0f-(a/0.33f))*0.3f; + } + else if ( a < 0.67f ) + { + dir.y = 0.0f; + } + else + { + dir.y = -(a-0.67f)/0.33f*0.3f; + } + dir.x = 0.0f; + dir.z = 0.0f; + SetLinVibration(dir); + } + + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetCirVibration(dir); + + m_object->SetAngleZ(1, sinf(m_armTimeAbs*1.4f)*0.20f); // tête + m_object->SetAngleX(1, sinf(m_armTimeAbs*1.9f)*0.10f); // tête + m_object->SetAngleY(1, sinf(m_armTimeAbs*2.1f)*0.50f); // tête + } + + return TRUE; +} + + diff --git a/src/motionant.h b/src/motionant.h new file mode 100644 index 00000000..03659270 --- /dev/null +++ b/src/motionant.h @@ -0,0 +1,61 @@ +// motionant.h + +#ifndef _MOTIONANT_H_ +#define _MOTIONANT_H_ + + +class CInstanceManager; +class CEngine; +class CLight; +class CParticule; +class CTerrain; +class CCamera; +class CBrain; +class CPhysics; +class CObject; + + +#define MA_MARCH 0 +#define MA_STOP 1 +#define MA_SPEC 2 + +#define MAS_PREPARE 0 +#define MAS_FIRE 1 +#define MAS_TERMINATE 2 +#define MAS_BURN 3 +#define MAS_RUIN 4 +#define MAS_BACK1 5 +#define MAS_BACK2 6 +#define MAS_BACK3 7 + + +class CMotionAnt : public CMotion +{ +public: + CMotionAnt(CInstanceManager* iMan, CObject* object); + ~CMotionAnt(); + + void DeleteObject(BOOL bAll=FALSE); + BOOL Create(D3DVECTOR pos, float angle, ObjectType type, float power); + BOOL EventProcess(const Event &event); + +protected: + void CreatePhysics(); + BOOL EventFrame(const Event &event); + +protected: + float m_armMember; + float m_armTimeAbs; + float m_armTimeMarch; + float m_armTimeAction; + short m_armAngles[3*3*3*3*3 + 3*3*3*8]; + int m_armTimeIndex; + int m_armPartIndex; + int m_armMemberIndex; + int m_armLastAction; + BOOL m_bArmStop; + float m_lastParticule; +}; + + +#endif //_MOTIONANT_H_ diff --git a/src/motionbee.cpp b/src/motionbee.cpp new file mode 100644 index 00000000..8d4de314 --- /dev/null +++ b/src/motionbee.cpp @@ -0,0 +1,647 @@ +// motionbee.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "modfile.h" +#include "sound.h" +#include "motion.h" +#include "motionbee.h" + + + +#define ADJUST_ANGLE FALSE // TRUE -> ajuste les angles des membres +#define START_TIME 1000.0f // début du temps relatif + + + +// Constructeur de l'objet. + +CMotionBee::CMotionBee(CInstanceManager* iMan, CObject* object) + : CMotion(iMan, object) +{ + CMotion::CMotion(iMan, object); + + m_armMember = START_TIME; + m_armTimeAbs = START_TIME; + m_armTimeMarch = START_TIME; + m_armTimeAction = START_TIME; + m_armTimeIndex = 0; + m_armPartIndex = 0; + m_armMemberIndex = 0; + m_armLastAction = -1; + m_bArmStop = FALSE; +} + +// Destructeur de l'objet. + +CMotionBee::~CMotionBee() +{ +} + + +// Supprime un objet. + +void CMotionBee::DeleteObject(BOOL bAll) +{ +} + + +// Crée un véhicule roulant quelconque posé sur le sol. + +BOOL CMotionBee::Create(D3DVECTOR pos, float angle, ObjectType type, + float power) +{ + CModFile* pModFile; + int rank; + + if ( m_engine->RetRestCreate() < 3+18+2 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + m_object->SetType(type); + + // Crée la base principale. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEVEHICULE); // c'est un objet mobile + m_object->SetObjectRank(0, rank); + + pModFile->ReadModel("objects\\bee1.mod"); + pModFile->CreateEngineObject(rank); + + m_object->SetPosition(0, pos); + m_object->SetAngleY(0, angle); + + // Un véhicule doit avoir obligatoirement une sphère de + // collision avec un centre (0;y;0) (voir GetCrashSphere). + m_object->CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 4.0f, SOUND_BOUM, 0.20f); + m_object->SetGlobalSphere(D3DVECTOR(-1.0f, 1.0f, 0.0f), 5.0f); + + // Crée la tête. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\bee2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(1.6f, 0.3f, 0.0f)); + + // Crée la queue. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 0); + pModFile->ReadModel("objects\\bee3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(-0.8f, 0.0f, 0.0f)); + + // Crée la cuisse 1 arrière-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(3, rank); + m_object->SetObjectParent(3, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(3, D3DVECTOR(-0.3f, -0.1f, -0.2f)); + + // Crée la jambe 1 arrière-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(4, rank); + m_object->SetObjectParent(4, 3); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(4, D3DVECTOR(0.0f, 0.0f, -1.0f)); + + // Crée le pied 1 arrière-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(5, rank); + m_object->SetObjectParent(5, 4); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(5, D3DVECTOR(0.0f, 0.0f, -2.0f)); + + // Crée la cuisse 2 milieu-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(0.3f, -0.1f, -0.4f)); + + // Crée la jambe 2 milieu-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 6); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(0.0f, 0.0f, -1.0f)); + + // Crée le pied 2 milieu-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(8, rank); + m_object->SetObjectParent(8, 7); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(8, D3DVECTOR(0.0f, 0.0f, -2.0f)); + + // Crée la cuisse 3 avant-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(9, rank); + m_object->SetObjectParent(9, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(9, D3DVECTOR(1.0f, -0.1f, -0.7f)); + + // Crée la jambe 3 avant-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(10, rank); + m_object->SetObjectParent(10, 9); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(10, D3DVECTOR(0.0f, 0.0f, -1.0f)); + + // Crée le pied 3 avant-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(11, rank); + m_object->SetObjectParent(11, 10); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(11, D3DVECTOR(0.0f, 0.0f, -2.0f)); + + // Crée la cuisse 1 arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(12, rank); + m_object->SetObjectParent(12, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(12, D3DVECTOR(-0.3f, -0.1f, 0.2f)); + m_object->SetAngleY(12, PI); + + // Crée la jambe 1 arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(13, rank); + m_object->SetObjectParent(13, 12); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(13, D3DVECTOR(0.0f, 0.0f, -1.0f)); + + // Crée le pied 1 arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(14, rank); + m_object->SetObjectParent(14, 13); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(14, D3DVECTOR(0.0f, 0.0f, -2.0f)); + + // Crée la cuisse 2 milieu-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(15, rank); + m_object->SetObjectParent(15, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(15, D3DVECTOR(0.3f, -0.1f, 0.4f)); + m_object->SetAngleY(15, PI); + + // Crée la jambe 2 milieu-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(16, rank); + m_object->SetObjectParent(16, 15); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(16, D3DVECTOR(0.0f, 0.0f, -1.0f)); + + // Crée le pied 2 milieu-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(17, rank); + m_object->SetObjectParent(17, 16); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(17, D3DVECTOR(0.0f, 0.0f, -2.0f)); + + // Crée la cuisse 3 avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(18, rank); + m_object->SetObjectParent(18, 0); + pModFile->ReadModel("objects\\ant4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(18, D3DVECTOR(1.0f, -0.1f, 0.7f)); + m_object->SetAngleY(18, PI); + + // Crée la jambe 3 avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(19, rank); + m_object->SetObjectParent(19, 18); + pModFile->ReadModel("objects\\ant5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(19, D3DVECTOR(0.0f, 0.0f, -1.0f)); + + // Crée le pied 3 avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(20, rank); + m_object->SetObjectParent(20, 19); + pModFile->ReadModel("objects\\ant6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(20, D3DVECTOR(0.0f, 0.0f, -2.0f)); + + // Crée l'aile droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(21, rank); + m_object->SetObjectParent(21, 0); + pModFile->ReadModel("objects\\bee7.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(21, D3DVECTOR(0.8f, 0.4f, -0.5f)); + + // Crée l'aile gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(22, rank); + m_object->SetObjectParent(22, 0); + pModFile->ReadModel("objects\\bee7.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(22, D3DVECTOR(0.8f, 0.4f, 0.5f)); + + m_object->CreateShadowCircle(6.0f, 0.5f); + + CreatePhysics(); + m_object->SetFloorHeight(0.0f); + + pos = m_object->RetPosition(0); + m_object->SetPosition(0, pos); // pour afficher les ombres tout de suite + + m_engine->LoadAllTexture(); + + delete pModFile; + return TRUE; +} + +// Crée la physique de l'objet. + +void CMotionBee::CreatePhysics() +{ + Character* character; + int i; + + int member_march[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, // en l'air : + 0,45,0, 0,45,0, 0,50,0, // t0: cuisses 1..3 + 30,-70,0, 20,-105,20, 25,-100,0, // t0: jambes 1..3 + -70,75,0, -30,80,0, -80,80,0, // t0: pieds 1..3 + // au sol devant : + 0,30,0, 0,20,0, 0,15,0, // t1: cuisses 1..3 + -15,-50,0, -20,-60,0, -10,-75,0, // t1: jambes 1..3 + -40,50,0, -25,15,0, -50,35,0, // t1: pieds 1..3 + // au sol derrière : + 0,35,0, 0,30,0, 0,20,0, // t2: cuisses 1..3 + -20,-15,0, -30,-55,0, -25,-70,15, // t2: jambes 1..3 + -25,25,0, -20,60,0, -30,95,0, // t2: pieds 1..3 + }; + + int member_spec[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, // porte boulet : + 0,45,0, 0,45,0, 0,50,0, // s0: cuisses 1..3 + -35,-70,0, -20,-85,-25,-25,-100,0, // s0: jambes 1..3 + -110,75,-15,-130,80,-25,-125,40,0, // s0: pieds 1..3 + // brûle : + 0,45,0, 0,45,0, 0,50,0, // s1: cuisses 1..3 + -35,-70,0, -20,-85,-25,-25,-100,0, // s1: jambes 1..3 + -110,75,-15,-130,80,-25,-125,40,0, // s1: pieds 1..3 + // ruine : + 0,45,0, 0,45,0, 0,50,0, // s2: cuisses 1..3 + -35,-70,0, -20,-85,-25,-25,-100,0, // s2: jambes 1..3 + -110,75,-15,-130,80,-25,-125,40,0, // s2: pieds 1..3 + }; + + m_physics->SetType(TYPE_FLYING); + + character = m_object->RetCharacter(); + character->wheelFront = 3.0f; + character->wheelBack = 3.0f; + character->wheelLeft = 5.0f; + character->wheelRight = 5.0f; + character->height = 2.5f; + + m_physics->SetLinMotionX(MO_ADVSPEED, 50.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 50.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 20.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 20.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 20.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 10.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 10.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 40.0f); + m_physics->SetLinMotionY(MO_ADVSPEED, 60.0f); + m_physics->SetLinMotionY(MO_RECSPEED, 60.0f); + m_physics->SetLinMotionY(MO_ADVACCEL, 20.0f); + m_physics->SetLinMotionY(MO_RECACCEL, 50.0f); + m_physics->SetLinMotionY(MO_STOACCEL, 50.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 1.0f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 1.0f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 20.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 20.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 40.0f); + + for ( i=0 ; i<3*3*3*3 ; i++ ) + { + m_armAngles[3*3*3*3*MB_MARCH+i] = member_march[i]; + } + for ( i=0 ; i<3*3*3*3 ; i++ ) + { + m_armAngles[3*3*3*3*MB_SPEC+i] = member_spec[i]; + } +} + + +// Gestion d'un événement. + +BOOL CMotionBee::EventProcess(const Event &event) +{ + CMotion::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + + if ( event.event == EVENT_KEYDOWN ) + { +#if ADJUST_ANGLE + int i; + + if ( event.param == 'A' ) m_armTimeIndex++; + if ( m_armTimeIndex >= 3 ) m_armTimeIndex = 0; + + if ( event.param == 'Q' ) m_armPartIndex++; + if ( m_armPartIndex >= 3 ) m_armPartIndex = 0; + + if ( event.param == 'W' ) m_armMemberIndex++; + if ( m_armMemberIndex >= 3 ) m_armMemberIndex = 0; + + i = m_armMemberIndex*3; + i += m_armPartIndex*3*3; + i += m_armTimeIndex*3*3*3; +//? i += 3*3*3*3; + + if ( event.param == 'E' ) m_armAngles[i+0] += 5; + if ( event.param == 'D' ) m_armAngles[i+0] -= 5; + if ( event.param == 'R' ) m_armAngles[i+1] += 5; + if ( event.param == 'F' ) m_armAngles[i+1] -= 5; + if ( event.param == 'T' ) m_armAngles[i+2] += 5; + if ( event.param == 'G' ) m_armAngles[i+2] -= 5; + + if ( event.param == 'Y' ) m_bArmStop = !m_bArmStop; +#endif + } + + return TRUE; +} + +// Gestion d'un événement. + +BOOL CMotionBee::EventFrame(const Event &event) +{ + D3DVECTOR dir; + float s, a, prog; + int action, i, st, nd; + BOOL bStop; + + if ( m_engine->RetPause() ) return TRUE; + if ( !m_engine->IsVisiblePoint(m_object->RetPosition(0)) ) return TRUE; + + s = m_physics->RetLinMotionX(MO_MOTSPEED)*0.30f; + a = Abs(m_physics->RetCirMotionY(MO_MOTSPEED)*2.00f); + + if ( s == 0.0f && a != 0.0f ) a *= 1.5f; + + m_armTimeAbs += event.rTime; + m_armTimeMarch += (s)*event.rTime*0.15f; + m_armMember += (s+a)*event.rTime*0.15f; + + bStop = ( a == 0.0f && s == 0.0f ); // a l'arrêt ? + if ( !m_physics->RetLand() ) bStop = TRUE; + + if ( bStop ) + { + prog = Mod(m_armTimeAbs, 2.0f)/10.0f; + a = Mod(m_armMember, 1.0f); + a = (prog-a)*event.rTime*2.0f; // vient gentiment à position stop + m_armMember += a; + } + + action = MB_MARCH; // marche + + m_actionType = -1; + if ( m_object->RetFret() != 0 ) m_actionType = MBS_HOLD; // porte le boulet + + if ( m_object->RetRuin() ) // ruine ? + { + m_actionType = MBS_RUIN; + } + if ( m_object->RetBurn() ) // brûle ? + { + m_actionType = MBS_BURN; + } + + for ( i=0 ; i<6 ; i++ ) // les 6 pattes + { + if ( m_actionType != -1 ) // action spéciale en cours ? + { + st = 3*3*3*3*MB_SPEC + 3*3*3*m_actionType + (i%3)*3; + nd = st; + } + else + { + if ( i < 3 ) prog = Mod(m_armMember+(2.0f-(i%3))*0.33f+0.0f, 1.0f); + else prog = Mod(m_armMember+(2.0f-(i%3))*0.33f+0.3f, 1.0f); + if ( m_bArmStop ) + { + prog = (float)m_armTimeIndex/3.0f; + } + if ( prog < 0.33f ) // t0..t1 ? + { + prog = prog/0.33f; // 0..1 + st = 0; // index start + nd = 1; // index end + } + else if ( prog < 0.67f ) // t1..t2 ? + { + prog = (prog-0.33f)/0.33f; // 0..1 + st = 1; // index start + nd = 2; // index end + } + else // t2..t0 ? + { + prog = (prog-0.67f)/0.33f; // 0..1 + st = 2; // index start + nd = 0; // index end + } + st = 3*3*3*3*action + st*3*3*3 + (i%3)*3; + nd = 3*3*3*3*action + nd*3*3*3 + (i%3)*3; + } + + if ( i < 3 ) // patte droite (1..3) ? + { + m_object->SetAngleX(3+3*i+0, Prop(m_armAngles[st+ 0], m_armAngles[nd+ 0], prog)); + m_object->SetAngleY(3+3*i+0, Prop(m_armAngles[st+ 1], m_armAngles[nd+ 1], prog)); + m_object->SetAngleZ(3+3*i+0, Prop(m_armAngles[st+ 2], m_armAngles[nd+ 2], prog)); + m_object->SetAngleX(3+3*i+1, Prop(m_armAngles[st+ 9], m_armAngles[nd+ 9], prog)); + m_object->SetAngleY(3+3*i+1, Prop(m_armAngles[st+10], m_armAngles[nd+10], prog)); + m_object->SetAngleZ(3+3*i+1, Prop(m_armAngles[st+11], m_armAngles[nd+11], prog)); + m_object->SetAngleX(3+3*i+2, Prop(m_armAngles[st+18], m_armAngles[nd+18], prog)); + m_object->SetAngleY(3+3*i+2, Prop(m_armAngles[st+19], m_armAngles[nd+19], prog)); + m_object->SetAngleZ(3+3*i+2, Prop(m_armAngles[st+20], m_armAngles[nd+20], prog)); + } + else // patte gauche (4..6) ? + { + m_object->SetAngleX(3+3*i+0, Prop( -m_armAngles[st+ 0], -m_armAngles[nd+ 0], prog)); + m_object->SetAngleY(3+3*i+0, Prop(180-m_armAngles[st+ 1], 180-m_armAngles[nd+ 1], prog)); + m_object->SetAngleZ(3+3*i+0, Prop( -m_armAngles[st+ 2], -m_armAngles[nd+ 2], prog)); + m_object->SetAngleX(3+3*i+1, Prop( m_armAngles[st+ 9], m_armAngles[nd+ 9], prog)); + m_object->SetAngleY(3+3*i+1, Prop( -m_armAngles[st+10], -m_armAngles[nd+10], prog)); + m_object->SetAngleZ(3+3*i+1, Prop( -m_armAngles[st+11], -m_armAngles[nd+11], prog)); + m_object->SetAngleX(3+3*i+2, Prop( m_armAngles[st+18], m_armAngles[nd+18], prog)); + m_object->SetAngleY(3+3*i+2, Prop( -m_armAngles[st+19], -m_armAngles[nd+19], prog)); + m_object->SetAngleZ(3+3*i+2, Prop( -m_armAngles[st+20], -m_armAngles[nd+20], prog)); + } + } + +#if ADJUST_ANGLE + if ( m_object->RetSelect() ) + { + char s[100]; + sprintf(s, "A:time=%d Q:part=%d W:member=%d", m_armTimeIndex, m_armPartIndex, m_armMemberIndex); + m_engine->SetInfoText(4, s); + } +#endif + + if ( m_physics->RetLand() ) // au sol ? + { + if ( m_object->RetRuin() ) + { + } + else if ( bStop || m_object->RetBurn() ) + { + m_object->SetAngleZ(2, sinf(m_armTimeAbs*1.7f)*0.15f+0.35f); // queue + } + else + { + a = Mod(m_armTimeMarch, 1.0f); + if ( a < 0.5f ) a = -1.0f+4.0f*a; // -1..1 + else a = 3.0f-4.0f*a; // 1..-1 + dir.x = sinf(a)*0.05f; + + s = Mod(m_armTimeMarch/2.0f, 1.0f); + if ( s < 0.5f ) s = -1.0f+4.0f*s; // -1..1 + else s = 3.0f-4.0f*s; // 1..-1 + dir.z = sinf(s)*0.1f; + + dir.y = 0.0f; + m_object->SetInclinaison(dir); + + m_object->SetAngleZ(2, -sinf(a)*0.3f); // queue + + a = Mod(m_armMember-0.1f, 1.0f); + if ( a < 0.33f ) + { + dir.y = -(1.0f-(a/0.33f))*0.3f; + } + else if ( a < 0.67f ) + { + dir.y = 0.0f; + } + else + { + dir.y = -(a-0.67f)/0.33f*0.3f; + } + dir.x = 0.0f; + dir.z = 0.0f; + m_object->SetLinVibration(dir); + } + } + + if ( m_physics->RetLand() ) + { + if ( bStop ) prog = 0.05f; + else prog = 0.15f; + } + else + { + prog = 1.00f; + } + +#if 0 + a = Rand()*PI/2.0f*prog; + m_object->SetAngleX(21, a); // aile droite + a = -Rand()*PI/4.0f*prog; + m_object->SetAngleY(21, a); + + a = -Rand()*PI/2.0f*prog; + m_object->SetAngleX(22, a); // aile gauche + a = Rand()*PI/4.0f*prog; + m_object->SetAngleY(22, a); +#else + m_object->SetAngleX(21, (sinf(m_armTimeAbs*30.0f)+1.0f)*(PI/4.0f)*prog); + m_object->SetAngleY(21, -Rand()*PI/6.0f*prog); + + m_object->SetAngleX(22, -(sinf(m_armTimeAbs*30.0f)+1.0f)*(PI/4.0f)*prog); + m_object->SetAngleY(22, Rand()*PI/6.0f*prog); +#endif + + m_object->SetAngleZ(1, sinf(m_armTimeAbs*1.4f)*0.20f); // tête + m_object->SetAngleX(1, sinf(m_armTimeAbs*1.9f)*0.10f); // tête + m_object->SetAngleY(1, sinf(m_armTimeAbs*2.1f)*0.50f); // tête + +#if 0 + h = m_terrain->RetFloorHeight(RetPosition(0)); + radius = 4.0f+h/4.0f; + color.r = 0.3f+h/80.0f; + color.g = color.r; + color.b = color.r; + color.a = color.r; + m_engine->SetObjectShadowRadius(m_objectPart[0].object, radius); + m_engine->SetObjectShadowColor(m_objectPart[0].object, color); +#endif + + return TRUE; +} + + diff --git a/src/motionbee.h b/src/motionbee.h new file mode 100644 index 00000000..18636c27 --- /dev/null +++ b/src/motionbee.h @@ -0,0 +1,54 @@ +// motionbee.h + +#ifndef _MOTIONBEE_H_ +#define _MOTIONBEE_H_ + + +class CInstanceManager; +class CEngine; +class CLight; +class CParticule; +class CTerrain; +class CCamera; +class CBrain; +class CPhysics; +class CObject; + + +#define MB_MARCH 0 +#define MB_SPEC 1 + +#define MBS_HOLD 0 +#define MBS_BURN 1 +#define MBS_RUIN 2 + + +class CMotionBee : public CMotion +{ +public: + CMotionBee(CInstanceManager* iMan, CObject* object); + ~CMotionBee(); + + void DeleteObject(BOOL bAll=FALSE); + BOOL Create(D3DVECTOR pos, float angle, ObjectType type, float power); + BOOL EventProcess(const Event &event); + +protected: + void CreatePhysics(); + BOOL EventFrame(const Event &event); + +protected: + float m_armMember; + float m_armTimeAbs; + float m_armTimeMarch; + float m_armTimeAction; + short m_armAngles[3*3*3*3*2]; + int m_armTimeIndex; + int m_armPartIndex; + int m_armMemberIndex; + int m_armLastAction; + BOOL m_bArmStop; +}; + + +#endif //_MOTIONBEE_H_ diff --git a/src/motionhuman.cpp b/src/motionhuman.cpp new file mode 100644 index 00000000..9306b438 --- /dev/null +++ b/src/motionhuman.cpp @@ -0,0 +1,1784 @@ +// motionhuman.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "water.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "modfile.h" +#include "robotmain.h" +#include "sound.h" +#include "motion.h" +#include "motionhuman.h" + + + +#define ADJUST_ANGLE FALSE // TRUE -> ajuste les angles des membres +#define ADJUST_ACTION (3*3*3*3*MH_SPEC+3*3*3*MHS_SATCOM) + +#define START_TIME 1000.0f // début du temps relatif + + + +// Constructeur de l'objet. + +CMotionHuman::CMotionHuman(CInstanceManager* iMan, CObject* object) + : CMotion(iMan, object) +{ + CMotion::CMotion(iMan, object); + + m_partiReactor = -1; + m_armMember = START_TIME; + m_armTimeAbs = START_TIME; + m_armTimeAction = START_TIME; + m_armTimeSwim = START_TIME; + m_armTimeIndex = 0; + m_armPartIndex = 0; + m_armMemberIndex = 0; + m_armLastAction = -1; + m_bArmStop = FALSE; + m_lastSoundMarch = 0.0f; + m_lastSoundHhh = 0.0f; + m_time = 0.0f; + m_tired = 0.0f; + m_bDisplayPerso = FALSE; +} + +// Destructeur de l'objet. + +CMotionHuman::~CMotionHuman() +{ +} + + +// Supprime un objet. + +void CMotionHuman::DeleteObject(BOOL bAll) +{ + if ( m_partiReactor != -1 ) + { + m_particule->DeleteParticule(m_partiReactor); + m_partiReactor = -1; + } +} + + +// Démarre une action. + +Error CMotionHuman::SetAction(int action, float time) +{ + CMotion::SetAction(action, time); + m_time = 0.0f; + return ERR_OK; +} + + +// Crée le cosmonaute posé sur le sol. + +BOOL CMotionHuman::Create(D3DVECTOR pos, float angle, ObjectType type, + float power) +{ + CModFile* pModFile; + char filename[100]; + int rank, option, face, glasses; + + if ( m_engine->RetRestCreate() < 16 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + m_object->SetType(type); + option = m_object->RetOption(); + + if ( m_main->RetGamerOnlyHead() ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEVEHICULE); // c'est un objet mobile + m_object->SetObjectRank(0, rank); + face = m_main->RetGamerFace(); + sprintf(filename, "objects\\human2h%d.mod", face+1); + pModFile->ReadModel(filename); + pModFile->CreateEngineObject(rank); + + glasses = m_main->RetGamerGlasses(); + if ( glasses != 0 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + sprintf(filename, "objects\\human2g%d.mod", glasses); + pModFile->ReadModel(filename); + pModFile->CreateEngineObject(rank); + } + + CreatePhysics(type); + m_object->SetFloorHeight(0.0f); + + m_engine->LoadAllTexture(); + + delete pModFile; + return TRUE; + } + + // Crée la base principale. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEVEHICULE); // c'est un objet mobile + m_object->SetObjectRank(0, rank); + + if ( option == 0 ) // tête dans casque ? + { + pModFile->ReadModel("objects\\human1c.mod"); + } + if ( option == 1 ) // tête à l'air ? + { + pModFile->ReadModel("objects\\human1h.mod"); + } + if ( option == 2 ) // sans sac à dos ? + { + pModFile->ReadModel("objects\\human1v.mod"); + } + pModFile->CreateEngineObject(rank); + + m_object->SetPosition(0, pos); + m_object->SetAngleY(0, angle); + + // Un véhicule doit avoir obligatoirement une sphère de + // collision avec un centre (0;y;0) (voir GetCrashSphere). + m_object->CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 2.0f, SOUND_AIE, 0.20f); + m_object->SetGlobalSphere(D3DVECTOR(0.0f, 1.0f, 0.0f), 4.0f); + + // Crée la tête. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + + if ( type == OBJECT_HUMAN ) + { + if ( option == 0 ) // tête dans casque ? + { + face = m_main->RetGamerFace(); + sprintf(filename, "objects\\human2c%d.mod", face+1); + pModFile->ReadModel(filename); + } + if ( option == 1 || // tête à l'air ? + option == 2 ) // sans sac à dos ? + { + face = m_main->RetGamerFace(); + sprintf(filename, "objects\\human2h%d.mod", face+1); + pModFile->ReadModel(filename); + } + } + if ( type == OBJECT_TECH ) + { + pModFile->ReadModel("objects\\human2t.mod"); + } + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(0.0f, 2.7f, 0.0f)); + if ( option == 1 || // tête à l'air ? + option == 2 ) // sans sac à dos ? + { + m_object->SetZoom(1, D3DVECTOR(1.0f, 1.05f, 1.0f)); + } + + // Crée les lunettes. + glasses = m_main->RetGamerGlasses(); + if ( glasses != 0 && type == OBJECT_HUMAN ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(15, rank); + m_object->SetObjectParent(15, 1); + sprintf(filename, "objects\\human2g%d.mod", glasses); + pModFile->ReadModel(filename); + pModFile->CreateEngineObject(rank); + } + + // Crée le bras droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 0); + pModFile->ReadModel("objects\\human3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(0.0f, 2.3f, -1.2f)); + m_object->SetAngle(2, D3DVECTOR(90.0f*PI/180.0f, 90.0f*PI/180.0f, -50.0f*PI/180.0f)); + + // Crée l'avant-bras droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(3, rank); + m_object->SetObjectParent(3, 2); + pModFile->ReadModel("objects\\human4r.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(3, D3DVECTOR(1.3f, 0.0f, 0.0f)); + m_object->SetAngle(3, D3DVECTOR(0.0f*PI/180.0f, -20.0f*PI/180.0f, 0.0f*PI/180.0f)); + + // Crée la main droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(4, rank); + m_object->SetObjectParent(4, 3); + pModFile->ReadModel("objects\\human5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(4, D3DVECTOR(1.2f, 0.0f, 0.0f)); + + // Crée la cuisse droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(5, rank); + m_object->SetObjectParent(5, 0); + pModFile->ReadModel("objects\\human6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(5, D3DVECTOR(0.0f, 0.0f, -0.7f)); + m_object->SetAngle(5, D3DVECTOR(10.0f*PI/180.0f, 0.0f*PI/180.0f, 5.0f*PI/180.0f)); + + // Crée la jambe droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 5); + pModFile->ReadModel("objects\\human7.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(0.0f, -1.5f, 0.0f)); + m_object->SetAngle(6, D3DVECTOR(0.0f*PI/180.0f, 0.0f*PI/180.0f, -10.0f*PI/180.0f)); + + // Crée le pied droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 6); + pModFile->ReadModel("objects\\human8.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(0.0f, -1.5f, 0.0f)); + m_object->SetAngle(7, D3DVECTOR(-10.0f*PI/180.0f, 5.0f*PI/180.0f, 5.0f*PI/180.0f)); + + // Crée le bras gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(8, rank); + m_object->SetObjectParent(8, 0); + pModFile->ReadModel("objects\\human3.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(8, D3DVECTOR(0.0f, 2.3f, 1.2f)); + m_object->SetAngle(8, D3DVECTOR(-90.0f*PI/180.0f, -90.0f*PI/180.0f, -50.0f*PI/180.0f)); + + // Crée l'avant-bras gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(9, rank); + m_object->SetObjectParent(9, 8); + pModFile->ReadModel("objects\\human4l.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(9, D3DVECTOR(1.3f, 0.0f, 0.0f)); + m_object->SetAngle(9, D3DVECTOR(0.0f*PI/180.0f, 20.0f*PI/180.0f, 0.0f*PI/180.0f)); + + // Crée la main gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(10, rank); + m_object->SetObjectParent(10, 9); + pModFile->ReadModel("objects\\human5.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(10, D3DVECTOR(1.2f, 0.0f, 0.0f)); + + // Crée la cuisse gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(11, rank); + m_object->SetObjectParent(11, 0); + pModFile->ReadModel("objects\\human6.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(11, D3DVECTOR(0.0f, 0.0f, 0.7f)); + m_object->SetAngle(11, D3DVECTOR(-10.0f*PI/180.0f, 0.0f*PI/180.0f, 5.0f*PI/180.0f)); + + // Crée la jambe gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(12, rank); + m_object->SetObjectParent(12, 11); + pModFile->ReadModel("objects\\human7.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(12, D3DVECTOR(0.0f, -1.5f, 0.0f)); + m_object->SetAngle(12, D3DVECTOR(0.0f*PI/180.0f, 0.0f*PI/180.0f, -10.0f*PI/180.0f)); + + // Crée le pied gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(13, rank); + m_object->SetObjectParent(13, 12); + pModFile->ReadModel("objects\\human8.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(13, D3DVECTOR(0.0f, -1.5f, 0.0f)); + m_object->SetAngle(13, D3DVECTOR(10.0f*PI/180.0f, -5.0f*PI/180.0f, 5.0f*PI/180.0f)); + + // Crée le pistolet. + if ( option != 2 ) // avec sac à dos ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(14, rank); + m_object->SetObjectParent(14, 0); + pModFile->ReadModel("objects\\human9.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(14, D3DVECTOR(-1.5f, 0.3f, -1.35f)); + m_object->SetAngleZ(14, PI); + } + + m_object->CreateShadowCircle(2.0f, 0.8f); + + CreatePhysics(type); + m_object->SetFloorHeight(0.0f); + + pos = m_object->RetPosition(0); + m_object->SetPosition(0, pos); // pour afficher les ombres tout de suite + + m_engine->LoadAllTexture(); + + delete pModFile; + return TRUE; +} + +// Crée la physique de l'objet. + +void CMotionHuman::CreatePhysics(ObjectType type) +{ + Character* character; + int i; + + int member_march[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, // en l'air : + 90,90,-50, 10,0,55, 0,0,0, // t0: bras/cuisses/- + 0,-20,0, -5,0,-110, 0,0,0, // t0: avant-bras/jambes/- + 0,0,0, -5,0,40, 0,0,0, // t0: mains/pieds/- + // au sol devant : + 125,115,-45,10,0,50, 0,0,0, // t1: bras/cuisses/- + 0,-20,0, -5,0,-15, 0,0,0, // t1: avant-bras/jambes/- + 0,0,0, -5,0,0, 0,0,0, // t1: mains/pieds/- + // au sol derrière : + 25,55,-40, 10,0,-15, 0,0,0, // t2: bras/cuisses/- + 30,-50,40, -5,0,-55, 0,0,0, // t2: avant-bras/jambes/- + 0,0,0, -5,0,25, 0,0,0, // t2: mains/pieds/- + }; + + int member_march_take[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, // en l'air : + 15,50,-50, 10,0,55, 0,0,0, // t0: bras/cuisses/- + 45,-70,10, -5,0,-110, 0,0,0, // t0: avant-bras/jambes/- + -10,25,0, -5,0,40, 0,0,0, // t0: mains/pieds/- + // au sol devant : + 15,50,-55, 10,0,50, 0,0,0, // t1: bras/cuisses/- + 45,-70,10, -5,0,-15, 0,0,0, // t1: avant-bras/jambes/- + -10,25,0, -5,0,0, 0,0,0, // t1: mains/pieds/- + // au sol derrière : + 15,50,-45, 10,0,-15, 0,0,0, // t2: bras/cuisses/- + 45,-70,10, -5,0,-55, 0,0,0, // t2: avant-bras/jambes/- + -10,25,0, -5,0,45, 0,0,0, // t2: mains/pieds/- + }; + + int member_turn[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, // en l'air : + 90,90,-50, 10,0,30, 0,0,0, // t0: bras/cuisses/- + 0,-20,0, -5,0,-60, 0,0,0, // t0: avant-bras/jambes/- + 0,0,0, -5,0,30, 0,0,0, // t0: mains/pieds/- + // au sol devant : + 90,110,-45, 10,0,0, 0,0,0, // t1: bras/cuisses/- + 0,-20,0, -5,5,0, 0,0,0, // t1: avant-bras/jambes/- + 0,0,0, -5,10,0, 0,0,0, // t1: mains/pieds/- + // au sol derrière : + 90,70,-45, 10,0,0, 0,0,0, // t2: bras/cuisses/- + 0,-20,10, -5,-5,0, 0,0,0, // t2: avant-bras/jambes/- + 0,0,0, -5,-10,0, 0,0,0, // t2: mains/pieds/- + }; + + int member_stop[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, + 90,90,-50, 10,0,5, 0,0,0, // bras/cuisses/- + 0,-20,0, 0,0,-10, 0,0,0, // avant-bras/jambes/- + 0,0,0, -10,5,5, 0,0,0, // mains/pieds/- + // + 90,90,-55, 10,0,5, 0,0,0, // bras/cuisses/- + 0,-15,0, 0,0,-10, 0,0,0, // avant-bras/jambes/- + 0,0,0, -10,5,5, 0,0,0, // mains/pieds/- + // + 90,90,-60, 10,0,5, 0,0,0, // bras/cuisses/- + 0,-10,0, 0,0,-10, 0,0,0, // avant-bras/jambes/- + 0,0,0, -10,5,5, 0,0,0, // mains/pieds/- + }; + + int member_fly[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, + -5,90,-60, 20,5,-25, 0,0,0, // bras/cuisses/- + 85,-40,-25, 10,0,-30, 0,0,0, // avant-bras/jambes/- + 40,10,25, 0,15,0, 0,0,0, // mains/pieds/- + // + -15,90,-40, 20,5,-35, 0,0,0, // bras/cuisses/- + 85,-40,-25, 10,0,-40, 0,0,0, // avant-bras/jambes/- + 45,5,20, 0,15,0, 0,0,0, // mains/pieds/- + // + -25,90,-50, 20,5,-20, 0,0,0, // bras/cuisses/- + 85,-40,-25, 10,0,-10, 0,0,0, // avant-bras/jambes/- + 30,15,25, 0,15,0, 0,0,0, // mains/pieds/- + }; + + int member_swim[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, +#if 1 + 130,-70,200,10,20,55, 0,0,0, // bras/cuisses/- + 115,-125,0, -5,0,-110, 0,0,0, // avant-bras/jambes/- + 0,0,0, -5,10,-5, 0,0,0, // mains/pieds/- + // + 130,-95,115,55,5,5, 0,0,0, // bras/cuisses/- + 75,-50,25, -5,0,-15, 0,0,0, // avant-bras/jambes/- + 0,0,0, -5,5,-30, 0,0,0, // mains/pieds/- + // + 130,-100,220,5,0,0, 0,0,0, // bras/cuisses/- + 150,5,0, -5,0,-15, 0,0,0, // avant-bras/jambes/- + 0,0,0, -5,30,-20, 0,0,0, // mains/pieds/- +#endif +#if 0 + 130,-70,200,5,0,0, 0,0,0, // bras/cuisses/- + 115,-125,0, -5,0,-15, 0,0,0, // avant-bras/jambes/- + 0,0,0, -5,30,-20, 0,0,0, // mains/pieds/- + // + 130,-95,115,10,20,55, 0,0,0, // bras/cuisses/- + 75,-50,25, -5,0,-110, 0,0,0, // avant-bras/jambes/- + 0,0,0, -5,10,-5, 0,0,0, // mains/pieds/- + // + 130,-100,220,55,5,5, 0,0,0, // bras/cuisses/- + 150,5,0, -5,0,-15, 0,0,0, // avant-bras/jambes/- + 0,0,0, -5,5,-30, 0,0,0, // mains/pieds/- +#endif +#if 0 + 130,-70,200,55,5,5, 0,0,0, // bras/cuisses/- + 115,-125,0, -5,0,-15, 0,0,0, // avant-bras/jambes/- + 0,0,0, -5,5,-30, 0,0,0, // mains/pieds/- + // + 130,-95,115,5,0,0, 0,0,0, // bras/cuisses/- + 75,-50,25, -5,0,-15, 0,0,0, // avant-bras/jambes/- + 0,0,0, -5,30,-20, 0,0,0, // mains/pieds/- + // + 130,-100,220,10,20,55, 0,0,0, // bras/cuisses/- + 150,5,0, -5,0,-110, 0,0,0, // avant-bras/jambes/- + 0,0,0, -5,10,-5, 0,0,0, // mains/pieds/- +#endif + }; + + int member_spec[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, // tir : + 65,5,-20, 10,0,40, 0,0,0, // s0: bras/cuisses/- + -50,-30,50, 0,0,-70, 0,0,0, // s0: avant-bras/jambes/- + 0,50,0, -10,0,35, 0,0,0, // s0: mains/pieds/- + // prend arme : + 160,135,-20,10,0,5, 0,0,0, // s1: bras/cuisses/- + 10,-60,40, 0,0,-10, 0,0,0, // s1: avant-bras/jambes/- + 0,-5,-25, -10,5,5, 0,0,0, // s1: mains/pieds/- + // porte à terre : + 25,40,-40, 10,0,60, 0,0,0, // s2: bras/cuisses/- + 0,-45,0, 0,0,-120, 0,0,0, // s2: avant-bras/jambes/- + 0,15,5, -10,0,70, 0,0,0, // s2: mains/pieds/- + // porte devant : + 25,20,5, 10,0,55, 0,0,0, // s3: bras/cuisses/- + -15,-30,10, 0,0,-110, 0,0,0, // s3: avant-bras/jambes/- + 0,0,0, -10,0,65, 0,0,0, // s3: mains/pieds/- + // porte en hauteur : + -30,15,-5, 10,0,15, 0,0,0, // s4: bras/cuisses/- + 0,-15,15, 0,0,-30, 0,0,0, // s4: avant-bras/jambes/- + 35,0,-15, -10,0,25, 0,0,0, // s4: mains/pieds/- + // se relève : + 15,50,-50, 10,0,5, 0,0,0, // s5: bras/cuisses/- + 45,-70,10, 0,0,-10, 0,0,0, // s5: avant-bras/jambes/- + -10,25,0, -10,5,5, 0,0,0, // s5: mains/pieds/- + // gagné : + 90,90,-30, 20,0,5, 0,0,0, // s6: bras/cuisses/- + 0,-90,0, -10,0,-10, 0,0,0, // s6: avant-bras/jambes/- + 0,25,0, -10,5,5, 0,0,0, // s6: mains/pieds/- + // perdu : + -70,45,35, 10,0,40, 0,0,0, // s7: bras/cuisses/- + 15,-95,-5, 0,0,-70, 0,0,0, // s7: avant-bras/jambes/- + 0,0,0, -10,0,35, 0,0,0, // s7: mains/pieds/- + // mort par balle (tombe) : + 90,90,-50, 10,0,5, 0,0,0, // s8: bras/cuisses/- + 0,-20,0, 0,0,-10, 0,0,0, // s8: avant-bras/jambes/- + 0,0,0, -10,5,5, 0,0,0, // s8: mains/pieds/- + // mort par balle (genoux) : + 110,105,-5, 10,0,25, 0,0,0, // s9: bras/cuisses/- + 0,-40,20, 0,0,-120, 0,0,0, // s9: avant-bras/jambes/- + 0,0,0, -10,5,5, 0,0,0, // s9: mains/pieds/- + // mort par balle (genoux) : + 110,120,-25,10,0,25, 0,0,0, // s10: bras/cuisses/- + 0,-40,20, 0,0,-120, 0,0,0, // s10: avant-bras/jambes/- + 0,0,0, -10,5,5, 0,0,0, // s10: mains/pieds/- + // mort par balle (plat ventre) : + 110,100,-25,25,0,10, 0,0,0, // s11: bras/cuisses/- + 0,-40,20, 0,0,-25, 0,0,0, // s11: avant-bras/jambes/- + 0,0,0, -10,5,5, 0,0,0, // s11: mains/pieds/- + // mort par balle (plat ventre) : + 110,100,-25,25,0,10, 0,0,0, // s12: bras/cuisses/- + 0,-40,20, 0,0,-25, 0,0,0, // s12: avant-bras/jambes/- + 0,0,0, -10,5,5, 0,0,0, // s12: mains/pieds/- + // mort noyé : + 110,100,-25,25,0,10, 0,0,0, // s13: bras/cuisses/- + 0,-40,20, 0,0,-25, 0,0,0, // s13: avant-bras/jambes/- + 0,0,0, -10,5,5, 0,0,0, // s13: mains/pieds/- + // met/enlève drapeau : + 85,45,-50, 10,0,60, 0,0,0, // s14: bras/cuisses/- + -60,15,65, 0,0,-105, 0,0,0, // s14: avant-bras/jambes/- + 0,10,0, -10,0,60, 0,0,0, // s14: mains/pieds/- + // lit SatCom : + 70,30,-20, 10,0,5, 0,0,0, // s15: bras/cuisses/- + 115,-65,60, 0,0,-10, 0,0,0, // s15: avant-bras/jambes/- + 0,20,0, -10,5,5, 0,0,0, // s15: mains/pieds/- + }; + + m_physics->SetType(TYPE_FLYING); + + character = m_object->RetCharacter(); + character->wheelFront = 4.0f; + character->wheelBack = 4.0f; + character->wheelLeft = 4.0f; + character->wheelRight = 4.0f; + character->height = 3.5f; + + if ( type == OBJECT_HUMAN ) + { + m_physics->SetLinMotionX(MO_ADVSPEED, 50.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 35.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 20.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 20.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 20.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 70.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 40.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 40.0f); + m_physics->SetLinMotionY(MO_ADVSPEED, 60.0f); + m_physics->SetLinMotionY(MO_RECSPEED, 60.0f); + m_physics->SetLinMotionY(MO_ADVACCEL, 20.0f); + m_physics->SetLinMotionY(MO_RECACCEL, 50.0f); + m_physics->SetLinMotionY(MO_STOACCEL, 50.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.8f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.8f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 6.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 6.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 4.0f); + } + else + { + m_physics->SetLinMotionX(MO_ADVSPEED, 40.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 15.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 8.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 8.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 8.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 50.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 50.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 40.0f); + m_physics->SetLinMotionY(MO_ADVSPEED, 60.0f); + m_physics->SetLinMotionY(MO_RECSPEED, 60.0f); + m_physics->SetLinMotionY(MO_ADVACCEL, 20.0f); + m_physics->SetLinMotionY(MO_RECACCEL, 50.0f); + m_physics->SetLinMotionY(MO_STOACCEL, 50.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.6f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.6f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 4.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 4.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 3.0f); + } + + for ( i=0 ; i<3*3*3*3 ; i++ ) + { + m_armAngles[3*3*3*3*MH_MARCH+i] = member_march[i]; + } + for ( i=0 ; i<3*3*3*3 ; i++ ) + { + m_armAngles[3*3*3*3*MH_MARCHTAKE+i] = member_march_take[i]; + } + for ( i=0 ; i<3*3*3*3 ; i++ ) + { + m_armAngles[3*3*3*3*MH_TURN+i] = member_turn[i]; + } + for ( i=0 ; i<3*3*3*3 ; i++ ) + { + m_armAngles[3*3*3*3*MH_STOP+i] = member_stop[i]; + } + for ( i=0 ; i<3*3*3*3 ; i++ ) + { + m_armAngles[3*3*3*3*MH_FLY+i] = member_fly[i]; + } + for ( i=0 ; i<3*3*3*3 ; i++ ) + { + m_armAngles[3*3*3*3*MH_SWIM+i] = member_swim[i]; + } + for ( i=0 ; i<3*3*3*16 ; i++ ) + { + m_armAngles[3*3*3*3*MH_SPEC+i] = member_spec[i]; + } +} + + +// Gestion d'un événement. + +BOOL CMotionHuman::EventProcess(const Event &event) +{ + CMotion::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + + if ( event.event == EVENT_KEYDOWN ) + { +#if ADJUST_ANGLE + int i; + + if ( event.param == 'A' ) m_armTimeIndex++; + if ( m_armTimeIndex >= 3 ) m_armTimeIndex = 0; + + if ( event.param == 'Q' ) m_armPartIndex++; + if ( m_armPartIndex >= 3 ) m_armPartIndex = 0; + + if ( event.param == 'W' ) m_armMemberIndex++; + if ( m_armMemberIndex >= 3 ) m_armMemberIndex = 0; + + i = m_armMemberIndex*3; + i += m_armPartIndex*3*3; + i += m_armTimeIndex*3*3*3; + i += ADJUST_ACTION; + + if ( event.param == 'E' ) m_armAngles[i+0] += 5; + if ( event.param == 'D' ) m_armAngles[i+0] -= 5; + if ( event.param == 'R' ) m_armAngles[i+1] += 5; + if ( event.param == 'F' ) m_armAngles[i+1] -= 5; + if ( event.param == 'T' ) m_armAngles[i+2] += 5; + if ( event.param == 'G' ) m_armAngles[i+2] -= 5; + + if ( event.param == 'Y' ) m_bArmStop = !m_bArmStop; + + if ( event.param == 'Y' ) + { + char s[100]; + sprintf(s, "index dans table = %d %d %d\n", i, i+9, i+18); + OutputDebugString(s); + } +#endif + } + + return TRUE; +} + +// Calcule une valeur (radians) proportionnelle comprise +// entre a et b (degrés). + +inline float Propf(float a, float b, float p) +{ + float aa, bb; + + aa = a*PI/180.0f; + bb = b*PI/180.0f; + + return aa+p*(bb-aa); +} + +// Gestion d'un événement. + +BOOL CMotionHuman::EventFrame(const Event &event) +{ + D3DMATRIX* mat; + D3DVECTOR dir, actual, pos, speed, pf; + FPOINT center, dim, p2; + float s, a, prog, rTime[2], lTime[2], time, rot, hr, hl; + float al, ar, af; + float tSt[9], tNd[9]; + float aa, bb, shield, deadFactor, level; + int i, ii, st, nd, action, legAction, armAction; + BOOL bOnBoard, bSwim, bStop; + + if ( m_engine->RetPause() ) + { + if ( m_actionType == MHS_SATCOM ) + { + m_progress += event.rTime*m_actionTime; + } + else + { + return TRUE; + } + } + + bOnBoard = FALSE; + if ( m_object->RetSelect() && + m_camera->RetType() == CAMERA_ONBOARD ) + { + bOnBoard = TRUE; + } + + if ( m_bDisplayPerso && m_main->RetGamerOnlyHead() ) + { + m_time += event.rTime; + m_object->SetLinVibration(D3DVECTOR(0.0f, -0.55f, 0.0f)); + m_object->SetCirVibration(D3DVECTOR(0.0f, m_main->RetPersoAngle(), 0.0f)); + return TRUE; + } + if ( m_bDisplayPerso ) + { + m_object->SetCirVibration(D3DVECTOR(0.0f, m_main->RetPersoAngle()+0.2f, 0.0f)); + } + + shield = m_object->RetShield(); + shield += event.rTime*(1.0f/120.0f); // régénération en 120 secondes + if ( shield > 1.0f ) shield = 1.0f; + m_object->SetShield(shield); + + bSwim = m_physics->RetSwim(); + +#if 0 + rot = m_physics->RetCirMotionY(MO_MOTSPEED); + s = m_physics->RetLinMotionX(MO_REASPEED)*2.0f; + a = m_physics->RetLinMotionX(MO_TERSPEED); + if ( a < 0.0f ) // monte ? + { + if ( s > 0.0f && s < 20.0f ) s = 20.0f; // avance lentement ? +//? if ( s < 0.0f && s > -10.0f ) s = 0.0f; // recule lentement ? + } + if ( a > 0.0f && !bSwim ) // descend ? + { + if ( s > 0.0f && s < 10.0f ) s = 0.0f; // avance lentement ? +//? if ( s < 0.0f && s > -5.0f ) s = -5.0f; // recule lentement ? + } + a = Abs(rot*12.0f); + + if ( !m_physics->RetLand() && !bSwim ) // en vol ? + { + s = 0.0f; + } + + if ( m_object->RetFret() != 0 ) // porte qq chose ? + { + s *= 1.3f; + } +#else + rot = m_physics->RetCirMotionY(MO_MOTSPEED); +#if 0 + s = m_physics->RetLinMotionX(MO_REASPEED); +#else + a = m_physics->RetLinMotionX(MO_REASPEED); + s = m_physics->RetLinMotionX(MO_MOTSPEED)*0.2f; + if ( Abs(a) > Abs(s) ) s = a; // la plus grande valeur +#endif + a = m_physics->RetLinMotionX(MO_TERSPEED); + if ( a < 0.0f ) // monte ? + { + a += m_physics->RetLinMotionX(MO_TERSLIDE); + if ( a < 0.0f ) s -= a; + } + if ( a > 0.0f ) // descend ? + { + a -= m_physics->RetLinMotionX(MO_TERSLIDE); + if ( a > 0.0f ) s -= a; + } + s *= 2.0f; + a = Abs(rot*12.0f); + + if ( !m_physics->RetLand() && !bSwim ) // en vol ? + { + s = 0.0f; + } + + if ( m_object->RetFret() != 0 ) // porte qq chose ? + { + s *= 1.3f; + } +#endif + + m_time += event.rTime; + m_armTimeAbs += event.rTime; + m_armTimeAction += event.rTime; + m_armMember += s*event.rTime*0.05f; + + // Gestion de la fatigue lorsqu'on court. + if ( m_physics->RetLand() && s != 0.0f ) // au sol ? + { + m_tired += event.rTime*0.1f; + if ( m_tired > 1.0f ) + { + m_tired = 1.0f; + if ( m_lastSoundHhh > 3.0f ) m_lastSoundHhh = 0.5f; + } + } + else + { + m_tired -= event.rTime*0.2f; + if ( m_tired < 0.0f ) m_tired = 0.0f; + } + + if ( bSwim ) // nage ? + { + s += Abs(m_physics->RetLinMotionY(MO_REASPEED)*2.0f); + a *= 2.0f; + m_armTimeSwim += Min(Max(s,a,3.0f),15.0f)*event.rTime*0.05f; + } + + bStop = ( s == 0.0f ); // à l'arrêt ? + prog = 0.0f; + + if ( m_physics->RetLand() ) // au sol ? + { + if ( s == 0.0f && a == 0.0f ) + { + action = MH_STOP; // stop + rTime[0] = rTime[1] = m_armTimeAbs*0.21f; + lTime[0] = lTime[1] = m_armTimeAbs*0.25f; + m_armMember = START_TIME; + } + else + { + if ( s == 0.0f ) + { + action = MH_TURN; // turn + rTime[0] = rTime[1] = m_armTimeAbs; + lTime[0] = lTime[1] = m_armTimeAbs+0.5f; + if ( rot < 0.0f ) + { + rTime[1] = 1000000.0f-rTime[1]; + } + else + { + lTime[1] = 1000000.0f-lTime[1]; + } + m_armMember = START_TIME; + } + else + { + action = MH_MARCH; // march + if ( m_object->RetFret() != 0 ) action = MH_MARCHTAKE; // march-take + rTime[0] = rTime[1] = m_armMember; + lTime[0] = lTime[1] = m_armMember+0.5f; + } + } + if ( bSwim ) + { + rTime[0] *= 0.6f; + rTime[1] *= 0.6f; + lTime[0] = rTime[0]+0.5f; + lTime[1] = rTime[1]+0.5f; + } + } + else + { + if ( bSwim ) + { + action = MH_SWIM; // nage + rTime[0] = rTime[1] = m_armTimeSwim; + lTime[0] = lTime[1] = m_armTimeSwim; + } + else + { + action = MH_FLY; // fly + rTime[0] = rTime[1] = m_armTimeAbs*0.30f; + lTime[0] = lTime[1] = m_armTimeAbs*0.31f; + m_armMember = START_TIME; + } + } + + if ( action != m_armLastAction ) + { + m_armLastAction = action; + m_armTimeAction = 0.0f; + } + + armAction = action; + legAction = action; + + if ( m_object->RetFret() != 0 ) // porte qq chose ? + { + armAction = MH_MARCHTAKE; // march-take + } + + if ( m_physics->RetLand() ) // au sol ? + { + a = m_object->RetAngleY(0); + pos = m_object->RetPosition(0); + m_terrain->MoveOnFloor(pos); + + pf.x = pos.x+cosf(a+PI*1.5f)*0.7f; + pf.y = pos.y; + pf.z = pos.z-sinf(a+PI*1.5f)*0.7f; + m_terrain->MoveOnFloor(pf); + al = atanf((pf.y-pos.y)/0.7f); // angle pour jambe gauche + + pf = pos; + pf.x = pos.x+cosf(a+PI*0.5f)*0.7f; + pf.y = pos.y; + pf.z = pos.z-sinf(a+PI*0.5f)*0.7f; + m_terrain->MoveOnFloor(pf); + ar = atanf((pf.y-pos.y)/0.7f); // angle pour jambe droite + + pf.x = pos.x+cosf(a+PI)*0.3f; + pf.y = pos.y; + pf.z = pos.z-sinf(a+PI)*0.3f; + m_terrain->MoveOnFloor(pf); + af = atanf((pf.y-pos.y)/0.3f); // angle pour pieds + } + else + { + al = 0.0f; + ar = 0.0f; + af = 0.0f; + } + + for ( i=0 ; i<4 ; i++ ) // les 4 membres + { + if ( m_bArmStop ) // mise au point ? + { + st = ADJUST_ACTION + (i%2)*3; + nd = st; + time = 100.0f; + m_armTimeAction = 0.0f; + } + else if ( m_actionType != -1 ) // action spéciale en cours ? + { + st = 3*3*3*3*MH_SPEC + 3*3*3*m_actionType + (i%2)*3; + nd = st; + time = event.rTime*m_actionTime; + m_armTimeAction = 0.0f; + } + else + { + if ( i < 2 ) prog = Mod(rTime[i%2], 1.0f); + else prog = Mod(lTime[i%2], 1.0f); + if ( prog < 0.25f ) // t0..t1 ? + { + prog = prog/0.25f; // 0..1 + st = 0; // index start + nd = 1; // index end + } + else if ( prog < 0.75f ) // t1..t2 ? + { + prog = (prog-0.25f)/0.50f; // 0..1 + st = 1; // index start + nd = 2; // index end + } + else // t2..t0 ? + { + prog = (prog-0.75f)/0.25f; // 0..1 + st = 2; // index start + nd = 0; // index end + } + if ( i%2 == 0 ) // bras ? + { + st = 3*3*3*3*armAction + st*3*3*3 + (i%2)*3; + nd = 3*3*3*3*armAction + nd*3*3*3 + (i%2)*3; + } + else // jambe ? + { + st = 3*3*3*3*legAction + st*3*3*3 + (i%2)*3; + nd = 3*3*3*3*legAction + nd*3*3*3 + (i%2)*3; + } + + // De moins en moins mou ... + time = event.rTime*(5.0f+Min(m_armTimeAction*50.0f, 100.0f)); + if ( bSwim ) time *= 0.25f; + } + + tSt[0] = m_armAngles[st+ 0]; // x + tSt[1] = m_armAngles[st+ 1]; // y + tSt[2] = m_armAngles[st+ 2]; // z + tSt[3] = m_armAngles[st+ 9]; // x + tSt[4] = m_armAngles[st+10]; // y + tSt[5] = m_armAngles[st+11]; // z + tSt[6] = m_armAngles[st+18]; // x + tSt[7] = m_armAngles[st+19]; // y + tSt[8] = m_armAngles[st+20]; // z + + tNd[0] = m_armAngles[nd+ 0]; // x + tNd[1] = m_armAngles[nd+ 1]; // y + tNd[2] = m_armAngles[nd+ 2]; // z + tNd[3] = m_armAngles[nd+ 9]; // x + tNd[4] = m_armAngles[nd+10]; // y + tNd[5] = m_armAngles[nd+11]; // z + tNd[6] = m_armAngles[nd+18]; // x + tNd[7] = m_armAngles[nd+19]; // y + tNd[8] = m_armAngles[nd+20]; // z + + aa = 0.5f; + if ( i%2 == 0 ) // bras ? + { + if ( m_object->RetFret() == 0 ) // ne porte rien ? + { + aa = 2.0f; // bouge beaucoup + } + else + { + aa = 0.0f; // immobile + } + } + + if ( i < 2 ) // gauche ? + { + bb = sinf(m_time*1.1f)*aa; tSt[0] += bb; tNd[0] += bb; + bb = sinf(m_time*1.0f)*aa; tSt[1] += bb; tNd[1] += bb; + bb = sinf(m_time*1.2f)*aa; tSt[2] += bb; tNd[2] += bb; + bb = sinf(m_time*2.5f)*aa; tSt[3] += bb; tNd[3] += bb; + bb = sinf(m_time*2.0f)*aa; tSt[4] += bb; tNd[4] += bb; + bb = sinf(m_time*3.8f)*aa; tSt[5] += bb; tNd[5] += bb; + bb = sinf(m_time*3.0f)*aa; tSt[6] += bb; tNd[6] += bb; + bb = sinf(m_time*2.3f)*aa; tSt[7] += bb; tNd[7] += bb; + bb = sinf(m_time*4.0f)*aa; tSt[8] += bb; tNd[8] += bb; + } + else // droite ? + { + bb = sinf(m_time*0.9f)*aa; tSt[0] += bb; tNd[0] += bb; + bb = sinf(m_time*1.2f)*aa; tSt[1] += bb; tNd[1] += bb; + bb = sinf(m_time*1.4f)*aa; tSt[2] += bb; tNd[2] += bb; + bb = sinf(m_time*2.9f)*aa; tSt[3] += bb; tNd[3] += bb; + bb = sinf(m_time*1.4f)*aa; tSt[4] += bb; tNd[4] += bb; + bb = sinf(m_time*3.1f)*aa; tSt[5] += bb; tNd[5] += bb; + bb = sinf(m_time*3.7f)*aa; tSt[6] += bb; tNd[6] += bb; + bb = sinf(m_time*2.0f)*aa; tSt[7] += bb; tNd[7] += bb; + bb = sinf(m_time*3.1f)*aa; tSt[8] += bb; tNd[8] += bb; + } + +#if 1 + if ( i%2 == 1 && // jambe ? + m_actionType == -1 ) // pas action spéciale ? + { + if ( i == 1 ) // jambe droite ? + { + ii = 5; + a = ar*0.25f; + } + else + { + ii = 11; + a = al*0.25f; + } + if ( a < -0.2f ) a = -0.2f; + if ( a > 0.2f ) a = 0.2f; + + pos = m_object->RetPosition(ii+0); + pos.y = 0.0f+a; + m_object->SetPosition(ii+0, pos); // allonge/raccourci cuisse + + pos = m_object->RetPosition(ii+1); + pos.y = -1.5f+a; + m_object->SetPosition(ii+1, pos); // allonge/raccourci jambe + + pos = m_object->RetPosition(ii+2); + pos.y = -1.5f+a; + m_object->SetPosition(ii+2, pos); // allonge/raccourci pied + + if ( i == 1 ) // jambe droite ? + { + aa = (ar*180.0f/PI*0.5f); + } + else // jambe gauche ? + { + aa = (al*180.0f/PI*0.5f); + } + tSt[6] += aa; + tNd[6] += aa; // augmente l'angle X du pied + + if ( i == 1 ) // jambe droite ? + { + aa = (ar*180.0f/PI); + } + else // jambe gauche ? + { + aa = (al*180.0f/PI); + } + if ( aa < 0.0f ) aa = 0.0f; + if ( aa > 30.0f ) aa = 30.0f; + + tSt[2] += aa; + tNd[2] += aa; // augmente l'angle Z de la cuisse + tSt[5] -= aa*2; + tNd[5] -= aa*2; // augmente l'angle Z de la jambe + tSt[8] += aa; + tNd[8] += aa; // augmente l'angle Z du pied + + aa = (af*180.0f/PI)*0.7f; + if ( aa < -30.0f ) aa = -30.0f; + if ( aa > 30.0f ) aa = 30.0f; + + tSt[8] -= aa; + tNd[8] -= aa; // augmente l'angle Z du pied + } +#endif + + if ( m_actionType == MHS_DEADw ) // mort noyé ? + { + if ( m_progress < 0.5f ) + { + deadFactor = m_progress/0.5f; + } + else + { + deadFactor = 1.0f-(m_progress-0.5f)/0.5f; + } + if ( deadFactor < 0.0f ) deadFactor = 0.0f; + if ( deadFactor > 1.0f ) deadFactor = 1.0f; + + for ( ii=0 ; ii<9 ; ii++ ) + { + tSt[ii] += Rand()*20.0f*deadFactor; + tNd[ii] = tSt[ii]; + } + time = 100.0f; + } + + if ( i < 2 ) // membre droite (0..1) ? + { + m_object->SetAngleX(2+3*i+0, Smooth(m_object->RetAngleX(2+3*i+0), Propf(tSt[0], tNd[0], prog), time)); + m_object->SetAngleY(2+3*i+0, Smooth(m_object->RetAngleY(2+3*i+0), Propf(tSt[1], tNd[1], prog), time)); + m_object->SetAngleZ(2+3*i+0, Smooth(m_object->RetAngleZ(2+3*i+0), Propf(tSt[2], tNd[2], prog), time)); + m_object->SetAngleX(2+3*i+1, Smooth(m_object->RetAngleX(2+3*i+1), Propf(tSt[3], tNd[3], prog), time)); + m_object->SetAngleY(2+3*i+1, Smooth(m_object->RetAngleY(2+3*i+1), Propf(tSt[4], tNd[4], prog), time)); + m_object->SetAngleZ(2+3*i+1, Smooth(m_object->RetAngleZ(2+3*i+1), Propf(tSt[5], tNd[5], prog), time)); + m_object->SetAngleX(2+3*i+2, Smooth(m_object->RetAngleX(2+3*i+2), Propf(tSt[6], tNd[6], prog), time)); + m_object->SetAngleY(2+3*i+2, Smooth(m_object->RetAngleY(2+3*i+2), Propf(tSt[7], tNd[7], prog), time)); + m_object->SetAngleZ(2+3*i+2, Smooth(m_object->RetAngleZ(2+3*i+2), Propf(tSt[8], tNd[8], prog), time)); + } + else // membre gauche (2..3) ? + { + m_object->SetAngleX(2+3*i+0, Smooth(m_object->RetAngleX(2+3*i+0), Propf(-tSt[0], -tNd[0], prog), time)); + m_object->SetAngleY(2+3*i+0, Smooth(m_object->RetAngleY(2+3*i+0), Propf(-tSt[1], -tNd[1], prog), time)); + m_object->SetAngleZ(2+3*i+0, Smooth(m_object->RetAngleZ(2+3*i+0), Propf( tSt[2], tNd[2], prog), time)); + m_object->SetAngleX(2+3*i+1, Smooth(m_object->RetAngleX(2+3*i+1), Propf(-tSt[3], -tNd[3], prog), time)); + m_object->SetAngleY(2+3*i+1, Smooth(m_object->RetAngleY(2+3*i+1), Propf(-tSt[4], -tNd[4], prog), time)); + m_object->SetAngleZ(2+3*i+1, Smooth(m_object->RetAngleZ(2+3*i+1), Propf( tSt[5], tNd[5], prog), time)); + m_object->SetAngleX(2+3*i+2, Smooth(m_object->RetAngleX(2+3*i+2), Propf(-tSt[6], -tNd[6], prog), time)); + m_object->SetAngleY(2+3*i+2, Smooth(m_object->RetAngleY(2+3*i+2), Propf(-tSt[7], -tNd[7], prog), time)); + m_object->SetAngleZ(2+3*i+2, Smooth(m_object->RetAngleZ(2+3*i+2), Propf( tSt[8], tNd[8], prog), time)); + } + } + +#if ADJUST_ANGLE + if ( m_object->RetSelect() ) + { + char s[100]; + sprintf(s, "A:time=%d Q:part=%d W:member=%d", m_armTimeIndex, m_armPartIndex, m_armMemberIndex); + m_engine->SetInfoText(4, s); + } +#endif + + // Calcule la hauteur de rabaissement en fonction de la + // position des jambes. + hr = 1.5f*(1.0f-cosf(m_object->RetAngleZ(5))) + + 1.5f*(1.0f-cosf(m_object->RetAngleZ(5)+m_object->RetAngleZ(6))); + a = 1.0f*sinf(m_object->RetAngleZ(5)+m_object->RetAngleZ(6)+m_object->RetAngleZ(7)); + if ( a < 0.0f ) hr += a; + + hl = 1.5f*(1.0f-cosf(m_object->RetAngleZ(11))) + + 1.5f*(1.0f-cosf(m_object->RetAngleZ(11)+m_object->RetAngleZ(12))); + a = 1.0f*sinf(m_object->RetAngleZ(11)+m_object->RetAngleZ(12)+m_object->RetAngleZ(13)); + if ( a < 0.0f ) hl += a; + + hr = Min(hr, hl); + + if ( m_actionType == MHS_FIRE ) // tir ? + { + time = event.rTime*m_actionTime; + + dir.x = (Rand()-0.5f)/8.0f; + dir.z = (Rand()-0.5f)/8.0f; + dir.y = -0.5f; // légèrement plus bas + actual = m_object->RetLinVibration(); + dir.x = Smooth(actual.x, dir.x, time); +//? dir.y = Smooth(actual.y, dir.y, time); + dir.y = -hr; + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetLinVibration(dir); + + dir.x = 0.0f; + dir.y = (Rand()-0.5f)/3.0f; + dir.z = -0.1f; // légèrement penché en avant + actual = m_object->RetInclinaison(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetInclinaison(dir); + } + else if ( m_actionType == MHS_TAKE || // prend ? + m_actionType == MHS_TAKEOTHER ) // drapeau ? + { + time = event.rTime*m_actionTime*2.0f; + + dir.x = 0.0f; + dir.z = 0.0f; + dir.y = -1.5f; // légèrement plus bas + actual = m_object->RetLinVibration(); + dir.x = Smooth(actual.x, dir.x, time); +//? dir.y = Smooth(actual.y, dir.y, time); + dir.y = -hr; + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetLinVibration(dir); + + dir.x = 0.0f; + dir.y = 0.0f; + dir.z = -0.2f; + actual = m_object->RetInclinaison(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetInclinaison(dir); + } + else if ( m_actionType == MHS_TAKEHIGH ) // prend ? + { + time = event.rTime*m_actionTime*2.0f; + + dir.x = 0.4f; // avance légèrement + dir.z = 0.0f; + dir.y = 0.0f; + actual = m_object->RetLinVibration(); + dir.x = Smooth(actual.x, dir.x, time); +//? dir.y = Smooth(actual.y, dir.y, time); + dir.y = -hr; + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetLinVibration(dir); + + dir.x = 0.0f; + dir.y = 0.0f; + dir.z = -0.2f; + actual = m_object->RetInclinaison(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetInclinaison(dir); + } + else if ( m_actionType == MHS_FLAG ) // drapeau ? + { + time = event.rTime*m_actionTime*2.0f; + + dir.x = 0.0f; + dir.z = 0.0f; + dir.y = -2.0f; // légèrement plus bas + actual = m_object->RetLinVibration(); + dir.x = Smooth(actual.x, dir.x, time); +//? dir.y = Smooth(actual.y, dir.y, time); + dir.y = -hr; + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetLinVibration(dir); + + dir.x = 0.0f; + dir.y = 0.0f; + dir.z = -0.4f; + actual = m_object->RetInclinaison(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetInclinaison(dir); + } + else if ( m_actionType == MHS_DEADg ) // mort par balle (tombe) ? + { + if ( m_physics->RetLand() ) // au sol ? + { + SetAction(MHS_DEADg1, 0.5f); // genoux + } + } + else if ( m_actionType == MHS_DEADg1 ) // mort par balle (genoux) ? + { + prog = m_progress; + if ( prog >= 1.0f ) + { + prog = 1.0f; + + for ( i=0 ; i<10 ; i++ ) + { + pos = m_object->RetPosition(0); + pos.x += (Rand()-0.5f)*4.0f; + pos.z += (Rand()-0.5f)*4.0f; + m_terrain->MoveOnFloor(pos); + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 1.2f+Rand()*1.2f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f, 0.0f, 0.0f); + } + m_sound->Play(SOUND_BOUMv, m_object->RetPosition(0)); + + SetAction(MHS_DEADg2, 1.0f); // attente genoux + } + + time = 100.0f; + + dir.x = 0.0f; + dir.z = 0.0f; + dir.y = -1.5f*prog; + actual = m_object->RetLinVibration(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetLinVibration(dir); + + dir.x = 0.0f; + dir.y = 0.0f; + dir.z = -(20.0f*PI/180.0f)*prog; + actual = m_object->RetInclinaison(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetInclinaison(dir); + } + else if ( m_actionType == MHS_DEADg2 ) // mort par balle (genoux) ? + { + if ( m_progress >= 1.0f ) + { + SetAction(MHS_DEADg3, 1.0f); // plat ventre + } + + time = 100.0f; + + dir.x = 0.0f; + dir.z = 0.0f; + dir.y = -1.5f; + actual = m_object->RetLinVibration(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetLinVibration(dir); + + dir.x = 0.0f; + dir.y = 0.0f; + dir.z = -(20.0f*PI/180.0f); + actual = m_object->RetInclinaison(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetInclinaison(dir); + } + else if ( m_actionType == MHS_DEADg3 ) // mort par balle (plat ventre) ? + { + prog = m_progress; + if ( prog >= 1.0f ) + { + prog = 1.0f; + + for ( i=0 ; i<20 ; i++ ) + { + pos = m_object->RetPosition(0); + pos.x += (Rand()-0.5f)*8.0f; + pos.z += (Rand()-0.5f)*8.0f; + m_terrain->MoveOnFloor(pos); + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 2.0f+Rand()*1.5f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f, 0.0f, 0.0f); + } + m_sound->Play(SOUND_BOUMv, m_object->RetPosition(0)); + + SetAction(MHS_DEADg4, 3.0f); // attente plat ventre + } + + time = 100.0f; + prog = powf(prog, 3.0f); + + dir.y = -(1.5f+1.5f*prog); + dir.x = 0.0f; + dir.z = 0.0f; + actual = m_object->RetLinVibration(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetLinVibration(dir); + + dir.z = -((20.0f*PI/180.0f)+(70.0f*PI/180.0f)*prog); + dir.x = 0.0f; + dir.y = 0.0f; + actual = m_object->RetInclinaison(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetInclinaison(dir); + } + else if ( m_actionType == MHS_DEADg4 ) // mort par balle (plat ventre) ? + { + if ( m_progress >= 1.0f ) + { + m_object->SetEnable(FALSE); + } + + time = 100.0f; + + dir.y = -(1.5f+1.5f); + dir.x = 0.0f; + dir.z = 0.0f; + actual = m_object->RetLinVibration(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetLinVibration(dir); + + dir.z = -((20.0f*PI/180.0f)+(70.0f*PI/180.0f)); + dir.x = 0.0f; + dir.y = 0.0f; + actual = m_object->RetInclinaison(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetInclinaison(dir); + } + else if ( m_actionType == MHS_DEADw ) // mort noyé ? + { + pos = m_object->RetPosition(0); + level = m_water->RetLevel()-0.5f; + if ( pos.y < level ) + { + pos.y += 4.0f*event.rTime; // remonte à la surface + if ( pos.y > level ) pos.y = level; + m_object->SetPosition(0, pos); + } + if ( pos.y > level ) + { + pos.y -= 10.0f*event.rTime; // descend vite + if ( pos.y < level ) pos.y = level; + m_object->SetPosition(0, pos); + } + + prog = m_progress; + if ( prog >= 1.0f ) + { + prog = 1.0f; + if ( pos.y >= level ) m_object->SetEnable(FALSE); + } + + prog *= 2.0f; + if ( prog > 1.0f ) prog = 1.0f; + + time = 100.0f; + + dir.z = -(90.0f*PI/180.0f)*prog; + dir.x = Rand()*0.3f*deadFactor; + dir.y = Rand()*0.3f*deadFactor; + actual = m_object->RetInclinaison(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + m_object->SetInclinaison(dir); + + m_object->SetCirVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + } + else if ( m_actionType == MHS_LOST ) // perdu ? + { + time = m_time; + if ( time < 10.0f ) time *= time/10.0f; // démarre lentement + + dir.x = time*2.0f; + dir.y = sinf(m_time*0.8f)*0.8f; + dir.z = sinf(m_time*0.6f)*0.5f; + m_object->SetInclinaison(dir); + SetInclinaison(dir); + +//? dir.x = -(sinf(time*0.05f+PI*1.5f)+1.0f)*100.0f; + dir.x = -(powf(Min(time/30.0f), 4.0f))*1000.0f; // part au loin + dir.y = 0.0f; + dir.z = 0.0f; + m_object->SetLinVibration(dir); + SetLinVibration(dir); + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(0.5f, 3.7f, 0.0f); + pos.x += (Rand()-0.5f)*1.0f; + pos.y += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + pos = Transform(*mat, pos); + speed.x = (Rand()-0.5f)*0.5f; + speed.y = (Rand()-0.5f)*0.5f; + speed.z = (Rand()-0.5f)*0.5f; + dim.x = 0.5f+Rand()*0.5f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTILENS1, 5.0f, 0.0f, 0.0f); + } + else if ( m_actionType == MHS_SATCOM ) // regarde le SatCom ? + { + SetCirVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + SetLinVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + SetInclinaison(D3DVECTOR(0.0f, 0.0f, 0.0f)); + } + else + { + if ( m_physics->RetLand() ) // au sol ? + { + time = event.rTime*8.0f; + if ( bSwim ) time *= 0.25f; + + if ( action == MH_MARCH ) // march ? + { + dir.x = sinf(Mod(rTime[0]+0.5f, 1.0f)*PI*2.0f)*0.10f; + dir.y = sinf(Mod(rTime[0]+0.6f, 1.0f)*PI*2.0f)*0.20f; + s = m_physics->RetLinMotionX(MO_REASPEED)*0.03f; + } + else if ( action == MH_MARCHTAKE ) // march-take ? + { + dir.x = sinf(Mod(rTime[0]+0.5f, 1.0f)*PI*2.0f)*0.10f; + dir.y = sinf(Mod(rTime[0]+0.6f, 1.0f)*PI*2.0f)*0.15f; + s = m_physics->RetLinMotionX(MO_REASPEED)*0.02f; + } + else + { + dir.x = 0.0f; + dir.y = 0.0f; + s = m_physics->RetLinMotionX(MO_REASPEED)*0.03f; + } + + if ( s < 0.0f ) s *= 0.5f; + dir.z = -s*0.7f; + + actual = m_object->RetInclinaison(); + dir.x = Smooth(actual.x, dir.x, time); + dir.y = Smooth(actual.y, dir.y, time); + dir.z = Smooth(actual.z, dir.z, time); + if ( bOnBoard ) dir *= 0.3f; + m_object->SetInclinaison(dir); + SetInclinaison(dir); + + if ( action == MH_MARCH ) // march ? + { + p2.x = 0.0f; + p2.y = sinf(Mod(rTime[0]+0.5f, 1.0f)*PI*2.0f)*0.5f; + p2 = RotatePoint(-m_object->RetAngleY(0), p2); + dir.x = p2.x; + dir.z = p2.y; + dir.y = sinf(Mod(rTime[0]*2.0f, 1.0f)*PI*2.0f)*0.3f; + } + else if ( action == MH_MARCHTAKE ) // march-take ? + { + p2.x = 0.0f; + p2.y = sinf(Mod(rTime[0]+0.5f, 1.0f)*PI*2.0f)*0.25f; + p2 = RotatePoint(-m_object->RetAngleY(0), p2); + dir.x = p2.x; + dir.z = p2.y; + dir.y = sinf(Mod(rTime[0]*2.0f, 1.0f)*PI*2.0f)*0.05f-0.3f; + } + else + { + dir.x = 0.0f; + dir.z = 0.0f; + dir.y = 0.0f; + } + + actual = m_object->RetLinVibration(); + dir.x = Smooth(actual.x, dir.x, time); + if ( action == MH_MARCHTAKE ) // march-take ? + { + dir.y = -hr; + } + else + { + s = Min(m_armTimeAction, 1.0f); + dir.y = Smooth(actual.y, dir.y, time)*s; + dir.y += -hr*(1.0f-s); + } + dir.z = Smooth(actual.z, dir.z, time); + if ( bOnBoard ) dir *= 0.3f; + m_object->SetLinVibration(dir); + + dir.x = 0.0f; + dir.z = 0.0f; + dir.y = 0.0f; + SetCirVibration(dir); + } + } + + // Gestion de la tête. + if ( m_actionType == MHS_TAKE || // prend ? + m_actionType == MHS_FLAG ) // prend ? + { + m_object->SetAngleZ(1, Smooth(m_object->RetAngleZ(1), sinf(m_armTimeAbs*1.0f)*0.2f-0.6f, event.rTime*5.0f)); + m_object->SetAngleX(1, sinf(m_armTimeAbs*1.1f)*0.1f); + m_object->SetAngleY(1, Smooth(m_object->RetAngleY(1), sinf(m_armTimeAbs*1.3f)*0.2f+rot*0.3f, event.rTime*5.0f)); + } + else if ( m_actionType == MHS_TAKEOTHER || // prend ? + m_actionType == MHS_TAKEHIGH ) // prend ? + { + m_object->SetAngleZ(1, Smooth(m_object->RetAngleZ(1), sinf(m_armTimeAbs*1.0f)*0.2f-0.3f, event.rTime*5.0f)); + m_object->SetAngleX(1, sinf(m_armTimeAbs*1.1f)*0.1f); + m_object->SetAngleY(1, Smooth(m_object->RetAngleY(1), sinf(m_armTimeAbs*1.3f)*0.2f+rot*0.3f, event.rTime*5.0f)); + } + else if ( m_actionType == MHS_WIN ) // gagné ? + { + float factor = 0.6f+(sinf(m_armTimeAbs*0.5f)*0.40f); + m_object->SetAngleZ(1, sinf(m_armTimeAbs*5.0f)*0.20f*factor); + m_object->SetAngleX(1, sinf(m_armTimeAbs*0.6f)*0.10f); + m_object->SetAngleY(1, sinf(m_armTimeAbs*1.5f)*0.15f); + } + else if ( m_actionType == MHS_LOST ) // perdu ? + { + float factor = 0.6f+(sinf(m_armTimeAbs*0.5f)*0.40f); + m_object->SetAngleZ(1, sinf(m_armTimeAbs*0.6f)*0.10f); + m_object->SetAngleX(1, sinf(m_armTimeAbs*0.7f)*0.10f); + m_object->SetAngleY(1, sinf(m_armTimeAbs*3.0f)*0.30f*factor); + } + else if ( m_object->RetDead() ) // mort ? + { + } + else + { + m_object->SetAngleZ(1, Smooth(m_object->RetAngleZ(1), sinf(m_armTimeAbs*1.0f)*0.2f, event.rTime*5.0f)); + m_object->SetAngleX(1, sinf(m_armTimeAbs*1.1f)*0.1f); + m_object->SetAngleY(1, Smooth(m_object->RetAngleY(1), sinf(m_armTimeAbs*1.3f)*0.2f+rot*0.3f, event.rTime*5.0f)); + } + + if ( bOnBoard ) + { + m_object->SetAngleZ(1, 0.0f); + m_object->SetAngleX(1, 0.0f); + m_object->SetAngleY(1, 0.0f); + } + + // Bruitage des pas. + if ( legAction == MH_MARCH || + legAction == MH_MARCHTAKE ) + { + Sound sound[2]; + float speed, synchro, volume[2], freq[2], hard, level; + + speed = m_physics->RetLinMotionX(MO_REASPEED); + + if ( m_object->RetFret() == 0 ) + { + if ( speed > 0.0f ) synchro = 0.21f; // synchro en avant + else synchro = 0.29f; // synchro en arrière + } + else + { + if ( speed > 0.0f ) synchro = 0.15f; // synchro en avant + else synchro = 0.35f; // synchro en arrière + } + time = rTime[1]+synchro; + + if ( Abs(m_lastSoundMarch-time) > 0.4f && + Mod(time, 0.5f) < 0.1f ) + { + volume[0] = 0.5f; + freq[0] = 1.0f; + if ( m_object->RetFret() != 0 ) + { +//? volume[0] *= 2.0f; + freq[0] = 0.7f; + } + volume[1] = volume[0]; + freq[1] = freq[0]; + sound[0] = SOUND_CLICK; + sound[1] = SOUND_CLICK; + + pos = m_object->RetPosition(0); + + level = m_water->RetLevel(); + if ( pos.y <= level+3.0f ) // sous l'eau ? + { + sound[0] = SOUND_STEPw; + } + else + { + hard = m_terrain->RetHardness(pos); + + if ( hard >= 0.875 ) + { + sound[0] = SOUND_STEPm; // metal + } + else + { + hard /= 0.875; + sound[0] = SOUND_STEPs; // smooth + sound[1] = SOUND_STEPh; // hard + + volume[0] *= 1.0f-hard; + volume[1] *= hard; + if ( hard < 0.5f ) + { + volume[0] *= 1.0f+hard*2.0f; + volume[1] *= 1.0f+hard*2.0f; + } + else + { + volume[0] *= 3.0f-hard*2.0f; + volume[1] *= 3.0f-hard*2.0f; + } + freq[0] *= 1.0f+hard; + freq[1] *= 0.5f+hard; + } + } + + if ( sound[0] != SOUND_CLICK ) + { + m_sound->Play(sound[0], pos, volume[0], freq[0]); + } + if ( sound[1] != SOUND_CLICK ) + { + m_sound->Play(sound[1], pos, volume[1], freq[1]); + } + m_lastSoundMarch = time; + } + } + + if ( legAction == MH_SWIM ) + { + time = rTime[0]+0.5f; + + if ( Abs(m_lastSoundMarch-time) > 0.9f && + Mod(time, 1.0f) < 0.1f ) + { + m_sound->Play(SOUND_SWIM, m_object->RetPosition(0), 0.5f); + m_lastSoundMarch = time; + } + } + + m_lastSoundHhh -= event.rTime; + if ( m_lastSoundHhh <= 0.0f && + m_object->RetSelect() && + m_object->RetOption() == 0 ) // casque ? + { + m_sound->Play(SOUND_HUMAN1, m_object->RetPosition(0), (0.5f+m_tired*0.2f)); + m_lastSoundHhh = (4.0f-m_tired*2.5f)+(4.0f-m_tired*2.5f)*Rand(); + } + + return TRUE; +} + + +// Gestion du mode d'affichage lors de la personnalisation du perso. + +void CMotionHuman::StartDisplayPerso() +{ + m_bDisplayPerso = TRUE; +} + +void CMotionHuman::StopDisplayPerso() +{ + m_bDisplayPerso = FALSE; +} + + diff --git a/src/motionhuman.h b/src/motionhuman.h new file mode 100644 index 00000000..d312c979 --- /dev/null +++ b/src/motionhuman.h @@ -0,0 +1,82 @@ +// motionhuman.h + +#ifndef _MOTIONHUMAN_H_ +#define _MOTIONHUMAN_H_ + + +class CInstanceManager; +class CEngine; +class CLight; +class CParticule; +class CTerrain; +class CCamera; +class CBrain; +class CPhysics; +class CObject; + + +#define MH_MARCH 0 +#define MH_MARCHTAKE 1 +#define MH_TURN 2 +#define MH_STOP 3 +#define MH_FLY 4 +#define MH_SWIM 5 +#define MH_SPEC 6 + +#define MHS_FIRE 0 +#define MHS_GUN 1 +#define MHS_TAKE 2 +#define MHS_TAKEOTHER 3 +#define MHS_TAKEHIGH 4 +#define MHS_UPRIGHT 5 +#define MHS_WIN 6 +#define MHS_LOST 7 +#define MHS_DEADg 8 +#define MHS_DEADg1 9 +#define MHS_DEADg2 10 +#define MHS_DEADg3 11 +#define MHS_DEADg4 12 +#define MHS_DEADw 13 +#define MHS_FLAG 14 +#define MHS_SATCOM 15 + + +class CMotionHuman : public CMotion +{ +public: + CMotionHuman(CInstanceManager* iMan, CObject* object); + ~CMotionHuman(); + + void DeleteObject(BOOL bAll=FALSE); + BOOL Create(D3DVECTOR pos, float angle, ObjectType type, float power); + BOOL EventProcess(const Event &event); + Error SetAction(int action, float time=0.2f); + + void StartDisplayPerso(); + void StopDisplayPerso(); + +protected: + void CreatePhysics(ObjectType type); + BOOL EventFrame(const Event &event); + +protected: + int m_partiReactor; + float m_armMember; + float m_armTimeAbs; + float m_armTimeAction; + float m_armTimeSwim; + short m_armAngles[3*3*3*3*7 + 3*3*3*16]; + int m_armTimeIndex; + int m_armPartIndex; + int m_armMemberIndex; + int m_armLastAction; + BOOL m_bArmStop; + float m_lastSoundMarch; + float m_lastSoundHhh; + float m_time; + float m_tired; + BOOL m_bDisplayPerso; +}; + + +#endif //_MOTIONHUMAN_H_ diff --git a/src/motionmother.cpp b/src/motionmother.cpp new file mode 100644 index 00000000..b342eb85 --- /dev/null +++ b/src/motionmother.cpp @@ -0,0 +1,527 @@ +// motionmother.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "modfile.h" +#include "sound.h" +#include "motion.h" +#include "motionmother.h" + + + +#define ADJUST_ANGLE FALSE // TRUE -> ajuste les angles des membres +#define START_TIME 1000.0f // début du temps relatif + + + +// Constructeur de l'objet. + +CMotionMother::CMotionMother(CInstanceManager* iMan, CObject* object) + : CMotion(iMan, object) +{ + CMotion::CMotion(iMan, object); + + m_armMember = START_TIME; + m_armTimeAbs = START_TIME; + m_armTimeMarch = START_TIME; + m_armTimeAction = START_TIME; + m_armTimeIndex = 0; + m_armPartIndex = 0; + m_armMemberIndex = 0; + m_armLastAction = -1; + m_specAction = -1; + m_bArmStop = FALSE; +} + +// Destructeur de l'objet. + +CMotionMother::~CMotionMother() +{ +} + + +// Supprime un objet. + +void CMotionMother::DeleteObject(BOOL bAll) +{ +} + + +// Crée un véhicule roulant quelconque posé sur le sol. + +BOOL CMotionMother::Create(D3DVECTOR pos, float angle, ObjectType type, + float power) +{ + CModFile* pModFile; + int rank; + + if ( m_engine->RetRestCreate() < 2+12+6 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + m_object->SetType(type); + + // Crée la base principale. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEVEHICULE); // c'est un objet mobile + m_object->SetObjectRank(0, rank); + + pModFile->ReadModel("objects\\mother1.mod"); + pModFile->CreateEngineObject(rank); + + m_object->SetPosition(0, pos); + m_object->SetAngleY(0, angle); + + // Un véhicule doit avoir obligatoirement une sphère de + // collision avec un centre (0;y;0) (voir GetCrashSphere). + m_object->CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 20.0f, SOUND_BOUM, 0.20f); + m_object->SetGlobalSphere(D3DVECTOR(-2.0f, 10.0f, 0.0f), 25.0f); + + // Crée la tête. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\mother2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(16.0f, 3.0f, 0.0f)); + + // Crée la jambe 1 arrière-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 0); + pModFile->ReadModel("objects\\mother3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(-5.0f, -1.0f, -12.0f)); + + // Crée le pied 1 arrière-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(3, rank); + m_object->SetObjectParent(3, 2); + pModFile->ReadModel("objects\\mother4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(3, D3DVECTOR(0.0f, 0.0f, -8.5f)); + + // Crée la jambe 2 milieu-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(4, rank); + m_object->SetObjectParent(4, 0); + pModFile->ReadModel("objects\\mother3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(4, D3DVECTOR(3.5f, -1.0f, -12.0f)); + + // Crée le pied 2 milieu-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(5, rank); + m_object->SetObjectParent(5, 4); + pModFile->ReadModel("objects\\mother4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(5, D3DVECTOR(0.0f, 0.0f, -8.5f)); + + // Crée la jambe 3 avant-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 0); + pModFile->ReadModel("objects\\mother3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(10.0f, -1.0f, -10.0f)); + + // Crée le pied 3 avant-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 6); + pModFile->ReadModel("objects\\mother4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(0.0f, 0.0f, -8.5f)); + + // Crée la jambe 1 arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(8, rank); + m_object->SetObjectParent(8, 0); + pModFile->ReadModel("objects\\mother3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(8, D3DVECTOR(-5.0f, -1.0f, 12.0f)); + m_object->SetAngleY(8, PI); + + // Crée le pied 1 arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(9, rank); + m_object->SetObjectParent(9, 8); + pModFile->ReadModel("objects\\mother4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(9, D3DVECTOR(0.0f, 0.0f, -8.5f)); + + // Crée la jambe 2 milieu-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(10, rank); + m_object->SetObjectParent(10, 0); + pModFile->ReadModel("objects\\mother3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(10, D3DVECTOR(3.5f, -1.0f, 12.0f)); + m_object->SetAngleY(10, PI); + + // Crée le pied 2 milieu-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(11, rank); + m_object->SetObjectParent(11, 10); + pModFile->ReadModel("objects\\mother4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(11, D3DVECTOR(0.0f, 0.0f, -8.5f)); + + // Crée la jambe 3 avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(12, rank); + m_object->SetObjectParent(12, 0); + pModFile->ReadModel("objects\\mother3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(12, D3DVECTOR(10.0f, -1.0f, 10.0f)); + m_object->SetAngleY(12, PI); + + // Crée le pied 3 avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(13, rank); + m_object->SetObjectParent(13, 12); + pModFile->ReadModel("objects\\mother4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(13, D3DVECTOR(0.0f, 0.0f, -8.5f)); + + // Crée l'antenne droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(14, rank); + m_object->SetObjectParent(14, 1); + pModFile->ReadModel("objects\\mother5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(14, D3DVECTOR(6.0f, 1.0f, -2.5f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(15, rank); + m_object->SetObjectParent(15, 14); + pModFile->ReadModel("objects\\mother6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(15, D3DVECTOR(8.0f, 0.0f, 0.0f)); + + // Crée l'antenne gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(16, rank); + m_object->SetObjectParent(16, 1); + pModFile->ReadModel("objects\\mother5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(16, D3DVECTOR(6.0f, 1.0f, 2.5f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(17, rank); + m_object->SetObjectParent(17, 16); + pModFile->ReadModel("objects\\mother6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(17, D3DVECTOR(8.0f, 0.0f, 0.0f)); + + // Crée la pince droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(18, rank); + m_object->SetObjectParent(18, 1); + pModFile->ReadModel("objects\\mother7.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(18, D3DVECTOR(-4.0f, -3.5f, -8.0f)); + m_object->SetZoomX(18, 1.2f); + + // Crée la pince gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(19, rank); + m_object->SetObjectParent(19, 1); + pModFile->ReadModel("objects\\mother7.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(19, D3DVECTOR(-4.0f, -3.5f, 8.0f)); + m_object->SetZoomX(19, 1.2f); + + m_object->CreateShadowCircle(18.0f, 0.8f); + + CreatePhysics(); + m_object->SetFloorHeight(0.0f); + + pos = m_object->RetPosition(0); + m_object->SetPosition(0, pos); // pour afficher les ombres tout de suite + + m_engine->LoadAllTexture(); + + delete pModFile; + return TRUE; +} + +// Crée la physique de l'objet. + +void CMotionMother::CreatePhysics() +{ + Character* character; + int i; + + int member[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, // en l'air : + 30,30,10, 35,-15,10, 35,-35,10, // t0: jambes 1..3 + -80,-45,-35, -115,-40,-35, -90,10,-55, // t0: pieds 1..3 + 0,0,0, 0,0,0, 0,0,0, // t0: inutilisé + // au sol devant : + 15,-5,10, 10,-30,10, 5,-50,10, // t1: jambes 1..3 + -90,-15,-15, -110,-55,-35, -75,-75,-30, // t1: pieds 1..3 + 0,0,0, 0,0,0, 0,0,0, // t1: inutilisé + // au sol derrière : + 0,40,10, 5,5,10, 0,-15,10, // t2: jambes 1..3 + -45,0,-55, -65,10,-50, -125,-85,-45, // t2: pieds 1..3 + 0,0,0, 0,0,0, 0,0,0, // t2: inutilisé + }; + + m_physics->SetType(TYPE_ROLLING); + + character = m_object->RetCharacter(); + character->wheelFront = 10.0f; + character->wheelBack = 10.0f; + character->wheelLeft = 20.0f; + character->wheelRight = 20.0f; + character->height = 3.0f; + + m_physics->SetLinMotionX(MO_ADVSPEED, 8.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 8.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 10.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 10.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 30.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 20.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 40.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.1f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.1f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 10.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 10.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 20.0f); + + for ( i=0 ; i<3*3*3*3 ; i++ ) + { + m_armAngles[i] = member[i]; + } +} + + +// Gestion d'un événement. + +BOOL CMotionMother::EventProcess(const Event &event) +{ + CMotion::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + + if ( event.event == EVENT_KEYDOWN ) + { +#if ADJUST_ANGLE + int i; + + if ( event.param == 'A' ) m_armTimeIndex++; + if ( m_armTimeIndex >= 3 ) m_armTimeIndex = 0; + + if ( event.param == 'Q' ) m_armPartIndex++; + if ( m_armPartIndex >= 3 ) m_armPartIndex = 0; + + if ( event.param == 'W' ) m_armMemberIndex++; + if ( m_armMemberIndex >= 3 ) m_armMemberIndex = 0; + + i = m_armMemberIndex*3; + i += m_armPartIndex*3*3; + i += m_armTimeIndex*3*3*3; +//? i += 3*3*3*3; + + if ( event.param == 'E' ) m_armAngles[i+0] += 5; + if ( event.param == 'D' ) m_armAngles[i+0] -= 5; + if ( event.param == 'R' ) m_armAngles[i+1] += 5; + if ( event.param == 'F' ) m_armAngles[i+1] -= 5; + if ( event.param == 'T' ) m_armAngles[i+2] += 5; + if ( event.param == 'G' ) m_armAngles[i+2] -= 5; + + if ( event.param == 'Y' ) m_bArmStop = !m_bArmStop; +#endif + } + + return TRUE; +} + +// Gestion d'un événement. + +BOOL CMotionMother::EventFrame(const Event &event) +{ + D3DVECTOR dir; + float s, a, prog; + int i, st, nd; + BOOL bStop; + + if ( m_engine->RetPause() ) return TRUE; + if ( !m_engine->IsVisiblePoint(m_object->RetPosition(0)) ) return TRUE; + + s = m_physics->RetLinMotionX(MO_MOTSPEED)*1.5f; + a = Abs(m_physics->RetCirMotionY(MO_MOTSPEED)*26.0f); + + if ( s == 0.0f && a != 0.0f ) a *= 1.5f; + + m_armTimeAbs += event.rTime; + m_armTimeMarch += (s)*event.rTime*0.05f; + m_armMember += (s+a)*event.rTime*0.05f; + + bStop = ( a == 0.0f && s == 0.0f ); // a l'arrêt ? + + if ( bStop ) + { + prog = Mod(m_armTimeAbs, 2.0f)/10.0f; + a = Mod(m_armMember, 1.0f); + a = (prog-a)*event.rTime*1.0f; // vient gentiment à position stop + m_armMember += a; + } + + for ( i=0 ; i<6 ; i++ ) // les 6 pattes + { + if ( i < 3 ) prog = Mod(m_armMember+(2.0f-(i%3))*0.33f+0.0f, 1.0f); + else prog = Mod(m_armMember+(2.0f-(i%3))*0.33f+0.3f, 1.0f); + if ( m_bArmStop ) + { + prog = (float)m_armTimeIndex/3.0f; + } + if ( prog < 0.33f ) // t0..t1 ? + { + prog = prog/0.33f; // 0..1 + st = 0; // index start + nd = 1; // index end + } + else if ( prog < 0.67f ) // t1..t2 ? + { + prog = (prog-0.33f)/0.33f; // 0..1 + st = 1; // index start + nd = 2; // index end + } + else // t2..t0 ? + { + prog = (prog-0.67f)/0.33f; // 0..1 + st = 2; // index start + nd = 0; // index end + } + st = st*27+(i%3)*3; + nd = nd*27+(i%3)*3; + if ( i < 3 ) // patte droite (1..3) ? + { + m_object->SetAngleX(2+2*i+0, Prop(m_armAngles[st+ 0], m_armAngles[nd+ 0], prog)); + m_object->SetAngleY(2+2*i+0, Prop(m_armAngles[st+ 1], m_armAngles[nd+ 1], prog)); + m_object->SetAngleZ(2+2*i+0, Prop(m_armAngles[st+ 2], m_armAngles[nd+ 2], prog)); + m_object->SetAngleX(2+2*i+1, Prop(m_armAngles[st+ 9], m_armAngles[nd+ 9], prog)); + m_object->SetAngleY(2+2*i+1, Prop(m_armAngles[st+10], m_armAngles[nd+10], prog)); + m_object->SetAngleZ(2+2*i+1, Prop(m_armAngles[st+11], m_armAngles[nd+11], prog)); + } + else // patte gauche (4..6) ? + { + m_object->SetAngleX(2+2*i+0, Prop( m_armAngles[st+ 0], m_armAngles[nd+ 0], prog)); + m_object->SetAngleY(2+2*i+0, Prop(180-m_armAngles[st+ 1], 180-m_armAngles[nd+ 1], prog)); + m_object->SetAngleZ(2+2*i+0, Prop( -m_armAngles[st+ 2], -m_armAngles[nd+ 2], prog)); + m_object->SetAngleX(2+2*i+1, Prop( m_armAngles[st+ 9], m_armAngles[nd+ 9], prog)); + m_object->SetAngleY(2+2*i+1, Prop( -m_armAngles[st+10], -m_armAngles[nd+10], prog)); + m_object->SetAngleZ(2+2*i+1, Prop( -m_armAngles[st+11], -m_armAngles[nd+11], prog)); + } + } + +#if ADJUST_ANGLE + if ( m_object->RetSelect() ) + { + char s[100]; + sprintf(s, "A:time=%d Q:part=%d W:member=%d", m_armTimeIndex, m_armPartIndex, m_armMemberIndex); + m_engine->SetInfoText(4, s); + } +#endif + + if ( !bStop && !m_object->RetRuin() ) + { + a = Mod(m_armTimeMarch, 1.0f); + if ( a < 0.5f ) a = -1.0f+4.0f*a; // -1..1 + else a = 3.0f-4.0f*a; // 1..-1 + dir.x = sinf(a)*0.03f; + + s = Mod(m_armTimeMarch/2.0f, 1.0f); + if ( s < 0.5f ) s = -1.0f+4.0f*s; // -1..1 + else s = 3.0f-4.0f*s; // 1..-1 + dir.z = sinf(s)*0.05f; + + dir.y = 0.0f; + m_object->SetInclinaison(dir); + + a = Mod(m_armMember-0.1f, 1.0f); + if ( a < 0.33f ) + { + dir.y = -(1.0f-(a/0.33f))*0.3f; + } + else if ( a < 0.67f ) + { + dir.y = 0.0f; + } + else + { + dir.y = -(a-0.67f)/0.33f*0.3f; + } + dir.x = 0.0f; + dir.z = 0.0f; + m_object->SetLinVibration(dir); + } + + m_object->SetAngleZ(1, sinf(m_armTimeAbs*0.5f)*0.20f); // tête + m_object->SetAngleX(1, sinf(m_armTimeAbs*0.6f)*0.10f); // tête + m_object->SetAngleY(1, sinf(m_armTimeAbs*0.7f)*0.20f); // tête + + m_object->SetAngleZ(14, 0.50f); + m_object->SetAngleZ(16, 0.50f); + m_object->SetAngleY(14, 0.80f+sinf(m_armTimeAbs*1.1f)*0.53f); // antenne droite + m_object->SetAngleY(15, 0.70f-sinf(m_armTimeAbs*1.7f)*0.43f); + m_object->SetAngleY(16, -0.80f+sinf(m_armTimeAbs*0.9f)*0.53f); // antenne gauche + m_object->SetAngleY(17, -0.70f-sinf(m_armTimeAbs*1.3f)*0.43f); + + m_object->SetAngleY(18, sinf(m_armTimeAbs*1.1f)*0.20f); // pince droite + m_object->SetAngleZ(18, -0.20f); + m_object->SetAngleY(19, sinf(m_armTimeAbs*0.9f)*0.20f); // pince gauche + m_object->SetAngleZ(19, -0.20f); + + return TRUE; +} + + diff --git a/src/motionmother.h b/src/motionmother.h new file mode 100644 index 00000000..1555a22e --- /dev/null +++ b/src/motionmother.h @@ -0,0 +1,48 @@ +// motionmother.h + +#ifndef _MOTIONMOTHER_H_ +#define _MOTIONMOTHER_H_ + + +class CInstanceManager; +class CEngine; +class CLight; +class CParticule; +class CTerrain; +class CCamera; +class CBrain; +class CPhysics; +class CObject; + + +class CMotionMother : public CMotion +{ +public: + CMotionMother(CInstanceManager* iMan, CObject* object); + ~CMotionMother(); + + void DeleteObject(BOOL bAll=FALSE); + BOOL Create(D3DVECTOR pos, float angle, ObjectType type, float power); + BOOL EventProcess(const Event &event); + +protected: + void CreatePhysics(); + BOOL EventFrame(const Event &event); + +protected: + float m_armMember; + float m_armTimeAbs; + float m_armTimeMarch; + float m_armTimeAction; + short m_armAngles[3*3*3*3*10]; + int m_armTimeIndex; + int m_armPartIndex; + int m_armMemberIndex; + int m_armLastAction; + int m_specAction; + float m_specTime; + BOOL m_bArmStop; +}; + + +#endif //_MOTIONMOTHER_H_ diff --git a/src/motionspider.cpp b/src/motionspider.cpp new file mode 100644 index 00000000..dee93910 --- /dev/null +++ b/src/motionspider.cpp @@ -0,0 +1,774 @@ +// motionspider.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "modfile.h" +#include "sound.h" +#include "motion.h" +#include "motionspider.h" + + + +#define ADJUST_ANGLE FALSE // TRUE -> ajuste les angles des membres +#define START_TIME 1000.0f // début du temps relatif + + + +// Constructeur de l'objet. + +CMotionSpider::CMotionSpider(CInstanceManager* iMan, CObject* object) + : CMotion(iMan, object) +{ + CMotion::CMotion(iMan, object); + + m_armMember = START_TIME; + m_armTimeAbs = START_TIME; + m_armTimeMarch = START_TIME; + m_armTimeAction = START_TIME; + m_armTimeIndex = 0; + m_armPartIndex = 0; + m_armMemberIndex = 0; + m_armLastAction = -1; + m_bArmStop = FALSE; + m_lastParticule = 0.0f; +} + +// Destructeur de l'objet. + +CMotionSpider::~CMotionSpider() +{ +} + + +// Supprime un objet. + +void CMotionSpider::DeleteObject(BOOL bAll) +{ +} + + +// Crée un véhicule roulant quelconque posé sur le sol. + +BOOL CMotionSpider::Create(D3DVECTOR pos, float angle, ObjectType type, + float power) +{ + CModFile* pModFile; + int rank, i, j, parent; + char name[50]; + + float table[] = + { + // x y z + 0.6f, 0.0f, 0.0f, // patte arrière + 0.0f, 0.0f, -2.0f, + 0.0f, 0.0f, -2.0f, + 0.0f, 0.0f, -2.0f, + + 0.8f, 0.0f, -0.2f, // patte arrière-milieu + 0.0f, 0.0f, -2.0f, + 0.0f, 0.0f, -2.0f, + 0.0f, 0.0f, -2.0f, + + 1.0f, 0.0f, -0.2f, // patte avant-milieu + 0.0f, 0.0f, -2.0f, + 0.0f, 0.0f, -2.0f, + 0.0f, 0.0f, -2.0f, + + 1.2f, 0.0f, 0.0f, // patte avant + 0.0f, 0.0f, -2.0f, + 0.0f, 0.0f, -2.0f, + 0.0f, 0.0f, -2.0f, + }; + + if ( m_engine->RetRestCreate() < 3+32+2 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + m_object->SetType(type); + + // Crée la base principale. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEVEHICULE); // c'est un objet mobile + m_object->SetObjectRank(0, rank); + pModFile->ReadModel("objects\\spider0.mod"); // n'existe pas + pModFile->CreateEngineObject(rank); + m_object->SetPosition(0, pos); + m_object->SetAngleY(0, angle); + + // Un véhicule doit avoir obligatoirement une sphère de + // collision avec un centre (0;y;0) (voir GetCrashSphere). + m_object->CreateCrashSphere(D3DVECTOR(0.0f, -2.0f, 0.0f), 4.0f, SOUND_BOUM, 0.20f); + m_object->SetGlobalSphere(D3DVECTOR(-0.5f, 1.0f, 0.0f), 4.0f); + + // Crée l'abdomen. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\spider1.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(1.0f, 0.0f, 0.0f)); + + // Crée la tête. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 0); + pModFile->ReadModel("objects\\spider2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(1.0f, 0.0f, 0.0f)); + + // Crée les pattes. + for ( i=0 ; i<4 ; i++ ) + { + for ( j=0 ; j<4 ; j++ ) + { + sprintf(name, "objects\\spider%d.mod", j+3); // 3..6 + + // Crée la patte droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(3+i*4+j, rank); + if ( j == 0 ) parent = 0; + else parent = 3+i*4+j-1; + m_object->SetObjectParent(3+i*4+j, parent); + pModFile->ReadModel(name); + pModFile->CreateEngineObject(rank); + pos.x = table[i*12+j*3+0]; + pos.y = table[i*12+j*3+1]; + pos.z = table[i*12+j*3+2]; + m_object->SetPosition(3+i*4+j, pos); + + // Crée la patte gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(19+i*4+j, rank); + if ( j == 0 ) parent = 0; + else parent = 19+i*4+j-1; + m_object->SetObjectParent(19+i*4+j, parent); + pModFile->ReadModel(name); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + pos.x = table[i*12+j*3+0]; + pos.y = table[i*12+j*3+1]; + pos.z = -table[i*12+j*3+2]; + m_object->SetPosition(19+i*4+j, pos); + } + } + + // Crée la mandibule droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(35, rank); + m_object->SetObjectParent(35, 1); + pModFile->ReadModel("objects\\spider7.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(35, D3DVECTOR(0.0f, 0.0f, -0.3f)); + + // Crée la mandibule gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(36, rank); + m_object->SetObjectParent(36, 1); + pModFile->ReadModel("objects\\spider7.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(36, D3DVECTOR(0.0f, 0.0f, 0.3f)); + + m_object->CreateShadowCircle(4.0f, 0.5f); + + CreatePhysics(); + m_object->SetFloorHeight(0.0f); + + pos = m_object->RetPosition(0); + m_object->SetPosition(0, pos); // pour afficher les ombres tout de suite + + m_engine->LoadAllTexture(); + + delete pModFile; + return TRUE; +} + +// Crée la physique de l'objet. + +void CMotionSpider::CreatePhysics() +{ + Character* character; + int i; + + int member_march[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4, // en l'air : + 60,25,0, 60,0,0, 60,-25,0, 60,-50,0, // t0: cuisses 1..4 + -35,40,0, -35,0,0, -35,0,0, -35,-40,0, // t0: jambes 1..4 + -65,0,-30, -65,0,0, -65,0,0, -65,0,30, // t0: pieds 1..4 + 25,0,0, 25,0,0, 25,0,0, 25,0,0, // t0: doigt 1..4 + // au sol devant : + 30,15,0, 30,-10,0, 30,-35,0, 30,-60,0, // t1: cuisses 1..4 + -10,40,0, -45,0,0, -45,0,0, -45,-40,0, // t1: jambes 1..4 + -90,0,0, -20,0,0, -20,0,0, -20,0,0, // t1: pieds 1..4 + -5,0,0, -5,0,0, -5,0,0, -5,0,0, // t1: doigt 1..4 + // au sol derrière : + 35,35,0, 40,10,0, 40,-15,0, 40,-40,0, // t2: cuisses 1..4 + -35,40,0, -35,0,0, -35,0,0, -25,-40,0, // t2: jambes 1..4 + -50,-25,-30,-65,0,0, -65,0,0, -90,0,30, // t2: pieds 1..4 + -5,0,0, -5,0,0, -5,0,0, -5,0,0, // t2: doigt 1..4 + }; + + int member_stop[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4, // en l'air : + 35,35,0, 30,0,0, 30,-25,0, 30,-50,0, // t0: cuisses 1..4 + -35,40,0, -45,0,0, -45,0,0, -45,-40,0, // t0: jambes 1..4 + -50,-25,-30,-20,0,0, -20,0,0, -20,0,30, // t0: pieds 1..4 + -5,0,0, -5,0,0, -5,0,0, -5,0,0, // t0: doigt 1..4 + // au sol devant : + 35,35,0, 30,0,0, 30,-25,0, 30,-50,0, // t1: cuisses 1..4 + -30,40,0, -40,0,0, -40,0,0, -40,-40,0, // t1: jambes 1..4 + -55,-25,-30,-25,0,0, -25,0,0, -25,0,0, // t1: pieds 1..4 + -5,0,0, -5,0,0, -5,0,0, -5,0,0, // t1: doigt 1..4 + // au sol derrière : + 35,35,0, 30,0,0, 30,-25,0, 30,-50,0, // t2: cuisses 1..4 + -30,40,0, -40,0,0, -40,0,0, -40,-40,0, // t2: jambes 1..4 + -50,-25,-30,-20,0,0, -20,0,0, -20,0,30, // t2: pieds 1..4 + -10,0,0, -10,0,0, -10,0,0, -10,0,0, // t2: doigt 1..4 + }; + + int member_spec[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4, // brûle : + 30,25,0, 30,0,0, 30,-25,0, 30,-50,0, // s0: cuisses 1..4 + -45,0,0, -45,0,0, -45,0,0, -45,0,0, // s0: jambes 1..4 + -20,0,0, -20,0,0, -20,0,0, -20,0,0, // s0: pieds 1..4 + -5,0,0, -5,0,0, -5,0,0, -5,0,0, // s0: doigt 1..4 + // ruine : + 30,25,0, 30,0,0, 30,-25,0, 30,-50,0, // s1: cuisses 1..4 + -45,0,0, -45,0,0, -45,0,0, -45,0,0, // s1: jambes 1..4 + -20,0,0, -20,0,0, -20,0,0, -20,0,0, // s1: pieds 1..4 + -5,0,0, -5,0,0, -5,0,0, -5,0,0, // s1: doigt 1..4 + // explose : + 40,25,0, 40,0,0, 40,-25,0, 40,-50,0, // s2: cuisses 1..4 + -55,0,0, -55,0,0, -55,0,0, -55,0,0, // s2: jambes 1..4 + -30,0,0, -30,0,0, -30,0,0, -30,0,0, // s2: pieds 1..4 + -5,0,0, -5,0,0, -5,0,0, -5,0,0, // s2: doigt 1..4 + // back1 : + 35,35,0, 30,0,0, 30,-25,0, 30,-50,0, // s3: cuisses 1..4 + -30,40,0, -40,0,0, -40,0,0, -40,-40,0, // s3: jambes 1..4 + -55,-25,-30,-25,0,0, -25,0,0, -25,0,0, // s3: pieds 1..4 + -5,0,0, -5,0,0, -5,0,0, -5,0,0, // s3: doigt 1..4 + // back2 : + 15,35,0, 15,0,0, 15,-25,0, 15,-50,0, // s4: cuisses 1..4 + -60,40,0, -60,0,0, -60,0,0, -60,-40,0, // s4: jambes 1..4 + -65,-25,-30,-65,0,0, -65,0,0, -65,0,0, // s4: pieds 1..4 + -15,0,0, -15,0,0, -15,0,0, -15,0,0, // s4: doigt 1..4 + // back3 : + 35,35,0, 30,0,0, 30,-25,0, 30,-50,0, // s5: cuisses 1..4 + -30,40,0, -40,0,0, -40,0,0, -40,-40,0, // s5: jambes 1..4 + -55,-25,-30,-25,0,0, -25,0,0, -25,0,0, // s5: pieds 1..4 + -5,0,0, -5,0,0, -5,0,0, -5,0,0, // s5: doigt 1..4 + }; + + m_physics->SetType(TYPE_ROLLING); + + character = m_object->RetCharacter(); + character->wheelFront = 4.0f; + character->wheelBack = 4.0f; + character->wheelLeft = 6.0f; + character->wheelRight = 6.0f; + character->height = 0.6f; + + m_physics->SetLinMotionX(MO_ADVSPEED, 12.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 12.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 15.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 15.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 5.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 5.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 10.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 1.0f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 1.0f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 20.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 20.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 40.0f); + + for ( i=0 ; i<3*4*4*3 ; i++ ) + { + m_armAngles[3*4*4*3*MS_MARCH+i] = member_march[i]; + } + for ( i=0 ; i<3*4*4*3 ; i++ ) + { + m_armAngles[3*4*4*3*MS_STOP+i] = member_stop[i]; + } + for ( i=0 ; i<3*4*4*6 ; i++ ) + { + m_armAngles[3*4*4*3*MS_SPEC+i] = member_spec[i]; + } +} + + +// Gestion d'un événement. + +BOOL CMotionSpider::EventProcess(const Event &event) +{ + CMotion::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + + if ( event.event == EVENT_KEYDOWN ) + { +#if ADJUST_ANGLE + int i; + + if ( event.param == 'A' ) m_armTimeIndex++; + if ( m_armTimeIndex >= 3 ) m_armTimeIndex = 0; + + if ( event.param == 'Q' ) m_armPartIndex++; + if ( m_armPartIndex >= 4 ) m_armPartIndex = 0; + + if ( event.param == 'W' ) m_armMemberIndex++; + if ( m_armMemberIndex >= 4 ) m_armMemberIndex = 0; + + i = m_armMemberIndex*3; + i += m_armPartIndex*3*4; + i += m_armTimeIndex*3*4*4; + + if ( event.param == 'E' ) m_armAngles[i+0] += 5; + if ( event.param == 'D' ) m_armAngles[i+0] -= 5; + if ( event.param == 'R' ) m_armAngles[i+1] += 5; + if ( event.param == 'F' ) m_armAngles[i+1] -= 5; + if ( event.param == 'T' ) m_armAngles[i+2] += 5; + if ( event.param == 'G' ) m_armAngles[i+2] -= 5; + if ( event.param == 'Z' ) m_armAngles[i+3] += 5; + if ( event.param == 'H' ) m_armAngles[i+3] -= 5; + + if ( event.param == 'Y' ) m_bArmStop = !m_bArmStop; +#endif + } + + return TRUE; +} + +// Calcule une valeur (radians) proportionnelle comprise +// entre a et b (degrés). + +inline float Propf(float a, float b, float p) +{ + float aa, bb; + + aa = a*PI/180.0f; + bb = b*PI/180.0f; + + return aa+p*(bb-aa); +} + +// Gestion d'un événement. + +BOOL CMotionSpider::EventFrame(const Event &event) +{ + D3DVECTOR dir, pos, speed; + FPOINT dim; + float s, a, prog, time; + float tSt[12], tNd[12]; + int i, ii, st, nd, action; + BOOL bStop; + + if ( m_engine->RetPause() ) return TRUE; + if ( !m_engine->IsVisiblePoint(m_object->RetPosition(0)) ) return TRUE; + + s = m_physics->RetLinMotionX(MO_MOTSPEED)*1.5f; + a = Abs(m_physics->RetCirMotionY(MO_MOTSPEED)*2.0f); + + if ( s == 0.0f && a != 0.0f ) a *= 1.5f; + + m_armTimeAbs += event.rTime; + m_armTimeAction += event.rTime; + m_armTimeMarch += (s)*event.rTime*0.15f; + m_armMember += (s+a)*event.rTime*0.15f; + + bStop = ( a == 0.0f && s == 0.0f ); // a l'arrêt ? + + action = MS_MARCH; // marche + if ( s == 0.0f && a == 0.0f ) + { + action = MS_STOP; // stop + } + + if ( bStop ) + { + prog = Mod(m_armTimeAbs, 2.0f)/10.0f; + a = Mod(m_armMember, 1.0f); + a = (prog-a)*event.rTime*2.0f; // vient gentiment à position stop + m_armMember += a; + } + + if ( m_object->RetRuin() ) // ruine ? + { + m_actionType = MSS_RUIN; + } + if ( m_object->RetBurn() ) // brûle ? + { + if ( m_object->RetFixed() ) + { + m_actionType = MSS_BURN; + } + else + { + m_actionType = -1; + } + } + + for ( i=0 ; i<8 ; i++ ) // les 8 pattes + { + if ( m_actionType != -1 ) // action spéciale en cours ? + { + st = 3*4*4*3*MS_SPEC + 3*4*4*m_actionType + (i%4)*3; + nd = st; + time = event.rTime*m_actionTime; + m_armTimeAction = 0.0f; + } + else + { +//? if ( i < 4 ) prog = Mod(m_armMember+(2.0f-(i%4))*0.25f+0.0f, 1.0f); +//? else prog = Mod(m_armMember+(2.0f-(i%4))*0.25f+0.3f, 1.0f); + if ( i < 4 ) prog = Mod(m_armMember+(2.0f-(i%4))*0.25f+0.0f, 1.0f); + else prog = Mod(m_armMember+(2.0f-(i%4))*0.25f+0.5f, 1.0f); + if ( m_bArmStop ) + { + prog = (float)m_armTimeIndex/3.0f; + action = MS_MARCH; + } + if ( prog < 0.33f ) // t0..t1 ? + { + prog = prog/0.33f; // 0..1 + st = 0; // index start + nd = 1; // index end + } + else if ( prog < 0.67f ) // t1..t2 ? + { + prog = (prog-0.33f)/0.33f; // 0..1 + st = 1; // index start + nd = 2; // index end + } + else // t2..t0 ? + { + prog = (prog-0.67f)/0.33f; // 0..1 + st = 2; // index start + nd = 0; // index end + } + st = 3*4*4*3*action + st*3*4*4 + (i%4)*3; + nd = 3*4*4*3*action + nd*3*4*4 + (i%4)*3; + + // De moins en moins mou ... +//? time = event.rTime*(2.0f+Min(m_armTimeAction*20.0f, 40.0f)); + time = event.rTime*10.0f; + } + + tSt[ 0] = m_armAngles[st+ 0]; // x + tSt[ 1] = m_armAngles[st+ 1]; // y + tSt[ 2] = m_armAngles[st+ 2]; // z + tSt[ 3] = m_armAngles[st+12]; // x + tSt[ 4] = m_armAngles[st+13]; // y + tSt[ 5] = m_armAngles[st+14]; // z + tSt[ 6] = m_armAngles[st+24]; // x + tSt[ 7] = m_armAngles[st+25]; // y + tSt[ 8] = m_armAngles[st+26]; // z + tSt[ 9] = m_armAngles[st+36]; // x + tSt[10] = m_armAngles[st+37]; // y + tSt[11] = m_armAngles[st+38]; // z + + tNd[ 0] = m_armAngles[nd+ 0]; // x + tNd[ 1] = m_armAngles[nd+ 1]; // y + tNd[ 2] = m_armAngles[nd+ 2]; // z + tNd[ 3] = m_armAngles[nd+12]; // x + tNd[ 4] = m_armAngles[nd+13]; // y + tNd[ 5] = m_armAngles[nd+14]; // z + tNd[ 6] = m_armAngles[nd+24]; // x + tNd[ 7] = m_armAngles[nd+25]; // y + tNd[ 8] = m_armAngles[nd+26]; // z + tNd[ 9] = m_armAngles[nd+36]; // z + tNd[10] = m_armAngles[nd+37]; // z + tNd[11] = m_armAngles[nd+38]; // z + + if ( m_actionType == MSS_BACK2 ) // sur le dos ? + { + for ( ii=0 ; ii<12 ; ii++ ) + { + tSt[ii] += Rand()*20.0f; + tNd[ii] = tSt[ii]; + } +//? time = 100.0f; + time = event.rTime*10.0f; + } + + if ( i < 4 ) // patte droite (1..4) ? + { + m_object->SetAngleX(3+4*i+0, Smooth(m_object->RetAngleX(3+4*i+0), Propf(tSt[ 0], tNd[ 0], prog), time)); + m_object->SetAngleY(3+4*i+0, Smooth(m_object->RetAngleY(3+4*i+0), Propf(tSt[ 1], tNd[ 1], prog), time)); + m_object->SetAngleZ(3+4*i+0, Smooth(m_object->RetAngleZ(3+4*i+0), Propf(tSt[ 2], tNd[ 2], prog), time)); + m_object->SetAngleX(3+4*i+1, Smooth(m_object->RetAngleX(3+4*i+1), Propf(tSt[ 3], tNd[ 3], prog), time)); + m_object->SetAngleY(3+4*i+1, Smooth(m_object->RetAngleY(3+4*i+1), Propf(tSt[ 4], tNd[ 4], prog), time)); + m_object->SetAngleZ(3+4*i+1, Smooth(m_object->RetAngleZ(3+4*i+1), Propf(tSt[ 5], tNd[ 5], prog), time)); + m_object->SetAngleX(3+4*i+2, Smooth(m_object->RetAngleX(3+4*i+2), Propf(tSt[ 6], tNd[ 6], prog), time)); + m_object->SetAngleY(3+4*i+2, Smooth(m_object->RetAngleY(3+4*i+2), Propf(tSt[ 7], tNd[ 7], prog), time)); + m_object->SetAngleZ(3+4*i+2, Smooth(m_object->RetAngleZ(3+4*i+2), Propf(tSt[ 8], tNd[ 8], prog), time)); + m_object->SetAngleX(3+4*i+3, Smooth(m_object->RetAngleX(3+4*i+3), Propf(tSt[ 9], tNd[ 9], prog), time)); + m_object->SetAngleY(3+4*i+3, Smooth(m_object->RetAngleY(3+4*i+3), Propf(tSt[10], tNd[10], prog), time)); + m_object->SetAngleZ(3+4*i+3, Smooth(m_object->RetAngleZ(3+4*i+3), Propf(tSt[11], tNd[11], prog), time)); + } + else // patte gauche (5..8) ? + { + m_object->SetAngleX(3+4*i+0, Smooth(m_object->RetAngleX(3+4*i+0), Propf(-tSt[ 0], -tNd[ 0], prog), time)); + m_object->SetAngleY(3+4*i+0, Smooth(m_object->RetAngleY(3+4*i+0), Propf(-tSt[ 1], -tNd[ 1], prog), time)); + m_object->SetAngleZ(3+4*i+0, Smooth(m_object->RetAngleZ(3+4*i+0), Propf( tSt[ 2], tNd[ 2], prog), time)); + m_object->SetAngleX(3+4*i+1, Smooth(m_object->RetAngleX(3+4*i+1), Propf(-tSt[ 3], -tNd[ 3], prog), time)); + m_object->SetAngleY(3+4*i+1, Smooth(m_object->RetAngleY(3+4*i+1), Propf(-tSt[ 4], -tNd[ 4], prog), time)); + m_object->SetAngleZ(3+4*i+1, Smooth(m_object->RetAngleZ(3+4*i+1), Propf( tSt[ 5], tNd[ 5], prog), time)); + m_object->SetAngleX(3+4*i+2, Smooth(m_object->RetAngleX(3+4*i+2), Propf(-tSt[ 6], -tNd[ 6], prog), time)); + m_object->SetAngleY(3+4*i+2, Smooth(m_object->RetAngleY(3+4*i+2), Propf(-tSt[ 7], -tNd[ 7], prog), time)); + m_object->SetAngleZ(3+4*i+2, Smooth(m_object->RetAngleZ(3+4*i+2), Propf( tSt[ 8], tNd[ 8], prog), time)); + m_object->SetAngleX(3+4*i+3, Smooth(m_object->RetAngleX(3+4*i+3), Propf(-tSt[ 9], -tNd[ 9], prog), time)); + m_object->SetAngleY(3+4*i+3, Smooth(m_object->RetAngleY(3+4*i+3), Propf(-tSt[10], -tNd[10], prog), time)); + m_object->SetAngleZ(3+4*i+3, Smooth(m_object->RetAngleZ(3+4*i+3), Propf( tSt[11], tNd[11], prog), time)); + } + } + +#if ADJUST_ANGLE + if ( m_object->RetSelect() ) + { + char s[100]; + sprintf(s, "A:time=%d Q:part=%d W:member=%d", m_armTimeIndex, m_armPartIndex, m_armMemberIndex); + m_engine->SetInfoText(4, s); + } +#endif + + if ( m_actionType == MSS_BURN ) // brûle ? + { + dir = D3DVECTOR(PI, 0.0f, 0.0f); + SetCirVibration(dir); + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetLinVibration(dir); + SetInclinaison(dir); + + time = event.rTime*1.0f; + m_object->SetAngleZ(1, Smooth(m_object->RetAngleZ(1), 0.0f, time)); // tête + } + else if ( m_actionType == MSS_RUIN ) // ruine ? + { + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetLinVibration(dir); + SetCirVibration(dir); + SetInclinaison(dir); + } + else if ( m_actionType == MSS_EXPLO ) // explose ? + { + m_object->SetZoomY(1, 1.0f+m_progress); + m_object->SetZoomZ(1, 1.0f+m_progress); + m_object->SetZoomX(1, 1.0f+m_progress/2.0f); + + dir.x = (Rand()-0.5f)*0.1f*m_progress; + dir.y = (Rand()-0.5f)*0.1f*m_progress; + dir.z = (Rand()-0.5f)*0.1f*m_progress; + m_object->SetCirVibration(dir); + } + else if ( m_actionType == MSS_BACK1 ) // se met sur le dos ? + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_armTimeAbs ) + { + m_lastParticule = m_armTimeAbs; + + pos = m_object->RetPosition(0); + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + speed.y = Rand()*5.0f; + dim.x = Rand()*3.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f); + } + + if ( m_progress < 0.5f ) + { + dir.x = 0.0f; + dir.y = powf(m_progress/0.5f, 2.0f)*12.0f; + dir.z = 0.0f; + SetLinVibration(dir); + } + else + { + dir.x = 0.0f; + dir.y = powf(2.0f-m_progress/0.5f, 2.0f)*12.0f; + dir.z = 0.0f; + SetLinVibration(dir); + } + dir.x = m_progress*PI; + dir.y = 0.0f; + dir.z = 0.0f; + SetCirVibration(dir); + + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetInclinaison(dir); + + if ( m_progress >= 1.0f ) + { + SetAction(MSS_BACK2, 55.0f+Rand()*10.0f); + } + } + else if ( m_actionType == MSS_BACK2 ) // bouge sur le dos ? + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_armTimeAbs ) + { + m_lastParticule = m_armTimeAbs; + + if ( rand()%10 == 0 ) + { + pos = m_object->RetPosition(0); + pos.x += (Rand()-0.5f)*8.0f; + pos.z += (Rand()-0.5f)*8.0f; + pos.y -= 1.0f; + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = Rand()*2.0f; + dim.x = Rand()*1.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f); + } + } + + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetLinVibration(dir); + dir.x = sinf(m_armTimeAbs* 3.0f)*0.20f+ + sinf(m_armTimeAbs* 6.0f)*0.20f+ + sinf(m_armTimeAbs*10.0f)*0.20f+ + sinf(m_armTimeAbs*17.0f)*0.30f+PI; + dir.y = sinf(m_armTimeAbs* 4.0f)*0.02f+ + sinf(m_armTimeAbs* 5.0f)*0.02f+ + sinf(m_armTimeAbs*11.0f)*0.02f+ + sinf(m_armTimeAbs*18.0f)*0.03f; + dir.z = sinf(m_armTimeAbs* 2.0f)*0.02f+ + sinf(m_armTimeAbs* 7.0f)*0.02f+ + sinf(m_armTimeAbs*13.0f)*0.02f+ + sinf(m_armTimeAbs*15.0f)*0.03f; + SetCirVibration(dir); + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetInclinaison(dir); + + m_object->SetAngleY(1, sinf(m_armTimeAbs*5.0f)*0.05f); // queue + m_object->SetAngleY(2, cosf(m_armTimeAbs*5.0f)*0.20f); // tête + m_object->SetAngleZ(1, 0.4f); // queue + m_object->SetAngleZ(2, 0.0f); // tête + + if ( m_progress >= 1.0f ) + { + SetAction(MSS_BACK3, 0.4f); + } + } + else if ( m_actionType == MSS_BACK3 ) // se remet sur les pattes ? + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_armTimeAbs ) + { + m_lastParticule = m_armTimeAbs; + + pos = m_object->RetPosition(0); + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + speed.y = Rand()*5.0f; + dim.x = Rand()*3.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f); + } + + if ( m_progress < 0.5f ) + { + dir.x = 0.0f; + dir.y = powf(m_progress/0.5f, 2.0f)*5.0f; + dir.z = 0.0f; + SetLinVibration(dir); + } + else + { + dir.x = 0.0f; + dir.y = powf(2.0f-m_progress/0.5f, 2.0f)*5.0f; + dir.z = 0.0f; + SetLinVibration(dir); + } + dir.x = (1.0f-m_progress)*PI; + dir.y = 0.0f; + dir.z = 0.0f; + SetCirVibration(dir); + + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetInclinaison(dir); + + if ( m_progress >= 1.0f ) + { + SetAction(-1); + m_object->SetFixed(FALSE); // bouge de nouveau + } + } + else + { + if ( bStop ) + { + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetInclinaison(dir); + } + else + { + a = Mod(m_armMember, 1.0f); + if ( a < 0.5f ) a = -1.0f+4.0f*a; // -1..1 + else a = 3.0f-4.0f*a; // 1..-1 + dir.x = sinf(a)*0.05f; + + s = Mod(m_armMember/2.0f, 1.0f); + if ( s < 0.5f ) s = -1.0f+4.0f*s; // -1..1 + else s = 3.0f-4.0f*s; // 1..-1 + dir.z = sinf(s)*0.1f; + + dir.y = 0.0f; + SetInclinaison(dir); + } + + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + SetLinVibration(dir); + SetCirVibration(dir); + + m_object->SetAngleZ(1, sinf(m_armTimeAbs*1.7f)*0.02f); // queue + m_object->SetAngleX(1, sinf(m_armTimeAbs*1.3f)*0.05f); + m_object->SetAngleY(1, sinf(m_armTimeAbs*2.4f)*0.10f); + m_object->SetZoom(1, 1.0f+sinf(m_armTimeAbs*3.3f)*0.05f); + + m_object->SetAngleZ(2, sinf(m_armTimeAbs*1.4f)*0.20f); // tête + m_object->SetAngleX(2, sinf(m_armTimeAbs*1.9f)*0.10f); + m_object->SetAngleY(2, sinf(m_armTimeAbs*2.1f)*0.10f); + + m_object->SetAngleY(35, sinf(m_armTimeAbs*3.1f)*0.20f); // mandibule + m_object->SetAngleY(36, -sinf(m_armTimeAbs*3.1f)*0.20f); // mandibule + } + + return TRUE; +} + + diff --git a/src/motionspider.h b/src/motionspider.h new file mode 100644 index 00000000..b010a2cf --- /dev/null +++ b/src/motionspider.h @@ -0,0 +1,59 @@ +// motionspider.h + +#ifndef _MOTIONSPIDER_H_ +#define _MOTIONSPIDER_H_ + + +class CInstanceManager; +class CEngine; +class CLight; +class CParticule; +class CTerrain; +class CCamera; +class CBrain; +class CPhysics; +class CObject; + + +#define MS_MARCH 0 +#define MS_STOP 1 +#define MS_SPEC 2 + +#define MSS_BURN 0 +#define MSS_RUIN 1 +#define MSS_EXPLO 2 +#define MSS_BACK1 3 +#define MSS_BACK2 4 +#define MSS_BACK3 5 + + +class CMotionSpider : public CMotion +{ +public: + CMotionSpider(CInstanceManager* iMan, CObject* object); + ~CMotionSpider(); + + void DeleteObject(BOOL bAll=FALSE); + BOOL Create(D3DVECTOR pos, float angle, ObjectType type, float power); + BOOL EventProcess(const Event &event); + +protected: + void CreatePhysics(); + BOOL EventFrame(const Event &event); + +protected: + float m_armMember; + float m_armTimeAbs; + float m_armTimeMarch; + float m_armTimeAction; + short m_armAngles[3*4*4*3*3 + 3*4*4*6]; + int m_armTimeIndex; + int m_armPartIndex; + int m_armMemberIndex; + int m_armLastAction; + BOOL m_bArmStop; + float m_lastParticule; +}; + + +#endif //_MOTIONSPIDER_H_ diff --git a/src/motiontoto.cpp b/src/motiontoto.cpp new file mode 100644 index 00000000..1e4a8ae5 --- /dev/null +++ b/src/motiontoto.cpp @@ -0,0 +1,870 @@ +// motiontoto.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "water.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "modfile.h" +#include "robotmain.h" +#include "sound.h" +#include "motion.h" +#include "motiontoto.h" + + + +#define START_TIME 1000.0f // début du temps relatif + + + +// Constructeur de l'objet. + +CMotionToto::CMotionToto(CInstanceManager* iMan, CObject* object) + : CMotion(iMan, object) +{ + CMotion::CMotion(iMan, object); + + m_time = 0.0f; + m_bDisplayInfo = FALSE; + m_bQuickPos = FALSE; + m_bStartAction = FALSE; + m_speedAction = 20.0f; + m_soundChannel = -1; + m_clownRadius = 0.0f; + m_clownDelay = 0.0f; + m_clownTime = 0.0f; + m_blinkTime = 0.0f; + m_blinkProgress = -1.0f; + m_lastMotorParticule = 0.0f; + m_type = OBJECT_NULL; + m_mousePos = FPOINT(0.0f, 0.0f); +} + +// Destructeur de l'objet. + +CMotionToto::~CMotionToto() +{ +} + + +// Supprime un objet. + +void CMotionToto::DeleteObject(BOOL bAll) +{ + if ( m_soundChannel != -1 ) + { + m_sound->Stop(m_soundChannel); + m_soundChannel = -1; + } +} + + +// Crée un véhicule roulant quelconque posé sur le sol. + +BOOL CMotionToto::Create(D3DVECTOR pos, float angle, ObjectType type, + float power) +{ + CModFile* pModFile; + int rank; + + if ( m_engine->RetRestCreate() < 10 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + m_object->SetType(type); + + // Crée la tête. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEVEHICULE); // c'est un objet mobile + m_object->SetObjectRank(0, rank); + pModFile->ReadModel("objects\\toto1.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(0, pos); + m_object->SetAngleY(0, angle); + + // Crée la bouche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\toto2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(1.00f, 0.17f, 0.00f)); + + // Crée l'oeil gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 0); + pModFile->ReadModel("objects\\toto3.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(0.85f, 1.04f, 0.25f)); + m_object->SetAngleY(2, -20.0f*PI/180.0f); + + // Crée l'oeil droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(3, rank); + m_object->SetObjectParent(3, 0); + pModFile->ReadModel("objects\\toto3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(3, D3DVECTOR(0.85f, 1.04f, -0.25f)); + m_object->SetAngleY(3, 20.0f*PI/180.0f); + + // Crée l'antenne gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(4, rank); + m_object->SetObjectParent(4, 0); + pModFile->ReadModel("objects\\toto4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(4, D3DVECTOR(0.0f, 1.9f, 0.3f)); + m_object->SetAngleX(4, 30.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(5, rank); + m_object->SetObjectParent(5, 4); + pModFile->ReadModel("objects\\toto4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(5, D3DVECTOR(0.0f, 0.67f, 0.0f)); + m_object->SetAngleX(5, 30.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 5); + pModFile->ReadModel("objects\\toto5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(0.0f, 0.70f, 0.0f)); + m_object->SetAngleX(6, 30.0f*PI/180.0f); + + // Crée l'antenne droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 0); + pModFile->ReadModel("objects\\toto4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(0.0f, 1.9f, -0.3f)); + m_object->SetAngleX(7, -30.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(8, rank); + m_object->SetObjectParent(8, 7); + pModFile->ReadModel("objects\\toto4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(8, D3DVECTOR(0.0f, 0.67f, 0.0f)); + m_object->SetAngleX(8, -30.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(9, rank); + m_object->SetObjectParent(9, 8); + pModFile->ReadModel("objects\\toto5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(9, D3DVECTOR(0.0f, 0.70f, 0.0f)); + m_object->SetAngleX(9, -30.0f*PI/180.0f); + + m_object->SetZoom(0, 0.5f); // c'est p'tit + m_object->SetFloorHeight(0.0f); + + pos = m_object->RetPosition(0); + m_object->SetPosition(0, pos); // pour afficher les ombres tout de suite + + m_engine->LoadAllTexture(); + + delete pModFile; + return TRUE; +} + + +// Début de l'affichage des informations, avec toto dans la marge gauche. + +void CMotionToto::StartDisplayInfo() +{ +return; +//? + m_bDisplayInfo = TRUE; + + m_actionType = -1; + m_actionTime = 0.0f; + m_progress = 0.0f; + + m_object->SetAngleY(0, 0.0f); + m_mousePos = FPOINT(0.5f, 0.5f); +} + +// Fin de l'affichage des informartions. + +void CMotionToto::StopDisplayInfo() +{ + m_bDisplayInfo = FALSE; + m_bQuickPos = TRUE; +} + +// Donne la position de la souris. + +void CMotionToto::SetMousePos(FPOINT pos) +{ + m_mousePos = pos; +} + + +// Gestion d'un événement. + +BOOL CMotionToto::EventProcess(const Event &event) +{ + CMotion::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + + return TRUE; +} + +// Gestion d'un événement. + +BOOL CMotionToto::EventFrame(const Event &event) +{ + D3DMATRIX* mat; + D3DVECTOR eye, lookat, dir, perp, nPos, aPos, pos, speed; + D3DVECTOR vibLin, vibCir, dirSpeed, aAntenna; + FPOINT dim; + POINT wDim; + ParticuleType type; + float progress, focus, distance, shift, verti, level, zoom; + float aAngle, nAngle, mAngle, angle, linSpeed, cirSpeed; + int sheet, i, r; + BOOL bHidden; + + if ( m_engine->RetPause() && + !m_main->RetInfoLock() ) return TRUE; + + if ( m_bDisplayInfo ) // "regarde" la souris ? + { + bHidden = FALSE; + } + else + { + bHidden = FALSE; + + if ( m_main->RetMovieLock() ) // film en cours ? + { + bHidden = TRUE; + } + if ( !m_engine->RetTotoMode() ) + { + if ( !m_main->RetEditLock() ) // édition en cours ? + { + bHidden = TRUE; + } + } + } + + if ( bHidden ) + { + nPos = m_object->RetPosition(0); + m_terrain->MoveOnFloor(nPos, TRUE); + nPos.y -= 100.0f; // cache sous le sol ! + m_object->SetPosition(0, nPos); + return TRUE; + } + + m_time += event.rTime; + m_blinkTime -= event.rTime; + + progress = 0.0f; + if ( m_actionType != -1 ) // action en cours ? + { + if ( m_progress < 0.15f ) + { + progress = m_progress/0.15f; + } + else if ( m_progress < 0.85f ) + { + progress = 1.0f; + } + else + { + progress = (1.0f-m_progress)/0.15f; + } + } + + if ( m_progress >= 1.0f ) + { + m_actionType = -1; // action terminée + m_actionTime = 0.0f; + m_progress = 0.0f; + + m_clownTime = 0.0f; + m_clownDelay = 0.0f; + } + + focus = m_engine->RetFocus(); + eye = m_engine->RetEyePt(); + lookat = m_engine->RetLookatPt(); + + vibLin = D3DVECTOR(0.0f, 0.0f, 0.0f); + vibCir = D3DVECTOR(0.0f, 0.0f, 0.0f); + aAntenna = D3DVECTOR(0.0f, 0.0f, 0.0f); + aAntenna.x += 30.0f*PI/180.0f; + + // Calcule la nouvelle position. + if ( m_bDisplayInfo ) + { + wDim = m_engine->RetDim(); + nPos.x = -4.0f*((float)wDim.x/(float)wDim.y)/(640.0f/480.0f); + nPos.y = -0.5f; + nPos.z = 7.0f; // dans la marge gauche + + linSpeed = 0.0f; + } + else + { +#if 0 + distance = 30.0f-progress*24.5f; // éloignement + shift = 18.0f-progress*15.4f; // décalage à gauche + verti = 10.0f-progress* 9.6f; // décalage en haut +#else + distance = 30.0f-progress*18.0f; // éloignement + shift = 18.0f-progress*11.0f; // décalage à gauche + verti = 10.0f-progress* 8.0f; // décalage en haut +#endif + + if ( m_actionType == -1 && + (m_type == OBJECT_HUMAN || + m_type == OBJECT_TECH || + m_type == OBJECT_MOBILEwa || + m_type == OBJECT_MOBILEta || + m_type == OBJECT_MOBILEfa || + m_type == OBJECT_MOBILEia || + m_type == OBJECT_MOBILEwc || + m_type == OBJECT_MOBILEtc || + m_type == OBJECT_MOBILEfc || + m_type == OBJECT_MOBILEic || + m_type == OBJECT_MOBILEwi || + m_type == OBJECT_MOBILEti || + m_type == OBJECT_MOBILEfi || + m_type == OBJECT_MOBILEii || + m_type == OBJECT_MOBILEws || + m_type == OBJECT_MOBILEts || + m_type == OBJECT_MOBILEfs || + m_type == OBJECT_MOBILEis || + m_type == OBJECT_MOBILErt || + m_type == OBJECT_MOBILErc || + m_type == OBJECT_MOBILErr || + m_type == OBJECT_MOBILErs || + m_type == OBJECT_MOBILEsa || + m_type == OBJECT_MOBILEwt || + m_type == OBJECT_MOBILEtt || + m_type == OBJECT_MOBILEft || + m_type == OBJECT_MOBILEit || + m_type == OBJECT_MOBILEdr ) ) // véhicule ? + { + m_clownTime += event.rTime; + if ( m_clownTime >= m_clownDelay ) + { + if ( rand()%10 < 2 ) + { + m_clownRadius = 2.0f+Rand()*10.0f; +//? m_clownDelay = m_clownRadius/(2.0f+Rand()*2.0f); + m_clownDelay = 1.5f+Rand()*1.0f; + } + else + { + m_clownRadius = 0.0f; + m_clownDelay = 2.0f+Rand()*2.0f; + } + pos = m_object->RetPosition(0); + if ( pos.y < m_water->RetLevel() ) // sous l'eau ? + { + m_clownRadius /= 1.5f; + m_clownDelay *= 2.0f; + } + m_clownTime = 0.0f; + } + else + { + distance -= m_clownRadius*sinf(m_clownTime*PI*2.0f/m_clownDelay); + shift -= m_clownRadius-m_clownRadius*cosf(m_clownTime*PI*2.0f/m_clownDelay); + } + + verti += (18.0f-shift)*0.2f; + } + + distance /= focus; +//? shift *= focus; + verti /= focus; + + dir = Normalize(lookat-eye); + nPos = eye + dir*distance; + + perp.x = -dir.z; + perp.y = dir.y; + perp.z = dir.x; + nPos = nPos + perp*shift; + + nPos.y += verti; + + if ( m_bQuickPos ) // tout de suite en place ? + { + m_bQuickPos = FALSE; + linSpeed = 0.0f; + } + else + { + aPos = m_object->RetPosition(0); + if ( m_actionType == -1 ) + { + level = 4.0f; + } + else + { + if ( m_bStartAction ) + { + m_bStartAction = FALSE; + m_speedAction = Length(nPos, aPos)/15.0f; + if ( m_speedAction < 20.0f ) m_speedAction = 20.0f; + } + level = m_speedAction; + } + if ( level > 1.0f/event.rTime ) level = 1.0f/event.rTime; + nPos = aPos + (nPos-aPos)*event.rTime*level; // progression aPos -> nPos + + linSpeed = Length2d(nPos, aPos)/event.rTime; + dirSpeed = (nPos-aPos)/event.rTime; + nPos.y -= linSpeed*0.015f*(1.0f-progress); // au raz du sol si avance vite + } + } + + // Calcule le nouvel angle. + nAngle = NormAngle(RotateAngle(eye.x-lookat.x, lookat.z-eye.z)-0.9f); + if ( linSpeed == 0.0f || m_actionType != -1 ) + { + mAngle = nAngle; + } + else + { + mAngle = NormAngle(RotateAngle(dirSpeed.x, -dirSpeed.z)); + } + level = Min(linSpeed*0.1f, 1.0f); + nAngle = nAngle*(1.0f-level) + mAngle*level; + aAngle = NormAngle(m_object->RetAngleY(0)); + + if ( nAngle < aAngle ) + { + if ( nAngle+PI*2.0f-aAngle < aAngle-nAngle ) nAngle += PI*2.0f; + } + else + { + if ( aAngle+PI*2.0f-nAngle < nAngle-aAngle ) aAngle += PI*2.0f; + } + nAngle = aAngle + (nAngle-aAngle)*event.rTime*4.0f; + + // Penche de côté si tourne. + cirSpeed = (aAngle-nAngle)/event.rTime; + angle = cirSpeed*0.3f*(1.0f-progress); + if ( angle > 0.7f ) angle = 0.7f; + if ( angle < -0.7f ) angle = -0.7f; + vibCir.x += angle*1.5f; + aAntenna.x += Abs(angle)*0.8f; // écarte + + // Penche en avant si avance vite. + angle = linSpeed*0.10f*(1.0f-progress); + if ( angle > 1.0f ) angle = 1.0f; + vibCir.z -= angle/2.0f; // penche en avant + aAntenna.z -= angle; // penche en avant + + // Calcule le mouvement résiduel. +#if 1 + vibLin.y += (sinf(m_time*2.00f)*0.5f+ + sinf(m_time*2.11f)*0.2f)*(1.0f-progress); + + vibCir.z += sinf(m_time*PI* 2.01f)*(PI/ 75.0f)+ + sinf(m_time*PI* 2.51f)*(PI/100.0f)+ + sinf(m_time*PI*19.01f)*(PI/200.0f); + + vibCir.x += sinf(m_time*PI* 2.03f)*(PI/ 75.0f)+ + sinf(m_time*PI* 2.52f)*(PI/100.0f)+ + sinf(m_time*PI*19.53f)*(PI/200.0f); + + vibCir.y += (sinf(m_time*PI* 1.07f)*(PI/ 10.0f)+ + sinf(m_time*PI* 1.19f)*(PI/ 17.0f)+ + sinf(m_time*PI* 1.57f)*(PI/ 31.0f))*(1.0f-progress); +#endif + + // Calcule les animations lors d'une action. + if ( m_actionType == MT_ERROR ) // non-non ? + { + vibCir.y += progress*sinf(m_progress*PI*11.0f)*1.0f; + vibCir.z -= progress*0.5f; // penche en avant + + aAntenna.x -= progress*0.4f; // resserre + aAntenna.z += progress*1.0f; // penche en arrière + } + + if ( m_actionType == MT_WARNING ) // avertissement ? + { + vibCir.x += progress*sinf(m_progress*PI*17.0f)*0.5f; + + aAntenna.x += progress*sinf(m_progress*PI*17.0f)*0.5f; // écarte + aAntenna.z += progress*cosf(m_progress*PI*17.0f)*0.5f; // tourne + } + + if ( m_actionType == MT_INFO ) // oui-oui ? + { + vibCir.z += progress*sinf(m_progress*PI*19.0f)*0.7f; + + aAntenna.x -= progress*0.2f; // resserre + aAntenna.z -= progress*cosf(m_progress*PI*19.0f)*0.9f; // tourne + } + + if ( m_actionType == MT_MESSAGE ) // message ? + { + vibCir.x += progress*sinf(m_progress*PI*15.0f)*0.3f; + vibCir.z += progress*cosf(m_progress*PI*15.0f)*0.3f; + + aAntenna.x -= progress*0.4f; // resserre + aAntenna.z -= progress*cosf(m_progress*PI*19.0f)*0.8f; + } + + // Initialise l'objet. + if ( m_bDisplayInfo ) // "regarde" la souris ? + { + if ( m_mousePos.x < 0.15f ) + { + progress = 1.0f-m_mousePos.x/0.15f; + vibCir.y += progress*PI/2.0f; + } + else + { + progress = (m_mousePos.x-0.15f)/0.85f; + vibCir.y -= progress*PI/3.0f; + } + + angle = RotateAngle(m_mousePos.x-0.1f, m_mousePos.y-0.5f-vibLin.y*0.2f); + if ( angle < PI ) + { + if ( angle > PI*0.5f ) angle = PI-angle; + if ( angle > PI*0.3f ) angle = PI*0.3f; + vibCir.z += angle; + } + else + { + angle = PI*2.0f-angle; + if ( angle > PI*0.5f ) angle = PI-angle; + if ( angle > PI*0.3f ) angle = PI*0.3f; + vibCir.z -= angle; + } + } + else + { + nPos.y += vibLin.y; + level = m_terrain->RetFloorLevel(nPos); + if ( nPos.y < level+2.0f ) + { + nPos.y = level+2.0f; // juste au-dessus du sol + } + nPos.y -= vibLin.y; + } + m_object->SetPosition(0, nPos); + m_object->SetAngleY(0, nAngle); + + SetLinVibration(vibLin); + SetCirVibration(vibCir); + + // Calcule le mouvement résiduel des antennes. + pos = aAntenna*0.40f; + pos.x += sinf(m_time*PI*2.07f)*(PI/50.0f)+ + sinf(m_time*PI*2.59f)*(PI/70.0f)+ + sinf(m_time*PI*2.67f)*(PI/90.0f); + + pos.y += sinf(m_time*PI*2.22f)*(PI/50.0f)+ + sinf(m_time*PI*2.36f)*(PI/70.0f)+ + sinf(m_time*PI*3.01f)*(PI/90.0f); + + pos.z += sinf(m_time*PI*2.11f)*(PI/50.0f)+ + sinf(m_time*PI*2.83f)*(PI/70.0f)+ + sinf(m_time*PI*3.09f)*(PI/90.0f); + + m_object->SetAngle(4, pos); // antenne gauche + m_object->SetAngle(5, pos); // antenne gauche + m_object->SetAngle(6, pos); // antenne gauche + + pos = aAntenna*0.40f; + pos.x = -pos.x; + pos.x += sinf(m_time*PI*2.33f)*(PI/50.0f)+ + sinf(m_time*PI*2.19f)*(PI/70.0f)+ + sinf(m_time*PI*2.07f)*(PI/90.0f); + + pos.y += sinf(m_time*PI*2.44f)*(PI/50.0f)+ + sinf(m_time*PI*2.77f)*(PI/70.0f)+ + sinf(m_time*PI*3.22f)*(PI/90.0f); + + pos.z += sinf(m_time*PI*2.05f)*(PI/50.0f)+ + sinf(m_time*PI*2.38f)*(PI/70.0f)+ + sinf(m_time*PI*2.79f)*(PI/90.0f); + + m_object->SetAngle(7, pos); // antenne droite + m_object->SetAngle(8, pos); // antenne droite + m_object->SetAngle(9, pos); // antenne droite + + // Mouvement de la bouche. + if ( m_actionType == MT_ERROR ) // non-non ? + { + m_object->SetAngleX(1, 0.0f); + m_object->SetAngleZ(1, 0.2f+sinf(m_time*10.0f)*0.2f); + m_object->SetZoomY(1, 2.0f+sinf(m_time*10.0f)); + m_object->SetZoomZ(1, 1.0f); + } + else if ( m_actionType == MT_WARNING ) // avertissement ? + { + m_object->SetAngleX(1, 15.0f*PI/180.0f); + m_object->SetAngleZ(1, 0.0f); + m_object->SetZoomY(1, 1.0f); + m_object->SetZoomZ(1, 1.0f); + } + else if ( m_actionType == MT_INFO ) // oui-oui ? + { + m_object->SetAngleX(1, 0.0f); + m_object->SetAngleZ(1, 0.0f); + m_object->SetZoomY(1, 1.0f); + m_object->SetZoomZ(1, 0.7f+sinf(m_time*10.0f)*0.3f); + } + else if ( m_actionType == MT_MESSAGE ) // message ? + { + m_object->SetAngleX(1, 0.0f); + m_object->SetAngleZ(1, 0.0f); + m_object->SetZoomY(1, 1.0f); + m_object->SetZoomZ(1, 0.8f+sinf(m_time*7.0f)*0.2f); + } + else + { + m_object->SetAngleX(1, 0.0f); + m_object->SetAngleZ(1, 0.0f); + m_object->SetZoomY(1, 1.0f); + m_object->SetZoomZ(1, 1.0f); + } + + // Gestion du clignement des yeux. + if ( m_blinkTime <= 0.0f && m_blinkProgress == -1.0f ) + { + m_blinkProgress = 0.0f; + } + + if ( m_blinkProgress >= 0.0f ) + { + m_blinkProgress += event.rTime*3.2f; + + if ( m_blinkProgress < 1.0f ) + { + if ( m_blinkProgress < 0.5f ) zoom = m_blinkProgress/0.5f; + else zoom = 2.0f-m_blinkProgress/0.5f; + m_object->SetZoomY(2, 1.0f-zoom*0.9f); + m_object->SetZoomY(3, 1.0f-zoom*0.9f); + } + else + { + m_blinkProgress = -1.0f; + m_blinkTime = 0.1f+Rand()*4.0f; + m_object->SetZoomY(2, 1.0f); + m_object->SetZoomY(3, 1.0f); + } + } + + if ( m_actionType == MT_ERROR ) // non-non ? + { + m_object->SetAngleX(2, -30.0f*PI/180.0f); + m_object->SetAngleX(3, 30.0f*PI/180.0f); + } + else if ( m_actionType == MT_WARNING ) // avertissement ? + { + m_object->SetAngleX(2, -15.0f*PI/180.0f); + m_object->SetAngleX(3, 15.0f*PI/180.0f); + } + else if ( m_actionType == MT_INFO ) // oui-oui ? + { + m_object->SetAngleX(2, 40.0f*PI/180.0f); + m_object->SetAngleX(3, -40.0f*PI/180.0f); + } + else if ( m_actionType == MT_MESSAGE ) // message ? + { + m_object->SetAngleX(2, 20.0f*PI/180.0f); + m_object->SetAngleX(3, -20.0f*PI/180.0f); + } + else + { + m_object->SetAngleX(2, 0.0f); + m_object->SetAngleX(3, 0.0f); + } + + mat = m_object->RetWorldMatrix(0); // doit être fait chaque fois ! + + // Génère les particules. + if ( m_time-m_lastMotorParticule >= m_engine->ParticuleAdapt(0.05f) ) + { + m_lastMotorParticule = m_time; + + if ( m_bDisplayInfo ) sheet = SH_FRONT; + else sheet = SH_WORLD; + + pos = m_object->RetPosition(0); + if ( !m_bDisplayInfo && + pos.y < m_water->RetLevel() ) // sous l'eau ? + { + float t = Mod(m_time, 3.5f); + if ( t >= 2.2f || ( t >= 1.2f && t <= 1.4f ) ) // respire ? + { + pos = D3DVECTOR(1.0f, 0.2f, 0.0f); + pos.z += (Rand()-0.5f)*0.5f; + + speed = pos; + speed.y += 5.0f+Rand()*5.0f; + speed.x += Rand()*2.0f; + speed.z += (Rand()-0.5f)*2.0f; + + pos = Transform(*mat, pos); + speed = Transform(*mat, speed)-pos; + + dim.x = 0.12f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBUBBLE, 3.0f, 0.0f, 0.0f); + } + } + else // hors de l'eau ? + { + pos = D3DVECTOR(0.0f, -0.5f, 0.0f); + pos.z += (Rand()-0.5f)*0.5f; + + speed = pos; + speed.y -= (1.5f+Rand()*1.5f) + vibLin.y; + speed.x += (Rand()-0.5f)*2.0f; + speed.z += (Rand()-0.5f)*2.0f; + +// mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + speed = Transform(*mat, speed)-pos; + + dim.x = (Rand()*0.4f+0.4f)*(1.0f+Min(linSpeed*0.1f, 5.0f)); + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTITOTO, 1.0f+Rand()*1.0f, 0.0f, 1.0f, sheet); + } + + if ( m_actionType != -1 && // action en cours ? + m_progress <= 0.85f ) + { + pos.x = (Rand()-0.5f)*1.0f; + pos.y = (Rand()-0.5f)*1.0f+3.5f; + pos.z = (Rand()-0.5f)*1.0f; + pos = Transform(*mat, pos); + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = (Rand()*0.3f+0.3f); + dim.y = dim.x; + if ( m_actionType == MT_ERROR ) type = PARTIERROR; + if ( m_actionType == MT_WARNING ) type = PARTIWARNING; + if ( m_actionType == MT_INFO ) type = PARTIINFO; + if ( m_actionType == MT_MESSAGE ) type = PARTIWARNING; + m_particule->CreateParticule(pos, speed, dim, type, 0.5f+Rand()*0.5f, 0.0f, 1.0f, sheet); + + pos.x = 0.50f+(Rand()-0.5f)*0.80f; + pos.y = 0.86f+(Rand()-0.5f)*0.08f; + pos.z = 0.00f; + dim.x = (Rand()*0.04f+0.04f); + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, type, 0.5f+Rand()*0.5f, 0.0f, 1.0f, SH_INTERFACE); + } + +//? if ( m_bDisplayInfo && m_main->RetGlint() ) + if ( FALSE ) + { + pos.x = (Rand()-0.5f)*1.4f; + pos.y = (Rand()-0.5f)*1.4f+3.5f; + pos.z = (Rand()-0.5f)*1.4f; + pos = Transform(*mat, pos); + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = (Rand()*0.5f+0.5f); + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIERROR, 0.5f+Rand()*0.5f, 0.0f, 1.0f, sheet); + + for ( i=0 ; i<10 ; i++ ) + { + pos.x = 0.60f+(Rand()-0.5f)*0.76f; + pos.y = 0.47f+(Rand()-0.5f)*0.90f; + pos.z = 0.00f; + r = rand()%4; + if ( r == 0 ) pos.x = 0.21f; // sur le bord gauche + else if ( r == 1 ) pos.x = 0.98f; // sur le bord droite + else if ( r == 2 ) pos.y = 0.02f; // sur le bord inférieur + else pos.y = 0.92f; // sur le bord supérieur + dim.x = (Rand()*0.02f+0.02f); + dim.y = dim.x/0.75f; + m_particule->CreateParticule(pos, speed, dim, PARTIERROR, 0.5f+Rand()*0.5f, 0.0f, 1.0f, SH_INTERFACE); + } + } + } + + // Bouge le son. + if ( m_soundChannel != -1 ) + { + if ( !m_sound->Position(m_soundChannel, m_object->RetPosition(0)) ) + { + m_soundChannel = -1; + } + } + + return TRUE; +} + + +// Démarre une action. + +Error CMotionToto::SetAction(int action, float time) +{ + Sound sound; + + CMotion::SetAction(action, time); + + m_bStartAction = TRUE; + + sound = SOUND_CLICK; + if ( action == MT_ERROR ) sound = SOUND_ERROR; + if ( action == MT_WARNING ) sound = SOUND_WARNING; + if ( action == MT_INFO ) sound = SOUND_INFO; + if ( action == MT_MESSAGE ) sound = SOUND_MESSAGE; + + if ( sound != SOUND_CLICK ) + { + m_soundChannel = m_sound->Play(sound, m_object->RetPosition(0)); + } + + return ERR_OK; +} + +// Spécifie le type de l'objet rattaché à toto. + +void CMotionToto::SetLinkType(ObjectType type) +{ + m_type = type; +} + + diff --git a/src/motiontoto.h b/src/motiontoto.h new file mode 100644 index 00000000..e6821e87 --- /dev/null +++ b/src/motiontoto.h @@ -0,0 +1,61 @@ +// motiontoto.h + +#ifndef _MOTIONTOTO_H_ +#define _MOTIONTOTO_H_ + + +class CInstanceManager; +class CEngine; +class CLight; +class CParticule; +class CTerrain; +class CCamera; +class CBrain; +class CPhysics; +class CObject; + + +#define MT_ERROR 0 +#define MT_WARNING 1 +#define MT_INFO 2 +#define MT_MESSAGE 3 + + +class CMotionToto : public CMotion +{ +public: + CMotionToto(CInstanceManager* iMan, CObject* object); + ~CMotionToto(); + + void DeleteObject(BOOL bAll=FALSE); + BOOL Create(D3DVECTOR pos, float angle, ObjectType type, float power); + BOOL EventProcess(const Event &event); + Error SetAction(int action, float time=0.2f); + void SetLinkType(ObjectType type); + + void StartDisplayInfo(); + void StopDisplayInfo(); + void SetMousePos(FPOINT pos); + +protected: + BOOL EventFrame(const Event &event); + +protected: + float m_time; + float m_lastMotorParticule; + BOOL m_bDisplayInfo; + BOOL m_bQuickPos; + BOOL m_bStartAction; + float m_speedAction; + float m_clownRadius; + float m_clownDelay; + float m_clownTime; + float m_blinkTime; + float m_blinkProgress; + int m_soundChannel; + ObjectType m_type; + FPOINT m_mousePos; +}; + + +#endif //_MOTIONTOTO_H_ diff --git a/src/motionvehicle.cpp b/src/motionvehicle.cpp new file mode 100644 index 00000000..45465332 --- /dev/null +++ b/src/motionvehicle.cpp @@ -0,0 +1,2075 @@ +// motionvehicle.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "modfile.h" +#include "sound.h" +#include "motion.h" +#include "motionvehicle.h" + + + +#define ARM_NEUTRAL_ANGLE1 110.0f*PI/180.0f +#define ARM_NEUTRAL_ANGLE2 -130.0f*PI/180.0f +#define ARM_NEUTRAL_ANGLE3 -50.0f*PI/180.0f + + + +// Constructeur de l'objet. + +CMotionVehicle::CMotionVehicle(CInstanceManager* iMan, CObject* object) + : CMotion(iMan, object) +{ + int i; + + CMotion::CMotion(iMan, object); + + for ( i=0 ; i<4 ; i++ ) + { + m_wheelTurn[i] = 0.0f; + } + for ( i=0 ; i<3 ; i++ ) + { + m_flyPaw[i] = 0.0f; + } + m_posTrackLeft = 0.0f; + m_posTrackRight = 0.0f; + m_partiReactor = -1; + m_armTimeAbs = 1000.0f; + m_armMember = 1000.0f; + m_canonTime = 0.0f; + m_lastTimeCanon = 0.0f; + m_wheelLastPos = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_wheelLastAngle = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_posKey = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_bFlyFix = FALSE; + + m_bTraceDown = FALSE; + m_traceColor = 1; // black + m_traceWidth = 0.5f; +} + +// Destructeur de l'objet. + +CMotionVehicle::~CMotionVehicle() +{ +} + + +// Supprime un objet. + +void CMotionVehicle::DeleteObject(BOOL bAll) +{ + if ( m_partiReactor != -1 ) + { + m_particule->DeleteParticule(m_partiReactor); + m_partiReactor = -1; + } +} + + +// Crée un véhicule roulant quelconque posé sur le sol. + +BOOL CMotionVehicle::Create(D3DVECTOR pos, float angle, ObjectType type, + float power) +{ + CModFile* pModFile; + CObject* pPower; + int rank, i, j, parent; + D3DCOLORVALUE color; + char name[50]; + + if ( m_engine->RetRestCreate() < 1+5+18+1 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + m_object->SetType(type); + + // Crée la base principale. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEVEHICULE); // c'est un objet mobile + m_object->SetObjectRank(0, rank); + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEfs ) + { + pModFile->ReadModel("objects\\lem1f.mod"); + } + if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts ) + { + pModFile->ReadModel("objects\\lem1t.mod"); + } + if ( type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEws ) + { + if ( m_object->RetTrainer() ) + { + pModFile->ReadModel("objects\\lem1wt.mod"); + } + else + { + pModFile->ReadModel("objects\\lem1w.mod"); + } + } + if ( type == OBJECT_MOBILEia || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEis ) + { + pModFile->ReadModel("objects\\lem1i.mod"); + } + if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) + { + pModFile->ReadModel("objects\\roller1.mod"); + } + if ( type == OBJECT_MOBILEsa ) + { + pModFile->ReadModel("objects\\subm1.mod"); + } + if ( type == OBJECT_MOBILEtg ) + { + pModFile->ReadModel("objects\\target.mod"); + } + if ( type == OBJECT_MOBILEwt ) + { + pModFile->ReadModel("objects\\trainerw.mod"); + } + if ( type == OBJECT_MOBILEft ) + { + pModFile->ReadModel("objects\\trainerf.mod"); + } + if ( type == OBJECT_MOBILEtt ) + { + pModFile->ReadModel("objects\\trainert.mod"); + } + if ( type == OBJECT_MOBILEit ) + { + pModFile->ReadModel("objects\\traineri.mod"); + } + if ( type == OBJECT_MOBILEdr ) + { + pModFile->ReadModel("objects\\drawer1.mod"); + } + if ( type == OBJECT_APOLLO2 ) + { + pModFile->ReadModel("objects\\apolloj1.mod"); + } + pModFile->CreateEngineObject(rank); + + m_object->SetPosition(0, pos); + m_object->SetAngleY(0, angle); + + // Un véhicule doit avoir obligatoirement une sphère de + // collision avec un centre (0;y;0) (voir GetCrashSphere). + if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) + { + m_object->CreateCrashSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 6.5f, SOUND_BOUMm, 0.45f); + m_object->SetGlobalSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 7.0f); + } + else if ( type == OBJECT_MOBILEsa ) + { + m_object->CreateCrashSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 4.5f, SOUND_BOUMm, 0.45f); + m_object->SetGlobalSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 6.0f); + } + else if ( type == OBJECT_MOBILEdr ) + { + m_object->CreateCrashSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + m_object->SetGlobalSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 7.0f); + } + else if ( type == OBJECT_APOLLO2 ) + { + m_object->CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 8.0f, SOUND_BOUMm, 0.45f); + } + else + { + m_object->CreateCrashSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 4.5f, SOUND_BOUMm, 0.45f); + m_object->SetGlobalSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 6.0f); + } + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia ) + { + // Crée le bras. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\lem2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(0.0f, 5.3f, 0.0f)); + m_object->SetAngleZ(1, ARM_NEUTRAL_ANGLE1); + + // Crée l'avant-bras. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 1); + pModFile->ReadModel("objects\\lem3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(5.0f, 0.0f, 0.0f)); + m_object->SetAngleZ(2, ARM_NEUTRAL_ANGLE2); + + // Crée la main. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(3, rank); + m_object->SetObjectParent(3, 2); + pModFile->ReadModel("objects\\lem4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(3, D3DVECTOR(3.5f, 0.0f, 0.0f)); + m_object->SetAngleZ(3, ARM_NEUTRAL_ANGLE3); + m_object->SetAngleX(3, PI/2.0f); + + // Crée la pince proche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(4, rank); + m_object->SetObjectParent(4, 3); + pModFile->ReadModel("objects\\lem5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(4, D3DVECTOR(1.5f, 0.0f, 0.0f)); + m_object->SetAngleZ(4, -PI*0.10f); + + // Crée la pince éloignée. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(5, rank); + m_object->SetObjectParent(5, 3); + pModFile->ReadModel("objects\\lem6.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(5, D3DVECTOR(1.5f, 0.0f, 0.0f)); + m_object->SetAngleZ(5, PI*0.10f); + } + + if ( type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis ) + { + // Crée le bras. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\lem2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(0.0f, 5.3f, 0.0f)); + m_object->SetAngleZ(1, 110.0f*PI/180.0f); + + // Crée l'avant-bras. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 1); + pModFile->ReadModel("objects\\lem3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(5.0f, 0.0f, 0.0f)); + m_object->SetAngleZ(2, -110.0f*PI/180.0f); + + // Crée le capteur. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(3, rank); + m_object->SetObjectParent(3, 2); + pModFile->ReadModel("objects\\lem4s.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(3, D3DVECTOR(3.5f, 0.0f, 0.0f)); + m_object->SetAngleZ(3, -65.0f*PI/180.0f); + } + + if ( type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic ) + { + // Crée le canon. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\canon.mod"); + pModFile->CreateEngineObject(rank); +//? m_object->SetPosition(1, D3DVECTOR(0.0f, 5.3f, 0.0f)); + m_object->SetPosition(1, D3DVECTOR(0.0f, 5.3f, 0.0f)); + m_object->SetAngleZ(1, 0.0f); + } + + if ( type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii ) + { + // Crée le canon insecte. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\canoni1.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(0.0f, 5.3f, 0.0f)); + m_object->SetAngleZ(1, 0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 1); + pModFile->ReadModel("objects\\canoni2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(0.0f, 2.5f, 0.0f)); + m_object->SetAngleZ(2, 0.0f); + } + + if ( type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEwt ) + { + // Crée la roue arrière-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 0); + pModFile->ReadModel("objects\\lem2w.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(-3.0f, 1.0f, -3.0f)); + + // Crée la roue arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 0); + pModFile->ReadModel("objects\\lem2w.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(-3.0f, 1.0f, 3.0f)); + m_object->SetAngleY(7, PI); + + // Crée la roue avant-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(8, rank); + m_object->SetObjectParent(8, 0); + pModFile->ReadModel("objects\\lem2w.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(8, D3DVECTOR(2.0f, 1.0f, -3.0f)); + + // Crée la roue avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(9, rank); + m_object->SetObjectParent(9, 0); + pModFile->ReadModel("objects\\lem2w.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(9, D3DVECTOR(2.0f, 1.0f, 3.0f)); + m_object->SetAngleY(9, PI); + } + + if ( type == OBJECT_MOBILEtg ) + { + // Crée la roue arrière-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 0); + pModFile->ReadModel("objects\\lem2w.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(-2.0f, 1.0f, -3.0f)); + + // Crée la roue arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 0); + pModFile->ReadModel("objects\\lem2w.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(-2.0f, 1.0f, 3.0f)); + m_object->SetAngleY(7, PI); + + // Crée la roue avant-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(8, rank); + m_object->SetObjectParent(8, 0); + pModFile->ReadModel("objects\\lem2w.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(8, D3DVECTOR(3.0f, 1.0f, -3.0f)); + + // Crée la roue avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(9, rank); + m_object->SetObjectParent(9, 0); + pModFile->ReadModel("objects\\lem2w.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(9, D3DVECTOR(3.0f, 1.0f, 3.0f)); + m_object->SetAngleY(9, PI); + } + + if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts ) // chenilles ? + { + // Crée la chenille droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 0); + pModFile->ReadModel("objects\\lem2t.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(0.0f, 2.0f, -3.0f)); + + // Crée la chenille gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 0); + pModFile->ReadModel("objects\\lem3t.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(0.0f, 2.0f, 3.0f)); + } + + if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) // grosses chenilles ? + { + // Crée la chenille droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 0); + pModFile->ReadModel("objects\\roller2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(0.0f, 2.0f, -3.0f)); + + // Crée la chenille gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 0); + pModFile->ReadModel("objects\\roller3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(0.0f, 2.0f, 3.0f)); + } + + if ( type == OBJECT_MOBILEsa ) // chenilles sous-marin ? + { + // Crée la chenille droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 0); + pModFile->ReadModel("objects\\subm4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(0.0f, 1.0f, -3.0f)); + + // Crée la chenille gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 0); + pModFile->ReadModel("objects\\subm5.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(0.0f, 1.0f, 3.0f)); + } + + if ( type == OBJECT_MOBILEdr ) // chenilles ? + { + // Crée la chenille droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 0); + pModFile->ReadModel("objects\\drawer2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(0.0f, 1.0f, -3.0f)); + + // Crée la chenille gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 0); + pModFile->ReadModel("objects\\drawer3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(0.0f, 1.0f, 3.0f)); + } + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEft ) // volant ? + { + // Crée le pied avant. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 0); + pModFile->ReadModel("objects\\lem2f.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(1.7f, 3.0f, 0.0f)); + + // Crée le pied arrière droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 0); + pModFile->ReadModel("objects\\lem2f.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(-1.8f, 3.0f, -1.5f)); + m_object->SetAngleY(7, 120.0f*PI/180.0f); + + // Crée le pied arrière gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(8, rank); + m_object->SetObjectParent(8, 0); + pModFile->ReadModel("objects\\lem2f.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(8, D3DVECTOR(-1.8f, 3.0f, 1.5f)); + m_object->SetAngleY(8, -120.0f*PI/180.0f); + } + + if ( type == OBJECT_MOBILEia || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILEii ) // pattes d'insecte ? + { + float table[] = + { + // x y z + -1.5f, 1.2f, -0.7f, // patte arrière + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, -2.0f, + + 0.0f, 1.2f, -0.9f, // patte milieu + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, -2.0f, + + 1.5f, 1.2f, -0.7f, // patte avant + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, -2.0f, + }; + + for ( i=0 ; i<3 ; i++ ) + { + for ( j=0 ; j<3 ; j++ ) + { + sprintf(name, "objects\\ant%d.mod", j+4); // 4..6 + + // Crée la patte droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6+i*3+j, rank); + if ( j == 0 ) parent = 0; + else parent = 6+i*3+j-1; + m_object->SetObjectParent(6+i*3+j, parent); + pModFile->ReadModel(name); + pModFile->CreateEngineObject(rank); + pos.x = table[i*9+j*3+0]; + pos.y = table[i*9+j*3+1]; + pos.z = table[i*9+j*3+2]; + m_object->SetPosition(6+i*3+j, pos); + + // Crée la patte gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(15+i*3+j, rank); + if ( j == 0 ) parent = 0; + else parent = 15+i*3+j-1; + m_object->SetObjectParent(15+i*3+j, parent); + pModFile->ReadModel(name); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + pos.x = table[i*9+j*3+0]; + pos.y = table[i*9+j*3+1]; + pos.z = -table[i*9+j*3+2]; + m_object->SetPosition(15+i*3+j, pos); + } + } + } + + if ( type == OBJECT_MOBILErt ) + { + // Crée le support. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\roller2t.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_object->SetAngleZ(1, 0.0f); + + // Crée le pilon. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 0); + pModFile->ReadModel("objects\\roller3t.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(9.0f, 4.0f, 0.0f)); + m_object->SetAngleZ(2, 0.0f); + } + + if ( type == OBJECT_MOBILErc ) + { + // Crée le support. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\roller2c.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(3.0f, 4.6f, 0.0f)); + m_object->SetAngleZ(1, PI/8.0f); + + // Crée le canon. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 0); + pModFile->ReadModel("objects\\roller3p.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(7.0f, 6.5f, 0.0f)); + m_object->SetAngleZ(2, 0.0f); + } + + if ( type == OBJECT_MOBILErr ) + { + // Crée le support. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\recover1.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(2.0f, 5.0f, 0.0f)); + + // Crée le bras droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 1); + pModFile->ReadModel("objects\\recover2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(0.1f, 0.0f, -5.0f)); + m_object->SetAngleZ(2, 126.0f*PI/180.0f); + + // Crée l'avant-bras droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(3, rank); + m_object->SetObjectParent(3, 2); + pModFile->ReadModel("objects\\recover3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(3, D3DVECTOR(5.0f, 0.0f, -0.5f)); + m_object->SetAngleZ(3, -144.0f*PI/180.0f); + + // Crée le bras gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(4, rank); + m_object->SetObjectParent(4, 1); + pModFile->ReadModel("objects\\recover2.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(4, D3DVECTOR(0.1f, 0.0f, 5.0f)); + m_object->SetAngleZ(4, 126.0f*PI/180.0f); + + // Crée l'avant-bras gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(5, rank); + m_object->SetObjectParent(5, 4); + pModFile->ReadModel("objects\\recover3.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(5, D3DVECTOR(5.0f, 0.0f, 0.5f)); + m_object->SetAngleZ(5, -144.0f*PI/180.0f); + } + + if ( type == OBJECT_MOBILErs ) + { + // Crée le support. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\roller2s.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_object->SetAngleZ(1, 0.0f); + + // Crée le piston intermédiaire. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 1); + pModFile->ReadModel("objects\\roller3s.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(7.0f, 4.5f, 0.0f)); + m_object->SetAngleZ(2, 0.0f); + + // Crée le piston avec la sphère. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(3, rank); + m_object->SetObjectParent(3, 2); + pModFile->ReadModel("objects\\roller4s.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(3, D3DVECTOR(0.0f, 1.0f, 0.0f)); + m_object->SetAngleZ(3, 0.0f); + } + + if ( type == OBJECT_MOBILEsa ) + { + // Crée le support. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\subm2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(4.2f, 3.0f, 0.0f)); + + // Crée la pince droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 1); + pModFile->ReadModel("objects\\subm3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(0.5f, 0.0f, -1.5f)); + + // Crée la pince gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(3, rank); + m_object->SetObjectParent(3, 1); + pModFile->ReadModel("objects\\subm3.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(3, D3DVECTOR(0.5f, 0.0f, 1.5f)); + } + + if ( type == OBJECT_MOBILEdr ) + { + // Crée le carousel. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\drawer4.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(-3.0f, 3.0f, 0.0f)); + + // Crée la clé. + if ( m_object->RetToy() ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 0); + pModFile->ReadModel("objects\\drawer5.mod"); + pModFile->CreateEngineObject(rank); + m_posKey = D3DVECTOR(3.0f, 5.7f, 0.0f); + m_object->SetPosition(2, m_posKey); + m_object->SetAngleY(2, 90.0f*PI/180.0f); + } + + // Crée les crayons. + for ( i=0 ; i<8 ; i++ ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(10+i, rank); + m_object->SetObjectParent(10+i, 1); + sprintf(name, "objects\\drawer%d.mod", 10+i); + pModFile->ReadModel(name); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(10+i, D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_object->SetAngleY(10+i, 45.0f*PI/180.0f*i); + } + } + + if ( type == OBJECT_MOBILEwt ) + { + // Crée la clé. + if ( m_object->RetToy() ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 0); + pModFile->ReadModel("objects\\drawer5.mod"); + pModFile->CreateEngineObject(rank); + m_posKey = D3DVECTOR(0.2f, 4.1f, 0.0f); + m_object->SetPosition(2, m_posKey); + m_object->SetAngleY(2, 90.0f*PI/180.0f); + } + } + + if ( type == OBJECT_APOLLO2 ) + { + // Crée les accessoirs. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\apolloj2.mod"); // antenne + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(5.5f, 8.8f, 2.0f)); + m_object->SetAngleY(1, -120.0f*PI/180.0f); + m_object->SetAngleZ(1, 45.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2, rank); + m_object->SetObjectParent(2, 0); + pModFile->ReadModel("objects\\apolloj3.mod"); // caméra + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2, D3DVECTOR(5.5f, 2.8f, -2.0f)); + m_object->SetAngleY(2, 30.0f*PI/180.0f); + + // Crée les roues. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(6, rank); + m_object->SetObjectParent(6, 0); + pModFile->ReadModel("objects\\apolloj4.mod"); // roue + pModFile->CreateEngineObject(rank); + m_object->SetPosition(6, D3DVECTOR(-5.75f, 1.65f, -5.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(7, rank); + m_object->SetObjectParent(7, 0); + pModFile->ReadModel("objects\\apolloj4.mod"); // roue + pModFile->CreateEngineObject(rank); + m_object->SetPosition(7, D3DVECTOR(-5.75f, 1.65f, 5.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(8, rank); + m_object->SetObjectParent(8, 0); + pModFile->ReadModel("objects\\apolloj4.mod"); // roue + pModFile->CreateEngineObject(rank); + m_object->SetPosition(8, D3DVECTOR(5.75f, 1.65f, -5.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(9, rank); + m_object->SetObjectParent(9, 0); + pModFile->ReadModel("objects\\apolloj4.mod"); // roue + pModFile->CreateEngineObject(rank); + m_object->SetPosition(9, D3DVECTOR(5.75f, 1.65f, 5.00f)); + + // Crée les gardes boues. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(10, rank); + m_object->SetObjectParent(10, 0); + pModFile->ReadModel("objects\\apolloj6.mod"); // roue + pModFile->CreateEngineObject(rank); + m_object->SetPosition(10, D3DVECTOR(-5.75f, 1.65f, -5.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(11, rank); + m_object->SetObjectParent(11, 0); + pModFile->ReadModel("objects\\apolloj6.mod"); // roue + pModFile->CreateEngineObject(rank); + m_object->SetPosition(11, D3DVECTOR(-5.75f, 1.65f, 5.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(12, rank); + m_object->SetObjectParent(12, 0); + pModFile->ReadModel("objects\\apolloj5.mod"); // roue + pModFile->CreateEngineObject(rank); + m_object->SetPosition(12, D3DVECTOR(5.75f, 1.65f, -5.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(13, rank); + m_object->SetObjectParent(13, 0); + pModFile->ReadModel("objects\\apolloj5.mod"); // roue + pModFile->CreateEngineObject(rank); + m_object->SetPosition(13, D3DVECTOR(5.75f, 1.65f, 5.00f)); + } + +#if 1 + if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) + { + m_object->CreateShadowCircle(6.0f, 1.0f); + } + else if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEsa ) + { + m_object->CreateShadowCircle(5.0f, 1.0f); + } + else if ( type == OBJECT_MOBILEdr ) + { + m_object->CreateShadowCircle(4.5f, 1.0f); + } + else if ( type == OBJECT_APOLLO2 ) + { + m_object->CreateShadowCircle(7.0f, 0.8f); + } + else + { + m_object->CreateShadowCircle(4.0f, 1.0f); + } +#else + if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) + { + m_object->CreateShadowCircle(6.0f, 1.0f, D3DSHADOWTANK); + } + else if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts ) + { + m_object->CreateShadowCircle(4.0f, 1.0f, D3DSHADOWTANK); + } + else if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEfs ) + { + m_object->CreateShadowCircle(4.0f, 1.0f, D3DSHADOWFLY); + } + else if ( type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEws ) + { + m_object->CreateShadowCircle(4.0f, 1.0f, D3DSHADOWWHEEL); + } + else if ( type == OBJECT_APOLLO2 ) + { + m_object->CreateShadowCircle(6.0f, 0.8f); + } + else + { + m_object->CreateShadowCircle(4.0f, 1.0f, D3DSHADOWNORM); + } +#endif + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEft ) // volant ? + { +//? color.r = 0.5f-1.0f; +//? color.g = 0.2f-1.0f; +//? color.b = 0.0f-1.0f; // orange +//? color.r = 0.8f; +//? color.g = 0.6f; +//? color.b = 0.0f; // jaune-orange + color.r = 0.0f; + color.g = 0.4f; + color.b = 0.8f; // bleu + color.a = 0.0f; + m_object->CreateShadowLight(50.0f, color); + } + + CreatePhysics(type); + m_object->SetFloorHeight(0.0f); + + if ( power > 0.0f && + type != OBJECT_MOBILEdr && + type != OBJECT_APOLLO2 ) + { + color.r = 1.0f; + color.g = 1.0f; + color.b = 0.0f; // jaune + color.a = 0.0f; + m_object->CreateEffectLight(20.0f, color); + + // Crée la pile. + pPower = new CObject(m_iMan); + pPower->SetType(power<=1.0f?OBJECT_POWER:OBJECT_ATOMIC); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + pPower->SetObjectRank(0, rank); + + if ( power <= 1.0f ) pModFile->ReadModel("objects\\power.mod"); + else pModFile->ReadModel("objects\\atomic.mod"); + pModFile->CreateEngineObject(rank); + + pPower->SetPosition(0, m_object->RetCharacter()->posPower); + pPower->CreateCrashSphere(D3DVECTOR(0.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + pPower->SetGlobalSphere(D3DVECTOR(0.0f, 1.0f, 0.0f), 1.5f); + + pPower->SetTruck(m_object); + m_object->SetPower(pPower); + + if ( power <= 1.0f ) pPower->SetEnergy(power); + else pPower->SetEnergy(power/100.0f); + } + + pos = m_object->RetPosition(0); + m_object->SetPosition(0, pos); // pour afficher les ombres tout de suite + + m_engine->LoadAllTexture(); + + delete pModFile; + return TRUE; +} + +// Crée la physique de l'objet. + +void CMotionVehicle::CreatePhysics(ObjectType type) +{ + Character* character; + + character = m_object->RetCharacter(); + + if ( type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEwt ) // roues ? + { + m_physics->SetType(TYPE_ROLLING); + + character->wheelFront = 3.0f; + character->wheelBack = 4.0f; + character->wheelLeft = 4.0f; + character->wheelRight = 4.0f; + character->posPower = D3DVECTOR(-3.2f, 3.0f, 0.0f); + + m_physics->SetLinMotionX(MO_ADVSPEED, 20.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 10.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 40.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 20.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 50.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 30.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 20.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.8f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.8f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 8.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 8.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 12.0f); + } + + if ( type == OBJECT_MOBILEtg ) + { + m_physics->SetType(TYPE_ROLLING); + + character->wheelFront = 4.0f; + character->wheelBack = 3.0f; + character->wheelLeft = 4.0f; + character->wheelRight = 4.0f; + character->posPower = D3DVECTOR(-3.2f, 3.0f, 0.0f); + + m_physics->SetLinMotionX(MO_ADVSPEED, 20.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 10.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 40.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 20.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 50.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 20.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 20.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.8f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.8f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 10.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 10.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 15.0f); + } + + if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts ) // chenilles ? + { + m_physics->SetType(TYPE_ROLLING); + + character->wheelFront = 4.0f; + character->wheelBack = 4.0f; + character->wheelLeft = 4.8f; + character->wheelRight = 4.8f; + character->posPower = D3DVECTOR(-3.2f, 3.0f, 0.0f); + + m_physics->SetLinMotionX(MO_ADVSPEED, 15.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 8.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 15.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 8.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 20.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 10.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 40.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.5f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.5f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 10.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 10.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 6.0f); + } + + if ( type == OBJECT_MOBILEia || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEis ) // pattes ? + { + m_physics->SetType(TYPE_ROLLING); + + character->wheelFront = 4.0f; + character->wheelBack = 4.0f; + character->wheelLeft = 5.0f; + character->wheelRight = 5.0f; + character->posPower = D3DVECTOR(-3.2f, 3.0f, 0.0f); + + m_physics->SetLinMotionX(MO_ADVSPEED, 15.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 8.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 40.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 20.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 10.0f); +//? m_physics->SetLinMotionX(MO_TERFORCE, 15.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 10.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 40.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.5f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.5f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 10.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 10.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 15.0f); + } + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEft ) // volant ? + { + m_physics->SetType(TYPE_FLYING); + + character->wheelFront = 5.0f; + character->wheelBack = 4.0f; + character->wheelLeft = 4.5f; + character->wheelRight = 4.5f; + character->posPower = D3DVECTOR(-3.2f, 3.0f, 0.0f); + + m_physics->SetLinMotionX(MO_ADVSPEED, 50.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 50.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 20.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 20.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 20.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 50.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 50.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 40.0f); + m_physics->SetLinMotionY(MO_ADVSPEED, 60.0f); + m_physics->SetLinMotionY(MO_RECSPEED, 60.0f); + m_physics->SetLinMotionY(MO_ADVACCEL, 20.0f); + m_physics->SetLinMotionY(MO_RECACCEL, 50.0f); + m_physics->SetLinMotionY(MO_STOACCEL, 50.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.4f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.4f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 2.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 2.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 2.0f); + } + + if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) // grosses chenilles ? + { + m_physics->SetType(TYPE_ROLLING); + + character->wheelFront = 5.0f; + character->wheelBack = 5.0f; + character->wheelLeft = 6.0f; + character->wheelRight = 6.0f; + character->posPower = D3DVECTOR(-5.8f, 4.0f, 0.0f); + + m_physics->SetLinMotionX(MO_ADVSPEED, 10.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 5.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 10.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 5.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 20.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 10.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 40.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.3f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.3f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 2.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 2.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 4.0f); + } + + if ( type == OBJECT_MOBILEsa ) + { + m_physics->SetType(TYPE_ROLLING); + + character->wheelFront = 4.0f; + character->wheelBack = 4.0f; + character->wheelLeft = 4.0f; + character->wheelRight = 4.0f; + character->posPower = D3DVECTOR(-5.0f, 3.0f, 0.0f); + + m_physics->SetLinMotionX(MO_ADVSPEED, 15.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 10.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 20.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 10.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 20.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 10.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 40.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.5f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.5f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 5.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 5.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 10.0f); + } + + if ( type == OBJECT_MOBILEdr ) + { + m_physics->SetType(TYPE_ROLLING); + + character->wheelFront = 4.0f; + character->wheelBack = 4.0f; + character->wheelLeft = 4.0f; + character->wheelRight = 4.0f; + character->posPower = D3DVECTOR(-5.0f, 3.0f, 0.0f); + + m_physics->SetLinMotionX(MO_ADVSPEED, 15.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 10.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 20.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 10.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 20.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 10.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 40.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.5f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.5f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 5.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 5.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 10.0f); + } + + if ( type == OBJECT_APOLLO2 ) // jeep ? + { + m_physics->SetType(TYPE_ROLLING); + + character->wheelFront = 6.0f; + character->wheelBack = 6.0f; + character->wheelLeft = 5.0f; + character->wheelRight = 5.0f; + + m_physics->SetLinMotionX(MO_ADVSPEED, 15.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 10.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 20.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 20.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 2.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 2.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 30.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 10.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 20.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.4f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.4f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 2.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 2.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 4.0f); + } +} + + +// Gestion d'un événement. + +BOOL CMotionVehicle::EventProcess(const Event &event) +{ + CMotion::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + + if ( event.event == EVENT_KEYDOWN ) + { + } + + return TRUE; +} + +// Gestion d'un événement. + +BOOL CMotionVehicle::EventFrame(const Event &event) +{ + D3DMATRIX* mat; + Character* character; + D3DVECTOR pos, angle, floor; + ObjectType type; + float s, a, speedBL, speedBR, speedFL, speedFR, h, a1, a2; + float back, front, dist, radius, limit[2]; + + if ( m_engine->RetPause() ) return TRUE; + if ( !m_engine->IsVisiblePoint(m_object->RetPosition(0)) ) return TRUE; + + type = m_object->RetType(); + + if ( type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEtg || + type == OBJECT_APOLLO2 ) // roues ? + { + s = m_physics->RetLinMotionX(MO_MOTSPEED)*1.0f; + a = m_physics->RetCirMotionY(MO_MOTSPEED)*3.0f; + + if ( type == OBJECT_APOLLO2 ) s *= 0.5f; + + speedBR = -s+a; + speedBL = s+a; + speedFR = -s+a; + speedFL = s+a; + + m_object->SetAngleZ(6, m_object->RetAngleZ(6)+event.rTime*speedBR); // tourne les roues + m_object->SetAngleZ(7, m_object->RetAngleZ(7)+event.rTime*speedBL); + m_object->SetAngleZ(8, m_object->RetAngleZ(8)+event.rTime*speedFR); + m_object->SetAngleZ(9, m_object->RetAngleZ(9)+event.rTime*speedFL); + + if ( s > 0.0f ) + { + m_wheelTurn[0] = -a*0.05f; + m_wheelTurn[1] = -a*0.05f+PI; + m_wheelTurn[2] = a*0.05f; + m_wheelTurn[3] = a*0.05f+PI; + } + else if ( s < 0.0f ) + { + m_wheelTurn[0] = a*0.05f; + m_wheelTurn[1] = a*0.05f+PI; + m_wheelTurn[2] = -a*0.05f; + m_wheelTurn[3] = -a*0.05f+PI; + } + else + { + m_wheelTurn[0] = Abs(a)*0.05f; + m_wheelTurn[1] = -Abs(a)*0.05f+PI; + m_wheelTurn[2] = -Abs(a)*0.05f; + m_wheelTurn[3] = Abs(a)*0.05f+PI; + } + m_object->SetAngleY(6, m_object->RetAngleY(6)+(m_wheelTurn[0]-m_object->RetAngleY(6))*event.rTime*8.0f); + m_object->SetAngleY(7, m_object->RetAngleY(7)+(m_wheelTurn[1]-m_object->RetAngleY(7))*event.rTime*8.0f); + m_object->SetAngleY(8, m_object->RetAngleY(8)+(m_wheelTurn[2]-m_object->RetAngleY(8))*event.rTime*8.0f); + m_object->SetAngleY(9, m_object->RetAngleY(9)+(m_wheelTurn[3]-m_object->RetAngleY(9))*event.rTime*8.0f); + + if ( type == OBJECT_APOLLO2 ) + { + m_object->SetAngleY(10, m_object->RetAngleY(6)+(m_wheelTurn[0]-m_object->RetAngleY(6))*event.rTime*8.0f); + m_object->SetAngleY(11, m_object->RetAngleY(7)+(m_wheelTurn[1]-m_object->RetAngleY(7))*event.rTime*8.0f+PI); + m_object->SetAngleY(12, m_object->RetAngleY(8)+(m_wheelTurn[2]-m_object->RetAngleY(8))*event.rTime*8.0f); + m_object->SetAngleY(13, m_object->RetAngleY(9)+(m_wheelTurn[3]-m_object->RetAngleY(9))*event.rTime*8.0f+PI); + } + + pos = m_object->RetPosition(0); + angle = m_object->RetAngle(0); + if ( pos.x != m_wheelLastPos.x || + pos.y != m_wheelLastPos.y || + pos.z != m_wheelLastPos.z || + angle.x != m_wheelLastAngle.x || + angle.y != m_wheelLastAngle.y || + angle.z != m_wheelLastAngle.z ) + { + m_wheelLastPos = pos; + m_wheelLastAngle = angle; + + if ( type == OBJECT_MOBILEtg ) + { + back = -2.0f; // position roues arrières + front = 3.0f; // position roues avants + dist = 3.0f; // éloignement roues Z + radius = 1.0f; + } + else if ( type == OBJECT_APOLLO2 ) + { + back = -5.75f; // position roues arrières + front = 5.75f; // position roues avants + dist = 5.00f; // éloignement roues Z + radius = 1.65f; + } + else + { + back = -3.0f; // position roues arrières + front = 2.0f; // position roues avants + dist = 3.0f; // éloignement roues Z + radius = 1.0f; + } + + if ( Length(pos, m_engine->RetEyePt()) < 50.0f ) // suspension ? + { + character = m_object->RetCharacter(); + mat = m_object->RetWorldMatrix(0); + + pos.x = -character->wheelBack; // roue arrière droite + pos.z = -character->wheelRight; + pos.y = 0.0f; + pos = Transform(*mat, pos); + h = m_terrain->RetFloorHeight(pos); + if ( h > 0.5f ) h = 0.5f; + if ( h < -0.5f ) h = -0.5f; + pos.x = back; + pos.y = radius-h; + pos.z = -dist; + m_object->SetPosition(6, pos); + if ( type == OBJECT_APOLLO2 ) m_object->SetPosition(10, pos); + + pos.x = -character->wheelBack; // roue arrière gauche + pos.z = character->wheelLeft; + pos.y = 0.0f; + pos = Transform(*mat, pos); + h = m_terrain->RetFloorHeight(pos); + if ( h > 0.5f ) h = 0.5f; + if ( h < -0.5f ) h = -0.5f; + pos.x = back; + pos.y = radius-h; + pos.z = dist; + m_object->SetPosition(7, pos); + if ( type == OBJECT_APOLLO2 ) m_object->SetPosition(11, pos); + + pos.x = character->wheelFront; // roue avant droite + pos.z = -character->wheelRight; + pos.y = 0.0f; + pos = Transform(*mat, pos); + h = m_terrain->RetFloorHeight(pos); + if ( h > 0.5f ) h = 0.5f; + if ( h < -0.5f ) h = -0.5f; + pos.x = front; + pos.y = radius-h; + pos.z = -dist; + m_object->SetPosition(8, pos); + if ( type == OBJECT_APOLLO2 ) m_object->SetPosition(12, pos); + + pos.x = character->wheelFront; // roue avant gauche + pos.z = character->wheelLeft; + pos.y = 0.0f; + pos = Transform(*mat, pos); + h = m_terrain->RetFloorHeight(pos); + if ( h > 0.5f ) h = 0.5f; + if ( h < -0.5f ) h = -0.5f; + pos.x = front; + pos.y = radius-h; + pos.z = dist; + m_object->SetPosition(9, pos); + if ( type == OBJECT_APOLLO2 ) m_object->SetPosition(13, pos); + } + else + { + m_object->SetPosition(6, D3DVECTOR(back, radius, -dist)); + m_object->SetPosition(7, D3DVECTOR(back, radius, dist)); + m_object->SetPosition(8, D3DVECTOR(front, radius, -dist)); + m_object->SetPosition(9, D3DVECTOR(front, radius, dist)); + + if ( type == OBJECT_APOLLO2 ) + { + m_object->SetPosition(10, D3DVECTOR(back, radius, -dist)); + m_object->SetPosition(11, D3DVECTOR(back, radius, dist)); + m_object->SetPosition(12, D3DVECTOR(front, radius, -dist)); + m_object->SetPosition(13, D3DVECTOR(front, radius, dist)); + } + } + } + } + + if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEdr ) // chenilles ? + { + s = m_physics->RetLinMotionX(MO_MOTSPEED)*0.7f; + a = m_physics->RetCirMotionY(MO_MOTSPEED)*2.5f; + + m_posTrackLeft += event.rTime*(s+a); + m_posTrackRight += event.rTime*(s-a); + + UpdateTrackMapping(m_posTrackLeft, m_posTrackRight, type); + + pos = m_object->RetPosition(0); + angle = m_object->RetAngle(0); + if ( pos.x != m_wheelLastPos.x || + pos.y != m_wheelLastPos.y || + pos.z != m_wheelLastPos.z || + angle.x != m_wheelLastAngle.x || + angle.y != m_wheelLastAngle.y || + angle.z != m_wheelLastAngle.z ) + { + m_wheelLastPos = pos; + m_wheelLastAngle = angle; + + if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts ) + { + limit[0] = 8.0f*PI/180.0f; + limit[1] = -12.0f*PI/180.0f; + } + else if ( type == OBJECT_MOBILEsa ) + { + limit[0] = 15.0f*PI/180.0f; + limit[1] = -15.0f*PI/180.0f; + } + else if ( type == OBJECT_MOBILEdr ) + { + limit[0] = 10.0f*PI/180.0f; + limit[1] = -10.0f*PI/180.0f; + } + else + { + limit[0] = 15.0f*PI/180.0f; + limit[1] = -10.0f*PI/180.0f; + } + + if ( Length(pos, m_engine->RetEyePt()) < 50.0f ) // suspension ? + { + character = m_object->RetCharacter(); + mat = m_object->RetWorldMatrix(0); + + pos.x = character->wheelFront; // roue avant droite + pos.z = -character->wheelRight; + pos.y = 0.0f; + pos = Transform(*mat, pos); + a1 = atanf(m_terrain->RetFloorHeight(pos)/character->wheelFront); + + pos.x = -character->wheelBack; // roue arrière droite + pos.z = -character->wheelRight; + pos.y = 0.0f; + pos = Transform(*mat, pos); + a2 = atanf(m_terrain->RetFloorHeight(pos)/character->wheelBack); + + a = (a2-a1)/2.0f; + if ( a > limit[0] ) a = limit[0]; + if ( a < limit[1] ) a = limit[1]; + m_object->SetAngleZ(6, a); + + pos.x = character->wheelFront; // roue avant gauche + pos.z = character->wheelLeft; + pos.y = 0.0f; + pos = Transform(*mat, pos); + a1 = atanf(m_terrain->RetFloorHeight(pos)/character->wheelFront); + + pos.x = -character->wheelBack; // roue arrière gauche + pos.z = character->wheelLeft; + pos.y = 0.0f; + pos = Transform(*mat, pos); + a2 = atanf(m_terrain->RetFloorHeight(pos)/character->wheelBack); + + a = (a2-a1)/2.0f; + if ( a > limit[0] ) a = limit[0]; + if ( a < limit[1] ) a = limit[1]; + m_object->SetAngleZ(7, a); + } + else + { + m_object->SetAngleZ(6, 0.0f); + m_object->SetAngleZ(7, 0.0f); + } + } + } + + if ( type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEdr ) // jouet à clé ? + { + pos = m_posKey; + if ( m_object->RetSelect() && + m_camera->RetType() == CAMERA_ONBOARD ) + { + pos.y += 10.0f; // hors du champ de vision ! + } + m_object->SetPosition(2, pos); + + s = -Abs(m_physics->RetLinMotionX(MO_MOTSPEED)*0.1f); + s += -Abs(m_physics->RetCirMotionY(MO_MOTSPEED)*1.5f); + m_object->SetAngleY(2, m_object->RetAngleY(2)+event.rTime*s); // tourne la clé + } + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEft ) // volant ? + { + EventFrameFly(event); + } + + if ( type == OBJECT_MOBILEia || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEis ) // pattes ? + { + EventFrameInsect(event); + } + + if ( type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEii ) // canon-insecte ? + { + EventFrameCanoni(event); + } + + return TRUE; +} + +// Gestion d'un événement pour un robot volant. + +BOOL CMotionVehicle::EventFrameFly(const Event &event) +{ + D3DMATRIX* mat; + D3DVECTOR pos, angle, paw[3]; + float hope[3], actual, final, h, a; + int i; + + pos = m_object->RetPosition(0); + angle = m_object->RetAngle(0); + if ( m_bFlyFix && + pos.x == m_wheelLastPos.x && + pos.y == m_wheelLastPos.y && + pos.z == m_wheelLastPos.z && + angle.x == m_wheelLastAngle.x && + angle.y == m_wheelLastAngle.y && + angle.z == m_wheelLastAngle.z ) return TRUE; + + m_wheelLastPos = pos; + m_wheelLastAngle = angle; + + if ( m_physics->RetLand() ) // au sol ? + { + mat = m_object->RetWorldMatrix(0); + paw[0] = Transform(*mat, D3DVECTOR( 4.2f, 0.0f, 0.0f)); // avant + paw[1] = Transform(*mat, D3DVECTOR(-3.0f, 0.0f, -3.7f)); // arrière droite + paw[2] = Transform(*mat, D3DVECTOR(-3.0f, 0.0f, 3.7f)); // arrière gauche + + for ( i=0 ; i<3 ; i++ ) + { + h = m_terrain->RetFloorHeight(paw[i]); + a = -atanf(h*0.5f); + if ( a > PI*0.2f ) a = PI*0.2f; + if ( a < -PI*0.2f ) a = -PI*0.2f; + hope[i] = a; + } + } + else // en vol ? + { + hope[0] = 0.0f; // avant + hope[1] = 0.0f; // arrière droite + hope[2] = 0.0f; // arrière gauche + } + + m_bFlyFix = TRUE; + for ( i=0 ; i<3 ; i++ ) + { + actual = m_object->RetAngleZ(6+i); + final = Smooth(actual, hope[i], event.rTime*5.0f); + if ( final != actual ) + { + m_bFlyFix = FALSE; // ça a bougé + m_object->SetAngleZ(6+i, final); + } + } + + return TRUE; +} + +// Gestion d'un événement pour un insecte à pattes. + +BOOL CMotionVehicle::EventFrameInsect(const Event &event) +{ + D3DVECTOR dir; + float s, a, prog, time; + int i, st, nd, action; + BOOL bStop, bOnBoard; + + static int table[] = + { + // x1,y1,z1, x2,y2,z2, x3,y3,z3, // en l'air : + 60,25,0, 60,0,0, 60,-25,0, // t0: cuisses 1..4 + -35,0,0, -35,0,0, -35,0,0, // t0: jambes 1..4 + -65,0,0, -65,0,0, -65,0,0, // t0: pieds 1..4 + // au sol devant : + 30,10,0, 30,-15,0, 30,-40,0, // t1: cuisses 1..4 + -45,0,0, -45,0,0, -45,0,0, // t1: jambes 1..4 + -20,0,0, -20,0,0, -20,0,0, // t1: pieds 1..4 + // au sol derrière : + 35,40,0, 40,15,0, 40,-10,0, // t2: cuisses 1..4 + -35,0,0, -35,0,0, -35,0,0, // t2: jambes 1..4 + -50,0,0, -65,0,0, -65,0,0, // t2: pieds 1..4 + // stoppe : + 35,35,0, 40,10,0, 40,-15,0, // s0: cuisses 1..4 + -35,0,0, -35,0,0, -35,0,0, // s0: jambes 1..4 + -50,0,0, -65,0,0, -65,0,0, // s0: pieds 1..4 + }; + + bOnBoard = FALSE; + if ( m_object->RetSelect() && + m_camera->RetType() == CAMERA_ONBOARD ) + { + bOnBoard = TRUE; + } + + s = m_physics->RetLinMotionX(MO_MOTSPEED)*1.5f; + a = Abs(m_physics->RetCirMotionY(MO_MOTSPEED)*2.0f); + + if ( s == 0.0f && a != 0.0f ) a *= 1.5f; + + m_armTimeAbs += event.rTime; + m_armMember += (s+a)*event.rTime*0.15f; + + bStop = ( a == 0.0f && s == 0.0f ); // à l'arrêt ? + + action = 0; // marche + if ( s == 0.0f && a == 0.0f ) + { + action = 3; // stop + } + + if ( bStop ) + { + prog = Mod(m_armTimeAbs, 2.0f)/10.0f; + a = Mod(m_armMember, 1.0f); + a = (prog-a)*event.rTime*2.0f; // vient gentiment à position stop + m_armMember += a; + } + + if ( m_object->RetRuin() ) // brûle ou explose ? + { + action = 3; + } + + for ( i=0 ; i<6 ; i++ ) // les 6 pattes + { + if ( action != 0 ) // action spéciale en cours ? + { + st = 3*3*3*action + (i%3)*3; + nd = st; + time = event.rTime*5.0f; + } + else + { + if ( i < 3 ) prog = Mod(m_armMember+(2.0f-(i%3))*0.33f+0.0f, 1.0f); + else prog = Mod(m_armMember+(2.0f-(i%3))*0.33f+0.3f, 1.0f); + if ( prog < 0.33f ) // t0..t1 ? + { + prog = prog/0.33f; // 0..1 + st = 0; // index start + nd = 1; // index end + } + else if ( prog < 0.67f ) // t1..t2 ? + { + prog = (prog-0.33f)/0.33f; // 0..1 + st = 1; // index start + nd = 2; // index end + } + else // t2..t0 ? + { + prog = (prog-0.67f)/0.33f; // 0..1 + st = 2; // index start + nd = 0; // index end + } + st = 3*3*3*action + st*3*3*3 + (i%3)*3; + nd = 3*3*3*action + nd*3*3*3 + (i%3)*3; + + // De moins en moins mou ... + time = event.rTime*20.0f; + } + + if ( i < 3 ) // patte droite (1..3) ? + { + m_object->SetAngleX(6+3*i+0, Smooth(m_object->RetAngleX(6+3*i+0), Prop(table[st+ 0], table[nd+ 0], prog), time)); + m_object->SetAngleY(6+3*i+0, Smooth(m_object->RetAngleY(6+3*i+0), Prop(table[st+ 1], table[nd+ 1], prog), time)); + m_object->SetAngleZ(6+3*i+0, Smooth(m_object->RetAngleZ(6+3*i+0), Prop(table[st+ 2], table[nd+ 2], prog), time)); + m_object->SetAngleX(6+3*i+1, Smooth(m_object->RetAngleX(6+3*i+1), Prop(table[st+ 9], table[nd+ 9], prog), time)); + m_object->SetAngleY(6+3*i+1, Smooth(m_object->RetAngleY(6+3*i+1), Prop(table[st+10], table[nd+10], prog), time)); + m_object->SetAngleZ(6+3*i+1, Smooth(m_object->RetAngleZ(6+3*i+1), Prop(table[st+11], table[nd+11], prog), time)); + m_object->SetAngleX(6+3*i+2, Smooth(m_object->RetAngleX(6+3*i+2), Prop(table[st+18], table[nd+18], prog), time)); + m_object->SetAngleY(6+3*i+2, Smooth(m_object->RetAngleY(6+3*i+2), Prop(table[st+19], table[nd+19], prog), time)); + m_object->SetAngleZ(6+3*i+2, Smooth(m_object->RetAngleZ(6+3*i+2), Prop(table[st+20], table[nd+20], prog), time)); + } + else // patte gauche (4..6) ? + { + m_object->SetAngleX(6+3*i+0, Smooth(m_object->RetAngleX(6+3*i+0), Prop(-table[st+ 0], -table[nd+ 0], prog), time)); + m_object->SetAngleY(6+3*i+0, Smooth(m_object->RetAngleY(6+3*i+0), Prop(-table[st+ 1], -table[nd+ 1], prog), time)); + m_object->SetAngleZ(6+3*i+0, Smooth(m_object->RetAngleZ(6+3*i+0), Prop( table[st+ 2], table[nd+ 2], prog), time)); + m_object->SetAngleX(6+3*i+1, Smooth(m_object->RetAngleX(6+3*i+1), Prop(-table[st+ 9], -table[nd+ 9], prog), time)); + m_object->SetAngleY(6+3*i+1, Smooth(m_object->RetAngleY(6+3*i+1), Prop(-table[st+10], -table[nd+10], prog), time)); + m_object->SetAngleZ(6+3*i+1, Smooth(m_object->RetAngleZ(6+3*i+1), Prop( table[st+11], table[nd+11], prog), time)); + m_object->SetAngleX(6+3*i+2, Smooth(m_object->RetAngleX(6+3*i+2), Prop(-table[st+18], -table[nd+18], prog), time)); + m_object->SetAngleY(6+3*i+2, Smooth(m_object->RetAngleY(6+3*i+2), Prop(-table[st+19], -table[nd+19], prog), time)); + m_object->SetAngleZ(6+3*i+2, Smooth(m_object->RetAngleZ(6+3*i+2), Prop( table[st+20], table[nd+20], prog), time)); + } + } + + if ( bStop ) + { + } + else + { + a = Mod(m_armMember, 1.0f); + if ( a < 0.5f ) a = -1.0f+4.0f*a; // -1..1 + else a = 3.0f-4.0f*a; // 1..-1 + dir.x = sinf(a)*0.05f; + + s = Mod(m_armMember/2.0f, 1.0f); + if ( s < 0.5f ) s = -1.0f+4.0f*s; // -1..1 + else s = 3.0f-4.0f*s; // 1..-1 + dir.z = sinf(s)*0.1f; + + dir.y = 0.0f; + + if ( bOnBoard ) dir *= 0.6f; + SetInclinaison(dir); + } + + return TRUE; +} + +// Gestion d'un événement pour un canon-insecte. + +BOOL CMotionVehicle::EventFrameCanoni(const Event &event) +{ + CObject* power; + D3DVECTOR pos, speed; + FPOINT dim; + float zoom, angle, energy, factor; + BOOL bOnBoard = FALSE; + + m_canonTime += event.rTime; + + if ( m_object->RetSelect() && + m_camera->RetType() == CAMERA_ONBOARD ) + { + bOnBoard = TRUE; + } + + power = m_object->RetPower(); + if ( power == 0 ) + { + energy = 0.0f; + } + else + { + energy = power->RetEnergy(); + } + if ( energy == 0.0f ) return TRUE; + + factor = 0.5f+energy*0.5f; + if ( bOnBoard ) factor *= 0.8f; + + zoom = 1.3f+ + sinf(m_canonTime*PI*0.31f)*0.10f+ + sinf(m_canonTime*PI*0.52f)*0.08f+ + sinf(m_canonTime*PI*1.53f)*0.05f; + zoom *= factor; + m_object->SetZoomY(2, zoom); + + zoom = 1.0f+ + sinf(m_canonTime*PI*0.27f)*0.07f+ + sinf(m_canonTime*PI*0.62f)*0.06f+ + sinf(m_canonTime*PI*1.73f)*0.03f; + zoom *= factor; + m_object->SetZoomZ(2, zoom); + + angle = sinf(m_canonTime*1.0f)*0.10f+ + sinf(m_canonTime*1.3f)*0.15f+ + sinf(m_canonTime*2.7f)*0.05f; + m_object->SetAngleX(2, angle); + +#if 0 + m_lastTimeCanon -= event.rTime; + if ( m_lastTimeCanon <= 0.0f ) + { + m_lastTimeCanon = m_engine->ParticuleAdapt(0.5f+Rand()*0.5f); + + pos = m_object->RetPosition(0); + pos.y += 8.0f; + speed.y = 7.0f+Rand()*3.0f; + speed.x = (Rand()-0.5f)*2.0f; + speed.z = 2.0f+Rand()*2.0f; + if ( Rand() < 0.5f ) speed.z = -speed.z; + mat = m_object->RetRotateMatrix(0); + speed = Transform(*mat, speed); + dim.x = Rand()*0.1f+0.1f; + if ( bOnBoard ) dim.x *= 0.4f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIORGANIC2, 2.0f, 10.0f); + } +#endif + + return TRUE; +} + + +// Met à jour le mapping de la texture des chenilles. + +void CMotionVehicle::UpdateTrackMapping(float left, float right, ObjectType type) +{ + D3DMATERIAL7 mat; + float limit[4]; + int rRank, lRank, i; + + ZeroMemory( &mat, sizeof(D3DMATERIAL7) ); + mat.diffuse.r = 1.0f; + mat.diffuse.g = 1.0f; + mat.diffuse.b = 1.0f; // blanc + mat.ambient.r = 0.5f; + mat.ambient.g = 0.5f; + mat.ambient.b = 0.5f; + + rRank = m_object->RetObjectRank(6); + lRank = m_object->RetObjectRank(7); + + + if ( type == OBJECT_MOBILEdr ) + { + limit[0] = 0.0f; + limit[1] = 1000000.0f; + limit[2] = limit[1]; + limit[3] = m_engine->RetLimitLOD(1); + + m_engine->TrackTextureMapping(rRank, mat, D3DSTATEPART1, "drawer.tga", "", + limit[0], limit[1], D3DMAPPINGX, + right, 1.0f, 8.0f, 192.0f, 256.0f); + + m_engine->TrackTextureMapping(lRank, mat, D3DSTATEPART2, "drawer.tga", "", + limit[0], limit[1], D3DMAPPINGX, + left, 1.0f, 8.0f, 192.0f, 256.0f); + } + else + { + limit[0] = 0.0f; + limit[1] = m_engine->RetLimitLOD(0); + limit[2] = limit[1]; + limit[3] = m_engine->RetLimitLOD(1); + + for ( i=0 ; i<2 ; i++ ) + { + m_engine->TrackTextureMapping(rRank, mat, D3DSTATEPART1, "lemt.tga", "", + limit[i*2+0], limit[i*2+1], D3DMAPPINGX, + right, 1.0f, 8.0f, 192.0f, 256.0f); + + m_engine->TrackTextureMapping(lRank, mat, D3DSTATEPART2, "lemt.tga", "", + limit[i*2+0], limit[i*2+1], D3DMAPPINGX, + left, 1.0f, 8.0f, 192.0f, 256.0f); + } + } + +} + + + +// Gestion de l'état du crayon du robot dessinateur. + +BOOL CMotionVehicle::RetTraceDown() +{ + return m_bTraceDown; +} + +void CMotionVehicle::SetTraceDown(BOOL bDown) +{ + m_bTraceDown = bDown; +} + +int CMotionVehicle::RetTraceColor() +{ + return m_traceColor; +} + +void CMotionVehicle::SetTraceColor(int color) +{ + m_traceColor = color; +} + +float CMotionVehicle::RetTraceWidth() +{ + return m_traceWidth; +} + +void CMotionVehicle::SetTraceWidth(float width) +{ + m_traceWidth = width; +} + + diff --git a/src/motionvehicle.h b/src/motionvehicle.h new file mode 100644 index 00000000..f7bbf0a9 --- /dev/null +++ b/src/motionvehicle.h @@ -0,0 +1,63 @@ +// motionvehicle.h + +#ifndef _MOTIONVEHICLE_H_ +#define _MOTIONVEHICLE_H_ + + +class CInstanceManager; +class CEngine; +class CLight; +class CParticule; +class CTerrain; +class CCamera; +class CBrain; +class CPhysics; +class CObject; + + +class CMotionVehicle : public CMotion +{ +public: + CMotionVehicle(CInstanceManager* iMan, CObject* object); + ~CMotionVehicle(); + + void DeleteObject(BOOL bAll=FALSE); + BOOL Create(D3DVECTOR pos, float angle, ObjectType type, float power); + BOOL EventProcess(const Event &event); + + BOOL RetTraceDown(); + void SetTraceDown(BOOL bDown); + int RetTraceColor(); + void SetTraceColor(int color); + float RetTraceWidth(); + void SetTraceWidth(float width); + +protected: + void CreatePhysics(ObjectType type); + BOOL EventFrame(const Event &event); + BOOL EventFrameFly(const Event &event); + BOOL EventFrameInsect(const Event &event); + BOOL EventFrameCanoni(const Event &event); + void UpdateTrackMapping(float left, float right, ObjectType type); + +protected: + float m_wheelTurn[4]; + float m_flyPaw[3]; + float m_posTrackLeft; + float m_posTrackRight; + int m_partiReactor; + float m_armTimeAbs; + float m_armMember; + float m_canonTime; + float m_lastTimeCanon; + D3DVECTOR m_wheelLastPos; + D3DVECTOR m_wheelLastAngle; + D3DVECTOR m_posKey; + BOOL m_bFlyFix; + BOOL m_bTraceDown; + int m_traceColor; + float m_traceWidth; +}; + + +#endif //_MOTIONVEHICLE_H_ diff --git a/src/motionworm.cpp b/src/motionworm.cpp new file mode 100644 index 00000000..aa575d9d --- /dev/null +++ b/src/motionworm.cpp @@ -0,0 +1,365 @@ +// motionworm.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "modfile.h" +#include "sound.h" +#include "motion.h" +#include "motionworm.h" + + + +#define START_TIME 1000.0f // début du temps relatif +#define TIME_UPDOWN 2.0f // temps pour up/down +#define DOWN_ALTITUDE 3.0f // distance sous terre +#define WORM_PART 7 // nb de parties d'un ver + + + +// Constructeur de l'objet. + +CMotionWorm::CMotionWorm(CInstanceManager* iMan, CObject* object) + : CMotion(iMan, object) +{ + CMotion::CMotion(iMan, object); + + m_timeUp = 18.0f; + m_timeDown = 18.0f; + m_armMember = START_TIME; + m_armTimeAbs = START_TIME; + m_armTimeMarch = START_TIME; + m_armTimeAction = START_TIME; + m_armTimeIndex = 0; + m_armPartIndex = 0; + m_armMemberIndex = 0; + m_armLinSpeed = 0.0f; + m_armCirSpeed = 0.0f; + m_armLastAction = -1; + m_specAction = -1; + m_lastParticule = 0.0f; + m_bArmStop = FALSE; +} + +// Destructeur de l'objet. + +CMotionWorm::~CMotionWorm() +{ +} + + +// Supprime un objet. + +void CMotionWorm::DeleteObject(BOOL bAll) +{ +} + + +// Crée un véhicule roulant quelconque posé sur le sol. + +BOOL CMotionWorm::Create(D3DVECTOR pos, float angle, ObjectType type, + float power) +{ + CModFile* pModFile; + int rank, i; + float px; + + if ( m_engine->RetRestCreate() < 2+WORM_PART+1 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + m_object->SetType(type); + + // Crée la base principale. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEVEHICULE); // c'est un objet mobile + m_object->SetObjectRank(0, rank); + pModFile->ReadModel("objects\\worm0.mod"); // n'existe pas exprès ! + pModFile->CreateEngineObject(rank); + m_object->SetPosition(0, pos); + m_object->SetAngleY(0, angle); + + // Un véhicule doit avoir obligatoirement une sphère de + // collision avec un centre (0;y;0) (voir GetCrashSphere). + m_object->CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 4.0f, SOUND_BOUM, 0.20f); + m_object->SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 5.0f); + + px = 1.0f+WORM_PART/2; + + // Crée la tête. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(1, rank); + m_object->SetObjectParent(1, 0); + pModFile->ReadModel("objects\\worm1.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(1, D3DVECTOR(px, 0.0f, 0.0f)); + px -= 1.0f; + + // Crée le corps. + for ( i=0 ; iCreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2+i, rank); + m_object->SetObjectParent(2+i, 0); + pModFile->ReadModel("objects\\worm2.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2+i, D3DVECTOR(px, 0.0f, 0.0f)); + px -= 1.0f; + } + + // Crée la queue. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + m_object->SetObjectRank(2+WORM_PART, rank); + m_object->SetObjectParent(2+WORM_PART, 0); + pModFile->ReadModel("objects\\worm3.mod"); + pModFile->CreateEngineObject(rank); + m_object->SetPosition(2+WORM_PART, D3DVECTOR(px, 0.0f, 0.0f)); + + m_object->CreateShadowCircle(0.0f, 1.0f, D3DSHADOWWORM); + + CreatePhysics(); + m_object->SetFloorHeight(0.0f); + + pos = m_object->RetPosition(0); + m_object->SetPosition(0, pos); // pour afficher les ombres tout de suite + + m_engine->LoadAllTexture(); + + delete pModFile; + return TRUE; +} + +// Crée la physique de l'objet. + +void CMotionWorm::CreatePhysics() +{ + Character* character; + + m_physics->SetType(TYPE_ROLLING); + + character = m_object->RetCharacter(); + character->wheelFront = 10.0f; + character->wheelBack = 10.0f; + character->wheelLeft = 2.0f; + character->wheelRight = 2.0f; + character->height = -0.2f; + + m_physics->SetLinMotionX(MO_ADVSPEED, 3.0f); + m_physics->SetLinMotionX(MO_RECSPEED, 3.0f); + m_physics->SetLinMotionX(MO_ADVACCEL, 10.0f); + m_physics->SetLinMotionX(MO_RECACCEL, 10.0f); + m_physics->SetLinMotionX(MO_STOACCEL, 40.0f); + m_physics->SetLinMotionX(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionZ(MO_TERSLIDE, 5.0f); + m_physics->SetLinMotionX(MO_TERFORCE, 5.0f); + m_physics->SetLinMotionZ(MO_TERFORCE, 5.0f); + m_physics->SetLinMotionZ(MO_MOTACCEL, 40.0f); + + m_physics->SetCirMotionY(MO_ADVSPEED, 0.2f*PI); + m_physics->SetCirMotionY(MO_RECSPEED, 0.2f*PI); + m_physics->SetCirMotionY(MO_ADVACCEL, 10.0f); + m_physics->SetCirMotionY(MO_RECACCEL, 10.0f); + m_physics->SetCirMotionY(MO_STOACCEL, 20.0f); +} + + + +// Spécifie un paramètre spécial. + +BOOL CMotionWorm::SetParam(int rank, float value) +{ + if ( rank == 0 ) + { + m_timeDown = value; + return TRUE; + } + + if ( rank == 1 ) + { + m_timeUp = value; + return TRUE; + } + + return FALSE; +} + +float CMotionWorm::RetParam(int rank) +{ + if ( rank == 0 ) return m_timeDown; + if ( rank == 1 ) return m_timeUp; + return 0.0f; +} + + + +// Gestion d'un événement. + +BOOL CMotionWorm::EventProcess(const Event &event) +{ + CMotion::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + + if ( event.event == EVENT_KEYDOWN ) + { + } + + return TRUE; +} + +// Gestion d'un événement. + +BOOL CMotionWorm::EventFrame(const Event &event) +{ + D3DMATRIX* mat; + D3DVECTOR pos, p, angle, speed; + FPOINT center, pp, dim; + float height[WORM_PART+2]; + float floor, a, s, px, curve, phase, h, zoom, radius; + int i, under; + + if ( m_engine->RetPause() ) return TRUE; + + s = m_physics->RetLinMotionX(MO_MOTSPEED)/m_physics->RetLinMotionX(MO_ADVSPEED); + a = m_physics->RetCirMotionY(MO_MOTSPEED)/m_physics->RetCirMotionY(MO_ADVSPEED); + + if ( s == 0.0f && a != 0.0f ) s = a; + + m_armLinSpeed += (s-m_armLinSpeed)*event.rTime*3.0f; + m_armCirSpeed += (a-m_armCirSpeed)*event.rTime*1.5f; + + m_armTimeAbs += event.rTime; + m_armTimeMarch += event.rTime*m_armLinSpeed; + + under = 0; // aucun morceau sous terre + for ( i=0 ; iRetBurn() ) // brûle ? + { + h = 0.0f; // reste sur terre + } + h += 0.3f; + height[i] = h; + } + m_object->SetVisible(under!=WORM_PART+2); + + if ( !m_engine->IsVisiblePoint(m_object->RetPosition(0)) ) return TRUE; + + pos = m_object->RetPosition(0); + floor = m_terrain->RetFloorLevel(pos, TRUE); + + mat = m_object->RetWorldMatrix(0); + + px = 1.0f+WORM_PART/2; + for ( i=0 ; iSetObjectShadowRadius(m_object->RetObjectRank(0), radius); + + pos.x = px+ sinf(m_armTimeMarch*4.0f+0.5f*i)*0.6f; + pos.y = height[i]+sinf(m_armTimeMarch*4.0f+0.5f*i)*0.2f*m_armLinSpeed; + pos.y += sinf(m_armTimeAbs *1.3f+0.2f*i)*0.1f; + pos.z = sinf(m_armTimeAbs *2.0f+0.7f*i)*0.2f; + + curve = ((float)i-(WORM_PART+2)/2)*m_armCirSpeed*0.1f; + center.x = 0.0f; + center.y = 0.0f; + pp.x = pos.x; + pp.y = pos.z; + pp = RotatePoint(center, curve, pp); + pos.x = pp.x; + pos.z = pp.y; + + p = Transform(*mat, pos); + pos.y += m_terrain->RetFloorLevel(p, TRUE)-floor; + m_object->SetPosition(i+1, pos); + + zoom = Mod(m_armTimeAbs*0.5f+100.0f-i*0.1f, 2.0f); + if ( zoom > 1.0f ) zoom = 2.0f-zoom; + zoom *= 1.6f; + if ( zoom < 1.0f ) zoom = 1.0f; + m_object->SetZoomY(i+1, 0.2f+zoom*0.8f); + m_object->SetZoomZ(i+1, zoom); + + if ( height[i] >= -1.0f && height[i] < -0.2f && + m_lastParticule+m_engine->ParticuleAdapt(0.2f) <= m_armTimeMarch ) + { + m_lastParticule = m_armTimeMarch; + + pos = p; + pos.y += -height[i]; + pos.x += (Rand()-0.5f)*4.0f; + pos.z += (Rand()-0.5f)*4.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = Rand()*2.0f+1.5f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f); + } + + px -= 1.0f; + } + + for ( i=0 ; iRetPosition(i+2); + pos -= m_object->RetPosition(i+1); + + angle.z = -RotateAngle(Length(pos.x, pos.z), pos.y); + angle.y = PI-RotateAngle(pos.x, pos.z); + angle.x = 0.0f; + m_object->SetAngle(i+1, angle); + + if ( i == WORM_PART ) + { + m_object->SetAngle(i+2, angle); + } + } + + return TRUE; +} + + diff --git a/src/motionworm.h b/src/motionworm.h new file mode 100644 index 00000000..a464367b --- /dev/null +++ b/src/motionworm.h @@ -0,0 +1,56 @@ +// motionworm.h + +#ifndef _MOTIONWORM_H_ +#define _MOTIONWORM_H_ + + +class CInstanceManager; +class CEngine; +class CLight; +class CParticule; +class CTerrain; +class CCamera; +class CBrain; +class CPhysics; +class CObject; + + +class CMotionWorm : public CMotion +{ +public: + CMotionWorm(CInstanceManager* iMan, CObject* object); + ~CMotionWorm(); + + void DeleteObject(BOOL bAll=FALSE); + BOOL Create(D3DVECTOR pos, float angle, ObjectType type, float power); + BOOL EventProcess(const Event &event); + + BOOL SetParam(int rank, float value); + float RetParam(int rank); + +protected: + void CreatePhysics(); + BOOL EventFrame(const Event &event); + +protected: + float m_timeUp; + float m_timeDown; + float m_armMember; + float m_armTimeAbs; + float m_armTimeMarch; + float m_armTimeAction; + short m_armAngles[3*3*3*3*10]; + int m_armTimeIndex; + int m_armPartIndex; + int m_armMemberIndex; + int m_armLastAction; + float m_armLinSpeed; + float m_armCirSpeed; + int m_specAction; + float m_specTime; + BOOL m_bArmStop; + float m_lastParticule; +}; + + +#endif //_MOTIONWORM_H_ diff --git a/src/object.cpp b/src/object.cpp new file mode 100644 index 00000000..6eda9f72 --- /dev/null +++ b/src/object.cpp @@ -0,0 +1,7599 @@ +// object.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "cbot/cbotdll.h" +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "math3d.h" +#include "mainmovie.h" +#include "robotmain.h" +#include "light.h" +#include "terrain.h" +#include "water.h" +#include "blitz.h" +#include "camera.h" +#include "particule.h" +#include "physics.h" +#include "brain.h" +#include "motion.h" +#include "motionhuman.h" +#include "motiontoto.h" +#include "motionvehicle.h" +#include "motionmother.h" +#include "motionant.h" +#include "motionspider.h" +#include "motionbee.h" +#include "motionworm.h" +#include "modfile.h" +#include "auto.h" +#include "autobase.h" +#include "autoportico.h" +#include "autoderrick.h" +#include "autofactory.h" +#include "autorepair.h" +#include "autodestroyer.h" +#include "autostation.h" +#include "autoenergy.h" +#include "autoconvert.h" +#include "autotower.h" +#include "autoresearch.h" +#include "autolabo.h" +#include "autonuclear.h" +#include "autoradar.h" +#include "autoegg.h" +#include "autonest.h" +#include "autoroot.h" +#include "autoflag.h" +#include "autoinfo.h" +#include "autojostle.h" +#include "autopara.h" +#include "autosafe.h" +#include "autohuston.h" +#include "automush.h" +#include "autokid.h" +#include "task.h" +#include "pyro.h" +#include "displaytext.h" +#include "cmdtoken.h" +#include "cbottoken.h" +#include "sound.h" +#include "object.h" + + + +#define ADJUST_ONBOARD FALSE // TRUE -> ajuste la caméra ONBOARD +#define ADJUST_ARM FALSE // TRUE -> ajuste le bras manipulateur +#define VIRUS_DELAY 60.0f // durée d'infection d'un virus +#define LOSS_SHIELD 0.24f // perte du bouclier par coup +#define LOSS_SHIELD_H 0.10f // perte du bouclier pour l'homme +#define LOSS_SHIELD_M 0.02f // perte du bouclier pour la pondeuse + +#if ADJUST_ONBOARD +static float debug_x = 0.0f; +static float debug_y = 0.0f; +static float debug_z = 0.0f; +#endif + +#if ADJUST_ARM +static float debug_arm1 = 0.0f; +static float debug_arm2 = 0.0f; +static float debug_arm3 = 0.0f; +#endif + + + + +// Met à jour la classe Object. + +void uObject(CBotVar* botThis, void* user) +{ + CObject* object = (CObject*)user; + CObject* power; + CObject* fret; + CPhysics* physics; + CBotVar *pVar, *pSub; + ObjectType type; + D3DVECTOR pos; + float value; + int iValue; + + if ( object == 0 ) return; + + physics = object->RetPhysics(); + + // Met à jour le type de l'objet. + pVar = botThis->GivItemList(); // "category" + type = object->RetType(); + pVar->SetValInt(type, object->RetName()); + + // Met à jour la position de l'objet. + pVar = pVar->GivNext(); // "position" + if ( object->RetTruck() == 0 ) + { + pos = object->RetPosition(0); + pos.y -= object->RetWaterLevel(); // relatif au niveau de la mer ! + pSub = pVar->GivItemList(); // "x" + pSub->SetValFloat(pos.x/g_unit); + pSub = pSub->GivNext(); // "y" + pSub->SetValFloat(pos.z/g_unit); + pSub = pSub->GivNext(); // "z" + pSub->SetValFloat(pos.y/g_unit); + } + else // objet transporté ? + { + pSub = pVar->GivItemList(); // "x" + pSub->SetInit(IS_NAN); + pSub = pSub->GivNext(); // "y" + pSub->SetInit(IS_NAN); + pSub = pSub->GivNext(); // "z" + pSub->SetInit(IS_NAN); + } + + // Met à jour l'angle. + pos = object->RetAngle(0); + pos += object->RetInclinaison(); + pVar = pVar->GivNext(); // "orientation" + pVar->SetValFloat(360.0f-Mod(pos.y*180.0f/PI, 360.0f)); + pVar = pVar->GivNext(); // "pitch" + pVar->SetValFloat(pos.z*180.0f/PI); + pVar = pVar->GivNext(); // "roll" + pVar->SetValFloat(pos.x*180.0f/PI); + + // Met à jour le niveau d'énergie de l'objet. + pVar = pVar->GivNext(); // "energyLevel" + value = object->RetEnergy(); + pVar->SetValFloat(value); + + // Met à jour le niveau du bouclier de l'objet. + pVar = pVar->GivNext(); // "shieldLevel" + value = object->RetShield(); + pVar->SetValFloat(value); + + // Met à jour la température du bouclier. + pVar = pVar->GivNext(); // "temperature" + if ( physics == 0 ) value = 0.0f; + else value = 1.0f-physics->RetReactorRange(); + pVar->SetValFloat(value); + + // Met à jour la hauteur au-dessus du sol. + pVar = pVar->GivNext(); // "altitude" + if ( physics == 0 ) value = 0.0f; + else value = physics->RetFloorHeight(); + pVar->SetValFloat(value/g_unit); + + // Met à jour le temps de l'objet. + pVar = pVar->GivNext(); // "lifeTime" + value = object->RetAbsTime(); + pVar->SetValFloat(value); + + // Met à jour la matière de l'objet. + pVar = pVar->GivNext(); // "material" + iValue = object->RetMaterial(); + pVar->SetValInt(iValue); + + // Met à jour le type de la pile. + pVar = pVar->GivNext(); // "energyCell" + power = object->RetPower(); + if ( power == 0 ) pVar->SetPointer(0); + else pVar->SetPointer(power->RetBotVar()); + + // Met à jour le type de l'objet transporté. + pVar = pVar->GivNext(); // "load" + fret = object->RetFret(); + if ( fret == 0 ) pVar->SetPointer(0); + else pVar->SetPointer(fret->RetBotVar()); +} + + + + +// Constructeur de l'objet. + +CObject::CObject(CInstanceManager* iMan) +{ + int i; + + m_iMan = iMan; + m_iMan->AddInstance(CLASS_OBJECT, this, 500); + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_light = (CLight*)m_iMan->SearchInstance(CLASS_LIGHT); + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + m_displayText = (CDisplayText*)m_iMan->SearchInstance(CLASS_DISPLAYTEXT); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + m_physics = 0; + m_brain = 0; + m_motion = 0; + m_auto = 0; + m_runScript = 0; + + m_type = OBJECT_FIX; + m_id = ++g_id; + m_option = 0; + m_name[0] = 0; + m_partiReactor = -1; + m_shadowLight = -1; + m_effectLight = -1; + m_linVibration = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_cirVibration = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_inclinaison = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_lastParticule = 0.0f; + + m_power = 0; + m_fret = 0; + m_truck = 0; + m_truckLink = 0; + m_energy = 1.0f; + m_capacity = 1.0f; + m_shield = 1.0f; + m_range = 0.0f; + m_transparency = 0.0f; + m_lastEnergy = 999.9f; + m_bHilite = FALSE; + m_bSelect = FALSE; + m_bSelectable = TRUE; + m_bCheckToken = TRUE; + m_bVisible = TRUE; + m_bEnable = TRUE; + m_bGadget = FALSE; + m_bProxyActivate = FALSE; + m_bTrainer = FALSE; + m_bToy = FALSE; + m_bManual = FALSE; + m_bFixed = FALSE; + m_bClip = TRUE; + m_bShowLimit = FALSE; + m_showLimitRadius = 0.0f; + m_aTime = 0.0f; + m_shotTime = 0.0f; + m_bVirusMode = FALSE; + m_virusTime = 0.0f; + m_lastVirusParticule = 0.0f; + m_totalDesectList = 0; + m_bLock = FALSE; + m_bExplo = FALSE; + m_bCargo = FALSE; + m_bBurn = FALSE; + m_bDead = FALSE; + m_bFlat = FALSE; + m_gunGoalV = 0.0f; + m_gunGoalH = 0.0f; + m_shieldRadius = 0.0f; + m_defRank = -1; + m_magnifyDamage = 1.0f; + m_proxyDistance = 60.0f; + m_param = 0.0f; + + ZeroMemory(&m_character, sizeof(Character)); + m_character.wheelFront = 1.0f; + m_character.wheelBack = 1.0f; + m_character.wheelLeft = 1.0f; + m_character.wheelRight = 1.0f; + + m_resetCap = RESET_NONE; + m_bResetBusy = FALSE; + m_resetPosition = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_resetAngle = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_resetRun = -1; + + m_cameraType = CAMERA_BACK; + m_cameraDist = 50.0f; + m_bCameraLock = FALSE; + + m_infoTotal = 0; + m_infoReturn = NAN; + m_bInfoUpdate = FALSE; + + for ( i=0 ; iAddUpdateFunc(uObject); + } + + m_botVar = CBotVar::Create("", CBotTypResult(CBotTypClass, "object")); + m_botVar->SetUserPtr(this); + m_botVar->SetIdent(m_id); +} + +// Destructeur de l'objet. + +CObject::~CObject() +{ + if ( m_botVar != 0 ) + { + m_botVar->SetUserPtr(OBJECTDELETED); + delete m_botVar; + } + + delete m_physics; + delete m_brain; + delete m_motion; + delete m_auto; + + m_iMan->DeleteInstance(CLASS_OBJECT, this); +} + + +// Supprime un objet. +// Si bAll=TRUE, on n'arrange rien, car tous les objets de la +// scène sont détruits rapidement ! + +void CObject::DeleteObject(BOOL bAll) +{ + CObject* pObj; + CPyro* pPyro; + int i; + + if ( m_botVar != 0 ) + { + m_botVar->SetUserPtr(OBJECTDELETED); + } + + if ( m_camera->RetObject() == this ) + { + m_camera->SetObject(0); + } + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + pObj->DeleteDeselList(this); + } + + if ( !bAll ) + { +#if 0 + type = m_camera->RetType(); + if ( (type == CAMERA_BACK || + type == CAMERA_FIX || + type == CAMERA_EXPLO || + type == CAMERA_ONBOARD) && + m_camera->RetObject() == this ) + { + pObj = m_main->SearchNearest(RetPosition(0), this); + if ( pObj == 0 ) + { + m_camera->SetObject(0); + m_camera->SetType(CAMERA_FREE); + } + else + { + m_camera->SetObject(pObj); + m_camera->SetType(CAMERA_BACK); + } + } +#endif + for ( i=0 ; i<1000000 ; i++ ) + { + pPyro = (CPyro*)m_iMan->SearchInstance(CLASS_PYRO, i); + if ( pPyro == 0 ) break; + + pPyro->CutObjectLink(this); // l'objet n'existe plus + } + + if ( m_bSelect ) + { + SetSelect(FALSE); + } + + if ( m_type == OBJECT_BASE || + m_type == OBJECT_FACTORY || + m_type == OBJECT_REPAIR || + m_type == OBJECT_DESTROYER|| + m_type == OBJECT_DERRICK || + m_type == OBJECT_STATION || + m_type == OBJECT_CONVERT || + m_type == OBJECT_TOWER || + m_type == OBJECT_RESEARCH || + m_type == OBJECT_RADAR || + m_type == OBJECT_INFO || + m_type == OBJECT_ENERGY || + m_type == OBJECT_LABO || + m_type == OBJECT_NUCLEAR || + m_type == OBJECT_PARA || + m_type == OBJECT_SAFE || + m_type == OBJECT_HUSTON || + m_type == OBJECT_START || + m_type == OBJECT_END ) // batiment? + { + m_terrain->DeleteBuildingLevel(RetPosition(0)); // applanit le terrain + } + } + + m_type = OBJECT_NULL; // objet invalide jusqu'à destruction complète + + if ( m_partiReactor != -1 ) + { + m_particule->DeleteParticule(m_partiReactor); + m_partiReactor = -1; + } + + if ( m_shadowLight != -1 ) + { + m_light->DeleteLight(m_shadowLight); + m_shadowLight = -1; + } + + if ( m_effectLight != -1 ) + { + m_light->DeleteLight(m_effectLight); + m_effectLight = -1; + } + + if ( m_physics != 0 ) + { + m_physics->DeleteObject(bAll); + } + + if ( m_brain != 0 ) + { + m_brain->DeleteObject(bAll); + } + + if ( m_motion != 0 ) + { + m_motion->DeleteObject(bAll); + } + + if ( m_auto != 0 ) + { + m_auto->DeleteObject(bAll); + } + + for ( i=0 ; iDeleteObject(m_objectPart[i].object); + + if ( m_objectPart[i].masterParti != -1 ) + { + m_particule->DeleteParticule(m_objectPart[i].masterParti); + m_objectPart[i].masterParti = -1; + } + } + } + + if ( m_bShowLimit ) + { + m_main->FlushShowLimit(0); + m_bShowLimit = FALSE; + } + + if ( !bAll ) m_main->CreateShortcuts(); +} + +// Simplifie un objet (on lui ôte le cerveau, entre autres). + +void CObject::Simplify() +{ + if ( m_brain != 0 ) + { + m_brain->StopProgram(); + } + m_main->SaveOneScript(this); + + if ( m_physics != 0 ) + { + m_physics->DeleteObject(); + delete m_physics; + m_physics = 0; + } + + if ( m_brain != 0 ) + { + m_brain->DeleteObject(); + delete m_brain; + m_brain = 0; + } + + if ( m_motion != 0 ) + { + m_motion->DeleteObject(); + delete m_motion; + m_motion = 0; + } + + if ( m_auto != 0 ) + { + m_auto->DeleteObject(); + delete m_auto; + m_auto = 0; + } + + m_main->CreateShortcuts(); +} + + +// Fait exploser un objet, lorsqu'il est touché par un projectile. +// Si FALSE est retourné, l'objet n'est pas encore foutu. +// Si TRUE est retourné, l'objet est détruit. + +BOOL CObject::ExploObject(ExploType type, float force, float decay) +{ + PyroType pyroType; + CPyro* pyro; + float loss, shield; + + if ( type == EXPLO_BURN ) + { + if ( m_type == OBJECT_MOBILEtg || + m_type == OBJECT_TEEN28 || // bouteille ? + m_type == OBJECT_METAL || + m_type == OBJECT_POWER || + m_type == OBJECT_ATOMIC || + m_type == OBJECT_TNT || + m_type == OBJECT_SCRAP1 || + m_type == OBJECT_SCRAP2 || + m_type == OBJECT_SCRAP3 || + m_type == OBJECT_SCRAP4 || + m_type == OBJECT_SCRAP5 || + m_type == OBJECT_BULLET || + m_type == OBJECT_EGG ) // objet qui ne brûle pas ? + { + type = EXPLO_BOUM; + force = 1.0f; + decay = 1.0f; + } + } + + if ( EXPLO_BOUM ) + { + if ( m_shotTime < 0.5f ) return FALSE; + m_shotTime = 0.0f; + } + + if ( m_type == OBJECT_HUMAN && m_bDead ) return FALSE; + + // Calcule la puissance perdue par l'explosion. + if ( force == 0.0f ) + { + if ( m_type == OBJECT_HUMAN ) + { + loss = LOSS_SHIELD_H; + } + else if ( m_type == OBJECT_MOTHER ) + { + loss = LOSS_SHIELD_M; + } + else + { + loss = LOSS_SHIELD; + } + } + else + { + loss = force; + } + loss *= m_magnifyDamage; + loss *= decay; + + // Diminue la puissance du bouclier. + shield = RetShield(); + shield -= loss; + if ( shield < 0.0f ) shield = 0.0f; + SetShield(shield); + + if ( shield > 0.0f ) // pas encore mort ? + { + if ( type == EXPLO_WATER ) + { + if ( m_type == OBJECT_HUMAN ) + { + pyroType = PT_SHOTH; + } + else + { + pyroType = PT_SHOTW; + } + } + else + { + if ( m_type == OBJECT_HUMAN ) + { + pyroType = PT_SHOTH; + } + else if ( m_type == OBJECT_MOTHER ) + { + pyroType = PT_SHOTM; + } + else + { + pyroType = PT_SHOTT; + } + } + } + else // complètement mort ? + { + if ( type == EXPLO_BURN ) // brûle ? + { + if ( m_type == OBJECT_MOTHER || + m_type == OBJECT_ANT || + m_type == OBJECT_SPIDER || + m_type == OBJECT_BEE || + m_type == OBJECT_WORM || + m_type == OBJECT_BULLET ) + { + pyroType = PT_BURNO; + SetBurn(TRUE); + } + else if ( m_type == OBJECT_HUMAN ) + { + pyroType = PT_DEADG; + } + else + { + pyroType = PT_BURNT; + SetBurn(TRUE); + } + SetVirusMode(FALSE); + } + else if ( type == EXPLO_WATER ) + { + if ( m_type == OBJECT_HUMAN ) + { + pyroType = PT_DEADW; + } + else + { + pyroType = PT_FRAGW; + } + } + else // explosion ? + { + if ( m_type == OBJECT_ANT || + m_type == OBJECT_SPIDER || + m_type == OBJECT_BEE || + m_type == OBJECT_WORM ) + { + pyroType = PT_EXPLOO; + } + else if ( m_type == OBJECT_MOTHER || + m_type == OBJECT_NEST || + m_type == OBJECT_BULLET ) + { + pyroType = PT_FRAGO; + } + else if ( m_type == OBJECT_HUMAN ) + { + pyroType = PT_DEADG; + } + else if ( m_type == OBJECT_BASE || + m_type == OBJECT_DERRICK || + m_type == OBJECT_FACTORY || + m_type == OBJECT_STATION || + m_type == OBJECT_CONVERT || + m_type == OBJECT_REPAIR || + m_type == OBJECT_DESTROYER|| + m_type == OBJECT_TOWER || + m_type == OBJECT_NEST || + m_type == OBJECT_RESEARCH || + m_type == OBJECT_RADAR || + m_type == OBJECT_INFO || + m_type == OBJECT_ENERGY || + m_type == OBJECT_LABO || + m_type == OBJECT_NUCLEAR || + m_type == OBJECT_PARA || + m_type == OBJECT_SAFE || + m_type == OBJECT_HUSTON || + m_type == OBJECT_START || + m_type == OBJECT_END ) // batiment ? + { + pyroType = PT_FRAGT; + } + else if ( m_type == OBJECT_MOBILEtg || + m_type == OBJECT_TEEN28 || // bouteille ? + m_type == OBJECT_TEEN31 ) // basket ? + { + pyroType = PT_FRAGT; + } + else + { + pyroType = PT_EXPLOT; + } + } + + loss = 1.0f; + } + + pyro = new CPyro(m_iMan); + pyro->Create(pyroType, this, loss); + + if ( shield == 0.0f ) // mort ? + { + if ( m_brain != 0 ) + { + m_brain->StopProgram(); + } + m_main->SaveOneScript(this); + } + + if ( shield > 0.0f ) return FALSE; // pas encore mort + + if ( RetSelect() ) + { + SetSelect(FALSE); // désélectionne l'objet + m_camera->SetType(CAMERA_EXPLO); + m_main->DeselectAll(); + } + DeleteDeselList(this); + + if ( m_botVar != 0 ) + { + if ( m_type == OBJECT_STONE || + m_type == OBJECT_URANIUM || + m_type == OBJECT_METAL || + m_type == OBJECT_POWER || + m_type == OBJECT_ATOMIC || + m_type == OBJECT_BULLET || + m_type == OBJECT_BBOX || + m_type == OBJECT_TNT || + m_type == OBJECT_SCRAP1 || + m_type == OBJECT_SCRAP2 || + m_type == OBJECT_SCRAP3 || + m_type == OBJECT_SCRAP4 || + m_type == OBJECT_SCRAP5 ) // (*) + { + m_botVar->SetUserPtr(OBJECTDELETED); + } + } + + return TRUE; +} + +// (*) Si un robot ou le cosmonaute meurt, l'objet doit continuer +// d'exister, pour que les programmes des fourmis continuent +// de fonctionner comme si de rien était ! + + +// Initialise une nouvelle partie. + +void CObject::InitPart(int part) +{ + m_objectPart[part].bUsed = TRUE; + m_objectPart[part].object = -1; + m_objectPart[part].parentPart = -1; + + m_objectPart[part].position = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_objectPart[part].angle.y = 0.0f; + m_objectPart[part].angle.x = 0.0f; + m_objectPart[part].angle.z = 0.0f; + m_objectPart[part].zoom = D3DVECTOR(1.0f, 1.0f, 1.0f); + + m_objectPart[part].bTranslate = TRUE; + m_objectPart[part].bRotate = TRUE; + m_objectPart[part].bZoom = FALSE; + + D3DUtil_SetIdentityMatrix(m_objectPart[part].matTranslate); + D3DUtil_SetIdentityMatrix(m_objectPart[part].matRotate); + D3DUtil_SetIdentityMatrix(m_objectPart[part].matTransform); + D3DUtil_SetIdentityMatrix(m_objectPart[part].matWorld); + + m_objectPart[part].masterParti = -1; +} + +// Crée une nouvelle partie, et retourne son numéro. +// Retourne -1 en cas d'erreur. + +int CObject::CreatePart() +{ + int i; + + for ( i=0 ; iDeleteParticule(m_objectPart[part].masterParti); + m_objectPart[part].masterParti = -1; + } + + m_objectPart[part].bUsed = FALSE; + m_engine->DeleteObject(m_objectPart[part].object); + UpdateTotalPart(); +} + +void CObject::UpdateTotalPart() +{ + int i; + + m_totalPart = 0; + for ( i=0 ; iSetIdent(m_id); + } +} + +int CObject::RetID() +{ + return m_id; +} + + +// Sauve tous les paramètres de l'objet. + +BOOL CObject::Write(char *line) +{ + D3DVECTOR pos; + Info info; + char name[100]; + float value; + int i; + + sprintf(name, " camera=%s", GetCamera(RetCameraType())); + strcat(line, name); + + if ( RetCameraLock() != 0 ) + { + sprintf(name, " cameraLock=%d", RetCameraLock()); + strcat(line, name); + } + + if ( RetEnergy() != 0.0f ) + { + sprintf(name, " energy=%.2f", RetEnergy()); + strcat(line, name); + } + + if ( RetCapacity() != 1.0f ) + { + sprintf(name, " capacity=%.2f", RetCapacity()); + strcat(line, name); + } + + if ( RetShield() != 1.0f ) + { + sprintf(name, " shield=%.2f", RetShield()); + strcat(line, name); + } + + if ( RetRange() != 1.0f ) + { + sprintf(name, " range=%.2f", RetRange()); + strcat(line, name); + } + + if ( RetSelectable() != 1 ) + { + sprintf(name, " selectable=%d", RetSelectable()); + strcat(line, name); + } + + if ( RetEnable() != 1 ) + { + sprintf(name, " enable=%d", RetEnable()); + strcat(line, name); + } + + if ( RetFixed() != 0 ) + { + sprintf(name, " fixed=%d", RetFixed()); + strcat(line, name); + } + + if ( RetClip() != 1 ) + { + sprintf(name, " clip=%d", RetClip()); + strcat(line, name); + } + + if ( RetLock() != 0 ) + { + sprintf(name, " lock=%d", RetLock()); + strcat(line, name); + } + + if ( RetProxyActivate() != 0 ) + { + sprintf(name, " proxyActivate=%d", RetProxyActivate()); + strcat(line, name); + + sprintf(name, " proxyDistance=%.2f", RetProxyDistance()/g_unit); + strcat(line, name); + } + + if ( RetMagnifyDamage() != 1.0f ) + { + sprintf(name, " magnifyDamage=%.2f", RetMagnifyDamage()); + strcat(line, name); + } + + if ( RetGunGoalV() != 0.0f ) + { + sprintf(name, " aimV=%.2f", RetGunGoalV()); + strcat(line, name); + } + if ( RetGunGoalH() != 0.0f ) + { + sprintf(name, " aimH=%.2f", RetGunGoalH()); + strcat(line, name); + } + + if ( RetParam() != 0.0f ) + { + sprintf(name, " param=%.2f", RetParam()); + strcat(line, name); + } + + if ( RetResetCap() != 0 ) + { + sprintf(name, " resetCap=%d", RetResetCap()); + strcat(line, name); + + pos = RetResetPosition()/g_unit; + sprintf(name, " resetPos=%.2f;%.2f;%.2f", pos.x, pos.y, pos.z); + strcat(line, name); + + pos = RetResetAngle()/(PI/180.0f); + sprintf(name, " resetAngle=%.2f;%.2f;%.2f", pos.x, pos.y, pos.z); + strcat(line, name); + + sprintf(name, " resetRun=%d", RetResetRun()); + strcat(line, name); + } + + if ( m_bVirusMode != 0 ) + { + sprintf(name, " virusMode=%d", m_bVirusMode); + strcat(line, name); + } + + if ( m_virusTime != 0.0f ) + { + sprintf(name, " virusTime=%.2f", m_virusTime); + strcat(line, name); + } + + // Met les infos dans borne (OBJECT_INFO). + for ( i=0 ; iWrite(line); + } + + if ( m_brain != 0 ) + { + m_brain->Write(line); + } + + if ( m_physics != 0 ) + { + m_physics->Write(line); + } + + if ( m_auto != 0 ) + { + m_auto->Write(line); + } + + return TRUE; +} + +// Restitue tous les paramètres de l'objet. + +BOOL CObject::Read(char *line) +{ + D3DVECTOR pos, dir; + Info info; + CameraType cType; + char op[20]; + char text[100]; + char* p; + float value; + int i; + + cType = OpCamera(line, "camera"); + if ( cType != CAMERA_NULL ) + { + SetCameraType(cType); + } + + SetCameraLock(OpInt(line, "cameraLock", 0)); + SetEnergy(OpFloat(line, "energy", 0.0f)); + SetCapacity(OpFloat(line, "capacity", 1.0f)); + SetShield(OpFloat(line, "shield", 1.0f)); + SetRange(OpFloat(line, "range", 1.0f)); + SetSelectable(OpInt(line, "selectable", 1)); + SetEnable(OpInt(line, "enable", 1)); + SetFixed(OpInt(line, "fixed", 0)); + SetClip(OpInt(line, "clip", 1)); + SetLock(OpInt(line, "lock", 0)); + SetProxyActivate(OpInt(line, "proxyActivate", 0)); + SetProxyDistance(OpFloat(line, "proxyDistance", 15.0f)*g_unit); + SetRange(OpFloat(line, "range", 30.0f)); + SetMagnifyDamage(OpFloat(line, "magnifyDamage", 1.0f)); + SetGunGoalV(OpFloat(line, "aimV", 0.0f)); + SetGunGoalH(OpFloat(line, "aimH", 0.0f)); + SetParam(OpFloat(line, "param", 0.0f)); + SetResetCap((ResetCap)OpInt(line, "resetCap", 0)); + SetResetPosition(OpDir(line, "resetPos")*g_unit); + SetResetAngle(OpDir(line, "resetAngle")*(PI/180.0f)); + SetResetRun(OpInt(line, "resetRun", 0)); + m_bBurn = OpInt(line, "burnMode", 0); + m_bVirusMode = OpInt(line, "virusMode", 0); + m_virusTime = OpFloat(line, "virusTime", 0.0f); + + // Met les infos dans borne (OBJECT_INFO). + for ( i=0 ; i= MAXCRASHSPHERE ) return -1; + + zoom = RetZoomX(0); + m_crashSpherePos[m_crashSphereUsed] = pos; + m_crashSphereRadius[m_crashSphereUsed] = radius*zoom; + m_crashSphereHardness[m_crashSphereUsed] = hardness; + m_crashSphereSound[m_crashSphereUsed] = sound; + return m_crashSphereUsed++; +} + +// Retourne le nombre de sphères. + +int CObject::RetCrashSphereTotal() +{ + return m_crashSphereUsed; +} + +// Retourne une sphère pour les collisions. +// La position est absolue dans le monde. + +BOOL CObject::GetCrashSphere(int rank, D3DVECTOR &pos, float &radius) +{ + if ( rank < 0 || rank >= m_crashSphereUsed ) + { + pos = m_objectPart[0].position; + radius = 0.0f; + return FALSE; + } + + // Retourne la sphère pour les collisions, qui ne tient pas + // compte de l'inclinaison du véhicule. Ceci est nécessaire + // pour les collisions avec les véhicules, afin de ne pas tenir + // compte de SetInclinaison, par exemple. + // La sphère doit avoir obligatoirement un centre (0;y;0). + if ( rank == 0 && m_crashSphereUsed == 1 && + m_crashSpherePos[0].x == 0.0f && + m_crashSpherePos[0].z == 0.0f ) + { + pos = m_objectPart[0].position + m_crashSpherePos[0]; + radius = m_crashSphereRadius[0]; + return TRUE; + } + + if ( m_objectPart[0].bTranslate || + m_objectPart[0].bRotate ) + { + UpdateTransformObject(); + } + pos = Transform(m_objectPart[0].matWorld, m_crashSpherePos[rank]); + radius = m_crashSphereRadius[rank]; + return TRUE; +} + +// Retourne la dureté d'une sphère. + +Sound CObject::RetCrashSphereSound(int rank) +{ + return m_crashSphereSound[rank]; +} + +// Retourne la dureté d'une sphère. + +float CObject::RetCrashSphereHardness(int rank) +{ + return m_crashSphereHardness[rank]; +} + +// Supprime une sphère. + +void CObject::DeleteCrashSphere(int rank) +{ + int i; + + if ( rank < 0 || rank >= m_crashSphereUsed ) return; + + for ( i=rank+1 ; iMoveOnFloor(pos); + + if ( m_physics != 0 ) + { + m_physics->SetLand(height == 0.0f); + m_physics->SetMotor(height != 0.0f); + } + + m_objectPart[0].position.y = pos.y+height+m_character.height; + m_objectPart[0].bTranslate = TRUE; // il faudra recalculer les matrices +} + +// Ajuste l'inclinaison d'un objet posé sur le sol. + +void CObject::FloorAdjust() +{ + D3DVECTOR pos, n; + FPOINT nn; + float a; + + pos = RetPosition(0); + if ( m_terrain->GetNormal(n, pos) ) + { +#if 0 + SetAngleX(0, sinf(n.z)); + SetAngleZ(0, -sinf(n.x)); + SetAngleY(0, 0.0f); +#else + a = RetAngleY(0); + nn = RotatePoint(-a, FPOINT(n.z, n.x)); + SetAngleX(0, sinf(nn.x)); + SetAngleZ(0, -sinf(nn.y)); +#endif + } +} + + +// Donne la vibration linéaire. + +void CObject::SetLinVibration(D3DVECTOR dir) +{ + if ( m_linVibration.x != dir.x || + m_linVibration.y != dir.y || + m_linVibration.z != dir.z ) + { + m_linVibration = dir; + m_objectPart[0].bTranslate = TRUE; + } +} + +D3DVECTOR CObject::RetLinVibration() +{ + return m_linVibration; +} + +// Donne la vibration circulaire. + +void CObject::SetCirVibration(D3DVECTOR dir) +{ + if ( m_cirVibration.x != dir.x || + m_cirVibration.y != dir.y || + m_cirVibration.z != dir.z ) + { + m_cirVibration = dir; + m_objectPart[0].bRotate = TRUE; + } +} + +D3DVECTOR CObject::RetCirVibration() +{ + return m_cirVibration; +} + +// Donne l'inclinaison. + +void CObject::SetInclinaison(D3DVECTOR dir) +{ + if ( m_inclinaison.x != dir.x || + m_inclinaison.y != dir.y || + m_inclinaison.z != dir.z ) + { + m_inclinaison = dir; + m_objectPart[0].bRotate = TRUE; + } +} + +D3DVECTOR CObject::RetInclinaison() +{ + return m_inclinaison; +} + + +// Donne la position du centre de l'objet. + +void CObject::SetPosition(int part, const D3DVECTOR &pos) +{ + D3DVECTOR shPos, n[20], norm; + float height, radius; + int rank, i, j; + + m_objectPart[part].position = pos; + m_objectPart[part].bTranslate = TRUE; // il faudra recalculer les matrices + + if ( part == 0 && !m_bFlat ) // partie principale ? + { + rank = m_objectPart[0].object; + + shPos = pos; + m_terrain->MoveOnFloor(shPos, TRUE); + m_engine->SetObjectShadowPos(rank, shPos); + + if ( m_physics != 0 && m_physics->RetType() == TYPE_FLYING ) + { + height = pos.y-shPos.y; + } + else + { + height = 0.0f; + } + m_engine->SetObjectShadowHeight(rank, height); + + // Calcul la normale au terrain en 9 points stratégiques, + // puis effectue une moyenne pondérée (les points au centre + // ont plus d'importance). + radius = m_engine->RetObjectShadowRadius(rank); + i = 0; + + m_terrain->GetNormal(norm, pos); + n[i++] = norm; + n[i++] = norm; + n[i++] = norm; + + shPos = pos; + shPos.x += radius*0.6f; + shPos.z += radius*0.6f; + m_terrain->GetNormal(norm, shPos); + n[i++] = norm; + n[i++] = norm; + + shPos = pos; + shPos.x -= radius*0.6f; + shPos.z += radius*0.6f; + m_terrain->GetNormal(norm, shPos); + n[i++] = norm; + n[i++] = norm; + + shPos = pos; + shPos.x += radius*0.6f; + shPos.z -= radius*0.6f; + m_terrain->GetNormal(norm, shPos); + n[i++] = norm; + n[i++] = norm; + + shPos = pos; + shPos.x -= radius*0.6f; + shPos.z -= radius*0.6f; + m_terrain->GetNormal(norm, shPos); + n[i++] = norm; + n[i++] = norm; + + shPos = pos; + shPos.x += radius; + shPos.z += radius; + m_terrain->GetNormal(norm, shPos); + n[i++] = norm; + + shPos = pos; + shPos.x -= radius; + shPos.z += radius; + m_terrain->GetNormal(norm, shPos); + n[i++] = norm; + + shPos = pos; + shPos.x += radius; + shPos.z -= radius; + m_terrain->GetNormal(norm, shPos); + n[i++] = norm; + + shPos = pos; + shPos.x -= radius; + shPos.z -= radius; + m_terrain->GetNormal(norm, shPos); + n[i++] = norm; + + norm = 0.0f; + for ( j=0 ; jSetObjectShadowNormal(rank, norm); + + if ( m_shadowLight != -1 ) + { + shPos = pos; + shPos.y += m_shadowHeight; + m_light->SetLightPos(m_shadowLight, shPos); + } + + if ( m_effectLight != -1 ) + { + shPos = pos; + shPos.y += m_effectHeight; + m_light->SetLightPos(m_effectLight, shPos); + } + + if ( m_bShowLimit ) + { + m_main->AdjustShowLimit(0, pos); + } + } +} + +D3DVECTOR CObject::RetPosition(int part) +{ + return m_objectPart[part].position; +} + +// Donne la rotation autour des 3 axes. + +void CObject::SetAngle(int part, const D3DVECTOR &angle) +{ + m_objectPart[part].angle = angle; + m_objectPart[part].bRotate = TRUE; // il faudra recalculer les matrices + + if ( part == 0 && !m_bFlat ) // partie principale ? + { + m_engine->SetObjectShadowAngle(m_objectPart[0].object, m_objectPart[0].angle.y); + } +} + +D3DVECTOR CObject::RetAngle(int part) +{ + return m_objectPart[part].angle; +} + +// Donne la rotation autour de l'axe Y. + +void CObject::SetAngleY(int part, float angle) +{ + m_objectPart[part].angle.y = angle; + m_objectPart[part].bRotate = TRUE; // il faudra recalculer les matrices + + if ( part == 0 && !m_bFlat ) // partie principale ? + { + m_engine->SetObjectShadowAngle(m_objectPart[0].object, m_objectPart[0].angle.y); + } +} + +// Donne la rotation autour de l'axe X. + +void CObject::SetAngleX(int part, float angle) +{ + m_objectPart[part].angle.x = angle; + m_objectPart[part].bRotate = TRUE; // il faudra recalculer les matrices +} + +// Donne la rotation autour de l'axe Z. + +void CObject::SetAngleZ(int part, float angle) +{ + m_objectPart[part].angle.z = angle; + m_objectPart[part].bRotate = TRUE; // il faudra recalculer les matrices +} + +float CObject::RetAngleY(int part) +{ + return m_objectPart[part].angle.y; +} + +float CObject::RetAngleX(int part) +{ + return m_objectPart[part].angle.x; +} + +float CObject::RetAngleZ(int part) +{ + return m_objectPart[part].angle.z; +} + + +// Donne le zoom lobal. + +void CObject::SetZoom(int part, float zoom) +{ + m_objectPart[part].bTranslate = TRUE; // il faudra recalculer les matrices + m_objectPart[part].zoom.x = zoom; + m_objectPart[part].zoom.y = zoom; + m_objectPart[part].zoom.z = zoom; + + m_objectPart[part].bZoom = ( m_objectPart[part].zoom.x != 1.0f || + m_objectPart[part].zoom.y != 1.0f || + m_objectPart[part].zoom.z != 1.0f ); +} + +void CObject::SetZoom(int part, D3DVECTOR zoom) +{ + m_objectPart[part].bTranslate = TRUE; // il faudra recalculer les matrices + m_objectPart[part].zoom = zoom; + + m_objectPart[part].bZoom = ( m_objectPart[part].zoom.x != 1.0f || + m_objectPart[part].zoom.y != 1.0f || + m_objectPart[part].zoom.z != 1.0f ); +} + +D3DVECTOR CObject::RetZoom(int part) +{ + return m_objectPart[part].zoom; +} + +void CObject::SetZoomX(int part, float zoom) +{ + m_objectPart[part].bTranslate = TRUE; // il faudra recalculer les matrices + m_objectPart[part].zoom.x = zoom; + + m_objectPart[part].bZoom = ( m_objectPart[part].zoom.x != 1.0f || + m_objectPart[part].zoom.y != 1.0f || + m_objectPart[part].zoom.z != 1.0f ); +} + +void CObject::SetZoomY(int part, float zoom) +{ + m_objectPart[part].bTranslate = TRUE; // il faudra recalculer les matrices + m_objectPart[part].zoom.y = zoom; + + m_objectPart[part].bZoom = ( m_objectPart[part].zoom.x != 1.0f || + m_objectPart[part].zoom.y != 1.0f || + m_objectPart[part].zoom.z != 1.0f ); +} + +void CObject::SetZoomZ(int part, float zoom) +{ + m_objectPart[part].bTranslate = TRUE; // il faudra recalculer les matrices + m_objectPart[part].zoom.z = zoom; + + m_objectPart[part].bZoom = ( m_objectPart[part].zoom.x != 1.0f || + m_objectPart[part].zoom.y != 1.0f || + m_objectPart[part].zoom.z != 1.0f ); +} + +float CObject::RetZoomX(int part) +{ + return m_objectPart[part].zoom.x; +} + +float CObject::RetZoomY(int part) +{ + return m_objectPart[part].zoom.y; +} + +float CObject::RetZoomZ(int part) +{ + return m_objectPart[part].zoom.z; +} + + +// Retourne le niveau de l'eau. + +float CObject::RetWaterLevel() +{ + return m_water->RetLevel(); +} + + +void CObject::SetTrainer(BOOL bEnable) +{ + m_bTrainer = bEnable; + + if ( m_bTrainer ) // entraînement ? + { + m_cameraType = CAMERA_FIX; + } +} + +BOOL CObject::RetTrainer() +{ + return m_bTrainer; +} + +void CObject::SetToy(BOOL bEnable) +{ + m_bToy = bEnable; +} + +BOOL CObject::RetToy() +{ + return m_bToy; +} + +void CObject::SetManual(BOOL bManual) +{ + m_bManual = bManual; +} + +BOOL CObject::RetManual() +{ + return m_bManual; +} + +void CObject::SetResetCap(ResetCap cap) +{ + m_resetCap = cap; +} + +ResetCap CObject::RetResetCap() +{ + return m_resetCap; +} + +void CObject::SetResetBusy(BOOL bBusy) +{ + m_bResetBusy = bBusy; +} + +BOOL CObject::RetResetBusy() +{ + return m_bResetBusy; +} + +void CObject::SetResetPosition(const D3DVECTOR &pos) +{ + m_resetPosition = pos; +} + +D3DVECTOR CObject::RetResetPosition() +{ + return m_resetPosition; +} + +void CObject::SetResetAngle(const D3DVECTOR &angle) +{ + m_resetAngle = angle; +} + +D3DVECTOR CObject::RetResetAngle() +{ + return m_resetAngle; +} + +int CObject::RetResetRun() +{ + return m_resetRun; +} + +void CObject::SetResetRun(int run) +{ + m_resetRun = run; +} + + +// Gestion de la particule maîtresse. + +void CObject::SetMasterParticule(int part, int parti) +{ + m_objectPart[part].masterParti = parti; +} + +int CObject::RetMasterParticule(int part) +{ + return m_objectPart[part].masterParti; +} + + +// Gestion de la pile transportée. + +void CObject::SetPower(CObject* power) +{ + m_power = power; +} + +CObject* CObject::RetPower() +{ + return m_power; +} + +// Gestion de l'objet transporté. + +void CObject::SetFret(CObject* fret) +{ + m_fret = fret; +} + +CObject* CObject::RetFret() +{ + return m_fret; +} + +// Gestion de l'objet "camion" qui transporte celui-ci. + +void CObject::SetTruck(CObject* truck) +{ + m_truck = truck; + + // Ombre invisible si l'objet est transporté. + m_engine->SetObjectShadowHide(m_objectPart[0].object, (m_truck != 0)); +} + +CObject* CObject::RetTruck() +{ + return m_truck; +} + +// Gestion de la partie transporteuse. + +void CObject::SetTruckPart(int part) +{ + m_truckLink = part; +} + +int CObject::RetTruckPart() +{ + return m_truckLink; +} + + +// Gestion des informations utilisateur. + +void CObject::InfoFlush() +{ + m_infoTotal = 0; + m_bInfoUpdate = TRUE; +} + +void CObject::DeleteInfo(int rank) +{ + int i; + + if ( rank < 0 || rank >= m_infoTotal ) return; + + for ( i=rank ; i= OBJECTMAXINFO ) return; + m_info[rank] = info; + + if ( rank+1 > m_infoTotal ) m_infoTotal = rank+1; + m_bInfoUpdate = TRUE; +} + +Info CObject::RetInfo(int rank) +{ + if ( rank < 0 || rank >= OBJECTMAXINFO ) rank = 0; + return m_info[rank]; +} + +int CObject::RetInfoTotal() +{ + return m_infoTotal; +} + +void CObject::SetInfoReturn(float value) +{ + m_infoReturn = value; +} + +float CObject::RetInfoReturn() +{ + return m_infoReturn; +} + +void CObject::SetInfoUpdate(BOOL bUpdate) +{ + m_bInfoUpdate = bUpdate; +} + +BOOL CObject::RetInfoUpdate() +{ + return m_bInfoUpdate; +} + + +BOOL CObject::SetCmdLine(int rank, float value) +{ + if ( rank < 0 || rank >= OBJECTMAXCMDLINE ) return FALSE; + m_cmdLine[rank] = value; + return TRUE; +} + +float CObject::RetCmdLine(int rank) +{ + if ( rank < 0 || rank >= OBJECTMAXCMDLINE ) return 0.0f; + return m_cmdLine[rank]; +} + + +// Retourne les matrices d'une partie d'objet. + +D3DMATRIX* CObject::RetRotateMatrix(int part) +{ + return &m_objectPart[part].matRotate; +} + +D3DMATRIX* CObject::RetTranslateMatrix(int part) +{ + return &m_objectPart[part].matTranslate; +} + +D3DMATRIX* CObject::RetTransformMatrix(int part) +{ + return &m_objectPart[part].matTransform; +} + +D3DMATRIX* CObject::RetWorldMatrix(int part) +{ + if ( m_objectPart[0].bTranslate || + m_objectPart[0].bRotate ) + { + UpdateTransformObject(); + } + + return &m_objectPart[part].matWorld; +} + + +// Indique si l'objet doit être dessiné par dessous l'interface. + +void CObject::SetDrawWorld(BOOL bDraw) +{ + int i; + + for ( i=0 ; iSetDrawWorld(m_objectPart[i].object, bDraw); + } + } +} + +// Indique si l'objet doit être dessiné par dessus l'interface. + +void CObject::SetDrawFront(BOOL bDraw) +{ + int i; + + for ( i=0 ; iSetDrawFront(m_objectPart[i].object, bDraw); + } + } +} + + +// Crée un véhicule roulant quelconque posé sur le sol. + +BOOL CObject::CreateVehicle(D3DVECTOR pos, float angle, ObjectType type, + float power, BOOL bTrainer, BOOL bToy) +{ + m_type = type; + + if ( type == OBJECT_TOTO ) + { + m_motion = new CMotionToto(m_iMan, this); + m_motion->Create(pos, angle, type, 1.0f); + return TRUE; + } + + SetTrainer(bTrainer); + SetToy(bToy); + + m_physics = new CPhysics(m_iMan, this); + m_brain = new CBrain(m_iMan, this); + + m_physics->SetBrain(m_brain); + m_brain->SetPhysics(m_physics); + +#if 0 + if ( type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic ) // canon fireball ? + { + m_showLimitRadius = 160.0f; + } + if ( type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii ) // canon orgaball ? + { + m_showLimitRadius = 160.0f; + } + if ( type == OBJECT_MOBILErc ) // canon phazer ? + { + m_showLimitRadius = 160.0f; + } + if ( type == OBJECT_MOBILErs ) // robot bouclier ? + { + m_showLimitRadius = 50.0f; + } +#endif + if ( type == OBJECT_MOBILErt ) // robot secoueur ? + { + m_showLimitRadius = 400.0f; + } + + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH ) + { + m_motion = new CMotionHuman(m_iMan, this); + } + else + { + m_motion = new CMotionVehicle(m_iMan, this); + } + if ( m_motion == 0 ) return FALSE; + + m_physics->SetMotion(m_motion); + m_brain->SetMotion(m_motion); + m_motion->SetPhysics(m_physics); + m_motion->SetBrain(m_brain); + if ( !m_motion->Create(pos, angle, type, power) ) + { + if ( m_physics != 0 ) + { + m_physics->DeleteObject(); + delete m_physics; + m_physics = 0; + } + if ( m_brain != 0 ) + { + m_brain->DeleteObject(); + delete m_brain; + m_brain = 0; + } + if ( m_motion != 0 ) + { + m_motion->DeleteObject(); + delete m_motion; + m_motion = 0; + } + return FALSE; + } + + return TRUE; +} + +// Crée un insecte quelconque posé sur le sol. + +BOOL CObject::CreateInsect(D3DVECTOR pos, float angle, ObjectType type) +{ + m_type = type; + + m_physics = new CPhysics(m_iMan, this); + m_brain = new CBrain(m_iMan, this); + + m_physics->SetBrain(m_brain); + m_brain->SetPhysics(m_physics); + + if ( type == OBJECT_MOTHER ) + { + m_motion = new CMotionMother(m_iMan, this); + } + if ( type == OBJECT_ANT ) + { + m_motion = new CMotionAnt(m_iMan, this); + } + if ( type == OBJECT_SPIDER ) + { + m_motion = new CMotionSpider(m_iMan, this); + } + if ( type == OBJECT_BEE ) + { + m_motion = new CMotionBee(m_iMan, this); + } + if ( type == OBJECT_WORM ) + { + m_motion = new CMotionWorm(m_iMan, this); + } + if ( m_motion == 0 ) return FALSE; + + m_physics->SetMotion(m_motion); + m_brain->SetMotion(m_motion); + m_motion->SetPhysics(m_physics); + m_motion->SetBrain(m_brain); + if ( !m_motion->Create(pos, angle, type, 0.0f) ) + { + if ( m_physics != 0 ) + { + m_physics->DeleteObject(); + delete m_physics; + m_physics = 0; + } + if ( m_brain != 0 ) + { + m_brain->DeleteObject(); + delete m_brain; + m_brain = 0; + } + if ( m_motion != 0 ) + { + m_motion->DeleteObject(); + delete m_motion; + m_motion = 0; + } + return FALSE; + } + + return TRUE; +} + +// Crée l'ombre sous un véhicule sous forme d'une lumière +// négative. + +BOOL CObject::CreateShadowLight(float height, D3DCOLORVALUE color) +{ + D3DLIGHT7 light; + D3DVECTOR pos; + + if ( !m_engine->RetLightMode() ) return TRUE; + + pos = RetPosition(0); + m_shadowHeight = height; + + ZeroMemory( &light, sizeof(light) ); + light.dltType = D3DLIGHT_SPOT; + light.dcvDiffuse.r = color.r; + light.dcvDiffuse.g = color.g; + light.dcvDiffuse.b = color.b; + light.dvPosition.x = pos.x; + light.dvPosition.y = pos.y+height; + light.dvPosition.z = pos.z; + light.dvDirection.x = 0.0f; + light.dvDirection.y = -1.0f; // contre en bas + light.dvDirection.z = 0.0f; + light.dvRange = D3DLIGHT_RANGE_MAX; + light.dvFalloff = 1.0f; + light.dvAttenuation0 = 1.0f; + light.dvAttenuation1 = 0.0f; + light.dvAttenuation2 = 0.0f; + light.dvTheta = 0.0f; + light.dvPhi = PI/4.0f; + + m_shadowLight = m_light->CreateLight(); + if ( m_shadowLight == -1 ) return FALSE; + + m_light->SetLight(m_shadowLight, light); + + // N'éclaire que les objets du terrain. + m_light->SetLightIncluType(m_shadowLight, TYPETERRAIN); + + return TRUE; +} + +// Retourne le numéro de la lumière d'ombre négative. + +int CObject::RetShadowLight() +{ + return m_shadowLight; +} + +// Crée la lumière pour les effects d'un véhicule. + +BOOL CObject::CreateEffectLight(float height, D3DCOLORVALUE color) +{ + D3DLIGHT7 light; + + if ( !m_engine->RetLightMode() ) return TRUE; + + m_effectHeight = height; + + ZeroMemory( &light, sizeof(light) ); + light.dltType = D3DLIGHT_SPOT; + light.dcvDiffuse.r = color.r; + light.dcvDiffuse.g = color.g; + light.dcvDiffuse.b = color.b; + light.dvPosition.x = 0.0f; + light.dvPosition.y = 0.0f+height; + light.dvPosition.z = 0.0f; + light.dvDirection.x = 0.0f; + light.dvDirection.y = -1.0f; // contre en bas + light.dvDirection.z = 0.0f; + light.dvRange = D3DLIGHT_RANGE_MAX; + light.dvFalloff = 1.0f; + light.dvAttenuation0 = 1.0f; + light.dvAttenuation1 = 0.0f; + light.dvAttenuation2 = 0.0f; + light.dvTheta = 0.0f; + light.dvPhi = PI/4.0f; + + m_effectLight = m_light->CreateLight(); + if ( m_effectLight == -1 ) return FALSE; + + m_light->SetLight(m_effectLight, light); + m_light->SetLightIntensity(m_effectLight, 0.0f); + + return TRUE; +} + +// Retourne le numéro de la lumière des effets. + +int CObject::RetEffectLight() +{ + return m_effectLight; +} + +// Crée l'ombre circulaire sous un véhicule. + +BOOL CObject::CreateShadowCircle(float radius, float intensity, + D3DShadowType type) +{ + float zoom; + + if ( intensity == 0.0f ) return TRUE; + + zoom = RetZoomX(0); + + m_engine->ShadowCreate(m_objectPart[0].object); + + m_engine->SetObjectShadowRadius(m_objectPart[0].object, radius*zoom); + m_engine->SetObjectShadowIntensity(m_objectPart[0].object, intensity); + m_engine->SetObjectShadowHeight(m_objectPart[0].object, 0.0f); + m_engine->SetObjectShadowAngle(m_objectPart[0].object, m_objectPart[0].angle.y); + m_engine->SetObjectShadowType(m_objectPart[0].object, type); + + return TRUE; +} + +// Crée un batiment quelconque posé sur le sol. + +BOOL CObject::CreateBuilding(D3DVECTOR pos, float angle, float height, + ObjectType type, float power) +{ + CModFile* pModFile; + FPOINT p; + int rank, i; + + if ( m_engine->RetRestCreate() < 20 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + SetType(type); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); // c'est un objet fixe + SetObjectRank(0, rank); + + if ( m_type == OBJECT_PORTICO ) + { + pModFile->ReadModel("objects\\portico1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\portico2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(0.0f, 67.0f, 0.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(2, rank); + SetObjectParent(2, 1); + pModFile->ReadModel("objects\\portico3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(2, D3DVECTOR(0.0f, 0.0f, -33.0f)); + SetAngleY(2, 45.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(3, rank); + SetObjectParent(3, 2); + pModFile->ReadModel("objects\\portico4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(3, D3DVECTOR(50.0f, 0.0f, 0.0f)); + SetAngleY(3, -60.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(4, rank); + SetObjectParent(4, 3); + pModFile->ReadModel("objects\\portico5.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(4, D3DVECTOR(35.0f, 0.0f, 0.0f)); + SetAngleY(4, -55.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(5, rank); + SetObjectParent(5, 1); + pModFile->ReadModel("objects\\portico3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(5, D3DVECTOR(0.0f, 0.0f, 33.0f)); + SetAngleY(5, -45.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(6, rank); + SetObjectParent(6, 5); + pModFile->ReadModel("objects\\portico4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(6, D3DVECTOR(50.0f, 0.0f, 0.0f)); + SetAngleY(6, 60.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(7, rank); + SetObjectParent(7, 6); + pModFile->ReadModel("objects\\portico5.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(7, D3DVECTOR(35.0f, 0.0f, 0.0f)); + SetAngleY(7, 55.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(8, rank); + SetObjectParent(8, 0); + pModFile->ReadModel("objects\\portico6.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(8, D3DVECTOR(-35.0f, 50.0f, -35.0f)); + SetAngleY(8, -PI/2.0f); + SetZoom(8, 2.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(9, rank); + SetObjectParent(9, 8); + pModFile->ReadModel("objects\\portico7.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(9, D3DVECTOR(0.0f, 4.5f, 1.9f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(10, rank); + SetObjectParent(10, 0); + pModFile->ReadModel("objects\\portico6.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(10, D3DVECTOR(-35.0f, 50.0f, 35.0f)); + SetAngleY(10, -PI/2.0f); + SetZoom(10, 2.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(11, rank); + SetObjectParent(11, 10); + pModFile->ReadModel("objects\\portico7.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(11, D3DVECTOR(0.0f, 4.5f, 1.9f)); + + CreateCrashSphere(D3DVECTOR( 0.0f, 28.0f, 0.0f), 45.5f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 27.0f, 10.0f, -42.0f), 15.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 10.0f, -42.0f), 15.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-27.0f, 10.0f, -42.0f), 15.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 27.0f, 10.0f, 42.0f), 15.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 10.0f, 42.0f), 15.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-27.0f, 10.0f, 42.0f), 15.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-32.0f, 45.0f, -32.0f), 10.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-32.0f, 45.0f, 32.0f), 10.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 32.0f, 45.0f, -32.0f), 10.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 32.0f, 45.0f, 32.0f), 10.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 35.0f, 0.0f), 50.0f); + + CreateShadowCircle(50.0f, 1.0f); + } + + if ( m_type == OBJECT_BASE ) + { + pModFile->ReadModel("objects\\base1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + for ( i=0 ; i<8 ; i++ ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1+i, rank); + SetObjectParent(1+i, 0); + pModFile->ReadModel("objects\\base2.mod"); + pModFile->CreateEngineObject(rank); + p = RotatePoint(-PI/4.0f*i, 27.8f); + SetPosition(1+i, D3DVECTOR(p.x, 30.0f, p.y)); + SetAngleY(1+i, PI/4.0f*i); + SetAngleZ(1+i, PI/2.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(10+i, rank); + SetObjectParent(10+i, 1+i); + pModFile->ReadModel("objects\\base4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(10+i, D3DVECTOR(23.5f, 0.0f, 7.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(18+i, rank); + SetObjectParent(18+i, 1+i); + pModFile->ReadModel("objects\\base4.mod"); + pModFile->Mirror(); + pModFile->CreateEngineObject(rank); + SetPosition(18+i, D3DVECTOR(23.5f, 0.0f, -7.0f)); + } + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(9, rank); + SetObjectParent(9, 0); + pModFile->ReadModel("objects\\base3.mod"); // pilier central + pModFile->CreateEngineObject(rank); + + CreateCrashSphere(D3DVECTOR( 0.0f, 33.0f, 0.0f), 2.5f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 39.0f, 0.0f), 2.5f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 45.0f, 0.0f), 2.5f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 51.0f, 0.0f), 2.5f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 57.0f, 0.0f), 2.5f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 63.0f, 0.0f), 2.5f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 69.0f, 0.0f), 2.5f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 82.0f, 0.0f), 8.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 18.0f, 94.0f, 0.0f), 10.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-18.0f, 94.0f, 0.0f), 10.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 94.0f, 18.0f), 10.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 94.0f, -18.0f), 10.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 13.0f, 94.0f, 13.0f), 10.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-13.0f, 94.0f, 13.0f), 10.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 13.0f, 94.0f, -13.0f), 10.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-13.0f, 94.0f, -13.0f), 10.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f,104.0f, 0.0f), 14.0f, SOUND_BOUMm, 0.45f); + + SetGlobalSphere(D3DVECTOR(0.0f, 45.0f, 0.0f), 10.0f); + + CreateShadowCircle(60.0f, 1.0f); + m_showLimitRadius = 200.0f; + + m_terrain->AddBuildingLevel(pos, 28.6f, 73.4f, 30.0f, 0.4f); + } + + if ( m_type == OBJECT_DERRICK ) + { + pModFile->ReadModel("objects\\derrick1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\derrick2.mod"); + pModFile->CreateEngineObject(rank); + + CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 17.0f, 0.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 26.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(7.0f, 17.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 10.0f); + + CreateShadowCircle(10.0f, 0.4f); + } + + if ( m_type == OBJECT_RESEARCH ) + { + pModFile->ReadModel("objects\\search1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\search2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(0.0f, 13.0f, 0.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(2, rank); + SetObjectParent(2, 1); + pModFile->ReadModel("objects\\search3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(2, D3DVECTOR(0.0f, 4.0f, 0.0f)); + SetAngleZ(2, 35.0f*PI/180.0f); + + CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 9.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 6.0f, 0.0f), 9.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 14.0f, 0.0f), 7.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 8.0f, 0.0f), 12.0f); + + m_character.posPower = D3DVECTOR(7.5f, 3.0f, 0.0f); + + CreateShadowCircle(12.0f, 1.0f); + } + + if ( m_type == OBJECT_RADAR ) + { + pModFile->ReadModel("objects\\radar1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\radar2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(0.0f, 5.0f, 0.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(2, rank); + SetObjectParent(2, 0); + pModFile->ReadModel("objects\\radar3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(2, D3DVECTOR(0.0f, 11.0f, 0.0f)); + SetAngleY(2, -PI/2.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(3, rank); + SetObjectParent(3, 2); + pModFile->ReadModel("objects\\radar4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(3, D3DVECTOR(0.0f, 4.5f, 1.9f)); + + CreateCrashSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 11.0f, 0.0f), 6.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 7.0f, 0.0f), 7.0f); + + CreateShadowCircle(8.0f, 1.0f); + } + + if ( m_type == OBJECT_INFO ) + { + pModFile->ReadModel("objects\\info1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\info2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(0.0f, 5.0f, 0.0f)); + + for ( i=0 ; i<3 ; i++ ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(2+i*2, rank); + SetObjectParent(2+i*2, 1); + pModFile->ReadModel("objects\\info3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(2+i*2, D3DVECTOR(0.0f, 4.5f, 0.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(3+i*2, rank); + SetObjectParent(3+i*2, 2+i*2); + pModFile->ReadModel("objects\\radar4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(3+i*2, D3DVECTOR(0.0f, 0.0f, -4.0f)); + + SetAngleY(2+i*2, 2.0f*PI/3.0f*i); + } + + CreateCrashSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 11.0f, 0.0f), 6.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 6.0f); + + CreateShadowCircle(8.0f, 1.0f); + } + + if ( m_type == OBJECT_ENERGY ) + { + pModFile->ReadModel("objects\\energy.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + CreateCrashSphere(D3DVECTOR(-2.0f, 13.0f, 0.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-7.0f, 3.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 1.0f, 0.0f), 1.5f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(-7.0f, 5.0f, 0.0f), 5.0f); + + m_character.posPower = D3DVECTOR(0.0f, 3.0f, 0.0f); + m_energy = power; // initialise le niveau d'énergie + + CreateShadowCircle(6.0f, 0.5f); + } + + if ( m_type == OBJECT_LABO ) + { + pModFile->ReadModel("objects\\labo1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\labo2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(-9.0f, 3.0f, 0.0f)); + SetAngleZ(1, PI/2.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(2, rank); + SetObjectParent(2, 1); + pModFile->ReadModel("objects\\labo3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(2, D3DVECTOR(9.0f, -1.0f, 0.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(3, rank); + SetObjectParent(3, 2); + pModFile->ReadModel("objects\\labo4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(3, D3DVECTOR(0.0f, 0.0f, 0.0f)); + SetAngleZ(3, 80.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(4, rank); + SetObjectParent(4, 2); + pModFile->ReadModel("objects\\labo4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(4, D3DVECTOR(0.0f, 0.0f, 0.0f)); + SetAngleZ(4, 80.0f*PI/180.0f); + SetAngleY(4, PI*2.0f/3.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(5, rank); + SetObjectParent(5, 2); + pModFile->ReadModel("objects\\labo4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(5, D3DVECTOR(0.0f, 0.0f, 0.0f)); + SetAngleZ(5, 80.0f*PI/180.0f); + SetAngleY(5, -PI*2.0f/3.0f); + + CreateCrashSphere(D3DVECTOR( 0.0f, 1.0f, 0.0f), 1.5f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 11.0f, 0.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 10.0f, 0.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-12.0f, 3.0f, 3.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-12.0f, 3.0f, -3.0f), 4.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(-10.0f, 5.0f, 0.0f), 7.0f); + + m_character.posPower = D3DVECTOR(0.0f, 3.0f, 0.0f); + + CreateShadowCircle(7.0f, 0.5f); + } + + if ( m_type == OBJECT_FACTORY ) + { + pModFile->ReadModel("objects\\factory1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + for ( i=0 ; i<9 ; i++ ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1+i, rank); + SetObjectParent(1+i, 0); + pModFile->ReadModel("objects\\factory2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1+i, D3DVECTOR(10.0f, 2.0f*i, 10.0f)); + SetAngleZ(1+i, PI/2.0f); + SetZoomZ(1+i, 0.30f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(10+i, rank); + SetObjectParent(10+i, 0); + pModFile->ReadModel("objects\\factory2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(10+i, D3DVECTOR(10.0f, 2.0f*i, -10.0f)); + SetAngleZ(10+i, -PI/2.0f); + SetAngleY(10+i, PI); + SetZoomZ(10+i, 0.30f); + } + + for ( i=0 ; i<2 ; i++ ) + { + float s = (float)(i*2-1); + CreateCrashSphere(D3DVECTOR(-10.0f, 2.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -3.0f, 2.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 3.0f, 2.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 2.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 9.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -3.0f, 9.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 3.0f, 9.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 9.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 16.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -3.0f, 16.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 3.0f, 16.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 16.0f, 11.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 16.0f, 4.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -3.0f, 16.0f, 4.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 3.0f, 16.0f, 4.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 16.0f, 4.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 2.0f, 4.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 9.0f, 4.0f*s), 4.0f, SOUND_BOUMm, 0.45f); + } + CreateCrashSphere(D3DVECTOR(-10.0f, 21.0f, -4.0f), 3.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 18.0f); + + CreateShadowCircle(24.0f, 0.3f); + } + + if ( m_type == OBJECT_REPAIR ) + { + pModFile->ReadModel("objects\\repair1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\repair2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(-11.0f, 13.5f, 0.0f)); + SetAngleZ(1, PI/2.0f); + + m_terrain->AddBuildingLevel(pos, 7.0f, 9.0f, 1.0f, 0.5f); + + CreateCrashSphere(D3DVECTOR(-11.0f, 0.0f, 4.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-11.0f, 0.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-11.0f, 0.0f, -4.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-11.0f, 10.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(-11.0f, 13.0f, 0.0f), 15.0f); + } + + if ( m_type == OBJECT_DESTROYER ) + { + pModFile->ReadModel("objects\\destroy1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\destroy2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(0.0f, 0.0f, 0.0f)); + + m_terrain->AddBuildingLevel(pos, 7.0f, 9.0f, 1.0f, 0.5f); + + CreateCrashSphere(D3DVECTOR(-3.5f, 0.0f, -13.5f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 3.5f, 0.0f, -13.5f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-3.5f, 0.0f, 13.5f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 3.5f, 0.0f, 13.5f), 4.0f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(19.0f, 1.0f); + } + + if ( m_type == OBJECT_STATION ) + { + pModFile->ReadModel("objects\\station.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + m_terrain->AddBuildingLevel(pos, 7.0f, 9.0f, 1.0f, 0.5f); + + CreateCrashSphere(D3DVECTOR(-15.0f, 2.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-15.0f, 6.0f, 0.0f), 4.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(-15.0f, 5.0f, 0.0f), 6.0f); + + m_energy = power; // initialise le niveau d'énergie + } + + if ( m_type == OBJECT_CONVERT ) + { + pModFile->ReadModel("objects\\convert1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\convert2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(0.0f, 14.0f, 0.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(2, rank); + SetObjectParent(2, 0); + pModFile->ReadModel("objects\\convert3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(2, D3DVECTOR(0.0f, 11.5f, 0.0f)); + SetAngleX(2, -PI*0.35f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(3, rank); + SetObjectParent(3, 0); + pModFile->ReadModel("objects\\convert3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(3, D3DVECTOR(0.0f, 11.5f, 0.0f)); + SetAngleY(3, PI); + SetAngleX(3, -PI*0.35f); + + m_terrain->AddBuildingLevel(pos, 7.0f, 9.0f, 1.0f, 0.5f); + + CreateCrashSphere(D3DVECTOR(-10.0f, 2.0f, 4.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 2.0f, -4.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 9.0f, 0.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 14.0f, 0.0f), 1.5f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(-3.0f, 8.0f, 0.0f), 14.0f); + } + + if ( m_type == OBJECT_TOWER ) + { + pModFile->ReadModel("objects\\tower.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\roller2c.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(0.0f, 20.0f, 0.0f)); + SetAngleZ(1, PI/2.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(2, rank); + SetObjectParent(2, 1); + pModFile->ReadModel("objects\\roller3c.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(2, D3DVECTOR(4.5f, 0.0f, 0.0f)); + SetAngleZ(2, 0.0f); + + CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 6.5f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 8.0f, 0.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 15.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 24.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 7.0f); + + m_character.posPower = D3DVECTOR(5.0f, 3.0f, 0.0f); + + CreateShadowCircle(6.0f, 1.0f); + m_showLimitRadius = BLITZPARA; + } + + if ( m_type == OBJECT_NUCLEAR ) + { + pModFile->ReadModel("objects\\nuclear1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\nuclear2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(20.0f, 10.0f, 0.0f)); + SetAngleZ(1, 135.0f*PI/180.0f); + + CreateCrashSphere(D3DVECTOR( 0.0f, 0.0f, 0.0f), 19.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 24.0f, 0.0f), 15.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(22.0f, 1.0f, 0.0f), 1.5f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 17.0f, 0.0f), 26.0f); + + m_character.posPower = D3DVECTOR(22.0f, 3.0f, 0.0f); + + CreateShadowCircle(21.0f, 1.0f); + } + + if ( m_type == OBJECT_PARA ) + { + pModFile->ReadModel("objects\\para.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + m_terrain->AddBuildingLevel(pos, 16.0f, 18.0f, 1.0f, 0.5f); + + CreateCrashSphere(D3DVECTOR( 13.0f, 3.0f, 13.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 11.0f, 15.0f, 11.0f), 2.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-13.0f, 3.0f, 13.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-11.0f, 15.0f, -11.0f), 2.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 13.0f, 3.0f, -13.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 11.0f, 15.0f, -11.0f), 2.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-13.0f, 3.0f, -13.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-11.0f, 15.0f, -11.0f), 2.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 26.0f, 0.0f), 9.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 54.0f, 0.0f), 14.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 20.0f); + + CreateShadowCircle(21.0f, 1.0f); + m_showLimitRadius = BLITZPARA; + } + + if ( m_type == OBJECT_SAFE ) + { + pModFile->ReadModel("objects\\safe1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\safe2.mod"); + pModFile->CreateEngineObject(rank); + SetZoom(1, 1.05f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(2, rank); + SetObjectParent(2, 0); + pModFile->ReadModel("objects\\safe3.mod"); + pModFile->CreateEngineObject(rank); + SetZoom(2, 1.05f); + + m_terrain->AddBuildingLevel(pos, 18.0f, 20.0f, 1.0f, 0.5f); + + CreateCrashSphere(D3DVECTOR(0.0f, 1.0f, 0.0f), 13.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 1.0f, 0.0f), 13.0f); + + CreateShadowCircle(23.0f, 1.0f); + } + + if ( m_type == OBJECT_HUSTON ) + { + pModFile->ReadModel("objects\\huston1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\huston2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(0.0f, 39.0f, 30.0f)); + SetAngleY(1, -PI/2.0f); + SetZoom(1, 3.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(2, rank); + SetObjectParent(2, 1); + pModFile->ReadModel("objects\\huston3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(2, D3DVECTOR(0.0f, 4.5f, 1.9f)); + + CreateCrashSphere(D3DVECTOR( 15.0f, 6.0f, -53.0f), 16.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-15.0f, 6.0f, -53.0f), 16.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 15.0f, 6.0f, -26.0f), 16.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-15.0f, 6.0f, -26.0f), 16.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 15.0f, 6.0f, 0.0f), 16.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-15.0f, 6.0f, 0.0f), 16.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 15.0f, 6.0f, 26.0f), 16.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-15.0f, 6.0f, 26.0f), 16.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 15.0f, 6.0f, 53.0f), 16.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-15.0f, 6.0f, 53.0f), 16.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 27.0f, 30.0f), 12.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 45.0f, 30.0f), 14.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 26.0f, 4.0f, -61.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-26.0f, 4.0f, -61.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 26.0f, 4.0f, 61.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-26.0f, 4.0f, 61.0f), 5.0f, SOUND_BOUMm, 0.45f); + } + + if ( m_type == OBJECT_TARGET1 ) + { + pModFile->ReadModel("objects\\target1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, 1.5f); + SetFloorHeight(0.0f); + + CreateCrashSphere(D3DVECTOR( 0.0f, 50.0f+14.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -7.0f, 50.0f+12.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 7.0f, 50.0f+12.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-12.0f, 50.0f+ 7.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 12.0f, 50.0f+ 7.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-14.0f, 50.0f+ 0.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 14.0f, 50.0f+ 0.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-12.0f, 50.0f- 7.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 12.0f, 50.0f- 7.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -7.0f, 50.0f-12.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 7.0f, 50.0f-12.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 50.0f-14.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + + CreateCrashSphere(D3DVECTOR(0.0f, 30.0f, 0.0f), 2.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 24.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 16.0f, 0.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 8.0f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(15.0f, 1.0f); + } + + if ( m_type == OBJECT_TARGET2 ) + { + pModFile->ReadModel("objects\\target2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + height += 50.0f*1.5f; + } + + if ( m_type == OBJECT_NEST ) + { + pModFile->ReadModel("objects\\nest.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + m_terrain->AddBuildingLevel(pos, 3.0f, 5.0f, 1.0f, 0.5f); + + CreateShadowCircle(4.0f, 1.0f); + } + + if ( m_type == OBJECT_START ) + { + pModFile->ReadModel("objects\\start.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + m_terrain->AddBuildingLevel(pos, 7.0f, 9.0f, 1.0f, 0.5f); + } + + if ( m_type == OBJECT_END ) + { + pModFile->ReadModel("objects\\end.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + m_terrain->AddBuildingLevel(pos, 7.0f, 9.0f, 1.0f, 0.5f); + } + +#if 0 + if ( power > 0.0f ) // crée une pile ? + { + CObject* pPower; + + pPower = new CObject(m_iMan); + pPower->SetType(power<=1.0f?OBJECT_POWER:OBJECT_ATOMIC); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + pPower->SetObjectRank(0, rank); + + if ( power <= 1.0f ) pModFile->ReadModel("objects\\power.mod"); + else pModFile->ReadModel("objects\\atomic.mod"); + pModFile->CreateEngineObject(rank); + + pPower->SetPosition(0, RetCharacter()->posPower); + pPower->CreateCrashSphere(D3DVECTOR(0.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + pPower->SetGlobalSphere(D3DVECTOR(0.0f, 1.0f, 0.0f), 1.5f); + + pPower->SetTruck(this); + SetPower(pPower); + + if ( power <= 1.0f ) pPower->SetEnergy(power); + else pPower->SetEnergy(power/100.0f); + } +#endif + + pos = RetPosition(0); + pos.y += height; + SetPosition(0, pos); // pour afficher les ombres tout de suite + + CreateOtherObject(type); + m_engine->LoadAllTexture(); + + delete pModFile; + return TRUE; +} + +// Crée une petite ressource posée sur le sol. + +BOOL CObject::CreateResource(D3DVECTOR pos, float angle, ObjectType type, + float power) +{ + CModFile* pModFile; + char name[50]; + int rank; + float radius, height; + + if ( type != OBJECT_SHOW ) + { + if ( m_engine->RetRestCreate() < 1 ) return FALSE; + } + + pModFile = new CModFile(m_iMan); + + SetType(type); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); // c'est un objet fixe + SetObjectRank(0, rank); + SetEnergy(power); + + name[0] = 0; + if ( type == OBJECT_STONE ) strcpy(name, "objects\\stone.mod"); + if ( type == OBJECT_URANIUM ) strcpy(name, "objects\\uranium.mod"); + if ( type == OBJECT_METAL ) strcpy(name, "objects\\metal.mod"); + if ( type == OBJECT_POWER ) strcpy(name, "objects\\power.mod"); + if ( type == OBJECT_ATOMIC ) strcpy(name, "objects\\atomic.mod"); + if ( type == OBJECT_BULLET ) strcpy(name, "objects\\bullet.mod"); + if ( type == OBJECT_BBOX ) strcpy(name, "objects\\bbox.mod"); + if ( type == OBJECT_KEYa ) strcpy(name, "objects\\keya.mod"); + if ( type == OBJECT_KEYb ) strcpy(name, "objects\\keyb.mod"); + if ( type == OBJECT_KEYc ) strcpy(name, "objects\\keyc.mod"); + if ( type == OBJECT_KEYd ) strcpy(name, "objects\\keyd.mod"); + if ( type == OBJECT_TNT ) strcpy(name, "objects\\tnt.mod"); + if ( type == OBJECT_SCRAP1 ) strcpy(name, "objects\\scrap1.mod"); + if ( type == OBJECT_SCRAP2 ) strcpy(name, "objects\\scrap2.mod"); + if ( type == OBJECT_SCRAP3 ) strcpy(name, "objects\\scrap3.mod"); + if ( type == OBJECT_SCRAP4 ) strcpy(name, "objects\\scrap4.mod"); + if ( type == OBJECT_SCRAP5 ) strcpy(name, "objects\\scrap5.mod"); + if ( type == OBJECT_BOMB ) strcpy(name, "objects\\bomb.mod"); + if ( type == OBJECT_WAYPOINT ) strcpy(name, "objects\\waypoint.mod"); + if ( type == OBJECT_SHOW ) strcpy(name, "objects\\show.mod"); + if ( type == OBJECT_WINFIRE ) strcpy(name, "objects\\winfire.mod"); + if ( type == OBJECT_BAG ) strcpy(name, "objects\\bag.mod"); + if ( type == OBJECT_MARKSTONE ) strcpy(name, "objects\\cross1.mod"); + if ( type == OBJECT_MARKURANIUM ) strcpy(name, "objects\\cross3.mod"); + if ( type == OBJECT_MARKPOWER ) strcpy(name, "objects\\cross2.mod"); + if ( type == OBJECT_MARKKEYa ) strcpy(name, "objects\\crossa.mod"); + if ( type == OBJECT_MARKKEYb ) strcpy(name, "objects\\crossb.mod"); + if ( type == OBJECT_MARKKEYc ) strcpy(name, "objects\\crossc.mod"); + if ( type == OBJECT_MARKKEYd ) strcpy(name, "objects\\crossd.mod"); + if ( type == OBJECT_EGG ) strcpy(name, "objects\\egg.mod"); + + pModFile->ReadModel(name); + pModFile->CreateEngineObject(rank); + + SetPosition(0, pos); + SetAngleY(0, angle); + + if ( type == OBJECT_SHOW ) // reste en l'air ? + { + delete pModFile; + return TRUE; + } + + radius = 1.5f; + height = 0.0f; + + if ( type == OBJECT_MARKSTONE || + type == OBJECT_MARKURANIUM || + type == OBJECT_MARKKEYa || + type == OBJECT_MARKKEYb || + type == OBJECT_MARKKEYc || + type == OBJECT_MARKKEYd || + type == OBJECT_MARKPOWER || + type == OBJECT_WAYPOINT ) + { + } + else if ( type == OBJECT_EGG ) + { + CreateCrashSphere(D3DVECTOR(-1.0f, 2.8f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 10.0f); + radius = 3.0f; + } + else if ( type == OBJECT_BOMB ) + { + CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 3.0f); + radius = 3.0f; + } + else if ( type == OBJECT_BAG ) + { + CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 4.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 4.0f); + SetZoom(0, 1.5f); + radius = 5.0f; + height = -1.4f; + } + else + { + CreateCrashSphere(D3DVECTOR(0.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 1.0f, 0.0f), 1.5f); + } + CreateShadowCircle(radius, 1.0f); + + SetFloorHeight(0.0f); + CreateOtherObject(type); + m_engine->LoadAllTexture(); + FloorAdjust(); + + pos = RetPosition(0); + pos.y += height; + SetPosition(0, pos); // pour afficher les ombres tout de suite + + delete pModFile; + return TRUE; +} + +// Crée un drapeau posé sur le sol. + +BOOL CObject::CreateFlag(D3DVECTOR pos, float angle, ObjectType type) +{ + CModFile* pModFile; + char name[50]; + int rank, i; + + if ( m_engine->RetRestCreate() < 1+4 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + SetType(type); + + name[0] = 0; + if ( type == OBJECT_FLAGb ) strcpy(name, "objects\\flag1b.mod"); + if ( type == OBJECT_FLAGr ) strcpy(name, "objects\\flag1r.mod"); + if ( type == OBJECT_FLAGg ) strcpy(name, "objects\\flag1g.mod"); + if ( type == OBJECT_FLAGy ) strcpy(name, "objects\\flag1y.mod"); + if ( type == OBJECT_FLAGv ) strcpy(name, "objects\\flag1v.mod"); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); // c'est un objet fixe + SetObjectRank(0, rank); + pModFile->ReadModel(name); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + name[0] = 0; + if ( type == OBJECT_FLAGb ) strcpy(name, "objects\\flag2b.mod"); + if ( type == OBJECT_FLAGr ) strcpy(name, "objects\\flag2r.mod"); + if ( type == OBJECT_FLAGg ) strcpy(name, "objects\\flag2g.mod"); + if ( type == OBJECT_FLAGy ) strcpy(name, "objects\\flag2y.mod"); + if ( type == OBJECT_FLAGv ) strcpy(name, "objects\\flag2v.mod"); + + for ( i=0 ; i<4 ; i++ ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1+i, rank); + SetObjectParent(1+i, i); + pModFile->ReadModel(name); + pModFile->CreateEngineObject(rank); + if ( i == 0 ) SetPosition(1+i, D3DVECTOR(0.15f, 5.0f, 0.0f)); + else SetPosition(1+i, D3DVECTOR(0.79f, 0.0f, 0.0f)); + } + + SetJotlerSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 1.0f); + CreateShadowCircle(2.0f, 0.3f); + + SetFloorHeight(0.0f); + CreateOtherObject(type); + m_engine->LoadAllTexture(); + FloorAdjust(); + + pos = RetPosition(0); + SetPosition(0, pos); // pour afficher les ombres tout de suite + + delete pModFile; + return TRUE; +} + +// Crée une barrière posée sur le sol. + +BOOL CObject::CreateBarrier(D3DVECTOR pos, float angle, float height, + ObjectType type) +{ + CModFile* pModFile; + int rank; + + if ( m_engine->RetRestCreate() < 1 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + SetType(type); + + if ( type == OBJECT_BARRIER0 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\barrier0.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR( 3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(6.0f, 0.5f, D3DSHADOWWORM); + } + + if ( type == OBJECT_BARRIER1 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\barrier1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR( 8.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-8.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(12.0f, 0.5f, D3DSHADOWWORM); + } + + if ( type == OBJECT_BARRIER2 ) // en carton ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\barrier2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR( 8.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-8.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(12.0f, 0.8f, D3DSHADOWWORM); + } + + if ( type == OBJECT_BARRIER3 ) // allumettes + paille ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\barrier3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR( 8.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-3.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-8.5f, 3.0f, 0.0f), 0.7f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(10.0f, 0.5f, D3DSHADOWWORM); + } + + pos = RetPosition(0); + SetPosition(0, pos); // pour afficher les ombres tout de suite + + SetFloorHeight(0.0f); + CreateOtherObject(type); + FloorAdjust(); + + pos = RetPosition(0); + pos.y += height; + SetPosition(0, pos); + + delete pModFile; + return TRUE; +} + +// Crée une plante posée sur le sol. + +BOOL CObject::CreatePlant(D3DVECTOR pos, float angle, float height, + ObjectType type) +{ + CModFile* pModFile; + int rank; + + if ( m_engine->RetRestCreate() < 1 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + SetType(type); + + if ( type == OBJECT_PLANT0 || + type == OBJECT_PLANT1 || + type == OBJECT_PLANT2 || + type == OBJECT_PLANT3 || + type == OBJECT_PLANT4 ) // standard ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + if ( type == OBJECT_PLANT0 ) pModFile->ReadModel("objects\\plant0.mod"); + if ( type == OBJECT_PLANT1 ) pModFile->ReadModel("objects\\plant1.mod"); + if ( type == OBJECT_PLANT2 ) pModFile->ReadModel("objects\\plant2.mod"); + if ( type == OBJECT_PLANT3 ) pModFile->ReadModel("objects\\plant3.mod"); + if ( type == OBJECT_PLANT4 ) pModFile->ReadModel("objects\\plant4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + height -= 2.0f; + + CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 4.0f, SOUND_BOUM, 0.10f); + SetGlobalSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 6.0f); + SetJotlerSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 8.0f); + + CreateShadowCircle(8.0f, 0.5f); + } + + if ( type == OBJECT_PLANT5 || + type == OBJECT_PLANT6 || + type == OBJECT_PLANT7 ) // trèfle ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + if ( type == OBJECT_PLANT5 ) pModFile->ReadModel("objects\\plant5.mod"); + if ( type == OBJECT_PLANT6 ) pModFile->ReadModel("objects\\plant6.mod"); + if ( type == OBJECT_PLANT7 ) pModFile->ReadModel("objects\\plant7.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + +//? CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 3.0f, SOUND_BOUM, 0.10f); + SetJotlerSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 4.0f); + + CreateShadowCircle(5.0f, 0.3f); + } + + if ( type == OBJECT_PLANT8 || + type == OBJECT_PLANT9 ) // courgette ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + if ( type == OBJECT_PLANT8 ) pModFile->ReadModel("objects\\plant8.mod"); + if ( type == OBJECT_PLANT9 ) pModFile->ReadModel("objects\\plant9.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 4.0f, SOUND_BOUM, 0.10f); + CreateCrashSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 4.0f, SOUND_BOUM, 0.10f); + + CreateShadowCircle(10.0f, 0.5f); + } + + if ( type == OBJECT_PLANT10 || + type == OBJECT_PLANT11 || + type == OBJECT_PLANT12 || + type == OBJECT_PLANT13 || + type == OBJECT_PLANT14 ) // plante grasse ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + if ( type == OBJECT_PLANT10 ) pModFile->ReadModel("objects\\plant10.mod"); + if ( type == OBJECT_PLANT11 ) pModFile->ReadModel("objects\\plant11.mod"); + if ( type == OBJECT_PLANT12 ) pModFile->ReadModel("objects\\plant12.mod"); + if ( type == OBJECT_PLANT13 ) pModFile->ReadModel("objects\\plant13.mod"); + if ( type == OBJECT_PLANT14 ) pModFile->ReadModel("objects\\plant14.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR(0.0f, 12.0f, 0.0f), 5.0f, SOUND_BOUM, 0.10f); + SetGlobalSphere(D3DVECTOR(0.0f, 6.0f, 0.0f), 6.0f); + SetJotlerSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 8.0f); + + CreateShadowCircle(8.0f, 0.3f); + } + + if ( type == OBJECT_PLANT15 || + type == OBJECT_PLANT16 || + type == OBJECT_PLANT17 || + type == OBJECT_PLANT18 || + type == OBJECT_PLANT19 ) // fougère ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + if ( type == OBJECT_PLANT15 ) pModFile->ReadModel("objects\\plant15.mod"); + if ( type == OBJECT_PLANT16 ) pModFile->ReadModel("objects\\plant16.mod"); + if ( type == OBJECT_PLANT17 ) pModFile->ReadModel("objects\\plant17.mod"); + if ( type == OBJECT_PLANT18 ) pModFile->ReadModel("objects\\plant18.mod"); + if ( type == OBJECT_PLANT19 ) pModFile->ReadModel("objects\\plant19.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + if ( type != OBJECT_PLANT19 ) + { + CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 4.0f, SOUND_BOUM, 0.10f); + SetGlobalSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 6.0f); + } + SetJotlerSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 8.0f); + + CreateShadowCircle(8.0f, 0.5f); + } + + if ( type == OBJECT_TREE0 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\tree0.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR( 0.0f, 3.0f, 2.0f), 3.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR(-1.0f, 10.0f, 1.0f), 2.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR( 0.0f, 17.0f, 0.0f), 2.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR( 1.0f, 27.0f, 0.0f), 2.0f, SOUND_BOUMs, 0.20f); + + CreateShadowCircle(8.0f, 0.5f); + } + + if ( type == OBJECT_TREE1 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\tree1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR( 0.0f, 3.0f, 2.0f), 3.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR(-2.0f, 11.0f, 1.0f), 2.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR(-2.0f, 19.0f, 2.0f), 2.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR( 2.0f, 26.0f, 0.0f), 2.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR( 2.0f, 34.0f,-2.0f), 2.0f, SOUND_BOUMs, 0.20f); + + CreateShadowCircle(8.0f, 0.5f); + } + + if ( type == OBJECT_TREE2 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\tree2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR( 0.0f, 3.0f, 1.0f), 3.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR(-2.0f, 10.0f, 1.0f), 2.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR(-2.0f, 19.0f, 2.0f), 2.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR( 2.0f, 25.0f, 0.0f), 2.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR( 3.0f, 32.0f,-2.0f), 2.0f, SOUND_BOUMs, 0.20f); + + CreateShadowCircle(8.0f, 0.5f); + } + + if ( type == OBJECT_TREE3 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\tree3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR(-2.0f, 3.0f, 2.0f), 3.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR(-3.0f, 9.0f, 1.0f), 2.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR( 0.0f, 18.0f, 0.0f), 2.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR( 0.0f, 27.0f, 7.0f), 2.0f, SOUND_BOUMs, 0.20f); + + CreateShadowCircle(8.0f, 0.5f); + } + + if ( type == OBJECT_TREE4 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\tree4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 10.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR(0.0f, 21.0f, 0.0f), 8.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR(0.0f, 32.0f, 0.0f), 7.0f, SOUND_BOUMs, 0.20f); + + CreateShadowCircle(8.0f, 0.5f); + } + + if ( type == OBJECT_TREE5 ) // arbre géant (pour monde "teen") + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\tree5.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR( 0.0f, 5.0f,-10.0f), 25.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR(-65.0f, 5.0f, 65.0f), 20.0f, SOUND_BOUMs, 0.20f); + CreateCrashSphere(D3DVECTOR( 38.0f, 5.0f, 21.0f), 18.0f, SOUND_BOUMs, 0.20f); + + CreateShadowCircle(50.0f, 0.5f); + } + + pos = RetPosition(0); + SetPosition(0, pos); // pour afficher les ombres tout de suite + + SetFloorHeight(0.0f); + CreateOtherObject(type); + + pos = RetPosition(0); + pos.y += height; + SetPosition(0, pos); + + delete pModFile; + return TRUE; +} + +// Crée un champignon posé sur le sol. + +BOOL CObject::CreateMushroom(D3DVECTOR pos, float angle, float height, + ObjectType type) +{ + CModFile* pModFile; + int rank; + + if ( m_engine->RetRestCreate() < 1 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + SetType(type); + + if ( type == OBJECT_MUSHROOM1 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\mush1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 3.0f, SOUND_BOUM, 0.10f); + SetGlobalSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 5.5f); + SetJotlerSphere(D3DVECTOR(0.0f, 3.0f, 0.0f), 5.5f); + + CreateShadowCircle(6.0f, 0.5f); + } + + if ( type == OBJECT_MUSHROOM2 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\mush2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 3.0f, SOUND_BOUM, 0.10f); + SetGlobalSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 5.5f); + SetJotlerSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 5.5f); + + CreateShadowCircle(5.0f, 0.5f); + } + + pos = RetPosition(0); + SetPosition(0, pos); // pour afficher les ombres tout de suite + + SetFloorHeight(0.0f); + CreateOtherObject(type); + + pos = RetPosition(0); + pos.y += height; + SetPosition(0, pos); + + delete pModFile; + return TRUE; +} + +// Crée un jouet posé sur le sol. + +BOOL CObject::CreateTeen(D3DVECTOR pos, float angle, float zoom, float height, + ObjectType type) +{ + CModFile* pModFile; + D3DMATRIX* mat; + D3DCOLORVALUE color; + int rank; + float fShadow; + BOOL bFloorAdjust = TRUE; + + if ( m_engine->RetRestCreate() < 1 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + SetType(type); + + fShadow = Norm(1.0f-height/10.0f); + + if ( type == OBJECT_TEEN0 ) // crayon orange lg=10 + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen0.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR( 5.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 2.5f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-2.5f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-5.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(5.0f, 0.8f*fShadow, D3DSHADOWWORM); + } + + if ( type == OBJECT_TEEN1 ) // crayon bleu lg=14 + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR( 6.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 2.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-2.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-4.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-6.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(6.0f, 0.8f*fShadow, D3DSHADOWWORM); + } + + if ( type == OBJECT_TEEN2 ) // crayon rouge lg=16 + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR( 7.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.7f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 2.3f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-2.3f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-4.7f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-7.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(6.0f, 0.8f*fShadow, D3DSHADOWWORM); + } + + if ( type == OBJECT_TEEN3 ) // bocal avec crayon + { + rank = m_engine->CreateObject(); +//? m_engine->SetObjectType(rank, TYPEFIX); + m_engine->SetObjectType(rank, TYPEMETAL); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f, 0.0f), 4.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 4.0f); + CreateShadowCircle(6.0f, 0.5f*fShadow); + } + + if ( type == OBJECT_TEEN4 ) // ciseaux + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-9.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-6.0f, 1.0f, 0.0f), 1.1f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-3.0f, 1.0f, 0.0f), 1.2f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 1.0f, 0.0f), 1.3f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 5.1f, 1.0f,-1.3f), 2.6f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 8.0f, 1.0f, 2.2f), 2.3f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 9.4f, 1.0f,-2.0f), 2.0f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(10.0f, 0.5f*fShadow, D3DSHADOWWORM); + } + + if ( type == OBJECT_TEEN5 ) // CD + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen5.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + SetFloorHeight(0.0f); + bFloorAdjust = FALSE; + + m_terrain->AddBuildingLevel(pos, 5.9f, 6.1f, 0.2f, 0.5f); + CreateShadowCircle(8.0f, 0.2f*fShadow); + } + + if ( type == OBJECT_TEEN6 ) // livre 1 + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen6.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f, 7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f, 7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f,-7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f,-7.5f), 5.0f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(20.0f, 0.2f*fShadow); + } + + if ( type == OBJECT_TEEN7 ) // livre 2 + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen7.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f, 7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f, 7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f,-7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f,-7.5f), 5.0f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(20.0f, 0.2f*fShadow); + } + + if ( type == OBJECT_TEEN8 ) // pile de livres 1 + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen8.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f, 7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f, 7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f,-7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f,-7.5f), 5.0f, SOUND_BOUMm, 0.45f); + + SetGlobalSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 12.0f); + CreateShadowCircle(20.0f, 0.2f*fShadow); + } + + if ( type == OBJECT_TEEN9 ) // pile de livres 2 + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen9.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f, 7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f, 7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-5.0f, 3.0f,-7.5f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 4.5f, 3.0f,-7.5f), 5.0f, SOUND_BOUMm, 0.45f); + + SetGlobalSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 12.0f); + CreateShadowCircle(20.0f, 0.2f*fShadow); + } + + if ( type == OBJECT_TEEN10 ) // bibliothèque + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen10.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-26.0f, 3.0f, 0.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-15.0f, 3.0f,-4.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-15.0f, 3.0f, 5.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -4.0f, 3.0f,-4.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -4.0f, 3.0f, 5.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 6.0f, 3.0f,-4.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 6.0f, 3.0f, 4.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 14.0f, 3.0f,-3.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 14.0f, 3.0f, 2.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 24.0f, 3.0f, 5.0f), 6.0f, SOUND_BOUMm, 0.45f); + + SetGlobalSphere(D3DVECTOR(0.0f, 6.0f, 0.0f), 20.0f); + CreateShadowCircle(40.0f, 0.2f*fShadow); + } + + if ( type == OBJECT_TEEN11 ) // lampe + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen11.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + SetZoom(0, zoom); + + mat = RetWorldMatrix(0); + pos = Transform(*mat, D3DVECTOR(-56.0f, 22.0f, 0.0f)); + m_particule->CreateParticule(pos, D3DVECTOR(0.0f, 0.0f, 0.0f), FPOINT(20.0f, 20.0f), PARTISELY, 1.0f, 0.0f, 0.0f); + + pos = Transform(*mat, D3DVECTOR(-65.0f, 40.0f, 0.0f)); + color.r = 4.0f; + color.g = 2.0f; + color.b = 0.0f; // jaune-orange + color.a = 0.0f; + m_main->CreateSpot(pos, color); + } + + if ( type == OBJECT_TEEN12 ) // coca + { + rank = m_engine->CreateObject(); +//? m_engine->SetObjectType(rank, TYPEFIX); + m_engine->SetObjectType(rank, TYPEMETAL); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen12.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f, 0.0f), 4.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 9.0f, 0.0f), 5.0f); + CreateShadowCircle(4.5f, 1.0f*fShadow); + } + + if ( type == OBJECT_TEEN13 ) // carton fermé + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen13.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-10.0f, 4.0f,-7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f,-7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 4.0f,-7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 4.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 4.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 4.0f, 7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f, 7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 4.0f, 7.0f), 5.0f, SOUND_BOUMm, 0.45f); + + SetGlobalSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 15.0f); + CreateShadowCircle(20.0f, 1.0f*fShadow); + } + + if ( type == OBJECT_TEEN14 ) // carton ouvert + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen14.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-10.0f, 4.0f,-7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f,-7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 4.0f,-7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 4.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 4.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 4.0f, 7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f, 7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 4.0f, 7.0f), 5.0f, SOUND_BOUMm, 0.45f); + + SetGlobalSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 15.0f); + CreateShadowCircle(20.0f, 1.0f*fShadow); + } + + if ( type == OBJECT_TEEN15 ) // pile de cartons + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen15.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-10.0f, 4.0f,-7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f,-7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 4.0f,-7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 4.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 4.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 4.0f, 7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f, 7.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 4.0f, 7.0f), 5.0f, SOUND_BOUMm, 0.45f); + + SetGlobalSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 15.0f); + CreateShadowCircle(20.0f, 1.0f*fShadow); + } + + if ( type == OBJECT_TEEN16 ) // arrosoir + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen16.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-8.0f, 4.0f, 0.0f), 12.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 8.0f, 4.0f, 0.0f), 12.0f, SOUND_BOUMm, 0.45f); + + SetGlobalSphere(D3DVECTOR(0.0f, 13.0f, 0.0f), 20.0f); + CreateShadowCircle(18.0f, 1.0f*fShadow); + } + + if ( type == OBJECT_TEEN17 ) // roue | + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen17.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR( 0.0f, 31.0f, 0.0f), 31.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 31.0f, 0.0f), 31.0f); + CreateShadowCircle(24.0f, 0.5f*fShadow); + } + + if ( type == OBJECT_TEEN18 ) // roue / + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen18.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR( 0.0f, 31.0f, 0.0f), 31.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 31.0f, 0.0f), 31.0f); + CreateShadowCircle(24.0f, 0.5f*fShadow); + } + + if ( type == OBJECT_TEEN19 ) // roue = + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen19.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR( 0.0f, 10.0f, 0.0f), 32.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 32.0f); + CreateShadowCircle(33.0f, 1.0f*fShadow); + } + + if ( type == OBJECT_TEEN20 ) // mur avec étagère + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen20.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-175.0f, 0.0f, -5.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-175.0f, 0.0f, -35.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -55.0f, 0.0f, -5.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -55.0f, 0.0f, -35.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -37.0f, 0.0f, -5.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -37.0f, 0.0f, -35.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 83.0f, 0.0f, -5.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 83.0f, 0.0f, -35.0f), 4.0f, SOUND_BOUMm, 0.45f); + } + + if ( type == OBJECT_TEEN21 ) // mur avec fenêtre + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen21.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + } + + if ( type == OBJECT_TEEN22 ) // mur avec porte et étagère + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen22.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-135.0f, 0.0f, -5.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-135.0f, 0.0f, -35.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -15.0f, 0.0f, -5.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -15.0f, 0.0f, -35.0f), 4.0f, SOUND_BOUMm, 0.45f); + } + + if ( type == OBJECT_TEEN23 ) // skate sur ses roues + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen23.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + if ( m_option == 1 ) // passage sous le skate interdit ? + { + CreateCrashSphere(D3DVECTOR(-10.0f, 2.0f, 0.0f), 11.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 10.0f, 2.0f, 0.0f), 11.0f, SOUND_BOUMm, 0.45f); + } + + CreateCrashSphere(D3DVECTOR(-23.0f, 2.0f, 7.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-23.0f, 2.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-23.0f, 2.0f,-7.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 23.0f, 2.0f, 7.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 23.0f, 2.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 23.0f, 2.0f,-7.0f), 3.0f, SOUND_BOUMm, 0.45f); + + CreateShadowCircle(35.0f, 0.8f*fShadow, D3DSHADOWWORM); + } + + if ( type == OBJECT_TEEN24 ) // skate / + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen24.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-12.0f, 0.0f, -3.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-12.0f, 0.0f, 3.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateShadowCircle(20.0f, 0.2f*fShadow); + } + + if ( type == OBJECT_TEEN25 ) // skate / + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen25.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-12.0f, 0.0f, -3.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-12.0f, 0.0f, 3.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateShadowCircle(20.0f, 0.2f*fShadow); + } + + if ( type == OBJECT_TEEN26 ) // lampe au plafond + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen26.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + SetFloorHeight(0.0f); + + mat = RetWorldMatrix(0); + pos = Transform(*mat, D3DVECTOR(0.0f, 50.0f, 0.0f)); + m_particule->CreateParticule(pos, D3DVECTOR(0.0f, 0.0f, 0.0f), FPOINT(100.0f, 100.0f), PARTISELY, 1.0f, 0.0f, 0.0f); + + pos = Transform(*mat, D3DVECTOR(0.0f, 50.0f, 0.0f)); + color.r = 4.0f; + color.g = 2.0f; + color.b = 0.0f; // jaune-orange + color.a = 0.0f; + m_main->CreateSpot(pos, color); + } + + if ( type == OBJECT_TEEN27 ) // grande plante ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen27.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 4.0f, SOUND_BOUM, 0.10f); + CreateShadowCircle(40.0f, 0.5f); + } + + if ( type == OBJECT_TEEN28 ) // bouteille ? + { + rank = m_engine->CreateObject(); +//? m_engine->SetObjectType(rank, TYPEFIX); + m_engine->SetObjectType(rank, TYPEMETAL); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen28.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 5.0f, SOUND_BOUM, 0.10f); + CreateShadowCircle(7.0f, 0.6f*fShadow); + } + + if ( type == OBJECT_TEEN29 ) // pont ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen29.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + bFloorAdjust = FALSE; + } + + if ( type == OBJECT_TEEN30 ) // saut ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen30.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 15.0f, SOUND_BOUM, 0.10f); + SetGlobalSphere(D3DVECTOR(0.0f, 15.0f, 0.0f), 17.0f); + CreateShadowCircle(20.0f, 1.0f*fShadow); + } + + if ( type == OBJECT_TEEN31 ) // basket ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen31.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-10.0f, 2.0f, 0.0f), 5.0f, SOUND_BOUM, 0.10f); + CreateCrashSphere(D3DVECTOR( 0.0f, 2.0f, 0.0f), 6.0f, SOUND_BOUM, 0.10f); + CreateCrashSphere(D3DVECTOR( 9.0f, 4.0f, 1.0f), 6.0f, SOUND_BOUM, 0.10f); + + SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 10.0f); + CreateShadowCircle(16.0f, 0.6f*fShadow); + } + + if ( type == OBJECT_TEEN32 ) // chaise ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen32.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR( 17.5f, 1.0f, 17.5f), 3.5f, SOUND_BOUM, 0.10f); + CreateCrashSphere(D3DVECTOR( 17.5f, 1.0f, -17.5f), 3.5f, SOUND_BOUM, 0.10f); + CreateCrashSphere(D3DVECTOR(-17.5f, 1.0f, 17.5f), 3.5f, SOUND_BOUM, 0.10f); + CreateCrashSphere(D3DVECTOR(-17.5f, 1.0f, -17.5f), 3.5f, SOUND_BOUM, 0.10f); + SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 26.0f); + CreateShadowCircle(35.0f, 0.3f*fShadow); + } + + if ( type == OBJECT_TEEN33 ) // panneau ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen33.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 4.0f, SOUND_BOUM, 0.10f); + CreateShadowCircle(10.0f, 0.3f*fShadow); + } + + if ( type == OBJECT_TEEN34 ) // caillou ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen34.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 4.0f, SOUND_BOUM, 0.10f); + CreateShadowCircle(3.0f, 1.0f*fShadow); + } + + if ( type == OBJECT_TEEN35 ) // tuyau ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen35.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(-40.0f, 5.0f, 0.0f), 10.0f, SOUND_BOUM, 0.10f); + CreateCrashSphere(D3DVECTOR(-20.0f, 5.0f, 0.0f), 10.0f, SOUND_BOUM, 0.10f); + CreateCrashSphere(D3DVECTOR( 0.0f, 5.0f, 0.0f), 10.0f, SOUND_BOUM, 0.10f); + CreateCrashSphere(D3DVECTOR( 20.0f, 5.0f, 0.0f), 10.0f, SOUND_BOUM, 0.10f); + CreateCrashSphere(D3DVECTOR( 40.0f, 5.0f, 0.0f), 10.0f, SOUND_BOUM, 0.10f); + CreateShadowCircle(40.0f, 0.8f*fShadow, D3DSHADOWWORM); + } + + if ( type == OBJECT_TEEN36 ) // tronc ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen36.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + bFloorAdjust = FALSE; + } + + if ( type == OBJECT_TEEN37 ) // bateau ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen37.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + bFloorAdjust = FALSE; + } + + if ( type == OBJECT_TEEN38 ) // ventillateur ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen38a.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\teen38b.mod"); // moteur + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(0.0f, 30.0f, 0.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(2, rank); + SetObjectParent(2, 1); + pModFile->ReadModel("objects\\teen38c.mod"); // hélice + pModFile->CreateEngineObject(rank); + SetPosition(2, D3DVECTOR(0.0f, 0.0f, 0.0f)); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 10.0f, SOUND_BOUM, 0.10f); + SetGlobalSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 10.0f); + CreateShadowCircle(15.0f, 0.5f*fShadow); + } + + if ( type == OBJECT_TEEN39 ) // plante en pot ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen39.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 8.5f, SOUND_BOUM, 0.10f); + SetGlobalSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 8.5f); + CreateShadowCircle(10.0f, 1.0f*fShadow); + } + + if ( type == OBJECT_TEEN40 ) // ballon ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen40.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 11.0f, SOUND_BOUM, 0.10f); + SetGlobalSphere(D3DVECTOR(0.0f, 14.0f, 0.0f), 15.0f); + CreateShadowCircle(15.0f, 0.7f*fShadow); + } + + if ( type == OBJECT_TEEN41 ) // clôture ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen41.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + } + + if ( type == OBJECT_TEEN42 ) // trèfle ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen42.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 2.0f, SOUND_BOUM, 0.10f); + CreateShadowCircle(15.0f, 0.4f*fShadow); + } + + if ( type == OBJECT_TEEN43 ) // trèfle ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen43.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 2.0f, SOUND_BOUM, 0.10f); + CreateShadowCircle(15.0f, 0.4f*fShadow); + } + + if ( type == OBJECT_TEEN44 ) // caisse ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\teen44.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, zoom); + + CreateCrashSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 55.0f, SOUND_BOUM, 0.10f); + SetGlobalSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 55.0f); + CreateShadowCircle(55.0f, 1.0f*fShadow); + } + + pos = RetPosition(0); + SetPosition(0, pos); // pour afficher les ombres tout de suite + + if ( bFloorAdjust ) + { + SetFloorHeight(0.0f); + FloorAdjust(); + } + + CreateOtherObject(type); + + pos = RetPosition(0); + pos.y += height; + SetPosition(0, pos); + + delete pModFile; + return TRUE; +} + +// Crée un quartz posé sur le sol. + +BOOL CObject::CreateQuartz(D3DVECTOR pos, float angle, float height, + ObjectType type) +{ + CModFile* pModFile; + float radius; + int rank; + + if ( m_engine->RetRestCreate() < 1 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + SetType(type); + + if ( type == OBJECT_QUARTZ0 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEQUARTZ); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\quartz0.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 3.5f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 3.5f); + + CreateShadowCircle(4.0f, 0.5f); + } + if ( type == OBJECT_QUARTZ1 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEQUARTZ); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\quartz1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 5.0f); + + CreateShadowCircle(5.0f, 0.5f); + } + if ( type == OBJECT_QUARTZ2 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEQUARTZ); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\quartz2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR(0.0f, 6.0f, 0.0f), 6.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 6.0f, 0.0f), 6.0f); + + CreateShadowCircle(6.0f, 0.5f); + } + if ( type == OBJECT_QUARTZ3 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEQUARTZ); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\quartz3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + + CreateCrashSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 10.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 10.0f); + + CreateShadowCircle(10.0f, 0.5f); + } + + pos = RetPosition(0); + SetPosition(0, pos); // pour afficher les ombres tout de suite + + SetFloorHeight(0.0f); + CreateOtherObject(type); + + pos = RetPosition(0); + pos.y += height; + SetPosition(0, pos); + + if ( type == OBJECT_QUARTZ0 ) + { + pos.y += 4.0f; + radius = 2.0f; + } + if ( type == OBJECT_QUARTZ1 ) + { + pos.y += 6.0f; + radius = 4.0f; + } + if ( type == OBJECT_QUARTZ2 ) + { + pos.y += 10.0f; + radius = 5.0f; + } + if ( type == OBJECT_QUARTZ3 ) + { + pos.y += 16.0f; + radius = 8.0f; + } + m_particule->CreateParticule(pos, pos, FPOINT(2.0f, 2.0f), PARTIQUARTZ, 0.7f+Rand()*0.7f, radius, 0.0f); + m_particule->CreateParticule(pos, pos, FPOINT(2.0f, 2.0f), PARTIQUARTZ, 0.7f+Rand()*0.7f, radius, 0.0f); + + delete pModFile; + return TRUE; +} + +// Crée une racine posée sur le sol. + +BOOL CObject::CreateRoot(D3DVECTOR pos, float angle, float height, + ObjectType type) +{ + CModFile* pModFile; + int rank; + + if ( m_engine->RetRestCreate() < 1 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + SetType(type); + + if ( type == OBJECT_ROOT0 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\root0.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, 2.0f); + + CreateCrashSphere(D3DVECTOR(-5.0f, 1.0f, 0.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 4.0f, 1.0f, 2.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 4.0f, 1.0f, -3.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 2.0f, 5.0f, -1.0f), 1.5f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR(-4.0f, 5.0f, -1.0f), 1.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR(-2.0f, 8.0f, -0.5f), 1.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 0.0f, 10.0f, -0.5f), 1.0f, SOUND_BOUMv, 0.15f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 6.0f, 0.0f), 11.0f); + + CreateShadowCircle(16.0f, 0.5f); + } + if ( type == OBJECT_ROOT1 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\root1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, 2.0f); + + CreateCrashSphere(D3DVECTOR(-4.0f, 1.0f, 1.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 0.0f, 1.0f, 2.0f), 1.5f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 3.0f, 1.0f, -2.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR(-2.0f, 5.0f, 1.0f), 1.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 2.0f, 5.0f, 0.0f), 1.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 0.0f, 8.0f, 1.0f), 1.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 0.0f, 12.0f, 1.0f), 1.0f, SOUND_BOUMv, 0.15f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 6.0f, 0.0f), 12.0f); + + CreateShadowCircle(16.0f, 0.5f); + } + if ( type == OBJECT_ROOT2 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\root2.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, 2.0f); + + CreateCrashSphere(D3DVECTOR(-3.0f, 1.0f, 0.5f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 3.0f, 1.0f, -1.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR(-1.0f, 4.5f, 0.0f), 1.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 3.0f, 7.0f, 1.0f), 1.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 0.0f, 7.0f, -1.0f), 1.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 4.0f, 11.0f, 1.0f), 1.0f, SOUND_BOUMv, 0.15f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 6.0f, 0.0f), 10.0f); + + CreateShadowCircle(16.0f, 0.5f); + } + if ( type == OBJECT_ROOT3 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\root3.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, 2.0f); + + CreateCrashSphere(D3DVECTOR(-4.0f, 1.0f, 1.0f), 3.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 4.0f, 1.0f, -3.0f), 3.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 6.0f, 1.0f, 4.0f), 3.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR(-2.5f, 7.0f, 2.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 4.0f, 7.0f, 2.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 3.0f, 6.0f, -1.0f), 1.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 0.0f, 12.0f, 0.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 1.0f, 16.0f, 0.0f), 1.0f, SOUND_BOUMv, 0.15f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 10.0f, 0.0f), 14.0f); + + CreateShadowCircle(22.0f, 0.5f); + } + if ( type == OBJECT_ROOT4 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\root4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, 2.0f); + + CreateCrashSphere(D3DVECTOR( -7.0f, 2.0f, 3.0f), 4.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 5.0f, 2.0f, -6.0f), 4.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 6.0f, 2.0f, 6.0f), 3.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR(-11.0f, 1.0f, -2.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 1.0f, 1.0f, -7.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( -4.0f, 10.0f, 3.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 1.0f, 11.0f, 7.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 3.0f, 11.0f, -3.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( -3.0f, 17.0f, 1.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( -3.0f, 23.0f, -1.0f), 2.0f, SOUND_BOUMv, 0.15f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 12.0f, 0.0f), 20.0f); + + CreateShadowCircle(30.0f, 0.5f); + } + if ( type == OBJECT_ROOT5 ) // gravity root ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\root4.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, 2.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\root5.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(-5.0f, 28.0f, -4.0f)); + SetAngleX(1, -30.0f*PI/180.0f); + SetAngleZ(1, 20.0f*PI/180.0f); + + CreateCrashSphere(D3DVECTOR( -7.0f, 2.0f, 3.0f), 4.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 5.0f, 2.0f, -6.0f), 4.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 6.0f, 2.0f, 6.0f), 3.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR(-11.0f, 1.0f, -2.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 1.0f, 1.0f, -7.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( -4.0f, 10.0f, 3.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 1.0f, 11.0f, 7.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( 3.0f, 11.0f, -3.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( -3.0f, 17.0f, 1.0f), 2.0f, SOUND_BOUMv, 0.15f); + CreateCrashSphere(D3DVECTOR( -3.0f, 23.0f, -1.0f), 2.0f, SOUND_BOUMv, 0.15f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 12.0f, 0.0f), 20.0f); + + CreateShadowCircle(30.0f, 0.5f); + } + + pos = RetPosition(0); + SetPosition(0, pos); // pour afficher les ombres tout de suite + + SetFloorHeight(0.0f); + CreateOtherObject(type); + + pos = RetPosition(0); + pos.y += height; + SetPosition(0, pos); + + delete pModFile; + return TRUE; +} + +// Crée une petite maison. + +BOOL CObject::CreateHome(D3DVECTOR pos, float angle, float height, + ObjectType type) +{ + CModFile* pModFile; + int rank; + + if ( m_engine->RetRestCreate() < 1 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + SetType(type); + + if ( type == OBJECT_HOME1 ) + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\home1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, 1.3f); + + CreateCrashSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 10.0f, SOUND_BOUMs, 0.25f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 6.0f, 0.0f), 11.0f); + CreateShadowCircle(16.0f, 0.5f); + } + + pos = RetPosition(0); + SetPosition(0, pos); // pour afficher les ombres tout de suite + + SetFloorHeight(0.0f); + CreateOtherObject(type); + + pos = RetPosition(0); + pos.y += height; + SetPosition(0, pos); + + delete pModFile; + return TRUE; +} + +// Crée une ruine posée sur le sol. + +BOOL CObject::CreateRuin(D3DVECTOR pos, float angle, float height, + ObjectType type) +{ + CModFile* pModFile; + char name[50]; + int rank; + + if ( m_engine->RetRestCreate() < 1+4 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + SetType(type); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); // c'est un objet fixe + SetObjectRank(0, rank); + + name[0] = 0; + if ( type == OBJECT_RUINmobilew1 ) strcpy(name, "objects\\ruin1.mod"); + if ( type == OBJECT_RUINmobilew2 ) strcpy(name, "objects\\ruin1.mod"); + if ( type == OBJECT_RUINmobilet1 ) strcpy(name, "objects\\ruin2.mod"); + if ( type == OBJECT_RUINmobilet2 ) strcpy(name, "objects\\ruin2.mod"); + if ( type == OBJECT_RUINmobiler1 ) strcpy(name, "objects\\ruin3.mod"); + if ( type == OBJECT_RUINmobiler2 ) strcpy(name, "objects\\ruin3.mod"); + if ( type == OBJECT_RUINfactory ) strcpy(name, "objects\\ruin4.mod"); + if ( type == OBJECT_RUINdoor ) strcpy(name, "objects\\ruin5.mod"); + if ( type == OBJECT_RUINsupport ) strcpy(name, "objects\\ruin6.mod"); + if ( type == OBJECT_RUINradar ) strcpy(name, "objects\\ruin7.mod"); + if ( type == OBJECT_RUINconvert ) strcpy(name, "objects\\ruin8.mod"); + if ( type == OBJECT_RUINbase ) strcpy(name, "objects\\ruin9.mod"); + if ( type == OBJECT_RUINhead ) strcpy(name, "objects\\ruin10.mod"); + + pModFile->ReadModel(name); + pModFile->CreateEngineObject(rank); + + SetPosition(0, pos); + SetAngleY(0, angle); + + if ( type == OBJECT_RUINmobilew1 ) // véhicule à roues ? + { + // Crée la roue arrière-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(6, rank); + SetObjectParent(6, 0); + + pModFile->ReadModel("objects\\ruin1w.mod"); + pModFile->CreateEngineObject(rank); + + SetPosition(6, D3DVECTOR(-3.0f, 1.8f, -4.0f)); + SetAngleX(6, -PI/2.0f); + + // Crée la roue arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(7, rank); + SetObjectParent(7, 0); + + pModFile->ReadModel("objects\\ruin1w.mod"); + pModFile->CreateEngineObject(rank); + + SetPosition(7, D3DVECTOR(-3.0f, 1.0f, 3.0f)); + SetAngleY(7, PI-0.3f); + SetAngleX(7, -0.3f); + + // Crée la roue avant-droite. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(8, rank); + SetObjectParent(8, 0); + + pModFile->ReadModel("objects\\ruin1w.mod"); + pModFile->CreateEngineObject(rank); + + SetPosition(8, D3DVECTOR(2.0f, 1.6f, -3.0f)); + SetAngleY(8, 0.3f); + + // Crée la roue avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(9, rank); + SetObjectParent(9, 0); + + pModFile->ReadModel("objects\\ruin1w.mod"); + pModFile->CreateEngineObject(rank); + + SetPosition(9, D3DVECTOR(2.0f, 1.0f, 3.0f)); + SetAngleY(9, PI-0.2f); + SetAngleX(9, 0.2f); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.8f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 10.0f); + + CreateShadowCircle(4.0f, 1.0f); + } + + if ( type == OBJECT_RUINmobilew2 ) // véhicule à roues ? + { + // Crée la roue arrière-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(7, rank); + SetObjectParent(7, 0); + + pModFile->ReadModel("objects\\ruin1w.mod"); + pModFile->CreateEngineObject(rank); + + SetPosition(7, D3DVECTOR(-3.0f, 1.0f, 3.0f)); + SetAngleY(7, PI+0.3f); + SetAngleX(7, 0.4f); + + // Crée la roue avant-gauche. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(9, rank); + SetObjectParent(9, 0); + + pModFile->ReadModel("objects\\ruin1w.mod"); + pModFile->CreateEngineObject(rank); + + SetPosition(9, D3DVECTOR(2.0f, 1.0f, 3.0f)); + SetAngleY(9, PI+0.3f); + SetAngleX(9, -0.3f); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.8f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 10.0f); + + CreateShadowCircle(4.0f, 1.0f); + } + + if ( type == OBJECT_RUINmobilet1 ) // véhicule à chenilles ? + { + // Crée le canon. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + + pModFile->ReadModel("objects\\ruin2c.mod"); + pModFile->CreateEngineObject(rank); + + SetPosition(1, D3DVECTOR(3.0f, 5.0f, -2.5f)); + SetAngleX(1, -PI*0.85f); + SetAngleY(1, -0.4f); + SetAngleZ(1, -0.1f); + + CreateCrashSphere(D3DVECTOR(1.0f, 2.8f, -1.0f), 5.0f, SOUND_BOUMm, 0.45f); +//? SetGlobalSphere(D3DVECTOR(1.0f, 5.0f, -1.0f), 10.0f); + + CreateShadowCircle(5.0f, 1.0f); + } + + if ( type == OBJECT_RUINmobilet2 ) // véhicule à chenilles ? + { + CreateCrashSphere(D3DVECTOR(0.0f, 2.8f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 10.0f); + + CreateShadowCircle(5.0f, 1.0f); + } + + if ( type == OBJECT_RUINmobiler1 ) // véhicule roller ? + { + CreateCrashSphere(D3DVECTOR(1.0f, 2.8f, -1.0f), 5.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(1.0f, 5.0f, -1.0f), 10.0f); + + CreateShadowCircle(5.0f, 1.0f); + } + + if ( type == OBJECT_RUINmobiler2 ) // véhicule roller ? + { + CreateCrashSphere(D3DVECTOR(0.0f, 1.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 5.0f, 0.0f), 10.0f); + + CreateShadowCircle(6.0f, 1.0f); + } + + if ( type == OBJECT_RUINfactory ) // factory ? + { + CreateCrashSphere(D3DVECTOR( 9.0f, 1.0f, -11.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 2.0f, -11.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 4.0f, -10.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-12.0f, 11.0f, -4.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 4.0f, -2.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-11.0f, 8.0f, 3.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-11.0f, 2.0f, 4.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-11.0f, 2.0f, 10.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -4.0f, 0.0f, 10.0f), 3.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 18.0f); + + CreateShadowCircle(20.0f, 0.7f); + } + + if ( type == OBJECT_RUINdoor ) // porte convert ? + { + CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 6.0f); + + CreateShadowCircle(6.0f, 1.0f); + } + + if ( type == OBJECT_RUINsupport ) // porte radar ? + { + CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 4.0f); + + CreateShadowCircle(3.0f, 1.0f); + } + + if ( type == OBJECT_RUINradar ) // base radar ? + { + CreateCrashSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); +//? SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 6.0f); + + CreateShadowCircle(6.0f, 1.0f); + } + + if ( type == OBJECT_RUINconvert ) // convert ? + { + m_terrain->AddBuildingLevel(pos, 7.0f, 9.0f, 1.0f, 0.5f); + + CreateCrashSphere(D3DVECTOR(-10.0f, 0.0f, 4.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-10.0f, 0.0f, -4.0f), 5.0f, SOUND_BOUMm, 0.45f); +//? SetGlobalSphere(D3DVECTOR(-3.0f, 0.0f, 0.0f), 14.0f); + } + + if ( type == OBJECT_RUINbase ) // base ? + { + CreateCrashSphere(D3DVECTOR( 0.0f, 15.0f, 0.0f),28.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 17.0f, 6.0f, 42.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 17.0f, 17.0f, 42.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-17.0f, 6.0f, 42.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-17.0f, 17.0f, 42.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-42.0f, 6.0f, 17.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-42.0f, 17.0f, 17.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-42.0f, 6.0f, -17.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-42.0f, 17.0f, -17.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-17.0f, 6.0f, -42.0f), 6.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-17.0f, 10.0f, -42.0f), 4.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 15.0f, 13.0f, -34.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 31.0f, 15.0f, -13.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 21.0f, 8.0f, -39.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 26.0f, 8.0f, -33.0f), 5.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 48.0f); + + CreateShadowCircle(40.0f, 1.0f); + } + + if ( type == OBJECT_RUINhead ) // coiffe base ? + { + CreateCrashSphere(D3DVECTOR( 0.0f, 13.0f, 0.0f),20.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, -8.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f,-16.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f,-22.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-21.0f, 7.0f, 9.0f), 8.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -9.0f, 7.0f, 21.0f), 8.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 21.0f, 7.0f, 9.0f), 8.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 9.0f, 7.0f, 21.0f), 8.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-21.0f, 7.0f, -9.0f), 8.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( -9.0f, 7.0f, -21.0f), 8.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 21.0f, 7.0f, -9.0f), 8.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 9.0f, 7.0f, -21.0f), 8.0f, SOUND_BOUMm, 0.45f); + SetGlobalSphere(D3DVECTOR(0.0f, 0.0f, 0.0f), 35.0f); + + CreateShadowCircle(30.0f, 1.0f); + } + + pos = RetPosition(0); + SetPosition(0, pos); // pour afficher les ombres tout de suite + + SetFloorHeight(0.0f); + CreateOtherObject(type); + + if ( type != OBJECT_RUINfactory && + type != OBJECT_RUINconvert && + type != OBJECT_RUINbase ) + { + FloorAdjust(); + } + + pos = RetPosition(0); + pos.y += height; + SetPosition(0, pos); // pour afficher les ombres tout de suite + + if ( type == OBJECT_RUINmobilew1 ) + { + pos = RetPosition(0); + pos.y -= 0.5f; + SetPosition(0, pos); + + angle = RetAngleX(0)-0.1f; + SetAngleX(0, angle); + } + + if ( type == OBJECT_RUINmobilew2 ) + { + pos = RetPosition(0); + pos.y -= 1.5f; + SetPosition(0, pos); + + angle = RetAngleX(0)-0.9f; + SetAngleX(0, angle); + + angle = RetAngleZ(0)-0.1f; + SetAngleZ(0, angle); + } + + if ( type == OBJECT_RUINmobilet1 ) + { + pos = RetPosition(0); + pos.y -= 0.9f; + SetPosition(0, pos); + + angle = RetAngleX(0)-0.3f; + SetAngleX(0, angle); + } + + if ( type == OBJECT_RUINmobilet2 ) + { + pos = RetPosition(0); + pos.y -= 1.5f; + SetPosition(0, pos); + + angle = RetAngleX(0)-0.3f; + SetAngleX(0, angle); + + angle = RetAngleZ(0)+0.8f; + SetAngleZ(0, angle); + } + + if ( type == OBJECT_RUINmobiler1 ) + { + pos = RetPosition(0); + pos.y += 4.0f; + SetPosition(0, pos); + + angle = RetAngleX(0)-PI*0.6f; + SetAngleX(0, angle); + + angle = RetAngleZ(0)-0.2f; + SetAngleZ(0, angle); + } + + if ( type == OBJECT_RUINmobiler2 ) + { + pos = RetPosition(0); + pos.y += 2.0f; + SetPosition(0, pos); + + angle = RetAngleX(0)-0.1f; + SetAngleX(0, angle); + + angle = RetAngleZ(0)-0.3f; + SetAngleZ(0, angle); + } + + if ( type == OBJECT_RUINdoor ) + { + pos = RetPosition(0); + pos.y -= 0.5f; + SetPosition(0, pos); + + angle = RetAngleZ(0)-0.1f; + SetAngleZ(0, angle); + } + + if ( type == OBJECT_RUINsupport ) + { + pos = RetPosition(0); + pos.y += 0.5f; + SetPosition(0, pos); + +//? angle = RetAngleY(0)+0.1f; +//? SetAngleY(0, angle); + + angle = RetAngleX(0)+0.1f; + SetAngleX(0, angle); + + angle = RetAngleZ(0)+0.1f; + SetAngleZ(0, angle); + } + + if ( type == OBJECT_RUINradar ) + { + pos = RetPosition(0); + pos.y -= 0.5f; + SetPosition(0, pos); + + angle = RetAngleX(0)+0.15f; + SetAngleX(0, angle); + + angle = RetAngleZ(0)+0.1f; + SetAngleZ(0, angle); + } + + if ( type == OBJECT_RUINconvert ) + { + pos = RetPosition(0); + pos.y -= 1.0f; + SetPosition(0, pos); + } + + if ( type == OBJECT_RUINbase ) + { + pos = RetPosition(0); + pos.y -= 1.0f; + SetPosition(0, pos); + + angle = RetAngleX(0)+0.15f; + SetAngleX(0, angle); + } + + if ( type == OBJECT_RUINhead ) + { + pos = RetPosition(0); + pos.y += 8.0f; + SetPosition(0, pos); + + angle = RetAngleX(0)+PI*0.4f; + SetAngleX(0, angle); + } + + delete pModFile; + return TRUE; +} + +// Crée un gadget apollo. + +BOOL CObject::CreateApollo(D3DVECTOR pos, float angle, ObjectType type) +{ + CModFile* pModFile; + int rank, i; + + if ( m_engine->RetRestCreate() < 6 ) return FALSE; + + pModFile = new CModFile(m_iMan); + + SetType(type); + + if ( type == OBJECT_APOLLO1 ) // LEM ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); // c'est un objet fixe + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\apollol1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetZoom(0, 1.2f); + SetFloorHeight(0.0f); + + for ( i=0 ; i<4 ; i++ ) // crée les pieds + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(i+1, rank); + SetObjectParent(i+1, 0); + pModFile->ReadModel("objects\\apollol2.mod"); + pModFile->CreateEngineObject(rank); + SetAngleY(i+1, PI/2.0f*i); + } + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(5, rank); + SetObjectParent(5, 0); + pModFile->ReadModel("objects\\apollol3.mod"); // échelle + pModFile->CreateEngineObject(rank); + +//? m_terrain->AddBuildingLevel(pos, 10.0f, 13.0f, 12.0f, 0.0f); + + CreateCrashSphere(D3DVECTOR( 0.0f, 4.0f, 0.0f), 9.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 11.0f, 5.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-11.0f, 5.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 5.0f, -11.0f), 3.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 0.0f, 5.0f, 11.0f), 3.0f, SOUND_BOUMm, 0.45f); + + SetGlobalSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 9.0f); + + CreateShadowCircle(16.0f, 0.5f); + } + + if ( type == OBJECT_APOLLO2 ) // jeep ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); // c'est un objet fixe + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\apolloj1.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + // Roues. + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\apolloj4.mod"); // roue + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(-5.75f, 1.65f, -5.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(2, rank); + SetObjectParent(2, 0); + pModFile->ReadModel("objects\\apolloj4.mod"); // roue + pModFile->CreateEngineObject(rank); + SetPosition(2, D3DVECTOR(-5.75f, 1.65f, 5.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(3, rank); + SetObjectParent(3, 0); + pModFile->ReadModel("objects\\apolloj4.mod"); // roue + pModFile->CreateEngineObject(rank); + SetPosition(3, D3DVECTOR(5.75f, 1.65f, -5.0f)); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(4, rank); + SetObjectParent(4, 0); + pModFile->ReadModel("objects\\apolloj4.mod"); // roue + pModFile->CreateEngineObject(rank); + SetPosition(4, D3DVECTOR(5.75f, 1.65f, 5.0f)); + + // Accessoirs : + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(5, rank); + SetObjectParent(5, 0); + pModFile->ReadModel("objects\\apolloj2.mod"); // antenne + pModFile->CreateEngineObject(rank); + SetPosition(5, D3DVECTOR(5.5f, 8.8f, 2.0f)); + SetAngleY(5, -120.0f*PI/180.0f); + SetAngleZ(5, 45.0f*PI/180.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(6, rank); + SetObjectParent(6, 0); + pModFile->ReadModel("objects\\apolloj3.mod"); // caméra + pModFile->CreateEngineObject(rank); + SetPosition(6, D3DVECTOR(5.5f, 2.8f, -2.0f)); + SetAngleY(6, 30.0f*PI/180.0f); + + CreateCrashSphere(D3DVECTOR( 3.0f, 2.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR(-3.0f, 2.0f, 0.0f), 5.0f, SOUND_BOUMm, 0.45f); + CreateCrashSphere(D3DVECTOR( 7.0f, 9.0f, 2.0f), 2.0f, SOUND_BOUMm, 0.20f); + + CreateShadowCircle(7.0f, 0.8f); + + FloorAdjust(); + } + + if ( type == OBJECT_APOLLO3 ) // flag ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); // c'est un objet fixe + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\apollof.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + SetJotlerSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 1.0f); + CreateShadowCircle(2.0f, 0.3f); + } + + if ( type == OBJECT_APOLLO4 ) // module ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); // c'est un objet fixe + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\apollom.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + CreateCrashSphere(D3DVECTOR(0.0f, 2.0f, 0.0f), 2.0f, SOUND_BOUMm, 0.45f); + CreateShadowCircle(5.0f, 0.8f); + + FloorAdjust(); + } + + if ( type == OBJECT_APOLLO5 ) // antenna ? + { + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEFIX); // c'est un objet fixe + SetObjectRank(0, rank); + pModFile->ReadModel("objects\\apolloa.mod"); + pModFile->CreateEngineObject(rank); + SetPosition(0, pos); + SetAngleY(0, angle); + SetFloorHeight(0.0f); + + rank = m_engine->CreateObject(); + m_engine->SetObjectType(rank, TYPEDESCENDANT); + SetObjectRank(1, rank); + SetObjectParent(1, 0); + pModFile->ReadModel("objects\\apolloj2.mod"); // antenne + pModFile->CreateEngineObject(rank); + SetPosition(1, D3DVECTOR(0.0f, 5.0f, 0.0f)); + SetAngleY(1, -120.0f*PI/180.0f); + SetAngleZ(1, 45.0f*PI/180.0f); + + CreateCrashSphere(D3DVECTOR(0.0f, 4.0f, 0.0f), 3.0f, SOUND_BOUMm, 0.35f); + CreateShadowCircle(3.0f, 0.7f); + } + + CreateOtherObject(type); + + pos = RetPosition(0); + SetPosition(0, pos); // pour afficher les ombres tout de suite + + delete pModFile; + return TRUE; +} + +// Crée tous les sous-objets permettant de gérer cet objet. + +void CObject::CreateOtherObject(ObjectType type) +{ + if ( type == OBJECT_BASE ) + { + m_auto = new CAutoBase(m_iMan, this); + } + if ( type == OBJECT_PORTICO ) + { + m_auto = new CAutoPortico(m_iMan, this); + } + if ( type == OBJECT_DERRICK ) + { + m_auto = new CAutoDerrick(m_iMan, this); + } + if ( type == OBJECT_FACTORY ) + { + m_auto = new CAutoFactory(m_iMan, this); + } + if ( type == OBJECT_REPAIR ) + { + m_auto = new CAutoRepair(m_iMan, this); + } + if ( type == OBJECT_DESTROYER ) + { + m_auto = new CAutoDestroyer(m_iMan, this); + } + if ( type == OBJECT_STATION ) + { + m_auto = new CAutoStation(m_iMan, this); + } + if ( type == OBJECT_CONVERT ) + { + m_auto = new CAutoConvert(m_iMan, this); + } + if ( type == OBJECT_TOWER ) + { + m_auto = new CAutoTower(m_iMan, this); + } + if ( type == OBJECT_RESEARCH ) + { + m_auto = new CAutoResearch(m_iMan, this); + } + if ( type == OBJECT_RADAR ) + { + m_auto = new CAutoRadar(m_iMan, this); + } + if ( type == OBJECT_INFO ) + { + m_auto = new CAutoInfo(m_iMan, this); + } + if ( type == OBJECT_ENERGY ) + { + m_auto = new CAutoEnergy(m_iMan, this); + } + if ( type == OBJECT_LABO ) + { + m_auto = new CAutoLabo(m_iMan, this); + } + if ( type == OBJECT_NUCLEAR ) + { + m_auto = new CAutoNuclear(m_iMan, this); + } + if ( type == OBJECT_PARA ) + { + m_auto = new CAutoPara(m_iMan, this); + } + if ( type == OBJECT_SAFE ) + { + m_auto = new CAutoSafe(m_iMan, this); + } + if ( type == OBJECT_HUSTON ) + { + m_auto = new CAutoHuston(m_iMan, this); + } + if ( type == OBJECT_EGG ) + { + m_auto = new CAutoEgg(m_iMan, this); + } + if ( type == OBJECT_NEST ) + { + m_auto = new CAutoNest(m_iMan, this); + } + if ( type == OBJECT_ROOT5 ) + { + m_auto = new CAutoRoot(m_iMan, this); + } + if ( type == OBJECT_MUSHROOM2 ) + { + m_auto = new CAutoMush(m_iMan, this); + } + if ( type == OBJECT_FLAGb || + type == OBJECT_FLAGr || + type == OBJECT_FLAGg || + type == OBJECT_FLAGy || + type == OBJECT_FLAGv ) + { + m_auto = new CAutoFlag(m_iMan, this); + } + if ( type == OBJECT_TEEN36 || // tronc ? + type == OBJECT_TEEN37 || // bateau ? + type == OBJECT_TEEN38 ) // ventillateur ? + { + m_auto = new CAutoKid(m_iMan, this); + } +} + + +// Lit un programme. + +BOOL CObject::ReadProgram(int rank, char* filename) +{ + if ( m_brain != 0 ) + { + return m_brain->ReadProgram(rank, filename); + } + return FALSE; +} + +// Ecrit un programme. + +BOOL CObject::WriteProgram(int rank, char* filename) +{ + if ( m_brain != 0 ) + { + return m_brain->WriteProgram(rank, filename); + } + return FALSE; +} + +// Démarre un programme. + +BOOL CObject::RunProgram(int rank) +{ + if ( m_brain != 0 ) + { + m_brain->RunProgram(rank); + return TRUE; + } + if ( m_auto != 0 ) + { + m_auto->Start(rank); + return TRUE; + } + return FALSE; +} + + + + +// Calcule la matrice permettant de transformer l'objet. +// Retourne TRUE si la matrice a changé. +// Les rotations ont lieu dans l'ordre Y, Z et X. + +BOOL CObject::UpdateTransformObject(int part, BOOL bForceUpdate) +{ + D3DVECTOR position, angle, eye; + BOOL bModif = FALSE; + int parent; + + if ( m_truck != 0 ) // transporté par camion ? + { + m_objectPart[part].bTranslate = TRUE; + m_objectPart[part].bRotate = TRUE; + } + + if ( !bForceUpdate && + !m_objectPart[part].bTranslate && + !m_objectPart[part].bRotate ) return FALSE; + + position = m_objectPart[part].position; + angle = m_objectPart[part].angle; + + if ( part == 0 ) // partie principale ? + { + position += m_linVibration; + angle += m_cirVibration+m_inclinaison; + } + + if ( m_objectPart[part].bTranslate || + m_objectPart[part].bRotate ) + { + if ( m_objectPart[part].bTranslate ) + { + D3DUtil_SetIdentityMatrix(m_objectPart[part].matTranslate); + m_objectPart[part].matTranslate._41 = position.x; + m_objectPart[part].matTranslate._42 = position.y; + m_objectPart[part].matTranslate._43 = position.z; + } + + if ( m_objectPart[part].bRotate ) + { + MatRotateZXY(m_objectPart[part].matRotate, angle); + } + + if ( m_objectPart[part].bZoom ) + { + D3DMATRIX mz; + D3DUtil_SetIdentityMatrix(mz); + mz._11 = m_objectPart[part].zoom.x; + mz._22 = m_objectPart[part].zoom.y; + mz._33 = m_objectPart[part].zoom.z; + m_objectPart[part].matTransform = mz * + m_objectPart[part].matRotate * + m_objectPart[part].matTranslate; + } + else + { + m_objectPart[part].matTransform = m_objectPart[part].matRotate * + m_objectPart[part].matTranslate; + } + bModif = TRUE; + } + + if ( bForceUpdate || + m_objectPart[part].bTranslate || + m_objectPart[part].bRotate ) + { + parent = m_objectPart[part].parentPart; + + if ( part == 0 && m_truck != 0 ) // transporté par un camion ? + { + D3DMATRIX* matWorldTruck; + matWorldTruck = m_truck->RetWorldMatrix(m_truckLink); + m_objectPart[part].matWorld = m_objectPart[part].matTransform * + *matWorldTruck; + } + else + { + if ( parent == -1 ) // pas de parent ? + { + m_objectPart[part].matWorld = m_objectPart[part].matTransform; + } + else + { + m_objectPart[part].matWorld = m_objectPart[part].matTransform * + m_objectPart[parent].matWorld; + } + } + bModif = TRUE; + } + + if ( bModif ) + { + m_engine->SetObjectTransform(m_objectPart[part].object, + m_objectPart[part].matWorld); + } + + m_objectPart[part].bTranslate = FALSE; + m_objectPart[part].bRotate = FALSE; + + return bModif; +} + +// Met à jour toutes les matrices pour transformer l'objet père +// et tous ses fils. +// On suppose un maximum de 4 degrés de liberté. Cela convient, +// par exemple, pour un corps, un bras, un avant-bras, une main +// et des doigts. + +BOOL CObject::UpdateTransformObject() +{ + BOOL bUpdate1, bUpdate2, bUpdate3, bUpdate4; + int level1, level2, level3, level4, rank; + int parent1, parent2, parent3, parent4; + + if ( m_bFlat ) + { + for ( level1=0 ; level1RetLimitLOD(0); + limit[2] = limit[1]; + limit[3] = m_engine->RetLimitLOD(1); + limit[4] = limit[3]; + limit[5] = 1000000.0f; + + for ( j=0 ; j<3 ; j++ ) + { + m_engine->ChangeTextureMapping(m_objectPart[0].object, + mat, D3DSTATEPART3, "lemt.tga", "", + limit[j*2+0], limit[j*2+1], D3DMAPPING1Y, + au, bu, 1.0f, 0.0f); + } +} + + +// Action manuelle. + +BOOL CObject::EventProcess(const Event &event) +{ + if ( event.event == EVENT_KEYDOWN ) + { +#if ADJUST_ONBOARD + if ( m_bSelect ) + { + if ( event.param == 'E' ) debug_x += 0.1f; + if ( event.param == 'D' ) debug_x -= 0.1f; + if ( event.param == 'R' ) debug_y += 0.1f; + if ( event.param == 'F' ) debug_y -= 0.1f; + if ( event.param == 'T' ) debug_z += 0.1f; + if ( event.param == 'G' ) debug_z -= 0.1f; + } +#endif +#if ADJUST_ARM + if ( m_bSelect ) + { + if ( event.param == 'X' ) debug_arm1 += 5.0f*PI/180.0f; + if ( event.param == 'C' ) debug_arm1 -= 5.0f*PI/180.0f; + if ( event.param == 'V' ) debug_arm2 += 5.0f*PI/180.0f; + if ( event.param == 'B' ) debug_arm2 -= 5.0f*PI/180.0f; + if ( event.param == 'N' ) debug_arm3 += 5.0f*PI/180.0f; + if ( event.param == 'M' ) debug_arm3 -= 5.0f*PI/180.0f; + if ( event.param == 'X' || + event.param == 'C' || + event.param == 'V' || + event.param == 'B' || + event.param == 'N' || + event.param == 'M' ) + { + SetAngleZ(1, debug_arm1); + SetAngleZ(2, debug_arm2); + SetAngleZ(3, debug_arm3); + char s[100]; + sprintf(s, "a=%.2f b=%.2f c=%.2f", debug_arm1*180.0f/PI, debug_arm2*180.0f/PI, debug_arm3*180.0f/PI); + m_engine->SetInfoText(5, s); + } + } +#endif + } + + if ( m_physics != 0 ) + { + if ( !m_physics->EventProcess(event) ) // objet détruit ? + { + if ( RetSelect() && + m_type != OBJECT_ANT && + m_type != OBJECT_SPIDER && + m_type != OBJECT_BEE ) + { + if ( !m_bDead ) m_camera->SetType(CAMERA_EXPLO); + m_main->DeselectAll(); + } + return FALSE; + } + } + + if ( m_auto != 0 ) + { + m_auto->EventProcess(event); + + if ( event.event == EVENT_FRAME && + m_auto->IsEnded() != ERR_CONTINUE ) + { + m_auto->DeleteObject(); + delete m_auto; + m_auto = 0; + } + } + + if ( m_motion != 0 ) + { + m_motion->EventProcess(event); + } + + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + + return TRUE; +} + + +// Anime l'objet. + +BOOL CObject::EventFrame(const Event &event) +{ + if ( m_type == OBJECT_HUMAN && m_main->RetMainMovie() == MM_SATCOMopen ) + { + UpdateTransformObject(); + return TRUE; + } + + if ( m_type != OBJECT_SHOW && m_engine->RetPause() ) return TRUE; + + m_aTime += event.rTime; + m_shotTime += event.rTime; + + VirusFrame(event.rTime); + PartiFrame(event.rTime); + + UpdateMapping(); + UpdateTransformObject(); + UpdateSelectParticule(); + + if ( m_bProxyActivate ) // active si on est proche ? + { + CPyro* pyro; + D3DVECTOR eye; + float dist; + + eye = m_engine->RetLookatPt(); + dist = Length(eye, RetPosition(0)); + if ( dist < m_proxyDistance ) + { + m_bProxyActivate = FALSE; + m_main->CreateShortcuts(); + m_sound->Play(SOUND_FINDING); + pyro = new CPyro(m_iMan); + pyro->Create(PT_FINDING, this, 0.0f); + m_displayText->DisplayError(INFO_FINDING, this); + } + } + + return TRUE; +} + +// Met à jour le mapping de l'objet. + +void CObject::UpdateMapping() +{ + if ( m_type == OBJECT_POWER || + m_type == OBJECT_ATOMIC || + m_type == OBJECT_STATION || + m_type == OBJECT_ENERGY ) + { + UpdateEnergyMapping(); + } +} + + +// Gestion d'un virus. + +void CObject::VirusFrame(float rTime) +{ + ParticuleType type; + D3DVECTOR pos, speed; + FPOINT dim; + int r; + + if ( !m_bVirusMode ) return; // object sain ? + + m_virusTime += rTime; + if ( m_virusTime >= VIRUS_DELAY ) + { + m_bVirusMode = FALSE; // le virus n'est plus actif + } + + if ( m_lastVirusParticule+m_engine->ParticuleAdapt(0.2f) <= m_aTime ) + { + m_lastVirusParticule = m_aTime; + + r = rand()%10; + if ( r == 0 ) type = PARTIVIRUS1; + if ( r == 1 ) type = PARTIVIRUS2; + if ( r == 2 ) type = PARTIVIRUS3; + if ( r == 3 ) type = PARTIVIRUS4; + if ( r == 4 ) type = PARTIVIRUS5; + if ( r == 5 ) type = PARTIVIRUS6; + if ( r == 6 ) type = PARTIVIRUS7; + if ( r == 7 ) type = PARTIVIRUS8; + if ( r == 8 ) type = PARTIVIRUS9; + if ( r == 9 ) type = PARTIVIRUS10; + + pos = RetPosition(0); + pos.x += (Rand()-0.5f)*10.0f; + pos.z += (Rand()-0.5f)*10.0f; + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = Rand()*4.0f+4.0f; + dim.x = Rand()*0.3f+0.3f; + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, type, 3.0f); + } +} + +// Gestion des particules maîtresses. + +void CObject::PartiFrame(float rTime) +{ + D3DVECTOR pos, angle, factor; + int i, channel; + + for ( i=0 ; iGetPosition(channel, pos) ) + { + m_objectPart[i].masterParti = -1; // particule n'existe plus ! + continue; + } + + SetPosition(i, pos); + + // Chaque morceau tournoie différemment. + switch( i%5 ) + { + case 0: factor = D3DVECTOR( 0.5f, 0.3f, 0.6f); break; + case 1: factor = D3DVECTOR(-0.3f, 0.4f,-0.2f); break; + case 2: factor = D3DVECTOR( 0.4f,-0.6f,-0.3f); break; + case 3: factor = D3DVECTOR(-0.6f,-0.2f, 0.0f); break; + case 4: factor = D3DVECTOR( 0.4f, 0.1f,-0.7f); break; + } + + angle = RetAngle(i); + angle += rTime*PI*factor; + SetAngle(i, angle); + } +} + + +// Modifie le point de vue pour voir comme si on était +// dans le véhicule, ou derrière le véhicule. + +void CObject::SetViewFromHere(D3DVECTOR &eye, float &dirH, float &dirV, + D3DVECTOR &lookat, D3DVECTOR &upVec, + CameraType type) +{ + float speed; + int part; + + UpdateTransformObject(); + + part = 0; + if ( m_type == OBJECT_HUMAN || + m_type == OBJECT_TECH ) + { + eye.x = -0.2f; + eye.y = 3.3f; + eye.z = 0.0f; +//? eye.x = 1.0f; +//? eye.y = 3.3f; +//? eye.z = 0.0f; + } + else if ( m_type == OBJECT_MOBILErt || + m_type == OBJECT_MOBILErr || + m_type == OBJECT_MOBILErs ) + { + eye.x = -1.1f; // sur le capot + eye.y = 7.9f; + eye.z = 0.0f; + } + else if ( m_type == OBJECT_MOBILEwc || + m_type == OBJECT_MOBILEtc || + m_type == OBJECT_MOBILEfc || + m_type == OBJECT_MOBILEic ) // fireball ? + { +//? eye.x = -0.9f; // sur le canon +//? eye.y = 3.0f; +//? eye.z = 0.0f; +//? part = 1; + eye.x = -0.9f; // sur le canon + eye.y = 8.3f; + eye.z = 0.0f; + } + else if ( m_type == OBJECT_MOBILEwi || + m_type == OBJECT_MOBILEti || + m_type == OBJECT_MOBILEfi || + m_type == OBJECT_MOBILEii ) // orgaball ? + { +//? eye.x = -3.5f; // sur le canon +//? eye.y = 5.1f; +//? eye.z = 0.0f; +//? part = 1; + eye.x = -2.5f; // sur le canon + eye.y = 10.4f; + eye.z = 0.0f; + } + else if ( m_type == OBJECT_MOBILErc ) + { +//? eye.x = 2.0f; // dans le canon +//? eye.y = 0.0f; +//? eye.z = 0.0f; +//? part = 2; + eye.x = 4.0f; // sur le canon + eye.y = 11.0f; + eye.z = 0.0f; + } + else if ( m_type == OBJECT_MOBILEsa ) + { + eye.x = 3.0f; + eye.y = 4.5f; + eye.z = 0.0f; + } + else if ( m_type == OBJECT_MOBILEdr ) + { + eye.x = 1.0f; + eye.y = 6.5f; + eye.z = 0.0f; + } + else if ( m_type == OBJECT_APOLLO2 ) + { + eye.x = -3.0f; + eye.y = 6.0f; + eye.z = -2.0f; + } + else + { + eye.x = 0.7f; // entre les supports + eye.y = 4.8f; + eye.z = 0.0f; + } +#if ADJUST_ONBOARD + eye.x += debug_x; + eye.y += debug_y; + eye.z += debug_z; + char s[100]; + sprintf(s, "x=%.2f y=%.2f z=%.2f", eye.x, eye.y, eye.z); + m_engine->SetInfoText(4, s); +#endif + + if ( type == CAMERA_BACK ) + { + eye.x -= 20.0f; + eye.y += 1.0f; + } + + lookat.x = eye.x+1.0f; + lookat.y = eye.y+0.0f; + lookat.z = eye.z+0.0f; + + eye = Transform(m_objectPart[part].matWorld, eye); + lookat = Transform(m_objectPart[part].matWorld, lookat); + + // Penche la caméra dans les virages. + upVec = D3DVECTOR(0.0f, 1.0f, 0.0f); + if ( m_physics != 0 ) + { + if ( m_physics->RetLand() ) // au sol ? + { + speed = m_physics->RetLinMotionX(MO_REASPEED); + lookat.y -= speed*0.002f; + + speed = m_physics->RetCirMotionY(MO_REASPEED); + upVec.z -= speed*0.04f; + } + else // en vol ? + { + speed = m_physics->RetLinMotionX(MO_REASPEED); + lookat.y += speed*0.002f; + + speed = m_physics->RetCirMotionY(MO_REASPEED); + upVec.z += speed*0.08f; + } + } + upVec = Transform(m_objectPart[0].matRotate, upVec); + + dirH = -(m_objectPart[part].angle.y+PI/2.0f); + dirV = 0.0f; + +} + + +// Gestion des caractéristiques. + +void CObject::SetCharacter(Character* character) +{ + CopyMemory(&m_character, character, sizeof(Character)); +} + +void CObject::GetCharacter(Character* character) +{ + CopyMemory(character, &m_character, sizeof(Character)); +} + +Character* CObject::RetCharacter() +{ + return &m_character; +} + + +// Retourne le temps absolu. + +float CObject::RetAbsTime() +{ + return m_aTime; +} + + +// Gestion de l'énergie contenue dans une pile. +// Seul l'objet pile possède de l'énergie, mais pas le véhicule +// qui transporte la pile ! + +void CObject::SetEnergy(float level) +{ + if ( level < 0.0f ) level = 0.0f; + if ( level > 1.0f ) level = 1.0f; + m_energy = level; +} + +float CObject::RetEnergy() +{ + if ( m_type != OBJECT_POWER && + m_type != OBJECT_ATOMIC && + m_type != OBJECT_STATION && + m_type != OBJECT_ENERGY ) return 0.0f; + return m_energy; +} + + +// Gestion de la capacité d'une pile. +// Seul l'objet pile possède une capacité, mais pas le véhicule +// qui transporte la pile ! + +void CObject::SetCapacity(float capacity) +{ + m_capacity = capacity; +} + +float CObject::RetCapacity() +{ + return m_capacity; +} + + +// Gestion du bouclier. + +void CObject::SetShield(float level) +{ + m_shield = level; +} + +float CObject::RetShield() +{ + if ( m_type == OBJECT_FRET || + m_type == OBJECT_STONE || + m_type == OBJECT_URANIUM || + m_type == OBJECT_BULLET || + m_type == OBJECT_METAL || + m_type == OBJECT_BBOX || + m_type == OBJECT_KEYa || + m_type == OBJECT_KEYb || + m_type == OBJECT_KEYc || + m_type == OBJECT_KEYd || + m_type == OBJECT_TNT || + m_type == OBJECT_TEEN31 || // basket ? + m_type == OBJECT_SCRAP1 || + m_type == OBJECT_SCRAP2 || + m_type == OBJECT_SCRAP3 || + m_type == OBJECT_SCRAP4 || + m_type == OBJECT_SCRAP5 || + m_type == OBJECT_BOMB || + m_type == OBJECT_WAYPOINT || + m_type == OBJECT_FLAGb || + m_type == OBJECT_FLAGr || + m_type == OBJECT_FLAGg || + m_type == OBJECT_FLAGy || + m_type == OBJECT_FLAGv || + m_type == OBJECT_POWER || + m_type == OBJECT_ATOMIC || + m_type == OBJECT_ANT || + m_type == OBJECT_SPIDER || + m_type == OBJECT_BEE || + m_type == OBJECT_WORM ) return 0.0f; + return m_shield; +} + + +// Gestion de l'autonomie de vol (zéro = infini). + +void CObject::SetRange(float delay) +{ + m_range = delay; +} + +float CObject::RetRange() +{ + return m_range; +} + + +// Gestion du facteur de transparence de l'objet. + +void CObject::SetTransparency(float value) +{ + int i; + + m_transparency = value; + + for ( i=0 ; iSetObjectTransparency(m_objectPart[i].object, value); + } + } +} + +float CObject::RetTransparency() +{ + return m_transparency; +} + + +// Gestion de la matière de l'objet. + +ObjectMaterial CObject::RetMaterial() +{ + if ( m_type == OBJECT_HUMAN ) + { + return OM_HUMAN; + } + + if ( m_type == OBJECT_SCRAP4 || + m_type == OBJECT_SCRAP5 ) + { + return OM_HUMAN; + } + + return OM_METAL; +} + + +// Indique si l'objet est un gadget non indispensable. + +void CObject::SetGadget(BOOL bMode) +{ + m_bGadget = bMode; +} + +BOOL CObject::RetGadget() +{ + return m_bGadget; +} + + +// Indique si un objet est immobile (fourmi sur le dos). + +void CObject::SetFixed(BOOL bFixed) +{ + m_bFixed = bFixed; +} + +BOOL CObject::RetFixed() +{ + return m_bFixed; +} + + +// Indique si un objet est soumis au clipping (obstacles). + +void CObject::SetClip(BOOL bClip) +{ + m_bClip = bClip; +} + +BOOL CObject::RetClip() +{ + return m_bClip; +} + + + +// Bouscule un objet. + +BOOL CObject::JostleObject(float force) +{ + CAutoJostle* pa; + + if ( m_type == OBJECT_FLAGb || + m_type == OBJECT_FLAGr || + m_type == OBJECT_FLAGg || + m_type == OBJECT_FLAGy || + m_type == OBJECT_FLAGv ) // drapeau ? + { + if ( m_auto == 0 ) return FALSE; + + m_auto->Start(1); + } + else + { + if ( m_auto != 0 ) return FALSE; + + m_auto = new CAutoJostle(m_iMan, this); + pa = (CAutoJostle*)m_auto; + pa->Start(0, force); + } + + return TRUE; +} + + +// Début de l'effet lorsque l'instruction "detect" est utilisée. + +void CObject::StartDetectEffect(CObject *target, BOOL bFound) +{ + D3DMATRIX* mat; + D3DVECTOR pos, goal; + FPOINT dim; + + mat = RetWorldMatrix(0); + pos = Transform(*mat, D3DVECTOR(2.0f, 3.0f, 0.0f)); + + if ( target == 0 ) + { + goal = Transform(*mat, D3DVECTOR(50.0f, 3.0f, 0.0f)); + } + else + { + goal = target->RetPosition(0); + goal.y += 3.0f; + goal = SegmentDist(pos, goal, Length(pos, goal)-3.0f); + } + + dim.x = 3.0f; + dim.y = dim.x; + m_particule->CreateRay(pos, goal, PARTIRAY2, dim, 0.2f); + + if ( target != 0 ) + { + goal = target->RetPosition(0); + goal.y += 3.0f; + goal = SegmentDist(pos, goal, Length(pos, goal)-1.0f); + dim.x = 6.0f; + dim.y = dim.x; + m_particule->CreateParticule(goal, D3DVECTOR(0.0f, 0.0f, 0.0f), dim, + bFound?PARTIGLINT:PARTIGLINTr, 0.5f); + } + + m_sound->Play(bFound?SOUND_BUILD:SOUND_RECOVER); +} + + +// Gestion du temps depuis lequel un virus est actif. + +void CObject::SetVirusMode(BOOL bEnable) +{ + m_bVirusMode = bEnable; + m_virusTime = 0.0f; + + if ( m_bVirusMode && m_brain != 0 ) + { + if ( !m_brain->IntroduceVirus() ) // essaye de contaminer + { + m_bVirusMode = FALSE; // pas de programme à contaminer ! + } + } +} + +BOOL CObject::RetVirusMode() +{ + return m_bVirusMode; +} + +float CObject::RetVirusTime() +{ + return m_virusTime; +} + + +// Gestion du mode de la caméra. + +void CObject::SetCameraType(CameraType type) +{ + m_cameraType = type; +} + +CameraType CObject::RetCameraType() +{ + return m_cameraType; +} + +void CObject::SetCameraDist(float dist) +{ + m_cameraDist = dist; +} + +float CObject::RetCameraDist() +{ + return m_cameraDist; +} + +void CObject::SetCameraLock(BOOL bLock) +{ + m_bCameraLock = bLock; +} + +BOOL CObject::RetCameraLock() +{ + return m_bCameraLock; +} + + + +// Gestion de la mise en évidence de l'objet. + +void CObject::SetHilite(BOOL bMode) +{ + int list[OBJECTMAXPART+1]; + int i, j; + + m_bHilite = bMode; + + if ( m_bHilite ) + { + j = 0; + for ( i=0 ; iSetHiliteRank(list); // donne la liste des parties sélectionnées + } +} + +BOOL CObject::RetHilite() +{ + return m_bHilite; +} + + +// Indique si l'objet est sélecionné ou non. + +void CObject::SetSelect(BOOL bMode, BOOL bDisplayError) +{ + Error err; + + m_bSelect = bMode; + + if ( m_physics != 0 ) + { + m_physics->CreateInterface(m_bSelect); + } + + if ( m_auto != 0 ) + { + m_auto->CreateInterface(m_bSelect); + } + + CreateSelectParticule(); // crée/supprime les particules + + if ( !m_bSelect ) + { + SetGunGoalH(0.0f); // met le canon droit + return; // fini si pas sélectionné + } + + err = ERR_OK; + if ( m_physics != 0 ) + { + err = m_physics->RetError(); + } + if ( m_auto != 0 ) + { + err = m_auto->RetError(); + } + if ( err != ERR_OK && bDisplayError ) + { + m_displayText->DisplayError(err, this); + } +} + +// Indique si l'objet est sélectionné ou non. + +BOOL CObject::RetSelect(BOOL bReal) +{ + if ( !bReal && m_main->RetFixScene() ) return FALSE; + return m_bSelect; +} + + +// Indique si l'objet est sélecionnable ou non. + +void CObject::SetSelectable(BOOL bMode) +{ + m_bSelectable = bMode; +} + +// Indique si l'objet est sélecionnable ou non. + +BOOL CObject::RetSelectable() +{ + return m_bSelectable; +} + + +// Gestion de l'activité d'un objet. + +void CObject::SetActivity(BOOL bMode) +{ + if ( m_brain != 0 ) + { + m_brain->SetActivity(bMode); + } +} + +BOOL CObject::RetActivity() +{ + if ( m_brain != 0 ) + { + return m_brain->RetActivity(); + } + return FALSE; +} + + +// Indique si faut vérifier les tokens de l'objet. + +void CObject::SetCheckToken(BOOL bMode) +{ + m_bCheckToken = bMode; +} + +// Indique si faut vérifier les tokens de l'objet. + +BOOL CObject::RetCheckToken() +{ + return m_bCheckToken; +} + + +// Gestion de la visibilité d'un objet. +// L'objet n'est pas caché visuellement ni inactif, mais ignoré +// des détections ! Par exemple: ver sous terre. + +void CObject::SetVisible(BOOL bVisible) +{ + m_bVisible = bVisible; +} + +BOOL CObject::RetVisible() +{ + return m_bVisible; +} + + +// Gestion du mode de fonctionnement d'un objet. Un objet +// inactif est identique à un objet détruit, inexistant. +// Ce mode est utilisé pour les objets "resetables" lors +// d'entraînement, pour simuler une destruction. + +void CObject::SetEnable(BOOL bEnable) +{ + m_bEnable = bEnable; +} + +BOOL CObject::RetEnable() +{ + return m_bEnable; +} + + +// Gestion du mode où un objet n'est activé que lorsqu'on +// est proche. + +void CObject::SetProxyActivate(BOOL bActivate) +{ + m_bProxyActivate = bActivate; +} + +BOOL CObject::RetProxyActivate() +{ + return m_bProxyActivate; +} + +void CObject::SetProxyDistance(float distance) +{ + m_proxyDistance = distance; +} + +float CObject::RetProxyDistance() +{ + return m_proxyDistance; +} + + +// Gestion du mode d'augmentation des dommages. + +void CObject::SetMagnifyDamage(float factor) +{ + m_magnifyDamage = factor; +} + +float CObject::RetMagnifyDamage() +{ + return m_magnifyDamage; +} + + +// Gestion du paramètre libre. + +void CObject::SetParam(float value) +{ + m_param = value; +} + +float CObject::RetParam() +{ + return m_param; +} + + +// Gestion du mode "bloqué" d'un objet. +// Par exemple, un cube de titanium est bloqué pendant qu'il est utilisé +// pour fabriquer qq chose, ou un véhicule est bloqué tant que sa +// construction n'est pas terminée. + +void CObject::SetLock(BOOL bLock) +{ + m_bLock = bLock; +} + +BOOL CObject::RetLock() +{ + return m_bLock; +} + +// Gestion du mode "en cours d'explosion" d'un objet. +// Un objet dans ce mode n'est pas sauvegardé. + +void CObject::SetExplo(BOOL bExplo) +{ + m_bExplo = bExplo; +} + +BOOL CObject::RetExplo() +{ + return m_bExplo; +} + + +// Gestion du mode "cargaison du vaisseau" pendant les films. + +void CObject::SetCargo(BOOL bCargo) +{ + m_bCargo = bCargo; +} + +BOOL CObject::RetCargo() +{ + return m_bCargo; +} + + +// Gestion du mode HS d'un objet. + +void CObject::SetBurn(BOOL bBurn) +{ + m_bBurn = bBurn; + +//? if ( m_botVar != 0 ) +//? { +//? if ( m_bBurn ) m_botVar->SetUserPtr(OBJECTDELETED); +//? else m_botVar->SetUserPtr(this); +//? } +} + +BOOL CObject::RetBurn() +{ + return m_bBurn; +} + +void CObject::SetDead(BOOL bDead) +{ + m_bDead = bDead; + + if ( bDead && m_brain != 0 ) + { + m_brain->StopProgram(); // stoppe la tâche en cours + } + +//? if ( m_botVar != 0 ) +//? { +//? if ( m_bDead ) m_botVar->SetUserPtr(OBJECTDELETED); +//? else m_botVar->SetUserPtr(this); +//? } +} + +BOOL CObject::RetDead() +{ + return m_bDead; +} + +BOOL CObject::RetRuin() +{ + return m_bBurn|m_bFlat; +} + +BOOL CObject::RetActif() +{ + return !m_bLock && !m_bBurn && !m_bFlat && m_bVisible && m_bEnable; +} + + +// Gestion du point de visée. + +void CObject::SetGunGoalV(float gunGoal) +{ + if ( m_type == OBJECT_MOBILEfc || + m_type == OBJECT_MOBILEtc || + m_type == OBJECT_MOBILEwc || + m_type == OBJECT_MOBILEic ) // fireball ? + { + if ( gunGoal > 10.0f*PI/180.0f ) gunGoal = 10.0f*PI/180.0f; + if ( gunGoal < -20.0f*PI/180.0f ) gunGoal = -20.0f*PI/180.0f; + SetAngleZ(1, gunGoal); + } + else if ( m_type == OBJECT_MOBILEfi || + m_type == OBJECT_MOBILEti || + m_type == OBJECT_MOBILEwi || + m_type == OBJECT_MOBILEii ) // orgaball ? + { + if ( gunGoal > 20.0f*PI/180.0f ) gunGoal = 20.0f*PI/180.0f; + if ( gunGoal < -20.0f*PI/180.0f ) gunGoal = -20.0f*PI/180.0f; + SetAngleZ(1, gunGoal); + } + else if ( m_type == OBJECT_MOBILErc ) // phazer ? + { + if ( gunGoal > 45.0f*PI/180.0f ) gunGoal = 45.0f*PI/180.0f; + if ( gunGoal < -20.0f*PI/180.0f ) gunGoal = -20.0f*PI/180.0f; + SetAngleZ(2, gunGoal); + } + else + { + gunGoal = 0.0f; + } + + m_gunGoalV = gunGoal; +} + +void CObject::SetGunGoalH(float gunGoal) +{ + if ( m_type == OBJECT_MOBILEfc || + m_type == OBJECT_MOBILEtc || + m_type == OBJECT_MOBILEwc || + m_type == OBJECT_MOBILEic ) // fireball ? + { + if ( gunGoal > 40.0f*PI/180.0f ) gunGoal = 40.0f*PI/180.0f; + if ( gunGoal < -40.0f*PI/180.0f ) gunGoal = -40.0f*PI/180.0f; + SetAngleY(1, gunGoal); + } + else if ( m_type == OBJECT_MOBILEfi || + m_type == OBJECT_MOBILEti || + m_type == OBJECT_MOBILEwi || + m_type == OBJECT_MOBILEii ) // orgaball ? + { + if ( gunGoal > 40.0f*PI/180.0f ) gunGoal = 40.0f*PI/180.0f; + if ( gunGoal < -40.0f*PI/180.0f ) gunGoal = -40.0f*PI/180.0f; + SetAngleY(1, gunGoal); + } + else if ( m_type == OBJECT_MOBILErc ) // phazer ? + { + if ( gunGoal > 40.0f*PI/180.0f ) gunGoal = 40.0f*PI/180.0f; + if ( gunGoal < -40.0f*PI/180.0f ) gunGoal = -40.0f*PI/180.0f; + SetAngleY(2, gunGoal); + } + else + { + gunGoal = 0.0f; + } + + m_gunGoalH = gunGoal; +} + +float CObject::RetGunGoalV() +{ + return m_gunGoalV; +} + +float CObject::RetGunGoalH() +{ + return m_gunGoalH; +} + + + +// Montre les limites de l'objet. + +BOOL CObject::StartShowLimit() +{ + if ( m_showLimitRadius == 0.0f ) return FALSE; + + m_main->SetShowLimit(0, PARTILIMIT1, this, RetPosition(0), m_showLimitRadius); + m_bShowLimit = TRUE; + return TRUE; +} + +void CObject::StopShowLimit() +{ + m_bShowLimit = FALSE; +} + + + +// Indique si un programme est en cours d'exécution. + +BOOL CObject::IsProgram() +{ + if ( m_brain == 0 ) return FALSE; + return m_brain->IsProgram(); +} + + +// Crée ou supprime les particules associées à l'objet. + +void CObject::CreateSelectParticule() +{ + D3DVECTOR pos, speed; + FPOINT dim; + int i; + + // Supprime les particules précédentes. + for ( i=0 ; i<4 ; i++ ) + { + if ( m_partiSel[i] != -1 ) + { + m_particule->DeleteParticule(m_partiSel[i]); + m_partiSel[i] = -1; + } + } + + if ( m_bSelect || IsProgram() ) + { + // Crée les particules lens pour les phares. + if ( m_type == OBJECT_MOBILEfa || + m_type == OBJECT_MOBILEta || + m_type == OBJECT_MOBILEwa || + m_type == OBJECT_MOBILEia || + m_type == OBJECT_MOBILEfc || + m_type == OBJECT_MOBILEtc || + m_type == OBJECT_MOBILEwc || + m_type == OBJECT_MOBILEic || + m_type == OBJECT_MOBILEfi || + m_type == OBJECT_MOBILEti || + m_type == OBJECT_MOBILEwi || + m_type == OBJECT_MOBILEii || + m_type == OBJECT_MOBILEfs || + m_type == OBJECT_MOBILEts || + m_type == OBJECT_MOBILEws || + m_type == OBJECT_MOBILEis || + m_type == OBJECT_MOBILErt || + m_type == OBJECT_MOBILErc || + m_type == OBJECT_MOBILErr || + m_type == OBJECT_MOBILErs || + m_type == OBJECT_MOBILEsa || + m_type == OBJECT_MOBILEtg || + m_type == OBJECT_MOBILEft || + m_type == OBJECT_MOBILEtt || + m_type == OBJECT_MOBILEwt || + m_type == OBJECT_MOBILEit || + m_type == OBJECT_MOBILEdr ) // véhicule ? + { + pos = D3DVECTOR(0.0f, 0.0f, 0.0f); + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 0.0f; + dim.y = 0.0f; + m_partiSel[0] = m_particule->CreateParticule(pos, speed, dim, PARTISELY, 1.0f, 0.0f, 0.0f); + m_partiSel[1] = m_particule->CreateParticule(pos, speed, dim, PARTISELY, 1.0f, 0.0f, 0.0f); + m_partiSel[2] = m_particule->CreateParticule(pos, speed, dim, PARTISELR, 1.0f, 0.0f, 0.0f); + m_partiSel[3] = m_particule->CreateParticule(pos, speed, dim, PARTISELR, 1.0f, 0.0f, 0.0f); + UpdateSelectParticule(); + } + } +} + +// Met à jour les particules associées à l'objet. + +void CObject::UpdateSelectParticule() +{ + D3DVECTOR pos[4]; + FPOINT dim[4]; + float zoom[4]; + float angle; + int i; + + if ( !m_bSelect && !IsProgram() ) return; + + dim[0].x = 1.0f; + dim[1].x = 1.0f; + dim[2].x = 1.2f; + dim[3].x = 1.2f; + + // Lens avants jaunes. + if ( m_type == OBJECT_MOBILErt || + m_type == OBJECT_MOBILErc || + m_type == OBJECT_MOBILErr || + m_type == OBJECT_MOBILErs ) // grosses chenilles ? + { + pos[0] = D3DVECTOR(4.2f, 2.8f, 1.5f); + pos[1] = D3DVECTOR(4.2f, 2.8f, -1.5f); + dim[0].x = 1.5f; + dim[1].x = 1.5f; + } + else if ( m_type == OBJECT_MOBILEwt || + m_type == OBJECT_MOBILEtt || + m_type == OBJECT_MOBILEft || + m_type == OBJECT_MOBILEit ) // trainer ? + { + pos[0] = D3DVECTOR(4.2f, 2.5f, 1.2f); + pos[1] = D3DVECTOR(4.2f, 2.5f, -1.2f); + dim[0].x = 1.5f; + dim[1].x = 1.5f; + } + else if ( m_type == OBJECT_MOBILEsa ) // sous-marin ? + { + pos[0] = D3DVECTOR(3.6f, 4.0f, 2.0f); + pos[1] = D3DVECTOR(3.6f, 4.0f, -2.0f); + } + else if ( m_type == OBJECT_MOBILEtg ) // cible ? + { + pos[0] = D3DVECTOR(3.4f, 6.5f, 2.0f); + pos[1] = D3DVECTOR(3.4f, 6.5f, -2.0f); + } + else if ( m_type == OBJECT_MOBILEdr ) // dessinateur ? + { + pos[0] = D3DVECTOR(4.9f, 3.5f, 2.5f); + pos[1] = D3DVECTOR(4.9f, 3.5f, -2.5f); + } + else + { + pos[0] = D3DVECTOR(4.2f, 2.5f, 1.5f); + pos[1] = D3DVECTOR(4.2f, 2.5f, -1.5f); + } + + // Lens arrières rouges. + if ( m_type == OBJECT_MOBILEfa || + m_type == OBJECT_MOBILEfc || + m_type == OBJECT_MOBILEfi || + m_type == OBJECT_MOBILEfs || + m_type == OBJECT_MOBILEft ) // volant ? + { + pos[2] = D3DVECTOR(-4.0f, 3.1f, 4.5f); + pos[3] = D3DVECTOR(-4.0f, 3.1f, -4.5f); + dim[2].x = 0.6f; + dim[3].x = 0.6f; + } + if ( m_type == OBJECT_MOBILEwa || + m_type == OBJECT_MOBILEwc || + m_type == OBJECT_MOBILEwi || + m_type == OBJECT_MOBILEws ) // roues ? + { + pos[2] = D3DVECTOR(-4.5f, 2.7f, 2.8f); + pos[3] = D3DVECTOR(-4.5f, 2.7f, -2.8f); + } + if ( m_type == OBJECT_MOBILEwt ) // roues ? + { + pos[2] = D3DVECTOR(-4.0f, 2.5f, 2.2f); + pos[3] = D3DVECTOR(-4.0f, 2.5f, -2.2f); + } + if ( m_type == OBJECT_MOBILEia || + m_type == OBJECT_MOBILEic || + m_type == OBJECT_MOBILEii || + m_type == OBJECT_MOBILEis || + m_type == OBJECT_MOBILEit ) // pattes ? + { + pos[2] = D3DVECTOR(-4.5f, 2.7f, 2.8f); + pos[3] = D3DVECTOR(-4.5f, 2.7f, -2.8f); + } + if ( m_type == OBJECT_MOBILEta || + m_type == OBJECT_MOBILEtc || + m_type == OBJECT_MOBILEti || + m_type == OBJECT_MOBILEts || + m_type == OBJECT_MOBILEtt ) // chenilles ? + { + pos[2] = D3DVECTOR(-3.6f, 4.2f, 3.0f); + pos[3] = D3DVECTOR(-3.6f, 4.2f, -3.0f); + } + if ( m_type == OBJECT_MOBILErt || + m_type == OBJECT_MOBILErc || + m_type == OBJECT_MOBILErr || + m_type == OBJECT_MOBILErs ) // grosses chenilles ? + { + pos[2] = D3DVECTOR(-5.0f, 5.2f, 2.5f); + pos[3] = D3DVECTOR(-5.0f, 5.2f, -2.5f); + } + if ( m_type == OBJECT_MOBILEsa ) // sous-marin ? + { + pos[2] = D3DVECTOR(-3.6f, 4.0f, 2.0f); + pos[3] = D3DVECTOR(-3.6f, 4.0f, -2.0f); + } + if ( m_type == OBJECT_MOBILEtg ) // cible ? + { + pos[2] = D3DVECTOR(-2.4f, 6.5f, 2.0f); + pos[3] = D3DVECTOR(-2.4f, 6.5f, -2.0f); + } + if ( m_type == OBJECT_MOBILEdr ) // dessinateur ? + { + pos[2] = D3DVECTOR(-5.3f, 2.7f, 1.8f); + pos[3] = D3DVECTOR(-5.3f, 2.7f, -1.8f); + } + + angle = RetAngleY(0)/PI; + + zoom[0] = 1.0f; + zoom[1] = 1.0f; + zoom[2] = 1.0f; + zoom[3] = 1.0f; + + if ( IsProgram() && // programme en cours ? + Mod(m_aTime, 0.7f) < 0.3f ) + { + zoom[0] = 0.0f; // clignotte + zoom[1] = 0.0f; + zoom[2] = 0.0f; + zoom[3] = 0.0f; + } + + // Met à jour tous les lens. + for ( i=0 ; i<4 ; i++ ) + { + pos[i] = Transform(m_objectPart[0].matWorld, pos[i]); + dim[i].y = dim[i].x; + m_particule->SetParam(m_partiSel[i], pos[i], dim[i], zoom[i], angle, 1.0f); + } +} + + +// Donne le pointeur au script en cours d'exécution. + +void CObject::SetRunScript(CScript* script) +{ + m_runScript = script; +} + +CScript* CObject::RetRunScript() +{ + return m_runScript; +} + +// Retourne les variables du "this" pour CBOT. + +CBotVar* CObject::RetBotVar() +{ + return m_botVar; +} + +// Retourne la physique associée à l'objet. + +CPhysics* CObject::RetPhysics() +{ + return m_physics; +} + +// Retourne le cerveau associé à l'objet. + +CBrain* CObject::RetBrain() +{ + return m_brain; +} + +// Retourne le mouvement associé à l'objet. + +CMotion* CObject::RetMotion() +{ + return m_motion; +} + +// Retourne l'automate associé à l'objet. + +CAuto* CObject::RetAuto() +{ + return m_auto; +} + +void CObject::SetAuto(CAuto* automat) +{ + m_auto = automat; +} + + + +// Gestion du rang dans le fichier de définition. + +void CObject::SetDefRank(int rank) +{ + m_defRank = rank; +} + +int CObject::RetDefRank() +{ + return m_defRank; +} + + +// Donne le nom de l'objet pour le tooltip. + +BOOL CObject::GetTooltipName(char* name) +{ + GetResource(RES_OBJECT, m_type, name); + return ( name[0] != 0 ); +} + + +// Ajoute l'objet précédemment sélectionné dans la liste. + +void CObject::AddDeselList(CObject* pObj) +{ + int i; + + if ( m_totalDesectList >= OBJECTMAXDESELLIST ) + { + for ( i=0 ; iRetTraceDown(); +} + +void CObject::SetTraceDown(BOOL bDown) +{ + CMotionVehicle* mv; + if ( m_motion == 0 ) return; + mv = (CMotionVehicle*)m_motion; + mv->SetTraceDown(bDown); +} + +int CObject::RetTraceColor() +{ + CMotionVehicle* mv; + if ( m_motion == 0 ) return 0; + mv = (CMotionVehicle*)m_motion; + return mv->RetTraceColor(); +} + +void CObject::SetTraceColor(int color) +{ + CMotionVehicle* mv; + if ( m_motion == 0 ) return; + mv = (CMotionVehicle*)m_motion; + mv->SetTraceColor(color); +} + +float CObject::RetTraceWidth() +{ + CMotionVehicle* mv; + if ( m_motion == 0 ) return 0.0f; + mv = (CMotionVehicle*)m_motion; + return mv->RetTraceWidth(); +} + +void CObject::SetTraceWidth(float width) +{ + CMotionVehicle* mv; + if ( m_motion == 0 ) return; + mv = (CMotionVehicle*)m_motion; + mv->SetTraceWidth(width); +} + + diff --git a/src/object.h b/src/object.h new file mode 100644 index 00000000..785c6db0 --- /dev/null +++ b/src/object.h @@ -0,0 +1,767 @@ +// object.h + +#ifndef _OBJECT_H_ +#define _OBJECT_H_ + + +class CInstanceManager; +class CD3DEngine; +class CLight; +class CTerrain; +class CWater; +class CCamera; +class CParticule; +class CPhysics; +class CBrain; +class CMotion; +class CAuto; +class CDisplayText; +class CRobotMain; +class CBotVar; +class CScript; + +enum CameraType; +enum Sound; +enum D3DShadowType; + + + +// Le père de toutes les parties doit toujours être la partie +// numéro zéro ! + +#define OBJECTMAXPART 40 +#define MAXCRASHSPHERE 40 +#define OBJECTMAXDESELLIST 10 +#define OBJECTMAXINFO 10 +#define OBJECTMAXCMDLINE 20 + +enum ObjectType +{ + OBJECT_NULL = 0, // objet détruit + OBJECT_FIX = 1, // décor fixe + OBJECT_PORTICO = 2, // portique + OBJECT_BASE = 3, // grande base principale + OBJECT_DERRICK = 4, // derrick fixe + OBJECT_FACTORY = 5, // usine fixe + OBJECT_STATION = 6, // station de recharge + OBJECT_CONVERT = 7, // station de transformation + OBJECT_REPAIR = 8, // réparation + OBJECT_TOWER = 9, // tour de défense + OBJECT_NEST = 10, // nid + OBJECT_RESEARCH = 11, // centre de recherches + OBJECT_RADAR = 12, // radar + OBJECT_ENERGY = 13, // centrale d'énergie + OBJECT_LABO = 14, // laboratoire d'analyse pour insectes + OBJECT_NUCLEAR = 15, // centrale nucléaire + OBJECT_START = 16, // départ + OBJECT_END = 17, // arrivée + OBJECT_INFO = 18, // borne d'information + OBJECT_PARA = 19, // paratonnerre + OBJECT_TARGET1 = 20, // portique cible + OBJECT_TARGET2 = 21, // centre cible + OBJECT_SAFE = 22, // coffre fort + OBJECT_HUSTON = 23, // centre de contrôle + OBJECT_DESTROYER = 24, // destructeur + OBJECT_FRET = 30, // transportable + OBJECT_STONE = 31, // pierre + OBJECT_URANIUM = 32, // uranium + OBJECT_METAL = 33, // métal + OBJECT_POWER = 34, // pile normale + OBJECT_ATOMIC = 35, // pile atomique + OBJECT_BULLET = 36, // boulet + OBJECT_BBOX = 37, // black-box + OBJECT_TNT = 38, // caisse de TNT + OBJECT_SCRAP1 = 40, // déchet métallique + OBJECT_SCRAP2 = 41, // déchet métallique + OBJECT_SCRAP3 = 42, // déchet métallique + OBJECT_SCRAP4 = 43, // déchet plastique + OBJECT_SCRAP5 = 44, // déchet plastique + OBJECT_MARKPOWER = 50, // marque pile en sous-sol + OBJECT_MARKSTONE = 51, // marque minerai en sous-sol + OBJECT_MARKURANIUM = 52, // marque uranium en sous-sol + OBJECT_MARKKEYa = 53, // marque clé en sous-sol + OBJECT_MARKKEYb = 54, // marque clé en sous-sol + OBJECT_MARKKEYc = 55, // marque clé en sous-sol + OBJECT_MARKKEYd = 56, // marque clé en sous-sol + OBJECT_BOMB = 60, // bombe + OBJECT_WINFIRE = 61, // feu d'artifice + OBJECT_SHOW = 62, // montre un lieu + OBJECT_BAG = 63, // sac de survie + OBJECT_PLANT0 = 70, // plante 0 + OBJECT_PLANT1 = 71, // plante 1 + OBJECT_PLANT2 = 72, // plante 2 + OBJECT_PLANT3 = 73, // plante 3 + OBJECT_PLANT4 = 74, // plante 4 + OBJECT_PLANT5 = 75, // plante 5 + OBJECT_PLANT6 = 76, // plante 6 + OBJECT_PLANT7 = 77, // plante 7 + OBJECT_PLANT8 = 78, // plante 8 + OBJECT_PLANT9 = 79, // plante 9 + OBJECT_PLANT10 = 80, // plante 10 + OBJECT_PLANT11 = 81, // plante 11 + OBJECT_PLANT12 = 82, // plante 12 + OBJECT_PLANT13 = 83, // plante 13 + OBJECT_PLANT14 = 84, // plante 14 + OBJECT_PLANT15 = 85, // plante 15 + OBJECT_PLANT16 = 86, // plante 16 + OBJECT_PLANT17 = 87, // plante 17 + OBJECT_PLANT18 = 88, // plante 18 + OBJECT_PLANT19 = 89, // plante 19 + OBJECT_TREE0 = 90, // arbre 0 + OBJECT_TREE1 = 91, // arbre 1 + OBJECT_TREE2 = 92, // arbre 2 + OBJECT_TREE3 = 93, // arbre 3 + OBJECT_TREE4 = 94, // arbre 4 + OBJECT_TREE5 = 95, // arbre 5 + OBJECT_TREE6 = 96, // arbre 6 + OBJECT_TREE7 = 97, // arbre 7 + OBJECT_TREE8 = 98, // arbre 8 + OBJECT_TREE9 = 99, // arbre 9 + OBJECT_MOBILEwt = 100, // wheel-trainer + OBJECT_MOBILEtt = 101, // track-trainer + OBJECT_MOBILEft = 102, // fly-trainer + OBJECT_MOBILEit = 103, // insect-trainer + OBJECT_MOBILEwa = 110, // wheel-arm + OBJECT_MOBILEta = 111, // track-arm + OBJECT_MOBILEfa = 112, // fly-arm + OBJECT_MOBILEia = 113, // insect-arm + OBJECT_MOBILEwc = 120, // wheel-cannon + OBJECT_MOBILEtc = 121, // track-cannon + OBJECT_MOBILEfc = 122, // fly-cannon + OBJECT_MOBILEic = 123, // insect-cannon + OBJECT_MOBILEwi = 130, // wheel-insect-cannon + OBJECT_MOBILEti = 131, // track-insect-cannon + OBJECT_MOBILEfi = 132, // fly-insect-cannon + OBJECT_MOBILEii = 133, // insect-insect-cannon + OBJECT_MOBILEws = 140, // wheel-search + OBJECT_MOBILEts = 141, // track-search + OBJECT_MOBILEfs = 142, // fly-search + OBJECT_MOBILEis = 143, // insect-search + OBJECT_MOBILErt = 200, // roller-terraform + OBJECT_MOBILErc = 201, // roller-canon + OBJECT_MOBILErr = 202, // roller-recover + OBJECT_MOBILErs = 203, // roller-shield + OBJECT_MOBILEsa = 210, // sous-marin + OBJECT_MOBILEtg = 211, // cible d'exercice + OBJECT_MOBILEdr = 212, // robot de dessin + OBJECT_WAYPOINT = 250, // chemin + OBJECT_FLAGb = 260, // drapeau bleu + OBJECT_FLAGr = 261, // drapeau rouge + OBJECT_FLAGg = 262, // drapeau vert + OBJECT_FLAGy = 263, // drapeau jaune + OBJECT_FLAGv = 264, // drapeau violet + OBJECT_KEYa = 270, // clé a + OBJECT_KEYb = 271, // clé b + OBJECT_KEYc = 272, // clé c + OBJECT_KEYd = 273, // clé d + OBJECT_HUMAN = 300, // homme + OBJECT_TOTO = 301, // toto + OBJECT_TECH = 302, // technicien + OBJECT_BARRIER0 = 400, // barrière + OBJECT_BARRIER1 = 401, // barrière + OBJECT_BARRIER2 = 402, // barrière + OBJECT_BARRIER3 = 403, // barrière + OBJECT_BARRIER4 = 404, // barrière + OBJECT_MOTHER = 500, // mère pondeuse + OBJECT_EGG = 501, // oeuf + OBJECT_ANT = 502, // fourmi + OBJECT_SPIDER = 503, // araignée + OBJECT_BEE = 504, // abeille + OBJECT_WORM = 505, // ver + OBJECT_RUINmobilew1 = 600, // ruine 1 + OBJECT_RUINmobilew2 = 601, // ruine 1 + OBJECT_RUINmobilet1 = 602, // ruine 2 + OBJECT_RUINmobilet2 = 603, // ruine 2 + OBJECT_RUINmobiler1 = 604, // ruine 3 + OBJECT_RUINmobiler2 = 605, // ruine 3 + OBJECT_RUINfactory = 606, // ruine 4 + OBJECT_RUINdoor = 607, // ruine 5 + OBJECT_RUINsupport = 608, // ruine 6 + OBJECT_RUINradar = 609, // ruine 7 + OBJECT_RUINconvert = 610, // ruine 8 + OBJECT_RUINbase = 611, // ruine 9 + OBJECT_RUINhead = 612, // ruine 10 + OBJECT_TEEN0 = 620, // jouet + OBJECT_TEEN1 = 621, // jouet + OBJECT_TEEN2 = 622, // jouet + OBJECT_TEEN3 = 623, // jouet + OBJECT_TEEN4 = 624, // jouet + OBJECT_TEEN5 = 625, // jouet + OBJECT_TEEN6 = 626, // jouet + OBJECT_TEEN7 = 627, // jouet + OBJECT_TEEN8 = 628, // jouet + OBJECT_TEEN9 = 629, // jouet + OBJECT_TEEN10 = 630, // jouet + OBJECT_TEEN11 = 631, // jouet + OBJECT_TEEN12 = 632, // jouet + OBJECT_TEEN13 = 633, // jouet + OBJECT_TEEN14 = 634, // jouet + OBJECT_TEEN15 = 635, // jouet + OBJECT_TEEN16 = 636, // jouet + OBJECT_TEEN17 = 637, // jouet + OBJECT_TEEN18 = 638, // jouet + OBJECT_TEEN19 = 639, // jouet + OBJECT_TEEN20 = 640, // jouet + OBJECT_TEEN21 = 641, // jouet + OBJECT_TEEN22 = 642, // jouet + OBJECT_TEEN23 = 643, // jouet + OBJECT_TEEN24 = 644, // jouet + OBJECT_TEEN25 = 645, // jouet + OBJECT_TEEN26 = 646, // jouet + OBJECT_TEEN27 = 647, // jouet + OBJECT_TEEN28 = 648, // jouet + OBJECT_TEEN29 = 649, // jouet + OBJECT_TEEN30 = 650, // jouet + OBJECT_TEEN31 = 651, // jouet + OBJECT_TEEN32 = 652, // jouet + OBJECT_TEEN33 = 653, // jouet + OBJECT_TEEN34 = 654, // jouet + OBJECT_TEEN35 = 655, // jouet + OBJECT_TEEN36 = 656, // jouet + OBJECT_TEEN37 = 657, // jouet + OBJECT_TEEN38 = 658, // jouet + OBJECT_TEEN39 = 659, // jouet + OBJECT_TEEN40 = 660, // jouet + OBJECT_TEEN41 = 661, // jouet + OBJECT_TEEN42 = 662, // jouet + OBJECT_TEEN43 = 663, // jouet + OBJECT_TEEN44 = 664, // jouet + OBJECT_TEEN45 = 665, // jouet + OBJECT_TEEN46 = 666, // jouet + OBJECT_TEEN47 = 667, // jouet + OBJECT_TEEN48 = 668, // jouet + OBJECT_TEEN49 = 669, // jouet + OBJECT_QUARTZ0 = 700, // quartz 0 + OBJECT_QUARTZ1 = 701, // quartz 1 + OBJECT_QUARTZ2 = 702, // quartz 2 + OBJECT_QUARTZ3 = 703, // quartz 3 + OBJECT_QUARTZ4 = 704, // quartz 4 + OBJECT_QUARTZ5 = 705, // quartz 5 + OBJECT_QUARTZ6 = 706, // quartz 6 + OBJECT_QUARTZ7 = 707, // quartz 7 + OBJECT_QUARTZ8 = 708, // quartz 8 + OBJECT_QUARTZ9 = 709, // quartz 9 + OBJECT_ROOT0 = 710, // racine 0 + OBJECT_ROOT1 = 711, // racine 1 + OBJECT_ROOT2 = 712, // racine 2 + OBJECT_ROOT3 = 713, // racine 3 + OBJECT_ROOT4 = 714, // racine 4 + OBJECT_ROOT5 = 715, // racine 5 + OBJECT_ROOT6 = 716, // racine 6 + OBJECT_ROOT7 = 717, // racine 7 + OBJECT_ROOT8 = 718, // racine 8 + OBJECT_ROOT9 = 719, // racine 9 + OBJECT_SEAWEED0 = 720, // algue 0 + OBJECT_SEAWEED1 = 721, // algue 1 + OBJECT_SEAWEED2 = 722, // algue 2 + OBJECT_SEAWEED3 = 723, // algue 3 + OBJECT_SEAWEED4 = 724, // algue 4 + OBJECT_SEAWEED5 = 725, // algue 5 + OBJECT_SEAWEED6 = 726, // algue 6 + OBJECT_SEAWEED7 = 727, // algue 7 + OBJECT_SEAWEED8 = 728, // algue 8 + OBJECT_SEAWEED9 = 729, // algue 9 + OBJECT_MUSHROOM0 = 730, // champignon 0 + OBJECT_MUSHROOM1 = 731, // champignon 1 + OBJECT_MUSHROOM2 = 732, // champignon 2 + OBJECT_MUSHROOM3 = 733, // champignon 3 + OBJECT_MUSHROOM4 = 734, // champignon 4 + OBJECT_MUSHROOM5 = 735, // champignon 5 + OBJECT_MUSHROOM6 = 736, // champignon 6 + OBJECT_MUSHROOM7 = 737, // champignon 7 + OBJECT_MUSHROOM8 = 738, // champignon 8 + OBJECT_MUSHROOM9 = 739, // champignon 9 + OBJECT_APOLLO1 = 900, // apollo lem + OBJECT_APOLLO2 = 901, // apollo jeep + OBJECT_APOLLO3 = 902, // apollo flag + OBJECT_APOLLO4 = 903, // apollo module + OBJECT_APOLLO5 = 904, // apollo antenna + OBJECT_HOME1 = 910, // maison 1 + OBJECT_MAX = 1000, +}; + +enum ObjectMaterial +{ + OM_METAL = 0, // métal + OM_PLASTIC = 1, // plastique + OM_HUMAN = 2, // cosmonaute + OM_ANIMAL = 3, // insecte + OM_VEGETAL = 4, // plante + OM_MINERAL = 5, // pierre +}; + +typedef struct +{ + char bUsed; + int object; // numéro de l'objet dans CD3DEngine + int parentPart; // numéro de la partie père + int masterParti; // canal de la particule maître + D3DVECTOR position; + D3DVECTOR angle; + D3DVECTOR zoom; + char bTranslate; + char bRotate; + char bZoom; + D3DMATRIX matTranslate; + D3DMATRIX matRotate; + D3DMATRIX matTransform; + D3DMATRIX matWorld; +} +ObjectPart; + +typedef struct +{ + float wheelFront; // position X des roues avant + float wheelBack; // position X des roues arrières + float wheelLeft; // position Z des roues gauches + float wheelRight; // position Z des roues droites + float height; // hauteur normale au-dessus du sol + D3DVECTOR posPower; // position de la pile +} +Character; + +typedef struct +{ + char name[20]; // nom de l'information + float value; // valeur de l'information +} +Info; + +enum ExploType +{ + EXPLO_BOUM = 1, + EXPLO_BURN = 2, + EXPLO_WATER = 3, +}; + +enum ResetCap +{ + RESET_NONE = 0, + RESET_MOVE = 1, + RESET_DELETE = 2, +}; + +enum RadarFilter +{ + FILTER_NONE = 0, + FILTER_ONLYLANDING = 1, + FILTER_ONLYFLYING = 2, +}; + + + + +class CObject +{ +public: + CObject(CInstanceManager* iMan); + ~CObject(); + + void DeleteObject(BOOL bAll=FALSE); + void Simplify(); + BOOL ExploObject(ExploType type, float force, float decay=1.0f); + + BOOL EventProcess(const Event &event); + void UpdateMapping(); + + int CreatePart(); + void DeletePart(int part); + void SetObjectRank(int part, int objRank); + int RetObjectRank(int part); + void SetObjectParent(int part, int parent); + void SetType(ObjectType type); + ObjectType RetType(); + char* RetName(); + void SetOption(int option); + int RetOption(); + + void SetID(int id); + int RetID(); + + BOOL Write(char *line); + BOOL Read(char *line); + + void SetDrawWorld(BOOL bDraw); + void SetDrawFront(BOOL bDraw); + + BOOL CreateVehicle(D3DVECTOR pos, float angle, ObjectType type, float power, BOOL bTrainer, BOOL bToy); + BOOL CreateInsect(D3DVECTOR pos, float angle, ObjectType type); + BOOL CreateBuilding(D3DVECTOR pos, float angle, float height, ObjectType type, float power=1.0f); + BOOL CreateResource(D3DVECTOR pos, float angle, ObjectType type, float power=1.0f); + BOOL CreateFlag(D3DVECTOR pos, float angle, ObjectType type); + BOOL CreateBarrier(D3DVECTOR pos, float angle, float height, ObjectType type); + BOOL CreatePlant(D3DVECTOR pos, float angle, float height, ObjectType type); + BOOL CreateMushroom(D3DVECTOR pos, float angle, float height, ObjectType type); + BOOL CreateTeen(D3DVECTOR pos, float angle, float zoom, float height, ObjectType type); + BOOL CreateQuartz(D3DVECTOR pos, float angle, float height, ObjectType type); + BOOL CreateRoot(D3DVECTOR pos, float angle, float height, ObjectType type); + BOOL CreateHome(D3DVECTOR pos, float angle, float height, ObjectType type); + BOOL CreateRuin(D3DVECTOR pos, float angle, float height, ObjectType type); + BOOL CreateApollo(D3DVECTOR pos, float angle, ObjectType type); + + BOOL ReadProgram(int rank, char* filename); + BOOL WriteProgram(int rank, char* filename); + BOOL RunProgram(int rank); + + int RetShadowLight(); + int RetEffectLight(); + + void FlushCrashShere(); + int CreateCrashSphere(D3DVECTOR pos, float radius, Sound sound, float hardness=0.45f); + int RetCrashSphereTotal(); + BOOL GetCrashSphere(int rank, D3DVECTOR &pos, float &radius); + float RetCrashSphereHardness(int rank); + Sound RetCrashSphereSound(int rank); + void DeleteCrashSphere(int rank); + void SetGlobalSphere(D3DVECTOR pos, float radius); + void GetGlobalSphere(D3DVECTOR &pos, float &radius); + void SetJotlerSphere(D3DVECTOR pos, float radius); + void GetJotlerSphere(D3DVECTOR &pos, float &radius); + void SetShieldRadius(float radius); + float RetShieldRadius(); + + void SetFloorHeight(float height); + void FloorAdjust(); + + void SetLinVibration(D3DVECTOR dir); + D3DVECTOR RetLinVibration(); + void SetCirVibration(D3DVECTOR dir); + D3DVECTOR RetCirVibration(); + void SetInclinaison(D3DVECTOR dir); + D3DVECTOR RetInclinaison(); + + void SetPosition(int part, const D3DVECTOR &pos); + D3DVECTOR RetPosition(int part); + void SetAngle(int part, const D3DVECTOR &angle); + D3DVECTOR RetAngle(int part); + void SetAngleY(int part, float angle); + void SetAngleX(int part, float angle); + void SetAngleZ(int part, float angle); + float RetAngleY(int part); + float RetAngleX(int part); + float RetAngleZ(int part); + void SetZoom(int part, float zoom); + void SetZoom(int part, D3DVECTOR zoom); + D3DVECTOR RetZoom(int part); + void SetZoomX(int part, float zoom); + float RetZoomX(int part); + void SetZoomY(int part, float zoom); + float RetZoomY(int part); + void SetZoomZ(int part, float zoom); + float RetZoomZ(int part); + + float RetWaterLevel(); + + void SetTrainer(BOOL bEnable); + BOOL RetTrainer(); + + void SetToy(BOOL bEnable); + BOOL RetToy(); + + void SetManual(BOOL bManual); + BOOL RetManual(); + + void SetResetCap(ResetCap cap); + ResetCap RetResetCap(); + void SetResetBusy(BOOL bBusy); + BOOL RetResetBusy(); + void SetResetPosition(const D3DVECTOR &pos); + D3DVECTOR RetResetPosition(); + void SetResetAngle(const D3DVECTOR &angle); + D3DVECTOR RetResetAngle(); + void SetResetRun(int run); + int RetResetRun(); + + void SetMasterParticule(int part, int parti); + int RetMasterParticule(int part); + + void SetPower(CObject* power); + CObject* RetPower(); + void SetFret(CObject* fret); + CObject* RetFret(); + void SetTruck(CObject* truck); + CObject* RetTruck(); + void SetTruckPart(int part); + int RetTruckPart(); + + void InfoFlush(); + void DeleteInfo(int rank); + void SetInfo(int rank, Info info); + Info RetInfo(int rank); + int RetInfoTotal(); + void SetInfoReturn(float value); + float RetInfoReturn(); + void SetInfoUpdate(BOOL bUpdate); + BOOL RetInfoUpdate(); + + BOOL SetCmdLine(int rank, float value); + float RetCmdLine(int rank); + + D3DMATRIX* RetRotateMatrix(int part); + D3DMATRIX* RetTranslateMatrix(int part); + D3DMATRIX* RetTransformMatrix(int part); + D3DMATRIX* RetWorldMatrix(int part); + + void SetViewFromHere(D3DVECTOR &eye, float &dirH, float &dirV, D3DVECTOR &lookat, D3DVECTOR &upVec, CameraType type); + + void SetCharacter(Character* character); + void GetCharacter(Character* character); + Character* RetCharacter(); + + float RetAbsTime(); + + void SetEnergy(float level); + float RetEnergy(); + + void SetCapacity(float capacity); + float RetCapacity(); + + void SetShield(float level); + float RetShield(); + + void SetRange(float delay); + float RetRange(); + + void SetTransparency(float value); + float RetTransparency(); + + ObjectMaterial RetMaterial(); + + void SetGadget(BOOL bMode); + BOOL RetGadget(); + + void SetFixed(BOOL bFixed); + BOOL RetFixed(); + + void SetClip(BOOL bClip); + BOOL RetClip(); + + BOOL JostleObject(float force); + + void StartDetectEffect(CObject *target, BOOL bFound); + + void SetVirusMode(BOOL bEnable); + BOOL RetVirusMode(); + float RetVirusTime(); + + void SetCameraType(CameraType type); + CameraType RetCameraType(); + void SetCameraDist(float dist); + float RetCameraDist(); + void SetCameraLock(BOOL bLock); + BOOL RetCameraLock(); + + void SetHilite(BOOL bMode); + BOOL RetHilite(); + + void SetSelect(BOOL bMode, BOOL bDisplayError=TRUE); + BOOL RetSelect(BOOL bReal=FALSE); + + void SetSelectable(BOOL bMode); + BOOL RetSelectable(); + + void SetActivity(BOOL bMode); + BOOL RetActivity(); + + void SetVisible(BOOL bVisible); + BOOL RetVisible(); + + void SetEnable(BOOL bEnable); + BOOL RetEnable(); + + void SetCheckToken(BOOL bMode); + BOOL RetCheckToken(); + + void SetProxyActivate(BOOL bActivate); + BOOL RetProxyActivate(); + void SetProxyDistance(float distance); + float RetProxyDistance(); + + void SetMagnifyDamage(float factor); + float RetMagnifyDamage(); + + void SetParam(float value); + float RetParam(); + + void SetExplo(BOOL bExplo); + BOOL RetExplo(); + void SetLock(BOOL bLock); + BOOL RetLock(); + void SetCargo(BOOL bCargo); + BOOL RetCargo(); + void SetBurn(BOOL bBurn); + BOOL RetBurn(); + void SetDead(BOOL bDead); + BOOL RetDead(); + BOOL RetRuin(); + BOOL RetActif(); + + void SetGunGoalV(float gunGoal); + void SetGunGoalH(float gunGoal); + float RetGunGoalV(); + float RetGunGoalH(); + + BOOL StartShowLimit(); + void StopShowLimit(); + + BOOL IsProgram(); + void CreateSelectParticule(); + + void SetRunScript(CScript* script); + CScript* RetRunScript(); + CBotVar* RetBotVar(); + CPhysics* RetPhysics(); + CBrain* RetBrain(); + CMotion* RetMotion(); + CAuto* RetAuto(); + void SetAuto(CAuto* automat); + + void SetDefRank(int rank); + int RetDefRank(); + + BOOL GetTooltipName(char* name); + + void AddDeselList(CObject* pObj); + CObject* SubDeselList(); + void DeleteDeselList(CObject* pObj); + + BOOL CreateShadowCircle(float radius, float intensity, D3DShadowType type=D3DSHADOWNORM); + BOOL CreateShadowLight(float height, D3DCOLORVALUE color); + BOOL CreateEffectLight(float height, D3DCOLORVALUE color); + + void FlatParent(); + + BOOL RetTraceDown(); + void SetTraceDown(BOOL bDown); + int RetTraceColor(); + void SetTraceColor(int color); + float RetTraceWidth(); + void SetTraceWidth(float width); + +protected: + BOOL EventFrame(const Event &event); + void VirusFrame(float rTime); + void PartiFrame(float rTime); + void CreateOtherObject(ObjectType type); + void InitPart(int part); + void UpdateTotalPart(); + int SearchDescendant(int parent, int n); + void UpdateEnergyMapping(); + BOOL UpdateTransformObject(int part, BOOL bForceUpdate); + BOOL UpdateTransformObject(); + void UpdateSelectParticule(); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CLight* m_light; + CTerrain* m_terrain; + CWater* m_water; + CCamera* m_camera; + CParticule* m_particule; + CPhysics* m_physics; + CBrain* m_brain; + CMotion* m_motion; + CAuto* m_auto; + CDisplayText* m_displayText; + CRobotMain* m_main; + CSound* m_sound; + CBotVar* m_botVar; + CScript* m_runScript; + + ObjectType m_type; // OBJECT_* + int m_id; // identificateur unique + char m_name[50]; // nom de l'objet + Character m_character; // caractéristiques + int m_option; // option + int m_partiReactor; // numéro de la particule du réacteur + int m_shadowLight; // numéro de la lumière de l'ombre + float m_shadowHeight; // hauteur de la lumière de l'ombre + int m_effectLight; // numéro de la lumière des effets + float m_effectHeight; // hauteur de la lumière des effets + D3DVECTOR m_linVibration; // vibration linéaire + D3DVECTOR m_cirVibration; // vibration circulaire + D3DVECTOR m_inclinaison; // inclinaison + CObject* m_power; // pile utilisée par le véhicule + CObject* m_fret; // objet transporté + CObject* m_truck; // objet portant celui-ci + int m_truckLink; // partie + float m_energy; // énergie contenue (si pile) + float m_lastEnergy; + float m_capacity; // capacité (si pile) + float m_shield; // bouclier + float m_range; // autonomie de vol + float m_transparency; // transparence (0..1) + int m_material; // matière (0..n) + float m_aTime; + float m_shotTime; // temps depuis dernier coup + BOOL m_bVirusMode; // virus enclanché/déclanché + float m_virusTime; // temps de vie du virus + float m_lastVirusParticule; + float m_lastParticule; + BOOL m_bHilite; + BOOL m_bSelect; // objet sélectionné + BOOL m_bSelectable; // objet sélectionnable + BOOL m_bCheckToken; // objet avec tokens vérifiés + BOOL m_bVisible; // objet actif mais indétectable + BOOL m_bEnable; // objet mort + BOOL m_bProxyActivate; // objet activé si proche + BOOL m_bGadget; // objet non indispensable + BOOL m_bLock; + BOOL m_bExplo; + BOOL m_bCargo; + BOOL m_bBurn; + BOOL m_bDead; + BOOL m_bFlat; + BOOL m_bTrainer; // véhicule d'entraînement (sans télécommande) + BOOL m_bToy; // jouet à clé + BOOL m_bManual; // commandes manuelles (Scribbler) + BOOL m_bFixed; + BOOL m_bClip; + BOOL m_bShowLimit; + float m_showLimitRadius; + float m_gunGoalV; + float m_gunGoalH; + CameraType m_cameraType; + float m_cameraDist; + BOOL m_bCameraLock; + int m_defRank; + float m_magnifyDamage; + float m_proxyDistance; + float m_param; + + int m_crashSphereUsed; // nb de sphères utilisées + D3DVECTOR m_crashSpherePos[MAXCRASHSPHERE]; + float m_crashSphereRadius[MAXCRASHSPHERE]; + float m_crashSphereHardness[MAXCRASHSPHERE]; + Sound m_crashSphereSound[MAXCRASHSPHERE]; + D3DVECTOR m_globalSpherePos; + float m_globalSphereRadius; + D3DVECTOR m_jotlerSpherePos; + float m_jotlerSphereRadius; + float m_shieldRadius; + + int m_totalPart; + ObjectPart m_objectPart[OBJECTMAXPART]; + + int m_totalDesectList; + CObject* m_objectDeselectList[OBJECTMAXDESELLIST]; + + int m_partiSel[4]; + + ResetCap m_resetCap; + BOOL m_bResetBusy; + D3DVECTOR m_resetPosition; + D3DVECTOR m_resetAngle; + int m_resetRun; + + int m_infoTotal; + Info m_info[OBJECTMAXINFO]; + float m_infoReturn; + BOOL m_bInfoUpdate; + + float m_cmdLine[OBJECTMAXCMDLINE]; +}; + + +#endif //_OBJECT_H_ diff --git a/src/particule.cpp b/src/particule.cpp new file mode 100644 index 00000000..e98f249d --- /dev/null +++ b/src/particule.cpp @@ -0,0 +1,4357 @@ +// particule.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DMath.h" +#include "D3DTextr.h" +#include "D3DEngine.h" +#include "language.h" +#include "iman.h" +#include "math3d.h" +#include "event.h" +#include "object.h" +#include "physics.h" +#include "auto.h" +#include "robotmain.h" +#include "terrain.h" +#include "sound.h" +#include "water.h" +#include "particule.h" + + + +#define FOG_HSUP 10.0f +#define FOG_HINF 100.0f + + + + +// Vérifie si un objet est destructible, mais pas un ennemi. + +BOOL IsSoft(ObjectType type) +{ + return ( type == OBJECT_HUMAN || + type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr || // robot ? + type == OBJECT_METAL || + type == OBJECT_POWER || + type == OBJECT_ATOMIC || // fret ? + type == OBJECT_DERRICK || + type == OBJECT_STATION || + type == OBJECT_FACTORY || + type == OBJECT_REPAIR || + type == OBJECT_DESTROYER|| + type == OBJECT_CONVERT || + type == OBJECT_TOWER || + type == OBJECT_RESEARCH || + type == OBJECT_RADAR || + type == OBJECT_INFO || + type == OBJECT_ENERGY || + type == OBJECT_LABO || + type == OBJECT_NUCLEAR || + type == OBJECT_PARA ); // bâtiment ? +} + +// Vérifie si un objet est un ennemi destructible. + +BOOL IsAlien(ObjectType type) +{ + return ( type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM || + type == OBJECT_MOTHER || + type == OBJECT_NEST || + type == OBJECT_BULLET || + type == OBJECT_EGG || + type == OBJECT_MOBILEtg || + type == OBJECT_TEEN28 || + type == OBJECT_TEEN31 ); +} + +// Retourne le facteur d'aténumation pour les tirs amis. + +float RetDecay(ObjectType type) +{ + if ( IsSoft(type) ) return 0.2f; + return 1.0f; +} + + + +// Application constructor. + +CParticule::CParticule(CInstanceManager *iMan, CD3DEngine* engine) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_PARTICULE, this); + + m_pD3DDevice = 0; + m_engine = engine; + m_main = 0; + m_terrain = 0; + m_water = 0; + m_sound = 0; + m_uniqueStamp = 0; + m_exploGunCounter = 0; + m_lastTimeGunDel = 0.0f; + m_absTime = 0.0f; + + FlushParticule(); +} + +// Application destructor. Free memory. + +CParticule::~CParticule() +{ + m_iMan->DeleteInstance(CLASS_PARTICULE, this); +} + + +void CParticule::SetD3DDevice(LPDIRECT3DDEVICE7 device) +{ + m_pD3DDevice = device; +} + + +// Supprime toutes les particules. + +void CParticule::FlushParticule() +{ + int i, j; + + for ( i=0 ; iSearchInstance(CLASS_MAIN); + } + +#if 0 + if ( sheet == SH_WORLD && + type != PARTISELY && + type != PARTISELR && + type != PARTIGUN1 && + type != PARTIGUN2 && + type != PARTIGUN3 && + type != PARTIGUN4 && + type != PARTIQUARTZ && + !m_main->RetMovieLock() ) + { + dist = Length(pos, m_engine->RetEyePt()); + if ( dist > 300.0f ) return -1; + } +#endif + + t = -1; + if ( type == PARTIEXPLOT || + type == PARTIEXPLOO || + type == PARTIMOTOR || + type == PARTIBLITZ || + type == PARTICRASH || + type == PARTIVAPOR || + type == PARTIGAS || + type == PARTIBASE || + type == PARTIFIRE || + type == PARTIFIREZ || + type == PARTIBLUE || + type == PARTIROOT || + type == PARTIRECOVER || + type == PARTIEJECT || + type == PARTISCRAPS || + type == PARTIGUN2 || + type == PARTIGUN3 || + type == PARTIGUN4 || + type == PARTIQUEUE || + type == PARTIORGANIC1 || + type == PARTIORGANIC2 || + type == PARTIFLAME || + type == PARTIBUBBLE || + type == PARTIERROR || + type == PARTIWARNING || + type == PARTIINFO || + type == PARTISPHERE1 || + type == PARTISPHERE2 || + type == PARTISPHERE4 || + type == PARTISPHERE5 || + type == PARTISPHERE6 || + type == PARTIPLOUF0 || + type == PARTITRACK1 || + type == PARTITRACK2 || + type == PARTITRACK3 || + type == PARTITRACK4 || + type == PARTITRACK5 || + type == PARTITRACK6 || + type == PARTITRACK7 || + type == PARTITRACK8 || + type == PARTITRACK9 || + type == PARTITRACK10 || + type == PARTITRACK11 || + type == PARTITRACK12 || + type == PARTILENS1 || + type == PARTILENS2 || + type == PARTILENS3 || + type == PARTILENS4 || + type == PARTIGFLAT || + type == PARTIDROP || + type == PARTIWATER || + type == PARTILIMIT1 || + type == PARTILIMIT2 || + type == PARTILIMIT3 || + type == PARTILIMIT4 || + type == PARTIEXPLOG1 || + type == PARTIEXPLOG2 ) + { + t = 1; // effect00 + } + if ( type == PARTIGLINT || + type == PARTIGLINTb || + type == PARTIGLINTr || + type == PARTITOTO || + type == PARTISELY || + type == PARTISELR || + type == PARTIQUARTZ || + type == PARTIGUNDEL || + type == PARTICONTROL || + type == PARTISHOW || + type == PARTICHOC || + type == PARTIFOG4 || + type == PARTIFOG5 || + type == PARTIFOG6 || + type == PARTIFOG7 ) + { + t = 2; // effect01 + } + if ( type == PARTIGUN1 || + type == PARTIFLIC || + type == PARTISPHERE0 || + type == PARTISPHERE3 || + type == PARTIFOG0 || + type == PARTIFOG1 || + type == PARTIFOG2 || + type == PARTIFOG3 ) + { + t = 3; // effect02 + } + if ( type == PARTISMOKE1 || + type == PARTISMOKE2 || + type == PARTISMOKE3 || + type == PARTIBLOOD || + type == PARTIBLOODM || + type == PARTIVIRUS1 || + type == PARTIVIRUS2 || + type == PARTIVIRUS3 || + type == PARTIVIRUS4 || + type == PARTIVIRUS5 || + type == PARTIVIRUS6 || + type == PARTIVIRUS7 || + type == PARTIVIRUS8 || + type == PARTIVIRUS9 || + type == PARTIVIRUS10 ) + { + t = 4; // text (D3DSTATETTw) + } + if ( t >= MAXPARTITYPE ) return -1; + if ( t == -1 ) return -1; + + for ( j=0 ; j= PARTIFOG0 && + type <= PARTIFOG9 ) + { + if ( m_fogTotal < MAXPARTIFOG ) + m_fog[m_fogTotal++] = i; + } + + return i | ((m_particule[i].uniqueStamp&0xffff)<<16); + } + } + + return -1; +} + +// Créé une nouvelle particule triangulaire (débris). +// Retourne le canal de la particule crée ou -1 en cas d'erreur. + +int CParticule::CreateFrag(D3DVECTOR pos, D3DVECTOR speed, + D3DTriangle *triangle, + ParticuleType type, + float duration, float mass, + float windSensitivity, int sheet) +{ + D3DVECTOR p1, p2, p3, n; + float l1, l2, l3, dx, dy; + int i, j, t; + + t = 0; + for ( j=0 ; j= MAXPARTITYPE ) return -1; + if ( t == -1 ) return -1; + + for ( j=0 ; jRetWheelTraceQuantity()*MAXWHEELTRACE); + max = MAXWHEELTRACE; + i = m_wheelTraceIndex++; + if ( m_wheelTraceIndex > max ) m_wheelTraceIndex = 0; + + m_wheelTrace[i].type = type; + m_wheelTrace[i].pos[0] = p1; // ul + m_wheelTrace[i].pos[1] = p2; // dl + m_wheelTrace[i].pos[2] = p3; // ur + m_wheelTrace[i].pos[3] = p4; // dr + m_wheelTrace[i].startTime = m_absTime; + + if ( m_terrain == 0 ) + { + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + } + + m_terrain->MoveOnFloor(m_wheelTrace[i].pos[0]); + m_wheelTrace[i].pos[0].y += 0.2f; // juste en dessus du sol + + m_terrain->MoveOnFloor(m_wheelTrace[i].pos[1]); + m_wheelTrace[i].pos[1].y += 0.2f; // juste en dessus du sol + + m_terrain->MoveOnFloor(m_wheelTrace[i].pos[2]); + m_wheelTrace[i].pos[2].y += 0.2f; // juste en dessus du sol + + m_terrain->MoveOnFloor(m_wheelTrace[i].pos[3]); + m_wheelTrace[i].pos[3].y += 0.2f; // juste en dessus du sol + + if ( m_wheelTraceTotal < max ) + { + m_wheelTraceTotal ++; + } + else + { + m_wheelTraceTotal = max; + } +} + + +// Check un numéro de canal. +// Adapte le canal pour qu'il puisse être utilisé comme offset +// dans m_particule. + +BOOL CParticule::CheckChannel(int &channel) +{ + int uniqueStamp; + + uniqueStamp = (channel>>16)&0xffff; + channel &= 0xffff; + + if ( channel < 0 ) return FALSE; + if ( channel >= MAXPARTICULE*MAXPARTITYPE ) return FALSE; +#if 0 + if ( !m_particule[channel].bUsed ) return FALSE; + + if ( m_particule[channel].uniqueStamp != uniqueStamp ) return FALSE; +#else + if ( !m_particule[channel].bUsed ) + { + OutputDebugString("CheckChannel bUsed=FALSE !\n"); + return FALSE; + } + + if ( m_particule[channel].uniqueStamp != uniqueStamp ) + { + OutputDebugString("CheckChannel uniqueStamp !\n"); + return FALSE; + } +#endif + + return TRUE; +} + +// Supprime une particule d'après son rang. + +void CParticule::DeleteRank(int rank) +{ + int i; + + if ( m_totalInterface[rank/MAXPARTICULE][m_particule[rank].sheet] > 0 ) + { + m_totalInterface[rank/MAXPARTICULE][m_particule[rank].sheet] --; + } + + i = m_particule[rank].trackRank; + if ( i != -1 ) // traînée associée ? + { + m_track[i].bUsed = FALSE; // libère la traînée + } + + m_particule[rank].bUsed = FALSE; +} + +// Supprime toutes les particules d'un type donné. + +void CParticule::DeleteParticule(ParticuleType type) +{ + int i; + + for ( i=0 ; i 0 ) + { + m_totalInterface[channel/MAXPARTICULE][m_particule[channel].sheet] --; + } + + i = m_particule[channel].trackRank; + if ( i != -1 ) // traînée associée ? + { + m_track[i].bUsed = FALSE; // libère la traînée + } + + m_particule[channel].bUsed = FALSE; +} + + +// Spécifie l'objet auquel la particule est liée. + +void CParticule::SetObjectLink(int channel, CObject *object) +{ + if ( !CheckChannel(channel) ) return; + m_particule[channel].objLink = object; +} + +// Spécifie l'objet père qui a créé la particule. + +void CParticule::SetObjectFather(int channel, CObject *object) +{ + if ( !CheckChannel(channel) ) return; + m_particule[channel].objFather = object; +} + +void CParticule::SetPosition(int channel, D3DVECTOR pos) +{ + if ( !CheckChannel(channel) ) return; + m_particule[channel].pos = pos; +} + +void CParticule::SetDimension(int channel, FPOINT dim) +{ + if ( !CheckChannel(channel) ) return; + m_particule[channel].dim = dim; +} + +void CParticule::SetZoom(int channel, float zoom) +{ + if ( !CheckChannel(channel) ) return; + m_particule[channel].zoom = zoom; +} + +void CParticule::SetAngle(int channel, float angle) +{ + if ( !CheckChannel(channel) ) return; + m_particule[channel].angle = angle; +} + +void CParticule::SetIntensity(int channel, float intensity) +{ + if ( !CheckChannel(channel) ) return; + m_particule[channel].intensity = intensity; +} + +void CParticule::SetParam(int channel, D3DVECTOR pos, FPOINT dim, float zoom, + float angle, float intensity) +{ + if ( !CheckChannel(channel) ) return; + m_particule[channel].pos = pos; + m_particule[channel].dim = dim; + m_particule[channel].zoom = zoom; + m_particule[channel].angle = angle; + m_particule[channel].intensity = intensity; +} + +void CParticule::SetPhase(int channel, ParticulePhase phase, float duration) +{ + if ( !CheckChannel(channel) ) return; + m_particule[channel].phase = phase; + m_particule[channel].duration = duration; + m_particule[channel].phaseTime = m_particule[channel].time; +} + +// Retourne la position de la particule. + +BOOL CParticule::GetPosition(int channel, D3DVECTOR &pos) +{ + if ( !CheckChannel(channel) ) return FALSE; + pos = m_particule[channel].pos; + return TRUE; +} + + +// Indique si une feuille évolue ou non. + +void CParticule::SetFrameUpdate(int sheet, BOOL bUpdate) +{ + m_bFrameUpdate[sheet] = bUpdate; +} + +// Fait évoluer toutes les particules. + +void CParticule::FrameParticule(float rTime) +{ + CObject* object; + D3DVECTOR eye, pos, speed, wind; + FPOINT ts, ti, dim; + BOOL bPause; + float progress, dp, h, duration, mass, amplitude; + int i, j, r, total; + + if ( m_main == 0 ) + { + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + } + + bPause = ( m_engine->RetPause() && !m_main->RetInfoLock() ); + + if ( m_terrain == 0 ) + { + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + } + if ( m_water == 0 ) + { + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + } + + if ( !bPause ) + { + m_lastTimeGunDel += rTime; + m_absTime += rTime; + } + + wind = m_terrain->RetWind(); + eye = m_engine->RetEyePt(); + + for ( i=0 ; iRetFloorLevel(m_particule[i].pos, TRUE); + } + h += m_particule[i].dim.y*0.75f; + if ( m_particule[i].pos.y < h ) // choc avec le sol ? + { + if ( m_particule[i].type == PARTIPART && + m_particule[i].weight > 3.0f && // assez lourd ? + m_particule[i].bounce < 3 ) + { + amplitude = m_particule[i].weight*0.1f; + amplitude *= 1.0f-0.3f*m_particule[i].bounce; + if ( amplitude > 1.0f ) amplitude = 1.0f; + if ( amplitude > 0.0f ) + { + Play(SOUND_BOUM, m_particule[i].pos, amplitude); + } + } + + if ( m_particule[i].bounce < 3 ) + { + m_particule[i].pos.y = h; + m_particule[i].speed.y *= -0.4f; + m_particule[i].speed.x *= 0.4f; + m_particule[i].speed.z *= 0.4f; + m_particule[i].bounce ++; // un choc de plus + } + else // disparaît après 3 rebonds ? + { + if ( m_particule[i].pos.y < h-10.0f || + m_particule[i].time >= 20.0f ) + { + DeleteRank(i); + continue; + } + } + } + } + + // Gère la traînée associée. + r = m_particule[i].trackRank; + if ( r != -1 ) // traînée existe ? + { + if ( TrackMove(r, m_particule[i].pos, progress) ) + { + DeleteRank(i); + continue; + } + + m_track[r].bDrawParticule = (progress < 1.0f); + } + + if ( m_particule[i].type == PARTITRACK1 ) // explosion technique ? + { + m_particule[i].zoom = 1.0f-(m_particule[i].time-m_particule[i].duration); + + ts.x = 0.375f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTITRACK2 ) // jet bleu ? + { + m_particule[i].zoom = 1.0f-(m_particule[i].time-m_particule[i].duration); + + ts.x = 0.500f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTITRACK3 ) // araignée ? + { + m_particule[i].zoom = 1.0f-(m_particule[i].time-m_particule[i].duration); + + ts.x = 0.500f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTITRACK4 ) // explosion insecte ? + { + m_particule[i].zoom = 1.0f-(m_particule[i].time-m_particule[i].duration); + + ts.x = 0.625f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTITRACK5 ) // derrick ? + { + m_particule[i].zoom = 1.0f-(m_particule[i].time-m_particule[i].duration); + + ts.x = 0.750f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTITRACK6 ) // reset in/out ? + { + ts.x = 0.0f; + ts.y = 0.0f; + ti.x = 0.0f; + ti.y = 0.0f; + } + + if ( m_particule[i].type == PARTITRACK7 || // win-1 ? + m_particule[i].type == PARTITRACK8 || // win-2 ? + m_particule[i].type == PARTITRACK9 || // win-3 ? + m_particule[i].type == PARTITRACK10 ) // win-4 ? + { + m_particule[i].zoom = 1.0f-(m_particule[i].time-m_particule[i].duration); + + ts.x = 0.25f*(m_particule[i].type-PARTITRACK7); + ts.y = 0.25f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTITRACK11 ) // tir phazer ? + { + object = SearchObjectGun(m_particule[i].goal, m_particule[i].pos, m_particule[i].type, m_particule[i].objFather); + m_particule[i].goal = m_particule[i].pos; + if ( object != 0 ) + { + if ( object->RetType() == OBJECT_MOTHER ) + { + object->ExploObject(EXPLO_BOUM, 0.1f); + } + else + { + object->ExploObject(EXPLO_BOUM, 0.0f, RetDecay(object->RetType())); + } + } + + m_particule[i].zoom = 1.0f-(m_particule[i].time-m_particule[i].duration); + + ts.x = 0.375f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTITRACK12 ) // traînée réacteur ? + { + m_particule[i].zoom = 1.0f; + + ts.x = 0.375f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIMOTOR ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f-progress; + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.000f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIBLITZ ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f-progress; + m_particule[i].angle = Rand()*PI*2.0f; + + ts.x = 0.125f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTICRASH ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + +//? m_particule[i].intensity = 1.0f-progress; + if ( progress < 0.25f ) + { + m_particule[i].zoom = progress/0.25f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.25f)/0.75f; + } + +//? ts.x = 0.250f; + ts.x = 0.000f; +//? ts.x = 0.375f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIVAPOR ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].intensity = 1.0f-progress; + m_particule[i].zoom = 1.0f+progress*3.0f; + + ts.x = 0.000f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIGAS ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f-progress; + + ts.x = 0.375f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIBASE ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f+progress*7.0f; + m_particule[i].intensity = powf(1.0f-progress, 3.0f); + + ts.x = 0.375f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIFIRE || + m_particule[i].type == PARTIFIREZ ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( m_particule[i].type == PARTIFIRE ) + { + m_particule[i].zoom = 1.0f-progress; + } + else + { + m_particule[i].zoom = progress; + } + + ts.x = 0.500f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIGUN1 ) // tir fireball ? + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( m_particule[i].testTime >= 0.1f ) + { + m_particule[i].testTime = 0.0f; + + if ( m_terrain->RetFloorHeight(m_particule[i].pos, TRUE) < -2.0f ) + { + m_exploGunCounter ++; + + if ( m_exploGunCounter%2 == 0 ) + { + pos = m_particule[i].goal; + m_terrain->MoveOnFloor(pos, TRUE); + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 0.0f; + dim.x = Rand()*6.0f+6.0f; + dim.y = dim.x; + duration = Rand()*1.0f+1.0f; + mass = 0.0f; + CreateParticule(pos, speed, dim, PARTIEXPLOG1, duration, mass, 1.0f); + + pos.y += 1.0f; + total = (int)(2.0f*m_engine->RetParticuleDensity()); + for ( j=0 ; jExploObject(EXPLO_BURN, 0.0f, RetDecay(object->RetType())); + + m_exploGunCounter ++; + + if ( m_exploGunCounter%2 == 0 ) + { + pos = m_particule[i].pos; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 0.0f; + dim.x = Rand()*6.0f+6.0f; + dim.y = dim.x; + duration = Rand()*1.0f+1.0f; + mass = 0.0f; + CreateParticule(pos, speed, dim, PARTIEXPLOG1, duration, mass, 1.0f); + + pos.y += 1.0f; + total = (int)(2.0f*m_engine->RetParticuleDensity()); + for ( j=0 ; j= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( m_particule[i].testTime >= 0.2f ) + { + m_particule[i].testTime = 0.0f; + object = SearchObjectGun(m_particule[i].goal, m_particule[i].pos, m_particule[i].type, m_particule[i].objFather); + m_particule[i].goal = m_particule[i].pos; + if ( object != 0 ) + { + if ( object->RetShieldRadius() > 0.0f ) // protégé par bouclier ? + { + CreateParticule(m_particule[i].pos, D3DVECTOR(0.0f, 0.0f, 0.0f), FPOINT(6.0f, 6.0f), PARTIGUNDEL, 2.0f); + if ( m_lastTimeGunDel > 0.2f ) + { + m_lastTimeGunDel = 0.0f; + Play(SOUND_GUNDEL, m_particule[i].pos, 1.0f); + } + DeleteRank(i); + continue; + } + else + { + if ( object->RetType() != OBJECT_HUMAN ) + { + Play(SOUND_TOUCH, m_particule[i].pos, 1.0f); + } + object->ExploObject(EXPLO_BOUM, 0.0f); // démarre explosion + } + } + } + + m_particule[i].angle = Rand()*PI*2.0f; + m_particule[i].zoom = 1.0f-progress; + + ts.x = 0.125f; + ts.y = 0.875f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIGUN3 ) // suicide araignée ? + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( m_particule[i].testTime >= 0.2f ) + { + m_particule[i].testTime = 0.0f; + object = SearchObjectGun(m_particule[i].goal, m_particule[i].pos, m_particule[i].type, m_particule[i].objFather); + m_particule[i].goal = m_particule[i].pos; + if ( object != 0 ) + { + if ( object->RetShieldRadius() > 0.0f ) + { + CreateParticule(m_particule[i].pos, D3DVECTOR(0.0f, 0.0f, 0.0f), FPOINT(6.0f, 6.0f), PARTIGUNDEL, 2.0f); + if ( m_lastTimeGunDel > 0.2f ) + { + m_lastTimeGunDel = 0.0f; + Play(SOUND_GUNDEL, m_particule[i].pos, 1.0f); + } + DeleteRank(i); + continue; + } + else + { + object->ExploObject(EXPLO_BURN, 1.0f); // démarre explosion + } + } + } + +//? ts.x = 0.875f; +//? ts.y = 0.750f; + ts.x = 0.500f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIGUN4 ) // tir orgaball ? + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( m_particule[i].testTime >= 0.1f ) + { + m_particule[i].testTime = 0.0f; + + if ( m_terrain->RetFloorHeight(m_particule[i].pos, TRUE) < -2.0f ) + { + m_exploGunCounter ++; + + if ( m_exploGunCounter%2 == 0 ) + { + pos = m_particule[i].goal; + m_terrain->MoveOnFloor(pos, TRUE); + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 0.0f; + dim.x = Rand()*4.0f+2.0f; + dim.y = dim.x; + duration = Rand()*0.7f+0.7f; + mass = 0.0f; + CreateParticule(pos, speed, dim, PARTIEXPLOG2, duration, mass, 1.0f); + } + + if ( m_exploGunCounter%4 == 0 ) + { + Play(SOUND_EXPLOg2, pos, 0.5f); + } + + DeleteRank(i); + continue; + } + + object = SearchObjectGun(m_particule[i].goal, m_particule[i].pos, m_particule[i].type, m_particule[i].objFather); + m_particule[i].goal = m_particule[i].pos; + if ( object != 0 ) + { + object->ExploObject(EXPLO_BOUM, 0.0f, RetDecay(object->RetType())); + + m_exploGunCounter ++; + + if ( m_exploGunCounter%2 == 0 ) + { + pos = m_particule[i].pos; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 0.0f; + dim.x = Rand()*4.0f+2.0f; + dim.y = dim.x; + duration = Rand()*0.7f+0.7f; + mass = 0.0f; + CreateParticule(pos, speed, dim, PARTIEXPLOG2, duration, mass, 1.0f); + } + + if ( m_exploGunCounter%4 == 0 ) + { + Play(SOUND_EXPLOg2, pos, 0.5f); + } + + DeleteRank(i); + continue; + } + } + + m_particule[i].angle = Rand()*PI*2.0f; + m_particule[i].zoom = 1.0f-progress; + + ts.x = 0.125f; + ts.y = 0.875f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIFLIC ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 0.1f+progress; + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.00f; + ts.y = 0.75f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTISHOW ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress < 0.5f ) m_particule[i].intensity = progress/0.5f; + else m_particule[i].intensity = 2.0f-progress/0.5f; + m_particule[i].zoom = 1.0f-progress*0.8f; + m_particule[i].angle -= rTime*PI*0.5f; + + ts.x = 0.50f; + ts.y = 0.00f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTICHOC ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 0.1f+progress; + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.50f; + ts.y = 0.50f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTIGFLAT ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 0.1f+progress; + m_particule[i].intensity = 1.0f-progress; + m_particule[i].angle -= rTime*PI*2.0f; + + ts.x = 0.00f; + ts.y = 0.50f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTILIMIT1 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f; + m_particule[i].intensity = 1.0f; + + ts.x = 0.000f; + ts.y = 0.125f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + if ( m_particule[i].type == PARTILIMIT2 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f; + m_particule[i].intensity = 1.0f; + + ts.x = 0.375f; + ts.y = 0.125f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + if ( m_particule[i].type == PARTILIMIT3 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f; + m_particule[i].intensity = 1.0f; + + ts.x = 0.500f; + ts.y = 0.125f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIFOG0 ) + { + m_particule[i].zoom = progress; + m_particule[i].intensity = 0.3f+sinf(progress)*0.15f; + m_particule[i].angle += rTime*0.05f; + + ts.x = 0.25f; + ts.y = 0.75f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + if ( m_particule[i].type == PARTIFOG1 ) + { + m_particule[i].zoom = progress; + m_particule[i].intensity = 0.3f+sinf(progress)*0.15f; + m_particule[i].angle -= rTime*0.07f; + + ts.x = 0.25f; + ts.y = 0.75f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTIFOG2 ) + { + m_particule[i].zoom = progress; + m_particule[i].intensity = 0.6f+sinf(progress)*0.15f; + m_particule[i].angle += rTime*0.05f; + + ts.x = 0.75f; + ts.y = 0.75f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + if ( m_particule[i].type == PARTIFOG3 ) + { + m_particule[i].zoom = progress; + m_particule[i].intensity = 0.6f+sinf(progress)*0.15f; + m_particule[i].angle -= rTime*0.07f; + + ts.x = 0.75f; + ts.y = 0.75f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTIFOG4 ) + { + m_particule[i].zoom = progress; + m_particule[i].intensity = 0.5f+sinf(progress)*0.2f; + m_particule[i].angle += rTime*0.05f; + + ts.x = 0.00f; + ts.y = 0.25f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + if ( m_particule[i].type == PARTIFOG5 ) + { + m_particule[i].zoom = progress; + m_particule[i].intensity = 0.5f+sinf(progress)*0.2f; + m_particule[i].angle -= rTime*0.07f; + + ts.x = 0.00f; + ts.y = 0.25f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTIFOG6 ) + { + m_particule[i].zoom = progress; + m_particule[i].intensity = 0.5f+sinf(progress)*0.2f; + m_particule[i].angle += rTime*0.05f; + + ts.x = 0.50f; + ts.y = 0.25f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + if ( m_particule[i].type == PARTIFOG7 ) + { + m_particule[i].zoom = progress; + m_particule[i].intensity = 0.5f+sinf(progress)*0.2f; + m_particule[i].angle -= rTime*0.07f; + + ts.x = 0.50f; + ts.y = 0.25f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + // Diminue l'intensité si la caméra est presque à la même + // hauteur (nappe de brouillard à hauteur des yeux). + if ( m_particule[i].type >= PARTIFOG0 && + m_particule[i].type <= PARTIFOG9 ) + { + h = 10.0f; + + if ( m_particule[i].pos.y >= eye.y && + m_particule[i].pos.y < eye.y+h ) + { + m_particule[i].intensity *= (m_particule[i].pos.y-eye.y)/h; + } + if ( m_particule[i].pos.y > eye.y-h && + m_particule[i].pos.y < eye.y ) + { + m_particule[i].intensity *= (eye.y-m_particule[i].pos.y)/h; + } + } + + if ( m_particule[i].type == PARTIEXPLOT || + m_particule[i].type == PARTIEXPLOO ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f-progress/2.0f; + m_particule[i].intensity = 1.0f-progress; + + if ( m_particule[i].type == PARTIEXPLOT ) ts.x = 0.750f; + else ts.x = 0.875f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIEXPLOG1 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.375f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + if ( m_particule[i].type == PARTIEXPLOG2 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.625f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIFLAME ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f-progress/2.0f; + if ( progress < 0.5f ) + { + m_particule[i].intensity = progress/0.5f; + } + else + { + m_particule[i].intensity = 2.0f-progress/0.5f; + } + + ts.x = 0.750f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIBUBBLE ) + { + if ( progress >= 1.0f || + m_particule[i].pos.y >= m_water->RetLevel() ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f-progress/2.0f; + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.250f; + ts.y = 0.875f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTISMOKE1 || + m_particule[i].type == PARTISMOKE2 || + m_particule[i].type == PARTISMOKE3 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress < 0.25f ) + { + m_particule[i].zoom = progress/0.25f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.25f)/0.75f; + } + + ts.x = 0.500f+0.125f*(m_particule[i].type-PARTISMOKE1); + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIBLOOD ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.750f+(rand()%2)*0.125f; + ts.y = 0.875f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIBLOODM ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.875f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIVIRUS1 || + m_particule[i].type == PARTIVIRUS2 || + m_particule[i].type == PARTIVIRUS3 || + m_particule[i].type == PARTIVIRUS4 || + m_particule[i].type == PARTIVIRUS5 || + m_particule[i].type == PARTIVIRUS6 || + m_particule[i].type == PARTIVIRUS7 || + m_particule[i].type == PARTIVIRUS8 || + m_particule[i].type == PARTIVIRUS9 || + m_particule[i].type == PARTIVIRUS10 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress < 0.25f ) + { + m_particule[i].zoom = progress/0.25f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.25f)/0.75f; + } + m_particule[i].angle += rTime*PI*1.0f; + + if ( m_particule[i].type == PARTIVIRUS1 ) // A ? + { + ts.x = 0.0f/256.0f; ts.y = 19.0f/256.0f; + ti.x = 10.0f/256.0f; ti.y = 30.0f/256.0f; + } + if ( m_particule[i].type == PARTIVIRUS2 ) // C ? + { + ts.x = 19.0f/256.0f; ts.y = 19.0f/256.0f; + ti.x = 28.0f/256.0f; ti.y = 30.0f/256.0f; + } + if ( m_particule[i].type == PARTIVIRUS3 ) // E ? + { + ts.x = 36.0f/256.0f; ts.y = 19.0f/256.0f; + ti.x = 45.0f/256.0f; ti.y = 30.0f/256.0f; + } + if ( m_particule[i].type == PARTIVIRUS4 ) // N ? + { + ts.x = 110.0f/256.0f; ts.y = 19.0f/256.0f; + ti.x = 120.0f/256.0f; ti.y = 30.0f/256.0f; + } + if ( m_particule[i].type == PARTIVIRUS5 ) // R ? + { + ts.x = 148.0f/256.0f; ts.y = 19.0f/256.0f; + ti.x = 158.0f/256.0f; ti.y = 30.0f/256.0f; + } + if ( m_particule[i].type == PARTIVIRUS6 ) // T ? + { + ts.x = 166.0f/256.0f; ts.y = 19.0f/256.0f; + ti.x = 175.0f/256.0f; ti.y = 30.0f/256.0f; + } + if ( m_particule[i].type == PARTIVIRUS7 ) // 0 ? + { + ts.x = 90.0f/256.0f; ts.y = 2.0f/256.0f; + ti.x = 98.0f/256.0f; ti.y = 13.0f/256.0f; + } + if ( m_particule[i].type == PARTIVIRUS8 ) // 2 ? + { + ts.x = 103.0f/256.0f; ts.y = 2.0f/256.0f; + ti.x = 111.0f/256.0f; ti.y = 13.0f/256.0f; + } + if ( m_particule[i].type == PARTIVIRUS9 ) // 5 ? + { + ts.x = 125.0f/256.0f; ts.y = 2.0f/256.0f; + ti.x = 132.0f/256.0f; ti.y = 13.0f/256.0f; + } + if ( m_particule[i].type == PARTIVIRUS10 ) // 9 ? + { + ts.x = 153.0f/256.0f; ts.y = 2.0f/256.0f; + ti.x = 161.0f/256.0f; ti.y = 13.0f/256.0f; + } + } + + if ( m_particule[i].type == PARTIBLUE ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f-progress; + + ts.x = 0.625f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIROOT ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress < 0.25f ) + { + m_particule[i].zoom = progress/0.25f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.25f)/0.75f; + } + + ts.x = 0.000f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIRECOVER ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress < 0.25f ) + { + m_particule[i].zoom = progress/0.25f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.25f)/0.75f; + } + + ts.x = 0.875f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIEJECT ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f+powf(progress, 2.0f)*5.0f; + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.625f; + ts.y = 0.875f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTISCRAPS ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f-progress; + + ts.x = 0.625f; + ts.y = 0.875f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIFRAG ) + { + m_particule[i].angle += rTime*PI*0.5f; + + ts.x = 0.0f; + ts.y = 0.0f; + ti.x = 0.0f; + ti.y = 0.0f; + } + + if ( m_particule[i].type == PARTIPART ) + { + ts.x = 0.0f; + ts.y = 0.0f; + ti.x = 0.0f; + ti.y = 0.0f; + } + + if ( m_particule[i].type == PARTIQUEUE ) + { + if ( m_particule[i].testTime >= 0.05f ) + { + m_particule[i].testTime = 0.0f; + + D3DVECTOR pos, speed; + FPOINT dim; + + pos = m_particule[i].pos; +//? speed = -m_particule[i].speed*0.5f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 1.0f*(Rand()*0.8f+0.6f); + dim.y = dim.x; + CreateParticule(pos, speed, dim, PARTIGAS, 0.5f); + } + + ts.x = 0.375f; + ts.y = 0.750f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIORGANIC1 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + + pos = m_particule[i].pos; + dim.x = m_particule[i].dim.x/4.0f; + dim.y = dim.x; + duration = m_particule[i].duration; + mass = m_particule[i].mass; + total = (int)(10.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iRetParticuleDensity()); + for ( i=0 ; i= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f-(m_particule[i].time-m_particule[i].duration); + + ts.x = 0.125f; + ts.y = 0.875f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIGLINT ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress > 0.5f ) + { +//? m_particule[i].zoom = 1.0f-(m_particule[i].time-m_particule[i].duration/2.0f); + m_particule[i].zoom = 1.0f-(progress-0.5f)*2.0f; + } + m_particule[i].angle = m_particule[i].time*PI; + + ts.x = 0.75f; + ts.y = 0.25f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTIGLINTb ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress > 0.5f ) + { + m_particule[i].zoom = 1.0f-(progress-0.5f)*2.0f; + } + m_particule[i].angle = m_particule[i].time*PI; + + ts.x = 0.75f; + ts.y = 0.50f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTIGLINTr ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress > 0.5f ) + { + m_particule[i].zoom = 1.0f-(progress-0.5f)*2.0f; + } + m_particule[i].angle = m_particule[i].time*PI; + + ts.x = 0.75f; + ts.y = 0.00f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type >= PARTILENS1 && + m_particule[i].type <= PARTILENS4 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress < 0.5f ) + { + m_particule[i].zoom = progress*2.0f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.5f)*2.0f; + } +//? m_particule[i].angle = m_particule[i].time*PI; + + ts.x = 0.25f*(m_particule[i].type-PARTILENS1); + ts.y = 0.25f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTICONTROL ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress < 0.3f ) + { + m_particule[i].zoom = progress/0.3f; + } + else + { + m_particule[i].zoom = 1.0f; + m_particule[i].intensity = 1.0f-(progress-0.3f)/0.7f; + } + + ts.x = 0.00f; + ts.y = 0.00f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTIGUNDEL ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress > 0.5f ) + { + m_particule[i].zoom = 1.0f-(m_particule[i].time-m_particule[i].duration/2.0f); + } + m_particule[i].angle = m_particule[i].time*PI; + + ts.x = 0.75f; + ts.y = 0.50f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTIQUARTZ ) + { + if ( progress >= 1.0f ) + { + m_particule[i].time = 0.0f; + m_particule[i].duration = 0.5f+Rand()*2.0f; + m_particule[i].pos.x = m_particule[i].speed.x + (Rand()-0.5f)*m_particule[i].mass; + m_particule[i].pos.y = m_particule[i].speed.y + (Rand()-0.5f)*m_particule[i].mass; + m_particule[i].pos.z = m_particule[i].speed.z + (Rand()-0.5f)*m_particule[i].mass; + m_particule[i].dim.x = 0.5f+Rand()*1.5f; + m_particule[i].dim.y = m_particule[i].dim.x; + progress = 0.0f; + } + + if ( progress < 0.2f ) + { + m_particule[i].zoom = progress/0.2f; + m_particule[i].intensity = 1.0f; + } + else + { + m_particule[i].zoom = 1.0f; + m_particule[i].intensity = 1.0f-(progress-0.2f)/0.8f; + } + + ts.x = 0.25f; + ts.y = 0.25f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTITOTO ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f-progress; + if ( progress < 0.15f ) + { + m_particule[i].intensity = progress/0.15f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.15f)/0.85f; + } + m_particule[i].intensity *= 0.5f; + + ts.x = 0.25f; + ts.y = 0.50f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTIERROR ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = progress*1.0f; + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.500f; + ts.y = 0.875f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIWARNING ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = progress*1.0f; + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.875f; + ts.y = 0.875f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIINFO ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = progress*1.0f; + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.750f; + ts.y = 0.875f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTISELY ) + { + ts.x = 0.75f; + ts.y = 0.25f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + if ( m_particule[i].type == PARTISELR ) + { + ts.x = 0.75f; + ts.y = 0.00f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTISPHERE0 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = progress*m_particule[i].dim.x; +//? m_particule[i].intensity = 1.0f-progress; + if ( progress < 0.65f ) + { + m_particule[i].intensity = progress/0.65f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.65f)/0.35f; + } + m_particule[i].intensity *= 0.5f; + + ts.x = 0.50f; + ts.y = 0.75f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTISPHERE1 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress < 0.30f ) + { + m_particule[i].intensity = progress/0.30f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.30f)/0.70f; + } + m_particule[i].zoom = progress*m_particule[i].dim.x; + m_particule[i].angle = m_particule[i].time*PI*2.0f; + + ts.x = 0.000f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTISPHERE2 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( progress < 0.20f ) + { + m_particule[i].intensity = 1.0f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.20f)/0.80f; + } + m_particule[i].zoom = progress*m_particule[i].dim.x; + m_particule[i].angle = m_particule[i].time*PI*2.0f; + + ts.x = 0.125f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTISPHERE3 ) + { + if ( m_particule[i].phase == PARPHEND && + progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( m_particule[i].phase == PARPHSTART ) + { + m_particule[i].intensity = progress; + if ( m_particule[i].intensity > 1.0f ) + { + m_particule[i].intensity = 1.0f; + } + } + + if ( m_particule[i].phase == PARPHEND ) + { + m_particule[i].intensity = 1.0f-progress; + } + + m_particule[i].zoom = m_particule[i].dim.x; + m_particule[i].angle = m_particule[i].time*PI*0.2f; + + ts.x = 0.25f; + ts.y = 0.75f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTISPHERE4 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = progress*m_particule[i].dim.x; + if ( progress < 0.65f ) + { + m_particule[i].intensity = progress/0.65f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.65f)/0.35f; + } + m_particule[i].intensity *= 0.5f; + + ts.x = 0.125f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTISPHERE5 ) + { + m_particule[i].intensity = 0.7f+sinf(progress)*0.3f; + m_particule[i].zoom = m_particule[i].dim.x*(1.0f+sinf(progress*0.7f)*0.01f); + m_particule[i].angle = m_particule[i].time*PI*0.2f; + + ts.x = 0.25f; + ts.y = 0.50f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTISPHERE6 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = (1.0f-progress)*m_particule[i].dim.x; + m_particule[i].intensity = progress*0.5f; + + ts.x = 0.125f; + ts.y = 0.000f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIPLOUF0 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = progress; +#if 0 + if ( progress <= 0.5f ) + { + m_particule[i].intensity = 1.0f; + } + else + { + m_particule[i].intensity = 1.0f-(progress-0.5f)/0.5f; + } +#else +//? m_particule[i].intensity = 1.0f; + m_particule[i].intensity = 1.0f-progress; +#endif + + ts.x = 0.50f; + ts.y = 0.50f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTIDROP ) + { + if ( progress >= 1.0f || + m_particule[i].pos.y < m_water->RetLevel() ) + { + DeleteRank(i); + continue; + } + + m_particule[i].zoom = 1.0f-progress; + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.750f; + ts.y = 0.500f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIWATER ) + { + if ( progress >= 1.0f || + m_particule[i].pos.y < m_water->RetLevel() ) + { + DeleteRank(i); + continue; + } + + m_particule[i].intensity = 1.0f-progress; + + ts.x = 0.125f; + ts.y = 0.125f; + ti.x = ts.x+0.125f; + ti.y = ts.y+0.125f; + } + + if ( m_particule[i].type == PARTIRAY1 ) // rayon tour ? + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + if ( m_particule[i].testTime >= 0.2f ) + { + m_particule[i].testTime = 0.0f; + object = SearchObjectRay(m_particule[i].pos, m_particule[i].goal, + m_particule[i].type, m_particule[i].objFather); + if ( object != 0 ) + { + object->ExploObject(EXPLO_BOUM, 0.0f); + } + } + + ts.x = 0.00f; + ts.y = 0.00f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + if ( m_particule[i].type == PARTIRAY2 || + m_particule[i].type == PARTIRAY3 ) + { + if ( progress >= 1.0f ) + { + DeleteRank(i); + continue; + } + + ts.x = 0.00f; + ts.y = 0.25f; + ti.x = ts.x+0.25f; + ti.y = ts.y+0.25f; + } + + dp = (1.0f/256.0f)/2.0f; + m_particule[i].texSup.x = ts.x+dp; + m_particule[i].texSup.y = ts.y+dp; + m_particule[i].texInf.x = ti.x-dp; + m_particule[i].texInf.y = ti.y-dp; + m_particule[i].time += rTime; + m_particule[i].testTime += rTime; + } +} + + +// Déplace une traînée. +// Retourne TRUE lorsque la traînée est terminée. + +BOOL CParticule::TrackMove(int i, D3DVECTOR pos, float progress) +{ + D3DVECTOR last; + int h, hh; + + if ( i < 0 || i >= MAXTRACK ) return TRUE; + if ( m_track[i].bUsed == FALSE ) return TRUE; + + if ( progress < 1.0f ) // particule existe ? + { + h = m_track[i].head; + + if ( m_track[i].used == 1 || + m_track[i].last+m_track[i].step <= progress ) + { + m_track[i].last = progress; + last = m_track[i].pos[h]; + h ++; + if ( h == MAXTRACKLEN ) h = 0; + if ( m_track[i].used < MAXTRACKLEN ) m_track[i].used ++; + } + else + { + hh = h-1; + if ( hh < 0 ) hh = MAXTRACKLEN-1; + last = m_track[i].pos[hh]; + } + m_track[i].pos[h] = pos; + m_track[i].len[h] = Length(pos, last); + + m_track[i].head = h; + +//? m_track[i].intensity = 1.0f; + m_track[i].intensity = 1.0f-progress; + } + else // mort lente de la traînée ? + { +//? m_track[i].intensity = 1.0f-(progress-1.0f)/(m_track[i].step*MAXTRACKLEN); + m_track[i].intensity = 0.0f; + } + + return (m_track[i].intensity <= 0.0f); +} + +// Dessine une traînée. + +void CParticule::TrackDraw(int i, ParticuleType type) +{ + D3DVERTEX2 vertex[4]; // 2 triangles + D3DVECTOR corner[4], p1, p2, p, n, eye; + D3DMATRIX matrix; + FPOINT texInf, texSup, rot; + float lTotal, f1, f2, a; + int counter, h; + + // Calcule la longueur totale mémorisée. + lTotal = 0.0f; + h = m_track[i].head; + for ( counter=0 ; counterSetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + if ( type == PARTITRACK1 ) // explosion technique ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 21.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 22.0f/256.0f; // orange + } + if ( type == PARTITRACK2 ) // jet bleu ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 13.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 14.0f/256.0f; // bleu + } + if ( type == PARTITRACK3 ) // araignée ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 5.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 6.0f/256.0f; // brun + } + if ( type == PARTITRACK4 ) // explosion insecte ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 9.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 10.0f/256.0f; // vert foncé + } + if ( type == PARTITRACK5 ) // derrick ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 29.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 30.0f/256.0f; // brun foncé + } + if ( type == PARTITRACK6 ) // reset in/out ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 17.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 18.0f/256.0f; // cyan + } + if ( type == PARTITRACK7 ) // win-1 ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 41.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 42.0f/256.0f; // orange + } + if ( type == PARTITRACK8 ) // win-2 ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 45.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 46.0f/256.0f; // jaune + } + if ( type == PARTITRACK9 ) // win-3 ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 49.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 50.0f/256.0f; // rouge + } + if ( type == PARTITRACK10 ) // win-4 ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 53.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 54.0f/256.0f; // violet + } + if ( type == PARTITRACK11 ) // tir phazer ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 21.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 22.0f/256.0f; // orange + } + if ( type == PARTITRACK12 ) // traînée réacteur ? + { + texInf.x = 64.5f/256.0f; + texInf.y = 21.0f/256.0f; + texSup.x = 95.5f/256.0f; + texSup.y = 22.0f/256.0f; // orange + } + + h = m_track[i].head; + p1 = m_track[i].pos[h]; + f1 = m_track[i].intensity; + + eye = m_engine->RetEyePt(); + a = RotateAngle(eye.x-p1.x, eye.z-p1.z); + + for ( counter=0 ; counterDrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + + if ( f2 < 0.0f ) break; + f1 = f2; + p1 = p2; + } +} + +// Dessine une particule triangulaire. + +void CParticule::DrawParticuleTriangle(int i) +{ + CObject* object; + D3DMATRIX matrix; + D3DVECTOR eye, pos, angle; + + if ( m_particule[i].zoom == 0.0f ) return; + + eye = m_engine->RetEyePt(); + pos = m_particule[i].pos; + + object = m_particule[i].objLink; + if ( object != 0 ) + { + pos += object->RetPosition(0); + } + + angle.x = -RotateAngle(Length2d(pos, eye), pos.y-eye.y); + angle.y = RotateAngle(pos.z-eye.z, pos.x-eye.x); + angle.z = m_particule[i].angle; + + MatRotateXZY(matrix, angle); + matrix._41 = pos.x; + matrix._42 = pos.y; + matrix._43 = pos.z; + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, D3DFVF_VERTEX2, + m_triangle[i].triangle, 3, NULL); + m_engine->AddStatisticTriangle(1); +} + +// Dessine une particule normale. + +void CParticule::DrawParticuleNorm(int i) +{ + CObject* object; + D3DVERTEX2 vertex[4]; // 2 triangles + D3DMATRIX matrix; + D3DVECTOR corner[4], eye, pos, n, angle; + FPOINT dim; + float zoom; + + zoom = m_particule[i].zoom; + if ( !m_engine->RetStateColor() && m_particule[i].intensity < 0.5f ) + { + zoom *= m_particule[i].intensity/0.5f; + } + + if ( zoom == 0.0f ) return; + if ( m_particule[i].intensity == 0.0f ) return; + + if ( m_particule[i].sheet == SH_INTERFACE ) + { + pos = m_particule[i].pos; + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); + + dim.x = m_particule[i].dim.x * zoom; + dim.y = m_particule[i].dim.y * zoom; + + corner[0].x = pos.x+dim.x; + corner[0].y = pos.y+dim.y; + corner[0].z = 0.0f; + + corner[1].x = pos.x-dim.x; + corner[1].y = pos.y+dim.y; + corner[1].z = 0.0f; + + corner[2].x = pos.x+dim.x; + corner[2].y = pos.y-dim.y; + corner[2].z = 0.0f; + + corner[3].x = pos.x-dim.x; + corner[3].y = pos.y-dim.y; + corner[3].z = 0.0f; + + vertex[0] = D3DVERTEX2(corner[1], n, m_particule[i].texSup.x, m_particule[i].texSup.y); + vertex[1] = D3DVERTEX2(corner[0], n, m_particule[i].texInf.x, m_particule[i].texSup.y); + vertex[2] = D3DVERTEX2(corner[3], n, m_particule[i].texSup.x, m_particule[i].texInf.y); + vertex[3] = D3DVERTEX2(corner[2], n, m_particule[i].texInf.x, m_particule[i].texInf.y); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + } + else + { + eye = m_engine->RetEyePt(); + pos = m_particule[i].pos; + + object = m_particule[i].objLink; + if ( object != 0 ) + { + pos += object->RetPosition(0); + } + + angle.x = -RotateAngle(Length2d(pos, eye), pos.y-eye.y); + angle.y = RotateAngle(pos.z-eye.z, pos.x-eye.x); + angle.z = m_particule[i].angle; + + MatRotateXZY(matrix, angle); + matrix._41 = pos.x; + matrix._42 = pos.y; + matrix._43 = pos.z; + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); + + dim.x = m_particule[i].dim.x * zoom; + dim.y = m_particule[i].dim.y * zoom; + + corner[0].x = dim.x; + corner[0].y = dim.y; + corner[0].z = 0.0f; + + corner[1].x = -dim.x; + corner[1].y = dim.y; + corner[1].z = 0.0f; + + corner[2].x = dim.x; + corner[2].y = -dim.y; + corner[2].z = 0.0f; + + corner[3].x = -dim.x; + corner[3].y = -dim.y; + corner[3].z = 0.0f; + + vertex[0] = D3DVERTEX2(corner[1], n, m_particule[i].texSup.x, m_particule[i].texSup.y); + vertex[1] = D3DVERTEX2(corner[0], n, m_particule[i].texInf.x, m_particule[i].texSup.y); + vertex[2] = D3DVERTEX2(corner[3], n, m_particule[i].texSup.x, m_particule[i].texInf.y); + vertex[3] = D3DVERTEX2(corner[2], n, m_particule[i].texInf.x, m_particule[i].texInf.y); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + } +} + +// Dessine une particule plate (horizontale). + +void CParticule::DrawParticuleFlat(int i) +{ + CObject* object; + D3DVERTEX2 vertex[4]; // 2 triangles + D3DMATRIX matrix; + D3DVECTOR corner[4], pos, n, angle, eye; + FPOINT dim; + + if ( m_particule[i].zoom == 0.0f ) return; + if ( m_particule[i].intensity == 0.0f ) return; + + pos = m_particule[i].pos; + + object = m_particule[i].objLink; + if ( object != 0 ) + { + pos += object->RetPosition(0); + } + + angle.x = PI/2.0f; + angle.y = 0.0f; + angle.z = m_particule[i].angle; + +#if 0 + if ( m_engine->RetRankView() == 1 ) // sous l'eau ? + { + angle.x = -PI/2.0f; + pos.y -= 1.0f; + } +#else + if ( m_engine->RetRankView() == 1 ) // sous l'eau ? + { + pos.y -= 1.0f; + } + + eye = m_engine->RetEyePt(); + if ( pos.y > eye.y ) // vu par en-dessous ? + { + angle.x = -PI/2.0f; + } +#endif + + MatRotateXZY(matrix, angle); + matrix._41 = pos.x; + matrix._42 = pos.y; + matrix._43 = pos.z; + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); + + dim.x = m_particule[i].dim.x * m_particule[i].zoom; + dim.y = m_particule[i].dim.y * m_particule[i].zoom; + + corner[0].x = dim.x; + corner[0].y = dim.y; + corner[0].z = 0.0f; + + corner[1].x = -dim.x; + corner[1].y = dim.y; + corner[1].z = 0.0f; + + corner[2].x = dim.x; + corner[2].y = -dim.y; + corner[2].z = 0.0f; + + corner[3].x = -dim.x; + corner[3].y = -dim.y; + corner[3].z = 0.0f; + + vertex[0] = D3DVERTEX2(corner[1], n, m_particule[i].texSup.x, m_particule[i].texSup.y); + vertex[1] = D3DVERTEX2(corner[0], n, m_particule[i].texInf.x, m_particule[i].texSup.y); + vertex[2] = D3DVERTEX2(corner[3], n, m_particule[i].texSup.x, m_particule[i].texInf.y); + vertex[3] = D3DVERTEX2(corner[2], n, m_particule[i].texInf.x, m_particule[i].texInf.y); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); +} + +// Dessine une particule plate pour faire une nappe de brouillard. + +void CParticule::DrawParticuleFog(int i) +{ + CObject* object; + D3DVERTEX2 vertex[4]; // 2 triangles + D3DMATRIX matrix; + D3DVECTOR corner[4], pos, n, angle, eye; + FPOINT dim, zoom; + + if ( !m_engine->RetFog() ) return; + if ( m_particule[i].intensity == 0.0f ) return; + + pos = m_particule[i].pos; + + dim.x = m_particule[i].dim.x; + dim.y = m_particule[i].dim.y; + + if ( m_particule[i].type == PARTIFOG0 || + m_particule[i].type == PARTIFOG2 || + m_particule[i].type == PARTIFOG4 || + m_particule[i].type == PARTIFOG6 ) + { +//? pos.x += sinf(m_particule[i].zoom*1.2f)*dim.x*0.1f; +//? pos.y += cosf(m_particule[i].zoom*1.5f)*dim.y*0.1f; + zoom.x = 1.0f+sinf(m_particule[i].zoom*2.0f)/6.0f; + zoom.y = 1.0f+cosf(m_particule[i].zoom*2.7f)/6.0f; + } + if ( m_particule[i].type == PARTIFOG1 || + m_particule[i].type == PARTIFOG3 || + m_particule[i].type == PARTIFOG5 || + m_particule[i].type == PARTIFOG7 ) + { +//? pos.x += sinf(m_particule[i].zoom*1.0f)*dim.x*0.1f; +//? pos.y += cosf(m_particule[i].zoom*1.3f)*dim.y*0.1f; + zoom.x = 1.0f+sinf(m_particule[i].zoom*3.0f)/6.0f; + zoom.y = 1.0f+cosf(m_particule[i].zoom*3.7f)/6.0f; + } + + dim.x *= zoom.x; + dim.y *= zoom.y; + + object = m_particule[i].objLink; + if ( object != 0 ) + { + pos += object->RetPosition(0); + } + + angle.x = PI/2.0f; + angle.y = 0.0f; + angle.z = m_particule[i].angle; + + if ( m_engine->RetRankView() == 1 ) // sous l'eau ? + { + pos.y -= 1.0f; + } + + eye = m_engine->RetEyePt(); + if ( pos.y > eye.y ) // vu par en-dessous ? + { + angle.x = -PI/2.0f; + } + + MatRotateXZY(matrix, angle); + matrix._41 = pos.x; + matrix._42 = pos.y; + matrix._43 = pos.z; + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); + + corner[0].x = dim.x; + corner[0].y = dim.y; + corner[0].z = 0.0f; + + corner[1].x = -dim.x; + corner[1].y = dim.y; + corner[1].z = 0.0f; + + corner[2].x = dim.x; + corner[2].y = -dim.y; + corner[2].z = 0.0f; + + corner[3].x = -dim.x; + corner[3].y = -dim.y; + corner[3].z = 0.0f; + + vertex[0] = D3DVERTEX2(corner[1], n, m_particule[i].texSup.x, m_particule[i].texSup.y); + vertex[1] = D3DVERTEX2(corner[0], n, m_particule[i].texInf.x, m_particule[i].texSup.y); + vertex[2] = D3DVERTEX2(corner[3], n, m_particule[i].texSup.x, m_particule[i].texInf.y); + vertex[3] = D3DVERTEX2(corner[2], n, m_particule[i].texInf.x, m_particule[i].texInf.y); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); +} + +// Dessine une particule sous forme de rayon. + +void CParticule::DrawParticuleRay(int i) +{ + CObject* object; + D3DVERTEX2 vertex[4]; // 2 triangles + D3DMATRIX matrix; + D3DVECTOR corner[4], eye, pos, goal, n, angle, proj; + FPOINT dim, texInf, texSup; + BOOL bLeft; + float a, len, adv, prop, vario1, vario2; + int r, rank, step, first, last; + + if ( m_particule[i].zoom == 0.0f ) return; + if ( m_particule[i].intensity == 0.0f ) return; + + eye = m_engine->RetEyePt(); + pos = m_particule[i].pos; + goal = m_particule[i].goal; + + object = m_particule[i].objLink; + if ( object != 0 ) + { + pos += object->RetPosition(0); + } + + a = RotateAngle(FPOINT(pos.x,pos.z), FPOINT(goal.x,goal.z), FPOINT(eye.x,eye.z)); + bLeft = (a < PI); + + proj = Projection(pos, goal, eye); + angle.x = -RotateAngle(Length2d(proj, eye), proj.y-eye.y); + angle.y = RotateAngle(pos.z-goal.z, pos.x-goal.x)+PI/2.0f; + angle.z = -RotateAngle(Length2d(pos, goal), pos.y-goal.y); + if ( bLeft ) angle.x = -angle.x; + + MatRotateZXY(matrix, angle); + matrix._41 = pos.x; + matrix._42 = pos.y; + matrix._43 = pos.z; + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + n = D3DVECTOR(0.0f, 0.0f, bLeft?1.0f:-1.0f); + + dim.x = m_particule[i].dim.x * m_particule[i].zoom; + dim.y = m_particule[i].dim.y * m_particule[i].zoom; + + if ( bLeft ) dim.y = -dim.y; + + len = Length(pos, goal); + adv = 0.0f; + + step = (int)(len/(dim.x*2.0f))+1; + + if ( step == 1 ) + { + vario1 = 1.0f; + vario2 = 1.0f; + } + else + { + vario1 = 0.0f; + vario2 = 2.0f; + } + + if ( m_particule[i].type == PARTIRAY2 ) + { + first = 0; + last = step; + vario1 = 0.0f; + vario2 = 0.0f; + } + else if ( m_particule[i].type == PARTIRAY3 ) + { + if ( m_particule[i].time < m_particule[i].duration*0.40f ) + { + prop = m_particule[i].time / (m_particule[i].duration*0.40f); + first = 0; + last = (int)(prop*step); + } + else if ( m_particule[i].time < m_particule[i].duration*0.60f ) + { + first = 0; + last = step; + } + else + { + prop = (m_particule[i].time-m_particule[i].duration*0.60f) / (m_particule[i].duration*0.40f); + first = (int)(prop*step); + last = step; + } + } + else + { + if ( m_particule[i].time < m_particule[i].duration*0.50f ) + { + prop = m_particule[i].time / (m_particule[i].duration*0.50f); + first = 0; + last = (int)(prop*step); + } + else if ( m_particule[i].time < m_particule[i].duration*0.75f ) + { + first = 0; + last = step; + } + else + { + prop = (m_particule[i].time-m_particule[i].duration*0.75f) / (m_particule[i].duration*0.25f); + first = (int)(prop*step); + last = step; + } + } + + corner[0].x = adv; + corner[2].x = adv; + corner[0].y = dim.y; + corner[2].y = -dim.y; + corner[0].z = (Rand()-0.5f)*vario1; + corner[1].z = (Rand()-0.5f)*vario1; + corner[2].z = (Rand()-0.5f)*vario1; + corner[3].z = (Rand()-0.5f)*vario1; + + for ( rank=0 ; rank= first && rank <= last ) + { +#if 1 + texInf = m_particule[i].texInf; + texSup = m_particule[i].texSup; + + r = rand()%16; + texInf.x += 0.25f*(r/4); + texSup.x += 0.25f*(r/4); + if ( r%2 < 1 && adv > 0.0f && m_particule[i].type != PARTIRAY1 ) + { + Swap(texInf.x, texSup.x); + } + if ( r%4 < 2 ) + { + Swap(texInf.y, texSup.y); + } +#else + texInf.x = Mod(texInf.x+0.25f, 1.0f); + texSup.x = Mod(texSup.x+0.25f, 1.0f); +#endif + + vertex[0] = D3DVERTEX2(corner[1], n, texSup.x, texSup.y); + vertex[1] = D3DVERTEX2(corner[0], n, texInf.x, texSup.y); + vertex[2] = D3DVERTEX2(corner[3], n, texSup.x, texInf.y); + vertex[3] = D3DVERTEX2(corner[2], n, texInf.x, texInf.y); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + } + adv += dim.x*2.0f; + } +} + +// Dessine une particule sphérique. + +void CParticule::DrawParticuleSphere(int i) +{ + D3DVERTEX2 vertex[2*16*(16+1)]; // triangles + D3DMATRIX matrix, rot; + D3DVECTOR angle, v0, v1; + FPOINT ts, ti; + float zoom, deltaRingAngle, deltaSegAngle; + float r0,r1, tu0,tv0, tu1,tv1; + int j, ring, seg, numRings, numSegments; + + zoom = m_particule[i].zoom; +#if 0 + if ( !m_engine->RetStateColor() && m_particule[i].intensity < 0.5f ) + { + zoom *= m_particule[i].intensity/0.5f; + } +#endif + + if ( zoom == 0.0f ) return; + + m_engine->SetState(D3DSTATETTb|D3DSTATE2FACE|D3DSTATEWRAP, RetColor(m_particule[i].intensity)); + + D3DUtil_SetIdentityMatrix(matrix); + matrix._11 = zoom; + matrix._22 = zoom; + matrix._33 = zoom; + matrix._41 = m_particule[i].pos.x; + matrix._42 = m_particule[i].pos.y; + matrix._43 = m_particule[i].pos.z; + + if ( m_particule[i].angle != 0.0f ) + { + angle.x = m_particule[i].angle*0.4f; + angle.y = m_particule[i].angle*1.0f; + angle.z = m_particule[i].angle*0.7f; + MatRotateZXY(rot, angle); + matrix = rot*matrix; + } + + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + ts.x = m_particule[i].texSup.x; + ts.y = m_particule[i].texSup.y; + ti.x = m_particule[i].texInf.x; + ti.y = m_particule[i].texInf.y; + + // Choose a tesselation level. + if ( m_particule[i].type == PARTISPHERE3 || + m_particule[i].type == PARTISPHERE5 ) + { + numRings = 16; + numSegments = 16; + } + else + { + numRings = 8; + numSegments = 10; + } + + // Establish constants used in sphere generation. + deltaRingAngle = PI/numRings; + deltaSegAngle = 2.0f*PI/numSegments; + + // Generate the group of rings for the sphere. + j = 0; + for ( ring=0 ; ringDrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, j, NULL); + m_engine->AddStatisticTriangle(j); + + m_engine->SetState(D3DSTATETTb, RetColor(m_particule[i].intensity)); +} + +// Retourne la hauteur en fonction de la progression. + +float ProgressCylinder(float progress) +{ + if ( progress < 0.5f ) + { + return 1.0f - (powf(1.0f-progress*2.0f, 2.0f)); + } + else + { + return 1.0f - (powf(progress*2.0f-1.0f, 2.0f)); + } +} + +// Dessine une particule cylindrique. + +void CParticule::DrawParticuleCylinder(int i) +{ + D3DVERTEX2 vertex[2*5*(10+1)]; // triangles + D3DMATRIX matrix, rot; + D3DVECTOR angle, v0, v1; + FPOINT ts, ti; + float progress, zoom, diam, deltaSegAngle, h[6], d[6]; + float r0,r1, tu0,tv0, tu1,tv1, p1, p2, pp; + int j, ring, seg, numRings, numSegments; + + progress = m_particule[i].zoom; + zoom = m_particule[i].dim.x; + diam = m_particule[i].dim.y; + if ( progress >= 1.0f || zoom == 0.0f ) return; + + m_engine->SetState(D3DSTATETTb|D3DSTATE2FACE|D3DSTATEWRAP, RetColor(m_particule[i].intensity)); + + D3DUtil_SetIdentityMatrix(matrix); + matrix._11 = zoom; + matrix._22 = zoom; + matrix._33 = zoom; + matrix._41 = m_particule[i].pos.x; + matrix._42 = m_particule[i].pos.y; + matrix._43 = m_particule[i].pos.z; + + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + ts.x = m_particule[i].texSup.x; + ts.y = m_particule[i].texSup.y; + ti.x = m_particule[i].texInf.x; + ti.y = m_particule[i].texInf.y; + + numRings = 5; + numSegments = 10; + deltaSegAngle = 2.0f*PI/numSegments; + + if ( m_particule[i].type == PARTIPLOUF0 ) + { +#if 0 + if ( progress <= 0.5f ) + { + p1 = progress/0.5f; // avant + p2 = 0.0f; // arrière + } + else + { + p1 = 1.0f; // avant + p2 = (progress-0.5f)/0.5f; // arrière + ts.y += (ti.y-ts.y)*p2; + } +#else + p1 = progress; // avant + p2 = powf(progress, 5.0f); // arrière +#endif + + for ( ring=0 ; ring<=numRings ; ring++ ) + { + pp = p2+(p1-p2)*((float)ring/numRings); + d[ring] = diam/zoom+pp*2.0f; + h[ring] = ProgressCylinder(pp); + } + } + + j = 0; + for ( ring=0 ; ringDrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, j, NULL); + m_engine->AddStatisticTriangle(j); + + m_engine->SetState(D3DSTATETTb, RetColor(m_particule[i].intensity)); +} + +// Dessine une trace de pneu. + +void CParticule::DrawParticuleWheel(int i) +{ + D3DVECTOR pos[4], center; + D3DVERTEX2 vertex[4]; // 2 triangles + D3DVECTOR n; + FPOINT ts, ti; + float dist, dp; + + dist = Length2d(m_engine->RetEyePt(), m_wheelTrace[i].pos[0]); + if ( dist > 300.0f ) return; + + pos[0] = m_wheelTrace[i].pos[0]; + pos[1] = m_wheelTrace[i].pos[1]; + pos[2] = m_wheelTrace[i].pos[2]; + pos[3] = m_wheelTrace[i].pos[3]; + + if ( m_wheelTrace[i].type == PARTITRACE0 ) // trace au sol blanche ? + { + ts.x = 8.0f/256.0f; + ts.y = 224.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE1 ) // trace au sol noire ? + { + ts.x = 0.0f/256.0f; + ts.y = 224.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE2 ) // trace au sol grise ? + { + ts.x = 0.0f/256.0f; + ts.y = 232.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE3 ) // trace au sol gris clair ? + { + ts.x = 8.0f/256.0f; + ts.y = 232.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE4 ) // trace au sol rouge ? + { + ts.x = 32.0f/256.0f; + ts.y = 224.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE5 ) // trace au sol rose ? + { + ts.x = 40.0f/256.0f; + ts.y = 224.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE6 ) // trace au sol violette ? + { + ts.x = 32.0f/256.0f; + ts.y = 232.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE7 ) // trace au sol orange ? + { + ts.x = 40.0f/256.0f; + ts.y = 232.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE8 ) // trace au sol jaune ? + { + ts.x = 16.0f/256.0f; + ts.y = 224.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE9 ) // trace au sol beige ? + { + ts.x = 24.0f/256.0f; + ts.y = 224.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE10 ) // trace au sol brun ? + { + ts.x = 16.0f/256.0f; + ts.y = 232.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE11 ) // trace au sol peau ? + { + ts.x = 24.0f/256.0f; + ts.y = 232.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE12 ) // trace au sol vert ? + { + ts.x = 48.0f/256.0f; + ts.y = 224.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE13 ) // trace au sol vert clair ? + { + ts.x = 56.0f/256.0f; + ts.y = 224.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE14 ) // trace au sol bleu ? + { + ts.x = 48.0f/256.0f; + ts.y = 232.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE15 ) // trace au sol bleu clair ? + { + ts.x = 56.0f/256.0f; + ts.y = 232.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE16 ) // trace au sol flèche noire ? + { + ts.x = 160.0f/256.0f; + ts.y = 224.0f/256.0f; + } + else if ( m_wheelTrace[i].type == PARTITRACE17 ) // trace au sol flèche rouge ? + { + ts.x = 176.0f/256.0f; + ts.y = 224.0f/256.0f; + } + else + { + return; + } + + if ( m_wheelTrace[i].type == PARTITRACE16 || + m_wheelTrace[i].type == PARTITRACE17 ) + { + ti.x = ts.x+16.0f/256.0f; + ti.y = ts.y+16.0f/256.0f; + } + else + { + ti.x = ts.x+8.0f/256.0f; + ti.y = ts.y+8.0f/256.0f; + } + + dp = (1.0f/256.0f)/2.0f; + ts.x = ts.x+dp; + ts.y = ts.y+dp; + ti.x = ti.x-dp; + ti.y = ti.y-dp; + + n = D3DVECTOR(0.0f, 1.0f, 0.0f); + + vertex[0] = D3DVERTEX2(pos[0], n, ts.x, ts.y); + vertex[1] = D3DVERTEX2(pos[1], n, ti.x, ts.y); + vertex[2] = D3DVERTEX2(pos[2], n, ts.x, ti.y); + vertex[3] = D3DVERTEX2(pos[3], n, ti.x, ti.y); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); +} + +// Dessine toutes les particules. + +void CParticule::DrawParticule(int sheet) +{ + D3DMATERIAL7 mat; + D3DMATRIX matrix; + BOOL bLoadTexture; + char name[20]; + int state, t, i, j, r; + + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_AMBIENT, 0xffffffff); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, TRUE); +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + + // Dessine les particules à base de triangles. + if ( m_totalInterface[0][sheet] > 0 ) + { + for ( i=0 ; iSetTexture(m_triangle[i].texName1); + m_engine->SetMaterial(m_triangle[i].material); + m_engine->SetState(m_triangle[i].state); + DrawParticuleTriangle(i); + } + } + + // Dessines les particules à base de carrés calculés. + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE); + + ZeroMemory( &mat, sizeof(D3DMATERIAL7) ); + mat.diffuse.r = 1.0f; + mat.diffuse.g = 1.0f; + mat.diffuse.b = 1.0f; // blanc + mat.ambient.r = 0.5f; + mat.ambient.g = 0.5f; + mat.ambient.b = 0.5f; + m_engine->SetMaterial(mat); + + // Dessine les traces de pneu. + if ( m_wheelTraceTotal > 0 && sheet == SH_WORLD ) + { +#if _POLISH + m_engine->SetTexture("textp.tga"); +#else + m_engine->SetTexture("text.tga"); +#endif + m_engine->SetState(D3DSTATETTw); +//? m_engine->SetState(D3DSTATENORMAL); + D3DUtil_SetIdentityMatrix(matrix); + m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + for ( i=0 ; i=1 ; t-- ) // noir derrière ! + { + if ( m_totalInterface[t][sheet] == 0 ) continue; + + bLoadTexture = FALSE; + + if ( t == 4 ) state = D3DSTATETTw; // text.tga + else state = D3DSTATETTb; // effect[00..02].tga + m_engine->SetState(state); + + for ( j=0 ; jSetTexture(name); + bLoadTexture = TRUE; + } + + r = m_particule[i].trackRank; + if ( r != -1 ) + { + m_engine->SetState(state); + TrackDraw(r, m_particule[i].type); // dessine la traînée + if ( !m_track[r].bDrawParticule ) continue; + } + + m_engine->SetState(state, RetColor(m_particule[i].intensity)); + + if ( m_particule[i].bRay ) // rayon ? + { + DrawParticuleRay(i); + } + else if ( m_particule[i].type == PARTIFLIC || // rond dans l'eau ? + m_particule[i].type == PARTISHOW || + m_particule[i].type == PARTICHOC || + m_particule[i].type == PARTIGFLAT ) + { + DrawParticuleFlat(i); + } + else if ( m_particule[i].type >= PARTIFOG0 && + m_particule[i].type <= PARTIFOG9 ) + { + DrawParticuleFog(i); + } + else if ( m_particule[i].type >= PARTISPHERE0 && + m_particule[i].type <= PARTISPHERE9 ) // sphère ? + { + DrawParticuleSphere(i); + } + else if ( m_particule[i].type >= PARTIPLOUF0 && + m_particule[i].type <= PARTIPLOUF4 ) // cylindre ? + { + DrawParticuleCylinder(i); + } + else // normal ? + { + DrawParticuleNorm(i); + } + } + } + +//? m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, TRUE); + m_pD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, TRUE); +} + + +// Cherche si un objet fait collision avec une balle. + +CObject* CParticule::SearchObjectGun(D3DVECTOR old, D3DVECTOR pos, + ParticuleType type, CObject *father) +{ + CObject *pObj, *pBest; + D3DVECTOR box1, box2, oPos, p; + ObjectType oType; + BOOL bShield; + float min, oRadius, dist, shieldRadius; + int i, j; + BOOL bHimself; + + if ( m_main->RetMovieLock() ) return 0; // film en cours ? + + bHimself = m_main->RetHimselfDamage(); + + min = 5.0f; + if ( type == PARTIGUN2 ) min = 2.0f; // tir insecte ? + if ( type == PARTIGUN3 ) min = 3.0f; // suicide araignée ? + + box1 = old; + box2 = pos; + if ( box1.x > box2.x ) Swap(box1.x, box2.x); // box1 < box2 + if ( box1.y > box2.y ) Swap(box1.y, box2.y); + if ( box1.z > box2.z ) Swap(box1.z, box2.z); + box1.x -= min; + box1.y -= min; + box1.z -= min; + box2.x += min; + box2.y += min; + box2.z += min; + + pBest = 0; + bShield = FALSE; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; // inactif ? + if ( pObj == father ) continue; + + oType = pObj->RetType(); + + if ( oType == OBJECT_TOTO ) continue; + + if ( type == PARTIGUN1 ) // tir fireball ? + { + if ( oType == OBJECT_MOTHER ) continue; + if ( bHimself ) // dégâts à soi-même ? + { + if ( !IsAlien(oType) && + !IsSoft(oType) ) continue; + } + else // dégats seulement aux ennemis ? + { + if ( !IsAlien(oType) ) continue; + } + } + else if ( type == PARTIGUN2 ) // tir insecte ? + { + if ( !IsSoft(oType) ) continue; + } + else if ( type == PARTIGUN3 ) // suicide araignée ? + { + if ( !IsSoft(oType) ) continue; + } + else if ( type == PARTIGUN4 ) // tir orgaball ? + { + if ( oType == OBJECT_MOTHER ) continue; + if ( bHimself ) // dégâts à soi-même ? + { + if ( !IsAlien(oType) && + !IsSoft(oType) ) continue; + } + else // dégats seulement aux ennemis ? + { + if ( !IsAlien(oType) ) continue; + } + } + else if ( type == PARTITRACK11 ) // tir phazer ? + { + if ( bHimself ) // dégâts à soi-même ? + { + if ( !IsAlien(oType) && + !IsSoft(oType) ) continue; + } + else // dégats seulement aux ennemis ? + { + if ( !IsAlien(oType) ) continue; + } + } + else + { + continue; + } + + oPos = pObj->RetPosition(0); + + if ( type == PARTIGUN2 || // tir insecte ? + type == PARTIGUN3 ) // suicide araignée ? + { + // Test si la balle est entrée dans la sphère d'un bouclier. + shieldRadius = pObj->RetShieldRadius(); + if ( shieldRadius > 0.0f ) + { + dist = Length(oPos, pos); + if ( dist <= shieldRadius ) + { + pBest = pObj; + bShield = TRUE; + } + } + } + if ( bShield ) continue; + + // Test au centre de l'objet, ce qui est nécessaire pour + // les objets qui n'ont pas de sphère au centre (station). + dist = Length(oPos, pos)-4.0f; + if ( dist < min ) + { + pBest = pObj; + } + + // Test avec toutes les sphères de l'objet. + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + if ( oPos.x+oRadius < box1.x || oPos.x-oRadius > box2.x || // hors de la boîte ? + oPos.y+oRadius < box1.y || oPos.y-oRadius > box2.y || + oPos.z+oRadius < box1.z || oPos.z-oRadius > box2.z ) continue; + + p = Projection(old, pos, oPos); + dist = Length(p, oPos)-oRadius; + if ( dist < min ) + { + pBest = pObj; + } + } + } + + return pBest; +} + +// Cherche si un objet fait collision avec un rayon. + +CObject* CParticule::SearchObjectRay(D3DVECTOR pos, D3DVECTOR goal, + ParticuleType type, CObject *father) +{ + CObject* pObj; + D3DVECTOR box1, box2, oPos, p; + ObjectType oType; + float min, dist; + int i; + + if ( m_main->RetMovieLock() ) return 0; // film en cours ? + + min = 10.0f; + + box1 = pos; + box2 = goal; + if ( box1.x > box2.x ) Swap(box1.x, box2.x); // box1 < box2 + if ( box1.y > box2.y ) Swap(box1.y, box2.y); + if ( box1.z > box2.z ) Swap(box1.z, box2.z); + box1.x -= min; + box1.y -= min; + box1.z -= min; + box2.x += min; + box2.y += min; + box2.z += min; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; // inactif ? + if ( pObj == father ) continue; + + oType = pObj->RetType(); + + if ( oType == OBJECT_TOTO ) continue; + + if ( type == PARTIRAY1 && + oType != OBJECT_MOBILEtg && + oType != OBJECT_TEEN28 && + oType != OBJECT_TEEN31 && + oType != OBJECT_ANT && + oType != OBJECT_SPIDER && + oType != OBJECT_BEE && + oType != OBJECT_WORM && + oType != OBJECT_MOTHER && + oType != OBJECT_NEST ) continue; + + oPos = pObj->RetPosition(0); + + if ( oPos.x < box1.x || oPos.x > box2.x || // hors de la boîte ? + oPos.y < box1.y || oPos.y > box2.y || + oPos.z < box1.z || oPos.z > box2.z ) continue; + + p = Projection(pos, goal, oPos); + dist = Length(p, oPos); + if ( dist < min ) return pObj; + } + + return 0; +} + + +// Fait entendre un son. + +void CParticule::Play(Sound sound, D3DVECTOR pos, float amplitude) +{ + if ( m_sound == 0 ) + { + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + } + + m_sound->Play(sound, pos, amplitude); +} + + + +// Cherche la couleur si on est dans le brouillard. +// Retourne noir si on est pas dans le brouillard. + +D3DCOLORVALUE CParticule::RetFogColor(D3DVECTOR pos) +{ + D3DCOLORVALUE result, color; + float dist, factor; + int fog, i; + + result.r = 0.0f; + result.g = 0.0f; + result.b = 0.0f; + result.a = 0.0f; + + for ( fog=0 ; fog= m_particule[i].pos.y+FOG_HSUP ) continue; + if ( pos.y <= m_particule[i].pos.y-FOG_HINF ) continue; + + dist = Length2d(pos, m_particule[i].pos); + if ( dist >= m_particule[i].dim.x*1.5f ) continue; + + // Calcule le facteur horizontal. + factor = 1.0f-powf(dist/(m_particule[i].dim.x*1.5f), 4.0f); + + // Calcule le facteur vertical. + if ( pos.y > m_particule[i].pos.y ) + { + factor *= 1.0f-(pos.y-m_particule[i].pos.y)/FOG_HSUP; + } + else + { + factor *= 1.0f-(m_particule[i].pos.y-pos.y)/FOG_HINF; + } + + factor *= 0.3f; + + if ( m_particule[i].type == PARTIFOG0 || + m_particule[i].type == PARTIFOG1 ) // bleu ? + { + color.r = 0.0f; + color.g = 0.5f; + color.b = 1.0f; + } + else if ( m_particule[i].type == PARTIFOG2 || + m_particule[i].type == PARTIFOG3 ) // rouge ? + { + color.r = 2.0f; + color.g = 1.0f; + color.b = 0.0f; + } + else if ( m_particule[i].type == PARTIFOG4 || + m_particule[i].type == PARTIFOG5 ) // blanc ? + { + color.r = 1.0f; + color.g = 1.0f; + color.b = 1.0f; + } + else if ( m_particule[i].type == PARTIFOG6 || + m_particule[i].type == PARTIFOG7 ) // jaune ? + { + color.r = 0.8f; + color.g = 1.0f; + color.b = 0.4f; + } + else + { + color.r = 0.0f; + color.g = 0.0f; + color.b = 0.0f; + } + + result.r += color.r*factor; + result.g += color.g*factor; + result.b += color.b*factor; + } + + if ( result.r > 0.6f ) result.r = 0.6f; + if ( result.g > 0.6f ) result.g = 0.6f; + if ( result.b > 0.6f ) result.b = 0.6f; + + return result; +} + + +// Ecrit un fichier .BMP contenant toutes les traces de pneu. + +BOOL CParticule::WriteWheelTrace(char *filename, int width, int height, + D3DVECTOR dl, D3DVECTOR ur) +{ + HDC hDC; + HDC hDCImage; + HBITMAP hb; + PBITMAPINFO info; + HBRUSH hBrush; + HPEN hPen; + HGDIOBJ old; + RECT rect; + COLORREF color; + FPOINT pos[4]; + POINT list[4]; + int i; + + if ( !m_engine->GetRenderDC(hDC) ) return FALSE; + + hDCImage = CreateCompatibleDC(hDC); + if ( hDCImage == 0 ) + { + m_engine->ReleaseRenderDC(hDC); + return FALSE; + } + + hb = CreateCompatibleBitmap(hDC, width, height); + if ( hb == 0 ) + { + DeleteDC(hDCImage); + m_engine->ReleaseRenderDC(hDC); + return FALSE; + } + + SelectObject(hDCImage, hb); + + rect.left = 0; + rect.right = width; + rect.top = 0; + rect.bottom = height; + FillRect(hDCImage, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); + + hPen = CreatePen(PS_NULL, 1, 0); + SelectObject(hDCImage, hPen); + + for ( i=0 ; iCreateBitmapInfoStruct(hb); + if ( info == 0 ) + { + DeleteObject(hb); + DeleteDC(hDCImage); + m_engine->ReleaseRenderDC(hDC); + return FALSE; + } + + m_engine->CreateBMPFile(filename, info, hb, hDCImage); + + DeleteObject(hb); + DeleteDC(hDCImage); + m_engine->ReleaseRenderDC(hDC); + return TRUE; +} + diff --git a/src/particule.h b/src/particule.h new file mode 100644 index 00000000..34916e56 --- /dev/null +++ b/src/particule.h @@ -0,0 +1,326 @@ +// particule.h + +#ifndef _PARTICULE_H_ +#define _PARTICULE_H_ + + +#include "D3DEngine.h" + + +class CInstanceManager; +class CD3DEngine; +class CRobotMain; +class CTerrain; +class CWater; +class CObject; +class CSound; + +enum Sound; + + +#define MAXPARTICULE 500 +#define MAXPARTITYPE 5 +#define MAXTRACK 100 +#define MAXTRACKLEN 10 +#define MAXPARTIFOG 100 +#define MAXWHEELTRACE 1000 + +#define SH_WORLD 0 // particule dans le monde sous l'interface +#define SH_FRONT 1 // particule dans le monde sur l'interface +#define SH_INTERFACE 2 // particule dans l'interface +#define SH_MAX 3 + +// type == 0 -> triangles +// type == 1 -> effect00 (fond noir) +// type == 2 -> effect01 (fond noir) +// type == 3 -> effect02 (fond noir) +// type == 4 -> text (fond blanc) + + +enum ParticuleType +{ + PARTIEXPLOT = 1, // explosion technique + PARTIEXPLOO = 2, // explosion organique + PARTIMOTOR = 3, // gaz d'échappement du moteur + PARTIGLINT = 4, // reflet + PARTIBLITZ = 5, // éclair recharge batterie + PARTICRASH = 6, // poussière après chute + PARTIGAS = 7, // gaz du réacteur + PARTIFIRE = 9, // boule de feu qui rétricit + PARTIFIREZ = 10, // boule de feu qui grandit + PARTIBLUE = 11, // boule bleu + PARTISELY = 12, // sélection jaune + PARTISELR = 13, // sélection rouge + PARTIGUN1 = 18, // balle 1 (fireball) + PARTIGUN2 = 19, // balle 2 (fourmi) + PARTIGUN3 = 20, // balle 3 (araignée) + PARTIGUN4 = 21, // balle 4 (orgaball) + PARTIFRAG = 22, // fragment triangulaire + PARTIQUEUE = 23, // queue enflammée + PARTIORGANIC1 = 24, // boule organique mère + PARTIORGANIC2 = 25, // boule organique fille + PARTISMOKE1 = 26, // fumée noire + PARTISMOKE2 = 27, // fumée noire + PARTISMOKE3 = 28, // fumée noire + PARTISMOKE4 = 29, // fumée noire + PARTIBLOOD = 30, // sang homme + PARTIBLOODM = 31, // sang pondeuse + PARTIVAPOR = 32, // vapeur + PARTIVIRUS1 = 33, // virus 1 + PARTIVIRUS2 = 34, // virus 2 + PARTIVIRUS3 = 35, // virus 3 + PARTIVIRUS4 = 36, // virus 4 + PARTIVIRUS5 = 37, // virus 5 + PARTIVIRUS6 = 38, // virus 6 + PARTIVIRUS7 = 39, // virus 7 + PARTIVIRUS8 = 40, // virus 8 + PARTIVIRUS9 = 41, // virus 9 + PARTIVIRUS10 = 42, // virus 10 + PARTIRAY1 = 43, // rayon 1 (tour) + PARTIRAY2 = 44, // rayon 2 (electric arc) + PARTIRAY3 = 45, // rayon 3 + PARTIRAY4 = 46, // rayon 4 + PARTIFLAME = 47, // flamme + PARTIBUBBLE = 48, // bubble + PARTIFLIC = 49, // rond dans l'eau + PARTIEJECT = 50, // éjection du réacteur + PARTISCRAPS = 51, // déchets du réacteur + PARTITOTO = 52, // réacteur de toto + PARTIERROR = 53, // toto dit non + PARTIWARNING = 54, // toto dit bof + PARTIINFO = 54, // toto dit oui + PARTIQUARTZ = 55, // reflet quartz + PARTISPHERE0 = 56, // sphère d'explosion + PARTISPHERE1 = 57, // sphère d'énergie + PARTISPHERE2 = 58, // sphère d'analyse + PARTISPHERE3 = 59, // sphère de bouclier + PARTISPHERE4 = 60, // sphère d'information (emette) + PARTISPHERE5 = 61, // sphère végétale (gravity root) + PARTISPHERE6 = 62, // sphère d'information (receive) + PARTISPHERE7 = 63, // sphère + PARTISPHERE8 = 64, // sphère + PARTISPHERE9 = 65, // sphère + PARTIGUNDEL = 66, // destruction balle par bouclier + PARTIPART = 67, // partie d'objet + PARTITRACK1 = 68, // traînée 1 + PARTITRACK2 = 69, // traînée 2 + PARTITRACK3 = 70, // traînée 3 + PARTITRACK4 = 71, // traînée 4 + PARTITRACK5 = 72, // traînée 5 + PARTITRACK6 = 73, // traînée 6 + PARTITRACK7 = 74, // traînée 7 + PARTITRACK8 = 75, // traînée 8 + PARTITRACK9 = 76, // traînée 9 + PARTITRACK10 = 77, // traînée 10 + PARTITRACK11 = 78, // traînée 11 + PARTITRACK12 = 79, // traînée 12 + PARTITRACK13 = 80, // traînée 13 + PARTITRACK14 = 81, // traînée 14 + PARTITRACK15 = 82, // traînée 15 + PARTITRACK16 = 83, // traînée 16 + PARTITRACK17 = 84, // traînée 17 + PARTITRACK18 = 85, // traînée 18 + PARTITRACK19 = 86, // traînée 19 + PARTITRACK20 = 87, // traînée 20 + PARTIGLINTb = 88, // reflet bleu + PARTIGLINTr = 89, // reflet rouge + PARTILENS1 = 90, // éclat 1 (orange) + PARTILENS2 = 91, // éclat 2 (jaune) + PARTILENS3 = 92, // éclat 3 (rouge) + PARTILENS4 = 93, // éclat 4 (violet) + PARTICONTROL = 94, // reflet sur bouton + PARTISHOW = 95, // montre un lieu + PARTICHOC = 96, // onde de choc + PARTIGFLAT = 97, // montre si le sol est plat + PARTIRECOVER = 98, // boule bleu pour recycleur + PARTIROOT = 100, // fumée gravity root + PARTIPLOUF0 = 101, // plouf + PARTIPLOUF1 = 102, // plouf + PARTIPLOUF2 = 103, // plouf + PARTIPLOUF3 = 104, // plouf + PARTIPLOUF4 = 105, // plouf + PARTIDROP = 106, // goutte + PARTIFOG0 = 107, // brouillard 0 + PARTIFOG1 = 108, // brouillard 1 + PARTIFOG2 = 109, // brouillard 2 + PARTIFOG3 = 110, // brouillard 3 + PARTIFOG4 = 111, // brouillard 4 + PARTIFOG5 = 112, // brouillard 5 + PARTIFOG6 = 113, // brouillard 6 + PARTIFOG7 = 114, // brouillard 7 + PARTIFOG8 = 115, // brouillard 8 + PARTIFOG9 = 116, // brouillard 9 + PARTILIMIT1 = 117, // montre les limites 1 + PARTILIMIT2 = 118, // montre les limites 2 + PARTILIMIT3 = 119, // montre les limites 3 + PARTILIMIT4 = 120, // montre les limites 4 + PARTIWATER = 121, // goutte d'eau + PARTIEXPLOG1 = 122, // explosion balle 1 + PARTIEXPLOG2 = 123, // explosion balle 2 + PARTIBASE = 124, // gaz du vaisseau spatial + PARTITRACE0 = 140, // trace + PARTITRACE1 = 141, // trace + PARTITRACE2 = 142, // trace + PARTITRACE3 = 143, // trace + PARTITRACE4 = 144, // trace + PARTITRACE5 = 145, // trace + PARTITRACE6 = 146, // trace + PARTITRACE7 = 147, // trace + PARTITRACE8 = 148, // trace + PARTITRACE9 = 149, // trace + PARTITRACE10 = 150, // trace + PARTITRACE11 = 151, // trace + PARTITRACE12 = 152, // trace + PARTITRACE13 = 153, // trace + PARTITRACE14 = 154, // trace + PARTITRACE15 = 155, // trace + PARTITRACE16 = 156, // trace + PARTITRACE17 = 157, // trace + PARTITRACE18 = 158, // trace + PARTITRACE19 = 159, // trace +}; + +enum ParticulePhase +{ + PARPHSTART = 0, + PARPHEND = 1, +}; + +typedef struct +{ + char bUsed; // TRUE -> particule utilisée + char bRay; // TRUE -> rayon avec but + unsigned short uniqueStamp;// marque unique + short sheet; // feuille (0..n) + ParticuleType type; // type PARTI* + ParticulePhase phase; // phase PARPH* + float mass; // masse de la particule (pour les rebonds) + float weight; // poids de la particule (pour le bruit) + float duration; // durée de vie + D3DVECTOR pos; // position absolue (relative si objet lié) + D3DVECTOR goal; // position but (si bRay) + D3DVECTOR speed; // vitesses de déplacement + float windSensitivity; + short bounce; // nb de rebonds + FPOINT dim; // dimensions du rectangle + float zoom; // zoom (0..1) + float angle; // angle de rotation + float intensity; // intensité + FPOINT texSup; // coordonnée texture supérieure + FPOINT texInf; // cooddonnée texture inférieure + float time; // âge de la particule (0..n) + float phaseTime; // âge au début de la phase + float testTime; // temps depuis dernier test + CObject* objLink; // objet père (pour réacteur par exemple) + CObject* objFather; // objet père (pour réacteur par exemple) + short objRank; // rang de l'objet, ou -1 + short trackRank; // rang de la traînée +} +Particule; + +typedef struct +{ + char bUsed; // TRUE -> traînée utilisée + char bDrawParticule; + float step; // durée d'un pas + float last; // progression dernier pas mémorisé + float intensity; // intensité au départ (0..1) + float width; // largeur queue + int used; // nb de positions dans "pos" + int head; // index tête d'écriture + D3DVECTOR pos[MAXTRACKLEN]; + float len[MAXTRACKLEN]; +} +Track; + +typedef struct +{ + ParticuleType type; // type PARTI* + D3DVECTOR pos[4]; // positions rectangle + float startTime; // début de vie +} +WheelTrace; + + + +class CParticule +{ +public: + CParticule(CInstanceManager* iMan, CD3DEngine* engine); + ~CParticule(); + + void SetD3DDevice(LPDIRECT3DDEVICE7 device); + + void FlushParticule(); + void FlushParticule(int sheet); + int CreateParticule(D3DVECTOR pos, D3DVECTOR speed, FPOINT dim, ParticuleType type, float duration=1.0f, float mass=0.0f, float windSensitivity=1.0f, int sheet=0); + int CreateFrag(D3DVECTOR pos, D3DVECTOR speed, D3DTriangle *triangle, ParticuleType type, float duration=1.0f, float mass=0.0f, float windSensitivity=1.0f, int sheet=0); + int CreatePart(D3DVECTOR pos, D3DVECTOR speed, ParticuleType type, float duration=1.0f, float mass=0.0f, float weight=0.0f, float windSensitivity=1.0f, int sheet=0); + int CreateRay(D3DVECTOR pos, D3DVECTOR goal, ParticuleType type, FPOINT dim, float duration=1.0f, int sheet=0); + int CreateTrack(D3DVECTOR pos, D3DVECTOR speed, FPOINT dim, ParticuleType type, float duration=1.0f, float mass=0.0f, float length=10.0f, float width=1.0f); + void CreateWheelTrace(const D3DVECTOR &p1, const D3DVECTOR &p2, const D3DVECTOR &p3, const D3DVECTOR &p4, ParticuleType type); + void DeleteParticule(ParticuleType type); + void DeleteParticule(int channel); + void SetObjectLink(int channel, CObject *object); + void SetObjectFather(int channel, CObject *object); + void SetPosition(int channel, D3DVECTOR pos); + void SetDimension(int channel, FPOINT dim); + void SetZoom(int channel, float zoom); + void SetAngle(int channel, float angle); + void SetIntensity(int channel, float intensity); + void SetParam(int channel, D3DVECTOR pos, FPOINT dim, float zoom, float angle, float intensity); + void SetPhase(int channel, ParticulePhase phase, float duration); + BOOL GetPosition(int channel, D3DVECTOR &pos); + + D3DCOLORVALUE RetFogColor(D3DVECTOR pos); + + void SetFrameUpdate(int sheet, BOOL bUpdate); + void FrameParticule(float rTime); + void DrawParticule(int sheet); + + BOOL WriteWheelTrace(char *filename, int width, int height, D3DVECTOR dl, D3DVECTOR ur); + +protected: + void DeleteRank(int rank); + BOOL CheckChannel(int &channel); + void DrawParticuleTriangle(int i); + void DrawParticuleNorm(int i); + void DrawParticuleFlat(int i); + void DrawParticuleFog(int i); + void DrawParticuleRay(int i); + void DrawParticuleSphere(int i); + void DrawParticuleCylinder(int i); + void DrawParticuleWheel(int i); + CObject* SearchObjectGun(D3DVECTOR old, D3DVECTOR pos, ParticuleType type, CObject *father); + CObject* SearchObjectRay(D3DVECTOR pos, D3DVECTOR goal, ParticuleType type, CObject *father); + void Play(Sound sound, D3DVECTOR pos, float amplitude); + BOOL TrackMove(int i, D3DVECTOR pos, float progress); + void TrackDraw(int i, ParticuleType type); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + LPDIRECT3DDEVICE7 m_pD3DDevice; + CRobotMain* m_main; + CTerrain* m_terrain; + CWater* m_water; + CSound* m_sound; + + Particule m_particule[MAXPARTICULE*MAXPARTITYPE]; + D3DTriangle m_triangle[MAXPARTICULE]; // triangle si PartiType == 0 + Track m_track[MAXTRACK]; + int m_wheelTraceTotal; + int m_wheelTraceIndex; + WheelTrace m_wheelTrace[MAXWHEELTRACE]; + int m_totalInterface[MAXPARTITYPE][SH_MAX]; + BOOL m_bFrameUpdate[SH_MAX]; + int m_fogTotal; + int m_fog[MAXPARTIFOG]; + int m_uniqueStamp; + int m_exploGunCounter; + float m_lastTimeGunDel; + float m_absTime; +}; + + +#endif //_PARTICULE_H_ diff --git a/src/patch16.txt b/src/patch16.txt new file mode 100644 index 00000000..cfd39828 --- /dev/null +++ b/src/patch16.txt @@ -0,0 +1,10 @@ +Liste des fichiers pour le patch 1.6 + +help\cbot.txt (/f) +help\cbot\object.txt +help\cbot\grab.txt +help\cbot\drop.txt +help\cbot\category.txt +help\cbot\extern.txt (/f) +help\object\goal.txt +help\object\atomic.txt diff --git a/src/physics.cpp b/src/physics.cpp new file mode 100644 index 00000000..ad28b659 --- /dev/null +++ b/src/physics.cpp @@ -0,0 +1,3873 @@ +// physics.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "language.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "water.h" +#include "camera.h" +#include "object.h" +#include "pyro.h" +#include "brain.h" +#include "motion.h" +#include "motionhuman.h" +#include "sound.h" +#include "task.h" +#include "cmdtoken.h" +#include "physics.h" + + + +#define LANDING_SPEED 3.0f +#define LANDING_ACCEL 5.0f +#define LANDING_ACCELh 1.5f + + + + +// Constructeur de l'objet. + +CPhysics::CPhysics(CInstanceManager* iMan, CObject* object) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_PHYSICS, this, 100); + + m_object = object; + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_light = (CLight*)m_iMan->SearchInstance(CLASS_LIGHT); + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + m_brain = 0; + m_motion = 0; + + m_type = TYPE_ROLLING; + m_gravity = 9.81f; // gravité terrestre par défaut + m_time = 0.0f; + m_timeUnderWater = 0.0f; + m_motorSpeed = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_bMotor = FALSE; + m_bLand = TRUE; // au sol + m_bSwim = FALSE; // dans l'air + m_bCollision = FALSE; + m_bObstacle = FALSE; + m_repeatCollision = 0; + m_linVibrationFactor = 1.0f; + m_cirVibrationFactor = 1.0f; + m_inclinaisonFactor = 1.0f; + m_lastPowerParticule = 0.0f; + m_lastSlideParticule = 0.0f; + m_lastMotorParticule = 0.0f; + m_lastWaterParticule = 0.0f; + m_lastUnderParticule = 0.0f; + m_lastPloufParticule = 0.0f; + m_lastFlameParticule = 0.0f; + m_bWheelParticuleBrake = FALSE; + m_absorbWater = 0.0f; + m_reactorTemperature = 0.0f; + m_reactorRange = 1.0f; + m_timeReactorFail = 0.0f; + m_lastEnergy = 0.0f; + m_lastSoundWater = 0.0f; + m_lastSoundInsect = 0.0f; + m_restBreakParticule = 0.0f; + m_floorHeight = 0.0f; + m_soundChannel = -1; + m_soundChannelSlide = -1; + m_soundTimePshhh = 0.0f; + m_soundTimeJostle = 0.0f; + m_soundTimeBoum = 0.0f; + m_bSoundSlow = TRUE; + m_bFreeze = FALSE; + m_bForceUpdate = TRUE; + m_bLowLevel = FALSE; + + ZeroMemory(&m_linMotion, sizeof(Motion)); + ZeroMemory(&m_cirMotion, sizeof(Motion)); +} + +// Destructeur de l'objet. + +CPhysics::~CPhysics() +{ + m_iMan->DeleteInstance(CLASS_PHYSICS, this); +} + + +// Détruit l'objet. + +void CPhysics::DeleteObject(BOOL bAll) +{ + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 0.3f, SOPER_STOP); + m_soundChannel = -1; + } + if ( m_soundChannelSlide != -1 ) + { + m_sound->FlushEnvelope(m_soundChannelSlide); + m_sound->AddEnvelope(m_soundChannelSlide, 0.0f, 1.0f, 0.3f, SOPER_STOP); + m_soundChannelSlide = -1; + } +} + + + +void CPhysics::SetBrain(CBrain* brain) +{ + m_brain = brain; +} + +void CPhysics::SetMotion(CMotion* motion) +{ + m_motion = motion; +} + +// Gestion du type. + +void CPhysics::SetType(PhysicsType type) +{ + m_type = type; +} + +PhysicsType CPhysics::RetType() +{ + return m_type; +} + + + +// Sauve tous les paramètres de l'objet. + +BOOL CPhysics::Write(char *line) +{ + char name[100]; + + sprintf(name, " motor=%.2f;%.2f;%.2f", m_motorSpeed.x, m_motorSpeed.y, m_motorSpeed.z); + strcat(line, name); + + if ( m_type == TYPE_FLYING ) + { + sprintf(name, " reactorRange=%.2f", RetReactorRange()); + strcat(line, name); + + sprintf(name, " land=%d", RetLand()); + strcat(line, name); + } + + return TRUE; +} + +// Restitue tous les paramètres de l'objet. + +BOOL CPhysics::Read(char *line) +{ + m_motorSpeed = OpDir(line, "motor"); + + if ( m_type == TYPE_FLYING ) + { + SetReactorRange(OpFloat(line, "reactorRange", 0.0f)); + SetLand(OpInt(line, "land", 0)); + } + + return TRUE; +} + + + +// Gestion de la force de gravité. + +void CPhysics::SetGravity(float value) +{ + m_gravity = value; +} + +float CPhysics::RetGravity() +{ + return m_gravity; +} + + +// Retourne la hauteur au-dessus du sol. + +float CPhysics::RetFloorHeight() +{ + return m_floorHeight; +} + + +// Gestion de l'état du moteur. + +void CPhysics::SetMotor(BOOL bState) +{ + int light; + + m_bMotor = bState; + + light = m_object->RetShadowLight(); + if ( light != -1 ) + { + m_light->SetLightIntensity(light, m_bMotor?1.0f:0.0f); + m_light->SetLightIntensitySpeed(light, 3.0f); + } +} + +BOOL CPhysics::RetMotor() +{ + return m_bMotor; +} + + +// Gestion de l'état en vol/au sol. + +void CPhysics::SetLand(BOOL bState) +{ + m_bLand = bState; + SetMotor(!bState); // allume le réacteur si on part en vol +} + +BOOL CPhysics::RetLand() +{ + return m_bLand; +} + + +// Gestion de l'état dans l'air/l'eau. + +void CPhysics::SetSwim(BOOL bState) +{ + if ( !m_bSwim && bState ) // entre dans l'eau ? + { + m_timeUnderWater = 0.0f; + } + m_bSwim = bState; +} + +BOOL CPhysics::RetSwim() +{ + return m_bSwim; +} + + +// Indique s une collision a eu lieu. + +void CPhysics::SetCollision(BOOL bCollision) +{ + m_bCollision = bCollision; +} + +BOOL CPhysics::RetCollision() +{ + return m_bCollision; +} + + +// Indique si l'influence du sol est activée ou non. + +void CPhysics::SetFreeze(BOOL bFreeze) +{ + m_bFreeze = bFreeze; +} + +BOOL CPhysics::RetFreeze() +{ + return m_bFreeze; +} + + +// Retourne le niveau d'automie du réacteur. + +void CPhysics::SetReactorRange(float range) +{ + m_reactorRange = range; +} + +float CPhysics::RetReactorRange() +{ + return m_reactorRange; +} + + +// Spécifie la vitesse du moteur. +// x = avancer/reculer +// y = monter/descendre +// z = tourner + +void CPhysics::SetMotorSpeed(D3DVECTOR speed) +{ + m_motorSpeed = speed; +} + +// Spécifie la vitesse du moteur pour avancer/reculer. +// +1 = avancer +// -1 = reculer + +void CPhysics::SetMotorSpeedX(float speed) +{ + m_motorSpeed.x = speed; +} + +// Spécifie la vitesse du moteur pour monter/descendre. +// +1 = monter +// -1 = descendre + +void CPhysics::SetMotorSpeedY(float speed) +{ + m_motorSpeed.y = speed; +} + +// Spécifie la vitesse du moteur pour tourner. +// +1 = tourner à droite (CW) +// -1 = tourner à gauche (CCW) + +void CPhysics::SetMotorSpeedZ(float speed) +{ + m_motorSpeed.z = speed; +} + +D3DVECTOR CPhysics::RetMotorSpeed() +{ + return m_motorSpeed; +} + +float CPhysics::RetMotorSpeedX() +{ + return m_motorSpeed.x; +} + +float CPhysics::RetMotorSpeedY() +{ + return m_motorSpeed.y; +} + +float CPhysics::RetMotorSpeedZ() +{ + return m_motorSpeed.z; +} + + +// Gestion des vitesses linéaires et angulaires. +// Spécifie la vitesse parallèle au sens de marche. + +void CPhysics::SetLinMotion(PhysicsMode mode, D3DVECTOR value) +{ + if ( mode == MO_ADVACCEL ) m_linMotion.advanceAccel = value; + if ( mode == MO_RECACCEL ) m_linMotion.recedeAccel = value; + if ( mode == MO_STOACCEL ) m_linMotion.stopAccel = value; + if ( mode == MO_TERSPEED ) m_linMotion.terrainSpeed = value; + if ( mode == MO_TERSLIDE ) m_linMotion.terrainSlide = value; + if ( mode == MO_MOTACCEL ) m_linMotion.motorAccel = value; + if ( mode == MO_TERFORCE ) m_linMotion.terrainForce = value; + if ( mode == MO_ADVSPEED ) m_linMotion.advanceSpeed = value; + if ( mode == MO_RECSPEED ) m_linMotion.recedeSpeed = value; + if ( mode == MO_MOTSPEED ) m_linMotion.motorSpeed = value; + if ( mode == MO_CURSPEED ) m_linMotion.currentSpeed = value; + if ( mode == MO_REASPEED ) m_linMotion.realSpeed = value; +} + +D3DVECTOR CPhysics::RetLinMotion(PhysicsMode mode) +{ + if ( mode == MO_ADVACCEL ) return m_linMotion.advanceAccel; + if ( mode == MO_RECACCEL ) return m_linMotion.recedeAccel; + if ( mode == MO_STOACCEL ) return m_linMotion.stopAccel; + if ( mode == MO_TERSPEED ) return m_linMotion.terrainSpeed; + if ( mode == MO_TERSLIDE ) return m_linMotion.terrainSlide; + if ( mode == MO_MOTACCEL ) return m_linMotion.motorAccel; + if ( mode == MO_TERFORCE ) return m_linMotion.terrainForce; + if ( mode == MO_ADVSPEED ) return m_linMotion.advanceSpeed; + if ( mode == MO_RECSPEED ) return m_linMotion.recedeSpeed; + if ( mode == MO_MOTSPEED ) return m_linMotion.motorSpeed; + if ( mode == MO_CURSPEED ) return m_linMotion.currentSpeed; + if ( mode == MO_REASPEED ) return m_linMotion.realSpeed; + return D3DVECTOR(0.0f, 0.0f, 0.0f); +} + +void CPhysics::SetLinMotionX(PhysicsMode mode, float value) +{ + if ( mode == MO_ADVACCEL ) m_linMotion.advanceAccel.x = value; + if ( mode == MO_RECACCEL ) m_linMotion.recedeAccel.x = value; + if ( mode == MO_STOACCEL ) m_linMotion.stopAccel.x = value; + if ( mode == MO_TERSPEED ) m_linMotion.terrainSpeed.x = value; + if ( mode == MO_TERSLIDE ) m_linMotion.terrainSlide.x = value; + if ( mode == MO_MOTACCEL ) m_linMotion.motorAccel.x = value; + if ( mode == MO_TERFORCE ) m_linMotion.terrainForce.x = value; + if ( mode == MO_ADVSPEED ) m_linMotion.advanceSpeed.x = value; + if ( mode == MO_RECSPEED ) m_linMotion.recedeSpeed.x = value; + if ( mode == MO_MOTSPEED ) m_linMotion.motorSpeed.x = value; + if ( mode == MO_CURSPEED ) m_linMotion.currentSpeed.x = value; + if ( mode == MO_REASPEED ) m_linMotion.realSpeed.x = value; +} + +float CPhysics::RetLinMotionX(PhysicsMode mode) +{ + if ( mode == MO_ADVACCEL ) return m_linMotion.advanceAccel.x; + if ( mode == MO_RECACCEL ) return m_linMotion.recedeAccel.x; + if ( mode == MO_STOACCEL ) return m_linMotion.stopAccel.x; + if ( mode == MO_TERSPEED ) return m_linMotion.terrainSpeed.x; + if ( mode == MO_TERSLIDE ) return m_linMotion.terrainSlide.x; + if ( mode == MO_MOTACCEL ) return m_linMotion.motorAccel.x; + if ( mode == MO_TERFORCE ) return m_linMotion.terrainForce.x; + if ( mode == MO_ADVSPEED ) return m_linMotion.advanceSpeed.x; + if ( mode == MO_RECSPEED ) return m_linMotion.recedeSpeed.x; + if ( mode == MO_MOTSPEED ) return m_linMotion.motorSpeed.x; + if ( mode == MO_CURSPEED ) return m_linMotion.currentSpeed.x; + if ( mode == MO_REASPEED ) return m_linMotion.realSpeed.x; + return 0.0f; +} + +// Spécifie la vitesse d'élévation. + +void CPhysics::SetLinMotionY(PhysicsMode mode, float value) +{ + if ( mode == MO_ADVACCEL ) m_linMotion.advanceAccel.y = value; + if ( mode == MO_RECACCEL ) m_linMotion.recedeAccel.y = value; + if ( mode == MO_STOACCEL ) m_linMotion.stopAccel.y = value; + if ( mode == MO_TERSPEED ) m_linMotion.terrainSpeed.y = value; + if ( mode == MO_TERSLIDE ) m_linMotion.terrainSlide.y = value; + if ( mode == MO_MOTACCEL ) m_linMotion.motorAccel.y = value; + if ( mode == MO_TERFORCE ) m_linMotion.terrainForce.y = value; + if ( mode == MO_ADVSPEED ) m_linMotion.advanceSpeed.y = value; + if ( mode == MO_RECSPEED ) m_linMotion.recedeSpeed.y = value; + if ( mode == MO_MOTSPEED ) m_linMotion.motorSpeed.y = value; + if ( mode == MO_CURSPEED ) m_linMotion.currentSpeed.y = value; + if ( mode == MO_REASPEED ) m_linMotion.realSpeed.y = value; +} + +float CPhysics::RetLinMotionY(PhysicsMode mode) +{ + if ( mode == MO_ADVACCEL ) return m_linMotion.advanceAccel.y; + if ( mode == MO_RECACCEL ) return m_linMotion.recedeAccel.y; + if ( mode == MO_STOACCEL ) return m_linMotion.stopAccel.y; + if ( mode == MO_TERSPEED ) return m_linMotion.terrainSpeed.y; + if ( mode == MO_TERSLIDE ) return m_linMotion.terrainSlide.y; + if ( mode == MO_MOTACCEL ) return m_linMotion.motorAccel.y; + if ( mode == MO_TERFORCE ) return m_linMotion.terrainForce.y; + if ( mode == MO_ADVSPEED ) return m_linMotion.advanceSpeed.y; + if ( mode == MO_RECSPEED ) return m_linMotion.recedeSpeed.y; + if ( mode == MO_MOTSPEED ) return m_linMotion.motorSpeed.y; + if ( mode == MO_CURSPEED ) return m_linMotion.currentSpeed.y; + if ( mode == MO_REASPEED ) return m_linMotion.realSpeed.y; + return 0.0f; +} + +// Spécifie la vitesse perpendiculaire au sens de marche. + +void CPhysics::SetLinMotionZ(PhysicsMode mode, float value) +{ + if ( mode == MO_ADVACCEL ) m_linMotion.advanceAccel.z = value; + if ( mode == MO_RECACCEL ) m_linMotion.recedeAccel.z = value; + if ( mode == MO_STOACCEL ) m_linMotion.stopAccel.z = value; + if ( mode == MO_TERSPEED ) m_linMotion.terrainSpeed.z = value; + if ( mode == MO_TERSLIDE ) m_linMotion.terrainSlide.z = value; + if ( mode == MO_MOTACCEL ) m_linMotion.motorAccel.z = value; + if ( mode == MO_TERFORCE ) m_linMotion.terrainForce.z = value; + if ( mode == MO_ADVSPEED ) m_linMotion.advanceSpeed.z = value; + if ( mode == MO_RECSPEED ) m_linMotion.recedeSpeed.z = value; + if ( mode == MO_MOTSPEED ) m_linMotion.motorSpeed.z = value; + if ( mode == MO_CURSPEED ) m_linMotion.currentSpeed.z = value; + if ( mode == MO_REASPEED ) m_linMotion.realSpeed.z = value; +} + +float CPhysics::RetLinMotionZ(PhysicsMode mode) +{ + if ( mode == MO_ADVACCEL ) return m_linMotion.advanceAccel.z; + if ( mode == MO_RECACCEL ) return m_linMotion.recedeAccel.z; + if ( mode == MO_STOACCEL ) return m_linMotion.stopAccel.z; + if ( mode == MO_TERSPEED ) return m_linMotion.terrainSpeed.z; + if ( mode == MO_TERSLIDE ) return m_linMotion.terrainSlide.z; + if ( mode == MO_MOTACCEL ) return m_linMotion.motorAccel.z; + if ( mode == MO_TERFORCE ) return m_linMotion.terrainForce.z; + if ( mode == MO_ADVSPEED ) return m_linMotion.advanceSpeed.z; + if ( mode == MO_RECSPEED ) return m_linMotion.recedeSpeed.z; + if ( mode == MO_MOTSPEED ) return m_linMotion.motorSpeed.z; + if ( mode == MO_CURSPEED ) return m_linMotion.currentSpeed.z; + if ( mode == MO_REASPEED ) return m_linMotion.realSpeed.z; + return 0.0f; +} + +// Spécifie la rotation autour de l'axe de marche. + +void CPhysics::SetCirMotion(PhysicsMode mode, D3DVECTOR value) +{ + if ( mode == MO_ADVACCEL ) m_cirMotion.advanceAccel = value; + if ( mode == MO_RECACCEL ) m_cirMotion.recedeAccel = value; + if ( mode == MO_STOACCEL ) m_cirMotion.stopAccel = value; + if ( mode == MO_TERSPEED ) m_cirMotion.terrainSpeed = value; + if ( mode == MO_TERSLIDE ) m_cirMotion.terrainSlide = value; + if ( mode == MO_MOTACCEL ) m_cirMotion.motorAccel = value; + if ( mode == MO_TERFORCE ) m_cirMotion.terrainForce = value; + if ( mode == MO_ADVSPEED ) m_cirMotion.advanceSpeed = value; + if ( mode == MO_RECSPEED ) m_cirMotion.recedeSpeed = value; + if ( mode == MO_MOTSPEED ) m_cirMotion.motorSpeed = value; + if ( mode == MO_CURSPEED ) m_cirMotion.currentSpeed = value; + if ( mode == MO_REASPEED ) m_cirMotion.realSpeed = value; +} + +D3DVECTOR CPhysics::RetCirMotion(PhysicsMode mode) +{ + if ( mode == MO_ADVACCEL ) return m_cirMotion.advanceAccel; + if ( mode == MO_RECACCEL ) return m_cirMotion.recedeAccel; + if ( mode == MO_STOACCEL ) return m_cirMotion.stopAccel; + if ( mode == MO_TERSPEED ) return m_cirMotion.terrainSpeed; + if ( mode == MO_TERSLIDE ) return m_cirMotion.terrainSlide; + if ( mode == MO_MOTACCEL ) return m_cirMotion.motorAccel; + if ( mode == MO_TERFORCE ) return m_cirMotion.terrainForce; + if ( mode == MO_ADVSPEED ) return m_cirMotion.advanceSpeed; + if ( mode == MO_RECSPEED ) return m_cirMotion.recedeSpeed; + if ( mode == MO_MOTSPEED ) return m_cirMotion.motorSpeed; + if ( mode == MO_CURSPEED ) return m_cirMotion.currentSpeed; + if ( mode == MO_REASPEED ) return m_cirMotion.realSpeed; + return D3DVECTOR(0.0f, 0.0f, 0.0f); +} + +void CPhysics::SetCirMotionX(PhysicsMode mode, float value) +{ + if ( mode == MO_ADVACCEL ) m_cirMotion.advanceAccel.x = value; + if ( mode == MO_RECACCEL ) m_cirMotion.recedeAccel.x = value; + if ( mode == MO_STOACCEL ) m_cirMotion.stopAccel.x = value; + if ( mode == MO_TERSPEED ) m_cirMotion.terrainSpeed.x = value; + if ( mode == MO_TERSLIDE ) m_cirMotion.terrainSlide.x = value; + if ( mode == MO_MOTACCEL ) m_cirMotion.motorAccel.x = value; + if ( mode == MO_TERFORCE ) m_cirMotion.terrainForce.x = value; + if ( mode == MO_ADVSPEED ) m_cirMotion.advanceSpeed.x = value; + if ( mode == MO_RECSPEED ) m_cirMotion.recedeSpeed.x = value; + if ( mode == MO_MOTSPEED ) m_cirMotion.motorSpeed.x = value; + if ( mode == MO_CURSPEED ) m_cirMotion.currentSpeed.x = value; + if ( mode == MO_REASPEED ) m_cirMotion.realSpeed.x = value; +} + +float CPhysics::RetCirMotionX(PhysicsMode mode) +{ + if ( mode == MO_ADVACCEL ) return m_cirMotion.advanceAccel.x; + if ( mode == MO_RECACCEL ) return m_cirMotion.recedeAccel.x; + if ( mode == MO_STOACCEL ) return m_cirMotion.stopAccel.x; + if ( mode == MO_TERSPEED ) return m_cirMotion.terrainSpeed.x; + if ( mode == MO_TERSLIDE ) return m_cirMotion.terrainSlide.x; + if ( mode == MO_MOTACCEL ) return m_cirMotion.motorAccel.x; + if ( mode == MO_TERFORCE ) return m_cirMotion.terrainForce.x; + if ( mode == MO_ADVSPEED ) return m_cirMotion.advanceSpeed.x; + if ( mode == MO_RECSPEED ) return m_cirMotion.recedeSpeed.x; + if ( mode == MO_MOTSPEED ) return m_cirMotion.motorSpeed.x; + if ( mode == MO_CURSPEED ) return m_cirMotion.currentSpeed.x; + if ( mode == MO_REASPEED ) return m_cirMotion.realSpeed.x; + return 0.0f; +} + +// Spécifie la rotation de direction. + +void CPhysics::SetCirMotionY(PhysicsMode mode, float value) +{ + if ( mode == MO_ADVACCEL ) m_cirMotion.advanceAccel.y = value; + if ( mode == MO_RECACCEL ) m_cirMotion.recedeAccel.y = value; + if ( mode == MO_STOACCEL ) m_cirMotion.stopAccel.y = value; + if ( mode == MO_TERSPEED ) m_cirMotion.terrainSpeed.y = value; + if ( mode == MO_TERSLIDE ) m_cirMotion.terrainSlide.y = value; + if ( mode == MO_MOTACCEL ) m_cirMotion.motorAccel.y = value; + if ( mode == MO_TERFORCE ) m_cirMotion.terrainForce.y = value; + if ( mode == MO_ADVSPEED ) m_cirMotion.advanceSpeed.y = value; + if ( mode == MO_RECSPEED ) m_cirMotion.recedeSpeed.y = value; + if ( mode == MO_MOTSPEED ) m_cirMotion.motorSpeed.y = value; + if ( mode == MO_CURSPEED ) m_cirMotion.currentSpeed.y = value; + if ( mode == MO_REASPEED ) m_cirMotion.realSpeed.y = value; +} + +float CPhysics::RetCirMotionY(PhysicsMode mode) +{ + if ( mode == MO_ADVACCEL ) return m_cirMotion.advanceAccel.y; + if ( mode == MO_RECACCEL ) return m_cirMotion.recedeAccel.y; + if ( mode == MO_STOACCEL ) return m_cirMotion.stopAccel.y; + if ( mode == MO_TERSPEED ) return m_cirMotion.terrainSpeed.y; + if ( mode == MO_TERSLIDE ) return m_cirMotion.terrainSlide.y; + if ( mode == MO_MOTACCEL ) return m_cirMotion.motorAccel.y; + if ( mode == MO_TERFORCE ) return m_cirMotion.terrainForce.y; + if ( mode == MO_ADVSPEED ) return m_cirMotion.advanceSpeed.y; + if ( mode == MO_RECSPEED ) return m_cirMotion.recedeSpeed.y; + if ( mode == MO_MOTSPEED ) return m_cirMotion.motorSpeed.y; + if ( mode == MO_CURSPEED ) return m_cirMotion.currentSpeed.y; + if ( mode == MO_REASPEED ) return m_cirMotion.realSpeed.y; + return 0.0f; +} + +// Spécifie la rotation de montée/descente. + +void CPhysics::SetCirMotionZ(PhysicsMode mode, float value) +{ + if ( mode == MO_ADVACCEL ) m_cirMotion.advanceAccel.z = value; + if ( mode == MO_RECACCEL ) m_cirMotion.recedeAccel.z = value; + if ( mode == MO_STOACCEL ) m_cirMotion.stopAccel.z = value; + if ( mode == MO_TERSPEED ) m_cirMotion.terrainSpeed.z = value; + if ( mode == MO_TERSLIDE ) m_cirMotion.terrainSlide.z = value; + if ( mode == MO_MOTACCEL ) m_cirMotion.motorAccel.z = value; + if ( mode == MO_TERFORCE ) m_cirMotion.terrainForce.z = value; + if ( mode == MO_ADVSPEED ) m_cirMotion.advanceSpeed.z = value; + if ( mode == MO_RECSPEED ) m_cirMotion.recedeSpeed.z = value; + if ( mode == MO_MOTSPEED ) m_cirMotion.motorSpeed.z = value; + if ( mode == MO_CURSPEED ) m_cirMotion.currentSpeed.z = value; + if ( mode == MO_REASPEED ) m_cirMotion.realSpeed.z = value; +} + +float CPhysics::RetCirMotionZ(PhysicsMode mode) +{ + if ( mode == MO_ADVACCEL ) return m_cirMotion.advanceAccel.z; + if ( mode == MO_RECACCEL ) return m_cirMotion.recedeAccel.z; + if ( mode == MO_STOACCEL ) return m_cirMotion.stopAccel.z; + if ( mode == MO_TERSPEED ) return m_cirMotion.terrainSpeed.z; + if ( mode == MO_TERSLIDE ) return m_cirMotion.terrainSlide.z; + if ( mode == MO_MOTACCEL ) return m_cirMotion.motorAccel.z; + if ( mode == MO_TERFORCE ) return m_cirMotion.terrainForce.z; + if ( mode == MO_ADVSPEED ) return m_cirMotion.advanceSpeed.z; + if ( mode == MO_RECSPEED ) return m_cirMotion.recedeSpeed.z; + if ( mode == MO_MOTSPEED ) return m_cirMotion.motorSpeed.z; + if ( mode == MO_CURSPEED ) return m_cirMotion.currentSpeed.z; + if ( mode == MO_REASPEED ) return m_cirMotion.realSpeed.z; + return 0.0f; +} + + +// Retourne la distance linéaire de freinage. +// +// v*v +// d = ----- +// 2a + +float CPhysics::RetLinStopLength(PhysicsMode sMode, PhysicsMode aMode) +{ + float speed, accel; + + speed = RetLinMotionX(sMode); // MO_ADVSPEED/MO_RECSPEED + accel = RetLinMotionX(aMode); // MO_ADVACCEL/MO_RECACCEL/MO_STOACCEL + + if ( m_type == TYPE_FLYING && m_bLand ) // volant au sol ? + { + speed /= LANDING_SPEED; + accel *= LANDING_ACCEL; + } + + return (speed*speed) / (accel*2.0f); +} + +// Retourne l'angle circulaire de freinage. + +float CPhysics::RetCirStopLength() +{ + return m_cirMotion.advanceSpeed.y * m_cirMotion.advanceSpeed.y / + m_cirMotion.stopAccel.y / 2.0f; +} + +// Retourne la longueur avancée en une seconde, au sol, à vitesse maximale. + +float CPhysics::RetLinMaxLength(float dir) +{ + float dist; + + if ( dir > 0.0f ) dist = m_linMotion.advanceSpeed.x; + else dist = m_linMotion.recedeSpeed.x; + + if ( m_type == TYPE_FLYING ) + { + dist /= 5.0f; + } + + return dist; +} + +// Retourne le temps nécessaire pour parcourir une certaine distance. + +float CPhysics::RetLinTimeLength(float dist, float dir) +{ + float accel, decel, dps; + + if ( dir > 0.0f ) + { + accel = RetLinStopLength(MO_ADVSPEED, MO_ADVACCEL); + decel = RetLinStopLength(MO_ADVSPEED, MO_STOACCEL); + } + else + { + accel = RetLinStopLength(MO_RECSPEED, MO_RECACCEL); + decel = RetLinStopLength(MO_RECSPEED, MO_STOACCEL); + } + + dps = RetLinMaxLength(dir); + + return (dist+accel+decel)/dps; +} + +// Retourne la longueur à avancer pour parcourir une certaine +// distance, en tenant compte des accélérations/décélérations. + +float CPhysics::RetLinLength(float dist) +{ + float accDist, desDist; + + if ( dist > 0.0f ) + { + accDist = RetLinStopLength(MO_ADVSPEED, MO_ADVACCEL); + desDist = RetLinStopLength(MO_ADVSPEED, MO_STOACCEL); + + if ( dist > accDist+desDist ) + { + return dist-desDist; + } + + return dist*m_linMotion.stopAccel.x / + (m_linMotion.advanceAccel.x+m_linMotion.stopAccel.x); + } + else + { + dist = -dist; + accDist = RetLinStopLength(MO_RECSPEED, MO_RECACCEL); + desDist = RetLinStopLength(MO_RECSPEED, MO_STOACCEL); + + if ( dist > accDist+desDist ) + { + return dist-desDist; + } + + return dist*m_linMotion.stopAccel.x / + (m_linMotion.recedeAccel.x+m_linMotion.stopAccel.x); + } +} + + +// Gestion d'un événement. +// Retourne FALSE si l'objet est détruit. + +BOOL CPhysics::EventProcess(const Event &event) +{ + if ( !m_object->RetEnable() ) return TRUE; + + if ( m_brain != 0 ) + { + m_brain->EventProcess(event); + } + + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + return TRUE; +} + + +// Met à jour les consignes de vitesse du moteur. + +void CPhysics::MotorUpdate(float aTime, float rTime) +{ + ObjectType type; + CObject* power; + D3DVECTOR pos, motorSpeed; + float energy, speed, factor, h; + + type = m_object->RetType(); + + motorSpeed = m_motorSpeed; + + if ( type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM || + type == OBJECT_APOLLO2 || + type == OBJECT_MOBILEdr ) + { + power = 0; + } + else if ( type == OBJECT_HUMAN || + type == OBJECT_TECH ) + { + power = 0; + if ( m_object->RetFret() != 0 && // porte qq chose ? + !m_object->RetCargo() ) + { + motorSpeed.x *= 0.7f; // avance plus lentement + motorSpeed.z *= 0.5f; + motorSpeed.y = -1.0f; // tombe + } + if ( m_bSwim ) + { + if ( m_bLand ) // au fond de l'eau ? + { + motorSpeed.x *= 0.4f; // avance plus lentement + motorSpeed.z *= 0.5f; + motorSpeed.y *= 0.5f; + + if ( m_object->RetFret() != 0 ) // porte qq chose ? + { + motorSpeed.x *= 0.2f; + motorSpeed.z *= 0.9f; + motorSpeed.y *= 0.2f; + } + } + else // nage ? + { + motorSpeed.x *= 0.2f; // avance plus lentement + motorSpeed.z *= 0.5f; + motorSpeed.y *= 0.2f; + } + } + } + else + { + power = m_object->RetPower(); // cherche l'objet pile utilisé + if ( power == 0 || power->RetEnergy() == 0.0f ) // pas de pile ou plate ? + { + motorSpeed.x = 0.0f; + motorSpeed.z = 0.0f; + if ( m_bFreeze || m_bLand ) + { + motorSpeed.y = 0.0f; // immobile + } + else + { + motorSpeed.y = -1.0f; // tombe + } + SetMotor(FALSE); + } + } + + if ( m_object->RetDead() ) // homme mort ? + { + motorSpeed.x = 0.0f; + motorSpeed.z = 0.0f; + if ( m_motion->RetAction() == MHS_DEADw ) // mort noyé ? + { + motorSpeed.y = 0.0f; // c'est MHS_DEADw qui remonte + } + else + { + motorSpeed.y = -1.0f; // tombe + } + SetMotor(FALSE); + } + + if ( m_type == TYPE_FLYING && !m_bLand && motorSpeed.y > 0.0f ) + { + pos = m_object->RetPosition(0); + h = m_terrain->RetFlyingLimit(pos, type==OBJECT_BEE); + h += m_object->RetCharacter()->height; + if ( pos.y > h-40.0f ) // presque tout en haut ? + { + factor = 1.0f-(pos.y-(h-40.0f))/40.0f; + if ( factor < -1.0f ) factor = -1.0f; + if ( factor > 1.0f ) factor = 1.0f; + motorSpeed.y *= factor; // limite la vitesse de montée + } + } + + if ( type != OBJECT_BEE && + m_object->RetRange() > 0.0f ) // autonomie de vol limitée ? + { + if ( m_bLand || m_bSwim || m_bObstacle ) // au sol ou dans l'eau ? + { + factor = 1.0f; + if ( m_bObstacle ) factor = 3.0f; // pour pouvoir repartir ! + if ( m_bSwim ) factor = 3.0f; // refroidit plus vite dans l'eau + m_reactorRange += rTime*(1.0f/5.0f)*factor; + if ( m_reactorRange > 1.0f ) + { + m_reactorRange = 1.0f; + if ( m_bLowLevel && m_object->RetSelect() ) // bip froid ? + { + m_sound->Play(SOUND_INFO, m_object->RetPosition(0), 1.0f, 2.0f); + m_bLowLevel = FALSE; + } + } + m_bObstacle = FALSE; + } + else // en vol ? + { + m_reactorRange -= rTime*(1.0f/m_object->RetRange()); + if ( m_reactorRange < 0.0f ) m_reactorRange = 0.0f; + if ( m_reactorRange < 0.5f ) m_bLowLevel = TRUE; + } + + if ( m_reactorRange == 0.0f ) // réacteur tilté ? + { + motorSpeed.y = -1.0f; // tombe ... + } + } + +//? MotorParticule(aTime); + + // Avancer/reculer. + if ( motorSpeed.x > 0.0f ) + { + m_linMotion.motorAccel.x = m_linMotion.advanceAccel.x; + m_linMotion.motorSpeed.x = m_linMotion.advanceSpeed.x * motorSpeed.x; + } + if ( motorSpeed.x < 0.0f ) + { + m_linMotion.motorAccel.x = m_linMotion.recedeAccel.x; + m_linMotion.motorSpeed.x = m_linMotion.recedeSpeed.x * motorSpeed.x; + } + if ( motorSpeed.x == 0.0f ) + { + m_linMotion.motorAccel.x = m_linMotion.stopAccel.x; + m_linMotion.motorSpeed.x = 0.0f; + } + + // Monter/descendre. + if ( motorSpeed.y > 0.0f ) + { + m_linMotion.motorAccel.y = m_linMotion.advanceAccel.y; + m_linMotion.motorSpeed.y = m_linMotion.advanceSpeed.y * motorSpeed.y; + } + if ( motorSpeed.y < 0.0f ) + { + m_linMotion.motorAccel.y = m_linMotion.recedeAccel.y; + m_linMotion.motorSpeed.y = m_linMotion.recedeSpeed.y * motorSpeed.y; + } + if ( motorSpeed.y == 0.0f ) + { + m_linMotion.motorAccel.y = m_linMotion.stopAccel.y; + m_linMotion.motorSpeed.y = 0.0f; + } + + // Tourner à gauche/droite. + speed = motorSpeed.z; +//? if ( motorSpeed.x < 0.0f ) speed = -speed; // inverse tourne si recule + + if ( motorSpeed.z > 0.0f ) + { + m_cirMotion.motorAccel.y = m_cirMotion.advanceAccel.y; + m_cirMotion.motorSpeed.y = m_cirMotion.advanceSpeed.y * speed; + } + if ( motorSpeed.z < 0.0f ) + { + m_cirMotion.motorAccel.y = m_cirMotion.recedeAccel.y; + m_cirMotion.motorSpeed.y = m_cirMotion.recedeSpeed.y * speed; + } + if ( motorSpeed.z == 0.0f ) + { + m_cirMotion.motorAccel.y = m_cirMotion.stopAccel.y; + m_cirMotion.motorSpeed.y = 0.0f; + } + + if ( m_type == TYPE_FLYING && m_bLand ) // volant au sol ? + { + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH ) + { + factor = LANDING_ACCELh; + } + else + { + factor = LANDING_ACCEL; + } + m_linMotion.motorAccel.x = m_linMotion.stopAccel.x*factor; + m_cirMotion.motorAccel.y = m_cirMotion.stopAccel.y*factor; + + pos = m_object->RetPosition(0); + h = m_terrain->RetFlyingLimit(pos, type==OBJECT_BEE); + h += m_object->RetCharacter()->height; + if ( motorSpeed.y > 0.0f && m_reactorRange > 0.1f && pos.y < h ) + { + m_bLand = FALSE; // décolle + SetMotor(TRUE); + pos.y += 0.05f; // petite hauteur initiale (startup) + m_object->SetPosition(0, pos); + } + } + + if ( m_type == TYPE_ROLLING ) + { + if ( motorSpeed.x == 0.0f && + motorSpeed.z == 0.0f ) + { + SetMotor(FALSE); + } + else + { + SetMotor(TRUE); + } + } + + if ( power != 0 ) // pile transportée ? + { + factor = 1.0f; + if ( type == OBJECT_MOBILEia || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEii ) factor = 0.5f; + + factor /= power->RetCapacity(); + + energy = power->RetEnergy(); + energy -= Abs(motorSpeed.x)*rTime*factor*0.005f; + energy -= Abs(motorSpeed.z)*rTime*factor*0.005f; + + if ( m_type == TYPE_FLYING && motorSpeed.y > 0.0f ) + { + energy -= motorSpeed.y*rTime*factor*0.01f; + } + if ( energy < 0.0f ) energy = 0.0f; + power->SetEnergy(energy); + } +} + + +// Met à jour les effets de vibration et d'inclinaison. + +void CPhysics::EffectUpdate(float aTime, float rTime) +{ + Character* character; + D3DVECTOR vibLin, vibCir, incl; + float speedLin, speedCir, accel; + ObjectType type; + BOOL bOnBoard; + + if ( !m_engine->IsVisiblePoint(m_object->RetPosition(0)) ) return; + + type = m_object->RetType(); + character = m_object->RetCharacter(); + + bOnBoard = FALSE; + if ( m_object->RetSelect() && + m_camera->RetType() == CAMERA_ONBOARD ) + { + bOnBoard = TRUE; + } + + vibLin = m_motion->RetLinVibration(); + vibCir = m_motion->RetCirVibration(); + incl = m_motion->RetInclinaison(); + + if ( type == OBJECT_HUMAN || // homme ? + type == OBJECT_TECH ) + { + if ( !m_bLand && !m_bSwim ) // en vol ? + { + vibLin.y = sinf(aTime*2.00f)*0.5f+ + sinf(aTime*2.11f)*0.3f; + + vibCir.z = sinf(aTime*PI* 2.01f)*(PI/150.0f)+ + sinf(aTime*PI* 2.51f)*(PI/200.0f)+ + sinf(aTime*PI*19.01f)*(PI/400.0f); + + vibCir.x = sinf(aTime*PI* 2.03f)*(PI/150.0f)+ + sinf(aTime*PI* 2.52f)*(PI/200.0f)+ + sinf(aTime*PI*19.53f)*(PI/400.0f); + + speedLin = m_linMotion.realSpeed.x / m_linMotion.advanceSpeed.x; + speedCir = m_cirMotion.realSpeed.y / m_cirMotion.advanceSpeed.y; + incl.x = -speedLin*speedCir*0.5f; // penche si virage + +//? speedLin = m_linMotion.currentSpeed.x - m_linMotion.motorSpeed.x*1.5f; + speedLin = m_linMotion.currentSpeed.x - m_linMotion.motorSpeed.x*1.2f; + speedLin /= m_linMotion.advanceSpeed.x; + m_linMotion.finalInclin.z = speedLin*1.4f; + if ( incl.z < m_linMotion.finalInclin.z ) + { + incl.z += rTime*0.4f; + if ( incl.z > m_linMotion.finalInclin.z ) + { + incl.z = m_linMotion.finalInclin.z; + } + } + else if ( incl.z > m_linMotion.finalInclin.z ) + { + incl.z -= rTime*0.4f; + if ( incl.z < m_linMotion.finalInclin.z ) + { + incl.z = m_linMotion.finalInclin.z; + } + } + + vibLin *= m_linVibrationFactor; + vibCir *= m_cirVibrationFactor; + incl *= m_inclinaisonFactor; + + m_motion->SetLinVibration(vibLin); + m_motion->SetCirVibration(vibCir); + m_motion->SetInclinaison(incl); + } + else if ( m_bSwim ) // nage ? + { + vibLin.y = sinf(aTime*2.00f)*0.5f+ + sinf(aTime*2.11f)*0.3f; + + vibCir.z = sinf(aTime*PI* 2.01f)*(PI/150.0f)+ + sinf(aTime*PI* 2.51f)*(PI/200.0f)+ +//? sinf(aTime*PI*19.01f)*(PI/400.0f)-PI/2.0f; + sinf(aTime*PI*19.01f)*(PI/400.0f); + + vibCir.x = sinf(aTime*PI* 2.03f)*(PI/150.0f)+ + sinf(aTime*PI* 2.52f)*(PI/200.0f)+ + sinf(aTime*PI*19.53f)*(PI/400.0f); + + speedLin = m_linMotion.realSpeed.x / m_linMotion.advanceSpeed.x; + speedCir = m_cirMotion.realSpeed.y / m_cirMotion.advanceSpeed.y; + incl.x = -speedLin*speedCir*5.0f; // penche si virage + +//? speedLin = m_linMotion.currentSpeed.x - m_linMotion.motorSpeed.x*1.5f; + speedLin = m_linMotion.currentSpeed.x - m_linMotion.motorSpeed.x*1.2f; + speedLin /= m_linMotion.advanceSpeed.x; + m_linMotion.finalInclin.z = speedLin*1.4f; + if ( incl.z < m_linMotion.finalInclin.z ) + { + incl.z += rTime*0.4f; + if ( incl.z > m_linMotion.finalInclin.z ) + { + incl.z = m_linMotion.finalInclin.z; + } + } + else if ( incl.z > m_linMotion.finalInclin.z ) + { + incl.z -= rTime*0.4f; + if ( incl.z < m_linMotion.finalInclin.z ) + { + incl.z = m_linMotion.finalInclin.z; + } + } + + if ( m_linMotion.realSpeed.y > 0.0f ) // monte ? + { + vibCir.z += m_linMotion.realSpeed.y*0.05f; + } + else // descend ? + { + vibCir.z += m_linMotion.realSpeed.y*0.12f; + } + vibCir.z -= PI*0.4f; + + vibLin *= m_linVibrationFactor; + vibCir *= m_cirVibrationFactor; + incl *= m_inclinaisonFactor; + + m_motion->SetLinVibration(vibLin); + m_motion->SetCirVibration(vibCir); + m_motion->SetInclinaison(incl); + } + else + { + m_motion->SetLinVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); +//? m_motion->SetCirVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); +//? m_motion->SetInclinaison(D3DVECTOR(0.0f, 0.0f, 0.0f)); + } + } + + if ( type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEtg || + type == OBJECT_APOLLO2 ) // roues ? + { + speedLin = m_linMotion.realSpeed.x / m_linMotion.advanceSpeed.x; + speedCir = m_cirMotion.realSpeed.y / m_cirMotion.advanceSpeed.y; + incl.x = speedLin*speedCir*0.20f; // penche si virage + if ( type == OBJECT_APOLLO2 ) incl.x *= 0.25f; + + speedLin = m_linMotion.currentSpeed.x - m_linMotion.motorSpeed.x; + speedLin /= m_linMotion.advanceSpeed.x; + if ( speedLin > 1.0f ) speedLin = 1.0f; + m_linMotion.finalInclin.z = -speedLin*0.30f; + accel = (0.40f-Abs(incl.z))*4.0f; + if ( incl.z < m_linMotion.finalInclin.z ) + { + incl.z += rTime*accel; + if ( incl.z > m_linMotion.finalInclin.z ) + { + incl.z = m_linMotion.finalInclin.z; + } + } + else if ( incl.z > m_linMotion.finalInclin.z ) + { + incl.z -= rTime*accel; + if ( incl.z < m_linMotion.finalInclin.z ) + { + incl.z = m_linMotion.finalInclin.z; + } + } + if ( bOnBoard ) incl.z *= 0.1f; + if ( type == OBJECT_APOLLO2 ) incl.z *= 0.25f; + m_object->SetInclinaison(incl); + + vibLin.x = 0.0f; + vibLin.z = 0.0f; + vibLin.y = Abs(character->wheelFront*sinf(incl.z))*0.8f + + Abs(character->wheelRight*sinf(incl.x))*0.5f; + m_motion->SetLinVibration(vibLin); + } + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEft ) // volant ? + { + if ( m_bLand ) // au sol ? + { + m_motion->SetLinVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_motion->SetCirVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_motion->SetInclinaison(D3DVECTOR(0.0f, 0.0f, 0.0f)); + } + else // en vol ? + { + vibLin.y = sinf(aTime*2.00f)*0.5f+ + sinf(aTime*2.11f)*0.3f; + + vibCir.z = sinf(aTime*PI* 2.01f)*(PI/150.0f)+ + sinf(aTime*PI* 2.51f)*(PI/200.0f)+ + sinf(aTime*PI*19.01f)*(PI/400.0f); + + vibCir.x = sinf(aTime*PI* 2.03f)*(PI/150.0f)+ + sinf(aTime*PI* 2.52f)*(PI/200.0f)+ + sinf(aTime*PI*19.53f)*(PI/400.0f); + + if ( bOnBoard ) vibCir *= 0.4f; + + speedLin = m_linMotion.realSpeed.x / m_linMotion.advanceSpeed.x; + speedCir = m_cirMotion.realSpeed.y / m_cirMotion.advanceSpeed.y; + incl.x = -speedLin*speedCir*0.5f; // penche si virage + +//? speedLin = m_linMotion.currentSpeed.x - m_linMotion.motorSpeed.x*1.5f; + speedLin = m_linMotion.currentSpeed.x - m_linMotion.motorSpeed.x*1.2f; + speedLin /= m_linMotion.advanceSpeed.x; + m_linMotion.finalInclin.z = speedLin*0.8f; + if ( incl.z < m_linMotion.finalInclin.z ) + { + incl.z += rTime*0.4f; + if ( incl.z > m_linMotion.finalInclin.z ) + { + incl.z = m_linMotion.finalInclin.z; + } + } + else if ( incl.z > m_linMotion.finalInclin.z ) + { + incl.z -= rTime*0.4f; + if ( incl.z < m_linMotion.finalInclin.z ) + { + incl.z = m_linMotion.finalInclin.z; + } + } +//? if ( bOnBoard ) incl.z *= 0.5f; + + vibLin *= m_linVibrationFactor; + vibCir *= m_cirVibrationFactor; + incl *= m_inclinaisonFactor; + + m_motion->SetLinVibration(vibLin); + m_motion->SetCirVibration(vibCir); + m_motion->SetInclinaison(incl); + } + } + + if ( type == OBJECT_BEE ) // abeille ? + { + if ( !m_bLand ) // en vol ? + { + vibLin.y = sinf(aTime*2.00f)*0.5f+ + sinf(aTime*2.11f)*0.3f; + + vibCir.z = (Rand()-0.5f)*0.1f+ + sinf(aTime*PI* 2.01f)*(PI/150.0f)+ + sinf(aTime*PI* 2.51f)*(PI/200.0f)+ + sinf(aTime*PI*19.01f)*(PI/400.0f); + + vibCir.x = (Rand()-0.5f)*0.1f+ + sinf(aTime*PI* 2.03f)*(PI/150.0f)+ + sinf(aTime*PI* 2.52f)*(PI/200.0f)+ + sinf(aTime*PI*19.53f)*(PI/400.0f); + + speedLin = m_linMotion.realSpeed.x / m_linMotion.advanceSpeed.x; + speedCir = m_cirMotion.realSpeed.y / m_cirMotion.advanceSpeed.y; + incl.x = -speedLin*speedCir*1.5f; // penche si virage + +//? speedLin = m_linMotion.currentSpeed.x - m_linMotion.motorSpeed.x*1.5f; + speedLin = m_linMotion.currentSpeed.x - m_linMotion.motorSpeed.x*1.2f; + speedLin /= m_linMotion.advanceSpeed.x; + m_linMotion.finalInclin.z = speedLin*1.4f; + if ( incl.z < m_linMotion.finalInclin.z ) + { + incl.z += rTime*1.6f; + if ( incl.z > m_linMotion.finalInclin.z ) + { + incl.z = m_linMotion.finalInclin.z; + } + } + else if ( incl.z > m_linMotion.finalInclin.z ) + { + incl.z -= rTime*1.6f; + if ( incl.z < m_linMotion.finalInclin.z ) + { + incl.z = m_linMotion.finalInclin.z; + } + } + + vibLin *= m_linVibrationFactor; + vibCir *= m_cirVibrationFactor; + incl *= m_inclinaisonFactor; + + m_motion->SetLinVibration(vibLin); + m_motion->SetCirVibration(vibCir); + m_motion->SetInclinaison(incl); + } + } +} + + +// Met à jour une structure Motion. + +void CPhysics::UpdateMotionStruct(float rTime, Motion &motion) +{ + float speed, motor; + + // Gestion de la coordonnée x. + speed = motion.currentSpeed.x; + motor = motion.motorSpeed.x * m_inclinaisonFactor; + if ( speed < motor ) + { + speed += rTime*motion.motorAccel.x; // accélère + if ( speed > motor ) + { + speed = motor; // ne dépasse pas la vitesse + } + } + if ( speed > motor ) + { + speed -= rTime*motion.motorAccel.x; // déccélère + if ( speed < motor ) + { + speed = motor; // ne dépasse pas la vitesse + } + } + motion.currentSpeed.x = speed; + motion.realSpeed.x = speed; + + if ( Abs(motion.terrainSpeed.x) > motion.terrainSlide.x ) + { + if ( motion.terrainSpeed.x > 0 ) + { + speed = motion.terrainSpeed.x - motion.terrainSlide.x; + } + else + { + speed = motion.terrainSpeed.x + motion.terrainSlide.x; + } + motion.realSpeed.x += speed; + } + + // Gestion de la coordonnée y. + speed = motion.currentSpeed.y; + motor = motion.motorSpeed.y; // vitesse non limitée ! + if ( speed < motor ) + { + speed += rTime*motion.motorAccel.y; // accélère + if ( speed > motor ) + { + speed = motor; // ne dépasse pas la vitesse + } + } + if ( speed > motor ) + { + speed -= rTime*motion.motorAccel.y; // déccélère + if ( speed < motor ) + { + speed = motor; // ne dépasse pas la vitesse + } + } + motion.currentSpeed.y = speed; + motion.realSpeed.y = speed; + + if ( Abs(motion.terrainSpeed.y) > motion.terrainSlide.y ) + { + if ( motion.terrainSpeed.y > 0 ) + { + speed = motion.terrainSpeed.y - motion.terrainSlide.y; + } + else + { + speed = motion.terrainSpeed.y + motion.terrainSlide.y; + } + motion.realSpeed.y += speed; + } + + // Gestion de la coordonnée z. + speed = motion.currentSpeed.z; + motor = motion.motorSpeed.z * m_inclinaisonFactor; + if ( speed < motor ) + { + speed += rTime*motion.motorAccel.z; // accélère + if ( speed > motor ) + { + speed = motor; // ne dépasse pas la vitesse + } + } + if ( speed > motor ) + { + speed -= rTime*motion.motorAccel.z; // déccélère + if ( speed < motor ) + { + speed = motor; // ne dépasse pas la vitesse + } + } + motion.currentSpeed.z = speed; + motion.realSpeed.z = speed; + + if ( Abs(motion.terrainSpeed.z) > motion.terrainSlide.z ) + { + if ( motion.terrainSpeed.z > 0 ) + { + speed = motion.terrainSpeed.z - motion.terrainSlide.z; + } + else + { + speed = motion.terrainSpeed.z + motion.terrainSlide.z; + } + motion.realSpeed.z += speed; + } +} + + +// Fait évoluer la physique selon le temps écoulé. +// Retourne FALSE si l'objet est détruit. +// +// a: accélération +// v1: vitesse au temps t1 +// v2: vitesse au temps t2 +// dt: temps écoulé depuis t1, donc: dt=t2-t1 +// dd: différence de distance (avance) +// +// v2 = v1 + a*dt +// dd = v2*dt + +BOOL CPhysics::EventFrame(const Event &event) +{ + ObjectType type; + D3DMATRIX objRotate, matRotate; + D3DVECTOR iPos, iAngle, tAngle, pos, newpos, angle, newangle, n; + float h, w; + int i; + + if ( m_engine->RetPause() ) return TRUE; + + m_time += event.rTime; + m_timeUnderWater += event.rTime; + m_soundTimeJostle += event.rTime; + + type = m_object->RetType(); + + FrameParticule(m_time, event.rTime); + MotorUpdate(m_time, event.rTime); + EffectUpdate(m_time, event.rTime); + WaterFrame(m_time, event.rTime); + + iPos = pos = m_object->RetPosition(0); + iAngle = angle = m_object->RetAngle(0); + + // Accélère à la descente, freine à la montée. + if ( m_bFreeze || m_object->RetDead() ) + { + m_linMotion.terrainSpeed.x = 0.0f; + m_linMotion.terrainSpeed.z = 0.0f; + m_linMotion.terrainSpeed.y = 0.0f; + } + else + { + tAngle = angle; + h = m_terrain->RetBuildingFactor(pos); + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH ) + { + if ( m_linMotion.currentSpeed.x == 0.0f ) + { + h *= 0.5f; // homme immobile -> glisse moins + } + FloorAngle(pos, tAngle); // calcule l'angle avec le terrain + } +#if 1 + if ( pos.y < m_water->RetLevel(m_object) ) // sous l'eau ? + { + h *= 0.5f; + } +#endif +//? m_linMotion.terrainSpeed.x = -tAngle.z*m_linMotion.terrainForce.x*h; +//? m_linMotion.terrainSpeed.z = tAngle.x*m_linMotion.terrainForce.z*h; +//? m_linMotion.terrainSpeed.x = -sinf(tAngle.z)*PI*0.5f*m_linMotion.terrainForce.x*h; +//? m_linMotion.terrainSpeed.z = sinf(tAngle.x)*PI*0.5f*m_linMotion.terrainForce.z*h; + m_linMotion.terrainSpeed.x = -tanf(tAngle.z)*0.9f*m_linMotion.terrainForce.x*h; + m_linMotion.terrainSpeed.z = tanf(tAngle.x)*0.9f*m_linMotion.terrainForce.z*h; + m_linMotion.terrainSpeed.y = 0.0f; + + // Si le terrain est très pentu, n'exagère pas ! + if ( m_linMotion.terrainSpeed.x > 50.0f ) m_linMotion.terrainSpeed.x = 20.0f; + if ( m_linMotion.terrainSpeed.x < -50.0f ) m_linMotion.terrainSpeed.x = -20.0f; + if ( m_linMotion.terrainSpeed.z > 50.0f ) m_linMotion.terrainSpeed.z = 20.0f; + if ( m_linMotion.terrainSpeed.z < -50.0f ) m_linMotion.terrainSpeed.z = -20.0f; + } + + if ( type == OBJECT_BEE && !m_bLand ) + { + h = m_floorLevel; // niveau du sol + w = m_water->RetLevel(m_object); + if ( h < w ) h = w; + h = pos.y-h-10.0f; // hauteur maximale (*) + if ( h < 0.0f ) h = 0.0f; + m_linMotion.terrainSpeed.y = -h*2.5f; // ne va pas plus haut + } + + // (*) Assez haut pour passer par-dessus la tour de défence + // (OBJECT_TOWER), mais pas trop pour passer sous la coiffe + // du vaisseau (OBJECT_BASE) ! + + UpdateMotionStruct(event.rTime, m_linMotion); + UpdateMotionStruct(event.rTime, m_cirMotion); + + newangle = angle + event.rTime*m_cirMotion.realSpeed; + MatRotateZXY(matRotate, newangle); + newpos = event.rTime*m_linMotion.realSpeed; + newpos = Transform(matRotate, newpos); + newpos += pos; + + m_terrain->LimitPos(newpos); + + if ( m_type == TYPE_FLYING && !m_bLand ) + { + h = m_terrain->RetFlyingLimit(newpos, type==OBJECT_BEE); + h += m_object->RetCharacter()->height; + if ( newpos.y > h ) newpos.y = h; + } + + if ( m_bForceUpdate || + newpos.x != pos.x || + newpos.y != pos.y || + newpos.z != pos.z || + newangle.x != angle.x || + newangle.y != angle.y || + newangle.z != angle.z ) + { + FloorAdapt(m_time, event.rTime, newpos, newangle); + } + + if ( m_bForceUpdate || + newpos.x != pos.x || + newpos.y != pos.y || + newpos.z != pos.z ) + { + i = ObjectAdapt(newpos, newangle); + if ( i == 2 ) // objet détruit ? + { + return FALSE; + } + if ( i == 1 ) // objet immobile ? + { + newpos = iPos; // garde la position initiale, mais accepte la rotation + } + } + + if ( newangle.x != angle.x || + newangle.y != angle.y || + newangle.z != angle.z ) + { + m_object->SetAngle(0, newangle); + } + + if ( newpos.x != pos.x || + newpos.y != pos.y || + newpos.z != pos.z ) + { + m_object->SetPosition(0, newpos); + } + + MotorParticule(m_time, event.rTime); + SoundMotor(event.rTime); + + m_bForceUpdate = FALSE; + + return TRUE; +} + +// Démarre ou stoppe les bruits de moteur. + +void CPhysics::SoundMotor(float rTime) +{ + CObject* power; + ObjectType type; + float energy; + + m_lastSoundInsect -= rTime; + type = m_object->RetType(); + + if ( type == OBJECT_MOTHER ) + { + if ( m_lastSoundInsect <= 0.0f && m_object->RetActif() ) + { + m_sound->Play(SOUND_INSECTm, m_object->RetPosition(0)); + if ( m_bMotor ) m_lastSoundInsect = 0.4f+Rand()*2.5f; + else m_lastSoundInsect = 1.5f+Rand()*4.0f; + } + } + else if ( type == OBJECT_ANT ) + { + if ( m_object->RetBurn() || + m_object->RetFixed() ) + { + if ( m_lastSoundInsect <= 0.0f ) + { + m_sound->Play(SOUND_INSECTa, m_object->RetPosition(0), 1.0f, 1.5f+Rand()*0.5f); + m_lastSoundInsect = 0.4f+Rand()*0.6f; + } + } + else if ( m_object->RetActif() ) + { + if ( m_lastSoundInsect <= 0.0f ) + { + m_sound->Play(SOUND_INSECTa, m_object->RetPosition(0)); + if ( m_bMotor ) m_lastSoundInsect = 0.4f+Rand()*2.5f; + else m_lastSoundInsect = 1.5f+Rand()*4.0f; + } + } + } + else if ( type == OBJECT_BEE ) + { + if ( m_object->RetActif() ) + { + if ( m_lastSoundInsect <= 0.0f ) + { + m_sound->Play(SOUND_INSECTb, m_object->RetPosition(0)); + if ( m_bMotor ) m_lastSoundInsect = 0.4f+Rand()*2.5f; + else m_lastSoundInsect = 1.5f+Rand()*4.0f; + } + } + else if ( m_object->RetBurn() ) + { + if ( m_lastSoundInsect <= 0.0f ) + { + m_sound->Play(SOUND_INSECTb, m_object->RetPosition(0), 1.0f, 1.5f+Rand()*0.5f); + m_lastSoundInsect = 0.3f+Rand()*0.5f; + } + } + } + else if ( type == OBJECT_WORM ) + { + if ( m_object->RetActif() ) + { + if ( m_lastSoundInsect <= 0.0f ) + { + m_sound->Play(SOUND_INSECTw, m_object->RetPosition(0)); + if ( m_bMotor ) m_lastSoundInsect = 0.4f+Rand()*2.5f; + else m_lastSoundInsect = 1.5f+Rand()*4.0f; + } + } + else if ( m_object->RetBurn() ) + { + if ( m_lastSoundInsect <= 0.0f ) + { + m_sound->Play(SOUND_INSECTw, m_object->RetPosition(0), 1.0f, 1.5f+Rand()*0.5f); + m_lastSoundInsect = 0.2f+Rand()*0.2f; + } + } + } + else if ( type == OBJECT_SPIDER ) + { + if ( m_object->RetBurn() || + m_object->RetFixed() ) + { + if ( m_lastSoundInsect <= 0.0f ) + { + m_sound->Play(SOUND_INSECTs, m_object->RetPosition(0), 1.0f, 1.5f+Rand()*0.5f); + m_lastSoundInsect = 0.4f+Rand()*0.6f; + } + } + else if ( m_object->RetActif() ) + { + if ( m_lastSoundInsect <= 0.0f ) + { + m_sound->Play(SOUND_INSECTs, m_object->RetPosition(0)); + if ( m_bMotor ) m_lastSoundInsect = 0.4f+Rand()*2.5f; + else m_lastSoundInsect = 1.5f+Rand()*4.0f; + } + } + } + else // véhicule ? + { + if ( m_type == TYPE_ROLLING ) + { + if ( m_bMotor && m_object->RetActif() ) + { + SoundMotorFull(rTime, type); // plein régime + } + else + { + energy = 0.0f; + power = m_object->RetPower(); + if ( power != 0 ) + { + energy = power->RetEnergy(); + } + + if ( m_object->RetSelect() && + energy != 0.0f ) + { + SoundMotorSlow(rTime, type); // au ralenti + } + else + { + SoundMotorStop(rTime, type); // à l'arrêt + } + } + } + + if ( m_type == TYPE_FLYING ) + { + if ( m_bMotor && !m_bSwim && + m_object->RetActif() && !m_object->RetDead() ) + { + SoundReactorFull(rTime, type); // plein régime + } + else + { + SoundReactorStop(rTime, type); // à l'arrêt + } + } + } +} + +// Fait exploser l'objet s'il est sous l'eau. + +void CPhysics::WaterFrame(float aTime, float rTime) +{ + ObjectType type; + D3DVECTOR pos, speed; + FPOINT dim; + float level; + + level = m_water->RetLevel(); + if ( level == 0.0f ) return; // pas d'eau ? + if ( m_object->RetTruck() != 0 ) return; // objet transporté ? + + // Gestion des flammes dans la lave. + pos = m_object->RetPosition(0); + if ( m_water->RetLava() && + pos.y-m_object->RetCharacter()->height <= level ) + { + if ( m_lastFlameParticule+m_engine->ParticuleAdapt(0.05f) <= aTime ) + { + m_lastFlameParticule = aTime; + + pos = m_object->RetPosition(0); + pos.x += (Rand()-0.5f)*3.0f; + pos.z += (Rand()-0.5f)*3.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = Rand()*5.0f+3.0f; + dim.x = Rand()*2.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFLAME, 2.0f, 0.0f, 0.2f); + + pos = m_object->RetPosition(0); + pos.y -= 2.0f; + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 6.0f+Rand()*6.0f+6.0f; + dim.x = Rand()*1.5f+1.0f+3.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE3, 4.0f); + } + } + + pos = m_object->RetPosition(0); + if ( pos.y >= m_water->RetLevel(m_object) ) return; // hors de l'eau ? + + type = m_object->RetType(); + if ( type == OBJECT_TOTO ) return; + if ( type == OBJECT_NULL ) return; + + if ( !m_object->RetActif() ) return; + if ( m_object->RetResetBusy() ) return; // reset en cours ? + + if ( m_water->RetLava() || + (type == OBJECT_HUMAN && + m_object->RetOption() != 0 ) || // homme sans casque ? + type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr || + type == OBJECT_APOLLO2 ) // véhicule non sous-marin ? + { + m_object->ExploObject(EXPLO_WATER, 1.0f); // démarre explosion + } +} + +// Fait entendre le moteur à plein régime. + +void CPhysics::SoundMotorFull(float rTime, ObjectType type) +{ + Sound sound; + float amplitude, time, freq; + + if ( type == OBJECT_MOBILEia || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEis ) + { + if ( m_soundChannel == -1 ) + { + m_soundChannel = m_sound->Play(SOUND_MOTORi, m_object->RetPosition(0), 0.0f, 1.0f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, 0.2f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, 1.0f, SOPER_LOOP); + } + else + { + m_sound->Position(m_soundChannel, m_object->RetPosition(0)); + } + + freq = 1.0f+m_linMotion.terrainSpeed.x/50.0f; + if ( m_linMotion.realSpeed.x == 0.0f ) + { + freq -= Abs(m_cirMotion.realSpeed.y/3.0f); + } + else + { + freq -= Abs(m_cirMotion.realSpeed.y/4.0f); + } + m_sound->Frequency(m_soundChannel, freq); + + return; + } + + if ( type == OBJECT_MOBILEsa ) + { + sound = SOUND_MOTORs; + amplitude = 0.6f; + time = 0.5f; + } + else if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) + { + sound = SOUND_MOTORr; + amplitude = 1.0f; + time = 0.7f; + } + else if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts ) + { + sound = SOUND_MOTORt; + amplitude = 1.0f; + time = 0.5f; + } + else if ( type == OBJECT_APOLLO2 ) + { + sound = SOUND_MANIP; + amplitude = 1.0f; + time = 0.5f; + } + else + { + sound = SOUND_MOTORw; + amplitude = 0.7f; + time = 0.3f; + } + + if ( m_object->RetToy() ) + { + sound = SOUND_MOTORd; + amplitude = 1.0f; + time = 0.1f; + } + + freq = 0.75f+(Abs(m_motorSpeed.x)+Abs(m_motorSpeed.z))*0.25f; + if ( freq > 1.0f ) freq = 1.0f; + if ( m_object->RetToy() ) freq = 1.0f; + + if ( m_soundChannel == -1 ) + { + m_soundChannel = m_sound->Play(sound, m_object->RetPosition(0), 0.0f, 0.5f, TRUE); + m_sound->AddEnvelope(m_soundChannel, amplitude, freq, time, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, amplitude, freq, 1.0f, SOPER_LOOP); + } + else + { + m_sound->Position(m_soundChannel, m_object->RetPosition(0)); + + if ( m_bSoundSlow ) // au ralenti ? + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, amplitude, freq, time, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, amplitude, freq, 1.0f, SOPER_LOOP); + m_bSoundSlow = FALSE; + } + } + + freq *= 1.0f + m_linMotion.terrainSpeed.x/100.0f; + freq *= 1.0f + Abs(m_cirMotion.realSpeed.y/20.0f); + m_sound->Frequency(m_soundChannel, freq); + + m_soundTimePshhh -= rTime*2.0f; +} + +// Fait entendre le moteur au ralenti. + +void CPhysics::SoundMotorSlow(float rTime, ObjectType type) +{ + D3DMATRIX* mat; + D3DVECTOR pos, speed; + FPOINT dim; + Sound sound; + float amplitude; + int i, max; + + if ( type == OBJECT_MOBILEia || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEis ) + { + if ( m_soundChannel != -1 ) // moteur tourne ? + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 0.3f, SOPER_STOP); + m_soundChannel = -1; + } + return; + } + + if ( type == OBJECT_MOBILEsa ) + { + sound = SOUND_MOTORs; + amplitude = 0.4f; + } + else if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) + { + sound = SOUND_MOTORr; + amplitude = 0.9f; + } + else if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts ) + { + sound = SOUND_MOTORt; + amplitude = 0.7f; + } + else if ( type == OBJECT_APOLLO2 ) + { + if ( m_soundChannel != -1 ) // moteur tourne ? + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.5f, 0.3f, SOPER_STOP); + m_soundChannel = -1; + } + return; + } + else + { + sound = SOUND_MOTORw; + amplitude = 0.3f; + } + + if ( m_object->RetToy() ) + { + sound = SOUND_MOTORd; + amplitude = 0.0f; + } + + if ( m_soundChannel == -1 ) + { + m_soundChannel = m_sound->Play(sound, m_object->RetPosition(0), 0.0f, 0.25f, TRUE); + m_sound->AddEnvelope(m_soundChannel, amplitude, 0.5f, 0.2f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, amplitude, 0.5f, 1.0f, SOPER_LOOP); + } + else + { + m_sound->Position(m_soundChannel, m_object->RetPosition(0)); + + if ( !m_bSoundSlow ) // plein régime ? + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, amplitude, 0.5f, 0.3f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, amplitude, 0.5f, 1.0f, SOPER_LOOP); + m_bSoundSlow = TRUE; + } + } + + if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) + { + m_soundTimePshhh -= rTime; + + if ( m_soundTimePshhh <= 0.0f ) + { + amplitude = 0.5f-m_soundTimePshhh*0.08f; + if ( amplitude > 1.0f ) amplitude = 1.0f; +//? m_sound->Play(SOUND_PSHHH, m_object->RetPosition(0), amplitude); + m_sound->Play(SOUND_PSHHH, m_object->RetPosition(0), 1.0f); + + m_soundTimePshhh = 4.0f+4.0f*Rand(); + + max = (int)(10.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iRetWorldMatrix(0); + pos = Transform(*mat, pos); + speed = Transform(*mat, speed)-pos; + + dim.x = Rand()*1.0f+1.0f; + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, PARTIMOTOR, 2.0f); + } + } + } +} + +// Fait entendre le moteur à l'arrêt. + +void CPhysics::SoundMotorStop(float rTime, ObjectType type) +{ + if ( type == OBJECT_MOBILEia || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEis ) + { + if ( m_soundChannel != -1 ) // moteur tourne ? + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 0.3f, SOPER_STOP); + m_soundChannel = -1; + } + return; + } + + if ( m_soundChannel != -1 ) // moteur tourne ? + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.5f, 0.3f, SOPER_STOP); + m_soundChannel = -1; + } + + m_soundTimePshhh -= rTime*2.0f; +} + +// Fait entendre le réacteur à plein régime. + +void CPhysics::SoundReactorFull(float rTime, ObjectType type) +{ + Sound sound; + D3DMATRIX* mat; + D3DVECTOR pos, speed; + FPOINT dim; + float freq; + int i; + + if ( m_soundChannelSlide != -1 ) // glisse ? + { + m_sound->FlushEnvelope(m_soundChannelSlide); + m_sound->AddEnvelope(m_soundChannelSlide, 0.0f, 1.0f, 0.3f, SOPER_STOP); + m_soundChannelSlide = -1; + } + + if ( m_reactorRange > 0.0f ) + { + if ( m_soundChannel == -1 ) + { + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH ) + { + sound = SOUND_FLYh; + } + else + { + sound = SOUND_FLY; + } + + m_soundChannel = m_sound->Play(sound, m_object->RetPosition(0), 0.0f, 1.0f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, 0.6f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, 1.0f, SOPER_LOOP); + } + else + { + m_sound->Position(m_soundChannel, m_object->RetPosition(0)); + } + + freq = 1.0f + m_linMotion.realSpeed.y/100.0f; + freq *= 1.0f + Abs(m_cirMotion.realSpeed.y/5.0f); + m_sound->Frequency(m_soundChannel, freq); + } + else + { + if ( m_soundChannel != -1 ) // moteur tourne ? + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + if ( m_timeReactorFail <= m_time ) + { + freq = 1.0f+Rand()*0.5f; + m_sound->Play(SOUND_FLYf, m_object->RetPosition(0), 1.0f, freq); + m_camera->StartEffect(CE_PET, m_object->RetPosition(0), 1.0f); + + for ( i=0 ; i<5 ; i++ ) + { + if ( m_object->RetType() == OBJECT_HUMAN || + m_object->RetType() == OBJECT_TECH ) + { + pos = D3DVECTOR(-1.6f, -0.5f, 0.0f); + } + else + { + pos = D3DVECTOR(0.0f, -1.0f, 0.0f); + } + pos.x += (Rand()-0.5f)*2.0f; + pos.z += (Rand()-0.5f)*2.0f; + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + speed.x = (Rand()-0.5f)*5.0f; + speed.z = (Rand()-0.5f)*5.0f; + speed.y = -(4.0f+Rand()*4.0f); + dim.x = (2.0f+Rand()*1.0f); + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE1, 2.0f, 0.0f, 0.1f); + } + + m_timeReactorFail = m_time+0.10f+Rand()*0.30f; + } + else + { + if ( m_object->RetType() == OBJECT_HUMAN || + m_object->RetType() == OBJECT_TECH ) + { + pos = D3DVECTOR(-1.6f, -0.5f, 0.0f); + } + else + { + pos = D3DVECTOR(0.0f, -1.0f, 0.0f); + } + pos.x += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = -(4.0f+Rand()*4.0f); + dim.x = (0.7f+Rand()*0.4f); + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE1, 2.0f, 0.0f, 0.1f); + } + } + +} + +// Fait entendre le réacteur à l'arrêt. + +void CPhysics::SoundReactorStop(float rTime, ObjectType type) +{ + CObject* power; + float energy; + + energy = 0.0f; + power = m_object->RetPower(); + if ( power != 0 ) + { + energy = power->RetEnergy(); + } + + if ( m_soundChannel != -1 ) // moteur tourne ? + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH ) + { + if ( m_soundChannelSlide != -1 ) // glisse ? + { + m_sound->FlushEnvelope(m_soundChannelSlide); + m_sound->AddEnvelope(m_soundChannelSlide, 0.0f, 1.0f, 0.3f, SOPER_STOP); + m_soundChannelSlide = -1; + } + } + else + { + if ( energy != 0.0f && + (m_motorSpeed.x != 0.0f || // glisse avec petits réacteurs dans patins ? + m_cirMotion.realSpeed.y != 0.0f) ) + { + if ( m_soundChannelSlide == -1 ) + { + m_soundChannelSlide = m_sound->Play(SOUND_SLIDE, m_object->RetPosition(0), 0.0f, 1.0f, TRUE); + m_sound->AddEnvelope(m_soundChannelSlide, 0.5f, 1.0f, 0.3f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannelSlide, 0.5f, 1.0f, 1.0f, SOPER_LOOP); + } + m_sound->Position(m_soundChannelSlide, m_object->RetPosition(0)); + } + else + { + if ( m_soundChannelSlide != -1 ) // glisse ? + { + m_sound->FlushEnvelope(m_soundChannelSlide); + m_sound->AddEnvelope(m_soundChannelSlide, 0.0f, 1.0f, 0.3f, SOPER_STOP); + m_soundChannelSlide = -1; + } + } + } +} + + +// Adapte la physique de l'objet en fonction du terrain. + +void CPhysics::FloorAdapt(float aTime, float rTime, + D3DVECTOR &pos, D3DVECTOR &angle) +{ + Character* character; + ObjectType type; + D3DVECTOR norm; + D3DMATRIX matRotate; + float level, h, f, a1, volume, freq, force; + BOOL bOldSwim, bSlopingTerrain; + + type = m_object->RetType(); + character = m_object->RetCharacter(); + + level = m_water->RetLevel(m_object); + bOldSwim = m_bSwim; + SetSwim( pos.y < level ); + + m_floorLevel = m_terrain->RetFloorLevel(pos); // hauteur au-dessus du sol + h = pos.y-m_floorLevel; + h -= character->height; + m_floorHeight = h; + + WaterParticule(aTime, pos, type, m_floorLevel, + Abs(m_linMotion.realSpeed.x), + Abs(m_cirMotion.realSpeed.y*15.0f)); + + if ( m_type == TYPE_ROLLING ) + { + pos.y -= h; // plaque immédiatement au sol + pos.y += character->height; + m_floorHeight = 0.0f; + } + + if ( m_type == TYPE_FLYING ) + { + bSlopingTerrain = FALSE; // terrain possible pour atterrir + + if ( !m_bLand ) // en vol ? + { + m_terrain->GetNormal(norm, pos); + a1 = Abs(RotateAngle(Length(norm.x, norm.z), norm.y)); + if ( a1 < (90.0f-55.0f)*PI/180.0f ) // pente dépasse 55 degrés ? + { + bSlopingTerrain = TRUE; // terrain très pentu + + if ( h < 4.0f ) // choc avec le terrain ? + { + force = 5.0f+Abs(m_linMotion.realSpeed.x*0.3f)+ + Abs(m_linMotion.realSpeed.y*0.3f); + m_linMotion.currentSpeed = norm*force; + MatRotateXZY(matRotate, -angle); + m_linMotion.currentSpeed = Transform(matRotate, m_linMotion.currentSpeed); + + if ( aTime-m_soundTimeBoum > 0.5f ) + { + volume = Abs(m_linMotion.realSpeed.x*0.02f)+ + Abs(m_linMotion.realSpeed.y*0.02f); + freq = 0.5f+m_terrain->RetHardness(pos)*2.5f; + m_sound->Play(SOUND_BOUM, pos, volume, freq); + + m_soundTimeBoum = aTime; + } + +//? pos = m_object->RetPosition(0); // remet pos avant collision + } + } + } + + if ( (h <= 0.0f || m_bLand) && !bSlopingTerrain ) // au sol ? + { + if ( !m_bLand ) // en vol ? + { + volume = Abs(m_linMotion.realSpeed.y*0.02f); + freq = 0.5f+m_terrain->RetHardness(pos)*2.5f; + m_sound->Play(SOUND_BOUM, pos, volume, freq); + } + + m_bLand = TRUE; // au sol + SetMotor(FALSE); + pos.y -= h; // plaque immédiatement au sol + m_floorHeight = 0.0f; + + if ( h < 0.0f ) + { + f = Abs(m_linMotion.currentSpeed.y/m_linMotion.advanceSpeed.y); + CrashParticule(f); + } + m_linMotion.currentSpeed.y = 0.0f; + m_inclinaisonFactor = 1.0f/LANDING_SPEED; // glisse un peu au sol + m_linVibrationFactor = 0.0f; + m_cirVibrationFactor = 0.0f; + + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH ) return; // toujours droit + } + + if ( h > 4.0f || bSlopingTerrain ) // très au-dessus du sol ? + { + if ( m_bSwim ) + { + m_linVibrationFactor = 1.0f; // vibre un max + m_cirVibrationFactor = 1.0f; + } + else + { + m_linVibrationFactor = 2.0f; // vibre un gros max + m_cirVibrationFactor = 2.0f; + } + m_inclinaisonFactor = 1.0f; + + // Remet gentiment à l'horizontale. + if ( angle.x > 0.0f ) + { + angle.x -= rTime*0.5f; + if ( angle.x < 0.0f ) angle.x = 0.0f; + } + if ( angle.x < 0.0f ) + { + angle.x += rTime*0.5f; + if ( angle.x > 0.0f ) angle.x = 0.0f; + } + if ( angle.z > 0.0f ) + { + angle.z -= rTime*0.5f; + if ( angle.z < 0.0f ) angle.z = 0.0f; + } + if ( angle.z < 0.0f ) + { + angle.z += rTime*0.5f; + if ( angle.z > 0.0f ) angle.z = 0.0f; + } + return; + } + } + + if ( m_floorHeight == 0.0f ) // plaqué au sol ? + { + if ( m_object->RetTraceDown() ) + { + WheelParticule(m_object->RetTraceColor(), m_object->RetTraceWidth()*g_unit); + } + else + { + WheelParticule(-1, 0.0f); + } + } + + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH || + type == OBJECT_WORM ) return; // toujours droit + + FloorAngle(pos, angle); // adapte l'angle au terrain + + if ( m_type == TYPE_FLYING && !m_bLand ) // volant en l'air ? + { + f = h/1.0f; + if ( f < 0.0f ) f = 0.0f; + if ( f > 1.0f ) f = 1.0f; + m_linVibrationFactor = f; + m_cirVibrationFactor = f; + angle.z *= 1.0f-f; + angle.x *= 1.0f-f; + + f = h/1.0f; + if ( f < 0.0f ) f = 0.0f; + if ( f > 1.0f ) f = 1.0f; + m_inclinaisonFactor = f; + } +} + +// Calcule l'angle d'un objet avec le terrain. + +void CPhysics::FloorAngle(const D3DVECTOR &pos, D3DVECTOR &angle) +{ + Character* character; + D3DVECTOR pw, norm; + float a1, a2; + + character = m_object->RetCharacter(); + + pw.x = pos.x+character->wheelFront*cosf(angle.y+PI*0.0f); + pw.y = pos.y; + pw.z = pos.z-character->wheelFront*sinf(angle.y+PI*0.0f); + a1 = atanf(m_terrain->RetFloorHeight(pw)/character->wheelFront); + + pw.x = pos.x+character->wheelBack*cosf(angle.y+PI*1.0f); + pw.y = pos.y; + pw.z = pos.z-character->wheelBack*sinf(angle.y+PI*1.0f); + a2 = atanf(m_terrain->RetFloorHeight(pw)/character->wheelBack); + + angle.z = (a2-a1)/2.0f; + + pw.x = pos.x+character->wheelLeft*cosf(angle.y+PI*0.5f)*cosf(angle.z); + pw.y = pos.y; + pw.z = pos.z-character->wheelLeft*sinf(angle.y+PI*0.5f)*cosf(angle.z); + a1 = atanf(m_terrain->RetFloorHeight(pw)/character->wheelLeft); + + pw.x = pos.x+character->wheelRight*cosf(angle.y+PI*1.5f)*cosf(angle.z); + pw.y = pos.y; + pw.z = pos.z-character->wheelRight*sinf(angle.y+PI*1.5f)*cosf(angle.z); + a2 = atanf(m_terrain->RetFloorHeight(pw)/character->wheelRight); + + angle.x = (a2-a1)/2.0f; +} + + +// Adapte la physique de l'objet en fonction des autres objets. +// Retourne 0 -> objet mobile +// Retourne 1 -> objet immobile (à cause collision) +// Retourne 2 -> objet détruit + +int CPhysics::ObjectAdapt(const D3DVECTOR &pos, const D3DVECTOR &angle) +{ + CObject* pObj; + CPyro* pyro; + CPhysics* ph; + D3DMATRIX matRotate; + D3DVECTOR iPos, oPos, iiPos, oAngle, oSpeed; + Sound sound; + float iRad, oRad, distance, force, volume; + int i, j, colType; + ObjectType iType, oType; + + if ( m_object->RetRuin() ) return 0; // brûle ou explose ? + if ( !m_object->RetClip() ) return 0; + + // iiPos = centre sphère à l'ancienne position. + // iPos = centre sphère à la nouvelle position. + m_object->GetCrashSphere(0, iiPos, iRad); + iPos = iiPos + (pos - m_object->RetPosition(0)); + iType = m_object->RetType(); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; // soi-même ? + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + if ( !pObj->RetEnable() ) continue; // inactif ? + if ( pObj->RetRuin() ) continue; // brûle ou explose ? + if ( pObj->RetDead() ) continue; // homme mort ? + + oType = pObj->RetType(); + if ( oType == OBJECT_NULL ) continue; + if ( oType == OBJECT_TOTO ) continue; +//? if ( iType == OBJECT_BEE && oType == OBJECT_BEE ) continue; + if ( iType == OBJECT_WORM && oType != OBJECT_WORM ) continue; + if ( iType != OBJECT_WORM && oType == OBJECT_WORM ) continue; + if ( iType == OBJECT_MOTHER && oType == OBJECT_ANT ) continue; + if ( iType == OBJECT_ANT && oType == OBJECT_MOTHER ) continue; + if ( iType == OBJECT_MOTHER && oType == OBJECT_SPIDER ) continue; + if ( iType == OBJECT_SPIDER && oType == OBJECT_MOTHER ) continue; + if ( iType == OBJECT_MOTHER && oType == OBJECT_EGG ) continue; + if ( iType == OBJECT_EGG && oType == OBJECT_MOTHER ) continue; + + pObj->GetJotlerSphere(oPos, oRad); + if ( oRad > 0.0f ) + { + JostleObject(pObj, iPos, iRad, oPos, oRad); + } + + if ( iType == OBJECT_MOTHER || + iType == OBJECT_ANT || + iType == OBJECT_SPIDER || + iType == OBJECT_WORM || + iType == OBJECT_BEE ) // insecte ? + { + if ( oType == OBJECT_STONE || + oType == OBJECT_URANIUM || + oType == OBJECT_METAL || + oType == OBJECT_POWER || + oType == OBJECT_ATOMIC || + oType == OBJECT_BULLET || + oType == OBJECT_BBOX || + oType == OBJECT_KEYa || + oType == OBJECT_KEYb || + oType == OBJECT_KEYc || + oType == OBJECT_KEYd || + oType == OBJECT_TNT || + (oType >= OBJECT_PLANT0 && oType <= OBJECT_PLANT19 ) || + (oType >= OBJECT_MUSHROOM0 && oType <= OBJECT_MUSHROOM9) ) continue; + } + +#if _TEEN + if ( oType == OBJECT_WAYPOINT && + pObj->RetEnable() && + !m_object->RetResetBusy() ) // véhicule d'entraînement ? +#else + if ( oType == OBJECT_WAYPOINT && + pObj->RetEnable() && + !m_object->RetResetBusy() && + m_object->RetTrainer() ) // véhicule d'entraînement ? +#endif + { + oPos = pObj->RetPosition(0); + distance = Length2d(oPos, iPos); + if ( distance < 4.0f ) + { + m_sound->Play(SOUND_WAYPOINT, m_object->RetPosition(0)); + pyro = new CPyro(m_iMan); + pyro->Create(PT_WPCHECK, pObj); + } + } + + if ( oType == OBJECT_TARGET2 ) + { + oPos = pObj->RetPosition(0); + distance = Length(oPos, iPos); + if ( distance < 10.0f*1.5f ) + { + m_sound->Play(SOUND_WAYPOINT, m_object->RetPosition(0)); + pyro = new CPyro(m_iMan); + pyro->Create(PT_WPCHECK, pObj); + } + } + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRad) ) + { + if ( iType == OBJECT_MOTHER && oRad <= 1.2f ) continue; + if ( iType == OBJECT_ANT && oRad <= 1.2f ) continue; + if ( iType == OBJECT_SPIDER && oRad <= 1.2f ) continue; + if ( iType == OBJECT_BEE && oRad <= 1.2f ) continue; + if ( iType == OBJECT_WORM && oRad <= 1.2f ) continue; + + distance = Length(oPos, iPos); + if ( distance < iRad+oRad ) // collision ? + { + distance = Length(oPos, iiPos); + if ( distance >= iRad+oRad ) // voir (*) + { + m_bCollision = TRUE; + m_bObstacle = TRUE; + + sound = pObj->RetCrashSphereSound(j-1); + if ( sound != SOUND_CLICK ) + { + force = Abs(m_linMotion.realSpeed.x); + force *= pObj->RetCrashSphereHardness(j-1)*2.0f; + if ( ExploOther(iType, pObj, oType, force) ) continue; + colType = ExploHimself(iType, oType, force); + if ( colType == 2 ) return 2; // détruit ? + if ( colType == 0 ) continue; // passe outre ? + } + + force = Length(m_linMotion.realSpeed); + force *= pObj->RetCrashSphereHardness(j-1); + volume = Abs(force*0.05f); + if ( volume > 1.0f ) volume = 1.0f; + if ( sound != SOUND_CLICK ) + { + m_sound->Play(sound, m_object->RetPosition(0), volume); + } + if ( iType == OBJECT_HUMAN && volume > 0.5f ) + { + m_sound->Play(SOUND_AIE, m_object->RetPosition(0), volume); + } + + if ( m_repeatCollision > 0 ) + { + force *= 0.5f*m_repeatCollision; + if ( force > 20.0f ) force = 20.0f; + } + m_repeatCollision += 2; + if ( m_repeatCollision > 10 ) + { + m_repeatCollision = 10; + } + + m_linMotion.currentSpeed = Normalize(iPos-oPos)*force; + MatRotateXZY(matRotate, -angle); + m_linMotion.currentSpeed = Transform(matRotate, m_linMotion.currentSpeed); + if ( m_type == TYPE_ROLLING ) + { + m_linMotion.currentSpeed.y = 0.0f; + } + + ph = pObj->RetPhysics(); + if ( ph != 0 ) + { + oAngle = pObj->RetAngle(0); + oSpeed = Normalize(oPos-iPos)*force; + MatRotateXZY(matRotate, -oAngle); + oSpeed = Transform(matRotate, oSpeed); + if ( ph->RetType() == TYPE_ROLLING ) + { + oSpeed.y = 0.0f; + } + ph->SetLinMotion(MO_CURSPEED, oSpeed); + } + return 1; + } + } + } + } + + if ( m_repeatCollision > 0 ) + { + m_repeatCollision --; + } + return 0; +} + +// (*) En cas de collision à la position initiale (iiPos) et à la +// nouvelle position (iPos), l'obstacle est ignoré. On peut +// donc passer à travers. Ceci est nécessaire lorsqu'un obstacle +// se retrouve "dans" un véhicule, pour ne pas le bloquer +// définitivement ! + + +// Bouscule un objet. + +BOOL CPhysics::JostleObject(CObject* pObj, D3DVECTOR iPos, float iRad, + D3DVECTOR oPos, float oRad) +{ + D3DVECTOR speed; + float distance, force, d, f; + + distance = Length(oPos, iPos); + if ( distance >= iRad+oRad ) return FALSE; + + d = (iRad+oRad)/2.0f; + f = (distance-d)/d; // 0=loin, 1=proche + if ( f < 0.0f ) f = 0.0f; + if ( f > 1.0f ) f = 1.0f; + + speed = m_linMotion.realSpeed; + speed.y = 0.0f; + force = Length(speed)*f*0.05f; + if ( force > 1.0f ) force = 1.0f; + + if ( m_soundTimeJostle >= 0.20f ) + { + m_soundTimeJostle = 0.0f; + m_sound->Play(SOUND_JOSTLE, iPos, force); + } + + return pObj->JostleObject(force); +} + +// Bouscule forcément un objet. + +BOOL CPhysics::JostleObject(CObject* pObj, float force) +{ + D3DVECTOR oPos; + float oRad; + + pObj->GetJotlerSphere(oPos, oRad); + if ( oRad <= 0.0f ) return FALSE; + + if ( m_soundTimeJostle >= 0.20f ) + { + m_soundTimeJostle = 0.0f; + m_sound->Play(SOUND_JOSTLE, pObj->RetPosition(0), force); + } + + return pObj->JostleObject(force); +} + +// Action de l'explosion sur l'objet tamponné. +// Retourne TRUE s'il faut ignorer cet obstacle. + +BOOL CPhysics::ExploOther(ObjectType iType, + CObject *pObj, ObjectType oType, float force) +{ + CPyro* pyro; + + if ( !pObj->RetEnable() ) return TRUE; + + JostleObject(pObj, 1.0f); // bouscule l'objet + + if ( force > 50.0f && + (oType == OBJECT_FRET || + oType == OBJECT_METAL ) ) + { + pyro = new CPyro(m_iMan); + pyro->Create(PT_EXPLOT, pObj); // destruction totale + } + + if ( force > 50.0f && + (oType == OBJECT_POWER || + oType == OBJECT_ATOMIC ) ) + { + pyro = new CPyro(m_iMan); + pyro->Create(PT_FRAGT, pObj); // destruction totale + } + + if ( force > 25.0f && + (oType == OBJECT_STONE || + oType == OBJECT_URANIUM ) ) + { + pyro = new CPyro(m_iMan); + pyro->Create(PT_FRAGT, pObj); // destruction totale + } + + if ( force > 25.0f && + (oType == OBJECT_DERRICK || + oType == OBJECT_FACTORY || + oType == OBJECT_STATION || + oType == OBJECT_CONVERT || + oType == OBJECT_REPAIR || + oType == OBJECT_DESTROYER|| + oType == OBJECT_TOWER || + oType == OBJECT_RESEARCH || + oType == OBJECT_RADAR || + oType == OBJECT_INFO || + oType == OBJECT_ENERGY || + oType == OBJECT_LABO || + oType == OBJECT_NUCLEAR || + oType == OBJECT_PARA || + oType == OBJECT_SAFE || + oType == OBJECT_HUSTON ) ) // bâtiment ? + { + pObj->ExploObject(EXPLO_BOUM, force/400.0f); + } + + if ( force > 25.0f && + (oType == OBJECT_MOBILEwa || + oType == OBJECT_MOBILEta || + oType == OBJECT_MOBILEfa || + oType == OBJECT_MOBILEia || + oType == OBJECT_MOBILEwc || + oType == OBJECT_MOBILEtc || + oType == OBJECT_MOBILEfc || + oType == OBJECT_MOBILEic || + oType == OBJECT_MOBILEwi || + oType == OBJECT_MOBILEti || + oType == OBJECT_MOBILEfi || + oType == OBJECT_MOBILEii || + oType == OBJECT_MOBILEws || + oType == OBJECT_MOBILEts || + oType == OBJECT_MOBILEfs || + oType == OBJECT_MOBILEis || + oType == OBJECT_MOBILErt || + oType == OBJECT_MOBILErc || + oType == OBJECT_MOBILErr || + oType == OBJECT_MOBILErs || + oType == OBJECT_MOBILEsa || + oType == OBJECT_MOBILEwt || + oType == OBJECT_MOBILEtt || + oType == OBJECT_MOBILEft || + oType == OBJECT_MOBILEit || + oType == OBJECT_MOBILEdr || + oType == OBJECT_APOLLO2 ) ) // véhicule ? + { + pObj->ExploObject(EXPLO_BOUM, force/200.0f); + } + + if ( force > 10.0f && + (oType == OBJECT_MOBILEtg || + oType == OBJECT_TNT ) ) + { + pyro = new CPyro(m_iMan); + pyro->Create(PT_FRAGT, pObj); // destruction totale + } + + if ( force > 0.0f && + oType == OBJECT_BOMB ) + { + pyro = new CPyro(m_iMan); + pyro->Create(PT_FRAGT, pObj); // destruction totale + } + + return FALSE; +} + +// Action de l'explosion sur l'objet lui-même. +// Retourne 0 -> objet mobile +// Retourne 1 -> objet immobile +// Retourne 2 -> objet détruit + +int CPhysics::ExploHimself(ObjectType iType, ObjectType oType, float force) +{ + PyroType type; + CPyro* pyro; + + if ( force > 10.0f && + (oType == OBJECT_TNT || + oType == OBJECT_MOBILEtg ) ) + { + if ( iType == OBJECT_HUMAN ) type = PT_DEADG; + else type = PT_EXPLOT; + pyro = new CPyro(m_iMan); + pyro->Create(type, m_object); // destruction totale + return 2; + } + + if ( force > 0.0f && + oType == OBJECT_BOMB ) + { + if ( iType == OBJECT_HUMAN ) + { + type = PT_DEADG; + } + else if ( iType == OBJECT_ANT || + iType == OBJECT_SPIDER || + iType == OBJECT_BEE ) + { + type = PT_EXPLOO; + } + else + { + type = PT_EXPLOT; + } + pyro = new CPyro(m_iMan); + pyro->Create(type, m_object); // destruction totale + return 2; + } + + if ( force > 25.0f && + (iType == OBJECT_HUMAN || + iType == OBJECT_MOBILEwa || + iType == OBJECT_MOBILEta || + iType == OBJECT_MOBILEfa || + iType == OBJECT_MOBILEia || + iType == OBJECT_MOBILEwc || + iType == OBJECT_MOBILEtc || + iType == OBJECT_MOBILEfc || + iType == OBJECT_MOBILEic || + iType == OBJECT_MOBILEwi || + iType == OBJECT_MOBILEti || + iType == OBJECT_MOBILEfi || + iType == OBJECT_MOBILEii || + iType == OBJECT_MOBILEws || + iType == OBJECT_MOBILEts || + iType == OBJECT_MOBILEfs || + iType == OBJECT_MOBILEis || + iType == OBJECT_MOBILErt || + iType == OBJECT_MOBILErc || + iType == OBJECT_MOBILErr || + iType == OBJECT_MOBILErs || + iType == OBJECT_MOBILEsa || + iType == OBJECT_MOBILEwt || + iType == OBJECT_MOBILEtt || + iType == OBJECT_MOBILEft || + iType == OBJECT_MOBILEit || + iType == OBJECT_MOBILEdr || + iType == OBJECT_APOLLO2 ) ) // véhicule ? + { + if ( oType == OBJECT_DERRICK || + oType == OBJECT_FACTORY || + oType == OBJECT_STATION || + oType == OBJECT_CONVERT || + oType == OBJECT_REPAIR || + oType == OBJECT_DESTROYER|| + oType == OBJECT_TOWER || + oType == OBJECT_RESEARCH || + oType == OBJECT_RADAR || + oType == OBJECT_INFO || + oType == OBJECT_ENERGY || + oType == OBJECT_LABO || + oType == OBJECT_NUCLEAR || + oType == OBJECT_PARA || + oType == OBJECT_SAFE || + oType == OBJECT_HUSTON ) // bâtiment ? + { + force /= 200.0f; + } + else + if ( oType == OBJECT_MOTHER || + oType == OBJECT_ANT || + oType == OBJECT_SPIDER || + oType == OBJECT_BEE || + oType == OBJECT_WORM ) // insecte ? + { + force /= 400.0f; + } + else + if ( oType == OBJECT_FRET || + oType == OBJECT_STONE || + oType == OBJECT_METAL ) + { + force /= 500.0f; + } + else + if ( oType == OBJECT_URANIUM || + oType == OBJECT_POWER || + oType == OBJECT_ATOMIC ) + { + force /= 100.0f; + } + else + { + force /= 200.0f; + } + + if ( m_object->ExploObject(EXPLO_BOUM, force) ) return 2; + } + + return 1; +} + + + +// Fait évoluer les particules. + +void CPhysics::FrameParticule(float aTime, float rTime) +{ + D3DVECTOR pos; + CObject* power; + float energy, intensity; + int effectLight; + BOOL bFlash; + + m_restBreakParticule -= rTime; + if ( aTime-m_lastPowerParticule < m_engine->ParticuleAdapt(0.05f) ) return; + m_lastPowerParticule = aTime; + + bFlash = FALSE; + + energy = 0.0f; + power = m_object->RetPower(); + if ( power != 0 ) + { + energy = power->RetEnergy(); + } + + if ( energy != m_lastEnergy ) // changement du niveau d'énergie ? + { + if ( energy > m_lastEnergy ) // recharge ? + { + PowerParticule(1.0f, FALSE); + bFlash = TRUE; + } + + if ( energy == 0.0f || m_lastEnergy == 0.0f ) + { + m_restBreakParticule = 2.5f; // particules pendant 2.5s + } + + m_lastEnergy = energy; + } + + if ( m_restBreakParticule > 0.0f ) + { + PowerParticule(m_restBreakParticule/2.5f, (energy == 0)); + bFlash = TRUE; + } + + effectLight = m_object->RetEffectLight(); + if ( effectLight != -1 ) + { + if ( bFlash ) + { + intensity = 0.0f; + if ( Rand() < 0.5f ) intensity = 1.0f; + m_light->SetLightIntensity(effectLight, intensity); + m_light->SetLightIntensitySpeed(effectLight, 10000.0f); + } + else + { + m_light->SetLightIntensity(effectLight, 0.0f); + } + } +} + +// Génère qq particules suite à une recharge. + +void CPhysics::PowerParticule(float factor, BOOL bBreak) +{ + Character* character; + CObject* fret; + D3DMATRIX* mat; + D3DVECTOR pos, ppos, eye, speed; + FPOINT dim; + BOOL bCarryPower; + + bCarryPower = FALSE; + fret = m_object->RetFret(); + if ( fret != 0 && fret->RetType() == OBJECT_POWER && + m_object->RetAngleZ(1) == ARM_STOCK_ANGLE1 ) + { + bCarryPower = TRUE; // porte une batterie + } + + mat = m_object->RetWorldMatrix(0); + character = m_object->RetCharacter(); + + pos = character->posPower; + pos.x -= 0.3f; + pos.y += 1.0f; // position centre batterie + pos = Transform(*mat, pos); + + speed.x = (Rand()-0.5f)*12.0f; + speed.y = (Rand()-0.5f)*12.0f; + speed.z = (Rand()-0.5f)*12.0f; + + ppos.x = pos.x; + ppos.y = pos.y+(Rand()-0.5f)*2.0f; + ppos.z = pos.z; + + dim.x = 1.0f*factor; + dim.y = 1.0f*factor; + + m_particule->CreateParticule(ppos, speed, dim, PARTIBLITZ, 0.5f, 0.0f, 0.0f); + + if ( bCarryPower ) // porte une batterie ? + { + pos = D3DVECTOR(3.0f, 5.6f, 0.0f); // position batterie portée + pos = Transform(*mat, pos); + + speed.x = (Rand()-0.5f)*12.0f; + speed.y = (Rand()-0.5f)*12.0f; + speed.z = (Rand()-0.5f)*12.0f; + + ppos.x = pos.x; + ppos.y = pos.y; + ppos.z = pos.z+(Rand()-0.5f)*2.0f; + + dim.x = 1.0f*factor; + dim.y = 1.0f*factor; + + m_particule->CreateParticule(ppos, speed, dim, PARTIBLITZ, 0.5f, 0.0f, 0.0f); + } +} + +// Génère qq particules suite à une chute. +// crash: 0=super soft, 1=big crash + +void CPhysics::CrashParticule(float crash) +{ + D3DVECTOR pos, ppos, speed; + FPOINT dim; + float len; + int i, max; + + if ( crash < 0.2f ) return; + + pos = m_object->RetPosition(0); + m_camera->StartEffect(CE_CRASH, pos, crash); + +//? max = (int)(crash*50.0f); + max = (int)(crash*10.0f*m_engine->RetParticuleDensity()); + + for ( i=0 ; iCreateParticule(ppos, speed, dim, PARTICRASH, 2.0f); + } +} + +// Génère qq particules de gaz d'échappement. + +void CPhysics::MotorParticule(float aTime, float rTime) +{ + D3DMATRIX* mat; + D3DVECTOR pos, speed; + FPOINT dim; + ObjectType type; + FPOINT c, p; + float h, a, delay, level; + int r, i, nb; + + if ( m_object->RetToy() ) return; + + type = m_object->RetType(); + + if ( type == OBJECT_MOBILEia || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEis || // pattes ? + type == OBJECT_MOBILEdr || + type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM || + type == OBJECT_APOLLO2 ) return; + + if ( type == OBJECT_HUMAN ) delay = 3.0f; + else delay = 8.0f; + if ( m_bSwim && m_timeUnderWater < delay ) // bulles en entrant dans l'eau ? + { + if ( aTime-m_lastUnderParticule >= m_engine->ParticuleAdapt(0.05f) ) + { + m_lastUnderParticule = aTime; + + nb = (int)(20.0f-(20.0f/delay)*m_timeUnderWater); + for ( i=0 ; iRetPosition(0); + pos.x += (Rand()-0.5f)*4.0f; + pos.y += (Rand()-0.5f)*4.0f; + pos.z += (Rand()-0.5f)*4.0f; + speed.y = (Rand()-0.5f)*8.0f+8.0f; + speed.x = (Rand()-0.5f)*0.2f; + speed.z = (Rand()-0.5f)*0.2f; + dim.x = 0.06f+Rand()*0.10f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBUBBLE, 3.0f, 0.0f, 0.0f); + } + } + } + + level = m_water->RetLevel(); + pos = m_object->RetPosition(0); + if ( type == OBJECT_HUMAN ) pos.y -= 2.0f; + if ( pos.y < level ) // sous l'eau ? + { + m_absorbWater += rTime*(1.0f/2.0f); // se mouille + if ( m_absorbWater > 1.0f ) m_absorbWater = 1.0f; + } + else // hors de l'eau ? + { + m_absorbWater -= rTime*(1.0f/3.0f); // se sèche + if ( m_absorbWater < 0.0f ) m_absorbWater = 0.0f; + } + + if ( pos.y >= level && + m_absorbWater > 0.0f && + !m_water->RetLava() ) // gouttes en sortant de l'eau ? + { + if ( aTime-m_lastUnderParticule >= m_engine->ParticuleAdapt(0.05f) ) + { + m_lastUnderParticule = aTime; + + nb = (int)(8.0f*m_absorbWater); + for ( i=0 ; iRetPosition(0); + if ( type == OBJECT_HUMAN ) pos.y -= Rand()*2.0f; + else pos.y += Rand()*2.0f; + pos.x += (Rand()-0.5f)*2.0f; + pos.z += (Rand()-0.5f)*2.0f; + speed.y = -((Rand()-0.5f)*8.0f+8.0f); + speed.x = 0.0f; + speed.z = 0.0f; + dim.x = 0.2f; + dim.y = 0.2f; + m_particule->CreateParticule(pos, speed, dim, PARTIWATER, 2.0f, 0.0f, 1.0f); + } + } + } + + if ( type == OBJECT_HUMAN || // homme ? + type == OBJECT_TECH ) + { + if ( m_bLand && + aTime-m_lastSlideParticule >= m_engine->ParticuleAdapt(0.05f) ) + { + h = Max(Abs(m_linMotion.terrainSpeed.x), + Abs(m_linMotion.terrainSpeed.z)); + if ( h > m_linMotion.terrainSlide.x+0.5f && + m_linMotion.motorSpeed.x == 0.0f ) // glisse à l'arrêt ? + { + m_lastSlideParticule = aTime; + + mat = m_object->RetWorldMatrix(0); + pos.x = (Rand()-0.5f)*1.0f; + pos.y = -m_object->RetCharacter()->height; + pos.z = Rand()*0.4f+1.0f; + if ( rand()%2 == 0 ) pos.z = -pos.z; + pos = Transform(*mat, pos); + speed = D3DVECTOR(0.0f, 1.0f, 0.0f); + dim.x = Rand()*(h-5.0f)/2.0f+1.0f; + if ( dim.x > 2.5f ) dim.x = 2.5f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f, 0.0f, 0.2f); + } + } + } + + if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts ) // chenilles ? + { + if ( aTime-m_lastSlideParticule >= m_engine->ParticuleAdapt(0.05f) ) + { + h = Abs(m_linMotion.motorSpeed.x-m_linMotion.realSpeed.x); + if ( h > 5.0f ) + { + m_lastSlideParticule = aTime; + + mat = m_object->RetWorldMatrix(0); + pos.x = (Rand()-0.5f)*8.0f; + pos.y = 0.0f; + pos.z = Rand()*2.0f+3.0f; + if ( rand()%2 == 0 ) pos.z = -pos.z; + pos = Transform(*mat, pos); + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = Rand()*(h-5.0f)/2.0f+1.0f; + if ( dim.x > 3.0f ) dim.x = 3.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f, 0.0f, 0.2f); + } + } + } + + if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) // grosses chenilles ? + { + if ( aTime-m_lastSlideParticule >= m_engine->ParticuleAdapt(0.05f) ) + { + h = Abs(m_linMotion.motorSpeed.x-m_linMotion.realSpeed.x); + if ( h > 5.0f ) + { + m_lastSlideParticule = aTime; + + mat = m_object->RetWorldMatrix(0); + pos.x = (Rand()-0.5f)*9.0f; + pos.y = 0.0f; + pos.z = Rand()*3.0f+3.0f; + if ( rand()%2 == 0 ) pos.z = -pos.z; + pos = Transform(*mat, pos); + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = Rand()*(h-5.0f)/2.0f+1.0f; + if ( dim.x > 3.0f ) dim.x = 3.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f, 0.0f, 0.2f); + } + } + } + + if ( (type == OBJECT_HUMAN || type == OBJECT_TECH) && !m_bSwim ) + { + if ( m_bLand ) // au sol ? + { + if ( m_reactorTemperature > 0.0f ) + { + m_reactorTemperature -= rTime*(1.0f/10.0f); // ça refroidi + if ( m_reactorTemperature < 0.0f ) + { + m_reactorTemperature = 0.0f; + } + } + + if ( m_reactorTemperature == 0.0f || + aTime-m_lastMotorParticule < m_engine->ParticuleAdapt(0.05f) ) return; + m_lastMotorParticule = aTime; + + pos = D3DVECTOR(-1.6f, -0.5f, 0.0f); + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + + speed.x = (Rand()-0.5f)*0.6f; + speed.z = (Rand()-0.5f)*0.6f; + speed.y = -(0.5f+Rand()*0.3f)*(1.0f-m_reactorTemperature); + + dim.x = (1.0f+Rand()*0.5f)*(0.2f+m_reactorTemperature*0.8f); + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE2, 3.0f, 0.0f, 0.1f); + } + else // en vol ? + { + if ( !m_bMotor || m_reactorRange == 0.0f ) return; + + if ( m_reactorTemperature < 1.0f ) // pas trop chaud ? + { + m_reactorTemperature += rTime*(1.0f/4.0f); // ça chauffe + if ( m_reactorTemperature > 1.0f ) + { + m_reactorTemperature = 1.0f; // mais pas trop + } + } + + if ( aTime-m_lastMotorParticule < m_engine->ParticuleAdapt(0.02f) ) return; + m_lastMotorParticule = aTime; + + pos = D3DVECTOR(-1.6f, -1.0f, 0.0f); + pos.x += (Rand()-0.5f)*3.0f; + pos.y += (Rand()-0.5f)*1.5f; + pos.z += (Rand()-0.5f)*3.0f; + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + + h = m_floorHeight; + if ( h > 10.0f ) // assez haut ? + { + speed = D3DVECTOR(0.0f, -10.0f, 0.0f); // contre le bas + } + else + { + speed.y = 10.0f-2.0f*h - Rand()*(10.0f-h); // contre le haut + speed.x = (Rand()-0.5f)*(5.0f-h)*1.0f; // horizontal (xz) + speed.z = (Rand()-0.5f)*(5.0f-h)*1.0f; + } + + dim.x = 0.12f; + dim.y = 0.12f; + + m_particule->CreateParticule(pos, speed, dim, PARTISCRAPS, 2.0f, 10.0f); + +#if 1 + pos = D3DVECTOR(-1.6f, -0.5f, 0.0f); + pos = Transform(*mat, pos); + + speed.x = (Rand()-0.5f)*1.0f; + speed.z = (Rand()-0.5f)*1.0f; + speed.y = -(4.0f+Rand()*3.0f); + speed.x += m_linMotion.realSpeed.x*0.8f; + speed.z -= m_linMotion.realSpeed.x*m_cirMotion.realSpeed.y*0.05f; + if ( m_linMotion.realSpeed.y > 0.0f ) + { + speed.y += m_linMotion.realSpeed.y*0.5f; + } + else + { + speed.y += m_linMotion.realSpeed.y*1.2f; + } + a = m_object->RetAngleY(0); + p.x = speed.x; + p.y = speed.z; + p = RotatePoint(-a, p); + speed.x = p.x; + speed.z = p.y; + + dim.x = 0.4f+Rand()*0.2f; + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, PARTIEJECT, 0.3f, 10.0f); +#endif + } + } + + if ( (type == OBJECT_HUMAN || type == OBJECT_TECH) && m_bSwim ) + { + m_reactorTemperature = 0.0f; // réacteur froid + } + + if ( m_type == TYPE_FLYING && + type != OBJECT_HUMAN && + type != OBJECT_TECH && + !m_bSwim ) + { + if ( m_bLand ) // au sol ? + { + if ( m_motorSpeed.x == 0.0f && // glisse à cause pente terrain ? + m_cirMotion.realSpeed.y == 0.0f ) + { + h = Max(Abs(m_linMotion.realSpeed.x), + Abs(m_linMotion.realSpeed.z)); + + if ( h < 3.0f ) return; + + if ( aTime-m_lastMotorParticule < m_engine->ParticuleAdapt(0.2f) ) return; + m_lastMotorParticule = aTime; + + r = rand()%3; + if ( r == 0 ) pos = D3DVECTOR(-3.0f, 0.0f, -4.0f); + if ( r == 1 ) pos = D3DVECTOR(-3.0f, 0.0f, 4.0f); + if ( r == 2 ) pos = D3DVECTOR( 4.0f, 0.0f, 0.0f); + + pos.x += (Rand()-0.5f)*2.0f; + pos.z += (Rand()-0.5f)*2.0f; + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = Rand()*h/5.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f); + } + else // glisse avec petits réacteurs dans patins ? + { + if ( m_linMotion.realSpeed.x == 0.0f && + m_cirMotion.realSpeed.y == 0.0f ) return; + + if ( aTime-m_lastMotorParticule < m_engine->ParticuleAdapt(0.02f) ) return; + m_lastMotorParticule = aTime; + + r = rand()%3; + if ( r == 0 ) pos = D3DVECTOR(-3.0f, 0.0f, -4.0f); + if ( r == 1 ) pos = D3DVECTOR(-3.0f, 0.0f, 4.0f); + if ( r == 2 ) pos = D3DVECTOR( 4.0f, 0.0f, 0.0f); + + pos.x += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIEJECT); + } + } + else // en vol ? + { + if ( !m_bMotor || m_reactorRange == 0.0f ) return; + + if ( aTime-m_lastMotorParticule < m_engine->ParticuleAdapt(0.02f) ) return; + m_lastMotorParticule = aTime; + + pos = D3DVECTOR(0.0f, -1.0f, 0.0f); + pos.x += (Rand()-0.5f)*6.0f; + pos.y += (Rand()-0.5f)*3.0f; + pos.z += (Rand()-0.5f)*6.0f; + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + + h = m_floorHeight; + if ( h > 10.0f ) // assez haut ? + { + speed = D3DVECTOR(0.0f, -10.0f, 0.0f); // contre le bas + } + else + { + speed.y = 10.0f-2.0f*h - Rand()*(10.0f-h); // contre le haut + speed.x = (Rand()-0.5f)*(10.0f-h)*2.0f; // horizontal (xz) + speed.z = (Rand()-0.5f)*(10.0f-h)*2.0f; + } + + dim.x = 0.2f; + dim.y = 0.2f; + + m_particule->CreateParticule(pos, speed, dim, PARTISCRAPS, 2.0f, 10.0f); + +#if 1 + pos = D3DVECTOR(0.0f, 1.0f, 0.0f); + pos = Transform(*mat, pos); + + speed.x = (Rand()-0.5f)*1.0f; + speed.z = (Rand()-0.5f)*1.0f; + speed.y = -(6.0f+Rand()*4.5f); + speed.x += m_linMotion.realSpeed.x*0.8f; + speed.z -= m_linMotion.realSpeed.x*m_cirMotion.realSpeed.y*0.05f; + if ( m_linMotion.realSpeed.y > 0.0f ) + { + speed.y += m_linMotion.realSpeed.y*0.5f; + } + else + { + speed.y += m_linMotion.realSpeed.y*1.2f; + } + a = m_object->RetAngleY(0); + p.x = speed.x; + p.y = speed.z; + p = RotatePoint(-a, p); + speed.x = p.x; + speed.z = p.y; + + dim.x = 0.7f+Rand()*0.6f; + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, PARTIEJECT, 0.5f, 10.0f); +#endif + } + } + + if ( (type == OBJECT_HUMAN || type == OBJECT_TECH) && m_bSwim ) + { + if ( !m_object->RetDead() ) + { + h = Mod(aTime, 5.0f); + if ( h < 3.5f && ( h < 1.5f || h > 1.6f ) ) return; + } + if ( aTime-m_lastMotorParticule < m_engine->ParticuleAdapt(0.06f) ) return; + m_lastMotorParticule = aTime; + + pos = D3DVECTOR(0.0f, 3.0f, 0.0f); + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + pos.x += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + speed.y = (Rand()-0.5f)*8.0f+8.0f; + speed.x = (Rand()-0.5f)*0.2f; + speed.z = (Rand()-0.5f)*0.2f; + dim.x = 0.2f; + dim.y = 0.2f; + m_particule->CreateParticule(pos, speed, dim, PARTIBUBBLE, 3.0f, 0.0f, 0.0f); + + if ( aTime-m_lastSoundWater > 1.5f ) + { + m_lastSoundWater = aTime; + m_sound->Play(SOUND_BLUP, m_object->RetPosition(0), 0.5f+Rand()*0.5f); + } + } + + if ( type == OBJECT_MOBILEsa && m_bSwim ) + { + h = Mod(aTime, 3.0f); + if ( h < 1.5f && ( h < 0.5f || h > 0.9f ) ) return; + if ( aTime-m_lastMotorParticule < m_engine->ParticuleAdapt(0.06f) ) return; + m_lastMotorParticule = aTime; + + pos = D3DVECTOR(0.0f, 3.0f, 0.0f); + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + pos.x += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + speed.y = (Rand()-0.5f)*8.0f+8.0f; + speed.x = (Rand()-0.5f)*0.2f; + speed.z = (Rand()-0.5f)*0.2f; + dim.x = 0.2f; + dim.y = 0.2f; + m_particule->CreateParticule(pos, speed, dim, PARTIBUBBLE, 3.0f, 0.0f, 0.0f); + + if ( aTime-m_lastSoundWater > 1.5f ) + { + m_lastSoundWater = aTime; + m_sound->Play(SOUND_BLUP, m_object->RetPosition(0), 0.5f+Rand()*0.5f); + } + } + + if ( m_type == TYPE_ROLLING ) + { + if ( type == OBJECT_APOLLO2 ) return; // moteurs électriques ! + + if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) + { + if ( !m_bMotor ) return; + + if ( aTime-m_lastMotorParticule < m_engine->ParticuleAdapt(0.1f) ) return; + m_lastMotorParticule = aTime; + + pos = D3DVECTOR(-2.5f, 10.3f, -1.3f); + pos.x += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = 1.5f+Rand()*1.0f; + + dim.x = Rand()*0.6f+0.4f; + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, PARTIMOTOR, 2.0f); + } + else + { + if ( !m_bMotor ) return; + + if ( aTime-m_lastMotorParticule < m_engine->ParticuleAdapt(0.05f) ) return; + m_lastMotorParticule = aTime; + + pos = D3DVECTOR(-3.4f, 1.8f, 0.5f); + + speed = pos; + if ( m_linMotion.currentSpeed.x < 0.0f ) + { + speed.x += m_linMotion.currentSpeed.x*1.2f; + } + else if ( m_linMotion.currentSpeed.x > 0.0f ) + { + speed.x += 0.0f; + } + else + { + speed.x -= 3.0f; + } + speed.y -= 0.5f+Rand()*2.0f; + speed.z += (Rand()-0.5f)*3.0f; + + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, pos); + speed = Transform(*mat, speed)-pos; + + dim.x = Rand()*0.4f+0.3f; + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, PARTIMOTOR, 2.0f); + } + } +} + +// Génère qq particules suite à une chute dans l'eau. + +void CPhysics::WaterParticule(float aTime, D3DVECTOR pos, ObjectType type, + float floor, float advance, float turn) +{ + D3DVECTOR ppos, speed; + FPOINT dim; + float delay, level, min, max, force, volume, diam; + int i, nb; + + level = m_water->RetLevel(); + if ( floor >= level ) return; + + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH ) + { + min = 3.0f; + max = 3.0f; + } + else + { + min = 0.0f; + max = 9.0f; + } + + if ( pos.y+max < level || pos.y-min > level ) return; + + // Gestion de la particule "plouf". + if ( m_linMotion.realSpeed.y < -10.0f && + aTime-m_lastPloufParticule >= 1.0f ) + { + m_lastPloufParticule = aTime; + + force = -m_linMotion.realSpeed.y/20.0f; // force selon vitesse chute + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH ) + { + diam = 2.5f; + } + else + { + diam = 5.0f; + force *= 1.3f; // un robot est plus lourd + } + + pos = m_object->RetPosition(0); + pos.y = m_water->RetLevel()-1.0f; + dim.x = 2.0f*force; // hauteur + dim.y = diam; // diamètre + m_particule->CreateParticule(pos, D3DVECTOR(0.0f, 0.0f, 0.0f), dim, PARTIPLOUF0, 1.4f, 0.0f, 0.0f); + + force = (0.5f+force*0.5f); + nb = (int)(force*50.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iCreateParticule(ppos, speed, dim, PARTIDROP, 2.0f, 20.0f, 0.2f); + } + + volume = Abs(m_linMotion.realSpeed.y*0.02f); + if ( volume > 1.0f ) volume = 1.0f; + m_sound->Play(SOUND_PLOUF, pos, volume); + } + + // Gestion des particules "flic". + if ( m_water->RetLava() ) return; + + if ( advance == 0.0f && turn == 0.0f ) + { + turn = 10.0f; + delay = 0.50f; + } + else if ( advance == 0.0f ) + { + delay = 0.24f; + } + else + { + delay = 0.06f; + } + m_engine->ParticuleAdapt(delay); + + if ( aTime-m_lastWaterParticule < delay ) return; + m_lastWaterParticule = aTime; + + force = (advance+turn)*0.16f; + if ( force < 0.001f ) return; + + pos = m_object->RetPosition(0); + pos.y = level+0.1f; + if ( advance == 0 ) + { + pos.x += (Rand()-0.5f)*10.0f; + pos.z += (Rand()-0.5f)*10.0f; + } + else + { + pos.x += (Rand()-0.5f)*4.0f; + pos.z += (Rand()-0.5f)*4.0f; + } + speed.y = 0.0f; + speed.x = 0.0f; + speed.z = 0.0f; + dim.x = Min(Rand()*force+force+1.0f, 10.0f); + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFLIC, 3.0f, 0.0f, 0.0f); +} + +// Crée la trace sous le robot. + +void CPhysics::WheelParticule(int color, float width) +{ + Character* character; + D3DMATRIX* mat; + D3DVECTOR goal1, goal2, wheel1, wheel2; + ParticuleType parti; + float dist1, dist2, step; + + character = m_object->RetCharacter(); + mat = m_object->RetWorldMatrix(0); + + // Dessine une trace sur le sol. + if ( color >= 0 && color <= 17 ) + { + parti = (ParticuleType)(PARTITRACE0+color); + step = 2.0f; + if ( color >= 16 ) step = 4.0f; // flèche ? + step /= m_engine->RetTracePrecision(); + + goal1.x = step/2.0f; + goal1.y = 0.0f; + goal1.z = -width/2.0f; + goal1 = Transform(*mat, goal1); + + goal2.x = step/2.0f; + goal2.y = 0.0f; + goal2.z = width/2.0f; + goal2 = Transform(*mat, goal2); + + if ( !m_bWheelParticuleBrake ) + { + m_wheelParticulePos[0] = goal1; + m_wheelParticulePos[1] = goal2; + } + + while ( TRUE ) + { + dist1 = Length(m_wheelParticulePos[0], goal1); + if ( dist1 < step ) break; + dist2 = Length(m_wheelParticulePos[1], goal2); + wheel1 = SegmentDist(m_wheelParticulePos[0], goal1, step); + wheel2 = SegmentDist(m_wheelParticulePos[1], goal2, step*dist2/dist1); + if ( m_linMotion.realSpeed.x >= 0.0f ) + { + m_particule->CreateWheelTrace(m_wheelParticulePos[0], m_wheelParticulePos[1], wheel1, wheel2, parti); + } + else + { + m_particule->CreateWheelTrace(m_wheelParticulePos[1], m_wheelParticulePos[0], wheel2, wheel1, parti); + } + m_wheelParticulePos[0] = wheel1; + m_wheelParticulePos[1] = wheel2; + } + + m_bWheelParticuleBrake = TRUE; + } + else + { + m_bWheelParticuleBrake = FALSE; + } +} + + +// Crée l'interface. + +void CPhysics::CreateInterface(BOOL bSelect) +{ + if ( m_brain != 0 ) + { + m_brain->CreateInterface(bSelect); + } +} + + +// Retourne une erreur liée à l'état général. + +Error CPhysics::RetError() +{ + ObjectType type; + CObject* power; + + type = m_object->RetType(); + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH || + type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM || + type == OBJECT_APOLLO2 || + type == OBJECT_MOBILEdr ) return ERR_OK; + + if ( m_brain != 0 && m_brain->RetActiveVirus() ) + { + return ERR_VEH_VIRUS; + } + + power = m_object->RetPower(); // cherche l'objet pile utilisé + if ( power == 0 ) + { + return ERR_VEH_POWER; + } + else + { + if ( power->RetEnergy() == 0.0f ) return ERR_VEH_ENERGY; + } + + return ERR_OK; +} + diff --git a/src/physics.h b/src/physics.h new file mode 100644 index 00000000..3eb83f5d --- /dev/null +++ b/src/physics.h @@ -0,0 +1,228 @@ +// physics.h + +#ifndef _PHYSICS_H_ +#define _PHYSICS_H_ + + +class CInstanceManager; +class CD3DEngine; +class CLight; +class CParticule; +class CTerrain; +class CWater; +class CCamera; +class CObject; +class CBrain; +class CMotion; +class CSound; + + +enum PhysicsType +{ + TYPE_ROLLING = 1, + TYPE_FLYING = 2, +}; + +enum PhysicsMode +{ + MO_ADVACCEL = 0, + MO_RECACCEL = 1, + MO_STOACCEL = 2, + MO_MOTACCEL = 3, + MO_ADVSPEED = 4, + MO_RECSPEED = 5, + MO_MOTSPEED = 6, + MO_CURSPEED = 7, + MO_TERFORCE = 8, + MO_TERSPEED = 9, + MO_TERSLIDE = 10, + MO_REASPEED = 11, +}; + + +typedef struct +{ + D3DVECTOR advanceAccel; // accélération de départ (+) + D3DVECTOR recedeAccel; // accélération de départ (+) + D3DVECTOR stopAccel; // accélération d'arrêt (+) + D3DVECTOR motorAccel; // accélération actuelle (+/-) + + D3DVECTOR advanceSpeed; // vitesse en marche avant (+) + D3DVECTOR recedeSpeed; // vitesse en marche arrière (+) + D3DVECTOR motorSpeed; // vitesse souhaitée (+/-) + D3DVECTOR currentSpeed; // vitesse actuelle (+/-) + + D3DVECTOR terrainForce; // force de résistance du terrain (+) + D3DVECTOR terrainSpeed; // vitesse du terrain (+/-) + D3DVECTOR terrainSlide; // limite vitesse de glissement (+) + + D3DVECTOR realSpeed; // vitesse réelle (+/-) + + D3DVECTOR finalInclin; // inclinaison finale +} +Motion; + + + + +class CPhysics +{ +public: + CPhysics(CInstanceManager* iMan, CObject* object); + ~CPhysics(); + + void DeleteObject(BOOL bAll=FALSE); + + BOOL EventProcess(const Event &event); + + void SetBrain(CBrain* brain); + void SetMotion(CMotion* motion); + + void SetType(PhysicsType type); + PhysicsType RetType(); + + BOOL Write(char *line); + BOOL Read(char *line); + + void SetGravity(float value); + float RetGravity(); + + float RetFloorHeight(); + + void SetLinMotion(PhysicsMode mode, D3DVECTOR value); + D3DVECTOR RetLinMotion(PhysicsMode mode); + void SetLinMotionX(PhysicsMode mode, float value); + void SetLinMotionY(PhysicsMode mode, float value); + void SetLinMotionZ(PhysicsMode mode, float value); + float RetLinMotionX(PhysicsMode mode); + float RetLinMotionY(PhysicsMode mode); + float RetLinMotionZ(PhysicsMode mode); + + void SetCirMotion(PhysicsMode mode, D3DVECTOR value); + D3DVECTOR RetCirMotion(PhysicsMode mode); + void SetCirMotionX(PhysicsMode mode, float value); + void SetCirMotionY(PhysicsMode mode, float value); + void SetCirMotionZ(PhysicsMode mode, float value); + float RetCirMotionX(PhysicsMode mode); + float RetCirMotionY(PhysicsMode mode); + float RetCirMotionZ(PhysicsMode mode); + + float RetLinStopLength(PhysicsMode sMode=MO_ADVSPEED, PhysicsMode aMode=MO_STOACCEL); + float RetCirStopLength(); + float RetLinMaxLength(float dir); + float RetLinTimeLength(float dist, float dir=1.0f); + float RetLinLength(float dist); + + void SetMotor(BOOL bState); + BOOL RetMotor(); + void SetLand(BOOL bState); + BOOL RetLand(); + void SetSwim(BOOL bState); + BOOL RetSwim(); + void SetCollision(BOOL bCollision); + BOOL RetCollision(); + void SetFreeze(BOOL bFreeze); + BOOL RetFreeze(); + void SetReactorRange(float range); + float RetReactorRange(); + + void SetMotorSpeed(D3DVECTOR speed); + void SetMotorSpeedX(float speed); + void SetMotorSpeedY(float speed); + void SetMotorSpeedZ(float speed); + D3DVECTOR RetMotorSpeed(); + float RetMotorSpeedX(); + float RetMotorSpeedY(); + float RetMotorSpeedZ(); + + void CreateInterface(BOOL bSelect); + Error RetError(); + +protected: + BOOL EventFrame(const Event &event); + void WaterFrame(float aTime, float rTime); + void SoundMotor(float rTime); + void SoundMotorFull(float rTime, ObjectType type); + void SoundMotorSlow(float rTime, ObjectType type); + void SoundMotorStop(float rTime, ObjectType type); + void SoundReactorFull(float rTime, ObjectType type); + void SoundReactorStop(float rTime, ObjectType type); + void FrameParticule(float aTime, float rTime); + void MotorUpdate(float aTime, float rTime); + void EffectUpdate(float aTime, float rTime); + void UpdateMotionStruct(float rTime, Motion &motion); + void FloorAdapt(float aTime, float rTime, D3DVECTOR &pos, D3DVECTOR &angle); + void FloorAngle(const D3DVECTOR &pos, D3DVECTOR &angle); + int ObjectAdapt(const D3DVECTOR &pos, const D3DVECTOR &angle); + BOOL JostleObject(CObject* pObj, D3DVECTOR iPos, float iRad, D3DVECTOR oPos, float oRad); + BOOL JostleObject(CObject* pObj, float force); + BOOL ExploOther(ObjectType iType, CObject *pObj, ObjectType oType, float force); + int ExploHimself(ObjectType iType, ObjectType oType, float force); + + void PowerParticule(float factor, BOOL bBreak); + void CrashParticule(float crash); + void MotorParticule(float aTime, float rTime); + void WaterParticule(float aTime, D3DVECTOR pos, ObjectType type, float floor, float advance, float turn); + void WheelParticule(int color, float width); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CLight* m_light; + CParticule* m_particule; + CTerrain* m_terrain; + CWater* m_water; + CCamera* m_camera; + CObject* m_object; + CBrain* m_brain; + CMotion* m_motion; + CSound* m_sound; + + PhysicsType m_type; // TYPE_* + float m_gravity; // force de gravitation + float m_time; // temps absolu + D3DVECTOR m_motorSpeed; // vitesse du moteur (-1..1) + Motion m_linMotion; // mouvement linéaire + Motion m_cirMotion; // mouvement circulaire + BOOL m_bMotor; + BOOL m_bLand; + BOOL m_bSwim; + BOOL m_bCollision; + BOOL m_bObstacle; + BOOL m_bFreeze; + int m_repeatCollision; + float m_linVibrationFactor; + float m_cirVibrationFactor; + float m_inclinaisonFactor; + float m_lastPowerParticule; + float m_lastSlideParticule; + float m_lastMotorParticule; + float m_lastWaterParticule; + float m_lastUnderParticule; + float m_lastPloufParticule; + float m_lastFlameParticule; + BOOL m_bWheelParticuleBrake; + D3DVECTOR m_wheelParticulePos[2]; + float m_absorbWater; + float m_reactorTemperature; + float m_reactorRange; + float m_timeReactorFail; + float m_timeUnderWater; + float m_lastEnergy; + float m_lastSoundWater; + float m_lastSoundInsect; + float m_restBreakParticule; + float m_floorLevel; // niveau du sol + float m_floorHeight; // hauteur au-dessus du sol + int m_soundChannel; + int m_soundChannelSlide; + float m_soundTimePshhh; + float m_soundTimeJostle; + float m_soundTimeBoum; + BOOL m_bSoundSlow; + BOOL m_bForceUpdate; + BOOL m_bLowLevel; +}; + + +#endif //_PHYSICS_H_ diff --git a/src/planet.cpp b/src/planet.cpp new file mode 100644 index 00000000..c479b1ff --- /dev/null +++ b/src/planet.cpp @@ -0,0 +1,232 @@ +// planet.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "planet.h" + + + + +// Constructeur du terrain. + +CPlanet::CPlanet(CInstanceManager* iMan, CD3DEngine* engine) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_PLANET, this); + + m_engine = engine; + Flush(); + +} + +// Destructeur du terrain. + +CPlanet::~CPlanet() +{ +} + + +// Supprime toutes les planètes. + +void CPlanet::Flush() +{ + int i, j; + + for ( j=0 ; j<2 ; j++ ) + { + for ( i=0 ; iRetPause() ) return TRUE; + + m_time += event.rTime; + + for ( i=0 ; iLoadTexture(m_planet[j][i].name); + } + } +} + +// Dessine toutes les planètes. + +void CPlanet::Draw() +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2 vertex[4]; // 2 triangles + D3DVECTOR n; + FPOINT p1, p2; + float eyeDirH, eyeDirV, dp, u1, u2, v1, v2, a; + int i; + + device = m_engine->RetD3DDevice(); + eyeDirH = m_engine->RetEyeDirH(); + eyeDirV = m_engine->RetEyeDirV(); + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + dp = 0.5f/256.0f; + + for ( i=0 ; iSetTexture(m_planet[m_mode][i].name); + + if ( m_planet[m_mode][i].bTGA ) + { + m_engine->SetState(D3DSTATEWRAP|D3DSTATEALPHA); + } + else + { + m_engine->SetState(D3DSTATEWRAP|D3DSTATETTb); + } + + a = eyeDirH + m_planet[m_mode][i].angle.x; + p1.x = Mod(a, PI*2.0f)-0.5f; + + a = eyeDirV + m_planet[m_mode][i].angle.y; + p1.y = 0.4f+(Mod(a+PI, PI*2.0f)-PI)*(2.0f/PI); + + p1.x -= m_planet[m_mode][i].dim/2.0f*0.75f; + p1.y -= m_planet[m_mode][i].dim/2.0f; + p2.x = p1.x+m_planet[m_mode][i].dim*0.75f; + p2.y = p1.y+m_planet[m_mode][i].dim; + + u1 = m_planet[m_mode][i].uv1.x + dp; + v1 = m_planet[m_mode][i].uv1.y + dp; + u2 = m_planet[m_mode][i].uv2.x - dp; + v2 = m_planet[m_mode][i].uv2.y - dp; + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, u1,v2); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, u1,v1); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, u2,v2); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, u2,v1); + + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + } +} + + +// Crée une nouvelle planète. + +BOOL CPlanet::Create(int mode, FPOINT start, float dim, float speed, + float dir, char *name, FPOINT uv1, FPOINT uv2) +{ + int i; + + if ( mode < 0 ) mode = 0; + if ( mode > 1 ) mode = 1; + + for ( i=0 ; i 1 ) mode = 1; + m_mode = mode; +} + +int CPlanet::RetMode() +{ + return m_mode; +} + diff --git a/src/planet.h b/src/planet.h new file mode 100644 index 00000000..263926fb --- /dev/null +++ b/src/planet.h @@ -0,0 +1,60 @@ +// planet.h + +#ifndef _PLANET_H_ +#define _PLANET_H_ + + +class CInstanceManager; +class CD3DEngine; + + + +#define MAXPLANET 10 + +typedef struct +{ + char bUsed; // TRUE -> planète existe + FPOINT start; // position initiale en degrés + FPOINT angle; // position actuelle en degrés + float dim; // dimensions (0..1) + float speed; // vitesse + float dir; // direction dans le ciel + char name[20]; // nom de la texture + FPOINT uv1, uv2; // mapping de la texture + char bTGA; // texture .TGA +} +Planet; + + + + +class CPlanet +{ +public: + CPlanet(CInstanceManager* iMan, CD3DEngine* engine); + ~CPlanet(); + + void Flush(); + BOOL EventProcess(const Event &event); + BOOL Create(int mode, FPOINT start, float dim, float speed, float dir, char *name, FPOINT uv1, FPOINT uv2); + BOOL PlanetExist(); + void LoadTexture(); + void Draw(); + void SetMode(int mode); + int RetMode(); + +protected: + BOOL EventFrame(const Event &event); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + + float m_time; + int m_mode; + Planet m_planet[2][MAXPLANET]; + BOOL m_bPlanetExist; +}; + + +#endif //_PLANET_H_ diff --git a/src/profile.cpp b/src/profile.cpp new file mode 100644 index 00000000..b869bc2e --- /dev/null +++ b/src/profile.cpp @@ -0,0 +1,100 @@ +// profile.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "language.h" +#include "struct.h" +#include "profile.h" + + + +static char g_filename[100]; + + + +BOOL InitCurrentDirectory() +{ +#if _SCHOOL + _fullpath(g_filename, "ceebot.ini", 100); +#else + _fullpath(g_filename, "colobot.ini", 100); +#endif + return TRUE; +} + + +BOOL SetProfileString(char* section, char* key, char* string) +{ + WritePrivateProfileString(section, key, string, g_filename); + return TRUE; +} + +BOOL GetProfileString(char* section, char* key, char* buffer, int max) +{ + int nb; + + nb = GetPrivateProfileString(section, key, "", buffer, max, g_filename); + if ( nb == 0 ) + { + buffer[0] = 0; + return FALSE; + } + return TRUE; +} + + +BOOL SetProfileInt(char* section, char* key, int value) +{ + char s[20]; + + sprintf(s, "%d", value); + WritePrivateProfileString(section, key, s, g_filename); + return TRUE; +} + +BOOL GetProfileInt(char* section, char* key, int &value) +{ + char s[20]; + int nb; + + nb = GetPrivateProfileString(section, key, "", s, 20, g_filename); + if ( nb == 0 ) + { + value = 0; + return FALSE; + } + sscanf(s, "%d", &value); + return TRUE; +} + + +BOOL SetProfileFloat(char* section, char* key, float value) +{ + char s[20]; + + sprintf(s, "%.2f", value); + WritePrivateProfileString(section, key, s, g_filename); + return TRUE; +} + +BOOL GetProfileFloat(char* section, char* key, float &value) +{ + char s[20]; + int nb; + + nb = GetPrivateProfileString(section, key, "", s, 20, g_filename); + if ( nb == 0 ) + { + value = 0.0f; + return FALSE; + } + sscanf(s, "%f", &value); + return TRUE; +} + + diff --git a/src/profile.h b/src/profile.h new file mode 100644 index 00000000..e328ce1d --- /dev/null +++ b/src/profile.h @@ -0,0 +1,20 @@ +// profile.h + +#ifndef _PROFILE_H_ +#define _PROFILE_H_ + + +#define STRICT +#define D3D_OVERLOADS + + +extern BOOL InitCurrentDirectory(); +extern BOOL SetProfileString(char* section, char* key, char* string); +extern BOOL GetProfileString(char* section, char* key, char* buffer, int max); +extern BOOL SetProfileInt(char* section, char* key, int value); +extern BOOL GetProfileInt(char* section, char* key, int &value); +extern BOOL SetProfileFloat(char* section, char* key, float value); +extern BOOL GetProfileFloat(char* section, char* key, float &value); + + +#endif //_PROFILE_H_ diff --git a/src/projet1.dsp b/src/projet1.dsp new file mode 100644 index 00000000..a72113c1 --- /dev/null +++ b/src/projet1.dsp @@ -0,0 +1,608 @@ +# Microsoft Developer Studio Project File - Name="projet1" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=projet1 - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "projet1.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "projet1.mak" CFG="projet1 - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "projet1 - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "projet1 - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "projet1 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /Zi /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# SUBTRACT CPP /Fr +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x100c /d "NDEBUG" +# ADD RSC /l 0x100c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib c:\dx7sdk\lib\ddraw.lib c:\dx7sdk\lib\dinput.lib c:\dx7sdk\lib\dxguid.lib c:\dx7sdk\lib\d3dx.lib c:\dx7sdk\lib\dsound.lib cbot\cbot.lib /nologo /subsystem:windows /machine:I386 +# SUBTRACT LINK32 /debug + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x100c /d "_DEBUG" +# ADD RSC /l 0x100c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib c:\dx7sdk\lib\ddraw.lib c:\dx7sdk\lib\dinput.lib c:\dx7sdk\lib\dxguid.lib c:\dx7sdk\lib\d3dx.lib c:\dx7sdk\lib\dsound.lib cbot\cbot.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /map + +!ENDIF + +# Begin Target + +# Name "projet1 - Win32 Release" +# Name "projet1 - Win32 Debug" +# Begin Source File + +SOURCE=.\auto.cpp +# End Source File +# Begin Source File + +SOURCE=.\autobase.cpp +# End Source File +# Begin Source File + +SOURCE=.\autoconvert.cpp +# End Source File +# Begin Source File + +SOURCE=.\autoderrick.cpp +# End Source File +# Begin Source File + +SOURCE=.\autodestroyer.cpp +# End Source File +# Begin Source File + +SOURCE=.\autoegg.cpp +# End Source File +# Begin Source File + +SOURCE=.\autoenergy.cpp +# End Source File +# Begin Source File + +SOURCE=.\autofactory.cpp +# End Source File +# Begin Source File + +SOURCE=.\autoflag.cpp +# End Source File +# Begin Source File + +SOURCE=.\autohuston.cpp +# End Source File +# Begin Source File + +SOURCE=.\autoinfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\autojostle.cpp +# End Source File +# Begin Source File + +SOURCE=.\autokid.cpp +# End Source File +# Begin Source File + +SOURCE=.\autolabo.cpp +# End Source File +# Begin Source File + +SOURCE=.\automush.cpp +# End Source File +# Begin Source File + +SOURCE=.\autonest.cpp +# End Source File +# Begin Source File + +SOURCE=.\autonuclear.cpp +# End Source File +# Begin Source File + +SOURCE=.\autopara.cpp +# End Source File +# Begin Source File + +SOURCE=.\autoportico.cpp +# End Source File +# Begin Source File + +SOURCE=.\autoradar.cpp +# End Source File +# Begin Source File + +SOURCE=.\autorepair.cpp +# End Source File +# Begin Source File + +SOURCE=.\autoresearch.cpp +# End Source File +# Begin Source File + +SOURCE=.\autoroot.cpp +# End Source File +# Begin Source File + +SOURCE=.\autosafe.cpp +# End Source File +# Begin Source File + +SOURCE=.\autostation.cpp +# End Source File +# Begin Source File + +SOURCE=.\autotower.cpp +# End Source File +# Begin Source File + +SOURCE=.\blitz.cpp +# End Source File +# Begin Source File + +SOURCE=.\brain.cpp +# End Source File +# Begin Source File + +SOURCE=.\button.cpp +# End Source File +# Begin Source File + +SOURCE=.\camera.cpp +# End Source File +# Begin Source File + +SOURCE=.\cbottoken.cpp +# End Source File +# Begin Source File + +SOURCE=.\check.cpp +# End Source File +# Begin Source File + +SOURCE=.\cloud.cpp +# End Source File +# Begin Source File + +SOURCE=.\cmdtoken.cpp +# End Source File +# Begin Source File + +SOURCE=.\color.cpp +# End Source File +# Begin Source File + +SOURCE=.\compass.cpp +# End Source File +# Begin Source File + +SOURCE=.\control.cpp +# End Source File +# Begin Source File + +SOURCE=.\cur00001.cur +# End Source File +# Begin Source File + +SOURCE=.\cur00002.cur +# End Source File +# Begin Source File + +SOURCE=.\cur00003.cur +# End Source File +# Begin Source File + +SOURCE=.\cursor1.cur +# End Source File +# Begin Source File + +SOURCE=.\cursorha.cur +# End Source File +# Begin Source File + +SOURCE=.\cursorsc.cur +# End Source File +# Begin Source File + +SOURCE=.\d3dapp.cpp +# End Source File +# Begin Source File + +SOURCE=.\d3dengine.cpp +# End Source File +# Begin Source File + +SOURCE=.\d3denum.cpp +# End Source File +# Begin Source File + +SOURCE=.\d3dframe.cpp +# End Source File +# Begin Source File + +SOURCE=.\d3dmath.cpp +# End Source File +# Begin Source File + +SOURCE=.\d3dtextr.cpp +# End Source File +# Begin Source File + +SOURCE=.\d3dutil.cpp +# End Source File +# Begin Source File + +SOURCE=.\directx.ico +# End Source File +# Begin Source File + +SOURCE=.\displayinfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\displaytext.cpp +# End Source File +# Begin Source File + +SOURCE=.\edit.cpp +# End Source File +# Begin Source File + +SOURCE=.\editvalue.cpp +# End Source File +# Begin Source File + +SOURCE=.\event.cpp +# End Source File +# Begin Source File + +SOURCE=.\gauge.cpp +# End Source File +# Begin Source File + +SOURCE=.\group.cpp +# End Source File +# Begin Source File + +SOURCE=.\image.cpp +# End Source File +# Begin Source File + +SOURCE=.\iman.cpp +# End Source File +# Begin Source File + +SOURCE=.\interface.cpp +# End Source File +# Begin Source File + +SOURCE=.\joystick.cpp +# End Source File +# Begin Source File + +SOURCE=.\key.cpp +# End Source File +# Begin Source File + +SOURCE=.\label.cpp +# End Source File +# Begin Source File + +SOURCE=.\language.h +# End Source File +# Begin Source File + +SOURCE=.\light.cpp +# End Source File +# Begin Source File + +SOURCE=.\list.cpp +# End Source File +# Begin Source File + +SOURCE=.\maindialog.cpp +# End Source File +# Begin Source File + +SOURCE=.\mainmap.cpp +# End Source File +# Begin Source File + +SOURCE=.\mainmovie.cpp +# End Source File +# Begin Source File + +SOURCE=.\mainshort.cpp +# End Source File +# Begin Source File + +SOURCE=.\map.cpp +# End Source File +# Begin Source File + +SOURCE=.\math3d.cpp +# End Source File +# Begin Source File + +SOURCE=.\metafile.cpp +# End Source File +# Begin Source File + +SOURCE=.\misc.cpp +# End Source File +# Begin Source File + +SOURCE=.\model.cpp +# End Source File +# Begin Source File + +SOURCE=.\modfile.cpp +# End Source File +# Begin Source File + +SOURCE=.\motion.cpp +# End Source File +# Begin Source File + +SOURCE=.\motionant.cpp +# End Source File +# Begin Source File + +SOURCE=.\motionbee.cpp +# End Source File +# Begin Source File + +SOURCE=.\motionhuman.cpp +# End Source File +# Begin Source File + +SOURCE=.\motionmother.cpp +# End Source File +# Begin Source File + +SOURCE=.\motionspider.cpp +# End Source File +# Begin Source File + +SOURCE=.\motiontoto.cpp +# End Source File +# Begin Source File + +SOURCE=.\motionvehicle.cpp +# End Source File +# Begin Source File + +SOURCE=.\motionworm.cpp +# End Source File +# Begin Source File + +SOURCE=.\object.cpp +# End Source File +# Begin Source File + +SOURCE=.\particule.cpp +# End Source File +# Begin Source File + +SOURCE=.\physics.cpp +# End Source File +# Begin Source File + +SOURCE=.\planet.cpp +# End Source File +# Begin Source File + +SOURCE=.\profile.cpp +# End Source File +# Begin Source File + +SOURCE=.\pyro.cpp +# End Source File +# Begin Source File + +SOURCE=.\restext.cpp +# End Source File +# Begin Source File + +SOURCE=.\robotmain.cpp +# End Source File +# Begin Source File + +SOURCE=.\script.cpp +# End Source File +# Begin Source File + +SOURCE=.\scroll.cpp +# End Source File +# Begin Source File + +SOURCE=.\shortcut.cpp +# End Source File +# Begin Source File + +SOURCE=.\slider.cpp +# End Source File +# Begin Source File + +SOURCE=.\sound.cpp +# End Source File +# Begin Source File + +SOURCE=.\studio.cpp +# End Source File +# Begin Source File + +SOURCE=.\target.cpp +# End Source File +# Begin Source File + +SOURCE=.\task.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskadvance.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskbuild.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskfire.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskfireant.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskflag.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskgoto.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskgungoal.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskinfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskmanager.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskmanip.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskpen.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskrecover.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskreset.cpp +# End Source File +# Begin Source File + +SOURCE=.\tasksearch.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskshield.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskspiderexplo.cpp +# End Source File +# Begin Source File + +SOURCE=.\tasktake.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskterraform.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskturn.cpp +# End Source File +# Begin Source File + +SOURCE=.\taskwait.cpp +# End Source File +# Begin Source File + +SOURCE=.\terrain.cpp +# End Source File +# Begin Source File + +SOURCE=.\text.cpp +# End Source File +# Begin Source File + +SOURCE=.\water.cpp +# End Source File +# Begin Source File + +SOURCE=.\window.cpp +# End Source File +# Begin Source File + +SOURCE=.\winmain.rc + +!IF "$(CFG)" == "projet1 - Win32 Release" + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/src/projet1.dsw b/src/projet1.dsw new file mode 100644 index 00000000..dec64822 --- /dev/null +++ b/src/projet1.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 5.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "projet1"=.\projet1.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/src/projet1.mak b/src/projet1.mak new file mode 100644 index 00000000..619bb49f --- /dev/null +++ b/src/projet1.mak @@ -0,0 +1,8659 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on projet1.dsp +!IF "$(CFG)" == "" +CFG=projet1 - Win32 Debug +!MESSAGE No configuration specified. Defaulting to projet1 - Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "projet1 - Win32 Release" && "$(CFG)" !=\ + "projet1 - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "projet1.mak" CFG="projet1 - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "projet1 - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "projet1 - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "projet1 - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\projet1.exe" + +!ELSE + +ALL : "$(OUTDIR)\projet1.exe" + +!ENDIF + +CLEAN : + -@erase "$(INTDIR)\auto.obj" + -@erase "$(INTDIR)\autobase.obj" + -@erase "$(INTDIR)\autoconvert.obj" + -@erase "$(INTDIR)\autoderrick.obj" + -@erase "$(INTDIR)\autodestroyer.obj" + -@erase "$(INTDIR)\autoegg.obj" + -@erase "$(INTDIR)\autoenergy.obj" + -@erase "$(INTDIR)\autofactory.obj" + -@erase "$(INTDIR)\autoflag.obj" + -@erase "$(INTDIR)\autohuston.obj" + -@erase "$(INTDIR)\autoinfo.obj" + -@erase "$(INTDIR)\autojostle.obj" + -@erase "$(INTDIR)\autokid.obj" + -@erase "$(INTDIR)\autolabo.obj" + -@erase "$(INTDIR)\automush.obj" + -@erase "$(INTDIR)\autonest.obj" + -@erase "$(INTDIR)\autonuclear.obj" + -@erase "$(INTDIR)\autopara.obj" + -@erase "$(INTDIR)\autoportico.obj" + -@erase "$(INTDIR)\autoradar.obj" + -@erase "$(INTDIR)\autorepair.obj" + -@erase "$(INTDIR)\autoresearch.obj" + -@erase "$(INTDIR)\autoroot.obj" + -@erase "$(INTDIR)\autosafe.obj" + -@erase "$(INTDIR)\autostation.obj" + -@erase "$(INTDIR)\autotower.obj" + -@erase "$(INTDIR)\blitz.obj" + -@erase "$(INTDIR)\brain.obj" + -@erase "$(INTDIR)\button.obj" + -@erase "$(INTDIR)\camera.obj" + -@erase "$(INTDIR)\cbottoken.obj" + -@erase "$(INTDIR)\check.obj" + -@erase "$(INTDIR)\cloud.obj" + -@erase "$(INTDIR)\cmdtoken.obj" + -@erase "$(INTDIR)\color.obj" + -@erase "$(INTDIR)\compass.obj" + -@erase "$(INTDIR)\control.obj" + -@erase "$(INTDIR)\d3dapp.obj" + -@erase "$(INTDIR)\d3dengine.obj" + -@erase "$(INTDIR)\d3denum.obj" + -@erase "$(INTDIR)\d3dframe.obj" + -@erase "$(INTDIR)\d3dmath.obj" + -@erase "$(INTDIR)\d3dtextr.obj" + -@erase "$(INTDIR)\d3dutil.obj" + -@erase "$(INTDIR)\displayinfo.obj" + -@erase "$(INTDIR)\displaytext.obj" + -@erase "$(INTDIR)\edit.obj" + -@erase "$(INTDIR)\editvalue.obj" + -@erase "$(INTDIR)\event.obj" + -@erase "$(INTDIR)\gauge.obj" + -@erase "$(INTDIR)\group.obj" + -@erase "$(INTDIR)\image.obj" + -@erase "$(INTDIR)\iman.obj" + -@erase "$(INTDIR)\interface.obj" + -@erase "$(INTDIR)\joystick.obj" + -@erase "$(INTDIR)\key.obj" + -@erase "$(INTDIR)\label.obj" + -@erase "$(INTDIR)\light.obj" + -@erase "$(INTDIR)\list.obj" + -@erase "$(INTDIR)\maindialog.obj" + -@erase "$(INTDIR)\mainmap.obj" + -@erase "$(INTDIR)\mainmovie.obj" + -@erase "$(INTDIR)\mainshort.obj" + -@erase "$(INTDIR)\map.obj" + -@erase "$(INTDIR)\math3d.obj" + -@erase "$(INTDIR)\metafile.obj" + -@erase "$(INTDIR)\misc.obj" + -@erase "$(INTDIR)\model.obj" + -@erase "$(INTDIR)\modfile.obj" + -@erase "$(INTDIR)\motion.obj" + -@erase "$(INTDIR)\motionant.obj" + -@erase "$(INTDIR)\motionbee.obj" + -@erase "$(INTDIR)\motionhuman.obj" + -@erase "$(INTDIR)\motionmother.obj" + -@erase "$(INTDIR)\motionspider.obj" + -@erase "$(INTDIR)\motiontoto.obj" + -@erase "$(INTDIR)\motionvehicle.obj" + -@erase "$(INTDIR)\motionworm.obj" + -@erase "$(INTDIR)\object.obj" + -@erase "$(INTDIR)\particule.obj" + -@erase "$(INTDIR)\physics.obj" + -@erase "$(INTDIR)\planet.obj" + -@erase "$(INTDIR)\profile.obj" + -@erase "$(INTDIR)\pyro.obj" + -@erase "$(INTDIR)\restext.obj" + -@erase "$(INTDIR)\robotmain.obj" + -@erase "$(INTDIR)\script.obj" + -@erase "$(INTDIR)\scroll.obj" + -@erase "$(INTDIR)\shortcut.obj" + -@erase "$(INTDIR)\slider.obj" + -@erase "$(INTDIR)\sound.obj" + -@erase "$(INTDIR)\studio.obj" + -@erase "$(INTDIR)\target.obj" + -@erase "$(INTDIR)\task.obj" + -@erase "$(INTDIR)\taskadvance.obj" + -@erase "$(INTDIR)\taskbuild.obj" + -@erase "$(INTDIR)\taskfire.obj" + -@erase "$(INTDIR)\taskfireant.obj" + -@erase "$(INTDIR)\taskflag.obj" + -@erase "$(INTDIR)\taskgoto.obj" + -@erase "$(INTDIR)\taskgungoal.obj" + -@erase "$(INTDIR)\taskinfo.obj" + -@erase "$(INTDIR)\taskmanager.obj" + -@erase "$(INTDIR)\taskmanip.obj" + -@erase "$(INTDIR)\taskpen.obj" + -@erase "$(INTDIR)\taskrecover.obj" + -@erase "$(INTDIR)\taskreset.obj" + -@erase "$(INTDIR)\tasksearch.obj" + -@erase "$(INTDIR)\taskshield.obj" + -@erase "$(INTDIR)\taskspiderexplo.obj" + -@erase "$(INTDIR)\tasktake.obj" + -@erase "$(INTDIR)\taskterraform.obj" + -@erase "$(INTDIR)\taskturn.obj" + -@erase "$(INTDIR)\taskwait.obj" + -@erase "$(INTDIR)\terrain.obj" + -@erase "$(INTDIR)\text.obj" + -@erase "$(INTDIR)\vc50.idb" + -@erase "$(INTDIR)\vc50.pdb" + -@erase "$(INTDIR)\water.obj" + -@erase "$(INTDIR)\window.obj" + -@erase "$(INTDIR)\winmain.res" + -@erase "$(OUTDIR)\projet1.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /ML /W3 /GX /Zi /D "WIN32" /D "NDEBUG" /D "_WINDOWS"\ + /Fp"$(INTDIR)\projet1.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c +CPP_OBJS=.\Release/ +CPP_SBRS=. + +.c{$(CPP_OBJS)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(CPP_OBJS)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(CPP_OBJS)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(CPP_SBRS)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(CPP_SBRS)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(CPP_SBRS)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +RSC=rc.exe +RSC_PROJ=/l 0x100c /fo"$(INTDIR)\winmain.res" /d "NDEBUG" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\projet1.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib winmm.lib c:\dx7sdk\lib\ddraw.lib c:\dx7sdk\lib\dinput.lib\ + c:\dx7sdk\lib\dxguid.lib c:\dx7sdk\lib\d3dx.lib c:\dx7sdk\lib\dsound.lib\ + cbot\cbot.lib /nologo /subsystem:windows /incremental:no\ + /pdb:"$(OUTDIR)\projet1.pdb" /machine:I386 /out:"$(OUTDIR)\projet1.exe" +LINK32_OBJS= \ + "$(INTDIR)\auto.obj" \ + "$(INTDIR)\autobase.obj" \ + "$(INTDIR)\autoconvert.obj" \ + "$(INTDIR)\autoderrick.obj" \ + "$(INTDIR)\autodestroyer.obj" \ + "$(INTDIR)\autoegg.obj" \ + "$(INTDIR)\autoenergy.obj" \ + "$(INTDIR)\autofactory.obj" \ + "$(INTDIR)\autoflag.obj" \ + "$(INTDIR)\autohuston.obj" \ + "$(INTDIR)\autoinfo.obj" \ + "$(INTDIR)\autojostle.obj" \ + "$(INTDIR)\autokid.obj" \ + "$(INTDIR)\autolabo.obj" \ + "$(INTDIR)\automush.obj" \ + "$(INTDIR)\autonest.obj" \ + "$(INTDIR)\autonuclear.obj" \ + "$(INTDIR)\autopara.obj" \ + "$(INTDIR)\autoportico.obj" \ + "$(INTDIR)\autoradar.obj" \ + "$(INTDIR)\autorepair.obj" \ + "$(INTDIR)\autoresearch.obj" \ + "$(INTDIR)\autoroot.obj" \ + "$(INTDIR)\autosafe.obj" \ + "$(INTDIR)\autostation.obj" \ + "$(INTDIR)\autotower.obj" \ + "$(INTDIR)\blitz.obj" \ + "$(INTDIR)\brain.obj" \ + "$(INTDIR)\button.obj" \ + "$(INTDIR)\camera.obj" \ + "$(INTDIR)\cbottoken.obj" \ + "$(INTDIR)\check.obj" \ + "$(INTDIR)\cloud.obj" \ + "$(INTDIR)\cmdtoken.obj" \ + "$(INTDIR)\color.obj" \ + "$(INTDIR)\compass.obj" \ + "$(INTDIR)\control.obj" \ + "$(INTDIR)\d3dapp.obj" \ + "$(INTDIR)\d3dengine.obj" \ + "$(INTDIR)\d3denum.obj" \ + "$(INTDIR)\d3dframe.obj" \ + "$(INTDIR)\d3dmath.obj" \ + "$(INTDIR)\d3dtextr.obj" \ + "$(INTDIR)\d3dutil.obj" \ + "$(INTDIR)\displayinfo.obj" \ + "$(INTDIR)\displaytext.obj" \ + "$(INTDIR)\edit.obj" \ + "$(INTDIR)\editvalue.obj" \ + "$(INTDIR)\event.obj" \ + "$(INTDIR)\gauge.obj" \ + "$(INTDIR)\group.obj" \ + "$(INTDIR)\image.obj" \ + "$(INTDIR)\iman.obj" \ + "$(INTDIR)\interface.obj" \ + "$(INTDIR)\joystick.obj" \ + "$(INTDIR)\key.obj" \ + "$(INTDIR)\label.obj" \ + "$(INTDIR)\light.obj" \ + "$(INTDIR)\list.obj" \ + "$(INTDIR)\maindialog.obj" \ + "$(INTDIR)\mainmap.obj" \ + "$(INTDIR)\mainmovie.obj" \ + "$(INTDIR)\mainshort.obj" \ + "$(INTDIR)\map.obj" \ + "$(INTDIR)\math3d.obj" \ + "$(INTDIR)\metafile.obj" \ + "$(INTDIR)\misc.obj" \ + "$(INTDIR)\model.obj" \ + "$(INTDIR)\modfile.obj" \ + "$(INTDIR)\motion.obj" \ + "$(INTDIR)\motionant.obj" \ + "$(INTDIR)\motionbee.obj" \ + "$(INTDIR)\motionhuman.obj" \ + "$(INTDIR)\motionmother.obj" \ + "$(INTDIR)\motionspider.obj" \ + "$(INTDIR)\motiontoto.obj" \ + "$(INTDIR)\motionvehicle.obj" \ + "$(INTDIR)\motionworm.obj" \ + "$(INTDIR)\object.obj" \ + "$(INTDIR)\particule.obj" \ + "$(INTDIR)\physics.obj" \ + "$(INTDIR)\planet.obj" \ + "$(INTDIR)\profile.obj" \ + "$(INTDIR)\pyro.obj" \ + "$(INTDIR)\restext.obj" \ + "$(INTDIR)\robotmain.obj" \ + "$(INTDIR)\script.obj" \ + "$(INTDIR)\scroll.obj" \ + "$(INTDIR)\shortcut.obj" \ + "$(INTDIR)\slider.obj" \ + "$(INTDIR)\sound.obj" \ + "$(INTDIR)\studio.obj" \ + "$(INTDIR)\target.obj" \ + "$(INTDIR)\task.obj" \ + "$(INTDIR)\taskadvance.obj" \ + "$(INTDIR)\taskbuild.obj" \ + "$(INTDIR)\taskfire.obj" \ + "$(INTDIR)\taskfireant.obj" \ + "$(INTDIR)\taskflag.obj" \ + "$(INTDIR)\taskgoto.obj" \ + "$(INTDIR)\taskgungoal.obj" \ + "$(INTDIR)\taskinfo.obj" \ + "$(INTDIR)\taskmanager.obj" \ + "$(INTDIR)\taskmanip.obj" \ + "$(INTDIR)\taskpen.obj" \ + "$(INTDIR)\taskrecover.obj" \ + "$(INTDIR)\taskreset.obj" \ + "$(INTDIR)\tasksearch.obj" \ + "$(INTDIR)\taskshield.obj" \ + "$(INTDIR)\taskspiderexplo.obj" \ + "$(INTDIR)\tasktake.obj" \ + "$(INTDIR)\taskterraform.obj" \ + "$(INTDIR)\taskturn.obj" \ + "$(INTDIR)\taskwait.obj" \ + "$(INTDIR)\terrain.obj" \ + "$(INTDIR)\text.obj" \ + "$(INTDIR)\water.obj" \ + "$(INTDIR)\window.obj" \ + "$(INTDIR)\winmain.res" + +"$(OUTDIR)\projet1.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\projet1.exe" "$(OUTDIR)\projet1.bsc" + +!ELSE + +ALL : "$(OUTDIR)\projet1.exe" "$(OUTDIR)\projet1.bsc" + +!ENDIF + +CLEAN : + -@erase "$(INTDIR)\auto.obj" + -@erase "$(INTDIR)\auto.sbr" + -@erase "$(INTDIR)\autobase.obj" + -@erase "$(INTDIR)\autobase.sbr" + -@erase "$(INTDIR)\autoconvert.obj" + -@erase "$(INTDIR)\autoconvert.sbr" + -@erase "$(INTDIR)\autoderrick.obj" + -@erase "$(INTDIR)\autoderrick.sbr" + -@erase "$(INTDIR)\autodestroyer.obj" + -@erase "$(INTDIR)\autodestroyer.sbr" + -@erase "$(INTDIR)\autoegg.obj" + -@erase "$(INTDIR)\autoegg.sbr" + -@erase "$(INTDIR)\autoenergy.obj" + -@erase "$(INTDIR)\autoenergy.sbr" + -@erase "$(INTDIR)\autofactory.obj" + -@erase "$(INTDIR)\autofactory.sbr" + -@erase "$(INTDIR)\autoflag.obj" + -@erase "$(INTDIR)\autoflag.sbr" + -@erase "$(INTDIR)\autohuston.obj" + -@erase "$(INTDIR)\autohuston.sbr" + -@erase "$(INTDIR)\autoinfo.obj" + -@erase "$(INTDIR)\autoinfo.sbr" + -@erase "$(INTDIR)\autojostle.obj" + -@erase "$(INTDIR)\autojostle.sbr" + -@erase "$(INTDIR)\autokid.obj" + -@erase "$(INTDIR)\autokid.sbr" + -@erase "$(INTDIR)\autolabo.obj" + -@erase "$(INTDIR)\autolabo.sbr" + -@erase "$(INTDIR)\automush.obj" + -@erase "$(INTDIR)\automush.sbr" + -@erase "$(INTDIR)\autonest.obj" + -@erase "$(INTDIR)\autonest.sbr" + -@erase "$(INTDIR)\autonuclear.obj" + -@erase "$(INTDIR)\autonuclear.sbr" + -@erase "$(INTDIR)\autopara.obj" + -@erase "$(INTDIR)\autopara.sbr" + -@erase "$(INTDIR)\autoportico.obj" + -@erase "$(INTDIR)\autoportico.sbr" + -@erase "$(INTDIR)\autoradar.obj" + -@erase "$(INTDIR)\autoradar.sbr" + -@erase "$(INTDIR)\autorepair.obj" + -@erase "$(INTDIR)\autorepair.sbr" + -@erase "$(INTDIR)\autoresearch.obj" + -@erase "$(INTDIR)\autoresearch.sbr" + -@erase "$(INTDIR)\autoroot.obj" + -@erase "$(INTDIR)\autoroot.sbr" + -@erase "$(INTDIR)\autosafe.obj" + -@erase "$(INTDIR)\autosafe.sbr" + -@erase "$(INTDIR)\autostation.obj" + -@erase "$(INTDIR)\autostation.sbr" + -@erase "$(INTDIR)\autotower.obj" + -@erase "$(INTDIR)\autotower.sbr" + -@erase "$(INTDIR)\blitz.obj" + -@erase "$(INTDIR)\blitz.sbr" + -@erase "$(INTDIR)\brain.obj" + -@erase "$(INTDIR)\brain.sbr" + -@erase "$(INTDIR)\button.obj" + -@erase "$(INTDIR)\button.sbr" + -@erase "$(INTDIR)\camera.obj" + -@erase "$(INTDIR)\camera.sbr" + -@erase "$(INTDIR)\cbottoken.obj" + -@erase "$(INTDIR)\cbottoken.sbr" + -@erase "$(INTDIR)\check.obj" + -@erase "$(INTDIR)\check.sbr" + -@erase "$(INTDIR)\cloud.obj" + -@erase "$(INTDIR)\cloud.sbr" + -@erase "$(INTDIR)\cmdtoken.obj" + -@erase "$(INTDIR)\cmdtoken.sbr" + -@erase "$(INTDIR)\color.obj" + -@erase "$(INTDIR)\color.sbr" + -@erase "$(INTDIR)\compass.obj" + -@erase "$(INTDIR)\compass.sbr" + -@erase "$(INTDIR)\control.obj" + -@erase "$(INTDIR)\control.sbr" + -@erase "$(INTDIR)\d3dapp.obj" + -@erase "$(INTDIR)\d3dapp.sbr" + -@erase "$(INTDIR)\d3dengine.obj" + -@erase "$(INTDIR)\d3dengine.sbr" + -@erase "$(INTDIR)\d3denum.obj" + -@erase "$(INTDIR)\d3denum.sbr" + -@erase "$(INTDIR)\d3dframe.obj" + -@erase "$(INTDIR)\d3dframe.sbr" + -@erase "$(INTDIR)\d3dmath.obj" + -@erase "$(INTDIR)\d3dmath.sbr" + -@erase "$(INTDIR)\d3dtextr.obj" + -@erase "$(INTDIR)\d3dtextr.sbr" + -@erase "$(INTDIR)\d3dutil.obj" + -@erase "$(INTDIR)\d3dutil.sbr" + -@erase "$(INTDIR)\displayinfo.obj" + -@erase "$(INTDIR)\displayinfo.sbr" + -@erase "$(INTDIR)\displaytext.obj" + -@erase "$(INTDIR)\displaytext.sbr" + -@erase "$(INTDIR)\edit.obj" + -@erase "$(INTDIR)\edit.sbr" + -@erase "$(INTDIR)\editvalue.obj" + -@erase "$(INTDIR)\editvalue.sbr" + -@erase "$(INTDIR)\event.obj" + -@erase "$(INTDIR)\event.sbr" + -@erase "$(INTDIR)\gauge.obj" + -@erase "$(INTDIR)\gauge.sbr" + -@erase "$(INTDIR)\group.obj" + -@erase "$(INTDIR)\group.sbr" + -@erase "$(INTDIR)\image.obj" + -@erase "$(INTDIR)\image.sbr" + -@erase "$(INTDIR)\iman.obj" + -@erase "$(INTDIR)\iman.sbr" + -@erase "$(INTDIR)\interface.obj" + -@erase "$(INTDIR)\interface.sbr" + -@erase "$(INTDIR)\joystick.obj" + -@erase "$(INTDIR)\joystick.sbr" + -@erase "$(INTDIR)\key.obj" + -@erase "$(INTDIR)\key.sbr" + -@erase "$(INTDIR)\label.obj" + -@erase "$(INTDIR)\label.sbr" + -@erase "$(INTDIR)\light.obj" + -@erase "$(INTDIR)\light.sbr" + -@erase "$(INTDIR)\list.obj" + -@erase "$(INTDIR)\list.sbr" + -@erase "$(INTDIR)\maindialog.obj" + -@erase "$(INTDIR)\maindialog.sbr" + -@erase "$(INTDIR)\mainmap.obj" + -@erase "$(INTDIR)\mainmap.sbr" + -@erase "$(INTDIR)\mainmovie.obj" + -@erase "$(INTDIR)\mainmovie.sbr" + -@erase "$(INTDIR)\mainshort.obj" + -@erase "$(INTDIR)\mainshort.sbr" + -@erase "$(INTDIR)\map.obj" + -@erase "$(INTDIR)\map.sbr" + -@erase "$(INTDIR)\math3d.obj" + -@erase "$(INTDIR)\math3d.sbr" + -@erase "$(INTDIR)\metafile.obj" + -@erase "$(INTDIR)\metafile.sbr" + -@erase "$(INTDIR)\misc.obj" + -@erase "$(INTDIR)\misc.sbr" + -@erase "$(INTDIR)\model.obj" + -@erase "$(INTDIR)\model.sbr" + -@erase "$(INTDIR)\modfile.obj" + -@erase "$(INTDIR)\modfile.sbr" + -@erase "$(INTDIR)\motion.obj" + -@erase "$(INTDIR)\motion.sbr" + -@erase "$(INTDIR)\motionant.obj" + -@erase "$(INTDIR)\motionant.sbr" + -@erase "$(INTDIR)\motionbee.obj" + -@erase "$(INTDIR)\motionbee.sbr" + -@erase "$(INTDIR)\motionhuman.obj" + -@erase "$(INTDIR)\motionhuman.sbr" + -@erase "$(INTDIR)\motionmother.obj" + -@erase "$(INTDIR)\motionmother.sbr" + -@erase "$(INTDIR)\motionspider.obj" + -@erase "$(INTDIR)\motionspider.sbr" + -@erase "$(INTDIR)\motiontoto.obj" + -@erase "$(INTDIR)\motiontoto.sbr" + -@erase "$(INTDIR)\motionvehicle.obj" + -@erase "$(INTDIR)\motionvehicle.sbr" + -@erase "$(INTDIR)\motionworm.obj" + -@erase "$(INTDIR)\motionworm.sbr" + -@erase "$(INTDIR)\object.obj" + -@erase "$(INTDIR)\object.sbr" + -@erase "$(INTDIR)\particule.obj" + -@erase "$(INTDIR)\particule.sbr" + -@erase "$(INTDIR)\physics.obj" + -@erase "$(INTDIR)\physics.sbr" + -@erase "$(INTDIR)\planet.obj" + -@erase "$(INTDIR)\planet.sbr" + -@erase "$(INTDIR)\profile.obj" + -@erase "$(INTDIR)\profile.sbr" + -@erase "$(INTDIR)\pyro.obj" + -@erase "$(INTDIR)\pyro.sbr" + -@erase "$(INTDIR)\restext.obj" + -@erase "$(INTDIR)\restext.sbr" + -@erase "$(INTDIR)\robotmain.obj" + -@erase "$(INTDIR)\robotmain.sbr" + -@erase "$(INTDIR)\script.obj" + -@erase "$(INTDIR)\script.sbr" + -@erase "$(INTDIR)\scroll.obj" + -@erase "$(INTDIR)\scroll.sbr" + -@erase "$(INTDIR)\shortcut.obj" + -@erase "$(INTDIR)\shortcut.sbr" + -@erase "$(INTDIR)\slider.obj" + -@erase "$(INTDIR)\slider.sbr" + -@erase "$(INTDIR)\sound.obj" + -@erase "$(INTDIR)\sound.sbr" + -@erase "$(INTDIR)\studio.obj" + -@erase "$(INTDIR)\studio.sbr" + -@erase "$(INTDIR)\target.obj" + -@erase "$(INTDIR)\target.sbr" + -@erase "$(INTDIR)\task.obj" + -@erase "$(INTDIR)\task.sbr" + -@erase "$(INTDIR)\taskadvance.obj" + -@erase "$(INTDIR)\taskadvance.sbr" + -@erase "$(INTDIR)\taskbuild.obj" + -@erase "$(INTDIR)\taskbuild.sbr" + -@erase "$(INTDIR)\taskfire.obj" + -@erase "$(INTDIR)\taskfire.sbr" + -@erase "$(INTDIR)\taskfireant.obj" + -@erase "$(INTDIR)\taskfireant.sbr" + -@erase "$(INTDIR)\taskflag.obj" + -@erase "$(INTDIR)\taskflag.sbr" + -@erase "$(INTDIR)\taskgoto.obj" + -@erase "$(INTDIR)\taskgoto.sbr" + -@erase "$(INTDIR)\taskgungoal.obj" + -@erase "$(INTDIR)\taskgungoal.sbr" + -@erase "$(INTDIR)\taskinfo.obj" + -@erase "$(INTDIR)\taskinfo.sbr" + -@erase "$(INTDIR)\taskmanager.obj" + -@erase "$(INTDIR)\taskmanager.sbr" + -@erase "$(INTDIR)\taskmanip.obj" + -@erase "$(INTDIR)\taskmanip.sbr" + -@erase "$(INTDIR)\taskpen.obj" + -@erase "$(INTDIR)\taskpen.sbr" + -@erase "$(INTDIR)\taskrecover.obj" + -@erase "$(INTDIR)\taskrecover.sbr" + -@erase "$(INTDIR)\taskreset.obj" + -@erase "$(INTDIR)\taskreset.sbr" + -@erase "$(INTDIR)\tasksearch.obj" + -@erase "$(INTDIR)\tasksearch.sbr" + -@erase "$(INTDIR)\taskshield.obj" + -@erase "$(INTDIR)\taskshield.sbr" + -@erase "$(INTDIR)\taskspiderexplo.obj" + -@erase "$(INTDIR)\taskspiderexplo.sbr" + -@erase "$(INTDIR)\tasktake.obj" + -@erase "$(INTDIR)\tasktake.sbr" + -@erase "$(INTDIR)\taskterraform.obj" + -@erase "$(INTDIR)\taskterraform.sbr" + -@erase "$(INTDIR)\taskturn.obj" + -@erase "$(INTDIR)\taskturn.sbr" + -@erase "$(INTDIR)\taskwait.obj" + -@erase "$(INTDIR)\taskwait.sbr" + -@erase "$(INTDIR)\terrain.obj" + -@erase "$(INTDIR)\terrain.sbr" + -@erase "$(INTDIR)\text.obj" + -@erase "$(INTDIR)\text.sbr" + -@erase "$(INTDIR)\vc50.idb" + -@erase "$(INTDIR)\vc50.pdb" + -@erase "$(INTDIR)\water.obj" + -@erase "$(INTDIR)\water.sbr" + -@erase "$(INTDIR)\window.obj" + -@erase "$(INTDIR)\window.sbr" + -@erase "$(INTDIR)\winmain.res" + -@erase "$(OUTDIR)\projet1.bsc" + -@erase "$(OUTDIR)\projet1.exe" + -@erase "$(OUTDIR)\projet1.ilk" + -@erase "$(OUTDIR)\projet1.pdb" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS"\ + /FR"$(INTDIR)\\" /Fp"$(INTDIR)\projet1.pch" /YX /Fo"$(INTDIR)\\"\ + /Fd"$(INTDIR)\\" /FD /c +CPP_OBJS=.\Debug/ +CPP_SBRS=.\Debug/ + +.c{$(CPP_OBJS)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(CPP_OBJS)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(CPP_OBJS)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(CPP_SBRS)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(CPP_SBRS)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(CPP_SBRS)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +RSC=rc.exe +RSC_PROJ=/l 0x100c /fo"$(INTDIR)\winmain.res" /d "_DEBUG" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\projet1.bsc" +BSC32_SBRS= \ + "$(INTDIR)\auto.sbr" \ + "$(INTDIR)\autobase.sbr" \ + "$(INTDIR)\autoconvert.sbr" \ + "$(INTDIR)\autoderrick.sbr" \ + "$(INTDIR)\autodestroyer.sbr" \ + "$(INTDIR)\autoegg.sbr" \ + "$(INTDIR)\autoenergy.sbr" \ + "$(INTDIR)\autofactory.sbr" \ + "$(INTDIR)\autoflag.sbr" \ + "$(INTDIR)\autohuston.sbr" \ + "$(INTDIR)\autoinfo.sbr" \ + "$(INTDIR)\autojostle.sbr" \ + "$(INTDIR)\autokid.sbr" \ + "$(INTDIR)\autolabo.sbr" \ + "$(INTDIR)\automush.sbr" \ + "$(INTDIR)\autonest.sbr" \ + "$(INTDIR)\autonuclear.sbr" \ + "$(INTDIR)\autopara.sbr" \ + "$(INTDIR)\autoportico.sbr" \ + "$(INTDIR)\autoradar.sbr" \ + "$(INTDIR)\autorepair.sbr" \ + "$(INTDIR)\autoresearch.sbr" \ + "$(INTDIR)\autoroot.sbr" \ + "$(INTDIR)\autosafe.sbr" \ + "$(INTDIR)\autostation.sbr" \ + "$(INTDIR)\autotower.sbr" \ + "$(INTDIR)\blitz.sbr" \ + "$(INTDIR)\brain.sbr" \ + "$(INTDIR)\button.sbr" \ + "$(INTDIR)\camera.sbr" \ + "$(INTDIR)\cbottoken.sbr" \ + "$(INTDIR)\check.sbr" \ + "$(INTDIR)\cloud.sbr" \ + "$(INTDIR)\cmdtoken.sbr" \ + "$(INTDIR)\color.sbr" \ + "$(INTDIR)\compass.sbr" \ + "$(INTDIR)\control.sbr" \ + "$(INTDIR)\d3dapp.sbr" \ + "$(INTDIR)\d3dengine.sbr" \ + "$(INTDIR)\d3denum.sbr" \ + "$(INTDIR)\d3dframe.sbr" \ + "$(INTDIR)\d3dmath.sbr" \ + "$(INTDIR)\d3dtextr.sbr" \ + "$(INTDIR)\d3dutil.sbr" \ + "$(INTDIR)\displayinfo.sbr" \ + "$(INTDIR)\displaytext.sbr" \ + "$(INTDIR)\edit.sbr" \ + "$(INTDIR)\editvalue.sbr" \ + "$(INTDIR)\event.sbr" \ + "$(INTDIR)\gauge.sbr" \ + "$(INTDIR)\group.sbr" \ + "$(INTDIR)\image.sbr" \ + "$(INTDIR)\iman.sbr" \ + "$(INTDIR)\interface.sbr" \ + "$(INTDIR)\joystick.sbr" \ + "$(INTDIR)\key.sbr" \ + "$(INTDIR)\label.sbr" \ + "$(INTDIR)\light.sbr" \ + "$(INTDIR)\list.sbr" \ + "$(INTDIR)\maindialog.sbr" \ + "$(INTDIR)\mainmap.sbr" \ + "$(INTDIR)\mainmovie.sbr" \ + "$(INTDIR)\mainshort.sbr" \ + "$(INTDIR)\map.sbr" \ + "$(INTDIR)\math3d.sbr" \ + "$(INTDIR)\metafile.sbr" \ + "$(INTDIR)\misc.sbr" \ + "$(INTDIR)\model.sbr" \ + "$(INTDIR)\modfile.sbr" \ + "$(INTDIR)\motion.sbr" \ + "$(INTDIR)\motionant.sbr" \ + "$(INTDIR)\motionbee.sbr" \ + "$(INTDIR)\motionhuman.sbr" \ + "$(INTDIR)\motionmother.sbr" \ + "$(INTDIR)\motionspider.sbr" \ + "$(INTDIR)\motiontoto.sbr" \ + "$(INTDIR)\motionvehicle.sbr" \ + "$(INTDIR)\motionworm.sbr" \ + "$(INTDIR)\object.sbr" \ + "$(INTDIR)\particule.sbr" \ + "$(INTDIR)\physics.sbr" \ + "$(INTDIR)\planet.sbr" \ + "$(INTDIR)\profile.sbr" \ + "$(INTDIR)\pyro.sbr" \ + "$(INTDIR)\restext.sbr" \ + "$(INTDIR)\robotmain.sbr" \ + "$(INTDIR)\script.sbr" \ + "$(INTDIR)\scroll.sbr" \ + "$(INTDIR)\shortcut.sbr" \ + "$(INTDIR)\slider.sbr" \ + "$(INTDIR)\sound.sbr" \ + "$(INTDIR)\studio.sbr" \ + "$(INTDIR)\target.sbr" \ + "$(INTDIR)\task.sbr" \ + "$(INTDIR)\taskadvance.sbr" \ + "$(INTDIR)\taskbuild.sbr" \ + "$(INTDIR)\taskfire.sbr" \ + "$(INTDIR)\taskfireant.sbr" \ + "$(INTDIR)\taskflag.sbr" \ + "$(INTDIR)\taskgoto.sbr" \ + "$(INTDIR)\taskgungoal.sbr" \ + "$(INTDIR)\taskinfo.sbr" \ + "$(INTDIR)\taskmanager.sbr" \ + "$(INTDIR)\taskmanip.sbr" \ + "$(INTDIR)\taskpen.sbr" \ + "$(INTDIR)\taskrecover.sbr" \ + "$(INTDIR)\taskreset.sbr" \ + "$(INTDIR)\tasksearch.sbr" \ + "$(INTDIR)\taskshield.sbr" \ + "$(INTDIR)\taskspiderexplo.sbr" \ + "$(INTDIR)\tasktake.sbr" \ + "$(INTDIR)\taskterraform.sbr" \ + "$(INTDIR)\taskturn.sbr" \ + "$(INTDIR)\taskwait.sbr" \ + "$(INTDIR)\terrain.sbr" \ + "$(INTDIR)\text.sbr" \ + "$(INTDIR)\water.sbr" \ + "$(INTDIR)\window.sbr" + +"$(OUTDIR)\projet1.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib winmm.lib c:\dx7sdk\lib\ddraw.lib c:\dx7sdk\lib\dinput.lib\ + c:\dx7sdk\lib\dxguid.lib c:\dx7sdk\lib\d3dx.lib c:\dx7sdk\lib\dsound.lib\ + cbot\cbot.lib /nologo /subsystem:windows /incremental:yes\ + /pdb:"$(OUTDIR)\projet1.pdb" /debug /machine:I386 /out:"$(OUTDIR)\projet1.exe"\ + /pdbtype:sept +LINK32_OBJS= \ + "$(INTDIR)\auto.obj" \ + "$(INTDIR)\autobase.obj" \ + "$(INTDIR)\autoconvert.obj" \ + "$(INTDIR)\autoderrick.obj" \ + "$(INTDIR)\autodestroyer.obj" \ + "$(INTDIR)\autoegg.obj" \ + "$(INTDIR)\autoenergy.obj" \ + "$(INTDIR)\autofactory.obj" \ + "$(INTDIR)\autoflag.obj" \ + "$(INTDIR)\autohuston.obj" \ + "$(INTDIR)\autoinfo.obj" \ + "$(INTDIR)\autojostle.obj" \ + "$(INTDIR)\autokid.obj" \ + "$(INTDIR)\autolabo.obj" \ + "$(INTDIR)\automush.obj" \ + "$(INTDIR)\autonest.obj" \ + "$(INTDIR)\autonuclear.obj" \ + "$(INTDIR)\autopara.obj" \ + "$(INTDIR)\autoportico.obj" \ + "$(INTDIR)\autoradar.obj" \ + "$(INTDIR)\autorepair.obj" \ + "$(INTDIR)\autoresearch.obj" \ + "$(INTDIR)\autoroot.obj" \ + "$(INTDIR)\autosafe.obj" \ + "$(INTDIR)\autostation.obj" \ + "$(INTDIR)\autotower.obj" \ + "$(INTDIR)\blitz.obj" \ + "$(INTDIR)\brain.obj" \ + "$(INTDIR)\button.obj" \ + "$(INTDIR)\camera.obj" \ + "$(INTDIR)\cbottoken.obj" \ + "$(INTDIR)\check.obj" \ + "$(INTDIR)\cloud.obj" \ + "$(INTDIR)\cmdtoken.obj" \ + "$(INTDIR)\color.obj" \ + "$(INTDIR)\compass.obj" \ + "$(INTDIR)\control.obj" \ + "$(INTDIR)\d3dapp.obj" \ + "$(INTDIR)\d3dengine.obj" \ + "$(INTDIR)\d3denum.obj" \ + "$(INTDIR)\d3dframe.obj" \ + "$(INTDIR)\d3dmath.obj" \ + "$(INTDIR)\d3dtextr.obj" \ + "$(INTDIR)\d3dutil.obj" \ + "$(INTDIR)\displayinfo.obj" \ + "$(INTDIR)\displaytext.obj" \ + "$(INTDIR)\edit.obj" \ + "$(INTDIR)\editvalue.obj" \ + "$(INTDIR)\event.obj" \ + "$(INTDIR)\gauge.obj" \ + "$(INTDIR)\group.obj" \ + "$(INTDIR)\image.obj" \ + "$(INTDIR)\iman.obj" \ + "$(INTDIR)\interface.obj" \ + "$(INTDIR)\joystick.obj" \ + "$(INTDIR)\key.obj" \ + "$(INTDIR)\label.obj" \ + "$(INTDIR)\light.obj" \ + "$(INTDIR)\list.obj" \ + "$(INTDIR)\maindialog.obj" \ + "$(INTDIR)\mainmap.obj" \ + "$(INTDIR)\mainmovie.obj" \ + "$(INTDIR)\mainshort.obj" \ + "$(INTDIR)\map.obj" \ + "$(INTDIR)\math3d.obj" \ + "$(INTDIR)\metafile.obj" \ + "$(INTDIR)\misc.obj" \ + "$(INTDIR)\model.obj" \ + "$(INTDIR)\modfile.obj" \ + "$(INTDIR)\motion.obj" \ + "$(INTDIR)\motionant.obj" \ + "$(INTDIR)\motionbee.obj" \ + "$(INTDIR)\motionhuman.obj" \ + "$(INTDIR)\motionmother.obj" \ + "$(INTDIR)\motionspider.obj" \ + "$(INTDIR)\motiontoto.obj" \ + "$(INTDIR)\motionvehicle.obj" \ + "$(INTDIR)\motionworm.obj" \ + "$(INTDIR)\object.obj" \ + "$(INTDIR)\particule.obj" \ + "$(INTDIR)\physics.obj" \ + "$(INTDIR)\planet.obj" \ + "$(INTDIR)\profile.obj" \ + "$(INTDIR)\pyro.obj" \ + "$(INTDIR)\restext.obj" \ + "$(INTDIR)\robotmain.obj" \ + "$(INTDIR)\script.obj" \ + "$(INTDIR)\scroll.obj" \ + "$(INTDIR)\shortcut.obj" \ + "$(INTDIR)\slider.obj" \ + "$(INTDIR)\sound.obj" \ + "$(INTDIR)\studio.obj" \ + "$(INTDIR)\target.obj" \ + "$(INTDIR)\task.obj" \ + "$(INTDIR)\taskadvance.obj" \ + "$(INTDIR)\taskbuild.obj" \ + "$(INTDIR)\taskfire.obj" \ + "$(INTDIR)\taskfireant.obj" \ + "$(INTDIR)\taskflag.obj" \ + "$(INTDIR)\taskgoto.obj" \ + "$(INTDIR)\taskgungoal.obj" \ + "$(INTDIR)\taskinfo.obj" \ + "$(INTDIR)\taskmanager.obj" \ + "$(INTDIR)\taskmanip.obj" \ + "$(INTDIR)\taskpen.obj" \ + "$(INTDIR)\taskrecover.obj" \ + "$(INTDIR)\taskreset.obj" \ + "$(INTDIR)\tasksearch.obj" \ + "$(INTDIR)\taskshield.obj" \ + "$(INTDIR)\taskspiderexplo.obj" \ + "$(INTDIR)\tasktake.obj" \ + "$(INTDIR)\taskterraform.obj" \ + "$(INTDIR)\taskturn.obj" \ + "$(INTDIR)\taskwait.obj" \ + "$(INTDIR)\terrain.obj" \ + "$(INTDIR)\text.obj" \ + "$(INTDIR)\water.obj" \ + "$(INTDIR)\window.obj" \ + "$(INTDIR)\winmain.res" + +"$(OUTDIR)\projet1.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + + +!IF "$(CFG)" == "projet1 - Win32 Release" || "$(CFG)" ==\ + "projet1 - Win32 Debug" +SOURCE=.\auto.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTO_=\ + ".\auto.h"\ + ".\blitz.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cloud.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\label.h"\ + ".\light.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\planet.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\auto.obj" : $(SOURCE) $(DEP_CPP_AUTO_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTO_=\ + ".\auto.h"\ + ".\blitz.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cloud.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\label.h"\ + ".\light.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\planet.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\auto.obj" "$(INTDIR)\auto.sbr" : $(SOURCE) $(DEP_CPP_AUTO_)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autobase.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOB=\ + ".\auto.h"\ + ".\autobase.h"\ + ".\blitz.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cloud.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\planet.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autobase.obj" : $(SOURCE) $(DEP_CPP_AUTOB) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOB=\ + ".\auto.h"\ + ".\autobase.h"\ + ".\blitz.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cloud.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\planet.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autobase.obj" "$(INTDIR)\autobase.sbr" : $(SOURCE) $(DEP_CPP_AUTOB)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autoconvert.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOC=\ + ".\auto.h"\ + ".\autoconvert.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoconvert.obj" : $(SOURCE) $(DEP_CPP_AUTOC) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOC=\ + ".\auto.h"\ + ".\autoconvert.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoconvert.obj" "$(INTDIR)\autoconvert.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTOC) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autoderrick.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOD=\ + ".\auto.h"\ + ".\autoderrick.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoderrick.obj" : $(SOURCE) $(DEP_CPP_AUTOD) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOD=\ + ".\auto.h"\ + ".\autoderrick.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoderrick.obj" "$(INTDIR)\autoderrick.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTOD) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autodestroyer.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTODE=\ + ".\auto.h"\ + ".\autodestroyer.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autodestroyer.obj" : $(SOURCE) $(DEP_CPP_AUTODE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTODE=\ + ".\auto.h"\ + ".\autodestroyer.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autodestroyer.obj" "$(INTDIR)\autodestroyer.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTODE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autoegg.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOE=\ + ".\auto.h"\ + ".\autoegg.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\pyro.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoegg.obj" : $(SOURCE) $(DEP_CPP_AUTOE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOE=\ + ".\auto.h"\ + ".\autoegg.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\pyro.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoegg.obj" "$(INTDIR)\autoegg.sbr" : $(SOURCE) $(DEP_CPP_AUTOE)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autoenergy.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOEN=\ + ".\auto.h"\ + ".\autoenergy.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoenergy.obj" : $(SOURCE) $(DEP_CPP_AUTOEN) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOEN=\ + ".\auto.h"\ + ".\autoenergy.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoenergy.obj" "$(INTDIR)\autoenergy.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTOEN) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autofactory.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOF=\ + ".\auto.h"\ + ".\autofactory.h"\ + ".\brain.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autofactory.obj" : $(SOURCE) $(DEP_CPP_AUTOF) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOF=\ + ".\auto.h"\ + ".\autofactory.h"\ + ".\brain.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autofactory.obj" "$(INTDIR)\autofactory.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTOF) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autoflag.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOFL=\ + ".\auto.h"\ + ".\autoflag.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoflag.obj" : $(SOURCE) $(DEP_CPP_AUTOFL) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOFL=\ + ".\auto.h"\ + ".\autoflag.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoflag.obj" "$(INTDIR)\autoflag.sbr" : $(SOURCE) $(DEP_CPP_AUTOFL)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autohuston.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOH=\ + ".\auto.h"\ + ".\autohuston.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autohuston.obj" : $(SOURCE) $(DEP_CPP_AUTOH) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOH=\ + ".\auto.h"\ + ".\autohuston.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autohuston.obj" "$(INTDIR)\autohuston.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTOH) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autoinfo.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOI=\ + ".\auto.h"\ + ".\autoinfo.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoinfo.obj" : $(SOURCE) $(DEP_CPP_AUTOI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOI=\ + ".\auto.h"\ + ".\autoinfo.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoinfo.obj" "$(INTDIR)\autoinfo.sbr" : $(SOURCE) $(DEP_CPP_AUTOI)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autojostle.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOJ=\ + ".\auto.h"\ + ".\autojostle.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autojostle.obj" : $(SOURCE) $(DEP_CPP_AUTOJ) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOJ=\ + ".\auto.h"\ + ".\autojostle.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autojostle.obj" "$(INTDIR)\autojostle.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTOJ) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autokid.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOK=\ + ".\auto.h"\ + ".\autokid.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autokid.obj" : $(SOURCE) $(DEP_CPP_AUTOK) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOK=\ + ".\auto.h"\ + ".\autokid.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autokid.obj" "$(INTDIR)\autokid.sbr" : $(SOURCE) $(DEP_CPP_AUTOK)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autolabo.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOL=\ + ".\auto.h"\ + ".\autolabo.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autolabo.obj" : $(SOURCE) $(DEP_CPP_AUTOL) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOL=\ + ".\auto.h"\ + ".\autolabo.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autolabo.obj" "$(INTDIR)\autolabo.sbr" : $(SOURCE) $(DEP_CPP_AUTOL)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\automush.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOM=\ + ".\auto.h"\ + ".\automush.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\automush.obj" : $(SOURCE) $(DEP_CPP_AUTOM) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOM=\ + ".\auto.h"\ + ".\automush.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\automush.obj" "$(INTDIR)\automush.sbr" : $(SOURCE) $(DEP_CPP_AUTOM)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autonest.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTON=\ + ".\auto.h"\ + ".\autonest.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autonest.obj" : $(SOURCE) $(DEP_CPP_AUTON) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTON=\ + ".\auto.h"\ + ".\autonest.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autonest.obj" "$(INTDIR)\autonest.sbr" : $(SOURCE) $(DEP_CPP_AUTON)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autonuclear.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTONU=\ + ".\auto.h"\ + ".\autonuclear.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autonuclear.obj" : $(SOURCE) $(DEP_CPP_AUTONU) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTONU=\ + ".\auto.h"\ + ".\autonuclear.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autonuclear.obj" "$(INTDIR)\autonuclear.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTONU) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autopara.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOP=\ + ".\auto.h"\ + ".\autopara.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autopara.obj" : $(SOURCE) $(DEP_CPP_AUTOP) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOP=\ + ".\auto.h"\ + ".\autopara.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autopara.obj" "$(INTDIR)\autopara.sbr" : $(SOURCE) $(DEP_CPP_AUTOP)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autoportico.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOPO=\ + ".\auto.h"\ + ".\autoportico.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoportico.obj" : $(SOURCE) $(DEP_CPP_AUTOPO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOPO=\ + ".\auto.h"\ + ".\autoportico.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoportico.obj" "$(INTDIR)\autoportico.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTOPO) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autoradar.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOR=\ + ".\auto.h"\ + ".\autoradar.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoradar.obj" : $(SOURCE) $(DEP_CPP_AUTOR) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOR=\ + ".\auto.h"\ + ".\autoradar.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoradar.obj" "$(INTDIR)\autoradar.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTOR) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autorepair.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTORE=\ + ".\auto.h"\ + ".\autorepair.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autorepair.obj" : $(SOURCE) $(DEP_CPP_AUTORE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTORE=\ + ".\auto.h"\ + ".\autorepair.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autorepair.obj" "$(INTDIR)\autorepair.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTORE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autoresearch.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTORES=\ + ".\auto.h"\ + ".\autoresearch.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoresearch.obj" : $(SOURCE) $(DEP_CPP_AUTORES) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTORES=\ + ".\auto.h"\ + ".\autoresearch.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoresearch.obj" "$(INTDIR)\autoresearch.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTORES) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autoroot.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTORO=\ + ".\auto.h"\ + ".\autoroot.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoroot.obj" : $(SOURCE) $(DEP_CPP_AUTORO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTORO=\ + ".\auto.h"\ + ".\autoroot.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autoroot.obj" "$(INTDIR)\autoroot.sbr" : $(SOURCE) $(DEP_CPP_AUTORO)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autosafe.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOS=\ + ".\auto.h"\ + ".\autosafe.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autosafe.obj" : $(SOURCE) $(DEP_CPP_AUTOS) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOS=\ + ".\auto.h"\ + ".\autosafe.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autosafe.obj" "$(INTDIR)\autosafe.sbr" : $(SOURCE) $(DEP_CPP_AUTOS)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autostation.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOST=\ + ".\auto.h"\ + ".\autostation.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autostation.obj" : $(SOURCE) $(DEP_CPP_AUTOST) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOST=\ + ".\auto.h"\ + ".\autostation.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autostation.obj" "$(INTDIR)\autostation.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTOST) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\autotower.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_AUTOT=\ + ".\auto.h"\ + ".\autotower.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autotower.obj" : $(SOURCE) $(DEP_CPP_AUTOT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_AUTOT=\ + ".\auto.h"\ + ".\autotower.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\autotower.obj" "$(INTDIR)\autotower.sbr" : $(SOURCE)\ + $(DEP_CPP_AUTOT) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\blitz.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_BLITZ=\ + ".\auto.h"\ + ".\autopara.h"\ + ".\blitz.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\blitz.obj" : $(SOURCE) $(DEP_CPP_BLITZ) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_BLITZ=\ + ".\auto.h"\ + ".\autopara.h"\ + ".\blitz.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\blitz.obj" "$(INTDIR)\blitz.sbr" : $(SOURCE) $(DEP_CPP_BLITZ)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\brain.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_BRAIN=\ + ".\brain.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cbot\cbotdll.h"\ + ".\cmdtoken.h"\ + ".\color.h"\ + ".\compass.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\global.h"\ + ".\group.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\label.h"\ + ".\language.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionspider.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\script.h"\ + ".\slider.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\studio.h"\ + ".\target.h"\ + ".\task.h"\ + ".\taskflag.h"\ + ".\taskmanager.h"\ + ".\taskmanip.h"\ + ".\taskshield.h"\ + ".\terrain.h"\ + ".\text.h"\ + ".\water.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\brain.obj" : $(SOURCE) $(DEP_CPP_BRAIN) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_BRAIN=\ + ".\brain.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cbot\cbotdll.h"\ + ".\cmdtoken.h"\ + ".\color.h"\ + ".\compass.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\global.h"\ + ".\group.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\label.h"\ + ".\language.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionspider.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\script.h"\ + ".\slider.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\studio.h"\ + ".\target.h"\ + ".\task.h"\ + ".\taskflag.h"\ + ".\taskmanager.h"\ + ".\taskmanip.h"\ + ".\taskshield.h"\ + ".\terrain.h"\ + ".\text.h"\ + ".\water.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\brain.obj" "$(INTDIR)\brain.sbr" : $(SOURCE) $(DEP_CPP_BRAIN)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\button.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_BUTTO=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\button.obj" : $(SOURCE) $(DEP_CPP_BUTTO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_BUTTO=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\button.obj" "$(INTDIR)\button.sbr" : $(SOURCE) $(DEP_CPP_BUTTO)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\camera.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_CAMER=\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\camera.obj" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_CAMER=\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\camera.obj" "$(INTDIR)\camera.sbr" : $(SOURCE) $(DEP_CPP_CAMER)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\cbottoken.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_CBOTT=\ + ".\cbottoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\language.h"\ + ".\object.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\cbottoken.obj" : $(SOURCE) $(DEP_CPP_CBOTT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_CBOTT=\ + ".\cbottoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\language.h"\ + ".\object.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\cbottoken.obj" "$(INTDIR)\cbottoken.sbr" : $(SOURCE)\ + $(DEP_CPP_CBOTT) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\check.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_CHECK=\ + ".\check.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\check.obj" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_CHECK=\ + ".\check.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\check.obj" "$(INTDIR)\check.sbr" : $(SOURCE) $(DEP_CPP_CHECK)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\cloud.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_CLOUD=\ + ".\cloud.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\cloud.obj" : $(SOURCE) $(DEP_CPP_CLOUD) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_CLOUD=\ + ".\cloud.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\cloud.obj" "$(INTDIR)\cloud.sbr" : $(SOURCE) $(DEP_CPP_CLOUD)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\cmdtoken.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_CMDTO=\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\language.h"\ + ".\object.h"\ + ".\pyro.h"\ + ".\struct.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\cmdtoken.obj" : $(SOURCE) $(DEP_CPP_CMDTO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_CMDTO=\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\language.h"\ + ".\object.h"\ + ".\pyro.h"\ + ".\struct.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\cmdtoken.obj" "$(INTDIR)\cmdtoken.sbr" : $(SOURCE) $(DEP_CPP_CMDTO)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\color.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_COLOR=\ + ".\color.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\color.obj" : $(SOURCE) $(DEP_CPP_COLOR) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_COLOR=\ + ".\color.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\color.obj" "$(INTDIR)\color.sbr" : $(SOURCE) $(DEP_CPP_COLOR)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\compass.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_COMPA=\ + ".\compass.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\compass.obj" : $(SOURCE) $(DEP_CPP_COMPA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_COMPA=\ + ".\compass.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\compass.obj" "$(INTDIR)\compass.sbr" : $(SOURCE) $(DEP_CPP_COMPA)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\control.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_CONTR=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\particule.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\control.obj" : $(SOURCE) $(DEP_CPP_CONTR) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_CONTR=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\particule.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\control.obj" "$(INTDIR)\control.sbr" : $(SOURCE) $(DEP_CPP_CONTR)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\d3dapp.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_D3DAP=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dtextr.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\joystick.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\profile.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + "c:\dx7sdk\include\dinput.h"\ + + +"$(INTDIR)\d3dapp.obj" : $(SOURCE) $(DEP_CPP_D3DAP) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_D3DAP=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dtextr.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\joystick.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\profile.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + "c:\dx7sdk\include\dinput.h"\ + + +"$(INTDIR)\d3dapp.obj" "$(INTDIR)\d3dapp.sbr" : $(SOURCE) $(DEP_CPP_D3DAP)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\d3dengine.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_D3DEN=\ + ".\blitz.h"\ + ".\cloud.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dtextr.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\language.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\planet.h"\ + ".\profile.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\text.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\d3dengine.obj" : $(SOURCE) $(DEP_CPP_D3DEN) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_D3DEN=\ + ".\blitz.h"\ + ".\cloud.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dtextr.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\language.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\planet.h"\ + ".\profile.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\text.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\d3dengine.obj" "$(INTDIR)\d3dengine.sbr" : $(SOURCE)\ + $(DEP_CPP_D3DEN) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\d3denum.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_D3DENU=\ + ".\d3denum.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + + +"$(INTDIR)\d3denum.obj" : $(SOURCE) $(DEP_CPP_D3DENU) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_D3DENU=\ + ".\d3denum.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + + +"$(INTDIR)\d3denum.obj" "$(INTDIR)\d3denum.sbr" : $(SOURCE) $(DEP_CPP_D3DENU)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\d3dframe.cpp +DEP_CPP_D3DFR=\ + ".\d3dframe.h"\ + ".\d3dutil.h"\ + + +!IF "$(CFG)" == "projet1 - Win32 Release" + + +"$(INTDIR)\d3dframe.obj" : $(SOURCE) $(DEP_CPP_D3DFR) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + + +"$(INTDIR)\d3dframe.obj" "$(INTDIR)\d3dframe.sbr" : $(SOURCE) $(DEP_CPP_D3DFR)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\d3dmath.cpp +DEP_CPP_D3DMA=\ + ".\d3dmath.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +!IF "$(CFG)" == "projet1 - Win32 Release" + + +"$(INTDIR)\d3dmath.obj" : $(SOURCE) $(DEP_CPP_D3DMA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + + +"$(INTDIR)\d3dmath.obj" "$(INTDIR)\d3dmath.sbr" : $(SOURCE) $(DEP_CPP_D3DMA)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\d3dtextr.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_D3DTE=\ + ".\d3dtextr.h"\ + ".\d3dutil.h"\ + ".\language.h"\ + ".\metafile.h"\ + ".\misc.h"\ + + +"$(INTDIR)\d3dtextr.obj" : $(SOURCE) $(DEP_CPP_D3DTE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_D3DTE=\ + ".\d3dtextr.h"\ + ".\d3dutil.h"\ + ".\language.h"\ + ".\metafile.h"\ + ".\misc.h"\ + + +"$(INTDIR)\d3dtextr.obj" "$(INTDIR)\d3dtextr.sbr" : $(SOURCE) $(DEP_CPP_D3DTE)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\d3dutil.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_D3DUT=\ + ".\d3dutil.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\d3dutil.obj" : $(SOURCE) $(DEP_CPP_D3DUT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_D3DUT=\ + ".\d3dutil.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\d3dutil.obj" "$(INTDIR)\d3dutil.sbr" : $(SOURCE) $(DEP_CPP_D3DUT)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\displayinfo.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_DISPL=\ + ".\button.h"\ + ".\camera.h"\ + ".\cbottoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displayinfo.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\group.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\language.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motiontoto.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\slider.h"\ + ".\struct.h"\ + ".\text.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\displayinfo.obj" : $(SOURCE) $(DEP_CPP_DISPL) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_DISPL=\ + ".\button.h"\ + ".\camera.h"\ + ".\cbottoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displayinfo.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\group.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\language.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motiontoto.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\slider.h"\ + ".\struct.h"\ + ".\text.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\displayinfo.obj" "$(INTDIR)\displayinfo.sbr" : $(SOURCE)\ + $(DEP_CPP_DISPL) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\displaytext.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_DISPLA=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\group.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\label.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motiontoto.h"\ + ".\object.h"\ + ".\restext.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\text.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\displaytext.obj" : $(SOURCE) $(DEP_CPP_DISPLA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_DISPLA=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\group.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\label.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motiontoto.h"\ + ".\object.h"\ + ".\restext.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\text.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\displaytext.obj" "$(INTDIR)\displaytext.sbr" : $(SOURCE)\ + $(DEP_CPP_DISPLA) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\edit.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_EDIT_=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\scroll.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\edit.obj" : $(SOURCE) $(DEP_CPP_EDIT_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_EDIT_=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\scroll.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\edit.obj" "$(INTDIR)\edit.sbr" : $(SOURCE) $(DEP_CPP_EDIT_)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\editvalue.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_EDITV=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\editvalue.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\editvalue.obj" : $(SOURCE) $(DEP_CPP_EDITV) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_EDITV=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\editvalue.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\editvalue.obj" "$(INTDIR)\editvalue.sbr" : $(SOURCE)\ + $(DEP_CPP_EDITV) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\event.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_EVENT=\ + ".\event.h"\ + ".\iman.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\event.obj" : $(SOURCE) $(DEP_CPP_EVENT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_EVENT=\ + ".\event.h"\ + ".\iman.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\event.obj" "$(INTDIR)\event.sbr" : $(SOURCE) $(DEP_CPP_EVENT)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\gauge.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_GAUGE=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\gauge.obj" : $(SOURCE) $(DEP_CPP_GAUGE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_GAUGE=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\gauge.obj" "$(INTDIR)\gauge.sbr" : $(SOURCE) $(DEP_CPP_GAUGE)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\group.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_GROUP=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\group.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\group.obj" : $(SOURCE) $(DEP_CPP_GROUP) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_GROUP=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\group.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\group.obj" "$(INTDIR)\group.sbr" : $(SOURCE) $(DEP_CPP_GROUP)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\image.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_IMAGE=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\image.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\image.obj" : $(SOURCE) $(DEP_CPP_IMAGE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_IMAGE=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\image.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\image.obj" "$(INTDIR)\image.sbr" : $(SOURCE) $(DEP_CPP_IMAGE)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\iman.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_IMAN_=\ + ".\iman.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\iman.obj" : $(SOURCE) $(DEP_CPP_IMAN_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_IMAN_=\ + ".\iman.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\iman.obj" "$(INTDIR)\iman.sbr" : $(SOURCE) $(DEP_CPP_IMAN_)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\interface.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_INTER=\ + ".\button.h"\ + ".\camera.h"\ + ".\check.h"\ + ".\color.h"\ + ".\compass.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\editvalue.h"\ + ".\event.h"\ + ".\group.h"\ + ".\image.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\key.h"\ + ".\label.h"\ + ".\list.h"\ + ".\map.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\scroll.h"\ + ".\shortcut.h"\ + ".\slider.h"\ + ".\struct.h"\ + ".\target.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\interface.obj" : $(SOURCE) $(DEP_CPP_INTER) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_INTER=\ + ".\button.h"\ + ".\camera.h"\ + ".\check.h"\ + ".\color.h"\ + ".\compass.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\editvalue.h"\ + ".\event.h"\ + ".\group.h"\ + ".\image.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\key.h"\ + ".\label.h"\ + ".\list.h"\ + ".\map.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\scroll.h"\ + ".\shortcut.h"\ + ".\slider.h"\ + ".\struct.h"\ + ".\target.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\interface.obj" "$(INTDIR)\interface.sbr" : $(SOURCE)\ + $(DEP_CPP_INTER) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\joystick.cpp +DEP_CPP_JOYST=\ + ".\joystick.h"\ + "c:\dx7sdk\include\dinput.h"\ + + +!IF "$(CFG)" == "projet1 - Win32 Release" + + +"$(INTDIR)\joystick.obj" : $(SOURCE) $(DEP_CPP_JOYST) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + + +"$(INTDIR)\joystick.obj" "$(INTDIR)\joystick.sbr" : $(SOURCE) $(DEP_CPP_JOYST)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\key.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_KEY_C=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\key.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\key.obj" : $(SOURCE) $(DEP_CPP_KEY_C) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_KEY_C=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\key.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\key.obj" "$(INTDIR)\key.sbr" : $(SOURCE) $(DEP_CPP_KEY_C)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\label.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_LABEL=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\label.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\label.obj" : $(SOURCE) $(DEP_CPP_LABEL) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_LABEL=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\label.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\label.obj" "$(INTDIR)\label.sbr" : $(SOURCE) $(DEP_CPP_LABEL)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\light.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_LIGHT=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\light.obj" : $(SOURCE) $(DEP_CPP_LIGHT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_LIGHT=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\light.obj" "$(INTDIR)\light.sbr" : $(SOURCE) $(DEP_CPP_LIGHT)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\list.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_LIST_=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\scroll.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\list.obj" : $(SOURCE) $(DEP_CPP_LIST_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_LIST_=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\scroll.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\list.obj" "$(INTDIR)\list.sbr" : $(SOURCE) $(DEP_CPP_LIST_)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\maindialog.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MAIND=\ + ".\button.h"\ + ".\camera.h"\ + ".\check.h"\ + ".\cmdtoken.h"\ + ".\color.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\editvalue.h"\ + ".\event.h"\ + ".\global.h"\ + ".\group.h"\ + ".\image.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\key.h"\ + ".\label.h"\ + ".\language.h"\ + ".\list.h"\ + ".\maindialog.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\particule.h"\ + ".\profile.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\scroll.h"\ + ".\slider.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\text.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\maindialog.obj" : $(SOURCE) $(DEP_CPP_MAIND) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MAIND=\ + ".\button.h"\ + ".\camera.h"\ + ".\check.h"\ + ".\cmdtoken.h"\ + ".\color.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\editvalue.h"\ + ".\event.h"\ + ".\global.h"\ + ".\group.h"\ + ".\image.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\key.h"\ + ".\label.h"\ + ".\language.h"\ + ".\list.h"\ + ".\maindialog.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\particule.h"\ + ".\profile.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\scroll.h"\ + ".\slider.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\text.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\maindialog.obj" "$(INTDIR)\maindialog.sbr" : $(SOURCE)\ + $(DEP_CPP_MAIND) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\mainmap.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MAINM=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\group.h"\ + ".\image.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\mainmap.h"\ + ".\map.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\scroll.h"\ + ".\slider.h"\ + ".\struct.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\mainmap.obj" : $(SOURCE) $(DEP_CPP_MAINM) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MAINM=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\group.h"\ + ".\image.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\mainmap.h"\ + ".\map.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\scroll.h"\ + ".\slider.h"\ + ".\struct.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\mainmap.obj" "$(INTDIR)\mainmap.sbr" : $(SOURCE) $(DEP_CPP_MAINM)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\mainmovie.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MAINMO=\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\mainmovie.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\mainmovie.obj" : $(SOURCE) $(DEP_CPP_MAINMO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MAINMO=\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\mainmovie.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\mainmovie.obj" "$(INTDIR)\mainmovie.sbr" : $(SOURCE)\ + $(DEP_CPP_MAINMO) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\mainshort.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MAINS=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\mainshort.h"\ + ".\map.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\robotmain.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\mainshort.obj" : $(SOURCE) $(DEP_CPP_MAINS) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MAINS=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\mainshort.h"\ + ".\map.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\robotmain.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\mainshort.obj" "$(INTDIR)\mainshort.sbr" : $(SOURCE)\ + $(DEP_CPP_MAINS) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\map.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MAP_C=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\map.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\robotmain.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MAP_C=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\map.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\robotmain.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\map.obj" "$(INTDIR)\map.sbr" : $(SOURCE) $(DEP_CPP_MAP_C)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\math3d.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MATH3=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\math3d.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\math3d.obj" : $(SOURCE) $(DEP_CPP_MATH3) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MATH3=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\math3d.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\math3d.obj" "$(INTDIR)\math3d.sbr" : $(SOURCE) $(DEP_CPP_MATH3)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\metafile.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_METAF=\ + ".\language.h"\ + ".\metafile.h"\ + + +"$(INTDIR)\metafile.obj" : $(SOURCE) $(DEP_CPP_METAF) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_METAF=\ + ".\language.h"\ + ".\metafile.h"\ + + +"$(INTDIR)\metafile.obj" "$(INTDIR)\metafile.sbr" : $(SOURCE) $(DEP_CPP_METAF)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\misc.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MISC_=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\language.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\misc.obj" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MISC_=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\language.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\misc.obj" "$(INTDIR)\misc.sbr" : $(SOURCE) $(DEP_CPP_MISC_)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\model.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MODEL=\ + ".\button.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\model.h"\ + ".\modfile.h"\ + ".\robotmain.h"\ + ".\struct.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\model.obj" : $(SOURCE) $(DEP_CPP_MODEL) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MODEL=\ + ".\button.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\model.h"\ + ".\modfile.h"\ + ".\robotmain.h"\ + ".\struct.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\model.obj" "$(INTDIR)\model.sbr" : $(SOURCE) $(DEP_CPP_MODEL)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\modfile.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MODFI=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\modfile.obj" : $(SOURCE) $(DEP_CPP_MODFI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MODFI=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\modfile.obj" "$(INTDIR)\modfile.sbr" : $(SOURCE) $(DEP_CPP_MODFI)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\motion.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MOTIO=\ + ".\brain.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motion.obj" : $(SOURCE) $(DEP_CPP_MOTIO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MOTIO=\ + ".\brain.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motion.obj" "$(INTDIR)\motion.sbr" : $(SOURCE) $(DEP_CPP_MOTIO)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\motionant.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MOTION=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionant.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionant.obj" : $(SOURCE) $(DEP_CPP_MOTION) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MOTION=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionant.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionant.obj" "$(INTDIR)\motionant.sbr" : $(SOURCE)\ + $(DEP_CPP_MOTION) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\motionbee.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MOTIONB=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionbee.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionbee.obj" : $(SOURCE) $(DEP_CPP_MOTIONB) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MOTIONB=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionbee.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionbee.obj" "$(INTDIR)\motionbee.sbr" : $(SOURCE)\ + $(DEP_CPP_MOTIONB) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\motionhuman.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MOTIONH=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionhuman.obj" : $(SOURCE) $(DEP_CPP_MOTIONH) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MOTIONH=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionhuman.obj" "$(INTDIR)\motionhuman.sbr" : $(SOURCE)\ + $(DEP_CPP_MOTIONH) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\motionmother.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MOTIONM=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionmother.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionmother.obj" : $(SOURCE) $(DEP_CPP_MOTIONM) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MOTIONM=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionmother.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionmother.obj" "$(INTDIR)\motionmother.sbr" : $(SOURCE)\ + $(DEP_CPP_MOTIONM) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\motionspider.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MOTIONS=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionspider.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionspider.obj" : $(SOURCE) $(DEP_CPP_MOTIONS) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MOTIONS=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionspider.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionspider.obj" "$(INTDIR)\motionspider.sbr" : $(SOURCE)\ + $(DEP_CPP_MOTIONS) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\motiontoto.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MOTIONT=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motiontoto.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motiontoto.obj" : $(SOURCE) $(DEP_CPP_MOTIONT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MOTIONT=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motiontoto.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motiontoto.obj" "$(INTDIR)\motiontoto.sbr" : $(SOURCE)\ + $(DEP_CPP_MOTIONT) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\motionvehicle.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MOTIONV=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionvehicle.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionvehicle.obj" : $(SOURCE) $(DEP_CPP_MOTIONV) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MOTIONV=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionvehicle.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionvehicle.obj" "$(INTDIR)\motionvehicle.sbr" : $(SOURCE)\ + $(DEP_CPP_MOTIONV) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\motionworm.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_MOTIONW=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionworm.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionworm.obj" : $(SOURCE) $(DEP_CPP_MOTIONW) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_MOTIONW=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionworm.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\motionworm.obj" "$(INTDIR)\motionworm.sbr" : $(SOURCE)\ + $(DEP_CPP_MOTIONW) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\object.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_OBJEC=\ + ".\auto.h"\ + ".\autobase.h"\ + ".\autoconvert.h"\ + ".\autoderrick.h"\ + ".\autodestroyer.h"\ + ".\autoegg.h"\ + ".\autoenergy.h"\ + ".\autofactory.h"\ + ".\autoflag.h"\ + ".\autohuston.h"\ + ".\autoinfo.h"\ + ".\autojostle.h"\ + ".\autokid.h"\ + ".\autolabo.h"\ + ".\automush.h"\ + ".\autonest.h"\ + ".\autonuclear.h"\ + ".\autopara.h"\ + ".\autoportico.h"\ + ".\autoradar.h"\ + ".\autorepair.h"\ + ".\autoresearch.h"\ + ".\autoroot.h"\ + ".\autosafe.h"\ + ".\autostation.h"\ + ".\autotower.h"\ + ".\blitz.h"\ + ".\brain.h"\ + ".\camera.h"\ + ".\cbot\cbotdll.h"\ + ".\cbottoken.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\mainmovie.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionant.h"\ + ".\motionbee.h"\ + ".\motionhuman.h"\ + ".\motionmother.h"\ + ".\motionspider.h"\ + ".\motiontoto.h"\ + ".\motionvehicle.h"\ + ".\motionworm.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\object.obj" : $(SOURCE) $(DEP_CPP_OBJEC) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_OBJEC=\ + ".\auto.h"\ + ".\autobase.h"\ + ".\autoconvert.h"\ + ".\autoderrick.h"\ + ".\autodestroyer.h"\ + ".\autoegg.h"\ + ".\autoenergy.h"\ + ".\autofactory.h"\ + ".\autoflag.h"\ + ".\autohuston.h"\ + ".\autoinfo.h"\ + ".\autojostle.h"\ + ".\autokid.h"\ + ".\autolabo.h"\ + ".\automush.h"\ + ".\autonest.h"\ + ".\autonuclear.h"\ + ".\autopara.h"\ + ".\autoportico.h"\ + ".\autoradar.h"\ + ".\autorepair.h"\ + ".\autoresearch.h"\ + ".\autoroot.h"\ + ".\autosafe.h"\ + ".\autostation.h"\ + ".\autotower.h"\ + ".\blitz.h"\ + ".\brain.h"\ + ".\camera.h"\ + ".\cbot\cbotdll.h"\ + ".\cbottoken.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\mainmovie.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionant.h"\ + ".\motionbee.h"\ + ".\motionhuman.h"\ + ".\motionmother.h"\ + ".\motionspider.h"\ + ".\motiontoto.h"\ + ".\motionvehicle.h"\ + ".\motionworm.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\object.obj" "$(INTDIR)\object.sbr" : $(SOURCE) $(DEP_CPP_OBJEC)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\particule.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_PARTI=\ + ".\auto.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dtextr.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\particule.obj" : $(SOURCE) $(DEP_CPP_PARTI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_PARTI=\ + ".\auto.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dtextr.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\particule.obj" "$(INTDIR)\particule.sbr" : $(SOURCE)\ + $(DEP_CPP_PARTI) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\physics.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_PHYSI=\ + ".\brain.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\physics.obj" : $(SOURCE) $(DEP_CPP_PHYSI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_PHYSI=\ + ".\brain.h"\ + ".\camera.h"\ + ".\cmdtoken.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\physics.obj" "$(INTDIR)\physics.sbr" : $(SOURCE) $(DEP_CPP_PHYSI)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\planet.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_PLANE=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\planet.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\planet.obj" : $(SOURCE) $(DEP_CPP_PLANE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_PLANE=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\planet.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\planet.obj" "$(INTDIR)\planet.sbr" : $(SOURCE) $(DEP_CPP_PLANE)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\profile.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_PROFI=\ + ".\language.h"\ + ".\profile.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\profile.obj" : $(SOURCE) $(DEP_CPP_PROFI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_PROFI=\ + ".\language.h"\ + ".\profile.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\profile.obj" "$(INTDIR)\profile.sbr" : $(SOURCE) $(DEP_CPP_PROFI)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\pyro.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_PYRO_=\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\pyro.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\pyro.obj" : $(SOURCE) $(DEP_CPP_PYRO_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_PYRO_=\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\pyro.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\pyro.obj" "$(INTDIR)\pyro.sbr" : $(SOURCE) $(DEP_CPP_PYRO_)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\restext.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_RESTE=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\language.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\restext.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\restext.obj" : $(SOURCE) $(DEP_CPP_RESTE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_RESTE=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\language.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\restext.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\restext.obj" "$(INTDIR)\restext.sbr" : $(SOURCE) $(DEP_CPP_RESTE)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\robotmain.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_ROBOT=\ + ".\auto.h"\ + ".\autobase.h"\ + ".\blitz.h"\ + ".\brain.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cbot\cbotdll.h"\ + ".\cbottoken.h"\ + ".\classfile.cpp"\ + ".\cloud.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displayinfo.h"\ + ".\displaytext.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\label.h"\ + ".\language.h"\ + ".\light.h"\ + ".\maindialog.h"\ + ".\mainmap.h"\ + ".\mainmovie.h"\ + ".\mainshort.h"\ + ".\map.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\model.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\motiontoto.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\planet.h"\ + ".\profile.h"\ + ".\pyro.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\script.h"\ + ".\shortcut.h"\ + ".\slider.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskbuild.h"\ + ".\taskmanip.h"\ + ".\terrain.h"\ + ".\text.h"\ + ".\water.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\robotmain.obj" : $(SOURCE) $(DEP_CPP_ROBOT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_ROBOT=\ + ".\auto.h"\ + ".\autobase.h"\ + ".\blitz.h"\ + ".\brain.h"\ + ".\button.h"\ + ".\camera.h"\ + ".\cbot\cbotdll.h"\ + ".\cbottoken.h"\ + ".\classfile.cpp"\ + ".\cloud.h"\ + ".\cmdtoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displayinfo.h"\ + ".\displaytext.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\label.h"\ + ".\language.h"\ + ".\light.h"\ + ".\maindialog.h"\ + ".\mainmap.h"\ + ".\mainmovie.h"\ + ".\mainshort.h"\ + ".\map.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\model.h"\ + ".\modfile.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\motiontoto.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\planet.h"\ + ".\profile.h"\ + ".\pyro.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\script.h"\ + ".\shortcut.h"\ + ".\slider.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskbuild.h"\ + ".\taskmanip.h"\ + ".\terrain.h"\ + ".\text.h"\ + ".\water.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\robotmain.obj" "$(INTDIR)\robotmain.sbr" : $(SOURCE)\ + $(DEP_CPP_ROBOT) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\script.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_SCRIP=\ + ".\cbot\cbotdll.h"\ + ".\cbottoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\script.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskgoto.h"\ + ".\taskmanager.h"\ + ".\taskmanip.h"\ + ".\taskshield.h"\ + ".\terrain.h"\ + ".\text.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\script.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_SCRIP=\ + ".\cbot\cbotdll.h"\ + ".\cbottoken.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\global.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\script.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskgoto.h"\ + ".\taskmanager.h"\ + ".\taskmanip.h"\ + ".\taskshield.h"\ + ".\terrain.h"\ + ".\text.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\script.obj" "$(INTDIR)\script.sbr" : $(SOURCE) $(DEP_CPP_SCRIP)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\scroll.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_SCROL=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\scroll.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\scroll.obj" : $(SOURCE) $(DEP_CPP_SCROL) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_SCROL=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\scroll.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\scroll.obj" "$(INTDIR)\scroll.sbr" : $(SOURCE) $(DEP_CPP_SCROL)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\shortcut.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_SHORT=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\shortcut.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\shortcut.obj" : $(SOURCE) $(DEP_CPP_SHORT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_SHORT=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\shortcut.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\shortcut.obj" "$(INTDIR)\shortcut.sbr" : $(SOURCE) $(DEP_CPP_SHORT)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\slider.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_SLIDE=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\slider.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\slider.obj" : $(SOURCE) $(DEP_CPP_SLIDE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_SLIDE=\ + ".\button.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\slider.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\slider.obj" "$(INTDIR)\slider.sbr" : $(SOURCE) $(DEP_CPP_SLIDE)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\sound.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_SOUND=\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\sound.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\sound.obj" : $(SOURCE) $(DEP_CPP_SOUND) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_SOUND=\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\sound.h"\ + ".\struct.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\sound.obj" "$(INTDIR)\sound.sbr" : $(SOURCE) $(DEP_CPP_SOUND)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\studio.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_STUDI=\ + ".\button.h"\ + ".\camera.h"\ + ".\cbottoken.h"\ + ".\check.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\group.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\label.h"\ + ".\language.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\script.h"\ + ".\slider.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\studio.h"\ + ".\text.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\studio.obj" : $(SOURCE) $(DEP_CPP_STUDI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_STUDI=\ + ".\button.h"\ + ".\camera.h"\ + ".\cbottoken.h"\ + ".\check.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\event.h"\ + ".\group.h"\ + ".\iman.h"\ + ".\interface.h"\ + ".\label.h"\ + ".\language.h"\ + ".\list.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\script.h"\ + ".\slider.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\studio.h"\ + ".\text.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\studio.obj" "$(INTDIR)\studio.sbr" : $(SOURCE) $(DEP_CPP_STUDI)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\target.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TARGE=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\struct.h"\ + ".\target.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\target.obj" : $(SOURCE) $(DEP_CPP_TARGE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TARGE=\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\restext.h"\ + ".\robotmain.h"\ + ".\struct.h"\ + ".\target.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\target.obj" "$(INTDIR)\target.sbr" : $(SOURCE) $(DEP_CPP_TARGE)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\task.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASK_=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\task.obj" : $(SOURCE) $(DEP_CPP_TASK_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASK_=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\task.obj" "$(INTDIR)\task.sbr" : $(SOURCE) $(DEP_CPP_TASK_)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskadvance.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKA=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskadvance.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskadvance.obj" : $(SOURCE) $(DEP_CPP_TASKA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKA=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskadvance.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskadvance.obj" "$(INTDIR)\taskadvance.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKA) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskbuild.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKB=\ + ".\auto.h"\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskbuild.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskbuild.obj" : $(SOURCE) $(DEP_CPP_TASKB) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKB=\ + ".\auto.h"\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskbuild.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskbuild.obj" "$(INTDIR)\taskbuild.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKB) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskfire.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKF=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskfire.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskfire.obj" : $(SOURCE) $(DEP_CPP_TASKF) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKF=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskfire.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskfire.obj" "$(INTDIR)\taskfire.sbr" : $(SOURCE) $(DEP_CPP_TASKF)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskfireant.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKFI=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionant.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskfireant.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskfireant.obj" : $(SOURCE) $(DEP_CPP_TASKFI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKFI=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionant.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskfireant.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskfireant.obj" "$(INTDIR)\taskfireant.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKFI) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskflag.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKFL=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskflag.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskflag.obj" : $(SOURCE) $(DEP_CPP_TASKFL) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKFL=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskflag.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskflag.obj" "$(INTDIR)\taskflag.sbr" : $(SOURCE) $(DEP_CPP_TASKFL)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskgoto.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKG=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskgoto.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskgoto.obj" : $(SOURCE) $(DEP_CPP_TASKG) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKG=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskgoto.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskgoto.obj" "$(INTDIR)\taskgoto.sbr" : $(SOURCE) $(DEP_CPP_TASKG)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskgungoal.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKGU=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskgungoal.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskgungoal.obj" : $(SOURCE) $(DEP_CPP_TASKGU) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKGU=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskgungoal.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskgungoal.obj" "$(INTDIR)\taskgungoal.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKGU) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskinfo.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKI=\ + ".\auto.h"\ + ".\autoinfo.h"\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskinfo.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskinfo.obj" : $(SOURCE) $(DEP_CPP_TASKI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKI=\ + ".\auto.h"\ + ".\autoinfo.h"\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskinfo.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskinfo.obj" "$(INTDIR)\taskinfo.sbr" : $(SOURCE) $(DEP_CPP_TASKI)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskmanager.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKM=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskadvance.h"\ + ".\taskbuild.h"\ + ".\taskfire.h"\ + ".\taskfireant.h"\ + ".\taskflag.h"\ + ".\taskgoto.h"\ + ".\taskgungoal.h"\ + ".\taskinfo.h"\ + ".\taskmanager.h"\ + ".\taskmanip.h"\ + ".\taskpen.h"\ + ".\taskrecover.h"\ + ".\taskreset.h"\ + ".\tasksearch.h"\ + ".\taskshield.h"\ + ".\taskspiderexplo.h"\ + ".\tasktake.h"\ + ".\taskterraform.h"\ + ".\taskturn.h"\ + ".\taskwait.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskmanager.obj" : $(SOURCE) $(DEP_CPP_TASKM) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKM=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskadvance.h"\ + ".\taskbuild.h"\ + ".\taskfire.h"\ + ".\taskfireant.h"\ + ".\taskflag.h"\ + ".\taskgoto.h"\ + ".\taskgungoal.h"\ + ".\taskinfo.h"\ + ".\taskmanager.h"\ + ".\taskmanip.h"\ + ".\taskpen.h"\ + ".\taskrecover.h"\ + ".\taskreset.h"\ + ".\tasksearch.h"\ + ".\taskshield.h"\ + ".\taskspiderexplo.h"\ + ".\tasktake.h"\ + ".\taskterraform.h"\ + ".\taskturn.h"\ + ".\taskwait.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskmanager.obj" "$(INTDIR)\taskmanager.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKM) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskmanip.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKMA=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskmanip.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskmanip.obj" : $(SOURCE) $(DEP_CPP_TASKMA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKMA=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskmanip.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskmanip.obj" "$(INTDIR)\taskmanip.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKMA) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskpen.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKP=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionant.h"\ + ".\motionspider.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskpen.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskpen.obj" : $(SOURCE) $(DEP_CPP_TASKP) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKP=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionant.h"\ + ".\motionspider.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskpen.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskpen.obj" "$(INTDIR)\taskpen.sbr" : $(SOURCE) $(DEP_CPP_TASKP)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskrecover.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKR=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskrecover.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskrecover.obj" : $(SOURCE) $(DEP_CPP_TASKR) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKR=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskrecover.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskrecover.obj" "$(INTDIR)\taskrecover.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKR) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskreset.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKRE=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskreset.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskreset.obj" : $(SOURCE) $(DEP_CPP_TASKRE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKRE=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskreset.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskreset.obj" "$(INTDIR)\taskreset.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKRE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\tasksearch.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKS=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\tasksearch.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\tasksearch.obj" : $(SOURCE) $(DEP_CPP_TASKS) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKS=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\displaytext.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\tasksearch.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\tasksearch.obj" "$(INTDIR)\tasksearch.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKS) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskshield.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKSH=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskshield.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskshield.obj" : $(SOURCE) $(DEP_CPP_TASKSH) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKSH=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\light.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskshield.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskshield.obj" "$(INTDIR)\taskshield.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKSH) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskspiderexplo.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKSP=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionspider.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskspiderexplo.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskspiderexplo.obj" : $(SOURCE) $(DEP_CPP_TASKSP) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKSP=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionspider.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskspiderexplo.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskspiderexplo.obj" "$(INTDIR)\taskspiderexplo.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKSP) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\tasktake.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKT=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\tasktake.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\tasktake.obj" : $(SOURCE) $(DEP_CPP_TASKT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKT=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionhuman.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\robotmain.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\tasktake.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\tasktake.obj" "$(INTDIR)\tasktake.sbr" : $(SOURCE) $(DEP_CPP_TASKT)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskterraform.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKTE=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionant.h"\ + ".\motionspider.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskterraform.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskterraform.obj" : $(SOURCE) $(DEP_CPP_TASKTE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKTE=\ + ".\brain.h"\ + ".\camera.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\motion.h"\ + ".\motionant.h"\ + ".\motionspider.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\physics.h"\ + ".\pyro.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskterraform.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskterraform.obj" "$(INTDIR)\taskterraform.sbr" : $(SOURCE)\ + $(DEP_CPP_TASKTE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskturn.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKTU=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskturn.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskturn.obj" : $(SOURCE) $(DEP_CPP_TASKTU) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKTU=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskturn.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskturn.obj" "$(INTDIR)\taskturn.sbr" : $(SOURCE) $(DEP_CPP_TASKTU)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\taskwait.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TASKW=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskwait.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskwait.obj" : $(SOURCE) $(DEP_CPP_TASKW) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TASKW=\ + ".\brain.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\physics.h"\ + ".\struct.h"\ + ".\task.h"\ + ".\taskwait.h"\ + ".\terrain.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\taskwait.obj" "$(INTDIR)\taskwait.sbr" : $(SOURCE) $(DEP_CPP_TASKW)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\terrain.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TERRA=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\terrain.obj" : $(SOURCE) $(DEP_CPP_TERRA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TERRA=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\modfile.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\terrain.obj" "$(INTDIR)\terrain.sbr" : $(SOURCE) $(DEP_CPP_TERRA)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\text.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_TEXT_=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\text.obj" : $(SOURCE) $(DEP_CPP_TEXT_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_TEXT_=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\language.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\struct.h"\ + ".\text.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\text.obj" "$(INTDIR)\text.sbr" : $(SOURCE) $(DEP_CPP_TEXT_)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\water.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_WATER=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\water.obj" : $(SOURCE) $(DEP_CPP_WATER) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_WATER=\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dmath.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\event.h"\ + ".\iman.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\object.h"\ + ".\particule.h"\ + ".\sound.h"\ + ".\struct.h"\ + ".\terrain.h"\ + ".\water.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\water.obj" "$(INTDIR)\water.sbr" : $(SOURCE) $(DEP_CPP_WATER)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\window.cpp + +!IF "$(CFG)" == "projet1 - Win32 Release" + +DEP_CPP_WINDO=\ + ".\button.h"\ + ".\check.h"\ + ".\color.h"\ + ".\compass.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\editvalue.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\group.h"\ + ".\image.h"\ + ".\iman.h"\ + ".\key.h"\ + ".\label.h"\ + ".\language.h"\ + ".\list.h"\ + ".\map.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\scroll.h"\ + ".\shortcut.h"\ + ".\slider.h"\ + ".\struct.h"\ + ".\target.h"\ + ".\text.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\window.obj" : $(SOURCE) $(DEP_CPP_WINDO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "projet1 - Win32 Debug" + +DEP_CPP_WINDO=\ + ".\button.h"\ + ".\check.h"\ + ".\color.h"\ + ".\compass.h"\ + ".\control.h"\ + ".\d3dapp.h"\ + ".\d3dengine.h"\ + ".\d3denum.h"\ + ".\d3dframe.h"\ + ".\d3dres.h"\ + ".\d3dutil.h"\ + ".\edit.h"\ + ".\editvalue.h"\ + ".\event.h"\ + ".\gauge.h"\ + ".\group.h"\ + ".\image.h"\ + ".\iman.h"\ + ".\key.h"\ + ".\label.h"\ + ".\language.h"\ + ".\list.h"\ + ".\map.h"\ + ".\math3d.h"\ + ".\metafile.h"\ + ".\misc.h"\ + ".\restext.h"\ + ".\scroll.h"\ + ".\shortcut.h"\ + ".\slider.h"\ + ".\struct.h"\ + ".\target.h"\ + ".\text.h"\ + ".\window.h"\ + "c:\dx7sdk\include\d3dvec.inl"\ + + +"$(INTDIR)\window.obj" "$(INTDIR)\window.sbr" : $(SOURCE) $(DEP_CPP_WINDO)\ + "$(INTDIR)" + + +!ENDIF + +SOURCE=.\winmain.rc +DEP_RSC_WINMA=\ + ".\cur00001.cur"\ + ".\cur00002.cur"\ + ".\cur00003.cur"\ + ".\cursor1.cur"\ + ".\cursorha.cur"\ + ".\cursorsc.cur"\ + ".\directx.ico"\ + + +"$(INTDIR)\winmain.res" : $(SOURCE) $(DEP_RSC_WINMA) "$(INTDIR)" + $(RSC) $(RSC_PROJ) $(SOURCE) + + + +!ENDIF + diff --git a/src/projet1.opt b/src/projet1.opt new file mode 100644 index 0000000000000000000000000000000000000000..46bb4db0934c8f92ecfde210e2357039c2417573 GIT binary patch literal 58880 zcmeHQdu$y?y&lK$dDyX?m*ccenoUX5mefuhQ%Vc8wa>AOgY&=+HIz_T?-?I&yl3}x zA9lPlCYxFz@d&F;pINoLo< zC{6QQ>+|mH{C0jb-_FkNerIRq$4_kf8YTatSf;4DxE=ytM-Bfi{57c@Zc9WuP7C0O&k-V%-IF1E>OG6R;WB0&E3( z0J7`FdOL6ha3ydRumji$Tn+33$nG^*UjsZ3*aKV(>;pq00nP@Ml-$MI7mG+S|FR7=CiT58$>E>r@@3x=Z=UvZr{JPpYg~hT+{43-B+o zwfbeFSl7~u!&-p6{x^`D#QzA+x{b0D3m3922-#D5FUALYH+^u3cZ z9N1C4^~8iX?S&_j9qGEi7BtC_7iNGNUTAz=@or078Ep{t^|M*=Tex6bog?Q|lIJ`?KkkHb88ix&GDEocCh{M+^B@)^q z1Vzfvo%@xR33O-d;h;(ZV?$$o1h z!2HkCad=dzj?nWU7*c$@QeB}pLI|YrkWwmz`0T+ryiF(-M|}5(aky8I;u`CMWM5PY zZmcI#xR5CMq+wFHO)0%XJn$){;~K|y-UDfNop>qAyAkm}6o;YqtQU_sEh^mX*$;;$ z@z~XI*y{WqDnzGv>aDT;^UI%vthGgaBnkiT`qnm5fd8<7_+DZBwN!?be3kfb86Ur= z^L03Wm-y+&wKa&k^zFB>a`q?~&hEh|#ac{tDdz z{7s3WU&Z#@OZOuF9`U1upF7&d!O!c(w-Wy6cAf(N)8b8^!0`=XJWzbJNQ&^HkK@~g zG1Kxi&c}>sQh5xYFuvjjuo2k|0%2Zr-bEp zl-}~?aReR&kf*cKJD(l7&q@y_O4`k1N480kXpzkuQoZQvW)8U0nMG_*Y*N zC*`l;8%-0v&jXAaV59U4!oXLftKxsv<$nbgDDmHlPe0SX-{p#92EkpnTxfgA$kB=0 zx|1&8hDG~j5bxE+o%@Il6Eg53-;zs7FHdUIg6%d;+co^eEOB(c{iRXcS(eo9DbJq^ znx-X<>yiv#cdIdAsBJ5}KDePXEyoNFJgXGviaFQ5(wIBrE#-c5RnAMtYf9fZ=6kis z3WN5$>>x57qq1+`&ZSL?uKQ-AA^qzD#Xv0iG|n6lTsaZBeaG#FG{)^| z-}D!a2{|Ku=~^;oecP1n*1UzlnDneU83swpIU;*1UZZKN+>qaK+nqx$mY>TN=SFws za2zwh3@`)C05iZ0Fayj0Gr$Zm1I)n1V1V^sL($IqFYCXTlm7eaZ^s>>dyh>VJv4HB z!00ok#*PdgG$uyIM}{UxIuh+H9{#Z;Dp0geN-I2{eQ#n<>DVr%BOPf5lKOwQyp6hC zQy22TwYBxRo>immr`q=X)0mUVL1)IEl&4ez|ItPXP*Q(aTi6giD;-wlbW~q*M_ci6 zvExEtEj3t5q)^&AIXOImF5w-ba@_RmG8_wK<9vlVzh8<;5^JqV44Ao9RI0Km-5T__ z#fs-b-gBj&m=rC?i)#I|Ef@@AnHlMuiAAY4STmbVok=@P=B3qdyBMnBeCZS%I|#L@ zG@v|^KB_f>s~O0dTBCBqt7RorYC5JXwHYcp=CpKlx#_Ig0n%9v<*AI6?E0+MqKtAi z%y3rctzb7yS7*>+yP@=FphN4dicpSSZDxvfGpcLTpzKOFYH0mTLr;E#)?&d#ezg`* z?Okp4EF%RUHCmTSDF}Tixs_@35PhfhQL(vK`Ue%qZVB~8omm-lX0>yrx&yYf^`M9!g!FYl(S$}U z^n7iG)VY~)%m6fDpGyfG;M9px>RB^Kmn|5 zK77y9@}bHZm^0azbr72L8>o(4RnE{`$hcIquAPFc+1gBZgk~^@CjeXjEJjvYS4FAe zWtFN}_q|B#X#;r*W^L(ct9sc&yI*s(jY3zPpP-3fr?u6f6gb%?mTxz;OyYjkXySVU z-C6BS*f#4~ISQkit-TORp?aLxc`I6;<7rD?p#12P=pddmpHGU`tkmCq=wY7fsx~)e zyz*o|VdEo%W~4n4?I*p(0PTpjU>oAf5qkPZmGoIWs}(N=79Lf!)v<#v#d*_-wCzI? zkK_8My{XhtnPM{NyLe;Lvh+6f8PfVqXrGBbV5eL#NB3j^)wv@+hjAVp1tvV=HDVf_Y@Ed^%`*9=-WlD-vZ& zKlxRpH57@~PTg4}xk7K4s=m(MZ|IP`=hmBr4_5cGgNq zVv&eNA{L28m%hkgk;qzHMn^7@C7&XaQ(nn0OXX3VRQ|K1t2LFSR--t@BGK~y^nnpC z%m6dM3@`)C05iZ0FaylMg)%U(P7Hqj;VyCPb027X`1FJ8FEkxKju~JEm;q*h8DIu3 z9R||=e=TQ50Yl!Ym!0}|?#^`-d-^o}=Y<(y2ABb6fEi#0m;q*h8DIvO0cL<1Um#Z>Ut_K5hgP{hO^SUEpX%lEb0v!wrD3&#H^ zx$pF8HTG4-2}YgTBnA>mCk42a8Zpi*Gr$Zm1Iz$3zzi@0%m6dM3@`)C05iZ0Xc=Jr zZ+L>ccAnL>Gk-@**8i4wiM^!szbfV&ofeU(<4zw&t|pr&*n=tztz9uzg@J#5lxbCVUryY zjZl#9DAU{q7}~S1Lir{|xrDu)>E2-sT{xGUmAk70$=_-hfr{fOt%IO?_=nbQuEP3K=tjrOWUT#QR<*NiGA9`FL2 zM)6JK-;Ma5AqVLjXItg8dd3HL`CH0D5j9bOsc8LdttIQ zF|p?{)~+$VXxuG*FEx1IlEIiOPhpsD%&!qd&Kcs~4};z~g|Q^F%E$7oegHOIvCUD8 zvpC~;3u*fIBWhC|H5r=LoKc|xYtz^dBDOn@eI+KeIP1s{A#U3kW@J-fD`@<&_pptzX#;O2JbOajG$pFN>v5&DHcmN!RhTkEYjyzheW;Ct z>BeBX@d6>fgJZr=2lB!UFayj0Gr$Zm1Iz$3zzi@0%)qmT0oH$6|D`D^mwo2S`tJ%t zo{Qgq7NEBk#2tk%tZx$!pVb;w_E+}pJ8RFsH}ED1Esp?IxJlQk$Bq3bmQA#S$yxTk z@!-(N!JCH$D}4hukKWWbFi<_vH#9mh+;`x>(1FS=2S#tHj=b>ALfn`nK`KtFlRk}& zCxL$KBm4%V-i$#8U4M}ocm#R*GC&KhNtsQTj{XzztPpEecp~Ni@HH^=C_J@ZEyg? zRet)Jh48AR{4E!`{O>?sJ_d{~TVqo#o&6`$*)P|s_$5>{f{ULbzMw9%i`({5IXi$I z%ht4y7F2#rmRyh2bZ2MUy=snjkABsm!mVugevf^G;WMR2fEQ+f8DIvO0cL<1U" with flags "" + +Creating temp file "C:\Users\DANIEL~1\AppData\Local\Temp\RSPCDA8.tmp" with contents +Creating command line "cl.exe @C:\Users\DANIEL~1\AppData\Local\Temp\RSPCDA8.tmp" +Creating temp file "C:\Users\DANIEL~1\AppData\Local\Temp\RSPCDA9.tmp" with contents +Creating command line "link.exe @C:\Users\DANIEL~1\AppData\Local\Temp\RSPCDA9.tmp" +Compiling... +autobase.cpp +brain.cpp +button.cpp +camera.cpp +cbottoken.cpp +cmdtoken.cpp +color.cpp +control.cpp +d3dapp.cpp +d3dengine.cpp +d3dtextr.cpp +displayinfo.cpp +edit.cpp +maindialog.cpp +metafile.cpp +misc.cpp +modfile.cpp +particule.cpp +physics.cpp +profile.cpp +restext.cpp +robotmain.cpp +sound.cpp +studio.cpp +taskterraform.cpp +terrain.cpp +text.cpp +window.cpp +Linking... + + + +projet1.exe - 0 error(s), 0 warning(s) diff --git a/src/pyro.cpp b/src/pyro.cpp new file mode 100644 index 00000000..9ef4eacf --- /dev/null +++ b/src/pyro.cpp @@ -0,0 +1,2470 @@ +// pyro.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "robotmain.h" +#include "terrain.h" +#include "camera.h" +#include "particule.h" +#include "light.h" +#include "object.h" +#include "motion.h" +#include "motionhuman.h" +#include "displaytext.h" +#include "sound.h" +#include "pyro.h" + + + + +// Constructeur de l'objet. + +CPyro::CPyro(CInstanceManager* iMan) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_PYRO, this, 100); + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + m_light = (CLight*)m_iMan->SearchInstance(CLASS_LIGHT); + m_displayText = (CDisplayText*)m_iMan->SearchInstance(CLASS_DISPLAYTEXT); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + m_object = 0; + + m_progress = 0.0f; + m_speed = 0.0f; + m_lightRank = -1; + m_soundChannel = -1; + LightOperFlush(); +} + +// Destructeur de l'objet. + +CPyro::~CPyro() +{ + m_iMan->DeleteInstance(CLASS_PYRO, this); +} + + +// Détruit l'objet. + +void CPyro::DeleteObject(BOOL bAll) +{ + if ( m_lightRank != -1 ) + { + m_light->DeleteLight(m_lightRank); + m_lightRank = -1; + } +} + + +// Crée un effet pyrotechnique. + +BOOL CPyro::Create(PyroType type, CObject* pObj, float force) +{ + D3DMATRIX* mat; + CObject* power; + CMotion* motion; + D3DVECTOR min, max, pos, speed; + FPOINT dim; + ObjectType oType; + Sound sound; + float duration, mass, h, limit; + int part, objRank, total, i, channel; + + m_object = pObj; + m_force = force; + + oType = pObj->RetType(); + objRank = pObj->RetObjectRank(0); + if ( objRank == -1 ) return FALSE; + m_engine->GetBBox(objRank, min, max); + pos = pObj->RetPosition(0); + + DisplayError(type, pObj); // affiche message éventuel + + // Copie toutes les sphères de l'objet. + for ( i=0 ; i<50 ; i++ ) + { + if ( !pObj->GetCrashSphere(i, m_crashSpherePos[i], m_crashSphereRadius[i]) ) break; + } + m_crashSphereUsed = i; + + // Calcule la dimension de l'effet. + if ( oType == OBJECT_ANT || + oType == OBJECT_BEE || + oType == OBJECT_WORM || + oType == OBJECT_SPIDER ) + { + m_size = 40.0f; + } + else + { + m_size = Length(min, max)*2.0f; + if ( m_size < 4.0f ) m_size = 4.0f; + if ( m_size > 80.0f ) m_size = 80.0f; + } + if ( oType == OBJECT_TNT || + oType == OBJECT_BOMB ) + { + m_size *= 2.0f; + } + + m_pos = pos+(min+max)/2.0f; + m_type = type; + m_progress = 0.0f; + m_speed = 1.0f/20.0f; + m_time = 0.0f; + m_lastParticule = 0.0f; + m_lastParticuleSmoke = 0.0f; + m_lightRank = -1; + + if ( oType == OBJECT_TEEN28 || + oType == OBJECT_TEEN31 ) + { + m_pos.y = pos.y+1.0f; + } + + // Cherche la position de la pile. + power = pObj->RetPower(); + if ( power == 0 ) + { + m_bPower = FALSE; + } + else + { + m_bPower = TRUE; + pos = power->RetPosition(0); + pos.y += 1.0f; + mat = pObj->RetWorldMatrix(0); + m_posPower = Transform(*mat, pos); + } + if ( oType == OBJECT_POWER || + oType == OBJECT_ATOMIC || + oType == OBJECT_URANIUM || + oType == OBJECT_TNT || + oType == OBJECT_BOMB ) + { + m_bPower = TRUE; + m_posPower = m_pos; + m_posPower.y += 1.0f; + m_pos = m_posPower; + } + if ( oType == OBJECT_STATION ) + { + m_bPower = TRUE; + mat = pObj->RetWorldMatrix(0); + m_posPower = Transform(*mat, D3DVECTOR(-15.0f, 7.0f, 0.0f)); + m_pos = m_posPower; + } + if ( oType == OBJECT_ENERGY ) + { + m_bPower = TRUE; + mat = pObj->RetWorldMatrix(0); + m_posPower = Transform(*mat, D3DVECTOR(-7.0f, 6.0f, 0.0f)); + m_pos = m_posPower; + } + if ( oType == OBJECT_NUCLEAR ) + { + m_bPower = TRUE; + m_posPower = m_pos; + } + if ( oType == OBJECT_PARA ) + { + m_bPower = TRUE; + m_posPower = m_pos; + } + if ( oType == OBJECT_SCRAP4 || + oType == OBJECT_SCRAP5 ) // matière plastique ? + { + m_bPower = TRUE; + m_posPower = m_pos; + } + + // Fait entendre le bruit de l'effet pyrotechnique. + if ( type == PT_FRAGT || + type == PT_FRAGW || + type == PT_EXPLOT || + type == PT_EXPLOW ) + { + if ( m_bPower ) + { + sound = SOUND_EXPLOp; + } + else + { + sound = SOUND_EXPLO; + } + if ( oType == OBJECT_STONE || + oType == OBJECT_METAL || + oType == OBJECT_BULLET || + oType == OBJECT_BBOX || + oType == OBJECT_KEYa || + oType == OBJECT_KEYb || + oType == OBJECT_KEYc || + oType == OBJECT_KEYd ) + { + sound = SOUND_EXPLOl; + } + if ( oType == OBJECT_URANIUM || + oType == OBJECT_POWER || + oType == OBJECT_ATOMIC || + oType == OBJECT_TNT || + oType == OBJECT_BOMB ) + { + sound = SOUND_EXPLOlp; + } + m_sound->Play(sound, m_pos); + } + if ( type == PT_FRAGO || + type == PT_EXPLOO || + type == PT_SPIDER || + type == PT_SHOTM ) + { + m_sound->Play(SOUND_EXPLOi, m_pos); + } + if ( type == PT_BURNT || + type == PT_BURNO ) + { + m_soundChannel = m_sound->Play(SOUND_BURN, m_pos, 1.0f, 1.0f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, 12.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 5.0f, SOPER_STOP); + } + if ( type == PT_BURNO ) + { + m_sound->Play(SOUND_DEADi, m_pos); + m_sound->Play(SOUND_DEADi, m_engine->RetEyePt()); + } + if ( type == PT_EGG ) + { + m_sound->Play(SOUND_EGG, m_pos); + } + if ( type == PT_WPCHECK || + type == PT_FLCREATE || + type == PT_FLDELETE ) + { + m_sound->Play(SOUND_WAYPOINT, m_pos); + } + if ( oType == OBJECT_HUMAN ) + { + if ( type == PT_DEADG ) + { + m_sound->Play(SOUND_DEADg, m_pos); + } + if ( type == PT_DEADW ) + { + m_sound->Play(SOUND_DEADw, m_pos); + } + if ( type == PT_SHOTH && m_object->RetSelect() ) + { + m_sound->Play(SOUND_AIE, m_pos); + m_sound->Play(SOUND_AIE, m_engine->RetEyePt()); + } + } + + if ( m_type == PT_FRAGT || + m_type == PT_FRAGO || + m_type == PT_FRAGW ) + { + m_engine->ShadowDelete(m_object->RetObjectRank(0)); + } + + if ( m_type == PT_DEADG ) + { + m_object->SetDead(TRUE); + + motion = m_object->RetMotion(); + if ( motion != 0 ) + { + motion->SetAction(MHS_DEADg, 1.0f); + } + m_camera->StartCentering(m_object, PI*0.5f, 99.9f, 0.0f, 1.5f); + m_camera->StartOver(OE_FADEOUTw, m_pos, 1.0f); + m_speed = 1.0f/10.0f; + return TRUE; + } + if ( m_type == PT_DEADW ) + { + m_object->SetDead(TRUE); + + motion = m_object->RetMotion(); + if ( motion != 0 ) + { + motion->SetAction(MHS_DEADw, 4.0f); + } + m_camera->StartCentering(m_object, PI*0.5f, 99.9f, 0.0f, 3.0f); + m_camera->StartOver(OE_FADEOUTb, m_pos, 1.0f); + m_speed = 1.0f/10.0f; + return TRUE; + } + + if ( m_type == PT_SHOTT || + m_type == PT_SHOTM ) + { + m_camera->StartEffect(CE_SHOT, m_pos, force); + m_speed = 1.0f/1.0f; + return TRUE; + } + if ( m_type == PT_SHOTH ) + { + if ( m_object->RetSelect() ) + { + m_camera->StartOver(OE_BLOOD, m_pos, force); + } + m_speed = 1.0f/0.2f; + return TRUE; + } + + if ( m_type == PT_SHOTW ) + { + m_speed = 1.0f/1.0f; + } + + if ( m_type == PT_BURNT ) + { + BurnStart(); + } + + if ( m_type == PT_WPCHECK ) + { + m_speed = 1.0f/8.0f; + m_object->SetEnable(FALSE); // objet plus fonctionnel + } + if ( m_type == PT_FLCREATE ) + { + m_speed = 1.0f/2.0f; + } + if ( m_type == PT_FLDELETE ) + { + m_speed = 1.0f/2.0f; + m_object->SetEnable(FALSE); // objet plus fonctionnel + } + if ( m_type == PT_RESET ) + { + m_speed = 1.0f/2.0f; + m_object->SetPosition(0, m_object->RetResetPosition()); + m_object->SetAngle(0, m_object->RetResetAngle()); + m_object->SetZoom(0, 0.0f); + } + if ( m_type == PT_FINDING ) + { + limit = (m_size-1.0f)/4.0f; + if ( limit > 8.0f ) limit = 8.0f; + if ( oType == OBJECT_APOLLO2 ) limit = 2.0f; + m_speed = 1.0f/limit; + } + + if ( m_type == PT_EXPLOT || + m_type == PT_EXPLOO || + m_type == PT_EXPLOW ) + { + CreateTriangle(pObj, oType, 0); + m_engine->ShadowDelete(m_object->RetObjectRank(0)); + ExploStart(); + } + + if ( m_type == PT_FALL ) + { + FallStart(); + return TRUE; + } + + if ( m_type == PT_BURNT || + m_type == PT_BURNO ) + { + m_speed = 1.0f/15.0f; + + LightOperAdd(0.00f, 0.0f, 2.0f, 1.0f, 0.0f); // rouge-orange + LightOperAdd(0.30f, 1.0f, -0.8f, -0.8f, -0.8f); // gris foncé + LightOperAdd(0.80f, 1.0f, -0.8f, -0.8f, -0.8f); // gris foncé + LightOperAdd(1.00f, 0.0f, -0.8f, -0.8f, -0.8f); // gris foncé + CreateLight(m_pos, 40.0f); + return TRUE; + } + + if ( m_type == PT_SPIDER ) + { + m_speed = 1.0f/15.0f; + + pos = D3DVECTOR(-3.0f, 2.0f, 0.0f); + mat = pObj->RetWorldMatrix(0); + m_pos = Transform(*mat, pos); + + m_engine->ShadowDelete(m_object->RetObjectRank(0)); + } + + if ( m_type != PT_EGG && + m_type != PT_WIN && + m_type != PT_LOST ) + { + h = 40.0f; + if ( m_type == PT_FRAGO || + m_type == PT_EXPLOO ) + { + LightOperAdd(0.00f, 0.0f, -1.0f, -0.5f, -1.0f); // vert foncé + LightOperAdd(0.05f, 1.0f, -1.0f, -0.5f, -1.0f); // vert foncé + LightOperAdd(1.00f, 0.0f, -1.0f, -0.5f, -1.0f); // vert foncé + } + else if ( m_type == PT_FRAGT || + m_type == PT_EXPLOT ) + { + LightOperAdd(0.00f, 1.0f, 4.0f, 4.0f, 2.0f); // jaune + LightOperAdd(0.02f, 1.0f, 4.0f, 2.0f, 0.0f); // rouge-orange + LightOperAdd(0.16f, 1.0f, -0.8f, -0.8f, -0.8f); // gris foncé + LightOperAdd(1.00f, 0.0f, -0.8f, -0.8f, -0.8f); // gris foncé + h = m_size*2.0f; + } + else if ( m_type == PT_SPIDER ) + { + LightOperAdd(0.00f, 0.0f, -0.5f, -1.0f, -1.0f); // rouge foncé + LightOperAdd(0.05f, 1.0f, -0.5f, -1.0f, -1.0f); // rouge foncé + LightOperAdd(1.00f, 0.0f, -0.5f, -1.0f, -1.0f); // rouge foncé + } + else if ( m_type == PT_FRAGW || + m_type == PT_EXPLOW || + m_type == PT_SHOTW ) + { + LightOperAdd(0.00f, 0.0f, -0.5f, -0.5f, -1.0f); // jaune foncé + LightOperAdd(0.05f, 1.0f, -0.5f, -0.5f, -1.0f); // jaune foncé + LightOperAdd(1.00f, 0.0f, -0.5f, -0.5f, -1.0f); // jaune foncé + } + else if ( m_type == PT_WPCHECK || + m_type == PT_FLCREATE || + m_type == PT_FLDELETE || + m_type == PT_RESET || + m_type == PT_FINDING ) + { + LightOperAdd(0.00f, 1.0f, 4.0f, 4.0f, 2.0f); // jaune + LightOperAdd(1.00f, 0.0f, 4.0f, 4.0f, 2.0f); // jaune + } + else + { + LightOperAdd(0.00f, 0.0f, -0.8f, -0.8f, -0.8f); // gris foncé + LightOperAdd(0.05f, 1.0f, -0.8f, -0.8f, -0.8f); // gris foncé + LightOperAdd(1.00f, 0.0f, -0.8f, -0.8f, -0.8f); // gris foncé + } + CreateLight(m_pos, h); + + if ( m_type != PT_SHOTW && + m_type != PT_WPCHECK && + m_type != PT_FLCREATE && + m_type != PT_FLDELETE && + m_type != PT_RESET && + m_type != PT_FINDING ) + { + m_camera->StartEffect(CE_EXPLO, m_pos, force); + } + } + + if ( m_type == PT_SHOTW ) return TRUE; + + // Génère les triangles de l'explosion. + if ( m_type == PT_FRAGT || + m_type == PT_FRAGO || + m_type == PT_FRAGW || + m_type == PT_SPIDER || + m_type == PT_EGG || + (m_type == PT_EXPLOT && oType == OBJECT_MOBILEtg) || + (m_type == PT_EXPLOT && oType == OBJECT_TEEN28 ) || + (m_type == PT_EXPLOT && oType == OBJECT_TEEN31 ) ) + { + for ( part=0 ; partRetParticuleDensity()); + if ( oType == OBJECT_TNT || + oType == OBJECT_BOMB ) total *= 3; + for ( i=0 ; iCreateTrack(pos, speed, dim, PARTITRACK1, + duration, mass, Rand()+0.7f, 1.0f); + } + } + + if ( m_size > 10.0f ) // assez grand (fret exclu) ? + { + if ( m_bPower ) + { + pos = m_posPower; + } + else + { + pos = m_pos; + m_terrain->MoveOnFloor(pos); + pos.y += 1.0f; + } + dim.x = m_size*0.4f; + dim.y = dim.x; + m_particule->CreateParticule(pos, D3DVECTOR(0.0f,0.0f,0.0f), dim, PARTISPHERE0, 2.0f, 0.0f, 0.0f); + } + } + + if ( m_type == PT_FRAGO || + m_type == PT_EXPLOO ) + { + total = (int)(10.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iCreateParticule(pos, speed, dim, PARTIORGANIC1, + duration, mass); + } + total = (int)(5.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iCreateTrack(pos, speed, dim, PARTITRACK4, + duration, mass, duration*0.5f, dim.x*2.0f); + } + } + + if ( m_type == PT_SPIDER ) + { + for ( i=0 ; i<50 ; i++ ) + { + pos = m_pos; + pos.x += (Rand()-0.5f)*3.0f; + pos.z += (Rand()-0.5f)*3.0f; + pos.y += (Rand()-0.5f)*2.0f; + speed.x = (Rand()-0.5f)*24.0f; + speed.z = (Rand()-0.5f)*24.0f; + speed.y = 10.0f+Rand()*10.0f; + dim.x = 1.0f; + dim.y = dim.x; + channel = m_particule->CreateParticule(pos, speed, dim, PARTIGUN3, 2.0f+Rand()*2.0f, 10.0f); + m_particule->SetObjectFather(channel, pObj); + } + total = (int)(10.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iCreateTrack(pos, speed, dim, PARTITRACK3, + 2.0f+Rand()*2.0f, 10.0f, 2.0f, 0.6f); + } + } + + if ( type == PT_FRAGT || + type == PT_FRAGW || + type == PT_EXPLOT || + type == PT_EXPLOW ) + { + if ( m_size > 10.0f || m_bPower ) + { + pos = m_pos; +//? m_terrain->MoveOnFloor(pos); +//? pos.y += 2.0f; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = m_size; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICHOC, 2.0f); + } + } + + return TRUE; +} + +// Crée une explosion sous forme de particules triangulaires. + +void CPyro::CreateTriangle(CObject* pObj, ObjectType oType, int part) +{ + D3DTriangle buffer[100]; + D3DMATRIX* mat; + D3DVECTOR offset, pos, speed; + float percent, min, max, h, duration, mass; + int objRank, total, i; + + objRank = pObj->RetObjectRank(part); + if ( objRank == -1 ) return; + + min = 0.0f; + max = m_engine->RetLimitLOD(0); + total = m_engine->RetTotalTriangles(objRank); + percent = 0.10f; + if ( total < 50 ) percent = 0.25f; + if ( total < 20 ) percent = 0.50f; + if ( m_type == PT_EGG ) percent = 0.30f; + if ( oType == OBJECT_POWER || + oType == OBJECT_ATOMIC || + oType == OBJECT_URANIUM || + oType == OBJECT_TNT || + oType == OBJECT_BOMB ) percent = 0.75f; + if ( oType == OBJECT_MOBILEtg ) percent = 0.50f; + if ( oType == OBJECT_TEEN28 ) percent = 0.75f; + if ( oType == OBJECT_MOTHER ) max = 1000000.0f; + if ( oType == OBJECT_TEEN28 ) max = 1000000.0f; + if ( oType == OBJECT_TEEN31 ) max = 1000000.0f; + total = m_engine->GetTriangles(objRank, min, max, buffer, 100, percent); + + for ( i=0 ; i 5.0f ) + { + p2.x = p1.x+((p2.x-p1.x)*5.0f/h); + p2.y = p1.y+((p2.y-p1.y)*5.0f/h); + p2.z = p1.z+((p2.z-p1.z)*5.0f/h); + } + + h = Length(p2, p3); + if ( h > 5.0f ) + { + p3.x = p2.x+((p3.x-p2.x)*5.0f/h); + p3.y = p2.y+((p3.y-p2.y)*5.0f/h); + p3.z = p2.z+((p3.z-p2.z)*5.0f/h); + } + + h = Length(p3, p1); + if ( h > 5.0f ) + { + p1.x = p3.x+((p1.x-p3.x)*5.0f/h); + p1.y = p3.y+((p1.y-p3.y)*5.0f/h); + p1.z = p3.z+((p1.z-p3.z)*5.0f/h); + } + + buffer[i].triangle[0].x = p1.x; + buffer[i].triangle[0].y = p1.y; + buffer[i].triangle[0].z = p1.z; + buffer[i].triangle[1].x = p2.x; + buffer[i].triangle[1].y = p2.y; + buffer[i].triangle[1].z = p2.z; + buffer[i].triangle[2].x = p3.x; + buffer[i].triangle[2].y = p3.y; + buffer[i].triangle[2].z = p3.z; + + offset.x = (buffer[i].triangle[0].x+buffer[i].triangle[1].x+buffer[i].triangle[2].x)/3.0f; + offset.y = (buffer[i].triangle[0].y+buffer[i].triangle[1].y+buffer[i].triangle[2].y)/3.0f; + offset.z = (buffer[i].triangle[0].z+buffer[i].triangle[1].z+buffer[i].triangle[2].z)/3.0f; + + buffer[i].triangle[0].x -= offset.x; + buffer[i].triangle[1].x -= offset.x; + buffer[i].triangle[2].x -= offset.x; + + buffer[i].triangle[0].y -= offset.y; + buffer[i].triangle[1].y -= offset.y; + buffer[i].triangle[2].y -= offset.y; + + buffer[i].triangle[0].z -= offset.z; + buffer[i].triangle[1].z -= offset.z; + buffer[i].triangle[2].z -= offset.z; + + mat = pObj->RetWorldMatrix(part); + pos = Transform(*mat, offset); + if ( m_type == PT_EGG ) + { + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + speed.y = Rand()*15.0f; + mass = Rand()*20.0f+20.0f; + } + else if ( m_type == PT_SPIDER ) + { + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + speed.y = Rand()*20.0f; + mass = Rand()*10.0f+15.0f; + } + else + { + speed.x = (Rand()-0.5f)*30.0f; + speed.z = (Rand()-0.5f)*30.0f; + speed.y = Rand()*30.0f; + mass = Rand()*10.0f+15.0f; + } + if ( oType == OBJECT_STONE ) speed *= 0.5f; + if ( oType == OBJECT_URANIUM ) speed *= 0.4f; + duration = Rand()*3.0f+3.0f; + m_particule->CreateFrag(pos, speed, &buffer[i], PARTIFRAG, + duration, mass, 0.5f); + } +} + +// Affiche l'erreur ou l'information éventuelle, liée à la destruction +// d'un insecte, d'un véhicule ou d'un batiment. + +void CPyro::DisplayError(PyroType type, CObject* pObj) +{ + ObjectType oType; + Error err; + + oType = pObj->RetType(); + + if ( type == PT_FRAGT || + type == PT_FRAGO || + type == PT_FRAGW || + type == PT_EXPLOT || + type == PT_EXPLOO || + type == PT_EXPLOW || + type == PT_BURNT || + type == PT_BURNO ) + { + err = ERR_OK; + if ( oType == OBJECT_MOTHER ) err = INFO_DELETEMOTHER; + if ( oType == OBJECT_ANT ) err = INFO_DELETEANT; + if ( oType == OBJECT_BEE ) err = INFO_DELETEBEE; + if ( oType == OBJECT_WORM ) err = INFO_DELETEWORM; + if ( oType == OBJECT_SPIDER ) err = INFO_DELETESPIDER; + + if ( oType == OBJECT_MOBILEwa || + oType == OBJECT_MOBILEta || + oType == OBJECT_MOBILEfa || + oType == OBJECT_MOBILEia || + oType == OBJECT_MOBILEwc || + oType == OBJECT_MOBILEtc || + oType == OBJECT_MOBILEfc || + oType == OBJECT_MOBILEic || + oType == OBJECT_MOBILEwi || + oType == OBJECT_MOBILEti || + oType == OBJECT_MOBILEfi || + oType == OBJECT_MOBILEii || + oType == OBJECT_MOBILEws || + oType == OBJECT_MOBILEts || + oType == OBJECT_MOBILEfs || + oType == OBJECT_MOBILEis || + oType == OBJECT_MOBILErt || + oType == OBJECT_MOBILErc || + oType == OBJECT_MOBILErr || + oType == OBJECT_MOBILErs || + oType == OBJECT_MOBILEsa || + oType == OBJECT_MOBILEwt || + oType == OBJECT_MOBILEtt || + oType == OBJECT_MOBILEft || + oType == OBJECT_MOBILEit || + oType == OBJECT_MOBILEdr ) + { + err = ERR_DELETEMOBILE; + } + + if ( oType == OBJECT_DERRICK || + oType == OBJECT_FACTORY || + oType == OBJECT_STATION || + oType == OBJECT_CONVERT || + oType == OBJECT_REPAIR || + oType == OBJECT_DESTROYER|| + oType == OBJECT_TOWER || + oType == OBJECT_RESEARCH || + oType == OBJECT_RADAR || + oType == OBJECT_INFO || + oType == OBJECT_ENERGY || + oType == OBJECT_LABO || + oType == OBJECT_NUCLEAR || + oType == OBJECT_PARA || + oType == OBJECT_SAFE || + oType == OBJECT_HUSTON || + oType == OBJECT_START || + oType == OBJECT_END ) + { + err = ERR_DELETEBUILDING; + m_displayText->DisplayError(err, pObj->RetPosition(0), 5.0f); + return; + } + + if ( err != ERR_OK ) + { + m_displayText->DisplayError(err, pObj); + } + } +} + + +// Gestion d'un événement. + +BOOL CPyro::EventProcess(const Event &event) +{ + ParticuleType type; + D3DVECTOR pos, speed, angle; + FPOINT dim; + float prog, factor, duration; + int i, r; + + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_engine->RetPause() ) return TRUE; + + m_time += event.rTime; + m_progress += event.rTime*m_speed; + + if ( m_soundChannel != -1 && m_object != 0 ) + { + pos = m_object->RetPosition(0); + m_sound->Position(m_soundChannel, pos); + + if ( m_lightRank != -1 ) + { + pos.y += m_lightHeight; + m_light->SetLightPos(m_lightRank, pos); + } + } + + if ( m_type == PT_SHOTT && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + if ( m_crashSphereUsed > 0 ) + { + i = rand()%m_crashSphereUsed; + pos = m_crashSpherePos[i]; + pos.x += (Rand()-0.5f)*m_crashSphereRadius[i]*2.0f; + pos.z += (Rand()-0.5f)*m_crashSphereRadius[i]*2.0f; + speed.x = (Rand()-0.5f)*m_crashSphereRadius[i]*0.5f; + speed.z = (Rand()-0.5f)*m_crashSphereRadius[i]*0.5f; + speed.y = Rand()*m_crashSphereRadius[i]*1.0f; + dim.x = Rand()*m_crashSphereRadius[i]*0.5f+m_crashSphereRadius[i]*0.75f*m_force; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE1, 3.0f); + } + else + { + pos = m_pos; + pos.x += (Rand()-0.5f)*m_size*0.3f; + pos.z += (Rand()-0.5f)*m_size*0.3f; + speed.x = (Rand()-0.5f)*m_size*0.1f; + speed.z = (Rand()-0.5f)*m_size*0.1f; + speed.y = Rand()*m_size*0.2f; + dim.x = Rand()*m_size/10.0f+m_size/10.0f*m_force; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE1, 3.0f); + } + } + + if ( m_type == PT_SHOTH && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + for ( i=0 ; i<10 ; i++ ) + { + pos = m_pos; + pos.x += (Rand()-0.5f)*m_size*0.2f; + pos.z += (Rand()-0.5f)*m_size*0.2f; + pos.y += (Rand()-0.5f)*m_size*0.5f; + speed.x = (Rand()-0.5f)*5.0f; + speed.z = (Rand()-0.5f)*5.0f; + speed.y = Rand()*1.0f; + dim.x = 1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLOOD, Rand()*3.0f+3.0f, Rand()*10.0f+15.0f, 0.5f); + } + } + + if ( m_type == PT_SHOTM && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + r = (int)(10.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iCreateParticule(pos, speed, dim, PARTIBLOODM, 2.0f, 50.0f, 0.0f); + } + } + + if ( m_type == PT_SHOTW && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + if ( m_crashSphereUsed > 0 ) + { + i = rand()%m_crashSphereUsed; + pos = m_crashSpherePos[i]; + pos.x += (Rand()-0.5f)*m_crashSphereRadius[i]*2.0f; + pos.z += (Rand()-0.5f)*m_crashSphereRadius[i]*2.0f; + speed.x = (Rand()-0.5f)*m_crashSphereRadius[i]*0.5f; + speed.z = (Rand()-0.5f)*m_crashSphereRadius[i]*0.5f; + speed.y = Rand()*m_crashSphereRadius[i]*1.0f; + dim.x = 1.0f*m_force; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLITZ, 0.5f, 0.0f, 0.0f); + } + else + { + pos = m_pos; + pos.x += (Rand()-0.5f)*m_size*0.3f; + pos.z += (Rand()-0.5f)*m_size*0.3f; + speed.x = (Rand()-0.5f)*m_size*0.1f; + speed.z = (Rand()-0.5f)*m_size*0.1f; + speed.y = Rand()*m_size*0.2f; + dim.x = 1.0f*m_force; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLITZ, 0.5f, 0.0f, 0.0f); + } + } + + if ( m_type == PT_SHOTW && + m_lastParticuleSmoke+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticuleSmoke = m_time; + + pos = m_pos; + pos.y -= 2.0f; + pos.x += (Rand()-0.5f)*4.0f; + pos.z += (Rand()-0.5f)*4.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 10.0f+Rand()*10.0f; + dim.x = Rand()*2.5f+2.0f*m_force; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 4.0f); + } + + if ( (m_type == PT_FRAGT || m_type == PT_EXPLOT) && + m_progress < 0.05f && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_pos; + speed.x = (Rand()-0.5f)*m_size*1.0f; + speed.z = (Rand()-0.5f)*m_size*1.0f; + speed.y = Rand()*m_size*0.50f; + dim.x = Rand()*m_size/5.0f+m_size/5.0f; + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, PARTIEXPLOT); + } + + if ( (m_type == PT_FRAGT || m_type == PT_EXPLOT) && + m_progress < 0.10f && + m_lastParticuleSmoke+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticuleSmoke = m_time; + + dim.x = Rand()*m_size/3.0f+m_size/3.0f; + dim.y = dim.x; + pos = m_pos; + pos.x += (Rand()-0.5f)*m_size*0.5f; + pos.z += (Rand()-0.5f)*m_size*0.5f; + m_terrain->MoveOnFloor(pos); + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = -dim.x/2.0f/4.0f; + pos.y += dim.x/2.0f; + + r = rand()%2; + if ( r == 0 ) type = PARTISMOKE1; + if ( r == 1 ) type = PARTISMOKE2; + m_particule->CreateParticule(pos, speed, dim, type, 6.0f); + } + + if ( (m_type == PT_FRAGO || m_type == PT_EXPLOO) && + m_progress < 0.03f && + m_lastParticule+m_engine->ParticuleAdapt(0.1f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_pos; + speed.x = (Rand()-0.5f)*m_size*2.0f; + speed.z = (Rand()-0.5f)*m_size*2.0f; + speed.y = Rand()*m_size*1.0f; + dim.x = Rand()*m_size/2.0f+m_size/2.0f; + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, PARTIEXPLOO); + } + + if ( (m_type == PT_FRAGW || m_type == PT_EXPLOW) && + m_progress < 0.05f && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_pos; + speed.x = (Rand()-0.5f)*m_size*1.0f; + speed.z = (Rand()-0.5f)*m_size*1.0f; + speed.y = Rand()*m_size*0.50f; + dim.x = 1.0f; + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, PARTIBLITZ, 0.5f, 0.0f, 0.0f); + } + + if ( (m_type == PT_FRAGW || m_type == PT_EXPLOW) && + m_progress < 0.25f && + m_lastParticuleSmoke+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticuleSmoke = m_time; + + pos = m_pos; + pos.y -= 2.0f; + pos.x += (Rand()-0.5f)*4.0f; + pos.z += (Rand()-0.5f)*4.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 4.0f+Rand()*4.0f; + dim.x = Rand()*2.5f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 4.0f); + } + + if ( m_type == PT_WPCHECK ) + { + if ( m_progress < 0.25f ) + { + factor = 0.0f; + } + else + { + factor = powf((m_progress-0.25f)/0.75f, 2.0f)*30.0f; + } + + if ( m_progress < 0.85f && + m_lastParticule+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_pos; + pos.y += factor; + pos.x += (Rand()-0.5f)*3.0f; + pos.z += (Rand()-0.5f)*3.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 5.0f+Rand()*5.0f; + dim.x = Rand()*1.5f+1.5f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINT, 2.0f); +//? m_particule->CreateParticule(pos, speed, dim, (ParticuleType)(PARTILENS1+rand()%4), 2.0f); + } + + angle = m_object->RetAngle(0); + angle.y = m_progress*20.0f; + angle.x = sinf(m_progress*49.0f)*0.3f; + angle.z = sinf(m_progress*47.0f)*0.2f; + m_object->SetAngle(0, angle); + + pos = m_pos; + pos.y += factor; + m_object->SetPosition(0, pos); + + if ( m_progress > 0.85f ) + { + m_object->SetZoom(0, 1.0f-(m_progress-0.85f)/0.15f); + } + } + + if ( m_type == PT_FLCREATE ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_pos; + m_terrain->MoveOnFloor(pos); + pos.x += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = 2.0f+Rand()*2.0f; + dim.x = (Rand()*1.0f+1.0f)*(0.2f+m_progress*0.8f); + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINT, 2.0f, 0.0f, 0.0f); + } + + angle = m_object->RetAngle(0); +//? angle.y = powf(m_progress, 0.2f)*20.0f; + angle.x = sinf(m_progress*49.0f)*0.3f*(1.0f-m_progress); + angle.z = sinf(m_progress*47.0f)*0.2f*(1.0f-m_progress); + m_object->SetAngle(0, angle); + + m_object->SetZoom(0, m_progress); + } + + if ( m_type == PT_FLDELETE ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_pos; + m_terrain->MoveOnFloor(pos); + pos.x += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = 2.0f+Rand()*2.0f; + dim.x = (Rand()*1.0f+1.0f)*(0.2f+m_progress*0.8f); + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINT, 2.0f, 0.0f, 0.5f); + } + + angle = m_object->RetAngle(0); + angle.y = m_progress*20.0f; + angle.x = sinf(m_progress*49.0f)*0.3f; + angle.z = sinf(m_progress*47.0f)*0.2f; + m_object->SetAngle(0, angle); + + m_object->SetZoom(0, 1.0f-m_progress); + } + + if ( m_type == PT_RESET ) + { +#if 0 + if ( m_lastParticule+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_pos; + speed.x = (Rand()-0.5f)*6.0f; + speed.z = (Rand()-0.5f)*6.0f; + speed.y = Rand()*12.0f; + dim.x = (Rand()*2.5f+2.5f)*(1.0f-m_progress*0.9f); + dim.y = dim.x; + pos.y += dim.y; + m_particule->CreateParticule(pos, speed, dim, + (ParticuleType)(PARTILENS1+rand()%4), + Rand()*2.5f+2.5f, + Rand()*5.0f+5.0f, 0.0f); + } +#else + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_pos; + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 5.0f+Rand()*5.0f; + dim.x = Rand()*2.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINTb, 2.0f); + + pos = m_pos; + speed.x = (Rand()-0.5f)*20.0f; + speed.z = (Rand()-0.5f)*20.0f; + speed.y = Rand()*10.0f; + speed *= 0.5f+m_progress*0.5f; + dim.x = 0.6f; + dim.y = dim.x; + pos.y += dim.y; + duration = Rand()*1.5f+1.5f; + m_particule->CreateTrack(pos, speed, dim, PARTITRACK6, + duration, 0.0f, + duration*0.9f, 0.7f); + } +#endif + + angle = m_object->RetResetAngle(); + m_object->SetAngleY(0, angle.y-powf((1.0f-m_progress)*5.0f, 2.0f)); + m_object->SetZoom(0, m_progress); + } + + if ( m_type == PT_FINDING ) + { + if ( m_object != 0 && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + factor = m_size*0.3f; + if ( m_object->RetType() == OBJECT_SAFE ) factor *= 1.3f; + if ( factor > 40.0f ) factor = 40.0f; + pos = m_pos; + m_terrain->MoveOnFloor(pos); + pos.x += (Rand()-0.5f)*factor; + pos.z += (Rand()-0.5f)*factor; + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = 4.0f+Rand()*4.0f; + dim.x = (Rand()*3.0f+3.0f)*(1.0f-m_progress*0.9f); + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINT, 2.0f, 0.0f, 0.5f); + } + } + + if ( (m_type == PT_BURNT || m_type == PT_BURNO) && + m_object != 0 ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + factor = m_size/25.0f; // 1 = taille standard + + pos = m_object->RetPosition(0); + pos.y -= m_object->RetCharacter()->height; + pos.x += (Rand()-0.5f)*(4.0f+8.0f*m_progress)*factor; + pos.z += (Rand()-0.5f)*(4.0f+8.0f*m_progress)*factor; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 0.0f; + dim.x = (Rand()*2.5f+1.0f)*factor; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFLAME, 2.0f, 0.0f, 0.2f); + + pos = m_object->RetPosition(0); + pos.y -= m_object->RetCharacter()->height; + pos.x += (Rand()-0.5f)*(2.0f+4.0f*m_progress)*factor; + pos.z += (Rand()-0.5f)*(2.0f+4.0f*m_progress)*factor; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = (Rand()*5.0f*m_progress+3.0f)*factor; + dim.x = (Rand()*2.0f+1.0f)*factor; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFLAME, 2.0f, 0.0f, 0.2f); + + pos = m_object->RetPosition(0); + pos.y -= 2.0f; + pos.x += (Rand()-0.5f)*5.0f*factor; + pos.z += (Rand()-0.5f)*5.0f*factor; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = (6.0f+Rand()*6.0f+m_progress*6.0f)*factor; + dim.x = (Rand()*1.5f+1.0f+m_progress*3.0f)*factor; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE3, 4.0f); + } + + if ( m_type == PT_BURNT ) + { + BurnProgress(); + } + else + { + speed.y = 0.0f; + speed.x = (Rand()-0.5f)*m_progress*1.0f; + speed.z = (Rand()-0.5f)*m_progress*1.0f; + if ( m_progress > 0.8f ) + { + prog = (m_progress-0.8f)/0.2f; // 0..1 + speed.y = -prog*6.0f; // s'enfonce dans le sol + m_object->SetZoom(0, 1.0f-prog*0.5f); + } + m_object->SetLinVibration(speed); + } + } + + if ( m_type == PT_WIN ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + pos.y += 1.5f; + speed.x = (Rand()-0.5f)*10.0f; + speed.z = (Rand()-0.5f)*10.0f; + speed.y = 8.0f+Rand()*8.0f; + dim.x = Rand()*0.2f+0.2f; + dim.y = dim.x; + m_particule->CreateTrack(pos, speed, dim, + (ParticuleType)(PARTITRACK7+rand()%4), + 3.0f, 20.0f, 1.0f, 0.4f); + } + } + + if ( m_type == PT_LOST ) + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.10f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_object->RetPosition(0); + pos.y -= 2.0f; + pos.x += (Rand()-0.5f)*10.0f; + pos.z += (Rand()-0.5f)*10.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 1.0f+Rand()*1.0f; + dim.x = Rand()*1.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE1, 8.0f, 0.0f, 0.0f); + } + } + + if ( m_type == PT_FALL ) + { + FallProgress(event.rTime); + } + + if ( m_lightRank != -1 ) + { + LightOperFrame(event.rTime); + } + + return TRUE; +} + +// Indique que l'objet lié à l'effet n'existe plus, sans le détruire. + +void CPyro::CutObjectLink(CObject* pObj) +{ + if ( m_object == pObj ) + { + m_object = 0; + } +} + +// Indique si l'effet pyrotechnique est terminé. + +Error CPyro::IsEnded() +{ + // Détruit l'objet qui explose. Il ne doit pas être détruit à la fin + // du Create, car c'est parfois l'objet lui-même qui fait le Create : + // pyro->Create(PT_FRAGT, this); + if ( m_type == PT_FRAGT || + m_type == PT_FRAGO || + m_type == PT_FRAGW || + m_type == PT_SPIDER || + m_type == PT_EGG ) + { + DeleteObject(TRUE, TRUE); + } + + if ( m_type == PT_FALL ) // fret qui tombe ? + { + return FallIsEnded(); + } + + if ( m_type == PT_WIN || + m_type == PT_LOST ) + { + return ERR_CONTINUE; + } + + // Fin de l'effet pyrotechnique ? + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + if ( m_type == PT_EXPLOT || + m_type == PT_EXPLOO || + m_type == PT_EXPLOW ) // explosion ? + { + ExploTerminate(); + } + + if ( m_type == PT_BURNT || + m_type == PT_BURNO ) // brûle ? + { + BurnTerminate(); + } + + if ( m_type == PT_WPCHECK || + m_type == PT_FLDELETE ) + { + DeleteObject(TRUE, TRUE); + } + + if ( m_type == PT_FLCREATE ) + { + m_object->SetAngleX(0, 0.0f); + m_object->SetAngleZ(0, 0.0f); + m_object->SetZoom(0, 1.0f); + } + + if ( m_type == PT_RESET ) + { + m_object->SetPosition(0, m_object->RetResetPosition()); + m_object->SetAngle(0, m_object->RetResetAngle()); + m_object->SetZoom(0, 1.0f); + } + + if ( m_lightRank != -1 ) + { + m_light->DeleteLight(m_lightRank); + m_lightRank = -1; + } + + return ERR_STOP; +} + +// Supprime l'objet lié à l'effet pyrotechnique. + +void CPyro::DeleteObject(BOOL bPrimary, BOOL bSecondary) +{ + CObject *sub, *truck; + D3DVECTOR pos; + ObjectType type; + + if ( m_object == 0 ) return; + + if ( m_object->RetResetCap() == RESET_MOVE ) // objet resetable ? + { + m_object->SetEnable(FALSE); // objet caché et inactif + pos = m_object->RetPosition(0); + pos.y = -100.0f; + m_object->SetPosition(0, pos); + return; + } + + type = m_object->RetType(); + if ( bSecondary && + type != OBJECT_FACTORY && + type != OBJECT_NUCLEAR && + type != OBJECT_ENERGY ) + { + sub = m_object->RetPower(); + if ( sub != 0 ) + { + sub->DeleteObject(); // supprime la pile + delete sub; + m_object->SetPower(0); + } + + sub = m_object->RetFret(); + if ( sub != 0 ) + { + sub->DeleteObject(); // supprime l'objet transporté + delete sub; + m_object->SetFret(0); + } + } + + if ( bPrimary ) + { + truck = m_object->RetTruck(); + if ( truck != 0 ) // objet porté ? + { + if ( truck->RetPower() == m_object ) + { + truck->SetPower(0); + } + if ( truck->RetFret() == m_object ) + { + truck->SetFret(0); + } + } + + sub = m_object; + sub->DeleteObject(); // supprime l'objet (*) + delete sub; + m_object = 0; + } +} + +// (*) CObject::DeleteObject peut remettre à zéro m_object +// par le biais de CPyro::CutObjectLink ! + + +// Vide la table des opérations d'animation de la lumière. + +void CPyro::LightOperFlush() +{ + m_lightOperTotal = 0; +} + +// Ajoute une opération d'animation de la lumière. + +void CPyro::LightOperAdd(float progress, float intensity, + float r, float g, float b) +{ + int i; + + i = m_lightOperTotal; + + m_lightOper[i].progress = progress; + m_lightOper[i].intensity = intensity; + m_lightOper[i].color.r = r; + m_lightOper[i].color.g = g; + m_lightOper[i].color.b = b; + + m_lightOperTotal ++; +} + +// Fait évoluer la lumière associée. + +void CPyro::LightOperFrame(float rTime) +{ + D3DCOLORVALUE color; + float progress, intensity; + int i; + + for ( i=0 ; iSetLightIntensity(m_lightRank, intensity); + m_light->SetLightColor(m_lightRank, color); + break; + } + } +} + + +// Crée la lumière pour accompagner un effet pyrotechnique. + +BOOL CPyro::CreateLight(D3DVECTOR pos, float height) +{ + D3DLIGHT7 light; + + if ( !m_engine->RetLightMode() ) return TRUE; + + m_lightHeight = height; + + ZeroMemory( &light, sizeof(light) ); + light.dltType = D3DLIGHT_SPOT; + light.dvPosition.x = pos.x; + light.dvPosition.y = pos.y+height; + light.dvPosition.z = pos.z; + light.dvDirection.x = 0.0f; + light.dvDirection.y = -1.0f; // contre en bas + light.dvDirection.z = 0.0f; + light.dvRange = D3DLIGHT_RANGE_MAX; + light.dvFalloff = 1.0f; + light.dvAttenuation0 = 1.0f; + light.dvAttenuation1 = 0.0f; + light.dvAttenuation2 = 0.0f; + light.dvTheta = 0.0f; + light.dvPhi = PI/4.0f; + + m_lightRank = m_light->CreateLight(); + if ( m_lightRank == -1 ) return FALSE; + + m_light->SetLight(m_lightRank, light); + m_light->SetLightIntensity(m_lightRank, 0.0f); + + // N'éclaire que les objets du terrain. + m_light->SetLightIncluType(m_lightRank, TYPETERRAIN); + + return TRUE; +} + + +// Démarre l'explosion d'un véhicule. + +void CPyro::ExploStart() +{ + D3DVECTOR pos, angle, speed, min, max; + float weight; + int i, objRank, channel; + + m_burnType = m_object->RetType(); + + pos = m_object->RetPosition(0); + m_burnFall = m_terrain->RetFloorHeight(pos, TRUE); + + m_object->Simplify(); + m_object->SetLock(TRUE); // ruine pas encore utilisable + m_object->SetExplo(TRUE); // en cours de destruction + m_object->FlatParent(); + + if ( m_object->RetSelect() ) + { + m_object->SetSelect(FALSE); // désélectionne l'objet + m_camera->SetType(CAMERA_EXPLO); + m_main->DeselectAll(); + } + m_object->DeleteDeselList(m_object); + + for ( i=0 ; iRetObjectRank(i); + if ( objRank == -1 ) continue; + m_engine->ChangeSecondTexture(objRank, "dirty04.tga"); + + pos = m_object->RetPosition(i); + + if ( i == 0 ) // partie principale ? + { + weight = 0.0f; + + speed.y = -1.0f; + speed.x = 0.0f; + speed.z = 0.0f; + } + else + { + m_engine->GetBBox(objRank, min, max); + weight = Length(min, max); // poids selon dimensions ! + + speed.y = 10.0f+Rand()*20.0f; + speed.x = (Rand()-0.5f)*20.0f; + speed.z = (Rand()-0.5f)*20.0f; + } + + channel = m_particule->CreatePart(pos, speed, PARTIPART, 10.0f, 20.0f, weight, 0.5f); + if ( channel != -1 ) + { + m_object->SetMasterParticule(i, channel); + } + } + m_engine->LoadTexture("dirty04.tga", 1); + + DeleteObject(FALSE, TRUE); // détruit l'objet transporté + la pile +} + +// Termine l'explosion d'un véhicule. + +void CPyro::ExploTerminate() +{ + DeleteObject(TRUE, FALSE); // supprime l'objet principal +} + + +// Démarre le feu d'un véhicule. + +void CPyro::BurnStart() +{ + D3DVECTOR pos, angle; + int i, objRank; + + m_burnType = m_object->RetType(); + + pos = m_object->RetPosition(0); + m_burnFall = m_terrain->RetFloorHeight(pos, TRUE); + + m_object->Simplify(); + m_object->SetLock(TRUE); // ruine pas encore utilisable + + if ( m_object->RetSelect() ) + { + m_object->SetSelect(FALSE); // désélectionne l'objet + m_camera->SetType(CAMERA_EXPLO); + m_main->DeselectAll(); + } + m_object->DeleteDeselList(m_object); + + for ( i=0 ; iRetObjectRank(i); + if ( objRank == -1 ) continue; + m_engine->ChangeSecondTexture(objRank, "dirty04.tga"); + } + m_engine->LoadTexture("dirty04.tga", 1); + + m_burnPartTotal = 0; + + if ( m_burnType == OBJECT_DERRICK || + m_burnType == OBJECT_FACTORY || + m_burnType == OBJECT_REPAIR || + m_burnType == OBJECT_DESTROYER|| + m_burnType == OBJECT_CONVERT || + m_burnType == OBJECT_TOWER || + m_burnType == OBJECT_RESEARCH || + m_burnType == OBJECT_ENERGY || + m_burnType == OBJECT_LABO ) + { + pos.x = 0.0f; + pos.y = -(4.0f+Rand()*4.0f); + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.4f; + angle.y = 0.0f; + angle.z = (Rand()-0.5f)*0.4f; + } + else if ( m_burnType == OBJECT_STATION || + m_burnType == OBJECT_RADAR || + m_burnType == OBJECT_INFO ) + { + pos.x = 0.0f; + pos.y = -(1.0f+Rand()*1.0f); + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.2f; + angle.y = 0.0f; + angle.z = (Rand()-0.5f)*0.2f; + } + else if ( m_burnType == OBJECT_NUCLEAR ) + { + pos.x = 0.0f; + pos.y = -(10.0f+Rand()*10.0f); + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.4f; + angle.y = 0.0f; + angle.z = (Rand()-0.5f)*0.4f; + } + else if ( m_burnType == OBJECT_PARA ) + { + pos.x = 0.0f; + pos.y = -(10.0f+Rand()*10.0f); + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.4f; + angle.y = 0.0f; + angle.z = (Rand()-0.5f)*0.4f; + } + else if ( m_burnType == OBJECT_SAFE ) + { + pos.x = 0.0f; + pos.y = -(10.0f+Rand()*10.0f); + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.4f; + angle.y = 0.0f; + angle.z = (Rand()-0.5f)*0.4f; + } + else if ( m_burnType == OBJECT_HUSTON ) + { + pos.x = 0.0f; + pos.y = -(10.0f+Rand()*10.0f); + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.4f; + angle.y = 0.0f; + angle.z = (Rand()-0.5f)*0.4f; + } + else if ( m_burnType == OBJECT_MOBILEwa || + m_burnType == OBJECT_MOBILEwc || + m_burnType == OBJECT_MOBILEwi || + m_burnType == OBJECT_MOBILEws || + m_burnType == OBJECT_MOBILEwt ) + { + pos.x = 0.0f; + pos.y = -(0.5f+Rand()*1.0f); + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.8f; + angle.y = 0.0f; + angle.z = (Rand()-0.5f)*0.4f; + } + else if ( m_burnType == OBJECT_TEEN31 ) // basket ? + { + pos.x = 0.0f; + pos.y = 0.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.8f; + angle.y = 0.0f; + angle.z = (Rand()-0.5f)*0.2f; + } + else + { + pos.x = 0.0f; + pos.y = -(2.0f+Rand()*2.0f); + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.8f; + angle.y = 0.0f; + angle.z = (Rand()-0.5f)*0.8f; + } + BurnAddPart(0, pos, angle); // mouvement de la partie principale + + m_burnKeepPart[0] = -1; // rien à garder + + if ( m_burnType == OBJECT_DERRICK ) + { + pos.x = 0.0f; + pos.y = -40.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = 0.0f; + BurnAddPart(1, pos, angle); // descend le foret + } + + if ( m_burnType == OBJECT_REPAIR ) + { + pos.x = 0.0f; + pos.y = -12.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.2f; + angle.y = (Rand()-0.5f)*0.2f; + angle.z = -90.0f*PI/180.0f; + BurnAddPart(1, pos, angle); // descend le capteur + } + + if ( m_burnType == OBJECT_DESTROYER ) + { + pos.x = 0.0f; + pos.y = -12.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.2f; + angle.y = (Rand()-0.5f)*0.2f; + angle.z = -90.0f*PI/180.0f; + BurnAddPart(1, pos, angle); // descend le capteur + } + + if ( m_burnType == OBJECT_CONVERT ) + { + pos.x = 0.0f; + pos.y = -200.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.5f; + angle.y = (Rand()-0.5f)*0.5f; + angle.z = 0.0f; + BurnAddPart(1, pos, angle); // descend le couvercle + BurnAddPart(2, pos, angle); + BurnAddPart(3, pos, angle); + } + + if ( m_burnType == OBJECT_TOWER ) + { + pos.x = 0.0f; + pos.y = -7.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.4f; + angle.y = (Rand()-0.5f)*0.4f; + angle.z = 0.0f; + BurnAddPart(1, pos, angle); // descend le canon + } + + if ( m_burnType == OBJECT_RESEARCH ) + { + pos.x = 0.0f; + pos.y = -7.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.2f; + angle.y = (Rand()-0.5f)*0.2f; + angle.z = 0.0f; + BurnAddPart(1, pos, angle); // descend l'anémomètre + } + + if ( m_burnType == OBJECT_RADAR ) + { + pos.x = 0.0f; + pos.y = -14.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.4f; + angle.y = (Rand()-0.5f)*0.4f; + angle.z = 0.0f; + BurnAddPart(1, pos, angle); // descend le radar + BurnAddPart(2, pos, angle); + } + + if ( m_burnType == OBJECT_INFO ) + { + pos.x = 0.0f; + pos.y = -14.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.4f; + angle.y = (Rand()-0.5f)*0.4f; + angle.z = 0.0f; + BurnAddPart(1, pos, angle); // descend la borne d'information + BurnAddPart(2, pos, angle); + } + + if ( m_burnType == OBJECT_LABO ) + { + pos.x = 0.0f; + pos.y = -12.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = 0.0f; + BurnAddPart(1, pos, angle); // descend le bras + } + + if ( m_burnType == OBJECT_NUCLEAR ) + { + pos.x = 0.0f; + pos.y = 0.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = -135.0f*PI/180.0f; + BurnAddPart(1, pos, angle); // descend le couvercle + } + + if ( m_burnType == OBJECT_MOBILEfa || + m_burnType == OBJECT_MOBILEta || + m_burnType == OBJECT_MOBILEwa || + m_burnType == OBJECT_MOBILEia ) + { + pos.x = 2.0f; + pos.y = -5.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.2f; + angle.y = (Rand()-0.5f)*0.2f; + angle.z = 40.0f*PI/180.0f; + BurnAddPart(1, pos, angle); // descend le bras + } + + if ( m_burnType == OBJECT_MOBILEfs || + m_burnType == OBJECT_MOBILEts || + m_burnType == OBJECT_MOBILEws || + m_burnType == OBJECT_MOBILEis ) + { + pos.x = 0.0f; + pos.y = -7.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.2f; + angle.y = (Rand()-0.5f)*0.2f; + angle.z = 50.0f*PI/180.0f; + BurnAddPart(1, pos, angle); // descend le capteur + } + + if ( m_burnType == OBJECT_MOBILEfc || + m_burnType == OBJECT_MOBILEtc || + m_burnType == OBJECT_MOBILEwc || + m_burnType == OBJECT_MOBILEic ) + { + pos.x = -1.5f; + pos.y = -5.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.2f; + angle.y = (Rand()-0.5f)*0.2f; + angle.z = -25.0f*PI/180.0f; + BurnAddPart(1, pos, angle); // descend le canon + } + + if ( m_burnType == OBJECT_MOBILEfi || + m_burnType == OBJECT_MOBILEti || + m_burnType == OBJECT_MOBILEwi || + m_burnType == OBJECT_MOBILEii ) + { + pos.x = -1.5f; + pos.y = -5.0f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*0.2f; + angle.y = (Rand()-0.5f)*0.2f; + angle.z = -25.0f*PI/180.0f; + BurnAddPart(1, pos, angle); // descend le canon-insecte + } + + if ( m_burnType == OBJECT_MOBILErt || + m_burnType == OBJECT_MOBILErc ) + { + pos.x = 0.0f; + pos.y = -10.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = 0.0f; + BurnAddPart(1, pos, angle); // descend le support + + pos.x = 0.0f; + pos.y = -10.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = 0.0f; + BurnAddPart(2, pos, angle); // descend le pilon/canon + } + + if ( m_burnType == OBJECT_MOBILErr ) + { + pos.x = 0.0f; + pos.y = -10.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = 0.0f; + BurnAddPart(1, pos, angle); // descend le support + + pos.x = 0.0f; + pos.y = 0.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = -PI/2.0f; + BurnAddPart(4, pos, angle); + + pos.x = 0.0f; + pos.y = 0.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = PI/2.5f; + BurnAddPart(2, pos, angle); + } + + if ( m_burnType == OBJECT_MOBILErs ) + { + pos.x = 0.0f; + pos.y = -10.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = 0.0f; + BurnAddPart(1, pos, angle); // descend le support + + pos.x = 0.0f; + pos.y = -5.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = 0.0f; + BurnAddPart(2, pos, angle); + + pos.x = 0.0f; + pos.y = -5.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = 0.0f; + BurnAddPart(3, pos, angle); + } + + if ( m_burnType == OBJECT_MOBILEsa ) + { + pos.x = 0.0f; + pos.y = -10.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = 0.0f; + BurnAddPart(1, pos, angle); // descend le support + } + + if ( m_burnType == OBJECT_MOBILEwa || + m_burnType == OBJECT_MOBILEwc || + m_burnType == OBJECT_MOBILEwi || + m_burnType == OBJECT_MOBILEws || + m_burnType == OBJECT_MOBILEwt ) // roues ? + { + for ( i=0 ; i<4 ; i++ ) + { + pos.x = 0.0f; + pos.y = Rand()*0.5f; + pos.z = 0.0f; + angle.x = (Rand()-0.5f)*PI/2.0f; + angle.y = (Rand()-0.5f)*PI/2.0f; + angle.z = 0.0f; + BurnAddPart(6+i, pos, angle); // roue + + m_burnKeepPart[i] = 6+i; // on garde les roues + } + m_burnKeepPart[i] = -1; + } + + if ( m_burnType == OBJECT_MOBILEta || + m_burnType == OBJECT_MOBILEtc || + m_burnType == OBJECT_MOBILEti || + m_burnType == OBJECT_MOBILEts || + m_burnType == OBJECT_MOBILErt || + m_burnType == OBJECT_MOBILErc || + m_burnType == OBJECT_MOBILErr || + m_burnType == OBJECT_MOBILErs || + m_burnType == OBJECT_MOBILEsa || + m_burnType == OBJECT_MOBILEdr ) // chenilles ? + { + pos.x = 0.0f; + pos.y = -4.0f; + pos.z = 2.0f; + angle.x = (Rand()-0.5f)*20.0f*PI/180.0f; + angle.y = (Rand()-0.5f)*10.0f*PI/180.0f; + angle.z = (Rand()-0.5f)*30.0f*PI/180.0f; + BurnAddPart(6, pos, angle); // descend la chenille droite + + pos.x = 0.0f; + pos.y = -4.0f; + pos.z = -2.0f; + angle.x = (Rand()-0.5f)*20.0f*PI/180.0f; + angle.y = (Rand()-0.5f)*10.0f*PI/180.0f; + angle.z = (Rand()-0.5f)*30.0f*PI/180.0f; + BurnAddPart(7, pos, angle); // descend la chenille gauche + } + + if ( m_burnType == OBJECT_MOBILEfa || + m_burnType == OBJECT_MOBILEfc || + m_burnType == OBJECT_MOBILEfi || + m_burnType == OBJECT_MOBILEfs || + m_burnType == OBJECT_MOBILEft ) // volant ? + { + for ( i=0 ; i<3 ; i++ ) + { + pos.x = 0.0f; + pos.y = -3.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = 0.0f; + angle.z = (Rand()-0.5f)*PI/2.0f; + BurnAddPart(6+i, pos, angle); // pied + } + m_burnKeepPart[i] = -1; + } + + if ( m_burnType == OBJECT_MOBILEia || + m_burnType == OBJECT_MOBILEic || + m_burnType == OBJECT_MOBILEii || + m_burnType == OBJECT_MOBILEis ) // pattes ? + { + for ( i=0 ; i<6; i++ ) + { + pos.x = 0.0f; + pos.y = -3.0f; + pos.z = 0.0f; + angle.x = 0.0f; + angle.y = (Rand()-0.5f)*PI/4.0f; + angle.z = (Rand()-0.5f)*PI/4.0f; + BurnAddPart(6+i, pos, angle); // patte + } + } +} + +// Ajoute une partie à bouger. + +void CPyro::BurnAddPart(int part, D3DVECTOR pos, D3DVECTOR angle) +{ + int i; + + i = m_burnPartTotal; + m_burnPart[i].part = part; + m_burnPart[i].initialPos = m_object->RetPosition(part); + m_burnPart[i].finalPos = m_burnPart[i].initialPos+pos; + m_burnPart[i].initialAngle = m_object->RetAngle(part); + m_burnPart[i].finalAngle = m_burnPart[i].initialAngle+angle; + + m_burnPartTotal ++; +} + +// Fait progresser le feu d'un véhicule. + +void CPyro::BurnProgress() +{ + CObject* sub; + D3DVECTOR pos; + float h; + int i; + + if ( m_burnType == OBJECT_TEEN31 ) // basket ? + { + m_object->SetZoomY(0, 1.0f-m_progress*0.5f); // léger applatissement + } + + for ( i=0 ; i 0.0f ) + { + h = powf(m_progress, 2.0f)*1000.0f; + if ( h > m_burnFall ) h = m_burnFall; + pos.y -= h; + } + m_object->SetPosition(m_burnPart[i].part, pos); + + pos = m_burnPart[i].initialAngle + m_progress*(m_burnPart[i].finalAngle-m_burnPart[i].initialAngle); + m_object->SetAngle(m_burnPart[i].part, pos); + } + + sub = m_object->RetPower(); + if ( sub != 0 ) // y a-t-il une pile ? + { + sub->SetZoomY(0, 1.0f-m_progress); // aplatissement complet + } +} + +// Indique si une partie doit être conservée. + +BOOL CPyro::BurnIsKeepPart(int part) +{ + int i; + + i = 0; + while ( m_burnKeepPart[i] != -1 ) + { + if ( part == m_burnKeepPart[i++] ) return TRUE; // faut garder + } + return FALSE; // faut détruire +} + +// Termine le feu d'un insecte ou d'un véhicule. + +void CPyro::BurnTerminate() +{ + int i, objRank; + + if ( m_type == PT_BURNO ) // brûle objet organique ? + { + DeleteObject(TRUE, TRUE); // supprime l'insecte + return; + } + + for ( i=1 ; iRetObjectRank(i); + if ( objRank == -1 ) continue; + if ( BurnIsKeepPart(i) ) continue; + + m_object->DeletePart(i); + } + + DeleteObject(FALSE, TRUE); // détruit l'objet transporté + la pile + + if ( m_burnType == OBJECT_DERRICK || + m_burnType == OBJECT_STATION || + m_burnType == OBJECT_FACTORY || + m_burnType == OBJECT_REPAIR || + m_burnType == OBJECT_DESTROYER|| + m_burnType == OBJECT_CONVERT || + m_burnType == OBJECT_TOWER || + m_burnType == OBJECT_RESEARCH || + m_burnType == OBJECT_RADAR || + m_burnType == OBJECT_INFO || + m_burnType == OBJECT_ENERGY || + m_burnType == OBJECT_LABO || + m_burnType == OBJECT_NUCLEAR || + m_burnType == OBJECT_PARA || + m_burnType == OBJECT_SAFE || + m_burnType == OBJECT_HUSTON || + m_burnType == OBJECT_START || + m_burnType == OBJECT_END ) + { + m_object->SetType(OBJECT_RUINfactory); // ça devient une ruine + m_object->SetLock(FALSE); + } + else + { + m_object->SetType(OBJECT_RUINmobilew1); // ça devient une ruine + m_object->SetLock(FALSE); + } + + m_object->SetBurn(FALSE); // ruine utilisable (c-à-d. récupérable) +} + + +// Début d'un objet fret qui tombe. + +void CPyro::FallStart() +{ + D3DVECTOR pos; + + m_object->SetBurn(TRUE); // plus utilisable + + pos = m_object->RetPosition(0); + m_fallFloor = m_terrain->RetFloorLevel(pos); + m_fallSpeed = 0.0f; + m_fallBulletTime = 0.0f; + m_bFallEnding = FALSE; +} + +// Cherche un objet à exploser par le boulet de l'abeille qui tombe. + +CObject* CPyro::FallSearchBeeExplo() +{ + CObject* pObj; + D3DVECTOR iPos, oPos; + ObjectType oType; + float iRadius, oRadius, distance, shieldRadius; + int i, j; + + m_object->GetCrashSphere(0, iPos, iRadius); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + oType = pObj->RetType(); + if ( oType != OBJECT_HUMAN && + oType != OBJECT_MOBILEfa && + oType != OBJECT_MOBILEta && + oType != OBJECT_MOBILEwa && + oType != OBJECT_MOBILEia && + oType != OBJECT_MOBILEfc && + oType != OBJECT_MOBILEtc && + oType != OBJECT_MOBILEwc && + oType != OBJECT_MOBILEic && + oType != OBJECT_MOBILEfi && + oType != OBJECT_MOBILEti && + oType != OBJECT_MOBILEwi && + oType != OBJECT_MOBILEii && + oType != OBJECT_MOBILEfs && + oType != OBJECT_MOBILEts && + oType != OBJECT_MOBILEws && + oType != OBJECT_MOBILEis && + oType != OBJECT_MOBILErt && + oType != OBJECT_MOBILErc && + oType != OBJECT_MOBILErr && + oType != OBJECT_MOBILErs && + oType != OBJECT_MOBILEsa && + oType != OBJECT_MOBILEtg && + oType != OBJECT_MOBILEft && + oType != OBJECT_MOBILEtt && + oType != OBJECT_MOBILEwt && + oType != OBJECT_MOBILEit && + oType != OBJECT_MOBILEdr && + oType != OBJECT_BASE && + oType != OBJECT_DERRICK && + oType != OBJECT_STATION && + oType != OBJECT_FACTORY && + oType != OBJECT_REPAIR && + oType != OBJECT_DESTROYER&& + oType != OBJECT_CONVERT && + oType != OBJECT_TOWER && + oType != OBJECT_RESEARCH && + oType != OBJECT_RADAR && + oType != OBJECT_INFO && + oType != OBJECT_ENERGY && + oType != OBJECT_LABO && + oType != OBJECT_NUCLEAR && + oType != OBJECT_PARA && + oType != OBJECT_SAFE && + oType != OBJECT_HUSTON && + oType != OBJECT_METAL && + oType != OBJECT_POWER && + oType != OBJECT_ATOMIC ) continue; + + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + + oPos = pObj->RetPosition(0); + + shieldRadius = pObj->RetShieldRadius(); + if ( shieldRadius > 0.0f ) + { + distance = Length(oPos, iPos); + if ( distance <= shieldRadius ) return pObj; + } + + if ( oType == OBJECT_BASE ) + { + distance = Length(oPos, iPos); + if ( distance < 25.0f ) return pObj; + } + + // Test au centre de l'objet, ce qui est nécessaire pour + // les objets qui n'ont pas de sphère au centre (station). + distance = Length(oPos, iPos)-4.0f; + if ( distance < 5.0f ) return pObj; + + // Test avec toutes les sphères de l'objet. + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + distance = Length(oPos, iPos); + if ( distance <= iRadius+oRadius ) + { + return pObj; + } + } + } + return 0; +} + +// Chute d'un objet fret. + +void CPyro::FallProgress(float rTime) +{ + CObject* pObj; + D3DVECTOR pos; + BOOL bFloor = FALSE; + + if ( m_object == 0 ) return; + + m_fallSpeed += rTime*50.0f; // v2 = v1 + a*dt + pos = m_object->RetPosition(0); + pos.y -= m_fallSpeed*rTime; // dd -= v2*dt + + if ( pos.y <= m_fallFloor ) // sous le niveau du sol ? + { + pos.y = m_fallFloor; + bFloor = TRUE; + } + m_object->SetPosition(0, pos); + + if ( m_object->RetType() == OBJECT_BULLET ) + { + m_fallBulletTime += rTime; + + if ( m_fallBulletTime > 0.2f || bFloor ) + { + m_fallBulletTime = 0.0f; + + pObj = FallSearchBeeExplo(); + if ( pObj == 0 ) + { + if ( bFloor ) // arrivé au niveau du sol ? + { + m_object->ExploObject(EXPLO_BOUM, 0.0f); // démarre explosion + } + } + else + { + if ( pObj->RetShieldRadius() > 0.0f ) // protégé par bouclier ? + { + m_particule->CreateParticule(pos, D3DVECTOR(0.0f, 0.0f, 0.0f), FPOINT(6.0f, 6.0f), PARTIGUNDEL, 2.0f, 0.0f, 0.0f); + m_sound->Play(SOUND_GUNDEL); + + DeleteObject(TRUE, TRUE); // supprime le boulet + } + else + { + if ( pObj->ExploObject(EXPLO_BOUM, 1.0f) ) // démarre explosion + { + DeleteObject(TRUE, TRUE); // supprime le boulet + } + else + { + m_object->ExploObject(EXPLO_BOUM, 0.0f); // démarre explosion + } + } + } + + if ( bFloor || pObj != 0 ) + { + m_bFallEnding = TRUE; + } + } + } +} + +// Indique si la chute est terminée. + +Error CPyro::FallIsEnded() +{ + D3DVECTOR pos; + + if ( m_bFallEnding || m_object == 0 ) return ERR_STOP; + + pos = m_object->RetPosition(0); + if ( pos.y > m_fallFloor ) return ERR_CONTINUE; + + m_sound->Play(SOUND_BOUM, pos); + m_object->SetBurn(FALSE); // de nouveau utilisable + + return ERR_STOP; +} + diff --git a/src/pyro.h b/src/pyro.h new file mode 100644 index 00000000..cfab33a4 --- /dev/null +++ b/src/pyro.h @@ -0,0 +1,158 @@ +// pyro.h + +#ifndef _PYRO_H_ +#define _PYRO_H_ + + +class CInstanceManager; +class CD3DEngine; +class CTerrain; +class CCamera; +class CParticule; +class CLight; +class CObject; +class CDisplayText; +class CRobotMain; +class CSound; + + + +enum PyroType +{ + PT_NULL = 0, + PT_FRAGT = 1, // fragmentation objet technique + PT_FRAGO = 2, // fragmentation objet organique + PT_FRAGW = 4, // fragmentation objet sous l'eau + PT_EXPLOT = 5, // explosion objet technique + PT_EXPLOO = 6, // explosion objet organique + PT_EXPLOW = 8, // explosion objet sous l'eau + PT_SHOTT = 9, // coup objet technique + PT_SHOTH = 10, // coup homme + PT_SHOTM = 11, // coup pondeuse + PT_SHOTW = 12, // coup sous l'eau + PT_EGG = 13, // casse l'oeuf + PT_BURNT = 14, // brûle objet technique + PT_BURNO = 15, // brûle objet organique + PT_SPIDER = 16, // explosion araignée + PT_FALL = 17, // fret qui tombe + PT_WPCHECK = 18, // indicateur atteint + PT_FLCREATE = 19, // drapeau créé + PT_FLDELETE = 20, // drapeau détruit + PT_RESET = 21, // reset position de l'objet + PT_WIN = 22, // feu d'artifice + PT_LOST = 23, // fumée noire + PT_DEADG = 24, // mort par balle + PT_DEADW = 25, // mort noyé + PT_FINDING = 26, // objet découvert +}; + + +enum ObjectType; +enum Error; + + +typedef struct +{ + int part; + D3DVECTOR initialPos; + D3DVECTOR finalPos; + D3DVECTOR initialAngle; + D3DVECTOR finalAngle; +} +PyroBurnPart; + +typedef struct +{ + float progress; + float intensity; + D3DCOLORVALUE color; +} +PyroLightOper; + + + +class CPyro +{ +public: + CPyro(CInstanceManager* iMan); + ~CPyro(); + + void DeleteObject(BOOL bAll=FALSE); + BOOL Create(PyroType type, CObject* pObj, float force=1.0f); + BOOL EventProcess(const Event &event); + Error IsEnded(); + void CutObjectLink(CObject* pObj); + +protected: + void DisplayError(PyroType type, CObject* pObj); + BOOL CreateLight(D3DVECTOR pos, float height); + void DeleteObject(BOOL bPrimary, BOOL bSecondary); + + void CreateTriangle(CObject* pObj, ObjectType oType, int part); + + void ExploStart(); + void ExploTerminate(); + + void BurnStart(); + void BurnAddPart(int part, D3DVECTOR pos, D3DVECTOR angle); + void BurnProgress(); + BOOL BurnIsKeepPart(int part); + void BurnTerminate(); + + void FallStart(); + CObject* FallSearchBeeExplo(); + void FallProgress(float rTime); + Error FallIsEnded(); + + void LightOperFlush(); + void LightOperAdd(float progress, float intensity, float r, float g, float b); + void LightOperFrame(float rTime); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CTerrain* m_terrain; + CCamera* m_camera; + CParticule* m_particule; + CLight* m_light; + CObject* m_object; + CDisplayText* m_displayText; + CRobotMain* m_main; + CSound* m_sound; + + D3DVECTOR m_pos; // centre de l'effet + D3DVECTOR m_posPower; // centre de la pile + BOOL m_bPower; // pile existe ? + PyroType m_type; + float m_force; + float m_size; + float m_progress; + float m_speed; + float m_time; + float m_lastParticule; + float m_lastParticuleSmoke; + int m_soundChannel; + + int m_lightRank; + int m_lightOperTotal; + PyroLightOper m_lightOper[10]; + float m_lightHeight; + + ObjectType m_burnType; + int m_burnPartTotal; + PyroBurnPart m_burnPart[10]; + int m_burnKeepPart[10]; + float m_burnFall; + + float m_fallFloor; + float m_fallSpeed; + float m_fallBulletTime; + BOOL m_bFallEnding; + + int m_crashSphereUsed; // nb de sphères utilisées + D3DVECTOR m_crashSpherePos[50]; + float m_crashSphereRadius[50]; +}; + + +#endif //_PYRO_H_ diff --git a/src/readme.txt b/src/readme.txt new file mode 100644 index 00000000..55bdeeb5 --- /dev/null +++ b/src/readme.txt @@ -0,0 +1,872 @@ +COLOBOT version 1.4 /f +---------------------- + +COLOBOT est un mélange d'un jeu de stratégie en temps réel et +d'une initiation à la programmation. Le scénario vous place à +la tête d'une expédition spatiale, composée d'un seul humain +(vous) et de quelques robots. Vous devrez explorer et coloniser +différentes planètes, tout en cherchant des matières premières +nécessaires à votre survie. + +Petit à petit, vous pourrez construire et programmer de nouveaux +robots, qui vous aideront dans vos tâches. Certaines planètes +sont habitées par des créatures primitives et hostiles qu'il +vous faudra combattre. + + +Configuration minimale +---------------------- + +Processeur 300 MHz +64 Mb RAM +Carte graphique 3D avec 16 Mb RAM +100 Mb d'espace libre sur le disque dur + +Si ce n'est pas déjà fait, DirectX 8a est installé. +La présence du CD est nécessaire pour jouer. + + +Missions +-------- + +Les missions contiennent la partie "aventure" de COLOBOT. Elles +sont au nombre de 36, réparties sur 9 planètes différentes. +Elles doivent être faites dans l'ordre imposé. + +Jeu libre +--------- + +Le mode "jeu libre" vous permet d'agir librement sur une +planète, sans but déterminé. Seuls les planètes déjà visitées et +les recherches effectuées sont disponibles. + +Programmation +------------- + +Cette partie de COLOBOT permet d'apprendre à programmer, même si +vous n'avez aucune notion dans ce domaine. Différents chapitres +présentent des sujets de plus en plus compliqués. Vous pouvez +effectuer n'importe quel exercice, bien qu'il soit conseillé de +commencer par les plus simples. + +Défis +----- + +Les défis vous demandent une bonne connaissance de la +programmation. Ils permettent de vérifier si les notions +apprises sous "programmation" sont effectivement comprises. + + +Autre joueur +------------ + +Après avoir installé COLOBOT sur un ordinateur, plusieurs +personnes peuvent y jouer. Pour chaque joueur, COLOBOT +enregistre automatiquement la progression dans les missions, +les programmes écrits, etc. + + +Options +------- + +Réglages du jeu, répartis dans 5 pages. + + +Options - Affichage +------------------- + +- Pilotes +Il faut choisir un pilote qui porte la mention HAL (Hardware +Abstraction Layer). Evitez les pilotes ayant la mention +"Emulation" ou "T&L". Il arrive fréquemment que des pilotes +avec des noms différents aient des comportements identiques. + +- Résolutions +Les premier et deuxième chiffres indiquent le nombre de pixels +en largeur et en hauteur dans l'écran. Le troisième chiffre +indique le nombre de couleurs affichables (16 pour 65'000 +couleurs et 32 pour 4 millions de couleurs). +Plus la résolution est grande et plus le jeu est beau. +Mais il risque de devenir lent. Commencez par mettre +640 x 480 x 16. La plupart des cartes graphiques modernes +supportent 1024 x 768 x 16. A vous d'essayer le meilleur +compromis. + +- Plein écran +Normalement, COLOBOT occupe tout l'écran, quelle que soit la +résolution. Si vous enlevez la coche, COLOBOT tournera dans +une fenêtre fixe ayant approximativement 640 x 480 pixels. + +- Appliquer les changements +Il faut cliquer ce bouton pour que les changements effectués +dans cette page prennent effet. + + +Options - Graphique +------------------- + +- Ombres +Normalement, tous les objets (robots, bâtiments, titanium, +etc.) projettent une ombre sur le sol. Avec certaines cartes +graphiques anciennes, un carré gris peut apparaître, en plus +de l'ombre. Si cela vous semble inesthétique, supprimez cette +option. + +- Marques sur le sol +Les marques accentuent les berges. Elles donnent aussi des +teintes différentes à certains endroits du terrain. Si cette +option est déclenchée, il n'est pas possible de montrer les +zones plates avec le cosmonaute. + +- Salissures +Les salissures donnent un effet vieux et sale aux robots et aux +bâtiments. + +- Ciel +Avec cette option, le ciel de certaines planètes contient des +nuages poussés par le vent. Sans cela, le ciel est généralement +rempli par un simple dégradé de couleurs. + +- Rayons du soleil +Lorsque vous vous tournez en direction du soleil, un reflet +apparaît. + +- Planètes et étoiles +Sur certaines planètes, vous voyez dans le ciel des planètes +proches qui bougent lentement. + +- Brouillard +Cette option correspond aux nappes de brouillard horizontales +qui sont généralement très proches du sol. + +- Lumières dynamiques +Les lumières dynamiques apparaissent lors d'explosions, ou +lorsqu'un robot refait le plein d'énergie. + +- Quantité de particules (0% à 200%) +Les particules servent à simuler la poussière, la fumée, les +éclats, les bulles sous l'eau, etc. + +- Profondeur de champ (50% à 200%) +La profondeur de champ détermine jusqu'où porte votre regard. +Cette profondeur est de toute façon très différente selon +l'atmosphère de la planète. Une grande valeur (par exemple +200%) permet de voir loin, mais nécessite une bonne carte +graphique 3D. + +- Détails des objets (0% à 200%) +Lorsqu'un objet est au loin, il est représenté avec moins de +détails. Une grande valeur éloigne la distance à laquelle le +changement est effectué. + +- Nombre d'objets décoratifs (0% à 100%) +Ce nombre détermine la quantité d'objets décoratifs présents, +tels que les plantes, les arbres, les cristaux, etc. + + +Options - Jeu +------------- + +- Séquences cinématiques +Certaines missions commencent ou finissent par un petit film +montrant l'atterrissage ou le décollage du vaisseau. La touche +Esc permet toujours de stopper ces séquences. Si cette option +est supprimée, ces films sont complètement sautés. + +- Défilement dans les bords +Lorsque la caméra est derrière le cosmonaute ou derrière un +robot, une rotation est effectuée si la souris s'approche du +bord de l'écran. + +- Inversion souris X +Inverse le sens de la rotation lorsque la souris touche le bord +gauche ou le bord droite de l'écran. + +- Inversion souris Y +Inverse le sens du mouvement lorsque la souris touche le bord +supérieur ou le bord inférieur de l'écran, dans l'éditeur de +programmes. + +- Secousses lors d'explosions +Lors d'une explosion, ou lorsque vous effectuez un atterrissage +brusque, la caméra subit un choc qui se manifeste par une +secousse plus on moins prononcée. Supprimez cette option pour +que la caméra soit toujours parfaitement stable. + +- Retour animé +Indique comment la situation est réinitialisée dans les exercices +de programmation et dans les défis. + +- Bulles d'aide +Les bulles d'aide vous donnent un petit texte explicatif lorsque +la souris s'arrête sur un bouton ou un objet. + +- Reflets sur les boutons +Les reflets sont visibles lorsque la souris survole un bouton. + +- Particules dans l'interface +Pluie de particules lorsque la souris bouge dans les écrans +d'interface. + +- Souris ombrée +La souris ombrée est gérée par COLOBOT. La souris normale est +dessinée par Windows. Lorsque COLOBOT ne fonctionne pas en mode +"plein écran", la souris est forcément normale. + +- Indentation automatique +L'indentation déplace automatiquement le curseur vers la droite +lors de l'édition d'un programme, en fonction des accolades +{ et }. + +- Grande indentation +Une grande indentation décale vers la droite d'une distance +égale à 4 espaces. Sinon, le décalage est de 2 espaces. + + +Options - Commandes +------------------- + +- Flèches gauche, droite, haut et bas +Pour faire tourner, avancer ou reculer le cosmonaute ou un +robot. Dans les exercices de programmation, notez que les robots +ne peuvent pas être déplacés ainsi. + +- Shirt et Ctrl +Pour faire monter ou descendre le cosmonaute ou un robot volant. +Sur certaines planètes, le vol est impossible. + +- Entrée +Cette touche effectue l'action principale du robot sélectionné, +qui correspond au bouton avec un cadre rouge. + +- Espace +Change le point de vue de la caméra. Pour la plupart des robots, +la caméra passe alternativement d'une vue arrière à une vue +intérieure. + +- . (pavé numérique) +Met le jeu en pause et montre l'endroit correspondant au dernier +message affiché en haut de l'écran. Si plusieurs messages sont +affichés, une nouvelle pression montre le message précédent, +chronologiquement parlant. Esc enlève la pause et le jeu reprend +son cours. + +- Tab +Sélectionne l'objet suivant, selon l'ordre des petites icônes +tout en haut de l'écran. + +- Home +Sélectionne toujours rapidement le cosmonaute. + +- 0 (pavé numérique) +Sélectionne le robot ou le bâtiment qui était sélectionné +précédemment. + +- + et - (pavé numérique) +Approche ou éloigne la caméra de l'objet sélectionné. + +- F1 +Affiche les instructions sur la mission ou l'exercice à l'aide +du SatCom. + +- F2 +Affiche le glossaire sur la programmation à l'aide du SatCom. + +- F3 +Pendant l'édition d'un programme, cette touche affiche des +informations complémentaires sur l'instruction en cours de +frappe. + +- F4, F5 et F6 +Choix de la vitesse du jeu. Le mode normal x1 correspond à la +touche F4. Les modes rapides x1.5 et x2 doivent être utilisés +avec prudence, car les ennemis vont également plus vite ! + +- Esc +Quitte la mission en cours. + + +Options - Son +------------- + +- Bruitages +Les bruitages sont générés par l'action en cours. Il s'agit de +bruits de moteur, de pas, d'explosions, etc. + +- Fond sonore +Le fond sonore dépend de la planète. Il donne une ambiance +générale, indépendamment de l'action en cours. Chaque fond +sonore correspond à une piste audio sur le CD. Dans les +exercices, les défis ainsi que sur la lune, il n'y a pas de +fond sonore. + +- Bruitages 3D +Certaines cartes son permettent de localiser un son dans +l'espace à l'aide de 4 haut-parleurs. Le réalisme est alors +superbe. Si votre carte son ne le permet pas, le bouton est +grisé. + + +Créér des exercices +------------------- + +Tous les exercices sont dans le sous-dossier \scene\ : + + \scene\trainxyy.txt -> exercices + \scene\defixyy.txt -> défis + \scene\scenexyy.txt -> missions + \scene\freexyy.txt -> jeux libres + +Le numéro de 3 chiffres est composé de : + + x -> numéro du chapitre (1..9) + yy -> rang dans le chapitre (01..99) + +Par exemple, train102.txt est le deuxième exercice du premier +chapitre. +Lorsque le rang dans le chapitre est 00, c'est qu'il s'agit de +la description du chapitre. +Les fichiers doivent avoir des numéros sucessifs. Supposons par +exemple que les fichiers suivants existent : + + train600.txt + train601.txt + train602.txt + train605.txt + +Le chapitre 6 ne contiendra dans ce cas que 2 exercices, les +numéros 01 et 02. Le numéro 05 n'apparaît pas dans la liste, +car il manque les numéros 03 et 04. + +Description +----------- + +Un fichier de description d'exercice détermine le relief du +terrain, les textures utilisées, la position initiale des +différents robots, matières premières, plantes, etc. + + + +Couleur +------- + +Les couleurs sont spécifiées à l'aide de 4 composantes +rouge/vert/bleu/alpha. Les valeurs sont comprises entre +0 (noir) et 255 (blanc). La composante alpha est généralement +nulle. Par exemple : + + color=175;209;215;0 // bleu-sable + + + + +Title.E text="Power Cell 1" +Nom court de l'exercice, tel qu'il apparaît dans la liste de +gauche. + +Resume.E text="Instruct a bot to change the power cell of a nearby winged shooter." +Résumé de l'exercice, tel qu'il apparaît en dessous des deux +listes. + +ScriptName.E text="Spider2" +Nom par défaut donné à un nouveau programme créé. + +Instructions name="tcell1.txt" +Nom du fichier qui contient les instructions de l'exercice, +qui seront affichées dans le SatCom. +Le fichier ttit1.txt doit être placé dans le dossier help\. + +HelpFile name="cbot.txt" +Non du fichier qui contient les instructions sur la program- +mation, affichées lorsque l'on presse sur F2. +Le fichier cbot.txt doit être placé dans le dossier help\. +Normalement, tous les exercices font référence aux mêmes +instructions générales contenues dans cbot.txt. + +EndingFile win=2 lost=0 +Scène à utiliser lorsque l'exercice est réussi ou raté. +win=2 signifie qu'il faut utiliser scene\win002.txt. +lost=0 signifie qu'il faut utiliser scene\lost000.txt. + +Audio track=0 +Numéro de la piste audio du CD à jouer pendant l'exercice. +Normalement, les exercices restent silencieux, en donnant le +numéro de piste 0. Si nécessaire, il est possible de donner +les numéros suivants : + 2: Terre + 3: Tropica + 4: Crystalium + 5: Saari + 6: Volcano + 7: Centaury + 8: Orphéon + 9: Terranova + +AmbiantColor air=102;102;102;102 water=20;20;20;20 +Couleur ambiante utilisée lorsqu'on est à l'air libre ou +sous l'eau. + +FogColor air=180;222;255;0 water=10;20;100;0 +Couleur que prennent les objets lorsqu'ils sont au loin. + +VehicleColor color=175;209;215;0 +Couleur des robots et des bâtiments. + +DeepView air=100.00 water=25.00 +Distance en mètres jusqu'où porte la vue. Au delà de cette +distance, plus rien n'est affiché. + +FogStart air=0.1 water=0.1 +Plus on s'approche de la distance maximale de vue (DeepView) +et plus la couleur des objets fusionne avec la couleur du +brouillard (FogColor), ce qui simule du brouillard. Une valeur +de 0.1 indique un brouillard qui commence proche du point de +vue, donc un brouillard dense. Une valeur de 0.9 indique un +brouillard très peu dense. + +Par exemple : + DeepView air=100.00 + FogStart air=0.2 + +Distances à partir de l'observateur : +0 à 20 mètres -> affichage normal +20 à 100 mètres -> affichage de plus en plus brouillardeux +100 mètres et plus -> plus rien n'est affiché + +SecondTexture rank=3 +Texture utilisée pour salir les robots et les bâtiments. Vous +pouvez utiliser une valeur comprise entre 1 et 8. + +Background up=76;105;226;0 down=192;250;255;0 +Couleurs du fond d'écran, si aucune texture n'est utilisée. +Un dégradé de couleur passe de "up" tout en haut de l'écran +progressivement jusqu'à "down" en milieu d'écran. La moitié +inférieure de l'écran prend la couleur unie "down". Cette +moitié inférieure n'est en principe jamais visible, puisqu'il +y a toujours une partie de terrain qui la recouvre. + +FrontsizeName image="lens5.tga" +Nom de la texture d'avant-plan, qui contient un effet de +"lens flare", plus ou moins visible selon l'orientation. + + +TerrainRelief image="textures\relief41.bmp" factor=1.0 +Le relief du terrain est décrit dans une image BMP à 256 +niveaux de gris mesurant exactement 161 x 161 pixels. +La couleur blanche correspond à l'altitude la plus basse. +La couleur noire correspond à l'altitude la plus haute. +Normalement, on utilise "factor=1.0". Dans ce cas, les 256 +niveaux permettent de s'élever de 64 mètres. Une différence +d'intensité de gris de 1 correspond donc à une différence +d'altitude de 0.25 mètres. + +La coordonnée du pixel central 80;80 de l'image correspond +à la coordonnée 0;0 dans CoLoBoT. +La coordonnée 0;0 du pixel en haut à gauche dans l'image +correspond au point à l'extrème nord-ouest -400;400 dans CoLoBoT. +Un pixel dans l'image correspond à un carré au sol de 5x5 +mètres dans CoLoBoT. + +Vous pouvez dessiner de nouveaux reliefs avec un logiciel tel +que PaintShop, ou réutiliser les nombreux fichiers reliefxx.bmp +placés dans le dossier textures\. + +TerrainResource image="textures\res00.bmp" +Cette image détermine la présence des ressources dans le +sous-sol. Il s'agit d'une image BMP en 256 couleurs de +161 x 161 pixels. + +Rouge = 255;0;0 (index=5) -> titanium +Vert = 0;255;0 (index=30) -> énergie +Jaune = 255;255;0 (index=35) -> uranium + +Toutes les autres couleurs ou niveaux de gris sont ignorés. + +Généralement, un bon truc pour placer les zones de couleur au +bon endroit est de partir de l'image à niveaux de gris du +relief et de la convertir en 256 couleurs. + +TerrainWater level=7.5 ... +Cette commande contient plusieurs paramètres, dont seul "level" +nous intéresse ici. "level" indique donc le niveau de l'eau ou +de la lave, par-rapport au niveau zéro qui correspond à la +couleur blanche dans l'image du relief (TerrainResource). +L'altitude des robots est par la suite toujours calculée par- +rapport au niveau de la mer (level). Une altitude négative +indiquera donc que le robot est sous l'eau. + +BeginObject +Cette commande doit précéder le premier CreateObject. + +CreateObject pos=7;-10 dir=1.5 type=Me +Création d'un objet dans l'exercice. Il peut s'agir d'un robot, +d'un bâtiment, d'une matière première, d'une plante, etc. + +Pour déterminer la position d'un objet, un bon moyen consiste +à déplacer le cosmonaute à l'endroit souhaité, puis de taper +les commandes : + + Ctrl+Pause showstat Entrée + Ctrl+Pause showpos Entrée + +La partie inférieure de l'écran indique alors les coordonnées +de l'objet sélectionné, qu'il n'y a plus qu'à reporter dans le +fichier de description de l'exercice. + +La direction est un nombre compris entre 0 et 2. + 0.0 -> est + 0.5 -> sud + 1.0 -> ouest + 1.5 -> nord +Le sens de rotation est donc horaire. + +Les différents types possibles pour les objets sont : + +Base : + type=Me // cosmonaute + type=SpaceShip + +Robots : + type=PracticeBot // robot d'entraînement + type=TargetBot // robot cible + + type=WheeledGrabber + type=TrackedGrabber + type=WingedGrabber + type=LeggedGrabber + + type=WheeledShooter + type=TrackedShooter + type=WingedShooter + type=LeggedShooter + + type=WheeledOrgaShooter + type=TrackedOrgaShooter + type=WingedOrgaShooter + type=LeggedOrgaShooter + + type=WheeledSniffer + type=TrackedSniffer + type=WingedSniffer + type=LeggedSniffer + + type=Thumper + type=PhazerShooter + type=Recycler + type=Shielder + type=Subber + +Bâtiments : + type=Derrick + type=BotFactory + type=PowerStation + type=Converter + type=RepairCenter + type=DefenseTower + type=AlienNest + type=ResearchCenter + type=RadarStation + type=ExchangePost + type=PowerPlant + type=AutoLab + type=NuclearPlant + type=PowerCaptor + type=Vault + type=StartArea + type=GoalArea + type=Target1 // pour l'entraînement au vol + type=Target2 + type=Houston // centre de contrôle + +Objets transportables : + type=TitaniumOre + type=UraniumOre + type=Titanium + type=PowerCell + type=NuclearCell + type=OrgaMatter + type=BlackBox // boîte noire + type=KeyA..D + type=TNT // caisse d'explosif + +Plantes et décors : + type=Greenery0..4 // plante standard basse + type=Greenery5..7 // petit trèfle bas + type=Greenery10..14 // plante grasse montante + type=Greenery15..19 // fougère + type=Tree0..3 // arbre haut + type=Mushroom1 // champignon inoffensif + type=Mushroom2 // champignon corrosif + type=MegaStalk0..5 // plante étrange + + type=Quartz0..3 // quartz petit..grand + type=Barrier0 // barrière courte + type=Barrier1 // barrière longue + type=ApolloLEM // sur la lune uniquement : + type=ApolloJeep + type=ApolloFlag + type=ApolloModule + type=ApolloAntenna + +Epaves de robots recyclables : + type=WreckBotw1..2 // robot à roues + type=WreckBott1..2 // robot à petites chenilles + type=WreckBotr1..2 // robot à grosses chenilles + +Bâtiments en ruine : + type=RuinBotFactory + type=RuinDoor // porte de convertisseur + type=RuinSupport // support de radar + type=RuinRadar // socle de radar + type=RuinConvert + type=RuinBaseCamp // socle du vaisseau spatial + type=RuinHeadCamp // coiffe du vaisseau spatial + +Ennemis : + type=AlienQueen + type=AlienEgg + type=AlienAnt + type=AlienSpider + type=AlienWasp + type=AlienWorm + +Indicateurs : + type=PowerSpot // indique la présence d'énergie en sous-sol + type=TitaniumSpot // indique la présence de titanium en sous-sol + type=UraniumSpot // indique la présence d'uranium en sous-sol + type=KeyA..DSpot // indique la présence de clé en sous-sol + type=WayPoint // croix pour les exercices + type=BlueFlag + type=RedFlag + type=GreenFlag + type=YellowFlag + type=VioletFlag + +Divers : + type=Mine // bombe fixe à éviter + type=Portico // portique géant (sur la terre) + type=Bag // sac de survie + type=Home // petite maison sympa (sur terranova) + type=Tech // technicien de Houston + type=Firework // feu d'artifice + +La commande CreateObject peut contenir des paramètres +supplémentaires : + +CreateObject ... script1="ttit1.txt" +Nom du programme CBOT à charger à la position 1 dans le robot +ou l'insecte. Il est possible de charger jusqu'à 10 programmes +en utilisant les commandes script1 à script10. +Les fichiers .txt des programmes doivent être placés dans le +dossier script\. + +CreateObject ... run=1 +Numéro du programme à exécuter directement lorsque l'exercice +démarre. Cela peut être utile, par exemple, pour un robot +TargetBot que l'élève devra suivre. +Si plusieurs programmes sont chargés (avec script1, script2, +etc.), un seul pourra être exécuté, bien entendu. + +EnableResearch type=WINGER +Liste des recherches déjà effectuées. + +DoneResearch type=WINGER +Liste des recherches qu'il est autorisé de faire, en construisant +un centre de recherche (ResearchCenter) ou un laboratoire (AutoLab). + + + TRACKER Robots Tracked* + WINGER Robots Winged* + THUMPER Robots Thumper + SHOOTER Robots *Shooter + TOWER Bâtiment DefenseTower + PHAZER Robots PhazerShooter + SHIELDER Robots Shielder + ATOMIC Bâtiment NuclearPlant + + iPAW Robots Legged* + iGUN Robots *OrgaShooter + + RECYCLER Robots recycleurs + SUBBER Robots Subber + SNIFFER Robots *Sniffer + + +EndMissionTake pos=0.00;0.00 dist=25000.00 type=Me lost=0 +EndMissionTake pos=0.00;0.00 dist=25000.00 type=WheeledGrabber lost=0 +EndMissionTake pos=0.00;0.00 dist=1000.00 type=Titanium min=1 max=1 +Critères pour déterminer à quel moment l'exercice est terminé. + + + + +Dossiers et réseau +------------------ + +Dans une utilisation en réseau, COLOBOT doit être installé +sur chaque machine individuellement. Après cette opération, +les exercices et les missions sont chargés localement, +généralement dans le dossier : + + C:\Program Files\Colobot\scene\ + +Si vous avez créé des exercices spécifiques, il peut être +utile de les charger à partir d'un dossier central commun à +tous les ordinateurs. Pour cela, il faut modifier le fichier + + C:\Program Files\Colobot\colobot.ini + +sur chaque machine. La section suivante permet de donner le +chemin d'accès : + + [Directory] + scene=scene + +Ici, il s'agit d'un chemin d'accès relatif. Vous pouvez par +exemple le changer en un chemin absolu sur un serveur : + + [Directory] + scene=\\Serveur\c\colobot\scene\ + +De la même façon, vous pouvez changer le dossier dans lequel +sont placés les programmes lorsque vous utilisez la commande +ouvrir/enregistrer avec le mode "public", dans l'éditeur de +programmes : + + [Directory] + public=program + +En donnant un chemin d'accès commun à tous les ordinateurs, il +sera possible d'échanger des programmes : + + [Directory] + public=\\Serveur\c\colobot\program\ + + + +Réglages +-------- + +Pour afficher le nombre d'images par seconde, il faut appuyer +sur Ctrl+Pause puis taper la commande "showstat" et valider +avec la touche Entrée : + + Ctrl+Pause showstat Entrée + +La partie supérieure de l'écran affiche alors, par exemple : + + 32.46 fps T=11558 (640x480x16) + +Le premier chiffre correspond au nombre d'images par seconde +(fps = frame per second). +Le deuxième chiffre indique le nombre de triangles affiché dans +la scène. +Les 3 derniers chiffres entre parenthèses sont la résolution +(largeur x hauteur) et le nombre de bits pour les couleurs. + +Tous les réglages sont mémorisés dans le fichier colobot.ini, +présent dans le dossier principal où COLOBOT a été installé. +Ce fichier peut être modifié (avec prudence et après en avoir +fait une copie) avec un éditeur de texte, comme par exemple le +bloc-notes de Windows. + + +Problèmes +--------- + +Pour résoudre certains problèmes, il est possible de modifier +le fichier colobot.ini avec un éditeur de texte (par exemple +avec le bloc-notes de Windows). N'ajoutez pas de nouvelles +lignes, mais modifiez simplement les valeurs existantes. +Attention à ne pas insérer d'espace. Il faut quitter COLOBOT +avant de modifier le fichier. + +Si la végétation s'affiche mal, ou même pas du tout, vous +avez peut-être mis à zéro le nombre d'objets décoratifs dans +les options. Pour remettre 100% : + [Setup] + GadgetQuantity=1.00 +Si la végétation ne s'affiche toujours pas, essayez : + [Engine] + AlphaMode=0 +ou + [Engine] + AlphaMode=2 + +Si un carré apparaît autour des ombres, essayez : + [Engine] + WhiteSrcBlend=9 + WhiteDestBlend=6 +ou + [Engine] + WhiteSrcBlend=6 + WhiteDestBlend=3 +Si cela ne fonctionne pas, il faut supprimer les ombres : + [Engine] + WhiteSrcBlend=0 + WhiteDestBlend=0 + [Setup] + GroundShadow=0 + +Lorsque un objet s'interpose entre l'objet sélectionné et la +caméra, il devient transparent. Si l'objet n'est pas assez +transparent, essayez : + [Engine] + StateColor=0 +ou + [Engine] + StateColor=1 + + +Equipe de développement +----------------------- + +- Daniel Roux +- Denis Dumoulin +- Otto Kölbl +- Michael Walz +- Didier Gertsch + +Beta testeurs +------------- + +- Adrien Roux +- Didier Raboud +- Nicolas Beuchat +- Joël Roux +- Michael Jubin +- Daniel Sauthier +- Nicolas Stubi +- Patrick Thévoz + +Copyright +--------- + +La photo de la nébuleuse NGC3603 servant de fond pour la planète +Orphéon a été prise avec le télescope spatial Hubble. Elle est +utilisée avec l'autorisation des auteurs Wolfgang Brandner +(JPL/IPAC), Eva K. Grebel (Université de Washington), You-Hua Chu +(Université d'Illinois Urbana-Champaign) et de la NASA. + +Le son de tonnerre de la planète Orphéon est utilisé avec +l'autorisation limitée de CREATIVE moyennant la mention : +Material from products are used by limited permission from CREATIVE. + +Développeur +----------- + +EPSITEC SA +Mouette 5 +CH-1092 Belmont + +colobot@epsitec.ch +www.colobot.com + +Editeur de la version française +------------------------------- + +ALSYD +43, Ch. du vieux Chêne +F-38240 Meylan + +www.alsyd.com diff --git a/src/resource.h b/src/resource.h new file mode 100644 index 00000000..0daaca63 --- /dev/null +++ b/src/resource.h @@ -0,0 +1,39 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by winmain.rc +// +#define IDI_MAIN_ICON 101 +#define IDR_MAIN_ACCEL 113 +#define IDR_MENU 141 +#define IDR_POPUP 142 +#define IDD_ABOUT 143 +#define IDD_CHANGEDEVICE 144 +#define IDC_CURSORHAND 149 +#define IDC_CURSORSCROLLL 150 +#define IDC_CURSORSCROLLR 151 +#define IDC_CURSORSCROLLU 152 +#define IDC_CURSORSCROLLD 153 +#define IDC_CURSORTARGET 154 +#define IDC_DEVICE_COMBO 1000 +#define IDC_MODE_COMBO 1001 +#define IDC_WINDOWED_CHECKBOX 1012 +#define IDC_STEREO_CHECKBOX 1013 +#define IDC_FULLSCREEN_TEXT 1014 +#define IDM_ABOUT 40001 +#define IDM_CHANGEDEVICE 40002 +#define IDM_TOGGLEFULLSCREEN 40003 +#define IDM_TOGGLESTART 40004 +#define IDM_SINGLESTEP 40005 +#define IDM_EXIT 40006 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 159 +#define _APS_NEXT_COMMAND_VALUE 40011 +#define _APS_NEXT_CONTROL_VALUE 1015 +#define _APS_NEXT_SYMED_VALUE 102 +#endif +#endif diff --git a/src/restext-old.cpp b/src/restext-old.cpp new file mode 100644 index 00000000..073692dd --- /dev/null +++ b/src/restext-old.cpp @@ -0,0 +1,2527 @@ +// restext.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include "struct.h" +#include "D3DEngine.h" +#include "language.h" +#include "misc.h" +#include "event.h" +#include "object.h" +#include "cbot\resource.h" +#include "restext.h" + + + + +// Donne le pointeur au moteur. + +void SetEngine(CD3DEngine *engine) +{ + g_engine = engine; +} + +// Donne le nom du joueur. + +void SetGlobalGamerName(char *name) +{ + strcpy(g_gamerName, name); +} + + + +typedef struct +{ + KeyRank key; + char name[20]; +} +KeyDesc; + +static KeyDesc keyTable[22] = +{ + { KEYRANK_LEFT, "left;" }, + { KEYRANK_RIGHT, "right;" }, + { KEYRANK_UP, "up;" }, + { KEYRANK_DOWN, "down;" }, + { KEYRANK_GUP, "gup;" }, + { KEYRANK_GDOWN, "gdown;" }, + { KEYRANK_CAMERA, "camera;" }, + { KEYRANK_DESEL, "desel;" }, + { KEYRANK_ACTION, "action;" }, + { KEYRANK_NEAR, "near;" }, + { KEYRANK_AWAY, "away;" }, + { KEYRANK_NEXT, "next;" }, + { KEYRANK_HUMAN, "human;" }, + { KEYRANK_QUIT, "quit;" }, + { KEYRANK_HELP, "help;" }, + { KEYRANK_PROG, "prog;" }, + { KEYRANK_CBOT, "cbot;" }, + { KEYRANK_VISIT, "visit;" }, + { KEYRANK_SPEED10, "speed10;" }, + { KEYRANK_SPEED15, "speed15;" }, + { KEYRANK_SPEED20, "speed20;" }, + { KEYRANK_SPEED30, "speed30;" }, +}; + +// Cherche une touche. + +BOOL SearchKey(char *cmd, KeyRank &key) +{ + int i; + + for ( i=0 ; i<22 ; i++ ) + { + if ( strstr(cmd, keyTable[i].name) == cmd ) + { + key = keyTable[i].key; + return TRUE; + } + } + return FALSE; +} + +// Remplace les commandes "\key name;" dans un texte. + +void PutKeyName(char* dst, char* src) +{ + KeyRank key; + char name[50]; + int s, d, n, res; + + s = d = 0; + while ( src[s] != 0 ) + { + if ( src[s+0] == '\\' && + src[s+1] == 'k' && + src[s+2] == 'e' && + src[s+3] == 'y' && + src[s+4] == ' ' ) + { + if ( SearchKey(src+s+5, key) ) + { + res = g_engine->RetKey(key, 0); + if ( res != 0 ) + { + if ( GetResource(RES_KEY, res, name) ) + { + n = 0; + while ( name[n] != 0 ) + { + dst[d++] = name[n++]; + } + while ( src[s++] != ';' ); + continue; + } + } + } + } + + dst[d++] = src[s++]; + } + dst[d++] = 0; +} + + +// Retourne le texte d'une ressource. + +BOOL GetResource(ResType type, int num, char* text) +{ + char buffer[100]; + + if ( !GetResourceBase(type, num, buffer) ) + { + text[0] = 0; + return FALSE; + } + + PutKeyName(text, buffer); + return TRUE; +} + + +// Retourne le texte d'une ressource. + +BOOL GetResourceBase(ResType type, int num, char* text) +{ + text[0] = 0; + +#if _ENGLISH + if ( type == RES_TEXT ) + { + #if _FULL + if ( num == RT_VERSION_ID ) strcpy(text, "Version 1.8 /e"); + #endif + #if _NET | _SCHOOL + if ( num == RT_VERSION_ID ) strcpy(text, "School 1.8 /e"); + #endif + #if _DEMO + if ( num == RT_VERSION_ID ) strcpy(text, "Demo 1.8 /e"); + #endif + if ( num == RT_DISINFO_TITLE ) strcpy(text, "SatCom"); + if ( num == RT_WINDOW_MAXIMIZED ) strcpy(text, "Maximize"); + if ( num == RT_WINDOW_MINIMIZED ) strcpy(text, "Minimize"); + if ( num == RT_WINDOW_STANDARD ) strcpy(text, "Normal size"); + if ( num == RT_WINDOW_CLOSE ) strcpy(text, "Close"); + + if ( num == RT_STUDIO_TITLE ) strcpy(text, "Program editor"); + if ( num == RT_SCRIPT_NEW ) strcpy(text, "New"); + if ( num == RT_NAME_DEFAULT ) strcpy(text, "Player"); + if ( num == RT_IO_NEW ) strcpy(text, "New ..."); + if ( num == RT_KEY_OR ) strcpy(text, " or "); + + if ( num == RT_TITLE_BASE ) strcpy(text, "COLOBOT"); + if ( num == RT_TITLE_INIT ) strcpy(text, "COLOBOT"); + if ( num == RT_TITLE_TRAINER ) strcpy(text, "Programming exercises"); + if ( num == RT_TITLE_DEFI ) strcpy(text, "Challenges"); + if ( num == RT_TITLE_MISSION ) strcpy(text, "Missions"); + if ( num == RT_TITLE_FREE ) strcpy(text, "Free game"); + if ( num == RT_TITLE_USER ) strcpy(text, "User levels"); + if ( num == RT_TITLE_PROTO ) strcpy(text, "Prototypes"); + if ( num == RT_TITLE_SETUP ) strcpy(text, "Options"); + if ( num == RT_TITLE_NAME ) strcpy(text, "Player name"); + if ( num == RT_TITLE_PERSO ) strcpy(text, "Customize your appearance"); + if ( num == RT_TITLE_WRITE ) strcpy(text, "Save the current mission"); + if ( num == RT_TITLE_READ ) strcpy(text, "Load a saved mission"); + + if ( num == RT_PLAY_CHAPt ) strcpy(text, " Chapters:"); + if ( num == RT_PLAY_CHAPd ) strcpy(text, " Chapters:"); + if ( num == RT_PLAY_CHAPm ) strcpy(text, " Planets:"); + if ( num == RT_PLAY_CHAPf ) strcpy(text, " Planets:"); + if ( num == RT_PLAY_CHAPu ) strcpy(text, " User levels:"); + if ( num == RT_PLAY_CHAPp ) strcpy(text, " Planets:"); + if ( num == RT_PLAY_LISTt ) strcpy(text, " Exercises in the chapter:"); + if ( num == RT_PLAY_LISTd ) strcpy(text, " Challenges in the chapter:"); + if ( num == RT_PLAY_LISTm ) strcpy(text, " Missions on this planet:"); + if ( num == RT_PLAY_LISTf ) strcpy(text, " Free game on this planet:"); + if ( num == RT_PLAY_LISTu ) strcpy(text, " Missions on this level:"); + if ( num == RT_PLAY_LISTp ) strcpy(text, " Prototypes on this planet:"); + if ( num == RT_PLAY_RESUME ) strcpy(text, " Summary:"); + + if ( num == RT_SETUP_DEVICE ) strcpy(text, " Drivers:"); + if ( num == RT_SETUP_MODE ) strcpy(text, " Resolution:"); + if ( num == RT_SETUP_KEY1 ) strcpy(text, "1) First click on the key you want to redefine."); + if ( num == RT_SETUP_KEY2 ) strcpy(text, "2) Then push the key you want to use instead."); + + if ( num == RT_PERSO_FACE ) strcpy(text, "Face type:"); + if ( num == RT_PERSO_GLASSES ) strcpy(text, "Eyeglasses:"); + if ( num == RT_PERSO_HAIR ) strcpy(text, "Hair color:"); + if ( num == RT_PERSO_COMBI ) strcpy(text, "Suit color:"); + if ( num == RT_PERSO_BAND ) strcpy(text, "Strip color:"); + + if ( num == RT_DIALOG_TITLE ) strcpy(text, "COLOBOT"); + if ( num == RT_DIALOG_ABORT ) strcpy(text, "Quit the mission?"); + if ( num == RT_DIALOG_QUIT ) strcpy(text, "Do you want to quit COLOBOT ?"); + if ( num == RT_DIALOG_YES ) strcpy(text, "Abort\\Abort the current mission"); + if ( num == RT_DIALOG_NO ) strcpy(text, "Continue\\Continue the current mission"); + if ( num == RT_DIALOG_YESQUIT ) strcpy(text, "Quit\\Quit COLOBOT"); + if ( num == RT_DIALOG_NOQUIT ) strcpy(text, "Continue\\Continue the game"); + if ( num == RT_DIALOG_DELOBJ ) strcpy(text, "Do you really want to destroy the selected building?"); + if ( num == RT_DIALOG_DELGAME ) strcpy(text, "Do you want to delete %s's saved games? "); + if ( num == RT_DIALOG_YESDEL ) strcpy(text, "Delete"); + if ( num == RT_DIALOG_NODEL ) strcpy(text, "Cancel"); + if ( num == RT_DIALOG_LOADING ) strcpy(text, "LOADING"); + + if ( num == RT_STUDIO_LISTTT ) strcpy(text, "Keyword help(\\key cbot;)"); + if ( num == RT_STUDIO_COMPOK ) strcpy(text, "Compilation ok (0 errors)"); + if ( num == RT_STUDIO_PROGSTOP ) strcpy(text, "Program finished"); + + if ( num == RT_SATCOM_LIST ) strcpy(text, "\\b;List of objects\n"); + if ( num == RT_SATCOM_BOT ) strcpy(text, "\\b;Robots\n"); + if ( num == RT_SATCOM_BUILDING ) strcpy(text, "\\b;Buildings\n"); + if ( num == RT_SATCOM_FRET ) strcpy(text, "\\b;Moveable objects\n"); + if ( num == RT_SATCOM_ALIEN ) strcpy(text, "\\b;Aliens\n"); + if ( num == RT_SATCOM_NULL ) strcpy(text, "\\c; (none)\\n;\n"); + if ( num == RT_SATCOM_ERROR1 ) strcpy(text, "\\b;Error\n"); + if ( num == RT_SATCOM_ERROR2 ) strcpy(text, "The list is only available if a \\l;radar station\\u object\\radar; is working.\n"); + + if ( num == RT_IO_OPEN ) strcpy(text, "Open"); + if ( num == RT_IO_SAVE ) strcpy(text, "Save"); + if ( num == RT_IO_LIST ) strcpy(text, "Folder: %s"); + if ( num == RT_IO_NAME ) strcpy(text, "Name:"); + if ( num == RT_IO_DIR ) strcpy(text, "Folder:"); + if ( num == RT_IO_PRIVATE ) strcpy(text, "Private\\Private folder"); + if ( num == RT_IO_PUBLIC ) strcpy(text, "Public\\Common folder"); + + if ( num == RT_GENERIC_DEV1 ) strcpy(text, "Developed by :"); + if ( num == RT_GENERIC_DEV2 ) strcpy(text, "www.epsitec.com"); +//? if ( num == RT_GENERIC_EDIT1 ) strcpy(text, "English version published by:"); +//? if ( num == RT_GENERIC_EDIT2 ) strcpy(text, "www.?.com"); + if ( num == RT_GENERIC_EDIT1 ) strcpy(text, " "); + if ( num == RT_GENERIC_EDIT2 ) strcpy(text, " "); + } + + if ( type == RES_EVENT ) + { + if ( num == EVENT_BUTTON_OK ) strcpy(text, "OK"); + if ( num == EVENT_BUTTON_CANCEL ) strcpy(text, "Cancel"); + if ( num == EVENT_BUTTON_NEXT ) strcpy(text, "Next"); + if ( num == EVENT_BUTTON_PREV ) strcpy(text, "Previous"); + if ( num == EVENT_BUTTON_QUIT ) strcpy(text, "Menu (\\key quit;)"); + + if ( num == EVENT_DIALOG_OK ) strcpy(text, "OK"); + if ( num == EVENT_DIALOG_CANCEL ) strcpy(text, "Cancel"); + + if ( num == EVENT_INTERFACE_TRAINER) strcpy(text, "Exercises\\Programming exercises"); + if ( num == EVENT_INTERFACE_DEFI ) strcpy(text, "Challenges\\Programming challenges"); + if ( num == EVENT_INTERFACE_MISSION) strcpy(text, "Missions\\Select mission"); + if ( num == EVENT_INTERFACE_FREE ) strcpy(text, "Free game\\Free game without precise goal"); + if ( num == EVENT_INTERFACE_USER ) strcpy(text, "User\\User levels"); + if ( num == EVENT_INTERFACE_PROTO ) strcpy(text, "Proto\\Prototypes under development"); + if ( num == EVENT_INTERFACE_NAME ) strcpy(text, "New player\\Choose player name"); + if ( num == EVENT_INTERFACE_SETUP ) strcpy(text, "Options\\Preferences"); + if ( num == EVENT_INTERFACE_AGAIN ) strcpy(text, "Restart\\Restart the mission from the beginning"); + if ( num == EVENT_INTERFACE_WRITE ) strcpy(text, "Save\\Save the current mission "); + if ( num == EVENT_INTERFACE_READ ) strcpy(text, "Load\\Load a saved mission"); + if ( num == EVENT_INTERFACE_ABORT ) strcpy(text, "\\Return to COLOBOT"); + if ( num == EVENT_INTERFACE_QUIT ) strcpy(text, "Quit\\Quit COLOBOT"); + if ( num == EVENT_INTERFACE_BACK ) strcpy(text, "<< Back \\Back to the previous screen"); + if ( num == EVENT_INTERFACE_PLAY ) strcpy(text, "Play\\Start mission!"); + if ( num == EVENT_INTERFACE_SETUPd ) strcpy(text, "Device\\Driver and resolution settings"); + if ( num == EVENT_INTERFACE_SETUPg ) strcpy(text, "Graphics\\Graphics settings"); + if ( num == EVENT_INTERFACE_SETUPp ) strcpy(text, "Game\\Game settings"); + if ( num == EVENT_INTERFACE_SETUPc ) strcpy(text, "Controls\\Keyboard, joystick and mouse settings"); + if ( num == EVENT_INTERFACE_SETUPs ) strcpy(text, "Sound\\Music and game sound volume"); + if ( num == EVENT_INTERFACE_DEVICE ) strcpy(text, "Unit"); + if ( num == EVENT_INTERFACE_RESOL ) strcpy(text, "Resolution"); + if ( num == EVENT_INTERFACE_FULL ) strcpy(text, "Full screen\\Full screen or window mode"); + if ( num == EVENT_INTERFACE_APPLY ) strcpy(text, "Apply changes\\Activates the changed settings"); + + if ( num == EVENT_INTERFACE_TOTO ) strcpy(text, "Robbie\\Your assistant"); + if ( num == EVENT_INTERFACE_SHADOW ) strcpy(text, "Shadows\\Shadows on the ground"); + if ( num == EVENT_INTERFACE_GROUND ) strcpy(text, "Marks on the ground\\Marks on the ground"); + if ( num == EVENT_INTERFACE_DIRTY ) strcpy(text, "Dust\\Dust and dirt on bots and buildings"); + if ( num == EVENT_INTERFACE_FOG ) strcpy(text, "Fog\\Fog"); + if ( num == EVENT_INTERFACE_LENS ) strcpy(text, "Sunbeams\\Sunbeams in the sky"); + if ( num == EVENT_INTERFACE_SKY ) strcpy(text, "Sky\\Clouds and nebulae"); + if ( num == EVENT_INTERFACE_PLANET ) strcpy(text, "Planets and stars\\Astronomical objects in the sky"); + if ( num == EVENT_INTERFACE_LIGHT ) strcpy(text, "Dynamic lighting\\Mobile light sources"); + if ( num == EVENT_INTERFACE_PARTI ) strcpy(text, "Number of particles\\Explosions, dust, reflections, etc."); + if ( num == EVENT_INTERFACE_CLIP ) strcpy(text, "Depth of field\\Maximum visibility"); + if ( num == EVENT_INTERFACE_DETAIL ) strcpy(text, "Details\\Visual quality of 3D objects"); + if ( num == EVENT_INTERFACE_TEXTURE) strcpy(text, "Textures\\Quality of textures "); + if ( num == EVENT_INTERFACE_GADGET ) strcpy(text, "Number of decorative objects \\Number of purely ornamental objects"); + if ( num == EVENT_INTERFACE_RAIN ) strcpy(text, "Particles in the interface\\Steam clouds and sparks in the interface"); + if ( num == EVENT_INTERFACE_GLINT ) strcpy(text, "Reflections on the buttons \\Shiny buttons"); + if ( num == EVENT_INTERFACE_TOOLTIP) strcpy(text, "Help balloons\\Explain the function of the buttons"); + if ( num == EVENT_INTERFACE_MOVIES ) strcpy(text, "Film sequences\\Films before and after the missions"); + if ( num == EVENT_INTERFACE_NICERST) strcpy(text, "Exit film\\Film at the exit of exercises"); + if ( num == EVENT_INTERFACE_HIMSELF) strcpy(text, "Friendly fire\\Your shooting can damage your own objects "); + if ( num == EVENT_INTERFACE_SCROLL ) strcpy(text, "Scrolling\\Scrolling when the mouse touches right or left border"); + if ( num == EVENT_INTERFACE_INVERTX) strcpy(text, "Mouse inversion X\\Inversion of the scrolling direction on the X axis"); + if ( num == EVENT_INTERFACE_INVERTY) strcpy(text, "Mouse inversion Y\\Inversion of the scrolling direction on the Y axis"); + if ( num == EVENT_INTERFACE_EFFECT ) strcpy(text, "Quake at explosions\\The screen shakes at explosions"); + if ( num == EVENT_INTERFACE_MOUSE ) strcpy(text, "Mouse shadow\\Gives the mouse a shadow"); + if ( num == EVENT_INTERFACE_EDITMODE) strcpy(text, "Automatic indent\\When program editing"); + if ( num == EVENT_INTERFACE_EDITVALUE)strcpy(text, "Big indent\\Indent 2 or 4 spaces per level defined by braces"); + + if ( num == EVENT_INTERFACE_KDEF ) strcpy(text, "Standard controls\\Standard key functions"); + if ( num == EVENT_INTERFACE_KLEFT ) strcpy(text, "Turn left\\turns the bot to the left"); + if ( num == EVENT_INTERFACE_KRIGHT ) strcpy(text, "Turn right\\turns the bot to the right"); + if ( num == EVENT_INTERFACE_KUP ) strcpy(text, "Forward\\Moves forward"); + if ( num == EVENT_INTERFACE_KDOWN ) strcpy(text, "Backward\\Moves backward"); + if ( num == EVENT_INTERFACE_KGUP ) strcpy(text, "Climb\\Increases the power of the jet"); + if ( num == EVENT_INTERFACE_KGDOWN ) strcpy(text, "Descend\\Reduces the power of the jet"); + if ( num == EVENT_INTERFACE_KCAMERA) strcpy(text, "Change camera\\Switches between onboard camera and following camera"); + if ( num == EVENT_INTERFACE_KDESEL ) strcpy(text, "Previous object\\Selects the previous object"); + if ( num == EVENT_INTERFACE_KACTION) strcpy(text, "Standard action\\Standard action of the bot (take/grab, shoot, sniff, etc)"); + if ( num == EVENT_INTERFACE_KNEAR ) strcpy(text, "Camera closer\\Moves the camera forward"); + if ( num == EVENT_INTERFACE_KAWAY ) strcpy(text, "Camera back\\Moves the camera backward"); + if ( num == EVENT_INTERFACE_KNEXT ) strcpy(text, "Next object\\Selects the next object"); + if ( num == EVENT_INTERFACE_KHUMAN ) strcpy(text, "Select the astronaut\\Selects the astronaut"); + if ( num == EVENT_INTERFACE_KQUIT ) strcpy(text, "Quit\\Quit the current mission or exercise"); + if ( num == EVENT_INTERFACE_KHELP ) strcpy(text, "Instructions\\Shows the instructions for the current mission"); + if ( num == EVENT_INTERFACE_KPROG ) strcpy(text, "Programming help\\Gives more detailed help with programming"); + if ( num == EVENT_INTERFACE_KCBOT ) strcpy(text, "Key word help\\More detailed help about key words"); + if ( num == EVENT_INTERFACE_KVISIT ) strcpy(text, "Origin of last message\\Shows where the last message was sent from"); + if ( num == EVENT_INTERFACE_KSPEED10) strcpy(text, "Speed 1.0x\\Normal speed"); + if ( num == EVENT_INTERFACE_KSPEED15) strcpy(text, "Speed 1.5x\\1.5 times faster"); + if ( num == EVENT_INTERFACE_KSPEED20) strcpy(text, "Speed 2.0x\\Double speed"); + if ( num == EVENT_INTERFACE_KSPEED30) strcpy(text, "Speed 3.0x\\Three times faster"); + + if ( num == EVENT_INTERFACE_VOLSOUND) strcpy(text, "Sound effects:\\Volume of engines, voice, shooting, etc."); + if ( num == EVENT_INTERFACE_VOLMUSIC) strcpy(text, "Background sound :\\Volume of audio tracks on the CD"); + if ( num == EVENT_INTERFACE_SOUND3D) strcpy(text, "3D sound\\3D positioning of the sound"); + + if ( num == EVENT_INTERFACE_MIN ) strcpy(text, "Lowest\\Minimum graphic quality (highest frame rate)"); + if ( num == EVENT_INTERFACE_NORM ) strcpy(text, "Normal\\Normal graphic quality"); + if ( num == EVENT_INTERFACE_MAX ) strcpy(text, "Highest\\Highest graphic quality (lowest frame rate)"); + + if ( num == EVENT_INTERFACE_SILENT ) strcpy(text, "Silent\\No sound"); + if ( num == EVENT_INTERFACE_NOISY ) strcpy(text, "Normal\\Normal sound volume"); + + if ( num == EVENT_INTERFACE_JOYSTICK) strcpy(text, "Use a joystick\\Joystick or keyboard"); + if ( num == EVENT_INTERFACE_SOLUCE ) strcpy(text, "Access to solution\\Shows the solution (detailed instructions for missions)"); + + if ( num == EVENT_INTERFACE_NEDIT ) strcpy(text, "\\New player name"); + if ( num == EVENT_INTERFACE_NOK ) strcpy(text, "OK\\Choose the selected player"); + if ( num == EVENT_INTERFACE_NCANCEL) strcpy(text, "Cancel\\Keep current player name"); + if ( num == EVENT_INTERFACE_NDELETE) strcpy(text, "Delete player\\Deletes the player from the list"); + if ( num == EVENT_INTERFACE_NLABEL ) strcpy(text, "Player name"); + + if ( num == EVENT_INTERFACE_IOWRITE) strcpy(text, "Save\\Saves the current mission"); + if ( num == EVENT_INTERFACE_IOREAD ) strcpy(text, "Load\\Loads the selected mission"); + if ( num == EVENT_INTERFACE_IOLIST ) strcpy(text, "List of saved missions"); + if ( num == EVENT_INTERFACE_IOLABEL) strcpy(text, "Filename:"); + if ( num == EVENT_INTERFACE_IONAME ) strcpy(text, "Mission name"); + if ( num == EVENT_INTERFACE_IOIMAGE) strcpy(text, "Photography"); + if ( num == EVENT_INTERFACE_IODELETE) strcpy(text, "Delete\\Deletes the selected file"); + + if ( num == EVENT_INTERFACE_PERSO ) strcpy(text, "Appearance\\Choose your appearance"); + if ( num == EVENT_INTERFACE_POK ) strcpy(text, "OK"); + if ( num == EVENT_INTERFACE_PCANCEL) strcpy(text, "Cancel"); + if ( num == EVENT_INTERFACE_PDEF ) strcpy(text, "Standard\\Standard appearance settings"); + if ( num == EVENT_INTERFACE_PHEAD ) strcpy(text, "Head\\Face and hair"); + if ( num == EVENT_INTERFACE_PBODY ) strcpy(text, "Suit\\Astronaut suit"); + if ( num == EVENT_INTERFACE_PLROT ) strcpy(text, "\\Turn left"); + if ( num == EVENT_INTERFACE_PRROT ) strcpy(text, "\\Turn right"); + if ( num == EVENT_INTERFACE_PCRa ) strcpy(text, "Red"); + if ( num == EVENT_INTERFACE_PCGa ) strcpy(text, "Green"); + if ( num == EVENT_INTERFACE_PCBa ) strcpy(text, "Blue"); + if ( num == EVENT_INTERFACE_PCRb ) strcpy(text, "Red"); + if ( num == EVENT_INTERFACE_PCGb ) strcpy(text, "Green"); + if ( num == EVENT_INTERFACE_PCBb ) strcpy(text, "Blue"); + if ( num == EVENT_INTERFACE_PFACE1 ) strcpy(text, "\\Face 1"); + if ( num == EVENT_INTERFACE_PFACE2 ) strcpy(text, "\\Face 4"); + if ( num == EVENT_INTERFACE_PFACE3 ) strcpy(text, "\\Face 3"); + if ( num == EVENT_INTERFACE_PFACE4 ) strcpy(text, "\\Face 2"); + if ( num == EVENT_INTERFACE_PGLASS0) strcpy(text, "\\No eyeglasses"); + if ( num == EVENT_INTERFACE_PGLASS1) strcpy(text, "\\Eyeglasses 1"); + if ( num == EVENT_INTERFACE_PGLASS2) strcpy(text, "\\Eyeglasses 2"); + if ( num == EVENT_INTERFACE_PGLASS3) strcpy(text, "\\Eyeglasses 3"); + if ( num == EVENT_INTERFACE_PGLASS4) strcpy(text, "\\Eyeglasses 4"); + if ( num == EVENT_INTERFACE_PGLASS5) strcpy(text, "\\Eyeglasses 5"); + + if ( num == EVENT_OBJECT_DESELECT ) strcpy(text, "Previous selection (\\key desel;)"); + if ( num == EVENT_OBJECT_LEFT ) strcpy(text, "Turn left (\\key left;)"); + if ( num == EVENT_OBJECT_RIGHT ) strcpy(text, "Turn right (\\key right;)"); + if ( num == EVENT_OBJECT_UP ) strcpy(text, "Forward (\\key up;)"); + if ( num == EVENT_OBJECT_DOWN ) strcpy(text, "Backward (\\key down;)"); + if ( num == EVENT_OBJECT_GASUP ) strcpy(text, "Up (\\key gup;)"); + if ( num == EVENT_OBJECT_GASDOWN ) strcpy(text, "Down (\\key gdown;)"); + if ( num == EVENT_OBJECT_HTAKE ) strcpy(text, "Grab or drop (\\key action;)"); + if ( num == EVENT_OBJECT_MTAKE ) strcpy(text, "Grab or drop (\\key action;)"); + if ( num == EVENT_OBJECT_MFRONT ) strcpy(text, "..in front"); + if ( num == EVENT_OBJECT_MBACK ) strcpy(text, "..behind"); + if ( num == EVENT_OBJECT_MPOWER ) strcpy(text, "..power cell"); + if ( num == EVENT_OBJECT_BHELP ) strcpy(text, "Instructions for the mission (\\key help;)"); + if ( num == EVENT_OBJECT_BTAKEOFF ) strcpy(text, "Take off to finish the mission"); + if ( num == EVENT_OBJECT_BDERRICK ) strcpy(text, "Build a derrick"); + if ( num == EVENT_OBJECT_BSTATION ) strcpy(text, "Build a power station"); + if ( num == EVENT_OBJECT_BFACTORY ) strcpy(text, "Build a bot factory"); + if ( num == EVENT_OBJECT_BREPAIR ) strcpy(text, "Build a repair center"); + if ( num == EVENT_OBJECT_BCONVERT ) strcpy(text, "Build a converter"); + if ( num == EVENT_OBJECT_BTOWER ) strcpy(text, "Build a defense tower"); + if ( num == EVENT_OBJECT_BRESEARCH ) strcpy(text, "Build a research center"); + if ( num == EVENT_OBJECT_BRADAR ) strcpy(text, "Build a radar station"); + if ( num == EVENT_OBJECT_BENERGY ) strcpy(text, "Build a power cell factory"); + if ( num == EVENT_OBJECT_BLABO ) strcpy(text, "Build an autolab"); + if ( num == EVENT_OBJECT_BNUCLEAR ) strcpy(text, "Build a nuclear power plant"); + if ( num == EVENT_OBJECT_BPARA ) strcpy(text, "Build a lightning conductor"); + if ( num == EVENT_OBJECT_BINFO ) strcpy(text, "Build a exchange post"); + if ( num == EVENT_OBJECT_GFLAT ) strcpy(text, "Show if the ground is flat"); + if ( num == EVENT_OBJECT_FCREATE ) strcpy(text, "Plant a flag"); + if ( num == EVENT_OBJECT_FDELETE ) strcpy(text, "Remove a flag"); + if ( num == EVENT_OBJECT_FCOLORb ) strcpy(text, "\\Blue flags"); + if ( num == EVENT_OBJECT_FCOLORr ) strcpy(text, "\\Red flags"); + if ( num == EVENT_OBJECT_FCOLORg ) strcpy(text, "\\Green flags"); + if ( num == EVENT_OBJECT_FCOLORy ) strcpy(text, "\\Yellow flags"); + if ( num == EVENT_OBJECT_FCOLORv ) strcpy(text, "\\Violet flags"); + if ( num == EVENT_OBJECT_FACTORYfa ) strcpy(text, "Build a winged grabber"); + if ( num == EVENT_OBJECT_FACTORYta ) strcpy(text, "Build a tracked grabber"); + if ( num == EVENT_OBJECT_FACTORYwa ) strcpy(text, "Build a wheeled grabber"); + if ( num == EVENT_OBJECT_FACTORYia ) strcpy(text, "Build a legged grabber"); + if ( num == EVENT_OBJECT_FACTORYfc ) strcpy(text, "Build a winged shooter"); + if ( num == EVENT_OBJECT_FACTORYtc ) strcpy(text, "Build a tracked shooter"); + if ( num == EVENT_OBJECT_FACTORYwc ) strcpy(text, "Build a wheeled shooter"); + if ( num == EVENT_OBJECT_FACTORYic ) strcpy(text, "Build a legged shooter"); + if ( num == EVENT_OBJECT_FACTORYfi ) strcpy(text, "Build a winged orga shooter"); + if ( num == EVENT_OBJECT_FACTORYti ) strcpy(text, "Build a tracked orga shooter"); + if ( num == EVENT_OBJECT_FACTORYwi ) strcpy(text, "Build a wheeled orga shooter"); + if ( num == EVENT_OBJECT_FACTORYii ) strcpy(text, "Build a legged orga shooter"); + if ( num == EVENT_OBJECT_FACTORYfs ) strcpy(text, "Build a winged sniffer"); + if ( num == EVENT_OBJECT_FACTORYts ) strcpy(text, "Build a tracked sniffer"); + if ( num == EVENT_OBJECT_FACTORYws ) strcpy(text, "Build a wheeled sniffer"); + if ( num == EVENT_OBJECT_FACTORYis ) strcpy(text, "Build a legged sniffer"); + if ( num == EVENT_OBJECT_FACTORYrt ) strcpy(text, "Build a thumper"); + if ( num == EVENT_OBJECT_FACTORYrc ) strcpy(text, "Build a phazer shooter"); + if ( num == EVENT_OBJECT_FACTORYrr ) strcpy(text, "Build a recycler"); + if ( num == EVENT_OBJECT_FACTORYrs ) strcpy(text, "Build a shielder"); + if ( num == EVENT_OBJECT_FACTORYsa ) strcpy(text, "Build a subber"); + if ( num == EVENT_OBJECT_RTANK ) strcpy(text, "Run research program for tracked bots"); + if ( num == EVENT_OBJECT_RFLY ) strcpy(text, "Run research program for winged bots"); + if ( num == EVENT_OBJECT_RTHUMP ) strcpy(text, "Run research program for thumper"); + if ( num == EVENT_OBJECT_RCANON ) strcpy(text, "Run research program for shooter"); + if ( num == EVENT_OBJECT_RTOWER ) strcpy(text, "Run research program for defense tower"); + if ( num == EVENT_OBJECT_RPHAZER ) strcpy(text, "Run research program for phazer shooter"); + if ( num == EVENT_OBJECT_RSHIELD ) strcpy(text, "Run research program for shielder"); + if ( num == EVENT_OBJECT_RATOMIC ) strcpy(text, "Run research program for nuclear power"); + if ( num == EVENT_OBJECT_RiPAW ) strcpy(text, "Run research program for legged bots"); + if ( num == EVENT_OBJECT_RiGUN ) strcpy(text, "Run research program for orga shooter"); + if ( num == EVENT_OBJECT_RESET ) strcpy(text, "Return to start"); + if ( num == EVENT_OBJECT_SEARCH ) strcpy(text, "Sniff (\\key action;)"); + if ( num == EVENT_OBJECT_TERRAFORM ) strcpy(text, "Thump (\\key action;)"); + if ( num == EVENT_OBJECT_FIRE ) strcpy(text, "Shoot (\\key action;)"); + if ( num == EVENT_OBJECT_RECOVER ) strcpy(text, "Recycle (\\key action;)"); + if ( num == EVENT_OBJECT_BEGSHIELD ) strcpy(text, "Extend shield (\\key action;)"); + if ( num == EVENT_OBJECT_ENDSHIELD ) strcpy(text, "Withdraw shield (\\key action;)"); + if ( num == EVENT_OBJECT_DIMSHIELD ) strcpy(text, "Shield radius"); + if ( num == EVENT_OBJECT_PROGRUN ) strcpy(text, "Execute the selected program"); + if ( num == EVENT_OBJECT_PROGEDIT ) strcpy(text, "Edit the selected program"); + if ( num == EVENT_OBJECT_INFOOK ) strcpy(text, "\\SatCom on standby"); + if ( num == EVENT_OBJECT_DELETE ) strcpy(text, "Destroy the building"); + if ( num == EVENT_OBJECT_GENERGY ) strcpy(text, "Energy level"); + if ( num == EVENT_OBJECT_GSHIELD ) strcpy(text, "Shield level"); + if ( num == EVENT_OBJECT_GRANGE ) strcpy(text, "Jet temperature"); + if ( num == EVENT_OBJECT_GPROGRESS ) strcpy(text, "Still working ..."); + if ( num == EVENT_OBJECT_GRADAR ) strcpy(text, "Number of insects detected"); + if ( num == EVENT_OBJECT_GINFO ) strcpy(text, "Transmitted information"); + if ( num == EVENT_OBJECT_COMPASS ) strcpy(text, "Compass"); +//? if ( num == EVENT_OBJECT_MAP ) strcpy(text, "Mini-map"); + if ( num == EVENT_OBJECT_MAPZOOM ) strcpy(text, "Zoom mini-map"); + if ( num == EVENT_OBJECT_CAMERA ) strcpy(text, "Camera (\\key camera;)"); + if ( num == EVENT_OBJECT_HELP ) strcpy(text, "Help about selected object"); + if ( num == EVENT_OBJECT_SOLUCE ) strcpy(text, "Show the solution"); + if ( num == EVENT_OBJECT_SHORTCUT00) strcpy(text, "Switch bots <-> buildings"); + if ( num == EVENT_OBJECT_LIMIT ) strcpy(text, "Show the range"); + if ( num == EVENT_DT_VISIT0 || + num == EVENT_DT_VISIT1 || + num == EVENT_DT_VISIT2 || + num == EVENT_DT_VISIT3 || + num == EVENT_DT_VISIT4 ) strcpy(text, "Show the place"); + if ( num == EVENT_DT_END ) strcpy(text, "Continue"); + if ( num == EVENT_CMD ) strcpy(text, "Command line"); + if ( num == EVENT_SPEED ) strcpy(text, "Game speed"); + + if ( num == EVENT_HYPER_PREV ) strcpy(text, "Back"); + if ( num == EVENT_HYPER_NEXT ) strcpy(text, "Forward"); + if ( num == EVENT_HYPER_HOME ) strcpy(text, "Home"); + if ( num == EVENT_HYPER_COPY ) strcpy(text, "Copy"); + if ( num == EVENT_HYPER_SIZE1 ) strcpy(text, "Size 1"); + if ( num == EVENT_HYPER_SIZE2 ) strcpy(text, "Size 2"); + if ( num == EVENT_HYPER_SIZE3 ) strcpy(text, "Size 3"); + if ( num == EVENT_HYPER_SIZE4 ) strcpy(text, "Size 4"); + if ( num == EVENT_HYPER_SIZE5 ) strcpy(text, "Size 5"); + if ( num == EVENT_SATCOM_HUSTON ) strcpy(text, "Instructions from Houston"); + if ( num == EVENT_SATCOM_SAT ) strcpy(text, "Satellite report"); + if ( num == EVENT_SATCOM_LOADING ) strcpy(text, "Programs dispatched by Houston"); + if ( num == EVENT_SATCOM_OBJECT ) strcpy(text, "List of objects"); + if ( num == EVENT_SATCOM_PROG ) strcpy(text, "Programming help"); + if ( num == EVENT_SATCOM_SOLUCE ) strcpy(text, "Solution"); + + if ( num == EVENT_STUDIO_OK ) strcpy(text, "OK\\Close program editor and return to game"); + if ( num == EVENT_STUDIO_CANCEL ) strcpy(text, "Cancel\\Cancel all changes"); + if ( num == EVENT_STUDIO_NEW ) strcpy(text, "New"); + if ( num == EVENT_STUDIO_OPEN ) strcpy(text, "Open (Ctrl+o)"); + if ( num == EVENT_STUDIO_SAVE ) strcpy(text, "Save (Ctrl+s)"); + if ( num == EVENT_STUDIO_UNDO ) strcpy(text, "Undo (Ctrl+z)"); + if ( num == EVENT_STUDIO_CUT ) strcpy(text, "Cut (Ctrl+x)"); + if ( num == EVENT_STUDIO_COPY ) strcpy(text, "Copy (Ctrl+c)"); + if ( num == EVENT_STUDIO_PASTE ) strcpy(text, "Paste (Ctrl+v)"); + if ( num == EVENT_STUDIO_SIZE ) strcpy(text, "Font size"); + if ( num == EVENT_STUDIO_TOOL ) strcpy(text, "Instructions (\\key help;)"); + if ( num == EVENT_STUDIO_HELP ) strcpy(text, "Programming help (\\key prog;)"); + if ( num == EVENT_STUDIO_COMPILE ) strcpy(text, "Compile"); + if ( num == EVENT_STUDIO_RUN ) strcpy(text, "Execute/stop"); + if ( num == EVENT_STUDIO_REALTIME ) strcpy(text, "Pause/continue"); + if ( num == EVENT_STUDIO_STEP ) strcpy(text, "One step"); + } + + if ( type == RES_OBJECT ) + { + if ( num == OBJECT_PORTICO ) strcpy(text, "Gantry crane"); + if ( num == OBJECT_BASE ) strcpy(text, "Spaceship"); + if ( num == OBJECT_DERRICK ) strcpy(text, "Derrick"); + if ( num == OBJECT_FACTORY ) strcpy(text, "Bot factory"); + if ( num == OBJECT_REPAIR ) strcpy(text, "Repair center"); + if ( num == OBJECT_STATION ) strcpy(text, "Power station"); + if ( num == OBJECT_CONVERT ) strcpy(text, "Converts ore to titanium"); + if ( num == OBJECT_TOWER ) strcpy(text, "Defense tower"); + if ( num == OBJECT_NEST ) strcpy(text, "Nest"); + if ( num == OBJECT_RESEARCH ) strcpy(text, "Research center"); + if ( num == OBJECT_RADAR ) strcpy(text, "Radar station"); + if ( num == OBJECT_INFO ) strcpy(text, "Information exchange post"); + if ( num == OBJECT_ENERGY ) strcpy(text, "Power cell factory"); + if ( num == OBJECT_LABO ) strcpy(text, "Autolab"); + if ( num == OBJECT_NUCLEAR ) strcpy(text, "Nuclear power station"); + if ( num == OBJECT_PARA ) strcpy(text, "Lightning conductor"); + if ( num == OBJECT_SAFE ) strcpy(text, "Vault"); + if ( num == OBJECT_HUSTON ) strcpy(text, "Houston Mission Control"); + if ( num == OBJECT_TARGET1 ) strcpy(text, "Target"); + if ( num == OBJECT_TARGET2 ) strcpy(text, "Target"); + if ( num == OBJECT_START ) strcpy(text, "Start"); + if ( num == OBJECT_END ) strcpy(text, "Finish"); + if ( num == OBJECT_STONE ) strcpy(text, "Titanium ore"); + if ( num == OBJECT_URANIUM ) strcpy(text, "Uranium ore"); + if ( num == OBJECT_BULLET ) strcpy(text, "Organic matter"); + if ( num == OBJECT_METAL ) strcpy(text, "Titanium"); + if ( num == OBJECT_POWER ) strcpy(text, "Power cell"); + if ( num == OBJECT_ATOMIC ) strcpy(text, "Nuclear power cell"); + if ( num == OBJECT_BBOX ) strcpy(text, "Black box"); + if ( num == OBJECT_KEYa ) strcpy(text, "Key A"); + if ( num == OBJECT_KEYb ) strcpy(text, "Key B"); + if ( num == OBJECT_KEYc ) strcpy(text, "Key C"); + if ( num == OBJECT_KEYd ) strcpy(text, "Key D"); + if ( num == OBJECT_TNT ) strcpy(text, "Explosive"); + if ( num == OBJECT_BOMB ) strcpy(text, "Fixed mine"); + if ( num == OBJECT_BAG ) strcpy(text, "Survival kit"); + if ( num == OBJECT_WAYPOINT ) strcpy(text, "Checkpoint"); + if ( num == OBJECT_FLAGb ) strcpy(text, "Blue flag"); + if ( num == OBJECT_FLAGr ) strcpy(text, "Red flag"); + if ( num == OBJECT_FLAGg ) strcpy(text, "Green flag"); + if ( num == OBJECT_FLAGy ) strcpy(text, "Yellow flag"); + if ( num == OBJECT_FLAGv ) strcpy(text, "Violet flag"); + if ( num == OBJECT_MARKPOWER ) strcpy(text, "Energy deposit (site for power station)"); + if ( num == OBJECT_MARKURANIUM ) strcpy(text, "Uranium deposit (site for derrick)"); + if ( num == OBJECT_MARKKEYa ) strcpy(text, "Found key A (site for derrick)"); + if ( num == OBJECT_MARKKEYb ) strcpy(text, "Found key B (site for derrick)"); + if ( num == OBJECT_MARKKEYc ) strcpy(text, "Found key C (site for derrick)"); + if ( num == OBJECT_MARKKEYd ) strcpy(text, "Found key D (site for derrick)"); + if ( num == OBJECT_MARKSTONE ) strcpy(text, "Titanium deposit (site for derrick)"); + if ( num == OBJECT_MOBILEft ) strcpy(text, "Practice bot"); + if ( num == OBJECT_MOBILEtt ) strcpy(text, "Practice bot"); + if ( num == OBJECT_MOBILEwt ) strcpy(text, "Practice bot"); + if ( num == OBJECT_MOBILEit ) strcpy(text, "Practice bot"); + if ( num == OBJECT_MOBILEfa ) strcpy(text, "Winged grabber"); + if ( num == OBJECT_MOBILEta ) strcpy(text, "Tracked grabber"); + if ( num == OBJECT_MOBILEwa ) strcpy(text, "Wheeled grabber"); + if ( num == OBJECT_MOBILEia ) strcpy(text, "Legged grabber"); + if ( num == OBJECT_MOBILEfc ) strcpy(text, "Winged shooter"); + if ( num == OBJECT_MOBILEtc ) strcpy(text, "Tracked shooter"); + if ( num == OBJECT_MOBILEwc ) strcpy(text, "Wheeled shooter"); + if ( num == OBJECT_MOBILEic ) strcpy(text, "Legged shooter"); + if ( num == OBJECT_MOBILEfi ) strcpy(text, "Winged orga shooter"); + if ( num == OBJECT_MOBILEti ) strcpy(text, "Tracked orga shooter"); + if ( num == OBJECT_MOBILEwi ) strcpy(text, "Wheeled orga shooter"); + if ( num == OBJECT_MOBILEii ) strcpy(text, "Legged orga shooter"); + if ( num == OBJECT_MOBILEfs ) strcpy(text, "Winged sniffer"); + if ( num == OBJECT_MOBILEts ) strcpy(text, "Tracked sniffer"); + if ( num == OBJECT_MOBILEws ) strcpy(text, "Wheeled sniffer"); + if ( num == OBJECT_MOBILEis ) strcpy(text, "Legged sniffer"); + if ( num == OBJECT_MOBILErt ) strcpy(text, "Thumper"); + if ( num == OBJECT_MOBILErc ) strcpy(text, "Phazer shooter"); + if ( num == OBJECT_MOBILErr ) strcpy(text, "Recycler"); + if ( num == OBJECT_MOBILErs ) strcpy(text, "Shielder"); + if ( num == OBJECT_MOBILEsa ) strcpy(text, "Subber"); + if ( num == OBJECT_MOBILEtg ) strcpy(text, "Target bot"); + if ( num == OBJECT_HUMAN ) strcpy(text, g_gamerName); + if ( num == OBJECT_TECH ) strcpy(text, "Engineer"); + if ( num == OBJECT_TOTO ) strcpy(text, "Robbie"); + if ( num == OBJECT_MOTHER ) strcpy(text, "Alien Queen"); + if ( num == OBJECT_ANT ) strcpy(text, "Ant"); + if ( num == OBJECT_SPIDER ) strcpy(text, "Spider"); + if ( num == OBJECT_BEE ) strcpy(text, "Wasp"); + if ( num == OBJECT_WORM ) strcpy(text, "Worm"); + if ( num == OBJECT_EGG ) strcpy(text, "Egg"); + if ( num == OBJECT_RUINmobilew1 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINmobilew2 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINmobilet1 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINmobilet2 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINmobiler1 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINmobiler2 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINfactory ) strcpy(text, "Ruin"); + if ( num == OBJECT_RUINdoor ) strcpy(text, "Ruin"); + if ( num == OBJECT_RUINsupport ) strcpy(text, "Waste"); + if ( num == OBJECT_RUINradar ) strcpy(text, "Ruin"); + if ( num == OBJECT_RUINconvert ) strcpy(text, "Ruin"); + if ( num == OBJECT_RUINbase ) strcpy(text, "Spaceship ruin"); + if ( num == OBJECT_RUINhead ) strcpy(text, "Spaceship ruin"); + if ( num == OBJECT_APOLLO1 || + num == OBJECT_APOLLO3 || + num == OBJECT_APOLLO4 || + num == OBJECT_APOLLO5 ) strcpy(text, "Derelict of Apollo mission"); + if ( num == OBJECT_APOLLO2 ) strcpy(text, "Lunar Roving Vehicle"); + } + + if ( type == RES_ERR ) + { + strcpy(text, "Error"); + if ( num == ERR_CMD ) strcpy(text, "Unknown command"); + if ( num == ERR_INSTALL ) strcpy(text, "COLOBOT not installed."); + if ( num == ERR_NOCD ) strcpy(text, "Please insert the COLOBOT CD\nand re-run the game."); + if ( num == ERR_MANIP_VEH ) strcpy(text, "Inappropriate bot"); + if ( num == ERR_MANIP_FLY ) strcpy(text, "Impossible when flying"); + if ( num == ERR_MANIP_BUSY ) strcpy(text, "Already carrying something"); + if ( num == ERR_MANIP_NIL ) strcpy(text, "Nothing to grab"); + if ( num == ERR_MANIP_MOTOR ) strcpy(text, "Impossible when moving"); + if ( num == ERR_MANIP_OCC ) strcpy(text, "Place occupied"); + if ( num == ERR_MANIP_FRIEND ) strcpy(text, "No other robot"); + if ( num == ERR_MANIP_RADIO ) strcpy(text, "You can not carry a radioactive object"); + if ( num == ERR_MANIP_WATER ) strcpy(text, "You can not carry an object under water"); + if ( num == ERR_MANIP_EMPTY ) strcpy(text, "Nothing to drop"); + if ( num == ERR_BUILD_FLY ) strcpy(text, "Impossible when flying"); + if ( num == ERR_BUILD_WATER ) strcpy(text, "Impossible under water"); + if ( num == ERR_BUILD_ENERGY ) strcpy(text, "Not enough energy"); + if ( num == ERR_BUILD_METALAWAY ) strcpy(text, "Titanium too far away"); + if ( num == ERR_BUILD_METALNEAR ) strcpy(text, "Titanium too close"); + if ( num == ERR_BUILD_METALINEX ) strcpy(text, "No titanium around"); + if ( num == ERR_BUILD_FLAT ) strcpy(text, "Ground not flat enough"); + if ( num == ERR_BUILD_FLATLIT ) strcpy(text, "Flat ground not large enough"); + if ( num == ERR_BUILD_BUSY ) strcpy(text, "Place occupied"); + if ( num == ERR_BUILD_BASE ) strcpy(text, "Too close to space ship"); + if ( num == ERR_BUILD_NARROW ) strcpy(text, "Too close to a building"); + if ( num == ERR_BUILD_MOTOR ) strcpy(text, "Impossible when moving"); + if ( num == ERR_SEARCH_FLY ) strcpy(text, "Impossible when flying"); + if ( num == ERR_SEARCH_VEH ) strcpy(text, "Bot inappropriate"); + if ( num == ERR_SEARCH_MOTOR ) strcpy(text, "Impossible when moving"); + if ( num == ERR_TERRA_VEH ) strcpy(text, "Bot inappropriate"); + if ( num == ERR_TERRA_ENERGY ) strcpy(text, "Not enough energy"); + if ( num == ERR_TERRA_FLOOR ) strcpy(text, "Ground inappropriate"); + if ( num == ERR_TERRA_BUILDING ) strcpy(text, "Building too close"); + if ( num == ERR_TERRA_OBJECT ) strcpy(text, "Object too close"); + if ( num == ERR_RECOVER_VEH ) strcpy(text, "Bot inappropriate"); + if ( num == ERR_RECOVER_ENERGY ) strcpy(text, "Not enough energy"); + if ( num == ERR_RECOVER_NULL ) strcpy(text, "Nothing to recycle"); + if ( num == ERR_SHIELD_VEH ) strcpy(text, "Bot inappropriate"); + if ( num == ERR_SHIELD_ENERGY ) strcpy(text, "No more energy"); + if ( num == ERR_MOVE_IMPOSSIBLE ) strcpy(text, "Error in instruction move"); + if ( num == ERR_GOTO_IMPOSSIBLE ) strcpy(text, "Goto: destination inaccessible"); + if ( num == ERR_GOTO_ITER ) strcpy(text, "Goto: destination inaccessible"); + if ( num == ERR_GOTO_BUSY ) strcpy(text, "Goto: destination occupied"); + if ( num == ERR_FIRE_VEH ) strcpy(text, "Bot inappropriate"); + if ( num == ERR_FIRE_ENERGY ) strcpy(text, "Not enough energy"); + if ( num == ERR_FIRE_FLY ) strcpy(text, "Impossible when flying"); + if ( num == ERR_CONVERT_EMPTY ) strcpy(text, "No titanium ore to convert"); + if ( num == ERR_DERRICK_NULL ) strcpy(text, "No ore in the subsoil"); + if ( num == ERR_STATION_NULL ) strcpy(text, "No energy in the subsoil"); + if ( num == ERR_TOWER_POWER ) strcpy(text, "No power cell"); + if ( num == ERR_TOWER_ENERGY ) strcpy(text, "No more energy"); + if ( num == ERR_RESEARCH_POWER ) strcpy(text, "No power cell"); + if ( num == ERR_RESEARCH_ENERGY ) strcpy(text, "Not enough energy"); + if ( num == ERR_RESEARCH_TYPE ) strcpy(text, "Cell type inappropriate"); + if ( num == ERR_RESEARCH_ALREADY) strcpy(text, "Research program already performed"); + if ( num == ERR_ENERGY_NULL ) strcpy(text, "No energy in the subsoil"); + if ( num == ERR_ENERGY_LOW ) strcpy(text, "Not yet enough energy"); + if ( num == ERR_ENERGY_EMPTY ) strcpy(text, "No titanium to transform"); + if ( num == ERR_ENERGY_BAD ) strcpy(text, "Transforms only titanium"); + if ( num == ERR_BASE_DLOCK ) strcpy(text, "Doors blocked by a robot or another object "); + if ( num == ERR_BASE_DHUMAN ) strcpy(text, "You must get on the spaceship to take off "); + if ( num == ERR_LABO_NULL ) strcpy(text, "Nothing to analyze"); + if ( num == ERR_LABO_BAD ) strcpy(text, "Analyzes only organic matter"); + if ( num == ERR_LABO_ALREADY ) strcpy(text, "Analysis already performed"); + if ( num == ERR_NUCLEAR_NULL ) strcpy(text, "No energy in the subsoil"); + if ( num == ERR_NUCLEAR_LOW ) strcpy(text, "Not yet enough energy"); + if ( num == ERR_NUCLEAR_EMPTY ) strcpy(text, "No uranium to transform"); + if ( num == ERR_NUCLEAR_BAD ) strcpy(text, "Transforms only uranium"); + if ( num == ERR_FACTORY_NULL ) strcpy(text, "No titanium"); + if ( num == ERR_FACTORY_NEAR ) strcpy(text, "Object too close"); + if ( num == ERR_RESET_NEAR ) strcpy(text, "Place occupied"); + if ( num == ERR_INFO_NULL ) strcpy(text, "No information exchange post within range"); + if ( num == ERR_VEH_VIRUS ) strcpy(text, "Program infected by a virus"); + if ( num == ERR_BAT_VIRUS ) strcpy(text, "Infected by a virus, temporarily out of order"); + if ( num == ERR_VEH_POWER ) strcpy(text, "No power cell"); + if ( num == ERR_VEH_ENERGY ) strcpy(text, "No more energy"); + if ( num == ERR_FLAG_FLY ) strcpy(text, "Impossible when flying"); + if ( num == ERR_FLAG_WATER ) strcpy(text, "Impossible when swimming"); + if ( num == ERR_FLAG_MOTOR ) strcpy(text, "Impossible when moving"); + if ( num == ERR_FLAG_BUSY ) strcpy(text, "Impossible when carrying an object"); + if ( num == ERR_FLAG_CREATE ) strcpy(text, "Too many flags of this color (maximum 5)"); + if ( num == ERR_FLAG_PROXY ) strcpy(text, "Too close to an existing flag"); + if ( num == ERR_FLAG_DELETE ) strcpy(text, "No flag nearby"); + if ( num == ERR_MISSION_NOTERM ) strcpy(text, "The mission is not accomplished yet (push \\key help; for more details)"); + if ( num == ERR_DELETEMOBILE ) strcpy(text, "Bot destroyed"); + if ( num == ERR_DELETEBUILDING ) strcpy(text, "Building destroyed"); + if ( num == ERR_TOOMANY ) strcpy(text, "Can not create this, there are too many objects"); + + if ( num == INFO_BUILD ) strcpy(text, "Building completed"); + if ( num == INFO_CONVERT ) strcpy(text, "Titanium available"); + if ( num == INFO_RESEARCH ) strcpy(text, "Research program completed"); + if ( num == INFO_RESEARCHTANK ) strcpy(text, "Plans for tracked robots available "); + if ( num == INFO_RESEARCHFLY ) strcpy(text, "You can fly with the keys (\\key gup;) and (\\key gdown;)"); + if ( num == INFO_RESEARCHTHUMP ) strcpy(text, "Plans for thumper available"); + if ( num == INFO_RESEARCHCANON ) strcpy(text, "Plans for shooter available"); + if ( num == INFO_RESEARCHTOWER ) strcpy(text, "Plans for defense tower available"); + if ( num == INFO_RESEARCHPHAZER ) strcpy(text, "Plans for phazer shooter available"); + if ( num == INFO_RESEARCHSHIELD ) strcpy(text, "Plans for shielder available"); + if ( num == INFO_RESEARCHATOMIC ) strcpy(text, "Plans for nuclear power plant available"); + if ( num == INFO_FACTORY ) strcpy(text, "New bot available"); + if ( num == INFO_LABO ) strcpy(text, "Analysis performed"); + if ( num == INFO_ENERGY ) strcpy(text, "Power cell available"); + if ( num == INFO_NUCLEAR ) strcpy(text, "Nuclear power cell available"); + if ( num == INFO_FINDING ) strcpy(text, "You found a usable object"); + if ( num == INFO_MARKPOWER ) strcpy(text, "Found a site for power station"); + if ( num == INFO_MARKURANIUM ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_MARKSTONE ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_MARKKEYa ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_MARKKEYb ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_MARKKEYc ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_MARKKEYd ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_WIN ) strcpy(text, "<<< Well done, mission accomplished >>>"); + if ( num == INFO_LOST ) strcpy(text, "<<< Sorry, mission failed >>>"); + if ( num == INFO_LOSTq ) strcpy(text, "<<< Sorry, mission failed >>>"); + if ( num == INFO_WRITEOK ) strcpy(text, "Current mission saved"); + if ( num == INFO_DELETEPATH ) strcpy(text, "Checkpoint crossed"); + if ( num == INFO_DELETEMOTHER ) strcpy(text, "Alien Queen killed"); + if ( num == INFO_DELETEANT ) strcpy(text, "Ant fatally wounded"); + if ( num == INFO_DELETEBEE ) strcpy(text, "Wasp fatally wounded"); + if ( num == INFO_DELETEWORM ) strcpy(text, "Worm fatally wounded"); + if ( num == INFO_DELETESPIDER ) strcpy(text, "Spider fatally wounded"); + if ( num == INFO_BEGINSATCOM ) strcpy(text, "Push \\key help; to get instructions on your SatCom"); + } + + if ( type == RES_CBOT ) + { + strcpy(text, "Error"); + if ( num == TX_OPENPAR ) strcpy(text, "Opening bracket missing"); + if ( num == TX_CLOSEPAR ) strcpy(text, "Closing bracket missing "); + if ( num == TX_NOTBOOL ) strcpy(text, "The expression must return a boolean value"); + if ( num == TX_UNDEFVAR ) strcpy(text, "Variable not declared"); + if ( num == TX_BADLEFT ) strcpy(text, "Assignment impossible"); + if ( num == TX_ENDOF ) strcpy(text, "Semicolon terminator missing"); + if ( num == TX_OUTCASE ) strcpy(text, "Instruction ""case"" outside a block ""switch"""); + if ( num == TX_NOTERM ) strcpy(text, "Instructions after the final closing brace"); + if ( num == TX_CLOSEBLK ) strcpy(text, "End of block missing"); + if ( num == TX_ELSEWITHOUTIF ) strcpy(text, "Instruction ""else"" without corresponding ""if"" "); + if ( num == TX_OPENBLK ) strcpy(text, "Opening brace missing ");//début d'un bloc attendu? + if ( num == TX_BADTYPE ) strcpy(text, "Wrong type for the assignment"); + if ( num == TX_REDEFVAR ) strcpy(text, "A variable can not be declared twice"); + if ( num == TX_BAD2TYPE ) strcpy(text, "The types of the two operands are incompatible "); + if ( num == TX_UNDEFCALL ) strcpy(text, "Unknown function"); + if ( num == TX_MISDOTS ) strcpy(text, "Sign "" : "" missing"); + if ( num == TX_WHILE ) strcpy(text, "Keyword ""while"" missing"); + if ( num == TX_BREAK ) strcpy(text, "Instruction ""break"" outside a loop"); + if ( num == TX_LABEL ) strcpy(text, "A label must be followed by ""for"", ""while"", ""do"" or ""switch"""); + if ( num == TX_NOLABEL ) strcpy(text, "This label does not exist");// Cette étiquette n'existe pas + if ( num == TX_NOCASE ) strcpy(text, "Instruction ""case"" missing"); + if ( num == TX_BADNUM ) strcpy(text, "Number missing"); + if ( num == TX_VOID ) strcpy(text, "Void parameter"); + if ( num == TX_NOTYP ) strcpy(text, "Type declaration missing"); + if ( num == TX_NOVAR ) strcpy(text, "Variable name missing"); + if ( num == TX_NOFONC ) strcpy(text, "Function name missing"); + if ( num == TX_OVERPARAM ) strcpy(text, "Too many parameters"); + if ( num == TX_REDEF ) strcpy(text, "Function already exists"); + if ( num == TX_LOWPARAM ) strcpy(text, "Parameters missing "); + if ( num == TX_BADPARAM ) strcpy(text, "No function of this name accepts this kind of parameter"); + if ( num == TX_NUMPARAM ) strcpy(text, "No function of this name accepts this number of parameters"); + if ( num == TX_NOITEM ) strcpy(text, "This is not a member of this class"); + if ( num == TX_DOT ) strcpy(text, "This object is not a member of a class"); + if ( num == TX_NOCONST ) strcpy(text, "Appropriate constructor missing"); + if ( num == TX_REDEFCLASS ) strcpy(text, "This class already exists"); + if ( num == TX_CLBRK ) strcpy(text, """ ] "" missing"); + if ( num == TX_RESERVED ) strcpy(text, "Reserved keyword of CBOT language"); + if ( num == TX_BADNEW ) strcpy(text, "Bad argument for ""new"""); + if ( num == TX_OPBRK ) strcpy(text, """ [ "" expected"); + if ( num == TX_BADSTRING ) strcpy(text, "String missing"); + if ( num == TX_BADINDEX ) strcpy(text, "Incorrect index type"); + if ( num == TX_PRIVATE ) strcpy(text, "Private element"); + if ( num == TX_NOPUBLIC ) strcpy(text, "Public required"); + if ( num == TX_DIVZERO ) strcpy(text, "Dividing through zero"); + if ( num == TX_NOTINIT ) strcpy(text, "Variable not initialized"); + if ( num == TX_BADTHROW ) strcpy(text, "Negative value rejected by ""throw""");//C'est quoi, ça? + if ( num == TX_NORETVAL ) strcpy(text, "The function returned no value "); + if ( num == TX_NORUN ) strcpy(text, "No function running"); + if ( num == TX_NOCALL ) strcpy(text, "Calling an unknown function"); + if ( num == TX_NOCLASS ) strcpy(text, "This class does not exist"); + if ( num == TX_NULLPT ) strcpy(text, "Unknown Object"); + if ( num == TX_OPNAN ) strcpy(text, "Operation impossible with value ""nan"""); + if ( num == TX_OUTARRAY ) strcpy(text, "Access beyond array limit"); + if ( num == TX_STACKOVER ) strcpy(text, "Stack overflow"); + if ( num == TX_DELETEDPT ) strcpy(text, "Illegal object"); + if ( num == TX_FILEOPEN ) strcpy(text, "Can't open file"); + if ( num == TX_NOTOPEN ) strcpy(text, "File not open"); + if ( num == TX_ERRREAD ) strcpy(text, "Read error"); + if ( num == TX_ERRWRITE ) strcpy(text, "Write error"); + } + + if ( type == RES_KEY ) + { + if ( num == 0 ) strcpy(text, "< none >"); + if ( num == VK_LEFT ) strcpy(text, "Arrow left"); + if ( num == VK_RIGHT ) strcpy(text, "Arrow right"); + if ( num == VK_UP ) strcpy(text, "Arrow up"); + if ( num == VK_DOWN ) strcpy(text, "Arrow down"); + if ( num == VK_CANCEL ) strcpy(text, "Control-break"); + if ( num == VK_BACK ) strcpy(text, "<--"); + if ( num == VK_TAB ) strcpy(text, "Tab"); + if ( num == VK_CLEAR ) strcpy(text, "Clear"); + if ( num == VK_RETURN ) strcpy(text, "Enter"); + if ( num == VK_SHIFT ) strcpy(text, "Shift"); + if ( num == VK_CONTROL ) strcpy(text, "Ctrl"); + if ( num == VK_MENU ) strcpy(text, "Alt"); + if ( num == VK_PAUSE ) strcpy(text, "Pause"); + if ( num == VK_CAPITAL ) strcpy(text, "Caps Lock"); + if ( num == VK_ESCAPE ) strcpy(text, "Esc"); + if ( num == VK_SPACE ) strcpy(text, "Space"); + if ( num == VK_PRIOR ) strcpy(text, "Page Up"); + if ( num == VK_NEXT ) strcpy(text, "Page Down"); + if ( num == VK_END ) strcpy(text, "End"); + if ( num == VK_HOME ) strcpy(text, "Home"); + if ( num == VK_SELECT ) strcpy(text, "Select"); + if ( num == VK_EXECUTE ) strcpy(text, "Execute"); + if ( num == VK_SNAPSHOT ) strcpy(text, "Print Scrn"); + if ( num == VK_INSERT ) strcpy(text, "Insert"); + if ( num == VK_DELETE ) strcpy(text, "Delete"); + if ( num == VK_HELP ) strcpy(text, "Help"); + if ( num == VK_LWIN ) strcpy(text, "Left Windows"); + if ( num == VK_RWIN ) strcpy(text, "Right Windows"); + if ( num == VK_APPS ) strcpy(text, "Application key"); + if ( num == VK_NUMPAD0 ) strcpy(text, "NumPad 0"); + if ( num == VK_NUMPAD1 ) strcpy(text, "NumPad 1"); + if ( num == VK_NUMPAD2 ) strcpy(text, "NumPad 2"); + if ( num == VK_NUMPAD3 ) strcpy(text, "NumPad 3"); + if ( num == VK_NUMPAD4 ) strcpy(text, "NumPad 4"); + if ( num == VK_NUMPAD5 ) strcpy(text, "NumPad 5"); + if ( num == VK_NUMPAD6 ) strcpy(text, "NumPad 6"); + if ( num == VK_NUMPAD7 ) strcpy(text, "NumPad 7"); + if ( num == VK_NUMPAD8 ) strcpy(text, "NumPad 8"); + if ( num == VK_NUMPAD9 ) strcpy(text, "NumPad 9"); + if ( num == VK_MULTIPLY ) strcpy(text, "NumPad *"); + if ( num == VK_ADD ) strcpy(text, "NumPad +"); + if ( num == VK_SEPARATOR ) strcpy(text, "NumPad sep"); + if ( num == VK_SUBTRACT ) strcpy(text, "NumPad -"); + if ( num == VK_DECIMAL ) strcpy(text, "NumPad ."); + if ( num == VK_DIVIDE ) strcpy(text, "NumPad /"); + if ( num == VK_F1 ) strcpy(text, "F1"); + if ( num == VK_F2 ) strcpy(text, "F2"); + if ( num == VK_F3 ) strcpy(text, "F3"); + if ( num == VK_F4 ) strcpy(text, "F4"); + if ( num == VK_F5 ) strcpy(text, "F5"); + if ( num == VK_F6 ) strcpy(text, "F6"); + if ( num == VK_F7 ) strcpy(text, "F7"); + if ( num == VK_F8 ) strcpy(text, "F8"); + if ( num == VK_F9 ) strcpy(text, "F9"); + if ( num == VK_F10 ) strcpy(text, "F10"); + if ( num == VK_F11 ) strcpy(text, "F11"); + if ( num == VK_F12 ) strcpy(text, "F12"); + if ( num == VK_F13 ) strcpy(text, "F13"); + if ( num == VK_F14 ) strcpy(text, "F14"); + if ( num == VK_F15 ) strcpy(text, "F15"); + if ( num == VK_F16 ) strcpy(text, "F16"); + if ( num == VK_F17 ) strcpy(text, "F17"); + if ( num == VK_F18 ) strcpy(text, "F18"); + if ( num == VK_F19 ) strcpy(text, "F19"); + if ( num == VK_F20 ) strcpy(text, "F20"); + if ( num == VK_NUMLOCK ) strcpy(text, "Num Lock"); + if ( num == VK_SCROLL ) strcpy(text, "Scroll"); + if ( num == VK_ATTN ) strcpy(text, "Attn"); + if ( num == VK_CRSEL ) strcpy(text, "CrSel"); + if ( num == VK_EXSEL ) strcpy(text, "ExSel"); + if ( num == VK_EREOF ) strcpy(text, "Erase EOF"); + if ( num == VK_PLAY ) strcpy(text, "Play"); + if ( num == VK_ZOOM ) strcpy(text, "Zoom"); + if ( num == VK_PA1 ) strcpy(text, "PA1"); + if ( num == VK_OEM_CLEAR ) strcpy(text, "Clear"); + if ( num == VK_BUTTON1 ) strcpy(text, "Button 1"); + if ( num == VK_BUTTON2 ) strcpy(text, "Button 2"); + if ( num == VK_BUTTON3 ) strcpy(text, "Button 3"); + if ( num == VK_BUTTON4 ) strcpy(text, "Button 4"); + if ( num == VK_BUTTON5 ) strcpy(text, "Button 5"); + if ( num == VK_BUTTON6 ) strcpy(text, "Button 6"); + if ( num == VK_BUTTON7 ) strcpy(text, "Button 7"); + if ( num == VK_BUTTON8 ) strcpy(text, "Button 8"); + if ( num == VK_BUTTON9 ) strcpy(text, "Button 9"); + if ( num == VK_BUTTON10 ) strcpy(text, "Button 10"); + if ( num == VK_BUTTON11 ) strcpy(text, "Button 11"); + if ( num == VK_BUTTON12 ) strcpy(text, "Button 12"); + if ( num == VK_BUTTON13 ) strcpy(text, "Button 13"); + if ( num == VK_BUTTON14 ) strcpy(text, "Button 14"); + if ( num == VK_BUTTON15 ) strcpy(text, "Button 15"); + if ( num == VK_BUTTON16 ) strcpy(text, "Button 16"); + if ( num == VK_BUTTON17 ) strcpy(text, "Button 17"); + if ( num == VK_BUTTON18 ) strcpy(text, "Button 18"); + if ( num == VK_BUTTON19 ) strcpy(text, "Button 19"); + if ( num == VK_BUTTON20 ) strcpy(text, "Button 20"); + if ( num == VK_BUTTON21 ) strcpy(text, "Button 21"); + if ( num == VK_BUTTON22 ) strcpy(text, "Button 22"); + if ( num == VK_BUTTON23 ) strcpy(text, "Button 23"); + if ( num == VK_BUTTON24 ) strcpy(text, "Button 24"); + if ( num == VK_BUTTON25 ) strcpy(text, "Button 25"); + if ( num == VK_BUTTON26 ) strcpy(text, "Button 26"); + if ( num == VK_BUTTON27 ) strcpy(text, "Button 27"); + if ( num == VK_BUTTON28 ) strcpy(text, "Button 28"); + if ( num == VK_BUTTON29 ) strcpy(text, "Button 29"); + if ( num == VK_BUTTON30 ) strcpy(text, "Button 30"); + if ( num == VK_BUTTON31 ) strcpy(text, "Button 31"); + if ( num == VK_BUTTON32 ) strcpy(text, "Button 32"); + if ( num == VK_WHEELUP ) strcpy(text, "Wheel up"); + if ( num == VK_WHEELDOWN ) strcpy(text, "Wheel down"); + } +#endif + +#if _FRENCH + if ( type == RES_TEXT ) + { + #if _FULL + if ( num == RT_VERSION_ID ) strcpy(text, "Version 1.8 /f"); + #endif + #if _NET | _SCHOOL + if ( num == RT_VERSION_ID ) strcpy(text, "Ecole 1.8 /f"); + #endif + #if _DEMO + if ( num == RT_VERSION_ID ) strcpy(text, "Demo 1.8 /f"); + #endif + if ( num == RT_DISINFO_TITLE ) strcpy(text, "SatCom"); + if ( num == RT_WINDOW_MAXIMIZED ) strcpy(text, "Taille maximale"); + if ( num == RT_WINDOW_MINIMIZED ) strcpy(text, "Taille réduite"); + if ( num == RT_WINDOW_STANDARD ) strcpy(text, "Taille normale"); + if ( num == RT_WINDOW_CLOSE ) strcpy(text, "Fermer"); + + if ( num == RT_STUDIO_TITLE ) strcpy(text, "Edition du programme"); + if ( num == RT_SCRIPT_NEW ) strcpy(text, "Nouveau"); + if ( num == RT_NAME_DEFAULT ) strcpy(text, "Joueur"); + if ( num == RT_IO_NEW ) strcpy(text, "Nouveau ..."); + if ( num == RT_KEY_OR ) strcpy(text, " ou "); + + if ( num == RT_TITLE_BASE ) strcpy(text, "COLOBOT"); + if ( num == RT_TITLE_INIT ) strcpy(text, "COLOBOT"); + if ( num == RT_TITLE_TRAINER ) strcpy(text, "Programmation"); + if ( num == RT_TITLE_DEFI ) strcpy(text, "Défis"); + if ( num == RT_TITLE_MISSION ) strcpy(text, "Missions"); + if ( num == RT_TITLE_FREE ) strcpy(text, "Jeu libre"); + if ( num == RT_TITLE_USER ) strcpy(text, "Niveaux supplémentaires"); + if ( num == RT_TITLE_PROTO ) strcpy(text, "Prototypes"); + if ( num == RT_TITLE_SETUP ) strcpy(text, "Options"); + if ( num == RT_TITLE_NAME ) strcpy(text, "Nom du joueur"); + if ( num == RT_TITLE_PERSO ) strcpy(text, "Personnalisation de votre apparence"); + if ( num == RT_TITLE_WRITE ) strcpy(text, "Enregistrement de la mission en cours"); + if ( num == RT_TITLE_READ ) strcpy(text, "Chargement d'une mission enregistrée"); + + if ( num == RT_PLAY_CHAPt ) strcpy(text, " Liste des chapitres :"); + if ( num == RT_PLAY_CHAPd ) strcpy(text, " Liste des chapitres :"); + if ( num == RT_PLAY_CHAPm ) strcpy(text, " Liste des planètes :"); + if ( num == RT_PLAY_CHAPf ) strcpy(text, " Liste des planètes :"); + if ( num == RT_PLAY_CHAPu ) strcpy(text, " Niveaux supplémentaires :"); + if ( num == RT_PLAY_CHAPp ) strcpy(text, " Liste des planètes :"); + if ( num == RT_PLAY_LISTt ) strcpy(text, " Liste des exercices du chapitre :"); + if ( num == RT_PLAY_LISTd ) strcpy(text, " Liste des défis du chapitre :"); + if ( num == RT_PLAY_LISTm ) strcpy(text, " Liste des missions du chapitre :"); + if ( num == RT_PLAY_LISTf ) strcpy(text, " Liste des jeux libres du chapitre :"); + if ( num == RT_PLAY_LISTu ) strcpy(text, " Missions du niveau :"); + if ( num == RT_PLAY_LISTp ) strcpy(text, " Liste des prototypes du chapitre :"); + if ( num == RT_PLAY_RESUME ) strcpy(text, " Résumé :"); + + if ( num == RT_SETUP_DEVICE ) strcpy(text, " Pilotes :"); + if ( num == RT_SETUP_MODE ) strcpy(text, " Résolutions :"); + if ( num == RT_SETUP_KEY1 ) strcpy(text, "1) Cliquez d'abord sur la touche à redéfinir."); + if ( num == RT_SETUP_KEY2 ) strcpy(text, "2) Appuyez ensuite sur la nouvelle touche souhaitée."); + + if ( num == RT_PERSO_FACE ) strcpy(text, "Type de visage :"); + if ( num == RT_PERSO_GLASSES ) strcpy(text, "Lunettes :"); + if ( num == RT_PERSO_HAIR ) strcpy(text, "Couleur des cheveux :"); + if ( num == RT_PERSO_COMBI ) strcpy(text, "Couleur de la combinaison :"); + if ( num == RT_PERSO_BAND ) strcpy(text, "Couleur des bandes :"); + + if ( num == RT_DIALOG_TITLE ) strcpy(text, "COLOBOT"); + if ( num == RT_DIALOG_ABORT ) strcpy(text, "Quitter la mission ?"); + if ( num == RT_DIALOG_QUIT ) strcpy(text, "Voulez-vous quitter COLOBOT ?"); + if ( num == RT_DIALOG_YES ) strcpy(text, "Abandonner\\Abandonner la mission en cours"); + if ( num == RT_DIALOG_NO ) strcpy(text, "Continuer\\Continuer la mission en cours"); + if ( num == RT_DIALOG_YESQUIT ) strcpy(text, "Quitter\\Quitter COLOBOT"); + if ( num == RT_DIALOG_NOQUIT ) strcpy(text, "Continuer\\Continuer de jouer"); + if ( num == RT_DIALOG_DELOBJ ) strcpy(text, "Voulez-vous vraiment détruire le bâtiment sélectionné ?"); + if ( num == RT_DIALOG_DELGAME ) strcpy(text, "Voulez-vous détruire les sauvegardes de %s ?"); + if ( num == RT_DIALOG_YESDEL ) strcpy(text, "Détruire"); + if ( num == RT_DIALOG_NODEL ) strcpy(text, "Annuler"); + if ( num == RT_DIALOG_LOADING ) strcpy(text, "CHARGEMENT"); + + if ( num == RT_STUDIO_LISTTT ) strcpy(text, "Aide sur le mot-clé (\\key cbot;)"); + if ( num == RT_STUDIO_COMPOK ) strcpy(text, "Compilation ok (0 erreur)"); + if ( num == RT_STUDIO_PROGSTOP ) strcpy(text, "Programme terminé"); + + if ( num == RT_SATCOM_LIST ) strcpy(text, "\\b;Listes des objets\n"); + if ( num == RT_SATCOM_BOT ) strcpy(text, "\\b;Listes des robots\n"); + if ( num == RT_SATCOM_BUILDING ) strcpy(text, "\\b;Listes des bâtiments\n"); + if ( num == RT_SATCOM_FRET ) strcpy(text, "\\b;Listes des objets transportables\n"); + if ( num == RT_SATCOM_ALIEN ) strcpy(text, "\\b;Listes des ennemis\n"); + if ( num == RT_SATCOM_NULL ) strcpy(text, "\\c; (aucun)\\n;\n"); + if ( num == RT_SATCOM_ERROR1 ) strcpy(text, "\\b;Erreur\n"); + if ( num == RT_SATCOM_ERROR2 ) strcpy(text, "Liste non disponible sans \\l;radar\\u object\\radar; !\n"); + + if ( num == RT_IO_OPEN ) strcpy(text, "Ouvrir"); + if ( num == RT_IO_SAVE ) strcpy(text, "Enregistrer"); + if ( num == RT_IO_LIST ) strcpy(text, "Dossier: %s"); + if ( num == RT_IO_NAME ) strcpy(text, "Nom:"); + if ( num == RT_IO_DIR ) strcpy(text, "Dans:"); + if ( num == RT_IO_PRIVATE ) strcpy(text, "Privé\\Dossier privé"); + if ( num == RT_IO_PUBLIC ) strcpy(text, "Public\\Dossier commun à tous les joueurs"); + + if ( num == RT_GENERIC_DEV1 ) strcpy(text, "Développé par :"); + if ( num == RT_GENERIC_DEV2 ) strcpy(text, "www.epsitec.com"); + if ( num == RT_GENERIC_EDIT1 ) strcpy(text, "Version française éditée par :"); + if ( num == RT_GENERIC_EDIT2 ) strcpy(text, "www.alsyd.com"); + } + + if ( type == RES_EVENT ) + { + if ( num == EVENT_BUTTON_OK ) strcpy(text, "D'accord"); + if ( num == EVENT_BUTTON_CANCEL ) strcpy(text, "Annuler"); + if ( num == EVENT_BUTTON_NEXT ) strcpy(text, "Suivant"); + if ( num == EVENT_BUTTON_PREV ) strcpy(text, "Précédent"); + if ( num == EVENT_BUTTON_QUIT ) strcpy(text, "Menu (\\key quit;)"); + + if ( num == EVENT_DIALOG_OK ) strcpy(text, "D'accord"); + if ( num == EVENT_DIALOG_CANCEL ) strcpy(text, "Annuler"); + + if ( num == EVENT_INTERFACE_TRAINER) strcpy(text, "Programmation\\Exercices de programmation"); + if ( num == EVENT_INTERFACE_DEFI ) strcpy(text, "Défis\\Défis de programmation"); + if ( num == EVENT_INTERFACE_MISSION) strcpy(text, "Missions\\La grande aventure"); + if ( num == EVENT_INTERFACE_FREE ) strcpy(text, "Jeu libre\\Jeu libre sans but précis"); + if ( num == EVENT_INTERFACE_USER ) strcpy(text, "Suppl.\\Niveaux supplémentaires"); + if ( num == EVENT_INTERFACE_PROTO ) strcpy(text, "Proto\\Prototypes en cours d'élaboration"); + if ( num == EVENT_INTERFACE_NAME ) strcpy(text, "Autre joueur\\Choix du nom du joueur"); + if ( num == EVENT_INTERFACE_SETUP ) strcpy(text, "Options\\Réglages"); + if ( num == EVENT_INTERFACE_AGAIN ) strcpy(text, "Recommencer\\Recommencer la mission au début"); + if ( num == EVENT_INTERFACE_WRITE ) strcpy(text, "Enregistrer\\Enregistrer la mission en cours"); + if ( num == EVENT_INTERFACE_READ ) strcpy(text, "Charger\\Charger une mission enregistrée"); + if ( num == EVENT_INTERFACE_ABORT ) strcpy(text, "\\Retourner dans COLOBOT"); + if ( num == EVENT_INTERFACE_QUIT ) strcpy(text, "Quitter\\Quitter COLOBOT"); + if ( num == EVENT_INTERFACE_BACK ) strcpy(text, "<< Retour \\Retour au niveau précédent"); + if ( num == EVENT_INTERFACE_PLAY ) strcpy(text, "Jouer ...\\Démarrer l'action"); + if ( num == EVENT_INTERFACE_SETUPd ) strcpy(text, "Affichage\\Pilote et résolution d'affichage"); + if ( num == EVENT_INTERFACE_SETUPg ) strcpy(text, "Graphique\\Options graphiques"); + if ( num == EVENT_INTERFACE_SETUPp ) strcpy(text, "Jeu\\Options de jouabilité"); + if ( num == EVENT_INTERFACE_SETUPc ) strcpy(text, "Commandes\\Touches du clavier"); + if ( num == EVENT_INTERFACE_SETUPs ) strcpy(text, "Son\\Volumes bruitages & musiques"); + if ( num == EVENT_INTERFACE_DEVICE ) strcpy(text, "Unité"); + if ( num == EVENT_INTERFACE_RESOL ) strcpy(text, "Résolution"); + if ( num == EVENT_INTERFACE_FULL ) strcpy(text, "Plein écran\\Plein écran ou fenêtré"); + if ( num == EVENT_INTERFACE_APPLY ) strcpy(text, "Appliquer les changements\\Active les changements effectués"); + + if ( num == EVENT_INTERFACE_TOTO ) strcpy(text, "Robbie\\Votre assistant"); + if ( num == EVENT_INTERFACE_SHADOW ) strcpy(text, "Ombres\\Ombres projetées au sol"); + if ( num == EVENT_INTERFACE_GROUND ) strcpy(text, "Marques sur le sol\\Marques dessinées sur le sol"); + if ( num == EVENT_INTERFACE_DIRTY ) strcpy(text, "Salissures\\Salissures des robots et bâtiments"); + if ( num == EVENT_INTERFACE_FOG ) strcpy(text, "Brouillard\\Nappes de brouillard"); + if ( num == EVENT_INTERFACE_LENS ) strcpy(text, "Rayons du soleil\\Rayons selon l'orientation"); + if ( num == EVENT_INTERFACE_SKY ) strcpy(text, "Ciel\\Ciel et nuages"); + if ( num == EVENT_INTERFACE_PLANET ) strcpy(text, "Planètes et étoiles\\Motifs mobiles dans le ciel"); + if ( num == EVENT_INTERFACE_LIGHT ) strcpy(text, "Lumières dynamiques\\Eclairages mobiles"); + if ( num == EVENT_INTERFACE_PARTI ) strcpy(text, "Quantité de particules\\Explosions, poussières, reflets, etc."); + if ( num == EVENT_INTERFACE_CLIP ) strcpy(text, "Profondeur de champ\\Distance de vue maximale"); + if ( num == EVENT_INTERFACE_DETAIL ) strcpy(text, "Détails des objets\\Qualité des objets en 3D"); + if ( num == EVENT_INTERFACE_TEXTURE) strcpy(text, "Qualité des textures\\Qualité des images"); + if ( num == EVENT_INTERFACE_GADGET ) strcpy(text, "Nombre d'objets décoratifs\\Qualité d'objets non indispensables"); + if ( num == EVENT_INTERFACE_RAIN ) strcpy(text, "Particules dans l'interface\\Pluie de particules"); + if ( num == EVENT_INTERFACE_GLINT ) strcpy(text, "Reflets sur les boutons\\Boutons brillants"); + if ( num == EVENT_INTERFACE_TOOLTIP) strcpy(text, "Bulles d'aide\\Bulles explicatives"); + if ( num == EVENT_INTERFACE_MOVIES ) strcpy(text, "Séquences cinématiques\\Films avant ou après une mission"); + if ( num == EVENT_INTERFACE_NICERST) strcpy(text, "Retour animé\\Retour animé dans les exercices"); + if ( num == EVENT_INTERFACE_HIMSELF) strcpy(text, "Dégâts à soi-même\\Vos tirs infligent des dommages à vos unités"); + if ( num == EVENT_INTERFACE_SCROLL ) strcpy(text, "Défilement dans les bords\\Défilement lorsque la souris touches les bords gauche ou droite"); + if ( num == EVENT_INTERFACE_INVERTX) strcpy(text, "Inversion souris X\\Inversion de la rotation lorsque la souris touche un bord"); + if ( num == EVENT_INTERFACE_INVERTY) strcpy(text, "Inversion souris Y\\Inversion de la rotation lorsque la souris touche un bord"); + if ( num == EVENT_INTERFACE_EFFECT ) strcpy(text, "Secousses lors d'explosions\\L'écran vibre lors d'une explosion"); + if ( num == EVENT_INTERFACE_MOUSE ) strcpy(text, "Souris ombrée\\Jolie souris avec une ombre"); + if ( num == EVENT_INTERFACE_EDITMODE) strcpy(text, "Indentation automatique\\Pendant l'édition d'un programme"); + if ( num == EVENT_INTERFACE_EDITVALUE)strcpy(text, "Grande indentation\\Indente avec 2 ou 4 espaces"); + + if ( num == EVENT_INTERFACE_KDEF ) strcpy(text, "Tout réinitialiser\\Remet toutes les touches standards"); + if ( num == EVENT_INTERFACE_KLEFT ) strcpy(text, "Tourner à gauche\\Moteur à gauche"); + if ( num == EVENT_INTERFACE_KRIGHT ) strcpy(text, "Tourner à droite\\Moteur à droite"); + if ( num == EVENT_INTERFACE_KUP ) strcpy(text, "Avancer\\Moteur en avant"); + if ( num == EVENT_INTERFACE_KDOWN ) strcpy(text, "Reculer\\Moteur en arrière"); + if ( num == EVENT_INTERFACE_KGUP ) strcpy(text, "Monter\\Augmenter la puissance du réacteur"); + if ( num == EVENT_INTERFACE_KGDOWN ) strcpy(text, "Descendre\\Diminuer la puissance du réacteur"); + if ( num == EVENT_INTERFACE_KCAMERA) strcpy(text, "Changement de caméra\\Autre de point de vue"); + if ( num == EVENT_INTERFACE_KDESEL ) strcpy(text, "Sélection précédente\\Sélectionne l'objet précédent"); + if ( num == EVENT_INTERFACE_KACTION) strcpy(text, "Action standard\\Action du bouton avec le cadre rouge"); + if ( num == EVENT_INTERFACE_KNEAR ) strcpy(text, "Caméra plus proche\\Avance la caméra"); + if ( num == EVENT_INTERFACE_KAWAY ) strcpy(text, "Caméra plus loin\\Recule la caméra"); + if ( num == EVENT_INTERFACE_KNEXT ) strcpy(text, "Sélectionner l'objet suivant\\Sélectionner l'objet suivant"); + if ( num == EVENT_INTERFACE_KHUMAN ) strcpy(text, "Sélectionner le cosmonaute\\Sélectionner le cosmonaute"); + if ( num == EVENT_INTERFACE_KQUIT ) strcpy(text, "Quitter la mission en cours\\Terminer un exercice ou une mssion"); + if ( num == EVENT_INTERFACE_KHELP ) strcpy(text, "Instructions mission\\Marche à suivre"); + if ( num == EVENT_INTERFACE_KPROG ) strcpy(text, "Instructions programmation\\Explication sur la programmation"); + if ( num == EVENT_INTERFACE_KCBOT ) strcpy(text, "Instructions mot-clé\\Explication sur le mot-clé"); + if ( num == EVENT_INTERFACE_KVISIT ) strcpy(text, "Montrer le lieu d'un message\\Montrer le lieu du dernier message"); + if ( num == EVENT_INTERFACE_KSPEED10) strcpy(text, "Vitesse 1.0x\\Vitesse normale"); + if ( num == EVENT_INTERFACE_KSPEED15) strcpy(text, "Vitesse 1.5x\\Une fois et demi plus rapide"); + if ( num == EVENT_INTERFACE_KSPEED20) strcpy(text, "Vitesse 2.0x\\Deux fois plus rapide"); + if ( num == EVENT_INTERFACE_KSPEED30) strcpy(text, "Vitesse 3.0x\\Trois fois plus rapide"); + + if ( num == EVENT_INTERFACE_VOLSOUND) strcpy(text, "Bruitages :\\Volume des moteurs, voix, etc."); + if ( num == EVENT_INTERFACE_VOLMUSIC) strcpy(text, "Fond sonore :\\Volume des pistes audio du CD"); + if ( num == EVENT_INTERFACE_SOUND3D) strcpy(text, "Bruitages 3D\\Positionnement sonore dans l'espace"); + + if ( num == EVENT_INTERFACE_MIN ) strcpy(text, "Mini\\Qualité minimale (+ rapide)"); + if ( num == EVENT_INTERFACE_NORM ) strcpy(text, "Normal\\Qualité standard"); + if ( num == EVENT_INTERFACE_MAX ) strcpy(text, "Maxi\\Haute qualité (+ lent)"); + + if ( num == EVENT_INTERFACE_SILENT ) strcpy(text, "Silencieux\\Totalement silencieux"); + if ( num == EVENT_INTERFACE_NOISY ) strcpy(text, "Normal\\Niveaux normaux"); + + if ( num == EVENT_INTERFACE_JOYSTICK) strcpy(text, "Utilise un joystick\\Joystick ou clavier"); + if ( num == EVENT_INTERFACE_SOLUCE ) strcpy(text, "Accès à la solution\\Donne la solution"); + + if ( num == EVENT_INTERFACE_NEDIT ) strcpy(text, "\\Nom du joueur à créer"); + if ( num == EVENT_INTERFACE_NOK ) strcpy(text, "D'accord\\Choisir le joueur"); + if ( num == EVENT_INTERFACE_NCANCEL) strcpy(text, "Annuler\\Conserver le joueur actuel"); + if ( num == EVENT_INTERFACE_NDELETE) strcpy(text, "Supprimer le joueur\\Supprimer le joueur de la liste"); + if ( num == EVENT_INTERFACE_NLABEL ) strcpy(text, "Nom du joueur"); + + if ( num == EVENT_INTERFACE_IOWRITE) strcpy(text, "Enregistrer\\Enregistrer la mission en cours"); + if ( num == EVENT_INTERFACE_IOREAD ) strcpy(text, "Charger\\Charger la mission sélectionnée"); + if ( num == EVENT_INTERFACE_IOLIST ) strcpy(text, "Liste des missions enregistrées"); + if ( num == EVENT_INTERFACE_IOLABEL) strcpy(text, "Nom du fichier :"); + if ( num == EVENT_INTERFACE_IONAME ) strcpy(text, "Nom de la mission"); + if ( num == EVENT_INTERFACE_IOIMAGE) strcpy(text, "Vue de la mission"); + if ( num == EVENT_INTERFACE_IODELETE) strcpy(text, "Supprimer\\Supprime l'enregistrement sélectionné"); + + if ( num == EVENT_INTERFACE_PERSO ) strcpy(text, "Aspect\\Choisir votre aspect"); + if ( num == EVENT_INTERFACE_POK ) strcpy(text, "D'accord"); + if ( num == EVENT_INTERFACE_PCANCEL) strcpy(text, "Annuler"); + if ( num == EVENT_INTERFACE_PDEF ) strcpy(text, "Standard\\Remet les couleurs standards"); + if ( num == EVENT_INTERFACE_PHEAD ) strcpy(text, "Tête\\Visage et cheveux"); + if ( num == EVENT_INTERFACE_PBODY ) strcpy(text, "Corps\\Combinaison"); + if ( num == EVENT_INTERFACE_PLROT ) strcpy(text, "\\Rotation à gauche"); + if ( num == EVENT_INTERFACE_PRROT ) strcpy(text, "\\Rotation à droite"); + if ( num == EVENT_INTERFACE_PCRa ) strcpy(text, "Rouge"); + if ( num == EVENT_INTERFACE_PCGa ) strcpy(text, "Vert"); + if ( num == EVENT_INTERFACE_PCBa ) strcpy(text, "Bleu"); + if ( num == EVENT_INTERFACE_PCRb ) strcpy(text, "Rouge"); + if ( num == EVENT_INTERFACE_PCGb ) strcpy(text, "Vert"); + if ( num == EVENT_INTERFACE_PCBb ) strcpy(text, "Bleu"); + if ( num == EVENT_INTERFACE_PFACE1 ) strcpy(text, "\\Visage 1"); + if ( num == EVENT_INTERFACE_PFACE2 ) strcpy(text, "\\Visage 4"); + if ( num == EVENT_INTERFACE_PFACE3 ) strcpy(text, "\\Visage 3"); + if ( num == EVENT_INTERFACE_PFACE4 ) strcpy(text, "\\Visage 2"); + if ( num == EVENT_INTERFACE_PGLASS0) strcpy(text, "\\Pas de lunettes"); + if ( num == EVENT_INTERFACE_PGLASS1) strcpy(text, "\\Lunettes 1"); + if ( num == EVENT_INTERFACE_PGLASS2) strcpy(text, "\\Lunettes 2"); + if ( num == EVENT_INTERFACE_PGLASS3) strcpy(text, "\\Lunettes 3"); + if ( num == EVENT_INTERFACE_PGLASS4) strcpy(text, "\\Lunettes 4"); + if ( num == EVENT_INTERFACE_PGLASS5) strcpy(text, "\\Lunettes 5"); + + if ( num == EVENT_OBJECT_DESELECT ) strcpy(text, "Sélection précédente (\\key desel;)"); + if ( num == EVENT_OBJECT_LEFT ) strcpy(text, "Tourne à gauche (\\key left;)"); + if ( num == EVENT_OBJECT_RIGHT ) strcpy(text, "Tourne à droite (\\key right;)"); + if ( num == EVENT_OBJECT_UP ) strcpy(text, "Avance (\\key up;)"); + if ( num == EVENT_OBJECT_DOWN ) strcpy(text, "Recule (\\key down;)"); + if ( num == EVENT_OBJECT_GASUP ) strcpy(text, "Monte (\\key gup;)"); + if ( num == EVENT_OBJECT_GASDOWN ) strcpy(text, "Descend (\\key gdown;)"); + if ( num == EVENT_OBJECT_HTAKE ) strcpy(text, "Prend ou dépose (\\key action;)"); + if ( num == EVENT_OBJECT_MTAKE ) strcpy(text, "Prend ou dépose (\\key action;)"); + if ( num == EVENT_OBJECT_MFRONT ) strcpy(text, "..devant"); + if ( num == EVENT_OBJECT_MBACK ) strcpy(text, "..derrière"); + if ( num == EVENT_OBJECT_MPOWER ) strcpy(text, "..pile"); + if ( num == EVENT_OBJECT_BHELP ) strcpy(text, "Instructions sur la mission (\\key help;)"); + if ( num == EVENT_OBJECT_BTAKEOFF ) strcpy(text, "Décolle pour terminer la mission"); + if ( num == EVENT_OBJECT_BDERRICK ) strcpy(text, "Construit un derrick"); + if ( num == EVENT_OBJECT_BSTATION ) strcpy(text, "Construit une station"); + if ( num == EVENT_OBJECT_BFACTORY ) strcpy(text, "Construit une fabrique de robots"); + if ( num == EVENT_OBJECT_BREPAIR ) strcpy(text, "Construit un centre de réparation"); + if ( num == EVENT_OBJECT_BCONVERT ) strcpy(text, "Construit un convertisseur"); + if ( num == EVENT_OBJECT_BTOWER ) strcpy(text, "Construit une tour"); + if ( num == EVENT_OBJECT_BRESEARCH ) strcpy(text, "Construit un centre de recherches"); + if ( num == EVENT_OBJECT_BRADAR ) strcpy(text, "Construit un radar"); + if ( num == EVENT_OBJECT_BENERGY ) strcpy(text, "Construit une fabrique de piles"); + if ( num == EVENT_OBJECT_BLABO ) strcpy(text, "Construit un laboratoire"); + if ( num == EVENT_OBJECT_BNUCLEAR ) strcpy(text, "Construit une centrale nucléaire"); + if ( num == EVENT_OBJECT_BPARA ) strcpy(text, "Construit un paratonnerre"); + if ( num == EVENT_OBJECT_BINFO ) strcpy(text, "Construit une borne d'information"); + if ( num == EVENT_OBJECT_GFLAT ) strcpy(text, "Montre si le sol est plat"); + if ( num == EVENT_OBJECT_FCREATE ) strcpy(text, "Pose un drapeau de couleur"); + if ( num == EVENT_OBJECT_FDELETE ) strcpy(text, "Enlève un drapeau"); + if ( num == EVENT_OBJECT_FCOLORb ) strcpy(text, "\\Drapeaux bleus"); + if ( num == EVENT_OBJECT_FCOLORr ) strcpy(text, "\\Drapeaux rouges"); + if ( num == EVENT_OBJECT_FCOLORg ) strcpy(text, "\\Drapeaux verts"); + if ( num == EVENT_OBJECT_FCOLORy ) strcpy(text, "\\Drapeaux jaunes"); + if ( num == EVENT_OBJECT_FCOLORv ) strcpy(text, "\\Drapeaux violets"); + if ( num == EVENT_OBJECT_FACTORYfa ) strcpy(text, "Fabrique un déménageur volant"); + if ( num == EVENT_OBJECT_FACTORYta ) strcpy(text, "Fabrique un déménageur à chenilles"); + if ( num == EVENT_OBJECT_FACTORYwa ) strcpy(text, "Fabrique un déménageur à roues"); + if ( num == EVENT_OBJECT_FACTORYia ) strcpy(text, "Fabrique un déménageur à pattes"); + if ( num == EVENT_OBJECT_FACTORYfc ) strcpy(text, "Fabrique un shooter volant"); + if ( num == EVENT_OBJECT_FACTORYtc ) strcpy(text, "Fabrique un shooter à chenilles"); + if ( num == EVENT_OBJECT_FACTORYwc ) strcpy(text, "Fabrique un shooter à roues"); + if ( num == EVENT_OBJECT_FACTORYic ) strcpy(text, "Fabrique un shooter à pattes"); + if ( num == EVENT_OBJECT_FACTORYfi ) strcpy(text, "Fabrique un orgaShooter volant"); + if ( num == EVENT_OBJECT_FACTORYti ) strcpy(text, "Fabrique un orgaShooter à chenilles"); + if ( num == EVENT_OBJECT_FACTORYwi ) strcpy(text, "Fabrique un orgaShooter à roues"); + if ( num == EVENT_OBJECT_FACTORYii ) strcpy(text, "Fabrique un orgaShooter à pattes"); + if ( num == EVENT_OBJECT_FACTORYfs ) strcpy(text, "Fabrique un renifleur volant"); + if ( num == EVENT_OBJECT_FACTORYts ) strcpy(text, "Fabrique un renifleur à chenilles"); + if ( num == EVENT_OBJECT_FACTORYws ) strcpy(text, "Fabrique un renifleur à roues"); + if ( num == EVENT_OBJECT_FACTORYis ) strcpy(text, "Fabrique un renifleur à pattes"); + if ( num == EVENT_OBJECT_FACTORYrt ) strcpy(text, "Fabrique un robot secoueur"); + if ( num == EVENT_OBJECT_FACTORYrc ) strcpy(text, "Fabrique un robot phazer"); + if ( num == EVENT_OBJECT_FACTORYrr ) strcpy(text, "Fabrique un robot recycleur"); + if ( num == EVENT_OBJECT_FACTORYrs ) strcpy(text, "Fabrique un robot bouclier"); + if ( num == EVENT_OBJECT_FACTORYsa ) strcpy(text, "Fabrique un robot sous-marin"); + if ( num == EVENT_OBJECT_RTANK ) strcpy(text, "Recherche les chenilles"); + if ( num == EVENT_OBJECT_RFLY ) strcpy(text, "Recherche les robots volants"); + if ( num == EVENT_OBJECT_RTHUMP ) strcpy(text, "Recherche le secoueur"); + if ( num == EVENT_OBJECT_RCANON ) strcpy(text, "Recherche le canon shooter"); + if ( num == EVENT_OBJECT_RTOWER ) strcpy(text, "Recherche la tour de défense"); + if ( num == EVENT_OBJECT_RPHAZER ) strcpy(text, "Recherche le canon phazer"); + if ( num == EVENT_OBJECT_RSHIELD ) strcpy(text, "Recherche le bouclier"); + if ( num == EVENT_OBJECT_RATOMIC ) strcpy(text, "Recherche le nucléaire"); + if ( num == EVENT_OBJECT_RiPAW ) strcpy(text, "Recherche les pattes"); + if ( num == EVENT_OBJECT_RiGUN ) strcpy(text, "Recherche le canon orgaShooter"); + if ( num == EVENT_OBJECT_RESET ) strcpy(text, "Remet au départ"); + if ( num == EVENT_OBJECT_SEARCH ) strcpy(text, "Cherche (\\key action;)"); + if ( num == EVENT_OBJECT_TERRAFORM ) strcpy(text, "Secoue (\\key action;)"); + if ( num == EVENT_OBJECT_FIRE ) strcpy(text, "Tir (\\key action;)"); + if ( num == EVENT_OBJECT_RECOVER ) strcpy(text, "Recycle (\\key action;)"); + if ( num == EVENT_OBJECT_BEGSHIELD ) strcpy(text, "Déploie le bouclier (\\key action;)"); + if ( num == EVENT_OBJECT_ENDSHIELD ) strcpy(text, "Stoppe le bouclier (\\key action;)"); + if ( num == EVENT_OBJECT_DIMSHIELD ) strcpy(text, "Rayon du bouclier"); + if ( num == EVENT_OBJECT_PROGRUN ) strcpy(text, "Exécute le programme sélectionné"); + if ( num == EVENT_OBJECT_PROGEDIT ) strcpy(text, "Edite le programme sélectionné"); + if ( num == EVENT_OBJECT_INFOOK ) strcpy(text, "\\Mettre le SatCom en veille"); + if ( num == EVENT_OBJECT_DELETE ) strcpy(text, "Démolit le bâtiment"); + if ( num == EVENT_OBJECT_GENERGY ) strcpy(text, "Niveau d'énergie"); + if ( num == EVENT_OBJECT_GSHIELD ) strcpy(text, "Niveau du bouclier"); + if ( num == EVENT_OBJECT_GRANGE ) strcpy(text, "Température du réacteur"); + if ( num == EVENT_OBJECT_GPROGRESS ) strcpy(text, "Travail en cours ..."); + if ( num == EVENT_OBJECT_GRADAR ) strcpy(text, "Nombre d'insectes détectés"); + if ( num == EVENT_OBJECT_GINFO ) strcpy(text, "Informations diffusées"); + if ( num == EVENT_OBJECT_COMPASS ) strcpy(text, "Boussole"); +//? if ( num == EVENT_OBJECT_MAP ) strcpy(text, "Mini-carte"); + if ( num == EVENT_OBJECT_MAPZOOM ) strcpy(text, "Zoom mini-carte"); + if ( num == EVENT_OBJECT_CAMERA ) strcpy(text, "Caméra (\\key camera;)"); + if ( num == EVENT_OBJECT_HELP ) strcpy(text, "Instructions sur la sélection"); + if ( num == EVENT_OBJECT_SOLUCE ) strcpy(text, "Donne la solution"); + if ( num == EVENT_OBJECT_SHORTCUT00) strcpy(text, "Permute robots <-> bâtiments"); + if ( num == EVENT_OBJECT_LIMIT ) strcpy(text, "Montre le rayon d'action"); + if ( num == EVENT_DT_VISIT0 || + num == EVENT_DT_VISIT1 || + num == EVENT_DT_VISIT2 || + num == EVENT_DT_VISIT3 || + num == EVENT_DT_VISIT4 ) strcpy(text, "Montre l'endroit"); + if ( num == EVENT_DT_END ) strcpy(text, "Continuer"); + if ( num == EVENT_CMD ) strcpy(text, "Console de commande"); + if ( num == EVENT_SPEED ) strcpy(text, "Vitesse du jeu"); + + if ( num == EVENT_HYPER_PREV ) strcpy(text, "Page précédente"); + if ( num == EVENT_HYPER_NEXT ) strcpy(text, "Page suivante"); + if ( num == EVENT_HYPER_HOME ) strcpy(text, "Page initiale"); + if ( num == EVENT_HYPER_COPY ) strcpy(text, "Copier"); + if ( num == EVENT_HYPER_SIZE1 ) strcpy(text, "Taille 1"); + if ( num == EVENT_HYPER_SIZE2 ) strcpy(text, "Taille 2"); + if ( num == EVENT_HYPER_SIZE3 ) strcpy(text, "Taille 3"); + if ( num == EVENT_HYPER_SIZE4 ) strcpy(text, "Taille 4"); + if ( num == EVENT_HYPER_SIZE5 ) strcpy(text, "Taille 5"); + if ( num == EVENT_SATCOM_HUSTON ) strcpy(text, "Instructions de Houston"); + if ( num == EVENT_SATCOM_SAT ) strcpy(text, "Rapport du satellite"); + if ( num == EVENT_SATCOM_LOADING ) strcpy(text, "Programmes envoyés par Houston"); + if ( num == EVENT_SATCOM_OBJECT ) strcpy(text, "Liste des objets"); + if ( num == EVENT_SATCOM_PROG ) strcpy(text, "Aide à la programmation"); + if ( num == EVENT_SATCOM_SOLUCE ) strcpy(text, "Solution"); + + if ( num == EVENT_STUDIO_OK ) strcpy(text, "D'accord\\Compiler le programme"); + if ( num == EVENT_STUDIO_CANCEL ) strcpy(text, "Annuler\\Annuler toutes les modifications"); + if ( num == EVENT_STUDIO_NEW ) strcpy(text, "Nouveau"); + if ( num == EVENT_STUDIO_OPEN ) strcpy(text, "Ouvrir (Ctrl+o)"); + if ( num == EVENT_STUDIO_SAVE ) strcpy(text, "Enregistrer (Ctrl+s)"); + if ( num == EVENT_STUDIO_UNDO ) strcpy(text, "Annuler (Ctrl+z)"); + if ( num == EVENT_STUDIO_CUT ) strcpy(text, "Couper (Ctrl+x)"); + if ( num == EVENT_STUDIO_COPY ) strcpy(text, "Copier (Ctrl+c)"); + if ( num == EVENT_STUDIO_PASTE ) strcpy(text, "Coller (Ctrl+v)"); + if ( num == EVENT_STUDIO_SIZE ) strcpy(text, "Taille des caractères"); + if ( num == EVENT_STUDIO_TOOL ) strcpy(text, "Instructions (\\key help;)"); + if ( num == EVENT_STUDIO_HELP ) strcpy(text, "Aide à la programmation (\\key prog;)"); + if ( num == EVENT_STUDIO_COMPILE ) strcpy(text, "Compiler"); + if ( num == EVENT_STUDIO_RUN ) strcpy(text, "Démarrer/stopper"); + if ( num == EVENT_STUDIO_REALTIME ) strcpy(text, "Pause/continuer"); + if ( num == EVENT_STUDIO_STEP ) strcpy(text, "Un pas"); + } + + if ( type == RES_OBJECT ) + { + if ( num == OBJECT_PORTICO ) strcpy(text, "Portique"); + if ( num == OBJECT_BASE ) strcpy(text, "Vaisseau spatial"); + if ( num == OBJECT_DERRICK ) strcpy(text, "Derrick"); + if ( num == OBJECT_FACTORY ) strcpy(text, "Fabrique de robots"); + if ( num == OBJECT_REPAIR ) strcpy(text, "Centre de réparation"); + if ( num == OBJECT_STATION ) strcpy(text, "Station de recharge"); + if ( num == OBJECT_CONVERT ) strcpy(text, "Conversion minerai en titanium"); + if ( num == OBJECT_TOWER ) strcpy(text, "Tour de défense"); + if ( num == OBJECT_NEST ) strcpy(text, "Nid"); + if ( num == OBJECT_RESEARCH ) strcpy(text, "Centre de recherches"); + if ( num == OBJECT_RADAR ) strcpy(text, "Radar"); + if ( num == OBJECT_INFO ) strcpy(text, "Borne d'information"); + if ( num == OBJECT_ENERGY ) strcpy(text, "Fabrique de piles"); + if ( num == OBJECT_LABO ) strcpy(text, "Laboratoire de matières organiques"); + if ( num == OBJECT_NUCLEAR ) strcpy(text, "Centrale nucléaire"); + if ( num == OBJECT_PARA ) strcpy(text, "Paratonnerre"); + if ( num == OBJECT_SAFE ) strcpy(text, "Coffre-fort"); + if ( num == OBJECT_HUSTON ) strcpy(text, "Centre de contrôle"); + if ( num == OBJECT_TARGET1 ) strcpy(text, "Cible"); + if ( num == OBJECT_TARGET2 ) strcpy(text, "Cible"); + if ( num == OBJECT_START ) strcpy(text, "Départ"); + if ( num == OBJECT_END ) strcpy(text, "But"); + if ( num == OBJECT_STONE ) strcpy(text, "Minerai de titanium"); + if ( num == OBJECT_URANIUM ) strcpy(text, "Minerai d'uranium"); + if ( num == OBJECT_BULLET ) strcpy(text, "Matière organique"); + if ( num == OBJECT_METAL ) strcpy(text, "Titanium"); + if ( num == OBJECT_POWER ) strcpy(text, "Pile normale"); + if ( num == OBJECT_ATOMIC ) strcpy(text, "Pile nucléaire"); + if ( num == OBJECT_BBOX ) strcpy(text, "Boîte noire"); + if ( num == OBJECT_KEYa ) strcpy(text, "Clé A"); + if ( num == OBJECT_KEYb ) strcpy(text, "Clé B"); + if ( num == OBJECT_KEYc ) strcpy(text, "Clé C"); + if ( num == OBJECT_KEYd ) strcpy(text, "Clé D"); + if ( num == OBJECT_TNT ) strcpy(text, "Explosif"); + if ( num == OBJECT_BOMB ) strcpy(text, "Mine fixe"); + if ( num == OBJECT_BAG ) strcpy(text, "Sac de survie"); + if ( num == OBJECT_WAYPOINT ) strcpy(text, "Indicateur"); + if ( num == OBJECT_FLAGb ) strcpy(text, "Drapeau bleu"); + if ( num == OBJECT_FLAGr ) strcpy(text, "Drapeau rouge"); + if ( num == OBJECT_FLAGg ) strcpy(text, "Drapeau vert"); + if ( num == OBJECT_FLAGy ) strcpy(text, "Drapeau jaune"); + if ( num == OBJECT_FLAGv ) strcpy(text, "Drapeau violet"); + if ( num == OBJECT_MARKPOWER ) strcpy(text, "Emplacement pour station"); + if ( num == OBJECT_MARKURANIUM ) strcpy(text, "Emplacement pour derrick (uranium)"); + if ( num == OBJECT_MARKKEYa ) strcpy(text, "Emplacement pour derrick (clé A)"); + if ( num == OBJECT_MARKKEYb ) strcpy(text, "Emplacement pour derrick (clé B)"); + if ( num == OBJECT_MARKKEYc ) strcpy(text, "Emplacement pour derrick (clé C)"); + if ( num == OBJECT_MARKKEYd ) strcpy(text, "Emplacement pour derrick (clé D)"); + if ( num == OBJECT_MARKSTONE ) strcpy(text, "Emplacement pour derrick (titanium)"); + if ( num == OBJECT_MOBILEft ) strcpy(text, "Robot d'entraînement"); + if ( num == OBJECT_MOBILEtt ) strcpy(text, "Robot d'entraînement"); + if ( num == OBJECT_MOBILEwt ) strcpy(text, "Robot d'entraînement"); + if ( num == OBJECT_MOBILEit ) strcpy(text, "Robot d'entraînement"); + if ( num == OBJECT_MOBILEfa ) strcpy(text, "Robot déménageur"); + if ( num == OBJECT_MOBILEta ) strcpy(text, "Robot déménageur"); + if ( num == OBJECT_MOBILEwa ) strcpy(text, "Robot déménageur"); + if ( num == OBJECT_MOBILEia ) strcpy(text, "Robot déménageur"); + if ( num == OBJECT_MOBILEfc ) strcpy(text, "Robot shooter"); + if ( num == OBJECT_MOBILEtc ) strcpy(text, "Robot shooter"); + if ( num == OBJECT_MOBILEwc ) strcpy(text, "Robot shooter"); + if ( num == OBJECT_MOBILEic ) strcpy(text, "Robot shooter"); + if ( num == OBJECT_MOBILEfi ) strcpy(text, "Robot orgaShooter"); + if ( num == OBJECT_MOBILEti ) strcpy(text, "Robot orgaShooter"); + if ( num == OBJECT_MOBILEwi ) strcpy(text, "Robot orgaShooter"); + if ( num == OBJECT_MOBILEii ) strcpy(text, "Robot orgaShooter"); + if ( num == OBJECT_MOBILEfs ) strcpy(text, "Robot renifleur"); + if ( num == OBJECT_MOBILEts ) strcpy(text, "Robot renifleur"); + if ( num == OBJECT_MOBILEws ) strcpy(text, "Robot renifleur"); + if ( num == OBJECT_MOBILEis ) strcpy(text, "Robot renifleur"); + if ( num == OBJECT_MOBILErt ) strcpy(text, "Robot secoueur"); + if ( num == OBJECT_MOBILErc ) strcpy(text, "Robot phazer"); + if ( num == OBJECT_MOBILErr ) strcpy(text, "Robot recycleur"); + if ( num == OBJECT_MOBILErs ) strcpy(text, "Robot bouclier"); + if ( num == OBJECT_MOBILEsa ) strcpy(text, "Robot sous-marin"); + if ( num == OBJECT_MOBILEtg ) strcpy(text, "Cible d'entraînement"); + if ( num == OBJECT_HUMAN ) strcpy(text, g_gamerName); + if ( num == OBJECT_TECH ) strcpy(text, "Technicien"); + if ( num == OBJECT_TOTO ) strcpy(text, "Robbie"); + if ( num == OBJECT_MOTHER ) strcpy(text, "Pondeuse"); + if ( num == OBJECT_ANT ) strcpy(text, "Fourmi"); + if ( num == OBJECT_SPIDER ) strcpy(text, "Araignée"); + if ( num == OBJECT_BEE ) strcpy(text, "Guêpe"); + if ( num == OBJECT_WORM ) strcpy(text, "Ver"); + if ( num == OBJECT_EGG ) strcpy(text, "Oeuf"); + if ( num == OBJECT_RUINmobilew1 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINmobilew2 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINmobilet1 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINmobilet2 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINmobiler1 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINmobiler2 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINfactory ) strcpy(text, "Bâtiment en ruine"); + if ( num == OBJECT_RUINdoor ) strcpy(text, "Bâtiment en ruine"); + if ( num == OBJECT_RUINsupport ) strcpy(text, "Déchet"); + if ( num == OBJECT_RUINradar ) strcpy(text, "Bâtiment en ruine"); + if ( num == OBJECT_RUINconvert ) strcpy(text, "Bâtiment en ruine"); + if ( num == OBJECT_RUINbase ) strcpy(text, "Epave de vaisseau spatial"); + if ( num == OBJECT_RUINhead ) strcpy(text, "Epave de vaisseau spatial"); + if ( num == OBJECT_APOLLO1 || + num == OBJECT_APOLLO3 || + num == OBJECT_APOLLO4 || + num == OBJECT_APOLLO5 ) strcpy(text, "Vestige d'une mission Apollo"); + if ( num == OBJECT_APOLLO2 ) strcpy(text, "Lunar Roving Vehicle"); + } + + if ( type == RES_ERR ) + { + strcpy(text, "Erreur"); + if ( num == ERR_CMD ) strcpy(text, "Commande inconnue"); + if ( num == ERR_INSTALL ) strcpy(text, "COLOBOT n'est pas installé."); + if ( num == ERR_NOCD ) strcpy(text, "Veuillez mettre le CD de COLOBOT\net relancer le jeu."); + if ( num == ERR_MANIP_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_MANIP_FLY ) strcpy(text, "Impossible en vol"); + if ( num == ERR_MANIP_BUSY ) strcpy(text, "Porte déjà quelque chose"); + if ( num == ERR_MANIP_NIL ) strcpy(text, "Rien à prendre"); + if ( num == ERR_MANIP_MOTOR ) strcpy(text, "Impossible en mouvement"); + if ( num == ERR_MANIP_OCC ) strcpy(text, "Emplacement occupé"); + if ( num == ERR_MANIP_FRIEND ) strcpy(text, "Pas d'autre robot"); + if ( num == ERR_MANIP_RADIO ) strcpy(text, "Vous ne pouvez pas transporter un objet radioactif"); + if ( num == ERR_MANIP_WATER ) strcpy(text, "Vous ne pouvez pas transporter un objet sous l'eau"); + if ( num == ERR_MANIP_EMPTY ) strcpy(text, "Rien à déposer"); + if ( num == ERR_BUILD_FLY ) strcpy(text, "Impossible en vol"); + if ( num == ERR_BUILD_WATER ) strcpy(text, "Impossible sous l'eau"); + if ( num == ERR_BUILD_ENERGY ) strcpy(text, "Pas assez d'énergie"); + if ( num == ERR_BUILD_METALAWAY ) strcpy(text, "Titanium trop loin"); + if ( num == ERR_BUILD_METALNEAR ) strcpy(text, "Titanium trop proche"); + if ( num == ERR_BUILD_METALINEX ) strcpy(text, "Titanium inexistant"); + if ( num == ERR_BUILD_FLAT ) strcpy(text, "Sol pas assez plat"); + if ( num == ERR_BUILD_FLATLIT ) strcpy(text, "Sol plat pas assez grand"); + if ( num == ERR_BUILD_BUSY ) strcpy(text, "Emplacement occupé"); + if ( num == ERR_BUILD_BASE ) strcpy(text, "Trop proche du vaisseau spatial"); + if ( num == ERR_BUILD_NARROW ) strcpy(text, "Trop proche d'un bâtiment"); + if ( num == ERR_BUILD_MOTOR ) strcpy(text, "Impossible en mouvement"); + if ( num == ERR_SEARCH_FLY ) strcpy(text, "Impossible en vol"); + if ( num == ERR_SEARCH_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_SEARCH_MOTOR ) strcpy(text, "Impossible en mouvement"); + if ( num == ERR_TERRA_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_TERRA_ENERGY ) strcpy(text, "Pas assez d'énergie"); + if ( num == ERR_TERRA_FLOOR ) strcpy(text, "Terrain inadapté"); + if ( num == ERR_TERRA_BUILDING ) strcpy(text, "Bâtiment trop proche"); + if ( num == ERR_TERRA_OBJECT ) strcpy(text, "Objet trop proche"); + if ( num == ERR_RECOVER_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_RECOVER_ENERGY ) strcpy(text, "Pas assez d'énergie"); + if ( num == ERR_RECOVER_NULL ) strcpy(text, "Rien à recycler"); + if ( num == ERR_SHIELD_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_SHIELD_ENERGY ) strcpy(text, "Plus d'énergie"); + if ( num == ERR_MOVE_IMPOSSIBLE ) strcpy(text, "Déplacement impossible"); + if ( num == ERR_GOTO_IMPOSSIBLE ) strcpy(text, "Chemin introuvable"); + if ( num == ERR_GOTO_ITER ) strcpy(text, "Position inaccessible"); + if ( num == ERR_GOTO_BUSY ) strcpy(text, "Destination occupée"); + if ( num == ERR_FIRE_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_FIRE_ENERGY ) strcpy(text, "Pas assez d'énergie"); + if ( num == ERR_FIRE_FLY ) strcpy(text, "Impossible en vol"); + if ( num == ERR_CONVERT_EMPTY ) strcpy(text, "Pas de minerai de titanium à convertir"); + if ( num == ERR_DERRICK_NULL ) strcpy(text, "Pas de minerai en sous-sol"); + if ( num == ERR_STATION_NULL ) strcpy(text, "Pas d'énergie en sous-sol"); + if ( num == ERR_TOWER_POWER ) strcpy(text, "Pas de pile"); + if ( num == ERR_TOWER_ENERGY ) strcpy(text, "Plus d'énergie"); + if ( num == ERR_RESEARCH_POWER ) strcpy(text, "Pas de pile"); + if ( num == ERR_RESEARCH_ENERGY ) strcpy(text, "Plus assez d'énergie"); + if ( num == ERR_RESEARCH_TYPE ) strcpy(text, "Pas le bon type de pile"); + if ( num == ERR_RESEARCH_ALREADY) strcpy(text, "Recherche déjà effectuée"); + if ( num == ERR_ENERGY_NULL ) strcpy(text, "Pas d'énergie en sous-sol"); + if ( num == ERR_ENERGY_LOW ) strcpy(text, "Pas encore assez d'énergie"); + if ( num == ERR_ENERGY_EMPTY ) strcpy(text, "Pas de titanium à transformer"); + if ( num == ERR_ENERGY_BAD ) strcpy(text, "Ne transforme que le titanium"); + if ( num == ERR_BASE_DLOCK ) strcpy(text, "Portes bloquées par un robot ou un objet"); + if ( num == ERR_BASE_DHUMAN ) strcpy(text, "Vous devez embarquer pour pouvoir décoller"); + if ( num == ERR_LABO_NULL ) strcpy(text, "Rien à analyser"); + if ( num == ERR_LABO_BAD ) strcpy(text, "N'analyse que la matière organique"); + if ( num == ERR_LABO_ALREADY ) strcpy(text, "Analyse déjà effectuée"); + if ( num == ERR_NUCLEAR_NULL ) strcpy(text, "Pas d'énergie en sous-sol"); + if ( num == ERR_NUCLEAR_LOW ) strcpy(text, "Pas encore assez d'énergie"); + if ( num == ERR_NUCLEAR_EMPTY ) strcpy(text, "Pas d'uranium à transformer"); + if ( num == ERR_NUCLEAR_BAD ) strcpy(text, "Ne transforme que l'uranium"); + if ( num == ERR_FACTORY_NULL ) strcpy(text, "Pas de titanium"); + if ( num == ERR_FACTORY_NEAR ) strcpy(text, "Quelque chose est trop proche"); + if ( num == ERR_RESET_NEAR ) strcpy(text, "Emplacement occupé"); + if ( num == ERR_INFO_NULL ) strcpy(text, "Pas trouvé de borne d'information"); + if ( num == ERR_VEH_VIRUS ) strcpy(text, "Un programme est infecté par un virus"); + if ( num == ERR_BAT_VIRUS ) strcpy(text, "Infecté par un virus, ne fonctionne plus temporairement"); + if ( num == ERR_VEH_POWER ) strcpy(text, "Pas de pile"); + if ( num == ERR_VEH_ENERGY ) strcpy(text, "Plus d'énergie"); + if ( num == ERR_FLAG_FLY ) strcpy(text, "Impossible en vol"); + if ( num == ERR_FLAG_WATER ) strcpy(text, "Impossible en nageant"); + if ( num == ERR_FLAG_MOTOR ) strcpy(text, "Impossible en mouvement"); + if ( num == ERR_FLAG_BUSY ) strcpy(text, "Impossible en portant un objet"); + if ( num == ERR_FLAG_CREATE ) strcpy(text, "Trop de drapeaux de cette couleur (maximum 5)"); + if ( num == ERR_FLAG_PROXY ) strcpy(text, "Trop proche d'un drapeau existant"); + if ( num == ERR_FLAG_DELETE ) strcpy(text, "Aucun drapeau à proximité"); + if ( num == ERR_MISSION_NOTERM ) strcpy(text, "La misssion n'est pas terminée (appuyez sur \\key help; pour plus de détails)"); + if ( num == ERR_DELETEMOBILE ) strcpy(text, "Robot détruit"); + if ( num == ERR_DELETEBUILDING ) strcpy(text, "Bâtiment détruit"); + if ( num == ERR_TOOMANY ) strcpy(text, "Création impossible, il y a trop d'objets"); + + if ( num == INFO_BUILD ) strcpy(text, "Bâtiment terminé"); + if ( num == INFO_CONVERT ) strcpy(text, "Titanium disponible"); + if ( num == INFO_RESEARCH ) strcpy(text, "Recherche terminée"); + if ( num == INFO_RESEARCHTANK ) strcpy(text, "Fabrication d'un robot à chenilles possible"); + if ( num == INFO_RESEARCHFLY ) strcpy(text, "Il est possible de voler avec les touches (\\key gup;) et (\\key gdown;)"); + if ( num == INFO_RESEARCHTHUMP ) strcpy(text, "Fabrication d'un robot secoueur possible"); + if ( num == INFO_RESEARCHCANON ) strcpy(text, "Fabrication de robots shooter possible"); + if ( num == INFO_RESEARCHTOWER ) strcpy(text, "Construction d'une tour de défense possible"); + if ( num == INFO_RESEARCHPHAZER ) strcpy(text, "Fabrication d'un robot phazer possible"); + if ( num == INFO_RESEARCHSHIELD ) strcpy(text, "Fabrication d'un robot bouclier possible"); + if ( num == INFO_RESEARCHATOMIC ) strcpy(text, "Construction d'une centrale nucléaire possible"); + if ( num == INFO_FACTORY ) strcpy(text, "Nouveau robot disponible"); + if ( num == INFO_LABO ) strcpy(text, "Analyse terminée"); + if ( num == INFO_ENERGY ) strcpy(text, "Pile disponible"); + if ( num == INFO_NUCLEAR ) strcpy(text, "Pile nucléaire disponible"); + if ( num == INFO_FINDING ) strcpy(text, "Vous avez trouvé un objet utilisable"); + if ( num == INFO_MARKPOWER ) strcpy(text, "Emplacement pour station trouvé"); + if ( num == INFO_MARKURANIUM ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_MARKSTONE ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_MARKKEYa ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_MARKKEYb ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_MARKKEYc ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_MARKKEYd ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_WIN ) strcpy(text, "<<< Bravo, mission terminée >>>"); + if ( num == INFO_LOST ) strcpy(text, "<<< Désolé, mission échouée >>>"); + if ( num == INFO_LOSTq ) strcpy(text, "<<< Désolé, mission échouée >>>"); + if ( num == INFO_WRITEOK ) strcpy(text, "Enregistrement effectué"); + if ( num == INFO_DELETEPATH ) strcpy(text, "Indicateur atteint"); + if ( num == INFO_DELETEMOTHER ) strcpy(text, "Pondeuse mortellement touchée"); + if ( num == INFO_DELETEANT ) strcpy(text, "Fourmi mortellement touchée"); + if ( num == INFO_DELETEBEE ) strcpy(text, "Guêpe mortellement touchée"); + if ( num == INFO_DELETEWORM ) strcpy(text, "Ver mortellement touché"); + if ( num == INFO_DELETESPIDER ) strcpy(text, "Araignée mortellement touchée"); + if ( num == INFO_BEGINSATCOM ) strcpy(text, "Consultez votre SatCom en appuyant sur \\key help;"); + } + + if ( type == RES_CBOT ) + { + strcpy(text, "Erreur"); + if ( num == TX_OPENPAR ) strcpy(text, "Il manque une parenthèse ouvrante"); + if ( num == TX_CLOSEPAR ) strcpy(text, "Il manque une parenthèse fermante"); + if ( num == TX_NOTBOOL ) strcpy(text, "L'expression doit être un boolean"); + if ( num == TX_UNDEFVAR ) strcpy(text, "Variable non déclarée"); + if ( num == TX_BADLEFT ) strcpy(text, "Assignation impossible"); + if ( num == TX_ENDOF ) strcpy(text, "Terminateur point-virgule non trouvé"); + if ( num == TX_OUTCASE ) strcpy(text, "Instruction ""case"" hors d'un bloc ""switch"""); + if ( num == TX_NOTERM ) strcpy(text, "Instructions après la fin"); + if ( num == TX_CLOSEBLK ) strcpy(text, "Il manque la fin du bloc"); + if ( num == TX_ELSEWITHOUTIF ) strcpy(text, "Instruction ""else"" sans ""if"" correspondant"); + if ( num == TX_OPENBLK ) strcpy(text, "Début d'un bloc attendu"); + if ( num == TX_BADTYPE ) strcpy(text, "Mauvais type de résultat pour l'assignation"); + if ( num == TX_REDEFVAR ) strcpy(text, "Redéfinition d'une variable"); + if ( num == TX_BAD2TYPE ) strcpy(text, "Les deux opérandes ne sont pas de types compatibles"); + if ( num == TX_UNDEFCALL ) strcpy(text, "Routine inconnue"); + if ( num == TX_MISDOTS ) strcpy(text, "Séparateur "" : "" attendu"); + if ( num == TX_WHILE ) strcpy(text, "Manque le mot ""while"""); + if ( num == TX_BREAK ) strcpy(text, "Instruction ""break"" en dehors d'une boucle"); + if ( num == TX_LABEL ) strcpy(text, "Un label ne peut se placer que devant un ""for"", un ""while"", un ""do"" ou un ""switch"""); + if ( num == TX_NOLABEL ) strcpy(text, "Cette étiquette n'existe pas"); + if ( num == TX_NOCASE ) strcpy(text, "Manque une instruction ""case"""); + if ( num == TX_BADNUM ) strcpy(text, "Un nombre est attendu"); + if ( num == TX_VOID ) strcpy(text, "Paramètre void"); + if ( num == TX_NOTYP ) strcpy(text, "Déclaration de type attendu"); + if ( num == TX_NOVAR ) strcpy(text, "Nom d'une variable attendu"); + if ( num == TX_NOFONC ) strcpy(text, "Nom de la fonction attendu"); + if ( num == TX_OVERPARAM ) strcpy(text, "Trop de paramètres"); + if ( num == TX_REDEF ) strcpy(text, "Cette fonction existe déjà"); + if ( num == TX_LOWPARAM ) strcpy(text, "Pas assez de paramètres"); + if ( num == TX_BADPARAM ) strcpy(text, "Aucune fonction de ce nom n'accepte ce(s) type(s) de paramètre(s)"); + if ( num == TX_NUMPARAM ) strcpy(text, "Aucune fonction de ce nom n'accepte ce nombre de paramètres"); + if ( num == TX_NOITEM ) strcpy(text, "Cet élément n'existe pas dans cette classe"); + if ( num == TX_DOT ) strcpy(text, "L'objet n'est pas une instance d'une classe"); + if ( num == TX_NOCONST ) strcpy(text, "Il n'y a pas de constructeur approprié"); + if ( num == TX_REDEFCLASS ) strcpy(text, "Cette classe existe déjà"); + if ( num == TX_CLBRK ) strcpy(text, """ ] "" attendu"); + if ( num == TX_RESERVED ) strcpy(text, "Ce mot est réservé"); + if ( num == TX_BADNEW ) strcpy(text, "Mauvais argument pour ""new"""); + if ( num == TX_OPBRK ) strcpy(text, """ [ "" attendu"); + if ( num == TX_BADSTRING ) strcpy(text, "Une chaîne de caractère est attendue"); + if ( num == TX_BADINDEX ) strcpy(text, "Mauvais type d'index"); + if ( num == TX_PRIVATE ) strcpy(text, "Elément protégé"); + if ( num == TX_NOPUBLIC ) strcpy(text, "Public requis"); + if ( num == TX_DIVZERO ) strcpy(text, "Division par zéro"); + if ( num == TX_NOTINIT ) strcpy(text, "Variable non initialisée"); + if ( num == TX_BADTHROW ) strcpy(text, "Valeur négative refusée pour ""throw"""); + if ( num == TX_NORETVAL ) strcpy(text, "La fonction n'a pas retourné de résultat"); + if ( num == TX_NORUN ) strcpy(text, "Pas de fonction en exécution"); + if ( num == TX_NOCALL ) strcpy(text, "Appel d'une fonction inexistante"); + if ( num == TX_NOCLASS ) strcpy(text, "Cette classe n'existe pas"); + if ( num == TX_NULLPT ) strcpy(text, "Objet n'existe pas"); + if ( num == TX_OPNAN ) strcpy(text, "Opération sur un ""nan"""); + if ( num == TX_OUTARRAY ) strcpy(text, "Accès hors du tableau"); + if ( num == TX_STACKOVER ) strcpy(text, "Débordement de la pile"); + if ( num == TX_DELETEDPT ) strcpy(text, "Objet inaccessible"); + if ( num == TX_FILEOPEN ) strcpy(text, "Ouverture du fichier impossible"); + if ( num == TX_NOTOPEN ) strcpy(text, "Le fichier n'est pas ouvert"); + if ( num == TX_ERRREAD ) strcpy(text, "Erreur à la lecture"); + if ( num == TX_ERRWRITE ) strcpy(text, "Erreur à l'écriture"); + } + + if ( type == RES_KEY ) + { + if ( num == 0 ) strcpy(text, "< aucune >"); + if ( num == VK_LEFT ) strcpy(text, "Flèche Gauche"); + if ( num == VK_RIGHT ) strcpy(text, "Flèche Droite"); + if ( num == VK_UP ) strcpy(text, "Flèche Haut"); + if ( num == VK_DOWN ) strcpy(text, "Flèche Bas"); + if ( num == VK_CANCEL ) strcpy(text, "Control-break"); + if ( num == VK_BACK ) strcpy(text, "<--"); + if ( num == VK_TAB ) strcpy(text, "Tab"); + if ( num == VK_CLEAR ) strcpy(text, "Clear"); + if ( num == VK_RETURN ) strcpy(text, "Entrée"); + if ( num == VK_SHIFT ) strcpy(text, "Shift"); + if ( num == VK_CONTROL ) strcpy(text, "Ctrl"); + if ( num == VK_MENU ) strcpy(text, "Alt"); + if ( num == VK_PAUSE ) strcpy(text, "Pause"); + if ( num == VK_CAPITAL ) strcpy(text, "Caps Lock"); + if ( num == VK_ESCAPE ) strcpy(text, "Esc"); + if ( num == VK_SPACE ) strcpy(text, "Espace"); + if ( num == VK_PRIOR ) strcpy(text, "Page Up"); + if ( num == VK_NEXT ) strcpy(text, "Page Down"); + if ( num == VK_END ) strcpy(text, "End"); + if ( num == VK_HOME ) strcpy(text, "Home"); + if ( num == VK_SELECT ) strcpy(text, "Select"); + if ( num == VK_EXECUTE ) strcpy(text, "Execute"); + if ( num == VK_SNAPSHOT ) strcpy(text, "Print Scrn"); + if ( num == VK_INSERT ) strcpy(text, "Insert"); + if ( num == VK_DELETE ) strcpy(text, "Delete"); + if ( num == VK_HELP ) strcpy(text, "Help"); + if ( num == VK_LWIN ) strcpy(text, "Left Windows"); + if ( num == VK_RWIN ) strcpy(text, "Right Windows"); + if ( num == VK_APPS ) strcpy(text, "Application key"); + if ( num == VK_NUMPAD0 ) strcpy(text, "NumPad 0"); + if ( num == VK_NUMPAD1 ) strcpy(text, "NumPad 1"); + if ( num == VK_NUMPAD2 ) strcpy(text, "NumPad 2"); + if ( num == VK_NUMPAD3 ) strcpy(text, "NumPad 3"); + if ( num == VK_NUMPAD4 ) strcpy(text, "NumPad 4"); + if ( num == VK_NUMPAD5 ) strcpy(text, "NumPad 5"); + if ( num == VK_NUMPAD6 ) strcpy(text, "NumPad 6"); + if ( num == VK_NUMPAD7 ) strcpy(text, "NumPad 7"); + if ( num == VK_NUMPAD8 ) strcpy(text, "NumPad 8"); + if ( num == VK_NUMPAD9 ) strcpy(text, "NumPad 9"); + if ( num == VK_MULTIPLY ) strcpy(text, "NumPad *"); + if ( num == VK_ADD ) strcpy(text, "NumPad +"); + if ( num == VK_SEPARATOR ) strcpy(text, "NumPad sep"); + if ( num == VK_SUBTRACT ) strcpy(text, "NumPad -"); + if ( num == VK_DECIMAL ) strcpy(text, "NumPad ."); + if ( num == VK_DIVIDE ) strcpy(text, "NumPad /"); + if ( num == VK_F1 ) strcpy(text, "F1"); + if ( num == VK_F2 ) strcpy(text, "F2"); + if ( num == VK_F3 ) strcpy(text, "F3"); + if ( num == VK_F4 ) strcpy(text, "F4"); + if ( num == VK_F5 ) strcpy(text, "F5"); + if ( num == VK_F6 ) strcpy(text, "F6"); + if ( num == VK_F7 ) strcpy(text, "F7"); + if ( num == VK_F8 ) strcpy(text, "F8"); + if ( num == VK_F9 ) strcpy(text, "F9"); + if ( num == VK_F10 ) strcpy(text, "F10"); + if ( num == VK_F11 ) strcpy(text, "F11"); + if ( num == VK_F12 ) strcpy(text, "F12"); + if ( num == VK_F13 ) strcpy(text, "F13"); + if ( num == VK_F14 ) strcpy(text, "F14"); + if ( num == VK_F15 ) strcpy(text, "F15"); + if ( num == VK_F16 ) strcpy(text, "F16"); + if ( num == VK_F17 ) strcpy(text, "F17"); + if ( num == VK_F18 ) strcpy(text, "F18"); + if ( num == VK_F19 ) strcpy(text, "F19"); + if ( num == VK_F20 ) strcpy(text, "F20"); + if ( num == VK_NUMLOCK ) strcpy(text, "Num Lock"); + if ( num == VK_SCROLL ) strcpy(text, "Scroll"); + if ( num == VK_ATTN ) strcpy(text, "Attn"); + if ( num == VK_CRSEL ) strcpy(text, "CrSel"); + if ( num == VK_EXSEL ) strcpy(text, "ExSel"); + if ( num == VK_EREOF ) strcpy(text, "Erase EOF"); + if ( num == VK_PLAY ) strcpy(text, "Play"); + if ( num == VK_ZOOM ) strcpy(text, "Zoom"); + if ( num == VK_PA1 ) strcpy(text, "PA1"); + if ( num == VK_OEM_CLEAR ) strcpy(text, "Clear"); + if ( num == VK_BUTTON1 ) strcpy(text, "Bouton 1"); + if ( num == VK_BUTTON2 ) strcpy(text, "Bouton 2"); + if ( num == VK_BUTTON3 ) strcpy(text, "Bouton 3"); + if ( num == VK_BUTTON4 ) strcpy(text, "Bouton 4"); + if ( num == VK_BUTTON5 ) strcpy(text, "Bouton 5"); + if ( num == VK_BUTTON6 ) strcpy(text, "Bouton 6"); + if ( num == VK_BUTTON7 ) strcpy(text, "Bouton 7"); + if ( num == VK_BUTTON8 ) strcpy(text, "Bouton 8"); + if ( num == VK_BUTTON9 ) strcpy(text, "Bouton 9"); + if ( num == VK_BUTTON10 ) strcpy(text, "Bouton 10"); + if ( num == VK_BUTTON11 ) strcpy(text, "Bouton 11"); + if ( num == VK_BUTTON12 ) strcpy(text, "Bouton 12"); + if ( num == VK_BUTTON13 ) strcpy(text, "Bouton 13"); + if ( num == VK_BUTTON14 ) strcpy(text, "Bouton 14"); + if ( num == VK_BUTTON15 ) strcpy(text, "Bouton 15"); + if ( num == VK_BUTTON16 ) strcpy(text, "Bouton 16"); + if ( num == VK_BUTTON17 ) strcpy(text, "Bouton 17"); + if ( num == VK_BUTTON18 ) strcpy(text, "Bouton 18"); + if ( num == VK_BUTTON19 ) strcpy(text, "Bouton 19"); + if ( num == VK_BUTTON20 ) strcpy(text, "Bouton 20"); + if ( num == VK_BUTTON21 ) strcpy(text, "Bouton 21"); + if ( num == VK_BUTTON22 ) strcpy(text, "Bouton 22"); + if ( num == VK_BUTTON23 ) strcpy(text, "Bouton 23"); + if ( num == VK_BUTTON24 ) strcpy(text, "Bouton 24"); + if ( num == VK_BUTTON25 ) strcpy(text, "Bouton 25"); + if ( num == VK_BUTTON26 ) strcpy(text, "Bouton 26"); + if ( num == VK_BUTTON27 ) strcpy(text, "Bouton 27"); + if ( num == VK_BUTTON28 ) strcpy(text, "Bouton 28"); + if ( num == VK_BUTTON29 ) strcpy(text, "Bouton 29"); + if ( num == VK_BUTTON30 ) strcpy(text, "Bouton 30"); + if ( num == VK_BUTTON31 ) strcpy(text, "Bouton 31"); + if ( num == VK_BUTTON32 ) strcpy(text, "Bouton 32"); + if ( num == VK_WHEELUP ) strcpy(text, "Molette haut"); + if ( num == VK_WHEELDOWN ) strcpy(text, "Molette bas"); + } +#endif + +#if _POLISH + if ( type == RES_TEXT ) + { + #if _FULL + if ( num == RT_VERSION_ID ) strcpy(text, "Wersja 1.9 /pl"); + #endif + #if _NET | _SCHOOL + if ( num == RT_VERSION_ID ) strcpy(text, "Szko³a 1.9 /pl"); + #endif + #if _DEMO + if ( num == RT_VERSION_ID ) strcpy(text, "Demo 1.9 /pl"); + #endif + if ( num == RT_DISINFO_TITLE ) strcpy(text, "SatCom"); + if ( num == RT_WINDOW_MAXIMIZED ) strcpy(text, "Powiêksz"); + if ( num == RT_WINDOW_MINIMIZED ) strcpy(text, "Pomniejsz"); + if ( num == RT_WINDOW_STANDARD ) strcpy(text, "Normalna wielkoœæ"); + if ( num == RT_WINDOW_CLOSE ) strcpy(text, "Zamknij"); + + if ( num == RT_STUDIO_TITLE ) strcpy(text, "Edytor programu"); + if ( num == RT_SCRIPT_NEW ) strcpy(text, "Nowy"); + if ( num == RT_NAME_DEFAULT ) strcpy(text, "Gracz"); + if ( num == RT_IO_NEW ) strcpy(text, "Nowy ..."); + if ( num == RT_KEY_OR ) strcpy(text, " lub "); + + if ( num == RT_TITLE_BASE ) strcpy(text, "COLOBOT"); + if ( num == RT_TITLE_INIT ) strcpy(text, "COLOBOT"); + if ( num == RT_TITLE_TRAINER ) strcpy(text, "Æwiczenia programistyczne"); + if ( num == RT_TITLE_DEFI ) strcpy(text, "Wyzwania"); + if ( num == RT_TITLE_MISSION ) strcpy(text, "Misje"); + if ( num == RT_TITLE_FREE ) strcpy(text, "Swobodna gra"); + if ( num == RT_TITLE_USER ) strcpy(text, "Poziom u¿ytkownika"); + if ( num == RT_TITLE_PROTO ) strcpy(text, "Prototypy"); + if ( num == RT_TITLE_SETUP ) strcpy(text, "Opcje"); + if ( num == RT_TITLE_NAME ) strcpy(text, "Imiê gracza"); + if ( num == RT_TITLE_PERSO ) strcpy(text, "Dostosuj wygl¹d"); + if ( num == RT_TITLE_WRITE ) strcpy(text, "Zapisz bie¿¹c¹ misjê"); + if ( num == RT_TITLE_READ ) strcpy(text, "Wczytaj zapisan¹ misjê"); + + if ( num == RT_PLAY_CHAPt ) strcpy(text, " Rozdzia³y:"); + if ( num == RT_PLAY_CHAPd ) strcpy(text, " Rozdzia³y:"); + if ( num == RT_PLAY_CHAPm ) strcpy(text, " Planety:"); + if ( num == RT_PLAY_CHAPf ) strcpy(text, " Planety:"); + if ( num == RT_PLAY_CHAPu ) strcpy(text, " Poziom u¿ytkownika:"); + if ( num == RT_PLAY_CHAPp ) strcpy(text, " Planety:"); + if ( num == RT_PLAY_LISTt ) strcpy(text, " Æwiczenia w tym rozdziale:"); + if ( num == RT_PLAY_LISTd ) strcpy(text, " Wyzwania w tym rozdziale:"); + if ( num == RT_PLAY_LISTm ) strcpy(text, " Misje na tej planecie:"); + if ( num == RT_PLAY_LISTf ) strcpy(text, " Swobodna gra na tej planecie:"); + if ( num == RT_PLAY_LISTu ) strcpy(text, " Misje na tym poziomie:"); + if ( num == RT_PLAY_LISTp ) strcpy(text, " Prototypy na tej planecie:"); + if ( num == RT_PLAY_RESUME ) strcpy(text, " Streszczenie:"); + + if ( num == RT_SETUP_DEVICE ) strcpy(text, " Sterowniki:"); + if ( num == RT_SETUP_MODE ) strcpy(text, " Rozdzielczoœæ:"); + if ( num == RT_SETUP_KEY1 ) strcpy(text, "1) Najpierw kliknij klawisz, który chcesz przedefiniowaæ."); + if ( num == RT_SETUP_KEY2 ) strcpy(text, "2) Nastêpnie naciœnij klawisz, którego chcesz u¿ywaæ."); + + if ( num == RT_PERSO_FACE ) strcpy(text, "Rodzaj twarzy:"); + if ( num == RT_PERSO_GLASSES ) strcpy(text, "Okulary:"); + if ( num == RT_PERSO_HAIR ) strcpy(text, "Kolor w³osów:"); + if ( num == RT_PERSO_COMBI ) strcpy(text, "Kolor skafandra:"); + if ( num == RT_PERSO_BAND ) strcpy(text, "Kolor pasków:"); + + if ( num == RT_DIALOG_TITLE ) strcpy(text, "COLOBOT"); + if ( num == RT_DIALOG_ABORT ) strcpy(text, "Opuœciæ misjê?"); + if ( num == RT_DIALOG_QUIT ) strcpy(text, "Czy na pewno chcesz opuœciæ grê COLOBOT?"); + if ( num == RT_DIALOG_YES ) strcpy(text, "Przerwij\\Przerywa bie¿¹c¹ misjê"); + if ( num == RT_DIALOG_NO ) strcpy(text, "Kontynuuj\\Kontynuuje bie¿¹c¹ misjê"); + if ( num == RT_DIALOG_YESQUIT ) strcpy(text, "Zakoñcz\\Koñczy grê COLOTOT"); + if ( num == RT_DIALOG_NOQUIT ) strcpy(text, "Kontynuuj\\Kontynuuje grê"); + if ( num == RT_DIALOG_DELOBJ ) strcpy(text, "Czy na pewno chcesz zniszczyæ zaznaczony budynek?"); + if ( num == RT_DIALOG_DELGAME ) strcpy(text, "Czy na pewno chcesz skasowaæ grê gracza %s? "); + if ( num == RT_DIALOG_YESDEL ) strcpy(text, "Usuñ"); + if ( num == RT_DIALOG_NODEL ) strcpy(text, "Anuluj"); + if ( num == RT_DIALOG_LOADING ) strcpy(text, "WCZYTYWANIE"); + + if ( num == RT_STUDIO_LISTTT ) strcpy(text, "Skróty klawiszowe (\\key cbot;)"); + if ( num == RT_STUDIO_COMPOK ) strcpy(text, "Program skompilowany (0 b³êdów)"); + if ( num == RT_STUDIO_PROGSTOP ) strcpy(text, "Program zakoñczony"); + + if ( num == RT_SATCOM_LIST ) strcpy(text, "\\b;Lista obiektów\n"); + if ( num == RT_SATCOM_BOT ) strcpy(text, "\\b;Roboty\n"); + if ( num == RT_SATCOM_BUILDING ) strcpy(text, "\\b;Budynki\n"); + if ( num == RT_SATCOM_FRET ) strcpy(text, "\\b;Obiekty ruchome\n"); + if ( num == RT_SATCOM_ALIEN ) strcpy(text, "\\b;Obcy\n"); + if ( num == RT_SATCOM_NULL ) strcpy(text, "\\c; (brak)\\n;\n"); + if ( num == RT_SATCOM_ERROR1 ) strcpy(text, "\\b;B³¹d\n"); + if ( num == RT_SATCOM_ERROR2 ) strcpy(text, "Lista jest dostêpna jedynie gdy dzia³a \\l;stacja radarowa\\u object\\radar;.\n"); + + if ( num == RT_IO_OPEN ) strcpy(text, "Otwórz"); + if ( num == RT_IO_SAVE ) strcpy(text, "Zapisz"); + if ( num == RT_IO_LIST ) strcpy(text, "Folder: %s"); + if ( num == RT_IO_NAME ) strcpy(text, "Nazwa:"); + if ( num == RT_IO_DIR ) strcpy(text, "Folder:"); + if ( num == RT_IO_PRIVATE ) strcpy(text, "Prywatny\\Folder prywatny"); + if ( num == RT_IO_PUBLIC ) strcpy(text, "Publiczny\\Folder ogólnodostêpny"); + + if ( num == RT_GENERIC_DEV1 ) strcpy(text, "Twórcy:"); + if ( num == RT_GENERIC_DEV2 ) strcpy(text, "www.epsitec.com"); +//? if ( num == RT_GENERIC_EDIT1 ) strcpy(text, "Wersja angielska wydana przez:"); +//? if ( num == RT_GENERIC_EDIT2 ) strcpy(text, "www.?.com"); + if ( num == RT_GENERIC_EDIT1 ) strcpy(text, " "); + if ( num == RT_GENERIC_EDIT2 ) strcpy(text, " "); + } + + if ( type == RES_EVENT ) + { + if ( num == EVENT_BUTTON_OK ) strcpy(text, "OK"); + if ( num == EVENT_BUTTON_CANCEL ) strcpy(text, "Anuluj"); + if ( num == EVENT_BUTTON_NEXT ) strcpy(text, "Nastêpny"); + if ( num == EVENT_BUTTON_PREV ) strcpy(text, "Poprzedni"); + if ( num == EVENT_BUTTON_QUIT ) strcpy(text, "Menu (\\key quit;)"); + + if ( num == EVENT_DIALOG_OK ) strcpy(text, "OK"); + if ( num == EVENT_DIALOG_CANCEL ) strcpy(text, "Anuluj"); + + if ( num == EVENT_INTERFACE_TRAINER) strcpy(text, "Æwiczenia\\Æwiczenia programistyczne"); + if ( num == EVENT_INTERFACE_DEFI ) strcpy(text, "Wyzwania\\Wyzwania programistyczne"); + if ( num == EVENT_INTERFACE_MISSION) strcpy(text, "Misje\\Wybierz misjê"); + if ( num == EVENT_INTERFACE_FREE ) strcpy(text, "Swobodna gra\\Swobodna gra bez konkretnych celów"); + if ( num == EVENT_INTERFACE_USER ) strcpy(text, "Poziom\\Poziomy u¿ytkownika"); + if ( num == EVENT_INTERFACE_PROTO ) strcpy(text, "Prototypy\\Prototypy w trakcie rozwijania"); + if ( num == EVENT_INTERFACE_NAME ) strcpy(text, "Nowy gracz\\Wybierz imiê gracza"); + if ( num == EVENT_INTERFACE_SETUP ) strcpy(text, "Opcje\\Preferencje"); + if ( num == EVENT_INTERFACE_AGAIN ) strcpy(text, "Uruchom ponownie\\Uruchamia ponownie misjê od pocz¹tku"); + if ( num == EVENT_INTERFACE_WRITE ) strcpy(text, "Zapisz\\Zapisuje bie¿¹c¹ misjê"); + if ( num == EVENT_INTERFACE_READ ) strcpy(text, "Wczytaj\\Wczytuje zapisan¹ misjê"); + if ( num == EVENT_INTERFACE_ABORT ) strcpy(text, "\\Powróæ do gry COLOBOT"); + if ( num == EVENT_INTERFACE_QUIT ) strcpy(text, "Zakoñcz\\Koñczy grê COLOTOT"); + if ( num == EVENT_INTERFACE_BACK ) strcpy(text, "<< Wstecz \\Wraca do poprzedniego ekranu"); + if ( num == EVENT_INTERFACE_PLAY ) strcpy(text, "Graj\\Rozpoczyna misjê!"); + if ( num == EVENT_INTERFACE_SETUPd ) strcpy(text, "Urz¹dzenie\\Ustawienia sterownika i rozdzielczoœci"); + if ( num == EVENT_INTERFACE_SETUPg ) strcpy(text, "Grafika\\Ustawienia grafiki"); + if ( num == EVENT_INTERFACE_SETUPp ) strcpy(text, "Gra\\Ustawienia gry"); + if ( num == EVENT_INTERFACE_SETUPc ) strcpy(text, "Sterowanie\\Ustawienia klawiatury, joysticka i myszy"); + if ( num == EVENT_INTERFACE_SETUPs ) strcpy(text, "DŸwiêk\\G³oœnoœæ muzyki i dŸwiêków gry"); + if ( num == EVENT_INTERFACE_DEVICE ) strcpy(text, "Jednostka"); + if ( num == EVENT_INTERFACE_RESOL ) strcpy(text, "Rozdzielczoœæ"); + if ( num == EVENT_INTERFACE_FULL ) strcpy(text, "Pe³ny ekran\\Pe³ny ekran lub tryb okna"); + if ( num == EVENT_INTERFACE_APPLY ) strcpy(text, "Zastosuj zmiany\\Aktywuje zmienione ustawienia"); + + if ( num == EVENT_INTERFACE_TOTO ) strcpy(text, "Robbie\\Twój asystent"); + if ( num == EVENT_INTERFACE_SHADOW ) strcpy(text, "Cienie\\Cienie na ziemi"); + if ( num == EVENT_INTERFACE_GROUND ) strcpy(text, "Znaki na ziemi\\Znaki na ziemi"); + if ( num == EVENT_INTERFACE_DIRTY ) strcpy(text, "Kurz\\Kurz i bród na robotach i budynkach"); + if ( num == EVENT_INTERFACE_FOG ) strcpy(text, "Mg³a\\Mg³a"); + if ( num == EVENT_INTERFACE_LENS ) strcpy(text, "Promienie s³oneczne\\Promienie s³oneczne na niebie"); + if ( num == EVENT_INTERFACE_SKY ) strcpy(text, "Niebo\\Chmury i mg³awice"); + if ( num == EVENT_INTERFACE_PLANET ) strcpy(text, "Planety i gwiazdy\\Obiekty astronomiczne na niebie"); + if ( num == EVENT_INTERFACE_LIGHT ) strcpy(text, "Dynamiczne oœwietlenie\\Ruchome Ÿród³a œwiat³a"); + if ( num == EVENT_INTERFACE_PARTI ) strcpy(text, "Liczba cz¹stek\\Wybuchy, kurz, odbicia, itp."); + if ( num == EVENT_INTERFACE_CLIP ) strcpy(text, "G³êbokoœæ pola\\Maksymalna widocznoœæ"); + if ( num == EVENT_INTERFACE_DETAIL ) strcpy(text, "Szczegó³y\\Jakoœæ wizualna obiektów 3D"); + if ( num == EVENT_INTERFACE_TEXTURE) strcpy(text, "Tekstury\\Jakoœæ tekstur "); + if ( num == EVENT_INTERFACE_GADGET ) strcpy(text, "Iloœæ elementów dekoracyjnych \\Iloœæ elementów czysto dekoracyjnych"); + if ( num == EVENT_INTERFACE_RAIN ) strcpy(text, "Cz¹stki w interfejsie\\Para i iskry z silników w interfejsie"); + if ( num == EVENT_INTERFACE_GLINT ) strcpy(text, "Odbicia na przyciskach \\Œwiec¹ce przyciski"); + if ( num == EVENT_INTERFACE_TOOLTIP) strcpy(text, "Dymki pomocy\\Wyjaœnia funkcje przycisków"); + if ( num == EVENT_INTERFACE_MOVIES ) strcpy(text, "Sekwencje filmowe\\Filmy przed rozpoczêciem i na zakoñczenie misji"); + if ( num == EVENT_INTERFACE_NICERST) strcpy(text, "Koñcowy film\\Film na zakoñczenie æwiczeñ"); + if ( num == EVENT_INTERFACE_HIMSELF) strcpy(text, "Przyjacielski ogieñ\\W³asne strza³y uszkadzaj¹ Twoje obiekty"); + if ( num == EVENT_INTERFACE_SCROLL ) strcpy(text, "Przewijanie\\Ekran jest przewijany gdy mysz dotknie prawej lub lewej jego krawêdzi"); + if ( num == EVENT_INTERFACE_INVERTX) strcpy(text, "Odwrócenie myszy X\\Odwrócenie kierunków przewijania na w poziomie"); + if ( num == EVENT_INTERFACE_INVERTY) strcpy(text, "Odwrócenie myszy Y\\Odwrócenie kierunków przewijania na w pionie"); + if ( num == EVENT_INTERFACE_EFFECT ) strcpy(text, "Wstrz¹sy przy wybuchach\\Ekran trzêsie siê podczas wybuchów"); + if ( num == EVENT_INTERFACE_MOUSE ) strcpy(text, "Cieñ myszy\\Dodaje cieñ kursorowi myszy"); + if ( num == EVENT_INTERFACE_EDITMODE) strcpy(text, "Automatyczne wciêcia\\Podczas edycji programu"); + if ( num == EVENT_INTERFACE_EDITVALUE)strcpy(text, "Du¿e wciêcie\\2 lub 4 spacje wciêcia na ka¿dy poziom zdefiniowany przez klamry"); + + if ( num == EVENT_INTERFACE_KDEF ) strcpy(text, "Standardowa kontrola\\Standardowe klawisze funkcyjne"); + if ( num == EVENT_INTERFACE_KLEFT ) strcpy(text, "Skrêæ w lewo\\obraca robota w lewo"); + if ( num == EVENT_INTERFACE_KRIGHT ) strcpy(text, "Obróæ w prawo\\obraca robota w prawo"); + if ( num == EVENT_INTERFACE_KUP ) strcpy(text, "Naprzód\\Porusza do przodu"); + if ( num == EVENT_INTERFACE_KDOWN ) strcpy(text, "Wstecz\\Porusza do ty³u"); + if ( num == EVENT_INTERFACE_KGUP ) strcpy(text, "W górê\\Zwiêksza moc silnika'"); + if ( num == EVENT_INTERFACE_KGDOWN ) strcpy(text, "W dó³\\Zmniejsza moc silnika"); + if ( num == EVENT_INTERFACE_KCAMERA) strcpy(text, "Zmieñ kamerê\\Prze³¹cza pomiêdzy kamer¹ pok³adow¹ i œledz¹c¹"); + if ( num == EVENT_INTERFACE_KDESEL ) strcpy(text, "Poprzedni obiekt\\Zaznacz poprzedni obiekt"); + if ( num == EVENT_INTERFACE_KACTION) strcpy(text, "Standardowa akcja\\Standardowa akcja robota (podnieœ/upuœæ, strzelaj, szukaj, itp.)"); + if ( num == EVENT_INTERFACE_KNEAR ) strcpy(text, "Kamera bli¿ej\\Przybli¿a kamerê"); + if ( num == EVENT_INTERFACE_KAWAY ) strcpy(text, "Kamera dalej\\Oddala kamerê"); + if ( num == EVENT_INTERFACE_KNEXT ) strcpy(text, "Nastêpny obiekt\\Zaznacza nastêpny obiekt"); + if ( num == EVENT_INTERFACE_KHUMAN ) strcpy(text, "Zaznacz astronautê\\Zaznacza astronautê"); + if ( num == EVENT_INTERFACE_KQUIT ) strcpy(text, "Zakoñcz\\Koñczy bie¿¹c¹ misjê lub æwiczenie"); + if ( num == EVENT_INTERFACE_KHELP ) strcpy(text, "Rozkazy\\Pokazuje rozkazy dotycz¹ce bie¿¹cej misji"); + if ( num == EVENT_INTERFACE_KPROG ) strcpy(text, "Podrêcznik programowania"); + if ( num == EVENT_INTERFACE_KCBOT ) strcpy(text, "Pomoc dot. s³ów kluczowych\\Dok³adniejsza pomoc na temat s³ów kluczowych"); + if ( num == EVENT_INTERFACE_KVISIT ) strcpy(text, "Miejsce nadania wiadomoœci\\Pokazuje sk¹d zosta³a wys³ana ostatnia wiadomoœæ"); + if ( num == EVENT_INTERFACE_KSPEED10) strcpy(text, "Prêdkoœæ 1,0x\\Prêdkoœæ normalna"); + if ( num == EVENT_INTERFACE_KSPEED15) strcpy(text, "Prêdkoœæ 1,5x\\1,5 raza szybciej"); + if ( num == EVENT_INTERFACE_KSPEED20) strcpy(text, "Prêdkoœæ 2,0x\\Dwa razy szybciej"); + if ( num == EVENT_INTERFACE_KSPEED30) strcpy(text, "Prêdkoœæ 3,0x\\Trzy razy szybciej"); + + if ( num == EVENT_INTERFACE_VOLSOUND) strcpy(text, "Efekty dŸwiêkowe:\\G³oœnoœæ silników, g³osów, strza³ów, itp."); + if ( num == EVENT_INTERFACE_VOLMUSIC) strcpy(text, "Muzyka w tle :\\G³oœnoœæ œcie¿ek dŸwiêkowych z p³yty CD"); + if ( num == EVENT_INTERFACE_SOUND3D) strcpy(text, "DŸwiêk 3D\\Przestrzenne pozycjonowanie dŸwiêków"); + + if ( num == EVENT_INTERFACE_MIN ) strcpy(text, "Najni¿sza\\Minimalna jakoœæ grafiki (najwy¿sza czêstotliwoœæ odœwie¿ania)"); + if ( num == EVENT_INTERFACE_NORM ) strcpy(text, "Normalna\\Normalna jakoœæ grafiki"); + if ( num == EVENT_INTERFACE_MAX ) strcpy(text, "Najwy¿sza\\Maksymalna jakoœæ grafiki (najni¿sza czêstotliwoœæ odœwie¿ania)"); + + if ( num == EVENT_INTERFACE_SILENT ) strcpy(text, "Cisza\\Bez dŸwiêków"); + if ( num == EVENT_INTERFACE_NOISY ) strcpy(text, "Normalne\\Normalna g³oœnoœæ dŸwiêków"); + + if ( num == EVENT_INTERFACE_JOYSTICK) strcpy(text, "U¿ywaj joysticka\\Joystick lub klawiatura"); + if ( num == EVENT_INTERFACE_SOLUCE ) strcpy(text, "Dostêp do rozwi¹zania\\Pokazuje rozwi¹zanie (szczegó³owe instrukcje dotycz¹ce misji)"); + + if ( num == EVENT_INTERFACE_NEDIT ) strcpy(text, "\\Nowe imiê gracza"); + if ( num == EVENT_INTERFACE_NOK ) strcpy(text, "OK\\Wybiera zaznaczonego gracza"); + if ( num == EVENT_INTERFACE_NCANCEL) strcpy(text, "Anuluj\\Zachowuje bie¿¹ce imiê gracza"); + if ( num == EVENT_INTERFACE_NDELETE) strcpy(text, "Usuñ gracza\\Usuwa gracza z listy"); + if ( num == EVENT_INTERFACE_NLABEL ) strcpy(text, "Imiê gracza"); + + if ( num == EVENT_INTERFACE_IOWRITE) strcpy(text, "Zapisz\\Zapisuje bie¿¹c¹ misjê"); + if ( num == EVENT_INTERFACE_IOREAD ) strcpy(text, "Wczytaj\\Wczytuje zaznaczon¹ misjê"); + if ( num == EVENT_INTERFACE_IOLIST ) strcpy(text, "Lista zapisanych misji"); + if ( num == EVENT_INTERFACE_IOLABEL) strcpy(text, "Nazwa pliku:"); + if ( num == EVENT_INTERFACE_IONAME ) strcpy(text, "Nazwa misji"); + if ( num == EVENT_INTERFACE_IOIMAGE) strcpy(text, "Fotografia"); + if ( num == EVENT_INTERFACE_IODELETE) strcpy(text, "Usuñ\\Usuwa zaznaczony plik"); + + if ( num == EVENT_INTERFACE_PERSO ) strcpy(text, "Wygl¹d\\Wybierz swoj¹ postaæ"); + if ( num == EVENT_INTERFACE_POK ) strcpy(text, "OK"); + if ( num == EVENT_INTERFACE_PCANCEL) strcpy(text, "Anuluj"); + if ( num == EVENT_INTERFACE_PDEF ) strcpy(text, "Standardowe\\Standardowe ustawienia wygl¹du"); + if ( num == EVENT_INTERFACE_PHEAD ) strcpy(text, "G³owa\\Twarz i w³osy"); + if ( num == EVENT_INTERFACE_PBODY ) strcpy(text, "Skafander\\Skafander astronauty"); + if ( num == EVENT_INTERFACE_PLROT ) strcpy(text, "\\Obróæ w lewo"); + if ( num == EVENT_INTERFACE_PRROT ) strcpy(text, "\\Obróæ w prawo"); + if ( num == EVENT_INTERFACE_PCRa ) strcpy(text, "Czerwony"); + if ( num == EVENT_INTERFACE_PCGa ) strcpy(text, "Zielony"); + if ( num == EVENT_INTERFACE_PCBa ) strcpy(text, "Niebieski"); + if ( num == EVENT_INTERFACE_PCRb ) strcpy(text, "Czerwony"); + if ( num == EVENT_INTERFACE_PCGb ) strcpy(text, "Zielony"); + if ( num == EVENT_INTERFACE_PCBb ) strcpy(text, "Niebieski"); + if ( num == EVENT_INTERFACE_PFACE1 ) strcpy(text, "\\Twarz 1"); + if ( num == EVENT_INTERFACE_PFACE2 ) strcpy(text, "\\Twarz 4"); + if ( num == EVENT_INTERFACE_PFACE3 ) strcpy(text, "\\Twarz 3"); + if ( num == EVENT_INTERFACE_PFACE4 ) strcpy(text, "\\Twarz 2"); + if ( num == EVENT_INTERFACE_PGLASS0) strcpy(text, "\\Bez okularów"); + if ( num == EVENT_INTERFACE_PGLASS1) strcpy(text, "\\Okulary 1"); + if ( num == EVENT_INTERFACE_PGLASS2) strcpy(text, "\\Okulary 2"); + if ( num == EVENT_INTERFACE_PGLASS3) strcpy(text, "\\Okulary 3"); + if ( num == EVENT_INTERFACE_PGLASS4) strcpy(text, "\\Okulary 4"); + if ( num == EVENT_INTERFACE_PGLASS5) strcpy(text, "\\Okulary 5"); + + if ( num == EVENT_OBJECT_DESELECT ) strcpy(text, "Poprzednie zaznaczenie (\\key desel;)"); + if ( num == EVENT_OBJECT_LEFT ) strcpy(text, "Skrêæ w lewo (\\key left;)"); + if ( num == EVENT_OBJECT_RIGHT ) strcpy(text, "Skrêæ w prawo (\\key right;)"); + if ( num == EVENT_OBJECT_UP ) strcpy(text, "Naprzód (\\key up;)"); + if ( num == EVENT_OBJECT_DOWN ) strcpy(text, "Cofnij (\\key down;)"); + if ( num == EVENT_OBJECT_GASUP ) strcpy(text, "Góra (\\key gup;)"); + if ( num == EVENT_OBJECT_GASDOWN ) strcpy(text, "Dó³ (\\key gdown;)"); + if ( num == EVENT_OBJECT_HTAKE ) strcpy(text, "Podnieœ lub upuœæ (\\key action;)"); + if ( num == EVENT_OBJECT_MTAKE ) strcpy(text, "Podnieœ lub upuœæ (\\key action;)"); + if ( num == EVENT_OBJECT_MFRONT ) strcpy(text, "..przed"); + if ( num == EVENT_OBJECT_MBACK ) strcpy(text, "..za"); + if ( num == EVENT_OBJECT_MPOWER ) strcpy(text, "..ogniwo elektryczne"); + if ( num == EVENT_OBJECT_BHELP ) strcpy(text, "Rozkazy dotycz¹ce misji (\\key help;)"); + if ( num == EVENT_OBJECT_BTAKEOFF ) strcpy(text, "Odleæ, aby zakoñczyæ misjê"); + if ( num == EVENT_OBJECT_BDERRICK ) strcpy(text, "Zbuduj kopalniê"); + if ( num == EVENT_OBJECT_BSTATION ) strcpy(text, "Zbuduj elektrowniê"); + if ( num == EVENT_OBJECT_BFACTORY ) strcpy(text, "Zbuduj fabrykê robotów"); + if ( num == EVENT_OBJECT_BREPAIR ) strcpy(text, "Zbuduj warsztat"); + if ( num == EVENT_OBJECT_BCONVERT ) strcpy(text, "Zbuduj hutê"); + if ( num == EVENT_OBJECT_BTOWER ) strcpy(text, "Zbuduj wie¿ê obronn¹"); + if ( num == EVENT_OBJECT_BRESEARCH ) strcpy(text, "Zbuduj centrum badawcze"); + if ( num == EVENT_OBJECT_BRADAR ) strcpy(text, "Zbuduj stacjê radarow¹"); + if ( num == EVENT_OBJECT_BENERGY ) strcpy(text, "Zbuduj fabrykê ogniw elektrycznych"); + if ( num == EVENT_OBJECT_BLABO ) strcpy(text, "Zbuduj laboratorium"); + if ( num == EVENT_OBJECT_BNUCLEAR ) strcpy(text, "Zbuduj elektrowniê atomow¹"); + if ( num == EVENT_OBJECT_BPARA ) strcpy(text, "Zbuduj odgromnik"); + if ( num == EVENT_OBJECT_BINFO ) strcpy(text, "Zbuduj stacjê przekaŸnikow¹"); + if ( num == EVENT_OBJECT_GFLAT ) strcpy(text, "Poka¿ czy teren jest p³aski"); + if ( num == EVENT_OBJECT_FCREATE ) strcpy(text, "Postaw flagê"); + if ( num == EVENT_OBJECT_FDELETE ) strcpy(text, "Usuñ flagê"); + if ( num == EVENT_OBJECT_FCOLORb ) strcpy(text, "\\Niebieskie flagi"); + if ( num == EVENT_OBJECT_FCOLORr ) strcpy(text, "\\Czerwone flagi"); + if ( num == EVENT_OBJECT_FCOLORg ) strcpy(text, "\\Zielone flagi"); + if ( num == EVENT_OBJECT_FCOLORy ) strcpy(text, "\\¯ó³te flagi"); + if ( num == EVENT_OBJECT_FCOLORv ) strcpy(text, "\\Fioletowe flagi"); + if ( num == EVENT_OBJECT_FACTORYfa ) strcpy(text, "Zbuduj transporter lataj¹cy"); + if ( num == EVENT_OBJECT_FACTORYta ) strcpy(text, "Zbuduj transporter na g¹sienicach"); + if ( num == EVENT_OBJECT_FACTORYwa ) strcpy(text, "Zbuduj transporter na ko³ach"); + if ( num == EVENT_OBJECT_FACTORYia ) strcpy(text, "Zbuduj transporter na nogach"); + if ( num == EVENT_OBJECT_FACTORYfc ) strcpy(text, "Zbuduj dzia³o lataj¹ce"); + if ( num == EVENT_OBJECT_FACTORYtc ) strcpy(text, "Zbuduj dzia³o na g¹sienicach"); + if ( num == EVENT_OBJECT_FACTORYwc ) strcpy(text, "Zbuduj dzia³o na ko³ach"); + if ( num == EVENT_OBJECT_FACTORYic ) strcpy(text, "Zbuduj dzia³o na nogach"); + if ( num == EVENT_OBJECT_FACTORYfi ) strcpy(text, "Zbuduj lataj¹ce dzia³o organiczne"); + if ( num == EVENT_OBJECT_FACTORYti ) strcpy(text, "Zbuduj dzia³o organiczne na g¹sienicach"); + if ( num == EVENT_OBJECT_FACTORYwi ) strcpy(text, "Zbuduj dzia³o organiczne na ko³ach"); + if ( num == EVENT_OBJECT_FACTORYii ) strcpy(text, "Zbuduj dzia³o organiczne na nogach"); + if ( num == EVENT_OBJECT_FACTORYfs ) strcpy(text, "Zbuduj szperacz lataj¹cy"); + if ( num == EVENT_OBJECT_FACTORYts ) strcpy(text, "Zbuduj szperacz na g¹sienicach"); + if ( num == EVENT_OBJECT_FACTORYws ) strcpy(text, "Zbuduj szperacz na ko³ach"); + if ( num == EVENT_OBJECT_FACTORYis ) strcpy(text, "Zbuduj szperacz na nogach"); + if ( num == EVENT_OBJECT_FACTORYrt ) strcpy(text, "Zbuduj robota uderzacza"); + if ( num == EVENT_OBJECT_FACTORYrc ) strcpy(text, "Zbuduj dzia³o fazowe"); + if ( num == EVENT_OBJECT_FACTORYrr ) strcpy(text, "Zbuduj robota recyklera"); + if ( num == EVENT_OBJECT_FACTORYrs ) strcpy(text, "Zbuduj robota os³aniajacza"); + if ( num == EVENT_OBJECT_FACTORYsa ) strcpy(text, "Zbuduj robota nurka"); + if ( num == EVENT_OBJECT_RTANK ) strcpy(text, "Rozpocznij prace badawcze robotów na g¹sienicach"); + if ( num == EVENT_OBJECT_RFLY ) strcpy(text, "Rozpocznij prace badawcze robotów lataj¹cych"); + if ( num == EVENT_OBJECT_RTHUMP ) strcpy(text, "Rozpocznij prace badawcze robota uderzacza"); + if ( num == EVENT_OBJECT_RCANON ) strcpy(text, "Rozpocznij prace badawcze dzia³a"); + if ( num == EVENT_OBJECT_RTOWER ) strcpy(text, "Rozpocznij prace badawcze wie¿y obronnej"); + if ( num == EVENT_OBJECT_RPHAZER ) strcpy(text, "Rozpocznij prace badawcze dzia³a fazowego"); + if ( num == EVENT_OBJECT_RSHIELD ) strcpy(text, "Rozpocznij prace badawcze robota os³aniacza"); + if ( num == EVENT_OBJECT_RATOMIC ) strcpy(text, "Rozpocznij prace badawcze energii atomowej"); + if ( num == EVENT_OBJECT_RiPAW ) strcpy(text, "Rozpocznij prace badawcze robotów na nogach"); + if ( num == EVENT_OBJECT_RiGUN ) strcpy(text, "Rozpocznij prace badawcze dzia³a organicznego"); + if ( num == EVENT_OBJECT_RESET ) strcpy(text, "Powrót do pocz¹tku"); + if ( num == EVENT_OBJECT_SEARCH ) strcpy(text, "Szukaj (\\key action;)"); + if ( num == EVENT_OBJECT_TERRAFORM ) strcpy(text, "Uderz (\\key action;)"); + if ( num == EVENT_OBJECT_FIRE ) strcpy(text, "Strzelaj (\\key action;)"); + if ( num == EVENT_OBJECT_RECOVER ) strcpy(text, "Odzyskaj (\\key action;)"); + if ( num == EVENT_OBJECT_BEGSHIELD ) strcpy(text, "Rozszerz os³onê (\\key action;)"); + if ( num == EVENT_OBJECT_ENDSHIELD ) strcpy(text, "Wy³¹cz os³onê (\\key action;)"); + if ( num == EVENT_OBJECT_DIMSHIELD ) strcpy(text, "Zasiêg os³ony"); + if ( num == EVENT_OBJECT_PROGRUN ) strcpy(text, "Wykonaj zaznaczony program"); + if ( num == EVENT_OBJECT_PROGEDIT ) strcpy(text, "Edytuj zaznaczony program"); + if ( num == EVENT_OBJECT_INFOOK ) strcpy(text, "\\Prze³¹cz przekaŸnik SatCom w stan gotowoœci"); + if ( num == EVENT_OBJECT_DELETE ) strcpy(text, "Zniszcz budynek"); + if ( num == EVENT_OBJECT_GENERGY ) strcpy(text, "Poziom energii"); + if ( num == EVENT_OBJECT_GSHIELD ) strcpy(text, "Poziom os³ony"); + if ( num == EVENT_OBJECT_GRANGE ) strcpy(text, "Temperatura silnika"); + if ( num == EVENT_OBJECT_GPROGRESS ) strcpy(text, "Wci¹¿ pracuje..."); + if ( num == EVENT_OBJECT_GRADAR ) strcpy(text, "Liczba wykrytych insektów"); + if ( num == EVENT_OBJECT_GINFO ) strcpy(text, "Przes³ane informacje"); + if ( num == EVENT_OBJECT_COMPASS ) strcpy(text, "Kompas"); +//? if ( num == EVENT_OBJECT_MAP ) strcpy(text, "Mapka"); + if ( num == EVENT_OBJECT_MAPZOOM ) strcpy(text, "Powiêkszenie mapki"); + if ( num == EVENT_OBJECT_CAMERA ) strcpy(text, "Kamera (\\key camera;)"); + if ( num == EVENT_OBJECT_HELP ) strcpy(text, "Pomoc na temat zaznaczonego obiektu"); + if ( num == EVENT_OBJECT_SOLUCE ) strcpy(text, "Poka¿ rozwi¹zanie"); + if ( num == EVENT_OBJECT_SHORTCUT00) strcpy(text, "Prze³¹cz roboty <-> budynki"); + if ( num == EVENT_OBJECT_LIMIT ) strcpy(text, "Poka¿ zasiêg"); + if ( num == EVENT_DT_VISIT0 || + num == EVENT_DT_VISIT1 || + num == EVENT_DT_VISIT2 || + num == EVENT_DT_VISIT3 || + num == EVENT_DT_VISIT4 ) strcpy(text, "Poka¿ miejsce"); + if ( num == EVENT_DT_END ) strcpy(text, "Kontynuuj"); + if ( num == EVENT_CMD ) strcpy(text, "Linia polecenia"); + if ( num == EVENT_SPEED ) strcpy(text, "Prêdkoœæ gry"); + + if ( num == EVENT_HYPER_PREV ) strcpy(text, "Wstecz"); + if ( num == EVENT_HYPER_NEXT ) strcpy(text, "Naprzód"); + if ( num == EVENT_HYPER_HOME ) strcpy(text, "Home"); + if ( num == EVENT_HYPER_COPY ) strcpy(text, "Kopiuj"); + if ( num == EVENT_HYPER_SIZE1 ) strcpy(text, "Wielkoœæ 1"); + if ( num == EVENT_HYPER_SIZE2 ) strcpy(text, "Wielkoœæ 2"); + if ( num == EVENT_HYPER_SIZE3 ) strcpy(text, "Wielkoœæ 3"); + if ( num == EVENT_HYPER_SIZE4 ) strcpy(text, "Wielkoœæ 4"); + if ( num == EVENT_HYPER_SIZE5 ) strcpy(text, "Wielkoœæ 5"); + if ( num == EVENT_SATCOM_HUSTON ) strcpy(text, "Rozkazy z Houston"); + if ( num == EVENT_SATCOM_SAT ) strcpy(text, "Raport z satelity"); + if ( num == EVENT_SATCOM_LOADING ) strcpy(text, "Program dostarczony z Houston"); + if ( num == EVENT_SATCOM_OBJECT ) strcpy(text, "Lista obiektów"); + if ( num == EVENT_SATCOM_PROG ) strcpy(text, "Podrêcznik programowania"); + if ( num == EVENT_SATCOM_SOLUCE ) strcpy(text, "Rozwi¹zanie"); + + if ( num == EVENT_STUDIO_OK ) strcpy(text, "OK\\Zamyka edytor programu i powraca do gry"); + if ( num == EVENT_STUDIO_CANCEL ) strcpy(text, "Anuluj\\Pomija wszystkie zmiany"); + if ( num == EVENT_STUDIO_NEW ) strcpy(text, "Nowy"); + if ( num == EVENT_STUDIO_OPEN ) strcpy(text, "Otwórz (Ctrl+o)"); + if ( num == EVENT_STUDIO_SAVE ) strcpy(text, "Zapisz (Ctrl+s)"); + if ( num == EVENT_STUDIO_UNDO ) strcpy(text, "Cofnij (Ctrl+z)"); + if ( num == EVENT_STUDIO_CUT ) strcpy(text, "Wytnij (Ctrl+x)"); + if ( num == EVENT_STUDIO_COPY ) strcpy(text, "Kopiuj (Ctrl+c)"); + if ( num == EVENT_STUDIO_PASTE ) strcpy(text, "Wklej (Ctrl+v)"); + if ( num == EVENT_STUDIO_SIZE ) strcpy(text, "Wielkoœæ czcionki"); + if ( num == EVENT_STUDIO_TOOL ) strcpy(text, "Rozkazy (\\key help;)"); + if ( num == EVENT_STUDIO_HELP ) strcpy(text, "Podrêcznik programowania (\\key prog;)"); + if ( num == EVENT_STUDIO_COMPILE ) strcpy(text, "Kompiluj"); + if ( num == EVENT_STUDIO_RUN ) strcpy(text, "Wykonaj/zatrzymaj"); + if ( num == EVENT_STUDIO_REALTIME ) strcpy(text, "Pauza/kontynuuj"); + if ( num == EVENT_STUDIO_STEP ) strcpy(text, "Jeden krok"); + } + + if ( type == RES_OBJECT ) + { + if ( num == OBJECT_PORTICO ) strcpy(text, "¯uraw przesuwalny"); + if ( num == OBJECT_BASE ) strcpy(text, "Statek kosmiczny"); + if ( num == OBJECT_DERRICK ) strcpy(text, "Kopalnia"); + if ( num == OBJECT_FACTORY ) strcpy(text, "Fabryka robotów"); + if ( num == OBJECT_REPAIR ) strcpy(text, "Warsztat"); + if ( num == OBJECT_STATION ) strcpy(text, "Stacja energetyczna"); + if ( num == OBJECT_CONVERT ) strcpy(text, "Przetop rudê na tytan"); + if ( num == OBJECT_TOWER ) strcpy(text, "Wie¿a obronna"); + if ( num == OBJECT_NEST ) strcpy(text, "Gniazdo"); + if ( num == OBJECT_RESEARCH ) strcpy(text, "Centrum badawcze"); + if ( num == OBJECT_RADAR ) strcpy(text, "Stacja radarowa"); + if ( num == OBJECT_INFO ) strcpy(text, "Stacja przekaŸnikowa informacji"); + if ( num == OBJECT_ENERGY ) strcpy(text, "Fabryka ogniw elektrycznych"); + if ( num == OBJECT_LABO ) strcpy(text, "Laboratorium"); + if ( num == OBJECT_NUCLEAR ) strcpy(text, "Elektrownia atomowa"); + if ( num == OBJECT_PARA ) strcpy(text, "Odgromnik"); + if ( num == OBJECT_SAFE ) strcpy(text, "Skrytka"); + if ( num == OBJECT_HUSTON ) strcpy(text, "Centrum Kontroli Misji w Houston"); + if ( num == OBJECT_TARGET1 ) strcpy(text, "Cel"); + if ( num == OBJECT_TARGET2 ) strcpy(text, "Cel"); + if ( num == OBJECT_START ) strcpy(text, "Pocz¹tek"); + if ( num == OBJECT_END ) strcpy(text, "Koniec"); + if ( num == OBJECT_STONE ) strcpy(text, "Ruda tytanu"); + if ( num == OBJECT_URANIUM ) strcpy(text, "Ruda uranu"); + if ( num == OBJECT_BULLET ) strcpy(text, "Materia organiczna"); + if ( num == OBJECT_METAL ) strcpy(text, "Tytan"); + if ( num == OBJECT_POWER ) strcpy(text, "Ogniwo elektryczne"); + if ( num == OBJECT_ATOMIC ) strcpy(text, "Atomowe ogniwa elektryczne"); + if ( num == OBJECT_BBOX ) strcpy(text, "Czarna skrzynka"); + if ( num == OBJECT_KEYa ) strcpy(text, "Klucz A"); + if ( num == OBJECT_KEYb ) strcpy(text, "Klucz B"); + if ( num == OBJECT_KEYc ) strcpy(text, "Klucz C"); + if ( num == OBJECT_KEYd ) strcpy(text, "Klucz D"); + if ( num == OBJECT_TNT ) strcpy(text, "Materia³y wybuchowe"); + if ( num == OBJECT_BOMB ) strcpy(text, "Mina"); + if ( num == OBJECT_BAG ) strcpy(text, "Zestaw przetrwania"); + if ( num == OBJECT_WAYPOINT ) strcpy(text, "Punkt kontrolny"); + if ( num == OBJECT_FLAGb ) strcpy(text, "Niebieska flaga"); + if ( num == OBJECT_FLAGr ) strcpy(text, "Czerwona flaga"); + if ( num == OBJECT_FLAGg ) strcpy(text, "Zielona flaga"); + if ( num == OBJECT_FLAGy ) strcpy(text, "¯ó³ta flaga"); + if ( num == OBJECT_FLAGv ) strcpy(text, "Fioletowa flaga"); + if ( num == OBJECT_MARKPOWER ) strcpy(text, "ród³o energii (miejsce na elektrowniê)"); + if ( num == OBJECT_MARKURANIUM ) strcpy(text, "Z³o¿e uranu (miejsce na kopalniê)"); + if ( num == OBJECT_MARKKEYa ) strcpy(text, "Znaleziono klucz A (miejsce na kopalniê)"); + if ( num == OBJECT_MARKKEYb ) strcpy(text, "Znaleziono klucz B (miejsce na kopalniê)"); + if ( num == OBJECT_MARKKEYc ) strcpy(text, "Znaleziono klucz C (miejsce na kopalniê)"); + if ( num == OBJECT_MARKKEYd ) strcpy(text, "Znaleziono klucz D (miejsce na kopalniê)"); + if ( num == OBJECT_MARKSTONE ) strcpy(text, "Z³o¿e tytanu (miejsce na kopalniê)"); + if ( num == OBJECT_MOBILEft ) strcpy(text, "Robot treningowy"); + if ( num == OBJECT_MOBILEtt ) strcpy(text, "Robot treningowy"); + if ( num == OBJECT_MOBILEwt ) strcpy(text, "Robot treningowy"); + if ( num == OBJECT_MOBILEit ) strcpy(text, "Robot treningowy"); + if ( num == OBJECT_MOBILEfa ) strcpy(text, "Transporter lataj¹cy"); + if ( num == OBJECT_MOBILEta ) strcpy(text, "Transporter na g¹sienicach"); + if ( num == OBJECT_MOBILEwa ) strcpy(text, "Transporter na ko³ach"); + if ( num == OBJECT_MOBILEia ) strcpy(text, "Transporter na nogach"); + if ( num == OBJECT_MOBILEfc ) strcpy(text, "Dzia³o lataj¹ce"); + if ( num == OBJECT_MOBILEtc ) strcpy(text, "Dzia³o na g¹sienicach"); + if ( num == OBJECT_MOBILEwc ) strcpy(text, "Dzia³o na ko³ach"); + if ( num == OBJECT_MOBILEic ) strcpy(text, "Dzia³o na nogach"); + if ( num == OBJECT_MOBILEfi ) strcpy(text, "Lataj¹ce dzia³o organiczne"); + if ( num == OBJECT_MOBILEti ) strcpy(text, "Dzia³o organiczne na g¹sienicach"); + if ( num == OBJECT_MOBILEwi ) strcpy(text, "Dzia³o organiczne na ko³ach"); + if ( num == OBJECT_MOBILEii ) strcpy(text, "Dzia³o organiczne na nogach"); + if ( num == OBJECT_MOBILEfs ) strcpy(text, "Szperacz lataj¹cy"); + if ( num == OBJECT_MOBILEts ) strcpy(text, "Szperacz na g¹sienicach"); + if ( num == OBJECT_MOBILEws ) strcpy(text, "Szperacz na ko³ach"); + if ( num == OBJECT_MOBILEis ) strcpy(text, "Szperacz na nogach"); + if ( num == OBJECT_MOBILErt ) strcpy(text, "Uderzacz"); + if ( num == OBJECT_MOBILErc ) strcpy(text, "Dzia³o fazowe"); + if ( num == OBJECT_MOBILErr ) strcpy(text, "Recykler"); + if ( num == OBJECT_MOBILErs ) strcpy(text, "Os³aniacz"); + if ( num == OBJECT_MOBILEsa ) strcpy(text, "Robot nurek"); + if ( num == OBJECT_MOBILEtg ) strcpy(text, "Robot cel"); + if ( num == OBJECT_HUMAN ) strcpy(text, g_gamerName); + if ( num == OBJECT_TECH ) strcpy(text, "In¿ynier"); + if ( num == OBJECT_TOTO ) strcpy(text, "Robbie"); + if ( num == OBJECT_MOTHER ) strcpy(text, "Królowa Obcych"); + if ( num == OBJECT_ANT ) strcpy(text, "Mrówka"); + if ( num == OBJECT_SPIDER ) strcpy(text, "Paj¹k"); + if ( num == OBJECT_BEE ) strcpy(text, "Osa"); + if ( num == OBJECT_WORM ) strcpy(text, "Robal"); + if ( num == OBJECT_EGG ) strcpy(text, "Jajo"); + if ( num == OBJECT_RUINmobilew1 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINmobilew2 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINmobilet1 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINmobilet2 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINmobiler1 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINmobiler2 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINfactory ) strcpy(text, "Ruiny"); + if ( num == OBJECT_RUINdoor ) strcpy(text, "Ruiny"); + if ( num == OBJECT_RUINsupport ) strcpy(text, "Odpady"); + if ( num == OBJECT_RUINradar ) strcpy(text, "Ruiny"); + if ( num == OBJECT_RUINconvert ) strcpy(text, "Ruiny"); + if ( num == OBJECT_RUINbase ) strcpy(text, "Ruiny statku kosmicznego"); + if ( num == OBJECT_RUINhead ) strcpy(text, "Ruiny statku kosmicznego"); + if ( num == OBJECT_APOLLO1 || + num == OBJECT_APOLLO3 || + num == OBJECT_APOLLO4 || + num == OBJECT_APOLLO5 ) strcpy(text, "Pozosta³oœci z misji Apollo"); + if ( num == OBJECT_APOLLO2 ) strcpy(text, "Pojazd Ksiê¿ycowy"); + } + + if ( type == RES_ERR ) + { + strcpy(text, "B³¹d"); + if ( num == ERR_CMD ) strcpy(text, "Nieznane polecenie"); + if ( num == ERR_INSTALL ) strcpy(text, "Gra COLOBOT nie jest zainstalowana."); + if ( num == ERR_NOCD ) strcpy(text, "W³ó¿ dysk CD z gr¹ COLOBOT\ni uruchom grê jeszcze raz."); + if ( num == ERR_MANIP_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_MANIP_FLY ) strcpy(text, "Niemo¿liwe podczas lotu"); + if ( num == ERR_MANIP_BUSY ) strcpy(text, "Nie mo¿na nieœæ wiêcej przedmiotów"); + if ( num == ERR_MANIP_NIL ) strcpy(text, "Nie ma nic do podniesienia"); + if ( num == ERR_MANIP_MOTOR ) strcpy(text, "Niemo¿liwe podczas ruchu"); + if ( num == ERR_MANIP_OCC ) strcpy(text, "Miejsce zajête"); + if ( num == ERR_MANIP_FRIEND ) strcpy(text, "Brak innego robota"); + if ( num == ERR_MANIP_RADIO ) strcpy(text, "Nie mo¿esz przenosiæ przedmiotów radioaktywnych"); + if ( num == ERR_MANIP_WATER ) strcpy(text, "Nie mo¿esz przenosiæ przedmiotów pod wod¹"); + if ( num == ERR_MANIP_EMPTY ) strcpy(text, "Nie ma nic do upuszczenia"); + if ( num == ERR_BUILD_FLY ) strcpy(text, "Niemo¿liwe podczas lotu"); + if ( num == ERR_BUILD_WATER ) strcpy(text, "Niemo¿liwe pod wod¹"); + if ( num == ERR_BUILD_ENERGY ) strcpy(text, "Za ma³o energii"); + if ( num == ERR_BUILD_METALAWAY ) strcpy(text, "Tytan za daleko"); + if ( num == ERR_BUILD_METALNEAR ) strcpy(text, "Tytan za blisko"); + if ( num == ERR_BUILD_METALINEX ) strcpy(text, "Brak tytanu w pobli¿u"); + if ( num == ERR_BUILD_FLAT ) strcpy(text, "Powierzchnia nie jest wystarczaj¹co p³aska"); + if ( num == ERR_BUILD_FLATLIT ) strcpy(text, "Za ma³o p³askiego terenu"); + if ( num == ERR_BUILD_BUSY ) strcpy(text, "Miejsce zajête"); + if ( num == ERR_BUILD_BASE ) strcpy(text, "Za blisko statku kosmicznego"); + if ( num == ERR_BUILD_NARROW ) strcpy(text, "Za blisko budynku"); + if ( num == ERR_BUILD_MOTOR ) strcpy(text, "Niemo¿liwe podczas ruchu"); + if ( num == ERR_SEARCH_FLY ) strcpy(text, "Niemo¿liwe podczas lotu"); + if ( num == ERR_SEARCH_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_SEARCH_MOTOR ) strcpy(text, "Niemo¿liwe podczas ruchu"); + if ( num == ERR_TERRA_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_TERRA_ENERGY ) strcpy(text, "Za ma³o energii"); + if ( num == ERR_TERRA_FLOOR ) strcpy(text, "Nieodpowiedni teren"); + if ( num == ERR_TERRA_BUILDING ) strcpy(text, "Budynek za blisko"); + if ( num == ERR_TERRA_OBJECT ) strcpy(text, "Obiekt za blisko"); + if ( num == ERR_RECOVER_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_RECOVER_ENERGY ) strcpy(text, "Za ma³o energii"); + if ( num == ERR_RECOVER_NULL ) strcpy(text, "Nie ma niczego do odzysku"); + if ( num == ERR_SHIELD_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_SHIELD_ENERGY ) strcpy(text, "Nie ma wiêcej energii"); + if ( num == ERR_MOVE_IMPOSSIBLE ) strcpy(text, "B³¹d w poleceniu ruchu"); + if ( num == ERR_GOTO_IMPOSSIBLE ) strcpy(text, "Goto: miejsce docelowe niedostêpne"); + if ( num == ERR_GOTO_ITER ) strcpy(text, "Goto: miejsce docelowe niedostêpne"); + if ( num == ERR_GOTO_BUSY ) strcpy(text, "Goto: miejsce docelowe zajête"); + if ( num == ERR_FIRE_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_FIRE_ENERGY ) strcpy(text, "Za ma³o energii"); + if ( num == ERR_FIRE_FLY ) strcpy(text, "Niemo¿liwe podczas lotu"); + if ( num == ERR_CONVERT_EMPTY ) strcpy(text, "Brak rudy tytanu do przetopienia"); + if ( num == ERR_DERRICK_NULL ) strcpy(text, "W ziemi nie ma ¿adnej rudy"); + if ( num == ERR_STATION_NULL ) strcpy(text, "Brak energii w ziemi"); + if ( num == ERR_TOWER_POWER ) strcpy(text, "Brak ogniwa elektrycznego"); + if ( num == ERR_TOWER_ENERGY ) strcpy(text, "Nie ma wiêcej energii"); + if ( num == ERR_RESEARCH_POWER ) strcpy(text, "Brak ogniwa elektrycznego"); + if ( num == ERR_RESEARCH_ENERGY ) strcpy(text, "Za ma³o energii"); + if ( num == ERR_RESEARCH_TYPE ) strcpy(text, "Nieodpowiedni rodzaj ogniw"); + if ( num == ERR_RESEARCH_ALREADY) strcpy(text, "Program badawczy zosta³ ju¿ wykonany"); + if ( num == ERR_ENERGY_NULL ) strcpy(text, "Brak energii w ziemi"); + if ( num == ERR_ENERGY_LOW ) strcpy(text, "Wci¹¿ za ma³o energii"); + if ( num == ERR_ENERGY_EMPTY ) strcpy(text, "Brak tytanu do przetworzenia"); + if ( num == ERR_ENERGY_BAD ) strcpy(text, "Przetwarza jedynie tytan"); + if ( num == ERR_BASE_DLOCK ) strcpy(text, "Drzwi zablokowane przez robota lub inny obiekt "); + if ( num == ERR_BASE_DHUMAN ) strcpy(text, "Musisz byæ na statku kosmicznym aby nim odlecieæ"); + if ( num == ERR_LABO_NULL ) strcpy(text, "Nie ma niczego do zanalizowania"); + if ( num == ERR_LABO_BAD ) strcpy(text, "Analizuje jedynie materiê organiczn¹"); + if ( num == ERR_LABO_ALREADY ) strcpy(text, "Analiza zosta³a ju¿ wykonana"); + if ( num == ERR_NUCLEAR_NULL ) strcpy(text, "Brak energii w ziemi"); + if ( num == ERR_NUCLEAR_LOW ) strcpy(text, "Wci¹¿ za ma³o energii"); + if ( num == ERR_NUCLEAR_EMPTY ) strcpy(text, "Brak uranu do przetworzenia"); + if ( num == ERR_NUCLEAR_BAD ) strcpy(text, "Przetwarza jedynie uran"); + if ( num == ERR_FACTORY_NULL ) strcpy(text, "Brak tytanu"); + if ( num == ERR_FACTORY_NEAR ) strcpy(text, "Obiekt za blisko"); + if ( num == ERR_RESET_NEAR ) strcpy(text, "Miejsce zajête"); + if ( num == ERR_INFO_NULL ) strcpy(text, "Nie ma ¿adnej stacji przekaŸnikowej w zasiêgu"); + if ( num == ERR_VEH_VIRUS ) strcpy(text, "Program zawirusowany"); + if ( num == ERR_BAT_VIRUS ) strcpy(text, "Zainfekowane wirusem, chwilowo niesprawne"); + if ( num == ERR_VEH_POWER ) strcpy(text, "Brak ogniwa elektrycznego"); + if ( num == ERR_VEH_ENERGY ) strcpy(text, "Nie ma wiêcej energii"); + if ( num == ERR_FLAG_FLY ) strcpy(text, "Niemo¿liwe podczas lotu"); + if ( num == ERR_FLAG_WATER ) strcpy(text, "Niemo¿liwe podczas p³ywania"); + if ( num == ERR_FLAG_MOTOR ) strcpy(text, "Niemo¿liwe podczas ruchu"); + if ( num == ERR_FLAG_BUSY ) strcpy(text, "Niemo¿liwe podczas przenoszenia przedmiotu"); + if ( num == ERR_FLAG_CREATE ) strcpy(text, "Za du¿o flag w tym kolorze (maksymalnie 5)"); + if ( num == ERR_FLAG_PROXY ) strcpy(text, "Za blisko istniej¹cej flagi"); + if ( num == ERR_FLAG_DELETE ) strcpy(text, "Nie ma flagi w pobli¿u"); + if ( num == ERR_MISSION_NOTERM ) strcpy(text, "Misja nie jest wype³niona (naciœnij \\key help; aby uzyskaæ szczegó³y)"); + if ( num == ERR_DELETEMOBILE ) strcpy(text, "Robot zniszczony"); + if ( num == ERR_DELETEBUILDING ) strcpy(text, "Budynek zniszczony"); + if ( num == ERR_TOOMANY ) strcpy(text, "Nie mo¿na tego utworzyæ, za du¿o obiektów"); + + if ( num == INFO_BUILD ) strcpy(text, "Budowa zakoñczona"); + if ( num == INFO_CONVERT ) strcpy(text, "Tytan dostêpny"); + if ( num == INFO_RESEARCH ) strcpy(text, "Program badawczy zakoñczony"); + if ( num == INFO_RESEARCHTANK ) strcpy(text, "Dostêpne plany robotów na g¹sienicach"); + if ( num == INFO_RESEARCHFLY ) strcpy(text, "Mo¿esz lataæ u¿ywaj¹c klawiszy (\\key gup;) oraz (\\key gdown;)"); + if ( num == INFO_RESEARCHTHUMP ) strcpy(text, "Dostêpne plany robota uderzacza"); + if ( num == INFO_RESEARCHCANON ) strcpy(text, "Dostêpne plany dzia³a"); + if ( num == INFO_RESEARCHTOWER ) strcpy(text, "Dostêpne plany wie¿y obronnej"); + if ( num == INFO_RESEARCHPHAZER ) strcpy(text, "Dostêpne plany dzia³a fazowego"); + if ( num == INFO_RESEARCHSHIELD ) strcpy(text, "Dostêpne plany robota os³aniacza"); + if ( num == INFO_RESEARCHATOMIC ) strcpy(text, "Dostêpne plany elektrowni atomowej"); + if ( num == INFO_FACTORY ) strcpy(text, "Dostêpny nowy robot"); + if ( num == INFO_LABO ) strcpy(text, "Analiza wykonana"); + if ( num == INFO_ENERGY ) strcpy(text, "Wytworzono ogniwo elektryczne"); + if ( num == INFO_NUCLEAR ) strcpy(text, "Wytworzono atomowe ogniwo elektryczne"); + if ( num == INFO_FINDING ) strcpy(text, "Znaleziono u¿yteczny przedmiot"); + if ( num == INFO_MARKPOWER ) strcpy(text, "Znaleziono miejsce na elektrowniê"); + if ( num == INFO_MARKURANIUM ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_MARKSTONE ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_MARKKEYa ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_MARKKEYb ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_MARKKEYc ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_MARKKEYd ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_WIN ) strcpy(text, "<<< Dobra robota, misja wype³niona >>>"); + if ( num == INFO_LOST ) strcpy(text, "<<< Niestety, misja nie powiod³a siê >>>"); + if ( num == INFO_LOSTq ) strcpy(text, "<<< Niestety, misja nie powiod³a siê >>>"); + if ( num == INFO_WRITEOK ) strcpy(text, "Bie¿¹ca misja zapisana"); + if ( num == INFO_DELETEPATH ) strcpy(text, "Przekroczono punkt kontrolny"); + if ( num == INFO_DELETEMOTHER ) strcpy(text, "Królowa Obcych zosta³a zabita"); + if ( num == INFO_DELETEANT ) strcpy(text, "Mrówka œmiertelnie ranna"); + if ( num == INFO_DELETEBEE ) strcpy(text, "Osa œmiertelnie ranna"); + if ( num == INFO_DELETEWORM ) strcpy(text, "Robal œmiertelnie ranny"); + if ( num == INFO_DELETESPIDER ) strcpy(text, "Paj¹k œmiertelnie ranny"); + if ( num == INFO_BEGINSATCOM ) strcpy(text, "Naciœnij klawisz \\key help; aby wyœwietliæ rozkazy na przekaŸniku SatCom"); + } + + if ( type == RES_CBOT ) + { + strcpy(text, "B³¹d"); + if ( num == TX_OPENPAR ) strcpy(text, "Brak nawiasu otwieraj¹cego"); + if ( num == TX_CLOSEPAR ) strcpy(text, "Brak nawiasu zamykaj¹cego"); + if ( num == TX_NOTBOOL ) strcpy(text, "Wyra¿enie musi zwróciæ wartoœæ logiczn¹"); + if ( num == TX_UNDEFVAR ) strcpy(text, "Zmienna nie zosta³a zadeklarowana"); + if ( num == TX_BADLEFT ) strcpy(text, "Przypisanie niemo¿liwe"); + if ( num == TX_ENDOF ) strcpy(text, "Brak œrednika na koñcu wiersza"); + if ( num == TX_OUTCASE ) strcpy(text, "Polecenie ""case"" outside a block ""switch"""); + if ( num == TX_NOTERM ) strcpy(text, "Polecenie po koñcowej klamrze zamykaj¹cej"); + if ( num == TX_CLOSEBLK ) strcpy(text, "Brak koñca bloku"); + if ( num == TX_ELSEWITHOUTIF ) strcpy(text, "Polecenie ""else"" without corresponding ""if"" "); + if ( num == TX_OPENBLK ) strcpy(text, "Brak klamry otwieraj¹cej");//début d'un bloc attendu? + if ( num == TX_BADTYPE ) strcpy(text, "Z³y typ dla przypisania"); + if ( num == TX_REDEFVAR ) strcpy(text, "Zmienna nie mo¿e byæ zadeklarowana dwukrotnie"); + if ( num == TX_BAD2TYPE ) strcpy(text, "Niezgodne typy operatorów"); + if ( num == TX_UNDEFCALL ) strcpy(text, "Funkcja nieznana"); + if ( num == TX_MISDOTS ) strcpy(text, "Znak "" : "" missing"); + if ( num == TX_WHILE ) strcpy(text, "Skrót klawiszowy ""while"" missing"); + if ( num == TX_BREAK ) strcpy(text, "Polecenie ""break"" outside a loop"); + if ( num == TX_LABEL ) strcpy(text, "Po etykiecie musi wyst¹piæ ""for"", ""while"", ""do"" or ""switch"""); + if ( num == TX_NOLABEL ) strcpy(text, "Taka etykieta nie istnieje");// Cette étiquette n'existe pas + if ( num == TX_NOCASE ) strcpy(text, "Polecenie ""case"" missing"); + if ( num == TX_BADNUM ) strcpy(text, "Brak liczby"); + if ( num == TX_VOID ) strcpy(text, "Pusty parametr"); + if ( num == TX_NOTYP ) strcpy(text, "Brak deklaracji typu"); + if ( num == TX_NOVAR ) strcpy(text, "Brak nazwy zmiennej"); + if ( num == TX_NOFONC ) strcpy(text, "Brakuj¹ca nazwa funkcji"); + if ( num == TX_OVERPARAM ) strcpy(text, "Za du¿o parametrów"); + if ( num == TX_REDEF ) strcpy(text, "Funkcja ju¿ istnieje"); + if ( num == TX_LOWPARAM ) strcpy(text, "Brak wymaganego parametru"); + if ( num == TX_BADPARAM ) strcpy(text, "Funkcja o tej nazwie nie akceptuje parametrów tego typu"); + if ( num == TX_NUMPARAM ) strcpy(text, "Funkcja o tej nazwie nie akceptuje takiej liczby parametrów"); + if ( num == TX_NOITEM ) strcpy(text, "To nie jest obiekt tej klasy"); + if ( num == TX_DOT ) strcpy(text, "Ten obiekt nie jest cz³onkiem klasy"); + if ( num == TX_NOCONST ) strcpy(text, "Brak odpowiedniego konstruktora"); + if ( num == TX_REDEFCLASS ) strcpy(text, "Taka klasa ju¿ istnieje"); + if ( num == TX_CLBRK ) strcpy(text, """ ] "" missing"); + if ( num == TX_RESERVED ) strcpy(text, "S³owo zarezerwowane jêzyka CBOT"); + if ( num == TX_BADNEW ) strcpy(text, "Z³y argument dla funkcji ""new"""); + if ( num == TX_OPBRK ) strcpy(text, """ [ "" expected"); + if ( num == TX_BADSTRING ) strcpy(text, "Brak ³añcucha"); + if ( num == TX_BADINDEX ) strcpy(text, "Nieprawid³owy typ indeksu"); + if ( num == TX_PRIVATE ) strcpy(text, "Element prywatny"); + if ( num == TX_NOPUBLIC ) strcpy(text, "Wymagany publiczny"); + if ( num == TX_DIVZERO ) strcpy(text, "Dzielenie przez zero"); + if ( num == TX_NOTINIT ) strcpy(text, "Zmienna nie zosta³a zainicjalizowana"); + if ( num == TX_BADTHROW ) strcpy(text, "Wartoœæ ujemna odrzucona przez ""throw""");//C'est quoi, ça? + if ( num == TX_NORETVAL ) strcpy(text, "Funkcja nie zwróci³a ¿adnej wartoœci "); + if ( num == TX_NORUN ) strcpy(text, "¯adna funkcja nie dzia³a"); + if ( num == TX_NOCALL ) strcpy(text, "Odwo³anie do nieznanej funkcji"); + if ( num == TX_NOCLASS ) strcpy(text, "Taka klasa nie istnieje"); + if ( num == TX_NULLPT ) strcpy(text, "Obiekt nieznany"); + if ( num == TX_OPNAN ) strcpy(text, "Dzia³anie niemo¿liwe z wartoœci¹ ""nan"""); + if ( num == TX_OUTARRAY ) strcpy(text, "Dostêp poza tablicê"); + if ( num == TX_STACKOVER ) strcpy(text, "Przepe³nienie stosu"); + if ( num == TX_DELETEDPT ) strcpy(text, "Nieprawid³owy obiekt"); + if ( num == TX_FILEOPEN ) strcpy(text, "Nie mo¿na otworzyæ pliku"); + if ( num == TX_NOTOPEN ) strcpy(text, "Plik nie jest otwarty"); + if ( num == TX_ERRREAD ) strcpy(text, "B³¹d odczytu"); + if ( num == TX_ERRWRITE ) strcpy(text, "B³¹d zapisu"); + } + + if ( type == RES_KEY ) + { + if ( num == 0 ) strcpy(text, "< brak >"); + if ( num == VK_LEFT ) strcpy(text, "Strza³ka w lewo"); + if ( num == VK_RIGHT ) strcpy(text, "Strza³ka w prawo"); + if ( num == VK_UP ) strcpy(text, "Strza³ka w górê"); + if ( num == VK_DOWN ) strcpy(text, "Strza³ka w dó³"); + if ( num == VK_CANCEL ) strcpy(text, "Ctrl-break"); + if ( num == VK_BACK ) strcpy(text, "<--"); + if ( num == VK_TAB ) strcpy(text, "Tab"); + if ( num == VK_CLEAR ) strcpy(text, "Wyczyœæ"); + if ( num == VK_RETURN ) strcpy(text, "Enter"); + if ( num == VK_SHIFT ) strcpy(text, "Shift"); + if ( num == VK_CONTROL ) strcpy(text, "Ctrl"); + if ( num == VK_MENU ) strcpy(text, "Alt"); + if ( num == VK_PAUSE ) strcpy(text, "Pause"); + if ( num == VK_CAPITAL ) strcpy(text, "Caps Lock"); + if ( num == VK_ESCAPE ) strcpy(text, "Esc"); + if ( num == VK_SPACE ) strcpy(text, "Spacja"); + if ( num == VK_PRIOR ) strcpy(text, "Page Up"); + if ( num == VK_NEXT ) strcpy(text, "Page Down"); + if ( num == VK_END ) strcpy(text, "End"); + if ( num == VK_HOME ) strcpy(text, "Home"); + if ( num == VK_SELECT ) strcpy(text, "Zaznacz"); + if ( num == VK_EXECUTE ) strcpy(text, "Wykonaj"); + if ( num == VK_SNAPSHOT ) strcpy(text, "Print Scrn"); + if ( num == VK_INSERT ) strcpy(text, "Insert"); + if ( num == VK_DELETE ) strcpy(text, "Delete"); + if ( num == VK_HELP ) strcpy(text, "Pomoc"); + if ( num == VK_LWIN ) strcpy(text, "Lewy klawisz Windows"); + if ( num == VK_RWIN ) strcpy(text, "Prawy klawisz Windows"); + if ( num == VK_APPS ) strcpy(text, "Klawisz menu kontekstowego"); + if ( num == VK_NUMPAD0 ) strcpy(text, "Szary 0"); + if ( num == VK_NUMPAD1 ) strcpy(text, "Szary 1"); + if ( num == VK_NUMPAD2 ) strcpy(text, "Szary 2"); + if ( num == VK_NUMPAD3 ) strcpy(text, "Szary 3"); + if ( num == VK_NUMPAD4 ) strcpy(text, "Szary 4"); + if ( num == VK_NUMPAD5 ) strcpy(text, "Szary 5"); + if ( num == VK_NUMPAD6 ) strcpy(text, "Szary 6"); + if ( num == VK_NUMPAD7 ) strcpy(text, "Szary 7"); + if ( num == VK_NUMPAD8 ) strcpy(text, "Szary 8"); + if ( num == VK_NUMPAD9 ) strcpy(text, "Szary 9"); + if ( num == VK_MULTIPLY ) strcpy(text, "Szary *"); + if ( num == VK_ADD ) strcpy(text, "Szary +"); + if ( num == VK_SEPARATOR ) strcpy(text, "Szary separator"); + if ( num == VK_SUBTRACT ) strcpy(text, "Szary -"); + if ( num == VK_DECIMAL ) strcpy(text, "Szary ."); + if ( num == VK_DIVIDE ) strcpy(text, "Szary /"); + if ( num == VK_F1 ) strcpy(text, "F1"); + if ( num == VK_F2 ) strcpy(text, "F2"); + if ( num == VK_F3 ) strcpy(text, "F3"); + if ( num == VK_F4 ) strcpy(text, "F4"); + if ( num == VK_F5 ) strcpy(text, "F5"); + if ( num == VK_F6 ) strcpy(text, "F6"); + if ( num == VK_F7 ) strcpy(text, "F7"); + if ( num == VK_F8 ) strcpy(text, "F8"); + if ( num == VK_F9 ) strcpy(text, "F9"); + if ( num == VK_F10 ) strcpy(text, "F10"); + if ( num == VK_F11 ) strcpy(text, "F11"); + if ( num == VK_F12 ) strcpy(text, "F12"); + if ( num == VK_F13 ) strcpy(text, "F13"); + if ( num == VK_F14 ) strcpy(text, "F14"); + if ( num == VK_F15 ) strcpy(text, "F15"); + if ( num == VK_F16 ) strcpy(text, "F16"); + if ( num == VK_F17 ) strcpy(text, "F17"); + if ( num == VK_F18 ) strcpy(text, "F18"); + if ( num == VK_F19 ) strcpy(text, "F19"); + if ( num == VK_F20 ) strcpy(text, "F20"); + if ( num == VK_NUMLOCK ) strcpy(text, "Num Lock"); + if ( num == VK_SCROLL ) strcpy(text, "Scroll Lock"); + if ( num == VK_ATTN ) strcpy(text, "Attn"); + if ( num == VK_CRSEL ) strcpy(text, "CrSel"); + if ( num == VK_EXSEL ) strcpy(text, "ExSel"); + if ( num == VK_EREOF ) strcpy(text, "Erase EOF"); + if ( num == VK_PLAY ) strcpy(text, "Graj"); + if ( num == VK_ZOOM ) strcpy(text, "Powiêkszenie"); + if ( num == VK_PA1 ) strcpy(text, "PA1"); + if ( num == VK_OEM_CLEAR ) strcpy(text, "Wyczyœæ"); + if ( num == VK_BUTTON1 ) strcpy(text, "Przycisk 1"); + if ( num == VK_BUTTON2 ) strcpy(text, "Przycisk 2"); + if ( num == VK_BUTTON3 ) strcpy(text, "Przycisk 3"); + if ( num == VK_BUTTON4 ) strcpy(text, "Przycisk 4"); + if ( num == VK_BUTTON5 ) strcpy(text, "Przycisk 5"); + if ( num == VK_BUTTON6 ) strcpy(text, "Przycisk 6"); + if ( num == VK_BUTTON7 ) strcpy(text, "Przycisk 7"); + if ( num == VK_BUTTON8 ) strcpy(text, "Przycisk 8"); + if ( num == VK_BUTTON9 ) strcpy(text, "Przycisk 9"); + if ( num == VK_BUTTON10 ) strcpy(text, "Przycisk 10"); + if ( num == VK_BUTTON11 ) strcpy(text, "Przycisk 11"); + if ( num == VK_BUTTON12 ) strcpy(text, "Przycisk 12"); + if ( num == VK_BUTTON13 ) strcpy(text, "Przycisk 13"); + if ( num == VK_BUTTON14 ) strcpy(text, "Przycisk 14"); + if ( num == VK_BUTTON15 ) strcpy(text, "Przycisk 15"); + if ( num == VK_BUTTON16 ) strcpy(text, "Przycisk 16"); + if ( num == VK_BUTTON17 ) strcpy(text, "Przycisk 17"); + if ( num == VK_BUTTON18 ) strcpy(text, "Przycisk 18"); + if ( num == VK_BUTTON19 ) strcpy(text, "Przycisk 19"); + if ( num == VK_BUTTON20 ) strcpy(text, "Przycisk 20"); + if ( num == VK_BUTTON21 ) strcpy(text, "Przycisk 21"); + if ( num == VK_BUTTON22 ) strcpy(text, "Przycisk 22"); + if ( num == VK_BUTTON23 ) strcpy(text, "Przycisk 23"); + if ( num == VK_BUTTON24 ) strcpy(text, "Przycisk 24"); + if ( num == VK_BUTTON25 ) strcpy(text, "Przycisk 25"); + if ( num == VK_BUTTON26 ) strcpy(text, "Przycisk 26"); + if ( num == VK_BUTTON27 ) strcpy(text, "Przycisk 27"); + if ( num == VK_BUTTON28 ) strcpy(text, "Przycisk 28"); + if ( num == VK_BUTTON29 ) strcpy(text, "Przycisk 29"); + if ( num == VK_BUTTON30 ) strcpy(text, "Przycisk 30"); + if ( num == VK_BUTTON31 ) strcpy(text, "Przycisk 31"); + if ( num == VK_BUTTON32 ) strcpy(text, "Przycisk 32"); + if ( num == VK_WHEELUP ) strcpy(text, "Kó³ko w górê"); + if ( num == VK_WHEELDOWN ) strcpy(text, "Kó³ko w dó³"); + } +#endif + + return ( text[0] != 0 ); +} + + diff --git a/src/restext.cpp b/src/restext.cpp new file mode 100644 index 00000000..8c25f011 --- /dev/null +++ b/src/restext.cpp @@ -0,0 +1,3649 @@ +// restext.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include "struct.h" +#include "D3DEngine.h" +#include "language.h" +#include "misc.h" +#include "event.h" +#include "object.h" +#include "cbot\resource.h" +#include "restext.h" + + + +//** -> texte à traduire !!! + + + +// Donne le pointeur au moteur. + +void SetEngine(CD3DEngine *engine) +{ + g_engine = engine; +} + +// Donne le nom du joueur. + +void SetGlobalGamerName(char *name) +{ + strcpy(g_gamerName, name); +} + + + +typedef struct +{ + KeyRank key; + char name[20]; +} +KeyDesc; + +static KeyDesc keyTable[22] = +{ + { KEYRANK_LEFT, "left;" }, + { KEYRANK_RIGHT, "right;" }, + { KEYRANK_UP, "up;" }, + { KEYRANK_DOWN, "down;" }, + { KEYRANK_GUP, "gup;" }, + { KEYRANK_GDOWN, "gdown;" }, + { KEYRANK_CAMERA, "camera;" }, + { KEYRANK_DESEL, "desel;" }, + { KEYRANK_ACTION, "action;" }, + { KEYRANK_NEAR, "near;" }, + { KEYRANK_AWAY, "away;" }, + { KEYRANK_NEXT, "next;" }, + { KEYRANK_HUMAN, "human;" }, + { KEYRANK_QUIT, "quit;" }, + { KEYRANK_HELP, "help;" }, + { KEYRANK_PROG, "prog;" }, + { KEYRANK_CBOT, "cbot;" }, + { KEYRANK_VISIT, "visit;" }, + { KEYRANK_SPEED10, "speed10;" }, + { KEYRANK_SPEED15, "speed15;" }, + { KEYRANK_SPEED20, "speed20;" }, + { KEYRANK_SPEED30, "speed30;" }, +}; + +// Cherche une touche. + +BOOL SearchKey(char *cmd, KeyRank &key) +{ + int i; + + for ( i=0 ; i<22 ; i++ ) + { + if ( strstr(cmd, keyTable[i].name) == cmd ) + { + key = keyTable[i].key; + return TRUE; + } + } + return FALSE; +} + +// Remplace les commandes "\key name;" dans un texte. + +void PutKeyName(char* dst, char* src) +{ + KeyRank key; + char name[50]; + int s, d, n, res; + + s = d = 0; + while ( src[s] != 0 ) + { + if ( src[s+0] == '\\' && + src[s+1] == 'k' && + src[s+2] == 'e' && + src[s+3] == 'y' && + src[s+4] == ' ' ) + { + if ( SearchKey(src+s+5, key) ) + { + res = g_engine->RetKey(key, 0); + if ( res != 0 ) + { + if ( GetResource(RES_KEY, res, name) ) + { + n = 0; + while ( name[n] != 0 ) + { + dst[d++] = name[n++]; + } + while ( src[s++] != ';' ); + continue; + } + } + } + } + + dst[d++] = src[s++]; + } + dst[d++] = 0; +} + + +// Retourne le texte d'une ressource. + +BOOL GetResource(ResType type, int num, char* text) +{ + char buffer[100]; + + if ( !GetResourceBase(type, num, buffer) ) + { + text[0] = 0; + return FALSE; + } + + PutKeyName(text, buffer); + return TRUE; +} + + +// Retourne le texte d'une ressource. + +BOOL GetResourceBase(ResType type, int num, char* text) +{ + text[0] = 0; + +#if _ENGLISH + if ( type == RES_TEXT ) + { + #if _FULL + if ( num == RT_VERSION_ID ) strcpy(text, "1.18 /e"); + #endif + #if _NET + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A 1.18"); + #endif + #if _SCHOOL & _EDU + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen EDU 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A EDU 1.18"); + #endif + #endif + #if _SCHOOL & _PERSO + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen PERSO 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A PERSO 1.18"); + #endif + #endif + #if _SCHOOL & _CEEBOTDEMO + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen DEMO 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A DEMO 1.18"); + #endif + #endif + #if _DEMO + if ( num == RT_VERSION_ID ) strcpy(text, "Demo 1.18 /e"); + #endif + if ( num == RT_DISINFO_TITLE ) strcpy(text, "SatCom"); + if ( num == RT_WINDOW_MAXIMIZED ) strcpy(text, "Maximize"); + if ( num == RT_WINDOW_MINIMIZED ) strcpy(text, "Minimize"); + if ( num == RT_WINDOW_STANDARD ) strcpy(text, "Normal size"); + if ( num == RT_WINDOW_CLOSE ) strcpy(text, "Close"); + + if ( num == RT_STUDIO_TITLE ) strcpy(text, "Program editor"); + if ( num == RT_SCRIPT_NEW ) strcpy(text, "New"); + if ( num == RT_NAME_DEFAULT ) strcpy(text, "Player"); + if ( num == RT_IO_NEW ) strcpy(text, "New ..."); + if ( num == RT_KEY_OR ) strcpy(text, " or "); + +#if _NEWLOOK + if ( num == RT_TITLE_BASE ) strcpy(text, "CeeBot"); + if ( num == RT_TITLE_INIT ) strcpy(text, "CeeBot"); +#else + if ( num == RT_TITLE_BASE ) strcpy(text, "COLOBOT"); + if ( num == RT_TITLE_INIT ) strcpy(text, "COLOBOT"); +#endif + if ( num == RT_TITLE_TRAINER ) strcpy(text, "Programming exercises"); + if ( num == RT_TITLE_DEFI ) strcpy(text, "Challenges"); + if ( num == RT_TITLE_MISSION ) strcpy(text, "Missions"); + if ( num == RT_TITLE_FREE ) strcpy(text, "Free game"); + if ( num == RT_TITLE_TEEN ) strcpy(text, "Free game"); + if ( num == RT_TITLE_USER ) strcpy(text, "User levels"); + if ( num == RT_TITLE_PROTO ) strcpy(text, "Prototypes"); + if ( num == RT_TITLE_SETUP ) strcpy(text, "Options"); + if ( num == RT_TITLE_NAME ) strcpy(text, "Player's name"); + if ( num == RT_TITLE_PERSO ) strcpy(text, "Customize your appearance"); + if ( num == RT_TITLE_WRITE ) strcpy(text, "Save the current mission"); + if ( num == RT_TITLE_READ ) strcpy(text, "Load a saved mission"); + + if ( num == RT_PLAY_CHAPt ) strcpy(text, " Chapters:"); + if ( num == RT_PLAY_CHAPd ) strcpy(text, " Chapters:"); + if ( num == RT_PLAY_CHAPm ) strcpy(text, " Planets:"); + if ( num == RT_PLAY_CHAPf ) strcpy(text, " Planets:"); + if ( num == RT_PLAY_CHAPu ) strcpy(text, " User levels:"); + if ( num == RT_PLAY_CHAPp ) strcpy(text, " Planets:"); + if ( num == RT_PLAY_CHAPte ) strcpy(text, " Chapters:"); + if ( num == RT_PLAY_LISTt ) strcpy(text, " Exercises in the chapter:"); + if ( num == RT_PLAY_LISTd ) strcpy(text, " Challenges in the chapter:"); + if ( num == RT_PLAY_LISTm ) strcpy(text, " Missions on this planet:"); + if ( num == RT_PLAY_LISTf ) strcpy(text, " Free game on this planet:"); + if ( num == RT_PLAY_LISTu ) strcpy(text, " Missions on this level:"); + if ( num == RT_PLAY_LISTp ) strcpy(text, " Prototypes on this planet:"); + if ( num == RT_PLAY_LISTk ) strcpy(text, " Free game on this chapter:"); + if ( num == RT_PLAY_RESUME ) strcpy(text, " Summary:"); + + if ( num == RT_SETUP_DEVICE ) strcpy(text, " Drivers:"); + if ( num == RT_SETUP_MODE ) strcpy(text, " Resolution:"); + if ( num == RT_SETUP_KEY1 ) strcpy(text, "1) First click on the key you want to redefine."); + if ( num == RT_SETUP_KEY2 ) strcpy(text, "2) Then press the key you want to use instead."); + + if ( num == RT_PERSO_FACE ) strcpy(text, "Face type:"); + if ( num == RT_PERSO_GLASSES ) strcpy(text, "Eyeglasses:"); + if ( num == RT_PERSO_HAIR ) strcpy(text, "Hair color:"); + if ( num == RT_PERSO_COMBI ) strcpy(text, "Suit color:"); + if ( num == RT_PERSO_BAND ) strcpy(text, "Strip color:"); + +#if _NEWLOOK + if ( num == RT_DIALOG_QUIT ) strcpy(text, "Do you want to quit CeeBot ?"); + if ( num == RT_DIALOG_TITLE ) strcpy(text, "CeeBot"); + if ( num == RT_DIALOG_YESQUIT ) strcpy(text, "Quit\\Quit CeeBot"); +#else + if ( num == RT_DIALOG_QUIT ) strcpy(text, "Do you want to quit COLOBOT ?"); + if ( num == RT_DIALOG_TITLE ) strcpy(text, "COLOBOT"); + if ( num == RT_DIALOG_YESQUIT ) strcpy(text, "Quit\\Quit COLOBOT"); +#endif + if ( num == RT_DIALOG_ABORT ) strcpy(text, "Quit the mission?"); + if ( num == RT_DIALOG_YES ) strcpy(text, "Abort\\Abort the current mission"); + if ( num == RT_DIALOG_NO ) strcpy(text, "Continue\\Continue the current mission"); + if ( num == RT_DIALOG_NOQUIT ) strcpy(text, "Continue\\Continue the game"); + if ( num == RT_DIALOG_DELOBJ ) strcpy(text, "Do you really want to destroy the selected building?"); + if ( num == RT_DIALOG_DELGAME ) strcpy(text, "Do you want to delete %s's saved games? "); + if ( num == RT_DIALOG_YESDEL ) strcpy(text, "Delete"); + if ( num == RT_DIALOG_NODEL ) strcpy(text, "Cancel"); + if ( num == RT_DIALOG_LOADING ) strcpy(text, "LOADING"); + + if ( num == RT_STUDIO_LISTTT ) strcpy(text, "Keyword help(\\key cbot;)"); + if ( num == RT_STUDIO_COMPOK ) strcpy(text, "Compilation ok (0 errors)"); + if ( num == RT_STUDIO_PROGSTOP ) strcpy(text, "Program finished"); + + if ( num == RT_SATCOM_LIST ) strcpy(text, "\\b;List of objects\n"); + if ( num == RT_SATCOM_BOT ) strcpy(text, "\\b;Robots\n"); + if ( num == RT_SATCOM_BUILDING ) strcpy(text, "\\b;Buildings\n"); + if ( num == RT_SATCOM_FRET ) strcpy(text, "\\b;Moveable objects\n"); + if ( num == RT_SATCOM_ALIEN ) strcpy(text, "\\b;Aliens\n"); + if ( num == RT_SATCOM_NULL ) strcpy(text, "\\c; (none)\\n;\n"); + if ( num == RT_SATCOM_ERROR1 ) strcpy(text, "\\b;Error\n"); + if ( num == RT_SATCOM_ERROR2 ) strcpy(text, "The list is only available if a \\l;radar station\\u object\\radar; is working.\n"); + + if ( num == RT_IO_OPEN ) strcpy(text, "Open"); + if ( num == RT_IO_SAVE ) strcpy(text, "Save"); + if ( num == RT_IO_LIST ) strcpy(text, "Folder: %s"); + if ( num == RT_IO_NAME ) strcpy(text, "Name:"); + if ( num == RT_IO_DIR ) strcpy(text, "Folder:"); + if ( num == RT_IO_PRIVATE ) strcpy(text, "Private\\Private folder"); + if ( num == RT_IO_PUBLIC ) strcpy(text, "Public\\Common folder"); + + if ( num == RT_GENERIC_DEV1 ) strcpy(text, "Developed by :"); + if ( num == RT_GENERIC_DEV2 ) strcpy(text, "www.epsitec.com"); +//? if ( num == RT_GENERIC_EDIT1 ) strcpy(text, "English version published by:"); +//? if ( num == RT_GENERIC_EDIT2 ) strcpy(text, "www.?.com"); + if ( num == RT_GENERIC_EDIT1 ) strcpy(text, " "); + if ( num == RT_GENERIC_EDIT2 ) strcpy(text, " "); + + if ( num == RT_INTERFACE_REC ) strcpy(text, "Recorder"); + } + + if ( type == RES_EVENT ) + { + if ( num == EVENT_BUTTON_OK ) strcpy(text, "OK"); + if ( num == EVENT_BUTTON_CANCEL ) strcpy(text, "Cancel"); + if ( num == EVENT_BUTTON_NEXT ) strcpy(text, "Next"); + if ( num == EVENT_BUTTON_PREV ) strcpy(text, "Previous"); + if ( num == EVENT_BUTTON_QUIT ) strcpy(text, "Menu (\\key quit;)"); + + if ( num == EVENT_DIALOG_OK ) strcpy(text, "OK"); + if ( num == EVENT_DIALOG_CANCEL ) strcpy(text, "Cancel"); + + if ( num == EVENT_INTERFACE_TRAINER) strcpy(text, "Exercises\\Programming exercises"); + if ( num == EVENT_INTERFACE_DEFI ) strcpy(text, "Challenges\\Programming challenges"); + if ( num == EVENT_INTERFACE_MISSION) strcpy(text, "Missions\\Select mission"); + if ( num == EVENT_INTERFACE_FREE ) strcpy(text, "Free game\\Free game without a specific goal"); + if ( num == EVENT_INTERFACE_TEEN ) strcpy(text, "Free game\\Free game without a specific goal"); + if ( num == EVENT_INTERFACE_USER ) strcpy(text, "User\\User levels"); + if ( num == EVENT_INTERFACE_PROTO ) strcpy(text, "Proto\\Prototypes under development"); + if ( num == EVENT_INTERFACE_NAME ) strcpy(text, "New player\\Choose player's name"); + if ( num == EVENT_INTERFACE_SETUP ) strcpy(text, "Options\\Preferences"); + if ( num == EVENT_INTERFACE_AGAIN ) strcpy(text, "Restart\\Restart the mission from the beginning"); + if ( num == EVENT_INTERFACE_WRITE ) strcpy(text, "Save\\Save the current mission "); + if ( num == EVENT_INTERFACE_READ ) strcpy(text, "Load\\Load a saved mission"); +#if _NEWLOOK + if ( num == EVENT_INTERFACE_ABORT ) strcpy(text, "\\Return to CeeBot"); + if ( num == EVENT_INTERFACE_QUIT ) strcpy(text, "Quit\\Quit CeeBot"); +#else + if ( num == EVENT_INTERFACE_ABORT ) strcpy(text, "\\Return to COLOBOT"); + if ( num == EVENT_INTERFACE_QUIT ) strcpy(text, "Quit\\Quit COLOBOT"); +#endif + if ( num == EVENT_INTERFACE_BACK ) strcpy(text, "<< Back \\Back to the previous screen"); + if ( num == EVENT_INTERFACE_PLAY ) strcpy(text, "Play\\Start mission!"); + if ( num == EVENT_INTERFACE_SETUPd ) strcpy(text, "Device\\Driver and resolution settings"); + if ( num == EVENT_INTERFACE_SETUPg ) strcpy(text, "Graphics\\Graphics settings"); + if ( num == EVENT_INTERFACE_SETUPp ) strcpy(text, "Game\\Game settings"); + if ( num == EVENT_INTERFACE_SETUPc ) strcpy(text, "Controls\\Keyboard, joystick and mouse settings"); + if ( num == EVENT_INTERFACE_SETUPs ) strcpy(text, "Sound\\Music and game sound volume"); + if ( num == EVENT_INTERFACE_DEVICE ) strcpy(text, "Unit"); + if ( num == EVENT_INTERFACE_RESOL ) strcpy(text, "Resolution"); + if ( num == EVENT_INTERFACE_FULL ) strcpy(text, "Full screen\\Full screen or window mode"); + if ( num == EVENT_INTERFACE_APPLY ) strcpy(text, "Apply changes\\Activates the changed settings"); + + if ( num == EVENT_INTERFACE_TOTO ) strcpy(text, "Robbie\\Your assistant"); + if ( num == EVENT_INTERFACE_SHADOW ) strcpy(text, "Shadows\\Shadows on the ground"); + if ( num == EVENT_INTERFACE_GROUND ) strcpy(text, "Marks on the ground\\Marks on the ground"); + if ( num == EVENT_INTERFACE_DIRTY ) strcpy(text, "Dust\\Dust and dirt on bots and buildings"); + if ( num == EVENT_INTERFACE_FOG ) strcpy(text, "Fog\\Fog"); + if ( num == EVENT_INTERFACE_LENS ) strcpy(text, "Sunbeams\\Sunbeams in the sky"); + if ( num == EVENT_INTERFACE_SKY ) strcpy(text, "Sky\\Clouds and nebulae"); + if ( num == EVENT_INTERFACE_PLANET ) strcpy(text, "Planets and stars\\Astronomical objects in the sky"); + if ( num == EVENT_INTERFACE_LIGHT ) strcpy(text, "Dynamic lighting\\Mobile light sources"); + if ( num == EVENT_INTERFACE_PARTI ) strcpy(text, "Number of particles\\Explosions, dust, reflections, etc."); + if ( num == EVENT_INTERFACE_CLIP ) strcpy(text, "Depth of field\\Maximum visibility"); + if ( num == EVENT_INTERFACE_DETAIL ) strcpy(text, "Details\\Visual quality of 3D objects"); + if ( num == EVENT_INTERFACE_TEXTURE) strcpy(text, "Textures\\Quality of textures "); + if ( num == EVENT_INTERFACE_GADGET ) strcpy(text, "Num of decorative objects\\Number of purely ornamental objects"); + if ( num == EVENT_INTERFACE_RAIN ) strcpy(text, "Particles in the interface\\Steam clouds and sparks in the interface"); + if ( num == EVENT_INTERFACE_GLINT ) strcpy(text, "Reflections on the buttons \\Shiny buttons"); + if ( num == EVENT_INTERFACE_TOOLTIP) strcpy(text, "Help balloons\\Explain the function of the buttons"); + if ( num == EVENT_INTERFACE_MOVIES ) strcpy(text, "Film sequences\\Films before and after the missions"); + if ( num == EVENT_INTERFACE_NICERST) strcpy(text, "Exit film\\Film at the exit of exercises"); + if ( num == EVENT_INTERFACE_HIMSELF) strcpy(text, "Friendly fire\\Your shooting can damage your own objects "); + if ( num == EVENT_INTERFACE_SCROLL ) strcpy(text, "Scrolling\\Scrolling when the mouse touches right or left border"); + if ( num == EVENT_INTERFACE_INVERTX) strcpy(text, "Mouse inversion X\\Inversion of the scrolling direction on the X axis"); + if ( num == EVENT_INTERFACE_INVERTY) strcpy(text, "Mouse inversion Y\\Inversion of the scrolling direction on the Y axis"); + if ( num == EVENT_INTERFACE_EFFECT ) strcpy(text, "Quake at explosions\\The screen shakes at explosions"); + if ( num == EVENT_INTERFACE_MOUSE ) strcpy(text, "Mouse shadow\\Gives the mouse a shadow"); + if ( num == EVENT_INTERFACE_EDITMODE) strcpy(text, "Automatic indent\\When program editing"); + if ( num == EVENT_INTERFACE_EDITVALUE)strcpy(text, "Big indent\\Indent 2 or 4 spaces per level defined by braces"); + if ( num == EVENT_INTERFACE_SOLUCE4) strcpy(text, "Access to solutions\\Show program \"4: Solution\" in the exercises"); //** + + if ( num == EVENT_INTERFACE_KDEF ) strcpy(text, "Standard controls\\Standard key functions"); + if ( num == EVENT_INTERFACE_KLEFT ) strcpy(text, "Turn left\\turns the bot to the left"); + if ( num == EVENT_INTERFACE_KRIGHT ) strcpy(text, "Turn right\\turns the bot to the right"); + if ( num == EVENT_INTERFACE_KUP ) strcpy(text, "Forward\\Moves forward"); + if ( num == EVENT_INTERFACE_KDOWN ) strcpy(text, "Backward\\Moves backward"); + if ( num == EVENT_INTERFACE_KGUP ) strcpy(text, "Climb\\Increases the power of the jet"); + if ( num == EVENT_INTERFACE_KGDOWN ) strcpy(text, "Descend\\Reduces the power of the jet"); + if ( num == EVENT_INTERFACE_KCAMERA) strcpy(text, "Change camera\\Switches between onboard camera and following camera"); + if ( num == EVENT_INTERFACE_KDESEL ) strcpy(text, "Previous object\\Selects the previous object"); + if ( num == EVENT_INTERFACE_KACTION) strcpy(text, "Standard action\\Standard action of the bot (take/grab, shoot, sniff, etc)"); + if ( num == EVENT_INTERFACE_KNEAR ) strcpy(text, "Camera closer\\Moves the camera forward"); + if ( num == EVENT_INTERFACE_KAWAY ) strcpy(text, "Camera back\\Moves the camera backward"); + if ( num == EVENT_INTERFACE_KNEXT ) strcpy(text, "Next object\\Selects the next object"); + if ( num == EVENT_INTERFACE_KHUMAN ) strcpy(text, "Select the astronaut\\Selects the astronaut"); + if ( num == EVENT_INTERFACE_KQUIT ) strcpy(text, "Quit\\Quit the current mission or exercise"); + if ( num == EVENT_INTERFACE_KHELP ) strcpy(text, "Instructions\\Shows the instructions for the current mission"); + if ( num == EVENT_INTERFACE_KPROG ) strcpy(text, "Programming help\\Gives more detailed help with programming"); + if ( num == EVENT_INTERFACE_KCBOT ) strcpy(text, "Key word help\\More detailed help about key words"); + if ( num == EVENT_INTERFACE_KVISIT ) strcpy(text, "Origin of last message\\Shows where the last message was sent from"); + if ( num == EVENT_INTERFACE_KSPEED10) strcpy(text, "Speed 1.0x\\Normal speed"); + if ( num == EVENT_INTERFACE_KSPEED15) strcpy(text, "Speed 1.5x\\1.5 times faster"); + if ( num == EVENT_INTERFACE_KSPEED20) strcpy(text, "Speed 2.0x\\Double speed"); + if ( num == EVENT_INTERFACE_KSPEED30) strcpy(text, "Speed 3.0x\\Three times faster"); + + if ( num == EVENT_INTERFACE_VOLSOUND) strcpy(text, "Sound effects:\\Volume of engines, voice, shooting, etc."); + if ( num == EVENT_INTERFACE_VOLMUSIC) strcpy(text, "Background sound :\\Volume of audio tracks on the CD"); + if ( num == EVENT_INTERFACE_SOUND3D) strcpy(text, "3D sound\\3D positioning of the sound"); + + if ( num == EVENT_INTERFACE_MIN ) strcpy(text, "Lowest\\Minimum graphic quality (highest frame rate)"); + if ( num == EVENT_INTERFACE_NORM ) strcpy(text, "Normal\\Normal graphic quality"); + if ( num == EVENT_INTERFACE_MAX ) strcpy(text, "Highest\\Highest graphic quality (lowest frame rate)"); + + if ( num == EVENT_INTERFACE_SILENT ) strcpy(text, "Mute\\No sound"); + if ( num == EVENT_INTERFACE_NOISY ) strcpy(text, "Normal\\Normal sound volume"); + + if ( num == EVENT_INTERFACE_JOYSTICK) strcpy(text, "Use a joystick\\Joystick or keyboard"); + if ( num == EVENT_INTERFACE_SOLUCE ) strcpy(text, "Access to solution\\Shows the solution (detailed instructions for missions)"); + + if ( num == EVENT_INTERFACE_NEDIT ) strcpy(text, "\\New player name"); + if ( num == EVENT_INTERFACE_NOK ) strcpy(text, "OK\\Choose the selected player"); + if ( num == EVENT_INTERFACE_NCANCEL) strcpy(text, "Cancel\\Keep current player name"); + if ( num == EVENT_INTERFACE_NDELETE) strcpy(text, "Delete player\\Deletes the player from the list"); + if ( num == EVENT_INTERFACE_NLABEL ) strcpy(text, "Player name"); + + if ( num == EVENT_INTERFACE_IOWRITE) strcpy(text, "Save\\Saves the current mission"); + if ( num == EVENT_INTERFACE_IOREAD ) strcpy(text, "Load\\Loads the selected mission"); + if ( num == EVENT_INTERFACE_IOLIST ) strcpy(text, "List of saved missions"); + if ( num == EVENT_INTERFACE_IOLABEL) strcpy(text, "Filename:"); + if ( num == EVENT_INTERFACE_IONAME ) strcpy(text, "Mission name"); + if ( num == EVENT_INTERFACE_IOIMAGE) strcpy(text, "Photography"); + if ( num == EVENT_INTERFACE_IODELETE) strcpy(text, "Delete\\Deletes the selected file"); + + if ( num == EVENT_INTERFACE_PERSO ) strcpy(text, "Appearance\\Choose your appearance"); + if ( num == EVENT_INTERFACE_POK ) strcpy(text, "OK"); + if ( num == EVENT_INTERFACE_PCANCEL) strcpy(text, "Cancel"); + if ( num == EVENT_INTERFACE_PDEF ) strcpy(text, "Standard\\Standard appearance settings"); + if ( num == EVENT_INTERFACE_PHEAD ) strcpy(text, "Head\\Face and hair"); + if ( num == EVENT_INTERFACE_PBODY ) strcpy(text, "Suit\\Astronaut suit"); + if ( num == EVENT_INTERFACE_PLROT ) strcpy(text, "\\Turn left"); + if ( num == EVENT_INTERFACE_PRROT ) strcpy(text, "\\Turn right"); + if ( num == EVENT_INTERFACE_PCRa ) strcpy(text, "Red"); + if ( num == EVENT_INTERFACE_PCGa ) strcpy(text, "Green"); + if ( num == EVENT_INTERFACE_PCBa ) strcpy(text, "Blue"); + if ( num == EVENT_INTERFACE_PCRb ) strcpy(text, "Red"); + if ( num == EVENT_INTERFACE_PCGb ) strcpy(text, "Green"); + if ( num == EVENT_INTERFACE_PCBb ) strcpy(text, "Blue"); + if ( num == EVENT_INTERFACE_PFACE1 ) strcpy(text, "\\Face 1"); + if ( num == EVENT_INTERFACE_PFACE2 ) strcpy(text, "\\Face 4"); + if ( num == EVENT_INTERFACE_PFACE3 ) strcpy(text, "\\Face 3"); + if ( num == EVENT_INTERFACE_PFACE4 ) strcpy(text, "\\Face 2"); + if ( num == EVENT_INTERFACE_PGLASS0) strcpy(text, "\\No eyeglasses"); + if ( num == EVENT_INTERFACE_PGLASS1) strcpy(text, "\\Eyeglasses 1"); + if ( num == EVENT_INTERFACE_PGLASS2) strcpy(text, "\\Eyeglasses 2"); + if ( num == EVENT_INTERFACE_PGLASS3) strcpy(text, "\\Eyeglasses 3"); + if ( num == EVENT_INTERFACE_PGLASS4) strcpy(text, "\\Eyeglasses 4"); + if ( num == EVENT_INTERFACE_PGLASS5) strcpy(text, "\\Eyeglasses 5"); + + if ( num == EVENT_OBJECT_DESELECT ) strcpy(text, "Previous selection (\\key desel;)"); + if ( num == EVENT_OBJECT_LEFT ) strcpy(text, "Turn left (\\key left;)"); + if ( num == EVENT_OBJECT_RIGHT ) strcpy(text, "Turn right (\\key right;)"); + if ( num == EVENT_OBJECT_UP ) strcpy(text, "Forward (\\key up;)"); + if ( num == EVENT_OBJECT_DOWN ) strcpy(text, "Backward (\\key down;)"); + if ( num == EVENT_OBJECT_GASUP ) strcpy(text, "Up (\\key gup;)"); + if ( num == EVENT_OBJECT_GASDOWN ) strcpy(text, "Down (\\key gdown;)"); + if ( num == EVENT_OBJECT_HTAKE ) strcpy(text, "Grab or drop (\\key action;)"); + if ( num == EVENT_OBJECT_MTAKE ) strcpy(text, "Grab or drop (\\key action;)"); + if ( num == EVENT_OBJECT_MFRONT ) strcpy(text, "..in front"); + if ( num == EVENT_OBJECT_MBACK ) strcpy(text, "..behind"); + if ( num == EVENT_OBJECT_MPOWER ) strcpy(text, "..power cell"); + if ( num == EVENT_OBJECT_BHELP ) strcpy(text, "Instructions for the mission (\\key help;)"); + if ( num == EVENT_OBJECT_BTAKEOFF ) strcpy(text, "Take off to finish the mission"); + if ( num == EVENT_OBJECT_BDERRICK ) strcpy(text, "Build a derrick"); + if ( num == EVENT_OBJECT_BSTATION ) strcpy(text, "Build a power station"); + if ( num == EVENT_OBJECT_BFACTORY ) strcpy(text, "Build a bot factory"); + if ( num == EVENT_OBJECT_BREPAIR ) strcpy(text, "Build a repair center"); + if ( num == EVENT_OBJECT_BCONVERT ) strcpy(text, "Build a converter"); + if ( num == EVENT_OBJECT_BTOWER ) strcpy(text, "Build a defense tower"); + if ( num == EVENT_OBJECT_BRESEARCH ) strcpy(text, "Build a research center"); + if ( num == EVENT_OBJECT_BRADAR ) strcpy(text, "Build a radar station"); + if ( num == EVENT_OBJECT_BENERGY ) strcpy(text, "Build a power cell factory"); + if ( num == EVENT_OBJECT_BLABO ) strcpy(text, "Build an autolab"); + if ( num == EVENT_OBJECT_BNUCLEAR ) strcpy(text, "Build a nuclear power plant"); + if ( num == EVENT_OBJECT_BPARA ) strcpy(text, "Build a lightning conductor"); + if ( num == EVENT_OBJECT_BINFO ) strcpy(text, "Build a exchange post"); + if ( num == EVENT_OBJECT_GFLAT ) strcpy(text, "Show if the ground is flat"); + if ( num == EVENT_OBJECT_FCREATE ) strcpy(text, "Plant a flag"); + if ( num == EVENT_OBJECT_FDELETE ) strcpy(text, "Remove a flag"); + if ( num == EVENT_OBJECT_FCOLORb ) strcpy(text, "\\Blue flags"); + if ( num == EVENT_OBJECT_FCOLORr ) strcpy(text, "\\Red flags"); + if ( num == EVENT_OBJECT_FCOLORg ) strcpy(text, "\\Green flags"); + if ( num == EVENT_OBJECT_FCOLORy ) strcpy(text, "\\Yellow flags"); + if ( num == EVENT_OBJECT_FCOLORv ) strcpy(text, "\\Violet flags"); + if ( num == EVENT_OBJECT_FACTORYfa ) strcpy(text, "Build a winged grabber"); + if ( num == EVENT_OBJECT_FACTORYta ) strcpy(text, "Build a tracked grabber"); + if ( num == EVENT_OBJECT_FACTORYwa ) strcpy(text, "Build a wheeled grabber"); + if ( num == EVENT_OBJECT_FACTORYia ) strcpy(text, "Build a legged grabber"); + if ( num == EVENT_OBJECT_FACTORYfc ) strcpy(text, "Build a winged shooter"); + if ( num == EVENT_OBJECT_FACTORYtc ) strcpy(text, "Build a tracked shooter"); + if ( num == EVENT_OBJECT_FACTORYwc ) strcpy(text, "Build a wheeled shooter"); + if ( num == EVENT_OBJECT_FACTORYic ) strcpy(text, "Build a legged shooter"); + if ( num == EVENT_OBJECT_FACTORYfi ) strcpy(text, "Build a winged orga shooter"); + if ( num == EVENT_OBJECT_FACTORYti ) strcpy(text, "Build a tracked orga shooter"); + if ( num == EVENT_OBJECT_FACTORYwi ) strcpy(text, "Build a wheeled orga shooter"); + if ( num == EVENT_OBJECT_FACTORYii ) strcpy(text, "Build a legged orga shooter"); + if ( num == EVENT_OBJECT_FACTORYfs ) strcpy(text, "Build a winged sniffer"); + if ( num == EVENT_OBJECT_FACTORYts ) strcpy(text, "Build a tracked sniffer"); + if ( num == EVENT_OBJECT_FACTORYws ) strcpy(text, "Build a wheeled sniffer"); + if ( num == EVENT_OBJECT_FACTORYis ) strcpy(text, "Build a legged sniffer"); + if ( num == EVENT_OBJECT_FACTORYrt ) strcpy(text, "Build a thumper"); + if ( num == EVENT_OBJECT_FACTORYrc ) strcpy(text, "Build a phazer shooter"); + if ( num == EVENT_OBJECT_FACTORYrr ) strcpy(text, "Build a recycler"); + if ( num == EVENT_OBJECT_FACTORYrs ) strcpy(text, "Build a shielder"); + if ( num == EVENT_OBJECT_FACTORYsa ) strcpy(text, "Build a subber"); + if ( num == EVENT_OBJECT_RTANK ) strcpy(text, "Run research program for tracked bots"); + if ( num == EVENT_OBJECT_RFLY ) strcpy(text, "Run research program for winged bots"); + if ( num == EVENT_OBJECT_RTHUMP ) strcpy(text, "Run research program for thumper"); + if ( num == EVENT_OBJECT_RCANON ) strcpy(text, "Run research program for shooter"); + if ( num == EVENT_OBJECT_RTOWER ) strcpy(text, "Run research program for defense tower"); + if ( num == EVENT_OBJECT_RPHAZER ) strcpy(text, "Run research program for phazer shooter"); + if ( num == EVENT_OBJECT_RSHIELD ) strcpy(text, "Run research program for shielder"); + if ( num == EVENT_OBJECT_RATOMIC ) strcpy(text, "Run research program for nuclear power"); + if ( num == EVENT_OBJECT_RiPAW ) strcpy(text, "Run research program for legged bots"); + if ( num == EVENT_OBJECT_RiGUN ) strcpy(text, "Run research program for orga shooter"); + if ( num == EVENT_OBJECT_RESET ) strcpy(text, "Return to start"); + if ( num == EVENT_OBJECT_SEARCH ) strcpy(text, "Sniff (\\key action;)"); + if ( num == EVENT_OBJECT_TERRAFORM ) strcpy(text, "Thump (\\key action;)"); + if ( num == EVENT_OBJECT_FIRE ) strcpy(text, "Shoot (\\key action;)"); + if ( num == EVENT_OBJECT_RECOVER ) strcpy(text, "Recycle (\\key action;)"); + if ( num == EVENT_OBJECT_BEGSHIELD ) strcpy(text, "Extend shield (\\key action;)"); + if ( num == EVENT_OBJECT_ENDSHIELD ) strcpy(text, "Withdraw shield (\\key action;)"); + if ( num == EVENT_OBJECT_DIMSHIELD ) strcpy(text, "Shield radius"); + if ( num == EVENT_OBJECT_PROGRUN ) strcpy(text, "Execute the selected program"); + if ( num == EVENT_OBJECT_PROGEDIT ) strcpy(text, "Edit the selected program"); + if ( num == EVENT_OBJECT_INFOOK ) strcpy(text, "\\SatCom on standby"); + if ( num == EVENT_OBJECT_DELETE ) strcpy(text, "Destroy the building"); + if ( num == EVENT_OBJECT_GENERGY ) strcpy(text, "Energy level"); + if ( num == EVENT_OBJECT_GSHIELD ) strcpy(text, "Shield level"); + if ( num == EVENT_OBJECT_GRANGE ) strcpy(text, "Jet temperature"); + if ( num == EVENT_OBJECT_GPROGRESS ) strcpy(text, "Still working ..."); + if ( num == EVENT_OBJECT_GRADAR ) strcpy(text, "Number of insects detected"); + if ( num == EVENT_OBJECT_GINFO ) strcpy(text, "Transmitted information"); + if ( num == EVENT_OBJECT_COMPASS ) strcpy(text, "Compass"); +//? if ( num == EVENT_OBJECT_MAP ) strcpy(text, "Mini-map"); + if ( num == EVENT_OBJECT_MAPZOOM ) strcpy(text, "Zoom mini-map"); + if ( num == EVENT_OBJECT_CAMERA ) strcpy(text, "Camera (\\key camera;)"); + if ( num == EVENT_OBJECT_CAMERAleft) strcpy(text, "Camera to left"); + if ( num == EVENT_OBJECT_CAMERAright) strcpy(text, "Camera to right"); + if ( num == EVENT_OBJECT_CAMERAnear) strcpy(text, "Camera nearest"); + if ( num == EVENT_OBJECT_CAMERAaway) strcpy(text, "Camera awayest"); + if ( num == EVENT_OBJECT_HELP ) strcpy(text, "Help about selected object"); + if ( num == EVENT_OBJECT_SOLUCE ) strcpy(text, "Show the solution"); + if ( num == EVENT_OBJECT_SHORTCUT00) strcpy(text, "Switch bots <-> buildings"); + if ( num == EVENT_OBJECT_LIMIT ) strcpy(text, "Show the range"); + if ( num == EVENT_OBJECT_PEN0 ) strcpy(text, "\\Raise the pencil"); + if ( num == EVENT_OBJECT_PEN1 ) strcpy(text, "\\Use the black pencil"); + if ( num == EVENT_OBJECT_PEN2 ) strcpy(text, "\\Use the yellow pencil"); + if ( num == EVENT_OBJECT_PEN3 ) strcpy(text, "\\Use the orange pencil"); + if ( num == EVENT_OBJECT_PEN4 ) strcpy(text, "\\Use the red pencil"); + if ( num == EVENT_OBJECT_PEN5 ) strcpy(text, "\\Use the purple pencil"); + if ( num == EVENT_OBJECT_PEN6 ) strcpy(text, "\\Use the blue pencil"); + if ( num == EVENT_OBJECT_PEN7 ) strcpy(text, "\\Use the green pencil"); + if ( num == EVENT_OBJECT_PEN8 ) strcpy(text, "\\Use the brown pencil"); + if ( num == EVENT_OBJECT_REC ) strcpy(text, "\\Start recording"); + if ( num == EVENT_OBJECT_STOP ) strcpy(text, "\\Stop recording"); + if ( num == EVENT_DT_VISIT0 || + num == EVENT_DT_VISIT1 || + num == EVENT_DT_VISIT2 || + num == EVENT_DT_VISIT3 || + num == EVENT_DT_VISIT4 ) strcpy(text, "Show the place"); + if ( num == EVENT_DT_END ) strcpy(text, "Continue"); + if ( num == EVENT_CMD ) strcpy(text, "Command line"); + if ( num == EVENT_SPEED ) strcpy(text, "Game speed"); + + if ( num == EVENT_HYPER_PREV ) strcpy(text, "Back"); + if ( num == EVENT_HYPER_NEXT ) strcpy(text, "Forward"); + if ( num == EVENT_HYPER_HOME ) strcpy(text, "Home"); + if ( num == EVENT_HYPER_COPY ) strcpy(text, "Copy"); + if ( num == EVENT_HYPER_SIZE1 ) strcpy(text, "Size 1"); + if ( num == EVENT_HYPER_SIZE2 ) strcpy(text, "Size 2"); + if ( num == EVENT_HYPER_SIZE3 ) strcpy(text, "Size 3"); + if ( num == EVENT_HYPER_SIZE4 ) strcpy(text, "Size 4"); + if ( num == EVENT_HYPER_SIZE5 ) strcpy(text, "Size 5"); + if ( num == EVENT_SATCOM_HUSTON ) strcpy(text, "Instructions from Houston"); +#if _TEEN + if ( num == EVENT_SATCOM_SAT ) strcpy(text, "Dictionnary"); +#else + if ( num == EVENT_SATCOM_SAT ) strcpy(text, "Satellite report"); +#endif + if ( num == EVENT_SATCOM_LOADING ) strcpy(text, "Programs dispatched by Houston"); + if ( num == EVENT_SATCOM_OBJECT ) strcpy(text, "List of objects"); + if ( num == EVENT_SATCOM_PROG ) strcpy(text, "Programming help"); + if ( num == EVENT_SATCOM_SOLUCE ) strcpy(text, "Solution"); + + if ( num == EVENT_STUDIO_OK ) strcpy(text, "OK\\Close program editor and return to game"); + if ( num == EVENT_STUDIO_CANCEL ) strcpy(text, "Cancel\\Cancel all changes"); + if ( num == EVENT_STUDIO_NEW ) strcpy(text, "New"); + if ( num == EVENT_STUDIO_OPEN ) strcpy(text, "Open (Ctrl+o)"); + if ( num == EVENT_STUDIO_SAVE ) strcpy(text, "Save (Ctrl+s)"); + if ( num == EVENT_STUDIO_UNDO ) strcpy(text, "Undo (Ctrl+z)"); + if ( num == EVENT_STUDIO_CUT ) strcpy(text, "Cut (Ctrl+x)"); + if ( num == EVENT_STUDIO_COPY ) strcpy(text, "Copy (Ctrl+c)"); + if ( num == EVENT_STUDIO_PASTE ) strcpy(text, "Paste (Ctrl+v)"); + if ( num == EVENT_STUDIO_SIZE ) strcpy(text, "Font size"); + if ( num == EVENT_STUDIO_TOOL ) strcpy(text, "Instructions (\\key help;)"); + if ( num == EVENT_STUDIO_HELP ) strcpy(text, "Programming help (\\key prog;)"); + if ( num == EVENT_STUDIO_COMPILE ) strcpy(text, "Compile"); + if ( num == EVENT_STUDIO_RUN ) strcpy(text, "Execute/stop"); + if ( num == EVENT_STUDIO_REALTIME ) strcpy(text, "Pause/continue"); + if ( num == EVENT_STUDIO_STEP ) strcpy(text, "One step"); + } + + if ( type == RES_OBJECT ) + { + if ( num == OBJECT_PORTICO ) strcpy(text, "Gantry crane"); + if ( num == OBJECT_BASE ) strcpy(text, "Spaceship"); + if ( num == OBJECT_DERRICK ) strcpy(text, "Derrick"); + if ( num == OBJECT_FACTORY ) strcpy(text, "Bot factory"); + if ( num == OBJECT_REPAIR ) strcpy(text, "Repair center"); + if ( num == OBJECT_DESTROYER ) strcpy(text, "Destroyer"); + if ( num == OBJECT_STATION ) strcpy(text, "Power station"); + if ( num == OBJECT_CONVERT ) strcpy(text, "Converts ore to titanium"); + if ( num == OBJECT_TOWER ) strcpy(text, "Defense tower"); + if ( num == OBJECT_NEST ) strcpy(text, "Nest"); + if ( num == OBJECT_RESEARCH ) strcpy(text, "Research center"); + if ( num == OBJECT_RADAR ) strcpy(text, "Radar station"); + if ( num == OBJECT_INFO ) strcpy(text, "Information exchange post"); +#if _TEEN + if ( num == OBJECT_ENERGY ) strcpy(text, "Power cell factory"); +#else + if ( num == OBJECT_ENERGY ) strcpy(text, "Power cell factory"); +#endif + if ( num == OBJECT_LABO ) strcpy(text, "Autolab"); + if ( num == OBJECT_NUCLEAR ) strcpy(text, "Nuclear power station"); + if ( num == OBJECT_PARA ) strcpy(text, "Lightning conductor"); + if ( num == OBJECT_SAFE ) strcpy(text, "Vault"); + if ( num == OBJECT_HUSTON ) strcpy(text, "Houston Mission Control"); + if ( num == OBJECT_TARGET1 ) strcpy(text, "Target"); + if ( num == OBJECT_TARGET2 ) strcpy(text, "Target"); + if ( num == OBJECT_START ) strcpy(text, "Start"); + if ( num == OBJECT_END ) strcpy(text, "Finish"); + if ( num == OBJECT_STONE ) strcpy(text, "Titanium ore"); + if ( num == OBJECT_URANIUM ) strcpy(text, "Uranium ore"); + if ( num == OBJECT_BULLET ) strcpy(text, "Organic matter"); + if ( num == OBJECT_METAL ) strcpy(text, "Titanium"); + if ( num == OBJECT_POWER ) strcpy(text, "Power cell"); + if ( num == OBJECT_ATOMIC ) strcpy(text, "Nuclear power cell"); + if ( num == OBJECT_BBOX ) strcpy(text, "Black box"); + if ( num == OBJECT_KEYa ) strcpy(text, "Key A"); + if ( num == OBJECT_KEYb ) strcpy(text, "Key B"); + if ( num == OBJECT_KEYc ) strcpy(text, "Key C"); + if ( num == OBJECT_KEYd ) strcpy(text, "Key D"); + if ( num == OBJECT_TNT ) strcpy(text, "Explosive"); + if ( num == OBJECT_BOMB ) strcpy(text, "Fixed mine"); + if ( num == OBJECT_BAG ) strcpy(text, "Survival kit"); + if ( num == OBJECT_WAYPOINT ) strcpy(text, "Checkpoint"); + if ( num == OBJECT_FLAGb ) strcpy(text, "Blue flag"); + if ( num == OBJECT_FLAGr ) strcpy(text, "Red flag"); + if ( num == OBJECT_FLAGg ) strcpy(text, "Green flag"); + if ( num == OBJECT_FLAGy ) strcpy(text, "Yellow flag"); + if ( num == OBJECT_FLAGv ) strcpy(text, "Violet flag"); + if ( num == OBJECT_MARKPOWER ) strcpy(text, "Energy deposit (site for power station)"); + if ( num == OBJECT_MARKURANIUM ) strcpy(text, "Uranium deposit (site for derrick)"); + if ( num == OBJECT_MARKKEYa ) strcpy(text, "Found key A (site for derrick)"); + if ( num == OBJECT_MARKKEYb ) strcpy(text, "Found key B (site for derrick)"); + if ( num == OBJECT_MARKKEYc ) strcpy(text, "Found key C (site for derrick)"); + if ( num == OBJECT_MARKKEYd ) strcpy(text, "Found key D (site for derrick)"); + if ( num == OBJECT_MARKSTONE ) strcpy(text, "Titanium deposit (site for derrick)"); + if ( num == OBJECT_MOBILEft ) strcpy(text, "Practice bot"); + if ( num == OBJECT_MOBILEtt ) strcpy(text, "Practice bot"); + if ( num == OBJECT_MOBILEwt ) strcpy(text, "Practice bot"); + if ( num == OBJECT_MOBILEit ) strcpy(text, "Practice bot"); + if ( num == OBJECT_MOBILEfa ) strcpy(text, "Winged grabber"); + if ( num == OBJECT_MOBILEta ) strcpy(text, "Tracked grabber"); + if ( num == OBJECT_MOBILEwa ) strcpy(text, "Wheeled grabber"); + if ( num == OBJECT_MOBILEia ) strcpy(text, "Legged grabber"); + if ( num == OBJECT_MOBILEfc ) strcpy(text, "Winged shooter"); + if ( num == OBJECT_MOBILEtc ) strcpy(text, "Tracked shooter"); + if ( num == OBJECT_MOBILEwc ) strcpy(text, "Wheeled shooter"); + if ( num == OBJECT_MOBILEic ) strcpy(text, "Legged shooter"); + if ( num == OBJECT_MOBILEfi ) strcpy(text, "Winged orga shooter"); + if ( num == OBJECT_MOBILEti ) strcpy(text, "Tracked orga shooter"); + if ( num == OBJECT_MOBILEwi ) strcpy(text, "Wheeled orga shooter"); + if ( num == OBJECT_MOBILEii ) strcpy(text, "Legged orga shooter"); + if ( num == OBJECT_MOBILEfs ) strcpy(text, "Winged sniffer"); + if ( num == OBJECT_MOBILEts ) strcpy(text, "Tracked sniffer"); + if ( num == OBJECT_MOBILEws ) strcpy(text, "Wheeled sniffer"); + if ( num == OBJECT_MOBILEis ) strcpy(text, "Legged sniffer"); + if ( num == OBJECT_MOBILErt ) strcpy(text, "Thumper"); + if ( num == OBJECT_MOBILErc ) strcpy(text, "Phazer shooter"); + if ( num == OBJECT_MOBILErr ) strcpy(text, "Recycler"); + if ( num == OBJECT_MOBILErs ) strcpy(text, "Shielder"); + if ( num == OBJECT_MOBILEsa ) strcpy(text, "Subber"); + if ( num == OBJECT_MOBILEtg ) strcpy(text, "Target bot"); + if ( num == OBJECT_MOBILEdr ) strcpy(text, "Drawer bot"); + if ( num == OBJECT_HUMAN ) strcpy(text, g_gamerName); + if ( num == OBJECT_TECH ) strcpy(text, "Engineer"); + if ( num == OBJECT_TOTO ) strcpy(text, "Robbie"); + if ( num == OBJECT_MOTHER ) strcpy(text, "Alien Queen"); + if ( num == OBJECT_ANT ) strcpy(text, "Ant"); + if ( num == OBJECT_SPIDER ) strcpy(text, "Spider"); + if ( num == OBJECT_BEE ) strcpy(text, "Wasp"); + if ( num == OBJECT_WORM ) strcpy(text, "Worm"); + if ( num == OBJECT_EGG ) strcpy(text, "Egg"); + if ( num == OBJECT_RUINmobilew1 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINmobilew2 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINmobilet1 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINmobilet2 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINmobiler1 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINmobiler2 ) strcpy(text, "Wreckage"); + if ( num == OBJECT_RUINfactory ) strcpy(text, "Ruin"); + if ( num == OBJECT_RUINdoor ) strcpy(text, "Ruin"); + if ( num == OBJECT_RUINsupport ) strcpy(text, "Waste"); + if ( num == OBJECT_RUINradar ) strcpy(text, "Ruin"); + if ( num == OBJECT_RUINconvert ) strcpy(text, "Ruin"); + if ( num == OBJECT_RUINbase ) strcpy(text, "Spaceship ruin"); + if ( num == OBJECT_RUINhead ) strcpy(text, "Spaceship ruin"); + if ( num == OBJECT_APOLLO1 || + num == OBJECT_APOLLO3 || + num == OBJECT_APOLLO4 || + num == OBJECT_APOLLO5 ) strcpy(text, "Remains of Apollo mission"); + if ( num == OBJECT_APOLLO2 ) strcpy(text, "Lunar Roving Vehicle"); + } + + if ( type == RES_ERR ) + { + strcpy(text, "Error"); + if ( num == ERR_CMD ) strcpy(text, "Unknown command"); +#if _NEWLOOK + if ( num == ERR_INSTALL ) strcpy(text, "CeeBot not installed."); + if ( num == ERR_NOCD ) strcpy(text, "Please insert the CeeBot CD\nand re-run the game."); +#else + if ( num == ERR_INSTALL ) strcpy(text, "COLOBOT not installed."); + if ( num == ERR_NOCD ) strcpy(text, "Please insert the COLOBOT CD\nand re-run the game."); +#endif + if ( num == ERR_MANIP_VEH ) strcpy(text, "Inappropriate bot"); + if ( num == ERR_MANIP_FLY ) strcpy(text, "Impossible when flying"); + if ( num == ERR_MANIP_BUSY ) strcpy(text, "Already carrying something"); + if ( num == ERR_MANIP_NIL ) strcpy(text, "Nothing to grab"); + if ( num == ERR_MANIP_MOTOR ) strcpy(text, "Impossible when moving"); + if ( num == ERR_MANIP_OCC ) strcpy(text, "Place occupied"); + if ( num == ERR_MANIP_FRIEND ) strcpy(text, "No other robot"); + if ( num == ERR_MANIP_RADIO ) strcpy(text, "You can not carry a radioactive object"); + if ( num == ERR_MANIP_WATER ) strcpy(text, "You can not carry an object under water"); + if ( num == ERR_MANIP_EMPTY ) strcpy(text, "Nothing to drop"); + if ( num == ERR_BUILD_FLY ) strcpy(text, "Impossible when flying"); + if ( num == ERR_BUILD_WATER ) strcpy(text, "Impossible under water"); + if ( num == ERR_BUILD_ENERGY ) strcpy(text, "Not enough energy"); + if ( num == ERR_BUILD_METALAWAY ) strcpy(text, "Titanium too far away"); + if ( num == ERR_BUILD_METALNEAR ) strcpy(text, "Titanium too close"); + if ( num == ERR_BUILD_METALINEX ) strcpy(text, "No titanium around"); + if ( num == ERR_BUILD_FLAT ) strcpy(text, "Ground not flat enough"); + if ( num == ERR_BUILD_FLATLIT ) strcpy(text, "Flat ground not large enough"); + if ( num == ERR_BUILD_BUSY ) strcpy(text, "Place occupied"); + if ( num == ERR_BUILD_BASE ) strcpy(text, "Too close to space ship"); + if ( num == ERR_BUILD_NARROW ) strcpy(text, "Too close to a building"); + if ( num == ERR_BUILD_MOTOR ) strcpy(text, "Impossible when moving"); + if ( num == ERR_SEARCH_FLY ) strcpy(text, "Impossible when flying"); + if ( num == ERR_SEARCH_VEH ) strcpy(text, "Inappropriate bot"); + if ( num == ERR_SEARCH_MOTOR ) strcpy(text, "Impossible when moving"); + if ( num == ERR_TERRA_VEH ) strcpy(text, "Inappropriate bot"); + if ( num == ERR_TERRA_ENERGY ) strcpy(text, "Not enough energy"); + if ( num == ERR_TERRA_FLOOR ) strcpy(text, "Ground inappropriate"); + if ( num == ERR_TERRA_BUILDING ) strcpy(text, "Building too close"); + if ( num == ERR_TERRA_OBJECT ) strcpy(text, "Object too close"); + if ( num == ERR_RECOVER_VEH ) strcpy(text, "Inappropriate bot"); + if ( num == ERR_RECOVER_ENERGY ) strcpy(text, "Not enough energy"); + if ( num == ERR_RECOVER_NULL ) strcpy(text, "Nothing to recycle"); + if ( num == ERR_SHIELD_VEH ) strcpy(text, "Inappropriate bot"); + if ( num == ERR_SHIELD_ENERGY ) strcpy(text, "No more energy"); + if ( num == ERR_MOVE_IMPOSSIBLE ) strcpy(text, "Error in instruction move"); + if ( num == ERR_FIND_IMPOSSIBLE ) strcpy(text, "Object not found"); + if ( num == ERR_GOTO_IMPOSSIBLE ) strcpy(text, "Goto: inaccessible destination"); + if ( num == ERR_GOTO_ITER ) strcpy(text, "Goto: inaccessible destination"); + if ( num == ERR_GOTO_BUSY ) strcpy(text, "Goto: destination occupied"); + if ( num == ERR_FIRE_VEH ) strcpy(text, "Inappropriate bot"); + if ( num == ERR_FIRE_ENERGY ) strcpy(text, "Not enough energy"); + if ( num == ERR_FIRE_FLY ) strcpy(text, "Impossible when flying"); + if ( num == ERR_CONVERT_EMPTY ) strcpy(text, "No titanium ore to convert"); + if ( num == ERR_DERRICK_NULL ) strcpy(text, "No ore in the subsoil"); + if ( num == ERR_STATION_NULL ) strcpy(text, "No energy in the subsoil"); + if ( num == ERR_TOWER_POWER ) strcpy(text, "No power cell"); + if ( num == ERR_TOWER_ENERGY ) strcpy(text, "No more energy"); + if ( num == ERR_RESEARCH_POWER ) strcpy(text, "No power cell"); + if ( num == ERR_RESEARCH_ENERGY ) strcpy(text, "Not enough energy"); + if ( num == ERR_RESEARCH_TYPE ) strcpy(text, "Inappropriate cell type"); + if ( num == ERR_RESEARCH_ALREADY) strcpy(text, "Research program already performed"); + if ( num == ERR_ENERGY_NULL ) strcpy(text, "No energy in the subsoil"); + if ( num == ERR_ENERGY_LOW ) strcpy(text, "Not enough energy yet"); + if ( num == ERR_ENERGY_EMPTY ) strcpy(text, "No titanium to transform"); + if ( num == ERR_ENERGY_BAD ) strcpy(text, "Transforms only titanium"); + if ( num == ERR_BASE_DLOCK ) strcpy(text, "Doors blocked by a robot or another object "); + if ( num == ERR_BASE_DHUMAN ) strcpy(text, "You must get on the spaceship to take off "); + if ( num == ERR_LABO_NULL ) strcpy(text, "Nothing to analyze"); + if ( num == ERR_LABO_BAD ) strcpy(text, "Analyzes only organic matter"); + if ( num == ERR_LABO_ALREADY ) strcpy(text, "Analysis already performed"); + if ( num == ERR_NUCLEAR_NULL ) strcpy(text, "No energy in the subsoil"); + if ( num == ERR_NUCLEAR_LOW ) strcpy(text, "Not yet enough energy"); + if ( num == ERR_NUCLEAR_EMPTY ) strcpy(text, "No uranium to transform"); + if ( num == ERR_NUCLEAR_BAD ) strcpy(text, "Transforms only uranium"); + if ( num == ERR_FACTORY_NULL ) strcpy(text, "No titanium"); + if ( num == ERR_FACTORY_NEAR ) strcpy(text, "Object too close"); + if ( num == ERR_RESET_NEAR ) strcpy(text, "Place occupied"); + if ( num == ERR_INFO_NULL ) strcpy(text, "No information exchange post within range"); + if ( num == ERR_VEH_VIRUS ) strcpy(text, "Program infected by a virus"); + if ( num == ERR_BAT_VIRUS ) strcpy(text, "Infected by a virus, temporarily out of order"); + if ( num == ERR_VEH_POWER ) strcpy(text, "No power cell"); + if ( num == ERR_VEH_ENERGY ) strcpy(text, "No more energy"); + if ( num == ERR_FLAG_FLY ) strcpy(text, "Impossible when flying"); + if ( num == ERR_FLAG_WATER ) strcpy(text, "Impossible when swimming"); + if ( num == ERR_FLAG_MOTOR ) strcpy(text, "Impossible when moving"); + if ( num == ERR_FLAG_BUSY ) strcpy(text, "Impossible when carrying an object"); + if ( num == ERR_FLAG_CREATE ) strcpy(text, "Too many flags of this color (maximum 5)"); + if ( num == ERR_FLAG_PROXY ) strcpy(text, "Too close to an existing flag"); + if ( num == ERR_FLAG_DELETE ) strcpy(text, "No flag nearby"); + if ( num == ERR_MISSION_NOTERM ) strcpy(text, "The mission is not accomplished yet (press \\key help; for more details)"); + if ( num == ERR_DELETEMOBILE ) strcpy(text, "Bot destroyed"); + if ( num == ERR_DELETEBUILDING ) strcpy(text, "Building destroyed"); + if ( num == ERR_TOOMANY ) strcpy(text, "Can not create this, there are too many objects"); + if ( num == ERR_OBLIGATORYTOKEN ) strcpy(text, "\"%s\" missing in this exercise"); //** + if ( num == ERR_PROHIBITEDTOKEN ) strcpy(text, "Do not use in this exercise"); //** + + if ( num == INFO_BUILD ) strcpy(text, "Building completed"); + if ( num == INFO_CONVERT ) strcpy(text, "Titanium available"); + if ( num == INFO_RESEARCH ) strcpy(text, "Research program completed"); + if ( num == INFO_RESEARCHTANK ) strcpy(text, "Plans for tracked robots available "); + if ( num == INFO_RESEARCHFLY ) strcpy(text, "You can fly with the keys (\\key gup;) and (\\key gdown;)"); + if ( num == INFO_RESEARCHTHUMP ) strcpy(text, "Plans for thumper available"); + if ( num == INFO_RESEARCHCANON ) strcpy(text, "Plans for shooter available"); + if ( num == INFO_RESEARCHTOWER ) strcpy(text, "Plans for defense tower available"); + if ( num == INFO_RESEARCHPHAZER ) strcpy(text, "Plans for phazer shooter available"); + if ( num == INFO_RESEARCHSHIELD ) strcpy(text, "Plans for shielder available"); + if ( num == INFO_RESEARCHATOMIC ) strcpy(text, "Plans for nuclear power plant available"); + if ( num == INFO_FACTORY ) strcpy(text, "New bot available"); + if ( num == INFO_LABO ) strcpy(text, "Analysis performed"); + if ( num == INFO_ENERGY ) strcpy(text, "Power cell available"); + if ( num == INFO_NUCLEAR ) strcpy(text, "Nuclear power cell available"); + if ( num == INFO_FINDING ) strcpy(text, "You found a usable object"); + if ( num == INFO_MARKPOWER ) strcpy(text, "Found a site for power station"); + if ( num == INFO_MARKURANIUM ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_MARKSTONE ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_MARKKEYa ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_MARKKEYb ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_MARKKEYc ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_MARKKEYd ) strcpy(text, "Found a site for a derrick"); + if ( num == INFO_WIN ) strcpy(text, "<<< Well done, mission accomplished >>>"); + if ( num == INFO_LOST ) strcpy(text, "<<< Sorry, mission failed >>>"); + if ( num == INFO_LOSTq ) strcpy(text, "<<< Sorry, mission failed >>>"); + if ( num == INFO_WRITEOK ) strcpy(text, "Current mission saved"); + if ( num == INFO_DELETEPATH ) strcpy(text, "Checkpoint crossed"); + if ( num == INFO_DELETEMOTHER ) strcpy(text, "Alien Queen killed"); + if ( num == INFO_DELETEANT ) strcpy(text, "Ant fatally wounded"); + if ( num == INFO_DELETEBEE ) strcpy(text, "Wasp fatally wounded"); + if ( num == INFO_DELETEWORM ) strcpy(text, "Worm fatally wounded"); + if ( num == INFO_DELETESPIDER ) strcpy(text, "Spider fatally wounded"); + if ( num == INFO_BEGINSATCOM ) strcpy(text, "Press \\key help; to read instructions on your SatCom"); + } + + if ( type == RES_CBOT ) + { + strcpy(text, "Error"); + if ( num == TX_OPENPAR ) strcpy(text, "Opening bracket missing"); + if ( num == TX_CLOSEPAR ) strcpy(text, "Closing bracket missing "); + if ( num == TX_NOTBOOL ) strcpy(text, "The expression must return a boolean value"); + if ( num == TX_UNDEFVAR ) strcpy(text, "Variable not declared"); + if ( num == TX_BADLEFT ) strcpy(text, "Assignment impossible"); + if ( num == TX_ENDOF ) strcpy(text, "Semicolon terminator missing"); + if ( num == TX_OUTCASE ) strcpy(text, "Instruction ""case"" outside a block ""switch"""); + if ( num == TX_NOTERM ) strcpy(text, "Instructions after the final closing brace"); + if ( num == TX_CLOSEBLK ) strcpy(text, "End of block missing"); + if ( num == TX_ELSEWITHOUTIF ) strcpy(text, "Instruction ""else"" without corresponding ""if"" "); + if ( num == TX_OPENBLK ) strcpy(text, "Opening brace missing ");//début d'un bloc attendu? + if ( num == TX_BADTYPE ) strcpy(text, "Wrong type for the assignment"); + if ( num == TX_REDEFVAR ) strcpy(text, "A variable can not be declared twice"); + if ( num == TX_BAD2TYPE ) strcpy(text, "The types of the two operands are incompatible "); + if ( num == TX_UNDEFCALL ) strcpy(text, "Unknown function"); + if ( num == TX_MISDOTS ) strcpy(text, "Sign "" : "" missing"); + if ( num == TX_WHILE ) strcpy(text, "Keyword ""while"" missing"); + if ( num == TX_BREAK ) strcpy(text, "Instruction ""break"" outside a loop"); + if ( num == TX_LABEL ) strcpy(text, "A label must be followed by ""for"", ""while"", ""do"" or ""switch"""); + if ( num == TX_NOLABEL ) strcpy(text, "This label does not exist");// Cette étiquette n'existe pas + if ( num == TX_NOCASE ) strcpy(text, "Instruction ""case"" missing"); + if ( num == TX_BADNUM ) strcpy(text, "Number missing"); + if ( num == TX_VOID ) strcpy(text, "Void parameter"); + if ( num == TX_NOTYP ) strcpy(text, "Type declaration missing"); + if ( num == TX_NOVAR ) strcpy(text, "Variable name missing"); + if ( num == TX_NOFONC ) strcpy(text, "Function name missing"); + if ( num == TX_OVERPARAM ) strcpy(text, "Too many parameters"); + if ( num == TX_REDEF ) strcpy(text, "Function already exists"); + if ( num == TX_LOWPARAM ) strcpy(text, "Parameters missing "); + if ( num == TX_BADPARAM ) strcpy(text, "No function with this name accepts this kind of parameter"); + if ( num == TX_NUMPARAM ) strcpy(text, "No function with this name accepts this number of parameters"); + if ( num == TX_NOITEM ) strcpy(text, "This is not a member of this class"); + if ( num == TX_DOT ) strcpy(text, "This object is not a member of a class"); + if ( num == TX_NOCONST ) strcpy(text, "Appropriate constructor missing"); + if ( num == TX_REDEFCLASS ) strcpy(text, "This class already exists"); + if ( num == TX_CLBRK ) strcpy(text, """ ] "" missing"); + if ( num == TX_RESERVED ) strcpy(text, "Reserved keyword of CBOT language"); + if ( num == TX_BADNEW ) strcpy(text, "Bad argument for ""new"""); + if ( num == TX_OPBRK ) strcpy(text, """ [ "" expected"); + if ( num == TX_BADSTRING ) strcpy(text, "String missing"); + if ( num == TX_BADINDEX ) strcpy(text, "Incorrect index type"); + if ( num == TX_PRIVATE ) strcpy(text, "Private element"); + if ( num == TX_NOPUBLIC ) strcpy(text, "Public required"); + if ( num == TX_DIVZERO ) strcpy(text, "Dividing by zero"); + if ( num == TX_NOTINIT ) strcpy(text, "Variable not initialized"); + if ( num == TX_BADTHROW ) strcpy(text, "Negative value rejected by ""throw""");//C'est quoi, ça? + if ( num == TX_NORETVAL ) strcpy(text, "The function returned no value "); + if ( num == TX_NORUN ) strcpy(text, "No function running"); + if ( num == TX_NOCALL ) strcpy(text, "Calling an unknown function"); + if ( num == TX_NOCLASS ) strcpy(text, "This class does not exist"); + if ( num == TX_NULLPT ) strcpy(text, "Unknown Object"); + if ( num == TX_OPNAN ) strcpy(text, "Operation impossible with value ""nan"""); + if ( num == TX_OUTARRAY ) strcpy(text, "Access beyond array limit"); + if ( num == TX_STACKOVER ) strcpy(text, "Stack overflow"); + if ( num == TX_DELETEDPT ) strcpy(text, "Illegal object"); + if ( num == TX_FILEOPEN ) strcpy(text, "Can't open file"); + if ( num == TX_NOTOPEN ) strcpy(text, "File not open"); + if ( num == TX_ERRREAD ) strcpy(text, "Read error"); + if ( num == TX_ERRWRITE ) strcpy(text, "Write error"); + } + + if ( type == RES_KEY ) + { + if ( num == 0 ) strcpy(text, "< none >"); + if ( num == VK_LEFT ) strcpy(text, "Arrow left"); + if ( num == VK_RIGHT ) strcpy(text, "Arrow right"); + if ( num == VK_UP ) strcpy(text, "Arrow up"); + if ( num == VK_DOWN ) strcpy(text, "Arrow down"); + if ( num == VK_CANCEL ) strcpy(text, "Control-break"); + if ( num == VK_BACK ) strcpy(text, "<--"); + if ( num == VK_TAB ) strcpy(text, "Tab"); + if ( num == VK_CLEAR ) strcpy(text, "Clear"); + if ( num == VK_RETURN ) strcpy(text, "Enter"); + if ( num == VK_SHIFT ) strcpy(text, "Shift"); + if ( num == VK_CONTROL ) strcpy(text, "Ctrl"); + if ( num == VK_MENU ) strcpy(text, "Alt"); + if ( num == VK_PAUSE ) strcpy(text, "Pause"); + if ( num == VK_CAPITAL ) strcpy(text, "Caps Lock"); + if ( num == VK_ESCAPE ) strcpy(text, "Esc"); + if ( num == VK_SPACE ) strcpy(text, "Space"); + if ( num == VK_PRIOR ) strcpy(text, "Page Up"); + if ( num == VK_NEXT ) strcpy(text, "Page Down"); + if ( num == VK_END ) strcpy(text, "End"); + if ( num == VK_HOME ) strcpy(text, "Home"); + if ( num == VK_SELECT ) strcpy(text, "Select"); + if ( num == VK_EXECUTE ) strcpy(text, "Execute"); + if ( num == VK_SNAPSHOT ) strcpy(text, "Print Scrn"); + if ( num == VK_INSERT ) strcpy(text, "Insert"); + if ( num == VK_DELETE ) strcpy(text, "Delete"); + if ( num == VK_HELP ) strcpy(text, "Help"); + if ( num == VK_LWIN ) strcpy(text, "Left Windows"); + if ( num == VK_RWIN ) strcpy(text, "Right Windows"); + if ( num == VK_APPS ) strcpy(text, "Application key"); + if ( num == VK_NUMPAD0 ) strcpy(text, "NumPad 0"); + if ( num == VK_NUMPAD1 ) strcpy(text, "NumPad 1"); + if ( num == VK_NUMPAD2 ) strcpy(text, "NumPad 2"); + if ( num == VK_NUMPAD3 ) strcpy(text, "NumPad 3"); + if ( num == VK_NUMPAD4 ) strcpy(text, "NumPad 4"); + if ( num == VK_NUMPAD5 ) strcpy(text, "NumPad 5"); + if ( num == VK_NUMPAD6 ) strcpy(text, "NumPad 6"); + if ( num == VK_NUMPAD7 ) strcpy(text, "NumPad 7"); + if ( num == VK_NUMPAD8 ) strcpy(text, "NumPad 8"); + if ( num == VK_NUMPAD9 ) strcpy(text, "NumPad 9"); + if ( num == VK_MULTIPLY ) strcpy(text, "NumPad *"); + if ( num == VK_ADD ) strcpy(text, "NumPad +"); + if ( num == VK_SEPARATOR ) strcpy(text, "NumPad sep"); + if ( num == VK_SUBTRACT ) strcpy(text, "NumPad -"); + if ( num == VK_DECIMAL ) strcpy(text, "NumPad ."); + if ( num == VK_DIVIDE ) strcpy(text, "NumPad /"); + if ( num == VK_F1 ) strcpy(text, "F1"); + if ( num == VK_F2 ) strcpy(text, "F2"); + if ( num == VK_F3 ) strcpy(text, "F3"); + if ( num == VK_F4 ) strcpy(text, "F4"); + if ( num == VK_F5 ) strcpy(text, "F5"); + if ( num == VK_F6 ) strcpy(text, "F6"); + if ( num == VK_F7 ) strcpy(text, "F7"); + if ( num == VK_F8 ) strcpy(text, "F8"); + if ( num == VK_F9 ) strcpy(text, "F9"); + if ( num == VK_F10 ) strcpy(text, "F10"); + if ( num == VK_F11 ) strcpy(text, "F11"); + if ( num == VK_F12 ) strcpy(text, "F12"); + if ( num == VK_F13 ) strcpy(text, "F13"); + if ( num == VK_F14 ) strcpy(text, "F14"); + if ( num == VK_F15 ) strcpy(text, "F15"); + if ( num == VK_F16 ) strcpy(text, "F16"); + if ( num == VK_F17 ) strcpy(text, "F17"); + if ( num == VK_F18 ) strcpy(text, "F18"); + if ( num == VK_F19 ) strcpy(text, "F19"); + if ( num == VK_F20 ) strcpy(text, "F20"); + if ( num == VK_NUMLOCK ) strcpy(text, "Num Lock"); + if ( num == VK_SCROLL ) strcpy(text, "Scroll"); + if ( num == VK_ATTN ) strcpy(text, "Attn"); + if ( num == VK_CRSEL ) strcpy(text, "CrSel"); + if ( num == VK_EXSEL ) strcpy(text, "ExSel"); + if ( num == VK_EREOF ) strcpy(text, "Erase EOF"); + if ( num == VK_PLAY ) strcpy(text, "Play"); + if ( num == VK_ZOOM ) strcpy(text, "Zoom"); + if ( num == VK_PA1 ) strcpy(text, "PA1"); + if ( num == VK_OEM_CLEAR ) strcpy(text, "Clear"); + if ( num == VK_BUTTON1 ) strcpy(text, "Button 1"); + if ( num == VK_BUTTON2 ) strcpy(text, "Button 2"); + if ( num == VK_BUTTON3 ) strcpy(text, "Button 3"); + if ( num == VK_BUTTON4 ) strcpy(text, "Button 4"); + if ( num == VK_BUTTON5 ) strcpy(text, "Button 5"); + if ( num == VK_BUTTON6 ) strcpy(text, "Button 6"); + if ( num == VK_BUTTON7 ) strcpy(text, "Button 7"); + if ( num == VK_BUTTON8 ) strcpy(text, "Button 8"); + if ( num == VK_BUTTON9 ) strcpy(text, "Button 9"); + if ( num == VK_BUTTON10 ) strcpy(text, "Button 10"); + if ( num == VK_BUTTON11 ) strcpy(text, "Button 11"); + if ( num == VK_BUTTON12 ) strcpy(text, "Button 12"); + if ( num == VK_BUTTON13 ) strcpy(text, "Button 13"); + if ( num == VK_BUTTON14 ) strcpy(text, "Button 14"); + if ( num == VK_BUTTON15 ) strcpy(text, "Button 15"); + if ( num == VK_BUTTON16 ) strcpy(text, "Button 16"); + if ( num == VK_BUTTON17 ) strcpy(text, "Button 17"); + if ( num == VK_BUTTON18 ) strcpy(text, "Button 18"); + if ( num == VK_BUTTON19 ) strcpy(text, "Button 19"); + if ( num == VK_BUTTON20 ) strcpy(text, "Button 20"); + if ( num == VK_BUTTON21 ) strcpy(text, "Button 21"); + if ( num == VK_BUTTON22 ) strcpy(text, "Button 22"); + if ( num == VK_BUTTON23 ) strcpy(text, "Button 23"); + if ( num == VK_BUTTON24 ) strcpy(text, "Button 24"); + if ( num == VK_BUTTON25 ) strcpy(text, "Button 25"); + if ( num == VK_BUTTON26 ) strcpy(text, "Button 26"); + if ( num == VK_BUTTON27 ) strcpy(text, "Button 27"); + if ( num == VK_BUTTON28 ) strcpy(text, "Button 28"); + if ( num == VK_BUTTON29 ) strcpy(text, "Button 29"); + if ( num == VK_BUTTON30 ) strcpy(text, "Button 30"); + if ( num == VK_BUTTON31 ) strcpy(text, "Button 31"); + if ( num == VK_BUTTON32 ) strcpy(text, "Button 32"); + if ( num == VK_WHEELUP ) strcpy(text, "Wheel up"); + if ( num == VK_WHEELDOWN ) strcpy(text, "Wheel down"); + } +#endif + +#if _FRENCH + if ( type == RES_TEXT ) + { + #if _FULL + if ( num == RT_VERSION_ID ) strcpy(text, "1.18 /f"); + #endif + #if _NET + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A 1.18"); + #endif + #if _SCHOOL & _EDU + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen EDU 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A EDU 1.18"); + #endif + #endif + #if _SCHOOL & _PERSO + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen PERSO 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A PERSO 1.18"); + #endif + #endif + #if _SCHOOL & _CEEBOTDEMO + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen DEMO 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A DEMO 1.18"); + #endif + #endif + #if _DEMO + if ( num == RT_VERSION_ID ) strcpy(text, "Demo 1.18 /f"); + #endif + if ( num == RT_DISINFO_TITLE ) strcpy(text, "SatCom"); + if ( num == RT_WINDOW_MAXIMIZED ) strcpy(text, "Taille maximale"); + if ( num == RT_WINDOW_MINIMIZED ) strcpy(text, "Taille réduite"); + if ( num == RT_WINDOW_STANDARD ) strcpy(text, "Taille normale"); + if ( num == RT_WINDOW_CLOSE ) strcpy(text, "Fermer"); + + if ( num == RT_STUDIO_TITLE ) strcpy(text, "Edition du programme"); + if ( num == RT_SCRIPT_NEW ) strcpy(text, "Nouveau"); + if ( num == RT_NAME_DEFAULT ) strcpy(text, "Joueur"); + if ( num == RT_IO_NEW ) strcpy(text, "Nouveau ..."); + if ( num == RT_KEY_OR ) strcpy(text, " ou "); + +#if _NEWLOOK + if ( num == RT_TITLE_BASE ) strcpy(text, "CeeBot"); + if ( num == RT_TITLE_INIT ) strcpy(text, "CeeBot"); +#else + if ( num == RT_TITLE_BASE ) strcpy(text, "COLOBOT"); + if ( num == RT_TITLE_INIT ) strcpy(text, "COLOBOT"); +#endif + if ( num == RT_TITLE_TRAINER ) strcpy(text, "Programmation"); + if ( num == RT_TITLE_DEFI ) strcpy(text, "Défis"); + if ( num == RT_TITLE_MISSION ) strcpy(text, "Missions"); + if ( num == RT_TITLE_FREE ) strcpy(text, "Jeu libre"); + if ( num == RT_TITLE_TEEN ) strcpy(text, "Jeu libre"); + if ( num == RT_TITLE_USER ) strcpy(text, "Niveaux supplémentaires"); + if ( num == RT_TITLE_PROTO ) strcpy(text, "Prototypes"); + if ( num == RT_TITLE_SETUP ) strcpy(text, "Options"); + if ( num == RT_TITLE_NAME ) strcpy(text, "Nom du joueur"); + if ( num == RT_TITLE_PERSO ) strcpy(text, "Personnalisation de votre apparence"); + if ( num == RT_TITLE_WRITE ) strcpy(text, "Enregistrement de la mission en cours"); + if ( num == RT_TITLE_READ ) strcpy(text, "Chargement d'une mission enregistrée"); + + if ( num == RT_PLAY_CHAPt ) strcpy(text, " Liste des chapitres :"); + if ( num == RT_PLAY_CHAPd ) strcpy(text, " Liste des chapitres :"); + if ( num == RT_PLAY_CHAPm ) strcpy(text, " Liste des planètes :"); + if ( num == RT_PLAY_CHAPf ) strcpy(text, " Liste des planètes :"); + if ( num == RT_PLAY_CHAPu ) strcpy(text, " Niveaux supplémentaires :"); + if ( num == RT_PLAY_CHAPp ) strcpy(text, " Liste des planètes :"); + if ( num == RT_PLAY_CHAPte ) strcpy(text, " Liste des chapitres :"); + if ( num == RT_PLAY_LISTt ) strcpy(text, " Liste des exercices du chapitre :"); + if ( num == RT_PLAY_LISTd ) strcpy(text, " Liste des défis du chapitre :"); + if ( num == RT_PLAY_LISTm ) strcpy(text, " Liste des missions du chapitre :"); + if ( num == RT_PLAY_LISTf ) strcpy(text, " Liste des jeux libres du chapitre :"); + if ( num == RT_PLAY_LISTu ) strcpy(text, " Missions du niveau :"); + if ( num == RT_PLAY_LISTp ) strcpy(text, " Liste des prototypes du chapitre :"); + if ( num == RT_PLAY_LISTk ) strcpy(text, " Liste des jeux libres du chapitre :"); + if ( num == RT_PLAY_RESUME ) strcpy(text, " Résumé :"); + + if ( num == RT_SETUP_DEVICE ) strcpy(text, " Pilotes :"); + if ( num == RT_SETUP_MODE ) strcpy(text, " Résolutions :"); + if ( num == RT_SETUP_KEY1 ) strcpy(text, "1) Cliquez d'abord sur la touche à redéfinir."); + if ( num == RT_SETUP_KEY2 ) strcpy(text, "2) Appuyez ensuite sur la nouvelle touche souhaitée."); + + if ( num == RT_PERSO_FACE ) strcpy(text, "Type de visage :"); + if ( num == RT_PERSO_GLASSES ) strcpy(text, "Lunettes :"); + if ( num == RT_PERSO_HAIR ) strcpy(text, "Couleur des cheveux :"); + if ( num == RT_PERSO_COMBI ) strcpy(text, "Couleur de la combinaison :"); + if ( num == RT_PERSO_BAND ) strcpy(text, "Couleur des bandes :"); + +#if _NEWLOOK + if ( num == RT_DIALOG_TITLE ) strcpy(text, "CeeBot"); + if ( num == RT_DIALOG_QUIT ) strcpy(text, "Voulez-vous quitter CeeBot ?"); + if ( num == RT_DIALOG_YESQUIT ) strcpy(text, "Quitter\\Quitter CeeBot"); +#else + if ( num == RT_DIALOG_TITLE ) strcpy(text, "COLOBOT"); + if ( num == RT_DIALOG_QUIT ) strcpy(text, "Voulez-vous quitter COLOBOT ?"); + if ( num == RT_DIALOG_YESQUIT ) strcpy(text, "Quitter\\Quitter COLOBOT"); +#endif + if ( num == RT_DIALOG_ABORT ) strcpy(text, "Quitter la mission ?"); + if ( num == RT_DIALOG_YES ) strcpy(text, "Abandonner\\Abandonner la mission en cours"); + if ( num == RT_DIALOG_NO ) strcpy(text, "Continuer\\Continuer la mission en cours"); + if ( num == RT_DIALOG_NOQUIT ) strcpy(text, "Continuer\\Continuer de jouer"); + if ( num == RT_DIALOG_DELOBJ ) strcpy(text, "Voulez-vous vraiment détruire le bâtiment sélectionné ?"); + if ( num == RT_DIALOG_DELGAME ) strcpy(text, "Voulez-vous détruire les sauvegardes de %s ?"); + if ( num == RT_DIALOG_YESDEL ) strcpy(text, "Détruire"); + if ( num == RT_DIALOG_NODEL ) strcpy(text, "Annuler"); + if ( num == RT_DIALOG_LOADING ) strcpy(text, "CHARGEMENT"); + + if ( num == RT_STUDIO_LISTTT ) strcpy(text, "Aide sur le mot-clé (\\key cbot;)"); + if ( num == RT_STUDIO_COMPOK ) strcpy(text, "Compilation ok (0 erreur)"); + if ( num == RT_STUDIO_PROGSTOP ) strcpy(text, "Programme terminé"); + + if ( num == RT_SATCOM_LIST ) strcpy(text, "\\b;Listes des objets\n"); + if ( num == RT_SATCOM_BOT ) strcpy(text, "\\b;Listes des robots\n"); + if ( num == RT_SATCOM_BUILDING ) strcpy(text, "\\b;Listes des bâtiments\n"); + if ( num == RT_SATCOM_FRET ) strcpy(text, "\\b;Listes des objets transportables\n"); + if ( num == RT_SATCOM_ALIEN ) strcpy(text, "\\b;Listes des ennemis\n"); + if ( num == RT_SATCOM_NULL ) strcpy(text, "\\c; (aucun)\\n;\n"); + if ( num == RT_SATCOM_ERROR1 ) strcpy(text, "\\b;Erreur\n"); + if ( num == RT_SATCOM_ERROR2 ) strcpy(text, "Liste non disponible sans \\l;radar\\u object\\radar; !\n"); + + if ( num == RT_IO_OPEN ) strcpy(text, "Ouvrir"); + if ( num == RT_IO_SAVE ) strcpy(text, "Enregistrer"); + if ( num == RT_IO_LIST ) strcpy(text, "Dossier: %s"); + if ( num == RT_IO_NAME ) strcpy(text, "Nom:"); + if ( num == RT_IO_DIR ) strcpy(text, "Dans:"); + if ( num == RT_IO_PRIVATE ) strcpy(text, "Privé\\Dossier privé"); + if ( num == RT_IO_PUBLIC ) strcpy(text, "Public\\Dossier commun à tous les joueurs"); + + if ( num == RT_GENERIC_DEV1 ) strcpy(text, "Développé par :"); + if ( num == RT_GENERIC_DEV2 ) strcpy(text, "www.epsitec.com"); +#if _SCHOOL + if ( num == RT_GENERIC_EDIT1 ) strcpy(text, " "); + if ( num == RT_GENERIC_EDIT2 ) strcpy(text, " "); +#else + //?if ( num == RT_GENERIC_EDIT1 ) strcpy(text, "Version française éditée par :"); + //?if ( num == RT_GENERIC_EDIT2 ) strcpy(text, "www.alsyd.com"); + if ( num == RT_GENERIC_EDIT1 ) strcpy(text, " "); + if ( num == RT_GENERIC_EDIT2 ) strcpy(text, " "); +#endif + + if ( num == RT_INTERFACE_REC ) strcpy(text, "Enregistreur"); + } + + if ( type == RES_EVENT ) + { + if ( num == EVENT_BUTTON_OK ) strcpy(text, "D'accord"); + if ( num == EVENT_BUTTON_CANCEL ) strcpy(text, "Annuler"); + if ( num == EVENT_BUTTON_NEXT ) strcpy(text, "Suivant"); + if ( num == EVENT_BUTTON_PREV ) strcpy(text, "Précédent"); + if ( num == EVENT_BUTTON_QUIT ) strcpy(text, "Menu (\\key quit;)"); + + if ( num == EVENT_DIALOG_OK ) strcpy(text, "D'accord"); + if ( num == EVENT_DIALOG_CANCEL ) strcpy(text, "Annuler"); + + if ( num == EVENT_INTERFACE_TRAINER) strcpy(text, "Programmation\\Exercices de programmation"); + if ( num == EVENT_INTERFACE_DEFI ) strcpy(text, "Défis\\Défis de programmation"); + if ( num == EVENT_INTERFACE_MISSION) strcpy(text, "Missions\\La grande aventure"); + if ( num == EVENT_INTERFACE_FREE ) strcpy(text, "Jeu libre\\Jeu libre sans but précis"); + if ( num == EVENT_INTERFACE_TEEN ) strcpy(text, "Jeu libre\\Jeu libre sans but précis"); + if ( num == EVENT_INTERFACE_USER ) strcpy(text, "Suppl.\\Niveaux supplémentaires"); + if ( num == EVENT_INTERFACE_PROTO ) strcpy(text, "Proto\\Prototypes en cours d'élaboration"); + if ( num == EVENT_INTERFACE_NAME ) strcpy(text, "Autre joueur\\Choix du nom du joueur"); + if ( num == EVENT_INTERFACE_SETUP ) strcpy(text, "Options\\Réglages"); + if ( num == EVENT_INTERFACE_AGAIN ) strcpy(text, "Recommencer\\Recommencer la mission au début"); + if ( num == EVENT_INTERFACE_WRITE ) strcpy(text, "Enregistrer\\Enregistrer la mission en cours"); + if ( num == EVENT_INTERFACE_READ ) strcpy(text, "Charger\\Charger une mission enregistrée"); +#if _NEWLOOK + if ( num == EVENT_INTERFACE_ABORT ) strcpy(text, "\\Retourner dans CeeBot"); + if ( num == EVENT_INTERFACE_QUIT ) strcpy(text, "Quitter\\Quitter CeeBot"); +#else + if ( num == EVENT_INTERFACE_ABORT ) strcpy(text, "\\Retourner dans COLOBOT"); + if ( num == EVENT_INTERFACE_QUIT ) strcpy(text, "Quitter\\Quitter COLOBOT"); +#endif + if ( num == EVENT_INTERFACE_BACK ) strcpy(text, "<< Retour \\Retour au niveau précédent"); + if ( num == EVENT_INTERFACE_PLAY ) strcpy(text, "Jouer ...\\Démarrer l'action"); + if ( num == EVENT_INTERFACE_SETUPd ) strcpy(text, "Affichage\\Pilote et résolution d'affichage"); + if ( num == EVENT_INTERFACE_SETUPg ) strcpy(text, "Graphique\\Options graphiques"); + if ( num == EVENT_INTERFACE_SETUPp ) strcpy(text, "Jeu\\Options de jouabilité"); + if ( num == EVENT_INTERFACE_SETUPc ) strcpy(text, "Commandes\\Touches du clavier"); + if ( num == EVENT_INTERFACE_SETUPs ) strcpy(text, "Son\\Volumes bruitages & musiques"); + if ( num == EVENT_INTERFACE_DEVICE ) strcpy(text, "Unité"); + if ( num == EVENT_INTERFACE_RESOL ) strcpy(text, "Résolution"); + if ( num == EVENT_INTERFACE_FULL ) strcpy(text, "Plein écran\\Plein écran ou fenêtré"); + if ( num == EVENT_INTERFACE_APPLY ) strcpy(text, "Appliquer les changements\\Active les changements effectués"); + + if ( num == EVENT_INTERFACE_TOTO ) strcpy(text, "Robbie\\Votre assistant"); + if ( num == EVENT_INTERFACE_SHADOW ) strcpy(text, "Ombres\\Ombres projetées au sol"); + if ( num == EVENT_INTERFACE_GROUND ) strcpy(text, "Marques sur le sol\\Marques dessinées sur le sol"); + if ( num == EVENT_INTERFACE_DIRTY ) strcpy(text, "Salissures\\Salissures des robots et bâtiments"); + if ( num == EVENT_INTERFACE_FOG ) strcpy(text, "Brouillard\\Nappes de brouillard"); + if ( num == EVENT_INTERFACE_LENS ) strcpy(text, "Rayons du soleil\\Rayons selon l'orientation"); + if ( num == EVENT_INTERFACE_SKY ) strcpy(text, "Ciel\\Ciel et nuages"); + if ( num == EVENT_INTERFACE_PLANET ) strcpy(text, "Planètes et étoiles\\Motifs mobiles dans le ciel"); + if ( num == EVENT_INTERFACE_LIGHT ) strcpy(text, "Lumières dynamiques\\Eclairages mobiles"); + if ( num == EVENT_INTERFACE_PARTI ) strcpy(text, "Quantité de particules\\Explosions, poussières, reflets, etc."); + if ( num == EVENT_INTERFACE_CLIP ) strcpy(text, "Profondeur de champ\\Distance de vue maximale"); + if ( num == EVENT_INTERFACE_DETAIL ) strcpy(text, "Détails des objets\\Qualité des objets en 3D"); + if ( num == EVENT_INTERFACE_TEXTURE) strcpy(text, "Qualité des textures\\Qualité des images"); + if ( num == EVENT_INTERFACE_GADGET ) strcpy(text, "Nb d'objets décoratifs\\Qualité d'objets non indispensables"); + if ( num == EVENT_INTERFACE_RAIN ) strcpy(text, "Particules dans l'interface\\Pluie de particules"); + if ( num == EVENT_INTERFACE_GLINT ) strcpy(text, "Reflets sur les boutons\\Boutons brillants"); + if ( num == EVENT_INTERFACE_TOOLTIP) strcpy(text, "Bulles d'aide\\Bulles explicatives"); + if ( num == EVENT_INTERFACE_MOVIES ) strcpy(text, "Séquences cinématiques\\Films avant ou après une mission"); + if ( num == EVENT_INTERFACE_NICERST) strcpy(text, "Retour animé\\Retour animé dans les exercices"); + if ( num == EVENT_INTERFACE_HIMSELF) strcpy(text, "Dégâts à soi-même\\Vos tirs infligent des dommages à vos unités"); + if ( num == EVENT_INTERFACE_SCROLL ) strcpy(text, "Défilement dans les bords\\Défilement lorsque la souris touches les bords gauche ou droite"); + if ( num == EVENT_INTERFACE_INVERTX) strcpy(text, "Inversion souris X\\Inversion de la rotation lorsque la souris touche un bord"); + if ( num == EVENT_INTERFACE_INVERTY) strcpy(text, "Inversion souris Y\\Inversion de la rotation lorsque la souris touche un bord"); + if ( num == EVENT_INTERFACE_EFFECT ) strcpy(text, "Secousses lors d'explosions\\L'écran vibre lors d'une explosion"); + if ( num == EVENT_INTERFACE_MOUSE ) strcpy(text, "Souris ombrée\\Jolie souris avec une ombre"); + if ( num == EVENT_INTERFACE_EDITMODE) strcpy(text, "Indentation automatique\\Pendant l'édition d'un programme"); + if ( num == EVENT_INTERFACE_EDITVALUE)strcpy(text, "Grande indentation\\Indente avec 2 ou 4 espaces"); + if ( num == EVENT_INTERFACE_SOLUCE4) strcpy(text, "Accès aux solutions\\Programme \"4: Solution\" dans les exercices"); + + if ( num == EVENT_INTERFACE_KDEF ) strcpy(text, "Tout réinitialiser\\Remet toutes les touches standards"); + if ( num == EVENT_INTERFACE_KLEFT ) strcpy(text, "Tourner à gauche\\Moteur à gauche"); + if ( num == EVENT_INTERFACE_KRIGHT ) strcpy(text, "Tourner à droite\\Moteur à droite"); + if ( num == EVENT_INTERFACE_KUP ) strcpy(text, "Avancer\\Moteur en avant"); + if ( num == EVENT_INTERFACE_KDOWN ) strcpy(text, "Reculer\\Moteur en arrière"); + if ( num == EVENT_INTERFACE_KGUP ) strcpy(text, "Monter\\Augmenter la puissance du réacteur"); + if ( num == EVENT_INTERFACE_KGDOWN ) strcpy(text, "Descendre\\Diminuer la puissance du réacteur"); + if ( num == EVENT_INTERFACE_KCAMERA) strcpy(text, "Changement de caméra\\Autre de point de vue"); + if ( num == EVENT_INTERFACE_KDESEL ) strcpy(text, "Sélection précédente\\Sélectionne l'objet précédent"); + if ( num == EVENT_INTERFACE_KACTION) strcpy(text, "Action standard\\Action du bouton avec le cadre rouge"); + if ( num == EVENT_INTERFACE_KNEAR ) strcpy(text, "Caméra plus proche\\Avance la caméra"); + if ( num == EVENT_INTERFACE_KAWAY ) strcpy(text, "Caméra plus loin\\Recule la caméra"); + if ( num == EVENT_INTERFACE_KNEXT ) strcpy(text, "Sélectionner l'objet suivant\\Sélectionner l'objet suivant"); + if ( num == EVENT_INTERFACE_KHUMAN ) strcpy(text, "Sélectionner le cosmonaute\\Sélectionner le cosmonaute"); + if ( num == EVENT_INTERFACE_KQUIT ) strcpy(text, "Quitter la mission en cours\\Terminer un exercice ou une mssion"); + if ( num == EVENT_INTERFACE_KHELP ) strcpy(text, "Instructions mission\\Marche à suivre"); + if ( num == EVENT_INTERFACE_KPROG ) strcpy(text, "Instructions programmation\\Explication sur la programmation"); + if ( num == EVENT_INTERFACE_KCBOT ) strcpy(text, "Instructions mot-clé\\Explication sur le mot-clé"); + if ( num == EVENT_INTERFACE_KVISIT ) strcpy(text, "Montrer le lieu d'un message\\Montrer le lieu du dernier message"); + if ( num == EVENT_INTERFACE_KSPEED10) strcpy(text, "Vitesse 1.0x\\Vitesse normale"); + if ( num == EVENT_INTERFACE_KSPEED15) strcpy(text, "Vitesse 1.5x\\Une fois et demi plus rapide"); + if ( num == EVENT_INTERFACE_KSPEED20) strcpy(text, "Vitesse 2.0x\\Deux fois plus rapide"); + if ( num == EVENT_INTERFACE_KSPEED30) strcpy(text, "Vitesse 3.0x\\Trois fois plus rapide"); + + if ( num == EVENT_INTERFACE_VOLSOUND) strcpy(text, "Bruitages :\\Volume des moteurs, voix, etc."); + if ( num == EVENT_INTERFACE_VOLMUSIC) strcpy(text, "Fond sonore :\\Volume des pistes audio du CD"); + if ( num == EVENT_INTERFACE_SOUND3D) strcpy(text, "Bruitages 3D\\Positionnement sonore dans l'espace"); + + if ( num == EVENT_INTERFACE_MIN ) strcpy(text, "Mini\\Qualité minimale (+ rapide)"); + if ( num == EVENT_INTERFACE_NORM ) strcpy(text, "Normal\\Qualité standard"); + if ( num == EVENT_INTERFACE_MAX ) strcpy(text, "Maxi\\Haute qualité (+ lent)"); + + if ( num == EVENT_INTERFACE_SILENT ) strcpy(text, "Silencieux\\Totalement silencieux"); + if ( num == EVENT_INTERFACE_NOISY ) strcpy(text, "Normal\\Niveaux normaux"); + + if ( num == EVENT_INTERFACE_JOYSTICK) strcpy(text, "Utilise un joystick\\Joystick ou clavier"); + if ( num == EVENT_INTERFACE_SOLUCE ) strcpy(text, "Accès à la solution\\Donne la solution"); + + if ( num == EVENT_INTERFACE_NEDIT ) strcpy(text, "\\Nom du joueur à créer"); + if ( num == EVENT_INTERFACE_NOK ) strcpy(text, "D'accord\\Choisir le joueur"); + if ( num == EVENT_INTERFACE_NCANCEL) strcpy(text, "Annuler\\Conserver le joueur actuel"); + if ( num == EVENT_INTERFACE_NDELETE) strcpy(text, "Supprimer le joueur\\Supprimer le joueur de la liste"); + if ( num == EVENT_INTERFACE_NLABEL ) strcpy(text, "Nom du joueur"); + + if ( num == EVENT_INTERFACE_IOWRITE) strcpy(text, "Enregistrer\\Enregistrer la mission en cours"); + if ( num == EVENT_INTERFACE_IOREAD ) strcpy(text, "Charger\\Charger la mission sélectionnée"); + if ( num == EVENT_INTERFACE_IOLIST ) strcpy(text, "Liste des missions enregistrées"); + if ( num == EVENT_INTERFACE_IOLABEL) strcpy(text, "Nom du fichier :"); + if ( num == EVENT_INTERFACE_IONAME ) strcpy(text, "Nom de la mission"); + if ( num == EVENT_INTERFACE_IOIMAGE) strcpy(text, "Vue de la mission"); + if ( num == EVENT_INTERFACE_IODELETE) strcpy(text, "Supprimer\\Supprime l'enregistrement sélectionné"); + + if ( num == EVENT_INTERFACE_PERSO ) strcpy(text, "Aspect\\Choisir votre aspect"); + if ( num == EVENT_INTERFACE_POK ) strcpy(text, "D'accord"); + if ( num == EVENT_INTERFACE_PCANCEL) strcpy(text, "Annuler"); + if ( num == EVENT_INTERFACE_PDEF ) strcpy(text, "Standard\\Remet les couleurs standards"); + if ( num == EVENT_INTERFACE_PHEAD ) strcpy(text, "Tête\\Visage et cheveux"); + if ( num == EVENT_INTERFACE_PBODY ) strcpy(text, "Corps\\Combinaison"); + if ( num == EVENT_INTERFACE_PLROT ) strcpy(text, "\\Rotation à gauche"); + if ( num == EVENT_INTERFACE_PRROT ) strcpy(text, "\\Rotation à droite"); + if ( num == EVENT_INTERFACE_PCRa ) strcpy(text, "Rouge"); + if ( num == EVENT_INTERFACE_PCGa ) strcpy(text, "Vert"); + if ( num == EVENT_INTERFACE_PCBa ) strcpy(text, "Bleu"); + if ( num == EVENT_INTERFACE_PCRb ) strcpy(text, "Rouge"); + if ( num == EVENT_INTERFACE_PCGb ) strcpy(text, "Vert"); + if ( num == EVENT_INTERFACE_PCBb ) strcpy(text, "Bleu"); + if ( num == EVENT_INTERFACE_PFACE1 ) strcpy(text, "\\Visage 1"); + if ( num == EVENT_INTERFACE_PFACE2 ) strcpy(text, "\\Visage 4"); + if ( num == EVENT_INTERFACE_PFACE3 ) strcpy(text, "\\Visage 3"); + if ( num == EVENT_INTERFACE_PFACE4 ) strcpy(text, "\\Visage 2"); + if ( num == EVENT_INTERFACE_PGLASS0) strcpy(text, "\\Pas de lunettes"); + if ( num == EVENT_INTERFACE_PGLASS1) strcpy(text, "\\Lunettes 1"); + if ( num == EVENT_INTERFACE_PGLASS2) strcpy(text, "\\Lunettes 2"); + if ( num == EVENT_INTERFACE_PGLASS3) strcpy(text, "\\Lunettes 3"); + if ( num == EVENT_INTERFACE_PGLASS4) strcpy(text, "\\Lunettes 4"); + if ( num == EVENT_INTERFACE_PGLASS5) strcpy(text, "\\Lunettes 5"); + + if ( num == EVENT_OBJECT_DESELECT ) strcpy(text, "Sélection précédente (\\key desel;)"); + if ( num == EVENT_OBJECT_LEFT ) strcpy(text, "Tourne à gauche (\\key left;)"); + if ( num == EVENT_OBJECT_RIGHT ) strcpy(text, "Tourne à droite (\\key right;)"); + if ( num == EVENT_OBJECT_UP ) strcpy(text, "Avance (\\key up;)"); + if ( num == EVENT_OBJECT_DOWN ) strcpy(text, "Recule (\\key down;)"); + if ( num == EVENT_OBJECT_GASUP ) strcpy(text, "Monte (\\key gup;)"); + if ( num == EVENT_OBJECT_GASDOWN ) strcpy(text, "Descend (\\key gdown;)"); + if ( num == EVENT_OBJECT_HTAKE ) strcpy(text, "Prend ou dépose (\\key action;)"); + if ( num == EVENT_OBJECT_MTAKE ) strcpy(text, "Prend ou dépose (\\key action;)"); + if ( num == EVENT_OBJECT_MFRONT ) strcpy(text, "..devant"); + if ( num == EVENT_OBJECT_MBACK ) strcpy(text, "..derrière"); + if ( num == EVENT_OBJECT_MPOWER ) strcpy(text, "..pile"); + if ( num == EVENT_OBJECT_BHELP ) strcpy(text, "Instructions sur la mission (\\key help;)"); + if ( num == EVENT_OBJECT_BTAKEOFF ) strcpy(text, "Décolle pour terminer la mission"); + if ( num == EVENT_OBJECT_BDERRICK ) strcpy(text, "Construit un derrick"); + if ( num == EVENT_OBJECT_BSTATION ) strcpy(text, "Construit une station"); + if ( num == EVENT_OBJECT_BFACTORY ) strcpy(text, "Construit une fabrique de robots"); + if ( num == EVENT_OBJECT_BREPAIR ) strcpy(text, "Construit un centre de réparation"); + if ( num == EVENT_OBJECT_BCONVERT ) strcpy(text, "Construit un convertisseur"); + if ( num == EVENT_OBJECT_BTOWER ) strcpy(text, "Construit une tour"); + if ( num == EVENT_OBJECT_BRESEARCH ) strcpy(text, "Construit un centre de recherches"); + if ( num == EVENT_OBJECT_BRADAR ) strcpy(text, "Construit un radar"); + if ( num == EVENT_OBJECT_BENERGY ) strcpy(text, "Construit une fabrique de piles"); + if ( num == EVENT_OBJECT_BLABO ) strcpy(text, "Construit un laboratoire"); + if ( num == EVENT_OBJECT_BNUCLEAR ) strcpy(text, "Construit une centrale nucléaire"); + if ( num == EVENT_OBJECT_BPARA ) strcpy(text, "Construit un paratonnerre"); + if ( num == EVENT_OBJECT_BINFO ) strcpy(text, "Construit une borne d'information"); + if ( num == EVENT_OBJECT_GFLAT ) strcpy(text, "Montre si le sol est plat"); + if ( num == EVENT_OBJECT_FCREATE ) strcpy(text, "Pose un drapeau de couleur"); + if ( num == EVENT_OBJECT_FDELETE ) strcpy(text, "Enlève un drapeau"); + if ( num == EVENT_OBJECT_FCOLORb ) strcpy(text, "\\Drapeaux bleus"); + if ( num == EVENT_OBJECT_FCOLORr ) strcpy(text, "\\Drapeaux rouges"); + if ( num == EVENT_OBJECT_FCOLORg ) strcpy(text, "\\Drapeaux verts"); + if ( num == EVENT_OBJECT_FCOLORy ) strcpy(text, "\\Drapeaux jaunes"); + if ( num == EVENT_OBJECT_FCOLORv ) strcpy(text, "\\Drapeaux violets"); + if ( num == EVENT_OBJECT_FACTORYfa ) strcpy(text, "Fabrique un déménageur volant"); + if ( num == EVENT_OBJECT_FACTORYta ) strcpy(text, "Fabrique un déménageur à chenilles"); + if ( num == EVENT_OBJECT_FACTORYwa ) strcpy(text, "Fabrique un déménageur à roues"); + if ( num == EVENT_OBJECT_FACTORYia ) strcpy(text, "Fabrique un déménageur à pattes"); + if ( num == EVENT_OBJECT_FACTORYfc ) strcpy(text, "Fabrique un shooter volant"); + if ( num == EVENT_OBJECT_FACTORYtc ) strcpy(text, "Fabrique un shooter à chenilles"); + if ( num == EVENT_OBJECT_FACTORYwc ) strcpy(text, "Fabrique un shooter à roues"); + if ( num == EVENT_OBJECT_FACTORYic ) strcpy(text, "Fabrique un shooter à pattes"); + if ( num == EVENT_OBJECT_FACTORYfi ) strcpy(text, "Fabrique un orgaShooter volant"); + if ( num == EVENT_OBJECT_FACTORYti ) strcpy(text, "Fabrique un orgaShooter à chenilles"); + if ( num == EVENT_OBJECT_FACTORYwi ) strcpy(text, "Fabrique un orgaShooter à roues"); + if ( num == EVENT_OBJECT_FACTORYii ) strcpy(text, "Fabrique un orgaShooter à pattes"); + if ( num == EVENT_OBJECT_FACTORYfs ) strcpy(text, "Fabrique un renifleur volant"); + if ( num == EVENT_OBJECT_FACTORYts ) strcpy(text, "Fabrique un renifleur à chenilles"); + if ( num == EVENT_OBJECT_FACTORYws ) strcpy(text, "Fabrique un renifleur à roues"); + if ( num == EVENT_OBJECT_FACTORYis ) strcpy(text, "Fabrique un renifleur à pattes"); + if ( num == EVENT_OBJECT_FACTORYrt ) strcpy(text, "Fabrique un robot secoueur"); + if ( num == EVENT_OBJECT_FACTORYrc ) strcpy(text, "Fabrique un robot phazer"); + if ( num == EVENT_OBJECT_FACTORYrr ) strcpy(text, "Fabrique un robot recycleur"); + if ( num == EVENT_OBJECT_FACTORYrs ) strcpy(text, "Fabrique un robot bouclier"); + if ( num == EVENT_OBJECT_FACTORYsa ) strcpy(text, "Fabrique un robot sous-marin"); + if ( num == EVENT_OBJECT_RTANK ) strcpy(text, "Recherche les chenilles"); + if ( num == EVENT_OBJECT_RFLY ) strcpy(text, "Recherche les robots volants"); + if ( num == EVENT_OBJECT_RTHUMP ) strcpy(text, "Recherche le secoueur"); + if ( num == EVENT_OBJECT_RCANON ) strcpy(text, "Recherche le canon shooter"); + if ( num == EVENT_OBJECT_RTOWER ) strcpy(text, "Recherche la tour de défense"); + if ( num == EVENT_OBJECT_RPHAZER ) strcpy(text, "Recherche le canon phazer"); + if ( num == EVENT_OBJECT_RSHIELD ) strcpy(text, "Recherche le bouclier"); + if ( num == EVENT_OBJECT_RATOMIC ) strcpy(text, "Recherche le nucléaire"); + if ( num == EVENT_OBJECT_RiPAW ) strcpy(text, "Recherche les pattes"); + if ( num == EVENT_OBJECT_RiGUN ) strcpy(text, "Recherche le canon orgaShooter"); + if ( num == EVENT_OBJECT_RESET ) strcpy(text, "Remet au départ"); + if ( num == EVENT_OBJECT_SEARCH ) strcpy(text, "Cherche (\\key action;)"); + if ( num == EVENT_OBJECT_TERRAFORM ) strcpy(text, "Secoue (\\key action;)"); + if ( num == EVENT_OBJECT_FIRE ) strcpy(text, "Tir (\\key action;)"); + if ( num == EVENT_OBJECT_RECOVER ) strcpy(text, "Recycle (\\key action;)"); + if ( num == EVENT_OBJECT_BEGSHIELD ) strcpy(text, "Déploie le bouclier (\\key action;)"); + if ( num == EVENT_OBJECT_ENDSHIELD ) strcpy(text, "Stoppe le bouclier (\\key action;)"); + if ( num == EVENT_OBJECT_DIMSHIELD ) strcpy(text, "Rayon du bouclier"); + if ( num == EVENT_OBJECT_PROGRUN ) strcpy(text, "Exécute le programme sélectionné"); + if ( num == EVENT_OBJECT_PROGEDIT ) strcpy(text, "Edite le programme sélectionné"); + if ( num == EVENT_OBJECT_INFOOK ) strcpy(text, "\\Mettre le SatCom en veille"); + if ( num == EVENT_OBJECT_DELETE ) strcpy(text, "Démolit le bâtiment"); + if ( num == EVENT_OBJECT_GENERGY ) strcpy(text, "Niveau d'énergie"); + if ( num == EVENT_OBJECT_GSHIELD ) strcpy(text, "Niveau du bouclier"); + if ( num == EVENT_OBJECT_GRANGE ) strcpy(text, "Température du réacteur"); + if ( num == EVENT_OBJECT_GPROGRESS ) strcpy(text, "Travail en cours ..."); + if ( num == EVENT_OBJECT_GRADAR ) strcpy(text, "Nombre d'insectes détectés"); + if ( num == EVENT_OBJECT_GINFO ) strcpy(text, "Informations diffusées"); + if ( num == EVENT_OBJECT_COMPASS ) strcpy(text, "Boussole"); +//? if ( num == EVENT_OBJECT_MAP ) strcpy(text, "Mini-carte"); + if ( num == EVENT_OBJECT_MAPZOOM ) strcpy(text, "Zoom mini-carte"); + if ( num == EVENT_OBJECT_CAMERA ) strcpy(text, "Caméra (\\key camera;)"); + if ( num == EVENT_OBJECT_CAMERAleft) strcpy(text, "Caméra à gauche"); + if ( num == EVENT_OBJECT_CAMERAright) strcpy(text, "Caméra à droite"); + if ( num == EVENT_OBJECT_CAMERAnear) strcpy(text, "Caméra plus proche"); + if ( num == EVENT_OBJECT_CAMERAaway) strcpy(text, "Caméra plus loin"); + if ( num == EVENT_OBJECT_HELP ) strcpy(text, "Instructions sur la sélection"); + if ( num == EVENT_OBJECT_SOLUCE ) strcpy(text, "Donne la solution"); + if ( num == EVENT_OBJECT_SHORTCUT00) strcpy(text, "Permute robots <-> bâtiments"); + if ( num == EVENT_OBJECT_LIMIT ) strcpy(text, "Montre le rayon d'action"); + if ( num == EVENT_OBJECT_PEN0 ) strcpy(text, "\\Relève le crayon"); + if ( num == EVENT_OBJECT_PEN1 ) strcpy(text, "\\Abaisse le crayon noir"); + if ( num == EVENT_OBJECT_PEN2 ) strcpy(text, "\\Abaisse le crayon jaune"); + if ( num == EVENT_OBJECT_PEN3 ) strcpy(text, "\\Abaisse le crayon orange"); + if ( num == EVENT_OBJECT_PEN4 ) strcpy(text, "\\Abaisse le crayon rouge"); + if ( num == EVENT_OBJECT_PEN5 ) strcpy(text, "\\Abaisse le crayon violet"); + if ( num == EVENT_OBJECT_PEN6 ) strcpy(text, "\\Abaisse le crayon bleu"); + if ( num == EVENT_OBJECT_PEN7 ) strcpy(text, "\\Abaisse le crayon vert"); + if ( num == EVENT_OBJECT_PEN8 ) strcpy(text, "\\Abaisse le crayon brun"); + if ( num == EVENT_OBJECT_REC ) strcpy(text, "\\Démarre l'enregistrement"); + if ( num == EVENT_OBJECT_STOP ) strcpy(text, "\\Stoppe l'enregistrement"); + if ( num == EVENT_DT_VISIT0 || + num == EVENT_DT_VISIT1 || + num == EVENT_DT_VISIT2 || + num == EVENT_DT_VISIT3 || + num == EVENT_DT_VISIT4 ) strcpy(text, "Montre l'endroit"); + if ( num == EVENT_DT_END ) strcpy(text, "Continuer"); + if ( num == EVENT_CMD ) strcpy(text, "Console de commande"); + if ( num == EVENT_SPEED ) strcpy(text, "Vitesse du jeu"); + + if ( num == EVENT_HYPER_PREV ) strcpy(text, "Page précédente"); + if ( num == EVENT_HYPER_NEXT ) strcpy(text, "Page suivante"); + if ( num == EVENT_HYPER_HOME ) strcpy(text, "Page initiale"); + if ( num == EVENT_HYPER_COPY ) strcpy(text, "Copier"); + if ( num == EVENT_HYPER_SIZE1 ) strcpy(text, "Taille 1"); + if ( num == EVENT_HYPER_SIZE2 ) strcpy(text, "Taille 2"); + if ( num == EVENT_HYPER_SIZE3 ) strcpy(text, "Taille 3"); + if ( num == EVENT_HYPER_SIZE4 ) strcpy(text, "Taille 4"); + if ( num == EVENT_HYPER_SIZE5 ) strcpy(text, "Taille 5"); + if ( num == EVENT_SATCOM_HUSTON ) strcpy(text, "Instructions de Houston"); +#if _TEEN + if ( num == EVENT_SATCOM_SAT ) strcpy(text, "Dictionnaire anglais-français"); +#else + if ( num == EVENT_SATCOM_SAT ) strcpy(text, "Rapport du satellite"); +#endif + if ( num == EVENT_SATCOM_LOADING ) strcpy(text, "Programmes envoyés par Houston"); + if ( num == EVENT_SATCOM_OBJECT ) strcpy(text, "Liste des objets"); + if ( num == EVENT_SATCOM_PROG ) strcpy(text, "Aide à la programmation"); + if ( num == EVENT_SATCOM_SOLUCE ) strcpy(text, "Solution"); + + if ( num == EVENT_STUDIO_OK ) strcpy(text, "D'accord\\Compiler le programme"); + if ( num == EVENT_STUDIO_CANCEL ) strcpy(text, "Annuler\\Annuler toutes les modifications"); + if ( num == EVENT_STUDIO_NEW ) strcpy(text, "Nouveau"); + if ( num == EVENT_STUDIO_OPEN ) strcpy(text, "Ouvrir (Ctrl+o)"); + if ( num == EVENT_STUDIO_SAVE ) strcpy(text, "Enregistrer (Ctrl+s)"); + if ( num == EVENT_STUDIO_UNDO ) strcpy(text, "Annuler (Ctrl+z)"); + if ( num == EVENT_STUDIO_CUT ) strcpy(text, "Couper (Ctrl+x)"); + if ( num == EVENT_STUDIO_COPY ) strcpy(text, "Copier (Ctrl+c)"); + if ( num == EVENT_STUDIO_PASTE ) strcpy(text, "Coller (Ctrl+v)"); + if ( num == EVENT_STUDIO_SIZE ) strcpy(text, "Taille des caractères"); + if ( num == EVENT_STUDIO_TOOL ) strcpy(text, "Instructions (\\key help;)"); + if ( num == EVENT_STUDIO_HELP ) strcpy(text, "Aide à la programmation (\\key prog;)"); + if ( num == EVENT_STUDIO_COMPILE ) strcpy(text, "Compiler"); + if ( num == EVENT_STUDIO_RUN ) strcpy(text, "Démarrer/stopper"); + if ( num == EVENT_STUDIO_REALTIME ) strcpy(text, "Pause/continuer"); + if ( num == EVENT_STUDIO_STEP ) strcpy(text, "Un pas"); + } + + if ( type == RES_OBJECT ) + { + if ( num == OBJECT_PORTICO ) strcpy(text, "Portique"); + if ( num == OBJECT_BASE ) strcpy(text, "Vaisseau spatial"); + if ( num == OBJECT_DERRICK ) strcpy(text, "Derrick"); + if ( num == OBJECT_FACTORY ) strcpy(text, "Fabrique de robots"); + if ( num == OBJECT_REPAIR ) strcpy(text, "Centre de réparation"); + if ( num == OBJECT_DESTROYER ) strcpy(text, "Destructeur"); + if ( num == OBJECT_STATION ) strcpy(text, "Station de recharge"); + if ( num == OBJECT_CONVERT ) strcpy(text, "Conversion minerai en titanium"); + if ( num == OBJECT_TOWER ) strcpy(text, "Tour de défense"); + if ( num == OBJECT_NEST ) strcpy(text, "Nid"); + if ( num == OBJECT_RESEARCH ) strcpy(text, "Centre de recherches"); + if ( num == OBJECT_RADAR ) strcpy(text, "Radar"); + if ( num == OBJECT_INFO ) strcpy(text, "Borne d'information"); +#if _TEEN + if ( num == OBJECT_ENERGY ) strcpy(text, "Désintégrateur"); +#else + if ( num == OBJECT_ENERGY ) strcpy(text, "Fabrique de piles"); +#endif + if ( num == OBJECT_LABO ) strcpy(text, "Laboratoire de matières organiques"); + if ( num == OBJECT_NUCLEAR ) strcpy(text, "Centrale nucléaire"); + if ( num == OBJECT_PARA ) strcpy(text, "Paratonnerre"); + if ( num == OBJECT_SAFE ) strcpy(text, "Coffre-fort"); + if ( num == OBJECT_HUSTON ) strcpy(text, "Centre de contrôle"); + if ( num == OBJECT_TARGET1 ) strcpy(text, "Cible"); + if ( num == OBJECT_TARGET2 ) strcpy(text, "Cible"); + if ( num == OBJECT_START ) strcpy(text, "Départ"); + if ( num == OBJECT_END ) strcpy(text, "But"); + if ( num == OBJECT_STONE ) strcpy(text, "Minerai de titanium"); + if ( num == OBJECT_URANIUM ) strcpy(text, "Minerai d'uranium"); + if ( num == OBJECT_BULLET ) strcpy(text, "Matière organique"); + if ( num == OBJECT_METAL ) strcpy(text, "Titanium"); + if ( num == OBJECT_POWER ) strcpy(text, "Pile normale"); + if ( num == OBJECT_ATOMIC ) strcpy(text, "Pile nucléaire"); + if ( num == OBJECT_BBOX ) strcpy(text, "Boîte noire"); + if ( num == OBJECT_KEYa ) strcpy(text, "Clé A"); + if ( num == OBJECT_KEYb ) strcpy(text, "Clé B"); + if ( num == OBJECT_KEYc ) strcpy(text, "Clé C"); + if ( num == OBJECT_KEYd ) strcpy(text, "Clé D"); + if ( num == OBJECT_TNT ) strcpy(text, "Explosif"); + if ( num == OBJECT_BOMB ) strcpy(text, "Mine fixe"); + if ( num == OBJECT_BAG ) strcpy(text, "Sac de survie"); + if ( num == OBJECT_WAYPOINT ) strcpy(text, "Indicateur"); + if ( num == OBJECT_FLAGb ) strcpy(text, "Drapeau bleu"); + if ( num == OBJECT_FLAGr ) strcpy(text, "Drapeau rouge"); + if ( num == OBJECT_FLAGg ) strcpy(text, "Drapeau vert"); + if ( num == OBJECT_FLAGy ) strcpy(text, "Drapeau jaune"); + if ( num == OBJECT_FLAGv ) strcpy(text, "Drapeau violet"); + if ( num == OBJECT_MARKPOWER ) strcpy(text, "Emplacement pour station"); + if ( num == OBJECT_MARKURANIUM ) strcpy(text, "Emplacement pour derrick (uranium)"); + if ( num == OBJECT_MARKKEYa ) strcpy(text, "Emplacement pour derrick (clé A)"); + if ( num == OBJECT_MARKKEYb ) strcpy(text, "Emplacement pour derrick (clé B)"); + if ( num == OBJECT_MARKKEYc ) strcpy(text, "Emplacement pour derrick (clé C)"); + if ( num == OBJECT_MARKKEYd ) strcpy(text, "Emplacement pour derrick (clé D)"); + if ( num == OBJECT_MARKSTONE ) strcpy(text, "Emplacement pour derrick (titanium)"); + if ( num == OBJECT_MOBILEft ) strcpy(text, "Robot d'entraînement"); + if ( num == OBJECT_MOBILEtt ) strcpy(text, "Robot d'entraînement"); + if ( num == OBJECT_MOBILEwt ) strcpy(text, "Robot d'entraînement"); + if ( num == OBJECT_MOBILEit ) strcpy(text, "Robot d'entraînement"); + if ( num == OBJECT_MOBILEfa ) strcpy(text, "Robot déménageur"); + if ( num == OBJECT_MOBILEta ) strcpy(text, "Robot déménageur"); + if ( num == OBJECT_MOBILEwa ) strcpy(text, "Robot déménageur"); + if ( num == OBJECT_MOBILEia ) strcpy(text, "Robot déménageur"); + if ( num == OBJECT_MOBILEfc ) strcpy(text, "Robot shooter"); + if ( num == OBJECT_MOBILEtc ) strcpy(text, "Robot shooter"); + if ( num == OBJECT_MOBILEwc ) strcpy(text, "Robot shooter"); + if ( num == OBJECT_MOBILEic ) strcpy(text, "Robot shooter"); + if ( num == OBJECT_MOBILEfi ) strcpy(text, "Robot orgaShooter"); + if ( num == OBJECT_MOBILEti ) strcpy(text, "Robot orgaShooter"); + if ( num == OBJECT_MOBILEwi ) strcpy(text, "Robot orgaShooter"); + if ( num == OBJECT_MOBILEii ) strcpy(text, "Robot orgaShooter"); + if ( num == OBJECT_MOBILEfs ) strcpy(text, "Robot renifleur"); + if ( num == OBJECT_MOBILEts ) strcpy(text, "Robot renifleur"); + if ( num == OBJECT_MOBILEws ) strcpy(text, "Robot renifleur"); + if ( num == OBJECT_MOBILEis ) strcpy(text, "Robot renifleur"); + if ( num == OBJECT_MOBILErt ) strcpy(text, "Robot secoueur"); + if ( num == OBJECT_MOBILErc ) strcpy(text, "Robot phazer"); + if ( num == OBJECT_MOBILErr ) strcpy(text, "Robot recycleur"); + if ( num == OBJECT_MOBILErs ) strcpy(text, "Robot bouclier"); + if ( num == OBJECT_MOBILEsa ) strcpy(text, "Robot sous-marin"); + if ( num == OBJECT_MOBILEtg ) strcpy(text, "Cible d'entraînement"); + if ( num == OBJECT_MOBILEdr ) strcpy(text, "Robot dessinateur"); + if ( num == OBJECT_HUMAN ) strcpy(text, g_gamerName); + if ( num == OBJECT_TECH ) strcpy(text, "Technicien"); + if ( num == OBJECT_TOTO ) strcpy(text, "Robbie"); + if ( num == OBJECT_MOTHER ) strcpy(text, "Pondeuse"); + if ( num == OBJECT_ANT ) strcpy(text, "Fourmi"); + if ( num == OBJECT_SPIDER ) strcpy(text, "Araignée"); + if ( num == OBJECT_BEE ) strcpy(text, "Guêpe"); + if ( num == OBJECT_WORM ) strcpy(text, "Ver"); + if ( num == OBJECT_EGG ) strcpy(text, "Oeuf"); + if ( num == OBJECT_RUINmobilew1 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINmobilew2 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINmobilet1 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINmobilet2 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINmobiler1 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINmobiler2 ) strcpy(text, "Epave de robot"); + if ( num == OBJECT_RUINfactory ) strcpy(text, "Bâtiment en ruine"); + if ( num == OBJECT_RUINdoor ) strcpy(text, "Bâtiment en ruine"); + if ( num == OBJECT_RUINsupport ) strcpy(text, "Déchet"); + if ( num == OBJECT_RUINradar ) strcpy(text, "Bâtiment en ruine"); + if ( num == OBJECT_RUINconvert ) strcpy(text, "Bâtiment en ruine"); + if ( num == OBJECT_RUINbase ) strcpy(text, "Epave de vaisseau spatial"); + if ( num == OBJECT_RUINhead ) strcpy(text, "Epave de vaisseau spatial"); + if ( num == OBJECT_APOLLO1 || + num == OBJECT_APOLLO3 || + num == OBJECT_APOLLO4 || + num == OBJECT_APOLLO5 ) strcpy(text, "Vestige d'une mission Apollo"); + if ( num == OBJECT_APOLLO2 ) strcpy(text, "Lunar Roving Vehicle"); + } + + if ( type == RES_ERR ) + { + strcpy(text, "Erreur"); + if ( num == ERR_CMD ) strcpy(text, "Commande inconnue"); +#if _NEWLOOK + if ( num == ERR_INSTALL ) strcpy(text, "CeeBot n'est pas installé."); + if ( num == ERR_NOCD ) strcpy(text, "Veuillez mettre le CD de CeeBot\net relancer le jeu."); +#else + if ( num == ERR_INSTALL ) strcpy(text, "COLOBOT n'est pas installé."); + if ( num == ERR_NOCD ) strcpy(text, "Veuillez mettre le CD de COLOBOT\net relancer le jeu."); +#endif + if ( num == ERR_MANIP_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_MANIP_FLY ) strcpy(text, "Impossible en vol"); + if ( num == ERR_MANIP_BUSY ) strcpy(text, "Porte déjà quelque chose"); + if ( num == ERR_MANIP_NIL ) strcpy(text, "Rien à prendre"); + if ( num == ERR_MANIP_MOTOR ) strcpy(text, "Impossible en mouvement"); + if ( num == ERR_MANIP_OCC ) strcpy(text, "Emplacement occupé"); + if ( num == ERR_MANIP_FRIEND ) strcpy(text, "Pas d'autre robot"); + if ( num == ERR_MANIP_RADIO ) strcpy(text, "Vous ne pouvez pas transporter un objet radioactif"); + if ( num == ERR_MANIP_WATER ) strcpy(text, "Vous ne pouvez pas transporter un objet sous l'eau"); + if ( num == ERR_MANIP_EMPTY ) strcpy(text, "Rien à déposer"); + if ( num == ERR_BUILD_FLY ) strcpy(text, "Impossible en vol"); + if ( num == ERR_BUILD_WATER ) strcpy(text, "Impossible sous l'eau"); + if ( num == ERR_BUILD_ENERGY ) strcpy(text, "Pas assez d'énergie"); + if ( num == ERR_BUILD_METALAWAY ) strcpy(text, "Titanium trop loin"); + if ( num == ERR_BUILD_METALNEAR ) strcpy(text, "Titanium trop proche"); + if ( num == ERR_BUILD_METALINEX ) strcpy(text, "Titanium inexistant"); + if ( num == ERR_BUILD_FLAT ) strcpy(text, "Sol pas assez plat"); + if ( num == ERR_BUILD_FLATLIT ) strcpy(text, "Sol plat pas assez grand"); + if ( num == ERR_BUILD_BUSY ) strcpy(text, "Emplacement occupé"); + if ( num == ERR_BUILD_BASE ) strcpy(text, "Trop proche du vaisseau spatial"); + if ( num == ERR_BUILD_NARROW ) strcpy(text, "Trop proche d'un bâtiment"); + if ( num == ERR_BUILD_MOTOR ) strcpy(text, "Impossible en mouvement"); + if ( num == ERR_SEARCH_FLY ) strcpy(text, "Impossible en vol"); + if ( num == ERR_SEARCH_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_SEARCH_MOTOR ) strcpy(text, "Impossible en mouvement"); + if ( num == ERR_TERRA_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_TERRA_ENERGY ) strcpy(text, "Pas assez d'énergie"); + if ( num == ERR_TERRA_FLOOR ) strcpy(text, "Terrain inadapté"); + if ( num == ERR_TERRA_BUILDING ) strcpy(text, "Bâtiment trop proche"); + if ( num == ERR_TERRA_OBJECT ) strcpy(text, "Objet trop proche"); + if ( num == ERR_RECOVER_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_RECOVER_ENERGY ) strcpy(text, "Pas assez d'énergie"); + if ( num == ERR_RECOVER_NULL ) strcpy(text, "Rien à recycler"); + if ( num == ERR_SHIELD_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_SHIELD_ENERGY ) strcpy(text, "Plus d'énergie"); + if ( num == ERR_MOVE_IMPOSSIBLE ) strcpy(text, "Déplacement impossible"); + if ( num == ERR_FIND_IMPOSSIBLE ) strcpy(text, "Objet n'existe pas"); + if ( num == ERR_GOTO_IMPOSSIBLE ) strcpy(text, "Chemin introuvable"); + if ( num == ERR_GOTO_ITER ) strcpy(text, "Position inaccessible"); + if ( num == ERR_GOTO_BUSY ) strcpy(text, "Destination occupée"); + if ( num == ERR_FIRE_VEH ) strcpy(text, "Robot inadapté"); + if ( num == ERR_FIRE_ENERGY ) strcpy(text, "Pas assez d'énergie"); + if ( num == ERR_FIRE_FLY ) strcpy(text, "Impossible en vol"); + if ( num == ERR_CONVERT_EMPTY ) strcpy(text, "Pas de minerai de titanium à convertir"); + if ( num == ERR_DERRICK_NULL ) strcpy(text, "Pas de minerai en sous-sol"); + if ( num == ERR_STATION_NULL ) strcpy(text, "Pas d'énergie en sous-sol"); + if ( num == ERR_TOWER_POWER ) strcpy(text, "Pas de pile"); + if ( num == ERR_TOWER_ENERGY ) strcpy(text, "Plus d'énergie"); + if ( num == ERR_RESEARCH_POWER ) strcpy(text, "Pas de pile"); + if ( num == ERR_RESEARCH_ENERGY ) strcpy(text, "Plus assez d'énergie"); + if ( num == ERR_RESEARCH_TYPE ) strcpy(text, "Pas le bon type de pile"); + if ( num == ERR_RESEARCH_ALREADY) strcpy(text, "Recherche déjà effectuée"); + if ( num == ERR_ENERGY_NULL ) strcpy(text, "Pas d'énergie en sous-sol"); + if ( num == ERR_ENERGY_LOW ) strcpy(text, "Pas encore assez d'énergie"); + if ( num == ERR_ENERGY_EMPTY ) strcpy(text, "Pas de titanium à transformer"); + if ( num == ERR_ENERGY_BAD ) strcpy(text, "Ne transforme que le titanium"); + if ( num == ERR_BASE_DLOCK ) strcpy(text, "Portes bloquées par un robot ou un objet"); + if ( num == ERR_BASE_DHUMAN ) strcpy(text, "Vous devez embarquer pour pouvoir décoller"); + if ( num == ERR_LABO_NULL ) strcpy(text, "Rien à analyser"); + if ( num == ERR_LABO_BAD ) strcpy(text, "N'analyse que la matière organique"); + if ( num == ERR_LABO_ALREADY ) strcpy(text, "Analyse déjà effectuée"); + if ( num == ERR_NUCLEAR_NULL ) strcpy(text, "Pas d'énergie en sous-sol"); + if ( num == ERR_NUCLEAR_LOW ) strcpy(text, "Pas encore assez d'énergie"); + if ( num == ERR_NUCLEAR_EMPTY ) strcpy(text, "Pas d'uranium à transformer"); + if ( num == ERR_NUCLEAR_BAD ) strcpy(text, "Ne transforme que l'uranium"); + if ( num == ERR_FACTORY_NULL ) strcpy(text, "Pas de titanium"); + if ( num == ERR_FACTORY_NEAR ) strcpy(text, "Quelque chose est trop proche"); + if ( num == ERR_RESET_NEAR ) strcpy(text, "Emplacement occupé"); + if ( num == ERR_INFO_NULL ) strcpy(text, "Pas trouvé de borne d'information"); + if ( num == ERR_VEH_VIRUS ) strcpy(text, "Un programme est infecté par un virus"); + if ( num == ERR_BAT_VIRUS ) strcpy(text, "Infecté par un virus, ne fonctionne plus temporairement"); + if ( num == ERR_VEH_POWER ) strcpy(text, "Pas de pile"); + if ( num == ERR_VEH_ENERGY ) strcpy(text, "Plus d'énergie"); + if ( num == ERR_FLAG_FLY ) strcpy(text, "Impossible en vol"); + if ( num == ERR_FLAG_WATER ) strcpy(text, "Impossible en nageant"); + if ( num == ERR_FLAG_MOTOR ) strcpy(text, "Impossible en mouvement"); + if ( num == ERR_FLAG_BUSY ) strcpy(text, "Impossible en portant un objet"); + if ( num == ERR_FLAG_CREATE ) strcpy(text, "Trop de drapeaux de cette couleur (maximum 5)"); + if ( num == ERR_FLAG_PROXY ) strcpy(text, "Trop proche d'un drapeau existant"); + if ( num == ERR_FLAG_DELETE ) strcpy(text, "Aucun drapeau à proximité"); + if ( num == ERR_MISSION_NOTERM ) strcpy(text, "La misssion n'est pas terminée (appuyez sur \\key help; pour plus de détails)"); + if ( num == ERR_DELETEMOBILE ) strcpy(text, "Robot détruit"); + if ( num == ERR_DELETEBUILDING ) strcpy(text, "Bâtiment détruit"); + if ( num == ERR_TOOMANY ) strcpy(text, "Création impossible, il y a trop d'objets"); + if ( num == ERR_OBLIGATORYTOKEN ) strcpy(text, "Il manque \"%s\" dans le programme"); + if ( num == ERR_PROHIBITEDTOKEN ) strcpy(text, "Interdit dans cet exercice"); + + if ( num == INFO_BUILD ) strcpy(text, "Bâtiment terminé"); + if ( num == INFO_CONVERT ) strcpy(text, "Titanium disponible"); + if ( num == INFO_RESEARCH ) strcpy(text, "Recherche terminée"); + if ( num == INFO_RESEARCHTANK ) strcpy(text, "Fabrication d'un robot à chenilles possible"); + if ( num == INFO_RESEARCHFLY ) strcpy(text, "Il est possible de voler avec les touches (\\key gup;) et (\\key gdown;)"); + if ( num == INFO_RESEARCHTHUMP ) strcpy(text, "Fabrication d'un robot secoueur possible"); + if ( num == INFO_RESEARCHCANON ) strcpy(text, "Fabrication de robots shooter possible"); + if ( num == INFO_RESEARCHTOWER ) strcpy(text, "Construction d'une tour de défense possible"); + if ( num == INFO_RESEARCHPHAZER ) strcpy(text, "Fabrication d'un robot phazer possible"); + if ( num == INFO_RESEARCHSHIELD ) strcpy(text, "Fabrication d'un robot bouclier possible"); + if ( num == INFO_RESEARCHATOMIC ) strcpy(text, "Construction d'une centrale nucléaire possible"); + if ( num == INFO_FACTORY ) strcpy(text, "Nouveau robot disponible"); + if ( num == INFO_LABO ) strcpy(text, "Analyse terminée"); + if ( num == INFO_ENERGY ) strcpy(text, "Pile disponible"); + if ( num == INFO_NUCLEAR ) strcpy(text, "Pile nucléaire disponible"); + if ( num == INFO_FINDING ) strcpy(text, "Vous avez trouvé un objet utilisable"); + if ( num == INFO_MARKPOWER ) strcpy(text, "Emplacement pour station trouvé"); + if ( num == INFO_MARKURANIUM ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_MARKSTONE ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_MARKKEYa ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_MARKKEYb ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_MARKKEYc ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_MARKKEYd ) strcpy(text, "Emplacement pour derrick trouvé"); + if ( num == INFO_WIN ) strcpy(text, "<<< Bravo, mission terminée >>>"); + if ( num == INFO_LOST ) strcpy(text, "<<< Désolé, mission échouée >>>"); + if ( num == INFO_LOSTq ) strcpy(text, "<<< Désolé, mission échouée >>>"); + if ( num == INFO_WRITEOK ) strcpy(text, "Enregistrement effectué"); + if ( num == INFO_DELETEPATH ) strcpy(text, "Indicateur atteint"); + if ( num == INFO_DELETEMOTHER ) strcpy(text, "Pondeuse mortellement touchée"); + if ( num == INFO_DELETEANT ) strcpy(text, "Fourmi mortellement touchée"); + if ( num == INFO_DELETEBEE ) strcpy(text, "Guêpe mortellement touchée"); + if ( num == INFO_DELETEWORM ) strcpy(text, "Ver mortellement touché"); + if ( num == INFO_DELETESPIDER ) strcpy(text, "Araignée mortellement touchée"); + if ( num == INFO_BEGINSATCOM ) strcpy(text, "Consultez votre SatCom en appuyant sur \\key help;"); + } + + if ( type == RES_CBOT ) + { + strcpy(text, "Erreur"); + if ( num == TX_OPENPAR ) strcpy(text, "Il manque une parenthèse ouvrante"); + if ( num == TX_CLOSEPAR ) strcpy(text, "Il manque une parenthèse fermante"); + if ( num == TX_NOTBOOL ) strcpy(text, "L'expression doit être un boolean"); + if ( num == TX_UNDEFVAR ) strcpy(text, "Variable non déclarée"); + if ( num == TX_BADLEFT ) strcpy(text, "Assignation impossible"); + if ( num == TX_ENDOF ) strcpy(text, "Terminateur point-virgule non trouvé"); + if ( num == TX_OUTCASE ) strcpy(text, "Instruction ""case"" hors d'un bloc ""switch"""); + if ( num == TX_NOTERM ) strcpy(text, "Instructions après la fin"); + if ( num == TX_CLOSEBLK ) strcpy(text, "Il manque la fin du bloc"); + if ( num == TX_ELSEWITHOUTIF ) strcpy(text, "Instruction ""else"" sans ""if"" correspondant"); + if ( num == TX_OPENBLK ) strcpy(text, "Début d'un bloc attendu"); + if ( num == TX_BADTYPE ) strcpy(text, "Mauvais type de résultat pour l'assignation"); + if ( num == TX_REDEFVAR ) strcpy(text, "Redéfinition d'une variable"); + if ( num == TX_BAD2TYPE ) strcpy(text, "Les deux opérandes ne sont pas de types compatibles"); + if ( num == TX_UNDEFCALL ) strcpy(text, "Routine inconnue"); + if ( num == TX_MISDOTS ) strcpy(text, "Séparateur "" : "" attendu"); + if ( num == TX_WHILE ) strcpy(text, "Manque le mot ""while"""); + if ( num == TX_BREAK ) strcpy(text, "Instruction ""break"" en dehors d'une boucle"); + if ( num == TX_LABEL ) strcpy(text, "Un label ne peut se placer que devant un ""for"", un ""while"", un ""do"" ou un ""switch"""); + if ( num == TX_NOLABEL ) strcpy(text, "Cette étiquette n'existe pas"); + if ( num == TX_NOCASE ) strcpy(text, "Manque une instruction ""case"""); + if ( num == TX_BADNUM ) strcpy(text, "Un nombre est attendu"); + if ( num == TX_VOID ) strcpy(text, "Paramètre void"); + if ( num == TX_NOTYP ) strcpy(text, "Déclaration de type attendu"); + if ( num == TX_NOVAR ) strcpy(text, "Nom d'une variable attendu"); + if ( num == TX_NOFONC ) strcpy(text, "Nom de la fonction attendu"); + if ( num == TX_OVERPARAM ) strcpy(text, "Trop de paramètres"); + if ( num == TX_REDEF ) strcpy(text, "Cette fonction existe déjà"); + if ( num == TX_LOWPARAM ) strcpy(text, "Pas assez de paramètres"); + if ( num == TX_BADPARAM ) strcpy(text, "Aucune fonction de ce nom n'accepte ce(s) type(s) de paramètre(s)"); + if ( num == TX_NUMPARAM ) strcpy(text, "Aucune fonction de ce nom n'accepte ce nombre de paramètres"); + if ( num == TX_NOITEM ) strcpy(text, "Cet élément n'existe pas dans cette classe"); + if ( num == TX_DOT ) strcpy(text, "L'objet n'est pas une instance d'une classe"); + if ( num == TX_NOCONST ) strcpy(text, "Il n'y a pas de constructeur approprié"); + if ( num == TX_REDEFCLASS ) strcpy(text, "Cette classe existe déjà"); + if ( num == TX_CLBRK ) strcpy(text, """ ] "" attendu"); + if ( num == TX_RESERVED ) strcpy(text, "Ce mot est réservé"); + if ( num == TX_BADNEW ) strcpy(text, "Mauvais argument pour ""new"""); + if ( num == TX_OPBRK ) strcpy(text, """ [ "" attendu"); + if ( num == TX_BADSTRING ) strcpy(text, "Une chaîne de caractère est attendue"); + if ( num == TX_BADINDEX ) strcpy(text, "Mauvais type d'index"); + if ( num == TX_PRIVATE ) strcpy(text, "Elément protégé"); + if ( num == TX_NOPUBLIC ) strcpy(text, "Public requis"); + if ( num == TX_DIVZERO ) strcpy(text, "Division par zéro"); + if ( num == TX_NOTINIT ) strcpy(text, "Variable non initialisée"); + if ( num == TX_BADTHROW ) strcpy(text, "Valeur négative refusée pour ""throw"""); + if ( num == TX_NORETVAL ) strcpy(text, "La fonction n'a pas retourné de résultat"); + if ( num == TX_NORUN ) strcpy(text, "Pas de fonction en exécution"); + if ( num == TX_NOCALL ) strcpy(text, "Appel d'une fonction inexistante"); + if ( num == TX_NOCLASS ) strcpy(text, "Cette classe n'existe pas"); + if ( num == TX_NULLPT ) strcpy(text, "Objet n'existe pas"); + if ( num == TX_OPNAN ) strcpy(text, "Opération sur un ""nan"""); + if ( num == TX_OUTARRAY ) strcpy(text, "Accès hors du tableau"); + if ( num == TX_STACKOVER ) strcpy(text, "Débordement de la pile"); + if ( num == TX_DELETEDPT ) strcpy(text, "Objet inaccessible"); + if ( num == TX_FILEOPEN ) strcpy(text, "Ouverture du fichier impossible"); + if ( num == TX_NOTOPEN ) strcpy(text, "Le fichier n'est pas ouvert"); + if ( num == TX_ERRREAD ) strcpy(text, "Erreur à la lecture"); + if ( num == TX_ERRWRITE ) strcpy(text, "Erreur à l'écriture"); + } + + if ( type == RES_KEY ) + { + if ( num == 0 ) strcpy(text, "< aucune >"); + if ( num == VK_LEFT ) strcpy(text, "Flèche Gauche"); + if ( num == VK_RIGHT ) strcpy(text, "Flèche Droite"); + if ( num == VK_UP ) strcpy(text, "Flèche Haut"); + if ( num == VK_DOWN ) strcpy(text, "Flèche Bas"); + if ( num == VK_CANCEL ) strcpy(text, "Control-break"); + if ( num == VK_BACK ) strcpy(text, "<--"); + if ( num == VK_TAB ) strcpy(text, "Tab"); + if ( num == VK_CLEAR ) strcpy(text, "Clear"); + if ( num == VK_RETURN ) strcpy(text, "Entrée"); + if ( num == VK_SHIFT ) strcpy(text, "Shift"); + if ( num == VK_CONTROL ) strcpy(text, "Ctrl"); + if ( num == VK_MENU ) strcpy(text, "Alt"); + if ( num == VK_PAUSE ) strcpy(text, "Pause"); + if ( num == VK_CAPITAL ) strcpy(text, "Caps Lock"); + if ( num == VK_ESCAPE ) strcpy(text, "Esc"); + if ( num == VK_SPACE ) strcpy(text, "Espace"); + if ( num == VK_PRIOR ) strcpy(text, "Page Up"); + if ( num == VK_NEXT ) strcpy(text, "Page Down"); + if ( num == VK_END ) strcpy(text, "End"); + if ( num == VK_HOME ) strcpy(text, "Home"); + if ( num == VK_SELECT ) strcpy(text, "Select"); + if ( num == VK_EXECUTE ) strcpy(text, "Execute"); + if ( num == VK_SNAPSHOT ) strcpy(text, "Print Scrn"); + if ( num == VK_INSERT ) strcpy(text, "Insert"); + if ( num == VK_DELETE ) strcpy(text, "Delete"); + if ( num == VK_HELP ) strcpy(text, "Help"); + if ( num == VK_LWIN ) strcpy(text, "Left Windows"); + if ( num == VK_RWIN ) strcpy(text, "Right Windows"); + if ( num == VK_APPS ) strcpy(text, "Application key"); + if ( num == VK_NUMPAD0 ) strcpy(text, "NumPad 0"); + if ( num == VK_NUMPAD1 ) strcpy(text, "NumPad 1"); + if ( num == VK_NUMPAD2 ) strcpy(text, "NumPad 2"); + if ( num == VK_NUMPAD3 ) strcpy(text, "NumPad 3"); + if ( num == VK_NUMPAD4 ) strcpy(text, "NumPad 4"); + if ( num == VK_NUMPAD5 ) strcpy(text, "NumPad 5"); + if ( num == VK_NUMPAD6 ) strcpy(text, "NumPad 6"); + if ( num == VK_NUMPAD7 ) strcpy(text, "NumPad 7"); + if ( num == VK_NUMPAD8 ) strcpy(text, "NumPad 8"); + if ( num == VK_NUMPAD9 ) strcpy(text, "NumPad 9"); + if ( num == VK_MULTIPLY ) strcpy(text, "NumPad *"); + if ( num == VK_ADD ) strcpy(text, "NumPad +"); + if ( num == VK_SEPARATOR ) strcpy(text, "NumPad sep"); + if ( num == VK_SUBTRACT ) strcpy(text, "NumPad -"); + if ( num == VK_DECIMAL ) strcpy(text, "NumPad ."); + if ( num == VK_DIVIDE ) strcpy(text, "NumPad /"); + if ( num == VK_F1 ) strcpy(text, "F1"); + if ( num == VK_F2 ) strcpy(text, "F2"); + if ( num == VK_F3 ) strcpy(text, "F3"); + if ( num == VK_F4 ) strcpy(text, "F4"); + if ( num == VK_F5 ) strcpy(text, "F5"); + if ( num == VK_F6 ) strcpy(text, "F6"); + if ( num == VK_F7 ) strcpy(text, "F7"); + if ( num == VK_F8 ) strcpy(text, "F8"); + if ( num == VK_F9 ) strcpy(text, "F9"); + if ( num == VK_F10 ) strcpy(text, "F10"); + if ( num == VK_F11 ) strcpy(text, "F11"); + if ( num == VK_F12 ) strcpy(text, "F12"); + if ( num == VK_F13 ) strcpy(text, "F13"); + if ( num == VK_F14 ) strcpy(text, "F14"); + if ( num == VK_F15 ) strcpy(text, "F15"); + if ( num == VK_F16 ) strcpy(text, "F16"); + if ( num == VK_F17 ) strcpy(text, "F17"); + if ( num == VK_F18 ) strcpy(text, "F18"); + if ( num == VK_F19 ) strcpy(text, "F19"); + if ( num == VK_F20 ) strcpy(text, "F20"); + if ( num == VK_NUMLOCK ) strcpy(text, "Num Lock"); + if ( num == VK_SCROLL ) strcpy(text, "Scroll"); + if ( num == VK_ATTN ) strcpy(text, "Attn"); + if ( num == VK_CRSEL ) strcpy(text, "CrSel"); + if ( num == VK_EXSEL ) strcpy(text, "ExSel"); + if ( num == VK_EREOF ) strcpy(text, "Erase EOF"); + if ( num == VK_PLAY ) strcpy(text, "Play"); + if ( num == VK_ZOOM ) strcpy(text, "Zoom"); + if ( num == VK_PA1 ) strcpy(text, "PA1"); + if ( num == VK_OEM_CLEAR ) strcpy(text, "Clear"); + if ( num == VK_BUTTON1 ) strcpy(text, "Bouton 1"); + if ( num == VK_BUTTON2 ) strcpy(text, "Bouton 2"); + if ( num == VK_BUTTON3 ) strcpy(text, "Bouton 3"); + if ( num == VK_BUTTON4 ) strcpy(text, "Bouton 4"); + if ( num == VK_BUTTON5 ) strcpy(text, "Bouton 5"); + if ( num == VK_BUTTON6 ) strcpy(text, "Bouton 6"); + if ( num == VK_BUTTON7 ) strcpy(text, "Bouton 7"); + if ( num == VK_BUTTON8 ) strcpy(text, "Bouton 8"); + if ( num == VK_BUTTON9 ) strcpy(text, "Bouton 9"); + if ( num == VK_BUTTON10 ) strcpy(text, "Bouton 10"); + if ( num == VK_BUTTON11 ) strcpy(text, "Bouton 11"); + if ( num == VK_BUTTON12 ) strcpy(text, "Bouton 12"); + if ( num == VK_BUTTON13 ) strcpy(text, "Bouton 13"); + if ( num == VK_BUTTON14 ) strcpy(text, "Bouton 14"); + if ( num == VK_BUTTON15 ) strcpy(text, "Bouton 15"); + if ( num == VK_BUTTON16 ) strcpy(text, "Bouton 16"); + if ( num == VK_BUTTON17 ) strcpy(text, "Bouton 17"); + if ( num == VK_BUTTON18 ) strcpy(text, "Bouton 18"); + if ( num == VK_BUTTON19 ) strcpy(text, "Bouton 19"); + if ( num == VK_BUTTON20 ) strcpy(text, "Bouton 20"); + if ( num == VK_BUTTON21 ) strcpy(text, "Bouton 21"); + if ( num == VK_BUTTON22 ) strcpy(text, "Bouton 22"); + if ( num == VK_BUTTON23 ) strcpy(text, "Bouton 23"); + if ( num == VK_BUTTON24 ) strcpy(text, "Bouton 24"); + if ( num == VK_BUTTON25 ) strcpy(text, "Bouton 25"); + if ( num == VK_BUTTON26 ) strcpy(text, "Bouton 26"); + if ( num == VK_BUTTON27 ) strcpy(text, "Bouton 27"); + if ( num == VK_BUTTON28 ) strcpy(text, "Bouton 28"); + if ( num == VK_BUTTON29 ) strcpy(text, "Bouton 29"); + if ( num == VK_BUTTON30 ) strcpy(text, "Bouton 30"); + if ( num == VK_BUTTON31 ) strcpy(text, "Bouton 31"); + if ( num == VK_BUTTON32 ) strcpy(text, "Bouton 32"); + if ( num == VK_WHEELUP ) strcpy(text, "Molette haut"); + if ( num == VK_WHEELDOWN ) strcpy(text, "Molette bas"); + } +#endif + +#if _GERMAN | _WG + if ( type == RES_TEXT ) + { + #if _FULL + if ( num == RT_VERSION_ID ) strcpy(text, "1.18 /d"); + #endif + #if _NET + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A 1.18"); + #endif + #if _SCHOOL & _EDU + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen EDU 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A EDU 1.18"); + #endif + #endif + #if _SCHOOL & _PERSO + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen PERSO 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A PERSO 1.18"); + #endif + #endif + #if _SCHOOL & _CEEBOTDEMO + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen DEMO 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A DEMO 1.18"); + #endif + #endif + #if _DEMO + if ( num == RT_VERSION_ID ) strcpy(text, "Demo 1.18 /d"); + #endif + if ( num == RT_DISINFO_TITLE ) strcpy(text, "SatCom"); + if ( num == RT_WINDOW_MAXIMIZED ) strcpy(text, "Großes Fenster"); + if ( num == RT_WINDOW_MINIMIZED ) strcpy(text, "Reduzieren"); + if ( num == RT_WINDOW_STANDARD ) strcpy(text, "Normale Größe"); + if ( num == RT_WINDOW_CLOSE ) strcpy(text, "Schließen"); + + if ( num == RT_STUDIO_TITLE ) strcpy(text, "Programmeditor"); + if ( num == RT_SCRIPT_NEW ) strcpy(text, "Neu"); + if ( num == RT_NAME_DEFAULT ) strcpy(text, "Spieler"); + if ( num == RT_IO_NEW ) strcpy(text, "Neu ..."); + if ( num == RT_KEY_OR ) strcpy(text, " oder "); + +#if _NEWLOOK + if ( num == RT_TITLE_BASE ) strcpy(text, "CeeBot"); + if ( num == RT_TITLE_INIT ) strcpy(text, "CeeBot"); +#else + if ( num == RT_TITLE_BASE ) strcpy(text, "COLOBOT"); + if ( num == RT_TITLE_INIT ) strcpy(text, "COLOBOT"); +#endif +#if _SCHOOL + if ( num == RT_TITLE_TRAINER ) strcpy(text, "Übungen"); +#else + if ( num == RT_TITLE_TRAINER ) strcpy(text, "Programmieren"); +#endif + if ( num == RT_TITLE_DEFI ) strcpy(text, "Challenges"); + if ( num == RT_TITLE_MISSION ) strcpy(text, "Missionen"); + if ( num == RT_TITLE_FREE ) strcpy(text, "Freestyle"); + if ( num == RT_TITLE_TEEN ) strcpy(text, "Freestyle"); + if ( num == RT_TITLE_USER ) strcpy(text, "Userlevels"); + if ( num == RT_TITLE_PROTO ) strcpy(text, "Prototypen"); + if ( num == RT_TITLE_SETUP ) strcpy(text, "Einstellungen"); + if ( num == RT_TITLE_NAME ) strcpy(text, "Name "); + if ( num == RT_TITLE_PERSO ) strcpy(text, "Aussehen einstellen"); + if ( num == RT_TITLE_WRITE ) strcpy(text, "Aktuelle Mission speichern"); + if ( num == RT_TITLE_READ ) strcpy(text, "Gespeicherte Mission laden"); + + if ( num == RT_PLAY_CHAPt ) strcpy(text, " Liste der Kapitel:"); + if ( num == RT_PLAY_CHAPd ) strcpy(text, " Liste der Kapitel:"); + if ( num == RT_PLAY_CHAPm ) strcpy(text, " Liste der Planeten:"); + if ( num == RT_PLAY_CHAPf ) strcpy(text, " Liste der Planeten:"); + if ( num == RT_PLAY_CHAPu ) strcpy(text, " Userlevels:"); + if ( num == RT_PLAY_CHAPp ) strcpy(text, " Liste der Planeten:"); + if ( num == RT_PLAY_CHAPte ) strcpy(text, " Liste der Kapitel:"); + if ( num == RT_PLAY_LISTt ) strcpy(text, " Liste der Übungen des Kapitels:"); + if ( num == RT_PLAY_LISTd ) strcpy(text, " Liste der Challenges des Kapitels:"); + if ( num == RT_PLAY_LISTm ) strcpy(text, " Liste der Missionen des Planeten:"); + if ( num == RT_PLAY_LISTf ) strcpy(text, " Liste der freien Levels des Planeten:"); + if ( num == RT_PLAY_LISTu ) strcpy(text, " Missionen des Userlevels:"); + if ( num == RT_PLAY_LISTp ) strcpy(text, " Liste der Prototypen des Planeten:"); + if ( num == RT_PLAY_LISTk ) strcpy(text, " Liste der freien Levels des Kapitel:"); + if ( num == RT_PLAY_RESUME ) strcpy(text, " Zusammenfassung:"); + + if ( num == RT_SETUP_DEVICE ) strcpy(text, " Driver:"); + if ( num == RT_SETUP_MODE ) strcpy(text, " Auflösung:"); + if ( num == RT_SETUP_KEY1 ) strcpy(text, "1) Klicken Sie auf die neu zu definierende Taste."); + if ( num == RT_SETUP_KEY2 ) strcpy(text, "2) Drücken Sie auf die neue Taste."); + + if ( num == RT_PERSO_FACE ) strcpy(text, "Kopf:"); + if ( num == RT_PERSO_GLASSES ) strcpy(text, "Brille:"); + if ( num == RT_PERSO_HAIR ) strcpy(text, "Haarfarbe:"); + if ( num == RT_PERSO_COMBI ) strcpy(text, "Farbe des Anzugs:"); + if ( num == RT_PERSO_BAND ) strcpy(text, "Farbe der Streifen:"); + +#if _NEWLOOK + if ( num == RT_DIALOG_TITLE ) strcpy(text, "CeeBot"); + if ( num == RT_DIALOG_QUIT ) strcpy(text, "Wollen Sie CeeBot schließen ?"); + if ( num == RT_DIALOG_YESQUIT ) strcpy(text, "Schließen\\CeeBot schließen"); +#else + if ( num == RT_DIALOG_TITLE ) strcpy(text, "COLOBOT"); + if ( num == RT_DIALOG_QUIT ) strcpy(text, "Wollen Sie COLOBOT schließen ?"); + if ( num == RT_DIALOG_YESQUIT ) strcpy(text, "Schließen\\COLOBOT schließen"); +#endif + if ( num == RT_DIALOG_ABORT ) strcpy(text, "Mission abbrechen ?"); + if ( num == RT_DIALOG_YES ) strcpy(text, "Abbrechen\\Mission abbrechen"); + if ( num == RT_DIALOG_NO ) strcpy(text, "Weitermachen\\Mission weitermachen"); + if ( num == RT_DIALOG_NOQUIT ) strcpy(text, "Weitermachen\\Weitermachen"); + if ( num == RT_DIALOG_DELOBJ ) strcpy(text, "Wollen Sie das angewählte Gebäude wirklich zerstören ?"); + if ( num == RT_DIALOG_DELGAME ) strcpy(text, "Wollen Sie die gespeicherten Missionen von %s löschen ?"); + if ( num == RT_DIALOG_YESDEL ) strcpy(text, "Zerstören"); + if ( num == RT_DIALOG_NODEL ) strcpy(text, "Abbrechen"); + if ( num == RT_DIALOG_LOADING ) strcpy(text, "Laden"); + + if ( num == RT_STUDIO_LISTTT ) strcpy(text, "Hilfe über den Begriff (\\key cbot;)"); + if ( num == RT_STUDIO_COMPOK ) strcpy(text, "Kompilieren OK (0 Fehler)"); + if ( num == RT_STUDIO_PROGSTOP ) strcpy(text, "Programm beendet"); + + if ( num == RT_SATCOM_LIST ) strcpy(text, "\\b;Liste der Objekte\n"); + if ( num == RT_SATCOM_BOT ) strcpy(text, "\\b;Liste der Roboter\n"); + if ( num == RT_SATCOM_BUILDING ) strcpy(text, "\\b;Listes der Gebäude\n"); + if ( num == RT_SATCOM_FRET ) strcpy(text, "\\b;Listes der tragbaren Gegenstände\n"); + if ( num == RT_SATCOM_ALIEN ) strcpy(text, "\\b;Listes der Feinde\n"); + if ( num == RT_SATCOM_NULL ) strcpy(text, "\\c; (keine)\\n;\n"); + if ( num == RT_SATCOM_ERROR1 ) strcpy(text, "\\b;Fehler\n"); + if ( num == RT_SATCOM_ERROR2 ) strcpy(text, "Die Liste ist ohne \\l;Radar\\u object\\radar; nicht verfügbar !\n"); + + if ( num == RT_IO_OPEN ) strcpy(text, "Öffnen"); + if ( num == RT_IO_SAVE ) strcpy(text, "Speichern"); + if ( num == RT_IO_LIST ) strcpy(text, "Ordner: %s"); + if ( num == RT_IO_NAME ) strcpy(text, "Name:"); + if ( num == RT_IO_DIR ) strcpy(text, "In:"); + if ( num == RT_IO_PRIVATE ) strcpy(text, "Privat\\Privater Ordner"); + if ( num == RT_IO_PUBLIC ) strcpy(text, "Öffentlich\\Gemeinsamer Ordner für alle Spieler"); + + if ( num == RT_GENERIC_DEV1 ) strcpy(text, "Entwickelt von:"); + if ( num == RT_GENERIC_DEV2 ) strcpy(text, "www.epsitec.com"); +#if _WG + if ( num == RT_GENERIC_EDIT1 ) strcpy(text, "Herausgegeben von:"); + if ( num == RT_GENERIC_EDIT2 ) strcpy(text, "www.wg-verlag.ch"); +#else + if ( num == RT_GENERIC_EDIT1 ) strcpy(text, " "); + if ( num == RT_GENERIC_EDIT2 ) strcpy(text, " "); +#endif + + if ( num == RT_INTERFACE_REC ) strcpy(text, "Recorder"); + } + + if ( type == RES_EVENT ) + { + if ( num == EVENT_BUTTON_OK ) strcpy(text, "OK"); + if ( num == EVENT_BUTTON_CANCEL ) strcpy(text, "Abbrechen"); + if ( num == EVENT_BUTTON_NEXT ) strcpy(text, "Nächster"); + if ( num == EVENT_BUTTON_PREV ) strcpy(text, "Vorherg."); + if ( num == EVENT_BUTTON_QUIT ) strcpy(text, "Menü (\\key quit;)"); + + if ( num == EVENT_DIALOG_OK ) strcpy(text, "OK"); + if ( num == EVENT_DIALOG_CANCEL ) strcpy(text, "Abbrechen"); + +#if _SCHOOL + if ( num == EVENT_INTERFACE_TRAINER) strcpy(text, "Übungen\\Programmierübungen"); +#else + if ( num == EVENT_INTERFACE_TRAINER) strcpy(text, "Programmieren\\Programmierübungen"); +#endif + if ( num == EVENT_INTERFACE_DEFI ) strcpy(text, "Challenges\\Herausforderungen"); + if ( num == EVENT_INTERFACE_MISSION) strcpy(text, "Missionen\\Aufbruch ins Weltall"); + if ( num == EVENT_INTERFACE_FREE ) strcpy(text, "Freestyle\\Freies Spielen ohne vorgegebenes Ziel"); + if ( num == EVENT_INTERFACE_TEEN ) strcpy(text, "Freestyle\\Freies Spielen ohne vorgegebenes Ziel"); + if ( num == EVENT_INTERFACE_USER ) strcpy(text, "User\\Userlevels"); + if ( num == EVENT_INTERFACE_PROTO ) strcpy(text, "Proto\\In Entwicklung befindliche Prototypen"); + if ( num == EVENT_INTERFACE_NAME ) strcpy(text, "Anderer Spieler\\Spielername ändern"); + if ( num == EVENT_INTERFACE_SETUP ) strcpy(text, "Einstellungen\\Einstellungen"); + if ( num == EVENT_INTERFACE_AGAIN ) strcpy(text, "Neu anfangen\\Die Mission von vorne anfangen"); + if ( num == EVENT_INTERFACE_WRITE ) strcpy(text, "Speichern\\Aktuelle Mission speichern"); + if ( num == EVENT_INTERFACE_READ ) strcpy(text, "Laden\\Eine gespeicherte Mission öffnen"); +#if _NEWLOOK + if ( num == EVENT_INTERFACE_ABORT ) strcpy(text, "\\Zurück zu CeeBot"); + if ( num == EVENT_INTERFACE_QUIT ) strcpy(text, "Schließen\\CeeBot schließen"); +#else + if ( num == EVENT_INTERFACE_ABORT ) strcpy(text, "\\Zurück zu COLOBOT"); + if ( num == EVENT_INTERFACE_QUIT ) strcpy(text, "Schließen\\COLOBOT schließen"); +#endif + if ( num == EVENT_INTERFACE_BACK ) strcpy(text, "<< Zurück \\Zurück zum Hauptmenü"); + if ( num == EVENT_INTERFACE_PLAY ) strcpy(text, "Spielen ...\\Los geht's"); + if ( num == EVENT_INTERFACE_SETUPd ) strcpy(text, "Bildschirm\\Driver und Bildschirmauflösung"); + if ( num == EVENT_INTERFACE_SETUPg ) strcpy(text, "Grafik\\Grafische Einstellungen"); + if ( num == EVENT_INTERFACE_SETUPp ) strcpy(text, "Spiel\\Gameplay Einstellungen"); + if ( num == EVENT_INTERFACE_SETUPc ) strcpy(text, "Steuerung\\Auswahl der Tasten"); + if ( num == EVENT_INTERFACE_SETUPs ) strcpy(text, "Geräusche\\Lautstärke Geräusche und Musik"); + if ( num == EVENT_INTERFACE_DEVICE ) strcpy(text, "Einheit"); + if ( num == EVENT_INTERFACE_RESOL ) strcpy(text, "Auflösung"); + if ( num == EVENT_INTERFACE_FULL ) strcpy(text, "Vollbildschirm\\Vollbildschirm oder Fenster"); + if ( num == EVENT_INTERFACE_APPLY ) strcpy(text, "Änderungen ausführen\\Getätigte Einstellungen ausführen"); + + if ( num == EVENT_INTERFACE_TOTO ) strcpy(text, "Robby\\Ihr Assistent"); + if ( num == EVENT_INTERFACE_SHADOW ) strcpy(text, "Schatten\\Schlagschatten auf dem Boden"); + if ( num == EVENT_INTERFACE_GROUND ) strcpy(text, "Markierungen\\Markierungen auf dem Boden"); + if ( num == EVENT_INTERFACE_DIRTY ) strcpy(text, "Schmutz\\Schmutz auf Robotern und Bauten"); + if ( num == EVENT_INTERFACE_FOG ) strcpy(text, "Nebel\\Nebelschwaden"); + if ( num == EVENT_INTERFACE_LENS ) strcpy(text, "Sonnenstrahlen\\Sonnenstrahlen"); + if ( num == EVENT_INTERFACE_SKY ) strcpy(text, "Himmel\\Himmel und Wolken"); + if ( num == EVENT_INTERFACE_PLANET ) strcpy(text, "Planeten und Sterne\\Kreisende Planeten und Sterne"); + if ( num == EVENT_INTERFACE_LIGHT ) strcpy(text, "Dynamische Beleuchtung\\Dynamische Beleuchtung"); + if ( num == EVENT_INTERFACE_PARTI ) strcpy(text, "Anzahl Partikel\\Explosionen, Staub, usw."); + if ( num == EVENT_INTERFACE_CLIP ) strcpy(text, "Sichtweite\\Maximale Sichtweite"); + if ( num == EVENT_INTERFACE_DETAIL ) strcpy(text, "Details\\Detailliertheit der Objekte in 3D"); + if ( num == EVENT_INTERFACE_TEXTURE) strcpy(text, "Qualität der Texturen\\Qualität der Anzeige"); + if ( num == EVENT_INTERFACE_GADGET ) strcpy(text, "Anzahl Ziergegenstände\\Anzahl Gegenstände ohne Funktion"); + if ( num == EVENT_INTERFACE_RAIN ) strcpy(text, "Partikel in den Menüs\\Funken und Sterne in den Menüs"); + if ( num == EVENT_INTERFACE_GLINT ) strcpy(text, "Glänzende Tasten\\Glänzende Tasten in den Menüs"); + if ( num == EVENT_INTERFACE_TOOLTIP) strcpy(text, "Hilfsblasen\\Hilfsblasen"); + if ( num == EVENT_INTERFACE_MOVIES ) strcpy(text, "Filme\\Filme vor und nach den Missionen"); + if ( num == EVENT_INTERFACE_NICERST) strcpy(text, "Zurücksetzen \\Kleine Show beim Zurücksetzen in den Übungen"); + if ( num == EVENT_INTERFACE_HIMSELF) strcpy(text, "Eigenbeschuss\\Ihre Einheiten werden von Ihren Waffen beschädigt."); + if ( num == EVENT_INTERFACE_SCROLL ) strcpy(text, "Kameradrehung mit der Maus\\Die Kamera dreht wenn die Maus den Rand erreicht"); + if ( num == EVENT_INTERFACE_INVERTX) strcpy(text, "Umkehr X\\Umkehr der Kameradrehung X-Achse"); + if ( num == EVENT_INTERFACE_INVERTY) strcpy(text, "Umkehr Y\\Umkehr der Kameradrehung Y-Achse"); + if ( num == EVENT_INTERFACE_EFFECT ) strcpy(text, "Beben bei Explosionen\\Die Kamera bebt bei Explosionen"); + if ( num == EVENT_INTERFACE_MOUSE ) strcpy(text, "Schatten unter der Maus\\Ein Schatten erscheint unter der Maus"); + if ( num == EVENT_INTERFACE_EDITMODE) strcpy(text, "Automatisches Einrücken\\Beim Bearbeiten der Programme"); + if ( num == EVENT_INTERFACE_EDITVALUE)strcpy(text, "Einrücken mit 4 Leerstellen\\Einrücken mit 2 oder 4 Leerstellen"); + if ( num == EVENT_INTERFACE_SOLUCE4) strcpy(text, "Lösung zugänglich\\Die Lösung ist im Programmslot \"4: Lösung\" zugänglich"); + + if ( num == EVENT_INTERFACE_KDEF ) strcpy(text, "Alles zurücksetzen\\Standarddefinition aller Tasten"); + if ( num == EVENT_INTERFACE_KLEFT ) strcpy(text, "Drehung nach links\\Steuer links"); + if ( num == EVENT_INTERFACE_KRIGHT ) strcpy(text, "Drehung nach rechts\\Steuer rechts"); + if ( num == EVENT_INTERFACE_KUP ) strcpy(text, "Vorwärts\\Bewegung nach vorne"); + if ( num == EVENT_INTERFACE_KDOWN ) strcpy(text, "Rückwärts\\Bewegung nach hinten"); + if ( num == EVENT_INTERFACE_KGUP ) strcpy(text, "Steigen\\Leistung des Triebwerks steigern"); + if ( num == EVENT_INTERFACE_KGDOWN ) strcpy(text, "Sinken\\Leistung des Triebwerks drosseln"); + if ( num == EVENT_INTERFACE_KCAMERA) strcpy(text, "Andere Kamera\\Sichtpunkt einstellen"); + if ( num == EVENT_INTERFACE_KDESEL ) strcpy(text, "Vorherg. Auswahl\\Das vorhergehende Objekt auswählen"); + if ( num == EVENT_INTERFACE_KACTION) strcpy(text, "Standardhandlung\\Führt die Standardhandlung des Roboters aus."); + if ( num == EVENT_INTERFACE_KNEAR ) strcpy(text, "Kamera näher\\Bewegung der Kamera vorwärts"); + if ( num == EVENT_INTERFACE_KAWAY ) strcpy(text, "Kamera weiter\\Bewegung der Kamera rückwärts"); + if ( num == EVENT_INTERFACE_KNEXT ) strcpy(text, "Nächstes auswählen\\Nächstes Objekt auswählen"); + if ( num == EVENT_INTERFACE_KHUMAN ) strcpy(text, "Astronauten auswählen\\Astronauten auswählen"); + if ( num == EVENT_INTERFACE_KQUIT ) strcpy(text, "Mission verlassen\\Eine Mission oder Übung verlassen"); + if ( num == EVENT_INTERFACE_KHELP ) strcpy(text, "Anweisungen\\Anweisungen für die Mission oder Übung"); + if ( num == EVENT_INTERFACE_KPROG ) strcpy(text, "Hilfe CBOT-Sprache\\Hilfe über die Programmiersprache CBOT"); + if ( num == EVENT_INTERFACE_KCBOT ) strcpy(text, "Hilfe über Begriff\\Hilfe über einen Begriff"); + if ( num == EVENT_INTERFACE_KVISIT ) strcpy(text, "Ort der Meldung\\Zeigt den Ort, von dem die letzte Meldung stammt"); + if ( num == EVENT_INTERFACE_KSPEED10) strcpy(text, "Geschwindigkeit 1.0x\\Normale Spielgeschwindigkeit"); + if ( num == EVENT_INTERFACE_KSPEED15) strcpy(text, "Geschwindigkeit 1.5x\\Spielgeschwindigkeit anderthalb Mal schneller"); + if ( num == EVENT_INTERFACE_KSPEED20) strcpy(text, "Geschwindigkeit 2.0x\\Spielgeschwindigkeit doppelt so schnell"); + if ( num == EVENT_INTERFACE_KSPEED30) strcpy(text, "Geschwindigkeit 3.0x\\Spielgeschwindigkeit drei Mal schneller"); + + if ( num == EVENT_INTERFACE_VOLSOUND) strcpy(text, "Geräusche:\\Lautstärke Motoren, Stimmen, usw."); + if ( num == EVENT_INTERFACE_VOLMUSIC) strcpy(text, "Geräuschkulisse:\\Lautstärke der Soundtracks der CD"); + if ( num == EVENT_INTERFACE_SOUND3D) strcpy(text, "3D-Geräusche\\Orten der Geräusche im Raum"); + + if ( num == EVENT_INTERFACE_MIN ) strcpy(text, "Min.\\Minimale Qualität (großes Framerate)"); + if ( num == EVENT_INTERFACE_NORM ) strcpy(text, "Normal\\Standardqualität"); + if ( num == EVENT_INTERFACE_MAX ) strcpy(text, "Max.\\Beste Qualität (niedriges Framerate)"); + + if ( num == EVENT_INTERFACE_SILENT ) strcpy(text, "Kein Ton\\Keine Geräusche und Geräuschkulisse"); + if ( num == EVENT_INTERFACE_NOISY ) strcpy(text, "Normal\\Normale Lautstärke"); + + if ( num == EVENT_INTERFACE_JOYSTICK) strcpy(text, "Joystick\\Joystick oder Tastatur"); + if ( num == EVENT_INTERFACE_SOLUCE ) strcpy(text, "Zeigt die Lösung\\Zeigt nach 3mal Scheitern die Lösung"); + + if ( num == EVENT_INTERFACE_NEDIT ) strcpy(text, "\\Name des Spielers"); + if ( num == EVENT_INTERFACE_NOK ) strcpy(text, "OK\\Spieler auswählen"); + if ( num == EVENT_INTERFACE_NCANCEL) strcpy(text, "Abbrechen\\Behält den bisherigen Spieler bei"); + if ( num == EVENT_INTERFACE_NDELETE) strcpy(text, "Spieler löschen\\Löscht den Spieler aus der Liste"); + if ( num == EVENT_INTERFACE_NLABEL ) strcpy(text, "Name "); + + if ( num == EVENT_INTERFACE_IOWRITE) strcpy(text, "Speichern\\Speichert die Mission"); + if ( num == EVENT_INTERFACE_IOREAD ) strcpy(text, "Laden\\Öffnet eine gespeicherte Mission"); + if ( num == EVENT_INTERFACE_IOLIST ) strcpy(text, "Liste der gespeicherten Missionen"); + if ( num == EVENT_INTERFACE_IOLABEL) strcpy(text, "Dateiname:"); + if ( num == EVENT_INTERFACE_IONAME ) strcpy(text, "Name der Mission"); + if ( num == EVENT_INTERFACE_IOIMAGE) strcpy(text, "Ansicht der Mission"); + if ( num == EVENT_INTERFACE_IODELETE) strcpy(text, "Löschen\\Löscht die gespeicherte Mission"); + + if ( num == EVENT_INTERFACE_PERSO ) strcpy(text, "Aussehen\\Erscheinungsbild des Astronauten einstellen"); + if ( num == EVENT_INTERFACE_POK ) strcpy(text, "OK"); + if ( num == EVENT_INTERFACE_PCANCEL) strcpy(text, "Abbrechen"); + if ( num == EVENT_INTERFACE_PDEF ) strcpy(text, "Standard\\Standardfarben einsetzen"); + if ( num == EVENT_INTERFACE_PHEAD ) strcpy(text, "Kopf\\Gesicht und Haare"); + if ( num == EVENT_INTERFACE_PBODY ) strcpy(text, "Anzug\\Raumfahrtanzug"); + if ( num == EVENT_INTERFACE_PLROT ) strcpy(text, "\\Drehung links"); + if ( num == EVENT_INTERFACE_PRROT ) strcpy(text, "\\Drehung rechts"); + if ( num == EVENT_INTERFACE_PCRa ) strcpy(text, "Rot"); + if ( num == EVENT_INTERFACE_PCGa ) strcpy(text, "Grün"); + if ( num == EVENT_INTERFACE_PCBa ) strcpy(text, "Blau"); + if ( num == EVENT_INTERFACE_PCRb ) strcpy(text, "Rot"); + if ( num == EVENT_INTERFACE_PCGb ) strcpy(text, "Grün"); + if ( num == EVENT_INTERFACE_PCBb ) strcpy(text, "Blau"); + if ( num == EVENT_INTERFACE_PFACE1 ) strcpy(text, "\\Kopf 1"); + if ( num == EVENT_INTERFACE_PFACE2 ) strcpy(text, "\\Kopf 4"); + if ( num == EVENT_INTERFACE_PFACE3 ) strcpy(text, "\\Kopf 3"); + if ( num == EVENT_INTERFACE_PFACE4 ) strcpy(text, "\\Kopf 2"); + if ( num == EVENT_INTERFACE_PGLASS0) strcpy(text, "\\Keine Brille"); + if ( num == EVENT_INTERFACE_PGLASS1) strcpy(text, "\\Brille 1"); + if ( num == EVENT_INTERFACE_PGLASS2) strcpy(text, "\\Brille 2"); + if ( num == EVENT_INTERFACE_PGLASS3) strcpy(text, "\\Brille 3"); + if ( num == EVENT_INTERFACE_PGLASS4) strcpy(text, "\\Brille 4"); + if ( num == EVENT_INTERFACE_PGLASS5) strcpy(text, "\\Brille 5"); + + if ( num == EVENT_OBJECT_DESELECT ) strcpy(text, "Vorherg. Auwahl (\\key desel;)"); + if ( num == EVENT_OBJECT_LEFT ) strcpy(text, "Drehung links (\\key left;)"); + if ( num == EVENT_OBJECT_RIGHT ) strcpy(text, "Drehung rechts (\\key right;)"); + if ( num == EVENT_OBJECT_UP ) strcpy(text, "Vorwärts (\\key up;)"); + if ( num == EVENT_OBJECT_DOWN ) strcpy(text, "Rückwärts (\\key down;)"); + if ( num == EVENT_OBJECT_GASUP ) strcpy(text, "Steigt (\\key gup;)"); + if ( num == EVENT_OBJECT_GASDOWN ) strcpy(text, "Sinkt (\\key gdown;)"); + if ( num == EVENT_OBJECT_HTAKE ) strcpy(text, "Nehmen oder hinlegen (\\key action;)"); + if ( num == EVENT_OBJECT_MTAKE ) strcpy(text, "Nehmen oder hinlegen (\\key action;)"); + if ( num == EVENT_OBJECT_MFRONT ) strcpy(text, "..vorne"); + if ( num == EVENT_OBJECT_MBACK ) strcpy(text, "..hinten"); + if ( num == EVENT_OBJECT_MPOWER ) strcpy(text, "..Batterie"); + if ( num == EVENT_OBJECT_BHELP ) strcpy(text, "Anweisungen über die Mission(\\key help;)"); + if ( num == EVENT_OBJECT_BTAKEOFF ) strcpy(text, "Abheben nach vollbrachter Mission"); + if ( num == EVENT_OBJECT_BDERRICK ) strcpy(text, "Baut einen Bohrturm"); + if ( num == EVENT_OBJECT_BSTATION ) strcpy(text, "Baut ein Kraftwerk"); + if ( num == EVENT_OBJECT_BFACTORY ) strcpy(text, "Baut eine Roboterfabrik"); + if ( num == EVENT_OBJECT_BREPAIR ) strcpy(text, "Baut ein Reparaturzentrum"); + if ( num == EVENT_OBJECT_BCONVERT ) strcpy(text, "Baut einen Konverter"); + if ( num == EVENT_OBJECT_BTOWER ) strcpy(text, "Baut einen Geschützturm"); + if ( num == EVENT_OBJECT_BRESEARCH ) strcpy(text, "Baut ein Forschungszentrum"); + if ( num == EVENT_OBJECT_BRADAR ) strcpy(text, "Baut ein Radar"); + if ( num == EVENT_OBJECT_BENERGY ) strcpy(text, "Baut eine Batteriefabrik"); + if ( num == EVENT_OBJECT_BLABO ) strcpy(text, "Baut ein automatisches Labor"); + if ( num == EVENT_OBJECT_BNUCLEAR ) strcpy(text, "Baut eine Brennstoffzellenfabrik"); + if ( num == EVENT_OBJECT_BPARA ) strcpy(text, "Baut einen Blitzableiter"); + if ( num == EVENT_OBJECT_BINFO ) strcpy(text, "Baut einen Infoserver"); + if ( num == EVENT_OBJECT_GFLAT ) strcpy(text, "Zeigt ob der Boden eben ist"); + if ( num == EVENT_OBJECT_FCREATE ) strcpy(text, "Setzt eine Fahne"); + if ( num == EVENT_OBJECT_FDELETE ) strcpy(text, "Sammelt die Fahne ein"); + if ( num == EVENT_OBJECT_FCOLORb ) strcpy(text, "\\Blaue Fahne"); + if ( num == EVENT_OBJECT_FCOLORr ) strcpy(text, "\\Rote Fahne"); + if ( num == EVENT_OBJECT_FCOLORg ) strcpy(text, "\\Grüne Fahne"); + if ( num == EVENT_OBJECT_FCOLORy ) strcpy(text, "\\Gelbe Fahne"); + if ( num == EVENT_OBJECT_FCOLORv ) strcpy(text, "\\Violette Fahne"); + if ( num == EVENT_OBJECT_FACTORYfa ) strcpy(text, "Baut einen Jettransporter"); + if ( num == EVENT_OBJECT_FACTORYta ) strcpy(text, "Baut einen Kettentransporter"); + if ( num == EVENT_OBJECT_FACTORYwa ) strcpy(text, "Baut einen Radtransporter"); + if ( num == EVENT_OBJECT_FACTORYia ) strcpy(text, "Baut einen Krabbeltransporter"); + if ( num == EVENT_OBJECT_FACTORYfc ) strcpy(text, "Baut einen Jetshooter"); + if ( num == EVENT_OBJECT_FACTORYtc ) strcpy(text, "Baut einen Kettenshooter"); + if ( num == EVENT_OBJECT_FACTORYwc ) strcpy(text, "Baut einen Radshooter"); + if ( num == EVENT_OBJECT_FACTORYic ) strcpy(text, "Baut einen Krabbelshooter"); + if ( num == EVENT_OBJECT_FACTORYfi ) strcpy(text, "Baut einen Jetorgashooter"); + if ( num == EVENT_OBJECT_FACTORYti ) strcpy(text, "Baut einen Kettenorgashooter"); + if ( num == EVENT_OBJECT_FACTORYwi ) strcpy(text, "Baut einen Radorgashooter"); + if ( num == EVENT_OBJECT_FACTORYii ) strcpy(text, "Baut einen Krabbelorgashooter"); + if ( num == EVENT_OBJECT_FACTORYfs ) strcpy(text, "Baut einen Jetschnüffler"); + if ( num == EVENT_OBJECT_FACTORYts ) strcpy(text, "Baut einen Kettenschnüffler"); + if ( num == EVENT_OBJECT_FACTORYws ) strcpy(text, "Baut einen Radschnüffler"); + if ( num == EVENT_OBJECT_FACTORYis ) strcpy(text, "Baut einen Krabbelschnüffler"); + if ( num == EVENT_OBJECT_FACTORYrt ) strcpy(text, "Baut einen Stampfer"); + if ( num == EVENT_OBJECT_FACTORYrc ) strcpy(text, "Baut einen Phazershooter"); + if ( num == EVENT_OBJECT_FACTORYrr ) strcpy(text, "Baut einen Recycler"); + if ( num == EVENT_OBJECT_FACTORYrs ) strcpy(text, "Baut einen Schutzschild"); + if ( num == EVENT_OBJECT_FACTORYsa ) strcpy(text, "Baut einen Kettentaucher"); + if ( num == EVENT_OBJECT_RTANK ) strcpy(text, "Forschungsprogramm Kettenantrieb"); + if ( num == EVENT_OBJECT_RFLY ) strcpy(text, "Forschungsprogramm Jetantrieb"); + if ( num == EVENT_OBJECT_RTHUMP ) strcpy(text, "Forschungsprogramm Stampfer"); + if ( num == EVENT_OBJECT_RCANON ) strcpy(text, "Forschungsprogramm Shooterkanone"); + if ( num == EVENT_OBJECT_RTOWER ) strcpy(text, "Forschungsprogramm Geschützturm"); + if ( num == EVENT_OBJECT_RPHAZER ) strcpy(text, "Forschungsprogramm Phazerkanone"); + if ( num == EVENT_OBJECT_RSHIELD ) strcpy(text, "Forschungsprogramm Schutzschild"); + if ( num == EVENT_OBJECT_RATOMIC ) strcpy(text, "Forschungsprogramm Brennstoffzelle"); + if ( num == EVENT_OBJECT_RiPAW ) strcpy(text, "Forschungsprogramm Krabbelantrieb"); + if ( num == EVENT_OBJECT_RiGUN ) strcpy(text, "Forschungsprogramm Orgashooterkanone"); + if ( num == EVENT_OBJECT_RESET ) strcpy(text, "Alles zurücksetzen"); + if ( num == EVENT_OBJECT_SEARCH ) strcpy(text, "Schnüffeln (\\key action;)"); + if ( num == EVENT_OBJECT_TERRAFORM ) strcpy(text, "Stampfen (\\key action;)"); + if ( num == EVENT_OBJECT_FIRE ) strcpy(text, "Feuer (\\key action;)"); + if ( num == EVENT_OBJECT_RECOVER ) strcpy(text, "Recyceln (\\key action;)"); + if ( num == EVENT_OBJECT_BEGSHIELD ) strcpy(text, "Schutzschild ausfahren (\\key action;)"); + if ( num == EVENT_OBJECT_ENDSHIELD ) strcpy(text, "Schutzschild einholen (\\key action;)"); + if ( num == EVENT_OBJECT_DIMSHIELD ) strcpy(text, "Reichweite Schutzschild"); + if ( num == EVENT_OBJECT_PROGRUN ) strcpy(text, "Gewähltes Programm ausführen"); + if ( num == EVENT_OBJECT_PROGEDIT ) strcpy(text, "Gewähltes Programm bearbeiten"); + if ( num == EVENT_OBJECT_INFOOK ) strcpy(text, "\\SatCom in Standby"); + if ( num == EVENT_OBJECT_DELETE ) strcpy(text, "Gebäude sprengen"); + if ( num == EVENT_OBJECT_GENERGY ) strcpy(text, "Energievorrat"); + if ( num == EVENT_OBJECT_GSHIELD ) strcpy(text, "Schäden"); + if ( num == EVENT_OBJECT_GRANGE ) strcpy(text, "Triebwerktemperatur"); + if ( num == EVENT_OBJECT_GPROGRESS ) strcpy(text, "Prozess im Gang ..."); + if ( num == EVENT_OBJECT_GRADAR ) strcpy(text, "Anzahl erfasster Insekten"); + if ( num == EVENT_OBJECT_GINFO ) strcpy(text, "Gesendete Informationen"); + if ( num == EVENT_OBJECT_COMPASS ) strcpy(text, "Kompass"); +//? if ( num == EVENT_OBJECT_MAP ) strcpy(text, "Minikarte"); + if ( num == EVENT_OBJECT_MAPZOOM ) strcpy(text, "Zoom Minikarte"); + if ( num == EVENT_OBJECT_CAMERA ) strcpy(text, "Kamera (\\key camera;)"); + if ( num == EVENT_OBJECT_CAMERAleft) strcpy(text, "Kamera links"); + if ( num == EVENT_OBJECT_CAMERAright) strcpy(text, "Kamera rechts"); + if ( num == EVENT_OBJECT_CAMERAnear) strcpy(text, "Kamera näher"); + if ( num == EVENT_OBJECT_CAMERAaway) strcpy(text, "Kamera weiter weg"); + if ( num == EVENT_OBJECT_HELP ) strcpy(text, "Anweisungen über das ausgewählte Objekt"); + if ( num == EVENT_OBJECT_SOLUCE ) strcpy(text, "Zeigt die Lösung"); + if ( num == EVENT_OBJECT_SHORTCUT00) strcpy(text, "Anzeige Roboter <-> Bauten"); + if ( num == EVENT_OBJECT_LIMIT ) strcpy(text, "Zeigt die Reichweite"); + if ( num == EVENT_OBJECT_PEN0 ) strcpy(text, "\\Bleistift abheben"); + if ( num == EVENT_OBJECT_PEN1 ) strcpy(text, "\\Schwarzen Bleistift hinunterlassen"); + if ( num == EVENT_OBJECT_PEN2 ) strcpy(text, "\\Gelben Bleistift hinunterlassen"); + if ( num == EVENT_OBJECT_PEN3 ) strcpy(text, "\\Orangefarbenen Bleistift hinunterlassen"); + if ( num == EVENT_OBJECT_PEN4 ) strcpy(text, "\\Roten Bleistift hinunterlassen"); + if ( num == EVENT_OBJECT_PEN5 ) strcpy(text, "\\Violetten Bleistift hinunterlassen"); + if ( num == EVENT_OBJECT_PEN6 ) strcpy(text, "\\Blauen Bleistift hinunterlassen"); + if ( num == EVENT_OBJECT_PEN7 ) strcpy(text, "\\Grünen Bleistift hinunterlassen"); + if ( num == EVENT_OBJECT_PEN8 ) strcpy(text, "\\Braunen Bleistift hinunterlassen"); + if ( num == EVENT_OBJECT_REC ) strcpy(text, "\\Aufnahme starten"); + if ( num == EVENT_OBJECT_STOP ) strcpy(text, "\\Aufnahme stoppen"); + if ( num == EVENT_DT_VISIT0 || + num == EVENT_DT_VISIT1 || + num == EVENT_DT_VISIT2 || + num == EVENT_DT_VISIT3 || + num == EVENT_DT_VISIT4 ) strcpy(text, "Zeigt den Ort"); + if ( num == EVENT_DT_END ) strcpy(text, "Weitermachen"); + if ( num == EVENT_CMD ) strcpy(text, "Befehleingabe"); + if ( num == EVENT_SPEED ) strcpy(text, "Spielgeschwindigkeit"); + + if ( num == EVENT_HYPER_PREV ) strcpy(text, "Vorherg. Seite"); + if ( num == EVENT_HYPER_NEXT ) strcpy(text, "Nächste Seite"); + if ( num == EVENT_HYPER_HOME ) strcpy(text, "Home"); + if ( num == EVENT_HYPER_COPY ) strcpy(text, "Kopieren"); + if ( num == EVENT_HYPER_SIZE1 ) strcpy(text, "Größe 1"); + if ( num == EVENT_HYPER_SIZE2 ) strcpy(text, "Größe 2"); + if ( num == EVENT_HYPER_SIZE3 ) strcpy(text, "Größe 3"); + if ( num == EVENT_HYPER_SIZE4 ) strcpy(text, "Größe 4"); + if ( num == EVENT_HYPER_SIZE5 ) strcpy(text, "Größe 5"); + if ( num == EVENT_SATCOM_HUSTON ) strcpy(text, "Anweisungen von Houston"); +#if _TEEN + if ( num == EVENT_SATCOM_SAT ) strcpy(text, "Wörterbuch Englisch-Deutsch"); +#else + if ( num == EVENT_SATCOM_SAT ) strcpy(text, "Satellitenbericht"); +#endif + if ( num == EVENT_SATCOM_LOADING ) strcpy(text, "Von Houston übermittelte Programme"); + if ( num == EVENT_SATCOM_OBJECT ) strcpy(text, "Liste der Objekte"); + if ( num == EVENT_SATCOM_PROG ) strcpy(text, "Hilfe über Programmieren"); + if ( num == EVENT_SATCOM_SOLUCE ) strcpy(text, "Lösung"); + + if ( num == EVENT_STUDIO_OK ) strcpy(text, "OK\\Programm kompilieren"); + if ( num == EVENT_STUDIO_CANCEL ) strcpy(text, "Abbrechen\\Editor schließen"); + if ( num == EVENT_STUDIO_NEW ) strcpy(text, "Neu"); + if ( num == EVENT_STUDIO_OPEN ) strcpy(text, "Öffnen (Ctrl+o)"); + if ( num == EVENT_STUDIO_SAVE ) strcpy(text, "Speichern (Ctrl+s)"); + if ( num == EVENT_STUDIO_UNDO ) strcpy(text, "Widerrufen (Ctrl+z)"); + if ( num == EVENT_STUDIO_CUT ) strcpy(text, "Ausschneiden (Ctrl+x)"); + if ( num == EVENT_STUDIO_COPY ) strcpy(text, "Kopieren (Ctrl+c)"); + if ( num == EVENT_STUDIO_PASTE ) strcpy(text, "Einfügen (Ctrl+v)"); + if ( num == EVENT_STUDIO_SIZE ) strcpy(text, "Zeichengröße"); + if ( num == EVENT_STUDIO_TOOL ) strcpy(text, "Anweisungen (\\key help;)"); + if ( num == EVENT_STUDIO_HELP ) strcpy(text, "Hilfe über Programmieren (\\key prog;)"); + if ( num == EVENT_STUDIO_COMPILE ) strcpy(text, "Kompilieren"); + if ( num == EVENT_STUDIO_RUN ) strcpy(text, "Start/Stop"); + if ( num == EVENT_STUDIO_REALTIME ) strcpy(text, "Pause/Weitermachen"); + if ( num == EVENT_STUDIO_STEP ) strcpy(text, "Ein Schritt"); + } + + if ( type == RES_OBJECT ) + { + if ( num == OBJECT_PORTICO ) strcpy(text, "Träger"); + if ( num == OBJECT_BASE ) strcpy(text, "Raumschiff"); + if ( num == OBJECT_DERRICK ) strcpy(text, "Bohrturm"); + if ( num == OBJECT_FACTORY ) strcpy(text, "Roboterfabrik"); + if ( num == OBJECT_REPAIR ) strcpy(text, "Reparaturzentrum"); + if ( num == OBJECT_DESTROYER ) strcpy(text, "Einstampfer"); + if ( num == OBJECT_STATION ) strcpy(text, "Kraftwerk"); + if ( num == OBJECT_CONVERT ) strcpy(text, "Konverter Erz-Titan"); + if ( num == OBJECT_TOWER ) strcpy(text, "Geschützturm"); + if ( num == OBJECT_NEST ) strcpy(text, "Orgastoffquelle"); + if ( num == OBJECT_RESEARCH ) strcpy(text, "Forschungszentrum"); + if ( num == OBJECT_RADAR ) strcpy(text, "Radar"); + if ( num == OBJECT_INFO ) strcpy(text, "Infoserver"); +#if _TEEN + if ( num == OBJECT_ENERGY ) strcpy(text, "Auflöser"); +#else + if ( num == OBJECT_ENERGY ) strcpy(text, "Batteriefabrik"); +#endif + if ( num == OBJECT_LABO ) strcpy(text, "Automatisches Labor"); + if ( num == OBJECT_NUCLEAR ) strcpy(text, "Brennstoffzellenfabrik"); + if ( num == OBJECT_PARA ) strcpy(text, "Blitzableiter"); + if ( num == OBJECT_SAFE ) strcpy(text, "Bunker"); + if ( num == OBJECT_HUSTON ) strcpy(text, "Kontrollzentrum"); + if ( num == OBJECT_TARGET1 ) strcpy(text, "Zielscheibe"); + if ( num == OBJECT_TARGET2 ) strcpy(text, "Zielscheibe"); + if ( num == OBJECT_START ) strcpy(text, "Startfläche"); + if ( num == OBJECT_END ) strcpy(text, "Zielfläche"); + if ( num == OBJECT_STONE ) strcpy(text, "Titanerz"); + if ( num == OBJECT_URANIUM ) strcpy(text, "Platinerz"); + if ( num == OBJECT_BULLET ) strcpy(text, "Orgastoff"); + if ( num == OBJECT_METAL ) strcpy(text, "Titan"); + if ( num == OBJECT_POWER ) strcpy(text, "Elektrolytische Batterie"); + if ( num == OBJECT_ATOMIC ) strcpy(text, "Brennstoffzelle"); + if ( num == OBJECT_BBOX ) strcpy(text, "Flugschreiber"); + if ( num == OBJECT_KEYa ) strcpy(text, "Schlüssel A"); + if ( num == OBJECT_KEYb ) strcpy(text, "Schlüssel B"); + if ( num == OBJECT_KEYc ) strcpy(text, "Schlüssel C"); + if ( num == OBJECT_KEYd ) strcpy(text, "Schlüssel D"); + if ( num == OBJECT_TNT ) strcpy(text, "Sprengstoff"); + if ( num == OBJECT_BOMB ) strcpy(text, "Landmine"); + if ( num == OBJECT_BAG ) strcpy(text, "Überlebenskit"); + if ( num == OBJECT_WAYPOINT ) strcpy(text, "Checkpoint"); + if ( num == OBJECT_FLAGb ) strcpy(text, "Blaue Fahne"); + if ( num == OBJECT_FLAGr ) strcpy(text, "Rote Fahne"); + if ( num == OBJECT_FLAGg ) strcpy(text, "Grüne Fahne"); + if ( num == OBJECT_FLAGy ) strcpy(text, "Gelbe Fahne"); + if ( num == OBJECT_FLAGv ) strcpy(text, "Violette Fahne"); + if ( num == OBJECT_MARKPOWER ) strcpy(text, "Markierung für unterirdische Energiequelle"); + if ( num == OBJECT_MARKURANIUM ) strcpy(text, "Markierung für unterirdisches Platinvorkommen"); + if ( num == OBJECT_MARKKEYa ) strcpy(text, "Markierung für vergrabenen Schlüssel A"); + if ( num == OBJECT_MARKKEYb ) strcpy(text, "Markierung für vergrabenen Schlüssel B"); + if ( num == OBJECT_MARKKEYc ) strcpy(text, "Markierung für vergrabenen Schlüssel C"); + if ( num == OBJECT_MARKKEYd ) strcpy(text, "Markierung für vergrabenen Schlüssel D"); + if ( num == OBJECT_MARKSTONE ) strcpy(text, "Markierung für unterirdisches Titanvorkommen"); + if ( num == OBJECT_MOBILEft ) strcpy(text, "Übungsroboter"); + if ( num == OBJECT_MOBILEtt ) strcpy(text, "Übungsroboter"); + if ( num == OBJECT_MOBILEwt ) strcpy(text, "Übungsroboter"); + if ( num == OBJECT_MOBILEit ) strcpy(text, "Übungsroboter"); + if ( num == OBJECT_MOBILEfa ) strcpy(text, "Transporter"); + if ( num == OBJECT_MOBILEta ) strcpy(text, "Transporter"); + if ( num == OBJECT_MOBILEwa ) strcpy(text, "Transporter"); + if ( num == OBJECT_MOBILEia ) strcpy(text, "Transporter"); + if ( num == OBJECT_MOBILEfc ) strcpy(text, "Shooter"); + if ( num == OBJECT_MOBILEtc ) strcpy(text, "Shooter"); + if ( num == OBJECT_MOBILEwc ) strcpy(text, "Shooter"); + if ( num == OBJECT_MOBILEic ) strcpy(text, "Shooter"); + if ( num == OBJECT_MOBILEfi ) strcpy(text, "OrgaShooter"); + if ( num == OBJECT_MOBILEti ) strcpy(text, "OrgaShooter"); + if ( num == OBJECT_MOBILEwi ) strcpy(text, "OrgaShooter"); + if ( num == OBJECT_MOBILEii ) strcpy(text, "OrgaShooter"); + if ( num == OBJECT_MOBILEfs ) strcpy(text, "Schnüffler"); + if ( num == OBJECT_MOBILEts ) strcpy(text, "Schnüffler"); + if ( num == OBJECT_MOBILEws ) strcpy(text, "Schnüffler"); + if ( num == OBJECT_MOBILEis ) strcpy(text, "Schnüffler"); + if ( num == OBJECT_MOBILErt ) strcpy(text, "Stampfer"); + if ( num == OBJECT_MOBILErc ) strcpy(text, "Phazershooter"); + if ( num == OBJECT_MOBILErr ) strcpy(text, "Recycler"); + if ( num == OBJECT_MOBILErs ) strcpy(text, "Schutzschild"); + if ( num == OBJECT_MOBILEsa ) strcpy(text, "Kettentaucher"); + if ( num == OBJECT_MOBILEtg ) strcpy(text, "Mobile Zielscheibe"); + if ( num == OBJECT_MOBILEdr ) strcpy(text, "Zeichner"); + if ( num == OBJECT_HUMAN ) strcpy(text, g_gamerName); + if ( num == OBJECT_TECH ) strcpy(text, "Techniker"); + if ( num == OBJECT_TOTO ) strcpy(text, "Robby"); + if ( num == OBJECT_MOTHER ) strcpy(text, "Insektenkönigin"); + if ( num == OBJECT_ANT ) strcpy(text, "Ameise"); + if ( num == OBJECT_SPIDER ) strcpy(text, "Spinne"); + if ( num == OBJECT_BEE ) strcpy(text, "Wespe"); + if ( num == OBJECT_WORM ) strcpy(text, "Wurm"); + if ( num == OBJECT_EGG ) strcpy(text, "Ei"); + if ( num == OBJECT_RUINmobilew1 ) strcpy(text, "Roboterwrack"); + if ( num == OBJECT_RUINmobilew2 ) strcpy(text, "Roboterwrack"); + if ( num == OBJECT_RUINmobilet1 ) strcpy(text, "Roboterwrack"); + if ( num == OBJECT_RUINmobilet2 ) strcpy(text, "Roboterwrack"); + if ( num == OBJECT_RUINmobiler1 ) strcpy(text, "Roboterwrack"); + if ( num == OBJECT_RUINmobiler2 ) strcpy(text, "Roboterwrack"); + if ( num == OBJECT_RUINfactory ) strcpy(text, "Gebäuderuine"); + if ( num == OBJECT_RUINdoor ) strcpy(text, "Gebäuderuine"); + if ( num == OBJECT_RUINsupport ) strcpy(text, "Abfall"); + if ( num == OBJECT_RUINradar ) strcpy(text, "Gebäuderuine"); + if ( num == OBJECT_RUINconvert ) strcpy(text, "Gebäuderuine"); + if ( num == OBJECT_RUINbase ) strcpy(text, "Raumschiffruine"); + if ( num == OBJECT_RUINhead ) strcpy(text, "Raumschiffruine"); + if ( num == OBJECT_APOLLO1 || + num == OBJECT_APOLLO3 || + num == OBJECT_APOLLO4 || + num == OBJECT_APOLLO5 ) strcpy(text, "Überreste einer Apollo-Mission"); + if ( num == OBJECT_APOLLO2 ) strcpy(text, "Lunar Roving Vehicle"); + } + + if ( type == RES_ERR ) + { + strcpy(text, "Fehler"); + if ( num == ERR_CMD ) strcpy(text, "Befehl unbekannt"); +#if _NEWLOOK + if ( num == ERR_INSTALL ) strcpy(text, "CeeBot wurde nicht installiert."); + if ( num == ERR_NOCD ) strcpy(text, "Legen Sie die CeeBot-CD ein\nund starten Sie das Spiel neu."); +#else + if ( num == ERR_INSTALL ) strcpy(text, "COLOBOT wurde nicht installiert."); + if ( num == ERR_NOCD ) strcpy(text, "Legen Sie die COLOBOT-CD ein\nund starten Sie das Spiel neu."); +#endif + if ( num == ERR_MANIP_VEH ) strcpy(text, "Roboter ungeeignet"); + if ( num == ERR_MANIP_FLY ) strcpy(text, "Im Flug unmöglich"); + if ( num == ERR_MANIP_BUSY ) strcpy(text, "Trägt schon etwas"); + if ( num == ERR_MANIP_NIL ) strcpy(text, "Nichts zu ergreifen"); + if ( num == ERR_MANIP_MOTOR ) strcpy(text, "In Fahrt unmöglich"); + if ( num == ERR_MANIP_OCC ) strcpy(text, "Stelle schon besetzt"); + if ( num == ERR_MANIP_FRIEND ) strcpy(text, "Kein anderer Roboter"); + if ( num == ERR_MANIP_RADIO ) strcpy(text, "Sie können keinen radioaktiven Gegenstand tragen"); + if ( num == ERR_MANIP_WATER ) strcpy(text, "Sie können unter Wasser nichts tragen"); + if ( num == ERR_MANIP_EMPTY ) strcpy(text, "Nichts abzulegen"); + if ( num == ERR_BUILD_FLY ) strcpy(text, "Im Flug unmöglich"); + if ( num == ERR_BUILD_WATER ) strcpy(text, "Unter Wasser unmöglich"); + if ( num == ERR_BUILD_ENERGY ) strcpy(text, "Nicht genug Energie"); + if ( num == ERR_BUILD_METALAWAY ) strcpy(text, "Titan zu weit weg"); + if ( num == ERR_BUILD_METALNEAR ) strcpy(text, "Titan zu nahe"); + if ( num == ERR_BUILD_METALINEX ) strcpy(text, "Kein Titan vorhanden"); + if ( num == ERR_BUILD_FLAT ) strcpy(text, "Boden nicht eben genug"); + if ( num == ERR_BUILD_FLATLIT ) strcpy(text, "Ebener Boden nicht groß genug"); + if ( num == ERR_BUILD_BUSY ) strcpy(text, "Stelle schon besetzt"); + if ( num == ERR_BUILD_BASE ) strcpy(text, "Zu nahe am Raumschiff"); + if ( num == ERR_BUILD_NARROW ) strcpy(text, "Zu nahe an einem Gebäude"); + if ( num == ERR_BUILD_MOTOR ) strcpy(text, "In Fahrt unmöglich"); + if ( num == ERR_SEARCH_FLY ) strcpy(text, "Im Flug unmöglich"); + if ( num == ERR_SEARCH_VEH ) strcpy(text, "Roboter ungeeignet"); + if ( num == ERR_SEARCH_MOTOR ) strcpy(text, "In Fahrt unmöglich"); + if ( num == ERR_TERRA_VEH ) strcpy(text, "Roboter ungeeignet"); + if ( num == ERR_TERRA_ENERGY ) strcpy(text, "Nicht genug Energie"); + if ( num == ERR_TERRA_FLOOR ) strcpy(text, "Boden ungeeignet"); + if ( num == ERR_TERRA_BUILDING ) strcpy(text, "Gebäude zu nahe"); + if ( num == ERR_TERRA_OBJECT ) strcpy(text, "Gegenstand zu nahe"); + if ( num == ERR_RECOVER_VEH ) strcpy(text, "Roboter ungeeignet"); + if ( num == ERR_RECOVER_ENERGY ) strcpy(text, "Nicht genug Energie"); + if ( num == ERR_RECOVER_NULL ) strcpy(text, "Nichts zu recyceln"); + if ( num == ERR_SHIELD_VEH ) strcpy(text, "Roboter ungeeignet"); + if ( num == ERR_SHIELD_ENERGY ) strcpy(text, "Keine Energie mehr"); +//? if ( num == ERR_COM ) strcpy(text, "Kommunikationsproblem mit dem Roboter"); + if ( num == ERR_MOVE_IMPOSSIBLE ) strcpy(text, "Ziel kann nicht erreicht werden"); + if ( num == ERR_FIND_IMPOSSIBLE ) strcpy(text, "Das Objekt existiert nicht"); + if ( num == ERR_GOTO_IMPOSSIBLE ) strcpy(text, "Ziel kann nicht erreicht werden"); + if ( num == ERR_GOTO_ITER ) strcpy(text, "Ziel kann nicht erreicht werden"); + if ( num == ERR_GOTO_BUSY ) strcpy(text, "Ziel ist schon besetzt"); + if ( num == ERR_FIRE_VEH ) strcpy(text, "Roboter ungeeignet"); + if ( num == ERR_FIRE_ENERGY ) strcpy(text, "Nicht genug Energie"); + if ( num == ERR_FIRE_FLY ) strcpy(text, "Im Flug unmöglich"); + if ( num == ERR_CONVERT_EMPTY ) strcpy(text, "Kein konvertierbares Titanerz vorhanden"); + if ( num == ERR_DERRICK_NULL ) strcpy(text, "Keine unterirdische Erzlagerstätte"); + if ( num == ERR_STATION_NULL ) strcpy(text, "Kein unterirdisches Energievorkommen"); + if ( num == ERR_TOWER_POWER ) strcpy(text, "Keine Batterie"); + if ( num == ERR_TOWER_ENERGY ) strcpy(text, "Keine Energie mehr"); + if ( num == ERR_RESEARCH_POWER ) strcpy(text, "Keine Batterie"); + if ( num == ERR_RESEARCH_ENERGY ) strcpy(text, "Nicht mehr genug Energie"); + if ( num == ERR_RESEARCH_TYPE ) strcpy(text, "Falscher Batterietyp"); + if ( num == ERR_RESEARCH_ALREADY) strcpy(text, "Forschungsprogramm schon ausgeführt"); + if ( num == ERR_ENERGY_NULL ) strcpy(text, "Kein unterirdisches Energievorkommen"); + if ( num == ERR_ENERGY_LOW ) strcpy(text, "Noch nicht genug Energie"); + if ( num == ERR_ENERGY_EMPTY ) strcpy(text, "Kein konvertierbares Titanerz vorhanden"); + if ( num == ERR_ENERGY_BAD ) strcpy(text, "Wandelt nur Titanerz um"); + if ( num == ERR_BASE_DLOCK ) strcpy(text, "Die Türen werden von einem Gegenstand blockiert"); + if ( num == ERR_BASE_DHUMAN ) strcpy(text, "Gehen Sie an Bord, bevor Sie abheben"); + if ( num == ERR_LABO_NULL ) strcpy(text, "Nichts zu analysieren"); + if ( num == ERR_LABO_BAD ) strcpy(text, "Analysiert nur Orgastoff"); + if ( num == ERR_LABO_ALREADY ) strcpy(text, "Analyse schon durchgeführt"); + if ( num == ERR_NUCLEAR_NULL ) strcpy(text, "Kein unterirdisches Energievorkommen"); + if ( num == ERR_NUCLEAR_LOW ) strcpy(text, "Noch nicht genug Energie"); + if ( num == ERR_NUCLEAR_EMPTY ) strcpy(text, "Kein konvertierbares Platin"); + if ( num == ERR_NUCLEAR_BAD ) strcpy(text, "Wandelt nur Platin um"); + if ( num == ERR_FACTORY_NULL ) strcpy(text, "Kein Titan vorhanden"); + if ( num == ERR_FACTORY_NEAR ) strcpy(text, "Ein Gegenstand ist zu nahe"); + if ( num == ERR_RESET_NEAR ) strcpy(text, "Stelle schon besetzt"); + if ( num == ERR_INFO_NULL ) strcpy(text, "Kein Infoserver in Reichweite"); + if ( num == ERR_VEH_VIRUS ) strcpy(text, "Ein Programm wurde von einem Virus infiziert"); + if ( num == ERR_BAT_VIRUS ) strcpy(text, "Von Virus infiziert, zeitweise außer Betrieb"); + if ( num == ERR_VEH_POWER ) strcpy(text, "Keine Batterie"); + if ( num == ERR_VEH_ENERGY ) strcpy(text, "Keine Energie mehr"); + if ( num == ERR_FLAG_FLY ) strcpy(text, "Im Flug unmöglich"); + if ( num == ERR_FLAG_WATER ) strcpy(text, "Im Wasser unmöglich"); + if ( num == ERR_FLAG_MOTOR ) strcpy(text, "Beim Gehen unmöglich"); + if ( num == ERR_FLAG_BUSY ) strcpy(text, "Unmöglich wenn Sie etwas tragen"); + if ( num == ERR_FLAG_CREATE ) strcpy(text, "Zu viele Fahnen dieser Farbe (Maximum 5)"); + if ( num == ERR_FLAG_PROXY ) strcpy(text, "Zu nahe an einer anderen Fahne"); + if ( num == ERR_FLAG_DELETE ) strcpy(text, "Keine Fahne in Reichweite"); + if ( num == ERR_MISSION_NOTERM ) strcpy(text, "Mission noch nicht beendet (Drücken Sie auf \\key help; für weitere Informationen)"); + if ( num == ERR_DELETEMOBILE ) strcpy(text, "Roboter zerstört"); + if ( num == ERR_DELETEBUILDING ) strcpy(text, "Gebäude zerstört"); + if ( num == ERR_TOOMANY ) strcpy(text, "Kein neues Objekt kann erstellt werden (zu viele vorhanden)"); + if ( num == ERR_OBLIGATORYTOKEN ) strcpy(text, "Es fehlt \"%s\" in Ihrem Programm"); + if ( num == ERR_PROHIBITEDTOKEN ) strcpy(text, "In dieser Übung verboten"); + + if ( num == INFO_BUILD ) strcpy(text, "Gebäude fertiggestellt"); + if ( num == INFO_CONVERT ) strcpy(text, "Titan verfügbar"); + if ( num == INFO_RESEARCH ) strcpy(text, "Forschungsprogramm abgeschlossen"); + if ( num == INFO_RESEARCHTANK ) strcpy(text, "Herstellung eines Roboters mit Kettenantrieb möglich"); + if ( num == INFO_RESEARCHFLY ) strcpy(text, "Sie können jetzt mit den Tasten \\key gup; und \\key gdown; fliegen"); + if ( num == INFO_RESEARCHTHUMP ) strcpy(text, "Herstellung eines Stampfers möglich"); + if ( num == INFO_RESEARCHCANON ) strcpy(text, "Herstellung eines Shooters möglich"); + if ( num == INFO_RESEARCHTOWER ) strcpy(text, "Errichtung eines Geschützturms möglich"); + if ( num == INFO_RESEARCHPHAZER ) strcpy(text, "Herstellung eines Phazershooters möglich"); + if ( num == INFO_RESEARCHSHIELD ) strcpy(text, "Herstellung eines Schutzschildes möglich"); + if ( num == INFO_RESEARCHATOMIC ) strcpy(text, "Errichtung einer Brennstoffzellenfabrik möglich"); + if ( num == INFO_FACTORY ) strcpy(text, "Neuer Roboter verfügbar"); + if ( num == INFO_LABO ) strcpy(text, "Analyse vollendet"); + if ( num == INFO_ENERGY ) strcpy(text, "Batterie verfügbar"); + if ( num == INFO_NUCLEAR ) strcpy(text, "Brennstoffzelle verfügbar"); + if ( num == INFO_FINDING ) strcpy(text, "Sie haben ein brauchbares Objekt gefunden"); + if ( num == INFO_MARKPOWER ) strcpy(text, "Geeignete Stelle für Kraftwerk gefunden"); + if ( num == INFO_MARKURANIUM ) strcpy(text, "Geeignete Stelle für Bohrturm gefunden"); + if ( num == INFO_MARKSTONE ) strcpy(text, "Geeignete Stelle für Bohrturm gefunden"); + if ( num == INFO_MARKKEYa ) strcpy(text, "Geeignete Stelle für Bohrturm gefunden"); + if ( num == INFO_MARKKEYb ) strcpy(text, "Geeignete Stelle für Bohrturm gefunden"); + if ( num == INFO_MARKKEYc ) strcpy(text, "Geeignete Stelle für Bohrturm gefunden"); + if ( num == INFO_MARKKEYd ) strcpy(text, "Geeignete Stelle für Bohrturm gefunden"); + if ( num == INFO_WIN ) strcpy(text, "<<< Bravo, Mission vollendet >>>"); + if ( num == INFO_LOST ) strcpy(text, "<<< Mission gescheitert >>>"); + if ( num == INFO_LOSTq ) strcpy(text, "<<< Mission gescheitert >>>"); + if ( num == INFO_WRITEOK ) strcpy(text, "Mission gespeichert"); + if ( num == INFO_DELETEPATH ) strcpy(text, "Checkpoint erreicht"); + if ( num == INFO_DELETEMOTHER ) strcpy(text, "Insektenkönigin tödlich verwundet"); + if ( num == INFO_DELETEANT ) strcpy(text, "Ameise tödlich verwundet"); + if ( num == INFO_DELETEBEE ) strcpy(text, "Wespe tödlich verwundet"); + if ( num == INFO_DELETEWORM ) strcpy(text, "Wurm tödlich verwundet"); + if ( num == INFO_DELETESPIDER ) strcpy(text, "Spinne tödlich verwundet"); + if ( num == INFO_BEGINSATCOM ) strcpy(text, "Beziehen Sie sich auf Ihren SatCom, indem Sie auf \\key help; drücken"); + } + + if ( type == RES_CBOT ) + { + strcpy(text, "Fehler"); + if ( num == TX_OPENPAR ) strcpy(text, "Es fehlt eine offene Klammer ""("""); + if ( num == TX_CLOSEPAR ) strcpy(text, "Es fehlt eine geschlossene Klammer "")"""); + if ( num == TX_NOTBOOL ) strcpy(text, "Der Ausdruck muss einen boolschen Wert ergeben"); + if ( num == TX_UNDEFVAR ) strcpy(text, "Variable nicht deklariert"); + if ( num == TX_BADLEFT ) strcpy(text, "Zuweisung unmöglich"); + if ( num == TX_ENDOF ) strcpy(text, "Es fehlt ein Strichpunkt "";"" am Ende der Anweisung"); + if ( num == TX_OUTCASE ) strcpy(text, "Anweisung ""case"" ohne vorhergehende Anweisung ""switch"""); + if ( num == TX_NOTERM ) strcpy(text, "Hier ist eine Anweisung nach dem Ende des Programms"); + if ( num == TX_CLOSEBLK ) strcpy(text, "Es fehlt eine geschlossene geschweifte Klammer ""}"" (Ende des Blocks)"); + if ( num == TX_ELSEWITHOUTIF ) strcpy(text, "Anweisung ""else"" ohne vorhergehende Anweisung ""if"""); + if ( num == TX_OPENBLK ) strcpy(text, "Es fehlt eine offene geschweifte Klammer""{"""); + if ( num == TX_BADTYPE ) strcpy(text, "Der Ausdruck ergibt einen falschen Typ für die Zuweisung"); + if ( num == TX_REDEFVAR ) strcpy(text, "Eine Variable wird zum zweiten Mal deklariert"); + if ( num == TX_BAD2TYPE ) strcpy(text, "Die zwei Operanden sind nicht kompatibel"); + if ( num == TX_UNDEFCALL ) strcpy(text, "Unbekannte Funktion"); + if ( num == TX_MISDOTS ) strcpy(text, "Es fehlt ein Doppelpunkt "" : """); + if ( num == TX_WHILE ) strcpy(text, "Es fehlt das Wort ""while"""); + if ( num == TX_BREAK ) strcpy(text, "Anweisung ""break"" außerhalb einer Schleife"); + if ( num == TX_LABEL ) strcpy(text, "Ein Label kann nur vor den Anweisungen ""for"", ""while"", ""do"" oder ""switch"" vorkommen"); + if ( num == TX_NOLABEL ) strcpy(text, "Dieses Label existiert nicht"); + if ( num == TX_NOCASE ) strcpy(text, "Es fehlt eine Anweisung ""case"""); + if ( num == TX_BADNUM ) strcpy(text, "Es fehlt eine Zahl"); + if ( num == TX_VOID ) strcpy(text, "Parameter void"); + if ( num == TX_NOTYP ) strcpy(text, "Hier muss ein Variablentyp stehen"); + if ( num == TX_NOVAR ) strcpy(text, "Es fehlt der Name einer Variable"); + if ( num == TX_NOFONC ) strcpy(text, "Hier muss der Name der Funktion stehen"); + if ( num == TX_OVERPARAM ) strcpy(text, "Zu viele Parameter"); + if ( num == TX_REDEF ) strcpy(text, "Diese Funktion gibt es schon"); + if ( num == TX_LOWPARAM ) strcpy(text, "Nicht genug Parameter"); + if ( num == TX_BADPARAM ) strcpy(text, "Keine Funktion mit diesem Namen verträgt Parameter diesen Typs"); + if ( num == TX_NUMPARAM ) strcpy(text, "Keine Funktion mit diesem Namen verträgt diese Anzahl Parameter"); + if ( num == TX_NOITEM ) strcpy(text, "Dieses Element gibt es nicht in dieser Klasse"); + if ( num == TX_DOT ) strcpy(text, "Das Objekt ist nicht eine Instanz einer Klasse"); + if ( num == TX_NOCONST ) strcpy(text, "Es gibt keinen geeigneten Konstruktor"); + if ( num == TX_REDEFCLASS ) strcpy(text, "Diese Klasse gibt es schon"); + if ( num == TX_CLBRK ) strcpy(text, "Es fehlt eine geschlossene eckige Klammer "" ] """); + if ( num == TX_RESERVED ) strcpy(text, "Dieses Wort ist reserviert"); + if ( num == TX_BADNEW ) strcpy(text, "Falsche Argumente für ""new"""); + if ( num == TX_OPBRK ) strcpy(text, "Es fehlt eine offene eckige Klammer "" [ """); + if ( num == TX_BADSTRING ) strcpy(text, "Hier wird eine Zeichenkette erwartet"); + if ( num == TX_BADINDEX ) strcpy(text, "Falscher Typ für einen Index"); + if ( num == TX_PRIVATE ) strcpy(text, "Geschütztes Element (private)"); + if ( num == TX_NOPUBLIC ) strcpy(text, "Hier muss das Wort ""public"" stehen"); + if ( num == TX_DIVZERO ) strcpy(text, "Teilung durch Null"); + if ( num == TX_NOTINIT ) strcpy(text, "Der Wert dieser Variable wurde nicht definiert"); + if ( num == TX_BADTHROW ) strcpy(text, "Negativer Wert ungeeignet für Anweisung ""throw"""); + if ( num == TX_NORETVAL ) strcpy(text, "Die Funktion hat kein Ergebnis zurückgegeben"); + if ( num == TX_NORUN ) strcpy(text, "Keine Funktion wird ausgeführt"); + if ( num == TX_NOCALL ) strcpy(text, "Die aufgerufene Funktion existiert nicht"); + if ( num == TX_NOCLASS ) strcpy(text, "Diese Klasse existiert nicht"); + if ( num == TX_NULLPT ) strcpy(text, "Das Objekt existiert nicht"); + if ( num == TX_OPNAN ) strcpy(text, "Operation mit dem Wert ""nan"""); + if ( num == TX_OUTARRAY ) strcpy(text, "Zugriff im Array außerhalb der Grenzen"); + if ( num == TX_STACKOVER ) strcpy(text, "Stack overflow"); + if ( num == TX_DELETEDPT ) strcpy(text, "Objekt nicht verfügbar"); + if ( num == TX_FILEOPEN ) strcpy(text, "Die Datei kann nicht geöffnet werden"); + if ( num == TX_NOTOPEN ) strcpy(text, "Die Datei wurde nicht geöffnet"); + if ( num == TX_ERRREAD ) strcpy(text, "Fehler beim Lesezugriff"); + if ( num == TX_ERRWRITE ) strcpy(text, "Fehler beim Schreibzugriff"); + } + + if ( type == RES_KEY ) + { + if ( num == 0 ) strcpy(text, "< keine >"); + if ( num == VK_LEFT ) strcpy(text, "Pfeiltaste links"); + if ( num == VK_RIGHT ) strcpy(text, "Pfeiltaste rechts"); + if ( num == VK_UP ) strcpy(text, "Pfeil nach oben"); + if ( num == VK_DOWN ) strcpy(text, "Pfeil nach unten"); + if ( num == VK_CANCEL ) strcpy(text, "Ctrl-Break"); + if ( num == VK_BACK ) strcpy(text, "<--"); + if ( num == VK_TAB ) strcpy(text, "Tab"); + if ( num == VK_CLEAR ) strcpy(text, "Clear"); + if ( num == VK_RETURN ) strcpy(text, "Eingabe"); + if ( num == VK_SHIFT ) strcpy(text, "Shift"); + if ( num == VK_CONTROL ) strcpy(text, "Ctrl"); + if ( num == VK_MENU ) strcpy(text, "Alt"); + if ( num == VK_PAUSE ) strcpy(text, "Pause"); + if ( num == VK_CAPITAL ) strcpy(text, "Caps Lock"); + if ( num == VK_ESCAPE ) strcpy(text, "Esc"); + if ( num == VK_SPACE ) strcpy(text, "Leertaste"); + if ( num == VK_PRIOR ) strcpy(text, "Page Up"); + if ( num == VK_NEXT ) strcpy(text, "Page Down"); + if ( num == VK_END ) strcpy(text, "End"); + if ( num == VK_HOME ) strcpy(text, "Home"); + if ( num == VK_SELECT ) strcpy(text, "Select"); + if ( num == VK_EXECUTE ) strcpy(text, "Execute"); + if ( num == VK_SNAPSHOT ) strcpy(text, "Print Scrn"); + if ( num == VK_INSERT ) strcpy(text, "Insert"); + if ( num == VK_DELETE ) strcpy(text, "Delete"); + if ( num == VK_HELP ) strcpy(text, "Help"); + if ( num == VK_LWIN ) strcpy(text, "Left Windows"); + if ( num == VK_RWIN ) strcpy(text, "Right Windows"); + if ( num == VK_APPS ) strcpy(text, "Application key"); + if ( num == VK_NUMPAD0 ) strcpy(text, "NumPad 0"); + if ( num == VK_NUMPAD1 ) strcpy(text, "NumPad 1"); + if ( num == VK_NUMPAD2 ) strcpy(text, "NumPad 2"); + if ( num == VK_NUMPAD3 ) strcpy(text, "NumPad 3"); + if ( num == VK_NUMPAD4 ) strcpy(text, "NumPad 4"); + if ( num == VK_NUMPAD5 ) strcpy(text, "NumPad 5"); + if ( num == VK_NUMPAD6 ) strcpy(text, "NumPad 6"); + if ( num == VK_NUMPAD7 ) strcpy(text, "NumPad 7"); + if ( num == VK_NUMPAD8 ) strcpy(text, "NumPad 8"); + if ( num == VK_NUMPAD9 ) strcpy(text, "NumPad 9"); + if ( num == VK_MULTIPLY ) strcpy(text, "NumPad *"); + if ( num == VK_ADD ) strcpy(text, "NumPad +"); + if ( num == VK_SEPARATOR ) strcpy(text, "NumPad sep"); + if ( num == VK_SUBTRACT ) strcpy(text, "NumPad -"); + if ( num == VK_DECIMAL ) strcpy(text, "NumPad ."); + if ( num == VK_DIVIDE ) strcpy(text, "NumPad /"); + if ( num == VK_F1 ) strcpy(text, "F1"); + if ( num == VK_F2 ) strcpy(text, "F2"); + if ( num == VK_F3 ) strcpy(text, "F3"); + if ( num == VK_F4 ) strcpy(text, "F4"); + if ( num == VK_F5 ) strcpy(text, "F5"); + if ( num == VK_F6 ) strcpy(text, "F6"); + if ( num == VK_F7 ) strcpy(text, "F7"); + if ( num == VK_F8 ) strcpy(text, "F8"); + if ( num == VK_F9 ) strcpy(text, "F9"); + if ( num == VK_F10 ) strcpy(text, "F10"); + if ( num == VK_F11 ) strcpy(text, "F11"); + if ( num == VK_F12 ) strcpy(text, "F12"); + if ( num == VK_F13 ) strcpy(text, "F13"); + if ( num == VK_F14 ) strcpy(text, "F14"); + if ( num == VK_F15 ) strcpy(text, "F15"); + if ( num == VK_F16 ) strcpy(text, "F16"); + if ( num == VK_F17 ) strcpy(text, "F17"); + if ( num == VK_F18 ) strcpy(text, "F18"); + if ( num == VK_F19 ) strcpy(text, "F19"); + if ( num == VK_F20 ) strcpy(text, "F20"); + if ( num == VK_NUMLOCK ) strcpy(text, "Num Lock"); + if ( num == VK_SCROLL ) strcpy(text, "Scroll"); + if ( num == VK_ATTN ) strcpy(text, "Attn"); + if ( num == VK_CRSEL ) strcpy(text, "CrSel"); + if ( num == VK_EXSEL ) strcpy(text, "ExSel"); + if ( num == VK_EREOF ) strcpy(text, "Erase EOF"); + if ( num == VK_PLAY ) strcpy(text, "Play"); + if ( num == VK_ZOOM ) strcpy(text, "Zoom"); + if ( num == VK_PA1 ) strcpy(text, "PA1"); + if ( num == VK_OEM_CLEAR ) strcpy(text, "Clear"); + if ( num == VK_BUTTON1 ) strcpy(text, "Knopf 1"); + if ( num == VK_BUTTON2 ) strcpy(text, "Knopf 2"); + if ( num == VK_BUTTON3 ) strcpy(text, "Knopf 3"); + if ( num == VK_BUTTON4 ) strcpy(text, "Knopf 4"); + if ( num == VK_BUTTON5 ) strcpy(text, "Knopf 5"); + if ( num == VK_BUTTON6 ) strcpy(text, "Knopf 6"); + if ( num == VK_BUTTON7 ) strcpy(text, "Knopf 7"); + if ( num == VK_BUTTON8 ) strcpy(text, "Knopf 8"); + if ( num == VK_BUTTON9 ) strcpy(text, "Knopf 9"); + if ( num == VK_BUTTON10 ) strcpy(text, "Knopf 10"); + if ( num == VK_BUTTON11 ) strcpy(text, "Knopf 11"); + if ( num == VK_BUTTON12 ) strcpy(text, "Knopf 12"); + if ( num == VK_BUTTON13 ) strcpy(text, "Knopf 13"); + if ( num == VK_BUTTON14 ) strcpy(text, "Knopf 14"); + if ( num == VK_BUTTON15 ) strcpy(text, "Knopf 15"); + if ( num == VK_BUTTON16 ) strcpy(text, "Knopf 16"); + if ( num == VK_BUTTON17 ) strcpy(text, "Knopf 17"); + if ( num == VK_BUTTON18 ) strcpy(text, "Knopf 18"); + if ( num == VK_BUTTON19 ) strcpy(text, "Knopf 19"); + if ( num == VK_BUTTON20 ) strcpy(text, "Knopf 20"); + if ( num == VK_BUTTON21 ) strcpy(text, "Knopf 21"); + if ( num == VK_BUTTON22 ) strcpy(text, "Knopf 22"); + if ( num == VK_BUTTON23 ) strcpy(text, "Knopf 23"); + if ( num == VK_BUTTON24 ) strcpy(text, "Knopf 24"); + if ( num == VK_BUTTON25 ) strcpy(text, "Knopf 25"); + if ( num == VK_BUTTON26 ) strcpy(text, "Knopf 26"); + if ( num == VK_BUTTON27 ) strcpy(text, "Knopf 27"); + if ( num == VK_BUTTON28 ) strcpy(text, "Knopf 28"); + if ( num == VK_BUTTON29 ) strcpy(text, "Knopf 29"); + if ( num == VK_BUTTON30 ) strcpy(text, "Knopf 30"); + if ( num == VK_BUTTON31 ) strcpy(text, "Knopf 31"); + if ( num == VK_BUTTON32 ) strcpy(text, "Knopf 32"); + if ( num == VK_WHEELUP ) strcpy(text, "Mausrad nach vorne"); + if ( num == VK_WHEELDOWN ) strcpy(text, "Mausrad zurück"); + } +#endif + +#if _POLISH + if ( type == RES_TEXT ) + { + #if _FULL + if ( num == RT_VERSION_ID ) strcpy(text, "Wersja 1.18 /pl"); + #endif + #if _NET + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A 1.18"); + #endif + #if _SCHOOL & _EDU + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen EDU 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A EDU 1.18"); + #endif + #endif + #if _SCHOOL & _PERSO + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen PERSO 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A PERSO 1.18"); + #endif + #endif + #if _SCHOOL & _CEEBOTDEMO + #if _TEEN + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-Teen DEMO 1.18"); + #else + if ( num == RT_VERSION_ID ) strcpy(text, "CeeBot-A DEMO 1.18"); + #endif + #endif + #if _DEMO + if ( num == RT_VERSION_ID ) strcpy(text, "Demo 1.18 /pl"); + #endif + if ( num == RT_DISINFO_TITLE ) strcpy(text, "SatCom"); + if ( num == RT_WINDOW_MAXIMIZED ) strcpy(text, "Powiêksz"); + if ( num == RT_WINDOW_MINIMIZED ) strcpy(text, "Pomniejsz"); + if ( num == RT_WINDOW_STANDARD ) strcpy(text, "Normalna wielkoœæ"); + if ( num == RT_WINDOW_CLOSE ) strcpy(text, "Zamknij"); + + if ( num == RT_STUDIO_TITLE ) strcpy(text, "Edytor programu"); + if ( num == RT_SCRIPT_NEW ) strcpy(text, "Nowy"); + if ( num == RT_NAME_DEFAULT ) strcpy(text, "Gracz"); + if ( num == RT_IO_NEW ) strcpy(text, "Nowy ..."); + if ( num == RT_KEY_OR ) strcpy(text, " lub "); + +#if _NEWLOOK + if ( num == RT_TITLE_BASE ) strcpy(text, "CeeBot"); + if ( num == RT_TITLE_INIT ) strcpy(text, "CeeBot"); +#else + if ( num == RT_TITLE_BASE ) strcpy(text, "COLOBOT"); + if ( num == RT_TITLE_INIT ) strcpy(text, "COLOBOT"); +#endif + if ( num == RT_TITLE_TRAINER ) strcpy(text, "Æwiczenia programistyczne"); + if ( num == RT_TITLE_DEFI ) strcpy(text, "Wyzwania"); + if ( num == RT_TITLE_MISSION ) strcpy(text, "Misje"); + if ( num == RT_TITLE_FREE ) strcpy(text, "Swobodna gra"); + if ( num == RT_TITLE_TEEN ) strcpy(text, "Swobodna gra"); + if ( num == RT_TITLE_USER ) strcpy(text, "Poziomy u¿ytkownika"); + if ( num == RT_TITLE_PROTO ) strcpy(text, "Prototypy"); + if ( num == RT_TITLE_SETUP ) strcpy(text, "Opcje"); + if ( num == RT_TITLE_NAME ) strcpy(text, "Imiê gracza"); + if ( num == RT_TITLE_PERSO ) strcpy(text, "Dostosuj wygl¹d"); + if ( num == RT_TITLE_WRITE ) strcpy(text, "Zapisz bie¿¹c¹ misjê"); + if ( num == RT_TITLE_READ ) strcpy(text, "Wczytaj zapisan¹ misjê"); + + if ( num == RT_PLAY_CHAPt ) strcpy(text, " Rozdzia³y:"); + if ( num == RT_PLAY_CHAPd ) strcpy(text, " Rozdzia³y:"); + if ( num == RT_PLAY_CHAPm ) strcpy(text, " Planety:"); + if ( num == RT_PLAY_CHAPf ) strcpy(text, " Planety:"); + if ( num == RT_PLAY_CHAPu ) strcpy(text, " Poziomy u¿ytkownika:"); + if ( num == RT_PLAY_CHAPp ) strcpy(text, " Planety:"); + if ( num == RT_PLAY_CHAPte ) strcpy(text, " Planety:"); + if ( num == RT_PLAY_LISTt ) strcpy(text, " Æwiczenia w tym rozdziale:"); + if ( num == RT_PLAY_LISTd ) strcpy(text, " Wyzwania w tym rozdziale:"); + if ( num == RT_PLAY_LISTm ) strcpy(text, " Misje na tej planecie:"); + if ( num == RT_PLAY_LISTf ) strcpy(text, " Swobodna gra na tej planecie:"); + if ( num == RT_PLAY_LISTu ) strcpy(text, " Misje na tym poziomie:"); + if ( num == RT_PLAY_LISTp ) strcpy(text, " Prototypy na tej planecie:"); + if ( num == RT_PLAY_LISTk ) strcpy(text, " Prototypy na tej planecie:"); + if ( num == RT_PLAY_RESUME ) strcpy(text, " Streszczenie:"); + + if ( num == RT_SETUP_DEVICE ) strcpy(text, " Sterowniki:"); + if ( num == RT_SETUP_MODE ) strcpy(text, " Rozdzielczoœæ:"); + if ( num == RT_SETUP_KEY1 ) strcpy(text, "1) Najpierw kliknij klawisz, który chcesz przedefiniowaæ."); + if ( num == RT_SETUP_KEY2 ) strcpy(text, "2) Nastêpnie naciœnij klawisz, którego chcesz u¿ywaæ."); + + if ( num == RT_PERSO_FACE ) strcpy(text, "Rodzaj twarzy:"); + if ( num == RT_PERSO_GLASSES ) strcpy(text, "Okulary:"); + if ( num == RT_PERSO_HAIR ) strcpy(text, "Kolor w³osów:"); + if ( num == RT_PERSO_COMBI ) strcpy(text, "Kolor skafandra:"); + if ( num == RT_PERSO_BAND ) strcpy(text, "Kolor pasków:"); + +#if _NEWLOOK + if ( num == RT_DIALOG_TITLE ) strcpy(text, "CeeBot"); + if ( num == RT_DIALOG_QUIT ) strcpy(text, "Czy na pewno chcesz opuœciæ grê CeeBot?"); + if ( num == RT_DIALOG_YESQUIT ) strcpy(text, "Zakoñcz\\Koñczy grê CeeBot"); +#else + if ( num == RT_DIALOG_TITLE ) strcpy(text, "COLOBOT"); + if ( num == RT_DIALOG_QUIT ) strcpy(text, "Czy na pewno chcesz opuœciæ grê COLOBOT?"); + if ( num == RT_DIALOG_YESQUIT ) strcpy(text, "Zakoñcz\\Koñczy grê COLOBOT"); +#endif + if ( num == RT_DIALOG_ABORT ) strcpy(text, "Opuœciæ misjê?"); + if ( num == RT_DIALOG_YES ) strcpy(text, "Przerwij\\Przerywa bie¿¹c¹ misjê"); + if ( num == RT_DIALOG_NO ) strcpy(text, "Kontynuuj\\Kontynuuje bie¿¹c¹ misjê"); + if ( num == RT_DIALOG_NOQUIT ) strcpy(text, "Kontynuuj\\Kontynuuje grê"); + if ( num == RT_DIALOG_DELOBJ ) strcpy(text, "Czy na pewno chcesz zniszczyæ zaznaczony budynek?"); + if ( num == RT_DIALOG_DELGAME ) strcpy(text, "Czy na pewno chcesz skasowaæ zapisane gry gracza %s? "); + if ( num == RT_DIALOG_YESDEL ) strcpy(text, "Usuñ"); + if ( num == RT_DIALOG_NODEL ) strcpy(text, "Anuluj"); + if ( num == RT_DIALOG_LOADING ) strcpy(text, "WCZYTYWANIE"); + + if ( num == RT_STUDIO_LISTTT ) strcpy(text, "Skróty klawiszowe (\\key cbot;)"); + if ( num == RT_STUDIO_COMPOK ) strcpy(text, "Program skompilowany (0 b³êdów)"); + if ( num == RT_STUDIO_PROGSTOP ) strcpy(text, "Program zakoñczony"); + + if ( num == RT_SATCOM_LIST ) strcpy(text, "\\b;Lista obiektów\n"); + if ( num == RT_SATCOM_BOT ) strcpy(text, "\\b;Roboty\n"); + if ( num == RT_SATCOM_BUILDING ) strcpy(text, "\\b;Budynki\n"); + if ( num == RT_SATCOM_FRET ) strcpy(text, "\\b;Obiekty ruchome\n"); + if ( num == RT_SATCOM_ALIEN ) strcpy(text, "\\b;Obcy\n"); + if ( num == RT_SATCOM_NULL ) strcpy(text, "\\c; (brak)\\n;\n"); + if ( num == RT_SATCOM_ERROR1 ) strcpy(text, "\\b;B³¹d\n"); + if ( num == RT_SATCOM_ERROR2 ) strcpy(text, "Lista jest dostêpna jedynie gdy dzia³a \\l;stacja radarowa\\u object\\radar;.\n"); + + if ( num == RT_IO_OPEN ) strcpy(text, "Otwórz"); + if ( num == RT_IO_SAVE ) strcpy(text, "Zapisz"); + if ( num == RT_IO_LIST ) strcpy(text, "Folder: %s"); + if ( num == RT_IO_NAME ) strcpy(text, "Nazwa:"); + if ( num == RT_IO_DIR ) strcpy(text, "Folder:"); + if ( num == RT_IO_PRIVATE ) strcpy(text, "Prywatny\\Folder prywatny"); + if ( num == RT_IO_PUBLIC ) strcpy(text, "Publiczny\\Folder ogólnodostêpny"); + + if ( num == RT_GENERIC_DEV1 ) strcpy(text, "Twórcy:"); + if ( num == RT_GENERIC_DEV2 ) strcpy(text, "www.epsitec.com"); + if ( num == RT_GENERIC_EDIT1 ) strcpy(text, "Wersja polska wydana przez:"); + if ( num == RT_GENERIC_EDIT2 ) strcpy(text, "www.manta.com.pl"); + if ( num == RT_GENERIC_EDIT1 ) strcpy(text, " "); + if ( num == RT_GENERIC_EDIT2 ) strcpy(text, " "); + + if ( num == RT_INTERFACE_REC ) strcpy(text, "Recorder"); + } + + if ( type == RES_EVENT ) + { + if ( num == EVENT_BUTTON_OK ) strcpy(text, "OK"); + if ( num == EVENT_BUTTON_CANCEL ) strcpy(text, "Anuluj"); + if ( num == EVENT_BUTTON_NEXT ) strcpy(text, "Nastêpny"); + if ( num == EVENT_BUTTON_PREV ) strcpy(text, "Poprzedni"); + if ( num == EVENT_BUTTON_QUIT ) strcpy(text, "Menu (\\key quit;)"); + + if ( num == EVENT_DIALOG_OK ) strcpy(text, "OK"); + if ( num == EVENT_DIALOG_CANCEL ) strcpy(text, "Anuluj"); + + if ( num == EVENT_INTERFACE_TRAINER) strcpy(text, "Æwiczenia\\Æwiczenia programistyczne"); + if ( num == EVENT_INTERFACE_DEFI ) strcpy(text, "Wyzwania\\Wyzwania programistyczne"); + if ( num == EVENT_INTERFACE_MISSION) strcpy(text, "Misje\\Wybierz misjê"); + if ( num == EVENT_INTERFACE_FREE ) strcpy(text, "Swobodna gra\\Swobodna gra bez konkretnych celów"); + if ( num == EVENT_INTERFACE_TEEN ) strcpy(text, "Swobodna gra\\Swobodna gra bez konkretnych celów"); + if ( num == EVENT_INTERFACE_USER ) strcpy(text, "Poziomy\\Poziomy u¿ytkownika"); + if ( num == EVENT_INTERFACE_PROTO ) strcpy(text, "Prototypy\\Prototypy w trakcie rozwijania"); + if ( num == EVENT_INTERFACE_NAME ) strcpy(text, "Nowy gracz\\Wybierz imiê gracza"); + if ( num == EVENT_INTERFACE_SETUP ) strcpy(text, "Opcje\\Preferencje"); + if ( num == EVENT_INTERFACE_AGAIN ) strcpy(text, "Uruchom ponownie\\Uruchamia ponownie misjê od pocz¹tku"); + if ( num == EVENT_INTERFACE_WRITE ) strcpy(text, "Zapisz\\Zapisuje bie¿¹c¹ misjê"); + if ( num == EVENT_INTERFACE_READ ) strcpy(text, "Wczytaj\\Wczytuje zapisan¹ misjê"); +#if _NEWLOOK + if ( num == EVENT_INTERFACE_ABORT ) strcpy(text, "\\Powróæ do gry CeeBot"); + if ( num == EVENT_INTERFACE_QUIT ) strcpy(text, "Zakoñcz\\Koñczy grê CeeBot"); +#else + if ( num == EVENT_INTERFACE_ABORT ) strcpy(text, "\\Powróæ do gry COLOBOT"); + if ( num == EVENT_INTERFACE_QUIT ) strcpy(text, "Zakoñcz\\Koñczy grê COLOBOT"); +#endif + if ( num == EVENT_INTERFACE_BACK ) strcpy(text, "<< Wstecz \\Wraca do poprzedniego ekranu"); + if ( num == EVENT_INTERFACE_PLAY ) strcpy(text, "Graj\\Rozpoczyna misjê!"); + if ( num == EVENT_INTERFACE_SETUPd ) strcpy(text, "Urz¹dzenie\\Ustawienia sterownika i rozdzielczoœci"); + if ( num == EVENT_INTERFACE_SETUPg ) strcpy(text, "Grafika\\Ustawienia grafiki"); + if ( num == EVENT_INTERFACE_SETUPp ) strcpy(text, "Gra\\Ustawienia gry"); + if ( num == EVENT_INTERFACE_SETUPc ) strcpy(text, "Sterowanie\\Ustawienia klawiatury, joysticka i myszy"); + if ( num == EVENT_INTERFACE_SETUPs ) strcpy(text, "DŸwiêk\\G³oœnoœæ muzyki i dŸwiêków gry"); + if ( num == EVENT_INTERFACE_DEVICE ) strcpy(text, "Jednostka"); + if ( num == EVENT_INTERFACE_RESOL ) strcpy(text, "Rozdzielczoœæ"); + if ( num == EVENT_INTERFACE_FULL ) strcpy(text, "Pe³ny ekran\\Pe³ny ekran lub tryb okna"); + if ( num == EVENT_INTERFACE_APPLY ) strcpy(text, "Zastosuj zmiany\\Aktywuje zmienione ustawienia"); + + if ( num == EVENT_INTERFACE_TOTO ) strcpy(text, "Robbie\\Twój asystent"); + if ( num == EVENT_INTERFACE_SHADOW ) strcpy(text, "Cienie\\Cienie na ziemi"); + if ( num == EVENT_INTERFACE_GROUND ) strcpy(text, "Znaki na ziemi\\Znaki na ziemi"); + if ( num == EVENT_INTERFACE_DIRTY ) strcpy(text, "Kurz\\Kurz i bród na robotach i budynkach"); + if ( num == EVENT_INTERFACE_FOG ) strcpy(text, "Mg³a\\Mg³a"); + if ( num == EVENT_INTERFACE_LENS ) strcpy(text, "Promienie s³oneczne\\Promienie s³oneczne na niebie"); + if ( num == EVENT_INTERFACE_SKY ) strcpy(text, "Niebo\\Chmury i mg³awice"); + if ( num == EVENT_INTERFACE_PLANET ) strcpy(text, "Planety i gwiazdy\\Obiekty astronomiczne na niebie"); + if ( num == EVENT_INTERFACE_LIGHT ) strcpy(text, "Dynamiczne oœwietlenie\\Ruchome Ÿród³a œwiat³a"); + if ( num == EVENT_INTERFACE_PARTI ) strcpy(text, "Liczba cz¹stek\\Wybuchy, kurz, odbicia, itp."); + if ( num == EVENT_INTERFACE_CLIP ) strcpy(text, "G³êbokoœæ pola\\Maksymalna widocznoœæ"); + if ( num == EVENT_INTERFACE_DETAIL ) strcpy(text, "Szczegó³y\\Jakoœæ wizualna obiektów 3D"); + if ( num == EVENT_INTERFACE_TEXTURE) strcpy(text, "Tekstury\\Jakoœæ tekstur "); + if ( num == EVENT_INTERFACE_GADGET ) strcpy(text, "Iloœæ elementów dekoracyjnych \\Iloœæ elementów czysto dekoracyjnych"); + if ( num == EVENT_INTERFACE_RAIN ) strcpy(text, "Cz¹stki w interfejsie\\Para i iskry z silników w interfejsie"); + if ( num == EVENT_INTERFACE_GLINT ) strcpy(text, "Odbicia na przyciskach \\Œwiec¹ce przyciski"); + if ( num == EVENT_INTERFACE_TOOLTIP) strcpy(text, "Dymki pomocy\\Wyjaœnia funkcje przycisków"); + if ( num == EVENT_INTERFACE_MOVIES ) strcpy(text, "Sekwencje filmowe\\Filmy przed rozpoczêciem i na zakoñczenie misji"); + if ( num == EVENT_INTERFACE_NICERST) strcpy(text, "Koñcowy film\\Film na zakoñczenie æwiczeñ"); + if ( num == EVENT_INTERFACE_HIMSELF) strcpy(text, "Przyjacielski ogieñ\\W³asne strza³y uszkadzaj¹ Twoje obiekty"); + if ( num == EVENT_INTERFACE_SCROLL ) strcpy(text, "Przewijanie\\Ekran jest przewijany gdy mysz dotknie prawej lub lewej jego krawêdzi"); + if ( num == EVENT_INTERFACE_INVERTX) strcpy(text, "Odwrócenie myszy X\\Odwrócenie kierunków przewijania w poziomie"); + if ( num == EVENT_INTERFACE_INVERTY) strcpy(text, "Odwrócenie myszy Y\\Odwrócenie kierunków przewijania w pionie"); + if ( num == EVENT_INTERFACE_EFFECT ) strcpy(text, "Wstrz¹sy przy wybuchach\\Ekran trzêsie siê podczas wybuchów"); + if ( num == EVENT_INTERFACE_MOUSE ) strcpy(text, "Cieñ kursora myszy\\Dodaje cieñ kursorowi myszy"); + if ( num == EVENT_INTERFACE_EDITMODE) strcpy(text, "Automatyczne wciêcia\\Automatyczne wciêcia podczas edycji programu"); + if ( num == EVENT_INTERFACE_EDITVALUE)strcpy(text, "Du¿e wciêcie\\2 lub 4 spacje wciêcia na ka¿dy poziom zdefiniowany przez klamry"); + if ( num == EVENT_INTERFACE_SOLUCE4) strcpy(text, "Accès aux solutions\\Programme \"4: Solution\" dans les exercices"); + + if ( num == EVENT_INTERFACE_KDEF ) strcpy(text, "Standardowa kontrola\\Standardowe klawisze funkcyjne"); + if ( num == EVENT_INTERFACE_KLEFT ) strcpy(text, "Skrêæ w lewo\\Obraca robota w lewo"); + if ( num == EVENT_INTERFACE_KRIGHT ) strcpy(text, "Obróæ w prawo\\Obraca robota w prawo"); + if ( num == EVENT_INTERFACE_KUP ) strcpy(text, "Naprzód\\Porusza do przodu"); + if ( num == EVENT_INTERFACE_KDOWN ) strcpy(text, "Wstecz\\Porusza do ty³u"); + if ( num == EVENT_INTERFACE_KGUP ) strcpy(text, "W górê\\Zwiêksza moc silnika"); + if ( num == EVENT_INTERFACE_KGDOWN ) strcpy(text, "W dó³\\Zmniejsza moc silnika"); + if ( num == EVENT_INTERFACE_KCAMERA) strcpy(text, "Zmieñ kamerê\\Prze³¹cza pomiêdzy kamer¹ pok³adow¹ i œledz¹c¹"); + if ( num == EVENT_INTERFACE_KDESEL ) strcpy(text, "Poprzedni obiekt\\Zaznacz poprzedni obiekt"); + if ( num == EVENT_INTERFACE_KACTION) strcpy(text, "Standardowa akcja\\Standardowa akcja robota (podnieœ/upuœæ, strzelaj, szukaj, itp.)"); + if ( num == EVENT_INTERFACE_KNEAR ) strcpy(text, "Kamera bli¿ej\\Przybli¿a kamerê"); + if ( num == EVENT_INTERFACE_KAWAY ) strcpy(text, "Kamera dalej\\Oddala kamerê"); + if ( num == EVENT_INTERFACE_KNEXT ) strcpy(text, "Nastêpny obiekt\\Zaznacza nastêpny obiekt"); + if ( num == EVENT_INTERFACE_KHUMAN ) strcpy(text, "Zaznacz astronautê\\Zaznacza astronautê"); + if ( num == EVENT_INTERFACE_KQUIT ) strcpy(text, "Zakoñcz\\Koñczy bie¿¹c¹ misjê lub æwiczenie"); + if ( num == EVENT_INTERFACE_KHELP ) strcpy(text, "Rozkazy\\Pokazuje rozkazy dotycz¹ce bie¿¹cej misji"); + if ( num == EVENT_INTERFACE_KPROG ) strcpy(text, "Podrêcznik programowania\\Dostarcza szczegó³ow¹ pomoc w programowaniu"); + if ( num == EVENT_INTERFACE_KCBOT ) strcpy(text, "Pomoc dot. s³ów kluczowych\\Dok³adniejsza pomoc na temat s³ów kluczowych"); + if ( num == EVENT_INTERFACE_KVISIT ) strcpy(text, "Miejsce nadania wiadomoœci\\Pokazuje sk¹d zosta³a wys³ana ostatnia wiadomoœæ"); + if ( num == EVENT_INTERFACE_KSPEED10) strcpy(text, "Prêdkoœæ 1,0x\\Prêdkoœæ normalna"); + if ( num == EVENT_INTERFACE_KSPEED15) strcpy(text, "Prêdkoœæ 1,5x\\1,5 raza szybciej"); + if ( num == EVENT_INTERFACE_KSPEED20) strcpy(text, "Prêdkoœæ 2,0x\\Dwa razy szybciej"); + if ( num == EVENT_INTERFACE_KSPEED30) strcpy(text, "Prêdkoœæ 3,0x\\Trzy razy szybciej"); + + if ( num == EVENT_INTERFACE_VOLSOUND) strcpy(text, "Efekty dŸwiêkowe:\\G³oœnoœæ silników, g³osów, strza³ów, itp."); + if ( num == EVENT_INTERFACE_VOLMUSIC) strcpy(text, "Muzyka w tle :\\G³oœnoœæ œcie¿ek dŸwiêkowych z p³yty CD"); + if ( num == EVENT_INTERFACE_SOUND3D) strcpy(text, "DŸwiêk 3D\\Przestrzenne pozycjonowanie dŸwiêków"); + + if ( num == EVENT_INTERFACE_MIN ) strcpy(text, "Najni¿sza\\Minimalna jakoœæ grafiki (najwy¿sza czêstotliwoœæ odœwie¿ania)"); + if ( num == EVENT_INTERFACE_NORM ) strcpy(text, "Normalna\\Normalna jakoœæ grafiki"); + if ( num == EVENT_INTERFACE_MAX ) strcpy(text, "Najwy¿sza\\Maksymalna jakoœæ grafiki (najni¿sza czêstotliwoœæ odœwie¿ania)"); + + if ( num == EVENT_INTERFACE_SILENT ) strcpy(text, "Cisza\\Brak dŸwiêków"); + if ( num == EVENT_INTERFACE_NOISY ) strcpy(text, "Normalne\\Normalna g³oœnoœæ dŸwiêków"); + + if ( num == EVENT_INTERFACE_JOYSTICK) strcpy(text, "U¿ywaj joysticka\\Joystick lub klawiatura"); + if ( num == EVENT_INTERFACE_SOLUCE ) strcpy(text, "Dostêp do rozwi¹zania\\Pokazuje rozwi¹zanie (szczegó³owe instrukcje dotycz¹ce misji)"); + + if ( num == EVENT_INTERFACE_NEDIT ) strcpy(text, "\\Nowe imiê gracza"); + if ( num == EVENT_INTERFACE_NOK ) strcpy(text, "OK\\Wybiera zaznaczonego gracza"); + if ( num == EVENT_INTERFACE_NCANCEL) strcpy(text, "Anuluj\\Zachowuje bie¿¹ce imiê gracza"); + if ( num == EVENT_INTERFACE_NDELETE) strcpy(text, "Usuñ gracza\\Usuwa gracza z listy"); + if ( num == EVENT_INTERFACE_NLABEL ) strcpy(text, "Imiê gracza"); + + if ( num == EVENT_INTERFACE_IOWRITE) strcpy(text, "Zapisz\\Zapisuje bie¿¹c¹ misjê"); + if ( num == EVENT_INTERFACE_IOREAD ) strcpy(text, "Wczytaj\\Wczytuje zaznaczon¹ misjê"); + if ( num == EVENT_INTERFACE_IOLIST ) strcpy(text, "Lista zapisanych misji"); + if ( num == EVENT_INTERFACE_IOLABEL) strcpy(text, "Nazwa pliku:"); + if ( num == EVENT_INTERFACE_IONAME ) strcpy(text, "Nazwa misji"); + if ( num == EVENT_INTERFACE_IOIMAGE) strcpy(text, "Fotografia"); + if ( num == EVENT_INTERFACE_IODELETE) strcpy(text, "Usuñ\\Usuwa zaznaczony plik"); + + if ( num == EVENT_INTERFACE_PERSO ) strcpy(text, "Wygl¹d\\Wybierz swoj¹ postaæ"); + if ( num == EVENT_INTERFACE_POK ) strcpy(text, "OK"); + if ( num == EVENT_INTERFACE_PCANCEL) strcpy(text, "Anuluj"); + if ( num == EVENT_INTERFACE_PDEF ) strcpy(text, "Standardowe\\Standardowe ustawienia wygl¹du"); + if ( num == EVENT_INTERFACE_PHEAD ) strcpy(text, "G³owa\\Twarz i w³osy"); + if ( num == EVENT_INTERFACE_PBODY ) strcpy(text, "Skafander\\Skafander astronauty"); + if ( num == EVENT_INTERFACE_PLROT ) strcpy(text, "\\Obróæ w lewo"); + if ( num == EVENT_INTERFACE_PRROT ) strcpy(text, "\\Obróæ w prawo"); + if ( num == EVENT_INTERFACE_PCRa ) strcpy(text, "Czerwony"); + if ( num == EVENT_INTERFACE_PCGa ) strcpy(text, "Zielony"); + if ( num == EVENT_INTERFACE_PCBa ) strcpy(text, "Niebieski"); + if ( num == EVENT_INTERFACE_PCRb ) strcpy(text, "Czerwony"); + if ( num == EVENT_INTERFACE_PCGb ) strcpy(text, "Zielony"); + if ( num == EVENT_INTERFACE_PCBb ) strcpy(text, "Niebieski"); + if ( num == EVENT_INTERFACE_PFACE1 ) strcpy(text, "\\Twarz 1"); + if ( num == EVENT_INTERFACE_PFACE2 ) strcpy(text, "\\Twarz 4"); + if ( num == EVENT_INTERFACE_PFACE3 ) strcpy(text, "\\Twarz 3"); + if ( num == EVENT_INTERFACE_PFACE4 ) strcpy(text, "\\Twarz 2"); + if ( num == EVENT_INTERFACE_PGLASS0) strcpy(text, "\\Bez okularów"); + if ( num == EVENT_INTERFACE_PGLASS1) strcpy(text, "\\Okulary 1"); + if ( num == EVENT_INTERFACE_PGLASS2) strcpy(text, "\\Okulary 2"); + if ( num == EVENT_INTERFACE_PGLASS3) strcpy(text, "\\Okulary 3"); + if ( num == EVENT_INTERFACE_PGLASS4) strcpy(text, "\\Okulary 4"); + if ( num == EVENT_INTERFACE_PGLASS5) strcpy(text, "\\Okulary 5"); + + if ( num == EVENT_OBJECT_DESELECT ) strcpy(text, "Poprzednie zaznaczenie (\\key desel;)"); + if ( num == EVENT_OBJECT_LEFT ) strcpy(text, "Skrêæ w lewo (\\key left;)"); + if ( num == EVENT_OBJECT_RIGHT ) strcpy(text, "Skrêæ w prawo (\\key right;)"); + if ( num == EVENT_OBJECT_UP ) strcpy(text, "Naprzód (\\key up;)"); + if ( num == EVENT_OBJECT_DOWN ) strcpy(text, "Cofnij (\\key down;)"); + if ( num == EVENT_OBJECT_GASUP ) strcpy(text, "Góra (\\key gup;)"); + if ( num == EVENT_OBJECT_GASDOWN ) strcpy(text, "Dó³ (\\key gdown;)"); + if ( num == EVENT_OBJECT_HTAKE ) strcpy(text, "Podnieœ lub upuœæ (\\key action;)"); + if ( num == EVENT_OBJECT_MTAKE ) strcpy(text, "Podnieœ lub upuœæ (\\key action;)"); + if ( num == EVENT_OBJECT_MFRONT ) strcpy(text, "..przed"); + if ( num == EVENT_OBJECT_MBACK ) strcpy(text, "..za"); + if ( num == EVENT_OBJECT_MPOWER ) strcpy(text, "..ogniwo elektryczne"); + if ( num == EVENT_OBJECT_BHELP ) strcpy(text, "Rozkazy dotycz¹ce misji (\\key help;)"); + if ( num == EVENT_OBJECT_BTAKEOFF ) strcpy(text, "Odleæ, aby zakoñczyæ misjê"); + if ( num == EVENT_OBJECT_BDERRICK ) strcpy(text, "Zbuduj kopalniê"); + if ( num == EVENT_OBJECT_BSTATION ) strcpy(text, "Zbuduj elektrowniê"); + if ( num == EVENT_OBJECT_BFACTORY ) strcpy(text, "Zbuduj fabrykê robotów"); + if ( num == EVENT_OBJECT_BREPAIR ) strcpy(text, "Zbuduj warsztat"); + if ( num == EVENT_OBJECT_BCONVERT ) strcpy(text, "Zbuduj hutê"); + if ( num == EVENT_OBJECT_BTOWER ) strcpy(text, "Zbuduj wie¿ê obronn¹"); + if ( num == EVENT_OBJECT_BRESEARCH ) strcpy(text, "Zbuduj centrum badawcze"); + if ( num == EVENT_OBJECT_BRADAR ) strcpy(text, "Zbuduj stacjê radarow¹"); + if ( num == EVENT_OBJECT_BENERGY ) strcpy(text, "Zbuduj fabrykê ogniw elektrycznych"); + if ( num == EVENT_OBJECT_BLABO ) strcpy(text, "Zbuduj laboratorium"); + if ( num == EVENT_OBJECT_BNUCLEAR ) strcpy(text, "Zbuduj elektrowniê atomow¹"); + if ( num == EVENT_OBJECT_BPARA ) strcpy(text, "Zbuduj odgromnik"); + if ( num == EVENT_OBJECT_BINFO ) strcpy(text, "Zbuduj stacjê przekaŸnikow¹"); + if ( num == EVENT_OBJECT_GFLAT ) strcpy(text, "Poka¿ czy teren jest p³aski"); + if ( num == EVENT_OBJECT_FCREATE ) strcpy(text, "Postaw flagê"); + if ( num == EVENT_OBJECT_FDELETE ) strcpy(text, "Usuñ flagê"); + if ( num == EVENT_OBJECT_FCOLORb ) strcpy(text, "\\Niebieskie flagi"); + if ( num == EVENT_OBJECT_FCOLORr ) strcpy(text, "\\Czerwone flagi"); + if ( num == EVENT_OBJECT_FCOLORg ) strcpy(text, "\\Zielone flagi"); + if ( num == EVENT_OBJECT_FCOLORy ) strcpy(text, "\\¯ó³te flagi"); + if ( num == EVENT_OBJECT_FCOLORv ) strcpy(text, "\\Fioletowe flagi"); + if ( num == EVENT_OBJECT_FACTORYfa ) strcpy(text, "Zbuduj transporter lataj¹cy"); + if ( num == EVENT_OBJECT_FACTORYta ) strcpy(text, "Zbuduj transporter na g¹sienicach"); + if ( num == EVENT_OBJECT_FACTORYwa ) strcpy(text, "Zbuduj transporter na ko³ach"); + if ( num == EVENT_OBJECT_FACTORYia ) strcpy(text, "Zbuduj transporter na nogach"); + if ( num == EVENT_OBJECT_FACTORYfc ) strcpy(text, "Zbuduj dzia³o lataj¹ce"); + if ( num == EVENT_OBJECT_FACTORYtc ) strcpy(text, "Zbuduj dzia³o na g¹sienicach"); + if ( num == EVENT_OBJECT_FACTORYwc ) strcpy(text, "Zbuduj dzia³o na ko³ach"); + if ( num == EVENT_OBJECT_FACTORYic ) strcpy(text, "Zbuduj dzia³o na nogach"); + if ( num == EVENT_OBJECT_FACTORYfi ) strcpy(text, "Zbuduj lataj¹ce dzia³o organiczne"); + if ( num == EVENT_OBJECT_FACTORYti ) strcpy(text, "Zbuduj dzia³o organiczne na g¹sienicach"); + if ( num == EVENT_OBJECT_FACTORYwi ) strcpy(text, "Zbuduj dzia³o organiczne na ko³ach"); + if ( num == EVENT_OBJECT_FACTORYii ) strcpy(text, "Zbuduj dzia³o organiczne na nogach"); + if ( num == EVENT_OBJECT_FACTORYfs ) strcpy(text, "Zbuduj szperacz lataj¹cy"); + if ( num == EVENT_OBJECT_FACTORYts ) strcpy(text, "Zbuduj szperacz na g¹sienicach"); + if ( num == EVENT_OBJECT_FACTORYws ) strcpy(text, "Zbuduj szperacz na ko³ach"); + if ( num == EVENT_OBJECT_FACTORYis ) strcpy(text, "Zbuduj szperacz na nogach"); + if ( num == EVENT_OBJECT_FACTORYrt ) strcpy(text, "Zbuduj robota uderzacza"); + if ( num == EVENT_OBJECT_FACTORYrc ) strcpy(text, "Zbuduj dzia³o fazowe"); + if ( num == EVENT_OBJECT_FACTORYrr ) strcpy(text, "Zbuduj robota recyklera"); + if ( num == EVENT_OBJECT_FACTORYrs ) strcpy(text, "Zbuduj robota os³aniajacza"); + if ( num == EVENT_OBJECT_FACTORYsa ) strcpy(text, "Zbuduj robota nurka"); + if ( num == EVENT_OBJECT_RTANK ) strcpy(text, "Rozpocznij prace badawcze nad transporterem na g¹sienicach"); + if ( num == EVENT_OBJECT_RFLY ) strcpy(text, "Rozpocznij prace badawcze nad transporterem lataj¹cym"); + if ( num == EVENT_OBJECT_RTHUMP ) strcpy(text, "Rozpocznij prace badawcze nad robotem uderzaczem"); + if ( num == EVENT_OBJECT_RCANON ) strcpy(text, "Rozpocznij prace badawcze nad dzia³em"); + if ( num == EVENT_OBJECT_RTOWER ) strcpy(text, "Rozpocznij prace badawcze nad wie¿¹ obronn¹"); + if ( num == EVENT_OBJECT_RPHAZER ) strcpy(text, "Rozpocznij prace badawcze nad dzia³em fazowym"); + if ( num == EVENT_OBJECT_RSHIELD ) strcpy(text, "Rozpocznij prace badawcze nad robotem os³aniaczem"); + if ( num == EVENT_OBJECT_RATOMIC ) strcpy(text, "Rozpocznij prace badawcze nad energi¹ atomow¹"); + if ( num == EVENT_OBJECT_RiPAW ) strcpy(text, "Rozpocznij prace badawcze nad transporterem na nogach"); + if ( num == EVENT_OBJECT_RiGUN ) strcpy(text, "Rozpocznij prace badawcze nad dzia³em organicznym"); + if ( num == EVENT_OBJECT_RESET ) strcpy(text, "Powrót do pocz¹tku"); + if ( num == EVENT_OBJECT_SEARCH ) strcpy(text, "Szukaj (\\key action;)"); + if ( num == EVENT_OBJECT_TERRAFORM ) strcpy(text, "Uderz (\\key action;)"); + if ( num == EVENT_OBJECT_FIRE ) strcpy(text, "Strzelaj (\\key action;)"); + if ( num == EVENT_OBJECT_RECOVER ) strcpy(text, "Odzyskaj (\\key action;)"); + if ( num == EVENT_OBJECT_BEGSHIELD ) strcpy(text, "Rozszerz os³onê (\\key action;)"); + if ( num == EVENT_OBJECT_ENDSHIELD ) strcpy(text, "Wy³¹cz os³onê (\\key action;)"); + if ( num == EVENT_OBJECT_DIMSHIELD ) strcpy(text, "Zasiêg os³ony"); + if ( num == EVENT_OBJECT_PROGRUN ) strcpy(text, "Wykonaj zaznaczony program"); + if ( num == EVENT_OBJECT_PROGEDIT ) strcpy(text, "Edytuj zaznaczony program"); + if ( num == EVENT_OBJECT_INFOOK ) strcpy(text, "\\Prze³¹cz przekaŸnik SatCom w stan gotowoœci"); + if ( num == EVENT_OBJECT_DELETE ) strcpy(text, "Zniszcz budynek"); + if ( num == EVENT_OBJECT_GENERGY ) strcpy(text, "Poziom energii"); + if ( num == EVENT_OBJECT_GSHIELD ) strcpy(text, "Poziom os³ony"); + if ( num == EVENT_OBJECT_GRANGE ) strcpy(text, "Temperatura silnika"); + if ( num == EVENT_OBJECT_GPROGRESS ) strcpy(text, "Wci¹¿ pracuje..."); + if ( num == EVENT_OBJECT_GRADAR ) strcpy(text, "Liczba wykrytych insektów"); + if ( num == EVENT_OBJECT_GINFO ) strcpy(text, "Przes³ane informacje"); + if ( num == EVENT_OBJECT_COMPASS ) strcpy(text, "Kompas"); +//? if ( num == EVENT_OBJECT_MAP ) strcpy(text, "Mapka"); + if ( num == EVENT_OBJECT_MAPZOOM ) strcpy(text, "Powiêkszenie mapki"); + if ( num == EVENT_OBJECT_CAMERA ) strcpy(text, "Kamera (\\key camera;)"); + if ( num == EVENT_OBJECT_CAMERAleft) strcpy(text, "Camera to left"); + if ( num == EVENT_OBJECT_CAMERAright) strcpy(text, "Camera to right"); + if ( num == EVENT_OBJECT_CAMERAnear) strcpy(text, "Camera nearest"); + if ( num == EVENT_OBJECT_CAMERAaway) strcpy(text, "Camera awayest"); + if ( num == EVENT_OBJECT_HELP ) strcpy(text, "Pomoc na temat zaznaczonego obiektu"); + if ( num == EVENT_OBJECT_SOLUCE ) strcpy(text, "Poka¿ rozwi¹zanie"); + if ( num == EVENT_OBJECT_SHORTCUT00) strcpy(text, "Prze³¹cz roboty <-> budynki"); + if ( num == EVENT_OBJECT_LIMIT ) strcpy(text, "Poka¿ zasiêg"); + if ( num == EVENT_OBJECT_PEN0 ) strcpy(text, "\\Relève le crayon"); + if ( num == EVENT_OBJECT_PEN1 ) strcpy(text, "\\Abaisse le crayon noir"); + if ( num == EVENT_OBJECT_PEN2 ) strcpy(text, "\\Abaisse le crayon jaune"); + if ( num == EVENT_OBJECT_PEN3 ) strcpy(text, "\\Abaisse le crayon orange"); + if ( num == EVENT_OBJECT_PEN4 ) strcpy(text, "\\Abaisse le crayon rouge"); + if ( num == EVENT_OBJECT_PEN5 ) strcpy(text, "\\Abaisse le crayon violet"); + if ( num == EVENT_OBJECT_PEN6 ) strcpy(text, "\\Abaisse le crayon bleu"); + if ( num == EVENT_OBJECT_PEN7 ) strcpy(text, "\\Abaisse le crayon vert"); + if ( num == EVENT_OBJECT_PEN8 ) strcpy(text, "\\Abaisse le crayon brun"); + if ( num == EVENT_OBJECT_REC ) strcpy(text, "\\Démarre l'enregistrement"); + if ( num == EVENT_OBJECT_STOP ) strcpy(text, "\\Stoppe l'enregistrement"); + if ( num == EVENT_DT_VISIT0 || + num == EVENT_DT_VISIT1 || + num == EVENT_DT_VISIT2 || + num == EVENT_DT_VISIT3 || + num == EVENT_DT_VISIT4 ) strcpy(text, "Poka¿ miejsce"); + if ( num == EVENT_DT_END ) strcpy(text, "Kontynuuj"); + if ( num == EVENT_CMD ) strcpy(text, "Linia polecenia"); + if ( num == EVENT_SPEED ) strcpy(text, "Prêdkoœæ gry"); + + if ( num == EVENT_HYPER_PREV ) strcpy(text, "Wstecz"); + if ( num == EVENT_HYPER_NEXT ) strcpy(text, "Naprzód"); + if ( num == EVENT_HYPER_HOME ) strcpy(text, "Pocz¹tek"); + if ( num == EVENT_HYPER_COPY ) strcpy(text, "Kopiuj"); + if ( num == EVENT_HYPER_SIZE1 ) strcpy(text, "Wielkoœæ 1"); + if ( num == EVENT_HYPER_SIZE2 ) strcpy(text, "Wielkoœæ 2"); + if ( num == EVENT_HYPER_SIZE3 ) strcpy(text, "Wielkoœæ 3"); + if ( num == EVENT_HYPER_SIZE4 ) strcpy(text, "Wielkoœæ 4"); + if ( num == EVENT_HYPER_SIZE5 ) strcpy(text, "Wielkoœæ 5"); + if ( num == EVENT_SATCOM_HUSTON ) strcpy(text, "Rozkazy z Houston"); +#if _TEEN + if ( num == EVENT_SATCOM_SAT ) strcpy(text, "Raport z satelity"); +#else + if ( num == EVENT_SATCOM_SAT ) strcpy(text, "Raport z satelity"); +#endif + if ( num == EVENT_SATCOM_LOADING ) strcpy(text, "Program dostarczony z Houston"); + if ( num == EVENT_SATCOM_OBJECT ) strcpy(text, "Lista obiektów"); + if ( num == EVENT_SATCOM_PROG ) strcpy(text, "Podrêcznik programowania"); + if ( num == EVENT_SATCOM_SOLUCE ) strcpy(text, "Rozwi¹zanie"); + + if ( num == EVENT_STUDIO_OK ) strcpy(text, "OK\\Zamyka edytor programu i powraca do gry"); + if ( num == EVENT_STUDIO_CANCEL ) strcpy(text, "Anuluj\\Pomija wszystkie zmiany"); + if ( num == EVENT_STUDIO_NEW ) strcpy(text, "Nowy"); + if ( num == EVENT_STUDIO_OPEN ) strcpy(text, "Otwórz (Ctrl+O)"); + if ( num == EVENT_STUDIO_SAVE ) strcpy(text, "Zapisz (Ctrl+S)"); + if ( num == EVENT_STUDIO_UNDO ) strcpy(text, "Cofnij (Ctrl+Z)"); + if ( num == EVENT_STUDIO_CUT ) strcpy(text, "Wytnij (Ctrl+X)"); + if ( num == EVENT_STUDIO_COPY ) strcpy(text, "Kopiuj (Ctrl+C)"); + if ( num == EVENT_STUDIO_PASTE ) strcpy(text, "Wklej (Ctrl+V)"); + if ( num == EVENT_STUDIO_SIZE ) strcpy(text, "Wielkoœæ czcionki"); + if ( num == EVENT_STUDIO_TOOL ) strcpy(text, "Rozkazy (\\key help;)"); + if ( num == EVENT_STUDIO_HELP ) strcpy(text, "Podrêcznik programowania (\\key prog;)"); + if ( num == EVENT_STUDIO_COMPILE ) strcpy(text, "Kompiluj"); + if ( num == EVENT_STUDIO_RUN ) strcpy(text, "Wykonaj/Zatrzymaj"); + if ( num == EVENT_STUDIO_REALTIME ) strcpy(text, "Pauza/Kontynuuj"); + if ( num == EVENT_STUDIO_STEP ) strcpy(text, "Jeden krok"); + } + + if ( type == RES_OBJECT ) + { + if ( num == OBJECT_PORTICO ) strcpy(text, "¯uraw przesuwalny"); + if ( num == OBJECT_BASE ) strcpy(text, "Statek kosmiczny"); + if ( num == OBJECT_DERRICK ) strcpy(text, "Kopalnia"); + if ( num == OBJECT_FACTORY ) strcpy(text, "Fabryka robotów"); + if ( num == OBJECT_REPAIR ) strcpy(text, "Warsztat"); + if ( num == OBJECT_DESTROYER ) strcpy(text, "Destroyer"); + if ( num == OBJECT_STATION ) strcpy(text, "Stacja energetyczna"); + if ( num == OBJECT_CONVERT ) strcpy(text, "Przetop rudê na tytan"); + if ( num == OBJECT_TOWER ) strcpy(text, "Wie¿a obronna"); + if ( num == OBJECT_NEST ) strcpy(text, "Gniazdo"); + if ( num == OBJECT_RESEARCH ) strcpy(text, "Centrum badawcze"); + if ( num == OBJECT_RADAR ) strcpy(text, "Stacja radarowa"); + if ( num == OBJECT_INFO ) strcpy(text, "Stacja przekaŸnikowa informacji"); +#if _TEEN + if ( num == OBJECT_ENERGY ) strcpy(text, "Fabryka ogniw elektrycznych"); +#else + if ( num == OBJECT_ENERGY ) strcpy(text, "Fabryka ogniw elektrycznych"); +#endif + if ( num == OBJECT_LABO ) strcpy(text, "Laboratorium"); + if ( num == OBJECT_NUCLEAR ) strcpy(text, "Elektrownia atomowa"); + if ( num == OBJECT_PARA ) strcpy(text, "Odgromnik"); + if ( num == OBJECT_SAFE ) strcpy(text, "Skrytka"); + if ( num == OBJECT_HUSTON ) strcpy(text, "Centrum Kontroli Misji w Houston"); + if ( num == OBJECT_TARGET1 ) strcpy(text, "Cel"); + if ( num == OBJECT_TARGET2 ) strcpy(text, "Cel"); + if ( num == OBJECT_START ) strcpy(text, "Pocz¹tek"); + if ( num == OBJECT_END ) strcpy(text, "Koniec"); + if ( num == OBJECT_STONE ) strcpy(text, "Ruda tytanu"); + if ( num == OBJECT_URANIUM ) strcpy(text, "Ruda uranu"); + if ( num == OBJECT_BULLET ) strcpy(text, "Materia organiczna"); + if ( num == OBJECT_METAL ) strcpy(text, "Tytan"); + if ( num == OBJECT_POWER ) strcpy(text, "Ogniwo elektryczne"); + if ( num == OBJECT_ATOMIC ) strcpy(text, "Atomowe ogniwa elektryczne"); + if ( num == OBJECT_BBOX ) strcpy(text, "Czarna skrzynka"); + if ( num == OBJECT_KEYa ) strcpy(text, "Klucz A"); + if ( num == OBJECT_KEYb ) strcpy(text, "Klucz B"); + if ( num == OBJECT_KEYc ) strcpy(text, "Klucz C"); + if ( num == OBJECT_KEYd ) strcpy(text, "Klucz D"); + if ( num == OBJECT_TNT ) strcpy(text, "Materia³y wybuchowe"); + if ( num == OBJECT_BOMB ) strcpy(text, "Mina"); + if ( num == OBJECT_BAG ) strcpy(text, "Zestaw przetrwania"); + if ( num == OBJECT_WAYPOINT ) strcpy(text, "Punkt kontrolny"); + if ( num == OBJECT_FLAGb ) strcpy(text, "Niebieska flaga"); + if ( num == OBJECT_FLAGr ) strcpy(text, "Czerwona flaga"); + if ( num == OBJECT_FLAGg ) strcpy(text, "Zielona flaga"); + if ( num == OBJECT_FLAGy ) strcpy(text, "¯ó³ta flaga"); + if ( num == OBJECT_FLAGv ) strcpy(text, "Fioletowa flaga"); + if ( num == OBJECT_MARKPOWER ) strcpy(text, "ród³o energii (miejsce na elektrowniê)"); + if ( num == OBJECT_MARKURANIUM ) strcpy(text, "Z³o¿e uranu (miejsce na kopalniê)"); + if ( num == OBJECT_MARKKEYa ) strcpy(text, "Znaleziono klucz A (miejsce na kopalniê)"); + if ( num == OBJECT_MARKKEYb ) strcpy(text, "Znaleziono klucz B (miejsce na kopalniê)"); + if ( num == OBJECT_MARKKEYc ) strcpy(text, "Znaleziono klucz C (miejsce na kopalniê)"); + if ( num == OBJECT_MARKKEYd ) strcpy(text, "Znaleziono klucz D (miejsce na kopalniê)"); + if ( num == OBJECT_MARKSTONE ) strcpy(text, "Z³o¿e tytanu (miejsce na kopalniê)"); + if ( num == OBJECT_MOBILEft ) strcpy(text, "Robot treningowy"); + if ( num == OBJECT_MOBILEtt ) strcpy(text, "Robot treningowy"); + if ( num == OBJECT_MOBILEwt ) strcpy(text, "Robot treningowy"); + if ( num == OBJECT_MOBILEit ) strcpy(text, "Robot treningowy"); + if ( num == OBJECT_MOBILEfa ) strcpy(text, "Transporter lataj¹cy"); + if ( num == OBJECT_MOBILEta ) strcpy(text, "Transporter na g¹sienicach"); + if ( num == OBJECT_MOBILEwa ) strcpy(text, "Transporter na ko³ach"); + if ( num == OBJECT_MOBILEia ) strcpy(text, "Transporter na nogach"); + if ( num == OBJECT_MOBILEfc ) strcpy(text, "Dzia³o lataj¹ce"); + if ( num == OBJECT_MOBILEtc ) strcpy(text, "Dzia³o na g¹sienicach"); + if ( num == OBJECT_MOBILEwc ) strcpy(text, "Dzia³o na ko³ach"); + if ( num == OBJECT_MOBILEic ) strcpy(text, "Dzia³o na nogach"); + if ( num == OBJECT_MOBILEfi ) strcpy(text, "Lataj¹ce dzia³o organiczne"); + if ( num == OBJECT_MOBILEti ) strcpy(text, "Dzia³o organiczne na g¹sienicach"); + if ( num == OBJECT_MOBILEwi ) strcpy(text, "Dzia³o organiczne na ko³ach"); + if ( num == OBJECT_MOBILEii ) strcpy(text, "Dzia³o organiczne na nogach"); + if ( num == OBJECT_MOBILEfs ) strcpy(text, "Szperacz lataj¹cy"); + if ( num == OBJECT_MOBILEts ) strcpy(text, "Szperacz na g¹sienicach"); + if ( num == OBJECT_MOBILEws ) strcpy(text, "Szperacz na ko³ach"); + if ( num == OBJECT_MOBILEis ) strcpy(text, "Szperacz na nogach"); + if ( num == OBJECT_MOBILErt ) strcpy(text, "Uderzacz"); + if ( num == OBJECT_MOBILErc ) strcpy(text, "Dzia³o fazowe"); + if ( num == OBJECT_MOBILErr ) strcpy(text, "Recykler"); + if ( num == OBJECT_MOBILErs ) strcpy(text, "Os³aniacz"); + if ( num == OBJECT_MOBILEsa ) strcpy(text, "Robot nurek"); + if ( num == OBJECT_MOBILEtg ) strcpy(text, "Robot cel"); + if ( num == OBJECT_MOBILEdr ) strcpy(text, "Drawer bot"); + if ( num == OBJECT_HUMAN ) strcpy(text, g_gamerName); + if ( num == OBJECT_TECH ) strcpy(text, "In¿ynier"); + if ( num == OBJECT_TOTO ) strcpy(text, "Robbie"); + if ( num == OBJECT_MOTHER ) strcpy(text, "Królowa Obcych"); + if ( num == OBJECT_ANT ) strcpy(text, "Mrówka"); + if ( num == OBJECT_SPIDER ) strcpy(text, "Paj¹k"); + if ( num == OBJECT_BEE ) strcpy(text, "Osa"); + if ( num == OBJECT_WORM ) strcpy(text, "Robal"); + if ( num == OBJECT_EGG ) strcpy(text, "Jajo"); + if ( num == OBJECT_RUINmobilew1 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINmobilew2 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINmobilet1 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINmobilet2 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINmobiler1 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINmobiler2 ) strcpy(text, "Wrak"); + if ( num == OBJECT_RUINfactory ) strcpy(text, "Ruiny"); + if ( num == OBJECT_RUINdoor ) strcpy(text, "Ruiny"); + if ( num == OBJECT_RUINsupport ) strcpy(text, "Odpady"); + if ( num == OBJECT_RUINradar ) strcpy(text, "Ruiny"); + if ( num == OBJECT_RUINconvert ) strcpy(text, "Ruiny"); + if ( num == OBJECT_RUINbase ) strcpy(text, "Ruiny statku kosmicznego"); + if ( num == OBJECT_RUINhead ) strcpy(text, "Ruiny statku kosmicznego"); + if ( num == OBJECT_APOLLO1 || + num == OBJECT_APOLLO3 || + num == OBJECT_APOLLO4 || + num == OBJECT_APOLLO5 ) strcpy(text, "Pozosta³oœci z misji Apollo"); + if ( num == OBJECT_APOLLO2 ) strcpy(text, "Pojazd Ksiê¿ycowy"); + } + + if ( type == RES_ERR ) + { + strcpy(text, "B³¹d"); + if ( num == ERR_CMD ) strcpy(text, "Nieznane polecenie"); +#if _NEWLOOK + if ( num == ERR_INSTALL ) strcpy(text, "Gra CeeBot nie jest zainstalowana."); + if ( num == ERR_NOCD ) strcpy(text, "W³ó¿ dysk CD z gr¹ CeeBot\ni uruchom grê jeszcze raz."); +#else + if ( num == ERR_INSTALL ) strcpy(text, "Gra COLOBOT nie jest zainstalowana."); + if ( num == ERR_NOCD ) strcpy(text, "W³ó¿ dysk CD z gr¹ COLOBOT\ni uruchom grê jeszcze raz."); +#endif + if ( num == ERR_MANIP_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_MANIP_FLY ) strcpy(text, "Niemo¿liwe podczas lotu"); + if ( num == ERR_MANIP_BUSY ) strcpy(text, "Nie mo¿na nieœæ wiêcej przedmiotów"); + if ( num == ERR_MANIP_NIL ) strcpy(text, "Nie ma nic do podniesienia"); + if ( num == ERR_MANIP_MOTOR ) strcpy(text, "Niemo¿liwe podczas ruchu"); + if ( num == ERR_MANIP_OCC ) strcpy(text, "Miejsce zajête"); + if ( num == ERR_MANIP_FRIEND ) strcpy(text, "Brak innego robota"); + if ( num == ERR_MANIP_RADIO ) strcpy(text, "Nie mo¿esz przenosiæ przedmiotów radioaktywnych"); + if ( num == ERR_MANIP_WATER ) strcpy(text, "Nie mo¿esz przenosiæ przedmiotów pod wod¹"); + if ( num == ERR_MANIP_EMPTY ) strcpy(text, "Nie ma nic do upuszczenia"); + if ( num == ERR_BUILD_FLY ) strcpy(text, "Niemo¿liwe podczas lotu"); + if ( num == ERR_BUILD_WATER ) strcpy(text, "Niemo¿liwe pod wod¹"); + if ( num == ERR_BUILD_ENERGY ) strcpy(text, "Za ma³o energii"); + if ( num == ERR_BUILD_METALAWAY ) strcpy(text, "Tytan za daleko"); + if ( num == ERR_BUILD_METALNEAR ) strcpy(text, "Tytan za blisko"); + if ( num == ERR_BUILD_METALINEX ) strcpy(text, "Brak tytanu w pobli¿u"); + if ( num == ERR_BUILD_FLAT ) strcpy(text, "Powierzchnia nie jest wystarczaj¹co p³aska"); + if ( num == ERR_BUILD_FLATLIT ) strcpy(text, "Za ma³o p³askiego terenu"); + if ( num == ERR_BUILD_BUSY ) strcpy(text, "Miejsce zajête"); + if ( num == ERR_BUILD_BASE ) strcpy(text, "Za blisko statku kosmicznego"); + if ( num == ERR_BUILD_NARROW ) strcpy(text, "Za blisko budynku"); + if ( num == ERR_BUILD_MOTOR ) strcpy(text, "Niemo¿liwe podczas ruchu"); + if ( num == ERR_SEARCH_FLY ) strcpy(text, "Niemo¿liwe podczas lotu"); + if ( num == ERR_SEARCH_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_SEARCH_MOTOR ) strcpy(text, "Niemo¿liwe podczas ruchu"); + if ( num == ERR_TERRA_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_TERRA_ENERGY ) strcpy(text, "Za ma³o energii"); + if ( num == ERR_TERRA_FLOOR ) strcpy(text, "Nieodpowiedni teren"); + if ( num == ERR_TERRA_BUILDING ) strcpy(text, "Budynek za blisko"); + if ( num == ERR_TERRA_OBJECT ) strcpy(text, "Obiekt za blisko"); + if ( num == ERR_RECOVER_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_RECOVER_ENERGY ) strcpy(text, "Za ma³o energii"); + if ( num == ERR_RECOVER_NULL ) strcpy(text, "Nie ma niczego do odzysku"); + if ( num == ERR_SHIELD_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_SHIELD_ENERGY ) strcpy(text, "Nie ma wiêcej energii"); + if ( num == ERR_MOVE_IMPOSSIBLE ) strcpy(text, "B³¹d w poleceniu ruchu"); + if ( num == ERR_FIND_IMPOSSIBLE ) strcpy(text, "Obiekt nieznany"); + if ( num == ERR_GOTO_IMPOSSIBLE ) strcpy(text, "Goto: miejsce docelowe niedostêpne"); + if ( num == ERR_GOTO_ITER ) strcpy(text, "Goto: miejsce docelowe niedostêpne"); + if ( num == ERR_GOTO_BUSY ) strcpy(text, "Goto: miejsce docelowe zajête"); + if ( num == ERR_FIRE_VEH ) strcpy(text, "Nieodpowiedni robot"); + if ( num == ERR_FIRE_ENERGY ) strcpy(text, "Za ma³o energii"); + if ( num == ERR_FIRE_FLY ) strcpy(text, "Niemo¿liwe podczas lotu"); + if ( num == ERR_CONVERT_EMPTY ) strcpy(text, "Brak rudy tytanu do przetopienia"); + if ( num == ERR_DERRICK_NULL ) strcpy(text, "W ziemi nie ma ¿adnej rudy"); + if ( num == ERR_STATION_NULL ) strcpy(text, "Brak energii w ziemi"); + if ( num == ERR_TOWER_POWER ) strcpy(text, "Brak ogniwa elektrycznego"); + if ( num == ERR_TOWER_ENERGY ) strcpy(text, "Nie ma wiêcej energii"); + if ( num == ERR_RESEARCH_POWER ) strcpy(text, "Brak ogniwa elektrycznego"); + if ( num == ERR_RESEARCH_ENERGY ) strcpy(text, "Za ma³o energii"); + if ( num == ERR_RESEARCH_TYPE ) strcpy(text, "Nieodpowiedni rodzaj ogniw"); + if ( num == ERR_RESEARCH_ALREADY) strcpy(text, "Program badawczy zosta³ ju¿ wykonany"); + if ( num == ERR_ENERGY_NULL ) strcpy(text, "Brak energii w ziemi"); + if ( num == ERR_ENERGY_LOW ) strcpy(text, "Wci¹¿ za ma³o energii"); + if ( num == ERR_ENERGY_EMPTY ) strcpy(text, "Brak tytanu do przetworzenia"); + if ( num == ERR_ENERGY_BAD ) strcpy(text, "Przetwarza jedynie tytan"); + if ( num == ERR_BASE_DLOCK ) strcpy(text, "Drzwi zablokowane przez robota lub inny obiekt "); + if ( num == ERR_BASE_DHUMAN ) strcpy(text, "Musisz byæ na statku kosmicznym aby nim odlecieæ"); + if ( num == ERR_LABO_NULL ) strcpy(text, "Nie ma niczego do zanalizowania"); + if ( num == ERR_LABO_BAD ) strcpy(text, "Analizuje jedynie materiê organiczn¹"); + if ( num == ERR_LABO_ALREADY ) strcpy(text, "Analiza zosta³a ju¿ wykonana"); + if ( num == ERR_NUCLEAR_NULL ) strcpy(text, "Brak energii w ziemi"); + if ( num == ERR_NUCLEAR_LOW ) strcpy(text, "Wci¹¿ za ma³o energii"); + if ( num == ERR_NUCLEAR_EMPTY ) strcpy(text, "Brak uranu do przetworzenia"); + if ( num == ERR_NUCLEAR_BAD ) strcpy(text, "Przetwarza jedynie uran"); + if ( num == ERR_FACTORY_NULL ) strcpy(text, "Brak tytanu"); + if ( num == ERR_FACTORY_NEAR ) strcpy(text, "Obiekt za blisko"); + if ( num == ERR_RESET_NEAR ) strcpy(text, "Miejsce zajête"); + if ( num == ERR_INFO_NULL ) strcpy(text, "Nie ma ¿adnej stacji przekaŸnikowej w zasiêgu"); + if ( num == ERR_VEH_VIRUS ) strcpy(text, "Program zawirusowany"); + if ( num == ERR_BAT_VIRUS ) strcpy(text, "Zainfekowane wirusem, chwilowo niesprawne"); + if ( num == ERR_VEH_POWER ) strcpy(text, "Brak ogniwa elektrycznego"); + if ( num == ERR_VEH_ENERGY ) strcpy(text, "Nie ma wiêcej energii"); + if ( num == ERR_FLAG_FLY ) strcpy(text, "Niemo¿liwe podczas lotu"); + if ( num == ERR_FLAG_WATER ) strcpy(text, "Niemo¿liwe podczas p³ywania"); + if ( num == ERR_FLAG_MOTOR ) strcpy(text, "Niemo¿liwe podczas ruchu"); + if ( num == ERR_FLAG_BUSY ) strcpy(text, "Niemo¿liwe podczas przenoszenia przedmiotu"); + if ( num == ERR_FLAG_CREATE ) strcpy(text, "Za du¿o flag w tym kolorze (maksymalnie 5)"); + if ( num == ERR_FLAG_PROXY ) strcpy(text, "Za blisko istniej¹cej flagi"); + if ( num == ERR_FLAG_DELETE ) strcpy(text, "Nie ma flagi w pobli¿u"); + if ( num == ERR_MISSION_NOTERM ) strcpy(text, "Misja nie jest wype³niona (naciœnij \\key help; aby uzyskaæ szczegó³y)"); + if ( num == ERR_DELETEMOBILE ) strcpy(text, "Robot zniszczony"); + if ( num == ERR_DELETEBUILDING ) strcpy(text, "Budynek zniszczony"); + if ( num == ERR_TOOMANY ) strcpy(text, "Nie mo¿na tego utworzyæ, za du¿o obiektów"); + if ( num == ERR_OBLIGATORYTOKEN ) strcpy(text, "It misses \"%s\" in this exercise"); + if ( num == ERR_PROHIBITEDTOKEN ) strcpy(text, "Do not use in this exercise"); + + if ( num == INFO_BUILD ) strcpy(text, "Budowa zakoñczona"); + if ( num == INFO_CONVERT ) strcpy(text, "Tytan dostêpny"); + if ( num == INFO_RESEARCH ) strcpy(text, "Program badawczy zakoñczony"); + if ( num == INFO_RESEARCHTANK ) strcpy(text, "Dostêpne plany tranporterów na g¹sienicach"); + if ( num == INFO_RESEARCHFLY ) strcpy(text, "Mo¿esz lataæ u¿ywaj¹c klawiszy (\\key gup;) oraz (\\key gdown;)"); + if ( num == INFO_RESEARCHTHUMP ) strcpy(text, "Dostêpne plany robota uderzacza"); + if ( num == INFO_RESEARCHCANON ) strcpy(text, "Dostêpne plany dzia³a"); + if ( num == INFO_RESEARCHTOWER ) strcpy(text, "Dostêpne plany wie¿y obronnej"); + if ( num == INFO_RESEARCHPHAZER ) strcpy(text, "Dostêpne plany dzia³a fazowego"); + if ( num == INFO_RESEARCHSHIELD ) strcpy(text, "Dostêpne plany robota os³aniacza"); + if ( num == INFO_RESEARCHATOMIC ) strcpy(text, "Dostêpne plany elektrowni atomowej"); + if ( num == INFO_FACTORY ) strcpy(text, "Dostêpny nowy robot"); + if ( num == INFO_LABO ) strcpy(text, "Analiza wykonana"); + if ( num == INFO_ENERGY ) strcpy(text, "Wytworzono ogniwo elektryczne"); + if ( num == INFO_NUCLEAR ) strcpy(text, "Wytworzono atomowe ogniwo elektryczne"); + if ( num == INFO_FINDING ) strcpy(text, "Znaleziono u¿yteczny przedmiot"); + if ( num == INFO_MARKPOWER ) strcpy(text, "Znaleziono miejsce na elektrowniê"); + if ( num == INFO_MARKURANIUM ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_MARKSTONE ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_MARKKEYa ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_MARKKEYb ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_MARKKEYc ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_MARKKEYd ) strcpy(text, "Znaleziono miejsce na kopalniê"); + if ( num == INFO_WIN ) strcpy(text, "<<< Dobra robota, misja wype³niona >>>"); + if ( num == INFO_LOST ) strcpy(text, "<<< Niestety, misja nie powiod³a siê >>>"); + if ( num == INFO_LOSTq ) strcpy(text, "<<< Niestety, misja nie powiod³a siê >>>"); + if ( num == INFO_WRITEOK ) strcpy(text, "Bie¿¹ca misja zapisana"); + if ( num == INFO_DELETEPATH ) strcpy(text, "Przekroczono punkt kontrolny"); + if ( num == INFO_DELETEMOTHER ) strcpy(text, "Królowa Obcych zosta³a zabita"); + if ( num == INFO_DELETEANT ) strcpy(text, "Mrówka œmiertelnie raniona"); + if ( num == INFO_DELETEBEE ) strcpy(text, "Osa œmiertelnie raniona"); + if ( num == INFO_DELETEWORM ) strcpy(text, "Robal œmiertelnie raniony"); + if ( num == INFO_DELETESPIDER ) strcpy(text, "Paj¹k œmiertelnie raniony"); + if ( num == INFO_BEGINSATCOM ) strcpy(text, "Naciœnij klawisz \\key help; aby wyœwietliæ rozkazy na przekaŸniku SatCom"); + } + + if ( type == RES_CBOT ) + { + strcpy(text, "B³¹d"); + if ( num == TX_OPENPAR ) strcpy(text, "Brak nawiasu otwieraj¹cego"); + if ( num == TX_CLOSEPAR ) strcpy(text, "Brak nawiasu zamykaj¹cego"); + if ( num == TX_NOTBOOL ) strcpy(text, "Wyra¿enie musi zwróciæ wartoœæ logiczn¹"); + if ( num == TX_UNDEFVAR ) strcpy(text, "Zmienna nie zosta³a zadeklarowana"); + if ( num == TX_BADLEFT ) strcpy(text, "Przypisanie niemo¿liwe"); + if ( num == TX_ENDOF ) strcpy(text, "Brak œrednika na koñcu wiersza"); + if ( num == TX_OUTCASE ) strcpy(text, "Polecenie ""case"" na zewn¹trz bloku ""switch"""); + if ( num == TX_NOTERM ) strcpy(text, "Polecenie po koñcowej klamrze zamykaj¹cej"); + if ( num == TX_CLOSEBLK ) strcpy(text, "Brak koñca bloku"); + if ( num == TX_ELSEWITHOUTIF ) strcpy(text, "Polecenie ""else"" bez wyst¹pienia ""if"" "); + if ( num == TX_OPENBLK ) strcpy(text, "Brak klamry otwieraj¹cej");//début d'un bloc attendu? + if ( num == TX_BADTYPE ) strcpy(text, "Z³y typ dla przypisania"); + if ( num == TX_REDEFVAR ) strcpy(text, "Zmienna nie mo¿e byæ zadeklarowana dwukrotnie"); + if ( num == TX_BAD2TYPE ) strcpy(text, "Niezgodne typy operatorów"); + if ( num == TX_UNDEFCALL ) strcpy(text, "Funkcja nieznana"); + if ( num == TX_MISDOTS ) strcpy(text, "Brak znaku "" : "); + if ( num == TX_WHILE ) strcpy(text, "Brak kluczowego s³owa ""while"); + if ( num == TX_BREAK ) strcpy(text, "Polecenie ""break"" na zewn¹trz pêtli"); + if ( num == TX_LABEL ) strcpy(text, "Po etykiecie musi wyst¹piæ ""for"", ""while"", ""do"" lub ""switch"""); + if ( num == TX_NOLABEL ) strcpy(text, "Taka etykieta nie istnieje");// Cette étiquette n'existe pas + if ( num == TX_NOCASE ) strcpy(text, "Brak polecenia ""case"); + if ( num == TX_BADNUM ) strcpy(text, "Brak liczby"); + if ( num == TX_VOID ) strcpy(text, "Pusty parametr"); + if ( num == TX_NOTYP ) strcpy(text, "Brak deklaracji typu"); + if ( num == TX_NOVAR ) strcpy(text, "Brak nazwy zmiennej"); + if ( num == TX_NOFONC ) strcpy(text, "Brakuj¹ca nazwa funkcji"); + if ( num == TX_OVERPARAM ) strcpy(text, "Za du¿o parametrów"); + if ( num == TX_REDEF ) strcpy(text, "Funkcja ju¿ istnieje"); + if ( num == TX_LOWPARAM ) strcpy(text, "Brak wymaganego parametru"); + if ( num == TX_BADPARAM ) strcpy(text, "Funkcja o tej nazwie nie akceptuje parametrów tego typu"); + if ( num == TX_NUMPARAM ) strcpy(text, "Funkcja o tej nazwie nie akceptuje takiej liczby parametrów"); + if ( num == TX_NOITEM ) strcpy(text, "To nie jest obiekt tej klasy"); + if ( num == TX_DOT ) strcpy(text, "Ten obiekt nie jest cz³onkiem klasy"); + if ( num == TX_NOCONST ) strcpy(text, "Brak odpowiedniego konstruktora"); + if ( num == TX_REDEFCLASS ) strcpy(text, "Taka klasa ju¿ istnieje"); + if ( num == TX_CLBRK ) strcpy(text, "Brak "" ] """); + if ( num == TX_RESERVED ) strcpy(text, "S³owo zarezerwowane jêzyka CBOT"); + if ( num == TX_BADNEW ) strcpy(text, "Z³y argument dla funkcji ""new"""); + if ( num == TX_OPBRK ) strcpy(text, "Oczekiwane "" [ """); + if ( num == TX_BADSTRING ) strcpy(text, "Brak ³añcucha"); + if ( num == TX_BADINDEX ) strcpy(text, "Nieprawid³owy typ indeksu"); + if ( num == TX_PRIVATE ) strcpy(text, "Element prywatny"); + if ( num == TX_NOPUBLIC ) strcpy(text, "Wymagany publiczny"); + if ( num == TX_DIVZERO ) strcpy(text, "Dzielenie przez zero"); + if ( num == TX_NOTINIT ) strcpy(text, "Zmienna nie zosta³a zainicjalizowana"); + if ( num == TX_BADTHROW ) strcpy(text, "Wartoœæ ujemna odrzucona przez ""throw""");//C'est quoi, ça? + if ( num == TX_NORETVAL ) strcpy(text, "Funkcja nie zwróci³a ¿adnej wartoœci "); + if ( num == TX_NORUN ) strcpy(text, "¯adna funkcja nie dzia³a"); + if ( num == TX_NOCALL ) strcpy(text, "Odwo³anie do nieznanej funkcji"); + if ( num == TX_NOCLASS ) strcpy(text, "Taka klasa nie istnieje"); + if ( num == TX_NULLPT ) strcpy(text, "Obiekt nieznany"); + if ( num == TX_OPNAN ) strcpy(text, "Dzia³anie niemo¿liwe z wartoœci¹ ""nan"""); + if ( num == TX_OUTARRAY ) strcpy(text, "Dostêp poza tablicê"); + if ( num == TX_STACKOVER ) strcpy(text, "Przepe³nienie stosu"); + if ( num == TX_DELETEDPT ) strcpy(text, "Nieprawid³owy obiekt"); + if ( num == TX_FILEOPEN ) strcpy(text, "Nie mo¿na otworzyæ pliku"); + if ( num == TX_NOTOPEN ) strcpy(text, "Plik nie jest otwarty"); + if ( num == TX_ERRREAD ) strcpy(text, "B³¹d odczytu"); + if ( num == TX_ERRWRITE ) strcpy(text, "B³¹d zapisu"); + } + + if ( type == RES_KEY ) + { + if ( num == 0 ) strcpy(text, "< brak >"); + if ( num == VK_LEFT ) strcpy(text, "Strza³ka w lewo"); + if ( num == VK_RIGHT ) strcpy(text, "Strza³ka w prawo"); + if ( num == VK_UP ) strcpy(text, "Strza³ka w górê"); + if ( num == VK_DOWN ) strcpy(text, "Strza³ka w dó³"); + if ( num == VK_CANCEL ) strcpy(text, "Ctrl-break"); + if ( num == VK_BACK ) strcpy(text, "<--"); + if ( num == VK_TAB ) strcpy(text, "Tab"); + if ( num == VK_CLEAR ) strcpy(text, "Delete"); + if ( num == VK_RETURN ) strcpy(text, "Enter"); + if ( num == VK_SHIFT ) strcpy(text, "Shift"); + if ( num == VK_CONTROL ) strcpy(text, "Ctrl"); + if ( num == VK_MENU ) strcpy(text, "Alt"); + if ( num == VK_PAUSE ) strcpy(text, "Pause"); + if ( num == VK_CAPITAL ) strcpy(text, "Caps Lock"); + if ( num == VK_ESCAPE ) strcpy(text, "Esc"); + if ( num == VK_SPACE ) strcpy(text, "Spacja"); + if ( num == VK_PRIOR ) strcpy(text, "Page Up"); + if ( num == VK_NEXT ) strcpy(text, "Page Down"); + if ( num == VK_END ) strcpy(text, "End"); + if ( num == VK_HOME ) strcpy(text, "Home"); + if ( num == VK_SELECT ) strcpy(text, "Zaznacz"); + if ( num == VK_EXECUTE ) strcpy(text, "Wykonaj"); + if ( num == VK_SNAPSHOT ) strcpy(text, "Print Scrn"); + if ( num == VK_INSERT ) strcpy(text, "Insert"); + if ( num == VK_DELETE ) strcpy(text, "Delete"); + if ( num == VK_HELP ) strcpy(text, "Pomoc"); + if ( num == VK_LWIN ) strcpy(text, "Lewy klawisz Windows"); + if ( num == VK_RWIN ) strcpy(text, "Prawy klawisz Windows"); + if ( num == VK_APPS ) strcpy(text, "Klawisz menu kontekstowego"); + if ( num == VK_NUMPAD0 ) strcpy(text, "Klaw. Num. 0"); + if ( num == VK_NUMPAD1 ) strcpy(text, "Klaw. Num. 1"); + if ( num == VK_NUMPAD2 ) strcpy(text, "Klaw. Num. 2"); + if ( num == VK_NUMPAD3 ) strcpy(text, "Klaw. Num. 3"); + if ( num == VK_NUMPAD4 ) strcpy(text, "Klaw. Num. 4"); + if ( num == VK_NUMPAD5 ) strcpy(text, "Klaw. Num. 5"); + if ( num == VK_NUMPAD6 ) strcpy(text, "Klaw. Num. 6"); + if ( num == VK_NUMPAD7 ) strcpy(text, "Klaw. Num. 7"); + if ( num == VK_NUMPAD8 ) strcpy(text, "Klaw. Num. 8"); + if ( num == VK_NUMPAD9 ) strcpy(text, "Klaw. Num. 9"); + if ( num == VK_MULTIPLY ) strcpy(text, "Klaw. Num. *"); + if ( num == VK_ADD ) strcpy(text, "Klaw. Num. +"); + if ( num == VK_SEPARATOR ) strcpy(text, "Klaw. Num. separator"); + if ( num == VK_SUBTRACT ) strcpy(text, "Klaw. Num. -"); + if ( num == VK_DECIMAL ) strcpy(text, "Klaw. Num. ."); + if ( num == VK_DIVIDE ) strcpy(text, "Klaw. Num. /"); + if ( num == VK_F1 ) strcpy(text, "F1"); + if ( num == VK_F2 ) strcpy(text, "F2"); + if ( num == VK_F3 ) strcpy(text, "F3"); + if ( num == VK_F4 ) strcpy(text, "F4"); + if ( num == VK_F5 ) strcpy(text, "F5"); + if ( num == VK_F6 ) strcpy(text, "F6"); + if ( num == VK_F7 ) strcpy(text, "F7"); + if ( num == VK_F8 ) strcpy(text, "F8"); + if ( num == VK_F9 ) strcpy(text, "F9"); + if ( num == VK_F10 ) strcpy(text, "F10"); + if ( num == VK_F11 ) strcpy(text, "F11"); + if ( num == VK_F12 ) strcpy(text, "F12"); + if ( num == VK_F13 ) strcpy(text, "F13"); + if ( num == VK_F14 ) strcpy(text, "F14"); + if ( num == VK_F15 ) strcpy(text, "F15"); + if ( num == VK_F16 ) strcpy(text, "F16"); + if ( num == VK_F17 ) strcpy(text, "F17"); + if ( num == VK_F18 ) strcpy(text, "F18"); + if ( num == VK_F19 ) strcpy(text, "F19"); + if ( num == VK_F20 ) strcpy(text, "F20"); + if ( num == VK_NUMLOCK ) strcpy(text, "Num Lock"); + if ( num == VK_SCROLL ) strcpy(text, "Scroll Lock"); + if ( num == VK_ATTN ) strcpy(text, "Attn"); + if ( num == VK_CRSEL ) strcpy(text, "CrSel"); + if ( num == VK_EXSEL ) strcpy(text, "ExSel"); + if ( num == VK_EREOF ) strcpy(text, "Erase EOF"); + if ( num == VK_PLAY ) strcpy(text, "Graj"); + if ( num == VK_ZOOM ) strcpy(text, "Powiêkszenie"); + if ( num == VK_PA1 ) strcpy(text, "PA1"); + if ( num == VK_OEM_CLEAR ) strcpy(text, "Wyczyœæ"); + if ( num == VK_BUTTON1 ) strcpy(text, "Przycisk 1"); + if ( num == VK_BUTTON2 ) strcpy(text, "Przycisk 2"); + if ( num == VK_BUTTON3 ) strcpy(text, "Przycisk 3"); + if ( num == VK_BUTTON4 ) strcpy(text, "Przycisk 4"); + if ( num == VK_BUTTON5 ) strcpy(text, "Przycisk 5"); + if ( num == VK_BUTTON6 ) strcpy(text, "Przycisk 6"); + if ( num == VK_BUTTON7 ) strcpy(text, "Przycisk 7"); + if ( num == VK_BUTTON8 ) strcpy(text, "Przycisk 8"); + if ( num == VK_BUTTON9 ) strcpy(text, "Przycisk 9"); + if ( num == VK_BUTTON10 ) strcpy(text, "Przycisk 10"); + if ( num == VK_BUTTON11 ) strcpy(text, "Przycisk 11"); + if ( num == VK_BUTTON12 ) strcpy(text, "Przycisk 12"); + if ( num == VK_BUTTON13 ) strcpy(text, "Przycisk 13"); + if ( num == VK_BUTTON14 ) strcpy(text, "Przycisk 14"); + if ( num == VK_BUTTON15 ) strcpy(text, "Przycisk 15"); + if ( num == VK_BUTTON16 ) strcpy(text, "Przycisk 16"); + if ( num == VK_BUTTON17 ) strcpy(text, "Przycisk 17"); + if ( num == VK_BUTTON18 ) strcpy(text, "Przycisk 18"); + if ( num == VK_BUTTON19 ) strcpy(text, "Przycisk 19"); + if ( num == VK_BUTTON20 ) strcpy(text, "Przycisk 20"); + if ( num == VK_BUTTON21 ) strcpy(text, "Przycisk 21"); + if ( num == VK_BUTTON22 ) strcpy(text, "Przycisk 22"); + if ( num == VK_BUTTON23 ) strcpy(text, "Przycisk 23"); + if ( num == VK_BUTTON24 ) strcpy(text, "Przycisk 24"); + if ( num == VK_BUTTON25 ) strcpy(text, "Przycisk 25"); + if ( num == VK_BUTTON26 ) strcpy(text, "Przycisk 26"); + if ( num == VK_BUTTON27 ) strcpy(text, "Przycisk 27"); + if ( num == VK_BUTTON28 ) strcpy(text, "Przycisk 28"); + if ( num == VK_BUTTON29 ) strcpy(text, "Przycisk 29"); + if ( num == VK_BUTTON30 ) strcpy(text, "Przycisk 30"); + if ( num == VK_BUTTON31 ) strcpy(text, "Przycisk 31"); + if ( num == VK_BUTTON32 ) strcpy(text, "Przycisk 32"); + if ( num == VK_WHEELUP ) strcpy(text, "Kó³ko w górê"); + if ( num == VK_WHEELDOWN ) strcpy(text, "Kó³ko w dó³"); + } +#endif + + return ( text[0] != 0 ); +} + + diff --git a/src/restext.h b/src/restext.h new file mode 100644 index 00000000..619e74a6 --- /dev/null +++ b/src/restext.h @@ -0,0 +1,141 @@ +// restext.h + +#ifndef _RESTEXT_H_ +#define _RESTEXT_H_ + + +#define STRICT +#define D3D_OVERLOADS + + + +enum KeyRank; + + +// Types possibles pour les ressources texte. + +enum ResType +{ + RES_TEXT = 0, // RT_* + RES_EVENT = 1, // EVENT_* (EventMsg) + RES_OBJECT = 2, // OBJECT_* (ObjectType) + RES_ERR = 3, // ERR_* (Error) + RES_KEY = 4, // VK_* (touches) + RES_CBOT = 5, // TX_* (cbot.dll) +}; + + +// Ressources de type RES_TEXT. + +#define RT_VERSION_ID 1 +#define RT_DISINFO_TITLE 2 +#define RT_WINDOW_MAXIMIZED 3 +#define RT_WINDOW_MINIMIZED 4 +#define RT_WINDOW_STANDARD 5 +#define RT_WINDOW_CLOSE 6 + +#define RT_STUDIO_TITLE 10 +#define RT_SCRIPT_NEW 20 +#define RT_NAME_DEFAULT 21 +#define RT_IO_NEW 22 +#define RT_KEY_OR 23 + +#define RT_TITLE_BASE 40 +#define RT_TITLE_INIT 41 +#define RT_TITLE_TRAINER 42 +#define RT_TITLE_DEFI 43 +#define RT_TITLE_MISSION 44 +#define RT_TITLE_FREE 45 +#define RT_TITLE_PROTO 46 +#define RT_TITLE_SETUP 47 +#define RT_TITLE_NAME 48 +#define RT_TITLE_PERSO 49 +#define RT_TITLE_WRITE 50 +#define RT_TITLE_READ 51 +#define RT_TITLE_USER 52 +#define RT_TITLE_TEEN 53 + +#define RT_PLAY_CHAPt 60 +#define RT_PLAY_CHAPd 61 +#define RT_PLAY_CHAPm 62 +#define RT_PLAY_CHAPf 63 +#define RT_PLAY_CHAPp 64 +#define RT_PLAY_LISTt 65 +#define RT_PLAY_LISTd 66 +#define RT_PLAY_LISTm 67 +#define RT_PLAY_LISTf 68 +#define RT_PLAY_LISTp 69 +#define RT_PLAY_RESUME 70 +#define RT_PLAY_CHAPu 71 +#define RT_PLAY_LISTu 72 +#define RT_PLAY_CHAPte 73 +#define RT_PLAY_LISTk 74 + +#define RT_SETUP_DEVICE 80 +#define RT_SETUP_MODE 81 +#define RT_SETUP_KEY1 82 +#define RT_SETUP_KEY2 83 + +#define RT_PERSO_FACE 90 +#define RT_PERSO_GLASSES 91 +#define RT_PERSO_HAIR 92 +#define RT_PERSO_COMBI 93 +#define RT_PERSO_BAND 94 + +#define RT_DIALOG_TITLE 100 +#define RT_DIALOG_ABORT 101 +#define RT_DIALOG_QUIT 102 +#define RT_DIALOG_YES 103 +#define RT_DIALOG_NO 104 +#define RT_DIALOG_DELOBJ 105 +#define RT_DIALOG_DELGAME 106 +#define RT_DIALOG_YESDEL 107 +#define RT_DIALOG_NODEL 108 +#define RT_DIALOG_LOADING 109 +#define RT_DIALOG_YESQUIT 110 +#define RT_DIALOG_NOQUIT 111 + +#define RT_STUDIO_LISTTT 120 +#define RT_STUDIO_COMPOK 121 +#define RT_STUDIO_PROGSTOP 122 + +#define RT_SATCOM_LIST 140 +#define RT_SATCOM_BOT 141 +#define RT_SATCOM_BUILDING 142 +#define RT_SATCOM_FRET 143 +#define RT_SATCOM_ALIEN 144 +#define RT_SATCOM_NULL 145 +#define RT_SATCOM_ERROR1 146 +#define RT_SATCOM_ERROR2 147 + +#define RT_IO_OPEN 150 +#define RT_IO_SAVE 151 +#define RT_IO_LIST 152 +#define RT_IO_NAME 153 +#define RT_IO_DIR 154 +#define RT_IO_PRIVATE 155 +#define RT_IO_PUBLIC 156 + +#define RT_GENERIC_DEV1 170 +#define RT_GENERIC_DEV2 171 +#define RT_GENERIC_EDIT1 172 +#define RT_GENERIC_EDIT2 173 + +#define RT_INTERFACE_REC 180 + +#define RT_MESSAGE_WIN 200 +#define RT_MESSAGE_LOST 201 + + +static CD3DEngine* g_engine = 0; +static char g_gamerName[100]; + +extern void SetEngine(CD3DEngine *engine); +extern void SetGlobalGamerName(char *name); +extern BOOL SearchKey(char *cmd, KeyRank &key); +extern void PutKeyName(char* dst, char* src); +extern BOOL GetResource(ResType type, int num, char* text); +extern BOOL GetResourceBase(ResType type, int num, char* text); + + +#endif //_RESTEXT_H_ diff --git a/src/robotmain.cpp b/src/robotmain.cpp new file mode 100644 index 00000000..1f6a7a91 --- /dev/null +++ b/src/robotmain.cpp @@ -0,0 +1,7018 @@ +// robotmain.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "cbot/cbotdll.h" +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "language.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "profile.h" +#include "iman.h" +#include "restext.h" +#include "math3d.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "water.h" +#include "cloud.h" +#include "blitz.h" +#include "planet.h" +#include "object.h" +#include "motion.h" +#include "motiontoto.h" +#include "motionhuman.h" +#include "physics.h" +#include "brain.h" +#include "pyro.h" +#include "modfile.h" +#include "model.h" +#include "camera.h" +#include "task.h" +#include "taskmanip.h" +#include "taskbuild.h" +#include "auto.h" +#include "autobase.h" +#include "displayinfo.h" +#include "interface.h" +#include "shortcut.h" +#include "map.h" +#include "label.h" +#include "button.h" +#include "slider.h" +#include "window.h" +#include "edit.h" +#include "displaytext.h" +#include "text.h" +#include "sound.h" +#include "cbottoken.h" +#include "cmdtoken.h" +#include "mainmovie.h" +#include "maindialog.h" +#include "mainshort.h" +#include "mainmap.h" +#include "script.h" +#include "robotmain.h" + + + +#define CBOT_STACK TRUE // enregistre le stack des programmes CBOT +#define UNIT 4.0f + + + +// Variables globales. + +long g_id; // identificateur unique +long g_build; // bâtiments constructibles +long g_researchDone; // recherches effectuées +long g_researchEnable; // recherches accessibles +float g_unit; // facteur de conversion + + + +#include "classfile.cpp" + + + +// Compilation de la classe "point". + +CBotTypResult cPoint(CBotVar* pThis, CBotVar* &var) +{ + if ( !pThis->IsElemOfClass("point") ) return CBotTypResult(CBotErrBadNum); + + if ( var == NULL ) return CBotTypResult(0); // ok si aucun paramètre + + // Premier paramètre (x) : + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + // Deuxième paramètre (y) : + if ( var == NULL ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + // Troisième paramètre (z) : + if ( var == NULL ) // seulement 2 paramètres ? + { + return CBotTypResult(0); // cette fonction retourne void + } + + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + if ( var != NULL ) return CBotTypResult(CBotErrOverParam); + + return CBotTypResult(0); // cette fonction retourne void +} + +// Exécution de la classe "point". + +BOOL rPoint(CBotVar* pThis, CBotVar* var, CBotVar* pResult, int& Exception) +{ + CBotVar *pX, *pY, *pZ; + + if ( var == NULL ) return TRUE; // constructeur sans paramètres est ok + + if ( var->GivType() > CBotTypDouble ) + { + Exception = CBotErrBadNum; return FALSE; + } + + pX = pThis->GivItem("x"); + if ( pX == NULL ) + { + Exception = CBotErrUndefItem; return FALSE; + } + pX->SetValFloat( var->GivValFloat() ); + var = var->GivNext(); + + if ( var == NULL ) + { + Exception = CBotErrLowParam; return FALSE; + } + + if ( var->GivType() > CBotTypDouble ) + { + Exception = CBotErrBadNum; return FALSE; + } + + pY = pThis->GivItem("y"); + if ( pY == NULL ) + { + Exception = CBotErrUndefItem; return FALSE; + } + pY->SetValFloat( var->GivValFloat() ); + var = var->GivNext(); + + if ( var == NULL ) + { + return TRUE; // ok avec seulement 2 paramètres + } + + pZ = pThis->GivItem("z"); + if ( pZ == NULL ) + { + Exception = CBotErrUndefItem; return FALSE; + } + pZ->SetValFloat( var->GivValFloat() ); + var = var->GivNext(); + + if ( var != NULL ) + { + Exception = CBotErrOverParam; return FALSE; + } + + return TRUE; // pas d'interruption +} + + + + +// Constructeur de l'application robot. + +CRobotMain::CRobotMain(CInstanceManager* iMan) +{ + ObjectType type; + float fValue; + int iValue, i; + char* token; + + m_iMan = iMan; + m_iMan->AddInstance(CLASS_MAIN, this); + + m_event = (CEvent*)m_iMan->SearchInstance(CLASS_EVENT); + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_light = (CLight*)m_iMan->SearchInstance(CLASS_LIGHT); + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + m_cloud = (CCloud*)m_iMan->SearchInstance(CLASS_CLOUD); + m_blitz = (CBlitz*)m_iMan->SearchInstance(CLASS_BLITZ); + m_planet = (CPlanet*)m_iMan->SearchInstance(CLASS_PLANET); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + + m_interface = new CInterface(m_iMan); + m_terrain = new CTerrain(m_iMan); + m_model = new CModel(m_iMan); + m_camera = new CCamera(m_iMan); + m_displayText = new CDisplayText(m_iMan); + m_movie = new CMainMovie(m_iMan); + m_dialog = new CMainDialog(m_iMan); + m_short = new CMainShort(m_iMan); + m_map = new CMainMap(m_iMan); + m_displayInfo = 0; + + m_engine->SetTerrain(m_terrain); + m_filesDir = m_dialog->RetFilesDir(); + + m_time = 0.0f; + m_gameTime = 0.0f; + m_checkEndTime = 0.0f; + + m_phase = PHASE_NAME; + m_cameraRank = -1; + m_visitLast = EVENT_NULL; + m_visitObject = 0; + m_visitArrow = 0; + m_audioTrack = 0; + m_bAudioRepeat = TRUE; + m_delayWriteMessage = 0; + m_selectObject = 0; + m_infoUsed = 0; + + m_bBeginSatCom = FALSE; + m_bMovieLock = FALSE; + m_bSatComLock = FALSE; + m_bEditLock = FALSE; + m_bEditFull = FALSE; + m_bPause = FALSE; + m_bHilite = FALSE; + m_bFreePhoto = FALSE; + m_bShowPos = FALSE; + m_bSelectInsect = FALSE; + m_bShowSoluce = FALSE; + m_bShowAll = FALSE; + m_bCheatRadar = FALSE; + m_bFixScene = FALSE; + m_bTrainerPilot = FALSE; + m_bSuspend = FALSE; + m_bFriendAim = FALSE; + m_bResetCreate = FALSE; + m_bShortCut = TRUE; + + m_engine->SetMovieLock(m_bMovieLock); + + m_movie->Flush(); + m_movieInfoIndex = -1; + + m_tooltipPos = FPOINT(0.0f, 0.0f); + m_tooltipName[0] = 0; + m_tooltipTime = 0.0f; + + m_endingWinRank = 0; + m_endingLostRank = 0; + m_bWinTerminate = FALSE; + + FlushDisplayInfo(); + + m_fontSize = 9.0f; + m_windowPos = FPOINT(0.15f, 0.17f); + m_windowDim = FPOINT(0.70f, 0.66f); + + if ( GetProfileFloat("Edit", "FontSize", fValue) ) m_fontSize = fValue; + if ( GetProfileFloat("Edit", "WindowPos.x", fValue) ) m_windowPos.x = fValue; + if ( GetProfileFloat("Edit", "WindowPos.y", fValue) ) m_windowPos.y = fValue; + if ( GetProfileFloat("Edit", "WindowDim.x", fValue) ) m_windowDim.x = fValue; + if ( GetProfileFloat("Edit", "WindowDim.y", fValue) ) m_windowDim.y = fValue; + + m_IOPublic = FALSE; + m_IODim = FPOINT(320.0f/640.0f, (121.0f+18.0f*8)/480.0f); + m_IOPos.x = (1.0f-m_IODim.x)/2.0f; // au milieu + m_IOPos.y = (1.0f-m_IODim.y)/2.0f; + + if ( GetProfileInt ("Edit", "IOPublic", iValue) ) m_IOPublic = iValue; + if ( GetProfileFloat("Edit", "IOPos.x", fValue) ) m_IOPos.x = fValue; + if ( GetProfileFloat("Edit", "IOPos.y", fValue) ) m_IOPos.y = fValue; + if ( GetProfileFloat("Edit", "IODim.x", fValue) ) m_IODim.x = fValue; + if ( GetProfileFloat("Edit", "IODim.y", fValue) ) m_IODim.y = fValue; + + m_short->FlushShortcuts(); + InitEye(); + + m_engine->SetTracePrecision(1.0f); + + m_cameraPan = 0.0f; + m_cameraZoom = 0.0f; + + g_id = 0; + g_build = 0; + g_researchDone = 0; // aucune recherche effectuée + g_researchEnable = 0; + g_unit = 4.0f; + + m_gamerName[0] = 0; + GetProfileString("Gamer", "LastName", m_gamerName, 100); + SetGlobalGamerName(m_gamerName); + ReadFreeParam(); + m_dialog->SetupRecall(); + + for ( i=0 ; iAddItem("x", CBotTypFloat); + bc->AddItem("y", CBotTypFloat); + bc->AddItem("z", CBotTypFloat); + bc->AddFunction("point", rPoint, cPoint); + + // Ajoute la classe Object. + bc = new CBotClass("object", NULL); + bc->AddItem("category", CBotTypResult(CBotTypInt), PR_READ); + bc->AddItem("position", CBotTypResult(CBotTypClass, "point"), PR_READ); + bc->AddItem("orientation", CBotTypResult(CBotTypFloat), PR_READ); + bc->AddItem("pitch", CBotTypResult(CBotTypFloat), PR_READ); + bc->AddItem("roll", CBotTypResult(CBotTypFloat), PR_READ); + bc->AddItem("energyLevel", CBotTypResult(CBotTypFloat), PR_READ); + bc->AddItem("shieldLevel", CBotTypResult(CBotTypFloat), PR_READ); + bc->AddItem("temperature", CBotTypResult(CBotTypFloat), PR_READ); + bc->AddItem("altitude", CBotTypResult(CBotTypFloat), PR_READ); + bc->AddItem("lifeTime", CBotTypResult(CBotTypFloat), PR_READ); + bc->AddItem("material", CBotTypResult(CBotTypInt), PR_READ); + bc->AddItem("energyCell", CBotTypResult(CBotTypPointer, "object"), PR_READ); + bc->AddItem("load", CBotTypResult(CBotTypPointer, "object"), PR_READ); + + // Initialise la classe FILE. + InitClassFILE(); + + CScript::InitFonctions(); +} + +// Destructeur de l'application robot. + +CRobotMain::~CRobotMain() +{ + delete m_movie; + delete m_dialog; + delete m_short; + delete m_map; + delete m_terrain; + delete m_model; +} + + +// Crée le fichier colobot.ini la première fois. + +void CRobotMain::CreateIni() +{ + int iValue; + + // colobot.ini inexistant ? + if ( !GetProfileInt("Setup", "TotoMode", iValue) ) + { + m_dialog->SetupMemorize(); + } +} + + +// Change de phase. + +void CRobotMain::ChangePhase(Phase phase) +{ + CEdit* pe; + CButton* pb; + D3DCOLORVALUE color; + FPOINT pos, dim, ddim; + float ox, oy, sx, sy; + char* read; + int rank, numTry; + BOOL bLoading; + + if ( m_phase == PHASE_SIMUL ) // termine une simulation ? + { + SaveAllScript(); + m_sound->StopMusic(); + m_camera->SetObject(0); + +#if _SCHOOL + if ( TRUE ) +#else + if ( m_gameTime > 10.0f ) // a-t-on joué au moins 10 secondes ? +#endif + { + rank = m_dialog->RetSceneRank(); + numTry = m_dialog->RetGamerInfoTry(rank); + m_dialog->SetGamerInfoTry(rank, numTry+1); + m_dialog->WriteGamerInfo(); + } + } + + if ( phase == PHASE_WIN ) // gagné une simulation ? + { + rank = m_dialog->RetSceneRank(); + m_dialog->SetGamerInfoPassed(rank, TRUE); + m_dialog->NextMission(); // passe à la mission suivante + m_dialog->WriteGamerInfo(); + } + + DeleteAllObjects(); // supprime toute la scène 3D actuelle + + m_phase = phase; + m_winDelay = 0.0f; + m_lostDelay = 0.0f; + m_bBeginSatCom = FALSE; + m_bMovieLock = FALSE; + m_bSatComLock = FALSE; + m_bEditLock = FALSE; + m_bFreePhoto = FALSE; + m_bResetCreate = FALSE; + + m_engine->SetMovieLock(m_bMovieLock); + ChangePause(FALSE); + FlushDisplayInfo(); + m_engine->SetRankView(0); + m_engine->FlushObject(); + color.r = color.g = color.b = color.a = 0.0f; + m_engine->SetWaterAddColor(color); + m_engine->SetBackground(""); + m_engine->SetBackForce(FALSE); + m_engine->SetFrontsizeName(""); + m_engine->SetOverColor(); + m_engine->GroundMarkDelete(0); + SetSpeed(1.0f); + m_terrain->SetWind(D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_terrain->FlushBuildingLevel(); + m_terrain->FlushFlyingLimit(); + m_light->FlushLight(); + m_particule->FlushParticule(); + m_water->Flush(); + m_cloud->Flush(); + m_blitz->Flush(); + m_planet->Flush(); + m_iMan->Flush(CLASS_OBJECT); + m_iMan->Flush(CLASS_PHYSICS); + m_iMan->Flush(CLASS_BRAIN); + m_iMan->Flush(CLASS_PYRO); + m_model->StopUserAction(); + m_interface->Flush(); + ClearInterface(); + FlushNewScriptName(); + m_sound->SetListener(D3DVECTOR(0.0f, 0.0f, 0.0f), D3DVECTOR(0.0f, 0.0f, 1.0f)); + m_camera->SetType(CAMERA_DIALOG); + m_movie->Flush(); + m_movieInfoIndex = -1; + m_cameraPan = 0.0f; + m_cameraZoom = 0.0f; + m_bShortCut = TRUE; + + // Crée et cache la console de commande. + dim.x = 200.0f/640.0f; + dim.y = 18.0f/480.0f; + pos.x = 50.0f/640.0f; + pos.y = 452.0f/480.0f; + pe = m_interface->CreateEdit(pos, dim, 0, EVENT_CMD); + if ( pe == 0 ) return; + pe->ClearState(STATE_VISIBLE); + m_bCmdEdit = FALSE; // caché pour l'instant + + // Crée l'indicateur de vitesse. +#if _TEEN + dim.x = 30.0f/640.0f; + dim.y = 20.0f/480.0f; + pos.x = 4.0f/640.0f; + pos.y = 454.0f/480.0f; +#else + dim.x = 30.0f/640.0f; + dim.y = 20.0f/480.0f; + pos.x = 4.0f/640.0f; + pos.y = 426.0f/480.0f; +#endif + pb = m_interface->CreateButton(pos, dim, 0, EVENT_SPEED); + if ( pb == 0 ) return; + pb->SetState(STATE_SIMPLY); + pb->ClearState(STATE_VISIBLE); + + m_dialog->ChangePhase(m_phase); + + dim.x = 32.0f/640.0f; + dim.y = 32.0f/480.0f; + ox = 3.0f/640.0f; + oy = 3.0f/480.0f; + sx = (32.0f+2.0f)/640.0f; + sy = (32.0f+2.0f)/480.0f; + + if ( m_phase != PHASE_PERSO ) + { + m_engine->SetDrawWorld(TRUE); + m_engine->SetDrawFront(FALSE); + m_bFixScene = FALSE; + } + + if ( m_phase == PHASE_INIT ) + { +#if _NEWLOOK + m_engine->FreeTexture("generna.tga"); + m_engine->FreeTexture("genernb.tga"); + m_engine->FreeTexture("genernc.tga"); + m_engine->FreeTexture("genernd.tga"); +#else +#if _FRENCH +#if _DEMO + m_engine->FreeTexture("genedfa.tga"); + m_engine->FreeTexture("genedfb.tga"); + m_engine->FreeTexture("genedfc.tga"); + m_engine->FreeTexture("genedfd.tga"); +#else + m_engine->FreeTexture("generfa.tga"); + m_engine->FreeTexture("generfb.tga"); + m_engine->FreeTexture("generfc.tga"); + m_engine->FreeTexture("generfd.tga"); +#endif +#endif +#if _ENGLISH +#if _DEMO + m_engine->FreeTexture("genedea.tga"); + m_engine->FreeTexture("genedeb.tga"); + m_engine->FreeTexture("genedec.tga"); + m_engine->FreeTexture("geneded.tga"); +#else + m_engine->FreeTexture("generea.tga"); + m_engine->FreeTexture("genereb.tga"); + m_engine->FreeTexture("generec.tga"); + m_engine->FreeTexture("genered.tga"); +#endif +#endif +#if _GERMAN +#if _DEMO + m_engine->FreeTexture("genedda.tga"); + m_engine->FreeTexture("geneddb.tga"); + m_engine->FreeTexture("geneddc.tga"); + m_engine->FreeTexture("geneddd.tga"); +#else + m_engine->FreeTexture("generea.tga"); + m_engine->FreeTexture("genereb.tga"); + m_engine->FreeTexture("generec.tga"); + m_engine->FreeTexture("genered.tga"); +#endif +#endif +#if _WG +#if _DEMO + m_engine->FreeTexture("genedda.tga"); + m_engine->FreeTexture("geneddb.tga"); + m_engine->FreeTexture("geneddc.tga"); + m_engine->FreeTexture("geneddd.tga"); +#else + m_engine->FreeTexture("generda.tga"); + m_engine->FreeTexture("generdb.tga"); + m_engine->FreeTexture("generdc.tga"); + m_engine->FreeTexture("generdd.tga"); +#endif +#endif +#if _POLISH +#if _DEMO + m_engine->FreeTexture("genedpa.tga"); + m_engine->FreeTexture("genedpb.tga"); + m_engine->FreeTexture("genedpc.tga"); + m_engine->FreeTexture("genedpd.tga"); +#else + m_engine->FreeTexture("generpa.tga"); + m_engine->FreeTexture("generpb.tga"); + m_engine->FreeTexture("generpc.tga"); + m_engine->FreeTexture("generpd.tga"); +#endif +#endif +#endif + } + + if ( m_phase == PHASE_SIMUL ) + { + m_engine->FreeTexture("inter01a.tga"); + m_engine->FreeTexture("inter01b.tga"); + m_engine->FreeTexture("inter01c.tga"); + m_engine->FreeTexture("inter01d.tga"); + + read = m_dialog->RetSceneRead(); + bLoading = (read[0] != 0); + + m_map->CreateMap(); + CreateScene(m_dialog->RetSceneSoluce(), FALSE, FALSE); // scène interractive + if ( m_bMapImage ) + { + m_map->SetFixImage(m_mapFilename); + } + + pos.x = 620.0f/640.0f; + pos.y = 460.0f/480.0f; + ddim.x = 20.0f/640.0f; + ddim.y = 20.0f/480.0f; + m_interface->CreateButton(pos, ddim, 11, EVENT_BUTTON_QUIT); + + if ( m_bImmediatSatCom && !bLoading && + m_infoFilename[SATCOM_HUSTON][0] != 0 ) + { + StartDisplayInfo(SATCOM_HUSTON, FALSE); // affiche les instructions + } + + m_sound->StopMusic(); + if ( !m_bBase || bLoading ) StartMusic(); + } + + if ( m_phase == PHASE_WIN ) + { + if ( m_endingWinRank == -1 ) + { + ChangePhase(PHASE_TERM); + } + else + { +#if _TEEN + m_bWinTerminate = (m_endingWinRank == 900); + m_dialog->SetSceneName("teenw"); +#else + m_bWinTerminate = (m_endingWinRank == 904); + m_dialog->SetSceneName("win"); +#endif + m_dialog->SetSceneRank(m_endingWinRank); + CreateScene(FALSE, TRUE, FALSE); // scène fixe + + pos.x = ox+sx*1; pos.y = oy+sy*1; + ddim.x = dim.x*2; ddim.y = dim.y*2; + m_interface->CreateButton(pos, ddim, 16, EVENT_BUTTON_OK); + + if ( m_bWinTerminate ) + { +#if _TEEN + pos.x = ox+sx*3; pos.y = oy+sy*1; + ddim.x = dim.x*15; ddim.y = dim.y*2; + pe = m_interface->CreateEdit(pos, ddim, 0, EVENT_EDIT0); + pe->SetFontType(FONT_COLOBOT); + pe->SetEditCap(FALSE); + pe->SetHiliteCap(FALSE); + pe->ReadText("help\\teenw.txt"); +#else + pos.x = ox+sx*3; pos.y = oy+sy*0.2f; + ddim.x = dim.x*15; ddim.y = dim.y*3.0f; + pe = m_interface->CreateEdit(pos, ddim, 0, EVENT_EDIT0); + pe->SetGenericMode(TRUE); + pe->SetFontType(FONT_COLOBOT); + pe->SetEditCap(FALSE); + pe->SetHiliteCap(FALSE); + pe->ReadText("help\\win.txt"); +#endif + } + else + { + m_displayText->DisplayError(INFO_WIN, D3DVECTOR(0.0f,0.0f,0.0f), 15.0f, 60.0f, 1000.0f); + } + } + m_sound->StopAll(); + StartMusic(); + } + + if ( m_phase == PHASE_LOST ) + { + if ( m_endingLostRank == -1 ) + { + ChangePhase(PHASE_TERM); + } + else + { + m_bWinTerminate = FALSE; + m_dialog->SetSceneName("lost"); + m_dialog->SetSceneRank(m_endingLostRank); + CreateScene(FALSE, TRUE, FALSE); // scène fixe + + pos.x = ox+sx*1; pos.y = oy+sy*1; + ddim.x = dim.x*2; ddim.y = dim.y*2; + m_interface->CreateButton(pos, ddim, 16, EVENT_BUTTON_OK); + m_displayText->DisplayError(INFO_LOST, D3DVECTOR(0.0f,0.0f,0.0f), 15.0f, 60.0f, 1000.0f); + } + m_sound->StopAll(); + StartMusic(); + } + + if ( m_phase == PHASE_MODEL ) + { + pos.x = ox+sx*0; pos.y = oy+sy*0; + m_interface->CreateButton(pos, dim, 11, EVENT_BUTTON_CANCEL); + + CreateModel(); + } + + if ( m_phase == PHASE_LOADING ) + { + m_engine->SetMouseHide(TRUE); + } + else + { + m_engine->SetMouseHide(FALSE); + } + + m_engine->LoadAllTexture(); +} + + +// Traite un événement. + +BOOL CRobotMain::EventProcess(const Event &event) +{ + CEdit* pe; + CObject* pObj; + Event newEvent; + MainMovieType type; + int i; + + if ( event.event == EVENT_FRAME ) + { + if ( !m_movie->EventProcess(event) ) // fin du film ? + { + type = m_movie->RetStopType(); + if ( type == MM_SATCOMopen ) + { + ChangePause(FALSE); + SelectObject(m_infoObject, FALSE); // remet les boutons de commande + m_map->ShowMap(m_bMapShow); + m_displayText->HideText(FALSE); + i = m_movieInfoIndex; + StartDisplayInfo(m_movieInfoIndex, FALSE); + m_movieInfoIndex = i; + } + } + + m_dialog->EventProcess(event); + m_displayText->EventProcess(event); + RemoteCamera(m_cameraPan, m_cameraZoom, event.rTime); + + m_interface->EventProcess(event); + if ( m_displayInfo != 0 ) // édition en cours ? + { + m_displayInfo->EventProcess(event); + } + return EventFrame(event); + } + + // Gestion de la console de commande. +#if 0 + if ( m_phase != PHASE_NAME && + !m_movie->IsExist() && + event.event == EVENT_KEYDOWN && + event.param == VK_PAUSE && + (event.keyState&KS_CONTROL) != 0 ) +#else + if ( m_phase != PHASE_NAME && + !m_movie->IsExist() && + event.event == EVENT_KEYDOWN && + event.param == VK_CANCEL ) // Ctrl+Pause ? +#endif + { + pe = (CEdit*)m_interface->SearchControl(EVENT_CMD); + if ( pe == 0 ) return FALSE; + pe->SetState(STATE_VISIBLE); + pe->SetFocus(TRUE); + if ( m_phase == PHASE_SIMUL ) ChangePause(TRUE); + m_bCmdEdit = TRUE; + return FALSE; + } + if ( event.event == EVENT_KEYDOWN && + event.param == VK_RETURN && m_bCmdEdit ) + { + char cmd[50]; + pe = (CEdit*)m_interface->SearchControl(EVENT_CMD); + if ( pe == 0 ) return FALSE; + pe->GetText(cmd, 50); + pe->SetText(""); + pe->ClearState(STATE_VISIBLE); + if ( m_phase == PHASE_SIMUL ) ChangePause(FALSE); + ExecuteCmd(cmd); + m_bCmdEdit = FALSE; + return FALSE; + } + + // Gestion du changement de vitesse. + if ( event.event == EVENT_SPEED ) + { + SetSpeed(1.0f); + } + + if ( !m_dialog->EventProcess(event) ) + { + if ( event.event == EVENT_MOUSEMOVE ) + { + m_lastMousePos = event.pos; + HiliteObject(event.pos); + } + return FALSE; + } + + if ( !m_displayText->EventProcess(event) ) + { + return FALSE; + } + + if ( event.event == EVENT_MOUSEMOVE ) + { + m_lastMousePos = event.pos; + HiliteObject(event.pos); + } + + if ( m_displayInfo != 0 ) // info en cours ? + { + m_displayInfo->EventProcess(event); + + if ( event.event == EVENT_KEYDOWN ) + { + if ( event.param == m_engine->RetKey(KEYRANK_HELP, 0) || + event.param == m_engine->RetKey(KEYRANK_HELP, 1) || + event.param == m_engine->RetKey(KEYRANK_PROG, 0) || + event.param == m_engine->RetKey(KEYRANK_PROG, 1) || + event.param == VK_ESCAPE ) + { + StopDisplayInfo(); + } + } + if ( event.event == EVENT_OBJECT_INFOOK ) + { + StopDisplayInfo(); + } + return FALSE; + } + + // Phase de simulation du jeu. + if ( m_phase == PHASE_SIMUL ) + { + UpdateInfoText(); + + if ( !m_bEditFull ) + { + m_camera->EventProcess(event); + } + + switch( event.event ) + { + case EVENT_KEYDOWN: + KeyCamera(event.event, event.param); + HiliteClear(); + if ( event.param == VK_F11 ) + { + m_particule->WriteWheelTrace("Savegame\\t.bmp", 256, 256, D3DVECTOR(16.0f, 0.0f, -368.0f), D3DVECTOR(140.0f, 0.0f, -248.0f)); + return FALSE; + } + if ( m_bEditLock ) // édition en cours ? + { + if ( event.param == m_engine->RetKey(KEYRANK_HELP, 0) || + event.param == m_engine->RetKey(KEYRANK_HELP, 1) ) + { + StartDisplayInfo(SATCOM_HUSTON, FALSE); + return FALSE; + } + if ( event.param == m_engine->RetKey(KEYRANK_PROG, 0) || + event.param == m_engine->RetKey(KEYRANK_PROG, 1) ) + { + StartDisplayInfo(SATCOM_PROG, FALSE); + return FALSE; + } + break; + } + if ( m_bMovieLock ) // film en cours ? + { + if ( event.param == m_engine->RetKey(KEYRANK_QUIT, 0) || + event.param == m_engine->RetKey(KEYRANK_QUIT, 1) || + event.param == VK_ESCAPE ) + { + AbortMovie(); + } + return FALSE; + } + if ( m_camera->RetType() == CAMERA_VISIT ) + { + if ( event.param == m_engine->RetKey(KEYRANK_VISIT, 0) || + event.param == m_engine->RetKey(KEYRANK_VISIT, 1) ) + { + StartDisplayVisit(EVENT_NULL); + } + if ( event.param == m_engine->RetKey(KEYRANK_QUIT, 0) || + event.param == m_engine->RetKey(KEYRANK_QUIT, 1) || + event.param == VK_ESCAPE ) + { + StopDisplayVisit(); + } + return FALSE; + } + if ( event.param == m_engine->RetKey(KEYRANK_QUIT, 0) || + event.param == m_engine->RetKey(KEYRANK_QUIT, 1) ) + { + if ( m_movie->IsExist() ) + { + StartDisplayInfo(SATCOM_HUSTON, FALSE); + } + else if ( m_winDelay > 0.0f ) + { + ChangePhase(PHASE_WIN); + } + else if ( m_lostDelay > 0.0f ) + { + ChangePhase(PHASE_LOST); + } + else + { + m_dialog->StartAbort(); // voulez-vous quitter ? + } + } + if ( event.param == VK_PAUSE ) + { + if ( !m_bMovieLock && !m_bEditLock && !m_bCmdEdit && + m_camera->RetType() != CAMERA_VISIT && + !m_movie->IsExist() ) + { + ChangePause(!m_engine->RetPause()); + } + } + if ( event.param == m_engine->RetKey(KEYRANK_CAMERA, 0) || + event.param == m_engine->RetKey(KEYRANK_CAMERA, 1) ) + { + ChangeCamera(); + } + if ( event.param == m_engine->RetKey(KEYRANK_DESEL, 0) || + event.param == m_engine->RetKey(KEYRANK_DESEL, 1) ) + { + if ( m_bShortCut ) + { + DeselectObject(); + } + } + if ( event.param == m_engine->RetKey(KEYRANK_HUMAN, 0) || + event.param == m_engine->RetKey(KEYRANK_HUMAN, 1) ) + { + SelectHuman(); + } + if ( event.param == m_engine->RetKey(KEYRANK_NEXT, 0) || + event.param == m_engine->RetKey(KEYRANK_NEXT, 1) ) + { + if ( m_bShortCut ) + { + m_short->SelectNext(); + } + } + if ( event.param == m_engine->RetKey(KEYRANK_HELP, 0) || + event.param == m_engine->RetKey(KEYRANK_HELP, 1) ) + { + StartDisplayInfo(SATCOM_HUSTON, TRUE); + } + if ( event.param == m_engine->RetKey(KEYRANK_PROG, 0) || + event.param == m_engine->RetKey(KEYRANK_PROG, 1) ) + { + StartDisplayInfo(SATCOM_PROG, TRUE); + } + if ( event.param == m_engine->RetKey(KEYRANK_VISIT, 0) || + event.param == m_engine->RetKey(KEYRANK_VISIT, 1) ) + { + StartDisplayVisit(EVENT_NULL); + } + if ( event.param == m_engine->RetKey(KEYRANK_SPEED10, 0) || + event.param == m_engine->RetKey(KEYRANK_SPEED10, 1) ) + { + SetSpeed(1.0f); + } + if ( event.param == m_engine->RetKey(KEYRANK_SPEED15, 0) || + event.param == m_engine->RetKey(KEYRANK_SPEED15, 1) ) + { + SetSpeed(1.5f); + } + if ( event.param == m_engine->RetKey(KEYRANK_SPEED20, 0) || + event.param == m_engine->RetKey(KEYRANK_SPEED20, 1) ) + { + SetSpeed(2.0f); + } + if ( event.param == m_engine->RetKey(KEYRANK_SPEED30, 0) || + event.param == m_engine->RetKey(KEYRANK_SPEED30, 1) ) + { + SetSpeed(3.0f); + } + break; + + case EVENT_KEYUP: + KeyCamera(event.event, event.param); + break; + + case EVENT_LBUTTONDOWN: + pObj = DetectObject(event.pos); + if ( !m_bShortCut ) pObj = 0; + if ( pObj != 0 && pObj->RetType() == OBJECT_TOTO ) + { + if ( m_displayInfo != 0 ) // info en cours ? + { + StopDisplayInfo(); + } + else + { + if ( !m_bEditLock ) + { + StartDisplayInfo(SATCOM_HUSTON, TRUE); + } + } + } + else + { + SelectObject(pObj); + } + break; + + case EVENT_LBUTTONUP: + m_cameraPan = 0.0f; + m_cameraZoom = 0.0f; + break; + + case EVENT_BUTTON_QUIT: + if ( m_movie->IsExist() ) + { + StartDisplayInfo(SATCOM_HUSTON, FALSE); + } + else if ( m_winDelay > 0.0f ) + { + ChangePhase(PHASE_WIN); + } + else if ( m_lostDelay > 0.0f ) + { + ChangePhase(PHASE_LOST); + } + else + { + m_dialog->StartAbort(); // voulez-vous quitter ? + } + break; + + case EVENT_OBJECT_LIMIT: + StartShowLimit(); + break; + + case EVENT_OBJECT_DESELECT: + if ( m_bShortCut ) + { + DeselectObject(); + } + break; + + case EVENT_OBJECT_HELP: + HelpObject(); + break; + + case EVENT_OBJECT_CAMERA: + ChangeCamera(); + break; + + case EVENT_OBJECT_CAMERAleft: + m_cameraPan = -1.0f; + break; + case EVENT_OBJECT_CAMERAright: + m_cameraPan = 1.0f; + break; + case EVENT_OBJECT_CAMERAnear: + m_cameraZoom = -1.0f; + break; + case EVENT_OBJECT_CAMERAaway: + m_cameraZoom = 1.0f; + break; + + case EVENT_OBJECT_DELETE: + m_dialog->StartDeleteObject(); // voulez-vous détruire ? + break; + + case EVENT_OBJECT_BHELP: + StartDisplayInfo(SATCOM_HUSTON, TRUE); + break; + + case EVENT_OBJECT_SOLUCE: + StartDisplayInfo(SATCOM_SOLUCE, TRUE); + break; + + case EVENT_OBJECT_MAPZOOM: + m_map->ZoomMap(); + break; + + case EVENT_DT_VISIT0: + case EVENT_DT_VISIT1: + case EVENT_DT_VISIT2: + case EVENT_DT_VISIT3: + case EVENT_DT_VISIT4: + StartDisplayVisit(event.event); + break; + + case EVENT_DT_END: + StopDisplayVisit(); + break; + + case EVENT_OBJECT_SHORTCUT00: + case EVENT_OBJECT_SHORTCUT01: + case EVENT_OBJECT_SHORTCUT02: + case EVENT_OBJECT_SHORTCUT03: + case EVENT_OBJECT_SHORTCUT04: + case EVENT_OBJECT_SHORTCUT05: + case EVENT_OBJECT_SHORTCUT06: + case EVENT_OBJECT_SHORTCUT07: + case EVENT_OBJECT_SHORTCUT08: + case EVENT_OBJECT_SHORTCUT09: + case EVENT_OBJECT_SHORTCUT10: + case EVENT_OBJECT_SHORTCUT11: + case EVENT_OBJECT_SHORTCUT12: + case EVENT_OBJECT_SHORTCUT13: + case EVENT_OBJECT_SHORTCUT14: + case EVENT_OBJECT_SHORTCUT15: + case EVENT_OBJECT_SHORTCUT16: + case EVENT_OBJECT_SHORTCUT17: + case EVENT_OBJECT_SHORTCUT18: + case EVENT_OBJECT_SHORTCUT19: + m_short->SelectShortcut(event.event); + break; + + case EVENT_OBJECT_MOVIELOCK: + AbortMovie(); + break; + + case EVENT_WIN: + ChangePhase(PHASE_WIN); + break; + + case EVENT_LOST: + ChangePhase(PHASE_LOST); + break; + } + + EventObject(event); + return FALSE; + } + + if ( m_phase == PHASE_PERSO ) + { + EventObject(event); + } + + if ( m_phase == PHASE_WIN || + m_phase == PHASE_LOST ) + { + EventObject(event); + + switch( event.event ) + { + case EVENT_KEYDOWN: + if ( event.param == VK_ESCAPE || + event.param == VK_RETURN ) + { + if ( m_bWinTerminate ) + { + ChangePhase(PHASE_INIT); + } + else + { + ChangePhase(PHASE_TERM); + } + } + break; + + case EVENT_BUTTON_OK: + if ( m_bWinTerminate ) + { + ChangePhase(PHASE_INIT); + } + else + { + ChangePhase(PHASE_TERM); + } + break; + } + } + + if ( m_phase == PHASE_MODEL ) + { + switch( event.event ) + { + case EVENT_KEYDOWN: + if ( event.param == VK_ESCAPE ) + { + ChangePhase(PHASE_INIT); + } + if ( event.param == VK_HOME ) + { + InitEye(); + } + break; + + case EVENT_BUTTON_CANCEL: + ChangePhase(PHASE_INIT); + break; + } + + m_model->EventProcess(event); + return FALSE; + } + + return TRUE; +} + + + +// Exécute une commande. + +void CRobotMain::ExecuteCmd(char *cmd) +{ + if ( cmd[0] == 0 ) return; + + if ( m_phase == PHASE_SIMUL ) + { + if ( strcmp(cmd, "winmission") == 0 ) + { + Event newEvent; + m_event->MakeEvent(newEvent, EVENT_WIN); + m_event->AddEvent(newEvent); + } + + if ( strcmp(cmd, "lostmission") == 0 ) + { + Event newEvent; + m_event->MakeEvent(newEvent, EVENT_LOST); + m_event->AddEvent(newEvent); + } + + if ( strcmp(cmd, "trainerpilot") == 0 ) + { + m_bTrainerPilot = !m_bTrainerPilot; + return; + } + + if ( strcmp(cmd, "fly") == 0 ) + { + Event newEvent; + + g_researchDone |= RESEARCH_FLY; + + m_event->MakeEvent(newEvent, EVENT_UPDINTERFACE); + m_event->AddEvent(newEvent); + return; + } + + if ( strcmp(cmd, "allresearch") == 0 ) + { + Event newEvent; + + g_researchDone = -1; // toutes les recherches sont effectuées + + m_event->MakeEvent(newEvent, EVENT_UPDINTERFACE); + m_event->AddEvent(newEvent); + return; + } + + if ( strcmp(cmd, "nolimit") == 0 ) + { + m_terrain->SetFlyingMaxHeight(280.0f); + return; + } + + if ( strcmp(cmd, "photo1") == 0 ) + { + m_bFreePhoto = !m_bFreePhoto; + if ( m_bFreePhoto ) + { + m_camera->SetType(CAMERA_FREE); + ChangePause(TRUE); + } + else + { + m_camera->SetType(CAMERA_BACK); + ChangePause(FALSE); + } + return; + } + + if ( strcmp(cmd, "photo2") == 0 ) + { + m_bFreePhoto = !m_bFreePhoto; + if ( m_bFreePhoto ) + { + m_camera->SetType(CAMERA_FREE); + ChangePause(TRUE); + DeselectAll(); // enlève les boutons de commande + m_map->ShowMap(FALSE); + m_displayText->HideText(TRUE); + } + else + { + m_camera->SetType(CAMERA_BACK); + ChangePause(FALSE); + m_map->ShowMap(m_bMapShow); + m_displayText->HideText(FALSE); + } + return; + } + + if ( strcmp(cmd, "noclip") == 0 ) + { + CObject* object; + + object = RetSelect(); + if ( object != 0 ) + { + object->SetClip(FALSE); + } + return; + } + + if ( strcmp(cmd, "clip") == 0 ) + { + CObject* object; + + object = RetSelect(); + if ( object != 0 ) + { + object->SetClip(TRUE); + } + return; + } + + if ( strcmp(cmd, "addhusky") == 0 ) + { + CObject* object; + + object = RetSelect(); + if ( object != 0 ) + { + object->SetMagnifyDamage(object->RetMagnifyDamage()*0.1f); + } + return; + } + + if ( strcmp(cmd, "addfreezer") == 0 ) + { + CObject* object; + + object = RetSelect(); + if ( object != 0 ) + { + object->SetRange(object->RetRange()*10.0f); + } + return; + } + + if ( strcmp(cmd, "fullpower") == 0 ) + { + CObject* object; + CObject* power; + CPhysics* physics; + + object = RetSelect(); + if ( object != 0 ) + { + power = object->RetPower(); + if ( power != 0 ) + { + power->SetEnergy(1.0f); + } + object->SetShield(1.0f); + physics = object->RetPhysics(); + if ( physics != 0 ) + { + physics->SetReactorRange(1.0f); + } + } + return; + } + + if ( strcmp(cmd, "fullenergy") == 0 ) + { + CObject* object; + CObject* power; + + object = RetSelect(); + if ( object != 0 ) + { + power = object->RetPower(); + if ( power != 0 ) + { + power->SetEnergy(1.0f); + } + } + return; + } + + if ( strcmp(cmd, "fullshield") == 0 ) + { + CObject* object; + + object = RetSelect(); + if ( object != 0 ) + { + object->SetShield(1.0f); + } + return; + } + + if ( strcmp(cmd, "fullrange") == 0 ) + { + CObject* object; + CPhysics* physics; + + object = RetSelect(); + if ( object != 0 ) + { + physics = object->RetPhysics(); + if ( physics != 0 ) + { + physics->SetReactorRange(1.0f); + } + } + return; + } + } + + if ( strcmp(cmd, "debugmode") == 0 ) + { + m_engine->SetDebugMode(!m_engine->RetDebugMode()); + return; + } + + if ( strcmp(cmd, "showstat") == 0 ) + { + m_engine->SetShowStat(!m_engine->RetShowStat()); + return; + } + + if ( strcmp(cmd, "invshadow") == 0 ) + { + m_engine->SetShadow(!m_engine->RetShadow()); + return; + } + + if ( strcmp(cmd, "invdirty") == 0 ) + { + m_engine->SetDirty(!m_engine->RetDirty()); + return; + } + + if ( strcmp(cmd, "invfog") == 0 ) + { + m_engine->SetFog(!m_engine->RetFog()); + return; + } + + if ( strcmp(cmd, "invlens") == 0 ) + { + m_engine->SetLensMode(!m_engine->RetLensMode()); + return; + } + + if ( strcmp(cmd, "invwater") == 0 ) + { + m_engine->SetWaterMode(!m_engine->RetWaterMode()); + return; + } + + if ( strcmp(cmd, "invsky") == 0 ) + { + m_engine->SetSkyMode(!m_engine->RetSkyMode()); + return; + } + + if ( strcmp(cmd, "invplanet") == 0 ) + { + m_engine->SetPlanetMode(!m_engine->RetPlanetMode()); + return; + } + + if ( strcmp(cmd, "showpos") == 0 ) + { + m_bShowPos = !m_bShowPos; + return; + } + + if ( strcmp(cmd, "selectinsect") == 0 ) + { + m_bSelectInsect = !m_bSelectInsect; + return; + } + + if ( strcmp(cmd, "showsoluce") == 0 ) + { + m_bShowSoluce = !m_bShowSoluce; + m_dialog->ShowSoluceUpdate(); + return; + } + +#if _TEEN + if ( strcmp(cmd, "allteens") == 0 ) +#else + if ( strcmp(cmd, "allmission") == 0 ) +#endif + { + m_bShowAll = !m_bShowAll; + m_dialog->AllMissionUpdate(); + return; + } + + if ( strcmp(cmd, "invradar") == 0 ) + { + m_bCheatRadar = !m_bCheatRadar; + return; + } + + if ( m_phase == PHASE_SIMUL ) + { + m_displayText->DisplayError(ERR_CMD, D3DVECTOR(0.0f,0.0f,0.0f)); + } +} + + + +// Retourne le type de film en cours. + +MainMovieType CRobotMain::RetMainMovie() +{ + return m_movie->RetType(); +} + + +// Efface l'affichage d'instructions. + +void CRobotMain::FlushDisplayInfo() +{ + int i; + + for ( i=0 ; iRetType() == OBJECT_HUMAN ); + + if ( !m_bEditLock && bMovie && !m_movie->IsExist() && bHuman ) + { + motion = pObj->RetMotion(); + if ( motion != 0 && motion->RetAction() == -1 ) + { + m_movieInfoIndex = index; + m_movie->Start(MM_SATCOMopen, 2.5f); + ChangePause(TRUE); +//? m_map->ShowMap(FALSE); + m_infoObject = DeselectAll(); // enlève les boutons de commande + m_displayText->HideText(TRUE); + return; + } + } + + if ( m_movie->IsExist() ) + { + m_movie->Stop(); + ChangePause(FALSE); + SelectObject(m_infoObject, FALSE); // remet les boutons de commande +//? m_map->ShowMap(m_bMapShow); + m_displayText->HideText(FALSE); + } + + StartDisplayInfo(m_infoFilename[index], index); +} + +// Début de l'affichage d'instructions. + +void CRobotMain::StartDisplayInfo(char *filename, int index) +{ + CButton* pb; + BOOL bSoluce; + + if ( m_bCmdEdit ) return; + + m_movieInfoIndex = -1; + ClearInterface(); // enlève mise en évidence et tooltip + + if ( !m_bEditLock ) + { +//? m_map->ShowMap(FALSE); + m_infoObject = DeselectAll(); // enlève les boutons de commande + m_displayText->HideText(TRUE); + m_sound->MuteAll(TRUE); + } + + pb = (CButton*)m_interface->SearchControl(EVENT_BUTTON_QUIT); + if ( pb != 0 ) + { + pb->ClearState(STATE_VISIBLE); + } + + bSoluce = m_dialog->RetSceneSoluce(); + + m_displayInfo = new CDisplayInfo(m_iMan); + m_displayInfo->StartDisplayInfo(filename, index, bSoluce); + + m_infoIndex = index; + if ( index != -1 ) + { + m_displayInfo->SetPosition(m_infoPos[index]); + } +} + +// Fin de l'affichage d'instructions. + +void CRobotMain::StopDisplayInfo() +{ + CButton* pb; + + if ( m_movieInfoIndex != -1 ) // film pour lire le SatCom ? + { + m_movie->Start(MM_SATCOMclose, 2.0f); + } + + if ( m_infoIndex != -1 ) + { + m_infoPos[m_infoIndex] = m_displayInfo->RetPosition(); + } + m_displayInfo->StopDisplayInfo(); + + delete m_displayInfo; + m_displayInfo = 0; + + if ( !m_bEditLock ) + { + pb = (CButton*)m_interface->SearchControl(EVENT_BUTTON_QUIT); + if ( pb != 0 ) + { + pb->SetState(STATE_VISIBLE); + } + + SelectObject(m_infoObject, FALSE); // remet les boutons de commande +//? m_map->ShowMap(m_bMapShow); + m_displayText->HideText(FALSE); + + m_sound->MuteAll(FALSE); + } + + if ( m_infoUsed == 0 ) + { + m_displayText->ClearText(); // enlève message "voir SatCom ..." + } + m_infoUsed ++; +} + +// Retourne le nom du texte à afficher. + +char* CRobotMain::RetDisplayInfoName(int index) +{ + return m_infoFilename[index]; +} + +// Retourne le nom du texte à afficher. + +int CRobotMain::RetDisplayInfoPosition(int index) +{ + return m_infoPos[index]; +} + +// Retourne le nom du texte à afficher. + +void CRobotMain::SetDisplayInfoPosition(int index, int pos) +{ + m_infoPos[index] = pos; +} + + +// Début d'un dialogue pendant le jeu, + +void CRobotMain::StartSuspend() +{ + CButton* pb; + + m_map->ShowMap(FALSE); + m_infoObject = DeselectAll(); // enlève les boutons de commande + m_displayText->HideText(TRUE); + + pb = (CButton*)m_interface->SearchControl(EVENT_BUTTON_QUIT); + if ( pb != 0 ) + { + pb->ClearState(STATE_VISIBLE); + } + + m_bSuspend = TRUE; +} + +// Fin d'un dialogue pendant le jeu, + +void CRobotMain::StopSuspend() +{ + CButton* pb; + + pb = (CButton*)m_interface->SearchControl(EVENT_BUTTON_QUIT); + if ( pb != 0 ) + { + pb->SetState(STATE_VISIBLE); + } + + SelectObject(m_infoObject, FALSE); // remet les boutons de commande + m_map->ShowMap(m_bMapShow); + m_displayText->HideText(FALSE); + + m_bSuspend = FALSE; +} + + +// Retourne le temps absoul du jeu. + +float CRobotMain::RetGameTime() +{ + return m_gameTime; +} + + + +// Gestion de la taille des caractères par défaut. + +void CRobotMain::SetFontSize(float size) +{ + m_fontSize = size; + SetProfileFloat("Edit", "FontSize", m_fontSize); +} + +float CRobotMain::RetFontSize() +{ + return m_fontSize; +} + +// Gestion de la taille de la fenêtre par défaut. + +void CRobotMain::SetWindowPos(FPOINT pos) +{ + m_windowPos = pos; + SetProfileFloat("Edit", "WindowPos.x", m_windowPos.x); + SetProfileFloat("Edit", "WindowPos.y", m_windowPos.y); +} + +FPOINT CRobotMain::RetWindowPos() +{ + return m_windowPos; +} + +void CRobotMain::SetWindowDim(FPOINT dim) +{ + m_windowDim = dim; + SetProfileFloat("Edit", "WindowDim.x", m_windowDim.x); + SetProfileFloat("Edit", "WindowDim.y", m_windowDim.y); +} + +FPOINT CRobotMain::RetWindowDim() +{ + return m_windowDim; +} + + +// Gestion des fenêtres ouvrir/enregistrer. + +void CRobotMain::SetIOPublic(BOOL bMode) +{ + m_IOPublic = bMode; + SetProfileInt("Edit", "IOPublic", m_IOPublic); +} + +BOOL CRobotMain::RetIOPublic() +{ + return m_IOPublic; +} + +void CRobotMain::SetIOPos(FPOINT pos) +{ + m_IOPos = pos; + SetProfileFloat("Edit", "IOPos.x", m_IOPos.x); + SetProfileFloat("Edit", "IOPos.y", m_IOPos.y); +} + +FPOINT CRobotMain::RetIOPos() +{ + return m_IOPos; +} + +void CRobotMain::SetIODim(FPOINT dim) +{ + m_IODim = dim; + SetProfileFloat("Edit", "IODim.x", m_IODim.x); + SetProfileFloat("Edit", "IODim.y", m_IODim.y); +} + +FPOINT CRobotMain::RetIODim() +{ + return m_IODim; +} + + + +// Début de la visite du lieu d'une erreur. + +void CRobotMain::StartDisplayVisit(EventMsg event) +{ + CWindow* pw; + CButton* button; + CGroup* group; + D3DVECTOR goal; + FPOINT pos, dim; + int i, j; + + if ( m_bEditLock ) return; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW2); + if ( pw == 0 ) return; + + if ( event == EVENT_NULL ) // visite par raccourci clavier ? + { + if ( m_visitLast != EVENT_NULL ) // déjà une visite en cours ? + { + i = m_visitLast-EVENT_DT_VISIT0; + } + else + { + i = MAXDTLINE; + } + + // Cherche la précédente. + for ( j=0 ; jSearchControl(EventMsg(EVENT_DT_VISIT0+i)); + if ( button == 0 || !button->TestState(STATE_ENABLE) ) continue; + + group = (CGroup*)pw->SearchControl(EventMsg(EVENT_DT_GROUP0+i)); + if ( group != 0 ) + { + event = EventMsg(EVENT_DT_VISIT0+i); + break; + } + } + } + if ( event == EVENT_NULL ) + { + m_sound->Play(SOUND_TZOING); // rien à voir ! + return; + } + + m_visitLast = event; + + ClearInterface(); // enlève mise en évidence et tooltip + + if ( m_camera->RetType() == CAMERA_VISIT ) // déjà une visite en cours ? + { + m_camera->StopVisit(); + m_displayText->ClearVisit(); + } + else + { + m_visitObject = DeselectAll(); // enlève les boutons de commande + } + + // Crée le bouton "continuer". + if ( m_interface->SearchControl(EVENT_DT_END) == 0 ) + { + pos.x = 10.0f/640.0f; + pos.y = 10.0f/480.0f; + dim.x = 50.0f/640.0f; + dim.y = 50.0f/480.0f; + m_interface->CreateButton(pos, dim, 16, EVENT_DT_END); + } + + // Crée la flèche pour montrer l'endroit. + if ( m_visitArrow != 0 ) + { + m_visitArrow->DeleteObject(); + delete m_visitArrow; + m_visitArrow = 0; + } + goal = m_displayText->RetVisitGoal(event); + m_visitArrow = CreateObject(goal, 0.0f, 1.0f, 10.0f, OBJECT_SHOW, FALSE, FALSE, 0); + + m_visitPos = m_visitArrow->RetPosition(0); + m_visitPosArrow = m_visitPos; + m_visitPosArrow.y += m_displayText->RetVisitHeight(event); + m_visitArrow->SetPosition(0, m_visitPosArrow); + + m_visitTime = 0.0; + m_visitParticule = 0.0f; + + m_particule->DeleteParticule(PARTISHOW); + + m_camera->StartVisit(m_displayText->RetVisitGoal(event), + m_displayText->RetVisitDist(event)); + m_displayText->SetVisit(event); + ChangePause(TRUE); +} + +// Bouge la flèche de visite. + +void CRobotMain::FrameVisit(float rTime) +{ + D3DVECTOR pos, speed; + FPOINT dim; + float level; + + if ( m_visitArrow == 0 ) return; + + // Bouge la flèche. + m_visitTime += rTime; + + pos = m_visitPosArrow; + pos.y += 1.5f+sinf(m_visitTime*4.0f)*4.0f; + m_visitArrow->SetPosition(0, pos); + m_visitArrow->SetAngleY(0, m_visitTime*2.0f); + + // Gère les particules "flèches". + m_visitParticule -= rTime; + if ( m_visitParticule <= 0.0f ) + { + m_visitParticule = 1.5f; + + pos = m_visitPos; + level = m_terrain->RetFloorLevel(pos)+2.0f; + if ( pos.y < level ) pos.y = level; // pas en-dessous du sol + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 30.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISHOW, 2.0f); + } +} + +// Fin de la visite du lieu d'une erreur. + +void CRobotMain::StopDisplayVisit() +{ + m_visitLast = EVENT_NULL; + + // Supprime le bouton. + m_interface->DeleteControl(EVENT_DT_END); + + // Supprime la flèche. + if ( m_visitArrow != 0 ) + { + m_visitArrow->DeleteObject(); + delete m_visitArrow; + m_visitArrow = 0; + } + + // Supprime les particules "flèches". + m_particule->DeleteParticule(PARTISHOW); + + m_camera->StopVisit(); + m_displayText->ClearVisit(); + ChangePause(FALSE); + if ( m_visitObject != 0 ) + { + SelectObject(m_visitObject, FALSE); // remet les boutons de commande + m_visitObject = 0; + } +} + + + +// Met à jour tous les raccourcis. + +void CRobotMain::UpdateShortcuts() +{ + m_short->UpdateShortcuts(); +} + +// Retourne l'objet par défaut à sélectionner après la création d'une scène. + +CObject* CRobotMain::RetSelectObject() +{ + if ( m_selectObject != 0 ) return m_selectObject; + return SearchHuman(); +} + +// Désélectionne tout, et retourne l'objet qui était sélectionné. + +CObject* CRobotMain::DeselectAll() +{ + CObject* pObj; + CObject* pPrev; + int i; + + pPrev = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetSelect() ) pPrev = pObj; + pObj->SetSelect(FALSE); + } + return pPrev; +} + +// Sélectionne un objet, sans s'occuper de désélectionner le reste. + +void CRobotMain::SelectOneObject(CObject* pObj, BOOL bDisplayError) +{ + ObjectType type; + CObject* toto; + CMotionToto* mt; + + pObj->SetSelect(TRUE, bDisplayError); + m_camera->SetObject(pObj); + + type = pObj->RetType(); + if ( type == OBJECT_HUMAN || + type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr || + type == OBJECT_APOLLO2 ) + { + m_camera->SetType(pObj->RetCameraType()); + m_camera->SetDist(pObj->RetCameraDist()); + } + else + { + m_camera->SetType(CAMERA_BACK); + } + + toto = SearchToto(); + if ( toto != 0 ) + { + mt = (CMotionToto*)toto->RetMotion(); + if ( mt != 0 ) + { + mt->SetLinkType(type); + } + } +} + +// Sélectionne l'objet visé par la souris. + +BOOL CRobotMain::SelectObject(CObject* pObj, BOOL bDisplayError) +{ + CObject* pPrev; + + if ( m_camera->RetType() == CAMERA_VISIT ) + { + StopDisplayVisit(); + } + + if ( m_bMovieLock || m_bEditLock || m_bPause ) return FALSE; + if ( m_movie->IsExist() ) return FALSE; + if ( pObj == 0 || !IsSelectable(pObj) ) return FALSE; + + pPrev = DeselectAll(); + + if ( pPrev != 0 && pPrev != pObj ) + { + pObj->AddDeselList(pPrev); + } + + SelectOneObject(pObj, bDisplayError); + m_short->UpdateShortcuts(); + return TRUE; +} + +// Désélectionne l'objet sélectionné. + +BOOL CRobotMain::DeselectObject() +{ + CObject* pObj; + CObject* pPrev; + + pPrev = DeselectAll(); + + if ( pPrev == 0 ) + { + pObj = SearchHuman(); + } + else + { + pObj = pPrev->SubDeselList(); + } + if ( pObj == 0 ) + { + pObj = SearchHuman(); + } + + if ( pObj != 0 ) + { + SelectOneObject(pObj); + } + else + { + m_camera->SetType(CAMERA_FREE); + } + + m_short->UpdateShortcuts(); + return TRUE; +} + +// Supprime rapidement tous les objets. + +void CRobotMain::DeleteAllObjects() +{ + CPyro* pyro; + CObject* pObj; + int i; + + // Supprime tous les effets pyrotechniques en cours. + while ( TRUE ) + { + pyro = (CPyro*)m_iMan->SearchInstance(CLASS_PYRO, 0); + if ( pyro == 0 ) break; + + pyro->DeleteObject(); + delete pyro; + } + + // Supprime la flèche. + if ( m_visitArrow != 0 ) + { + m_visitArrow->DeleteObject(); + delete m_visitArrow; + m_visitArrow = 0; + } + + for ( i=0 ; iSearchInstance(CLASS_OBJECT, 0); + if ( pObj == 0 ) break; + + pObj->DeleteObject(TRUE); // détruit rapidement + delete pObj; + } +} + +// Sélectionne l'homme. + +void CRobotMain::SelectHuman() +{ + SelectObject(SearchHuman()); +} + +// Retourne l'objet de l'homme. + +CObject* CRobotMain::SearchHuman() +{ + ObjectType type; + CObject* pObj; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type == OBJECT_HUMAN ) + { + return pObj; + } + } + return 0; +} + +// Retourne l'objet de toto. + +CObject* CRobotMain::SearchToto() +{ + ObjectType type; + CObject* pObj; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type == OBJECT_TOTO ) + { + return pObj; + } + } + return 0; +} + +// Retourne l'objet sélectionnable le plus proche d'une position donnée. + +CObject* CRobotMain::SearchNearest(D3DVECTOR pos, CObject* pExclu) +{ + ObjectType type; + CObject *pObj, *pBest; + D3DVECTOR oPos; + float min, dist; + int i; + + min = 100000.0f; + pBest = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == pExclu ) continue; + if ( !IsSelectable(pObj) ) continue; + + type = pObj->RetType(); + if ( type == OBJECT_TOTO ) continue; + + oPos = pObj->RetPosition(0); + dist = Length2d(oPos, pos); + if ( dist < min ) + { + min = dist; + pBest = pObj; + } + } + return pBest; +} + +// Retourne l'objet sélectionné. + +CObject* CRobotMain::RetSelect() +{ + CObject* pObj; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetSelect() ) + { + return pObj; + } + } + return 0; +} + +CObject* CRobotMain::SearchObject(ObjectType type) +{ + CObject* pObj; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetType() == type ) + { + return pObj; + } + } + return 0; +} + +// Détecte l'objet visé par la souris. + +CObject* CRobotMain::DetectObject(FPOINT pos) +{ + ObjectType type; + CObject *pObj, *pTarget; + int objRank, i, j, rank; + + objRank = m_engine->DetectObject(pos); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; + if ( pObj->RetProxyActivate() ) continue; + + pTarget = 0; + type = pObj->RetType(); + if ( type == OBJECT_PORTICO || + type == OBJECT_BASE || + type == OBJECT_DERRICK || + type == OBJECT_FACTORY || + type == OBJECT_REPAIR || + type == OBJECT_DESTROYER || + type == OBJECT_STATION || + type == OBJECT_CONVERT || + type == OBJECT_TOWER || + type == OBJECT_RESEARCH || + type == OBJECT_RADAR || + type == OBJECT_INFO || + type == OBJECT_ENERGY || + type == OBJECT_LABO || + type == OBJECT_NUCLEAR || + type == OBJECT_PARA || + type == OBJECT_SAFE || + type == OBJECT_HUSTON || + type == OBJECT_TARGET1 || + type == OBJECT_TARGET2 || + type == OBJECT_START || + type == OBJECT_END || + type == OBJECT_STONE || + type == OBJECT_URANIUM || + type == OBJECT_BULLET || + type == OBJECT_METAL || + type == OBJECT_BBOX || + type == OBJECT_KEYa || + type == OBJECT_KEYb || + type == OBJECT_KEYc || + type == OBJECT_KEYd || + type == OBJECT_TNT || + type == OBJECT_SCRAP1 || + type == OBJECT_SCRAP2 || + type == OBJECT_SCRAP3 || + type == OBJECT_SCRAP4 || + type == OBJECT_SCRAP5 || + type == OBJECT_BOMB || + type == OBJECT_BAG || + type == OBJECT_WAYPOINT || + type == OBJECT_FLAGb || + type == OBJECT_FLAGr || + type == OBJECT_FLAGg || + type == OBJECT_FLAGy || + type == OBJECT_FLAGv || + type == OBJECT_MARKPOWER || + type == OBJECT_MARKSTONE || + type == OBJECT_MARKURANIUM || + type == OBJECT_MARKKEYa || + type == OBJECT_MARKKEYb || + type == OBJECT_MARKKEYc || + type == OBJECT_MARKKEYd || + type == OBJECT_HUMAN || + type == OBJECT_TECH || + type == OBJECT_TOTO || + type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEtg || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr || + type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM || + type == OBJECT_EGG || + type == OBJECT_RUINmobilew1 || + type == OBJECT_RUINmobilew2 || + type == OBJECT_RUINmobilet1 || + type == OBJECT_RUINmobilet2 || + type == OBJECT_RUINmobiler1 || + type == OBJECT_RUINmobiler2 || + type == OBJECT_RUINfactory || + type == OBJECT_RUINdoor || + type == OBJECT_RUINsupport || + type == OBJECT_RUINradar || + type == OBJECT_RUINconvert || + type == OBJECT_RUINbase || + type == OBJECT_RUINhead || + type == OBJECT_APOLLO1 || + type == OBJECT_APOLLO2 || + type == OBJECT_APOLLO3 || + type == OBJECT_APOLLO4 || + type == OBJECT_APOLLO5 ) + { + pTarget = pObj; + } + else if ( (type == OBJECT_POWER || + type == OBJECT_ATOMIC ) && + pObj->RetTruck() != 0 ) // pile utilisée ? + { + pTarget = pObj->RetTruck(); + } + else if ( type == OBJECT_POWER || + type == OBJECT_ATOMIC ) + { + pTarget = pObj; + } + + for ( j=0 ; jRetObjectRank(j); + if ( rank == -1 ) continue; + if ( rank != objRank ) continue; + return pTarget; + } + } + return 0; +} + +// Indique si un objet est sélectionnable. + +BOOL CRobotMain::IsSelectable(CObject* pObj) +{ + ObjectType type; + + if ( !pObj->RetSelectable() ) return FALSE; + + type = pObj->RetType(); + if ( type == OBJECT_HUMAN || + type == OBJECT_TOTO || + type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr || + type == OBJECT_APOLLO2 || + type == OBJECT_BASE || + type == OBJECT_DERRICK || + type == OBJECT_FACTORY || + type == OBJECT_REPAIR || + type == OBJECT_DESTROYER|| + type == OBJECT_STATION || + type == OBJECT_CONVERT || + type == OBJECT_TOWER || + type == OBJECT_RESEARCH || + type == OBJECT_RADAR || + type == OBJECT_INFO || + type == OBJECT_ENERGY || + type == OBJECT_LABO || + type == OBJECT_NUCLEAR || + type == OBJECT_PARA || + type == OBJECT_SAFE || + type == OBJECT_HUSTON ) + { + return TRUE; + } + + if ( m_bSelectInsect ) + { + if ( type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM || + type == OBJECT_MOBILEtg ) + { + return TRUE; + } + } + + return FALSE; +} + + +// Supprime l'objet sélectionné. + +BOOL CRobotMain::DeleteObject() +{ + CObject* pObj; + CPyro* pyro; + + pObj = RetSelect(); + if ( pObj == 0 ) return FALSE; + + pyro = new CPyro(m_iMan); + pyro->Create(PT_FRAGT, pObj); + + pObj->SetSelect(FALSE); // désélectionne l'objet + m_camera->SetType(CAMERA_EXPLO); + DeselectAll(); + pObj->DeleteDeselList(pObj); + + return TRUE; +} + + +// Enlève la mise en évidence de l'objet survolé par la souris. + +void CRobotMain::HiliteClear() +{ + CObject* pObj; + int i; + + ClearTooltip(); + m_tooltipName[0] = 0; // enlève vraiment le tooltip + + if ( !m_bHilite ) return; + + i = -1; + m_engine->SetHiliteRank(&i); // plus rien de sélectionné + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + pObj->SetHilite(FALSE); + m_map->SetHilite(0); + m_short->SetHilite(0); + } + + m_bHilite = FALSE; +} + +// Met en évidence l'objet survolé par la souris. + +void CRobotMain::HiliteObject(FPOINT pos) +{ + CObject* pObj; + char name[100]; + BOOL bInMap; + + if ( m_bFixScene && m_phase != PHASE_PERSO ) return; + if ( m_bMovieLock ) return; + if ( m_movie->IsExist() ) return; + if ( m_engine->RetMouseHide() ) return; + + ClearInterface(); // enlève mise en évidence et tooltip + + pObj = m_short->DetectShort(pos); + + if ( m_dialog->RetTooltip() && m_interface->GetTooltip(pos, name) ) + { + m_tooltipPos = pos; + strcpy(m_tooltipName, name); + m_tooltipTime = 0.0f; + if ( pObj == 0 ) return; + } + + if ( m_bSuspend ) return; + + if ( pObj == 0 ) + { + pObj = m_map->DetectMap(pos, bInMap); + if ( pObj == 0 ) + { + if ( bInMap ) return; + + pObj = DetectObject(pos); + + if ( m_camera->RetType() == CAMERA_ONBOARD && + m_camera->RetObject() == pObj ) + { + return; + } + } + } + + if ( pObj != 0 ) + { + if ( m_dialog->RetTooltip() && pObj->GetTooltipName(name) ) + { + m_tooltipPos = pos; + strcpy(m_tooltipName, name); + m_tooltipTime = 0.0f; + } + + if ( IsSelectable(pObj) ) + { + pObj->SetHilite(TRUE); + m_map->SetHilite(pObj); + m_short->SetHilite(pObj); + m_bHilite = TRUE; + } + } +} + +// Met en évidence l'objet survolé par la souris. + +void CRobotMain::HiliteFrame(float rTime) +{ + if ( m_bFixScene && m_phase != PHASE_PERSO ) return; + if ( m_bMovieLock ) return; + if ( m_movie->IsExist() ) return; + + m_tooltipTime += rTime; + + ClearTooltip(); + + if ( m_tooltipTime >= 0.2f && + m_tooltipName[0] != 0 ) + { + CreateTooltip(m_tooltipPos, m_tooltipName); + } +} + +// Crée un tooltip. + +void CRobotMain::CreateTooltip(FPOINT pos, char* text) +{ + CWindow* pw; + FPOINT start, end, dim, offset, corner; + + corner.x = pos.x+0.022f; + corner.y = pos.y-0.052f; + + m_engine->RetText()->DimText(text, corner, 1, + SMALLFONT, NORMSTRETCH, FONT_COLOBOT, + start, end); + start.x -= 0.010f; + start.y -= 0.002f; + end.x += 0.010f; + end.y += 0.004f; // ch'tite marge + + pos.x = start.x; + pos.y = start.y; + dim.x = end.x-start.x; + dim.y = end.y-start.y; + + offset.x = 0.0f; + offset.y = 0.0f; + if ( pos.x+dim.x > 1.0f ) offset.x = 1.0f-(pos.x+dim.x); + if ( pos.y < 0.0f ) offset.y = -pos.y; + + corner.x += offset.x; + corner.y += offset.y; + pos.x += offset.x; + pos.y += offset.y; + + m_interface->CreateWindows(pos, dim, 1, EVENT_TOOLTIP); + + pw = (CWindow*)m_interface->SearchControl(EVENT_TOOLTIP); + if ( pw != 0 ) + { + pw->SetState(STATE_SHADOW); + pw->SetTrashEvent(FALSE); + + pos.y -= m_engine->RetText()->RetHeight(SMALLFONT, FONT_COLOBOT)/2.0f; + pw->CreateLabel(pos, dim, -1, EVENT_LABEL2, text); + } +} + +// Efface le tooltip précédent. + +void CRobotMain::ClearTooltip() +{ + m_interface->DeleteControl(EVENT_TOOLTIP); +} + + +// Affiche l'aide pour un objet. + +void CRobotMain::HelpObject() +{ + CObject* pObj; + char* filename; + + pObj = RetSelect(); + if ( pObj == 0 ) return; + + filename = RetHelpFilename(pObj->RetType()); + if ( filename[0] == 0 ) return; + + StartDisplayInfo(filename, -1); +} + + +// Change le mode de la caméra. + +void CRobotMain::ChangeCamera() +{ + CObject* pObj; + ObjectType oType; + CameraType type; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetSelect() ) + { + if ( pObj->RetCameraLock() ) return; + + oType = pObj->RetType(); + type = pObj->RetCameraType(); + + if ( oType != OBJECT_MOBILEfa && + oType != OBJECT_MOBILEta && + oType != OBJECT_MOBILEwa && + oType != OBJECT_MOBILEia && + oType != OBJECT_MOBILEfc && + oType != OBJECT_MOBILEtc && + oType != OBJECT_MOBILEwc && + oType != OBJECT_MOBILEic && + oType != OBJECT_MOBILEfi && + oType != OBJECT_MOBILEti && + oType != OBJECT_MOBILEwi && + oType != OBJECT_MOBILEii && + oType != OBJECT_MOBILEfs && + oType != OBJECT_MOBILEts && + oType != OBJECT_MOBILEws && + oType != OBJECT_MOBILEis && + oType != OBJECT_MOBILErt && + oType != OBJECT_MOBILErc && + oType != OBJECT_MOBILErr && + oType != OBJECT_MOBILErs && + oType != OBJECT_MOBILEsa && + oType != OBJECT_MOBILEtg && + oType != OBJECT_MOBILEft && + oType != OBJECT_MOBILEtt && + oType != OBJECT_MOBILEwt && + oType != OBJECT_MOBILEit && + oType != OBJECT_MOBILEdr && + oType != OBJECT_APOLLO2 ) return; + + if ( oType == OBJECT_MOBILEdr ) // dessinateur ? + { + if ( type == CAMERA_PLANE ) type = CAMERA_BACK; + else if ( type == CAMERA_BACK ) type = CAMERA_PLANE; + } + else if ( pObj->RetTrainer() ) // entraînement ? + { + if ( type == CAMERA_ONBOARD ) type = CAMERA_FIX; + else if ( type == CAMERA_FIX ) type = CAMERA_PLANE; + else if ( type == CAMERA_PLANE ) type = CAMERA_BACK; + else if ( type == CAMERA_BACK ) type = CAMERA_ONBOARD; + } + else + { + if ( type == CAMERA_ONBOARD ) type = CAMERA_BACK; + else if ( type == CAMERA_BACK ) type = CAMERA_ONBOARD; + } + + pObj->SetCameraType(type); + m_camera->SetType(type); + } + } +} + +// Télécommande la caméra avec les touches flèches. + +void CRobotMain::KeyCamera(EventMsg event, long param) +{ + CObject* pObj; + + if ( event == EVENT_KEYUP ) + { + if ( param == m_engine->RetKey(KEYRANK_LEFT, 0) || + param == m_engine->RetKey(KEYRANK_LEFT, 1) ) + { + m_cameraPan = 0.0f; + } + + if ( param == m_engine->RetKey(KEYRANK_RIGHT, 0) || + param == m_engine->RetKey(KEYRANK_RIGHT, 1) ) + { + m_cameraPan = 0.0f; + } + + if ( param == m_engine->RetKey(KEYRANK_UP, 0) || + param == m_engine->RetKey(KEYRANK_UP, 1) ) + { + m_cameraZoom = 0.0f; + } + + if ( param == m_engine->RetKey(KEYRANK_DOWN, 0) || + param == m_engine->RetKey(KEYRANK_DOWN, 1) ) + { + m_cameraZoom = 0.0f; + } + } + + if ( m_phase != PHASE_SIMUL ) return; + if ( m_bEditLock ) return; // édition en cours ? + if ( m_bTrainerPilot ) return; + + pObj = RetSelect(); + if ( pObj == 0 ) return; + if ( !pObj->RetTrainer() ) return; + + if ( event == EVENT_KEYDOWN ) + { + if ( param == m_engine->RetKey(KEYRANK_LEFT, 0) || + param == m_engine->RetKey(KEYRANK_LEFT, 1) ) + { + m_cameraPan = -1.0f; + } + + if ( param == m_engine->RetKey(KEYRANK_RIGHT, 0) || + param == m_engine->RetKey(KEYRANK_RIGHT, 1) ) + { + m_cameraPan = 1.0f; + } + + if ( param == m_engine->RetKey(KEYRANK_UP, 0) || + param == m_engine->RetKey(KEYRANK_UP, 1) ) + { + m_cameraZoom = -1.0f; + } + + if ( param == m_engine->RetKey(KEYRANK_DOWN, 0) || + param == m_engine->RetKey(KEYRANK_DOWN, 1) ) + { + m_cameraZoom = 1.0f; + } + } +} + +// Effectue un panoramique avec la caméra si un bouton est enfoncé. + +void CRobotMain::RemoteCamera(float pan, float zoom, float rTime) +{ + float value; + + if ( pan != 0.0f ) + { + value = m_camera->RetRemotePan(); + value += pan*rTime*1.5f; + m_camera->SetRemotePan(value); + } + + if ( zoom != 0.0f ) + { + value = m_camera->RetRemoteZoom(); + value += zoom*rTime*0.3f; + m_camera->SetRemoteZoom(value); + } +} + + + +// Annule le film en cours. + +void CRobotMain::AbortMovie() +{ + CObject* pObj; + CAuto* automat; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + automat = pObj->RetAuto(); + if ( automat != 0 ) + { + automat->Abort(); + } + } + + m_engine->SetMouseHide(FALSE); +} + + + +// Met à jour le texte d'informations. + +void CRobotMain::UpdateInfoText() +{ + CObject* pObj; + D3DVECTOR pos; + char info[100]; + + if ( m_bShowPos ) + { + pObj = RetSelect(); + if ( pObj != 0 ) + { + pos = pObj->RetPosition(0); + sprintf(info, "Pos = %.2f ; %.2f", pos.x/g_unit, pos.z/g_unit); + m_engine->SetInfoText(4, info); + } + } +} + + +// Initialise le point de vue. + +void CRobotMain::InitEye() +{ + if ( m_phase == PHASE_SIMUL ) + { + m_camera->Init(D3DVECTOR( 0.0f, 10.0f, 0.0f), + D3DVECTOR(10.0f, 5.0f, 0.0f), 0.0f); + } + + if ( m_phase == PHASE_MODEL ) + { + m_model->InitView(); + } +} + +// Fait progresser toute la scène. + +BOOL CRobotMain::EventFrame(const Event &event) +{ + ObjectType type; + CObject *pObj, *toto; + CPyro* pPyro; + CWindow* pw; + CMap* pm; + int i; + + m_time += event.rTime; + if ( !m_bMovieLock ) m_gameTime += event.rTime; + + if ( !m_bImmediatSatCom && !m_bBeginSatCom && + m_gameTime > 0.1f && m_phase == PHASE_SIMUL ) + { + m_displayText->DisplayError(INFO_BEGINSATCOM, D3DVECTOR(0.0f,0.0f,0.0f)); + m_bBeginSatCom = TRUE; // message affiché + } + + m_water->EventProcess(event); + m_cloud->EventProcess(event); + m_blitz->EventProcess(event); + m_planet->EventProcess(event); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw == 0 ) + { + pm = 0; + } + else + { + pm = (CMap*)pw->SearchControl(EVENT_OBJECT_MAP); + if ( pm != 0 ) pm->FlushObject(); + } + + toto = 0; + if ( !m_bFreePhoto ) + { + // Fait progresser tous les robots, mais pas toto. + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + if ( pm != 0 ) pm->UpdateObject(pObj); + if ( pObj->RetTruck() != 0 ) continue; + type = pObj->RetType(); + if ( type == OBJECT_TOTO ) + { + toto = pObj; + } + else + { + pObj->EventProcess(event); + } + } + // Fait progresser tous les objets transportés par les robots. + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + if ( pObj->RetTruck() == 0 ) continue; + pObj->EventProcess(event); + } + + // Fait progresser les effets pyrotechniques. + for ( i=0 ; i<1000000 ; i++ ) + { + pPyro = (CPyro*)m_iMan->SearchInstance(CLASS_PYRO, i); + if ( pPyro == 0 ) break; + + pPyro->EventProcess(event); + if ( pPyro->IsEnded() != ERR_CONTINUE ) + { + pPyro->DeleteObject(); + delete pPyro; + } + } + } + + // Fait bouger la caméra après les objets, car sa position peut + // dépendre de l'objet sélectionné (CAMERA_ONBOARD ou CAMERA_BACK). + if ( m_phase == PHASE_SIMUL && !m_bEditFull ) + { + m_camera->EventProcess(event); + + if ( m_engine->RetFog() ) + { + m_camera->SetOverBaseColor(m_particule->RetFogColor(m_engine->RetEyePt())); + } + } + if ( m_phase == PHASE_PERSO || + m_phase == PHASE_WIN || + m_phase == PHASE_LOST ) + { + m_camera->EventProcess(event); + } + + // Fait progresser toto après la caméra, car sa position dépend + // de la caméra. + if ( toto != 0 ) + { + toto->EventProcess(event); + } + + // Fait progresser le modèle. + if ( m_phase == PHASE_MODEL ) + { + m_model->ViewMove(event, 2.0f); + m_model->UpdateView(); + m_model->EventProcess(event); + } + + HiliteFrame(event.rTime); + + // Fait bouger l'indicateur de film. + if ( m_bMovieLock && !m_bEditLock ) // film en cours ? + { + CControl* pc; + FPOINT pos, dim; + float zoom; + + pc = m_interface->SearchControl(EVENT_OBJECT_MOVIELOCK); + if ( pc != 0 ) + { + dim.x = 32.0f/640.0f; + dim.y = 32.0f/480.0f; + pos.x = 20.0f/640.0f; + pos.y = (480.0f-24.0f)/480.0f; + + zoom = 1.0f+sinf(m_time*6.0f)*0.1f; // 0.9 .. 1.1 + dim.x *= zoom; + dim.y *= zoom; + pos.x -= dim.x/2.0f; + pos.y -= dim.y/2.0f; + + pc->SetPos(pos); + pc->SetDim(dim); + } + } + + // Fait bouger l'indicateur d'édition. + if ( m_bEditLock || m_bPause ) // édition en cours ? + { + CControl* pc; + FPOINT pos, dim; + float zoom; + + pc = m_interface->SearchControl(EVENT_OBJECT_EDITLOCK); + if ( pc != 0 ) + { + if ( m_bEditFull || m_bEditLock ) + { + dim.x = 10.0f/640.0f; + dim.y = 10.0f/480.0f; + pos.x = -20.0f/640.0f; + pos.y = -20.0f/480.0f; // invisible ! + } + else + { + dim.x = 32.0f/640.0f; + dim.y = 32.0f/480.0f; + pos.x = 20.0f/640.0f; + pos.y = (480.0f-24.0f)/480.0f; + + zoom = 1.0f+sinf(m_time*6.0f)*0.1f; // 0.9 .. 1.1 + dim.x *= zoom; + dim.y *= zoom; + pos.x -= dim.x/2.0f; + pos.y -= dim.y/2.0f; + } + pc->SetPos(pos); + pc->SetDim(dim); + } + } + + // Fait bouger la flèche de visite. + if ( m_camera->RetType() == CAMERA_VISIT ) + { + FrameVisit(event.rTime); + } + + // Fait bouger les limites. + FrameShowLimit(event.rTime); + + if ( m_phase == PHASE_SIMUL ) + { + if ( !m_bEditLock && m_checkEndTime+1.0f < m_time ) + { + m_checkEndTime = m_time; + CheckEndMission(TRUE); + } + + if ( m_winDelay > 0.0f && !m_bEditLock ) + { + m_winDelay -= event.rTime; + if ( m_winDelay <= 0.0f ) + { + if ( m_bMovieLock ) + { + m_winDelay = 1.0f; + } + else + { + Event newEvent; + m_event->MakeEvent(newEvent, EVENT_WIN); + m_event->AddEvent(newEvent); + } + } + } + + if ( m_lostDelay > 0.0f && !m_bEditLock ) + { + m_lostDelay -= event.rTime; + if ( m_lostDelay <= 0.0f ) + { + if ( m_bMovieLock ) + { + m_winDelay = 1.0f; + } + else + { + Event newEvent; + m_event->MakeEvent(newEvent, EVENT_LOST); + m_event->AddEvent(newEvent); + } + } + } + } + + if ( m_delayWriteMessage > 0 ) + { + m_delayWriteMessage --; + if ( m_delayWriteMessage == 0 ) + { + m_displayText->DisplayError(INFO_WRITEOK, D3DVECTOR(0.0f,0.0f,0.0f)); + } + } + + return S_OK; +} + +// Donne l'événement à tous les robots. + +BOOL CRobotMain::EventObject(const Event &event) +{ + CObject* pObj; + int i; + + if ( m_bFreePhoto ) return S_OK; + + m_bResetCreate = FALSE; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + pObj->EventProcess(event); + } + + if ( m_bResetCreate ) + { + ResetCreate(); + } + + return S_OK; +} + + +// Calcule le point d'arrivée de la caméra. + +D3DVECTOR CRobotMain::LookatPoint(D3DVECTOR eye, float angleH, float angleV, + float length) +{ + D3DVECTOR lookat; + + lookat = eye; + lookat.z += length; + + RotatePoint(eye, angleH, angleV, lookat); + return lookat; +} + + + +char* SkipNum(char *p) +{ + while ( *p == ' ' || *p == '.' || *p == '-' || (*p >= '0' && *p <= '9') ) + { + p++; + } + return p; +} + +// Conversion des unités. + +void CRobotMain::Convert() +{ + FILE* file = NULL; + FILE* fileNew = NULL; + char line[500]; + char lineNew[500]; + char s[200]; + char* base; + char* p; + int rank; + D3DVECTOR pos; + float value; + + base = m_dialog->RetSceneName(); + rank = m_dialog->RetSceneRank(); + + m_dialog->BuildSceneName(line, base, rank); + file = fopen(line, "r"); + if ( file == NULL ) return; + + strcpy(line+strlen(line)-4, ".new"); + fileNew = fopen(line, "w"); + if ( fileNew == NULL ) return; + + while ( fgets(line, 500, file) != NULL ) + { + strcpy(lineNew, line); + + if ( Cmd(line, "DeepView") ) + { + p = strstr(line, "air="); + if ( p != 0 ) + { + value = OpFloat(line, "air", 500.0f); + value /= g_unit; + p[0] = 0; + p = SkipNum(p+4); + strcpy(lineNew, line); + strcat(lineNew, "air="); + sprintf(s, "%.2f", value); + strcat(lineNew, s); + strcat(lineNew, " "); + strcat(lineNew, p); + } + strcpy(line, lineNew); + + p = strstr(line, "water="); + if ( p != 0 ) + { + value = OpFloat(line, "water", 100.0f); + value /= g_unit; + p[0] = 0; + p = SkipNum(p+6); + strcpy(lineNew, line); + strcat(lineNew, "water="); + sprintf(s, "%.2f", value); + strcat(lineNew, s); + strcat(lineNew, " "); + strcat(lineNew, p); + } + strcpy(line, lineNew); + } + + if ( Cmd(line, "TerrainGenerate") ) + { + p = strstr(line, "vision="); + if ( p != 0 ) + { + value = OpFloat(line, "vision", 500.0f); + value /= g_unit; + p[0] = 0; + p = SkipNum(p+7); + strcpy(lineNew, line); + strcat(lineNew, "vision="); + sprintf(s, "%.2f", value); + strcat(lineNew, s); + strcat(lineNew, " "); + strcat(lineNew, p); + } + } + + if ( Cmd(line, "CreateObject") || + Cmd(line, "CreateSpot") ) + { + p = strstr(line, "pos="); + if ( p != 0 ) + { + pos = OpPos(line, "pos"); + pos.x /= g_unit; + pos.y /= g_unit; + pos.z /= g_unit; + p[0] = 0; + p = SkipNum(p+4); + p = SkipNum(p+1); + strcpy(lineNew, line); + strcat(lineNew, "pos="); + sprintf(s, "%.2f", pos.x); + strcat(lineNew, s); + strcat(lineNew, ";"); + sprintf(s, "%.2f", pos.z); + strcat(lineNew, s); + strcat(lineNew, " "); + strcat(lineNew, p); + } + } + + if ( Cmd(line, "EndMissionTake") ) + { + p = strstr(line, "pos="); + if ( p != 0 ) + { + pos = OpPos(line, "pos"); + pos.x /= g_unit; + pos.y /= g_unit; + pos.z /= g_unit; + p[0] = 0; + p = SkipNum(p+4); + p = SkipNum(p+1); + strcpy(lineNew, line); + strcat(lineNew, "pos="); + sprintf(s, "%.2f", pos.x); + strcat(lineNew, s); + strcat(lineNew, ";"); + sprintf(s, "%.2f", pos.z); + strcat(lineNew, s); + strcat(lineNew, " "); + strcat(lineNew, p); + } + strcpy(line, lineNew); + + p = strstr(line, "dist="); + if ( p != 0 ) + { + value = OpFloat(line, "dist", 32.0f); + value /= g_unit; + p[0] = 0; + p = SkipNum(p+5); + strcpy(lineNew, line); + strcat(lineNew, "dist="); + sprintf(s, "%.2f", value); + strcat(lineNew, s); + strcat(lineNew, " "); + strcat(lineNew, p); + } + strcpy(line, lineNew); + } + + if ( Cmd(line, "Camera") ) + { + p = strstr(line, "pos="); + if ( p != 0 ) + { + pos = OpPos(line, "pos"); + pos.x /= g_unit; + pos.y /= g_unit; + pos.z /= g_unit; + p[0] = 0; + p = SkipNum(p+4); + p = SkipNum(p+1); + strcpy(lineNew, line); + strcat(lineNew, "pos="); + sprintf(s, "%.2f", pos.x); + strcat(lineNew, s); + strcat(lineNew, ";"); + sprintf(s, "%.2f", pos.z); + strcat(lineNew, s); + strcat(lineNew, " "); + strcat(lineNew, p); + } + strcpy(line, lineNew); + + p = strstr(line, "h="); + if ( p != 0 ) + { + value = OpFloat(line, "h", 32.0f); + value /= g_unit; + p[0] = 0; + p = SkipNum(p+2); + strcpy(lineNew, line); + strcat(lineNew, "h="); + sprintf(s, "%.2f", value); + strcat(lineNew, s); + strcat(lineNew, " "); + strcat(lineNew, p); + } + strcpy(line, lineNew); + } + + fputs(lineNew, fileNew); + } + + fclose(fileNew); + fclose(file); +} + +// Charge la scène pour le personnage. + +void CRobotMain::ScenePerso() +{ + CObject* pObj; + + DeleteAllObjects(); // supprime toute la scène 3D actuelle + m_engine->FlushObject(); + m_terrain->FlushRelief(); // tout plat + m_terrain->FlushBuildingLevel(); + m_terrain->FlushFlyingLimit(); + m_light->FlushLight(); + m_particule->FlushParticule(); + m_iMan->Flush(CLASS_OBJECT); + m_iMan->Flush(CLASS_PHYSICS); + m_iMan->Flush(CLASS_BRAIN); + m_iMan->Flush(CLASS_PYRO); + + m_dialog->SetSceneName("perso"); + m_dialog->SetSceneRank(0); + CreateScene(FALSE, TRUE, FALSE); // scène fixe + + m_engine->SetDrawWorld(FALSE); // ne dessine rien sous l'interface + m_engine->SetDrawFront(TRUE); // dessine human sur l'interface + pObj = SearchHuman(); + if ( pObj != 0 ) + { + CMotionHuman* mh; + + pObj->SetDrawFront(TRUE); // dessine sur l'interface + + mh = (CMotionHuman*)pObj->RetMotion(); + if ( mh != 0 ) + { + mh->StartDisplayPerso(); + } + } +} + +// Crée toute la scène. + +void CRobotMain::CreateScene(BOOL bSoluce, BOOL bFixScene, BOOL bResetObject) +{ + CObject* pObj; + CObject* pSel; + CMotion* motion; + FILE* file = NULL; + char line[500]; + char name[200]; + char dir[100]; + char op[100]; + char* read; + char* stack; + char* base; + D3DCOLORVALUE color; + D3DVECTOR pos; + int rank, obj, i, rankObj, rankGadget; + +//? Convert(); + + base = m_dialog->RetSceneName(); + rank = m_dialog->RetSceneRank(); + read = m_dialog->RetSceneRead(); + stack = m_dialog->RetStackRead(); + m_dialog->SetUserDir(base, rank); + + m_bFixScene = bFixScene; + + g_id = 0; + m_bBase = FALSE; + + if ( !bResetObject ) + { + g_build = 0; + g_researchDone = 0; // aucune recherche effectuée + g_researchEnable = 0; + + FlushDisplayInfo(); + m_terrain->LevelFlush(); + m_audioTrack = 0; + m_bAudioRepeat = TRUE; + m_displayText->SetDelay(1.0f); + m_displayText->SetEnable(TRUE); + m_bImmediatSatCom = FALSE; + m_endingWinRank = 0; + m_endingLostRank = 0; + m_endTakeTotal = 0; + m_endTakeResearch = 0; + m_endTakeWinDelay = 2.0f; + m_endTakeLostDelay = 2.0f; + m_obligatoryTotal = 0; + m_prohibitedTotal = 0; + m_bMapShow = TRUE; + m_bMapImage = FALSE; + m_mapFilename[0] = 0; + + m_colorRefBot.r = 10.0f/256.0f; + m_colorRefBot.g = 166.0f/256.0f; + m_colorRefBot.b = 254.0f/256.0f; // bleu + m_colorRefBot.a = 0.0f; + m_colorNewBot = m_colorRefBot; + + m_colorRefAlien.r = 135.0f/256.0f; + m_colorRefAlien.g = 170.0f/256.0f; + m_colorRefAlien.b = 13.0f/256.0f; // vert + m_colorRefAlien.a = 0.0f; + m_colorNewAlien = m_colorRefAlien; + + m_colorRefGreen.r = 135.0f/256.0f; + m_colorRefGreen.g = 170.0f/256.0f; + m_colorRefGreen.b = 13.0f/256.0f; // vert + m_colorRefGreen.a = 0.0f; + m_colorNewGreen = m_colorRefGreen; + + m_colorRefWater.r = 25.0f/256.0f; + m_colorRefWater.g = 255.0f/256.0f; + m_colorRefWater.b = 240.0f/256.0f; // cyan + m_colorRefWater.a = 0.0f; + m_colorNewWater = m_colorRefWater; + + m_dialog->BuildResumeName(m_title, base, rank); + m_dialog->BuildResumeName(m_resume, base, rank); + GetResource(RES_TEXT, RT_SCRIPT_NEW, m_scriptName); + m_scriptFile[0] = 0; + } + + m_dialog->BuildSceneName(line, base, rank); + file = fopen(line, "r"); + if ( file == NULL ) return; + + rankObj = 0; + rankGadget = 0; + pSel = 0; + + while ( fgets(line, 500, file) != NULL ) + { + for ( i=0 ; i<500 ; i++ ) + { + if ( line[i] == '\t' ) line[i] = ' '; // remplace tab par space + if ( line[i] == '/' && line[i+1] == '/' ) + { + line[i] = 0; + break; + } + } + + sprintf(op, "Title.%c", RetLanguageLetter()); + if ( Cmd(line, op) && !bResetObject ) + { + OpString(line, "text", m_title); + } + + sprintf(op, "Resume.%c", RetLanguageLetter()); + if ( Cmd(line, op) && !bResetObject ) + { + OpString(line, "text", m_resume); + } + + sprintf(op, "ScriptName.%c", RetLanguageLetter()); + if ( Cmd(line, op) && !bResetObject ) + { + OpString(line, "text", m_scriptName); + } + + if ( Cmd(line, "ScriptFile") && !bResetObject ) + { + OpString(line, "name", m_scriptFile); + } + + if ( Cmd(line, "Instructions") && !bResetObject ) + { + OpString(line, "name", name); +//? sprintf(m_infoFilename[SATCOM_HUSTON], "help\\%s", name); + UserDir(m_infoFilename[SATCOM_HUSTON], name, "help"); + + m_bImmediatSatCom = OpInt(line, "immediat", 0); + } + + if ( Cmd(line, "Satellite") && !bResetObject ) + { + OpString(line, "name", name); +//? sprintf(m_infoFilename[SATCOM_SAT], "help\\%s", name); + UserDir(m_infoFilename[SATCOM_SAT], name, "help"); + } + + if ( Cmd(line, "Loading") && !bResetObject ) + { + OpString(line, "name", name); +//? sprintf(m_infoFilename[SATCOM_LOADING], "help\\%s", name); + UserDir(m_infoFilename[SATCOM_LOADING], name, "help"); + } + + if ( Cmd(line, "HelpFile") && !bResetObject ) + { + OpString(line, "name", name); +//? sprintf(m_infoFilename[SATCOM_PROG], "help\\%s", name); + UserDir(m_infoFilename[SATCOM_PROG], name, "help"); + } + if ( Cmd(line, "SoluceFile") && !bResetObject ) + { + OpString(line, "name", name); +//? sprintf(m_infoFilename[SATCOM_SOLUCE], "help\\%s", name); + UserDir(m_infoFilename[SATCOM_SOLUCE], name, "help"); + } + + if ( Cmd(line, "EndingFile") && !bResetObject ) + { + m_endingWinRank = OpInt(line, "win", 0); + m_endingLostRank = OpInt(line, "lost", 0); + } + + if ( Cmd(line, "MessageDelay") && !bResetObject ) + { + m_displayText->SetDelay(OpFloat(line, "factor", 1.0f)); + } + + if ( Cmd(line, "Audio") && !bResetObject ) + { + m_audioTrack = OpInt(line, "track", 0); + m_bAudioRepeat = OpInt(line, "repeat", 1); + } + + if ( Cmd(line, "AmbiantColor") && !bResetObject ) + { + m_engine->SetAmbiantColor(OpColor(line, "air", 0x88888888), 0); + m_engine->SetAmbiantColor(OpColor(line, "water", 0x88888888), 1); + } + + if ( Cmd(line, "FogColor") && !bResetObject ) + { + m_engine->SetFogColor(OpColor(line, "air", 0x88888888), 0); + m_engine->SetFogColor(OpColor(line, "water", 0x88888888), 1); + } + + if ( Cmd(line, "VehicleColor") && !bResetObject ) + { + m_colorNewBot = RetColor(OpColor(line, "color", 0x88888888)); + } + + if ( Cmd(line, "InsectColor") && !bResetObject ) + { + m_colorNewAlien = RetColor(OpColor(line, "color", 0x88888888)); + } + + if ( Cmd(line, "GreeneryColor") && !bResetObject ) + { + m_colorNewGreen = RetColor(OpColor(line, "color", 0x88888888)); + } + + if ( Cmd(line, "DeepView") && !bResetObject ) + { + m_engine->SetDeepView(OpFloat(line, "air", 500.0f)*UNIT, 0, TRUE); + m_engine->SetDeepView(OpFloat(line, "water", 100.0f)*UNIT, 1, TRUE); + } + + if ( Cmd(line, "FogStart") && !bResetObject ) + { + m_engine->SetFogStart(OpFloat(line, "air", 0.5f), 0); + m_engine->SetFogStart(OpFloat(line, "water", 0.5f), 1); + } + + if ( Cmd(line, "SecondTexture") && !bResetObject ) + { + m_engine->SetSecondTexture(OpInt(line, "rank", 1)); + } + + if ( Cmd(line, "Background") && !bResetObject ) + { + OpString(line, "image", name); + UserDir(dir, name, ""); + m_engine->SetBackground(dir, + OpColor(line, "up", 0x00000000), + OpColor(line, "down", 0x00000000), + OpColor(line, "cloudUp", 0x00000000), + OpColor(line, "cloudDown", 0x00000000), + OpInt(line, "full", 0)); + } + + if ( Cmd(line, "Planet") && !bResetObject ) + { + D3DVECTOR ppos, uv1, uv2; + + ppos = OpPos(line, "pos"); + uv1 = OpPos(line, "uv1"); + uv2 = OpPos(line, "uv2"); + OpString(line, "image", name); + UserDir(dir, name, ""); + m_planet->Create(OpInt(line, "mode", 0), + FPOINT(ppos.x, ppos.z), + OpFloat(line, "dim", 0.2f), + OpFloat(line, "speed", 0.0f), + OpFloat(line, "dir", 0.0f), + dir, + FPOINT(uv1.x, uv1.z), + FPOINT(uv2.x, uv2.z)); + } + + if ( Cmd(line, "FrontsizeName") && !bResetObject ) + { + OpString(line, "image", name); + UserDir(dir, name, ""); + m_engine->SetFrontsizeName(dir); + } + + if ( Cmd(line, "Global") && !bResetObject ) + { + g_unit = OpFloat(line, "unitScale", 4.0f); + m_engine->SetTracePrecision(OpFloat(line, "traceQuality", 1.0f)); + m_bShortCut = OpInt(line, "shortcut", 1); + } + + if ( Cmd(line, "TerrainGenerate") && !bResetObject ) + { + m_terrain->Generate(OpInt(line, "mosaic", 20), + OpInt(line, "brick", 3), + OpFloat(line, "size", 20.0f), + OpFloat(line, "vision", 500.0f)*UNIT, + OpInt(line, "depth", 2), + OpFloat(line, "hard", 0.5f)); + } + + if ( Cmd(line, "TerrainWind") && !bResetObject ) + { + m_terrain->SetWind(OpPos(line, "speed")); + } + + if ( Cmd(line, "TerrainRelief") && !bResetObject ) + { + OpString(line, "image", name); + UserDir(dir, name, "textures"); + m_terrain->ReliefFromBMP(dir, OpFloat(line, "factor", 1.0f), OpInt(line, "border", 1)); + } + + if ( Cmd(line, "TerrainReliefDXF") && !bResetObject ) + { + OpString(line, "image", name); + UserDir(dir, name, "textures"); + m_terrain->ReliefFromDXF(dir, OpFloat(line, "factor", 1.0f)); + } + + if ( Cmd(line, "TerrainResource") && !bResetObject ) + { + OpString(line, "image", name); + UserDir(dir, name, "textures"); + m_terrain->ResFromBMP(dir); + } + + if ( Cmd(line, "TerrainWater") && !bResetObject ) + { + OpString(line, "image", name); + UserDir(dir, name, ""); + pos.x = OpFloat(line, "moveX", 0.0f); + pos.y = OpFloat(line, "moveY", 0.0f); + pos.z = pos.x; + m_water->Create(OpTypeWater(line, "air", WATER_TT), + OpTypeWater(line, "water", WATER_TT), + dir, + RetColor(OpColor(line, "diffuse", 0xffffffff)), + RetColor(OpColor(line, "ambiant", 0xffffffff)), + OpFloat(line, "level", 100.0f)*UNIT, + OpFloat(line, "glint", 1.0f), + pos); + m_colorNewWater = RetColor(OpColor(line, "color", RetColor(m_colorRefWater))); + m_colorShiftWater = OpFloat(line, "brightness", 0.0f); + } + + if ( Cmd(line, "TerrainLava") && !bResetObject ) + { + m_water->SetLava(OpInt(line, "mode", 0)); + } + + if ( Cmd(line, "TerrainCloud") && !bResetObject ) + { + OpString(line, "image", name); + UserDir(dir, name, ""); + m_cloud->Create(dir, + RetColor(OpColor(line, "diffuse", 0xffffffff)), + RetColor(OpColor(line, "ambiant", 0xffffffff)), + OpFloat(line, "level", 500.0f)*UNIT); + } + + if ( Cmd(line, "TerrainBlitz") && !bResetObject ) + { + m_blitz->Create(OpFloat(line, "sleep", 0.0f), + OpFloat(line, "delay", 3.0f), + OpFloat(line, "magnetic", 50.0f)*UNIT); + } + + if ( Cmd(line, "TerrainInitTextures") && !bResetObject ) + { + int dx, dy, tt[100]; + char* op; + + OpString(line, "image", name); + AddExt(name, ".tga"); + dx = OpInt(line, "dx", 1); + dy = OpInt(line, "dy", 1); + op = SearchOp(line, "table"); + for ( i=0 ; iInitTextures(name, tt, dx, dy); + } + + if ( Cmd(line, "TerrainInit") && !bResetObject ) + { + m_terrain->LevelInit(OpInt(line, "id", 1)); + } + + if ( Cmd(line, "TerrainMaterial") && !bResetObject ) + { + OpString(line, "image", name); + AddExt(name, ".tga"); + if ( strstr(name, "%user%") != 0 ) + { + CopyFileToTemp(name); + } + + m_terrain->LevelMaterial(OpInt(line, "id", 0), + name, + OpFloat(line, "u", 0.0f), + OpFloat(line, "v", 0.0f), + OpInt(line, "up", 1), + OpInt(line, "right", 1), + OpInt(line, "down", 1), + OpInt(line, "left", 1), + OpFloat(line, "hard", 0.5f)); + } + + if ( Cmd(line, "TerrainLevel") && !bResetObject ) + { + int id[50]; + char* op; + + op = SearchOp(line, "id"); + i = 0; + while ( TRUE ) + { + id[i] = GetInt(op, i, 0); + if ( id[i++] == 0 ) break; + } + + m_terrain->LevelGenerate(id, + OpFloat(line, "min", 0.0f)*UNIT, + OpFloat(line, "max", 100.0f)*UNIT, + OpFloat(line, "slope", 5.0f), + OpFloat(line, "freq", 100.0f), + OpPos(line, "center")*g_unit, + OpFloat(line, "radius", 0.0f)*g_unit); + } + + if ( Cmd(line, "TerrainCreate") && !bResetObject ) + { + m_terrain->CreateObjects(TRUE); + } + + if ( Cmd(line, "BeginObject") ) + { + InitEye(); + SetMovieLock(FALSE); + if ( !m_bFixScene ) + { +//? CreateObject(D3DVECTOR(0.0f, 0.0f, 0.0f), 0.0f, 0.0f, OBJECT_TOTO); + } + + if ( read[0] != 0 ) // loading file ? + { + pSel = IOReadScene(read, stack); + } + } + + if ( Cmd(line, "CreateObject") && read[0] == 0 ) + { + CObject* pObj; + CBrain* pBrain; + CAuto* pAuto; + CPyro* pyro; + ObjectType type; + PyroType pType; + CameraType cType; + Info info; + float dir; + char op[20]; + char text[100]; + char* p; + int run, gadget; + + type = OpTypeObject(line, "type", OBJECT_NULL); + + gadget = OpInt(line, "gadget", -1); + if ( gadget == -1 ) + { + gadget = 0; + if ( type == OBJECT_TECH || + (type >= OBJECT_PLANT0 && + type <= OBJECT_PLANT19 ) || + (type >= OBJECT_TREE0 && + type <= OBJECT_TREE9 ) || + (type >= OBJECT_TEEN0 && + type <= OBJECT_TEEN49 ) || + (type >= OBJECT_QUARTZ0 && + type <= OBJECT_QUARTZ9 ) || + (type >= OBJECT_ROOT0 && + type <= OBJECT_ROOT4 ) ) // pas ROOT5 ! + { + if ( type != OBJECT_TEEN11 && // lampe ? + type != OBJECT_TEEN12 && // coke ? + type != OBJECT_TEEN20 && // mur ? + type != OBJECT_TEEN21 && // mur ? + type != OBJECT_TEEN22 && // mur ? + type != OBJECT_TEEN26 && // lampe ? + type != OBJECT_TEEN28 && // bouteille ? + type != OBJECT_TEEN34 ) // pierre ? + { + gadget = 1; + } + } + } + if ( gadget != 0 ) // est-ce un gadget ? + { + if ( !TestGadgetQuantity(rankGadget++) ) continue; + } + + pos = OpPos(line, "pos")*g_unit; + dir = OpFloat(line, "dir", 0.0f)*PI; + pObj = CreateObject(pos, dir, + OpFloat(line, "z", 1.0f), + OpFloat(line, "h", 0.0f), + type, + OpFloat(line, "power", 1.0f), + OpInt(line, "trainer", 0), + OpInt(line, "toy", 0), + OpInt(line, "option", 0)); + + if ( pObj != 0 ) + { + pObj->SetDefRank(rankObj); + + if ( type == OBJECT_BASE ) m_bBase = TRUE; + + cType = OpCamera(line, "camera"); + if ( cType != CAMERA_NULL ) + { + pObj->SetCameraType(cType); + } + pObj->SetCameraDist(OpFloat(line, "cameraDist", 50.0f)); + pObj->SetCameraLock(OpInt(line, "cameraLock", 0)); + + pType = OpPyro(line, "pyro"); + if ( pType != PT_NULL ) + { + pyro = new CPyro(m_iMan); + pyro->Create(pType, pObj); + } + + // Met les infos dans borne (OBJECT_INFO). + for ( i=0 ; iRetGamerColorCombi(); + colorRef2.r = 255.0f/256.0f; + colorRef2.g = 132.0f/256.0f; + colorRef2.b = 1.0f/256.0f; // orange + colorNew2 = m_dialog->RetGamerColorBand(); + exclu[0] = FPOINT(192.0f/256.0f, 0.0f/256.0f); + exclu[1] = FPOINT(256.0f/256.0f, 64.0f/256.0f); // crystaux + bombonnes + exclu[2] = FPOINT(208.0f/256.0f, 224.0f/256.0f); + exclu[3] = FPOINT(256.0f/256.0f, 256.0f/256.0f); // écran SatCom + exclu[4] = FPOINT(0.0f, 0.0f); + exclu[5] = FPOINT(0.0f, 0.0f); // terminateur + m_engine->ChangeColor("human.tga", colorRef1, colorNew1, colorRef2, colorNew2, 0.30f, 0.01f, ts, ti, exclu); + + face = RetGamerFace(); + if ( face == 0 ) // normal ? + { + colorRef1.r = 90.0f/256.0f; + colorRef1.g = 95.0f/256.0f; + colorRef1.b = 85.0f/256.0f; // noir + tolerance = 0.15f; + } + if ( face == 1 ) // chauve ? + { + colorRef1.r = 74.0f/256.0f; + colorRef1.g = 58.0f/256.0f; + colorRef1.b = 46.0f/256.0f; // brun + tolerance = 0.20f; + } + if ( face == 2 ) // carlos ? + { + colorRef1.r = 70.0f/256.0f; + colorRef1.g = 40.0f/256.0f; + colorRef1.b = 8.0f/256.0f; // brun + tolerance = 0.30f; + } + if ( face == 3 ) // blond ? + { + colorRef1.r = 74.0f/256.0f; + colorRef1.g = 16.0f/256.0f; + colorRef1.b = 0.0f/256.0f; // jaune + tolerance = 0.20f; + } + colorNew1 = m_dialog->RetGamerColorHair(); + colorRef2.r = 0.0f; + colorRef2.g = 0.0f; + colorRef2.b = 0.0f; + colorNew2.r = 0.0f; + colorNew2.g = 0.0f; + colorNew2.b = 0.0f; + sprintf(name, "face%.2d.tga", face+1); + exclu[0] = FPOINT(105.0f/256.0f, 47.0f/166.0f); + exclu[1] = FPOINT(153.0f/256.0f, 79.0f/166.0f); // bombonne bleu + exclu[2] = FPOINT(0.0f, 0.0f); + exclu[3] = FPOINT(0.0f, 0.0f); // terminateur + m_engine->ChangeColor(name, colorRef1, colorNew1, colorRef2, colorNew2, tolerance, 0.00f, ts, ti, exclu); + + colorRef2.r = 0.0f; + colorRef2.g = 0.0f; + colorRef2.b = 0.0f; + colorNew2.r = 0.0f; + colorNew2.g = 0.0f; + colorNew2.b = 0.0f; + + m_engine->ChangeColor("base1.tga", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, TRUE); + m_engine->ChangeColor("convert.tga", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, TRUE); + m_engine->ChangeColor("derrick.tga", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, TRUE); + m_engine->ChangeColor("factory.tga", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, TRUE); + m_engine->ChangeColor("lemt.tga", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, TRUE); + m_engine->ChangeColor("roller.tga", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, TRUE); + m_engine->ChangeColor("search.tga", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, TRUE); + + exclu[0] = FPOINT( 0.0f/256.0f, 160.0f/256.0f); + exclu[1] = FPOINT(256.0f/256.0f, 256.0f/256.0f); // crayons + exclu[2] = FPOINT(0.0f, 0.0f); + exclu[3] = FPOINT(0.0f, 0.0f); // terminateur + m_engine->ChangeColor("drawer.tga", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, exclu, 0, TRUE); + + exclu[0] = FPOINT(237.0f/256.0f, 176.0f/256.0f); + exclu[1] = FPOINT(256.0f/256.0f, 220.0f/256.0f); // bombonne bleu + exclu[2] = FPOINT(106.0f/256.0f, 150.0f/256.0f); + exclu[3] = FPOINT(130.0f/256.0f, 214.0f/256.0f); // emplacement safe + exclu[4] = FPOINT(0.0f, 0.0f); + exclu[5] = FPOINT(0.0f, 0.0f); // terminateur + m_engine->ChangeColor("subm.tga", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, exclu, 0, TRUE); + + exclu[0] = FPOINT(128.0f/256.0f, 160.0f/256.0f); + exclu[1] = FPOINT(256.0f/256.0f, 256.0f/256.0f); // SatCom + exclu[2] = FPOINT(0.0f, 0.0f); + exclu[3] = FPOINT(0.0f, 0.0f); // terminateur + m_engine->ChangeColor("ant.tga", m_colorRefAlien, m_colorNewAlien, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti, exclu); + m_engine->ChangeColor("mother.tga", m_colorRefAlien, m_colorNewAlien, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti); + + m_engine->ChangeColor("plant.tga", m_colorRefGreen, m_colorNewGreen, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti); + + // PARTIPLOUF0 et PARTIDROP : + ts = FPOINT(0.500f, 0.500f); + ti = FPOINT(0.875f, 0.750f); + m_engine->ChangeColor("effect00.tga", m_colorRefWater, m_colorNewWater, colorRef2, colorNew2, 0.20f, -1.0f, ts, ti, 0, m_colorShiftWater, TRUE); + + // PARTIFLIC : + ts = FPOINT(0.00f, 0.75f); + ti = FPOINT(0.25f, 1.00f); + m_engine->ChangeColor("effect02.tga", m_colorRefWater, m_colorNewWater, colorRef2, colorNew2, 0.20f, -1.0f, ts, ti, 0, m_colorShiftWater, TRUE); +} + +// Met à jour le nombre d'objets non indispansables. + +BOOL CRobotMain::TestGadgetQuantity(int rank) +{ + float percent; + int *table; + + static int table10[10] = {0,1,0,0,0,0,0,0,0,0}; + static int table20[10] = {0,1,0,0,0,1,0,0,0,0}; + static int table30[10] = {0,1,0,1,0,1,0,0,0,0}; + static int table40[10] = {0,1,0,1,0,1,0,1,0,0}; + static int table50[10] = {0,1,0,1,0,1,0,1,0,1}; + static int table60[10] = {0,1,0,1,1,1,0,1,0,1}; + static int table70[10] = {0,1,0,1,1,1,0,1,1,1}; + static int table80[10] = {0,1,1,1,1,1,0,1,1,1}; + static int table90[10] = {0,1,1,1,1,1,1,1,1,1}; + + percent = m_engine->RetGadgetQuantity(); + if ( percent == 0.0f ) return FALSE; + if ( percent == 1.0f ) return TRUE; + + if ( percent <= 0.15f ) table = table10; + else if ( percent <= 0.25f ) table = table20; + else if ( percent <= 0.35f ) table = table30; + else if ( percent <= 0.45f ) table = table40; + else if ( percent <= 0.55f ) table = table50; + else if ( percent <= 0.65f ) table = table60; + else if ( percent <= 0.75f ) table = table70; + else if ( percent <= 0.85f ) table = table80; + else table = table90; + + return table[rank%10]; +} + + + +// Calcule la distance jusqu'à l'objet le plus proche. + +float CRobotMain::SearchNearestObject(D3DVECTOR center, CObject *exclu) +{ + CObject* pObj; + ObjectType type; + D3DVECTOR oPos; + float min, dist, oRadius; + int i, j; + + min = 100000.0f; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; // inactif ? + if ( pObj->RetTruck() != 0 ) continue; // objet porté ? + if ( pObj == exclu ) continue; + + type = pObj->RetType(); + + if ( type == OBJECT_BASE ) + { + oPos = pObj->RetPosition(0); + if ( oPos.x != center.x || + oPos.z != center.z ) + { + dist = Length(center, oPos)-80.0f; + if ( dist < 0.0f ) dist = 0.0f; + min = Min(min, dist); + continue; + } + } + + if ( type == OBJECT_STATION || + type == OBJECT_REPAIR || + type == OBJECT_DESTROYER ) + { + oPos = pObj->RetPosition(0); + dist = Length(center, oPos)-8.0f; + if ( dist < 0.0f ) dist = 0.0f; + min = Min(min, dist); + } + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + dist = Length(center, oPos)-oRadius; + if ( dist < 0.0f ) dist = 0.0f; + min = Min(min, dist); + } + } + return min; +} + +// Calcule un emplacement libre. + +BOOL CRobotMain::FreeSpace(D3DVECTOR ¢er, float minRadius, float maxRadius, + float space, CObject *exclu) +{ + D3DVECTOR pos; + FPOINT p; + float radius, ia, angle, dist, flat; + + if ( minRadius < maxRadius ) // de l'intérieur vers l'extérieur ? + { + for ( radius=minRadius ; radius<=maxRadius ; radius+=space ) + { + ia = space/radius; + for ( angle=0.0f ; angleMoveOnFloor(pos, TRUE); + dist = SearchNearestObject(pos, exclu); + if ( dist >= space ) + { + flat = m_terrain->RetFlatZoneRadius(pos, dist/2.0f); + if ( flat >= dist/2.0f ) + { + center = pos; + return TRUE; + } + } + } + } + } + else // de l'extérieur vers l'intérieur ? + { + for ( radius=maxRadius ; radius>=minRadius ; radius-=space ) + { + ia = space/radius; + for ( angle=0.0f ; angleMoveOnFloor(pos, TRUE); + dist = SearchNearestObject(pos, exclu); + if ( dist >= space ) + { + flat = m_terrain->RetFlatZoneRadius(pos, dist/2.0f); + if ( flat >= dist/2.0f ) + { + center = pos; + return TRUE; + } + } + } + } + } + return FALSE; +} + +// Calcule le rayon maximal d'un emplacement libre. + +float CRobotMain::RetFlatZoneRadius(D3DVECTOR center, float maxRadius, + CObject *exclu) +{ + float dist; + + dist = SearchNearestObject(center, exclu); + if ( dist == 0.0f ) return 0.0f; + if ( dist < maxRadius ) + { + maxRadius = dist; + } + return m_terrain->RetFlatZoneRadius(center, maxRadius); +} + + +// Cache la zone constructible lorsqu'un cube de métal est repris. + +void CRobotMain::HideDropZone(CObject* metal) +{ + if ( m_showLimit[1].bUsed && + m_showLimit[1].link == metal ) + { + FlushShowLimit(1); + } + + if ( m_showLimit[2].bUsed && + m_showLimit[2].link == metal ) + { + FlushShowLimit(2); + } +} + +// Montre la zone constructible lorsqu'un cube de métal est déposé. + +void CRobotMain::ShowDropZone(CObject* metal, CObject* truck) +{ + CObject* pObj; + ObjectType type; + D3DVECTOR center, oPos; + float oMax, tMax, dist, oRadius, radius; + int i, j; + + if ( metal == 0 ) return; + + center = metal->RetPosition(0); + + // Calcule le rayon maximal possible en fonction des autres objets. + oMax = 30.0f; // rayon permettant de construire le plus grand bâtiment + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; // inactif ? + if ( pObj->RetTruck() != 0 ) continue; // objet porté ? + if ( pObj == metal ) continue; + if ( pObj == truck ) continue; + + type = pObj->RetType(); + if ( type == OBJECT_BASE ) + { + oPos = pObj->RetPosition(0); + dist = Length(center, oPos)-80.0f; + oMax = Min(oMax, dist); + } + else + { + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + dist = Length(center, oPos)-oRadius; + oMax = Min(oMax, dist); + } + } + + if ( type == OBJECT_DERRICK || + type == OBJECT_FACTORY || + type == OBJECT_STATION || + type == OBJECT_CONVERT || + type == OBJECT_REPAIR || + type == OBJECT_DESTROYER|| + type == OBJECT_TOWER || + type == OBJECT_RESEARCH || + type == OBJECT_RADAR || + type == OBJECT_ENERGY || + type == OBJECT_LABO || + type == OBJECT_NUCLEAR || + type == OBJECT_START || + type == OBJECT_END || + type == OBJECT_INFO || + type == OBJECT_PARA || + type == OBJECT_SAFE || + type == OBJECT_HUSTON ) // bâtiment ? + { + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + dist = Length(center, oPos)-oRadius-BUILDMARGIN; + oMax = Min(oMax, dist); + } + } + } + + // Calcule le rayon maximal possible en fonction du terrain. + if ( oMax >= 2.0f ) + { + tMax = m_terrain->RetFlatZoneRadius(center, 30.0f); + } + else + { + tMax = 0.0f; + } + + radius = Min(oMax, tMax); + if ( radius >= 2.0f ) + { + SetShowLimit(1, PARTILIMIT2, metal, center, radius, 10.0f); + } +} + +// Efface les limites montrées. + +void CRobotMain::FlushShowLimit(int i) +{ + int j; + + if ( m_showLimit[i].link != 0 ) + { + m_showLimit[i].link->StopShowLimit(); + } + + for ( j=0 ; jDeleteParticule(m_showLimit[i].parti[j]); + m_showLimit[i].parti[j] = 0; + } + + m_showLimit[i].total = 0; + m_showLimit[i].link = 0; + m_showLimit[i].bUsed = FALSE; +} + +// Spécifie les limites à montrer. + +void CRobotMain::SetShowLimit(int i, ParticuleType parti, CObject *pObj, + D3DVECTOR pos, float radius, float duration) +{ + FPOINT dim; + float dist; + int j; + + FlushShowLimit(i); // efface les limites actuelles + + if ( radius <= 0.0f ) return; + + if ( radius <= 50.0f ) + { + dim = FPOINT(0.3f, 0.3f); + dist = 2.5f; + } + else + { + dim = FPOINT(1.5f, 1.5f); + dist = 10.0f; + } + + m_showLimit[i].bUsed = TRUE; + m_showLimit[i].link = pObj; + m_showLimit[i].pos = pos; + m_showLimit[i].radius = radius; + m_showLimit[i].duration = duration; + m_showLimit[i].total = (int)((radius*2.0f*PI)/dist); + if ( m_showLimit[i].total > MAXSHOWPARTI ) m_showLimit[i].total = MAXSHOWPARTI; + m_showLimit[i].time = 0.0f; + + for ( j=0 ; jCreateParticule(pos, D3DVECTOR(0.0f, 0.0f, 0.0f), dim, parti, duration); + } +} + +// Ajuste les limites à montrer. + +void CRobotMain::AdjustShowLimit(int i, D3DVECTOR pos) +{ + m_showLimit[i].pos = pos; +} + +// Monter les limites de l'objet sélectionné. + +void CRobotMain::StartShowLimit() +{ + CObject* pObj; + + pObj = RetSelect(); + if ( pObj == 0 ) return; + + pObj->StartShowLimit(); +} + +// Fait avancer les limites montrées. + +void CRobotMain::FrameShowLimit(float rTime) +{ + D3DVECTOR pos; + FPOINT center, rotate; + float angle, factor, speed; + int i, j; + + if ( m_engine->RetPause() ) return; + + for ( i=0 ; i= m_showLimit[i].duration ) + { + FlushShowLimit(i); + continue; + } + + if ( m_showLimit[i].time < 1.0f ) + { + factor = m_showLimit[i].time; + } + else if ( m_showLimit[i].time > m_showLimit[i].duration-1.0f ) + { + factor = m_showLimit[i].duration-m_showLimit[i].time; + } + else + { + factor = 1.0f; + } + + speed = 0.4f-m_showLimit[i].radius*0.001f; + if ( speed < 0.1f ) speed = 0.1f; + angle = m_showLimit[i].time*speed; + + for ( j=0 ; jMoveOnFloor(pos, TRUE); + if ( m_showLimit[i].radius <= 50.0f ) pos.y += 0.5f; + else pos.y += 2.0f; + m_particule->SetPosition(m_showLimit[i].parti[j], pos); +//? m_particule->SetAngle(m_showLimit[i].parti[j], angle-PI/2.0f); + + angle += (2.0f*PI)/m_showLimit[i].total; + } + } +} + + + +// Retourne un pointeur sur le dernier backslash d'un nom de fichier. + +char* SearchLastDir(char *filename) +{ + char* p = filename; + + while ( *p++ != 0 ); + p --; // ^sur le zéro terminateur + + while ( p != filename ) + { + if ( *(--p) == '\\' ) return p; + } + return 0; +} + + +// Compile tous les scripts des robots. + +void CRobotMain::CompileScript(BOOL bSoluce) +{ + CObject* pObj; + CBrain* brain; + int i, j, nbError, lastError, run; + char* name; + + nbError = 0; + do + { + lastError = nbError; + nbError = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + if ( pObj->RetTruck() != 0 ) continue; + + brain = pObj->RetBrain(); + if ( brain == 0 ) continue; + + for ( j=0 ; j<10 ; j++ ) + { + if ( brain->RetCompile(j) ) continue; + + name = brain->RetScriptName(j); + if ( name[0] != 0 ) + { + brain->ReadProgram(j, name); + if ( !brain->RetCompile(j) ) nbError++; + } + } + + LoadOneScript(pObj, nbError); + } + } + while ( nbError > 0 && nbError != lastError ); + + // Charge toutes les solutions. + if ( bSoluce ) + { + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + if ( pObj->RetTruck() != 0 ) continue; + + brain = pObj->RetBrain(); + if ( brain == 0 ) continue; + + name = brain->RetSoluceName(); + if ( name[0] != 0 ) + { + brain->ReadSoluce(name); // charge solution + } + } + } + + // Démarre tous les programmes selon la commande "run". + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + if ( pObj->RetTruck() != 0 ) continue; + + brain = pObj->RetBrain(); + if ( brain == 0 ) continue; + + run = brain->RetScriptRun(); + if ( run != -1 ) + { + brain->RunProgram(run); // démarre le programme + } + } +} + +// Charge tous les programmes d'un robot. + +void CRobotMain::LoadOneScript(CObject *pObj, int &nbError) +{ + ObjectType type; + CBrain* brain; + char filename[_MAX_FNAME]; + char* name; + int rank, i, objRank; + + brain = pObj->RetBrain(); + if ( brain == 0 ) return; + + if ( !IsSelectable(pObj) ) return; + + type = pObj->RetType(); + if ( type == OBJECT_HUMAN ) return; + + objRank = pObj->RetDefRank(); + if ( objRank == -1 ) return; + + name = m_dialog->RetSceneName(); + rank = m_dialog->RetSceneRank(); + + for ( i=0 ; iRetCompile(i) ) continue; +//? if ( brain->ProgramExist(i) ) continue; + + sprintf(filename, "%s\\%s\\%c%.3d%.3d%.1d.txt", + RetSavegameDir(), m_gamerName, name[0], rank, objRank, i); + brain->ReadProgram(i, filename); + if ( !brain->RetCompile(i) ) nbError++; + } +} + +// Charge tous les programmes d'un robot. + +void CRobotMain::LoadFileScript(CObject *pObj, char* filename, int objRank, + int &nbError) +{ + ObjectType type; + CBrain* brain; + char fn[_MAX_FNAME]; + char* ldir; + char* name; + int rank, i; + + if ( objRank == -1 ) return; + + brain = pObj->RetBrain(); + if ( brain == 0 ) return; + + type = pObj->RetType(); + if ( type == OBJECT_HUMAN ) return; + + name = m_dialog->RetSceneName(); + rank = m_dialog->RetSceneRank(); + + strcpy(fn, filename); + ldir = SearchLastDir(fn); + if ( ldir == 0 ) return; + + for ( i=0 ; iRetCompile(i) ) continue; +//? if ( brain->ProgramExist(i) ) continue; + + sprintf(ldir, "\\prog%.3d%.1d.txt", objRank, i); + brain->ReadProgram(i, fn); + if ( !brain->RetCompile(i) ) nbError++; + } +} + +// Sauve tous les programmes de tous les robots. + +void CRobotMain::SaveAllScript() +{ + CObject* pObj; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + SaveOneScript(pObj); + } +} + +// Sauve tous les programmes d'un robot. +// Si un programme n'existe pas, le fichier correspondant est détruit. + +void CRobotMain::SaveOneScript(CObject *pObj) +{ + ObjectType type; + CBrain* brain; + char filename[_MAX_FNAME]; + char* name; + int rank, i, objRank; + + brain = pObj->RetBrain(); + if ( brain == 0 ) return; + + if ( !IsSelectable(pObj) ) return; + + type = pObj->RetType(); + if ( type == OBJECT_HUMAN ) return; + + objRank = pObj->RetDefRank(); + if ( objRank == -1 ) return; + + name = m_dialog->RetSceneName(); + rank = m_dialog->RetSceneRank(); + + for ( i=0 ; iWriteProgram(i, filename); + } +} + +// Sauve tous les programmes d'un robot. +// Si un programme n'existe pas, le fichier correspondant est détruit. + +void CRobotMain::SaveFileScript(CObject *pObj, char* filename, int objRank) +{ + ObjectType type; + CBrain* brain; + char fn[_MAX_FNAME]; + char* ldir; + char* name; + int rank, i; + + if ( objRank == -1 ) return; + + brain = pObj->RetBrain(); + if ( brain == 0 ) return; + + type = pObj->RetType(); + if ( type == OBJECT_HUMAN ) return; + + name = m_dialog->RetSceneName(); + rank = m_dialog->RetSceneRank(); + + strcpy(fn, filename); + ldir = SearchLastDir(fn); + if ( ldir == 0 ) return; + + for ( i=0 ; iWriteProgram(i, fn); + } +} + +// Sauve le stack du programme en exécution d'un robot. + +BOOL CRobotMain::SaveFileStack(CObject *pObj, FILE *file, int objRank) +{ + ObjectType type; + CBrain* brain; + + if ( objRank == -1 ) return TRUE; + + brain = pObj->RetBrain(); + if ( brain == 0 ) return TRUE; + + type = pObj->RetType(); + if ( type == OBJECT_HUMAN ) return TRUE; + + return brain->WriteStack(file); +} + +// Reprend le stack du programme en exécution d'un robot. + +BOOL CRobotMain::ReadFileStack(CObject *pObj, FILE *file, int objRank) +{ + ObjectType type; + CBrain* brain; + + if ( objRank == -1 ) return TRUE; + + brain = pObj->RetBrain(); + if ( brain == 0 ) return TRUE; + + type = pObj->RetType(); + if ( type == OBJECT_HUMAN ) return TRUE; + + return brain->ReadStack(file); +} + + +// Vide la liste. + +BOOL CRobotMain::FlushNewScriptName() +{ + int i; + + for ( i=0 ; i 0 ) return TRUE; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + pBrain = pObj->RetBrain(); + if ( pBrain != 0 ) + { + if ( pBrain->IsBusy() ) return TRUE; + } + +//? pAuto = pObj->RetAuto(); +//? if ( pAuto != 0 ) +//? { +//? if ( pAuto->RetBusy() ) return TRUE; +//? } + } + return FALSE; +} + +// Ecrit un objet dans le fichier de sauvegarde. + +void CRobotMain::IOWriteObject(FILE *file, CObject* pObj, char *cmd) +{ + D3DVECTOR pos; + CBrain* pBrain; + char line[3000]; + char name[100]; + int run, i; + + if ( pObj->RetType() == OBJECT_FIX ) return; + + strcpy(line, cmd); + + sprintf(name, " type=%s", GetTypeObject(pObj->RetType())); + strcat(line, name); + + sprintf(name, " id=%d", pObj->RetID()); + strcat(line, name); + + pos = pObj->RetPosition(0)/g_unit; + sprintf(name, " pos=%.2f;%.2f;%.2f", pos.x, pos.y, pos.z); + strcat(line, name); + + pos = pObj->RetAngle(0)/(PI/180.0f); + sprintf(name, " angle=%.2f;%.2f;%.2f", pos.x, pos.y, pos.z); + strcat(line, name); + + pos = pObj->RetZoom(0); + sprintf(name, " zoom=%.2f;%.2f;%.2f", pos.x, pos.y, pos.z); + strcat(line, name); + + for ( i=1 ; iRetObjectRank(i) == -1 ) continue; + + pos = pObj->RetPosition(i); + if ( pos.x != 0.0f || pos.y != 0.0f || pos.z != 0.0f ) + { + pos /= g_unit; + sprintf(name, " p%d=%.2f;%.2f;%.2f", i, pos.x, pos.y, pos.z); + strcat(line, name); + } + + pos = pObj->RetAngle(i); + if ( pos.x != 0.0f || pos.y != 0.0f || pos.z != 0.0f ) + { + pos /= (PI/180.0f); + sprintf(name, " a%d=%.2f;%.2f;%.2f", i, pos.x, pos.y, pos.z); + strcat(line, name); + } + + pos = pObj->RetZoom(i); + if ( pos.x != 1.0f || pos.y != 1.0f || pos.z != 1.0f ) + { + sprintf(name, " z%d=%.2f;%.2f;%.2f", i, pos.x, pos.y, pos.z); + strcat(line, name); + } + } + + sprintf(name, " trainer=%d", pObj->RetTrainer()); + strcat(line, name); + + sprintf(name, " option=%d", pObj->RetOption()); + strcat(line, name); + + if ( pObj == m_infoObject ) // objet sélectionné ? + { + sprintf(name, " select=1"); + strcat(line, name); + } + + pObj->Write(line); + + if ( pObj->RetType() == OBJECT_BASE ) + { + sprintf(name, " run=3"); // stoppé et ouvert (PARAM_FIXSCENE) + strcat(line, name); + } + + pBrain = pObj->RetBrain(); + if ( pBrain != 0 ) + { + run = pBrain->RetProgram(); + if ( run != -1 ) + { + sprintf(name, " run=%d", run+1); + strcat(line, name); + } + } + + strcat(line, "\n"); + fputs(line, file); +} + +// Enregistre la partie en cours. + +BOOL CRobotMain::IOWriteScene(char *filename, char *filecbot, char *info) +{ + FILE* file; + char line[500]; + char* name; + CObject *pObj, *pPower, *pFret; + float sleep, delay, magnetic, progress; + int i, objRank; + long version; + + file = fopen(filename, "w"); + if ( file == NULL ) return FALSE; + + sprintf(line, "Title text=\"%s\"\n", info); + fputs(line, file); + + sprintf(line, "Version maj=%d min=%d\n", 0, 1); + fputs(line, file); + + name = m_dialog->RetSceneName(); + if ( strcmp(name, "user") == 0 ) + { + sprintf(line, "Mission base=\"%s\" rank=%.3d dir=\"%s\"\n", name, m_dialog->RetSceneRank(), m_dialog->RetSceneDir()); + } + else + { + sprintf(line, "Mission base=\"%s\" rank=%.3d\n", name, m_dialog->RetSceneRank()); + } + fputs(line, file); + + sprintf(line, "Map zoom=%.2f\n", m_map->RetZoomMap()); + fputs(line, file); + + sprintf(line, "DoneResearch bits=%d\n", g_researchDone); + fputs(line, file); + + if ( m_blitz->GetStatus(sleep, delay, magnetic, progress) ) + { + sprintf(line, "BlitzMode sleep=%.2f delay=%.2f magnetic=%.2f progress=%.2f\n", sleep, delay, magnetic/g_unit, progress); + fputs(line, file); + } + + objRank = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetType() == OBJECT_TOTO ) continue; + if ( pObj->RetType() == OBJECT_FIX ) continue; + if ( pObj->RetTruck() != 0 ) continue; + if ( pObj->RetBurn() ) continue; + if ( pObj->RetDead() ) continue; + if ( pObj->RetExplo() ) continue; + + pPower = pObj->RetPower(); + pFret = pObj->RetFret(); + + if ( pFret != 0 ) // objet transporté ? + { + IOWriteObject(file, pFret, "CreateFret"); + } + + if ( pPower != 0 ) // pile transportée ? + { + IOWriteObject(file, pPower, "CreatePower"); + } + + IOWriteObject(file, pObj, "CreateObject"); + + SaveFileScript(pObj, filename, objRank++); + } + fclose(file); + +#if CBOT_STACK + // Ecrit le fichier des stacks d'exécution. + file = fOpen(filecbot, "wb"); + if ( file == NULL ) return FALSE; + + version = 1; + fWrite(&version, sizeof(long), 1, file); // version de COLOBOT + version = CBotProgram::GivVersion(); + fWrite(&version, sizeof(long), 1, file); // version de CBOT + + objRank = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetType() == OBJECT_TOTO ) continue; + if ( pObj->RetType() == OBJECT_FIX ) continue; + if ( pObj->RetTruck() != 0 ) continue; + if ( pObj->RetBurn() ) continue; + if ( pObj->RetDead() ) continue; + + if ( !SaveFileStack(pObj, file, objRank++) ) break; + } + CBotClass::SaveStaticState(file); + fClose(file); +#endif + + m_delayWriteMessage = 4; // affiche message dans 3 frames + return TRUE; +} + +// Reprend un objet enregistré. + +CObject* CRobotMain::IOReadObject(char *line, char* filename, int objRank) +{ + CObject* pObj; +//? CBrain* pBrain; + CAuto* pAuto; + D3DVECTOR pos, dir, zoom; + ObjectType type; + int id, run, trainer, toy, option, i; + char op[10]; + + pos = OpDir(line, "pos")*g_unit; + dir = OpDir(line, "angle")*(PI/180.0f); + zoom = OpDir(line, "zoom"); + type = OpTypeObject(line, "type", OBJECT_NULL); + id = OpInt(line, "id", 0); + if ( type == OBJECT_NULL ) return 0; + trainer = OpInt(line, "trainer", 0); + toy = OpInt(line, "toy", 0); + option = OpInt(line, "option", 0); + pObj = CreateObject(pos, dir.y, 1.0f, 0.0f, type, 0.0f, trainer, toy, option); + pObj->SetDefRank(objRank); + pObj->SetPosition(0, pos); + pObj->SetAngle(0, dir); + pObj->SetID(id); + if ( g_id < id ) g_id = id; + + if ( zoom.x != 0.0f || zoom.y != 0.0f || zoom.z != 0.0f ) + { + pObj->SetZoom(0, zoom); + } + + for ( i=1 ; iRetObjectRank(i) == -1 ) continue; + + sprintf(op, "p%d", i); + pos = OpDir(line, op); + if ( pos.x != 0.0f || pos.y != 0.0f || pos.z != 0.0f ) + { + pObj->SetPosition(i, pos*g_unit); + } + + sprintf(op, "a%d", i); + dir = OpDir(line, op); + if ( dir.x != 0.0f || dir.y != 0.0f || dir.z != 0.0f ) + { + pObj->SetAngle(i, dir*(PI/180.0f)); + } + + sprintf(op, "z%d", i); + zoom = OpDir(line, op); + if ( zoom.x != 0.0f || zoom.y != 0.0f || zoom.z != 0.0f ) + { + pObj->SetZoom(i, zoom); + } + } + + if ( type == OBJECT_BASE ) m_bBase = TRUE; + + pObj->Read(line); + +#if CBOT_STACK +#else + LoadFileScript(pObj, filename, objRank, i); +#endif + + run = OpInt(line, "run", -1); + if ( run != -1 ) + { +#if CBOT_STACK +#else + pBrain = pObj->RetBrain(); + if ( pBrain != 0 ) + { + pBrain->RunProgram(run-1); // démarre le programme + } +#endif + + pAuto = pObj->RetAuto(); + if ( pAuto != 0 ) + { + pAuto->Start(run); // démarre le film + } + } + + return pObj; +} + +// Reprend une partie enregistrée. + +CObject* CRobotMain::IOReadScene(char *filename, char *filecbot) +{ + FILE* file; + CObject *pObj, *pPower, *pFret, *pSel; + char line[3000]; + float sleep, delay, progress, magnetic; + int i, objRank, nbError, lastError; + long version; + + m_bBase = FALSE; + + file = fopen(filename, "r"); + if ( file == NULL ) return 0; + + pFret = 0; + pPower = 0; + pSel = 0; + objRank = 0; + while ( fgets(line, 3000, file) != NULL ) + { + for ( i=0 ; i<3000 ; i++ ) + { + if ( line[i] == '\t' ) line[i] = ' '; // remplace tab par space + if ( line[i] == '/' && line[i+1] == '/' ) + { + line[i] = 0; + break; + } + } + + if ( Cmd(line, "Map") ) + { + m_map->ZoomMap(OpFloat(line, "zoom", 1.0f)); + } + + if ( Cmd(line, "DoneResearch") ) + { + g_researchDone = OpInt(line, "bits", 0); + } + + if ( Cmd(line, "BlitzMode") ) + { + sleep = OpFloat(line, "sleep", 0.0f); + delay = OpFloat(line, "delay", 3.0f); + magnetic = OpFloat(line, "magnetic", 50.0f)*g_unit; + progress = OpFloat(line, "progress", 0.0f); + m_blitz->SetStatus(sleep, delay, magnetic, progress); + } + + if ( Cmd(line, "CreateFret") ) + { + pFret = IOReadObject(line, filename, -1); + } + + if ( Cmd(line, "CreatePower") ) + { + pPower = IOReadObject(line, filename, -1); + } + + if ( Cmd(line, "CreateObject") ) + { + pObj = IOReadObject(line, filename, objRank++); + + if ( OpInt(line, "select", 0) ) + { + pSel = pObj; + } + + if ( pFret != 0 ) + { + CTaskManip* task; + + pObj->SetFret(pFret); + task = new CTaskManip(m_iMan, pObj); + task->Start(TMO_AUTO, TMA_GRAB); // tient l'objet ! + delete task; + } + + if ( pPower != 0 ) + { + pObj->SetPower(pPower); + pPower->SetTruck(pObj); + } + + pFret = 0; + pPower = 0; + } + } + fclose(file); + +#if CBOT_STACK + // Compile les scripts. + nbError = 0; + do + { + lastError = nbError; + nbError = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + if ( pObj->RetTruck() != 0 ) continue; + + objRank = pObj->RetDefRank(); + if ( objRank == -1 ) continue; + + LoadFileScript(pObj, filename, objRank, nbError); + } + } + while ( nbError > 0 && nbError != lastError ); + + // Lit le fichier des stacks d'exécution. + file = fOpen(filecbot, "rb"); + if ( file != NULL ) + { + fRead(&version, sizeof(long), 1, file); // version de COLOBOT + if ( version == 1 ) + { + fRead(&version, sizeof(long), 1, file); // version de CBOT + if ( version == CBotProgram::GivVersion() ) + { + objRank = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetType() == OBJECT_TOTO ) continue; + if ( pObj->RetType() == OBJECT_FIX ) continue; + if ( pObj->RetTruck() != 0 ) continue; + if ( pObj->RetBurn() ) continue; + if ( pObj->RetDead() ) continue; + + if ( !ReadFileStack(pObj, file, objRank++) ) break; + } + } + } + CBotClass::RestoreStaticState(file); + fClose(file); + } +#endif + + return pSel; +} + + +// Ecrit les paramètres globaux pour le jeu libre. + +void CRobotMain::WriteFreeParam() +{ + FILE* file; + char filename[_MAX_FNAME]; + char line[100]; + + m_freeResearch |= g_researchDone; + m_freeBuild |= g_build; + + if ( m_gamerName[0] == 0 ) return; + + sprintf(filename, "%s\\%s\\research.gam", RetSavegameDir(), m_gamerName); + file = fopen(filename, "w"); + if ( file == NULL ) return; + + sprintf(line, "research=%d build=%d\n", m_freeResearch, m_freeBuild); + fputs(line, file); + fclose(file); +} + +// Lit les paramètres globaux pour le jeu libre. + +void CRobotMain::ReadFreeParam() +{ + FILE* file; + char filename[_MAX_FNAME]; + char line[100]; + + m_freeResearch = 0; + m_freeBuild = 0; + + if ( m_gamerName[0] == 0 ) return; + + sprintf(filename, "%s\\%s\\research.gam", RetSavegameDir(), m_gamerName); + file = fopen(filename, "r"); + if ( file == NULL ) return; + + if ( fgets(line, 100, file) != NULL ) + { + sscanf(line, "research=%d build=%d\n", &m_freeResearch, &m_freeBuild); + } + + fclose(file); +} + + +// Remet tous les objets à leur place initiale. + +void CRobotMain::ResetObject() +{ +#if 0 + CObject* pObj; + CObject* pTruck; + CAuto* pAuto; + CBrain* brain; + CPyro* pyro; + ResetCap cap; + D3DVECTOR pos, angle; + int i; + + // Supprime tous les effets pyrotechniques en cours. + while ( TRUE ) + { + pyro = (CPyro*)m_iMan->SearchInstance(CLASS_PYRO, 0); + if ( pyro == 0 ) break; + + pyro->DeleteObject(); + delete pyro; + } + + // Supprime toutes les balles en cours. + m_particule->DeleteParticule(PARTIGUN1); + m_particule->DeleteParticule(PARTIGUN2); + m_particule->DeleteParticule(PARTIGUN3); + m_particule->DeleteParticule(PARTIGUN4); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + cap = pObj->RetResetCap(); + if ( cap == RESET_NONE ) continue; + + if ( cap == RESET_DELETE ) + { + pTruck = pObj->RetTruck(); + if ( pTruck != 0 ) + { + pTruck->SetFret(0); + pObj->SetTruck(0); + } + pObj->DeleteObject(); + delete pObj; + i --; + continue; + } + + pAuto = pObj->RetAuto(); + if ( pAuto != 0 ) + { + pAuto->Abort(); + } + + if ( pObj->RetEnable() ) // objet toujours actif ? + { + brain = pObj->RetBrain(); + if ( brain != 0 ) + { + pos = pObj->RetResetPosition(); + angle = pObj->RetResetAngle(); + + if ( pos == pObj->RetPosition(0) && + angle == pObj->RetAngle(0) ) continue; + brain->StartTaskReset(pos, angle); + continue; + } + } + + pObj->SetEnable(TRUE); // de nouveau actif + + pos = pObj->RetResetPosition(); + angle = pObj->RetResetAngle(); + + if ( pos == pObj->RetPosition(0) && + angle == pObj->RetAngle(0) ) continue; + + pyro = new CPyro(m_iMan); + pyro->Create(PT_RESET, pObj); + + brain = pObj->RetBrain(); + if ( brain != 0 ) + { + brain->RunProgram(pObj->RetResetRun()); + } + } +#else + m_bResetCreate = TRUE; +#endif +} + +// Remet tous les objets à leur place initiale. + +void CRobotMain::ResetCreate() +{ + CObject* pObj; + CPyro* pyro; + ResetCap cap; + int i; + + SaveAllScript(); + + // Supprime toutes les balles en cours. + m_particule->DeleteParticule(PARTIGUN1); + m_particule->DeleteParticule(PARTIGUN2); + m_particule->DeleteParticule(PARTIGUN3); + m_particule->DeleteParticule(PARTIGUN4); + + DeselectAll(); // enlève les boutons de commande + DeleteAllObjects(); // supprime toute la scène 3D actuelle + + m_particule->FlushParticule(); + m_terrain->FlushBuildingLevel(); + m_iMan->Flush(CLASS_OBJECT); + m_iMan->Flush(CLASS_PHYSICS); + m_iMan->Flush(CLASS_BRAIN); + m_iMan->Flush(CLASS_PYRO); + m_camera->SetType(CAMERA_DIALOG); + + CreateScene(m_dialog->RetSceneSoluce(), FALSE, TRUE); + + if ( !RetNiceReset() ) return; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + cap = pObj->RetResetCap(); + if ( cap == RESET_NONE ) continue; + + pyro = new CPyro(m_iMan); + pyro->Create(PT_RESET, pObj); + } +} + +// Vérifie si la mission est terminée. + +Error CRobotMain::CheckEndMission(BOOL bFrame) +{ + CObject* pObj; + D3DVECTOR bPos, oPos; + ObjectType type; + int t, i, nb; + + for ( t=0 ; tSearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + // Ne pas utiliser RetActif(), car un ver invisible (sous terre) + // doit être considéré comme existant ici ! + if ( pObj->RetLock() ) continue; + if ( pObj->RetRuin() ) continue; + if ( !pObj->RetEnable() ) continue; + + type = pObj->RetType(); + if ( type == OBJECT_SCRAP2 || + type == OBJECT_SCRAP3 || + type == OBJECT_SCRAP4 || + type == OBJECT_SCRAP5 ) // déchet ? + { + type = OBJECT_SCRAP1; + } + if ( type != m_endTake[t].type ) continue; + + if ( pObj->RetTruck() == 0 ) + { + oPos = pObj->RetPosition(0); + } + else + { + oPos = pObj->RetTruck()->RetPosition(0); + } + oPos.y = 0.0f; + if ( Length2d(oPos, bPos) <= m_endTake[t].dist ) + { + nb ++; + } + } + + if ( nb <= m_endTake[t].lost ) + { + if ( m_endTake[t].type == OBJECT_HUMAN ) + { + if ( m_lostDelay == 0.0f ) + { + m_lostDelay = 0.1f; // perdu immédiatement + m_winDelay = 0.0f; + } + m_displayText->SetEnable(FALSE); + return INFO_LOSTq; + } + else + { + if ( m_lostDelay == 0.0f ) + { + m_displayText->DisplayError(INFO_LOST, D3DVECTOR(0.0f,0.0f,0.0f)); + m_lostDelay = m_endTakeLostDelay; // perdu dans 6 secondes + m_winDelay = 0.0f; + } + m_displayText->SetEnable(FALSE); + return INFO_LOST; + } + } + if ( nb < m_endTake[t].min || + nb > m_endTake[t].max ) + { + m_displayText->SetEnable(TRUE); + return ERR_MISSION_NOTERM; + } + if ( m_endTake[t].bImmediat ) + { + if ( m_winDelay == 0.0f ) + { + m_winDelay = m_endTakeWinDelay; // gagné dans x seconde + m_lostDelay = 0.0f; + } + m_displayText->SetEnable(FALSE); + return ERR_OK; // mission terminée + } + } + + if ( m_endTakeResearch != 0 ) + { + if ( m_endTakeResearch != (m_endTakeResearch&g_researchDone) ) + { + m_displayText->SetEnable(TRUE); + return ERR_MISSION_NOTERM; + } + } + + if ( m_endTakeWinDelay == -1.0f ) + { + m_winDelay = 1.0f; // gagné dans 1 seconde + m_lostDelay = 0.0f; + m_displayText->SetEnable(FALSE); + return ERR_OK; // mission terminée + } + + if ( bFrame && m_bBase ) return ERR_MISSION_NOTERM; + + if ( m_winDelay == 0.0f ) + { + m_displayText->DisplayError(INFO_WIN, D3DVECTOR(0.0f,0.0f,0.0f)); + m_winDelay = m_endTakeWinDelay; // gagné dans 2 secondes + m_lostDelay = 0.0f; + } + m_displayText->SetEnable(FALSE); + return ERR_OK; // mission terminée +} + +// Vérifie si la mission est terminée suite à l'affichage d'un message. + +void CRobotMain::CheckEndMessage(char *message) +{ + int t; + + for ( t=0 ; tDisplayError(INFO_WIN, D3DVECTOR(0.0f,0.0f,0.0f)); + m_winDelay = m_endTakeWinDelay; // gagné dans 2 secondes + m_lostDelay = 0.0f; + } + } +} + + +// Retourne le nombre d'instructions obligatoires. + +int CRobotMain::RetObligatoryToken() +{ + return m_obligatoryTotal; +} + +// Retourne le nom d'une instruction obligatoire. + +char* CRobotMain::RetObligatoryToken(int i) +{ + return m_obligatoryToken[i]; +} + +// Vérifie si une instruction fait partie de la liste obligatoire. + +int CRobotMain::IsObligatoryToken(char *token) +{ + int i; + + for ( i=0 ; iRetGlint(); +} + +BOOL CRobotMain::RetSoluce4() +{ + return m_dialog->RetSoluce4(); +} + +BOOL CRobotMain::RetMovies() +{ + return m_dialog->RetMovies(); +} + +BOOL CRobotMain::RetNiceReset() +{ + return m_dialog->RetNiceReset(); +} + +BOOL CRobotMain::RetHimselfDamage() +{ + return m_dialog->RetHimselfDamage(); +} + +BOOL CRobotMain::RetShowSoluce() +{ + return m_bShowSoluce; +} + +BOOL CRobotMain::RetSceneSoluce() +{ + if ( m_infoFilename[SATCOM_SOLUCE][0] == 0 ) return FALSE; + return m_dialog->RetSceneSoluce(); +} + +BOOL CRobotMain::RetShowAll() +{ + return m_bShowAll; +} + +BOOL CRobotMain::RetCheatRadar() +{ + return m_bCheatRadar; +} + +char* CRobotMain::RetSavegameDir() +{ + return m_dialog->RetSavegameDir(); +} + +char* CRobotMain::RetPublicDir() +{ + return m_dialog->RetPublicDir(); +} + +char* CRobotMain::RetFilesDir() +{ + return m_dialog->RetFilesDir(); +} + + +// Change le nom du joueur. + +void CRobotMain::SetGamerName(char *name) +{ + strcpy(m_gamerName, name); + SetGlobalGamerName(m_gamerName); + ReadFreeParam(); +} + +// Donne le nom du joueur. + +char* CRobotMain::RetGamerName() +{ + return m_gamerName; +} + + +// Retourne la représentation à utiliser pour le joueur. + +int CRobotMain::RetGamerFace() +{ + return m_dialog->RetGamerFace(); +} + +// Retourne la représentation à utiliser pour le joueur. + +int CRobotMain::RetGamerGlasses() +{ + return m_dialog->RetGamerGlasses(); +} + +// Retourne le mode avec seulement la tête. + +BOOL CRobotMain::RetGamerOnlyHead() +{ + return m_dialog->RetGamerOnlyHead(); +} + +// Retourne l'angle de présentation. + +float CRobotMain::RetPersoAngle() +{ + return m_dialog->RetPersoAngle(); +} + + +// Change le mode de pause. + +void CRobotMain::ChangePause(BOOL bPause) +{ + m_bPause = bPause; + m_engine->SetPause(m_bPause); + + m_sound->MuteAll(m_bPause); + CreateShortcuts(); + if ( m_bPause ) HiliteClear(); +} + + +// Change la vitesse du jeu. + +void CRobotMain::SetSpeed(float speed) +{ + CButton* pb; + char text[10]; + + m_engine->SetSpeed(speed); + + pb = (CButton*)m_interface->SearchControl(EVENT_SPEED); + if ( pb != 0 ) + { + if ( speed == 1.0f ) + { + pb->ClearState(STATE_VISIBLE); + } + else + { + sprintf(text, "x%.1f", speed); + pb->SetName(text); + pb->SetState(STATE_VISIBLE); + } + } +} + +float CRobotMain::RetSpeed() +{ + return m_engine->RetSpeed(); +} + + +// Crée l'interface des raccourcis aux unités. + +BOOL CRobotMain::CreateShortcuts() +{ + if ( m_phase != PHASE_SIMUL ) return FALSE; + if ( !m_bShortCut ) return FALSE; + return m_short->CreateShortcuts(); +} + +// Met à jour la carte. + +void CRobotMain::UpdateMap() +{ + m_map->UpdateMap(); +} + +// Indique si la mini-carte est visible. + +BOOL CRobotMain::RetShowMap() +{ + return m_map->RetShowMap() && m_bMapShow; +} + + +// Gestion du mode de blocage pendant les films. + +void CRobotMain::SetMovieLock(BOOL bLock) +{ + m_bMovieLock = bLock; + m_engine->SetMovieLock(m_bMovieLock); + + CreateShortcuts(); + m_map->ShowMap(!m_bMovieLock && m_bMapShow); + if ( m_bMovieLock ) HiliteClear(); + m_engine->SetMouseHide(m_bMovieLock); +} + +BOOL CRobotMain::RetMovieLock() +{ + return m_bMovieLock; +} + +BOOL CRobotMain::RetInfoLock() +{ + return ( m_displayInfo != 0 ); // info en cours ? +} + +// Gestion du blocage de l'appel du SatCom. + +void CRobotMain::SetSatComLock(BOOL bLock) +{ + m_bSatComLock = bLock; +} + +BOOL CRobotMain::RetSatComLock() +{ + return m_bSatComLock; +} + +// Gestion du mode de blocage pendant l'édition. + +void CRobotMain::SetEditLock(BOOL bLock, BOOL bEdit) +{ + m_bEditLock = bLock; + + CreateShortcuts(); + + // N'enlève pas la carte si elle contient une image fixe. + if ( !bLock || !m_map->RetFixImage() ) + { + m_map->ShowMap(!m_bEditLock && m_bMapShow); + } + + m_displayText->HideText(bLock); + m_engine->FlushPressKey(); + + if ( m_bEditLock ) + { + HiliteClear(); + } + else + { + m_bEditFull = FALSE; + } +} + +BOOL CRobotMain::RetEditLock() +{ + return m_bEditLock; +} + +// Gestion du mode plein écran pendant l'édition. + +void CRobotMain::SetEditFull(BOOL bFull) +{ + m_bEditFull = bFull; +} + +BOOL CRobotMain::RetEditFull() +{ + return m_bEditFull; +} + + +BOOL CRobotMain::RetFreePhoto() +{ + return m_bFreePhoto; +} + + +// Indique si la souris vise un objet ami, sur lequel il ne faut +// pas tirer. + +void CRobotMain::SetFriendAim(BOOL bFriend) +{ + m_bFriendAim = bFriend; +} + +BOOL CRobotMain::RetFriendAim() +{ + return m_bFriendAim; +} + + +// Gestion de la précision du dessin au sol. + +void CRobotMain::SetTracePrecision(float factor) +{ + m_engine->SetTracePrecision(factor); +} + +float CRobotMain::RetTracePrecision() +{ + return m_engine->RetTracePrecision(); +} + + +// Débute la musique d'une mission. + +void CRobotMain::StartMusic() +{ + if ( m_audioTrack != 0 ) + { + m_sound->StopMusic(); + m_sound->PlayMusic(m_audioTrack, m_bAudioRepeat); + } +} + +// Enlève hilite et tooltip. + +void CRobotMain::ClearInterface() +{ + HiliteClear(); // enlève la mise en évidence + m_tooltipName[0] = 0; // enlève vraiment le tooltip +} + + diff --git a/src/robotmain.h b/src/robotmain.h new file mode 100644 index 00000000..799a0b78 --- /dev/null +++ b/src/robotmain.h @@ -0,0 +1,449 @@ +// robotmain.h + +#ifndef _ROBOTMAIN_H_ +#define _ROBOTMAIN_H_ + + + +enum Phase +{ + PHASE_INIT, + PHASE_TERM, + PHASE_NAME, + PHASE_PERSO, + PHASE_TRAINER, + PHASE_DEFI, + PHASE_MISSION, + PHASE_FREE, + PHASE_TEEN, + PHASE_USER, + PHASE_PROTO, + PHASE_LOADING, + PHASE_SIMUL, + PHASE_MODEL, + PHASE_SETUPd, + PHASE_SETUPg, + PHASE_SETUPp, + PHASE_SETUPc, + PHASE_SETUPs, + PHASE_SETUPds, + PHASE_SETUPgs, + PHASE_SETUPps, + PHASE_SETUPcs, + PHASE_SETUPss, + PHASE_WRITE, + PHASE_READ, + PHASE_WRITEs, + PHASE_READs, + PHASE_WIN, + PHASE_LOST, + PHASE_WELCOME1, + PHASE_WELCOME2, + PHASE_WELCOME3, + PHASE_GENERIC, +}; + + +class CInstanceManager; +class CMainMovie; +class CMainDialog; +class CMainShort; +class CMainMap; +class CEvent; +class CD3DEngine; +class CLight; +class CParticule; +class CWater; +class CCloud; +class CBlitz; +class CPlanet; +class CTerrain; +class CObject; +class CModel; +class CCamera; +class CInterface; +class CWindow; +class CControl; +class CDisplayText; +class CDisplayInfo; +class CSound; + +enum ObjectType; +enum CameraType; +enum MainMovieType; +enum ParticuleType; + + +typedef struct +{ + D3DVECTOR pos; + float dist; + ObjectType type; + int min; // gagné si > + int max; // gagné si < + int lost; // perdu si <= + BOOL bImmediat; + char message[100]; +} +EndTake; + + +#define MAXNEWSCRIPTNAME 20 + +typedef struct +{ + BOOL bUsed; + ObjectType type; + char name[40]; +} +NewScriptName; + + +#define MAXSHOWLIMIT 5 +#define MAXSHOWPARTI 200 +#define SHOWLIMITTIME 20.0f + +typedef struct +{ + BOOL bUsed; + D3DVECTOR pos; + float radius; + int total; + int parti[MAXSHOWPARTI]; + CObject* link; + float duration; + float time; +} +ShowLimit; + + +#define SATCOM_HUSTON 0 +#define SATCOM_SAT 1 +#define SATCOM_OBJECT 2 +#define SATCOM_LOADING 3 +#define SATCOM_PROG 4 +#define SATCOM_SOLUCE 5 +#define SATCOM_MAX 6 + + + +class CRobotMain +{ +public: + CRobotMain(CInstanceManager* iMan); + ~CRobotMain(); + + void CreateIni(); + + void ChangePhase(Phase phase); + BOOL EventProcess(const Event &event); + + BOOL CreateShortcuts(); + void ScenePerso(); + + void SetMovieLock(BOOL bLock); + BOOL RetMovieLock(); + BOOL RetInfoLock(); + void SetSatComLock(BOOL bLock); + BOOL RetSatComLock(); + void SetEditLock(BOOL bLock, BOOL bEdit); + BOOL RetEditLock(); + void SetEditFull(BOOL bFull); + BOOL RetEditFull(); + BOOL RetFreePhoto(); + void SetFriendAim(BOOL bFriend); + BOOL RetFriendAim(); + + void SetTracePrecision(float factor); + float RetTracePrecision(); + + void ChangePause(BOOL bPause); + + void SetSpeed(float speed); + float RetSpeed(); + + void UpdateShortcuts(); + void SelectHuman(); + CObject* SearchHuman(); + CObject* SearchToto(); + CObject* SearchNearest(D3DVECTOR pos, CObject* pExclu); + BOOL SelectObject(CObject* pObj, BOOL bDisplayError=TRUE); + CObject* RetSelectObject(); + CObject* DeselectAll(); + BOOL DeleteObject(); + + void ResetObject(); + void ResetCreate(); + Error CheckEndMission(BOOL bFrame); + void CheckEndMessage(char *message); + int RetObligatoryToken(); + char* RetObligatoryToken(int i); + int IsObligatoryToken(char *token); + BOOL IsProhibitedToken(char *token); + void UpdateMap(); + BOOL RetShowMap(); + + MainMovieType RetMainMovie(); + + void FlushDisplayInfo(); + void StartDisplayInfo(int index, BOOL bMovie); + void StartDisplayInfo(char *filename, int index); + void StopDisplayInfo(); + char* RetDisplayInfoName(int index); + int RetDisplayInfoPosition(int index); + void SetDisplayInfoPosition(int index, int pos); + + void StartSuspend(); + void StopSuspend(); + + float RetGameTime(); + + void SetFontSize(float size); + float RetFontSize(); + void SetWindowPos(FPOINT pos); + FPOINT RetWindowPos(); + void SetWindowDim(FPOINT dim); + FPOINT RetWindowDim(); + + void SetIOPublic(BOOL bMode); + BOOL RetIOPublic(); + void SetIOPos(FPOINT pos); + FPOINT RetIOPos(); + void SetIODim(FPOINT dim); + FPOINT RetIODim(); + + char* RetTitle(); + char* RetResume(); + char* RetScriptName(); + char* RetScriptFile(); + BOOL RetTrainerPilot(); + BOOL RetFixScene(); + BOOL RetGlint(); + BOOL RetSoluce4(); + BOOL RetMovies(); + BOOL RetNiceReset(); + BOOL RetHimselfDamage(); + BOOL RetShowSoluce(); + BOOL RetSceneSoluce(); + BOOL RetShowAll(); + BOOL RetCheatRadar(); + char* RetSavegameDir(); + char* RetPublicDir(); + char* RetFilesDir(); + + void SetGamerName(char *name); + char* RetGamerName(); + int RetGamerFace(); + int RetGamerGlasses(); + BOOL RetGamerOnlyHead(); + float RetPersoAngle(); + + void StartMusic(); + void ClearInterface(); + void ChangeColor(); + + float SearchNearestObject(D3DVECTOR center, CObject *exclu); + BOOL FreeSpace(D3DVECTOR ¢er, float minRadius, float maxRadius, float space, CObject *exclu); + float RetFlatZoneRadius(D3DVECTOR center, float maxRadius, CObject *exclu); + void HideDropZone(CObject* metal); + void ShowDropZone(CObject* metal, CObject* truck); + void FlushShowLimit(int i); + void SetShowLimit(int i, ParticuleType parti, CObject *pObj, D3DVECTOR pos, float radius, float duration=SHOWLIMITTIME); + void AdjustShowLimit(int i, D3DVECTOR pos); + void StartShowLimit(); + void FrameShowLimit(float rTime); + + void CompileScript(BOOL bSoluce); + void LoadOneScript(CObject *pObj, int &nbError); + void LoadFileScript(CObject *pObj, char* filename, int objRank, int &nbError); + void SaveAllScript(); + void SaveOneScript(CObject *pObj); + void SaveFileScript(CObject *pObj, char* filename, int objRank); + BOOL SaveFileStack(CObject *pObj, FILE *file, int objRank); + BOOL ReadFileStack(CObject *pObj, FILE *file, int objRank); + + BOOL FlushNewScriptName(); + BOOL AddNewScriptName(ObjectType type, char *name); + char* RetNewScriptName(ObjectType type, int rank); + + void WriteFreeParam(); + void ReadFreeParam(); + + BOOL IsBusy(); + BOOL IOWriteScene(char *filename, char *filecbot, char *info); + CObject* IOReadScene(char *filename, char *filecbot); + void IOWriteObject(FILE *file, CObject* pObj, char *cmd); + CObject* IOReadObject(char *line, char* filename, int objRank); + + int CreateSpot(D3DVECTOR pos, D3DCOLORVALUE color); + +protected: + BOOL EventFrame(const Event &event); + BOOL EventObject(const Event &event); + void InitEye(); + + void Convert(); + void CreateScene(BOOL bSoluce, BOOL bFixScene, BOOL bResetObject); + + void CreateModel(); + D3DVECTOR LookatPoint( D3DVECTOR eye, float angleH, float angleV, float length ); + CObject* CreateObject(D3DVECTOR pos, float angle, float zoom, float height, ObjectType type, float power=1.0f, BOOL bTrainer=FALSE, BOOL bToy=FALSE, int option=0); + int CreateLight(D3DVECTOR direction, D3DCOLORVALUE color); + void HiliteClear(); + void HiliteObject(FPOINT pos); + void HiliteFrame(float rTime); + void CreateTooltip(FPOINT pos, char* text); + void ClearTooltip(); + CObject* DetectObject(FPOINT pos); + void ChangeCamera(); + void RemoteCamera(float pan, float zoom, float rTime); + void KeyCamera(EventMsg event, long param); + void AbortMovie(); + BOOL IsSelectable(CObject* pObj); + void SelectOneObject(CObject* pObj, BOOL bDisplayError=TRUE); + void HelpObject(); + BOOL DeselectObject(); + void DeleteAllObjects(); + void UpdateInfoText(); + CObject* SearchObject(ObjectType type); + CObject* RetSelect(); + void StartDisplayVisit(EventMsg event); + void FrameVisit(float rTime); + void StopDisplayVisit(); + void ExecuteCmd(char *cmd); + BOOL TestGadgetQuantity(int rank); + +protected: + CInstanceManager* m_iMan; + CMainMovie* m_movie; + CMainDialog* m_dialog; + CMainShort* m_short; + CMainMap* m_map; + CEvent* m_event; + CD3DEngine* m_engine; + CParticule* m_particule; + CWater* m_water; + CCloud* m_cloud; + CBlitz* m_blitz; + CPlanet* m_planet; + CLight* m_light; + CTerrain* m_terrain; + CModel* m_model; + CInterface* m_interface; + CCamera* m_camera; + CDisplayText* m_displayText; + CDisplayInfo* m_displayInfo; + CSound* m_sound; + + float m_time; + float m_gameTime; + float m_checkEndTime; + float m_winDelay; + float m_lostDelay; + BOOL m_bFixScene; // scène fixe, sans interraction + BOOL m_bBase; // OBJECT_BASE existe dans mission + FPOINT m_lastMousePos; + CObject* m_selectObject; + + Phase m_phase; + int m_cameraRank; + D3DCOLORVALUE m_color; + BOOL m_bFreePhoto; + BOOL m_bCmdEdit; + BOOL m_bShowPos; + BOOL m_bSelectInsect; + BOOL m_bShowSoluce; + BOOL m_bShowAll; + BOOL m_bCheatRadar; + BOOL m_bAudioRepeat; + BOOL m_bShortCut; + int m_audioTrack; + int m_delayWriteMessage; + int m_movieInfoIndex; + + BOOL m_bImmediatSatCom; // SatCom tout de suite ? + BOOL m_bBeginSatCom; // message SatCom affiché ? + BOOL m_bMovieLock; // film en cours ? + BOOL m_bSatComLock; // appel du SatCom possible ? + BOOL m_bEditLock; // édition en cours ? + BOOL m_bEditFull; // édition en plein écran ? + BOOL m_bPause; // simulation en pause + BOOL m_bHilite; + BOOL m_bTrainerPilot; // télécommande trainer ? + BOOL m_bSuspend; + BOOL m_bFriendAim; + BOOL m_bResetCreate; + BOOL m_bMapShow; + BOOL m_bMapImage; + char m_mapFilename[100]; + + FPOINT m_tooltipPos; + char m_tooltipName[100]; + float m_tooltipTime; + + char m_infoFilename[SATCOM_MAX][100]; // noms des fichiers texte + CObject* m_infoObject; + int m_infoIndex; + int m_infoPos[SATCOM_MAX]; + int m_infoUsed; + + char m_title[100]; + char m_resume[500]; + char m_scriptName[100]; + char m_scriptFile[100]; + int m_endingWinRank; + int m_endingLostRank; + BOOL m_bWinTerminate; + + float m_fontSize; + FPOINT m_windowPos; + FPOINT m_windowDim; + + BOOL m_IOPublic; + FPOINT m_IOPos; + FPOINT m_IODim; + + NewScriptName m_newScriptName[MAXNEWSCRIPTNAME]; + + float m_cameraPan; + float m_cameraZoom; + + EventMsg m_visitLast; + CObject* m_visitObject; + CObject* m_visitArrow; + float m_visitTime; + float m_visitParticule; + D3DVECTOR m_visitPos; + D3DVECTOR m_visitPosArrow; + + int m_endTakeTotal; + EndTake m_endTake[10]; + long m_endTakeResearch; + float m_endTakeWinDelay; + float m_endTakeLostDelay; + + int m_obligatoryTotal; + char m_obligatoryToken[100][20]; + int m_prohibitedTotal; + char m_prohibitedToken[100][20]; + + char m_gamerName[100]; + + long m_freeBuild; // bâtiments constructibles + long m_freeResearch; // recherches effectuées + + ShowLimit m_showLimit[MAXSHOWLIMIT]; + + D3DCOLORVALUE m_colorRefBot; + D3DCOLORVALUE m_colorNewBot; + D3DCOLORVALUE m_colorRefAlien; + D3DCOLORVALUE m_colorNewAlien; + D3DCOLORVALUE m_colorRefGreen; + D3DCOLORVALUE m_colorNewGreen; + D3DCOLORVALUE m_colorRefWater; + D3DCOLORVALUE m_colorNewWater; + float m_colorShiftWater; +}; + + +#endif //_ROBOTMAIN_H_ diff --git a/src/script.cpp b/src/script.cpp new file mode 100644 index 00000000..16ff612c --- /dev/null +++ b/src/script.cpp @@ -0,0 +1,3762 @@ +// script.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "cbot/cbotdll.h" +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "global.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "math3d.h" +#include "robotmain.h" +#include "terrain.h" +#include "water.h" +#include "object.h" +#include "physics.h" +#include "interface.h" +#include "edit.h" +#include "list.h" +#include "text.h" +#include "displaytext.h" +#include "taskmanager.h" +#include "task.h" +#include "taskmanip.h" +#include "taskgoto.h" +#include "taskshield.h" +#include "cbottoken.h" +#include "script.h" + + + +#define CBOT_IPF 100 // CBOT: number of instructions / frame + +#define ERM_CONT 0 // si erreur -> continue +#define ERM_STOP 1 // si erreur -> stoppe + + + + +// Compilation d'une procédure sans ancun paramètre. + +CBotTypResult cNull(CBotVar* &var, void* user) +{ + if ( var != 0 ) return CBotErrOverParam; + return CBotTypResult(CBotTypFloat); +} + +// Compilation d'une procédure avec un seul nombre réel. + +CBotTypResult cOneFloat(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypFloat); +} + +// Compilation d'une procédure avec deux nombres réels. + +CBotTypResult cTwoFloat(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypFloat); +} + +// Compilation d'une procédure avec un "point". + +CBotTypResult cPoint(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + + if ( var->GivType() <= CBotTypDouble ) + { + var = var->GivNext(); + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); +//? if ( var == 0 ) return CBotTypResult(CBotErrLowParam); +//? if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); +//? var = var->GivNext(); + return CBotTypResult(0); + } + + if ( var->GivType() == CBotTypClass ) + { + if ( !var->IsElemOfClass("point") ) return CBotTypResult(CBotErrBadParam); + var = var->GivNext(); + return CBotTypResult(0); + } + + return CBotTypResult(CBotErrBadParam); +} + +// Compilation d'une procédure avec un seul "point". + +CBotTypResult cOnePoint(CBotVar* &var, void* user) +{ + CBotTypResult ret; + + ret = cPoint(var, user); + if ( ret.GivType() != 0 ) return ret; + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypFloat); +} + +// Compilation d'une procédure avec une seule chaîne. + +CBotTypResult cString(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() != CBotTypString && + var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypFloat); +} + + +// Cherche une valeur dans un tableau d'entiers. + +BOOL FindList(CBotVar* array, int type) +{ + while ( array != 0 ) + { + if ( type == array->GivValInt() ) return TRUE; + array = array->GivNext(); + } + return FALSE; +} + + +// Donne un paramètre de type "point". + +BOOL GetPoint(CBotVar* &var, int& exception, D3DVECTOR& pos) +{ + CBotVar *pX, *pY, *pZ; + + if ( var->GivType() <= CBotTypDouble ) + { + pos.x = var->GivValFloat()*g_unit; + var = var->GivNext(); + + pos.z = var->GivValFloat()*g_unit; + var = var->GivNext(); + + pos.y = 0.0f; + } + else + { + pX = var->GivItem("x"); + if ( pX == NULL ) + { + exception = CBotErrUndefItem; return TRUE; + } + pos.x = pX->GivValFloat()*g_unit; + + pY = var->GivItem("y"); + if ( pY == NULL ) + { + exception = CBotErrUndefItem; return TRUE; + } + pos.z = pY->GivValFloat()*g_unit; // attention y -> z ! + + pZ = var->GivItem("z"); + if ( pZ == NULL ) + { + exception = CBotErrUndefItem; return TRUE; + } + pos.y = pZ->GivValFloat()*g_unit; // attention z -> y ! + + var = var->GivNext(); + } + return TRUE; +} + + +// Instruction "sin(degrés)". + +BOOL rSin(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + float value; + + value = var->GivValFloat(); + result->SetValFloat(sinf(value*PI/180.0f)); + return TRUE; +} + +// Instruction "cos(degrés)". + +BOOL rCos(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + float value; + + value = var->GivValFloat(); + result->SetValFloat(cosf(value*PI/180.0f)); + return TRUE; +} + +// Instruction "tan(degrés)". + +BOOL rTan(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + float value; + + value = var->GivValFloat(); + result->SetValFloat(tanf(value*PI/180.0f)); + return TRUE; +} + +// Instruction "asin(valeur)". + +BOOL raSin(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + float value; + + value = var->GivValFloat(); + result->SetValFloat(asinf(value)*180.0f/PI); + return TRUE; +} + +// Instruction "acos(valeur)". + +BOOL raCos(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + float value; + + value = var->GivValFloat(); + result->SetValFloat(acosf(value)*180.0f/PI); + return TRUE; +} + +// Instruction "atan(valeur)". + +BOOL raTan(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + float value; + + value = var->GivValFloat(); + result->SetValFloat(atanf(value)*180.0f/PI); + return TRUE; +} + +// Instruction "sqrt(valeur)". + +BOOL rSqrt(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + float value; + + value = var->GivValFloat(); + result->SetValFloat(sqrtf(value)); + return TRUE; +} + +// Instruction "pow(x, y)". + +BOOL rPow(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + float x, y; + + x = var->GivValFloat(); + var = var->GivNext(); + y = var->GivValFloat(); + result->SetValFloat(powf(x, y)); + return TRUE; +} + +// Instruction "rand()". + +BOOL rRand(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + result->SetValFloat(Rand()); + return TRUE; +} + +// Instruction "abs()". + +BOOL rAbs(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + float value; + + value = var->GivValFloat(); + result->SetValFloat(Abs(value)); + return TRUE; +} + + +// Compilation de l'instruction "retobject(rank)". + +CBotTypResult cRetObject(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + + return CBotTypResult(CBotTypPointer, "object"); +} + +// Instruction "retobject(rank)". + +BOOL rRetObject(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pObj; + int rank; + + rank = var->GivValInt(); + + pObj = (CObject*)script->m_iMan->SearchInstance(CLASS_OBJECT, rank); + if ( pObj == 0 ) + { + result->SetPointer(0); + } + else + { + result->SetPointer(pObj->RetBotVar()); + } + return TRUE; +} + + +// Compilation de l'instruction "search(type, pos)". + +CBotTypResult cSearch(CBotVar* &var, void* user) +{ + CBotVar* array; + CBotTypResult ret; + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() == CBotTypArrayPointer ) + { + array = var->GivItemList(); + if ( array == 0 ) return CBotTypResult(CBotTypPointer); + if ( array->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + } + else if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + if ( var != 0 ) + { + ret = cPoint(var, user); + if ( ret.GivType() != 0 ) return ret; + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + } + + return CBotTypResult(CBotTypPointer, "object"); +} + +// Instruction "search(type, pos)". + +BOOL rSearch(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject *pObj, *pBest; + CBotVar* array; + D3DVECTOR pos, oPos; + BOOL bNearest = FALSE; + BOOL bArray; + float min, dist; + int type, oType, i; + + if ( var->GivType() == CBotTypArrayPointer ) + { + array = var->GivItemList(); + bArray = TRUE; + } + else + { + type = var->GivValInt(); + bArray = FALSE; + } + var = var->GivNext(); + if ( var != 0 ) + { + if ( !GetPoint(var, exception, pos) ) return TRUE; + bNearest = TRUE; + } + + min = 100000.0f; + pBest = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)script->m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + if ( !pObj->RetActif() ) continue; + + oType = pObj->RetType(); + if ( oType == OBJECT_TOTO ) continue; + + if ( oType == OBJECT_RUINmobilew2 || + oType == OBJECT_RUINmobilet1 || + oType == OBJECT_RUINmobilet2 || + oType == OBJECT_RUINmobiler1 || + oType == OBJECT_RUINmobiler2 ) + { + oType = OBJECT_RUINmobilew1; // n'importe quelle ruine + } + + if ( oType == OBJECT_SCRAP2 || + oType == OBJECT_SCRAP3 || + oType == OBJECT_SCRAP4 || + oType == OBJECT_SCRAP5 ) // déchet ? + { + oType = OBJECT_SCRAP1; // n'importe quel déchet + } + + if ( oType == OBJECT_BARRIER2 || + oType == OBJECT_BARRIER3 ) // barrière ? + { + oType = OBJECT_BARRIER1; // n'importe quelle barrière + } + + if ( bArray ) + { + if ( !FindList(array, oType) ) continue; + } + else + { + if ( type != oType && type != OBJECT_NULL ) continue; + } + + if ( bNearest ) + { + oPos = pObj->RetPosition(0); + dist = Length2d(pos, oPos); + if ( dist < min ) + { + min = dist; + pBest = pObj; + } + } + else + { + pBest = pObj; + break; + } + } + + if ( pBest == 0 ) + { + result->SetPointer(0); + } + else + { + result->SetPointer(pBest->RetBotVar()); + } + return TRUE; +} + + +// Compilation de l'instruction "radar(type, angle, focus, min, max, sens)". + +CBotTypResult cRadar(CBotVar* &var, void* user) +{ + CBotVar* array; + + if ( var == 0 ) return CBotTypResult(CBotTypPointer, "object"); + if ( var->GivType() == CBotTypArrayPointer ) + { + array = var->GivItemList(); + if ( array == 0 ) return CBotTypResult(CBotTypPointer, "object"); + if ( array->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // type + } + else if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // type + var = var->GivNext(); + if ( var == 0 ) return CBotTypResult(CBotTypPointer, "object"); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // angle + var = var->GivNext(); + if ( var == 0 ) return CBotTypResult(CBotTypPointer, "object"); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // focus + var = var->GivNext(); + if ( var == 0 ) return CBotTypResult(CBotTypPointer, "object"); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // min + var = var->GivNext(); + if ( var == 0 ) return CBotTypResult(CBotTypPointer, "object"); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // max + var = var->GivNext(); + if ( var == 0 ) return CBotTypResult(CBotTypPointer, "object"); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // sens + var = var->GivNext(); + if ( var == 0 ) return CBotTypResult(CBotTypPointer, "object"); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); // filtre + var = var->GivNext(); + if ( var == 0 ) return CBotTypResult(CBotTypPointer, "object"); + return CBotTypResult(CBotErrOverParam); +} + +// Instruction "radar(type, angle, focus, min, max, sens, filter)". + +BOOL rRadar(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + CObject *pObj, *pBest; + CPhysics* physics; + CBotVar* array; + D3DVECTOR iPos, oPos; + RadarFilter filter; + float best, minDist, maxDist, sens, iAngle, angle, focus, d, a; + int type, oType, i; + BOOL bArray; + + type = OBJECT_NULL; + angle = 0.0f; + focus = PI*2.0f; + minDist = 0.0f*g_unit; + maxDist = 1000.0f*g_unit; + sens = 1.0f; + filter = FILTER_NONE; + + if ( var != 0 ) + { + if ( var->GivType() == CBotTypArrayPointer ) + { + array = var->GivItemList(); + bArray = TRUE; + } + else + { + type = var->GivValInt(); + bArray = FALSE; + } + + var = var->GivNext(); + if ( var != 0 ) + { + angle = -var->GivValFloat()*PI/180.0f; + + var = var->GivNext(); + if ( var != 0 ) + { + focus = var->GivValFloat()*PI/180.0f; + + var = var->GivNext(); + if ( var != 0 ) + { + minDist = var->GivValFloat()*g_unit; + + var = var->GivNext(); + if ( var != 0 ) + { + maxDist = var->GivValFloat()*g_unit; + + var = var->GivNext(); + if ( var != 0 ) + { + sens = var->GivValFloat(); + + var = var->GivNext(); + if ( var != 0 ) + { + filter = (RadarFilter)var->GivValInt(); + } + } + } + } + } + } + } + + iPos = pThis->RetPosition(0); + iAngle = pThis->RetAngleY(0)+angle; + iAngle = NormAngle(iAngle); // 0..2*PI + + if ( sens >= 0.0f ) best = 100000.0f; + else best = 0.0f; + pBest = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)script->m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + if ( pObj == pThis ) continue; + + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + if ( !pObj->RetActif() ) continue; + if ( pObj->RetProxyActivate() ) continue; + + oType = pObj->RetType(); + if ( oType == OBJECT_TOTO ) continue; + + if ( oType == OBJECT_RUINmobilew2 || + oType == OBJECT_RUINmobilet1 || + oType == OBJECT_RUINmobilet2 || + oType == OBJECT_RUINmobiler1 || + oType == OBJECT_RUINmobiler2 ) + { + oType = OBJECT_RUINmobilew1; // n'importe quelle ruine + } + + if ( oType == OBJECT_SCRAP2 || + oType == OBJECT_SCRAP3 || + oType == OBJECT_SCRAP4 || + oType == OBJECT_SCRAP5 ) // déchet ? + { + oType = OBJECT_SCRAP1; // n'importe quel déchet + } + + if ( oType == OBJECT_BARRIER2 || + oType == OBJECT_BARRIER3 ) // barrière ? + { + oType = OBJECT_BARRIER1; // n'importe quelle barrière + } + + if ( filter == FILTER_ONLYLANDING ) + { + physics = pObj->RetPhysics(); + if ( physics != 0 && !physics->RetLand() ) continue; + } + if ( filter == FILTER_ONLYFLYING ) + { + physics = pObj->RetPhysics(); + if ( physics != 0 && physics->RetLand() ) continue; + } + + if ( bArray ) + { + if ( !FindList(array, oType) ) continue; + } + else + { + if ( type != oType && type != OBJECT_NULL ) continue; + } + + oPos = pObj->RetPosition(0); + d = Length2d(iPos, oPos); + if ( d < minDist || d > maxDist ) continue; // trop proche ou trop loin ? + + if ( focus >= PI*2.0f ) + { + if ( (sens >= 0.0f && d < best) || + (sens < 0.0f && d > best) ) + { + best = d; + pBest = pObj; + } + continue; + } + + a = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + if ( TestAngle(a, iAngle-focus/2.0f, iAngle+focus/2.0f) ) + { + if ( (sens >= 0.0f && d < best) || + (sens < 0.0f && d > best) ) + { + best = d; + pBest = pObj; + } + } + } + + if ( pBest == 0 ) + { + result->SetPointer(0); + } + else + { + result->SetPointer(pBest->RetBotVar()); + } + return TRUE; +} + + +// Suivi d'une tâche. + +BOOL Process(CScript* script, CBotVar* result, int &exception) +{ + Error err; + + err = script->m_primaryTask->IsEnded(); + if ( err != ERR_CONTINUE ) // tâche terminée ? + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + + script->m_bContinue = FALSE; + + if ( err == ERR_STOP ) err = ERR_OK; + result->SetValInt(err); // indique l'erreur ou ok + if ( err != ERR_OK && script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; // c'est fini + } + + script->m_primaryTask->EventProcess(script->m_event); + script->m_bContinue = TRUE; + return FALSE; // pas fini +} + + +// Compilation de l'instruction "detect(type)". + +CBotTypResult cDetect(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypBoolean); +} + +// Instruction "detect(type)". + +BOOL rDetect(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + CObject *pObj, *pGoal, *pBest; + CPhysics* physics; + CBotVar* array; + D3DVECTOR iPos, oPos; + RadarFilter filter; + float bGoal, best, minDist, maxDist, sens, iAngle, angle, focus, d, a; + int type, oType, i; + BOOL bArray; + Error err; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + type = OBJECT_NULL; + angle = 0.0f; + focus = 45.0f*PI/180.0f; + minDist = 0.0f*g_unit; + maxDist = 20.0f*g_unit; + sens = 1.0f; + filter = FILTER_NONE; + + if ( var != 0 ) + { + if ( var->GivType() == CBotTypArrayPointer ) + { + array = var->GivItemList(); + bArray = TRUE; + } + else + { + type = var->GivValInt(); + bArray = FALSE; + } + } + + iPos = pThis->RetPosition(0); + iAngle = pThis->RetAngleY(0)+angle; + iAngle = NormAngle(iAngle); // 0..2*PI + + bGoal = 100000.0f; + pGoal = 0; + if ( sens >= 0.0f ) best = 100000.0f; + else best = 0.0f; + pBest = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)script->m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + if ( pObj == pThis ) continue; + + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + if ( !pObj->RetActif() ) continue; + if ( pObj->RetProxyActivate() ) continue; + + oType = pObj->RetType(); + if ( oType == OBJECT_TOTO ) continue; + + if ( oType == OBJECT_RUINmobilew2 || + oType == OBJECT_RUINmobilet1 || + oType == OBJECT_RUINmobilet2 || + oType == OBJECT_RUINmobiler1 || + oType == OBJECT_RUINmobiler2 ) + { + oType = OBJECT_RUINmobilew1; // n'importe quelle ruine + } + + if ( oType == OBJECT_SCRAP2 || + oType == OBJECT_SCRAP3 || + oType == OBJECT_SCRAP4 || + oType == OBJECT_SCRAP5 ) // déchet ? + { + oType = OBJECT_SCRAP1; // n'importe quel déchet + } + + if ( oType == OBJECT_BARRIER2 || + oType == OBJECT_BARRIER3 ) // barrière ? + { + oType = OBJECT_BARRIER1; // n'importe quelle barrière + } + + if ( filter == FILTER_ONLYLANDING ) + { + physics = pObj->RetPhysics(); + if ( physics != 0 && !physics->RetLand() ) continue; + } + if ( filter == FILTER_ONLYFLYING ) + { + physics = pObj->RetPhysics(); + if ( physics != 0 && physics->RetLand() ) continue; + } + + if ( bArray ) + { + if ( !FindList(array, oType) ) continue; + } + else + { + if ( type != oType && type != OBJECT_NULL ) continue; + } + + oPos = pObj->RetPosition(0); + d = Length2d(iPos, oPos); + a = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + + if ( d < bGoal && + TestAngle(a, iAngle-(5.0f*PI/180.0f)/2.0f, iAngle+(5.0f*PI/180.0f)/2.0f) ) + { + bGoal = d; + pGoal = pObj; + } + + if ( d < minDist || d > maxDist ) continue; // trop proche ou trop loin ? + + if ( focus >= PI*2.0f ) + { + if ( (sens >= 0.0f && d < best) || + (sens < 0.0f && d > best) ) + { + best = d; + pBest = pObj; + } + continue; + } + + if ( TestAngle(a, iAngle-focus/2.0f, iAngle+focus/2.0f) ) + { + if ( (sens >= 0.0f && d < best) || + (sens < 0.0f && d > best) ) + { + best = d; + pBest = pObj; + } + } + } + + pThis->StartDetectEffect(pGoal, pBest!=0); + + if ( pBest == 0 ) + { + script->m_returnValue = 0.0f; + } + else + { + script->m_returnValue = 1.0f; + } + + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + err = script->m_primaryTask->StartTaskWait(0.3f); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + if ( !Process(script, result, exception) ) return FALSE; // pas terminé + result->SetValFloat(script->m_returnValue); + return TRUE; +} + + +// Compilation de l'instruction "direction(pos)". + +CBotTypResult cDirection(CBotVar* &var, void* user) +{ + CBotTypResult ret; + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + ret = cPoint(var, user); + if ( ret.GivType() != 0 ) return ret; + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + + return CBotTypResult(CBotTypFloat); +} + +// Instruction "direction(pos)". + +BOOL rDirection(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + D3DVECTOR iPos, oPos; + float a, g; + + if ( !GetPoint(var, exception, oPos) ) return TRUE; + + iPos = pThis->RetPosition(0); + + a = pThis->RetAngleY(0); + g = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + + result->SetValFloat(-Direction(a, g)*180.0f/PI); + return TRUE; +} + + +// Compilation de l'instruction "produce(pos, angle, type, scriptName)". + +CBotTypResult cProduce(CBotVar* &var, void* user) +{ + CBotTypResult ret; + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + ret = cPoint(var, user); + if ( ret.GivType() != 0 ) return ret; + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() != CBotTypString ) return CBotTypResult(CBotErrBadString); + var = var->GivNext(); + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + + return CBotTypResult(CBotTypFloat); +} + +// Instruction "produce(pos, angle, type, scriptName)". + +BOOL rProduce(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* object; + CBotString cbs; + const char* name; + D3DVECTOR pos; + float angle; + ObjectType type; + + if ( !GetPoint(var, exception, pos) ) return TRUE; + + angle = var->GivValFloat()*PI/180.0f; + var = var->GivNext(); + + type = (ObjectType)var->GivValInt(); + var = var->GivNext(); + + cbs = var->GivValString(); + name = cbs; + + if ( type == OBJECT_FRET || + type == OBJECT_STONE || + type == OBJECT_URANIUM || + type == OBJECT_METAL || + type == OBJECT_POWER || + type == OBJECT_ATOMIC || + type == OBJECT_BULLET || + type == OBJECT_BBOX || + type == OBJECT_KEYa || + type == OBJECT_KEYb || + type == OBJECT_KEYc || + type == OBJECT_KEYd || + type == OBJECT_TNT || + type == OBJECT_SCRAP1 || + type == OBJECT_SCRAP2 || + type == OBJECT_SCRAP3 || + type == OBJECT_SCRAP4 || + type == OBJECT_SCRAP5 || + type == OBJECT_BOMB || + type == OBJECT_WAYPOINT || + type == OBJECT_SHOW || + type == OBJECT_WINFIRE ) + { + object = new CObject(script->m_iMan); + if ( !object->CreateResource(pos, angle, type) ) + { + delete object; + result->SetValInt(1); // erreur + return TRUE; + } + } + else + if ( type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM ) + { + CObject* egg; + + object = new CObject(script->m_iMan); + if ( !object->CreateInsect(pos, angle, type) ) + { + delete object; + result->SetValInt(1); // erreur + return TRUE; + } + + egg = new CObject(script->m_iMan); + if ( !egg->CreateResource(pos, angle, OBJECT_EGG, 0.0f) ) + { + delete egg; + } + } + else + { + result->SetValInt(1); // impossible + return TRUE; + } + object->SetActivity(FALSE); + object->ReadProgram(0, (char*)name); + object->RunProgram(0); + + result->SetValInt(0); // pas d'erreur + return TRUE; +} + + +// Compilation de l'instruction "distance(p1, p2)". + +CBotTypResult cDistance(CBotVar* &var, void* user) +{ + CBotTypResult ret; + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + ret = cPoint(var, user); + if ( ret.GivType() != 0 ) return ret; + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + ret = cPoint(var, user); + if ( ret.GivType() != 0 ) return ret; + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + + return CBotTypResult(CBotTypFloat); +} + +// Instruction "distance(p1, p2)". + +BOOL rDistance(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + D3DVECTOR p1, p2; + float value; + + if ( !GetPoint(var, exception, p1) ) return TRUE; + if ( !GetPoint(var, exception, p2) ) return TRUE; + + value = Length(p1, p2); + result->SetValFloat(value/g_unit); + return TRUE; +} + +// Instruction "distance2d(p1, p2)". + +BOOL rDistance2d(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + D3DVECTOR p1, p2; + float value; + + if ( !GetPoint(var, exception, p1) ) return TRUE; + if ( !GetPoint(var, exception, p2) ) return TRUE; + + value = Length2d(p1, p2); + result->SetValFloat(value/g_unit); + return TRUE; +} + + +// Compilation de l'instruction "space(center, rMin, rMax, dist)". + +CBotTypResult cSpace(CBotVar* &var, void* user) +{ + CBotTypResult ret; + + if ( var == 0 ) return CBotTypResult(CBotTypIntrinsic, "point"); + ret = cPoint(var, user); + if ( ret.GivType() != 0 ) return ret; + + if ( var == 0 ) return CBotTypResult(CBotTypIntrinsic, "point"); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypIntrinsic, "point"); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypIntrinsic, "point"); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypIntrinsic, "point"); +} + +// Instruction "space(center, rMin, rMax, dist)". + +BOOL rSpace(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + CBotVar* pSub; + D3DVECTOR center; + float rMin, rMax, dist; + + rMin = 10.0f*g_unit; + rMax = 50.0f*g_unit; + dist = 4.0f*g_unit; + + if ( var == 0 ) + { + center = pThis->RetPosition(0); + } + else + { + if ( !GetPoint(var, exception, center) ) return TRUE; + + if ( var != 0 ) + { + rMin = var->GivValFloat()*g_unit; + var = var->GivNext(); + + if ( var != 0 ) + { + rMax = var->GivValFloat()*g_unit; + var = var->GivNext(); + + if ( var != 0 ) + { + dist = var->GivValFloat()*g_unit; + var = var->GivNext(); + } + } + } + } + script->m_main->FreeSpace(center, rMin, rMax, dist, pThis); + + if ( result != 0 ) + { + pSub = result->GivItemList(); + if ( pSub != 0 ) + { + pSub->SetValFloat(center.x/g_unit); + pSub = pSub->GivNext(); // "y" + pSub->SetValFloat(center.z/g_unit); + pSub = pSub->GivNext(); // "z" + pSub->SetValFloat(center.y/g_unit); + } + } + return TRUE; +} + + +// Compilation de l'instruction "flatground(center, rMax)". + +CBotTypResult cFlatGround(CBotVar* &var, void* user) +{ + CBotTypResult ret; + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + ret = cPoint(var, user); + if ( ret.GivType() != 0 ) return ret; + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + + return CBotTypResult(CBotTypFloat); +} + +// Instruction "flatground(center, rMax)". + +BOOL rFlatGround(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + D3DVECTOR center; + float rMax, dist; + + if ( !GetPoint(var, exception, center) ) return TRUE; + rMax = var->GivValFloat()*g_unit; + var = var->GivNext(); + + dist = script->m_main->RetFlatZoneRadius(center, rMax, pThis); + result->SetValFloat(dist/g_unit); + + return TRUE; +} + + +// Instruction "wait(t)". + +BOOL rWait(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + float value; + Error err; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + value = var->GivValFloat(); + err = script->m_primaryTask->StartTaskWait(value); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); +} + +// Instruction "move(dist)". + +BOOL rMove(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + float value; + Error err; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + value = var->GivValFloat(); + err = script->m_primaryTask->StartTaskAdvance(value*g_unit); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); +} + +// Instruction "turn(angle)". + +BOOL rTurn(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + float value; + Error err; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + value = var->GivValFloat(); + err = script->m_primaryTask->StartTaskTurn(-value*PI/180.0f); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); +} + +// Compilation de l'instruction "goto(pos, altitude, crash, goal)". + +CBotTypResult cGoto(CBotVar* &var, void* user) +{ + CBotTypResult ret; + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + ret = cPoint(var, user); + if ( ret.GivType() != 0 ) return ret; + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + return CBotTypResult(CBotErrOverParam); +} + +// Instruction "goto(pos, altitude, mode)". + +BOOL rGoto(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + D3DVECTOR pos; + TaskGotoGoal goal; + TaskGotoCrash crash; + float altitude; + Error err; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + if ( !GetPoint(var, exception, pos) ) return TRUE; + + goal = TGG_DEFAULT; + crash = TGC_DEFAULT; + altitude = 0.0f*g_unit; + + if ( var != 0 ) + { + altitude = var->GivValFloat()*g_unit; + + var = var->GivNext(); + if ( var != 0 ) + { + goal = (TaskGotoGoal)var->GivValInt(); + + var = var->GivNext(); + if ( var != 0 ) + { + crash = (TaskGotoCrash)var->GivValInt(); + } + } + } + + err = script->m_primaryTask->StartTaskGoto(pos, altitude, goal, crash); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); +} + +// Instruction "find(type)". + +BOOL rFind(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + D3DVECTOR pos; + TaskGotoGoal goal; + TaskGotoCrash crash; + float altitude; + Error err; + CObject* pThis = (CObject*)user; + CObject *pObj, *pBest; + CBotVar* array; + D3DVECTOR iPos, oPos; + float best, minDist, maxDist, sens, iAngle, angle, focus, d, a; + int type, oType, i; + BOOL bArray; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + type = OBJECT_NULL; + angle = 0.0f; + focus = PI*2.0f; + minDist = 0.0f*g_unit; + maxDist = 1000.0f*g_unit; + sens = 1.0f; + + if ( var->GivType() == CBotTypArrayPointer ) + { + array = var->GivItemList(); + bArray = TRUE; + } + else + { + type = var->GivValInt(); + bArray = FALSE; + } + + best = 100000.0f; + pBest = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)script->m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + if ( pObj == pThis ) continue; + + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + if ( !pObj->RetActif() ) continue; + if ( pObj->RetProxyActivate() ) continue; + + oType = pObj->RetType(); + if ( oType == OBJECT_TOTO ) continue; + + if ( oType == OBJECT_RUINmobilew2 || + oType == OBJECT_RUINmobilet1 || + oType == OBJECT_RUINmobilet2 || + oType == OBJECT_RUINmobiler1 || + oType == OBJECT_RUINmobiler2 ) + { + oType = OBJECT_RUINmobilew1; // n'importe quelle ruine + } + + if ( oType == OBJECT_SCRAP2 || + oType == OBJECT_SCRAP3 || + oType == OBJECT_SCRAP4 || + oType == OBJECT_SCRAP5 ) // déchet ? + { + oType = OBJECT_SCRAP1; // n'importe quel déchet + } + + if ( oType == OBJECT_BARRIER2 || + oType == OBJECT_BARRIER3 ) // barrière ? + { + oType = OBJECT_BARRIER1; // n'importe quelle barrière + } + + if ( bArray ) + { + if ( !FindList(array, oType) ) continue; + } + else + { + if ( type != oType && type != OBJECT_NULL ) continue; + } + + oPos = pObj->RetPosition(0); + d = Length2d(iPos, oPos); + if ( d < minDist || d > maxDist ) continue; // trop proche ou trop loin ? + + if ( focus >= PI*2.0f ) + { + if ( d < best ) + { + best = d; + pBest = pObj; + } + continue; + } + + a = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + if ( TestAngle(a, iAngle-focus/2.0f, iAngle+focus/2.0f) ) + { + if ( d < best ) + { + best = d; + pBest = pObj; + } + } + } + + if ( pBest == 0 ) + { + exception = ERR_FIND_IMPOSSIBLE; + return FALSE; + } + + pos = pBest->RetPosition(0); + goal = TGG_DEFAULT; + crash = TGC_DEFAULT; + altitude = 0.0f*g_unit; + + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + err = script->m_primaryTask->StartTaskGoto(pos, altitude, goal, crash); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); +} + +// Compilation "grab/drop(oper)". + +CBotTypResult cGrabDrop(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypFloat); +} + +// Instruction "grab(oper)". + +BOOL rGrab(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + ObjectType oType; + TaskManipArm type; + Error err; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + if ( var == 0 ) type = TMA_FFRONT; + else type = (TaskManipArm)var->GivValInt(); + + oType = pThis->RetType(); + if ( oType == OBJECT_HUMAN || + oType == OBJECT_TECH ) + { + err = script->m_primaryTask->StartTaskTake(); + } + else + { + err = script->m_primaryTask->StartTaskManip(TMO_GRAB, type); + } + + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); +} + +// Instruction "drop(oper)". + +BOOL rDrop(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + ObjectType oType; + TaskManipArm type; + Error err; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + if ( var == 0 ) type = TMA_FFRONT; + else type = (TaskManipArm)var->GivValInt(); + + oType = pThis->RetType(); + if ( oType == OBJECT_HUMAN || + oType == OBJECT_TECH ) + { + err = script->m_primaryTask->StartTaskTake(); + } + else + { + err = script->m_primaryTask->StartTaskManip(TMO_DROP, type); + } + + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); +} + +// Instruction "sniff()". + +BOOL rSniff(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + Error err; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + err = script->m_primaryTask->StartTaskSearch(); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); +} + +// Compilation de l'instruction "receive(nom, power)". + +CBotTypResult cReceive(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() != CBotTypString ) return CBotTypResult(CBotErrBadString); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypFloat); +} + +// Instruction "receive(nom, power)". + +BOOL rReceive(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + CBotString cbs; + Error err; + const char* p; + float value, power; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + + cbs = var->GivValString(); + p = cbs; + var = var->GivNext(); + + power = 10.0f*g_unit; + if ( var != 0 ) + { + power = var->GivValFloat()*g_unit; + var = var->GivNext(); + } + + err = script->m_primaryTask->StartTaskInfo((char*)p, 0.0f, power, FALSE); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetInit(IS_NAN); + return TRUE; + } + } + if ( !Process(script, result, exception) ) return FALSE; // pas terminé + + value = pThis->RetInfoReturn(); + if ( value == NAN ) + { + result->SetInit(IS_NAN); + } + else + { + result->SetValFloat(value); + } + return TRUE; +} + +// Compilation de l'instruction "send(nom, value, power)". + +CBotTypResult cSend(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() != CBotTypString ) return CBotTypResult(CBotErrBadString); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypFloat); +} + +// Instruction "send(nom, value, power)". + +BOOL rSend(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + CBotString cbs; + Error err; + const char* p; + float value, power; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + + cbs = var->GivValString(); + p = cbs; + var = var->GivNext(); + + value = var->GivValFloat(); + var = var->GivNext(); + + power = 10.0f*g_unit; + if ( var != 0 ) + { + power = var->GivValFloat()*g_unit; + var = var->GivNext(); + } + + err = script->m_primaryTask->StartTaskInfo((char*)p, value, power, TRUE); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); +} + +// Cherche la borne d'information la plus proche. + +CObject* SearchInfo(CScript* script, CObject* object, float power) +{ + CObject *pObj, *pBest; + D3DVECTOR iPos, oPos; + ObjectType type; + float dist, min; + int i; + + iPos = object->RetPosition(0); + + min = 100000.0f; + pBest = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)script->m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_INFO ) continue; + + if ( !pObj->RetActif() ) continue; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, iPos); + if ( dist > power ) continue; // trop loin ? + if ( dist < min ) + { + min = dist; + pBest = pObj; + } + } + + return pBest; +} + +// Compilation de l'instruction "deleteinfo(nom, power)". + +CBotTypResult cDeleteInfo(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() != CBotTypString ) return CBotTypResult(CBotErrBadString); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypFloat); +} + +// Instruction "deleteinfo(nom, power)". + +BOOL rDeleteInfo(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + CObject* pInfo; + CBotString cbs; + Info info; + const char* p; + float power; + int i, total; + + exception = 0; + + cbs = var->GivValString(); + p = cbs; + var = var->GivNext(); + + power = 10.0f*g_unit; + if ( var != 0 ) + { + power = var->GivValFloat()*g_unit; + var = var->GivNext(); + } + + pInfo = SearchInfo(script, pThis, power); + if ( pInfo == 0 ) + { + result->SetValFloat(0.0f); // false + return TRUE; + } + + total = pInfo->RetInfoTotal(); + for ( i=0 ; iRetInfo(i); + if ( strcmp(info.name, p) == 0 ) + { + pInfo->DeleteInfo(i); + result->SetValFloat(1.0f); // true + return TRUE; + } + } + result->SetValFloat(0.0f); // false + return TRUE; +} + +// Compilation de l'instruction "testinfo(nom, power)". + +CBotTypResult cTestInfo(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() != CBotTypString ) return CBotTypResult(CBotErrBadString); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypBoolean); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypBoolean); +} + +// Instruction "testinfo(nom, power)". + +BOOL rTestInfo(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + CObject* pInfo; + CBotString cbs; + Info info; + const char* p; + float power; + int i, total; + + exception = 0; + + cbs = var->GivValString(); + p = cbs; + var = var->GivNext(); + + power = 10.0f*g_unit; + if ( var != 0 ) + { + power = var->GivValFloat()*g_unit; + var = var->GivNext(); + } + + pInfo = SearchInfo(script, pThis, power); + if ( pInfo == 0 ) + { + result->SetValInt(FALSE); + return TRUE; + } + + total = pInfo->RetInfoTotal(); + for ( i=0 ; iRetInfo(i); + if ( strcmp(info.name, p) == 0 ) + { + result->SetValInt(TRUE); + return TRUE; + } + } + result->SetValInt(FALSE); + return TRUE; +} + +// Instruction "thump()". + +BOOL rThump(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + Error err; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + err = script->m_primaryTask->StartTaskTerraform(); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); +} + +// Instruction "recycle()". + +BOOL rRecycle(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + Error err; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + err = script->m_primaryTask->StartTaskRecover(); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); +} + +// Compilation "shield(oper, radius)". + +CBotTypResult cShield(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + + return CBotTypResult(CBotTypFloat); +} + +// Instruction "shield(oper, radius)". + +BOOL rShield(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + float oper, radius; + Error err; + + oper = var->GivValFloat(); // 0=down, 1=up + var = var->GivNext(); + + radius = var->GivValFloat(); + if ( radius < 10.0f ) radius = 10.0f; + if ( radius > 25.0f ) radius = 25.0f; + radius = (radius-10.0f)/15.0f; + + if ( *script->m_secondaryTask == 0 ) // bouclier replié ? + { + if ( oper == 0.0f ) // down ? + { + result->SetValInt(1); // indique une erreur + } + else // up ? + { + pThis->SetParam(radius); + + *script->m_secondaryTask = new CTaskManager(script->m_iMan, script->m_object); + err = (*script->m_secondaryTask)->StartTaskShield(TSM_UP, 1000.0f); + if ( err != ERR_OK ) + { + delete *script->m_secondaryTask; + *script->m_secondaryTask = 0; + result->SetValInt(err); // indique l'erreur + } + } + } + else // bouclier deployé ? + { + if ( oper == 0.0f ) // down ? + { + (*script->m_secondaryTask)->StartTaskShield(TSM_DOWN, 0.0f); + } + else // up ? + { +//? result->SetValInt(1); // indique une erreur + pThis->SetParam(radius); + (*script->m_secondaryTask)->StartTaskShield(TSM_UPDATE, 0.0f); + } + } + + return TRUE; +} + +// Compilation "fire(delay)". + +CBotTypResult cFire(CBotVar* &var, void* user) +{ +#if 0 + CObject* pThis = (CObject*)user; + ObjectType type; + + type = pThis->RetType(); + + if ( type == OBJECT_ANT ) + { + return cOnePoint(var, user); + } + else if ( type == OBJECT_SPIDER ) + { + return cNull(var, user); + } + else + { + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + return CBotTypResult(CBotTypFloat); + } +#else + return CBotTypResult(CBotTypFloat); +#endif +} + +// Instruction "fire(delay)". + +BOOL rFire(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + float delay; + D3DVECTOR impact; + Error err; + ObjectType type; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + + type = pThis->RetType(); + + if ( type == OBJECT_ANT ) + { + if ( !GetPoint(var, exception, impact) ) return TRUE; + impact.y += pThis->RetWaterLevel(); + err = script->m_primaryTask->StartTaskFireAnt(impact); + } + else if ( type == OBJECT_SPIDER ) + { + err = script->m_primaryTask->StartTaskSpiderExplo(); + } + else + { + if ( var == 0 ) delay = 0.0f; + else delay = var->GivValFloat(); + err = script->m_primaryTask->StartTaskFire(delay); + } + + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + return TRUE; + } + } + return Process(script, result, exception); +} + +// Instruction "aim(dir)". + +BOOL rAim(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + float value; + Error err; + + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + value = var->GivValFloat(); + err = script->m_primaryTask->StartTaskGunGoal(value*PI/180.0f, 0.0f); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + return TRUE; + } + } + return Process(script, result, exception); +} + +// Compilation de l'instruction "motor(left, right)". + +CBotTypResult cMotor(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var != 0 ) return CBotTypResult(CBotErrOverParam); + + return CBotTypResult(CBotTypFloat); +} + +// Instruction "motor(left, right)". + +BOOL rMotor(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CObject* pThis = (CObject*)user; + CPhysics* physics = ((CObject*)user)->RetPhysics(); + float left, right, speed, turn; + + left = var->GivValFloat(); + var = var->GivNext(); + right = var->GivValFloat(); + + speed = (left+right)/2.0f; + if ( speed < -1.0f ) speed = -1.0f; + if ( speed > 1.0f ) speed = 1.0f; + + turn = left-right; + if ( turn < -1.0f ) turn = -1.0f; + if ( turn > 1.0f ) turn = 1.0f; + + if ( pThis->RetFixed() ) // fourmi sur le dos ? + { + speed = 0.0f; + turn = 0.0f; + } + + physics->SetMotorSpeedX(speed); // avance/recule + physics->SetMotorSpeedZ(turn); // tourne + + return TRUE; +} + +// Instruction "jet(power)". + +BOOL rJet(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CPhysics* physics = ((CObject*)user)->RetPhysics(); + float value; + + value = var->GivValFloat(); + physics->SetMotorSpeedY(value); + + return TRUE; +} + +// Compilation de l'instruction "topo(pos)". + +CBotTypResult cTopo(CBotVar* &var, void* user) +{ + CBotTypResult ret; + + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + ret = cPoint(var, user); + if ( ret.GivType() != 0 ) return ret; + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + return CBotTypResult(CBotErrOverParam); +} + +// Instruction "topo(pos)". + +BOOL rTopo(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + D3DVECTOR pos; + float level; + + exception = 0; + + if ( !GetPoint(var, exception, pos) ) return TRUE; + + level = script->m_terrain->RetFloorLevel(pos); + level -= script->m_water->RetLevel(); + result->SetValFloat(level/g_unit); + return TRUE; +} + +// Compilation de l'instruction "message(string, type)". + +CBotTypResult cMessage(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotErrLowParam); + if ( var->GivType() != CBotTypString && + var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + return CBotTypResult(CBotErrOverParam); +} + +// Instruction "message(string, type)". + +BOOL rMessage(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CBotString cbs; + const char* p; + TextType type; + + cbs = var->GivValString(); + p = cbs; + + type = TT_MESSAGE; + var = var->GivNext(); + if ( var != 0 ) + { + type = (TextType)var->GivValInt(); + } + + script->m_displayText->DisplayText((char*)p, script->m_object, 10.0f, type); + script->m_main->CheckEndMessage((char*)p); + + return TRUE; +} + +// Instruction "cmdline(rank)". + +BOOL rCmdline(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + float value; + int rank; + + rank = var->GivValInt(); + value = pThis->RetCmdLine(rank); + result->SetValFloat(value); + + return TRUE; +} + +// Instruction "ismovie()". + +BOOL rIsMovie(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + float value; + + value = script->m_main->RetMovieLock()?1.0f:0.0f; + result->SetValFloat(value); + + return TRUE; +} + +// Instruction "errmode(mode)". + +BOOL rErrMode(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + int value; + + value = var->GivValInt(); + if ( value < 0 ) value = 0; + if ( value > 1 ) value = 1; + script->m_errMode = value; + + return TRUE; +} + +// Instruction "ipf(num)". + +BOOL rIPF(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + int value; + + value = var->GivValInt(); + if ( value < 1 ) value = 1; + if ( value > 10000 ) value = 10000; + script->m_ipf = value; + + return TRUE; +} + +// Instruction "abstime()". + +BOOL rAbsTime(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + float value; + + value = script->m_main->RetGameTime(); + result->SetValFloat(value); + return TRUE; +} + + +// Prépare un nom de fichier. + +void PrepareFilename(CBotString &filename, char *dir) +{ + int pos; + + pos = filename.ReverseFind('\\'); + if ( pos > 0 ) + { + filename = filename.Mid(pos+1); // enlève les dossiers + } + + pos = filename.ReverseFind('/'); + if ( pos > 0 ) + { + filename = filename.Mid(pos+1); // aussi ceux avec / + } + + pos = filename.ReverseFind(':'); + if ( pos > 0 ) + { + filename = filename.Mid(pos+1); // enlève aussi la lettre d'unité C: + } + + filename = CBotString(dir) + CBotString("\\") + filename; +} + +// Instruction "deletefile(filename)". + +BOOL rDeleteFile(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CBotString cbs; + const char* p; + char* dir; + + cbs = var->GivValString(); + dir = script->m_main->RetFilesDir(); + PrepareFilename(cbs, dir); + p = cbs; + DeleteFile(p); + + return TRUE; +} + +// Compilation de l'instruction "pendown(color, width)". + +CBotTypResult cPenDown(CBotVar* &var, void* user) +{ + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + if ( var->GivType() > CBotTypDouble ) return CBotTypResult(CBotErrBadNum); + var = var->GivNext(); + + if ( var == 0 ) return CBotTypResult(CBotTypFloat); + return CBotTypResult(CBotErrOverParam); +} + +// Instruction "pendown(color, width)". + +BOOL rPenDown(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + int color; + float width; + Error err; + + if ( pThis->RetType() == OBJECT_MOBILEdr ) + { + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + if ( var != 0 ) + { + color = var->GivValInt(); + if ( color < 0 ) color = 0; + if ( color > 17 ) color = 17; + pThis->SetTraceColor(color); + + var = var->GivNext(); + if ( var != 0 ) + { + width = var->GivValFloat(); + if ( width < 0.1f ) width = 0.1f; + if ( width > 1.0f ) width = 1.0f; + pThis->SetTraceWidth(width); + } + } + pThis->SetTraceDown(TRUE); + + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + err = script->m_primaryTask->StartTaskPen(pThis->RetTraceDown(), pThis->RetTraceColor()); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); + } + else + { + if ( var != 0 ) + { + color = var->GivValInt(); + if ( color < 0 ) color = 0; + if ( color > 17 ) color = 17; + pThis->SetTraceColor(color); + + var = var->GivNext(); + if ( var != 0 ) + { + width = var->GivValFloat(); + if ( width < 0.1f ) width = 0.1f; + if ( width > 1.0f ) width = 1.0f; + pThis->SetTraceWidth(width); + } + } + pThis->SetTraceDown(TRUE); + + return TRUE; + } +} + +// Instruction "penup()". + +BOOL rPenUp(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CObject* pThis = (CObject*)user; + Error err; + + if ( pThis->RetType() == OBJECT_MOBILEdr ) + { + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + pThis->SetTraceDown(FALSE); + + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + err = script->m_primaryTask->StartTaskPen(pThis->RetTraceDown(), pThis->RetTraceColor()); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); + } + else + { + pThis->SetTraceDown(FALSE); + return TRUE; + } +} + +// Instruction "pencolor()". + +BOOL rPenColor(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CScript* script = ((CObject*)user)->RetRunScript(); + CPhysics* physics = ((CObject*)user)->RetPhysics(); + CObject* pThis = (CObject*)user; + int color; + Error err; + + if ( pThis->RetType() == OBJECT_MOBILEdr ) + { + exception = 0; + + if ( script->m_primaryTask == 0 ) // pas de tâche en cours ? + { + color = var->GivValInt(); + if ( color < 0 ) color = 0; + if ( color > 17 ) color = 17; + pThis->SetTraceColor(color); + + script->m_primaryTask = new CTaskManager(script->m_iMan, script->m_object); + err = script->m_primaryTask->StartTaskPen(pThis->RetTraceDown(), pThis->RetTraceColor()); + if ( err != ERR_OK ) + { + delete script->m_primaryTask; + script->m_primaryTask = 0; + result->SetValInt(err); // indique l'erreur + if ( script->m_errMode == ERM_STOP ) + { + exception = err; + return FALSE; + } + return TRUE; + } + } + return Process(script, result, exception); + } + else + { + color = var->GivValInt(); + if ( color < 0 ) color = 0; + if ( color > 17 ) color = 17; + pThis->SetTraceColor(color); + + return TRUE; + } +} + +// Instruction "penwidth()". + +BOOL rPenWidth(CBotVar* var, CBotVar* result, int& exception, void* user) +{ + CObject* pThis = (CObject*)user; + float width; + + width = var->GivValFloat(); + if ( width < 0.1f ) width = 0.1f; + if ( width > 1.0f ) width = 1.0f; + pThis->SetTraceWidth(width); + return TRUE; +} + + + +// Constructeur de l'objet. + +CScript::CScript(CInstanceManager* iMan, CObject* object, CTaskManager** secondaryTask) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_SCRIPT, this, 100); + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_interface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + m_displayText = (CDisplayText*)m_iMan->SearchInstance(CLASS_DISPLAYTEXT); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + m_botProg = 0; + m_object = object; + m_primaryTask = 0; + m_secondaryTask = secondaryTask; + + m_ipf = CBOT_IPF; + m_errMode = ERM_STOP; + m_len = 0; + m_script = 0; + m_bRun = FALSE; + m_bStepMode = FALSE; + m_bCompile = FALSE; + m_title[0] = 0; + m_cursor1 = 0; + m_cursor2 = 0; + m_filename[0] = 0; +} + +// Initialise toutes les fonctions pour le module CBOT. + +void CScript::InitFonctions() +{ + CBotProgram::AddFunction("sin", rSin, cOneFloat); + CBotProgram::AddFunction("cos", rCos, cOneFloat); + CBotProgram::AddFunction("tan", rTan, cOneFloat); + CBotProgram::AddFunction("asin", raSin, cOneFloat); + CBotProgram::AddFunction("acos", raCos, cOneFloat); + CBotProgram::AddFunction("atan", raTan, cOneFloat); + CBotProgram::AddFunction("sqrt", rSqrt, cOneFloat); + CBotProgram::AddFunction("pow", rPow, cTwoFloat); + CBotProgram::AddFunction("rand", rRand, cNull); + CBotProgram::AddFunction("abs", rAbs, cOneFloat); + + CBotProgram::AddFunction("retobject", rRetObject, cRetObject); + CBotProgram::AddFunction("search", rSearch, cSearch); + CBotProgram::AddFunction("radar", rRadar, cRadar); + CBotProgram::AddFunction("detect", rDetect, cDetect); + CBotProgram::AddFunction("direction", rDirection, cDirection); + CBotProgram::AddFunction("produce", rProduce, cProduce); + CBotProgram::AddFunction("distance", rDistance, cDistance); + CBotProgram::AddFunction("distance2d",rDistance2d,cDistance); + CBotProgram::AddFunction("space", rSpace, cSpace); + CBotProgram::AddFunction("flatground",rFlatGround,cFlatGround); + CBotProgram::AddFunction("wait", rWait, cOneFloat); + CBotProgram::AddFunction("move", rMove, cOneFloat); + CBotProgram::AddFunction("turn", rTurn, cOneFloat); + CBotProgram::AddFunction("goto", rGoto, cGoto); + CBotProgram::AddFunction("find", rFind, cOneFloat); + CBotProgram::AddFunction("grab", rGrab, cGrabDrop); + CBotProgram::AddFunction("drop", rDrop, cGrabDrop); + CBotProgram::AddFunction("sniff", rSniff, cNull); + CBotProgram::AddFunction("receive", rReceive, cReceive); + CBotProgram::AddFunction("send", rSend, cSend); + CBotProgram::AddFunction("deleteinfo",rDeleteInfo,cDeleteInfo); + CBotProgram::AddFunction("testinfo", rTestInfo, cTestInfo); + CBotProgram::AddFunction("thump", rThump, cNull); + CBotProgram::AddFunction("recycle", rRecycle, cNull); + CBotProgram::AddFunction("shield", rShield, cShield); + CBotProgram::AddFunction("fire", rFire, cFire); + CBotProgram::AddFunction("aim", rAim, cOneFloat); + CBotProgram::AddFunction("motor", rMotor, cMotor); + CBotProgram::AddFunction("jet", rJet, cOneFloat); + CBotProgram::AddFunction("topo", rTopo, cTopo); + CBotProgram::AddFunction("message", rMessage, cMessage); + CBotProgram::AddFunction("cmdline", rCmdline, cOneFloat); + CBotProgram::AddFunction("ismovie", rIsMovie, cNull); + CBotProgram::AddFunction("errmode", rErrMode, cOneFloat); + CBotProgram::AddFunction("ipf", rIPF, cOneFloat); + CBotProgram::AddFunction("abstime", rAbsTime, cNull); + CBotProgram::AddFunction("deletefile",rDeleteFile,cString); + CBotProgram::AddFunction("pendown", rPenDown, cPenDown); + CBotProgram::AddFunction("penup", rPenUp, cNull); + CBotProgram::AddFunction("pencolor", rPenColor, cOneFloat); + CBotProgram::AddFunction("penwidth", rPenWidth, cOneFloat); +} + +// Destructeur de l'objet. + +CScript::~CScript() +{ + delete m_botProg; + delete m_primaryTask; + delete m_script; + m_script = 0; + m_len = 0; + + m_iMan->DeleteInstance(CLASS_SCRIPT, this); +} + + +// Donne le script éditable à un pavé de texte. + +void CScript::PutScript(CEdit* edit, char* name) +{ + if ( m_script == 0 ) + { + New(edit, name); + } + else + { + edit->SetText(m_script); + edit->SetCursor(m_cursor2, m_cursor1); + edit->ShowSelect(); + } + edit->SetFocus(TRUE); +} + +// Reprend le script d'un pavé de texte. + +BOOL CScript::GetScript(CEdit* edit) +{ + int len; + + delete m_script; + m_script = 0; + + len = edit->RetTextLength(); + m_script = (char*)malloc(sizeof(char)*(len+1)); + + edit->GetText(m_script, len+1); + edit->GetCursor(m_cursor2, m_cursor1); + m_len = strlen(m_script); + + if ( !CheckToken() ) + { + edit->SetCursor(m_cursor2, m_cursor1); + edit->ShowSelect(); + edit->SetFocus(TRUE); + return FALSE; + } + + if ( !Compile() ) + { + edit->SetCursor(m_cursor2, m_cursor1); + edit->ShowSelect(); + edit->SetFocus(TRUE); + return FALSE; + } + + return TRUE; +} + +// Indique si un programme est correctement compilé. + +BOOL CScript::RetCompile() +{ + return m_bCompile; +} + +// Indique si le programme est vide. + +BOOL CScript::IsEmpty() +{ + int i; + + for ( i=0 ; iRetCheckToken() ) return TRUE; + + m_error = 0; + m_title[0] = 0; + m_token[0] = 0; + m_bCompile = FALSE; + + for ( i=0 ; iRetObligatoryToken() ; i++ ) + { + used[i] = 0; // token pas utilisé + } + + bt = CBotToken::CompileTokens(m_script, error); + while ( bt != 0 ) + { + bs = bt->GivString(); + token = bs; + type = bt->GivType(); + + cursor1 = bt->GivStart(); + cursor2 = bt->GivEnd(); + + i = m_main->IsObligatoryToken((char*)token); + if ( i != -1 ) + { + used[i] = 1; // token utilisé + } + + if ( !m_main->IsProhibitedToken((char*)token) ) + { + m_error = ERR_PROHIBITEDTOKEN; + m_cursor1 = cursor1; + m_cursor2 = cursor2; + strcpy(m_title, ""); + CBotToken::Delete(bt); + return FALSE; + } + + bt = bt->GivNext(); + } + + // Au moins une fois chaque instruction obligatoire ? + for ( i=0 ; iRetObligatoryToken() ; i++ ) + { + if ( used[i] == 0 ) // token pas utilisé ? + { + strcpy(m_token, m_main->RetObligatoryToken(i)); + m_error = ERR_OBLIGATORYTOKEN; + strcpy(m_title, ""); + CBotToken::Delete(bt); + return FALSE; + } + } + + CBotToken::Delete(bt); + return TRUE; +} + +// Compile le script d'un pavé de texte. + +BOOL CScript::Compile() +{ + CBotStringArray liste; + int i; + const char* p; + + m_error = 0; + m_cursor1 = 0; + m_cursor2 = 0; + m_title[0] = 0; + m_bCompile = FALSE; + + if ( IsEmpty() ) // programme inexistant ? + { + delete m_botProg; + m_botProg = 0; + return TRUE; + } + + if ( m_botProg == 0 ) + { + m_botProg = new CBotProgram(m_object->RetBotVar()); + } + + if ( m_botProg->Compile(m_script, liste, this) ) + { + if ( liste.GivSize() == 0 ) + { + strcpy(m_title, ""); + } + else + { + p = liste[0]; + i = 0; + while ( TRUE ) + { + if ( p[i] == 0 || p[i] == '(' ) break; + if ( i >= 20 ) + { + m_title[i++] = '.'; + m_title[i++] = '.'; + m_title[i++] = '.'; + break; + } + m_title[i] = p[i]; + i ++; + } + m_title[i] = 0; + } + m_bCompile = TRUE; + return TRUE; + } + else + { + m_botProg->GetError(m_error, m_cursor1, m_cursor2); + if ( m_cursor1 < 0 || m_cursor1 > m_len || + m_cursor2 < 0 || m_cursor2 > m_len ) + { + m_cursor1 = 0; + m_cursor2 = 0; + } + if ( m_error == 0 ) + { + m_cursor1 = m_cursor2 = 0; + } + strcpy(m_title, ""); + return FALSE; + } +} + + +// Retourne le titre du script. + +void CScript::GetTitle(char* buffer) +{ + strcpy(buffer, m_title); +} + + +// Choix du mode d'exécution. + +void CScript::SetStepMode(BOOL bStep) +{ + m_bStepMode = bStep; +} + + +// Lance le programme depuis le début. + +BOOL CScript::Run() +{ + if( m_botProg == 0 ) return FALSE; + if ( m_script == 0 || m_len == 0 ) return FALSE; + + if ( !m_botProg->Start(m_title) ) return FALSE; + + m_object->SetRunScript(this); + m_bRun = TRUE; + m_bContinue = FALSE; + m_ipf = CBOT_IPF; + m_errMode = ERM_STOP; + + if ( m_bStepMode ) // mode step by step ? + { + Event newEvent; + ZeroMemory(&newEvent, sizeof(Event)); + Step(newEvent); + } + + return TRUE; +} + +// Continue le programme en cours d'exécution. +// Retourne TRUE lorsque l'exécution est terminée. + +BOOL CScript::Continue(const Event &event) +{ + if( m_botProg == 0 ) return TRUE; + if ( !m_bRun ) return TRUE; + + m_event = event; + + if ( m_bStepMode ) // mode step by step ? + { + if ( m_bContinue ) // instuction "move", "goto", etc. ? + { + if ( m_botProg->Run(m_object, 0) ) + { + m_botProg->GetError(m_error, m_cursor1, m_cursor2); + if ( m_cursor1 < 0 || m_cursor1 > m_len || + m_cursor2 < 0 || m_cursor2 > m_len ) + { + m_cursor1 = 0; + m_cursor2 = 0; + } + if ( m_error == 0 ) + { + m_cursor1 = m_cursor2 = 0; + } + m_bRun = FALSE; + + if ( m_error != 0 && m_errMode == ERM_STOP ) + { + char s[100]; + GetError(s); + m_displayText->DisplayText(s, m_object, 10.0f, TT_ERROR); + } + m_engine->SetPause(TRUE); // remet la pause + return TRUE; + } + if ( !m_bContinue ) + { + m_engine->SetPause(TRUE); // remet la pause + } + } + + return FALSE; + } + + if ( m_botProg->Run(m_object, m_ipf) ) + { + m_botProg->GetError(m_error, m_cursor1, m_cursor2); + if ( m_cursor1 < 0 || m_cursor1 > m_len || + m_cursor2 < 0 || m_cursor2 > m_len ) + { + m_cursor1 = 0; + m_cursor2 = 0; + } + if ( m_error == 0 ) + { + m_cursor1 = m_cursor2 = 0; + } + m_bRun = FALSE; + + if ( m_error != 0 && m_errMode == ERM_STOP ) + { + char s[100]; + GetError(s); + m_displayText->DisplayText(s, m_object, 10.0f, TT_ERROR); + } + return TRUE; + } + + return FALSE; +} + +// Continue le programme en cours d'exécution. +// Retourne TRUE lorsque l'exécution est terminée. + +BOOL CScript::Step(const Event &event) +{ + if( m_botProg == 0 ) return TRUE; + if ( !m_bRun ) return TRUE; + if ( !m_bStepMode ) return FALSE; + + m_engine->SetPause(FALSE); + m_engine->StepSimul(0.01f); // avance de 10ms + m_engine->SetPause(TRUE); + + m_event = event; + + if ( m_botProg->Run(m_object, 0) ) // en mode step + { + m_botProg->GetError(m_error, m_cursor1, m_cursor2); + if ( m_cursor1 < 0 || m_cursor1 > m_len || + m_cursor2 < 0 || m_cursor2 > m_len ) + { + m_cursor1 = 0; + m_cursor2 = 0; + } + if ( m_error == 0 ) + { + m_cursor1 = m_cursor2 = 0; + } + m_bRun = FALSE; + + if ( m_error != 0 && m_errMode == ERM_STOP ) + { + char s[100]; + GetError(s); + m_displayText->DisplayText(s, m_object, 10.0f, TT_ERROR); + } + return TRUE; + } + + if ( m_bContinue ) // instuction "move", "goto", etc. ? + { + m_engine->SetPause(FALSE); // enlève la pause + } + return FALSE; +} + +// Stoppe le programme. + +void CScript::Stop() +{ + if ( !m_bRun ) return; + + if( m_botProg != 0 ) + { + m_botProg->Stop(); + } + + if ( m_primaryTask != 0 ) + { + m_primaryTask->Abort(); + delete m_primaryTask; + m_primaryTask = 0; + } + + m_bRun = FALSE; +} + +// Indique si le programme tourne. + +BOOL CScript::IsRunning() +{ + return m_bRun; +} + +// Indique si le programme continue un step. + +BOOL CScript::IsContinue() +{ + return m_bContinue; +} + + +// Donne la position des curseurs pendant l'exécution. + +BOOL CScript::GetCursor(int &cursor1, int &cursor2) +{ + const char* funcName; + + cursor1 = cursor2 = 0; + + if( m_botProg == 0 ) return FALSE; + if ( !m_bRun ) return FALSE; + + m_botProg->GetRunPos(funcName, cursor1, cursor2); + if ( cursor1 < 0 || cursor1 > m_len || + cursor2 < 0 || cursor2 > m_len ) + { + cursor1 = 0; + cursor2 = 0; + } + return TRUE; +} + + +// Met des variables dans une liste. + +void PutList(char *baseName, BOOL bArray, CBotVar *var, CList *list, int &rankList) +{ + CBotString bs; + CBotVar *svar, *pStatic; + char varName[100]; + char buffer[100]; + const char *p; + int index, type; + + if ( var == 0 && baseName[0] != 0 ) + { + sprintf(buffer, "%s = null;", baseName); + list->SetName(rankList++, buffer); + return; + } + + index = 0; + while ( var != 0 ) + { + var->Maj(NULL, FALSE); + pStatic = var->GivStaticVar(); // retrouve l'élément static + + bs = pStatic->GivName(); // nom de la variable + p = bs; +//? if ( strcmp(p, "this") == 0 ) +//? { +//? var = var->GivNext(); +//? continue; +//? } + + if ( baseName[0] == 0 ) + { + sprintf(varName, "%s", p); + } + else + { + if ( bArray ) + { + sprintf(varName, "%s[%d]", baseName, index); + } + else + { + sprintf(varName, "%s.%s", baseName, p); + } + } + + type = pStatic->GivType(); + + if ( type < CBotTypBoolean ) + { + CBotString value; + value = pStatic->GivValString(); + p = value; + sprintf(buffer, "%s = %s;", varName, p); + list->SetName(rankList++, buffer); + } + else if ( type == CBotTypString ) + { + CBotString value; + value = pStatic->GivValString(); + p = value; + sprintf(buffer, "%s = \"%s\";", varName, p); + list->SetName(rankList++, buffer); + } + else if ( type == CBotTypArrayPointer ) + { + svar = pStatic->GivItemList(); + PutList(varName, TRUE, svar, list, rankList); + } + else if ( type == CBotTypClass || + type == CBotTypPointer ) + { + svar = pStatic->GivItemList(); + PutList(varName, FALSE, svar, list, rankList); + } + else + { + sprintf(buffer, "%s = ?;", varName); + list->SetName(rankList++, buffer); + } + + index ++; + var = var->GivNext(); + } +} + +// Rempli une liste avec les variables. + +void CScript::UpdateList(CList* list) +{ + CBotVar *var; + const char *progName, *funcName; + int total, select, level, cursor1, cursor2, rank; + + if( m_botProg == 0 ) return; + + total = list->RetTotal(); + select = list->RetSelect(); + + list->Flush(); // vide la liste + m_botProg->GetRunPos(progName, cursor1, cursor2); + if ( progName == 0 ) return; + + level = 0; + rank = 0; + while ( TRUE ) + { + var = m_botProg->GivStackVars(funcName, level--); + if ( funcName != progName ) break; + + PutList("", FALSE, var, list, rank); + } + + if ( total == list->RetTotal() ) // même total ? + { + list->SetSelect(select); + } + + list->SetTooltip(""); + list->SetState(STATE_ENABLE); +} + + +// Colorise le texte selon la syntaxe. + +void CScript::ColorizeScript(CEdit* edit) +{ + CBotToken* bt; + CBotString bs; + const char* token; + int error, type, cursor1, cursor2, color; + + edit->ClearFormat(); + + bt = CBotToken::CompileTokens(edit->RetText(), error); + while ( bt != 0 ) + { + bs = bt->GivString(); + token = bs; + type = bt->GivType(); + + cursor1 = bt->GivStart(); + cursor2 = bt->GivEnd(); + + color = 0; + if ( type >= TokenKeyWord && type < TokenKeyWord+100 ) + { + color = COLOR_TOKEN; + } + if ( type >= TokenKeyDeclare && type < TokenKeyDeclare+100 ) + { + color = COLOR_TYPE; + } + if ( type >= TokenKeyVal && type < TokenKeyVal+100 ) + { + color = COLOR_CONST; + } + if ( type == TokenTypVar ) + { + if ( IsType(token) ) + { + color = COLOR_TYPE; + } + else if ( IsFunction(token) ) + { + color = COLOR_TOKEN; + } + } + if ( type == TokenTypDef ) + { + color = COLOR_CONST; + } + + if ( cursor1 < cursor2 && color != 0 ) + { + edit->SetFormat(cursor1, cursor2, color); + } + + bt = bt->GivNext(); + } + + CBotToken::Delete(bt); +} + + +// Cherche un token au hazard dans un script. +// Retourne l'index du début du token trouvé, ou -1. + +int SearchToken(char* script, char* token) +{ + int lScript, lToken, i, iFound; + int found[100]; + char* p; + + lScript = strlen(script); + lToken = strlen(token); + iFound = 0; + for ( i=0 ; i= 100 ) break; + } + } + + if ( iFound == 0 ) return -1; + return found[rand()%iFound]; +} + +// Supprime un token dans un script. + +void DeleteToken(char* script, int pos, int len) +{ + while ( TRUE ) + { + script[pos] = script[pos+len]; + if ( script[pos++] == 0 ) break; + } +} + +// Insère un token dans un script. + +void InsertToken(char* script, int pos, char* token) +{ + int lScript, lToken, i; + + lScript = strlen(script); + lToken = strlen(token); + for ( i=lScript ; i>=pos ; i-- ) + { + script[i+lToken] = script[i]; + } + memcpy(script+pos, token, lToken); +} + +// Introduit un virus dans un programme. + +BOOL CScript::IntroduceVirus() +{ + int i, start, iFound; + int found[11*2]; + char* newScript; + + char* names[11*2] = + { + "==", "!=", + "!=", "==", + ">", "<", + "<", ">", + "true", "false", + "false", "true", + "grab", "drop", + "drop", "grab", + "InFront", "Behind", + "Behind", "EnergyCell", + "EnergyCell", "InFront", + }; + + iFound = 0; + for ( i=0 ; i<11 ; i++ ) + { + start = SearchToken(m_script, names[i*2]); + if ( start != -1 ) + { + found[iFound++] = i*2; + found[iFound++] = start; + } + } + if ( iFound == 0 ) return FALSE; + + i = (rand()%(iFound/2))*2; + start = found[i+1]; + i = found[i+0]; + + newScript = (char*)malloc(sizeof(char)*(m_len+strlen(names[i+1])+1)); + strcpy(newScript, m_script); + delete m_script; + m_script = newScript; + + DeleteToken(m_script, start, strlen(names[i])); + InsertToken(m_script, start, names[i+1]); + m_len = strlen(m_script); + Compile(); // recompile avec le virus + + return TRUE; +} + + +// Retourne le numéro de l'erreur. + +int CScript::RetError() +{ + return m_error; +} + +// Retourne le texte de l'erreur. + +void CScript::GetError(char* buffer) +{ + if ( m_error == 0 ) + { + buffer[0] = 0; + } + else + { + if ( m_error == ERR_OBLIGATORYTOKEN ) + { + char s[100]; + GetResource(RES_ERR, m_error, s); + sprintf(buffer, s, m_token); + } + else if ( m_error < 1000 ) + { + GetResource(RES_ERR, m_error, buffer); + } + else + { + GetResource(RES_CBOT, m_error, buffer); + } + } +} + + +// Nouveau programme. + +void CScript::New(CEdit* edit, char* name) +{ + FILE *file = NULL; + char res[100]; + char text[100]; + char filename[100]; + char script[500]; + char buffer[500]; + char *sf; + int cursor1, cursor2, len, i, j; + + GetResource(RES_TEXT, RT_SCRIPT_NEW, res); + if ( name[0] == 0 ) strcpy(text, res); + else strcpy(text, name); + + sprintf(script, "extern void object::%s()\n{\n\t\n\t\n\t\n}\n", text); + edit->SetText(script, FALSE); + + if ( strcmp(text, res) == 0 ) + { + cursor1 = 20; + cursor2 = 20+strlen(text); // màj "Nouveau" + } + else + { + if ( edit->RetAutoIndent() ) + { + cursor1 = 20+strlen(text)+6; + cursor2 = cursor1; // curseur dans { } + } + else + { + cursor1 = 20+strlen(text)+8; + cursor2 = cursor1; // curseur dans { } + } + } + + edit->SetCursor(cursor2, cursor1); + edit->ShowSelect(); + edit->SetFocus(TRUE); + + sf = m_main->RetScriptFile(); + if ( sf[0] != 0 ) // charge un programme vide spécifique ? + { + strcpy(filename, "script\\"); + strcat(filename, sf); + file = fopen(filename, "rb"); + if ( file != NULL ) + { + fseek(file, 0, SEEK_END); + len = ftell(file); + fseek(file, 0, SEEK_SET); + + if ( len > 500-1 ) len = 500-1; + fread(buffer, 1, len, file); + buffer[len] = 0; + fclose(file); + + cursor1 = 0; + i = 0; + j = 0; + while ( TRUE ) + { + if ( buffer[i] == 0 ) break; + + if ( buffer[i] == '\r' ) + { + i ++; + continue; + } + + if ( buffer[i] == '\t' && edit->RetAutoIndent() ) + { + i ++; + continue; + } + + if ( buffer[i+0] == '%' && + buffer[i+1] == 's' ) + { + strcpy(script+j, text); + j += strlen(text); + i += 2; + continue; + } + + if ( buffer[i] == '#' ) + { + cursor1 = j; + i ++; + continue; + } + + script[j++] = buffer[i++]; + } + script[j] = 0; + edit->SetText(script, FALSE); + + cursor2 = cursor1; + edit->SetCursor(cursor2, cursor1); + edit->ShowSelect(); + edit->SetFocus(TRUE); + } + } + + ColorizeScript(edit); +} + + +// Fourni un script de toutes pièces. + +BOOL CScript::SendScript(char* text) +{ + m_len = strlen(text); + m_script = (char*)malloc(sizeof(char)*(m_len+1)); + strcpy(m_script, text); + if ( !CheckToken() ) return FALSE; + if ( !Compile() ) return FALSE; + + return TRUE; +} + +// Lit un script sous la forme d'un fichier texte. + +BOOL CScript::ReadScript(char* filename) +{ + FILE* file; + CEdit* edit; + char name[100]; + + if ( strchr(filename, '\\') == 0 ) + { + strcpy(name, "script\\"); + strcat(name, filename); + } + else + { +//? strcpy(name, filename); + UserDir(name, filename, ""); + } + + file = fopen(name, "rb"); + if ( file == NULL ) return FALSE; + fclose(file); + + delete m_script; + m_script = 0; + + edit = m_interface->CreateEdit(FPOINT(0.0f, 0.0f), FPOINT(0.0f, 0.0f), 0, EVENT_EDIT9); + edit->SetMaxChar(EDITSTUDIOMAX); + edit->SetAutoIndent(m_engine->RetEditIndentMode()); + edit->ReadText(name); + GetScript(edit); + m_interface->DeleteControl(EVENT_EDIT9); + return TRUE; +} + +// Ecrit un script sous la forme d'un fichier texte. + +BOOL CScript::WriteScript(char* filename) +{ + CEdit* edit; + char name[100]; + + if ( strchr(filename, '\\') == 0 ) + { + strcpy(name, "script\\"); + strcat(name, filename); + } + else + { + strcpy(name, filename); + } + + if ( m_script == 0 ) + { + remove(filename); + return FALSE; + } + + edit = m_interface->CreateEdit(FPOINT(0.0f, 0.0f), FPOINT(0.0f, 0.0f), 0, EVENT_EDIT9); + edit->SetMaxChar(EDITSTUDIOMAX); + edit->SetAutoIndent(m_engine->RetEditIndentMode()); + edit->SetText(m_script); + edit->WriteText(name); + m_interface->DeleteControl(EVENT_EDIT9); + return TRUE; +} + + +// Lit un stack de script en exécution sous la forme d'un fichier. + +BOOL CScript::ReadStack(FILE *file) +{ + int nb; + + fRead(&nb, sizeof(int), 1, file); + fRead(&m_ipf, sizeof(int), 1, file); + fRead(&m_errMode, sizeof(int), 1, file); + + if ( m_botProg == 0 ) return FALSE; + if ( !m_botProg->RestoreState(file) ) return FALSE; + + m_object->SetRunScript(this); + m_bRun = TRUE; + m_bContinue = FALSE; + return TRUE; +} + +// Ecrit un stack de script en exécution sous la forme d'un fichier. + +BOOL CScript::WriteStack(FILE *file) +{ + int nb; + + nb = 2; + fWrite(&nb, sizeof(int), 1, file); + fWrite(&m_ipf, sizeof(int), 1, file); + fWrite(&m_errMode, sizeof(int), 1, file); + + return m_botProg->SaveState(file); +} + + +// Compare deux scripts. + +BOOL CScript::Compare(CScript* other) +{ + if ( m_len != other->m_len ) return FALSE; + + return ( strcmp(m_script, other->m_script) == 0 ); +} + + +// Gestion du nom de fichier lorsque le script est sauvegardé. + +void CScript::SetFilename(char *filename) +{ + strcpy(m_filename, filename); +} + +char* CScript::RetFilename() +{ + return m_filename; +} + diff --git a/src/script.h b/src/script.h new file mode 100644 index 00000000..8078715a --- /dev/null +++ b/src/script.h @@ -0,0 +1,99 @@ +// script.h + +#ifndef _SCRIPT_H_ +#define _SCRIPT_H_ + + +class CInstanceManager; +class CD3DEngine; +class CInterface; +class CDisplayText; +class CEdit; +class CList; +class CObject; +class CTaskManager; +class CBotProgram; +class CRobotMain; +class CTerrain; +class CWater; + + + +class CScript +{ +public: + CScript(CInstanceManager* iMan, CObject* object, CTaskManager** secondaryTask); + ~CScript(); + + static void InitFonctions(); + + void PutScript(CEdit* edit, char* name); + BOOL GetScript(CEdit* edit); + BOOL RetCompile(); + + void GetTitle(char* buffer); + + void SetStepMode(BOOL bStep); + BOOL Run(); + BOOL Continue(const Event &event); + BOOL Step(const Event &event); + void Stop(); + BOOL IsRunning(); + BOOL IsContinue(); + BOOL GetCursor(int &cursor1, int &cursor2); + void UpdateList(CList* list); + void ColorizeScript(CEdit* edit); + BOOL IntroduceVirus(); + + int RetError(); + void GetError(char* buffer); + + void New(CEdit* edit, char* name); + BOOL SendScript(char* text); + BOOL ReadScript(char* filename); + BOOL WriteScript(char* filename); + BOOL ReadStack(FILE *file); + BOOL WriteStack(FILE *file); + BOOL Compare(CScript* other); + + void SetFilename(char *filename); + char* RetFilename(); + +protected: + BOOL IsEmpty(); + BOOL CheckToken(); + BOOL Compile(); + +public: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CInterface* m_interface; + CDisplayText* m_displayText; + CBotProgram* m_botProg; + CRobotMain* m_main; + CTerrain* m_terrain; + CWater* m_water; + CTaskManager* m_primaryTask; + CTaskManager** m_secondaryTask; + CObject* m_object; + + int m_ipf; // nb d'instructions / seconde + int m_errMode; // que faire en cas d'erreur + int m_len; // longueur du script (sans le <0>) + char* m_script; // script terminé par <0> + BOOL m_bRun; // programme en cours d'exécution ? + BOOL m_bStepMode; // step by step + BOOL m_bContinue; // fonction externe à continuer + BOOL m_bCompile; // compilation ok ? + char m_title[50]; // titre du script + char m_filename[50]; // nom du fichier + char m_token[50]; // instruction manquante + int m_error; // erreur (0=ok) + int m_cursor1; + int m_cursor2; + Event m_event; + float m_returnValue; +}; + + +#endif //_SCRIPT_H_ diff --git a/src/scroll.cpp b/src/scroll.cpp new file mode 100644 index 00000000..c5a7afcc --- /dev/null +++ b/src/scroll.cpp @@ -0,0 +1,459 @@ +// scroll.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "button.h" +#include "scroll.h" + + + + +// Constructeur de l'objet. + +CScroll::CScroll(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); + + m_buttonUp = 0; + m_buttonDown = 0; + + m_visibleValue = 0.0f; + m_visibleRatio = 1.0f; + m_step = 0.0f; + + m_eventUp = EVENT_NULL; + m_eventDown = EVENT_NULL; + + m_bCapture = FALSE; +} + +// Destructeur de l'objet. + +CScroll::~CScroll() +{ + delete m_buttonUp; + delete m_buttonDown; + + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CScroll::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + CControl::Create(pos, dim, icon, eventMsg); + + MoveAdjust(); + return TRUE; +} + + +void CScroll::SetPos(FPOINT pos) +{ + CControl::SetPos(pos); + MoveAdjust(); +} + +void CScroll::SetDim(FPOINT dim) +{ + CControl::SetDim(dim); + MoveAdjust(); +} + +// Ajuste les deux boutons. + +void CScroll::MoveAdjust() +{ + CButton* pc; + FPOINT pos, dim; + + if ( m_dim.y < m_dim.x*2.0f ) // ascenseur très court ? + { + delete m_buttonUp; + m_buttonUp = 0; + + delete m_buttonDown; + m_buttonDown = 0; + } + else + { + if ( m_buttonUp == 0 ) + { + m_buttonUp = new CButton(m_iMan); + pc = (CButton*)m_buttonUp; + pc->Create(FPOINT(0.0f, 0.0f), FPOINT(0.0f, 0.0f), 49, EVENT_NULL); + pc->SetRepeat(TRUE); + m_eventUp = pc->RetEventMsg(); + } + + if ( m_buttonDown == 0 ) + { + m_buttonDown = new CButton(m_iMan); + pc = (CButton*)m_buttonDown; + pc->Create(FPOINT(0.0f, 0.0f), FPOINT(0.0f, 0.0f), 50, EVENT_NULL); + pc->SetRepeat(TRUE); + m_eventDown = pc->RetEventMsg(); + } + } + + if ( m_buttonUp != 0 ) + { + pos.x = m_pos.x; + pos.y = m_pos.y+m_dim.y-m_dim.x/0.75f; + dim.x = m_dim.x; + dim.y = m_dim.x/0.75f; + m_buttonUp->SetPos(pos); + m_buttonUp->SetDim(dim); + } + + if ( m_buttonDown != 0 ) + { + pos.x = m_pos.x; + pos.y = m_pos.y; + dim.x = m_dim.x; + dim.y = m_dim.x/0.75f; + m_buttonDown->SetPos(pos); + m_buttonDown->SetDim(dim); + } + + AdjustGlint(); +} + +// Ajuste la position du reflet. + +void CScroll::AdjustGlint() +{ + FPOINT ref; + float hButton, h; + + hButton = m_buttonUp?m_dim.x/0.75f:0.0f; + h = m_dim.y-hButton*2.0f; + + ref.x = m_pos.x; + ref.y = m_pos.y+hButton+h*m_visibleRatio+0.003f; + ref.y += h*(1.0f-m_visibleRatio)*(1.0f-m_visibleValue); + + GlintCreate(ref); +} + + + +BOOL CScroll::SetState(int state, BOOL bState) +{ + if ( state & STATE_ENABLE ) + { + if ( m_buttonUp != 0 ) m_buttonUp->SetState(state, bState); + if ( m_buttonDown != 0 ) m_buttonDown->SetState(state, bState); + } + + return CControl::SetState(state, bState); +} + +BOOL CScroll::SetState(int state) +{ + if ( state & STATE_ENABLE ) + { + if ( m_buttonUp != 0 ) m_buttonUp->SetState(state); + if ( m_buttonDown != 0 ) m_buttonDown->SetState(state); + } + + return CControl::SetState(state); +} + +BOOL CScroll::ClearState(int state) +{ + if ( state & STATE_ENABLE ) + { + if ( m_buttonUp != 0 ) m_buttonUp->ClearState(state); + if ( m_buttonDown != 0 ) m_buttonDown->ClearState(state); + } + + return CControl::ClearState(state); +} + + +// Gestion d'un événement. + +BOOL CScroll::EventProcess(const Event &event) +{ + FPOINT pos, dim; + float hButton, h, value; + + CControl::EventProcess(event); + + if ( m_buttonUp != 0 && !m_bCapture ) + { + if ( !m_buttonUp->EventProcess(event) ) return FALSE; + } + if ( m_buttonDown != 0 && !m_bCapture ) + { + if ( !m_buttonDown->EventProcess(event) ) return FALSE; + } + + if ( event.event == m_eventUp && m_step > 0.0f ) + { + m_visibleValue -= m_step; + if ( m_visibleValue < 0.0f ) m_visibleValue = 0.0f; + AdjustGlint(); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + } + + if ( event.event == m_eventDown && m_step > 0.0f ) + { + m_visibleValue += m_step; + if ( m_visibleValue > 1.0f ) m_visibleValue = 1.0f; + AdjustGlint(); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + } + + hButton = m_buttonUp?m_dim.x/0.75f:0.0f; + + if ( event.event == EVENT_LBUTTONDOWN && + (m_state & STATE_VISIBLE) && + (m_state & STATE_ENABLE) ) + { + if ( CControl::Detect(event.pos) ) + { + pos.y = m_pos.y+hButton; + dim.y = m_dim.y-hButton*2.0f; + pos.y += dim.y*(1.0f-m_visibleRatio)*(1.0f-m_visibleValue); + dim.y *= m_visibleRatio; + if ( event.pos.y < pos.y || + event.pos.y > pos.y+dim.y ) // clic hors cabine ? + { + h = (m_dim.y-hButton*2.0f)*(1.0f-m_visibleRatio); + value = 1.0f-(event.pos.y-(m_pos.y+hButton+dim.y*0.5f))/h; + if ( value < 0.0f ) value = 0.0f; + if ( value > 1.0f ) value = 1.0f; + m_visibleValue = value; + AdjustGlint(); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + } + m_bCapture = TRUE; + m_pressPos = event.pos; + m_pressValue = m_visibleValue; + } + } + + if ( event.event == EVENT_MOUSEMOVE && m_bCapture ) + { + h = (m_dim.y-hButton*2.0f)*(1.0f-m_visibleRatio); + if ( h != 0 ) + { + value = m_pressValue - (event.pos.y-m_pressPos.y)/h; + if ( value < 0.0f ) value = 0.0f; + if ( value > 1.0f ) value = 1.0f; + + if ( value != m_visibleValue ) + { + m_visibleValue = value; + AdjustGlint(); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + } + } + } + + if ( event.event == EVENT_LBUTTONUP && m_bCapture ) + { + m_bCapture = FALSE; + } + + if ( event.event == EVENT_KEYDOWN && + event.param == VK_WHEELUP && + Detect(event.pos) && + m_buttonUp != 0 ) + { + Event newEvent = event; + newEvent.event = m_buttonUp->RetEventMsg(); + m_event->AddEvent(newEvent); + } + if ( event.event == EVENT_KEYDOWN && + event.param == VK_WHEELDOWN && + Detect(event.pos) && + m_buttonDown != 0 ) + { + Event newEvent = event; + newEvent.event = m_buttonDown->RetEventMsg(); + m_event->AddEvent(newEvent); + } + + return TRUE; +} + + +// Dessine le bouton. + +void CScroll::Draw() +{ + FPOINT pos, dim, ppos, ddim; + float hButton; + int icon, n, i; + + hButton = m_buttonUp?m_dim.x/0.75f:0.0f; + + // Dessine le fond. + pos.x = m_pos.x; + pos.y = m_pos.y+hButton; + dim.x = m_dim.x; + dim.y = m_dim.y-hButton*2.0f; + if ( m_state & STATE_ENABLE ) icon = 0; + else icon = 1; + DrawVertex(pos, dim, icon); + + // Dessine la cabine. + if ( m_visibleRatio < 1.0f && (m_state & STATE_ENABLE) ) + { + pos.x += 0.003f; // ch'tite marge + pos.y += 0.003f; + dim.x -= 0.006f; + dim.y -= 0.006f; + pos.y += dim.y*(1.0f-m_visibleRatio)*(1.0f-m_visibleValue); + dim.y *= m_visibleRatio; + DrawVertex(pos, dim, 2); + + n = (int)(dim.y*0.8f/0.012f); + if ( n < 1 ) n = 1; + if ( n > 5 ) n = 5; + + ppos.x = pos.x+0.003f; + ppos.y = pos.y+(dim.y-(n-1)*0.012f-0.008f)/2.0f; + ddim.x = dim.x-0.006f; + ddim.y = 0.008f; + for ( i=0 ; iDraw(); + } + if ( m_buttonDown != 0 ) + { + m_buttonDown->Draw(); + } +} + +// Dessine un rectangle. + +void CScroll::DrawVertex(FPOINT pos, FPOINT dim, int icon) +{ + FPOINT uv1, uv2; + float ex, dp; + + if ( icon == 0 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 0.0f/256.0f; // rectangle jaune + uv1.y = 32.0f/256.0f; + uv2.x = 32.0f/256.0f; + uv2.y = 64.0f/256.0f; + ex = 8.0f/256.0f; + } + else if ( icon == 1 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 128.0f/256.0f; // rectangle gris + uv1.y = 32.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 64.0f/256.0f; + ex = 8.0f/256.0f; + } + else if ( icon == 2 ) + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 64.0f/256.0f; // rectangle bleu + uv1.y = 0.0f/256.0f; + uv2.x = 96.0f/256.0f; + uv2.y = 32.0f/256.0f; + ex = 8.0f/256.0f; + } + else + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 104.0f/256.0f; // ligne bleu - + uv1.y = 32.0f/256.0f; + uv2.x = 128.0f/256.0f; + uv2.y = 40.0f/256.0f; + ex = 0.0f; + } + + dp = 0.5f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + DrawIcon(pos, dim, uv1, uv2, ex); +} + + +void CScroll::SetVisibleValue(float value) +{ + if ( value < 0.0 ) value = 0.0f; + if ( value > 1.0 ) value = 1.0f; + m_visibleValue = value; + AdjustGlint(); +} + +float CScroll::RetVisibleValue() +{ + return m_visibleValue; +} + + +void CScroll::SetVisibleRatio(float value) +{ + if ( value < 0.1 ) value = 0.1f; + if ( value > 1.0 ) value = 1.0f; + m_visibleRatio = value; + AdjustGlint(); +} + +float CScroll::RetVisibleRatio() +{ + return m_visibleRatio; +} + + +void CScroll::SetArrowStep(float step) +{ + m_step = step; +} + +float CScroll::RetArrowStep() +{ + return m_step; +} + diff --git a/src/scroll.h b/src/scroll.h new file mode 100644 index 00000000..f8cbc7bd --- /dev/null +++ b/src/scroll.h @@ -0,0 +1,67 @@ +// scroll.h + +#ifndef _SCROLL_H_ +#define _SCROLL_H_ + + +#include "control.h" + + +class CD3DEngine; +class CButton; + + +#define SCROLL_WIDTH (15.0f/640.0f) + + + +class CScroll : public CControl +{ +public: + CScroll(CInstanceManager* iMan); + ~CScroll(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + void SetPos(FPOINT pos); + void SetDim(FPOINT dim); + + BOOL SetState(int state, BOOL bState); + BOOL SetState(int state); + BOOL ClearState(int state); + + BOOL EventProcess(const Event &event); + void Draw(); + + void SetVisibleValue(float value); + float RetVisibleValue(); + + void SetVisibleRatio(float value); + float RetVisibleRatio(); + + void SetArrowStep(float step); + float RetArrowStep(); + +protected: + void MoveAdjust(); + void AdjustGlint(); + void DrawVertex(FPOINT pos, FPOINT dim, int icon); + +protected: + CButton* m_buttonUp; + CButton* m_buttonDown; + + float m_visibleValue; + float m_visibleRatio; + float m_step; + + BOOL m_bCapture; + FPOINT m_pressPos; + float m_pressValue; + + EventMsg m_eventUp; + EventMsg m_eventDown; +}; + + +#endif //_SCROLL_H_ diff --git a/src/shortcut.cpp b/src/shortcut.cpp new file mode 100644 index 00000000..f17b58b3 --- /dev/null +++ b/src/shortcut.cpp @@ -0,0 +1,229 @@ +// shortcut.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "shortcut.h" + + + + +// Constructeur de l'objet. + +CShortcut::CShortcut(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); + m_time = 0.0f; +} + +// Destructeur de l'objet. + +CShortcut::~CShortcut() +{ + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CShortcut::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + return TRUE; +} + + +// Gestion d'un événement. + +BOOL CShortcut::EventProcess(const Event &event) +{ + CControl::EventProcess(event); + + if ( event.event == EVENT_FRAME ) + { + m_time += event.rTime; + } + + if ( event.event == EVENT_LBUTTONDOWN ) + { + if ( CControl::Detect(event.pos) ) + { + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + return FALSE; + } + } + + return TRUE; +} + + +// Dessine le bouton. + +void CShortcut::Draw() +{ + float zoom; + int icon, mode; + + icon = 0; + zoom = 0.8f; + mode = D3DSTATETTw; + if ( m_state & STATE_HILIGHT ) + { + icon = 4; + zoom = 0.9f; + mode = D3DSTATENORMAL; + } + if ( m_state & STATE_CHECK ) + { + icon = 1; + zoom = 0.8f; + mode = D3DSTATENORMAL; + } + if ( m_state & STATE_PRESS ) + { + icon = 1; + zoom = 1.0f; + mode = D3DSTATENORMAL; + } + if ( m_icon == 6 || m_icon == 7 ) // pause ou film ? + { + icon = -1; // pas de fond + zoom = 1.0f; + } + + m_engine->SetTexture("button3.tga"); + + if ( icon != -1 ) + { + m_engine->SetState(mode); + DrawVertex(icon, 0.95f); + } + + m_engine->SetState(D3DSTATETTb); + DrawVertex(m_icon, zoom); + + if ( m_state & STATE_FRAME ) + { + FPOINT p1, p2, c, uv1, uv2; + float zoom, dp; + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + + zoom = 0.9f+sinf(m_time*8.0f)*0.1f; + + p1.x = m_pos.x; + p1.y = m_pos.y; + p2.x = m_pos.x + m_dim.x; + p2.y = m_pos.y + m_dim.y; + + c.x = (p1.x+p2.x)/2.0f; + c.y = (p1.y+p2.y)/2.0f; // centre + + p1.x = (p1.x-c.x)*zoom + c.x; + p1.y = (p1.y-c.y)*zoom + c.y; + p2.x = (p2.x-c.x)*zoom + c.x; + p2.y = (p2.y-c.y)*zoom + c.y; + + p2.x -= p1.x; + p2.y -= p1.y; + + uv1.x = 176.0f/256.0f; + uv1.y = 224.0f/256.0f; + uv2.x = 192.0f/256.0f; + uv2.y = 240.0f/256.0f; + + dp = 0.5f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + DrawIcon(p1, p2, uv1, uv2); + } + + if ( (m_state & STATE_RUN) && Mod(m_time, 0.7f) >= 0.3f ) + { + FPOINT uv1, uv2; + float dp; + + m_engine->SetTexture("button3.tga"); + m_engine->SetState(D3DSTATETTw); + + uv1.x = 160.0f/256.0f; + uv1.y = 0.0f/256.0f; + uv2.x = 192.0f/256.0f; + uv2.y = 32.0f/256.0f; + + dp = 0.5f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + DrawIcon(m_pos, m_dim, uv1, uv2); + } +} + +// Dessine le tableau des vertex. + +void CShortcut::DrawVertex(int icon, float zoom) +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2 vertex[4]; // 2 triangles + FPOINT p1, p2, c; + D3DVECTOR n; + float u1, u2, v1, v2, dp; + + device = m_engine->RetD3DDevice(); + + p1.x = m_pos.x; + p1.y = m_pos.y; + p2.x = m_pos.x + m_dim.x; + p2.y = m_pos.y + m_dim.y; + + c.x = (p1.x+p2.x)/2.0f; + c.y = (p1.y+p2.y)/2.0f; // centre + + p1.x = (p1.x-c.x)*zoom + c.x; + p1.y = (p1.y-c.y)*zoom + c.y; + + p2.x = (p2.x-c.x)*zoom + c.x; + p2.y = (p2.y-c.y)*zoom + c.y; + + u1 = (32.0f/256.0f)*(icon%8); + v1 = (32.0f/256.0f)*(icon/8); // u-v texture + u2 = (32.0f/256.0f)+u1; + v2 = (32.0f/256.0f)+v1; + + dp = 0.5f/256.0f; + u1 += dp; + v1 += dp; + u2 -= dp; + v2 -= dp; + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, u1,v2); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, u1,v1); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, u2,v2); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, u2,v1); + + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); +} + diff --git a/src/shortcut.h b/src/shortcut.h new file mode 100644 index 00000000..bce6ee0f --- /dev/null +++ b/src/shortcut.h @@ -0,0 +1,34 @@ +// shortcut.h + +#ifndef _SHORTCUT_H_ +#define _SHORTCUT_H_ + + +#include "control.h" + + +class CD3DEngine; + + + +class CShortcut : public CControl +{ +public: + CShortcut(CInstanceManager* iMan); + ~CShortcut(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + BOOL EventProcess(const Event &event); + + void Draw(); + +protected: + void DrawVertex(int icon, float zoom); + +protected: + float m_time; +}; + + +#endif //_SHORTCUT_H_ diff --git a/src/slider.cpp b/src/slider.cpp new file mode 100644 index 00000000..6fbda729 --- /dev/null +++ b/src/slider.cpp @@ -0,0 +1,570 @@ +// slider.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "text.h" +#include "button.h" +#include "slider.h" + + + +#define CURSOR_WIDTH (10.0f/640.0f) +#define HOLE_WIDTH (5.0f/480.0f) + + + + +// Constructeur de l'objet. + +CSlider::CSlider(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); + + m_buttonLeft = 0; + m_buttonRight = 0; + + m_min = 0.0f; + m_max = 1.0f; + m_visibleValue = 0.0f; + m_step = 0.0f; + + m_marginButton = 0.0f; + m_bHoriz = FALSE; + + m_eventUp = EVENT_NULL; + m_eventDown = EVENT_NULL; + + m_bCapture = FALSE; +} + +// Destructeur de l'objet. + +CSlider::~CSlider() +{ + delete m_buttonLeft; + delete m_buttonRight; + + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CSlider::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + CControl::Create(pos, dim, icon, eventMsg); + + MoveAdjust(); + return TRUE; +} + + +void CSlider::SetPos(FPOINT pos) +{ + CControl::SetPos(pos); + MoveAdjust(); +} + +void CSlider::SetDim(FPOINT dim) +{ + CControl::SetDim(dim); + MoveAdjust(); +} + +void CSlider::MoveAdjust() +{ + FPOINT pos, dim; + + m_bHoriz = ( m_dim.x > m_dim.y ); + + if ( ( m_bHoriz && m_dim.x < m_dim.y*4.0f) || + (!m_bHoriz && m_dim.y < m_dim.x*4.0f) ) // slider très court ? + { + delete m_buttonLeft; + m_buttonLeft = 0; + + delete m_buttonRight; + m_buttonRight = 0; + + m_marginButton = 0.0f; + } + else + { +#if 1 + if ( m_buttonLeft == 0 ) + { + m_buttonLeft = new CButton(m_iMan); + m_buttonLeft->Create(FPOINT(0.0f, 0.0f), FPOINT(0.0f, 0.0f), m_bHoriz?55:49, EVENT_NULL); // SetRepeat(TRUE); + if ( m_state & STATE_SHADOW ) m_buttonLeft->SetState(STATE_SHADOW); + m_eventUp = m_buttonLeft->RetEventMsg(); + } + + if ( m_buttonRight == 0 ) + { + m_buttonRight = new CButton(m_iMan); + m_buttonRight->Create(FPOINT(0.0f, 0.0f), FPOINT(0.0f, 0.0f), m_bHoriz?48:50, EVENT_NULL); // >/v + m_buttonRight->SetRepeat(TRUE); + if ( m_state & STATE_SHADOW ) m_buttonRight->SetState(STATE_SHADOW); + m_eventDown = m_buttonRight->RetEventMsg(); + } + + m_marginButton = m_bHoriz?(m_dim.y*0.75f):(m_dim.x/0.75f); +#endif + } + + if ( m_buttonLeft != 0 ) + { + if ( m_bHoriz ) + { + pos.x = m_pos.x; + pos.y = m_pos.y; + dim.x = m_dim.y*0.75f; + dim.y = m_dim.y; + } + else + { + pos.x = m_pos.x; + pos.y = m_pos.y+m_dim.y-m_dim.x/0.75f; + dim.x = m_dim.x; + dim.y = m_dim.x/0.75f; + } + m_buttonLeft->SetPos(pos); + m_buttonLeft->SetDim(dim); + } + + if ( m_buttonRight != 0 ) + { + if ( m_bHoriz ) + { + pos.x = m_pos.x+m_dim.x-m_dim.y*0.75f; + pos.y = m_pos.y; + dim.x = m_dim.y*0.75f; + dim.y = m_dim.y; + } + else + { + pos.x = m_pos.x; + pos.y = m_pos.y; + dim.x = m_dim.x; + dim.y = m_dim.x/0.75f; + } + m_buttonRight->SetPos(pos); + m_buttonRight->SetDim(dim); + } + + AdjustGlint(); +} + +// Ajuste la position du reflet. + +void CSlider::AdjustGlint() +{ + FPOINT ref; + float w; + + if ( m_bHoriz ) + { + w = m_dim.x-m_marginButton*0.75f; + ref.x = m_pos.x+m_marginButton; + ref.x += (w-CURSOR_WIDTH)*m_visibleValue; + ref.y = m_pos.y+m_dim.y; + } + else + { + w = m_dim.y-m_marginButton*2.0f; + ref.y = m_pos.y+m_marginButton+CURSOR_WIDTH; + ref.y += (w-CURSOR_WIDTH)*m_visibleValue; + ref.x = m_pos.x; + } + + GlintCreate(ref); +} + + +BOOL CSlider::SetState(int state, BOOL bState) +{ + if ( (state & STATE_ENABLE) || + (state & STATE_SHADOW) ) + { + if ( m_buttonLeft != 0 ) m_buttonLeft->SetState(state, bState); + if ( m_buttonRight != 0 ) m_buttonRight->SetState(state, bState); + } + + return CControl::SetState(state, bState); +} + +BOOL CSlider::SetState(int state) +{ + if ( (state & STATE_ENABLE) || + (state & STATE_SHADOW) ) + { + if ( m_buttonLeft != 0 ) m_buttonLeft->SetState(state); + if ( m_buttonRight != 0 ) m_buttonRight->SetState(state); + } + + return CControl::SetState(state); +} + +BOOL CSlider::ClearState(int state) +{ + if ( (state & STATE_ENABLE) || + (state & STATE_SHADOW) ) + { + if ( m_buttonLeft != 0 ) m_buttonLeft->ClearState(state); + if ( m_buttonRight != 0 ) m_buttonRight->ClearState(state); + } + + return CControl::ClearState(state); +} + + +// Gestion d'un événement. + +BOOL CSlider::EventProcess(const Event &event) +{ + FPOINT pos, dim; + float value; + + if ( (m_state & STATE_VISIBLE) == 0 ) return TRUE; + + CControl::EventProcess(event); + + if ( m_buttonLeft != 0 && !m_bCapture ) + { + if ( !m_buttonLeft->EventProcess(event) ) return FALSE; + } + if ( m_buttonRight != 0 && !m_bCapture ) + { + if ( !m_buttonRight->EventProcess(event) ) return FALSE; + } + + if ( event.event == m_eventUp && m_step > 0.0f ) + { + m_visibleValue -= m_bHoriz?m_step:-m_step; + if ( m_visibleValue < 0.0f ) m_visibleValue = 0.0f; + if ( m_visibleValue > 1.0f ) m_visibleValue = 1.0f; + AdjustGlint(); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + } + + if ( event.event == m_eventDown && m_step > 0.0f ) + { + m_visibleValue += m_bHoriz?m_step:-m_step; + if ( m_visibleValue < 0.0f ) m_visibleValue = 0.0f; + if ( m_visibleValue > 1.0f ) m_visibleValue = 1.0f; + AdjustGlint(); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + } + + if ( event.event == EVENT_LBUTTONDOWN && + (m_state & STATE_VISIBLE) && + (m_state & STATE_ENABLE) ) + { + if ( CControl::Detect(event.pos) ) + { + if ( m_bHoriz ) + { + pos.x = m_pos.x+m_marginButton; + dim.x = m_dim.x-m_marginButton*2.0f; + value = (event.pos.x-pos.x-CURSOR_WIDTH/2.0f); + value /= (dim.x-CURSOR_WIDTH); + } + else + { + pos.y = m_pos.y+m_marginButton; + dim.y = m_dim.y-m_marginButton*2.0f; + value = (event.pos.y-pos.y-CURSOR_WIDTH/2.0f); + value /= (dim.y-CURSOR_WIDTH); + } + if ( value < 0.0f ) value = 0.0f; + if ( value > 1.0f ) value = 1.0f; + m_visibleValue = value; + AdjustGlint(); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + + m_bCapture = TRUE; + m_pressPos = event.pos; + m_pressValue = m_visibleValue; + } + } + + if ( event.event == EVENT_MOUSEMOVE && m_bCapture ) + { + if ( m_bHoriz ) + { + pos.x = m_pos.x+m_marginButton; + dim.x = m_dim.x-m_marginButton*2.0f; + value = (event.pos.x-pos.x-CURSOR_WIDTH/2.0f); + value /= (dim.x-CURSOR_WIDTH); + } + else + { + pos.y = m_pos.y+m_marginButton; + dim.y = m_dim.y-m_marginButton*2.0f; + value = (event.pos.y-pos.y-CURSOR_WIDTH/2.0f); + value /= (dim.y-CURSOR_WIDTH); + } + if ( value < 0.0f ) value = 0.0f; + if ( value > 1.0f ) value = 1.0f; + + if ( value != m_visibleValue ) + { + m_visibleValue = value; + AdjustGlint(); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + } + } + + if ( event.event == EVENT_LBUTTONUP && m_bCapture ) + { + m_bCapture = FALSE; + } + + if ( event.event == EVENT_KEYDOWN && + event.param == VK_WHEELUP && + Detect(event.pos) && + m_buttonLeft != 0 ) + { + Event newEvent = event; + newEvent.event = m_buttonLeft->RetEventMsg(); + m_event->AddEvent(newEvent); + } + if ( event.event == EVENT_KEYDOWN && + event.param == VK_WHEELDOWN && + Detect(event.pos) && + m_buttonRight != 0 ) + { + Event newEvent = event; + newEvent.event = m_buttonRight->RetEventMsg(); + m_event->AddEvent(newEvent); + } + + return TRUE; +} + + +// Dessine le bouton. + +void CSlider::Draw() +{ + FPOINT pos, dim, ppos, ddim, spos; + int icon; + float h; + char text[100]; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + if ( m_buttonLeft != 0 ) + { + m_buttonLeft->Draw(); + } + + if ( m_bHoriz ) + { + pos.x = m_pos.x+m_marginButton; + pos.y = m_pos.y; + dim.x = m_dim.x-m_marginButton*2.0f; + dim.y = m_dim.y; + } + else + { + pos.x = m_pos.x; + pos.y = m_pos.y+m_marginButton; + dim.x = m_dim.x; + dim.y = m_dim.y-m_marginButton*2.0f; + } + + // Dessine le fond. + if ( m_bHoriz ) + { + ppos.x = pos.x + CURSOR_WIDTH/2.0f; + ppos.y = pos.y + (dim.y-HOLE_WIDTH)/2.0f; + ddim.x = dim.x - CURSOR_WIDTH; + ddim.y = HOLE_WIDTH; + } + else + { + ppos.x = pos.x + (dim.x-HOLE_WIDTH*0.75f)/2.0f; + ppos.y = pos.y + CURSOR_WIDTH/2.0f; + ddim.x = HOLE_WIDTH*0.75f; + ddim.y = dim.y - CURSOR_WIDTH; + } + + if ( m_state & STATE_SHADOW ) + { + spos = ppos; + spos.x -= 0.005f*0.75f; + spos.y += 0.005f; + DrawShadow(spos, ddim); + } + + if ( m_state & STATE_ENABLE ) icon = 0; + else icon = 1; + DrawVertex(ppos, ddim, icon); + + // Dessine la cabine. + if ( m_state & STATE_ENABLE ) + { + if ( m_bHoriz ) + { + ppos.x = pos.x + (dim.x-CURSOR_WIDTH)*m_visibleValue; + ppos.y = pos.y; + ddim.x = CURSOR_WIDTH; + ddim.y = dim.y; + } + else + { + ppos.x = pos.x; + ppos.y = pos.y + (dim.y-CURSOR_WIDTH)*m_visibleValue; + ddim.x = dim.x; + ddim.y = CURSOR_WIDTH; + } + DrawShadow(ppos, ddim, 0.7f); + DrawVertex(ppos, ddim, 2); + } + + if ( m_buttonRight != 0 ) + { + m_buttonRight->Draw(); + } + + if ( m_bHoriz ) + { + sprintf(text, "%d", (int)(m_min+m_visibleValue*(m_max-m_min))); + h = m_engine->RetText()->RetHeight(m_fontSize, m_fontType); + pos.x = m_pos.x+m_dim.x+(10.0f/640.0f); + pos.y = m_pos.y+(m_dim.y-h)/2.0f; + m_engine->RetText()->DrawText(text, pos, m_dim.x, 1, m_fontSize, m_fontStretch, m_fontType, 0); + } + else + { + if ( m_state & STATE_VALUE ) + { + pos.x = m_pos.x+m_dim.x+4.0f/640.0f; + h = m_dim.y-m_marginButton*2.0f; + pos.y = m_pos.y+m_marginButton-4.0f/480.0f; + pos.y += (h-CURSOR_WIDTH)*m_visibleValue; + dim.x = 50.0f/640.0f; + dim.y = 16.0f/480.0f; + sprintf(text, "%d", (int)(m_min+(m_visibleValue*(m_max-m_min)))); + m_engine->RetText()->DrawText(text, pos, dim.x, 1, m_fontSize, m_fontStretch, m_fontType, 0); + } + } +} + +// Dessine un rectangle. + +void CSlider::DrawVertex(FPOINT pos, FPOINT dim, int icon) +{ + FPOINT uv1, uv2, corner; + float ex, dp; + + if ( icon == 0 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 0.0f/256.0f; // rectangle jaune + uv1.y = 32.0f/256.0f; + uv2.x = 32.0f/256.0f; + uv2.y = 64.0f/256.0f; + corner.x = 2.0f/640.0f; + corner.y = 2.0f/480.0f; + ex = 4.0f/256.0f; + } + else if ( icon == 1 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 128.0f/256.0f; // rectangle gris + uv1.y = 32.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 64.0f/256.0f; + corner.x = 2.0f/640.0f; + corner.y = 2.0f/480.0f; + ex = 4.0f/256.0f; + } + else + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 224.0f/256.0f; // curseur + uv1.y = 32.0f/256.0f; + uv2.x = 256.0f/256.0f; + uv2.y = 64.0f/256.0f; + if ( !m_bHoriz ) + { + uv1.y += 64.0f/256.0f; + uv2.y += 64.0f/256.0f; + } + corner.x = 2.0f/640.0f; + corner.y = 2.0f/480.0f; + ex = 4.0f/256.0f; + } + + dp = 0.5f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + DrawIcon(pos, dim, uv1, uv2, corner, ex); +} + + +void CSlider::SetLimit(float min, float max) +{ + m_min = min; + m_max = max; +} + +void CSlider::SetVisibleValue(float value) +{ + value = (value-m_min)/(m_max-m_min); + if ( value < 0.0 ) value = 0.0f; + if ( value > 1.0 ) value = 1.0f; + m_visibleValue = value; + AdjustGlint(); +} + +float CSlider::RetVisibleValue() +{ + return m_min+m_visibleValue*(m_max-m_min); +} + + +void CSlider::SetArrowStep(float step) +{ + m_step = step/(m_max-m_min); +} + +float CSlider::RetArrowStep() +{ + return m_step*(m_max-m_min); +} + + diff --git a/src/slider.h b/src/slider.h new file mode 100644 index 00000000..eea3070e --- /dev/null +++ b/src/slider.h @@ -0,0 +1,67 @@ +// slider.h + +#ifndef _SLIDER_H_ +#define _SLIDER_H_ + + +#include "control.h" + + +class CD3DEngine; +class CButton; + + + +class CSlider : public CControl +{ +public: + CSlider(CInstanceManager* iMan); + ~CSlider(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + void SetPos(FPOINT pos); + void SetDim(FPOINT dim); + + BOOL SetState(int state, BOOL bState); + BOOL SetState(int state); + BOOL ClearState(int state); + + BOOL EventProcess(const Event &event); + void Draw(); + + void SetLimit(float min, float max); + + void SetVisibleValue(float value); + float RetVisibleValue(); + + void SetArrowStep(float step); + float RetArrowStep(); + +protected: + void MoveAdjust(); + void AdjustGlint(); + void DrawVertex(FPOINT pos, FPOINT dim, int icon); + +protected: + CButton* m_buttonLeft; + CButton* m_buttonRight; + + float m_min; + float m_max; + float m_visibleValue; + float m_step; + + BOOL m_bHoriz; + float m_marginButton; + + BOOL m_bCapture; + FPOINT m_pressPos; + float m_pressValue; + + EventMsg m_eventUp; + EventMsg m_eventDown; +}; + + +#endif //_SLIDER_H_ diff --git a/src/sound.cpp b/src/sound.cpp new file mode 100644 index 00000000..ac06e14d --- /dev/null +++ b/src/sound.cpp @@ -0,0 +1,1640 @@ +// sound.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include "language.h" +#include "struct.h" +#include "iman.h" +#include "math3d.h" +#include "sound.h" + + +///////////////////////////////////////////////////////////////////////////// + + +#define LXIMAGE 640 +#define LYIMAGE 480 + + + +// Header .WAV file. + +struct WaveHeader +{ + BYTE RIFF[4]; // "RIFF" + DWORD dwSize; // size of data to follow + BYTE WAVE[4]; // "WAVE" + BYTE fmt_[4]; // "fmt " + DWORD dw16; // 16 + WORD wOne_0; // 1 + WORD wChnls; // number of Channels + DWORD dwSRate; // sample Rate + DWORD BytesPerSec; // sample Rate + WORD wBlkAlign; // 1 + WORD BitsPerSample; // sample size + BYTE DATA[4]; // "DATA" + DWORD dwDSize; // number of Samples +}; + + + + +// Affiche une erreur DirectSound. + +void DisplayError(char *name, Sound sound, HRESULT err) +{ + char s[100]; + unsigned int i = err; + if ( err == DS_OK ) return; + sprintf(s, "SoundError in %s, sound=%d err=%d\n", name, sound, i); + OutputDebugString(s); + + if ( err == DSERR_ALLOCATED ) OutputDebugString("DSERR_ALLOCATED\n"); + if ( err == DSERR_CONTROLUNAVAIL ) OutputDebugString("DSERR_CONTROLUNAVAIL\n"); + if ( err == DSERR_INVALIDPARAM ) OutputDebugString("DSERR_INVALIDPARAM\n"); + if ( err == DSERR_INVALIDCALL ) OutputDebugString("DSERR_INVALIDCALL\n"); + if ( err == DSERR_GENERIC ) OutputDebugString("DSERR_GENERIC\n"); + if ( err == DSERR_PRIOLEVELNEEDED ) OutputDebugString("DSERR_PRIOLEVELNEEDED\n"); + if ( err == DSERR_OUTOFMEMORY ) OutputDebugString("DSERR_OUTOFMEMORY\n"); + if ( err == DSERR_BADFORMAT ) OutputDebugString("DSERR_BADFORMAT\n"); + if ( err == DSERR_UNSUPPORTED ) OutputDebugString("DSERR_UNSUPPORTED\n"); + if ( err == DSERR_NODRIVER ) OutputDebugString("DSERR_NODRIVER\n"); + if ( err == DSERR_ALREADYINITIALIZED ) OutputDebugString("DSERR_ALREADYINITIALIZED\n"); + if ( err == DSERR_NOAGGREGATION ) OutputDebugString("DSERR_NOAGGREGATION\n"); + if ( err == DSERR_BUFFERLOST ) OutputDebugString("DSERR_BUFFERLOST\n"); + if ( err == DSERR_OTHERAPPHASPRIO ) OutputDebugString("DSERR_OTHERAPPHASPRIO\n"); + if ( err == DSERR_UNINITIALIZED ) OutputDebugString("DSERR_UNINITIALIZED\n"); + if ( err == DSERR_NOINTERFACE ) OutputDebugString("DSERR_NOINTERFACE\n"); + if ( err == DSERR_ACCESSDENIED ) OutputDebugString("DSERR_ACCESSDENIED\n"); +} + +// Retourne le nom de dossier en cours. + +void GetCurrentDir(char *pName, int lg) +{ + int i; + + strncpy(pName, _pgmptr, lg-1); + pName[lg-1] = 0; + + lg = strlen(pName); + if ( lg == 0 ) return; + + for ( i=0 ; i 0 ) + { + lg --; + if ( pName[lg] == '\\' ) + { + pName[lg+1] = 0; + break; + } + } + + if ( lg > 6 && strcmp(pName+lg-6, "\\debug\\") == 0 ) + { + pName[lg-5] = 0; // ignore le dossier \debug ! + } + + if ( lg > 6 && strcmp(pName+lg-6, "\\release\\") == 0 ) + { + pName[lg-7] = 0; // ignore le dossier \release ! + } +} + + + + +///////////////////////////////////////////////////////////////////////////// + + +// Modifie le volume midi. +// Le volume est compris entre 0 et 20 ! + +void InitMidiVolume(int volume) +{ + int nb, i, n; + MMRESULT result; + HMIDIOUT hmo = 0; + + static int table[21] = + { + 0x00000000, + 0x11111111, + 0x22222222, + 0x33333333, + 0x44444444, + 0x55555555, + 0x66666666, + 0x77777777, + 0x88888888, + 0x99999999, + 0xAAAAAAAA, + 0xBBBBBBBB, + 0xCCCCCCCC, + 0xDDDDDDDD, + 0xEEEEEEEE, + 0xF222F222, + 0xF555F555, + 0xF777F777, + 0xFAAAFAAA, + 0xFDDDFDDD, + 0xFFFFFFFF, + }; + + if ( volume < 0 ) volume = 0; + if ( volume > MAXVOLUME ) volume = MAXVOLUME; + + nb = midiOutGetNumDevs(); + for ( i=0 ; i MAXVOLUME ) volume = MAXVOLUME; + + // Open the mixer. This opens the mixer with a deviceID of 0. If you + // have a single sound card/mixer, then this will open it. If you have + // multiple sound cards/mixers, the deviceIDs will be 0, 1, 2, and + // so on. + rc = mixerOpen(&hMixer, 0,0,0,0); + if ( rc != MMSYSERR_NOERROR ) + { + return FALSE; // Couldn't open the mixer. + } + + // Initialize MIXERLINE structure. + ZeroMemory(&mxl,sizeof(mxl)); + mxl.cbStruct = sizeof(mxl); + + // Specify the line you want to get. You are getting the input line + // here. If you want to get the output line, you need to use + // MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT. + mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC; + + rc = mixerGetLineInfo((HMIXEROBJ)hMixer, &mxl, + MIXER_GETLINEINFOF_COMPONENTTYPE); + if ( rc != MMSYSERR_NOERROR ) + { + return FALSE; // Couldn't get the mixer line. + } + + // Get the control. + ZeroMemory(&mxlc, sizeof(mxlc)); + mxlc.cbStruct = sizeof(mxlc); + mxlc.dwLineID = mxl.dwLineID; +//? mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_PEAKMETER; +//? mxlc.dwControlType = MIXERCONTROL_CONTROLF_UNIFORM; + mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; + mxlc.cControls = 1; + mxlc.cbmxctrl = sizeof(mxc); + mxlc.pamxctrl = &mxc; + ZeroMemory(&mxc, sizeof(mxc)); + mxc.cbStruct = sizeof(mxc); + rc = mixerGetLineControls((HMIXEROBJ)hMixer,&mxlc, + MIXER_GETLINECONTROLSF_ONEBYTYPE); +//? MIXER_GETLINECONTROLSF_ALL); + if ( rc != MMSYSERR_NOERROR ) + { + return FALSE; // Couldn't get the control. + } + + // After successfully getting the peakmeter control, the volume range + // will be specified by mxc.Bounds.lMinimum to mxc.Bounds.lMaximum. + + MIXERCONTROLDETAILS mxcd; // Gets the control values. + MIXERCONTROLDETAILS_SIGNED volStruct; // Gets the control values. + + volStruct.lValue = volume*(mxc.Bounds.lMaximum-mxc.Bounds.lMinimum); + volStruct.lValue /= MAXVOLUME; + volStruct.lValue += mxc.Bounds.lMinimum; + + // Initialize the MIXERCONTROLDETAILS structure + ZeroMemory(&mxcd, sizeof(mxcd)); + mxcd.cbStruct = sizeof(mxcd); + mxcd.cbDetails = sizeof(volStruct); + mxcd.dwControlID = mxc.dwControlID; + mxcd.paDetails = &volStruct; + mxcd.cChannels = 1; + + // Get the current value of the peakmeter control. Typically, you + // would set a timer in your program to query the volume every 10th + // of a second or so. + rc = mixerSetControlDetails((HMIXEROBJ)hMixer, &mxcd, + MIXER_SETCONTROLDETAILSF_VALUE); + if ( rc != MMSYSERR_NOERROR ) + { + return FALSE; // Couldn't get the current volume. + } +#endif + + return TRUE; +} + + +///////////////////////////////////////////////////////////////////////////// + + +// Constructeur. + +CSound::CSound(CInstanceManager* iMan) +{ + int i; + + m_iMan = iMan; + m_iMan->AddInstance(CLASS_SOUND, this); + + m_bEnable = FALSE; + m_bState = FALSE; + m_bAudioTrack = TRUE; + m_ctrl3D = TRUE; + m_bDebugMode = FALSE; + m_MidiDeviceID = 0; + m_MIDIMusic = 0; + m_audioVolume = 20; + m_midiVolume = 15; + m_lastMidiVolume = 0; + m_listener = 0; + m_lastTime = 0.0f; + m_playTime = 0.0f; + m_uniqueStamp = 0; + m_maxSound = MAXSOUND; + m_eye = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_hWnd = 0; + + m_lpDS = NULL; + + ZeroMemory(m_channel, sizeof(SoundChannel)*MAXSOUND); + for ( i=0 ; iStop(); + m_channel[i].soundBuffer->Release(); + m_channel[i].soundBuffer = 0; + m_channel[i].bUsed = FALSE; + } + } + + if ( m_listener != NULL ) + { + m_listener->Release(); + m_listener = NULL; + } + + if ( m_lpDS != NULL ) + { + m_lpDS->Release(); + m_lpDS = NULL; + } +} + + +// Spécifie si on est en mode debug. + +void CSound::SetDebugMode(BOOL bMode) +{ + m_bDebugMode = bMode; +} + + +// Initialisation de DirectSound. + +BOOL CSound::Create(HWND hWnd, BOOL b3D) +{ + LPDIRECTSOUNDBUFFER primary; + DSBUFFERDESC dsbdesc; + DSCAPS dscaps; + WAVEFORMATEX wfx; + HRESULT hr; + + if ( !DirectSoundCreate(NULL, &m_lpDS, NULL) == DS_OK ) + { + OutputDebugString("Fatal error: DirectSoundCreate\n"); + m_bEnable = FALSE; + return FALSE; + } + +//? m_lpDS->SetCooperativeLevel(hWnd, DSSCL_NORMAL); + m_lpDS->SetCooperativeLevel(hWnd, DSSCL_PRIORITY); + + if ( !RetSound3DCap() ) b3D = FALSE; + + m_ctrl3D = FALSE; + if ( b3D ) + { + // Obtain primary buffer, asking it for 3D control. + ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) ); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRL3D; + hr = m_lpDS->CreateSoundBuffer( &dsbdesc, &primary, NULL ); + if ( hr == S_OK ) + { + m_ctrl3D = TRUE; + } + } + + if ( !m_ctrl3D ) + { + // Obtain primary buffer, without 3D control. + ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) ); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; + hr = m_lpDS->CreateSoundBuffer( &dsbdesc, &primary, NULL ); + if ( hr != S_OK ) + { + return FALSE; + } + m_ctrl3D = FALSE; + } + + if ( m_ctrl3D ) + { + hr = primary->QueryInterface( IID_IDirectSound3DListener, + (VOID**)&m_listener ); + if ( hr != S_OK ) + { + primary->Release(); + return FALSE; + } + } + + // Set primary buffer format to 44kHz and 16-bit output. + ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = 22050; +//? wfx.nSamplesPerSec = 44100; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + hr = primary->SetFormat(&wfx); + if ( hr != S_OK ) + { + DisplayError("SetFormat", SOUND_CLICK, hr); + } + + // Release the primary buffer, since it is not need anymore. + primary->Release(); + + // Search the maximum possible voices. + if ( m_ctrl3D ) + { + ZeroMemory( &dscaps, sizeof(DSCAPS) ); + dscaps.dwSize = sizeof(DSCAPS); + hr = m_lpDS->GetCaps(&dscaps); + if ( hr == DS_OK ) + { + m_maxSound = dscaps.dwMaxHwMixingAllBuffers; + if ( dscaps.dwMaxHw3DAllBuffers > 0 && + m_maxSound > (int)dscaps.dwMaxHw3DAllBuffers ) + { + m_maxSound = dscaps.dwMaxHw3DAllBuffers; + } + if ( m_maxSound > MAXSOUND ) m_maxSound = MAXSOUND; + } + } + + m_bEnable = TRUE; + m_hWnd = hWnd; + return TRUE; +} + + +// Indique s'il faut jouer les sons en 3D ou pas. + +void CSound::SetSound3D(BOOL bMode) +{ + StopAll(); + + if ( m_listener != NULL ) + { + m_listener->Release(); + m_listener = NULL; + } + + if ( m_lpDS != NULL ) + { + m_lpDS->Release(); + m_lpDS = NULL; + } + + Create(m_hWnd, bMode); +} + +BOOL CSound::RetSound3D() +{ + return m_ctrl3D; +} + +// Indique s'il est possible de jouer les sons en 3D. + +BOOL CSound::RetSound3DCap() +{ + DSCAPS dscaps; + HRESULT hr; + + ZeroMemory( &dscaps, sizeof(DSCAPS) ); + dscaps.dwSize = sizeof(DSCAPS); + hr = m_lpDS->GetCaps(&dscaps); + if ( hr != DS_OK ) return FALSE; + + return ( dscaps.dwMaxHw3DAllBuffers > 0 ); +} + + + +// Retourne l'état de DirectSound. + +BOOL CSound::RetEnable() +{ + return m_bEnable; +} + + +// Enclenche ou déclenche le son. + +void CSound::SetState(BOOL bState) +{ + m_bState = bState; +} + +// Spécifie le chamin d'accès au CD. + +void CSound::SetCDpath(char *path) +{ + strcpy(m_CDpath, path); +} + +// Enclenche ou déclenche les musiques CD-audio. + +void CSound::SetAudioTrack(BOOL bAudio) +{ + m_bAudioTrack = bAudio; +} + + +// Gestion des volumes audio (.wav) et midi (.mid). + +void CSound::SetAudioVolume(int volume) +{ + m_audioVolume = volume; +} + +int CSound::RetAudioVolume() +{ + if ( !m_bEnable ) return 0; + return m_audioVolume; +} + +void CSound::SetMidiVolume(int volume) +{ + m_midiVolume = volume; + + if ( m_bAudioTrack ) + { + InitAudioTrackVolume(m_midiVolume); + } +} + +int CSound::RetMidiVolume() +{ + if ( !m_bEnable ) return 0; + return m_midiVolume; +} + + +// Lit un fichier. + +BOOL CSound::ReadFile(Sound sound, char *metaname, char *filename) +{ + WaveHeader wavHdr; + DWORD size; + int err; + + // Open the wave file. + err = g_metafile.Open(metaname, filename); + if ( err != 0 ) return FALSE; + + // Read in the wave header. + g_metafile.Read(&wavHdr, sizeof(wavHdr)); + + // Figure out the size of the data region. + size = wavHdr.dwDSize; + + if ( m_files[sound] != 0 ) + { + free(m_files[sound]); + } + m_files[sound] = (char*)malloc(sizeof(WaveHeader)+size); + + memcpy(m_files[sound], &wavHdr, sizeof(WaveHeader)); + g_metafile.Read(m_files[sound]+sizeof(WaveHeader), size); + + // Close out the wave file. + g_metafile.Close(); + return TRUE; +} + +// Cache tous les ficheirs son (.wav). + +void CSound::CacheAll() +{ + int i; + char meta[50]; + char name[50]; + + if ( !m_bEnable ) return; + + if ( m_bDebugMode ) + { + strcpy(meta, ""); + } + else + { +#if _SCHOOL + strcpy(meta, "ceebot3.dat"); +#else + strcpy(meta, "colobot3.dat"); +#endif + } + + for ( i=0 ; iGetStatus(&status); + if ( (status&DSBSTATUS_PLAYING) == 0 ) + { + m_channel[i].priority = priority; + m_channel[i].uniqueStamp = m_uniqueStamp++; + channel = i; + bAlreadyLoaded = TRUE; + return TRUE; + } + } +#endif + + // Cherche un canal complètement libre. + for ( i=0 ; iGetStatus(&status); + if ( (status&DSBSTATUS_PLAYING) == 0 ) + { + m_channel[i].soundBuffer->Release(); + m_channel[i].soundBuffer = 0; + m_channel[i].priority = priority; + m_channel[i].uniqueStamp = m_uniqueStamp++; + + channel = i; + bAlreadyLoaded = FALSE; + return TRUE; + } + } + + // Cherche un canal utilisé moins prioritaire. + for ( i=0 ; i= priority ) continue; + + m_channel[i].soundBuffer->Stop(); + m_channel[i].soundBuffer->Release(); + m_channel[i].soundBuffer = 0; + m_channel[i].priority = priority; + m_channel[i].uniqueStamp = m_uniqueStamp++; + + channel = i; + bAlreadyLoaded = FALSE; + return TRUE; + } + + // Cherche un canal utilisé moins prioritaire ou identique. + for ( i=0 ; i priority ) continue; + + m_channel[i].soundBuffer->Stop(); + m_channel[i].soundBuffer->Release(); + m_channel[i].soundBuffer = 0; + m_channel[i].priority = priority; + m_channel[i].uniqueStamp = m_uniqueStamp++; + + channel = i; + bAlreadyLoaded = FALSE; + return TRUE; + } + + char s[100]; + sprintf(s, "Sound %d forget (priority=%d)\n", sound, priority); + OutputDebugString(s); + + return FALSE; +} + +// Reads in data from a wave file. + +BOOL CSound::ReadData(LPDIRECTSOUNDBUFFER lpDSB, Sound sound, DWORD size) +{ + LPVOID pData1; + DWORD dwData1Size; + LPVOID pData2; + DWORD dwData2Size; + HRESULT hr; + + // Lock data in buffer for writing. + hr = lpDSB->Lock(0, size, &pData1, &dwData1Size, &pData2, &dwData2Size, DSBLOCK_FROMWRITECURSOR); + if ( hr != DS_OK ) + { + return FALSE; + } + + // Read in first chunk of data. + if ( dwData1Size > 0 ) + { + memcpy(pData1, m_files[sound]+sizeof(WaveHeader), dwData1Size); + } + + // Read in second chunk if necessary. + if ( dwData2Size > 0 ) + { + memcpy(pData2, m_files[sound]+sizeof(WaveHeader)+dwData1Size, dwData2Size); + } + + // Unlock data in buffer. + hr = lpDSB->Unlock(pData1, dwData1Size, pData2, dwData2Size); + if ( hr != DS_OK ) + { + return FALSE; + } + + return TRUE; +} + +// Creates a DirectSound buffer. + +BOOL CSound::CreateSoundBuffer(int channel, DWORD size, DWORD freq, + DWORD bitsPerSample, DWORD blkAlign, + BOOL bStereo) +{ + PCMWAVEFORMAT pcmwf; + DSBUFFERDESC dsbdesc; + DS3DBUFFER bufferParams; // 3D buffer properties + HRESULT hr; + + // Set up wave format structure. + memset( &pcmwf, 0, sizeof(PCMWAVEFORMAT) ); + pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM; + pcmwf.wf.nChannels = bStereo ? 2 : 1; + pcmwf.wf.nSamplesPerSec = freq; + pcmwf.wf.nBlockAlign = (WORD)blkAlign; + pcmwf.wf.nAvgBytesPerSec = pcmwf.wf.nSamplesPerSec * pcmwf.wf.nBlockAlign; + pcmwf.wBitsPerSample = (WORD)bitsPerSample; + + // Set up DSBUFFERDESC structure. + memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); // Zero it out. + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + if ( m_ctrl3D ) + { + dsbdesc.dwFlags = DSBCAPS_CTRL3D|DSBCAPS_MUTE3DATMAXDISTANCE| + DSBCAPS_LOCDEFER| + DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLFREQUENCY; + } + else + { + dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLPAN|DSBCAPS_CTRLFREQUENCY; + } + dsbdesc.dwBufferBytes = size; + dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; + + hr = m_lpDS->CreateSoundBuffer(&dsbdesc, &m_channel[channel].soundBuffer, NULL); + if ( hr != DS_OK ) return FALSE; + + if ( m_ctrl3D ) + { + hr = m_channel[channel].soundBuffer->QueryInterface + ( + IID_IDirectSound3DBuffer, + (VOID**)&m_channel[channel].soundBuffer3D + ); + if ( hr != DS_OK ) return FALSE; + } + + m_channel[channel].bUsed = TRUE; + m_channel[channel].bMute = FALSE; + return TRUE; +} + +// Creates a DirectSound buffer from a wave file. + +BOOL CSound::CreateBuffer(int channel, Sound sound) +{ + WaveHeader* wavHdr; + DWORD size; + BOOL bStereo; + + if ( m_files[sound] == 0 ) return FALSE; + + wavHdr = (WaveHeader*)m_files[sound]; + size = wavHdr->dwDSize; + bStereo = wavHdr->wChnls > 1 ? TRUE : FALSE; + + // Create the sound buffer for the wave file. + if ( !CreateSoundBuffer(channel, size, wavHdr->dwSRate, + wavHdr->BitsPerSample, wavHdr->wBlkAlign, bStereo) ) + { + return FALSE; + } + + // Read the data for the wave file into the sound buffer. + if ( !ReadData(m_channel[channel].soundBuffer, sound, size) ) + { + return FALSE; + } + + m_channel[channel].type = sound; + + // Close out the wave file. + return TRUE; +} + +// Calcule le volume et le panoramique d'un son, en mode non 3D. + +void CSound::ComputeVolumePan2D(int channel, const D3DVECTOR &pos) +{ + float dist, a, g; + + if ( pos.x == m_eye.x && + pos.y == m_eye.y && + pos.z == m_eye.z ) + { + m_channel[channel].volume = 1.0f; // volume maximal + m_channel[channel].pan = 0.0f; // au centre + return; + } + +#if _TEEN + dist = Length(pos, m_eye); + if ( dist >= 210.0f ) // très loin ? + { + m_channel[channel].volume = 0.0f; // silence + m_channel[channel].pan = 0.0f; // au centre + return; + } + if ( dist <= 10.0f ) // très proche ? + { + m_channel[channel].volume = 1.0f; // volume maximal + m_channel[channel].pan = 0.0f; // au centre + return; + } + m_channel[channel].volume = 1.0f-((dist-10.0f)/200.0f); +#else + dist = Length(pos, m_eye); + if ( dist >= 110.0f ) // très loin ? + { + m_channel[channel].volume = 0.0f; // silence + m_channel[channel].pan = 0.0f; // au centre + return; + } + if ( dist <= 10.0f ) // très proche ? + { + m_channel[channel].volume = 1.0f; // volume maximal + m_channel[channel].pan = 0.0f; // au centre + return; + } + m_channel[channel].volume = 1.0f-((dist-10.0f)/100.0f); +#endif + + a = RotateAngle(m_lookat.x-m_eye.x, m_eye.z-m_lookat.z); + g = RotateAngle(pos.x-m_eye.x, m_eye.z-pos.z); + m_channel[channel].pan = sinf(Direction(a, g)); +} + +// Fait entendre un son au milieu. +// Retourne le canal associé ou -1. + +int CSound::Play(Sound sound, float amplitude, float frequency, BOOL bLoop) +{ + return Play(sound, m_lookat, amplitude, frequency, bLoop); +} + +// Fait entendre un son à une position donnée. +// Retourne le canal associé ou -1. + +int CSound::Play(Sound sound, D3DVECTOR pos, + float amplitude, float frequency, BOOL bLoop) +{ + DS3DBUFFER sb; + int channel, iVolume, iPan, iFreq, uniqueStamp; + BOOL bAlreadyLoaded; + DWORD flag, freq; + HRESULT err; + + if ( !m_bEnable ) return -1; + if ( !m_bState || m_audioVolume == 0 ) return -1; + +//? if ( Length(pos, m_eye) > 100.0f ) return -1; + + if ( !SearchFreeBuffer(sound, channel, bAlreadyLoaded) ) return -1; + + if ( !bAlreadyLoaded ) + { + if ( !CreateBuffer(channel, sound) ) + { + if ( m_channel[channel].bUsed && + m_channel[channel].soundBuffer != 0 ) + { + m_channel[channel].soundBuffer->Release(); + m_channel[channel].soundBuffer = 0; + } + m_channel[channel].bUsed = FALSE; + return -1; + } + } + + m_channel[channel].pos = pos; + + if ( m_ctrl3D ) + { + m_channel[channel].volume = 1.0f; + m_channel[channel].pan = 0.0f; + } + else + { + ComputeVolumePan2D(channel, pos); + } + +#if 0 + DWORD status; + m_channel[channel].soundBuffer->GetStatus(&status); + char s[100]; + sprintf(s, "Play sound=%d status=%d channel=%d flag=%d\n", sound, status, channel, bAlreadyLoaded); + OutputDebugString(s); +#endif + + m_channel[channel].oper[0].bUsed = FALSE; + m_channel[channel].startAmplitude = amplitude; + m_channel[channel].startFrequency = frequency; + m_channel[channel].changeFrequency = 1.0f; + + if ( m_ctrl3D ) + { + sb.dwSize = sizeof(DS3DBUFFER); + err = m_channel[channel].soundBuffer3D->GetAllParameters(&sb); + DisplayError("GetAllParameters", sound, err); + + sb.vPosition = pos; +//? sb.dwInsideConeAngle = 90; +//? sb.dwOutsideConeAngle = 180; +//? sb.vConeOrientation = D3DVECTOR(0.0f, 1.0f, 0.0f); + sb.lConeOutsideVolume = DSBVOLUME_MIN; +#if _TEEN + sb.flMinDistance = 50.0f; +#else + sb.flMinDistance = 20.0f; +#endif + sb.flMaxDistance = DS3D_DEFAULTMAXDISTANCE; + + err = m_channel[channel].soundBuffer3D->SetAllParameters(&sb, DS3D_IMMEDIATE); + DisplayError("SetAllParameters", sound, err); + } + + amplitude *= m_channel[channel].volume; + amplitude *= (float)m_audioVolume/MAXVOLUME; + iVolume = (int)((powf(amplitude, 0.2f)-1.0f)*10000.0f); + if ( iVolume > 0 ) iVolume = 0; + err = m_channel[channel].soundBuffer->SetVolume(iVolume); + DisplayError("SetVolume", sound, err); + + if ( !m_ctrl3D ) + { + iPan = (int)(m_channel[channel].pan*10000.0f); + err = m_channel[channel].soundBuffer->SetPan(iPan); + DisplayError("SetPan", sound, err); + } + + if ( !bAlreadyLoaded ) + { + err = m_channel[channel].soundBuffer->GetFrequency(&freq); + DisplayError("GetFrequency", sound, err); + m_channel[channel].initFrequency = freq; + } + iFreq = (int)(frequency*m_channel[channel].initFrequency); + err = m_channel[channel].soundBuffer->SetFrequency(iFreq); + DisplayError("SetFrequency", sound, err); + + err = m_channel[channel].soundBuffer->SetCurrentPosition(0); + DisplayError("SetCurrentPosition", sound, err); + + flag = bLoop?DSBPLAY_LOOPING:0; +//? flag |= DSBPLAY_LOCHARDWARE|DSBPLAY_TERMINATEBY_DISTANCE; +//? flag |= DSBPLAY_TERMINATEBY_DISTANCE; + err = m_channel[channel].soundBuffer->Play(0, 0, flag); + DisplayError("Play", sound, err); + if ( err == DSERR_BADFORMAT ) + { + iFreq = m_channel[channel].initFrequency; + err = m_channel[channel].soundBuffer->SetFrequency(iFreq); + DisplayError("SetFrequency (repeat)", sound, err); + + err = m_channel[channel].soundBuffer->Play(0, 0, flag); + DisplayError("Play (repeat)", sound, err); + } + + uniqueStamp = m_channel[channel].uniqueStamp; + return channel | ((uniqueStamp&0xffff)<<16); +} + +// Check un numéro de canal. +// Adapte le canal pour qu'il puisse être utilisé comme offset +// dans m_channel. + +BOOL CSound::CheckChannel(int &channel) +{ + int uniqueStamp; + + uniqueStamp = (channel>>16)&0xffff; + channel &= 0xffff; + + if ( !m_bEnable ) return FALSE; + if ( !m_bState || m_audioVolume == 0 ) return FALSE; + + if ( channel < 0 || channel >= m_maxSound ) return FALSE; + if ( !m_channel[channel].bUsed ) return FALSE; + + if ( m_channel[channel].uniqueStamp != uniqueStamp ) return FALSE; + + return TRUE; +} + +// Supprime toutes les enveloppes. + +BOOL CSound::FlushEnvelope(int channel) +{ + if ( !CheckChannel(channel) ) return FALSE; + + m_channel[channel].oper[0].bUsed = FALSE; + return TRUE; +} + +// Ajoute une opération d'enveloppe. + +BOOL CSound::AddEnvelope(int channel, float amplitude, float frequency, + float time, SoundNext oper) +{ + int i; + + if ( !CheckChannel(channel) ) return FALSE; + + for ( i=0 ; iSetPosition(pos.x, pos.y, pos.z, DS3D_DEFERRED); + } + else + { + ComputeVolumePan2D(channel, pos); + + if ( !m_channel[channel].oper[0].bUsed ) + { + amplitude = m_channel[channel].startAmplitude; + amplitude *= m_channel[channel].volume; + amplitude *= (float)m_audioVolume/MAXVOLUME; + iVolume = (int)((powf(amplitude, 0.2f)-1.0f)*10000.0f); + if ( iVolume > 0 ) iVolume = 0; + err = m_channel[channel].soundBuffer->SetVolume(iVolume); + DisplayError("SetVolume", m_channel[channel].type, err); + } + + pan = m_channel[channel].pan; + iPan = (int)(pan*10000.0f); + err = m_channel[channel].soundBuffer->SetPan(iPan); + DisplayError("SetPan", m_channel[channel].type, err); + } + return TRUE; +} + +// Modifie la fréquence d'un son. +// 0.5 descend d'une octave et 2.0 monte d'une octave. + +BOOL CSound::Frequency(int channel, float frequency) +{ + HRESULT err; + int iFreq; + + if ( !CheckChannel(channel) ) return FALSE; + + m_channel[channel].changeFrequency = frequency; + + if ( !m_channel[channel].oper[0].bUsed ) + { + iFreq = (int)(frequency*m_channel[channel].initFrequency); + err = m_channel[channel].soundBuffer->SetFrequency(iFreq); + DisplayError("Frequency", m_channel[channel].type, err); + } + + return TRUE; +} + +// Stoppe un son. + +BOOL CSound::Stop(int channel) +{ + if ( !CheckChannel(channel) ) return FALSE; + + m_channel[channel].soundBuffer->Stop(); + return TRUE; +} + +// Stops all sounds. + +BOOL CSound::StopAll() +{ + DWORD status; + int i; + + for ( i=0 ; iGetStatus(&status); + if ( (status&DSBSTATUS_PLAYING) == DSBSTATUS_PLAYING ) + { + m_channel[i].soundBuffer->Stop(); + } + m_channel[i].soundBuffer->Stop(); + m_channel[i].soundBuffer->Release(); + m_channel[i].soundBuffer = 0; + + m_channel[i].bUsed = FALSE; + } + return TRUE; +} + +// Silent all sounds. + +BOOL CSound::MuteAll(BOOL bMute) +{ + int i; + + for ( i=0 ; iSetVolume(-10000); // silence + continue; + } + + m_channel[i].oper[0].currentTime += rTime; + + progress = m_channel[i].oper[0].currentTime / m_channel[i].oper[0].totalTime; + if ( progress > 1.0f ) progress = 1.0f; + + volume = progress; + volume *= m_channel[i].oper[0].finalAmplitude-m_channel[i].startAmplitude; + volume += m_channel[i].startAmplitude; + volume *= m_channel[i].volume; + volume *= (float)m_audioVolume/MAXVOLUME; + iVolume = (int)((powf(volume, 0.2f)-1.0f)*10000.0f); + if ( iVolume > 0 ) iVolume = 0; + m_channel[i].soundBuffer->SetVolume(iVolume); + + freq = progress; + freq *= m_channel[i].oper[0].finalFrequency-m_channel[i].startFrequency; + freq += m_channel[i].startFrequency; + freq *= m_channel[i].changeFrequency; + iFreq = (int)(freq*m_channel[i].initFrequency); + err = m_channel[i].soundBuffer->SetFrequency(iFreq); + DisplayError("FrameMove::Frequency", m_channel[i].type, err); + + if ( m_channel[i].oper[0].currentTime >= + m_channel[i].oper[0].totalTime ) + { + next = m_channel[i].oper[0].nextOper; + + if ( next == SOPER_LOOP ) + { + m_channel[i].oper[0].currentTime = 0.0f; + } + else + { + OperNext(i); + + if ( next == SOPER_STOP ) + { + m_channel[i].soundBuffer->Stop(); + } + } + } + } + + m_lastTime += rTime; + if ( m_lastTime >= 0.05f && m_listener != 0 ) + { + m_lastTime = 0.0f; + m_listener->CommitDeferredSettings(); + } +} + +// Spécifie la position de l'auditeur. +// Doit être appelé chaque fois que la caméra se déplace. + +void CSound::SetListener(D3DVECTOR eye, D3DVECTOR lookat) +{ + DS3DLISTENER listenerParams; + HRESULT err; + float amplitude, pan; + int i, iVolume, iPan; + + m_eye = eye; + m_lookat = lookat; + + if ( m_listener == 0 ) + { + if ( m_ctrl3D ) return; + + for ( i=0 ; i 0 ) iVolume = 0; + err = m_channel[i].soundBuffer->SetVolume(iVolume); + DisplayError("SetVolume", m_channel[i].type, err); + } + + pan = m_channel[i].pan; + iPan = (int)(pan*10000.0f); + err = m_channel[i].soundBuffer->SetPan(iPan); + DisplayError("SetPan", m_channel[i].type, err); + } + return; + } + + // Get listener parameters. + listenerParams.dwSize = sizeof(DS3DLISTENER); + m_listener->GetAllParameters(&listenerParams); + + listenerParams.vPosition = eye; + listenerParams.vOrientFront = lookat-eye; + listenerParams.vOrientTop = D3DVECTOR(0.0f, 1.0f, 0.0f); + listenerParams.flDistanceFactor = 10.0f; + listenerParams.flRolloffFactor = 1.0f; + + m_listener->SetAllParameters(&listenerParams, DS3D_DEFERRED); +} + + + + +// Uses MCI to play a MIDI file. The window procedure +// is notified when playback is complete. + +BOOL CSound::PlayMusic(int rank, BOOL bRepeat) +{ + MCI_OPEN_PARMS mciOpenParms; + MCI_PLAY_PARMS mciPlayParms; + DWORD dwReturn; + char filename[MAX_PATH]; + + m_bRepeatMusic = bRepeat; + m_playTime = 0.0f; + + if ( m_midiVolume == 0 ) return TRUE; + + if ( m_bAudioTrack ) + { + return PlayAudioTrack(rank); + } + + if ( !m_bEnable ) return TRUE; + InitMidiVolume(m_midiVolume); + m_lastMidiVolume = m_midiVolume; + + GetCurrentDir(filename, MAX_PATH-30); + sprintf(filename+strlen(filename), "sound\\music%.3d.blp", rank-1); + + // Open the device by specifying the device and filename. + // MCI will attempt to choose the MIDI mapper as the output port. + mciOpenParms.lpstrDeviceType = "sequencer"; + mciOpenParms.lpstrElementName = filename; + dwReturn = mciSendCommand(NULL, + MCI_OPEN, + MCI_OPEN_TYPE|MCI_OPEN_ELEMENT, + (DWORD)(LPVOID)&mciOpenParms); + if ( dwReturn != 0 ) + { + mciGetErrorString(dwReturn, filename, 128); + // Failed to open device. Don't close it; just return error. + return FALSE; + } + + // The device opened successfully; get the device ID. + m_MidiDeviceID = mciOpenParms.wDeviceID; + + // Begin playback. + mciPlayParms.dwCallback = (DWORD)m_hWnd; + dwReturn = mciSendCommand(m_MidiDeviceID, + MCI_PLAY, + MCI_NOTIFY, + (DWORD)(LPVOID)&mciPlayParms); + if ( dwReturn != 0 ) + { + mciGetErrorString(dwReturn, filename, 128); + StopMusic(); + return FALSE; + } + + m_MIDIMusic = rank; + return TRUE; +} + +// Uses MCI to play a CD-audio track. The window procedure +// is notified when playback is complete. +// The rank parameter is in space [1..n] ! +// For CD mix (data/audio), it will be [2..n] ! + +BOOL CSound::PlayAudioTrack(int rank) +{ +#if _SOUNDTRACKS + MCI_OPEN_PARMS mciOpenParms; + MCI_PLAY_PARMS mciPlayParms; + MCI_SET_PARMS mciSetParms; + DWORD dwReturn; + char filename[MAX_PATH]; + char device[10]; + + if ( !m_bEnable ) return TRUE; +//? if ( m_midiVolume == 0 ) return TRUE; + InitAudioTrackVolume(m_midiVolume); + m_lastMidiVolume = m_midiVolume; + + // Open the device by specifying the device and filename. + // MCI will attempt to choose the MIDI mapper as the output port. + memset(&mciOpenParms, 0, sizeof(MCI_OPEN_PARMS)); +//? mciOpenParms.lpstrDeviceType = (LPCTSTR)MCI_DEVTYPE_CD_AUDIO; +//? dwReturn = mciSendCommand(NULL, +//? MCI_OPEN, +//? MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID, +//? (DWORD)(LPVOID)&mciOpenParms); + mciOpenParms.lpstrDeviceType = (LPCTSTR)MCI_DEVTYPE_CD_AUDIO; + if ( m_CDpath[0] == 0 ) + { + dwReturn = mciSendCommand(NULL, + MCI_OPEN, + MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID, + (DWORD)(LPVOID)&mciOpenParms); + } + else + { + device[0] = m_CDpath[0]; + device[1] = ':'; + device[2] = 0; + mciOpenParms.lpstrElementName = device; + dwReturn = mciSendCommand(NULL, + MCI_OPEN, + MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID|MCI_OPEN_ELEMENT, + (DWORD)(LPVOID)&mciOpenParms); + } + if ( dwReturn != 0 ) + { + mciGetErrorString(dwReturn, filename, 128); + // Failed to open device. Don't close it; just return error. + return FALSE; + } + + // The device opened successfully; get the device ID. + m_MidiDeviceID = mciOpenParms.wDeviceID; + + // Set the time format to track/minute/second/frame (TMSF). + memset(&mciSetParms, 0, sizeof(MCI_SET_PARMS)); + mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF; + dwReturn = mciSendCommand(m_MidiDeviceID, + MCI_SET, + MCI_SET_TIME_FORMAT, + (DWORD)&mciSetParms); + if ( dwReturn != 0 ) + { + mciGetErrorString(dwReturn, filename, 128); + StopMusic(); + return FALSE; + } + + // Begin playback. + memset(&mciPlayParms, 0, sizeof(MCI_PLAY_PARMS)); + mciPlayParms.dwCallback = (DWORD)m_hWnd; + mciPlayParms.dwFrom = MCI_MAKE_TMSF(rank+0, 0, 0, 0); + mciPlayParms.dwTo = MCI_MAKE_TMSF(rank+1, 0, 0, 0); + dwReturn = mciSendCommand(m_MidiDeviceID, + MCI_PLAY, + MCI_NOTIFY|MCI_FROM|MCI_TO, + (DWORD)(LPVOID)&mciPlayParms); + if ( dwReturn != 0 ) + { + mciGetErrorString(dwReturn, filename, 128); + StopMusic(); + return FALSE; + } + + m_MIDIMusic = rank; +#endif + return TRUE; +} + +// Restart the MIDI player. + +BOOL CSound::RestartMusic() +{ + if ( !m_bRepeatMusic ) return FALSE; + + OutputDebugString("RestartMusic\n"); + if ( !m_bEnable ) return TRUE; +//? if ( m_midiVolume == 0 ) return TRUE; + if ( m_MIDIMusic == 0 ) return FALSE; + if ( m_playTime < 5.0f ) return FALSE; + + return PlayMusic(m_MIDIMusic, TRUE); +} + +// Shuts down the MIDI player. + +void CSound::SuspendMusic() +{ + if ( !m_bEnable ) return; + +//? if ( m_MidiDeviceID && m_midiVolume != 0 ) + if ( m_MidiDeviceID ) + { + if ( m_bAudioTrack ) mciSendCommand(m_MidiDeviceID, MCI_STOP, 0, NULL); + mciSendCommand(m_MidiDeviceID, MCI_CLOSE, 0, NULL); + } + m_MidiDeviceID = 0; +} + +// Shuts down the MIDI player. + +void CSound::StopMusic() +{ + SuspendMusic(); + m_MIDIMusic = 0; +} + +// Retourne TRUE si une musique est en cours. + +BOOL CSound::IsPlayingMusic() +{ + return (m_MIDIMusic != 0); +} + +// Adapte le volume de la musique en cours, si nécessaire. + +void CSound::AdaptVolumeMusic() +{ + if ( m_midiVolume != m_lastMidiVolume ) + { + if ( m_bAudioTrack ) + { + InitAudioTrackVolume(m_midiVolume); + } + else + { + InitMidiVolume(m_midiVolume); + } + m_lastMidiVolume = m_midiVolume; + RestartMusic(); + } +} + diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 00000000..1f425627 --- /dev/null +++ b/src/sound.h @@ -0,0 +1,225 @@ +// sound.h + + +#include + + +#define MAXFILES 200 +#define MAXSOUND 32 +#define MAXVOLUME 20 +#define MAXOPER 4 + +class CInstanceManager; + + +enum Sound +{ + SOUND_CLICK = 0, + SOUND_BOUM = 1, + SOUND_EXPLO = 2, + SOUND_FLYh = 3, // human + SOUND_FLY = 4, + SOUND_STEPs = 5, // smooth + SOUND_MOTORw = 6, // wheel + SOUND_MOTORt = 7, // tank + SOUND_MOTORr = 8, // roller + SOUND_ERROR = 9, + SOUND_CONVERT = 10, + SOUND_ENERGY = 11, + SOUND_PLOUF = 12, + SOUND_BLUP = 13, + SOUND_WARNING = 14, + SOUND_DERRICK = 15, + SOUND_LABO = 16, + SOUND_STATION = 17, + SOUND_REPAIR = 18, + SOUND_RESEARCH = 19, + SOUND_INSECTs = 20, // spider + SOUND_BURN = 21, + SOUND_TZOING = 22, + SOUND_GGG = 23, + SOUND_MANIP = 24, + SOUND_FIRE = 25, // tir avec fireball + SOUND_HUMAN1 = 26, // respiration + SOUND_STEPw = 27, // water + SOUND_SWIM = 28, + SOUND_RADAR = 29, + SOUND_BUILD = 30, + SOUND_ALARM = 31, // alarme énergie + SOUND_SLIDE = 32, + SOUND_EXPLOi = 33, // insect + SOUND_INSECTa = 34, // ant + SOUND_INSECTb = 35, // bee + SOUND_INSECTw = 36, // worm + SOUND_INSECTm = 37, // mother + SOUND_TREMBLE = 38, + SOUND_PSHHH = 39, + SOUND_NUCLEAR = 40, + SOUND_INFO = 41, + SOUND_OPEN = 42, + SOUND_CLOSE = 43, + SOUND_FACTORY = 44, + SOUND_EGG = 45, + SOUND_MOTORs = 46, // submarine + SOUND_MOTORi = 47, // insect (pattes) + SOUND_SHIELD = 48, + SOUND_FIREi = 49, // tir avec orgaball (insect) + SOUND_GUNDEL = 50, + SOUND_PSHHH2 = 51, // shield + SOUND_MESSAGE = 52, + SOUND_BOUMm = 53, // metal + SOUND_BOUMv = 54, // vegetal + SOUND_BOUMs = 55, // smooth + SOUND_EXPLOl = 56, // little + SOUND_EXPLOlp = 57, // little power + SOUND_EXPLOp = 58, // power + SOUND_STEPh = 59, // hard + SOUND_STEPm = 60, // metal + SOUND_POWERON = 61, + SOUND_POWEROFF = 62, + SOUND_AIE = 63, + SOUND_WAYPOINT = 64, + SOUND_RECOVER = 65, + SOUND_DEADi = 66, + SOUND_JOSTLE = 67, + SOUND_GFLAT = 68, + SOUND_DEADg = 69, // mort par balle + SOUND_DEADw = 70, // mort noyé + SOUND_FLYf = 71, // reactor fail + SOUND_ALARMt = 72, // alarme température + SOUND_FINDING = 73, // trouvé un objet caché + SOUND_THUMP = 74, + SOUND_TOUCH = 75, + SOUND_BLITZ = 76, + SOUND_MUSHROOM = 77, + SOUND_FIREp = 78, // tir avec phazer + SOUND_EXPLOg1 = 79, // impact gun 1 + SOUND_EXPLOg2 = 80, // impact gun 2 + SOUND_MOTORd = 81, // moteur à friction +}; + +enum SoundNext +{ + SOPER_CONTINUE = 1, + SOPER_STOP = 2, + SOPER_LOOP = 3, +}; + +typedef struct +{ + char bUsed; + float finalAmplitude; + float finalFrequency; + float totalTime; + float currentTime; + SoundNext nextOper; +} +SoundOper; + +typedef struct +{ + char bUsed; // buffer utilisé ? + char bMute; // silence ? + Sound type; // SOUND_* + int priority; // si grand -> important + D3DVECTOR pos; // position dans l'espace + unsigned short uniqueStamp; // marqueur unique + LPDIRECTSOUNDBUFFER soundBuffer; + LPDIRECTSOUND3DBUFFER soundBuffer3D; + float startAmplitude; + float startFrequency; + float changeFrequency; + int initFrequency; + float volume; // 2D: volume 1..0 selon position + float pan; // 2D: pan -1..+1 selon position + SoundOper oper[MAXOPER]; +} +SoundChannel; + + + +class CSound +{ +public: + CSound(CInstanceManager* iMan); + ~CSound(); + + void SetDebugMode(BOOL bMode); + BOOL Create(HWND hWnd, BOOL b3D); + void CacheAll(); + + void SetState(BOOL bState); + BOOL RetEnable(); + + void SetCDpath(char *path); + void SetAudioTrack(BOOL bAudio); + + void SetSound3D(BOOL bMode); + BOOL RetSound3D(); + BOOL RetSound3DCap(); + + void SetAudioVolume(int volume); + int RetAudioVolume(); + void SetMidiVolume(int volume); + int RetMidiVolume(); + + void SetListener(D3DVECTOR eye, D3DVECTOR lookat); + void FrameMove(float rTime); + + int Play(Sound sound, float amplitude=1.0f, float frequency=1.0f, BOOL bLoop=FALSE); + int Play(Sound sound, D3DVECTOR pos, float amplitude=1.0f, float frequency=1.0f, BOOL bLoop=FALSE); + BOOL FlushEnvelope(int channel); + BOOL AddEnvelope(int channel, float amplitude, float frequency, float time, SoundNext oper); + BOOL Position(int channel, D3DVECTOR pos); + BOOL Frequency(int channel, float frequency); + BOOL Stop(int channel); + BOOL StopAll(); + BOOL MuteAll(BOOL bMute); + + BOOL PlayMusic(int rank, BOOL bRepeat); + BOOL RestartMusic(); + void SuspendMusic(); + void StopMusic(); + BOOL IsPlayingMusic(); + void AdaptVolumeMusic(); + +protected: + BOOL CheckChannel(int &channel); + BOOL CreateSoundBuffer(int channel, DWORD size, DWORD freq, DWORD bitsPerSample, DWORD blkAlign, BOOL bStereo); + BOOL ReadData(LPDIRECTSOUNDBUFFER lpDSB, Sound sound, DWORD size); + BOOL CreateBuffer(int channel, Sound sound); + void ComputeVolumePan2D(int channel, const D3DVECTOR &pos); + BOOL ReadFile(Sound sound, char *metaname, char *filename); + int RetPriority(Sound sound); + BOOL SearchFreeBuffer(Sound sound, int &channel, BOOL &bAlreadyLoaded); + void OperNext(int channel); + BOOL PlayAudioTrack(int rank); + +protected: + CInstanceManager* m_iMan; + + HWND m_hWnd; + BOOL m_bEnable; + BOOL m_bState; + BOOL m_bAudioTrack; + BOOL m_ctrl3D; + BOOL m_bDebugMode; + LPDIRECTSOUND m_lpDS; + LPDIRECTSOUND3DLISTENER m_listener; + SoundChannel m_channel[MAXSOUND]; + char* m_files[MAXFILES]; + UINT m_MidiDeviceID; + int m_MIDIMusic; + BOOL m_bRepeatMusic; + int m_audioVolume; + int m_midiVolume; + int m_lastMidiVolume; + D3DVECTOR m_eye; + D3DVECTOR m_lookat; + float m_lastTime; + float m_playTime; + int m_uniqueStamp; + int m_maxSound; + char m_CDpath[100]; +}; + diff --git a/src/struct.h b/src/struct.h new file mode 100644 index 00000000..d81b30fb --- /dev/null +++ b/src/struct.h @@ -0,0 +1,57 @@ +// struct.h + +#ifndef _STRUCT_H_ +#define _STRUCT_H_ + +#include + + +#define NAN 999999 + +#define D3DFVF_VERTEX2 (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2) + +struct D3DVERTEX2 +{ + float x,y,z; + float nx,ny,nz; + float tu, tv; + float tu2, tv2; + + D3DVERTEX2() { } + D3DVERTEX2(const D3DVECTOR& _v, const D3DVECTOR& _n, float _tu=0.0f, float _tv=0.0f, float _tu2=0.0f, float _tv2=0.0f) + { + x = _v.x; + y = _v.y; + z = _v.z; + nx = _n.x; + ny = _n.y; + nz = _n.z; + tu = _tu; + tv = _tv; + tu2 = _tu2; + tv2 = _tv2; + } +}; + + +struct FPOINT +{ + float x; + float y; + + FPOINT() { } + FPOINT(float _x, float _y) + { + x = _x; + y = _y; + } +}; + + +struct ColorHSV +{ + float h,s,v; +}; + + +#endif //_STRUCT_H_ diff --git a/src/studio.cpp b/src/studio.cpp new file mode 100644 index 00000000..aa6907dc --- /dev/null +++ b/src/studio.cpp @@ -0,0 +1,1649 @@ +// studio.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "language.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "restext.h" +#include "math3d.h" +#include "robotmain.h" +#include "object.h" +#include "camera.h" +#include "sound.h" +#include "script.h" +#include "interface.h" +#include "button.h" +#include "check.h" +#include "slider.h" +#include "edit.h" +#include "list.h" +#include "label.h" +#include "group.h" +#include "window.h" +#include "text.h" +#include "cbottoken.h" +#include "studio.h" + + + + +// Constructeur de l'objet. + +CStudio::CStudio(CInstanceManager* iMan) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_STUDIO, this); + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_event = (CEvent*)m_iMan->SearchInstance(CLASS_EVENT); + m_interface = (CInterface*)m_iMan->SearchInstance(CLASS_INTERFACE); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + + m_bEditMaximized = FALSE; + m_bEditMinimized = FALSE; + + m_time = 0.0f; + m_bRealTime = TRUE; + m_bRunning = FALSE; + m_fixInfoTextTime = 0.0f; + m_helpFilename[0] = 0; + m_dialog = SD_NULL; +} + +// Destructeur de l'objet. + +CStudio::~CStudio() +{ + m_iMan->DeleteInstance(CLASS_STUDIO, this); +} + + +// Gestion d'un événement. + +BOOL CStudio::EventProcess(const Event &event) +{ + CWindow* pw; + CEdit* edit; + CSlider* slider; + char res[100]; + + if ( m_dialog != SD_NULL ) // dialogue existe ? + { + return EventDialog(event); + } + + if ( event.event == EVENT_FRAME ) + { + EventFrame(event); + } + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw == 0 ) return FALSE; + + edit = (CEdit*)pw->SearchControl(EVENT_STUDIO_EDIT); + if ( edit == 0 ) return FALSE; + + if ( event.event == pw->RetEventMsgClose() ) + { + Event newEvent = event; + newEvent.event = EVENT_STUDIO_OK; + m_event->AddEvent(newEvent); + } + + if ( event.event == EVENT_STUDIO_EDIT ) // texte modifié ? + { + ColorizeScript(edit); + } + + if ( event.event == EVENT_STUDIO_LIST ) // liste cliquée ? + { + m_main->StartDisplayInfo(m_helpFilename, -1); + } + + if ( event.event == EVENT_STUDIO_NEW ) // nouveau ? + { + m_script->New(edit, ""); + } + + if ( event.event == EVENT_STUDIO_OPEN ) // ouvrir ? + { + StartDialog(SD_OPEN); + } + if ( event.event == EVENT_STUDIO_SAVE ) // enregistrer ? + { + StartDialog(SD_SAVE); + } + + if ( event.event == EVENT_STUDIO_UNDO ) // annuler ? + { + edit->Undo(); + } + if ( event.event == EVENT_STUDIO_CUT ) // couper ? + { + edit->Cut(); + } + if ( event.event == EVENT_STUDIO_COPY ) // copier ? + { + edit->Copy(); + } + if ( event.event == EVENT_STUDIO_PASTE ) // coller ? + { + edit->Paste(); + } + + if ( event.event == EVENT_STUDIO_SIZE ) // taille ? + { + slider = (CSlider*)pw->SearchControl(EVENT_STUDIO_SIZE); + if ( slider == 0 ) return FALSE; + m_main->SetFontSize(9.0f+slider->RetVisibleValue()*6.0f); + ViewEditScript(); + } + + if ( event.event == EVENT_STUDIO_TOOL && // instructions ? + m_dialog == SD_NULL ) + { + m_main->StartDisplayInfo(SATCOM_HUSTON, FALSE); + } + if ( event.event == EVENT_STUDIO_HELP && // aide ? + m_dialog == SD_NULL ) + { + m_main->StartDisplayInfo(SATCOM_PROG, FALSE); + } + + if ( event.event == EVENT_STUDIO_COMPILE ) // compile ? + { + char buffer[100]; + + if ( m_script->GetScript(edit) ) // compile + { + GetResource(RES_TEXT, RT_STUDIO_COMPOK, res); + SetInfoText(res, FALSE); + } + else + { + m_script->GetError(buffer); + SetInfoText(buffer, FALSE); + } + } + + if ( event.event == EVENT_STUDIO_RUN ) // run/stop ? + { + if ( m_script->IsRunning() ) + { + Event newEvent = event; + newEvent.event = EVENT_OBJECT_PROGSTOP; + m_event->AddEvent(newEvent); // stoppe + } + else + { + if ( m_script->GetScript(edit) ) // compile + { + SetInfoText("", FALSE); + + Event newEvent = event; + newEvent.event = EVENT_OBJECT_PROGSTART; + m_event->AddEvent(newEvent); // start + } + else + { + char buffer[100]; + m_script->GetError(buffer); + SetInfoText(buffer, FALSE); + } + } + } + + if ( event.event == EVENT_STUDIO_REALTIME ) // temps réel ? + { + m_bRealTime = !m_bRealTime; + m_script->SetStepMode(!m_bRealTime); + UpdateFlux(); + UpdateButtons(); + } + + if ( event.event == EVENT_STUDIO_STEP ) // step ? + { + m_script->Step(event); + } + + if ( event.event == EVENT_KEYDOWN ) + { + if ( event.param == m_engine->RetKey(KEYRANK_CBOT, 0) || + event.param == m_engine->RetKey(KEYRANK_CBOT, 1) ) + { + if ( m_helpFilename[0] != 0 ) + { + m_main->StartDisplayInfo(m_helpFilename, -1); + } + } + } + + if ( event.event == EVENT_WINDOW3 ) // fenêtre déplacée ? + { + m_editActualPos = m_editFinalPos = pw->RetPos(); + m_editActualDim = m_editFinalDim = pw->RetDim(); + m_main->SetWindowPos(m_editActualPos); + m_main->SetWindowDim(m_editActualDim); + AdjustEditScript(); + } + if ( event.event == pw->RetEventMsgReduce() ) + { + if ( m_bEditMinimized ) + { + m_editFinalPos = m_main->RetWindowPos(); + m_editFinalDim = m_main->RetWindowDim(); + m_bEditMinimized = FALSE; + m_bEditMaximized = FALSE; + } + else + { + m_editFinalPos.x = 0.00f; + m_editFinalPos.y = -0.44f; + m_editFinalDim.x = 1.00f; + m_editFinalDim.y = 0.50f; + m_bEditMinimized = TRUE; + m_bEditMaximized = FALSE; + } + m_main->SetEditFull(m_bEditMaximized); + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw != 0 ) + { + pw->SetMaximized(m_bEditMaximized); + pw->SetMinimized(m_bEditMinimized); + } + } + if ( event.event == pw->RetEventMsgFull() ) + { + if ( m_bEditMaximized ) + { + m_editFinalPos = m_main->RetWindowPos(); + m_editFinalDim = m_main->RetWindowDim(); + m_bEditMinimized = FALSE; + m_bEditMaximized = FALSE; + } + else + { + m_editFinalPos.x = 0.00f; + m_editFinalPos.y = 0.00f; + m_editFinalDim.x = 1.00f; + m_editFinalDim.y = 1.00f; + m_bEditMinimized = FALSE; + m_bEditMaximized = TRUE; + } + m_main->SetEditFull(m_bEditMaximized); + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw != 0 ) + { + pw->SetMaximized(m_bEditMaximized); + pw->SetMinimized(m_bEditMinimized); + } + } + + return TRUE; +} + + +// Fait évoluer une valeur en fonction du temps écoulé. + +float Evolution(float final, float actual, float time) +{ + float value; + + value = actual + (final-actual)*time; + + if ( final > actual ) + { + if ( value > final ) value = final; // ne dépasse pas + } + else + { + if ( value < final ) value = final; // ne dépasse pas + } + + return value; +} + +// Fait évoluer le studio selon le temps écoulé. + +BOOL CStudio::EventFrame(const Event &event) +{ + CWindow* pw; + CEdit* edit; + CList* list; + float time; + int cursor1, cursor2, iCursor1, iCursor2; + char res[100]; + + m_time += event.rTime; + m_fixInfoTextTime -= event.rTime; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw == 0 ) return FALSE; + + edit = (CEdit*)pw->SearchControl(EVENT_STUDIO_EDIT); + if ( edit == 0 ) return FALSE; + + list = (CList*)pw->SearchControl(EVENT_STUDIO_LIST); + if ( list == 0 ) return FALSE; + + if ( !m_script->IsRunning() && m_bRunning ) // arrêt ? + { + m_bRunning = FALSE; + UpdateFlux(); // stop + AdjustEditScript(); + GetResource(RES_TEXT, RT_STUDIO_PROGSTOP, res); + SetInfoText(res, FALSE); + + Event newEvent = event; + newEvent.event = EVENT_OBJECT_PROGSTOP; + m_event->AddEvent(newEvent); // stoppe + } + + if ( m_script->IsRunning() && !m_bRunning ) // départ ? + { + m_bRunning = TRUE; + UpdateFlux(); // run + AdjustEditScript(); + } + UpdateButtons(); + + if ( m_bRunning ) + { + m_script->GetCursor(cursor1, cursor2); + edit->GetCursor(iCursor1, iCursor2); + if ( cursor1 != iCursor1 || + cursor2 != iCursor2 ) // curseurs changés ? + { + edit->SetCursor(cursor1, cursor2); // montre où en est l'exécution + edit->ShowSelect(); + } + + m_script->UpdateList(list); // met à jour la liste des variables + } + else + { + SearchToken(edit); + } + + if ( m_editFinalPos.x != m_editActualPos.x || + m_editFinalPos.y != m_editActualPos.y || + m_editFinalDim.x != m_editActualDim.x || + m_editFinalDim.y != m_editActualDim.y ) + { + time = event.rTime*20.0f; + m_editActualPos.x = Evolution(m_editFinalPos.x, m_editActualPos.x, time); + m_editActualPos.y = Evolution(m_editFinalPos.y, m_editActualPos.y, time); + m_editActualDim.x = Evolution(m_editFinalDim.x, m_editActualDim.x, time); + m_editActualDim.y = Evolution(m_editFinalDim.y, m_editActualDim.y, time); + AdjustEditScript(); + } + + return TRUE; +} + + +// Indique si un caractère fait partie d'un mot. + +BOOL IsToken(int character) +{ + char c; + + c = tolower(RetNoAccent(character)); + + return ( (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + c == '_' ); +} + +// Cherche si le curseur est sur un mot-clé. + +void CStudio::SearchToken(CEdit* edit) +{ + ObjectType type; + int len, cursor1, cursor2, i, character, level; + char* text; + char token[100]; + + text = edit->RetText(); + len = edit->RetTextLength(); + edit->GetCursor(cursor1, cursor2); + + i = cursor1; + if ( i > 0 ) + { + character = (unsigned char)text[i-1]; + if ( !IsToken(character) ) + { + level = 1; + while ( i > 0 ) + { + character = (unsigned char)text[i-1]; + if ( character == ')' ) + { + level ++; + } + else if ( character == '(' ) + { + level --; + if ( level == 0 ) break; + } + i --; + } + if ( level > 0 ) + { + m_helpFilename[0] = 0; + SetInfoText("", TRUE); + return; + } + while ( i > 0 ) + { + character = (unsigned char)text[i-1]; + if ( IsToken(character) ) break; + i --; + } + } + } + + while ( i > 0 ) + { + character = (unsigned char)text[i-1]; + if ( !IsToken(character) ) break; + i --; + } + cursor2 = i; + + while ( i < len ) + { + character = (unsigned char)text[i]; + if ( !IsToken(character) ) break; + i ++; + } + cursor1 = i; + len = cursor1-cursor2; + + if ( len > 100-1 ) len = 100-1; + for ( i=0 ; iColorizeScript(edit); +} + + +// Début de l'édition d'un programme. + +void CStudio::StartEditScript(CScript *script, char* name, int rank) +{ + FPOINT pos, dim; + CWindow* pw; + CEdit* edit; + CButton* button; + CSlider* slider; + CList* list; + char res[100]; + + m_script = script; + m_rank = rank; + + m_main->SetEditLock(TRUE, TRUE); + m_main->SetEditFull(FALSE); + m_bInitPause = m_engine->RetPause(); + m_main->SetSpeed(1.0f); + m_editCamera = m_camera->RetType(); + m_camera->SetType(CAMERA_EDIT); + + m_bRunning = m_script->IsRunning(); + m_bRealTime = m_bRunning; + m_script->SetStepMode(!m_bRealTime); + + button = (CButton*)m_interface->SearchControl(EVENT_BUTTON_QUIT); + if ( button != 0 ) + { + button->ClearState(STATE_VISIBLE); + } + + pos = m_editFinalPos = m_editActualPos = m_main->RetWindowPos(); + dim = m_editFinalDim = m_editActualDim = m_main->RetWindowDim(); + pw = m_interface->CreateWindows(pos, dim, 8, EVENT_WINDOW3); + if ( pw == 0 ) return; + pw->SetState(STATE_SHADOW); + pw->SetRedim(TRUE); // avant SetName ! + pw->SetMovable(TRUE); + pw->SetClosable(TRUE); + GetResource(RES_TEXT, RT_STUDIO_TITLE, res); + pw->SetName(res); + pw->SetMinDim(FPOINT(0.49f, 0.50f)); + pw->SetMaximized(m_bEditMaximized); + pw->SetMinimized(m_bEditMinimized); + m_main->SetEditFull(m_bEditMaximized); + + edit = pw->CreateEdit(pos, dim, 0, EVENT_STUDIO_EDIT); + if ( edit == 0 ) return; + edit->SetState(STATE_SHADOW); + edit->SetInsideScroll(FALSE); +//? if ( m_bRunning ) edit->SetEdit(FALSE); + edit->SetMaxChar(EDITSTUDIOMAX); + edit->SetFontType(FONT_COURIER); + edit->SetFontStretch(0.7f); + edit->SetDisplaySpec(TRUE); + edit->SetAutoIndent(m_engine->RetEditIndentMode()); + m_script->PutScript(edit, name); + ColorizeScript(edit); + + ViewEditScript(); + + list = pw->CreateList(pos, dim, 1, EVENT_STUDIO_LIST, 1.2f); + list->SetState(STATE_SHADOW); + list->SetFontType(FONT_COURIER); + list->SetSelectCap(FALSE); + list->SetFontSize(SMALLFONT*0.85f); +//? list->SetFontStretch(1.0f); + + button = pw->CreateButton(pos, dim, 56, EVENT_STUDIO_NEW); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 57, EVENT_STUDIO_OPEN); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 58, EVENT_STUDIO_SAVE); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 59, EVENT_STUDIO_UNDO); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 60, EVENT_STUDIO_CUT); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 61, EVENT_STUDIO_COPY); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 62, EVENT_STUDIO_PASTE); + button->SetState(STATE_SHADOW); + slider = pw->CreateSlider(pos, dim, 0, EVENT_STUDIO_SIZE); + slider->SetState(STATE_SHADOW); + slider->SetVisibleValue((m_main->RetFontSize()-9.0f)/6.0f); + pw->CreateGroup(pos, dim, 19, EVENT_LABEL1); // logo SatCom + button = pw->CreateButton(pos, dim, 128+57, EVENT_STUDIO_TOOL); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 128+60, EVENT_STUDIO_HELP); + button->SetState(STATE_SHADOW); + + button = pw->CreateButton(pos, dim, -1, EVENT_STUDIO_OK); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, -1, EVENT_STUDIO_CANCEL); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 64+23, EVENT_STUDIO_COMPILE); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 21, EVENT_STUDIO_RUN); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 64+22, EVENT_STUDIO_REALTIME); + button->SetState(STATE_SHADOW); + button = pw->CreateButton(pos, dim, 64+29, EVENT_STUDIO_STEP); + button->SetState(STATE_SHADOW); + + UpdateFlux(); + UpdateButtons(); + AdjustEditScript(); +} + +// Repositionne tous les contrôles d'édition. + +void CStudio::AdjustEditScript() +{ + CWindow* pw; + CEdit* edit; + CButton* button; + CGroup* group; + CSlider* slider; + CList* list; + FPOINT wpos, wdim, pos, dim, ppos, ddim; + float hList; + + wpos = m_editActualPos; + wdim = m_editActualDim; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw != 0 ) + { + pw->SetPos(wpos); + pw->SetDim(wdim); + wdim = pw->RetDim(); + } + + if ( m_bRunning ) hList = 80.0f/480.0f; + else hList = 20.0f/480.0f; + + pos.x = wpos.x+0.01f; + pos.y = wpos.y+0.09f+hList; + dim.x = wdim.x-0.02f; + dim.y = wdim.y-0.22f-hList; + edit = (CEdit*)pw->SearchControl(EVENT_STUDIO_EDIT); + if ( edit != 0 ) + { + edit->SetPos(pos); + edit->SetDim(dim); + } + + pos.x = wpos.x+0.01f; + pos.y = wpos.y+0.09f; + dim.x = wdim.x-0.02f; + dim.y = hList; + list = (CList*)pw->SearchControl(EVENT_STUDIO_LIST); + if ( list != 0 ) + { + list->SetPos(pos); + list->SetDim(dim); + } + + dim.x = 0.04f; + dim.y = 0.04f*1.5f; + dim.y = 25.0f/480.0f; + + pos.y = wpos.y+wdim.y-dim.y-0.06f; + pos.x = wpos.x+0.01f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_NEW); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.05f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_OPEN); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.09f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_SAVE); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.14f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_UNDO); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.19f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_CUT); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.23f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_COPY); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.27f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_PASTE); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.32f; + slider = (CSlider*)pw->SearchControl(EVENT_STUDIO_SIZE); + if ( slider != 0 ) + { + ppos = pos; + ddim.x = dim.x*0.7f; + ddim.y = dim.y; + ppos.y -= 3.0f/480.0f; + ddim.y += 6.0f/480.0f; + slider->SetPos(ppos); + slider->SetDim(ddim); + } + pos.x = wpos.x+0.36f; + group = (CGroup*)pw->SearchControl(EVENT_LABEL1); + if ( group != 0 ) + { + group->SetPos(pos); + group->SetDim(dim); + } + pos.x = wpos.x+0.40f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_TOOL); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.44f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_HELP); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + + pos.y = wpos.y+0.02f; + pos.x = wpos.x+0.01f; + dim.x = 80.0f/640.0f; + dim.y = 25.0f/480.0f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_OK); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.14f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_CANCEL); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.28f; + dim.x = dim.y*0.75f; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_COMPILE); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.28f+dim.x*1; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_RUN); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.28f+dim.x*2; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_REALTIME); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } + pos.x = wpos.x+0.28f+dim.x*3; + button = (CButton*)pw->SearchControl(EVENT_STUDIO_STEP); + if ( button != 0 ) + { + button->SetPos(pos); + button->SetDim(dim); + } +} + +// Fin de l'édition d'un programme. + +BOOL CStudio::StopEditScript(BOOL bCancel) +{ + CWindow* pw; + CEdit* edit; + CButton* button; + char buffer[100]; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw == 0 ) return FALSE; + + if ( !bCancel && !m_script->IsRunning() ) + { + edit = (CEdit*)pw->SearchControl(EVENT_STUDIO_EDIT); + if ( edit != 0 ) + { + if ( !m_script->GetScript(edit) ) // compile + { + m_script->GetError(buffer); + SetInfoText(buffer, FALSE); + return FALSE; + } + } + } + m_script->SetStepMode(FALSE); + + m_interface->DeleteControl(EVENT_WINDOW3); + + button = (CButton*)m_interface->SearchControl(EVENT_BUTTON_QUIT); + if ( button != 0 ) + { + button->SetState(STATE_VISIBLE); + } + + if ( !m_bInitPause ) m_engine->SetPause(FALSE); + m_sound->MuteAll(FALSE); + m_main->SetEditLock(FALSE, TRUE); + m_camera->SetType(m_editCamera); + return TRUE; +} + +// Spécifie le message à afficher. +// Les messages non cliquables restent 8 secondes, même si un +// message cliquable est affiché avant. + +void CStudio::SetInfoText(char *text, BOOL bClickable) +{ + CWindow* pw; + CList* list; + char res[100]; + + if ( bClickable && m_fixInfoTextTime > 0.0f ) return; + if ( !bClickable ) m_fixInfoTextTime = 8.0f; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw == 0 ) return; + + list = (CList*)pw->SearchControl(EVENT_STUDIO_LIST); + if ( list == 0 ) return; + + list->Flush(); // juste le texte + list->SetName(0, text); + + if ( text[0] == 0 ) bClickable = FALSE; + list->SetSelectCap(bClickable); + + if ( bClickable ) + { + GetResource(RES_TEXT, RT_STUDIO_LISTTT, res); + list->SetTooltip(res); + list->SetState(STATE_ENABLE); + } + else + { + list->SetTooltip(""); +//? list->ClearState(STATE_ENABLE); + list->SetState(STATE_ENABLE, text[0] != 0); + } +} + + +// Changement de la taille l'édition d'un programme. + +void CStudio::ViewEditScript() +{ + CWindow* pw; + CEdit* edit; + POINT dim; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw == 0 ) return; + + edit = (CEdit*)pw->SearchControl(EVENT_STUDIO_EDIT); + if ( edit == 0 ) return; + + dim = m_engine->RetDim(); + edit->SetFontSize(m_main->RetFontSize()/(dim.x/640.0f)); +} + + +// Met à jour le mode de fonctionnement. + +void CStudio::UpdateFlux() +{ + if ( m_bRunning ) + { +#if 1 + if ( m_bRealTime ) // run ? + { + m_engine->SetPause(FALSE); + m_sound->MuteAll(FALSE); + } + else // step by step ? + { + m_engine->SetPause(TRUE); + m_sound->MuteAll(TRUE); + } +#else + m_engine->SetPause(FALSE); + m_sound->MuteAll(FALSE); +#endif + } + else // stop ? + { + m_engine->SetPause(TRUE); + m_sound->MuteAll(TRUE); + } +} + +// Met à jour les boutons. + +void CStudio::UpdateButtons() +{ + CWindow* pw; + CEdit* edit; + CButton* button; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw == 0 ) return; + + edit = (CEdit*)pw->SearchControl(EVENT_STUDIO_EDIT); + if ( edit == 0 ) return; + + if ( m_bRunning ) + { + edit->SetIcon(1); // fond rouge + edit->SetEditCap(FALSE); // juste pour voir + edit->SetHiliteCap(TRUE); + } + else + { + edit->SetIcon(0); // fond standard + edit->SetEditCap(TRUE); + edit->SetHiliteCap(TRUE); + } + + button = (CButton*)pw->SearchControl(EVENT_STUDIO_COMPILE); + if ( button == 0 ) return; + button->SetState(STATE_ENABLE, !m_bRunning); + + button = (CButton*)pw->SearchControl(EVENT_STUDIO_RUN); + if ( button == 0 ) return; + button->SetIcon(m_bRunning?8:21); // stop/run + + button = (CButton*)pw->SearchControl(EVENT_STUDIO_REALTIME); + if ( button == 0 ) return; + button->SetIcon(m_bRealTime?64+22:64+21); + button->SetState(STATE_ENABLE, (!m_bRunning || !m_script->IsContinue())); + + button = (CButton*)pw->SearchControl(EVENT_STUDIO_STEP); + if ( button == 0 ) return; + button->SetState(STATE_ENABLE, (m_bRunning && !m_bRealTime && !m_script->IsContinue())); +} + + +// Début de l'affichage d'un dialogue. + +void CStudio::StartDialog(StudioDialog type) +{ + CWindow* pw; + CButton* pb; + CCheck* pc; + CLabel* pla; + CList* pli; + CEdit* pe; + FPOINT pos, dim; + char name[100]; + + m_dialog = type; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW6); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW7); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW8); + if ( pw != 0 ) pw->ClearState(STATE_ENABLE); + + if ( m_dialog == SD_OPEN || + m_dialog == SD_SAVE ) + { + pos = m_main->RetIOPos(); + dim = m_main->RetIODim(); + } +//? pw = m_interface->CreateWindows(pos, dim, 8, EVENT_WINDOW9); + pw = m_interface->CreateWindows(pos, dim, m_dialog==SD_OPEN?14:13, EVENT_WINDOW9); + pw->SetState(STATE_SHADOW); + pw->SetMovable(TRUE); + pw->SetClosable(TRUE); + pw->SetMinDim(FPOINT(320.0f/640.0f, (121.0f+18.0f*4)/480.0f)); + if ( m_dialog == SD_OPEN ) GetResource(RES_TEXT, RT_IO_OPEN, name); + if ( m_dialog == SD_SAVE ) GetResource(RES_TEXT, RT_IO_SAVE, name); + pw->SetName(name); + + pos = FPOINT(0.0f, 0.0f); + dim = FPOINT(0.0f, 0.0f); + + if ( m_dialog == SD_OPEN || + m_dialog == SD_SAVE ) + { + GetResource(RES_TEXT, RT_IO_LIST, name); + pla = pw->CreateLabel(pos, dim, 0, EVENT_DIALOG_LABEL1, name); + pla->SetJustif(1); + + pli = pw->CreateList(pos, dim, 0, EVENT_DIALOG_LIST); + pli->SetState(STATE_SHADOW); + + GetResource(RES_TEXT, RT_IO_NAME, name); + pla = pw->CreateLabel(pos, dim, 0, EVENT_DIALOG_LABEL2, name); + pla->SetJustif(1); + + pe = pw->CreateEdit(pos, dim, 0, EVENT_DIALOG_EDIT); + pe->SetState(STATE_SHADOW); + if ( m_dialog == SD_SAVE ) + { + pe->SetText(m_script->RetFilename()); + } + + GetResource(RES_TEXT, RT_IO_DIR, name); + pla = pw->CreateLabel(pos, dim, 0, EVENT_DIALOG_LABEL3, name); + pla->SetJustif(1); + + pc = pw->CreateCheck(pos, dim, 0, EVENT_DIALOG_CHECK1); + GetResource(RES_TEXT, RT_IO_PRIVATE, name); + pc->SetName(name); + pc->SetState(STATE_SHADOW); +#if _POLISH + pc->SetFontSize(8.0f); +#endif + + pc = pw->CreateCheck(pos, dim, 0, EVENT_DIALOG_CHECK2); + GetResource(RES_TEXT, RT_IO_PUBLIC, name); + pc->SetName(name); + pc->SetState(STATE_SHADOW); +#if _POLISH + pc->SetFontSize(8.0f); +#endif + + pb = pw->CreateButton(pos, dim, -1, EVENT_DIALOG_OK); + pb->SetState(STATE_SHADOW); + if ( m_dialog == SD_OPEN ) GetResource(RES_TEXT, RT_IO_OPEN, name); + if ( m_dialog == SD_SAVE ) GetResource(RES_TEXT, RT_IO_SAVE, name); + pb->SetName(name); + + pb = pw->CreateButton(pos, dim, -1, EVENT_DIALOG_CANCEL); + pb->SetState(STATE_SHADOW); + GetResource(RES_EVENT, EVENT_DIALOG_CANCEL, name); + pb->SetName(name); + + AdjustDialog(); + UpdateDialogList(); + UpdateDialogPublic(); + UpdateDialogAction(); + + pe->SetCursor(999, 0); // sélectionne tout + pe->SetFocus(TRUE); + } + + m_main->SetSatComLock(TRUE); // impossible d'utiliser le SatCom +} + +// Fin de l'affichage d'un dialogue. + +void CStudio::StopDialog() +{ + CWindow* pw; + + if ( m_dialog == SD_NULL ) return; + m_dialog = SD_NULL; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW0); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW1); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW4); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW5); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW6); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW7); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW8); + if ( pw != 0 ) pw->SetState(STATE_ENABLE); + + m_interface->DeleteControl(EVENT_WINDOW9); + m_main->SetSatComLock(FALSE); // possible d'utiliser le SatCom +} + +// Ajuste tous les contrôles de dialogue suite à un changement de géométrie. + +void CStudio::AdjustDialog() +{ + CWindow* pw; + CButton* pb; + CCheck* pc; + CLabel* pla; + CList* pli; + CEdit* pe; + FPOINT wpos, wdim, ppos, ddim; + int nli, nch; + char name[100]; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return; + + wpos = pw->RetPos(); + wdim = pw->RetDim(); + pw->SetPos(wpos); // pour déplacer les boutons de la barre de titre + + if ( m_dialog == SD_OPEN || + m_dialog == SD_SAVE ) + { + ppos.x = wpos.x+10.0f/640.0f; + ppos.y = wpos.y+wdim.y-55.0f/480.0f; + ddim.x = wdim.x-20.0f/640.0f; + ddim.y = 20.0f/480.0f; + pla = (CLabel*)pw->SearchControl(EVENT_DIALOG_LABEL1); + if ( pla != 0 ) + { + pla->SetPos(ppos); + pla->SetDim(ddim); + } + + nli = (int)((wdim.y-120.0f/480.0f)/(18.0f/480.0f)); + ddim.y = nli*18.0f/480.0f+9.0f/480.0f; + ppos.y = wpos.y+wdim.y-48.0f/480.0f-ddim.y; + pli = (CList*)pw->SearchControl(EVENT_DIALOG_LIST); + if ( pli != 0 ) + { + pli->SetPos(ppos); + pli->SetDim(ddim); + pli->SetTabs(0, ddim.x-(50.0f+130.0f+16.0f)/640.0f); + pli->SetTabs(1, 50.0f/640.0f, -1); + pli->SetTabs(2, 130.0f/640.0f); +//? pli->ShowSelect(); + } + + ppos.y = wpos.y+30.0f/480.0f; + ddim.x = 50.0f/640.0f; + ddim.y = 20.0f/480.0f; + pla = (CLabel*)pw->SearchControl(EVENT_DIALOG_LABEL2); + if ( pla != 0 ) + { + pla->SetPos(ppos); + pla->SetDim(ddim); + } + + ppos.x += 50.0f/640.0f; + ppos.y = wpos.y+36.0f/480.0f; + ddim.x = wdim.x-170.0f/640.0f; + pe = (CEdit*)pw->SearchControl(EVENT_DIALOG_EDIT); + if ( pe != 0 ) + { + pe->SetPos(ppos); + pe->SetDim(ddim); + + nch = (int)((ddim.x*640.0f-22.0f)/8.0f); + pe->GetText(name, 100); + pe->SetMaxChar(nch); + name[nch] = 0; // tronque le texte selon max + pe->SetText(name); + } + + ppos.x = wpos.x+10.0f/640.0f; + ppos.y = wpos.y+5.0f/480.0f; + ddim.x = 50.0f/640.0f; + ddim.y = 16.0f/480.0f; + pla = (CLabel*)pw->SearchControl(EVENT_DIALOG_LABEL3); + if ( pla != 0 ) + { + pla->SetPos(ppos); + pla->SetDim(ddim); + } + + ppos.x += 50.0f/640.0f; + ppos.y = wpos.y+12.0f/480.0f; + ddim.x = 70.0f/640.0f; + pc = (CCheck*)pw->SearchControl(EVENT_DIALOG_CHECK1); + if ( pc != 0 ) + { + pc->SetPos(ppos); + pc->SetDim(ddim); + } + + ppos.x += 80.0f/640.0f; + pc = (CCheck*)pw->SearchControl(EVENT_DIALOG_CHECK2); + if ( pc != 0 ) + { + pc->SetPos(ppos); + pc->SetDim(ddim); + } + + ppos.x = wpos.x+wdim.x-100.0f/640.0f; + ppos.y = wpos.y+34.0f/480.0f; + ddim.x = 90.0f/640.0f; + ddim.y = 23.0f/480.0f; + pb = (CButton*)pw->SearchControl(EVENT_DIALOG_OK); + if ( pb != 0 ) + { + pb->SetPos(ppos); + pb->SetDim(ddim); + } + + ppos.y -= 26.0f/480.0f; + pb = (CButton*)pw->SearchControl(EVENT_DIALOG_CANCEL); + if ( pb != 0 ) + { + pb->SetPos(ppos); + pb->SetDim(ddim); + } + } +} + +// Gestion de l'événement d'un dialogue. + +BOOL CStudio::EventDialog(const Event &event) +{ + CWindow* pw; + FPOINT wpos, wdim; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return FALSE; + + if ( event.event == EVENT_WINDOW9 ) // fenêtre déplacée ? + { + wpos = pw->RetPos(); + wdim = pw->RetDim(); + m_main->SetIOPos(wpos); + m_main->SetIODim(wdim); + AdjustDialog(); + } + + if ( m_dialog == SD_OPEN || + m_dialog == SD_SAVE ) + { + if ( event.event == EVENT_DIALOG_LIST ) + { + UpdateChangeList(); + } + if ( event.event == EVENT_DIALOG_EDIT ) + { + UpdateChangeEdit(); + } + + if ( event.event == EVENT_DIALOG_CHECK1 ) // private ? + { + m_main->SetIOPublic(FALSE); + UpdateDialogPublic(); + UpdateDialogList(); + } + if ( event.event == EVENT_DIALOG_CHECK2 ) // public ? + { + m_main->SetIOPublic(TRUE); + UpdateDialogPublic(); + UpdateDialogList(); + } + } + + if ( event.event == EVENT_DIALOG_OK || + (event.event == EVENT_KEYDOWN && event.param == VK_RETURN) ) + { + if ( m_dialog == SD_OPEN ) + { + if ( !ReadProgram() ) return TRUE; + } + if ( m_dialog == SD_SAVE ) + { + if ( !WriteProgram() ) return TRUE; + } + + StopDialog(); + return TRUE; + } + + if ( event.event == EVENT_DIALOG_CANCEL || + (event.event == EVENT_KEYDOWN && event.param == VK_ESCAPE) || + event.event == pw->RetEventMsgClose() ) + { + StopDialog(); + return TRUE; + } + + return TRUE; +} + +// Met à jour le nom suite à un clic dans la liste. + +void CStudio::UpdateChangeList() +{ + CWindow* pw; + CList* pl; + CEdit* pe; + char name[100]; + char* p; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_DIALOG_LIST); + if ( pl == 0 ) return; + pe = (CEdit*)pw->SearchControl(EVENT_DIALOG_EDIT); + if ( pe == 0 ) return; + + strcpy(name, pl->RetName(pl->RetSelect())); + name[pe->RetMaxChar()] = 0; // tronque selon lg max éditable + p = strchr(name, '\t'); // cherche 1er tabulateur + if ( p != 0 ) *p = 0; + pe->SetText(name); + pe->SetCursor(999, 0); // sélectionne tout + pe->SetFocus(TRUE); + + UpdateDialogAction(); +} + +// Met à jour la liste suite à un changement dans le nom. + +void CStudio::UpdateChangeEdit() +{ + CWindow* pw; + CList* pl; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_DIALOG_LIST); + if ( pl == 0 ) return; + + pl->SetSelect(-1); + + UpdateDialogAction(); +} + +// Met à jour le bouton d'action. + +void CStudio::UpdateDialogAction() +{ + CWindow* pw; + CEdit* pe; + CButton* pb; + char name[100]; + int len, i; + BOOL bError; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return; + pe = (CEdit*)pw->SearchControl(EVENT_DIALOG_EDIT); + if ( pe == 0 ) return; + pb = (CButton*)pw->SearchControl(EVENT_DIALOG_OK); + if ( pb == 0 ) return; + + pe->GetText(name, 100); + len = strlen(name); + if ( len == 0 ) + { + bError = TRUE; + } + else + { + bError = FALSE; + for ( i=0 ; i' || + name[i] == '"' || + name[i] == '|' || + name[i] == '/' || + name[i] == '\\' ) bError = TRUE; + } + } + + pb->SetState(STATE_ENABLE, !bError); +} + +// Met à jour les boutons private/public. + +void CStudio::UpdateDialogPublic() +{ + CWindow* pw; + CCheck* pc; + CLabel* pl; + char name[100]; + char dir[_MAX_FNAME]; + char text[_MAX_FNAME+100]; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return; + + pc = (CCheck*)pw->SearchControl(EVENT_DIALOG_CHECK1); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, !m_main->RetIOPublic()); + } + + pc = (CCheck*)pw->SearchControl(EVENT_DIALOG_CHECK2); + if ( pc != 0 ) + { + pc->SetState(STATE_CHECK, m_main->RetIOPublic()); + } + + pl = (CLabel*)pw->SearchControl(EVENT_DIALOG_LABEL1); + if ( pl != 0 ) + { + GetResource(RES_TEXT, RT_IO_LIST, name); + SearchDirectory(dir, FALSE); + sprintf(text, name, dir); + pl->SetName(text, FALSE); + } +} + +// Remplit la liste avec tous les programmes sauvés. + +void CStudio::UpdateDialogList() +{ + CWindow* pw; + CList* pl; + long hFile; + struct _finddata_t fileBuffer; + struct _finddata_t* listBuffer; + BOOL bDo; + char dir[_MAX_FNAME]; + char temp[_MAX_FNAME]; + int nbFilenames, i; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return; + pl = (CList*)pw->SearchControl(EVENT_DIALOG_LIST); + if ( pl == 0 ) return; + pl->Flush(); + + nbFilenames = 0; + listBuffer = (_finddata_t*)malloc(sizeof(_finddata_t)*1000); + + SearchDirectory(dir, FALSE); + strcat(dir, "*"); // liste tout + hFile = _findfirst(dir, &fileBuffer); + if ( hFile != -1 ) + { + do + { + if ( (fileBuffer.attrib & _A_SUBDIR) == 0 ) + { + listBuffer[nbFilenames++] = fileBuffer; + } + } + while ( _findnext(hFile, &fileBuffer) == 0 && nbFilenames < 1000 ); + } + do // trie tous les noms : + { + bDo = FALSE; + for ( i=0 ; i 0 ) + { + fileBuffer = listBuffer[i]; // échange i et i+1 + listBuffer[i] = listBuffer[i+1]; + listBuffer[i+1] = fileBuffer; + bDo = TRUE; + } + } + } + while ( bDo ); + + for ( i=0 ; iSetName(i, temp); + } + + free(listBuffer); +} + +// Construit le nom du dossier où ouvrir/enregistrer. +// Si le dossier n'existe pas, il est éventuellement créé. + +void CStudio::SearchDirectory(char *dir, BOOL bCreate) +{ + if ( m_main->RetIOPublic() ) + { + sprintf(dir, "%s\\", m_main->RetPublicDir()); + } + else + { + sprintf(dir, "%s\\%s\\Program\\", m_main->RetSavegameDir(), m_main->RetGamerName()); + } + + if ( bCreate ) + { + _mkdir(dir); // si n'existe pas encore ! + } +} + +// Lit un nouveau programme. + +BOOL CStudio::ReadProgram() +{ + CWindow* pw; + CEdit* pe; + char filename[100]; + char dir[100]; + char* p; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return FALSE; + + pe = (CEdit*)pw->SearchControl(EVENT_DIALOG_EDIT); + if ( pe == 0 ) return FALSE; + pe->GetText(filename, 100); + if ( filename[0] == 0 ) return FALSE; + + p = strstr(filename, ".txt"); + if ( p == 0 || p != filename+strlen(filename)-4 ) + { + strcat(filename, ".txt"); + } + SearchDirectory(dir, TRUE); + strcat(dir, filename); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw == 0 ) return FALSE; + pe = (CEdit*)pw->SearchControl(EVENT_STUDIO_EDIT); + if ( pe == 0 ) return FALSE; + + if ( !pe->ReadText(dir) ) return FALSE; + + m_script->SetFilename(filename); + ColorizeScript(pe); + return TRUE; +} + +// Ecrit le programme actuel. + +BOOL CStudio::WriteProgram() +{ + CWindow* pw; + CEdit* pe; + char filename[100]; + char dir[100]; + char* p; + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW9); + if ( pw == 0 ) return FALSE; + + pe = (CEdit*)pw->SearchControl(EVENT_DIALOG_EDIT); + if ( pe == 0 ) return FALSE; + pe->GetText(filename, 100); + if ( filename[0] == 0 ) return FALSE; + + p = strstr(filename, ".txt"); + if ( p == 0 || p != filename+strlen(filename)-4 ) + { + strcat(filename, ".txt"); + } + SearchDirectory(dir, TRUE); + strcat(dir, filename); + + pw = (CWindow*)m_interface->SearchControl(EVENT_WINDOW3); + if ( pw == 0 ) return FALSE; + pe = (CEdit*)pw->SearchControl(EVENT_STUDIO_EDIT); + if ( pe == 0 ) return FALSE; + + if ( !pe->WriteText(dir) ) return FALSE; + + m_script->SetFilename(filename); + return TRUE; +} + diff --git a/src/studio.h b/src/studio.h new file mode 100644 index 00000000..1da9e10d --- /dev/null +++ b/src/studio.h @@ -0,0 +1,97 @@ +// studio.h + +#ifndef _STUDIO_H_ +#define _STUDIO_H_ + + +class CInstanceManager; +class CD3DEngine; +class CEvent; +class CRobotMain; +class CCamera; +class CSound; +class CInterface; +class CScript; +class CList; +class CEdit; + + + +enum StudioDialog +{ + SD_NULL, + SD_OPEN, + SD_SAVE, + SD_FIND, + SD_REPLACE, +}; + + + +class CStudio +{ +public: + CStudio(CInstanceManager* iMan); + ~CStudio(); + + BOOL EventProcess(const Event &event); + + void StartEditScript(CScript *script, char* name, int rank); + BOOL StopEditScript(BOOL bCancel); + +protected: + BOOL EventFrame(const Event &event); + void SearchToken(CEdit* edit); + void ColorizeScript(CEdit* edit); + void AdjustEditScript(); + void SetInfoText(char *text, BOOL bClickable); + void ViewEditScript(); + void UpdateFlux(); + void UpdateButtons(); + + void StartDialog(StudioDialog type); + void StopDialog(); + void AdjustDialog(); + BOOL EventDialog(const Event &event); + void UpdateChangeList(); + void UpdateChangeEdit(); + void UpdateDialogAction(); + void UpdateDialogPublic(); + void UpdateDialogList(); + void SearchDirectory(char *dir, BOOL bCreate); + BOOL ReadProgram(); + BOOL WriteProgram(); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CEvent* m_event; + CRobotMain* m_main; + CCamera* m_camera; + CSound* m_sound; + CInterface* m_interface; + + int m_rank; + CScript* m_script; + + BOOL m_bEditMaximized; + BOOL m_bEditMinimized; + + CameraType m_editCamera; + FPOINT m_editActualPos; + FPOINT m_editActualDim; + FPOINT m_editFinalPos; + FPOINT m_editFinalDim; + + float m_time; + float m_fixInfoTextTime; + BOOL m_bRunning; + BOOL m_bRealTime; + BOOL m_bInitPause; + char m_helpFilename[100]; + + StudioDialog m_dialog; +}; + + +#endif //_STUDIO_H_ diff --git a/src/t.txt b/src/t.txt new file mode 100644 index 00000000..9dd81fb2 --- /dev/null +++ b/src/t.txt @@ -0,0 +1,11 @@ +extern void object::Go() +{ +//float a = energyLevel; +//message(a); + +//object item=energyCell; +//float i = item.energyLevel; +//message(i); +float i = energyCell.energyLevel; + +} diff --git a/src/target.cpp b/src/target.cpp new file mode 100644 index 00000000..b0b64c27 --- /dev/null +++ b/src/target.cpp @@ -0,0 +1,271 @@ +// target.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "robotmain.h" +#include "object.h" +#include "restext.h" +#include "target.h" + + + + +// Constructeur de l'objet. + +CTarget::CTarget(CInstanceManager* iMan) : CControl(iMan) +{ + CControl::CControl(iMan); +} + +// Destructeur de l'objet. + +CTarget::~CTarget() +{ + CControl::~CControl(); +} + + +// Crée un nouveau bouton. + +BOOL CTarget::Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + CControl::Create(pos, dim, icon, eventMsg); + + return TRUE; +} + + +// Gestion d'un événement. + +BOOL CTarget::EventProcess(const Event &event) +{ +#if 0 + if ( (m_state & STATE_VISIBLE) == 0 ) return TRUE; + if ( m_state & STATE_DEAD ) return TRUE; + + CControl::EventProcess(event); + + if ( event.event == EVENT_MOUSEMOVE ) + { + if ( CControl::Detect(event.pos) ) + { + m_engine->SetMouseType(D3DMOUSETARGET); + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + return FALSE; + } + } + + if ( event.event == EVENT_LBUTTONDOWN && + (m_state & STATE_VISIBLE) && + (m_state & STATE_ENABLE) ) + { + if ( CControl::Detect(event.pos) ) + { + Event newEvent = event; + newEvent.event = EVENT_OBJECT_FIRE; + m_event->AddEvent(newEvent); + return FALSE; + } + } + + return TRUE; +#else + CObject* pObj; + + if ( (m_state & STATE_VISIBLE) == 0 ) return TRUE; + if ( m_state & STATE_DEAD ) return TRUE; + + CControl::EventProcess(event); + + if ( event.event == EVENT_MOUSEMOVE ) + { + m_main->SetFriendAim(FALSE); + + if ( CControl::Detect(event.pos) ) + { + pObj = DetectFriendObject(event.pos); + if ( pObj == 0 ) + { + m_engine->SetMouseType(D3DMOUSETARGET); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + return FALSE; + } + else + { + m_main->SetFriendAim(TRUE); + m_engine->SetMouseType(D3DMOUSENORM); + } + } + } + + if ( event.event == EVENT_LBUTTONDOWN && + (m_state & STATE_VISIBLE) && + (m_state & STATE_ENABLE) ) + { + if ( CControl::Detect(event.pos) ) + { + if ( !m_main->RetFriendAim() ) + { + Event newEvent = event; + newEvent.event = EVENT_OBJECT_FIRE; + m_event->AddEvent(newEvent); + return FALSE; + } + } + } + + return TRUE; +#endif +} + + +// Dessine le bouton. + +void CTarget::Draw() +{ + // C'est totalement invisible ! +} + + +// Retourne le tooltip. + +BOOL CTarget::GetTooltip(FPOINT pos, char* name) +{ +#if 0 + if ( (m_state&STATE_VISIBLE) && Detect(pos) ) // dans la fenêtre ? + { + strcpy(name, m_tooltip); + return TRUE; // ne détecte pas les objets dessous ! + } + + return FALSE; +#else +//? CObject* pObj; + + if ( (m_state & STATE_VISIBLE) == 0 ) return FALSE; + + if ( (m_state&STATE_VISIBLE) && Detect(pos) ) // dans la fenêtre ? + { +//? pObj = DetectFriendObject(pos); +//? if ( pObj == 0 ) + if ( !m_main->RetFriendAim() ) + { + strcpy(name, m_tooltip); + return TRUE; // ne détecte pas les objets dessous ! + } + } + + return FALSE; +#endif +} + + +// Détecte l'objet visé par la souris. + +CObject* CTarget::DetectFriendObject(FPOINT pos) +{ + ObjectType type; + CObject *pObj, *pTarget; + int objRank, i, j, rank; + + objRank = m_engine->DetectObject(pos); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; + if ( pObj->RetProxyActivate() ) continue; + if ( pObj->RetSelect() ) continue; + + pTarget = 0; + type = pObj->RetType(); + if ( type == OBJECT_DERRICK || + type == OBJECT_FACTORY || + type == OBJECT_REPAIR || + type == OBJECT_DESTROYER || + type == OBJECT_STATION || + type == OBJECT_CONVERT || + type == OBJECT_TOWER || + type == OBJECT_RESEARCH || + type == OBJECT_RADAR || + type == OBJECT_INFO || + type == OBJECT_ENERGY || + type == OBJECT_LABO || + type == OBJECT_NUCLEAR || + type == OBJECT_PARA || + type == OBJECT_SAFE || + type == OBJECT_HUSTON || + type == OBJECT_HUMAN || + type == OBJECT_TECH || + type == OBJECT_TOTO || + type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr ) + { + pTarget = pObj; + } + else if ( (type == OBJECT_POWER || + type == OBJECT_ATOMIC ) && + pObj->RetTruck() != 0 ) // pile utilisée ? + { + pTarget = pObj->RetTruck(); + if ( pTarget->RetType() == OBJECT_MOBILEtg ) + { + pTarget = 0; + } + } + + for ( j=0 ; jRetObjectRank(j); + if ( rank == -1 ) continue; + if ( rank != objRank ) continue; + return pTarget; + } + } + return 0; +} + diff --git a/src/target.h b/src/target.h new file mode 100644 index 00000000..2f479dcc --- /dev/null +++ b/src/target.h @@ -0,0 +1,34 @@ +// target.h + +#ifndef _TARGET_H_ +#define _TARGET_H_ + + +#include "control.h" + + +class CD3DEngine; +class CObject; + + + +class CTarget : public CControl +{ +public: + CTarget(CInstanceManager* iMan); + ~CTarget(); + + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + + BOOL EventProcess(const Event &event); + void Draw(); + BOOL GetTooltip(FPOINT pos, char* name); + +protected: + CObject* DetectFriendObject(FPOINT pos); + +protected: +}; + + +#endif //_TARGET_H_ diff --git a/src/task.cpp b/src/task.cpp new file mode 100644 index 00000000..7b3dd841 --- /dev/null +++ b/src/task.cpp @@ -0,0 +1,93 @@ +// task.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "water.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "motion.h" +#include "camera.h" +#include "sound.h" +#include "robotmain.h" +#include "displaytext.h" +#include "task.h" + + + + +// Constructeur de l'objet. + +CTask::CTask(CInstanceManager* iMan, CObject* object) +{ + m_iMan = iMan; + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_light = (CLight*)m_iMan->SearchInstance(CLASS_LIGHT); + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + m_camera = (CCamera*)m_iMan->SearchInstance(CLASS_CAMERA); + m_main = (CRobotMain*)m_iMan->SearchInstance(CLASS_MAIN); + m_displayText = (CDisplayText*)m_iMan->SearchInstance(CLASS_DISPLAYTEXT); + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + + m_object = object; + m_physics = m_object->RetPhysics(); + m_brain = m_object->RetBrain(); + m_motion = m_object->RetMotion(); +} + +// Destructeur de l'objet. + +CTask::~CTask() +{ +} + + +// Gestion d'un événement. + +BOOL CTask::EventProcess(const Event &event) +{ + return TRUE; +} + + +// Indique si l'action est terminée. + +Error CTask::IsEnded() +{ + return ERR_STOP; +} + + +// Indique si l'action est en cours. + +BOOL CTask::IsBusy() +{ + return TRUE; +} + + +// Termine brutalement l'action en cours. + +BOOL CTask::Abort() +{ + return TRUE; +} + + diff --git a/src/task.h b/src/task.h new file mode 100644 index 00000000..5b6041de --- /dev/null +++ b/src/task.h @@ -0,0 +1,70 @@ +// task.h + +#ifndef _TASK_H_ +#define _TASK_H_ + + +class CInstanceManager; +class CD3DEngine; +class CEngine; +class CLight; +class CParticule; +class CTerrain; +class CWater; +class CCamera; +class CBrain; +class CPhysics; +class CMotion; +class CObject; +class CRobotMain; +class CDisplayText; +class CSound; + + +#define TAKE_DIST 6.0f // distance d'un objet pour le prendre +#define TAKE_DIST_OTHER 1.5f // distance supplémentaire si sur ami + +//?#define ARM_NEUTRAL_ANGLE1 155.0f*PI/180.0f +//?#define ARM_NEUTRAL_ANGLE2 -125.0f*PI/180.0f +//?#define ARM_NEUTRAL_ANGLE3 -45.0f*PI/180.0f +#define ARM_NEUTRAL_ANGLE1 110.0f*PI/180.0f +#define ARM_NEUTRAL_ANGLE2 -130.0f*PI/180.0f +#define ARM_NEUTRAL_ANGLE3 -50.0f*PI/180.0f + +#define ARM_STOCK_ANGLE1 110.0f*PI/180.0f +#define ARM_STOCK_ANGLE2 -100.0f*PI/180.0f +#define ARM_STOCK_ANGLE3 -70.0f*PI/180.0f + + +class CTask +{ +public: + CTask(CInstanceManager* iMan, CObject* object); + virtual ~CTask(); + + virtual BOOL EventProcess(const Event &event); + virtual Error IsEnded(); + virtual BOOL IsBusy(); + virtual BOOL Abort(); + +protected: + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CLight* m_light; + CParticule* m_particule; + CTerrain* m_terrain; + CWater* m_water; + CCamera* m_camera; + CMotion* m_motion; + CBrain* m_brain; + CPhysics* m_physics; + CObject* m_object; + CRobotMain* m_main; + CDisplayText* m_displayText; + CSound* m_sound; +}; + + +#endif //_TASK_H_ diff --git a/src/taskadvance.cpp b/src/taskadvance.cpp new file mode 100644 index 00000000..365562fb --- /dev/null +++ b/src/taskadvance.cpp @@ -0,0 +1,143 @@ +// taskadvance.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "task.h" +#include "taskadvance.h" + + + + +// Constructeur de l'objet. + +CTaskAdvance::CTaskAdvance(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); +} + +// Destructeur de l'objet. + +CTaskAdvance::~CTaskAdvance() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskAdvance::EventProcess(const Event &event) +{ + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_fixTime += event.rTime; + + // Objet momentanément immobile (fourmi sur le dos) ? + if ( m_object->RetFixed() ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + m_bError = TRUE; + return TRUE; + } + + m_timeLimit -= event.rTime; + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskAdvance::Start(float length) +{ + m_direction = (length>=0.0f)?1.0f:-1.0f; + m_totalLength = Abs(length); + m_advanceLength = m_physics->RetLinLength(length); + m_startPos = m_object->RetPosition(0); + m_lastDist = 0.0f; + m_fixTime = 0.0f; + + m_timeLimit = m_physics->RetLinTimeLength(m_totalLength, m_direction)*3.0f; + if ( m_timeLimit < 2.0f ) m_timeLimit = 2.0f; + + m_physics->SetMotorSpeedX(m_direction*1.0f); // avance/recule + m_physics->SetMotorSpeedY(0.0f); + m_physics->SetMotorSpeedZ(0.0f); + + m_bError = FALSE; + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskAdvance::IsEnded() +{ + D3DVECTOR pos; + float length; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + + if ( m_bError ) + { + return ERR_STOP; + } + + if ( m_timeLimit < 0.0f ) + { + m_physics->SetMotorSpeedX(0.0f); + return ERR_MOVE_IMPOSSIBLE; + } + + pos = m_object->RetPosition(0); + length = Length2d(pos, m_startPos); + + if ( length > m_lastDist ) // avance ? + { + m_fixTime = 0.0f; + } + else // n'avance plus ? + { + if ( m_fixTime > 1.0f ) // depuis plus d'une seconde ? + { + m_physics->SetMotorSpeedX(0.0f); + return ERR_MOVE_IMPOSSIBLE; + } + } + m_lastDist = length; + + if ( length >= m_totalLength ) + { + m_physics->SetMotorSpeedX(0.0f); + m_physics->SetLinMotionX(MO_CURSPEED, 0.0f); + + if ( length != 0.0f ) + { + pos = m_startPos+((pos-m_startPos)*m_totalLength/length); + m_object->SetPosition(0, pos); + } + return ERR_STOP; + } + + if ( length >= m_advanceLength ) + { + m_physics->SetMotorSpeedX(m_direction*0.1f); + } + return ERR_CONTINUE; +} + + diff --git a/src/taskadvance.h b/src/taskadvance.h new file mode 100644 index 00000000..9ff20ceb --- /dev/null +++ b/src/taskadvance.h @@ -0,0 +1,39 @@ +// taskadvance.h + +#ifndef _TASKADVANCE_H_ +#define _TASKADVANCE_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + +class CTaskAdvance : public CTask +{ +public: + CTaskAdvance(CInstanceManager* iMan, CObject* object); + ~CTaskAdvance(); + + BOOL EventProcess(const Event &event); + + Error Start(float length); + Error IsEnded(); + +protected: + +protected: + float m_totalLength; + float m_advanceLength; + float m_direction; + float m_timeLimit; + D3DVECTOR m_startPos; + float m_lastDist; + float m_fixTime; + BOOL m_bError; +}; + + +#endif //_TASKADVANCE_H_ diff --git a/src/taskbuild.cpp b/src/taskbuild.cpp new file mode 100644 index 00000000..d4b7d10d --- /dev/null +++ b/src/taskbuild.cpp @@ -0,0 +1,806 @@ +// taskbuild.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "light.h" +#include "particule.h" +#include "terrain.h" +#include "water.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "auto.h" +#include "camera.h" +#include "motion.h" +#include "motionhuman.h" +#include "robotmain.h" +#include "sound.h" +#include "displaytext.h" +#include "task.h" +#include "taskbuild.h" + + + + +// Constructeur de l'objet. + +CTaskBuild::CTaskBuild(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + int i; + + CTask::CTask(iMan, object); + + m_type = OBJECT_DERRICK; + m_time = 0.0f; + m_soundChannel = -1; + + for ( i=0 ; iFlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + for ( i=0 ; iDeleteLight(m_lightRank[i]); + } +} + + +// Crée un batiment. + +BOOL CTaskBuild::CreateBuilding(D3DVECTOR pos, float angle) +{ + m_building = new CObject(m_iMan); + if ( !m_building->CreateBuilding(pos, angle, 0.0f, m_type, 0.0f) ) + { + delete m_building; + m_building = 0; + return FALSE; + } + m_building->UpdateMapping(); + m_building->SetLock(TRUE); // pas encore utilisable + + if ( m_type == OBJECT_DERRICK ) m_buildingHeight = 35.0f; + if ( m_type == OBJECT_FACTORY ) m_buildingHeight = 28.0f; + if ( m_type == OBJECT_REPAIR ) m_buildingHeight = 30.0f; + if ( m_type == OBJECT_STATION ) m_buildingHeight = 13.0f; + if ( m_type == OBJECT_CONVERT ) m_buildingHeight = 20.0f; + if ( m_type == OBJECT_TOWER ) m_buildingHeight = 30.0f; + if ( m_type == OBJECT_RESEARCH ) m_buildingHeight = 22.0f; + if ( m_type == OBJECT_RADAR ) m_buildingHeight = 19.0f; + if ( m_type == OBJECT_ENERGY ) m_buildingHeight = 20.0f; + if ( m_type == OBJECT_LABO ) m_buildingHeight = 16.0f; + if ( m_type == OBJECT_NUCLEAR ) m_buildingHeight = 40.0f; + if ( m_type == OBJECT_PARA ) m_buildingHeight = 68.0f; + if ( m_type == OBJECT_INFO ) m_buildingHeight = 19.0f; + m_buildingHeight *= 0.25f; + + m_buildingPos = m_building->RetPosition(0); + m_buildingPos.y -= m_buildingHeight; + m_building->SetPosition(0, m_buildingPos); + return TRUE; +} + +// Crée les lumières pour les effets. + +void CTaskBuild::CreateLight() +{ + D3DLIGHT7 light; + D3DCOLORVALUE color; + D3DVECTOR center, pos, dir; + FPOINT c, p; + float angle; + int i; + + if ( !m_engine->RetLightMode() ) return; + + center = m_metal->RetPosition(0); + + angle = 0; + for ( i=0 ; iCreateLight(); + if ( m_lightRank[i] == -1 ) continue; + + c.x = center.x; + c.y = center.z; + p.x = center.x+40.0f; + p.y = center.z; + p = RotatePoint(c, angle, p); + pos.x = p.x; + pos.z = p.y; + pos.y = center.y+40.0f; + dir = center-pos; + + ZeroMemory( &light, sizeof(light) ); + light.dltType = D3DLIGHT_SPOT; + light.dcvDiffuse.r = 0.0f; + light.dcvDiffuse.g = 0.0f; + light.dcvDiffuse.b = 0.0f; // blanc (invisible) + light.dvPosition.x = pos.x; + light.dvPosition.y = pos.y; + light.dvPosition.z = pos.z; + light.dvDirection.x = dir.x; + light.dvDirection.y = dir.y; + light.dvDirection.z = dir.z; + light.dvRange = D3DLIGHT_RANGE_MAX; + light.dvFalloff = 1.0f; + light.dvAttenuation0 = 1.0f; + light.dvAttenuation1 = 0.0f; + light.dvAttenuation2 = 0.0f; + light.dvTheta = 0.0f; + light.dvPhi = PI/4.0f; + m_light->SetLight(m_lightRank[i], light); + + color.r = -1.0f; + color.g = -1.0f; + color.b = -0.5f; // violet + color.a = 0.0f; + m_light->SetLightColor(m_lightRank[i], color); + m_light->SetLightColorSpeed(m_lightRank[i], 1.0f/((1.0f/m_speed)*0.25f)); + + angle += (PI*2.0f)/TBMAXLIGHT; + } + + m_bBlack = FALSE; +} + +// Fait passer les lumières du noir au blanc. + +void CTaskBuild::BlackLight() +{ + D3DCOLORVALUE color; + int i; + + for ( i=0 ; iSetLightColor(m_lightRank[i], color); + m_light->SetLightColorSpeed(m_lightRank[i], 1.0f/((1.0f/m_speed)*0.75f)); + } + + m_bBlack = TRUE; +} + +// Gestion d'un événement. + +BOOL CTaskBuild::EventProcess(const Event &event) +{ + D3DMATRIX* mat; + D3DVECTOR pos, dir, speed; + FPOINT dim; + float a, g, cirSpeed, dist, linSpeed; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + m_time += event.rTime; + + m_progress += event.rTime*m_speed; // ça avance + + if ( m_phase == TBP_TURN ) // rotation préliminaire ? + { + a = m_object->RetAngleY(0); + g = m_angleY; + cirSpeed = Direction(a, g)*1.0f; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + return TRUE; + } + + if ( m_phase == TBP_MOVE ) // avance/recule préliminaire ? + { + dist = Length(m_object->RetPosition(0), m_metal->RetPosition(0)); + linSpeed = 0.0f; + if ( dist > 30.0f ) linSpeed = 1.0f; + if ( dist < 30.0f ) linSpeed = -1.0f; + m_physics->SetMotorSpeedX(linSpeed); // avance/recule + return TRUE; + } + + if ( m_phase == TBP_RECEDE ) // recule terminal ? + { + m_physics->SetMotorSpeedX(-1.0f); // recule + return TRUE; + } + + if ( m_phase == TBP_TAKE ) // prend arme ? + { + return TRUE; + } + + if ( m_phase == TBP_PREP ) // prépare ? + { + return TRUE; + } + + if ( m_phase == TBP_TERM ) // termine ? + { + return TRUE; + } + + if ( !m_bBuild ) // batiment à construire ? + { + m_bBuild = TRUE; + + pos = m_metal->RetPosition(0); + a = m_object->RetAngleY(0); + if ( !CreateBuilding(pos, a+PI) ) + { + m_metal->SetLock(FALSE); // de nouveau utilisable + m_motion->SetAction(-1); + m_object->SetObjectParent(14, 0); + m_object->SetPosition(14, D3DVECTOR(-1.5f, 0.3f, -1.35f)); + m_object->SetAngleZ(14, PI); + m_camera->FlushEffect(); + Abort(); + m_bError = TRUE; + m_displayText->DisplayError(ERR_TOOMANY, m_object->RetPosition(0)); + return FALSE; + } + CreateLight(); + } + + pos = m_buildingPos; + pos.y += m_buildingHeight*m_progress; + m_building->SetPosition(0, pos); // le batiment monte + + m_building->SetZoom(0, m_progress*0.75f+0.25f); + m_metal->SetZoom(0, 1.0f-m_progress); + + a = (2.0f-2.0f*m_progress); + if ( a > 1.0f ) a = 1.0f; + dir.x = (Rand()-0.5f)*a*0.1f; + dir.z = (Rand()-0.5f)*a*0.1f; + dir.y = (Rand()-0.5f)*a*0.1f; + m_building->SetCirVibration(dir); + + if ( !m_bBlack && m_progress >= 0.25f ) + { + BlackLight(); + } + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_metal->RetPosition(0); + speed.x = (Rand()-0.5f)*20.0f; + speed.z = (Rand()-0.5f)*20.0f; + speed.y = Rand()*10.0f; + dim.x = Rand()*6.0f+4.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFIRE); + + pos = D3DVECTOR(0.0f, 0.5f, 0.0f); + mat = m_object->RetWorldMatrix(14); + pos = Transform(*mat, pos); + speed = m_metal->RetPosition(0); + speed.x += (Rand()-0.5f)*5.0f; + speed.z += (Rand()-0.5f)*5.0f; + speed -= pos; + dim.x = 2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFIREZ); + + if ( Rand() < 0.3f ) + { + m_sound->Play(SOUND_BUILD, m_object->RetPosition(0), 0.5f, 1.0f*Rand()*1.5f); + } + } + + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskBuild::Start(ObjectType type) +{ + D3DVECTOR pos, speed, pv, pm; + Error err; + float iAngle, oAngle; + + m_type = type; + m_lastParticule = 0.0f; + m_progress = 0.0f; + + iAngle = m_object->RetAngleY(0); + iAngle = NormAngle(iAngle); // 0..2*PI + oAngle = iAngle; + + m_bError = TRUE; // opération impossible + + pos = m_object->RetPosition(0); + if ( pos.y < m_water->RetLevel() ) return ERR_BUILD_WATER; + + if ( !m_physics->RetLand() ) return ERR_BUILD_FLY; + + speed = m_physics->RetMotorSpeed(); + if ( speed.x != 0.0f || + speed.z != 0.0f ) return ERR_BUILD_MOTOR; + + if ( m_object->RetFret() != 0 ) return ERR_MANIP_BUSY; + + m_metal = SearchMetalObject(oAngle, 2.0f, 100.0f, PI*0.25f, err); + if ( err == ERR_BUILD_METALNEAR && m_metal != 0 ) + { + err = FlatFloor(); + if ( err != ERR_OK ) return err; + return ERR_BUILD_METALNEAR; + } + if ( err != ERR_OK ) return err; + + err = FlatFloor(); + if ( err != ERR_OK ) return err; + + m_metal->SetLock(TRUE); // plus utilisable + m_camera->StartCentering(m_object, PI*0.15f, 99.9f, 0.0f, 1.0f); + + m_phase = TBP_TURN; // rotation préliminaire nécessaire + m_angleY = oAngle; // angle à atteindre + + pv = m_object->RetPosition(0); + pv.y += 8.3f; + pm = m_metal->RetPosition(0); + m_angleZ = RotateAngle(Length2d(pv, pm), Abs(pv.y-pm.y)); + + m_physics->SetFreeze(TRUE); // on ne bouge plus + + m_bBuild = FALSE; // pas encore construit + m_bError = FALSE; // ok + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskBuild::IsEnded() +{ + CAuto* automat; + float angle, dist, time; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + + if ( m_phase == TBP_TURN ) // rotation préliminaire ? + { + angle = m_object->RetAngleY(0); + angle = NormAngle(angle); // 0..2*PI + + if ( TestAngle(angle, m_angleY-PI*0.01f, m_angleY+PI*0.01f) ) + { + m_physics->SetMotorSpeedZ(0.0f); + + dist = Length(m_object->RetPosition(0), m_metal->RetPosition(0)); + if ( dist > 30.0f ) + { + time = m_physics->RetLinTimeLength(dist-30.0f, 1.0f); + m_speed = 1.0f/time; + } + else + { + time = m_physics->RetLinTimeLength(30.0f-dist, -1.0f); + m_speed = 1.0f/time; + } + m_phase = TBP_MOVE; + m_progress = 0.0f; + } + return ERR_CONTINUE; + } + + if ( m_phase == TBP_MOVE ) // avance/recule préliminaire ? + { + dist = Length(m_object->RetPosition(0), m_metal->RetPosition(0)); + + if ( dist >= 25.0f && dist <= 35.0f ) + { + m_physics->SetMotorSpeedX(0.0f); + m_motion->SetAction(MHS_GUN); // prend arme + + m_phase = TBP_TAKE; + m_speed = 1.0f/1.0f; + m_progress = 0.0f; + } + else + { + if ( m_progress > 1.0f ) // timeout ? + { + m_metal->SetLock(FALSE); // de nouveau utilisable + if ( dist < 30.0f ) return ERR_BUILD_METALNEAR; + else return ERR_BUILD_METALAWAY; + } + } + return ERR_CONTINUE; + } + + if ( m_phase == TBP_TAKE ) // prend arme ? + { + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + m_motion->SetAction(MHS_FIRE); // position de tir + m_object->SetObjectParent(14, 4); + m_object->SetPosition(14, D3DVECTOR(0.6f, 0.1f, 0.3f)); + m_object->SetAngleZ(14, 0.0f); + + m_phase = TBP_PREP; + m_speed = 1.0f/1.0f; + m_progress = 0.0f; + } + + if ( m_phase == TBP_PREP ) // prépare ? + { + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + m_soundChannel = m_sound->Play(SOUND_TREMBLE, m_object->RetPosition(0), 0.0f, 1.0f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 0.7f, 1.0f, 1.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.7f, 1.5f, 7.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.5f, 2.0f, SOPER_STOP); + + m_camera->StartEffect(CE_VIBRATION, m_metal->RetPosition(0), 1.0f); + + m_phase = TBP_BUILD; + m_speed = 1.0f/10.f; // durée de 10s + m_progress = 0.0f; + } + + if ( m_phase == TBP_BUILD ) // construction ? + { + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + DeleteMark(m_metal->RetPosition(0), 20.0f); + + m_metal->DeleteObject(); // supprime le métal + delete m_metal; + m_metal = 0; + + m_building->SetZoom(0, 1.0f); + m_building->SetCirVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_building->SetLock(FALSE); // batiment utilisable + m_main->CreateShortcuts(); + m_displayText->DisplayError(INFO_BUILD, m_buildingPos, 10.0f, 50.0f); + + automat = m_building->RetAuto(); + if ( automat != 0 ) + { + automat->Init(); + } + + m_motion->SetAction(MHS_GUN); // remet arme + m_phase = TBP_TERM; + m_speed = 1.0f/1.0f; + m_progress = 0.0f; + } + + if ( m_phase == TBP_TERM ) // rotation terminale ? + { + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + m_motion->SetAction(-1); + m_object->SetObjectParent(14, 0); + m_object->SetPosition(14, D3DVECTOR(-1.5f, 0.3f, -1.35f)); + m_object->SetAngleZ(14, PI); + + if ( m_type == OBJECT_FACTORY || + m_type == OBJECT_RESEARCH || + m_type == OBJECT_NUCLEAR ) + { + + m_phase = TBP_RECEDE; + m_speed = 1.0f/1.5f; + m_progress = 0.0f; + } + } + + if ( m_phase == TBP_RECEDE ) // recule ? + { + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + m_physics->SetMotorSpeedX(0.0f); + } + + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskBuild::Abort() +{ + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + m_camera->StopCentering(m_object, 2.0f); + m_physics->SetFreeze(FALSE); // on bouge de nouveau + return TRUE; +} + + +// Vérifie si le terrain est assez plat et s'il n'y a pas +// un autre objet trop proche. + +Error CTaskBuild::FlatFloor() +{ + CObject *pObj; + ObjectType type; + D3DVECTOR center, pos, oPos, bPos; + FPOINT c, p; + float radius, max, oRadius, bRadius, angle, dist; + int i, j; + BOOL bLittleFlat, bBase; + + radius = 0.0f; + if ( m_type == OBJECT_DERRICK ) radius = 5.0f; + if ( m_type == OBJECT_FACTORY ) radius = 15.0f; + if ( m_type == OBJECT_REPAIR ) radius = 12.0f; + if ( m_type == OBJECT_STATION ) radius = 12.0f; + if ( m_type == OBJECT_CONVERT ) radius = 12.0f; + if ( m_type == OBJECT_TOWER ) radius = 7.0f; + if ( m_type == OBJECT_RESEARCH ) radius = 10.0f; + if ( m_type == OBJECT_RADAR ) radius = 5.0f; + if ( m_type == OBJECT_ENERGY ) radius = 8.0f; + if ( m_type == OBJECT_LABO ) radius = 12.0f; + if ( m_type == OBJECT_NUCLEAR ) radius = 20.0f; + if ( m_type == OBJECT_PARA ) radius = 20.0f; + if ( m_type == OBJECT_INFO ) radius = 5.0f; + if ( radius == 0.0f ) return ERR_GENERIC; + + center = m_metal->RetPosition(0); + angle = m_terrain->RetFineSlope(center); + bLittleFlat = ( angle < FLATLIMIT ); + + max = m_terrain->RetFlatZoneRadius(center, radius); + if ( max < radius ) // zone trop petite ? + { + if ( bLittleFlat ) + { + m_main->SetShowLimit(1, PARTILIMIT3, m_metal, center, max, 10.0f); + } + return bLittleFlat?ERR_BUILD_FLATLIT:ERR_BUILD_FLAT; + } + + max = 100000.0f; + bBase = FALSE; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; // inactif ? + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + if ( pObj == m_metal ) continue; + if ( pObj == m_object ) continue; + + type = pObj->RetType(); + if ( type == OBJECT_BASE ) + { + oPos = pObj->RetPosition(0); + dist = Length(center, oPos)-80.0f; + if ( dist < max ) + { + max = dist; + bPos = oPos; + bRadius = oRadius; + bBase = TRUE; + } + } + else + { + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + dist = Length(center, oPos)-oRadius; + if ( dist < max ) + { + max = dist; + bPos = oPos; + bRadius = oRadius; + bBase = FALSE; + } + } + } + } + if ( max < radius ) + { + m_main->SetShowLimit(1, PARTILIMIT2, m_metal, center, max, 10.0f); + if ( bRadius < 2.0f ) bRadius = 2.0f; + m_main->SetShowLimit(2, PARTILIMIT3, m_metal, bPos, bRadius, 10.0f); + return bBase?ERR_BUILD_BASE:ERR_BUILD_BUSY; + } + + max = 100000.0f; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; // inactif ? + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + if ( pObj == m_metal ) continue; + if ( pObj == m_object ) continue; + + type = pObj->RetType(); + if ( type == OBJECT_DERRICK || + type == OBJECT_FACTORY || + type == OBJECT_STATION || + type == OBJECT_CONVERT || + type == OBJECT_REPAIR || + type == OBJECT_TOWER || + type == OBJECT_RESEARCH || + type == OBJECT_RADAR || + type == OBJECT_ENERGY || + type == OBJECT_LABO || + type == OBJECT_NUCLEAR || + type == OBJECT_START || + type == OBJECT_END || + type == OBJECT_INFO || + type == OBJECT_PARA || + type == OBJECT_SAFE || + type == OBJECT_HUSTON ) // bâtiment ? + { + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + dist = Length(center, oPos)-oRadius; + if ( dist < max ) + { + max = dist; + bPos = oPos; + bRadius = oRadius; + } + } + } + } + if ( max-BUILDMARGIN < radius ) + { + m_main->SetShowLimit(1, PARTILIMIT2, m_metal, center, max-BUILDMARGIN, 10.0f); + m_main->SetShowLimit(2, PARTILIMIT3, m_metal, bPos, bRadius+BUILDMARGIN, 10.0f); + return bBase?ERR_BUILD_BASE:ERR_BUILD_NARROW; + } + + return ERR_OK; +} + +// Cherche l'objet métal le plus proche. + +CObject* CTaskBuild::SearchMetalObject(float &angle, float dMin, float dMax, + float aLimit, Error &err) +{ + CObject *pObj, *pBest; + D3DVECTOR iPos, oPos; + ObjectType type; + float min, iAngle, a, aa, aBest, distance, magic; + int i; + BOOL bMetal; + + iPos = m_object->RetPosition(0); + iAngle = m_object->RetAngleY(0); + iAngle = NormAngle(iAngle); // 0..2*PI + + min = 1000000.0f; + pBest = 0; + bMetal = FALSE; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; // objet inactif ? + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + + type = pObj->RetType(); + if ( type != OBJECT_METAL ) continue; + + bMetal = TRUE; // métal existe + + oPos = pObj->RetPosition(0); + distance = Length(oPos, iPos); + a = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + + if ( distance > dMax ) continue; + if ( !TestAngle(a, iAngle-aLimit, iAngle+aLimit) ) continue; + + if ( distance < dMin ) + { + err = ERR_BUILD_METALNEAR; // trop proche + return pObj; + } + + aa = Abs(a-iAngle); + if ( aa > PI ) aa = PI*2.0f-aa; + magic = distance*aa; + + if ( magic < min ) + { + min = magic; + aBest = a; + pBest = pObj; + } + } + + if ( pBest == 0 ) + { + if ( bMetal ) err = ERR_BUILD_METALAWAY; // trop loin + else err = ERR_BUILD_METALINEX; // inexistant + } + else + { + angle = aBest; + err = ERR_OK; + } + return pBest; +} + +// Détruit toutes les marques proches. + +void CTaskBuild::DeleteMark(D3DVECTOR pos, float radius) +{ + CObject* pObj; + D3DVECTOR oPos; + ObjectType type; + float distance; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_MARKSTONE && + type != OBJECT_MARKURANIUM && + type != OBJECT_MARKKEYa && + type != OBJECT_MARKKEYb && + type != OBJECT_MARKKEYc && + type != OBJECT_MARKKEYd && + type != OBJECT_MARKPOWER ) continue; + + oPos = pObj->RetPosition(0); + distance = Length(oPos, pos); + if ( distance <= radius ) + { + pObj->DeleteObject(); // supprime la marque + delete pObj; + i --; + } + } +} + diff --git a/src/taskbuild.h b/src/taskbuild.h new file mode 100644 index 00000000..6d8a234d --- /dev/null +++ b/src/taskbuild.h @@ -0,0 +1,74 @@ +// taskbuild.h + +#ifndef _TASKBUILD_H_ +#define _TASKBUILD_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + + +#define BUILDMARGIN 16.0f +#define TBMAXLIGHT 4 + + +enum TaskBuildPhase +{ + TBP_TURN = 1, // tourne + TBP_MOVE = 2, // avance/recule + TBP_TAKE = 3, // prend arme + TBP_PREP = 4, // prépare + TBP_BUILD = 5, // construit + TBP_TERM = 6, // termine + TBP_RECEDE = 7, // recule terminal +}; + + + +class CTaskBuild : public CTask +{ +public: + CTaskBuild(CInstanceManager* iMan, CObject* object); + ~CTaskBuild(); + + BOOL EventProcess(const Event &event); + + Error Start(ObjectType type); + Error IsEnded(); + BOOL Abort(); + +protected: + Error FlatFloor(); + BOOL CreateBuilding(D3DVECTOR pos, float angle); + void CreateLight(); + void BlackLight(); + CObject* SearchMetalObject(float &angle, float dMin, float dMax, float aLimit, Error &err); + void DeleteMark(D3DVECTOR pos, float radius); + +protected: + ObjectType m_type; // type de construction + CObject* m_metal; // objet metal transformé + CObject* m_power; // pile du véhicule + CObject* m_building; // batiment construit + TaskBuildPhase m_phase; // phase de l'opération + BOOL m_bError; // TRUE -> opération impossible + BOOL m_bBuild; // TRUE -> batiment construit + BOOL m_bBlack; // TRUE -> lumières noir -> blanc + float m_time; // temps absolu + float m_lastParticule;// temps génération dernière particule + float m_progress; // progression (0..1) + float m_speed; // vitesse de la progression + float m_angleY; // angle de rotation du véhicule + float m_angleZ; // angle de rotation du canon + D3DVECTOR m_buildingPos; // position initiale du batiment + float m_buildingHeight;// hauteur du building + int m_lightRank[TBMAXLIGHT];// lumières pour les effets + int m_soundChannel; +}; + + +#endif //_TASKBUILD_H_ diff --git a/src/taskfire.cpp b/src/taskfire.cpp new file mode 100644 index 00000000..5347ad64 --- /dev/null +++ b/src/taskfire.cpp @@ -0,0 +1,382 @@ +// taskfire.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "sound.h" +#include "task.h" +#include "taskfire.h" + + + +#define ENERGY_FIRE (0.25f/2.5f) // énergie consommée /s de tir +#define ENERGY_FIREr (0.25f/1.5f) // énergie consommée /s de rayon +#define ENERGY_FIREi (0.10f/2.5f) // énergie consommée /s d'organique + + +// Constructeur de l'objet. + +CTaskFire::CTaskFire(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); + m_soundChannel = -1; +} + +// Destructeur de l'objet. + +CTaskFire::~CTaskFire() +{ + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } +} + + +// Gestion d'un événement. + +BOOL CTaskFire::EventProcess(const Event &event) +{ + CObject* power; + CPhysics* physics; + D3DMATRIX* mat; + D3DVECTOR pos, speed, dir, vib; + ObjectType type; + FPOINT dim; + float energy, fire; + int i, channel; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + m_time += event.rTime; + m_lastSound -= event.rTime; + m_progress += event.rTime*m_speed; + + power = m_object->RetPower(); + if ( power != 0 ) + { + energy = power->RetEnergy(); + if ( m_bOrganic ) fire = ENERGY_FIREi; + else if ( m_bRay ) fire = ENERGY_FIREr; + else fire = ENERGY_FIRE; + energy -= event.rTime*fire/power->RetCapacity(); + power->SetEnergy(energy); + } + + if ( m_lastParticule+0.05f <= m_time ) + { + m_lastParticule = m_time; + + if ( m_bOrganic ) + { + mat = m_object->RetWorldMatrix(1); // canon-insecte + + for ( i=0 ; i<6 ; i++ ) + { + pos = D3DVECTOR(0.0f, 2.5f, 0.0f); + pos = Transform(*mat, pos); + + speed = D3DVECTOR(200.0f, 0.0f, 0.0f); + + physics = m_object->RetPhysics(); + if ( physics != 0 ) + { + speed += physics->RetLinMotion(MO_REASPEED); + } + + speed.x += (Rand()-0.5f)*10.0f; + speed.y += (Rand()-0.5f)*20.0f; + speed.z += (Rand()-0.5f)*30.0f; + speed = Transform(*mat, speed); + speed -= pos; + + dim.x = Rand()*0.5f+0.5f; + dim.y = dim.x; + + channel = m_particule->CreateParticule(pos, speed, dim, PARTIGUN4, 0.8f, 0.0f, 0.0f); + m_particule->SetObjectFather(channel, m_object); + } + } + else if ( m_bRay ) + { + mat = m_object->RetWorldMatrix(2); // canon + + for ( i=0 ; i<4 ; i++ ) + { + pos = D3DVECTOR(4.0f, 0.0f, 0.0f); + pos.y += (rand()%3-1)*1.5f; + pos.z += (rand()%3-1)*1.5f; + pos = Transform(*mat, pos); + + speed = D3DVECTOR(200.0f, 0.0f, 0.0f); + speed.x += (Rand()-0.5f)*6.0f; + speed.y += (Rand()-0.5f)*12.0f; + speed.z += (Rand()-0.5f)*12.0f; + speed = Transform(*mat, speed); + speed -= pos; + + dim.x = 1.0f; + dim.y = dim.x; + channel = m_particule->CreateTrack(pos, speed, dim, PARTITRACK11, + 2.0f, 200.0f, 0.5f, 1.0f); + m_particule->SetObjectFather(channel, m_object); + + speed = D3DVECTOR(5.0f, 0.0f, 0.0f); + speed.x += (Rand()-0.5f)*1.0f; + speed.y += (Rand()-0.5f)*2.0f; + speed.z += (Rand()-0.5f)*2.0f; + speed = Transform(*mat, speed); + speed -= pos; + speed.y += 5.0f; + + dim.x = 2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE2, 2.0f, 0.0f, 0.5f); + } + } + else + { + type = m_object->RetType(); + + if ( type == OBJECT_MOBILErc ) + { + mat = m_object->RetWorldMatrix(2); // canon + } + else + { + mat = m_object->RetWorldMatrix(1); // canon + } + + for ( i=0 ; i<3 ; i++ ) + { + if ( type == OBJECT_MOBILErc ) + { + pos = D3DVECTOR(0.0f, 0.0f, 0.0f); + } + else + { + pos = D3DVECTOR(3.0f, 1.0f, 0.0f); + } + pos.y += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + pos = Transform(*mat, pos); + + speed = D3DVECTOR(200.0f, 0.0f, 0.0f); + + physics = m_object->RetPhysics(); + if ( physics != 0 ) + { + speed += physics->RetLinMotion(MO_REASPEED); + } + + speed.x += (Rand()-0.5f)*3.0f; + speed.y += (Rand()-0.5f)*6.0f; + speed.z += (Rand()-0.5f)*6.0f; + speed = Transform(*mat, speed); + speed -= pos; + + dim.x = Rand()*0.7f+0.7f; + dim.y = dim.x; + + channel = m_particule->CreateParticule(pos, speed, dim, PARTIGUN1, 0.8f, 0.0f, 0.0f); + m_particule->SetObjectFather(channel, m_object); + } + + if ( type != OBJECT_MOBILErc && + m_progress > 0.3f ) + { + pos = D3DVECTOR(-1.0f, 1.0f, 0.0f); + pos.y += (Rand()-0.5f)*0.4f; + pos.z += (Rand()-0.5f)*0.4f; + pos = Transform(*mat, pos); + + speed = D3DVECTOR(-4.0f, 0.0f, 0.0f); + speed.x += (Rand()-0.5f)*2.0f; + speed.y += (Rand()-0.2f)*4.0f; + speed.z += (Rand()-0.5f)*4.0f; + speed = Transform(*mat, speed); + speed -= pos; + + dim.x = Rand()*1.2f+1.2f; + dim.y = dim.x; + + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f, 0.0f, 0.0f); +//? m_particule->CreateParticule(pos, speed, dim, PARTISMOKE2, 4.0f, 0.0f, 0.0f); + } + } + + dir = D3DVECTOR(0.0f, 0.0f, 0.0f); + if ( m_progress < 0.1f ) + { + dir.z = (PI*0.04f)*(m_progress*10.0f); + } + else if ( m_progress < 0.9f ) + { + dir.z = (PI*0.04f); + } + else + { + dir.z = (PI*0.04f)*(1.0f-(m_progress-0.9f)*10.0f); + } + m_object->SetInclinaison(dir); + + vib.x = (Rand()-0.5f)*0.01f; + vib.y = (Rand()-0.5f)*0.02f; + vib.z = (Rand()-0.5f)*0.02f; + m_object->SetCirVibration(vib); + + vib.x = (Rand()-0.5f)*0.20f; + vib.y = (Rand()-0.5f)*0.05f; + vib.z = (Rand()-0.5f)*0.20f; + m_object->SetLinVibration(vib); + } + + if ( m_bRay && m_lastSound <= 0.0f ) + { + m_lastSound = Rand()*0.4f+0.4f; + m_sound->Play(SOUND_FIREp, m_object->RetPosition(0)); + } + + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskFire::Start(float delay) +{ + CObject* power; + D3DVECTOR pos, goal, speed; + float energy, fire; + ObjectType type; + + m_bError = TRUE; // opération impossible + + type = m_object->RetType(); + if ( type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILErc ) return ERR_FIRE_VEH; + +//? if ( !m_physics->RetLand() ) return ERR_FIRE_FLY; + + speed = m_physics->RetMotorSpeed(); +//? if ( speed.x != 0.0f || +//? speed.z != 0.0f ) return ERR_FIRE_MOTOR; + + m_bRay = (type == OBJECT_MOBILErc); + + m_bOrganic = FALSE; + if ( type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii ) + { + m_bOrganic = TRUE; + } + + if ( delay == 0.0f ) + { + if ( m_bRay ) delay = 1.2f; + else delay = 2.0f; + } + m_delay = delay; + + power = m_object->RetPower(); + if ( power == 0 ) return ERR_FIRE_ENERGY; + energy = power->RetEnergy(); + if ( m_bOrganic ) fire = m_delay*ENERGY_FIREi; + else if ( m_bRay ) fire = m_delay*ENERGY_FIREr; + else fire = m_delay*ENERGY_FIRE; + if ( energy < fire/power->RetCapacity()+0.05f ) return ERR_FIRE_ENERGY; + + m_speed = 1.0f/m_delay; + m_progress = 0.0f; + m_time = 0.0f; + m_lastParticule = 0.0f; + m_lastSound = 0.0f; + m_bError = FALSE; // ok + +//? m_camera->StartCentering(m_object, PI*0.15f, 99.9f, 0.0f, 1.0f); + + if ( m_bOrganic ) + { + m_soundChannel = m_sound->Play(SOUND_FIREi, m_object->RetPosition(0), 1.0f, 1.0f, TRUE); + if ( m_soundChannel != -1 ) + { + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, m_delay, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 0.5f, SOPER_STOP); + } + } + else if ( m_bRay ) + { + } + else + { + m_soundChannel = m_sound->Play(SOUND_FIRE, m_object->RetPosition(0), 1.0f, 1.0f, TRUE); + if ( m_soundChannel != -1 ) + { + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, m_delay, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 0.5f, SOPER_STOP); + } + } + + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskFire::IsEnded() +{ + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskFire::Abort() +{ + m_object->SetInclinaison(D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_object->SetCirVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_object->SetLinVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + +//? m_camera->StopCentering(m_object, 1.0f); + return TRUE; +} + diff --git a/src/taskfire.h b/src/taskfire.h new file mode 100644 index 00000000..9ce1c9bd --- /dev/null +++ b/src/taskfire.h @@ -0,0 +1,42 @@ +// taskfire.h + +#ifndef _TASKFIRE_H_ +#define _TASKTIRE_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + +class CTaskFire : public CTask +{ +public: + CTaskFire(CInstanceManager* iMan, CObject* object); + ~CTaskFire(); + + BOOL EventProcess(const Event &event); + + Error Start(float delay); + Error IsEnded(); + BOOL Abort(); + +protected: + +protected: + float m_delay; + float m_progress; + BOOL m_bError; + BOOL m_bRay; + BOOL m_bOrganic; + float m_time; + float m_speed; + float m_lastParticule; + float m_lastSound; + int m_soundChannel; +}; + + +#endif //_TASKFIRE_H_ diff --git a/src/taskfireant.cpp b/src/taskfireant.cpp new file mode 100644 index 00000000..d82ec329 --- /dev/null +++ b/src/taskfireant.cpp @@ -0,0 +1,211 @@ +// taskfireant.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "motion.h" +#include "motionant.h" +#include "task.h" +#include "taskfireant.h" + + + + +// Constructeur de l'objet. + +CTaskFireAnt::CTaskFireAnt(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); + + m_phase = TFA_NULL; +} + +// Destructeur de l'objet. + +CTaskFireAnt::~CTaskFireAnt() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskFireAnt::EventProcess(const Event &event) +{ + D3DVECTOR dir, vib; + float a, g, cirSpeed; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + if ( m_object->RetFixed() ) // insecte sur le dos ? + { + m_bError = TRUE; + return FALSE; + } + + m_time += event.rTime; + m_progress += event.rTime*m_speed; + + if ( m_phase == TFA_TURN ) // rotation préliminaire ? + { + a = m_object->RetAngleY(0); + g = m_angle; + cirSpeed = Direction(a, g)*2.0f; + if ( cirSpeed > 2.0f ) cirSpeed = 2.0f; + if ( cirSpeed < -2.0f ) cirSpeed = -2.0f; + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + } + + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskFireAnt::Start(D3DVECTOR impact) +{ + D3DVECTOR pos; + ObjectType type; + + m_impact = impact; + + m_bError = TRUE; // opération impossible + if ( !m_physics->RetLand() ) return ERR_FIRE_VEH; + + type = m_object->RetType(); + if ( type != OBJECT_ANT ) return ERR_FIRE_VEH; + + // Insecte sur le dos ? + if ( m_object->RetFixed() ) return ERR_FIRE_VEH; + + m_physics->SetMotorSpeed(D3DVECTOR(0.0f, 0.0f, 0.0f)); + + pos = m_object->RetPosition(0); + m_angle = RotateAngle(m_impact.x-pos.x, pos.z-m_impact.z); // CW ! + + m_phase = TFA_TURN; + m_speed = 1.0f/1.0f; + m_progress = 0.0f; + m_time = 0.0f; + m_lastParticule = 0.0f; + m_bError = FALSE; // ok + m_bFire = FALSE; // un seul coup ! + + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskFireAnt::IsEnded() +{ + D3DMATRIX* mat; + D3DVECTOR pos, speed; + FPOINT dim; + float angle, dist; + int i, channel; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + if ( m_object->RetFixed() ) return ERR_STOP; // insecte sur le dos ? + + if ( m_phase == TFA_TURN ) // rotation ? + { + angle = m_object->RetAngleY(0); + angle = NormAngle(angle); // 0..2*PI + if ( !TestAngle(angle, m_angle-PI*0.05f, m_angle+PI*0.05f) ) return ERR_CONTINUE; + + m_physics->SetMotorSpeedZ(0.0f); // rotation terminée + + m_phase = TFA_PREPARE; +//? m_speed = 1.0f/1.5f; + m_speed = 1.0f/0.4f; + m_progress = 0.0f; +//? m_motion->SetAction(MAS_PREPARE, 1.5f); + m_motion->SetAction(MAS_PREPARE, 0.4f); + } + + if ( m_phase == TFA_PREPARE ) // préparation ? + { + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + m_phase = TFA_FIRE; +//? m_speed = 1.0f/2.0f; + m_speed = 1.0f/0.5f; + m_progress = 0.0f; +//? m_motion->SetAction(MAS_FIRE, 2.0f); + m_motion->SetAction(MAS_FIRE, 0.5f); + } + + if ( m_phase == TFA_FIRE ) // tir ? + { + if ( m_progress > 0.75f && !m_bFire ) + { + m_bFire = TRUE; // un seul coup + + for ( i=0 ; i<20 ; i++ ) + { + pos = D3DVECTOR(-2.5f, -0.7f, 0.0f); + mat = m_object->RetWorldMatrix(2); + pos = Transform(*mat, pos); + dist = Length(pos, m_impact); + speed = m_impact-pos; + speed.x += (Rand()-0.5f)*dist*1.2f; + speed.y += (Rand()-0.5f)*dist*0.4f+50.0f; + speed.z += (Rand()-0.5f)*dist*1.2f; + dim.x = 1.0f; + dim.y = dim.x; + channel = m_particule->CreateParticule(pos, speed, dim, PARTIGUN2, 2.0f, 100.0f, 0.0f); + m_particule->SetObjectFather(channel, m_object); + } + } + + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + m_phase = TFA_TERMINATE; +//? m_speed = 1.0f/0.9f; + m_speed = 1.0f/0.4f; + m_progress = 0.0f; +//? m_motion->SetAction(MAS_TERMINATE, 0.9f); + m_motion->SetAction(MAS_TERMINATE, 0.4f); + } + + if ( m_phase == TFA_TERMINATE ) // termine ? + { + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + m_phase = TFA_NULL; + m_speed = 1.0f/1.0f; + m_progress = 0.0f; + } + + Abort(); + return ERR_STOP; +} + + +// Termine brutalement l'action en cours. + +BOOL CTaskFireAnt::Abort() +{ + m_motion->SetAction(-1); + return TRUE; +} + diff --git a/src/taskfireant.h b/src/taskfireant.h new file mode 100644 index 00000000..d251b9b4 --- /dev/null +++ b/src/taskfireant.h @@ -0,0 +1,53 @@ +// taskfireant.h + +#ifndef _TASKFIREANT_H_ +#define _TASKTIREANT_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + + +enum TaskFireAnt +{ + TFA_NULL = 0, // rien à faire + TFA_TURN = 1, // tourne + TFA_PREPARE = 2, // prépare position de tir + TFA_FIRE = 3, // tir + TFA_TERMINATE = 4, // termine position de tir +}; + + + +class CTaskFireAnt : public CTask +{ +public: + CTaskFireAnt(CInstanceManager* iMan, CObject* object); + ~CTaskFireAnt(); + + BOOL EventProcess(const Event &event); + + Error Start(D3DVECTOR impact); + Error IsEnded(); + BOOL Abort(); + +protected: + +protected: + D3DVECTOR m_impact; + TaskFireAnt m_phase; + float m_progress; + float m_speed; + float m_angle; + BOOL m_bError; + BOOL m_bFire; + float m_time; + float m_lastParticule; +}; + + +#endif //_TASKFIREANT_H_ diff --git a/src/taskflag.cpp b/src/taskflag.cpp new file mode 100644 index 00000000..a475e678 --- /dev/null +++ b/src/taskflag.cpp @@ -0,0 +1,305 @@ +// taskflag.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "terrain.h" +#include "water.h" +#include "object.h" +#include "pyro.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "motion.h" +#include "motionhuman.h" +#include "sound.h" +#include "task.h" +#include "taskflag.h" + + + + + +// Constructeur de l'objet. + +CTaskFlag::CTaskFlag(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); +} + +// Destructeur de l'objet. + +CTaskFlag::~CTaskFlag() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskFlag::EventProcess(const Event &event) +{ + if ( m_bError ) return TRUE; + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_time += event.rTime; + + return TRUE; +} + + + +// Assigne le but à atteindre. + +Error CTaskFlag::Start(TaskFlagOrder order, int rank) +{ + D3DVECTOR pos, speed; + Error err; + + m_order = order; + m_time = 0.0f; + + m_bError = TRUE; // opération impossible + if ( !m_physics->RetLand() ) + { + pos = m_object->RetPosition(0); + if ( pos.y < m_water->RetLevel() ) return ERR_FLAG_WATER; + return ERR_FLAG_FLY; + } + + speed = m_physics->RetMotorSpeed(); + if ( speed.x != 0.0f || + speed.z != 0.0f ) return ERR_FLAG_MOTOR; + + if ( m_object->RetFret() != 0 ) return ERR_FLAG_BUSY; + + if ( order == TFL_CREATE ) + { + err = CreateFlag(rank); + if ( err != ERR_OK ) return err; + } + + if ( order == TFL_DELETE ) + { + err = DeleteFlag(); + if ( err != ERR_OK ) return err; + } + + m_bError = FALSE; + + m_motion->SetAction(MHS_FLAG); // met/enlève drapeau + m_camera->StartCentering(m_object, PI*0.3f, 99.9f, 0.0f, 0.5f); + + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskFlag::IsEnded() +{ + if ( m_engine->RetPause() ) return ERR_CONTINUE; + + if ( m_bError ) return ERR_STOP; + if ( m_time < 2.0f ) return ERR_CONTINUE; + + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskFlag::Abort() +{ + m_motion->SetAction(-1); + m_camera->StopCentering(m_object, 2.0f); + return TRUE; +} + + + +// Retourne l'objet le plus proche d'une position donnée. + +CObject* CTaskFlag::SearchNearest(D3DVECTOR pos, ObjectType type) +{ + ObjectType oType; + CObject *pObj, *pBest; + D3DVECTOR oPos; + float min, dist; + int i; + + min = 100000.0f; + pBest = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetEnable() ) continue; + + oType = pObj->RetType(); + if ( type == OBJECT_NULL ) + { + if ( oType != OBJECT_FLAGb && + oType != OBJECT_FLAGr && + oType != OBJECT_FLAGg && + oType != OBJECT_FLAGy && + oType != OBJECT_FLAGv ) continue; + } + else + { + if ( oType != type ) continue; + } + + oPos = pObj->RetPosition(0); + dist = Length2d(oPos, pos); + if ( dist < min ) + { + min = dist; + pBest = pObj; + } + } + return pBest; +} + +// Compte le nombre d'objets existants. + +int CTaskFlag::CountObject(ObjectType type) +{ + ObjectType oType; + CObject *pObj; + D3DVECTOR oPos; + int i, count; + + count = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetEnable() ) continue; + + oType = pObj->RetType(); + if ( type == OBJECT_NULL ) + { + if ( oType != OBJECT_FLAGb && + oType != OBJECT_FLAGr && + oType != OBJECT_FLAGg && + oType != OBJECT_FLAGy && + oType != OBJECT_FLAGv ) continue; + } + else + { + if ( oType != type ) continue; + } + + count ++; + } + return count; +} + +// Crée un indicateur de couleur. + +Error CTaskFlag::CreateFlag(int rank) +{ + CObject* pObj; + CObject* pNew; + CPyro* pyro; + D3DMATRIX* mat; + D3DVECTOR pos; + float dist; + int i; + + ObjectType table[5] = + { + OBJECT_FLAGb, + OBJECT_FLAGr, + OBJECT_FLAGg, + OBJECT_FLAGy, + OBJECT_FLAGv, + }; + + mat = m_object->RetWorldMatrix(0); + pos = Transform(*mat, D3DVECTOR(4.0f, 0.0f, 0.0f)); + + pObj = SearchNearest(pos, OBJECT_NULL); + if ( pObj != 0 ) + { + dist = Length(pos, pObj->RetPosition(0)); + if ( dist < 10.0f ) + { + return ERR_FLAG_PROXY; + } + } + + i = rank; + if ( CountObject(table[i]) >= 5 ) + { + return ERR_FLAG_CREATE; + } + + pNew = new CObject(m_iMan); + if ( !pNew->CreateFlag(pos, 0.0f, table[i]) ) + { + delete pNew; + return ERR_TOOMANY; + } + pNew->SetZoom(0, 0.0f); + + m_sound->Play(SOUND_WAYPOINT, pos); + pyro = new CPyro(m_iMan); + pyro->Create(PT_FLCREATE, pNew); + + return ERR_OK; +} + +// Supprime un indicateur de couleur. + +Error CTaskFlag::DeleteFlag() +{ + CObject* pObj; + CPyro* pyro; + D3DVECTOR iPos, oPos; + float iAngle, angle, aLimit, dist; + + iPos = m_object->RetPosition(0); + iAngle = m_object->RetAngleY(0); + iAngle = NormAngle(iAngle); // 0..2*PI + + pObj = SearchNearest(iPos, OBJECT_NULL); + if ( pObj == 0 ) + { + return ERR_FLAG_DELETE; + } + dist = Length(iPos, pObj->RetPosition(0)); + if ( dist > 10.0f ) + { + return ERR_FLAG_DELETE; + } + + oPos = pObj->RetPosition(0); + angle = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + aLimit = 45.0f*PI/180.0f; + if ( !TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + { + return ERR_FLAG_DELETE; + } + + m_sound->Play(SOUND_WAYPOINT, iPos); + pyro = new CPyro(m_iMan); + pyro->Create(PT_FLDELETE, pObj); + + return ERR_OK; +} + diff --git a/src/taskflag.h b/src/taskflag.h new file mode 100644 index 00000000..af940aac --- /dev/null +++ b/src/taskflag.h @@ -0,0 +1,48 @@ +// taskflag.h + +#ifndef _TASKFLAG_H_ +#define _TASKFLAG_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + + +enum TaskFlagOrder +{ + TFL_CREATE = 0, // met + TFL_DELETE = 1, // enlève +}; + + + +class CTaskFlag : public CTask +{ +public: + CTaskFlag(CInstanceManager* iMan, CObject* object); + ~CTaskFlag(); + + BOOL EventProcess(const Event &event); + + Error Start(TaskFlagOrder order, int rank); + Error IsEnded(); + BOOL Abort(); + +protected: + Error CreateFlag(int rank); + Error DeleteFlag(); + CObject* SearchNearest(D3DVECTOR pos, ObjectType type); + int CountObject(ObjectType type); + +protected: + TaskFlagOrder m_order; + float m_time; + BOOL m_bError; +}; + + +#endif //_TASKFLAG_H_ diff --git a/src/taskgoto.cpp b/src/taskgoto.cpp new file mode 100644 index 00000000..cd9575b9 --- /dev/null +++ b/src/taskgoto.cpp @@ -0,0 +1,2340 @@ +// taskgoto.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "terrain.h" +#include "water.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "task.h" +#include "taskgoto.h" + + + +#define FLY_DIST_GROUND 80.0f // distance minimale pour rester au sol +#define FLY_DEF_HEIGHT 50.0f // hauteur de vol par défaut +#define BM_DIM_STEP 5.0f + + + + +// Constructeur de l'objet. + +CTaskGoto::CTaskGoto(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); + + m_bmArray = 0; +} + +// Destructeur de l'objet. + +CTaskGoto::~CTaskGoto() +{ + BitmapClose(); +} + + +// Gestion d'un événement. + +BOOL CTaskGoto::EventProcess(const Event &event) +{ + D3DVECTOR pos, goal; + FPOINT rot, repulse; + float a, g, dist, linSpeed, cirSpeed, h, hh, factor, dir; + Error ret; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + // Objet momentanément immobile (fourmi sur le dos) ? + if ( m_object->RetFixed() ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + return TRUE; + } + + if ( m_error != ERR_OK ) return FALSE; + + if ( m_bWorm ) + { + WormFrame(event.rTime); + } + + if ( m_phase == TGP_BEAMLEAK ) // fuite ? + { + m_leakTime += event.rTime; + + pos = m_object->RetPosition(0); + + rot.x = m_leakPos.x-pos.x; + rot.y = m_leakPos.z-pos.z; + dist = Length(rot.x, rot.y); + rot.x /= dist; + rot.y /= dist; + + a = m_object->RetAngleY(0); + g = RotateAngle(rot.x, -rot.y); // CW ! + a = Direction(a, g)*1.0f; + cirSpeed = a; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + a = NormAngle(a); + if ( a > PI*0.5f && a < PI*1.5f ) + { + linSpeed = 1.0f; // obstacle derrière -> avance + cirSpeed = -cirSpeed; + } + else + { + linSpeed = -1.0f; // obstacle devant -> recule + } + + if ( m_bLeakRecede ) + { + linSpeed = -1.0f; + cirSpeed = 0.0f; + } + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + m_physics->SetMotorSpeedX(linSpeed); // avance + return TRUE; + } + + if ( m_phase == TGP_BEAMSEARCH ) // recherche chemin ? + { + if ( m_bmStep == 0 ) + { + // Libère la zone autour du départ. + BitmapClearCircle(m_object->RetPosition(0), BM_DIM_STEP*1.8f); + } + + pos = m_object->RetPosition(0); + + if ( m_bmFretObject == 0 ) + { + goal = m_goal; + dist = 0.0f; + } + else + { + goal = m_goalObject; + dist = TAKE_DIST+2.0f; + if ( m_bmFretObject->RetType() == OBJECT_BASE ) dist = 12.0f; + } + + ret = BeamSearch(pos, goal, dist); + if ( ret == ERR_OK ) + { +#if 0 + D3DVECTOR min, max; + min = pos; + max = m_goal; + if ( min.x > max.x ) Swap(min.x, max.x); + if ( min.z > max.z ) Swap(min.z, max.z); + min.x -= 50.0f; + min.z -= 50.0f; + max.x += 50.0f; + max.z += 50.0f; + BitmapDebug(min, max, m_object->RetPosition(0), m_goal); +#endif + if ( m_physics->RetLand() ) m_phase = TGP_BEAMWCOLD; + else m_phase = TGP_BEAMGOTO; + m_bmIndex = 0; + m_bmWatchDogPos = m_object->RetPosition(0); + m_bmWatchDogTime = 0.0f; + } + if ( ret == ERR_GOTO_IMPOSSIBLE || ret == ERR_GOTO_ITER ) + { +#if 0 + D3DVECTOR min, max; + min = pos; + max = m_goal; + if ( min.x > max.x ) Swap(min.x, max.x); + if ( min.z > max.z ) Swap(min.z, max.z); + min.x -= 50.0f; + min.z -= 50.0f; + max.x += 50.0f; + max.z += 50.0f; + BitmapDebug(min, max, m_object->RetPosition(0), m_goal); +#endif + m_error = ret; + return FALSE; + } + return TRUE; + } + + if ( m_phase == TGP_BEAMWCOLD ) // attend refroidissement réacteur ? + { + return TRUE; + } + + if ( m_phase == TGP_BEAMUP ) // décolle ? + { + m_physics->SetMotorSpeedY(1.0f); // monte + return TRUE; + } + + if ( m_phase == TGP_BEAMGOTO ) // goto dot list ? + { + if ( m_physics->RetCollision() ) // collision ? + { + m_physics->SetCollision(FALSE); // y'a plus + } + + pos = m_object->RetPosition(0); + + if ( m_physics->RetType() == TYPE_FLYING && m_altitude == 0.0f ) + { + if ( m_physics->RetLand() ) + { + m_physics->SetMotorSpeedY(0.0f); + } + else + { + m_physics->SetMotorSpeedY(-1.0f); + } + } + + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) + { + goal = m_bmPoints[m_bmIndex]; + goal.y = pos.y; + h = m_terrain->RetFloorHeight(goal, TRUE, TRUE); + dist = Length2d(pos, goal); + if ( dist != 0.0f ) // anticipe ? + { + linSpeed = m_physics->RetLinMotionX(MO_REASPEED); + linSpeed /= m_physics->RetLinMotionX(MO_ADVSPEED); + goal.x = pos.x + (goal.x-pos.x)*linSpeed*20.0f/dist; + goal.z = pos.z + (goal.z-pos.z)*linSpeed*20.0f/dist; + } + goal.y = pos.y; + hh = m_terrain->RetFloorHeight(goal, TRUE, TRUE); + h = Min(h, hh); + linSpeed = 0.0f; + if ( h < m_altitude-1.0f ) + { + linSpeed = 0.2f+((m_altitude-1.0f)-h)*0.1f; // monte + if ( linSpeed > 1.0f ) linSpeed = 1.0f; + } + if ( h > m_altitude+1.0f ) + { + linSpeed = -0.2f; // descend + } + m_physics->SetMotorSpeedY(linSpeed); + } + + rot.x = m_bmPoints[m_bmIndex].x-pos.x; + rot.y = m_bmPoints[m_bmIndex].z-pos.z; + dist = Length(rot.x, rot.y); + rot.x /= dist; + rot.y /= dist; + + a = m_object->RetAngleY(0); + g = RotateAngle(rot.x, -rot.y); // CW ! + cirSpeed = Direction(a, g)*2.0f; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + if ( dist < 4.0f ) cirSpeed *= dist/4.0f; // si proche -> tourne moins + + if ( m_bmIndex == m_bmTotal ) // dernier point ? + { + linSpeed = dist/(m_physics->RetLinStopLength()*1.5f); + if ( linSpeed > 1.0f ) linSpeed = 1.0f; + } + else + { + linSpeed = 1.0f; // fonce sans s'arrêter + } + + linSpeed *= 1.0f-(1.0f-0.3f)*Abs(cirSpeed); + +//? if ( dist < 20.0f && Abs(cirSpeed) >= 0.5f ) + if ( Abs(cirSpeed) >= 0.2f ) + { + linSpeed = 0.0f; // tourne d'abord, puis avance + } + + dist = Length2d(pos, m_bmWatchDogPos); + if ( dist < 1.0f && linSpeed != 0.0f ) + { + m_bmWatchDogTime += event.rTime; + } + else + { + m_bmWatchDogTime = 0.0f; + m_bmWatchDogPos = pos; + } + + if ( m_bmWatchDogTime >= 1.0f ) // immobile depuis longtemps ? + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + BeamStart(); // on recommence tout + return TRUE; + } + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + m_physics->SetMotorSpeedX(linSpeed); // avance + return TRUE; + } + + if ( m_phase == TGP_BEAMDOWN ) // atterri ? + { + m_physics->SetMotorSpeedY(-0.5f); // tombe + return TRUE; + } + + if ( m_phase == TGP_LAND ) // atterri ? + { + m_physics->SetMotorSpeedY(-0.5f); // tombe + return TRUE; + } + + if ( m_goalMode == TGG_EXPRESS ) + { + if ( m_crashMode == TGC_HALT ) + { + if ( m_physics->RetCollision() ) // collision ? + { + m_physics->SetCollision(FALSE); // y'a plus + m_error = ERR_STOP; + return TRUE; + } + } + + pos = m_object->RetPosition(0); + + if ( m_altitude > 0.0f ) + { + h = m_terrain->RetFloorHeight(pos, TRUE, TRUE); + linSpeed = 0.0f; + if ( h < m_altitude ) + { + linSpeed = 0.1f; // monte + } + if ( h > m_altitude ) + { + linSpeed = -0.2f; // descend + } + m_physics->SetMotorSpeedY(linSpeed); + } + + rot.x = m_goal.x-pos.x; + rot.y = m_goal.z-pos.z; + a = m_object->RetAngleY(0); + g = RotateAngle(rot.x, -rot.y); // CW ! + cirSpeed = Direction(a, g)*1.0f; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + m_physics->SetMotorSpeedX(1.0f); // avance + return TRUE; + } + + if ( m_phase != TGP_TURN && + m_physics->RetType() == TYPE_FLYING && + m_altitude > 0.0f ) + { + pos = m_object->RetPosition(0); + dist = Length2d(m_goal, pos); + factor = (dist-20.0f)/20.0f; + if ( factor < 0.0f ) factor = 0.0f; + if ( factor > 1.0f ) factor = 1.0f; + + h = m_terrain->RetFloorHeight(m_object->RetPosition(0), TRUE, TRUE); + linSpeed = 0.0f; + if ( h < (m_altitude-0.5f)*factor && factor == 1.0f ) + { + linSpeed = 0.1f; // monte + } + if ( h > m_altitude*factor ) + { + linSpeed = -0.2f; // descend + } + ComputeFlyingRepulse(dir); + linSpeed += dir*0.2f; + + m_physics->SetMotorSpeedY(linSpeed); + } + + if ( m_phase == TGP_ADVANCE ) // va vers l'objectif ? + { + if ( m_physics->RetCollision() ) // collision ? + { + m_physics->SetCollision(FALSE); // y'a plus + m_time = 0.0f; + m_phase = TGP_CRWAIT; + return TRUE; + } + +#if 0 + pos = m_object->RetPosition(0); + a = m_object->RetAngleY(0); + g = RotateAngle(m_goal.x-pos.x, pos.z-m_goal.z); // CW ! + cirSpeed = Direction(a, g)*1.0f; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + dist = Length2d(m_goal, pos); + linSpeed = dist/(m_physics->RetLinStopLength()*1.5f); + if ( linSpeed > 1.0f ) linSpeed = 1.0f; + + if ( dist < 20.0f && Abs(cirSpeed) >= 0.5f ) + { + linSpeed = 0.0f; // tourne d'abord, puis avance + } +#else + pos = m_object->RetPosition(0); + + rot.x = m_goal.x-pos.x; + rot.y = m_goal.z-pos.z; + dist = Length(rot.x, rot.y); + rot.x /= dist; + rot.y /= dist; + + ComputeRepulse(repulse); + rot.x += repulse.x*2.0f; + rot.y += repulse.y*2.0f; + + a = m_object->RetAngleY(0); + g = RotateAngle(rot.x, -rot.y); // CW ! + cirSpeed = Direction(a, g)*1.0f; +//? if ( m_physics->RetType() == TYPE_FLYING && +//? m_physics->RetLand() ) // volant au sol ? +//? { +//? cirSpeed *= 4.0f; // plus de pèche +//? } + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + dist = Length2d(m_goal, pos); + linSpeed = dist/(m_physics->RetLinStopLength()*1.5f); +//? if ( m_physics->RetType() == TYPE_FLYING && +//? m_physics->RetLand() ) // volant au sol ? +//? { +//? linSpeed *= 8.0f; // plus de pèche +//? } + if ( linSpeed > 1.0f ) linSpeed = 1.0f; + + linSpeed *= 1.0f-(1.0f-0.3f)*Abs(cirSpeed); + + if ( dist < 20.0f && Abs(cirSpeed) >= 0.5f ) + { + linSpeed = 0.0f; // tourne d'abord, puis avance + } +#endif + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + m_physics->SetMotorSpeedX(linSpeed); // avance + } + + if ( m_phase == TGP_TURN || // tourne vers l'objet ? + m_phase == TGP_CRTURN || // tourne après collision ? + m_phase == TGP_CLTURN ) // tourne après collision ? + { + a = m_object->RetAngleY(0); + g = m_angle; + cirSpeed = Direction(a, g)*1.0f; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + } + + if ( m_phase == TGP_CRWAIT || // attend après collision ? + m_phase == TGP_CLWAIT ) // attend après collision ? + { + m_time += event.rTime; + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + } + + if ( m_phase == TGP_CRADVANCE ) // avance après collision ? + { + if ( m_physics->RetCollision() ) // collision ? + { + m_physics->SetCollision(FALSE); // y'a plus + m_time = 0.0f; + m_phase = TGP_CLWAIT; + return TRUE; + } + m_physics->SetMotorSpeedX(0.5f); // avance mollo + } + + if ( m_phase == TGP_CLADVANCE ) // avance après collision ? + { + if ( m_physics->RetCollision() ) // collision ? + { + m_physics->SetCollision(FALSE); // y'a plus + m_time = 0.0f; + m_phase = TGP_CRWAIT; + return TRUE; + } + m_physics->SetMotorSpeedX(0.5f); // avance mollo + } + + if ( m_phase == TGP_MOVE ) // avance finale ? + { + m_bmTimeLimit -= event.rTime; + m_physics->SetMotorSpeedX(1.0f); + } + + return TRUE; +} + + +// Cherche une cible pour le ver. + +CObject* CTaskGoto::WormSearch(D3DVECTOR &impact) +{ + CObject* pObj; + CObject* pBest = 0; + D3DVECTOR iPos, oPos; + ObjectType oType; + float distance, min, radius; + int i; + + iPos = m_object->RetPosition(0); + min = 1000000.0f; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + oType = pObj->RetType(); + if ( oType != OBJECT_MOBILEfa && + oType != OBJECT_MOBILEta && + oType != OBJECT_MOBILEwa && + oType != OBJECT_MOBILEia && + oType != OBJECT_MOBILEfc && + oType != OBJECT_MOBILEtc && + oType != OBJECT_MOBILEwc && + oType != OBJECT_MOBILEic && + oType != OBJECT_MOBILEfi && + oType != OBJECT_MOBILEti && + oType != OBJECT_MOBILEwi && + oType != OBJECT_MOBILEii && + oType != OBJECT_MOBILEfs && + oType != OBJECT_MOBILEts && + oType != OBJECT_MOBILEws && + oType != OBJECT_MOBILEis && + oType != OBJECT_MOBILErt && + oType != OBJECT_MOBILErc && + oType != OBJECT_MOBILErr && + oType != OBJECT_MOBILErs && + oType != OBJECT_MOBILEsa && + oType != OBJECT_MOBILEtg && + oType != OBJECT_MOBILEft && + oType != OBJECT_MOBILEtt && + oType != OBJECT_MOBILEwt && + oType != OBJECT_MOBILEit && + oType != OBJECT_MOBILEdr && + oType != OBJECT_DERRICK && + oType != OBJECT_STATION && + oType != OBJECT_FACTORY && + oType != OBJECT_REPAIR && + oType != OBJECT_DESTROYER && + oType != OBJECT_CONVERT && + oType != OBJECT_TOWER && + oType != OBJECT_RESEARCH && + oType != OBJECT_RADAR && + oType != OBJECT_INFO && + oType != OBJECT_ENERGY && + oType != OBJECT_LABO && + oType != OBJECT_NUCLEAR && + oType != OBJECT_PARA && + oType != OBJECT_SAFE && + oType != OBJECT_HUSTON ) continue; + + if ( pObj->RetVirusMode() ) continue; // objet infecté ? + + if ( !pObj->GetCrashSphere(0, oPos, radius) ) continue; + distance = Length2d(oPos, iPos); + if ( distance < min ) + { + min = distance; + pBest = pObj; + } + } + if ( pBest == 0 ) return 0; + + impact = pBest->RetPosition(0); + return pBest; +} + +// Contamine les objets proches du ver. + +void CTaskGoto::WormFrame(float rTime) +{ + CObject* pObj; + D3DVECTOR impact, pos; + float dist; + + m_wormLastTime += rTime; + + if ( m_wormLastTime >= 0.5f ) + { + m_wormLastTime = 0.0f; + + pObj = WormSearch(impact); + if ( pObj != 0 ) + { + pos = m_object->RetPosition(0); + dist = Length(pos, impact); + if ( dist <= 15.0f ) + { + pObj->SetVirusMode(TRUE); // paf, infecté ! + } + } + } +} + + + +// Assigne le but à atteindre. +// "dist" est la distance de laquelle il faut s'éloigner pour +// prendre ou déposer un objet. + +Error CTaskGoto::Start(D3DVECTOR goal, float altitude, + TaskGotoGoal goalMode, TaskGotoCrash crashMode) +{ + D3DVECTOR pos; + CObject* target; + ObjectType type; + float dist; + int x, y; + + type = m_object->RetType(); + + if ( goalMode == TGG_DEFAULT ) + { + goalMode = TGG_STOP; + if ( type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_WORM ) + { + goalMode = TGG_EXPRESS; + } + } + + if ( crashMode == TGC_DEFAULT ) + { +//? crashMode = TGC_RIGHTLEFT; + crashMode = TGC_BEAM; + if ( type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_WORM || + type == OBJECT_BEE ) + { + crashMode = TGC_HALT; + } + } + + m_altitude = altitude; + m_goalMode = goalMode; + m_crashMode = crashMode; + m_goalObject = goal; + m_goal = goal; + + m_bTake = FALSE; + m_phase = TGP_ADVANCE; + m_error = ERR_OK; + m_try = 0; + m_bmFretObject = 0; + m_bmFinalMove = 0.0f; + + pos = m_object->RetPosition(0); + dist = Length2d(pos, m_goal); + if ( dist < 10.0f && m_crashMode == TGC_BEAM ) + { + m_crashMode = TGC_RIGHTLEFT; + } + + m_bWorm = FALSE; + if ( type == OBJECT_WORM ) + { + m_bWorm = TRUE; + m_wormLastTime = 0.0f; + } + + m_bApprox = FALSE; + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH || + type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) + { + m_bApprox = TRUE; + } + + if ( !m_bApprox && m_crashMode != TGC_BEAM ) + { + target = SearchTarget(goal, 1.0f); + if ( target != 0 ) + { + m_goal = target->RetPosition(0); + dist = 0.0f; + if ( !AdjustBuilding(m_goal, 1.0f, dist) ) + { + dist = 0.0f; + AdjustTarget(target, m_goal, dist); + } + m_bTake = TRUE; // objet à prendre à l'arrivée (rotation finale) + } + } + + m_lastDistance = 1000.0f; + m_physics->SetCollision(FALSE); + + if ( m_crashMode == TGC_BEAM ) // avec l'algorithme des rayons ? + { + target = SearchTarget(goal, 1.0f); + if ( target != 0 ) + { + m_goal = target->RetPosition(0); + dist = 4.0f; + if ( AdjustBuilding(m_goal, 1.0f, dist) ) + { + m_bmFinalMove = dist; + } + else + { + dist = 4.0f; + if ( AdjustTarget(target, m_goal, dist) ) + { + m_bmFretObject = target; // fret posé au sol + } + else + { + m_bmFinalMove = dist; + } + } + m_bTake = TRUE; // objet à prendre à l'arrivée (rotation finale) + } + + if ( m_physics->RetType() == TYPE_FLYING && m_altitude == 0.0f ) + { + pos = m_object->RetPosition(0); + dist = Length2d(pos, m_goal); + if ( dist > FLY_DIST_GROUND ) // plus de 20 mètres ? + { + m_altitude = FLY_DEF_HEIGHT; // altitude par défaut + } + } + + BeamStart(); + + if ( m_bmFretObject == 0 ) + { + x = (int)((m_goal.x+1600.0f)/BM_DIM_STEP); + y = (int)((m_goal.z+1600.0f)/BM_DIM_STEP); + if ( BitmapTestDot(0, x, y) ) // arrivée occupée ? + { +#if 0 + D3DVECTOR min, max; + min = m_object->RetPosition(0); + max = m_goal; + if ( min.x > max.x ) Swap(min.x, max.x); + if ( min.z > max.z ) Swap(min.z, max.z); + min.x -= 50.0f; + min.z -= 50.0f; + max.x += 50.0f; + max.z += 50.0f; + BitmapDebug(min, max, m_object->RetPosition(0), m_goal); +#endif + m_error = ERR_GOTO_BUSY; + return m_error; + } + } + } + + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskGoto::IsEnded() +{ + D3DVECTOR pos; + float limit, angle, dist, h, level; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_error != ERR_OK ) return m_error; + + pos = m_object->RetPosition(0); + + if ( m_phase == TGP_BEAMLEAK ) // fuite ? + { + if ( m_leakTime >= m_leakDelay ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + BeamInit(); + m_phase = TGP_BEAMSEARCH; // faudra chercher le chemin + } + return ERR_CONTINUE; + } + + if ( m_phase == TGP_BEAMSEARCH ) // recherche du chemin ? + { + return ERR_CONTINUE; + } + + if ( m_phase == TGP_BEAMWCOLD ) // attend refroidissement réacteur ? + { + if ( m_altitude != 0.0f && + m_physics->RetReactorRange() < 1.0f ) return ERR_CONTINUE; + m_phase = TGP_BEAMUP; + } + + if ( m_phase == TGP_BEAMUP ) // décolle ? + { + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) + { + level = m_terrain->RetFloorLevel(pos, TRUE, TRUE); + h = level+m_altitude-20.0f; + limit = m_terrain->RetFlyingMaxHeight(); + if ( h > limit ) h = limit; + if ( pos.y < h-1.0f ) return ERR_CONTINUE; + + m_physics->SetMotorSpeedY(0.0f); // stoppe la montée + } + m_phase = TGP_BEAMGOTO; + } + + if ( m_phase == TGP_BEAMGOTO ) // goto dot list ? + { + if ( m_altitude != 0.0f && + m_physics->RetReactorRange() < 0.1f ) // surchauffe ? + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + m_physics->SetMotorSpeedY(-1.0f); // tombe + m_phase = TGP_BEAMWCOLD; + return ERR_CONTINUE; + } + + if ( m_physics->RetLand() ) // au sol ? + { + limit = 1.0f; + } + else // en vol ? + { + limit = 2.0f; + if ( m_bmIndex < m_bmTotal ) limit *= 2.0f; // point intermédiaire + } + if ( m_bApprox ) limit = 2.0f; + + if ( Abs(pos.x - m_bmPoints[m_bmIndex].x) < limit && + Abs(pos.z - m_bmPoints[m_bmIndex].z) < limit ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + + m_bmIndex = BeamShortcut(); + + if ( m_bmIndex > m_bmTotal ) + { + m_phase = TGP_BEAMDOWN; + } + } + } + + if ( m_phase == TGP_BEAMDOWN ) // atteri ? + { + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) + { + if ( !m_physics->RetLand() ) return ERR_CONTINUE; + m_physics->SetMotorSpeedY(0.0f); // stoppe la descente + + m_altitude = 0.0f; + m_phase = TGP_BEAMGOTO; // avance finement au sol pour finir + m_bmIndex = m_bmTotal; + return ERR_CONTINUE; + } + + if ( m_bTake ) + { + m_angle = RotateAngle(m_goalObject.x-pos.x, pos.z-m_goalObject.z); + m_phase = TGP_TURN; + } + else + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + return ERR_STOP; + } + } + + if ( m_goalMode == TGG_EXPRESS ) + { + dist = Length2d(m_goal, pos); + if ( dist < 10.0f && dist > m_lastDistance ) + { + return ERR_STOP; + } + m_lastDistance = dist; + } + + if ( m_phase == TGP_ADVANCE ) // va vers l'objectif ? + { + if ( m_physics->RetLand() ) limit = 0.1f; // au sol + else limit = 1.0f; // en vol + if ( m_bApprox ) limit = 2.0f; + + if ( Abs(pos.x - m_goal.x) < limit && + Abs(pos.z - m_goal.z) < limit ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + m_phase = TGP_LAND; + } + } + + if ( m_phase == TGP_LAND ) // atterri ? + { + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) + { + if ( !m_physics->RetLand() ) return ERR_CONTINUE; + m_physics->SetMotorSpeedY(0.0f); + } + + if ( m_bTake ) + { + m_angle = RotateAngle(m_goalObject.x-pos.x, pos.z-m_goalObject.z); + m_phase = TGP_TURN; + } + else + { + return ERR_STOP; + } + } + + if ( m_phase == TGP_TURN ) // tourne vers l'objet ? + { + angle = NormAngle(m_object->RetAngleY(0)); + limit = 0.02f; + if ( m_bApprox ) limit = 0.10f; + if ( Abs(angle-m_angle) < limit ) + { + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + if ( m_bmFinalMove == 0.0f ) return ERR_STOP; + + m_bmFinalPos = m_object->RetPosition(0); + m_bmFinalDist = m_physics->RetLinLength(m_bmFinalMove); + m_bmTimeLimit = m_physics->RetLinTimeLength(Abs(m_bmFinalMove))*1.5f; + if ( m_bmTimeLimit < 0.5f ) m_bmTimeLimit = 0.5f; + m_phase = TGP_MOVE; + } + } + + if ( m_phase == TGP_CRWAIT ) // attend après collision ? + { + if ( m_crashMode == TGC_HALT ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + m_error = ERR_GENERIC; + return m_error; + } + if ( m_time >= 1.0f ) + { + if ( m_crashMode == TGC_RIGHTLEFT || + m_crashMode == TGC_RIGHT ) angle = PI/2.0f; // 90 à droite + else angle = -PI/2.0f; // 90 à gauche + m_angle = NormAngle(m_object->RetAngleY(0)+angle); + m_phase = TGP_CRTURN; +//? m_phase = TGP_ADVANCE; + } + } + + if ( m_phase == TGP_CRTURN ) // tourne après collision ? + { + angle = NormAngle(m_object->RetAngleY(0)); + limit = 0.1f; + if ( Abs(angle-m_angle) < limit ) + { + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + m_pos = pos; + m_phase = TGP_CRADVANCE; + } + } + + if ( m_phase == TGP_CRADVANCE ) // avance après collision ? + { + if ( Length(pos, m_pos) >= 5.0f ) + { + m_phase = TGP_ADVANCE; + } + } + + if ( m_phase == TGP_CLWAIT ) // attend après collision ? + { + if ( m_time >= 1.0f ) + { + if ( m_crashMode == TGC_RIGHTLEFT ) angle = -PI; + if ( m_crashMode == TGC_LEFTRIGHT ) angle = PI; + if ( m_crashMode == TGC_RIGHT ) angle = PI/2.0f; + if ( m_crashMode == TGC_LEFT ) angle = -PI/2.0f; + m_angle = NormAngle(m_object->RetAngleY(0)+angle); + m_phase = TGP_CLTURN; + } + } + + if ( m_phase == TGP_CLTURN ) // tourne après collision ? + { + angle = NormAngle(m_object->RetAngleY(0)); + limit = 0.1f; + if ( Abs(angle-m_angle) < limit ) + { + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + m_pos = pos; + m_phase = TGP_CLADVANCE; + } + } + + if ( m_phase == TGP_CLADVANCE ) // avance après collision ? + { + if ( Length(pos, m_pos) >= 10.0f ) + { + m_phase = TGP_ADVANCE; + m_try ++; + } + } + + if ( m_phase == TGP_MOVE ) // avance finale ? + { + if ( m_bmTimeLimit <= 0.0f ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe + Abort(); + return ERR_STOP; + } + + dist = Length(m_bmFinalPos, m_object->RetPosition(0)); + if ( dist < m_bmFinalDist ) return ERR_CONTINUE; + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + return ERR_STOP; + } + + return ERR_CONTINUE; +} + + +// Cherche l'objet à la position cible. + +CObject* CTaskGoto::SearchTarget(D3DVECTOR pos, float margin) +{ + CObject *pObj, *pBest; + D3DVECTOR oPos; + float dist, min; + int i; + + pBest = 0; + min = 1000000.0f; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; + if ( pObj->RetTruck() != 0 ) continue; // objet porté ? + + oPos = pObj->RetPosition(0); + dist = Length2d(pos, oPos); + + if ( dist <= margin && dist <= min ) + { + min = dist; + pBest = pObj; + } + } + + return pBest; +} + +// Ajuste la cible en fonction de l'objet. +// Retourne TRUE s'il s'agit de fret posé au sol, dont on peut +// s'approcher par n'importe quel côté. + +BOOL CTaskGoto::AdjustTarget(CObject* pObj, D3DVECTOR &pos, float &distance) +{ + ObjectType type; + Character* character; + D3DMATRIX* mat; + D3DVECTOR goal; + float dist, suppl; + + type = m_object->RetType(); + if ( type == OBJECT_BEE || + type == OBJECT_WORM ) + { + pos = pObj->RetPosition(0); + return FALSE; // approche unique + } + + type = pObj->RetType(); + + if ( type == OBJECT_FRET || + type == OBJECT_STONE || + type == OBJECT_URANIUM || + type == OBJECT_METAL || + type == OBJECT_POWER || + type == OBJECT_ATOMIC || + type == OBJECT_BULLET || + type == OBJECT_BBOX || + type == OBJECT_KEYa || + type == OBJECT_KEYb || + type == OBJECT_KEYc || + type == OBJECT_KEYd || + type == OBJECT_TNT || + type == OBJECT_SCRAP1 || + type == OBJECT_SCRAP2 || + type == OBJECT_SCRAP3 || + type == OBJECT_SCRAP4 || + type == OBJECT_SCRAP5 || + type == OBJECT_BOMB || + type == OBJECT_RUINmobilew1 || + type == OBJECT_RUINmobilew2 || + type == OBJECT_RUINmobilet1 || + type == OBJECT_RUINmobilet2 || + type == OBJECT_RUINmobiler1 || + type == OBJECT_RUINmobiler2 ) + { + pos = m_object->RetPosition(0); + goal = pObj->RetPosition(0); + dist = Length(goal, pos); + pos = (pos-goal)*(TAKE_DIST+distance)/dist + goal; + return TRUE; // approche par tous les côtés + } + + if ( type == OBJECT_BASE ) + { + pos = m_object->RetPosition(0); + goal = pObj->RetPosition(0); + dist = Length(goal, pos); + pos = (pos-goal)*(TAKE_DIST+distance)/dist + goal; + return TRUE; // approche par tous les côtés + } + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEtg || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr ) + { + character = pObj->RetCharacter(); + pos = character->posPower; + pos.x -= TAKE_DIST+TAKE_DIST_OTHER+distance; + mat = pObj->RetWorldMatrix(0); + pos = Transform(*mat, pos); + return FALSE; // approche unique + } + + if ( GetHotPoint(pObj, goal, TRUE, distance, suppl) ) + { + pos = goal; + distance += suppl; + return FALSE; // approche unique + } + + pos = pObj->RetPosition(0); + distance = 0.0f; + return FALSE; // approche unique +} + +// S'il on est sur un objet produit par un bâtiment (minerai produit +// par derrick), modifie la position par-rapport au bâtiment. + +BOOL CTaskGoto::AdjustBuilding(D3DVECTOR &pos, float margin, float &distance) +{ + CObject* pObj; + D3DVECTOR oPos; + float dist, suppl; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( !pObj->RetActif() ) continue; + if ( pObj->RetTruck() != 0 ) continue; // objet porté ? + + if ( !GetHotPoint(pObj, oPos, FALSE, 0.0f, suppl) ) continue; + dist = Length2d(pos, oPos); + if ( dist <= margin ) + { + GetHotPoint(pObj, pos, TRUE, distance, suppl); + distance += suppl; + return TRUE; + } + } + return FALSE; +} + +// Retourne le point où est produit ou posé qq chose sur un bâtiment. + +BOOL CTaskGoto::GetHotPoint(CObject *pObj, D3DVECTOR &pos, + BOOL bTake, float distance, float &suppl) +{ + ObjectType type; + D3DMATRIX* mat; + + pos = D3DVECTOR(0.0f, 0.0f, 0.0f); + suppl = 0.0f; + type = pObj->RetType(); + + if ( type == OBJECT_DERRICK ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 8.0f; + if ( bTake && distance != 0.0f ) suppl = 4.0f; + if ( bTake ) pos.x += TAKE_DIST+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_CONVERT ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 0.0f; + if ( bTake && distance != 0.0f ) suppl = 4.0f; + if ( bTake ) pos.x += TAKE_DIST+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_RESEARCH ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 10.0f; + if ( bTake && distance != 0.0f ) suppl = 2.5f; + if ( bTake ) pos.x += TAKE_DIST+TAKE_DIST_OTHER+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_ENERGY ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 6.0f; + if ( bTake && distance != 0.0f ) suppl = 6.0f; + if ( bTake ) pos.x += TAKE_DIST+TAKE_DIST_OTHER+distance; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_TOWER ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 5.0f; + if ( bTake && distance != 0.0f ) suppl = 4.0f; + if ( bTake ) pos.x += TAKE_DIST+TAKE_DIST_OTHER+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_LABO ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 6.0f; + if ( bTake && distance != 0.0f ) suppl = 6.0f; + if ( bTake ) pos.x += TAKE_DIST+TAKE_DIST_OTHER+distance; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_NUCLEAR ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 22.0f; + if ( bTake && distance != 0.0f ) suppl = 4.0f; + if ( bTake ) pos.x += TAKE_DIST+TAKE_DIST_OTHER+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_FACTORY ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 4.0f; + if ( bTake && distance != 0.0f ) suppl = 6.0f; + if ( bTake ) pos.x += TAKE_DIST+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_STATION ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 4.0f; + if ( bTake && distance != 0.0f ) suppl = 4.0f; + if ( bTake ) pos.x += distance; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_REPAIR ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 4.0f; + if ( bTake && distance != 0.0f ) suppl = 4.0f; + if ( bTake ) pos.x += distance; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_DESTROYER ) + { + mat = pObj->RetWorldMatrix(0); + pos.x += 0.0f; + if ( bTake && distance != 0.0f ) suppl = 4.0f; + if ( bTake ) pos.x += TAKE_DIST+distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + if ( type == OBJECT_PARA && m_physics->RetType() == TYPE_FLYING ) + { + mat = pObj->RetWorldMatrix(0); + if ( bTake && distance != 0.0f ) suppl = 20.0f; + if ( bTake ) pos.x += distance+suppl; + pos = Transform(*mat, pos); + return TRUE; + } + + suppl = 0.0f; + return FALSE; +} + + +// Cherche un objet trop proche qu'il faut fuire. + +BOOL CTaskGoto::LeakSearch(D3DVECTOR &pos, float &delay) +{ + CObject *pObj, *pObstacle; + D3DVECTOR iPos, oPos, bPos; + float iRadius, oRadius, bRadius, dist, min, dir; + int i, j; + + if ( !m_physics->RetLand() ) return FALSE; // en vol ? + + m_object->GetCrashSphere(0, iPos, iRadius); + + min = 100000.0f; + bRadius = 0.0f; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + if ( !pObj->RetActif() ) continue; + if ( pObj->RetTruck() != 0 ) continue; // objet porté ? + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + dist = Length2d(oPos, iPos); + if ( dist < min ) + { + min = dist; + bPos = oPos; + bRadius = oRadius; + pObstacle = pObj; + } + } + } + if ( min > iRadius+bRadius+4.0f ) return FALSE; + + m_bLeakRecede = FALSE; + + dist = 4.0f; + dir = 1.0f; + if ( pObstacle->RetType() == OBJECT_FACTORY ) + { + dist = 16.0f; + dir = -1.0f; + m_bLeakRecede = TRUE; // recule simplement + } + + pos = bPos; + delay = m_physics->RetLinTimeLength(dist, dir); + return TRUE; +} + + +// Calcule la force de répulsion en fonction des obstacles. +// La longueur du vecteur rendu est comprise entre 0 et 1. + +void CTaskGoto::ComputeRepulse(FPOINT &dir) +{ +#if 0 + D3DVECTOR iPos, oPos; + FPOINT repulse; + CObject *pObj; + float dist, iRadius, oRadius; + int i; + + dir.x = 0.0f; + dir.y = 0.0f; + + m_object->GetCrashSphere(0, iPos, iRadius); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, m_goalObject); + if ( dist <= 1.0f ) continue; + + pObj->GetGlobalSphere(oPos, oRadius); + oRadius += iRadius+m_physics->RetLinStopLength()*1.1f; + dist = Length2d(oPos, iPos); + if ( dist <= oRadius ) + { + repulse.x = iPos.x-oPos.x; + repulse.y = iPos.z-oPos.z; + +//? dist = 0.2f-(0.2f*dist/oRadius); + dist = powf(dist/oRadius, 2.0f); + dist = 0.2f-0.2f*dist; + repulse.x *= dist; + repulse.y *= dist; +//? repulse.x /= dist; +//? repulse.y /= dist; + + dir.x += repulse.x; + dir.y += repulse.y; + } + } +#else + ObjectType iType, oType; + D3DVECTOR iPos, oPos; + FPOINT repulse; + CObject *pObj; + float gDist, add, addi, fac, dist, iRadius, oRadius; + int i, j; + BOOL bAlien; + + dir.x = 0.0f; + dir.y = 0.0f; + + // Le ver passe partout et à travers tout ! + iType = m_object->RetType(); + if ( iType == OBJECT_WORM ) return; + + m_object->GetCrashSphere(0, iPos, iRadius); + gDist = Length(iPos, m_goal); + + add = m_physics->RetLinStopLength()*1.1f; // distance de freinage + fac = 2.0f; + + if ( iType == OBJECT_MOBILEwa || + iType == OBJECT_MOBILEwc || + iType == OBJECT_MOBILEwi || + iType == OBJECT_MOBILEws || + iType == OBJECT_MOBILEwt ) // roues ? + { + add = 5.0f; + fac = 1.5f; + } + if ( iType == OBJECT_MOBILEta || + iType == OBJECT_MOBILEtc || + iType == OBJECT_MOBILEti || + iType == OBJECT_MOBILEts || + iType == OBJECT_MOBILEtt || + iType == OBJECT_MOBILEdr ) // chenilles ? + { + add = 4.0f; + fac = 1.5f; + } + if ( iType == OBJECT_MOBILEfa || + iType == OBJECT_MOBILEfc || + iType == OBJECT_MOBILEfi || + iType == OBJECT_MOBILEfs || + iType == OBJECT_MOBILEft ) // volant ? + { + if ( m_physics->RetLand() ) + { + add = 5.0f; + fac = 1.5f; + } + else + { + add = 10.0f; + fac = 1.5f; + } + } + if ( iType == OBJECT_MOBILEia || + iType == OBJECT_MOBILEic || + iType == OBJECT_MOBILEii || + iType == OBJECT_MOBILEis || + iType == OBJECT_MOBILEit ) // pattes ? + { + add = 4.0f; + fac = 1.5f; + } + if ( iType == OBJECT_BEE ) // guêpe ? + { + if ( m_physics->RetLand() ) + { + add = 3.0f; + fac = 1.5f; + } + else + { + add = 5.0f; + fac = 1.5f; + } + } + + bAlien = FALSE; + if ( iType == OBJECT_MOTHER || + iType == OBJECT_ANT || + iType == OBJECT_SPIDER || + iType == OBJECT_BEE || + iType == OBJECT_WORM ) + { + bAlien = TRUE; + } + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + oType = pObj->RetType(); + + if ( oType == OBJECT_WORM ) continue; + + if ( bAlien ) + { + if ( oType == OBJECT_STONE || + oType == OBJECT_URANIUM || + oType == OBJECT_METAL || + oType == OBJECT_POWER || + oType == OBJECT_ATOMIC || + oType == OBJECT_BULLET || + oType == OBJECT_BBOX || + oType == OBJECT_KEYa || + oType == OBJECT_KEYb || + oType == OBJECT_KEYc || + oType == OBJECT_KEYd || + oType == OBJECT_TNT || + oType == OBJECT_SCRAP1 || + oType == OBJECT_SCRAP2 || + oType == OBJECT_SCRAP3 || + oType == OBJECT_SCRAP4 || + oType == OBJECT_SCRAP5 || + oType == OBJECT_BOMB || + (oType >= OBJECT_PLANT0 && + oType <= OBJECT_PLANT19 ) || + (oType >= OBJECT_MUSHROOM0 && + oType <= OBJECT_MUSHROOM9 ) ) continue; + } + + addi = add; + if ( iType == OBJECT_BEE && + oType == OBJECT_BEE ) + { + addi = 2.0f; // entre guèpes, faut pas trop s'embêter + } + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + if ( oPos.y-oRadius > iPos.y+iRadius ) continue; + if ( oPos.y+oRadius < iPos.y-iRadius ) continue; + + dist = Length(oPos, m_goal); + if ( dist <= 1.0f ) continue; // sur le but ? + + oRadius += iRadius+addi; + dist = Length2d(oPos, iPos); + if ( dist > gDist ) continue; // plus loin que le but ? + if ( dist <= oRadius ) + { + repulse.x = iPos.x-oPos.x; + repulse.y = iPos.z-oPos.z; + + dist = powf(dist/oRadius, fac); + dist = 0.2f-0.2f*dist; + repulse.x *= dist; + repulse.y *= dist; + + dir.x += repulse.x; + dir.y += repulse.y; + } + } + } +#endif +} + +// Calcule la force de répulsion verticale en fonction des obstacles. +// La longueur du vecteur rendu est comprise entre -1 et 1. + +void CTaskGoto::ComputeFlyingRepulse(float &dir) +{ + ObjectType oType; + D3DVECTOR iPos, oPos; + CObject *pObj; + float add, fac, dist, iRadius, oRadius, repulse; + int i, j; + + m_object->GetCrashSphere(0, iPos, iRadius); + + add = 0.0f; + fac = 1.5f; + dir = 0.0f; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + oType = pObj->RetType(); + + if ( oType == OBJECT_WORM ) continue; + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + oRadius += iRadius+add; + dist = Length2d(oPos, iPos); + if ( dist <= oRadius ) + { + repulse = iPos.y-oPos.y; + + dist = powf(dist/oRadius, fac); + dist = 0.2f-0.2f*dist; + repulse *= dist; + + dir += repulse; + } + } + } + + if ( dir < -1.0f ) dir = -1.0f; + if ( dir > 1.0f ) dir = 1.0f; +} + + + +// Parmi tous les points suivants, cherche s'il en existe un qui +// permet d'y aller directement à vol d'oiseau. Si oui, saute tous +// les points intermédiaires inutiles. + +int CTaskGoto::BeamShortcut() +{ + int i; + + for ( i=m_bmTotal ; i>=m_bmIndex+2 ; i-- ) // cherche depuis le dernier + { + if ( BitmapTestLine(m_bmPoints[m_bmIndex], m_bmPoints[i], 0.0f, FALSE) ) + { + return i; // bingo, trouvé + } + } + + return m_bmIndex+1; // va simplement au point suivant +} + +// C'est le grand départ. + +void CTaskGoto::BeamStart() +{ + D3DVECTOR min, max; + + BitmapOpen(); + BitmapObject(); + + min = m_object->RetPosition(0); + max = m_goal; + if ( min.x > max.x ) Swap(min.x, max.x); + if ( min.z > max.z ) Swap(min.z, max.z); + min.x -= 10.0f*BM_DIM_STEP; + min.z -= 10.0f*BM_DIM_STEP; + max.x += 10.0f*BM_DIM_STEP; + max.z += 10.0f*BM_DIM_STEP; + BitmapTerrain(min, max); + + if ( LeakSearch(m_leakPos, m_leakDelay) ) + { + m_phase = TGP_BEAMLEAK; // il faut d'abord fuire + m_leakTime = 0.0f; + } + else + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + BeamInit(); + m_phase = TGP_BEAMSEARCH; // faudra chercher le chemin + } +} + +// Initialisation avant le premier BeamSearch. + +void CTaskGoto::BeamInit() +{ + int i; + + for ( i=0 ; i 20.0f ) step = 20.0f; + nbIter = 200; // pour ne pas trop baisser le framerate + m_bmIterCounter = 0; + return BeamExplore(start, start, goal, goalRadius, 165.0f*PI/180.0f, 22, step, 0, nbIter); +} + +// prevPos: position précédente +// curPos: position courante +// goalPos: position qu'on cherche à atteindre +// angle: angle par rapport au but qu'on explore +// nbDiv: nombre du sous-divisions qu'on fait avec angle +// step longuer d'un pas +// i nombre de récursions effectuées +// nbIter nombre max. d'iterations qu'on a le droit de faire avant d'interrompre provisoirement + +Error CTaskGoto::BeamExplore(const D3DVECTOR &prevPos, const D3DVECTOR &curPos, + const D3DVECTOR &goalPos, float goalRadius, + float angle, int nbDiv, float step, + int i, int nbIter) +{ + D3DVECTOR newPos; + Error ret; + int iDiv, iClear, iLar; + + iLar = 0; + if ( i >= MAXPOINTS ) return ERR_GOTO_ITER; // trop de récursion + + if ( m_bmIter[i] == -1 ) + { + m_bmIter[i] = 0; + + if ( i == 0 ) + { + m_bmPoints[i] = curPos; + } + else + { + if ( !BitmapTestLine(prevPos, curPos, angle/nbDiv, TRUE) ) return ERR_GOTO_IMPOSSIBLE; + + m_bmPoints[i] = curPos; + + if ( Length2d(curPos, goalPos)-goalRadius <= step ) + { + if ( goalRadius == 0.0f ) + { + newPos = goalPos; + } + else + { + newPos = BeamPoint(curPos, goalPos, 0, Length2d(curPos, goalPos)-goalRadius); + } + if ( BitmapTestLine(curPos, newPos, angle/nbDiv, FALSE) ) + { + m_bmPoints[i+1] = newPos; + m_bmTotal = i+1; + return ERR_OK; + } + } + } + } + + if ( iLar >= m_bmIter[i] ) + { + newPos = BeamPoint(curPos, goalPos, 0, step); + ret = BeamExplore(curPos, newPos, goalPos, goalRadius, angle, nbDiv, step, i+1, nbIter); + if ( ret != ERR_GOTO_IMPOSSIBLE ) return ret; + m_bmIter[i] = iLar+1; + for ( iClear=i+1 ; iClear<=MAXPOINTS ; iClear++ ) m_bmIter[iClear] = -1; + m_bmIterCounter ++; + if ( m_bmIterCounter >= nbIter ) return ERR_CONTINUE; + } + iLar ++; + + for ( iDiv=1 ; iDiv<=nbDiv ; iDiv++ ) + { + if ( iLar >= m_bmIter[i] ) + { + newPos = BeamPoint(curPos, goalPos, angle*iDiv/nbDiv, step); + ret = BeamExplore(curPos, newPos, goalPos, goalRadius, angle, nbDiv, step, i+1, nbIter); + if ( ret != ERR_GOTO_IMPOSSIBLE ) return ret; + m_bmIter[i] = iLar+1; + for ( iClear=i+1 ; iClear<=MAXPOINTS ; iClear++ ) m_bmIter[iClear] = -1; + m_bmIterCounter ++; + if ( m_bmIterCounter >= nbIter ) return ERR_CONTINUE; + } + iLar ++; + + if ( iLar >= m_bmIter[i] ) + { + newPos = BeamPoint(curPos, goalPos, -angle*iDiv/nbDiv, step); + ret = BeamExplore(curPos, newPos, goalPos, goalRadius, angle, nbDiv, step, i+1, nbIter); + if ( ret != ERR_GOTO_IMPOSSIBLE ) return ret; + m_bmIter[i] = iLar+1; + for ( iClear=i+1 ; iClear<=MAXPOINTS ; iClear++ ) m_bmIter[iClear] = -1; + m_bmIterCounter ++; + if ( m_bmIterCounter >= nbIter ) return ERR_CONTINUE; + } + iLar ++; + } + + return ERR_GOTO_IMPOSSIBLE; +} + +// Soit une droite "start-goal". Calcule le point situé à la distance +// "step" du point "start" et faisant un angle "angle" avec la droite. + +D3DVECTOR CTaskGoto::BeamPoint(const D3DVECTOR &startPoint, + const D3DVECTOR &goalPoint, + float angle, float step) +{ + D3DVECTOR resPoint; + float goalAngle; + + goalAngle = RotateAngle(goalPoint.x-startPoint.x, goalPoint.z-startPoint.z); + + resPoint.x = startPoint.x + cosf(goalAngle+angle)*step; + resPoint.z = startPoint.z + sinf(goalAngle+angle)*step; + resPoint.y = 0.0f; + + return resPoint; +} + +// Affiche une partion de bitmap. + +void CTaskGoto::BitmapDebug(const D3DVECTOR &min, const D3DVECTOR &max, + const D3DVECTOR &start, const D3DVECTOR &goal) +{ + int minx, miny, maxx, maxy, x, y, i ,n; + char s[2000]; + + minx = (int)((min.x+1600.0f)/BM_DIM_STEP); + miny = (int)((min.z+1600.0f)/BM_DIM_STEP); + maxx = (int)((max.x+1600.0f)/BM_DIM_STEP); + maxy = (int)((max.z+1600.0f)/BM_DIM_STEP); + + if ( minx > maxx ) Swap(minx, maxx); + if ( miny > maxy ) Swap(miny, maxy); + + OutputDebugString("Bitmap :\n"); + for ( y=miny ; y<=maxy ; y++ ) + { + s[0] = 0; + for ( x=minx ; x<=maxx ; x++ ) + { + n = -1; + for ( i=0 ; i<=m_bmTotal ; i++ ) + { + if ( x == (int)((m_bmPoints[i].x+1600.0f)/BM_DIM_STEP) && + y == (int)((m_bmPoints[i].z+1600.0f)/BM_DIM_STEP) ) + { + n = i; + break; + } + } + + if ( BitmapTestDot(0, x,y) ) + { + strcat(s, "o"); + } + else + { + if ( BitmapTestDot(1, x,y) ) + { + strcat(s, "-"); + } + else + { + strcat(s, "."); + } + } + + if ( x == (int)((start.x+1600.0f)/BM_DIM_STEP) && + y == (int)((start.z+1600.0f)/BM_DIM_STEP) ) + { + strcat(s, "s"); + } + else + if ( x == (int)((goal.x+1600.0f)/BM_DIM_STEP) && + y == (int)((goal.z+1600.0f)/BM_DIM_STEP) ) + { + strcat(s, "g"); + } + else + if ( n != -1 ) + { + char ss[2]; + ss[0] = 'A'+n; + ss[1] = 0; + strcat(s, ss); + } + else + { + strcat(s, " "); + } + } + strcat(s, "\n"); + OutputDebugString(s); + } +} + +// Teste si un chemin le long d'une droite est possible. + +BOOL CTaskGoto::BitmapTestLine(const D3DVECTOR &start, const D3DVECTOR &goal, + float stepAngle, BOOL bSecond) +{ + D3DVECTOR pos, inc; + float dist, step; + float distNoB2; + int i, max, x, y; + + if ( m_bmArray == 0 ) return TRUE; + + dist = Length2d(start, goal); + if ( dist == 0.0f ) return TRUE; + step = BM_DIM_STEP*0.5f; + + inc.x = (goal.x-start.x)*step/dist; + inc.z = (goal.z-start.z)*step/dist; + + pos = start; + + if ( bSecond ) + { + x = (int)((pos.x+1600.0f)/BM_DIM_STEP); + y = (int)((pos.z+1600.0f)/BM_DIM_STEP); + BitmapSetDot(1, x, y); // met le flag du point de départ + } + + max = (int)(dist/step); + if ( max == 0 ) max = 1; + distNoB2 = BM_DIM_STEP*sqrtf(2.0f)/sinf(stepAngle); + for ( i=0 ; i 2 && BitmapTestDot(1, x, y) ) return FALSE; + + if ( step*(i+1) > distNoB2 && i < max-2 ) + { + BitmapSetDot(1, x, y); + } + } + + if ( BitmapTestDot(0, x, y) ) return FALSE; + } + return TRUE; +} + +// Ajoute les objets dans le bitmap. + +void CTaskGoto::BitmapObject() +{ + CObject *pObj; + ObjectType type; + D3DVECTOR iPos, oPos; + float iRadius, oRadius, h; + int i, j; + + m_object->GetCrashSphere(0, iPos, iRadius); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + + if ( pObj == m_object ) continue; + if ( pObj == m_bmFretObject ) continue; + if ( pObj->RetTruck() != 0 ) continue; + + h = m_terrain->RetFloorLevel(pObj->RetPosition(0), FALSE); + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) + { + h += m_altitude; + } + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + if ( m_physics->RetType() == TYPE_FLYING && m_altitude > 0.0f ) // volant ? + { + if ( oPos.y-oRadius > h+8.0f || + oPos.y+oRadius < h-8.0f ) continue; + } + else // rampant ? + { + if ( oPos.y-oRadius > h+8.0f ) continue; + } + + if ( type == OBJECT_PARA ) oRadius -= 2.0f; + BitmapSetCircle(oPos, oRadius+iRadius+4.0f); + } + } +} + +// Ajoute une portion de terrain dans le bitmap. + +void CTaskGoto::BitmapTerrain(const D3DVECTOR &min, const D3DVECTOR &max) +{ + int minx, miny, maxx, maxy; + + minx = (int)((min.x+1600.0f)/BM_DIM_STEP); + miny = (int)((min.z+1600.0f)/BM_DIM_STEP); + maxx = (int)((max.x+1600.0f)/BM_DIM_STEP); + maxy = (int)((max.z+1600.0f)/BM_DIM_STEP); + + BitmapTerrain(minx, miny, maxx, maxy); +} + +// Ajoute une portion de terrain dans le bitmap. + +void CTaskGoto::BitmapTerrain(int minx, int miny, int maxx, int maxy) +{ + ObjectType type; + D3DVECTOR p; + float aLimit, angle, h; + int x, y; + BOOL bAcceptWater, bFly; + + if ( minx > maxx ) Swap(minx, maxx); + if ( miny > maxy ) Swap(miny, maxy); + + if ( minx < 0 ) minx = 0; + if ( miny < 0 ) miny = 0; + if ( maxx > m_bmSize-1 ) maxx = m_bmSize-1; + if ( maxy > m_bmSize-1 ) maxy = m_bmSize-1; + + if ( minx > m_bmMinX ) minx = m_bmMinX; + if ( miny > m_bmMinY ) miny = m_bmMinY; + if ( maxx < m_bmMaxX ) maxx = m_bmMaxX; + if ( maxy < m_bmMaxY ) maxy = m_bmMaxY; + + if ( minx >= m_bmMinX && maxx <= m_bmMaxX && + miny >= m_bmMinY && maxy <= m_bmMaxY ) return; + + aLimit = 20.0f*PI/180.0f; + bAcceptWater = FALSE; + bFly = FALSE; + + type = m_object->RetType(); + + if ( type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEtg ) // roues ? + { + aLimit = 20.0f*PI/180.0f; + } + + if ( type == OBJECT_MOBILEta || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEts ) // chenilles ? + { + aLimit = 35.0f*PI/180.0f; + } + + if ( type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs ) // grosses chenilles ? + { + aLimit = 35.0f*PI/180.0f; + } + + if ( type == OBJECT_MOBILEsa ) // chenilles sous-marin ? + { + aLimit = 35.0f*PI/180.0f; + bAcceptWater = TRUE; + } + + if ( type == OBJECT_MOBILEdr ) // chenilles dessinateur ? + { + aLimit = 35.0f*PI/180.0f; + } + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEft ) // volant ? + { + aLimit = 15.0f*PI/180.0f; + bFly = TRUE; + } + + if ( type == OBJECT_MOBILEia || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILEii ) // pattes d'insecte ? + { + aLimit = 60.0f*PI/180.0f; + } + + for ( y=miny ; y<=maxy ; y++ ) + { + for ( x=minx ; x<=maxx ; x++ ) + { + if ( x >= m_bmMinX && x <= m_bmMaxX && + y >= m_bmMinY && y <= m_bmMaxY ) continue; + + p.x = x*BM_DIM_STEP-1600.0f; + p.z = y*BM_DIM_STEP-1600.0f; + + if ( bFly ) // robot volant ? + { + h = m_terrain->RetFloorLevel(p, TRUE); + if ( h >= m_terrain->RetFlyingMaxHeight()-5.0f ) + { + BitmapSetDot(0, x, y); + } + continue; + } + + if ( !bAcceptWater ) // ne va pas sous l'eau ? + { + h = m_terrain->RetFloorLevel(p, TRUE); + if ( h < m_water->RetLevel()-2.0f ) // sous l'eau (*) ? + { +//? BitmapSetDot(0, x, y); + BitmapSetCircle(p, BM_DIM_STEP*1.0f); + continue; + } + } + + angle = m_terrain->RetFineSlope(p); + if ( angle > aLimit ) + { + BitmapSetDot(0, x, y); + } + } + } + + m_bmMinX = minx; + m_bmMinY = miny; + m_bmMaxX = maxx; + m_bmMaxY = maxy; // agrandi la zone rectangulaire +} + +// (*) Accepte qu'un robot soit 50cm sous l'eau, par exemple +// sur Tropica 3 ! + +// Ouvre un bitmap vide. + +BOOL CTaskGoto::BitmapOpen() +{ + BitmapClose(); + + m_bmSize = (int)(3200.0f/BM_DIM_STEP); + m_bmArray = (unsigned char*)malloc(m_bmSize*m_bmSize/8*2); + ZeroMemory(m_bmArray, m_bmSize*m_bmSize/8*2); + + m_bmOffset = m_bmSize/2; + m_bmLine = m_bmSize/8; + + m_bmMinX = m_bmSize; // zone rectangulaire inexistante + m_bmMinY = m_bmSize; + m_bmMaxX = 0; + m_bmMaxY = 0; + + return TRUE; +} + +// Ferme le bitmap. + +BOOL CTaskGoto::BitmapClose() +{ + free(m_bmArray); + m_bmArray = 0; + return TRUE; +} + +// Met un cercle dans le bitmap. + +void CTaskGoto::BitmapSetCircle(const D3DVECTOR &pos, float radius) +{ + float d, r; + int cx, cy, ix, iy; + + cx = (int)((pos.x+1600.0f)/BM_DIM_STEP); + cy = (int)((pos.z+1600.0f)/BM_DIM_STEP); + r = radius/BM_DIM_STEP; + + for ( iy=cy-(int)r ; iy<=cy+(int)r ; iy++ ) + { + for ( ix=cx-(int)r ; ix<=cx+(int)r ; ix++ ) + { + d = Length((float)(ix-cx), (float)(iy-cy)); + if ( d > r ) continue; + BitmapSetDot(0, ix, iy); + } + } +} + +// Enlève un cercle dans le bitmap. + +void CTaskGoto::BitmapClearCircle(const D3DVECTOR &pos, float radius) +{ + float d, r; + int cx, cy, ix, iy; + + cx = (int)((pos.x+1600.0f)/BM_DIM_STEP); + cy = (int)((pos.z+1600.0f)/BM_DIM_STEP); + r = radius/BM_DIM_STEP; + + for ( iy=cy-(int)r ; iy<=cy+(int)r ; iy++ ) + { + for ( ix=cx-(int)r ; ix<=cx+(int)r ; ix++ ) + { + d = Length((float)(ix-cx), (float)(iy-cy)); + if ( d > r ) continue; + BitmapClearDot(0, ix, iy); + } + } +} + +// Met un point dans le bitmap. +// x:y: 0..m_bmSize-1 + +void CTaskGoto::BitmapSetDot(int rank, int x, int y) +{ + if ( x < 0 || x >= m_bmSize || + y < 0 || y >= m_bmSize ) return; + + m_bmArray[rank*m_bmLine*m_bmSize + m_bmLine*y + x/8] |= (1<= m_bmSize || + y < 0 || y >= m_bmSize ) return; + + m_bmArray[rank*m_bmLine*m_bmSize + m_bmLine*y + x/8] &= ~(1<= m_bmSize || + y < 0 || y >= m_bmSize ) return FALSE; + + if ( x < m_bmMinX || x > m_bmMaxX || + y < m_bmMinY || y > m_bmMaxY ) + { + BitmapTerrain(x-10,y-10, x+10,y+10); // refait une couche + } + + return m_bmArray[rank*m_bmLine*m_bmSize + m_bmLine*y + x/8] & (1< +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "object.h" +#include "sound.h" +#include "task.h" +#include "taskgungoal.h" + + + + +// Constructeur de l'objet. + +CTaskGunGoal::CTaskGunGoal(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); +} + +// Destructeur de l'objet. + +CTaskGunGoal::~CTaskGunGoal() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskGunGoal::EventProcess(const Event &event) +{ + float dir; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_progress += event.rTime*m_speed; + + if ( m_progress < 1.0f ) + { + dir = m_initialDirV + (m_finalDirV-m_initialDirV)*m_progress; + } + else + { + dir = m_finalDirV; + } + m_object->SetGunGoalV(dir); + + if ( m_progress < 1.0f ) + { + dir = m_initialDirH + (m_finalDirH-m_initialDirH)*m_progress; + } + else + { + dir = m_finalDirH; + } + m_object->SetGunGoalH(dir); + + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskGunGoal::Start(float dirV, float dirH) +{ + float speedV, speedH; + int i; + + m_initialDirV = m_object->RetGunGoalV(); + m_object->SetGunGoalV(dirV); + m_finalDirV = m_object->RetGunGoalV(); // direction possible + m_object->SetGunGoalV(m_initialDirV); // remet direction initiale + + if ( m_finalDirV == m_initialDirV ) + { + speedV = 100.0f; + } + else + { + speedV = 1.0f/(Abs(m_finalDirV-m_initialDirV)*1.0f); + } + + m_initialDirH = m_object->RetGunGoalH(); + m_object->SetGunGoalH(dirH); + m_finalDirH = m_object->RetGunGoalH(); // direction possible + m_object->SetGunGoalH(m_initialDirH); // remet direction initiale + + if ( m_finalDirH == m_initialDirH ) + { + speedH = 100.0f; + } + else + { + speedH = 1.0f/(Abs(m_finalDirH-m_initialDirH)*1.0f); + } + + m_speed = Min(speedV, speedH); + + if ( m_finalDirV != m_initialDirV || + m_finalDirH != m_initialDirH ) + { + i = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 0.3f, 1.5f, TRUE); + m_sound->AddEnvelope(i, 0.3f, 1.5f, 1.0f/m_speed, SOPER_STOP); + } + + m_progress = 0.0f; + + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskGunGoal::IsEnded() +{ + if ( m_engine->RetPause() ) return ERR_CONTINUE; + + if ( m_initialDirV == m_finalDirV && + m_initialDirH == m_finalDirH ) return ERR_STOP; + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + m_object->SetGunGoalV(m_finalDirV); + m_object->SetGunGoalH(m_finalDirH); + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskGunGoal::Abort() +{ + return TRUE; +} + diff --git a/src/taskgungoal.h b/src/taskgungoal.h new file mode 100644 index 00000000..63630a05 --- /dev/null +++ b/src/taskgungoal.h @@ -0,0 +1,38 @@ +// taskgungoal.h + +#ifndef _TASKGUNGOAL_H_ +#define _TASKGUNGOAL_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + +class CTaskGunGoal : public CTask +{ +public: + CTaskGunGoal(CInstanceManager* iMan, CObject* object); + ~CTaskGunGoal(); + + BOOL EventProcess(const Event &event); + + Error Start(float dirV, float dirH); + Error IsEnded(); + BOOL Abort(); + +protected: + +protected: + float m_progress; + float m_speed; + float m_initialDirV; // direction initiale + float m_finalDirV; // direction à atteindre + float m_initialDirH; // direction initiale + float m_finalDirH; // direction à atteindre +}; + + +#endif //_TASKGUNGOAL_H_ diff --git a/src/taskinfo.cpp b/src/taskinfo.cpp new file mode 100644 index 00000000..7789732f --- /dev/null +++ b/src/taskinfo.cpp @@ -0,0 +1,217 @@ +// taskinfo.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "sound.h" +#include "auto.h" +#include "autoinfo.h" +#include "task.h" +#include "taskinfo.h" + + + + +// Constructeur de l'objet. + +CTaskInfo::CTaskInfo(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); +} + +// Destructeur de l'objet. + +CTaskInfo::~CTaskInfo() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskInfo::EventProcess(const Event &event) +{ + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + m_progress += event.rTime*m_speed; // ça avance + m_time += event.rTime; + + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskInfo::Start(char *name, float value, float power, BOOL bSend) +{ + CObject* pInfo; + CAutoInfo* pAuto; + D3DVECTOR pos, goal; + Info info; + int i, total, op; + + m_bError = TRUE; + m_object->SetInfoReturn(NAN); + + pInfo = SearchInfo(power); // cherche borne + if ( pInfo == 0 ) + { + return ERR_INFO_NULL; + } + + pAuto = (CAutoInfo*)pInfo->RetAuto(); + if ( pAuto == 0 ) + { + return ERR_INFO_NULL; + } + + op = 1; // émission impossible + if ( bSend ) // send ? + { + total = pInfo->RetInfoTotal(); + for ( i=0 ; iRetInfo(i); + if ( strcmp(info.name, name) == 0 ) + { + info.value = value; + pInfo->SetInfo(i, info); + break; + } + } + if ( i == total ) + { + if ( total < OBJECTMAXINFO ) + { + strcpy(info.name, name); + info.value = value; + pInfo->SetInfo(total, info); + op = 2; // début de réception (pour la borne) + } + } + else + { + op = 2; // début de réception (pour la borne) + } + } + else // receive ? + { + total = pInfo->RetInfoTotal(); + for ( i=0 ; iRetInfo(i); + if ( strcmp(info.name, name) == 0 ) + { + m_object->SetInfoReturn(info.value); + break; + } + } + if ( i < total ) + { + op = 0; // début d'émission (pour la borne) + } + } + + pAuto->Start(op); + + if ( op == 0 ) // émission ? + { + pos = pInfo->RetPosition(0); + pos.y += 9.5f; + goal = m_object->RetPosition(0); + goal.y += 4.0f; + m_particule->CreateRay(pos, goal, PARTIRAY3, FPOINT(2.0f, 2.0f), 1.0f); + } + if ( op == 2 ) // réception ? + { + goal = pInfo->RetPosition(0); + goal.y += 9.5f; + pos = m_object->RetPosition(0); + pos.y += 4.0f; + m_particule->CreateRay(pos, goal, PARTIRAY3, FPOINT(2.0f, 2.0f), 1.0f); + } + + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + m_time = 0.0f; + + m_bError = FALSE; // ok + + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskInfo::IsEnded() +{ + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + + if ( m_progress < 1.0f ) return ERR_CONTINUE; + m_progress = 0.0f; + + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskInfo::Abort() +{ + return TRUE; +} + + +// Cherche la borne d'information la plus proche. + +CObject* CTaskInfo::SearchInfo(float power) +{ + CObject *pObj, *pBest; + D3DVECTOR iPos, oPos; + ObjectType type; + float dist, min; + int i; + + iPos = m_object->RetPosition(0); + + min = 100000.0f; + pBest = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type != OBJECT_INFO ) continue; + + if ( !pObj->RetActif() ) continue; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, iPos); + if ( dist > power ) continue; // trop loin ? + if ( dist < min ) + { + min = dist; + pBest = pObj; + } + } + + return pBest; +} + diff --git a/src/taskinfo.h b/src/taskinfo.h new file mode 100644 index 00000000..c2d6147d --- /dev/null +++ b/src/taskinfo.h @@ -0,0 +1,38 @@ +// taskinfo.h + +#ifndef _TASKINFO_H_ +#define _TASKINFO_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + + +class CTaskInfo : public CTask +{ +public: + CTaskInfo(CInstanceManager* iMan, CObject* object); + ~CTaskInfo(); + + BOOL EventProcess(const Event &event); + + Error Start(char *name, float value, float power, BOOL bSend); + Error IsEnded(); + BOOL Abort(); + +protected: + CObject* SearchInfo(float power); + +protected: + float m_progress; + float m_speed; + float m_time; + BOOL m_bError; +}; + + +#endif //_TASKINFO_H_ diff --git a/src/taskmanager.cpp b/src/taskmanager.cpp new file mode 100644 index 00000000..0a61c93b --- /dev/null +++ b/src/taskmanager.cpp @@ -0,0 +1,275 @@ +// taskmanager.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "misc.h" +#include "iman.h" +#include "event.h" +#include "object.h" +#include "task.h" +#include "taskwait.h" +#include "taskadvance.h" +#include "taskturn.h" +#include "taskgoto.h" +#include "tasktake.h" +#include "taskmanip.h" +#include "taskflag.h" +#include "taskbuild.h" +#include "tasksearch.h" +#include "taskterraform.h" +#include "taskpen.h" +#include "taskrecover.h" +#include "taskshield.h" +#include "taskinfo.h" +#include "taskfire.h" +#include "taskfireant.h" +#include "taskgungoal.h" +#include "taskspiderexplo.h" +#include "taskreset.h" +#include "taskmanager.h" + + + + +// Constructeur de l'objet. + +CTaskManager::CTaskManager(CInstanceManager* iMan, CObject* object) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_TASKMANAGER, this, 100); + + m_task = 0; + m_object = object; + m_bPilot = FALSE; +} + +// Destructeur de l'objet. + +CTaskManager::~CTaskManager() +{ + delete m_task; +} + + + +// Attend un certain temps. + +Error CTaskManager::StartTaskWait(float time) +{ + m_task = new CTaskWait(m_iMan, m_object); + return ((CTaskWait*)m_task)->Start(time); +} + +// Avance droit devant d'une certaine distance. + +Error CTaskManager::StartTaskAdvance(float length) +{ + m_task = new CTaskAdvance(m_iMan, m_object); + return ((CTaskAdvance*)m_task)->Start(length); +} + +// Tourne d'un certain angle. + +Error CTaskManager::StartTaskTurn(float angle) +{ + m_task = new CTaskTurn(m_iMan, m_object); + return ((CTaskTurn*)m_task)->Start(angle); +} + +// Atteint une position donnée. + +Error CTaskManager::StartTaskGoto(D3DVECTOR pos, float altitude, TaskGotoGoal goalMode, TaskGotoCrash crashMode) +{ + m_task = new CTaskGoto(m_iMan, m_object); + return ((CTaskGoto*)m_task)->Start(pos, altitude, goalMode, crashMode); +} + +// Bouge le bras manipulateur. + +Error CTaskManager::StartTaskTake() +{ + m_task = new CTaskTake(m_iMan, m_object); + return ((CTaskTake*)m_task)->Start(); +} + +// Bouge le bras manipulateur. + +Error CTaskManager::StartTaskManip(TaskManipOrder order, TaskManipArm arm) +{ + m_task = new CTaskManip(m_iMan, m_object); + return ((CTaskManip*)m_task)->Start(order, arm); +} + +// Met ou enlève un drapeau. + +Error CTaskManager::StartTaskFlag(TaskFlagOrder order, int rank) +{ + m_task = new CTaskFlag(m_iMan, m_object); + return ((CTaskFlag*)m_task)->Start(order, rank); +} + +// Construit un batiment. + +Error CTaskManager::StartTaskBuild(ObjectType type) +{ + m_task = new CTaskBuild(m_iMan, m_object); + return ((CTaskBuild*)m_task)->Start(type); +} + +// Sonde le sol. + +Error CTaskManager::StartTaskSearch() +{ + m_task = new CTaskSearch(m_iMan, m_object); + return ((CTaskSearch*)m_task)->Start(); +} + +// Lit une borne d'information. + +Error CTaskManager::StartTaskInfo(char *name, float value, float power, BOOL bSend) +{ + m_task = new CTaskInfo(m_iMan, m_object); + return ((CTaskInfo*)m_task)->Start(name, value, power, bSend); +} + +// Terraforme le sol. + +Error CTaskManager::StartTaskTerraform() +{ + m_task = new CTaskTerraform(m_iMan, m_object); + return ((CTaskTerraform*)m_task)->Start(); +} + +// Change de crayon. + +Error CTaskManager::StartTaskPen(BOOL bDown, int color) +{ + m_task = new CTaskPen(m_iMan, m_object); + return ((CTaskPen*)m_task)->Start(bDown, color); +} + +// Récupère une ruine. + +Error CTaskManager::StartTaskRecover() +{ + m_task = new CTaskRecover(m_iMan, m_object); + return ((CTaskRecover*)m_task)->Start(); +} + +// Déploie le bouclier. + +Error CTaskManager::StartTaskShield(TaskShieldMode mode, float delay) +{ + if ( mode == TSM_UP ) + { + m_task = new CTaskShield(m_iMan, m_object); + return ((CTaskShield*)m_task)->Start(mode, delay); + } + if ( mode == TSM_DOWN && m_task != 0 ) + { + return ((CTaskShield*)m_task)->Start(mode, delay); + } + if ( mode == TSM_UPDATE && m_task != 0 ) + { + return ((CTaskShield*)m_task)->Start(mode, delay); + } + return ERR_GENERIC; +} + +// Tire. + +Error CTaskManager::StartTaskFire(float delay) +{ + m_bPilot = TRUE; + m_task = new CTaskFire(m_iMan, m_object); + return ((CTaskFire*)m_task)->Start(delay); +} + +// Tire avec la fourmi. + +Error CTaskManager::StartTaskFireAnt(D3DVECTOR impact) +{ + m_task = new CTaskFireAnt(m_iMan, m_object); + return ((CTaskFireAnt*)m_task)->Start(impact); +} + +// Ajuste la hausse. + +Error CTaskManager::StartTaskGunGoal(float dirV, float dirH) +{ + m_task = new CTaskGunGoal(m_iMan, m_object); + return ((CTaskGunGoal*)m_task)->Start(dirV, dirH); +} + +// Suicide de l'araignée. + +Error CTaskManager::StartTaskSpiderExplo() +{ + m_task = new CTaskSpiderExplo(m_iMan, m_object); + return ((CTaskSpiderExplo*)m_task)->Start(); +} + +// Reset. + +Error CTaskManager::StartTaskReset(D3DVECTOR goal, D3DVECTOR angle) +{ + m_task = new CTaskReset(m_iMan, m_object); + return ((CTaskReset*)m_task)->Start(goal, angle); +} + + + + + +// Gestion d'un événement. + +BOOL CTaskManager::EventProcess(const Event &event) +{ + if ( m_task == 0 ) return FALSE; + return m_task->EventProcess(event); +} + + +// Indique si l'action est terminée. + +Error CTaskManager::IsEnded() +{ + if ( m_task == 0 ) return ERR_GENERIC; + return m_task->IsEnded(); +} + + +// Indique si l'action est en cours. + +BOOL CTaskManager::IsBusy() +{ + if ( m_task == 0 ) return FALSE; + return m_task->IsBusy(); +} + + +// Indique s'il est possible de piloter le robot pendant l'exécution +// de la tâche en cours. + +BOOL CTaskManager::IsPilot() +{ + return m_bPilot; +} + + +// Termine brutalement l'action en cours. + +BOOL CTaskManager::Abort() +{ + if ( m_task == 0 ) return FALSE; + return m_task->Abort(); +} + + diff --git a/src/taskmanager.h b/src/taskmanager.h new file mode 100644 index 00000000..4384f155 --- /dev/null +++ b/src/taskmanager.h @@ -0,0 +1,62 @@ +// taskmanager.h + +#ifndef _TASKMANAGER_H_ +#define _TASKMANAGER_H_ + + +class CInstanceManager; +class CTask; + +enum TaskManipOrder; +enum TaskManipArm; +enum TaskFlagOrder; +enum TaskGotoGoal; +enum TaskGotoCrash; +enum TaskShieldMode; +enum ObjectType; + + + +class CTaskManager +{ +public: + CTaskManager(CInstanceManager* iMan, CObject* object); + ~CTaskManager(); + + Error StartTaskWait(float time); + Error StartTaskAdvance(float length); + Error StartTaskTurn(float angle); + Error StartTaskGoto(D3DVECTOR pos, float altitude, TaskGotoGoal goalMode, TaskGotoCrash crashMode); + Error StartTaskTake(); + Error StartTaskManip(TaskManipOrder order, TaskManipArm arm); + Error StartTaskFlag(TaskFlagOrder order, int rank); + Error StartTaskBuild(ObjectType type); + Error StartTaskSearch(); + Error StartTaskInfo(char *name, float value, float power, BOOL bSend); + Error StartTaskTerraform(); + Error StartTaskPen(BOOL bDown, int color); + Error StartTaskRecover(); + Error StartTaskShield(TaskShieldMode mode, float delay); + Error StartTaskFire(float delay); + Error StartTaskFireAnt(D3DVECTOR impact); + Error StartTaskGunGoal(float dirV, float dirH); + Error StartTaskSpiderExplo(); + Error StartTaskReset(D3DVECTOR goal, D3DVECTOR angle); + + BOOL EventProcess(const Event &event); + Error IsEnded(); + BOOL IsBusy(); + BOOL IsPilot(); + BOOL Abort(); + +protected: + +protected: + CInstanceManager* m_iMan; + CTask* m_task; + CObject* m_object; + BOOL m_bPilot; +}; + + +#endif //_TASKMANAGER_H_ diff --git a/src/taskmanip.cpp b/src/taskmanip.cpp new file mode 100644 index 00000000..2ccc27fe --- /dev/null +++ b/src/taskmanip.cpp @@ -0,0 +1,1383 @@ +// taskmanip.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "terrain.h" +#include "object.h" +#include "pyro.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "sound.h" +#include "robotmain.h" +#include "task.h" +#include "taskmanip.h" + + +//?#define MARGIN_FRONT 2.0f +//?#define MARGIN_BACK 2.0f +//?#define MARGIN_FRIEND 2.0f +//?#define MARGIN_BEE 5.0f +#define MARGIN_FRONT 4.0f //OK 1.9 +#define MARGIN_BACK 4.0f //OK 1.9 +#define MARGIN_FRIEND 4.0f //OK 1.9 +#define MARGIN_BEE 5.0f //OK 1.9 + + + + +// Constructeur de l'objet. + +CTaskManip::CTaskManip(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); + + m_arm = TMA_NEUTRAL; + m_hand = TMH_OPEN; +} + +// Destructeur de l'objet. + +CTaskManip::~CTaskManip() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskManip::EventProcess(const Event &event) +{ + D3DVECTOR pos; + float angle, a, g, cirSpeed, progress; + int i; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + if ( m_bBee ) // abeille ? + { + return TRUE; + } + + if ( m_bTurn ) // rotation préliminaire ? + { + a = m_object->RetAngleY(0); + g = m_angle; + cirSpeed = Direction(a, g)*1.0f; + if ( m_physics->RetType() == TYPE_FLYING ) // volant au sol ? + { + cirSpeed *= 4.0f; // plus de pèche + } + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + return TRUE; + } + + if ( m_move != 0 ) // avance préliminaire ? + { + m_timeLimit -= event.rTime; + m_physics->SetMotorSpeedX(m_move); // avance/recule + return TRUE; + } + + m_progress += event.rTime*m_speed; // ça avance + progress = m_progress; + if ( progress > 1.0f ) progress = 1.0f; + + if ( m_bSubm ) // sous-marin ? + { + if ( m_order == TMO_GRAB ) + { + if ( m_step == 0 ) // descend ? + { + pos = m_object->RetPosition(1); + pos.y = 3.0f-progress*2.0f; + m_object->SetPosition(1, pos); + } + if ( m_step == 1 ) // ferme ? + { + pos = m_object->RetPosition(2); + pos.z = -1.5f+progress*0.5f; + m_object->SetPosition(2, pos); + + pos = m_object->RetPosition(3); + pos.z = 1.5f-progress*0.5f; + m_object->SetPosition(3, pos); + } + if ( m_step == 2 ) // monte ? + { + pos = m_object->RetPosition(1); + pos.y = 3.0f-(1.0f-progress)*2.0f; + m_object->SetPosition(1, pos); + } + } + else + { + if ( m_step == 0 ) // descend ? + { + pos = m_object->RetPosition(1); + pos.y = 3.0f-progress*2.0f; + m_object->SetPosition(1, pos); + } + if ( m_step == 1 ) // ferme ? + { + pos = m_object->RetPosition(2); + pos.z = -1.5f+(1.0f-progress)*0.5f; + m_object->SetPosition(2, pos); + + pos = m_object->RetPosition(3); + pos.z = 1.5f-(1.0f-progress)*0.5f; + m_object->SetPosition(3, pos); + } + if ( m_step == 2 ) // monte ? + { + pos = m_object->RetPosition(1); + pos.y = 3.0f-(1.0f-progress)*2.0f; + m_object->SetPosition(1, pos); + } + } + } + else + { + for ( i=0 ; i<5 ; i++ ) + { + angle = (m_finalAngle[i]-m_initialAngle[i])*progress; + angle += m_initialAngle[i]; + m_object->SetAngleZ(i+1, angle); + } + } + + return TRUE; +} + + +// Initialise les angles finaux et initiaux. + +void CTaskManip::InitAngle() +{ + CObject* power; + float max, energy; + int i; + + if ( m_bSubm || m_bBee ) return; + + if ( m_arm == TMA_NEUTRAL || + m_arm == TMA_GRAB ) + { + m_finalAngle[0] = ARM_NEUTRAL_ANGLE1; // bras + m_finalAngle[1] = ARM_NEUTRAL_ANGLE2; // avant-bras + m_finalAngle[2] = ARM_NEUTRAL_ANGLE3; // main + } + if ( m_arm == TMA_STOCK ) + { + m_finalAngle[0] = ARM_STOCK_ANGLE1; // bras + m_finalAngle[1] = ARM_STOCK_ANGLE2; // avant-bras + m_finalAngle[2] = ARM_STOCK_ANGLE3; // main + } + if ( m_arm == TMA_FFRONT ) + { + m_finalAngle[0] = 35.0f*PI/180.0f; // bras + m_finalAngle[1] = -95.0f*PI/180.0f; // avant-bras + m_finalAngle[2] = -27.0f*PI/180.0f; // main + } + if ( m_arm == TMA_FBACK ) + { + m_finalAngle[0] = 145.0f*PI/180.0f; // bras + m_finalAngle[1] = 95.0f*PI/180.0f; // avant-bras + m_finalAngle[2] = 27.0f*PI/180.0f; // main + } + if ( m_arm == TMA_POWER ) + { + m_finalAngle[0] = 95.0f*PI/180.0f; // bras + m_finalAngle[1] = 125.0f*PI/180.0f; // avant-bras + m_finalAngle[2] = 50.0f*PI/180.0f; // main + } + if ( m_arm == TMA_OTHER ) + { + if ( m_height <= 3.0f ) + { + m_finalAngle[0] = 55.0f*PI/180.0f; // bras + m_finalAngle[1] = -90.0f*PI/180.0f; // avant-bras + m_finalAngle[2] = -35.0f*PI/180.0f; // main + } + else + { + m_finalAngle[0] = 70.0f*PI/180.0f; // bras + m_finalAngle[1] = -90.0f*PI/180.0f; // avant-bras + m_finalAngle[2] = -50.0f*PI/180.0f; // main + } + } + + if ( m_hand == TMH_OPEN ) // pince ouverte ? + { + m_finalAngle[3] = -PI*0.10f; // pince proche + m_finalAngle[4] = PI*0.10f; // pince éloignée + } + if ( m_hand == TMH_CLOSE ) // pince fermée ? + { + m_finalAngle[3] = PI*0.05f; // pince proche + m_finalAngle[4] = -PI*0.05f; // pince éloignée + } + + for ( i=0 ; i<5 ; i++ ) + { + m_initialAngle[i] = m_object->RetAngleZ(i+1); + } + + max = 0.0f; + for ( i=0 ; i<5 ; i++ ) + { + max = Max(max, Abs(m_initialAngle[i] - m_finalAngle[i])); + } + m_speed = (PI*1.0f)/max; + if ( m_speed > 3.0f ) m_speed = 3.0f; // piano, ma non troppo + + energy = 0.0f; + power = m_object->RetPower(); + if ( power != 0 ) + { + energy = power->RetEnergy(); + } + + if ( energy == 0.0f ) + { + m_speed *= 0.7f; // plus lent si plus d'énergie ! + } +} + + +// Teste si un objet est compatible avec l'opération TMA_OTHER. + +BOOL TestFriend(ObjectType oType, ObjectType fType) +{ + if ( oType == OBJECT_ENERGY ) + { + return ( fType == OBJECT_METAL ); + } + if ( oType == OBJECT_LABO ) + { + return ( fType == OBJECT_BULLET ); + } + if ( oType == OBJECT_NUCLEAR ) + { + return ( fType == OBJECT_URANIUM ); + } + + return ( fType == OBJECT_POWER || + fType == OBJECT_ATOMIC ); +} + +// Assigne le but à atteindre. + +Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) +{ + ObjectType type; + CObject *front, *other, *power; + CPyro *pyro; + float iAngle, dist, len; + float fDist, fAngle, oDist, oAngle, oHeight; + D3DVECTOR pos, fPos, oPos; + + m_arm = arm; + m_height = 0.0f; + m_step = 0; + m_progress = 0.0f; + m_speed = 1.0f/1.5f; + + iAngle = m_object->RetAngleY(0); + iAngle = NormAngle(iAngle); // 0..2*PI + oAngle = iAngle; + + m_bError = TRUE; // opération impossible + + if ( m_arm != TMA_FFRONT && + m_arm != TMA_FBACK && + m_arm != TMA_POWER && + m_arm != TMA_GRAB ) return ERR_MANIP_VEH; + + m_physics->SetMotorSpeed(D3DVECTOR(0.0f, 0.0f, 0.0f)); + + type = m_object->RetType(); + if ( type == OBJECT_BEE ) // abeille ? + { + if ( m_object->RetFret() == 0 ) + { + if ( !m_physics->RetLand() ) return ERR_MANIP_FLY; + + other = SearchTakeUnderObject(m_targetPos, MARGIN_BEE); + if ( other == 0 ) return ERR_MANIP_NIL; + m_object->SetFret(other); // prend le boulet + other->SetTruck(m_object); + other->SetTruckPart(0); // prend avec la base + other->SetPosition(0, D3DVECTOR(0.0f, -3.0f, 0.0f)); + } + else + { + other = m_object->RetFret(); // other = boulet + m_object->SetFret(0); // lâche le boulet + other->SetTruck(0); + pos = m_object->RetPosition(0); + pos.y -= 3.0f; + other->SetPosition(0, pos); + + pos = m_object->RetPosition(0); + pos.y += 2.0f; + m_object->SetPosition(0, pos); // bond contre le haut + + pyro = new CPyro(m_iMan); + pyro->Create(PT_FALL, other); // le boulet tombe + } + + m_bBee = TRUE; + m_bError = FALSE; // ok + return ERR_OK; + } + m_bBee = FALSE; + + m_bSubm = ( type == OBJECT_MOBILEsa ); // sous-marin ? + + if ( m_arm == TMA_GRAB ) // prend immédiatement ? + { + TruckTakeObject(); + Abort(); + return ERR_OK; + } + + m_energy = 0.0f; + power = m_object->RetPower(); + if ( power != 0 ) + { + m_energy = power->RetEnergy(); + } + + if ( !m_physics->RetLand() ) return ERR_MANIP_FLY; + + if ( type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEsa ) return ERR_MANIP_VEH; + + if ( m_bSubm ) // sous-marin ? + { + m_arm = TMA_FFRONT; // possible seulement devant ! + } + + m_move = 0.0f; // avance pas nécessaire + m_angle = iAngle; + + if ( order == TMO_AUTO ) + { + if ( m_object->RetFret() == 0 ) + { + m_order = TMO_GRAB; + } + else + { + m_order = TMO_DROP; + } + } + else + { + m_order = order; + } + + if ( m_order == TMO_GRAB && m_object->RetFret() != 0 ) + { + return ERR_MANIP_BUSY; + } + if ( m_order == TMO_DROP && m_object->RetFret() == 0 ) + { + return ERR_MANIP_EMPTY; + } + +//? speed = m_physics->RetMotorSpeed(); +//? if ( speed.x != 0.0f || +//? speed.z != 0.0f ) return ERR_MANIP_MOTOR; + + if ( m_order == TMO_GRAB ) + { + if ( m_arm == TMA_FFRONT ) + { + front = SearchTakeFrontObject(TRUE, fPos, fDist, fAngle); + other = SearchOtherObject(TRUE, oPos, oDist, oAngle, oHeight); + + if ( front != 0 && fDist < oDist ) + { + m_targetPos = fPos; + m_angle = fAngle; + m_move = 1.0f; // avance nécessaire + } + else if ( other != 0 && oDist < fDist ) + { + if ( other->RetPower() == 0 ) return ERR_MANIP_NIL; + m_targetPos = oPos; + m_angle = oAngle; + m_height = oHeight; + m_move = 1.0f; // avance nécessaire + m_arm = TMA_OTHER; + } + else + { + return ERR_MANIP_NIL; + } + m_main->HideDropZone(front); // cache zone constructible + } + if ( m_arm == TMA_FBACK ) + { + if ( SearchTakeBackObject(TRUE, m_targetPos, fDist, m_angle) == 0 ) + { + return ERR_MANIP_NIL; + } + m_angle += PI; + m_move = -1.0f; // recule nécessaire + } + if ( m_arm == TMA_POWER ) + { + if ( m_object->RetPower() == 0 ) return ERR_MANIP_NIL; + } + } + + if ( m_order == TMO_DROP ) + { + if ( m_arm == TMA_FFRONT ) + { + other = SearchOtherObject(TRUE, oPos, oDist, oAngle, oHeight); + if ( other != 0 && other->RetPower() == 0 ) + { + m_targetPos = oPos; + m_angle = oAngle; + m_height = oHeight; + m_move = 1.0f; // avance nécessaire + m_arm = TMA_OTHER; + } + else + { + if ( !IsFreeDeposeObject(D3DVECTOR(TAKE_DIST, 0.0f, 0.0f)) ) return ERR_MANIP_OCC; + } + } + if ( m_arm == TMA_FBACK ) + { + if ( !IsFreeDeposeObject(D3DVECTOR(-TAKE_DIST, 0.0f, 0.0f)) ) return ERR_MANIP_OCC; + } + if ( m_arm == TMA_POWER ) + { + if ( m_object->RetPower() != 0 ) return ERR_MANIP_OCC; + } + } + + dist = Length(m_object->RetPosition(0), m_targetPos); + len = dist-TAKE_DIST; + if ( m_arm == TMA_OTHER ) len -= TAKE_DIST_OTHER; + if ( len < 0.0f ) len = 0.0f; + if ( m_arm == TMA_FBACK ) len = -len; + m_advanceLength = dist-m_physics->RetLinLength(len); + if ( dist <= m_advanceLength+0.2f ) m_move = 0.0f; // pas nécessaire d'avancer + + if ( m_energy == 0.0f ) m_move = 0.0f; + + if ( m_move != 0.0f ) // avance ou recule ? + { + m_timeLimit = m_physics->RetLinTimeLength(Abs(len))*1.5f; + if ( m_timeLimit < 0.5f ) m_timeLimit = 0.5f; + } + + if ( m_object->RetFret() == 0 ) // ne transporte rien ? + { + m_hand = TMH_OPEN; // pince ouverte + } + else + { + m_hand = TMH_CLOSE; // pince fermée + } + + InitAngle(); + + if ( iAngle == m_angle || m_energy == 0.0f ) + { + m_bTurn = FALSE; // rotation préliminaire inutile + SoundManip(1.0f/m_speed); + } + else + { + m_bTurn = TRUE; // rotation préliminaire nécessaire + } + + if ( m_bSubm ) + { + m_camera->StartCentering(m_object, PI*0.8f, 99.9f, 0.0f, 0.5f); + } + + m_physics->SetFreeze(TRUE); // on ne bouge plus + + m_bError = FALSE; // ok + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskManip::IsEnded() +{ + CObject* fret; + D3DVECTOR pos; + float angle, dist; + int i; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + + if ( m_bBee ) // abeille ? + { + return ERR_STOP; + } + + if ( m_bTurn ) // rotation préliminaire ? + { + angle = m_object->RetAngleY(0); + angle = NormAngle(angle); // 0..2*PI + + if ( TestAngle(angle, m_angle-PI*0.01f, m_angle+PI*0.01f) ) + { + m_bTurn = FALSE; // rotation terminée + m_physics->SetMotorSpeedZ(0.0f); + if ( m_move == 0.0f ) + { + SoundManip(1.0f/m_speed); + } + } + return ERR_CONTINUE; + } + + if ( m_move != 0.0f ) // avance préliminaire ? + { + if ( m_timeLimit <= 0.0f ) + { +//OK 1.9 + dist = Length(m_object->RetPosition(0), m_targetPos); + if ( dist <= m_advanceLength + 2.0f ) + { + m_move = 0.0f; // avance terminée + m_physics->SetMotorSpeedX(0.0f); + SoundManip(1.0f/m_speed); + return ERR_CONTINUE; + } + else + { +//EOK 1.9 + m_move = 0.0f; // avance terminée + m_physics->SetMotorSpeedX(0.0f); // stoppe + Abort(); + return ERR_STOP; + } + } + + dist = Length(m_object->RetPosition(0), m_targetPos); + if ( dist <= m_advanceLength ) + { + m_move = 0.0f; // avance terminée + m_physics->SetMotorSpeedX(0.0f); + SoundManip(1.0f/m_speed); + } + return ERR_CONTINUE; + } + + if ( m_progress < 1.0f ) return ERR_CONTINUE; + m_progress = 0.0f; + + if ( !m_bSubm ) + { + for ( i=0 ; i<5 ; i++ ) + { + m_object->SetAngleZ(i+1, m_finalAngle[i]); + } + } + m_step ++; + + if ( m_order == TMO_GRAB ) + { + if ( m_step == 1 ) + { + if ( m_bSubm ) m_speed = 1.0f/0.7f; + m_hand = TMH_CLOSE; // ferme la pince pour prendre + InitAngle(); + SoundManip(1.0f/m_speed, 0.8f, 1.5f); + return ERR_CONTINUE; + } + if ( m_step == 2 ) + { + if ( m_bSubm ) m_speed = 1.0f/1.5f; + if ( !TruckTakeObject() && + m_object->RetFret() == 0 ) + { + m_hand = TMH_OPEN; // réouvre la pince + m_arm = TMA_NEUTRAL; + InitAngle(); + SoundManip(1.0f/m_speed, 0.8f, 1.5f); + } + else + { + if ( (m_arm == TMA_OTHER || + m_arm == TMA_POWER ) && + (m_fretType == OBJECT_POWER || + m_fretType == OBJECT_ATOMIC ) ) + { + m_sound->Play(SOUND_POWEROFF, m_object->RetPosition(0)); + } + m_arm = TMA_STOCK; + InitAngle(); + SoundManip(1.0f/m_speed); + } + return ERR_CONTINUE; + } + } + + if ( m_order == TMO_DROP ) + { + if ( m_step == 1 ) + { + if ( m_bSubm ) m_speed = 1.0f/0.7f; + fret = m_object->RetFret(); + if ( TruckDeposeObject() ) + { + if ( (m_arm == TMA_OTHER || + m_arm == TMA_POWER ) && + (m_fretType == OBJECT_POWER || + m_fretType == OBJECT_ATOMIC ) ) + { + m_sound->Play(SOUND_POWERON, m_object->RetPosition(0)); + } + if ( fret != 0 && m_fretType == OBJECT_METAL && m_arm == TMA_FFRONT ) + { + m_main->ShowDropZone(fret, m_object); // montre zone constructible + } + m_hand = TMH_OPEN; // ouvre la pince pour déposer + SoundManip(1.0f/m_speed, 0.8f, 1.5f); + } + InitAngle(); + return ERR_CONTINUE; + } + if ( m_step == 2 ) + { + if ( m_bSubm ) m_speed = 1.0f/1.5f; + m_arm = TMA_NEUTRAL; + InitAngle(); + SoundManip(1.0f/m_speed); + return ERR_CONTINUE; + } + } + + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskManip::Abort() +{ + int i; + + if ( m_object->RetFret() == 0 ) // ne transporte rien ? + { + m_hand = TMH_OPEN; // pince ouverte + m_arm = TMA_NEUTRAL; + } + else + { + m_hand = TMH_CLOSE; // pince fermée + m_arm = TMA_STOCK; + } + InitAngle(); + + if ( !m_bSubm ) + { + for ( i=0 ; i<5 ; i++ ) + { + m_object->SetAngleZ(i+1, m_finalAngle[i]); + } + } + + m_camera->StopCentering(m_object, 2.0f); + m_physics->SetFreeze(FALSE); // on bouge de nouveau + return TRUE; +} + + +// Cherche l'objet à prendre dessous (pour l'abeille). + +CObject* CTaskManip::SearchTakeUnderObject(D3DVECTOR &pos, float dLimit) +{ + CObject *pObj, *pBest; + D3DVECTOR iPos, oPos; + ObjectType type; + float min, distance; + int i; + + iPos = m_object->RetPosition(0); + + min = 1000000.0f; + pBest = 0; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + + if ( type != OBJECT_FRET && + type != OBJECT_STONE && + type != OBJECT_URANIUM && + type != OBJECT_BULLET && + type != OBJECT_METAL && + type != OBJECT_POWER && + type != OBJECT_ATOMIC && + type != OBJECT_BBOX && + type != OBJECT_KEYa && + type != OBJECT_KEYb && + type != OBJECT_KEYc && + type != OBJECT_KEYd && + type != OBJECT_TNT ) continue; + + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + if ( pObj->RetLock() ) continue; + if ( pObj->RetZoomY(0) != 1.0f ) continue; + + oPos = pObj->RetPosition(0); + distance = Length(oPos, iPos); + if ( distance <= dLimit && + distance < min ) + { + min = distance; + pBest = pObj; + } + } + if ( pBest != 0 ) + { + pos = pBest->RetPosition(0); + } + return pBest; +} + +// Cherche l'objet à prendre devant. + +CObject* CTaskManip::SearchTakeFrontObject(BOOL bAdvance, D3DVECTOR &pos, + float &distance, float &angle) +{ + CObject *pObj, *pBest; + D3DVECTOR iPos, oPos; + ObjectType type; + float min, iAngle, bAngle, aLimit, dLimit, f; + int i; + + iPos = m_object->RetPosition(0); + iAngle = m_object->RetAngleY(0); + iAngle = NormAngle(iAngle); // 0..2*PI + + if ( bAdvance && m_energy > 0.0f ) + { + aLimit = 60.0f*PI/180.0f; + dLimit = MARGIN_FRONT+10.0f; + } + else + { +//? aLimit = 7.0f*PI/180.0f; + aLimit = 15.0f*PI/180.0f; //OK 1.9 + dLimit = MARGIN_FRONT; + } + + min = 1000000.0f; + pBest = 0; + bAngle = 0.0f; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + + if ( type != OBJECT_FRET && + type != OBJECT_STONE && + type != OBJECT_URANIUM && + type != OBJECT_BULLET && + type != OBJECT_METAL && + type != OBJECT_POWER && + type != OBJECT_ATOMIC && + type != OBJECT_BBOX && + type != OBJECT_KEYa && + type != OBJECT_KEYb && + type != OBJECT_KEYc && + type != OBJECT_KEYd && + type != OBJECT_TNT && + type != OBJECT_SCRAP1 && + type != OBJECT_SCRAP2 && + type != OBJECT_SCRAP3 && + type != OBJECT_SCRAP4 && + type != OBJECT_SCRAP5 ) continue; + + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + if ( pObj->RetLock() ) continue; + if ( pObj->RetZoomY(0) != 1.0f ) continue; + + oPos = pObj->RetPosition(0); + distance = Abs(Length(oPos, iPos)-TAKE_DIST); + f = 1.0f-distance/50.0f; + if ( f < 0.5f ) f = 0.5f; + + angle = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + if ( !TestAngle(angle, iAngle-aLimit*f, iAngle+aLimit*f) ) continue; + + if ( distance < -dLimit || + distance > dLimit ) continue; + + if ( distance < min ) + { + min = distance; + pBest = pObj; + bAngle = angle; + } + } + if ( pBest == 0 ) + { + distance = 1000000.0f; + angle = 0.0f; + } + else + { + pos = pBest->RetPosition(0); + distance = min; + angle = bAngle; + } + return pBest; +} + +// Cherche l'objet à prendre derrière. + +CObject* CTaskManip::SearchTakeBackObject(BOOL bAdvance, D3DVECTOR &pos, + float &distance, float &angle) +{ + CObject *pObj, *pBest; + D3DVECTOR iPos, oPos; + ObjectType type; + float min, iAngle, bAngle, aLimit, dLimit, f; + int i; + + iPos = m_object->RetPosition(0); + iAngle = m_object->RetAngleY(0)+PI; + iAngle = NormAngle(iAngle); // 0..2*PI + + if ( bAdvance && m_energy > 0.0f ) + { + aLimit = 60.0f*PI/180.0f; + dLimit = MARGIN_BACK+5.0f; + } + else + { + aLimit = 7.0f*PI/180.0f; + dLimit = MARGIN_BACK; + } + + min = 1000000.0f; + pBest = 0; + bAngle = 0.0f; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + + if ( type != OBJECT_FRET && + type != OBJECT_STONE && + type != OBJECT_URANIUM && + type != OBJECT_BULLET && + type != OBJECT_METAL && + type != OBJECT_POWER && + type != OBJECT_ATOMIC && + type != OBJECT_BBOX && + type != OBJECT_KEYa && + type != OBJECT_KEYb && + type != OBJECT_KEYc && + type != OBJECT_KEYd && + type != OBJECT_TNT && + type != OBJECT_SCRAP1 && + type != OBJECT_SCRAP2 && + type != OBJECT_SCRAP3 && + type != OBJECT_SCRAP4 && + type != OBJECT_SCRAP5 ) continue; + + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + if ( pObj->RetLock() ) continue; + if ( pObj->RetZoomY(0) != 1.0f ) continue; + + oPos = pObj->RetPosition(0); + distance = Abs(Length(oPos, iPos)-TAKE_DIST); + f = 1.0f-distance/50.0f; + if ( f < 0.5f ) f = 0.5f; + + angle = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + if ( !TestAngle(angle, iAngle-aLimit*f, iAngle+aLimit*f) ) continue; + + if ( distance < -dLimit || + distance > dLimit ) continue; + + if ( distance < min ) + { + min = distance; + pBest = pObj; + bAngle = angle; + } + } + if ( pBest == 0 ) + { + distance = 1000000.0f; + angle = 0.0f; + } + else + { + pos = pBest->RetPosition(0); + distance = min; + angle = bAngle; + } + return pBest; +} + +// Cherche le robot ou le bâtiment sur lequel on veut prendre ou poser +// une pile ou un autre objet. + +CObject* CTaskManip::SearchOtherObject(BOOL bAdvance, D3DVECTOR &pos, + float &distance, float &angle, + float &height) +{ + Character* character; + CObject* pObj; + CObject* pPower; + D3DMATRIX* mat; + D3DVECTOR iPos, oPos; + ObjectType type, powerType; + float iAngle, iRad, oAngle, oLimit, aLimit, dLimit; + int i; + + distance = 1000000.0f; + angle = 0.0f; + + if ( m_bSubm ) return 0; // impossible avec le sous-marin + + if ( !m_object->GetCrashSphere(0, iPos, iRad) ) return 0; + iAngle = m_object->RetAngleY(0); + iAngle = NormAngle(iAngle); // 0..2*PI + + if ( bAdvance && m_energy > 0.0f ) + { + aLimit = 60.0f*PI/180.0f; + dLimit = MARGIN_FRIEND+10.0f; + } + else + { + aLimit = 7.0f*PI/180.0f; + dLimit = MARGIN_FRIEND; + } + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; // soi-même ? + + type = pObj->RetType(); + if ( type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis && + type != OBJECT_MOBILErt && + type != OBJECT_MOBILErc && + type != OBJECT_MOBILErr && + type != OBJECT_MOBILErs && + type != OBJECT_MOBILEsa && + type != OBJECT_MOBILEtg && + type != OBJECT_MOBILEft && + type != OBJECT_MOBILEtt && + type != OBJECT_MOBILEwt && + type != OBJECT_MOBILEit && + type != OBJECT_TOWER && + type != OBJECT_RESEARCH && + type != OBJECT_ENERGY && + type != OBJECT_LABO && + type != OBJECT_NUCLEAR ) continue; + + pPower = pObj->RetPower(); + if ( pPower != 0 ) + { + if ( pPower->RetLock() ) continue; + if ( pPower->RetZoomY(0) != 1.0f ) continue; + + powerType = pPower->RetType(); + if ( powerType == OBJECT_NULL || + powerType == OBJECT_FIX ) continue; + } + + mat = pObj->RetWorldMatrix(0); + character = pObj->RetCharacter(); + oPos = Transform(*mat, character->posPower); + + oAngle = pObj->RetAngleY(0); + if ( type == OBJECT_TOWER || + type == OBJECT_RESEARCH ) + { + oLimit = 45.0f*PI/180.0f; + } + else if ( type == OBJECT_ENERGY ) + { + oLimit = 90.0f*PI/180.0f; + } + else if ( type == OBJECT_LABO ) + { + oLimit = 120.0f*PI/180.0f; + } + else if ( type == OBJECT_NUCLEAR ) + { + oLimit = 45.0f*PI/180.0f; + } + else + { + oLimit = 45.0f*PI/180.0f; + oAngle += PI; // c'est derrière + } + oAngle = NormAngle(oAngle); // 0..2*PI + angle = RotateAngle(iPos.x-oPos.x, oPos.z-iPos.z); // CW ! + if ( !TestAngle(angle, oAngle-oLimit, oAngle+oLimit) ) continue; + + distance = Abs(Length(oPos, iPos)-TAKE_DIST); + if ( distance <= dLimit ) + { + angle = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + if ( TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + { + character = pObj->RetCharacter(); + height = character->posPower.y; + pos = oPos; + return pObj; + } + } + } + + distance = 1000000.0f; + angle = 0.0f; + return 0; +} + +// Prend l'objet placé devant. + +BOOL CTaskManip::TruckTakeObject() +{ + CObject* fret; + CObject* other; + D3DMATRIX matRotate; + D3DVECTOR pos; + float angle, dist; + + if ( m_arm == TMA_GRAB ) // prend immédiatement ? + { + fret = m_object->RetFret(); + if ( fret == 0 ) return FALSE; // rien à prendre ? + m_fretType = fret->RetType(); + + if ( m_object->RetType() == OBJECT_HUMAN || + m_object->RetType() == OBJECT_TECH ) + { + fret->SetTruck(m_object); + fret->SetTruckPart(4); // prend avec la main + + fret->SetPosition(0, D3DVECTOR(1.7f, -0.5f, 1.1f)); + fret->SetAngleY(0, 0.1f); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, 0.8f); + } + else if ( m_bSubm ) + { + fret->SetTruck(m_object); + fret->SetTruckPart(2); // prend avec la pince droite + + pos = D3DVECTOR(1.1f, -1.0f, 1.0f); // relatif + fret->SetPosition(0, pos); + fret->SetAngleX(0, 0.0f); + fret->SetAngleY(0, 0.0f); + fret->SetAngleZ(0, 0.0f); + } + else + { + fret->SetTruck(m_object); + fret->SetTruckPart(3); // prend avec la main + + pos = D3DVECTOR(4.7f, 0.0f, 0.0f); // relatif à la main (lem4) + fret->SetPosition(0, pos); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, PI/2.0f); + fret->SetAngleY(0, 0.0f); + } + + m_object->SetFret(fret); // prend + } + + if ( m_arm == TMA_FFRONT ) // prend au sol devant ? + { + fret = SearchTakeFrontObject(FALSE, pos, dist, angle); + if ( fret == 0 ) return FALSE; // rien à prendre ? + m_fretType = fret->RetType(); + + if ( m_bSubm ) + { + fret->SetTruck(m_object); + fret->SetTruckPart(2); // prend avec la pince droite + + pos = D3DVECTOR(1.1f, -1.0f, 1.0f); // relatif + fret->SetPosition(0, pos); + fret->SetAngleX(0, 0.0f); + fret->SetAngleY(0, 0.0f); + fret->SetAngleZ(0, 0.0f); + } + else + { + fret->SetTruck(m_object); + fret->SetTruckPart(3); // prend avec la main + + pos = D3DVECTOR(4.7f, 0.0f, 0.0f); // relatif à la main (lem4) + fret->SetPosition(0, pos); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, PI/2.0f); + fret->SetAngleY(0, 0.0f); + } + + m_object->SetFret(fret); // prend + } + + if ( m_arm == TMA_FBACK ) // prend au sol derrière ? + { + fret = SearchTakeBackObject(FALSE, pos, dist, angle); + if ( fret == 0 ) return FALSE; // rien à prendre ? + m_fretType = fret->RetType(); + + fret->SetTruck(m_object); + fret->SetTruckPart(3); // prend avec la main + + pos = D3DVECTOR(4.7f, 0.0f, 0.0f); // relatif à la main (lem4) + fret->SetPosition(0, pos); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, PI/2.0f); + fret->SetAngleY(0, 0.0f); + + m_object->SetFret(fret); // prend + } + + if ( m_arm == TMA_POWER ) // prend pile à l'arrière ? + { + fret = m_object->RetPower(); + if ( fret == 0 ) return FALSE; // pas de pile ? + m_fretType = fret->RetType(); + + pos = D3DVECTOR(4.7f, 0.0f, 0.0f); // relatif à la main (lem4) + fret->SetPosition(0, pos); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, PI/2.0f); + fret->SetAngleY(0, 0.0f); + fret->SetTruckPart(3); // prend avec la main + + m_object->SetPower(0); + m_object->SetFret(fret); // prend + } + + if ( m_arm == TMA_OTHER ) // prend pile sur amis ? + { + other = SearchOtherObject(FALSE, pos, dist, angle, m_height); + if ( other == 0 ) return FALSE; + + fret = other->RetPower(); + if ( fret == 0 ) return FALSE; // l'autre n'a pas de pile ? + m_fretType = fret->RetType(); + + other->SetPower(0); + fret->SetTruck(m_object); + fret->SetTruckPart(3); // prend avec la main + + pos = D3DVECTOR(4.7f, 0.0f, 0.0f); // relatif à la main (lem4) + fret->SetPosition(0, pos); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, PI/2.0f); + fret->SetAngleY(0, 0.0f); + + m_object->SetFret(fret); // prend + } + + return TRUE; +} + +// Dépose l'objet pris. + +BOOL CTaskManip::TruckDeposeObject() +{ + Character* character; + CObject* fret; + CObject* other; + D3DMATRIX* mat; + D3DVECTOR pos; + float angle, dist; + + if ( m_arm == TMA_FFRONT ) // dépose au sol devant ? + { + fret = m_object->RetFret(); + if ( fret == 0 ) return FALSE; // ne porte rien ? + m_fretType = fret->RetType(); + + mat = fret->RetWorldMatrix(0); + pos = Transform(*mat, D3DVECTOR(0.0f, 1.0f, 0.0f)); + m_terrain->MoveOnFloor(pos); + fret->SetPosition(0, pos); + fret->SetAngleY(0, m_object->RetAngleY(0)+PI/2.0f); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, 0.0f); + fret->FloorAdjust(); // plaque bien au sol + + fret->SetTruck(0); + m_object->SetFret(0); // dépose + } + + if ( m_arm == TMA_FBACK ) // dépose au sol derrière ? + { + fret = m_object->RetFret(); + if ( fret == 0 ) return FALSE; // ne porte rien ? + m_fretType = fret->RetType(); + + mat = fret->RetWorldMatrix(0); + pos = Transform(*mat, D3DVECTOR(0.0f, 1.0f, 0.0f)); + m_terrain->MoveOnFloor(pos); + fret->SetPosition(0, pos); + fret->SetAngleY(0, m_object->RetAngleY(0)+PI/2.0f); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, 0.0f); + + fret->SetTruck(0); + m_object->SetFret(0); // dépose + } + + if ( m_arm == TMA_POWER ) // dépose pile à l'arrière ? + { + fret = m_object->RetFret(); + if ( fret == 0 ) return FALSE; // ne porte rien ? + m_fretType = fret->RetType(); + + if ( m_object->RetPower() != 0 ) return FALSE; + + fret->SetTruck(m_object); + fret->SetTruckPart(0); // porté par la base + + character = m_object->RetCharacter(); + fret->SetPosition(0, character->posPower); + fret->SetAngleY(0, 0.0f); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, 0.0f); + + m_object->SetPower(fret); // utilise + m_object->SetFret(0); + } + + if ( m_arm == TMA_OTHER ) // dépose pile sur amis ? + { + other = SearchOtherObject(FALSE, pos, dist, angle, m_height); + if ( other == 0 ) return FALSE; + + fret = other->RetPower(); + if ( fret != 0 ) return FALSE; // l'autre a déjà une pile ? + + fret = m_object->RetFret(); + if ( fret == 0 ) return FALSE; + m_fretType = fret->RetType(); + + other->SetPower(fret); + fret->SetTruck(other); + + character = other->RetCharacter(); + fret->SetPosition(0, character->posPower); + fret->SetAngleY(0, 0.0f); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, 0.0f); + fret->SetTruckPart(0); // porté par la base + + m_object->SetFret(0); // dépose + } + + return TRUE; +} + +// Cherche si un emplacement permet de déposer un objet. + +BOOL CTaskManip::IsFreeDeposeObject(D3DVECTOR pos) +{ + CObject* pObj; + D3DMATRIX* mat; + D3DVECTOR iPos, oPos; + float oRadius; + int i, j; + + mat = m_object->RetWorldMatrix(0); + iPos = Transform(*mat, pos); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + if ( !pObj->RetActif() ) continue; // inactif ? + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + if ( Length(iPos, oPos)-(oRadius+1.0f) < 2.0f ) + { + return FALSE; // emplacement occupé + } + } + } + return TRUE; // emplacement libre +} + +// Fait entendre le son du bras manipulateur. + +void CTaskManip::SoundManip(float time, float amplitude, float frequency) +{ + int i; + + i = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 0.0f, 0.3f*frequency, TRUE); + m_sound->AddEnvelope(i, 0.5f*amplitude, 1.0f*frequency, 0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.5f*amplitude, 1.0f*frequency, time-0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.0f, 0.3f*frequency, 0.1f, SOPER_STOP); +} + diff --git a/src/taskmanip.h b/src/taskmanip.h new file mode 100644 index 00000000..90672358 --- /dev/null +++ b/src/taskmanip.h @@ -0,0 +1,88 @@ +// taskmanip.h + +#ifndef _TASKMANIP_H_ +#define _TASKMANIP_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + + +enum TaskManipOrder +{ + TMO_AUTO = 0, // prend ou dépose automatique + TMO_GRAB = 1, // prend un objet + TMO_DROP = 2, // dépose l'objet +}; + +enum TaskManipArm +{ + TMA_NEUTRAL = 1, // bras vide au repos + TMA_STOCK = 2, // bras plein au repos + TMA_FFRONT = 3, // bras au sol + TMA_FBACK = 4, // bras derrière le robot + TMA_POWER = 5, // bras derrière le robot + TMA_OTHER = 6, // bras derrière un robot amis + TMA_GRAB = 7, // prend immédiatement +}; + +enum TaskManipHand +{ + TMH_OPEN = 1, // pince ouverte + TMH_CLOSE = 2, // pince fermée +}; + + + +class CTaskManip : public CTask +{ +public: + CTaskManip(CInstanceManager* iMan, CObject* object); + ~CTaskManip(); + + BOOL EventProcess(const Event &event); + + Error Start(TaskManipOrder order, TaskManipArm arm); + Error IsEnded(); + BOOL Abort(); + +protected: + void InitAngle(); + CObject* SearchTakeUnderObject(D3DVECTOR &pos, float dLimit); + CObject* SearchTakeFrontObject(BOOL bAdvance, D3DVECTOR &pos, float &distance, float &angle); + CObject* SearchTakeBackObject(BOOL bAdvance, D3DVECTOR &pos, float &distance, float &angle); + CObject* SearchOtherObject(BOOL bAdvance, D3DVECTOR &pos, float &distance, float &angle, float &height); + BOOL TruckTakeObject(); + BOOL TruckDeposeObject(); + BOOL IsFreeDeposeObject(D3DVECTOR pos); + void SoundManip(float time, float amplitude=1.0f, float frequency=1.0f); + +protected: + TaskManipOrder m_order; + TaskManipArm m_arm; + TaskManipHand m_hand; + int m_step; + float m_speed; + float m_progress; + float m_initialAngle[5]; + float m_finalAngle[5]; + float m_height; + float m_advanceLength; + float m_energy; + BOOL m_bError; + BOOL m_bTurn; + BOOL m_bSubm; + BOOL m_bBee; + float m_angle; + float m_move; + D3DVECTOR m_targetPos; + float m_timeLimit; + ObjectType m_fretType; +}; + + +#endif //_TASKMANIP_H_ diff --git a/src/taskpen.cpp b/src/taskpen.cpp new file mode 100644 index 00000000..49f2faf9 --- /dev/null +++ b/src/taskpen.cpp @@ -0,0 +1,288 @@ +// taskpen.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "sound.h" +#include "motion.h" +#include "motionant.h" +#include "motionspider.h" +#include "task.h" +#include "taskpen.h" + + + +// Constructeur de l'objet. + +CTaskPen::CTaskPen(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); +} + +// Destructeur de l'objet. + +CTaskPen::~CTaskPen() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskPen::EventProcess(const Event &event) +{ + D3DVECTOR pos, speed; + FPOINT dim; + int i; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + if ( m_delay == 0.0f ) + { + m_progress = 1.0f; + } + else + { + m_progress += event.rTime*(1.0f/m_delay); // ça avance + if ( m_progress > 1.0f ) m_progress = 1.0f; + } + + m_time += event.rTime; + + if ( m_phase == TPP_UP ) // remonte le crayon ? + { + i = AngleToRank(m_object->RetAngleY(1)); + pos = m_object->RetPosition(10+i); + pos.y = -3.2f*(1.0f-m_progress); + m_object->SetPosition(10+i, pos); + } + + if ( m_phase == TPP_TURN ) // tourne le carrousel ? + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_supportPos; + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + speed.x = (Rand()-0.5f)*3.0f; + speed.z = (Rand()-0.5f)*3.0f; + speed.y = Rand()*2.0f; + dim.x = Rand()*1.5f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE3, 4.0f); + } + + m_object->SetAngleY(1, m_oldAngle+(m_newAngle-m_oldAngle)*m_progress); + } + + if ( m_phase == TPP_DOWN ) // descend le crayon ? + { + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_supportPos; + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + speed.x = (Rand()-0.5f)*3.0f; + speed.z = (Rand()-0.5f)*3.0f; + speed.y = Rand()*5.0f; + dim.x = Rand()*1.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIVAPOR, 4.0f); + } + + i = AngleToRank(m_object->RetAngleY(1)); + pos = m_object->RetPosition(10+i); + if ( m_timeDown == 0.0f ) + { + pos.y = 0.0f; + } + else + { + pos.y = -3.2f*Bounce(Min(m_progress*1.8f, 1.0f)); + } + m_object->SetPosition(10+i, pos); + } + + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskPen::Start(BOOL bDown, int color) +{ + D3DVECTOR pos; + D3DMATRIX* mat; + ObjectType type; + int i; + + m_bError = TRUE; // opération impossible + + type = m_object->RetType(); + if ( type != OBJECT_MOBILEdr ) return ERR_FIRE_VEH; + + m_bError = FALSE; // ok + + m_oldAngle = m_object->RetAngleY(1); + m_newAngle = ColorToAngle(color); + + i = AngleToRank(m_oldAngle); + pos = m_object->RetPosition(10+i); + + if ( pos.y == 0.0f ) // crayon en haut ? + { + m_timeUp = 0.0f; + } + else // crayon en bas ? + { + m_timeUp = 1.0f; // faut le remonter + } + + if ( bDown ) // faudra-t-il le descendre en dernier ? + { + m_timeDown = 0.7f; + } + else + { + m_timeDown = 0.0f; + } + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(-3.0f, 7.0f, 0.0f); + pos = Transform(*mat, pos); // position carrousel + m_supportPos = pos; + + m_phase = TPP_UP; + m_progress = 0.0f; + m_delay = m_timeUp; + m_time = 0.0f; + + if ( m_timeUp > 0.0f ) + { + SoundManip(m_timeUp, 1.0f, 0.5f); + } + + m_lastParticule = 0.0f; + +//? m_camera->StartCentering(m_object, PI*0.60f, 99.9f, 5.0f, 0.5f); + + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskPen::IsEnded() +{ + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + + if ( m_progress < 1.0f ) return ERR_CONTINUE; + m_progress = 0.0f; + + if ( m_phase == TPP_UP ) + { + m_phase = TPP_TURN; + m_progress = 0.0f; + m_delay = Abs(m_oldAngle-m_newAngle)/PI; + m_time = 0.0f; + m_lastParticule = 0.0f; + if ( m_delay > 0.0f ) + { + SoundManip(m_delay, 1.0f, 1.0f); + } + return ERR_CONTINUE; + } + + if ( m_phase == TPP_TURN ) + { + m_sound->Play(SOUND_PSHHH2, m_supportPos, 1.0f, 1.4f); + m_phase = TPP_DOWN; + m_progress = 0.0f; + m_delay = m_timeDown; + m_time = 0.0f; + m_lastParticule = 0.0f; + return ERR_CONTINUE; + } + + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskPen::Abort() +{ +//? m_camera->StopCentering(m_object, 0.5f); + return TRUE; +} + + +// Fait entendre le son du bras manipulateur. + +void CTaskPen::SoundManip(float time, float amplitude, float frequency) +{ + int i; + + i = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 0.0f, 0.3f*frequency, TRUE); + m_sound->AddEnvelope(i, 0.5f*amplitude, 1.0f*frequency, 0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.5f*amplitude, 1.0f*frequency, time-0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.0f, 0.3f*frequency, 0.1f, SOPER_STOP); +} + + +// Conversion d'un angle en numéro de crayon. + +int CTaskPen::AngleToRank(float angle) +{ +//? return (int)(angle/(-45.0f*PI/180.0f)); + angle = -angle; + angle += (45.0f*PI/180.0f)/2.0f; + return (int)(angle/(45.0f*PI/180.0f)); +} + +// Conversion d'une couleur en angle pour le carrousel de crayons. + +float CTaskPen::ColorToAngle(int color) +{ + return -45.0f*PI/180.0f*ColorToRank(color); +} + +// Conversion d'une couleur en numéro de crayon (0..7). + +int CTaskPen::ColorToRank(int color) +{ + if ( color == 8 ) return 1; // yellow + if ( color == 7 ) return 2; // orange + if ( color == 5 ) return 2; // pink + if ( color == 4 ) return 3; // red + if ( color == 6 ) return 4; // purple + if ( color == 14 ) return 5; // blue + if ( color == 15 ) return 5; // light blue + if ( color == 12 ) return 6; // green + if ( color == 13 ) return 6; // light green + if ( color == 10 ) return 7; // brown + if ( color == 9 ) return 7; // beige + return 0; // black +} + diff --git a/src/taskpen.h b/src/taskpen.h new file mode 100644 index 00000000..35740a6a --- /dev/null +++ b/src/taskpen.h @@ -0,0 +1,58 @@ +// taskpen.h + +#ifndef _TASKSPEN_H_ +#define _TASKSPEN_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + + +enum TaskPenPhase +{ + TPP_UP = 1, // monte le crayon + TPP_TURN = 2, // tourne le carrousel + TPP_DOWN = 3, // descend le crayon +}; + + + +class CTaskPen : public CTask +{ +public: + CTaskPen(CInstanceManager* iMan, CObject* object); + ~CTaskPen(); + + BOOL EventProcess(const Event &event); + + Error Start(BOOL bDown, int color); + Error IsEnded(); + BOOL Abort(); + +protected: + void SoundManip(float time, float amplitude, float frequency); + int AngleToRank(float angle); + float ColorToAngle(int color); + int ColorToRank(int color); + +protected: + BOOL m_bError; + TaskPenPhase m_phase; + float m_progress; + float m_delay; + float m_time; + float m_lastParticule; + D3DVECTOR m_supportPos; + + float m_timeUp; + float m_oldAngle; + float m_newAngle; + float m_timeDown; +}; + + +#endif //_TASKSPEN_H_ diff --git a/src/taskrecover.cpp b/src/taskrecover.cpp new file mode 100644 index 00000000..ce066690 --- /dev/null +++ b/src/taskrecover.cpp @@ -0,0 +1,415 @@ +// taskrecover.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "sound.h" +#include "displaytext.h" +#include "task.h" +#include "taskrecover.h" + + +#define ENERGY_RECOVER 0.25f // énergie consommée par récupération +#define RECOVER_DIST 11.8f + + + +// Constructeur de l'objet. + +CTaskRecover::CTaskRecover(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); + + m_ruin = 0; + m_soundChannel = -1; +} + +// Destructeur de l'objet. + +CTaskRecover::~CTaskRecover() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskRecover::EventProcess(const Event &event) +{ + CObject* power; + D3DVECTOR pos, speed; + FPOINT dim; + float a, g, cirSpeed, angle, energy, dist, linSpeed; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + if ( m_phase == TRP_TURN ) // rotation préliminaire ? + { + a = m_object->RetAngleY(0); + g = m_angle; + cirSpeed = Direction(a, g)*1.0f; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + return TRUE; + } + + m_progress += event.rTime*m_speed; // ça avance + m_time += event.rTime; + + if ( m_phase == TRP_DOWN ) + { + angle = Prop(126, -10, m_progress); + m_object->SetAngleZ(2, angle); + m_object->SetAngleZ(4, angle); + + angle = Prop(-144, 0, m_progress); + m_object->SetAngleZ(3, angle); + m_object->SetAngleZ(5, angle); + } + + if ( m_phase == TRP_MOVE ) // avance/recule préliminaire ? + { + dist = Length(m_object->RetPosition(0), m_ruin->RetPosition(0)); + linSpeed = 0.0f; + if ( dist > RECOVER_DIST ) linSpeed = 1.0f; + if ( dist < RECOVER_DIST ) linSpeed = -1.0f; + m_physics->SetMotorSpeedX(linSpeed); // avance/recule + return TRUE; + } + + if ( m_phase == TRP_OPER ) + { + power = m_object->RetPower(); + if ( power != 0 ) + { + energy = power->RetEnergy(); + power->SetEnergy(energy-ENERGY_RECOVER*event.rTime*m_speed); + } + + speed.x = (Rand()-0.5f)*0.1f*m_progress; + speed.y = (Rand()-0.5f)*0.1f*m_progress; + speed.z = (Rand()-0.5f)*0.1f*m_progress; + m_ruin->SetCirVibration(speed); + + if ( m_progress >= 0.75f ) + { + m_ruin->SetZoom(0, 1.0f-(m_progress-0.75f)/0.25f); + } + + if ( m_progress > 0.5f && m_progress < 0.8f ) + { + m_metal->SetZoom(0, (m_progress-0.5f)/0.3f); + } + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.02f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_recoverPos; + pos.x += (Rand()-0.5f)*8.0f*(1.0f-m_progress); + pos.z += (Rand()-0.5f)*8.0f*(1.0f-m_progress); + pos.y -= 4.0f; + speed.x = (Rand()-0.5f)*0.0f; + speed.z = (Rand()-0.5f)*0.0f; + speed.y = Rand()*15.0f; + dim.x = Rand()*2.0f+1.5f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIRECOVER, 1.0f, 0.0f, 0.0f); + } + } + + if ( m_phase == TRP_UP ) + { + angle = Prop(-10, 126, m_progress); + m_object->SetAngleZ(2, angle); + m_object->SetAngleZ(4, angle); + + angle = Prop(0, -144, m_progress); + m_object->SetAngleZ(3, angle); + m_object->SetAngleZ(5, angle); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.02f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_recoverPos; + pos.y -= 4.0f; + speed.x = (Rand()-0.5f)*0.0f; + speed.z = (Rand()-0.5f)*0.0f; + speed.y = Rand()*15.0f; + dim.x = Rand()*2.0f+1.5f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIRECOVER, 1.0f, 0.0f, 0.0f); + } + } + + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskRecover::Start() +{ + CObject* power; + D3DMATRIX* mat; + D3DVECTOR pos, iPos, oPos; + float energy; + + ObjectType type; + + m_bError = TRUE; // opération impossible + if ( !m_physics->RetLand() ) return ERR_RECOVER_VEH; + + type = m_object->RetType(); + if ( type != OBJECT_MOBILErr ) return ERR_RECOVER_VEH; + + power = m_object->RetPower(); + if ( power == 0 ) return ERR_RECOVER_ENERGY; + energy = power->RetEnergy(); + if ( energy < ENERGY_RECOVER/power->RetCapacity()+0.05f ) return ERR_RECOVER_ENERGY; + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(RECOVER_DIST, 3.3f, 0.0f); + pos = Transform(*mat, pos); // position devant + m_recoverPos = pos; + + m_ruin = SearchRuin(); + if ( m_ruin == 0 ) return ERR_RECOVER_NULL; + m_ruin->SetLock(TRUE); // ruine plus utilisable + + iPos = m_object->RetPosition(0); + oPos = m_ruin->RetPosition(0); + m_angle = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + + m_metal = 0; + + m_phase = TRP_TURN; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + m_time = 0.0f; + m_lastParticule = 0.0f; + + m_bError = FALSE; // ok + + m_camera->StartCentering(m_object, PI*0.85f, 99.9f, 10.0f, 3.0f); + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskRecover::IsEnded() +{ + D3DMATRIX* mat; + D3DVECTOR pos, speed, goal; + FPOINT dim; + float angle, dist, time; + int i; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + + if ( m_phase == TRP_TURN ) // rotation préliminaire ? + { + angle = m_object->RetAngleY(0); + angle = NormAngle(angle); // 0..2*PI + + if ( TestAngle(angle, m_angle-PI*0.01f, m_angle+PI*0.01f) ) + { + m_physics->SetMotorSpeedZ(0.0f); + + dist = Length(m_object->RetPosition(0), m_ruin->RetPosition(0)); + if ( dist > RECOVER_DIST ) + { + time = m_physics->RetLinTimeLength(dist-RECOVER_DIST, 1.0f); + m_speed = 1.0f/time; + } + else + { + time = m_physics->RetLinTimeLength(RECOVER_DIST-dist, -1.0f); + m_speed = 1.0f/time; + } + m_phase = TRP_MOVE; + m_progress = 0.0f; + } + return ERR_CONTINUE; + } + + if ( m_phase == TRP_MOVE ) // avance préliminaire ? + { + dist = Length(m_object->RetPosition(0), m_ruin->RetPosition(0)); + + if ( dist >= RECOVER_DIST-1.0f && + dist <= RECOVER_DIST+1.0f ) + { + m_physics->SetMotorSpeedX(0.0f); + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(RECOVER_DIST, 3.3f, 0.0f); + pos = Transform(*mat, pos); // position devant + m_recoverPos = pos; + + i = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 0.0f, 0.9f, TRUE); + m_sound->AddEnvelope(i, 1.0f, 1.5f, 0.3f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 1.0f, 1.5f, 1.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.0f, 0.9f, 0.3f, SOPER_STOP); + + m_phase = TRP_DOWN; + m_progress = 0.0f; + m_speed = 1.0f/1.5f; + m_time = 0.0f; + } + else + { + if ( m_progress > 1.0f ) // timeout ? + { + m_ruin->SetLock(FALSE); // de nouveau utilisable + m_camera->StopCentering(m_object, 2.0f); + return ERR_RECOVER_NULL; + } + } + return ERR_CONTINUE; + } + + if ( m_progress < 1.0f ) return ERR_CONTINUE; + m_progress = 0.0f; + + if ( m_phase == TRP_DOWN ) + { + m_metal = new CObject(m_iMan); + if ( !m_metal->CreateResource(m_recoverPos, 0.0f, OBJECT_METAL) ) + { + delete m_metal; + m_metal = 0; + Abort(); + m_bError = TRUE; + m_displayText->DisplayError(ERR_TOOMANY, m_object); + return ERR_STOP; + } + m_metal->SetLock(TRUE); // métal pas encore utilisable + m_metal->SetZoom(0, 0.0f); + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(RECOVER_DIST, 3.1f, 3.9f); + pos = Transform(*mat, pos); + goal = D3DVECTOR(RECOVER_DIST, 3.1f, -3.9f); + goal = Transform(*mat, goal); + m_particule->CreateRay(pos, goal, PARTIRAY2, + FPOINT(2.0f, 2.0f), 8.0f); + + m_soundChannel = m_sound->Play(SOUND_RECOVER, m_ruin->RetPosition(0), 0.0f, 1.0f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 0.6f, 1.0f, 2.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.6f, 1.0f, 4.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.7f, 2.0f, SOPER_STOP); + + m_phase = TRP_OPER; + m_speed = 1.0f/8.0f; + return ERR_CONTINUE; + } + + if ( m_phase == TRP_OPER ) + { + m_metal->SetZoom(0, 1.0f); + + m_ruin->DeleteObject(); // détruit la ruine + delete m_ruin; + m_ruin = 0; + + m_soundChannel = -1; + + i = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 0.0f, 0.9f, TRUE); + m_sound->AddEnvelope(i, 1.0f, 1.5f, 0.3f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 1.0f, 1.5f, 1.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.0f, 0.9f, 0.3f, SOPER_STOP); + + m_phase = TRP_UP; + m_speed = 1.0f/1.5f; + return ERR_CONTINUE; + } + + m_metal->SetLock(FALSE); // métal utilisable + + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskRecover::Abort() +{ + m_object->SetAngleZ(2, 126.0f*PI/180.0f); + m_object->SetAngleZ(4, 126.0f*PI/180.0f); + m_object->SetAngleZ(3, -144.0f*PI/180.0f); + m_object->SetAngleZ(5, -144.0f*PI/180.0f); // repos + + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, 1.0f, SOPER_STOP); + m_soundChannel = -1; + } + + m_camera->StopCentering(m_object, 2.0f); + return TRUE; +} + + +// Cherche si un ruine est devant le véhicule. + +CObject* CTaskRecover::SearchRuin() +{ + CObject *pObj, *pBest; + D3DVECTOR oPos; + ObjectType type; + float dist, min; + int i; + + pBest = 0; + min = 100000.0f; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type == OBJECT_RUINmobilew1 || + type == OBJECT_RUINmobilew2 || + type == OBJECT_RUINmobilet1 || + type == OBJECT_RUINmobilet2 || + type == OBJECT_RUINmobiler1 || + type == OBJECT_RUINmobiler2 ) // véhicule en ruine ? + { + oPos = pObj->RetPosition(0); + dist = Length(oPos, m_recoverPos); + if ( dist > 40.0f ) continue; + + if ( dist < min ) + { + min = dist; + pBest = pObj; + } + } + + } + return pBest; +} + diff --git a/src/taskrecover.h b/src/taskrecover.h new file mode 100644 index 00000000..bc14d00b --- /dev/null +++ b/src/taskrecover.h @@ -0,0 +1,56 @@ +// taskrecover.h + +#ifndef _TASKSRECOVER_H_ +#define _TASKSRECOVER_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + + +enum TaskRecoverPhase +{ + TRP_TURN = 1, // tourne + TRP_MOVE = 2, // avance + TRP_DOWN = 3, // descend + TRP_OPER = 4, // opère + TRP_UP = 5, // remonte +}; + + + +class CTaskRecover : public CTask +{ +public: + CTaskRecover(CInstanceManager* iMan, CObject* object); + ~CTaskRecover(); + + BOOL EventProcess(const Event &event); + + Error Start(); + Error IsEnded(); + BOOL Abort(); + +protected: + CObject* SearchRuin(); + +protected: + TaskRecoverPhase m_phase; + float m_progress; + float m_speed; + float m_time; + float m_angle; + float m_lastParticule; + BOOL m_bError; + CObject* m_ruin; + CObject* m_metal; + D3DVECTOR m_recoverPos; + int m_soundChannel; +}; + + +#endif //_TASKSRECOVER_H_ diff --git a/src/taskreset.cpp b/src/taskreset.cpp new file mode 100644 index 00000000..0d4aa284 --- /dev/null +++ b/src/taskreset.cpp @@ -0,0 +1,329 @@ +// taskreset.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "sound.h" +#include "robotmain.h" +#include "task.h" +#include "taskreset.h" + + + +#define RESET_DELAY_ZOOM 0.7f +#define RESET_DELAY_MOVE 0.7f + + + + +// Constructeur de l'objet. + +CTaskReset::CTaskReset(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); +} + +// Destructeur de l'objet. + +CTaskReset::~CTaskReset() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskReset::EventProcess(const Event &event) +{ + D3DVECTOR pos, speed; + FPOINT dim; + float angle, duration; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + m_time += event.rTime; + m_progress += event.rTime*m_speed; + + if ( m_phase == TRSP_ZOUT ) + { + angle = m_iAngle; + angle += powf(m_progress*5.0f, 2.0f); // accélère + m_object->SetAngleY(0, angle); + m_object->SetZoom(0, 1.0f-m_progress); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_begin; + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 5.0f+Rand()*5.0f; + dim.x = Rand()*2.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINTb, 2.0f); + + pos = m_begin; + speed.x = (Rand()-0.5f)*20.0f; + speed.z = (Rand()-0.5f)*20.0f; + speed.y = Rand()*10.0f; + speed *= 1.0f-m_progress*0.5f; + pos += speed*1.5f; + speed = -speed; + dim.x = 0.6f; + dim.y = dim.x; + pos.y += dim.y; + duration = Rand()*1.5f+1.5f; + m_particule->CreateTrack(pos, speed, dim, PARTITRACK6, + duration, 0.0f, + duration*0.9f, 0.7f); + } + } + + if ( m_phase == TRSP_MOVE ) + { + pos = m_begin+(m_goal-m_begin)*m_progress; + m_object->SetPosition(0, pos); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 2.0f+Rand()*2.0f; + dim.x = Rand()*2.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINTb, 2.0f); + } + } + + if ( m_phase == TRSP_ZIN ) + { + angle = m_angle.y; + angle += -powf((1.0f-m_progress)*5.0f, 2.0f); // freine + m_object->SetAngleY(0, angle); + m_object->SetZoom(0, m_progress); + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_goal; + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + speed.x = 0.0f; + speed.z = 0.0f; + speed.y = 5.0f+Rand()*5.0f; + dim.x = Rand()*2.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGLINTb, 2.0f); + + pos = m_goal; + speed.x = (Rand()-0.5f)*20.0f; + speed.z = (Rand()-0.5f)*20.0f; + speed.y = Rand()*10.0f; + speed *= 0.5f+m_progress*0.5f; + dim.x = 0.6f; + dim.y = dim.x; + pos.y += dim.y; + duration = Rand()*1.5f+1.5f; + m_particule->CreateTrack(pos, speed, dim, PARTITRACK6, + duration, 0.0f, + duration*0.9f, 0.7f); + } + } + + return TRUE; +} + + +// Assigne le but à atteindre. +// Un angle positif effectue un virage à droite. + +Error CTaskReset::Start(D3DVECTOR goal, D3DVECTOR angle) +{ + CObject* fret; + int i; + + fret = m_object->RetFret(); + if ( fret != 0 && fret->RetResetCap() == RESET_MOVE ) + { + fret->SetTruck(0); + m_object->SetFret(0); // ne porte plus rien + } + + if ( !m_main->RetNiceReset() ) // retour rapide ? + { + m_object->SetPosition(0, goal); + m_object->SetAngle(0, angle); + m_brain->RunProgram(m_object->RetResetRun()); + + m_bError = FALSE; + return ERR_OK; + } + + m_begin = m_object->RetPosition(0); + m_goal = goal; + m_angle = angle; + + if ( SearchVehicle() ) // emplacement de départ occupé ? + { + m_bError = TRUE; + return ERR_RESET_NEAR; + } + + m_iAngle = m_object->RetAngleY(0); + m_time = 0.0f; + m_phase = TRSP_ZOUT; + m_speed = 1.0f/RESET_DELAY_ZOOM; + m_progress = 0.0f; + m_lastParticule = 0.0f; + + m_object->SetResetBusy(TRUE); + + i = m_sound->Play(SOUND_GGG, m_begin, 1.0f, 2.0f, TRUE); + m_sound->AddEnvelope(i, 0.0f, 0.5f, RESET_DELAY_ZOOM, SOPER_STOP); + + m_bError = FALSE; + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskReset::IsEnded() +{ + CObject* power; + float dist; + int i; + + if ( !m_main->RetNiceReset() ) // retour rapide ? + { + return ERR_STOP; + } + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + if ( m_progress < 1.0f ) return ERR_CONTINUE; + + if ( m_phase == TRSP_ZOUT ) + { + dist = Length(m_begin, m_goal); + m_phase = TRSP_MOVE; + m_speed = 1.0f/(dist*RESET_DELAY_MOVE/100.0f); + m_progress = 0.0f; + return ERR_CONTINUE; + } + + if ( m_phase == TRSP_MOVE ) + { + m_object->SetPosition(0, m_goal); + m_object->SetAngle(0, m_angle); + + i = m_sound->Play(SOUND_GGG, m_goal, 1.0f, 0.5f, TRUE); + m_sound->AddEnvelope(i, 0.0f, 2.0f, RESET_DELAY_ZOOM, SOPER_STOP); + + m_phase = TRSP_ZIN; + m_speed = 1.0f/RESET_DELAY_ZOOM; + m_progress = 0.0f; + return ERR_CONTINUE; + } + + m_object->SetAngle(0, m_angle); + m_object->SetZoom(0, 1.0f); + + power = m_object->RetPower(); + if ( power != 0 ) + { + power->SetEnergy(power->RetCapacity()); // refait le plein + } + + m_brain->RunProgram(m_object->RetResetRun()); + m_object->SetResetBusy(FALSE); + return ERR_STOP; +} + + +// Cherche si un véhicule est trop proche. + +BOOL CTaskReset::SearchVehicle() +{ + CObject* pObj; + D3DVECTOR oPos; + ObjectType type; + float oRadius, dist; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + + type = pObj->RetType(); + if ( type != OBJECT_HUMAN && + type != OBJECT_TECH && + type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis && + type != OBJECT_MOBILErt && + type != OBJECT_MOBILErc && + type != OBJECT_MOBILErr && + type != OBJECT_MOBILErs && + type != OBJECT_MOBILEsa && + type != OBJECT_MOBILEtg && + type != OBJECT_MOBILEft && + type != OBJECT_MOBILEtt && + type != OBJECT_MOBILEwt && + type != OBJECT_MOBILEit && + type != OBJECT_MOBILEdr && + type != OBJECT_MOTHER && + type != OBJECT_ANT && + type != OBJECT_SPIDER && + type != OBJECT_BEE && + type != OBJECT_WORM ) continue; + + if ( !pObj->GetCrashSphere(0, oPos, oRadius) ) continue; + dist = Length(oPos, m_goal)-oRadius; + + if ( dist < 5.0f ) return TRUE; + } + + return FALSE; +} + diff --git a/src/taskreset.h b/src/taskreset.h new file mode 100644 index 00000000..fa80a404 --- /dev/null +++ b/src/taskreset.h @@ -0,0 +1,53 @@ +// taskreset.h + +#ifndef _TASKRESET_H_ +#define _TASKRESET_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + + +enum TaskResetPhase +{ + TRSP_ZOUT = 1, // disparaît + TRSP_MOVE = 2, // déplace + TRSP_ZIN = 3, // réapparaît +}; + + + +class CTaskReset : public CTask +{ +public: + CTaskReset(CInstanceManager* iMan, CObject* object); + ~CTaskReset(); + + BOOL EventProcess(const Event &event); + + Error Start(D3DVECTOR goal, D3DVECTOR angle); + Error IsEnded(); + +protected: + BOOL SearchVehicle(); + +protected: + D3DVECTOR m_begin; + D3DVECTOR m_goal; + D3DVECTOR m_angle; + + TaskResetPhase m_phase; + BOOL m_bError; + float m_time; + float m_speed; + float m_progress; + float m_lastParticule; // temps génération dernière particule + float m_iAngle; +}; + + +#endif //_TASKRESET_H_ diff --git a/src/tasksearch.cpp b/src/tasksearch.cpp new file mode 100644 index 00000000..f4b65b52 --- /dev/null +++ b/src/tasksearch.cpp @@ -0,0 +1,318 @@ +// tasksearch.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "sound.h" +#include "displaytext.h" +#include "task.h" +#include "tasksearch.h" + + + + +// Constructeur de l'objet. + +CTaskSearch::CTaskSearch(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); + + m_hand = TSH_UP; +} + +// Destructeur de l'objet. + +CTaskSearch::~CTaskSearch() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskSearch::EventProcess(const Event &event) +{ + D3DMATRIX* mat; + D3DVECTOR pos, speed; + FPOINT dim; + float angle; + int i; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + m_progress += event.rTime*m_speed; // ça avance + m_time += event.rTime; + + if ( m_phase == TSP_DOWN || + m_phase == TSP_UP ) + { + for ( i=0 ; i<3 ; i++ ) + { + angle = (m_finalAngle[i]-m_initialAngle[i])*m_progress; + angle += m_initialAngle[i]; + m_object->SetAngleZ(i+1, angle); + } + } + + if ( m_phase == TSP_SEARCH && + m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(6.5f, 0.2f, 0.0f); + pos = Transform(*mat, pos); // position capteur + + speed.x = (Rand()-0.5f)*20.0f; + speed.z = (Rand()-0.5f)*20.0f; + speed.y = 0.0f; + dim.x = Rand()*1.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIGAS); + } + + return TRUE; +} + + +// Initialise les angles finaux et initiaux. + +void CTaskSearch::InitAngle() +{ + int i; + + if ( m_hand == TSH_UP ) + { + m_finalAngle[0] = 110.0f*PI/180.0f; // bras + m_finalAngle[1] = -110.0f*PI/180.0f; // avant-bras + m_finalAngle[2] = -65.0f*PI/180.0f; // capteur + } + if ( m_hand == TSH_DOWN ) + { + m_finalAngle[0] = 25.0f*PI/180.0f; // bras + m_finalAngle[1] = -70.0f*PI/180.0f; // avant-bras + m_finalAngle[2] = -45.0f*PI/180.0f; // capteur + } + + for ( i=0 ; i<3 ; i++ ) + { + m_initialAngle[i] = m_object->RetAngleZ(i+1); + } +} + + +// Assigne le but à atteindre. + +Error CTaskSearch::Start() +{ + ObjectType type; + D3DVECTOR speed; + int i; + + m_bError = TRUE; + if ( !m_physics->RetLand() ) return ERR_SEARCH_FLY; + + speed = m_physics->RetMotorSpeed(); + if ( speed.x != 0.0f || + speed.z != 0.0f ) return ERR_SEARCH_MOTOR; + + type = m_object->RetType(); + if ( type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis ) return ERR_SEARCH_VEH; + + m_hand = TSH_DOWN; + m_phase = TSP_DOWN; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + m_time = 0.0f; + m_lastParticule = 0.0f; + + InitAngle(); + m_bError = FALSE; // ok + + m_camera->StartCentering(m_object, PI*0.50f, 99.9f, 0.0f, 1.0f); + + i = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 0.0f, 0.3f, TRUE); + m_sound->AddEnvelope(i, 0.5f, 1.0f, 0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.5f, 1.0f, 0.9f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.0f, 0.3f, 0.1f, SOPER_STOP); + + m_physics->SetFreeze(TRUE); // on ne bouge plus + + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskSearch::IsEnded() +{ + int i; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + + if ( m_progress < 1.0f ) return ERR_CONTINUE; + m_progress = 0.0f; + + if ( m_phase == TSP_DOWN || + m_phase == TSP_UP ) + { + for ( i=0 ; i<3 ; i++ ) + { + m_object->SetAngleZ(i+1, m_finalAngle[i]); + } + } + + if ( m_phase == TSP_DOWN ) + { + m_sound->Play(SOUND_REPAIR, m_object->RetPosition(0)); + + m_phase = TSP_SEARCH; + m_speed = 1.0f/4.0f; + return ERR_CONTINUE; + } + + if ( m_phase == TSP_SEARCH ) + { + CreateMark(); + + m_hand = TSH_UP; + InitAngle(); + + i = m_sound->Play(SOUND_MANIP, m_object->RetPosition(0), 0.0f, 0.3f, TRUE); + m_sound->AddEnvelope(i, 0.5f, 1.0f, 0.1f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.5f, 1.0f, 0.9f, SOPER_CONTINUE); + m_sound->AddEnvelope(i, 0.0f, 0.3f, 0.1f, SOPER_STOP); + + m_phase = TSP_UP; + m_speed = 1.0f/1.0f; + return ERR_CONTINUE; + } + + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskSearch::Abort() +{ + m_camera->StopCentering(m_object, 2.0f); + m_physics->SetFreeze(FALSE); // on bouge de nouveau + return TRUE; +} + + +// Crée une marque si c'est possible. + +BOOL CTaskSearch::CreateMark() +{ + CObject* fret; + ObjectType type; + D3DMATRIX* mat; + D3DVECTOR pos; + TerrainRes res; + Error info; + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(7.5f, 0.0f, 0.0f); + pos = Transform(*mat, pos); // position capteur + + res = m_terrain->RetResource(pos); + if ( res == TR_NULL ) return FALSE; + + type = OBJECT_NULL; + if ( res == TR_STONE ) + { + type = OBJECT_MARKSTONE; + info = INFO_MARKSTONE; + } + if ( res == TR_URANIUM ) + { + type = OBJECT_MARKURANIUM; + info = INFO_MARKURANIUM; + } + if ( res == TR_POWER ) + { + type = OBJECT_MARKPOWER; + info = INFO_MARKPOWER; + } + if ( res == TR_KEYa ) + { + type = OBJECT_MARKKEYa; + info = INFO_MARKKEYa; + } + if ( res == TR_KEYb ) + { + type = OBJECT_MARKKEYb; + info = INFO_MARKKEYb; + } + if ( res == TR_KEYc ) + { + type = OBJECT_MARKKEYc; + info = INFO_MARKKEYc; + } + if ( res == TR_KEYd ) + { + type = OBJECT_MARKKEYd; + info = INFO_MARKKEYd; + } + if ( type == OBJECT_NULL ) return FALSE; + +//? DeleteMark(type); + + fret = new CObject(m_iMan); + if ( !fret->CreateResource(pos, 0.0f, type) ) + { + delete fret; + m_displayText->DisplayError(ERR_TOOMANY, m_object); + return FALSE; + } + + m_displayText->DisplayError(info, pos, 5.0f, 50.0f); // affiche le message + + return TRUE; +} + +// Détruit les marques d'un type donné.. + +void CTaskSearch::DeleteMark(ObjectType type) +{ + CObject* pObj; + D3DVECTOR oPos; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( type == pObj->RetType() ) + { + pObj->DeleteObject(); // supprime la marque + delete pObj; + break; + } + } +} + + diff --git a/src/tasksearch.h b/src/tasksearch.h new file mode 100644 index 00000000..3d6cca54 --- /dev/null +++ b/src/tasksearch.h @@ -0,0 +1,60 @@ +// tasksearch.h + +#ifndef _TASKSEARCH_H_ +#define _TASKSEARCH_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + + +enum TaskSearchHand +{ + TSH_UP = 1, // capteur en haut + TSH_DOWN = 2, // capteur en bas +}; + +enum TaskSearchPhase +{ + TSP_DOWN = 1, // descend + TSP_SEARCH = 2, // cherche + TSP_UP = 3, // remonte +}; + + + +class CTaskSearch : public CTask +{ +public: + CTaskSearch(CInstanceManager* iMan, CObject* object); + ~CTaskSearch(); + + BOOL EventProcess(const Event &event); + + Error Start(); + Error IsEnded(); + BOOL Abort(); + +protected: + void InitAngle(); + BOOL CreateMark(); + void DeleteMark(ObjectType type); + +protected: + TaskSearchHand m_hand; + TaskSearchPhase m_phase; + float m_progress; + float m_speed; + float m_time; + float m_lastParticule; + float m_initialAngle[3]; + float m_finalAngle[3]; + BOOL m_bError; +}; + + +#endif //_TASKSEARCH_H_ diff --git a/src/taskshield.cpp b/src/taskshield.cpp new file mode 100644 index 00000000..3ae44c8b --- /dev/null +++ b/src/taskshield.cpp @@ -0,0 +1,557 @@ +// taskshield.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "camera.h" +#include "light.h" +#include "sound.h" +#include "task.h" +#include "taskshield.h" + + +#define ENERGY_TIME 20.0f // durée maximale si pile pleine + + + +// Constructeur de l'objet. + +CTaskShield::CTaskShield(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); + + m_rankSphere = -1; + m_soundChannel = -1; + m_effectLight = -1; +} + +// Destructeur de l'objet. + +CTaskShield::~CTaskShield() +{ + Abort(); +} + + +// Gestion d'un événement. + +BOOL CTaskShield::EventProcess(const Event &event) +{ + CObject* power; + D3DMATRIX* mat; + D3DMATRIX matrix; + D3DVECTOR pos, speed, goal, angle; + D3DCOLORVALUE color; + FPOINT dim; + float energy; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + m_progress += event.rTime*m_speed; // ça avance + m_time += event.rTime; + m_delay -= event.rTime; + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(7.0f, 15.0f, 0.0f); + pos = Transform(*mat, pos); // position sphère + m_shieldPos = pos; + + if ( m_rankSphere != -1 ) + { + m_particule->SetPosition(m_rankSphere, m_shieldPos); + dim.x = RetRadius(); + dim.y = dim.x; + m_particule->SetDimension(m_rankSphere, dim); + } + + if ( m_phase == TS_UP1 ) + { + pos.x = 7.0f; + pos.y = 4.5f+Bounce(m_progress)*3.0f; + pos.z = 0.0f; + m_object->SetPosition(2, pos); + } + + if ( m_phase == TS_UP2 ) + { + pos.x = 0.0f; + pos.y = 1.0f+Bounce(m_progress)*3.0f; + pos.z = 0.0f; + m_object->SetPosition(3, pos); + } + + if ( m_phase == TS_SHIELD ) + { + energy = (1.0f/ENERGY_TIME)*event.rTime; + energy *= RetRadius()/RADIUS_SHIELD_MAX; + power = m_object->RetPower(); + if ( power != 0 ) + { + power->SetEnergy(power->RetEnergy()-energy/power->RetCapacity()); + } + m_energyUsed += energy; + + if ( m_soundChannel == -1 ) + { + m_soundChannel = m_sound->Play(SOUND_SHIELD, m_shieldPos, 0.5f, 0.5f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, 2.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, 1.0f, SOPER_LOOP); + } + else + { + m_sound->Position(m_soundChannel, m_shieldPos); + } + + pos = m_shieldPos; + pos.y += RetRadius()*(2.0f+sinf(m_time*9.0f)*0.2f); + if ( m_effectLight == -1 ) + { + CreateLight(pos); + } + else + { + m_light->SetLightPos(m_effectLight, pos); + + color.r = 0.0f+sinf(m_time*33.2f)*0.2f; + color.g = 0.5f+sinf(m_time*20.0f)*0.5f; + color.b = 0.5f+sinf(m_time*21.3f)*1.0f; + color.a = 0.0f; + m_light->SetLightColor(m_effectLight, color); + } + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_shieldPos; + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + speed.x = (Rand()-0.5f)*0.0f; + speed.z = (Rand()-0.5f)*0.0f; + speed.y = Rand()*15.0f; + dim.x = Rand()*6.0f+4.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLUE, 1.0f, 0.0f, 0.0f); + } + + if ( m_lastRay+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastRay = m_time; + + pos = m_shieldPos; + dim.x = RetRadius()/20.0f; + dim.y = dim.x; + angle.x = (Rand()-0.5f)*PI*1.2f; + angle.y = 0.0f; + angle.z = (Rand()-0.5f)*PI*1.2f; + MatRotateXZY(matrix, angle); + goal = Transform(matrix, D3DVECTOR(0.0f, RetRadius()-dim.x, 0.0f)); + goal += pos; + m_particule->CreateRay(pos, goal, PARTIRAY2, dim, 0.3f); + } + + if ( m_lastIncrease+0.2f <= m_time ) + { + m_lastIncrease = m_time; + IncreaseShield(); + } + } + + if ( m_phase == TS_SMOKE ) + { + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.5f, 0.5f, 2.0f, SOPER_STOP); + m_soundChannel = -1; + } + + if ( m_lastParticule+m_engine->ParticuleAdapt(0.05f) <= m_time ) + { + m_lastParticule = m_time; + + pos = m_shieldPos; + pos.x += (Rand()-0.5f)*5.0f; + pos.z += (Rand()-0.5f)*5.0f; + speed.x = (Rand()-0.5f)*3.0f; + speed.z = (Rand()-0.5f)*3.0f; + speed.y = (Rand()-0.5f)*3.0f; + dim.x = Rand()*1.5f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE3, 4.0f); + } + } + + if ( m_phase == TS_DOWN1 ) + { + pos.x = 0.0f; + pos.y = 1.0f+(1.0f-Bounce(m_progress))*3.0f; + pos.z = 0.0f; + m_object->SetPosition(3, pos); + } + + if ( m_phase == TS_DOWN2 ) + { + pos.x = 7.0f; + pos.y = 4.5f+(1.0f-Bounce(m_progress))*3.0f; + pos.z = 0.0f; + m_object->SetPosition(2, pos); + } + + return TRUE; +} + + +// Déploie le bouclier. +// Le délai n'est utile qu'avec TSM_UP ! + +Error CTaskShield::Start(TaskShieldMode mode, float delay) +{ + CObject* power; + D3DMATRIX* mat; + D3DVECTOR pos, iPos, oPos, speed; + ObjectType type; + float energy; + + if ( mode == TSM_DOWN ) + { + return Stop(); + } + + if ( mode == TSM_UPDATE ) + { + if ( m_object->RetSelect() ) + { + m_brain->UpdateInterface(); + } + return ERR_OK; + } + + type = m_object->RetType(); + if ( type != OBJECT_MOBILErs ) return ERR_SHIELD_VEH; + + m_bError = TRUE; // opération impossible + if ( !m_physics->RetLand() ) return ERR_SHIELD_VEH; + + power = m_object->RetPower(); + if ( power == 0 ) return ERR_SHIELD_ENERGY; + energy = power->RetEnergy(); + if ( energy == 0.0f ) return ERR_SHIELD_ENERGY; + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(7.0f, 15.0f, 0.0f); + pos = Transform(*mat, pos); // position sphère + m_shieldPos = pos; + + m_sound->Play(SOUND_PSHHH2, m_shieldPos, 1.0f, 0.7f); + + m_phase = TS_UP1; + m_progress = 0.0f; + m_speed = 1.0f/1.0f; + m_time = 0.0f; + m_delay = delay; + m_lastParticule = 0.0f; + m_lastRay = 0.0f; + m_lastIncrease = 0.0f; + m_energyUsed = 0.0f; + + m_bError = FALSE; // ok + + if ( m_object->RetSelect() ) + { + m_brain->UpdateInterface(); + } +//? m_camera->StartCentering(m_object, PI*0.85f, -PI*0.15f, RetRadius()+40.0f, 3.0f); + return ERR_OK; +} + +// Rentre le bouclier. + +Error CTaskShield::Stop() +{ + float time; + + if ( m_phase == TS_SHIELD ) + { + m_object->SetShieldRadius(0.0f); + + if ( m_rankSphere != -1 ) + { + m_particule->SetPhase(m_rankSphere, PARPHEND, 3.0f); + m_rankSphere = -1; + } + + if ( m_effectLight != -1 ) + { + m_light->DeleteLight(m_effectLight); + m_effectLight = -1; + } + + time = m_energyUsed*4.0f; + if ( time < 1.0f ) time = 1.0f; + if ( time > 4.0f ) time = 4.0f; + + m_phase = TS_SMOKE; + m_speed = 1.0f/time; + + m_camera->StopCentering(m_object, 4.0f); + + if ( m_object->RetSelect() ) + { + m_brain->UpdateInterface(); + } + return ERR_CONTINUE; + } + + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskShield::IsEnded() +{ + CObject* power; + D3DVECTOR pos, speed; + FPOINT dim; + float energy; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + + if ( m_phase == TS_SHIELD ) + { + m_object->SetShieldRadius(RetRadius()); + + power = m_object->RetPower(); + if ( power == 0 ) + { + energy = 0.0f; + } + else + { + energy = power->RetEnergy(); + } + + if ( energy == 0.0f || m_delay <= 0.0f ) + { + Stop(); + } + return ERR_CONTINUE; + } + + if ( m_progress < 1.0f ) return ERR_CONTINUE; + m_progress = 0.0f; + + if ( m_phase == TS_UP1 ) + { + pos.x = 7.0f; + pos.y = 4.5f+3.0f; + pos.z = 0.0f; + m_object->SetPosition(2, pos); + + m_sound->Play(SOUND_PSHHH2, m_shieldPos, 1.0f, 1.0f); + + m_phase = TS_UP2; + m_speed = 1.0f/0.8f; + return ERR_CONTINUE; + } + + if ( m_phase == TS_UP2 ) + { + pos.x = 0.0f; + pos.y = 1.0f+3.0f; + pos.z = 0.0f; + m_object->SetPosition(3, pos); + + m_object->SetShieldRadius(RetRadius()); + + pos = m_shieldPos; + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = RetRadius(); + dim.y = dim.x; + m_rankSphere = m_particule->CreateParticule(pos, speed, dim, PARTISPHERE3, 2.0f, 0.0f, 0.0f); + + m_phase = TS_SHIELD; + m_speed = 1.0f/999.9f; + + if ( m_object->RetSelect() ) + { + m_brain->UpdateInterface(); + } + return ERR_CONTINUE; + } + + if ( m_phase == TS_SMOKE ) + { + m_sound->Play(SOUND_PSHHH2, m_shieldPos, 1.0f, 1.0f); + + m_phase = TS_DOWN1; + m_speed = 1.0f/0.8f; + return ERR_CONTINUE; + } + + if ( m_phase == TS_DOWN1 ) + { + m_sound->Play(SOUND_PSHHH2, m_shieldPos, 1.0f, 0.7f); + + m_phase = TS_DOWN2; + m_speed = 1.0f/1.0f; + return ERR_CONTINUE; + } + + Abort(); + return ERR_STOP; +} + +// Indique si l'action est en cours. + +BOOL CTaskShield::IsBusy() +{ + if ( m_phase == TS_SHIELD ) + { + return FALSE; + } + + return TRUE; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskShield::Abort() +{ + D3DVECTOR pos; + + m_object->SetShieldRadius(0.0f); + + pos.x = 7.0f; + pos.y = 4.5f; + pos.z = 0.0f; + m_object->SetPosition(2, pos); + + pos.x = 0.0f; + pos.y = 1.0f; + pos.z = 0.0f; + m_object->SetPosition(3, pos); + + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.5f, 0.5f, 2.0f, SOPER_STOP); + m_soundChannel = -1; + } + + if ( m_rankSphere != -1 ) + { + m_particule->SetPhase(m_rankSphere, PARPHEND, 3.0f); + m_rankSphere = -1; + } + + if ( m_effectLight != -1 ) + { + m_light->DeleteLight(m_effectLight); + m_effectLight = -1; + } + + m_camera->StopCentering(m_object, 2.0f); + return TRUE; +} + + +// Crée la lumière pour accompagner un effet pyrotechnique. + +BOOL CTaskShield::CreateLight(D3DVECTOR pos) +{ + D3DLIGHT7 light; + + if ( !m_engine->RetLightMode() ) return TRUE; + + ZeroMemory( &light, sizeof(light) ); + light.dltType = D3DLIGHT_SPOT; + light.dcvDiffuse.r = 0.0f; + light.dcvDiffuse.g = 1.0f; + light.dcvDiffuse.b = 2.0f; + light.dvPosition.x = pos.x; + light.dvPosition.y = pos.y; + light.dvPosition.z = pos.z; + light.dvDirection.x = 0.0f; + light.dvDirection.y = -1.0f; // contre en bas + light.dvDirection.z = 0.0f; + light.dvRange = D3DLIGHT_RANGE_MAX; + light.dvFalloff = 1.0f; + light.dvAttenuation0 = 1.0f; + light.dvAttenuation1 = 0.0f; + light.dvAttenuation2 = 0.0f; + light.dvTheta = 0.0f; + light.dvPhi = PI/4.0f; + + m_effectLight = m_light->CreateLight(); + if ( m_effectLight == -1 ) return FALSE; + + m_light->SetLight(m_effectLight, light); + m_light->SetLightIntensity(m_effectLight, 1.0f); + + return TRUE; +} + + +// Répare le bouclier des objets dans la sphère du bouclier. + +void CTaskShield::IncreaseShield() +{ + ObjectType type; + CObject* pObj; + D3DVECTOR oPos; + float dist, shield; + int i; + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type == OBJECT_MOTHER || + type == OBJECT_ANT || + type == OBJECT_SPIDER || + type == OBJECT_BEE || + type == OBJECT_WORM ) continue; + + oPos = pObj->RetPosition(0); + dist = Length(oPos, m_shieldPos); + if ( dist <= RetRadius()+10.0f ) + { + shield = pObj->RetShield(); + shield += 0.1f; + if ( shield > 1.0f ) shield = 1.0f; + pObj->SetShield(shield); + } + } +} + + +// Retourne le rayon du bouclier. + +float CTaskShield::RetRadius() +{ + return RADIUS_SHIELD_MIN + (RADIUS_SHIELD_MAX-RADIUS_SHIELD_MIN)*m_object->RetParam(); +} + + + diff --git a/src/taskshield.h b/src/taskshield.h new file mode 100644 index 00000000..e6bfd0ca --- /dev/null +++ b/src/taskshield.h @@ -0,0 +1,74 @@ +// taskshield.h + +#ifndef _TASKSHIELD_H_ +#define _TASKSHIELD_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + +#define RADIUS_SHIELD_MIN 40.0f // rayon min de la zone protégée +#define RADIUS_SHIELD_MAX 100.0f // rayon max de la zone protégée + + +enum TaskShieldPhase +{ + TS_UP1 = 1, // monte + TS_UP2 = 2, // monte + TS_SHIELD = 3, // bouclier déployé + TS_SMOKE = 4, // fume + TS_DOWN1 = 5, // descend + TS_DOWN2 = 6, // descend +}; + +enum TaskShieldMode +{ + TSM_UP = 1, // déploie le bouclier + TSM_DOWN = 2, // rentre le bouclier + TSM_UPDATE = 3, // changement de rayon +}; + + + +class CTaskShield : public CTask +{ +public: + CTaskShield(CInstanceManager* iMan, CObject* object); + ~CTaskShield(); + + BOOL EventProcess(const Event &event); + + Error Start(TaskShieldMode mode, float delay); + Error IsEnded(); + BOOL IsBusy(); + BOOL Abort(); + +protected: + Error Stop(); + BOOL CreateLight(D3DVECTOR pos); + void IncreaseShield(); + float RetRadius(); + +protected: + TaskShieldPhase m_phase; + float m_progress; + float m_speed; + float m_time; + float m_delay; + float m_lastParticule; + float m_lastRay; + float m_lastIncrease; + float m_energyUsed; + BOOL m_bError; + D3DVECTOR m_shieldPos; + int m_rankSphere; + int m_soundChannel; + int m_effectLight; +}; + + +#endif //_TASKSHIELD_H_ diff --git a/src/taskspiderexplo.cpp b/src/taskspiderexplo.cpp new file mode 100644 index 00000000..6a2a322c --- /dev/null +++ b/src/taskspiderexplo.cpp @@ -0,0 +1,108 @@ +// taskspiderexplo.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "object.h" +#include "physics.h" +#include "pyro.h" +#include "motion.h" +#include "motionspider.h" +#include "task.h" +#include "taskspiderexplo.h" + + + + +// Constructeur de l'objet. + +CTaskSpiderExplo::CTaskSpiderExplo(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); + + m_time = 0.0f; + m_bError = FALSE; +} + +// Destructeur de l'objet. + +CTaskSpiderExplo::~CTaskSpiderExplo() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskSpiderExplo::EventProcess(const Event &event) +{ + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + // Objet momentanément immobile (fourmi sur le dos) ? + if ( m_object->RetFixed() ) + { + m_bError = TRUE; + return TRUE; + } + + m_time += event.rTime; + + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskSpiderExplo::Start() +{ + m_motion->SetAction(MSS_EXPLO, 1.0f); // l'abdomen gonfle + m_time = 0.0f; + + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + + m_bError = FALSE; + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskSpiderExplo::IsEnded() +{ + CPyro* pyro; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + + if ( m_bError ) + { + Abort(); + return ERR_STOP; + } + + if ( m_time < 1.0f ) return ERR_CONTINUE; + + pyro = new CPyro(m_iMan); + pyro->Create(PT_SPIDER, m_object); // l'araignée explose (suicide) + + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskSpiderExplo::Abort() +{ + return TRUE; +} + diff --git a/src/taskspiderexplo.h b/src/taskspiderexplo.h new file mode 100644 index 00000000..a934b836 --- /dev/null +++ b/src/taskspiderexplo.h @@ -0,0 +1,34 @@ +// taskspiderexplo.h + +#ifndef _TASKSPIDEREXPLO_H_ +#define _TASKSPIDEREXPLO_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + +class CTaskSpiderExplo : public CTask +{ +public: + CTaskSpiderExplo(CInstanceManager* iMan, CObject* object); + ~CTaskSpiderExplo(); + + BOOL EventProcess(const Event &event); + + Error Start(); + Error IsEnded(); + BOOL Abort(); + +protected: + +protected: + float m_time; + BOOL m_bError; +}; + + +#endif //_TASKSPIDEREXPLO_H_ diff --git a/src/tasktake.cpp b/src/tasktake.cpp new file mode 100644 index 00000000..a979a3e5 --- /dev/null +++ b/src/tasktake.cpp @@ -0,0 +1,596 @@ +// tasktake.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "water.h" +#include "camera.h" +#include "motion.h" +#include "motionhuman.h" +#include "sound.h" +#include "robotmain.h" +#include "task.h" +#include "tasktake.h" + + + + +// Constructeur de l'objet. + +CTaskTake::CTaskTake(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); + + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + + m_arm = TTA_NEUTRAL; +} + +// Destructeur de l'objet. + +CTaskTake::~CTaskTake() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskTake::EventProcess(const Event &event) +{ + float a, g, cirSpeed; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + if ( m_bTurn ) // rotation préliminaire ? + { + a = m_object->RetAngleY(0); + g = m_angle; + cirSpeed = Direction(a, g)*2.0f; + if ( cirSpeed > 1.0f ) cirSpeed = 1.0f; + if ( cirSpeed < -1.0f ) cirSpeed = -1.0f; + + m_physics->SetMotorSpeedZ(cirSpeed); // tourne à gauche/droite + return TRUE; + } + + m_progress += event.rTime*m_speed; // ça avance + + m_physics->SetMotorSpeed(D3DVECTOR(0.0f, 0.0f, 0.0f)); // immobile ! + + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskTake::Start() +{ + ObjectType type; + CObject* other; + float iAngle, oAngle, h; + D3DVECTOR pos; + + m_height = 0.0f; + m_step = 0; + m_progress = 0.0f; + + iAngle = m_object->RetAngleY(0); + iAngle = NormAngle(iAngle); // 0..2*PI + oAngle = iAngle; + + m_bError = TRUE; // opération impossible + if ( !m_physics->RetLand() ) + { + pos = m_object->RetPosition(0); + h = m_water->RetLevel(m_object); + if ( pos.y < h ) return ERR_MANIP_WATER; // impossible sous l'eau + return ERR_MANIP_FLY; + } + + type = m_object->RetType(); + if ( type != OBJECT_HUMAN && + type != OBJECT_TECH ) return ERR_MANIP_VEH; + + m_physics->SetMotorSpeed(D3DVECTOR(0.0f, 0.0f, 0.0f)); + + if ( m_object->RetFret() == 0 ) + { + m_order = TTO_TAKE; + } + else + { + m_order = TTO_DEPOSE; + } + + if ( m_order == TTO_TAKE ) + { + pos = m_object->RetPosition(0); + h = m_water->RetLevel(m_object); + if ( pos.y < h ) return ERR_MANIP_WATER; // impossible sous l'eau + + other = SearchFriendObject(oAngle, 1.5f, PI*0.50f); + if ( other != 0 && other->RetPower() != 0 ) + { + type = other->RetPower()->RetType(); + if ( type == OBJECT_URANIUM ) return ERR_MANIP_RADIO; + if ( type != OBJECT_FRET && + type != OBJECT_STONE && + type != OBJECT_BULLET && + type != OBJECT_METAL && + type != OBJECT_POWER && + type != OBJECT_ATOMIC && + type != OBJECT_BBOX && + type != OBJECT_KEYa && + type != OBJECT_KEYb && + type != OBJECT_KEYc && + type != OBJECT_KEYd && + type != OBJECT_TNT ) return ERR_MANIP_FRIEND; +//? m_camera->StartCentering(m_object, PI*0.3f, -PI*0.1f, 0.0f, 0.8f); + m_arm = TTA_FRIEND; + } + else + { + other = SearchTakeObject(oAngle, 1.5f, PI*0.45f); + if ( other == 0 ) return ERR_MANIP_NIL; + type = other->RetType(); + if ( type == OBJECT_URANIUM ) return ERR_MANIP_RADIO; +//? m_camera->StartCentering(m_object, PI*0.3f, 99.9f, 0.0f, 0.8f); + m_arm = TTA_FFRONT; + m_main->HideDropZone(other); // cache zone constructible + } + } + + if ( m_order == TTO_DEPOSE ) + { +//? speed = m_physics->RetMotorSpeed(); +//? if ( speed.x != 0.0f || +//? speed.z != 0.0f ) return ERR_MANIP_MOTOR; + + other = SearchFriendObject(oAngle, 1.5f, PI*0.50f); + if ( other != 0 && other->RetPower() == 0 ) + { +//? m_camera->StartCentering(m_object, PI*0.3f, -PI*0.1f, 0.0f, 0.8f); + m_arm = TTA_FRIEND; + } + else + { + if ( !IsFreeDeposeObject(D3DVECTOR(2.5f, 0.0f, 0.0f)) ) return ERR_MANIP_OCC; +//? m_camera->StartCentering(m_object, PI*0.3f, 99.9f, 0.0f, 0.8f); + m_arm = TTA_FFRONT; + } + } + + m_bTurn = TRUE; // rotation préliminaire nécessaire + m_angle = oAngle; // angle à atteindre + + m_physics->SetFreeze(TRUE); // on ne bouge plus + + m_bError = FALSE; // ok + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskTake::IsEnded() +{ + CObject* fret; + float angle; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + + if ( m_bTurn ) // rotation préliminaire ? + { + angle = m_object->RetAngleY(0); + angle = NormAngle(angle); // 0..2*PI + + if ( TestAngle(angle, m_angle-PI*0.01f, m_angle+PI*0.01f) ) + { + m_bTurn = FALSE; // rotation terminée + m_physics->SetMotorSpeedZ(0.0f); + + if ( m_arm == TTA_FFRONT ) + { + m_motion->SetAction(MHS_TAKE, 0.2f); // se baisse + } + if ( m_arm == TTA_FRIEND ) + { + if ( m_height <= 3.0f ) + { + m_motion->SetAction(MHS_TAKEOTHER, 0.2f); // se baisse + } + else + { + m_motion->SetAction(MHS_TAKEHIGH, 0.2f); // se baisse + } + } + m_progress = 0.0f; + m_speed = 1.0f/0.6f; + } + return ERR_CONTINUE; + } + + if ( m_progress < 1.0f ) return ERR_CONTINUE; + m_progress = 0.0f; + + m_step ++; + + if ( m_order == TTO_TAKE ) + { + if ( m_step == 1 ) + { + if ( TruckTakeObject() ) + { + if ( m_arm == TTA_FRIEND && + (m_fretType == OBJECT_POWER || + m_fretType == OBJECT_ATOMIC ) ) + { + m_sound->Play(SOUND_POWEROFF, m_object->RetPosition(0)); + } + } + m_motion->SetAction(MHS_UPRIGHT, 0.4f); // se relève + m_progress = 0.0f; + m_speed = 1.0f/0.8f; + m_camera->StopCentering(m_object, 0.8f); + return ERR_CONTINUE; + } + } + + if ( m_order == TTO_DEPOSE ) + { + if ( m_step == 1 ) + { + fret = m_object->RetFret(); + TruckDeposeObject(); + if ( m_arm == TTA_FRIEND && + (m_fretType == OBJECT_POWER || + m_fretType == OBJECT_ATOMIC ) ) + { + m_sound->Play(SOUND_POWERON, m_object->RetPosition(0)); + } + if ( fret != 0 && m_fretType == OBJECT_METAL && m_arm == TTA_FFRONT ) + { + m_main->ShowDropZone(fret, m_object); // montre zone constructible + } + m_motion->SetAction(-1); // se relève + m_progress = 0.0f; + m_speed = 1.0f/0.4f; + m_camera->StopCentering(m_object, 0.8f); + return ERR_CONTINUE; + } + } + + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskTake::Abort() +{ + m_motion->SetAction(-1); + m_camera->StopCentering(m_object, 0.8f); + m_physics->SetFreeze(FALSE); // on bouge de nouveau + return TRUE; +} + + +// Cherche l'objet à prendre devant. + +CObject* CTaskTake::SearchTakeObject(float &angle, + float dLimit, float aLimit) +{ + CObject *pObj, *pBest; + D3DVECTOR iPos, oPos; + ObjectType type; + float min, iAngle, bAngle, a, distance; + int i; + + iPos = m_object->RetPosition(0); + iAngle = m_object->RetAngleY(0); + iAngle = NormAngle(iAngle); // 0..2*PI + + min = 1000000.0f; + pBest = 0; + bAngle = 0.0f; + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + + if ( type != OBJECT_FRET && + type != OBJECT_STONE && + type != OBJECT_URANIUM && + type != OBJECT_BULLET && + type != OBJECT_METAL && + type != OBJECT_POWER && + type != OBJECT_ATOMIC && + type != OBJECT_BBOX && + type != OBJECT_KEYa && + type != OBJECT_KEYb && + type != OBJECT_KEYc && + type != OBJECT_KEYd && + type != OBJECT_TNT ) continue; + + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + if ( pObj->RetLock() ) continue; + if ( pObj->RetZoomY(0) != 1.0f ) continue; + + oPos = pObj->RetPosition(0); + distance = Length(oPos, iPos); + if ( distance >= 4.0f-dLimit && + distance <= 4.0f+dLimit ) + { + angle = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + if ( TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + { + a = Abs(angle-iAngle); + if ( a > PI ) a = PI*2.0f-a; + if ( a < min ) + { + min = a; + pBest = pObj; + bAngle = angle; + } + } + } + } + angle = bAngle; + return pBest; +} + +// Cherche le robot sur lequel on veut prendre ou poser une pile. + +CObject* CTaskTake::SearchFriendObject(float &angle, + float dLimit, float aLimit) +{ + Character* character; + CObject* pObj; + CObject* pPower; + D3DMATRIX* mat; + D3DVECTOR iPos, oPos; + ObjectType type, powerType; + float iAngle, iRad, distance; + int i; + + if ( !m_object->GetCrashSphere(0, iPos, iRad) ) return 0; + iAngle = m_object->RetAngleY(0); + iAngle = NormAngle(iAngle); // 0..2*PI + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; // soi-même ? + + type = pObj->RetType(); + if ( type != OBJECT_MOBILEfa && + type != OBJECT_MOBILEta && + type != OBJECT_MOBILEwa && + type != OBJECT_MOBILEia && + type != OBJECT_MOBILEfc && + type != OBJECT_MOBILEtc && + type != OBJECT_MOBILEwc && + type != OBJECT_MOBILEic && + type != OBJECT_MOBILEfi && + type != OBJECT_MOBILEti && + type != OBJECT_MOBILEwi && + type != OBJECT_MOBILEii && + type != OBJECT_MOBILEfs && + type != OBJECT_MOBILEts && + type != OBJECT_MOBILEws && + type != OBJECT_MOBILEis && + type != OBJECT_MOBILErt && + type != OBJECT_MOBILErc && + type != OBJECT_MOBILErr && + type != OBJECT_MOBILErs && + type != OBJECT_MOBILEsa && + type != OBJECT_MOBILEtg && + type != OBJECT_MOBILEft && + type != OBJECT_MOBILEtt && + type != OBJECT_MOBILEwt && + type != OBJECT_MOBILEit && + type != OBJECT_TOWER && + type != OBJECT_RESEARCH && + type != OBJECT_ENERGY && + type != OBJECT_LABO && + type != OBJECT_NUCLEAR ) continue; + + pPower = pObj->RetPower(); + if ( pPower != 0 ) + { + if ( pPower->RetLock() ) continue; + if ( pPower->RetZoomY(0) != 1.0f ) continue; + + powerType = pPower->RetType(); + if ( powerType == OBJECT_NULL || + powerType == OBJECT_FIX ) continue; + } + + mat = pObj->RetWorldMatrix(0); + character = pObj->RetCharacter(); + oPos = Transform(*mat, character->posPower); + + distance = Abs(Length(oPos, iPos) - (iRad+1.0f)); + if ( distance <= dLimit ) + { + angle = RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! + if ( TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + { + character = pObj->RetCharacter(); + m_height = character->posPower.y; + return pObj; + } + } + } + + return 0; +} + +// Prend l'objet placé devant. + +BOOL CTaskTake::TruckTakeObject() +{ + CObject* fret; + CObject* other; + D3DMATRIX matRotate; + float angle; + + if ( m_arm == TTA_FFRONT ) // prend au sol devant ? + { +//? fret = SearchTakeObject(angle, 1.5f, PI*0.04f); + fret = SearchTakeObject(angle, 1.5f, PI*0.15f); //OK 1.9 + if ( fret == 0 ) return FALSE; // rien à prendre ? + m_fretType = fret->RetType(); + + fret->SetTruck(m_object); + fret->SetTruckPart(4); // prend avec la main + +//? fret->SetPosition(0, D3DVECTOR(2.2f, -1.0f, 1.1f)); + fret->SetPosition(0, D3DVECTOR(1.7f, -0.5f, 1.1f)); + fret->SetAngleY(0, 0.1f); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, 0.8f); + + m_object->SetFret(fret); // prend + } + + if ( m_arm == TTA_FRIEND ) // prend pile sur amis ? + { + other = SearchFriendObject(angle, 1.5f, PI*0.04f); + if ( other == 0 ) return FALSE; + + fret = other->RetPower(); + if ( fret == 0 ) return FALSE; // l'autre n'a pas de pile ? + m_fretType = fret->RetType(); + + other->SetPower(0); + fret->SetTruck(m_object); + fret->SetTruckPart(4); // prend avec la main + +//? fret->SetPosition(0, D3DVECTOR(2.2f, -1.0f, 1.1f)); + fret->SetPosition(0, D3DVECTOR(1.7f, -0.5f, 1.1f)); + fret->SetAngleY(0, 0.1f); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, 0.8f); + + m_object->SetFret(fret); // prend + } + + return TRUE; +} + +// Dépose l'objet pris. + +BOOL CTaskTake::TruckDeposeObject() +{ + Character* character; + CObject* fret; + CObject* other; + D3DMATRIX* mat; + D3DVECTOR pos; + float angle; + + if ( m_arm == TTA_FFRONT ) // dépose au sol devant ? + { + fret = m_object->RetFret(); + if ( fret == 0 ) return FALSE; // ne porte rien ? + m_fretType = fret->RetType(); + + mat = fret->RetWorldMatrix(0); + pos = Transform(*mat, D3DVECTOR(-0.5f, 1.0f, 0.0f)); + m_terrain->MoveOnFloor(pos); + fret->SetPosition(0, pos); + fret->SetAngleY(0, m_object->RetAngleY(0)+PI/2.0f); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, 0.0f); + fret->FloorAdjust(); // plaque bien au sol + + fret->SetTruck(0); + m_object->SetFret(0); // dépose + } + + if ( m_arm == TTA_FRIEND ) // dépose pile sur amis ? + { + other = SearchFriendObject(angle, 1.5f, PI*0.04f); + if ( other == 0 ) return FALSE; + + fret = other->RetPower(); + if ( fret != 0 ) return FALSE; // l'autre a déjà une pile ? + + fret = m_object->RetFret(); + if ( fret == 0 ) return FALSE; + m_fretType = fret->RetType(); + + other->SetPower(fret); + fret->SetTruck(other); + + character = other->RetCharacter(); + fret->SetPosition(0, character->posPower); + fret->SetAngleY(0, 0.0f); + fret->SetAngleX(0, 0.0f); + fret->SetAngleZ(0, 0.0f); + fret->SetTruckPart(0); // porté par la base + + m_object->SetFret(0); // dépose + } + + return TRUE; +} + +// Cherche si un emplacement permet de déposer un objet. + +BOOL CTaskTake::IsFreeDeposeObject(D3DVECTOR pos) +{ + CObject* pObj; + D3DMATRIX* mat; + D3DVECTOR iPos, oPos; + float oRadius; + int i, j; + + mat = m_object->RetWorldMatrix(0); + iPos = Transform(*mat, pos); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + if ( pObj == m_object ) continue; + if ( !pObj->RetActif() ) continue; // inactif ? + if ( pObj->RetTruck() != 0 ) continue; // objet transporté ? + + j = 0; + while ( pObj->GetCrashSphere(j++, oPos, oRadius) ) + { + if ( Length(iPos, oPos)-(oRadius+1.0f) < 1.0f ) + { + return FALSE; // emplacement occupé + } + } + } + return TRUE; // emplacement libre +} + + diff --git a/src/tasktake.h b/src/tasktake.h new file mode 100644 index 00000000..a721022a --- /dev/null +++ b/src/tasktake.h @@ -0,0 +1,65 @@ +// tasktake.h + +#ifndef _TASKTAKE_H_ +#define _TASKTAKE_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + + +enum TaskTakeOrder +{ + TTO_TAKE = 1, // prend un objet + TTO_DEPOSE = 2, // dépose l'objet +}; + +enum TaskTakeArm +{ + TTA_NEUTRAL = 1, // bras vide au repos + TTA_FFRONT = 2, // bras au sol + TTA_FRIEND = 3, // bras derrière un robot amis +}; + + + +class CTaskTake : public CTask +{ +public: + CTaskTake(CInstanceManager* iMan, CObject* object); + ~CTaskTake(); + + BOOL EventProcess(const Event &event); + + Error Start(); + Error IsEnded(); + BOOL Abort(); + +protected: + CObject* SearchTakeObject(float &angle, float dLimit, float aLimit); + CObject* SearchFriendObject(float &angle, float dLimit, float aLimit); + BOOL TruckTakeObject(); + BOOL TruckDeposeObject(); + BOOL IsFreeDeposeObject(D3DVECTOR pos); + +protected: + CTerrain* m_terrain; + + TaskTakeOrder m_order; + TaskTakeArm m_arm; + int m_step; + float m_speed; + float m_progress; + float m_height; + BOOL m_bError; + BOOL m_bTurn; + float m_angle; + ObjectType m_fretType; +}; + + +#endif //_TASKTAKE_H_ diff --git a/src/taskterraform.cpp b/src/taskterraform.cpp new file mode 100644 index 00000000..835da53f --- /dev/null +++ b/src/taskterraform.cpp @@ -0,0 +1,413 @@ +// taskterraform.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "language.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "pyro.h" +#include "brain.h" +#include "camera.h" +#include "sound.h" +#include "motion.h" +#include "motionant.h" +#include "motionspider.h" +#include "task.h" +#include "taskterraform.h" + + +#define ENERGY_TERRA 0.40f // énergie consommée par coup +#define ACTION_RADIUS 400.0f + + + +// Constructeur de l'objet. + +CTaskTerraform::CTaskTerraform(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); + m_lastParticule = 0.0f; + m_soundChannel = -1; +} + +// Destructeur de l'objet. + +CTaskTerraform::~CTaskTerraform() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskTerraform::EventProcess(const Event &event) +{ + CObject* power; + D3DMATRIX* mat; + D3DVECTOR pos, dir, speed; + FPOINT dim; + float energy; + + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + if ( m_bError ) return FALSE; + + m_progress += event.rTime*m_speed; // ça avance + m_time += event.rTime; + + if ( m_phase == TTP_CHARGE ) + { + if ( m_soundChannel == -1 ) + { +#if _TEEN + m_soundChannel = m_sound->Play(SOUND_GGG, m_object->RetPosition(0), 1.0f, 0.5f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 2.0f, 1.5f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.5f, 0.5f, SOPER_STOP); +#else + m_soundChannel = m_sound->Play(SOUND_GGG, m_object->RetPosition(0), 1.0f, 0.5f, TRUE); + m_sound->AddEnvelope(m_soundChannel, 1.0f, 2.0f, 4.0f, SOPER_CONTINUE); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.5f, 0.5f, SOPER_STOP); +#endif + } + + dir.x = 0.0f; + dir.y = (Rand()-0.5f)*0.2f*m_progress; + dir.z = 0.0f; + m_object->SetCirVibration(dir); + + m_object->SetZoom(0, 1.0f+m_progress*0.2f); + + power = m_object->RetPower(); + if ( power != 0 ) + { + power->SetZoom(0, 1.0f+m_progress*1.0f); + + energy = power->RetEnergy(); + energy -= event.rTime*ENERGY_TERRA/power->RetCapacity()/4.0f; + if ( energy < 0.0f ) energy = 0.0f; + power->SetEnergy(energy); + } + } + + if ( m_phase == TTP_DOWN ) + { + pos.x = 9.0f; +#if _TEEN + pos.y = 4.0f-m_progress*4.0f; +#else + pos.y = 4.0f-m_progress*5.8f; +#endif + pos.z = 0.0f; + m_object->SetPosition(2, pos); + } + + if ( m_phase == TTP_UP ) + { + pos.x = 9.0f; +#if _TEEN + pos.y = 4.0f-(1.0f-m_progress)*4.0f; +#else + pos.y = 4.0f-(1.0f-m_progress)*5.8f; +#endif + pos.z = 0.0f; + m_object->SetPosition(2, pos); + } + + dir.x = 0.0f; + dir.y = 0.0f; + dir.z = 0.0f; + pos = m_object->RetPosition(2); + if ( pos.y < 0.0f ) + { + dir.z = -atanf((pos.y/2.0f)/9.0f); + } + m_object->SetInclinaison(dir); + + if ( m_time-m_lastParticule >= m_engine->ParticuleAdapt(0.05f) ) + { + m_lastParticule = m_time; + + mat = m_object->RetWorldMatrix(0); + + if ( m_phase == TTP_CHARGE ) + { + // Pile. + pos = D3DVECTOR(-6.0f, 5.5f+2.0f*m_progress, 0.0f); + pos.x += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + pos = Transform(*mat, pos); + speed.x = (Rand()-0.5f)*6.0f*(1.0f+m_progress*4.0f); + speed.z = (Rand()-0.5f)*6.0f*(1.0f+m_progress*4.0f); + speed.y = 6.0f+Rand()*4.0f*(1.0f+m_progress*2.0f); + dim.x = 0.5f+1.5f*m_progress; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIBLITZ, 2.0f, 20.0f); + } + + if ( m_phase != TTP_CHARGE ) + { + // Grille gauche. + pos = D3DVECTOR(-1.0f, 5.8f, 3.5f); + pos.x += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + pos = Transform(*mat, pos); + speed.x = Rand()*4.0f; + speed.z = Rand()*2.0f; + speed.y = 2.5f+Rand()*1.0f; + speed = Transform(*mat, speed); + speed -= m_object->RetPosition(0); + dim.x = Rand()*1.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE1, 3.0f); + + // Grille droite. + pos = D3DVECTOR(-1.0f, 5.8f, -3.5f); + pos.x += (Rand()-0.5f)*1.0f; + pos.z += (Rand()-0.5f)*1.0f; + pos = Transform(*mat, pos); + speed.x = Rand()*4.0f; + speed.z = -Rand()*2.0f; + speed.y = 2.5f+Rand()*1.0f; + speed = Transform(*mat, speed); + speed -= m_object->RetPosition(0); + dim.x = Rand()*1.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTISMOKE1, 3.0f); + } + } + + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskTerraform::Start() +{ + CObject* power; + D3DMATRIX* mat; + D3DVECTOR pos, speed; + float energy; + + ObjectType type; + + m_bError = TRUE; // opération impossible + if ( !m_physics->RetLand() ) return ERR_TERRA_VEH; + + type = m_object->RetType(); + if ( type != OBJECT_MOBILErt ) return ERR_TERRA_VEH; + + power = m_object->RetPower(); + if ( power == 0 ) return ERR_TERRA_ENERGY; + energy = power->RetEnergy(); + if ( energy < ENERGY_TERRA/power->RetCapacity()+0.05f ) return ERR_TERRA_ENERGY; + + speed = m_physics->RetMotorSpeed(); + if ( speed.x != 0.0f || + speed.z != 0.0f ) return ERR_MANIP_MOTOR; + + mat = m_object->RetWorldMatrix(0); + pos = D3DVECTOR(9.0f, 0.0f, 0.0f); + pos = Transform(*mat, pos); // position pillon + m_terraPos = pos; + + m_phase = TTP_CHARGE; + m_progress = 0.0f; +#if _TEEN + m_speed = 1.0f/1.5f; +#else + m_speed = 1.0f/4.0f; +#endif + m_time = 0.0f; + + m_bError = FALSE; // ok + + m_camera->StartCentering(m_object, PI*0.35f, 99.9f, 20.0f, 2.0f); + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskTerraform::IsEnded() +{ + CObject* power; + D3DVECTOR pos, speed; + FPOINT dim; + float dist, duration; + int i, max; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bError ) return ERR_STOP; + + if ( m_progress < 1.0f ) return ERR_CONTINUE; + m_progress = 0.0f; + + if ( m_phase == TTP_CHARGE ) + { +#if _TEEN + Terraform(); // modifie le terrain. +#endif + + m_phase = TTP_DOWN; + m_speed = 1.0f/0.2f; + return ERR_CONTINUE; + } + + if ( m_phase == TTP_DOWN ) + { +#if !_TEEN + Terraform(); // modifie le terrain. +#endif + + m_object->SetCirVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_object->SetZoom(0, 1.0f); + + power = m_object->RetPower(); + if ( power != 0 ) + { + power->SetZoom(0, 1.0f); + } + + max= (int)(50.0f*m_engine->RetParticuleDensity()); + for ( i=0 ; iMoveOnFloor(pos); + dist = Length(pos, m_terraPos); + speed = D3DVECTOR(0.0f, 0.0f, 0.0f); + dim.x = 2.0f+(40.0f-dist)/(1.0f+Rand()*4.0f); + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTICRASH, 2.0f); + + pos = m_terraPos; + speed.x = (Rand()-0.5f)*40.0f; + speed.z = (Rand()-0.5f)*40.0f; + speed.y = Rand()*15.0f+15.0f; + dim.x = 0.6f; + dim.y = dim.x; + pos.y += dim.y; + duration = Rand()*3.0f+3.0f; + m_particule->CreateTrack(pos, speed, dim, PARTITRACK5, + duration, Rand()*10.0f+15.0f, + duration*0.2f, 1.0f); + } + + m_phase = TTP_TERRA; + m_speed = 1.0f/2.0f; + return ERR_CONTINUE; + } + + if ( m_phase == TTP_TERRA ) + { + m_phase = TTP_UP; + m_speed = 1.0f/1.0f; + return ERR_CONTINUE; + } + + Abort(); + return ERR_STOP; +} + +// Termine brutalement l'action en cours. + +BOOL CTaskTerraform::Abort() +{ + CObject* power; + + if ( m_soundChannel != -1 ) + { + m_sound->FlushEnvelope(m_soundChannel); + m_sound->AddEnvelope(m_soundChannel, 0.0f, 0.5f, 0.3f, SOPER_STOP); + m_soundChannel = -1; + } + + m_object->SetPosition(2, D3DVECTOR(9.0f, 4.0f, 0.0f)); + m_object->SetInclinaison(D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_object->SetCirVibration(D3DVECTOR(0.0f, 0.0f, 0.0f)); + m_object->SetZoom(0, 1.0f); + + power = m_object->RetPower(); + if ( power != 0 ) + { + power->SetZoom(0, 1.0f); + } + + m_camera->StopCentering(m_object, 2.0f); + return TRUE; +} + + +// Retourne toutes les fourmis et les araignées proches. + +BOOL CTaskTerraform::Terraform() +{ + CObject* pObj; + CBrain* brain; + CMotion* motion; + CPyro* pyro; + ObjectType type; + float dist; + int i; + + m_camera->StartEffect(CE_TERRAFORM, m_terraPos, 1.0f); + + m_sound->Play(SOUND_THUMP, m_terraPos); + + for ( i=0 ; i<1000000 ; i++ ) + { + pObj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i); + if ( pObj == 0 ) break; + + type = pObj->RetType(); + if ( type == OBJECT_NULL ) continue; + + if ( type == OBJECT_TEEN34 ) // caillou ? + { + dist = Length(m_terraPos, pObj->RetPosition(0)); + if ( dist > 20.0f ) continue; + + pyro = new CPyro(m_iMan); + pyro->Create(PT_FRAGT, pObj); + } + else + { + motion = pObj->RetMotion(); + if ( motion == 0 ) continue; + + dist = Length(m_terraPos, pObj->RetPosition(0)); + if ( dist > ACTION_RADIUS ) continue; + + if ( type == OBJECT_ANT ) + { + brain = pObj->RetBrain(); + if ( brain != 0 ) brain->StopTask(); + motion->SetAction(MAS_BACK1, 0.8f+Rand()*0.3f); + pObj->SetFixed(TRUE); // ne bouge plus + } + if ( type == OBJECT_SPIDER ) + { + brain = pObj->RetBrain(); + if ( brain != 0 ) brain->StopTask(); + motion->SetAction(MSS_BACK1, 0.8f+Rand()*0.3f); + pObj->SetFixed(TRUE); // ne bouge plus + } + } + } + + return TRUE; +} + diff --git a/src/taskterraform.h b/src/taskterraform.h new file mode 100644 index 00000000..5da6bb7f --- /dev/null +++ b/src/taskterraform.h @@ -0,0 +1,52 @@ +// taskterraform.h + +#ifndef _TASKSTERRAFORM_H_ +#define _TASKSTERRAFORM_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + + +enum TaskTerraPhase +{ + TTP_CHARGE = 1, // charge d'énergie + TTP_DOWN = 2, // descend + TTP_TERRA = 3, // frappe + TTP_UP = 4, // remonte +}; + + + +class CTaskTerraform : public CTask +{ +public: + CTaskTerraform(CInstanceManager* iMan, CObject* object); + ~CTaskTerraform(); + + BOOL EventProcess(const Event &event); + + Error Start(); + Error IsEnded(); + BOOL Abort(); + +protected: + BOOL Terraform(); + +protected: + TaskTerraPhase m_phase; + float m_progress; + float m_speed; + float m_time; + float m_lastParticule; + int m_soundChannel; + BOOL m_bError; + D3DVECTOR m_terraPos; +}; + + +#endif //_TASKSTERRAFORM_H_ diff --git a/src/taskturn.cpp b/src/taskturn.cpp new file mode 100644 index 00000000..1086b8a4 --- /dev/null +++ b/src/taskturn.cpp @@ -0,0 +1,131 @@ +// taskturn.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "task.h" +#include "taskturn.h" + + + + +// Constructeur de l'objet. + +CTaskTurn::CTaskTurn(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); +} + +// Destructeur de l'objet. + +CTaskTurn::~CTaskTurn() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskTurn::EventProcess(const Event &event) +{ + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + // Objet momentanément immobile (fourmi sur le dos) ? + if ( m_object->RetFixed() ) + { + m_physics->SetMotorSpeedX(0.0f); // stoppe l'avance + m_physics->SetMotorSpeedZ(0.0f); // stoppe la rotation + m_bError = TRUE; + return TRUE; + } + + return TRUE; +} + + +// Assigne le but à atteindre. +// Un angle positif effectue un virage à droite. + +Error CTaskTurn::Start(float angle) +{ + m_startAngle = m_object->RetAngleY(0); + m_finalAngle = m_startAngle+angle; + + if ( angle < 0.0f ) + { + m_angle = angle+m_physics->RetCirStopLength(); + m_physics->SetMotorSpeedZ(-1.0f); // tourne à gauche + m_bLeft = TRUE; + } + else + { + m_angle = angle-m_physics->RetCirStopLength(); + m_physics->SetMotorSpeedZ(1.0f); // tourne à droite + m_bLeft = FALSE; + } + m_physics->SetMotorSpeedX(0.0f); + m_physics->SetMotorSpeedY(0.0f); + + m_bError = FALSE; + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskTurn::IsEnded() +{ + float angle; + + if ( m_engine->RetPause() ) return ERR_CONTINUE; + + if ( m_bError ) + { + return ERR_STOP; + } + + angle = m_object->RetAngleY(0); + + if ( m_bLeft ) + { + if ( angle <= m_startAngle+m_angle ) + { + m_physics->SetMotorSpeedZ(0.0f); +//? m_physics->SetCirMotionY(MO_MOTSPEED, 0.0f); + m_physics->SetCirMotionY(MO_CURSPEED, 0.0f); +//? m_physics->SetCirMotionY(MO_REASPEED, 0.0f); + m_object->SetAngleY(0, m_finalAngle); + return ERR_STOP; + } + } + else + { + if ( angle >= m_startAngle+m_angle ) + { + m_physics->SetMotorSpeedZ(0.0f); +//? m_physics->SetCirMotionY(MO_MOTSPEED, 0.0f); + m_physics->SetCirMotionY(MO_CURSPEED, 0.0f); +//? m_physics->SetCirMotionY(MO_REASPEED, 0.0f); + m_object->SetAngleY(0, m_finalAngle); + return ERR_STOP; + } + } + + return ERR_CONTINUE; +} + + diff --git a/src/taskturn.h b/src/taskturn.h new file mode 100644 index 00000000..73ea3ea8 --- /dev/null +++ b/src/taskturn.h @@ -0,0 +1,36 @@ +// taskturn.h + +#ifndef _TASKTURN_H_ +#define _TASKTURN_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + +class CTaskTurn : public CTask +{ +public: + CTaskTurn(CInstanceManager* iMan, CObject* object); + ~CTaskTurn(); + + BOOL EventProcess(const Event &event); + + Error Start(float angle); + Error IsEnded(); + +protected: + +protected: + float m_angle; + float m_startAngle; + float m_finalAngle; + BOOL m_bLeft; + BOOL m_bError; +}; + + +#endif //_TASKTURN_H_ diff --git a/src/taskwait.cpp b/src/taskwait.cpp new file mode 100644 index 00000000..d884e3c7 --- /dev/null +++ b/src/taskwait.cpp @@ -0,0 +1,73 @@ +// taskwait.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "terrain.h" +#include "object.h" +#include "physics.h" +#include "brain.h" +#include "task.h" +#include "taskwait.h" + + + + +// Constructeur de l'objet. + +CTaskWait::CTaskWait(CInstanceManager* iMan, CObject* object) + : CTask(iMan, object) +{ + CTask::CTask(iMan, object); +} + +// Destructeur de l'objet. + +CTaskWait::~CTaskWait() +{ +} + + +// Gestion d'un événement. + +BOOL CTaskWait::EventProcess(const Event &event) +{ + if ( m_engine->RetPause() ) return TRUE; + if ( event.event != EVENT_FRAME ) return TRUE; + + m_passTime += event.rTime; + m_bEnded = (m_passTime >= m_waitTime); + return TRUE; +} + + +// Assigne le but à atteindre. + +Error CTaskWait::Start(float time) +{ + m_waitTime = time; // durée à attendre + m_passTime = 0.0f; // durée écoulée + m_bEnded = FALSE; + return ERR_OK; +} + +// Indique si l'action est terminée. + +Error CTaskWait::IsEnded() +{ + if ( m_engine->RetPause() ) return ERR_CONTINUE; + if ( m_bEnded ) return ERR_STOP; + return ERR_CONTINUE; +} + + diff --git a/src/taskwait.h b/src/taskwait.h new file mode 100644 index 00000000..7a1f7820 --- /dev/null +++ b/src/taskwait.h @@ -0,0 +1,34 @@ +// taskwait.h + +#ifndef _TASKWAIT_H_ +#define _TASKWAIT_H_ + + +class CInstanceManager; +class CTerrain; +class CBrain; +class CPhysics; +class CObject; + + +class CTaskWait : public CTask +{ +public: + CTaskWait(CInstanceManager* iMan, CObject* object); + ~CTaskWait(); + + BOOL EventProcess(const Event &event); + + Error Start(float time); + Error IsEnded(); + +protected: + +protected: + float m_waitTime; + float m_passTime; + BOOL m_bEnded; +}; + + +#endif //_TASKWAIT_H_ diff --git a/src/terrain.cpp b/src/terrain.cpp new file mode 100644 index 00000000..3d19cacc --- /dev/null +++ b/src/terrain.cpp @@ -0,0 +1,2263 @@ +// terrain.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "language.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "modfile.h" +#include "water.h" +#include "terrain.h" + + +#define BMPHEAD 1078 + + + +// Constructeur du terrain. + +CTerrain::CTerrain(CInstanceManager* iMan) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_TERRAIN, this); + + m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE); + m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER); + + m_mosaic = 20; + m_brick = 1<<4; + m_size = 10.0f; + m_vision = 200.0f; + m_relief = 0; + m_texture = 0; + m_objRank = 0; + m_scaleMapping = 0.01f; + m_scaleRelief = 1.0f; + m_subdivMapping = 1; + m_depth = 2; + m_texBaseName[0]= 0; + m_texBaseExt[0] = 0; + m_bMultiText = TRUE; + m_bLevelText = FALSE; + m_resources = 0; + m_levelMatTotal = 0; + m_levelMatMax = 0; + m_levelDot = 0; + m_wind = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_defHardness = 0.5f; + + FlushBuildingLevel(); + FlushFlyingLimit(); +} + +// Destructeur du terrain. + +CTerrain::~CTerrain() +{ + free(m_relief); + free(m_texture); + free(m_objRank); + free(m_resources); +} + + +// Génère un nouveau terrain plat. +// Le terrain est composé de mosaïques, elles-mêmes composées +// de briques. Chaque brique est composée de 2 triangles. +// mosaic: nombre de mosaïques selon les axes X et Z +// brick: nombre de briques (puissance de 2) +// size: taille d'une brique selon les axes X et Z +// vision: vision avant un changement de résolution +// scaleMapping: échelle pour mapper les textures +// +// ^ z +// | <---> brick*size +// +---+---+---+---+ +// | | | |_|_| mosaic = 4 +// | | | | | | brick = 2 (brickP2=1) +// +---+---+---+---+ +// |\ \| | | | +// |\ \| | | | +// +---+---o---+---+---> x +// | | | | | +// | | | | | +// +---+---+---+---+ Le terrain est ici +// | | | | | vu de dessus. +// | | | | | +// +---+---+---+---+ +// <---------------> mosaic*brick*size + +BOOL CTerrain::Generate(int mosaic, int brickP2, float size, float vision, + int depth, float hardness) +{ + int dim; + + m_mosaic = mosaic; + m_brick = 1<SetTerrainVision(vision); + + m_bMultiText = TRUE; + m_bLevelText = FALSE; + m_scaleMapping = 1.0f/(m_brick*m_size); + m_subdivMapping = 1; + + dim = (m_mosaic*m_brick+1)*(m_mosaic*m_brick+1); + m_relief = (float*)malloc(sizeof(float)*dim); + ZeroMemory(m_relief, sizeof(float)*dim); + + dim = m_mosaic*m_subdivMapping*m_mosaic*m_subdivMapping; + m_texture = (int*)malloc(sizeof(int)*dim); + ZeroMemory(m_texture, sizeof(int)*dim); + + dim = m_mosaic*m_mosaic; + m_objRank = (int*)malloc(sizeof(int)*dim); + ZeroMemory(m_objRank, sizeof(int)*dim); + + return TRUE; +} + + +int CTerrain::RetMosaic() +{ + return m_mosaic; +} + +int CTerrain::RetBrick() +{ + return m_brick; +} + +float CTerrain::RetSize() +{ + return m_size; +} + +float CTerrain::RetScaleRelief() +{ + return m_scaleRelief; +} + + +// Initialise les noms des textures à utiliser pour le terrain. + +BOOL CTerrain::InitTextures(char* baseName, int* table, int dx, int dy) +{ + int x, y; + char* p; + + m_bLevelText = FALSE; + + strcpy(m_texBaseName, baseName); + p = strchr(m_texBaseName, '.'); // p <- ^début de l'extension + if ( p == 0 ) + { + strcpy(m_texBaseExt, ".tga"); + } + else + { + strcpy(m_texBaseExt, p); // m_texBaseExt <- ".tga" ou ".bmp" + *p = 0; // m_texBaseName <- nom sans extension + } + + for ( y=0 ; y= MAXMATTERRAIN-1 ) return FALSE; + + LevelOpenTable(); + + if ( id == 0 ) + { + id = m_levelID++; // met un id interne standard + } + + strcpy(m_levelMat[i].texName, baseName); + m_levelMat[i].id = id; + m_levelMat[i].u = u; + m_levelMat[i].v = v; + m_levelMat[i].mat[0] = up; + m_levelMat[i].mat[1] = right; + m_levelMat[i].mat[2] = down; + m_levelMat[i].mat[3] = left; + m_levelMat[i].hardness = hardness; + + if ( m_levelMatMax < up+1 ) m_levelMatMax = up+1; + if ( m_levelMatMax < right+1 ) m_levelMatMax = right+1; + if ( m_levelMatMax < down+1 ) m_levelMatMax = down+1; + if ( m_levelMatMax < left+1 ) m_levelMatMax = left+1; + + m_bLevelText = TRUE; + m_subdivMapping = 4; + + m_levelMatTotal ++; + return TRUE; +} + + +// Charge le relief dans un fichier BMP. +// La taille de l'image doit être de dimension dx et dy, +// avec dx=dy=(mosaic*brick)+1. +// L'image doit avoir 8 bits/pixels, 256 couleurs avec +// une palette standard. + +// Conversion coordonnée image (x;y) -> world (x;-;z) : +// Wx = 5*Ix-400 +// Wz = -(5*Iy-400) + +// Conversion coordonnée world (x;-;z) -> image (x;y) : +// Ix = (400+Wx)/5 +// Iy = (400-Wz)/5 + +BOOL CTerrain::ResFromBMP(const char* filename) +{ + FILE* file; + int size, sizem; + + file = fopen(filename, "rb"); + if ( file == NULL ) return FALSE; + + size = (m_mosaic*m_brick)+1; + sizem = ((size+4-1)/4)*4; // taille multiple de 4 supérieur + + if ( m_resources != 0 ) + { + free(m_resources); + } + + m_resources = (unsigned char*)malloc(BMPHEAD+sizem*size); + fread(m_resources, BMPHEAD+sizem*size, 1, file); + + if ( m_resources[18] != (size&0xff) || m_resources[19] != (size>>8) || + m_resources[22] != (size&0xff) || m_resources[23] != (size>>8) ) + { + free(m_resources); + m_resources = 0; + fclose(file); + return FALSE; + } + + fclose(file); + return TRUE; +} + +// Retourne le type de ressource disponible en sous-sol. + +TerrainRes CTerrain::RetResource(const D3DVECTOR &p) +{ + int x, y, size, sizem, ress; + + if ( m_resources == 0 ) return TR_NULL; + + x = (int)((p.x + (m_mosaic*m_brick*m_size)/2.0f)/m_size); + y = (int)((p.z + (m_mosaic*m_brick*m_size)/2.0f)/m_size); + + if ( x < 0 || x > m_mosaic*m_brick || + y < 0 || y > m_mosaic*m_brick ) return TR_NULL; + + size = (m_mosaic*m_brick)+1; + sizem = ((size+4-1)/4)*4; // taille multiple de 4 supérieur + + ress = m_resources[BMPHEAD+x+sizem*y]; + if ( ress == 5 ) return TR_STONE; // rouge ? + if ( ress == 35 ) return TR_URANIUM; // jaune ? + if ( ress == 30 ) return TR_POWER; // vert ? + if ( ress == 24 ) return TR_KEYa; // ~vert ? + if ( ress == 25 ) return TR_KEYb; // ~vert ? + if ( ress == 26 ) return TR_KEYc; // ~vert ? + if ( ress == 27 ) return TR_KEYd; // ~vert ? + + return TR_NULL; +} + + +// Initialise un relief tout plat. + +void CTerrain::FlushRelief() +{ + free(m_relief); + m_relief = 0; +} + +// Charge le relief dans un fichier BMP. +// La taille de l'image doit être de dimension dx et dy, +// avec dx=dy=(mosaic*brick)+1. +// L'image doit avoir 8 bits/pixels, 256 niveaux de gris : +// blanc = sol (y=0) +// noir = montagne (y=255*scaleRelief) + +// Conversion coordonnée image (x;y) -> world (x;-;z) : +// Wx = 5*Ix-400 +// Wz = -(5*Iy-400) + +// Conversion coordonnée world (x;-;z) -> image (x;y) : +// Ix = (400+Wx)/5 +// Iy = (400-Wz)/5 + +BOOL CTerrain::ReliefFromBMP(const char* filename, float scaleRelief, + BOOL adjustBorder) +{ + FILE* file; + unsigned char* buffer; + int size, sizem, x, y; + float level, limit, dist, border; + + m_scaleRelief = scaleRelief; + + file = fopen(filename, "rb"); + if ( file == NULL ) return FALSE; + + size = (m_mosaic*m_brick)+1; + sizem = ((size+4-1)/4)*4; // taille multiple de 4 supérieur + + buffer = (unsigned char*)malloc(BMPHEAD+sizem*size); + fread(buffer, BMPHEAD+sizem*size, 1, file); + + if ( buffer[18] != (size&0xff) || buffer[19] != (size>>8) || + buffer[22] != (size&0xff) || buffer[23] != (size>>8) ) + { + free(buffer); + fclose(file); + return FALSE; + } + + limit = 0.9f; + for ( y=0 ; y limit && adjustBorder ) + { + dist = (dist-limit)/(1.0f-limit); // 0..1 + if ( dist > 1.0f ) dist = 1.0f; + border = 300.0f+Rand()*20.0f; + level = level+dist*(border-level); + } + + m_relief[x+y*size] = level; + } + } + + free(buffer); + fclose(file); + return TRUE; +} + +// Ajoute un point d'élévation dans le buffer du relief. + +BOOL CTerrain::ReliefAddDot(D3DVECTOR pos, float scaleRelief) +{ + float dim; + int size, x, y; + + dim = (m_mosaic*m_brick*m_size)/2.0f; + size = (m_mosaic*m_brick)+1; + + pos.x = (pos.x+dim)/m_size; + pos.z = (pos.z+dim)/m_size; + + x = (int)pos.x; + y = (int)pos.z; + + if ( x < 0 || x >= size || + y < 0 || y >= size ) return FALSE; + + if ( m_relief[x+y*size] < pos.y*scaleRelief ) + { + m_relief[x+y*size] = pos.y*scaleRelief; + } + return TRUE; +} + +// Charge le relief dans un fichier DXF. + +BOOL CTerrain::ReliefFromDXF(const char* filename, float scaleRelief) +{ + FILE* file = NULL; + char line[100]; + int command, rankSommet, nbSommet, nbFace, size; + D3DVECTOR* table; + BOOL bWaitNbSommet; + BOOL bWaitNbFace; + BOOL bWaitSommetX; + BOOL bWaitSommetY; + BOOL bWaitSommetZ; + BOOL bWaitFaceX; + BOOL bWaitFaceY; + BOOL bWaitFaceZ; + float x,y,z; + int p1,p2,p3; + + ZeroMemory(m_relief, sizeof(float)*(m_mosaic*m_brick+1)*(m_mosaic*m_brick+1)); + + file = fopen(filename, "r"); + if ( file == NULL ) return FALSE; + + size = (m_mosaic*m_brick)+1; + table = (D3DVECTOR*)malloc(sizeof(D3DVECTOR)*size*size); + + rankSommet = 0; + bWaitNbSommet = FALSE; + bWaitNbFace = FALSE; + bWaitSommetX = FALSE; + bWaitSommetY = FALSE; + bWaitSommetZ = FALSE; + bWaitFaceX = FALSE; + bWaitFaceY = FALSE; + bWaitFaceZ = FALSE; + + while ( fgets(line, 100, file) != NULL ) + { + sscanf(line, "%d", &command); + if ( fgets(line, 100, file) == NULL ) break; + + if ( command == 66 ) + { + bWaitNbSommet = TRUE; + } + + if ( command == 71 && bWaitNbSommet ) + { + bWaitNbSommet = FALSE; + sscanf(line, "%d", &nbSommet); + if ( nbSommet > size*size ) nbSommet = size*size; + rankSommet = 0; + bWaitNbFace = TRUE; + } + + if ( command == 72 && bWaitNbFace ) + { + bWaitNbFace = FALSE; + sscanf(line, "%d", &nbFace); + bWaitSommetX = TRUE; + } + + if ( command == 10 && bWaitSommetX ) + { + bWaitSommetX = FALSE; + sscanf(line, "%f", &x); + bWaitSommetY = TRUE; + } + + if ( command == 20 && bWaitSommetY ) + { + bWaitSommetY = FALSE; + sscanf(line, "%f", &y); + bWaitSommetZ = TRUE; + } + + if ( command == 30 && bWaitSommetZ ) + { + bWaitSommetZ = FALSE; + sscanf(line, "%f", &z); + + nbSommet --; + if ( nbSommet >= 0 ) + { + D3DVECTOR p(x,z,y); // permutation de Y et Z ! + table[rankSommet++] = p; + bWaitSommetX = TRUE; + } + else + { + bWaitFaceX = TRUE; + } + } + + if ( command == 71 && bWaitFaceX ) + { + bWaitFaceX = FALSE; + sscanf(line, "%d", &p1); + if ( p1 < 0 ) p1 = -p1; + bWaitFaceY = TRUE; + } + + if ( command == 72 && bWaitFaceY ) + { + bWaitFaceY = FALSE; + sscanf(line, "%d", &p2); + if ( p2 < 0 ) p2 = -p2; + bWaitFaceZ = TRUE; + } + + if ( command == 73 && bWaitFaceZ ) + { + bWaitFaceZ = FALSE; + sscanf(line, "%d", &p3); + if ( p3 < 0 ) p3 = -p3; + + nbFace --; + if ( nbFace >= 0 ) + { + ReliefAddDot(table[p3-1], scaleRelief); + ReliefAddDot(table[p2-1], scaleRelief); + ReliefAddDot(table[p1-1], scaleRelief); + bWaitFaceX = TRUE; + } + } + + } + + free(table); + fclose(file); + return TRUE; +} + + +// Ajuste une position pour qu'elle ne dépasse pas les limites. + +void CTerrain::LimitPos(D3DVECTOR &pos) +{ + float dim; + +#if _TEEN + dim = (m_mosaic*m_brick*m_size)/2.0f*0.98f; +#else + dim = (m_mosaic*m_brick*m_size)/2.0f*0.92f; +#endif + + if ( pos.x < -dim ) pos.x = -dim; + if ( pos.x > dim ) pos.x = dim; + if ( pos.z < -dim ) pos.z = -dim; + if ( pos.z > dim ) pos.z = dim; +} + + +// Ajuste les bords de chaque mosaïque pour être compatible +// avec toutes les résolutions inférieures. + +void CTerrain::AdjustRelief() +{ + int x, y, xx, yy, ii, b; + float level1, level2; + + if ( m_depth == 1 ) return; + + ii = m_mosaic*m_brick+1; + b = 1<<(m_depth-1); + + for ( y=0 ; y= 0 && x <= m_mosaic*m_brick && + y >= 0 && y <= m_mosaic*m_brick ) + { + p.y = m_relief[x+y*(m_mosaic*m_brick+1)]; + } + else + { + p.y = 0.0f; + } + + return p; +} + +// Calcule un vertex du terrain. +// Calcule une normale adoucie, en tenant compte des 6 triangles +// adjacents : +// +// ^ y +// | +// b---c---+ +// |\ |\ | +// | \| \| +// a---o---d +// |\ |\ | +// | \| \| +// +---f---e--> x + +D3DVERTEX2 CTerrain::RetVertex(int x, int y, int step) +{ + D3DVERTEX2 v; + D3DVECTOR o, oo, a,b,c,d,e,f, n, s; + int brick; + + o = RetVector(x, y); + v.x = o.x; + v.y = o.y; + v.z = o.z; + + a = RetVector(x-step, y ); + b = RetVector(x-step, y+step); + c = RetVector(x, y+step); + d = RetVector(x+step, y ); + e = RetVector(x+step, y-step); + f = RetVector(x, y-step); + + s = D3DVECTOR(0.0f, 0.0f, 0.0f); + + if ( x-step >= 0 && y+step <= m_mosaic*m_brick+1 ) + { + s += ComputeNormal(b,a,o); + s += ComputeNormal(c,b,o); + } + + if ( x+step <= m_mosaic*m_brick+1 && y+step <= m_mosaic*m_brick+1 ) + { + s += ComputeNormal(d,c,o); + } + + if ( x+step <= m_mosaic*m_brick+1 && y-step >= 0 ) + { + s += ComputeNormal(e,d,o); + s += ComputeNormal(f,e,o); + } + + if ( x-step >= 0 && y-step >= 0 ) + { + s += ComputeNormal(a,f,o); + } + + s = Normalize(s); + v.nx = s.x; + v.ny = s.y; + v.nz = s.z; + + if ( m_bMultiText ) + { + brick = m_brick/m_subdivMapping; + oo = RetVector((x/brick)*brick, (y/brick)*brick); + o = RetVector(x, y); + v.tu = (o.x-oo.x)*m_scaleMapping*m_subdivMapping; + v.tv = 1.0f - (o.z-oo.z)*m_scaleMapping*m_subdivMapping; + } + else + { + v.tu = o.x*m_scaleMapping; + v.tv = o.z*m_scaleMapping; + } + + return v; +} + +// Crée tous les objets d'une mosaïque. +// L'origine d'une mosaïque est son centre. +// +// ^ z +// | +// | 2---4---6-- +// | |\ |\ |\ +// | | \| \| +// | 1---3---5--- ... +// | +// +-------------------> x + +BOOL CTerrain::CreateMosaic(int ox, int oy, int step, int objRank, + const D3DMATERIAL7 &mat, + float min, float max) +{ + D3DMATRIX transform; + D3DVERTEX2 o, p1, p2; + D3DObjLevel6* buffer; + FPOINT uv; + int brick, total, size, mx, my, x, y, xx, yy, i; + char texName1[20]; + char texName2[20]; + float pixel, dp; + + if ( step == 1 && m_engine->RetGroundSpot() ) + { + i = (ox/5) + (oy/5)*(m_mosaic/5); + sprintf(texName2, "shadow%.2d.tga", i); + } + else + { + texName2[0] = 0; + } + + brick = m_brick/m_subdivMapping; + + o = RetVertex(ox*m_brick+m_brick/2, oy*m_brick+m_brick/2, step); + total = ((brick/step)+1)*2; + size = sizeof(D3DObjLevel6)+sizeof(D3DVERTEX2)*(total-1); + + pixel = 1.0f/256.0f; // 1 pixel de recouvrement (*) +//? dp = 0.5f/512.0f; + dp = 1.0f/512.0f; + + for ( my=0 ; mytotalPossible = total; + buffer->totalUsed = total; + buffer->type = D3DTYPE6S; + buffer->material = mat; + if ( m_bMultiText ) + { +//? buffer->state = D3DSTATENORMAL; + buffer->state = D3DSTATEWRAP; + } + else + { + buffer->state = D3DSTATEWRAP; + } + buffer->state |= D3DSTATESECOND; + if ( step == 1 ) + { + buffer->state |= D3DSTATEDUALb; + } + i = 0; + for ( x=0 ; x<=brick ; x+=step ) + { + p1 = RetVertex(ox*m_brick+mx*brick+x, oy*m_brick+my*brick+y+0 , step); + p2 = RetVertex(ox*m_brick+mx*brick+x, oy*m_brick+my*brick+y+step, step); + p1.x -= o.x; p1.z -= o.z; + p2.x -= o.x; p2.z -= o.z; + + if ( m_bMultiText ) + { + if ( x == 0 ) + { + p1.tu = 0.0f+(0.5f/256.0f); + p2.tu = 0.0f+(0.5f/256.0f); + } + if ( x == brick ) + { + p1.tu = 1.0f-(0.5f/256.0f); + p2.tu = 1.0f-(0.5f/256.0f); + } + if ( y == 0 ) + { + p1.tv = 1.0f-(0.5f/256.0f); + } + if ( y == brick-step ) + { + p2.tv = 0.0f+(0.5f/256.0f); + } + } + + if ( m_bLevelText ) + { + p1.tu /= m_subdivMapping; // 0..1 -> 0..0.25 + p1.tv /= m_subdivMapping; + p2.tu /= m_subdivMapping; + p2.tv /= m_subdivMapping; + + if ( x == 0 ) + { + p1.tu = 0.0f+dp; + p2.tu = 0.0f+dp; + } + if ( x == brick ) + { + p1.tu = (1.0f/m_subdivMapping)-dp; + p2.tu = (1.0f/m_subdivMapping)-dp; + } + if ( y == 0 ) + { + p1.tv = (1.0f/m_subdivMapping)-dp; + } + if ( y == brick-step ) + { + p2.tv = 0.0f+dp; + } + + p1.tu += uv.x; + p1.tv += uv.y; + p2.tu += uv.x; + p2.tv += uv.y; + } + +#if 1 + xx = mx*(m_brick/m_subdivMapping) + x; + yy = my*(m_brick/m_subdivMapping) + y; + p1.tu2 = ((float)(ox%5)*m_brick+xx+0.0f)/(m_brick*5); + p1.tv2 = ((float)(oy%5)*m_brick+yy+0.0f)/(m_brick*5); + p2.tu2 = ((float)(ox%5)*m_brick+xx+0.0f)/(m_brick*5); + p2.tv2 = ((float)(oy%5)*m_brick+yy+1.0f)/(m_brick*5); + + // Correction pour 1 pixel de recouvrement (*). + p1.tu2 = (p1.tu2+pixel)*(1.0f-pixel)/(1.0f+pixel); + p1.tv2 = (p1.tv2+pixel)*(1.0f-pixel)/(1.0f+pixel); + p2.tu2 = (p2.tu2+pixel)*(1.0f-pixel)/(1.0f+pixel); + p2.tv2 = (p2.tv2+pixel)*(1.0f-pixel)/(1.0f+pixel); +#endif + + buffer->vertex[i++] = p1; + buffer->vertex[i++] = p2; + } + m_engine->AddQuick(objRank, buffer, texName1, texName2, min, max, TRUE); + } + } + } + + D3DUtil_SetIdentityMatrix(transform); + transform._41 = o.x; + transform._43 = o.z; + m_engine->SetObjectTransform(objRank, transform); + + return TRUE; +} + +// (*) Il y a 1 pixel de recouvrement autour de chacune des 16 surfaces : +// +// |<--------------256-------------->| +// | |<----------254---------->| | +// |---|---|---|-- ... --|---|---|---| +// | 0.0 1.0 | +// | | | | +// 0.0 min max 1.0 +// +// Les coordonnées u-v utilisées pour le texturage sont comprises +// entre min et max (au lieu de 0 et 1). Ceci permet d'exclure les +// pixels situés dans une marge d'un pixel tout autour de la surface. + + +// Cherche un matériaux d'après son identificateur. + +TerrainMaterial* CTerrain::LevelSearchMat(int id) +{ + int i; + + for ( i=0 ; itexName); + strcpy(name, tm->texName); + uv.x = tm->u; + uv.y = tm->v; + } +} + +// Retourne la hauteur du terrain. + +float CTerrain::LevelRetHeight(int x, int y) +{ + int size; + + size = (m_mosaic*m_brick+1); + + if ( x < 0 ) x = 0; + if ( x >= size ) x = size-1; + if ( y < 0 ) y = 0; + if ( y >= size ) y = size-1; + + return m_relief[x+y*size]; +} + +// Décide si un point utilise le matériaux. + +BOOL CTerrain::LevelGetDot(int x, int y, float min, float max, float slope) +{ + float hc, h[4]; + int i; + + hc = LevelRetHeight(x, y); + h[0] = LevelRetHeight(x+0, y+1); + h[1] = LevelRetHeight(x+1, y+0); + h[2] = LevelRetHeight(x+0, y-1); + h[3] = LevelRetHeight(x-1, y+0); + + if ( hc < min || + hc > max ) return FALSE; + + if ( slope == 0.0f ) + { + return TRUE; + } + + if ( slope > 0.0f ) + { + for ( i=0 ; i<4 ; i++ ) + { + if ( Abs(hc-h[i]) >= slope ) + { + return FALSE; + } + } + return TRUE; + } + + if ( slope < 0.0f ) + { + for ( i=0 ; i<4 ; i++ ) + { + if ( Abs(hc-h[i]) < -slope ) + { + return FALSE; + } + } + return TRUE; + } + + return FALSE; +} + +// Cherche si un matériau existe. +// Retourne l'index dans m_levelMat ou -1 s'il n'existe pas. +// m_levelMat[i].id donne l'identificateur. + +int CTerrain::LevelTestMat(char *mat) +{ + int i; + + for ( i=0 ; imat[0] != mat[0] || + tm->mat[1] != mat[1] || + tm->mat[2] != mat[2] || + tm->mat[3] != mat[3] ) // id incompatible avec mat ? + { + ii = LevelTestMat(mat); + if ( ii == -1 ) return; + id = m_levelMat[ii].id; // cherche un id compatible avec mat + } + + // Modifie le point. + m_levelDot[x+y*m_levelDotSize].id = id; + m_levelDot[x+y*m_levelDotSize].mat[0] = mat[0]; + m_levelDot[x+y*m_levelDotSize].mat[1] = mat[1]; + m_levelDot[x+y*m_levelDotSize].mat[2] = mat[2]; + m_levelDot[x+y*m_levelDotSize].mat[3] = mat[3]; + + // Modifie le voisin inférieur. + if ( (x+0) >= 0 && (x+0) < m_levelDotSize && + (y-1) >= 0 && (y-1) < m_levelDotSize ) + { + i = (x+0)+(y-1)*m_levelDotSize; + if ( m_levelDot[i].mat[0] != mat[2] ) + { + m_levelDot[i].mat[0] = mat[2]; + ii = LevelTestMat(m_levelDot[i].mat); + if ( ii != -1 ) + { + m_levelDot[i].id = m_levelMat[ii].id; + } + } + } + + // Modifie le voisin gauche. + if ( (x-1) >= 0 && (x-1) < m_levelDotSize && + (y+0) >= 0 && (y+0) < m_levelDotSize ) + { + i = (x-1)+(y+0)*m_levelDotSize; + if ( m_levelDot[i].mat[1] != mat[3] ) + { + m_levelDot[i].mat[1] = mat[3]; + ii = LevelTestMat(m_levelDot[i].mat); + if ( ii != -1 ) + { + m_levelDot[i].id = m_levelMat[ii].id; + } + } + } + + // Modifie le voisin supérieur. + if ( (x+0) >= 0 && (x+0) < m_levelDotSize && + (y+1) >= 0 && (y+1) < m_levelDotSize ) + { + i = (x+0)+(y+1)*m_levelDotSize; + if ( m_levelDot[i].mat[2] != mat[0] ) + { + m_levelDot[i].mat[2] = mat[0]; + ii = LevelTestMat(m_levelDot[i].mat); + if ( ii != -1 ) + { + m_levelDot[i].id = m_levelMat[ii].id; + } + } + } + + // Modifie le voisin droite. + if ( (x+1) >= 0 && (x+1) < m_levelDotSize && + (y+0) >= 0 && (y+0) < m_levelDotSize ) + { + i = (x+1)+(y+0)*m_levelDotSize; + if ( m_levelDot[i].mat[3] != mat[1] ) + { + m_levelDot[i].mat[3] = mat[1]; + ii = LevelTestMat(m_levelDot[i].mat); + if ( ii != -1 ) + { + m_levelDot[i].id = m_levelMat[ii].id; + } + } + } +} + +// Teste si un matériau est possible à un endroit donné, en fonction +// de ses 4 voisins. Si oui, met le point. + +BOOL CTerrain::LevelIfDot(int x, int y, int id, char *mat) +{ + char test[4]; + + // Compatible avec voisin inférieur ? + if ( x+0 >= 0 && x+0 < m_levelDotSize && + y-1 >= 0 && y-1 < m_levelDotSize ) + { + test[0] = mat[2]; + test[1] = m_levelDot[(x+0)+(y-1)*m_levelDotSize].mat[1]; + test[2] = m_levelDot[(x+0)+(y-1)*m_levelDotSize].mat[2]; + test[3] = m_levelDot[(x+0)+(y-1)*m_levelDotSize].mat[3]; + + if ( LevelTestMat(test) == -1 ) return FALSE; + } + + // Compatible avec voisin gauche ? + if ( x-1 >= 0 && x-1 < m_levelDotSize && + y+0 >= 0 && y+0 < m_levelDotSize ) + { + test[0] = m_levelDot[(x-1)+(y+0)*m_levelDotSize].mat[0]; + test[1] = mat[3]; + test[2] = m_levelDot[(x-1)+(y+0)*m_levelDotSize].mat[2]; + test[3] = m_levelDot[(x-1)+(y+0)*m_levelDotSize].mat[3]; + + if ( LevelTestMat(test) == -1 ) return FALSE; + } + + // Compatible avec voisin supérieur ? + if ( x+0 >= 0 && x+0 < m_levelDotSize && + y+1 >= 0 && y+1 < m_levelDotSize ) + { + test[0] = m_levelDot[(x+0)+(y+1)*m_levelDotSize].mat[0]; + test[1] = m_levelDot[(x+0)+(y+1)*m_levelDotSize].mat[1]; + test[2] = mat[0]; + test[3] = m_levelDot[(x+0)+(y+1)*m_levelDotSize].mat[3]; + + if ( LevelTestMat(test) == -1 ) return FALSE; + } + + // Compatible avec voisin droite ? + if ( x+1 >= 0 && x+1 < m_levelDotSize && + y+0 >= 0 && y+0 < m_levelDotSize ) + { + test[0] = m_levelDot[(x+1)+(y+0)*m_levelDotSize].mat[0]; + test[1] = m_levelDot[(x+1)+(y+0)*m_levelDotSize].mat[1]; + test[2] = m_levelDot[(x+1)+(y+0)*m_levelDotSize].mat[2]; + test[3] = mat[1]; + + if ( LevelTestMat(test) == -1 ) return FALSE; + } + + LevelSetDot(x, y, id, mat); // met le point + return TRUE; +} + +// Modifie l'état d'un point. + +BOOL CTerrain::LevelPutDot(int x, int y, int id) +{ + TerrainMaterial *tm; + char mat[4]; + int up, right, down, left; + + x /= m_brick/m_subdivMapping; + y /= m_brick/m_subdivMapping; + + if ( x < 0 || x >= m_levelDotSize || + y < 0 || y >= m_levelDotSize ) return FALSE; + + tm = LevelSearchMat(id); + if ( tm == 0 ) return FALSE; + + // Essaye sans modifier les voisins. + if ( LevelIfDot(x, y, id, tm->mat) ) return TRUE; + + // Essaye en modifiant un seul voisin (4x). + for ( up=0 ; upmat[1]; + mat[2] = tm->mat[2]; + mat[3] = tm->mat[3]; + + if ( LevelIfDot(x, y, id, mat) ) return TRUE; + } + + for ( right=0 ; rightmat[0]; + mat[1] = right; + mat[2] = tm->mat[2]; + mat[3] = tm->mat[3]; + + if ( LevelIfDot(x, y, id, mat) ) return TRUE; + } + + for ( down=0 ; downmat[0]; + mat[1] = tm->mat[1]; + mat[2] = down; + mat[3] = tm->mat[3]; + + if ( LevelIfDot(x, y, id, mat) ) return TRUE; + } + + for ( left=0 ; leftmat[0]; + mat[1] = tm->mat[1]; + mat[2] = tm->mat[2]; + mat[3] = left; + + if ( LevelIfDot(x, y, id, mat) ) return TRUE; + } + + // Essaye en modifiant deux voisins (6x). + for ( up=0 ; upmat[1]; + mat[2] = down; + mat[3] = tm->mat[3]; + + if ( LevelIfDot(x, y, id, mat) ) return TRUE; + } + } + + for ( right=0 ; rightmat[0]; + mat[1] = right; + mat[2] = tm->mat[2]; + mat[3] = left; + + if ( LevelIfDot(x, y, id, mat) ) return TRUE; + } + } + + for ( up=0 ; upmat[2]; + mat[3] = tm->mat[3]; + + if ( LevelIfDot(x, y, id, mat) ) return TRUE; + } + } + + for ( right=0 ; rightmat[0]; + mat[1] = right; + mat[2] = down; + mat[3] = tm->mat[3]; + + if ( LevelIfDot(x, y, id, mat) ) return TRUE; + } + } + + for ( down=0 ; downmat[0]; + mat[1] = tm->mat[1]; + mat[2] = down; + mat[3] = left; + + if ( LevelIfDot(x, y, id, mat) ) return TRUE; + } + } + + for ( up=0 ; upmat[1]; + mat[2] = tm->mat[2]; + mat[3] = left; + + if ( LevelIfDot(x, y, id, mat) ) return TRUE; + } + } + + // Essaye en modifiant tous les voisins. + for ( up=0 ; upmat[j]; + } + } + + return TRUE; +} + +// Génère un niveau dans le terrain. + +BOOL CTerrain::LevelGenerate(int *id, float min, float max, + float slope, float freq, + D3DVECTOR center, float radius) +{ + TerrainMaterial *tm; + D3DVECTOR pos; + int i, numID, x, y, xx, yy, group, rnd; + float dim; + + static char random[100] = + { + 84,25,12, 6,34,52,85,38,97,16, + 21,31,65,19,62,40,72,22,48,61, + 56,47, 8,53,73,77, 4,91,26,88, + 76, 1,44,93,39,11,71,17,98,95, + 88,83,18,30, 3,57,28,49,74, 9, + 32,13,96,66,15,70,36,10,59,94, + 45,86, 2,29,63,42,51, 0,79,27, + 54, 7,20,69,89,23,64,43,81,92, + 90,33,46,14,67,35,50, 5,87,60, + 68,55,24,78,41,75,58,80,37,82, + }; + + i = 0; + while ( id[i] != 0 ) + { + tm = LevelSearchMat(id[i++]); + if ( tm == 0 ) return FALSE; + } + numID = i; + + group = m_brick/m_subdivMapping; + + if ( radius > 0.0f && radius < 5.0f ) // juste un carré ? + { + dim = (m_mosaic*m_brick*m_size)/2.0f; + + xx = (int)((center.x+dim)/m_size); + yy = (int)((center.z+dim)/m_size); + + x = xx/group; + y = yy/group; + + tm = LevelSearchMat(id[0]); + if ( tm != 0 ) + { + LevelSetDot(x, y, id[0], tm->mat); // met le point + } +//? LevelPutDot(xx,yy, id[0]); + } + else + { + for ( y=0 ; y radius ) continue; + } + + if ( freq < 100.0f ) + { + rnd = random[(x%10)+(y%10)*10]; + if ( (float)rnd > freq ) continue; + } + + xx = x*group + group/2; + yy = y*group + group/2; + + if ( LevelGetDot(xx,yy, min, max, slope) ) + { + rnd = random[(x%10)+(y%10)*10]; + i = rnd%numID; + LevelPutDot(xx,yy, id[i]); + } + } + } + } + + return TRUE; +} + +// Initialise une table des niveaux vide. + +void CTerrain::LevelOpenTable() +{ + int i, j; + + if ( !m_bLevelText ) return; + if ( m_levelDot != 0 ) return; // déjà alloué + + m_levelDotSize = (m_mosaic*m_brick)/(m_brick/m_subdivMapping)+1; + m_levelDot = (DotLevel*)malloc(m_levelDotSize*m_levelDotSize*sizeof(DotLevel)); + + for ( i=0 ; iCreateObject(); + m_engine->SetObjectType(objRank, TYPETERRAIN); // c'est un terrain + + m_objRank[x+y*m_mosaic] = objRank; + + if ( bMultiRes ) + { + min = 0.0f; + max = m_vision; + max *= m_engine->RetClippingDistance(); + for ( step=0 ; step tp2.x ) + { + x = tp1.x; + tp1.x = tp2.x; + tp2.x = x; + } + + if ( tp1.y > tp2.y ) + { + y = tp1.y; + tp1.y = tp2.y; + tp2.y = y; + } + + size = (m_mosaic*m_brick)+1; + + // Calcule la hauteur moyenne actuelle. + avg = 0.0f; + nb = 0; + for ( y=tp1.y ; y<=tp2.y ; y++ ) + { + for ( x=tp1.x ; x<=tp2.x ; x++ ) + { + avg += m_relief[x+y*size]; + nb ++; + } + } + avg /= (float)nb; + + // Modifie la description du relief. + for ( y=tp1.y ; y<=tp2.y ; y++ ) + { + for ( x=tp1.x ; x<=tp2.x ; x++ ) + { + m_relief[x+y*size] = avg+height; + + if ( x%m_brick == 0 && y%m_depth != 0 ) + { + m_relief[(x+0)+(y-1)*size] = avg+height; + m_relief[(x+0)+(y+1)*size] = avg+height; + } + + if ( y%m_brick == 0 && x%m_depth != 0 ) + { + m_relief[(x-1)+(y+0)*size] = avg+height; + m_relief[(x+1)+(y+0)*size] = avg+height; + } + } + } + AdjustRelief(); + + pp1.x = (tp1.x-2)/m_brick; + pp1.y = (tp1.y-2)/m_brick; + pp2.x = (tp2.x+1)/m_brick; + pp2.y = (tp2.y+1)/m_brick; + + if ( pp1.x < 0 ) pp1.x = 0; + if ( pp1.x >= m_mosaic ) pp1.x = m_mosaic-1; + if ( pp1.y < 0 ) pp1.y = 0; + if ( pp1.y >= m_mosaic ) pp1.y = m_mosaic-1; + + for ( y=pp1.y ; y<=pp2.y ; y++ ) + { + for ( x=pp1.x ; x<=pp2.x ; x++ ) + { + m_engine->DeleteObject(m_objRank[x+y*m_mosaic]); + CreateSquare(m_bMultiText, x, y); // recrée le carré + } + } + m_engine->Update(); + + return TRUE; +} + + +// Gestion du vent. + +void CTerrain::SetWind(D3DVECTOR speed) +{ + m_wind = speed; +} + +D3DVECTOR CTerrain::RetWind() +{ + return m_wind; +} + + +// Donne la pente exacte du terrain à un endroit donné. + +float CTerrain::RetFineSlope(const D3DVECTOR &pos) +{ + D3DVECTOR n; + + if ( !GetNormal(n, pos) ) return 0.0f; + return Abs(RotateAngle(Length(n.x, n.z), n.y)-PI/2.0f); +} + +// Donne la pente approximative du terrain à un endroit donné. + +float CTerrain::RetCoarseSlope(const D3DVECTOR &pos) +{ + float dim, level[4], min, max; + int x, y; + + if ( m_relief == 0 ) return 0.0f; + + dim = (m_mosaic*m_brick*m_size)/2.0f; + + x = (int)((pos.x+dim)/m_size); + y = (int)((pos.z+dim)/m_size); + + if ( x < 0 || x >= m_mosaic*m_brick || + y < 0 || y >= m_mosaic*m_brick ) return 0.0f; + + level[0] = m_relief[(x+0)+(y+0)*(m_mosaic*m_brick+1)]; + level[1] = m_relief[(x+1)+(y+0)*(m_mosaic*m_brick+1)]; + level[2] = m_relief[(x+0)+(y+1)*(m_mosaic*m_brick+1)]; + level[3] = m_relief[(x+1)+(y+1)*(m_mosaic*m_brick+1)]; + + min = Min(level[0], level[1], level[2], level[3]); + max = Max(level[0], level[1], level[2], level[3]); + + return atanf((max-min)/m_size); +} + +// Donne le vecteur normal à la position p(x,-,z) du terrain. + +BOOL CTerrain::GetNormal(D3DVECTOR &n, const D3DVECTOR &p) +{ + D3DVECTOR p1, p2, p3, p4; + float dim; + int x, y; + + dim = (m_mosaic*m_brick*m_size)/2.0f; + + x = (int)((p.x+dim)/m_size); + y = (int)((p.z+dim)/m_size); + + if ( x < 0 || x > m_mosaic*m_brick || + y < 0 || y > m_mosaic*m_brick ) return FALSE; + + p1 = RetVector(x+0, y+0); + p2 = RetVector(x+1, y+0); + p3 = RetVector(x+0, y+1); + p4 = RetVector(x+1, y+1); + + if ( Abs(p.z-p2.z) < Abs(p.x-p2.x) ) + { + n = ComputeNormal(p1,p2,p3); + } + else + { + n = ComputeNormal(p2,p4,p3); + } + return TRUE; +} + +// Retourne la hauteur du sol. + +float CTerrain::RetFloorLevel(const D3DVECTOR &p, BOOL bBrut, BOOL bWater) +{ + D3DVECTOR p1, p2, p3, p4, ps; + float dim, level; + int x, y; + + dim = (m_mosaic*m_brick*m_size)/2.0f; + + x = (int)((p.x+dim)/m_size); + y = (int)((p.z+dim)/m_size); + + if ( x < 0 || x > m_mosaic*m_brick || + y < 0 || y > m_mosaic*m_brick ) return FALSE; + + p1 = RetVector(x+0, y+0); + p2 = RetVector(x+1, y+0); + p3 = RetVector(x+0, y+1); + p4 = RetVector(x+1, y+1); + + ps = p; + if ( Abs(p.z-p2.z) < Abs(p.x-p2.x) ) + { + if ( !IntersectY(p1, p2, p3, ps) ) return 0.0f; + } + else + { + if ( !IntersectY(p2, p4, p3, ps) ) return 0.0f; + } + + if ( !bBrut ) AdjustBuildingLevel(ps); + + if ( bWater ) // ne va pas sous l'eau ? + { + level = m_water->RetLevel(); + if ( ps.y < level ) ps.y = level; // pas sous l'eau + } + + return ps.y; +} + +// Retourne la hauteur jusqu'au sol. Cette hauteur est positive +// lorsqu'on est au-dessus du sol. + +float CTerrain::RetFloorHeight(const D3DVECTOR &p, BOOL bBrut, BOOL bWater) +{ + D3DVECTOR p1, p2, p3, p4, ps; + float dim, level; + int x, y; + + dim = (m_mosaic*m_brick*m_size)/2.0f; + + x = (int)((p.x+dim)/m_size); + y = (int)((p.z+dim)/m_size); + + if ( x < 0 || x > m_mosaic*m_brick || + y < 0 || y > m_mosaic*m_brick ) return FALSE; + + p1 = RetVector(x+0, y+0); + p2 = RetVector(x+1, y+0); + p3 = RetVector(x+0, y+1); + p4 = RetVector(x+1, y+1); + + ps = p; + if ( Abs(p.z-p2.z) < Abs(p.x-p2.x) ) + { + if ( !IntersectY(p1, p2, p3, ps) ) return 0.0f; + } + else + { + if ( !IntersectY(p2, p4, p3, ps) ) return 0.0f; + } + + if ( !bBrut ) AdjustBuildingLevel(ps); + + if ( bWater ) // ne va pas sous l'eau ? + { + level = m_water->RetLevel(); + if ( ps.y < level ) ps.y = level; // pas sous l'eau + } + + return p.y-ps.y; +} + +// Modifie la coordonnée "y" du point "p" pour qu'il repose +// sur le sol du terrain. + +BOOL CTerrain::MoveOnFloor(D3DVECTOR &p, BOOL bBrut, BOOL bWater) +{ + D3DVECTOR p1, p2, p3, p4; + float dim, level; + int x, y; + + dim = (m_mosaic*m_brick*m_size)/2.0f; + + x = (int)((p.x+dim)/m_size); + y = (int)((p.z+dim)/m_size); + + if ( x < 0 || x > m_mosaic*m_brick || + y < 0 || y > m_mosaic*m_brick ) return FALSE; + + p1 = RetVector(x+0, y+0); + p2 = RetVector(x+1, y+0); + p3 = RetVector(x+0, y+1); + p4 = RetVector(x+1, y+1); + + if ( Abs(p.z-p2.z) < Abs(p.x-p2.x) ) + { + if ( !IntersectY(p1, p2, p3, p) ) return FALSE; + } + else + { + if ( !IntersectY(p2, p4, p3, p) ) return FALSE; + } + + if ( !bBrut ) AdjustBuildingLevel(p); + + if ( bWater ) // ne va pas sous l'eau ? + { + level = m_water->RetLevel(); + if ( p.y < level ) p.y = level; // pas sous l'eau + } + + return TRUE; +} + +// Modifie une coordonnée pour qu'elle soit sur le terrain. +// Retourne FALSE si la coordonnée initiale était trop loin. + +BOOL CTerrain::ValidPosition(D3DVECTOR &p, float marging) +{ + BOOL bOK = TRUE; + float limit; + + limit = m_mosaic*m_brick*m_size/2.0f - marging; + + if ( p.x < -limit ) + { + p.x = -limit; + bOK = FALSE; + } + + if ( p.z < -limit ) + { + p.z = -limit; + bOK = FALSE; + } + + if ( p.x > limit ) + { + p.x = limit; + bOK = FALSE; + } + + if ( p.z > limit ) + { + p.z = limit; + bOK = FALSE; + } + + return bOK; +} + + + +// Vide la table des élévations. + +void CTerrain::FlushBuildingLevel() +{ + m_buildingUsed = 0; +} + +// Ajoute une nouvelle élévation pour un batiment. + +BOOL CTerrain::AddBuildingLevel(D3DVECTOR center, float min, float max, + float height, float factor) +{ + int i; + + for ( i=0 ; i= MAXBUILDINGLEVEL ) return FALSE; + i = m_buildingUsed++; + + update: + m_buildingTable[i].center = center; + m_buildingTable[i].min = min; + m_buildingTable[i].max = max; + m_buildingTable[i].level = RetFloorLevel(center, TRUE); + m_buildingTable[i].height = height; + m_buildingTable[i].factor = factor; + m_buildingTable[i].bboxMinX = center.x-max; + m_buildingTable[i].bboxMaxX = center.x+max; + m_buildingTable[i].bboxMinZ = center.z-max; + m_buildingTable[i].bboxMaxZ = center.z+max; + + return TRUE; +} + +// Met à jour l'élévation pour un batiment lorsqu'il a été déplacé +// en hauteur (suite à un terraformage). + +BOOL CTerrain::UpdateBuildingLevel(D3DVECTOR center) +{ + int i; + + for ( i=0 ; i m_buildingTable[i].bboxMaxX || + p.z < m_buildingTable[i].bboxMinZ || + p.z > m_buildingTable[i].bboxMaxZ ) continue; + + dist = Length2d(p, m_buildingTable[i].center); + + if ( dist <= m_buildingTable[i].max ) + { + return m_buildingTable[i].factor; + } + } + return 1.0f; // on est sur le sol normnal +} + +// Ajuste une position en fonction d'une élévation éventuelle. + +void CTerrain::AdjustBuildingLevel(D3DVECTOR &p) +{ + D3DVECTOR border; + float dist, base; + int i; + + for ( i=0 ; i m_buildingTable[i].bboxMaxX || + p.z < m_buildingTable[i].bboxMinZ || + p.z > m_buildingTable[i].bboxMaxZ ) continue; + + dist = Length2d(p, m_buildingTable[i].center); + + if ( dist > m_buildingTable[i].max ) continue; + + if ( dist < m_buildingTable[i].min ) + { + p.y = m_buildingTable[i].level+m_buildingTable[i].height; + return; + } + +#if 0 + p.y = m_buildingTable[i].level; + p.y += (m_buildingTable[i].max-dist)/ + (m_buildingTable[i].max-m_buildingTable[i].min)* + m_buildingTable[i].height; + + base = RetFloorLevel(p, TRUE); + if ( p.y < base ) p.y = base; +#else + border.x = ((p.x-m_buildingTable[i].center.x)*m_buildingTable[i].max)/ + dist+m_buildingTable[i].center.x; + border.z = ((p.z-m_buildingTable[i].center.z)*m_buildingTable[i].max)/ + dist+m_buildingTable[i].center.z; + + base = RetFloorLevel(border, TRUE); + + p.y = (m_buildingTable[i].max-dist)/ + (m_buildingTable[i].max-m_buildingTable[i].min)* + (m_buildingTable[i].level+m_buildingTable[i].height-base)+ + base; +#endif + return; + } +} + + +// Retourne la dureté du terrain à un endroit donné. +// La dureté détermine le bruit (SOUND_STEP et SOUND_BOUM). + +float CTerrain::RetHardness(const D3DVECTOR &p) +{ + TerrainMaterial* tm; + float factor, dim; + int x, y, id; + + factor = RetBuildingFactor(p); + if ( factor != 1.0f ) return 1.0f; // sur bâtiment + + if ( m_levelDot == 0 ) return m_defHardness; + + dim = (m_mosaic*m_brick*m_size)/2.0f; + + x = (int)((p.x+dim)/m_size); + y = (int)((p.z+dim)/m_size); + + if ( x < 0 || x > m_mosaic*m_brick || + y < 0 || y > m_mosaic*m_brick ) return m_defHardness; + + x /= m_brick/m_subdivMapping; + y /= m_brick/m_subdivMapping; + + if ( x < 0 || x >= m_levelDotSize || + y < 0 || y >= m_levelDotSize ) return m_defHardness; + + id = m_levelDot[x+y*m_levelDotSize].id; + tm = LevelSearchMat(id); + if ( tm == 0 ) return m_defHardness; + + return tm->hardness; +} + + +// Montre les zones plates sur le terrain. + +void CTerrain::GroundFlat(D3DVECTOR pos) +{ + D3DVECTOR p; + float rapport, angle; + int x, y, i; + static char table[41*41]; + + + rapport = 3200.0f/1024.0f; + + for ( y=0 ; y<=40 ; y++ ) + { + for ( x=0 ; x<=40 ; x++ ) + { + i = x + y*41; + table[i] = 0; + + p.x = (x-20)*rapport; + p.z = (y-20)*rapport; + p.y = 0.0f; + if ( Length(p.x, p.y) > 20.0f*rapport ) continue; + + angle = RetFineSlope(pos+p); + + if ( angle < FLATLIMIT ) + { + table[i] = 1; + } + else + { + table[i] = 2; + } + } + } + + m_engine->GroundMarkCreate(pos, 40.0f, 0.001f, 15.0f, 0.001f, 41, 41, table); +} + + +// Calcule le rayon de la plus grande zone platte disponible. +// Ce calcul n'est pas optimisé ! + +float CTerrain::RetFlatZoneRadius(D3DVECTOR center, float max) +{ + D3DVECTOR pos; + FPOINT c, p; + float ref, radius, angle, h; + int i, nb; + + angle = RetFineSlope(center); + if ( angle >= FLATLIMIT ) return 0.0f; + + ref = RetFloorLevel(center, TRUE); + + radius = 1.0f; + while ( radius <= max ) + { + angle = 0.0f; + nb = (int)(2.0f*PI*radius); + if ( nb < 8 ) nb = 8; + for ( i=0 ; i 1.0f ) return radius; + + angle += PI*2.0f/8.0f; + } + radius += 1.0f; + } + return max; +} + + + +// Spécifie la hauteur maximale de vol. + +void CTerrain::SetFlyingMaxHeight(float height) +{ + m_flyingMaxHeight = height; +} + +// Retourne la hauteur maximale de vol. + +float CTerrain::RetFlyingMaxHeight() +{ + return m_flyingMaxHeight; +} + + +// Vide la table des limites de vol. + +void CTerrain::FlushFlyingLimit() +{ + m_flyingMaxHeight = 280.0f; + m_flyingLimitTotal = 0; +} + +// Vide la table des limites de vol. + +BOOL CTerrain::AddFlyingLimit(D3DVECTOR center, + float extRadius, float intRadius, + float maxHeight) +{ + int i; + + if ( m_flyingLimitTotal >= MAXFLYINGLIMIT ) return FALSE; + + i = m_flyingLimitTotal; + m_flyingLimit[i].center = center; + m_flyingLimit[i].extRadius = extRadius; + m_flyingLimit[i].intRadius = intRadius; + m_flyingLimit[i].maxHeight = maxHeight; + m_flyingLimitTotal = i+1; + + return TRUE; +} + +// Retourne la hauteur maximale de vol. + +float CTerrain::RetFlyingLimit(D3DVECTOR pos, BOOL bNoLimit) +{ + float dist, h; + int i; + + if ( bNoLimit ) return 280.0f; + if ( m_flyingLimitTotal == 0 ) return m_flyingMaxHeight; + + for ( i=0 ; i= m_flyingLimit[i].extRadius ) continue; + + if ( dist <= m_flyingLimit[i].intRadius ) + { + return m_flyingLimit[i].maxHeight; + } + + dist -= m_flyingLimit[i].intRadius; + + h = dist*(m_flyingMaxHeight-m_flyingLimit[i].maxHeight)/ + (m_flyingLimit[i].extRadius-m_flyingLimit[i].intRadius); + + return h + m_flyingLimit[i].maxHeight; + } + + return m_flyingMaxHeight; +} + diff --git a/src/terrain.h b/src/terrain.h new file mode 100644 index 00000000..f8f74350 --- /dev/null +++ b/src/terrain.h @@ -0,0 +1,195 @@ +// terrain.h + +#ifndef _TERRAIN_H_ +#define _TERRAIN_H_ + + +class CInstanceManager; +class CD3DEngine; +class CWater; + + + +#define FLATLIMIT (5.0f*PI/180.0f) + + +enum TerrainRes +{ + TR_NULL = 0, + TR_STONE = 1, + TR_URANIUM = 2, + TR_POWER = 3, + TR_KEYa = 4, + TR_KEYb = 5, + TR_KEYc = 6, + TR_KEYd = 7, +}; + + +#define MAXBUILDINGLEVEL 100 + +typedef struct +{ + D3DVECTOR center; + float factor; + float min; + float max; + float level; + float height; + float bboxMinX; + float bboxMaxX; + float bboxMinZ; + float bboxMaxZ; +} +BuildingLevel; + + +#define MAXMATTERRAIN 100 + +typedef struct +{ + short id; + char texName[20]; + float u,v; + float hardness; + char mat[4]; // up, right, down, left +} +TerrainMaterial; + +typedef struct +{ + short id; + char mat[4]; // up, right, down, left +} +DotLevel; + + +#define MAXFLYINGLIMIT 10 + +typedef struct +{ + D3DVECTOR center; + float extRadius; + float intRadius; + float maxHeight; +} +FlyingLimit; + + + +class CTerrain +{ +public: + CTerrain(CInstanceManager* iMan); + ~CTerrain(); + + BOOL Generate(int mosaic, int brickP2, float size, float vision, int depth, float hardness); + BOOL InitTextures(char* baseName, int* table, int dx, int dy); + void LevelFlush(); + BOOL LevelMaterial(int id, char* baseName, float u, float v, int up, int right, int down, int left, float hardness); + BOOL LevelInit(int id); + BOOL LevelGenerate(int *id, float min, float max, float slope, float freq, D3DVECTOR center, float radius); + void FlushRelief(); + BOOL ReliefFromBMP(const char* filename, float scaleRelief, BOOL adjustBorder); + BOOL ReliefFromDXF(const char* filename, float scaleRelief); + BOOL ResFromBMP(const char* filename); + BOOL CreateObjects(BOOL bMultiRes); + BOOL Terraform(const D3DVECTOR &p1, const D3DVECTOR &p2, float height); + + void SetWind(D3DVECTOR speed); + D3DVECTOR RetWind(); + + float RetFineSlope(const D3DVECTOR &pos); + float RetCoarseSlope(const D3DVECTOR &pos); + BOOL GetNormal(D3DVECTOR &n, const D3DVECTOR &p); + float RetFloorLevel(const D3DVECTOR &p, BOOL bBrut=FALSE, BOOL bWater=FALSE); + float RetFloorHeight(const D3DVECTOR &p, BOOL bBrut=FALSE, BOOL bWater=FALSE); + BOOL MoveOnFloor(D3DVECTOR &p, BOOL bBrut=FALSE, BOOL bWater=FALSE); + BOOL ValidPosition(D3DVECTOR &p, float marging); + TerrainRes RetResource(const D3DVECTOR &p); + void LimitPos(D3DVECTOR &pos); + + void FlushBuildingLevel(); + BOOL AddBuildingLevel(D3DVECTOR center, float min, float max, float height, float factor); + BOOL UpdateBuildingLevel(D3DVECTOR center); + BOOL DeleteBuildingLevel(D3DVECTOR center); + float RetBuildingFactor(const D3DVECTOR &p); + float RetHardness(const D3DVECTOR &p); + + int RetMosaic(); + int RetBrick(); + float RetSize(); + float RetScaleRelief(); + + void GroundFlat(D3DVECTOR pos); + float RetFlatZoneRadius(D3DVECTOR center, float max); + + void SetFlyingMaxHeight(float height); + float RetFlyingMaxHeight(); + void FlushFlyingLimit(); + BOOL AddFlyingLimit(D3DVECTOR center, float extRadius, float intRadius, float maxHeight); + float RetFlyingLimit(D3DVECTOR pos, BOOL bNoLimit); + +protected: + BOOL ReliefAddDot(D3DVECTOR pos, float scaleRelief); + void AdjustRelief(); + D3DVECTOR RetVector(int x, int y); + D3DVERTEX2 RetVertex(int x, int y, int step); + BOOL CreateMosaic(int ox, int oy, int step, int objRank, const D3DMATERIAL7 &mat, float min, float max); + BOOL CreateSquare(BOOL bMultiRes, int x, int y); + + TerrainMaterial* LevelSearchMat(int id); + void LevelTextureName(int x, int y, char *name, FPOINT &uv); + float LevelRetHeight(int x, int y); + BOOL LevelGetDot(int x, int y, float min, float max, float slope); + int LevelTestMat(char *mat); + void LevelSetDot(int x, int y, int id, char *mat); + BOOL LevelIfDot(int x, int y, int id, char *mat); + BOOL LevelPutDot(int x, int y, int id); + void LevelOpenTable(); + void LevelCloseTable(); + + void AdjustBuildingLevel(D3DVECTOR &p); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + CWater* m_water; + + int m_mosaic; // nb de mosaïque + int m_brick; // nb de briques par mosaïque + float m_size; // taille d'un élément dans une brique + float m_vision; // vision avant un changement de résolution + float* m_relief; // table du relief + int* m_texture; // table des textures + int* m_objRank; // table des rangs des objets + BOOL m_bMultiText; + BOOL m_bLevelText; + float m_scaleMapping; // échelle du mapping + float m_scaleRelief; + int m_subdivMapping; + int m_depth; // nb de résolutions différentes (1,2,3,4) + char m_texBaseName[20]; + char m_texBaseExt[10]; + float m_defHardness; + + TerrainMaterial m_levelMat[MAXMATTERRAIN+1]; + int m_levelMatTotal; + int m_levelMatMax; + int m_levelDotSize; + DotLevel* m_levelDot; + int m_levelID; + + int m_buildingUsed; + BuildingLevel m_buildingTable[MAXBUILDINGLEVEL]; + + unsigned char* m_resources; + D3DVECTOR m_wind; // vitesse du vent + + float m_flyingMaxHeight; + int m_flyingLimitTotal; + FlyingLimit m_flyingLimit[MAXFLYINGLIMIT]; +}; + + +#endif //_TERRAIN_H_ diff --git a/src/text.cpp b/src/text.cpp new file mode 100644 index 00000000..12c6c372 --- /dev/null +++ b/src/text.cpp @@ -0,0 +1,1865 @@ +// text.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "language.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "text.h" + + + +static short table_text_colobot[] = +{ +// x1, y1, x2, y2 + 219,34, 225,50, // 0 + 1,188, 9,203, // . + 51,188,59,203, // carré + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 11,188,19,203, // \t + 21,188,29,203, // \n + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, // \r + 219,34, 225,50, + 219,34, 225,50, + 41,188,49,203, // > + 31,188,39,203, // < + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + +#if _NEWLOOK + 0, 0, 4, 16, // 32 + 7, 0, 9, 16, // ! + 9, 0, 13, 16, // " + 13, 0, 24, 16, // # + 24, 0, 31, 16, // $ + 31, 0, 43, 16, // % + 43, 0, 54, 16, // & + 54, 0, 56, 16, // ' + 56, 0, 61, 16, // ( + 61, 0, 67, 16, // ) + 67, 0, 73, 16, // * + 73, 0, 83, 16, // + + 83, 0, 87, 16, // , + 87, 0, 92, 16, // - + 92, 0, 94, 16, // . + 94, 0, 101,16, // / + 101,0, 109,16, // 0 + 109,0, 114,16, // 1 + 114,0, 122,16, // 2 + 122,0, 129,16, // 3 + 129,0, 138,16, // 4 + 138,0, 146,16, // 5 + 146,0, 154,16, // 6 + 154,0, 161,16, // 7 + 161,0, 169,16, // 8 + 169,0, 177,16, // 9 + 177,0, 179,16, // : + 179,0, 183,16, // ; + 183,0, 193,16, // < + 193,0, 203,16, // = + 203,0, 213,16, // > + 213,0, 219,16, // ? + + 0, 17, 14, 33, // @ 64 + 14, 17, 26, 33, // A + 26, 17, 33, 33, // B + 33, 17, 42, 33, // C + 42, 17, 51, 33, // D + 51, 17, 58, 33, // E + 58, 17, 63, 33, // F + 63, 17, 73, 33, // G + 73, 17, 82, 33, // H + 82, 17, 84, 33, // I + 84, 17, 90, 33, // J + 90, 17, 98, 33, // K + 98, 17, 103,33, // L + 103,17, 115,33, // M + 115,17, 124,33, // N + 124,17, 136,33, // O + 136,17, 142,33, // P + 142,17, 154,33, // Q + 154,17, 160,33, // R + 160,17, 167,33, // S + 167,17, 175,33, // T + 175,17, 183,33, // U + 183,17, 194,33, // V + 194,17, 208,33, // W + 208,17, 218,33, // X + 218,17, 227,33, // Y + 227,17, 236,33, // Z + 236,17, 241,33, // [ + 241,17, 248,33, // \ + 248,17, 252,33, // ] + 219,0, 229,16, // ^ + 0, 34, 9, 50, // _ + + 54, 0, 56, 16, // ` 96 + 9, 34, 16, 50, // a + 16, 34, 25, 50, // b + 25, 34, 33, 50, // c + 33, 34, 42, 50, // d + 42, 34, 50, 50, // e + 50, 34, 55, 50, // f + 55, 34, 62, 50, // g + 62, 34, 69, 50, // h + 69, 34, 71, 50, // i + 71, 34, 75, 50, // j + 75, 34, 81, 50, // k + 81, 34, 83, 50, // l + 83, 34, 93, 50, // m + 93, 34, 100,50, // n + 100,34, 109,50, // o + 109,34, 118,50, // p + 118,34, 127,50, // q + 127,34, 132,50, // r + 132,34, 138,50, // s + 138,34, 143,50, // t + 143,34, 150,50, // u + 150,34, 158,50, // v + 158,34, 171,50, // w + 171,34, 179,50, // x + 179,34, 187,50, // y + 187,34, 195,50, // z + 195,34, 201,50, // { + 201,34, 203,50, // | + 203,34, 209,50, // } + 209,34, 219,50, // ~ + 219,34, 228,50, // + + 219,34, 225,50, // 128 + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + + 219,34, 225,50, // 144 + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + + 219,34, 225,50, // 160 + 219,34, 225,50, // 161 A1 ! inversé + 219,34, 225,50, + 219,34, 225,50, // 163 A3 £ + 219,34, 225,50, + 219,34, 225,50, + 0, 0, 4, 16, // 166 A6 ¦ (cadratin) + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + + 219,34, 225,50, // 176 + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, // 191 BF ? inversé + + 12, 51, 24, 67, // 192 C0 à maj + 0, 51, 12, 67, // 193 C1 á maj + 24, 51, 36, 67, // 194 C2 â maj + 48, 51, 60, 67, // 195 C3 ã maj + 36, 51, 48, 67, // 196 C4 ä maj + 219,34, 225,50, + 219,34, 225,50, + 60, 51, 69, 67, // 199 C7 ç maj + 77, 51, 84, 67, // 200 C8 è maj + 70, 51, 77, 67, // 201 C9 é maj + 85, 51, 92, 67, // 202 CA ê maj + 93, 51, 100,67, // 203 CB ë maj + 219,34, 225,50, + 100,51, 104,67, // 205 CD í maj + 108,51, 113,67, // 206 CE î maj + 113,51, 117,67, // 207 CF ï maj + + 219,34, 225,50, // 208 + 117,51, 126,67, // 209 D1 ñ maj + 219,34, 225,50, + 126,51, 138,67, // 211 D3 ó maj + 150,51, 162,67, // 212 D4 ô maj + 219,34, 225,50, + 162,51, 174,67, // 214 D6 ö maj + 219,34, 225,50, + 219,34, 225,50, + 194,51, 202,67, // 217 D9 ù maj + 186,51, 194,67, // 218 DA ú maj + 202,51, 210,67, // 219 DB û maj + 210,51, 218,67, // 220 DC ü maj + 219,34, 225,50, + 219,34, 225,50, + 218,51, 227,67, // 223 DF ss allemand + + 7, 68, 14, 84, // 224 E0 à min + 0, 68, 7, 84, // 225 E1 á min + 14, 68, 21, 84, // 226 E2 â min + 28, 68, 35, 84, // 227 E3 ã min + 21, 68, 28, 84, // 228 E4 ä min + 219,34, 225,50, + 219,34, 225,50, + 35, 68, 43, 84, // 231 E7 ç min + 51, 68, 59, 84, // 232 E8 è min + 43, 68, 51, 84, // 233 E9 é min + 59, 68, 67, 84, // 234 EA ê min + 67, 68, 75, 84, // 235 EB ë min + 219,34, 225,50, + 75, 68, 79, 84, // 237 ED í min + 83, 68, 88, 84, // 238 EE î min + 88, 68, 92, 84, // 239 EF ï min + + 219,34, 225,50, // 240 + 92, 68, 99, 84, // 241 F1 ñ min + 219,34, 225,50, + 99, 68, 108,84, // 243 F3 ó min + 117,68, 126,84, // 244 F4 ô min + 219,34, 225,50, + 126,68, 135,84, // 246 F6 ö min + 219,34, 225,50, + 219,34, 225,50, + 151,68, 158,84, // 249 F9 ù min + 144,68, 151,84, // 250 FA ú min + 158,68, 165,84, // 251 FB û min + 165,68, 172,84, // 252 FC ü min + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, +#else + 0, 0, 4, 16, // 32 + 4, 0, 7, 16, // ! + 7, 0, 13, 16, + 14, 0, 21, 16, + 22, 0, 28, 16, + 29, 0, 38, 16, + 39, 0, 48, 16, + 48, 0, 51, 16, + 51, 0, 55, 16, + 55, 0, 59, 16, + 59, 0, 65, 16, + 66, 0, 72, 16, + 73, 0, 76, 16, + 76, 0, 82, 16, + 82, 0, 85, 16, + 85, 0, 90, 16, + 90, 0, 97, 16, + 98, 0, 103,16, + 104,0, 111,16, + 111,0, 118,16, + 118,0, 125,16, + 125,0, 132,16, + 132,0, 139,16, + 139,0, 146,16, + 146,0, 153,16, + 153,0, 160,16, + 160,0, 165,16, // : + 164,0, 169,16, // ; + 169,0, 177,16, // < + 177,0, 185,16, // = + 185,0, 193,16, // > + 193,0, 201,16, // ? + + 201,0, 215,16, // 64 + 0, 17, 10, 33, // A + 10, 17, 18, 33, + 19, 17, 28, 33, + 28, 17, 36, 33, + 37, 17, 44, 33, + 45, 17, 52, 33, + 53, 17, 62, 33, + 63, 17, 71, 33, + 72, 17, 75, 33, + 75, 17, 82, 33, + 83, 17, 91, 33, + 92, 17, 99, 33, + 100,17, 110,33, + 111,17, 119,33, +//? 120,17, 129,33, // O + 216,0, 227,16, // O + 130,17, 138,33, + 139,17, 148,33, + 149,17, 158,33, + 158,17, 166,33, + 166,17, 175,33, + 175,17, 183,33, + 183,17, 193,33, + 193,17, 207,33, + 207,17, 215,33, + 215,17, 224,33, + 224,17, 232,33, // Z + 232,17, 236,33, + 236,17, 241,33, + 241,17, 245,33, + 245,17, 252,33, // ^ + 0, 34, 8, 50, // _ + + 45, 17, 52, 33, // 96 + 8, 34, 15, 50, // a + 16, 34, 23, 50, + 24, 34, 31, 50, + 31, 34, 38, 50, + 39, 34, 46, 50, + 46, 34, 52, 50, + 52, 34, 59, 50, + 60, 34, 67, 50, + 68, 34, 71, 50, + 71, 34, 76, 50, + 77, 34, 84, 50, + 84, 34, 87, 50, + 88, 34, 99, 50, + 100,34, 107,50, +//? 108,34, 115,50, // o + 238,0, 246,16, // o + 116,34, 123,50, + 124,34, 131,50, + 132,34, 137,50, + 137,34, 144,50, + 144,34, 149,50, + 149,34, 156,50, + 156,34, 164,50, + 164,34, 176,50, + 176,34, 183,50, + 183,34, 191,50, + 191,34, 197,50, // z + 197,34, 203,50, + 203,34, 205,50, + 205,34, 211,50, + 211,34, 219,50, + 219,34, 225,50, + +#if _POLISH + 219,34, 225,50, // 128 + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 0, 51, 8, 67, // 140 S´ + 219,34, 225,50, + 219,34, 225,50, + 9, 51, 17, 67, // 143 Z´ + + 219,34, 225,50, // 144 + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 0, 68, 7, 84, // 156 s´ + 219,34, 225,50, + 219,34, 225,50, + 8, 68, 14, 84, // 159 z´ + + 219,34, 225,50, // 160 + 219,34, 225,50, + 219,34, 225,50, + 18, 51, 27, 67, // 163 L/ + 219,34, 225,50, + 28, 51, 39, 67, // 165 A, + 0, 0, 4, 16, // 166 A6 ¦ (cadratin) + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 40, 51, 48, 67, // 175 Zo + + 219,34, 225,50, // 176 + 219,34, 225,50, + 219,34, 225,50, + 16, 68, 21, 84, // 179 l/ + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 23, 68, 31, 84, // 185 a, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 32, 68, 38, 84, // 191 zo + + 219,34, 225,50, // 192 + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 49, 51, 58, 67, // 198 C´ + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 59, 51, 66, 67, // 202 E, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + + 219,34, 225,50, // 208 + 67, 51, 75, 67, // 209 N´ + 219,34, 225,50, +//? 76, 51, 85, 67, // 211 O´ + 86, 51, 97, 67, // 211 O´ + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + + 219,34, 225,50, // 224 + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 39, 68, 46, 84, // 230 c´ + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 47, 68, 54, 84, // 234 e, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + + 219,34, 225,50, // 240 + 55, 68, 62, 84, // 241 n´ + 219,34, 225,50, +//? 63, 68, 70, 84, // 243 o´ + 71, 68, 79, 84, // 243 o´ + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, +#else + 219,34, 225,50, // 128 + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + + 219,34, 225,50, // 144 + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + + 219,34, 225,50, // 160 + 219,34, 225,50, // 161 A1 ! inversé + 219,34, 225,50, + 219,34, 225,50, // 163 A3 £ + 219,34, 225,50, + 219,34, 225,50, + 0, 0, 4, 16, // 166 A6 ¦ (cadratin) + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + + 219,34, 225,50, // 176 + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, // 191 BF ? inversé + + 10, 51, 20, 67, // 192 C0 à maj + 0, 51, 10, 67, // 193 C1 á maj + 20, 51, 30, 67, // 194 C2 â maj + 40, 51, 50, 67, // 195 C3 ã maj + 30, 51, 40, 67, // 196 C4 ä maj + 219,34, 225,50, + 219,34, 225,50, + 50, 51, 59, 67, // 199 C7 ç maj + 67, 51, 74, 67, // 200 C8 è maj + 59, 51, 66, 67, // 201 C9 é maj + 75, 51, 82, 67, // 202 CA ê maj + 83, 51, 90, 67, // 203 CB ë maj + 219,34, 225,50, + 91, 51, 95, 67, // 205 CD í maj + 100,51, 103,67, // 206 CE î maj + 104,51, 109,67, // 207 CF ï maj + + 219,34, 225,50, // 208 + 109,51, 117,67, // 209 D1 ñ maj + 219,34, 225,50, + 118,51, 127,67, // 211 D3 ó maj + 138,51, 147,67, // 212 D4 ô maj + 219,34, 225,50, + 148,51, 157,67, // 214 D6 ö maj + 219,34, 225,50, + 219,34, 225,50, + 177,51, 185,67, // 217 D9 ù maj + 168,51, 176,67, // 218 DA ú maj + 186,51, 194,67, // 219 DB û maj + 195,51, 203,67, // 220 DC ü maj + 219,34, 225,50, + 219,34, 225,50, + 211,51, 220,67, // 223 DF ss allemand + + 8, 68, 15, 84, // 224 E0 à min + 0, 68, 7, 84, // 225 E1 á min + 16, 68, 23, 84, // 226 E2 â min + 32, 68, 39, 84, // 227 E3 ã min + 24, 68, 31, 84, // 228 E4 ä min + 219,34, 225,50, + 219,34, 225,50, + 40, 68, 47, 84, // 231 E7 ç min + 55, 68, 62, 84, // 232 E8 è min + 47, 68, 54, 84, // 233 E9 é min + 63, 68, 70, 84, // 234 EA ê min + 71, 68, 78, 84, // 235 EB ë min + 219,34, 225,50, + 79, 68, 83, 84, // 237 ED í min + 88, 68, 92, 84, // 238 EE î min + 92, 68, 97, 84, // 239 EF ï min + + 219,34, 225,50, // 240 + 97, 68, 104,84, // 241 F1 ñ min + 219,34, 225,50, + 105,68, 112,84, // 243 F3 ó min + 121,68, 128,84, // 244 F4 ô min + 219,34, 225,50, + 129,68, 136,84, // 246 F6 ö min + 219,34, 225,50, + 219,34, 225,50, + 153,68, 160,84, // 249 F9 ù min + 145,68, 152,84, // 250 FA ú min + 161,68, 168,84, // 251 FB û min + 169,68, 176,84, // 252 FC ü min + 219,34, 225,50, + 219,34, 225,50, + 219,34, 225,50, +#endif +#endif +}; + + +static short table_text_courier[] = +{ +// x1, y1, x2, y2 + 231,137,239,153, // 0 + 1,188, 9,204, // . + 51,188,59,204, // carré + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 11,188,19,204, // \t + 21,188,29,204, // \n + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, // \r + 231,137,239,153, + 231,137,239,153, + 41,188,49,204, // > + 31,188,39,204, // < + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + + 1, 86, 9,102, // 32 + 11, 86, 19,102, + 21, 86, 29,102, + 31, 86, 39,102, + 41, 86, 49,102, + 51, 86, 59,102, + 61, 86, 69,102, + 71, 86, 79,102, + 81, 86, 89,102, + 91, 86, 99,102, + 101, 86,109,102, + 111, 86,119,102, + 121, 86,129,102, + 131, 86,139,102, + 141, 86,149,102, + 151, 86,159,102, + 161, 86,169,102, + 171, 86,179,102, + 181, 86,189,102, + 191, 86,199,102, + 201, 86,209,102, + 211, 86,219,102, + 221, 86,229,102, + 231, 86,239,102, + 1,103, 9,119, // 56 + 11,103, 19,119, + 21,103, 29,119, + 31,103, 39,119, + 41,103, 49,119, + 51,103, 59,119, + 61,103, 69,119, + 71,103, 79,119, + + 81,103, 89,119, // @ + 91,103, 99,119, + 101,103,109,119, + 111,103,119,119, + 121,103,129,119, + 131,103,139,119, + 141,103,149,119, + 151,103,159,119, + 161,103,169,119, + 171,103,179,119, + 181,103,189,119, + 191,103,199,119, + 201,103,209,119, + 211,103,219,119, + 221,103,229,119, + 231,103,239,119, + 1,120, 9,136, // P + 11,120, 19,136, + 21,120, 29,136, + 31,120, 39,136, + 41,120, 49,136, + 51,120, 59,136, + 61,120, 69,136, + 71,120, 79,136, + 81,120, 89,136, + 91,120, 99,136, + 101,120,109,136, + 111,120,119,136, // [ + 121,120,129,136, + 131,120,139,136, + 141,120,149,136, + 151,120,159,136, // _ + + 161,120,169,136, + 171,120,179,136, // a + 181,120,189,136, + 191,120,199,136, + 201,120,209,136, + 211,120,219,136, + 221,120,229,136, + 231,120,239,136, + 1,137, 9,153, + 11,137, 19,153, + 21,137, 29,153, + 31,137, 39,153, + 41,137, 49,153, + 51,137, 59,153, + 61,137, 69,153, + 71,137, 79,153, // o + 81,137, 89,153, + 91,137, 99,153, + 101,137,109,153, + 111,137,119,153, + 121,137,129,153, + 131,137,139,153, + 141,137,149,153, + 151,137,159,153, + 161,137,169,153, + 171,137,179,153, + 181,137,189,153, + 191,137,199,153, + 201,137,209,153, + 211,137,219,153, + 221,137,229,153, // ~ + 231,137,239,153, + +#if _POLISH + 231,137,239,153, // 128 + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 1,154, 9,170, // 140 S´ + 231,137,239,153, + 231,137,239,153, + 11,154, 19,170, // 143 Z´ + + 231,137,239,153, // 144 + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 1,171, 9,187, // 156 s´ + 231,137,239,153, + 231,137,239,153, + 11,171, 19,187, // 159 z´ + + 231,137,239,153, // 160 + 231,137,239,153, + 231,137,239,153, + 21,154, 29,170, // 163 L/ + 231,137,239,153, + 31,154, 39,170, // 165 A, + 1, 86, 9,102, // 166 A6 ¦ (cadratin) + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 41,154, 49,170, // 175 Zo + + 231,137,239,153, // 176 + 231,137,239,153, + 231,137,239,153, + 21,171, 29,187, // 179 l/ + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 31,171, 39,187, // 185 a, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 41,171, 49,187, // 191 zo + + 231,137,239,153, // 192 + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 51,154, 59,170, // 198 C´ + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 61,154, 69,170, // 202 E, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + + 231,137,239,153, // 208 + 71,154, 79,170, // 209 N´ + 231,137,239,153, + 81,171, 89,187, // 211 O´ + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + + 231,137,239,153, // 224 + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 51,171, 59,187, // 230 c´ + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 61,171, 69,187, // 234 e, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + + 231,137,239,153, // 240 + 71,171, 79,187, // 241 n´ + 231,137,239,153, + 81,171, 89,187, // 243 ó min + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, +#else + 231,137,239,153, // 128 + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + + 231,137,239,153, // 144 + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + + 231,137,239,153, // 160 + 231,137,239,153, // 161 A1 ! inversé + 231,137,239,153, + 231,137,239,153, // 163 A3 £ + 231,137,239,153, + 231,137,239,153, + 1, 86, 9,102, // 166 A6 ¦ (cadratin) + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + + 231,137,239,153, // 176 + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, // 191 BF ? inversé + + 11,154, 19,170, // à maj + 1,154, 9,170, // á maj + 21,154, 29,170, // â maj + 41,154, 49,170, // ã maj + 31,154, 39,170, // ä maj + 231,137,239,153, + 231,137,239,153, + 51,154, 59,170, // ç maj + 71,154, 79,170, // è maj + 61,154, 69,170, // é maj + 81,154, 89,170, // ê maj + 91,154, 99,170, // ë maj + 231,137,239,153, + 101,154,109,170, // í maj + 121,154,129,170, // î maj + 131,154,139,170, // ï maj + 231,137,239,153, + 141,154,149,170, // ñ maj + 231,137,239,153, + 151,154,159,170, // ó maj + 171,154,179,170, // ô maj + 231,137,239,153, + 181,154,189,170, // ö maj + 231,137,239,153, + 231,137,239,153, + 211,154,219,170, // ù maj + 201,154,209,170, // ú maj + 221,154,229,170, // û maj + 231,154,239,170, // ü maj + 231,137,239,153, + 231,137,239,153, + 241,154,249,170, // 223 DF ss allemand + + 11,171, 19,187, // à min + 1,171, 9,187, // á min + 21,171, 29,187, // â min + 41,171, 49,187, // ã min + 31,171, 39,187, // ä min + 231,137,239,153, + 231,137,239,153, + 51,171, 59,187, // ç min + 71,171, 79,187, // è min + 61,171, 69,187, // é min + 81,171, 89,187, // ê min + 91,171, 99,187, // ë min + 231,137,239,153, + 111,171,119,187, // ì min + 121,171,129,187, // î min + 131,171,139,187, // ï min + 231,137,239,153, + 141,171,149,187, // ñ min + 231,137,239,153, + 151,171,159,187, // ó min + 171,171,179,187, // ô min + 231,137,239,153, + 181,171,189,187, // ö min + 231,137,239,153, + 231,137,239,153, + 211,171,219,187, // ù min + 201,171,209,187, // ú min + 221,171,229,187, // û min + 231,171,239,187, // ü min + 231,137,239,153, + 231,137,239,153, + 231,137,239,153, +#endif +}; + + +// Retourne le pointeur à la table selon la fonte. + +short* RetTable(FontType font) +{ + if ( font == FONT_COLOBOT ) return table_text_colobot; + else return table_text_courier; +} + + + +// Constructeur de l'objet. + +CText::CText(CInstanceManager* iMan, CD3DEngine* engine) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_TEXT, this); + + m_pD3DDevice = 0; + m_engine = engine; +} + +// Destructeur de l'objet. + +CText::~CText() +{ + m_iMan->DeleteInstance(CLASS_TEXT, this); +} + + +void CText::SetD3DDevice(LPDIRECT3DDEVICE7 device) +{ + m_pD3DDevice = device; +} + + +// Affiche un texte multi-fonte. +// La position verticale est en bas de la boîte du caractère. + +void CText::DrawText(char *string, char *format, int len, FPOINT pos, + float width, int justif, float size, float stretch, + int eol) +{ + float sw; + + if ( justif == 0 ) // centré ? + { + sw = RetStringWidth(string, format, len, size, stretch); + if ( sw > width ) sw = width; + pos.x -= sw/2.0f; + } + if ( justif < 0 ) // drapeau à gauche ? + { + sw = RetStringWidth(string, format, len, size, stretch); + if ( sw > width ) sw = width; + pos.x -= sw; + } + DrawString(string, format, len, pos, width, size, stretch, eol); +} + +// Affiche un texte multi-fonte. +// La position verticale est en bas de la boîte du caractère. + +void CText::DrawText(char *string, char *format, FPOINT pos, float width, + int justif, float size, float stretch, + int eol) +{ + DrawText(string, format, strlen(string), pos, width, justif, size, stretch, eol); +} + +// Affiche un texte. +// La position verticale est en bas de la boîte du caractère. + +void CText::DrawText(char *string, int len, FPOINT pos, float width, + int justif, float size, float stretch, FontType font, + int eol) +{ + float sw; + + if ( justif == 0 ) // centré ? + { + sw = RetStringWidth(string, len, size, stretch, font); + if ( sw > width ) sw = width; + pos.x -= sw/2.0f; + } + if ( justif < 0 ) // drapeau à gauche ? + { + sw = RetStringWidth(string, len, size, stretch, font); + if ( sw > width ) sw = width; + pos.x -= sw; + } + DrawString(string, len, pos, width, size, stretch, font, eol); +} + +// Affiche un texte. +// La position verticale est en bas de la boîte du caractère. + +void CText::DrawText(char *string, FPOINT pos, float width, + int justif, float size, float stretch, FontType font, + int eol) +{ + DrawText(string, strlen(string), pos, width, justif, size, stretch, font, eol); +} + + +// Retourne les dimensions d'un texte multi-fonte. + +void CText::DimText(char *string, char *format, int len, FPOINT pos, + int justif, float size, float stretch, + FPOINT &start, FPOINT &end) +{ + float sw; + + start = end = pos; + + sw = RetStringWidth(string, format, len, size, stretch); + end.x += sw; + if ( justif == 0 ) // centré ? + { + start.x -= sw/2.0f; + end.x -= sw/2.0f; + } + if ( justif < 0 ) // drapeau à gauche ? + { + start.x -= sw; + end.x -= sw; + } + + start.y -= RetDescent(size, FONT_COLOBOT); + end.y += RetAscent(size, FONT_COLOBOT); +} + +// Retourne les dimensions d'un texte multi-fonte. + +void CText::DimText(char *string, char *format, FPOINT pos, int justif, + float size, float stretch, + FPOINT &start, FPOINT &end) +{ + DimText(string, format, strlen(string), pos, justif, size, stretch, start, end); +} + +// Retourne les dimensions d'un texte. + +void CText::DimText(char *string, int len, FPOINT pos, int justif, + float size, float stretch, FontType font, + FPOINT &start, FPOINT &end) +{ + float sw; + + start = end = pos; + + sw = RetStringWidth(string, len, size, stretch, font); + end.x += sw; + if ( justif == 0 ) // centré ? + { + start.x -= sw/2.0f; + end.x -= sw/2.0f; + } + if ( justif < 0 ) // drapeau à gauche ? + { + start.x -= sw; + end.x -= sw; + } + + start.y -= RetDescent(size, font); + end.y += RetAscent(size, font); +} + +// Retourne les dimensions d'un texte. + +void CText::DimText(char *string, FPOINT pos, int justif, + float size, float stretch, FontType font, + FPOINT &start, FPOINT &end) +{ + DimText(string, strlen(string), pos, justif, size, stretch, font, start, end); +} + + +// Retourne la hauteur au-dessus de la ligne de base. + +float CText::RetAscent(float size, FontType font) +{ + return (13.0f/256.0f)*(size/20.0f); +} + +// Retourne la hauteur au-dessous de la ligne de base. + +float CText::RetDescent(float size, FontType font) +{ + return (3.0f/256.0f)*(size/20.0f); +} + +// Retourne la hauteur totale du caractère. + +float CText::RetHeight(float size, FontType font) +{ + return (16.0f/256.0f)*(size/20.0f); +} + + +// Retourne la largeur d'une chaîne de caractères multi-fonte. + +float CText::RetStringWidth(char *string, char *format, int len, + float size, float stretch) +{ + FontType font; + float st, tab, w, width = 0.0f; + short *table, *pt; + int i, c; + + for ( i=0 ; iRetEditIndentValue(); + w = tab-Mod(width, tab); + if ( w < tab*0.1f ) w += tab; + width += w; + continue; + } + + if ( c > 255 ) continue; + + pt = table+c*4; + st = stretch; + if ( font == FONT_COLOBOT && (c == 'O' || c == 'o') ) st = 0.8f; + width += (float)(pt[2]-pt[0])/256.0f*(size/20.0f)*st; + } + } + + return width; +} + +// Retourne la largeur d'une chaîne de caractères. + +float CText::RetStringWidth(char *string, int len, + float size, float stretch, FontType font) +{ + float st, tab, w, width = 0.0f; + short *table, *pt; + int i, c; + + table = RetTable(font); + for ( i=0 ; iRetEditIndentValue(); + w = tab-Mod(width, tab); + if ( w < tab*0.1f ) w += tab; + width += w; + continue; + } + + if ( c > 255 ) continue; + + pt = table+c*4; + st = stretch; + if ( font == FONT_COLOBOT && (c == 'O' || c == 'o') ) st = 0.8f; + width += (float)(pt[2]-pt[0])/256.0f*(size/20.0f)*st; + } + + return width; +} + +// Retourne la largeur d'un caractère. +// 'offset' est la position actuelle dans la ligne. + +float CText::RetCharWidth(int character, float offset, + float size, float stretch, FontType font) +{ + float st, tab, w; + short* pt; + + if ( font == FONT_BUTTON ) return (12.0f/256.0f)*(size/20.0f); + + if ( character == '\t' ) + { + pt = RetTable(font)+' '*4; + tab = (float)(pt[2]-pt[0])/256.0f*(size/20.0f)*stretch*m_engine->RetEditIndentValue(); + w = tab-Mod(offset, tab); + if ( w < tab*0.1f ) w += tab; + return w; + } + + if ( character > 255 ) return 0.0f; + + pt = RetTable(font)+character*4; + st = stretch; +#if !_NEWLOOK + if ( font == FONT_COLOBOT && (character == 'O' || character == 'o') ) st = 0.8f; +#endif + return (float)(pt[2]-pt[0])/256.0f*(size/20.0f)*st; +} + + +// Justifie une ligne de texte multi-fonte. Retourne l'offset de la coupure. + +int CText::Justif(char *string, char *format, int len, float width, + float size, float stretch) +{ + FontType font; + float pos; + int i, character, cut; + + pos = 0.0f; + cut = 0; + for ( i=0 ; i width ) + { + if ( cut == 0 ) return i; + else return cut; + } + } + return i; +} + +// Justifie une ligne de texte. Retourne l'offset de la coupure. + +int CText::Justif(char *string, int len, float width, + float size, float stretch, FontType font) +{ + float pos; + int i, character, cut; + + pos = 0.0f; + cut = 0; + for ( i=0 ; i width ) + { + if ( cut == 0 ) return i; + else return cut; + } + } + return i; +} + +// Retourne la position convenant le mieux à une offset donnée (multi-fonte). + +int CText::Detect(char *string, char *format, int len, float offset, + float size, float stretch) +{ + FontType font; + float pos, width; + int i, character, cut; + + pos = 0.0f; + cut = 0; + for ( i=0 ; iSetTexture("textp.tga"); +#else + m_engine->SetTexture("text.tga"); +#endif + m_engine->SetState(D3DSTATETTw); + + font = FONT_COLOBOT; + + start = pos.x; + offset = 0.0f; + for ( i=0 ; i width ) // dépasse la largeur maximale ? + { + cw = RetCharWidth(16, offset, size, stretch, font); + pos.x = start+width-cw; + DrawChar(16, pos, size, stretch, font); // > + break; + } + + if ( (format[i]&COLOR_MASK) != 0 ) + { + DrawColor(pos, size, cw, format[i]&COLOR_MASK); + } + DrawChar(c, pos, size, stretch, font); + offset += cw; + pos.x += cw; + } + + if ( eol != 0 ) + { + DrawChar(eol, pos, size, stretch, font); + } +} + +// Affiche un texte. + +void CText::DrawString(char *string, int len, FPOINT pos, float width, + float size, float stretch, FontType font, + int eol) +{ + float start, offset, cw; + int i, c; + +#if _POLISH + m_engine->SetTexture("textp.tga"); +#else + m_engine->SetTexture("text.tga"); +#endif + m_engine->SetState(D3DSTATETTw); + + start = pos.x; + offset = 0.0f; + for ( i=0 ; i width ) // dépasse la largeur maximale ? + { + cw = RetCharWidth(16, offset, size, stretch, font); + pos.x = start+width-cw; + DrawChar(16, pos, size, stretch, font); // > + break; + } + + DrawChar(c, pos, size, stretch, font); + offset += cw; + pos.x += cw; + } + + if ( eol != 0 ) + { + DrawChar(eol, pos, size, stretch, font); + } +} + +// Affiche le lien d'un caractère. + +void CText::DrawColor(FPOINT pos, float size, float width, int color) +{ + D3DVERTEX2 vertex[4]; // 2 triangles + FPOINT p1, p2; + POINT dim; + D3DVECTOR n; + float h, u1, u2, v1, v2, dp; + int icon; + + icon = -1; + if ( color == COLOR_LINK ) icon = 9; // bleu + if ( color == COLOR_TOKEN ) icon = 4; // orange + if ( color == COLOR_TYPE ) icon = 5; // vert + if ( color == COLOR_CONST ) icon = 8; // rouge + if ( color == COLOR_REM ) icon = 6; // magenta + if ( color == COLOR_KEY ) icon = 10; // gris + if ( icon == -1 ) return; + + if ( color == COLOR_LINK ) + { + m_engine->SetState(D3DSTATENORMAL); + } + + dim = m_engine->RetDim(); + if ( dim.y <= 768.0f ) // 1024x768 ou moins ? + { + h = 1.01f/dim.y; // 1 pixel + } + else // plus que 1024x768 ? + { + h = 2.0f/dim.y; // 2 pixels + } + + p1.x = pos.x; + p2.x = pos.x + width; + + if ( color == COLOR_LINK ) + { + p1.y = pos.y; + p2.y = pos.y + h; // juste souligné + } + else + { +#if 1 + p1.y = pos.y; + p2.y = pos.y + (16.0f/256.0f)*(size/20.0f); +//? p2.y = pos.y + h*4.0f; // juste souligné épais +#else + p1.y = pos.y; + p2.y = pos.y + (16.0f/256.0f)*(size/20.0f)/4.0f; +#endif + } + + u1 = (16.0f/256.0f)*(icon%16); + v1 = (240.0f/256.0f); + u2 = (16.0f/256.0f)+u1; + v2 = (16.0f/256.0f)+v1; + + dp = 0.5f/256.0f; + u1 += dp; + v1 += dp; + u2 -= dp; + v2 -= dp; + + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, u1,v2); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, u1,v1); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, u2,v2); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, u2,v1); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + + if ( color == COLOR_LINK ) + { + m_engine->SetState(D3DSTATETTw); + } +} + +// Affiche un caractère. + +void CText::DrawChar(int character, FPOINT pos, float size, + float stretch, FontType font) +{ + D3DVERTEX2 vertex[4]; // 2 triangles + FPOINT p1, p2; + D3DVECTOR n; + float width, height, u1, u2, v1, v2, dp; + short* pt; + + dp = 0.5f/256.0f; + n = D3DVECTOR(0.0f, 0.0f, -1.0f); // normale + + if ( font == FONT_BUTTON ) + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + + p1.x = pos.x; + p1.y = pos.y; + p2.x = pos.x + (12.0f/256.0f)*(size/20.0f); + p2.y = pos.y + (16.0f/256.0f)*(size/20.0f); + + if ( character <= 64 || character >= 128+56 ) + { + u1 = 66.0f/256.0f; + v1 = 2.0f/256.0f; + u2 = 94.0f/256.0f; + v2 = 30.0f/256.0f; + } + else + { + u1 = 224.0f/256.0f; + v1 = 32.0f/256.0f; + u2 = 256.0f/256.0f; + v2 = 64.0f/256.0f; + } + + u1 += dp; + v1 += dp; + u2 -= dp; + v2 -= dp; + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, u1,v2); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, u1,v1); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, u2,v2); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, u2,v1); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + +//? p1.x += (12.0f/256.0f)*(size/20.0f)*0.1f; +//? p1.y += (16.0f/256.0f)*(size/20.0f)*0.1f; +//? p2.x -= (12.0f/256.0f)*(size/20.0f)*0.1f; +//? p2.y -= (16.0f/256.0f)*(size/20.0f)*0.1f; + + if ( character >= 64 && character < 64+64 ) + { + character -= 64; + m_engine->SetTexture("button2.tga"); + } + if ( character >= 128 && character < 128+64 ) + { + character -= 128; + m_engine->SetTexture("button3.tga"); + } + + m_engine->SetState(D3DSTATETTw); + + u1 = (32.0f/256.0f)*(character%8); + v1 = (32.0f/256.0f)*(character/8); // uv texture + u2 = (32.0f/256.0f)+u1; + v2 = (32.0f/256.0f)+v1; + + u1 += dp; + v1 += dp; + u2 -= dp; + v2 -= dp; + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, u1,v2); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, u1,v1); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, u2,v2); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, u2,v1); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + + +#if _POLISH + m_engine->SetTexture("textp.tga"); +#else + m_engine->SetTexture("text.tga"); +#endif + return; + } + + if ( character > 255 ) return; + +//? if ( character == '\t' ) character = ' '; // si tab, n'affiche pas -> + +#if !_NEWLOOK + if ( font == FONT_COLOBOT && (character == 'O' || character == 'o') ) + { + stretch = 0.8f; + } +#endif + if ( font == FONT_COURIER ) + { + stretch *= 1.2f; + } + + pt = RetTable(font)+character*4; + width = (float)(pt[2]-pt[0])/256.0f*stretch*0.9f; +//? width = (float)(pt[2]-pt[0])/256.0f*stretch; + height = (float)(pt[3]-pt[1])/256.0f; + +#if _NEWLOOK + pos.y += height*(size/20.0f)/17.0f; +#endif + p1.x = pos.x; + p1.y = pos.y; + p2.x = pos.x + width*(size/20.0f); + p2.y = pos.y + height*(size/20.0f); + + u1 = (float)pt[0]/256.0f; + v1 = (float)pt[1]/256.0f; + u2 = (float)pt[2]/256.0f; + v2 = (float)pt[3]/256.0f; + + if ( font == FONT_COLOBOT ) + { + u1 += dp; + u2 += dp; +#if _NEWLOOK + v2 += dp; +#endif + } + if ( font == FONT_COURIER ) + { + u1 -= dp; + u2 += dp*2.0f; + } + + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, 0.0f), n, u1,v2); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, 0.0f), n, u1,v1); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, 0.0f), n, u2,v2); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, 0.0f), n, u2,v1); + + m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); +} + diff --git a/src/text.h b/src/text.h new file mode 100644 index 00000000..0ae1ac28 --- /dev/null +++ b/src/text.h @@ -0,0 +1,96 @@ +// text.h + +#ifndef _TEXT_H_ +#define _TEXT_H_ + + + +class CInstanceManager; +class CD3DEngine; + + + +#define SMALLFONT 10.0f +#define BIGFONT 15.0f + +#define NORMSTRETCH 0.8f + + + +enum FontType +{ + FONT_COLOBOT = 0, + FONT_COURIER = 1, + FONT_BUTTON = 2, +}; + +enum FontTitle +{ + TITLE_BIG = 0x04, + TITLE_NORM = 0x08, + TITLE_LITTLE = 0x0c, +}; + +enum FontColor +{ + COLOR_LINK = 0x10, + COLOR_TOKEN = 0x20, + COLOR_TYPE = 0x30, + COLOR_CONST = 0x40, + COLOR_REM = 0x50, + COLOR_KEY = 0x60, + COLOR_TABLE = 0x70, +}; + +#define FONT_MASK 0x03 +#define TITLE_MASK 0x0c +#define COLOR_MASK 0x70 +#define IMAGE_MASK 0x80 + + + +class CText +{ +public: + CText(CInstanceManager *iMan, CD3DEngine* engine); + ~CText(); + + void SetD3DDevice(LPDIRECT3DDEVICE7 device); + + void DrawText(char *string, char *format, int len, FPOINT pos, float width, int justif, float size, float stretch, int eol); + void DrawText(char *string, char *format, FPOINT pos, float width, int justif, float size, float stretch, int eol); + void DrawText(char *string, int len, FPOINT pos, float width, int justif, float size, float stretch, FontType font, int eol); + void DrawText(char *string, FPOINT pos, float width, int justif, float size, float stretch, FontType font, int eol); + void DimText(char *string, char *format, int len, FPOINT pos, int justif, float size, float stretch, FPOINT &start, FPOINT &end); + void DimText(char *string, char *format, FPOINT pos, int justif, float size, float stretch, FPOINT &start, FPOINT &end); + void DimText(char *string, int len, FPOINT pos, int justif, float size, float stretch, FontType font, FPOINT &start, FPOINT &end); + void DimText(char *string, FPOINT pos, int justif, float size, float stretch, FontType font, FPOINT &start, FPOINT &end); + + float RetAscent(float size, FontType font); + float RetDescent(float size, FontType font); + float RetHeight(float size, FontType font); + + float RetStringWidth(char *string, char *format, int len, float size, float stretch); + float RetStringWidth(char *string, int len, float size, float stretch, FontType font); + float RetCharWidth(int character, float offset, float size, float stretch, FontType font); + + int Justif(char *string, char *format, int len, float width, float size, float stretch); + int Justif(char *string, int len, float width, float size, float stretch, FontType font); + int Detect(char *string, char *format, int len, float offset, float size, float stretch); + int Detect(char *string, int len, float offset, float size, float stretch, FontType font); + +protected: + void DrawString(char *string, char *format, int len, FPOINT pos, float width, float size, float stretch, int eol); + void DrawString(char *string, int len, FPOINT pos, float width, float size, float stretch, FontType font, int eol); + void DrawColor(FPOINT pos, float size, float width, int color); + void DrawChar(int character, FPOINT pos, float size, float stretch, FontType font); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + LPDIRECT3DDEVICE7 m_pD3DDevice; + +}; + + +#endif //_TEXT_H_ diff --git a/src/tracks.txt b/src/tracks.txt new file mode 100644 index 00000000..8d808725 --- /dev/null +++ b/src/tracks.txt @@ -0,0 +1,17 @@ +Utilisation des tracks + + 1: data + 2: terre Terre + 3: tropica Tropica + 4: crystalium Crystalium + 5: saari Saari + 6: volcano Volcano + 7: centaury Centaury + 8: orpheon Orphéon + 9: terranova Terranova +10: lost perdu +11: win gagné +12: fox fin du jeu +13: shuffle - + +DR 12.06.01 diff --git a/src/traduc.txt b/src/traduc.txt new file mode 100644 index 00000000..73bbf554 --- /dev/null +++ b/src/traduc.txt @@ -0,0 +1,151 @@ +Types des objets COLOBOT +------------------------ + + + ... // sac de survie + + BaseCamp // fusée principale + Derrick // derrick + BotFactory // fabrique de véhicules + PowerStation // station de recharge + Converter // conversion minerai en titanium + RepairCenter // centre de réparation + DefenceTower // tour de défense + AlienNest // nid + ResearchCenter // centre de recherches + RadarStation // radar + PowerPlant // fabrique de piles + AutoLab // analyseur de matières organiques + NuclearPlant // centrale nucléaire + StartArea // départ + GoalArea // arrivée + ExchangePost // borne d'information + + TitaniumOre // minerai de titanium + UraniumOre // minerai d'uranium + Titanium // titanium + PowerPack // pile normale + NuclearPack // pile nucléaire + OrgaStuff // matière organique + BlackBox // boîte noire + TNT // explosif + + PowerSpot // énergie en sous-sol + TitaniumSpot // titanium en sous-sol + UraniumSpot // uranium en sous-sol + PathSpot // chemin + + TrainingRollerBot // véhicule d'entraînement : + TrainingCrawlerBot + TrainingJetBot + TrainingSpiderBot + + GrabberRollerBot // véhicule manipulateur : + GrabberCrawlerBot + GrabberJetBot + GrabberSpiderBot + + FireballRollerBot // véhicule de défense : + FireballCrawlerBot + FireballJetBot + FireballSpiderBot + + OrgaballRollerBot // véhicule de défense : + OrgaballCrawlerBot + OrgaballJetBot + OrgaballSpiderBot + + SnifferRollerBot // véhicule sondeur : + SnifferCrawlerBot + SnifferJetBot + SnifferSpiderBot + + ThumperBot // véhicule de terrassement + FazerBot // véhicule de défense + RecyclerBot // véhicule de récupération + ShieldBot // véhicule de protection + SubBot // sous-marin + TargetBot // cible d'entraînement + + Me // homme + Wiz // toto + + AlienBigLady // mère pondeuse + AlienEgg // oeuf + AlienAnt // fourmi + AlienSpider // araignée + AlienWasp // guèpe + AlienWorm // ver + + Wreck // épave de véhicule + Ruin // bâtiment en ruine + Firework // feu d'artifice + Mine // mine + Barrier // barrière + Greenery // plante + Tree // arbre + Quartz // quartz + MegaStalk // racine + ... // gravi-plante + + +Fonction du langage CBOT : + + Move // avance d'une certaine distance + Turn // tourne d'un certain angle + Goto // va à une coordonnée donnée + Motor // commande directe des moteurs gauche & droite + Jet // commande directe du réacteur + + Radar // détecte un objet + Direction // calcule une direction + Distance // calcule la distance entre deux points + Wait // attend + Produce // crée un objet + Message // affiche un message + + Grab // prend un objet avec le bras manipulateur + Drop // dépose un objet avec le bras manipulateur + Sniff // sonde le sous-sol + Receive // reçoit une information + Send // envoie une information + Thump // terraforme le terrain + Recycle // récupère une épave + Shield // actionne le bouclier de protection + Fire // tir avec un canon + Aim // hausse du canon + + +Classes : + + point // point avec coordonnées x,y,z + object // descriptif d'un objet + + +Descripteur d'un objet : + + category // catégorie de l'objet + energyPack // object pile + load // objet transporté + position // position x,y,z + direction // orientation + energyLevel // niveau d'énergie + shieldLevel // niveau du bouclier + altitude // hauteur par rapport au sol + + +Argument de la commande Manip( ) : + + InFront // prend ou dépose devant + Behind // prend ou dépose derrière + EnergyPack // prend ou dépose sa propre pile + + +Filtre pour la commande Radar( ) ; + + FilterNone // pas de filtre + FilterOnlyLanding // seulement les robots qui roulent + FilterOnlyFliying // seulement les robots qui volent + + +DR 14.03.01 diff --git a/src/version.txt b/src/version.txt new file mode 100644 index 00000000..da312874 --- /dev/null +++ b/src/version.txt @@ -0,0 +1,108 @@ +1.18 (24.01.08) +- Ne vérifie plus la présence des pistes audio. + +1.17 (08.01.08) +- Sound.cpp:InitAudioTrackVolume:mixerGetLineControls ne devrait plus planter + sous Vista, grace à l'abandon de MIXER_GETLINECONTROLSF_ALL. +- CoLoBoT ne joue plus les pistes audio, et le réglage du volume a disparu +- CoLoBoT n'affiche plus Alsyd +- Texte "www.colobot.com" remplacé par "www.ceebot.com" + +1.16 (10.11.03) +- Options: si "accès aux solutions" décoché -> Lancer défi, + éditer programme, Esc, Abandonner: plus de bouton "accès à la solution" + +1.15 (02.09.03) +- EVENT_INTERFACE_KGUP: plus si CeeBot-Teen ! +- EVENT_INTERFACE_KGDOWN: plus si CeeBot-Teen ! +- EVENT_INTERFACE_KACTION: plus si CeeBot-Teen ! +- EVENT_INTERFACE_KVISIT: plus si CeeBot-Teen ! +- EVENT_INTERFACE_KNEXT: plus si CeeBot-Teen ! +- EVENT_INTERFACE_KHUMAN: plus si CeeBot-Teen ! +- EVENT_INTERFACE_KDESEL: plus si CeeBot-Teen ! +- EVENT_INTERFACE_KCBOT: plus si CeeBot-Teen ! + +1.14 (28.07.03) +- Switch -nosetup -> plus de bouton "Options" pendant jeu + +1.8 () +- instruction shield(1, radius) ok avec radius 10..25 +- instruction shield(1, radius) possible si bouclier déjà déployé + +1.8 BETA (05.10.01) +- CBOT: public, static, synchronized, private, protected +- CBOT: strlen, strleft, strright, strmid, strval, strfind, strupper, strlower +- CBOT: file, open, close, writeln, readln, eof, deletefile +- extern/public: aide en cliquant dans la ligne d'info de l'éditeur +- ExchangePost: UpdateList si infos reçues +- ok si lien d'un seul caractère: \l;x\u file; +- colobot.ini: [Setup] DeleteGamer=1 +- colobot.ini: [Directory] files=files +- studio: nb max de caractères: 10000 -> 20000 +- script antt41 (scene904): AlienMother -> AlienQueen +- load: recompilation ok si procédures "public" dans d'autres robots +- si le volume CD est nul, la piste n'est pas du tout jouée +- goto ResearchCenter toujours possible +- meilleure gestion des touches Ctrl et Shift dans l'éditeur +- object.temperature=12 impossible (PR_READ) +- studio: ne plante plus si on tape plus de 100 car. sans espace +- studio: "enregistrer" se rappelle du nom de fichier dans la mission +- studio: Ctrl+A sélectionne tout +- studio: run: si liste < 5 variables -> fond gris + +1.7 (13.09.01) +- object.lifeTime +- object.range Winged* idem si camera OnBoard/Back +- float flatground(center, rmax); +- float abstime(); +- float receive(name, power); +- void send(name, value, power); +- void deleteinfo(name, power); +- bool testinfo(name, power); +- Le cosmonaute peut construire une borne d'information + +1.6 (06.09.01) +- niveaux supplémentaires avec %user% +- fonction "send" plus coloriée +- english help: EnergyPack -> EnergyCell +- english help: energyPack -> energyCell +- english help: FinishArea -> GoalArea + +1.5 (24.08.01) +- first execute: prefered first non-T&L, second T&L +- fondus welcome: D3DSTATETTb/w -> D3DSTATETCb/w + +1.4 (02.08.01) +- n'importe quelle touche/clic passe les écrans welcome1, 2 et 3 +- icône associée au CD dans autorun.inf +- SatCom: icône "Aide à la programmation" présente dans exercices +- welcome: phase "Alsyd" plus longue (8s) +- SaveOneScript quand on quitte le studio par ok, cancel ou run + +1.3 (27.07.01) +- goto BotFactory toujours possible +- documentation errmode(0) +- petites corrections anglaises par MW +- enlever pile pendant recherche ne bloque plus +- si human porte qq chose et vaisseau décole -> ne reste plus sur place +- robot sur vaisseau puis sauvegarder-reprendre -> impossible gravir pentes + +GoldMaster 1.3 /e envoyé à CD PRESS pour 1000x + +1.2 +- bouton de fermeture (en haut à droite) supprimé en mode fenêtré +- suppression du réglage "nice reset" + +1.1 (20.07.01) +- volume du son non nul après installation +- images logo au démarrage du jeu +- image generic != si français ou anglais +- install: nouvelle clé pour uninstall depuis autorun + +GoldMaster 1.1 /f envoyé à Alsyd + +1.0 +- commande "reset" (exercices) tjrs ok (fichier rechargé) +- nom sauvegarde avec date/heure au format anglais +- ScriptName.F et .E + diff --git a/src/water.cpp b/src/water.cpp new file mode 100644 index 00000000..f3e4dc8c --- /dev/null +++ b/src/water.cpp @@ -0,0 +1,819 @@ +// water.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "D3DMath.h" +#include "event.h" +#include "misc.h" +#include "iman.h" +#include "math3d.h" +#include "particule.h" +#include "terrain.h" +#include "object.h" +#include "sound.h" +#include "water.h" + + + + +// Constructeur du terrain. + +CWater::CWater(CInstanceManager* iMan, CD3DEngine* engine) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_WATER, this); + + m_engine = engine; + m_terrain = 0; + m_particule = 0; + m_sound = 0; + + m_type[0] = WATER_NULL; + m_type[1] = WATER_NULL; + m_level = 0.0f; + m_bDraw = TRUE; + m_bLava = FALSE; + m_color = 0xffffffff; + m_subdiv = 4; + m_filename[0] = 0; +} + +// Destructeur du terrain. + +CWater::~CWater() +{ +} + + +BOOL CWater::EventProcess(const Event &event) +{ + if ( event.event == EVENT_FRAME ) + { + return EventFrame(event); + } + + if ( event.event == EVENT_KEYDOWN ) + { +#if 0 + if ( event.param == 'S' ) + { + if ( m_subdiv == 1 ) m_subdiv = 2; + else if ( m_subdiv == 2 ) m_subdiv = 4; + else if ( m_subdiv == 4 ) m_subdiv = 8; + else if ( m_subdiv == 8 ) m_subdiv = 1; + SetLevel(m_level); + } + if ( event.param == 'M' ) + { + SetLevel(m_level+1.0f); + } + if ( event.param == 'D' ) + { + SetLevel(m_level-1.0f); + } + if ( event.param == 'H' ) + { + m_bDraw = !m_bDraw; + } + if ( event.param == 'C' ) + { + if ( m_color == 0xffffffff ) m_color = 0xcccccccc; + else if ( m_color == 0xcccccccc ) m_color = 0x88888888; + else if ( m_color == 0x88888888 ) m_color = 0x44444444; + else if ( m_color == 0x44444444 ) m_color = 0x00000000; + else if ( m_color == 0x00000000 ) m_color = 0xffffffff; + } + if ( event.param == 'Q' ) + { + int i; + i = (m_color>>24); + i += 0x44; + i &= 0xff; + i = (i<<24); + m_color &= 0x00ffffff; + m_color |= i; + } + if ( event.param == 'W' ) + { + int i; + i = (m_color>>16); + i += 0x44; + i &= 0xff; + i = (i<<16); + m_color &= 0xff00ffff; + m_color |= i; + } + if ( event.param == 'E' ) + { + int i; + i = (m_color>>8); + i += 0x44; + i &= 0xff; + i = (i<<8); + m_color &= 0xffff00ff; + m_color |= i; + } + if ( event.param == 'R' ) + { + int i; + i = m_color; + i += 0x44; + i &= 0xff; + m_color &= 0xffffff00; + m_color |= i; + } +#endif + } + return TRUE; +} + +// Fait évoluer l'eau. + +BOOL CWater::EventFrame(const Event &event) +{ + if ( m_engine->RetPause() ) return TRUE; + + m_time += event.rTime; + + if ( m_type[0] == WATER_NULL ) return TRUE; + + if ( m_bLava ) + { + LavaFrame(event.rTime); + } + return TRUE; +} + +// Fait évoluer les jets de vapeur sur la lave. + +void CWater::LavaFrame(float rTime) +{ + D3DVECTOR eye, lookat, dir, perp, pos; + float distance, shift, level; + int i; + + if ( m_particule == 0 ) + { + m_particule = (CParticule*)m_iMan->SearchInstance(CLASS_PARTICULE); + } + + for ( i=0 ; i= 0.1f ) + { + eye = m_engine->RetEyePt(); + lookat = m_engine->RetLookatPt(); + + distance = Rand()*200.0f; + shift = (Rand()-0.5f)*200.0f; + + dir = Normalize(lookat-eye); + pos = eye + dir*distance; + + perp.x = -dir.z; + perp.y = dir.y; + perp.z = dir.x; + pos = pos + perp*shift; + + level = m_terrain->RetFloorLevel(pos, TRUE); + if ( level < m_level ) + { + pos.y = m_level; + + level = Rand(); + if ( level < 0.8f ) + { + if ( VaporCreate(PARTIFIRE, pos, 0.02f+Rand()*0.06f) ) + { + m_lastLava = m_time; + } + } + else if ( level < 0.9f ) + { + if ( VaporCreate(PARTIFLAME, pos, 0.5f+Rand()*3.0f) ) + { + m_lastLava = m_time; + } + } + else + { + if ( VaporCreate(PARTIVAPOR, pos, 0.2f+Rand()*2.0f) ) + { + m_lastLava = m_time; + } + } + } + } +} + +// Supprime tous les jets de vapeur. + +void CWater::VaporFlush() +{ + int i; + + for ( i=0 ; iPlay(SOUND_BLUP, pos, 1.0f, 1.0f-Rand()*0.5f); + } + if ( m_vapor[i].type == PARTIFLAME ) + { +//? m_sound->Play(SOUND_SWIM, pos, 1.0f, 1.0f-Rand()*0.5f); + } + if ( m_vapor[i].type == PARTIVAPOR ) + { + m_sound->Play(SOUND_PSHHH, pos, 0.3f, 2.0f); + } + + return TRUE; + } + } + return FALSE; +} + +// Fait évoluer un jet de vapeur, + +void CWater::VaporFrame(int i, float rTime) +{ + D3DVECTOR pos, speed; + FPOINT dim; + int j; + + m_vapor[i].time += rTime; + + if ( m_sound == 0 ) + { + m_sound = (CSound*)m_iMan->SearchInstance(CLASS_SOUND); + } + + if ( m_vapor[i].time <= m_vapor[i].delay ) + { + if ( m_time-m_vapor[i].last >= m_engine->ParticuleAdapt(0.02f) ) + { + m_vapor[i].last = m_time; + + if ( m_vapor[i].type == PARTIFIRE ) + { + for ( j=0 ; j<10 ; j++ ) + { + pos = m_vapor[i].pos; + pos.x += (Rand()-0.5f)*2.0f; + pos.z += (Rand()-0.5f)*2.0f; + pos.y -= 1.0f; + speed.x = (Rand()-0.5f)*6.0f; + speed.z = (Rand()-0.5f)*6.0f; + speed.y = 8.0f+Rand()*5.0f; + dim.x = Rand()*1.5f+1.5f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIERROR, 2.0f, 10.0f); + } + } + else if ( m_vapor[i].type == PARTIFLAME ) + { + pos = m_vapor[i].pos; + pos.x += (Rand()-0.5f)*8.0f; + pos.z += (Rand()-0.5f)*8.0f; + pos.y -= 2.0f; + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = 4.0f+Rand()*4.0f; + dim.x = Rand()*2.0f+2.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIFLAME); + } + else + { + pos = m_vapor[i].pos; + pos.x += (Rand()-0.5f)*4.0f; + pos.z += (Rand()-0.5f)*4.0f; + pos.y -= 2.0f; + speed.x = (Rand()-0.5f)*2.0f; + speed.z = (Rand()-0.5f)*2.0f; + speed.y = 8.0f+Rand()*8.0f; + dim.x = Rand()*1.0f+1.0f; + dim.y = dim.x; + m_particule->CreateParticule(pos, speed, dim, PARTIVAPOR); + } + } + } + else + { + m_vapor[i].bUsed = FALSE; + } +} + + +// Ajuste la position et la normale, pour imiter des reflets +// sur une étendue d'eau au repos. + +void CWater::AdjustLevel(D3DVECTOR &pos, D3DVECTOR &norm, + FPOINT &uv1, FPOINT &uv2) +{ +#if 0 + float t1, t2; + + uv1.x = (pos.x+10000.0f)/40.0f; + uv1.y = (pos.z+10000.0f)/40.0f; + + t1 = m_time*1.5f + pos.x*0.1f * pos.z*0.2f; + pos.y += sinf(t1)*m_eddy.y; + + t1 = m_time*0.6f + pos.x*0.1f * pos.z*0.2f; + t2 = m_time*0.7f + pos.x*0.3f * pos.z*0.4f; + pos.x += sinf(t1)*m_eddy.x; + pos.z += sinf(t2)*m_eddy.z; + +//? uv2.x = (pos.x+10000.0f)/40.0f+0.3f; +//? uv2.y = (pos.z+10000.0f)/40.0f+0.4f; + uv2.x = (pos.x+10000.0f)/20.0f; + uv2.y = (pos.z+10000.0f)/20.0f; + + t1 = m_time*0.7f + pos.x*5.5f + pos.z*5.6f; + t2 = m_time*0.8f + pos.x*5.7f + pos.z*5.8f; + norm = D3DVECTOR(sinf(t1)*m_glint, 1.0f, sinf(t2)*m_glint); +#else + float t1, t2; + + t1 = m_time*1.5f + pos.x*0.1f * pos.z*0.2f; + pos.y += sinf(t1)*m_eddy.y; + + t1 = m_time*1.5f; + uv1.x = (pos.x+10000.0f)/40.0f+sinf(t1)*m_eddy.x*0.02f; + uv1.y = (pos.z+10000.0f)/40.0f-cosf(t1)*m_eddy.z*0.02f; + uv2.x = (pos.x+10010.0f)/20.0f+cosf(-t1)*m_eddy.x*0.02f; + uv2.y = (pos.z+10010.0f)/20.0f-sinf(-t1)*m_eddy.z*0.02f; + + t1 = m_time*0.50f + pos.x*2.1f + pos.z*1.1f; + t2 = m_time*0.75f + pos.x*2.0f + pos.z*1.0f; + norm = D3DVECTOR(sinf(t1)*m_glint, 1.0f, sinf(t2)*m_glint); +#endif +} + +// Dessine la surface arrière de l'eau. +// Cette surface empèche de voir le ciel (background) sous l'eau ! + +void CWater::DrawBack() +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2 vertex[4]; // 2 triangles + D3DMATERIAL7 material; + D3DMATRIX matrix; + D3DVECTOR eye, lookat, n, p, p1, p2; + FPOINT uv1, uv2; + float deep, dist; + + if ( !m_bDraw ) return; + if ( m_type[0] == WATER_NULL ) return; + if ( m_lineUsed == 0 ) return; + + eye = m_engine->RetEyePt(); + lookat = m_engine->RetLookatPt(); + + ZeroMemory( &material, sizeof(D3DMATERIAL7) ); + material.diffuse = m_diffuse; + material.ambient = m_ambient; + m_engine->SetMaterial(material); + + m_engine->SetTexture("", 0); + + device = m_engine->RetD3DDevice(); + device->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE); + device->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE); + device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + m_engine->SetState(D3DSTATENORMAL); + + deep = m_engine->RetDeepView(0); + m_engine->SetDeepView(deep*2.0f, 0); + m_engine->SetFocus(m_engine->RetFocus()); + m_engine->UpdateMatProj(); // double la profondeur de vue + + D3DUtil_SetIdentityMatrix(matrix); + device->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + p.x = eye.x; + p.z = eye.z; + dist = Length2d(eye, lookat); + p.x = (lookat.x-eye.x)*deep*1.0f/dist + eye.x; + p.z = (lookat.z-eye.z)*deep*1.0f/dist + eye.z; + + p1.x = (lookat.z-eye.z)*deep*2.0f/dist + p.x; + p1.z = -(lookat.x-eye.x)*deep*2.0f/dist + p.z; + p2.x = -(lookat.z-eye.z)*deep*2.0f/dist + p.x; + p2.z = (lookat.x-eye.x)*deep*2.0f/dist + p.z; + + p1.y = -50.0f; + p2.y = m_level; + + n.x = (lookat.x-eye.x)/dist; + n.z = (lookat.z-eye.z)/dist; + n.y = 0.0f; + + uv1.x = uv1.y = 0.0f; + uv2.x = uv2.y = 0.0f; + + vertex[0] = D3DVERTEX2(D3DVECTOR(p1.x, p2.y, p1.z), n, uv1.x,uv2.y); + vertex[1] = D3DVERTEX2(D3DVECTOR(p1.x, p1.y, p1.z), n, uv1.x,uv1.y); + vertex[2] = D3DVERTEX2(D3DVECTOR(p2.x, p2.y, p2.z), n, uv2.x,uv2.y); + vertex[3] = D3DVERTEX2(D3DVECTOR(p2.x, p1.y, p2.z), n, uv2.x,uv1.y); + + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL); + m_engine->AddStatisticTriangle(2); + + m_engine->SetDeepView(deep, 0); + m_engine->SetFocus(m_engine->RetFocus()); + m_engine->UpdateMatProj(); // remet profondeur de vue initiale + + device->SetRenderState(D3DRENDERSTATE_LIGHTING, TRUE); + device->SetRenderState(D3DRENDERSTATE_ZENABLE, TRUE); + device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); +} + +// Dessine la surface plane de l'eau. + +void CWater::DrawSurf() +{ + LPDIRECT3DDEVICE7 device; + D3DVERTEX2* vertex; // triangles + D3DMATERIAL7 material; + D3DMATRIX matrix; + D3DVECTOR eye, lookat, n, pos, p; + FPOINT uv1, uv2; + BOOL bUnder; + DWORD flags; + float deep, size, sizez, radius; + int rankview, i, j, u; + + if ( !m_bDraw ) return; + if ( m_type[0] == WATER_NULL ) return; + if ( m_lineUsed == 0 ) return; + + vertex = (D3DVERTEX2*)malloc(sizeof(D3DVERTEX2)*(m_brick+2)*2); + + eye = m_engine->RetEyePt(); + lookat = m_engine->RetLookatPt(); + + rankview = m_engine->RetRankView(); + bUnder = ( rankview == 1); + + device = m_engine->RetD3DDevice(); +//? device->SetRenderState(D3DRENDERSTATE_AMBIENT, 0xffffffff); +//? device->SetRenderState(D3DRENDERSTATE_LIGHTING, TRUE); +//? device->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE); + device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE); + + D3DUtil_SetIdentityMatrix(matrix); + device->SetTransform(D3DTRANSFORMSTATE_WORLD, &matrix); + + ZeroMemory( &material, sizeof(D3DMATERIAL7) ); + material.diffuse = m_diffuse; + material.ambient = m_ambient; + m_engine->SetMaterial(material); + + m_engine->SetTexture(m_filename, 0); + m_engine->SetTexture(m_filename, 1); + + if ( m_type[rankview] == WATER_TT ) + { + m_engine->SetState(D3DSTATETTb|D3DSTATEDUALw|D3DSTATEWRAP, m_color); + } + if ( m_type[rankview] == WATER_TO ) + { + m_engine->SetState(D3DSTATENORMAL|D3DSTATEDUALw|D3DSTATEWRAP); + } + if ( m_type[rankview] == WATER_CT ) + { + m_engine->SetState(D3DSTATETTb); + } + if ( m_type[rankview] == WATER_CO ) + { + m_engine->SetState(D3DSTATENORMAL); + } + device->SetRenderState(D3DRENDERSTATE_FOGENABLE, TRUE); + + size = m_size/2.0f; + if ( bUnder ) sizez = -size; + else sizez = size; + + // Dessine toutes les lignes. + deep = m_engine->RetDeepView(0)*1.5f; + + for ( i=0 ; i deep+radius ) continue; + device->ComputeSphereVisibility(&p, &radius, 1, 0, &flags); + if ( flags & D3DSTATUS_CLIPINTERSECTIONALL ) continue; + + u = 0; + p.x = pos.x-size; + p.z = pos.z-sizez; + p.y = pos.y; + AdjustLevel(p, n, uv1, uv2); + if ( bUnder ) n.y = -n.y; + vertex[u++] = D3DVERTEX2(p, n, uv1.x,uv1.y, uv2.x,uv2.y); + + p.x = pos.x-size; + p.z = pos.z+sizez; + p.y = pos.y; + AdjustLevel(p, n, uv1, uv2); + if ( bUnder ) n.y = -n.y; + vertex[u++] = D3DVERTEX2(p, n, uv1.x,uv1.y, uv2.x,uv2.y); + + for ( j=0 ; jDrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, u, NULL); + m_engine->AddStatisticTriangle(u-2); + } + + free(vertex); +} + + +// Indique s'il y a de l'eau à une position donnée. + +BOOL CWater::RetWater(int x, int y) +{ + D3DVECTOR pos; + float size, offset, level; + int dx, dy; + + x *= m_subdiv; + y *= m_subdiv; + + size = m_size/m_subdiv; + offset = m_brick*m_size/2.0f; + + for ( dy=0 ; dy<=m_subdiv ; dy++ ) + { + for ( dx=0 ; dx<=m_subdiv ; dx++ ) + { + pos.x = (x+dx)*size - offset; + pos.z = (y+dy)*size - offset; + pos.y = 0.0f; + level = m_terrain->RetFloorLevel(pos, TRUE); + if ( level < m_level+m_eddy.y ) return TRUE; + } + } + return FALSE; +} + +// Met à jour les positions par-rapport au terrain. + +BOOL CWater::CreateLine(int x, int y, int len) +{ + float offset; + + m_line[m_lineUsed].x = x; + m_line[m_lineUsed].y = y; + m_line[m_lineUsed].len = len; + + offset = m_brick*m_size/2.0f - m_size/2.0f; + + m_line[m_lineUsed].px1 = m_size* m_line[m_lineUsed].x - offset; + m_line[m_lineUsed].px2 = m_size*(m_line[m_lineUsed].x+m_line[m_lineUsed].len) - offset; + m_line[m_lineUsed].pz = m_size* m_line[m_lineUsed].y - offset; + + m_lineUsed ++; + + return ( m_lineUsed < MAXWATERLINE ); +} + +// Crée toutes les étendues d'eau. + +BOOL CWater::Create(WaterType type1, WaterType type2, const char *filename, + D3DCOLORVALUE diffuse, D3DCOLORVALUE ambient, + float level, float glint, D3DVECTOR eddy) +{ + int x, y, len; + + m_type[0] = type1; + m_type[1] = type2; + m_diffuse = diffuse; + m_ambient = ambient; + m_level = level; + m_glint = glint; + m_eddy = eddy; + m_time = 0.0f; + m_lastLava = 0.0f; + strcpy(m_filename, filename); + + VaporFlush(); + + if ( m_filename[0] != 0 ) + { + m_engine->LoadTexture(m_filename, 0); + m_engine->LoadTexture(m_filename, 1); + } + + if ( m_terrain == 0 ) + { + m_terrain = (CTerrain*)m_iMan->SearchInstance(CLASS_TERRAIN); + } + m_brick = m_terrain->RetBrick()*m_terrain->RetMosaic(); + m_size = m_terrain->RetSize(); + + m_brick /= m_subdiv; + m_size *= m_subdiv; + + if ( m_type[0] == WATER_NULL ) return TRUE; + + m_lineUsed = 0; + for ( y=0 ; y= 5 ) + { + if ( !CreateLine(x-len+1, y, len) ) return FALSE; + len = 0; + } + } + else // sec ? + { + if ( len != 0 ) + { + if ( !CreateLine(x-len, y, len) ) return FALSE; + len = 0; + } + } + } + if ( len != 0 ) + { + if ( !CreateLine(x-len, y, len) ) return FALSE; + } + } + return TRUE; +} + +// Supprime toute l'eau. + +void CWater::Flush() +{ + m_type[0] = WATER_NULL; + m_type[1] = WATER_NULL; + m_level = 0.0f; + m_bLava = FALSE; +} + + +// Modifie le niveau de l'eau. + +BOOL CWater::SetLevel(float level) +{ + m_level = level; + + return Create(m_type[0], m_type[1], m_filename, m_diffuse, m_ambient, + m_level, m_glint, m_eddy); +} + +// Retourne le niveau actuel de l'eau. + +float CWater::RetLevel() +{ + return m_level; +} + +// Retourne le niveau actuel de l'eau pour un objet donné. + +float CWater::RetLevel(CObject* object) +{ + ObjectType type; + + type = object->RetType(); + + if ( type == OBJECT_HUMAN || + type == OBJECT_TECH ) + { + return m_level-3.0f; + } + + if ( type == OBJECT_MOBILEfa || + type == OBJECT_MOBILEta || + type == OBJECT_MOBILEwa || + type == OBJECT_MOBILEia || + type == OBJECT_MOBILEfc || + type == OBJECT_MOBILEtc || + type == OBJECT_MOBILEwc || + type == OBJECT_MOBILEic || + type == OBJECT_MOBILEfi || + type == OBJECT_MOBILEti || + type == OBJECT_MOBILEwi || + type == OBJECT_MOBILEii || + type == OBJECT_MOBILEfs || + type == OBJECT_MOBILEts || + type == OBJECT_MOBILEws || + type == OBJECT_MOBILEis || + type == OBJECT_MOBILErt || + type == OBJECT_MOBILErc || + type == OBJECT_MOBILErr || + type == OBJECT_MOBILErs || + type == OBJECT_MOBILEsa || + type == OBJECT_MOBILEtg || + type == OBJECT_MOBILEft || + type == OBJECT_MOBILEtt || + type == OBJECT_MOBILEwt || + type == OBJECT_MOBILEit || + type == OBJECT_MOBILEdr ) + { + return m_level-2.0f; + } + + return m_level; +} + + +// Gestion du mode lave/eau. + +void CWater::SetLava(BOOL bLava) +{ + m_bLava = bLava; +} + +BOOL CWater::RetLava() +{ + return m_bLava; +} + + +// Ajuste l'oeil de la caméra, pour ne pas être entre deux eaux. + +void CWater::AdjustEye(D3DVECTOR &eye) +{ + if ( m_bLava ) + { + if ( eye.y < m_level+2.0f ) + { + eye.y = m_level+2.0f; // jamais sous la lave + } + } + else + { + if ( eye.y >= m_level-2.0f && + eye.y <= m_level+2.0f ) // proche de la surface ? + { + eye.y = m_level+2.0f; // paf, bien dessus + } + } +} + diff --git a/src/water.h b/src/water.h new file mode 100644 index 00000000..f5751ae2 --- /dev/null +++ b/src/water.h @@ -0,0 +1,118 @@ +// water.h + +#ifndef _WATER_H_ +#define _WATER_H_ + + +class CInstanceManager; +class CD3DEngine; +class CTerrain; +class CParticule; +class CSound; + +enum ParticuleType; + + + +#define MAXWATERLINE 500 + +typedef struct +{ + short x, y; // début + short len; // longueur en x + float px1, px2, pz; +} +WaterLine; + + +#define MAXWATVAPOR 10 + +typedef struct +{ + BOOL bUsed; + ParticuleType type; + D3DVECTOR pos; + float delay; + float time; + float last; +} +WaterVapor; + + +enum WaterType +{ + WATER_NULL = 0, // pas d'eau + WATER_TT = 1, // texture transparente + WATER_TO = 2, // texture opaque + WATER_CT = 3, // couleur transparente + WATER_CO = 4, // couleur opaque +}; + + +class CWater +{ +public: + CWater(CInstanceManager* iMan, CD3DEngine* engine); + ~CWater(); + + void SetD3DDevice(LPDIRECT3DDEVICE7 device); + BOOL EventProcess(const Event &event); + void Flush(); + BOOL Create(WaterType type1, WaterType type2, const char *filename, D3DCOLORVALUE diffuse, D3DCOLORVALUE ambient, float level, float glint, D3DVECTOR eddy); + void DrawBack(); + void DrawSurf(); + + BOOL SetLevel(float level); + float RetLevel(); + float RetLevel(CObject* object); + + void SetLava(BOOL bLava); + BOOL RetLava(); + + void AdjustEye(D3DVECTOR &eye); + +protected: + BOOL EventFrame(const Event &event); + void LavaFrame(float rTime); + void AdjustLevel(D3DVECTOR &pos, D3DVECTOR &norm, FPOINT &uv1, FPOINT &uv2); + BOOL RetWater(int x, int y); + BOOL CreateLine(int x, int y, int len); + + void VaporFlush(); + BOOL VaporCreate(ParticuleType type, D3DVECTOR pos, float delay); + void VaporFrame(int i, float rTime); + +protected: + CInstanceManager* m_iMan; + CD3DEngine* m_engine; + LPDIRECT3DDEVICE7 m_pD3DDevice; + CTerrain* m_terrain; + CParticule* m_particule; + CSound* m_sound; + + WaterType m_type[2]; + char m_filename[100]; + float m_level; // niveau global + float m_glint; // amplitude des reflets + D3DVECTOR m_eddy; // amplitude des remous + D3DCOLORVALUE m_diffuse; // couleur diffuse + D3DCOLORVALUE m_ambient; // couleur ambiante + float m_time; + float m_lastLava; + int m_subdiv; + + int m_brick; // nb de briques*mosaïque + float m_size; // taille d'un élément dans une brique + + int m_lineUsed; + WaterLine m_line[MAXWATERLINE]; + + WaterVapor m_vapor[MAXWATVAPOR]; + + BOOL m_bDraw; + BOOL m_bLava; + D3DCOLOR m_color; +}; + + +#endif //_WATER_H_ diff --git a/src/window.cpp b/src/window.cpp new file mode 100644 index 00000000..17409193 --- /dev/null +++ b/src/window.cpp @@ -0,0 +1,1610 @@ +// window.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include + +#include "struct.h" +#include "D3DEngine.h" +#include "language.h" +#include "math3d.h" +#include "event.h" +#include "misc.h" +#include "restext.h" +#include "iman.h" +#include "button.h" +#include "color.h" +#include "check.h" +#include "key.h" +#include "group.h" +#include "image.h" +#include "label.h" +#include "edit.h" +#include "editvalue.h" +#include "scroll.h" +#include "slider.h" +#include "list.h" +#include "shortcut.h" +#include "map.h" +#include "gauge.h" +#include "compass.h" +#include "target.h" +#include "text.h" +#include "window.h" + + + + +// Constructeur de l'objet. + +CWindow::CWindow(CInstanceManager* iMan) : CControl(iMan) +{ + int i; + + CControl::CControl(iMan); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau bouton. + +CColor* CWindow::CreateColor(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CColor* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau bouton. + +CCheck* CWindow::CreateCheck(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CCheck* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau bouton. + +CKey* CWindow::CreateKey(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CKey* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau bouton. + +CGroup* CWindow::CreateGroup(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CGroup* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau bouton. + +CImage* CWindow::CreateImage(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CImage* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau label. + +CLabel* CWindow::CreateLabel(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg, + char *name) +{ + CLabel* pc; + char* p; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + + p = strchr(name, '\\'); + if ( p == 0 ) + { + pc->SetName(name); + } + else + { + char text[100]; + strncpy(text, name, 100); + text[100-1] = 0; + if ( p-name < 100 ) + { + text[p-name] = 0; // supprime texte après "\\" (tooltip) + } + pc->SetName(text); + } + return pc; + } + } + return 0; +} + +// Crée un nouveau pavé éditable. + +CEdit* CWindow::CreateEdit(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CEdit* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau pavé éditable. + +CEditValue* CWindow::CreateEditValue(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CEditValue* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouvel ascenseur. + +CScroll* CWindow::CreateScroll(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CScroll* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée un nouveau curseur. + +CSlider* CWindow::CreateSlider(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CSlider* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée une nouvelle liste. + +CList* CWindow::CreateList(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg, + float expand) +{ + CList* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg, expand); + return pc; + } + } + return 0; +} + +// Crée un nouveau raccourci. + +CShortcut* CWindow::CreateShortcut(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CShortcut* ps; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return ps; + } + } + return 0; +} + +// Crée une nouvelle carte. + +CMap* CWindow::CreateMap(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CMap* pm; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pm; + } + } + return 0; +} + +// Crée une nouvelle jauge. + +CGauge* CWindow::CreateGauge(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CGauge* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée une nouvelle boussole. + +CCompass* CWindow::CreateCompass(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CCompass* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Crée une nouvelle cible. + +CTarget* CWindow::CreateTarget(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg) +{ + CTarget* pc; + int i; + + if ( eventMsg == EVENT_NULL ) eventMsg = GetUniqueEventMsg(); + + for ( i=0 ; iCreate(pos, dim, icon, eventMsg); + return pc; + } + } + return 0; +} + +// Supprime un contrôle. + +BOOL CWindow::DeleteControl(EventMsg eventMsg) +{ + int i; + + for ( i=0 ; iRetEventMsg() ) + { + delete m_table[i]; + m_table[i] = 0; + return TRUE; + } + } + } + return FALSE; +} + +// Donne un contrôle. + +CControl* CWindow::SearchControl(EventMsg eventMsg) +{ + int i; + + for ( i=0 ; iRetEventMsg() ) + { + return m_table[i]; + } + } + } + return 0; +} + + +// Donne le tooltip lié à la fenêtre. + +BOOL CWindow::GetTooltip(FPOINT pos, char* name) +{ + int i; + + for ( i=MAXWINDOW-1 ; i>=0 ; i-- ) + { + if ( m_table[i] != 0 ) + { + if ( m_table[i]->GetTooltip(pos, name) ) + { + return TRUE; + } + } + } + + if ( m_buttonClose != 0 && + m_buttonClose->GetTooltip(pos, name) ) + { + return TRUE; + } + if ( m_buttonFull != 0 && + m_buttonFull->GetTooltip(pos, name) ) + { + return TRUE; + } + if ( m_buttonReduce != 0 && + m_buttonReduce->GetTooltip(pos, name) ) + { + return TRUE; + } + + if ( Detect(pos) ) // dans la fenêtre ? + { + strcpy(name, m_tooltip); + return TRUE; + } + + return FALSE; +} + + +// Spécifie le nom pour la barre de titre. + +void CWindow::SetName(char* name) +{ + CButton* pc; + BOOL bAdjust; + + CControl::SetName(name); + + if ( m_buttonReduce != 0 ) + { + delete m_buttonReduce; + m_buttonReduce = 0; + } + + if ( m_buttonFull != 0 ) + { + delete m_buttonFull; + m_buttonFull = 0; + } + + if ( m_buttonClose != 0 ) + { + delete m_buttonClose; + m_buttonClose = 0; + } + + bAdjust = FALSE; + + if ( m_name[0] != 0 && m_bRedim ) // barre de titre existe ? + { + m_buttonReduce = new CButton(m_iMan); + pc = (CButton*)m_buttonReduce; + pc->Create(m_pos, m_dim, 0, EVENT_NULL); + + m_buttonFull = new CButton(m_iMan); + pc = (CButton*)m_buttonFull; + pc->Create(m_pos, m_dim, 0, EVENT_NULL); + + bAdjust = TRUE; + } + + if ( m_name[0] != 0 && m_bClosable ) // barre de titre existe ? + { + m_buttonClose = new CButton(m_iMan); + pc = (CButton*)m_buttonClose; + pc->Create(m_pos, m_dim, 0, EVENT_NULL); + + bAdjust = TRUE; + } + + if ( bAdjust ) + { + AdjustButtons(); + } + + MoveAdjust(); +} + + +void CWindow::SetPos(FPOINT pos) +{ + CControl::SetPos(pos); + MoveAdjust(); +} + +void CWindow::SetDim(FPOINT dim) +{ + if ( dim.x < m_minDim.x ) dim.x = m_minDim.x; + if ( dim.x > m_maxDim.x ) dim.x = m_maxDim.x; + if ( dim.y < m_minDim.y ) dim.y = m_minDim.y; + if ( dim.y > m_maxDim.y ) dim.y = m_maxDim.y; + + CControl::SetDim(dim); + MoveAdjust(); +} + +void CWindow::MoveAdjust() +{ + FPOINT pos, dim; + float h, offset; + + h = m_engine->RetText()->RetHeight(m_fontSize, m_fontType); + dim.y = h*1.2f; + dim.x = dim.y*0.75f; + + if ( m_buttonClose != 0 ) + { + pos.x = m_pos.x+m_dim.x-0.01f-dim.x; + pos.y = m_pos.y+m_dim.y-0.01f-h*1.2f; + m_buttonClose->SetPos(pos); + m_buttonClose->SetDim(dim); + offset = dim.x*1.0f; + } + else + { + offset = 0.0f; + } + + if ( m_buttonFull != 0 ) + { + pos.x = m_pos.x+m_dim.x-0.01f-dim.x-offset; + pos.y = m_pos.y+m_dim.y-0.01f-h*1.2f; + m_buttonFull->SetPos(pos); + m_buttonFull->SetDim(dim); + } + + if ( m_buttonReduce != 0 ) + { + pos.x = m_pos.x+m_dim.x-0.01f-dim.x*2.0f-offset; + pos.y = m_pos.y+m_dim.y-0.01f-h*1.2f; + m_buttonReduce->SetPos(pos); + m_buttonReduce->SetDim(dim); + } +} + + +void CWindow::SetMinDim(FPOINT dim) +{ + m_minDim = dim; +} + +void CWindow::SetMaxDim(FPOINT dim) +{ + m_maxDim = dim; +} + +FPOINT CWindow::RetMinDim() +{ + return m_minDim; +} + +FPOINT CWindow::RetMaxDim() +{ + return m_maxDim; +} + + +// Indique si la fenêtre est déplaçable. + +void CWindow::SetMovable(BOOL bMode) +{ + m_bMovable = bMode; +} + +BOOL CWindow::RetMovable() +{ + return m_bMovable; +} + + +// Gestion de la présence des boutons minimise/maximise. + +void CWindow::SetRedim(BOOL bMode) +{ + m_bRedim = bMode; +} + +BOOL CWindow::RetRedim() +{ + return m_bRedim; +} + + +// Gestion de la présence du bouton de fermeture. + +void CWindow::SetClosable(BOOL bMode) +{ + m_bClosable = bMode; +} + +BOOL CWindow::RetClosable() +{ + return m_bClosable; +} + + +void CWindow::SetMaximized(BOOL bMaxi) +{ + m_bMaximized = bMaxi; + AdjustButtons(); +} + +BOOL CWindow::RetMaximized() +{ + return m_bMaximized; +} + +void CWindow::SetMinimized(BOOL bMini) +{ + m_bMinimized = bMini; + AdjustButtons(); +} + +BOOL CWindow::RetMinimized() +{ + return m_bMinimized; +} + +void CWindow::SetFixed(BOOL bFix) +{ + m_bFixed = bFix; +} + +BOOL CWindow::RetFixed() +{ + return m_bFixed; +} + + +// Ajuste les boutons dans la barre de titre. + +void CWindow::AdjustButtons() +{ + char res[100]; + + if ( m_buttonFull != 0 ) + { + if ( m_bMaximized ) + { + m_buttonFull->SetIcon(54); + GetResource(RES_TEXT, RT_WINDOW_STANDARD, res); + m_buttonFull->SetTooltip(res); + } + else + { + m_buttonFull->SetIcon(52); + GetResource(RES_TEXT, RT_WINDOW_MAXIMIZED, res); + m_buttonFull->SetTooltip(res); + } + } + + if ( m_buttonReduce != 0 ) + { + if ( m_bMinimized ) + { + m_buttonReduce->SetIcon(54); + GetResource(RES_TEXT, RT_WINDOW_STANDARD, res); + m_buttonReduce->SetTooltip(res); + } + else + { + m_buttonReduce->SetIcon(51); + GetResource(RES_TEXT, RT_WINDOW_MINIMIZED, res); + m_buttonReduce->SetTooltip(res); + } + } + + if ( m_buttonClose != 0 ) + { + m_buttonClose->SetIcon(11); // x + GetResource(RES_TEXT, RT_WINDOW_CLOSE, res); + m_buttonClose->SetTooltip(res); + } +} + + +void CWindow::SetTrashEvent(BOOL bTrash) +{ + m_bTrashEvent = bTrash; +} + +BOOL CWindow::RetTrashEvent() +{ + return m_bTrashEvent; +} + + +// Retourne le message du bouton "reduce". + +EventMsg CWindow::RetEventMsgReduce() +{ + if ( m_buttonReduce == 0 ) return EVENT_NULL; + return m_buttonReduce->RetEventMsg(); +} + +// Retourne le message du bouton "full". + +EventMsg CWindow::RetEventMsgFull() +{ + if ( m_buttonFull == 0 ) return EVENT_NULL; + return m_buttonFull->RetEventMsg(); +} + +// Retourne le message du bouton "close". + +EventMsg CWindow::RetEventMsgClose() +{ + if ( m_buttonClose == 0 ) return EVENT_NULL; + return m_buttonClose->RetEventMsg(); +} + + +// Détecte si la souris est dans un bord de la fenêtre, pour +// la redimensionner. +// Bits retournée: 0=gauche, 1=bas, 2=droite, 3=haut, -1=tout. + +int CWindow::BorderDetect(FPOINT pos) +{ + FPOINT dim; + float h; + int flags; + + if ( m_bMaximized || m_bMinimized || m_bFixed ) return 0; + + flags = 0; + if ( pos.x < m_pos.x+0.030f ) + { + flags |= (1<<0); + } + if ( pos.y < m_pos.y+0.020f ) + { + flags |= (1<<1); + } + if ( pos.x > m_pos.x+m_dim.x-0.030f ) + { + flags |= (1<<2); + } + if ( pos.y > m_pos.y+m_dim.y-0.020f ) + { + flags |= (1<<3); + } + + if ( pos.x > m_pos.x+ 0.015f && + pos.x < m_pos.x+m_dim.x-0.015f && + pos.y > m_pos.y+ 0.010f && + pos.y < m_pos.y+m_dim.y-0.010f ) + { + flags = 0; + } + + if ( flags == 0 ) + { + h = m_engine->RetText()->RetHeight(m_fontSize, m_fontType); + dim.y = h*1.2f; + dim.x = dim.y*0.75f; + if ( pos.x < m_pos.x+m_dim.x-0.01f-dim.x*3.0f && + pos.y >= m_pos.y+m_dim.y-0.01f-h*1.2f ) + { + flags = -1; + } + } + + return flags; +} + +// Gestion d'un événement. + +BOOL CWindow::EventProcess(const Event &event) +{ + FPOINT pos; + int i, flags; + + if ( event.event == EVENT_MOUSEMOVE ) + { + if ( m_bCapture ) + { + m_engine->SetMouseType(m_pressMouse); + } + else + { + m_pressMouse = D3DMOUSENORM; + + if ( m_name[0] != 0 && m_bMovable && // barre de titre ? + Detect(event.pos) ) + { + flags = BorderDetect(event.pos); + if ( flags == -1 ) + { + m_pressMouse = D3DMOUSEMOVE; // + + } + else if ( ((flags & (1<<0)) && (flags & (1<<3))) || + ((flags & (1<<1)) && (flags & (1<<2))) ) + { + m_pressMouse = D3DMOUSEMOVEI; // \ + } + else if ( ((flags & (1<<0)) && (flags & (1<<1))) || + ((flags & (1<<2)) && (flags & (1<<3))) ) + { + m_pressMouse = D3DMOUSEMOVED; // / + } + else if ( (flags & (1<<0)) || (flags & (1<<2)) ) + { + m_pressMouse = D3DMOUSEMOVEH; // - + } + else if ( (flags & (1<<1)) || (flags & (1<<3)) ) + { + m_pressMouse = D3DMOUSEMOVEV; // | + } + } + + if ( m_pressMouse != D3DMOUSENORM ) + { + m_engine->SetMouseType(m_pressMouse); + } + } + } + + if ( !m_bCapture ) + { + for ( i=MAXWINDOW-1 ; i>=0 ; i-- ) + { + if ( m_table[i] != 0 ) + { + if ( !m_table[i]->EventProcess(event) ) + { + return FALSE; + } + } + } + + if ( m_buttonReduce != 0 ) + { + m_buttonReduce->EventProcess(event); + } + if ( m_buttonFull != 0 ) + { + m_buttonFull->EventProcess(event); + } + if ( m_buttonClose != 0 ) + { + m_buttonClose->EventProcess(event); + } + } + + if ( m_bTrashEvent && event.event == EVENT_LBUTTONDOWN ) + { + if ( Detect(event.pos) ) + { + if ( m_name[0] != 0 && m_bMovable ) // barre de titre ? + { + m_pressFlags = BorderDetect(event.pos); + if ( m_pressFlags != 0 ) + { + m_bCapture = TRUE; + m_pressPos = event.pos; + } + } + return FALSE; + } + } + + if ( event.event == EVENT_MOUSEMOVE && m_bCapture ) + { + pos = event.pos; + if ( m_pressFlags == -1 ) // déplace tout ? + { + m_pos.x += pos.x-m_pressPos.x; + m_pos.y += pos.y-m_pressPos.y; + } + else + { + if ( m_pressFlags & (1<<0) ) // bord gauche ? + { + if ( pos.x > m_pressPos.x+m_dim.x-m_minDim.x ) + { + pos.x = m_pressPos.x+m_dim.x-m_minDim.x; + } + m_pos.x += pos.x-m_pressPos.x; + m_dim.x -= pos.x-m_pressPos.x; + } + if ( m_pressFlags & (1<<1) ) // bord inf ? + { + if ( pos.y > m_pressPos.y+m_dim.y-m_minDim.y ) + { + pos.y = m_pressPos.y+m_dim.y-m_minDim.y; + } + m_pos.y += pos.y-m_pressPos.y; + m_dim.y -= pos.y-m_pressPos.y; + } + if ( m_pressFlags & (1<<2) ) // bord droite ? + { + if ( pos.x < m_pressPos.x-m_dim.x+m_minDim.x ) + { + pos.x = m_pressPos.x-m_dim.x+m_minDim.x; + } + m_dim.x += pos.x-m_pressPos.x; + } + if ( m_pressFlags & (1<<3) ) // bord sup ? + { + if ( pos.y < m_pressPos.y-m_dim.y+m_minDim.y ) + { + pos.y = m_pressPos.y-m_dim.y+m_minDim.y; + } + m_dim.y += pos.y-m_pressPos.y; + } + } + m_pressPos = pos; + AdjustButtons(); + + Event newEvent = event; + newEvent.event = m_eventMsg; + m_event->AddEvent(newEvent); + } + + if ( event.event == EVENT_LBUTTONUP && m_bCapture ) + { + m_bCapture = FALSE; + } + + return TRUE; +} + + +// Dessine la fenêtre. + +void CWindow::Draw() +{ + FPOINT pos, dim; + float width, h, sw; + int i; + + if ( (m_state & STATE_VISIBLE) == 0 ) return; + + if ( m_state & STATE_SHADOW ) + { + DrawShadow(m_pos, m_dim); + } + + DrawVertex(m_pos, m_dim, m_icon); // dessine le fond + + if ( m_name[0] != 0 ) // barre de titre ? + { + h = m_engine->RetText()->RetHeight(m_fontSize, m_fontType); + + // Dessine l'ombre sous la barre de titre. + { + FPOINT sPos, sDim; + + pos.x = m_pos.x+0.01f; + dim.x = m_dim.x-0.02f; + pos.y = m_pos.y+m_dim.y-0.01f-h*1.2f; + dim.y = h*1.2f; + DrawShadow(pos, dim); + } + + width = m_dim.x; + if ( m_bRedim ) width -= h*1.2f*0.75f*2.0f; + if ( m_bClosable ) width -= h*1.2f*0.75f; + + pos.x = m_pos.x+0.01f; + dim.x = width-0.02f; + pos.y = m_pos.y+m_dim.y-0.01f-h*1.2f; + dim.y = h*1.2f; + DrawVertex(pos, dim, (m_state&STATE_ENABLE)?2:9); + + sw = m_engine->RetText()->RetStringWidth(m_name, strlen(m_name), m_fontSize, m_fontStretch, m_fontType); + + if ( m_state&STATE_ENABLE ) + { + pos.x = m_pos.x+0.015f; + dim.x = (width-sw-0.06f)/2.0f; + pos.y = m_pos.y+m_dim.y-0.01f-h*1.0f; + dim.y = h*0.8f; + DrawHach(pos, dim); // hachures gauches + pos.x = m_pos.x+width-dim.x-0.015f; + DrawHach(pos, dim); // hachures droites + } + + pos.x = m_pos.x+width/2.0f; + pos.y = m_pos.y+m_dim.y-0.01f-h*1.10f; + m_engine->RetText()->DrawText(m_name, pos, width, 0, m_fontSize, m_fontStretch, m_fontType, 0); + + if ( m_buttonReduce != 0 ) + { + m_buttonReduce->Draw(); + } + + if ( m_buttonFull != 0 ) + { + m_buttonFull->Draw(); + } + + if ( m_buttonClose != 0 ) + { + m_buttonClose->Draw(); + } + } + + for ( i=0 ; iDraw(); + } + } +} + +// Dessine un rectangle. + +void CWindow::DrawVertex(FPOINT pos, FPOINT dim, int icon) +{ + FPOINT p1, p2, uv1, uv2, corner; + float dp; + int i; + + dp = 0.5f/256.0f; + + if ( icon == 0 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 64.0f/256.0f; // bleu foncé transparent + uv1.y = 64.0f/256.0f; + uv2.x = 128.0f/256.0f; + uv2.y = 128.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 14.0f/640.0f; + corner.y = 14.0f/480.0f; + DrawIcon(pos, dim, uv1, uv2, corner, 8.0f/256.0f); + } + else if ( icon == 1 ) + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 128.0f/256.0f; // jaune tooltip + uv1.y = 0.0f/256.0f; + uv2.x = 224.0f/256.0f; + uv2.y = 16.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2, 8.0f/256.0f); + } + else if ( icon == 2 ) + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 128.0f/256.0f; // jaune + uv1.y = 16.0f/256.0f; + uv2.x = 224.0f/256.0f; + uv2.y = 32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2, 8.0f/256.0f); + } + else if ( icon == 3 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 0.0f/256.0f; // bleu transparent avec barre jaune supérieure + uv1.y = 64.0f/256.0f; + uv2.x = 64.0f/256.0f; + uv2.y = 128.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2, 8.0f/256.0f); + } + else if ( icon == 4 ) // SatCom ? + { + pos.x -= 50.0f/640.0f; + pos.y -= 30.0f/480.0f; + dim.x += 100.0f/640.0f; + dim.y += 60.0f/480.0f; + + m_engine->SetTexture("human.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 140.0f/256.0f; + uv1.y = 32.0f/256.0f; + uv2.x = 182.0f/256.0f; + uv2.y = 64.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2); // vêtement + + pos.x += 20.0f/640.0f; + pos.y -= 10.0f/480.0f; + dim.x -= 20.0f/640.0f; + dim.y += 0.0f/480.0f; + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTw); + uv1.x = 192.0f/256.0f; + uv1.y = 32.0f/256.0f; + uv2.x = 224.0f/256.0f; + uv2.y = 64.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 30.0f/640.0f; + corner.y = 30.0f/480.0f; + DrawIcon(pos, dim, uv1, uv2, corner, 5.0f/256.0f); // ombre + + pos.x += 0.0f/640.0f; + pos.y += 20.0f/480.0f; + dim.x -= 20.0f/640.0f; + dim.y -= 20.0f/480.0f; + + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 64.0f/256.0f; + uv1.y = 0.0f/256.0f; + uv2.x = 96.0f/256.0f; + uv2.y = 32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 14.0f/640.0f; + corner.y = 14.0f/480.0f; + DrawIcon(pos, dim, uv1, uv2, corner, 8.0f/256.0f); // ext. bleu + + pos.x += 20.0f/640.0f; + pos.y += 10.0f/480.0f; + dim.x -= 40.0f/640.0f; + dim.y -= 20.0f/480.0f; + + uv1.x = 96.0f/256.0f; + uv1.y = 0.0f/256.0f; + uv2.x = 128.0f/256.0f; + uv2.y = 32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 14.0f/640.0f; + corner.y = 14.0f/480.0f; + DrawIcon(pos, dim, uv1, uv2, corner, 8.0f/256.0f); // int. bleu + + pos.x += 10.0f/640.0f; + pos.y += 10.0f/480.0f; + dim.x -= 20.0f/640.0f; + dim.y -= 20.0f/480.0f; + + m_engine->SetTexture("button3.tga"); + uv1.x = 0.0f/256.0f; + uv1.y = 224.0f/256.0f; + uv2.x = 32.0f/256.0f; + uv2.y = 256.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2); // fond bleu foncé + + m_engine->SetTexture("button2.tga"); + uv1.x = 224.0f/256.0f; + uv1.y = 224.0f/256.0f; + uv2.x = 249.0f/256.0f; + uv2.y = 235.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + pos.x = 20.0f/640.0f; + pos.y = 70.0f/480.0f; + dim.x = 25.0f/640.0f; + dim.y = 11.0f/480.0f; + for ( i=0 ; i<5 ; i++ ) + { + DrawIcon(pos, dim, uv1, uv2); // = inf/gauche + pos.y += 15.0f/480.0f; + } + pos.y = (480.0f-70.0f-11.0f)/480.0f; + for ( i=0 ; i<5 ; i++ ) + { + DrawIcon(pos, dim, uv1, uv2); // = sup/gauche + pos.y -= 15.0f/480.0f; + } + pos.x = (640.0f-25.0f-20.0f)/640.0f; + pos.y = 70.0f/480.0f; + for ( i=0 ; i<5 ; i++ ) + { + DrawIcon(pos, dim, uv1, uv2); // = inf/droite + pos.y += 15.0f/480.0f; + } + pos.y = (480.0f-70.0f-11.0f)/480.0f; + for ( i=0 ; i<5 ; i++ ) + { + DrawIcon(pos, dim, uv1, uv2); // = sup/droite + pos.y -= 15.0f/480.0f; + } + + uv1.x = 208.0f/256.0f; + uv1.y = 224.0f/256.0f; + uv2.x = 224.0f/256.0f; + uv2.y = 240.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + dim.x = 10.0f/640.0f; + dim.y = 10.0f/480.0f; + pos.x = 534.0f/640.0f; + pos.y = 430.0f/480.0f; + for ( i=0 ; i<3 ; i++ ) + { + DrawIcon(pos, dim, uv1, uv2); // micro + pos.x += 12.0f/640.0f; + } + pos.x = 528.0f/640.0f; + pos.y -= 12.0f/480.0f; + for ( i=0 ; i<4 ; i++ ) + { + DrawIcon(pos, dim, uv1, uv2); // micro + pos.x += 12.0f/640.0f; + } + pos.x = 534.0f/640.0f; + pos.y -= 12.0f/480.0f; + for ( i=0 ; i<3 ; i++ ) + { + DrawIcon(pos, dim, uv1, uv2); // micro + pos.x += 12.0f/640.0f; + } + } + else if ( icon == 5 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 64.0f/256.0f; // vert transparent + uv1.y = 160.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 176.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2, 8.0f/256.0f); + } + else if ( icon == 6 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 64.0f/256.0f; // rouge transparent + uv1.y = 176.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 192.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2, 8.0f/256.0f); + } + else if ( icon == 7 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 64.0f/256.0f; // bleu transparent + uv1.y = 192.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 208.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2, 8.0f/256.0f); + } + else if ( icon == 8 ) + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 0.0f/256.0f; // orange opaque + uv1.y = 0.0f/256.0f; + uv2.x = 32.0f/256.0f; + uv2.y = 32.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 14.0f/640.0f; + corner.y = 14.0f/480.0f; + DrawIcon(pos, dim, uv1, uv2, corner, 8.0f/256.0f); + } + else if ( icon == 9 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 32.0f/256.0f; // gris opaque + uv1.y = 32.0f/256.0f; + uv2.x = 64.0f/256.0f; + uv2.y = 64.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 14.0f/640.0f; + corner.y = 14.0f/480.0f; + DrawIcon(pos, dim, uv1, uv2, corner, 8.0f/256.0f); + } + else if ( icon == 10 ) + { + // rien (dans l'image de fond) ! + } + else if ( icon == 11 ) + { + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATETTb); + uv1.x = 64.0f/256.0f; // jaune transparent + uv1.y = 224.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 240.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + DrawIcon(pos, dim, uv1, uv2, 8.0f/256.0f); + } + else if ( icon == 12 ) + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 128.0f/256.0f; // gris sale opaque + uv1.y = 128.0f/256.0f; + uv2.x = 160.0f/256.0f; + uv2.y = 160.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 6.0f/640.0f; + corner.y = 6.0f/480.0f; + DrawIcon(pos, dim, uv1, uv2, corner, 5.0f/256.0f); + } + else if ( icon == 13 ) + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 192.0f/256.0f; // bleu sale opaque + uv1.y = 128.0f/256.0f; + uv2.x = 224.0f/256.0f; + uv2.y = 160.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 6.0f/640.0f; + corner.y = 6.0f/480.0f; + DrawIcon(pos, dim, uv1, uv2, corner, 5.0f/256.0f); + } + else if ( icon == 14 ) + { + m_engine->SetTexture("button1.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 160.0f/256.0f; // rouge sale opaque + uv1.y = 128.0f/256.0f; + uv2.x = 192.0f/256.0f; + uv2.y = 160.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + corner.x = 6.0f/640.0f; + corner.y = 6.0f/480.0f; + DrawIcon(pos, dim, uv1, uv2, corner, 5.0f/256.0f); + } +} + +// Dessine des hachures. + +void CWindow::DrawHach(FPOINT pos, FPOINT dim) +{ +#if _NEWLOOK +#else + FPOINT ppos, ddim, uv1, uv2; + float dp, max, ndim; + BOOL bStop; + + dp = 0.5f/256.0f; + + m_engine->SetTexture("button2.tga"); + m_engine->SetState(D3DSTATENORMAL); + uv1.x = 64.0f/256.0f; // hachures + uv1.y = 208.0f/256.0f; + uv2.x = 145.0f/256.0f; + uv2.y = 224.0f/256.0f; + uv1.x += dp; + uv1.y += dp; + uv2.x -= dp; + uv2.y -= dp; + + max = dim.y*(uv2.x-uv1.x)/(uv2.y-uv1.y); + + ppos = pos; + ddim = dim; + bStop = FALSE; + do + { + ddim.x = max; + if ( ppos.x+ddim.x > pos.x+dim.x ) + { + ndim = pos.x+dim.x-ppos.x; + uv2.x = uv1.x+(uv2.x-uv1.x)*(ndim/ddim.x); + ddim.x = ndim; + bStop = TRUE; + } + DrawIcon(ppos, ddim, uv1, uv2); + + ppos.x += ddim.x; + } + while ( !bStop ); +#endif +} + diff --git a/src/window.h b/src/window.h new file mode 100644 index 00000000..dfccfbd3 --- /dev/null +++ b/src/window.h @@ -0,0 +1,132 @@ +// window.h + +#ifndef _WINDOW_H_ +#define _WINDOW_H_ + + +#include "control.h" + + +class CD3DEngine; +class CButton; +class CColor; +class CCheck; +class CKey; +class CGroup; +class CImage; +class CLabel; +class CEdit; +class CEditValue; +class CScroll; +class CSlider; +class CList; +class CShortcut; +class CMap; +class CGauge; +class CCompass; +class CTarget; + + +#define MAXWINDOW 100 + + +class CWindow : public CControl +{ +public: + CWindow(CInstanceManager* iMan); + ~CWindow(); + + void Flush(); + BOOL Create(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CButton* CreateButton(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CColor* CreateColor(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CCheck* CreateCheck(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CKey* CreateKey(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CGroup* CreateGroup(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CImage* CreateImage(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CLabel* CreateLabel(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg, char *name); + CEdit* CreateEdit(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CEditValue* CreateEditValue(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CScroll* CreateScroll(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CSlider* CreateSlider(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CList* CreateList(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg, float expand=1.2f); + CShortcut* CreateShortcut(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CMap* CreateMap(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CGauge* CreateGauge(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CCompass* CreateCompass(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + CTarget* CreateTarget(FPOINT pos, FPOINT dim, int icon, EventMsg eventMsg); + BOOL DeleteControl(EventMsg eventMsg); + CControl* SearchControl(EventMsg eventMsg); + + EventMsg RetEventMsgReduce(); + EventMsg RetEventMsgFull(); + EventMsg RetEventMsgClose(); + + void SetName(char* name); + + void SetTrashEvent(BOOL bTrash); + BOOL RetTrashEvent(); + + void SetPos(FPOINT pos); + void SetDim(FPOINT dim); + + void SetMinDim(FPOINT dim); + void SetMaxDim(FPOINT dim); + FPOINT RetMinDim(); + FPOINT RetMaxDim(); + + void SetMovable(BOOL bMode); + BOOL RetMovable(); + + void SetRedim(BOOL bMode); + BOOL RetRedim(); + + void SetClosable(BOOL bMode); + BOOL RetClosable(); + + void SetMaximized(BOOL bMaxi); + BOOL RetMaximized(); + void SetMinimized(BOOL bMini); + BOOL RetMinimized(); + void SetFixed(BOOL bFix); + BOOL RetFixed(); + + BOOL GetTooltip(FPOINT pos, char* name); + + BOOL EventProcess(const Event &event); + + void Draw(); + +protected: + int BorderDetect(FPOINT pos); + void AdjustButtons(); + void MoveAdjust(); + void DrawVertex(FPOINT pos, FPOINT dim, int icon); + void DrawHach(FPOINT pos, FPOINT dim); + +protected: + CControl* m_table[MAXWINDOW]; + + BOOL m_bTrashEvent; + BOOL m_bMaximized; + BOOL m_bMinimized; + BOOL m_bFixed; + + FPOINT m_minDim; + FPOINT m_maxDim; + + CButton* m_buttonReduce; + CButton* m_buttonFull; + CButton* m_buttonClose; + + BOOL m_bMovable; + BOOL m_bRedim; + BOOL m_bClosable; + BOOL m_bCapture; + FPOINT m_pressPos; + int m_pressFlags; + D3DMouse m_pressMouse; +}; + + +#endif //_WINDOW_H_ diff --git a/src/winmain.aps b/src/winmain.aps new file mode 100644 index 0000000000000000000000000000000000000000..6a1e0dfce100b4afdc58001a54c6cb629b54916d GIT binary patch literal 40696 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 +#include "D3DApp.h" + + +//----------------------------------------------------------------------------- +// Name: WinMain() +// Desc: Entry point to the program. Initializes everything, and goes into a +// message-processing loop. Idle time is used to render the scene. +//----------------------------------------------------------------------------- +INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT ) +{ + CD3DApplication d3dApp; + + if( FAILED( d3dApp.Create( hInst, strCmdLine ) ) ) + return 0; + + return d3dApp.Run(); +} + + diff --git a/src/winmain.rc b/src/winmain.rc new file mode 100644 index 00000000..547d4c25 --- /dev/null +++ b/src/winmain.rc @@ -0,0 +1,265 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define IDC_STATIC -1 +#include + + + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MAIN_ICON ICON DISCARDABLE "DirectX.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#define IDC_STATIC -1\r\n" + "#include \r\n" + "\r\n" + "\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAIN_ACCEL ACCELERATORS DISCARDABLE +BEGIN + VK_F12, IDM_CHANGEDEVICE, VIRTKEY, SHIFT, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 165 + TOPMARGIN, 7 + BOTTOMMARGIN, 117 + END + + IDD_CHANGEDEVICE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 174 + TOPMARGIN, 7 + BOTTOMMARGIN, 83 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUT DIALOG DISCARDABLE 0, 0, 172, 124 +STYLE DS_SYSMODAL | DS_MODALFRAME | DS_NOIDLEMSG | DS_SETFOREGROUND | + DS_3DLOOK | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "About" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,115,103,50,14 + ICON IDI_MAIN_ICON,IDC_STATIC,5,5,20,20 + LTEXT "Robot Sample",IDC_STATIC,35,5,46,8 + LTEXT "Copyright (c) 1999-2000 EPSITEC SA",IDC_STATIC,35,15, + 120,8 + LTEXT "About",IDC_STATIC,62,52,20,8 + LTEXT "Select Driver / Device / Mode",IDC_STATIC,62,62,97,8 + CTEXT "",IDC_STATIC,12,72,45,8 + LTEXT "Toggle Fullscreen / Windowed",IDC_STATIC,62,72,98,8 + LTEXT "Exit",IDC_STATIC,62,82,12,8 + CTEXT "",IDC_STATIC,12,52,45,8 + CTEXT "",IDC_STATIC,12,62,45,8 + CTEXT "",IDC_STATIC,12,82,45,8 + GROUPBOX "Usage",IDC_STATIC,7,42,160,55 + LTEXT "Daniel Roux, version 0.1 Janvier 2000",IDC_STATIC,35,26, + 121,8 +END + +IDD_CHANGEDEVICE DIALOG DISCARDABLE 0, 0, 182, 90 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Change device" +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "&Device selection",-1,5,5,115,40 + COMBOBOX IDC_DEVICE_COMBO,10,15,105,100,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Use desktop &window",IDC_WINDOWED_CHECKBOX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,30,85,10 + GROUPBOX "Fullscreen &modes",IDC_FULLSCREEN_TEXT,5,45,115,40 + COMBOBOX IDC_MODE_COMBO,10,55,105,100,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "&Stereoscopic viewing",IDC_STEREO_CHECKBOX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,70,85,9 + DEFPUSHBUTTON "OK",IDOK,125,5,50,14 + PUSHBUTTON "Cancel",IDCANCEL,125,25,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENU MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Go/stop\tEnter", IDM_TOGGLESTART + MENUITEM "&Single step\tSpace", IDM_SINGLESTEP + MENUITEM SEPARATOR + MENUITEM "&About...\tF1", IDM_ABOUT + MENUITEM "&Change device...\tF2", IDM_CHANGEDEVICE + MENUITEM SEPARATOR + MENUITEM "E&xit\tESC", IDM_EXIT + END +END + +IDR_POPUP MENU DISCARDABLE +BEGIN + POPUP "Popup" + BEGIN + MENUITEM "&Go/stop", IDM_TOGGLESTART + MENUITEM "&Single step", IDM_SINGLESTEP + MENUITEM SEPARATOR + MENUITEM "&About...", IDM_ABOUT + MENUITEM "&Change device...", IDM_CHANGEDEVICE + MENUITEM SEPARATOR + MENUITEM "E&xit", IDM_EXIT + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// French (Switzerland) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRS) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH_SWISS +#pragma code_page(1252) +#endif //_WIN32 + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "100c04b0" + BEGIN + VALUE "CompanyName", "www.epsitec.com\0" + VALUE "FileDescription", "CeeBot\0" + VALUE "FileVersion", "Version 1.0\0" + VALUE "InternalName", "CeeBot\0" + VALUE "LegalCopyright", "Copyright © 2001 by EPSITEC SA\0" + VALUE "OriginalFilename", "ceebot.exe\0" + VALUE "ProductName", "EPSITEC CeeBot\0" + VALUE "ProductVersion", "Version 1.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x100c, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// Cursor +// + +IDC_CURSORHAND CURSOR DISCARDABLE "cursor1.cur" +IDC_CURSORSCROLLL CURSOR DISCARDABLE "cursorha.cur" +IDC_CURSORSCROLLR CURSOR DISCARDABLE "cursorsc.cur" +IDC_CURSORSCROLLU CURSOR DISCARDABLE "cur00001.cur" +IDC_CURSORSCROLLD CURSOR DISCARDABLE "cur00002.cur" +IDC_CURSORTARGET CURSOR DISCARDABLE "cur00003.cur" +#endif // French (Switzerland) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED +