summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon Robertshaw <simon@hardwired.org.uk>2012-11-17 19:44:09 (GMT)
committer Simon Robertshaw <simon@hardwired.org.uk>2012-11-17 19:44:09 (GMT)
commit058a2edd75debbd0297f92572316daa704bd379f (patch)
treead303f091f9a08b209b91eb34a9fcad996a3de69 /src
parente3594aba9e05c6865d396418c028049cda92c2f3 (diff)
parent7a21ae192fe19868539956f3fe28e62b2c7c4429 (diff)
downloadpowder-058a2edd75debbd0297f92572316daa704bd379f.zip
powder-058a2edd75debbd0297f92572316daa704bd379f.tar.gz
Merge branch 'master' of github.com:FacialTurd/PowderToypp
Diffstat (limited to 'src')
-rw-r--r--src/Activity.h42
-rw-r--r--src/Config.h214
-rw-r--r--src/Controller.h27
-rw-r--r--src/Format.cpp369
-rw-r--r--src/Format.h33
-rw-r--r--src/Misc.cpp717
-rw-r--r--src/Misc.h115
-rw-r--r--src/PowderToy.h3
-rw-r--r--src/PowderToyRenderer.cpp120
-rw-r--r--src/PowderToySDL.cpp606
-rw-r--r--src/Singleton.h16
-rw-r--r--src/Style.cpp26
-rw-r--r--src/Style.h35
-rw-r--r--src/Update.cpp200
-rw-r--r--src/Update.h16
-rw-r--r--src/bson/BSON.cpp1122
-rw-r--r--src/bson/BSON.h1205
-rw-r--r--src/cajun/elements.cpp403
-rw-r--r--src/cajun/elements.h337
-rw-r--r--src/cajun/reader.cpp534
-rw-r--r--src/cajun/reader.h148
-rw-r--r--src/cajun/visitor.h65
-rw-r--r--src/cajun/writer.cpp178
-rw-r--r--src/cajun/writer.h78
-rw-r--r--src/cat/CommandInterface.cpp118
-rw-r--r--src/cat/CommandInterface.h44
-rw-r--r--src/cat/LegacyLuaAPI.cpp1828
-rw-r--r--src/cat/LuaBit.cpp192
-rw-r--r--src/cat/LuaBit.h32
-rw-r--r--src/cat/LuaButton.cpp114
-rw-r--r--src/cat/LuaButton.h33
-rw-r--r--src/cat/LuaCheckbox.cpp114
-rw-r--r--src/cat/LuaCheckbox.h33
-rw-r--r--src/cat/LuaComponent.cpp82
-rw-r--r--src/cat/LuaComponent.h33
-rw-r--r--src/cat/LuaLabel.cpp56
-rw-r--r--src/cat/LuaLabel.h29
-rw-r--r--src/cat/LuaLuna.h160
-rw-r--r--src/cat/LuaProgressBar.cpp71
-rw-r--r--src/cat/LuaProgressBar.h30
-rw-r--r--src/cat/LuaScriptHelper.h144
-rw-r--r--src/cat/LuaScriptInterface.cpp1706
-rw-r--r--src/cat/LuaScriptInterface.h128
-rw-r--r--src/cat/LuaSlider.cpp112
-rw-r--r--src/cat/LuaSlider.h33
-rw-r--r--src/cat/LuaTextbox.cpp116
-rw-r--r--src/cat/LuaTextbox.h33
-rw-r--r--src/cat/LuaWindow.cpp569
-rw-r--r--src/cat/LuaWindow.h81
-rw-r--r--src/cat/TPTSTypes.cpp113
-rw-r--r--src/cat/TPTSTypes.h119
-rw-r--r--src/cat/TPTScriptInterface.cpp488
-rw-r--r--src/cat/TPTScriptInterface.h33
-rw-r--r--src/client/Client.cpp2351
-rw-r--r--src/client/Client.h176
-rw-r--r--src/client/ClientListener.h24
-rw-r--r--src/client/GameSave.cpp2092
-rw-r--r--src/client/GameSave.h112
-rw-r--r--src/client/HTTP.cpp1104
-rw-r--r--src/client/HTTP.h45
-rw-r--r--src/client/MD5.cpp231
-rw-r--r--src/client/MD5.h18
-rw-r--r--src/client/SaveFile.cpp80
-rw-r--r--src/client/SaveFile.h38
-rw-r--r--src/client/SaveInfo.cpp128
-rw-r--r--src/client/SaveInfo.h78
-rw-r--r--src/client/ThumbnailBroker.cpp347
-rw-r--r--src/client/ThumbnailBroker.h108
-rw-r--r--src/client/ThumbnailListener.h12
-rw-r--r--src/client/User.h38
-rw-r--r--src/colourpicker/ColourPickerActivity.cpp311
-rw-r--r--src/colourpicker/ColourPickerActivity.h43
-rw-r--r--src/console/ConsoleCommand.h30
-rw-r--r--src/console/ConsoleController.cpp81
-rw-r--r--src/console/ConsoleController.h38
-rw-r--r--src/console/ConsoleModel.cpp82
-rw-r--r--src/console/ConsoleModel.h35
-rw-r--r--src/console/ConsoleView.cpp106
-rw-r--r--src/console/ConsoleView.h37
-rw-r--r--src/dialogues/ConfirmPrompt.cpp159
-rw-r--r--src/dialogues/ConfirmPrompt.h32
-rw-r--r--src/dialogues/ErrorMessage.cpp85
-rw-r--r--src/dialogues/ErrorMessage.h30
-rw-r--r--src/dialogues/InformationMessage.cpp61
-rw-r--r--src/dialogues/InformationMessage.h20
-rw-r--r--src/dialogues/LegacyDialogues.h11
-rw-r--r--src/dialogues/TextPrompt.cpp121
-rw-r--r--src/dialogues/TextPrompt.h35
-rw-r--r--src/elementsearch/ElementSearchActivity.cpp192
-rw-r--r--src/elementsearch/ElementSearchActivity.h39
-rw-r--r--src/filebrowser/FileBrowserActivity.cpp290
-rw-r--r--src/filebrowser/FileBrowserActivity.h62
-rw-r--r--src/game/Brush.cpp48
-rw-r--r--src/game/Brush.h114
-rw-r--r--src/game/DecorationTool.h43
-rw-r--r--src/game/EllipseBrush.h46
-rw-r--r--src/game/GameController.cpp1326
-rw-r--r--src/game/GameController.h147
-rw-r--r--src/game/GameModel.cpp1133
-rw-r--r--src/game/GameModel.h206
-rw-r--r--src/game/GameModelException.h26
-rw-r--r--src/game/GameView.cpp2079
-rw-r--r--src/game/GameView.h187
-rw-r--r--src/game/Menu.h58
-rw-r--r--src/game/Notification.h23
-rw-r--r--src/game/PropertyTool.cpp235
-rw-r--r--src/game/QuickOption.h76
-rw-r--r--src/game/QuickOptions.h120
-rw-r--r--src/game/RenderPreset.h19
-rw-r--r--src/game/SampleTool.cpp67
-rw-r--r--src/game/SignTool.cpp289
-rw-r--r--src/game/Tool.cpp216
-rw-r--r--src/game/Tool.h190
-rw-r--r--src/game/ToolButton.cpp99
-rw-r--r--src/game/ToolButton.h25
-rw-r--r--src/game/TriangleBrush.h46
-rw-r--r--src/graphics/DrawMethodsDef.inc17
-rw-r--r--src/graphics/Graphics.cpp1028
-rw-r--r--src/graphics/Graphics.h248
-rw-r--r--src/graphics/OpenGLDrawMethods.inl350
-rw-r--r--src/graphics/OpenGLGraphics.cpp95
-rw-r--r--src/graphics/OpenGLHeaders.h24
-rw-r--r--src/graphics/RasterDrawMethods.inl366
-rw-r--r--src/graphics/RasterGraphics.cpp45
-rw-r--r--src/graphics/Renderer.cpp2719
-rw-r--r--src/graphics/Renderer.h184
-rw-r--r--src/interface/Appearance.cpp61
-rw-r--r--src/interface/Appearance.h64
-rw-r--r--src/interface/Border.h69
-rw-r--r--src/interface/Button.cpp222
-rw-r--r--src/interface/Button.h68
-rw-r--r--src/interface/Checkbox.cpp112
-rw-r--r--src/interface/Checkbox.h46
-rw-r--r--src/interface/Colour.h24
-rw-r--r--src/interface/Component.cpp244
-rw-r--r--src/interface/Component.h224
-rw-r--r--src/interface/ContextMenu.cpp99
-rw-r--r--src/interface/ContextMenu.h40
-rw-r--r--src/interface/DropDown.cpp204
-rw-r--r--src/interface/DropDown.h48
-rw-r--r--src/interface/Engine.cpp306
-rw-r--r--src/interface/Engine.h108
-rw-r--r--src/interface/Keys.h84
-rw-r--r--src/interface/Label.cpp418
-rw-r--r--src/interface/Label.h72
-rw-r--r--src/interface/LuaProgressBar.h30
-rw-r--r--src/interface/Panel.cpp454
-rw-r--r--src/interface/Panel.h150
-rw-r--r--src/interface/Platform.h71
-rw-r--r--src/interface/Point.h146
-rw-r--r--src/interface/ProgressBar.cpp81
-rw-r--r--src/interface/ProgressBar.h21
-rw-r--r--src/interface/RichLabel.cpp198
-rw-r--r--src/interface/RichLabel.h39
-rw-r--r--src/interface/SaveButton.cpp415
-rw-r--r--src/interface/SaveButton.h83
-rw-r--r--src/interface/ScrollPanel.cpp121
-rw-r--r--src/interface/ScrollPanel.h25
-rw-r--r--src/interface/Slider.cpp149
-rw-r--r--src/interface/Slider.h47
-rw-r--r--src/interface/Spinner.cpp45
-rw-r--r--src/interface/Spinner.h30
-rw-r--r--src/interface/Textbox.cpp707
-rw-r--r--src/interface/Textbox.h108
-rw-r--r--src/interface/Window.cpp558
-rw-r--r--src/interface/Window.h135
-rw-r--r--src/localbrowser/LocalBrowserController.cpp172
-rw-r--r--src/localbrowser/LocalBrowserController.h42
-rw-r--r--src/localbrowser/LocalBrowserModel.cpp143
-rw-r--r--src/localbrowser/LocalBrowserModel.h45
-rw-r--r--src/localbrowser/LocalBrowserModelException.h23
-rw-r--r--src/localbrowser/LocalBrowserView.cpp220
-rw-r--r--src/localbrowser/LocalBrowserView.h44
-rw-r--r--src/login/LoginController.cpp59
-rw-r--r--src/login/LoginController.h35
-rw-r--r--src/login/LoginModel.cpp67
-rw-r--r--src/login/LoginModel.h35
-rw-r--r--src/login/LoginView.cpp151
-rw-r--r--src/login/LoginView.h45
-rw-r--r--src/options/OptionsController.cpp113
-rw-r--r--src/options/OptionsController.h42
-rw-r--r--src/options/OptionsModel.cpp145
-rw-r--r--src/options/OptionsModel.h48
-rw-r--r--src/options/OptionsView.cpp240
-rw-r--r--src/options/OptionsView.h40
-rw-r--r--src/pim/Generator.cpp518
-rw-r--r--src/pim/Generator.h178
-rw-r--r--src/pim/Machine.cpp637
-rw-r--r--src/pim/Machine.h120
-rw-r--r--src/pim/Opcodes.h12
-rw-r--r--src/pim/Opcodes.inl28
-rw-r--r--src/pim/Parser.cpp653
-rw-r--r--src/pim/Parser.h79
-rw-r--r--src/pim/Scanner.cpp199
-rw-r--r--src/pim/Scanner.h22
-rw-r--r--src/pim/Token.cpp51
-rw-r--r--src/pim/Token.h83
-rw-r--r--src/powdertoyjava/OpenGLCanvasMacOS.h35
-rw-r--r--src/powdertoyjava/OpenGLCanvasMacOS.mm169
-rw-r--r--src/powdertoyjava/OpenGLCanvasWin32.cpp169
-rw-r--r--src/powdertoyjava/OpenGLCanvasWin32.h35
-rw-r--r--src/powdertoyjava/PowderToyJava.cpp77
-rw-r--r--src/powdertoyjava/PowderToyJava.h21
-rw-r--r--src/preview/Comment.h34
-rw-r--r--src/preview/PreviewController.cpp196
-rw-r--r--src/preview/PreviewController.h52
-rw-r--r--src/preview/PreviewModel.cpp320
-rw-r--r--src/preview/PreviewModel.h89
-rw-r--r--src/preview/PreviewModelException.h26
-rw-r--r--src/preview/PreviewView.cpp560
-rw-r--r--src/preview/PreviewView.h78
-rw-r--r--src/render/RenderController.cpp74
-rw-r--r--src/render/RenderController.h36
-rw-r--r--src/render/RenderModel.cpp143
-rw-r--r--src/render/RenderModel.h42
-rw-r--r--src/render/RenderView.cpp409
-rw-r--r--src/render/RenderView.h50
-rw-r--r--src/save/LocalSaveActivity.cpp138
-rw-r--r--src/save/LocalSaveActivity.h39
-rw-r--r--src/save/ServerSaveActivity.cpp261
-rw-r--r--src/save/ServerSaveActivity.h48
-rw-r--r--src/search/SearchController.cpp357
-rw-r--r--src/search/SearchController.h50
-rw-r--r--src/search/SearchModel.cpp283
-rw-r--r--src/search/SearchModel.h83
-rw-r--r--src/search/SearchView.cpp703
-rw-r--r--src/search/SearchView.h74
-rw-r--r--src/search/Thumbnail.cpp82
-rw-r--r--src/search/Thumbnail.h25
-rw-r--r--src/simulation/Air.cpp321
-rw-r--r--src/simulation/Air.h37
-rw-r--r--src/simulation/Element.h23
-rw-r--r--src/simulation/ElementGraphics.h55
-rw-r--r--src/simulation/Elements.h100
-rw-r--r--src/simulation/GOLMenu.h20
-rw-r--r--src/simulation/Gravity.cpp527
-rw-r--r--src/simulation/Gravity.h123
-rw-r--r--src/simulation/MenuSection.h20
-rw-r--r--src/simulation/Particle.cpp27
-rw-r--r--src/simulation/Particle.h31
-rw-r--r--src/simulation/Player.h23
-rw-r--r--src/simulation/Sample.h29
-rw-r--r--src/simulation/SaveRenderer.cpp183
-rw-r--r--src/simulation/SaveRenderer.h37
-rw-r--r--src/simulation/Sign.cpp55
-rw-r--r--src/simulation/Sign.h26
-rw-r--r--src/simulation/Simulation.cpp4835
-rw-r--r--src/simulation/Simulation.h214
-rw-r--r--src/simulation/SimulationData.cpp344
-rw-r--r--src/simulation/SimulationData.h178
-rw-r--r--src/simulation/Snapshot.h55
-rw-r--r--src/simulation/StorageClasses.h54
-rw-r--r--src/simulation/StructProperty.h39
-rw-r--r--src/simulation/Tools.h7
-rw-r--r--src/simulation/WallType.h25
-rw-r--r--src/simulation/elements/116.cpp49
-rw-r--r--src/simulation/elements/146.cpp49
-rw-r--r--src/simulation/elements/ACEL.cpp85
-rw-r--r--src/simulation/elements/ACID.cpp144
-rw-r--r--src/simulation/elements/AMTR.cpp79
-rw-r--r--src/simulation/elements/ANAR.cpp78
-rw-r--r--src/simulation/elements/ARAY.cpp154
-rw-r--r--src/simulation/elements/BANG.cpp131
-rw-r--r--src/simulation/elements/BCLN.cpp99
-rw-r--r--src/simulation/elements/BCOL.cpp146
-rw-r--r--src/simulation/elements/BGLA.cpp49
-rw-r--r--src/simulation/elements/BHOL.cpp49
-rw-r--r--src/simulation/elements/BIZR.cpp124
-rw-r--r--src/simulation/elements/BIZRG.cpp124
-rw-r--r--src/simulation/elements/BIZRS.cpp124
-rw-r--r--src/simulation/elements/BMTL.cpp80
-rw-r--r--src/simulation/elements/BOMB.cpp113
-rw-r--r--src/simulation/elements/BOYL.cpp94
-rw-r--r--src/simulation/elements/BRAY.cpp109
-rw-r--r--src/simulation/elements/BRCK.cpp65
-rw-r--r--src/simulation/elements/BREC.cpp64
-rw-r--r--src/simulation/elements/BRMT.cpp85
-rw-r--r--src/simulation/elements/BTRY.cpp75
-rw-r--r--src/simulation/elements/BVBR.cpp50
-rw-r--r--src/simulation/elements/C5.cpp75
-rw-r--r--src/simulation/elements/CAUS.cpp86
-rw-r--r--src/simulation/elements/CBNW.cpp153
-rw-r--r--src/simulation/elements/CLNE.cpp91
-rw-r--r--src/simulation/elements/CLST.cpp101
-rw-r--r--src/simulation/elements/CNCT.cpp49
-rw-r--r--src/simulation/elements/CO2.cpp106
-rw-r--r--src/simulation/elements/COAL.cpp153
-rw-r--r--src/simulation/elements/CONV.cpp96
-rw-r--r--src/simulation/elements/DESL.cpp49
-rw-r--r--src/simulation/elements/DEST.cpp121
-rw-r--r--src/simulation/elements/DEUT.cpp149
-rw-r--r--src/simulation/elements/DLAY.cpp111
-rw-r--r--src/simulation/elements/DMG.cpp118
-rw-r--r--src/simulation/elements/DMND.cpp49
-rw-r--r--src/simulation/elements/DRIC.cpp49
-rw-r--r--src/simulation/elements/DSTW.cpp92
-rw-r--r--src/simulation/elements/DTEC.cpp99
-rw-r--r--src/simulation/elements/DUST.cpp49
-rw-r--r--src/simulation/elements/DYST.cpp49
-rw-r--r--src/simulation/elements/ELEC.cpp160
-rw-r--r--src/simulation/elements/EMBR.cpp126
-rw-r--r--src/simulation/elements/EMP.cpp183
-rw-r--r--src/simulation/elements/ETRD.cpp49
-rw-r--r--src/simulation/elements/EXOT.cpp211
-rw-r--r--src/simulation/elements/Element.cpp225
-rw-r--r--src/simulation/elements/Element.h63
-rw-r--r--src/simulation/elements/FIGH.cpp140
-rw-r--r--src/simulation/elements/FILT.cpp77
-rw-r--r--src/simulation/elements/FIRE.cpp201
-rw-r--r--src/simulation/elements/FIRW.cpp134
-rw-r--r--src/simulation/elements/FOG.cpp73
-rw-r--r--src/simulation/elements/FRAY.cpp80
-rw-r--r--src/simulation/elements/FRZW.cpp81
-rw-r--r--src/simulation/elements/FRZZ.cpp76
-rw-r--r--src/simulation/elements/FSEP.cpp86
-rw-r--r--src/simulation/elements/FUSE.cpp92
-rw-r--r--src/simulation/elements/FWRK.cpp120
-rw-r--r--src/simulation/elements/GAS.cpp49
-rw-r--r--src/simulation/elements/GBMB.cpp93
-rw-r--r--src/simulation/elements/GEL.cpp155
-rw-r--r--src/simulation/elements/GLAS.cpp62
-rw-r--r--src/simulation/elements/GLOW.cpp96
-rw-r--r--src/simulation/elements/GOO.cpp64
-rw-r--r--src/simulation/elements/GPMP.cpp94
-rw-r--r--src/simulation/elements/GRAV.cpp111
-rw-r--r--src/simulation/elements/GUNP.cpp49
-rw-r--r--src/simulation/elements/H2.cpp124
-rw-r--r--src/simulation/elements/HFLM.cpp75
-rw-r--r--src/simulation/elements/HSWC.cpp87
-rw-r--r--src/simulation/elements/ICEI.cpp76
-rw-r--r--src/simulation/elements/IGNT.cpp95
-rw-r--r--src/simulation/elements/INSL.cpp49
-rw-r--r--src/simulation/elements/INST.cpp49
-rw-r--r--src/simulation/elements/INVIS.cpp75
-rw-r--r--src/simulation/elements/INWR.cpp49
-rw-r--r--src/simulation/elements/IRON.cpp76
-rw-r--r--src/simulation/elements/ISOZ.cpp65
-rw-r--r--src/simulation/elements/ISZS.cpp65
-rw-r--r--src/simulation/elements/LAVA.cpp71
-rw-r--r--src/simulation/elements/LCRY.cpp154
-rw-r--r--src/simulation/elements/LIFE.cpp125
-rw-r--r--src/simulation/elements/LIGH.cpp359
-rw-r--r--src/simulation/elements/LNTG.cpp49
-rw-r--r--src/simulation/elements/LO2.cpp49
-rw-r--r--src/simulation/elements/LOLZ.cpp71
-rw-r--r--src/simulation/elements/LOVE.cpp71
-rw-r--r--src/simulation/elements/LRBD.cpp49
-rw-r--r--src/simulation/elements/MERC.cpp121
-rw-r--r--src/simulation/elements/METL.cpp49
-rw-r--r--src/simulation/elements/MORT.cpp57
-rw-r--r--src/simulation/elements/MWAX.cpp49
-rw-r--r--src/simulation/elements/NBHL.cpp60
-rw-r--r--src/simulation/elements/NBLE.cpp93
-rw-r--r--src/simulation/elements/NEUT.cpp207
-rw-r--r--src/simulation/elements/NICE.cpp49
-rw-r--r--src/simulation/elements/NITR.cpp49
-rw-r--r--src/simulation/elements/NONE.cpp66
-rw-r--r--src/simulation/elements/NSCN.cpp49
-rw-r--r--src/simulation/elements/NTCT.cpp58
-rw-r--r--src/simulation/elements/NWHL.cpp57
-rw-r--r--src/simulation/elements/O2.cpp104
-rw-r--r--src/simulation/elements/OIL.cpp49
-rw-r--r--src/simulation/elements/PBCN.cpp164
-rw-r--r--src/simulation/elements/PCLN.cpp154
-rw-r--r--src/simulation/elements/PHOT.cpp141
-rw-r--r--src/simulation/elements/PIPE.cpp516
-rw-r--r--src/simulation/elements/PLEX.cpp49
-rw-r--r--src/simulation/elements/PLNT.cpp126
-rw-r--r--src/simulation/elements/PLSM.cpp70
-rw-r--r--src/simulation/elements/PLUT.cpp60
-rw-r--r--src/simulation/elements/PPIP.cpp160
-rw-r--r--src/simulation/elements/PQRT.cpp168
-rw-r--r--src/simulation/elements/PRTI.cpp140
-rw-r--r--src/simulation/elements/PRTO.cpp177
-rw-r--r--src/simulation/elements/PSCN.cpp49
-rw-r--r--src/simulation/elements/PSTE.cpp49
-rw-r--r--src/simulation/elements/PSTS.cpp49
-rw-r--r--src/simulation/elements/PTCT.cpp58
-rw-r--r--src/simulation/elements/PUMP.cpp98
-rw-r--r--src/simulation/elements/PVOD.cpp91
-rw-r--r--src/simulation/elements/QRTZ.cpp168
-rw-r--r--src/simulation/elements/RBDM.cpp49
-rw-r--r--src/simulation/elements/REPL.cpp73
-rw-r--r--src/simulation/elements/RIME.cpp77
-rw-r--r--src/simulation/elements/SALT.cpp49
-rw-r--r--src/simulation/elements/SAND.cpp49
-rw-r--r--src/simulation/elements/SHLD1.cpp88
-rw-r--r--src/simulation/elements/SHLD2.cpp91
-rw-r--r--src/simulation/elements/SHLD3.cpp101
-rw-r--r--src/simulation/elements/SHLD4.cpp92
-rw-r--r--src/simulation/elements/SING.cpp155
-rw-r--r--src/simulation/elements/SLTW.cpp81
-rw-r--r--src/simulation/elements/SMKE.cpp68
-rw-r--r--src/simulation/elements/SNOW.cpp74
-rw-r--r--src/simulation/elements/SOAP.cpp291
-rw-r--r--src/simulation/elements/SPAWN.cpp60
-rw-r--r--src/simulation/elements/SPAWN2.cpp60
-rw-r--r--src/simulation/elements/SPNG.cpp198
-rw-r--r--src/simulation/elements/SPRK.cpp290
-rw-r--r--src/simulation/elements/STKM.cpp557
-rw-r--r--src/simulation/elements/STKM2.cpp56
-rw-r--r--src/simulation/elements/STNE.cpp49
-rw-r--r--src/simulation/elements/STOR.cpp112
-rw-r--r--src/simulation/elements/SWCH.cpp113
-rw-r--r--src/simulation/elements/TESC.cpp49
-rw-r--r--src/simulation/elements/THDR.cpp102
-rw-r--r--src/simulation/elements/THRM.cpp80
-rw-r--r--src/simulation/elements/TRON.cpp236
-rw-r--r--src/simulation/elements/TSNS.cpp93
-rw-r--r--src/simulation/elements/TTAN.cpp76
-rw-r--r--src/simulation/elements/URAN.cpp61
-rw-r--r--src/simulation/elements/VIBR.cpp235
-rw-r--r--src/simulation/elements/VINE.cpp91
-rw-r--r--src/simulation/elements/VOID.cpp49
-rw-r--r--src/simulation/elements/WARP.cpp95
-rw-r--r--src/simulation/elements/WATR.cpp89
-rw-r--r--src/simulation/elements/WAX.cpp49
-rw-r--r--src/simulation/elements/WHOL.cpp49
-rw-r--r--src/simulation/elements/WIFI.cpp101
-rw-r--r--src/simulation/elements/WIRE.cpp127
-rw-r--r--src/simulation/elements/WOOD.cpp70
-rw-r--r--src/simulation/elements/WTRV.cpp73
-rw-r--r--src/simulation/elements/YEST.cpp72
-rw-r--r--src/simulation/elements/dcel.cpp85
-rw-r--r--src/simulation/tools/AirTool.cpp22
-rw-r--r--src/simulation/tools/Cool.cpp23
-rw-r--r--src/simulation/tools/GravTool.cpp18
-rw-r--r--src/simulation/tools/Heat.cpp23
-rw-r--r--src/simulation/tools/NGrv.cpp18
-rw-r--r--src/simulation/tools/SimTool.cpp10
-rw-r--r--src/simulation/tools/SimTool.h23
-rw-r--r--src/simulation/tools/Vac.cpp22
-rw-r--r--src/tags/TagsController.cpp60
-rw-r--r--src/tags/TagsController.h32
-rw-r--r--src/tags/TagsModel.cpp85
-rw-r--r--src/tags/TagsModel.h31
-rw-r--r--src/tags/TagsModelException.h22
-rw-r--r--src/tags/TagsView.cpp168
-rw-r--r--src/tags/TagsView.h40
-rw-r--r--src/tasks/Task.cpp185
-rw-r--r--src/tasks/Task.h63
-rw-r--r--src/tasks/TaskListener.h21
-rw-r--r--src/tasks/TaskWindow.cpp133
-rw-r--r--src/tasks/TaskWindow.h38
-rw-r--r--src/tests/PowderInteractionMachine.cpp21
-rw-r--r--src/tests/VirtualMachineTest.cpp16
-rw-r--r--src/tests/test.c171
-rw-r--r--src/tests/test.qvmbin0 -> 2340 bytes
-rw-r--r--src/update/UpdateActivity.cpp172
-rw-r--r--src/update/UpdateActivity.h24
-rw-r--r--src/virtualmachine/Exceptions.h100
-rw-r--r--src/virtualmachine/JustInTime.cpp1144
-rw-r--r--src/virtualmachine/Operations.cpp356
-rw-r--r--src/virtualmachine/Operations.inl60
-rw-r--r--src/virtualmachine/Syscalls.cpp99
-rw-r--r--src/virtualmachine/Syscalls.inl14
-rw-r--r--src/virtualmachine/VirtualMachine.cpp405
-rw-r--r--src/virtualmachine/VirtualMachine.h282
458 files changed, 77707 insertions, 0 deletions
diff --git a/src/Activity.h b/src/Activity.h
new file mode 100644
index 0000000..0263467
--- /dev/null
+++ b/src/Activity.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "interface/Window.h"
+
+class Activity
+{
+public:
+ virtual void Exit() {}
+ virtual void Show() {}
+ virtual void Hide() {}
+ virtual ~Activity() {}
+};
+
+class WindowActivity: public ui::Window, public Activity
+{
+public:
+ WindowActivity(ui::Point position, ui::Point size) :
+ ui::Window(position, size)
+ {
+ Show();
+ }
+ virtual void Exit()
+ {
+ Hide();
+ SelfDestruct();
+ }
+ virtual void Show()
+ {
+ if(ui::Engine::Ref().GetWindow() != this)
+ {
+ ui::Engine::Ref().ShowWindow(this);
+ }
+ }
+ virtual void Hide()
+ {
+ if(ui::Engine::Ref().GetWindow() == this)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ }
+ virtual ~WindowActivity() {}
+}; \ No newline at end of file
diff --git a/src/Config.h b/src/Config.h
new file mode 100644
index 0000000..5d2a1a5
--- /dev/null
+++ b/src/Config.h
@@ -0,0 +1,214 @@
+/*
+ * Config.h
+ *
+ * Created on: Jan 5, 2012
+ * Author: Simon
+ */
+
+//#ifndef CONFIG_H_
+//#define CONFIG_H_
+
+
+#ifdef WIN
+#define PATH_SEP "\\"
+#else
+#define PATH_SEP "/"
+#endif
+
+//VersionInfoStart
+#ifndef SAVE_VERSION
+#define SAVE_VERSION 84
+#endif
+
+#ifndef MINOR_VERSION
+#define MINOR_VERSION 2
+#endif
+
+#ifndef BUILD_NUM
+#define BUILD_NUM 248
+#endif
+
+#ifndef SNAPSHOT_ID
+#define SNAPSHOT_ID 0
+#endif
+
+#ifndef STABLE
+#ifndef BETA
+#define BETA
+#define SNAPSHOT
+#endif
+#endif
+//VersionInfoEnd
+
+#if defined(SNAPSHOT)
+#define IDENT_RELTYPE "S"
+#elif defined(BETA)
+#define IDENT_RELTYPE "B"
+#else
+#define IDENT_RELTYPE "R"
+#endif
+
+#if defined(WIN)
+#if defined(_64BIT)
+#define IDENT_PLATFORM "WIN64"
+#else
+#define IDENT_PLATFORM "WIN32"
+#endif
+#elif defined(LIN)
+#if defined(_64BIT)
+#define IDENT_PLATFORM "LIN64"
+#else
+#define IDENT_PLATFORM "LIN32"
+#endif
+#elif defined(MACOSX)
+#define IDENT_PLATFORM "MACOSX"
+#else
+#define IDENT_PLATFORM "UNKNOWN"
+#endif
+
+#if defined(X86_SSE3)
+#define IDENT_BUILD "SSE3"
+#elif defined(X86_SSE2)
+#define IDENT_BUILD "SSE2"
+#elif defined(X86_SSE)
+#define IDENT_BUILD "SSE"
+#else
+#define IDENT_BUILD "NO"
+#endif
+
+#define IDENT_VERSION "G" //Change this if you're not Simon! It should be a single letter
+
+#define MTOS_EXPAND(str) #str
+#define MTOS(str) MTOS_EXPAND(str)
+
+#define SERVER "powdertoy.co.uk"
+#define SCRIPTSERVER "powdertoy.co.uk"
+#define STATICSERVER "static.powdertoy.co.uk"
+
+#define LOCAL_SAVE_DIR "Saves"
+
+#define STAMPS_DIR "stamps"
+
+#define APPDATA_SUBDIR "\\HardWIRED"
+
+//Number of unique thumbnails to have in cache at one time
+#define THUMB_CACHE_SIZE 256
+
+#ifndef M_PI
+#define M_PI 3.14159265f
+#endif
+#ifndef M_GRAV
+#define M_GRAV 6.67300e-1
+#endif
+
+//Number of asynchronous connections used to retrieve thumnails
+#define IMGCONNS 5
+//Not sure
+#define TIMEOUT 100
+//HTTP request timeout in seconds
+#define HTTP_TIMEOUT 10
+
+#ifdef RENDERER
+#define MENUSIZE 0
+#define BARSIZE 0
+#else
+#define MENUSIZE 40
+//#define MENUSIZE 20
+//#define BARSIZE 50
+#define BARSIZE 17
+#endif
+#define XRES 612
+#define YRES 384
+#define NPART XRES*YRES
+
+#define XCNTR 306
+#define YCNTR 192
+
+#define MAX_DISTANCE sqrt(pow((float)XRES, 2)+pow((float)YRES, 2))
+
+#define GRAV_DIFF
+
+#define MAXSIGNS 16
+#define TAG_MAX 256
+
+#define ZSIZE_D 16
+#define ZFACTOR_D 8
+extern unsigned char ZFACTOR;
+extern unsigned char ZSIZE;
+
+#define CELL 4
+#define ISTP (CELL/2)
+#define CFDS (4.0f/CELL)
+
+#define AIR_TSTEPP 0.3f
+#define AIR_TSTEPV 0.4f
+#define AIR_VADV 0.3f
+#define AIR_VLOSS 0.999f
+#define AIR_PLOSS 0.9999f
+
+#define GRID_X 5
+#define GRID_Y 4
+#define GRID_P 3
+#define GRID_S 6
+#define GRID_Z 3
+
+#define CATALOGUE_X 4
+#define CATALOGUE_Y 3
+#define CATALOGUE_S 6
+#define CATALOGUE_Z 3
+
+#define STAMP_MAX 240
+
+#define SAVE_OPS
+
+#define NGOL 24
+#define NGOLALT 24 //NGOL should be 24, but use this var until I find out why
+
+#define CIRCLE_BRUSH 0
+#define SQUARE_BRUSH 1
+#define TRI_BRUSH 2
+#define BRUSH_NUM 3
+
+#define SURF_RANGE 10
+#define NORMAL_MIN_EST 3
+#define NORMAL_INTERP 20
+#define NORMAL_FRAC 16
+
+#define REFRACT 0x80000000
+
+/* heavy flint glass, for awesome refraction/dispersion
+ this way you can make roof prisms easily */
+#define GLASS_IOR 1.9
+#define GLASS_DISP 0.07
+
+#ifdef WIN
+#define strcasecmp stricmp
+#endif
+#if defined(_MSC_VER)
+#define fmin min
+#define fminf min
+#define fmax max
+#define fmaxf max
+#endif
+
+#if defined(_MSC_VER)
+#define TPT_INLINE _inline
+#define TPT_NO_INLINE
+#elif defined(__llvm__)
+#define TPT_INLINE
+#define TPT_NO_INLINE
+#else
+#define TPT_INLINE inline
+#define TPT_NO_INLINE inline
+#endif
+
+#define SDEUT
+//#define REALHEAT
+
+#define DEBUG_PARTS 0x0001
+#define DEBUG_PARTCOUNT 0x0002
+#define DEBUG_DRAWTOOL 0x0004
+#define DEBUG_PERFORMANCE_CALC 0x0008
+#define DEBUG_PERFORMANCE_FRAME 0x0010
+
+//#endif /* CONFIG_H_ */
diff --git a/src/Controller.h b/src/Controller.h
new file mode 100644
index 0000000..12748be
--- /dev/null
+++ b/src/Controller.h
@@ -0,0 +1,27 @@
+/*
+ * Controller.h
+ *
+ * Created on: Jan 25, 2012
+ * Author: Simon
+ */
+
+#ifndef CONTROLLER_H_
+#define CONTROLLER_H_
+
+class ControllerCallback
+{
+public:
+ ControllerCallback() {}
+ virtual void ControllerExit() {}
+ virtual ~ControllerCallback() {}
+};
+
+class Controller
+{
+private:
+ virtual void Exit();
+ virtual void Show();
+ virtual void Hide();
+};
+
+#endif /* CONTROLLER_H_ */
diff --git a/src/Format.cpp b/src/Format.cpp
new file mode 100644
index 0000000..5f9741d
--- /dev/null
+++ b/src/Format.cpp
@@ -0,0 +1,369 @@
+
+#include <ctime>
+#include <string>
+#include <stdexcept>
+#include <iostream>
+#include <iterator>
+#include <zlib.h>
+#include <stdio.h>
+#include "Format.h"
+#include "graphics/Graphics.h"
+
+std::string format::URLEncode(std::string source)
+{
+ char * src = (char *)source.c_str();
+ char * dst = new char[(source.length()*3)+2];
+ std::fill(dst, dst+(source.length()*3)+2, 0);
+
+ char *d;
+ unsigned char *s;
+
+ for (d=dst; *d; d++) ;
+
+ for (s=(unsigned char *)src; *s; s++)
+ {
+ if ((*s>='0' && *s<='9') ||
+ (*s>='a' && *s<='z') ||
+ (*s>='A' && *s<='Z'))
+ *(d++) = *s;
+ else
+ {
+ *(d++) = '%';
+ *(d++) = hex[*s>>4];
+ *(d++) = hex[*s&15];
+ }
+ }
+ *d = 0;
+
+ std::string finalString(dst);
+ delete[] dst;
+ return finalString;
+}
+
+std::string format::UnixtimeToDate(time_t unixtime, std::string dateFormat)
+{
+ struct tm * timeData;
+ char buffer[128];
+
+ timeData = localtime(&unixtime);
+
+ strftime(buffer, 128, dateFormat.c_str(), timeData);
+ return std::string(buffer);
+}
+
+std::string format::UnixtimeToDateMini(time_t unixtime)
+{
+ time_t currentTime = time(NULL);
+ struct tm currentTimeData = *localtime(&currentTime);
+ struct tm timeData = *localtime(&unixtime);
+
+ if(currentTimeData.tm_year != timeData.tm_year)
+ {
+ return UnixtimeToDate(unixtime, "%b %Y");
+ }
+ else if(currentTimeData.tm_mon != timeData.tm_mon || currentTimeData.tm_mday != timeData.tm_mday)
+ {
+ return UnixtimeToDate(unixtime, "%d %B");
+ }
+ else
+ {
+ return UnixtimeToDate(unixtime, "%H:%M:%S");
+ }
+}
+
+std::vector<char> format::VideoBufferToPTI(const VideoBuffer & vidBuf)
+{
+ std::vector<char> data;
+ int dataSize = 0;
+ char * buffer = (char*)Graphics::ptif_pack(vidBuf.Buffer, vidBuf.Width, vidBuf.Height, &dataSize);
+
+ if(buffer)
+ {
+ data.insert(data.end(), buffer, buffer+dataSize);
+ free(buffer);
+ }
+
+ return data;
+}
+
+std::vector<char> format::VideoBufferToPPM(const VideoBuffer & vidBuf)
+{
+ std::vector<char> data;
+ char buffer[256];
+ sprintf(buffer, "P6\n%d %d\n255\n", vidBuf.Width, vidBuf.Height);
+ data.insert(data.end(), buffer, buffer+strlen(buffer));
+
+ unsigned char * currentRow = new unsigned char[vidBuf.Width*3];
+ for(int y = 0; y < vidBuf.Height; y++)
+ {
+ int rowPos = 0;
+ for(int x = 0; x < vidBuf.Width; x++)
+ {
+ currentRow[rowPos++] = PIXR(vidBuf.Buffer[(y*vidBuf.Width)+x]);
+ currentRow[rowPos++] = PIXG(vidBuf.Buffer[(y*vidBuf.Width)+x]);
+ currentRow[rowPos++] = PIXB(vidBuf.Buffer[(y*vidBuf.Width)+x]);
+ }
+ data.insert(data.end(), currentRow, currentRow+(vidBuf.Width*3));
+ }
+ delete currentRow;
+
+ return data;
+}
+
+struct PNGChunk
+{
+ int Length;
+ char Name[4];
+ char * Data;
+
+ //char[4] CRC();
+
+ PNGChunk(int length, std::string name)
+ {
+ if(name.length()!=4)
+ throw std::runtime_error("Invalid chunk name");
+ std::copy(name.begin(), name.begin()+4, Name);
+ Length = length;
+ if(length)
+ {
+ Data = new char[length];
+ std::fill(Data, Data+length, 0);
+ }
+ else
+ {
+ Data = NULL;
+ }
+ }
+ unsigned long CRC()
+ {
+ if(!Data)
+ {
+ return format::CalculateCRC((unsigned char*)Name, 4);
+ }
+ else
+ {
+ unsigned char * temp = new unsigned char[4+Length];
+ std::copy(Name, Name+4, temp);
+ std::copy(Data, Data+Length, temp+4);
+ unsigned long tempRet = format::CalculateCRC(temp, 4+Length);
+ delete[] temp;
+ return tempRet;
+ }
+ }
+ ~PNGChunk()
+ {
+ if(Data)
+ delete[] Data;
+ }
+};
+
+std::vector<char> format::VideoBufferToPNG(const VideoBuffer & vidBuf)
+{
+ std::vector<PNGChunk*> chunks;
+
+ //Begin IHDR (Image header) chunk (Image size and depth)
+ PNGChunk IHDRChunk = PNGChunk(13, "IHDR");
+
+ //Image Width
+ IHDRChunk.Data[0] = (vidBuf.Width>>24)&0xFF;
+ IHDRChunk.Data[1] = (vidBuf.Width>>16)&0xFF;
+ IHDRChunk.Data[2] = (vidBuf.Width>>8)&0xFF;
+ IHDRChunk.Data[3] = (vidBuf.Width)&0xFF;
+
+ //Image Height
+ IHDRChunk.Data[4] = (vidBuf.Height>>24)&0xFF;
+ IHDRChunk.Data[5] = (vidBuf.Height>>16)&0xFF;
+ IHDRChunk.Data[6] = (vidBuf.Height>>8)&0xFF;
+ IHDRChunk.Data[7] = (vidBuf.Height)&0xFF;
+
+ //Bit depth
+ IHDRChunk.Data[8] = 8; //8bits per channel or 24bpp
+
+ //Colour type
+ IHDRChunk.Data[9] = 2; //RGB triple
+
+ //Everything else is default
+ chunks.push_back(&IHDRChunk);
+
+ //Begin image data, format is 8bit RGB (24bit pixel)
+ int dataPos = 0;
+ unsigned char * uncompressedData = new unsigned char[(vidBuf.Width*vidBuf.Height*3)+vidBuf.Height];
+
+ //Byte ordering and filtering
+ unsigned char * previousRow = new unsigned char[vidBuf.Width*3];
+ std::fill(previousRow, previousRow+(vidBuf.Width*3), 0);
+ unsigned char * currentRow = new unsigned char[vidBuf.Width*3];
+ for(int y = 0; y < vidBuf.Height; y++)
+ {
+ int rowPos = 0;
+ for(int x = 0; x < vidBuf.Width; x++)
+ {
+ currentRow[rowPos++] = PIXR(vidBuf.Buffer[(y*vidBuf.Width)+x]);
+ currentRow[rowPos++] = PIXG(vidBuf.Buffer[(y*vidBuf.Width)+x]);
+ currentRow[rowPos++] = PIXB(vidBuf.Buffer[(y*vidBuf.Width)+x]);
+ }
+
+ uncompressedData[dataPos++] = 2; //Up Sub(x) filter
+ for(int b = 0; b < rowPos; b++)
+ {
+ int filteredByte = (currentRow[b]-previousRow[b])&0xFF;
+ uncompressedData[dataPos++] = filteredByte;
+ }
+
+ unsigned char * tempRow = previousRow;
+ previousRow = currentRow;
+ currentRow = tempRow;
+ }
+ delete[] currentRow;
+ delete[] previousRow;
+
+ //Compression
+ int compressedBufferSize = (vidBuf.Width*vidBuf.Height*3)*2;
+ unsigned char * compressedData = new unsigned char[compressedBufferSize];
+
+ int result;
+ z_stream zipStream;
+ zipStream.zalloc = Z_NULL;
+ zipStream.zfree = Z_NULL;
+ zipStream.opaque = Z_NULL;
+
+ result = deflateInit2(&zipStream,
+ 9, // level
+ Z_DEFLATED, // method
+ 10, // windowBits
+ 1, // memLevel
+ Z_DEFAULT_STRATEGY // strategy
+ );
+
+ if (result != Z_OK) exit(result);
+
+ zipStream.next_in = uncompressedData;
+ zipStream.avail_in = dataPos;
+
+ zipStream.next_out = compressedData;
+ zipStream.avail_out = compressedBufferSize;
+
+
+ result = deflate(&zipStream, Z_FINISH);
+ if (result != Z_STREAM_END) exit(result);
+
+ int compressedSize = compressedBufferSize-zipStream.avail_out;
+ PNGChunk IDATChunk = PNGChunk(compressedSize, "IDAT");
+ std::copy(compressedData, compressedData+compressedSize, IDATChunk.Data);
+ chunks.push_back(&IDATChunk);
+
+ deflateEnd(&zipStream);
+
+ delete[] compressedData;
+ delete[] uncompressedData;
+
+ PNGChunk IENDChunk = PNGChunk(0, "IEND");
+ chunks.push_back(&IENDChunk);
+
+ //Write chunks to output buffer
+ int finalDataSize = 8;
+ for(std::vector<PNGChunk*>::iterator iter = chunks.begin(), end = chunks.end(); iter != end; ++iter)
+ {
+ PNGChunk * cChunk = *iter;
+ finalDataSize += 4 + 4 + 4;
+ finalDataSize += cChunk->Length;
+ }
+ unsigned char * finalData = new unsigned char[finalDataSize];
+ int finalDataPos = 0;
+
+ //PNG File header
+ finalData[finalDataPos++] = 0x89;
+ finalData[finalDataPos++] = 0x50;
+ finalData[finalDataPos++] = 0x4E;
+ finalData[finalDataPos++] = 0x47;
+ finalData[finalDataPos++] = 0x0D;
+ finalData[finalDataPos++] = 0x0A;
+ finalData[finalDataPos++] = 0x1A;
+ finalData[finalDataPos++] = 0x0A;
+
+ for(std::vector<PNGChunk*>::iterator iter = chunks.begin(), end = chunks.end(); iter != end; ++iter)
+ {
+ PNGChunk * cChunk = *iter;
+
+ //Chunk length
+ finalData[finalDataPos++] = (cChunk->Length>>24)&0xFF;
+ finalData[finalDataPos++] = (cChunk->Length>>16)&0xFF;
+ finalData[finalDataPos++] = (cChunk->Length>>8)&0xFF;
+ finalData[finalDataPos++] = (cChunk->Length)&0xFF;
+
+ //Chunk name
+ std::copy(cChunk->Name, cChunk->Name+4, finalData+finalDataPos);
+ finalDataPos += 4;
+
+ //Chunk data
+ if(cChunk->Data)
+ {
+ std::copy(cChunk->Data, cChunk->Data+cChunk->Length, finalData+finalDataPos);
+ finalDataPos += cChunk->Length;
+ }
+
+ //Chunk CRC
+ unsigned long tempCRC = cChunk->CRC();
+ finalData[finalDataPos++] = (tempCRC>>24)&0xFF;
+ finalData[finalDataPos++] = (tempCRC>>16)&0xFF;
+ finalData[finalDataPos++] = (tempCRC>>8)&0xFF;
+ finalData[finalDataPos++] = (tempCRC)&0xFF;
+ }
+
+ std::vector<char> outputData(finalData, finalData+finalDataPos);
+
+ delete[] finalData;
+
+ return outputData;
+}
+
+//CRC functions, copypasta from W3 PNG spec.
+
+/* Table of CRCs of all 8-bit messages. */
+unsigned long crc_table[256];
+
+/* Flag: has the table been computed? Initially false. */
+int crc_table_computed = 0;
+
+/* Make the table for a fast CRC. */
+void make_crc_table(void)
+{
+ unsigned long c;
+ int n, k;
+
+ for (n = 0; n < 256; n++) {
+ c = (unsigned long) n;
+ for (k = 0; k < 8; k++) {
+ if (c & 1)
+ c = 0xedb88320L ^ (c >> 1);
+ else
+ c = c >> 1;
+ }
+ crc_table[n] = c;
+ }
+ crc_table_computed = 1;
+}
+
+/* Update a running CRC with the bytes buf[0..len-1]--the CRC
+ should be initialized to all 1's, and the transmitted value
+ is the 1's complement of the final running CRC (see the
+ crc() routine below)). */
+
+unsigned long update_crc(unsigned long crc, unsigned char *buf, int len)
+{
+ unsigned long c = crc;
+ int n;
+
+ if (!crc_table_computed)
+ make_crc_table();
+ for (n = 0; n < len; n++)
+ {
+ c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
+ }
+ return c;
+}
+
+unsigned long format::CalculateCRC(unsigned char * data, int len)
+{
+ return update_crc(0xffffffffL, data, len) ^ 0xffffffffL;
+} \ No newline at end of file
diff --git a/src/Format.h b/src/Format.h
new file mode 100644
index 0000000..0aae25b
--- /dev/null
+++ b/src/Format.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <sstream>
+#include <vector>
+
+class VideoBuffer;
+
+namespace format
+{
+ static char hex[] = "0123456789ABCDEF";
+
+ template <typename T> std::string NumberToString(T number)
+ {
+ std::stringstream ss;
+ ss << number;
+ return ss.str();
+ }
+
+ template <typename T> T StringToNumber(const std::string & text)
+ {
+ std::stringstream ss(text);
+ T number;
+ return (ss >> number)?number:0;
+ }
+
+ std::string URLEncode(std::string value);
+ std::string UnixtimeToDate(time_t unixtime, std::string dateFomat = "%d %b %Y");
+ std::string UnixtimeToDateMini(time_t unixtime);
+ std::vector<char> VideoBufferToPNG(const VideoBuffer & vidBuf);
+ std::vector<char> VideoBufferToPPM(const VideoBuffer & vidBuf);
+ std::vector<char> VideoBufferToPTI(const VideoBuffer & vidBuf);
+ unsigned long CalculateCRC(unsigned char * data, int length);
+} \ No newline at end of file
diff --git a/src/Misc.cpp b/src/Misc.cpp
new file mode 100644
index 0000000..21e5cac
--- /dev/null
+++ b/src/Misc.cpp
@@ -0,0 +1,717 @@
+#include <stdio.h>
+#include <sstream>
+#include <stdlib.h>
+#include <string.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <cmath>
+#include "Config.h"
+#include "Misc.h"
+#include "icondoc.h"
+#if defined(WIN)
+#include <shlobj.h>
+#include <shlwapi.h>
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+#ifdef MACOSX
+#include <mach-o/dyld.h>
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+std::string URLEscape(std::string source)
+{
+ char * src = (char *)source.c_str();
+ char * dst = (char *)calloc((source.length()*3)+2, 1);
+ char *d;
+ unsigned char *s;
+
+ for (d=dst; *d; d++) ;
+
+ for (s=(unsigned char *)src; *s; s++)
+ {
+ if ((*s>='0' && *s<='9') ||
+ (*s>='a' && *s<='z') ||
+ (*s>='A' && *s<='Z'))
+ *(d++) = *s;
+ else
+ {
+ *(d++) = '%';
+ *(d++) = hex[*s>>4];
+ *(d++) = hex[*s&15];
+ }
+ }
+ *d = 0;
+
+ std::string finalString(dst);
+ free(dst);
+ return finalString;
+}
+
+#if defined(USE_SDL) && defined(LIN) && defined(SDL_VIDEO_DRIVER_X11)
+#include <SDL/SDL_syswm.h>
+SDL_SysWMinfo sdl_wminfo;
+Atom XA_CLIPBOARD, XA_TARGETS;
+#endif
+
+char *clipboard_text = NULL;
+
+char *exe_name(void)
+{
+#if defined(WIN)
+ char *name= (char *)malloc(64);
+ DWORD max=64, res;
+ while ((res = GetModuleFileName(NULL, name, max)) >= max)
+ {
+#elif defined MACOSX
+ char *fn=(char*)malloc(64),*name=(char*)malloc(PATH_MAX);
+ uint32_t max=64, res;
+ if (_NSGetExecutablePath(fn, &max) != 0)
+ {
+ fn = (char*)realloc(fn, max);
+ _NSGetExecutablePath(fn, &max);
+ }
+ if (realpath(fn, name) == NULL)
+ {
+ free(fn);
+ free(name);
+ return NULL;
+ }
+ res = 1;
+#else
+ char fn[64], *name=(char *)malloc(64);
+ size_t max=64, res;
+ sprintf(fn, "/proc/self/exe");
+ memset(name, 0, max);
+ while ((res = readlink(fn, name, max)) >= max-1)
+ {
+#endif
+#ifndef MACOSX
+ max *= 2;
+ name = (char *)realloc(name, max);
+ memset(name, 0, max);
+ }
+#endif
+ if (res <= 0)
+ {
+ free(name);
+ return NULL;
+ }
+ return name;
+}
+
+//Signum function
+int isign(float i) //TODO: INline or macro
+{
+ if (i<0)
+ return -1;
+ if (i>0)
+ return 1;
+ return 0;
+}
+
+TPT_NO_INLINE unsigned clamp_flt(float f, float min, float max) //TODO: Also inline/macro
+{
+ if (f<min)
+ return 0;
+ if (f>max)
+ return 255;
+ return (int)(255.0f*(f-min)/(max-min));
+}
+
+TPT_NO_INLINE float restrict_flt(float f, float min, float max) //TODO Inline or macro or something
+{
+ if (f<min)
+ return min;
+ if (f>max)
+ return max;
+ return f;
+}
+
+char *mystrdup(char *s)
+{
+ char *x;
+ if (s)
+ {
+ x = (char*)malloc(strlen(s)+1);
+ strcpy(x, s);
+ return x;
+ }
+ return s;
+}
+
+void strlist_add(struct strlist **list, char *str)
+{
+ struct strlist *item = (struct strlist*)malloc(sizeof(struct strlist));
+ item->str = mystrdup(str);
+ item->next = *list;
+ *list = item;
+}
+
+int strlist_find(struct strlist **list, char *str)
+{
+ struct strlist *item;
+ for (item=*list; item; item=item->next)
+ if (!strcmp(item->str, str))
+ return 1;
+ return 0;
+}
+
+void strlist_free(struct strlist **list)
+{
+ struct strlist *item;
+ while (*list)
+ {
+ item = *list;
+ *list = (*list)->next;
+ free(item);
+ }
+}
+
+void clean_text(char *text, int vwidth)
+{
+ int i = 0;
+ if(strlen(text)*10 > vwidth){
+ text[vwidth/10] = 0;
+ }
+ for(i = 0; i < strlen(text); i++){
+ if(! (text[i]>=' ' && text[i]<127)){
+ text[i] = ' ';
+ }
+ }
+}
+
+int sregexp(const char *str, char *pattern)
+{
+ int result;
+ regex_t patternc;
+ if (regcomp(&patternc, pattern, 0)!=0)
+ return 1;
+ result = regexec(&patternc, str, 0, NULL, 0);
+ regfree(&patternc);
+ return result;
+}
+
+void save_string(FILE *f, char *str)
+{
+ int li = strlen(str);
+ unsigned char lb[2];
+ lb[0] = li;
+ lb[1] = li >> 8;
+ fwrite(lb, 2, 1, f);
+ fwrite(str, li, 1, f);
+}
+
+int load_string(FILE *f, char *str, int max)
+{
+ int li;
+ unsigned char lb[2];
+ fread(lb, 2, 1, f);
+ li = lb[0] | (lb[1] << 8);
+ if (li > max)
+ {
+ str[0] = 0;
+ return 1;
+ }
+ fread(str, li, 1, f);
+ str[li] = 0;
+ return 0;
+}
+
+void strcaturl(char *dst, char *src)
+{
+ char *d;
+ unsigned char *s;
+
+ for (d=dst; *d; d++) ;
+
+ for (s=(unsigned char *)src; *s; s++)
+ {
+ if ((*s>='0' && *s<='9') ||
+ (*s>='a' && *s<='z') ||
+ (*s>='A' && *s<='Z'))
+ *(d++) = *s;
+ else
+ {
+ *(d++) = '%';
+ *(d++) = hex[*s>>4];
+ *(d++) = hex[*s&15];
+ }
+ }
+ *d = 0;
+}
+
+void strappend(char *dst, char *src)
+{
+ char *d;
+ unsigned char *s;
+
+ for (d=dst; *d; d++) ;
+
+ for (s=(unsigned char *)src; *s; s++)
+ {
+ *(d++) = *s;
+ }
+ *d = 0;
+}
+
+void *file_load(char *fn, int *size)
+{
+ FILE *f = fopen(fn, "rb");
+ void *s;
+
+ if (!f)
+ return NULL;
+ fseek(f, 0, SEEK_END);
+ *size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ s = malloc(*size);
+ if (!s)
+ {
+ fclose(f);
+ return NULL;
+ }
+ fread(s, *size, 1, f);
+ fclose(f);
+ return s;
+}
+
+int cpu_check(void)
+{
+#ifdef MACOSX
+ return 0;
+#else
+#ifdef X86
+ unsigned af,bf,cf,df;
+ x86_cpuid(0, af, bf, cf, df);
+ //if (bf==0x68747541 && cf==0x444D4163 && df==0x69746E65)
+ // amd = 1;
+ x86_cpuid(1, af, bf, cf, df);
+#ifdef X86_SSE
+ if (!(df&(1<<25)))
+ return 1;
+#endif
+#ifdef X86_SSE2
+ if (!(df&(1<<26)))
+ return 1;
+#endif
+#ifdef X86_SSE3
+ if (!(cf&1))
+ return 1;
+#endif
+#endif
+#endif
+ return 0;
+}
+
+matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2)
+{
+ matrix2d result = {
+ m1.a*m2.a+m1.b*m2.c, m1.a*m2.b+m1.b*m2.d,
+ m1.c*m2.a+m1.d*m2.c, m1.c*m2.b+m1.d*m2.d
+ };
+ return result;
+}
+vector2d m2d_multiply_v2d(matrix2d m, vector2d v)
+{
+ vector2d result = {
+ m.a*v.x+m.b*v.y,
+ m.c*v.x+m.d*v.y
+ };
+ return result;
+}
+matrix2d m2d_multiply_float(matrix2d m, float s)
+{
+ matrix2d result = {
+ m.a*s, m.b*s,
+ m.c*s, m.d*s,
+ };
+ return result;
+}
+
+vector2d v2d_multiply_float(vector2d v, float s)
+{
+ vector2d result = {
+ v.x*s,
+ v.y*s
+ };
+ return result;
+}
+
+vector2d v2d_add(vector2d v1, vector2d v2)
+{
+ vector2d result = {
+ v1.x+v2.x,
+ v1.y+v2.y
+ };
+ return result;
+}
+vector2d v2d_sub(vector2d v1, vector2d v2)
+{
+ vector2d result = {
+ v1.x-v2.x,
+ v1.y-v2.y
+ };
+ return result;
+}
+
+matrix2d m2d_new(float me0, float me1, float me2, float me3)
+{
+ matrix2d result = {me0,me1,me2,me3};
+ return result;
+}
+vector2d v2d_new(float x, float y)
+{
+ vector2d result = {x, y};
+ return result;
+}
+
+void clipboard_push_text(char * text)
+{
+#ifdef MACOSX
+ PasteboardRef newclipboard;
+
+ if (PasteboardCreate(kPasteboardClipboard, &newclipboard)!=noErr) return;
+ if (PasteboardClear(newclipboard)!=noErr) return;
+ PasteboardSynchronize(newclipboard);
+
+ CFDataRef data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
+ PasteboardPutItemFlavor(newclipboard, (PasteboardItemID)1, CFSTR("com.apple.traditional-mac-plain-text"), data, 0);
+#elif defined(WIN)
+ if (OpenClipboard(NULL))
+ {
+ HGLOBAL cbuffer;
+ char * glbuffer;
+
+ EmptyClipboard();
+
+ cbuffer = GlobalAlloc(GMEM_DDESHARE, strlen(text)+1);
+ glbuffer = (char*)GlobalLock(cbuffer);
+
+ strcpy(glbuffer, text);
+
+ GlobalUnlock(cbuffer);
+ SetClipboardData(CF_TEXT, cbuffer);
+ CloseClipboard();
+ }
+#elif defined(LIN) && defined(SDL_VIDEO_DRIVER_X11)
+ if (clipboard_text!=NULL) {
+ free(clipboard_text);
+ clipboard_text = NULL;
+ }
+ clipboard_text = mystrdup(text);
+ sdl_wminfo.info.x11.lock_func();
+ XSetSelectionOwner(sdl_wminfo.info.x11.display, XA_CLIPBOARD, sdl_wminfo.info.x11.window, CurrentTime);
+ XFlush(sdl_wminfo.info.x11.display);
+ sdl_wminfo.info.x11.unlock_func();
+#else
+ printf("Not implemented: put text on clipboard \"%s\"\n", text);
+#endif
+}
+
+char * clipboard_pull_text()
+{
+#ifdef MACOSX
+ printf("Not implemented: get text from clipboard\n");
+#elif defined(WIN)
+ if (OpenClipboard(NULL))
+ {
+ HANDLE cbuffer;
+ char * glbuffer;
+
+ cbuffer = GetClipboardData(CF_TEXT);
+ glbuffer = (char*)GlobalLock(cbuffer);
+ GlobalUnlock(cbuffer);
+ CloseClipboard();
+ if(glbuffer!=NULL){
+ return mystrdup(glbuffer);
+ } else {
+ return mystrdup("");
+ }
+ }
+#elif defined(LIN) && defined(SDL_VIDEO_DRIVER_X11)
+ printf("Not implemented: get text from clipboard\n");
+#else
+ printf("Not implemented: get text from clipboard\n");
+#endif
+ return mystrdup("");
+}
+
+int register_extension()
+{
+#if defined(WIN)
+ int returnval;
+ LONG rresult;
+ HKEY newkey;
+ char *currentfilename = exe_name();
+ char *iconname = NULL;
+ char *opencommand = NULL;
+ //char AppDataPath[MAX_PATH];
+ char *AppDataPath = NULL;
+ iconname = (char*)malloc(strlen(currentfilename)+6);
+ sprintf(iconname, "%s,-102", currentfilename);
+
+ //Create Roaming application data folder
+ /*if(!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, AppDataPath)))
+ {
+ returnval = 0;
+ goto finalise;
+ }*/
+
+ //AppDataPath = _getcwd(NULL, 0);
+
+ //Move Game executable into application data folder
+ //TODO: Implement
+
+ opencommand = (char*)malloc(strlen(currentfilename)+53+strlen(AppDataPath));
+ /*if((strlen(AppDataPath)+strlen(APPDATA_SUBDIR "\\Powder Toy"))<MAX_PATH)
+ {
+ strappend(AppDataPath, APPDATA_SUBDIR);
+ _mkdir(AppDataPath);
+ strappend(AppDataPath, "\\Powder Toy");
+ _mkdir(AppDataPath);
+ } else {
+ returnval = 0;
+ goto finalise;
+ }*/
+ sprintf(opencommand, "\"%s\" open \"%%1\" ddir \"%s\"", currentfilename, AppDataPath);
+
+ //Create extension entry
+ rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\.cps", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL);
+ if (rresult != ERROR_SUCCESS) {
+ returnval = 0;
+ goto finalise;
+ }
+ rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)"PowderToySave", strlen("PowderToySave")+1);
+ if (rresult != ERROR_SUCCESS) {
+ RegCloseKey(newkey);
+ returnval = 0;
+ goto finalise;
+ }
+ RegCloseKey(newkey);
+
+ rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\.stm", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL);
+ if (rresult != ERROR_SUCCESS) {
+ returnval = 0;
+ goto finalise;
+ }
+ rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)"PowderToySave", strlen("PowderToySave")+1);
+ if (rresult != ERROR_SUCCESS) {
+ RegCloseKey(newkey);
+ returnval = 0;
+ goto finalise;
+ }
+ RegCloseKey(newkey);
+
+ //Create program entry
+ rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\PowderToySave", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL);
+ if (rresult != ERROR_SUCCESS) {
+ returnval = 0;
+ goto finalise;
+ }
+ rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)"Powder Toy Save", strlen("Powder Toy Save")+1);
+ if (rresult != ERROR_SUCCESS) {
+ RegCloseKey(newkey);
+ returnval = 0;
+ goto finalise;
+ }
+ RegCloseKey(newkey);
+
+ //Set DefaultIcon
+ rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\PowderToySave\\DefaultIcon", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL);
+ if (rresult != ERROR_SUCCESS) {
+ returnval = 0;
+ goto finalise;
+ }
+ rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)iconname, strlen(iconname)+1);
+ if (rresult != ERROR_SUCCESS) {
+ RegCloseKey(newkey);
+ returnval = 0;
+ goto finalise;
+ }
+ RegCloseKey(newkey);
+
+ //Set Launch command
+ rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\PowderToySave\\shell\\open\\command", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL);
+ if (rresult != ERROR_SUCCESS) {
+ returnval = 0;
+ goto finalise;
+ }
+ rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)opencommand, strlen(opencommand)+1);
+ if (rresult != ERROR_SUCCESS) {
+ RegCloseKey(newkey);
+ returnval = 0;
+ goto finalise;
+ }
+ RegCloseKey(newkey);
+
+ returnval = 1;
+ finalise:
+
+ if(iconname) free(iconname);
+ if(opencommand) free(opencommand);
+ if(currentfilename) free(currentfilename);
+
+ return returnval;
+#elif defined(LIN)
+ char *currentfilename = exe_name();
+ FILE *f;
+ char *mimedata =
+"<?xml version=\"1.0\"?>\n"
+" <mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>\n"
+" <mime-type type=\"application/vnd.powdertoy.save\">\n"
+" <comment>Powder Toy save</comment>\n"
+" <glob pattern=\"*.cps\"/>\n"
+" <glob pattern=\"*.stm\"/>\n"
+" </mime-type>\n"
+"</mime-info>\n";
+ f = fopen("powdertoy-save.xml", "wb");
+ if (!f)
+ return 0;
+ fwrite(mimedata, 1, strlen(mimedata), f);
+ fclose(f);
+
+ char *desktopfiledata_tmp =
+"[Desktop Entry]\n"
+"Type=Application\n"
+"Name=Powder Toy\n"
+"Comment=Physics sandbox game\n"
+"MimeType=application/vnd.powdertoy.save;\n"
+"NoDisplay=true\n";
+ char *desktopfiledata = (char *)malloc(strlen(desktopfiledata_tmp)+strlen(currentfilename)+100);
+ strcpy(desktopfiledata, desktopfiledata_tmp);
+ strappend(desktopfiledata, "Exec=");
+ strappend(desktopfiledata, currentfilename);
+ strappend(desktopfiledata, " open %f\n");
+ f = fopen("powdertoy-tpt.desktop", "wb");
+ if (!f)
+ return 0;
+ fwrite(desktopfiledata, 1, strlen(desktopfiledata), f);
+ fclose(f);
+ system("xdg-mime install powdertoy-save.xml");
+ system("xdg-desktop-menu install powdertoy-tpt.desktop");
+ f = fopen("powdertoy-save-32.png", "wb");
+ if (!f)
+ return 0;
+ fwrite(icon_doc_32_png, 1, sizeof(icon_doc_32_png), f);
+ fclose(f);
+ f = fopen("powdertoy-save-16.png", "wb");
+ if (!f)
+ return 0;
+ fwrite(icon_doc_16_png, 1, sizeof(icon_doc_16_png), f);
+ fclose(f);
+ system("xdg-icon-resource install --noupdate --context mimetypes --size 32 powdertoy-save-32.png application-vnd.powdertoy.save");
+ system("xdg-icon-resource install --noupdate --context mimetypes --size 16 powdertoy-save-16.png application-vnd.powdertoy.save");
+ system("xdg-icon-resource forceupdate");
+ system("xdg-mime default powdertoy-tpt.desktop application/vnd.powdertoy.save");
+ unlink("powdertoy-save-32.png");
+ unlink("powdertoy-save-16.png");
+ unlink("powdertoy-save.xml");
+ unlink("powdertoy-tpt.desktop");
+ return 1;
+#elif defined MACOSX
+ return 0;
+#endif
+}
+
+void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b)//convert 0-255(0-360 for H) HSV values to 0-255 RGB
+{
+ float hh, ss, vv, c, x;
+ int m;
+ hh = h/60.0f;//normalize values
+ ss = s/255.0f;
+ vv = v/255.0f;
+ c = vv * ss;
+ x = c * ( 1 - fabs(fmod(hh,2.0f) -1) );
+ if(hh<1){
+ *r = (int)(c*255.0);
+ *g = (int)(x*255.0);
+ *b = 0;
+ }
+ else if(hh<2){
+ *r = (int)(x*255.0);
+ *g = (int)(c*255.0);
+ *b = 0;
+ }
+ else if(hh<3){
+ *r = 0;
+ *g = (int)(c*255.0);
+ *b = (int)(x*255.0);
+ }
+ else if(hh<4){
+ *r = 0;
+ *g = (int)(x*255.0);
+ *b = (int)(c*255.0);
+ }
+ else if(hh<5){
+ *r = (int)(x*255.0);
+ *g = 0;
+ *b = (int)(c*255.0);
+ }
+ else if(hh<6){
+ *r = (int)(c*255.0);
+ *g = 0;
+ *b = (int)(x*255.0);
+ }
+ m = (int)((vv-c)*255.0);
+ *r += m;
+ *g += m;
+ *b += m;
+}
+
+void OpenURI(std::string uri) {
+#if defined(WIN)
+ ShellExecute(0, "OPEN", uri.c_str(), NULL, NULL, 0);
+#elif defined(MACOSX)
+ char *cmd = (char*)malloc(7+uri.length());
+ strcpy(cmd, "open ");
+ strappend(cmd, (char*)uri.c_str());
+ system(cmd);
+#elif defined(LIN)
+ char *cmd = (char*)malloc(11+uri.length());
+ strcpy(cmd, "xdg-open ");
+ strappend(cmd, (char*)uri.c_str());
+ system(cmd);
+#else
+ printf("Cannot open browser\n");
+#endif
+}
+
+void RGB_to_HSV(int r,int g,int b,int *h,int *s,int *v)//convert 0-255 RGB values to 0-255(0-360 for H) HSV
+{
+ float rr, gg, bb, a,x,c,d;
+ rr = r/255.0f;//normalize values
+ gg = g/255.0f;
+ bb = b/255.0f;
+ a = fmin(rr,gg);
+ a = fmin(a,bb);
+ x = fmax(rr,gg);
+ x = fmax(x,bb);
+ if (a==x)//greyscale
+ {
+ *h = 0;
+ *s = 0;
+ *v = (int)(a*255.0);
+ }
+ else
+ {
+ c = (rr==a) ? gg-bb : ((bb==a) ? rr-gg : bb-rr);
+ d = (rr==a) ? 3 : ((bb==a) ? 1 : 5);
+ *h = (int)(60.0*(d - c/(x - a)));
+ *s = (int)(255.0*((x - a)/x));
+ *v = (int)(255.0*x);
+ }
+}
+
+void membwand(void * destv, void * srcv, size_t destsize, size_t srcsize)
+{
+ size_t i;
+ unsigned char * dest = (unsigned char*)destv;
+ unsigned char * src = (unsigned char*)srcv;
+ for(i = 0; i < destsize; i++){
+ dest[i] = dest[i] & src[i%srcsize];
+ }
+}
+vector2d v2d_zero = {0,0};
+matrix2d m2d_identity = {1,0,0,1};
diff --git a/src/Misc.h b/src/Misc.h
new file mode 100644
index 0000000..cfac841
--- /dev/null
+++ b/src/Misc.h
@@ -0,0 +1,115 @@
+#ifndef UTILS_H
+#define UTILS_H
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <sstream>
+#include <vector>
+
+#if defined(WIN) && !defined(__GNUC__)
+#define x86_cpuid(func,af,bf,cf,df) \
+ do {\
+ __asm mov eax, func\
+ __asm cpuid\
+ __asm mov af, eax\
+ __asm mov bf, ebx\
+ __asm mov cf, ecx\
+ __asm mov df, edx\
+ } while(0)
+#else
+#define x86_cpuid(func,af,bf,cf,df) \
+__asm__ __volatile ("cpuid":\
+ "=a" (af), "=b" (bf), "=c" (cf), "=d" (df) : "a" (func));
+#endif
+
+static char hex[] = "0123456789ABCDEF";
+
+char *exe_name(void);
+
+//Signum function
+int isign(float i);
+
+unsigned clamp_flt(float f, float min, float max);
+
+float restrict_flt(float f, float min, float max);
+
+char *mystrdup(char *s);
+
+struct strlist
+{
+ char *str;
+ struct strlist *next;
+};
+
+void strlist_add(struct strlist **list, char *str);
+
+int strlist_find(struct strlist **list, char *str);
+
+void strlist_free(struct strlist **list);
+
+void save_presets(int do_update);
+
+void clean_text(char *text, int vwidth);
+
+void load_presets(void);
+
+void save_string(FILE *f, char *str);
+
+int sregexp(const char *str, char *pattern);
+
+int load_string(FILE *f, char *str, int max);
+
+void strcaturl(char *dst, char *src);
+
+std::string URLEscape(std::string source);
+
+void strappend(char *dst, char *src);
+
+void *file_load(char *fn, int *size);
+
+void clipboard_push_text(char * text);
+
+char * clipboard_pull_text();
+
+extern char *clipboard_text;
+
+int register_extension();
+
+int cpu_check(void);
+
+void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b);
+
+void RGB_to_HSV(int r,int g,int b,int *h,int *s,int *v);
+
+void OpenURI(std::string uri);
+
+void membwand(void * dest, void * src, size_t destsize, size_t srcsize);
+// a b
+// c d
+
+struct matrix2d {
+ float a,b,c,d;
+};
+typedef struct matrix2d matrix2d;
+
+// column vector
+struct vector2d {
+ float x,y;
+};
+typedef struct vector2d vector2d;
+
+matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2);
+vector2d m2d_multiply_v2d(matrix2d m, vector2d v);
+matrix2d m2d_multiply_float(matrix2d m, float s);
+vector2d v2d_multiply_float(vector2d v, float s);
+
+vector2d v2d_add(vector2d v1, vector2d v2);
+vector2d v2d_sub(vector2d v1, vector2d v2);
+
+matrix2d m2d_new(float me0, float me1, float me2, float me3);
+vector2d v2d_new(float x, float y);
+
+extern vector2d v2d_zero;
+extern matrix2d m2d_identity;
+
+#endif
diff --git a/src/PowderToy.h b/src/PowderToy.h
new file mode 100644
index 0000000..2e8cb36
--- /dev/null
+++ b/src/PowderToy.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void EngineProcess(); \ No newline at end of file
diff --git a/src/PowderToyRenderer.cpp b/src/PowderToyRenderer.cpp
new file mode 100644
index 0000000..2376f95
--- /dev/null
+++ b/src/PowderToyRenderer.cpp
@@ -0,0 +1,120 @@
+#if defined(RENDERER)
+
+#include <time.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <fstream>
+#include <vector>
+
+#include "Config.h"
+#include "Format.h"
+#include "interface/Engine.h"
+#include "graphics/Graphics.h"
+#include "graphics/Renderer.h"
+
+#include "client/GameSave.h"
+#include "simulation/Simulation.h"
+
+
+void EngineProcess() {}
+
+void readFile(std::string filename, std::vector<char> & storage)
+{
+ std::ifstream fileStream;
+ fileStream.open(std::string(filename).c_str(), std::ios::binary);
+ if(fileStream.is_open())
+ {
+ fileStream.seekg(0, std::ios::end);
+ size_t fileSize = fileStream.tellg();
+ fileStream.seekg(0);
+
+ unsigned char * tempData = new unsigned char[fileSize];
+ fileStream.read((char *)tempData, fileSize);
+ fileStream.close();
+
+ std::vector<unsigned char> fileData;
+ storage.clear();
+ storage.insert(storage.end(), tempData, tempData+fileSize);
+ delete[] tempData;
+ }
+}
+
+void writeFile(std::string filename, std::vector<char> & fileData)
+{
+ std::ofstream fileStream;
+ fileStream.open(std::string(filename).c_str(), std::ios::binary);
+ if(fileStream.is_open())
+ {
+ fileStream.write(&fileData[0], fileData.size());
+ fileStream.close();
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ ui::Engine * engine;
+ std::string outputPrefix, inputFilename;
+ std::vector<char> inputFile;
+ std::string ppmFilename, ptiFilename, ptiSmallFilename, pngFilename, pngSmallFilename;
+ std::vector<char> ppmFile, ptiFile, ptiSmallFile, pngFile, pngSmallFile;
+
+ inputFilename = std::string(argv[1]);
+ outputPrefix = std::string(argv[2]);
+
+ ppmFilename = outputPrefix+".ppm";
+ ptiFilename = outputPrefix+".pti";
+ ptiSmallFilename = outputPrefix+"-small.pti";
+ pngFilename = outputPrefix+".png";
+ pngSmallFilename = outputPrefix+"-small.png";
+
+ readFile(inputFilename, inputFile);
+
+ ui::Engine::Ref().g = new Graphics();
+
+ engine = &ui::Engine::Ref();
+ engine->Begin(XRES+BARSIZE, YRES+MENUSIZE);
+
+ GameSave * gameSave = new GameSave(inputFile);
+
+ Simulation * sim = new Simulation();
+ Renderer * ren = new Renderer(ui::Engine::Ref().g, sim);
+
+ sim->Load(gameSave);
+
+
+ //Render save
+ ren->decorations_enable = true;
+ ren->blackDecorations = true;
+
+ int frame = 15;
+ while(frame)
+ {
+ frame--;
+ ren->render_parts();
+ ren->render_fire();
+ ren->clearScreen(1.0f);
+ }
+
+ ren->RenderBegin();
+ ren->RenderEnd();
+
+ VideoBuffer screenBuffer = ren->DumpFrame();
+ //ppmFile = format::VideoBufferToPPM(screenBuffer);
+ ptiFile = format::VideoBufferToPTI(screenBuffer);
+ pngFile = format::VideoBufferToPNG(screenBuffer);
+
+ screenBuffer.Resize(1.0f/3.0f, true);
+ ptiSmallFile = format::VideoBufferToPTI(screenBuffer);
+ pngSmallFile = format::VideoBufferToPNG(screenBuffer);
+
+
+
+ //writeFile(ppmFilename, ppmFile);
+ writeFile(ptiFilename, ptiFile);
+ writeFile(ptiSmallFilename, ptiSmallFile);
+ writeFile(pngFilename, pngFile);
+ writeFile(pngSmallFilename, pngSmallFile);
+}
+
+#endif
diff --git a/src/PowderToySDL.cpp b/src/PowderToySDL.cpp
new file mode 100644
index 0000000..f289958
--- /dev/null
+++ b/src/PowderToySDL.cpp
@@ -0,0 +1,606 @@
+#ifdef USE_SDL
+
+#include <map>
+#include <string>
+#include <time.h>
+#include "SDL.h"
+#ifdef WIN
+#include "SDL_syswm.h"
+#include <direct.h>
+#endif
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "Config.h"
+#include "graphics/Graphics.h"
+#if defined(LIN)
+#include "icon.h"
+#endif
+
+#ifndef WIN
+#include <unistd.h>
+#endif
+
+#include "Format.h"
+#include "Style.h"
+#include "interface/Engine.h"
+#include "interface/Button.h"
+#include "interface/Panel.h"
+#include "interface/Point.h"
+#include "interface/Label.h"
+#include "interface/Keys.h"
+
+#include "client/GameSave.h"
+#include "client/SaveFile.h"
+#include "simulation/SaveRenderer.h"
+#include "client/Client.h"
+#include "Misc.h"
+
+#include "game/GameController.h"
+#include "game/GameView.h"
+
+#include "dialogues/ErrorMessage.h"
+
+#include "client/HTTP.h"
+
+using namespace std;
+
+
+#ifdef WIN
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+#endif
+
+int desktopWidth = 1280, desktopHeight = 1024;
+
+SDL_Surface * sdl_scrn;
+int scale = 1;
+bool fullscreen = false;
+
+#ifdef OGLI
+void blit()
+{
+ SDL_GL_SwapBuffers();
+}
+#else
+void blit(pixel * vid)
+{
+ if(sdl_scrn)
+ {
+ pixel * src = vid;
+ int j, x = 0, y = 0, w = XRES+BARSIZE, h = YRES+MENUSIZE, pitch = XRES+BARSIZE;
+ pixel *dst;
+ if (SDL_MUSTLOCK(sdl_scrn))
+ if (SDL_LockSurface(sdl_scrn)<0)
+ return;
+ dst=(pixel *)sdl_scrn->pixels+y*sdl_scrn->pitch/PIXELSIZE+x;
+ if (SDL_MapRGB(sdl_scrn->format,0x33,0x55,0x77)!=PIXPACK(0x335577))
+ {
+ //pixel format conversion
+ int i;
+ pixel px;
+ SDL_PixelFormat *fmt = sdl_scrn->format;
+ for (j=0; j<h; j++)
+ {
+ for (i=0; i<w; i++)
+ {
+ px = src[i];
+ dst[i] = ((PIXR(px)>>fmt->Rloss)<<fmt->Rshift)|
+ ((PIXG(px)>>fmt->Gloss)<<fmt->Gshift)|
+ ((PIXB(px)>>fmt->Bloss)<<fmt->Bshift);
+ }
+ dst+=sdl_scrn->pitch/PIXELSIZE;
+ src+=pitch;
+ }
+ }
+ else
+ {
+ for (j=0; j<h; j++)
+ {
+ memcpy(dst, src, w*PIXELSIZE);
+ dst+=sdl_scrn->pitch/PIXELSIZE;
+ src+=pitch;
+ }
+ }
+ if (SDL_MUSTLOCK(sdl_scrn))
+ SDL_UnlockSurface(sdl_scrn);
+ SDL_UpdateRect(sdl_scrn,0,0,0,0);
+ }
+}
+void blit2(pixel * vid, int currentScale)
+{
+ if(sdl_scrn)
+ {
+ pixel * src = vid;
+ int j, x = 0, y = 0, w = XRES+BARSIZE, h = YRES+MENUSIZE, pitch = XRES+BARSIZE;
+ pixel *dst;
+ int i,k;
+ if (SDL_MUSTLOCK(sdl_scrn))
+ if (SDL_LockSurface(sdl_scrn)<0)
+ return;
+ dst=(pixel *)sdl_scrn->pixels+y*sdl_scrn->pitch/PIXELSIZE+x;
+ if (SDL_MapRGB(sdl_scrn->format,0x33,0x55,0x77)!=PIXPACK(0x335577))
+ {
+ //pixel format conversion
+ pixel px;
+ SDL_PixelFormat *fmt = sdl_scrn->format;
+ for (j=0; j<h; j++)
+ {
+ for (k=0; k<currentScale; k++)
+ {
+ for (i=0; i<w; i++)
+ {
+ px = src[i];
+ px = ((PIXR(px)>>fmt->Rloss)<<fmt->Rshift)|
+ ((PIXG(px)>>fmt->Gloss)<<fmt->Gshift)|
+ ((PIXB(px)>>fmt->Bloss)<<fmt->Bshift);
+ dst[i*2]=px;
+ dst[i*2+1]=px;
+ }
+ dst+=sdl_scrn->pitch/PIXELSIZE;
+ }
+ src+=pitch;
+ }
+ }
+ else
+ {
+ for (j=0; j<h; j++)
+ {
+ for (k=0; k<currentScale; k++)
+ {
+ for (i=0; i<w; i++)
+ {
+ dst[i*2]=src[i];
+ dst[i*2+1]=src[i];
+ }
+ dst+=sdl_scrn->pitch/PIXELSIZE;
+ }
+ src+=pitch;
+ }
+ }
+ if (SDL_MUSTLOCK(sdl_scrn))
+ SDL_UnlockSurface(sdl_scrn);
+ SDL_UpdateRect(sdl_scrn,0,0,0,0);
+ }
+}
+#endif
+
+int SDLOpen()
+{
+ SDL_Surface * surface;
+#if defined(WIN) && defined(WINCONSOLE)
+ FILE * console = fopen("CON", "w" );
+#endif
+ if (SDL_Init(SDL_INIT_VIDEO)<0)
+ {
+ fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+ const SDL_VideoInfo * vidInfo = SDL_GetVideoInfo();
+ desktopWidth = vidInfo->current_w;
+ desktopHeight = vidInfo->current_h;
+ SDL_EnableUNICODE(1);
+#if defined(WIN) && defined(WINCONSOLE)
+ //On Windows, SDL redirects stdout to stdout.txt, which can be annoying when debugging, here we redirect back to the console
+ if (console)
+ {
+ freopen("CON", "w", stdout);
+ freopen("CON", "w", stderr);
+ //fclose(console);
+ }
+#endif
+#ifdef WIN
+ SDL_SysWMinfo SysInfo;
+ SDL_VERSION(&SysInfo.version);
+ if(SDL_GetWMInfo(&SysInfo) <= 0) {
+ printf("%s : %d\n", SDL_GetError(), SysInfo.window);
+ exit(-1);
+ }
+ HWND WindowHandle = SysInfo.window;
+ HICON hIconSmall = (HICON)LoadImage(reinterpret_cast<HMODULE>(&__ImageBase), MAKEINTRESOURCE(101), IMAGE_ICON, 16, 16, LR_SHARED);
+ HICON hIconBig = (HICON)LoadImage(reinterpret_cast<HMODULE>(&__ImageBase), MAKEINTRESOURCE(101), IMAGE_ICON, 32, 32, LR_SHARED);
+ SendMessage(WindowHandle, WM_SETICON, ICON_SMALL, (LPARAM)hIconSmall);
+ SendMessage(WindowHandle, WM_SETICON, ICON_BIG, (LPARAM)hIconBig);
+#elif defined(LIN)
+ SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(app_icon, 16, 16, 32, 64, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
+ SDL_WM_SetIcon(icon, NULL);
+#endif
+
+ SDL_WM_SetCaption("The Powder Toy", "Powder Toy");
+ //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+ atexit(SDL_Quit);
+
+ return 0;
+}
+
+SDL_Surface * SDLSetScreen(int newScale, bool newFullscreen)
+{
+ scale = newScale;
+ fullscreen = newFullscreen;
+ SDL_Surface * surface;
+#ifndef OGLI
+ surface = SDL_SetVideoMode((XRES + BARSIZE) * newScale, (YRES + MENUSIZE) * newScale, 32, SDL_SWSURFACE | (newFullscreen?SDL_FULLSCREEN:0));
+#else
+ surface = SDL_SetVideoMode((XRES + BARSIZE) * newScale, (YRES + MENUSIZE) * newScale, 32, SDL_OPENGL | SDL_RESIZABLE | (newFullscreen?SDL_FULLSCREEN:0));
+#endif
+ return surface;
+}
+
+std::map<std::string, std::string> readArguments(int argc, char * argv[])
+{
+ std::map<std::string, std::string> arguments;
+
+ //Defaults
+ arguments["scale"] = "";
+ arguments["proxy"] = "";
+ arguments["nohud"] = "false"; //the nohud, sound, and scripts commands currently do nothing.
+ arguments["sound"] = "false";
+ arguments["kiosk"] = "false";
+ arguments["scripts"] = "false";
+ arguments["open"] = "";
+ arguments["ddir"] = "";
+ arguments["ptsave"] = "";
+
+ for (int i=1; i<argc; i++)
+ {
+ if (!strncmp(argv[i], "scale:", 6) && argv[i]+6)
+ {
+ arguments["scale"] = std::string(argv[i]+6);
+ }
+ else if (!strncmp(argv[i], "proxy:", 6))
+ {
+ if(argv[i]+6)
+ arguments["proxy"] = std::string(argv[i]+6);
+ else
+ arguments["proxy"] = "false";
+ }
+ else if (!strncmp(argv[i], "nohud", 5))
+ {
+ arguments["nohud"] = "true";
+ }
+ else if (!strncmp(argv[i], "kiosk", 5))
+ {
+ arguments["kiosk"] = "true";
+ }
+ else if (!strncmp(argv[i], "sound", 5))
+ {
+ arguments["sound"] = "true";
+ }
+ else if (!strncmp(argv[i], "scripts", 8))
+ {
+ arguments["scripts"] = "true";
+ }
+ else if (!strncmp(argv[i], "open", 5) && i+1<argc)
+ {
+ arguments["open"] = std::string(argv[i+1]);;
+ i++;
+ }
+ else if (!strncmp(argv[i], "ddir", 5) && i+1<argc)
+ {
+ arguments["ddir"] = std::string(argv[i+1]);
+ i++;
+ }
+ else if (!strncmp(argv[i], "ptsave", 7) && i+1<argc)
+ {
+ arguments["ptsave"] = std::string(argv[i+1]);
+ i++;
+ break;
+ }
+ }
+ return arguments;
+}
+
+int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0;
+unsigned int lastTick = 0;
+float fps = 0, delta = 1.0f, inputScale = 1.0f;
+ui::Engine * engine = NULL;
+float currentWidth, currentHeight;
+void EngineProcess()
+{
+ int frameStart;
+ float frameTime;
+ float frameTimeAvg = 0.0f, correctedFrameTimeAvg = 0.0f;
+ SDL_Event event;
+ while(engine->Running())
+ {
+ if(engine->Broken()) { engine->UnBreak(); break; }
+ event.type = 0;
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_QUIT:
+ if (engine->GetFastQuit() || engine->CloseWindow())
+ engine->Exit();
+ break;
+ case SDL_KEYDOWN:
+ engine->onKeyPress(event.key.keysym.sym, event.key.keysym.unicode, event.key.keysym.mod&KEY_MOD_LSHIFT, event.key.keysym.mod&KEY_MOD_LCONTROL, event.key.keysym.mod&KEY_MOD_LALT);
+ break;
+ case SDL_KEYUP:
+ engine->onKeyRelease(event.key.keysym.sym, event.key.keysym.unicode, event.key.keysym.mod&KEY_MOD_LSHIFT, event.key.keysym.mod&KEY_MOD_LCONTROL, event.key.keysym.mod&KEY_MOD_LALT);
+ break;
+ case SDL_MOUSEMOTION:
+ engine->onMouseMove(event.motion.x*inputScale, event.motion.y*inputScale);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ if(event.button.button == SDL_BUTTON_WHEELUP)
+ {
+ engine->onMouseWheel(event.motion.x*inputScale, event.motion.y*inputScale, 1);
+ }
+ else if (event.button.button == SDL_BUTTON_WHEELDOWN)
+ {
+ engine->onMouseWheel(event.motion.x*inputScale, event.motion.y*inputScale, -1);
+ }
+ else
+ {
+ engine->onMouseClick(event.motion.x*inputScale, event.motion.y*inputScale, event.button.button);
+ }
+ break;
+ case SDL_MOUSEBUTTONUP:
+ if(event.button.button != SDL_BUTTON_WHEELUP && event.button.button != SDL_BUTTON_WHEELDOWN)
+ engine->onMouseUnclick(event.motion.x*inputScale, event.motion.y*inputScale, event.button.button);
+ break;
+#ifdef OGLI
+ case SDL_VIDEORESIZE:
+ float ratio = float(XRES+BARSIZE) / float(YRES+MENUSIZE);
+ float width = event.resize.w;
+ float height = width/ratio;
+
+ sdl_scrn = SDL_SetVideoMode(event.resize.w, height, 32, SDL_OPENGL | SDL_RESIZABLE);
+
+ glViewport(0, 0, width, height);
+ engine->g->Reset();
+ //glScaled(width/currentWidth, height/currentHeight, 1.0f);
+
+ currentWidth = width;
+ currentHeight = height;
+ inputScale = float(XRES+BARSIZE)/currentWidth;
+
+ glLineWidth(currentWidth/float(XRES+BARSIZE));
+ if(sdl_scrn == NULL)
+ {
+ std::cerr << "Oh bugger" << std::endl;
+ }
+ break;
+#endif
+ }
+ event.type = 0; //Clear last event
+ }
+ if(engine->Broken()) { engine->UnBreak(); break; }
+
+ frameStart = SDL_GetTicks();
+ engine->Tick();
+ engine->Draw();
+ frameTime = SDL_GetTicks() - frameStart;
+
+ frameTimeAvg = (frameTimeAvg*(1.0f-0.2f)) + (0.2f*frameTime);
+ if(ui::Engine::Ref().FpsLimit > 2.0f)
+ {
+ float targetFrameTime = 1000.0f/((float)ui::Engine::Ref().FpsLimit);
+ if(targetFrameTime - frameTimeAvg > 0)
+ {
+ SDL_Delay((targetFrameTime - frameTimeAvg) + 0.5f);
+ frameTime = SDL_GetTicks() - frameStart;//+= (int)(targetFrameTime - frameTimeAvg);
+ }
+ }
+
+ correctedFrameTimeAvg = (correctedFrameTimeAvg*(1.0f-0.05f)) + (0.05f*frameTime);
+ fps = 1000.0f/correctedFrameTimeAvg;
+ engine->SetFps(fps);
+
+ if(frameStart-lastTick>250)
+ {
+ //Run client tick every second
+ lastTick = frameStart;
+ Client::Ref().Tick();
+ }
+
+ if(scale != engine->Scale || fullscreen != engine->Fullscreen)
+ {
+ sdl_scrn = SDLSetScreen(engine->Scale, engine->Fullscreen);
+ inputScale = 1.0f/float(scale);
+ }
+
+#ifdef OGLI
+ blit();
+#else
+ if(engine->Scale==2)
+ blit2(engine->g->vid, engine->Scale);
+ else
+ blit(engine->g->vid);
+#endif
+ }
+#ifdef DEBUG
+ std::cout << "Breaking out of EngineProcess" << std::endl;
+#endif
+}
+
+int main(int argc, char * argv[])
+{
+ currentWidth = XRES+BARSIZE;
+ currentHeight = YRES+MENUSIZE;
+
+
+ std::map<std::string, std::string> arguments = readArguments(argc, argv);
+
+ if(arguments["ddir"].length())
+#ifdef WIN
+ _chdir(arguments["ddir"].c_str());
+#else
+ chdir(arguments["ddir"].c_str());
+#endif
+
+ int tempScale = 1;
+ bool tempFullscreen = false;
+
+ tempScale = Client::Ref().GetPrefInteger("Scale", 1);
+ tempFullscreen = Client::Ref().GetPrefBool("Fullscreen", false);
+
+
+ if(arguments["kiosk"] == "true")
+ {
+ tempFullscreen = true;
+ Client::Ref().SetPref("Fullscreen", tempFullscreen);
+ }
+
+ if(arguments["scale"].length())
+ {
+ tempScale = format::StringToNumber<int>(arguments["scale"]);
+ Client::Ref().SetPref("Scale", tempScale);
+ }
+
+ std::string proxyString = "";
+ if(arguments["proxy"].length())
+ {
+ if(arguments["proxy"] == "false")
+ {
+ proxyString = "";
+ Client::Ref().SetPref("Proxy", "");
+ }
+ else
+ {
+ proxyString = (arguments["proxy"]);
+ Client::Ref().SetPref("Proxy", arguments["proxy"]);
+ }
+ }
+ else if(Client::Ref().GetPrefString("Proxy", "").length())
+ {
+ proxyString = (Client::Ref().GetPrefString("Proxy", ""));
+ }
+
+ Client::Ref().Initialise(proxyString);
+
+ if(tempScale != 1 && tempScale != 2)
+ tempScale = 1;
+
+ int sdlStatus = SDLOpen();
+ sdl_scrn = SDLSetScreen(tempScale, tempFullscreen);
+#ifdef OGLI
+ SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
+ //glScaled(2.0f, 2.0f, 1.0f);
+#endif
+#if defined(OGLI)
+ int status = glewInit();
+ if(status != GLEW_OK)
+ {
+ fprintf(stderr, "Initializing Glew: %d\n", status);
+ exit(-1);
+ }
+#endif
+ ui::Engine::Ref().g = new Graphics();
+ ui::Engine::Ref().Scale = scale;
+ inputScale = 1.0f/float(scale);
+ ui::Engine::Ref().Fullscreen = fullscreen;
+
+ engine = &ui::Engine::Ref();
+ engine->SetMaxSize(desktopWidth, desktopHeight);
+ engine->Begin(XRES+BARSIZE, YRES+MENUSIZE);
+ engine->SetFastQuit(Client::Ref().GetPrefBool("FastQuit", true));
+
+ GameController * gameController = new GameController();
+ engine->ShowWindow(gameController->GetView());
+
+ if(arguments["open"].length())
+ {
+#ifdef DEBUG
+ std::cout << "Loading " << arguments["open"] << std::endl;
+#endif
+ if(Client::Ref().FileExists(arguments["open"]))
+ {
+ try
+ {
+ std::vector<unsigned char> gameSaveData = Client::Ref().ReadFile(arguments["open"]);
+ if(!gameSaveData.size())
+ {
+ new ErrorMessage("Error", "Could not read file");
+ }
+ else
+ {
+ SaveFile * newFile = new SaveFile(arguments["open"]);
+ GameSave * newSave = new GameSave(gameSaveData);
+ newFile->SetGameSave(newSave);
+ gameController->LoadSaveFile(newFile);
+ delete newFile;
+ }
+
+ }
+ catch(std::exception & e)
+ {
+ new ErrorMessage("Error", "Could not open save file:\n"+std::string(e.what())) ;
+ }
+ }
+ else
+ {
+ new ErrorMessage("Error", "Could not open file");
+ }
+ }
+
+ if(arguments["ptsave"].length())
+ {
+ engine->g->fillrect((engine->GetWidth()/2)-101, (engine->GetHeight()/2)-26, 202, 52, 0, 0, 0, 210);
+ engine->g->drawrect((engine->GetWidth()/2)-100, (engine->GetHeight()/2)-25, 200, 50, 255, 255, 255, 180);
+ engine->g->drawtext((engine->GetWidth()/2)-(Graphics::textwidth("Loading save...")/2), (engine->GetHeight()/2)-5, "Loading save...", style::Colour::InformationTitle.Red, style::Colour::InformationTitle.Green, style::Colour::InformationTitle.Blue, 255);
+
+#ifdef OGLI
+ blit();
+#else
+ if(engine->Scale==2)
+ blit2(engine->g->vid, engine->Scale);
+ else
+ blit(engine->g->vid);
+#endif
+ std::string ptsaveArg = arguments["ptsave"];
+ try
+ {
+ if(!ptsaveArg.find("ptsave:"))
+ {
+ std::string saveIdPart = "";
+ int saveId;
+ int hashPos = ptsaveArg.find('#');
+ if(hashPos != std::string::npos)
+ {
+ saveIdPart = ptsaveArg.substr(7, hashPos-7);
+ }
+ else
+ {
+ saveIdPart = ptsaveArg.substr(7);
+ }
+ if(saveIdPart.length())
+ {
+#ifdef DEBUG
+ std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
+#endif
+ saveId = format::StringToNumber<int>(saveIdPart);
+ if(!saveId)
+ throw std::runtime_error("Invalid Save ID");
+
+ SaveInfo * newSave = Client::Ref().GetSave(saveId, 0);
+ GameSave * newGameSave = new GameSave(Client::Ref().GetSaveData(saveId, 0));
+ newSave->SetGameSave(newGameSave);
+ if(!newSave)
+ throw std::runtime_error("Could not load save");
+
+ gameController->LoadSave(newSave);
+ delete newSave;
+ }
+ else
+ {
+ throw std::runtime_error("No Save ID");
+ }
+ }
+ }
+ catch (std::exception & e)
+ {
+ new ErrorMessage("Error", "Invalid save link");
+ }
+ }
+
+ EngineProcess();
+
+ ui::Engine::Ref().CloseWindow();
+ delete gameController;
+ delete ui::Engine::Ref().g;
+ Client::Ref().Shutdown();
+ return 0;
+}
+
+#endif
diff --git a/src/Singleton.h b/src/Singleton.h
new file mode 100644
index 0000000..6b2214e
--- /dev/null
+++ b/src/Singleton.h
@@ -0,0 +1,16 @@
+#ifndef SINGLETON_H
+#define SINGLETON_H
+
+template<typename T>
+
+class Singleton
+{
+public:
+ static T& Ref()
+ {
+ static T instance;
+ return instance;
+ }
+};
+
+#endif // SINGLETON_H
diff --git a/src/Style.cpp b/src/Style.cpp
new file mode 100644
index 0000000..9bb5c65
--- /dev/null
+++ b/src/Style.cpp
@@ -0,0 +1,26 @@
+//
+// Style.cpp
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 14/05/2012.
+// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#include <iostream>
+
+#include "Style.h"
+#include "interface/Colour.h"
+
+namespace style {
+ ui::Colour Colour::InformationTitle = ui::Colour(140, 140, 255);
+ ui::Colour Colour::WarningTitle = ui::Colour(255, 216, 32);
+ ui::Colour Colour::ErrorTitle = ui::Colour(255, 64, 32);
+
+ ui::Colour Colour::ConfirmButton = ui::Colour(255, 255, 50);
+
+ ui::Colour Colour::ActiveBorder = ui::Colour(255, 255, 255);
+ ui::Colour Colour::InactiveBorder = ui::Colour(100, 100, 100);
+
+ ui::Colour Colour::ActiveBackground = ui::Colour(50, 50, 50);
+ ui::Colour Colour::InactiveBackground = ui::Colour(0, 0, 0);
+}
diff --git a/src/Style.h b/src/Style.h
new file mode 100644
index 0000000..0463a22
--- /dev/null
+++ b/src/Style.h
@@ -0,0 +1,35 @@
+//
+// Style.h
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 14/05/2012.
+//
+
+#ifndef The_Powder_Toy_Style_h
+#define The_Powder_Toy_Style_h
+
+namespace ui { class Colour; }
+
+namespace style
+{
+ class Colour
+ {
+ public:
+ static ui::Colour InformationTitle;
+ static ui::Colour WarningTitle;
+ static ui::Colour ErrorTitle;
+
+ static ui::Colour ConfirmButton;
+
+ static ui::Colour ActiveBorder;
+ static ui::Colour InactiveBorder;
+
+ static ui::Colour ActiveBackground;
+ static ui::Colour InactiveBackground;
+ };
+ class Metrics
+ {
+ };
+}
+
+#endif
diff --git a/src/Update.cpp b/src/Update.cpp
new file mode 100644
index 0000000..4ba2362
--- /dev/null
+++ b/src/Update.cpp
@@ -0,0 +1,200 @@
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef WIN
+#include <sys/param.h>
+#endif
+#if !defined(MACOSX) && !defined(BSD)
+#include <malloc.h>
+#endif
+#include <string.h>
+
+#ifdef WIN
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/stat.h>
+#endif
+#ifdef MACOSX
+#include <mach-o/dyld.h>
+#include <errno.h>
+#endif
+
+#include <Update.h>
+#include <Misc.h>
+
+/*char *exe_name(void)
+{
+#if defined(WIN)
+ char *name= (char *)malloc(64);
+ DWORD max=64, res;
+ while ((res = GetModuleFileName(NULL, name, max)) >= max)
+ {
+#elif defined MACOSX
+ char *fn=malloc(64),*name=malloc(PATH_MAX);
+ uint32_t max=64, res;
+ if (_NSGetExecutablePath(fn, &max) != 0)
+ {
+ fn = realloc(fn, max);
+ _NSGetExecutablePath(fn, &max);
+ }
+ if (realpath(fn, name) == NULL)
+ {
+ free(fn);
+ free(name);
+ return NULL;
+ }
+ res = 1;
+#else
+ char fn[64], *name=malloc(64);
+ size_t max=64, res;
+ sprintf(fn, "/proc/self/exe");
+ memset(name, 0, max);
+ while ((res = readlink(fn, name, max)) >= max-1)
+ {
+#endif
+#ifndef MACOSX
+ max *= 2;
+ name = (char*)realloc(name, max);
+ memset(name, 0, max);
+ }
+#endif
+ if (res <= 0)
+ {
+ free(name);
+ return NULL;
+ }
+ return name;
+}*/
+
+int update_start(char *data, int len)
+{
+ char *self=exe_name(), *temp;
+#ifdef WIN
+ char *p;
+#endif
+ FILE *f;
+ int res = 1;
+
+ if (!self)
+ return 1;
+
+#ifdef WIN
+ temp = (char*)malloc(strlen(self)+12);
+ strcpy(temp, self);
+ p = temp + strlen(temp) - 4;
+ if (_stricmp(p, ".exe"))
+ p += 4;
+ strcpy(p, "_update.exe");
+
+ if (!MoveFile(self, temp))
+ goto fail;
+
+ f = fopen(self, "wb");
+ if (!f)
+ goto fail;
+ if (fwrite(data, 1, len, f) != len)
+ {
+ fclose(f);
+ DeleteFile(self);
+ goto fail;
+ }
+ fclose(f);
+
+ if ((uintptr_t)ShellExecute(NULL, "open", self, NULL, NULL, SW_SHOWNORMAL) <= 32)
+ {
+ DeleteFile(self);
+ goto fail;
+ }
+
+ return 0;
+#else
+ temp = (char*)malloc(strlen(self)+8);
+ strcpy(temp, self);
+ strcat(temp, "-update");
+
+ f = fopen(temp, "w");
+ if (!f)
+ goto fail;
+ if (fwrite(data, 1, len, f) != len)
+ {
+ fclose(f);
+ unlink(temp);
+ goto fail;
+ }
+ fclose(f);
+
+ if (chmod(temp, 0755))
+ {
+ unlink(temp);
+ goto fail;
+ }
+
+ if (rename(temp, self))
+ {
+ unlink(temp);
+ goto fail;
+ }
+
+ execl(self, "powder-update", NULL);
+#endif
+
+fail:
+ free(temp);
+ free(self);
+ return res;
+}
+
+int update_finish(void)
+{
+#ifdef WIN
+ char *temp, *self=exe_name(), *p;
+ int timeout = 60, err;
+
+#ifdef DEBUG
+ printf("Update: Current EXE name: %s\n", self);
+#endif
+
+ temp = (char*)malloc(strlen(self)+12);
+ strcpy(temp, self);
+ p = temp + strlen(temp) - 4;
+ if (_stricmp(p, ".exe"))
+ p += 4;
+ strcpy(p, "_update.exe");
+
+#ifdef DEBUG
+ printf("Update: Temp EXE name: %s\n", temp);
+#endif
+
+ while (!DeleteFile(temp))
+ {
+ err = GetLastError();
+ if (err == ERROR_FILE_NOT_FOUND)
+ {
+#ifdef DEBUG
+ printf("Update: Temp file deleted\n");
+#endif
+ free(temp);
+ return 0;
+ }
+ Sleep(500);
+ timeout--;
+ if (timeout <= 0)
+ {
+#ifdef DEBUG
+ printf("Update: Delete timeout\n");
+#endif
+ free(temp);
+ return 1;
+ }
+ }
+ free(temp);
+#endif
+ return 0;
+}
+
+void update_cleanup(void)
+{
+#ifdef WIN
+ update_finish();
+#endif
+}
diff --git a/src/Update.h b/src/Update.h
new file mode 100644
index 0000000..118396b
--- /dev/null
+++ b/src/Update.h
@@ -0,0 +1,16 @@
+/*
+ * Update.h
+ *
+ * Created on: Jun 21, 2012
+ * Author: Simon
+ */
+
+#ifndef UPDATE_H_
+#define UPDATE_H_
+
+//char *exe_name(void);
+int update_start(char *data, int len);
+int update_finish(void);
+void update_cleanup(void);
+
+#endif /* UPDATE_H_ */
diff --git a/src/bson/BSON.cpp b/src/bson/BSON.cpp
new file mode 100644
index 0000000..634290c
--- /dev/null
+++ b/src/bson/BSON.cpp
@@ -0,0 +1,1122 @@
+/* bson.c */
+
+/* Copyright 2009, 2010 10gen Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <limits.h>
+
+#include "BSON.h"
+
+const int initialBufferSize = 128;
+
+/* only need one of these */
+static const int zero = 0;
+
+/* Custom standard function pointers. */
+void *( *bson_malloc_func )( size_t ) = malloc;
+void *( *bson_realloc_func )( void *, size_t ) = realloc;
+void ( *bson_free )( void * ) = free;
+bson_printf_func bson_printf = printf;
+bson_fprintf_func bson_fprintf = fprintf;
+bson_sprintf_func bson_sprintf = sprintf;
+
+static int _bson_errprintf( const char *, ... );
+bson_printf_func bson_errprintf = _bson_errprintf;
+
+/* ObjectId fuzz functions. */
+static int ( *oid_fuzz_func )( void ) = NULL;
+static int ( *oid_inc_func )( void ) = NULL;
+
+/* ----------------------------
+ READING
+ ------------------------------ */
+
+bson *bson_empty( bson *obj ) {
+ static char *data = "\005\0\0\0\0";
+ bson_init_data( obj, data );
+ obj->finished = 1;
+ obj->err = 0;
+ obj->stackPos = 0;
+ return obj;
+}
+
+int bson_copy( bson *out, const bson *in ) {
+ if ( !out ) return BSON_ERROR;
+ if ( !in->finished ) return BSON_ERROR;
+ bson_init_size( out, bson_size( in ) );
+ memcpy( out->data, in->data, bson_size( in ) );
+ out->finished = 1;
+
+ return BSON_OK;
+}
+
+int bson_init_data( bson *b, char *data ) {
+ b->data = data;
+ return BSON_OK;
+}
+
+int bson_init_finished_data( bson *b, char *data ) {
+ bson_init_data( b, data );
+ b->stackPos = 0;
+ b->finished = 1;
+ return BSON_OK;
+}
+
+static void _bson_reset( bson *b ) {
+ b->finished = 0;
+ b->stackPos = 0;
+ b->err = 0;
+ b->errstr = NULL;
+}
+
+int bson_size( const bson *b ) {
+ int i;
+ if ( ! b || ! b->data )
+ return 0;
+ bson_little_endian32( &i, b->data );
+ return i;
+}
+
+const char *bson_data( bson *b ) {
+ return (const char *)b->data;
+}
+
+static char hexbyte( char hex ) {
+ switch ( hex ) {
+ case '0':
+ return 0x0;
+ case '1':
+ return 0x1;
+ case '2':
+ return 0x2;
+ case '3':
+ return 0x3;
+ case '4':
+ return 0x4;
+ case '5':
+ return 0x5;
+ case '6':
+ return 0x6;
+ case '7':
+ return 0x7;
+ case '8':
+ return 0x8;
+ case '9':
+ return 0x9;
+ case 'a':
+ case 'A':
+ return 0xa;
+ case 'b':
+ case 'B':
+ return 0xb;
+ case 'c':
+ case 'C':
+ return 0xc;
+ case 'd':
+ case 'D':
+ return 0xd;
+ case 'e':
+ case 'E':
+ return 0xe;
+ case 'f':
+ case 'F':
+ return 0xf;
+ default:
+ return 0x0; /* something smarter? */
+ }
+}
+
+void bson_oid_from_string( bson_oid_t *oid, const char *str ) {
+ int i;
+ for ( i=0; i<12; i++ ) {
+ oid->bytes[i] = ( hexbyte( str[2*i] ) << 4 ) | hexbyte( str[2*i + 1] );
+ }
+}
+
+void bson_oid_to_string( const bson_oid_t *oid, char *str ) {
+ static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+ int i;
+ for ( i=0; i<12; i++ ) {
+ str[2*i] = hex[( oid->bytes[i] & 0xf0 ) >> 4];
+ str[2*i + 1] = hex[ oid->bytes[i] & 0x0f ];
+ }
+ str[24] = '\0';
+}
+
+void bson_set_oid_fuzz( int ( *func )( void ) ) {
+ oid_fuzz_func = func;
+}
+
+void bson_set_oid_inc( int ( *func )( void ) ) {
+ oid_inc_func = func;
+}
+
+void bson_oid_gen( bson_oid_t *oid ) {
+ static int incr = 0;
+ static int fuzz = 0;
+ int i;
+ int t = time( NULL );
+
+ if( oid_inc_func )
+ i = oid_inc_func();
+ else
+ i = incr++;
+
+ if ( !fuzz ) {
+ if ( oid_fuzz_func )
+ fuzz = oid_fuzz_func();
+ else {
+ srand( t );
+ fuzz = rand();
+ }
+ }
+
+ bson_big_endian32( &oid->ints[0], &t );
+ oid->ints[1] = fuzz;
+ bson_big_endian32( &oid->ints[2], &i );
+}
+
+time_t bson_oid_generated_time( bson_oid_t *oid ) {
+ time_t out;
+ bson_big_endian32( &out, &oid->ints[0] );
+
+ return out;
+}
+
+void bson_print( bson *b ) {
+ bson_print_raw( b->data , 0 );
+}
+
+void bson_print_raw( const char *data , int depth ) {
+ bson_iterator i;
+ const char *key;
+ int temp;
+ bson_timestamp_t ts;
+ char oidhex[25];
+ bson scope;
+ bson_iterator_from_buffer( &i, data );
+
+ while ( bson_iterator_next( &i ) ) {
+ bson_type t = bson_iterator_type( &i );
+ if ( t == 0 )
+ break;
+ key = bson_iterator_key( &i );
+
+ for ( temp=0; temp<=depth; temp++ )
+ bson_printf( "\t" );
+ bson_printf( "%s : %d \t " , key , t );
+ switch ( t ) {
+ case BSON_DOUBLE:
+ bson_printf( "%f" , bson_iterator_double( &i ) );
+ break;
+ case BSON_STRING:
+ bson_printf( "%s" , bson_iterator_string( &i ) );
+ break;
+ case BSON_SYMBOL:
+ bson_printf( "SYMBOL: %s" , bson_iterator_string( &i ) );
+ break;
+ case BSON_OID:
+ bson_oid_to_string( bson_iterator_oid( &i ), oidhex );
+ bson_printf( "%s" , oidhex );
+ break;
+ case BSON_BOOL:
+ bson_printf( "%s" , bson_iterator_bool( &i ) ? "true" : "false" );
+ break;
+ case BSON_DATE:
+ bson_printf( "%ld" , ( long int )bson_iterator_date( &i ) );
+ break;
+ case BSON_BINDATA:
+ bson_printf( "BSON_BINDATA" );
+ break;
+ case BSON_UNDEFINED:
+ bson_printf( "BSON_UNDEFINED" );
+ break;
+ case BSON_NULL:
+ bson_printf( "BSON_NULL" );
+ break;
+ case BSON_REGEX:
+ bson_printf( "BSON_REGEX: %s", bson_iterator_regex( &i ) );
+ break;
+ case BSON_CODE:
+ bson_printf( "BSON_CODE: %s", bson_iterator_code( &i ) );
+ break;
+ case BSON_CODEWSCOPE:
+ bson_printf( "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) );
+ bson_init( &scope );
+ bson_iterator_code_scope( &i, &scope );
+ bson_printf( "\n\t SCOPE: " );
+ bson_print( &scope );
+ break;
+ case BSON_INT:
+ bson_printf( "%d" , bson_iterator_int( &i ) );
+ break;
+ case BSON_LONG:
+ bson_printf( "%lld" , ( uint64_t )bson_iterator_long( &i ) );
+ break;
+ case BSON_TIMESTAMP:
+ ts = bson_iterator_timestamp( &i );
+ bson_printf( "i: %d, t: %d", ts.i, ts.t );
+ break;
+ case BSON_OBJECT:
+ case BSON_ARRAY:
+ bson_printf( "\n" );
+ bson_print_raw( bson_iterator_value( &i ) , depth + 1 );
+ break;
+ default:
+ bson_errprintf( "can't print type : %d\n" , t );
+ }
+ bson_printf( "\n" );
+ }
+}
+
+/* ----------------------------
+ ITERATOR
+ ------------------------------ */
+
+void bson_iterator_init( bson_iterator *i, const bson *b ) {
+ i->cur = b->data + 4;
+ i->first = 1;
+}
+
+void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ) {
+ i->cur = buffer + 4;
+ i->first = 1;
+}
+
+bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ) {
+ bson_iterator_init( it, (bson *)obj );
+ while( bson_iterator_next( it ) ) {
+ if ( strcmp( name, bson_iterator_key( it ) ) == 0 )
+ break;
+ }
+ return bson_iterator_type( it );
+}
+
+bson_bool_t bson_iterator_more( const bson_iterator *i ) {
+ return *( i->cur );
+}
+
+bson_type bson_iterator_next( bson_iterator *i ) {
+ int ds;
+
+ if ( i->first ) {
+ i->first = 0;
+ return ( bson_type )( *i->cur );
+ }
+
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_EOO:
+ return BSON_EOO; /* don't advance */
+ case BSON_UNDEFINED:
+ case BSON_NULL:
+ ds = 0;
+ break;
+ case BSON_BOOL:
+ ds = 1;
+ break;
+ case BSON_INT:
+ ds = 4;
+ break;
+ case BSON_LONG:
+ case BSON_DOUBLE:
+ case BSON_TIMESTAMP:
+ case BSON_DATE:
+ ds = 8;
+ break;
+ case BSON_OID:
+ ds = 12;
+ break;
+ case BSON_STRING:
+ case BSON_SYMBOL:
+ case BSON_CODE:
+ ds = 4 + bson_iterator_int_raw( i );
+ break;
+ case BSON_BINDATA:
+ ds = 5 + bson_iterator_int_raw( i );
+ break;
+ case BSON_OBJECT:
+ case BSON_ARRAY:
+ case BSON_CODEWSCOPE:
+ ds = bson_iterator_int_raw( i );
+ break;
+ case BSON_DBREF:
+ ds = 4+12 + bson_iterator_int_raw( i );
+ break;
+ case BSON_REGEX: {
+ const char *s = bson_iterator_value( i );
+ const char *p = s;
+ p += strlen( p )+1;
+ p += strlen( p )+1;
+ ds = p-s;
+ break;
+ }
+
+ default: {
+ char msg[] = "unknown type: 000000000000";
+ bson_numstr( msg+14, ( unsigned )( i->cur[0] ) );
+ bson_fatal_msg( 0, msg );
+ return (bson_type)0;
+ }
+ }
+
+ i->cur += 1 + strlen( i->cur + 1 ) + 1 + ds;
+
+ return ( bson_type )( *i->cur );
+}
+
+bson_type bson_iterator_type( const bson_iterator *i ) {
+ return ( bson_type )i->cur[0];
+}
+
+const char *bson_iterator_key( const bson_iterator *i ) {
+ return i->cur + 1;
+}
+
+const char *bson_iterator_value( const bson_iterator *i ) {
+ const char *t = i->cur + 1;
+ t += strlen( t ) + 1;
+ return t;
+}
+
+/* types */
+
+int bson_iterator_int_raw( const bson_iterator *i ) {
+ int out;
+ bson_little_endian32( &out, bson_iterator_value( i ) );
+ return out;
+}
+
+double bson_iterator_double_raw( const bson_iterator *i ) {
+ double out;
+ bson_little_endian64( &out, bson_iterator_value( i ) );
+ return out;
+}
+
+int64_t bson_iterator_long_raw( const bson_iterator *i ) {
+ int64_t out;
+ bson_little_endian64( &out, bson_iterator_value( i ) );
+ return out;
+}
+
+bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ) {
+ return bson_iterator_value( i )[0];
+}
+
+bson_oid_t *bson_iterator_oid( const bson_iterator *i ) {
+ return ( bson_oid_t * )bson_iterator_value( i );
+}
+
+int bson_iterator_int( const bson_iterator *i ) {
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_INT:
+ return bson_iterator_int_raw( i );
+ case BSON_LONG:
+ return bson_iterator_long_raw( i );
+ case BSON_DOUBLE:
+ return bson_iterator_double_raw( i );
+ default:
+ return 0;
+ }
+}
+
+double bson_iterator_double( const bson_iterator *i ) {
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_INT:
+ return bson_iterator_int_raw( i );
+ case BSON_LONG:
+ return bson_iterator_long_raw( i );
+ case BSON_DOUBLE:
+ return bson_iterator_double_raw( i );
+ default:
+ return 0;
+ }
+}
+
+int64_t bson_iterator_long( const bson_iterator *i ) {
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_INT:
+ return bson_iterator_int_raw( i );
+ case BSON_LONG:
+ return bson_iterator_long_raw( i );
+ case BSON_DOUBLE:
+ return bson_iterator_double_raw( i );
+ default:
+ return 0;
+ }
+}
+
+bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ) {
+ bson_timestamp_t ts;
+ bson_little_endian32( &( ts.i ), bson_iterator_value( i ) );
+ bson_little_endian32( &( ts.t ), bson_iterator_value( i ) + 4 );
+ return ts;
+}
+
+bson_bool_t bson_iterator_bool( const bson_iterator *i ) {
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_BOOL:
+ return bson_iterator_bool_raw( i );
+ case BSON_INT:
+ return bson_iterator_int_raw( i ) != 0;
+ case BSON_LONG:
+ return bson_iterator_long_raw( i ) != 0;
+ case BSON_DOUBLE:
+ return bson_iterator_double_raw( i ) != 0;
+ case BSON_EOO:
+ case BSON_NULL:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+const char *bson_iterator_string( const bson_iterator *i ) {
+ return bson_iterator_value( i ) + 4;
+}
+
+int bson_iterator_string_len( const bson_iterator *i ) {
+ return bson_iterator_int_raw( i );
+}
+
+const char *bson_iterator_code( const bson_iterator *i ) {
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_STRING:
+ case BSON_CODE:
+ return bson_iterator_value( i ) + 4;
+ case BSON_CODEWSCOPE:
+ return bson_iterator_value( i ) + 8;
+ default:
+ return NULL;
+ }
+}
+
+void bson_iterator_code_scope( const bson_iterator *i, bson *scope ) {
+ if ( bson_iterator_type( i ) == BSON_CODEWSCOPE ) {
+ int code_len;
+ bson_little_endian32( &code_len, bson_iterator_value( i )+4 );
+ bson_init_data( scope, (char*)((void *)(bson_iterator_value(i)+8+code_len )));
+ _bson_reset( scope );
+ scope->finished = 1;
+ } else {
+ bson_empty( scope );
+ }
+}
+
+bson_date_t bson_iterator_date( const bson_iterator *i ) {
+ return bson_iterator_long_raw( i );
+}
+
+time_t bson_iterator_time_t( const bson_iterator *i ) {
+ return bson_iterator_date( i ) / 1000;
+}
+
+int bson_iterator_bin_len( const bson_iterator *i ) {
+ return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
+ ? bson_iterator_int_raw( i ) - 4
+ : bson_iterator_int_raw( i );
+}
+
+char bson_iterator_bin_type( const bson_iterator *i ) {
+ return bson_iterator_value( i )[4];
+}
+
+const char *bson_iterator_bin_data( const bson_iterator *i ) {
+ return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
+ ? bson_iterator_value( i ) + 9
+ : bson_iterator_value( i ) + 5;
+}
+
+const char *bson_iterator_regex( const bson_iterator *i ) {
+ return bson_iterator_value( i );
+}
+
+const char *bson_iterator_regex_opts( const bson_iterator *i ) {
+ const char *p = bson_iterator_value( i );
+ return p + strlen( p ) + 1;
+
+}
+
+void bson_iterator_subobject( const bson_iterator *i, bson *sub ) {
+ bson_init_data( sub, ( char * )bson_iterator_value( i ) );
+ _bson_reset( sub );
+ sub->finished = 1;
+}
+
+void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ) {
+ bson_iterator_from_buffer( sub, bson_iterator_value( i ) );
+}
+
+/* ----------------------------
+ BUILDING
+ ------------------------------ */
+
+static void _bson_init_size( bson *b, int size ) {
+ if( size == 0 )
+ b->data = NULL;
+ else
+ b->data = ( char * )bson_malloc( size );
+ b->dataSize = size;
+ b->cur = b->data + 4;
+ _bson_reset( b );
+}
+
+void bson_init( bson *b ) {
+ _bson_init_size( b, initialBufferSize );
+}
+
+void bson_init_size( bson *b, int size ) {
+ _bson_init_size( b, size );
+}
+
+static void bson_append_byte( bson *b, char c ) {
+ b->cur[0] = c;
+ b->cur++;
+}
+
+static void bson_append( bson *b, const void *data, int len ) {
+ memcpy( b->cur , data , len );
+ b->cur += len;
+}
+
+static void bson_append32( bson *b, const void *data ) {
+ bson_little_endian32( b->cur, data );
+ b->cur += 4;
+}
+
+static void bson_append64( bson *b, const void *data ) {
+ bson_little_endian64( b->cur, data );
+ b->cur += 8;
+}
+
+int bson_ensure_space( bson *b, const int bytesNeeded ) {
+ int pos = b->cur - b->data;
+ char *orig = b->data;
+ int new_size;
+
+ if ( pos + bytesNeeded <= b->dataSize )
+ return BSON_OK;
+
+ new_size = 1.5 * ( b->dataSize + bytesNeeded );
+
+ if( new_size < b->dataSize ) {
+ if( ( b->dataSize + bytesNeeded ) < INT_MAX )
+ new_size = INT_MAX;
+ else {
+ b->err = BSON_SIZE_OVERFLOW;
+ return BSON_ERROR;
+ }
+ }
+
+ b->data = (char*)bson_realloc( b->data, new_size );
+ if ( !b->data )
+ bson_fatal_msg( !!b->data, "realloc() failed" );
+
+ b->dataSize = new_size;
+ b->cur += b->data - orig;
+
+ return BSON_OK;
+}
+
+int bson_finish( bson *b ) {
+ int i;
+
+ if( b->err & BSON_NOT_UTF8 )
+ return BSON_ERROR;
+
+ if ( ! b->finished ) {
+ if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
+ bson_append_byte( b, 0 );
+ i = b->cur - b->data;
+ bson_little_endian32( b->data, &i );
+ b->finished = 1;
+ }
+
+ return BSON_OK;
+}
+
+void bson_destroy( bson *b ) {
+ bson_free( b->data );
+ b->err = 0;
+ b->data = 0;
+ b->cur = 0;
+ b->finished = 1;
+}
+
+static int bson_append_estart( bson *b, int type, const char *name, const int dataSize ) {
+ const int len = strlen( name ) + 1;
+
+ if ( b->finished ) {
+ b->err |= BSON_ALREADY_FINISHED;
+ return BSON_ERROR;
+ }
+
+ if ( bson_ensure_space( b, 1 + len + dataSize ) == BSON_ERROR ) {
+ return BSON_ERROR;
+ }
+
+ if( bson_check_field_name( b, ( const char * )name, len - 1 ) == BSON_ERROR ) {
+ bson_builder_error( b );
+ return BSON_ERROR;
+ }
+
+ bson_append_byte( b, ( char )type );
+ bson_append( b, name, len );
+ return BSON_OK;
+}
+
+/* ----------------------------
+ BUILDING TYPES
+ ------------------------------ */
+
+int bson_append_int( bson *b, const char *name, const int i ) {
+ if ( bson_append_estart( b, BSON_INT, name, 4 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append32( b , &i );
+ return BSON_OK;
+}
+
+int bson_append_long( bson *b, const char *name, const int64_t i ) {
+ if ( bson_append_estart( b , BSON_LONG, name, 8 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append64( b , &i );
+ return BSON_OK;
+}
+
+int bson_append_double( bson *b, const char *name, const double d ) {
+ if ( bson_append_estart( b, BSON_DOUBLE, name, 8 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append64( b , &d );
+ return BSON_OK;
+}
+
+int bson_append_bool( bson *b, const char *name, const bson_bool_t i ) {
+ if ( bson_append_estart( b, BSON_BOOL, name, 1 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append_byte( b , i != 0 );
+ return BSON_OK;
+}
+
+int bson_append_null( bson *b, const char *name ) {
+ if ( bson_append_estart( b , BSON_NULL, name, 0 ) == BSON_ERROR )
+ return BSON_ERROR;
+ return BSON_OK;
+}
+
+int bson_append_undefined( bson *b, const char *name ) {
+ if ( bson_append_estart( b, BSON_UNDEFINED, name, 0 ) == BSON_ERROR )
+ return BSON_ERROR;
+ return BSON_OK;
+}
+
+static int bson_append_string_base( bson *b, const char *name,
+ const char *value, int len, bson_type type ) {
+
+ int sl = len + 1;
+ if ( bson_check_string( b, ( const char * )value, sl - 1 ) == BSON_ERROR )
+ return BSON_ERROR;
+ if ( bson_append_estart( b, type, name, 4 + sl ) == BSON_ERROR ) {
+ return BSON_ERROR;
+ }
+ bson_append32( b , &sl );
+ bson_append( b , value , sl - 1 );
+ bson_append( b , "\0" , 1 );
+ return BSON_OK;
+}
+
+int bson_append_string( bson *b, const char *name, const char *value ) {
+ return bson_append_string_base( b, name, value, strlen ( value ), BSON_STRING );
+}
+
+int bson_append_symbol( bson *b, const char *name, const char *value ) {
+ return bson_append_string_base( b, name, value, strlen ( value ), BSON_SYMBOL );
+}
+
+int bson_append_code( bson *b, const char *name, const char *value ) {
+ return bson_append_string_base( b, name, value, strlen ( value ), BSON_CODE );
+}
+
+int bson_append_string_n( bson *b, const char *name, const char *value, int len ) {
+ return bson_append_string_base( b, name, value, len, BSON_STRING );
+}
+
+int bson_append_symbol_n( bson *b, const char *name, const char *value, int len ) {
+ return bson_append_string_base( b, name, value, len, BSON_SYMBOL );
+}
+
+int bson_append_code_n( bson *b, const char *name, const char *value, int len ) {
+ return bson_append_string_base( b, name, value, len, BSON_CODE );
+}
+
+int bson_append_code_w_scope_n( bson *b, const char *name,
+ const char *code, int len, const bson *scope ) {
+
+ int sl = len + 1;
+ int size = 4 + 4 + sl + bson_size( scope );
+ if ( bson_append_estart( b, BSON_CODEWSCOPE, name, size ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append32( b, &size );
+ bson_append32( b, &sl );
+ bson_append( b, code, sl );
+ bson_append( b, scope->data, bson_size( scope ) );
+ return BSON_OK;
+}
+
+int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ) {
+ return bson_append_code_w_scope_n( b, name, code, strlen ( code ), scope );
+}
+
+int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ) {
+ if ( type == BSON_BIN_BINARY_OLD ) {
+ int subtwolen = len + 4;
+ if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+4+len ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append32( b, &subtwolen );
+ bson_append_byte( b, type );
+ bson_append32( b, &len );
+ bson_append( b, str, len );
+ } else {
+ if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+len ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append32( b, &len );
+ bson_append_byte( b, type );
+ bson_append( b, str, len );
+ }
+ return BSON_OK;
+}
+
+int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ) {
+ if ( bson_append_estart( b, BSON_OID, name, 12 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append( b , oid , 12 );
+ return BSON_OK;
+}
+
+int bson_append_new_oid( bson *b, const char *name ) {
+ bson_oid_t oid;
+ bson_oid_gen( &oid );
+ return bson_append_oid( b, name, &oid );
+}
+
+int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ) {
+ const int plen = strlen( pattern )+1;
+ const int olen = strlen( opts )+1;
+ if ( bson_append_estart( b, BSON_REGEX, name, plen + olen ) == BSON_ERROR )
+ return BSON_ERROR;
+ if ( bson_check_string( b, pattern, plen - 1 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append( b , pattern , plen );
+ bson_append( b , opts , olen );
+ return BSON_OK;
+}
+
+int bson_append_bson( bson *b, const char *name, const bson *bson ) {
+ if ( bson_append_estart( b, BSON_OBJECT, name, bson_size( bson ) ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append( b , bson->data , bson_size( bson ) );
+ return BSON_OK;
+}
+
+int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ) {
+ bson_iterator next = *elem;
+ int size;
+
+ bson_iterator_next( &next );
+ size = next.cur - elem->cur;
+
+ if ( name_or_null == NULL ) {
+ if( bson_ensure_space( b, size ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append( b, elem->cur, size );
+ } else {
+ int data_size = size - 2 - strlen( bson_iterator_key( elem ) );
+ bson_append_estart( b, elem->cur[0], name_or_null, data_size );
+ bson_append( b, bson_iterator_value( elem ), data_size );
+ }
+
+ return BSON_OK;
+}
+
+int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ) {
+ if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
+
+ bson_append32( b , &( ts->i ) );
+ bson_append32( b , &( ts->t ) );
+
+ return BSON_OK;
+}
+
+int bson_append_date( bson *b, const char *name, bson_date_t millis ) {
+ if ( bson_append_estart( b, BSON_DATE, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
+ bson_append64( b , &millis );
+ return BSON_OK;
+}
+
+int bson_append_time_t( bson *b, const char *name, time_t secs ) {
+ return bson_append_date( b, name, ( bson_date_t )secs * 1000 );
+}
+
+int bson_append_start_object( bson *b, const char *name ) {
+ if ( bson_append_estart( b, BSON_OBJECT, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
+ b->stack[ b->stackPos++ ] = b->cur - b->data;
+ bson_append32( b , &zero );
+ return BSON_OK;
+}
+
+int bson_append_start_array( bson *b, const char *name ) {
+ if ( bson_append_estart( b, BSON_ARRAY, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
+ b->stack[ b->stackPos++ ] = b->cur - b->data;
+ bson_append32( b , &zero );
+ return BSON_OK;
+}
+
+int bson_append_finish_object( bson *b ) {
+ char *start;
+ int i;
+ if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
+ bson_append_byte( b , 0 );
+
+ start = b->data + b->stack[ --b->stackPos ];
+ i = b->cur - start;
+ bson_little_endian32( start, &i );
+
+ return BSON_OK;
+}
+
+int bson_append_finish_array( bson *b ) {
+ return bson_append_finish_object( b );
+}
+
+
+/* Error handling and allocators. */
+
+static bson_err_handler err_handler = NULL;
+
+bson_err_handler set_bson_err_handler( bson_err_handler func ) {
+ bson_err_handler old = err_handler;
+ err_handler = func;
+ return old;
+}
+
+void *bson_malloc( int size ) {
+ void *p;
+ p = bson_malloc_func( size );
+ bson_fatal_msg( !!p, "malloc() failed" );
+ return p;
+}
+
+void *bson_realloc( void *ptr, int size ) {
+ void *p;
+ p = bson_realloc_func( ptr, size );
+ bson_fatal_msg( !!p, "realloc() failed" );
+ return p;
+}
+
+int _bson_errprintf( const char *format, ... ) {
+ va_list ap;
+ int ret;
+ va_start( ap, format );
+ ret = vfprintf( stderr, format, ap );
+ va_end( ap );
+
+ return ret;
+}
+
+/**
+ * This method is invoked when a non-fatal bson error is encountered.
+ * Calls the error handler if available.
+ *
+ * @param
+ */
+void bson_builder_error( bson *b ) {
+ if( err_handler )
+ err_handler( "BSON error." );
+}
+
+void bson_fatal( int ok ) {
+ bson_fatal_msg( ok, "" );
+}
+
+void bson_fatal_msg( int ok , const char *msg ) {
+ if ( ok )
+ return;
+
+ if ( err_handler ) {
+ err_handler( msg );
+ }
+
+ bson_errprintf( "error: %s\n" , msg );
+ exit( -5 );
+}
+
+
+/* Efficiently copy an integer to a string. */
+
+void bson_numstr( char *str, int i ) {
+ if( i < 1000 )
+ memcpy( str, bson_numstrs[i], 4 );
+ else
+ bson_sprintf( str,"%d", i );
+}
+
+/* encoding.c */
+
+/*
+ * Copyright 2009-2011 10gen, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Portions Copyright 2001 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * The length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns 0. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+static int isLegalUTF8( const unsigned char *source, int length ) {
+ unsigned char a;
+ const unsigned char *srcptr = source + length;
+ switch ( length ) {
+ default:
+ return 0;
+ /* Everything else falls through when "true"... */
+ case 4:
+ if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
+ case 3:
+ if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
+ case 2:
+ if ( ( a = ( *--srcptr ) ) > 0xBF ) return 0;
+ switch ( *source ) {
+ /* no fall-through in this inner switch */
+ case 0xE0:
+ if ( a < 0xA0 ) return 0;
+ break;
+ case 0xF0:
+ if ( a < 0x90 ) return 0;
+ break;
+ case 0xF4:
+ if ( a > 0x8F ) return 0;
+ break;
+ default:
+ if ( a < 0x80 ) return 0;
+ }
+ case 1:
+ if ( *source >= 0x80 && *source < 0xC2 ) return 0;
+ if ( *source > 0xF4 ) return 0;
+ }
+ return 1;
+}
+
+static int bson_validate_string( bson *b, const unsigned char *string,
+ const int length, const char check_utf8, const char check_dot,
+ const char check_dollar ) {
+
+ int position = 0;
+ int sequence_length = 1;
+
+ if( check_dollar && string[0] == '$' ) {
+ b->err |= BSON_FIELD_INIT_DOLLAR;
+ }
+
+ while ( position < length ) {
+ if ( check_dot && *( string + position ) == '.' ) {
+ b->err |= BSON_FIELD_HAS_DOT;
+ }
+
+ if ( check_utf8 ) {
+ sequence_length = trailingBytesForUTF8[*( string + position )] + 1;
+ if ( ( position + sequence_length ) > length ) {
+ b->err |= BSON_NOT_UTF8;
+ return BSON_ERROR;
+ }
+ if ( !isLegalUTF8( string + position, sequence_length ) ) {
+ b->err |= BSON_NOT_UTF8;
+ return BSON_ERROR;
+ }
+ }
+ position += sequence_length;
+ }
+
+ return BSON_OK;
+}
+
+
+int bson_check_string( bson *b, const char *string,
+ const int length ) {
+
+ return bson_validate_string( b, ( const unsigned char * )string, length, 1, 0, 0 );
+}
+
+int bson_check_field_name( bson *b, const char *string,
+ const int length ) {
+
+ return bson_validate_string( b, ( const unsigned char * )string, length, 1, 1, 1 );
+}
diff --git a/src/bson/BSON.h b/src/bson/BSON.h
new file mode 100644
index 0000000..ff3aa8a
--- /dev/null
+++ b/src/bson/BSON.h
@@ -0,0 +1,1205 @@
+/**
+ * @file bson.h
+ * @brief BSON Declarations
+ */
+
+/* Copyright 2009-2011 10gen Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BSON_H_
+#define _BSON_H_
+
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "Config.h"
+
+#if defined(LIN) || defined(USE_STDINT)
+#include <sys/types.h>
+#include <stdint.h>
+#else
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#endif
+
+
+#define BSON_OK 0
+#define BSON_ERROR -1
+
+static const char bson_numstrs[1000][4] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
+ "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
+ "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
+ "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
+ "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
+ "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
+ "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
+ "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
+ "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
+
+ "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
+ "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
+ "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
+ "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
+ "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
+ "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
+ "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
+ "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
+ "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
+ "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
+
+ "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
+ "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
+ "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
+ "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
+ "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
+ "250", "251", "252", "253", "254", "255", "256", "257", "258", "259",
+ "260", "261", "262", "263", "264", "265", "266", "267", "268", "269",
+ "270", "271", "272", "273", "274", "275", "276", "277", "278", "279",
+ "280", "281", "282", "283", "284", "285", "286", "287", "288", "289",
+ "290", "291", "292", "293", "294", "295", "296", "297", "298", "299",
+
+ "300", "301", "302", "303", "304", "305", "306", "307", "308", "309",
+ "310", "311", "312", "313", "314", "315", "316", "317", "318", "319",
+ "320", "321", "322", "323", "324", "325", "326", "327", "328", "329",
+ "330", "331", "332", "333", "334", "335", "336", "337", "338", "339",
+ "340", "341", "342", "343", "344", "345", "346", "347", "348", "349",
+ "350", "351", "352", "353", "354", "355", "356", "357", "358", "359",
+ "360", "361", "362", "363", "364", "365", "366", "367", "368", "369",
+ "370", "371", "372", "373", "374", "375", "376", "377", "378", "379",
+ "380", "381", "382", "383", "384", "385", "386", "387", "388", "389",
+ "390", "391", "392", "393", "394", "395", "396", "397", "398", "399",
+
+ "400", "401", "402", "403", "404", "405", "406", "407", "408", "409",
+ "410", "411", "412", "413", "414", "415", "416", "417", "418", "419",
+ "420", "421", "422", "423", "424", "425", "426", "427", "428", "429",
+ "430", "431", "432", "433", "434", "435", "436", "437", "438", "439",
+ "440", "441", "442", "443", "444", "445", "446", "447", "448", "449",
+ "450", "451", "452", "453", "454", "455", "456", "457", "458", "459",
+ "460", "461", "462", "463", "464", "465", "466", "467", "468", "469",
+ "470", "471", "472", "473", "474", "475", "476", "477", "478", "479",
+ "480", "481", "482", "483", "484", "485", "486", "487", "488", "489",
+ "490", "491", "492", "493", "494", "495", "496", "497", "498", "499",
+
+ "500", "501", "502", "503", "504", "505", "506", "507", "508", "509",
+ "510", "511", "512", "513", "514", "515", "516", "517", "518", "519",
+ "520", "521", "522", "523", "524", "525", "526", "527", "528", "529",
+ "530", "531", "532", "533", "534", "535", "536", "537", "538", "539",
+ "540", "541", "542", "543", "544", "545", "546", "547", "548", "549",
+ "550", "551", "552", "553", "554", "555", "556", "557", "558", "559",
+ "560", "561", "562", "563", "564", "565", "566", "567", "568", "569",
+ "570", "571", "572", "573", "574", "575", "576", "577", "578", "579",
+ "580", "581", "582", "583", "584", "585", "586", "587", "588", "589",
+ "590", "591", "592", "593", "594", "595", "596", "597", "598", "599",
+
+ "600", "601", "602", "603", "604", "605", "606", "607", "608", "609",
+ "610", "611", "612", "613", "614", "615", "616", "617", "618", "619",
+ "620", "621", "622", "623", "624", "625", "626", "627", "628", "629",
+ "630", "631", "632", "633", "634", "635", "636", "637", "638", "639",
+ "640", "641", "642", "643", "644", "645", "646", "647", "648", "649",
+ "650", "651", "652", "653", "654", "655", "656", "657", "658", "659",
+ "660", "661", "662", "663", "664", "665", "666", "667", "668", "669",
+ "670", "671", "672", "673", "674", "675", "676", "677", "678", "679",
+ "680", "681", "682", "683", "684", "685", "686", "687", "688", "689",
+ "690", "691", "692", "693", "694", "695", "696", "697", "698", "699",
+
+ "700", "701", "702", "703", "704", "705", "706", "707", "708", "709",
+ "710", "711", "712", "713", "714", "715", "716", "717", "718", "719",
+ "720", "721", "722", "723", "724", "725", "726", "727", "728", "729",
+ "730", "731", "732", "733", "734", "735", "736", "737", "738", "739",
+ "740", "741", "742", "743", "744", "745", "746", "747", "748", "749",
+ "750", "751", "752", "753", "754", "755", "756", "757", "758", "759",
+ "760", "761", "762", "763", "764", "765", "766", "767", "768", "769",
+ "770", "771", "772", "773", "774", "775", "776", "777", "778", "779",
+ "780", "781", "782", "783", "784", "785", "786", "787", "788", "789",
+ "790", "791", "792", "793", "794", "795", "796", "797", "798", "799",
+
+ "800", "801", "802", "803", "804", "805", "806", "807", "808", "809",
+ "810", "811", "812", "813", "814", "815", "816", "817", "818", "819",
+ "820", "821", "822", "823", "824", "825", "826", "827", "828", "829",
+ "830", "831", "832", "833", "834", "835", "836", "837", "838", "839",
+ "840", "841", "842", "843", "844", "845", "846", "847", "848", "849",
+ "850", "851", "852", "853", "854", "855", "856", "857", "858", "859",
+ "860", "861", "862", "863", "864", "865", "866", "867", "868", "869",
+ "870", "871", "872", "873", "874", "875", "876", "877", "878", "879",
+ "880", "881", "882", "883", "884", "885", "886", "887", "888", "889",
+ "890", "891", "892", "893", "894", "895", "896", "897", "898", "899",
+
+ "900", "901", "902", "903", "904", "905", "906", "907", "908", "909",
+ "910", "911", "912", "913", "914", "915", "916", "917", "918", "919",
+ "920", "921", "922", "923", "924", "925", "926", "927", "928", "929",
+ "930", "931", "932", "933", "934", "935", "936", "937", "938", "939",
+ "940", "941", "942", "943", "944", "945", "946", "947", "948", "949",
+ "950", "951", "952", "953", "954", "955", "956", "957", "958", "959",
+ "960", "961", "962", "963", "964", "965", "966", "967", "968", "969",
+ "970", "971", "972", "973", "974", "975", "976", "977", "978", "979",
+ "980", "981", "982", "983", "984", "985", "986", "987", "988", "989",
+ "990", "991", "992", "993", "994", "995", "996", "997", "998", "999",
+};
+
+enum bson_error_t {
+ BSON_SIZE_OVERFLOW = 1 /**< Trying to create a BSON object larger than INT_MAX. */
+};
+
+enum bson_validity_t {
+ BSON_VALID = 0, /**< BSON is valid and UTF-8 compliant. */
+ BSON_NOT_UTF8 = ( 1<<1 ), /**< A key or a string is not valid UTF-8. */
+ BSON_FIELD_HAS_DOT = ( 1<<2 ), /**< Warning: key contains '.' character. */
+ BSON_FIELD_INIT_DOLLAR = ( 1<<3 ), /**< Warning: key starts with '$' character. */
+ BSON_ALREADY_FINISHED = ( 1<<4 ) /**< Trying to modify a finished BSON object. */
+};
+
+enum bson_binary_subtype_t {
+ BSON_BIN_BINARY = 0,
+ BSON_BIN_FUNC = 1,
+ BSON_BIN_BINARY_OLD = 2,
+ BSON_BIN_UUID = 3,
+ BSON_BIN_MD5 = 5,
+ BSON_BIN_USER = 128
+};
+
+typedef enum {
+ BSON_EOO = 0,
+ BSON_DOUBLE = 1,
+ BSON_STRING = 2,
+ BSON_OBJECT = 3,
+ BSON_ARRAY = 4,
+ BSON_BINDATA = 5,
+ BSON_UNDEFINED = 6,
+ BSON_OID = 7,
+ BSON_BOOL = 8,
+ BSON_DATE = 9,
+ BSON_NULL = 10,
+ BSON_REGEX = 11,
+ BSON_DBREF = 12, /**< Deprecated. */
+ BSON_CODE = 13,
+ BSON_SYMBOL = 14,
+ BSON_CODEWSCOPE = 15,
+ BSON_INT = 16,
+ BSON_TIMESTAMP = 17,
+ BSON_LONG = 18
+} bson_type;
+
+typedef int bson_bool_t;
+
+typedef struct {
+ const char *cur;
+ bson_bool_t first;
+} bson_iterator;
+
+typedef struct {
+ char *data;
+ char *cur;
+ int dataSize;
+ bson_bool_t finished;
+ int stack[32];
+ int stackPos;
+ int err; /**< Bitfield representing errors or warnings on this buffer */
+ char *errstr; /**< A string representation of the most recent error or warning. */
+} bson;
+
+#pragma pack(1)
+typedef union {
+ char bytes[12];
+ int ints[3];
+} bson_oid_t;
+#pragma pack()
+
+typedef int64_t bson_date_t; /* milliseconds since epoch UTC */
+
+typedef struct {
+ int i; /* increment */
+ int t; /* time in seconds */
+} bson_timestamp_t;
+
+/* ----------------------------
+ READING
+ ------------------------------ */
+
+/**
+ * Size of a BSON object.
+ *
+ * @param b the BSON object.
+ *
+ * @return the size.
+ */
+int bson_size( const bson *b );
+
+/**
+ * Print a string representation of a BSON object.
+ *
+ * @param b the BSON object to print.
+ */
+void bson_print( bson *b );
+
+/**
+ * Return a pointer to the raw buffer stored by this bson object.
+ *
+ * @param b a BSON object
+ */
+const char *bson_data( bson *b );
+
+/**
+ * Print a string representation of a BSON object.
+ *
+ * @param bson the raw data to print.
+ * @param depth the depth to recurse the object.x
+ */
+void bson_print_raw( const char *bson , int depth );
+
+/**
+ * Advance a bson_iterator to the named field.
+ *
+ * @param it the bson_iterator to use.
+ * @param obj the BSON object to use.
+ * @param name the name of the field to find.
+ *
+ * @return the type of the found object or BSON_EOO if it is not found.
+ */
+bson_type bson_find( bson_iterator *it, const bson *obj, const char *name );
+
+/**
+ * Initialize a bson_iterator.
+ *
+ * @param i the bson_iterator to initialize.
+ * @param bson the BSON object to associate with the iterator.
+ */
+void bson_iterator_init( bson_iterator *i , const bson *b );
+
+/**
+ * Initialize a bson iterator from a const char* buffer. Note
+ * that this is mostly used internally.
+ *
+ * @param i the bson_iterator to initialize.
+ * @param buffer the buffer to point to.
+ */
+void bson_iterator_from_buffer( bson_iterator *i, const char *buffer );
+
+/* more returns true for eoo. best to loop with bson_iterator_next(&it) */
+/**
+ * Check to see if the bson_iterator has more data.
+ *
+ * @param i the iterator.
+ *
+ * @return returns true if there is more data.
+ */
+bson_bool_t bson_iterator_more( const bson_iterator *i );
+
+/**
+ * Point the iterator at the next BSON object.
+ *
+ * @param i the bson_iterator.
+ *
+ * @return the type of the next BSON object.
+ */
+bson_type bson_iterator_next( bson_iterator *i );
+
+/**
+ * Get the type of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the type of the current BSON object.
+ */
+bson_type bson_iterator_type( const bson_iterator *i );
+
+/**
+ * Get the key of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the key of the current BSON object.
+ */
+const char *bson_iterator_key( const bson_iterator *i );
+
+/**
+ * Get the value of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+const char *bson_iterator_value( const bson_iterator *i );
+
+/* these convert to the right type (return 0 if non-numeric) */
+/**
+ * Get the double value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+double bson_iterator_double( const bson_iterator *i );
+
+/**
+ * Get the int value of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+int bson_iterator_int( const bson_iterator *i );
+
+/**
+ * Get the long value of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+int64_t bson_iterator_long( const bson_iterator *i );
+
+/* return the bson timestamp as a whole or in parts */
+/**
+ * Get the timestamp value of the BSON object currently pointed to by
+ * the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i );
+
+/**
+ * Get the boolean value of the BSON object currently pointed to by
+ * the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+/* false: boolean false, 0 in any type, or null */
+/* true: anything else (even empty strings and objects) */
+bson_bool_t bson_iterator_bool( const bson_iterator *i );
+
+/**
+ * Get the double value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+/* these assume you are using the right type */
+double bson_iterator_double_raw( const bson_iterator *i );
+
+/**
+ * Get the int value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+int bson_iterator_int_raw( const bson_iterator *i );
+
+/**
+ * Get the long value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+int64_t bson_iterator_long_raw( const bson_iterator *i );
+
+/**
+ * Get the bson_bool_t value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+bson_bool_t bson_iterator_bool_raw( const bson_iterator *i );
+
+/**
+ * Get the bson_oid_t value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+bson_oid_t *bson_iterator_oid( const bson_iterator *i );
+
+/**
+ * Get the string value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+/* these can also be used with bson_code and bson_symbol*/
+const char *bson_iterator_string( const bson_iterator *i );
+
+/**
+ * Get the string length of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the length of the current BSON object.
+ */
+int bson_iterator_string_len( const bson_iterator *i );
+
+/**
+ * Get the code value of the BSON object currently pointed to by the
+ * iterator. Works with bson_code, bson_codewscope, and BSON_STRING
+ * returns NULL for everything else.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the code value of the current BSON object.
+ */
+/* works with bson_code, bson_codewscope, and BSON_STRING */
+/* returns NULL for everything else */
+const char *bson_iterator_code( const bson_iterator *i );
+
+/**
+ * Calls bson_empty on scope if not a bson_codewscope
+ *
+ * @param i the bson_iterator.
+ * @param scope the bson scope.
+ */
+/* calls bson_empty on scope if not a bson_codewscope */
+void bson_iterator_code_scope( const bson_iterator *i, bson *scope );
+
+/**
+ * Get the date value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the date value of the current BSON object.
+ */
+/* both of these only work with bson_date */
+bson_date_t bson_iterator_date( const bson_iterator *i );
+
+/**
+ * Get the time value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the time value of the current BSON object.
+ */
+time_t bson_iterator_time_t( const bson_iterator *i );
+
+/**
+ * Get the length of the BSON binary object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the length of the current BSON binary object.
+ */
+int bson_iterator_bin_len( const bson_iterator *i );
+
+/**
+ * Get the type of the BSON binary object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the type of the current BSON binary object.
+ */
+char bson_iterator_bin_type( const bson_iterator *i );
+
+/**
+ * Get the value of the BSON binary object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON binary object.
+ */
+const char *bson_iterator_bin_data( const bson_iterator *i );
+
+/**
+ * Get the value of the BSON regex object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON regex object.
+ */
+const char *bson_iterator_regex( const bson_iterator *i );
+
+/**
+ * Get the options of the BSON regex object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator.
+ *
+ * @return the options of the current BSON regex object.
+ */
+const char *bson_iterator_regex_opts( const bson_iterator *i );
+
+/* these work with BSON_OBJECT and BSON_ARRAY */
+/**
+ * Get the BSON subobject currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator.
+ * @param sub the BSON subobject destination.
+ */
+void bson_iterator_subobject( const bson_iterator *i, bson *sub );
+
+/**
+ * Get a bson_iterator that on the BSON subobject.
+ *
+ * @param i the bson_iterator.
+ * @param sub the iterator to point at the BSON subobject.
+ */
+void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub );
+
+/* str must be at least 24 hex chars + null byte */
+/**
+ * Create a bson_oid_t from a string.
+ *
+ * @param oid the bson_oid_t destination.
+ * @param str a null terminated string comprised of at least 24 hex chars.
+ */
+void bson_oid_from_string( bson_oid_t *oid, const char *str );
+
+/**
+ * Create a string representation of the bson_oid_t.
+ *
+ * @param oid the bson_oid_t source.
+ * @param str the string representation destination.
+ */
+void bson_oid_to_string( const bson_oid_t *oid, char *str );
+
+/**
+ * Create a bson_oid object.
+ *
+ * @param oid the destination for the newly created bson_oid_t.
+ */
+void bson_oid_gen( bson_oid_t *oid );
+
+/**
+ * Set a function to be used to generate the second four bytes
+ * of an object id.
+ *
+ * @param func a pointer to a function that returns an int.
+ */
+void bson_set_oid_fuzz( int ( *func )( void ) );
+
+/**
+ * Set a function to be used to generate the incrementing part
+ * of an object id (last four bytes). If you need thread-safety
+ * in generating object ids, you should set this function.
+ *
+ * @param func a pointer to a function that returns an int.
+ */
+void bson_set_oid_inc( int ( *func )( void ) );
+
+/**
+ * Get the time a bson_oid_t was created.
+ *
+ * @param oid the bson_oid_t.
+ */
+time_t bson_oid_generated_time( bson_oid_t *oid ); /* Gives the time the OID was created */
+
+/* ----------------------------
+ BUILDING
+ ------------------------------ */
+
+/**
+ * Initialize a new bson object. If not created
+ * with bson_new, you must initialize each new bson
+ * object using this function.
+ *
+ * @note When finished, you must pass the bson object to
+ * bson_destroy( ).
+ */
+void bson_init( bson *b );
+
+/**
+ * Initialize a BSON object, and point its data
+ * pointer to the provided char*.
+ *
+ * @param b the BSON object to initialize.
+ * @param data the raw BSON data.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_init_data( bson *b , char *data );
+
+/**
+ * Initialize a BSON object, and point its data
+ * pointer to the provided char*. We assume
+ * that the data represents a finished BSON object.
+ *
+ * @param b the BSON object to initialize.
+ * @param data the raw BSON data.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_init_finished_data( bson *b, char *data );
+
+/**
+ * Initialize a BSON object, and set its
+ * buffer to the given size.
+ *
+ * @param b the BSON object to initialize.
+ * @param size the initial size of the buffer.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+void bson_init_size( bson *b, int size );
+
+/**
+ * Grow a bson object.
+ *
+ * @param b the bson to grow.
+ * @param bytesNeeded the additional number of bytes needed.
+ *
+ * @return BSON_OK or BSON_ERROR with the bson error object set.
+ * Exits if allocation fails.
+ */
+int bson_ensure_space( bson *b, const int bytesNeeded );
+
+/**
+ * Finalize a bson object.
+ *
+ * @param b the bson object to finalize.
+ *
+ * @return the standard error code. To deallocate memory,
+ * call bson_destroy on the bson object.
+ */
+int bson_finish( bson *b );
+
+/**
+ * Destroy a bson object.
+ *
+ * @param b the bson object to destroy.
+ *
+ */
+void bson_destroy( bson *b );
+
+/**
+ * Returns a pointer to a static empty BSON object.
+ *
+ * @param obj the BSON object to initialize.
+ *
+ * @return the empty initialized BSON object.
+ */
+/* returns pointer to static empty bson object */
+bson *bson_empty( bson *obj );
+
+/**
+ * Make a complete copy of the a BSON object.
+ * The source bson object must be in a finished
+ * state; otherwise, the copy will fail.
+ *
+ * @param out the copy destination BSON object.
+ * @param in the copy source BSON object.
+ */
+int bson_copy( bson *out, const bson *in ); /* puts data in new buffer. NOOP if out==NULL */
+
+/**
+ * Append a previously created bson_oid_t to a bson object.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the bson_oid_t.
+ * @param oid the bson_oid_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid );
+
+/**
+ * Append a bson_oid_t to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the bson_oid_t.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_new_oid( bson *b, const char *name );
+
+/**
+ * Append an int to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the int.
+ * @param i the int to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_int( bson *b, const char *name, const int i );
+
+/**
+ * Append an long to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the long.
+ * @param i the long to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_long( bson *b, const char *name, const int64_t i );
+
+/**
+ * Append an double to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the double.
+ * @param d the double to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_double( bson *b, const char *name, const double d );
+
+/**
+ * Append a string to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the string.
+ * @param str the string to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+*/
+int bson_append_string( bson *b, const char *name, const char *str );
+
+/**
+ * Append len bytes of a string to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the string.
+ * @param str the string to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_string_n( bson *b, const char *name, const char *str, int len );
+
+/**
+ * Append a symbol to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the symbol.
+ * @param str the symbol to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_symbol( bson *b, const char *name, const char *str );
+
+/**
+ * Append len bytes of a symbol to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the symbol.
+ * @param str the symbol to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_symbol_n( bson *b, const char *name, const char *str, int len );
+
+/**
+ * Append code to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the code to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_code( bson *b, const char *name, const char *str );
+
+/**
+ * Append len bytes of code to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the code to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_code_n( bson *b, const char *name, const char *str, int len );
+
+/**
+ * Append code to a bson with scope.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the string to append.
+ * @param scope a BSON object containing the scope.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope );
+
+/**
+ * Append len bytes of code to a bson with scope.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the string to append.
+ * @param len the number of bytes from str to append.
+ * @param scope a BSON object containing the scope.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_code_w_scope_n( bson *b, const char *name, const char *code, int size, const bson *scope );
+
+/**
+ * Append binary data to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the data.
+ * @param type the binary data type.
+ * @param str the binary data.
+ * @param len the length of the data.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_binary( bson *b, const char *name, char type, const char *str, int len );
+
+/**
+ * Append a bson_bool_t to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the boolean value.
+ * @param v the bson_bool_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_bool( bson *b, const char *name, const bson_bool_t v );
+
+/**
+ * Append a null value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the null value.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_null( bson *b, const char *name );
+
+/**
+ * Append an undefined value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the undefined value.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_undefined( bson *b, const char *name );
+
+/**
+ * Append a regex value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the regex value.
+ * @param pattern the regex pattern to append.
+ * @param the regex options.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts );
+
+/**
+ * Append bson data to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the bson data.
+ * @param bson the bson object to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_bson( bson *b, const char *name, const bson *bson );
+
+/**
+ * Append a BSON element to a bson from the current point of an iterator.
+ *
+ * @param b the bson to append to.
+ * @param name_or_null the key for the BSON element, or NULL.
+ * @param elem the bson_iterator.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem );
+
+/**
+ * Append a bson_timestamp_t value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the timestampe value.
+ * @param ts the bson_timestamp_t value to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts );
+
+/* these both append a bson_date */
+/**
+ * Append a bson_date_t value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the date value.
+ * @param millis the bson_date_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_date( bson *b, const char *name, bson_date_t millis );
+
+/**
+ * Append a time_t value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the date value.
+ * @param secs the time_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_time_t( bson *b, const char *name, time_t secs );
+
+/**
+ * Start appending a new object to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the name of the new object.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_start_object( bson *b, const char *name );
+
+/**
+ * Start appending a new array to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the name of the new array.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_start_array( bson *b, const char *name );
+
+/**
+ * Finish appending a new object or array to a bson.
+ *
+ * @param b the bson to append to.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_finish_object( bson *b );
+
+/**
+ * Finish appending a new object or array to a bson. This
+ * is simply an alias for bson_append_finish_object.
+ *
+ * @param b the bson to append to.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_finish_array( bson *b );
+
+void bson_numstr( char *str, int i );
+
+void bson_incnumstr( char *str );
+
+/* Error handling and stadard library function over-riding. */
+/* -------------------------------------------------------- */
+
+/* bson_err_handlers shouldn't return!!! */
+typedef void( *bson_err_handler )( const char *errmsg );
+
+typedef int (*bson_printf_func)( const char *, ... );
+typedef int (*bson_fprintf_func)( FILE *, const char *, ... );
+typedef int (*bson_sprintf_func)( char *, const char *, ... );
+
+extern void *( *bson_malloc_func )( size_t );
+extern void *( *bson_realloc_func )( void *, size_t );
+extern void ( *bson_free )( void * );
+
+extern bson_printf_func bson_printf;
+extern bson_fprintf_func bson_fprintf;
+extern bson_sprintf_func bson_sprintf;
+
+extern bson_printf_func bson_errprintf;
+
+/**
+ * Allocates memory and checks return value, exiting fatally if malloc() fails.
+ *
+ * @param size bytes to allocate.
+ *
+ * @return a pointer to the allocated memory.
+ *
+ * @sa malloc(3)
+ */
+void *bson_malloc( int size );
+
+/**
+ * Changes the size of allocated memory and checks return value,
+ * exiting fatally if realloc() fails.
+ *
+ * @param ptr pointer to the space to reallocate.
+ * @param size bytes to allocate.
+ *
+ * @return a pointer to the allocated memory.
+ *
+ * @sa realloc()
+ */
+void *bson_realloc( void *ptr, int size );
+
+/**
+ * Set a function for error handling.
+ *
+ * @param func a bson_err_handler function.
+ *
+ * @return the old error handling function, or NULL.
+ */
+bson_err_handler set_bson_err_handler( bson_err_handler func );
+
+/* does nothing if ok != 0 */
+/**
+ * Exit fatally.
+ *
+ * @param ok exits if ok is equal to 0.
+ */
+void bson_fatal( int ok );
+
+/**
+ * Exit fatally with an error message.
+ *
+ * @param ok exits if ok is equal to 0.
+ * @param msg prints to stderr before exiting.
+ */
+void bson_fatal_msg( int ok, const char *msg );
+
+/**
+ * Invoke the error handler, but do not exit.
+ *
+ * @param b the buffer object.
+ */
+void bson_builder_error( bson *b );
+
+/* encoding.h */
+
+/*
+ * Copyright 2009-2011 10gen, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Check that a field name is valid UTF8, does not start with a '$',
+ * and contains no '.' characters. Set bson bit field appropriately.
+ * Note that we don't need to check for '\0' because we're using
+ * strlen(3), which stops at '\0'.
+ *
+ * @param b The bson object to which field name will be appended.
+ * @param string The field name as char*.
+ * @param length The length of the field name.
+ *
+ * @return BSON_OK if valid UTF8 and BSON_ERROR if not. All BSON strings must be
+ * valid UTF8. This function will also check whether the string
+ * contains '.' or starts with '$', since the validity of this depends on context.
+ * Set the value of b->err appropriately.
+ */
+int bson_check_field_name( bson *b, const char *string,
+ const int length );
+
+/**
+ * Check that a string is valid UTF8. Sets the buffer bit field appropriately.
+ *
+ * @param b The bson object to which string will be appended.
+ * @param string The string to check.
+ * @param length The length of the string.
+ *
+ * @return BSON_OK if valid UTF-8; otherwise, BSON_ERROR.
+ * Sets b->err on error.
+ */
+bson_bool_t bson_check_string( bson *b, const char *string,
+ const int length );
+
+/* platform.h */
+
+/** Copyright 2009-2011 10gen Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/* all platform-specific ifdefs should go here */
+
+/* big endian is only used for OID generation. little is used everywhere else */
+//#ifdef BIG_ENDIAN
+//#define bson_little_endian64(out, in) ( bson_swap_endian64(out, in) )
+//#define bson_little_endian32(out, in) ( bson_swap_endian32(out, in) )
+//#define bson_big_endian64(out, in) ( memcpy(out, in, 8) )
+//#define bson_big_endian32(out, in) ( memcpy(out, in, 4) )
+//#else
+#define bson_little_endian64(out, in) ( memcpy(out, in, 8) )
+#define bson_little_endian32(out, in) ( memcpy(out, in, 4) )
+#define bson_big_endian64(out, in) ( bson_swap_endian64(out, in) )
+#define bson_big_endian32(out, in) ( bson_swap_endian32(out, in) )
+//#endif
+
+static TPT_INLINE void bson_swap_endian64( void *outp, const void *inp ) {
+ const char *in = ( const char * )inp;
+ char *out = ( char * )outp;
+
+ out[0] = in[7];
+ out[1] = in[6];
+ out[2] = in[5];
+ out[3] = in[4];
+ out[4] = in[3];
+ out[5] = in[2];
+ out[6] = in[1];
+ out[7] = in[0];
+
+}
+static TPT_INLINE void bson_swap_endian32( void *outp, const void *inp ) {
+ const char *in = ( const char * )inp;
+ char *out = ( char * )outp;
+
+ out[0] = in[3];
+ out[1] = in[2];
+ out[2] = in[1];
+ out[3] = in[0];
+}
+
+#endif
diff --git a/src/cajun/elements.cpp b/src/cajun/elements.cpp
new file mode 100644
index 0000000..83602f1
--- /dev/null
+++ b/src/cajun/elements.cpp
@@ -0,0 +1,403 @@
+/******************************************************************************
+
+Copyright (c) 2009-2010, Terry Caton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the projecct nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <cassert>
+#include <algorithm>
+#include <map>
+#include "visitor.h"
+#include "reader.h"
+
+/*
+
+TODO:
+* better documentation
+
+*/
+
+namespace json
+{
+
+
+TPT_NO_INLINE Exception::Exception(const std::string& sMessage) :
+ std::runtime_error(sMessage) {}
+
+
+/////////////////////////
+// UnknownElement members
+
+class UnknownElement::Imp
+{
+public:
+ virtual ~Imp() {}
+ virtual Imp* Clone() const = 0;
+
+ virtual bool Compare(const Imp& imp) const = 0;
+
+ virtual void Accept(ConstVisitor& visitor) const = 0;
+ virtual void Accept(Visitor& visitor) = 0;
+};
+
+
+template <typename ElementTypeT>
+class UnknownElement::Imp_T : public UnknownElement::Imp
+{
+public:
+ Imp_T(const ElementTypeT& element) : m_Element(element) {}
+ virtual Imp* Clone() const { return new Imp_T<ElementTypeT>(*this); }
+
+ virtual void Accept(ConstVisitor& visitor) const { visitor.Visit(m_Element); }
+ virtual void Accept(Visitor& visitor) { visitor.Visit(m_Element); }
+
+ virtual bool Compare(const Imp& imp) const
+ {
+ ConstCastVisitor_T<ElementTypeT> castVisitor;
+ imp.Accept(castVisitor);
+ return castVisitor.m_pElement &&
+ m_Element == *castVisitor.m_pElement;
+ }
+
+private:
+ ElementTypeT m_Element;
+};
+
+
+class UnknownElement::ConstCastVisitor : public ConstVisitor
+{
+ virtual void Visit(const Array& array) {}
+ virtual void Visit(const Object& object) {}
+ virtual void Visit(const Number& number) {}
+ virtual void Visit(const String& string) {}
+ virtual void Visit(const Boolean& boolean) {}
+ virtual void Visit(const Null& null) {}
+};
+
+template <typename ElementTypeT>
+class UnknownElement::ConstCastVisitor_T : public ConstCastVisitor
+{
+public:
+ ConstCastVisitor_T() : m_pElement(0) {}
+ virtual void Visit(const ElementTypeT& element) { m_pElement = &element; } // we don't know what this is, but it overrides one of the base's no-op functions
+ const ElementTypeT* m_pElement;
+};
+
+
+class UnknownElement::CastVisitor : public Visitor
+{
+ virtual void Visit(Array& array) {}
+ virtual void Visit(Object& object) {}
+ virtual void Visit(Number& number) {}
+ virtual void Visit(String& string) {}
+ virtual void Visit(Boolean& boolean) {}
+ virtual void Visit(Null& null) {}
+};
+
+template <typename ElementTypeT>
+class UnknownElement::CastVisitor_T : public CastVisitor
+{
+public:
+ CastVisitor_T() : m_pElement(0) {}
+ virtual void Visit(ElementTypeT& element) { m_pElement = &element; } // we don't know what this is, but it overrides one of the base's no-op functions
+ ElementTypeT* m_pElement;
+};
+
+
+
+
+TPT_NO_INLINE UnknownElement::UnknownElement() : m_pImp( new Imp_T<Null>( Null() ) ) {}
+TPT_NO_INLINE UnknownElement::UnknownElement(const UnknownElement& unknown) : m_pImp( unknown.m_pImp->Clone()) {}
+TPT_NO_INLINE UnknownElement::UnknownElement(const Object& object) : m_pImp( new Imp_T<Object>(object) ) {}
+TPT_NO_INLINE UnknownElement::UnknownElement(const Array& array) : m_pImp( new Imp_T<Array>(array) ) {}
+TPT_NO_INLINE UnknownElement::UnknownElement(const Number& number) : m_pImp( new Imp_T<Number>(number) ) {}
+TPT_NO_INLINE UnknownElement::UnknownElement(const Boolean& boolean) : m_pImp( new Imp_T<Boolean>(boolean) ) {}
+TPT_NO_INLINE UnknownElement::UnknownElement(const String& string) : m_pImp( new Imp_T<String>(string) ) {}
+TPT_NO_INLINE UnknownElement::UnknownElement(const Null& null) : m_pImp( new Imp_T<Null>(null) ) {}
+
+TPT_NO_INLINE UnknownElement::~UnknownElement() { delete m_pImp; }
+
+TPT_NO_INLINE UnknownElement::operator const Object& () const { return CastTo<Object>(); }
+TPT_NO_INLINE UnknownElement::operator const Array& () const { return CastTo<Array>(); }
+TPT_NO_INLINE UnknownElement::operator const Number& () const { return CastTo<Number>(); }
+TPT_NO_INLINE UnknownElement::operator const Boolean& () const { return CastTo<Boolean>(); }
+TPT_NO_INLINE UnknownElement::operator const String& () const { return CastTo<String>(); }
+TPT_NO_INLINE UnknownElement::operator const Null& () const { return CastTo<Null>(); }
+
+TPT_NO_INLINE UnknownElement::operator Object& () { return ConvertTo<Object>(); }
+TPT_NO_INLINE UnknownElement::operator Array& () { return ConvertTo<Array>(); }
+TPT_NO_INLINE UnknownElement::operator Number& () { return ConvertTo<Number>(); }
+TPT_NO_INLINE UnknownElement::operator Boolean& () { return ConvertTo<Boolean>(); }
+TPT_NO_INLINE UnknownElement::operator String& () { return ConvertTo<String>(); }
+TPT_NO_INLINE UnknownElement::operator Null& () { return ConvertTo<Null>(); }
+
+TPT_NO_INLINE UnknownElement& UnknownElement::operator = (const UnknownElement& unknown)
+{
+ // always check for this
+ if (&unknown != this)
+ {
+ // we might be copying from a subtree of ourselves. delete the old imp
+ // only after the clone operation is complete. yes, this could be made
+ // more efficient, but isn't worth the complexity
+ Imp* pOldImp = m_pImp;
+ m_pImp = unknown.m_pImp->Clone();
+ delete pOldImp;
+ }
+
+ return *this;
+}
+
+TPT_NO_INLINE UnknownElement& UnknownElement::operator[] (const std::string& key)
+{
+ // the people want an object. make us one if we aren't already
+ Object& object = ConvertTo<Object>();
+ return object[key];
+}
+
+TPT_NO_INLINE const UnknownElement& UnknownElement::operator[] (const std::string& key) const
+{
+ // throws if we aren't an object
+ const Object& object = CastTo<Object>();
+ return object[key];
+}
+
+TPT_NO_INLINE UnknownElement& UnknownElement::operator[] (size_t index)
+{
+ // the people want an array. make us one if we aren't already
+ Array& array = ConvertTo<Array>();
+ return array[index];
+}
+
+TPT_NO_INLINE const UnknownElement& UnknownElement::operator[] (size_t index) const
+{
+ // throws if we aren't an array
+ const Array& array = CastTo<Array>();
+ return array[index];
+}
+
+
+template <typename ElementTypeT>
+const ElementTypeT& UnknownElement::CastTo() const
+{
+ ConstCastVisitor_T<ElementTypeT> castVisitor;
+ m_pImp->Accept(castVisitor);
+ if (castVisitor.m_pElement == 0)
+ throw Exception("Bad cast");
+ return *castVisitor.m_pElement;
+}
+
+
+
+template <typename ElementTypeT>
+ElementTypeT& UnknownElement::ConvertTo()
+{
+ CastVisitor_T<ElementTypeT> castVisitor;
+ m_pImp->Accept(castVisitor);
+ if (castVisitor.m_pElement == 0)
+ {
+ // we're not the right type. fix it & try again
+ *this = ElementTypeT();
+ m_pImp->Accept(castVisitor);
+ }
+
+ return *castVisitor.m_pElement;
+}
+
+
+TPT_NO_INLINE void UnknownElement::Accept(ConstVisitor& visitor) const { m_pImp->Accept(visitor); }
+TPT_NO_INLINE void UnknownElement::Accept(Visitor& visitor) { m_pImp->Accept(visitor); }
+
+
+TPT_NO_INLINE bool UnknownElement::operator == (const UnknownElement& element) const
+{
+ return m_pImp->Compare(*element.m_pImp);
+}
+
+
+
+//////////////////
+// Object members
+
+
+TPT_NO_INLINE Object::Member::Member(const std::string& nameIn, const UnknownElement& elementIn) :
+ name(nameIn), element(elementIn) {}
+
+TPT_NO_INLINE bool Object::Member::operator == (const Member& member) const
+{
+ return name == member.name &&
+ element == member.element;
+}
+
+class Object::Finder : public std::unary_function<Object::Member, bool>
+{
+public:
+ Finder(const std::string& name) : m_name(name) {}
+ bool operator () (const Object::Member& member) {
+ return member.name == m_name;
+ }
+
+private:
+ std::string m_name;
+};
+
+
+
+TPT_NO_INLINE Object::iterator Object::Begin() { return m_Members.begin(); }
+TPT_NO_INLINE Object::iterator Object::End() { return m_Members.end(); }
+TPT_NO_INLINE Object::const_iterator Object::Begin() const { return m_Members.begin(); }
+TPT_NO_INLINE Object::const_iterator Object::End() const { return m_Members.end(); }
+
+TPT_NO_INLINE size_t Object::Size() const { return m_Members.size(); }
+TPT_NO_INLINE bool Object::Empty() const { return m_Members.empty(); }
+
+TPT_NO_INLINE Object::iterator Object::Find(const std::string& name)
+{
+ return std::find_if(m_Members.begin(), m_Members.end(), Finder(name));
+}
+
+TPT_NO_INLINE Object::const_iterator Object::Find(const std::string& name) const
+{
+ return std::find_if(m_Members.begin(), m_Members.end(), Finder(name));
+}
+
+TPT_NO_INLINE Object::iterator Object::Insert(const Member& member)
+{
+ return Insert(member, End());
+}
+
+TPT_NO_INLINE Object::iterator Object::Insert(const Member& member, iterator itWhere)
+{
+ iterator it = Find(member.name);
+ if (it != m_Members.end())
+ throw Exception(std::string("Object member already exists: ") + member.name);
+
+ it = m_Members.insert(itWhere, member);
+ return it;
+}
+
+TPT_NO_INLINE Object::iterator Object::Erase(iterator itWhere)
+{
+ return m_Members.erase(itWhere);
+}
+
+TPT_NO_INLINE UnknownElement& Object::operator [](const std::string& name)
+{
+
+ iterator it = Find(name);
+ if (it == m_Members.end())
+ {
+ Member member(name);
+ it = Insert(member, End());
+ }
+ return it->element;
+}
+
+TPT_NO_INLINE const UnknownElement& Object::operator [](const std::string& name) const
+{
+ const_iterator it = Find(name);
+ if (it == End())
+ throw Exception(std::string("Object member not found: ") + name);
+ return it->element;
+}
+
+TPT_NO_INLINE void Object::Clear()
+{
+ m_Members.clear();
+}
+
+TPT_NO_INLINE bool Object::operator == (const Object& object) const
+{
+ return m_Members == object.m_Members;
+}
+
+
+/////////////////
+// Array members
+
+TPT_NO_INLINE Array::iterator Array::Begin() { return m_Elements.begin(); }
+TPT_NO_INLINE Array::iterator Array::End() { return m_Elements.end(); }
+TPT_NO_INLINE Array::const_iterator Array::Begin() const { return m_Elements.begin(); }
+TPT_NO_INLINE Array::const_iterator Array::End() const { return m_Elements.end(); }
+
+TPT_NO_INLINE Array::iterator Array::Insert(const UnknownElement& element, iterator itWhere)
+{
+ return m_Elements.insert(itWhere, element);
+}
+
+TPT_NO_INLINE Array::iterator Array::Insert(const UnknownElement& element)
+{
+ return Insert(element, End());
+}
+
+TPT_NO_INLINE Array::iterator Array::Erase(iterator itWhere)
+{
+ return m_Elements.erase(itWhere);
+}
+
+TPT_NO_INLINE void Array::Resize(size_t newSize)
+{
+ m_Elements.resize(newSize);
+}
+
+TPT_NO_INLINE size_t Array::Size() const { return m_Elements.size(); }
+TPT_NO_INLINE bool Array::Empty() const { return m_Elements.empty(); }
+
+TPT_NO_INLINE UnknownElement& Array::operator[] (size_t index)
+{
+ size_t nMinSize = index + 1; // zero indexed
+ if (m_Elements.size() < nMinSize)
+ m_Elements.resize(nMinSize);
+ return m_Elements[index];
+}
+
+TPT_NO_INLINE const UnknownElement& Array::operator[] (size_t index) const
+{
+ if (index >= m_Elements.size())
+ throw Exception("Array out of bounds");
+ return m_Elements[index];
+}
+
+TPT_NO_INLINE void Array::Clear() {
+ m_Elements.clear();
+}
+
+TPT_NO_INLINE bool Array::operator == (const Array& array) const
+{
+ return m_Elements == array.m_Elements;
+}
+
+
+//////////////////
+// Null members
+
+TPT_NO_INLINE bool Null::operator == (const Null& trivial) const
+{
+ return true;
+}
+
+
+
+} // End namespace
diff --git a/src/cajun/elements.h b/src/cajun/elements.h
new file mode 100644
index 0000000..213d79d
--- /dev/null
+++ b/src/cajun/elements.h
@@ -0,0 +1,337 @@
+/******************************************************************************
+
+Copyright (c) 2009-2010, Terry Caton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the projecct nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#pragma once
+
+#include <deque>
+#include <list>
+//#include <iterator>
+#include <string>
+#include <stdexcept>
+#include "Config.h"
+/*
+
+TODO:
+* better documentation (doxygen?)
+* Unicode support
+* parent element accessors
+
+*/
+
+namespace json
+{
+
+namespace Version
+{
+ enum { MAJOR = 2 };
+ enum { MINOR = 0 };
+ enum {ENGINEERING = 2 };
+}
+
+/////////////////////////////////////////////////
+// forward declarations (more info further below)
+
+
+class Visitor;
+class ConstVisitor;
+
+template <typename ValueTypeT>
+class TrivialType_T;
+
+typedef TrivialType_T<double> Number;
+typedef TrivialType_T<bool> Boolean;
+typedef TrivialType_T<std::string> String;
+
+class Object;
+class Array;
+class Null;
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// Exception - base class for all JSON-related runtime errors
+
+class Exception : public std::runtime_error
+{
+public:
+ Exception(const std::string& sMessage);
+};
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// UnknownElement - provides a typesafe surrogate for any of the JSON-
+// sanctioned element types. This class allows the Array and Object
+// class to effectively contain a heterogeneous set of child elements.
+// The cast operators provide convenient implicit downcasting, while
+// preserving dynamic type safety by throwing an exception during a
+// a bad cast.
+// The object & array element index operators (operators [std::string]
+// and [size_t]) provide convenient, quick access to child elements.
+// They are a logical extension of the cast operators. These child
+// element accesses can be chained together, allowing the following
+// (when document structure is well-known):
+// String str = objInvoices[1]["Customer"]["Company"];
+
+
+class UnknownElement
+{
+public:
+ UnknownElement();
+ UnknownElement(const UnknownElement& unknown);
+ UnknownElement(const Object& object);
+ UnknownElement(const Array& array);
+ UnknownElement(const Number& number);
+ UnknownElement(const Boolean& boolean);
+ UnknownElement(const String& string);
+ UnknownElement(const Null& null);
+
+ ~UnknownElement();
+
+ UnknownElement& operator = (const UnknownElement& unknown);
+
+ // implicit cast to actual element type. throws on failure
+ operator const Object& () const;
+ operator const Array& () const;
+ operator const Number& () const;
+ operator const Boolean& () const;
+ operator const String& () const;
+ operator const Null& () const;
+
+ // implicit cast to actual element type. *converts* on failure, and always returns success
+ operator Object& ();
+ operator Array& ();
+ operator Number& ();
+ operator Boolean& ();
+ operator String& ();
+ operator Null& ();
+
+ // provides quick access to children when real element type is object
+ UnknownElement& operator[] (const std::string& key);
+ const UnknownElement& operator[] (const std::string& key) const;
+
+ // provides quick access to children when real element type is array
+ UnknownElement& operator[] (size_t index);
+ const UnknownElement& operator[] (size_t index) const;
+
+ // implements visitor pattern
+ void Accept(ConstVisitor& visitor) const;
+ void Accept(Visitor& visitor);
+
+ // tests equality. first checks type, then value if possible
+ bool operator == (const UnknownElement& element) const;
+
+private:
+ class Imp;
+
+ template <typename ElementTypeT>
+ class Imp_T;
+
+ class CastVisitor;
+ class ConstCastVisitor;
+
+ template <typename ElementTypeT>
+ class CastVisitor_T;
+
+ template <typename ElementTypeT>
+ class ConstCastVisitor_T;
+
+ template <typename ElementTypeT>
+ const ElementTypeT& CastTo() const;
+
+ template <typename ElementTypeT>
+ ElementTypeT& ConvertTo();
+
+ Imp* m_pImp;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// Array - mimics std::deque<UnknownElement>. The array contents are effectively
+// heterogeneous thanks to the ElementUnknown class. push_back has been replaced
+// by more generic insert functions.
+
+class Array
+{
+public:
+ typedef std::deque<UnknownElement> Elements;
+ typedef Elements::iterator iterator;
+ typedef Elements::const_iterator const_iterator;
+
+ iterator Begin();
+ iterator End();
+ const_iterator Begin() const;
+ const_iterator End() const;
+
+ iterator Insert(const UnknownElement& element, iterator itWhere);
+ iterator Insert(const UnknownElement& element);
+ iterator Erase(iterator itWhere);
+ void Resize(size_t newSize);
+ void Clear();
+
+ size_t Size() const;
+ bool Empty() const;
+
+ UnknownElement& operator[] (size_t index);
+ const UnknownElement& operator[] (size_t index) const;
+
+ bool operator == (const Array& array) const;
+
+private:
+ Elements m_Elements;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// Object - mimics std::map<std::string, UnknownElement>. The member value
+// contents are effectively heterogeneous thanks to the UnknownElement class
+
+class Object
+{
+public:
+ struct Member {
+ Member(const std::string& nameIn = std::string(), const UnknownElement& elementIn = UnknownElement());
+
+ bool operator == (const Member& member) const;
+
+ std::string name;
+ UnknownElement element;
+ };
+
+ typedef std::list<Member> Members; // map faster, but does not preserve order
+ typedef Members::iterator iterator;
+ typedef Members::const_iterator const_iterator;
+
+ bool operator == (const Object& object) const;
+
+ iterator Begin();
+ iterator End();
+ const_iterator Begin() const;
+ const_iterator End() const;
+
+ size_t Size() const;
+ bool Empty() const;
+
+ iterator Find(const std::string& name);
+ const_iterator Find(const std::string& name) const;
+
+ iterator Insert(const Member& member);
+ iterator Insert(const Member& member, iterator itWhere);
+ iterator Erase(iterator itWhere);
+ void Clear();
+
+ UnknownElement& operator [](const std::string& name);
+ const UnknownElement& operator [](const std::string& name) const;
+
+private:
+ class Finder;
+
+ Members m_Members;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// TrivialType_T - class template for encapsulates a simple data type, such as
+// a string, number, or boolean. Provides implicit const & noncost cast operators
+// for that type, allowing "DataTypeT type = trivialType;"
+
+
+template <typename DataTypeT>
+class TrivialType_T
+{
+public:
+ TrivialType_T(const DataTypeT& t = DataTypeT());
+
+ operator DataTypeT&();
+ operator const DataTypeT&() const;
+
+ DataTypeT& Value();
+ const DataTypeT& Value() const;
+
+ bool operator == (const TrivialType_T<DataTypeT>& trivial) const;
+
+private:
+ DataTypeT m_tValue;
+};
+
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// Null - doesn't do much of anything but satisfy the JSON spec. It is the default
+// element type of UnknownElement
+
+class Null
+{
+public:
+ bool operator == (const Null& trivial) const;
+};
+
+////////////////////////
+// TrivialType_T members
+
+template <typename DataTypeT>
+TrivialType_T<DataTypeT>::TrivialType_T(const DataTypeT& t) :
+ m_tValue(t) {}
+
+template <typename DataTypeT>
+TrivialType_T<DataTypeT>::operator DataTypeT&()
+{
+ return Value();
+}
+
+template <typename DataTypeT>
+TrivialType_T<DataTypeT>::operator const DataTypeT&() const
+{
+ return Value();
+}
+
+template <typename DataTypeT>
+DataTypeT& TrivialType_T<DataTypeT>::Value()
+{
+ return m_tValue;
+}
+
+template <typename DataTypeT>
+const DataTypeT& TrivialType_T<DataTypeT>::Value() const
+{
+ return m_tValue;
+}
+
+template <typename DataTypeT>
+bool TrivialType_T<DataTypeT>::operator == (const TrivialType_T<DataTypeT>& trivial) const
+{
+ return m_tValue == trivial.m_tValue;
+}
+
+
+} // End namespace
+
+
+//#include "elements.inl"
diff --git a/src/cajun/reader.cpp b/src/cajun/reader.cpp
new file mode 100644
index 0000000..be35ed1
--- /dev/null
+++ b/src/cajun/reader.cpp
@@ -0,0 +1,534 @@
+/******************************************************************************
+
+Copyright (c) 2009-2010, Terry Caton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the projecct nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <cassert>
+#include <set>
+#include <sstream>
+#include "reader.h"
+
+/*
+
+TODO:
+* better documentation
+* unicode character decoding
+
+*/
+
+namespace json
+{
+
+TPT_NO_INLINE std::istream& operator >> (std::istream& istr, UnknownElement& elementRoot) {
+ Reader::Read(elementRoot, istr);
+ return istr;
+}
+
+TPT_NO_INLINE Reader::Location::Location() :
+ m_nLine(0),
+ m_nLineOffset(0),
+ m_nDocOffset(0)
+{}
+
+
+//////////////////////
+// Reader::InputStream
+
+class Reader::InputStream // would be cool if we could inherit from std::istream & override "get"
+{
+public:
+ InputStream(std::istream& iStr) :
+ m_iStr(iStr) {}
+
+ // protect access to the input stream, so we can keeep track of document/line offsets
+ char Get(); // big, define outside
+ char Peek() {
+ assert(m_iStr.eof() == false); // enforce reading of only valid stream data
+ return m_iStr.peek();
+ }
+
+ bool EOS() {
+ m_iStr.peek(); // apparently eof flag isn't set until a character read is attempted. whatever.
+ return m_iStr.eof();
+ }
+
+ const Location& GetLocation() const { return m_Location; }
+
+private:
+ std::istream& m_iStr;
+ Location m_Location;
+};
+
+
+TPT_NO_INLINE char Reader::InputStream::Get()
+{
+ assert(m_iStr.eof() == false); // enforce reading of only valid stream data
+ char c = m_iStr.get();
+
+ ++m_Location.m_nDocOffset;
+ if (c == '\n') {
+ ++m_Location.m_nLine;
+ m_Location.m_nLineOffset = 0;
+ }
+ else {
+ ++m_Location.m_nLineOffset;
+ }
+
+ return c;
+}
+
+
+
+//////////////////////
+// Reader::TokenStream
+
+class Reader::TokenStream
+{
+public:
+ TokenStream(const Tokens& tokens);
+
+ const Token& Peek();
+ const Token& Get();
+
+ bool EOS() const;
+
+private:
+ const Tokens& m_Tokens;
+ Tokens::const_iterator m_itCurrent;
+};
+
+
+TPT_NO_INLINE Reader::TokenStream::TokenStream(const Tokens& tokens) :
+ m_Tokens(tokens),
+ m_itCurrent(tokens.begin())
+{}
+
+TPT_NO_INLINE const Reader::Token& Reader::TokenStream::Peek() {
+ if (EOS())
+ {
+ const Token& lastToken = *m_Tokens.rbegin();
+ std::string sMessage = "Unexpected end of token stream";
+ throw ParseException(sMessage, lastToken.locBegin, lastToken.locEnd); // nowhere to point to
+ }
+ return *(m_itCurrent);
+}
+
+TPT_NO_INLINE const Reader::Token& Reader::TokenStream::Get() {
+ const Token& token = Peek();
+ ++m_itCurrent;
+ return token;
+}
+
+TPT_NO_INLINE bool Reader::TokenStream::EOS() const {
+ return m_itCurrent == m_Tokens.end();
+}
+
+///////////////////
+// Reader (finally)
+
+
+TPT_NO_INLINE void Reader::Read(Object& object, std::istream& istr) { Read_i(object, istr); }
+TPT_NO_INLINE void Reader::Read(Array& array, std::istream& istr) { Read_i(array, istr); }
+TPT_NO_INLINE void Reader::Read(String& string, std::istream& istr) { Read_i(string, istr); }
+TPT_NO_INLINE void Reader::Read(Number& number, std::istream& istr) { Read_i(number, istr); }
+TPT_NO_INLINE void Reader::Read(Boolean& boolean, std::istream& istr) { Read_i(boolean, istr); }
+TPT_NO_INLINE void Reader::Read(Null& null, std::istream& istr) { Read_i(null, istr); }
+TPT_NO_INLINE void Reader::Read(UnknownElement& unknown, std::istream& istr) { Read_i(unknown, istr); }
+
+
+template <typename ElementTypeT>
+void Reader::Read_i(ElementTypeT& element, std::istream& istr)
+{
+ Reader reader;
+
+ Tokens tokens;
+ InputStream inputStream(istr);
+ reader.Scan(tokens, inputStream);
+
+ TokenStream tokenStream(tokens);
+ reader.Parse(element, tokenStream);
+
+ if (tokenStream.EOS() == false)
+ {
+ const Token& token = tokenStream.Peek();
+ std::string sMessage = std::string("Expected End of token stream; found ") + token.sValue;
+ throw ParseException(sMessage, token.locBegin, token.locEnd);
+ }
+}
+
+
+TPT_NO_INLINE void Reader::Scan(Tokens& tokens, InputStream& inputStream)
+{
+ while (EatWhiteSpace(inputStream), // ignore any leading white space...
+ inputStream.EOS() == false) // ...before checking for EOS
+ {
+ // if all goes well, we'll create a token each pass
+ Token token;
+ token.locBegin = inputStream.GetLocation();
+
+ // gives us null-terminated string
+ char sChar = inputStream.Peek();
+ switch (sChar)
+ {
+ case '{':
+ token.sValue = MatchExpectedString(inputStream, "{");
+ token.nType = Token::TOKEN_OBJECT_BEGIN;
+ break;
+
+ case '}':
+ token.sValue = MatchExpectedString(inputStream, "}");
+ token.nType = Token::TOKEN_OBJECT_END;
+ break;
+
+ case '[':
+ token.sValue = MatchExpectedString(inputStream, "[");
+ token.nType = Token::TOKEN_ARRAY_BEGIN;
+ break;
+
+ case ']':
+ token.sValue = MatchExpectedString(inputStream, "]");
+ token.nType = Token::TOKEN_ARRAY_END;
+ break;
+
+ case ',':
+ token.sValue = MatchExpectedString(inputStream, ",");
+ token.nType = Token::TOKEN_NEXT_ELEMENT;
+ break;
+
+ case ':':
+ token.sValue = MatchExpectedString(inputStream, ":");
+ token.nType = Token::TOKEN_MEMBER_ASSIGN;
+ break;
+
+ case '"':
+ token.sValue = MatchString(inputStream);
+ token.nType = Token::TOKEN_STRING;
+ break;
+
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ token.sValue = MatchNumber(inputStream);
+ token.nType = Token::TOKEN_NUMBER;
+ break;
+
+ case 't':
+ token.sValue = MatchExpectedString(inputStream, "true");
+ token.nType = Token::TOKEN_BOOLEAN;
+ break;
+
+ case 'f':
+ token.sValue = MatchExpectedString(inputStream, "false");
+ token.nType = Token::TOKEN_BOOLEAN;
+ break;
+
+ case 'n':
+ token.sValue = MatchExpectedString(inputStream, "null");
+ token.nType = Token::TOKEN_NULL;
+ break;
+
+ default:
+ {
+ std::string sErrorMessage = std::string("Unexpected character in stream: ") + sChar;
+ throw ScanException(sErrorMessage, inputStream.GetLocation());
+ }
+ }
+
+ token.locEnd = inputStream.GetLocation();
+ tokens.push_back(token);
+ }
+}
+
+
+TPT_NO_INLINE void Reader::EatWhiteSpace(InputStream& inputStream)
+{
+ while (inputStream.EOS() == false &&
+ ::isspace(inputStream.Peek()))
+ inputStream.Get();
+}
+
+TPT_NO_INLINE std::string Reader::MatchExpectedString(InputStream& inputStream, const std::string& sExpected)
+{
+ std::string::const_iterator it(sExpected.begin()),
+ itEnd(sExpected.end());
+ for ( ; it != itEnd; ++it) {
+ if (inputStream.EOS() || // did we reach the end before finding what we're looking for...
+ inputStream.Get() != *it) // ...or did we find something different?
+ {
+ std::string sMessage = std::string("Expected string: ") + sExpected;
+ throw ScanException(sMessage, inputStream.GetLocation());
+ }
+ }
+
+ // all's well if we made it here
+ return sExpected;
+}
+
+
+TPT_NO_INLINE std::string Reader::MatchString(InputStream& inputStream)
+{
+ MatchExpectedString(inputStream, "\"");
+
+ std::string string;
+ while (inputStream.EOS() == false &&
+ inputStream.Peek() != '"')
+ {
+ char c = inputStream.Get();
+
+ // escape?
+ if (c == '\\' &&
+ inputStream.EOS() == false) // shouldn't have reached the end yet
+ {
+ c = inputStream.Get();
+ switch (c) {
+ case '/': string.push_back('/'); break;
+ case '"': string.push_back('"'); break;
+ case '\\': string.push_back('\\'); break;
+ case 'b': string.push_back('\b'); break;
+ case 'f': string.push_back('\f'); break;
+ case 'n': string.push_back('\n'); break;
+ case 'r': string.push_back('\r'); break;
+ case 't': string.push_back('\t'); break;
+ //case 'u': string.push_back('\u'); break; // TODO: what do we do with this?
+ default: {
+ std::string sMessage = std::string("Unrecognized escape sequence found in string: \\") + c;
+ throw ScanException(sMessage, inputStream.GetLocation());
+ }
+ }
+ }
+ else {
+ string.push_back(c);
+ }
+ }
+
+ // eat the last '"' that we just peeked
+ MatchExpectedString(inputStream, "\"");
+
+ // all's well if we made it here
+ return string;
+}
+
+
+TPT_NO_INLINE std::string Reader::MatchNumber(InputStream& inputStream)
+{
+ const char sNumericChars[] = "0123456789.eE-+";
+ std::set<char> numericChars;
+ numericChars.insert(sNumericChars, sNumericChars + sizeof(sNumericChars));
+
+ std::string sNumber;
+ while (inputStream.EOS() == false &&
+ numericChars.find(inputStream.Peek()) != numericChars.end())
+ {
+ sNumber.push_back(inputStream.Get());
+ }
+
+ return sNumber;
+}
+
+
+TPT_NO_INLINE void Reader::Parse(UnknownElement& element, Reader::TokenStream& tokenStream)
+{
+ const Token& token = tokenStream.Peek();
+ switch (token.nType) {
+ case Token::TOKEN_OBJECT_BEGIN:
+ {
+ // implicit non-const cast will perform conversion for us (if necessary)
+ Object& object = element;
+ Parse(object, tokenStream);
+ break;
+ }
+
+ case Token::TOKEN_ARRAY_BEGIN:
+ {
+ Array& array = element;
+ Parse(array, tokenStream);
+ break;
+ }
+
+ case Token::TOKEN_STRING:
+ {
+ String& string = element;
+ Parse(string, tokenStream);
+ break;
+ }
+
+ case Token::TOKEN_NUMBER:
+ {
+ Number& number = element;
+ Parse(number, tokenStream);
+ break;
+ }
+
+ case Token::TOKEN_BOOLEAN:
+ {
+ Boolean& boolean = element;
+ Parse(boolean, tokenStream);
+ break;
+ }
+
+ case Token::TOKEN_NULL:
+ {
+ Null& null = element;
+ Parse(null, tokenStream);
+ break;
+ }
+
+ default:
+ {
+ std::string sMessage = std::string("Unexpected token: ") + token.sValue;
+ throw ParseException(sMessage, token.locBegin, token.locEnd);
+ }
+ }
+}
+
+
+TPT_NO_INLINE void Reader::Parse(Object& object, Reader::TokenStream& tokenStream)
+{
+ MatchExpectedToken(Token::TOKEN_OBJECT_BEGIN, tokenStream);
+
+ bool bContinue = (tokenStream.EOS() == false &&
+ tokenStream.Peek().nType != Token::TOKEN_OBJECT_END);
+ while (bContinue)
+ {
+ Object::Member member;
+
+ // first the member name. save the token in case we have to throw an exception
+ const Token& tokenName = tokenStream.Peek();
+ member.name = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
+
+ // ...then the key/value separator...
+ MatchExpectedToken(Token::TOKEN_MEMBER_ASSIGN, tokenStream);
+
+ // ...then the value itself (can be anything).
+ Parse(member.element, tokenStream);
+
+ // try adding it to the object (this could throw)
+ try
+ {
+ object.Insert(member);
+ }
+ catch (Exception&)
+ {
+ // must be a duplicate name
+ std::string sMessage = std::string("Duplicate object member token: ") + member.name;
+ throw ParseException(sMessage, tokenName.locBegin, tokenName.locEnd);
+ }
+
+ bContinue = (tokenStream.EOS() == false &&
+ tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT);
+ if (bContinue)
+ MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
+ }
+
+ MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream);
+}
+
+
+TPT_NO_INLINE void Reader::Parse(Array& array, Reader::TokenStream& tokenStream)
+{
+ MatchExpectedToken(Token::TOKEN_ARRAY_BEGIN, tokenStream);
+
+ bool bContinue = (tokenStream.EOS() == false &&
+ tokenStream.Peek().nType != Token::TOKEN_ARRAY_END);
+ while (bContinue)
+ {
+ // ...what's next? could be anything
+ Array::iterator itElement = array.Insert(UnknownElement());
+ UnknownElement& element = *itElement;
+ Parse(element, tokenStream);
+
+ bContinue = (tokenStream.EOS() == false &&
+ tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT);
+ if (bContinue)
+ MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
+ }
+
+ MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream);
+}
+
+
+TPT_NO_INLINE void Reader::Parse(String& string, Reader::TokenStream& tokenStream)
+{
+ string = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
+}
+
+
+TPT_NO_INLINE void Reader::Parse(Number& number, Reader::TokenStream& tokenStream)
+{
+ const Token& currentToken = tokenStream.Peek(); // might need this later for throwing exception
+ const std::string& sValue = MatchExpectedToken(Token::TOKEN_NUMBER, tokenStream);
+
+ std::istringstream iStr(sValue);
+ double dValue;
+ iStr >> dValue;
+
+ // did we consume all characters in the token?
+ if (iStr.eof() == false)
+ {
+ char c = iStr.peek();
+ std::string sMessage = std::string("Unexpected character in NUMBER token: ") + c;
+ throw ParseException(sMessage, currentToken.locBegin, currentToken.locEnd);
+ }
+
+ number = dValue;
+}
+
+
+TPT_NO_INLINE void Reader::Parse(Boolean& boolean, Reader::TokenStream& tokenStream)
+{
+ const std::string& sValue = MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream);
+ boolean = (sValue == "true" ? true : false);
+}
+
+
+TPT_NO_INLINE void Reader::Parse(Null&, Reader::TokenStream& tokenStream)
+{
+ MatchExpectedToken(Token::TOKEN_NULL, tokenStream);
+}
+
+
+TPT_NO_INLINE const std::string& Reader::MatchExpectedToken(Token::Type nExpected, Reader::TokenStream& tokenStream)
+{
+ const Token& token = tokenStream.Get();
+ if (token.nType != nExpected)
+ {
+ std::string sMessage = std::string("Unexpected token: ") + token.sValue;
+ throw ParseException(sMessage, token.locBegin, token.locEnd);
+ }
+
+ return token.sValue;
+}
+
+} // End namespace
diff --git a/src/cajun/reader.h b/src/cajun/reader.h
new file mode 100644
index 0000000..553af35
--- /dev/null
+++ b/src/cajun/reader.h
@@ -0,0 +1,148 @@
+/******************************************************************************
+
+Copyright (c) 2009-2010, Terry Caton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the projecct nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+
+
+#pragma once
+
+#include <iostream>
+#include <vector>
+#include "elements.h"
+
+namespace json
+{
+
+class Reader
+{
+public:
+ // this structure will be reported in one of the exceptions defined below
+ struct Location
+ {
+ Location();
+
+ unsigned int m_nLine; // document line, zero-indexed
+ unsigned int m_nLineOffset; // character offset from beginning of line, zero indexed
+ unsigned int m_nDocOffset; // character offset from entire document, zero indexed
+ };
+
+ // thrown during the first phase of reading. generally catches low-level problems such
+ // as errant characters or corrupt/incomplete documents
+ class ScanException : public Exception
+ {
+ public:
+ ScanException(const std::string& sMessage, const Reader::Location& locError) :
+ Exception(sMessage),
+ m_locError(locError) {}
+
+ Reader::Location m_locError;
+ };
+
+ // thrown during the second phase of reading. generally catches higher-level problems such
+ // as missing commas or brackets
+ class ParseException : public Exception
+ {
+ public:
+ ParseException(const std::string& sMessage, const Reader::Location& locTokenBegin, const Reader::Location& locTokenEnd) :
+ Exception(sMessage),
+ m_locTokenBegin(locTokenBegin),
+ m_locTokenEnd(locTokenEnd) {}
+
+ Reader::Location m_locTokenBegin;
+ Reader::Location m_locTokenEnd;
+ };
+
+
+ // if you know what the document looks like, call one of these...
+ static void Read(Object& object, std::istream& istr);
+ static void Read(Array& array, std::istream& istr);
+ static void Read(String& string, std::istream& istr);
+ static void Read(Number& number, std::istream& istr);
+ static void Read(Boolean& boolean, std::istream& istr);
+ static void Read(Null& null, std::istream& istr);
+
+ // ...otherwise, if you don't know, call this & visit it
+ static void Read(UnknownElement& elementRoot, std::istream& istr);
+
+private:
+ struct Token
+ {
+ enum Type
+ {
+ TOKEN_OBJECT_BEGIN, // {
+ TOKEN_OBJECT_END, // }
+ TOKEN_ARRAY_BEGIN, // [
+ TOKEN_ARRAY_END, // ]
+ TOKEN_NEXT_ELEMENT, // ,
+ TOKEN_MEMBER_ASSIGN, // :
+ TOKEN_STRING, // "xxx"
+ TOKEN_NUMBER, // [+/-]000.000[e[+/-]000]
+ TOKEN_BOOLEAN, // true -or- false
+ TOKEN_NULL, // null
+ };
+
+ Type nType;
+ std::string sValue;
+
+ // for malformed file debugging
+ Reader::Location locBegin;
+ Reader::Location locEnd;
+ };
+
+ class InputStream;
+ class TokenStream;
+ typedef std::vector<Token> Tokens;
+
+ template <typename ElementTypeT>
+ static void Read_i(ElementTypeT& element, std::istream& istr);
+
+ // scanning istream into token sequence
+ void Scan(Tokens& tokens, InputStream& inputStream);
+
+ void EatWhiteSpace(InputStream& inputStream);
+ std::string MatchString(InputStream& inputStream);
+ std::string MatchNumber(InputStream& inputStream);
+ std::string MatchExpectedString(InputStream& inputStream, const std::string& sExpected);
+
+ // parsing token sequence into element structure
+ void Parse(UnknownElement& element, TokenStream& tokenStream);
+ void Parse(Object& object, TokenStream& tokenStream);
+ void Parse(Array& array, TokenStream& tokenStream);
+ void Parse(String& string, TokenStream& tokenStream);
+ void Parse(Number& number, TokenStream& tokenStream);
+ void Parse(Boolean& boolean, TokenStream& tokenStream);
+ void Parse(Null& null, TokenStream& tokenStream);
+
+ const std::string& MatchExpectedToken(Token::Type nExpected, TokenStream& tokenStream);
+};
+
+
+} // End namespace
+
+
+//#include "reader.inl" \ No newline at end of file
diff --git a/src/cajun/visitor.h b/src/cajun/visitor.h
new file mode 100644
index 0000000..a06299d
--- /dev/null
+++ b/src/cajun/visitor.h
@@ -0,0 +1,65 @@
+/******************************************************************************
+
+Copyright (c) 2009-2010, Terry Caton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the projecct nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#pragma once
+
+#include "elements.h"
+
+namespace json
+{
+
+
+class Visitor
+{
+public:
+ virtual ~Visitor() {}
+
+ virtual void Visit(Array& array) = 0;
+ virtual void Visit(Object& object) = 0;
+ virtual void Visit(Number& number) = 0;
+ virtual void Visit(String& string) = 0;
+ virtual void Visit(Boolean& boolean) = 0;
+ virtual void Visit(Null& null) = 0;
+};
+
+class ConstVisitor
+{
+public:
+ virtual ~ConstVisitor() {}
+
+ virtual void Visit(const Array& array) = 0;
+ virtual void Visit(const Object& object) = 0;
+ virtual void Visit(const Number& number) = 0;
+ virtual void Visit(const String& string) = 0;
+ virtual void Visit(const Boolean& boolean) = 0;
+ virtual void Visit(const Null& null) = 0;
+};
+
+
+} // End namespace
diff --git a/src/cajun/writer.cpp b/src/cajun/writer.cpp
new file mode 100644
index 0000000..a7cbfbe
--- /dev/null
+++ b/src/cajun/writer.cpp
@@ -0,0 +1,178 @@
+/******************************************************************************
+
+Copyright (c) 2009-2010, Terry Caton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the projecct nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <iostream>
+#include <iomanip>
+#include "writer.h"
+
+/*
+
+TODO:
+* better documentation
+* unicode character encoding
+
+*/
+
+namespace json
+{
+
+
+TPT_NO_INLINE void Writer::Write(const UnknownElement& elementRoot, std::ostream& ostr) { Write_i(elementRoot, ostr); }
+TPT_NO_INLINE void Writer::Write(const Object& object, std::ostream& ostr) { Write_i(object, ostr); }
+TPT_NO_INLINE void Writer::Write(const Array& array, std::ostream& ostr) { Write_i(array, ostr); }
+TPT_NO_INLINE void Writer::Write(const Number& number, std::ostream& ostr) { Write_i(number, ostr); }
+TPT_NO_INLINE void Writer::Write(const String& string, std::ostream& ostr) { Write_i(string, ostr); }
+TPT_NO_INLINE void Writer::Write(const Boolean& boolean, std::ostream& ostr) { Write_i(boolean, ostr); }
+TPT_NO_INLINE void Writer::Write(const Null& null, std::ostream& ostr) { Write_i(null, ostr); }
+
+
+TPT_NO_INLINE Writer::Writer(std::ostream& ostr) :
+ m_ostr(ostr),
+ m_nTabDepth(0)
+{}
+
+template <typename ElementTypeT>
+void Writer::Write_i(const ElementTypeT& element, std::ostream& ostr)
+{
+ Writer writer(ostr);
+ writer.Write_i(element);
+ ostr.flush(); // all done
+}
+
+TPT_NO_INLINE void Writer::Write_i(const Array& array)
+{
+ if (array.Empty())
+ m_ostr << "[]";
+ else
+ {
+ m_ostr << '[' << std::endl;
+ ++m_nTabDepth;
+
+ Array::const_iterator it(array.Begin()),
+ itEnd(array.End());
+ while (it != itEnd) {
+ m_ostr << std::string(m_nTabDepth, '\t');
+
+ Write_i(*it);
+
+ if (++it != itEnd)
+ m_ostr << ',';
+ m_ostr << std::endl;
+ }
+
+ --m_nTabDepth;
+ m_ostr << std::string(m_nTabDepth, '\t') << ']';
+ }
+}
+
+TPT_NO_INLINE void Writer::Write_i(const Object& object)
+{
+ if (object.Empty())
+ m_ostr << "{}";
+ else
+ {
+ m_ostr << '{' << std::endl;
+ ++m_nTabDepth;
+
+ Object::const_iterator it(object.Begin()),
+ itEnd(object.End());
+ while (it != itEnd) {
+ m_ostr << std::string(m_nTabDepth, '\t');
+
+ Write_i(it->name);
+
+ m_ostr << " : ";
+ Write_i(it->element);
+
+ if (++it != itEnd)
+ m_ostr << ',';
+ m_ostr << std::endl;
+ }
+
+ --m_nTabDepth;
+ m_ostr << std::string(m_nTabDepth, '\t') << '}';
+ }
+}
+
+TPT_NO_INLINE void Writer::Write_i(const Number& numberElement)
+{
+ m_ostr << std::setprecision(20) << numberElement.Value();
+}
+
+TPT_NO_INLINE void Writer::Write_i(const Boolean& booleanElement)
+{
+ m_ostr << (booleanElement.Value() ? "true" : "false");
+}
+
+TPT_NO_INLINE void Writer::Write_i(const String& stringElement)
+{
+ m_ostr << '"';
+
+ const std::string& s = stringElement.Value();
+ std::string::const_iterator it(s.begin()),
+ itEnd(s.end());
+ for (; it != itEnd; ++it)
+ {
+ switch (*it)
+ {
+ case '"': m_ostr << "\\\""; break;
+ case '\\': m_ostr << "\\\\"; break;
+ case '\b': m_ostr << "\\b"; break;
+ case '\f': m_ostr << "\\f"; break;
+ case '\n': m_ostr << "\\n"; break;
+ case '\r': m_ostr << "\\r"; break;
+ case '\t': m_ostr << "\\t"; break;
+ //case '\u': m_ostr << "\\u"; break; // uh...
+ default: m_ostr << *it; break;
+ }
+ }
+
+ m_ostr << '"';
+}
+
+TPT_NO_INLINE void Writer::Write_i(const Null& )
+{
+ m_ostr << "null";
+}
+
+TPT_NO_INLINE void Writer::Write_i(const UnknownElement& unknown)
+{
+ unknown.Accept(*this);
+}
+
+TPT_NO_INLINE void Writer::Visit(const Array& array) { Write_i(array); }
+TPT_NO_INLINE void Writer::Visit(const Object& object) { Write_i(object); }
+TPT_NO_INLINE void Writer::Visit(const Number& number) { Write_i(number); }
+TPT_NO_INLINE void Writer::Visit(const String& string) { Write_i(string); }
+TPT_NO_INLINE void Writer::Visit(const Boolean& boolean) { Write_i(boolean); }
+TPT_NO_INLINE void Writer::Visit(const Null& null) { Write_i(null); }
+
+
+
+} // End namespace
diff --git a/src/cajun/writer.h b/src/cajun/writer.h
new file mode 100644
index 0000000..f3d9596
--- /dev/null
+++ b/src/cajun/writer.h
@@ -0,0 +1,78 @@
+/******************************************************************************
+
+Copyright (c) 2009-2010, Terry Caton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the projecct nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#pragma once
+
+#include "elements.h"
+#include "visitor.h"
+
+namespace json
+{
+
+class Writer : private ConstVisitor
+{
+public:
+ static void Write(const Object& object, std::ostream& ostr);
+ static void Write(const Array& array, std::ostream& ostr);
+ static void Write(const String& string, std::ostream& ostr);
+ static void Write(const Number& number, std::ostream& ostr);
+ static void Write(const Boolean& boolean, std::ostream& ostr);
+ static void Write(const Null& null, std::ostream& ostr);
+ static void Write(const UnknownElement& elementRoot, std::ostream& ostr);
+
+private:
+ Writer(std::ostream& ostr);
+
+ template <typename ElementTypeT>
+ static void Write_i(const ElementTypeT& element, std::ostream& ostr);
+
+ void Write_i(const Object& object);
+ void Write_i(const Array& array);
+ void Write_i(const String& string);
+ void Write_i(const Number& number);
+ void Write_i(const Boolean& boolean);
+ void Write_i(const Null& null);
+ void Write_i(const UnknownElement& unknown);
+
+ virtual void Visit(const Array& array);
+ virtual void Visit(const Object& object);
+ virtual void Visit(const Number& number);
+ virtual void Visit(const String& string);
+ virtual void Visit(const Boolean& boolean);
+ virtual void Visit(const Null& null);
+
+ std::ostream& m_ostr;
+ int m_nTabDepth;
+};
+
+
+} // End namespace
+
+
+//#include "writer.inl" \ No newline at end of file
diff --git a/src/cat/CommandInterface.cpp b/src/cat/CommandInterface.cpp
new file mode 100644
index 0000000..3ebc795
--- /dev/null
+++ b/src/cat/CommandInterface.cpp
@@ -0,0 +1,118 @@
+/*
+ * Kitty.cpp
+ *
+ * Created on: Feb 2, 2012
+ * Author: Simon
+ */
+
+#include <iostream>
+#include <string>
+#include <string.h>
+#if !defined(WIN) || defined(__GNUC__)
+#include <strings.h>
+#endif
+#include "CommandInterface.h"
+#include "game/GameModel.h"
+#include "game/GameController.h"
+
+CommandInterface::CommandInterface(GameController * c, GameModel * m) {
+ this->m = m;
+ this->c = c;
+}
+
+/*void CommandInterface::AttachGameModel(GameModel * m)
+{
+ this->m = m;
+}*/
+
+int CommandInterface::Command(std::string command)
+{
+ lastError = "No interpreter";
+ return -1;
+}
+
+std::string CommandInterface::FormatCommand(std::string command)
+{
+ return command;
+}
+
+void CommandInterface::Log(LogType type, std::string message)
+{
+ m->Log(message);
+}
+
+int CommandInterface::GetPropertyOffset(std::string key_, FormatType & format)
+{
+ char * key = (char *)key_.c_str();
+ int offset;
+ if (strcmp(key, "type")==0){
+ offset = offsetof(Particle, type);
+ format = FormatInt;
+ } else if (strcmp(key, "life")==0){
+ offset = offsetof(Particle, life);
+ format = FormatInt;
+ } else if (strcmp(key, "ctype")==0){
+ offset = offsetof(Particle, ctype);
+ format = FormatInt;
+ } else if (strcmp(key, "temp")==0){
+ offset = offsetof(Particle, temp);
+ format = FormatFloat;
+ } else if (strcmp(key, "tmp2")==0){
+ offset = offsetof(Particle, tmp2);
+ format = FormatInt;
+ } else if (strcmp(key, "tmp")==0){
+ offset = offsetof(Particle, tmp);
+ format = FormatInt;
+ } else if (strcmp(key, "vy")==0){
+ offset = offsetof(Particle, vy);
+ format = FormatFloat;
+ } else if (strcmp(key, "vx")==0){
+ offset = offsetof(Particle, vx);
+ format = FormatFloat;
+ } else if (strcmp(key, "x")==0){
+ offset = offsetof(Particle, x);
+ format = FormatFloat;
+ } else if (strcmp(key, "y")==0){
+ offset = offsetof(Particle, y);
+ format = FormatFloat;
+ } else if (strcmp(key, "dcolour")==0){
+ offset = offsetof(Particle, dcolour);
+ format = FormatInt;
+ } else if (strcmp(key, "dcolor")==0){
+ offset = offsetof(Particle, dcolour);
+ format = FormatInt;
+ } else {
+ offset = -1;
+ }
+ return offset;
+}
+
+int CommandInterface::GetParticleType(std::string type)
+{
+ int i = -1;
+ char * txt = (char*)type.c_str();
+
+ //Scope
+ Element * elements = m->GetSimulation()->elements;
+
+ // alternative names for some elements
+ if (strcasecmp(txt,"C4")==0) return PT_PLEX;
+ else if (strcasecmp(txt,"C5")==0) return PT_C5;
+ else if (strcasecmp(txt,"NONE")==0) return PT_NONE;
+ for (i=1; i<PT_NUM; i++) {
+ if (strcasecmp(txt, elements[i].Name)==0 && elements[i].Enabled)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+std::string CommandInterface::GetLastError()
+{
+ return lastError;
+}
+
+CommandInterface::~CommandInterface() {
+}
+
diff --git a/src/cat/CommandInterface.h b/src/cat/CommandInterface.h
new file mode 100644
index 0000000..e6119d3
--- /dev/null
+++ b/src/cat/CommandInterface.h
@@ -0,0 +1,44 @@
+/*
+ * Kitty.h
+ *
+ * Created on: Feb 2, 2012
+ * Author: Simon
+ */
+
+#ifndef KITTY_H_
+#define KITTY_H_
+
+#include <string>
+#include "interface/Engine.h"
+//#include "game/GameModel.h"
+
+class GameModel;
+class GameController;
+class CommandInterface {
+protected:
+ std::string lastError;
+ GameModel * m;
+ GameController * c;
+public:
+ enum LogType { LogError, LogWarning, LogNotice };
+ enum FormatType { FormatInt, FormatString, FormatChar, FormatFloat };
+ CommandInterface(GameController * c, GameModel * m);
+ int GetPropertyOffset(std::string key_, FormatType & format);
+ int GetParticleType(std::string type);
+ void Log(LogType type, std::string message);
+ //void AttachGameModel(GameModel * m);
+ virtual bool OnBrushChanged(int brushType, int rx, int ry) {return true;}
+ virtual bool OnMouseMove(int x, int y, int dx, int dy) {return true;}
+ virtual bool OnMouseDown(int x, int y, unsigned button) {return true;}
+ virtual bool OnMouseUp(int x, int y, unsigned button) {return true;}
+ virtual bool OnMouseWheel(int x, int y, int d) {return true;}
+ virtual bool OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) {return true;}
+ virtual bool OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) {return true;}
+ virtual void OnTick() {}
+ virtual int Command(std::string command);
+ virtual std::string FormatCommand(std::string command);
+ std::string GetLastError();
+ virtual ~CommandInterface();
+};
+
+#endif /* KITTY_H_ */
diff --git a/src/cat/LegacyLuaAPI.cpp b/src/cat/LegacyLuaAPI.cpp
new file mode 100644
index 0000000..9a478e7
--- /dev/null
+++ b/src/cat/LegacyLuaAPI.cpp
@@ -0,0 +1,1828 @@
+#include <string>
+#include <iomanip>
+#include <vector>
+#include <algorithm>
+#include <locale>
+
+#include "client/HTTP.h"
+#include "Format.h"
+#include "LuaScriptInterface.h"
+#include "LuaScriptHelper.h"
+
+#include "dialogues/ErrorMessage.h"
+#include "dialogues/InformationMessage.h"
+#include "dialogues/TextPrompt.h"
+#include "dialogues/ConfirmPrompt.h"
+#include "simulation/Simulation.h"
+#include "game/GameModel.h"
+
+#include <time.h>
+
+#ifndef FFI
+int luacon_partread(lua_State* l){
+ int format, offset, tempinteger;
+ float tempfloat;
+ int i;
+ char * key = mystrdup((char*)luaL_optstring(l, 2, ""));
+ offset = luacon_particle_getproperty(key, &format);
+
+ i = cIndex;
+
+ if(i < 0 || i >= NPART || offset==-1)
+ {
+ if(i < 0 || i >= NPART) {
+ free(key);
+ return luaL_error(l, "Out of range");
+ } else if(strcmp(key, "id")==0) {
+ free(key);
+ lua_pushnumber(l, i);
+ return 1;
+ } else {
+ free(key);
+ return luaL_error(l, "Invalid property");
+ }
+ }
+ free(key);
+ switch(format)
+ {
+ case 0:
+ tempinteger = *((int*)(((unsigned char*)&luacon_sim->parts[i])+offset));
+ lua_pushnumber(l, tempinteger);
+ break;
+ case 1:
+ tempfloat = *((float*)(((unsigned char*)&luacon_sim->parts[i])+offset));
+ lua_pushnumber(l, tempfloat);
+ break;
+ }
+ return 1;
+}
+int luacon_partwrite(lua_State* l){
+ int format, offset;
+ int i;
+ char * key = mystrdup((char*)luaL_optstring(l, 2, ""));
+ offset = luacon_particle_getproperty(key, &format);
+
+ i = cIndex;
+
+ if(i < 0 || i >= NPART || offset==-1)
+ {
+ if(i < 0 || i >= NPART) {
+ free(key);
+ return luaL_error(l, "array index out of bounds");
+ } else {
+ free(key);
+ return luaL_error(l, "Invalid property");
+ }
+ }
+ free(key);
+ switch(format)
+ {
+ case 0:
+ *((int*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = luaL_optinteger(l, 3, 0);
+ break;
+ case 1:
+ *((float*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = luaL_optnumber(l, 3, 0);
+ break;
+ }
+ return 1;
+}
+int luacon_partsread(lua_State* l){
+ int format, offset;
+ char * tempstring;
+ int tempinteger;
+ float tempfloat;
+ int i, currentPart, currentPartMeta;
+
+ i = luaL_optinteger(l, 2, 0);
+
+ if(i<0 || i>=NPART)
+ {
+ return luaL_error(l, "array index out of bounds");
+ }
+
+ lua_rawgeti(l, LUA_REGISTRYINDEX, tptPart);
+ cIndex = i;
+ return 1;
+}
+int luacon_partswrite(lua_State* l){
+ return luaL_error(l, "table readonly");
+}
+#endif
+int luacon_particle_getproperty(char * key, int * format)
+{
+ int offset;
+ if (strcmp(key, "type")==0){
+ offset = offsetof(Particle, type);
+ *format = 0;
+ } else if (strcmp(key, "life")==0){
+ offset = offsetof(Particle, life);
+ *format = 0;
+ } else if (strcmp(key, "ctype")==0){
+ offset = offsetof(Particle, ctype);
+ *format = 0;
+ } else if (strcmp(key, "temp")==0){
+ offset = offsetof(Particle, temp);
+ *format = 1;
+ } else if (strcmp(key, "tmp")==0){
+ offset = offsetof(Particle, tmp);
+ *format = 0;
+ } else if (strcmp(key, "tmp2")==0){
+ offset = offsetof(Particle, tmp2);
+ *format = 0;
+ } else if (strcmp(key, "vy")==0){
+ offset = offsetof(Particle, vy);
+ *format = 1;
+ } else if (strcmp(key, "vx")==0){
+ offset = offsetof(Particle, vx);
+ *format = 1;
+ } else if (strcmp(key, "x")==0){
+ offset = offsetof(Particle, x);
+ *format = 1;
+ } else if (strcmp(key, "y")==0){
+ offset = offsetof(Particle, y);
+ *format = 1;
+ } else if (strcmp(key, "dcolour")==0){
+ offset = offsetof(Particle, dcolour);
+ *format = 0;
+ } else if (strcmp(key, "dcolor")==0){
+ offset = offsetof(Particle, dcolour);
+ *format = 0;
+ } else {
+ offset = -1;
+ }
+ return offset;
+}
+int luacon_transition_getproperty(char * key, int * format)
+{
+ int offset;
+ if (strcmp(key, "presHighValue")==0){
+ offset = offsetof(Element, HighPressure);
+ *format = 1;
+ } else if (strcmp(key, "presHighType")==0){
+ offset = offsetof(Element, HighPressureTransition);
+ *format = 0;
+ } else if (strcmp(key, "presLowValue")==0){
+ offset = offsetof(Element, LowPressure);
+ *format = 1;
+ } else if (strcmp(key, "presLowType")==0){
+ offset = offsetof(Element, LowPressureTransition);
+ *format = 0;
+ } else if (strcmp(key, "tempHighValue")==0){
+ offset = offsetof(Element, HighTemperature);
+ *format = 1;
+ } else if (strcmp(key, "tempHighType")==0){
+ offset = offsetof(Element, HighTemperatureTransition);
+ *format = 0;
+ } else if (strcmp(key, "tempLowValue")==0){
+ offset = offsetof(Element, LowTemperature);
+ *format = 1;
+ } else if (strcmp(key, "tempLowType")==0){
+ offset = offsetof(Element, LowTemperatureTransition);
+ *format = 0;
+ } else {
+ offset = -1;
+ }
+ return offset;
+}
+int luacon_transitionread(lua_State* l){
+ int format, offset;
+ int tempinteger;
+ float tempfloat;
+ int i;
+ char * key = mystrdup((char*)luaL_optstring(l, 2, ""));
+ offset = luacon_transition_getproperty(key, &format);
+ free(key);
+
+ //Get Raw Index value for element
+ lua_pushstring(l, "value");
+ lua_rawget(l, 1);
+
+ i = lua_tointeger(l, lua_gettop(l));
+
+ lua_pop(l, 1);
+
+ if(i < 0 || i >= PT_NUM || offset==-1)
+ {
+ return luaL_error(l, "Invalid property");
+ }
+ switch(format)
+ {
+ case 0:
+ tempinteger = *((int*)(((unsigned char*)&luacon_sim->elements[i])+offset));
+ lua_pushnumber(l, tempinteger);
+ break;
+ case 1:
+ tempfloat = *((float*)(((unsigned char*)&luacon_sim->elements[i])+offset));
+ lua_pushnumber(l, tempfloat);
+ break;
+ }
+ return 1;
+}
+int luacon_transitionwrite(lua_State* l){
+ int format, offset;
+ int tempinteger;
+ float tempfloat;
+ int i;
+ char * key = mystrdup((char*)luaL_optstring(l, 2, ""));
+ offset = luacon_transition_getproperty(key, &format);
+ free(key);
+
+ //Get Raw Index value for element
+ lua_pushstring(l, "value");
+ lua_rawget(l, 1);
+
+ i = lua_tointeger (l, lua_gettop(l));
+
+ lua_pop(l, 1);
+
+ if(i < 0 || i >= PT_NUM || offset==-1)
+ {
+ return luaL_error(l, "Invalid property");
+ }
+ switch(format)
+ {
+ case 0:
+ *((int*)(((unsigned char*)&luacon_sim->elements[i])+offset)) = luaL_optinteger(l, 3, 0);
+ break;
+ case 1:
+ *((float*)(((unsigned char*)&luacon_sim->elements[i])+offset)) = luaL_optnumber(l, 3, 0);
+ break;
+ }
+ return 0;
+}
+int luacon_element_getproperty(char * key, int * format, unsigned int * modified_stuff)
+{
+ int offset;
+ if (strcmp(key, "name")==0){
+ offset = offsetof(Element, Name);
+ *format = 2;
+ if(modified_stuff)
+ *modified_stuff |= LUACON_EL_MODIFIED_MENUS;
+ }
+ else if (strcmp(key, "color")==0){
+ offset = offsetof(Element, Colour);
+ *format = 0;
+ if (modified_stuff)
+ *modified_stuff |= LUACON_EL_MODIFIED_GRAPHICS;
+ }
+ else if (strcmp(key, "colour")==0){
+ offset = offsetof(Element, Colour);
+ *format = 0;
+ if (modified_stuff)
+ *modified_stuff |= LUACON_EL_MODIFIED_GRAPHICS;
+ }
+ else if (strcmp(key, "advection")==0){
+ offset = offsetof(Element, Advection);
+ *format = 1;
+ }
+ else if (strcmp(key, "airdrag")==0){
+ offset = offsetof(Element, AirDrag);
+ *format = 1;
+ }
+ else if (strcmp(key, "airloss")==0){
+ offset = offsetof(Element, AirLoss);
+ *format = 1;
+ }
+ else if (strcmp(key, "loss")==0){
+ offset = offsetof(Element, Loss);
+ *format = 1;
+ }
+ else if (strcmp(key, "collision")==0){
+ offset = offsetof(Element, Collision);
+ *format = 1;
+ }
+ else if (strcmp(key, "gravity")==0){
+ offset = offsetof(Element, Gravity);
+ *format = 1;
+ }
+ else if (strcmp(key, "diffusion")==0){
+ offset = offsetof(Element, Diffusion);
+ *format = 1;
+ }
+ else if (strcmp(key, "hotair")==0){
+ offset = offsetof(Element, HotAir);
+ *format = 1;
+ }
+ else if (strcmp(key, "falldown")==0){
+ offset = offsetof(Element, Falldown);
+ *format = 0;
+ }
+ else if (strcmp(key, "flammable")==0){
+ offset = offsetof(Element, Flammable);
+ *format = 0;
+ }
+ else if (strcmp(key, "explosive")==0){
+ offset = offsetof(Element, Explosive);
+ *format = 0;
+ }
+ else if (strcmp(key, "meltable")==0){
+ offset = offsetof(Element, Meltable);
+ *format = 0;
+ }
+ else if (strcmp(key, "hardness")==0){
+ offset = offsetof(Element, Hardness);
+ *format = 0;
+ }
+ else if (strcmp(key, "menu")==0){
+ offset = offsetof(Element, MenuVisible);
+ *format = 0;
+ if (modified_stuff)
+ *modified_stuff |= LUACON_EL_MODIFIED_MENUS;
+ }
+ else if (strcmp(key, "enabled")==0){
+ offset = offsetof(Element, Enabled);
+ *format = 0;
+ }
+ else if (strcmp(key, "weight")==0){
+ offset = offsetof(Element, Weight);
+ *format = 0;
+ if (modified_stuff)
+ *modified_stuff |= LUACON_EL_MODIFIED_CANMOVE;
+ }
+ else if (strcmp(key, "menusection")==0){
+ offset = offsetof(Element, MenuSection);
+ *format = 0;
+ if (modified_stuff)
+ *modified_stuff |= LUACON_EL_MODIFIED_MENUS;
+ }
+ else if (strcmp(key, "heat")==0){
+ offset = offsetof(Element, Temperature);
+ *format = 1;
+ }
+ else if (strcmp(key, "hconduct")==0){
+ offset = offsetof(Element, HeatConduct);
+ *format = 3;
+ }
+ else if (strcmp(key, "state")==0){
+ offset = offsetof(Element, State);
+ *format = 3;
+ }
+ else if (strcmp(key, "properties")==0){
+ offset = offsetof(Element, Properties);
+ *format = 0;
+ if (modified_stuff)
+ *modified_stuff |= LUACON_EL_MODIFIED_GRAPHICS | LUACON_EL_MODIFIED_CANMOVE;
+ }
+ else if (strcmp(key, "description")==0){
+ offset = offsetof(Element, Description);
+ *format = 2;
+ if(modified_stuff)
+ *modified_stuff |= LUACON_EL_MODIFIED_MENUS;
+ }
+ else {
+ return -1;
+ }
+ return offset;
+}
+int luacon_elementread(lua_State* l){
+ int format, offset;
+ char * tempstring;
+ int tempinteger;
+ float tempfloat;
+ int i;
+ char * key = mystrdup((char*)luaL_optstring(l, 2, ""));
+ offset = luacon_element_getproperty(key, &format, NULL);
+ free(key);
+
+ //Get Raw Index value for element
+ lua_pushstring(l, "id");
+ lua_rawget(l, 1);
+
+ i = lua_tointeger (l, lua_gettop(l));
+
+ lua_pop(l, 1);
+
+ if(i < 0 || i >= PT_NUM || offset==-1)
+ {
+ return luaL_error(l, "Invalid property");
+ }
+ switch(format)
+ {
+ case 0:
+ tempinteger = *((int*)(((unsigned char*)&luacon_sim->elements[i])+offset));
+ lua_pushnumber(l, tempinteger);
+ break;
+ case 1:
+ tempfloat = *((float*)(((unsigned char*)&luacon_sim->elements[i])+offset));
+ lua_pushnumber(l, tempfloat);
+ break;
+ case 2:
+ tempstring = *((char**)(((unsigned char*)&luacon_sim->elements[i])+offset));
+ lua_pushstring(l, tempstring);
+ break;
+ case 3:
+ tempinteger = *((unsigned char*)(((unsigned char*)&luacon_sim->elements[i])+offset));
+ lua_pushnumber(l, tempinteger);
+ break;
+ }
+ return 1;
+}
+int luacon_elementwrite(lua_State* l){
+ int format, offset;
+ char * tempstring;
+ int tempinteger;
+ float tempfloat;
+ int i;
+ unsigned int modified_stuff = 0;
+ char * key = mystrdup((char*)luaL_optstring(l, 2, ""));
+ offset = luacon_element_getproperty(key, &format, &modified_stuff);
+
+ //Get Raw Index value for element
+ lua_pushstring(l, "id");
+ lua_rawget(l, 1);
+
+ i = lua_tointeger (l, lua_gettop(l));
+
+ lua_pop(l, 1);
+
+ if(i < 0 || i >= PT_NUM || offset==-1)
+ {
+ free(key);
+ return luaL_error(l, "Invalid property");
+ }
+ switch(format)
+ {
+ case 0:
+ *((int*)(((unsigned char*)&luacon_sim->elements[i])+offset)) = luaL_optinteger(l, 3, 0);
+ break;
+ case 1:
+ *((float*)(((unsigned char*)&luacon_sim->elements[i])+offset)) = luaL_optnumber(l, 3, 0);
+ break;
+ case 2:
+ tempstring = mystrdup((char*)luaL_optstring(l, 3, ""));
+ if(strcmp(key, "name")==0)
+ {
+ int j = 0;
+ //Convert to upper case
+ for(j = 0; j < strlen(tempstring); j++)
+ tempstring[j] = toupper(tempstring[j]);
+ if(strlen(tempstring)>4)
+ {
+ free(tempstring);
+ free(key);
+ return luaL_error(l, "Name too long");
+ }
+ if(luacon_ci->GetParticleType(tempstring) != -1)
+ {
+ free(tempstring);
+ free(key);
+ return luaL_error(l, "Name in use");
+ }
+ }
+ *((char**)(((unsigned char*)&luacon_sim->elements[i])+offset)) = tempstring;
+ //Need some way of cleaning up previous values
+ break;
+ case 3:
+ *((unsigned char*)(((unsigned char*)&luacon_sim->elements[i])+offset)) = luaL_optinteger(l, 3, 0);
+ break;
+ }
+ if (modified_stuff)
+ {
+ if (modified_stuff & LUACON_EL_MODIFIED_MENUS)
+ luacon_model->BuildMenus();
+ if (modified_stuff & LUACON_EL_MODIFIED_CANMOVE)
+ luacon_sim->init_can_move();
+ if (modified_stuff & LUACON_EL_MODIFIED_GRAPHICS)
+ memset(luacon_ren->graphicscache, 0, sizeof(gcache_item)*PT_NUM);
+ }
+ free(key);
+ return 0;
+}
+int luacon_keyevent(int key, int modifier, int event){
+ int i = 0, kpcontinue = 1, callret;
+ char tempkey[] = {key, 0};
+ if(keypress_function_count){
+ for(i = 0; i < keypress_function_count && kpcontinue; i++){
+ lua_rawgeti(luacon_ci->l, LUA_REGISTRYINDEX, keypress_functions[i]);
+ lua_pushstring(luacon_ci->l, tempkey);
+ lua_pushinteger(luacon_ci->l, key);
+ lua_pushinteger(luacon_ci->l, modifier);
+ lua_pushinteger(luacon_ci->l, event);
+ callret = lua_pcall(luacon_ci->l, 4, 1, 0);
+ if (callret)
+ {
+ luacon_ci->Log(CommandInterface::LogError, luacon_geterror());
+ }
+ if(lua_isboolean(luacon_ci->l, -1)){
+ kpcontinue = lua_toboolean(luacon_ci->l, -1);
+ }
+ lua_pop(luacon_ci->l, 1);
+ }
+ }
+ return kpcontinue;
+}
+int luacon_mouseevent(int mx, int my, int mb, int event, int mouse_wheel){
+ int i = 0, mpcontinue = 1, callret;
+ if(mouseclick_function_count){
+ for(i = 0; i < mouseclick_function_count && mpcontinue; i++){
+ lua_rawgeti(luacon_ci->l, LUA_REGISTRYINDEX, mouseclick_functions[i]);
+ lua_pushinteger(luacon_ci->l, mx);
+ lua_pushinteger(luacon_ci->l, my);
+ lua_pushinteger(luacon_ci->l, mb);
+ lua_pushinteger(luacon_ci->l, event);
+ lua_pushinteger(luacon_ci->l, mouse_wheel);
+ callret = lua_pcall(luacon_ci->l, 5, 1, 0);
+ if (callret)
+ {
+ luacon_ci->Log(CommandInterface::LogError, luacon_geterror());
+ }
+ if(lua_isboolean(luacon_ci->l, -1)){
+ mpcontinue = lua_toboolean(luacon_ci->l, -1);
+ }
+ lua_pop(luacon_ci->l, 1);
+ }
+ }
+ return mpcontinue;
+}
+
+int luacon_step(int mx, int my, int selectl, int selectr, int bsx, int bsy){
+ int tempret = 0, tempb, i, callret;
+ lua_pushinteger(luacon_ci->l, bsy);
+ lua_pushinteger(luacon_ci->l, bsx);
+ lua_pushinteger(luacon_ci->l, selectr);
+ lua_pushinteger(luacon_ci->l, selectl);
+ lua_pushinteger(luacon_ci->l, my);
+ lua_pushinteger(luacon_ci->l, mx);
+ lua_setfield(luacon_ci->l, tptProperties, "mousex");
+ lua_setfield(luacon_ci->l, tptProperties, "mousey");
+ lua_setfield(luacon_ci->l, tptProperties, "selectedl");
+ lua_setfield(luacon_ci->l, tptProperties, "selectedr");
+ lua_setfield(luacon_ci->l, tptProperties, "brushx");
+ lua_setfield(luacon_ci->l, tptProperties, "brushy");
+ for(i = 0; i<6; i++){
+ if(step_functions[i]){
+ lua_rawgeti(luacon_ci->l, LUA_REGISTRYINDEX, step_functions[i]);
+ callret = lua_pcall(luacon_ci->l, 0, 0, 0);
+ if (callret)
+ {
+ if (!strcmp(luacon_geterror(),"Error: Script not responding"))
+ {
+ ui::Engine::Ref().LastTick(clock());
+ lua_pushcfunction(luacon_ci->l, &luatpt_unregister_step);
+ lua_rawgeti(luacon_ci->l, LUA_REGISTRYINDEX, step_functions[i]);
+ lua_pcall(luacon_ci->l, 1, 0, 0);
+ }
+ luacon_ci->Log(CommandInterface::LogError, luacon_geterror());
+ }
+ }
+ }
+ return 0;
+}
+
+
+int luacon_eval(char *command){
+ ui::Engine::Ref().LastTick(clock());
+ return luaL_dostring (luacon_ci->l, command);
+}
+
+void luacon_hook(lua_State * l, lua_Debug * ar)
+{
+ if(ar->event == LUA_HOOKCOUNT && clock()-ui::Engine::Ref().LastTick() > CLOCKS_PER_SEC*3)
+ {
+ if(ConfirmPrompt::Blocking("Script not responding", "The Lua script may have stopped responding. There might be an infinite loop. Press \"Stop\" to stop it", "Stop"))
+ luaL_error(l, "Error: Script not responding");
+ ui::Engine::Ref().LastTick(clock());
+ }
+}
+
+char *luacon_geterror(){
+ char *error = (char*)lua_tostring(luacon_ci->l, -1);
+ if(error==NULL || !error[0]){
+ error = "failed to execute";
+ }
+ return error;
+}
+/*void luacon_close(){
+ lua_close(l);
+}*/
+
+//TPT Interface methods
+int luatpt_test(lua_State* l)
+{
+ int testint = 0;
+ testint = luaL_optint(l, 1, 0);
+ printf("Test successful, got %d\n", testint);
+ return 0;
+}
+int luatpt_getelement(lua_State *l)
+{
+ int t;
+ if(lua_isnumber(l, 1))
+ {
+ t = luaL_optint(l, 1, 1);
+ if (t<0 || t>=PT_NUM)
+ return luaL_error(l, "Unrecognised element number '%d'", t);
+ lua_pushstring(l, luacon_sim->elements[t].Name);
+ }
+ else
+ {
+ char* name = (char*)luaL_optstring(l, 1, "dust");
+ if ((t = luacon_ci->GetParticleType(name))==-1)
+ return luaL_error(l, "Unrecognised element '%s'", name);
+ lua_pushinteger(l, t);
+ }
+ return 1;
+}
+
+int luacon_elementReplacement(UPDATE_FUNC_ARGS)
+{
+ int retval = 0;
+ if(lua_el_func[parts[i].type]){
+ lua_rawgeti(luacon_ci->l, LUA_REGISTRYINDEX, lua_el_func[parts[i].type]);
+ lua_pushinteger(luacon_ci->l, i);
+ lua_pushinteger(luacon_ci->l, x);
+ lua_pushinteger(luacon_ci->l, y);
+ lua_pushinteger(luacon_ci->l, surround_space);
+ lua_pushinteger(luacon_ci->l, nt);
+ lua_pcall(luacon_ci->l, 5, 1, 0);
+ if(lua_isboolean(luacon_ci->l, -1)){
+ retval = lua_toboolean(luacon_ci->l, -1);
+ }
+ lua_pop(luacon_ci->l, 1);
+ }
+ return retval;
+}
+
+int luatpt_element_func(lua_State *l)
+{
+ if(lua_isfunction(l, 1))
+ {
+ int element = luaL_optint(l, 2, 0);
+ int function;
+ lua_pushvalue(l, 1);
+ function = luaL_ref(l, LUA_REGISTRYINDEX);
+ if(element > 0 && element < PT_NUM)
+ {
+ lua_el_func[element] = function;
+ luacon_sim->elements[element].Update = &luacon_elementReplacement;
+ return 0;
+ }
+ else
+ {
+ return luaL_error(l, "Invalid element");
+ }
+ }
+ else
+ return luaL_error(l, "Not a function");
+ return 0;
+}
+
+int luacon_graphicsReplacement(GRAPHICS_FUNC_ARGS)
+{
+ int cache = 0;
+ lua_rawgeti(luacon_ci->l, LUA_REGISTRYINDEX, lua_gr_func[cpart->type]);
+ lua_pushinteger(luacon_ci->l, 0);
+ lua_pushinteger(luacon_ci->l, *colr);
+ lua_pushinteger(luacon_ci->l, *colg);
+ lua_pushinteger(luacon_ci->l, *colb);
+ lua_pcall(luacon_ci->l, 4, 10, 0);
+
+ cache = luaL_optint(luacon_ci->l, -10, 0);
+ *pixel_mode = luaL_optint(luacon_ci->l, -9, *pixel_mode);
+ *cola = luaL_optint(luacon_ci->l, -8, *cola);
+ *colr = luaL_optint(luacon_ci->l, -7, *colr);
+ *colg = luaL_optint(luacon_ci->l, -6, *colg);
+ *colb = luaL_optint(luacon_ci->l, -5, *colb);
+ *firea = luaL_optint(luacon_ci->l, -4, *firea);
+ *firer = luaL_optint(luacon_ci->l, -3, *firer);
+ *fireg = luaL_optint(luacon_ci->l, -2, *fireg);
+ *fireb = luaL_optint(luacon_ci->l, -1, *fireb);
+ lua_pop(luacon_ci->l, 10);
+ return cache;
+}
+
+int luatpt_graphics_func(lua_State *l)
+{
+ if(lua_isfunction(l, 1))
+ {
+ int element = luaL_optint(l, 2, 0);
+ int function;
+ lua_pushvalue(l, 1);
+ function = luaL_ref(l, LUA_REGISTRYINDEX);
+ if(element > 0 && element < PT_NUM)
+ {
+ lua_gr_func[element] = function;
+ luacon_ren->graphicscache[element].isready = 0;
+ luacon_sim->elements[element].Graphics = &luacon_graphicsReplacement;
+ return 0;
+ }
+ else
+ {
+ return luaL_error(l, "Invalid element");
+ }
+ }
+ else
+ return luaL_error(l, "Not a function");
+ return 0;
+}
+
+int luatpt_error(lua_State* l)
+{
+ std::string errorMessage = std::string(luaL_optstring(l, 1, "Error text"));
+ ErrorMessage::Blocking("Error", errorMessage);
+ return 0;
+}
+int luatpt_drawtext(lua_State* l)
+{
+ char *string;
+ int textx, texty, textred, textgreen, textblue, textalpha;
+ textx = luaL_optint(l, 1, 0);
+ texty = luaL_optint(l, 2, 0);
+ string = (char*)luaL_optstring(l, 3, "");
+ textred = luaL_optint(l, 4, 255);
+ textgreen = luaL_optint(l, 5, 255);
+ textblue = luaL_optint(l, 6, 255);
+ textalpha = luaL_optint(l, 7, 255);
+ if (textx<0 || texty<0 || textx>=XRES+BARSIZE || texty>=YRES+MENUSIZE)
+ return luaL_error(l, "Screen coordinates out of range (%d,%d)", textx, texty);
+ if (textred<0) textred = 0;
+ if (textred>255) textred = 255;
+ if (textgreen<0) textgreen = 0;
+ if (textgreen>255) textgreen = 255;
+ if (textblue<0) textblue = 0;
+ if (textblue>255) textblue = 255;
+ if (textalpha<0) textalpha = 0;
+ if (textalpha>255) textalpha = 255;
+ if(luacon_g!=NULL){
+ luacon_g->drawtext(textx, texty, string, textred, textgreen, textblue, textalpha);
+ return 0;
+ }
+ return luaL_error(l, "Screen buffer does not exist");
+}
+
+int luatpt_create(lua_State* l)
+{
+ int x, y, retid, t = -1;
+ char * name;
+ x = abs(luaL_optint(l, 1, 0));
+ y = abs(luaL_optint(l, 2, 0));
+ if(x < XRES && y < YRES){
+ if(lua_isnumber(l, 3)){
+ t = luaL_optint(l, 3, 0);
+ if (t<0 || t >= PT_NUM || !luacon_sim->elements[t].Enabled)
+ return luaL_error(l, "Unrecognised element number '%d'", t);
+ } else {
+ name = (char*)luaL_optstring(l, 3, "dust");
+ if ((t = luacon_ci->GetParticleType(std::string(name))) == -1)
+ return luaL_error(l,"Unrecognised element '%s'", name);
+ }
+ retid = luacon_sim->create_part(-1, x, y, t);
+ // failing to create a particle often happens (e.g. if space is already occupied) and isn't usually important, so don't raise an error
+ lua_pushinteger(l, retid);
+ return 1;
+ }
+ return luaL_error(l, "Coordinates out of range (%d,%d)", x, y);
+}
+
+int luatpt_setpause(lua_State* l)
+{
+ int pausestate;
+ pausestate = luaL_optint(l, 1, 0);
+ luacon_model->SetPaused(pausestate==0?0:1);
+ return 0;
+}
+
+int luatpt_togglepause(lua_State* l)
+{
+ luacon_model->SetPaused(!luacon_model->GetPaused());
+ //sys_pause=!sys_pause;
+ return 0;
+}
+
+int luatpt_togglewater(lua_State* l)
+{
+ luacon_sim->water_equal_test=!luacon_sim->water_equal_test;
+ return 0;
+}
+
+int luatpt_setconsole(lua_State* l)
+{
+ /*int consolestate;
+ consolestate = luaL_optint(l, 1, 0);
+ console_mode = (consolestate==0?0:1);
+ return 0;*/
+ //TODO IMPLEMENT
+ return 0;
+}
+
+int luatpt_log(lua_State* l)
+{
+ int args = lua_gettop(l);
+ for(int i = 1; i <= args; i++)
+ {
+ if((*luacon_currentCommand))
+ {
+ if(!(*luacon_lastError).length())
+ (*luacon_lastError) = luaL_optstring(l, i, "");
+ else
+ (*luacon_lastError) += ", " + std::string(luaL_optstring(l, i, ""));
+ }
+ else
+ luacon_ci->Log(CommandInterface::LogNotice, luaL_optstring(l, i, ""));
+ }
+ return 0;
+}
+
+int luatpt_set_pressure(lua_State* l)
+{
+ int nx, ny;
+ int x1, y1, width, height;
+ float value;
+ x1 = abs(luaL_optint(l, 1, 0));
+ y1 = abs(luaL_optint(l, 2, 0));
+ width = abs(luaL_optint(l, 3, XRES/CELL));
+ height = abs(luaL_optint(l, 4, YRES/CELL));
+ value = (float)luaL_optint(l, 5, 0.0f);
+ if(value > 256.0f)
+ value = 256.0f;
+ else if(value < -256.0f)
+ value = -256.0f;
+
+ if(x1 > (XRES/CELL)-1)
+ x1 = (XRES/CELL)-1;
+ if(y1 > (YRES/CELL)-1)
+ y1 = (YRES/CELL)-1;
+ if(x1+width > (XRES/CELL)-1)
+ width = (XRES/CELL)-x1;
+ if(y1+height > (YRES/CELL)-1)
+ height = (YRES/CELL)-y1;
+ for (nx = x1; nx<x1+width; nx++)
+ for (ny = y1; ny<y1+height; ny++)
+ {
+ luacon_sim->pv[ny][nx] = value;
+ }
+ return 0;
+}
+
+int luatpt_set_gravity(lua_State* l)
+{
+ int nx, ny;
+ int x1, y1, width, height;
+ float value;
+ x1 = abs(luaL_optint(l, 1, 0));
+ y1 = abs(luaL_optint(l, 2, 0));
+ width = abs(luaL_optint(l, 3, XRES/CELL));
+ height = abs(luaL_optint(l, 4, YRES/CELL));
+ value = (float)luaL_optint(l, 5, 0.0f);
+ if(value > 256.0f)
+ value = 256.0f;
+ else if(value < -256.0f)
+ value = -256.0f;
+
+ if(x1 > (XRES/CELL)-1)
+ x1 = (XRES/CELL)-1;
+ if(y1 > (YRES/CELL)-1)
+ y1 = (YRES/CELL)-1;
+ if(x1+width > (XRES/CELL)-1)
+ width = (XRES/CELL)-x1;
+ if(y1+height > (YRES/CELL)-1)
+ height = (YRES/CELL)-y1;
+ for (nx = x1; nx<x1+width; nx++)
+ for (ny = y1; ny<y1+height; ny++)
+ {
+ luacon_sim->gravmap[ny*(XRES/CELL)+nx] = value;
+ }
+ return 0;
+}
+
+int luatpt_reset_gravity_field(lua_State* l)
+{
+ int nx, ny;
+ int x1, y1, width, height;
+ x1 = abs(luaL_optint(l, 1, 0));
+ y1 = abs(luaL_optint(l, 2, 0));
+ width = abs(luaL_optint(l, 3, XRES/CELL));
+ height = abs(luaL_optint(l, 4, YRES/CELL));
+ if(x1 > (XRES/CELL)-1)
+ x1 = (XRES/CELL)-1;
+ if(y1 > (YRES/CELL)-1)
+ y1 = (YRES/CELL)-1;
+ if(x1+width > (XRES/CELL)-1)
+ width = (XRES/CELL)-x1;
+ if(y1+height > (YRES/CELL)-1)
+ height = (YRES/CELL)-y1;
+ for (nx = x1; nx<x1+width; nx++)
+ for (ny = y1; ny<y1+height; ny++)
+ {
+ luacon_sim->gravx[ny*(XRES/CELL)+nx] = 0;
+ luacon_sim->gravy[ny*(XRES/CELL)+nx] = 0;
+ luacon_sim->gravp[ny*(XRES/CELL)+nx] = 0;
+ }
+ return 0;
+}
+
+int luatpt_reset_velocity(lua_State* l)
+{
+ int nx, ny;
+ int x1, y1, width, height;
+ x1 = abs(luaL_optint(l, 1, 0));
+ y1 = abs(luaL_optint(l, 2, 0));
+ width = abs(luaL_optint(l, 3, XRES/CELL));
+ height = abs(luaL_optint(l, 4, YRES/CELL));
+ if(x1 > (XRES/CELL)-1)
+ x1 = (XRES/CELL)-1;
+ if(y1 > (YRES/CELL)-1)
+ y1 = (YRES/CELL)-1;
+ if(x1+width > (XRES/CELL)-1)
+ width = (XRES/CELL)-x1;
+ if(y1+height > (YRES/CELL)-1)
+ height = (YRES/CELL)-y1;
+ for (nx = x1; nx<x1+width; nx++)
+ for (ny = y1; ny<y1+height; ny++)
+ {
+ luacon_sim->vx[ny][nx] = 0;
+ luacon_sim->vy[ny][nx] = 0;
+ }
+ return 0;
+}
+
+int luatpt_reset_spark(lua_State* l)
+{
+ int i;
+ for (i=0; i<NPART; i++)
+ {
+ if (luacon_sim->parts[i].type==PT_SPRK)
+ {
+ if (luacon_sim->parts[i].ctype >= 0 && luacon_sim->parts[i].ctype < PT_NUM)
+ {
+ luacon_sim->parts[i].type = luacon_sim->parts[i].ctype;
+ luacon_sim->parts[i].life = 0;
+ }
+ else
+ luacon_sim->kill_part(i);
+ }
+ }
+ return 0;
+}
+
+int luatpt_set_property(lua_State* l)
+{
+ char *prop, *name;
+ int r, i, x, y, w, h, t, nx, ny, partsel = 0, acount;
+ float f;
+ size_t offset;
+ acount = lua_gettop(l);
+ prop = (char*)luaL_optstring(l, 1, "");
+ if(lua_isnumber(l, 3))
+ i = abs(luaL_optint(l, 3, -1));
+ else
+ i = -1;
+ if(lua_isnumber(l, 4))
+ y = abs(luaL_optint(l, 4, -1));
+ else
+ y = -1;
+ if(lua_isnumber(l, 5))
+ w = abs(luaL_optint(l, 5, -1));
+ else
+ w = -1;
+ if(lua_isnumber(l, 6))
+ h = abs(luaL_optint(l, 6, -1));
+ else
+ h = -1;
+
+ CommandInterface::FormatType format;
+ offset = luacon_ci->GetPropertyOffset(prop, format);
+ if(offset == -1)
+ return luaL_error(l, "Invalid property '%s'", prop);
+ if(acount>2){
+ if(!lua_isnumber(l, acount) && lua_isstring(l, acount)){
+ name = (char*)luaL_optstring(l, acount, "none");
+ if((partsel = luacon_ci->GetParticleType(std::string(name)))==-1)
+ return luaL_error(l, "Unrecognised element '%s'", name);
+ }
+ }
+ if(lua_isnumber(l, 2)){
+ if(format == CommandInterface::FormatFloat){
+ f = luaL_optnumber(l, 2, 0);
+ } else {
+ t = luaL_optint(l, 2, 0);
+ }
+ //TODO Element ID check
+ //if (format == 3 && (t<0 || t>=PT_NUM))
+ // return luaL_error(l, "Unrecognised element number '%d'", t);
+ } else {
+ name = (char*)luaL_optstring(l, 2, "dust");
+ //if (!console_parse_type(name, &t, NULL))
+ if((t = luacon_ci->GetParticleType(std::string(name)))==-1)
+ return luaL_error(l, "Unrecognised element '%s'", name);
+ }
+ if(i == -1 || (w != -1 && h != -1)){
+ // Got a region
+ if(i == -1){
+ i = 0;
+ y = 0;
+ w = XRES;
+ h = YRES;
+ }
+ if (i>=XRES || y>=YRES)
+ return luaL_error(l, "Coordinates out of range (%d,%d)", i, y);
+ x = i;
+ if(x+w > XRES)
+ w = XRES-x;
+ if(y+h > YRES)
+ h = YRES-y;
+ Particle * parts = luacon_sim->parts;
+ for (i = 0; i < NPART; i++)
+ {
+ if (parts[i].type)
+ {
+ nx = (int)(parts[i].x + .5f);
+ ny = (int)(parts[i].y + .5f);
+ if (nx >= x && nx < x+w && ny >= y && ny < y+h && (!partsel || partsel == parts[i].type))
+ {
+ if(format == CommandInterface::FormatFloat){
+ *((float*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = f;
+ } else {
+ *((int*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = t;
+ }
+ }
+ }
+ }
+
+ } else {
+ // Got coords or particle index
+ if(i != -1 && y != -1){
+ if (i>=XRES || y>=YRES)
+ return luaL_error(l, "Coordinates out of range (%d,%d)", i, y);
+ r = luacon_sim->pmap[y][i];
+ if (!r || (partsel && partsel != luacon_sim->parts[r>>8].type))
+ r = luacon_sim->photons[y][i];
+ if (!r || (partsel && partsel != luacon_sim->parts[r>>8].type))
+ return 0;
+ i = r>>8;
+ }
+ if (i < 0 || i >= NPART)
+ return luaL_error(l, "Invalid particle ID '%d'", i);
+ if (!luacon_sim->parts[i].type)
+ return 0;
+ if (partsel && partsel != luacon_sim->parts[i].type)
+ return 0;
+ if(format == CommandInterface::FormatFloat){
+ *((float*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = f;
+ } else {
+ *((int*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = t;
+ }
+ }
+ return 0;
+}
+
+int luatpt_set_wallmap(lua_State* l)
+{
+ int nx, ny, acount;
+ int x1, y1, width, height;
+ float value;
+ acount = lua_gettop(l);
+
+ x1 = abs(luaL_optint(l, 1, 0));
+ y1 = abs(luaL_optint(l, 2, 0));
+ width = abs(luaL_optint(l, 3, XRES/CELL));
+ height = abs(luaL_optint(l, 4, YRES/CELL));
+ value = (float)luaL_optint(l, acount, 0);
+
+ if(acount==5) //Draw rect
+ {
+ if(x1 > (XRES/CELL))
+ x1 = (XRES/CELL);
+ if(y1 > (YRES/CELL))
+ y1 = (YRES/CELL);
+ if(x1+width > (XRES/CELL))
+ width = (XRES/CELL)-x1;
+ if(y1+height > (YRES/CELL))
+ height = (YRES/CELL)-y1;
+ for (nx = x1; nx<x1+width; nx++)
+ for (ny = y1; ny<y1+height; ny++)
+ {
+ luacon_sim->bmap[ny][nx] = value;
+ }
+ }
+ else //Set point
+ {
+ if(x1 > (XRES/CELL))
+ x1 = (XRES/CELL);
+ if(y1 > (YRES/CELL))
+ y1 = (YRES/CELL);
+ luacon_sim->bmap[y1][x1] = value;
+ }
+ return 0;
+}
+
+int luatpt_get_wallmap(lua_State* l)
+{
+ int nx, ny, acount;
+ int x1, y1, width, height;
+ float value;
+ acount = lua_gettop(l);
+
+ x1 = abs(luaL_optint(l, 1, 0));
+ y1 = abs(luaL_optint(l, 2, 0));
+
+ if(x1 > (XRES/CELL) || y1 > (YRES/CELL))
+ return luaL_error(l, "Out of range");
+
+ lua_pushinteger(l, luacon_sim->bmap[y1][x1]);
+ return 1;
+}
+
+int luatpt_set_elecmap(lua_State* l)
+{
+ int nx, ny, acount;
+ int x1, y1, width, height;
+ float value;
+ acount = lua_gettop(l);
+
+ x1 = abs(luaL_optint(l, 1, 0));
+ y1 = abs(luaL_optint(l, 2, 0));
+ width = abs(luaL_optint(l, 3, XRES/CELL));
+ height = abs(luaL_optint(l, 4, YRES/CELL));
+ value = (float)luaL_optint(l, acount, 0);
+
+ if(acount==5) //Draw rect
+ {
+ if(x1 > (XRES/CELL))
+ x1 = (XRES/CELL);
+ if(y1 > (YRES/CELL))
+ y1 = (YRES/CELL);
+ if(x1+width > (XRES/CELL))
+ width = (XRES/CELL)-x1;
+ if(y1+height > (YRES/CELL))
+ height = (YRES/CELL)-y1;
+ for (nx = x1; nx<x1+width; nx++)
+ for (ny = y1; ny<y1+height; ny++)
+ {
+ luacon_sim->emap[ny][nx] = value;
+ }
+ }
+ else //Set point
+ {
+ if(x1 > (XRES/CELL))
+ x1 = (XRES/CELL);
+ if(y1 > (YRES/CELL))
+ y1 = (YRES/CELL);
+ luacon_sim->emap[y1][x1] = value;
+ }
+ return 0;
+}
+
+int luatpt_get_elecmap(lua_State* l)
+{
+ int nx, ny, acount;
+ int x1, y1, width, height;
+ float value;
+ acount = lua_gettop(l);
+
+ x1 = abs(luaL_optint(l, 1, 0));
+ y1 = abs(luaL_optint(l, 2, 0));
+
+ if(x1 > (XRES/CELL) || y1 > (YRES/CELL))
+ return luaL_error(l, "Out of range");
+
+ lua_pushinteger(l, luacon_sim->emap[y1][x1]);
+ return 1;
+}
+
+int luatpt_get_property(lua_State* l)
+{
+ int i, r, y;
+ char *prop;
+ prop = (char*)luaL_optstring(l, 1, "");
+ i = luaL_optint(l, 2, 0);
+ y = luaL_optint(l, 3, -1);
+ if(y!=-1 && y < YRES && y >= 0 && i < XRES && i >= 0){
+ r = luacon_sim->pmap[y][i];
+ if (!r)
+ r = luacon_sim->photons[y][i];
+ if (!r)
+ {
+ if (strcmp(prop,"type")==0){
+ lua_pushinteger(l, 0);
+ return 1;
+ }
+ return luaL_error(l, "Particle does not exist");
+ }
+ i = r>>8;
+ }
+ else if (y!=-1)
+ return luaL_error(l, "Coordinates out of range (%d,%d)", i, y);
+ if (i < 0 || i >= NPART)
+ return luaL_error(l, "Invalid particle ID '%d'", i);
+ if (luacon_sim->parts[i].type)
+ {
+ //TODO: Use particle_getproperty
+ if (strcmp(prop,"type")==0){
+ lua_pushinteger(l, luacon_sim->parts[i].type);
+ return 1;
+ }
+ if (strcmp(prop,"life")==0){
+ lua_pushinteger(l, luacon_sim->parts[i].life);
+ return 1;
+ }
+ if (strcmp(prop,"ctype")==0){
+ lua_pushinteger(l, luacon_sim->parts[i].ctype);
+ return 1;
+ }
+ if (strcmp(prop,"temp")==0){
+ lua_pushnumber(l, luacon_sim->parts[i].temp);
+ return 1;
+ }
+ if (strcmp(prop,"tmp")==0){
+ lua_pushinteger(l, luacon_sim->parts[i].tmp);
+ return 1;
+ }
+ if (strcmp(prop,"tmp2")==0){
+ lua_pushinteger(l, luacon_sim->parts[i].tmp2);
+ return 1;
+ }
+ if (strcmp(prop,"vy")==0){
+ lua_pushnumber(l, (double)luacon_sim->parts[i].vy);
+ return 1;
+ }
+ if (strcmp(prop,"vx")==0){
+ lua_pushnumber(l, (double)luacon_sim->parts[i].vx);
+ return 1;
+ }
+ if (strcmp(prop,"x")==0){
+ lua_pushnumber(l, luacon_sim->parts[i].x);
+ return 1;
+ }
+ if (strcmp(prop,"y")==0){
+ lua_pushnumber(l, luacon_sim->parts[i].y);
+ return 1;
+ }
+ if (strcmp(prop,"dcolour")==0){
+ lua_pushinteger(l, luacon_sim->parts[i].dcolour);
+ return 1;
+ }
+ if (strcmp(prop,"dcolor")==0){
+ lua_pushinteger(l, luacon_sim->parts[i].dcolour);
+ return 1;
+ }
+ if (strcmp(prop,"id")==0){
+ lua_pushnumber(l, i);
+ return 1;
+ }
+ }
+ else if (strcmp(prop,"type")==0){
+ lua_pushinteger(l, 0);
+ return 1;
+ }
+ return luaL_error(l, "Particle does not exist");
+}
+
+int luatpt_drawpixel(lua_State* l)
+{
+ int x, y, r, g, b, a;
+ x = luaL_optint(l, 1, 0);
+ y = luaL_optint(l, 2, 0);
+ r = luaL_optint(l, 3, 255);
+ g = luaL_optint(l, 4, 255);
+ b = luaL_optint(l, 5, 255);
+ a = luaL_optint(l, 6, 255);
+
+ if (x<0 || y<0 || x>=XRES+BARSIZE || y>=YRES+MENUSIZE)
+ return luaL_error(l, "Screen coordinates out of range (%d,%d)", x, y);
+ if (r<0) r = 0;
+ if (r>255) r = 255;
+ if (g<0) g = 0;
+ if (g>255) g = 255;
+ if (b<0) b = 0;
+ if (b>255) b = 255;
+ if (a<0) a = 0;
+ if (a>255) a = 255;
+ luacon_g->blendpixel(x, y, r, g, b, a);
+ return 0;
+}
+
+int luatpt_drawrect(lua_State* l)
+{
+ int x, y, w, h, r, g, b, a;
+ x = luaL_optint(l, 1, 0);
+ y = luaL_optint(l, 2, 0);
+ w = luaL_optint(l, 3, 10)+1;
+ h = luaL_optint(l, 4, 10)+1;
+ r = luaL_optint(l, 5, 255);
+ g = luaL_optint(l, 6, 255);
+ b = luaL_optint(l, 7, 255);
+ a = luaL_optint(l, 8, 255);
+
+ if (x<0 || y<0 || x>=XRES+BARSIZE || y>=YRES+MENUSIZE)
+ return luaL_error(l, "Screen coordinates out of range (%d,%d)", x, y);
+ if(x+w > XRES+BARSIZE)
+ w = XRES+BARSIZE-x;
+ if(y+h > YRES+MENUSIZE)
+ h = YRES+MENUSIZE-y;
+ if (r<0) r = 0;
+ if (r>255) r = 255;
+ if (g<0) g = 0;
+ if (g>255) g = 255;
+ if (b<0) b = 0;
+ if (b>255) b = 255;
+ if (a<0) a = 0;
+ if (a>255) a = 255;
+ luacon_g->drawrect(x, y, w, h, r, g, b, a);
+ return 0;
+}
+
+int luatpt_fillrect(lua_State* l)
+{
+ int x,y,w,h,r,g,b,a;
+ x = luaL_optint(l, 1, 0)+1;
+ y = luaL_optint(l, 2, 0)+1;
+ w = luaL_optint(l, 3, 10)+1;
+ h = luaL_optint(l, 4, 10)+1;
+ r = luaL_optint(l, 5, 255);
+ g = luaL_optint(l, 6, 255);
+ b = luaL_optint(l, 7, 255);
+ a = luaL_optint(l, 8, 255);
+
+ if (x<0 || y<0 || x>=XRES+BARSIZE || y>=YRES+MENUSIZE)
+ return luaL_error(l, "Screen coordinates out of range (%d,%d)", x, y);
+ if(x+w > XRES+BARSIZE)
+ w = XRES+BARSIZE-x;
+ if(y+h > YRES+MENUSIZE)
+ h = YRES+MENUSIZE-y;
+ if (r<0) r = 0;
+ if (r>255) r = 255;
+ if (g<0) g = 0;
+ if (g>255) g = 255;
+ if (b<0) b = 0;
+ if (b>255) b = 255;
+ if (a<0) a = 0;
+ if (a>255) a = 255;
+ luacon_g->fillrect(x, y, w, h, r, g, b, a);
+ return 0;
+}
+
+int luatpt_drawline(lua_State* l)
+{
+ int x1,y1,x2,y2,r,g,b,a;
+ x1 = luaL_optint(l, 1, 0);
+ y1 = luaL_optint(l, 2, 0);
+ x2 = luaL_optint(l, 3, 10);
+ y2 = luaL_optint(l, 4, 10);
+ r = luaL_optint(l, 5, 255);
+ g = luaL_optint(l, 6, 255);
+ b = luaL_optint(l, 7, 255);
+ a = luaL_optint(l, 8, 255);
+
+ //Don't need to check coordinates, as they are checked in blendpixel
+ if (r<0) r = 0;
+ if (r>255) r = 255;
+ if (g<0) g = 0;
+ if (g>255) g = 255;
+ if (b<0) b = 0;
+ if (b>255) b = 255;
+ if (a<0) a = 0;
+ if (a>255) a = 255;
+ luacon_g->draw_line(x1, y1, x2, y2, r, g, b, a);
+ return 0;
+}
+
+int luatpt_textwidth(lua_State* l)
+{
+ char * string;
+ int strwidth = 0;
+ string = (char*)luaL_optstring(l, 1, "");
+ strwidth = Graphics::textwidth(string);
+ lua_pushinteger(l, strwidth);
+ return 1;
+}
+
+int luatpt_get_name(lua_State* l)
+{
+ if (luacon_model->GetUser().ID){
+ lua_pushstring(l, luacon_model->GetUser().Username.c_str());
+ return 1;
+ }
+ lua_pushstring(l, "");
+ return 1;
+}
+
+int luatpt_set_shortcuts(lua_State* l)
+{
+ return luaL_error(l, "set_shortcuts: deprecated");
+}
+
+int luatpt_delete(lua_State* l)
+{
+ int arg1, arg2;
+ arg1 = abs(luaL_optint(l, 1, 0));
+ arg2 = luaL_optint(l, 2, -1);
+ if(arg2 == -1 && arg1 < NPART){
+ luacon_sim->kill_part(arg1);
+ return 0;
+ }
+ arg2 = abs(arg2);
+ if(arg2 < YRES && arg1 < XRES){
+ luacon_sim->delete_part(arg1, arg2, 0);
+ return 0;
+ }
+ return luaL_error(l,"Invalid coordinates or particle ID");
+}
+
+int luatpt_register_step(lua_State* l)
+{
+ int ref, i, ifree = -1;
+ if(lua_isfunction(l, 1)){
+ for(i = 0; i<6; i++){
+ if(!step_functions[i]){
+ if (ifree<0) ifree = i;
+ } else {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, step_functions[i]);
+ if(lua_equal(l, 1, lua_gettop(l))){
+ lua_pop(l, 1);
+ return luaL_error(l, "Function already registered");
+ }
+ lua_pop(l, 1);
+ }
+ }
+ if (ifree>=0)
+ {
+ ref = luaL_ref(l, LUA_REGISTRYINDEX);
+ step_functions[ifree] = ref;
+ return 0;
+ }
+ else return luaL_error(l, "Step function limit reached");
+ }
+ return 0;
+}
+int luatpt_unregister_step(lua_State* l)
+{
+ int i;
+ if(lua_isfunction(l, 1)){
+ for(i = 0; i<6; i++){
+ if (step_functions[i]){
+ lua_rawgeti(l, LUA_REGISTRYINDEX, step_functions[i]);
+ if(lua_equal(l, 1, lua_gettop(l))){
+ lua_pop(l, 1);
+ luaL_unref(l, LUA_REGISTRYINDEX, step_functions[i]);
+ step_functions[i] = 0;
+ }
+ else lua_pop(l, 1);
+ }
+ }
+ }
+ return 0;
+}
+int luatpt_register_keypress(lua_State* l)
+{
+ int *newfunctions, i;
+ if(lua_isfunction(l, 1)){
+ for(i = 0; i<keypress_function_count; i++){
+ lua_rawgeti(l, LUA_REGISTRYINDEX, keypress_functions[i]);
+ if(lua_equal(l, 1, lua_gettop(l))){
+ lua_pop(l, 1);
+ return luaL_error(l, "Function already registered");
+ }
+ lua_pop(l, 1);
+ }
+ newfunctions = (int*)calloc(keypress_function_count+1, sizeof(int));
+ if(keypress_functions){
+ memcpy(newfunctions, keypress_functions, keypress_function_count*sizeof(int));
+ free(keypress_functions);
+ }
+ newfunctions[keypress_function_count] = luaL_ref(l, LUA_REGISTRYINDEX);
+ keypress_function_count++;
+ keypress_functions = newfunctions;
+ }
+ return 0;
+}
+int luatpt_unregister_keypress(lua_State* l)
+{
+ int *newfunctions, i, functionindex = -1;
+ if(lua_isfunction(l, 1)){
+ for(i = 0; i<keypress_function_count; i++){
+ lua_rawgeti(l, LUA_REGISTRYINDEX, keypress_functions[i]);
+ if(lua_equal(l, 1, lua_gettop(l))){
+ functionindex = i;
+ }
+ lua_pop(l, 1);
+ }
+ }
+ if(functionindex != -1){
+ luaL_unref(l, LUA_REGISTRYINDEX, keypress_functions[functionindex]);
+ if(functionindex != keypress_function_count-1){
+ memmove(keypress_functions+functionindex+1, keypress_functions+functionindex+1, (keypress_function_count-functionindex-1)*sizeof(int));
+ }
+ if(keypress_function_count-1 > 0){
+ newfunctions = (int*)calloc(keypress_function_count-1, sizeof(int));
+ memcpy(newfunctions, keypress_functions, (keypress_function_count-1)*sizeof(int));
+ free(keypress_functions);
+ keypress_functions = newfunctions;
+ } else {
+ free(keypress_functions);
+ keypress_functions = NULL;
+ }
+ keypress_function_count--;
+ } else {
+ return luaL_error(l, "Function not registered");
+ }
+ return 0;
+}
+int luatpt_register_mouseclick(lua_State* l)
+{
+ int *newfunctions, i;
+ if(lua_isfunction(l, 1)){
+ for(i = 0; i<mouseclick_function_count; i++){
+ lua_rawgeti(l, LUA_REGISTRYINDEX, mouseclick_functions[i]);
+ if(lua_equal(l, 1, lua_gettop(l))){
+ lua_pop(l, 1);
+ return luaL_error(l, "Function already registered");
+ }
+ lua_pop(l, 1);
+ }
+ newfunctions = (int*)calloc(mouseclick_function_count+1, sizeof(int));
+ if(mouseclick_functions){
+ memcpy(newfunctions, mouseclick_functions, mouseclick_function_count*sizeof(int));
+ free(mouseclick_functions);
+ }
+ newfunctions[mouseclick_function_count] = luaL_ref(l, LUA_REGISTRYINDEX);
+ mouseclick_function_count++;
+ mouseclick_functions = newfunctions;
+ }
+ return 0;
+}
+int luatpt_unregister_mouseclick(lua_State* l)
+{
+ int *newfunctions, i, functionindex = -1;
+ if(lua_isfunction(l, 1)){
+ for(i = 0; i<mouseclick_function_count; i++){
+ lua_rawgeti(l, LUA_REGISTRYINDEX, mouseclick_functions[i]);
+ if(lua_equal(l, 1, lua_gettop(l))){
+ functionindex = i;
+ }
+ lua_pop(l, 1);
+ }
+ }
+ if(functionindex != -1){
+ luaL_unref(l, LUA_REGISTRYINDEX, mouseclick_functions[functionindex]);
+ if(functionindex != mouseclick_function_count-1){
+ memmove(mouseclick_functions+functionindex+1, mouseclick_functions+functionindex+1, (mouseclick_function_count-functionindex-1)*sizeof(int));
+ }
+ if(mouseclick_function_count-1 > 0){
+ newfunctions = (int*)calloc(mouseclick_function_count-1, sizeof(int));
+ memcpy(newfunctions, mouseclick_functions, (mouseclick_function_count-1)*sizeof(int));
+ free(mouseclick_functions);
+ mouseclick_functions = newfunctions;
+ } else {
+ free(mouseclick_functions);
+ mouseclick_functions = NULL;
+ }
+ mouseclick_function_count--;
+ } else {
+ return luaL_error(l, "Function not registered");
+ }
+ return 0;
+}
+int luatpt_input(lua_State* l)
+{
+ std::string prompt, title, result, shadow, text;
+ title = std::string(luaL_optstring(l, 1, "Title"));
+ prompt = std::string(luaL_optstring(l, 2, "Enter some text:"));
+ text = std::string(luaL_optstring(l, 3, ""));
+ shadow = std::string(luaL_optstring(l, 4, ""));
+
+ result = TextPrompt::Blocking(title, prompt, text, shadow, false);
+
+ lua_pushstring(l, result.c_str());
+ return 1;
+}
+int luatpt_message_box(lua_State* l)
+{
+ std::string title = std::string(luaL_optstring(l, 1, "Title"));
+ std::string message = std::string(luaL_optstring(l, 2, "Message"));
+ new InformationMessage(title, message);
+ return 0;
+}
+int luatpt_get_numOfParts(lua_State* l)
+{
+ lua_pushinteger(l, luacon_sim->parts_lastActiveIndex);
+ return 1;
+}
+int luatpt_start_getPartIndex(lua_State* l)
+{
+ getPartIndex_curIdx = -1;
+ return 1;
+}
+int luatpt_next_getPartIndex(lua_State* l)
+{
+ while(1)
+ {
+ getPartIndex_curIdx++;
+ if(getPartIndex_curIdx >= NPART)
+ {
+ getPartIndex_curIdx = 0;
+ lua_pushboolean(l, 0);
+ return 1;
+ }
+ if(luacon_sim->parts[getPartIndex_curIdx].type)
+ break;
+
+ }
+
+ lua_pushboolean(l, 1);
+ return 1;
+}
+int luatpt_getPartIndex(lua_State* l)
+{
+ if(getPartIndex_curIdx < 0)
+ {
+ lua_pushinteger(l, 0);
+ return 1;
+ }
+ lua_pushinteger(l, getPartIndex_curIdx);
+ return 1;
+}
+int luatpt_hud(lua_State* l)
+{
+ /*int hudstate;
+ hudstate = luaL_optint(l, 1, 0);
+ hud_enable = (hudstate==0?0:1);
+ return 0;*/
+ //TODO IMPLEMENT
+ return 0;
+}
+int luatpt_gravity(lua_State* l)
+{
+ int gravstate;
+ gravstate = luaL_optint(l, 1, 0);
+ if(gravstate)
+ luacon_sim->grav->start_grav_async();
+ else
+ luacon_sim->grav->stop_grav_async();
+ return 0;
+}
+int luatpt_airheat(lua_State* l)
+{
+ int aheatstate;
+ aheatstate = luaL_optint(l, 1, 0);
+ luacon_sim->aheat_enable = (aheatstate==0?0:1);
+ return 0;
+}
+int luatpt_active_menu(lua_State* l)
+{
+ int menuid;
+ menuid = luaL_optint(l, 1, -1);
+ if (menuid < SC_TOTAL && menuid >= 0)
+ luacon_model->SetActiveMenu(luacon_model->GetMenuList()[menuid]);
+ else
+ return luaL_error(l, "Invalid menu");
+ return 0;
+}
+int luatpt_decorations_enable(lua_State* l)
+{
+ int decostate;
+ decostate = luaL_optint(l, 1, 0);
+ luacon_model->SetDecoration(decostate==0?false:true);
+ return 0;
+}
+
+int luatpt_heat(lua_State* l)
+{
+ int heatstate;
+ heatstate = luaL_optint(l, 1, 0);
+ luacon_sim->legacy_enable = (heatstate==1?0:1);
+ return 0;
+}
+int luatpt_cmode_set(lua_State* l)
+{
+ //TODO IMPLEMENT
+ return luaL_error(l, "cmode_set: Deprecated");
+}
+int luatpt_setfire(lua_State* l)
+{
+ int firesize = luaL_optint(l, 2, 4);
+ float fireintensity = (float)luaL_optnumber(l, 1, 1.0f);
+ luacon_model->GetRenderer()->prepare_alpha(firesize, fireintensity);
+ return 0;
+}
+int luatpt_setdebug(lua_State* l)
+{
+ return luaL_error(l, "setdebug: Deprecated");
+}
+int luatpt_setfpscap(lua_State* l)
+{
+ int fpscap = luaL_optint(l, 1, 0);
+ if (fpscap < 2)
+ return luaL_error(l, "fps cap too small");
+ ui::Engine::Ref().FpsLimit = fpscap;
+ return 0;
+}
+int luatpt_getscript(lua_State* l)
+{
+ char *filedata = NULL, *fileuri = NULL, *filename = NULL, *lastError = NULL, *luacommand = NULL;
+ std::string fileauthor = "", fileid = "";
+ int len, ret,run_script;
+ FILE * outputfile;
+
+ fileauthor = std::string(luaL_optstring(l, 1, ""));
+ fileid = std::string(luaL_optstring(l, 2, ""));
+ run_script = luaL_optint(l, 3, 0);
+ if(!fileauthor.length() || !fileid.length())
+ goto fin;
+ if(!ConfirmPrompt::Blocking("Do you want to install script?", fileid, "Install"))
+ goto fin;
+
+ fileuri = new char[strlen(SCRIPTSERVER)+fileauthor.length()+fileid.length()+44];
+ sprintf(fileuri, "http://" SCRIPTSERVER "/GetScript.api?Author=%s&Filename=%s", fileauthor.c_str(), fileid.c_str());
+
+ //filedata = http_auth_get(fileuri, svf_user_id, NULL, svf_session_id, &ret, &len);
+ filedata = http_auth_get(fileuri, NULL, NULL, NULL, &ret, &len);
+
+ if(len <= 0 || !filedata)
+ {
+ lastError = "Server did not return data.";
+ goto fin;
+ }
+ if(ret != 200)
+ {
+ lastError = http_ret_text(ret);
+ goto fin;
+ }
+
+ filename = new char[fileauthor.length()+fileid.length()+strlen(PATH_SEP)+strlen(LOCAL_LUA_DIR)+6];
+ sprintf(filename, LOCAL_LUA_DIR PATH_SEP "%s_%s.lua", fileauthor.c_str(), fileid.c_str());
+
+ Client::Ref().MakeDirectory(LOCAL_LUA_DIR);
+
+ outputfile = fopen(filename, "r");
+ if(outputfile)
+ {
+ fclose(outputfile);
+ outputfile = NULL;
+ if(ConfirmPrompt::Blocking("File already exists, overwrite?", filename, "Overwrite"))
+ {
+ outputfile = fopen(filename, "w");
+ }
+ else
+ {
+ goto fin;
+ }
+ }
+ else
+ {
+ outputfile = fopen(filename, "w");
+ }
+
+ if(!outputfile)
+ {
+ lastError = "Unable to write to file";
+ goto fin;
+ }
+
+
+ fputs(filedata, outputfile);
+ fclose(outputfile);
+ outputfile = NULL;
+ if(run_script)
+ {
+ luacommand = new char[strlen(filename)+20];
+ sprintf(luacommand,"dofile(\"%s\")",filename);
+ luaL_dostring (l, luacommand);
+ }
+
+fin:
+ if(filedata) free(filedata);
+ if(fileuri) delete[] fileuri;
+ if(filename) delete[] filename;
+ if(luacommand) delete[] luacommand;
+ luacommand = NULL;
+
+ if(lastError) return luaL_error(l, lastError);
+ return 0;
+}
+
+int luatpt_setwindowsize(lua_State* l)
+{
+ int scale = luaL_optint(l,1,1), kiosk = luaL_optint(l,2,0);
+ if (scale!=2) scale = 1;
+ if (kiosk!=1) kiosk = 0;
+ ui::Engine::Ref().SetScale(scale);
+ ui::Engine::Ref().SetFullscreen(kiosk);
+ return 0;
+}
+
+int screenshotIndex = 0;
+
+int luatpt_screenshot(lua_State* l)
+{
+ //TODO Implement
+ int captureUI = luaL_optint(l, 1, 0);
+ std::vector<char> data;
+ if(captureUI)
+ {
+ VideoBuffer screenshot(ui::Engine::Ref().g->DumpFrame());
+ data = format::VideoBufferToPNG(screenshot);
+ }
+ else
+ {
+ VideoBuffer screenshot(luacon_ren->DumpFrame());
+ data = format::VideoBufferToPNG(screenshot);
+ }
+ std::stringstream filename;
+ filename << "screenshot_";
+ filename << std::setfill('0') << std::setw(6) << (screenshotIndex++);
+ filename << ".png";
+ Client::Ref().WriteFile(data, filename.str());
+ return 0;
+}
+
diff --git a/src/cat/LuaBit.cpp b/src/cat/LuaBit.cpp
new file mode 100644
index 0000000..c55f45a
--- /dev/null
+++ b/src/cat/LuaBit.cpp
@@ -0,0 +1,192 @@
+/*
+** Lua BitOp -- a bit operations library for Lua 5.1/5.2.
+** http://bitop.luajit.org/
+**
+** Copyright (C) 2008-2012 Mike Pall. All rights reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
+*/
+
+#define LUA_BITOP_VERSION "1.0.2"
+
+extern "C"
+{
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#ifdef _MSC_VER
+/* MSVC is stuck in the last century and doesn't have C99's stdint.h. */
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+typedef int32_t SBits;
+typedef uint32_t UBits;
+
+typedef union {
+ lua_Number n;
+#ifdef LUA_NUMBER_DOUBLE
+ uint64_t b;
+#else
+ UBits b;
+#endif
+} BitNum;
+
+/* Convert argument to bit type. */
+static UBits barg(lua_State *L, int idx)
+{
+ BitNum bn;
+ UBits b;
+#if LUA_VERSION_NUM < 502
+ bn.n = lua_tonumber(L, idx);
+#else
+ bn.n = luaL_checknumber(L, idx);
+#endif
+#if defined(LUA_NUMBER_DOUBLE)
+ bn.n += 6755399441055744.0; /* 2^52+2^51 */
+#ifdef SWAPPED_DOUBLE
+ b = (UBits)(bn.b >> 32);
+#else
+ b = (UBits)bn.b;
+#endif
+#elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \
+ defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \
+ defined(LUA_NUMBER_LLONG)
+ if (sizeof(UBits) == sizeof(lua_Number))
+ b = bn.b;
+ else
+ b = (UBits)(SBits)bn.n;
+#elif defined(LUA_NUMBER_FLOAT)
+#error "A 'float' lua_Number type is incompatible with this library"
+#else
+#error "Unknown number type, check LUA_NUMBER_* in luaconf.h"
+#endif
+#if LUA_VERSION_NUM < 502
+ if (b == 0 && !lua_isnumber(L, idx)) {
+ luaL_typerror(L, idx, "number");
+ }
+#endif
+ return b;
+}
+
+/* Return bit type. */
+#define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1;
+
+static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) }
+static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) }
+
+#define BIT_OP(func, opr) \
+ static int func(lua_State *L) { int i; UBits b = barg(L, 1); \
+ for (i = lua_gettop(L); i > 1; i--) b opr barg(L, i); BRET(b) }
+BIT_OP(bit_band, &=)
+BIT_OP(bit_bor, |=)
+BIT_OP(bit_bxor, ^=)
+
+#define bshl(b, n) (b << n)
+#define bshr(b, n) (b >> n)
+#define bsar(b, n) ((SBits)b >> n)
+#define brol(b, n) ((b << n) | (b >> (32-n)))
+#define bror(b, n) ((b << (32-n)) | (b >> n))
+#define BIT_SH(func, fn) \
+ static int func(lua_State *L) { \
+ UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) }
+BIT_SH(bit_lshift, bshl)
+BIT_SH(bit_rshift, bshr)
+BIT_SH(bit_arshift, bsar)
+BIT_SH(bit_rol, brol)
+BIT_SH(bit_ror, bror)
+
+static int bit_bswap(lua_State *L)
+{
+ UBits b = barg(L, 1);
+ b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24);
+ BRET(b)
+}
+
+static int bit_tohex(lua_State *L)
+{
+ UBits b = barg(L, 1);
+ SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2);
+ const char *hexdigits = "0123456789abcdef";
+ char buf[8];
+ int i;
+ if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
+ if (n > 8) n = 8;
+ for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
+ lua_pushlstring(L, buf, (size_t)n);
+ return 1;
+}
+
+static const struct luaL_Reg bit_funcs[] = {
+ { "tobit", bit_tobit },
+ { "bnot", bit_bnot },
+ { "band", bit_band },
+ { "bor", bit_bor },
+ { "bxor", bit_bxor },
+ { "lshift", bit_lshift },
+ { "rshift", bit_rshift },
+ { "arshift", bit_arshift },
+ { "rol", bit_rol },
+ { "ror", bit_ror },
+ { "bswap", bit_bswap },
+ { "tohex", bit_tohex },
+ { NULL, NULL }
+};
+
+/* Signed right-shifts are implementation-defined per C89/C99.
+** But the de facto standard are arithmetic right-shifts on two's
+** complement CPUs. This behaviour is required here, so test for it.
+*/
+#define BAD_SAR (bsar(-8, 2) != (SBits)-2)
+
+int luaopen_bit(lua_State *L)
+{
+ UBits b;
+ lua_pushnumber(L, (lua_Number)1437217655L);
+ b = barg(L, -1);
+ if (b != (UBits)1437217655L || BAD_SAR) { /* Perform a simple self-test. */
+ const char *msg = "compiled with incompatible luaconf.h";
+#ifdef LUA_NUMBER_DOUBLE
+#ifdef _WIN32
+ if (b == (UBits)1610612736L)
+ msg = "use D3DCREATE_FPU_PRESERVE with DirectX";
+#endif
+ if (b == (UBits)1127743488L)
+ msg = "not compiled with SWAPPED_DOUBLE";
+#endif
+ if (BAD_SAR)
+ msg = "arithmetic right-shift broken";
+ luaL_error(L, "bit library self-test failed (%s)", msg);
+ }
+#if LUA_VERSION_NUM < 502
+ luaL_register(L, "bit", bit_funcs);
+#else
+ luaL_newlib(L, bit_funcs);
+#endif
+ return 1;
+}
+
diff --git a/src/cat/LuaBit.h b/src/cat/LuaBit.h
new file mode 100644
index 0000000..6d9c068
--- /dev/null
+++ b/src/cat/LuaBit.h
@@ -0,0 +1,32 @@
+/*
+** Lua BitOp -- a bit operations library for Lua 5.1/5.2.
+** http://bitop.luajit.org/
+**
+** Copyright (C) 2008-2012 Mike Pall. All rights reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
+*/
+
+#define LUA_BITOP_VERSION "1.0.2"
+
+int luaopen_bit(lua_State *L);
+
diff --git a/src/cat/LuaButton.cpp b/src/cat/LuaButton.cpp
new file mode 100644
index 0000000..01ca875
--- /dev/null
+++ b/src/cat/LuaButton.cpp
@@ -0,0 +1,114 @@
+extern "C"
+{
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include <iostream>
+#include "LuaButton.h"
+#include "LuaScriptInterface.h"
+#include "interface/Button.h"
+
+const char LuaButton::className[] = "Button";
+
+#define method(class, name) {#name, &class::name}
+Luna<LuaButton>::RegType LuaButton::methods[] = {
+ method(LuaButton, action),
+ method(LuaButton, text),
+ method(LuaButton, position),
+ method(LuaButton, size),
+ method(LuaButton, visible),
+ method(LuaButton, enabled),
+ {0, 0}
+};
+
+LuaButton::LuaButton(lua_State * l) :
+ LuaComponent(l),
+ actionFunction(0)
+{
+ int posX = luaL_optinteger(l, 1, 0);
+ int posY = luaL_optinteger(l, 2, 0);
+ int sizeX = luaL_optinteger(l, 3, 10);
+ int sizeY = luaL_optinteger(l, 4, 10);
+ std::string text = luaL_optstring(l, 5, "");
+ std::string toolTip = luaL_optstring(l, 6, "");
+
+ button = new ui::Button(ui::Point(posX, posY), ui::Point(sizeX, sizeY), text, toolTip);
+ component = button;
+ class ClickAction : public ui::ButtonAction
+ {
+ LuaButton * luaButton;
+ public:
+ ClickAction(LuaButton * luaButton) : luaButton(luaButton) {}
+ void ActionCallback(ui::Button * sender)
+ {
+ luaButton->triggerAction();
+ }
+ };
+ button->SetActionCallback(new ClickAction(this));
+}
+
+int LuaButton::enabled(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TBOOLEAN);
+ button->Enabled = lua_toboolean(l, 1);
+ return 0;
+ }
+ else
+ {
+ lua_pushboolean(l, button->Enabled);
+ return 1;
+ }
+}
+
+int LuaButton::action(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ actionFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ actionFunction = 0;
+ }
+ return 0;
+}
+
+int LuaButton::text(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TSTRING);
+ button->SetText(lua_tostring(l, 1));
+ return 0;
+ }
+ else
+ {
+ lua_pushstring(l, button->GetText().c_str());
+ return 1;
+ }
+}
+
+void LuaButton::triggerAction()
+{
+ if(actionFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, actionFunction);
+ lua_rawgeti(l, LUA_REGISTRYINDEX, UserData);
+ if (lua_pcall(l, 1, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+LuaButton::~LuaButton()
+{
+} \ No newline at end of file
diff --git a/src/cat/LuaButton.h b/src/cat/LuaButton.h
new file mode 100644
index 0000000..012779d
--- /dev/null
+++ b/src/cat/LuaButton.h
@@ -0,0 +1,33 @@
+#pragma once
+
+extern "C" {
+ #include "lua.h"
+ #include "lauxlib.h"
+ #include "lualib.h"
+}
+
+#include "LuaLuna.h"
+#include "LuaComponent.h"
+
+namespace ui
+{
+ class Button;
+}
+
+class LuaScriptInterface;
+
+class LuaButton: public LuaComponent
+{
+ ui::Button * button;
+ int actionFunction;
+ void triggerAction();
+ int action(lua_State * l);
+ int text(lua_State * l);
+ int enabled(lua_State * l);
+public:
+ static const char className[];
+ static Luna<LuaButton>::RegType methods[];
+
+ LuaButton(lua_State * l);
+ ~LuaButton();
+}; \ No newline at end of file
diff --git a/src/cat/LuaCheckbox.cpp b/src/cat/LuaCheckbox.cpp
new file mode 100644
index 0000000..844573e
--- /dev/null
+++ b/src/cat/LuaCheckbox.cpp
@@ -0,0 +1,114 @@
+extern "C"
+{
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include <iostream>
+#include "LuaCheckbox.h"
+#include "LuaScriptInterface.h"
+#include "interface/Checkbox.h"
+
+const char LuaCheckbox::className[] = "Checkbox";
+
+#define method(class, name) {#name, &class::name}
+Luna<LuaCheckbox>::RegType LuaCheckbox::methods[] = {
+ method(LuaCheckbox, action),
+ method(LuaCheckbox, text),
+ method(LuaCheckbox, position),
+ method(LuaCheckbox, size),
+ method(LuaCheckbox, visible),
+ method(LuaCheckbox, checked),
+ {0, 0}
+};
+
+LuaCheckbox::LuaCheckbox(lua_State * l) :
+ LuaComponent(l),
+ actionFunction(0)
+{
+ int posX = luaL_optinteger(l, 1, 0);
+ int posY = luaL_optinteger(l, 2, 0);
+ int sizeX = luaL_optinteger(l, 3, 10);
+ int sizeY = luaL_optinteger(l, 4, 10);
+ std::string text = luaL_optstring(l, 5, "");
+
+ checkbox = new ui::Checkbox(ui::Point(posX, posY), ui::Point(sizeX, sizeY), text, "");
+ component = checkbox;
+ class ClickAction : public ui::CheckboxAction
+ {
+ LuaCheckbox * luaCheckbox;
+ public:
+ ClickAction(LuaCheckbox * luaCheckbox) : luaCheckbox(luaCheckbox) {}
+ void ActionCallback(ui::Checkbox * sender)
+ {
+ luaCheckbox->triggerAction();
+ }
+ };
+ checkbox->SetActionCallback(new ClickAction(this));
+}
+
+int LuaCheckbox::checked(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TBOOLEAN);
+ checkbox->SetChecked(lua_toboolean(l, 1));
+ return 0;
+ }
+ else
+ {
+ lua_pushboolean(l, checkbox->GetChecked());
+ return 1;
+ }
+}
+
+int LuaCheckbox::action(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ actionFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ actionFunction = 0;
+ }
+ return 0;
+}
+
+int LuaCheckbox::text(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TSTRING);
+ checkbox->SetText(lua_tostring(l, 1));
+ return 0;
+ }
+ else
+ {
+ lua_pushstring(l, checkbox->GetText().c_str());
+ return 1;
+ }
+}
+
+void LuaCheckbox::triggerAction()
+{
+ if(actionFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, actionFunction);
+ lua_rawgeti(l, LUA_REGISTRYINDEX, UserData);
+ lua_pushboolean(l, checkbox->GetChecked());
+ if (lua_pcall(l, 2, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+LuaCheckbox::~LuaCheckbox()
+{
+} \ No newline at end of file
diff --git a/src/cat/LuaCheckbox.h b/src/cat/LuaCheckbox.h
new file mode 100644
index 0000000..479cab2
--- /dev/null
+++ b/src/cat/LuaCheckbox.h
@@ -0,0 +1,33 @@
+#pragma once
+
+extern "C" {
+ #include "lua.h"
+ #include "lauxlib.h"
+ #include "lualib.h"
+}
+
+#include "LuaLuna.h"
+#include "LuaComponent.h"
+
+namespace ui
+{
+ class Checkbox;
+}
+
+class LuaScriptInterface;
+
+class LuaCheckbox: public LuaComponent
+{
+ ui::Checkbox * checkbox;
+ int actionFunction;
+ void triggerAction();
+ int action(lua_State * l);
+ int checked(lua_State * l);
+ int text(lua_State * l);
+public:
+ static const char className[];
+ static Luna<LuaCheckbox>::RegType methods[];
+
+ LuaCheckbox(lua_State * l);
+ ~LuaCheckbox();
+}; \ No newline at end of file
diff --git a/src/cat/LuaComponent.cpp b/src/cat/LuaComponent.cpp
new file mode 100644
index 0000000..8c2d3a4
--- /dev/null
+++ b/src/cat/LuaComponent.cpp
@@ -0,0 +1,82 @@
+extern "C"
+{
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include <iostream>
+#include "LuaComponent.h"
+#include "LuaScriptInterface.h"
+#include "interface/Component.h"
+
+
+LuaComponent::LuaComponent(lua_State * l)
+{
+ this->l = l;
+
+ lua_pushstring(l, "Luacon_ci");
+ lua_gettable(l, LUA_REGISTRYINDEX);
+ ci = (LuaScriptInterface*)lua_touserdata(l, -1);
+ lua_pop(l, 1);
+}
+
+int LuaComponent::position(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TNUMBER);
+ luaL_checktype(l, 2, LUA_TNUMBER);
+ component->Position = ui::Point(lua_tointeger(l, 1), lua_tointeger(l, 2));
+ return 0;
+ }
+ else
+ {
+ lua_pushinteger(l, component->Position.X);
+ lua_pushinteger(l, component->Position.Y);
+ return 2;
+ }
+}
+
+int LuaComponent::size(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TNUMBER);
+ luaL_checktype(l, 2, LUA_TNUMBER);
+ component->Size = ui::Point(lua_tointeger(l, 1), lua_tointeger(l, 2));
+ component->Invalidate();
+ return 0;
+ }
+ else
+ {
+ lua_pushinteger(l, component->Size.X);
+ lua_pushinteger(l, component->Size.Y);
+ return 2;
+ }
+}
+
+int LuaComponent::visible(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TBOOLEAN);
+ component->Visible = lua_toboolean(l, 1);
+ return 0;
+ }
+ else
+ {
+ lua_pushboolean(l, component->Visible);
+ return 1;
+ }
+}
+
+LuaComponent::~LuaComponent()
+{
+ if(component->GetParentWindow())
+ component->GetParentWindow()->RemoveComponent(component);
+ delete component;
+} \ No newline at end of file
diff --git a/src/cat/LuaComponent.h b/src/cat/LuaComponent.h
new file mode 100644
index 0000000..9e11b12
--- /dev/null
+++ b/src/cat/LuaComponent.h
@@ -0,0 +1,33 @@
+#pragma once
+
+extern "C" {
+ #include "lua.h"
+ #include "lauxlib.h"
+ #include "lualib.h"
+}
+
+#include "LuaLuna.h"
+
+namespace ui
+{
+ class Component;
+}
+
+class LuaScriptInterface;
+
+class LuaComponent
+{
+protected:
+ ui::Component * component;
+ lua_State * l;
+ int position(lua_State * l);
+ int size(lua_State * l);
+ int visible(lua_State * l);
+public:
+ LuaScriptInterface * ci;
+ int UserData;
+
+ ui::Component * GetComponent() { return component; }
+ LuaComponent(lua_State * l);
+ ~LuaComponent();
+}; \ No newline at end of file
diff --git a/src/cat/LuaLabel.cpp b/src/cat/LuaLabel.cpp
new file mode 100644
index 0000000..e2ca56d
--- /dev/null
+++ b/src/cat/LuaLabel.cpp
@@ -0,0 +1,56 @@
+extern "C"
+{
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include <iostream>
+#include "LuaScriptInterface.h"
+#include "LuaLabel.h"
+#include "interface/Label.h"
+
+const char LuaLabel::className[] = "Label";
+
+#define method(class, name) {#name, &class::name}
+Luna<LuaLabel>::RegType LuaLabel::methods[] = {
+ method(LuaLabel, text),
+ method(LuaLabel, position),
+ method(LuaLabel, size),
+ method(LuaLabel, visible),
+ {0, 0}
+};
+
+LuaLabel::LuaLabel(lua_State * l) :
+ LuaComponent(l)
+{
+ this->l = l;
+ int posX = luaL_optinteger(l, 1, 0);
+ int posY = luaL_optinteger(l, 2, 0);
+ int sizeX = luaL_optinteger(l, 3, 10);
+ int sizeY = luaL_optinteger(l, 4, 10);
+ std::string text = luaL_optstring(l, 5, "");
+
+ label = new ui::Label(ui::Point(posX, posY), ui::Point(sizeX, sizeY), text);
+ component = label;
+}
+
+int LuaLabel::text(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TSTRING);
+ label->SetText(lua_tostring(l, 1));
+ return 0;
+ }
+ else
+ {
+ lua_pushstring(l, label->GetText().c_str());
+ return 1;
+ }
+}
+
+LuaLabel::~LuaLabel()
+{
+} \ No newline at end of file
diff --git a/src/cat/LuaLabel.h b/src/cat/LuaLabel.h
new file mode 100644
index 0000000..a80ea4f
--- /dev/null
+++ b/src/cat/LuaLabel.h
@@ -0,0 +1,29 @@
+#pragma once
+
+extern "C" {
+ #include "lua.h"
+ #include "lauxlib.h"
+ #include "lualib.h"
+}
+
+#include "LuaLuna.h"
+#include "LuaComponent.h"
+
+namespace ui
+{
+ class Label;
+}
+
+class LuaScriptInterface;
+
+class LuaLabel: public LuaComponent
+{
+ ui::Label * label;
+ int text(lua_State * l);
+public:
+ static const char className[];
+ static Luna<LuaLabel>::RegType methods[];
+
+ LuaLabel(lua_State * l);
+ ~LuaLabel();
+}; \ No newline at end of file
diff --git a/src/cat/LuaLuna.h b/src/cat/LuaLuna.h
new file mode 100644
index 0000000..1d5d937
--- /dev/null
+++ b/src/cat/LuaLuna.h
@@ -0,0 +1,160 @@
+#pragma once
+//http://lua-users.org/wiki/SimplerCppBinding
+
+extern "C" {
+#include "lua.h"
+#include "lauxlib.h"
+}
+
+template <typename T> class Luna
+{
+ typedef struct { T *pT; } userdataType;
+public:
+ typedef int (T::*mfp)(lua_State *L);
+ typedef struct { const char *name; mfp mfunc; } RegType;
+
+ static void Register(lua_State *L)
+ {
+ lua_newtable(L);
+ int methods = lua_gettop(L);
+
+ luaL_newmetatable(L, T::className);
+ int metatable = lua_gettop(L);
+
+ // store method table in globals so that
+ // scripts can add functions written in Lua.
+ lua_pushstring(L, T::className);
+ lua_pushvalue(L, methods);
+ lua_settable(L, LUA_GLOBALSINDEX);
+
+ lua_pushliteral(L, "__metatable");
+ lua_pushvalue(L, methods);
+ lua_settable(L, metatable); // hide metatable from Lua getmetatable()
+
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, methods);
+ lua_settable(L, metatable);
+
+ lua_pushliteral(L, "__tostring");
+ lua_pushcfunction(L, tostring_T);
+ lua_settable(L, metatable);
+
+ lua_pushliteral(L, "__gc");
+ lua_pushcfunction(L, gc_T);
+ lua_settable(L, metatable);
+
+ lua_newtable(L); // mt for method table
+ int mt = lua_gettop(L);
+ lua_pushliteral(L, "__call");
+ lua_pushcfunction(L, new_T);
+ lua_pushliteral(L, "new");
+ lua_pushvalue(L, -2); // dup new_T function
+ lua_settable(L, methods); // add new_T to method table
+ lua_settable(L, mt); // mt.__call = new_T
+ lua_setmetatable(L, methods);
+
+ // fill method table with methods from class T
+ for (RegType *l = T::methods; l->name; l++)
+ {
+ /* edited by Snaily: shouldn't it be const RegType *l ... ? */
+ lua_pushstring(L, l->name);
+ lua_pushlightuserdata(L, (void*)l);
+ lua_pushcclosure(L, thunk, 1);
+ lua_settable(L, methods);
+ }
+
+ lua_pop(L, 2); // drop metatable and method table
+ }
+
+ // get userdata from Lua stack and return pointer to T object
+ static T * check(lua_State * L, int narg)
+ {
+ userdataType *ud = static_cast<userdataType*>(luaL_checkudata(L, narg, T::className));
+ if(!ud)
+ luaL_typerror(L, narg, T::className);
+ return ud->pT; // pointer to T object
+ }
+
+ static void * tryGet(lua_State * L, int narg)
+ {
+ if(checkType(L, narg, T::className))
+ {
+ userdataType *ud = static_cast<userdataType*>(luaL_checkudata(L, narg, T::className));
+ if(!ud)
+ luaL_typerror(L, narg, T::className);
+ return ud; // pointer to T object
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+ static bool checkType (lua_State * L, int idx, const char *name)
+ {
+ // returns true if a userdata is of a certain type
+ int res;
+ if (lua_type(L, idx) != LUA_TUSERDATA) return false;
+ lua_getmetatable(L, idx);
+ luaL_newmetatable (L, name);
+ res = lua_equal(L, -2, -1);
+ lua_pop(L, 2); // pop both tables (metatables) off
+ return res;
+ }
+
+ static inline T * get(void * userData)
+ {
+ return ((userdataType*)userData)->pT;
+ }
+
+private:
+ Luna(); // hide default constructor
+
+ // member function dispatcher
+ static int thunk(lua_State * L)
+ {
+ // stack has userdata, followed by method args
+ T *obj = check(L, 1); // get 'self', or if you prefer, 'this'
+ lua_remove(L, 1); // remove self so member function args start at index 1
+ // get member function from upvalue
+ RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
+ return (obj->*(l->mfunc))(L); // call member function
+ }
+
+ // create a new T object and
+ // push onto the Lua stack a userdata containing a pointer to T object
+ static int new_T(lua_State * L)
+ {
+ lua_remove(L, 1); // use classname:new(), instead of classname.new()
+
+ T *obj = new T(L); // call constructor for T objects
+ userdataType *ud = static_cast<userdataType*>(lua_newuserdata(L, sizeof(userdataType)));
+ ud->pT = obj; // store pointer to object in userdata
+
+ obj->UserData = luaL_ref(L, LUA_REGISTRYINDEX);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, obj->UserData);
+
+ luaL_getmetatable(L, T::className); // lookup metatable in Lua registry
+ lua_setmetatable(L, -2);
+ return 1; // userdata containing pointer to T object
+ }
+
+ // garbage collection metamethod
+ static int gc_T(lua_State *L)
+ {
+ userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
+ T *obj = ud->pT;
+ delete obj; // call destructor for T objects
+ return 0;
+ }
+
+ static int tostring_T (lua_State * L)
+ {
+ char buff[32];
+ userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
+ T *obj = ud->pT;
+ sprintf(buff, "%p", obj);
+ lua_pushfstring(L, "%s (%s)", T::className, buff);
+ return 1;
+ }
+}; \ No newline at end of file
diff --git a/src/cat/LuaProgressBar.cpp b/src/cat/LuaProgressBar.cpp
new file mode 100644
index 0000000..71b8f7b
--- /dev/null
+++ b/src/cat/LuaProgressBar.cpp
@@ -0,0 +1,71 @@
+extern "C"
+{
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include <iostream>
+#include "LuaProgressBar.h"
+#include "LuaScriptInterface.h"
+#include "interface/ProgressBar.h"
+
+const char LuaProgressBar::className[] = "ProgressBar";
+
+#define method(class, name) {#name, &class::name}
+Luna<LuaProgressBar>::RegType LuaProgressBar::methods[] = {
+ method(LuaProgressBar, position),
+ method(LuaProgressBar, size),
+ method(LuaProgressBar, visible),
+ method(LuaProgressBar, progress),
+ method(LuaProgressBar, status),
+ {0, 0}
+};
+
+LuaProgressBar::LuaProgressBar(lua_State * l) :
+ LuaComponent(l)
+{
+ int posX = luaL_optinteger(l, 1, 0);
+ int posY = luaL_optinteger(l, 2, 0);
+ int sizeX = luaL_optinteger(l, 3, 10);
+ int sizeY = luaL_optinteger(l, 4, 10);
+ int value = luaL_optinteger(l, 5, 0);
+ std::string status = luaL_optstring(l, 6, "");
+
+ progressBar = new ui::ProgressBar(ui::Point(posX, posY), ui::Point(sizeX, sizeY), value, status);
+ component = progressBar;
+}
+
+int LuaProgressBar::progress(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ progressBar->SetProgress(lua_tointeger(l, 1));
+ return 0;
+ }
+ else
+ {
+ lua_pushinteger(l, progressBar->GetProgress());
+ return 1;
+ }
+}
+
+int LuaProgressBar::status(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ progressBar->SetStatus(std::string(lua_tostring(l, 1)));
+ return 0;
+ }
+ else
+ {
+ lua_pushstring(l, progressBar->GetStatus().c_str());
+ return 1;
+ }
+}
+
+LuaProgressBar::~LuaProgressBar()
+{
+} \ No newline at end of file
diff --git a/src/cat/LuaProgressBar.h b/src/cat/LuaProgressBar.h
new file mode 100644
index 0000000..9de56e7
--- /dev/null
+++ b/src/cat/LuaProgressBar.h
@@ -0,0 +1,30 @@
+#pragma once
+
+extern "C" {
+ #include "lua.h"
+ #include "lauxlib.h"
+ #include "lualib.h"
+}
+
+#include "LuaLuna.h"
+#include "LuaComponent.h"
+
+namespace ui
+{
+ class ProgressBar;
+}
+
+class LuaScriptInterface;
+
+class LuaProgressBar: public LuaComponent
+{
+ ui::ProgressBar * progressBar;
+ int progress(lua_State * l);
+ int status(lua_State * l);
+public:
+ static const char className[];
+ static Luna<LuaProgressBar>::RegType methods[];
+
+ LuaProgressBar(lua_State * l);
+ ~LuaProgressBar();
+}; \ No newline at end of file
diff --git a/src/cat/LuaScriptHelper.h b/src/cat/LuaScriptHelper.h
new file mode 100644
index 0000000..37920b5
--- /dev/null
+++ b/src/cat/LuaScriptHelper.h
@@ -0,0 +1,144 @@
+/*
+ * LuaScriptHelper.h
+ *
+ * Created on: Feb 12, 2012
+ * Author: Simon
+ */
+
+#ifndef LUASCRIPTHELPER_H_
+#define LUASCRIPTHELPER_H_
+
+extern GameModel * luacon_model;
+extern Simulation * luacon_sim;
+extern LuaScriptInterface * luacon_ci;
+extern Graphics * luacon_g;
+extern Renderer * luacon_ren;
+
+extern bool *luacon_currentCommand;
+extern std::string *luacon_lastError;
+
+extern int *lua_el_func, *lua_el_mode, *lua_gr_func;
+
+extern int getPartIndex_curIdx;
+extern int step_functions[6];//[6] = {0, 0, 0, 0, 0, 0};
+extern int keypress_function_count;// = 0;
+extern int *keypress_functions;// = NULL;
+extern int mouseclick_function_count;// = 0;
+extern int *mouseclick_functions;// = NULL;
+extern int tptProperties; //Table for some TPT properties
+extern int tptPropertiesVersion;
+extern int tptElements; //Table for TPT element names
+extern int tptParts, tptPartsMeta, tptElementTransitions, tptPartsCData, tptPartMeta, tptPart, cIndex;
+
+void luacon_hook(lua_State *L, lua_Debug *ar);
+int luacon_step(int mx, int my, int selectl, int selectr, int bsx, int bsy);
+int luacon_mouseevent(int mx, int my, int mb, int event, int mouse_wheel);
+int luacon_keyevent(int key, int modifier, int event);
+int luacon_eval(char *command);
+int luacon_part_update(int t, int i, int x, int y, int surround_space, int nt);
+char *luacon_geterror();
+void luacon_close();
+int luacon_partsread(lua_State* l);
+int luacon_partswrite(lua_State* l);
+int luacon_partread(lua_State* l);
+int luacon_partwrite(lua_State* l);
+int luacon_elementread(lua_State* l);
+int luacon_elementwrite(lua_State* l);
+int luacon_transitionread(lua_State* l);
+int luacon_transitionwrite(lua_State* l);
+int luacon_particle_getproperty(char * key, int * format);
+int luacon_transition_getproperty(char * key, int * format);
+int luacon_element_getproperty(char * key, int * format, unsigned int * modified_stuff);
+//int process_command_lua(pixel *vid_buf, char *console, char *console_error);
+
+//Interface
+int luatpt_test(lua_State* l);
+int luatpt_getelement(lua_State *l);
+
+int luacon_graphicsReplacement(GRAPHICS_FUNC_ARGS);
+int luatpt_graphics_func(lua_State *l);
+
+int luacon_elementReplacement(UPDATE_FUNC_ARGS);
+int luatpt_element_func(lua_State *l);
+
+int luatpt_error(lua_State* l);
+int luatpt_drawtext(lua_State* l);
+
+int luatpt_create(lua_State* l);
+
+int luatpt_setpause(lua_State* l);
+
+int luatpt_togglepause(lua_State* l);
+
+int luatpt_togglewater(lua_State* l);
+
+int luatpt_setconsole(lua_State* l);
+int luatpt_log(lua_State* l);
+
+int luatpt_set_pressure(lua_State* l);
+
+int luatpt_set_gravity(lua_State* l);
+int luatpt_reset_gravity_field(lua_State* l);
+
+int luatpt_reset_velocity(lua_State* l);
+
+int luatpt_reset_spark(lua_State* l);
+
+int luatpt_set_property(lua_State* l);
+
+int luatpt_get_property(lua_State* l);
+
+int luatpt_set_wallmap(lua_State* l);
+
+int luatpt_get_wallmap(lua_State* l);
+
+int luatpt_set_elecmap(lua_State* l);
+
+int luatpt_get_elecmap(lua_State* l);
+
+int luatpt_drawpixel(lua_State* l);
+
+int luatpt_drawrect(lua_State* l);
+
+int luatpt_fillrect(lua_State* l);
+
+int luatpt_drawline(lua_State* l);
+
+int luatpt_textwidth(lua_State* l);
+int luatpt_get_name(lua_State* l);
+
+int luatpt_set_shortcuts(lua_State* l);
+
+int luatpt_delete(lua_State* l);
+int luatpt_register_step(lua_State* l);
+int luatpt_unregister_step(lua_State* l);
+int luatpt_register_keypress(lua_State* l);
+int luatpt_unregister_keypress(lua_State* l);
+int luatpt_register_mouseclick(lua_State* l);
+int luatpt_unregister_mouseclick(lua_State* l);
+int luatpt_input(lua_State* l);
+int luatpt_message_box(lua_State* l);
+int luatpt_get_numOfParts(lua_State* l);
+int luatpt_start_getPartIndex(lua_State* l);
+int luatpt_next_getPartIndex(lua_State* l);
+int luatpt_getPartIndex(lua_State* l);
+int luatpt_hud(lua_State* l);
+int luatpt_gravity(lua_State* l);
+int luatpt_airheat(lua_State* l);
+int luatpt_active_menu(lua_State* l);
+int luatpt_decorations_enable(lua_State* l);
+
+int luatpt_heat(lua_State* l);
+int luatpt_cmode_set(lua_State* l);
+int luatpt_setfire(lua_State* l);
+int luatpt_setdebug(lua_State* l);
+
+int luatpt_setfpscap(lua_State* l);
+
+int luatpt_getscript(lua_State* l);
+
+int luatpt_setwindowsize(lua_State* l);
+
+int luatpt_screenshot(lua_State* l);
+
+#endif /* LUASCRIPTHELPER_H_ */
diff --git a/src/cat/LuaScriptInterface.cpp b/src/cat/LuaScriptInterface.cpp
new file mode 100644
index 0000000..7faf943
--- /dev/null
+++ b/src/cat/LuaScriptInterface.cpp
@@ -0,0 +1,1706 @@
+/*
+ * LuaScriptInterface.cpp
+ *
+ * Created on: Feb 11, 2012
+ * Author: Simon
+ */
+
+#include <string>
+#include <iomanip>
+#include <vector>
+#include <algorithm>
+#include <locale>
+#include <fstream>
+#include "Config.h"
+#include "Format.h"
+#include "LuaLuna.h"
+#include "LuaScriptInterface.h"
+#include "TPTScriptInterface.h"
+#include "dialogues/ErrorMessage.h"
+#include "dialogues/InformationMessage.h"
+#include "dialogues/TextPrompt.h"
+#include "dialogues/ConfirmPrompt.h"
+#include "simulation/Simulation.h"
+#include "game/GameModel.h"
+#include "LuaScriptHelper.h"
+#include "client/HTTP.h"
+
+//#include "virtualmachine/VirtualMachine.h"
+#include "pim/Parser.h"
+#include "pim/Machine.h"
+
+#include "LuaBit.h"
+
+#include "LuaWindow.h"
+#include "LuaButton.h"
+#include "LuaLabel.h"
+#include "LuaTextbox.h"
+#include "LuaCheckbox.h"
+#include "LuaSlider.h"
+#include "LuaProgressBar.h"
+
+#ifdef __unix__
+#include <unistd.h>
+#endif
+
+extern "C"
+{
+#ifdef WIN
+#include <direct.h>
+#endif
+#include <sys/stat.h>
+#include <dirent.h>
+#include <time.h>
+}
+
+GameModel * luacon_model;
+Simulation * luacon_sim;
+LuaScriptInterface * luacon_ci;
+Graphics * luacon_g;
+Renderer * luacon_ren;
+
+bool *luacon_currentCommand;
+std::string *luacon_lastError;
+
+int *lua_el_func, *lua_el_mode, *lua_gr_func;
+
+int getPartIndex_curIdx;
+int step_functions[6] = {0, 0, 0, 0, 0, 0};
+int keypress_function_count = 0;
+int *keypress_functions = NULL;
+int mouseclick_function_count = 0;
+int *mouseclick_functions = NULL;
+int tptProperties; //Table for some TPT properties
+int tptPropertiesVersion;
+int tptElements; //Table for TPT element names
+int tptParts, tptPartsMeta, tptElementTransitions, tptPartsCData, tptPartMeta, tptPart, cIndex;
+
+LuaScriptInterface::LuaScriptInterface(GameController * c, GameModel * m):
+ CommandInterface(c, m),
+ currentCommand(false),
+ legacy(new TPTScriptInterface(c, m))
+{
+ luacon_model = m;
+ luacon_sim = m->GetSimulation();
+ luacon_g = ui::Engine::Ref().g;
+ luacon_ren = m->GetRenderer();
+ luacon_ci = this;
+
+ //New TPT API
+ l = lua_open();
+ luaL_openlibs(l);
+ luaopen_bit(l);
+
+ lua_pushstring(l, "Luacon_ci");
+ lua_pushlightuserdata(l, this);
+ lua_settable(l, LUA_REGISTRYINDEX);
+
+ initSimulationAPI();
+ initInterfaceAPI();
+ initRendererAPI();
+ initElementsAPI();
+ initVirtualMachineAPI();
+ initGraphicsAPI();
+ initFileSystemAPI();
+
+ //Old TPT API
+ int i = 0, j;
+ char tmpname[12];
+ int currentElementMeta, currentElement;
+ const static struct luaL_reg tptluaapi [] = {
+ {"test", &luatpt_test},
+ {"drawtext", &luatpt_drawtext},
+ {"create", &luatpt_create},
+ {"set_pause", &luatpt_setpause},
+ {"toggle_pause", &luatpt_togglepause},
+ {"set_console", &luatpt_setconsole},
+ {"log", &luatpt_log},
+ {"set_pressure", &luatpt_set_pressure},
+ {"set_gravity", &luatpt_set_gravity},
+ {"reset_gravity_field", &luatpt_reset_gravity_field},
+ {"reset_velocity", &luatpt_reset_velocity},
+ {"reset_spark", &luatpt_reset_spark},
+ {"set_property", &luatpt_set_property},
+ {"get_property", &luatpt_get_property},
+ {"set_wallmap", &luatpt_set_wallmap},
+ {"get_wallmap", &luatpt_get_wallmap},
+ {"set_elecmap", &luatpt_set_elecmap},
+ {"get_elecmap", &luatpt_get_elecmap},
+ {"drawpixel", &luatpt_drawpixel},
+ {"drawrect", &luatpt_drawrect},
+ {"fillrect", &luatpt_fillrect},
+ {"drawline", &luatpt_drawline},
+ {"textwidth", &luatpt_textwidth},
+ {"get_name", &luatpt_get_name},
+ {"set_shortcuts", &luatpt_set_shortcuts},
+ {"delete", &luatpt_delete},
+ {"register_step", &luatpt_register_step},
+ {"unregister_step", &luatpt_unregister_step},
+ {"register_mouseclick", &luatpt_register_mouseclick},
+ {"unregister_mouseclick", &luatpt_unregister_mouseclick},
+ {"register_keypress", &luatpt_register_keypress},
+ {"unregister_keypress", &luatpt_unregister_keypress},
+ {"register_mouseevent", &luatpt_register_mouseclick},
+ {"unregister_mouseevent", &luatpt_unregister_mouseclick},
+ {"register_keyevent", &luatpt_register_keypress},
+ {"unregister_keyevent", &luatpt_unregister_keypress},
+ {"input", &luatpt_input},
+ {"message_box", &luatpt_message_box},
+ {"get_numOfParts", &luatpt_get_numOfParts},
+ {"start_getPartIndex", &luatpt_start_getPartIndex},
+ {"next_getPartIndex", &luatpt_next_getPartIndex},
+ {"getPartIndex", &luatpt_getPartIndex},
+ {"hud", &luatpt_hud},
+ {"newtonian_gravity", &luatpt_gravity},
+ {"ambient_heat", &luatpt_airheat},
+ {"active_menu", &luatpt_active_menu},
+ {"decorations_enable", &luatpt_decorations_enable},
+ {"display_mode", &luatpt_cmode_set},
+ {"throw_error", &luatpt_error},
+ {"heat", &luatpt_heat},
+ {"setfire", &luatpt_setfire},
+ {"setdebug", &luatpt_setdebug},
+ {"setfpscap",&luatpt_setfpscap},
+ {"getscript",&luatpt_getscript},
+ {"setwindowsize",&luatpt_setwindowsize},
+ {"watertest",&luatpt_togglewater},
+ {"screenshot",&luatpt_screenshot},
+ {"element",&luatpt_getelement},
+ {"element_func",&luatpt_element_func},
+ {"graphics_func",&luatpt_graphics_func},
+ {NULL,NULL}
+ };
+
+ luacon_mousedown = false;
+ luacon_mousebutton = 0;
+
+ luacon_currentCommand = &currentCommand;
+ luacon_lastError = &lastError;
+
+ //Replace print function with our screen logging thingy
+ lua_pushcfunction(l, luatpt_log);
+ lua_setglobal(l, "print");
+
+ //Register all tpt functions
+ luaL_register(l, "tpt", tptluaapi);
+
+ tptProperties = lua_gettop(l);
+
+ lua_pushinteger(l, 0);
+ lua_setfield(l, tptProperties, "mousex");
+ lua_pushinteger(l, 0);
+ lua_setfield(l, tptProperties, "mousey");
+ lua_pushinteger(l, 0);
+ lua_setfield(l, tptProperties, "selectedl");
+ lua_pushinteger(l, 0);
+ lua_setfield(l, tptProperties, "selectedr");
+
+ lua_newtable(l);
+ tptPropertiesVersion = lua_gettop(l);
+ lua_pushinteger(l, SAVE_VERSION);
+ lua_setfield(l, tptPropertiesVersion, "major");
+ lua_pushinteger(l, MINOR_VERSION);
+ lua_setfield(l, tptPropertiesVersion, "minor");
+ lua_pushinteger(l, BUILD_NUM);
+ lua_setfield(l, tptPropertiesVersion, "build");
+ lua_setfield(l, tptProperties, "version");
+
+ lua_sethook(l, &luacon_hook, LUA_MASKCOUNT, 200);
+#ifdef FFI
+ //LuaJIT's ffi gives us direct access to parts data, no need for nested metatables. HOWEVER, this is in no way safe, it's entirely possible for someone to try to read parts[-10]
+ lua_pushlightuserdata(l, parts);
+ lua_setfield(l, tptProperties, "partsdata");
+
+ luaL_dostring (l, "ffi = require(\"ffi\")\n\
+ffi.cdef[[\n\
+typedef struct { int type; int life, ctype; float x, y, vx, vy; float temp; float pavg[2]; int flags; int tmp; int tmp2; unsigned int dcolour; } particle;\n\
+]]\n\
+tpt.parts = ffi.cast(\"particle *\", tpt.partsdata)\n\
+ffi = nil\n\
+tpt.partsdata = nil");
+ //Since ffi is REALLY REALLY dangrous, we'll remove it from the environment completely (TODO)
+ //lua_pushstring(l, "parts");
+ //tptPartsCData = lua_gettable(l, tptProperties);
+#else
+ lua_newtable(l);
+ tptParts = lua_gettop(l);
+ lua_newtable(l);
+ tptPartsMeta = lua_gettop(l);
+ lua_pushcfunction(l, luacon_partswrite);
+ lua_setfield(l, tptPartsMeta, "__newindex");
+ lua_pushcfunction(l, luacon_partsread);
+ lua_setfield(l, tptPartsMeta, "__index");
+ lua_setmetatable(l, tptParts);
+ lua_setfield(l, tptProperties, "parts");
+
+ lua_newtable(l);
+ tptPart = lua_gettop(l);
+ lua_newtable(l);
+ tptPartMeta = lua_gettop(l);
+ lua_pushcfunction(l, luacon_partwrite);
+ lua_setfield(l, tptPartMeta, "__newindex");
+ lua_pushcfunction(l, luacon_partread);
+ lua_setfield(l, tptPartMeta, "__index");
+ lua_setmetatable(l, tptPart);
+
+ tptPart = luaL_ref(l, LUA_REGISTRYINDEX);
+#endif
+
+ lua_newtable(l);
+ tptElements = lua_gettop(l);
+ for(i = 1; i < PT_NUM; i++)
+ {
+ for(j = 0; j < strlen(luacon_sim->elements[i].Name); j++)
+ tmpname[j] = tolower(luacon_sim->elements[i].Name[j]);
+ tmpname[strlen(luacon_sim->elements[i].Name)] = 0;
+
+ lua_newtable(l);
+ currentElement = lua_gettop(l);
+ lua_pushinteger(l, i);
+ lua_setfield(l, currentElement, "id");
+
+ lua_newtable(l);
+ currentElementMeta = lua_gettop(l);
+ lua_pushcfunction(l, luacon_elementwrite);
+ lua_setfield(l, currentElementMeta, "__newindex");
+ lua_pushcfunction(l, luacon_elementread);
+ lua_setfield(l, currentElementMeta, "__index");
+ lua_setmetatable(l, currentElement);
+
+ lua_setfield(l, tptElements, tmpname);
+ }
+ lua_setfield(l, tptProperties, "el");
+
+ lua_newtable(l);
+ tptElementTransitions = lua_gettop(l);
+ for(i = 1; i < PT_NUM; i++)
+ {
+ for(j = 0; j < strlen(luacon_sim->elements[i].Name); j++)
+ tmpname[j] = tolower(luacon_sim->elements[i].Name[j]);
+ tmpname[strlen(luacon_sim->elements[i].Name)] = 0;
+
+ lua_newtable(l);
+ currentElement = lua_gettop(l);
+ lua_newtable(l);
+ currentElementMeta = lua_gettop(l);
+ lua_pushinteger(l, i);
+ lua_setfield(l, currentElement, "value");
+ lua_pushcfunction(l, luacon_transitionwrite);
+ lua_setfield(l, currentElementMeta, "__newindex");
+ lua_pushcfunction(l, luacon_transitionread);
+ lua_setfield(l, currentElementMeta, "__index");
+ lua_setmetatable(l, currentElement);
+
+ lua_setfield(l, tptElementTransitions, tmpname);
+ }
+ lua_setfield(l, tptProperties, "eltransition");
+
+ lua_el_func = (int*)calloc(PT_NUM, sizeof(int));
+ lua_el_mode = (int*)calloc(PT_NUM, sizeof(int));
+ lua_gr_func = (int*)calloc(PT_NUM, sizeof(int));
+ for(i = 0; i < PT_NUM; i++)
+ {
+ lua_el_mode[i] = 0;
+ }
+
+}
+
+void LuaScriptInterface::Init()
+{
+ if(Client::Ref().FileExists("autorun.lua"))
+ if(luacon_eval("dofile(\"autorun.lua\")"))
+ luacon_ci->Log(CommandInterface::LogError, luacon_geterror());
+}
+
+void LuaScriptInterface::SetWindow(ui::Window * window)
+{
+ Window = window;
+}
+
+//// Begin Interface API
+
+void LuaScriptInterface::initInterfaceAPI()
+{
+ struct luaL_reg interfaceAPIMethods [] = {
+ {"showWindow", interface_showWindow},
+ {"closeWindow", interface_closeWindow},
+ {"addComponent", interface_addComponent},
+ {NULL, NULL}
+ };
+ luaL_register(l, "interface", interfaceAPIMethods);
+
+ //Ren shortcut
+ lua_getglobal(l, "interface");
+ lua_setglobal(l, "ui");
+
+ Luna<LuaWindow>::Register(l);
+ Luna<LuaButton>::Register(l);
+ Luna<LuaLabel>::Register(l);
+ Luna<LuaTextbox>::Register(l);
+ Luna<LuaCheckbox>::Register(l);
+ Luna<LuaSlider>::Register(l);
+ Luna<LuaProgressBar>::Register(l);
+}
+
+int LuaScriptInterface::interface_addComponent(lua_State * l)
+{
+ void * luaComponent = NULL;
+ ui::Component * component = NULL;
+ if(luaComponent = Luna<LuaButton>::tryGet(l, 1))
+ component = Luna<LuaButton>::get(luaComponent)->GetComponent();
+ else if(luaComponent = Luna<LuaLabel>::tryGet(l, 1))
+ component = Luna<LuaLabel>::get(luaComponent)->GetComponent();
+ else if(luaComponent = Luna<LuaTextbox>::tryGet(l, 1))
+ component = Luna<LuaTextbox>::get(luaComponent)->GetComponent();
+ else if(luaComponent = Luna<LuaCheckbox>::tryGet(l, 1))
+ component = Luna<LuaCheckbox>::get(luaComponent)->GetComponent();
+ else if(luaComponent = Luna<LuaSlider>::tryGet(l, 1))
+ component = Luna<LuaSlider>::get(luaComponent)->GetComponent();
+ else if(luaComponent = Luna<LuaProgressBar>::tryGet(l, 1))
+ component = Luna<LuaProgressBar>::get(luaComponent)->GetComponent();
+ else
+ luaL_typerror(l, 1, "Component");
+ if(luacon_ci->Window && component)
+ luacon_ci->Window->AddComponent(component);
+ return 0;
+}
+
+int LuaScriptInterface::interface_showWindow(lua_State * l)
+{
+ LuaWindow * window = Luna<LuaWindow>::check(l, 1);
+ if(window && ui::Engine::Ref().GetWindow()!=window->GetWindow())
+ ui::Engine::Ref().ShowWindow(window->GetWindow());
+ return 0;
+}
+
+int LuaScriptInterface::interface_closeWindow(lua_State * l)
+{
+ LuaWindow * window = Luna<LuaWindow>::check(l, 1);
+ if(window && ui::Engine::Ref().GetWindow()==window->GetWindow())
+ ui::Engine::Ref().CloseWindow();
+ return 0;
+}
+
+//// Begin Simulation API
+
+void LuaScriptInterface::initSimulationAPI()
+{
+ //Methods
+ struct luaL_reg simulationAPIMethods [] = {
+ {"partNeighbours", simulation_partNeighbours},
+ {"partChangeType", simulation_partChangeType},
+ {"partCreate", simulation_partCreate},
+ {"partKill", simulation_partKill},
+ {NULL, NULL}
+ };
+ luaL_register(l, "simulation", simulationAPIMethods);
+ int simulationAPI = lua_gettop(l);
+
+ //Sim shortcut
+ lua_getglobal(l, "simulation");
+ lua_setglobal(l, "sim");
+
+}
+
+int LuaScriptInterface::simulation_partNeighbours(lua_State * l)
+{
+ int ids = 0;
+ if(lua_gettop(l) == 4)
+ {
+ int x = lua_tointeger(l, 1), y = lua_tointeger(l, 2), r = lua_tointeger(l, 3), t = lua_tointeger(l, 4), rx, ry, n;
+ for (rx = -r; rx <= r; rx++)
+ for (ry = -r; ry <= r; ry++)
+ if (x+rx >= 0 && y+ry >= 0 && x+rx < XRES && y+ry < YRES && (rx || ry))
+ {
+ n = luacon_sim->pmap[y+ry][x+rx];
+ if(n && (n&0xFF) == t)
+ {
+ ids++;
+ lua_pushinteger(l, n>>8);
+ }
+ }
+
+ }
+ else
+ {
+ int x = lua_tointeger(l, 1), y = lua_tointeger(l, 2), r = lua_tointeger(l, 3), rx, ry, n;
+ for (rx = -r; rx <= r; rx++)
+ for (ry = -r; ry <= r; ry++)
+ if (x+rx >= 0 && y+ry >= 0 && x+rx < XRES && y+ry < YRES && (rx || ry))
+ {
+ n = luacon_sim->pmap[y+ry][x+rx];
+ if(n)
+ {
+ ids++;
+ lua_pushinteger(l, n>>8);
+ }
+ }
+ }
+ return ids;
+}
+
+int LuaScriptInterface::simulation_partChangeType(lua_State * l)
+{
+ int partIndex = lua_tointeger(l, 1), x, y;
+ if(partIndex < 0 || partIndex >= NPART || !luacon_sim->parts[partIndex].type)
+ return 0;
+ luacon_sim->part_change_type(partIndex, luacon_sim->parts[partIndex].x+0.5f, luacon_sim->parts[partIndex].y+0.5f, lua_tointeger(l, 2));
+ return 0;
+}
+
+int LuaScriptInterface::simulation_partCreate(lua_State * l)
+{
+ int newID = lua_tointeger(l, 1);
+ if(newID >= NPART || newID < -3)
+ {
+ lua_pushinteger(l, -1);
+ return 1;
+ }
+ lua_pushinteger(l, luacon_sim->create_part(newID, lua_tointeger(l, 2), lua_tointeger(l, 3), lua_tointeger(l, 4)));
+ return 1;
+}
+
+int LuaScriptInterface::simulation_partKill(lua_State * l)
+{
+ if(lua_gettop(l)==2)
+ luacon_sim->delete_part(lua_tointeger(l, 1), lua_tointeger(l, 2), 0);
+ else
+ luacon_sim->kill_part(lua_tointeger(l, 1));
+ return 0;
+}
+
+
+//// Begin Renderer API
+
+void LuaScriptInterface::initRendererAPI()
+{
+ //Methods
+ struct luaL_reg rendererAPIMethods [] = {
+ {"renderModes", renderer_renderModes},
+ {"displayModes", renderer_displayModes},
+ {"colourMode", renderer_colourMode},
+ {"colorMode", renderer_colourMode}, //Duplicate of above to make americans happy
+ {"decorations", renderer_decorations},
+ {NULL, NULL}
+ };
+ luaL_register(l, "renderer", rendererAPIMethods);
+
+ //Ren shortcut
+ lua_getglobal(l, "renderer");
+ lua_setglobal(l, "ren");
+
+ int rendererAPI = lua_gettop(l);
+
+ //Static values
+ //Particle pixel modes/fire mode/effects
+ lua_pushinteger(l, PMODE); lua_setfield(l, rendererAPI, "PMODE");
+ lua_pushinteger(l, PMODE_NONE); lua_setfield(l, rendererAPI, "PMODE_NONE");
+ lua_pushinteger(l, PMODE_FLAT); lua_setfield(l, rendererAPI, "PMODE_FLAT");
+ lua_pushinteger(l, PMODE_BLOB); lua_setfield(l, rendererAPI, "PMODE_BLOB");
+ lua_pushinteger(l, PMODE_BLUR); lua_setfield(l, rendererAPI, "PMODE_BLUR");
+ lua_pushinteger(l, PMODE_GLOW); lua_setfield(l, rendererAPI, "PMODE_GLOW");
+ lua_pushinteger(l, PMODE_SPARK); lua_setfield(l, rendererAPI, "PMODE_SPARK");
+ lua_pushinteger(l, PMODE_FLARE); lua_setfield(l, rendererAPI, "PMODE_FLARE");
+ lua_pushinteger(l, PMODE_LFLARE); lua_setfield(l, rendererAPI, "PMODE_LFLARE");
+ lua_pushinteger(l, PMODE_ADD); lua_setfield(l, rendererAPI, "PMODE_ADD");
+ lua_pushinteger(l, PMODE_BLEND); lua_setfield(l, rendererAPI, "PMODE_BLEND");
+ lua_pushinteger(l, PSPEC_STICKMAN); lua_setfield(l, rendererAPI, "PSPEC_STICKMAN");
+ lua_pushinteger(l, OPTIONS); lua_setfield(l, rendererAPI, "OPTIONS");
+ lua_pushinteger(l, NO_DECO); lua_setfield(l, rendererAPI, "NO_DECO");
+ lua_pushinteger(l, DECO_FIRE); lua_setfield(l, rendererAPI, "DECO_FIRE");
+ lua_pushinteger(l, FIREMODE); lua_setfield(l, rendererAPI, "FIREMODE");
+ lua_pushinteger(l, FIRE_ADD); lua_setfield(l, rendererAPI, "FIRE_ADD");
+ lua_pushinteger(l, FIRE_BLEND); lua_setfield(l, rendererAPI, "FIRE_BLEND");
+ lua_pushinteger(l, EFFECT); lua_setfield(l, rendererAPI, "EFFECT");
+ lua_pushinteger(l, EFFECT_GRAVIN); lua_setfield(l, rendererAPI, "EFFECT_GRAVIN");
+ lua_pushinteger(l, EFFECT_GRAVOUT); lua_setfield(l, rendererAPI, "EFFECT_GRAVOUT");
+ lua_pushinteger(l, EFFECT_LINES); lua_setfield(l, rendererAPI, "EFFECT_LINES");
+ lua_pushinteger(l, EFFECT_DBGLINES); lua_setfield(l, rendererAPI, "EFFECT_DBGLINES");
+
+ //Display/Render/Colour modes
+ lua_pushinteger(l, RENDER_EFFE); lua_setfield(l, rendererAPI, "RENDER_EFFE");
+ lua_pushinteger(l, RENDER_FIRE); lua_setfield(l, rendererAPI, "RENDER_FIRE");
+ lua_pushinteger(l, RENDER_GLOW); lua_setfield(l, rendererAPI, "RENDER_GLOW");
+ lua_pushinteger(l, RENDER_BLUR); lua_setfield(l, rendererAPI, "RENDER_BLUR");
+ lua_pushinteger(l, RENDER_BLOB); lua_setfield(l, rendererAPI, "RENDER_BLOB");
+ lua_pushinteger(l, RENDER_BASC); lua_setfield(l, rendererAPI, "RENDER_BASC");
+ lua_pushinteger(l, RENDER_NONE); lua_setfield(l, rendererAPI, "RENDER_NONE");
+ lua_pushinteger(l, COLOUR_HEAT); lua_setfield(l, rendererAPI, "COLOUR_HEAT");
+ lua_pushinteger(l, COLOUR_LIFE); lua_setfield(l, rendererAPI, "COLOUR_LIFE");
+ lua_pushinteger(l, COLOUR_GRAD); lua_setfield(l, rendererAPI, "COLOUR_GRAD");
+ lua_pushinteger(l, COLOUR_BASC); lua_setfield(l, rendererAPI, "COLOUR_BASC");
+ lua_pushinteger(l, COLOUR_DEFAULT); lua_setfield(l, rendererAPI, "COLOUR_DEFAULT");
+ lua_pushinteger(l, DISPLAY_AIRC); lua_setfield(l, rendererAPI, "DISPLAY_AIRC");
+ lua_pushinteger(l, DISPLAY_AIRP); lua_setfield(l, rendererAPI, "DISPLAY_AIRP");
+ lua_pushinteger(l, DISPLAY_AIRV); lua_setfield(l, rendererAPI, "DISPLAY_AIRV");
+ lua_pushinteger(l, DISPLAY_AIRH); lua_setfield(l, rendererAPI, "DISPLAY_AIRH");
+ lua_pushinteger(l, DISPLAY_AIR); lua_setfield(l, rendererAPI, "DISPLAY_AIR");
+ lua_pushinteger(l, DISPLAY_WARP); lua_setfield(l, rendererAPI, "DISPLAY_WARP");
+ lua_pushinteger(l, DISPLAY_PERS); lua_setfield(l, rendererAPI, "DISPLAY_PERS");
+ lua_pushinteger(l, DISPLAY_EFFE); lua_setfield(l, rendererAPI, "DISPLAY_EFFE");
+}
+
+//get/set render modes list
+int LuaScriptInterface::renderer_renderModes(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ int size = 0;
+ luaL_checktype(l, 1, LUA_TTABLE);
+ size = luaL_getn(l, 1);
+
+ std::vector<unsigned int> renderModes;
+ for(int i = 1; i <= size; i++)
+ {
+ lua_rawgeti(l, 1, i);
+ renderModes.push_back(lua_tointeger(l, -1));
+ lua_pop(l, 1);
+ }
+ luacon_ren->SetRenderMode(renderModes);
+ return 0;
+ }
+ else
+ {
+ lua_newtable(l);
+ std::vector<unsigned int> renderModes = luacon_ren->GetRenderMode();
+ int i = 1;
+ for(std::vector<unsigned int>::iterator iter = renderModes.begin(), end = renderModes.end(); iter != end; ++iter)
+ {
+ lua_pushinteger(l, *iter);
+ lua_rawseti(l, -2, i++);
+ }
+ return 1;
+ }
+}
+
+int LuaScriptInterface::renderer_displayModes(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ int size = 0;
+ luaL_checktype(l, 1, LUA_TTABLE);
+ size = luaL_getn(l, 1);
+
+ std::vector<unsigned int> displayModes;
+ for(int i = 1; i <= size; i++)
+ {
+ lua_rawgeti(l, 1, i);
+ displayModes.push_back(lua_tointeger(l, -1));
+ lua_pop(l, 1);
+ }
+ luacon_ren->SetDisplayMode(displayModes);
+ return 0;
+ }
+ else
+ {
+ lua_newtable(l);
+ std::vector<unsigned int> displayModes = luacon_ren->GetDisplayMode();
+ int i = 1;
+ for(std::vector<unsigned int>::iterator iter = displayModes.begin(), end = displayModes.end(); iter != end; ++iter)
+ {
+ lua_pushinteger(l, *iter);
+ lua_rawseti(l, -2, i++);
+ }
+ return 1;
+ }
+}
+
+int LuaScriptInterface::renderer_colourMode(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TNUMBER);
+ luacon_ren->SetColourMode(lua_tointeger(l, 1));
+ return 0;
+ }
+ else
+ {
+ lua_pushinteger(l, luacon_ren->GetColourMode());
+ return 1;
+ }
+}
+
+int LuaScriptInterface::renderer_decorations(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luacon_ren->decorations_enable = lua_toboolean(l, 1);
+ return 0;
+ }
+ else
+ {
+ lua_pushboolean(l, luacon_ren->decorations_enable);
+ return 1;
+ }
+}
+
+void LuaScriptInterface::initElementsAPI()
+{
+ //Methods
+ struct luaL_reg elementsAPIMethods [] = {
+ {"allocate", elements_allocate},
+ {"element", elements_element},
+ {"property", elements_property},
+ {"free", elements_free},
+ {"loadDefault", elements_loadDefault},
+ {NULL, NULL}
+ };
+ luaL_register(l, "elements", elementsAPIMethods);
+
+ //elem shortcut
+ lua_getglobal(l, "elements");
+ lua_setglobal(l, "elem");
+
+ int elementsAPI = lua_gettop(l);
+
+ //Static values
+ //Element types/properties/states
+ lua_pushinteger(l, TYPE_PART); lua_setfield(l, elementsAPI, "TYPE_PART");
+ lua_pushinteger(l, TYPE_LIQUID); lua_setfield(l, elementsAPI, "TYPE_LIQUID");
+ lua_pushinteger(l, TYPE_SOLID); lua_setfield(l, elementsAPI, "TYPE_SOLID");
+ lua_pushinteger(l, TYPE_GAS); lua_setfield(l, elementsAPI, "TYPE_GAS");
+ lua_pushinteger(l, TYPE_ENERGY); lua_setfield(l, elementsAPI, "TYPE_ENERGY");
+ lua_pushinteger(l, PROP_CONDUCTS); lua_setfield(l, elementsAPI, "PROP_CONDUCTS");
+ lua_pushinteger(l, PROP_BLACK); lua_setfield(l, elementsAPI, "PROP_BLACK");
+ lua_pushinteger(l, PROP_NEUTPENETRATE); lua_setfield(l, elementsAPI, "PROP_NEUTPENETRATE");
+ lua_pushinteger(l, PROP_NEUTABSORB); lua_setfield(l, elementsAPI, "PROP_NEUTABSORB");
+ lua_pushinteger(l, PROP_NEUTPASS); lua_setfield(l, elementsAPI, "PROP_NEUTPASS");
+ lua_pushinteger(l, PROP_DEADLY); lua_setfield(l, elementsAPI, "PROP_DEADLY");
+ lua_pushinteger(l, PROP_HOT_GLOW); lua_setfield(l, elementsAPI, "PROP_HOT_GLOW");
+ lua_pushinteger(l, PROP_LIFE); lua_setfield(l, elementsAPI, "PROP_LIFE");
+ lua_pushinteger(l, PROP_RADIOACTIVE); lua_setfield(l, elementsAPI, "PROP_RADIOACTIVE");
+ lua_pushinteger(l, PROP_LIFE_DEC); lua_setfield(l, elementsAPI, "PROP_LIFE_DEC");
+ lua_pushinteger(l, PROP_LIFE_KILL); lua_setfield(l, elementsAPI, "PROP_LIFE_KILL");
+ lua_pushinteger(l, PROP_LIFE_KILL_DEC); lua_setfield(l, elementsAPI, "PROP_LIFE_KILL_DEC");
+ lua_pushinteger(l, PROP_SPARKSETTLE); lua_setfield(l, elementsAPI, "PROP_SPARKSETTLE");
+ lua_pushinteger(l, PROP_NOAMBHEAT); lua_setfield(l, elementsAPI, "PROP_NOAMBHEAT");
+ lua_pushinteger(l, FLAG_STAGNANT); lua_setfield(l, elementsAPI, "FLAG_STAGNANT");
+ lua_pushinteger(l, FLAG_SKIPMOVE); lua_setfield(l, elementsAPI, "FLAG_SKIPMOVE");
+ lua_pushinteger(l, FLAG_MOVABLE); lua_setfield(l, elementsAPI, "FLAG_MOVABLE");
+ lua_pushinteger(l, ST_NONE); lua_setfield(l, elementsAPI, "ST_NONE");
+ lua_pushinteger(l, ST_SOLID); lua_setfield(l, elementsAPI, "ST_SOLID");
+ lua_pushinteger(l, ST_LIQUID); lua_setfield(l, elementsAPI, "ST_LIQUID");
+ lua_pushinteger(l, ST_GAS); lua_setfield(l, elementsAPI, "ST_GAS");
+
+ lua_pushinteger(l, SC_WALL); lua_setfield(l, elementsAPI, "SC_WALL");
+ lua_pushinteger(l, SC_ELEC); lua_setfield(l, elementsAPI, "SC_ELEC");
+ lua_pushinteger(l, SC_POWERED); lua_setfield(l, elementsAPI, "SC_POWERED");
+ lua_pushinteger(l, SC_FORCE); lua_setfield(l, elementsAPI, "SC_FORCE");
+ lua_pushinteger(l, SC_EXPLOSIVE); lua_setfield(l, elementsAPI, "SC_EXPLOSIVE");
+ lua_pushinteger(l, SC_GAS); lua_setfield(l, elementsAPI, "SC_GAS");
+ lua_pushinteger(l, SC_LIQUID); lua_setfield(l, elementsAPI, "SC_LIQUID");
+ lua_pushinteger(l, SC_POWDERS); lua_setfield(l, elementsAPI, "SC_POWDERS");
+ lua_pushinteger(l, SC_SOLIDS); lua_setfield(l, elementsAPI, "SC_SOLIDS");
+ lua_pushinteger(l, SC_NUCLEAR); lua_setfield(l, elementsAPI, "SC_NUCLEAR");
+ lua_pushinteger(l, SC_SPECIAL); lua_setfield(l, elementsAPI, "SC_SPECIAL");
+ lua_pushinteger(l, SC_LIFE); lua_setfield(l, elementsAPI, "SC_LIFE");
+ lua_pushinteger(l, SC_TOOL); lua_setfield(l, elementsAPI, "SC_TOOL");
+ lua_pushinteger(l, SC_DECO); lua_setfield(l, elementsAPI, "SC_DECO");
+ lua_pushinteger(l, SC_SENSOR); lua_setfield(l, elementsAPI, "SC_SENSOR");
+
+ //Element identifiers
+ for(int i = 0; i < PT_NUM; i++)
+ {
+ if(luacon_sim->elements[i].Enabled)
+ {
+ lua_pushinteger(l, i);
+ lua_setfield(l, elementsAPI, luacon_sim->elements[i].Identifier);
+ }
+ }
+}
+
+pim::VirtualMachine * LuaScriptInterface::updateVirtualMachines[PT_NUM];
+
+int LuaScriptInterface::updateVM(UPDATE_FUNC_ARGS)
+{
+ pim::VirtualMachine * machine = updateVirtualMachines[parts[i].type];
+
+ machine->CSPush(i);
+ machine->CSPush(x);
+ machine->CSPush(y);
+ machine->Call(0);
+
+
+ /*vm::VirtualMachine * vMachine = updateVirtualMachines[parts[i].type];
+
+ vm::word w;
+ int argAddr = 0, argCount = 5;
+ vMachine->sim = sim;
+
+ vMachine->OpPUSH(w); //Pointless null in stack
+ w.int4 = (argCount + 2) * sizeof(vm::word);
+ vMachine->OpENTER(w);
+ argAddr = 8;
+
+ //Arguments
+ w.int4 = i; vMachine->Marshal(argAddr, w); argAddr += 4;
+ w.int4 = x; vMachine->Marshal(argAddr, w); argAddr += 4;
+ w.int4 = y; vMachine->Marshal(argAddr, w); argAddr += 4;
+ w.int4 = nt; vMachine->Marshal(argAddr, w); argAddr += 4;
+ w.int4 = surround_space; vMachine->Marshal(argAddr, w); argAddr += 4;
+
+ w.int4 = 0;
+ vMachine->Push(w);
+
+ vMachine->OpCALL(w);
+ vMachine->Run();
+ w.int4 = (argCount + 2) * sizeof(vm::word);
+ vMachine->OpLEAVE(w);
+ vMachine->OpPOP(w); //Pop pointless null
+ vMachine->End();*/
+
+ return 0;
+}
+
+int LuaScriptInterface::elements_loadDefault(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TNUMBER);
+ int id = lua_tointeger(l, 1);
+ if(id < 0 || id >= PT_NUM)
+ return luaL_error(l, "Invalid element");
+
+ lua_getglobal(l, "elements");
+ lua_pushnil(l);
+ lua_setfield(l, -2, luacon_sim->elements[id].Identifier);
+
+ std::vector<Element> elementList = GetElements();
+ if(id < elementList.size())
+ luacon_sim->elements[id] = elementList[id];
+ else
+ luacon_sim->elements[id] = Element();
+
+ lua_pushinteger(l, id);
+ lua_setfield(l, -2, luacon_sim->elements[id].Identifier);
+ lua_pop(l, 1);
+ }
+ else
+ {
+ std::vector<Element> elementList = GetElements();
+ for(int i = 0; i < PT_NUM; i++)
+ {
+ if(i < elementList.size())
+ luacon_sim->elements[i] = elementList[i];
+ else
+ luacon_sim->elements[i] = Element();
+ }
+ lua_pushnil(l);
+ lua_setglobal(l, "elements");
+ lua_pushnil(l);
+ lua_setglobal(l, "elem");
+
+ lua_getglobal(l, "package");
+ lua_getfield(l, -1, "loaded");
+ lua_pushnil(l);
+ lua_setfield(l, -2, "elements");
+
+ luacon_ci->initElementsAPI();
+ }
+
+ luacon_model->BuildMenus();
+ luacon_sim->init_can_move();
+ std::fill(luacon_ren->graphicscache, luacon_ren->graphicscache+PT_NUM, gcache_item());
+
+}
+
+int LuaScriptInterface::elements_allocate(lua_State * l)
+{
+ std::string group, id, identifier;
+ luaL_checktype(l, 1, LUA_TSTRING);
+ luaL_checktype(l, 2, LUA_TSTRING);
+ group = std::string(lua_tostring(l, 1));
+ std::transform(group.begin(), group.end(), group.begin(), ::toupper);
+ id = std::string(lua_tostring(l, 2));
+ std::transform(id.begin(), id.end(), id.begin(), ::toupper);
+
+ if(group == "DEFAULT")
+ return luaL_error(l, "You cannot create elements in the 'default' group.");
+
+ identifier = group + "_PT_" + id;
+
+ for(int i = 0; i < PT_NUM; i++)
+ {
+ if(luacon_sim->elements[i].Enabled && std::string(luacon_sim->elements[i].Identifier) == identifier)
+ return luaL_error(l, "Element identifier already in use");
+ }
+
+ int newID = -1;
+ for(int i = PT_NUM-1; i >= 0; i--)
+ {
+ if(!luacon_sim->elements[i].Enabled)
+ {
+ newID = i;
+ luacon_sim->elements[i] = Element();
+ luacon_sim->elements[i].Enabled = true;
+ luacon_sim->elements[i].Identifier = strdup(identifier.c_str());
+ break;
+ }
+ }
+
+ if(newID != -1)
+ {
+ lua_getglobal(l, "elements");
+ lua_pushinteger(l, newID);
+ lua_setfield(l, -2, identifier.c_str());
+ lua_pop(l, 1);
+ }
+
+ lua_pushinteger(l, newID);
+ return 1;
+}
+
+int LuaScriptInterface::elements_element(lua_State * l)
+{
+ int args = lua_gettop(l);
+ int id;
+ luaL_checktype(l, 1, LUA_TNUMBER);
+ id = lua_tointeger(l, 1);
+
+ if(id < 0 || id >= PT_NUM || !luacon_sim->elements[id].Enabled)
+ return luaL_error(l, "Invalid element");
+
+ if(args > 1)
+ {
+ luaL_checktype(l, 2, LUA_TTABLE);
+ std::vector<StructProperty> properties = Element::GetProperties();
+ //Write values from native data to a table
+ for(std::vector<StructProperty>::iterator iter = properties.begin(), end = properties.end(); iter != end; ++iter)
+ {
+ lua_getfield(l, -1, (*iter).Name.c_str());
+ if(lua_type(l, -1) != LUA_TNIL)
+ {
+ intptr_t offset = (*iter).Offset;
+ switch((*iter).Type)
+ {
+ case StructProperty::ParticleType:
+ case StructProperty::Integer:
+ *((int*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, -1);
+ break;
+ case StructProperty::UInteger:
+ *((unsigned int*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, -1);
+ break;
+ case StructProperty::Float:
+ *((float*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tonumber(l, -1);
+ break;
+ case StructProperty::Char:
+ *((char*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, -1);
+ break;
+ case StructProperty::UChar:
+ *((unsigned char*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, -1);
+ break;
+ case StructProperty::String:
+ *((char**)(((unsigned char*)&luacon_sim->elements[id])+offset)) = strdup(lua_tostring(l, -1));
+ break;
+ case StructProperty::Colour:
+#if PIXELSIZE == 4
+ *((unsigned int*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, -1);
+#else
+ *((unsigned short*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, -1);
+#endif
+ break;
+ }
+ lua_pop(l, 1);
+ }
+ }
+
+ lua_getfield(l, -1, "Update");
+ if(lua_type(l, -1) == LUA_TFUNCTION)
+ {
+ lua_el_func[id] = luaL_ref(l, LUA_REGISTRYINDEX);
+ luacon_sim->elements[id].Update = &luacon_elementReplacement;
+ }
+ else if(lua_type(l, -1) == LUA_TBOOLEAN && !lua_toboolean(l, -1))
+ {
+ lua_el_func[id] = 0;
+ luacon_sim->elements[id].Update = NULL;
+ }
+ else
+ lua_pop(l, 1);
+
+ lua_getfield(l, -1, "Graphics");
+ if(lua_type(l, -1) == LUA_TFUNCTION)
+ {
+ lua_el_func[id] = luaL_ref(l, LUA_REGISTRYINDEX);
+ luacon_sim->elements[id].Graphics = &luacon_graphicsReplacement;
+ }
+ else if(lua_type(l, -1) == LUA_TBOOLEAN && !lua_toboolean(l, -1))
+ {
+ lua_el_func[id] = 0;
+ luacon_sim->elements[id].Graphics = NULL;
+ }
+ else
+ lua_pop(l, 1);
+
+ luacon_model->BuildMenus();
+ luacon_sim->init_can_move();
+ std::fill(luacon_ren->graphicscache, luacon_ren->graphicscache+PT_NUM, gcache_item());
+
+ lua_pop(l, 1);
+ return 0;
+ }
+ else
+ {
+ std::vector<StructProperty> properties = Element::GetProperties();
+ //Write values from native data to a table
+ lua_newtable(l);
+ for(std::vector<StructProperty>::iterator iter = properties.begin(), end = properties.end(); iter != end; ++iter)
+ {
+ intptr_t offset = (*iter).Offset;
+ switch((*iter).Type)
+ {
+ case StructProperty::ParticleType:
+ case StructProperty::Integer:
+ lua_pushinteger(l, *((int*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::UInteger:
+ lua_pushinteger(l, *((unsigned int*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::Float:
+ lua_pushnumber(l, *((float*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::Char:
+ lua_pushinteger(l, *((char*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::UChar:
+ lua_pushinteger(l, *((unsigned char*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::String:
+ lua_pushstring(l, *((char**)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::Colour:
+#if PIXELSIZE == 4
+ lua_pushinteger(l, *((unsigned int*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+#else
+ lua_pushinteger(l, *((unsigned short*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+#endif
+ break;
+ default:
+ lua_pushnil(l);
+ }
+ lua_setfield(l, -2, (*iter).Name.c_str());
+ }
+ return 1;
+ }
+}
+
+int LuaScriptInterface::elements_property(lua_State * l)
+{
+ int args = lua_gettop(l);
+ int id;
+ std::string propertyName;
+ luaL_checktype(l, 1, LUA_TNUMBER);
+ id = lua_tointeger(l, 1);
+ luaL_checktype(l, 2, LUA_TSTRING);
+ propertyName = std::string(lua_tostring(l, 2));
+
+ if(id < 0 || id >= PT_NUM || !luacon_sim->elements[id].Enabled)
+ return luaL_error(l, "Invalid element");
+
+
+
+ if(args > 2)
+ {
+ StructProperty property;
+ bool propertyFound = false;
+ std::vector<StructProperty> properties = Element::GetProperties();
+
+ for(std::vector<StructProperty>::iterator iter = properties.begin(), end = properties.end(); iter != end; ++iter)
+ {
+ if((*iter).Name == propertyName)
+ {
+ property = *iter;
+ propertyFound = true;
+ break;
+ }
+ }
+
+ if(propertyFound)
+ {
+ if(lua_type(l, 3) != LUA_TNIL)
+ {
+ intptr_t offset = property.Offset;
+ switch(property.Type)
+ {
+ case StructProperty::ParticleType:
+ case StructProperty::Integer:
+ *((int*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, 3);
+ break;
+ case StructProperty::UInteger:
+ *((unsigned int*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, 3);
+ break;
+ case StructProperty::Float:
+ *((float*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tonumber(l, 3);
+ break;
+ case StructProperty::Char:
+ *((char*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, 3);
+ break;
+ case StructProperty::UChar:
+ *((unsigned char*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, 3);
+ break;
+ case StructProperty::String:
+ *((char**)(((unsigned char*)&luacon_sim->elements[id])+offset)) = strdup(lua_tostring(l, 3));
+ break;
+ case StructProperty::Colour:
+ #if PIXELSIZE == 4
+ *((unsigned int*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, 3);
+ #else
+ *((unsigned short*)(((unsigned char*)&luacon_sim->elements[id])+offset)) = lua_tointeger(l, 3);
+ #endif
+ break;
+ }
+ }
+
+ luacon_model->BuildMenus();
+ luacon_sim->init_can_move();
+ std::fill(luacon_ren->graphicscache, luacon_ren->graphicscache+PT_NUM, gcache_item());
+
+ return 0;
+ }
+ else if(propertyName == "Update")
+ {
+ if(lua_type(l, 3) == LUA_TFUNCTION)
+ {
+ lua_pushvalue(l, 3);
+ lua_el_func[id] = luaL_ref(l, LUA_REGISTRYINDEX);
+ luacon_sim->elements[id].Update = &luacon_elementReplacement;
+ }
+ else if(lua_type(l, 3) == LUA_TLIGHTUSERDATA)
+ {
+ updateVirtualMachines[id] = (pim::VirtualMachine*)lua_touserdata(l, 3);
+ luacon_sim->elements[id].Update = &updateVM;
+ }
+ else if(lua_type(l, 3) == LUA_TBOOLEAN && !lua_toboolean(l, 3))
+ {
+ lua_el_func[id] = 0;
+ luacon_sim->elements[id].Update = NULL;
+ }
+ }
+ else if(propertyName == "Graphics")
+ {
+ if(lua_type(l, 3) == LUA_TFUNCTION)
+ {
+ lua_pushvalue(l, 3);
+ lua_el_func[id] = luaL_ref(l, LUA_REGISTRYINDEX);
+ luacon_sim->elements[id].Graphics = &luacon_graphicsReplacement;
+ }
+ else if(lua_type(l, 3) == LUA_TBOOLEAN && !lua_toboolean(l, -1))
+ {
+ lua_el_func[id] = 0;
+ luacon_sim->elements[id].Graphics = NULL;
+ }
+ std::fill(luacon_ren->graphicscache, luacon_ren->graphicscache+PT_NUM, gcache_item());
+ }
+ else
+ return luaL_error(l, "Invalid element property");
+ }
+ else
+ {
+ StructProperty property;
+ bool propertyFound = false;
+ std::vector<StructProperty> properties = Element::GetProperties();
+
+ for(std::vector<StructProperty>::iterator iter = properties.begin(), end = properties.end(); iter != end; ++iter)
+ {
+ if((*iter).Name == propertyName)
+ {
+ property = *iter;
+ propertyFound = true;
+ break;
+ }
+ }
+
+ if(propertyFound)
+ {
+ intptr_t offset = property.Offset;
+ switch(property.Type)
+ {
+ case StructProperty::ParticleType:
+ case StructProperty::Integer:
+ lua_pushinteger(l, *((int*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::UInteger:
+ lua_pushinteger(l, *((unsigned int*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::Float:
+ lua_pushnumber(l, *((float*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::Char:
+ lua_pushinteger(l, *((char*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::UChar:
+ lua_pushinteger(l, *((unsigned char*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::String:
+ lua_pushstring(l, *((char**)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+ break;
+ case StructProperty::Colour:
+#if PIXELSIZE == 4
+ lua_pushinteger(l, *((unsigned int*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+#else
+ lua_pushinteger(l, *((unsigned short*)(((unsigned char*)&luacon_sim->elements[id])+offset)));
+#endif
+ break;
+ default:
+ lua_pushnil(l);
+ }
+ return 1;
+ }
+ else
+ return luaL_error(l, "Invalid element property");
+ }
+}
+
+int LuaScriptInterface::elements_free(lua_State * l)
+{
+ int id;
+ luaL_checktype(l, 1, LUA_TNUMBER);
+ id = lua_tointeger(l, 1);
+
+ if(id < 0 || id >= PT_NUM || !luacon_sim->elements[id].Enabled)
+ return luaL_error(l, "Invalid element");
+
+ std::string identifier = luacon_sim->elements[id].Identifier;
+ if(identifier.length()>7 && identifier.substr(0, 7) == "DEFAULT")
+ return luaL_error(l, "Cannot free default elements");
+
+ luacon_sim->elements[id].Enabled = false;
+
+ lua_getglobal(l, "elements");
+ lua_pushnil(l);
+ lua_setfield(l, -2, identifier.c_str());
+ lua_pop(l, 1);
+
+ return 0;
+}
+
+void LuaScriptInterface::initVirtualMachineAPI()
+{
+ //Methods
+ struct luaL_reg vmAPIMethods [] = {
+ {"loadProgram", virtualMachine_loadProgram},
+ {NULL, NULL}
+ };
+ luaL_register(l, "virtualMachine", vmAPIMethods);
+
+ //elem shortcut
+ lua_getglobal(l, "virtualMachine");
+ lua_setglobal(l, "vm");
+
+ int vmAPI = lua_gettop(l);
+}
+
+int LuaScriptInterface::virtualMachine_loadProgram(lua_State * l)
+{
+ /*luaL_checktype(l, 1, LUA_TSTRING);
+
+ vm::VirtualMachine * newVM = new vm::VirtualMachine(1);
+ try
+ {
+ const char * tempString = lua_tostring(l, 1);
+ int tempStringLength = lua_strlen(l, 1);
+ std::vector<char> programData(tempString, tempString+tempStringLength);
+ newVM->LoadProgram(programData);
+ }
+ catch(std::exception & e)
+ {
+ return luaL_error(l, "Unable to load program");
+ }
+ lua_pushlightuserdata(l, newVM);*/
+ std::string programSource(lua_tostring(l, 1));
+ std::stringstream input(programSource);
+
+
+ pim::compiler::Parser * parser = new pim::compiler::Parser(input);
+
+ std::vector<unsigned char> programData = parser->Compile();
+
+ pim::VirtualMachine * machine = new pim::VirtualMachine(luacon_sim);
+ machine->LoadProgram(programData);
+
+ lua_pushlightuserdata(l, machine);
+ return 1;
+}
+
+void LuaScriptInterface::initGraphicsAPI()
+{
+ //Methods
+ struct luaL_reg graphicsAPIMethods [] = {
+ {"textSize", graphics_textSize},
+ {"drawText", graphics_drawText},
+ {"drawLine", graphics_drawLine},
+ {"drawRect", graphics_drawRect},
+ {"fillRect", graphics_fillRect},
+ {NULL, NULL}
+ };
+ luaL_register(l, "graphics", graphicsAPIMethods);
+
+ //elem shortcut
+ lua_getglobal(l, "graphics");
+ lua_setglobal(l, "gfx");
+
+ int graphicsAPI = lua_gettop(l);
+
+ lua_pushinteger(l, XRES+BARSIZE); lua_setfield(l, graphicsAPI, "WIDTH");
+ lua_pushinteger(l, YRES+MENUSIZE); lua_setfield(l, graphicsAPI, "HEIGHT");
+}
+
+int LuaScriptInterface::graphics_textSize(lua_State * l)
+{
+ char * text;
+ int width, height;
+ text = (char*)lua_tostring(l, 1);
+ Graphics::textsize(text, width, height);
+
+ lua_pushinteger(l, width);
+ lua_pushinteger(l, height);
+ return 2;
+}
+
+int LuaScriptInterface::graphics_drawText(lua_State * l)
+{
+ char * text;
+ int x, y, r, g, b, a;
+ x = lua_tointeger(l, 1);
+ y = lua_tointeger(l, 2);
+ text = (char*)lua_tostring(l, 3);
+ r = luaL_optint(l, 4, 255);
+ g = luaL_optint(l, 5, 255);
+ b = luaL_optint(l, 6, 255);
+ a = luaL_optint(l, 7, 255);
+
+ if (r<0) r = 0;
+ if (r>255) r = 255;
+ if (g<0) g = 0;
+ if (g>255) g = 255;
+ if (b<0) b = 0;
+ if (b>255) b = 255;
+ if (a<0) a = 0;
+ if (a>255) a = 255;
+
+ luacon_g->drawtext(x, y, text, r, g, b, a);
+ return 0;
+}
+
+int LuaScriptInterface::graphics_drawLine(lua_State * l)
+{
+ int x1, y1, x2, y2, r, g, b, a;
+ x1 = lua_tointeger(l, 1);
+ y1 = lua_tointeger(l, 2);
+ x2 = lua_tointeger(l, 3);
+ y2 = lua_tointeger(l, 4);
+ r = luaL_optint(l, 5, 255);
+ g = luaL_optint(l, 6, 255);
+ b = luaL_optint(l, 7, 255);
+ a = luaL_optint(l, 8, 255);
+
+ if (r<0) r = 0;
+ if (r>255) r = 255;
+ if (g<0) g = 0;
+ if (g>255) g = 255;
+ if (b<0) b = 0;
+ if (b>255) b = 255;
+ if (a<0) a = 0;
+ if (a>255) a = 255;
+ luacon_g->draw_line(x1, y1, x2, y2, r, g, b, a);
+ return 0;
+}
+
+int LuaScriptInterface::graphics_drawRect(lua_State * l)
+{
+ int x, y, w, h, r, g, b, a;
+ x = lua_tointeger(l, 1);
+ y = lua_tointeger(l, 2);
+ w = lua_tointeger(l, 3);
+ h = lua_tointeger(l, 4);
+ r = luaL_optint(l, 5, 255);
+ g = luaL_optint(l, 6, 255);
+ b = luaL_optint(l, 7, 255);
+ a = luaL_optint(l, 8, 255);
+
+ if (r<0) r = 0;
+ if (r>255) r = 255;
+ if (g<0) g = 0;
+ if (g>255) g = 255;
+ if (b<0) b = 0;
+ if (b>255) b = 255;
+ if (a<0) a = 0;
+ if (a>255) a = 255;
+ luacon_g->drawrect(x, y, w, h, r, g, b, a);
+ return 0;
+}
+
+int LuaScriptInterface::graphics_fillRect(lua_State * l)
+{
+ int x, y, w, h, r, g, b, a;
+ x = lua_tointeger(l, 1);
+ y = lua_tointeger(l, 2);
+ w = lua_tointeger(l, 3);
+ h = lua_tointeger(l, 4);
+ r = luaL_optint(l, 5, 255);
+ g = luaL_optint(l, 6, 255);
+ b = luaL_optint(l, 7, 255);
+ a = luaL_optint(l, 8, 255);
+
+ if (r<0) r = 0;
+ if (r>255) r = 255;
+ if (g<0) g = 0;
+ if (g>255) g = 255;
+ if (b<0) b = 0;
+ if (b>255) b = 255;
+ if (a<0) a = 0;
+ if (a>255) a = 255;
+ luacon_g->fillrect(x, y, w, h, r, g, b, a);
+ return 0;
+}
+
+void LuaScriptInterface::initFileSystemAPI()
+{
+ //Methods
+ struct luaL_reg fileSystemAPIMethods [] = {
+ {"list", fileSystem_list},
+ {"exists", fileSystem_exists},
+ {"isFile", fileSystem_isFile},
+ {"isDirectory", fileSystem_isDirectory},
+ {"makeDirectory", fileSystem_makeDirectory},
+ {"removeDirectory", fileSystem_removeDirectory},
+ {"removeFile", fileSystem_removeFile},
+ {"move", fileSystem_move},
+ {"copy", fileSystem_copy},
+ {NULL, NULL}
+ };
+ luaL_register(l, "fileSystem", fileSystemAPIMethods);
+
+ //elem shortcut
+ lua_getglobal(l, "fileSystem");
+ lua_setglobal(l, "fs");
+
+ int fileSystemAPI = lua_gettop(l);
+}
+
+int LuaScriptInterface::fileSystem_list(lua_State * l)
+{
+ const char * directoryName = lua_tostring(l, 1);
+
+ int index = 1;
+ lua_newtable(l);
+
+ DIR * directory;
+ struct dirent * entry;
+
+ directory = opendir(directoryName);
+ if (directory != NULL)
+ {
+ while (entry = readdir(directory))
+ {
+ if(strncmp(entry->d_name, "..", 3) && strncmp(entry->d_name, ".", 2))
+ {
+ lua_pushstring(l, entry->d_name);
+ lua_rawseti(l, -2, index++);
+ }
+ }
+ closedir(directory);
+ }
+ else
+ {
+ lua_pushnil(l);
+ }
+
+ return 1;
+}
+
+int LuaScriptInterface::fileSystem_exists(lua_State * l)
+{
+ const char * filename = lua_tostring(l, 1);
+
+ bool exists = false;
+#ifdef WIN
+ struct _stat s;
+ if(_stat(filename, &s) == 0)
+#else
+ struct stat s;
+ if(stat(filename, &s) == 0)
+#endif
+ {
+ if(s.st_mode & S_IFDIR)
+ {
+ exists = true;
+ }
+ else if(s.st_mode & S_IFREG)
+ {
+ exists = true;
+ }
+ else
+ {
+ exists = true;
+ }
+ }
+ else
+ {
+ exists = false;
+ }
+
+ lua_pushboolean(l, exists);
+ return 1;
+}
+
+int LuaScriptInterface::fileSystem_isFile(lua_State * l)
+{
+ const char * filename = lua_tostring(l, 1);
+
+ bool exists = false;
+#ifdef WIN
+ struct _stat s;
+ if(_stat(filename, &s) == 0)
+#else
+ struct stat s;
+ if(stat(filename, &s) == 0)
+#endif
+ {
+ if(s.st_mode & S_IFDIR)
+ {
+ exists = true;
+ }
+ else if(s.st_mode & S_IFREG)
+ {
+ exists = false;
+ }
+ else
+ {
+ exists = false;
+ }
+ }
+ else
+ {
+ exists = false;
+ }
+
+ lua_pushboolean(l, exists);
+ return 1;
+}
+
+int LuaScriptInterface::fileSystem_isDirectory(lua_State * l)
+{
+ const char * filename = lua_tostring(l, 1);
+
+ bool exists = false;
+#ifdef WIN
+ struct _stat s;
+ if(_stat(filename, &s) == 0)
+#else
+ struct stat s;
+ if(stat(filename, &s) == 0)
+#endif
+ {
+ if(s.st_mode & S_IFDIR)
+ {
+ exists = false;
+ }
+ else if(s.st_mode & S_IFREG)
+ {
+ exists = true;
+ }
+ else
+ {
+ exists = false;
+ }
+ }
+ else
+ {
+ exists = false;
+ }
+
+ lua_pushboolean(l, exists);
+ return 1;
+}
+
+int LuaScriptInterface::fileSystem_makeDirectory(lua_State * l)
+{
+ const char * dirname = lua_tostring(l, 1);
+
+ int ret = 0;
+ ret = Client::Ref().MakeDirectory(dirname);
+ lua_pushboolean(l, ret == 0);
+ return 1;
+}
+
+int LuaScriptInterface::fileSystem_removeDirectory(lua_State * l)
+{
+ const char * filename = lua_tostring(l, 1);
+
+ int ret = 0;
+#ifdef WIN
+ ret = _rmdir(filename);
+#else
+ ret = rmdir(filename);
+#endif
+ lua_pushboolean(l, ret == 0);
+ return 1;
+}
+
+int LuaScriptInterface::fileSystem_removeFile(lua_State * l)
+{
+ const char * filename = lua_tostring(l, 1);
+
+ int ret = 0;
+#ifdef WIN
+ ret = _unlink(filename);
+#else
+ ret = unlink(filename);
+#endif
+ lua_pushboolean(l, ret == 0);
+ return 1;
+}
+
+int LuaScriptInterface::fileSystem_move(lua_State * l)
+{
+ const char * filename = lua_tostring(l, 1);
+ const char * newFilename = lua_tostring(l, 2);
+ int ret = 0;
+
+ ret = rename(filename, newFilename);
+
+ lua_pushboolean(l, ret == 0);
+ return 1;
+}
+
+int LuaScriptInterface::fileSystem_copy(lua_State * l)
+{
+ const char * filename = lua_tostring(l, 1);
+ const char * newFilename = lua_tostring(l, 2);
+ int ret = 0;
+
+ try
+ {
+ std::ifstream source(filename, std::ios::binary);
+ std::ofstream dest(newFilename, std::ios::binary);
+ source.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+ dest.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+
+ std::istreambuf_iterator<char> begin_source(source);
+ std::istreambuf_iterator<char> end_source;
+ std::ostreambuf_iterator<char> begin_dest(dest);
+ std::copy(begin_source, end_source, begin_dest);
+
+ source.close();
+ dest.close();
+
+ ret = 0;
+ }
+ catch (std::exception & e)
+ {
+ ret = 1;
+ }
+
+ lua_pushboolean(l, ret == 0);
+ return 1;
+}
+
+
+bool LuaScriptInterface::OnBrushChanged(int brushType, int rx, int ry)
+{
+ luacon_brushx = rx;
+ luacon_brushy = ry;
+ return true;
+}
+
+bool LuaScriptInterface::OnMouseMove(int x, int y, int dx, int dy)
+{
+ luacon_mousex = x;
+ luacon_mousey = y;
+ return true;
+}
+
+bool LuaScriptInterface::OnMouseDown(int x, int y, unsigned button)
+{
+ luacon_mousedown = true;
+ luacon_mousebutton = button;
+ return luacon_mouseevent(x, y, button, LUACON_MDOWN, 0);
+}
+
+bool LuaScriptInterface::OnMouseUp(int x, int y, unsigned button)
+{
+ luacon_mousedown = false;
+ return luacon_mouseevent(x, y, button, LUACON_MUP, 0);
+}
+
+bool LuaScriptInterface::OnMouseWheel(int x, int y, int d)
+{
+ return luacon_mouseevent(x, y, luacon_mousedown?luacon_mousebutton:0, 0, d);
+}
+
+bool LuaScriptInterface::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ int modifiers = 0;
+ if(shift)
+ modifiers |= 0x001;
+ if(ctrl)
+ modifiers |= 0x040;
+ if(alt)
+ modifiers |= 0x100;
+ return luacon_keyevent(key, modifiers, LUACON_KDOWN);
+}
+
+bool LuaScriptInterface::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ int modifiers = 0;
+ if(shift)
+ modifiers |= 0x001;
+ if(ctrl)
+ modifiers |= 0x040;
+ if(alt)
+ modifiers |= 0x100;
+ return luacon_keyevent(key, modifiers, LUACON_KUP);
+}
+
+void LuaScriptInterface::OnTick()
+{
+ ui::Engine::Ref().LastTick(clock());
+ if(luacon_mousedown)
+ luacon_mouseevent(luacon_mousex, luacon_mousey, luacon_mousebutton, LUACON_MPRESS, 0);
+ luacon_step(luacon_mousex, luacon_mousey, luacon_selectedl, luacon_selectedr, luacon_brushx, luacon_brushy);
+}
+
+int LuaScriptInterface::Command(std::string command)
+{
+ if(command[0] == '!')
+ {
+ lastError = "";
+ int ret = legacy->Command(command.substr(1));
+ lastError = legacy->GetLastError();
+ return ret;
+ }
+ else
+ {
+ int ret;
+ lastError = "";
+ currentCommand = true;
+ ui::Engine::Ref().LastTick(clock());
+ if((ret = luaL_dostring(l, command.c_str())))
+ {
+ lastError = luacon_geterror();
+ //Log(LogError, lastError);
+ }
+ currentCommand = false;
+ return ret;
+ }
+}
+
+std::string LuaScriptInterface::FormatCommand(std::string command)
+{
+ if(command[0] == '!')
+ {
+ return "!"+legacy->FormatCommand(command.substr(1));
+ }
+ else
+ return command;
+}
+
+LuaScriptInterface::~LuaScriptInterface() {
+ delete legacy;
+}
diff --git a/src/cat/LuaScriptInterface.h b/src/cat/LuaScriptInterface.h
new file mode 100644
index 0000000..bf7d277
--- /dev/null
+++ b/src/cat/LuaScriptInterface.h
@@ -0,0 +1,128 @@
+/*
+ * LuaScriptInterface.h
+ *
+ * Created on: Feb 11, 2012
+ * Author: Simon
+ */
+
+#ifndef LUASCRIPTINTERFACE_H_
+#define LUASCRIPTINTERFACE_H_
+
+extern "C"
+{
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include "CommandInterface.h"
+#include "simulation/Simulation.h"
+
+namespace ui
+{
+ class Window;
+}
+
+namespace pim
+{
+ class VirtualMachine;
+}
+
+
+//Because lua only has bindings for C, we're going to have to go outside "outside" the LuaScriptInterface, this means we can only have one instance :(
+
+#define LOCAL_LUA_DIR "Lua"
+
+#define LUACON_MDOWN 1
+#define LUACON_MUP 2
+#define LUACON_MPRESS 3
+#define LUACON_KDOWN 1
+#define LUACON_KUP 2
+
+//Bitmasks for things that might need recalculating after changes to tpt.el
+#define LUACON_EL_MODIFIED_CANMOVE 0x1
+#define LUACON_EL_MODIFIED_GRAPHICS 0x2
+#define LUACON_EL_MODIFIED_MENUS 0x4
+
+class TPTScriptInterface;
+class LuaScriptInterface: public CommandInterface
+{
+ int luacon_mousex, luacon_mousey, luacon_selectedl, luacon_selectedr, luacon_mousebutton, luacon_brushx, luacon_brushy;
+ bool luacon_mousedown;
+ bool currentCommand;
+ TPTScriptInterface * legacy;
+
+ //Simulation
+ void initSimulationAPI();
+ static int simulation_partNeighbours(lua_State * l);
+ static int simulation_partChangeType(lua_State * l);
+ static int simulation_partCreate(lua_State * l);
+ static int simulation_partKill(lua_State * l);
+
+ //Renderer
+ void initRendererAPI();
+ static int renderer_renderModes(lua_State * l);
+ static int renderer_displayModes(lua_State * l);
+ static int renderer_colourMode(lua_State * l);
+ static int renderer_decorations(lua_State * l);
+
+ //Elements
+ static pim::VirtualMachine * updateVirtualMachines[PT_NUM];
+ static int updateVM(UPDATE_FUNC_ARGS);
+ //
+ void initElementsAPI();
+ static int elements_allocate(lua_State * l);
+ static int elements_element(lua_State * l);
+ static int elements_property(lua_State * l);
+ static int elements_loadDefault(lua_State * l);
+ static int elements_free(lua_State * l);
+
+ //Interface
+ void initInterfaceAPI();
+ static int interface_showWindow(lua_State * l);
+ static int interface_closeWindow(lua_State * l);
+ static int interface_addComponent(lua_State * l);
+
+ //VM
+ void initVirtualMachineAPI();
+ static int virtualMachine_loadProgram(lua_State * l);
+
+ void initGraphicsAPI();
+ static int graphics_textSize(lua_State * l);
+ static int graphics_drawText(lua_State * l);
+ static int graphics_drawLine(lua_State * l);
+ static int graphics_drawRect(lua_State * l);
+ static int graphics_fillRect(lua_State * l);
+
+ void initFileSystemAPI();
+ static int fileSystem_list(lua_State * l);
+ static int fileSystem_exists(lua_State * l);
+ static int fileSystem_isFile(lua_State * l);
+ static int fileSystem_isDirectory(lua_State * l);
+ static int fileSystem_makeDirectory(lua_State * l);
+ static int fileSystem_removeDirectory(lua_State * l);
+ static int fileSystem_removeFile(lua_State * l);
+ static int fileSystem_move(lua_State * l);
+ static int fileSystem_copy(lua_State * l);
+
+public:
+ ui::Window * Window;
+ lua_State *l;
+ LuaScriptInterface(GameController * c, GameModel * m);
+ virtual bool OnBrushChanged(int brushType, int rx, int ry);
+ virtual bool OnMouseMove(int x, int y, int dx, int dy);
+ virtual bool OnMouseDown(int x, int y, unsigned button);
+ virtual bool OnMouseUp(int x, int y, unsigned button);
+ virtual bool OnMouseWheel(int x, int y, int d);
+ virtual bool OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual bool OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual void OnTick();
+ virtual void Init();
+ virtual void SetWindow(ui::Window * window);
+ virtual int Command(std::string command);
+ virtual std::string FormatCommand(std::string command);
+ virtual ~LuaScriptInterface();
+};
+
+
+#endif /* LUASCRIPTINTERFACE_H_ */
diff --git a/src/cat/LuaSlider.cpp b/src/cat/LuaSlider.cpp
new file mode 100644
index 0000000..0fedbfb
--- /dev/null
+++ b/src/cat/LuaSlider.cpp
@@ -0,0 +1,112 @@
+extern "C"
+{
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include <iostream>
+#include "LuaSlider.h"
+#include "LuaScriptInterface.h"
+#include "interface/Slider.h"
+
+const char LuaSlider::className[] = "Slider";
+
+#define method(class, name) {#name, &class::name}
+Luna<LuaSlider>::RegType LuaSlider::methods[] = {
+ method(LuaSlider, onValueChanged),
+ method(LuaSlider, position),
+ method(LuaSlider, size),
+ method(LuaSlider, visible),
+ method(LuaSlider, value),
+ method(LuaSlider, steps),
+ {0, 0}
+};
+
+LuaSlider::LuaSlider(lua_State * l) :
+ LuaComponent(l),
+ onValueChangedFunction(0)
+{
+ int posX = luaL_optinteger(l, 1, 0);
+ int posY = luaL_optinteger(l, 2, 0);
+ int sizeX = luaL_optinteger(l, 3, 10);
+ int sizeY = luaL_optinteger(l, 4, 10);
+ int steps = luaL_optinteger(l, 5, 10);
+
+ slider = new ui::Slider(ui::Point(posX, posY), ui::Point(sizeX, sizeY), steps);
+ component = slider;
+ class ValueAction : public ui::SliderAction
+ {
+ LuaSlider * luaSlider;
+ public:
+ ValueAction(LuaSlider * luaSlider) : luaSlider(luaSlider) {}
+ void ValueChangedCallback(ui::Slider * sender)
+ {
+ luaSlider->triggerOnValueChanged();
+ }
+ };
+ slider->SetActionCallback(new ValueAction(this));
+}
+
+int LuaSlider::steps(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ slider->SetSteps(lua_tointeger(l, 1));
+ return 0;
+ }
+ else
+ {
+ lua_pushinteger(l, slider->GetSteps());
+ return 1;
+ }
+}
+
+int LuaSlider::onValueChanged(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onValueChangedFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onValueChangedFunction = 0;
+ }
+ return 0;
+}
+
+int LuaSlider::value(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ slider->SetValue(lua_tointeger(l, 1));
+ return 0;
+ }
+ else
+ {
+ lua_pushinteger(l, slider->GetValue());
+ return 1;
+ }
+}
+
+void LuaSlider::triggerOnValueChanged()
+{
+ if(onValueChangedFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onValueChangedFunction);
+ lua_rawgeti(l, LUA_REGISTRYINDEX, UserData);
+ lua_pushinteger(l, slider->GetValue());
+ if (lua_pcall(l, 2, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+LuaSlider::~LuaSlider()
+{
+} \ No newline at end of file
diff --git a/src/cat/LuaSlider.h b/src/cat/LuaSlider.h
new file mode 100644
index 0000000..f9f327e
--- /dev/null
+++ b/src/cat/LuaSlider.h
@@ -0,0 +1,33 @@
+#pragma once
+
+extern "C" {
+ #include "lua.h"
+ #include "lauxlib.h"
+ #include "lualib.h"
+}
+
+#include "LuaLuna.h"
+#include "LuaComponent.h"
+
+namespace ui
+{
+ class Slider;
+}
+
+class LuaScriptInterface;
+
+class LuaSlider: public LuaComponent
+{
+ ui::Slider * slider;
+ int onValueChangedFunction;
+ void triggerOnValueChanged();
+ int onValueChanged(lua_State * l);
+ int steps(lua_State * l);
+ int value(lua_State * l);
+public:
+ static const char className[];
+ static Luna<LuaSlider>::RegType methods[];
+
+ LuaSlider(lua_State * l);
+ ~LuaSlider();
+}; \ No newline at end of file
diff --git a/src/cat/LuaTextbox.cpp b/src/cat/LuaTextbox.cpp
new file mode 100644
index 0000000..fa5753c
--- /dev/null
+++ b/src/cat/LuaTextbox.cpp
@@ -0,0 +1,116 @@
+extern "C"
+{
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include <iostream>
+#include "LuaScriptInterface.h"
+#include "LuaTextbox.h"
+#include "interface/Textbox.h"
+
+const char LuaTextbox::className[] = "Textbox";
+
+#define method(class, name) {#name, &class::name}
+Luna<LuaTextbox>::RegType LuaTextbox::methods[] = {
+ method(LuaTextbox, text),
+ method(LuaTextbox, readonly),
+ method(LuaTextbox, onTextChanged),
+ method(LuaTextbox, position),
+ method(LuaTextbox, size),
+ method(LuaTextbox, visible),
+ {0, 0}
+};
+
+LuaTextbox::LuaTextbox(lua_State * l) :
+ LuaComponent(l),
+ onTextChangedFunction(0)
+{
+ this->l = l;
+ int posX = luaL_optinteger(l, 1, 0);
+ int posY = luaL_optinteger(l, 2, 0);
+ int sizeX = luaL_optinteger(l, 3, 10);
+ int sizeY = luaL_optinteger(l, 4, 10);
+ std::string text = luaL_optstring(l, 5, "");
+ std::string placeholder = luaL_optstring(l, 6, "");
+
+ textbox = new ui::Textbox(ui::Point(posX, posY), ui::Point(sizeX, sizeY), text, placeholder);
+ textbox->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ class TextChangedAction : public ui::TextboxAction
+ {
+ LuaTextbox * t;
+ public:
+ TextChangedAction(LuaTextbox * t) : t(t) {}
+ void TextChangedCallback(ui::Textbox * sender)
+ {
+ t->triggerOnTextChanged();
+ }
+ };
+ textbox->SetActionCallback(new TextChangedAction(this));
+ component = textbox;
+}
+
+int LuaTextbox::readonly(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TBOOLEAN);
+ textbox->ReadOnly = lua_toboolean(l, 1);
+ return 0;
+ }
+ else
+ {
+ lua_pushboolean(l, textbox->ReadOnly);
+ return 1;
+ }
+}
+
+int LuaTextbox::onTextChanged(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onTextChangedFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onTextChangedFunction = 0;
+ }
+ return 0;
+}
+
+void LuaTextbox::triggerOnTextChanged()
+{
+ if(onTextChangedFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onTextChangedFunction);
+ lua_rawgeti(l, LUA_REGISTRYINDEX, UserData);
+ if (lua_pcall(l, 1, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+int LuaTextbox::text(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TSTRING);
+ textbox->SetText(lua_tostring(l, 1));
+ return 0;
+ }
+ else
+ {
+ lua_pushstring(l, textbox->GetText().c_str());
+ return 1;
+ }
+}
+
+LuaTextbox::~LuaTextbox()
+{
+} \ No newline at end of file
diff --git a/src/cat/LuaTextbox.h b/src/cat/LuaTextbox.h
new file mode 100644
index 0000000..437875f
--- /dev/null
+++ b/src/cat/LuaTextbox.h
@@ -0,0 +1,33 @@
+#pragma once
+
+extern "C" {
+ #include "lua.h"
+ #include "lauxlib.h"
+ #include "lualib.h"
+}
+
+#include "LuaLuna.h"
+#include "LuaComponent.h"
+
+namespace ui
+{
+ class Textbox;
+}
+
+class LuaScriptInterface;
+
+class LuaTextbox: public LuaComponent
+{
+ int onTextChangedFunction;
+ ui::Textbox * textbox;
+ int text(lua_State * l);
+ int readonly(lua_State * l);
+ int onTextChanged(lua_State * l);
+ void triggerOnTextChanged();
+public:
+ static const char className[];
+ static Luna<LuaTextbox>::RegType methods[];
+
+ LuaTextbox(lua_State * l);
+ ~LuaTextbox();
+}; \ No newline at end of file
diff --git a/src/cat/LuaWindow.cpp b/src/cat/LuaWindow.cpp
new file mode 100644
index 0000000..433e4d1
--- /dev/null
+++ b/src/cat/LuaWindow.cpp
@@ -0,0 +1,569 @@
+extern "C"
+{
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include <iostream>
+#include "LuaScriptInterface.h"
+#include "LuaWindow.h"
+#include "LuaButton.h"
+#include "LuaLabel.h"
+#include "LuaTextbox.h"
+#include "LuaCheckbox.h"
+#include "LuaSlider.h"
+#include "LuaProgressBar.h"
+#include "interface/Button.h"
+#include "interface/Label.h"
+#include "interface/Window.h"
+
+const char LuaWindow::className[] = "Window";
+
+#define method(class, name) {#name, &class::name}
+Luna<LuaWindow>::RegType LuaWindow::methods[] = {
+ method(LuaWindow, position),
+ method(LuaWindow, size),
+ method(LuaWindow, addComponent),
+ method(LuaWindow, onInitialized),
+ method(LuaWindow, onExit),
+ method(LuaWindow, onTick),
+ method(LuaWindow, onDraw),
+ method(LuaWindow, onFocus),
+ method(LuaWindow, onBlur),
+ method(LuaWindow, onTryExit),
+ method(LuaWindow, onTryOkay),
+ method(LuaWindow, onMouseMove),
+ method(LuaWindow, onMouseDown),
+ method(LuaWindow, onMouseUp),
+ method(LuaWindow, onMouseWheel),
+ method(LuaWindow, onKeyPress),
+ method(LuaWindow, onKeyRelease),
+ {0, 0}
+};
+
+LuaWindow::LuaWindow(lua_State * l) :
+ onInitializedFunction(0),
+ onExitFunction(0),
+ onTickFunction(0),
+ onDrawFunction(0),
+ onFocusFunction(0),
+ onBlurFunction(0),
+ onTryExitFunction(0),
+ onTryOkayFunction(0),
+ onMouseMoveFunction(0),
+ onMouseDownFunction(0),
+ onMouseUpFunction(0),
+ onMouseWheelFunction(0),
+ onKeyPressFunction(0),
+ onKeyReleaseFunction(0)
+{
+ this->l = l;
+ int posX = luaL_optinteger(l, 1, 0);
+ int posY = luaL_optinteger(l, 2, 0);
+ int sizeX = luaL_optinteger(l, 3, 10);
+ int sizeY = luaL_optinteger(l, 4, 10);
+
+ lua_pushstring(l, "Luacon_ci");
+ lua_gettable(l, LUA_REGISTRYINDEX);
+ ci = (LuaScriptInterface*)lua_touserdata(l, -1);
+ lua_pop(l, 1);
+
+ class DrawnWindow : public ui::Window
+ {
+ LuaWindow * luaWindow;
+ public:
+ DrawnWindow(ui::Point position, ui::Point size, LuaWindow * luaWindow) : ui::Window(position, size), luaWindow(luaWindow) {}
+ virtual void OnDraw()
+ {
+ Graphics * g = ui::Engine::Ref().g;
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+4, Size.Y+4);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
+ luaWindow->triggerOnDraw();
+ }
+ virtual void OnInitialized() { luaWindow->triggerOnInitialized(); }
+ virtual void OnExit() { luaWindow->triggerOnExit(); }
+ virtual void OnTick(float dt) { luaWindow->triggerOnTick( dt); }
+ virtual void OnFocus() { luaWindow->triggerOnFocus(); }
+ virtual void OnBlur() { luaWindow->triggerOnBlur(); }
+ virtual void OnTryExit(ExitMethod) { luaWindow->triggerOnTryExit(); }
+ virtual void OnTryOkay(OkayMethod) { luaWindow->triggerOnTryOkay(); }
+ virtual void OnMouseMove(int x, int y, int dx, int dy) { luaWindow->triggerOnMouseMove(x, y, dx, dy); }
+ virtual void OnMouseDown(int x, int y, unsigned button) { luaWindow->triggerOnMouseDown(x, y, button); }
+ virtual void OnMouseUp(int x, int y, unsigned button) { luaWindow->triggerOnMouseUp(x, y, button); }
+ virtual void OnMouseWheel(int x, int y, int d) { luaWindow->triggerOnMouseWheel(x, y, d); }
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) { luaWindow->triggerOnKeyPress(key, character, shift, ctrl, alt); }
+ virtual void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) { luaWindow->triggerOnKeyRelease(key, character, shift, ctrl, alt); }
+ };
+
+ window = new DrawnWindow(ui::Point(posX, posY), ui::Point(sizeX, sizeY), this);
+}
+
+int LuaWindow::addComponent(lua_State * l)
+{
+ void * luaComponent = NULL;
+ ui::Component * component = NULL;
+ if(luaComponent = Luna<LuaButton>::tryGet(l, 1))
+ component = Luna<LuaButton>::get(luaComponent)->GetComponent();
+ else if(luaComponent = Luna<LuaLabel>::tryGet(l, 1))
+ component = Luna<LuaLabel>::get(luaComponent)->GetComponent();
+ else if(luaComponent = Luna<LuaTextbox>::tryGet(l, 1))
+ component = Luna<LuaTextbox>::get(luaComponent)->GetComponent();
+ else if(luaComponent = Luna<LuaCheckbox>::tryGet(l, 1))
+ component = Luna<LuaCheckbox>::get(luaComponent)->GetComponent();
+ else if(luaComponent = Luna<LuaSlider>::tryGet(l, 1))
+ component = Luna<LuaSlider>::get(luaComponent)->GetComponent();
+ else if(luaComponent = Luna<LuaProgressBar>::tryGet(l, 1))
+ component = Luna<LuaProgressBar>::get(luaComponent)->GetComponent();
+ else
+ luaL_typerror(l, 1, "Component");
+ if(component)
+ window->AddComponent(component);
+ return 0;
+}
+
+int LuaWindow::position(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TNUMBER);
+ luaL_checktype(l, 2, LUA_TNUMBER);
+ window->Position = ui::Point(lua_tointeger(l, 1), lua_tointeger(l, 2));
+ return 0;
+ }
+ else
+ {
+ lua_pushinteger(l, window->Position.X);
+ lua_pushinteger(l, window->Position.Y);
+ return 2;
+ }
+}
+
+int LuaWindow::size(lua_State * l)
+{
+ int args = lua_gettop(l);
+ if(args)
+ {
+ luaL_checktype(l, 1, LUA_TNUMBER);
+ luaL_checktype(l, 2, LUA_TNUMBER);
+ window->Size = ui::Point(lua_tointeger(l, 1), lua_tointeger(l, 2));
+ return 0;
+ }
+ else
+ {
+ lua_pushinteger(l, window->Size.X);
+ lua_pushinteger(l, window->Size.Y);
+ return 2;
+ }
+}
+
+void LuaWindow::triggerOnInitialized()
+{
+ if(onInitializedFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onInitializedFunction);
+ if(lua_pcall(l, 0, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnExit()
+{
+ if(onExitFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onExitFunction);
+ if(lua_pcall(l, 0, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnTick(float dt)
+{
+ if(onTickFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onTickFunction);
+ lua_pushnumber(l, dt);
+ if(lua_pcall(l, 1, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnDraw()
+{
+ if(onDrawFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onDrawFunction);
+ if(lua_pcall(l, 0, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnFocus()
+{
+ if(onFocusFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onFocusFunction);
+ if(lua_pcall(l, 0, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnBlur()
+{
+ if(onBlurFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onBlurFunction);
+ if(lua_pcall(l, 0, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnTryExit()
+{
+ if(onTryExitFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onTryExitFunction);
+ if(lua_pcall(l, 0, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnTryOkay()
+{
+ if(onTryOkayFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onTryOkayFunction);
+ if(lua_pcall(l, 0, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnMouseMove(int x, int y, int dx, int dy)
+{
+ if(onMouseMoveFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onMouseMoveFunction);
+ lua_pushinteger(l, x);
+ lua_pushinteger(l, y);
+ lua_pushinteger(l, dx);
+ lua_pushinteger(l, dy);
+ if(lua_pcall(l, 4, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnMouseDown(int x, int y, unsigned button)
+{
+ if(onMouseDownFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onMouseDownFunction);
+ lua_pushinteger(l, x);
+ lua_pushinteger(l, y);
+ lua_pushinteger(l, button);
+ if(lua_pcall(l, 3, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnMouseUp(int x, int y, unsigned button)
+{
+ if(onMouseUpFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onMouseUpFunction);
+ lua_pushinteger(l, x);
+ lua_pushinteger(l, y);
+ lua_pushinteger(l, button);
+ if(lua_pcall(l, 3, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnMouseWheel(int x, int y, int d)
+{
+ if(onMouseWheelFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onMouseWheelFunction);
+ lua_pushinteger(l, x);
+ lua_pushinteger(l, y);
+ lua_pushinteger(l, d);
+ if(lua_pcall(l, 3, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(onKeyPressFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onKeyPressFunction);
+ lua_pushinteger(l, key);
+ lua_pushinteger(l, character);
+ lua_pushboolean(l, shift);
+ lua_pushboolean(l, ctrl);
+ lua_pushboolean(l, alt);
+ if(lua_pcall(l, 5, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+void LuaWindow::triggerOnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(onKeyReleaseFunction)
+ {
+ lua_rawgeti(l, LUA_REGISTRYINDEX, onKeyReleaseFunction);
+ lua_pushinteger(l, key);
+ lua_pushinteger(l, character);
+ lua_pushboolean(l, shift);
+ lua_pushboolean(l, ctrl);
+ lua_pushboolean(l, alt);
+ if(lua_pcall(l, 5, 0, 0))
+ {
+ ci->Log(CommandInterface::LogError, lua_tostring(l, -1));
+ }
+ }
+}
+
+int LuaWindow::onInitialized(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onInitializedFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onInitializedFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onExit(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onExitFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onExitFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onTick(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onTickFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onTickFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onDraw(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onDrawFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onDrawFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onFocus(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onFocusFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onFocusFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onBlur(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onBlurFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onBlurFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onTryExit(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onTryExitFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onTryExitFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onTryOkay(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onTryOkayFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onTryOkayFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onMouseMove(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onMouseMoveFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onMouseMoveFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onMouseDown(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onMouseDownFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onMouseDownFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onMouseUp(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onMouseUpFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onMouseUpFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onMouseWheel(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onMouseWheelFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onMouseWheelFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onKeyPress(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onKeyPressFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onKeyPressFunction = 0;
+ }
+ return 0;
+}
+
+int LuaWindow::onKeyRelease(lua_State * l)
+{
+ if(lua_type(l, 1) != LUA_TNIL)
+ {
+ luaL_checktype(l, 1, LUA_TFUNCTION);
+ lua_pushvalue(l, 1);
+ onKeyReleaseFunction = luaL_ref(l, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ onKeyReleaseFunction = 0;
+ }
+ return 0;
+}
+
+
+LuaWindow::~LuaWindow()
+{
+ if(ui::Engine::Ref().GetWindow() == window)
+ ui::Engine::Ref().CloseWindow();
+ delete window;
+} \ No newline at end of file
diff --git a/src/cat/LuaWindow.h b/src/cat/LuaWindow.h
new file mode 100644
index 0000000..be6af92
--- /dev/null
+++ b/src/cat/LuaWindow.h
@@ -0,0 +1,81 @@
+#pragma once
+
+extern "C" {
+ #include "lua.h"
+ #include "lauxlib.h"
+ #include "lualib.h"
+}
+
+#include "LuaLuna.h"
+
+#include "interface/Platform.h"
+namespace ui
+{
+ class Window;
+}
+
+class LuaScriptInterface;
+class LuaWindow
+{
+ int onInitializedFunction;
+ int onExitFunction;
+ int onTickFunction;
+ int onDrawFunction;
+ int onFocusFunction;
+ int onBlurFunction;
+ int onTryExitFunction;
+ int onTryOkayFunction;
+ int onMouseMoveFunction;
+ int onMouseDownFunction;
+ int onMouseUpFunction;
+ int onMouseWheelFunction;
+ int onKeyPressFunction;
+ int onKeyReleaseFunction;
+
+ ui::Window * window;
+ lua_State * l;
+ int position(lua_State * l);
+ int size(lua_State * l);
+ int addComponent(lua_State * l);
+
+ //Set event handlers
+ int onInitialized(lua_State * l);
+ int onExit(lua_State * l);
+ int onTick(lua_State * l);
+ int onDraw(lua_State * l);
+ int onFocus(lua_State * l);
+ int onBlur(lua_State * l);
+ int onTryExit(lua_State * l);
+ int onTryOkay(lua_State * l);
+ int onMouseMove(lua_State * l);
+ int onMouseDown(lua_State * l);
+ int onMouseUp(lua_State * l);
+ int onMouseWheel(lua_State * l);
+ int onKeyPress(lua_State * l);
+ int onKeyRelease(lua_State * l);
+
+ void triggerOnInitialized();
+ void triggerOnExit();
+ void triggerOnTick(float deltaTime);
+ void triggerOnDraw();
+ void triggerOnFocus();
+ void triggerOnBlur();
+ void triggerOnTryExit();
+ void triggerOnTryOkay();
+ void triggerOnMouseMove(int x, int y, int dx, int dy);
+ void triggerOnMouseDown(int x, int y, unsigned button);
+ void triggerOnMouseUp(int x, int y, unsigned button);
+ void triggerOnMouseWheel(int x, int y, int d);
+ void triggerOnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ void triggerOnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+
+public:
+ LuaScriptInterface * ci;
+ int UserData;
+ static const char className[];
+ static Luna<LuaWindow>::RegType methods[];
+
+ ui::Window * GetWindow() { return window; }
+ LuaWindow(lua_State * l);
+ ~LuaWindow();
+}; \ No newline at end of file
diff --git a/src/cat/TPTSTypes.cpp b/src/cat/TPTSTypes.cpp
new file mode 100644
index 0000000..a8fa962
--- /dev/null
+++ b/src/cat/TPTSTypes.cpp
@@ -0,0 +1,113 @@
+/*
+ * TPTSTypes.cpp
+ *
+ * Created on: Feb 4, 2012
+ * Author: Simon
+ */
+
+#include <iostream>
+#include <sstream>
+#include <stdint.h>
+#include "TPTSTypes.h"
+
+AnyType::AnyType(ValueType type_, void * value_):
+ type(type_),
+ value(value_)
+{
+}
+
+ValueType AnyType::GetType()
+{
+ return type;
+}
+
+AnyType::AnyType(const AnyType & v):
+ type(v.type),
+ value(v.value)
+{
+ if(type == TypeString)
+ {
+ value = new std::string(*((std::string*)value));
+ }
+ else if(type == TypePoint)
+ {
+ value = new ui::Point(*((ui::Point*)value));
+ }
+}
+
+AnyType::operator NumberType()
+{
+ if(type != TypeNumber)
+ throw InvalidConversionException(type, TypeNumber);
+ else
+ return NumberType((intptr_t)value);
+}
+
+AnyType::operator StringType()
+{
+ if(type == TypeNumber)
+ {
+ std::stringstream numberStream;
+ numberStream << ((NumberType*)this)->Value();
+ return StringType(numberStream.str());
+ }
+ else if(type == TypeString && value)
+ {
+ return StringType(*((std::string*)value));
+ }
+ else
+ throw InvalidConversionException(type, TypeString);
+
+}
+
+AnyType::operator PointType()
+{
+ if(type == TypePoint)
+ {
+ return PointType(*((ui::Point*)value));
+ }
+ else if(type == TypeString)
+ {
+ ui::Point thisPoint = *((ui::Point*)value);
+ std::stringstream pointStream;
+ pointStream << thisPoint.X << "," << thisPoint.Y;
+ return StringType(pointStream.str());
+ }
+ else
+ throw InvalidConversionException(type, TypePoint);
+}
+
+AnyType::~AnyType()
+{
+ if(type == TypeString || type == TypePoint)
+ delete value;
+}
+
+//Number Type
+
+NumberType::NumberType(int number): AnyType(TypeNumber, (void*)number) { }
+
+int NumberType::Value()
+{
+ return (intptr_t)value;
+}
+
+//String type
+
+StringType::StringType(std::string string): AnyType(TypeString, new std::string(string)) { }
+
+std::string StringType::Value()
+{
+ return std::string(*((std::string*)value));
+}
+
+//Point type
+
+PointType::PointType(ui::Point point): AnyType(TypePoint, new ui::Point(point)) { }
+
+PointType::PointType(int pointX, int pointY): AnyType(TypePoint, new ui::Point(pointX, pointY)) { }
+
+ui::Point PointType::Value()
+{
+ return ui::Point(*((ui::Point*)value));
+}
diff --git a/src/cat/TPTSTypes.h b/src/cat/TPTSTypes.h
new file mode 100644
index 0000000..dfea425
--- /dev/null
+++ b/src/cat/TPTSTypes.h
@@ -0,0 +1,119 @@
+/*
+ * TPTSTypes.h
+ *
+ * Created on: Feb 4, 2012
+ * Author: Simon
+ */
+
+#ifndef TPTSTYPES_H_
+#define TPTSTYPES_H_
+
+#include <string>
+#include <typeinfo>
+#include "interface/Point.h"
+
+enum ValueType { TypeNumber, TypePoint, TypeString, TypeNull, TypeFunction };
+
+class GeneralException
+{
+protected:
+ std::string exception;
+public:
+ GeneralException(std::string message){
+ exception = message;
+ }
+ std::string GetExceptionMessage() {
+ return exception;
+ }
+};
+
+
+class NumberType;
+class StringType;
+class PointType;
+
+class AnyType
+{
+protected:
+ ValueType type;
+ void * value;
+public:
+ AnyType(ValueType type_, void * value_);
+ AnyType(const AnyType & v);
+ operator NumberType();
+ operator StringType();
+ operator PointType();
+ ValueType GetType();
+ std::string TypeName()
+ {
+ switch(type)
+ {
+ case TypeNumber:
+ return "Number";
+ case TypePoint:
+ return "Point";
+ case TypeString:
+ return "String";
+ case TypeNull:
+ return "Null";
+ case TypeFunction:
+ return "Function";
+ default:
+ return "Unknown";
+ }
+ }
+ static std::string TypeName(ValueType type)
+ {
+ switch(type)
+ {
+ case TypeNumber:
+ return "Number";
+ case TypePoint:
+ return "Point";
+ case TypeString:
+ return "String";
+ case TypeNull:
+ return "Null";
+ case TypeFunction:
+ return "Function";
+ default:
+ return "Unknown";
+ }
+ }
+ ~AnyType();
+};
+
+class InvalidConversionException: public GeneralException
+{
+private:
+ ValueType from;
+ ValueType to;
+public:
+ InvalidConversionException(ValueType from_, ValueType to_):
+ GeneralException("Invalid conversion from " + AnyType::TypeName(from_) + " to " + AnyType::TypeName(to_)), from(from_), to(to_) {
+ }
+};
+
+class NumberType: public AnyType
+{
+public:
+ NumberType(int number);
+ int Value();
+};
+
+class StringType: public AnyType
+{
+public:
+ StringType(std::string string);
+ std::string Value();
+};
+
+class PointType: public AnyType
+{
+public:
+ PointType(ui::Point point);
+ PointType(int pointX, int pointY);
+ ui::Point Value();
+};
+
+#endif /* TPTSTYPES_H_ */
diff --git a/src/cat/TPTScriptInterface.cpp b/src/cat/TPTScriptInterface.cpp
new file mode 100644
index 0000000..684467f
--- /dev/null
+++ b/src/cat/TPTScriptInterface.cpp
@@ -0,0 +1,488 @@
+/*
+ * TPTScriptInterface.cpp
+ *
+ * Created on: Feb 5, 2012
+ * Author: Simon
+ */
+
+#include <stack>
+#include <iostream>
+#include <string>
+#include <deque>
+#include <string.h>
+#include <stdlib.h>
+#include "TPTScriptInterface.h"
+#include "game/GameModel.h"
+#include "simulation/Air.h"
+
+TPTScriptInterface::TPTScriptInterface(GameController * c, GameModel * m): CommandInterface(c, m)
+{
+}
+
+int TPTScriptInterface::Command(std::string command)
+{
+ lastError = "";
+ std::deque<std::string> words;
+ std::deque<AnyType> commandWords;
+ int retCode;
+
+ //Split command into words, put them on the stack
+ char * rawCommand;
+ rawCommand = (char*)calloc(command.length()+1, 1);
+ memcpy(rawCommand, (char*)command.c_str(), command.length());
+ char * currentWord = rawCommand;
+ char * currentCommand = rawCommand;
+ while((currentCommand = strchr(currentCommand, ' ')))
+ {
+ currentCommand[0] = 0;
+ words.push_back(std::string(currentWord));
+ currentWord = ++currentCommand;
+ }
+ words.push_back(std::string(currentWord));
+ while(!words.empty())
+ {
+ try
+ {
+ commandWords.push_back(eval(&words));
+ }
+ catch (GeneralException & e)
+ {
+ retCode = -1;
+ lastError = e.GetExceptionMessage();
+ break;
+ }
+ }
+ free(rawCommand);
+ if(commandWords.size())
+ {
+ retCode = 0;
+ lastError = ((StringType)commandWords.front()).Value();
+ }
+
+ //Evaluate
+ return 0;
+}
+
+ValueType TPTScriptInterface::testType(std::string word)
+{
+ int i = 0;
+ char * rawWord = (char *)word.c_str();
+ //Function
+ if(word == "set")
+ return TypeFunction;
+ else if(word == "create")
+ return TypeFunction;
+ else if(word == "delete")
+ return TypeFunction;
+ else if(word == "kill")
+ return TypeFunction;
+ else if(word == "load")
+ return TypeFunction;
+ else if(word == "reset")
+ return TypeFunction;
+ else if(word == "bubble")
+ return TypeFunction;
+ else if(word == "quit")
+ return TypeFunction;
+ //Basic type
+ parseNumber:
+ for(i = 0; i < word.length(); i++)
+ {
+ if(!(rawWord[i] >= '0' && rawWord[i] <= '9') && rawWord[i] != '.' && !(rawWord[i] == '-' && !i))
+ {
+ if(rawWord[i] == ',' && rawWord[i+1] >= '0' && rawWord[i+1] <= '9')
+ goto parsePoint;
+ else
+ goto parseString;
+ }
+ }
+ return TypeNumber;
+ parsePoint:
+ i++;
+ for(; i < word.length(); i++)
+ if(!(rawWord[i] >= '0' && rawWord[i] <= '9'))
+ {
+ goto parseString;
+ }
+ return TypePoint;
+ parseString:
+ return TypeString;
+}
+
+AnyType TPTScriptInterface::eval(std::deque<std::string> * words)
+{
+ if(words->size() < 1)
+ return AnyType(TypeNull, NULL);
+ std::string word = words->front(); words->pop_front();
+ char * rawWord = (char *)word.c_str();
+ ValueType wordType = testType(word);
+ switch(wordType)
+ {
+ case TypeFunction:
+ if(word == "set")
+ return tptS_set(words);
+ else if(word == "create")
+ return tptS_create(words);
+ else if(word == "delete" || word == "kill")
+ return tptS_delete(words);
+ else if(word == "load")
+ return tptS_load(words);
+ else if(word == "reset")
+ return tptS_reset(words);
+ else if(word == "bubble")
+ return tptS_bubble(words);
+ else if(word == "quit")
+ return tptS_quit(words);
+ break;
+ case TypeNumber:
+ return NumberType(atoi(rawWord));
+ case TypePoint:
+ {
+ int pointX, pointY;
+ sscanf(rawWord, "%d,%d", &pointX, &pointY);
+ return PointType(pointX, pointY);
+ }
+ case TypeString:
+ return StringType(word);
+ }
+}
+
+std::string TPTScriptInterface::FormatCommand(std::string command)
+{
+ std::deque<std::string> words;
+ std::deque<AnyType> commandWords;
+ std::string outputData;
+
+ //Split command into words, put them on the stack
+ char * rawCommand;
+ rawCommand = (char*)calloc(command.length()+1, 1);
+ memcpy(rawCommand, (char*)command.c_str(), command.length());
+ char * currentWord = rawCommand;
+ char * currentCommand = rawCommand;
+ while((currentCommand = strchr(currentCommand, ' ')))
+ {
+ currentCommand[0] = 0;
+ words.push_back(std::string(currentWord));
+ currentWord = ++currentCommand;
+ }
+ words.push_back(std::string(currentWord));
+ free(rawCommand);
+ while(!words.empty())
+ {
+ ValueType cType = testType(words.front());
+ switch(cType)
+ {
+ case TypeFunction:
+ outputData += "\bt";
+ break;
+ case TypeNumber:
+ case TypePoint:
+ outputData += "\bo";
+ break;
+ case TypeString:
+ outputData += "\bg";
+ break;
+ default:
+ outputData += "\bw";
+ break;
+ }
+ outputData += words.front() + " ";
+ words.pop_front();
+ }
+ return outputData;
+}
+
+AnyType TPTScriptInterface::tptS_set(std::deque<std::string> * words)
+{
+ //Arguments from stack
+ StringType property = eval(words);
+ AnyType selector = eval(words);
+ AnyType value = eval(words);
+
+ Simulation * sim = m->GetSimulation();
+ unsigned char * partsBlock = (unsigned char*)&sim->parts[0];
+
+ int returnValue = 0;
+
+ FormatType propertyFormat;
+ int propertyOffset = GetPropertyOffset(property.Value(), propertyFormat);
+
+ if(propertyOffset==-1)
+ throw GeneralException("Invalid property");
+
+ //Selector
+ int newValue;
+ if(value.GetType() == TypeNumber)
+ newValue = ((NumberType)value).Value();
+ else if(value.GetType() == TypeString)
+ {
+ newValue = GetParticleType(((StringType)value).Value());
+ if (newValue < 0 || newValue >= PT_NUM)
+ {
+ if (((StringType)value).Value() == "GOLD" || ((StringType)value).Value() == "gold")
+ throw GeneralException("No, GOLD will not be an element");
+ else
+ throw GeneralException("Invalid element");
+ }
+ }
+ else
+ throw GeneralException("Invalid value for assignment");
+ if (property.Value() == "type" && (newValue < 0 || newValue >= PT_NUM))
+ throw GeneralException("Invalid element");
+
+ if(selector.GetType() == TypePoint || selector.GetType() == TypeNumber)
+ {
+ int partIndex = -1;
+ if(selector.GetType() == TypePoint)
+ {
+ ui::Point tempPoint = ((PointType)selector).Value();
+ if(tempPoint.X<0 || tempPoint.Y<0 || tempPoint.Y >= YRES || tempPoint.X >= XRES)
+ throw GeneralException("Invalid position");
+
+ }
+ else
+ partIndex = ((NumberType)selector).Value();
+ if(partIndex<0 || partIndex>NPART || sim->parts[partIndex].type==0)
+ throw GeneralException("Invalid particle");
+
+ switch(propertyFormat)
+ {
+ case FormatInt:
+ *((int*)(partsBlock+(partIndex*sizeof(Particle))+propertyOffset)) = newValue;
+ break;
+ case FormatFloat:
+ *((float*)(partsBlock+(partIndex*sizeof(Particle))+propertyOffset)) = newValue;
+ break;
+ }
+ returnValue = 1;
+ }
+ else if(selector.GetType() == TypeString && ((StringType)selector).Value() == "all")
+ {
+ switch(propertyFormat)
+ {
+ case FormatInt:
+ {
+ for(int j = 0; j < NPART; j++)
+ if(sim->parts[j].type)
+ {
+ returnValue++;
+ *((int*)(partsBlock+(j*sizeof(Particle))+propertyOffset)) = newValue;
+ }
+ }
+ break;
+ case FormatFloat:
+ {
+ for(int j = 0; j < NPART; j++)
+ if(sim->parts[j].type)
+ {
+ returnValue++;
+ *((float*)(partsBlock+(j*sizeof(Particle))+propertyOffset)) = newValue;
+ }
+ }
+ break;
+ }
+ }
+ else if(selector.GetType() == TypeString || selector.GetType() == TypeNumber)
+ {
+ int type;
+ if(selector.GetType() == TypeNumber)
+ type = ((NumberType)selector).Value();
+ else if(selector.GetType() == TypeString)
+ type = GetParticleType(((StringType)selector).Value());
+
+ if(type<0 || type>=PT_NUM)
+ throw GeneralException("Invalid particle type");
+ std::cout << propertyOffset << std::endl;
+ switch(propertyFormat)
+ {
+ case FormatInt:
+ {
+ for(int j = 0; j < NPART; j++)
+ if(sim->parts[j].type == type)
+ {
+ returnValue++;
+ *((int*)(partsBlock+(j*sizeof(Particle))+propertyOffset)) = newValue;
+ }
+ }
+ break;
+ case FormatFloat:
+ {
+ for(int j = 0; j < NPART; j++)
+ if(sim->parts[j].type == type)
+ {
+ returnValue++;
+ *((float*)(partsBlock+(j*sizeof(Particle))+propertyOffset)) = newValue;
+ }
+ }
+ break;
+ }
+ }
+ else
+ throw GeneralException("Invalid selector");
+ return NumberType(returnValue);
+}
+
+AnyType TPTScriptInterface::tptS_create(std::deque<std::string> * words)
+{
+ //Arguments from stack
+ AnyType createType = eval(words);
+ PointType position = eval(words);
+
+ Simulation * sim = m->GetSimulation();
+
+ int type;
+ if(createType.GetType() == TypeNumber)
+ type = ((NumberType)createType).Value();
+ else if(createType.GetType() == TypeString)
+ type = GetParticleType(((StringType)createType).Value());
+ else
+ throw GeneralException("Invalid type");
+
+ if(type == -1)
+ throw GeneralException("Invalid particle type");
+
+ ui::Point tempPoint = position.Value();
+ if(tempPoint.X<0 || tempPoint.Y<0 || tempPoint.Y >= YRES || tempPoint.X >= XRES)
+ throw GeneralException("Invalid position");
+
+ int returnValue = sim->create_part(-1, tempPoint.X, tempPoint.Y, type);
+
+ return NumberType(returnValue);
+}
+
+AnyType TPTScriptInterface::tptS_delete(std::deque<std::string> * words)
+{
+ //Arguments from stack
+ AnyType partRef = eval(words);
+
+ Simulation * sim = m->GetSimulation();
+
+ if(partRef.GetType() == TypePoint)
+ {
+ ui::Point deletePoint = ((PointType)partRef).Value();
+ if(deletePoint.X<0 || deletePoint.Y<0 || deletePoint.Y >= YRES || deletePoint.X >= XRES)
+ throw GeneralException("Invalid position");
+ sim->delete_part(deletePoint.X, deletePoint.Y, 0);
+ }
+ else if(partRef.GetType() == TypeNumber)
+ {
+ int partIndex = ((NumberType)partRef).Value();
+ if(partIndex < 0 || partIndex >= NPART)
+ throw GeneralException("Invalid particle index");
+ sim->kill_part(partIndex);
+ }
+ else
+ throw GeneralException("Invalid particle reference");
+
+ return NumberType(0);
+}
+
+AnyType TPTScriptInterface::tptS_load(std::deque<std::string> * words)
+{
+ //Arguments from stack
+ NumberType saveID = eval(words);
+
+ c->OpenSavePreview(saveID.Value(), 0);
+
+ return NumberType(0);
+}
+
+AnyType TPTScriptInterface::tptS_bubble(std::deque<std::string> * words)
+{
+ //Arguments from stack
+ PointType bubblePosA = eval(words);
+ ui::Point bubblePos = bubblePosA.Value();
+
+ if(bubblePos.X<0 || bubblePos.Y<0 || bubblePos.Y >= YRES || bubblePos.X >= XRES)
+ throw GeneralException("Invalid position");
+
+ Simulation * sim = m->GetSimulation();
+
+ int first, rem1, rem2;
+
+ first = sim->create_part(-1, bubblePos.X+18, bubblePos.Y, PT_SOAP);
+ rem1 = first;
+
+ for (int i = 1; i<=30; i++)
+ {
+ rem2 = sim->create_part(-1, bubblePos.X+18*cosf(i/5.0), bubblePos.Y+18*sinf(i/5.0), PT_SOAP);
+
+ sim->parts[rem1].ctype = 7;
+ sim->parts[rem1].tmp = rem2;
+ sim->parts[rem2].tmp2 = rem1;
+
+ rem1 = rem2;
+ }
+
+ sim->parts[rem1].ctype = 7;
+ sim->parts[rem1].tmp = first;
+ sim->parts[first].tmp2 = rem1;
+ sim->parts[first].ctype = 7;
+
+ return NumberType(0);
+}
+
+AnyType TPTScriptInterface::tptS_reset(std::deque<std::string> * words)
+{
+ //Arguments from stack
+ StringType reset = eval(words);
+ std::string resetStr = reset.Value();
+
+ Simulation * sim = m->GetSimulation();
+
+ if (resetStr == "pressure")
+ {
+ for (int nx = 0; nx < XRES/CELL; nx++)
+ for (int ny = 0; ny < YRES/CELL; ny++)
+ {
+ sim->air->pv[ny][nx] = 0;
+ }
+ }
+ else if (resetStr == "velocity")
+ {
+ for (int nx = 0; nx < XRES/CELL; nx++)
+ for (int ny = 0; ny < YRES/CELL; ny++)
+ {
+ sim->air->vx[ny][nx] = 0;
+ sim->air->vy[ny][nx] = 0;
+ }
+ }
+ else if (resetStr == "sparks")
+ {
+ for (int i = 0; i < NPART; i++)
+ {
+ if (sim->parts[i].type == PT_SPRK)
+ {
+ sim->parts[i].type = sim->parts[i].ctype;
+ sim->parts[i].life = 4;
+ }
+ }
+ }
+ else if (resetStr == "temp")
+ {
+ for (int i = 0; i < NPART; i++)
+ {
+ if (sim->parts[i].type)
+ {
+ sim->parts[i].temp = sim->elements[sim->parts[i].type].Temperature;
+ }
+ }
+ }
+ else
+ {
+ throw GeneralException("Unknown reset command");
+ }
+
+ return NumberType(0);
+}
+
+AnyType TPTScriptInterface::tptS_quit(std::deque<std::string> * words)
+{
+ ui::Engine::Ref().Exit();
+
+ return NumberType(0);
+}
+
+TPTScriptInterface::~TPTScriptInterface() {
+}
+
diff --git a/src/cat/TPTScriptInterface.h b/src/cat/TPTScriptInterface.h
new file mode 100644
index 0000000..12240fa
--- /dev/null
+++ b/src/cat/TPTScriptInterface.h
@@ -0,0 +1,33 @@
+/*
+ * TPTScriptInterface.h
+ *
+ * Created on: Feb 5, 2012
+ * Author: Simon
+ */
+
+#ifndef TPTSCRIPTINTERFACE_H_
+#define TPTSCRIPTINTERFACE_H_
+
+#include "CommandInterface.h"
+#include "TPTSTypes.h"
+
+class TPTScriptInterface: public CommandInterface {
+protected:
+ AnyType eval(std::deque<std::string> * words);
+ AnyType tptS_set(std::deque<std::string> * words);
+ AnyType tptS_create(std::deque<std::string> * words);
+ AnyType tptS_delete(std::deque<std::string> * words);
+ AnyType tptS_load(std::deque<std::string> * words);
+ AnyType tptS_reset(std::deque<std::string> * words);
+ AnyType tptS_bubble(std::deque<std::string> * words);
+ AnyType tptS_quit(std::deque<std::string> * words);
+ ValueType testType(std::string word);
+public:
+ TPTScriptInterface(GameController * c, GameModel * m);
+ virtual void Tick() {}
+ virtual int Command(std::string command);
+ virtual std::string FormatCommand(std::string command);
+ virtual ~TPTScriptInterface();
+};
+
+#endif /* TPTSCRIPTINTERFACE_H_ */
diff --git a/src/client/Client.cpp b/src/client/Client.cpp
new file mode 100644
index 0000000..763814a
--- /dev/null
+++ b/src/client/Client.cpp
@@ -0,0 +1,2351 @@
+#include <stdlib.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iomanip>
+#include <time.h>
+#include <stdio.h>
+#include <deque>
+#include <fstream>
+#include <dirent.h>
+
+#ifdef MACOSX
+#include <mach-o/dyld.h>
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef WIN
+#include <shlobj.h>
+#include <shlwapi.h>
+#include <windows.h>
+#include <direct.h>
+#else
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#include "Config.h"
+#include "Format.h"
+#include "Client.h"
+#include "MD5.h"
+#include "graphics/Graphics.h"
+#include "Misc.h"
+#include "Update.h"
+#include "HTTP.h"
+
+#include "simulation/SaveRenderer.h"
+#include "interface/Point.h"
+#include "client/SaveInfo.h"
+#include "client/SaveFile.h"
+#include "client/GameSave.h"
+#include "search/Thumbnail.h"
+#include "preview/Comment.h"
+#include "ClientListener.h"
+#include "ThumbnailBroker.h"
+
+#include "cajun/reader.h"
+#include "cajun/writer.h"
+
+extern "C"
+{
+#if defined(WIN) && !defined(__GNUC__)
+#include <io.h>
+#else
+#include <dirent.h>
+#endif
+}
+
+Client::Client():
+ authUser(0, ""),
+ updateAvailable(false),
+ versionCheckRequest(NULL),
+ messageOfTheDay("")
+{
+ int i = 0;
+ for(i = 0; i < THUMB_CACHE_SIZE; i++)
+ {
+ thumbnailCache[i] = NULL;
+ }
+ for(i = 0; i < IMGCONNS; i++)
+ {
+ activeThumbRequests[i] = NULL;
+ activeThumbRequestTimes[i] = 0;
+ activeThumbRequestCompleteTimes[i] = 0;
+ }
+
+ //Read config
+ std::ifstream configFile;
+ configFile.open("powder.pref", std::ios::binary);
+ if(configFile)
+ {
+ int fsize = configFile.tellg();
+ configFile.seekg(0, std::ios::end);
+ fsize = configFile.tellg() - (std::streampos)fsize;
+ configFile.seekg(0, std::ios::beg);
+ if(fsize)
+ {
+ try
+ {
+ json::Reader::Read(configDocument, configFile);
+ authUser.ID = ((json::Number)(configDocument["User"]["ID"])).Value();
+ authUser.SessionID = ((json::String)(configDocument["User"]["SessionID"])).Value();
+ authUser.SessionKey = ((json::String)(configDocument["User"]["SessionKey"])).Value();
+ authUser.Username = ((json::String)(configDocument["User"]["Username"])).Value();
+
+ std::string userElevation = ((json::String)(configDocument["User"]["Elevation"])).Value();
+ if(userElevation == "Admin")
+ authUser.UserElevation = User::ElevationAdmin;
+ else if(userElevation == "Mod")
+ authUser.UserElevation = User::ElevationModerator;
+ else
+ authUser.UserElevation = User::ElevationNone;
+ }
+ catch (json::Exception &e)
+ {
+ authUser = User(0, "");
+ std::cerr << "Error: Could not read data from prefs: " << e.what() << std::endl;
+ }
+ }
+ configFile.close();
+ }
+}
+
+void Client::Initialise(std::string proxyString)
+{
+
+ if(GetPrefBool("version.update", false)==true)
+ {
+ SetPref("version.update", false);
+ update_finish();
+ }
+
+ if(proxyString.length())
+ http_init((char*)proxyString.c_str());
+ else
+ http_init(NULL);
+
+ //Read stamps library
+ std::ifstream stampsLib;
+ stampsLib.open(STAMPS_DIR PATH_SEP "stamps.def", std::ios::binary);
+ while(!stampsLib.eof())
+ {
+ char data[11];
+ memset(data, 0, 11);
+ stampsLib.read(data, 10);
+ if(!data[0])
+ break;
+ stampIDs.push_back(data);
+ }
+ stampsLib.close();
+
+ //Begin version check
+ versionCheckRequest = http_async_req_start(NULL, SERVER "/Startup.json", NULL, 0, 1);
+
+ if(authUser.ID)
+ {
+ http_auth_headers(versionCheckRequest, (char *)format::NumberToString<int>(authUser.ID).c_str(), NULL, (char *)authUser.SessionID.c_str());
+ }
+}
+
+bool Client::DoInstallation()
+{
+#if defined(WIN)
+ int returnval;
+ LONG rresult;
+ HKEY newkey;
+ char *currentfilename = exe_name();
+ char *iconname = NULL;
+ char *opencommand = NULL;
+ //char AppDataPath[MAX_PATH];
+ char *AppDataPath = NULL;
+ iconname = (char*)malloc(strlen(currentfilename)+6);
+ sprintf(iconname, "%s,-102", currentfilename);
+
+ //Create Roaming application data folder
+ /*if(!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, AppDataPath)))
+ {
+ returnval = 0;
+ goto finalise;
+ }*/
+
+ AppDataPath = _getcwd(NULL, 0);
+
+ //Move Game executable into application data folder
+ //TODO: Implement
+
+ opencommand = (char*)malloc(strlen(currentfilename)+53+strlen(AppDataPath));
+ /*if((strlen(AppDataPath)+strlen(APPDATA_SUBDIR "\\Powder Toy"))<MAX_PATH)
+ {
+ strappend(AppDataPath, APPDATA_SUBDIR);
+ _mkdir(AppDataPath);
+ strappend(AppDataPath, "\\Powder Toy");
+ _mkdir(AppDataPath);
+ } else {
+ returnval = 0;
+ goto finalise;
+ }*/
+ sprintf(opencommand, "\"%s\" open \"%%1\" ddir \"%s\"", currentfilename, AppDataPath);
+
+ //Create extension entry
+ rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\.cps", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL);
+ if (rresult != ERROR_SUCCESS) {
+ returnval = 0;
+ goto finalise;
+ }
+ rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)"PowderToySave", strlen("PowderToySave")+1);
+ if (rresult != ERROR_SUCCESS) {
+ RegCloseKey(newkey);
+ returnval = 0;
+ goto finalise;
+ }
+ RegCloseKey(newkey);
+
+ rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\.stm", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL);
+ if (rresult != ERROR_SUCCESS) {
+ returnval = 0;
+ goto finalise;
+ }
+ rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)"PowderToySave", strlen("PowderToySave")+1);
+ if (rresult != ERROR_SUCCESS) {
+ RegCloseKey(newkey);
+ returnval = 0;
+ goto finalise;
+ }
+ RegCloseKey(newkey);
+
+ //Create program entry
+ rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\PowderToySave", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL);
+ if (rresult != ERROR_SUCCESS) {
+ returnval = 0;
+ goto finalise;
+ }
+ rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)"Powder Toy Save", strlen("Powder Toy Save")+1);
+ if (rresult != ERROR_SUCCESS) {
+ RegCloseKey(newkey);
+ returnval = 0;
+ goto finalise;
+ }
+ RegCloseKey(newkey);
+
+ //Set DefaultIcon
+ rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\PowderToySave\\DefaultIcon", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL);
+ if (rresult != ERROR_SUCCESS) {
+ returnval = 0;
+ goto finalise;
+ }
+ rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)iconname, strlen(iconname)+1);
+ if (rresult != ERROR_SUCCESS) {
+ RegCloseKey(newkey);
+ returnval = 0;
+ goto finalise;
+ }
+ RegCloseKey(newkey);
+
+ //Set Launch command
+ rresult = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\PowderToySave\\shell\\open\\command", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &newkey, NULL);
+ if (rresult != ERROR_SUCCESS) {
+ returnval = 0;
+ goto finalise;
+ }
+ rresult = RegSetValueEx(newkey, 0, 0, REG_SZ, (LPBYTE)opencommand, strlen(opencommand)+1);
+ if (rresult != ERROR_SUCCESS) {
+ RegCloseKey(newkey);
+ returnval = 0;
+ goto finalise;
+ }
+ RegCloseKey(newkey);
+
+ returnval = 1;
+ finalise:
+
+ if(iconname) free(iconname);
+ if(opencommand) free(opencommand);
+ if(currentfilename) free(currentfilename);
+
+ return returnval;
+#elif defined(LIN)
+ #include "icondoc.h"
+
+ char *currentfilename = exe_name();
+ FILE *f;
+ char *mimedata =
+"<?xml version=\"1.0\"?>\n"
+" <mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>\n"
+" <mime-type type=\"application/vnd.powdertoy.save\">\n"
+" <comment>Powder Toy save</comment>\n"
+" <glob pattern=\"*.cps\"/>\n"
+" <glob pattern=\"*.stm\"/>\n"
+" </mime-type>\n"
+"</mime-info>\n";
+ f = fopen("powdertoy-save.xml", "wb");
+ if (!f)
+ return 0;
+ fwrite(mimedata, 1, strlen(mimedata), f);
+ fclose(f);
+
+ char *protocolfiledata_tmp =
+"[Desktop Entry]\n"
+"Type=Application\n"
+"Name=Powder Toy\n"
+"Comment=Physics sandbox game\n"
+"MimeType=x-scheme-handler/ptsave;\n"
+"NoDisplay=true\n";
+ char *protocolfiledata = (char *)malloc(strlen(protocolfiledata_tmp)+strlen(currentfilename)+100);
+ strcpy(protocolfiledata, protocolfiledata_tmp);
+ strappend(protocolfiledata, "Exec=");
+ strappend(protocolfiledata, currentfilename);
+ strappend(protocolfiledata, " ptsave %u\n");
+ f = fopen("powdertoy-tpt-ptsave.desktop", "wb");
+ if (!f)
+ return 0;
+ fwrite(protocolfiledata, 1, strlen(protocolfiledata), f);
+ fclose(f);
+ system("xdg-desktop-menu install powdertoy-tpt-ptsave.desktop");
+
+ char *desktopfiledata_tmp =
+"[Desktop Entry]\n"
+"Type=Application\n"
+"Name=Powder Toy\n"
+"Comment=Physics sandbox game\n"
+"MimeType=application/vnd.powdertoy.save;\n"
+"NoDisplay=true\n";
+ char *desktopfiledata = (char *)malloc(strlen(desktopfiledata_tmp)+strlen(currentfilename)+100);
+ strcpy(desktopfiledata, desktopfiledata_tmp);
+ strappend(desktopfiledata, "Exec=");
+ strappend(desktopfiledata, currentfilename);
+ strappend(desktopfiledata, " open %f\n");
+ f = fopen("powdertoy-tpt.desktop", "wb");
+ if (!f)
+ return 0;
+ fwrite(desktopfiledata, 1, strlen(desktopfiledata), f);
+ fclose(f);
+ system("xdg-mime install powdertoy-save.xml");
+ system("xdg-desktop-menu install powdertoy-tpt.desktop");
+ f = fopen("powdertoy-save-32.png", "wb");
+ if (!f)
+ return 0;
+ fwrite(icon_doc_32_png, 1, sizeof(icon_doc_32_png), f);
+ fclose(f);
+ f = fopen("powdertoy-save-16.png", "wb");
+ if (!f)
+ return 0;
+ fwrite(icon_doc_16_png, 1, sizeof(icon_doc_16_png), f);
+ fclose(f);
+ system("xdg-icon-resource install --noupdate --context mimetypes --size 32 powdertoy-save-32.png application-vnd.powdertoy.save");
+ system("xdg-icon-resource install --noupdate --context mimetypes --size 16 powdertoy-save-16.png application-vnd.powdertoy.save");
+ system("xdg-icon-resource forceupdate");
+ system("xdg-mime default powdertoy-tpt.desktop application/vnd.powdertoy.save");
+ system("xdg-mime default powdertoy-tpt-ptsave.desktop x-scheme-handler/ptsave");
+ unlink("powdertoy-save-32.png");
+ unlink("powdertoy-save-16.png");
+ unlink("powdertoy-save.xml");
+ unlink("powdertoy-tpt.desktop");
+ unlink("powdertoy-tpt-ptsave.desktop");
+ return true;
+#elif defined MACOSX
+ return false;
+#endif
+}
+
+void Client::SetProxy(std::string proxy)
+{
+ http_done();
+ if(proxy.length())
+ http_init((char*)proxy.c_str());
+ else
+ http_init(NULL);
+}
+
+std::vector<std::string> Client::DirectorySearch(std::string directory, std::string search, std::string extension)
+{
+ std::vector<std::string> extensions;
+ extensions.push_back(extension);
+ return DirectorySearch(directory, search, extensions);
+}
+
+std::vector<std::string> Client::DirectorySearch(std::string directory, std::string search, std::vector<std::string> extensions)
+{
+ //Get full file listing
+ std::vector<std::string> directoryList;
+#if defined(WIN) && !defined(__GNUC__)
+ //Windows
+ struct _finddata_t currentFile;
+ intptr_t findFileHandle;
+ std::string fileMatch = directory + "*.*";
+ findFileHandle = _findfirst(fileMatch.c_str(), &currentFile);
+ if (findFileHandle == -1L)
+ {
+ printf("Unable to open directory\n");
+ return std::vector<std::string>();
+ }
+ do
+ {
+ std::string currentFileName = std::string(currentFile.name);
+ if(currentFileName.length()>4)
+ directoryList.push_back(directory+currentFileName);
+ }
+ while (_findnext(findFileHandle, &currentFile) == 0);
+ _findclose(findFileHandle);
+#else
+ //Linux or MinGW
+ struct dirent * directoryEntry;
+ DIR *directoryHandle = opendir(directory.c_str());
+ if(!directoryHandle)
+ {
+ printf("Unable to open directory\n");
+ return std::vector<std::string>();
+ }
+ while(directoryEntry = readdir(directoryHandle))
+ {
+ std::string currentFileName = std::string(directoryEntry->d_name);
+ if(currentFileName.length()>4)
+ directoryList.push_back(directory+currentFileName);
+ }
+ closedir(directoryHandle);
+#endif
+
+ std::vector<std::string> searchResults;
+ for(std::vector<std::string>::iterator iter = directoryList.begin(), end = directoryList.end(); iter != end; ++iter)
+ {
+ std::string filename = *iter;
+ bool extensionMatch = !extensions.size();
+ for(std::vector<std::string>::iterator extIter = extensions.begin(), extEnd = extensions.end(); extIter != extEnd; ++extIter)
+ {
+ if(filename.find(*extIter)==filename.length()-(*extIter).length())
+ {
+ extensionMatch = true;
+ break;
+ }
+ }
+ bool searchMatch = !search.size();
+ if(search.size() && filename.find(search)!=std::string::npos)
+ searchMatch = true;
+
+ if(searchMatch && extensionMatch)
+ searchResults.push_back(filename);
+ }
+
+ //Filter results
+ return searchResults;
+}
+
+int Client::MakeDirectory(const char * dirName)
+{
+#ifdef WIN
+ return _mkdir(dirName);
+#else
+ return mkdir(dirName, 0755);
+#endif
+}
+
+void Client::WriteFile(std::vector<unsigned char> fileData, std::string filename)
+{
+ try
+ {
+ std::ofstream fileStream;
+ fileStream.open(std::string(filename).c_str(), std::ios::binary);
+ if(fileStream.is_open())
+ {
+ fileStream.write((char*)&fileData[0], fileData.size());
+ fileStream.close();
+ }
+ }
+ catch (std::exception & e)
+ {
+ std::cerr << "WriteFile:" << e.what() << std::endl;
+ throw;
+ }
+}
+
+bool Client::FileExists(std::string filename)
+{
+ bool exists = false;
+ try
+ {
+ std::ifstream fileStream;
+ fileStream.open(std::string(filename).c_str(), std::ios::binary);
+ if(fileStream.is_open())
+ {
+ exists = true;
+ fileStream.close();
+ }
+ }
+ catch (std::exception & e)
+ {
+ exists = false;
+ }
+ return exists;
+}
+
+void Client::WriteFile(std::vector<char> fileData, std::string filename)
+{
+ try
+ {
+ std::ofstream fileStream;
+ fileStream.open(std::string(filename).c_str(), std::ios::binary);
+ if(fileStream.is_open())
+ {
+ fileStream.write(&fileData[0], fileData.size());
+ fileStream.close();
+ }
+ }
+ catch (std::exception & e)
+ {
+ std::cerr << "WriteFile:" << e.what() << std::endl;
+ throw;
+ }
+}
+
+std::vector<unsigned char> Client::ReadFile(std::string filename)
+{
+ try
+ {
+ std::ifstream fileStream;
+ fileStream.open(std::string(filename).c_str(), std::ios::binary);
+ if(fileStream.is_open())
+ {
+ fileStream.seekg(0, std::ios::end);
+ size_t fileSize = fileStream.tellg();
+ fileStream.seekg(0);
+
+ unsigned char * tempData = new unsigned char[fileSize];
+ fileStream.read((char *)tempData, fileSize);
+ fileStream.close();
+
+ std::vector<unsigned char> fileData;
+ fileData.insert(fileData.end(), tempData, tempData+fileSize);
+ delete[] tempData;
+
+ return fileData;
+ }
+ else
+ {
+ return std::vector<unsigned char>();
+ }
+ }
+ catch(std::exception & e)
+ {
+ std::cerr << "Readfile: " << e.what() << std::endl;
+ throw;
+ }
+}
+
+void Client::SetMessageOfTheDay(std::string message)
+{
+ messageOfTheDay = message;
+ notifyMessageOfTheDay();
+}
+
+std::string Client::GetMessageOfTheDay()
+{
+ return messageOfTheDay;
+}
+
+void Client::Tick()
+{
+ //Check thumbnail queue
+ ThumbnailBroker::Ref().FlushThumbQueue();
+
+ //Check status on version check request
+ if(versionCheckRequest && http_async_req_status(versionCheckRequest))
+ {
+ int status;
+ int dataLength;
+ char * data = http_async_req_stop(versionCheckRequest, &status, &dataLength);
+ versionCheckRequest = NULL;
+
+ if(status != 200)
+ {
+ if(data)
+ free(data);
+ }
+ else
+ {
+ std::istringstream dataStream(data);
+
+ try
+ {
+ json::Object objDocument;
+ json::Reader::Read(objDocument, dataStream);
+
+ //Check session
+ json::Boolean sessionStatus = objDocument["Session"];
+ if(!sessionStatus.Value())
+ {
+ authUser = User(0, "");
+ }
+
+ //MOTD
+ json::String messageOfTheDay = objDocument["MessageOfTheDay"];
+ this->messageOfTheDay = messageOfTheDay.Value();
+ notifyMessageOfTheDay();
+
+ //Check for updates
+ json::Object versions = objDocument["Updates"];
+
+ json::Object stableVersion = versions["Stable"];
+ json::Object betaVersion = versions["Beta"];
+ json::Object snapshotVersion = versions["Snapshot"];
+
+ json::Number stableMajor = stableVersion["Major"];
+ json::Number stableMinor = stableVersion["Minor"];
+ json::Number stableBuild = stableVersion["Build"];
+ json::String stableFile = stableVersion["File"];
+
+ json::Number betaMajor = betaVersion["Major"];
+ json::Number betaMinor = betaVersion["Minor"];
+ json::Number betaBuild = betaVersion["Build"];
+ json::String betaFile = betaVersion["File"];
+
+ json::Number snapshotSnapshot = snapshotVersion["Snapshot"];
+ json::String snapshotFile = snapshotVersion["File"];
+
+ if(stableMajor.Value()>SAVE_VERSION || (stableMinor.Value()>MINOR_VERSION && stableMajor.Value()==SAVE_VERSION) || stableBuild.Value()>BUILD_NUM)
+ {
+ updateAvailable = true;
+ updateInfo = UpdateInfo(stableMajor.Value(), stableMinor.Value(), stableBuild.Value(), stableFile.Value(), UpdateInfo::Stable);
+ }
+
+#ifdef BETA
+ if(betaMajor.Value()>SAVE_VERSION || (betaMinor.Value()>MINOR_VERSION && betaMajor.Value()==SAVE_VERSION) || betaBuild.Value()>BUILD_NUM)
+ {
+ updateAvailable = true;
+ updateInfo = UpdateInfo(betaMajor.Value(), betaMinor.Value(), betaBuild.Value(), betaFile.Value(), UpdateInfo::Beta);
+ }
+#endif
+
+#ifdef SNAPSHOT
+ if(snapshotSnapshot.Value() > SNAPSHOT_ID)
+ {
+ updateAvailable = true;
+ updateInfo = UpdateInfo(snapshotSnapshot.Value(), snapshotFile.Value(), UpdateInfo::Snapshot);
+ }
+#endif
+
+ if(updateAvailable)
+ {
+ notifyUpdateAvailable();
+ }
+ }
+ catch (json::Exception &e)
+ {
+ //Do nothing
+ }
+
+ if(data)
+ free(data);
+ }
+ }
+}
+
+UpdateInfo Client::GetUpdateInfo()
+{
+ return updateInfo;
+}
+
+void Client::notifyUpdateAvailable()
+{
+ for (std::vector<ClientListener*>::iterator iterator = listeners.begin(), end = listeners.end(); iterator != end; ++iterator)
+ {
+ (*iterator)->NotifyUpdateAvailable(this);
+ }
+}
+
+void Client::notifyMessageOfTheDay()
+{
+ for (std::vector<ClientListener*>::iterator iterator = listeners.begin(), end = listeners.end(); iterator != end; ++iterator)
+ {
+ (*iterator)->NotifyMessageOfTheDay(this);
+ }
+}
+
+void Client::notifyAuthUserChanged()
+{
+ for (std::vector<ClientListener*>::iterator iterator = listeners.begin(), end = listeners.end(); iterator != end; ++iterator)
+ {
+ (*iterator)->NotifyAuthUserChanged(this);
+ }
+}
+
+void Client::AddListener(ClientListener * listener)
+{
+ listeners.push_back(listener);
+}
+
+void Client::RemoveListener(ClientListener * listener)
+{
+ for (std::vector<ClientListener*>::iterator iterator = listeners.begin(), end = listeners.end(); iterator != end; ++iterator)
+ {
+ if((*iterator) == listener)
+ {
+ listeners.erase(iterator);
+ return;
+ }
+ }
+}
+
+void Client::WritePrefs()
+{
+ std::ofstream configFile;
+ configFile.open("powder.pref", std::ios::trunc);
+ if(configFile)
+ {
+ if(authUser.ID)
+ {
+ configDocument["User"]["ID"] = json::Number(authUser.ID);
+ configDocument["User"]["SessionID"] = json::String(authUser.SessionID);
+ configDocument["User"]["SessionKey"] = json::String(authUser.SessionKey);
+ configDocument["User"]["Username"] = json::String(authUser.Username);
+ if(authUser.UserElevation == User::ElevationAdmin)
+ configDocument["User"]["Elevation"] = json::String("Admin");
+ else if(authUser.UserElevation == User::ElevationModerator)
+ configDocument["User"]["Elevation"] = json::String("Mod");
+ else
+ configDocument["User"]["Elevation"] = json::String("None");
+ }
+ else
+ {
+ configDocument["User"] = json::Null();
+ }
+ json::Writer::Write(configDocument, configFile);
+ configFile.close();
+ }
+}
+
+void Client::Shutdown()
+{
+ ClearThumbnailRequests();
+ http_done();
+
+ //Save config
+ WritePrefs();
+}
+
+Client::~Client()
+{
+}
+
+
+void Client::SetAuthUser(User user)
+{
+ authUser = user;
+ notifyAuthUserChanged();
+}
+
+User Client::GetAuthUser()
+{
+ return authUser;
+}
+
+RequestStatus Client::UploadSave(SaveInfo & save)
+{
+ lastError = "";
+ int gameDataLength;
+ char * gameData = NULL;
+ int dataStatus;
+ char * data;
+ int dataLength = 0;
+ std::stringstream userIDStream;
+ userIDStream << authUser.ID;
+ if(authUser.ID)
+ {
+ if(!save.GetGameSave())
+ {
+ lastError = "Empty game save";
+ return RequestFailure;
+ }
+ save.SetID(0);
+
+ gameData = save.GetGameSave()->Serialise(gameDataLength);
+
+ if(!gameData)
+ {
+ lastError = "Cannot upload game save";
+ return RequestFailure;
+ }
+
+ char *saveName = new char[save.GetName().length() + 1];
+ std::strcpy ( saveName, save.GetName().c_str() );
+ char *saveDescription = new char[save.GetDescription().length() + 1];
+ std::strcpy ( saveDescription, save.GetDescription().c_str() );
+
+ char * postNames[] = { "Name", "Description", "Data:save.bin", "Publish", NULL };
+ char * postDatas[] = { saveName, saveDescription, gameData, (char *)(save.GetPublished()?"Public":"Private") };
+ int postLengths[] = { save.GetName().length(), save.GetDescription().length(), gameDataLength, save.GetPublished()?6:7 };
+ //std::cout << postNames[0] << " " << postDatas[0] << " " << postLengths[0] << std::endl;
+ data = http_multipart_post("http://" SERVER "/Save.api", postNames, postDatas, postLengths, (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength);
+
+ delete[] saveDescription;
+ delete[] saveName;
+ }
+ else
+ {
+ lastError = "Not authenticated";
+ return RequestFailure;
+ }
+ if(data && dataStatus == 200)
+ {
+ if(strncmp((const char *)data, "OK", 2)!=0)
+ {
+ if(gameData) free(gameData);
+ lastError = std::string((const char *)data);
+ free(data);
+ return RequestFailure;
+ }
+ else
+ {
+ int tempID;
+ std::stringstream saveIDStream((char *)(data+3));
+ saveIDStream >> tempID;
+ if(!tempID)
+ {
+ lastError = "Server did not return Save ID";
+ return RequestFailure;
+ }
+ else
+ {
+ save.SetID(tempID);
+ }
+ }
+ free(data);
+ if(gameData) free(gameData);
+ return RequestOkay;
+ }
+ else if(data)
+ {
+ free(data);
+ }
+ if(gameData) free(gameData);
+ return RequestFailure;
+}
+
+SaveFile * Client::GetStamp(std::string stampID)
+{
+ std::string stampFile = std::string(STAMPS_DIR PATH_SEP + stampID + ".stm");
+ if(FileExists(stampFile))
+ {
+ SaveFile * file = new SaveFile(stampID);
+ try
+ {
+ GameSave * tempSave = new GameSave(ReadFile(stampFile));
+ file->SetGameSave(tempSave);
+ }
+ catch (ParseException & e)
+ {
+ std::cerr << "Client: Invalid stamp file, " << stampID << " " << std::string(e.what()) << std::endl;
+ }
+ return file;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void Client::DeleteStamp(std::string stampID)
+{
+ for (std::list<std::string>::iterator iterator = stampIDs.begin(), end = stampIDs.end(); iterator != end; ++iterator)
+ {
+ if((*iterator) == stampID)
+ {
+ std::stringstream stampFilename;
+ stampFilename << STAMPS_DIR;
+ stampFilename << PATH_SEP;
+ stampFilename << stampID;
+ stampFilename << ".stm";
+ remove(stampFilename.str().c_str());
+ stampIDs.erase(iterator);
+ return;
+ }
+ }
+
+ updateStamps();
+}
+
+std::string Client::AddStamp(GameSave * saveData)
+{
+ unsigned t=(unsigned)time(NULL);
+ if (lastStampTime!=t)
+ {
+ lastStampTime=t;
+ lastStampName=0;
+ }
+ else
+ lastStampName++;
+ std::stringstream saveID;
+ //sprintf(saveID, "%08x%02x", lastStampTime, lastStampName);
+ saveID
+ << std::setw(8) << std::setfill('0') << std::hex << lastStampTime
+ << std::setw(2) << std::setfill('0') << std::hex << lastStampName;
+
+ MakeDirectory(STAMPS_DIR);
+
+ int gameDataLength;
+ char * gameData = saveData->Serialise(gameDataLength);
+
+ std::ofstream stampStream;
+ stampStream.open(std::string(STAMPS_DIR PATH_SEP + saveID.str()+".stm").c_str(), std::ios::binary);
+ stampStream.write((const char *)gameData, gameDataLength);
+ stampStream.close();
+
+ delete[] gameData;
+
+ stampIDs.push_front(saveID.str());
+
+ updateStamps();
+
+ return saveID.str();
+}
+
+void Client::updateStamps()
+{
+ MakeDirectory(STAMPS_DIR);
+
+ std::ofstream stampsStream;
+ stampsStream.open(std::string(STAMPS_DIR PATH_SEP "stamps.def").c_str(), std::ios::binary);
+ for (std::list<std::string>::const_iterator iterator = stampIDs.begin(), end = stampIDs.end(); iterator != end; ++iterator)
+ {
+ stampsStream.write((*iterator).c_str(), 10);
+ }
+ stampsStream.write("\0", 1);
+ stampsStream.close();
+ return;
+}
+
+void Client::RescanStamps()
+{
+ DIR * directory;
+ struct dirent * entry;
+ directory = opendir("stamps");
+ if (directory != NULL)
+ {
+ stampIDs.clear();
+ while (entry = readdir(directory))
+ {
+ if(strncmp(entry->d_name, "..", 3) && strncmp(entry->d_name, ".", 2) && strstr(entry->d_name, ".stm") && strlen(entry->d_name) == 14)
+ {
+ char stampname[11];
+ strncpy(stampname, entry->d_name, 10);
+ stampIDs.push_front(stampname);
+ }
+ }
+ closedir(directory);
+ updateStamps();
+ }
+}
+
+int Client::GetStampsCount()
+{
+ return stampIDs.size();
+}
+
+std::vector<std::string> Client::GetStamps(int start, int count)
+{
+ if(start+count > stampIDs.size()) {
+ if(start > stampIDs.size())
+ return std::vector<std::string>();
+ count = stampIDs.size()-start;
+ }
+
+ std::vector<std::string> stampRange;
+ int index = 0;
+ for (std::list<std::string>::const_iterator iterator = stampIDs.begin(), end = stampIDs.end(); iterator != end; ++iterator, ++index) {
+ if(index>=start && index < start+count)
+ stampRange.push_back(*iterator);
+ }
+ return stampRange;
+}
+
+RequestStatus Client::ExecVote(int saveID, int direction)
+{
+ lastError = "";
+ int dataStatus;
+ char * data;
+ int dataLength = 0;
+ std::stringstream idStream;
+ idStream << saveID;
+
+ std::string saveIDText = format::NumberToString<int>(saveID);
+ std::string directionText = direction==1?"Up":"Down";
+
+ std::string userIDText = format::NumberToString<int>(authUser.ID);
+ if(authUser.ID)
+ {
+ char * postNames[] = { "ID", "Action", NULL };
+ char * postDatas[] = { (char*)(saveIDText.c_str()), (char*)(directionText.c_str()) };
+ int postLengths[] = { saveIDText.length(), directionText.length() };
+ //std::cout << postNames[0] << " " << postDatas[0] << " " << postLengths[0] << std::endl;
+ data = http_multipart_post("http://" SERVER "/Vote.api", postNames, postDatas, postLengths, (char *)(userIDText.c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength);
+ }
+ else
+ {
+ lastError = "Not authenticated";
+ return RequestFailure;
+ }
+ if(data && dataStatus == 200)
+ {
+ if(strncmp((const char *)data, "OK", 2)!=0)
+ {
+ lastError = std::string((const char *)data);
+ free(data);
+ return RequestFailure;
+ }
+ free(data);
+ return RequestOkay;
+ }
+ else if(data)
+ {
+ free(data);
+ }
+ lastError = http_ret_text(dataStatus);
+ return RequestFailure;
+}
+
+unsigned char * Client::GetSaveData(int saveID, int saveDate, int & dataLength)
+{
+ lastError = "";
+ int dataStatus;
+ unsigned char * data;
+ dataLength = 0;
+ std::stringstream urlStream;
+ if(saveDate)
+ {
+ urlStream << "http://" << STATICSERVER << "/" << saveID << "_" << saveDate << ".cps";
+ }
+ else
+ {
+ urlStream << "http://" << STATICSERVER << "/" << saveID << ".cps";
+ }
+
+ data = (unsigned char *)http_simple_get((char *)urlStream.str().c_str(), &dataStatus, &dataLength);
+ if(data && dataStatus == 200)
+ {
+ return data;
+ }
+ else if(data)
+ {
+ free(data);
+ }
+ return NULL;
+}
+
+std::vector<unsigned char> Client::GetSaveData(int saveID, int saveDate)
+{
+ int dataSize;
+ unsigned char * data = GetSaveData(saveID, saveDate, dataSize);
+
+ std::vector<unsigned char> saveData(data, data+dataSize);
+
+ delete[] data;
+ return saveData;
+}
+
+LoginStatus Client::Login(std::string username, std::string password, User & user)
+{
+ lastError = "";
+ std::stringstream urlStream;
+ std::stringstream hashStream;
+ char passwordHash[33];
+ char totalHash[33];
+
+ user.ID = 0;
+ user.Username = "";
+ user.SessionID = "";
+ user.SessionKey = "";
+
+ //Doop
+ md5_ascii(passwordHash, (const unsigned char *)password.c_str(), password.length());
+ passwordHash[32] = 0;
+ hashStream << username << "-" << passwordHash;
+ md5_ascii(totalHash, (const unsigned char *)(hashStream.str().c_str()), hashStream.str().length());
+ totalHash[32] = 0;
+
+ char * data;
+ int dataStatus, dataLength;
+ char * postNames[] = { "Username", "Hash", NULL };
+ char * postDatas[] = { (char*)username.c_str(), totalHash };
+ int postLengths[] = { username.length(), 32 };
+ data = http_multipart_post("http://" SERVER "/Login.json", postNames, postDatas, postLengths, NULL, NULL, NULL, &dataStatus, &dataLength);
+ //data = http_auth_get("http://" SERVER "/Login.json", (char*)username.c_str(), (char*)password.c_str(), NULL, &dataStatus, &dataLength);
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Object objDocument;
+ json::Reader::Read(objDocument, dataStream);
+ json::Number tempStatus = objDocument["Status"];
+
+ free(data);
+ if(tempStatus.Value() == 1)
+ {
+ json::Number userIDTemp = objDocument["UserID"];
+ json::String sessionIDTemp = objDocument["SessionID"];
+ json::String sessionKeyTemp = objDocument["SessionKey"];
+ json::String userElevationTemp = objDocument["Elevation"];
+ user.Username = username;
+ user.ID = userIDTemp.Value();
+ user.SessionID = sessionIDTemp.Value();
+ user.SessionKey = sessionKeyTemp.Value();
+ std::string userElevation = userElevationTemp.Value();
+ if(userElevation == "Admin")
+ user.UserElevation = User::ElevationAdmin;
+ else if(userElevation == "Mod")
+ user.UserElevation = User::ElevationModerator;
+ else
+ user.UserElevation= User::ElevationNone;
+ return LoginOkay;
+ }
+ else
+ {
+ json::String tempError = objDocument["Error"];
+ lastError = tempError.Value();
+ return LoginError;
+ }
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Server responded with crap";
+ return LoginError;
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ }
+ if(data)
+ {
+ free(data);
+ }
+ return LoginError;
+}
+
+RequestStatus Client::DeleteSave(int saveID)
+{
+ lastError = "";
+ std::vector<std::string> * tags = NULL;
+ std::stringstream urlStream;
+ char * data = NULL;
+ int dataStatus, dataLength;
+ urlStream << "http://" << SERVER << "/Browse/Delete.json?ID=" << saveID << "&Mode=Delete&Key=" << authUser.SessionKey;
+ if(authUser.ID)
+ {
+ std::stringstream userIDStream;
+ userIDStream << authUser.ID;
+ data = http_auth_get((char *)urlStream.str().c_str(), (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength);
+ }
+ else
+ {
+ lastError = "Not authenticated";
+ return RequestFailure;
+ }
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Object objDocument;
+ json::Reader::Read(objDocument, dataStream);
+
+ int status = ((json::Number)objDocument["Status"]).Value();
+
+ if(status!=1)
+ goto failure;
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Could not read response";
+ goto failure;
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ goto failure;
+ }
+ if(data)
+ free(data);
+ return RequestOkay;
+failure:
+ if(data)
+ free(data);
+ return RequestFailure;
+}
+
+RequestStatus Client::AddComment(int saveID, std::string comment)
+{
+ lastError = "";
+ std::vector<std::string> * tags = NULL;
+ std::stringstream urlStream;
+ char * data = NULL;
+ int dataStatus, dataLength;
+ urlStream << "http://" << SERVER << "/Browse/Comments.json?ID=" << saveID << "&Key=" << authUser.SessionKey;
+ if(authUser.ID)
+ {
+ std::stringstream userIDStream;
+ userIDStream << authUser.ID;
+
+ char * postNames[] = { "Comment", NULL };
+ char * postDatas[] = { (char*)(comment.c_str()) };
+ int postLengths[] = { comment.length() };
+ data = http_multipart_post((char *)urlStream.str().c_str(), postNames, postDatas, postLengths, (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength);
+ }
+ else
+ {
+ lastError = "Not authenticated";
+ return RequestFailure;
+ }
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Object objDocument;
+ json::Reader::Read(objDocument, dataStream);
+
+ int status = ((json::Number)objDocument["Status"]).Value();
+
+ if(status!=1)
+ {
+ lastError = ((json::Number)objDocument["Error"]).Value();
+ }
+
+ if(status!=1)
+ goto failure;
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Could not read response";
+ goto failure;
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ goto failure;
+ }
+ if(data)
+ free(data);
+ return RequestOkay;
+failure:
+ if(data)
+ free(data);
+ return RequestFailure;
+}
+
+RequestStatus Client::FavouriteSave(int saveID, bool favourite)
+{
+ lastError = "";
+ std::vector<std::string> * tags = NULL;
+ std::stringstream urlStream;
+ char * data = NULL;
+ int dataStatus, dataLength;
+ urlStream << "http://" << SERVER << "/Browse/Favourite.json?ID=" << saveID << "&Key=" << authUser.SessionKey;
+ if(!favourite)
+ urlStream << "&Mode=Remove";
+ if(authUser.ID)
+ {
+ std::stringstream userIDStream;
+ userIDStream << authUser.ID;
+ data = http_auth_get((char *)urlStream.str().c_str(), (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength);
+ }
+ else
+ {
+ lastError = "Not authenticated";
+ return RequestFailure;
+ }
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Object objDocument;
+ json::Reader::Read(objDocument, dataStream);
+
+ int status = ((json::Number)objDocument["Status"]).Value();
+
+ if(status!=1)
+ {
+ lastError = ((json::String)objDocument["Error"]).Value();
+ goto failure;
+ }
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Could not read response";
+ goto failure;
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ goto failure;
+ }
+ if(data)
+ free(data);
+ return RequestOkay;
+failure:
+ if(data)
+ free(data);
+ return RequestFailure;
+}
+
+RequestStatus Client::ReportSave(int saveID, std::string message)
+{
+ lastError = "";
+ std::vector<std::string> * tags = NULL;
+ std::stringstream urlStream;
+ char * data = NULL;
+ int dataStatus, dataLength;
+ urlStream << "http://" << SERVER << "/Browse/Report.json?ID=" << saveID << "&Key=" << authUser.SessionKey;
+ if(authUser.ID)
+ {
+ std::stringstream userIDStream;
+ userIDStream << authUser.ID;
+
+ char * postNames[] = { "Reason", NULL };
+ char * postDatas[] = { (char*)(message.c_str()) };
+ int postLengths[] = { message.length() };
+ data = http_multipart_post((char *)urlStream.str().c_str(), postNames, postDatas, postLengths, (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength);
+ }
+ else
+ {
+ lastError = "Not authenticated";
+ return RequestFailure;
+ }
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Object objDocument;
+ json::Reader::Read(objDocument, dataStream);
+
+ int status = ((json::Number)objDocument["Status"]).Value();
+
+ if(status!=1)
+ {
+ lastError = ((json::String)objDocument["Error"]).Value();
+ goto failure;
+ }
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Could not read response";
+ goto failure;
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ goto failure;
+ }
+ if(data)
+ free(data);
+ return RequestOkay;
+failure:
+ if(data)
+ free(data);
+ return RequestFailure;
+}
+
+RequestStatus Client::UnpublishSave(int saveID)
+{
+ lastError = "";
+ std::vector<std::string> * tags = NULL;
+ std::stringstream urlStream;
+ char * data = NULL;
+ int dataStatus, dataLength;
+ urlStream << "http://" << SERVER << "/Browse/Delete.json?ID=" << saveID << "&Mode=Unpublish&Key=" << authUser.SessionKey;
+ if(authUser.ID)
+ {
+ std::stringstream userIDStream;
+ userIDStream << authUser.ID;
+ data = http_auth_get((char *)urlStream.str().c_str(), (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength);
+ }
+ else
+ {
+ lastError = "Not authenticated";
+ return RequestFailure;
+ }
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Object objDocument;
+ json::Reader::Read(objDocument, dataStream);
+
+ int status = ((json::Number)objDocument["Status"]).Value();
+
+ if(status!=1)
+ goto failure;
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Could not read response";
+ goto failure;
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ goto failure;
+ }
+ if(data)
+ free(data);
+ return RequestOkay;
+failure:
+ if(data)
+ free(data);
+ return RequestFailure;
+}
+
+SaveInfo * Client::GetSave(int saveID, int saveDate)
+{
+ lastError = "";
+ std::stringstream urlStream;
+ urlStream << "http://" << SERVER << "/Browse/View.json?ID=" << saveID;
+ if(saveDate)
+ {
+ urlStream << "&Date=" << saveDate;
+ }
+ char * data;
+ int dataStatus, dataLength;
+ //Save(int _id, int _votesUp, int _votesDown, string _userName, string _name, string description_, string date_, bool published_):
+ if(authUser.ID)
+ {
+ std::stringstream userIDStream;
+ userIDStream << authUser.ID;
+ data = http_auth_get((char *)urlStream.str().c_str(), (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength);
+ }
+ else
+ {
+ data = http_simple_get((char *)urlStream.str().c_str(), &dataStatus, &dataLength);
+ }
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Object objDocument;
+ json::Reader::Read(objDocument, dataStream);
+
+ json::Number tempID = objDocument["ID"];
+ json::Number tempScoreUp = objDocument["ScoreUp"];
+ json::Number tempScoreDown = objDocument["ScoreDown"];
+ json::Number tempMyScore = objDocument["ScoreMine"];
+ json::String tempUsername = objDocument["Username"];
+ json::String tempName = objDocument["Name"];
+ json::String tempDescription = objDocument["Description"];
+ json::Number tempDate = objDocument["Date"];
+ json::Boolean tempPublished = objDocument["Published"];
+ json::Boolean tempFavourite = objDocument["Favourite"];
+ json::Number tempComments = objDocument["Comments"];
+ json::Number tempViews = objDocument["Views"];
+ json::Number tempVersion = objDocument["Version"];
+
+ json::Array tagsArray = objDocument["Tags"];
+ std::vector<std::string> tempTags;
+
+ for(int j = 0; j < tagsArray.Size(); j++)
+ {
+ json::String tempTag = tagsArray[j];
+ tempTags.push_back(tempTag.Value());
+ }
+
+ SaveInfo * tempSave = new SaveInfo(
+ tempID.Value(),
+ tempDate.Value(),
+ tempScoreUp.Value(),
+ tempScoreDown.Value(),
+ tempMyScore.Value(),
+ tempUsername.Value(),
+ tempName.Value(),
+ tempDescription.Value(),
+ tempPublished.Value(),
+ tempTags
+ );
+ tempSave->Comments = tempComments.Value();
+ tempSave->Favourite = tempFavourite.Value();
+ tempSave->Views = tempViews.Value();
+ tempSave->Version = tempVersion.Value();
+ return tempSave;
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Could not read response";
+ return NULL;
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ }
+ return NULL;
+}
+
+Thumbnail * Client::GetPreview(int saveID, int saveDate)
+{
+ std::stringstream urlStream;
+ urlStream << "http://" << STATICSERVER << "/" << saveID;
+ if(saveDate)
+ {
+ urlStream << "_" << saveDate;
+ }
+ urlStream << "_large.pti";
+ pixel * thumbData;
+ char * data;
+ int status, data_size, imgw, imgh;
+ data = http_simple_get((char *)urlStream.str().c_str(), &status, &data_size);
+ if (status == 200 && data)
+ {
+ thumbData = Graphics::ptif_unpack(data, data_size, &imgw, &imgh);
+ if(data)
+ {
+ free(data);
+ }
+ if(thumbData)
+ {
+ return new Thumbnail(saveID, saveDate, thumbData, ui::Point(imgw, imgh));
+ free(thumbData);
+ }
+ else
+ {
+ thumbData = (pixel *)malloc((128*128) * PIXELSIZE);
+ return new Thumbnail(saveID, saveDate, thumbData, ui::Point(128, 128));
+ free(thumbData);
+ }
+ }
+ else
+ {
+ if(data)
+ {
+ free(data);
+ }
+ }
+ return new Thumbnail(saveID, saveDate, (pixel *)malloc((128*128) * PIXELSIZE), ui::Point(128, 128));
+}
+
+std::vector<SaveComment*> * Client::GetComments(int saveID, int start, int count)
+{
+ lastError = "";
+ std::vector<SaveComment*> * commentArray = new std::vector<SaveComment*>();
+
+ std::stringstream urlStream;
+ char * data;
+ int dataStatus, dataLength;
+ urlStream << "http://" << SERVER << "/Browse/Comments.json?ID=" << saveID << "&Start=" << start << "&Count=" << count;
+ data = http_simple_get((char *)urlStream.str().c_str(), &dataStatus, &dataLength);
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Array commentsArray;
+ json::Reader::Read(commentsArray, dataStream);
+
+ for(int j = 0; j < commentsArray.Size(); j++)
+ {
+ json::Number tempUserID = commentsArray[j]["UserID"];
+ json::String tempUsername = commentsArray[j]["FormattedUsername"];
+ json::String tempComment = commentsArray[j]["Text"];
+ commentArray->push_back(
+ new SaveComment(
+ tempUserID.Value(),
+ tempUsername.Value(),
+ tempComment.Value()
+ )
+ );
+ }
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Could not read response";
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ }
+ if(data)
+ free(data);
+ return commentArray;
+}
+
+std::vector<std::pair<std::string, int> > * Client::GetTags(int start, int count, std::string query, int & resultCount)
+{
+ lastError = "";
+ resultCount = 0;
+ std::vector<std::pair<std::string, int> > * tagArray = new std::vector<std::pair<std::string, int> >();
+ std::stringstream urlStream;
+ char * data;
+ int dataStatus, dataLength;
+ urlStream << "http://" << SERVER << "/Browse/Tags.json?Start=" << start << "&Count=" << count;
+ if(query.length())
+ {
+ urlStream << "&Search_Query=";
+ if(query.length())
+ urlStream << URLEscape(query);
+ }
+
+ data = http_simple_get((char *)urlStream.str().c_str(), &dataStatus, &dataLength);
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Object objDocument;
+ json::Reader::Read(objDocument, dataStream);
+
+ json::Number tempCount = objDocument["TagTotal"];
+ resultCount = tempCount.Value();
+ json::Array tagsArray = objDocument["Tags"];
+ for(int j = 0; j < tagsArray.Size(); j++)
+ {
+ json::Number tagCount = tagsArray[j]["Count"];
+ json::String tag = tagsArray[j]["Tag"];
+ tagArray->push_back(std::pair<std::string, int>(tag.Value(), tagCount.Value()));
+ }
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Could not read response";
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ }
+ if(data)
+ free(data);
+ return tagArray;
+}
+
+std::vector<SaveInfo*> * Client::SearchSaves(int start, int count, std::string query, std::string sort, std::string category, int & resultCount)
+{
+ lastError = "";
+ resultCount = 0;
+ std::vector<SaveInfo*> * saveArray = new std::vector<SaveInfo*>();
+ std::stringstream urlStream;
+ char * data;
+ int dataStatus, dataLength;
+ urlStream << "http://" << SERVER << "/Browse.json?Start=" << start << "&Count=" << count;
+ if(query.length() || sort.length())
+ {
+ urlStream << "&Search_Query=";
+ if(query.length())
+ urlStream << URLEscape(query);
+ if(sort == "date")
+ {
+ if(query.length())
+ urlStream << URLEscape(" ");
+ urlStream << URLEscape("sort:") << URLEscape(sort);
+ }
+ }
+ if(category.length())
+ {
+ urlStream << "&Category=" << URLEscape(category);
+ }
+ if(authUser.ID)
+ {
+ std::stringstream userIDStream;
+ userIDStream << authUser.ID;
+ data = http_auth_get((char *)urlStream.str().c_str(), (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength);
+ }
+ else
+ {
+ data = http_simple_get((char *)urlStream.str().c_str(), &dataStatus, &dataLength);
+ }
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Object objDocument;
+ json::Reader::Read(objDocument, dataStream);
+
+ json::Number tempCount = objDocument["Count"];
+ resultCount = tempCount.Value();
+ json::Array savesArray = objDocument["Saves"];
+ for(int j = 0; j < savesArray.Size(); j++)
+ {
+ json::Number tempID = savesArray[j]["ID"];
+ json::Number tempDate = savesArray[j]["Date"];
+ json::Number tempScoreUp = savesArray[j]["ScoreUp"];
+ json::Number tempScoreDown = savesArray[j]["ScoreDown"];
+ json::String tempUsername = savesArray[j]["Username"];
+ json::String tempName = savesArray[j]["Name"];
+ json::Number tempVersion = savesArray[j]["Version"];
+ json::Boolean tempPublished = savesArray[j]["Published"];
+ SaveInfo * tempSaveInfo = new SaveInfo(
+ tempID.Value(),
+ tempDate.Value(),
+ tempScoreUp.Value(),
+ tempScoreDown.Value(),
+ tempUsername.Value(),
+ tempName.Value()
+ );
+ tempSaveInfo->Version = tempVersion.Value();
+ tempSaveInfo->SetPublished(tempPublished);
+ saveArray->push_back(tempSaveInfo);
+ }
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Could not read response";
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ }
+ if(data)
+ free(data);
+ return saveArray;
+}
+
+void Client::ClearThumbnailRequests()
+{
+ for(int i = 0; i < IMGCONNS; i++)
+ {
+ if(activeThumbRequests[i])
+ {
+ http_async_req_close(activeThumbRequests[i]);
+ activeThumbRequests[i] = NULL;
+ activeThumbRequestTimes[i] = 0;
+ activeThumbRequestCompleteTimes[i] = 0;
+ }
+ }
+}
+
+Thumbnail * Client::GetThumbnail(int saveID, int saveDate)
+{
+ std::stringstream urlStream;
+ std::stringstream idStream;
+ int i = 0, currentTime = time(NULL);
+ //Check active requests for any "forgotten" requests
+ for(i = 0; i < IMGCONNS; i++)
+ {
+ //If the request is active, and we've recieved a response
+ if(activeThumbRequests[i] && http_async_req_status(activeThumbRequests[i]))
+ {
+ //If we haven't already, mark the request as completed
+ if(!activeThumbRequestCompleteTimes[i])
+ {
+ activeThumbRequestCompleteTimes[i] = time(NULL);
+ }
+ else if(activeThumbRequestCompleteTimes[i] < (currentTime-2)) //Otherwise, if it completed more than 2 seconds ago, destroy it.
+ {
+ http_async_req_close(activeThumbRequests[i]);
+ activeThumbRequests[i] = NULL;
+ activeThumbRequestTimes[i] = 0;
+ activeThumbRequestCompleteTimes[i] = 0;
+ }
+ }
+ }
+ for(i = 0; i < THUMB_CACHE_SIZE; i++)
+ {
+ if(thumbnailCache[i] && thumbnailCache[i]->ID == saveID && thumbnailCache[i]->Datestamp == saveDate)
+ return thumbnailCache[i];
+ }
+ urlStream << "http://" << STATICSERVER << "/" << saveID;
+ if(saveDate)
+ {
+ urlStream << "_" << saveDate;
+ }
+ urlStream << "_small.pti";
+ idStream << saveID << ":" << saveDate;
+ std::string idString = idStream.str();
+ bool found = false;
+ for(i = 0; i < IMGCONNS; i++)
+ {
+ if(activeThumbRequests[i] && activeThumbRequestIDs[i] == idString)
+ {
+ found = true;
+ if(http_async_req_status(activeThumbRequests[i]))
+ {
+ pixel * thumbData;
+ char * data;
+ int status, data_size, imgw, imgh;
+ data = http_async_req_stop(activeThumbRequests[i], &status, &data_size);
+ free(activeThumbRequests[i]);
+ activeThumbRequests[i] = NULL;
+ if (status == 200 && data)
+ {
+ thumbData = Graphics::ptif_unpack(data, data_size, &imgw, &imgh);
+ if(data)
+ {
+ free(data);
+ }
+ thumbnailCacheNextID %= THUMB_CACHE_SIZE;
+ if(thumbnailCache[thumbnailCacheNextID])
+ {
+ delete thumbnailCache[thumbnailCacheNextID];
+ }
+ if(thumbData)
+ {
+ thumbnailCache[thumbnailCacheNextID] = new Thumbnail(saveID, saveDate, thumbData, ui::Point(imgw, imgh));
+ free(thumbData);
+ }
+ else
+ {
+ thumbData = (pixel *)malloc((128*128) * PIXELSIZE);
+ thumbnailCache[thumbnailCacheNextID] = new Thumbnail(saveID, saveDate, thumbData, ui::Point(128, 128));
+ free(thumbData);
+ }
+ return thumbnailCache[thumbnailCacheNextID++];
+ }
+ else
+ {
+ if(data)
+ {
+ free(data);
+ }
+ thumbnailCacheNextID %= THUMB_CACHE_SIZE;
+ if(thumbnailCache[thumbnailCacheNextID])
+ {
+ delete thumbnailCache[thumbnailCacheNextID];
+ }
+ thumbData = (pixel *)malloc((128*128) * PIXELSIZE);
+ thumbnailCache[thumbnailCacheNextID] = new Thumbnail(saveID, saveDate, thumbData, ui::Point(128, 128));
+ free(thumbData);
+ return thumbnailCache[thumbnailCacheNextID++];
+ }
+ }
+ }
+ }
+ if(!found)
+ {
+ for(i = 0; i < IMGCONNS; i++)
+ {
+ if(!activeThumbRequests[i])
+ {
+ activeThumbRequests[i] = http_async_req_start(NULL, (char *)urlStream.str().c_str(), NULL, 0, 1);
+ activeThumbRequestTimes[i] = currentTime;
+ activeThumbRequestCompleteTimes[i] = 0;
+ activeThumbRequestIDs[i] = idString;
+ return NULL;
+ }
+ }
+ }
+ //http_async_req_start(http, urlStream.str().c_str(), NULL, 0, 1);
+ return NULL;
+}
+
+std::vector<std::string> * Client::RemoveTag(int saveID, std::string tag)
+{
+ lastError = "";
+ std::vector<std::string> * tags = NULL;
+ std::stringstream urlStream;
+ char * data = NULL;
+ int dataStatus, dataLength;
+ urlStream << "http://" << SERVER << "/Browse/EditTag.json?Op=delete&ID=" << saveID << "&Tag=" << tag << "&Key=" << authUser.SessionKey;;
+ if(authUser.ID)
+ {
+ std::stringstream userIDStream;
+ userIDStream << authUser.ID;
+ data = http_auth_get((char *)urlStream.str().c_str(), (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength);
+ }
+ else
+ {
+ lastError = "Not authenticated";
+ return NULL;
+ }
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Object responseObject;
+ json::Reader::Read(responseObject, dataStream);
+
+ json::Number status = responseObject["Status"];
+
+ if(status.Value()==0)
+ {
+ json::String error = responseObject["Error"];
+ lastError = error.Value();
+ }
+ else
+ {
+ json::Array tagsArray = responseObject["Tags"];
+
+ tags = new std::vector<std::string>();
+
+ for(int j = 0; j < tagsArray.Size(); j++)
+ {
+ json::String tempTag = tagsArray[j];
+ tags->push_back(tempTag.Value());
+ }
+ }
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Could not read response";
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ }
+ if(data)
+ free(data);
+ return tags;
+}
+
+std::vector<std::string> * Client::AddTag(int saveID, std::string tag)
+{
+ lastError = "";
+ std::vector<std::string> * tags = NULL;
+ std::stringstream urlStream;
+ char * data = NULL;
+ int dataStatus, dataLength;
+ urlStream << "http://" << SERVER << "/Browse/EditTag.json?Op=add&ID=" << saveID << "&Tag=" << tag << "&Key=" << authUser.SessionKey;
+ if(authUser.ID)
+ {
+ std::stringstream userIDStream;
+ userIDStream << authUser.ID;
+ data = http_auth_get((char *)urlStream.str().c_str(), (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength);
+ }
+ else
+ {
+ lastError = "Not authenticated";
+ return NULL;
+ }
+ if(dataStatus == 200 && data)
+ {
+ try
+ {
+ std::istringstream dataStream(data);
+ json::Object responseObject;
+ json::Reader::Read(responseObject, dataStream);
+
+ json::Number status = responseObject["Status"];
+
+ if(status.Value()==0)
+ {
+ json::String error = responseObject["Error"];
+ lastError = error.Value();
+ }
+ else
+ {
+ json::Array tagsArray = responseObject["Tags"];
+
+ tags = new std::vector<std::string>();
+
+ for(int j = 0; j < tagsArray.Size(); j++)
+ {
+ json::String tempTag = tagsArray[j];
+ tags->push_back(tempTag.Value());
+ }
+ }
+ }
+ catch (json::Exception &e)
+ {
+ lastError = "Could not read response";
+ }
+ }
+ else
+ {
+ lastError = http_ret_text(dataStatus);
+ }
+ if(data)
+ free(data);
+ return tags;
+}
+
+std::vector<std::string> Client::explodePropertyString(std::string property)
+{
+ std::vector<std::string> stringArray;
+ std::string current = "";
+ for (std::string::iterator iter = property.begin(); iter != property.end(); ++iter) {
+ if (*iter == '.') {
+ if (current.length() > 0) {
+ stringArray.push_back(current);
+ current = "";
+ }
+ } else {
+ current += *iter;
+ }
+ }
+ if(current.length() > 0)
+ stringArray.push_back(current);
+ return stringArray;
+}
+
+std::string Client::GetPrefString(std::string property, std::string defaultValue)
+{
+ try
+ {
+ json::String value = GetPref(property);
+ return value.Value();
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ return defaultValue;
+}
+
+double Client::GetPrefNumber(std::string property, double defaultValue)
+{
+ try
+ {
+ json::Number value = GetPref(property);
+ return value.Value();
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ return defaultValue;
+}
+
+int Client::GetPrefInteger(std::string property, int defaultValue)
+{
+ try
+ {
+ std::stringstream defHexInt;
+ defHexInt << std::hex << defaultValue;
+
+ std::string hexString = GetPrefString(property, defHexInt.str());
+ int finalValue = defaultValue;
+
+ std::stringstream hexInt;
+ hexInt << hexString;
+
+ hexInt >> std::hex >> finalValue;
+
+ return finalValue;
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ catch(std::exception & e)
+ {
+
+ }
+ return defaultValue;
+}
+
+unsigned int Client::GetPrefUInteger(std::string property, unsigned int defaultValue)
+{
+ try
+ {
+ std::stringstream defHexInt;
+ defHexInt << std::hex << defaultValue;
+
+ std::string hexString = GetPrefString(property, defHexInt.str());
+ unsigned int finalValue = defaultValue;
+
+ std::stringstream hexInt;
+ hexInt << hexString;
+
+ hexInt >> std::hex >> finalValue;
+
+ return finalValue;
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ catch(std::exception & e)
+ {
+
+ }
+ return defaultValue;
+}
+
+std::vector<std::string> Client::GetPrefStringArray(std::string property)
+{
+ try
+ {
+ json::Array value = GetPref(property);
+ std::vector<std::string> strArray;
+ for(json::Array::iterator iter = value.Begin(); iter != value.End(); ++iter)
+ {
+ try
+ {
+ json::String cValue = *iter;
+ strArray.push_back(cValue.Value());
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ }
+ return strArray;
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ return std::vector<std::string>();
+}
+
+std::vector<double> Client::GetPrefNumberArray(std::string property)
+{
+ try
+ {
+ json::Array value = GetPref(property);
+ std::vector<double> strArray;
+ for(json::Array::iterator iter = value.Begin(); iter != value.End(); ++iter)
+ {
+ try
+ {
+ json::Number cValue = *iter;
+ strArray.push_back(cValue.Value());
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ }
+ return strArray;
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ return std::vector<double>();
+}
+
+std::vector<int> Client::GetPrefIntegerArray(std::string property)
+{
+ try
+ {
+ json::Array value = GetPref(property);
+ std::vector<int> intArray;
+ for(json::Array::iterator iter = value.Begin(); iter != value.End(); ++iter)
+ {
+ try
+ {
+ json::String cValue = *iter;
+ int finalValue = 0;
+
+ std::string hexString = cValue.Value();
+ std::stringstream hexInt;
+ hexInt << std::hex << hexString;
+ hexInt >> finalValue;
+
+ intArray.push_back(finalValue);
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ }
+ return intArray;
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ return std::vector<int>();
+}
+
+std::vector<unsigned int> Client::GetPrefUIntegerArray(std::string property)
+{
+ try
+ {
+ json::Array value = GetPref(property);
+ std::vector<unsigned int> intArray;
+ for(json::Array::iterator iter = value.Begin(); iter != value.End(); ++iter)
+ {
+ try
+ {
+ json::String cValue = *iter;
+ unsigned int finalValue = 0;
+
+ std::string hexString = cValue.Value();
+ std::stringstream hexInt;
+ hexInt << std::hex << hexString;
+ hexInt >> finalValue;
+
+ intArray.push_back(finalValue);
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ }
+ return intArray;
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ return std::vector<unsigned int>();
+}
+
+std::vector<bool> Client::GetPrefBoolArray(std::string property)
+{
+ try
+ {
+ json::Array value = GetPref(property);
+ std::vector<bool> strArray;
+ for(json::Array::iterator iter = value.Begin(); iter != value.End(); ++iter)
+ {
+ try
+ {
+ json::Boolean cValue = *iter;
+ strArray.push_back(cValue.Value());
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ }
+ return strArray;
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ return std::vector<bool>();
+}
+
+bool Client::GetPrefBool(std::string property, bool defaultValue)
+{
+ try
+ {
+ json::Boolean value = GetPref(property);
+ return value.Value();
+ }
+ catch (json::Exception & e)
+ {
+
+ }
+ return defaultValue;
+}
+
+void Client::SetPref(std::string property, std::string value)
+{
+ json::UnknownElement stringValue = json::String(value);
+ SetPref(property, stringValue);
+}
+
+void Client::SetPref(std::string property, double value)
+{
+ json::UnknownElement numberValue = json::Number(value);
+ SetPref(property, numberValue);
+}
+
+void Client::SetPref(std::string property, int value)
+{
+ std::stringstream hexInt;
+ hexInt << std::hex << value;
+ json::UnknownElement intValue = json::String(hexInt.str());
+ SetPref(property, intValue);
+}
+
+void Client::SetPref(std::string property, unsigned int value)
+{
+ std::stringstream hexInt;
+ hexInt << std::hex << value;
+ json::UnknownElement intValue = json::String(hexInt.str());
+ SetPref(property, intValue);
+}
+
+void Client::SetPref(std::string property, std::vector<std::string> value)
+{
+ json::Array newArray;
+ for(std::vector<std::string>::iterator iter = value.begin(); iter != value.end(); ++iter)
+ {
+ newArray.Insert(json::String(*iter));
+ }
+ json::UnknownElement newArrayValue = newArray;
+ SetPref(property, newArrayValue);
+}
+
+void Client::SetPref(std::string property, std::vector<double> value)
+{
+ json::Array newArray;
+ for(std::vector<double>::iterator iter = value.begin(); iter != value.end(); ++iter)
+ {
+ newArray.Insert(json::Number(*iter));
+ }
+ json::UnknownElement newArrayValue = newArray;
+ SetPref(property, newArrayValue);
+}
+
+void Client::SetPref(std::string property, std::vector<bool> value)
+{
+ json::Array newArray;
+ for(std::vector<bool>::iterator iter = value.begin(); iter != value.end(); ++iter)
+ {
+ newArray.Insert(json::Boolean(*iter));
+ }
+ json::UnknownElement newArrayValue = newArray;
+ SetPref(property, newArrayValue);
+}
+
+void Client::SetPref(std::string property, std::vector<int> value)
+{
+ json::Array newArray;
+ for(std::vector<int>::iterator iter = value.begin(); iter != value.end(); ++iter)
+ {
+ std::stringstream hexInt;
+ hexInt << std::hex << *iter;
+
+ newArray.Insert(json::String(hexInt.str()));
+ }
+ json::UnknownElement newArrayValue = newArray;
+ SetPref(property, newArrayValue);
+}
+
+void Client::SetPref(std::string property, std::vector<unsigned int> value)
+{
+ json::Array newArray;
+ for(std::vector<unsigned int>::iterator iter = value.begin(); iter != value.end(); ++iter)
+ {
+ std::stringstream hexInt;
+ hexInt << std::hex << *iter;
+
+ newArray.Insert(json::String(hexInt.str()));
+ }
+ json::UnknownElement newArrayValue = newArray;
+ SetPref(property, newArrayValue);
+}
+
+void Client::SetPref(std::string property, bool value)
+{
+ json::UnknownElement boolValue = json::Boolean(value);
+ SetPref(property, boolValue);
+}
+
+json::UnknownElement Client::GetPref(std::string property)
+{
+ std::vector<std::string> pTokens = Client::explodePropertyString(property);
+ const json::UnknownElement & configDocumentCopy = configDocument;
+ json::UnknownElement currentRef = configDocumentCopy;
+ for(std::vector<std::string>::iterator iter = pTokens.begin(); iter != pTokens.end(); ++iter)
+ {
+ currentRef = ((const json::UnknownElement &)currentRef)[*iter];
+ }
+ return currentRef;
+}
+
+void Client::setPrefR(std::deque<std::string> tokens, json::UnknownElement & element, json::UnknownElement & value)
+{
+ if(tokens.size())
+ {
+ std::string token = tokens.front();
+ tokens.pop_front();
+ setPrefR(tokens, element[token], value);
+ }
+ else
+ element = value;
+}
+
+void Client::SetPref(std::string property, json::UnknownElement & value)
+{
+ std::vector<std::string> pTokens = Client::explodePropertyString(property);
+ std::deque<std::string> dTokens(pTokens.begin(), pTokens.end());
+ std::string token = dTokens.front();
+ dTokens.pop_front();
+ setPrefR(dTokens, configDocument[token], value);
+}
diff --git a/src/client/Client.h b/src/client/Client.h
new file mode 100644
index 0000000..642fd63
--- /dev/null
+++ b/src/client/Client.h
@@ -0,0 +1,176 @@
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include <queue>
+#include <vector>
+#include <list>
+
+#include "Config.h"
+#include "Singleton.h"
+
+#include "User.h"
+
+#include "cajun/elements.h"
+
+class Thumbnail;
+class SaveInfo;
+class SaveFile;
+class SaveComment;
+class GameSave;
+
+enum LoginStatus {
+ LoginOkay, LoginError
+};
+
+enum RequestStatus {
+ RequestOkay, RequestFailure
+};
+
+class UpdateInfo
+{
+public:
+ enum BuildType { Stable, Beta, Snapshot };
+ std::string File;
+ int Major;
+ int Minor;
+ int Build;
+ int Time;
+ BuildType Type;
+ UpdateInfo() : Major(0), Minor(0), Build(0), Time(0), File(""), Type(Stable) {}
+ UpdateInfo(int major, int minor, int build, std::string file, BuildType type) : Major(major), Minor(minor), Build(build), Time(0), File(file), Type(type) {}
+ UpdateInfo(int time, std::string file, BuildType type) : Major(0), Minor(0), Build(0), Time(time), File(file), Type(type) {}
+};
+
+class ThumbnailListener;
+class ClientListener;
+class Client: public Singleton<Client> {
+private:
+ std::string messageOfTheDay;
+
+ void * versionCheckRequest;
+ bool updateAvailable;
+ UpdateInfo updateInfo;
+
+
+ std::string lastError;
+
+ std::list<std::string> stampIDs;
+ int lastStampTime;
+ int lastStampName;
+
+ //Auth session
+ User authUser;
+
+ //Thumbnail retreival
+ int thumbnailCacheNextID;
+ Thumbnail * thumbnailCache[THUMB_CACHE_SIZE];
+ void * activeThumbRequests[IMGCONNS];
+ int activeThumbRequestTimes[IMGCONNS];
+ int activeThumbRequestCompleteTimes[IMGCONNS];
+ std::string activeThumbRequestIDs[IMGCONNS];
+ void updateStamps();
+ static std::vector<std::string> explodePropertyString(std::string property);
+ void notifyUpdateAvailable();
+ void notifyAuthUserChanged();
+ void notifyMessageOfTheDay();
+
+ //Config file handle
+ json::Object configDocument;
+public:
+
+ std::vector<ClientListener*> listeners;
+
+ UpdateInfo GetUpdateInfo();
+
+ Client();
+ ~Client();
+
+ std::vector<std::string> DirectorySearch(std::string directory, std::string search, std::vector<std::string> extensions);
+ std::vector<std::string> DirectorySearch(std::string directory, std::string search, std::string extension);
+
+ bool DoInstallation();
+
+ std::vector<unsigned char> ReadFile(std::string filename);
+
+ void SetMessageOfTheDay(std::string message);
+ std::string GetMessageOfTheDay();
+
+ void Initialise(std::string proxyString);
+ void SetProxy(std::string proxy);
+
+ int MakeDirectory(const char * dirname);
+ void WriteFile(std::vector<unsigned char> fileData, std::string filename);
+ void WriteFile(std::vector<char> fileData, std::string filename);
+ bool FileExists(std::string filename);
+
+ void AddListener(ClientListener * listener);
+ void RemoveListener(ClientListener * listener);
+
+ RequestStatus ExecVote(int saveID, int direction);
+ RequestStatus UploadSave(SaveInfo & save);
+
+ SaveFile * GetStamp(std::string stampID);
+ void DeleteStamp(std::string stampID);
+ std::string AddStamp(GameSave * saveData);
+ std::vector<std::string> GetStamps(int start, int count);
+ void RescanStamps();
+ int GetStampsCount();
+ SaveFile * GetFirstStamp();
+
+ RequestStatus AddComment(int saveID, std::string comment);
+
+ unsigned char * GetSaveData(int saveID, int saveDate, int & dataLength);
+ std::vector<unsigned char> GetSaveData(int saveID, int saveDate);
+ LoginStatus Login(std::string username, std::string password, User & user);
+ void ClearThumbnailRequests();
+ std::vector<SaveInfo*> * SearchSaves(int start, int count, std::string query, std::string sort, std::string category, int & resultCount);
+ std::vector<std::pair<std::string, int> > * GetTags(int start, int count, std::string query, int & resultCount);
+ std::vector<SaveComment*> * GetComments(int saveID, int start, int count);
+ Thumbnail * GetPreview(int saveID, int saveDate);
+ Thumbnail * GetThumbnail(int saveID, int saveDate);
+ SaveInfo * GetSave(int saveID, int saveDate);
+ RequestStatus DeleteSave(int saveID);
+ RequestStatus ReportSave(int saveID, std::string message);
+ RequestStatus UnpublishSave(int saveID);
+ RequestStatus FavouriteSave(int saveID, bool favourite);
+ void SetAuthUser(User user);
+ User GetAuthUser();
+ std::vector<std::string> * RemoveTag(int saveID, std::string tag); //TODO RequestStatus
+ std::vector<std::string> * AddTag(int saveID, std::string tag);
+ std::string GetLastError() {
+ return lastError;
+ }
+ void Tick();
+ void Shutdown();
+
+ //Force flushing preferences to file on disk.
+ void WritePrefs();
+
+ std::string GetPrefString(std::string property, std::string defaultValue);
+ double GetPrefNumber(std::string property, double defaultValue);
+ int GetPrefInteger(std::string property, int defaultValue);
+ unsigned int GetPrefUInteger(std::string property, unsigned int defaultValue);
+ std::vector<std::string> GetPrefStringArray(std::string property);
+ std::vector<double> GetPrefNumberArray(std::string property);
+ std::vector<int> GetPrefIntegerArray(std::string property);
+ std::vector<unsigned int> GetPrefUIntegerArray(std::string property);
+ std::vector<bool> GetPrefBoolArray(std::string property);
+ bool GetPrefBool(std::string property, bool defaultValue);
+
+ void SetPref(std::string property, std::string value);
+ void SetPref(std::string property, double value);
+ void SetPref(std::string property, int value);
+ void SetPref(std::string property, unsigned int value);
+ void SetPref(std::string property, std::vector<std::string> value);
+ void SetPref(std::string property, std::vector<double> value);
+ void SetPref(std::string property, std::vector<int> value);
+ void SetPref(std::string property, std::vector<unsigned int> value);
+ void SetPref(std::string property, std::vector<bool> value);
+ void SetPref(std::string property, bool value);
+
+ json::UnknownElement GetPref(std::string property);
+ void setPrefR(std::deque<std::string> tokens, json::UnknownElement & element, json::UnknownElement & value);
+ void SetPref(std::string property, json::UnknownElement & value);
+};
+
+#endif // CLIENT_H
diff --git a/src/client/ClientListener.h b/src/client/ClientListener.h
new file mode 100644
index 0000000..07e784c
--- /dev/null
+++ b/src/client/ClientListener.h
@@ -0,0 +1,24 @@
+/*
+ * ClientListener.h
+ *
+ * Created on: Jun 19, 2012
+ * Author: Simon
+ */
+
+#ifndef CLIENTLISTENER_H_
+#define CLIENTLISTENER_H_
+
+class Client;
+class ClientListener
+{
+public:
+ ClientListener() {}
+ virtual ~ClientListener() {}
+
+ virtual void NotifyUpdateAvailable(Client * sender) {}
+ virtual void NotifyAuthUserChanged(Client * sender) {}
+ virtual void NotifyMessageOfTheDay(Client * sender) {}
+};
+
+
+#endif /* CLIENTLISTENER_H_ */
diff --git a/src/client/GameSave.cpp b/src/client/GameSave.cpp
new file mode 100644
index 0000000..1751c54
--- /dev/null
+++ b/src/client/GameSave.cpp
@@ -0,0 +1,2092 @@
+#include <iostream>
+#include <sstream>
+#include <cmath>
+#include <vector>
+#include <bzlib.h>
+#include "Config.h"
+#include "bson/BSON.h"
+#include "GameSave.h"
+#include "simulation/SimulationData.h"
+#include "ElementClasses.h"
+extern "C"
+{
+ #include "hmap.h"
+}
+
+GameSave::GameSave(GameSave & save) :
+waterEEnabled(save.waterEEnabled),
+legacyEnable(save.legacyEnable),
+gravityEnable(save.gravityEnable),
+paused(save.paused),
+gravityMode(save.gravityMode),
+airMode(save.airMode),
+signs(save.signs),
+expanded(save.expanded),
+hasOriginalData(save.hasOriginalData),
+originalData(save.originalData),
+palette(save.palette)
+{
+ blockMap = NULL;
+ blockMapPtr = NULL;
+ fanVelX = NULL;
+ fanVelXPtr = NULL;
+ fanVelY = NULL;
+ fanVelYPtr = NULL;
+ particles = NULL;
+ if(save.expanded)
+ {
+ setSize(save.blockWidth, save.blockHeight);
+
+ std::copy(save.particles, save.particles+NPART, particles);
+ std::copy(save.blockMapPtr, save.blockMapPtr+(blockHeight*blockWidth), blockMapPtr);
+ std::copy(save.fanVelXPtr, save.fanVelXPtr+(blockHeight*blockWidth), fanVelXPtr);
+ std::copy(save.fanVelYPtr, save.fanVelYPtr+(blockHeight*blockWidth), fanVelYPtr);
+ }
+ else
+ {
+ blockWidth = save.blockWidth;
+ blockHeight = save.blockHeight;
+ }
+ particlesCount = save.particlesCount;
+}
+
+GameSave::GameSave(int width, int height)
+{
+ blockMap = NULL;
+ blockMapPtr = NULL;
+ fanVelX = NULL;
+ fanVelXPtr = NULL;
+ fanVelY = NULL;
+ fanVelYPtr = NULL;
+ particles = NULL;
+
+ hasOriginalData = false;
+ expanded = true;
+ setSize(width, height);
+}
+
+GameSave::GameSave(std::vector<char> data)
+{
+ blockWidth = 0;
+ blockHeight = 0;
+
+ blockMap = NULL;
+ blockMapPtr = NULL;
+ fanVelX = NULL;
+ fanVelXPtr = NULL;
+ fanVelY = NULL;
+ fanVelYPtr = NULL;
+ particles = NULL;
+
+ expanded = false;
+ hasOriginalData = true;
+ originalData = data;
+#ifdef DEBUG
+ std::cout << "Creating Collapsed save from data" << std::endl;
+#endif
+ try
+ {
+ Expand();
+ }
+ catch(ParseException & e)
+ {
+ std::cout << e.what() << std::endl;
+ dealloc(); //Free any allocated memory
+ throw;
+ }
+ Collapse();
+}
+
+GameSave::GameSave(std::vector<unsigned char> data)
+{
+ blockWidth = 0;
+ blockHeight = 0;
+
+ blockMap = NULL;
+ blockMapPtr = NULL;
+ fanVelX = NULL;
+ fanVelXPtr = NULL;
+ fanVelY = NULL;
+ fanVelYPtr = NULL;
+ particles = NULL;
+
+ expanded = false;
+ hasOriginalData = true;
+ originalData = std::vector<char>(data.begin(), data.end());
+#ifdef DEBUG
+ std::cout << "Creating Collapsed save from data" << std::endl;
+#endif
+ try
+ {
+ Expand();
+ }
+ catch(ParseException & e)
+ {
+ std::cout << e.what() << std::endl;
+ dealloc(); //Free any allocated memory
+ throw;
+ }
+ Collapse();
+}
+
+GameSave::GameSave(char * data, int dataSize)
+{
+ blockWidth = 0;
+ blockHeight = 0;
+
+ blockMap = NULL;
+ blockMapPtr = NULL;
+ fanVelX = NULL;
+ fanVelXPtr = NULL;
+ fanVelY = NULL;
+ fanVelYPtr = NULL;
+ particles = NULL;
+
+ expanded = false;
+ hasOriginalData = true;
+ originalData = std::vector<char>(data, data+dataSize);
+#ifdef DEBUG
+ std::cout << "Creating Expanded save from data" << std::endl;
+#endif
+ try
+ {
+ Expand();
+ }
+ catch(ParseException & e)
+ {
+ std::cout << e.what() << std::endl;
+ dealloc(); //Free any allocated memory
+ throw;
+ }
+ //Collapse();
+}
+
+bool GameSave::Collapsed()
+{
+ return !expanded;
+}
+
+void GameSave::Expand()
+{
+ if(hasOriginalData && !expanded)
+ {
+ expanded = true;
+ read(&originalData[0], originalData.size());
+ }
+}
+
+void GameSave::Collapse()
+{
+ if(expanded && hasOriginalData)
+ {
+ expanded = false;
+ if(particles)
+ {
+ delete[] particles;
+ particles = NULL;
+ }
+ if(blockMap)
+ {
+ delete[] blockMap;
+ blockMap = NULL;
+ }
+ if(blockMapPtr)
+ {
+ delete[] blockMapPtr;
+ blockMapPtr = NULL;
+ }
+ if(fanVelX)
+ {
+ delete[] fanVelX;
+ fanVelX = NULL;
+ }
+ if(fanVelXPtr)
+ {
+ delete[] fanVelXPtr;
+ fanVelXPtr = NULL;
+ }
+ if(fanVelY)
+ {
+ delete[] fanVelY;
+ fanVelY = NULL;
+ }
+ if(fanVelYPtr)
+ {
+ delete[] fanVelYPtr;
+ fanVelYPtr = NULL;
+ }
+ }
+}
+
+void GameSave::read(char * data, int dataSize)
+{
+ if(dataSize > 0)
+ {
+ if(data[0] == 0x50 || data[0] == 0x66)
+ {
+#ifdef DEBUG
+ std::cout << "Reading PSv..." << std::endl;
+#endif
+ readPSv(data, dataSize);
+ }
+ else if(data[0] == 'O')
+ {
+#ifdef DEBUG
+ std::cout << "Reading OPS..." << std::endl;
+#endif
+ readOPS(data, dataSize);
+ }
+ else
+ {
+ std::cerr << "Got Magic number '" << data[0] << "'" << std::endl;
+ throw ParseException(ParseException::Corrupt, "Invalid save format");
+ }
+ }
+ else
+ {
+ throw ParseException(ParseException::Corrupt, "No data");
+ }
+}
+
+void GameSave::setSize(int newWidth, int newHeight)
+{
+ this->blockWidth = newWidth;
+ this->blockHeight = newHeight;
+
+ particlesCount = 0;
+ particles = new Particle[NPART];
+
+ blockMapPtr = new unsigned char[blockHeight*blockWidth];
+ std::fill(blockMapPtr, blockMapPtr+(blockHeight*blockWidth), 0);
+ fanVelXPtr = new float[(blockHeight)*(blockWidth)];
+ std::fill(fanVelXPtr, fanVelXPtr+((blockHeight)*(blockWidth)), 0);
+ fanVelYPtr = new float[(blockHeight)*(blockWidth)];
+ std::fill(fanVelYPtr, fanVelYPtr+((blockHeight)*(blockWidth)), 0);
+
+ blockMap = new unsigned char*[blockHeight];
+ for(int y = 0; y < blockHeight; y++)
+ blockMap[y] = &blockMapPtr[y*blockWidth];
+ fanVelX = new float*[blockHeight];
+ for(int y = 0; y < blockHeight; y++)
+ fanVelX[y] = &fanVelXPtr[y*(blockWidth)];
+ fanVelY = new float*[blockHeight];
+ for(int y = 0; y < blockHeight; y++)
+ fanVelY[y] = &fanVelYPtr[y*blockWidth];
+}
+
+std::vector<char> GameSave::Serialise()
+{
+ int dataSize;
+ char * data = Serialise(dataSize);
+ std::vector<char> dataVect(data, data+dataSize);
+ delete data;
+ return dataVect;
+}
+
+char * GameSave::Serialise(int & dataSize)
+{
+ return serialiseOPS(dataSize);
+}
+
+void GameSave::Transform(matrix2d transform, vector2d translate)
+{
+ if(Collapsed())
+ Expand();
+ int i, x, y, nx, ny, width = blockWidth*CELL, height = blockHeight*CELL, newWidth, newHeight, newBlockWidth, newBlockHeight;
+ vector2d pos, tmp, ctl, cbr, vel;
+ vector2d cornerso[4];
+ // undo any translation caused by rotation
+ cornerso[0] = v2d_new(0,0);
+ cornerso[1] = v2d_new(width-1,0);
+ cornerso[2] = v2d_new(0,height-1);
+ cornerso[3] = v2d_new(width-1,height-1);
+ for (i=0; i<4; i++)
+ {
+ tmp = m2d_multiply_v2d(transform,cornerso[i]);
+ if (i==0) ctl = cbr = tmp; // top left, bottom right corner
+ if (tmp.x<ctl.x) ctl.x = tmp.x;
+ if (tmp.y<ctl.y) ctl.y = tmp.y;
+ if (tmp.x>cbr.x) cbr.x = tmp.x;
+ if (tmp.y>cbr.y) cbr.y = tmp.y;
+ }
+ // casting as int doesn't quite do what we want with negative numbers, so use floor()
+ tmp = v2d_new(floor(ctl.x+0.5f),floor(ctl.y+0.5f));
+ translate = v2d_sub(translate,tmp);
+ newWidth = floor(cbr.x+0.5f)-floor(ctl.x+0.5f)+1;
+ newHeight = floor(cbr.y+0.5f)-floor(ctl.y+0.5f)+1;
+ if (newWidth>XRES) newWidth = XRES;
+ if (newHeight>YRES) newHeight = YRES;
+ newBlockWidth = newWidth/CELL;
+ newBlockHeight = newHeight/CELL;
+
+ unsigned char ** blockMapNew;
+ float ** fanVelXNew;
+ float ** fanVelYNew;
+
+ float * fanVelXPtrNew;
+ float * fanVelYPtrNew;
+ unsigned char * blockMapPtrNew;
+
+ blockMapPtrNew = new unsigned char[newBlockHeight*newBlockWidth];
+ std::fill(blockMapPtrNew, blockMapPtrNew+(newBlockHeight*newBlockWidth), 0);
+ fanVelXPtrNew = new float[newBlockHeight*newBlockWidth];
+ std::fill(fanVelXPtrNew, fanVelXPtrNew+(newBlockHeight*newBlockWidth), 0);
+ fanVelYPtrNew = new float[(newBlockHeight)*(newBlockWidth)];
+ std::fill(fanVelYPtrNew, fanVelYPtrNew+(newBlockHeight*newBlockWidth), 0);
+
+ blockMapNew = new unsigned char*[newBlockHeight];
+ for(int y = 0; y < newBlockHeight; y++)
+ blockMapNew[y] = &blockMapPtrNew[y*newBlockWidth];
+ fanVelXNew = new float*[newBlockHeight];
+ for(int y = 0; y < newBlockHeight; y++)
+ fanVelXNew[y] = &fanVelXPtrNew[y*newBlockWidth];
+ fanVelYNew = new float*[newBlockHeight];
+ for(int y = 0; y < newBlockHeight; y++)
+ fanVelYNew[y] = &fanVelYPtrNew[y*newBlockWidth];
+
+ // rotate and translate signs, parts, walls
+ for (i=0; i < signs.size(); i++)
+ {
+ pos = v2d_new(signs[i].x, signs[i].y);
+ pos = v2d_add(m2d_multiply_v2d(transform,pos),translate);
+ nx = floor(pos.x+0.5f);
+ ny = floor(pos.y+0.5f);
+ if (nx<0 || nx>=newWidth || ny<0 || ny>=newHeight)
+ {
+ signs[i].text[0] = 0;
+ continue;
+ }
+ signs[i].x = nx;
+ signs[i].y = ny;
+ }
+ for (i=0; i<NPART; i++)
+ {
+ if (!particles[i].type) continue;
+ pos = v2d_new(particles[i].x, particles[i].y);
+ pos = v2d_add(m2d_multiply_v2d(transform,pos),translate);
+ nx = floor(pos.x+0.5f);
+ ny = floor(pos.y+0.5f);
+ if (nx<0 || nx>=newWidth || ny<0 || ny>=newHeight)
+ {
+ particles[i].type = PT_NONE;
+ continue;
+ }
+ particles[i].x = nx;
+ particles[i].y = ny;
+ vel = v2d_new(particles[i].vx, particles[i].vy);
+ vel = m2d_multiply_v2d(transform, vel);
+ particles[i].vx = vel.x;
+ particles[i].vy = vel.y;
+ }
+ for (y=0; y<blockHeight; y++)
+ for (x=0; x<blockWidth; x++)
+ {
+ pos = v2d_new(x*CELL+CELL*0.4f, y*CELL+CELL*0.4f);
+ pos = v2d_add(m2d_multiply_v2d(transform,pos),translate);
+ nx = pos.x/CELL;
+ ny = pos.y/CELL;
+ if (nx<0 || nx>=newBlockWidth || ny<0 || ny>=newBlockHeight)
+ continue;
+ if (blockMap[y][x])
+ {
+ blockMapNew[ny][nx] = blockMap[y][x];
+ if (blockMap[y][x]==WL_FAN)
+ {
+ vel = v2d_new(fanVelX[y][x], fanVelY[y][x]);
+ vel = m2d_multiply_v2d(transform, vel);
+ fanVelXNew[ny][nx] = vel.x;
+ fanVelYNew[ny][nx] = vel.y;
+ }
+ }
+ }
+ //ndata = build_save(size,0,0,nw,nh,blockMapNew,vxn,vyn,pvn,fanVelXNew,fanVelYNew,signst,partst);
+ blockWidth = newBlockWidth;
+ blockHeight = newBlockHeight;
+
+ delete blockMap;
+ delete fanVelX;
+ delete fanVelY;
+
+ delete blockMapPtr;
+ delete fanVelXPtr;
+ delete fanVelYPtr;
+
+ blockMap = blockMapNew;
+ fanVelX = fanVelXNew;
+ fanVelY = fanVelYNew;
+
+ blockMapPtr = (unsigned char*)blockMapPtrNew;
+ fanVelXPtr = (float*)fanVelXPtrNew;
+ fanVelYPtr = (float*)fanVelYPtrNew;
+}
+
+void GameSave::readOPS(char * data, int dataLength)
+{
+ unsigned char * inputData = (unsigned char*)data, *bsonData = NULL, *partsData = NULL, *partsPosData = NULL, *fanData = NULL, *wallData = NULL, *soapLinkData = NULL;
+ unsigned int inputDataLen = dataLength, bsonDataLen = 0, partsDataLen, partsPosDataLen, fanDataLen, wallDataLen, soapLinkDataLen;
+ unsigned partsCount = 0, *partsSimIndex = NULL;
+ int i, freeIndicesCount, x, y, j;
+ int *freeIndices = NULL;
+ int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH;
+ int savedVersion = inputData[4];
+ bson b;
+ bson_iterator iter;
+
+ //Block sizes
+ blockX = 0;
+ blockY = 0;
+ blockW = inputData[6];
+ blockH = inputData[7];
+
+ //Full size, normalised
+ fullX = blockX*CELL;
+ fullY = blockY*CELL;
+ fullW = blockW*CELL;
+ fullH = blockH*CELL;
+
+ //From newer version
+ if(savedVersion > SAVE_VERSION)
+ throw ParseException(ParseException::WrongVersion, "Save from newer version");
+
+ //Incompatible cell size
+ if(inputData[5] > CELL)
+ throw ParseException(ParseException::InvalidDimensions, "Incorrect CELL size");
+
+ //Too large/off screen
+ if(blockX+blockW > XRES/CELL || blockY+blockH > YRES/CELL)
+ throw ParseException(ParseException::InvalidDimensions, "Save too large");
+
+ setSize(blockW, blockH);
+
+ bsonDataLen = ((unsigned)inputData[8]);
+ bsonDataLen |= ((unsigned)inputData[9]) << 8;
+ bsonDataLen |= ((unsigned)inputData[10]) << 16;
+ bsonDataLen |= ((unsigned)inputData[11]) << 24;
+
+ bsonData = (unsigned char*)malloc(bsonDataLen+1);
+ if(!bsonData)
+ throw ParseException(ParseException::InternalError, "Unable to allocate memory");
+
+ //Make sure bsonData is null terminated, since all string functions need null terminated strings
+ //(bson_iterator_key returns a pointer into bsonData, which is then used with strcmp)
+ bsonData[bsonDataLen] = 0;
+
+ if (BZ2_bzBuffToBuffDecompress((char*)bsonData, &bsonDataLen, (char*)(inputData+12), inputDataLen-12, 0, 0))
+ throw ParseException(ParseException::Corrupt, "Unable to decompress");
+
+ bson_init_data(&b, (char*)bsonData);
+ bson_iterator_init(&iter, &b);
+
+ std::vector<sign> tempSigns;
+
+ while(bson_iterator_next(&iter))
+ {
+ if(strcmp(bson_iterator_key(&iter), "signs")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_ARRAY)
+ {
+ bson_iterator subiter;
+ bson_iterator_subiterator(&iter, &subiter);
+ while(bson_iterator_next(&subiter))
+ {
+ if(strcmp(bson_iterator_key(&subiter), "sign")==0)
+ {
+ if(bson_iterator_type(&subiter)==BSON_OBJECT)
+ {
+ bson_iterator signiter;
+ bson_iterator_subiterator(&subiter, &signiter);
+
+ sign tempSign("", 0, 0, sign::Left);
+ while(bson_iterator_next(&signiter))
+ {
+ if(strcmp(bson_iterator_key(&signiter), "text")==0 && bson_iterator_type(&signiter)==BSON_STRING)
+ {
+ char tempString[256];
+ strncpy(tempString, bson_iterator_string(&signiter), 255);
+ tempString[255] = 0;
+ clean_text((char*)tempSign.text.c_str(), 158-14);
+
+ tempSign.text = tempString;
+ }
+ else if(strcmp(bson_iterator_key(&signiter), "justification")==0 && bson_iterator_type(&signiter)==BSON_INT)
+ {
+ tempSign.ju = (sign::Justification)bson_iterator_int(&signiter);
+ }
+ else if(strcmp(bson_iterator_key(&signiter), "x")==0 && bson_iterator_type(&signiter)==BSON_INT)
+ {
+ tempSign.x = bson_iterator_int(&signiter)+fullX;
+ }
+ else if(strcmp(bson_iterator_key(&signiter), "y")==0 && bson_iterator_type(&signiter)==BSON_INT)
+ {
+ tempSign.y = bson_iterator_int(&signiter)+fullY;
+ }
+ else
+ {
+ fprintf(stderr, "Unknown sign property %s\n", bson_iterator_key(&signiter));
+ }
+ }
+ tempSigns.push_back(tempSign);
+ }
+ else
+ {
+ fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&subiter));
+ }
+ }
+ }
+ }
+ else
+ {
+ fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
+ }
+ }
+ else if(strcmp(bson_iterator_key(&iter), "parts")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_BINDATA && ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER && (partsDataLen = bson_iterator_bin_len(&iter)) > 0)
+ {
+ partsData = (unsigned char*)bson_iterator_bin_data(&iter);
+ }
+ else
+ {
+ fprintf(stderr, "Invalid datatype of particle data: %d[%d] %d[%d] %d[%d]\n", bson_iterator_type(&iter), bson_iterator_type(&iter)==BSON_BINDATA, (unsigned char)bson_iterator_bin_type(&iter), ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER, bson_iterator_bin_len(&iter), bson_iterator_bin_len(&iter)>0);
+ }
+ }
+ if(strcmp(bson_iterator_key(&iter), "partsPos")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_BINDATA && ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER && (partsPosDataLen = bson_iterator_bin_len(&iter)) > 0)
+ {
+ partsPosData = (unsigned char*)bson_iterator_bin_data(&iter);
+ }
+ else
+ {
+ fprintf(stderr, "Invalid datatype of particle position data: %d[%d] %d[%d] %d[%d]\n", bson_iterator_type(&iter), bson_iterator_type(&iter)==BSON_BINDATA, (unsigned char)bson_iterator_bin_type(&iter), ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER, bson_iterator_bin_len(&iter), bson_iterator_bin_len(&iter)>0);
+ }
+ }
+ else if(strcmp(bson_iterator_key(&iter), "wallMap")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_BINDATA && ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER && (wallDataLen = bson_iterator_bin_len(&iter)) > 0)
+ {
+ wallData = (unsigned char*)bson_iterator_bin_data(&iter);
+ }
+ else
+ {
+ fprintf(stderr, "Invalid datatype of wall data: %d[%d] %d[%d] %d[%d]\n", bson_iterator_type(&iter), bson_iterator_type(&iter)==BSON_BINDATA, (unsigned char)bson_iterator_bin_type(&iter), ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER, bson_iterator_bin_len(&iter), bson_iterator_bin_len(&iter)>0);
+ }
+ }
+ else if(strcmp(bson_iterator_key(&iter), "fanMap")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_BINDATA && ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER && (fanDataLen = bson_iterator_bin_len(&iter)) > 0)
+ {
+ fanData = (unsigned char*)bson_iterator_bin_data(&iter);
+ }
+ else
+ {
+ fprintf(stderr, "Invalid datatype of fan data: %d[%d] %d[%d] %d[%d]\n", bson_iterator_type(&iter), bson_iterator_type(&iter)==BSON_BINDATA, (unsigned char)bson_iterator_bin_type(&iter), ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER, bson_iterator_bin_len(&iter), bson_iterator_bin_len(&iter)>0);
+ }
+ }
+ else if(strcmp(bson_iterator_key(&iter), "soapLinks")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_BINDATA && ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER && (soapLinkDataLen = bson_iterator_bin_len(&iter)) > 0)
+ {
+ soapLinkData = (unsigned char *)bson_iterator_bin_data(&iter);
+ }
+ else
+ {
+ fprintf(stderr, "Invalid datatype of soap data: %d[%d] %d[%d] %d[%d]\n", bson_iterator_type(&iter), bson_iterator_type(&iter)==BSON_BINDATA, (unsigned char)bson_iterator_bin_type(&iter), ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER, bson_iterator_bin_len(&iter), bson_iterator_bin_len(&iter)>0);
+ }
+ }
+ else if(strcmp(bson_iterator_key(&iter), "legacyEnable")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_BOOL)
+ {
+ legacyEnable = bson_iterator_bool(&iter);
+ }
+ else
+ {
+ fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
+ }
+ }
+ else if(strcmp(bson_iterator_key(&iter), "gravityEnable")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_BOOL)
+ {
+ gravityEnable = bson_iterator_bool(&iter);
+ }
+ else
+ {
+ fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
+ }
+ }
+ else if(strcmp(bson_iterator_key(&iter), "waterEEnabled")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_BOOL)
+ {
+ waterEEnabled = bson_iterator_bool(&iter);
+ }
+ else
+ {
+ fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
+ }
+ }
+ else if(strcmp(bson_iterator_key(&iter), "paused")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_BOOL)
+ {
+ paused = bson_iterator_bool(&iter);
+ }
+ else
+ {
+ fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
+ }
+ }
+ else if(strcmp(bson_iterator_key(&iter), "gravityMode")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_INT)
+ {
+ gravityMode = bson_iterator_int(&iter);
+ }
+ else
+ {
+ fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
+ }
+ }
+ else if(strcmp(bson_iterator_key(&iter), "airMode")==0)
+ {
+ if(bson_iterator_type(&iter)==BSON_INT)
+ {
+ airMode = bson_iterator_int(&iter);
+ }
+ else
+ {
+ fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
+ }
+ }
+ else if(strcmp(bson_iterator_key(&iter), "palette")==0)
+ {
+ palette.clear();
+ if(bson_iterator_type(&iter)==BSON_ARRAY)
+ {
+ bson_iterator subiter;
+ bson_iterator_subiterator(&iter, &subiter);
+ while(bson_iterator_next(&subiter))
+ {
+ if(bson_iterator_type(&subiter)==BSON_INT)
+ {
+ std::string id = std::string(bson_iterator_key(&subiter));
+ int num = bson_iterator_int(&subiter);
+ palette.push_back(PaletteItem(id, num));
+ }
+ }
+ }
+ }
+ }
+
+ //Read wall and fan data
+ if(wallData)
+ {
+ j = 0;
+ if(blockW * blockH > wallDataLen)
+ {
+ fprintf(stderr, "Not enough wall data\n");
+ goto fail;
+ }
+ for(x = 0; x < blockW; x++)
+ {
+ for(y = 0; y < blockH; y++)
+ {
+ if (wallData[y*blockW+x])
+ blockMap[blockY+y][blockX+x] = wallData[y*blockW+x];
+
+ if (blockMap[y][x]==O_WL_WALLELEC)
+ blockMap[y][x]=WL_WALLELEC;
+ if (blockMap[y][x]==O_WL_EWALL)
+ blockMap[y][x]=WL_EWALL;
+ if (blockMap[y][x]==O_WL_DETECT)
+ blockMap[y][x]=WL_DETECT;
+ if (blockMap[y][x]==O_WL_STREAM)
+ blockMap[y][x]=WL_STREAM;
+ if (blockMap[y][x]==O_WL_FAN||blockMap[y][x]==O_WL_FANHELPER)
+ blockMap[y][x]=WL_FAN;
+ if (blockMap[y][x]==O_WL_ALLOWLIQUID)
+ blockMap[y][x]=WL_ALLOWLIQUID;
+ if (blockMap[y][x]==O_WL_DESTROYALL)
+ blockMap[y][x]=WL_DESTROYALL;
+ if (blockMap[y][x]==O_WL_ERASE)
+ blockMap[y][x]=WL_ERASE;
+ if (blockMap[y][x]==O_WL_WALL)
+ blockMap[y][x]=WL_WALL;
+ if (blockMap[y][x]==O_WL_ALLOWAIR)
+ blockMap[y][x]=WL_ALLOWAIR;
+ if (blockMap[y][x]==O_WL_ALLOWSOLID)
+ blockMap[y][x]=WL_ALLOWSOLID;
+ if (blockMap[y][x]==O_WL_ALLOWALLELEC)
+ blockMap[y][x]=WL_ALLOWALLELEC;
+ if (blockMap[y][x]==O_WL_EHOLE)
+ blockMap[y][x]=WL_EHOLE;
+ if (blockMap[y][x]==O_WL_ALLOWGAS)
+ blockMap[y][x]=WL_ALLOWGAS;
+ if (blockMap[y][x]==O_WL_GRAV)
+ blockMap[y][x]=WL_GRAV;
+ if (blockMap[y][x]==O_WL_ALLOWENERGY)
+ blockMap[y][x]=WL_ALLOWENERGY;
+
+ if (blockMap[y][x] == WL_FAN && fanData)
+ {
+ if(j+1 >= fanDataLen)
+ {
+ fprintf(stderr, "Not enough fan data\n");
+ }
+ fanVelX[blockY+y][blockX+x] = (fanData[j++]-127.0f)/64.0f;
+ fanVelY[blockY+y][blockX+x] = (fanData[j++]-127.0f)/64.0f;
+ }
+ }
+ }
+ }
+
+ //Read particle data
+ if(partsData && partsPosData)
+ {
+ int newIndex = 0, fieldDescriptor, tempTemp;
+ int posCount, posTotal, partsPosDataIndex = 0;
+ int saved_x, saved_y;
+ if(fullW * fullH * 3 > partsPosDataLen)
+ {
+ fprintf(stderr, "Not enough particle position data\n");
+ goto fail;
+ }
+
+ partsSimIndex = (unsigned int*)calloc(NPART, sizeof(unsigned));
+ partsCount = 0;
+
+ i = 0;
+ newIndex = 0;
+ for (saved_y=0; saved_y<fullH; saved_y++)
+ {
+ for (saved_x=0; saved_x<fullW; saved_x++)
+ {
+ //Read total number of particles at this position
+ posTotal = 0;
+ posTotal |= partsPosData[partsPosDataIndex++]<<16;
+ posTotal |= partsPosData[partsPosDataIndex++]<<8;
+ posTotal |= partsPosData[partsPosDataIndex++];
+ //Put the next posTotal particles at this position
+ for (posCount=0; posCount<posTotal; posCount++)
+ {
+ particlesCount = newIndex+1;
+ if(newIndex>=NPART)
+ {
+ goto fail;
+ }
+
+ //i+3 because we have 4 bytes of required fields (type (1), descriptor (2), temp (1))
+ if (i+3 >= partsDataLen)
+ goto fail;
+ x = saved_x + fullX;
+ y = saved_y + fullY;
+ fieldDescriptor = partsData[i+1];
+ fieldDescriptor |= partsData[i+2] << 8;
+ if(x >= fullW || x < 0 || y >= fullH || y < 0)
+ {
+ fprintf(stderr, "Out of range [%d]: %d %d, [%d, %d], [%d, %d]\n", i, x, y, (unsigned)partsData[i+1], (unsigned)partsData[i+2], (unsigned)partsData[i+3], (unsigned)partsData[i+4]);
+ goto fail;
+ }
+ if(partsData[i] >= PT_NUM)
+ partsData[i] = PT_DMND; //Replace all invalid elements with diamond
+
+ if(newIndex < 0 || newIndex >= NPART)
+ goto fail;
+
+ //Store partsptr index+1 for this saved particle index (0 means not loaded)
+ partsSimIndex[partsCount++] = newIndex+1;
+
+ //Clear the particle, ready for our new properties
+ memset(&(particles[newIndex]), 0, sizeof(Particle));
+
+ //Required fields
+ particles[newIndex].type = partsData[i];
+ particles[newIndex].x = x;
+ particles[newIndex].y = y;
+ i+=3;
+
+ //Read temp
+ if(fieldDescriptor & 0x01)
+ {
+ //Full 16bit int
+ tempTemp = partsData[i++];
+ tempTemp |= (((unsigned)partsData[i++]) << 8);
+ particles[newIndex].temp = tempTemp;
+ }
+ else
+ {
+ //1 Byte room temp offset
+ tempTemp = (char)partsData[i++];
+ particles[newIndex].temp = tempTemp+294.15f;
+ }
+
+ //Read life
+ if(fieldDescriptor & 0x02)
+ {
+ if(i >= partsDataLen) goto fail;
+ particles[newIndex].life = partsData[i++];
+ //Read 2nd byte
+ if(fieldDescriptor & 0x04)
+ {
+ if(i >= partsDataLen) goto fail;
+ particles[newIndex].life |= (((unsigned)partsData[i++]) << 8);
+ }
+ }
+
+ //Read tmp
+ if(fieldDescriptor & 0x08)
+ {
+ if(i >= partsDataLen) goto fail;
+ particles[newIndex].tmp = partsData[i++];
+ //Read 2nd byte
+ if(fieldDescriptor & 0x10)
+ {
+ if(i >= partsDataLen) goto fail;
+ particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 8);
+ //Read 3rd and 4th bytes
+ if(fieldDescriptor & 0x1000)
+ {
+ if(i+1 >= partsDataLen) goto fail;
+ particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 24);
+ particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 16);
+ }
+ }
+ }
+
+ //Read ctype
+ if(fieldDescriptor & 0x20)
+ {
+ if(i >= partsDataLen) goto fail;
+ particles[newIndex].ctype = partsData[i++];
+ //Read additional bytes
+ if(fieldDescriptor & 0x200)
+ {
+ if(i+2 >= partsDataLen) goto fail;
+ particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 24);
+ particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 16);
+ particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 8);
+ }
+ }
+
+ //Read dcolour
+ if(fieldDescriptor & 0x40)
+ {
+ if(i+3 >= partsDataLen) goto fail;
+ particles[newIndex].dcolour = (((unsigned)partsData[i++]) << 24);
+ particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 16);
+ particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 8);
+ particles[newIndex].dcolour |= ((unsigned)partsData[i++]);
+ }
+
+ //Read vx
+ if(fieldDescriptor & 0x80)
+ {
+ if(i >= partsDataLen) goto fail;
+ particles[newIndex].vx = (partsData[i++]-127.0f)/16.0f;
+ }
+
+ //Read vy
+ if(fieldDescriptor & 0x100)
+ {
+ if(i >= partsDataLen) goto fail;
+ particles[newIndex].vy = (partsData[i++]-127.0f)/16.0f;
+ }
+
+ //Read tmp2
+ if(fieldDescriptor & 0x400)
+ {
+ if(i >= partsDataLen) goto fail;
+ particles[newIndex].tmp2 = partsData[i++];
+ if(fieldDescriptor & 0x800)
+ {
+ if(i >= partsDataLen) goto fail;
+ particles[newIndex].tmp2 |= (((unsigned)partsData[i++]) << 8);
+ }
+ }
+
+ //Particle specific parsing:
+ switch(particles[newIndex].type)
+ {
+ case PT_SOAP:
+ //Clear soap links, links will be added back in if soapLinkData is present
+ particles[newIndex].ctype &= ~6;
+ break;
+ case PT_BOMB:
+ if (particles[newIndex].tmp!=0 && savedVersion < 81)
+ {
+ particles[newIndex].type = PT_EMBR;
+ particles[newIndex].ctype = 0;
+ if (particles[newIndex].tmp==1)
+ particles[newIndex].tmp = 0;
+ }
+ break;
+ case PT_DUST:
+ if (particles[newIndex].life>0 && savedVersion < 81)
+ {
+ particles[newIndex].type = PT_EMBR;
+ particles[newIndex].ctype = (particles[newIndex].tmp2<<16) | (particles[newIndex].tmp<<8) | particles[newIndex].ctype;
+ particles[newIndex].tmp = 1;
+ }
+ break;
+ case PT_FIRW:
+ if (particles[newIndex].tmp>=2 && savedVersion < 81)
+ {
+ int caddress = restrict_flt(restrict_flt((float)(particles[newIndex].tmp-4), 0.0f, 200.0f)*3, 0.0f, (200.0f*3)-3);
+ particles[newIndex].type = PT_EMBR;
+ particles[newIndex].tmp = 1;
+ particles[newIndex].ctype = (((unsigned char)(firw_data[caddress]))<<16) | (((unsigned char)(firw_data[caddress+1]))<<8) | ((unsigned char)(firw_data[caddress+2]));
+ }
+ break;
+ }
+ newIndex++;
+ }
+ }
+ }
+ if (soapLinkData)
+ {
+ int soapLinkDataPos = 0;
+ for (i=0; i<partsCount; i++)
+ {
+ if (partsSimIndex[i] && particles[partsSimIndex[i]-1].type == PT_SOAP)
+ {
+ // Get the index of the particle forward linked from this one, if present in the save data
+ int linkedIndex = 0;
+ if (soapLinkDataPos+3 > soapLinkDataLen) break;
+ linkedIndex |= soapLinkData[soapLinkDataPos++]<<16;
+ linkedIndex |= soapLinkData[soapLinkDataPos++]<<8;
+ linkedIndex |= soapLinkData[soapLinkDataPos++];
+ // All indexes in soapLinkData and partsSimIndex have 1 added to them (0 means not saved/loaded)
+ if (!linkedIndex || linkedIndex-1>=partsCount || !partsSimIndex[linkedIndex-1])
+ continue;
+ linkedIndex = partsSimIndex[linkedIndex-1]-1;
+ newIndex = partsSimIndex[i]-1;
+
+ //Attach the two particles
+ particles[newIndex].ctype |= 2;
+ particles[newIndex].tmp = linkedIndex;
+ particles[linkedIndex].ctype |= 4;
+ particles[linkedIndex].tmp2 = newIndex;
+ }
+ }
+ }
+ }
+
+ if(tempSigns.size())
+ {
+ for (int i = 0; i < tempSigns.size(); i++)
+ {
+ if(signs.size() == MAXSIGNS)
+ break;
+ signs.push_back(tempSigns[i]);
+ }
+ }
+ goto fin;
+fail:
+ //Clean up everything
+ bson_destroy(&b);
+ if(freeIndices)
+ free(freeIndices);
+ if(partsSimIndex)
+ free(partsSimIndex);
+ throw ParseException(ParseException::Corrupt, "Save data corrupt");
+fin:
+ bson_destroy(&b);
+ if(freeIndices)
+ free(freeIndices);
+ if(partsSimIndex)
+ free(partsSimIndex);
+}
+
+void GameSave::readPSv(char * data, int dataLength)
+{
+ unsigned char * d = NULL, * c = (unsigned char *)data;
+ int q,i,j,k,x,y,p=0,*m=NULL, ver, pty, ty, legacy_beta=0, tempGrav = 0;
+ int bx0=0, by0=0, bw, bh, w, h, y0 = 0, x0 = 0;
+ int nf=0, new_format = 0, ttv = 0;
+ int *fp = (int *)malloc(NPART*sizeof(int));
+
+ std::vector<sign> tempSigns;
+ char tempSignText[255];
+ sign tempSign("", 0, 0, sign::Left);
+
+ //Gol data used to read older saves
+ int goltype[NGOL];
+ int grule[NGOL+1][10];
+
+ int golRulesCount;
+ int * golRulesT = LoadGOLRules(golRulesCount);
+ memcpy(grule, golRulesT, sizeof(int) * (golRulesCount*10));
+ free(golRulesT);
+
+ int golTypesCount;
+ int * golTypesT = LoadGOLTypes(golTypesCount);
+ memcpy(goltype, golTypesT, sizeof(int) * (golTypesCount));
+ free(golTypesT);
+
+ std::vector<Element> elements = GetElements();
+
+ try
+ {
+
+ //New file header uses PSv, replacing fuC. This is to detect if the client uses a new save format for temperatures
+ //This creates a problem for old clients, that display and "corrupt" error instead of a "newer version" error
+
+ if (dataLength<16)
+ throw ParseException(ParseException::Corrupt, "No save data");
+ if (!(c[2]==0x43 && c[1]==0x75 && c[0]==0x66) && !(c[2]==0x76 && c[1]==0x53 && c[0]==0x50))
+ throw ParseException(ParseException::Corrupt, "Unknown format");
+ if (c[2]==0x76 && c[1]==0x53 && c[0]==0x50) {
+ new_format = 1;
+ }
+ if (c[4]>SAVE_VERSION)
+ throw ParseException(ParseException::WrongVersion, "Save from newer version");
+ ver = c[4];
+
+ if (ver<34)
+ {
+ legacyEnable = 1;
+ }
+ else
+ {
+ if (ver>=44) {
+ legacyEnable = c[3]&0x01;
+ paused = (c[3]>>1)&0x01;
+ if (ver>=46) {
+ gravityMode = ((c[3]>>2)&0x03);// | ((c[3]>>2)&0x01);
+ airMode = ((c[3]>>4)&0x07);// | ((c[3]>>4)&0x02) | ((c[3]>>4)&0x01);
+ }
+ if (ver>=49) {
+ gravityEnable = ((c[3]>>7)&0x01);
+ }
+ } else {
+ if (c[3]==1||c[3]==0) {
+ legacyEnable = c[3];
+ } else {
+ legacy_beta = 1;
+ }
+ }
+ }
+
+ bw = c[6];
+ bh = c[7];
+ if (bx0+bw > XRES/CELL)
+ bx0 = XRES/CELL - bw;
+ if (by0+bh > YRES/CELL)
+ by0 = YRES/CELL - bh;
+ if (bx0 < 0)
+ bx0 = 0;
+ if (by0 < 0)
+ by0 = 0;
+
+ if (c[5]!=CELL || bx0+bw>XRES/CELL || by0+bh>YRES/CELL)
+ throw ParseException(ParseException::InvalidDimensions, "Save too large");
+ i = (unsigned)c[8];
+ i |= ((unsigned)c[9])<<8;
+ i |= ((unsigned)c[10])<<16;
+ i |= ((unsigned)c[11])<<24;
+ d = (unsigned char *)malloc(i);
+ if (!d)
+ throw ParseException(ParseException::Corrupt, "Cannot allocate memory");
+
+ setSize(bw, bh);
+
+ int bzStatus = 0;
+ if (bzStatus = BZ2_bzBuffToBuffDecompress((char *)d, (unsigned *)&i, (char *)(c+12), dataLength-12, 0, 0))
+ {
+ std::stringstream bzStatusStr;
+ bzStatusStr << bzStatus;
+ throw ParseException(ParseException::Corrupt, "Cannot decompress: " + bzStatusStr.str());
+ }
+ dataLength = i;
+
+ std::cout << "Parsing " << dataLength << " bytes of data, version " << ver << std::endl;
+
+ if (dataLength < bw*bh)
+ throw ParseException(ParseException::Corrupt, "Save data corrupt (missing data)");
+
+ // normalize coordinates
+ x0 = bx0*CELL;
+ y0 = by0*CELL;
+ w = bw *CELL;
+ h = bh *CELL;
+
+ if (ver<46) {
+ gravityMode = 0;
+ airMode = 0;
+ }
+ m = (int *)calloc(XRES*YRES, sizeof(int));
+
+ // load the required air state
+ for (y=by0; y<by0+bh; y++)
+ for (x=bx0; x<bx0+bw; x++)
+ {
+ if (d[p])
+ {
+ //In old saves, ignore walls created by sign tool bug
+ //Not ignoring other invalid walls or invalid walls in new saves, so that any other bugs causing them are easier to notice, find and fix
+ if (ver<71 && d[p]==O_WL_SIGN)
+ {
+ p++;
+ continue;
+ }
+ blockMap[y][x] = d[p];
+ if (blockMap[y][x]==1)
+ blockMap[y][x]=WL_WALL;
+ if (blockMap[y][x]==2)
+ blockMap[y][x]=WL_DESTROYALL;
+ if (blockMap[y][x]==3)
+ blockMap[y][x]=WL_ALLOWLIQUID;
+ if (blockMap[y][x]==4)
+ blockMap[y][x]=WL_FAN;
+ if (blockMap[y][x]==5)
+ blockMap[y][x]=WL_STREAM;
+ if (blockMap[y][x]==6)
+ blockMap[y][x]=WL_DETECT;
+ if (blockMap[y][x]==7)
+ blockMap[y][x]=WL_EWALL;
+ if (blockMap[y][x]==8)
+ blockMap[y][x]=WL_WALLELEC;
+ if (blockMap[y][x]==9)
+ blockMap[y][x]=WL_ALLOWAIR;
+ if (blockMap[y][x]==10)
+ blockMap[y][x]=WL_ALLOWSOLID;
+ if (blockMap[y][x]==11)
+ blockMap[y][x]=WL_ALLOWALLELEC;
+ if (blockMap[y][x]==12)
+ blockMap[y][x]=WL_EHOLE;
+ if (blockMap[y][x]==13)
+ blockMap[y][x]=WL_ALLOWGAS;
+
+ if (blockMap[y][x]==O_WL_WALLELEC)
+ blockMap[y][x]=WL_WALLELEC;
+ if (blockMap[y][x]==O_WL_EWALL)
+ blockMap[y][x]=WL_EWALL;
+ if (blockMap[y][x]==O_WL_DETECT)
+ blockMap[y][x]=WL_DETECT;
+ if (blockMap[y][x]==O_WL_STREAM)
+ blockMap[y][x]=WL_STREAM;
+ if (blockMap[y][x]==O_WL_FAN||blockMap[y][x]==O_WL_FANHELPER)
+ blockMap[y][x]=WL_FAN;
+ if (blockMap[y][x]==O_WL_ALLOWLIQUID)
+ blockMap[y][x]=WL_ALLOWLIQUID;
+ if (blockMap[y][x]==O_WL_DESTROYALL)
+ blockMap[y][x]=WL_DESTROYALL;
+ if (blockMap[y][x]==O_WL_ERASE)
+ blockMap[y][x]=WL_ERASE;
+ if (blockMap[y][x]==O_WL_WALL)
+ blockMap[y][x]=WL_WALL;
+ if (blockMap[y][x]==O_WL_ALLOWAIR)
+ blockMap[y][x]=WL_ALLOWAIR;
+ if (blockMap[y][x]==O_WL_ALLOWSOLID)
+ blockMap[y][x]=WL_ALLOWSOLID;
+ if (blockMap[y][x]==O_WL_ALLOWALLELEC)
+ blockMap[y][x]=WL_ALLOWALLELEC;
+ if (blockMap[y][x]==O_WL_EHOLE)
+ blockMap[y][x]=WL_EHOLE;
+ if (blockMap[y][x]==O_WL_ALLOWGAS)
+ blockMap[y][x]=WL_ALLOWGAS;
+ if (blockMap[y][x]==O_WL_GRAV)
+ blockMap[y][x]=WL_GRAV;
+ if (blockMap[y][x]==O_WL_ALLOWENERGY)
+ blockMap[y][x]=WL_ALLOWENERGY;
+ }
+
+ p++;
+ }
+ for (y=by0; y<by0+bh; y++)
+ for (x=bx0; x<bx0+bw; x++)
+ if (d[(y-by0)*bw+(x-bx0)]==4||d[(y-by0)*bw+(x-bx0)]==O_WL_FAN)
+ {
+ if (p >= dataLength)
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ fanVelX[y][x] = (d[p++]-127.0f)/64.0f;
+ }
+ for (y=by0; y<by0+bh; y++)
+ for (x=bx0; x<bx0+bw; x++)
+ if (d[(y-by0)*bw+(x-bx0)]==4||d[(y-by0)*bw+(x-bx0)]==O_WL_FAN)
+ {
+ if (p >= dataLength)
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ fanVelY[y][x] = (d[p++]-127.0f)/64.0f;
+ }
+
+ // load the particle map
+ i = 0;
+ k = 0;
+ pty = p;
+ for (y=y0; y<y0+h; y++)
+ for (x=x0; x<x0+w; x++)
+ {
+ if (p >= dataLength)
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ j=d[p++];
+ if (j >= PT_NUM) {
+ //TODO: Possibly some server side translation
+ j = PT_DUST;//throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ }
+ if (j)
+ {
+ memset(particles+k, 0, sizeof(Particle));
+ particles[k].type = j;
+ if (j == PT_COAL)
+ particles[k].tmp = 50;
+ if (j == PT_FUSE)
+ particles[k].tmp = 50;
+ if (j == PT_PHOT)
+ particles[k].ctype = 0x3fffffff;
+ if (j == PT_SOAP)
+ particles[k].ctype = 0;
+ if (j==PT_BIZR || j==PT_BIZRG || j==PT_BIZRS)
+ particles[k].ctype = 0x47FFFF;
+ particles[k].x = (float)x;
+ particles[k].y = (float)y;
+ m[(x-x0)+(y-y0)*w] = k+1;
+ particlesCount = ++k;
+ }
+ }
+
+ // load particle properties
+ for (j=0; j<w*h; j++)
+ {
+ i = m[j];
+ if (i)
+ {
+ i--;
+ if (p+1 >= dataLength)
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ if (i < NPART)
+ {
+ particles[i].vx = (d[p++]-127.0f)/16.0f;
+ particles[i].vy = (d[p++]-127.0f)/16.0f;
+ }
+ else
+ p += 2;
+ }
+ }
+ for (j=0; j<w*h; j++)
+ {
+ i = m[j];
+ if (i)
+ {
+ if (ver>=44) {
+ if (p >= dataLength) {
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ }
+ if (i <= NPART) {
+ ttv = (d[p++])<<8;
+ ttv |= (d[p++]);
+ particles[i-1].life = ttv;
+ } else {
+ p+=2;
+ }
+ } else {
+ if (p >= dataLength)
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ if (i <= NPART)
+ particles[i-1].life = d[p++]*4;
+ else
+ p++;
+ }
+ }
+ }
+ if (ver>=44) {
+ for (j=0; j<w*h; j++)
+ {
+ i = m[j];
+ if (i)
+ {
+ if (p >= dataLength) {
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ }
+ if (i <= NPART) {
+ ttv = (d[p++])<<8;
+ ttv |= (d[p++]);
+ particles[i-1].tmp = ttv;
+ if (ver<53 && !particles[i-1].tmp)
+ for (q = 1; q<=NGOLALT; q++) {
+ if (particles[i-1].type==goltype[q-1] && grule[q][9]==2)
+ particles[i-1].tmp = grule[q][9]-1;
+ }
+ if (ver>=51 && ver<53 && particles[i-1].type==PT_PBCN)
+ {
+ particles[i-1].tmp2 = particles[i-1].tmp;
+ particles[i-1].tmp = 0;
+ }
+ } else {
+ p+=2;
+ }
+ }
+ }
+ }
+ if (ver>=53) {
+ for (j=0; j<w*h; j++)
+ {
+ i = m[j];
+ ty = d[pty+j];
+ if (i && (ty==PT_PBCN || (ty==PT_TRON && ver>=77)))
+ {
+ if (p >= dataLength)
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ if (i <= NPART)
+ particles[i-1].tmp2 = d[p++];
+ else
+ p++;
+ }
+ }
+ }
+ //Read ALPHA component
+ for (j=0; j<w*h; j++)
+ {
+ i = m[j];
+ if (i)
+ {
+ if (ver>=49) {
+ if (p >= dataLength) {
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ }
+ if (i <= NPART) {
+ particles[i-1].dcolour = d[p++]<<24;
+ } else {
+ p++;
+ }
+ }
+ }
+ }
+ //Read RED component
+ for (j=0; j<w*h; j++)
+ {
+ i = m[j];
+ if (i)
+ {
+ if (ver>=49) {
+ if (p >= dataLength) {
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ }
+ if (i <= NPART) {
+ particles[i-1].dcolour |= d[p++]<<16;
+ } else {
+ p++;
+ }
+ }
+ }
+ }
+ //Read GREEN component
+ for (j=0; j<w*h; j++)
+ {
+ i = m[j];
+ if (i)
+ {
+ if (ver>=49) {
+ if (p >= dataLength) {
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ }
+ if (i <= NPART) {
+ particles[i-1].dcolour |= d[p++]<<8;
+ } else {
+ p++;
+ }
+ }
+ }
+ }
+ //Read BLUE component
+ for (j=0; j<w*h; j++)
+ {
+ i = m[j];
+ if (i)
+ {
+ if (ver>=49) {
+ if (p >= dataLength) {
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ }
+ if (i <= NPART) {
+ particles[i-1].dcolour |= d[p++];
+ } else {
+ p++;
+ }
+ }
+ }
+ }
+ for (j=0; j<w*h; j++)
+ {
+ i = m[j];
+ ty = d[pty+j];
+ if (i)
+ {
+ if (ver>=34&&legacy_beta==0)
+ {
+ if (p >= dataLength)
+ {
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ }
+ if (i <= NPART)
+ {
+ if (ver>=42) {
+ if (new_format) {
+ ttv = (d[p++])<<8;
+ ttv |= (d[p++]);
+ if (particles[i-1].type==PT_PUMP) {
+ particles[i-1].temp = ttv + 0.15;//fix PUMP saved at 0, so that it loads at 0.
+ } else {
+ particles[i-1].temp = ttv;
+ }
+ } else {
+ particles[i-1].temp = (d[p++]*((MAX_TEMP+(-MIN_TEMP))/255))+MIN_TEMP;
+ }
+ } else {
+ particles[i-1].temp = ((d[p++]*((O_MAX_TEMP+(-O_MIN_TEMP))/255))+O_MIN_TEMP)+273;
+ }
+ }
+ else
+ {
+ p++;
+ if (new_format) {
+ p++;
+ }
+ }
+ }
+ else
+ {
+ particles[i-1].temp = elements[particles[i-1].type].Temperature;
+ }
+ }
+ }
+ for (j=0; j<w*h; j++)
+ {
+ int gnum = 0;
+ i = m[j];
+ ty = d[pty+j];
+ if (i && (ty==PT_CLNE || (ty==PT_PCLN && ver>=43) || (ty==PT_BCLN && ver>=44) || (ty==PT_SPRK && ver>=21) || (ty==PT_LAVA && ver>=34) || (ty==PT_PIPE && ver>=43) || (ty==PT_LIFE && ver>=51) || (ty==PT_PBCN && ver>=52) || (ty==PT_WIRE && ver>=55) || (ty==PT_STOR && ver>=59) || (ty==PT_CONV && ver>=60)))
+ {
+ if (p >= dataLength)
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ if (i <= NPART)
+ particles[i-1].ctype = d[p++];
+ else
+ p++;
+ }
+ //TODO: STKM_init_legs
+ // no more particle properties to load, so we can change type here without messing up loading
+ if (i && i<=NPART)
+ {
+ if (ver<79 && particles[i-1].type == PT_SPNG)
+ {
+ if (fabs(particles[i-1].vx)>0.0f || fabs(particles[i-1].vy)>0.0f)
+ particles[i-1].flags |= FLAG_MOVABLE;
+ }
+
+ if (ver<48 && (ty==OLD_PT_WIND || (ty==PT_BRAY&&particles[i-1].life==0)))
+ {
+ // Replace invisible particles with something sensible and add decoration to hide it
+ x = (int)(particles[i-1].x+0.5f);
+ y = (int)(particles[i-1].y+0.5f);
+ particles[i-1].dcolour = 0xFF000000;
+ particles[i-1].type = PT_DMND;
+ }
+ if(ver<51 && ((ty>=78 && ty<=89) || (ty>=134 && ty<=146 && ty!=141))){
+ //Replace old GOL
+ particles[i-1].type = PT_LIFE;
+ for (gnum = 0; gnum<NGOLALT; gnum++){
+ if (ty==goltype[gnum])
+ particles[i-1].ctype = gnum;
+ }
+ ty = PT_LIFE;
+ }
+ if(ver<52 && (ty==PT_CLNE || ty==PT_PCLN || ty==PT_BCLN)){
+ //Replace old GOL ctypes in clone
+ for (gnum = 0; gnum<NGOLALT; gnum++){
+ if (particles[i-1].ctype==goltype[gnum])
+ {
+ particles[i-1].ctype = PT_LIFE;
+ particles[i-1].tmp = gnum;
+ }
+ }
+ }
+ if(ty==PT_LCRY){
+ if(ver<67)
+ {
+ //New LCRY uses TMP not life
+ if(particles[i-1].life>=10)
+ {
+ particles[i-1].life = 10;
+ particles[i-1].tmp2 = 10;
+ particles[i-1].tmp = 3;
+ }
+ else if(particles[i-1].life<=0)
+ {
+ particles[i-1].life = 0;
+ particles[i-1].tmp2 = 0;
+ particles[i-1].tmp = 0;
+ }
+ else if(particles[i-1].life < 10 && particles[i-1].life > 0)
+ {
+ particles[i-1].tmp = 1;
+ }
+ }
+ else
+ {
+ particles[i-1].tmp2 = particles[i-1].life;
+ }
+ }
+
+ if (ver<81)
+ {
+ if (particles[i-1].type==PT_BOMB && particles[i-1].tmp!=0)
+ {
+ particles[i-1].type = PT_EMBR;
+ particles[i-1].ctype = 0;
+ if (particles[i-1].tmp==1)
+ particles[i-1].tmp = 0;
+ }
+ if (particles[i-1].type==PT_DUST && particles[i-1].life>0)
+ {
+ particles[i-1].type = PT_EMBR;
+ particles[i-1].ctype = (particles[i-1].tmp2<<16) | (particles[i-1].tmp<<8) | particles[i-1].ctype;
+ particles[i-1].tmp = 1;
+ }
+ if (particles[i-1].type==PT_FIRW && particles[i-1].tmp>=2)
+ {
+ int caddress = restrict_flt(restrict_flt((float)(particles[i-1].tmp-4), 0.0f, 200.0f)*3, 0.0f, (200.0f*3)-3);
+ particles[i-1].type = PT_EMBR;
+ particles[i-1].tmp = 1;
+ particles[i-1].ctype = (((unsigned char)(firw_data[caddress]))<<16) | (((unsigned char)(firw_data[caddress+1]))<<8) | ((unsigned char)(firw_data[caddress+2]));
+ }
+ }
+ }
+ }
+
+ if (p >= dataLength)
+ goto version1;
+ j = d[p++];
+ for (i=0; i<j; i++)
+ {
+ if (p+6 > dataLength)
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ x = d[p++];
+ x |= ((unsigned)d[p++])<<8;
+ tempSign.x = x+x0;
+ x = d[p++];
+ x |= ((unsigned)d[p++])<<8;
+ tempSign.y = x+y0;
+ x = d[p++];
+ tempSign.ju = (sign::Justification)x;
+ x = d[p++];
+ if (p+x > dataLength)
+ throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
+ if(x>254)
+ x = 254;
+ memcpy(tempSignText, d+p, x);
+ tempSignText[x] = 0;
+ tempSign.text = tempSignText;
+ tempSigns.push_back(tempSign);
+ p += x;
+ }
+
+ for (i = 0; i < tempSigns.size(); i++)
+ {
+ if(signs.size() == MAXSIGNS)
+ break;
+ signs.push_back(tempSigns[i]);
+ }
+
+ }
+ catch(ParseException & e)
+ {
+ if (m)
+ {
+ free(m);
+ m = 0;
+ }
+ if (d)
+ {
+ free(d);
+ d = 0;
+ }
+ if (fp)
+ {
+ free(fp);
+ fp = 0;
+ }
+ throw;
+ }
+
+ version1:
+ if (m)
+ {
+ free(m);
+ m = 0;
+ }
+ if (d)
+ {
+ free(d);
+ d = 0;
+ }
+ if (fp)
+ {
+ free(fp);
+ fp = 0;
+ }
+}
+
+char * GameSave::serialiseOPS(int & dataLength)
+{
+ //Particle *particles = sim->parts;
+ unsigned char *partsData = NULL, *partsPosData = NULL, *fanData = NULL, *wallData = NULL, *finalData = NULL, *outputData = NULL, *soapLinkData = NULL;
+ unsigned *partsPosLink = NULL, *partsPosFirstMap = NULL, *partsPosCount = NULL, *partsPosLastMap = NULL;
+ unsigned partsCount = 0, *partsSaveIndex = NULL;
+ unsigned *elementCount = new unsigned[PT_NUM];
+ unsigned int partsDataLen, partsPosDataLen, fanDataLen, wallDataLen, finalDataLen, outputDataLen, soapLinkDataLen;
+ int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH;
+ int x, y, i, wallDataFound = 0;
+ int posCount, signsCount;
+ bson b;
+
+ std::fill(elementCount, elementCount+PT_NUM, 0);
+
+ //Get coords in blocks
+ blockX = 0;//orig_x0/CELL;
+ blockY = 0;//orig_y0/CELL;
+
+ //Snap full coords to block size
+ fullX = blockX*CELL;
+ fullY = blockY*CELL;
+
+ //Original size + offset of original corner from snapped corner, rounded up by adding CELL-1
+ blockW = blockWidth;//(blockWidth-fullX+CELL-1)/CELL;
+ blockH = blockHeight;//(blockHeight-fullY+CELL-1)/CELL;
+ fullW = blockW*CELL;
+ fullH = blockH*CELL;
+
+ //Copy fan and wall data
+ wallData = (unsigned char*)malloc(blockW*blockH);
+ wallDataLen = blockW*blockH;
+ fanData = (unsigned char*)malloc((blockW*blockH)*2);
+ fanDataLen = 0;
+ for(x = blockX; x < blockX+blockW; x++)
+ {
+ for(y = blockY; y < blockY+blockH; y++)
+ {
+ wallData[(y-blockY)*blockW+(x-blockX)] = blockMap[y][x];
+ if(blockMap[y][x] && !wallDataFound)
+ wallDataFound = 1;
+ if(blockMap[y][x]==WL_FAN)
+ {
+ i = (int)(fanVelX[y][x]*64.0f+127.5f);
+ if (i<0) i=0;
+ if (i>255) i=255;
+ fanData[fanDataLen++] = i;
+ i = (int)(fanVelY[y][x]*64.0f+127.5f);
+ if (i<0) i=0;
+ if (i>255) i=255;
+ fanData[fanDataLen++] = i;
+ }
+ }
+ }
+ if(!fanDataLen)
+ {
+ free(fanData);
+ fanData = NULL;
+ }
+ if(!wallDataFound)
+ {
+ free(wallData);
+ wallData = NULL;
+ }
+
+ //Index positions of all particles, using linked lists
+ //partsPosFirstMap is pmap for the first particle in each position
+ //partsPosLastMap is pmap for the last particle in each position
+ //partsPosCount is the number of particles in each position
+ //partsPosLink contains, for each particle, (i<<8)|1 of the next particle in the same position
+ partsPosFirstMap = (unsigned int *)calloc(fullW*fullH, sizeof(unsigned));
+ partsPosLastMap = (unsigned int *)calloc(fullW*fullH, sizeof(unsigned));
+ partsPosCount = (unsigned int *)calloc(fullW*fullH, sizeof(unsigned));
+ partsPosLink = (unsigned int *)calloc(NPART, sizeof(unsigned));
+ for(i = 0; i < NPART; i++)
+ {
+ if(particles[i].type)
+ {
+ x = (int)(particles[i].x+0.5f);
+ y = (int)(particles[i].y+0.5f);
+ //Coordinates relative to top left corner of saved area
+ x -= fullX;
+ y -= fullY;
+ if (!partsPosFirstMap[y*fullW + x])
+ {
+ //First entry in list
+ partsPosFirstMap[y*fullW + x] = (i<<8)|1;
+ partsPosLastMap[y*fullW + x] = (i<<8)|1;
+ }
+ else
+ {
+ //Add to end of list
+ partsPosLink[partsPosLastMap[y*fullW + x]>>8] = (i<<8)|1;//link to current end of list
+ partsPosLastMap[y*fullW + x] = (i<<8)|1;//set as new end of list
+ }
+ partsPosCount[y*fullW + x]++;
+ }
+ }
+
+ //Store number of particles in each position
+ partsPosData = (unsigned char*)malloc(fullW*fullH*3);
+ partsPosDataLen = 0;
+ for (y=0;y<fullH;y++)
+ {
+ for (x=0;x<fullW;x++)
+ {
+ posCount = partsPosCount[y*fullW + x];
+ partsPosData[partsPosDataLen++] = (posCount&0x00FF0000)>>16;
+ partsPosData[partsPosDataLen++] = (posCount&0x0000FF00)>>8;
+ partsPosData[partsPosDataLen++] = (posCount&0x000000FF);
+ }
+ }
+
+ //Copy parts data
+ /* Field descriptor format:
+ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+ | tmp2[2] | tmp2 | ctype[2] | vy | vx | dcololour | ctype[1] | tmp[2] | tmp[1] | life[2] | life[1] | temp dbl len|
+ life[2] means a second byte (for a 16 bit field) if life[1] is present
+ */
+ partsData = (unsigned char *)malloc(NPART * (sizeof(Particle)+1));
+ partsDataLen = 0;
+ partsSaveIndex = (unsigned int *)calloc(NPART, sizeof(unsigned));
+ partsCount = 0;
+ for (y=0;y<fullH;y++)
+ {
+ for (x=0;x<fullW;x++)
+ {
+ //Find the first particle in this position
+ i = partsPosFirstMap[y*fullW + x];
+
+ //Loop while there is a pmap entry
+ while (i)
+ {
+ unsigned short fieldDesc = 0;
+ int fieldDescLoc = 0, tempTemp, vTemp;
+
+ //Turn pmap entry into a particles index
+ i = i>>8;
+
+ //Store saved particle index+1 for this partsptr index (0 means not saved)
+ partsSaveIndex[i] = (partsCount++) + 1;
+
+ //Type (required)
+ partsData[partsDataLen++] = particles[i].type;
+ elementCount[particles[i].type]++;
+
+ //Location of the field descriptor
+ fieldDescLoc = partsDataLen++;
+ partsDataLen++;
+
+ //Extra Temperature (2nd byte optional, 1st required), 1 to 2 bytes
+ //Store temperature as an offset of 21C(294.15K) or go into a 16byte int and store the whole thing
+ if(fabs(particles[i].temp-294.15f)<127)
+ {
+ tempTemp = (particles[i].temp-294.15f);
+ partsData[partsDataLen++] = tempTemp;
+ }
+ else
+ {
+ fieldDesc |= 1;
+ tempTemp = particles[i].temp;
+ partsData[partsDataLen++] = tempTemp;
+ partsData[partsDataLen++] = tempTemp >> 8;
+ }
+
+ //Life (optional), 1 to 2 bytes
+ if(particles[i].life)
+ {
+ fieldDesc |= 1 << 1;
+ partsData[partsDataLen++] = particles[i].life;
+ if(particles[i].life > 255)
+ {
+ fieldDesc |= 1 << 2;
+ partsData[partsDataLen++] = particles[i].life >> 8;
+ }
+ }
+
+ //Tmp (optional), 1 to 2 bytes
+ if(particles[i].tmp)
+ {
+ fieldDesc |= 1 << 3;
+ partsData[partsDataLen++] = particles[i].tmp;
+ if(particles[i].tmp > 255)
+ {
+ fieldDesc |= 1 << 4;
+ partsData[partsDataLen++] = particles[i].tmp >> 8;
+ if(particles[i].tmp > 65535)
+ {
+ fieldDesc |= 1 << 12;
+ partsData[partsDataLen++] = (particles[i].tmp&0xFF000000)>>24;
+ partsData[partsDataLen++] = (particles[i].tmp&0x00FF0000)>>16;
+ }
+ }
+ }
+
+ //Ctype (optional), 1 or 4 bytes
+ if(particles[i].ctype)
+ {
+ fieldDesc |= 1 << 5;
+ partsData[partsDataLen++] = particles[i].ctype;
+ if(particles[i].ctype > 255)
+ {
+ fieldDesc |= 1 << 9;
+ partsData[partsDataLen++] = (particles[i].ctype&0xFF000000)>>24;
+ partsData[partsDataLen++] = (particles[i].ctype&0x00FF0000)>>16;
+ partsData[partsDataLen++] = (particles[i].ctype&0x0000FF00)>>8;
+ }
+ }
+
+ //Dcolour (optional), 4 bytes
+ if(particles[i].dcolour && (particles[i].dcolour & 0xFF000000))
+ {
+ fieldDesc |= 1 << 6;
+ partsData[partsDataLen++] = (particles[i].dcolour&0xFF000000)>>24;
+ partsData[partsDataLen++] = (particles[i].dcolour&0x00FF0000)>>16;
+ partsData[partsDataLen++] = (particles[i].dcolour&0x0000FF00)>>8;
+ partsData[partsDataLen++] = (particles[i].dcolour&0x000000FF);
+ }
+
+ //VX (optional), 1 byte
+ if(fabs(particles[i].vx) > 0.001f)
+ {
+ fieldDesc |= 1 << 7;
+ vTemp = (int)(particles[i].vx*16.0f+127.5f);
+ if (vTemp<0) vTemp=0;
+ if (vTemp>255) vTemp=255;
+ partsData[partsDataLen++] = vTemp;
+ }
+
+ //VY (optional), 1 byte
+ if(fabs(particles[i].vy) > 0.001f)
+ {
+ fieldDesc |= 1 << 8;
+ vTemp = (int)(particles[i].vy*16.0f+127.5f);
+ if (vTemp<0) vTemp=0;
+ if (vTemp>255) vTemp=255;
+ partsData[partsDataLen++] = vTemp;
+ }
+
+ //Tmp2 (optional), 1 or 2 bytes
+ if(particles[i].tmp2)
+ {
+ fieldDesc |= 1 << 10;
+ partsData[partsDataLen++] = particles[i].tmp2;
+ if(particles[i].tmp2 > 255)
+ {
+ fieldDesc |= 1 << 11;
+ partsData[partsDataLen++] = particles[i].tmp2 >> 8;
+ }
+ }
+
+ //Write the field descriptor;
+ partsData[fieldDescLoc] = fieldDesc;
+ partsData[fieldDescLoc+1] = fieldDesc>>8;
+
+ //Get the pmap entry for the next particle in the same position
+ i = partsPosLink[i];
+ }
+ }
+ }
+
+ soapLinkData = (unsigned char*)malloc(3*elementCount[PT_SOAP]);
+ soapLinkDataLen = 0;
+ //Iterate through particles in the same order that they were saved
+ for (y=0;y<fullH;y++)
+ {
+ for (x=0;x<fullW;x++)
+ {
+ //Find the first particle in this position
+ i = partsPosFirstMap[y*fullW + x];
+
+ //Loop while there is a pmap entry
+ while (i)
+ {
+ //Turn pmap entry into a partsptr index
+ i = i>>8;
+
+ if (particles[i].type==PT_SOAP)
+ {
+ //Only save forward link for each particle, back links can be deduced from other forward links
+ //linkedIndex is index within saved particles + 1, 0 means not saved or no link
+
+ unsigned linkedIndex = 0;
+ if ((particles[i].ctype&2) && particles[i].tmp>=0 && particles[i].tmp<NPART)
+ {
+ linkedIndex = partsSaveIndex[particles[i].tmp];
+ }
+ soapLinkData[soapLinkDataLen++] = (linkedIndex&0xFF0000)>>16;
+ soapLinkData[soapLinkDataLen++] = (linkedIndex&0x00FF00)>>8;
+ soapLinkData[soapLinkDataLen++] = (linkedIndex&0x0000FF);
+ }
+
+ //Get the pmap entry for the next particle in the same position
+ i = partsPosLink[i];
+ }
+ }
+ }
+ if(!soapLinkDataLen)
+ {
+ free(soapLinkData);
+ soapLinkData = NULL;
+ }
+ if(!partsDataLen)
+ {
+ free(partsData);
+ partsData = NULL;
+ }
+
+ bson_init(&b);
+ bson_append_bool(&b, "waterEEnabled", waterEEnabled);
+ bson_append_bool(&b, "legacyEnable", legacyEnable);
+ bson_append_bool(&b, "gravityEnable", gravityEnable);
+ bson_append_bool(&b, "paused", paused);
+ bson_append_int(&b, "gravityMode", gravityMode);
+ bson_append_int(&b, "airMode", airMode);
+
+ //bson_append_int(&b, "leftSelectedElement", sl);
+ //bson_append_int(&b, "rightSelectedElement", sr);
+ //bson_append_int(&b, "activeMenu", active_menu);
+ if(partsData)
+ bson_append_binary(&b, "parts", BSON_BIN_USER, (const char *)partsData, partsDataLen);
+ if(partsPosData)
+ bson_append_binary(&b, "partsPos", BSON_BIN_USER, (const char *)partsPosData, partsPosDataLen);
+ if(wallData)
+ bson_append_binary(&b, "wallMap", BSON_BIN_USER, (const char *)wallData, wallDataLen);
+ if(fanData)
+ bson_append_binary(&b, "fanMap", BSON_BIN_USER, (const char *)fanData, fanDataLen);
+ if(soapLinkData)
+ bson_append_binary(&b, "soapLinks", BSON_BIN_USER, (const char *)soapLinkData, soapLinkDataLen);
+ if(partsData && palette.size())
+ {
+ bson_append_start_array(&b, "palette");
+ for(std::vector<PaletteItem>::iterator iter = palette.begin(), end = palette.end(); iter != end; ++iter)
+ {
+ bson_append_int(&b, (*iter).first.c_str(), (*iter).second);
+ }
+ bson_append_finish_array(&b);
+ }
+ signsCount = 0;
+ for(i = 0; i < signs.size(); i++)
+ {
+ if(signs[i].text.length() && signs[i].x>=0 && signs[i].x<=fullW && signs[i].y>=0 && signs[i].y<=fullH)
+ {
+ signsCount++;
+ }
+ }
+ if(signsCount)
+ {
+ bson_append_start_array(&b, "signs");
+ for(i = 0; i < signs.size(); i++)
+ {
+ if(signs[i].text.length() && signs[i].x>=0 && signs[i].x<=fullW && signs[i].y>=0 && signs[i].y<=fullH)
+ {
+ bson_append_start_object(&b, "sign");
+ bson_append_string(&b, "text", signs[i].text.c_str());
+ bson_append_int(&b, "justification", signs[i].ju);
+ bson_append_int(&b, "x", signs[i].x);
+ bson_append_int(&b, "y", signs[i].y);
+ bson_append_finish_object(&b);
+ }
+ }
+ bson_append_finish_array(&b);
+ }
+ bson_finish(&b);
+#ifdef DEBUG
+ bson_print(&b);
+#endif
+
+ finalData = (unsigned char *)bson_data(&b);
+ finalDataLen = bson_size(&b);
+ outputDataLen = finalDataLen*2+12;
+ outputData = (unsigned char *)malloc(outputDataLen);
+
+ outputData[0] = 'O';
+ outputData[1] = 'P';
+ outputData[2] = 'S';
+ outputData[3] = '1';
+ outputData[4] = SAVE_VERSION;
+ outputData[5] = CELL;
+ outputData[6] = blockW;
+ outputData[7] = blockH;
+ outputData[8] = finalDataLen;
+ outputData[9] = finalDataLen >> 8;
+ outputData[10] = finalDataLen >> 16;
+ outputData[11] = finalDataLen >> 24;
+
+ if (BZ2_bzBuffToBuffCompress((char*)(outputData+12), &outputDataLen, (char*)finalData, bson_size(&b), 9, 0, 0) != BZ_OK)
+ {
+ puts("Save Error\n");
+ free(outputData);
+ dataLength = 0;
+ outputData = NULL;
+ goto fin;
+ }
+
+#ifdef DEBUG
+ printf("compressed data: %d\n", outputDataLen);
+#endif
+ dataLength = outputDataLen + 12;
+
+fin:
+ bson_destroy(&b);
+ if(partsData)
+ free(partsData);
+ if(wallData)
+ free(wallData);
+ if(fanData)
+ free(fanData);
+ if (elementCount)
+ delete[] elementCount;
+ if (partsSaveIndex)
+ free(partsSaveIndex);
+ if (soapLinkData)
+ free(soapLinkData);
+
+ return (char*)outputData;
+}
+
+void GameSave::dealloc()
+{
+ if(particles)
+ {
+ delete[] particles;
+ particles = NULL;
+ }
+ if(blockMap)
+ {
+ delete[] blockMap;
+ blockMap = NULL;
+ }
+ if(blockMapPtr)
+ {
+ delete[] blockMapPtr;
+ blockMapPtr = NULL;
+ }
+ if(fanVelX)
+ {
+ delete[] fanVelX;
+ fanVelX = NULL;
+ }
+ if(fanVelXPtr)
+ {
+ delete[] fanVelXPtr;
+ fanVelXPtr = NULL;
+ }
+ if(fanVelY)
+ {
+ delete[] fanVelY;
+ fanVelY = NULL;
+ }
+ if(fanVelYPtr)
+ {
+ delete[] fanVelYPtr;
+ fanVelYPtr = NULL;
+ }
+}
+
+GameSave::~GameSave()
+{
+ dealloc();
+}
diff --git a/src/client/GameSave.h b/src/client/GameSave.h
new file mode 100644
index 0000000..8ac1fce
--- /dev/null
+++ b/src/client/GameSave.h
@@ -0,0 +1,112 @@
+//
+// GameSave.h
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 04/06/2012.
+//
+
+#ifndef The_Powder_Toy_GameSave_h
+#define The_Powder_Toy_GameSave_h
+
+#include <vector>
+#include <string>
+#include "Config.h"
+#include "Misc.h"
+
+#include "simulation/Sign.h"
+#include "simulation/Particle.h"
+
+//using namespace std;
+
+struct ParseException: public std::exception {
+ enum ParseResult { OK = 0, Corrupt, WrongVersion, InvalidDimensions, InternalError, MissingElement };
+ std::string message;
+ ParseResult result;
+public:
+ ParseException(ParseResult result, std::string message_): message(message_), result(result) {}
+ const char * what() const throw()
+ {
+ return message.c_str();
+ }
+ ~ParseException() throw() {};
+};
+
+class GameSave
+{
+public:
+
+ int blockWidth, blockHeight;
+
+ //Simulation data
+ //int ** particleMap;
+ int particlesCount;
+ Particle * particles;
+ unsigned char ** blockMap;
+ float ** fanVelX;
+ float ** fanVelY;
+
+ //Simulation Options
+ bool waterEEnabled;
+ bool legacyEnable;
+ bool gravityEnable;
+ bool paused;
+ int gravityMode;
+ int airMode;
+
+ //Signs
+ std::vector<sign> signs;
+
+ //Element palette
+ typedef std::pair<std::string, int> PaletteItem;
+ std::vector<PaletteItem> palette;
+
+ GameSave();
+ GameSave(GameSave & save);
+ GameSave(int width, int height);
+ GameSave(char * data, int dataSize);
+ GameSave(std::vector<char> data);
+ GameSave(std::vector<unsigned char> data);
+ ~GameSave();
+ void setSize(int width, int height);
+ char * Serialise(int & dataSize);
+ std::vector<char> Serialise();
+ void Transform(matrix2d transform, vector2d translate);
+
+ void Expand();
+ void Collapse();
+ bool Collapsed();
+
+ inline GameSave& operator << (Particle v)
+ {
+ if(particlesCount<NPART && v.type)
+ {
+ particles[particlesCount++] = v;
+ }
+ return *this;
+ }
+
+ inline GameSave& operator << (sign v)
+ {
+ if(signs.size()<MAXSIGNS && v.text.length())
+ signs.push_back(v);
+ return *this;
+ }
+
+private:
+ bool expanded;
+ bool hasOriginalData;
+ float * fanVelXPtr;
+ float * fanVelYPtr;
+ unsigned char * blockMapPtr;
+
+ std::vector<char> originalData;
+
+ void dealloc();
+ void read(char * data, int dataSize);
+ void readOPS(char * data, int dataLength);
+ void readPSv(char * data, int dataLength);
+ char * serialiseOPS(int & dataSize);
+ //serialisePSv();
+};
+
+#endif
diff --git a/src/client/HTTP.cpp b/src/client/HTTP.cpp
new file mode 100644
index 0000000..5fc4d08
--- /dev/null
+++ b/src/client/HTTP.cpp
@@ -0,0 +1,1104 @@
+/**
+ * Powder Toy - HTTP Library
+ *
+ * Copyright (c) 2008 - 2010 Stanislaw Skowronek.
+ * Copyright (c) 2010 Simon Robertshaw
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+
+#include <string>
+#include <sstream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef WIN
+#include <sys/param.h>
+#endif
+#if !defined(MACOSX) && !defined(BSD)
+#include <malloc.h>
+#endif
+#include <time.h>
+#ifdef WIN
+#define _WIN32_WINNT 0x0501
+//#include <iphlpapi.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#endif
+
+#include "Config.h"
+#include "HTTP.h"
+#include "MD5.h"
+
+#ifdef WIN
+#define PERROR SOCKET_ERROR
+#define PERRNO WSAGetLastError()
+#define PEAGAIN WSAEWOULDBLOCK
+#define PEINTR WSAEINTR
+#define PEINPROGRESS WSAEINPROGRESS
+#define PEALREADY WSAEALREADY
+#define PCLOSE closesocket
+#else
+#define PERROR -1
+#define PERRNO errno
+#define PEAGAIN EAGAIN
+#define PEINTR EINTR
+#define PEINPROGRESS EINPROGRESS
+#define PEALREADY EALREADY
+#define PCLOSE close
+#endif
+
+char * userAgent;
+static int http_up = 0;
+static long http_timeout = 15;
+static int http_use_proxy = 0;
+static struct sockaddr_in http_proxy;
+
+static char * eatwhitespace(char * s)
+{
+ while(*s)
+ {
+ if(!(*s == ' ' || *s == '\t'))
+ break;
+ s++;
+ }
+ return s;
+}
+
+static char *mystrdup(char *s)
+{
+ char *x;
+ if (s)
+ {
+ x = (char *)malloc(strlen(s)+1);
+ strcpy(x, s);
+ return x;
+ }
+ return s;
+}
+
+static int splituri(char *uri, char **host, char **path)
+{
+ char *p=uri,*q,*x,*y;
+ if (!strncmp(p, "http://", 7))
+ p += 7;
+ q = strchr(p, '/');
+ if (!q)
+ q = p + strlen(p);
+ x = (char *)malloc(q-p+1);
+ if (*q)
+ y = mystrdup(q);
+ else
+ y = mystrdup("/");
+ strncpy(x, p, q-p);
+ x[q-p] = 0;
+ if (q==p || x[q-p-1]==':')
+ {
+ free(x);
+ free(y);
+ return 1;
+ }
+ *host = x;
+ *path = y;
+ return 0;
+}
+
+static char *getserv(char *host)
+{
+ char *q, *x = mystrdup(host);
+ q = strchr(x, ':');
+ if (q)
+ *q = 0;
+ return x;
+}
+
+static char *getport(char *host)
+{
+ char *p, *q;
+ q = strchr(host, ':');
+ if (q)
+ p = mystrdup(q+1);
+ else
+ p = mystrdup("80");
+ return p;
+}
+
+static int resolve(char *dns, char *srv, struct sockaddr_in *addr)
+{
+ struct addrinfo hnt, *res = 0;
+ if (http_use_proxy)
+ {
+ memcpy(addr, &http_proxy, sizeof(struct sockaddr_in));
+ return 0;
+ }
+ memset(&hnt, 0, sizeof(hnt));
+ hnt.ai_family = AF_INET;
+ hnt.ai_socktype = SOCK_STREAM;
+ if (getaddrinfo(dns, srv, &hnt, &res))
+ return 1;
+ if (res)
+ {
+ if (res->ai_family != AF_INET)
+ {
+ freeaddrinfo(res);
+ return 1;
+ }
+ memcpy(addr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ return 0;
+ }
+ return 1;
+}
+
+void http_init(char *proxy)
+{
+ char *host, *port;
+#ifdef WIN
+ WSADATA wsadata;
+ if (!WSAStartup(MAKEWORD(2,2), &wsadata))
+ http_up = 1;
+#else
+ signal(SIGPIPE, SIG_IGN);
+ http_up = 1;
+#endif
+ if (proxy)
+ {
+ host = getserv(proxy);
+ port = getport(proxy);
+ if (resolve(host, port, &http_proxy))
+ http_up = 0;
+ else
+ http_use_proxy = 1;
+ free(host);
+ free(port);
+ }
+ std::stringstream userAgentBuilder;
+ userAgentBuilder << "PowderToy/" << SAVE_VERSION << "." << MINOR_VERSION << " ";
+ userAgentBuilder << "(" << IDENT_PLATFORM << "; " << IDENT_BUILD << "; M0) ";
+ userAgentBuilder << "TPTPP/" << SAVE_VERSION << "." << MINOR_VERSION << "." << BUILD_NUM << IDENT_RELTYPE << "." << SNAPSHOT_ID;
+ std::string newUserAgent = userAgentBuilder.str();
+ userAgent = new char[newUserAgent.length()+1];
+ std::copy(newUserAgent.begin(), newUserAgent.end(), userAgent);
+ userAgent[newUserAgent.length()] = 0;
+ //"User-Agent: PowderToy/%d.%d (%s; %s; M%d) TPTPP/%d.%d.%d%s.%d\n", SAVE_VERSION, MINOR_VERSION, IDENT_PLATFORM, IDENT_BUILD, 0, SAVE_VERSION, MINOR_VERSION, BUILD_NUM, IDENT_RELTYPE, SNAPSHOT_ID
+}
+
+void http_done(void)
+{
+#ifdef WIN
+ WSACleanup();
+#endif
+ http_up = 0;
+}
+
+#define CHUNK 4096
+
+#define HTS_STRT 0
+#define HTS_RSLV 1
+#define HTS_CONN 2
+#define HTS_IDLE 3
+#define HTS_XMIT 4
+#define HTS_RECV 5
+#define HTS_DONE 6
+struct http_ctx
+{
+ int state;
+ time_t last;
+ int keep;
+ int ret;
+ char *host, *path;
+ char *thdr;
+ int thlen;
+ char *txd;
+ int txdl;
+ struct sockaddr_in addr;
+ char *tbuf;
+ int tlen, tptr;
+ char *hbuf;
+ int hlen, hptr;
+ char *rbuf;
+ int rlen, rptr;
+ int chunked, chunkhdr, rxtogo, contlen, cclose;
+ int fd;
+ char *fdhost;
+};
+void *http_async_req_start(void *ctx, char *uri, char *data, int dlen, int keep)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ if (!ctx)
+ {
+ ctx = calloc(1, sizeof(struct http_ctx));
+ cx = (http_ctx *)ctx;
+ cx->fd = PERROR;
+ }
+
+ if (!cx->hbuf)
+ {
+ cx->hbuf = (char *)malloc(256);
+ cx->hlen = 256;
+ }
+
+ if (!http_up)
+ {
+ cx->ret = 604;
+ cx->state = HTS_DONE;
+ return ctx;
+ }
+
+ if (cx->state!=HTS_STRT && cx->state!=HTS_IDLE)
+ {
+ fprintf(stderr, "HTTP: unclean request restart state.\n");
+ exit(1);
+ }
+
+ cx->keep = keep;
+ cx->ret = 600;
+ if (splituri(uri, &cx->host, &cx->path))
+ {
+ cx->ret = 601;
+ cx->state = HTS_DONE;
+ return ctx;
+ }
+ if (http_use_proxy)
+ {
+ free(cx->path);
+ cx->path = mystrdup(uri);
+ }
+ if (cx->fdhost && strcmp(cx->host, cx->fdhost))
+ {
+ free(cx->fdhost);
+ cx->fdhost = NULL;
+ PCLOSE(cx->fd);
+ cx->fd = PERROR;
+ cx->state = HTS_STRT;
+ }
+ if (data)
+ {
+ if (!dlen)
+ dlen = strlen(data);
+ cx->txd = (char*)malloc(dlen);
+ memcpy(cx->txd, data, dlen);
+ cx->txdl = dlen;
+ }
+ else
+ cx->txdl = 0;
+
+ cx->contlen = 0;
+ cx->chunked = 0;
+ cx->chunkhdr = 0;
+ cx->rxtogo = 0;
+ cx->cclose = 0;
+
+ cx->tptr = 0;
+ cx->tlen = 0;
+
+ cx->last = time(NULL);
+
+ return ctx;
+}
+
+void http_async_add_header(void *ctx, char *name, char *data)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ cx->thdr = (char *)realloc(cx->thdr, cx->thlen + strlen(name) + strlen(data) + 5);
+ cx->thlen += sprintf(cx->thdr+cx->thlen, "%s: %s\r\n", name, data);
+}
+
+static void process_header(struct http_ctx *cx, char *str)
+{
+ char *p;
+ if (cx->chunkhdr)
+ {
+ p = strchr(str, ';');
+ if (p)
+ *p = 0;
+ cx->rxtogo = strtoul(str, NULL, 16);
+ cx->chunkhdr = 0;
+ if (!cx->rxtogo)
+ cx->chunked = 0;
+ }
+ if (!str[0])
+ {
+ cx->rxtogo = cx->contlen;
+ cx->chunkhdr = cx->chunked;
+ if (!cx->contlen && !cx->chunked && cx->ret!=100)
+ cx->state = HTS_DONE;
+ return;
+ }
+ if (!strncmp(str, "HTTP/", 5))
+ {
+ p = strchr(str, ' ');
+ if (!p)
+ {
+ cx->ret = 603;
+ cx->state = HTS_DONE;
+ return;
+ }
+ p++;
+ cx->ret = atoi(p);
+ return;
+ }
+ if (!strncmp(str, "Content-Length: ", 16))
+ {
+ str = eatwhitespace(str+16);
+ cx->contlen = atoi(str);
+ return;
+ }
+ if (!strncmp(str, "Transfer-Encoding: ", 19))
+ {
+ str = eatwhitespace(str+19);
+ if(!strncmp(str, "chunked", 8))
+ {
+ cx->chunked = 1;
+ }
+ return;
+ }
+ if (!strncmp(str, "Connection: ", 12))
+ {
+ str = eatwhitespace(str+12);
+ if(!strncmp(str, "close", 6))
+ {
+ cx->cclose = 1;
+ }
+ return;
+ }
+}
+
+static void process_byte(struct http_ctx *cx, char ch)
+{
+ if (cx->rxtogo)
+ {
+ cx->rxtogo--;
+
+ if (!cx->rbuf)
+ {
+ cx->rbuf = (char *)malloc(256);
+ cx->rlen = 256;
+ }
+ if (cx->rptr >= cx->rlen-1)
+ {
+ cx->rlen *= 2;
+ cx->rbuf = (char *)realloc(cx->rbuf, cx->rlen);
+ }
+ cx->rbuf[cx->rptr++] = ch;
+
+ if (!cx->rxtogo && !cx->chunked)
+ cx->state = HTS_DONE;
+ }
+ else
+ {
+ if (ch == '\n')
+ {
+ cx->hbuf[cx->hptr] = 0;
+ process_header(cx, cx->hbuf);
+ cx->hptr = 0;
+ }
+ else if (ch != '\r')
+ {
+ if (cx->hptr >= cx->hlen-1)
+ {
+ cx->hlen *= 2;
+ cx->hbuf = (char *)realloc(cx->hbuf, cx->hlen);
+ }
+ cx->hbuf[cx->hptr++] = ch;
+ }
+ }
+}
+
+int http_async_req_status(void *ctx)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ char *dns,*srv,buf[CHUNK];
+ int tmp, i;
+ time_t now = time(NULL);
+#ifdef WIN
+ unsigned long tmp2;
+#endif
+
+ switch (cx->state)
+ {
+ case HTS_STRT:
+ dns = getserv(cx->host);
+ srv = getport(cx->host);
+ if (resolve(dns, srv, &cx->addr))
+ {
+ free(dns);
+ free(srv);
+ cx->state = HTS_DONE;
+ cx->ret = 602;
+ return 1;
+ }
+ free(dns);
+ free(srv);
+ cx->state = HTS_RSLV;
+ return 0;
+ case HTS_RSLV:
+ cx->state = HTS_CONN;
+ cx->last = now;
+ return 0;
+ case HTS_CONN:
+ if (cx->fd == PERROR)
+ {
+ cx->fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (cx->fd == PERROR)
+ goto fail;
+ cx->fdhost = mystrdup(cx->host);
+#ifdef WIN
+ tmp2 = 1;
+ if (ioctlsocket(cx->fd, FIONBIO, &tmp2) == SOCKET_ERROR)
+ goto fail;
+#else
+ tmp = fcntl(cx->fd, F_GETFL);
+ if (tmp < 0)
+ goto fail;
+ if (fcntl(cx->fd, F_SETFL, tmp|O_NONBLOCK) < 0)
+ goto fail;
+#endif
+ }
+ if (!connect(cx->fd, (struct sockaddr *)&cx->addr, sizeof(cx->addr)))
+ cx->state = HTS_IDLE;
+#ifdef WIN
+ else if (PERRNO==WSAEISCONN)
+ cx->state = HTS_IDLE;
+#endif
+#if defined(MACOSX) || defined(BSD)
+ else if (PERRNO==EISCONN)
+ cx->state = HTS_IDLE;
+#endif
+ else if (PERRNO!=PEINPROGRESS && PERRNO!=PEALREADY
+#ifdef WIN
+ && PERRNO!=PEAGAIN && PERRNO!=WSAEINVAL
+#endif
+ )
+ goto fail;
+ if (now-cx->last>http_timeout)
+ goto timeout;
+ return 0;
+ case HTS_IDLE:
+ if (cx->txdl)
+ {
+ // generate POST
+ cx->tbuf = (char *)malloc(strlen(cx->host) + strlen(cx->path) + 132 + strlen(userAgent) + cx->txdl + cx->thlen);
+ cx->tptr = 0;
+ cx->tlen = 0;
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "POST %s HTTP/1.1\r\n", cx->path);
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "Host: %s\r\n", cx->host);
+ if (!cx->keep)
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "Connection: close\r\n");
+ if (cx->thdr)
+ {
+ memcpy(cx->tbuf+cx->tlen, cx->thdr, cx->thlen);
+ cx->tlen += cx->thlen;
+ free(cx->thdr);
+ cx->thdr = NULL;
+ cx->thlen = 0;
+ }
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "Content-Length: %d\r\n", cx->txdl);
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "User-Agent: %s\r\n", userAgent);
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "\r\n");
+ memcpy(cx->tbuf+cx->tlen, cx->txd, cx->txdl);
+ cx->tlen += cx->txdl;
+ free(cx->txd);
+ cx->txd = NULL;
+ cx->txdl = 0;
+ }
+ else
+ {
+ // generate GET
+ cx->tbuf = (char *)malloc(strlen(cx->host) + strlen(cx->path) + 98 + strlen(userAgent) + cx->thlen);
+ cx->tptr = 0;
+ cx->tlen = 0;
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "GET %s HTTP/1.1\r\n", cx->path);
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "Host: %s\r\n", cx->host);
+ if (cx->thdr)
+ {
+ memcpy(cx->tbuf+cx->tlen, cx->thdr, cx->thlen);
+ cx->tlen += cx->thlen;
+ free(cx->thdr);
+ cx->thdr = NULL;
+ cx->thlen = 0;
+ }
+ if (!cx->keep)
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "Connection: close\r\n");
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "User-Agent: %s\r\n", userAgent);
+ cx->tlen += sprintf(cx->tbuf+cx->tlen, "\r\n");
+ }
+ cx->state = HTS_XMIT;
+ cx->last = now;
+ return 0;
+ case HTS_XMIT:
+ tmp = send(cx->fd, cx->tbuf+cx->tptr, cx->tlen-cx->tptr, 0);
+ if (tmp==PERROR && PERRNO!=PEAGAIN && PERRNO!=PEINTR)
+ goto fail;
+ if (tmp!=PERROR)
+ {
+ cx->tptr += tmp;
+ if (cx->tptr == cx->tlen)
+ {
+ cx->tptr = 0;
+ cx->tlen = 0;
+ if (cx->tbuf)
+ free(cx->tbuf);
+ cx->state = HTS_RECV;
+ }
+ cx->last = now;
+ }
+ if (now-cx->last>http_timeout)
+ goto timeout;
+ return 0;
+ case HTS_RECV:
+ tmp = recv(cx->fd, buf, CHUNK, 0);
+ if (tmp==PERROR && PERRNO!=PEAGAIN && PERRNO!=PEINTR)
+ goto fail;
+ if (tmp!=PERROR)
+ {
+ for (i=0; i<tmp; i++)
+ {
+ process_byte(cx, buf[i]);
+ if (cx->state == HTS_DONE)
+ return 1;
+ }
+ cx->last = now;
+ }
+ if (now-cx->last>http_timeout)
+ goto timeout;
+ return 0;
+ case HTS_DONE:
+ return 1;
+ }
+ return 0;
+
+fail:
+ cx->ret = 600;
+ cx->state = HTS_DONE;
+ return 1;
+
+timeout:
+ cx->ret = 605;
+ cx->state = HTS_DONE;
+ return 1;
+}
+
+char *http_async_req_stop(void *ctx, int *ret, int *len)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ char *rxd;
+
+ if (cx->state != HTS_DONE)
+ while (!http_async_req_status(ctx)) ;
+
+ if (cx->host)
+ {
+ free(cx->host);
+ cx->host = NULL;
+ }
+ if (cx->path)
+ {
+ free(cx->path);
+ cx->path = NULL;
+ }
+ if (cx->txd)
+ {
+ free(cx->txd);
+ cx->txd = NULL;
+ cx->txdl = 0;
+ }
+ if (cx->hbuf)
+ {
+ free(cx->hbuf);
+ cx->hbuf = NULL;
+ }
+ if (cx->thdr)
+ {
+ free(cx->thdr);
+ cx->thdr = NULL;
+ cx->thlen = 0;
+ }
+
+ if (ret)
+ *ret = cx->ret;
+ if (len)
+ *len = cx->rptr;
+ if (cx->rbuf)
+ cx->rbuf[cx->rptr] = 0;
+ rxd = cx->rbuf;
+ cx->rbuf = NULL;
+ cx->rlen = 0;
+ cx->rptr = 0;
+ cx->contlen = 0;
+
+ if (!cx->keep)
+ http_async_req_close(ctx);
+ else if (cx->cclose)
+ {
+ PCLOSE(cx->fd);
+ cx->fd = PERROR;
+ if (cx->fdhost)
+ {
+ free(cx->fdhost);
+ cx->fdhost = NULL;
+ }
+ cx->state = HTS_STRT;
+ }
+ else
+ cx->state = HTS_IDLE;
+
+ return rxd;
+}
+
+void http_async_get_length(void *ctx, int *total, int *done)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ if (done)
+ *done = cx->rptr;
+ if (total)
+ *total = cx->contlen;
+}
+
+void http_async_req_close(void *ctx)
+{
+ struct http_ctx *cx = (http_ctx *)ctx;
+ void *tmp;
+ if (cx->host)
+ {
+ cx->keep = 1;
+ tmp = http_async_req_stop(ctx, NULL, NULL);
+ if (tmp)
+ free(tmp);
+ }
+ if (cx->fdhost)
+ free(cx->fdhost);
+ PCLOSE(cx->fd);
+ free(ctx);
+}
+
+char *http_simple_get(char *uri, int *ret, int *len)
+{
+ void *ctx = http_async_req_start(NULL, uri, NULL, 0, 0);
+ if (!ctx)
+ {
+ if (ret)
+ *ret = 600;
+ if (len)
+ *len = 0;
+ return NULL;
+ }
+ return http_async_req_stop(ctx, ret, len);
+}
+void http_auth_headers(void *ctx, char *user, char *pass, char *session_id)
+{
+ char *tmp;
+ int i;
+ unsigned char hash[16];
+ unsigned int m;
+ struct md5_context md5;
+
+ if (user)
+ {
+ if (pass)
+ {
+ md5_init(&md5);
+ md5_update(&md5, (unsigned char *)user, strlen(user));
+ md5_update(&md5, (unsigned char *)"-", 1);
+ m = 0;
+
+ md5_update(&md5, (unsigned char *)pass, strlen(pass));
+ md5_final(hash, &md5);
+ tmp = (char *)malloc(33);
+ for (i=0; i<16; i++)
+ {
+ tmp[i*2] = hexChars[hash[i]>>4];
+ tmp[i*2+1] = hexChars[hash[i]&15];
+ }
+ tmp[32] = 0;
+ http_async_add_header(ctx, "X-Auth-Hash", tmp);
+ free(tmp);
+ }
+ if (session_id)
+ {
+ http_async_add_header(ctx, "X-Auth-User-Id", user);
+ http_async_add_header(ctx, "X-Auth-Session-Key", session_id);
+ }
+ else
+ {
+ http_async_add_header(ctx, "X-Auth-User", user);
+ }
+ }
+}
+char *http_auth_get(char *uri, char *user, char *pass, char *session_id, int *ret, int *len)
+{
+ void *ctx = http_async_req_start(NULL, uri, NULL, 0, 0);
+
+ if (!ctx)
+ {
+ if (ret)
+ *ret = 600;
+ if (len)
+ *len = 0;
+ return NULL;
+ }
+ http_auth_headers(ctx, user, pass, session_id);
+ return http_async_req_stop(ctx, ret, len);
+}
+
+char *http_simple_post(char *uri, char *data, int dlen, int *ret, int *len)
+{
+ void *ctx = http_async_req_start(NULL, uri, data, dlen, 0);
+ if (!ctx)
+ {
+ if (ret)
+ *ret = 600;
+ if (len)
+ *len = 0;
+ return NULL;
+ }
+ return http_async_req_stop(ctx, ret, len);
+}
+
+char *http_ret_text(int ret)
+{
+ switch (ret)
+ {
+ case 100:
+ return "Continue";
+ case 101:
+ return "Switching Protocols";
+ case 102:
+ return "Processing";
+
+ case 200:
+ return "OK";
+ case 201:
+ return "Created";
+ case 202:
+ return "Accepted";
+ case 203:
+ return "Non-Authoritative Information";
+ case 204:
+ return "No Content";
+ case 205:
+ return "Reset Content";
+ case 206:
+ return "Partial Content";
+ case 207:
+ return "Multi-Status";
+
+ case 300:
+ return "Multiple Choices";
+ case 301:
+ return "Moved Permanently";
+ case 302:
+ return "Found";
+ case 303:
+ return "See Other";
+ case 304:
+ return "Not Modified";
+ case 305:
+ return "Use Proxy";
+ case 306:
+ return "Switch Proxy";
+ case 307:
+ return "Temporary Redirect";
+
+ case 400:
+ return "Bad Request";
+ case 401:
+ return "Unauthorized";
+ case 402:
+ return "Payment Required";
+ case 403:
+ return "Forbidden";
+ case 404:
+ return "Not Found";
+ case 405:
+ return "Method Not Allowed";
+ case 406:
+ return "Not Acceptable";
+ case 407:
+ return "Proxy Authentication Required";
+ case 408:
+ return "Request Timeout";
+ case 409:
+ return "Conflict";
+ case 410:
+ return "Gone";
+ case 411:
+ return "Length Required";
+ case 412:
+ return "Precondition Failed";
+ case 413:
+ return "Request Entity Too Large";
+ case 414:
+ return "Request URI Too Long";
+ case 415:
+ return "Unsupported Media Type";
+ case 416:
+ return "Requested Range Not Satisfiable";
+ case 417:
+ return "Expectation Failed";
+ case 418:
+ return "I'm a teapot";
+ case 422:
+ return "Unprocessable Entity";
+ case 423:
+ return "Locked";
+ case 424:
+ return "Failed Dependency";
+ case 425:
+ return "Unordered Collection";
+ case 426:
+ return "Upgrade Required";
+ case 444:
+ return "No Response";
+ case 450:
+ return "Blocked by Windows Parental Controls";
+ case 499:
+ return "Client Closed Request";
+
+ case 500:
+ return "Internal Server Error";
+ case 501:
+ return "Not Implemented";
+ case 502:
+ return "Bad Gateway";
+ case 503:
+ return "Service Unavailable";
+ case 504:
+ return "Gateway Timeout";
+ case 505:
+ return "HTTP Version Not Supported";
+ case 506:
+ return "Variant Also Negotiates";
+ case 507:
+ return "Insufficient Storage";
+ case 509:
+ return "Bandwidth Limit Exceeded";
+ case 510:
+ return "Not Extended";
+
+ case 600:
+ return "Internal Client Error";
+ case 601:
+ return "Unsupported Protocol";
+ case 602:
+ return "Server Not Found";
+ case 603:
+ return "Malformed Response";
+ case 604:
+ return "Network Not Available";
+ case 605:
+ return "Request Timed Out";
+ default:
+ return "Unknown Status Code";
+ }
+}
+char *http_multipart_post(char *uri, char **names, char **parts, int *plens, char *user, char *pass, char *session_id, int *ret, int *len)
+{
+ void *ctx;
+ char *data = NULL, *tmp, *p;
+ int dlen = 0, i, j;
+ unsigned char hash[16];
+ unsigned char boundary[32], ch;
+ int blen = 0;
+ unsigned int map[62], m;
+ struct md5_context md5;
+ //struct md5_context md52;
+ int own_plen = 0;
+
+ if (names)
+ {
+ if (!plens)
+ {
+ own_plen = 1;
+ for (i=0; names[i]; i++) ;
+ plens = (int *)calloc(i, sizeof(int));
+ for (i=0; names[i]; i++)
+ plens[i] = strlen(parts[i]);
+ }
+
+retry:
+ if (blen >= 31)
+ goto fail;
+ memset(map, 0, 62*sizeof(int));
+ for (i=0; names[i]; i++)
+ {
+ for (j=0; j<plens[i]-blen; j++)
+ if (!blen || !memcmp(parts[i]+j, boundary, blen))
+ {
+ ch = parts[i][j+blen];
+ if (ch>='0' && ch<='9')
+ map[ch-'0']++;
+ else if (ch>='A' && ch<='Z')
+ map[ch-'A'+10]++;
+ else if (ch>='a' && ch<='z')
+ map[ch-'a'+36]++;
+ }
+ }
+ m = ~0;
+ j = 61;
+ for (i=0; i<62; i++)
+ if (map[i]<m)
+ {
+ m = map[i];
+ j = i;
+ }
+ if (j<10)
+ boundary[blen] = '0'+j;
+ else if (j<36)
+ boundary[blen] = 'A'+(j-10);
+ else
+ boundary[blen] = 'a'+(j-36);
+ blen++;
+ if (map[j])
+ goto retry;
+ boundary[blen] = 0;
+
+ for (i=0; names[i]; i++)
+ dlen += blen+strlen(names[i])+plens[i]+128;
+ dlen += blen+8;
+ data = (char *)malloc(dlen);
+ dlen = 0;
+ for (i=0; names[i]; i++)
+ {
+ dlen += sprintf(data+dlen, "--%s\r\n", boundary);
+ dlen += sprintf(data+dlen, "Content-transfer-encoding: binary\r\n");
+ if (strchr(names[i], ':'))
+ {
+ tmp = mystrdup(names[i]);
+ p = strchr(tmp, ':');
+ *p = 0;
+ dlen += sprintf(data+dlen, "content-disposition: form-data; name=\"%s\"; ", tmp);
+ free(tmp);
+ p = strchr(names[i], ':');
+ dlen += sprintf(data+dlen, "filename=\"%s\"\r\n\r\n", p+1);
+ }
+ else
+ dlen += sprintf(data+dlen, "content-disposition: form-data; name=\"%s\"\r\n\r\n", names[i]);
+ memcpy(data+dlen, parts[i], plens[i]);
+ dlen += plens[i];
+ dlen += sprintf(data+dlen, "\r\n");
+ }
+ dlen += sprintf(data+dlen, "--%s--\r\n", boundary);
+ }
+
+ ctx = http_async_req_start(NULL, uri, data, dlen, 0);
+ if (!ctx)
+ goto fail;
+
+ if (user)
+ {
+ //http_async_add_header(ctx, "X-Auth-User", user);
+ if (pass)
+ {
+ md5_init(&md5);
+ md5_update(&md5, (unsigned char *)user, strlen(user));
+ md5_update(&md5, (unsigned char *)"-", 1);
+ m = 0;
+ if (names)
+ {
+ for (i=0; names[i]; i++)
+ {
+ //md5_update(&md5, (unsigned char *)parts[i], plens[i]); //WHY?
+ //md5_update(&md5, (unsigned char *)"-", 1);
+ p = strchr(names[i], ':');
+ if (p)
+ m += (p - names[i]) + 1;
+ else
+ m += strlen(names[i])+1;
+ }
+
+ tmp = (char *)malloc(m);
+ m = 0;
+ for (i=0; names[i]; i++)
+ {
+ p = strchr(names[i], ':');
+ if (m)
+ {
+ tmp[m] = ' ';
+ m ++;
+ }
+ if (p)
+ {
+ memcpy(tmp+m, names[i], p-names[i]);
+ m += p - names[i];
+ }
+ else
+ {
+ strcpy(tmp+m, names[i]);
+ m += strlen(names[i]);
+ }
+ }
+ tmp[m] = 0;
+ http_async_add_header(ctx, "X-Auth-Objects", tmp);
+ free(tmp);
+ }
+
+ md5_update(&md5, (unsigned char *)pass, strlen(pass));
+ md5_final(hash, &md5);
+ tmp = (char *)malloc(33);
+ for (i=0; i<16; i++)
+ {
+ tmp[i*2] = hexChars[hash[i]>>4];
+ tmp[i*2+1] = hexChars[hash[i]&15];
+ }
+ tmp[32] = 0;
+ http_async_add_header(ctx, "X-Auth-Hash", tmp);
+ free(tmp);
+ }
+ if (session_id)
+ {
+ http_async_add_header(ctx, "X-Auth-User-Id", user);
+ http_async_add_header(ctx, "X-Auth-Session-Key", session_id);
+ }
+ else
+ {
+ http_async_add_header(ctx, "X-Auth-User", user);
+ }
+ }
+
+ if (data)
+ {
+ tmp = (char *)malloc(32+strlen((char *)boundary));
+ sprintf(tmp, "multipart/form-data, boundary=%s", boundary);
+ http_async_add_header(ctx, "Content-type", tmp);
+ free(tmp);
+ free(data);
+ }
+
+ if (own_plen)
+ free(plens);
+ return http_async_req_stop(ctx, ret, len);
+
+fail:
+ if (data)
+ free(data);
+ if (own_plen)
+ free(plens);
+ if (ret)
+ *ret = 600;
+ if (len)
+ *len = 0;
+ return NULL;
+}
diff --git a/src/client/HTTP.h b/src/client/HTTP.h
new file mode 100644
index 0000000..51b9438
--- /dev/null
+++ b/src/client/HTTP.h
@@ -0,0 +1,45 @@
+/**
+ * Powder Toy - HTTP Library (Header)
+ *
+ * Copyright (c) 2008 - 2010 Stanislaw Skowronek.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+#ifndef HTTP_H
+#define HTTP_H
+
+static char hexChars[] = "0123456789abcdef";
+
+void http_init(char *proxy);
+void http_done(void);
+
+char *http_simple_get(char *uri, int *ret, int *len);
+char *http_auth_get(char *uri, char *user, char *pass, char * session_id, int *ret, int *len);
+char *http_simple_post(char *uri, char *data, int dlen, int *ret, int *len);
+
+void http_auth_headers(void *ctx, char *user, char *pass, char * session_id);
+
+void *http_async_req_start(void *ctx, char *uri, char *data, int dlen, int keep);
+void http_async_add_header(void *ctx, char *name, char *data);
+int http_async_req_status(void *ctx);
+void http_async_get_length(void *ctx, int *total, int *done);
+char *http_async_req_stop(void *ctx, int *ret, int *len);
+void http_async_req_close(void *ctx);
+
+char *http_multipart_post(char *uri, char **names, char **parts, int *plens, char *user, char *pass, char * session_id, int *ret, int *len);
+
+char *http_ret_text(int ret);
+
+#endif
diff --git a/src/client/MD5.cpp b/src/client/MD5.cpp
new file mode 100644
index 0000000..d921bfa
--- /dev/null
+++ b/src/client/MD5.cpp
@@ -0,0 +1,231 @@
+// based on public-domain code from Colin Plumb (1993)
+#include <string.h>
+#include "MD5.h"
+
+static unsigned getu32(const unsigned char *addr)
+{
+ return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) | addr[1]) << 8 | addr[0];
+}
+
+static void putu32(unsigned data, unsigned char *addr)
+{
+ addr[0] = (unsigned char)data;
+ addr[1] = (unsigned char)(data >> 8);
+ addr[2] = (unsigned char)(data >> 16);
+ addr[3] = (unsigned char)(data >> 24);
+}
+
+void md5_init(struct md5_context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+void md5_update(struct md5_context *ctx, unsigned char const *buf, unsigned len)
+{
+ unsigned t;
+
+ // update bit count
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = (t + ((unsigned)len << 3)) & 0xffffffff) < t)
+ ctx->bits[1]++; // carry
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f;
+
+ // use leading data to top up the buffer
+
+ if (t)
+ {
+ unsigned char *p = ctx->in + t;
+
+ t = 64-t;
+ if (len < t)
+ {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ md5_transform(ctx->buf, ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ // following 64-byte chunks
+
+ while (len >= 64)
+ {
+ memcpy(ctx->in, buf, 64);
+ md5_transform(ctx->buf, ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ // save rest of bytes for later
+
+ memcpy(ctx->in, buf, len);
+}
+
+void md5_final(unsigned char digest[16], struct md5_context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ // #bytes mod64
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ // first char of padding = 0x80
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ // calculate # of bytes to pad
+ count = 64 - 1 - count;
+
+ // Pad out to 56 mod 64
+ if (count < 8)
+ {
+ // we need to finish a whole block before padding
+ memset(p, 0, count);
+ md5_transform(ctx->buf, ctx->in);
+ memset(ctx->in, 0, 56);
+ }
+ else
+ {
+ // just pad to 56 bytes
+ memset(p, 0, count-8);
+ }
+
+ // append length & final transform
+ putu32(ctx->bits[0], ctx->in + 56);
+ putu32(ctx->bits[1], ctx->in + 60);
+
+ md5_transform(ctx->buf, ctx->in);
+ putu32(ctx->buf[0], digest);
+ putu32(ctx->buf[1], digest + 4);
+ putu32(ctx->buf[2], digest + 8);
+ putu32(ctx->buf[3], digest + 12);
+ memset(ctx, 0, sizeof(ctx));
+}
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x )
+
+void md5_transform(unsigned buf[4], const unsigned char inraw[64])
+{
+ unsigned a, b, c, d;
+ unsigned in[16];
+ int i;
+
+ for (i = 0; i < 16; ++i)
+ in[i] = getu32 (inraw + 4 * i);
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+static char hexChars[] = "0123456789abcdef";
+void md5_ascii(char *result, unsigned char const *buf, unsigned len)
+{
+ struct md5_context md5;
+ unsigned char hash[16];
+ int i;
+
+ if (len==0)
+ len = strlen((char *)buf);
+
+ md5_init(&md5);
+ md5_update(&md5, buf, len);
+ md5_final(hash, &md5);
+
+ for (i=0; i<16; i++)
+ {
+ result[i*2] = hexChars[(hash[i]>>4)&0xF];
+ result[i*2+1] = hexChars[hash[i]&0x0F];
+ }
+ result[32] = 0;
+}
diff --git a/src/client/MD5.h b/src/client/MD5.h
new file mode 100644
index 0000000..a8ef123
--- /dev/null
+++ b/src/client/MD5.h
@@ -0,0 +1,18 @@
+#ifndef MD5_H
+#define MD5_H
+
+struct md5_context
+{
+ unsigned buf[4];
+ unsigned bits[2];
+ unsigned char in[64];
+};
+
+void md5_init(struct md5_context *context);
+void md5_update(struct md5_context *context, unsigned char const *buf, unsigned len);
+void md5_final(unsigned char digest[16], struct md5_context *context);
+void md5_transform(unsigned buf[4], const unsigned char in[64]);
+
+void md5_ascii(char *result, unsigned char const *buf, unsigned len);
+
+#endif
diff --git a/src/client/SaveFile.cpp b/src/client/SaveFile.cpp
new file mode 100644
index 0000000..fe7ad4f
--- /dev/null
+++ b/src/client/SaveFile.cpp
@@ -0,0 +1,80 @@
+/*
+ * SaveFile.cpp
+ *
+ * Created on: Jun 6, 2012
+ * Author: Simon
+ */
+
+#include "SaveFile.h"
+#include "GameSave.h"
+#include "Client.h"
+#include "search/Thumbnail.h"
+
+SaveFile::SaveFile(SaveFile & save):
+ gameSave(NULL),
+ thumbnail(NULL),
+ filename(save.filename),
+ displayName(save.displayName)
+{
+ if(save.gameSave)
+ gameSave = new GameSave(*save.gameSave);
+ if(save.thumbnail)
+ thumbnail = new Thumbnail(*save.thumbnail);
+}
+
+Thumbnail * SaveFile::GetThumbnail()
+{
+ return thumbnail;
+}
+
+void SaveFile::SetThumbnail(Thumbnail * thumb)
+{
+ thumbnail = thumb;
+}
+
+SaveFile::SaveFile(std::string filename):
+ filename(filename),
+ displayName(filename),
+ gameSave(NULL),
+ thumbnail(NULL)
+{
+
+}
+
+GameSave * SaveFile::GetGameSave()
+{
+ return gameSave;
+}
+
+void SaveFile::SetGameSave(GameSave * save)
+{
+ gameSave = save;
+}
+
+std::string SaveFile::GetName()
+{
+ return filename;
+}
+
+void SaveFile::SetFileName(std::string fileName)
+{
+ this->filename = fileName;
+}
+
+std::string SaveFile::GetDisplayName()
+{
+ return displayName;
+}
+
+void SaveFile::SetDisplayName(std::string displayName)
+{
+ this->displayName = displayName;
+}
+
+SaveFile::~SaveFile() {
+ if(gameSave)
+ delete gameSave;
+ if(thumbnail)
+ delete thumbnail;
+}
+
diff --git a/src/client/SaveFile.h b/src/client/SaveFile.h
new file mode 100644
index 0000000..b63d181
--- /dev/null
+++ b/src/client/SaveFile.h
@@ -0,0 +1,38 @@
+/*
+ * SaveFile.h
+ *
+ * Created on: Jun 6, 2012
+ * Author: Simon
+ */
+
+#ifndef SAVEFILE_H_
+#define SAVEFILE_H_
+
+#include <string>
+
+class GameSave;
+class Thumbnail;
+
+class SaveFile {
+public:
+ SaveFile(SaveFile & save);
+ SaveFile(std::string filename);
+
+ Thumbnail * GetThumbnail();
+ GameSave * GetGameSave();
+ void SetThumbnail(Thumbnail * thumb);
+ void SetGameSave(GameSave * save);
+ std::string GetDisplayName();
+ void SetDisplayName(std::string displayName);
+ std::string GetName();
+ void SetFileName(std::string fileName);
+
+ virtual ~SaveFile();
+private:
+ Thumbnail * thumbnail;
+ GameSave * gameSave;
+ std::string filename;
+ std::string displayName;
+};
+
+#endif /* SAVEFILE_H_ */
diff --git a/src/client/SaveInfo.cpp b/src/client/SaveInfo.cpp
new file mode 100644
index 0000000..2d8fd52
--- /dev/null
+++ b/src/client/SaveInfo.cpp
@@ -0,0 +1,128 @@
+/*
+ * Save.cpp
+ *
+ * Created on: Jan 26, 2012
+ * Author: Simon
+ */
+
+#include "SaveInfo.h"
+#include "GameSave.h"
+#include "Client.h"
+
+SaveInfo::SaveInfo(SaveInfo & save) :
+ userName(save.userName), name(save.name), Description(save.Description), date(
+ save.date), Published(save.Published), id(save.id), votesUp(
+ save.votesUp), votesDown(save.votesDown), gameSave(NULL), vote(save.vote), tags(save.tags), Comments(save.Comments), Views(save.Views), Version(save.Version) {
+ if(save.gameSave)
+ gameSave = new GameSave(*save.gameSave);
+}
+
+SaveInfo::SaveInfo(int _id, int _date, int _votesUp, int _votesDown, std::string _userName,
+ std::string _name) :
+ id(_id), votesUp(_votesUp), votesDown(_votesDown), userName(_userName), name(
+ _name), Description(""), date(_date), Published(
+ true), gameSave(NULL), vote(0), tags(), Comments(0), Views(0), Version(0) {
+}
+
+SaveInfo::SaveInfo(int _id, int date_, int _votesUp, int _votesDown, int _vote, std::string _userName,
+ std::string _name, std::string description_, bool published_, std::vector<std::string> tags_) :
+ id(_id), votesUp(_votesUp), votesDown(_votesDown), userName(_userName), name(
+ _name), Description(description_), date(date_), Published(
+ published_), gameSave(NULL), vote(_vote), tags(tags_), Views(0), Comments(0), Version(0) {
+}
+
+SaveInfo::~SaveInfo()
+{
+ if(gameSave)
+ {
+ delete gameSave;
+ }
+}
+
+void SaveInfo::SetName(std::string name) {
+ this->name = name;
+}
+std::string SaveInfo::GetName() {
+ return name;
+}
+
+void SaveInfo::SetDescription(std::string description) {
+ Description = description;
+}
+std::string SaveInfo::GetDescription() {
+ return Description;
+}
+
+void SaveInfo::SetPublished(bool published) {
+ Published = published;
+}
+
+bool SaveInfo::GetPublished() {
+ return Published;
+}
+
+void SaveInfo::SetVote(int vote)
+{
+ this->vote = vote;
+}
+int SaveInfo::GetVote()
+{
+ return vote;
+}
+
+void SaveInfo::SetUserName(std::string userName) {
+ this->userName = userName;
+}
+
+std::string SaveInfo::GetUserName() {
+ return userName;
+}
+
+void SaveInfo::SetID(int id) {
+ this->id = id;
+}
+int SaveInfo::GetID() {
+ return id;
+}
+
+void SaveInfo::SetVotesUp(int votesUp) {
+ this->votesUp = votesUp;
+}
+int SaveInfo::GetVotesUp() {
+ return votesUp;
+}
+
+void SaveInfo::SetVotesDown(int votesDown) {
+ this->votesDown = votesDown;
+}
+int SaveInfo::GetVotesDown() {
+ return votesDown;
+}
+
+void SaveInfo::SetVersion(int version) {
+ this->Version = version;
+}
+int SaveInfo::GetVersion() {
+ return Version;
+}
+
+void SaveInfo::SetTags(std::vector<std::string> tags)
+{
+ this->tags = tags;
+}
+std::vector<std::string> SaveInfo::GetTags()
+{
+ return tags;
+}
+
+GameSave * SaveInfo::GetGameSave()
+{
+ return gameSave;
+}
+
+void SaveInfo::SetGameSave(GameSave * saveGame)
+{
+ if(gameSave)
+ delete gameSave;
+ gameSave = saveGame;
+}
diff --git a/src/client/SaveInfo.h b/src/client/SaveInfo.h
new file mode 100644
index 0000000..3f52c25
--- /dev/null
+++ b/src/client/SaveInfo.h
@@ -0,0 +1,78 @@
+#ifndef SAVE_H
+#define SAVE_H
+
+#include <vector>
+#include <string>
+#include <stdlib.h>
+#include <iostream>
+
+class GameSave;
+
+class SaveInfo
+{
+private:
+public:
+ int id;
+ int date;
+ int votesUp, votesDown;
+ bool Favourite;
+ int Comments;
+ int Views;
+ int Version;
+
+ GameSave * gameSave;
+
+ SaveInfo(SaveInfo & save);
+
+ SaveInfo(int _id, int _date, int _votesUp, int _votesDown, std::string _userName, std::string _name);
+
+ SaveInfo(int _id, int date_, int _votesUp, int _votesDown, int _vote, std::string _userName, std::string _name, std::string description_, bool published_, std::vector<std::string> tags);
+
+ ~SaveInfo();
+
+ std::string userName;
+ std::string name;
+
+ std::string Description;
+
+ std::vector<std::string> tags;
+
+ int vote;
+
+ bool Published;
+
+ void SetName(std::string name);
+ std::string GetName();
+
+ void SetDescription(std::string description);
+ std::string GetDescription();
+
+ void SetPublished(bool published);
+ bool GetPublished();
+
+ void SetUserName(std::string userName);
+ std::string GetUserName();
+
+ void SetID(int id);
+ int GetID();
+
+ void SetVote(int vote);
+ int GetVote();
+
+ void SetVotesUp(int votesUp);
+ int GetVotesUp();
+
+ void SetVotesDown(int votesDown);
+ int GetVotesDown();
+
+ void SetVersion(int version);
+ int GetVersion();
+
+ void SetTags(std::vector<std::string> tags);
+ std::vector<std::string> GetTags();
+
+ GameSave * GetGameSave();
+ void SetGameSave(GameSave * gameSave);
+};
+
+#endif // SAVE_H
diff --git a/src/client/ThumbnailBroker.cpp b/src/client/ThumbnailBroker.cpp
new file mode 100644
index 0000000..e884a98
--- /dev/null
+++ b/src/client/ThumbnailBroker.cpp
@@ -0,0 +1,347 @@
+#include <algorithm>
+#include <iostream>
+#include <typeinfo>
+#include "ThumbnailBroker.h"
+#include "ThumbnailListener.h"
+#include "Client.h"
+#include "HTTP.h"
+#include "GameSave.h"
+#include "search/Thumbnail.h"
+#include "simulation/SaveRenderer.h"
+
+//Asynchronous Thumbnail render & request processing
+
+ThumbnailBroker::ThumbnailBroker()
+{
+ thumbnailQueueRunning = false;
+ //thumbnailQueueMutex = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutex_init (&thumbnailQueueMutex, NULL);
+
+ //listenersMutex = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutex_init (&listenersMutex, NULL);
+}
+
+ThumbnailBroker::~ThumbnailBroker()
+{
+
+}
+
+void ThumbnailBroker::RenderThumbnail(GameSave * gameSave, int width, int height, ThumbnailListener * tListener)
+{
+ RenderThumbnail(gameSave, true, true, width, height, tListener);
+}
+
+void ThumbnailBroker::RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, ThumbnailListener * tListener)
+{
+ AttachThumbnailListener(tListener);
+ pthread_mutex_lock(&thumbnailQueueMutex);
+ bool running = thumbnailQueueRunning;
+ thumbnailQueueRunning = true;
+ renderRequests.push_back(ThumbRenderRequest(new GameSave(*gameSave), decorations, fire, width, height, ListenerHandle(tListener->ListenerRand, tListener)));
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+
+ if(!running)
+ {
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " Starting background thread for new " << __FUNCTION__ << " request" << std::endl;
+#endif
+ pthread_create(&thumbnailQueueThread, 0, &ThumbnailBroker::thumbnailQueueProcessHelper, this);
+ }
+}
+
+void ThumbnailBroker::RetrieveThumbnail(int saveID, int saveDate, int width, int height, ThumbnailListener * tListener)
+{
+ AttachThumbnailListener(tListener);
+ pthread_mutex_lock(&thumbnailQueueMutex);
+ bool running = thumbnailQueueRunning;
+ thumbnailQueueRunning = true;
+ thumbnailRequests.push_back(ThumbnailRequest(saveID, saveDate, width, height, ListenerHandle(tListener->ListenerRand, tListener)));
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+
+ if(!running)
+ {
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " Starting background thread for new " << __FUNCTION__ << " request" << std::endl;
+#endif
+ pthread_create(&thumbnailQueueThread, 0, &ThumbnailBroker::thumbnailQueueProcessHelper, this);
+ }
+}
+
+void * ThumbnailBroker::thumbnailQueueProcessHelper(void * ref)
+{
+ ((ThumbnailBroker*)ref)->thumbnailQueueProcessTH();
+ return NULL;
+}
+
+void ThumbnailBroker::FlushThumbQueue()
+{
+ pthread_mutex_lock(&thumbnailQueueMutex);
+ while(thumbnailComplete.size())
+ {
+ if(CheckThumbnailListener(thumbnailComplete.front().first))
+ {
+ thumbnailComplete.front().first.second->OnThumbnailReady(thumbnailComplete.front().second);
+ }
+ else
+ {
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " Listener lost, discarding request" << std::endl;
+#endif
+ delete thumbnailComplete.front().second;
+ }
+ thumbnailComplete.pop_front();
+ }
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+}
+
+void ThumbnailBroker::thumbnailQueueProcessTH()
+{
+ time_t lastAction = time(NULL);
+ while(true)
+ {
+ //Shutdown after 2 seconds of idle
+ if(time(NULL) - lastAction > 2)
+ {
+ pthread_mutex_lock(&thumbnailQueueMutex);
+ thumbnailQueueRunning = false;
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+ break;
+ }
+
+ //Renderer
+ pthread_mutex_lock(&thumbnailQueueMutex);
+ if(renderRequests.size())
+ {
+ lastAction = time(NULL);
+ ThumbRenderRequest req;
+ req = renderRequests.front();
+ renderRequests.pop_front();
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " Processing render request" << std::endl;
+#endif
+
+ Thumbnail * thumbnail = SaveRenderer::Ref().Render(req.Save, req.Decorations, req.Fire);
+ delete req.Save;
+
+ if(thumbnail)
+ {
+ thumbnail->Resize(req.Width, req.Height);
+
+ pthread_mutex_lock(&thumbnailQueueMutex);
+ thumbnailComplete.push_back(std::pair<ListenerHandle, Thumbnail*>(req.CompletedListener, thumbnail));
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+ }
+ }
+ else
+ {
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+ }
+
+ //Renderer
+ pthread_mutex_lock(&thumbnailQueueMutex);
+ if(thumbnailRequests.size())
+ {
+ lastAction = time(NULL);
+ Thumbnail * thumbnail = NULL;
+
+ ThumbnailRequest req;
+ req = thumbnailRequests.front();
+
+ //Check the cache
+ for(std::deque<std::pair<ThumbnailID, Thumbnail*> >::iterator iter = thumbnailCache.begin(), end = thumbnailCache.end(); iter != end; ++iter)
+ {
+ if((*iter).first == req.ID)
+ {
+ thumbnail = (*iter).second;
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " " << req.ID.SaveID << ":" << req.ID.SaveDate << " found in cache" << std::endl;
+#endif
+ }
+ }
+
+ if(thumbnail)
+ {
+ //Got thumbnail from cache
+ thumbnailRequests.pop_front();
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+
+ for(std::vector<ThumbnailSpec>::iterator specIter = req.SubRequests.begin(), specEnd = req.SubRequests.end(); specIter != specEnd; ++specIter)
+ {
+ Thumbnail * tempThumbnail = new Thumbnail(*thumbnail);
+ tempThumbnail->Resize((*specIter).Width, (*specIter).Height);
+
+ pthread_mutex_lock(&thumbnailQueueMutex);
+ thumbnailComplete.push_back(std::pair<ListenerHandle, Thumbnail*>((*specIter).CompletedListener, tempThumbnail));
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+ }
+ }
+ else
+ {
+ //Check for ongoing requests
+ bool requested = false;
+ for(std::list<ThumbnailRequest>::iterator iter = currentRequests.begin(), end = currentRequests.end(); iter != end; ++iter)
+ {
+ if((*iter).ID == req.ID)
+ {
+ requested = true;
+
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " Request for " << req.ID.SaveID << ":" << req.ID.SaveDate << " found, appending." << std::endl;
+#endif
+
+ //Add the current listener to the item already being requested
+ (*iter).SubRequests.push_back(req.SubRequests.front());
+ }
+ }
+
+ if(requested)
+ {
+ //Already requested
+ thumbnailRequests.pop_front();
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+ }
+ else if(currentRequests.size() < IMGCONNS) //If it's not already being requested and we still have more space for a new connection, request it
+ {
+ thumbnailRequests.pop_front();
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+
+ //If it's not already being requested, request it
+ std::stringstream urlStream;
+ urlStream << "http://" << STATICSERVER << "/" << req.ID.SaveID;
+ if(req.ID.SaveDate)
+ {
+ urlStream << "_" << req.ID.SaveDate;
+ }
+ urlStream << "_small.pti";
+
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " Creating new request for " << req.ID.SaveID << ":" << req.ID.SaveDate << std::endl;
+#endif
+
+ req.HTTPContext = http_async_req_start(NULL, (char *)urlStream.str().c_str(), NULL, 0, 1);
+ req.RequestTime = time(NULL);
+ currentRequests.push_back(req);
+ }
+ else
+ {
+ //Already full of requests
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+
+ }
+ }
+ }
+ else
+ {
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+ }
+
+ std::list<ThumbnailRequest>::iterator iter = currentRequests.begin();
+ while (iter != currentRequests.end())
+ {
+ lastAction = time(NULL);
+
+ ThumbnailRequest req = *iter;
+ Thumbnail * thumbnail = NULL;
+
+ if(http_async_req_status(req.HTTPContext))
+ {
+
+ pixel * thumbData;
+ char * data;
+ int status, data_size, imgw, imgh;
+ data = http_async_req_stop(req.HTTPContext, &status, &data_size);
+ free(req.HTTPContext);
+
+ if (status == 200 && data)
+ {
+ thumbData = Graphics::ptif_unpack(data, data_size, &imgw, &imgh);
+ free(data);
+
+ if(thumbData)
+ {
+ thumbnail = new Thumbnail(req.ID.SaveID, req.ID.SaveID, thumbData, ui::Point(imgw, imgh));
+ free(thumbData);
+ }
+ else
+ {
+ //Error thumbnail
+ VideoBuffer errorThumb(128, 128);
+ errorThumb.SetCharacter(64, 64, 'x', 255, 255, 255, 255);
+
+ thumbnail = new Thumbnail(req.ID.SaveID, req.ID.SaveID, errorThumb.Buffer, ui::Point(errorThumb.Width, errorThumb.Height));
+ }
+
+ if(thumbnailCache.size() >= THUMB_CACHE_SIZE)
+ {
+ delete thumbnailCache.front().second;
+ thumbnailCache.pop_front();
+ }
+ thumbnailCache.push_back(std::pair<ThumbnailID, Thumbnail*>(req.ID, thumbnail));
+
+ for(std::vector<ThumbnailSpec>::iterator specIter = req.SubRequests.begin(), specEnd = req.SubRequests.end(); specIter != specEnd; ++specIter)
+ {
+ Thumbnail * tempThumbnail = new Thumbnail(*thumbnail);
+ tempThumbnail->Resize((*specIter).Width, (*specIter).Height);
+
+ pthread_mutex_lock(&thumbnailQueueMutex);
+ thumbnailComplete.push_back(std::pair<ListenerHandle, Thumbnail*>((*specIter).CompletedListener, tempThumbnail));
+ pthread_mutex_unlock(&thumbnailQueueMutex);
+ }
+ }
+ else
+ {
+#ifdef DEBUG
+ std::cout << typeid(*this).name() << " Request for " << req.ID.SaveID << ":" << req.ID.SaveDate << " failed with status " << status << std::endl;
+#endif
+ if(data)
+ free(data);
+ }
+ iter = currentRequests.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ }
+}
+
+void ThumbnailBroker::RetrieveThumbnail(int saveID, int width, int height, ThumbnailListener * tListener)
+{
+ RetrieveThumbnail(saveID, 0, width, height, tListener);
+}
+
+bool ThumbnailBroker::CheckThumbnailListener(ListenerHandle handle)
+{
+ pthread_mutex_lock(&listenersMutex);
+ int count = std::count(validListeners.begin(), validListeners.end(), handle);
+ pthread_mutex_unlock(&listenersMutex);
+
+ return count;
+}
+
+void ThumbnailBroker::AttachThumbnailListener(ThumbnailListener * tListener)
+{
+ pthread_mutex_lock(&listenersMutex);
+ validListeners.push_back(ListenerHandle(tListener->ListenerRand, tListener));
+ pthread_mutex_unlock(&listenersMutex);
+}
+
+void ThumbnailBroker::DetachThumbnailListener(ThumbnailListener * tListener)
+{
+ pthread_mutex_lock(&listenersMutex);
+
+ std::vector<ListenerHandle>::iterator iter = validListeners.begin();
+ while (iter != validListeners.end())
+ {
+ if(*iter == ListenerHandle(tListener->ListenerRand, tListener))
+ iter = validListeners.erase(iter);
+ else
+ ++iter;
+ }
+
+ pthread_mutex_unlock(&listenersMutex);
+} \ No newline at end of file
diff --git a/src/client/ThumbnailBroker.h b/src/client/ThumbnailBroker.h
new file mode 100644
index 0000000..ba6d3ae
--- /dev/null
+++ b/src/client/ThumbnailBroker.h
@@ -0,0 +1,108 @@
+#pragma once
+#include <queue>
+#include <list>
+#include <utility>
+#include <deque>
+#include <pthread.h>
+#undef GetUserName //God dammit microsoft!
+
+#include "Singleton.h"
+
+class GameSave;
+class Thumbnail;
+class ThumbnailListener;
+typedef std::pair<int, ThumbnailListener*> ListenerHandle;
+class ThumbnailBroker: public Singleton<ThumbnailBroker>
+{
+private:
+ class ThumbnailSpec
+ {
+ public:
+ int Width, Height;
+ ListenerHandle CompletedListener;
+ ThumbnailSpec(int width, int height, ListenerHandle completedListener) :
+ Width(width), Height(height), CompletedListener(completedListener) {}
+ };
+
+ class ThumbnailID
+ {
+ public:
+ int SaveID, SaveDate;
+ bool operator ==(const ThumbnailID & second)
+ {
+ return SaveID == second.SaveID && SaveDate == second.SaveDate;
+ }
+ ThumbnailID(int saveID, int saveDate) : SaveID(saveID), SaveDate(saveDate) {}
+ ThumbnailID() : SaveID(0), SaveDate(0) {}
+ };
+
+ class ThumbnailRequest
+ {
+ public:
+ bool Complete;
+ void * HTTPContext;
+ int RequestTime;
+
+ ThumbnailID ID;
+ std::vector<ThumbnailSpec> SubRequests;
+
+ ThumbnailRequest(int saveID, int saveDate, int width, int height, ListenerHandle completedListener) :
+ ID(saveID, saveDate), Complete(false), HTTPContext(NULL), RequestTime(0)
+ {
+ SubRequests.push_back(ThumbnailSpec(width, height, completedListener));
+ }
+ ThumbnailRequest() : Complete(false), HTTPContext(NULL), RequestTime(0) {}
+ };
+
+ class ThumbRenderRequest
+ {
+ public:
+ int Width, Height;
+ bool Decorations;
+ bool Fire;
+ GameSave * Save;
+ ListenerHandle CompletedListener;
+ ThumbRenderRequest(GameSave * save, bool decorations, bool fire, int width, int height, ListenerHandle completedListener) :
+ Save(save), Width(width), Height(height), CompletedListener(completedListener), Decorations(decorations), Fire(fire) {}
+ ThumbRenderRequest() : Save(0), Decorations(true), Fire(true), Width(0), Height(0), CompletedListener(ListenerHandle(0, (ThumbnailListener*)NULL)) {}
+ };
+
+ //Thumbnail retreival
+ /*int thumbnailCacheNextID;
+ Thumbnail * thumbnailCache[THUMB_CACHE_SIZE];
+ void * activeThumbRequests[IMGCONNS];
+ int activeThumbRequestTimes[IMGCONNS];
+ int activeThumbRequestCompleteTimes[IMGCONNS];
+ std::string activeThumbRequestIDs[IMGCONNS];*/
+
+ pthread_mutex_t thumbnailQueueMutex;
+ pthread_mutex_t listenersMutex;
+ pthread_t thumbnailQueueThread;
+ bool thumbnailQueueRunning;
+ std::deque<ThumbnailRequest> thumbnailRequests;
+ std::deque<ThumbRenderRequest> renderRequests;
+
+ std::deque<std::pair<ListenerHandle, Thumbnail*> > thumbnailComplete;
+ std::list<ThumbnailRequest> currentRequests;
+ std::deque<std::pair<ThumbnailID, Thumbnail*> > thumbnailCache;
+
+
+ std::vector<ListenerHandle> validListeners;
+
+ static void * thumbnailQueueProcessHelper(void * ref);
+ void thumbnailQueueProcessTH();
+
+public:
+ ThumbnailBroker();
+ virtual ~ThumbnailBroker();
+
+ void FlushThumbQueue();
+ void RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, ThumbnailListener * tListener);
+ void RenderThumbnail(GameSave * gameSave, int width, int height, ThumbnailListener * tListener);
+ void RetrieveThumbnail(int saveID, int saveDate, int width, int height, ThumbnailListener * tListener);
+ void RetrieveThumbnail(int saveID, int width, int height, ThumbnailListener * tListener);
+
+ bool CheckThumbnailListener(ListenerHandle handle);
+ void AttachThumbnailListener(ThumbnailListener * tListener);
+ void DetachThumbnailListener(ThumbnailListener * tListener);
+}; \ No newline at end of file
diff --git a/src/client/ThumbnailListener.h b/src/client/ThumbnailListener.h
new file mode 100644
index 0000000..97bdef5
--- /dev/null
+++ b/src/client/ThumbnailListener.h
@@ -0,0 +1,12 @@
+#pragma once
+
+class Thumbnail;
+class ThumbnailListener
+{
+public:
+ int ListenerRand;
+ ThumbnailListener() { ListenerRand = rand(); }
+ virtual ~ThumbnailListener() {}
+
+ virtual void OnThumbnailReady(Thumbnail * thumb) {}
+};
diff --git a/src/client/User.h b/src/client/User.h
new file mode 100644
index 0000000..5a47a3e
--- /dev/null
+++ b/src/client/User.h
@@ -0,0 +1,38 @@
+/*
+ * User.h
+ *
+ * Created on: Jan 25, 2012
+ * Author: Simon
+ */
+
+#ifndef USER_H_
+#define USER_H_
+
+#include <string>
+
+
+class User
+{
+public:
+ enum Elevation
+ {
+ ElevationAdmin, ElevationModerator, ElevationNone
+ };
+ int ID;
+ std::string Username;
+ std::string SessionID;
+ std::string SessionKey;
+ Elevation UserElevation;
+ User(int id, std::string username):
+ ID(id),
+ Username(username),
+ SessionID(""),
+ SessionKey(""),
+ UserElevation(ElevationNone)
+ {
+
+ }
+};
+
+
+#endif /* USER_H_ */
diff --git a/src/colourpicker/ColourPickerActivity.cpp b/src/colourpicker/ColourPickerActivity.cpp
new file mode 100644
index 0000000..65bccc9
--- /dev/null
+++ b/src/colourpicker/ColourPickerActivity.cpp
@@ -0,0 +1,311 @@
+/*
+ * ElementSearchActivity.cpp
+ *
+ * Created on: Jun 24, 2012
+ * Author: Simon
+ */
+
+#include <algorithm>
+#include <iomanip>
+#include "ColourPickerActivity.h"
+#include "interface/Textbox.h"
+#include "interface/Label.h"
+#include "interface/Keys.h"
+#include "game/Tool.h"
+#include "Style.h"
+#include "Format.h"
+#include "game/GameModel.h"
+
+ColourPickerActivity::ColourPickerActivity(ui::Colour initialColour, ColourPickedCallback * callback) :
+ WindowActivity(ui::Point(-1, -1), ui::Point(266, 175)),
+ currentHue(0),
+ currentSaturation(0),
+ currentValue(0),
+ mouseDown(false),
+ valueMouseDown(false),
+ callback(callback)
+{
+
+ class ColourChange : public ui::TextboxAction
+ {
+ ColourPickerActivity * a;
+ public:
+ ColourChange(ColourPickerActivity * a) : a(a) {}
+
+ void TextChangedCallback(ui::Textbox * sender)
+ {
+ int r, g, b, alpha;
+ r = format::StringToNumber<int>(a->rValue->GetText());
+ g = format::StringToNumber<int>(a->gValue->GetText());
+ b = format::StringToNumber<int>(a->bValue->GetText());
+ alpha = format::StringToNumber<int>(a->aValue->GetText());
+ if (r > 255)
+ r = 255;
+ if (g > 255)
+ g = 255;
+ if (b > 255)
+ b = 255;
+ if (alpha > 255)
+ alpha = 255;
+
+ RGB_to_HSV(r, g, b, &a->currentHue, &a->currentSaturation, &a->currentValue);
+ a->currentAlpha = alpha;
+ a->UpdateTextboxes(r, g, b, alpha);
+ }
+ };
+
+ rValue = new ui::Textbox(ui::Point(5, Size.Y-23), ui::Point(30, 17), "255");
+ rValue->SetActionCallback(new ColourChange(this));
+ rValue->SetLimit(3);
+ rValue->SetInputType(ui::Textbox::Number);
+ AddComponent(rValue);
+
+ gValue = new ui::Textbox(ui::Point(40, Size.Y-23), ui::Point(30, 17), "255");
+ gValue->SetActionCallback(new ColourChange(this));
+ gValue->SetLimit(3);
+ gValue->SetInputType(ui::Textbox::Number);
+ AddComponent(gValue);
+
+ bValue = new ui::Textbox(ui::Point(75, Size.Y-23), ui::Point(30, 17), "255");
+ bValue->SetActionCallback(new ColourChange(this));
+ bValue->SetLimit(3);
+ bValue->SetInputType(ui::Textbox::Number);
+ AddComponent(bValue);
+
+ aValue = new ui::Textbox(ui::Point(110, Size.Y-23), ui::Point(30, 17), "255");
+ aValue->SetActionCallback(new ColourChange(this));
+ aValue->SetLimit(3);
+ aValue->SetInputType(ui::Textbox::Number);
+ AddComponent(aValue);
+
+ hexValue = new::ui::Label(ui::Point(150, Size.Y-23), ui::Point(53, 17), "0xFFFFFFFF");
+ AddComponent(hexValue);
+
+ class OkayAction: public ui::ButtonAction
+ {
+ ColourPickerActivity * a;
+ public:
+ OkayAction(ColourPickerActivity * a) : a(a) { }
+ void ActionCallback(ui::Button * sender)
+ {
+ int Red, Green, Blue;
+ Red = format::StringToNumber<int>(a->rValue->GetText());
+ Green = format::StringToNumber<int>(a->gValue->GetText());
+ Blue = format::StringToNumber<int>(a->bValue->GetText());
+ ui::Colour col(Red, Green, Blue, a->currentAlpha);
+ if(a->callback)
+ a->callback->ColourPicked(col);
+ a->Exit();
+ }
+ };
+
+ ui::Button * doneButton = new ui::Button(ui::Point(Size.X-45, Size.Y-23), ui::Point(40, 17), "Done");
+ doneButton->SetActionCallback(new OkayAction(this));
+ AddComponent(doneButton);
+ SetOkayButton(doneButton);
+
+ RGB_to_HSV(initialColour.Red, initialColour.Green, initialColour.Blue, &currentHue, &currentSaturation, &currentValue);
+ currentAlpha = initialColour.Alpha;
+ UpdateTextboxes(initialColour.Red, initialColour.Green, initialColour.Blue, initialColour.Alpha);
+}
+
+void ColourPickerActivity::UpdateTextboxes(int r, int g, int b, int a)
+{
+ rValue->SetText(format::NumberToString<int>(r));
+ gValue->SetText(format::NumberToString<int>(g));
+ bValue->SetText(format::NumberToString<int>(b));
+ aValue->SetText(format::NumberToString<int>(a));
+ std::stringstream hex;
+ hex << std::hex << "0x" << std::setfill('0') << std::setw(2) << std::uppercase << a << std::setw(2) << r << std::setw(2) << g << std::setw(2) << b;
+ hexValue->SetText(hex.str());
+}
+void ColourPickerActivity::OnTryExit(ExitMethod method)
+{
+ Exit();
+}
+
+void ColourPickerActivity::OnMouseMove(int x, int y, int dx, int dy)
+{
+ if(mouseDown)
+ {
+ x -= Position.X+5;
+ y -= Position.Y+5;
+
+ currentHue = (float(x)/float(255))*359.0f;
+ currentSaturation = 255-(y*2);
+
+ if(currentSaturation > 255)
+ currentSaturation = 255;
+ if(currentSaturation < 0)
+ currentSaturation = 0;
+ if(currentHue > 359)
+ currentHue = 359;
+ if(currentHue < 0)
+ currentHue = 0;
+ }
+
+ if(valueMouseDown)
+ {
+ x -= Position.X+5;
+ y -= Position.Y+5;
+
+ currentValue = x;
+
+ if(currentValue > 255)
+ currentValue = 255;
+ if(currentValue < 0)
+ currentValue = 0;
+ }
+
+ if(mouseDown || valueMouseDown)
+ {
+ int cr, cg, cb;
+ HSV_to_RGB(currentHue, currentSaturation, currentValue, &cr, &cg, &cb);
+ UpdateTextboxes(cr, cg, cb, currentAlpha);
+ }
+}
+
+void ColourPickerActivity::OnMouseDown(int x, int y, unsigned button)
+{
+ x -= Position.X+5;
+ y -= Position.Y+5;
+ if(x >= 0 && x <= 256 && y >= 0 && y < 127)
+ {
+ mouseDown = true;
+ currentHue = (float(x)/float(255))*359.0f;
+ currentSaturation = 255-(y*2);
+
+ if(currentSaturation > 255)
+ currentSaturation = 255;
+ if(currentSaturation < 0)
+ currentSaturation = 0;
+ if(currentHue > 359)
+ currentHue = 359;
+ if(currentHue < 0)
+ currentHue = 0;
+ }
+
+ if(x >= 0 && x <= 256 && y >= 131 && y < 142)
+ {
+ valueMouseDown = true;
+ currentValue = x;
+
+ if(currentValue > 255)
+ currentValue = 255;
+ if(currentValue < 0)
+ currentValue = 0;
+ }
+
+ if(mouseDown || valueMouseDown)
+ {
+ int cr, cg, cb;
+ HSV_to_RGB(currentHue, currentSaturation, currentValue, &cr, &cg, &cb);
+ UpdateTextboxes(cr, cg, cb, currentAlpha);
+ }
+}
+
+void ColourPickerActivity::OnMouseUp(int x, int y, unsigned button)
+{
+ if(mouseDown || valueMouseDown)
+ {
+ int cr, cg, cb;
+ HSV_to_RGB(currentHue, currentSaturation, currentValue, &cr, &cg, &cb);
+ UpdateTextboxes(cr, cg, cb, currentAlpha);
+ }
+
+ if(mouseDown)
+ {
+ mouseDown = false;
+ x -= Position.X+5;
+ y -= Position.Y+5;
+
+ currentHue = (float(x)/float(255))*359.0f;
+ currentSaturation = 255-(y*2);
+
+ if(currentSaturation > 255)
+ currentSaturation = 255;
+ if(currentSaturation < 0)
+ currentSaturation = 0;
+ if(currentHue > 359)
+ currentHue = 359;
+ if(currentHue < 0)
+ currentHue = 0;
+ }
+
+ if(valueMouseDown)
+ {
+ valueMouseDown = false;
+
+ x -= Position.X+5;
+ y -= Position.Y+5;
+
+ currentValue = x;
+
+ if(currentValue > 255)
+ currentValue = 255;
+ if(currentValue < 0)
+ currentValue = 0;
+ }
+}
+
+
+void ColourPickerActivity::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ //g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->fillrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3, 0, 0, 0, currentAlpha);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
+
+ g->drawrect(Position.X+4, Position.Y+4, 258, 130, 180, 180, 180, 255);
+
+ g->drawrect(Position.X+4, Position.Y+4+4+128, 258, 12, 180, 180, 180, 255);
+
+
+ int offsetX = Position.X+5;
+ int offsetY = Position.Y+5;
+
+
+ //draw color square
+ int lastx = -1, currx = 0;
+ for(int saturation = 0; saturation <= 255; saturation+=2)
+ {
+ for(int hue = 0; hue <= 359; hue++)
+ {
+ currx = clamp_flt(hue, 0, 359)+offsetX;
+ if (currx == lastx)
+ continue;
+ lastx = currx;
+ int cr = 0;
+ int cg = 0;
+ int cb = 0;
+ HSV_to_RGB(hue, 255-saturation, currentValue, &cr, &cg, &cb);
+ g->blendpixel(currx, (saturation/2)+offsetY, cr, cg, cb, currentAlpha);
+ }
+ }
+
+ //draw brightness bar
+ for(int value = 0; value <= 255; value++)
+ for(int i = 0; i < 10; i++)
+ {
+ int cr = 0;
+ int cg = 0;
+ int cb = 0;
+ HSV_to_RGB(currentHue, currentSaturation, value, &cr, &cg, &cb);
+
+ g->blendpixel(value+offsetX, i+offsetY+127+5, cr, cg, cb, currentAlpha);
+ }
+
+ int currentHueX = clamp_flt(currentHue, 0, 359);
+ int currentSaturationY = ((255-currentSaturation)/2);
+ g->xor_line(offsetX+currentHueX, offsetY+currentSaturationY-5, offsetX+currentHueX, offsetY+currentSaturationY+5);
+ g->xor_line(offsetX+currentHueX-5, offsetY+currentSaturationY, offsetX+currentHueX+5, offsetY+currentSaturationY);
+
+ g->xor_line(offsetX+currentValue, offsetY+4+128, offsetX+currentValue, offsetY+13+128);
+ g->xor_line(offsetX+currentValue+1, offsetY+4+128, offsetX+currentValue+1, offsetY+13+128);
+}
+
+ColourPickerActivity::~ColourPickerActivity() {
+ if(callback)
+ delete callback;
+}
+
diff --git a/src/colourpicker/ColourPickerActivity.h b/src/colourpicker/ColourPickerActivity.h
new file mode 100644
index 0000000..5cc3f04
--- /dev/null
+++ b/src/colourpicker/ColourPickerActivity.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include "Activity.h"
+#include "interface/Window.h"
+#include "interface/Textbox.h"
+
+class ColourPickedCallback
+{
+public:
+ ColourPickedCallback() {}
+ virtual ~ColourPickedCallback() {}
+ virtual void ColourPicked(ui::Colour colour) {}
+};
+
+class ColourPickerActivity: public WindowActivity {
+ int currentHue;
+ int currentSaturation;
+ int currentValue;
+ int currentAlpha;
+
+ bool mouseDown;
+ bool valueMouseDown;
+
+ ui::Textbox * rValue;
+ ui::Textbox * gValue;
+ ui::Textbox * bValue;
+ ui::Textbox * aValue;
+ ui::Label * hexValue;
+
+ ColourPickedCallback * callback;
+
+ void UpdateTextboxes(int r, int g, int b, int a);
+public:
+ virtual void OnMouseMove(int x, int y, int dx, int dy);
+ virtual void OnMouseDown(int x, int y, unsigned button);
+ virtual void OnMouseUp(int x, int y, unsigned button);
+ virtual void OnTryExit(ExitMethod method);
+ ColourPickerActivity(ui::Colour initialColour, ColourPickedCallback * callback = NULL);
+ virtual ~ColourPickerActivity();
+ virtual void OnDraw();
+};
diff --git a/src/console/ConsoleCommand.h b/src/console/ConsoleCommand.h
new file mode 100644
index 0000000..a40a688
--- /dev/null
+++ b/src/console/ConsoleCommand.h
@@ -0,0 +1,30 @@
+/*
+ * ConsoleCommand.h
+ *
+ * Created on: Feb 1, 2012
+ * Author: Simon
+ */
+
+#ifndef CONSOLECOMMAND_H_
+#define CONSOLECOMMAND_H_
+
+class ConsoleCommand
+{
+public:
+ ConsoleCommand(std::string command, int returnStatus, std::string returnValue):
+ Command(command), ReturnStatus(returnStatus), ReturnValue(returnValue)
+ {
+
+ }
+ std::string Command;
+ int ReturnStatus;
+ std::string ReturnValue;
+
+ operator std::string() const
+ {
+ return Command;
+ }
+};
+
+
+#endif /* CONSOLECOMMAND_H_ */
diff --git a/src/console/ConsoleController.cpp b/src/console/ConsoleController.cpp
new file mode 100644
index 0000000..7d61303
--- /dev/null
+++ b/src/console/ConsoleController.cpp
@@ -0,0 +1,81 @@
+/*
+ * ConsoleController.cpp
+ *
+ * Created on: Jan 31, 2012
+ * Author: Simon
+ */
+
+#include <stack>
+#include "ConsoleController.h"
+
+ConsoleController::ConsoleController(ControllerCallback * callback, CommandInterface * commandInterface):
+ HasDone(false)
+{
+ consoleModel = new ConsoleModel();
+ consoleView = new ConsoleView();
+ consoleView->AttachController(this);
+ consoleModel->AddObserver(consoleView);
+
+ this->callback = callback;
+ this->commandInterface = commandInterface;
+}
+
+void ConsoleController::EvaluateCommand(std::string command)
+{
+ if (command.substr(0, 5) == "!load ")
+ CloseConsole();
+ int returnCode = commandInterface->Command(command);
+ if(command.length())
+ consoleModel->AddLastCommand(ConsoleCommand(command, returnCode, commandInterface->GetLastError()));
+ else
+ CloseConsole();
+}
+
+void ConsoleController::CloseConsole()
+{
+ if(ui::Engine::Ref().GetWindow() == consoleView)
+ ui::Engine::Ref().CloseWindow();
+}
+
+std::string ConsoleController::FormatCommand(std::string command)
+{
+ return commandInterface->FormatCommand(command);
+}
+
+void ConsoleController::NextCommand()
+{
+ int cIndex = consoleModel->GetCurrentCommandIndex();
+ if(cIndex < consoleModel->GetPreviousCommands().size())
+ consoleModel->SetCurrentCommandIndex(cIndex+1);
+}
+
+void ConsoleController::PreviousCommand()
+{
+ int cIndex = consoleModel->GetCurrentCommandIndex();
+ if(cIndex > 0)
+ consoleModel->SetCurrentCommandIndex(cIndex-1);
+}
+
+void ConsoleController::Exit()
+{
+ if(ui::Engine::Ref().GetWindow() == consoleView)
+ ui::Engine::Ref().CloseWindow();
+ if(callback)
+ callback->ControllerExit();
+ HasDone = true;
+}
+
+ConsoleView * ConsoleController::GetView()
+{
+ return consoleView;
+}
+
+ConsoleController::~ConsoleController() {
+ if(ui::Engine::Ref().GetWindow() == consoleView)
+ ui::Engine::Ref().CloseWindow();
+ if(callback)
+ delete callback;
+ delete consoleModel;
+ delete consoleView;
+}
+
diff --git a/src/console/ConsoleController.h b/src/console/ConsoleController.h
new file mode 100644
index 0000000..d5fc07a
--- /dev/null
+++ b/src/console/ConsoleController.h
@@ -0,0 +1,38 @@
+/*
+ * ConsoleController.h
+ *
+ * Created on: Jan 31, 2012
+ * Author: Simon
+ */
+
+#ifndef CONSOLECONTROLLER_H_
+#define CONSOLECONTROLLER_H_
+
+#include <string>
+#include "Controller.h"
+#include "ConsoleView.h"
+#include "ConsoleModel.h"
+#include "ConsoleCommand.h"
+#include "cat/CommandInterface.h"
+
+class ConsoleModel;
+class ConsoleView;
+class ConsoleController {
+ ControllerCallback * callback;
+ ConsoleView * consoleView;
+ ConsoleModel * consoleModel;
+ CommandInterface * commandInterface;
+public:
+ bool HasDone;
+ ConsoleController(ControllerCallback * callback, CommandInterface * commandInterface);
+ std::string FormatCommand(std::string command);
+ void EvaluateCommand(std::string command);
+ void NextCommand();
+ void PreviousCommand();
+ void Exit();
+ void CloseConsole();
+ ConsoleView * GetView();
+ virtual ~ConsoleController();
+};
+
+#endif /* CONSOLECONTROLLER_H_ */
diff --git a/src/console/ConsoleModel.cpp b/src/console/ConsoleModel.cpp
new file mode 100644
index 0000000..df900a3
--- /dev/null
+++ b/src/console/ConsoleModel.cpp
@@ -0,0 +1,82 @@
+/*
+ * ConsoleModel.cpp
+ *
+ * Created on: Feb 1, 2012
+ * Author: Simon
+ */
+
+#include "client/Client.h"
+#include "ConsoleModel.h"
+
+ConsoleModel::ConsoleModel() {
+ std::vector<std::string> previousHistory = Client::Ref().GetPrefStringArray("Console.History");
+ for(std::vector<std::string>::reverse_iterator iter = previousHistory.rbegin(), end = previousHistory.rend(); iter != end; ++iter)
+ {
+ if(previousCommands.size()<25)
+ {
+ previousCommands.push_front(ConsoleCommand(*iter, 0, ""));
+ currentCommandIndex = previousCommands.size();
+ }
+ }
+}
+
+void ConsoleModel::AddObserver(ConsoleView * observer)
+{
+ observers.push_back(observer);
+ observer->NotifyPreviousCommandsChanged(this);
+}
+
+int ConsoleModel::GetCurrentCommandIndex()
+{
+ return currentCommandIndex;
+}
+
+void ConsoleModel::SetCurrentCommandIndex(int index)
+{
+ currentCommandIndex = index;
+ notifyCurrentCommandChanged();
+}
+
+ConsoleCommand ConsoleModel::GetCurrentCommand()
+{
+ if(currentCommandIndex < 0 || currentCommandIndex >= previousCommands.size())
+ {
+ return ConsoleCommand("", 0, "");
+ }
+ return previousCommands[currentCommandIndex];
+}
+
+void ConsoleModel::AddLastCommand(ConsoleCommand command)
+{
+ previousCommands.push_back(command);
+ if(previousCommands.size()>25)
+ previousCommands.pop_front();
+ currentCommandIndex = previousCommands.size();
+ notifyPreviousCommandsChanged();
+}
+
+std::deque<ConsoleCommand> ConsoleModel::GetPreviousCommands()
+{
+ return previousCommands;
+}
+
+void ConsoleModel::notifyPreviousCommandsChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyPreviousCommandsChanged(this);
+ }
+}
+
+void ConsoleModel::notifyCurrentCommandChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyCurrentCommandChanged(this);
+ }
+}
+
+ConsoleModel::~ConsoleModel() {
+ Client::Ref().SetPref("Console.History", std::vector<std::string>(previousCommands.begin(), previousCommands.end()));
+}
+
diff --git a/src/console/ConsoleModel.h b/src/console/ConsoleModel.h
new file mode 100644
index 0000000..b340ea8
--- /dev/null
+++ b/src/console/ConsoleModel.h
@@ -0,0 +1,35 @@
+/*
+ * ConsoleModel.h
+ *
+ * Created on: Feb 1, 2012
+ * Author: Simon
+ */
+
+#ifndef CONSOLEMODEL_H_
+#define CONSOLEMODEL_H_
+
+#include <vector>
+#include <deque>
+#include "ConsoleView.h"
+#include "ConsoleCommand.h"
+
+class ConsoleView;
+class ConsoleModel {
+ int currentCommandIndex;
+ std::vector<ConsoleView*> observers;
+ std::deque<ConsoleCommand> previousCommands;
+ void notifyPreviousCommandsChanged();
+ void notifyCurrentCommandChanged();
+public:
+ int GetCurrentCommandIndex();
+ void SetCurrentCommandIndex(int index);
+ ConsoleCommand GetCurrentCommand();
+
+ std::deque<ConsoleCommand> GetPreviousCommands();
+ ConsoleModel();
+ void AddObserver(ConsoleView * observer);
+ void AddLastCommand(ConsoleCommand command);
+ virtual ~ConsoleModel();
+};
+
+#endif /* CONSOLEMODEL_H_ */
diff --git a/src/console/ConsoleView.cpp b/src/console/ConsoleView.cpp
new file mode 100644
index 0000000..67c83de
--- /dev/null
+++ b/src/console/ConsoleView.cpp
@@ -0,0 +1,106 @@
+/*
+ * ConsoleView.cpp
+ *
+ * Created on: Jan 31, 2012
+ * Author: Simon
+ */
+
+#include "ConsoleView.h"
+#include "interface/Keys.h"
+
+ConsoleView::ConsoleView():
+ ui::Window(ui::Point(0, 0), ui::Point(XRES+BARSIZE, 150)),
+ commandField(NULL)
+{
+ class CommandHighlighter: public ui::TextboxAction
+ {
+ ConsoleView * v;
+ public:
+ CommandHighlighter(ConsoleView * v_) { v = v_; }
+ virtual void TextChangedCallback(ui::Textbox * sender)
+ {
+ sender->SetDisplayText(v->c->FormatCommand(sender->GetText()));
+ }
+ };
+ commandField = new ui::Textbox(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "");
+ commandField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ commandField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ commandField->SetActionCallback(new CommandHighlighter(this));
+ AddComponent(commandField);
+ FocusComponent(commandField);
+ commandField->SetBorder(false);
+}
+
+void ConsoleView::DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ switch(key)
+ {
+ case KEY_ESCAPE:
+ case '`':
+ c->CloseConsole();
+ break;
+ case KEY_RETURN:
+ case KEY_ENTER:
+ c->EvaluateCommand(commandField->GetText());
+ commandField->SetText("");
+ commandField->SetDisplayText("");
+ break;
+ case KEY_DOWN:
+ c->NextCommand();
+ break;
+ case KEY_UP:
+ c->PreviousCommand();
+ break;
+ default:
+ Window::DoKeyPress(key, character, shift, ctrl, alt);
+ break;
+ }
+}
+
+void ConsoleView::NotifyPreviousCommandsChanged(ConsoleModel * sender)
+{
+ for(int i = 0; i < commandList.size(); i++)
+ {
+ RemoveComponent(commandList[i]);
+ delete commandList[i];
+ }
+ commandList.clear();
+ std::deque<ConsoleCommand> commands = sender->GetPreviousCommands();
+ int currentY = Size.Y - 32;
+ if(commands.size())
+ for(int i = commands.size()-1; i >= 0; i--)
+ {
+ if(currentY <= 0)
+ break;
+ ui::Label * tempLabel = new ui::Label(ui::Point(Size.X/2, currentY), ui::Point(Size.X/2, 16), commands[i].ReturnValue);
+ tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ commandList.push_back(tempLabel);
+ AddComponent(tempLabel);
+ tempLabel = new ui::Label(ui::Point(0, currentY), ui::Point(Size.X/2, 16), commands[i].Command);
+ tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ commandList.push_back(tempLabel);
+ AddComponent(tempLabel);
+ currentY-=16;
+ }
+}
+
+void ConsoleView::NotifyCurrentCommandChanged(ConsoleModel * sender)
+{
+ commandField->SetText(sender->GetCurrentCommand().Command);
+ commandField->SetDisplayText(c->FormatCommand(commandField->GetText()));
+}
+
+
+void ConsoleView::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ g->fillrect(Position.X, Position.Y, Size.X, Size.Y, 0, 0, 0, 110);
+ g->draw_line(Position.X, Position.Y+Size.Y-16, Position.X+Size.X, Position.Y+Size.Y-16, 255, 255, 255, 160);
+ g->draw_line(Position.X, Position.Y+Size.Y, Position.X+Size.X, Position.Y+Size.Y, 255, 255, 255, 200);
+}
+
+ConsoleView::~ConsoleView() {
+}
+
diff --git a/src/console/ConsoleView.h b/src/console/ConsoleView.h
new file mode 100644
index 0000000..f118293
--- /dev/null
+++ b/src/console/ConsoleView.h
@@ -0,0 +1,37 @@
+/*
+ * ConsoleView.h
+ *
+ * Created on: Jan 31, 2012
+ * Author: Simon
+ */
+
+#ifndef CONSOLEVIEW_H_
+#define CONSOLEVIEW_H_
+
+#include <vector>
+#include <queue>
+#include "interface/Label.h"
+#include "interface/Window.h"
+#include "ConsoleController.h"
+#include "ConsoleModel.h"
+#include "interface/Textbox.h"
+#include "ConsoleCommand.h"
+
+
+class ConsoleController;
+class ConsoleModel;
+class ConsoleView: public ui::Window {
+ ConsoleController * c;
+ ui::Textbox * commandField;
+ std::vector<ui::Label*> commandList;
+public:
+ ConsoleView();
+ virtual void OnDraw();
+ virtual void DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ void AttachController(ConsoleController * c_) { c = c_; }
+ void NotifyPreviousCommandsChanged(ConsoleModel * sender);
+ void NotifyCurrentCommandChanged(ConsoleModel * sender);
+ virtual ~ConsoleView();
+};
+
+#endif /* CONSOLEVIEW_H_ */
diff --git a/src/dialogues/ConfirmPrompt.cpp b/src/dialogues/ConfirmPrompt.cpp
new file mode 100644
index 0000000..1aa75e2
--- /dev/null
+++ b/src/dialogues/ConfirmPrompt.cpp
@@ -0,0 +1,159 @@
+/*
+ * ConfirmPrompt.cpp
+ *
+ * Created on: Apr 6, 2012
+ * Author: Simon
+ */
+
+#include "ConfirmPrompt.h"
+#include "Style.h"
+#include "interface/Label.h"
+#include "interface/Button.h"
+#include "PowderToy.h"
+
+ConfirmPrompt::ConfirmPrompt(std::string title, std::string message, ConfirmDialogueCallback * callback_):
+ ui::Window(ui::Point(-1, -1), ui::Point(250, 35)),
+ callback(callback_)
+{
+ int width, height;
+ ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 15), title);
+ titleLabel->SetTextColour(style::Colour::WarningTitle);
+ titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(titleLabel);
+
+
+ ui::Label * messageLabel = new ui::Label(ui::Point(4, 25), ui::Point(Size.X-8, -1), message);
+ messageLabel->SetMultiline(true);
+ messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+ AddComponent(messageLabel);
+
+ Size.Y += messageLabel->Size.Y+12;
+ Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2;
+
+ class CloseAction: public ui::ButtonAction
+ {
+ public:
+ ConfirmPrompt * prompt;
+ DialogueResult result;
+ CloseAction(ConfirmPrompt * prompt_, DialogueResult result_) { prompt = prompt_; result = result_; }
+ void ActionCallback(ui::Button * sender)
+ {
+ ui::Engine::Ref().CloseWindow();
+ if(prompt->callback)
+ prompt->callback->ConfirmCallback(result);
+ prompt->SelfDestruct();
+ }
+ };
+
+
+ ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X-75, 16), "Cancel");
+ cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ cancelButton->Appearance.BorderInactive = ui::Colour(200, 200, 200);
+ cancelButton->SetActionCallback(new CloseAction(this, ResultCancel));
+ AddComponent(cancelButton);
+ SetCancelButton(cancelButton);
+
+ ui::Button * okayButton = new ui::Button(ui::Point(Size.X-76, Size.Y-16), ui::Point(76, 16), "Continue");
+ okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ okayButton->Appearance.TextInactive = style::Colour::WarningTitle;
+ okayButton->SetActionCallback(new CloseAction(this, ResultOkay));
+ AddComponent(okayButton);
+ SetOkayButton(okayButton);
+
+ ui::Engine::Ref().ShowWindow(this);
+}
+
+ConfirmPrompt::ConfirmPrompt(std::string title, std::string message, std::string buttonText, ConfirmDialogueCallback * callback_):
+ ui::Window(ui::Point(-1, -1), ui::Point(250, 50)),
+ callback(callback_)
+{
+ int width, height;
+ ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 15), title);
+ titleLabel->SetTextColour(style::Colour::WarningTitle);
+ titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(titleLabel);
+
+
+ ui::Label * messageLabel = new ui::Label(ui::Point(4, 25), ui::Point(Size.X-8, -1), message);
+ messageLabel->SetMultiline(true);
+ messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+ AddComponent(messageLabel);
+
+ Size.Y += messageLabel->Size.Y+12;
+ Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2;
+
+ class CloseAction: public ui::ButtonAction
+ {
+ public:
+ ConfirmPrompt * prompt;
+ DialogueResult result;
+ CloseAction(ConfirmPrompt * prompt_, DialogueResult result_) { prompt = prompt_; result = result_; }
+ void ActionCallback(ui::Button * sender)
+ {
+ ui::Engine::Ref().CloseWindow();
+ if(prompt->callback)
+ prompt->callback->ConfirmCallback(result);
+ prompt->SelfDestruct();
+ }
+ };
+
+
+ ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X-75, 16), "Cancel");
+ cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ cancelButton->Appearance.BorderInactive = ui::Colour(200, 200, 200);
+ cancelButton->SetActionCallback(new CloseAction(this, ResultCancel));
+ AddComponent(cancelButton);
+ SetCancelButton(cancelButton);
+
+ ui::Button * okayButton = new ui::Button(ui::Point(Size.X-76, Size.Y-16), ui::Point(76, 16), buttonText);
+ okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ okayButton->Appearance.TextInactive = style::Colour::WarningTitle;
+ okayButton->SetActionCallback(new CloseAction(this, ResultOkay));
+ AddComponent(okayButton);
+ SetOkayButton(okayButton);
+
+ ui::Engine::Ref().ShowWindow(this);
+}
+
+bool ConfirmPrompt::Blocking(std::string title, std::string message, std::string buttonText)
+{
+ class BlockingPromptCallback: public ConfirmDialogueCallback {
+ public:
+ bool & outputResult;
+ BlockingPromptCallback(bool & output): outputResult(output) {}
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ outputResult = true;
+ else
+ outputResult = false;
+ ui::Engine::Ref().Break();
+ }
+ virtual ~BlockingPromptCallback() { }
+ };
+ bool result;
+ new ConfirmPrompt(title, message, buttonText, new BlockingPromptCallback(result));
+ EngineProcess();
+ return result;
+}
+
+void ConfirmPrompt::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255);
+}
+
+ConfirmPrompt::~ConfirmPrompt() {
+ if(callback)
+ delete callback;
+}
+
diff --git a/src/dialogues/ConfirmPrompt.h b/src/dialogues/ConfirmPrompt.h
new file mode 100644
index 0000000..b866338
--- /dev/null
+++ b/src/dialogues/ConfirmPrompt.h
@@ -0,0 +1,32 @@
+/*
+ * ConfirmPrompt.h
+ *
+ * Created on: Apr 6, 2012
+ * Author: Simon
+ */
+
+#ifndef CONFIRMPROMPT_H_
+#define CONFIRMPROMPT_H_
+
+#include "interface/Window.h"
+
+class ConfirmDialogueCallback;
+class ConfirmPrompt: public ui::Window {
+public:
+ enum DialogueResult { ResultCancel, ResultOkay };
+ ConfirmPrompt(std::string title, std::string message, ConfirmDialogueCallback * callback_ = NULL);
+ ConfirmPrompt(std::string title, std::string message, std::string buttonText, ConfirmDialogueCallback * callback_ = NULL);
+ static bool Blocking(std::string title, std::string message, std::string buttonText = "Confirm");
+ virtual void OnDraw();
+ virtual ~ConfirmPrompt();
+ ConfirmDialogueCallback * callback;
+};
+
+class ConfirmDialogueCallback
+{
+ public:
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {}
+ virtual ~ConfirmDialogueCallback() {}
+};
+
+#endif /* CONFIRMPROMPT_H_ */
diff --git a/src/dialogues/ErrorMessage.cpp b/src/dialogues/ErrorMessage.cpp
new file mode 100644
index 0000000..ef86152
--- /dev/null
+++ b/src/dialogues/ErrorMessage.cpp
@@ -0,0 +1,85 @@
+/*
+ * ErrorMessage.cpp
+ *
+ * Created on: Jan 29, 2012
+ * Author: Simon
+ */
+
+#include "Style.h"
+#include "ErrorMessage.h"
+#include "interface/Button.h"
+#include "interface/Label.h"
+#include "PowderToy.h"
+
+ErrorMessage::ErrorMessage(std::string title, std::string message, ErrorMessageCallback * callback_):
+ ui::Window(ui::Point(-1, -1), ui::Point(200, 35)),
+ callback(callback_)
+{
+ ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 16), title);
+ titleLabel->SetTextColour(style::Colour::ErrorTitle);
+ titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(titleLabel);
+
+ ui::Label * messageLabel = new ui::Label(ui::Point(4, 24), ui::Point(Size.X-8, -1), message);
+ messageLabel->SetMultiline(true);
+ messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+ AddComponent(messageLabel);
+
+ Size.Y += messageLabel->Size.Y+12;
+ Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2;
+
+ class DismissAction: public ui::ButtonAction
+ {
+ ErrorMessage * message;
+ public:
+ DismissAction(ErrorMessage * message_) { message = message_; }
+ void ActionCallback(ui::Button * sender)
+ {
+ ui::Engine::Ref().CloseWindow();
+ if(message->callback)
+ message->callback->DismissCallback();
+ message->SelfDestruct();
+ }
+ };
+
+ ui::Button * okayButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "Dismiss");
+ okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ okayButton->Appearance.BorderInactive = ui::Colour(200, 200, 200);
+ okayButton->SetActionCallback(new DismissAction(this));
+ AddComponent(okayButton);
+ SetOkayButton(okayButton);
+ SetCancelButton(okayButton);
+
+ ui::Engine::Ref().ShowWindow(this);
+}
+
+void ErrorMessage::Blocking(std::string title, std::string message)
+{
+ class BlockingDismissCallback: public ErrorMessageCallback {
+ public:
+ BlockingDismissCallback() {}
+ virtual void DismissCallback() {
+ ui::Engine::Ref().Break();
+ }
+ virtual ~BlockingDismissCallback() { }
+ };
+ new ErrorMessage(title, message, new BlockingDismissCallback());
+ EngineProcess();
+}
+
+void ErrorMessage::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255);
+}
+
+ErrorMessage::~ErrorMessage() {
+ if(callback)
+ delete callback;
+}
+
diff --git a/src/dialogues/ErrorMessage.h b/src/dialogues/ErrorMessage.h
new file mode 100644
index 0000000..769127e
--- /dev/null
+++ b/src/dialogues/ErrorMessage.h
@@ -0,0 +1,30 @@
+/*
+ * ErrorMessage.h
+ *
+ * Created on: Jan 29, 2012
+ * Author: Simon
+ */
+
+#ifndef ERRORMESSAGE_H_
+#define ERRORMESSAGE_H_
+
+#include "interface/Window.h"
+
+class ErrorMessageCallback;
+class ErrorMessage: public ui::Window {
+ ErrorMessageCallback * callback;
+public:
+ ErrorMessage(std::string title, std::string message, ErrorMessageCallback * callback_ = NULL);
+ static void Blocking(std::string title, std::string message);
+ virtual void OnDraw();
+ virtual ~ErrorMessage();
+};
+
+class ErrorMessageCallback
+{
+ public:
+ virtual void DismissCallback() {}
+ virtual ~ErrorMessageCallback() {}
+};
+
+#endif /* ERRORMESSAGE_H_ */
diff --git a/src/dialogues/InformationMessage.cpp b/src/dialogues/InformationMessage.cpp
new file mode 100644
index 0000000..b015b06
--- /dev/null
+++ b/src/dialogues/InformationMessage.cpp
@@ -0,0 +1,61 @@
+/*
+ * InformationMessage.cpp
+ *
+ * Created on: Jan 29, 2012
+ * Author: Simon
+ */
+
+#include "Style.h"
+#include "InformationMessage.h"
+#include "interface/Button.h"
+#include "interface/Label.h"
+
+InformationMessage::InformationMessage(std::string title, std::string message):
+ ui::Window(ui::Point(-1, -1), ui::Point(200, 75))
+{
+ ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 16), title);
+ titleLabel->SetTextColour(style::Colour::InformationTitle);
+ titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(titleLabel);
+
+ ui::Label * messageLabel = new ui::Label(ui::Point(4, 24), ui::Point(Size.X-8, 60), message);
+ messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+ AddComponent(messageLabel);
+
+ class DismissAction: public ui::ButtonAction
+ {
+ InformationMessage * message;
+ public:
+ DismissAction(InformationMessage * message_) { message = message_; }
+ void ActionCallback(ui::Button * sender)
+ {
+ ui::Engine::Ref().CloseWindow();
+ message->SelfDestruct(); //TODO: Fix component disposal
+ }
+ };
+
+ ui::Button * okayButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "Dismiss");
+ okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ okayButton->Appearance.BorderInactive = ui::Colour(200, 200, 200);
+ okayButton->SetActionCallback(new DismissAction(this));
+ AddComponent(okayButton);
+ SetOkayButton(okayButton);
+ SetCancelButton(okayButton);
+
+ ui::Engine::Ref().ShowWindow(this);
+}
+
+void InformationMessage::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255);
+}
+
+InformationMessage::~InformationMessage() {
+}
+
diff --git a/src/dialogues/InformationMessage.h b/src/dialogues/InformationMessage.h
new file mode 100644
index 0000000..6f03154
--- /dev/null
+++ b/src/dialogues/InformationMessage.h
@@ -0,0 +1,20 @@
+/*
+ * InformationMessage.h
+ *
+ * Created on: Jan 29, 2012
+ * Author: Simon
+ */
+
+#ifndef INFORMATIONMESSAGE_H_
+#define INFORMATIONMESSAGE_H_
+
+#include "interface/Window.h"
+
+class InformationMessage: public ui::Window {
+public:
+ InformationMessage(std::string title, std::string message);
+ virtual void OnDraw();
+ virtual ~InformationMessage();
+};
+
+#endif /* INFORMATIONMESSAGE_H_ */
diff --git a/src/dialogues/LegacyDialogues.h b/src/dialogues/LegacyDialogues.h
new file mode 100644
index 0000000..7f6097d
--- /dev/null
+++ b/src/dialogues/LegacyDialogues.h
@@ -0,0 +1,11 @@
+#pragma once
+
+//Legacy blocking prompts
+//This are not implemented here, but rather in the engine bootstrapper
+bool ConfirmUI(std::string title, std::string message, std::string confirmText) {}
+
+void ErrorUI(std::string title, std::string message) {}
+
+void InformationUI(std::string title, std::string message) {}
+
+std::string MessagePromptUI(std::string title, std::string message, std::string text, std::string placeholder) {} \ No newline at end of file
diff --git a/src/dialogues/TextPrompt.cpp b/src/dialogues/TextPrompt.cpp
new file mode 100644
index 0000000..dbaefd2
--- /dev/null
+++ b/src/dialogues/TextPrompt.cpp
@@ -0,0 +1,121 @@
+/*
+ * ConfirmPrompt.cpp
+ *
+ * Created on: Apr 6, 2012
+ * Author: Simon
+ */
+
+#include <iostream>
+#include "TextPrompt.h"
+#include "interface/Label.h"
+#include "interface/Button.h"
+#include "Style.h"
+#include "PowderToy.h"
+
+class CloseAction: public ui::ButtonAction
+{
+public:
+ TextPrompt * prompt;
+ TextPrompt::DialogueResult result;
+ CloseAction(TextPrompt * prompt_, TextPrompt::DialogueResult result_) { prompt = prompt_; result = result_; }
+ void ActionCallback(ui::Button * sender)
+ {
+ ui::Engine::Ref().CloseWindow();
+ if(prompt->callback)
+ prompt->callback->TextCallback(result, prompt->textField->GetText());
+ prompt->SelfDestruct(); //TODO: Fix component disposal
+ }
+};
+
+TextPrompt::TextPrompt(std::string title, std::string message, std::string text, std::string placeholder, bool multiline, TextDialogueCallback * callback_):
+ ui::Window(ui::Point(-1, -1), ui::Point(200, 65)),
+ callback(callback_)
+{
+ if(multiline)
+ Size.X += 100;
+
+ ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 18), title);
+ titleLabel->SetTextColour(style::Colour::WarningTitle);
+ titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(titleLabel);
+
+ ui::Label * messageLabel = new ui::Label(ui::Point(4, 25), ui::Point(Size.X-8, -1), message);
+ messageLabel->SetMultiline(true);
+ messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+ AddComponent(messageLabel);
+
+ Size.Y += messageLabel->Size.Y+4;
+
+ textField = new ui::Textbox(ui::Point(4, messageLabel->Position.Y + messageLabel->Size.Y + 7), ui::Point(Size.X-8, 16), text, placeholder);
+ if(multiline)
+ {
+ textField->SetMultiline(true);
+ textField->Size.Y = 60;
+ Size.Y += 45;
+ textField->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+ }
+ else
+ {
+ textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ }
+ textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ AddComponent(textField);
+ FocusComponent(textField);
+
+ ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point((Size.X/2)+1, 16), "Cancel");
+ cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ cancelButton->Appearance.BorderInactive = ui::Colour(200, 200, 200);
+ cancelButton->SetActionCallback(new CloseAction(this, ResultCancel));
+ AddComponent(cancelButton);
+ SetCancelButton(cancelButton);
+
+ ui::Button * okayButton = new ui::Button(ui::Point(Size.X/2, Size.Y-16), ui::Point(Size.X/2, 16), "Okay");
+ okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignRight;
+ okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ okayButton->Appearance.TextInactive = style::Colour::WarningTitle;
+ okayButton->SetActionCallback(new CloseAction(this, ResultOkay));
+ AddComponent(okayButton);
+ SetOkayButton(okayButton);
+
+ ui::Engine::Ref().ShowWindow(this);
+}
+
+std::string TextPrompt::Blocking(std::string title, std::string message, std::string text, std::string placeholder, bool multiline)
+{
+ std::string returnString = "";
+
+ class BlockingTextCallback: public TextDialogueCallback {
+ std::string & outputString;
+ public:
+ BlockingTextCallback(std::string & output) : outputString(output) {}
+ virtual void TextCallback(TextPrompt::DialogueResult result, std::string resultText) {
+ if(result == ResultOkay)
+ outputString = resultText;
+ else
+ outputString = "";
+ ui::Engine::Ref().Break();
+ }
+ virtual ~BlockingTextCallback() { }
+ };
+ new TextPrompt(title, message, text, placeholder, multiline, new BlockingTextCallback(returnString));
+ EngineProcess();
+
+ return returnString;
+}
+
+void TextPrompt::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255);
+}
+
+TextPrompt::~TextPrompt() {
+ if(callback)
+ delete callback;
+}
+
diff --git a/src/dialogues/TextPrompt.h b/src/dialogues/TextPrompt.h
new file mode 100644
index 0000000..1c4a672
--- /dev/null
+++ b/src/dialogues/TextPrompt.h
@@ -0,0 +1,35 @@
+/*
+ * ConfirmPrompt.h
+ *
+ * Created on: Apr 6, 2012
+ * Author: Simon
+ */
+
+#ifndef TEXTPROMPT_H_
+#define TEXTPROMPT_H_
+
+#include "interface/Window.h"
+#include "interface/Textbox.h"
+
+class TextDialogueCallback;
+class TextPrompt: public ui::Window {
+protected:
+ ui::Textbox * textField;
+public:
+ friend class CloseAction;
+ enum DialogueResult { ResultCancel, ResultOkay };
+ TextPrompt(std::string title, std::string message, std::string text, std::string placeholder, bool multiline, TextDialogueCallback * callback_);
+ static std::string Blocking(std::string title, std::string message, std::string text, std::string placeholder, bool multiline);
+ virtual void OnDraw();
+ virtual ~TextPrompt();
+ TextDialogueCallback * callback;
+};
+
+class TextDialogueCallback
+{
+ public:
+ virtual void TextCallback(TextPrompt::DialogueResult result, std::string resultText) {}
+ virtual ~TextDialogueCallback() {}
+};
+
+#endif /* TEXTPROMPT_H_ */
diff --git a/src/elementsearch/ElementSearchActivity.cpp b/src/elementsearch/ElementSearchActivity.cpp
new file mode 100644
index 0000000..8bf9e6e
--- /dev/null
+++ b/src/elementsearch/ElementSearchActivity.cpp
@@ -0,0 +1,192 @@
+/*
+ * ElementSearchActivity.cpp
+ *
+ * Created on: Jun 24, 2012
+ * Author: Simon
+ */
+
+#include <algorithm>
+#include "ElementSearchActivity.h"
+#include "interface/Textbox.h"
+#include "interface/Label.h"
+#include "interface/Keys.h"
+#include "game/Tool.h"
+#include "Style.h"
+#include "game/GameModel.h"
+
+class ElementSearchActivity::ToolAction: public ui::ButtonAction
+{
+ ElementSearchActivity * a;
+public:
+ Tool * tool;
+ ToolAction(ElementSearchActivity * a, Tool * tool) : a(a), tool(tool) { }
+ void ActionCallback(ui::Button * sender_)
+ {
+ ToolButton *sender = (ToolButton*)sender_;
+ if(sender->GetSelectionState() >= 0 && sender->GetSelectionState() <= 2)
+ a->SetActiveTool(sender->GetSelectionState(), tool);
+ }
+};
+
+ElementSearchActivity::ElementSearchActivity(GameModel * gameModel, std::vector<Tool*> tools) :
+ WindowActivity(ui::Point(-1, -1), ui::Point(236, 302)),
+ gameModel(gameModel),
+ tools(tools),
+ firstResult(NULL)
+{
+ ui::Label * title = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 15), "Element Search");
+ title->SetTextColour(style::Colour::InformationTitle);
+ title->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ AddComponent(title);
+
+ class SearchAction : public ui::TextboxAction
+ {
+ private:
+ ElementSearchActivity * a;
+ public:
+ SearchAction(ElementSearchActivity * a) : a(a) {}
+ virtual void TextChangedCallback(ui::Textbox * sender) {
+ a->searchTools(sender->GetText());
+ }
+ };
+
+ searchField = new ui::Textbox(ui::Point(8, 23), ui::Point(Size.X-16, 17), "");
+ searchField->SetActionCallback(new SearchAction(this));
+ searchField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ AddComponent(searchField);
+ FocusComponent(searchField);
+
+ class CloseAction: public ui::ButtonAction
+ {
+ ElementSearchActivity * a;
+ public:
+ CloseAction(ElementSearchActivity * a) : a(a) { }
+ void ActionCallback(ui::Button * sender_)
+ {
+ a->Exit();
+ }
+ };
+
+ class OKAction: public ui::ButtonAction
+ {
+ ElementSearchActivity * a;
+ public:
+ OKAction(ElementSearchActivity * a) : a(a) { }
+ void ActionCallback(ui::Button * sender_)
+ {
+ if(a->GetFirstResult())
+ a->SetActiveTool(0, a->GetFirstResult());
+ }
+ };
+
+ ui::Button * closeButton = new ui::Button(ui::Point(0, Size.Y-15), ui::Point((Size.X/2)+1, 15), "Close");
+ closeButton->SetActionCallback(new CloseAction(this));
+ ui::Button * okButton = new ui::Button(ui::Point(Size.X/2, Size.Y-15), ui::Point(Size.X/2, 15), "OK");
+ okButton->SetActionCallback(new OKAction(this));
+
+ AddComponent(okButton);
+ AddComponent(closeButton);
+
+ searchTools("");
+}
+
+void ElementSearchActivity::searchTools(std::string query)
+{
+ firstResult = NULL;
+ for(std::vector<ToolButton*>::iterator iter = toolButtons.begin(), end = toolButtons.end(); iter != end; ++iter) {
+ delete *iter;
+ RemoveComponent(*iter);
+ }
+ toolButtons.clear();
+
+ ui::Point viewPosition = searchField->Position + ui::Point(2+0, searchField->Size.Y+2+8);
+ ui::Point current = ui::Point(0, 0);
+
+ std::string queryLower = std::string(query);
+ std::transform(queryLower.begin(), queryLower.end(), queryLower.begin(), ::tolower);
+
+ for(std::vector<Tool*>::iterator iter = tools.begin(), end = tools.end(); iter != end; ++iter) {
+ std::string nameLower = std::string((*iter)->GetName());
+ std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), ::tolower);
+
+ if(strstr(nameLower.c_str(), queryLower.c_str())!=0)
+ {
+ Tool * tool = *iter;
+
+ if(!firstResult)
+ firstResult = tool;
+
+ VideoBuffer * tempTexture = tool->GetTexture(26, 14);
+ ToolButton * tempButton;
+
+ if(tempTexture)
+ tempButton = new ToolButton(current+viewPosition, ui::Point(30, 18), "", tool->GetDescription());
+ else
+ tempButton = new ToolButton(current+viewPosition, ui::Point(30, 18), tool->GetName(), tool->GetDescription());
+
+ tempButton->Appearance.SetTexture(tempTexture);
+ tempButton->Appearance.BackgroundInactive = ui::Colour(tool->colRed, tool->colGreen, tool->colBlue);
+ tempButton->SetActionCallback(new ToolAction(this, tool));
+
+ if(gameModel->GetActiveTool(0) == tool)
+ {
+ tempButton->SetSelectionState(0); //Primary
+ }
+ else if(gameModel->GetActiveTool(1) == tool)
+ {
+ tempButton->SetSelectionState(1); //Secondary
+ }
+ else if(gameModel->GetActiveTool(2) == tool)
+ {
+ tempButton->SetSelectionState(2); //Tertiary
+ }
+
+ toolButtons.push_back(tempButton);
+ AddComponent(tempButton);
+
+ current.X += 31;
+
+ if(current.X + 30 > searchField->Size.X) {
+ current.X = 0;
+ current.Y += 19;
+ }
+
+ if(current.Y + viewPosition.Y + 18 > Size.Y-23)
+ break;
+ }
+ }
+}
+
+void ElementSearchActivity::SetActiveTool(int selectionState, Tool * tool)
+{
+ gameModel->SetActiveTool(selectionState, tool);
+ Exit();
+}
+
+void ElementSearchActivity::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
+
+ g->drawrect(Position.X+searchField->Position.X, Position.Y+searchField->Position.Y+searchField->Size.Y+8, searchField->Size.X, Size.Y-(searchField->Position.Y+searchField->Size.Y+8)-23, 255, 255, 255, 180);
+}
+
+void ElementSearchActivity::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(key == KEY_ENTER || key == KEY_RETURN)
+ {
+ if(firstResult)
+ gameModel->SetActiveTool(0, firstResult);
+ Exit();
+ }
+ if(key == KEY_ESCAPE)
+ {
+ Exit();
+ }
+}
+
+ElementSearchActivity::~ElementSearchActivity() {
+ // TODO Auto-generated destructor stub
+}
+
diff --git a/src/elementsearch/ElementSearchActivity.h b/src/elementsearch/ElementSearchActivity.h
new file mode 100644
index 0000000..de9700d
--- /dev/null
+++ b/src/elementsearch/ElementSearchActivity.h
@@ -0,0 +1,39 @@
+/*
+ * ElementSearchActivity.h
+ *
+ * Created on: Jun 24, 2012
+ * Author: Simon
+ */
+
+#ifndef ELEMENTSEARCHACTIVITY_H_
+#define ELEMENTSEARCHACTIVITY_H_
+
+#include <vector>
+#include <string>
+#include "Activity.h"
+#include "interface/Window.h"
+#include "interface/Textbox.h"
+#include "game/ToolButton.h"
+
+class Tool;
+
+class GameModel;
+
+class ElementSearchActivity: public WindowActivity {
+ Tool * firstResult;
+ GameModel * gameModel;
+ std::vector<Tool*> tools;
+ ui::Textbox * searchField;
+ std::vector<ToolButton*> toolButtons;
+ void searchTools(std::string query);
+public:
+ class ToolAction;
+ Tool * GetFirstResult() { return firstResult; }
+ ElementSearchActivity(GameModel * gameModel, std::vector<Tool*> tools);
+ void SetActiveTool(int selectionState, Tool * tool);
+ virtual ~ElementSearchActivity();
+ virtual void OnDraw();
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+};
+
+#endif /* ELEMENTSEARCHACTIVITY_H_ */
diff --git a/src/filebrowser/FileBrowserActivity.cpp b/src/filebrowser/FileBrowserActivity.cpp
new file mode 100644
index 0000000..52656df
--- /dev/null
+++ b/src/filebrowser/FileBrowserActivity.cpp
@@ -0,0 +1,290 @@
+#include <sstream>
+#include <iostream>
+#include "FileBrowserActivity.h"
+#include "interface/Label.h"
+#include "interface/Textbox.h"
+#include "interface/ScrollPanel.h"
+#include "interface/SaveButton.h"
+#include "interface/ProgressBar.h"
+#include "client/Client.h"
+#include "client/SaveFile.h"
+#include "client/GameSave.h"
+#include "Style.h"
+#include "tasks/Task.h"
+#include "simulation/SaveRenderer.h"
+
+class Thumbnail;
+
+
+class SaveSelectedAction: public ui::SaveButtonAction
+{
+ FileBrowserActivity * a;
+public:
+ SaveSelectedAction(FileBrowserActivity * _a) { a = _a; }
+ virtual void ActionCallback(ui::SaveButton * sender)
+ {
+ a->SelectSave(sender->GetSaveFile());
+ }
+};
+
+//Currently, reading is done on another thread, we can't render outside the main thread due to some bullshit with OpenGL
+class LoadFilesTask: public Task
+{
+ std::string directory;
+ std::string search;
+ std::vector<SaveFile*> saveFiles;
+
+ virtual void before()
+ {
+
+ }
+
+ virtual void after()
+ {
+
+ }
+
+ virtual bool doWork()
+ {
+ std::vector<std::string> files = Client::Ref().DirectorySearch(directory, search, ".cps");
+
+
+ notifyProgress(-1);
+ for(std::vector<std::string>::iterator iter = files.begin(), end = files.end(); iter != end; ++iter)
+ {
+ SaveFile * saveFile = new SaveFile(*iter);
+ try
+ {
+ std::vector<unsigned char> data = Client::Ref().ReadFile(*iter);
+ GameSave * tempSave = new GameSave(data);
+ saveFile->SetGameSave(tempSave);
+ saveFiles.push_back(saveFile);
+
+ std::string filename = *iter;
+ size_t folderPos = filename.rfind(PATH_SEP);
+ if(folderPos!=std::string::npos && folderPos+1 < filename.size())
+ {
+ filename = filename.substr(folderPos+1);
+ }
+ size_t extPos = filename.rfind(".");
+ if(extPos!=std::string::npos)
+ {
+ filename = filename.substr(0, extPos);
+ }
+ saveFile->SetDisplayName(filename);
+ }
+ catch(std::exception & e)
+ {
+ //:(
+ }
+ }
+ return true;
+ }
+
+public:
+ std::vector<SaveFile*> GetSaveFiles()
+ {
+ return saveFiles;
+ }
+
+ LoadFilesTask(std::string directory, std::string search):
+ directory(directory),
+ search(search)
+ {
+
+ }
+};
+
+class FileBrowserActivity::SearchAction: public ui::TextboxAction
+{
+public:
+ FileBrowserActivity * a;
+ SearchAction(FileBrowserActivity * a) : a(a) {}
+ virtual void TextChangedCallback(ui::Textbox * sender) {
+ a->DoSearch(sender->GetText());
+ }
+};
+
+FileBrowserActivity::FileBrowserActivity(std::string directory, FileSelectedCallback * callback):
+ WindowActivity(ui::Point(-1, -1), ui::Point(450, 300)),
+ callback(callback),
+ directory(directory),
+ totalFiles(0)
+{
+
+ ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 18), "Save Browser");
+ titleLabel->SetTextColour(style::Colour::WarningTitle);
+ titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(titleLabel);
+
+ ui::Textbox * textField = new ui::Textbox(ui::Point(8, 25), ui::Point(Size.X-16, 16), "", "[search]");
+ textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ textField->SetActionCallback(new SearchAction(this));
+ AddComponent(textField);
+
+ itemList = new ui::ScrollPanel(ui::Point(4, 45), ui::Point(Size.X-8, Size.Y-53));
+ AddComponent(itemList);
+
+ progressBar = new ui::ProgressBar(ui::Point((Size.X-200)/2, 45+(Size.Y-66)/2), ui::Point(200, 17));
+ AddComponent(progressBar);
+
+ infoText = new ui::Label(ui::Point((Size.X-200)/2, 45+(Size.Y-66)/2), ui::Point(200, 17), "No saves found");
+ AddComponent(infoText);
+
+ filesX = 4;
+ filesY = 3;
+ buttonPadding = 2;
+ fileX = 0;
+ fileY = 0;
+
+ buttonXOffset = 0;
+ buttonYOffset = 0;
+ buttonAreaWidth = itemList->Size.X;
+ buttonAreaHeight = itemList->Size.Y;// - buttonYOffset - 18;
+ buttonWidth = (buttonAreaWidth/filesX) - buttonPadding*2;
+ buttonHeight = (buttonAreaHeight/filesY) - buttonPadding*2;
+
+ loadDirectory(directory, "");
+}
+
+void FileBrowserActivity::DoSearch(std::string search)
+{
+ if(!loadFiles)
+ {
+ loadDirectory(directory, search);
+ }
+}
+
+void FileBrowserActivity::SelectSave(SaveFile * file)
+{
+ if(callback)
+ callback->FileSelected(new SaveFile(*file));
+ Exit();
+}
+
+void FileBrowserActivity::loadDirectory(std::string directory, std::string search)
+{
+ for(int i = 0; i < components.size(); i++)
+ {
+ RemoveComponent(components[i]);
+ itemList->RemoveChild(components[i]);
+ delete components[i];
+ }
+ components.clear();
+
+ for(std::vector<ui::Component*>::iterator iter = componentsQueue.begin(), end = componentsQueue.end(); iter != end; ++iter)
+ {
+ delete *iter;
+ }
+ componentsQueue.clear();
+
+ for(std::vector<SaveFile*>::iterator iter = files.begin(), end = files.end(); iter != end; ++iter)
+ {
+ delete *iter;
+ }
+ files.clear();
+
+ infoText->Visible = false;
+ progressBar->Visible = true;
+ progressBar->SetProgress(-1);
+ progressBar->SetStatus("Loading files");
+ loadFiles = new LoadFilesTask(directory, search);
+ loadFiles->AddTaskListener(this);
+ loadFiles->Start();
+}
+
+void FileBrowserActivity::NotifyDone(Task * task)
+{
+ fileX = 0;
+ fileY = 0;
+ files = ((LoadFilesTask*)task)->GetSaveFiles();
+ totalFiles = files.size();
+ delete loadFiles;
+ loadFiles = NULL;
+ if(!files.size())
+ {
+ progressBar->Visible = false;
+ infoText->Visible = true;
+ }
+}
+
+void FileBrowserActivity::OnMouseDown(int x, int y, unsigned button)
+{
+ if(!(x > Position.X && y > Position.Y && y < Position.Y+Size.Y && x < Position.X+Size.X)) //Clicked outside window
+ Exit();
+}
+
+void FileBrowserActivity::NotifyError(Task * task)
+{
+
+}
+
+void FileBrowserActivity::NotifyProgress(Task * task)
+{
+ progressBar->SetProgress(task->GetProgress());
+}
+
+void FileBrowserActivity::NotifyStatus(Task * task)
+{
+
+}
+
+void FileBrowserActivity::OnTick(float dt)
+{
+ if(loadFiles)
+ loadFiles->Poll();
+
+ if(files.size())
+ {
+ SaveFile * saveFile = files.back();
+ files.pop_back();
+
+ if(fileX == filesX)
+ {
+ fileX = 0;
+ fileY++;
+ }
+ ui::SaveButton * saveButton = new ui::SaveButton(
+ ui::Point(
+ buttonXOffset + buttonPadding + fileX*(buttonWidth+buttonPadding*2),
+ buttonYOffset + buttonPadding + fileY*(buttonHeight+buttonPadding*2)
+ ),
+ ui::Point(buttonWidth, buttonHeight),
+ saveFile);
+ saveButton->Tick(dt);
+ saveButton->SetActionCallback(new SaveSelectedAction(this));
+ progressBar->SetStatus("Rendering thumbnails");
+ progressBar->SetProgress((float(totalFiles-files.size())/float(totalFiles))*100.0f);
+ componentsQueue.push_back(saveButton);
+ fileX++;
+ }
+ else if(componentsQueue.size())
+ {
+ for(std::vector<ui::Component*>::iterator iter = componentsQueue.begin(), end = componentsQueue.end(); iter != end; ++iter)
+ {
+ components.push_back(*iter);
+ itemList->AddChild(*iter);
+ }
+ componentsQueue.clear();
+ itemList->InnerSize.Y = (buttonHeight+(buttonPadding*2))*fileY;
+ if(!componentsQueue.size())
+ progressBar->Visible = false;
+ }
+}
+
+void FileBrowserActivity::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+
+ //Window Background+Outline
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+4, Size.Y+4);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
+}
+
+FileBrowserActivity::~FileBrowserActivity()
+{
+ if(callback)
+ delete callback;
+} \ No newline at end of file
diff --git a/src/filebrowser/FileBrowserActivity.h b/src/filebrowser/FileBrowserActivity.h
new file mode 100644
index 0000000..693a16d
--- /dev/null
+++ b/src/filebrowser/FileBrowserActivity.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include "Activity.h"
+#include "interface/Window.h"
+#include "tasks/TaskListener.h"
+
+
+class SaveFile;
+class FileSelectedCallback
+{
+public:
+ FileSelectedCallback() {}
+ virtual ~FileSelectedCallback() {}
+ virtual void FileSelected(SaveFile* file) {}
+};
+
+namespace ui
+{
+ class Label;
+ class ScrollPanel;
+ class ProgressBar;
+}
+
+class LoadFilesTask;
+class FileBrowserActivity: public TaskListener, public WindowActivity
+{
+ LoadFilesTask * loadFiles;
+ FileSelectedCallback * callback;
+ ui::ScrollPanel * itemList;
+ ui::Label * infoText;
+ std::vector<SaveFile*> files;
+ std::vector<ui::Component*> components;
+ std::vector<ui::Component*> componentsQueue;
+ std::string directory;
+
+ ui::ProgressBar * progressBar;
+
+ int totalFiles;
+ int filesX, filesY, buttonPadding;
+ int fileX, fileY;
+ int buttonWidth, buttonHeight, buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset;
+
+
+ class SearchAction;
+ void populateList();
+public:
+ FileBrowserActivity(std::string directory, FileSelectedCallback * callback);
+ virtual void OnDraw();
+ virtual void OnTick(float dt);
+ virtual void OnMouseDown(int x, int y, unsigned button);
+ void loadDirectory(std::string directory, std::string search);
+ void SelectSave(SaveFile * file);
+ void DoSearch(std::string search);
+ virtual ~FileBrowserActivity();
+
+ virtual void NotifyDone(Task * task);
+ virtual void NotifyError(Task * task);
+ virtual void NotifyProgress(Task * task);
+ virtual void NotifyStatus(Task * task);
+}; \ No newline at end of file
diff --git a/src/game/Brush.cpp b/src/game/Brush.cpp
new file mode 100644
index 0000000..38dd809
--- /dev/null
+++ b/src/game/Brush.cpp
@@ -0,0 +1,48 @@
+#include "Brush.h"
+#include "graphics/Renderer.h"
+
+void Brush::RenderRect(Renderer * ren, ui::Point position1, ui::Point position2)
+{
+ int width, height, t;
+ width = position2.X-position1.X;
+ height = position2.Y-position1.Y;
+ if(height<0)
+ {
+ position1.Y += height;
+ height *= -1;
+ }
+ if(width<0)
+ {
+ position1.X += width;
+ width *= -1;
+ }
+
+ ren->xor_line(position1.X, position1.Y, position1.X+width, position1.Y);
+ if(height>0){
+ ren->xor_line(position1.X, position1.Y+height, position1.X+width, position1.Y+height);
+ if(height>1){
+ ren->xor_line(position1.X+width, position1.Y+1, position1.X+width, position1.Y+height-1);
+ if(width>0)
+ ren->xor_line(position1.X, position1.Y+1, position1.X, position1.Y+height-1);
+ }
+ }
+}
+
+void Brush::RenderLine(Renderer * ren, ui::Point position1, ui::Point position2)
+{
+ ren->xor_line(position1.X, position1.Y, position2.X, position2.Y);
+}
+
+void Brush::RenderPoint(Renderer * ren, ui::Point position)
+{
+ if(!outline)
+ updateOutline();
+ if(!outline)
+ return;
+ ren->xor_bitmap(outline, position.X-radius.X, position.Y-radius.Y, size.X, size.Y);
+}
+
+void Brush::RenderFill(Renderer * ren, ui::Point position)
+{
+
+}
diff --git a/src/game/Brush.h b/src/game/Brush.h
new file mode 100644
index 0000000..068ecf5
--- /dev/null
+++ b/src/game/Brush.h
@@ -0,0 +1,114 @@
+/*
+ * Brush.h
+ *
+ * Created on: Jan 22, 2012
+ * Author: Simon
+ */
+#ifndef BRUSH_H_
+#define BRUSH_H_
+
+#include <iostream>
+#include "interface/Point.h"
+
+class Renderer;
+class Brush
+{
+protected:
+ unsigned char * outline;
+ unsigned char * bitmap;
+ ui::Point size;
+ ui::Point radius;
+ void updateOutline()
+ {
+ if(!bitmap)
+ GenerateBitmap();
+ if(!bitmap)
+ return;
+ if(outline)
+ delete[] outline;
+ outline = new unsigned char[size.X*size.Y];
+ for(int x = 0; x < size.X; x++)
+ {
+ for(int y = 0; y < size.Y; y++)
+ {
+ if(bitmap[y*size.X+x] && (!y || !x || x == size.X-1 || y == size.Y-1 || !bitmap[y*size.X+(x+1)] || !bitmap[y*size.X+(x-1)] || !bitmap[(y-1)*size.X+x] || !bitmap[(y+1)*size.X+x]))
+ {
+ outline[y*size.X+x] = 255;
+ }
+ else
+ outline[y*size.X+x] = 0;
+ }
+ }
+ }
+public:
+ Brush(ui::Point size_):
+ bitmap(NULL),
+ outline(NULL),
+ radius(0, 0),
+ size(0, 0)
+ {
+ SetRadius(size_);
+ };
+
+ //Radius of the brush 0x0 - infxinf (Radius of 0x0 would be 1x1, radius of 1x1 would be 3x3)
+ ui::Point GetRadius()
+ {
+ return radius;
+ }
+
+ //Size of the brush bitmap mask, 1x1 - infxinf
+ ui::Point GetSize()
+ {
+ return size;
+ }
+ void SetRadius(ui::Point radius)
+ {
+ this->radius = radius;
+ this->size = radius+radius+ui::Point(1, 1);
+
+ GenerateBitmap();
+ updateOutline();
+ }
+ virtual ~Brush() {
+ if(bitmap)
+ delete[] bitmap;
+ if(outline)
+ delete[] outline;
+ }
+ virtual void RenderRect(Renderer * ren, ui::Point position1, ui::Point position2);
+ virtual void RenderLine(Renderer * ren, ui::Point position1, ui::Point position2);
+ virtual void RenderPoint(Renderer * ren, ui::Point position);
+ virtual void RenderFill(Renderer * ren, ui::Point position);
+ virtual void GenerateBitmap()
+ {
+ if(bitmap)
+ delete[] bitmap;
+ bitmap = new unsigned char[size.X*size.Y];
+ for(int x = 0; x < size.X; x++)
+ {
+ for(int y = 0; y < size.Y; y++)
+ {
+ bitmap[(y*size.X)+x] = 255;
+ }
+ }
+ }
+ //Get a bitmap for drawing particles
+ unsigned char * GetBitmap()
+ {
+ if(!bitmap)
+ GenerateBitmap();
+ return bitmap;
+ }
+
+ unsigned char * GetOutline()
+ {
+ if(!outline)
+ updateOutline();
+ if(!outline)
+ return NULL;
+ return outline;
+ }
+};
+
+
+#endif /* BRUSH_H_ */
diff --git a/src/game/DecorationTool.h b/src/game/DecorationTool.h
new file mode 100644
index 0000000..679c854
--- /dev/null
+++ b/src/game/DecorationTool.h
@@ -0,0 +1,43 @@
+
+#ifndef DECORATIONTOOL_H_
+#define DECORATIONTOOL_H_
+
+#include "Tool.h"
+
+class DecorationTool: public Tool
+{
+public:
+ enum ToolType { BlendAdd = DECO_ADD, BlendRemove = DECO_SUBTRACT, BlendMultiply = DECO_MULTIPLY, BlendDivide = DECO_DIVIDE, BlendSet = DECO_DRAW, BlendSmudge = DECO_SMUDGE, Remove = DECO_CLEAR };
+
+ ToolType decoMode;
+
+ unsigned char Red;
+ unsigned char Green;
+ unsigned char Blue;
+ unsigned char Alpha;
+
+ DecorationTool(ToolType decoMode_, string name, string description, int r, int g, int b, std::string identifier):
+ Tool(0, name, description, r, g, b, identifier),
+ decoMode(decoMode_),
+ Red(0),
+ Green(0),
+ Blue(0),
+ Alpha(0)
+ {
+ }
+ virtual ~DecorationTool() {}
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position){
+ sim->ApplyDecorationPoint(position.X, position.Y, Red, Green, Blue, Alpha, decoMode, brush);
+ }
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) {
+ sim->ApplyDecorationLine(position1.X, position1.Y, position2.X, position2.Y, Red, Green, Blue, Alpha, decoMode, brush);
+ }
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) {
+ sim->ApplyDecorationBox(position1.X, position1.Y, position2.X, position2.Y, Red, Green, Blue, Alpha, decoMode);
+ }
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) {
+
+ }
+};
+
+#endif
diff --git a/src/game/EllipseBrush.h b/src/game/EllipseBrush.h
new file mode 100644
index 0000000..9a75dfb
--- /dev/null
+++ b/src/game/EllipseBrush.h
@@ -0,0 +1,46 @@
+/*
+ * ElipseBrush.h
+ *
+ * Created on: Jan 26, 2012
+ * Author: Simon
+ */
+
+#ifndef ELIPSEBRUSH_H_
+#define ELIPSEBRUSH_H_
+
+#include <cmath>
+#include "Brush.h"
+
+class EllipseBrush: public Brush
+{
+public:
+ EllipseBrush(ui::Point size_):
+ Brush(size_)
+ {
+ SetRadius(size_);
+ };
+ virtual void GenerateBitmap()
+ {
+ if(bitmap)
+ delete[] bitmap;
+ bitmap = new unsigned char[size.X*size.Y];
+ int rx = radius.X;
+ int ry = radius.Y;
+ for(int x = 0; x <= radius.X*2; x++)
+ {
+ for(int y = 0; y <= radius.Y*2; y++)
+ {
+ if((pow(x-radius.X,2.0f)*pow(ry,2.0f)+pow(y-radius.Y,2.0f)*pow(rx,2.0f)<=pow(rx,2.0f)*pow(ry,2.0f)))
+ {
+ bitmap[y*(size.X)+x] = 255;
+ }
+ else
+ {
+ bitmap[y*(size.X)+x] = 0;
+ }
+ }
+ }
+ }
+};
+
+#endif /* ELIPSEBRUSH_H_ */
diff --git a/src/game/GameController.cpp b/src/game/GameController.cpp
new file mode 100644
index 0000000..18b93d6
--- /dev/null
+++ b/src/game/GameController.cpp
@@ -0,0 +1,1326 @@
+
+#include <iostream>
+#include <queue>
+#include "Config.h"
+#include "Format.h"
+#include "GameController.h"
+#include "GameModel.h"
+#include "client/SaveInfo.h"
+#include "client/GameSave.h"
+#include "search/SearchController.h"
+#include "render/RenderController.h"
+#include "login/LoginController.h"
+#include "interface/Point.h"
+#include "dialogues/ErrorMessage.h"
+#include "dialogues/InformationMessage.h"
+#include "dialogues/ConfirmPrompt.h"
+#include "GameModelException.h"
+#include "simulation/Air.h"
+#include "elementsearch/ElementSearchActivity.h"
+#include "colourpicker/ColourPickerActivity.h"
+#include "update/UpdateActivity.h"
+#include "Notification.h"
+#include "filebrowser/FileBrowserActivity.h"
+#include "save/LocalSaveActivity.h"
+#include "save/ServerSaveActivity.h"
+#include "interface/Keys.h"
+#include "simulation/Snapshot.h"
+
+using namespace std;
+
+class GameController::LoginCallback: public ControllerCallback
+{
+ GameController * cc;
+public:
+ LoginCallback(GameController * cc_) { cc = cc_; }
+ virtual void ControllerExit()
+ {
+ cc->gameModel->SetUser(cc->loginWindow->GetUser());
+ }
+};
+
+
+class GameController::SearchCallback: public ControllerCallback
+{
+ GameController * cc;
+public:
+ SearchCallback(GameController * cc_) { cc = cc_; }
+ virtual void ControllerExit()
+ {
+ if(cc->search->GetLoadedSave())
+ {
+ try
+ {
+ cc->gameModel->SetSave(cc->search->GetLoadedSave());
+ }
+ catch(GameModelException & ex)
+ {
+ new ErrorMessage("Cannot open save", ex.what());
+ }
+ }
+ }
+};
+
+class GameController::SaveOpenCallback: public ControllerCallback
+{
+ GameController * cc;
+public:
+ SaveOpenCallback(GameController * cc_) { cc = cc_; }
+ virtual void ControllerExit()
+ {
+ if(cc->activePreview->GetDoOpen() && cc->activePreview->GetSave())
+ {
+ try
+ {
+ cc->LoadSave(cc->activePreview->GetSave());
+ }
+ catch(GameModelException & ex)
+ {
+ new ErrorMessage("Cannot open save", ex.what());
+ }
+ }
+ }
+};
+
+
+class GameController::RenderCallback: public ControllerCallback
+{
+ GameController * cc;
+public:
+ RenderCallback(GameController * cc_) { cc = cc_; }
+ virtual void ControllerExit()
+ {
+ //cc->gameModel->SetUser(cc->loginWindow->GetUser());
+ }
+};
+
+class GameController::OptionsCallback: public ControllerCallback
+{
+ GameController * cc;
+public:
+ OptionsCallback(GameController * cc_) { cc = cc_; }
+ virtual void ControllerExit()
+ {
+ cc->gameModel->UpdateQuickOptions();
+ //cc->gameModel->SetUser(cc->loginWindow->GetUser());
+ }
+};
+
+class GameController::TagsCallback: public ControllerCallback
+{
+ GameController * cc;
+public:
+ TagsCallback(GameController * cc_) { cc = cc_; }
+ virtual void ControllerExit()
+ {
+ cc->gameView->NotifySaveChanged(cc->gameModel);
+ }
+};
+
+class GameController::StampsCallback: public ControllerCallback
+{
+ GameController * cc;
+public:
+ StampsCallback(GameController * cc_) { cc = cc_; }
+ virtual void ControllerExit()
+ {
+ if(cc->localBrowser->GetSave())
+ {
+ cc->gameModel->SetStamp(cc->localBrowser->GetSave()->GetGameSave());
+ cc->LoadStamp();
+ }
+ }
+};
+
+GameController::GameController():
+ search(NULL),
+ renderOptions(NULL),
+ loginWindow(NULL),
+ console(NULL),
+ tagsWindow(NULL),
+ options(NULL),
+ activePreview(NULL),
+ localBrowser(NULL),
+ HasDone(false),
+ firstTick(true)
+{
+ gameView = new GameView();
+ gameModel = new GameModel();
+ gameModel->BuildQuickOptionMenu(this);
+
+ gameView->AttachController(this);
+ gameModel->AddObserver(gameView);
+
+ commandInterface = new LuaScriptInterface(this, gameModel);//new TPTScriptInterface();
+ ((LuaScriptInterface*)commandInterface)->SetWindow(gameView);
+
+ //sim = new Simulation();
+ Client::Ref().AddListener(this);
+}
+
+GameController::~GameController()
+{
+ if(search)
+ {
+ delete search;
+ }
+ if(renderOptions)
+ {
+ delete renderOptions;
+ }
+ if(loginWindow)
+ {
+ delete loginWindow;
+ }
+ if(tagsWindow)
+ {
+ delete tagsWindow;
+ }
+ if(console)
+ {
+ delete console;
+ }
+ if(activePreview)
+ {
+ delete activePreview;
+ }
+ if(localBrowser)
+ {
+ delete localBrowser;
+ }
+ if(ui::Engine::Ref().GetWindow() == gameView)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ delete gameModel;
+ delete gameView;
+}
+
+void GameController::HistoryRestore()
+{
+ std::deque<Snapshot*> history = gameModel->GetHistory();
+ if(history.size())
+ {
+ Snapshot * snap = history.back();
+ gameModel->GetSimulation()->Restore(*snap);
+ if(history.size()>1)
+ {
+ history.pop_back();
+ delete snap;
+ gameModel->SetHistory(history);
+ }
+ }
+}
+
+void GameController::HistorySnapshot()
+{
+ std::deque<Snapshot*> history = gameModel->GetHistory();
+ Snapshot * newSnap = gameModel->GetSimulation()->CreateSnapshot();
+ if(newSnap)
+ {
+ if(history.size() >= 1) //History limit is current 1
+ {
+ Snapshot * snap = history.front();
+ history.pop_front();
+ //snap->Particles.clear();
+ delete snap;
+ }
+ history.push_back(newSnap);
+ gameModel->SetHistory(history);
+ }
+}
+
+GameView * GameController::GetView()
+{
+ return gameView;
+}
+
+void GameController::PlaceSave(ui::Point position)
+{
+ if(gameModel->GetPlaceSave())
+ {
+ gameModel->GetSimulation()->Load(position.X, position.Y, gameModel->GetPlaceSave());
+ gameModel->SetPaused(gameModel->GetPlaceSave()->paused | gameModel->GetPaused());
+ }
+}
+
+void GameController::Install()
+{
+#if defined(MACOSX)
+ new InformationMessage("No Installation necessary", "You don't need to install The Powder Toy on Mac OS X");
+#elif defined(WIN) || defined(LIN)
+ class InstallConfirmation: public ConfirmDialogueCallback {
+ public:
+ GameController * c;
+ InstallConfirmation(GameController * c_) { c = c_; }
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ {
+ if(Client::Ref().DoInstallation())
+ {
+ new InformationMessage("Install Success", "The installation completed without error");
+ }
+ else
+ {
+ new ErrorMessage("Could not install", "The installation did not complete due to an error");
+ }
+ }
+ }
+ virtual ~InstallConfirmation() { }
+ };
+ new ConfirmPrompt("Install The Powder Toy", "You are about to install The Powder Toy onto this computer", new InstallConfirmation(this));
+#else
+ new ErrorMessage("Cannot install", "You cannot install The Powder Toy on this platform");
+#endif
+}
+
+void GameController::AdjustGridSize(int direction)
+{
+ if(direction > 0)
+ gameModel->GetRenderer()->SetGridSize((gameModel->GetRenderer()->GetGridSize()+1)%10);
+ else
+ gameModel->GetRenderer()->SetGridSize((gameModel->GetRenderer()->GetGridSize()+9)%10);
+}
+
+void GameController::InvertAirSim()
+{
+ gameModel->GetSimulation()->air->Invert();
+}
+
+
+void GameController::AdjustBrushSize(int direction, bool logarithmic, bool xAxis, bool yAxis)
+{
+ if(xAxis && yAxis)
+ return;
+
+ ui::Point newSize(0, 0);
+ ui::Point oldSize = gameModel->GetBrush()->GetRadius();
+ if(logarithmic)
+ newSize = gameModel->GetBrush()->GetRadius() + ui::Point(direction * ((gameModel->GetBrush()->GetRadius().X/5)>0?gameModel->GetBrush()->GetRadius().X/5:1), direction * ((gameModel->GetBrush()->GetRadius().Y/5)>0?gameModel->GetBrush()->GetRadius().Y/5:1));
+ else
+ newSize = gameModel->GetBrush()->GetRadius() + ui::Point(direction, direction);
+ if(newSize.X<0)
+ newSize.X = 0;
+ if(newSize.Y<0)
+ newSize.Y = 0;
+ if(newSize.X>128)
+ newSize.X = 128;
+ if(newSize.Y>128)
+ newSize.Y = 128;
+
+ if(xAxis)
+ gameModel->GetBrush()->SetRadius(ui::Point(newSize.X, oldSize.Y));
+ else if(yAxis)
+ gameModel->GetBrush()->SetRadius(ui::Point(oldSize.X, newSize.Y));
+ else
+ gameModel->GetBrush()->SetRadius(newSize);
+
+ BrushChanged(gameModel->GetBrushID(), gameModel->GetBrush()->GetRadius().X, gameModel->GetBrush()->GetRadius().Y);
+}
+
+void GameController::AdjustZoomSize(int direction, bool logarithmic)
+{
+ int newSize;
+ if(logarithmic)
+ newSize = gameModel->GetZoomSize()+(((gameModel->GetZoomSize()/10)>0?(gameModel->GetZoomSize()/10):1)*direction);
+ else
+ newSize = gameModel->GetZoomSize()+direction;
+ if(newSize<5)
+ newSize = 5;
+ if(newSize>64)
+ newSize = 64;
+ gameModel->SetZoomSize(newSize);
+
+ int newZoomFactor = 256/newSize;
+ if(newZoomFactor<3)
+ newZoomFactor = 3;
+ gameModel->SetZoomFactor(newZoomFactor);
+}
+
+ui::Point GameController::PointTranslate(ui::Point point)
+{
+ if(point.X >= XRES)
+ point.X = XRES-1;
+ if(point.Y >= YRES)
+ point.Y = YRES-1;
+ if(point.Y < 0)
+ point.Y = 0;
+ if(point.X < 0)
+ point.X = 0;
+
+ bool zoomEnabled = gameModel->GetZoomEnabled();
+ if(!zoomEnabled)
+ return point;
+ //If we try to draw inside the zoom window, normalise the coordinates
+ int zoomFactor = gameModel->GetZoomFactor();
+ ui::Point zoomWindowPosition = gameModel->GetZoomWindowPosition();
+ ui::Point zoomWindowSize = ui::Point(gameModel->GetZoomSize()*zoomFactor, gameModel->GetZoomSize()*zoomFactor);
+
+ if(point.X >= zoomWindowPosition.X && point.X >= zoomWindowPosition.Y && point.X <= zoomWindowPosition.X+zoomWindowSize.X && point.Y <= zoomWindowPosition.Y+zoomWindowSize.Y)
+ return ((point-zoomWindowPosition)/gameModel->GetZoomFactor())+gameModel->GetZoomPosition();
+ return point;
+}
+
+ui::Point GameController::NormaliseBlockCoord(ui::Point point)
+{
+ return (point/CELL)*CELL;
+}
+
+void GameController::DrawRect(int toolSelection, ui::Point point1, ui::Point point2)
+{
+ Simulation * sim = gameModel->GetSimulation();
+ Tool * activeTool = gameModel->GetActiveTool(toolSelection);
+ gameModel->SetLastTool(activeTool);
+ Brush * cBrush = gameModel->GetBrush();
+ if(!activeTool || !cBrush)
+ return;
+ activeTool->SetStrength(gameModel->GetToolStrength());
+ activeTool->DrawRect(sim, cBrush, point1, point2);
+}
+
+void GameController::DrawLine(int toolSelection, ui::Point point1, ui::Point point2)
+{
+ Simulation * sim = gameModel->GetSimulation();
+ Tool * activeTool = gameModel->GetActiveTool(toolSelection);
+ gameModel->SetLastTool(activeTool);
+ Brush * cBrush = gameModel->GetBrush();
+ if(!activeTool || !cBrush)
+ return;
+ activeTool->SetStrength(gameModel->GetToolStrength());
+ activeTool->DrawLine(sim, cBrush, point1, point2);
+}
+
+void GameController::DrawFill(int toolSelection, ui::Point point)
+{
+ Simulation * sim = gameModel->GetSimulation();
+ Tool * activeTool = gameModel->GetActiveTool(toolSelection);
+ gameModel->SetLastTool(activeTool);
+ Brush * cBrush = gameModel->GetBrush();
+ if(!activeTool || !cBrush)
+ return;
+ activeTool->SetStrength(gameModel->GetToolStrength());
+ activeTool->DrawFill(sim, cBrush, point);
+}
+
+void GameController::DrawPoints(int toolSelection, queue<ui::Point> & pointQueue)
+{
+ Simulation * sim = gameModel->GetSimulation();
+ Tool * activeTool = gameModel->GetActiveTool(toolSelection);
+ gameModel->SetLastTool(activeTool);
+ Brush * cBrush = gameModel->GetBrush();
+ if(!activeTool || !cBrush)
+ {
+ if(!pointQueue.empty())
+ {
+ while(!pointQueue.empty())
+ {
+ //delete pointQueue.front();
+ pointQueue.pop();
+ }
+ }
+ return;
+ }
+
+ activeTool->SetStrength(gameModel->GetToolStrength());
+ if(!pointQueue.empty())
+ {
+ ui::Point sPoint(0, 0);
+ bool first = true;
+ while(!pointQueue.empty())
+ {
+ ui::Point fPoint = pointQueue.front();
+ //delete pointQueue.front();
+ pointQueue.pop();
+ if(!first)
+ {
+ activeTool->DrawLine(sim, cBrush, sPoint, fPoint, true);
+ }
+ else
+ {
+ first = false;
+ activeTool->Draw(sim, cBrush, fPoint);
+ }
+ sPoint = fPoint;
+ }
+ }
+}
+
+void GameController::LoadClipboard()
+{
+ gameModel->SetPlaceSave(gameModel->GetClipboard());
+ if(gameModel->GetPlaceSave() && gameModel->GetPlaceSave()->Collapsed())
+ gameModel->GetPlaceSave()->Expand();
+}
+
+void GameController::LoadStamp()
+{
+ gameModel->SetPlaceSave(gameModel->GetStamp());
+ if(gameModel->GetPlaceSave() && gameModel->GetPlaceSave()->Collapsed())
+ gameModel->GetPlaceSave()->Expand();
+}
+
+void GameController::TranslateSave(ui::Point point)
+{
+ matrix2d transform = m2d_identity;
+ vector2d translate = v2d_new(point.X, point.Y);
+ gameModel->GetPlaceSave()->Transform(transform, translate);
+ gameModel->SetPlaceSave(gameModel->GetPlaceSave());
+}
+
+void GameController::TransformSave(matrix2d transform)
+{
+ vector2d translate = v2d_zero;
+ gameModel->GetPlaceSave()->Transform(transform, translate);
+ gameModel->SetPlaceSave(gameModel->GetPlaceSave());
+}
+
+void GameController::ToolClick(int toolSelection, ui::Point point)
+{
+ Simulation * sim = gameModel->GetSimulation();
+ Tool * activeTool = gameModel->GetActiveTool(toolSelection);
+ Brush * cBrush = gameModel->GetBrush();
+ if(!activeTool || !cBrush)
+ return;
+ activeTool->Click(sim, cBrush, point);
+}
+
+void GameController::StampRegion(ui::Point point1, ui::Point point2)
+{
+ GameSave * newSave;
+ newSave = gameModel->GetSimulation()->Save(point1.X, point1.Y, point2.X, point2.Y);
+ if(newSave)
+ {
+ newSave->paused = gameModel->GetPaused();
+ gameModel->AddStamp(newSave);
+ }
+ else
+ new ErrorMessage("Could not create stamp", "Error generating save file");
+}
+
+void GameController::CopyRegion(ui::Point point1, ui::Point point2)
+{
+ GameSave * newSave;
+ newSave = gameModel->GetSimulation()->Save(point1.X, point1.Y, point2.X, point2.Y);
+ if(newSave)
+ {
+ newSave->paused = gameModel->GetPaused();
+ gameModel->SetClipboard(newSave);
+ }
+}
+
+void GameController::CutRegion(ui::Point point1, ui::Point point2)
+{
+ CopyRegion(point1, point2);
+ gameModel->GetSimulation()->clear_area(point1.X, point1.Y, point2.X-point1.X, point2.Y-point1.Y);
+}
+
+bool GameController::MouseMove(int x, int y, int dx, int dy)
+{
+ return commandInterface->OnMouseMove(x, y, dx, dy);
+}
+
+bool GameController::BrushChanged(int brushType, int rx, int ry)
+{
+ return commandInterface->OnBrushChanged(brushType, rx, ry);
+}
+
+bool GameController::MouseDown(int x, int y, unsigned button)
+{
+ return commandInterface->OnMouseDown(x, y, button);
+}
+
+bool GameController::MouseUp(int x, int y, unsigned button)
+{
+ bool ret = commandInterface->OnMouseUp(x, y, button);
+ ui::Point point = PointTranslate(ui::Point(x, y));
+ x = point.X;
+ y = point.Y;
+ if(ret && y<YRES && x<XRES)
+ {
+ if (gameModel->GetActiveTool(0)->GetIdentifier() != "DEFAULT_UI_SIGN" || button != BUTTON_LEFT) //If it's not a sign tool or you are right/middle clicking
+ {
+ Simulation * sim = gameModel->GetSimulation();
+ for (std::vector<sign>::iterator iter = sim->signs.begin(), end = sim->signs.end(); iter != end; ++iter)
+ {
+ int signx, signy, signw, signh;
+ (*iter).pos(signx, signy, signw, signh);
+ if (x>=signx && x<=signx+signw && y>=signy && y<=signy+signh)
+ {
+ if (sregexp((*iter).text.c_str(), "^{[c|t]:[0-9]*|.*}$")==0)
+ {
+ const char * signText = (*iter).text.c_str();
+ char buff[256];
+ int sldr;
+
+ memset(buff, 0, sizeof(buff));
+
+ for (sldr=3; signText[sldr] != '|'; sldr++)
+ buff[sldr-3] = signText[sldr];
+
+ buff[sldr-3] = '\0';
+
+ int tempSaveID = format::StringToNumber<int>(std::string(buff));
+ if (tempSaveID)
+ {
+ if ((*iter).text.c_str()[1] == 'c')
+ OpenSavePreview(tempSaveID, 0);
+ else if ((*iter).text.c_str()[1] == 't')
+ {
+ char url[256];
+ sprintf(url, "http://powdertoy.co.uk/Discussions/Thread/View.html?Thread=%i", tempSaveID);
+ OpenURI(url);
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+bool GameController::MouseWheel(int x, int y, int d)
+{
+ return commandInterface->OnMouseWheel(x, y, d);
+}
+
+bool GameController::KeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ bool ret = commandInterface->OnKeyPress(key, character, shift, ctrl, alt);
+ if(ret)
+ {
+ Simulation * sim = gameModel->GetSimulation();
+ if (key == KEY_RIGHT)
+ {
+ sim->player.comm = (int)(sim->player.comm)|0x02; //Go right command
+ }
+ if (key == KEY_LEFT)
+ {
+ sim->player.comm = (int)(sim->player.comm)|0x01; //Go left command
+ }
+ if (key == KEY_DOWN && ((int)(sim->player.comm)&0x08)!=0x08)
+ {
+ sim->player.comm = (int)(sim->player.comm)|0x08; //Use element command
+ }
+ if (key == KEY_UP && ((int)(sim->player.comm)&0x04)!=0x04)
+ {
+ sim->player.comm = (int)(sim->player.comm)|0x04; //Jump command
+ }
+
+ if (key == KEY_d)
+ {
+ sim->player2.comm = (int)(sim->player2.comm)|0x02; //Go right command
+ }
+ if (key == KEY_a)
+ {
+ sim->player2.comm = (int)(sim->player2.comm)|0x01; //Go left command
+ }
+ if (key == KEY_s && ((int)(sim->player2.comm)&0x08)!=0x08)
+ {
+ sim->player2.comm = (int)(sim->player2.comm)|0x08; //Use element command
+ }
+ if (key == KEY_w && ((int)(sim->player2.comm)&0x04)!=0x04)
+ {
+ sim->player2.comm = (int)(sim->player2.comm)|0x04; //Jump command
+ }
+
+ if((!sim->elementCount[PT_STKM2] || ctrl) && gameView->GetSelectMode() == SelectNone)
+ {
+ switch(key)
+ {
+ case 'w':
+ SwitchGravity();
+ break;
+ case 'd':
+ gameView->ToggleDebug();
+ break;
+ case 's':
+ gameView->BeginStampSelection();
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+bool GameController::KeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ bool ret = commandInterface->OnKeyRelease(key, character, shift, ctrl, alt);
+ if(ret)
+ {
+ Simulation * sim = gameModel->GetSimulation();
+ if (key == KEY_RIGHT || key == KEY_LEFT)
+ {
+ sim->player.pcomm = sim->player.comm; //Saving last movement
+ sim->player.comm = (int)(sim->player.comm)&12; //Stop command
+ }
+ if (key == KEY_UP)
+ {
+ sim->player.comm = (int)(sim->player.comm)&11;
+ }
+ if (key == KEY_DOWN)
+ {
+ sim->player.comm = (int)(sim->player.comm)&7;
+ }
+
+ if (key == KEY_d || key == KEY_a)
+ {
+ sim->player2.pcomm = sim->player2.comm; //Saving last movement
+ sim->player2.comm = (int)(sim->player2.comm)&12; //Stop command
+ }
+ if (key == KEY_w)
+ {
+ sim->player2.comm = (int)(sim->player2.comm)&11;
+ }
+ if (key == KEY_s)
+ {
+ sim->player2.comm = (int)(sim->player2.comm)&7;
+ }
+ }
+ return ret;
+}
+
+void GameController::Tick()
+{
+ if(firstTick)
+ {
+ ((LuaScriptInterface*)commandInterface)->Init();
+ firstTick = false;
+ }
+ commandInterface->OnTick();
+}
+
+void GameController::Exit()
+{
+ if(ui::Engine::Ref().GetWindow() == gameView)
+ ui::Engine::Ref().CloseWindow();
+ HasDone = true;
+}
+
+void GameController::ResetAir()
+{
+ gameModel->GetSimulation()->air->Clear();
+}
+
+void GameController::ResetSpark()
+{
+ Simulation * sim = gameModel->GetSimulation();
+ for (int i = 0; i < NPART; i++)
+ if (sim->parts[i].type == PT_SPRK)
+ {
+ if (sim->parts[i].ctype >= 0 && sim->parts[i].ctype < PT_NUM && sim->elements[sim->parts[i].ctype].Enabled)
+ {
+ sim->parts[i].type = sim->parts[i].ctype;
+ sim->parts[i].life = 0;
+ }
+ else
+ sim->kill_part(i);
+ }
+}
+
+void GameController::SwitchGravity()
+{
+ gameModel->GetSimulation()->gravityMode = (gameModel->GetSimulation()->gravityMode+1)%3;
+
+ switch (gameModel->GetSimulation()->gravityMode)
+ {
+ case 0:
+ gameModel->SetInfoTip("Gravity: Vertical");
+ break;
+ case 1:
+ gameModel->SetInfoTip("Gravity: Off");
+ break;
+ case 2:
+ gameModel->SetInfoTip("Gravity: Radial");
+ break;
+ }
+}
+
+void GameController::SwitchAir()
+{
+ gameModel->GetSimulation()->air->airMode = (gameModel->GetSimulation()->air->airMode+1)%5;
+
+ switch (gameModel->GetSimulation()->air->airMode)
+ {
+ case 0:
+ gameModel->SetInfoTip("Air: On");
+ break;
+ case 1:
+ gameModel->SetInfoTip("Air: Pressure Off");
+ break;
+ case 2:
+ gameModel->SetInfoTip("Air: Velocity Off");
+ break;
+ case 3:
+ gameModel->SetInfoTip("Air: Off");
+ break;
+ case 4:
+ gameModel->SetInfoTip("Air: No Update");
+ break;
+ }
+}
+
+void GameController::ToggleAHeat()
+{
+ gameModel->SetAHeatEnable(!gameModel->GetAHeatEnable());
+}
+
+
+void GameController::LoadRenderPreset(int presetNum)
+{
+ Renderer * renderer = gameModel->GetRenderer();
+ RenderPreset preset = renderer->renderModePresets[presetNum];
+ gameModel->SetInfoTip(preset.Name);
+ renderer->SetRenderMode(preset.RenderModes);
+ renderer->SetDisplayMode(preset.DisplayModes);
+ renderer->SetColourMode(preset.ColourMode);
+}
+
+void GameController::Update()
+{
+ ui::Point pos = gameView->GetMousePosition();
+ if(pos.X >= 0 && pos.Y >= 0 && pos.X < XRES && pos.Y < YRES)
+ {
+ gameModel->GetRenderer()->mousePosX = pos.X;
+ gameModel->GetRenderer()->mousePosY = pos.Y;
+ gameView->SetSample(gameModel->GetSimulation()->Get(pos.X, pos.Y));
+ }
+
+ gameModel->GetSimulation()->update_particles();
+ if(renderOptions && renderOptions->HasExited)
+ {
+ delete renderOptions;
+ renderOptions = NULL;
+ }
+
+ if(search && search->HasExited)
+ {
+ delete search;
+ search = NULL;
+ }
+
+ if(activePreview && activePreview->HasExited)
+ {
+ delete activePreview;
+ activePreview = NULL;
+ }
+
+ if(loginWindow && loginWindow->HasExited)
+ {
+ delete loginWindow;
+ loginWindow = NULL;
+ }
+
+ if(localBrowser && localBrowser->HasDone)
+ {
+ delete localBrowser;
+ localBrowser = NULL;
+ }
+}
+
+void GameController::SetZoomEnabled(bool zoomEnabled)
+{
+ gameModel->SetZoomEnabled(zoomEnabled);
+}
+
+void GameController::SetToolStrength(float value)
+{
+ gameModel->SetToolStrength(value);
+}
+
+void GameController::SetZoomPosition(ui::Point position)
+{
+ ui::Point zoomPosition = position-(gameModel->GetZoomSize()/2);
+ if(zoomPosition.X < 0)
+ zoomPosition.X = 0;
+ if(zoomPosition.Y < 0)
+ zoomPosition.Y = 0;
+ if(zoomPosition.X >= XRES-gameModel->GetZoomSize())
+ zoomPosition.X = XRES-gameModel->GetZoomSize();
+ if(zoomPosition.Y >= YRES-gameModel->GetZoomSize())
+ zoomPosition.Y = YRES-gameModel->GetZoomSize();
+
+ ui::Point zoomWindowPosition = ui::Point(0, 0);
+ if(position.X < XRES/2)
+ zoomWindowPosition.X = XRES-(gameModel->GetZoomSize()*gameModel->GetZoomFactor());
+
+ gameModel->SetZoomPosition(zoomPosition);
+ gameModel->SetZoomWindowPosition(zoomWindowPosition);
+}
+
+void GameController::SetPaused(bool pauseState)
+{
+ gameModel->SetPaused(pauseState);
+}
+
+void GameController::SetPaused()
+{
+ gameModel->SetPaused(!gameModel->GetPaused());
+}
+
+void GameController::SetDecoration(bool decorationState)
+{
+ gameModel->SetDecoration(decorationState);
+}
+
+void GameController::SetDecoration()
+{
+ gameModel->SetDecoration(!gameModel->GetDecoration());
+}
+
+void GameController::ShowGravityGrid()
+{
+ gameModel->ShowGravityGrid(!gameModel->GetGravityGrid());
+ gameModel->UpdateQuickOptions();
+}
+
+void GameController::SetActiveColourPreset(int preset)
+{
+ gameModel->SetActiveColourPreset(preset);
+}
+
+void GameController::SetColour(ui::Colour colour)
+{
+ gameModel->SetColourSelectorColour(colour);
+ gameModel->SetPresetColour(colour);
+}
+
+void GameController::SetActiveMenu(Menu * menu)
+{
+ gameModel->SetActiveMenu(menu);
+ vector<Menu*> menuList = gameModel->GetMenuList();
+ bool set = false;
+ for(int i = 0; i < menuList.size(); i++)
+ {
+ if(menuList[i]==menu && i == SC_DECO)
+ {
+ gameModel->SetColourSelectorVisibility(true);
+ set = true;
+ }
+ }
+ if(!set)
+ gameModel->SetColourSelectorVisibility(false);
+}
+
+void GameController::SetActiveTool(int toolSelection, Tool * tool)
+{
+ gameModel->SetActiveTool(toolSelection, tool);
+ gameModel->GetRenderer()->gravityZonesEnabled = false;
+ gameModel->SetLastTool(tool);
+ for(int i = 0; i < 3; i++)
+ {
+ if(gameModel->GetActiveTool(i) == gameModel->GetMenuList().at(SC_WALL)->GetToolList().at(WL_GRAV))
+ {
+ gameModel->GetRenderer()->gravityZonesEnabled = true;
+ }
+ }
+}
+
+void GameController::OpenSearch()
+{
+ if(!search)
+ search = new SearchController(new SearchCallback(this));
+ ui::Engine::Ref().ShowWindow(search->GetView());
+}
+
+void GameController::OpenLocalSaveWindow(bool asCurrent)
+{
+ Simulation * sim = gameModel->GetSimulation();
+ GameSave * gameSave = sim->Save();
+ gameSave->paused = gameModel->GetPaused();
+ gameSave->gravityMode = sim->gravityMode;
+ gameSave->airMode = sim->air->airMode;
+ gameSave->legacyEnable = sim->legacy_enable;
+ gameSave->waterEEnabled = sim->water_equal_test;
+ gameSave->gravityEnable = sim->grav->ngrav_enable;
+ if(!gameSave)
+ {
+ new ErrorMessage("Error", "Unable to build save.");
+ }
+ else
+ {
+ std::string filename = "";
+ if (gameModel->GetSaveFile())
+ filename = gameModel->GetSaveFile()->GetDisplayName();
+ SaveFile tempSave(filename);
+ tempSave.SetGameSave(gameSave);
+
+ if (!asCurrent || !gameModel->GetSaveFile())
+ {
+ class LocalSaveCallback: public FileSavedCallback
+ {
+ GameController * c;
+ public:
+ LocalSaveCallback(GameController * _c): c(_c) {}
+ virtual ~LocalSaveCallback() {};
+ virtual void FileSaved(SaveFile* file)
+ {
+ c->gameModel->SetSaveFile(file);
+ }
+ };
+
+ new LocalSaveActivity(tempSave, new LocalSaveCallback(this));
+ }
+ else if (gameModel->GetSaveFile())
+ {
+ Client::Ref().MakeDirectory(LOCAL_SAVE_DIR);
+ Client::Ref().WriteFile(gameSave->Serialise(), gameModel->GetSaveFile()->GetName());
+ }
+ }
+}
+
+void GameController::LoadSaveFile(SaveFile * file)
+{
+ gameModel->SetSaveFile(file);
+}
+
+
+void GameController::LoadSave(SaveInfo * save)
+{
+ gameModel->SetSave(save);
+}
+
+void GameController::OpenSavePreview(int saveID, int saveDate)
+{
+ activePreview = new PreviewController(saveID, new SaveOpenCallback(this));
+ ui::Engine::Ref().ShowWindow(activePreview->GetView());
+}
+
+void GameController::OpenLocalBrowse()
+{
+ class LocalSaveOpenCallback: public FileSelectedCallback
+ {
+ GameController * c;
+ public:
+ LocalSaveOpenCallback(GameController * _c): c(_c) {}
+ virtual ~LocalSaveOpenCallback() {};
+ virtual void FileSelected(SaveFile* file)
+ {
+ c->LoadSaveFile(file);
+ delete file;
+ }
+ };
+ new FileBrowserActivity(LOCAL_SAVE_DIR PATH_SEP, new LocalSaveOpenCallback(this));
+}
+
+void GameController::OpenLogin()
+{
+ loginWindow = new LoginController(new LoginCallback(this));
+ ui::Engine::Ref().ShowWindow(loginWindow->GetView());
+}
+
+void GameController::OpenElementSearch()
+{
+ vector<Tool*> toolList;
+ vector<Menu*> menuList = gameModel->GetMenuList();
+ for(std::vector<Menu*>::iterator iter = menuList.begin(), end = menuList.end(); iter!=end; ++iter) {
+ if(!(*iter))
+ continue;
+ vector<Tool*> menuToolList = (*iter)->GetToolList();
+ if(!menuToolList.size())
+ continue;
+ toolList.insert(toolList.end(), menuToolList.begin(), menuToolList.end());
+ }
+ vector<Tool*> hiddenTools = gameModel->GetUnlistedTools();
+ toolList.insert(toolList.end(), hiddenTools.begin(), hiddenTools.end());
+ new ElementSearchActivity(gameModel, toolList);
+}
+
+void GameController::OpenColourPicker()
+{
+ class ColourPickerCallback: public ColourPickedCallback
+ {
+ GameController * c;
+ public:
+ ColourPickerCallback(GameController * _c): c(_c) {}
+ virtual ~ColourPickerCallback() {};
+ virtual void ColourPicked(ui::Colour colour)
+ {
+ c->SetColour(colour);
+ }
+ };
+ new ColourPickerActivity(gameModel->GetColourSelectorColour(), new ColourPickerCallback(this));
+}
+
+void GameController::OpenTags()
+{
+ if(gameModel->GetUser().ID)
+ {
+ if(gameModel->GetSave() && gameModel->GetSave()->GetID())
+ {
+ tagsWindow = new TagsController(new TagsCallback(this), gameModel->GetSave());
+ ui::Engine::Ref().ShowWindow(tagsWindow->GetView());
+ }
+ else
+ {
+ new ErrorMessage("Error", "No save open");
+ }
+ }
+ else
+ {
+ new ErrorMessage("Error", "You need to login to edit tags.");
+ }
+}
+
+void GameController::OpenStamps()
+{
+ localBrowser = new LocalBrowserController(new StampsCallback(this));
+ ui::Engine::Ref().ShowWindow(localBrowser->GetView());
+}
+
+void GameController::OpenOptions()
+{
+ options = new OptionsController(gameModel, new OptionsCallback(this));
+ ui::Engine::Ref().ShowWindow(options->GetView());
+
+}
+
+void GameController::ShowConsole()
+{
+ if(!console)
+ console = new ConsoleController(NULL, commandInterface);
+ ui::Engine::Ref().ShowWindow(console->GetView());
+}
+
+void GameController::OpenRenderOptions()
+{
+ renderOptions = new RenderController(gameModel->GetRenderer(), new RenderCallback(this));
+ ui::Engine::Ref().ShowWindow(renderOptions->GetView());
+}
+
+void GameController::OpenSaveWindow()
+{
+ class SaveUploadedCallback: public ServerSaveActivity::SaveUploadedCallback
+ {
+ GameController * c;
+ public:
+ SaveUploadedCallback(GameController * _c): c(_c) {}
+ virtual ~SaveUploadedCallback() {};
+ virtual void SaveUploaded(SaveInfo save)
+ {
+ c->LoadSave(&save);
+ }
+ };
+ if(gameModel->GetUser().ID)
+ {
+ Simulation * sim = gameModel->GetSimulation();
+ GameSave * gameSave = sim->Save();
+ gameSave->paused = gameModel->GetPaused();
+ gameSave->gravityMode = sim->gravityMode;
+ gameSave->airMode = sim->air->airMode;
+ gameSave->legacyEnable = sim->legacy_enable;
+ gameSave->waterEEnabled = sim->water_equal_test;
+ gameSave->gravityEnable = sim->grav->ngrav_enable;
+ if(!gameSave)
+ {
+ new ErrorMessage("Error", "Unable to build save.");
+ }
+ else
+ {
+ if(gameModel->GetSave())
+ {
+ SaveInfo tempSave(*gameModel->GetSave());
+ tempSave.SetGameSave(gameSave);
+ new ServerSaveActivity(tempSave, new SaveUploadedCallback(this));
+ }
+ else
+ {
+ SaveInfo tempSave(0, 0, 0, 0, gameModel->GetUser().Username, "");
+ tempSave.SetGameSave(gameSave);
+ new ServerSaveActivity(tempSave, new SaveUploadedCallback(this));
+ }
+ }
+ }
+ else
+ {
+ new ErrorMessage("Error", "You need to login to upload saves.");
+ }
+}
+
+void GameController::SaveAsCurrent()
+{
+
+ class SaveUploadedCallback: public ServerSaveActivity::SaveUploadedCallback
+ {
+ GameController * c;
+ public:
+ SaveUploadedCallback(GameController * _c): c(_c) {}
+ virtual ~SaveUploadedCallback() {};
+ virtual void SaveUploaded(SaveInfo save)
+ {
+ c->LoadSave(&save);
+ }
+ };
+
+ if(gameModel->GetSave() && gameModel->GetUser().ID && gameModel->GetUser().Username == gameModel->GetSave()->GetUserName())
+ {
+ Simulation * sim = gameModel->GetSimulation();
+ GameSave * gameSave = sim->Save();
+ gameSave->paused = gameModel->GetPaused();
+ gameSave->gravityMode = sim->gravityMode;
+ gameSave->airMode = sim->air->airMode;
+ gameSave->legacyEnable = sim->legacy_enable;
+ gameSave->waterEEnabled = sim->water_equal_test;
+ gameSave->gravityEnable = sim->grav->ngrav_enable;
+ if(!gameSave)
+ {
+ new ErrorMessage("Error", "Unable to build save.");
+ }
+ else
+ {
+ if(gameModel->GetSave())
+ {
+ SaveInfo tempSave(*gameModel->GetSave());
+ tempSave.SetGameSave(gameSave);
+ new ServerSaveActivity(tempSave, true, new SaveUploadedCallback(this));
+ }
+ else
+ {
+ SaveInfo tempSave(0, 0, 0, 0, gameModel->GetUser().Username, "");
+ tempSave.SetGameSave(gameSave);
+ new ServerSaveActivity(tempSave, true, new SaveUploadedCallback(this));
+ }
+ }
+ }
+ else if(gameModel->GetUser().ID)
+ {
+ OpenSaveWindow();
+ }
+ else
+ {
+ new ErrorMessage("Error", "You need to login to upload saves.");
+ }
+}
+
+void GameController::FrameStep()
+{
+ gameModel->FrameStep(1);
+ gameModel->SetPaused(true);
+}
+
+void GameController::Vote(int direction)
+{
+ if(gameModel->GetSave() && gameModel->GetUser().ID && gameModel->GetSave()->GetID() && gameModel->GetSave()->GetVote()==0)
+ {
+ try
+ {
+ gameModel->SetVote(direction);
+ }
+ catch(GameModelException & ex)
+ {
+ new ErrorMessage("Error while voting", ex.what());
+ }
+ }
+}
+
+void GameController::ChangeBrush()
+{
+ gameModel->SetBrush(gameModel->GetBrushID()+1);
+ BrushChanged(gameModel->GetBrushID(), gameModel->GetBrush()->GetRadius().X, gameModel->GetBrush()->GetRadius().Y);
+}
+
+void GameController::ClearSim()
+{
+ gameModel->SetSave(NULL);
+ gameModel->ClearSimulation();
+}
+
+void GameController::ReloadSim()
+{
+ if(gameModel->GetSave() && gameModel->GetSave()->GetGameSave())
+ {
+ gameModel->SetSave(gameModel->GetSave());
+ }
+ else if(gameModel->GetSaveFile() && gameModel->GetSaveFile()->GetGameSave())
+ {
+ gameModel->SetSaveFile(gameModel->GetSaveFile());
+ }
+}
+
+std::string GameController::ElementResolve(int type)
+{
+ if(gameModel && gameModel->GetSimulation() && gameModel->GetSimulation()->elements && type >= 0 && type < PT_NUM)
+ return std::string(gameModel->GetSimulation()->elements[type].Name);
+ else
+ return "";
+}
+
+std::string GameController::WallName(int type)
+{
+ if(gameModel && gameModel->GetSimulation() && gameModel->GetSimulation()->wtypes && type >= 0)
+ return std::string(gameModel->GetSimulation()->wtypes[type].name);
+ else
+ return "";
+}
+
+void GameController::NotifyUpdateAvailable(Client * sender)
+{
+ class UpdateConfirmation: public ConfirmDialogueCallback {
+ public:
+ GameController * c;
+ UpdateConfirmation(GameController * c_) { c = c_; }
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ {
+ c->RunUpdater();
+ }
+ }
+ virtual ~UpdateConfirmation() { }
+ };
+
+ class UpdateNotification : public Notification
+ {
+ GameController * c;
+ public:
+ UpdateNotification(GameController * c, std::string message) : c(c), Notification(message) {}
+ virtual ~UpdateNotification() {}
+
+ virtual void Action()
+ {
+ std::string currentVersion, newVersion;
+#ifdef BETA
+ currentVersion = MTOS(SAVE_VERSION) "." MTOS(MINOR_VERSION) " Beta, Build " MTOS(BUILD_NUM);
+#elif defined(SNAPSHOT)
+ currentVersion = "Snapshot " MTOS(SNAPSHOT_ID);
+#else
+ currentVersion = MTOS(SAVE_VERSION) "." MTOS(MINOR_VERSION) " Stable, Build " MTOS(BUILD_NUM);
+#endif
+
+ UpdateInfo info = Client::Ref().GetUpdateInfo();
+ if(info.Type == UpdateInfo::Beta)
+ newVersion = format::NumberToString<int>(info.Major) + " " + format::NumberToString<int>(info.Minor) + " Beta, Build " + format::NumberToString<int>(info.Build);
+ else if(info.Type == UpdateInfo::Snapshot)
+ newVersion = "Snapshot " + format::NumberToString<int>(info.Time);
+ else if(info.Type == UpdateInfo::Stable)
+ newVersion = format::NumberToString<int>(info.Major) + " " + format::NumberToString<int>(info.Minor) + " Stable, Build " + format::NumberToString<int>(info.Build);
+
+ new ConfirmPrompt("Run Updater", "Are you sure you want to run the updater, please save any changes before updating.\n\nCurrent version:\n " + currentVersion + "\nNew version:\n " + newVersion, new UpdateConfirmation(c));
+ }
+ };
+
+ switch(sender->GetUpdateInfo().Type)
+ {
+ case UpdateInfo::Snapshot:
+ gameModel->AddNotification(new UpdateNotification(this, std::string("A new snapshot is available - click here to update")));
+ break;
+ case UpdateInfo::Stable:
+ gameModel->AddNotification(new UpdateNotification(this, std::string("A new version is available - click here to update")));
+ break;
+ case UpdateInfo::Beta:
+ gameModel->AddNotification(new UpdateNotification(this, std::string("A new beta is available - click here to update")));
+ break;
+ }
+}
+
+void GameController::RemoveNotification(Notification * notification)
+{
+ gameModel->RemoveNotification(notification);
+}
+
+void GameController::RunUpdater()
+{
+ Exit();
+ new UpdateActivity();
+}
+
diff --git a/src/game/GameController.h b/src/game/GameController.h
new file mode 100644
index 0000000..4c7a8c9
--- /dev/null
+++ b/src/game/GameController.h
@@ -0,0 +1,147 @@
+ #ifndef GAMECONTROLLER_H
+#define GAMECONTROLLER_H
+
+#include <queue>
+#include "GameView.h"
+#include "GameModel.h"
+#include "interface/Point.h"
+#include "simulation/Simulation.h"
+#include "search/SearchController.h"
+#include "render/RenderController.h"
+#include "preview/PreviewController.h"
+#include "login/LoginController.h"
+#include "tags/TagsController.h"
+#include "console/ConsoleController.h"
+#include "localbrowser/LocalBrowserController.h"
+//#include "cat/TPTScriptInterface.h"
+#include "cat/LuaScriptInterface.h"
+#include "options/OptionsController.h"
+#include "client/ClientListener.h"
+#include "RenderPreset.h"
+#include "Menu.h"
+
+using namespace std;
+
+class Notification;
+class GameModel;
+class GameView;
+class CommandInterface;
+class ConsoleController;
+class GameController: public ClientListener
+{
+private:
+ //Simulation * sim;
+ bool firstTick;
+ int screenshotIndex;
+ PreviewController * activePreview;
+ GameView * gameView;
+ GameModel * gameModel;
+ SearchController * search;
+ RenderController * renderOptions;
+ LoginController * loginWindow;
+ ConsoleController * console;
+ TagsController * tagsWindow;
+ LocalBrowserController * localBrowser;
+ OptionsController * options;
+ CommandInterface * commandInterface;
+public:
+ bool HasDone;
+ class LoginCallback;
+ class SearchCallback;
+ class RenderCallback;
+ class SSaveCallback;
+ class TagsCallback;
+ class StampsCallback;
+ class OptionsCallback;
+ class SaveOpenCallback;
+ friend class SaveOpenCallback;
+ GameController();
+ ~GameController();
+ GameView * GetView();
+
+ bool BrushChanged(int brushType, int rx, int ry);
+ bool MouseMove(int x, int y, int dx, int dy);
+ bool MouseDown(int x, int y, unsigned button);
+ bool MouseUp(int x, int y, unsigned button);
+ bool MouseWheel(int x, int y, int d);
+ bool KeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ bool KeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ void Tick();
+ void Exit();
+
+ void Install();
+
+ void HistoryRestore();
+ void HistorySnapshot();
+
+ void AdjustGridSize(int direction);
+ void InvertAirSim();
+ void LoadRenderPreset(int presetNum);
+ void SetZoomEnabled(bool zoomEnable);
+ void SetZoomPosition(ui::Point position);
+ void AdjustBrushSize(int direction, bool logarithmic = false, bool xAxis = false, bool yAxis = false);
+ void AdjustZoomSize(int direction, bool logarithmic = false);
+ void ToolClick(int toolSelection, ui::Point point);
+ void DrawPoints(int toolSelection, queue<ui::Point> & pointQueue);
+ void DrawRect(int toolSelection, ui::Point point1, ui::Point point2);
+ void DrawLine(int toolSelection, ui::Point point1, ui::Point point2);
+ void DrawFill(int toolSelection, ui::Point point);
+ void StampRegion(ui::Point point1, ui::Point point2);
+ void CopyRegion(ui::Point point1, ui::Point point2);
+ void CutRegion(ui::Point point1, ui::Point point2);
+ void Update();
+ void SetPaused(bool pauseState);
+ void SetPaused();
+ void SetDecoration(bool decorationState);
+ void SetDecoration();
+ void ShowGravityGrid();
+ void SetActiveMenu(Menu * menu);
+ void SetActiveTool(int toolSelection, Tool * tool);
+ void SetActiveColourPreset(int preset);
+ void SetColour(ui::Colour colour);
+ void SetToolStrength(float value);
+ void LoadSaveFile(SaveFile * file);
+ void LoadSave(SaveInfo * save);
+ void OpenSearch();
+ void OpenLogin();
+ void OpenTags();
+ void OpenSavePreview(int saveID, int saveDate);
+ void OpenLocalSaveWindow(bool asCurrent);
+ void OpenLocalBrowse();
+ void OpenOptions();
+ void OpenRenderOptions();
+ void OpenSaveWindow();
+ void SaveAsCurrent();
+ void OpenStamps();
+ void OpenElementSearch();
+ void OpenColourPicker();
+ void PlaceSave(ui::Point position);
+ void ClearSim();
+ void ReloadSim();
+ void Vote(int direction);
+ void ChangeBrush();
+ void ShowConsole();
+ void FrameStep();
+ void TranslateSave(ui::Point point);
+ void TransformSave(matrix2d transform);
+ ui::Point PointTranslate(ui::Point point);
+ ui::Point NormaliseBlockCoord(ui::Point point);
+ std::string ElementResolve(int type);
+ std::string WallName(int type);
+
+ void ResetAir();
+ void ResetSpark();
+ void SwitchGravity();
+ void SwitchAir();
+ void ToggleAHeat();
+
+ void LoadClipboard();
+ void LoadStamp();
+
+ void RemoveNotification(Notification * notification);
+
+ virtual void NotifyUpdateAvailable(Client * sender);
+ void RunUpdater();
+};
+
+#endif // GAMECONTROLLER_H
diff --git a/src/game/GameModel.cpp b/src/game/GameModel.cpp
new file mode 100644
index 0000000..5c4ee97
--- /dev/null
+++ b/src/game/GameModel.cpp
@@ -0,0 +1,1133 @@
+#include "interface/Engine.h"
+#include "GameModel.h"
+#include "GameView.h"
+#include "simulation/Simulation.h"
+#include "simulation/Air.h"
+#include "simulation/Tools.h"
+#include "graphics/Renderer.h"
+#include "interface/Point.h"
+#include "Brush.h"
+#include "EllipseBrush.h"
+#include "TriangleBrush.h"
+#include "client/Client.h"
+#include "client/GameSave.h"
+#include "game/DecorationTool.h"
+#include "GameModelException.h"
+#include "QuickOptions.h"
+#include "Format.h"
+
+GameModel::GameModel():
+ sim(NULL),
+ ren(NULL),
+ currentBrush(0),
+ currentUser(0, ""),
+ currentSave(NULL),
+ currentFile(NULL),
+ colourSelector(false),
+ clipboard(NULL),
+ stamp(NULL),
+ placeSave(NULL),
+ colour(255, 0, 0, 255),
+ toolStrength(1.0f),
+ activeColourPreset(-1),
+ activeMenu(NULL),
+ edgeMode(0)
+{
+ sim = new Simulation();
+ ren = new Renderer(ui::Engine::Ref().g, sim);
+
+ activeTools = regularToolset;
+
+ std::fill(decoToolset, decoToolset+3, (Tool*)NULL);
+ std::fill(regularToolset, regularToolset+3, (Tool*)NULL);
+
+ //Default render prefs
+ std::vector<unsigned int> tempArray;
+ tempArray.push_back(RENDER_FIRE);
+ tempArray.push_back(RENDER_EFFE);
+ tempArray.push_back(RENDER_BASC);
+ ren->SetRenderMode(tempArray);
+ tempArray.clear();
+
+ ren->SetDisplayMode(tempArray);
+
+ ren->SetColourMode(0);
+
+ //Load config into renderer
+ try
+ {
+ ren->SetColourMode(Client::Ref().GetPrefUInteger("Renderer.ColourMode", 0));
+
+ vector<unsigned int> tempArray = Client::Ref().GetPrefUIntegerArray("Renderer.DisplayModes");
+ if(tempArray.size())
+ {
+ std::vector<unsigned int> displayModes(tempArray.begin(), tempArray.end());
+ ren->SetDisplayMode(displayModes);
+ }
+
+ tempArray = Client::Ref().GetPrefUIntegerArray("Renderer.RenderModes");
+ if(tempArray.size())
+ {
+ std::vector<unsigned int> renderModes(tempArray.begin(), tempArray.end());
+ ren->SetRenderMode(renderModes);
+ }
+
+ ren->gravityFieldEnabled = Client::Ref().GetPrefBool("Renderer.GravityField", false);
+ ren->decorations_enable = Client::Ref().GetPrefBool("Renderer.Decorations", true);
+ }
+ catch(json::Exception & e)
+ {
+ }
+
+ //Load config into simulation
+ edgeMode = Client::Ref().GetPrefInteger("Simulation.EdgeMode", 0);
+ sim->SetEdgeMode(edgeMode);
+
+ //Load last user
+ if(Client::Ref().GetAuthUser().ID)
+ {
+ currentUser = Client::Ref().GetAuthUser();
+ }
+
+ //Set stamp to first stamp in list
+ vector<string> stamps = Client::Ref().GetStamps(0, 1);
+ if(stamps.size()>0)
+ {
+ SaveFile * stampFile = Client::Ref().GetStamp(stamps[0]);
+ if(stampFile && stampFile->GetGameSave())
+ stamp = stampFile->GetGameSave();
+ }
+
+ BuildMenus();
+
+ //Set default decoration colour
+ unsigned char colourR = min(Client::Ref().GetPrefInteger("Decoration.Red", 200), 255);
+ unsigned char colourG = min(Client::Ref().GetPrefInteger("Decoration.Green", 100), 255);
+ unsigned char colourB = min(Client::Ref().GetPrefInteger("Decoration.Blue", 50), 255);
+ unsigned char colourA = min(Client::Ref().GetPrefInteger("Decoration.Alpha", 255), 255);
+
+ SetColourSelectorColour(ui::Colour(colourR, colourG, colourB, colourA));
+
+ colourPresets.push_back(ui::Colour(255, 255, 255));
+ colourPresets.push_back(ui::Colour(0, 255, 255));
+ colourPresets.push_back(ui::Colour(255, 0, 255));
+ colourPresets.push_back(ui::Colour(255, 255, 0));
+ colourPresets.push_back(ui::Colour(255, 0, 0));
+ colourPresets.push_back(ui::Colour(0, 255, 0));
+ colourPresets.push_back(ui::Colour(0, 0, 255));
+}
+
+GameModel::~GameModel()
+{
+ //Save to config:
+ Client::Ref().SetPref("Renderer.ColourMode", ren->GetColourMode());
+
+ std::vector<unsigned int> displayModes = ren->GetDisplayMode();
+ Client::Ref().SetPref("Renderer.DisplayModes", std::vector<unsigned int>(displayModes.begin(), displayModes.end()));
+
+ std::vector<unsigned int> renderModes = ren->GetRenderMode();
+ Client::Ref().SetPref("Renderer.RenderModes", std::vector<unsigned int>(renderModes.begin(), renderModes.end()));
+
+ Client::Ref().SetPref("Renderer.GravityField", (bool)ren->gravityFieldEnabled);
+ Client::Ref().SetPref("Renderer.Decorations", (bool)ren->decorations_enable);
+
+ Client::Ref().SetPref("Simulation.EdgeMode", sim->edgeMode);
+
+ Client::Ref().SetPref("Decoration.Red", (int)colour.Red);
+ Client::Ref().SetPref("Decoration.Green", (int)colour.Green);
+ Client::Ref().SetPref("Decoration.Blue", (int)colour.Blue);
+ Client::Ref().SetPref("Decoration.Alpha", (int)colour.Alpha);
+
+ for(int i = 0; i < menuList.size(); i++)
+ {
+ delete menuList[i];
+ }
+ for(std::vector<Tool*>::iterator iter = extraElementTools.begin(), end = extraElementTools.end(); iter != end; ++iter)
+ {
+ delete *iter;
+ }
+ for(int i = 0; i < brushList.size(); i++)
+ {
+ delete brushList[i];
+ }
+ delete sim;
+ delete ren;
+ if(placeSave)
+ delete placeSave;
+ if(clipboard)
+ delete clipboard;
+ if(stamp)
+ delete stamp;
+ if(currentSave)
+ delete currentSave;
+ if(currentFile)
+ delete currentFile;
+ //if(activeTools)
+ // delete[] activeTools;
+}
+
+void GameModel::UpdateQuickOptions()
+{
+ for(std::vector<QuickOption*>::iterator iter = quickOptions.begin(), end = quickOptions.end(); iter != end; ++iter)
+ {
+ QuickOption * option = *iter;
+ option->Update();
+ }
+}
+
+void GameModel::BuildQuickOptionMenu(GameController * controller)
+{
+ for(std::vector<QuickOption*>::iterator iter = quickOptions.begin(), end = quickOptions.end(); iter != end; ++iter)
+ {
+ delete *iter;
+ }
+ quickOptions.clear();
+
+ quickOptions.push_back(new SandEffectOption(this));
+ quickOptions.push_back(new DrawGravOption(this));
+ quickOptions.push_back(new DecorationsOption(this));
+ quickOptions.push_back(new NGravityOption(this));
+ quickOptions.push_back(new AHeatOption(this));
+ quickOptions.push_back(new ConsoleShowOption(this, controller));
+
+ notifyQuickOptionsChanged();
+ UpdateQuickOptions();
+}
+
+void GameModel::BuildMenus()
+{
+ char lastMenu = 0;
+ if(activeMenu)
+ lastMenu = activeMenu->GetIcon();
+
+ std::string activeToolIdentifiers[3];
+ if(regularToolset[0])
+ activeToolIdentifiers[0] = regularToolset[0]->GetIdentifier();
+ if(regularToolset[1])
+ activeToolIdentifiers[1] = regularToolset[1]->GetIdentifier();
+ if(regularToolset[2])
+ activeToolIdentifiers[2] = regularToolset[2]->GetIdentifier();
+
+ //Empty current menus
+ for(std::vector<Menu*>::iterator iter = menuList.begin(), end = menuList.end(); iter != end; ++iter)
+ {
+ delete *iter;
+ }
+ menuList.clear();
+ toolList.clear();
+
+ for(std::vector<Tool*>::iterator iter = extraElementTools.begin(), end = extraElementTools.end(); iter != end; ++iter)
+ {
+ delete *iter;
+ }
+ extraElementTools.clear();
+ elementTools.clear();
+
+ //Create menus
+ for(int i = 0; i < SC_TOTAL; i++)
+ {
+ menuList.push_back(new Menu((const char)sim->msections[i].icon[0], sim->msections[i].name));
+ }
+
+ //Build menus from Simulation elements
+ for(int i = 0; i < PT_NUM; i++)
+ {
+ if(sim->elements[i].Enabled)
+ {
+ Tool * tempTool;
+ if(i == PT_LIGH)
+ {
+ tempTool = new Element_LIGH_Tool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator);
+ }
+ else if(i == PT_TESC)
+ {
+ tempTool = new Element_TESC_Tool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator);
+ }
+ else if(i == PT_STKM || i == PT_FIGH || i == PT_STKM2)
+ {
+ tempTool = new PlopTool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator);
+ }
+ else
+ {
+ tempTool = new ElementTool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator);
+ }
+
+ if(sim->elements[i].MenuSection < 12 && sim->elements[i].MenuVisible)
+ {
+ menuList[sim->elements[i].MenuSection]->AddTool(tempTool);
+ }
+ else
+ {
+ extraElementTools.push_back(tempTool);
+ }
+ elementTools.push_back(tempTool);
+ }
+ }
+
+ //Build menu for GOL types
+ for(int i = 0; i < NGOL; i++)
+ {
+ Tool * tempTool = new GolTool(i, sim->gmenu[i].name, std::string(sim->gmenu[i].description), PIXR(sim->gmenu[i].colour), PIXG(sim->gmenu[i].colour), PIXB(sim->gmenu[i].colour), "DEFAULT_PT_LIFE_"+std::string(sim->gmenu[i].name));
+ menuList[SC_LIFE]->AddTool(tempTool);
+ }
+
+ //Build other menus from wall data
+ for(int i = 0; i < UI_WALLCOUNT; i++)
+ {
+ Tool * tempTool = new WallTool(i, "", std::string(sim->wtypes[i].descs), PIXR(sim->wtypes[i].colour), PIXG(sim->wtypes[i].colour), PIXB(sim->wtypes[i].colour), "DEFAULT_WL_"+format::NumberToString<int>(i), sim->wtypes[i].textureGen);
+ menuList[SC_WALL]->AddTool(tempTool);
+ //sim->wtypes[i]
+ }
+
+ //Add special sign and prop tools
+ menuList[SC_TOOL]->AddTool(new SampleTool(this));
+ menuList[SC_TOOL]->AddTool(new SignTool());
+ menuList[SC_TOOL]->AddTool(new PropertyTool());
+ menuList[SC_TOOL]->AddTool(new WindTool(0, "WIND", "Create air movement", 64, 64, 64, "DEFAULT_UI_WIND"));
+
+ //Build menu for simtools
+ for(int i = 0; i < sim->tools.size(); i++)
+ {
+ Tool * tempTool;
+ tempTool = new Tool(i, sim->tools[i]->Name, sim->tools[i]->Description, PIXR(sim->tools[i]->Colour), PIXG(sim->tools[i]->Colour), PIXB(sim->tools[i]->Colour), sim->tools[i]->Identifier);
+ menuList[SC_TOOL]->AddTool(tempTool);
+ }
+
+ //Add decoration tools to menu
+ menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendAdd, "ADD", "Colour blending: Add", 0, 0, 0, "DEFAULT_DECOR_ADD"));
+ menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendRemove, "SUB", "Colour blending: Subtract", 0, 0, 0, "DEFAULT_DECOR_SUB"));
+ menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendMultiply, "MUL", "Colour blending: Multiply", 0, 0, 0, "DEFAULT_DECOR_MUL"));
+ menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendDivide, "DIV", "Colour blending: Divide" , 0, 0, 0, "DEFAULT_DECOR_DIV"));
+ menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendSmudge, "SMDG", "Smudge colour", 0, 0, 0, "DEFAULT_DECOR_SMDG"));
+ menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendSet, "SET", "Set colour (No blending)", 0, 0, 0, "DEFAULT_DECOR_SET"));
+ menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::Remove, "CLR", "Clear any set decoration", 0, 0, 0, "DEFAULT_DECOR_CLR"));
+ decoToolset[0] = GetToolFromIdentifier("DEFAULT_DECOR_SET");
+ decoToolset[1] = GetToolFromIdentifier("DEFAULT_DECOR_CLR");
+ decoToolset[2] = GetToolFromIdentifier("DEFAULT_UI_SAMPLE");
+
+ //Set default brush palette
+ brushList.push_back(new EllipseBrush(ui::Point(4, 4)));
+ brushList.push_back(new Brush(ui::Point(4, 4)));
+ brushList.push_back(new TriangleBrush(ui::Point(4, 4)));
+
+ //Set default tools
+ regularToolset[0] = GetToolFromIdentifier("DEFAULT_PT_DUST");
+ regularToolset[1] = GetToolFromIdentifier("DEFAULT_PT_NONE");
+ regularToolset[2] = GetToolFromIdentifier("DEFAULT_UI_SAMPLE");
+
+
+ if(activeToolIdentifiers[0].length())
+ regularToolset[0] = GetToolFromIdentifier(activeToolIdentifiers[0]);
+ if(activeToolIdentifiers[1].length())
+ regularToolset[1] = GetToolFromIdentifier(activeToolIdentifiers[1]);
+ if(activeToolIdentifiers[2].length())
+ regularToolset[2] = GetToolFromIdentifier(activeToolIdentifiers[2]);
+
+ lastTool = activeTools[0];
+
+ //Set default menu
+ activeMenu = menuList[SC_POWDERS];
+
+ if(lastMenu)
+ {
+ for(std::vector<Menu*>::iterator iter = menuList.begin(), end = menuList.end(); iter != end; ++iter)
+ {
+ if((*iter)->GetIcon() == lastMenu)
+ activeMenu = *iter;
+ }
+ }
+
+ if(activeMenu)
+ toolList = activeMenu->GetToolList();
+ else
+ toolList = std::vector<Tool*>();
+
+ notifyMenuListChanged();
+ notifyToolListChanged();
+ notifyActiveToolsChanged();
+ notifyLastToolChanged();
+}
+
+Tool * GameModel::GetToolFromIdentifier(std::string identifier)
+{
+ for(std::vector<Menu*>::iterator iter = menuList.begin(), end = menuList.end(); iter != end; ++iter)
+ {
+ std::vector<Tool*> menuTools = (*iter)->GetToolList();
+ for(std::vector<Tool*>::iterator titer = menuTools.begin(), tend = menuTools.end(); titer != tend; ++titer)
+ {
+ if(identifier == (*titer)->GetIdentifier())
+ return *titer;
+ }
+ }
+ return NULL;
+}
+
+void GameModel::SetEdgeMode(int edgeMode)
+{
+ this->edgeMode = edgeMode;
+ sim->SetEdgeMode(edgeMode);
+}
+
+int GameModel::GetEdgeMode()
+{
+ return this->edgeMode;
+}
+
+std::deque<Snapshot*> GameModel::GetHistory()
+{
+ return history;
+}
+void GameModel::SetHistory(std::deque<Snapshot*> newHistory)
+{
+ history = newHistory;
+}
+
+void GameModel::SetVote(int direction)
+{
+ if(currentSave)
+ {
+ RequestStatus status = Client::Ref().ExecVote(currentSave->GetID(), direction);
+ if(status == RequestOkay)
+ {
+ currentSave->vote = direction;
+ notifySaveChanged();
+ }
+ else
+ {
+ throw GameModelException("Could not vote: "+Client::Ref().GetLastError());
+ }
+ }
+}
+
+Brush * GameModel::GetBrush()
+{
+ return brushList[currentBrush];
+}
+
+int GameModel::GetBrushID()
+{
+ return currentBrush;
+}
+
+void GameModel::SetBrush(int i)
+{
+ currentBrush = i%brushList.size();
+ notifyBrushChanged();
+}
+
+void GameModel::AddObserver(GameView * observer){
+ observers.push_back(observer);
+
+ observer->NotifySimulationChanged(this);
+ observer->NotifyRendererChanged(this);
+ observer->NotifyPausedChanged(this);
+ observer->NotifySaveChanged(this);
+ observer->NotifyBrushChanged(this);
+ observer->NotifyMenuListChanged(this);
+ observer->NotifyToolListChanged(this);
+ observer->NotifyUserChanged(this);
+ observer->NotifyZoomChanged(this);
+ observer->NotifyColourSelectorVisibilityChanged(this);
+ observer->NotifyColourSelectorColourChanged(this);
+ observer->NotifyColourPresetsChanged(this);
+ observer->NotifyColourActivePresetChanged(this);
+ observer->NotifyQuickOptionsChanged(this);
+ observer->NotifyLastToolChanged(this);
+ UpdateQuickOptions();
+}
+
+void GameModel::SetToolStrength(float value)
+{
+ toolStrength = value;
+}
+
+float GameModel::GetToolStrength()
+{
+ return toolStrength;
+}
+
+void GameModel::SetActiveMenu(Menu * menu)
+{
+ for(int i = 0; i < menuList.size(); i++)
+ {
+ if(menuList[i]==menu)
+ {
+ activeMenu = menu;
+ toolList = menu->GetToolList();
+ notifyToolListChanged();
+
+ if(menu == menuList[SC_DECO])
+ {
+ if(activeTools != decoToolset)
+ {
+ activeTools = decoToolset;
+ notifyActiveToolsChanged();
+ }
+ }
+ else
+ {
+ if(activeTools != regularToolset)
+ {
+ activeTools = regularToolset;
+ notifyActiveToolsChanged();
+ }
+ }
+ }
+ }
+}
+
+vector<Tool*> GameModel::GetUnlistedTools()
+{
+ return extraElementTools;
+}
+
+vector<Tool*> GameModel::GetToolList()
+{
+ return toolList;
+}
+
+Menu * GameModel::GetActiveMenu()
+{
+ return activeMenu;
+}
+
+Tool * GameModel::GetElementTool(int elementID)
+{
+ std::cout << elementID << std::endl;
+ for(std::vector<Tool*>::iterator iter = elementTools.begin(), end = elementTools.end(); iter != end; ++iter)
+ {
+ if((*iter)->GetToolID() == elementID)
+ return *iter;
+ }
+ return NULL;
+}
+
+Tool * GameModel::GetActiveTool(int selection)
+{
+ return activeTools[selection];
+}
+
+void GameModel::SetActiveTool(int selection, Tool * tool)
+{
+ activeTools[selection] = tool;
+ notifyActiveToolsChanged();
+}
+
+vector<QuickOption*> GameModel::GetQuickOptions()
+{
+ return quickOptions;
+}
+
+vector<Menu*> GameModel::GetMenuList()
+{
+ return menuList;
+}
+
+SaveInfo * GameModel::GetSave()
+{
+ return currentSave;
+}
+
+void GameModel::SetSave(SaveInfo * newSave)
+{
+ if(currentSave != newSave)
+ {
+ if(currentSave)
+ delete currentSave;
+ if(newSave == NULL)
+ currentSave = NULL;
+ else
+ currentSave = new SaveInfo(*newSave);
+ }
+ if(currentFile)
+ delete currentFile;
+ currentFile = NULL;
+
+ if(currentSave && currentSave->GetGameSave())
+ {
+ GameSave * saveData = currentSave->GetGameSave();
+ SetPaused(saveData->paused | GetPaused());
+ sim->gravityMode = saveData->gravityMode;
+ sim->air->airMode = saveData->airMode;
+ sim->legacy_enable = saveData->legacyEnable;
+ sim->water_equal_test = saveData->waterEEnabled;
+ if(saveData->gravityEnable)
+ sim->grav->start_grav_async();
+ else
+ sim->grav->stop_grav_async();
+ sim->SetEdgeMode(0);
+ sim->clear_sim();
+ ren->ClearAccumulation();
+ sim->Load(saveData);
+ }
+ notifySaveChanged();
+ UpdateQuickOptions();
+}
+
+SaveFile * GameModel::GetSaveFile()
+{
+ return currentFile;
+}
+
+void GameModel::SetSaveFile(SaveFile * newSave)
+{
+ if(currentFile != newSave)
+ {
+ if(currentFile)
+ delete currentFile;
+ if(newSave == NULL)
+ currentFile = NULL;
+ else
+ currentFile = new SaveFile(*newSave);
+ }
+ if (currentSave)
+ delete currentSave;
+ currentSave = NULL;
+
+ if(newSave && newSave->GetGameSave())
+ {
+ GameSave * saveData = newSave->GetGameSave();
+ SetPaused(saveData->paused | GetPaused());
+ sim->gravityMode = saveData->gravityMode;
+ sim->air->airMode = saveData->airMode;
+ sim->legacy_enable = saveData->legacyEnable;
+ sim->water_equal_test = saveData->waterEEnabled;
+ if(saveData->gravityEnable && !sim->grav->ngrav_enable)
+ {
+ sim->grav->start_grav_async();
+ }
+ else if(!saveData->gravityEnable && sim->grav->ngrav_enable)
+ {
+ sim->grav->stop_grav_async();
+ }
+ sim->SetEdgeMode(0);
+ sim->clear_sim();
+ ren->ClearAccumulation();
+ sim->Load(saveData);
+ }
+
+ notifySaveChanged();
+ UpdateQuickOptions();
+}
+
+Simulation * GameModel::GetSimulation()
+{
+ return sim;
+}
+
+Renderer * GameModel::GetRenderer()
+{
+ return ren;
+}
+
+User GameModel::GetUser()
+{
+ return currentUser;
+}
+
+Tool * GameModel::GetLastTool()
+{
+ return lastTool;
+}
+
+void GameModel::SetLastTool(Tool * newTool)
+{
+ if(lastTool != newTool)
+ {
+ lastTool = newTool;
+ notifyLastToolChanged();
+ }
+}
+
+void GameModel::SetZoomEnabled(bool enabled)
+{
+ ren->zoomEnabled = enabled;
+ notifyZoomChanged();
+}
+
+bool GameModel::GetZoomEnabled()
+{
+ return ren->zoomEnabled;
+}
+
+void GameModel::SetZoomPosition(ui::Point position)
+{
+ ren->zoomScopePosition = position;
+ notifyZoomChanged();
+}
+
+ui::Point GameModel::GetZoomPosition()
+{
+ return ren->zoomScopePosition;
+}
+
+void GameModel::SetZoomWindowPosition(ui::Point position)
+{
+ ren->zoomWindowPosition = position;
+ notifyZoomChanged();
+}
+
+ui::Point GameModel::GetZoomWindowPosition()
+{
+ return ren->zoomWindowPosition;
+}
+
+void GameModel::SetZoomSize(int size)
+{
+ ren->zoomScopeSize = size;
+ notifyZoomChanged();
+}
+
+int GameModel::GetZoomSize()
+{
+ return ren->zoomScopeSize;
+}
+
+void GameModel::SetZoomFactor(int factor)
+{
+ ren->ZFACTOR = factor;
+ notifyZoomChanged();
+}
+
+int GameModel::GetZoomFactor()
+{
+ return ren->ZFACTOR;
+}
+
+void GameModel::SetActiveColourPreset(int preset)
+{
+ activeColourPreset = preset;
+ notifyColourActivePresetChanged();
+}
+
+int GameModel::GetActiveColourPreset()
+{
+ return activeColourPreset;
+}
+
+void GameModel::SetPresetColour(ui::Colour colour)
+{
+ if(activeColourPreset >= 0 && activeColourPreset < colourPresets.size())
+ {
+ colourPresets[activeColourPreset] = colour;
+ notifyColourPresetsChanged();
+ }
+}
+
+std::vector<ui::Colour> GameModel::GetColourPresets()
+{
+ return colourPresets;
+}
+
+void GameModel::SetColourSelectorVisibility(bool visibility)
+{
+ if(colourSelector != visibility)
+ {
+ colourSelector = visibility;
+ notifyColourSelectorVisibilityChanged();
+ }
+}
+
+bool GameModel::GetColourSelectorVisibility()
+{
+ return colourSelector;
+}
+
+void GameModel::SetColourSelectorColour(ui::Colour colour_)
+{
+ colour = colour_;
+ notifyColourSelectorColourChanged();
+
+ vector<Tool*> tools = GetMenuList()[SC_DECO]->GetToolList();
+ for(int i = 0; i < tools.size(); i++)
+ {
+ ((DecorationTool*)tools[i])->Red = colour.Red;
+ ((DecorationTool*)tools[i])->Green = colour.Green;
+ ((DecorationTool*)tools[i])->Blue = colour.Blue;
+ ((DecorationTool*)tools[i])->Alpha = colour.Alpha;
+ }
+}
+
+ui::Colour GameModel::GetColourSelectorColour()
+{
+ return colour;
+}
+
+void GameModel::SetUser(User user)
+{
+ currentUser = user;
+ Client::Ref().SetAuthUser(user);
+ notifyUserChanged();
+}
+
+void GameModel::SetPaused(bool pauseState)
+{
+ sim->sys_pause = pauseState?1:0;
+ notifyPausedChanged();
+}
+
+bool GameModel::GetPaused()
+{
+ return sim->sys_pause?true:false;
+}
+
+void GameModel::SetDecoration(bool decorationState)
+{
+ ren->decorations_enable = decorationState?1:0;
+ notifyDecorationChanged();
+ UpdateQuickOptions();
+ if (decorationState)
+ SetInfoTip("Decorations Layer: On");
+ else
+ SetInfoTip("Decorations Layer: Off");
+}
+
+bool GameModel::GetDecoration()
+{
+ return ren->decorations_enable?true:false;
+}
+
+void GameModel::SetAHeatEnable(bool aHeat)
+{
+ sim->aheat_enable = aHeat;
+ UpdateQuickOptions();
+ if (aHeat)
+ SetInfoTip("Ambient Heat: On");
+ else
+ SetInfoTip("Ambient Heat: Off");
+}
+
+bool GameModel::GetAHeatEnable()
+{
+ return sim->aheat_enable;
+}
+
+void GameModel::ShowGravityGrid(bool showGrid)
+{
+ ren->gravityFieldEnabled = showGrid;
+ if (showGrid)
+ SetInfoTip("Gravity Grid: On");
+ else
+ SetInfoTip("Gravity Grid: Off");
+}
+
+bool GameModel::GetGravityGrid()
+{
+ return ren->gravityFieldEnabled;
+}
+
+void GameModel::FrameStep(int frames)
+{
+ sim->framerender += frames;
+}
+
+void GameModel::ClearSimulation()
+{
+
+ //Load defaults
+ sim->gravityMode = 0;
+ sim->air->airMode = 0;
+ sim->legacy_enable = false;
+ sim->water_equal_test = false;
+ sim->grav->stop_grav_async();
+ sim->SetEdgeMode(edgeMode);
+
+ sim->clear_sim();
+ ren->ClearAccumulation();
+
+ notifySaveChanged();
+ UpdateQuickOptions();
+}
+
+void GameModel::SetStamp(GameSave * save)
+{
+ if(stamp != save)
+ {
+ if(stamp)
+ delete stamp;
+ if(save)
+ stamp = new GameSave(*save);
+ else
+ stamp = NULL;
+ }
+}
+
+void GameModel::SetPlaceSave(GameSave * save)
+{
+ if(save != placeSave)
+ {
+ if(placeSave)
+ delete placeSave;
+ if(save)
+ placeSave = new GameSave(*save);
+ else
+ placeSave = NULL;
+ }
+ notifyPlaceSaveChanged();
+}
+
+void GameModel::AddStamp(GameSave * save)
+{
+ if(stamp)
+ delete stamp;
+ stamp = save;
+ Client::Ref().AddStamp(save);
+}
+
+void GameModel::SetClipboard(GameSave * save)
+{
+ if(clipboard)
+ delete clipboard;
+ clipboard = save;
+}
+
+GameSave * GameModel::GetClipboard()
+{
+ return clipboard;
+}
+
+GameSave * GameModel::GetPlaceSave()
+{
+ return placeSave;
+}
+
+GameSave * GameModel::GetStamp()
+{
+ return stamp;
+}
+
+void GameModel::Log(string message)
+{
+ consoleLog.push_front(message);
+ if(consoleLog.size()>100)
+ consoleLog.pop_back();
+ notifyLogChanged(message);
+}
+
+deque<string> GameModel::GetLog()
+{
+ return consoleLog;
+}
+
+std::vector<Notification*> GameModel::GetNotifications()
+{
+ return notifications;
+}
+
+void GameModel::AddNotification(Notification * notification)
+{
+ notifications.push_back(notification);
+ notifyNotificationsChanged();
+}
+
+void GameModel::RemoveNotification(Notification * notification)
+{
+ for(std::vector<Notification*>::iterator iter = notifications.begin(); iter != notifications.end(); ++iter)
+ {
+ if(*iter == notification)
+ {
+ delete *iter;
+ notifications.erase(iter);
+ break;
+ }
+ }
+ notifyNotificationsChanged();
+}
+
+void GameModel::SetToolTip(std::string text)
+{
+ toolTip = text;
+ notifyToolTipChanged();
+}
+
+void GameModel::SetInfoTip(std::string text)
+{
+ infoTip = text;
+ notifyInfoTipChanged();
+}
+
+std::string GameModel::GetToolTip()
+{
+ return toolTip;
+}
+
+std::string GameModel::GetInfoTip()
+{
+ return infoTip;
+}
+
+void GameModel::notifyNotificationsChanged()
+{
+ for(std::vector<GameView*>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
+ {
+ (*iter)->NotifyNotificationsChanged(this);
+ }
+}
+
+void GameModel::notifyColourPresetsChanged()
+{
+ for(std::vector<GameView*>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
+ {
+ (*iter)->NotifyColourPresetsChanged(this);
+ }
+}
+
+void GameModel::notifyColourActivePresetChanged()
+{
+ for(std::vector<GameView*>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
+ {
+ (*iter)->NotifyColourActivePresetChanged(this);
+ }
+}
+
+void GameModel::notifyColourSelectorColourChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyColourSelectorColourChanged(this);
+ }
+}
+
+void GameModel::notifyColourSelectorVisibilityChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyColourSelectorVisibilityChanged(this);
+ }
+}
+
+void GameModel::notifyRendererChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyRendererChanged(this);
+ }
+}
+
+void GameModel::notifySaveChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifySaveChanged(this);
+ }
+}
+
+void GameModel::notifySimulationChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifySimulationChanged(this);
+ }
+}
+
+void GameModel::notifyPausedChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyPausedChanged(this);
+ }
+}
+
+void GameModel::notifyDecorationChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ //observers[i]->NotifyPausedChanged(this);
+ }
+}
+
+void GameModel::notifyBrushChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyBrushChanged(this);
+ }
+}
+
+void GameModel::notifyMenuListChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyMenuListChanged(this);
+ }
+}
+
+void GameModel::notifyToolListChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyToolListChanged(this);
+ }
+}
+
+void GameModel::notifyActiveToolsChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyActiveToolsChanged(this);
+ }
+}
+
+void GameModel::notifyUserChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyUserChanged(this);
+ }
+}
+
+void GameModel::notifyZoomChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyZoomChanged(this);
+ }
+}
+
+void GameModel::notifyPlaceSaveChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyPlaceSaveChanged(this);
+ }
+}
+
+void GameModel::notifyLogChanged(string entry)
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyLogChanged(this, entry);
+ }
+}
+
+void GameModel::notifyInfoTipChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyInfoTipChanged(this);
+ }
+}
+
+void GameModel::notifyToolTipChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyToolTipChanged(this);
+ }
+}
+
+void GameModel::notifyQuickOptionsChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyQuickOptionsChanged(this);
+ }
+}
+
+void GameModel::notifyLastToolChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyLastToolChanged(this);
+ }
+}
diff --git a/src/game/GameModel.h b/src/game/GameModel.h
new file mode 100644
index 0000000..c1d97d6
--- /dev/null
+++ b/src/game/GameModel.h
@@ -0,0 +1,206 @@
+#ifndef GAMEMODEL_H
+#define GAMEMODEL_H
+
+#include <vector>
+#include <deque>
+#include "client/SaveInfo.h"
+#include "simulation/Simulation.h"
+#include "interface/Colour.h"
+#include "graphics/Renderer.h"
+#include "GameView.h"
+#include "GameController.h"
+#include "Brush.h"
+#include "client/User.h"
+#include "Notification.h"
+
+#include "Tool.h"
+#include "Menu.h"
+
+using namespace std;
+
+class GameView;
+class GameController;
+class Simulation;
+class Renderer;
+
+class QuickOption;
+class ToolSelection
+{
+public:
+ enum
+ {
+ ToolPrimary, ToolSecondary, ToolTertiary
+ };
+};
+
+class GameModel
+{
+private:
+ vector<Notification*> notifications;
+ //int clipboardSize;
+ //unsigned char * clipboardData;
+ GameSave * stamp;
+ GameSave * clipboard;
+ GameSave * placeSave;
+ deque<string> consoleLog;
+ vector<GameView*> observers;
+ vector<Tool*> toolList;
+
+ //All tools that are associated with elements
+ vector<Tool*> elementTools;
+ //Tools that are present in elementTools, but don't have an associated menu and need to be freed manually
+ vector<Tool*> extraElementTools;
+
+ vector<Menu*> menuList;
+ vector<QuickOption*> quickOptions;
+ Menu * activeMenu;
+ int currentBrush;
+ vector<Brush *> brushList;
+ SaveInfo * currentSave;
+ SaveFile * currentFile;
+ Simulation * sim;
+ Renderer * ren;
+ Tool * lastTool;
+ Tool ** activeTools;
+ Tool * decoToolset[3];
+ Tool * regularToolset[3];
+ User currentUser;
+ float toolStrength;
+ std::deque<Snapshot*> history;
+
+ int activeColourPreset;
+ std::vector<ui::Colour> colourPresets;
+ bool colourSelector;
+ ui::Colour colour;
+
+ int edgeMode;
+
+ std::string infoTip;
+ std::string toolTip;
+ //bool zoomEnabled;
+ void notifyRendererChanged();
+ void notifySimulationChanged();
+ void notifyPausedChanged();
+ void notifyDecorationChanged();
+ void notifySaveChanged();
+ void notifyBrushChanged();
+ void notifyMenuListChanged();
+ void notifyToolListChanged();
+ void notifyActiveToolsChanged();
+ void notifyUserChanged();
+ void notifyZoomChanged();
+ void notifyClipboardChanged();
+ void notifyPlaceSaveChanged();
+ void notifyColourSelectorColourChanged();
+ void notifyColourSelectorVisibilityChanged();
+ void notifyColourPresetsChanged();
+ void notifyColourActivePresetChanged();
+ void notifyNotificationsChanged();
+ void notifyLogChanged(string entry);
+ void notifyInfoTipChanged();
+ void notifyToolTipChanged();
+ void notifyQuickOptionsChanged();
+ void notifyLastToolChanged();
+public:
+ GameModel();
+ ~GameModel();
+
+ Tool * GetToolFromIdentifier(std::string identifier);
+
+ void SetEdgeMode(int edgeMode);
+ int GetEdgeMode();
+
+ void SetActiveColourPreset(int preset);
+ int GetActiveColourPreset();
+
+ void SetPresetColour(ui::Colour colour);
+
+ std::vector<ui::Colour> GetColourPresets();
+
+ void SetColourSelectorVisibility(bool visibility);
+ bool GetColourSelectorVisibility();
+
+ void SetColourSelectorColour(ui::Colour colour);
+ ui::Colour GetColourSelectorColour();
+
+ void SetToolTip(std::string text);
+ void SetInfoTip(std::string text);
+ std::string GetToolTip();
+ std::string GetInfoTip();
+
+ void BuildMenus();
+ void BuildQuickOptionMenu(GameController * controller);
+
+ std::deque<Snapshot*> GetHistory();
+ void SetHistory(std::deque<Snapshot*> newHistory);
+
+ void UpdateQuickOptions();
+
+ void SetToolStrength(float value);
+ float GetToolStrength();
+
+ Tool * GetLastTool();
+ void SetLastTool(Tool * newTool);
+
+ void SetVote(int direction);
+ SaveInfo * GetSave();
+ SaveFile * GetSaveFile();
+ Brush * GetBrush();
+ void SetSave(SaveInfo * newSave);
+ void SetSaveFile(SaveFile * newSave);
+ void AddObserver(GameView * observer);
+
+ //Get an element tool from an element ID
+ Tool * GetElementTool(int elementID);
+
+ Tool * GetActiveTool(int selection);
+ void SetActiveTool(int selection, Tool * tool);
+
+ bool GetPaused();
+ void SetPaused(bool pauseState);
+ bool GetDecoration();
+ void SetDecoration(bool decorationState);
+ bool GetAHeatEnable();
+ void SetAHeatEnable(bool aHeat);
+ bool GetGravityGrid();
+ void ShowGravityGrid(bool showGrid);
+ void ClearSimulation();
+ vector<Menu*> GetMenuList();
+ vector<Tool*> GetUnlistedTools();
+ vector<Tool*> GetToolList();
+ vector<QuickOption*> GetQuickOptions();
+ void SetActiveMenu(Menu * menu);
+ Menu * GetActiveMenu();
+ void FrameStep(int frames);
+ User GetUser();
+ void SetUser(User user);
+ void SetBrush(int i);
+ int GetBrushID();
+ Simulation * GetSimulation();
+ Renderer * GetRenderer();
+ void SetZoomEnabled(bool enabled);
+ bool GetZoomEnabled();
+ void SetZoomSize(int size);
+ int GetZoomSize();
+ void SetZoomFactor(int factor);
+ int GetZoomFactor();
+ void SetZoomPosition(ui::Point position);
+ ui::Point GetZoomPosition();
+ void SetZoomWindowPosition(ui::Point position);
+ ui::Point GetZoomWindowPosition();
+ void SetStamp(GameSave * newStamp);
+ void AddStamp(GameSave * save);
+ void SetClipboard(GameSave * save);
+ void SetPlaceSave(GameSave * save);
+ void Log(string message);
+ deque<string> GetLog();
+ GameSave * GetClipboard();
+ GameSave * GetStamp();
+ GameSave * GetPlaceSave();
+
+ std::vector<Notification*> GetNotifications();
+ void AddNotification(Notification * notification);
+ void RemoveNotification(Notification * notification);
+};
+
+#endif // GAMEMODEL_H
diff --git a/src/game/GameModelException.h b/src/game/GameModelException.h
new file mode 100644
index 0000000..05138f2
--- /dev/null
+++ b/src/game/GameModelException.h
@@ -0,0 +1,26 @@
+/*
+ * SaveLoadException.h
+ *
+ * Created on: Mar 29, 2012
+ * Author: Simon
+ */
+
+#ifndef SAVELOADEXCEPTION_H_
+#define SAVELOADEXCEPTION_H_
+
+#include <string>
+#include <exception>
+using namespace std;
+
+struct GameModelException: public exception {
+ string message;
+public:
+ GameModelException(string message_): message(message_) {}
+ const char * what() const throw()
+ {
+ return message.c_str();
+ }
+ ~GameModelException() throw() {};
+};
+
+#endif /* SAVELOADEXCEPTION_H_ */
diff --git a/src/game/GameView.cpp b/src/game/GameView.cpp
new file mode 100644
index 0000000..0e3f580
--- /dev/null
+++ b/src/game/GameView.cpp
@@ -0,0 +1,2079 @@
+#include <sstream>
+#include <iomanip>
+
+#include "Config.h"
+#include "Style.h"
+#include "GameView.h"
+#include "graphics/Graphics.h"
+#include "interface/Window.h"
+#include "interface/Button.h"
+#include "interface/Colour.h"
+#include "interface/Keys.h"
+#include "interface/Slider.h"
+#include "search/Thumbnail.h"
+#include "simulation/SaveRenderer.h"
+#include "dialogues/ConfirmPrompt.h"
+#include "Format.h"
+#include "QuickOption.h"
+#include "IntroText.h"
+
+
+class SplitButton;
+class SplitButtonAction
+{
+public:
+ virtual void ActionCallbackLeft(ui::Button * sender) {}
+ virtual void ActionCallbackRight(ui::Button * sender) {}
+ virtual ~SplitButtonAction() {}
+};
+class SplitButton : public ui::Button
+{
+private:
+ bool rightDown;
+ bool leftDown;
+ bool showSplit;
+ int splitPosition;
+ std::string toolTip2;
+ SplitButtonAction * splitActionCallback;
+public:
+ SplitButton(ui::Point position, ui::Point size, std::string buttonText, std::string toolTip, std::string toolTip2, int split) :
+ Button(position, size, buttonText, toolTip),
+ toolTip2(toolTip2),
+ splitPosition(split),
+ splitActionCallback(NULL),
+ showSplit(true)
+ {
+
+ }
+ bool GetShowSplit() { return showSplit; }
+ void SetShowSplit(bool split) { showSplit = split; }
+ SplitButtonAction * GetSplitActionCallback() { return splitActionCallback; }
+ void SetSplitActionCallback(SplitButtonAction * newAction) { splitActionCallback = newAction; }
+ virtual void OnMouseUnclick(int x, int y, unsigned int button)
+ {
+ if(isButtonDown)
+ {
+ if(leftDown)
+ DoLeftAction();
+ else if(rightDown)
+ DoRightAction();
+ }
+ ui::Button::OnMouseUnclick(x, y, button);
+
+ }
+ virtual void OnMouseMovedInside(int x, int y, int dx, int dy)
+ {
+ if(x >= splitPosition || !showSplit)
+ {
+ if(toolTip.length()>0 && GetParentWindow())
+ {
+ GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip2);
+ }
+ }
+ else if(x < splitPosition)
+ {
+ if(toolTip2.length()>0 && GetParentWindow())
+ {
+ GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip);
+ }
+ }
+ }
+ virtual void OnMouseEnter(int x, int y)
+ {
+ isMouseInside = true;
+ if(!Enabled)
+ return;
+ if(x >= splitPosition || !showSplit)
+ {
+ if(toolTip.length()>0 && GetParentWindow())
+ {
+ GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip2);
+ }
+ }
+ else if(x < splitPosition)
+ {
+ if(toolTip2.length()>0 && GetParentWindow())
+ {
+ GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip);
+ }
+ }
+ }
+ virtual void TextPosition()
+ {
+ ui::Button::TextPosition();
+ textPosition.X += 3;
+ }
+ virtual void OnMouseClick(int x, int y, unsigned int button)
+ {
+ ui::Button::OnMouseClick(x, y, button);
+ rightDown = false;
+ leftDown = false;
+ if(x >= splitPosition)
+ rightDown = true;
+ else if(x < splitPosition)
+ leftDown = true;
+ }
+ void DoRightAction()
+ {
+ if(!Enabled)
+ return;
+ if(splitActionCallback)
+ splitActionCallback->ActionCallbackRight(this);
+ }
+ void DoLeftAction()
+ {
+ if(!Enabled)
+ return;
+ if(splitActionCallback)
+ splitActionCallback->ActionCallbackLeft(this);
+ }
+ void Draw(const ui::Point& screenPos)
+ {
+ ui::Button::Draw(screenPos);
+ Graphics * g = ui::Engine::Ref().g;
+ drawn = true;
+
+ if(showSplit)
+ g->draw_line(splitPosition+screenPos.X, screenPos.Y+1, splitPosition+screenPos.X, screenPos.Y+Size.Y-2, 180, 180, 180, 255);
+ }
+ virtual ~SplitButton()
+ {
+ if(splitActionCallback)
+ delete splitActionCallback;
+ }
+};
+
+
+GameView::GameView():
+ ui::Window(ui::Point(0, 0), ui::Point(XRES+BARSIZE, YRES+MENUSIZE)),
+ pointQueue(queue<ui::Point>()),
+ isMouseDown(false),
+ ren(NULL),
+ activeBrush(NULL),
+ currentMouse(0, 0),
+ toolIndex(0),
+ zoomEnabled(false),
+ zoomCursorFixed(false),
+ drawPoint1(0, 0),
+ drawPoint2(0, 0),
+ drawMode(DrawPoints),
+ drawModeReset(false),
+ selectMode(SelectNone),
+ selectPoint1(0, 0),
+ selectPoint2(0, 0),
+ placeSaveThumb(NULL),
+ mousePosition(0, 0),
+ lastOffset(0),
+ drawSnap(false),
+ toolTip(""),
+ infoTip(""),
+ infoTipPresence(0),
+ toolTipPosition(-1, -1),
+ shiftBehaviour(false),
+ ctrlBehaviour(false),
+ altBehaviour(false),
+ showHud(true),
+ showDebug(false),
+ introText(2048),
+ introTextMessage(introTextData),
+ wallBrush(false),
+ doScreenshot(false),
+ recording(false),
+ screenshotIndex(0),
+ recordingIndex(0),
+ toolTipPresence(0),
+ currentSaveType(0)
+{
+
+ int currentX = 1;
+ //Set up UI
+ class SearchAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ SearchAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ if(v->CtrlBehaviour())
+ v->c->OpenLocalBrowse();
+ else
+ v->c->OpenSearch();
+ }
+ };
+
+ scrollBar = new ui::Button(ui::Point(0,YRES+21), ui::Point(XRES, 2), "");
+ scrollBar->Appearance.BackgroundInactive = ui::Colour(255, 255, 255);
+ scrollBar->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
+ scrollBar->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(scrollBar);
+
+ searchButton = new ui::Button(ui::Point(currentX, Size.Y-16), ui::Point(17, 15), "", "Find & open a simulation"); //Open
+ searchButton->SetIcon(IconOpen);
+ currentX+=18;
+ searchButton->SetTogglable(false);
+ searchButton->SetActionCallback(new SearchAction(this));
+ AddComponent(searchButton);
+
+ class ReloadAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ ReloadAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->ReloadSim();
+ }
+ };
+ reloadButton = new ui::Button(ui::Point(currentX, Size.Y-16), ui::Point(17, 15), "", "Reload the simulation");
+ reloadButton->SetIcon(IconReload);
+ reloadButton->Appearance.Margin.Left+=2;
+ currentX+=18;
+ reloadButton->SetActionCallback(new ReloadAction(this));
+ AddComponent(reloadButton);
+
+ class SaveSimulationAction : public SplitButtonAction
+ {
+ GameView * v;
+ public:
+ SaveSimulationAction(GameView * _v) { v = _v; }
+ void ActionCallbackRight(ui::Button * sender)
+ {
+ if(v->CtrlBehaviour())
+ v->c->OpenLocalSaveWindow(false);
+ else
+ v->c->OpenSaveWindow();
+ }
+ void ActionCallbackLeft(ui::Button * sender)
+ {
+ if(v->CtrlBehaviour())
+ v->c->OpenLocalSaveWindow(true);
+ else
+ v->c->SaveAsCurrent();
+ }
+ };
+ saveSimulationButton = new SplitButton(ui::Point(currentX, Size.Y-16), ui::Point(150, 15), "[untitled simulation]", "Save game as current name", "Save game as new name", 19);
+ saveSimulationButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ saveSimulationButton->SetIcon(IconSave);
+ currentX+=151;
+ ((SplitButton*)saveSimulationButton)->SetSplitActionCallback(new SaveSimulationAction(this));
+ AddComponent(saveSimulationButton);
+
+ class UpVoteAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ UpVoteAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->Vote(1);
+ }
+ };
+ upVoteButton = new ui::Button(ui::Point(currentX, Size.Y-16), ui::Point(15, 15), "", "Like this save");
+ upVoteButton->SetIcon(IconVoteUp);
+ upVoteButton->Appearance.Margin.Top+=2;
+ upVoteButton->Appearance.Margin.Left+=2;
+ currentX+=14;
+ upVoteButton->SetActionCallback(new UpVoteAction(this));
+ AddComponent(upVoteButton);
+
+ class DownVoteAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ DownVoteAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->Vote(-1);
+ }
+ };
+ downVoteButton = new ui::Button(ui::Point(currentX, Size.Y-16), ui::Point(15, 15), "", "Dislike this save");
+ downVoteButton->SetIcon(IconVoteDown);
+ downVoteButton->Appearance.Margin.Bottom+=2;
+ downVoteButton->Appearance.Margin.Left+=2;
+ currentX+=16;
+ downVoteButton->SetActionCallback(new DownVoteAction(this));
+ AddComponent(downVoteButton);
+
+ class TagSimulationAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ TagSimulationAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->OpenTags();
+ }
+ };
+ tagSimulationButton = new ui::Button(ui::Point(currentX, Size.Y-16), ui::Point(251, 15), "[no tags set]", "Add simulation tags");
+ tagSimulationButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ tagSimulationButton->SetIcon(IconTag);
+ currentX+=252;
+ tagSimulationButton->SetActionCallback(new TagSimulationAction(this));
+ AddComponent(tagSimulationButton);
+
+ class ClearSimAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ ClearSimAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->ClearSim();
+ }
+ };
+ clearSimButton = new ui::Button(ui::Point(Size.X-159, Size.Y-16), ui::Point(17, 15), "", "Erase everything");
+ clearSimButton->SetIcon(IconNew);
+ clearSimButton->Appearance.Margin.Left+=2;
+ clearSimButton->SetActionCallback(new ClearSimAction(this));
+ AddComponent(clearSimButton);
+
+ class LoginAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ LoginAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->OpenLogin();
+ }
+ };
+ loginButton = new ui::Button(ui::Point(Size.X-141, Size.Y-16), ui::Point(92, 15), "[sign in]", "Sign into simulation server");
+ loginButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ loginButton->SetIcon(IconLogin);
+ loginButton->SetActionCallback(new LoginAction(this));
+ AddComponent(loginButton);
+
+ class SimulationOptionAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ SimulationOptionAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->OpenOptions();
+ }
+ };
+ simulationOptionButton = new ui::Button(ui::Point(Size.X-48, Size.Y-16), ui::Point(15, 15), "", "Simulation options");
+ simulationOptionButton->SetIcon(IconSimulationSettings);
+ simulationOptionButton->Appearance.Margin.Left+=2;
+ simulationOptionButton->SetActionCallback(new SimulationOptionAction(this));
+ AddComponent(simulationOptionButton);
+
+ class DisplayModeAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ DisplayModeAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->OpenRenderOptions();
+ }
+ };
+ displayModeButton = new ui::Button(ui::Point(Size.X-32, Size.Y-16), ui::Point(15, 15), "", "Renderer options");
+ displayModeButton->SetIcon(IconRenderSettings);
+ displayModeButton->Appearance.Margin.Left+=2;
+ displayModeButton->SetActionCallback(new DisplayModeAction(this));
+ AddComponent(displayModeButton);
+
+ class PauseAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ PauseAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->SetPaused(sender->GetToggleState());
+ }
+ };
+ pauseButton = new ui::Button(ui::Point(Size.X-16, Size.Y-16), ui::Point(15, 15), "", "Pause/Resume the simulation"); //Pause
+ pauseButton->SetIcon(IconPause);
+ pauseButton->SetTogglable(true);
+ pauseButton->SetActionCallback(new PauseAction(this));
+ AddComponent(pauseButton);
+
+ class ElementSearchAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ ElementSearchAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->OpenElementSearch();
+ }
+ };
+ ui::Button * tempButton = new ui::Button(ui::Point(XRES+BARSIZE-16, YRES+MENUSIZE-32), ui::Point(15, 15), "\xE5", "Search for elements");
+ tempButton->Appearance.Margin = ui::Border(0, 2, 3, 2);
+ tempButton->SetActionCallback(new ElementSearchAction(this));
+ AddComponent(tempButton);
+
+ class ColourPickerAction : public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ ColourPickerAction(GameView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->OpenColourPicker();
+ }
+ };
+ colourPicker = new ui::Button(ui::Point((XRES/2)-8, YRES+1), ui::Point(16, 16), "", "Pick Colour");
+ colourPicker->SetActionCallback(new ColourPickerAction(this));
+}
+
+GameView::~GameView()
+{
+ if(!colourPicker->GetParentWindow())
+ delete colourPicker;
+
+ for(std::vector<ToolButton*>::iterator iter = colourPresets.begin(), end = colourPresets.end(); iter != end; ++iter)
+ {
+ ToolButton * button = *iter;
+ if(!button->GetParentWindow())
+ {
+ delete button;
+ }
+
+ }
+
+ if(placeSaveThumb)
+ delete placeSaveThumb;
+}
+
+class GameView::MenuAction: public ui::ButtonAction
+{
+ GameView * v;
+public:
+ Menu * menu;
+ MenuAction(GameView * _v, Menu * menu_) { v = _v; menu = menu_; }
+ void MouseEnterCallback(ui::Button * sender)
+ {
+ if(!ui::Engine::Ref().GetMouseButton())
+ v->c->SetActiveMenu(menu);
+ }
+ void ActionCallback(ui::Button * sender)
+ {
+ MouseEnterCallback(sender);
+ }
+};
+
+class GameView::OptionAction: public ui::ButtonAction
+{
+ QuickOption * option;
+public:
+ OptionAction(QuickOption * _option) { option = _option; }
+ void ActionCallback(ui::Button * sender)
+ {
+ option->Perform();
+ }
+};
+
+class GameView::OptionListener: public QuickOptionListener
+{
+ ui::Button * button;
+public:
+ OptionListener(ui::Button * _button) { button = _button; }
+ virtual void OnValueChanged(QuickOption * option)
+ {
+ switch(option->GetType())
+ {
+ case QuickOption::Toggle:
+ button->SetTogglable(true);
+ button->SetToggleState(option->GetToggle());
+ }
+ }
+};
+
+class GameView::ToolAction: public ui::ButtonAction
+{
+ GameView * v;
+public:
+ Tool * tool;
+ ToolAction(GameView * _v, Tool * tool_) { v = _v; tool = tool_; }
+ void ActionCallback(ui::Button * sender_)
+ {
+ ToolButton *sender = (ToolButton*)sender_;
+ if(sender->GetSelectionState() >= 0 && sender->GetSelectionState() <= 2)
+ v->c->SetActiveTool(sender->GetSelectionState(), tool);
+ }
+};
+
+void GameView::NotifyQuickOptionsChanged(GameModel * sender)
+{
+ for(int i = 0; i < quickOptionButtons.size(); i++)
+ {
+ RemoveComponent(quickOptionButtons[i]);
+ delete quickOptionButtons[i];
+ }
+
+ int currentY = 1;
+ vector<QuickOption*> optionList = sender->GetQuickOptions();
+ for(vector<QuickOption*>::iterator iter = optionList.begin(), end = optionList.end(); iter != end; ++iter)
+ {
+ QuickOption * option = *iter;
+ ui::Button * tempButton = new ui::Button(ui::Point(XRES+BARSIZE-16, currentY), ui::Point(15, 15), option->GetIcon(), option->GetDescription());
+ //tempButton->Appearance.Margin = ui::Border(0, 2, 3, 2);
+ tempButton->SetTogglable(true);
+ tempButton->SetActionCallback(new OptionAction(option));
+ option->AddListener(new OptionListener(tempButton));
+ AddComponent(tempButton);
+
+ quickOptionButtons.push_back(tempButton);
+ currentY += 16;
+ }
+}
+
+void GameView::NotifyMenuListChanged(GameModel * sender)
+{
+ int currentY = YRES+MENUSIZE-48;//-(sender->GetMenuList().size()*16);
+ for(int i = 0; i < menuButtons.size(); i++)
+ {
+ RemoveComponent(menuButtons[i]);
+ delete menuButtons[i];
+ }
+ menuButtons.clear();
+ for(int i = 0; i < toolButtons.size(); i++)
+ {
+ RemoveComponent(toolButtons[i]);
+ delete toolButtons[i];
+ }
+ toolButtons.clear();
+ vector<Menu*> menuList = sender->GetMenuList();
+ for(vector<Menu*>::reverse_iterator iter = menuList.rbegin(), end = menuList.rend(); iter != end; ++iter)
+ {
+ std::string tempString = "";
+ Menu * item = *iter;
+ tempString += item->GetIcon();
+ ui::Button * tempButton = new ui::Button(ui::Point(XRES+BARSIZE-16, currentY), ui::Point(15, 15), tempString, item->GetDescription());
+ tempButton->Appearance.Margin = ui::Border(0, 2, 3, 2);
+ tempButton->SetTogglable(true);
+ tempButton->SetActionCallback(new MenuAction(this, item));
+ currentY-=16;
+ AddComponent(tempButton);
+ menuButtons.push_back(tempButton);
+ }
+}
+
+void GameView::SetSample(SimulationSample sample)
+{
+ this->sample = sample;
+}
+
+ui::Point GameView::GetMousePosition()
+{
+ return mousePosition;
+}
+
+void GameView::NotifyActiveToolsChanged(GameModel * sender)
+{
+ for(int i = 0; i < toolButtons.size(); i++)
+ {
+ Tool * tool = ((ToolAction*)toolButtons[i]->GetActionCallback())->tool;
+ if(sender->GetActiveTool(0) == tool)
+ {
+ toolButtons[i]->SetSelectionState(0); //Primary
+ }
+ else if(sender->GetActiveTool(1) == tool)
+ {
+ toolButtons[i]->SetSelectionState(1); //Secondary
+ }
+ else if(sender->GetActiveTool(2) == tool)
+ {
+ toolButtons[i]->SetSelectionState(2); //Tertiary
+ }
+ else
+ {
+ toolButtons[i]->SetSelectionState(-1);
+ }
+ }
+}
+
+void GameView::NotifyLastToolChanged(GameModel * sender)
+{
+ if(sender->GetLastTool() && sender->GetLastTool()->GetResolution() == CELL)
+ {
+ wallBrush = true;
+ }
+ else
+ {
+ wallBrush = false;
+ }
+}
+
+void GameView::NotifyToolListChanged(GameModel * sender)
+{
+ //int currentY = YRES+MENUSIZE-36;
+ lastOffset = 0;
+ int currentX = XRES+BARSIZE-56;
+ int totalColour;
+ for(int i = 0; i < menuButtons.size(); i++)
+ {
+ if(((MenuAction*)menuButtons[i]->GetActionCallback())->menu==sender->GetActiveMenu())
+ {
+ menuButtons[i]->SetToggleState(true);
+ }
+ else
+ {
+ menuButtons[i]->SetToggleState(false);
+ }
+ }
+ for(int i = 0; i < toolButtons.size(); i++)
+ {
+ RemoveComponent(toolButtons[i]);
+ delete toolButtons[i];
+ }
+ toolButtons.clear();
+ vector<Tool*> toolList = sender->GetToolList();
+ for(int i = 0; i < toolList.size(); i++)
+ {
+ //ToolButton * tempButton = new ToolButton(ui::Point(XRES+1, currentY), ui::Point(28, 15), toolList[i]->GetName());
+ VideoBuffer * tempTexture = toolList[i]->GetTexture(26, 14);
+ ToolButton * tempButton;
+
+ if(tempTexture)
+ tempButton = new ToolButton(ui::Point(currentX, YRES+1), ui::Point(30, 18), "", toolList[i]->GetDescription());
+ else
+ tempButton = new ToolButton(ui::Point(currentX, YRES+1), ui::Point(30, 18), toolList[i]->GetName(), toolList[i]->GetDescription());
+
+ //currentY -= 17;
+ currentX -= 31;
+ tempButton->SetActionCallback(new ToolAction(this, toolList[i]));
+
+ tempButton->Appearance.SetTexture(tempTexture);
+ if(tempTexture)
+ delete tempTexture;
+
+ tempButton->Appearance.BackgroundInactive = ui::Colour(toolList[i]->colRed, toolList[i]->colGreen, toolList[i]->colBlue);
+
+ if(sender->GetActiveTool(0) == toolList[i])
+ {
+ tempButton->SetSelectionState(0); //Primary
+ }
+ else if(sender->GetActiveTool(1) == toolList[i])
+ {
+ tempButton->SetSelectionState(1); //Secondary
+ }
+ else if(sender->GetActiveTool(2) == toolList[i])
+ {
+ tempButton->SetSelectionState(2); //Tertiary
+ }
+
+ tempButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
+ tempButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempButton);
+ toolButtons.push_back(tempButton);
+ }
+
+}
+
+void GameView::NotifyColourSelectorVisibilityChanged(GameModel * sender)
+{
+ for(std::vector<ToolButton*>::iterator iter = colourPresets.begin(), end = colourPresets.end(); iter != end; ++iter)
+ {
+ ToolButton * button = *iter;
+ RemoveComponent(button);
+ button->SetParentWindow(NULL);
+ }
+
+ RemoveComponent(colourPicker);
+ colourPicker->SetParentWindow(NULL);
+
+ if(sender->GetColourSelectorVisibility())
+ {
+ for(std::vector<ToolButton*>::iterator iter = colourPresets.begin(), end = colourPresets.end(); iter != end; ++iter)
+ {
+ ToolButton * button = *iter;
+ AddComponent(button);
+ }
+ AddComponent(colourPicker);
+ }
+}
+
+void GameView::NotifyColourPresetsChanged(GameModel * sender)
+{
+ class ColourPresetAction: public ui::ButtonAction
+ {
+ GameView * v;
+ public:
+ int preset;
+ ColourPresetAction(GameView * _v, int preset) : preset(preset) { v = _v; }
+ void ActionCallback(ui::Button * sender_)
+ {
+ ToolButton *sender = (ToolButton*)sender_;
+ if(sender->GetSelectionState() == 0)
+ {
+ v->c->SetActiveColourPreset(preset);
+ v->c->SetColour(sender->Appearance.BackgroundInactive);
+ }
+ else
+ sender->SetSelectionState(0);
+ }
+ };
+
+
+ for(std::vector<ToolButton*>::iterator iter = colourPresets.begin(), end = colourPresets.end(); iter != end; ++iter)
+ {
+ ToolButton * button = *iter;
+ RemoveComponent(button);
+ delete button;
+ }
+ colourPresets.clear();
+
+ int currentX = 5;
+ std::vector<ui::Colour> colours = sender->GetColourPresets();
+ int i = 0;
+ for(std::vector<ui::Colour>::iterator iter = colours.begin(), end = colours.end(); iter != end; ++iter)
+ {
+ ToolButton * tempButton = new ToolButton(ui::Point(currentX, YRES+1), ui::Point(30, 18), "");
+ tempButton->Appearance.BackgroundInactive = *iter;
+ tempButton->SetActionCallback(new ColourPresetAction(this, i));
+
+ currentX += 31;
+
+ if(sender->GetColourSelectorVisibility())
+ AddComponent(tempButton);
+ colourPresets.push_back(tempButton);
+
+ i++;
+ }
+ NotifyColourActivePresetChanged(sender);
+}
+
+void GameView::NotifyColourActivePresetChanged(GameModel * sender)
+{
+ for(int i = 0; i < colourPresets.size(); i++)
+ {
+ if(sender->GetActiveColourPreset() == i)
+ {
+ colourPresets[i]->SetSelectionState(0); //Primary
+ }
+ else
+ {
+ colourPresets[i]->SetSelectionState(-1);
+ }
+ }
+}
+
+void GameView::NotifyColourSelectorColourChanged(GameModel * sender)
+{
+ colourPicker->Appearance.BackgroundInactive = sender->GetColourSelectorColour();
+ colourPicker->Appearance.BackgroundHover = sender->GetColourSelectorColour();
+}
+
+void GameView::NotifyRendererChanged(GameModel * sender)
+{
+ ren = sender->GetRenderer();
+}
+
+void GameView::NotifySimulationChanged(GameModel * sender)
+{
+
+}
+void GameView::NotifyUserChanged(GameModel * sender)
+{
+ if(!sender->GetUser().ID)
+ {
+ loginButton->SetText("[sign in]");
+ }
+ else
+ {
+ loginButton->SetText(sender->GetUser().Username);
+ }
+ NotifySaveChanged(sender);
+}
+
+
+void GameView::NotifyPausedChanged(GameModel * sender)
+{
+ pauseButton->SetToggleState(sender->GetPaused());
+}
+
+void GameView::NotifyToolTipChanged(GameModel * sender)
+{
+ toolTip = sender->GetToolTip();
+}
+
+void GameView::NotifyInfoTipChanged(GameModel * sender)
+{
+ infoTip = sender->GetInfoTip();
+ infoTipPresence = 120;
+}
+
+void GameView::NotifySaveChanged(GameModel * sender)
+{
+ if(sender->GetSave())
+ {
+ if(introText > 50)
+ introText = 50;
+
+ saveSimulationButton->SetText(sender->GetSave()->GetName());
+ if(sender->GetSave()->GetUserName() == sender->GetUser().Username)
+ ((SplitButton*)saveSimulationButton)->SetShowSplit(true);
+ else
+ ((SplitButton*)saveSimulationButton)->SetShowSplit(false);
+ reloadButton->Enabled = true;
+ upVoteButton->Enabled = (sender->GetSave()->GetID() && sender->GetUser().ID && sender->GetSave()->GetVote()==0);
+ if(sender->GetSave()->GetID() && sender->GetUser().ID && sender->GetSave()->GetVote()==1)
+ upVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 200, 40, 100));
+ else
+ upVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 0, 0));
+
+ downVoteButton->Enabled = upVoteButton->Enabled;
+ if(sender->GetSave()->GetID() && sender->GetUser().ID && sender->GetSave()->GetVote()==-1)
+ downVoteButton->Appearance.BackgroundDisabled = (ui::Colour(200, 40, 40, 100));
+ else
+ downVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 0, 0));
+
+ tagSimulationButton->Enabled = (sender->GetSave()->GetID() && sender->GetUser().ID);
+ if(sender->GetSave()->GetID())
+ {
+ std::stringstream tagsStream;
+ std::vector<string> tags = sender->GetSave()->GetTags();
+ if(tags.size())
+ {
+ for(int i = 0; i < tags.size(); i++)
+ {
+ tagsStream << sender->GetSave()->GetTags()[i];
+ if(i < tags.size()-1)
+ tagsStream << " ";
+ }
+ tagSimulationButton->SetText(tagsStream.str());
+ }
+ else
+ {
+ tagSimulationButton->SetText("[no tags set]");
+ }
+ }
+ else
+ {
+ tagSimulationButton->SetText("[no tags set]");
+ }
+ currentSaveType = 1;
+ }
+ else if (sender->GetSaveFile())
+ {
+ if (ctrlBehaviour)
+ ((SplitButton*)saveSimulationButton)->SetShowSplit(true);
+ else
+ ((SplitButton*)saveSimulationButton)->SetShowSplit(false);
+ saveSimulationButton->SetText(sender->GetSaveFile()->GetDisplayName());
+ reloadButton->Enabled = true;
+ upVoteButton->Enabled = false;
+ upVoteButton->Appearance.BackgroundInactive = (ui::Colour(0, 0, 0));
+ downVoteButton->Enabled = false;
+ upVoteButton->Appearance.BackgroundInactive = (ui::Colour(0, 0, 0));
+ tagSimulationButton->Enabled = false;
+ tagSimulationButton->SetText("[no tags set]");
+ currentSaveType = 2;
+ }
+ else
+ {
+ ((SplitButton*)saveSimulationButton)->SetShowSplit(false);
+ saveSimulationButton->SetText("[untitled simulation]");
+ reloadButton->Enabled = false;
+ upVoteButton->Enabled = false;
+ upVoteButton->Appearance.BackgroundInactive = (ui::Colour(0, 0, 0));
+ downVoteButton->Enabled = false;
+ upVoteButton->Appearance.BackgroundInactive = (ui::Colour(0, 0, 0));
+ tagSimulationButton->Enabled = false;
+ tagSimulationButton->SetText("[no tags set]");
+ currentSaveType = 0;
+ }
+}
+
+void GameView::NotifyBrushChanged(GameModel * sender)
+{
+ activeBrush = sender->GetBrush();
+}
+
+void GameView::screenshot()
+{
+ doScreenshot = true;
+}
+
+void GameView::record()
+{
+ if(recording)
+ {
+ recording = false;
+ }
+ else
+ {
+ class RecordingConfirmation: public ConfirmDialogueCallback {
+ public:
+ GameView * v;
+ RecordingConfirmation(GameView * v): v(v) {}
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ {
+ v->recording = true;
+ }
+ }
+ virtual ~RecordingConfirmation() { }
+ };
+ new ConfirmPrompt("Recording", "You're about to start recording all drawn frames. This may use a load of hard disk space.", new RecordingConfirmation(this));
+ }
+}
+
+void GameView::setToolButtonOffset(int offset)
+{
+ int offset_ = offset;
+ offset = offset-lastOffset;
+ lastOffset = offset_;
+
+ for(vector<ToolButton*>::iterator iter = toolButtons.begin(), end = toolButtons.end(); iter!=end; ++iter)
+ {
+ ToolButton * button = *iter;
+ button->Position.X -= offset;
+ if(button->Position.X <= 0 || (button->Position.X+button->Size.X) > XRES-2) {
+ button->Visible = false;
+ } else {
+ button->Visible = true;
+ }
+ }
+}
+
+void GameView::OnMouseMove(int x, int y, int dx, int dy)
+{
+ mousePosition = c->PointTranslate(ui::Point(x, y));
+ if(selectMode!=SelectNone)
+ {
+ if(selectMode==PlaceSave)
+ selectPoint1 = c->NormaliseBlockCoord(c->PointTranslate(ui::Point(x, y)));
+ if(selectPoint1.X!=-1)
+ selectPoint2 = c->NormaliseBlockCoord(c->PointTranslate(ui::Point(x, y)));
+ return;
+ }
+ currentMouse = ui::Point(x, y);
+ if(isMouseDown && drawMode == DrawPoints)
+ {
+ pointQueue.push(ui::Point(c->PointTranslate(ui::Point(x-dx, y-dy))));
+ pointQueue.push(ui::Point(c->PointTranslate(ui::Point(x, y))));
+ }
+}
+
+void GameView::OnMouseDown(int x, int y, unsigned button)
+{
+ if(altBehaviour && !shiftBehaviour)
+ button = BUTTON_MIDDLE;
+ if(selectMode!=SelectNone)
+ {
+ if(button==BUTTON_LEFT)
+ {
+ selectPoint1 = c->NormaliseBlockCoord(c->PointTranslate(ui::Point(x, y)));
+ selectPoint2 = selectPoint1;
+ }
+ return;
+ }
+ if(currentMouse.X >= 0 && currentMouse.X < XRES && currentMouse.Y >= 0 && currentMouse.Y < YRES && !(zoomEnabled && !zoomCursorFixed))
+ {
+ if(button == BUTTON_LEFT)
+ toolIndex = 0;
+ if(button == BUTTON_RIGHT)
+ toolIndex = 1;
+ if(button == BUTTON_MIDDLE)
+ toolIndex = 2;
+ isMouseDown = true;
+ if(!pointQueue.size())
+ c->HistorySnapshot();
+ if(drawMode == DrawRect || drawMode == DrawLine)
+ {
+ drawPoint1 = ui::Point(x, y);
+ }
+ if(drawMode == DrawPoints)
+ {
+ pointQueue.push(ui::Point(c->PointTranslate(ui::Point(x, y))));
+ }
+ }
+}
+
+void GameView::OnMouseUp(int x, int y, unsigned button)
+{
+ if(selectMode!=SelectNone)
+ {
+ if(button==BUTTON_LEFT)
+ {
+ if(selectMode==PlaceSave)
+ {
+ Thumbnail * tempThumb = placeSaveThumb;
+ if(tempThumb)
+ {
+ int thumbX = selectPoint2.X - (tempThumb->Size.X/2);
+ int thumbY = selectPoint2.Y - (tempThumb->Size.Y/2);
+
+ if(thumbX<0)
+ thumbX = 0;
+ if(thumbX+(tempThumb->Size.X)>=XRES)
+ thumbX = XRES-tempThumb->Size.X;
+
+ if(thumbY<0)
+ thumbY = 0;
+ if(thumbY+(tempThumb->Size.Y)>=YRES)
+ thumbY = YRES-tempThumb->Size.Y;
+
+ c->PlaceSave(ui::Point(thumbX, thumbY));
+ }
+ }
+ else
+ {
+ int x2 = (selectPoint1.X>selectPoint2.X)?selectPoint1.X:selectPoint2.X;
+ int y2 = (selectPoint1.Y>selectPoint2.Y)?selectPoint1.Y:selectPoint2.Y;
+ int x1 = (selectPoint2.X<selectPoint1.X)?selectPoint2.X:selectPoint1.X;
+ int y1 = (selectPoint2.Y<selectPoint1.Y)?selectPoint2.Y:selectPoint1.Y;
+ if(x2-x1>0 && y2-y1>0)
+ {
+ if(selectMode==SelectCopy)
+ c->CopyRegion(ui::Point(x1, y1), ui::Point(x2, y2));
+ else if(selectMode==SelectCut)
+ c->CutRegion(ui::Point(x1, y1), ui::Point(x2, y2));
+ else if(selectMode==SelectStamp)
+ c->StampRegion(ui::Point(x1, y1), ui::Point(x2, y2));
+ }
+ }
+ }
+ currentMouse = ui::Point(x, y);
+ selectMode = SelectNone;
+ return;
+ }
+
+ if(zoomEnabled && !zoomCursorFixed)
+ zoomCursorFixed = true;
+ else
+ {
+ if(isMouseDown)
+ {
+ isMouseDown = false;
+ if(drawMode == DrawRect || drawMode == DrawLine)
+ {
+ ui::Point finalDrawPoint2(0, 0);
+ drawPoint2 = c->PointTranslate(ui::Point(x, y));
+ finalDrawPoint2 = drawPoint2;
+
+ if(drawSnap && drawMode == DrawLine)
+ {
+ finalDrawPoint2 = lineSnapCoords(c->PointTranslate(drawPoint1), drawPoint2);
+ }
+
+ if(drawSnap && drawMode == DrawRect)
+ {
+ finalDrawPoint2 = rectSnapCoords(c->PointTranslate(drawPoint1), drawPoint2);
+ }
+
+ if(drawMode == DrawRect)
+ {
+ c->DrawRect(toolIndex, c->PointTranslate(drawPoint1), finalDrawPoint2);
+ }
+ if(drawMode == DrawLine)
+ {
+ c->DrawLine(toolIndex, c->PointTranslate(drawPoint1), finalDrawPoint2);
+ }
+ }
+ if(drawMode == DrawPoints)
+ {
+ c->ToolClick(toolIndex, c->PointTranslate(ui::Point(x, y)));
+ //pointQueue.push(ui::Point(x, y));
+ }
+ if(drawModeReset)
+ {
+ drawModeReset = false;
+ drawMode = DrawPoints;
+ }
+ }
+ }
+}
+
+void GameView::ExitPrompt()
+{
+ class ExitConfirmation: public ConfirmDialogueCallback {
+ public:
+ ExitConfirmation() {}
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ {
+ ui::Engine::Ref().Exit();
+ }
+ }
+ virtual ~ExitConfirmation() { }
+ };
+ new ConfirmPrompt("You are about to quit", "Are you sure you want to exit the game?", new ExitConfirmation());
+}
+
+void GameView::ToolTip(ui::Component * sender, ui::Point mousePosition, std::string toolTip)
+{
+ if(sender->Position.Y > Size.Y-17)
+ {
+ buttonTip = toolTip;
+ buttonTipShow = 120;
+ }
+ else if(sender->Position.X > Size.X-BARSIZE)// < Size.Y-(quickOptionButtons.size()+1)*16)
+ {
+ this->toolTip = toolTip;
+ toolTipPosition = ui::Point(Size.X-27-Graphics::textwidth((char*)toolTip.c_str()), sender->Position.Y+3);
+ if(toolTipPosition.Y+10 > Size.Y-MENUSIZE)
+ toolTipPosition = ui::Point(Size.X-27-Graphics::textwidth((char*)toolTip.c_str()), Size.Y-MENUSIZE-10);
+ toolTipPresence = 120;
+ }
+ else
+ {
+ this->toolTip = toolTip;
+ toolTipPosition = ui::Point(Size.X-27-Graphics::textwidth((char*)toolTip.c_str()), Size.Y-MENUSIZE-10);
+ toolTipPresence = 160;
+ }
+}
+
+void GameView::OnMouseWheel(int x, int y, int d)
+{
+ if(!d)
+ return;
+ if(selectMode!=SelectNone)
+ {
+ return;
+ }
+ if(zoomEnabled && !zoomCursorFixed)
+ {
+ c->AdjustZoomSize(d);
+ }
+ else
+ {
+ c->AdjustBrushSize(d, false, shiftBehaviour, ctrlBehaviour);
+ if(isMouseDown)
+ {
+ pointQueue.push(ui::Point(c->PointTranslate(ui::Point(x, y))));
+ }
+ }
+}
+
+void GameView::ToggleDebug()
+{
+ showDebug = !showDebug;
+ if (ren)
+ ren->debugLines = showDebug;
+}
+
+void GameView::BeginStampSelection()
+{
+ selectMode = SelectStamp;
+ selectPoint1 = ui::Point(-1, -1);
+ infoTip = "\x0F\xEF\xEF\x10Select an area to create a stamp";
+ infoTipPresence = 120;
+}
+
+void GameView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(introText > 50)
+ {
+ introText = 50;
+ }
+
+ if(selectMode!=SelectNone)
+ {
+ if(selectMode==PlaceSave)
+ {
+ switch(key)
+ {
+ case KEY_RIGHT:
+ case 'd':
+ c->TranslateSave(ui::Point(1, 0));
+ break;
+ case KEY_LEFT:
+ case 'a':
+ c->TranslateSave(ui::Point(-1, 0));
+ break;
+ case KEY_UP:
+ case 'w':
+ c->TranslateSave(ui::Point(0, -1));
+ break;
+ case KEY_DOWN:
+ case 's':
+ c->TranslateSave(ui::Point(0, 1));
+ break;
+ case 'r':
+ if(shift)
+ {
+ //Flip
+ c->TransformSave(m2d_new(-1,0,0,1));
+ }
+ else
+ {
+ //Rotate 90deg
+ c->TransformSave(m2d_new(0,1,-1,0));
+ }
+ break;
+ }
+ }
+ if(key != ' ')
+ return;
+ }
+ switch(key)
+ {
+ case KEY_ALT:
+ drawSnap = true;
+ enableAltBehaviour();
+ break;
+ case KEY_CTRL:
+ if(!isMouseDown)
+ {
+ if(drawModeReset)
+ drawModeReset = false;
+ else
+ drawPoint1 = currentMouse;
+ if(shift)
+ drawMode = DrawFill;
+ else
+ drawMode = DrawRect;
+ }
+ enableCtrlBehaviour();
+ break;
+ case KEY_SHIFT:
+ if(!isMouseDown)
+ {
+ if(drawModeReset)
+ drawModeReset = false;
+ else
+ drawPoint1 = currentMouse;
+ if(ctrl)
+ drawMode = DrawFill;
+ else
+ drawMode = DrawLine;
+ }
+ enableShiftBehaviour();
+ break;
+ case ' ': //Space
+ c->SetPaused();
+ break;
+ case KEY_TAB: //Tab
+ c->ChangeBrush();
+ break;
+ case 'z':
+ if(ctrl)
+ {
+ c->HistoryRestore();
+ }
+ else
+ {
+ isMouseDown = false;
+ zoomCursorFixed = false;
+ c->SetZoomEnabled(true);
+ }
+ break;
+ case '`':
+ c->ShowConsole();
+ break;
+ case 'p':
+ screenshot();
+ break;
+ case 'r':
+ record();
+ break;
+ case 'e':
+ c->OpenElementSearch();
+ break;
+ case 'f':
+ c->FrameStep();
+ break;
+ case 'g':
+ if (ctrl)
+ c->ShowGravityGrid();
+ else if(shift)
+ c->AdjustGridSize(-1);
+ else
+ c->AdjustGridSize(1);
+ break;
+ case KEY_F1:
+ if(!introText)
+ introText = 8047;
+ else
+ introText = 0;
+ break;
+ case 'h':
+ if(ctrl)
+ {
+ if(!introText)
+ introText = 8047;
+ else
+ introText = 0;
+ }
+ else
+ showHud = !showHud;
+ break;
+ case 'b':
+ if(ctrl)
+ c->SetDecoration();
+ break;
+ case 'y':
+ c->SwitchAir();
+ break;
+ case KEY_ESCAPE:
+ case 'q':
+ ExitPrompt();
+ break;
+ case 'u':
+ c->ToggleAHeat();
+ break;
+ case '=':
+ if(ctrl)
+ c->ResetSpark();
+ else
+ c->ResetAir();
+ break;
+ case 'c':
+ if(ctrl)
+ {
+ selectMode = SelectCopy;
+ selectPoint1 = ui::Point(-1, -1);
+ infoTip = "\x0F\xEF\xEF\x10Select an area to copy";
+ infoTipPresence = 120;
+ }
+ break;
+ case 'x':
+ if(ctrl)
+ {
+ selectMode = SelectCut;
+ selectPoint1 = ui::Point(-1, -1);
+ infoTip = "\x0F\xEF\xEF\x10Select an area to cut";
+ infoTipPresence = 120;
+ }
+ break;
+ case 'v':
+ if(ctrl)
+ {
+ c->LoadClipboard();
+ selectPoint2 = ui::Point(-1, -1);
+ selectPoint1 = selectPoint2;
+ }
+ break;
+ case 'l':
+ c->LoadStamp();
+ selectPoint2 = ui::Point(-1, -1);
+ selectPoint1 = selectPoint2;
+ isMouseDown = false;
+ drawMode = DrawPoints;
+ break;
+ case 'k':
+ selectPoint2 = ui::Point(-1, -1);
+ selectPoint1 = selectPoint2;
+ c->OpenStamps();
+ break;
+ case ']':
+ if(zoomEnabled && !zoomCursorFixed)
+ c->AdjustZoomSize(1, !alt);
+ else
+ c->AdjustBrushSize(1, !alt, shiftBehaviour, ctrlBehaviour);
+ break;
+ case '[':
+ if(zoomEnabled && !zoomCursorFixed)
+ c->AdjustZoomSize(-1, !alt);
+ else
+ c->AdjustBrushSize(-1, !alt, shiftBehaviour, ctrlBehaviour);
+ break;
+ case 'i':
+ if(ctrl)
+ c->Install();
+ else
+ c->InvertAirSim();
+ break;
+ }
+
+ if (shift && showDebug && key == '1')
+ c->LoadRenderPreset(10);
+ else if(key >= '0' && key <= '9')
+ {
+ c->LoadRenderPreset(key-'0');
+ }
+}
+
+void GameView::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(!isMouseDown)
+ drawMode = DrawPoints;
+ else
+ drawModeReset = true;
+ switch(key)
+ {
+ case KEY_ALT:
+ drawSnap = false;
+ disableAltBehaviour();
+ break;
+ case KEY_CTRL:
+ disableCtrlBehaviour();
+ break;
+ case KEY_SHIFT:
+ disableShiftBehaviour();
+ break;
+ case 'z':
+ if(!ctrl)
+ {
+ if(!zoomCursorFixed && !alt)
+ c->SetZoomEnabled(false);
+ }
+ break;
+ }
+}
+
+void GameView::OnBlur()
+{
+ disableAltBehaviour();
+ disableCtrlBehaviour();
+ disableShiftBehaviour();
+ isMouseDown = false;
+ drawMode = DrawPoints;
+}
+
+void GameView::OnTick(float dt)
+{
+ if(selectMode==PlaceSave && !placeSaveThumb)
+ selectMode = SelectNone;
+ if(zoomEnabled && !zoomCursorFixed)
+ c->SetZoomPosition(currentMouse);
+ if(drawMode == DrawPoints)
+ {
+ if(isMouseDown)
+ {
+ pointQueue.push(ui::Point(c->PointTranslate(currentMouse)));
+ }
+ if(!pointQueue.empty())
+ {
+ c->DrawPoints(toolIndex, pointQueue);
+ }
+ }
+ if(drawMode == DrawFill && isMouseDown)
+ {
+ c->DrawFill(toolIndex, c->PointTranslate(currentMouse));
+ }
+ if(introText)
+ {
+ introText -= int(dt)>0?((int)dt < 5? dt:5):1;
+ if(introText < 0)
+ introText = 0;
+ }
+ if(infoTipPresence>0)
+ {
+ infoTipPresence -= int(dt)>0?int(dt):1;
+ if(infoTipPresence<0)
+ infoTipPresence = 0;
+ }
+ if(buttonTipShow>0)
+ {
+ buttonTipShow -= int(dt)>0?int(dt):1;
+ if(buttonTipShow<0)
+ buttonTipShow = 0;
+ }
+ if(toolTipPresence>0)
+ {
+ toolTipPresence -= int(dt)>0?int(dt):1;
+ if(toolTipPresence<0)
+ toolTipPresence = 0;
+ }
+ c->Update();
+ if(lastLogEntry > -0.1f)
+ lastLogEntry -= 0.16*dt;
+}
+
+
+void GameView::DoMouseMove(int x, int y, int dx, int dy)
+{
+ if(c->MouseMove(x, y, dx, dy))
+ Window::DoMouseMove(x, y, dx, dy);
+
+ if(toolButtons.size())
+ {
+ int totalWidth = (toolButtons[0]->Size.X+1)*toolButtons.size();
+ int scrollSize = (int)(((float)(XRES-15))/((float)totalWidth) * ((float)XRES-15));
+ if (scrollSize>XRES)
+ scrollSize = XRES;
+ if(totalWidth > XRES-15)
+ {
+ int mouseX = x;
+ if(mouseX > XRES)
+ mouseX = XRES;
+
+ scrollBar->Position.X = (int)(((float)mouseX/((float)XRES-15))*(float)(XRES-scrollSize));
+
+ float overflow = totalWidth-(XRES-15), mouseLocation = float(XRES)/float(mouseX-(XRES));
+ setToolButtonOffset(overflow/mouseLocation);
+
+ //Ensure that mouseLeave events are make their way to the buttons should they move from underneith the mouse pointer
+ if(toolButtons[0]->Position.Y < y && toolButtons[0]->Position.Y+toolButtons[0]->Size.Y > y)
+ {
+ for(vector<ToolButton*>::iterator iter = toolButtons.begin(), end = toolButtons.end(); iter!=end; ++iter)
+ {
+ ToolButton * button = *iter;
+ if(button->Position.X < x && button->Position.X+button->Size.X > x)
+ button->OnMouseEnter(x, y);
+ else
+ button->OnMouseLeave(x, y);
+ }
+ }
+ }
+ else
+ {
+ scrollBar->Position.X = 0;
+ }
+ scrollBar->Size.X=scrollSize;
+ }
+}
+
+void GameView::DoMouseDown(int x, int y, unsigned button)
+{
+ if(introText > 50)
+ introText = 50;
+ if(c->MouseDown(x, y, button))
+ Window::DoMouseDown(x, y, button);
+}
+
+void GameView::DoMouseUp(int x, int y, unsigned button)
+{
+ if(c->MouseUp(x, y, button))
+ Window::DoMouseUp(x, y, button);
+}
+
+void GameView::DoMouseWheel(int x, int y, int d)
+{
+ if(c->MouseWheel(x, y, d))
+ Window::DoMouseWheel(x, y, d);
+}
+
+void GameView::DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(c->KeyPress(key, character, shift, ctrl, alt))
+ Window::DoKeyPress(key, character, shift, ctrl, alt);
+}
+
+void GameView::DoKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(c->KeyRelease(key, character, shift, ctrl, alt))
+ Window::DoKeyRelease(key, character, shift, ctrl, alt);
+}
+
+void GameView::DoDraw()
+{
+ Window::DoDraw();
+ c->Tick();
+}
+
+void GameView::NotifyNotificationsChanged(GameModel * sender)
+{
+ class NotificationButtonAction : public ui::ButtonAction
+ {
+ GameView * v;
+ Notification * notification;
+ public:
+ NotificationButtonAction(GameView * v, Notification * notification) : v(v), notification(notification) { }
+ void ActionCallback(ui::Button * sender)
+ {
+ notification->Action();
+ //v->c->RemoveNotification(notification);
+ }
+ };
+ class CloseNotificationButtonAction : public ui::ButtonAction
+ {
+ GameView * v;
+ Notification * notification;
+ public:
+ CloseNotificationButtonAction(GameView * v, Notification * notification) : v(v), notification(notification) { }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->RemoveNotification(notification);
+ }
+ };
+
+ for(std::vector<ui::Component*>::const_iterator iter = notificationComponents.begin(), end = notificationComponents.end(); iter != end; ++iter) {
+ ui::Component * cNotification = *iter;
+ RemoveComponent(cNotification);
+ delete cNotification;
+ }
+ notificationComponents.clear();
+
+ std::vector<Notification*> notifications = sender->GetNotifications();
+
+ int currentY = YRES-23;
+ for(std::vector<Notification*>::iterator iter = notifications.begin(), end = notifications.end(); iter != end; ++iter)
+ {
+ int width = (Graphics::textwidth((*iter)->Message.c_str()))+8;
+ ui::Button * tempButton = new ui::Button(ui::Point(XRES-width-22, currentY), ui::Point(width, 15), (*iter)->Message);
+ tempButton->SetActionCallback(new NotificationButtonAction(this, *iter));
+ tempButton->Appearance.BorderInactive = style::Colour::WarningTitle;
+ tempButton->Appearance.TextInactive = style::Colour::WarningTitle;
+ tempButton->Appearance.BorderHover = ui::Colour(255, 175, 0);
+ tempButton->Appearance.TextHover = ui::Colour(255, 175, 0);
+ AddComponent(tempButton);
+ notificationComponents.push_back(tempButton);
+
+ tempButton = new ui::Button(ui::Point(XRES-20, currentY), ui::Point(15, 15));
+ tempButton->SetIcon(IconClose);
+ tempButton->SetActionCallback(new CloseNotificationButtonAction(this, *iter));
+ tempButton->Appearance.Margin.Left+=2;
+ tempButton->Appearance.Margin.Top+=2;
+ tempButton->Appearance.BorderInactive = style::Colour::WarningTitle;
+ tempButton->Appearance.TextInactive = style::Colour::WarningTitle;
+ tempButton->Appearance.BorderHover = ui::Colour(255, 175, 0);
+ tempButton->Appearance.TextHover = ui::Colour(255, 175, 0);
+ AddComponent(tempButton);
+ notificationComponents.push_back(tempButton);
+
+ currentY -= 17;
+ }
+}
+
+void GameView::NotifyZoomChanged(GameModel * sender)
+{
+ zoomEnabled = sender->GetZoomEnabled();
+}
+
+void GameView::NotifyLogChanged(GameModel * sender, string entry)
+{
+ logEntries.push_front(entry);
+ lastLogEntry = 100.0f;
+ if(logEntries.size()>20)
+ logEntries.pop_back();
+}
+
+void GameView::NotifyPlaceSaveChanged(GameModel * sender)
+{
+ if(placeSaveThumb)
+ delete placeSaveThumb;
+ if(sender->GetPlaceSave())
+ {
+ placeSaveThumb = SaveRenderer::Ref().Render(sender->GetPlaceSave());
+ selectMode = PlaceSave;
+ }
+ else
+ {
+ placeSaveThumb = NULL;
+ selectMode = SelectNone;
+ }
+}
+
+void GameView::enableShiftBehaviour()
+{
+ if(!shiftBehaviour)
+ {
+ shiftBehaviour = true;
+ if(isMouseDown)
+ {
+ c->SetToolStrength(10.0f);
+ }
+ }
+}
+
+void GameView::disableShiftBehaviour()
+{
+ if(shiftBehaviour)
+ {
+ shiftBehaviour = false;
+ if(!ctrlBehaviour)
+ c->SetToolStrength(1.0f);
+ else
+ c->SetToolStrength(.1f);
+ }
+}
+
+void GameView::enableAltBehaviour()
+{
+ if(!altBehaviour)
+ {
+ altBehaviour = true;
+ }
+}
+
+void GameView::disableAltBehaviour()
+{
+ if(altBehaviour)
+ {
+ altBehaviour = false;
+ }
+}
+
+void GameView::enableCtrlBehaviour()
+{
+ if(!ctrlBehaviour)
+ {
+ ctrlBehaviour = true;
+
+ //Show HDD save & load buttons
+ saveSimulationButton->Appearance.BackgroundInactive = saveSimulationButton->Appearance.BackgroundHover = ui::Colour(255, 255, 255);
+ saveSimulationButton->Appearance.TextInactive = saveSimulationButton->Appearance.TextHover = ui::Colour(0, 0, 0);
+ searchButton->Appearance.BackgroundInactive = searchButton->Appearance.BackgroundHover = ui::Colour(255, 255, 255);
+ searchButton->Appearance.TextInactive = searchButton->Appearance.TextHover = ui::Colour(0, 0, 0);
+ if (currentSaveType == 2)
+ ((SplitButton*)saveSimulationButton)->SetShowSplit(true);
+ if(isMouseDown)
+ {
+ if(!shiftBehaviour)
+ c->SetToolStrength(.1f);
+ else
+ c->SetToolStrength(10.0f);
+ }
+ }
+}
+
+void GameView::disableCtrlBehaviour()
+{
+ if(ctrlBehaviour)
+ {
+ ctrlBehaviour = false;
+
+ //Hide HDD save & load buttons
+ saveSimulationButton->Appearance.BackgroundInactive = ui::Colour(0, 0, 0);
+ saveSimulationButton->Appearance.BackgroundHover = ui::Colour(20, 20, 20);
+ saveSimulationButton->Appearance.TextInactive = saveSimulationButton->Appearance.TextHover = ui::Colour(255, 255, 255);
+ searchButton->Appearance.BackgroundInactive = ui::Colour(0, 0, 0);
+ searchButton->Appearance.BackgroundHover = ui::Colour(20, 20, 20);
+ searchButton->Appearance.TextInactive = searchButton->Appearance.TextHover = ui::Colour(255, 255, 255);
+ if (currentSaveType == 2)
+ ((SplitButton*)saveSimulationButton)->SetShowSplit(false);
+ if(!shiftBehaviour)
+ c->SetToolStrength(1.0f);
+ else
+ c->SetToolStrength(10.0f);
+ }
+}
+
+void GameView::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ if(ren)
+ {
+ ren->clearScreen(1.0f);
+ ren->RenderBegin();
+ if(selectMode == SelectNone && (!zoomEnabled || zoomCursorFixed) && activeBrush && currentMouse.X >= 0 && currentMouse.X < XRES && currentMouse.Y >= 0 && currentMouse.Y < YRES)
+ {
+ ui::Point finalCurrentMouse = c->PointTranslate(currentMouse);
+ ui::Point initialDrawPoint = drawPoint1;
+
+ if(wallBrush)
+ {
+ finalCurrentMouse = c->NormaliseBlockCoord(finalCurrentMouse);
+ initialDrawPoint = c->NormaliseBlockCoord(initialDrawPoint);
+ }
+
+ if(drawMode==DrawRect && isMouseDown)
+ {
+ if(drawSnap)
+ {
+ finalCurrentMouse = rectSnapCoords(c->PointTranslate(initialDrawPoint), finalCurrentMouse);
+ }
+ activeBrush->RenderRect(ren, c->PointTranslate(initialDrawPoint), finalCurrentMouse);
+ }
+ else if(drawMode==DrawLine && isMouseDown)
+ {
+ if(drawSnap)
+ {
+ finalCurrentMouse = lineSnapCoords(c->PointTranslate(initialDrawPoint), finalCurrentMouse);
+ }
+ activeBrush->RenderLine(ren, c->PointTranslate(initialDrawPoint), finalCurrentMouse);
+ }
+ else if(drawMode==DrawFill)
+ {
+ activeBrush->RenderFill(ren, finalCurrentMouse);
+ }
+ else
+ {
+ if(wallBrush)
+ {
+ ui::Point finalBrushRadius = c->NormaliseBlockCoord(activeBrush->GetRadius());
+ ren->xor_line(finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y-finalBrushRadius.Y, finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y-finalBrushRadius.Y);
+ ren->xor_line(finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-1, finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-1);
+
+ ren->xor_line(finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y-finalBrushRadius.Y+1, finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-2);
+ ren->xor_line(finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y-finalBrushRadius.Y+1, finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-2);
+ }
+ else
+ {
+ activeBrush->RenderPoint(ren, finalCurrentMouse);
+ }
+ }
+ }
+
+ if(selectMode!=SelectNone)
+ {
+ if(selectMode==PlaceSave)
+ {
+ Thumbnail * tempThumb = placeSaveThumb;
+ if(tempThumb && selectPoint2.X!=-1)
+ {
+ int thumbX = selectPoint2.X - (tempThumb->Size.X/2);
+ int thumbY = selectPoint2.Y - (tempThumb->Size.Y/2);
+
+ ui::Point thumbPos = c->NormaliseBlockCoord(ui::Point(thumbX, thumbY));
+
+ if(thumbPos.X<0)
+ thumbPos.X = 0;
+ if(thumbPos.X+(tempThumb->Size.X)>=XRES)
+ thumbPos.X = XRES-tempThumb->Size.X;
+
+ if(thumbPos.Y<0)
+ thumbPos.Y = 0;
+ if(thumbPos.Y+(tempThumb->Size.Y)>=YRES)
+ thumbPos.Y = YRES-tempThumb->Size.Y;
+
+ ren->draw_image(tempThumb->Data, thumbPos.X, thumbPos.Y, tempThumb->Size.X, tempThumb->Size.Y, 128);
+
+ ren->xor_rect(thumbPos.X, thumbPos.Y, tempThumb->Size.X, tempThumb->Size.Y);
+ }
+ }
+ else
+ {
+ if(selectPoint1.X==-1)
+ {
+ ren->fillrect(0, 0, XRES, YRES, 0, 0, 0, 100);
+ }
+ else
+ {
+ int x2 = (selectPoint1.X>selectPoint2.X)?selectPoint1.X:selectPoint2.X;
+ int y2 = (selectPoint1.Y>selectPoint2.Y)?selectPoint1.Y:selectPoint2.Y;
+ int x1 = (selectPoint2.X<selectPoint1.X)?selectPoint2.X:selectPoint1.X;
+ int y1 = (selectPoint2.Y<selectPoint1.Y)?selectPoint2.Y:selectPoint1.Y;
+
+ if(x2>XRES-1)
+ x2 = XRES-1;
+ if(y2>YRES-1)
+ y2 = YRES-1;
+
+ ren->fillrect(0, 0, XRES, y1, 0, 0, 0, 100);
+ ren->fillrect(0, y2, XRES, YRES-y2, 0, 0, 0, 100);
+
+ ren->fillrect(0, y1, x1, (y2-y1), 0, 0, 0, 100);
+ ren->fillrect(x2, y1, XRES-x2, (y2-y1), 0, 0, 0, 100);
+
+ ren->xor_rect(x1, y1, (x2-x1)+1, (y2-y1)+1);
+ }
+ }
+ }
+
+ ren->RenderEnd();
+
+ if(doScreenshot)
+ {
+ VideoBuffer screenshot(ren->DumpFrame());
+ std::vector<char> data = format::VideoBufferToPNG(screenshot);
+
+ std::stringstream filename;
+ filename << "screenshot_";
+ filename << std::setfill('0') << std::setw(6) << (screenshotIndex++);
+ filename << ".png";
+
+ Client::Ref().WriteFile(data, filename.str());
+ doScreenshot = false;
+ }
+
+ if(recording)
+ {
+ VideoBuffer screenshot(ren->DumpFrame());
+ std::vector<char> data = format::VideoBufferToPPM(screenshot);
+
+ std::stringstream filename;
+ filename << "frame_";
+ filename << std::setfill('0') << std::setw(6) << (recordingIndex++);
+ filename << ".ppm";
+
+ Client::Ref().WriteFile(data, filename.str());
+ }
+
+ int startX = 20;
+ int startY = YRES-20;
+ int startAlpha;
+ if(lastLogEntry>0.1f && logEntries.size())
+ {
+ startAlpha = 2.55f*lastLogEntry;
+ deque<string>::iterator iter;
+ for(iter = logEntries.begin(); iter != logEntries.end() && startAlpha>0; iter++)
+ {
+ string message = (*iter);
+ startY -= 13;
+ g->fillrect(startX-3, startY-3, Graphics::textwidth((char*)message.c_str())+6 , 14, 0, 0, 0, 100);
+ g->drawtext(startX, startY, message.c_str(), 255, 255, 255, startAlpha);
+ startAlpha-=14;
+ }
+ }
+ }
+
+ if(recording)
+ {
+ std::stringstream sampleInfo;
+ sampleInfo << recordingIndex;
+ sampleInfo << ". \x8E REC";
+
+ int textWidth = Graphics::textwidth((char*)sampleInfo.str().c_str());
+ g->fillrect(XRES-20-textWidth, 12, textWidth+8, 15, 0, 0, 0, 255*0.5);
+ g->drawtext(XRES-16-textWidth, 16, (const char*)sampleInfo.str().c_str(), 255, 50, 20, 255);
+ }
+ else if(showHud && !introText)
+ {
+ //Draw info about simulation under cursor
+ int wavelengthGfx = 0;
+ std::stringstream sampleInfo;
+ sampleInfo.precision(2);
+ if(sample.particle.type)
+ {
+ if(showDebug)
+ {
+ int ctype = sample.particle.ctype;
+ if (sample.particle.type == PT_PIPE || sample.particle.type == PT_PPIP)
+ ctype = sample.particle.tmp;
+
+ if (sample.particle.type == PT_LAVA && ctype > 0 && ctype < PT_NUM)
+ sampleInfo << "Molten " << c->ElementResolve(ctype);
+ else if((sample.particle.type == PT_PIPE || sample.particle.type == PT_PPIP) && ctype > 0 && ctype < PT_NUM)
+ sampleInfo << c->ElementResolve(sample.particle.type) << " with " << c->ElementResolve(ctype);
+ else
+ {
+ sampleInfo << c->ElementResolve(sample.particle.type);
+ if(ctype > 0 && ctype < PT_NUM)
+ sampleInfo << " (" << c->ElementResolve(ctype) << ")";
+ else
+ sampleInfo << " ()";
+ }
+ sampleInfo << ", Temp: " << std::fixed << sample.particle.temp -273.15f;
+ sampleInfo << ", Life: " << sample.particle.life;
+ sampleInfo << ", Tmp: " << sample.particle.tmp;
+ sampleInfo << ", Pressure: " << std::fixed << sample.AirPressure;
+ }
+ else
+ {
+ if (sample.particle.type == PT_LAVA && sample.particle.ctype > 0 && sample.particle.ctype < PT_NUM)
+ sampleInfo << "Molten " << c->ElementResolve(sample.particle.ctype);
+ else if((sample.particle.type == PT_PIPE || sample.particle.type == PT_PPIP) && sample.particle.tmp > 0 && sample.particle.tmp < PT_NUM)
+ sampleInfo << c->ElementResolve(sample.particle.type) << " with " << c->ElementResolve(sample.particle.tmp);
+ else
+ sampleInfo << c->ElementResolve(sample.particle.type);
+ sampleInfo << ", Temp: " << std::fixed << sample.particle.temp -273.15f;
+ sampleInfo << ", Pressure: " << std::fixed << sample.AirPressure;
+ }
+ if(sample.particle.type == PT_PHOT)
+ wavelengthGfx = sample.particle.ctype;
+ }
+ else if (sample.WallType)
+ {
+ sampleInfo << c->WallName(sample.WallType);
+ sampleInfo << ", Pressure: " << std::fixed << sample.AirPressure;
+ }
+ else
+ {
+ sampleInfo << "Empty, Pressure: " << std::fixed << sample.AirPressure;
+ }
+
+ int textWidth = Graphics::textwidth((char*)sampleInfo.str().c_str());
+ g->fillrect(XRES-20-textWidth, 12, textWidth+8, 15, 0, 0, 0, 255*0.5);
+ g->drawtext(XRES-16-textWidth, 16, (const char*)sampleInfo.str().c_str(), 255, 255, 255, 255*0.75);
+
+#ifndef OGLI
+ if(wavelengthGfx)
+ {
+ int i, cr, cg, cb, j, h = 3, x = XRES-19-textWidth, y = 10;
+ int tmp;
+ g->fillrect(x, y, 30, h, 64, 64, 64, 255); // coords -1 size +1 to work around bug in fillrect - TODO: fix fillrect
+ for (i = 0; i < 30; i++)
+ {
+ if ((wavelengthGfx >> i)&1)
+ {
+ // Need a spread of wavelengths to get a smooth spectrum, 5 bits seems to work reasonably well
+ if (i>2) tmp = 0x1F << (i-2);
+ else tmp = 0x1F >> (2-i);
+
+ cg = 0;
+ cb = 0;
+ cr = 0;
+
+ for (j=0; j<12; j++)
+ {
+ cr += (tmp >> (j+18)) & 1;
+ cb += (tmp >> j) & 1;
+ }
+ for (j=0; j<13; j++)
+ cg += (tmp >> (j+9)) & 1;
+
+ tmp = 624/(cr+cg+cb+1);
+ cr *= tmp;
+ cg *= tmp;
+ cb *= tmp;
+ for (j=0; j<h; j++)
+ g->blendpixel(x+29-i,y+j,cr>255?255:cr,cg>255?255:cg,cb>255?255:cb,255);
+ }
+ }
+ }
+#endif
+
+ if(showDebug)
+ {
+ sampleInfo.str(std::string());
+
+ if(sample.particle.type)
+ {
+ sampleInfo << "#" << sample.ParticleID << ", ";
+ }
+ sampleInfo << "X:" << sample.PositionX << " Y:" << sample.PositionY;
+ if (std::abs(sample.Gravity) > 0.1f)
+ sampleInfo << " GX: " << sample.GravityVelocityX << " GY: " << sample.GravityVelocityY;
+
+ textWidth = Graphics::textwidth((char*)sampleInfo.str().c_str());
+ g->fillrect(XRES-20-textWidth, 26, textWidth+8, 15, 0, 0, 0, 255*0.5);
+ g->drawtext(XRES-16-textWidth, 30, (const char*)sampleInfo.str().c_str(), 255, 255, 255, 255*0.75);
+ }
+
+
+ //FPS and some version info
+#ifndef DEBUG //In debug mode, the Engine will draw FPS and other info instead
+ std::stringstream fpsInfo;
+ fpsInfo.precision(2);
+#ifdef SNAPSHOT
+ fpsInfo << "Snapshot " << SNAPSHOT_ID << ", ";
+#elif defined(BETA)
+ fpsInfo << "Beta " << SAVE_VERSION << "." << MINOR_VERSION << "." << BUILD_NUM << ", ";
+#endif
+ fpsInfo << "FPS: " << std::fixed << ui::Engine::Ref().GetFps();
+
+ if (showDebug)
+ fpsInfo << " Parts: " << sample.NumParts;
+ if (ren->GetGridSize())
+ fpsInfo << " [GRID: " << ren->GetGridSize() << "]";
+
+ textWidth = Graphics::textwidth((char*)fpsInfo.str().c_str());
+ g->fillrect(12, 12, textWidth+8, 15, 0, 0, 0, 255*0.5);
+ g->drawtext(16, 16, (const char*)fpsInfo.str().c_str(), 32, 216, 255, 255*0.75);
+#endif
+ }
+
+ //Tooltips
+ if(infoTipPresence)
+ {
+ int infoTipAlpha = (infoTipPresence>50?50:infoTipPresence)*5;
+ g->drawtext((XRES-Graphics::textwidth((char*)infoTip.c_str()))/2, (YRES/2)-2, (char*)infoTip.c_str(), 255, 255, 255, infoTipAlpha);
+ }
+
+ if(toolTipPresence && toolTipPosition.X!=-1 && toolTipPosition.Y!=-1 && toolTip.length())
+ {
+ g->drawtext(toolTipPosition.X, toolTipPosition.Y, (char*)toolTip.c_str(), 255, 255, 255, toolTipPresence>51?255:toolTipPresence*5);
+ }
+
+ if(buttonTipShow > 0)
+ {
+ g->drawtext(6, Size.Y-MENUSIZE-10, (char*)buttonTip.c_str(), 255, 255, 255, buttonTipShow>51?255:buttonTipShow*5);
+ }
+
+ //Introduction text
+ if(introText)
+ {
+ g->fillrect(0, 0, XRES+BARSIZE, YRES+MENUSIZE, 0, 0, 0, introText>51?102:introText*2);
+ g->drawtext(16, 20, (char*)introTextMessage.c_str(), 255, 255, 255, introText>51?255:introText*5);
+ }
+}
+
+ui::Point GameView::lineSnapCoords(ui::Point point1, ui::Point point2)
+{
+ ui::Point newPoint(0, 0);
+ float snapAngle = floor(atan2((float)point2.Y-point1.Y, point2.X-point1.X)/(M_PI*0.25)+0.5)*M_PI*0.25;
+ float lineMag = sqrtf(pow((float)(point2.X-point1.X),2)+pow((float)(point2.Y-point1.Y),2));
+ newPoint.X = (int)(lineMag*cos(snapAngle)+point1.X+0.5f);
+ newPoint.Y = (int)(lineMag*sin(snapAngle)+point1.Y+0.5f);
+ return newPoint;
+}
+
+ui::Point GameView::rectSnapCoords(ui::Point point1, ui::Point point2)
+{
+ ui::Point newPoint(0, 0);
+ float snapAngle = floor((atan2((float)point2.Y-point1.Y, point2.X-point1.X)+M_PI*0.25)/(M_PI*0.5)+0.5)*M_PI*0.5 - M_PI*0.25;
+ float lineMag = sqrtf(pow((float)(point2.X-point1.X),2)+pow((float)(point2.Y-point1.Y),2));
+ newPoint.X = (int)(lineMag*cos(snapAngle)+point1.X+0.5f);
+ newPoint.Y = (int)(lineMag*sin(snapAngle)+point1.Y+0.5f);
+ return newPoint;
+}
diff --git a/src/game/GameView.h b/src/game/GameView.h
new file mode 100644
index 0000000..e17279f
--- /dev/null
+++ b/src/game/GameView.h
@@ -0,0 +1,187 @@
+#ifndef GAMEVIEW_H
+#define GAMEVIEW_H
+
+#include <vector>
+#include <queue>
+#include <deque>
+#include <string>
+#include "GameController.h"
+#include "GameModel.h"
+#include "interface/Window.h"
+#include "interface/Point.h"
+#include "interface/Button.h"
+#include "interface/Slider.h"
+#include "interface/Textbox.h"
+#include "ToolButton.h"
+#include "Brush.h"
+#include "simulation/Sample.h"
+
+using namespace std;
+
+enum DrawMode
+{
+ DrawPoints, DrawLine, DrawRect, DrawFill
+};
+
+enum SelectMode
+{
+ SelectNone, SelectStamp, SelectCopy, SelectCut, PlaceSave
+};
+
+class GameController;
+class GameModel;
+class GameView: public ui::Window
+{
+private:
+ DrawMode drawMode;
+
+ bool doScreenshot;
+ bool recording;
+ int screenshotIndex;
+ int recordingIndex;
+
+ bool isMouseDown;
+ bool zoomEnabled;
+ bool zoomCursorFixed;
+ bool drawSnap;
+ bool shiftBehaviour;
+ bool ctrlBehaviour;
+ bool altBehaviour;
+ bool showHud;
+ bool showDebug;
+ bool wallBrush;
+ int introText;
+ int buttonTipShow;
+ std::string buttonTip;
+ std::string introTextMessage;
+ int toolIndex;
+ int currentSaveType;
+
+ int infoTipPresence;
+ std::string toolTip;
+ ui::Point toolTipPosition;
+ std::string infoTip;
+ int toolTipPresence;
+
+ queue<ui::Point> pointQueue;
+ GameController * c;
+ Renderer * ren;
+ Brush * activeBrush;
+ //UI Elements
+ vector<ui::Button*> quickOptionButtons;
+ vector<ui::Button*> menuButtons;
+ vector<ToolButton*> toolButtons;
+ vector<ui::Component*> notificationComponents;
+ deque<string> logEntries;
+ float lastLogEntry;
+ ui::Button * scrollBar;
+ ui::Button * searchButton;
+ ui::Button * reloadButton;
+ ui::Button * saveSimulationButton;
+ ui::Button * downVoteButton;
+ ui::Button * upVoteButton;
+ ui::Button * tagSimulationButton;
+ ui::Button * clearSimButton;
+ ui::Button * loginButton;
+ ui::Button * simulationOptionButton;
+ ui::Button * displayModeButton;
+ ui::Button * pauseButton;
+ ui::Point currentMouse;
+
+ ui::Button * colourPicker;
+ vector<ToolButton*> colourPresets;
+
+ bool drawModeReset;
+ ui::Point drawPoint1;
+ ui::Point drawPoint2;
+
+ SelectMode selectMode;
+ ui::Point selectPoint1;
+ ui::Point selectPoint2;
+
+ ui::Point mousePosition;
+
+ Thumbnail * placeSaveThumb;
+
+ SimulationSample sample;
+
+ int lastOffset;
+ void setToolButtonOffset(int offset);
+ virtual ui::Point lineSnapCoords(ui::Point point1, ui::Point point2);
+ virtual ui::Point rectSnapCoords(ui::Point point1, ui::Point point2);
+
+ void screenshot();
+ void record();
+
+ void enableShiftBehaviour();
+ void disableShiftBehaviour();
+ void enableCtrlBehaviour();
+ void disableCtrlBehaviour();
+ void enableAltBehaviour();
+ void disableAltBehaviour();
+public:
+ GameView();
+ virtual ~GameView();
+
+ //Breaks MVC, but any other way is going to be more of a mess.
+ ui::Point GetMousePosition();
+ void SetSample(SimulationSample sample);
+ bool CtrlBehaviour(){ return ctrlBehaviour; }
+ bool ShiftBehaviour(){ return shiftBehaviour; }
+ void ExitPrompt();
+ void ToggleDebug();
+ SelectMode GetSelectMode() { return selectMode; }
+ void BeginStampSelection();
+
+ void AttachController(GameController * _c){ c = _c; }
+ void NotifyRendererChanged(GameModel * sender);
+ void NotifySimulationChanged(GameModel * sender);
+ void NotifyPausedChanged(GameModel * sender);
+ void NotifySaveChanged(GameModel * sender);
+ void NotifyBrushChanged(GameModel * sender);
+ void NotifyMenuListChanged(GameModel * sender);
+ void NotifyToolListChanged(GameModel * sender);
+ void NotifyActiveToolsChanged(GameModel * sender);
+ void NotifyUserChanged(GameModel * sender);
+ void NotifyZoomChanged(GameModel * sender);
+ void NotifyColourSelectorVisibilityChanged(GameModel * sender);
+ void NotifyColourSelectorColourChanged(GameModel * sender);
+ void NotifyColourPresetsChanged(GameModel * sender);
+ void NotifyColourActivePresetChanged(GameModel * sender);
+ void NotifyPlaceSaveChanged(GameModel * sender);
+ void NotifyNotificationsChanged(GameModel * sender);
+ void NotifyLogChanged(GameModel * sender, string entry);
+ void NotifyToolTipChanged(GameModel * sender);
+ void NotifyInfoTipChanged(GameModel * sender);
+ void NotifyQuickOptionsChanged(GameModel * sender);
+ void NotifyLastToolChanged(GameModel * sender);
+
+
+ virtual void ToolTip(ui::Component * sender, ui::Point mousePosition, std::string toolTip);
+
+ virtual void OnMouseMove(int x, int y, int dx, int dy);
+ virtual void OnMouseDown(int x, int y, unsigned button);
+ virtual void OnMouseUp(int x, int y, unsigned button);
+ virtual void OnMouseWheel(int x, int y, int d);
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual void OnTick(float dt);
+ virtual void OnDraw();
+ virtual void OnBlur();
+
+ //Top-level handlers, for Lua interface
+ virtual void DoDraw();
+ virtual void DoMouseMove(int x, int y, int dx, int dy);
+ virtual void DoMouseDown(int x, int y, unsigned button);
+ virtual void DoMouseUp(int x, int y, unsigned button);
+ virtual void DoMouseWheel(int x, int y, int d);
+ virtual void DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual void DoKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+
+ class MenuAction;
+ class ToolAction;
+ class OptionAction;
+ class OptionListener;
+};
+
+#endif // GAMEVIEW_H
diff --git a/src/game/Menu.h b/src/game/Menu.h
new file mode 100644
index 0000000..261193e
--- /dev/null
+++ b/src/game/Menu.h
@@ -0,0 +1,58 @@
+/*
+ * Menu.h
+ *
+ * Created on: Jan 22, 2012
+ * Author: Simon
+ */
+
+#ifndef MENU_H_
+#define MENU_H_
+
+#include "Tool.h"
+
+class Menu
+{
+ char icon;
+ string description;
+ vector<Tool*> tools;
+public:
+ Menu(char icon_, string description_):
+ icon(icon_),
+ description(description_),
+ tools(vector<Tool*>())
+ {
+
+ }
+
+ virtual ~Menu()
+ {
+ for(int i = 0; i < tools.size(); i++)
+ {
+ delete tools[i];
+ }
+ tools.clear();
+ }
+
+ vector<Tool*> GetToolList()
+ {
+ return tools;
+ }
+
+ char GetIcon()
+ {
+ return icon;
+ }
+
+ string GetDescription()
+ {
+ return description;
+ }
+
+ void AddTool(Tool * tool_)
+ {
+ tools.push_back(tool_);
+ }
+};
+
+
+#endif /* MENU_H_ */
diff --git a/src/game/Notification.h b/src/game/Notification.h
new file mode 100644
index 0000000..4c64dea
--- /dev/null
+++ b/src/game/Notification.h
@@ -0,0 +1,23 @@
+/*
+ * Notification.h
+ *
+ * Created on: Jun 20, 2012
+ * Author: Simon
+ */
+
+#ifndef NOTIFICATION_H_
+#define NOTIFICATION_H_
+
+#include <string>
+
+class Notification
+{
+public:
+ Notification(std::string message) : Message(message) {}
+ virtual ~Notification() {};
+ std::string Message;
+
+ virtual void Action() { }
+};
+
+#endif /* NOTIFICATION_H_ */
diff --git a/src/game/PropertyTool.cpp b/src/game/PropertyTool.cpp
new file mode 100644
index 0000000..ee9f9eb
--- /dev/null
+++ b/src/game/PropertyTool.cpp
@@ -0,0 +1,235 @@
+#include <iostream>
+#include <sstream>
+#include "Style.h"
+#include "simulation/Simulation.h"
+#include "Tool.h"
+#include "interface/Window.h"
+#include "interface/Button.h"
+#include "interface/Label.h"
+#include "interface/Textbox.h"
+#include "interface/DropDown.h"
+#include "interface/Keys.h"
+#include "dialogues/ErrorMessage.h"
+
+class PropertyWindow: public ui::Window
+{
+public:
+ ui::DropDown * property;
+ ui::Textbox * textField;
+ SignTool * tool;
+ Simulation * sim;
+ int signID;
+ ui::Point position;
+ std::vector<StructProperty> properties;
+ PropertyWindow(PropertyTool * tool_, Simulation * sim_, ui::Point position_);
+ void SetProperty();
+ virtual void OnDraw();
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual void OnTryExit(ExitMethod method);
+ virtual ~PropertyWindow() {}
+ class OkayAction: public ui::ButtonAction
+ {
+ public:
+ PropertyWindow * prompt;
+ OkayAction(PropertyWindow * prompt_) { prompt = prompt_; }
+ void ActionCallback(ui::Button * sender)
+ {
+ ui::Engine::Ref().CloseWindow();
+ if(prompt->textField->GetText().length())
+ prompt->SetProperty();
+ prompt->SelfDestruct();
+ return;
+ }
+ };
+};
+
+PropertyWindow::PropertyWindow(PropertyTool * tool_, Simulation * sim_, ui::Point position_):
+ui::Window(ui::Point(-1, -1), ui::Point(200, 87)),
+sim(sim_),
+position(position_)
+{
+ properties = Particle::GetProperties();
+
+ ui::Label * messageLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 14), "Edit property");
+ messageLabel->SetTextColour(style::Colour::InformationTitle);
+ messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+ AddComponent(messageLabel);
+
+ ui::Button * okayButton = new ui::Button(ui::Point(0, Size.Y-17), ui::Point(Size.X, 17), "OK");
+ okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ okayButton->Appearance.BorderInactive = ui::Colour(200, 200, 200);
+ okayButton->SetActionCallback(new OkayAction(this));
+ AddComponent(okayButton);
+ SetOkayButton(okayButton);
+
+ class PropertyChanged: public ui::DropDownAction
+ {
+ PropertyWindow * w;
+ public:
+ PropertyChanged(PropertyWindow * w): w(w) { }
+ virtual void OptionChanged(ui::DropDown * sender, std::pair<std::string, int> option)
+ {
+ w->FocusComponent(w->textField);
+ }
+ };
+ property = new ui::DropDown(ui::Point(8, 25), ui::Point(Size.X-16, 17));
+ property->SetActionCallback(new PropertyChanged(this));
+ AddComponent(property);
+ for(int i = 0; i < properties.size(); i++)
+ {
+ property->AddOption(std::pair<std::string, int>(properties[i].Name, i));
+ }
+ property->SetOption(0);
+
+ textField = new ui::Textbox(ui::Point(8, 46), ui::Point(Size.X-16, 16), "", "[value]");
+ textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(textField);
+ FocusComponent(textField);
+
+ ui::Engine::Ref().ShowWindow(this);
+}
+void PropertyWindow::SetProperty()
+{
+ if(property->GetOption().second!=-1 && textField->GetText().length() > 0)
+ {
+ void * propValue;
+ int tempInt;
+ unsigned int tempUInt;
+ float tempFloat;
+ std::string value = textField->GetText();
+ try {
+ switch(properties[property->GetOption().second].Type)
+ {
+ case StructProperty::Integer:
+ case StructProperty::ParticleType:
+ if(value.length() > 2 && value.substr(0, 2) == "0x")
+ {
+ //0xC0FFEE
+ std::stringstream buffer;
+ buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit);
+ buffer << std::hex << value.substr(2);
+ buffer >> tempInt;
+ }
+ else if(value.length() > 1 && value[0] == '#')
+ {
+ //#C0FFEE
+ std::stringstream buffer;
+ buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit);
+ buffer << std::hex << value.substr(1);
+ buffer >> tempInt;
+ }
+ else
+ {
+ if(properties[property->GetOption().second].Type == StructProperty::ParticleType)
+ {
+ int type = sim->GetParticleType(value);
+ if(type != -1)
+ {
+#ifdef DEBUG
+ std::cout << "Got type from particle name" << std::endl;
+#endif
+ tempInt = type;
+ }
+ else
+ {
+ std::stringstream buffer(value);
+ buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit);
+ buffer >> tempInt;
+ }
+ }
+ else
+ {
+ std::stringstream buffer(value);
+ buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit);
+ buffer >> tempInt;
+ }
+ }
+#ifdef DEBUG
+ std::cout << "Got int value " << tempInt << std::endl;
+#endif
+ propValue = &tempInt;
+ break;
+ case StructProperty::UInteger:
+ if(value.length() > 2 && value.substr(0, 2) == "0x")
+ {
+ //0xC0FFEE
+ std::stringstream buffer;
+ buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit);
+ buffer << std::hex << value.substr(2);
+ buffer >> tempUInt;
+ }
+ else if(value.length() > 1 && value[0] == '#')
+ {
+ //#C0FFEE
+ std::stringstream buffer;
+ buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit);
+ buffer << std::hex << value.substr(1);
+ buffer >> tempUInt;
+ }
+ else
+ {
+ std::stringstream buffer(value);
+ buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit);
+ buffer >> tempUInt;
+ }
+#ifdef DEBUG
+ std::cout << "Got uint value " << tempUInt << std::endl;
+#endif
+ propValue = &tempUInt;
+ break;
+ case StructProperty::Float:
+ {
+ std::stringstream buffer(value);
+ buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit);
+ buffer >> tempFloat;
+#ifdef DEBUG
+ std::cout << "Got float value " << tempFloat << std::endl;
+#endif
+ propValue = &tempFloat;
+ }
+ break;
+ default:
+ new ErrorMessage("Could not set property", "Invalid property");
+ }
+ sim->flood_prop(
+ position.X,
+ position.Y,
+ properties[property->GetOption().second].Offset,
+ propValue,
+ properties[property->GetOption().second].Type
+ );
+ } catch (const std::exception& ex) {
+ new ErrorMessage("Could not set property", "Invalid value provided");
+ }
+ }
+}
+
+void PropertyWindow::OnTryExit(ExitMethod method)
+{
+ ui::Engine::Ref().CloseWindow();
+ SelfDestruct();
+}
+
+void PropertyWindow::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255);
+}
+
+void PropertyWindow::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if (key == KEY_UP)
+ property->SetOption(property->GetOption().second-1);
+ else if (key == KEY_DOWN)
+ property->SetOption(property->GetOption().second+1);
+}
+
+void PropertyTool::Click(Simulation * sim, Brush * brush, ui::Point position)
+{
+ new PropertyWindow(this, sim, position);
+} \ No newline at end of file
diff --git a/src/game/QuickOption.h b/src/game/QuickOption.h
new file mode 100644
index 0000000..4bf27ff
--- /dev/null
+++ b/src/game/QuickOption.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+class GameModel;
+class QuickOption;
+class QuickOptionListener
+{
+protected:
+ QuickOptionListener() {}
+public:
+ virtual ~QuickOptionListener() {}
+ virtual void OnValueChanged(QuickOption * sender) {}
+};
+class QuickOption
+{
+public:
+ enum Type {
+ Toggle, Multi
+ };
+protected:
+ std::vector<QuickOptionListener*> listeners;
+ GameModel * m;
+ Type type;
+ std::string icon;
+ std::string description;
+ QuickOption(std::string icon, std::string description, GameModel * m, Type type) :
+ icon(icon),
+ description(description),
+ m(m),
+ type(type)
+ {
+
+ }
+ virtual void perform() {}
+public:
+ virtual ~QuickOption()
+ {
+ //for(std::vector<QuickOptionListener*>::iterator iter = listeners.begin(), end = listeners.end(); iter != end; ++iter)
+ // delete *iter;
+ }
+
+ std::vector<QuickOptionListener*> GetListeners()
+ {
+ return listeners;
+ }
+
+ void AddListener(QuickOptionListener * listener)
+ {
+ listeners.push_back(listener);
+ }
+
+ Type GetType() { return type; }
+
+ virtual bool GetToggle() { return true;}
+ virtual int GetMutli() { return 0;}
+ virtual int GetMultiCount() { return 0;}
+
+ std::string GetIcon() { return icon; }
+ void SetIcon(std::string icon) { this->icon = icon; }
+ std::string GetDescription() { return description; }
+ void SetDescription(std::string description) { this->description = description; }
+ void Perform()
+ {
+ perform();
+ for(std::vector<QuickOptionListener*>::iterator iter = listeners.begin(), end = listeners.end(); iter != end; ++iter)
+ (*iter)->OnValueChanged(this);
+ }
+ void Update()
+ {
+ for(std::vector<QuickOptionListener*>::iterator iter = listeners.begin(), end = listeners.end(); iter != end; ++iter)
+ (*iter)->OnValueChanged(this);
+ }
+};
+
diff --git a/src/game/QuickOptions.h b/src/game/QuickOptions.h
new file mode 100644
index 0000000..7c31f9c
--- /dev/null
+++ b/src/game/QuickOptions.h
@@ -0,0 +1,120 @@
+#include "QuickOption.h"
+#include "GameModel.h"
+
+class SandEffectOption: public QuickOption
+{
+public:
+ SandEffectOption(GameModel * m):
+ QuickOption("P", "Sand effect", m, Toggle)
+ {
+
+ }
+ virtual bool GetToggle()
+ {
+ return m->GetSimulation()->pretty_powder;
+ }
+ virtual void perform()
+ {
+ m->GetSimulation()->pretty_powder = !m->GetSimulation()->pretty_powder;
+ }
+};
+
+class DrawGravOption: public QuickOption
+{
+public:
+ DrawGravOption(GameModel * m):
+ QuickOption("G", "Draw gravity field", m, Toggle)
+ {
+
+ }
+ virtual bool GetToggle()
+ {
+ return m->GetGravityGrid();
+ }
+ virtual void perform()
+ {
+ m->ShowGravityGrid(!m->GetGravityGrid());
+ }
+};
+
+class DecorationsOption: public QuickOption
+{
+public:
+ DecorationsOption(GameModel * m):
+ QuickOption("D", "Draw decorations", m, Toggle)
+ {
+
+ }
+ virtual bool GetToggle()
+ {
+ return m->GetDecoration();
+ }
+ virtual void perform()
+ {
+ m->SetDecoration(!m->GetDecoration());
+ }
+};
+
+class NGravityOption: public QuickOption
+{
+public:
+ NGravityOption(GameModel * m):
+ QuickOption("N", "Newtonian Gravity", m, Toggle)
+ {
+
+ }
+ virtual bool GetToggle()
+ {
+ return m->GetSimulation()->grav->ngrav_enable;
+ }
+ virtual void perform()
+ {
+ if(m->GetSimulation()->grav->ngrav_enable)
+ {
+ m->GetSimulation()->grav->stop_grav_async();
+ m->SetInfoTip("Newtonian Gravity: Off");
+ }
+ else
+ {
+ m->GetSimulation()->grav->start_grav_async();
+ m->SetInfoTip("Newtonian Gravity: On");
+ }
+ }
+};
+
+class AHeatOption: public QuickOption
+{
+public:
+ AHeatOption(GameModel * m):
+ QuickOption("A", "Ambient heat", m, Toggle)
+ {
+
+ }
+ virtual bool GetToggle()
+ {
+ return m->GetAHeatEnable();
+ }
+ virtual void perform()
+ {
+ m->SetAHeatEnable(!m->GetAHeatEnable());
+ }
+};
+
+class ConsoleShowOption: public QuickOption
+{
+ GameController * c;
+public:
+ ConsoleShowOption(GameModel * m, GameController * c_):
+ QuickOption("C", "Show Console", m, Toggle)
+ {
+ c = c_;
+ }
+ virtual bool GetToggle()
+ {
+ return 0;
+ }
+ virtual void perform()
+ {
+ c->ShowConsole();
+ }
+};
diff --git a/src/game/RenderPreset.h b/src/game/RenderPreset.h
new file mode 100644
index 0000000..9cc9f4c
--- /dev/null
+++ b/src/game/RenderPreset.h
@@ -0,0 +1,19 @@
+#ifndef RENDER_PRESET_H
+#define RENDER_PRESET_H
+class RenderPreset
+{
+public:
+ std::string Name;
+ std::vector<unsigned int> RenderModes;
+ std::vector<unsigned int> DisplayModes;
+ unsigned int ColourMode;
+
+ RenderPreset(): Name(""), ColourMode(0) {}
+ RenderPreset(std::string name, std::vector<unsigned int> renderModes, std::vector<unsigned int> displayModes, unsigned int colourMode):
+ Name(name),
+ RenderModes(renderModes),
+ DisplayModes(displayModes),
+ ColourMode(colourMode)
+ {}
+};
+#endif \ No newline at end of file
diff --git a/src/game/SampleTool.cpp b/src/game/SampleTool.cpp
new file mode 100644
index 0000000..296167b
--- /dev/null
+++ b/src/game/SampleTool.cpp
@@ -0,0 +1,67 @@
+#include <iostream>
+#include "graphics/Graphics.h"
+#include "Tool.h"
+#include "GameModel.h"
+#include "interface/Colour.h"
+
+VideoBuffer * SampleTool::GetIcon(int toolID, int width, int height)
+{
+ VideoBuffer * newTexture = new VideoBuffer(width, height);
+ for (int y=0; y<height; y++)
+ {
+ for (int x=0; x<width; x++)
+ {
+ pixel pc = x==0||x==width-1||y==0||y==height-1 ? PIXPACK(0xA0A0A0) : PIXPACK(0x000000);
+ newTexture->SetPixel(x, y, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ }
+ newTexture->SetCharacter((width/2)-5, (height/2)-5, 0xE6, 255, 255, 255, 255);
+ newTexture->BlendCharacter((width/2)-5, (height/2)-5, 0xE7, 100, 180, 255, 255);
+ return newTexture;
+}
+
+void SampleTool::Draw(Simulation * sim, Brush * brush, ui::Point position)
+{
+ if(gameModel->GetColourSelectorVisibility())
+ {
+ pixel colour = gameModel->GetRenderer()->GetPixel(position.X, position.Y);
+ gameModel->SetColourSelectorColour(ui::Colour(PIXR(colour), PIXG(colour), PIXB(colour), 255));
+ }
+ else
+ {
+ int particleType = 0;
+ int particleCtype = 0;
+ if(sim->pmap[position.Y][position.X])
+ {
+ particleType = sim->parts[sim->pmap[position.Y][position.X]>>8].type;
+ particleCtype = sim->parts[sim->pmap[position.Y][position.X]>>8].ctype;
+ }
+ else if(sim->photons[position.Y][position.X])
+ {
+ particleType = sim->parts[sim->photons[position.Y][position.X]>>8].type;
+ particleCtype = sim->parts[sim->pmap[position.Y][position.X]>>8].ctype;
+ }
+
+ if(particleType)
+ {
+ if(particleType == PT_LIFE)
+ {
+ Menu * lifeMenu = gameModel->GetMenuList()[SC_LIFE];
+ std::vector<Tool*> elementTools = lifeMenu->GetToolList();
+
+ for(std::vector<Tool*>::iterator iter = elementTools.begin(), end = elementTools.end(); iter != end; ++iter)
+ {
+ Tool * elementTool = *iter;
+ if(elementTool && elementTool->GetToolID() == particleCtype)
+ gameModel->SetActiveTool(0, elementTool);
+ }
+ }
+ else
+ {
+ Tool * elementTool = gameModel->GetElementTool(particleType);
+ if(elementTool)
+ gameModel->SetActiveTool(0, elementTool);
+ }
+ }
+ }
+}
diff --git a/src/game/SignTool.cpp b/src/game/SignTool.cpp
new file mode 100644
index 0000000..af267af
--- /dev/null
+++ b/src/game/SignTool.cpp
@@ -0,0 +1,289 @@
+#include <iostream>
+#include "Style.h"
+#include "simulation/Simulation.h"
+#include "Tool.h"
+#include "interface/Window.h"
+#include "interface/Button.h"
+#include "interface/Label.h"
+#include "interface/Textbox.h"
+#include "interface/DropDown.h"
+
+class SignWindow: public ui::Window
+{
+public:
+ ui::DropDown * justification;
+ ui::Textbox * textField;
+ SignTool * tool;
+ sign * movingSign;
+ bool signMoving;
+ Simulation * sim;
+ int signID;
+ ui::Point signPosition;
+ SignWindow(SignTool * tool_, Simulation * sim_, int signID_, ui::Point position_);
+ virtual void OnDraw();
+ virtual void DoDraw();
+ virtual void DoMouseMove(int x, int y, int dx, int dy);
+ virtual void DoMouseDown(int x, int y, unsigned button);
+ virtual void DoMouseUp(int x, int y, unsigned button) { if(!signMoving) ui::Window::DoMouseUp(x, y, button); }
+ virtual void DoMouseWheel(int x, int y, int d) { if(!signMoving) ui::Window::DoMouseWheel(x, y, d); }
+ virtual void DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) { if(!signMoving) ui::Window::DoKeyPress(key, character, shift, ctrl, alt); };
+ virtual void DoKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) { if(!signMoving) ui::Window::DoKeyRelease(key, character, shift, ctrl, alt); };
+ virtual ~SignWindow() {}
+ virtual void OnTryExit(ui::Window::ExitMethod method);
+ class OkayAction: public ui::ButtonAction
+ {
+ public:
+ SignWindow * prompt;
+ OkayAction(SignWindow * prompt_) { prompt = prompt_; }
+ void ActionCallback(ui::Button * sender)
+ {
+ ui::Engine::Ref().CloseWindow();
+ if(prompt->signID==-1 && prompt->textField->GetText().length())
+ {
+ prompt->sim->signs.push_back(sign(prompt->textField->GetText(), prompt->signPosition.X, prompt->signPosition.Y, (sign::Justification)prompt->justification->GetOption().second));
+ }
+ else if(prompt->signID!=-1 && prompt->textField->GetText().length())
+ {
+ prompt->sim->signs[prompt->signID] = sign(sign(prompt->textField->GetText(), prompt->signPosition.X, prompt->signPosition.Y, (sign::Justification)prompt->justification->GetOption().second));
+ }
+ prompt->SelfDestruct();
+ }
+ };
+ class DeleteAction: public ui::ButtonAction
+ {
+ public:
+ SignWindow * prompt;
+ DeleteAction(SignWindow * prompt_) { prompt = prompt_; }
+ void ActionCallback(ui::Button * sender)
+ {
+ ui::Engine::Ref().CloseWindow();
+ if(prompt->signID!=-1)
+ {
+ prompt->sim->signs.erase(prompt->sim->signs.begin()+prompt->signID);
+ }
+ prompt->SelfDestruct();
+ }
+ };
+
+ class SignTextAction: public ui::TextboxAction
+ {
+ public:
+ SignWindow * prompt;
+ SignTextAction(SignWindow * prompt_) { prompt = prompt_; }
+ virtual void TextChangedCallback(ui::Textbox * sender)
+ {
+ if(prompt->signID!=-1)
+ {
+ prompt->sim->signs[prompt->signID].text = sender->GetText();
+ }
+ }
+ };
+
+ class MoveAction: public ui::ButtonAction
+ {
+ public:
+ SignWindow * prompt;
+ MoveAction(SignWindow * prompt_) { prompt = prompt_; }
+ void ActionCallback(ui::Button * sender)
+ {
+ if(prompt->signID!=-1)
+ {
+ prompt->movingSign = &prompt->sim->signs[prompt->signID];
+ prompt->signMoving = true;
+ }
+ }
+ };
+};
+
+SignWindow::SignWindow(SignTool * tool_, Simulation * sim_, int signID_, ui::Point position_):
+ ui::Window(ui::Point(-1, -1), ui::Point(200, 87)),
+ tool(tool_),
+ signID(signID_),
+ sim(sim_),
+ signPosition(position_),
+ movingSign(NULL),
+ signMoving(false)
+{
+ ui::Label * messageLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 15), "New sign");
+ messageLabel->SetTextColour(style::Colour::InformationTitle);
+ messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(messageLabel);
+
+ ui::Button * okayButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "OK");
+ okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ okayButton->Appearance.BorderInactive = (ui::Colour(200, 200, 200));
+ okayButton->SetActionCallback(new OkayAction(this));
+ AddComponent(okayButton);
+ SetOkayButton(okayButton);
+
+ ui::Label * tempLabel = new ui::Label(ui::Point(8, 48), ui::Point(40, 15), "Justify:");
+ okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+
+ justification = new ui::DropDown(ui::Point(52, 48), ui::Point(50, 16));
+ AddComponent(justification);
+ justification->AddOption(std::pair<std::string, int>("\x9D Left", (int)sign::Left));
+ justification->AddOption(std::pair<std::string, int>("\x9E Centre", (int)sign::Centre));
+ justification->AddOption(std::pair<std::string, int>("\x9F Right", (int)sign::Right));
+ justification->SetOption(1);
+ justification->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+
+ textField = new ui::Textbox(ui::Point(8, 25), ui::Point(Size.X-16, 17), "", "[message]");
+ textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ textField->SetActionCallback(new SignTextAction(this));
+ AddComponent(textField);
+ FocusComponent(textField);
+
+ if(signID!=-1)
+ {
+ messageLabel->SetText("Edit sign");
+
+ textField->SetText(sim->signs[signID].text);
+ justification->SetOption(sim->signs[signID].ju);
+
+ ui::Point position = ui::Point(justification->Position.X+justification->Size.X+3, 48);
+ ui::Button * moveButton = new ui::Button(position, ui::Point(((Size.X-position.X-8)/2)-2, 16), "Move");
+ moveButton->SetActionCallback(new MoveAction(this));
+ AddComponent(moveButton);
+
+ position = ui::Point(justification->Position.X+justification->Size.X+3, 48)+ui::Point(moveButton->Size.X+3, 0);
+ ui::Button * deleteButton = new ui::Button(position, ui::Point((Size.X-position.X-8)-1, 16), "Delete");
+ //deleteButton->SetIcon(IconDelete);
+ deleteButton->SetActionCallback(new DeleteAction(this));
+
+ signPosition.X = sim->signs[signID].x;
+ signPosition.Y = sim->signs[signID].y;
+
+ AddComponent(deleteButton);
+ }
+
+ ui::Engine::Ref().ShowWindow(this);
+}
+
+void SignWindow::OnTryExit(ui::Window::ExitMethod method)
+{
+ ui::Engine::Ref().CloseWindow();
+ SelfDestruct();
+}
+
+void SignWindow::DoDraw()
+{
+ for(std::vector<sign>::iterator iter = sim->signs.begin(), end = sim->signs.end(); iter != end; ++iter)
+ {
+ sign & currentSign = *iter;
+ int x, y, w, h;
+ Graphics * g = ui::Engine::Ref().g;
+ char buff[256]; //Buffer
+ currentSign.pos(x, y, w, h);
+ g->clearrect(x, y, w, h);
+ g->drawrect(x, y, w, h, 192, 192, 192, 255);
+
+ //Displaying special information
+ if (currentSign.text == "{p}")
+ {
+ float pressure = 0.0f;
+ if (currentSign.x>=0 && currentSign.x<XRES && currentSign.y>=0 && currentSign.y<YRES)
+ pressure = sim->pv[currentSign.y/CELL][currentSign.x/CELL];
+ sprintf(buff, "Pressure: %3.2f", pressure); //...pressure
+ g->drawtext(x+3, y+3, buff, 255, 255, 255, 255);
+ }
+ else if (currentSign.text == "{t}")
+ {
+ if (currentSign.x>=0 && currentSign.x<XRES && currentSign.y>=0 && currentSign.y<YRES && sim->pmap[currentSign.y][currentSign.x])
+ sprintf(buff, "Temp: %4.2f", sim->parts[sim->pmap[currentSign.y][currentSign.x]>>8].temp-273.15); //...temperature
+ else
+ sprintf(buff, "Temp: 0.00"); //...temperature
+ g->drawtext(x+3, y+3, buff, 255, 255, 255, 255);
+ }
+ else if (sregexp(currentSign.text.c_str(), "^{[c|t]:[0-9]*|.*}$")==0)
+ {
+ int sldr, startm;
+ memset(buff, 0, sizeof(buff));
+ for (sldr=3; currentSign.text[sldr-1] != '|'; sldr++)
+ startm = sldr + 1;
+ sldr = startm;
+ while (currentSign.text[sldr] != '}')
+ {
+ buff[sldr - startm] = currentSign.text[sldr];
+ sldr++;
+ }
+ g->drawtext(x+3, y+3, buff, 0, 191, 255, 255);
+ }
+ else
+ {
+ g->drawtext(x+3, y+3, currentSign.text, 255, 255, 255, 255);
+ }
+ }
+ if(!signMoving)
+ {
+ ui::Window::DoDraw();
+ }
+}
+
+void SignWindow::DoMouseMove(int x, int y, int dx, int dy) {
+ if(!signMoving)
+ ui::Window::DoMouseMove(x, y, dx, dy);
+ else
+ {
+ if(x < XRES && y < YRES)
+ {
+ movingSign->x = x;
+ movingSign->y = y;
+ signPosition.X = x;
+ signPosition.Y = y;
+ }
+ }
+}
+
+void SignWindow::DoMouseDown(int x, int y, unsigned button)
+{
+ if(!signMoving)
+ ui::Window::DoMouseDown(x, y, button);
+ else
+ {
+ signMoving = false;
+ }
+}
+
+void SignWindow::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255);
+}
+
+VideoBuffer * SignTool::GetIcon(int toolID, int width, int height)
+{
+ VideoBuffer * newTexture = new VideoBuffer(width, height);
+ for (int y=0; y<height; y++)
+ {
+ for (int x=0; x<width; x++)
+ {
+ pixel pc = x==0||x==width-1||y==0||y==height-1 ? PIXPACK(0xA0A0A0) : PIXPACK(0x000000);
+ newTexture->SetPixel(x, y, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ }
+ newTexture->SetCharacter((width/2)-5, (height/2)-5, 0xA1, 32, 64, 128, 255);
+ newTexture->BlendCharacter((width/2)-5, (height/2)-5, 0xA0, 255, 255, 255, 255);
+ return newTexture;
+}
+
+void SignTool::Click(Simulation * sim, Brush * brush, ui::Point position)
+{
+ int signX, signY, signW, signH, signIndex = -1;
+ for(int i = 0; i < sim->signs.size(); i++){
+ sim->signs[i].pos(signX, signY, signW, signH);
+ if(position.X > signX && position.X < signX+signW && position.Y > signY && position.Y < signY+signH)
+ {
+ signIndex = i;
+ break;
+ }
+ }
+ if (signIndex != -1 || sim->signs.size() < MAXSIGNS)
+ new SignWindow(this, sim, signIndex, position);
+}
diff --git a/src/game/Tool.cpp b/src/game/Tool.cpp
new file mode 100644
index 0000000..7fa8e15
--- /dev/null
+++ b/src/game/Tool.cpp
@@ -0,0 +1,216 @@
+/*
+ * Tool.cpp
+ *
+ * Created on: Jun 24, 2012
+ * Author: Simon
+ */
+
+#include <string>
+#include "Tool.h"
+#include "game/Brush.h"
+
+#include "simulation/Simulation.h"
+
+using namespace std;
+
+Tool::Tool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)):
+ toolID(id),
+ toolName(name),
+ toolDescription(description),
+ colRed(r),
+ colGreen(g),
+ colBlue(b),
+ textureGen(textureGen),
+ strength(1.0f),
+ resolution(1),
+ identifier(identifier)
+{
+}
+VideoBuffer * Tool::GetTexture(int width, int height)
+{
+ if(textureGen)
+ {
+ return textureGen(toolID, width, height);
+ }
+ return NULL;
+}
+void Tool::SetTextureGen(VideoBuffer * (*textureGen)(int, int, int))
+{
+ this->textureGen = textureGen;
+}
+std::string Tool::GetIdentifier() { return identifier; }
+string Tool::GetName() { return toolName; }
+string Tool::GetDescription() { return toolDescription; }
+Tool::~Tool() {}
+void Tool::Click(Simulation * sim, Brush * brush, ui::Point position) { }
+void Tool::Draw(Simulation * sim, Brush * brush, ui::Point position) {
+ sim->ToolBrush(position.X, position.Y, toolID, brush, strength);
+}
+void Tool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) {
+ sim->ToolLine(position1.X, position1.Y, position2.X, position2.Y, toolID, brush, strength);
+}
+void Tool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) {
+ sim->ToolBox(position1.X, position1.Y, position2.X, position2.Y, toolID, brush, strength);
+}
+void Tool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) {};
+
+ElementTool::ElementTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)):
+ Tool(id, name, description, r, g, b, identifier, textureGen)
+{
+}
+ElementTool::~ElementTool() {}
+void ElementTool::Draw(Simulation * sim, Brush * brush, ui::Point position){
+ sim->CreateParts(position.X, position.Y, toolID, brush);
+}
+void ElementTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) {
+ sim->CreateLine(position1.X, position1.Y, position2.X, position2.Y, toolID, brush);
+}
+void ElementTool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) {
+ sim->CreateBox(position1.X, position1.Y, position2.X, position2.Y, toolID, 0);
+}
+void ElementTool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) {
+ sim->FloodParts(position.X, position.Y, toolID, -1, -1, 0);
+}
+
+
+WallTool::WallTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)):
+Tool(id, name, description, r, g, b, identifier, textureGen)
+{
+ resolution = CELL;
+}
+WallTool::~WallTool() {}
+void WallTool::Draw(Simulation * sim, Brush * brush, ui::Point position){
+ sim->CreateWalls(position.X, position.Y, 1, 1, toolID, 0, brush);
+}
+void WallTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) {
+ int wallX = position1.X/CELL;
+ int wallY = position1.Y/CELL;
+ if(dragging == false && toolID == WL_FAN && sim->bmap[wallY][wallX]==WL_FAN)
+ {
+ float newFanVelX = (position2.X-position1.X)*0.005f;
+ newFanVelX *= strength;
+ float newFanVelY = (position2.Y-position1.Y)*0.005f;
+ newFanVelY *= strength;
+ sim->FloodWalls(position1.X, position1.Y, WL_FLOODHELPER, -1, WL_FAN, 0);
+ for (int j = 0; j < YRES/CELL; j++)
+ for (int i = 0; i < XRES/CELL; i++)
+ if (sim->bmap[j][i] == WL_FLOODHELPER)
+ {
+ sim->fvx[j][i] = newFanVelX;
+ sim->fvy[j][i] = newFanVelY;
+ sim->bmap[j][i] = WL_FAN;
+ }
+ }
+ else
+ {
+ sim->CreateWallLine(position1.X, position1.Y, position2.X, position2.Y, 1, 1, toolID, 0, brush);
+ }
+}
+void WallTool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) {
+ sim->CreateWallBox(position1.X, position1.Y, position2.X, position2.Y, toolID, 0);
+}
+void WallTool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) {
+ sim->FloodWalls(position.X, position.Y, toolID, -1, -1, 0);
+}
+
+
+GolTool::GolTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)):
+ Tool(id, name, description, r, g, b, identifier, textureGen)
+{
+}
+GolTool::~GolTool() {}
+void GolTool::Draw(Simulation * sim, Brush * brush, ui::Point position){
+ sim->CreateParts(position.X, position.Y, PT_LIFE|(toolID<<8), brush);
+}
+void GolTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) {
+ sim->CreateLine(position1.X, position1.Y, position2.X, position2.Y, PT_LIFE|(toolID<<8), brush);
+}
+void GolTool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) {
+ sim->CreateBox(position1.X, position1.Y, position2.X, position2.Y, PT_LIFE|(toolID<<8), 0);
+}
+void GolTool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) {
+ sim->FloodParts(position.X, position.Y, PT_LIFE|(toolID<<8), -1, -1, 0);
+}
+
+WindTool::WindTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)):
+ Tool(id, name, description, r, g, b, identifier, textureGen)
+{
+}
+WindTool::~WindTool() {}
+void WindTool::Draw(Simulation * sim, Brush * brush, ui::Point position)
+{
+
+}
+void WindTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging)
+{
+ int radiusX, radiusY, sizeX, sizeY;
+
+ float strength = dragging?0.01f:0.002f;
+ strength *= this->strength;
+
+ radiusX = brush->GetRadius().X;
+ radiusY = brush->GetRadius().Y;
+
+ sizeX = brush->GetSize().X;
+ sizeY = brush->GetSize().Y;
+
+ unsigned char *bitmap = brush->GetBitmap();
+
+ for(int y = 0; y < sizeY; y++)
+ {
+ for(int x = 0; x < sizeX; x++)
+ {
+ if(bitmap[(y*sizeX)+x] && (position1.X+(x-radiusX) >= 0 && position1.Y+(y-radiusY) >= 0 && position1.X+(x-radiusX) < XRES && position1.Y+(y-radiusY) < YRES))
+ {
+ sim->vx[(position1.Y+(y-radiusY))/CELL][(position1.X+(x-radiusX))/CELL] += (position2.X-position1.X)*strength;
+ sim->vy[(position1.Y+(y-radiusY))/CELL][(position1.X+(x-radiusX))/CELL] += (position2.Y-position1.Y)*strength;
+ }
+ }
+ }
+}
+void WindTool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) {}
+
+void WindTool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) {}
+
+
+void Element_LIGH_Tool::Draw(Simulation * sim, Brush * brush, ui::Point position)
+{
+ if(sim->currentTick >= nextUse)
+ {
+ int p = sim->create_part(-2, position.X, position.Y, toolID);
+ if (p != -1)
+ {
+ sim->parts[p].life = brush->GetRadius().X+brush->GetRadius().Y;
+ if (sim->parts[p].life > 55)
+ sim->parts[p].life = 55;
+ sim->parts[p].temp = sim->parts[p].life*150; // temperature of the lighting shows the power of the lighting
+ nextUse = sim->currentTick+sim->parts[p].life/4;
+ }
+ }
+}
+
+Element_TESC_Tool::Element_TESC_Tool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)):
+ ElementTool(id, name, description, r, g, b, identifier, textureGen)
+ {
+ }
+void Element_TESC_Tool::Draw(Simulation * sim, Brush * brush, ui::Point position){
+ int radiusInfo = brush->GetRadius().X*4+brush->GetRadius().Y*4+7;
+ sim->CreateParts(position.X, position.Y, toolID | (radiusInfo << 8), brush);
+}
+void Element_TESC_Tool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) {
+ int radiusInfo = brush->GetRadius().X*4+brush->GetRadius().Y*4+7;
+ sim->CreateLine(position1.X, position1.Y, position2.X, position2.Y, toolID | (radiusInfo << 8), brush);
+}
+void Element_TESC_Tool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) {
+ int radiusInfo = brush->GetRadius().X*4+brush->GetRadius().Y*4+7;
+ sim->CreateBox(position1.X, position1.Y, position2.X, position2.Y, toolID | (radiusInfo << 8), 0);
+}
+void Element_TESC_Tool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) {
+ int radiusInfo = brush->GetRadius().X*4+brush->GetRadius().Y*4+7;
+ sim->FloodParts(position.X, position.Y, toolID | (radiusInfo << 8), -1, -1, 0);
+}
+
+void PlopTool::Click(Simulation * sim, Brush * brush, ui::Point position)
+{
+ sim->create_part(-1, position.X, position.Y, toolID);
+} \ No newline at end of file
diff --git a/src/game/Tool.h b/src/game/Tool.h
new file mode 100644
index 0000000..22898c7
--- /dev/null
+++ b/src/game/Tool.h
@@ -0,0 +1,190 @@
+/*
+ * Tool.h
+ *
+ * Created on: Jan 22, 2012
+ * Author: Simon
+ */
+
+#ifndef TOOL_H_
+#define TOOL_H_
+
+#include <iostream>
+
+using namespace std;
+
+#include "interface/Point.h"
+
+class Simulation;
+class Brush;
+class VideoBuffer;
+
+class Tool
+{
+protected:
+ VideoBuffer * (*textureGen)(int, int, int);
+ int toolID;
+ string toolName;
+ string toolDescription;
+ float strength;
+ int resolution;
+ std::string identifier;
+public:
+ Tool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL);
+ int GetToolID() { return toolID; }
+ string GetName();
+ string GetDescription();
+ std::string GetIdentifier();
+ int GetResolution() { return resolution; }
+ void SetStrength(float value) { strength = value; }
+ float GetStrength() { return strength; }
+ VideoBuffer * GetTexture(int width, int height);
+ void SetTextureGen(VideoBuffer * (*textureGen)(int, int, int));
+ virtual ~Tool();
+ virtual void Click(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false);
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2);
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position);
+ int colRed, colBlue, colGreen;
+};
+
+class SignTool: public Tool
+{
+public:
+ SignTool():
+ Tool(0, "SIGN", "Sign. Click a sign to edit or anywhere else to create a new one", 0, 0, 0, "DEFAULT_UI_SIGN", SignTool::GetIcon)
+ {
+ }
+ static VideoBuffer * GetIcon(int toolID, int width, int height);
+ virtual ~SignTool() {}
+ virtual void Click(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position) { }
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) { }
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { }
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) { }
+};
+
+class GameModel;
+
+class SampleTool: public Tool
+{
+ GameModel * gameModel;
+public:
+ SampleTool(GameModel * model):
+ Tool(0, "SMPL", "Sample an element on the screen", 0, 0, 0, "DEFAULT_UI_SAMPLE", SampleTool::GetIcon),
+ gameModel(model)
+ {
+ }
+ static VideoBuffer * GetIcon(int toolID, int width, int height);
+ virtual ~SampleTool() {}
+ virtual void Click(Simulation * sim, Brush * brush, ui::Point position) { }
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) { }
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { }
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) { }
+};
+
+class PropertyTool: public Tool
+{
+public:
+ PropertyTool():
+ Tool(0, "PROP", "Property Edit. Click to alter the properties of elements in the field", 0xfe, 0xa9, 0x00, "DEFAULT_UI_PROPERTY", NULL)
+ {
+ }
+ virtual ~PropertyTool() {}
+ virtual void Click(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position) {};
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) { }
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { }
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) { }
+};
+
+class Element_LIGH_Tool: public Tool
+{
+ int nextUse;
+public:
+ Element_LIGH_Tool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL):
+ Tool(id, name, description, r, g, b, identifier, textureGen),
+ nextUse(0)
+ {
+ }
+ virtual ~Element_LIGH_Tool() {}
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void Click(Simulation * sim, Brush * brush, ui::Point position) { }
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) { }
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { }
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) { }
+};
+
+
+class ElementTool: public Tool
+{
+public:
+ ElementTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL);
+ virtual ~ElementTool();
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false);
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2);
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position);
+};
+
+class Element_TESC_Tool: public ElementTool
+{
+public:
+ Element_TESC_Tool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL);
+ virtual ~Element_TESC_Tool() {}
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false);
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2);
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position);
+};
+
+class PlopTool: public ElementTool
+{
+public:
+ PlopTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL):
+ ElementTool(id, name, description, r, g, b, identifier, textureGen)
+ {
+ }
+ virtual ~PlopTool() {}
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position) {}
+ virtual void Click(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) { }
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { }
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) { }
+};
+
+class WallTool: public Tool
+{
+public:
+ WallTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL);
+ virtual ~WallTool();
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false);
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2);
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position);
+};
+
+class GolTool: public Tool
+{
+public:
+ GolTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL);
+ virtual ~GolTool();
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false);
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2);
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position);
+};
+
+class WindTool: public Tool
+{
+public:
+ WindTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL);
+ virtual ~WindTool();
+ virtual void Draw(Simulation * sim, Brush * brush, ui::Point position);
+ virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false);
+ virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2);
+ virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position);
+};
+
+#endif /* TOOL_H_ */
diff --git a/src/game/ToolButton.cpp b/src/game/ToolButton.cpp
new file mode 100644
index 0000000..aa31b3d
--- /dev/null
+++ b/src/game/ToolButton.cpp
@@ -0,0 +1,99 @@
+/*
+ * ToolButton.cpp
+ *
+ * Created on: Jan 30, 2012
+ * Author: Simon
+ */
+
+#include "ToolButton.h"
+#include "interface/Keys.h"
+
+ToolButton::ToolButton(ui::Point position, ui::Point size, std::string text_, std::string toolTip):
+ ui::Button(position, size, text_, toolTip)
+{
+ SetSelectionState(-1);
+ Appearance.BorderActive = ui::Colour(255, 0, 0);
+}
+
+void ToolButton::OnMouseClick(int x, int y, unsigned int button)
+{
+ isButtonDown = true;
+}
+
+void ToolButton::OnMouseUnclick(int x, int y, unsigned int button)
+{
+ if(isButtonDown)
+ {
+ if(button == BUTTON_LEFT)
+ SetSelectionState(0);
+ if(button == BUTTON_RIGHT)
+ SetSelectionState(1);
+ if(button == BUTTON_MIDDLE)
+ SetSelectionState(2);
+ DoAction();
+ }
+ isButtonDown = false;
+}
+
+void ToolButton::Draw(const ui::Point& screenPos)
+{
+ Graphics * g = ui::Engine::Ref().g;
+ int totalColour = Appearance.BackgroundInactive.Blue + (3*Appearance.BackgroundInactive.Green) + (2*Appearance.BackgroundInactive.Red);
+
+ if(Appearance.GetTexture())
+ {
+ g->draw_image(Appearance.GetTexture(), screenPos.X+2, screenPos.Y+2, 255);
+ }
+ else
+ {
+ g->fillrect(screenPos.X+2, screenPos.Y+2, Size.X-4, Size.Y-4, Appearance.BackgroundInactive.Red, Appearance.BackgroundInactive.Green, Appearance.BackgroundInactive.Blue, Appearance.BackgroundInactive.Alpha);
+ }
+
+ if(isMouseInside && currentSelection == -1)
+ {
+ g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, Appearance.BorderActive.Red, Appearance.BorderActive.Green, Appearance.BorderActive.Blue, Appearance.BorderActive.Alpha);
+ }
+ else
+ {
+ g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, Appearance.BorderInactive.Red, Appearance.BorderInactive.Green, Appearance.BorderInactive.Blue, Appearance.BorderInactive.Alpha);
+ }
+
+ if (totalColour<544)
+ {
+ g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, buttonDisplayText.c_str(), 255, 255, 255, 255);
+ }
+ else
+ {
+ g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, buttonDisplayText.c_str(), 0, 0, 0, 255);
+ }
+}
+
+void ToolButton::SetSelectionState(int state)
+{
+ currentSelection = state;
+ switch(state)
+ {
+ case 0:
+ Appearance.BorderInactive = ui::Colour(255, 0, 0);
+ break;
+ case 1:
+ Appearance.BorderInactive = ui::Colour(0, 0, 255);
+ break;
+ case 2:
+ Appearance.BorderInactive = ui::Colour(0, 255, 0);
+ break;
+ default:
+ Appearance.BorderInactive = ui::Colour(0, 0, 0);
+ break;
+ }
+}
+
+int ToolButton::GetSelectionState()
+{
+ return currentSelection;
+}
+
+ToolButton::~ToolButton() {
+ // TODO Auto-generated destructor stub
+}
+
diff --git a/src/game/ToolButton.h b/src/game/ToolButton.h
new file mode 100644
index 0000000..b311d0b
--- /dev/null
+++ b/src/game/ToolButton.h
@@ -0,0 +1,25 @@
+/*
+ * ToolButton.h
+ *
+ * Created on: Jan 30, 2012
+ * Author: Simon
+ */
+
+#ifndef TOOLBUTTON_H_
+#define TOOLBUTTON_H_
+
+#include "interface/Button.h"
+
+class ToolButton: public ui::Button {
+ int currentSelection;
+public:
+ ToolButton(ui::Point position, ui::Point size, std::string text_, std::string toolTip = "");
+ virtual void OnMouseUnclick(int x, int y, unsigned int button);
+ virtual void OnMouseClick(int x, int y, unsigned int button);
+ virtual void Draw(const ui::Point& screenPos);
+ void SetSelectionState(int state);
+ int GetSelectionState();
+ virtual ~ToolButton();
+};
+
+#endif /* TOOLBUTTON_H_ */
diff --git a/src/game/TriangleBrush.h b/src/game/TriangleBrush.h
new file mode 100644
index 0000000..7d537bd
--- /dev/null
+++ b/src/game/TriangleBrush.h
@@ -0,0 +1,46 @@
+/*
+ * TriangleBrush.h
+ *
+ * Created on: Jan 26, 2012
+ * Author: Savely Skresanov
+ */
+
+#ifndef TRIANGLEBRUSH_H_
+#define TRIANGLEBRUSH_H_
+
+#include <cmath>
+#include "Brush.h"
+
+class TriangleBrush: public Brush
+{
+public:
+ TriangleBrush(ui::Point size_):
+ Brush(size_)
+ {
+ SetRadius(size_);
+ };
+ virtual void GenerateBitmap()
+ {
+ if(bitmap)
+ delete[] bitmap;
+ bitmap = new unsigned char[size.X*size.Y];
+ int rx = radius.X;
+ int ry = radius.Y;
+ for(int x = -rx; x <= rx; x++)
+ {
+ for(int y = -ry; y <= ry; y++)
+ {
+ if ((abs((rx+2*x)*ry+rx*y) + abs(2*rx*(y-ry)) + abs((rx-2*x)*ry+rx*y))<=(4*rx*ry))
+ {
+ bitmap[(y+ry)*(size.X)+x+rx] = 255;
+ }
+ else
+ {
+ bitmap[(y+ry)*(size.X)+x+rx] = 0;
+ }
+ }
+ }
+ }
+};
+
+#endif /* TRIANGLEBRUSH_H_ */
diff --git a/src/graphics/DrawMethodsDef.inc b/src/graphics/DrawMethodsDef.inc
new file mode 100644
index 0000000..4aee0c9
--- /dev/null
+++ b/src/graphics/DrawMethodsDef.inc
@@ -0,0 +1,17 @@
+ int drawtext(int x, int y, const char *s, int r, int g, int b, int a);
+ int drawtext(int x, int y, std::string s, int r, int g, int b, int a);
+ int drawchar(int x, int y, int c, int r, int g, int b, int a);
+ int addchar(int x, int y, int c, int r, int g, int b, int a);
+
+ void xor_pixel(int x, int y);
+ void xor_line(int x, int y, int x2, int y2);
+ void xor_rect(int x, int y, int width, int height);
+ void xor_bitmap(unsigned char * bitmap, int x, int y, int w, int h);
+
+ void draw_line(int x, int y, int x2, int y2, int r, int g, int b, int a);
+ void drawrect(int x, int y, int width, int height, int r, int g, int b, int a);
+ void fillrect(int x, int y, int width, int height, int r, int g, int b, int a);
+ void clearrect(int x, int y, int width, int height);
+ void gradientrect(int x, int y, int width, int height, int r, int g, int b, int a, int r2, int g2, int b2, int a2);
+
+ void draw_image(pixel *img, int x, int y, int w, int h, int a); \ No newline at end of file
diff --git a/src/graphics/Graphics.cpp b/src/graphics/Graphics.cpp
new file mode 100644
index 0000000..11dcac3
--- /dev/null
+++ b/src/graphics/Graphics.cpp
@@ -0,0 +1,1028 @@
+#include <cmath>
+#include <iostream>
+#include <bzlib.h>
+#include <string>
+#include "Config.h"
+#include "Misc.h"
+#include "Graphics.h"
+#define INCLUDE_FONTDATA
+#include "font.h"
+
+VideoBuffer::VideoBuffer(int width, int height):
+ Width(width),
+ Height(height)
+{
+ Buffer = new pixel[width*height];
+ std::fill(Buffer, Buffer+(width*height), 0);
+};
+
+VideoBuffer::VideoBuffer(const VideoBuffer & old):
+ Width(old.Width),
+ Height(old.Height)
+{
+ Buffer = new pixel[old.Width*old.Height];
+ std::copy(old.Buffer, old.Buffer+(old.Width*old.Height), Buffer);
+};
+
+VideoBuffer::VideoBuffer(VideoBuffer * old):
+ Width(old->Width),
+ Height(old->Height)
+{
+ Buffer = new pixel[old->Width*old->Height];
+ std::copy(old->Buffer, old->Buffer+(old->Width*old->Height), Buffer);
+};
+
+void VideoBuffer::Resize(float factor, bool resample)
+{
+ int newWidth = ((float)Width)*factor;
+ int newHeight = ((float)Height)*factor;
+ pixel * newBuffer;
+ if(resample)
+ newBuffer = Graphics::resample_img(Buffer, Width, Height, newWidth, newHeight);
+ else
+ newBuffer = Graphics::resample_img_nn(Buffer, Width, Height, newWidth, newHeight);
+
+ if(newBuffer)
+ {
+ delete[] Buffer;
+ Buffer = newBuffer;
+ Width = newWidth;
+ Height = newHeight;
+ }
+}
+
+int VideoBuffer::SetCharacter(int x, int y, int c, int r, int g, int b, int a)
+{
+ int i, j, w, bn = 0, ba = 0;
+ char *rp = font_data + font_ptrs[c];
+ w = *(rp++);
+ for (j=0; j<FONT_H; j++)
+ for (i=0; i<w; i++)
+ {
+ if (!bn)
+ {
+ ba = *(rp++);
+ bn = 8;
+ }
+ SetPixel(x+i, y+j, r, g, b, ((ba&3)*a)/3);
+ ba >>= 2;
+ bn -= 2;
+ }
+ return x + w;
+}
+
+int VideoBuffer::BlendCharacter(int x, int y, int c, int r, int g, int b, int a)
+{
+ int i, j, w, bn = 0, ba = 0;
+ char *rp = font_data + font_ptrs[c];
+ w = *(rp++);
+ for (j=0; j<FONT_H; j++)
+ for (i=0; i<w; i++)
+ {
+ if (!bn)
+ {
+ ba = *(rp++);
+ bn = 8;
+ }
+ BlendPixel(x+i, y+j, r, g, b, ((ba&3)*a)/3);
+ ba >>= 2;
+ bn -= 2;
+ }
+ return x + w;
+}
+
+int VideoBuffer::AddCharacter(int x, int y, int c, int r, int g, int b, int a)
+{
+ int i, j, w, bn = 0, ba = 0;
+ char *rp = font_data + font_ptrs[c];
+ w = *(rp++);
+ for (j=0; j<FONT_H; j++)
+ for (i=0; i<w; i++)
+ {
+ if (!bn)
+ {
+ ba = *(rp++);
+ bn = 8;
+ }
+ AddPixel(x+i, y+j, r, g, b, ((ba&3)*a)/3);
+ ba >>= 2;
+ bn -= 2;
+ }
+ return x + w;
+}
+
+VideoBuffer::~VideoBuffer()
+{
+ delete[] Buffer;
+};
+
+/**
+ * Common graphics functions, mostly static methods that provide
+ * encoding/decoding of different formats and font metrics
+ */
+
+char * Graphics::GenerateGradient(pixel * colours, float * points, int pointcount, int size)
+{
+ int cp, i, j;
+ pixel ptemp;
+ char * newdata = (char*)malloc(size * 3);
+ float poss, pose, temp;
+ memset(newdata, 0, size*3);
+ //Sort the Colours and Points
+ for (i = (pointcount - 1); i > 0; i--)
+ {
+ for (j = 1; j <= i; j++)
+ {
+ if (points[j-1] > points[j])
+ {
+ temp = points[j-1];
+ points[j-1] = points[j];
+ points[j] = temp;
+
+ ptemp = colours[j-1];
+ colours[j-1] = colours[j];
+ colours[j] = ptemp;
+ }
+ }
+ }
+ i = 0;
+ j = 1;
+ poss = points[i];
+ pose = points[j];
+ for (cp = 0; cp < size; cp++)
+ {
+ float cpos = (float)cp / (float)size, ccpos, cccpos;
+ if(cpos > pose && j+1 < pointcount)
+ {
+ poss = points[++i];
+ pose = points[++j];
+ }
+ ccpos = cpos - poss;
+ cccpos = ccpos / (pose - poss);
+ if(cccpos > 1.0f)
+ cccpos = 1.0f;
+ newdata[(cp*3)] = PIXR(colours[i])*(1.0f-cccpos) + PIXR(colours[j])*(cccpos);
+ newdata[(cp*3)+1] = PIXG(colours[i])*(1.0f-cccpos) + PIXG(colours[j])*(cccpos);
+ newdata[(cp*3)+2] = PIXB(colours[i])*(1.0f-cccpos) + PIXB(colours[j])*(cccpos);
+ }
+ return newdata;
+}
+
+void *Graphics::ptif_pack(pixel *src, int w, int h, int *result_size){
+ int i = 0, datalen = (w*h)*3, cx = 0, cy = 0;
+ unsigned char *red_chan = (unsigned char*)calloc(1, w*h);
+ unsigned char *green_chan = (unsigned char*)calloc(1, w*h);
+ unsigned char *blue_chan = (unsigned char*)calloc(1, w*h);
+ unsigned char *data = (unsigned char*)malloc(((w*h)*3)+8);
+ unsigned char *result = (unsigned char*)malloc(((w*h)*3)+8);
+
+ for(cx = 0; cx<w; cx++){
+ for(cy = 0; cy<h; cy++){
+ red_chan[w*(cy)+(cx)] = PIXR(src[w*(cy)+(cx)]);
+ green_chan[w*(cy)+(cx)] = PIXG(src[w*(cy)+(cx)]);
+ blue_chan[w*(cy)+(cx)] = PIXB(src[w*(cy)+(cx)]);
+ }
+ }
+
+ memcpy(data, red_chan, w*h);
+ memcpy(data+(w*h), green_chan, w*h);
+ memcpy(data+((w*h)*2), blue_chan, w*h);
+ free(red_chan);
+ free(green_chan);
+ free(blue_chan);
+
+ result[0] = 'P';
+ result[1] = 'T';
+ result[2] = 'i';
+ result[3] = 1;
+ result[4] = w;
+ result[5] = w>>8;
+ result[6] = h;
+ result[7] = h>>8;
+
+ i -= 8;
+
+ if(BZ2_bzBuffToBuffCompress((char *)(result+8), (unsigned *)&i, (char *)data, datalen, 9, 0, 0) != 0){
+ free(data);
+ free(result);
+ return NULL;
+ }
+
+ *result_size = i+8;
+ free(data);
+ return result;
+}
+
+pixel *Graphics::ptif_unpack(void *datain, int size, int *w, int *h){
+ int width, height, i, cx, cy, resCode;
+ unsigned char *red_chan;
+ unsigned char *green_chan;
+ unsigned char *blue_chan;
+ unsigned char *data = (unsigned char*)datain;
+ unsigned char *undata;
+ pixel *result;
+ if(size<16){
+ printf("Image empty\n");
+ return NULL;
+ }
+ if(!(data[0]=='P' && data[1]=='T' && data[2]=='i')){
+ printf("Image header invalid\n");
+ return NULL;
+ }
+ width = data[4]|(data[5]<<8);
+ height = data[6]|(data[7]<<8);
+
+ i = (width*height)*3;
+ undata = (unsigned char*)calloc(1, (width*height)*3);
+ red_chan = (unsigned char*)calloc(1, width*height);
+ green_chan = (unsigned char*)calloc(1, width*height);
+ blue_chan = (unsigned char *)calloc(1, width*height);
+ result = (pixel *)calloc(width*height, PIXELSIZE);
+
+ resCode = BZ2_bzBuffToBuffDecompress((char *)undata, (unsigned *)&i, (char *)(data+8), size-8, 0, 0);
+ if (resCode){
+ printf("Decompression failure, %d\n", resCode);
+ free(red_chan);
+ free(green_chan);
+ free(blue_chan);
+ free(undata);
+ free(result);
+ return NULL;
+ }
+ if(i != (width*height)*3){
+ printf("Result buffer size mismatch, %d != %d\n", i, (width*height)*3);
+ free(red_chan);
+ free(green_chan);
+ free(blue_chan);
+ free(undata);
+ free(result);
+ return NULL;
+ }
+ memcpy(red_chan, undata, width*height);
+ memcpy(green_chan, undata+(width*height), width*height);
+ memcpy(blue_chan, undata+((width*height)*2), width*height);
+
+ for(cx = 0; cx<width; cx++){
+ for(cy = 0; cy<height; cy++){
+ result[width*(cy)+(cx)] = PIXRGB(red_chan[width*(cy)+(cx)], green_chan[width*(cy)+(cx)], blue_chan[width*(cy)+(cx)]);
+ }
+ }
+
+ *w = width;
+ *h = height;
+ free(red_chan);
+ free(green_chan);
+ free(blue_chan);
+ free(undata);
+ return result;
+}
+
+pixel *Graphics::resample_img_nn(pixel * src, int sw, int sh, int rw, int rh)
+{
+ int y, x;
+ pixel *q = NULL;
+ q = (pixel *)malloc(rw*rh*PIXELSIZE);
+ for (y=0; y<rh; y++)
+ for (x=0; x<rw; x++){
+ q[rw*y+x] = src[sw*(y*sh/rh)+(x*sw/rw)];
+ }
+ return q;
+}
+
+pixel *Graphics::resample_img(pixel *src, int sw, int sh, int rw, int rh)
+{
+#ifdef DEBUG
+ std::cout << "Resampling " << sw << "x" << sh << " to " << rw << "x" << rh << std::endl;
+#endif
+ bool stairstep = false;
+ if(rw < sw || rh < sh)
+ {
+ float fx = (float)(((float)sw)/((float)rw));
+ float fy = (float)(((float)sh)/((float)rh));
+
+ int fxint, fyint;
+ double fxintp_t, fyintp_t;
+
+ float fxf = modf(fx, &fxintp_t), fyf = modf(fy, &fyintp_t);
+ fxint = fxintp_t;
+ fyint = fyintp_t;
+
+ if(((fxint & (fxint-1)) == 0 && fxf < 0.1f) || ((fyint & (fyint-1)) == 0 && fyf < 0.1f))
+ stairstep = true;
+
+#ifdef DEBUG
+ if(stairstep)
+ std::cout << "Downsampling by " << fx << "x" << fy << " using stairstepping" << std::endl;
+ else
+ std::cout << "Downsampling by " << fx << "x" << fy << " without stairstepping" << std::endl;
+#endif
+ }
+
+ int y, x, fxceil, fyceil;
+ //int i,j,x,y,w,h,r,g,b,c;
+ pixel *q = NULL;
+ if(rw == sw && rh == sh){
+ //Don't resample
+ q = (pixel *)malloc(rw*rh*PIXELSIZE);
+ memcpy(q, src, rw*rh*PIXELSIZE);
+ } else if(!stairstep) {
+ float fx, fy, fyc, fxc;
+ double intp;
+ pixel tr, tl, br, bl;
+ q = (pixel *)malloc(rw*rh*PIXELSIZE);
+ //Bilinear interpolation for upscaling
+ for (y=0; y<rh; y++)
+ for (x=0; x<rw; x++)
+ {
+ fx = ((float)x)*((float)sw)/((float)rw);
+ fy = ((float)y)*((float)sh)/((float)rh);
+ fxc = modf(fx, &intp);
+ fyc = modf(fy, &intp);
+ fxceil = (int)ceil(fx);
+ fyceil = (int)ceil(fy);
+ if (fxceil>=sw) fxceil = sw-1;
+ if (fyceil>=sh) fyceil = sh-1;
+ tr = src[sw*(int)floor(fy)+fxceil];
+ tl = src[sw*(int)floor(fy)+(int)floor(fx)];
+ br = src[sw*fyceil+fxceil];
+ bl = src[sw*fyceil+(int)floor(fx)];
+ q[rw*y+x] = PIXRGB(
+ (int)(((((float)PIXR(tl))*(1.0f-fxc))+(((float)PIXR(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXR(bl))*(1.0f-fxc))+(((float)PIXR(br))*(fxc)))*(fyc)),
+ (int)(((((float)PIXG(tl))*(1.0f-fxc))+(((float)PIXG(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXG(bl))*(1.0f-fxc))+(((float)PIXG(br))*(fxc)))*(fyc)),
+ (int)(((((float)PIXB(tl))*(1.0f-fxc))+(((float)PIXB(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXB(bl))*(1.0f-fxc))+(((float)PIXB(br))*(fxc)))*(fyc))
+ );
+ }
+ } else {
+ //Stairstepping
+ float fx, fy, fyc, fxc;
+ double intp;
+ pixel tr, tl, br, bl;
+ int rrw = rw, rrh = rh;
+ pixel * oq;
+ oq = (pixel *)malloc(sw*sh*PIXELSIZE);
+ memcpy(oq, src, sw*sh*PIXELSIZE);
+ rw = sw;
+ rh = sh;
+ while(rrw != rw && rrh != rh){
+ if(rw > rrw)
+ rw *= 0.7;
+ if(rh > rrh)
+ rh *= 0.7;
+ if(rw <= rrw)
+ rw = rrw;
+ if(rh <= rrh)
+ rh = rrh;
+ q = (pixel *)malloc(rw*rh*PIXELSIZE);
+ //Bilinear interpolation
+ for (y=0; y<rh; y++)
+ for (x=0; x<rw; x++)
+ {
+ fx = ((float)x)*((float)sw)/((float)rw);
+ fy = ((float)y)*((float)sh)/((float)rh);
+ fxc = modf(fx, &intp);
+ fyc = modf(fy, &intp);
+ fxceil = (int)ceil(fx);
+ fyceil = (int)ceil(fy);
+ if (fxceil>=sw) fxceil = sw-1;
+ if (fyceil>=sh) fyceil = sh-1;
+ tr = oq[sw*(int)floor(fy)+fxceil];
+ tl = oq[sw*(int)floor(fy)+(int)floor(fx)];
+ br = oq[sw*fyceil+fxceil];
+ bl = oq[sw*fyceil+(int)floor(fx)];
+ q[rw*y+x] = PIXRGB(
+ (int)(((((float)PIXR(tl))*(1.0f-fxc))+(((float)PIXR(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXR(bl))*(1.0f-fxc))+(((float)PIXR(br))*(fxc)))*(fyc)),
+ (int)(((((float)PIXG(tl))*(1.0f-fxc))+(((float)PIXG(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXG(bl))*(1.0f-fxc))+(((float)PIXG(br))*(fxc)))*(fyc)),
+ (int)(((((float)PIXB(tl))*(1.0f-fxc))+(((float)PIXB(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXB(bl))*(1.0f-fxc))+(((float)PIXB(br))*(fxc)))*(fyc))
+ );
+ }
+ free(oq);
+ oq = q;
+ sw = rw;
+ sh = rh;
+ }
+ }
+ return q;
+}
+
+pixel *Graphics::rescale_img(pixel *src, int sw, int sh, int *qw, int *qh, int f)
+{
+ int i,j,x,y,w,h,r,g,b,c;
+ pixel p, *q;
+ w = (sw+f-1)/f;
+ h = (sh+f-1)/f;
+ q = (pixel *)malloc(w*h*PIXELSIZE);
+ for (y=0; y<h; y++)
+ for (x=0; x<w; x++)
+ {
+ r = g = b = c = 0;
+ for (j=0; j<f; j++)
+ for (i=0; i<f; i++)
+ if (x*f+i<sw && y*f+j<sh)
+ {
+ p = src[(y*f+j)*sw + (x*f+i)];
+ if (p)
+ {
+ r += PIXR(p);
+ g += PIXG(p);
+ b += PIXB(p);
+ c ++;
+ }
+ }
+ if (c>1)
+ {
+ r = (r+c/2)/c;
+ g = (g+c/2)/c;
+ b = (b+c/2)/c;
+ }
+ q[y*w+x] = PIXRGB(r, g, b);
+ }
+ *qw = w;
+ *qh = h;
+ return q;
+}
+
+int Graphics::textwidth(const char *s)
+{
+ int x = 0;
+ for (; *s; s++)
+ x += font_data[font_ptrs[(int)(*(unsigned char *)s)]];
+ return x-1;
+}
+
+int Graphics::CharWidth(char c)
+{
+ return font_data[font_ptrs[(int)c]];
+}
+
+int Graphics::textnwidth(char *s, int n)
+{
+ int x = 0;
+ for (; *s; s++)
+ {
+ if (!n)
+ break;
+ if(((char)*s)=='\b')
+ {
+ if(!s[1]) break;
+ s++;
+ continue;
+ } else if(*s == '\x0F') {
+ if(!s[1] || !s[2] || !s[3]) break;
+ s+=3;
+ continue;
+ }
+ x += font_data[font_ptrs[(int)(*(unsigned char *)s)]];
+ n--;
+ }
+ return x-1;
+}
+
+void Graphics::textnpos(char *s, int n, int w, int *cx, int *cy)
+{
+ int x = 0;
+ int y = 0;
+ int wordlen, charspace;
+ while (*s&&n)
+ {
+ wordlen = strcspn(s," .,!?\n");
+ charspace = textwidthx(s, w-x);
+ if (charspace<wordlen && wordlen && w-x<w/3)
+ {
+ x = 0;
+ y += FONT_H+2;
+ }
+ for (; *s && --wordlen>=-1; s++)
+ {
+ if (!n) {
+ break;
+ }
+ x += font_data[font_ptrs[(int)(*(unsigned char *)s)]];
+ if (x>=w)
+ {
+ x = 0;
+ y += FONT_H+2;
+ }
+ n--;
+ }
+ }
+ *cx = x-1;
+ *cy = y;
+}
+
+int Graphics::textwidthx(char *s, int w)
+{
+ int x=0,n=0,cw;
+ for (; *s; s++)
+ {
+ if((char)*s == '\b')
+ {
+ if(!s[1]) break;
+ s++;
+ continue;
+ } else if (*s == '\x0F')
+ {
+ if(!s[1] || !s[2] || !s[3]) break;
+ s+=3;
+ continue;
+ }
+ cw = font_data[font_ptrs[(int)(*(unsigned char *)s)]];
+ if (x+(cw/2) >= w)
+ break;
+ x += cw;
+ n++;
+ }
+ return n;
+}
+
+int Graphics::PositionAtCharIndex(char *s, int charIndex, int & positionX, int & positionY)
+{
+ int x = 0, y = 0, lines = 1;
+ for (; *s; s++)
+ {
+ if (!charIndex)
+ break;
+ if(*s == '\n') {
+ lines++;
+ x = 0;
+ y += FONT_H+2;
+ charIndex--;
+ continue;
+ } else if(*s =='\b') {
+ if(!s[1]) break;
+ s++;
+ charIndex-=2;
+ continue;
+ } else if(*s == '\x0F') {
+ if(!s[1] || !s[2] || !s[3]) break;
+ s+=3;
+ charIndex-=4;
+ continue;
+ }
+ x += font_data[font_ptrs[(int)(*(unsigned char *)s)]];
+ charIndex--;
+ }
+ positionX = x;
+ positionY = y;
+ return lines;
+}
+
+int Graphics::CharIndexAtPosition(char *s, int positionX, int positionY)
+{
+ int x=0, y=0,charIndex=0,cw;
+ for (; *s; s++)
+ {
+ if(*s == '\n') {
+ x = 0;
+ y += FONT_H+2;
+ charIndex++;
+ continue;
+ } else if(*s == '\b') {
+ if(!s[1]) break;
+ s++;
+ charIndex+=2;
+ continue;
+ } else if (*s == '\x0F') {
+ if(!s[1] || !s[2] || !s[3]) break;
+ s+=3;
+ charIndex+=4;
+ continue;
+ }
+ cw = font_data[font_ptrs[(int)(*(unsigned char *)s)]];
+ if ((x+(cw/2) >= positionX && y+FONT_H >= positionY) || y > positionY)
+ break;
+ x += cw;
+ charIndex++;
+ }
+ return charIndex;
+}
+
+
+int Graphics::textposxy(char *s, int width, int w, int h)
+{
+ int x=0,y=0,n=0,cw, wordlen, charspace;
+ while (*s)
+ {
+ wordlen = strcspn(s," .,!?\n");
+ charspace = textwidthx(s, width-x);
+ if (charspace<wordlen && wordlen && width-x<width/3)
+ {
+ x = 0;
+ y += FONT_H+2;
+ }
+ for (; *s && --wordlen>=-1; s++)
+ {
+ cw = font_data[font_ptrs[(int)(*(unsigned char *)s)]];
+ if ((x+(cw/2) >= w && y+6 >= h)||(y+6 >= h+FONT_H+2))
+ return n++;
+ x += cw;
+ if (x>=width) {
+ x = 0;
+ y += FONT_H+2;
+ }
+ n++;
+ }
+ }
+ return n;
+}
+int Graphics::textwrapheight(char *s, int width)
+{
+ int x=0, height=FONT_H+2, cw;
+ int wordlen;
+ int charspace;
+ while (*s)
+ {
+ wordlen = strcspn(s," .,!?\n");
+ charspace = textwidthx(s, width-x);
+ if (charspace<wordlen && wordlen && width-x<width/3)
+ {
+ x = 0;
+ height += FONT_H+2;
+ }
+ for (; *s && --wordlen>=-1; s++)
+ {
+ if (*s == '\n')
+ {
+ x = 0;
+ height += FONT_H+2;
+ }
+ else if (*s == '\b')
+ {
+ if(!s[1]) break;
+ s++;
+ }
+ else if (*s == '\x0F')
+ {
+ if(!s[1] || !s[2] || !s[3]) break;
+ s+=3;
+ }
+ else
+ {
+ cw = font_data[font_ptrs[(int)(*(unsigned char *)s)]];
+ if (x+cw>=width)
+ {
+ x = 0;
+ height += FONT_H+2;
+ }
+ x += cw;
+ }
+ }
+ }
+ return height;
+}
+
+void Graphics::textsize(const char * s, int & width, int & height)
+{
+ if(!strlen(s))
+ {
+ width = 0;
+ height = FONT_H;
+ return;
+ }
+
+ int cHeight = FONT_H, cWidth = 0, lWidth = 0;
+ for (; *s; s++)
+ {
+ if (*s == '\n')
+ {
+ cWidth = 0;
+ cHeight += FONT_H+2;
+ }
+ else if (*s == '\x0F')
+ {
+ if(!s[1] || !s[2] || !s[1]) break;
+ s+=3;
+ }
+ else if (*s == '\b')
+ {
+ if(!s[1]) break;
+ s++;
+ }
+ else
+ {
+ cWidth += font_data[font_ptrs[(int)(*(unsigned char *)s)]];
+ if(cWidth>lWidth)
+ lWidth = cWidth;
+ }
+ }
+ width = lWidth;
+ height = cHeight;
+}
+
+void Graphics::draw_icon(int x, int y, Icon icon, unsigned char alpha, bool invert)
+{
+ y--;
+ switch(icon)
+ {
+ case IconOpen:
+ if(invert)
+ drawchar(x, y, 0x81, 0, 0, 0, alpha);
+ else
+ drawchar(x, y, 0x81, 255, 255, 255, alpha);
+ break;
+ case IconReload:
+ if(invert)
+ drawchar(x, y, 0x91, 0, 0, 0, alpha);
+ else
+ drawchar(x, y, 0x91, 255, 255, 255, alpha);
+ break;
+ case IconSave:
+ if(invert)
+ drawchar(x, y, 0x82, 0, 0, 0, alpha);
+ else
+ drawchar(x, y, 0x82, 255, 255, 255, alpha);
+ break;
+ case IconVoteUp:
+ if(invert)
+ drawchar(x, y, 0xCB, 0, 100, 0, alpha);
+ else
+ drawchar(x, y, 0xCB, 0, 187, 18, alpha);
+ break;
+ case IconVoteDown:
+ if(invert)
+ drawchar(x, y, 0xCA, 100, 10, 0, alpha);
+ else
+ drawchar(x, y, 0xCA, 187, 40, 0, alpha);
+ break;
+ case IconTag:
+ if(invert)
+ drawchar(x, y, 0x83, 0, 0, 0, alpha);
+ else
+ drawchar(x, y, 0x83, 255, 255, 255, alpha);
+ break;
+ case IconNew:
+ if(invert)
+ drawchar(x, y, 0x92, 0, 0, 0, alpha);
+ else
+ drawchar(x, y, 0x92, 255, 255, 255, alpha);
+ break;
+ case IconLogin:
+ if(invert)
+ drawchar(x, y+1, 0x84, 0, 0, 0, alpha);
+ else
+ drawchar(x, y+1, 0x84, 255, 255, 255, alpha);
+ break;
+ case IconSimulationSettings:
+ if(invert)
+ drawchar(x, y+1, 0xCF, 0, 0, 0, alpha);
+ else
+ drawchar(x, y+1, 0xCF, 255, 255, 255, alpha);
+ break;
+ case IconRenderSettings:
+ if(invert)
+ {
+ drawchar(x, y+1, 0xD8, 255, 0, 0, alpha);
+ drawchar(x, y+1, 0xD9, 0, 255, 0, alpha);
+ drawchar(x, y+1, 0xDA, 0, 0, 255, alpha);
+ }
+ else
+ {
+ addchar(x, y+1, 0xD8, 255, 0, 0, alpha);
+ addchar(x, y+1, 0xD9, 0, 255, 0, alpha);
+ addchar(x, y+1, 0xDA, 0, 0, 255, alpha);
+ }
+ break;
+ case IconPause:
+ if(invert)
+ drawchar(x, y, 0x90, 0, 0, 0, alpha);
+ else
+ drawchar(x, y, 0x90, 255, 255, 255, alpha);
+ break;
+ case IconFavourite:
+ if(invert)
+ drawchar(x, y, 0xCC, 100, 80, 32, alpha);
+ else
+ drawchar(x, y, 0xCC, 192, 160, 64, alpha);
+ break;
+ case IconReport:
+ if(invert)
+ drawchar(x, y, 0xE3, 140, 140, 0, alpha);
+ else
+ drawchar(x, y, 0xE3, 255, 255, 0, alpha);
+ break;
+ case IconUsername:
+ if(invert)
+ {
+ drawchar(x, y, 0x8B, 32, 64, 128, alpha);
+ drawchar(x, y, 0x8A, 0, 0, 0, alpha);
+ }
+ else
+ {
+ drawchar(x, y, 0x8B, 32, 64, 128, alpha);
+ drawchar(x, y, 0x8A, 255, 255, 255, alpha);
+ }
+ break;
+ case IconPassword:
+ if(invert)
+ {
+ drawchar(x, y, 0x8C, 160, 144, 32, alpha);
+ drawchar(x, y, 0x84, 0, 0, 0, alpha);
+ }
+ else
+ {
+ drawchar(x, y, 0x8C, 160, 144, 32, alpha);
+ drawchar(x, y, 0x84, 255, 255, 255, alpha);
+ }
+ break;
+ case IconClose:
+ if(invert)
+ drawchar(x, y, 0xAA, 20, 20, 20, alpha);
+ else
+ drawchar(x, y, 0xAA, 230, 230, 230, alpha);
+ break;
+ case IconVoteSort:
+ if (invert)
+ {
+ drawchar(x, y, 0xA9, 44, 48, 32, alpha);
+ drawchar(x, y, 0xA8, 32, 44, 32, alpha);
+ drawchar(x, y, 0xA7, 128, 128, 128, alpha);
+ }
+ else
+ {
+ drawchar(x, y, 0xA9, 144, 48, 32, alpha);
+ drawchar(x, y, 0xA8, 32, 144, 32, alpha);
+ drawchar(x, y, 0xA7, 255, 255, 255, alpha);
+ }
+ break;
+ case IconDateSort:
+ if (invert)
+ {
+ drawchar(x, y, 0xA6, 32, 32, 32, alpha);
+ }
+ else
+ {
+ drawchar(x, y, 0xA6, 255, 255, 255, alpha);
+ }
+ break;
+ case IconMyOwn:
+ if (invert)
+ {
+ drawchar(x, y, 0x94, 192, 160, 64, alpha);
+ drawchar(x, y, 0x93, 32, 32, 32, alpha);
+ }
+ else
+ {
+ drawchar(x, y, 0x94, 192, 160, 64, alpha);
+ drawchar(x, y, 0x93, 255, 255, 255, alpha);
+ }
+ break;
+ case IconSearch:
+ drawchar(x, y, 0x8E, 30, 30, 180, alpha);
+ drawchar(x, y, 0x8F, 255, 255, 255, alpha);
+ break;
+ case IconDelete:
+ if(invert)
+ {
+ drawchar(x, y, 0x86, 159, 47, 31, alpha);
+ drawchar(x, y, 0x85, 0, 0, 0, alpha);
+ }
+ else
+ {
+ drawchar(x, y, 0x86, 159, 47, 31, alpha);
+ drawchar(x, y, 0x85, 255, 255, 255, alpha);
+ }
+ break;
+ case IconAdd:
+ if(invert)
+ {
+ drawchar(x, y, 0x86, 32, 144, 32, alpha);
+ drawchar(x, y, 0x89, 0, 0, 0, alpha);
+ }
+ else
+ {
+ drawchar(x, y, 0x86, 32, 144, 32, alpha);
+ drawchar(x, y, 0x89, 255, 255, 255, alpha);
+ }
+ break;
+ case IconVelocity:
+ drawchar(x+1, y, 0x98, 128, 160, 255, alpha);
+ break;
+ case IconPressure:
+ if(invert)
+ drawchar(x+1, y+1, 0x99, 180, 160, 16, alpha);
+ else
+ drawchar(x+1, y+1, 0x99, 255, 212, 32, alpha);
+ break;
+ case IconPersistant:
+ if(invert)
+ drawchar(x+1, y+1, 0x9A, 20, 20, 20, alpha);
+ else
+ drawchar(x+1, y+1, 0x9A, 212, 212, 212, alpha);
+ break;
+ case IconFire:
+ drawchar(x+1, y+1, 0x9B, 255, 0, 0, alpha);
+ drawchar(x+1, y+1, 0x9C, 255, 255, 64, alpha);
+ break;
+ case IconBlob:
+ if(invert)
+ drawchar(x+1, y, 0xBF, 55, 180, 55, alpha);
+ else
+ drawchar(x+1, y, 0xBF, 55, 255, 55, alpha);
+ break;
+ case IconHeat:
+ drawchar(x+3, y, 0xBE, 255, 0, 0, alpha);
+ if(invert)
+ drawchar(x+3, y, 0xBD, 0, 0, 0, alpha);
+ else
+ drawchar(x+3, y, 0xBD, 255, 255, 255, alpha);
+ break;
+ case IconBlur:
+ if(invert)
+ drawchar(x+1, y, 0xC4, 50, 70, 180, alpha);
+ else
+ drawchar(x+1, y, 0xC4, 100, 150, 255, alpha);
+ break;
+ case IconGradient:
+ if(invert)
+ drawchar(x+1, y+1, 0xD3, 255, 50, 255, alpha);
+ else
+ drawchar(x+1, y+1, 0xD3, 205, 50, 205, alpha);
+ break;
+ case IconLife:
+ if(invert)
+ drawchar(x, y+1, 0xE0, 0, 0, 0, alpha);
+ else
+ drawchar(x, y+1, 0xE0, 255, 255, 255, alpha);
+ break;
+ case IconEffect:
+ drawchar(x+1, y, 0xE1, 255, 255, 160, alpha);
+ break;
+ case IconGlow:
+ drawchar(x+1, y, 0xDF, 200, 255, 255, alpha);
+ break;
+ case IconWarp:
+ drawchar(x+1, y, 0xDE, 255, 255, 255, alpha);
+ break;
+ case IconBasic:
+ if(invert)
+ drawchar(x+1, y+1, 0xDB, 50, 50, 0, alpha);
+ else
+ drawchar(x+1, y+1, 0xDB, 255, 255, 200, alpha);
+ break;
+ case IconAltAir:
+ if(invert) {
+ drawchar(x+1, y+1, 0xD4, 180, 55, 55, alpha);
+ drawchar(x+1, y+1, 0xD5, 55, 180, 55, alpha);
+ } else {
+ drawchar(x+1, y+1, 0xD4, 255, 55, 55, alpha);
+ drawchar(x+1, y+1, 0xD5, 55, 255, 55, alpha);
+ }
+ break;
+ default:
+ if(invert)
+ drawchar(x, y, 't', 0, 0, 0, alpha);
+ else
+ drawchar(x, y, 't', 255, 255, 255, alpha);
+ break;
+ }
+}
+
+pixel *Graphics::render_packed_rgb(void *image, int width, int height, int cmp_size)
+{
+ unsigned char *tmp;
+ pixel *res;
+ int i;
+
+ tmp = (unsigned char *)malloc(width*height*3);
+ if (!tmp)
+ return NULL;
+ res = (pixel *)malloc(width*height*PIXELSIZE);
+ if (!res)
+ {
+ free(tmp);
+ return NULL;
+ }
+
+ i = width*height*3;
+ if (BZ2_bzBuffToBuffDecompress((char *)tmp, (unsigned *)&i, (char *)image, cmp_size, 0, 0))
+ {
+ free(res);
+ free(tmp);
+ return NULL;
+ }
+
+ for (i=0; i<width*height; i++)
+ res[i] = PIXRGB(tmp[3*i], tmp[3*i+1], tmp[3*i+2]);
+
+ free(tmp);
+ return res;
+}
+
+void Graphics::draw_image(const VideoBuffer & vidBuf, int x, int y, int a)
+{
+ draw_image(vidBuf.Buffer, x, y, vidBuf.Width, vidBuf.Height, a);
+}
+
+void Graphics::draw_image(VideoBuffer * vidBuf, int x, int y, int a)
+{
+ draw_image(vidBuf->Buffer, x, y, vidBuf->Width, vidBuf->Height, a);
+}
+
+VideoBuffer Graphics::DumpFrame()
+{
+#ifdef OGLI
+#else
+ VideoBuffer newBuffer(XRES+BARSIZE, YRES+MENUSIZE);
+ std::copy(vid, vid+((XRES+BARSIZE)*(YRES+MENUSIZE)), newBuffer.Buffer);
+ return newBuffer;
+#endif
+} \ No newline at end of file
diff --git a/src/graphics/Graphics.h b/src/graphics/Graphics.h
new file mode 100644
index 0000000..53d3ee7
--- /dev/null
+++ b/src/graphics/Graphics.h
@@ -0,0 +1,248 @@
+#ifndef GRAPHICS_H
+#define GRAPHICS_H
+
+#include <string>
+#include <cstdlib>
+#include <cstring>
+#include <vector>
+#if defined(OGLI)
+#include "OpenGLHeaders.h"
+#endif
+#include "Config.h"
+//#include "powder.h"
+
+#ifdef PIX16
+#define PIXELSIZE 2
+#define PIXPACK(x) ((((x)>>8)&0xF800)|(((x)>>5)&0x07E0)|(((x)>>3)&0x001F))
+#define PIXRGB(r,g,b) ((((r)<<8)&0xF800)|(((g)<<3)&0x07E0)|(((b)>>3)&0x001F))
+#define PIXR(x) (((x)>>8)&0xF8)
+#define PIXG(x) (((x)>>3)&0xFC)
+#define PIXB(x) (((x)<<3)&0xF8)
+#else
+#define PIXELSIZE 4
+#ifdef PIX32BGR
+#define PIXPACK(x) ((((x)>>16)&0x0000FF)|((x)&0x00FF00)|(((x)<<16)&0xFF0000))
+#define PIXRGB(r,g,b) (((b)<<16)|((g)<<8)|((r)))// (((b)<<16)|((g)<<8)|(r))
+#define PIXR(x) ((x)&0xFF)
+#define PIXG(x) (((x)>>8)&0xFF)
+#define PIXB(x) ((x)>>16)
+#else
+#ifdef PIX32BGRA
+#define PIXPACK(x) ((((x)>>8)&0x0000FF00)|(((x)<<8)&0x00FF0000)|(((x)<<24)&0xFF000000))
+#define PIXRGB(r,g,b) (((b)<<24)|((g)<<16)|((r)<<8))
+#define PIXR(x) (((x)>>8)&0xFF)
+#define PIXG(x) (((x)>>16)&0xFF)
+#define PIXB(x) (((x)>>24))
+#elif defined(PIX32OGL)
+#define PIXPACK(x) (0xFF000000|((x)&0xFFFFFF))
+#define PIXRGB(r,g,b) (0xFF000000|((r)<<16)|((g)<<8)|((b)))// (((b)<<16)|((g)<<8)|(r))
+#define PIXRGBA(r,g,b,a) (((a)<<24)|((r)<<16)|((g)<<8)|((b)))// (((b)<<16)|((g)<<8)|(r))
+#define PIXA(x) (((x)>>24)&0xFF)
+#define PIXR(x) (((x)>>16)&0xFF)
+#define PIXG(x) (((x)>>8)&0xFF)
+#define PIXB(x) ((x)&0xFF)
+#else
+#define PIXPACK(x) (x)
+#define PIXRGB(r,g,b) (((r)<<16)|((g)<<8)|(b))
+#define PIXR(x) ((x)>>16)
+#define PIXG(x) (((x)>>8)&0xFF)
+#define PIXB(x) ((x)&0xFF)
+#endif
+#endif
+#endif
+
+#ifdef PIX16
+typedef unsigned short pixel;
+#else
+typedef unsigned int pixel;
+#endif
+
+//Icon names, see Graphics::draw_icon
+enum Icon
+{
+ NoIcon = 0,
+ IconOpen,
+ IconReload,
+ IconSave,
+ IconVoteUp,
+ IconVoteDown,
+ IconTag,
+ IconNew,
+ IconLogin,
+ IconRenderSettings,
+ IconSimulationSettings,
+ IconPause,
+ IconVoteSort,
+ IconDateSort,
+ IconMyOwn,
+ IconFavourite,
+ IconSearch,
+ IconDelete,
+ IconAdd,
+ IconReport,
+ IconUsername,
+ IconPassword,
+ IconClose,
+ IconEffect,
+ IconFire,
+ IconGlow,
+ IconBlur,
+ IconBlob,
+ IconBasic,
+ IconAltAir,
+ IconPressure,
+ IconVelocity,
+ IconWarp,
+ IconPersistant,
+ IconHeat,
+ IconLife,
+ IconGradient
+};
+
+//"Graphics lite" - slightly lower performance due to variable size,
+class VideoBuffer
+{
+public:
+ pixel * Buffer;
+ int Width, Height;
+
+ VideoBuffer(const VideoBuffer & old);
+ VideoBuffer(VideoBuffer * old);
+ VideoBuffer(int width, int height);
+ void Resize(float factor, bool resample = false);
+ TPT_INLINE void BlendPixel(int x, int y, int r, int g, int b, int a)
+ {
+ #ifdef PIX32OGL
+ pixel t;
+ if (x<0 || y<0 || x>=Width || y>=Height)
+ return;
+ if (a!=255)
+ {
+ t = Buffer[y*(Width)+x];
+ r = (a*r + (255-a)*PIXR(t)) >> 8;
+ g = (a*g + (255-a)*PIXG(t)) >> 8;
+ b = (a*b + (255-a)*PIXB(t)) >> 8;
+ a = a > PIXA(t) ? a : PIXA(t);
+ }
+ Buffer[y*(Width)+x] = PIXRGBA(r,g,b,a);
+ #else
+ pixel t;
+ if (x<0 || y<0 || x>=Width || y>=Height)
+ return;
+ if (a!=255)
+ {
+ t = Buffer[y*(Width)+x];
+ r = (a*r + (255-a)*PIXR(t)) >> 8;
+ g = (a*g + (255-a)*PIXG(t)) >> 8;
+ b = (a*b + (255-a)*PIXB(t)) >> 8;
+ }
+ Buffer[y*(Width)+x] = PIXRGB(r,g,b);
+ #endif
+ }
+
+ TPT_INLINE void SetPixel(int x, int y, int r, int g, int b, int a)
+ {
+ if (x<0 || y<0 || x>=Width || y>=Height)
+ return;
+ #ifdef PIX32OGL
+ Buffer[y*(Width)+x] = PIXRGBA(r,g,b,a);
+ #else
+ Buffer[y*(Width)+x] = PIXRGB((r*a)>>8, (g*a)>>8, (b*a)>>8);
+ #endif
+ }
+
+ TPT_INLINE void AddPixel(int x, int y, int r, int g, int b, int a)
+ {
+ pixel t;
+ if (x<0 || y<0 || x>=Width || y>=Height)
+ return;
+ t = Buffer[y*(Width)+x];
+ r = (a*r + 255*PIXR(t)) >> 8;
+ g = (a*g + 255*PIXG(t)) >> 8;
+ b = (a*b + 255*PIXB(t)) >> 8;
+ if (r>255)
+ r = 255;
+ if (g>255)
+ g = 255;
+ if (b>255)
+ b = 255;
+ Buffer[y*(Width)+x] = PIXRGB(r,g,b);
+ }
+ int SetCharacter(int x, int y, int c, int r, int g, int b, int a);
+ int BlendCharacter(int x, int y, int c, int r, int g, int b, int a);
+ int AddCharacter(int x, int y, int c, int r, int g, int b, int a);
+ ~VideoBuffer();
+};
+
+class Graphics
+{
+public:
+ pixel *vid;
+ int sdl_scale;
+#ifdef OGLI
+ //OpenGL specific instance variables
+ GLuint vidBuf, textTexture;
+ void Reset();
+ #endif
+
+ //Common graphics methods in Graphics.cpp
+ static char * GenerateGradient(pixel * colours, float * points, int pointcount, int size);
+
+ //PTIF methods
+ static void *ptif_pack(pixel *src, int w, int h, int *result_size);
+ static pixel *ptif_unpack(void *datain, int size, int *w, int *h);
+ static pixel *resample_img_nn(pixel *src, int sw, int sh, int rw, int rh);
+ static pixel *resample_img(pixel *src, int sw, int sh, int rw, int rh);
+ static pixel *rescale_img(pixel *src, int sw, int sh, int *qw, int *qh, int f);
+ static pixel *render_packed_rgb(void *image, int width, int height, int cmp_size);
+
+ //Font/text metrics
+ static int CharIndexAtPosition(char *s, int positionX, int positionY);
+ static int PositionAtCharIndex(char *s, int charIndex, int & positionX, int & positionY);
+ static int CharWidth(char c);
+ static int textnwidth(char *s, int n);
+ static void textnpos(char *s, int n, int w, int *cx, int *cy);
+ static int textwidthx(char *s, int w);
+ static int textposxy(char *s, int width, int w, int h);
+ static int textwrapheight(char *s, int width);
+ static int textwidth(const char *s);
+ static void textsize(const char * s, int & width, int & height);
+
+ VideoBuffer DumpFrame();
+
+ void Acquire();
+ void Release();
+
+ void blendpixel(int x, int y, int r, int g, int b, int a);
+ void addpixel(int x, int y, int r, int g, int b, int a);
+
+ void draw_icon(int x, int y, Icon icon, unsigned char alpha = 255, bool invert = false);
+
+ void Clear();
+ void Finalise();
+ //
+ int drawtext(int x, int y, const char *s, int r, int g, int b, int a);
+ int drawtext(int x, int y, std::string s, int r, int g, int b, int a);
+ int drawchar(int x, int y, int c, int r, int g, int b, int a);
+ int addchar(int x, int y, int c, int r, int g, int b, int a);
+
+ void xor_pixel(int x, int y);
+ void xor_line(int x, int y, int x2, int y2);
+ void xor_rect(int x, int y, int width, int height);
+ void xor_bitmap(unsigned char * bitmap, int x, int y, int w, int h);
+
+ void draw_line(int x, int y, int x2, int y2, int r, int g, int b, int a);
+ void drawrect(int x, int y, int width, int height, int r, int g, int b, int a);
+ void fillrect(int x, int y, int width, int height, int r, int g, int b, int a);
+ void clearrect(int x, int y, int width, int height);
+ void gradientrect(int x, int y, int width, int height, int r, int g, int b, int a, int r2, int g2, int b2, int a2);
+
+ void draw_image(pixel *img, int x, int y, int w, int h, int a);
+ void draw_image(const VideoBuffer & vidBuf, int w, int h, int a);
+ void draw_image(VideoBuffer * vidBuf, int w, int h, int a);
+
+ Graphics();
+ ~Graphics();
+};
+
+#endif
diff --git a/src/graphics/OpenGLDrawMethods.inl b/src/graphics/OpenGLDrawMethods.inl
new file mode 100644
index 0000000..1ecd6b1
--- /dev/null
+++ b/src/graphics/OpenGLDrawMethods.inl
@@ -0,0 +1,350 @@
+#include "../data/font.h"
+int PIXELMETHODS_CLASS::drawtext(int x, int y, const char *s, int r, int g, int b, int a)
+{
+ bool invert = false;
+ if(!strlen(s))
+ return 0;
+ int oR = r, oG = g, oB = b;
+ int width, height;
+ Graphics::textsize(s, width, height);
+ VideoBuffer texture(width, height);
+ int characterX = 0, characterY = 0;
+ int startX = characterX;
+ for (; *s; s++)
+ {
+ if (*s == '\n')
+ {
+ characterX = startX;
+ characterY += FONT_H+2;
+ }
+ else if (*s == '\x0F')
+ {
+ if(!s[1] || !s[2] || !s[3]) break;
+ oR = r;
+ oG = g;
+ oB = b;
+ r = (unsigned char)s[1];
+ g = (unsigned char)s[2];
+ b = (unsigned char)s[3];
+ s += 3;
+ }
+ else if (*s == '\x0E')
+ {
+ r = oR;
+ g = oG;
+ b = oB;
+ }
+ else if (*s == '\x01')
+ {
+ invert = !invert;
+ r = 255-r;
+ g = 255-g;
+ b = 255-b;
+ }
+ else if (*s == '\b')
+ {
+ if(!s[1]) break;
+ switch (s[1])
+ {
+ case 'w':
+ r = g = b = 255;
+ break;
+ case 'g':
+ r = g = b = 192;
+ break;
+ case 'o':
+ r = 255;
+ g = 216;
+ b = 32;
+ break;
+ case 'r':
+ r = 255;
+ g = b = 0;
+ break;
+ case 'l':
+ r = 255;
+ g = b = 75;
+ break;
+ case 'b':
+ r = g = 0;
+ b = 255;
+ break;
+ case 't':
+ b = 255;
+ g = 170;
+ r = 32;
+ break;
+ }
+ if(invert)
+ {
+ r = 255-r;
+ g = 255-g;
+ b = 255-b;
+ }
+ s++;
+ }
+ else
+ {
+ characterX = texture.SetCharacter(characterX, characterY, *(unsigned char *)s, r, g, b, a);
+ }
+ }
+ glEnable(GL_TEXTURE_2D);
+
+ //Generate texture
+ glBindTexture(GL_TEXTURE_2D, textTexture);
+
+ //Draw texture
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.Width, texture.Height, 0, GL_BGRA, GL_UNSIGNED_BYTE, texture.Buffer);
+
+ //glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture.Width, texture.Height, GL_BGRA, GL_UNSIGNED_BYTE, texture.Buffer);
+ glBegin(GL_QUADS);
+ glTexCoord2d(0, 0);
+ glVertex2f(x, y);
+ glTexCoord2d(1, 0);
+ glVertex2f(x+texture.Width, y);
+ glTexCoord2d(1, 1);
+ glVertex2f(x+texture.Width, y+texture.Height);
+ glTexCoord2d(0, 1);
+ glVertex2f(x, y+texture.Height);
+ glEnd();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ return x;
+}
+
+int PIXELMETHODS_CLASS::drawtext(int x, int y, std::string s, int r, int g, int b, int a)
+{
+ return drawtext(x, y, s.c_str(), r, g, b, a);
+}
+
+TPT_INLINE int PIXELMETHODS_CLASS::drawchar(int x, int y, int c, int r, int g, int b, int a)
+{
+ int i, j, w, bn = 0, ba = 0;
+ char *rp = font_data + font_ptrs[c];
+ w = *(rp++);
+ VideoBuffer texture(w, 12);
+ texture.SetCharacter(0, 0, c, r, g, b, a);
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, textTexture);
+
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.Width, texture.Height, 0, GL_BGRA, GL_UNSIGNED_BYTE, texture.Buffer);
+ glBegin(GL_QUADS);
+ glTexCoord2d(0, 0);
+ glVertex2f(x, y);
+ glTexCoord2d(1, 0);
+ glVertex2f(x+texture.Width, y);
+ glTexCoord2d(1, 1);
+ glVertex2f(x+texture.Width, y+texture.Height);
+ glTexCoord2d(0, 1);
+ glVertex2f(x, y+texture.Height);
+ glEnd();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ return x + w;
+}
+
+TPT_NO_INLINE int PIXELMETHODS_CLASS::addchar(int x, int y, int c, int r, int g, int b, int a)
+{
+ int i, j, w, bn = 0, ba = 0;
+ char *rp = font_data + font_ptrs[c];
+ w = *(rp++);
+ VideoBuffer texture(w, 12);
+ texture.AddCharacter(0, 0, c, r, g, b, a);
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, textTexture);
+ glBlendFunc(GL_ONE, GL_ONE);
+
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.Width, texture.Height, 0, GL_BGRA, GL_UNSIGNED_BYTE, texture.Buffer);
+ glBegin(GL_QUADS);
+ glTexCoord2d(0, 0);
+ glVertex2f(x, y);
+ glTexCoord2d(1, 0);
+ glVertex2f(x+texture.Width, y);
+ glTexCoord2d(1, 1);
+ glVertex2f(x+texture.Width, y+texture.Height);
+ glTexCoord2d(0, 1);
+ glVertex2f(x, y+texture.Height);
+ glEnd();
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ return x + w;
+}
+
+TPT_INLINE void PIXELMETHODS_CLASS::xor_pixel(int x, int y)
+{
+ //OpenGL doesn't support single pixel manipulation, there are ways around it, but with poor performance
+}
+
+TPT_INLINE void PIXELMETHODS_CLASS::blendpixel(int x, int y, int r, int g, int b, int a)
+{
+ //OpenGL doesn't support single pixel manipulation, there are ways around it, but with poor performance
+}
+
+TPT_INLINE void PIXELMETHODS_CLASS::addpixel(int x, int y, int r, int g, int b, int a)
+{
+ //OpenGL doesn't support single pixel manipulation, there are ways around it, but with poor performance
+}
+
+void PIXELMETHODS_CLASS::xor_line(int x, int y, int x2, int y2)
+{
+ glEnable(GL_COLOR_LOGIC_OP);
+ //glEnable(GL_LINE_SMOOTH);
+ glLogicOp(GL_XOR);
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glBegin(GL_LINES);
+ glVertex2i(x, y);
+ glVertex2i(x2, y2);
+ glEnd();
+ glDisable(GL_COLOR_LOGIC_OP);
+}
+
+void PIXELMETHODS_CLASS::xor_rect(int x, int y, int width, int height)
+{
+ glEnable(GL_COLOR_LOGIC_OP);
+ //glEnable(GL_LINE_SMOOTH);
+ glLogicOp(GL_XOR);
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glBegin(GL_LINE_STRIP);
+ glVertex2i(x, y);
+ glVertex2i(x+width, y);
+ glVertex2i(x+width, y+height);
+ glVertex2i(x, y+height);
+ glVertex2i(x, y);
+ glEnd();
+ glDisable(GL_COLOR_LOGIC_OP);
+}
+
+void PIXELMETHODS_CLASS::xor_bitmap(unsigned char * bitmap, int x, int y, int w, int h)
+{
+ //glEnable(GL_COLOR_LOGIC_OP);
+ //glLogicOp(GL_XOR);
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, textTexture);
+
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap);
+
+ glBegin(GL_QUADS);
+ glTexCoord2d(0, 0);
+ glVertex2f(x, y);
+ glTexCoord2d(1, 0);
+ glVertex2f(x+w, y);
+ glTexCoord2d(1, 1);
+ glVertex2f(x+w, y+h);
+ glTexCoord2d(0, 1);
+ glVertex2f(x, y+h);
+ glEnd();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+ //glDisable(GL_COLOR_LOGIC_OP);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+}
+
+void PIXELMETHODS_CLASS::draw_line(int x, int y, int x2, int y2, int r, int g, int b, int a)
+{
+ a = 255;
+ glColor4ub(r, g, b, a);
+ glBegin(GL_LINES);
+ glVertex2i(x, y);
+ glVertex2i(x2, y2);
+ glEnd();
+}
+
+void PIXELMETHODS_CLASS::drawrect(int x, int y, int width, int height, int r, int g, int b, int a)
+{
+ float fx = float(x)+0.5f;
+ float fy = float(y)+0.5f;
+ float fwidth = width-1.0f;
+ float fheight = height-1.0f;
+ //x++;
+ //y++;
+ //height-=2;
+ //width-=2;
+ glColor4ub(r, g, b, a);
+ glBegin(GL_LINE_STRIP);
+ glVertex2f(fx, fy);
+ glVertex2f(fx+fwidth, fy);
+ glVertex2f(fx+fwidth, fy+fheight);
+ glVertex2f(fx, fy+fheight); //+1 is a hack to prevent squares from missing their corners, will make smoothed lines look like SHIT
+ glVertex2f(fx, fy);
+ glEnd();
+}
+
+void PIXELMETHODS_CLASS::fillrect(int x, int y, int width, int height, int r, int g, int b, int a)
+{
+ /*x++;
+ y++;
+ width-=1;
+ height-=1;*/
+
+ glColor4ub(r, g, b, a);
+ glBegin(GL_QUADS);
+ glVertex2i(x, y);
+ glVertex2i(x+width, y);
+ glVertex2i(x+width, y+height);
+ glVertex2i(x, y+height);
+ glEnd();
+}
+
+void PIXELMETHODS_CLASS::gradientrect(int x, int y, int width, int height, int r, int g, int b, int a, int r2, int g2, int b2, int a2)
+{
+ glBegin(GL_QUADS);
+ glColor4ub(r, g, b, a);
+ glVertex2i(x, y);
+ glColor4ub(r2, g2, b2, a2);
+ glVertex2i(x+width, y);
+ glColor4ub(r2, g2, b2, a2);
+ glVertex2i(x+width, y+height);
+ glColor4ub(r, g, b, a);
+ glVertex2i(x, y+height);
+ glEnd();
+}
+
+void PIXELMETHODS_CLASS::clearrect(int x, int y, int width, int height)
+{
+ glColor4ub(0, 0, 0, 255);
+ glBegin(GL_QUADS);
+ glVertex2i(x, y);
+ glVertex2i(x+width, y);
+ glVertex2i(x+width, y+height);
+ glVertex2i(x, y+height);
+ glEnd();
+}
+
+void PIXELMETHODS_CLASS::draw_image(pixel *img, int x, int y, int w, int h, int a)
+{
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, textTexture);
+
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, img);
+ glBegin(GL_QUADS);
+ glTexCoord2d(0, 0);
+ glVertex2f(x, y);
+ glTexCoord2d(1, 0);
+ glVertex2f(x+w, y);
+ glTexCoord2d(1, 1);
+ glVertex2f(x+w, y+h);
+ glTexCoord2d(0, 1);
+ glVertex2f(x, y+h);
+ glEnd();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+}
diff --git a/src/graphics/OpenGLGraphics.cpp b/src/graphics/OpenGLGraphics.cpp
new file mode 100644
index 0000000..4f1d5d4
--- /dev/null
+++ b/src/graphics/OpenGLGraphics.cpp
@@ -0,0 +1,95 @@
+#include "Graphics.h"
+#include "font.h"
+#include <pthread.h>
+#ifdef GetUserName
+#undef GetUserName //God dammit microsoft!
+#endif
+#ifdef OGLI
+
+static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
+//static pthread_mutex_t TMPMUT = PTHREAD_MUTEX_INITIALIZER;
+Graphics::Graphics():
+ sdl_scale(1)
+{
+// if(gMutex == TMPMUT)
+// pthread_mutex_init (&gMutex, NULL);
+ Reset();
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ //Texture for main UI
+ glEnable(GL_TEXTURE_2D);
+
+ glGenTextures(1, &vidBuf);
+ glBindTexture(GL_TEXTURE_2D, vidBuf);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, XRES+BARSIZE, YRES+MENUSIZE, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glGenTextures(1, &textTexture);
+ glBindTexture(GL_TEXTURE_2D, textTexture);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glDisable(GL_TEXTURE_2D);
+}
+
+void Graphics::Acquire()
+{
+ pthread_mutex_lock(&gMutex);
+}
+
+void Graphics::Release()
+{
+ pthread_mutex_unlock(&gMutex);
+}
+
+Graphics::~Graphics()
+{
+}
+
+void Graphics::Reset()
+{
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ //glOrtho(0, (XRES+BARSIZE)*sdl_scale, 0, (YRES+MENUSIZE)*sdl_scale, -1, 1);
+ glOrtho(0, (XRES+BARSIZE)*sdl_scale, (YRES+MENUSIZE)*sdl_scale, 0, -1, 1);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ //glRasterPos2i(0, (YRES+MENUSIZE));
+ glRasterPos2i(0, 0);
+ glPixelZoom(1, 1);
+}
+
+void Graphics::Clear()
+{
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void Graphics::Finalise()
+{
+ glFlush();
+}
+
+#define VIDXRES XRES+BARSIZE
+#define VIDYRES YRES+MENUSIZE
+#define PIXELMETHODS_CLASS Graphics
+#include "OpenGLDrawMethods.inl"
+#undef VIDYRES
+#undef VIDXRES
+#undef PIXELMETHODS_CLASS
+
+
+#endif
diff --git a/src/graphics/OpenGLHeaders.h b/src/graphics/OpenGLHeaders.h
new file mode 100644
index 0000000..de692e0
--- /dev/null
+++ b/src/graphics/OpenGLHeaders.h
@@ -0,0 +1,24 @@
+#ifdef MACOSX
+
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+#include <OpenGL/gl3.h>
+#include <OpenGL/glu.h>
+#else
+//#include <GL/glew.h>
+#include <OpenGL/gl.h>
+#include <OpenGL/glu.h>
+#define GL_RGBA32F 0x8814
+#endif
+
+#elif defined(WIN)
+
+#include <GL/glew.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#else
+
+#include <GL/glew.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+#endif
diff --git a/src/graphics/RasterDrawMethods.inl b/src/graphics/RasterDrawMethods.inl
new file mode 100644
index 0000000..55a64c9
--- /dev/null
+++ b/src/graphics/RasterDrawMethods.inl
@@ -0,0 +1,366 @@
+#include "font.h"
+
+int PIXELMETHODS_CLASS::drawtext(int x, int y, const char *s, int r, int g, int b, int a)
+{
+ if(!strlen(s))
+ return 0;
+ int width, height;
+
+ int invert = 0;
+ int oR = r, oG = g, oB = b;
+ int characterX = x, characterY = y;
+ int startX = characterX;
+ for (; *s; s++)
+ {
+ if (*s == '\n')
+ {
+ characterX = startX;
+ characterY += FONT_H+2;
+ }
+ else if (*s == '\x0F')
+ {
+ if(!s[1] || !s[2] || !s[3]) break;
+ oR = r;
+ oG = g;
+ oB = b;
+ r = (unsigned char)s[1];
+ g = (unsigned char)s[2];
+ b = (unsigned char)s[3];
+ s += 3;
+ }
+ else if (*s == '\x0E')
+ {
+ r = oR;
+ g = oG;
+ b = oB;
+ }
+ else if (*s == '\x01')
+ {
+ invert = !invert;
+ r = 255-r;
+ g = 255-g;
+ b = 255-b;
+ }
+ else if (*s == '\b')
+ {
+ if(!s[1]) break;
+ switch (s[1])
+ {
+ case 'w':
+ r = g = b = 255;
+ break;
+ case 'g':
+ r = g = b = 192;
+ break;
+ case 'o':
+ r = 255;
+ g = 216;
+ b = 32;
+ break;
+ case 'r':
+ r = 255;
+ g = b = 0;
+ break;
+ case 'l':
+ r = 255;
+ g = b = 75;
+ break;
+ case 'b':
+ r = g = 0;
+ b = 255;
+ break;
+ case 't':
+ b = 255;
+ g = 170;
+ r = 32;
+ break;
+ }
+ if(invert)
+ {
+ r = 255-r;
+ g = 255-g;
+ b = 255-b;
+ }
+ s++;
+ }
+ else
+ {
+ characterX = drawchar(characterX, characterY, *(unsigned char *)s, r, g, b, a);
+ }
+ }
+ return x;
+}
+
+int PIXELMETHODS_CLASS::drawtext(int x, int y, std::string s, int r, int g, int b, int a)
+{
+ return drawtext(x, y, s.c_str(), r, g, b, a);
+}
+
+TPT_INLINE int PIXELMETHODS_CLASS::drawchar(int x, int y, int c, int r, int g, int b, int a)
+{
+ int i, j, w, bn = 0, ba = 0;
+ char *rp = font_data + font_ptrs[c];
+ w = *(rp++);
+ for (j=0; j<FONT_H; j++)
+ for (i=0; i<w; i++)
+ {
+ if (!bn)
+ {
+ ba = *(rp++);
+ bn = 8;
+ }
+ blendpixel(x+i, y+j, r, g, b, ((ba&3)*a)/3);
+ ba >>= 2;
+ bn -= 2;
+ }
+ return x + w;
+}
+
+TPT_NO_INLINE int PIXELMETHODS_CLASS::addchar(int x, int y, int c, int r, int g, int b, int a)
+{
+ int i, j, w, bn = 0, ba = 0;
+ char *rp = font_data + font_ptrs[c];
+ w = *(rp++);
+ for (j=0; j<FONT_H; j++)
+ for (i=0; i<w; i++)
+ {
+ if (!bn)
+ {
+ ba = *(rp++);
+ bn = 8;
+ }
+ {
+ addpixel(x+i, y+j, r, g, b, ((ba&3)*a)/3);
+ }
+ ba >>= 2;
+ bn -= 2;
+ }
+ return x + w;
+}
+
+TPT_INLINE void PIXELMETHODS_CLASS::xor_pixel(int x, int y)
+{
+ int c;
+ if (x<0 || y<0 || x>=XRES || y>=YRES)
+ return;
+ c = vid[y*(VIDXRES)+x];
+ c = PIXB(c) + 3*PIXG(c) + 2*PIXR(c);
+ if (c<512)
+ vid[y*(VIDXRES)+x] = PIXPACK(0xC0C0C0);
+ else
+ vid[y*(VIDXRES)+x] = PIXPACK(0x404040);
+}
+
+TPT_INLINE void PIXELMETHODS_CLASS::blendpixel(int x, int y, int r, int g, int b, int a)
+{
+ pixel t;
+ if (x<0 || y<0 || x>=VIDXRES || y>=VIDYRES)
+ return;
+ if (a!=255)
+ {
+ t = vid[y*(VIDXRES)+x];
+ r = (a*r + (255-a)*PIXR(t)) >> 8;
+ g = (a*g + (255-a)*PIXG(t)) >> 8;
+ b = (a*b + (255-a)*PIXB(t)) >> 8;
+ }
+ vid[y*(VIDXRES)+x] = PIXRGB(r,g,b);
+}
+
+TPT_INLINE void PIXELMETHODS_CLASS::addpixel(int x, int y, int r, int g, int b, int a)
+{
+ pixel t;
+ if (x<0 || y<0 || x>=VIDXRES || y>=VIDYRES)
+ return;
+ t = vid[y*(VIDXRES)+x];
+ r = (a*r + 255*PIXR(t)) >> 8;
+ g = (a*g + 255*PIXG(t)) >> 8;
+ b = (a*b + 255*PIXB(t)) >> 8;
+ if (r>255)
+ r = 255;
+ if (g>255)
+ g = 255;
+ if (b>255)
+ b = 255;
+ vid[y*(VIDXRES)+x] = PIXRGB(r,g,b);
+}
+
+void PIXELMETHODS_CLASS::xor_line(int x1, int y1, int x2, int y2)
+{
+ int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy;
+ float e, de;
+ if (cp)
+ {
+ y = x1;
+ x1 = y1;
+ y1 = y;
+ y = x2;
+ x2 = y2;
+ y2 = y;
+ }
+ if (x1 > x2)
+ {
+ y = x1;
+ x1 = x2;
+ x2 = y;
+ y = y1;
+ y1 = y2;
+ y2 = y;
+ }
+ dx = x2 - x1;
+ dy = abs(y2 - y1);
+ e = 0.0f;
+ if (dx)
+ de = dy/(float)dx;
+ else
+ de = 0.0f;
+ y = y1;
+ sy = (y1<y2) ? 1 : -1;
+ for (x=x1; x<=x2; x++)
+ {
+ if (cp)
+ xor_pixel(y, x);
+ else
+ xor_pixel(x, y);
+ e += de;
+ if (e >= 0.5f)
+ {
+ y += sy;
+ e -= 1.0f;
+ }
+ }
+}
+
+void PIXELMETHODS_CLASS::xor_rect(int x, int y, int w, int h)
+{
+ int i;
+ for (i=0; i<w; i+=2)
+ {
+ xor_pixel(x+i, y);
+ xor_pixel(x+i, y+h-1);
+ }
+ for (i=2; i<h; i+=2)
+ {
+ xor_pixel(x, y+i);
+ xor_pixel(x+w-1, y+i);
+ }
+}
+
+void PIXELMETHODS_CLASS::xor_bitmap(unsigned char * bitmap, int x, int y, int w, int h)
+{
+ for(int x1 = 0; x1 < w; x1++)
+ {
+ for(int y1 = 0; y1 < h; y1++)
+ {
+ if(bitmap[y1*w+x1])
+ xor_pixel(x+x1, y+y1);
+ }
+ }
+}
+
+void PIXELMETHODS_CLASS::draw_line(int x1, int y1, int x2, int y2, int r, int g, int b, int a)
+{
+ int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy;
+ float e, de;
+ if (cp)
+ {
+ y = x1;
+ x1 = y1;
+ y1 = y;
+ y = x2;
+ x2 = y2;
+ y2 = y;
+ }
+ if (x1 > x2)
+ {
+ y = x1;
+ x1 = x2;
+ x2 = y;
+ y = y1;
+ y1 = y2;
+ y2 = y;
+ }
+ dx = x2 - x1;
+ dy = abs(y2 - y1);
+ e = 0.0f;
+ if (dx)
+ de = dy/(float)dx;
+ else
+ de = 0.0f;
+ y = y1;
+ sy = (y1<y2) ? 1 : -1;
+ for (x=x1; x<=x2; x++)
+ {
+ if (cp)
+ blendpixel(y, x, r, g, b, a);
+ else
+ blendpixel(x, y, r, g, b, a);
+ e += de;
+ if (e >= 0.5f)
+ {
+ y += sy;
+ e -= 1.0f;
+ }
+ }
+}
+
+void PIXELMETHODS_CLASS::drawrect(int x, int y, int w, int h, int r, int g, int b, int a)
+{
+ int i;
+ w--;
+ h--;
+ for (i=0; i<=w; i++)
+ {
+ blendpixel(x+i, y, r, g, b, a);
+ blendpixel(x+i, y+h, r, g, b, a);
+ }
+ for (i=1; i<h; i++)
+ {
+ blendpixel(x, y+i, r, g, b, a);
+ blendpixel(x+w, y+i, r, g, b, a);
+ }
+}
+
+void PIXELMETHODS_CLASS::fillrect(int x, int y, int w, int h, int r, int g, int b, int a)
+{
+ int i,j;
+ for (j=0; j<h; j++)
+ for (i=0; i<w; i++)
+ blendpixel(x+i, y+j, r, g, b, a);
+}
+
+void PIXELMETHODS_CLASS::gradientrect(int x, int y, int width, int height, int r, int g, int b, int a, int r2, int g2, int b2, int a2)
+{
+
+}
+
+void PIXELMETHODS_CLASS::clearrect(int x, int y, int w, int h)
+{
+ int i;
+ for (i=1; i<h; i++)
+ memset(vid+(x+1+(VIDXRES)*(y+i)), 0, PIXELSIZE*(w-1));
+}
+
+void PIXELMETHODS_CLASS::draw_image(pixel *img, int x, int y, int w, int h, int a)
+{
+ int i, j, r, g, b;
+ if (!img) return;
+ if(y + h > VIDYRES) h = ((VIDYRES)-y)-1; //Adjust height to prevent drawing off the bottom
+ if(!h || y < 0) return;
+ if(a >= 255)
+ for (j=0; j<h; j++)
+ for (i=0; i<w; i++)
+ {
+ vid[(y+j)*(VIDXRES)+(x+i)] = *img;
+ img++;
+ }
+ else
+ for (j=0; j<h; j++)
+ for (i=0; i<w; i++)
+ {
+ r = PIXR(*img);
+ g = PIXG(*img);
+ b = PIXB(*img);
+ blendpixel(x+i, y+j, r, g, b, a);
+ img++;
+ }
+} \ No newline at end of file
diff --git a/src/graphics/RasterGraphics.cpp b/src/graphics/RasterGraphics.cpp
new file mode 100644
index 0000000..2a485e5
--- /dev/null
+++ b/src/graphics/RasterGraphics.cpp
@@ -0,0 +1,45 @@
+#include "Graphics.h"
+
+#ifndef OGLI
+
+Graphics::Graphics():
+sdl_scale(1)
+{
+ vid = (pixel *)malloc(PIXELSIZE * ((XRES+BARSIZE) * (YRES+MENUSIZE)));
+
+}
+
+void Graphics::Acquire()
+{
+
+}
+
+void Graphics::Release()
+{
+
+}
+
+Graphics::~Graphics()
+{
+ free(vid);
+}
+
+void Graphics::Clear()
+{
+ memset(vid, 0, PIXELSIZE * ((XRES+BARSIZE) * (YRES+MENUSIZE)));
+}
+
+void Graphics::Finalise()
+{
+
+}
+
+#define VIDXRES XRES+BARSIZE
+#define VIDYRES YRES+MENUSIZE
+#define PIXELMETHODS_CLASS Graphics
+#include "RasterDrawMethods.inl"
+#undef VIDYRES
+#undef VIDXRES
+#undef PIXELMETHODS_CLASS
+
+#endif
diff --git a/src/graphics/Renderer.cpp b/src/graphics/Renderer.cpp
new file mode 100644
index 0000000..eaec74c
--- /dev/null
+++ b/src/graphics/Renderer.cpp
@@ -0,0 +1,2719 @@
+/*
+ * Renderer.cpp
+ *
+ * Created on: Jan 7, 2012
+ * Author: Simon
+ */
+#include <cmath>
+#include <iostream>
+#include <vector>
+#include <stdio.h>
+#include <stdlib.h>
+#include "Config.h"
+#include "Renderer.h"
+#include "Graphics.h"
+#include "simulation/Elements.h"
+#include "simulation/ElementGraphics.h"
+#include "simulation/Air.h"
+extern "C"
+{
+#include "hmap.h"
+#ifdef OGLR
+#include "Shaders.h"
+#endif
+}
+
+#ifndef OGLI
+#define VIDXRES (XRES+BARSIZE)
+#define VIDYRES (YRES+MENUSIZE)
+#else
+#define VIDXRES XRES
+#define VIDYRES YRES
+#endif
+
+
+void Renderer::RenderBegin()
+{
+#ifdef OGLI
+#ifdef OGLR
+ draw_air();
+ draw_grav();
+ DrawWalls();
+ render_parts();
+ render_fire();
+ draw_other();
+ draw_grav_zones();
+ DrawSigns();
+
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo);
+ glTranslated(0, MENUSIZE, 0);
+#else
+ if(display_mode & DISPLAY_PERS)
+ {
+ std::copy(persistentVid, persistentVid+(VIDXRES*YRES), vid);
+ }
+ pixel * oldVid;
+ if(display_mode & DISPLAY_WARP)
+ {
+ oldVid = vid;
+ vid = warpVid;
+ std::fill(warpVid, warpVid+(VIDXRES*VIDYRES), 0);
+ }
+
+ draw_air();
+ draw_grav();
+ DrawWalls();
+ render_parts();
+
+ if(display_mode & DISPLAY_PERS)
+ {
+ int i,r,g,b;
+ for (i = 0; i < VIDXRES*YRES; i++)
+ {
+ r = PIXR(vid[i]);
+ g = PIXG(vid[i]);
+ b = PIXB(vid[i]);
+ if (r>0)
+ r--;
+ if (g>0)
+ g--;
+ if (b>0)
+ b--;
+ persistentVid[i] = PIXRGB(r,g,b);
+ }
+ }
+
+ render_fire();
+ draw_other();
+ draw_grav_zones();
+ DrawSigns();
+ if(display_mode & DISPLAY_WARP)
+ {
+ vid = oldVid;
+ }
+#endif
+#else
+ if(display_mode & DISPLAY_PERS)
+ {
+ std::copy(persistentVid, persistentVid+(VIDXRES*YRES), vid);
+ }
+ pixel * oldVid;
+ if(display_mode & DISPLAY_WARP)
+ {
+ oldVid = vid;
+ vid = warpVid;
+ std::fill(warpVid, warpVid+(VIDXRES*VIDYRES), 0);
+ }
+
+ draw_air();
+ draw_grav();
+ DrawWalls();
+ render_parts();
+ if(display_mode & DISPLAY_PERS)
+ {
+ int i,r,g,b;
+ for (i = 0; i < VIDXRES*YRES; i++)
+ {
+ r = PIXR(vid[i]);
+ g = PIXG(vid[i]);
+ b = PIXB(vid[i]);
+ if (r>0)
+ r--;
+ if (g>0)
+ g--;
+ if (b>0)
+ b--;
+ persistentVid[i] = PIXRGB(r,g,b);
+ }
+ }
+
+ render_fire();
+ draw_other();
+ draw_grav_zones();
+ DrawSigns();
+
+ if(display_mode & DISPLAY_WARP)
+ {
+ vid = oldVid;
+ }
+
+ FinaliseParts();
+#endif
+}
+
+void Renderer::RenderEnd()
+{
+#ifdef OGLI
+#ifdef OGLR
+ glTranslated(0, -MENUSIZE, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);
+ FinaliseParts();
+ RenderZoom();
+#else
+ RenderZoom();
+ FinaliseParts();
+#endif
+#else
+ RenderZoom();
+#endif
+}
+
+void Renderer::clearScreen(float alpha)
+{
+#ifdef OGLR
+ GLint prevFbo;
+ if(alpha > 0.999f)
+ {
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);
+ }
+ else
+ {
+ glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
+ glColor4f(1.0f, 1.0f, 1.0f, alpha);
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo);
+ glBegin(GL_QUADS);
+ glVertex2f(0, 0);
+ glVertex2f(XRES, 0);
+ glVertex2f(XRES, YRES);
+ glVertex2f(0, YRES);
+ glEnd();
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);
+ glBlendEquation(GL_FUNC_ADD);
+ }
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+#endif
+#ifdef OGLI
+#ifndef OGLR
+ std::fill(vid, vid+(VIDXRES*VIDYRES), 0);
+#endif
+#else
+ g->Clear();
+#endif
+}
+#ifdef OGLR
+void Renderer::checkShader(GLuint shader, char * shname)
+{
+ GLint status;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (status == GL_FALSE)
+ {
+ char errorBuf[ GL_INFO_LOG_LENGTH];
+ int errLen;
+ glGetShaderInfoLog(shader, GL_INFO_LOG_LENGTH, &errLen, errorBuf);
+ fprintf(stderr, "Failed to compile %s shader:\n%s\n", shname, errorBuf);
+ exit(1);
+ }
+}
+void Renderer::checkProgram(GLuint program, char * progname)
+{
+ GLint status;
+ glGetProgramiv(program, GL_LINK_STATUS, &status);
+ if (status == GL_FALSE)
+ {
+ char errorBuf[ GL_INFO_LOG_LENGTH];
+ int errLen;
+ glGetShaderInfoLog(program, GL_INFO_LOG_LENGTH, &errLen, errorBuf);
+ fprintf(stderr, "Failed to link %s program:\n%s\n", progname, errorBuf);
+ exit(1);
+ }
+}
+void Renderer::loadShaders()
+{
+ GLuint vertexShader, fragmentShader;
+
+ //Particle texture
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ glShaderSource( vertexShader, 1, &fireVertex, NULL);
+ glShaderSource( fragmentShader, 1, &fireFragment, NULL);
+
+ glCompileShader( vertexShader );
+ checkShader(vertexShader, "FV");
+ glCompileShader( fragmentShader );
+ checkShader(fragmentShader, "FF");
+
+ fireProg = glCreateProgram();
+ glAttachShader( fireProg, vertexShader );
+ glAttachShader( fireProg, fragmentShader );
+ glLinkProgram( fireProg );
+ checkProgram(fireProg, "F");
+
+ //Lensing
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ glShaderSource( vertexShader, 1, &lensVertex, NULL);
+ glShaderSource( fragmentShader, 1, &lensFragment, NULL);
+
+ glCompileShader( vertexShader );
+ checkShader(vertexShader, "LV");
+ glCompileShader( fragmentShader );
+ checkShader(fragmentShader, "LF");
+
+ lensProg = glCreateProgram();
+ glAttachShader( lensProg, vertexShader );
+ glAttachShader( lensProg, fragmentShader );
+ glLinkProgram( lensProg );
+ checkProgram(lensProg, "L");
+
+ //Air Velocity
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ glShaderSource( vertexShader, 1, &airVVertex, NULL);
+ glShaderSource( fragmentShader, 1, &airVFragment, NULL);
+
+ glCompileShader( vertexShader );
+ checkShader(vertexShader, "AVX");
+ glCompileShader( fragmentShader );
+ checkShader(fragmentShader, "AVF");
+
+ airProg_Velocity = glCreateProgram();
+ glAttachShader( airProg_Velocity, vertexShader );
+ glAttachShader( airProg_Velocity, fragmentShader );
+ glLinkProgram( airProg_Velocity );
+ checkProgram(airProg_Velocity, "AV");
+
+ //Air Pressure
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ glShaderSource( vertexShader, 1, &airPVertex, NULL);
+ glShaderSource( fragmentShader, 1, &airPFragment, NULL);
+
+ glCompileShader( vertexShader );
+ checkShader(vertexShader, "APV");
+ glCompileShader( fragmentShader );
+ checkShader(fragmentShader, "APF");
+
+ airProg_Pressure = glCreateProgram();
+ glAttachShader( airProg_Pressure, vertexShader );
+ glAttachShader( airProg_Pressure, fragmentShader );
+ glLinkProgram( airProg_Pressure );
+ checkProgram(airProg_Pressure, "AP");
+
+ //Air cracker
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ glShaderSource( vertexShader, 1, &airCVertex, NULL);
+ glShaderSource( fragmentShader, 1, &airCFragment, NULL);
+
+ glCompileShader( vertexShader );
+ checkShader(vertexShader, "ACV");
+ glCompileShader( fragmentShader );
+ checkShader(fragmentShader, "ACF");
+
+ airProg_Cracker = glCreateProgram();
+ glAttachShader( airProg_Cracker, vertexShader );
+ glAttachShader( airProg_Cracker, fragmentShader );
+ glLinkProgram( airProg_Cracker );
+ checkProgram(airProg_Cracker, "AC");
+}
+#endif
+
+void Renderer::FinaliseParts()
+{
+#ifdef OGLR
+ glEnable( GL_TEXTURE_2D );
+ if(display_mode & DISPLAY_WARP)
+ {
+ float xres = XRES, yres = YRES;
+ glUseProgram(lensProg);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, partsFboTex);
+ glUniform1i(glGetUniformLocation(lensProg, "pTex"), 0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, partsTFX);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XRES/CELL, YRES/CELL, GL_RED, GL_FLOAT, sim->gravx);
+ glUniform1i(glGetUniformLocation(lensProg, "tfX"), 1);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, partsTFY);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XRES/CELL, YRES/CELL, GL_GREEN, GL_FLOAT, sim->gravy);
+ glUniform1i(glGetUniformLocation(lensProg, "tfY"), 2);
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1fv(glGetUniformLocation(lensProg, "xres"), 1, &xres);
+ glUniform1fv(glGetUniformLocation(lensProg, "yres"), 1, &yres);
+ }
+ else
+ {
+ glBindTexture(GL_TEXTURE_2D, partsFboTex);
+ glBlendFunc(GL_ONE, GL_ONE);
+ }
+
+ int sdl_scale = 1;
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glBegin(GL_QUADS);
+ glTexCoord2d(1, 0);
+ //glVertex3f(XRES*sdl_scale, (YRES+MENUSIZE)*sdl_scale, 1.0);
+ glVertex3f(XRES*sdl_scale, YRES*sdl_scale, 1.0);
+ glTexCoord2d(0, 0);
+ //glVertex3f(0, (YRES+MENUSIZE)*sdl_scale, 1.0);
+ glVertex3f(0, YRES*sdl_scale, 1.0);
+ glTexCoord2d(0, 1);
+ //glVertex3f(0, MENUSIZE*sdl_scale, 1.0);
+ glVertex3f(0, 0, 1.0);
+ glTexCoord2d(1, 1);
+ //glVertex3f(XRES*sdl_scale, MENUSIZE*sdl_scale, 1.0);
+ glVertex3f(XRES*sdl_scale, 0, 1.0);
+ glEnd();
+
+ if(display_mode & DISPLAY_WARP)
+ {
+ glUseProgram(0);
+
+ }
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable( GL_TEXTURE_2D );
+#endif
+
+#if defined(OGLI) && !defined(OGLR)
+ if(display_mode & DISPLAY_WARP)
+ {
+ render_gravlensing(warpVid);
+ }
+ g->draw_image(vid, 0, 0, VIDXRES, VIDYRES, 255);
+#endif
+
+#if !defined(OGLR) && !defined(OGLI)
+ if(display_mode & DISPLAY_WARP)
+ {
+ render_gravlensing(warpVid);
+ }
+#endif
+}
+
+void Renderer::RenderZoom()
+{
+ if(!zoomEnabled)
+ return;
+ #if defined(OGLR)
+ int sdl_scale = 1;
+ int origBlendSrc, origBlendDst;
+ float zcx1, zcx0, zcy1, zcy0, yfactor, xfactor, i; //X-Factor is shit, btw
+ xfactor = 1.0f/(float)XRES;
+ yfactor = 1.0f/(float)YRES;
+ yfactor*=-1.0f;
+
+ zcx1 = (zoomScopePosition.X)*xfactor;
+ zcx0 = (zoomScopePosition.X+zoomScopeSize)*xfactor;
+ zcy1 = (zoomScopePosition.Y-1)*yfactor;
+ zcy0 = ((zoomScopePosition.Y-1+zoomScopeSize))*yfactor;
+
+ glGetIntegerv(GL_BLEND_SRC, &origBlendSrc);
+ glGetIntegerv(GL_BLEND_DST, &origBlendDst);
+ glBlendFunc(GL_ONE, GL_ZERO);
+
+ glEnable( GL_TEXTURE_2D );
+ //glReadBuffer(GL_AUX0);
+ glBindTexture(GL_TEXTURE_2D, partsFboTex);
+
+ //Draw zoomed texture
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glBegin(GL_QUADS);
+ glTexCoord2d(zcx1, zcy1);
+ glVertex2i(zoomWindowPosition.X, zoomWindowPosition.Y);
+ glTexCoord2d(zcx0, zcy1);
+ glVertex2i(zoomWindowPosition.X+(zoomScopeSize*ZFACTOR), zoomWindowPosition.Y);
+ glTexCoord2d(zcx0, zcy0);
+ glVertex2i(zoomWindowPosition.X+(zoomScopeSize*ZFACTOR), zoomWindowPosition.Y+(zoomScopeSize*ZFACTOR));
+ glTexCoord2d(zcx1, zcy0);
+ glVertex2i(zoomWindowPosition.X, zoomWindowPosition.Y+(zoomScopeSize*ZFACTOR));
+ glEnd();
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable( GL_TEXTURE_2D );
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ //Lines to make the pixels stand out
+ glLineWidth(sdl_scale);
+ //glEnable(GL_LINE_SMOOTH);
+ glBegin(GL_LINES);
+ glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
+ for(i = 0; i < zoomScopeSize; i++)
+ {
+ //Across
+ glVertex2i(zoomWindowPosition.X, zoomWindowPosition.Y+(i*ZFACTOR));
+ glVertex2i(zoomWindowPosition.X+(zoomScopeSize*ZFACTOR), zoomWindowPosition.Y+(i*ZFACTOR));
+
+ //Down
+ glVertex2i(zoomWindowPosition.X+(i*ZFACTOR), zoomWindowPosition.Y);
+ glVertex2i(zoomWindowPosition.X+(i*ZFACTOR), zoomWindowPosition.Y+(zoomScopeSize*ZFACTOR));
+ }
+ glEnd();
+
+ //Draw zoom window border
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glBegin(GL_LINE_LOOP);
+ glVertex2i(zoomWindowPosition.X, zoomWindowPosition.Y);
+ glVertex2i(zoomWindowPosition.X+(zoomScopeSize*ZFACTOR), zoomWindowPosition.Y);
+ glVertex2i(zoomWindowPosition.X+(zoomScopeSize*ZFACTOR), zoomWindowPosition.Y+(zoomScopeSize*ZFACTOR));
+ glVertex2i(zoomWindowPosition.X, zoomWindowPosition.Y+(zoomScopeSize*ZFACTOR));
+ glEnd();
+ //glDisable(GL_LINE_SMOOTH);
+
+ if(zoomEnabled)
+ {
+ glEnable(GL_COLOR_LOGIC_OP);
+ //glEnable(GL_LINE_SMOOTH);
+ glLogicOp(GL_XOR);
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glBegin(GL_LINE_LOOP);
+ glVertex2i(zoomScopePosition.X, zoomScopePosition.Y);
+ glVertex2i(zoomScopePosition.X+zoomScopeSize, zoomScopePosition.Y);
+ glVertex2i(zoomScopePosition.X+zoomScopeSize, zoomScopePosition.Y+zoomScopeSize);
+ glVertex2i(zoomScopePosition.X, zoomScopePosition.Y+zoomScopeSize);
+ /*glVertex3i((zoomScopePosition.X-1)*sdl_scale, (YRES+MENUSIZE-(zoomScopePosition.Y-1))*sdl_scale, 0);
+ glVertex3i((zoomScopePosition.X-1)*sdl_scale, (YRES+MENUSIZE-(zoomScopePosition.Y+zoomScopeSize))*sdl_scale, 0);
+ glVertex3i((zoomScopePosition.X+zoomScopeSize)*sdl_scale, (YRES+MENUSIZE-(zoomScopePosition.Y+zoomScopeSize))*sdl_scale, 0);
+ glVertex3i((zoomScopePosition.X+zoomScopeSize)*sdl_scale, (YRES+MENUSIZE-(zoomScopePosition.Y-1))*sdl_scale, 0);
+ glVertex3i((zoomScopePosition.X-1)*sdl_scale, (YRES+MENUSIZE-(zoomScopePosition.Y-1))*sdl_scale, 0);*/
+ glEnd();
+ glDisable(GL_COLOR_LOGIC_OP);
+ }
+ glLineWidth(1);
+ glBlendFunc(origBlendSrc, origBlendDst);
+ #else
+ int x, y, i, j;
+ pixel pix;
+ pixel * img = vid;
+ clearrect(zoomWindowPosition.X-1, zoomWindowPosition.Y-1, zoomScopeSize*ZFACTOR+2, zoomScopeSize*ZFACTOR+2);
+ drawrect(zoomWindowPosition.X-2, zoomWindowPosition.Y-2, zoomScopeSize*ZFACTOR+4, zoomScopeSize*ZFACTOR+4, 192, 192, 192, 255);
+ drawrect(zoomWindowPosition.X-1, zoomWindowPosition.Y-1, zoomScopeSize*ZFACTOR+2, zoomScopeSize*ZFACTOR+2, 0, 0, 0, 255);
+ for (j=0; j<zoomScopeSize; j++)
+ for (i=0; i<zoomScopeSize; i++)
+ {
+ pix = img[(j+zoomScopePosition.Y)*(VIDXRES)+(i+zoomScopePosition.X)];
+ for (y=0; y<ZFACTOR-1; y++)
+ for (x=0; x<ZFACTOR-1; x++)
+ img[(j*ZFACTOR+y+zoomWindowPosition.Y)*(VIDXRES)+(i*ZFACTOR+x+zoomWindowPosition.X)] = pix;
+ }
+ if (zoomEnabled)
+ {
+ for (j=-1; j<=zoomScopeSize; j++)
+ {
+ xor_pixel(zoomScopePosition.X+j, zoomScopePosition.Y-1);
+ xor_pixel(zoomScopePosition.X+j, zoomScopePosition.Y+zoomScopeSize);
+ }
+ for (j=0; j<zoomScopeSize; j++)
+ {
+ xor_pixel(zoomScopePosition.X-1, zoomScopePosition.Y+j);
+ xor_pixel(zoomScopePosition.X+zoomScopeSize, zoomScopePosition.Y+j);
+ }
+ }
+ #endif
+}
+
+int Renderer_wtypesCount;
+wall_type * Renderer_wtypes = LoadWalls(Renderer_wtypesCount);
+
+
+VideoBuffer * Renderer::WallIcon(int wallID, int width, int height)
+{
+ int i, j, cr, cg, cb;
+ int wt = wallID;
+ if (wt<0 || wt>=Renderer_wtypesCount)
+ return 0;
+ wall_type *wtypes = Renderer_wtypes;
+ pixel pc = wtypes[wt].colour;
+ pixel gc = wtypes[wt].eglow;
+ VideoBuffer * newTexture = new VideoBuffer(width, height);
+ if (wtypes[wt].drawstyle==1)
+ {
+ for (j=0; j<height; j+=2)
+ for (i=(j>>1)&1; i<width; i+=2)
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ else if (wtypes[wt].drawstyle==2)
+ {
+ for (j=0; j<height; j+=2)
+ for (i=0; i<width; i+=2)
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ else if (wtypes[wt].drawstyle==3)
+ {
+ for (j=0; j<height; j++)
+ for (i=0; i<width; i++)
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ else if (wtypes[wt].drawstyle==4)
+ {
+ for (j=0; j<height; j++)
+ for (i=0; i<width; i++)
+ if(i%CELL == j%CELL)
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ else if (i%CELL == (j%CELL)+1 || (i%CELL == 0 && j%CELL == CELL-1))
+ newTexture->SetPixel(i, j, PIXR(gc), PIXG(gc), PIXB(gc), 255);
+ else
+ newTexture->SetPixel(i, j, 0x20, 0x20, 0x20, 255);
+ }
+
+ // special rendering for some walls
+ if (wt==WL_EWALL)
+ {
+ for (j=0; j<height; j++)
+ {
+ for (i=0; i<(width/4)+j; i++)
+ {
+ if (!(i&j&1))
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ for (; i<width; i++)
+ {
+ if (i&j&1)
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ }
+ }
+ else if (wt==WL_WALLELEC)
+ {
+ for (j=0; j<height; j++)
+ for (i=0; i<width; i++)
+ {
+ if (!(j%2) && !(i%2))
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ else
+ newTexture->SetPixel(i, j, 0x80, 0x80, 0x80, 255);
+ }
+ }
+ else if (wt==WL_EHOLE)
+ {
+ for (j=0; j<height; j++)
+ {
+ for (i=0; i<(width/4)+j; i++)
+ {
+ if (i&j&1)
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ for (; i<width; i++)
+ {
+ if (!(i&j&1))
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ }
+ }
+ else if (wt == WL_ERASE)
+ {
+ for (j=0; j<height; j+=2)
+ {
+ for (i=1+(1&(j>>1)); i<width/2; i+=2)
+ {
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ }
+ for (j=0; j<height; j++)
+ {
+ for (i=width/2; i<width; i++)
+ {
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ }
+ for (j=3; j<(width-4)/2; j++)
+ {
+ newTexture->SetPixel(j+6, j, 0xFF, 0, 0, 255);
+ newTexture->SetPixel(j+7, j, 0xFF, 0, 0, 255);
+ newTexture->SetPixel(-j+19, j, 0xFF, 0, 0, 255);
+ newTexture->SetPixel(-j+20, j, 0xFF, 0, 0, 255);
+ }
+ }
+ else if(wt == WL_STREAM)
+ {
+ for (j=0; j<height; j++)
+ {
+ for (i=0; i<width; i++)
+ {
+ pc = i==0||i==width-1||j==0||j==height-1 ? PIXPACK(0xA0A0A0) : PIXPACK(0x000000);
+ newTexture->SetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255);
+ }
+ }
+ newTexture->SetCharacter(4, 2, 0x8D, 255, 255, 255, 255);
+ for (i=width/3; i<width; i++)
+ {
+ newTexture->SetPixel(i, 7+(int)(3.9f*cos(i*0.3f)), 255, 255, 255, 255);
+ }
+ }
+ return newTexture;
+}
+
+void Renderer::DrawWalls()
+{
+#ifdef OGLR
+ GLint prevFbo;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo);
+ glTranslated(0, MENUSIZE, 0);
+
+ int x, y, i, j, cr, cg, cb;
+ unsigned char wt;
+ pixel pc;
+ pixel gc;
+ unsigned char (*bmap)[XRES/CELL] = sim->bmap;
+ unsigned char (*emap)[XRES/CELL] = sim->emap;
+ wall_type *wtypes = sim->wtypes;
+ for (y=0; y<YRES/CELL; y++)
+ for (x=0; x<XRES/CELL; x++)
+ if (bmap[y][x])
+ {
+ wt = bmap[y][x];
+ if (wt<0 || wt>=UI_WALLCOUNT)
+ continue;
+ pc = wtypes[wt].colour;
+ gc = wtypes[wt].eglow;
+
+ cr = PIXR(pc);
+ cg = PIXG(pc);
+ cb = PIXB(pc);
+
+ fillrect(x*CELL, y*CELL, CELL, CELL, cr, cg, cb, 255);
+ }
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);
+ glTranslated(0, -MENUSIZE, 0);
+#else
+ int x, y, i, j, cr, cg, cb;
+ unsigned char wt;
+ pixel pc;
+ pixel gc;
+ unsigned char (*bmap)[XRES/CELL] = sim->bmap;
+ unsigned char (*emap)[XRES/CELL] = sim->emap;
+ wall_type *wtypes = sim->wtypes;
+ for (y=0; y<YRES/CELL; y++)
+ for (x=0; x<XRES/CELL; x++)
+ if (bmap[y][x])
+ {
+ wt = bmap[y][x];
+ if (wt<0 || wt>=UI_WALLCOUNT)
+ continue;
+ pc = wtypes[wt].colour;
+ gc = wtypes[wt].eglow;
+
+ // standard wall patterns
+ if (wtypes[wt].drawstyle==1)
+ {
+ for (j=0; j<CELL; j+=2)
+ for (i=(j>>1)&1; i<CELL; i+=2)
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc;
+ }
+ else if (wtypes[wt].drawstyle==2)
+ {
+ for (j=0; j<CELL; j+=2)
+ for (i=0; i<CELL; i+=2)
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc;
+ }
+ else if (wtypes[wt].drawstyle==3)
+ {
+ for (j=0; j<CELL; j++)
+ for (i=0; i<CELL; i++)
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc;
+ }
+ else if (wtypes[wt].drawstyle==4)
+ {
+ for (j=0; j<CELL; j++)
+ for (i=0; i<CELL; i++)
+ if(i == j)
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc;
+ else if (i == j+1 || (i == 0 && j == CELL-1))
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = gc;
+ else
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x202020);
+ }
+
+ // special rendering for some walls
+ if (wt==WL_EWALL)
+ {
+ if (emap[y][x])
+ {
+ for (j=0; j<CELL; j++)
+ for (i=0; i<CELL; i++)
+ if (i&j&1)
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc;
+ }
+ else
+ {
+ for (j=0; j<CELL; j++)
+ for (i=0; i<CELL; i++)
+ if (!(i&j&1))
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc;
+ }
+ }
+ else if (wt==WL_WALLELEC)
+ {
+ for (j=0; j<CELL; j++)
+ for (i=0; i<CELL; i++)
+ {
+ if (!((y*CELL+j)%2) && !((x*CELL+i)%2))
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc;
+ else
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x808080);
+ }
+ }
+ else if (wt==WL_EHOLE)
+ {
+ if (emap[y][x])
+ {
+ for (j=0; j<CELL; j++)
+ for (i=0; i<CELL; i++)
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x242424);
+ for (j=0; j<CELL; j+=2)
+ for (i=0; i<CELL; i+=2)
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x000000);
+ }
+ else
+ {
+ for (j=0; j<CELL; j+=2)
+ for (i=0; i<CELL; i+=2)
+ vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x242424);
+ }
+ }
+ else if (wt==WL_STREAM)
+ {
+ float lx, ly, nx, ny;
+ lx = x*CELL + CELL*0.5f;
+ ly = y*CELL + CELL*0.5f;
+ for (int t = 0; t < 1024; t++)
+ {
+ nx = (int)(lx+0.5f);
+ ny = (int)(ly+0.5f);
+ if (nx<0 || nx>=XRES || ny<0 || ny>=YRES)
+ break;
+ addpixel(nx, ny, 255, 255, 255, 64);
+ i = nx/CELL;
+ j = ny/CELL;
+ lx += sim->vx[j][i]*0.125f;
+ ly += sim->vy[j][i]*0.125f;
+ if (bmap[j][i]==WL_STREAM && i!=x && j!=y)
+ break;
+ }
+ drawtext(x*CELL, y*CELL-2, "\x8D", 255, 255, 255, 128);
+ }
+ if (wtypes[wt].eglow && emap[y][x])
+ {
+ // glow if electrified
+ pc = wtypes[wt].eglow;
+ cr = fire_r[y][x] + PIXR(pc);
+ if (cr > 255) cr = 255;
+ fire_r[y][x] = cr;
+ cg = fire_g[y][x] + PIXG(pc);
+ if (cg > 255) cg = 255;
+ fire_g[y][x] = cg;
+ cb = fire_b[y][x] + PIXB(pc);
+ if (cb > 255) cb = 255;
+ fire_b[y][x] = cb;
+
+ }
+ }
+#endif
+}
+
+void Renderer::DrawSigns()
+{
+ int i, j, x, y, w, h, dx, dy,mx,my,b=1,bq;
+ std::vector<sign> signs = sim->signs;
+#ifdef OGLR
+ GLint prevFbo;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo);
+ glTranslated(0, MENUSIZE, 0);
+#endif
+ for (i=0; i < signs.size(); i++)
+ if (signs[i].text.length())
+ {
+ char buff[256]; //Buffer
+ sim->signs[i].pos(x, y, w, h);
+ clearrect(x, y, w, h);
+ drawrect(x, y, w, h, 192, 192, 192, 255);
+
+ //Displaying special information
+ if (signs[i].text == "{p}")
+ {
+ float pressure = 0.0f;
+ if (signs[i].x>=0 && signs[i].x<XRES && signs[i].y>=0 && signs[i].y<YRES)
+ pressure = sim->pv[signs[i].y/CELL][signs[i].x/CELL];
+ sprintf(buff, "Pressure: %3.2f", pressure); //...pressure
+ drawtext(x+3, y+3, buff, 255, 255, 255, 255);
+ }
+ else if (signs[i].text == "{t}")
+ {
+ if (signs[i].x>=0 && signs[i].x<XRES && signs[i].y>=0 && signs[i].y<YRES && sim->pmap[signs[i].y][signs[i].x])
+ sprintf(buff, "Temp: %4.2f", sim->parts[sim->pmap[signs[i].y][signs[i].x]>>8].temp-273.15); //...temperature
+ else
+ sprintf(buff, "Temp: 0.00"); //...temperature
+ drawtext(x+3, y+3, buff, 255, 255, 255, 255);
+ }
+ else if (sregexp(signs[i].text.c_str(), "^{[c|t]:[0-9]*|.*}$")==0)
+ {
+ int sldr, startm;
+ memset(buff, 0, sizeof(buff));
+ for (sldr=3; signs[i].text[sldr-1] != '|'; sldr++)
+ startm = sldr + 1;
+ sldr = startm;
+ while (signs[i].text[sldr] != '}')
+ {
+ buff[sldr - startm] = signs[i].text[sldr];
+ sldr++;
+ }
+ drawtext(x+3, y+3, buff, 0, 191, 255, 255);
+ }
+ else
+ {
+ drawtext(x+3, y+3, signs[i].text, 255, 255, 255, 255);
+ }
+
+ x = signs[i].x;
+ y = signs[i].y;
+ dx = 1 - signs[i].ju;
+ dy = (signs[i].y > 18) ? -1 : 1;
+#ifdef OGLR
+ glBegin(GL_LINES);
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glVertex2i(x, y);
+ glVertex2i(x+(dx*4), y+(dy*4));
+ glEnd();
+#else
+ for (j=0; j<4; j++)
+ {
+ blendpixel(x, y, 192, 192, 192, 255);
+ x+=dx;
+ y+=dy;
+ }
+#endif
+ /*if (MSIGN==i)
+ {
+ bq = b;
+ b = SDL_GetMouseState(&mx, &my);
+ mx /= sdl_scale;
+ my /= sdl_scale;
+ signs[i].x = mx;
+ signs[i].y = my;
+ }*/
+ }
+#ifdef OGLR
+ glTranslated(0, -MENUSIZE, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);
+#endif
+}
+
+void Renderer::render_gravlensing(pixel * source)
+{
+#ifndef OGLR
+ int nx, ny, rx, ry, gx, gy, bx, by, co;
+ int r, g, b;
+ pixel t;
+ pixel *src = source;
+ pixel *dst = vid;
+ for(nx = 0; nx < XRES; nx++)
+ {
+ for(ny = 0; ny < YRES; ny++)
+ {
+ co = (ny/CELL)*(XRES/CELL)+(nx/CELL);
+ rx = (int)(nx-sim->gravx[co]*0.75f+0.5f);
+ ry = (int)(ny-sim->gravy[co]*0.75f+0.5f);
+ gx = (int)(nx-sim->gravx[co]*0.875f+0.5f);
+ gy = (int)(ny-sim->gravy[co]*0.875f+0.5f);
+ bx = (int)(nx-sim->gravx[co]+0.5f);
+ by = (int)(ny-sim->gravy[co]+0.5f);
+ if(rx > 0 && rx < XRES && ry > 0 && ry < YRES && gx > 0 && gx < XRES && gy > 0 && gy < YRES && bx > 0 && bx < XRES && by > 0 && by < YRES)
+ {
+ t = dst[ny*(VIDXRES)+nx];
+ r = PIXR(src[ry*(VIDXRES)+rx]) + PIXR(t);
+ g = PIXG(src[gy*(VIDXRES)+gx]) + PIXG(t);
+ b = PIXB(src[by*(VIDXRES)+bx]) + PIXB(t);
+ if (r>255)
+ r = 255;
+ if (g>255)
+ g = 255;
+ if (b>255)
+ b = 255;
+ dst[ny*(VIDXRES)+nx] = PIXRGB(r,g,b);
+ }
+ }
+ }
+#endif
+}
+
+void Renderer::render_fire()
+{
+#ifndef OGLR
+ if(!(render_mode & FIREMODE))
+ return;
+ int i,j,x,y,r,g,b,nx,ny;
+ for (j=0; j<YRES/CELL; j++)
+ for (i=0; i<XRES/CELL; i++)
+ {
+ r = fire_r[j][i];
+ g = fire_g[j][i];
+ b = fire_b[j][i];
+ if (r || g || b)
+ for (y=-CELL; y<2*CELL; y++)
+ for (x=-CELL; x<2*CELL; x++)
+ addpixel(i*CELL+x, j*CELL+y, r, g, b, fire_alpha[y+CELL][x+CELL]);
+ r *= 8;
+ g *= 8;
+ b *= 8;
+ for (y=-1; y<2; y++)
+ for (x=-1; x<2; x++)
+ if ((x || y) && i+x>=0 && j+y>=0 && i+x<XRES/CELL && j+y<YRES/CELL)
+ {
+ r += fire_r[j+y][i+x];
+ g += fire_g[j+y][i+x];
+ b += fire_b[j+y][i+x];
+ }
+ r /= 16;
+ g /= 16;
+ b /= 16;
+ fire_r[j][i] = r>4 ? r-4 : 0;
+ fire_g[j][i] = g>4 ? g-4 : 0;
+ fire_b[j][i] = b>4 ? b-4 : 0;
+ }
+#endif
+}
+
+float temp[CELL*3][CELL*3];
+float fire_alphaf[CELL*3][CELL*3];
+float glow_alphaf[11][11];
+float blur_alphaf[7][7];
+void Renderer::prepare_alpha(int size, float intensity)
+{
+ //TODO: implement size
+ int x,y,i,j,c;
+ float multiplier = 255.0f*intensity;
+
+ memset(temp, 0, sizeof(temp));
+ for (x=0; x<CELL; x++)
+ for (y=0; y<CELL; y++)
+ for (i=-CELL; i<CELL; i++)
+ for (j=-CELL; j<CELL; j++)
+ temp[y+CELL+j][x+CELL+i] += expf(-0.1f*(i*i+j*j));
+ for (x=0; x<CELL*3; x++)
+ for (y=0; y<CELL*3; y++)
+ fire_alpha[y][x] = (int)(multiplier*temp[y][x]/(CELL*CELL));
+
+#ifdef OGLR
+ memset(fire_alphaf, 0, sizeof(fire_alphaf));
+ for (x=0; x<CELL*3; x++)
+ for (y=0; y<CELL*3; y++)
+ {
+ fire_alphaf[y][x] = intensity*temp[y][x]/((float)(CELL*CELL));
+ }
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, fireAlpha);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, CELL*3, CELL*3, GL_ALPHA, GL_FLOAT, fire_alphaf);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ memset(glow_alphaf, 0, sizeof(glow_alphaf));
+
+ c = 5;
+
+ glow_alphaf[c][c-1] = 0.4f;
+ glow_alphaf[c][c+1] = 0.4f;
+ glow_alphaf[c-1][c] = 0.4f;
+ glow_alphaf[c+1][c] = 0.4f;
+ for (x = 1; x < 6; x++) {
+ glow_alphaf[c][c-x] += 0.02f;
+ glow_alphaf[c][c+x] += 0.02f;
+ glow_alphaf[c-x][c] += 0.02f;
+ glow_alphaf[c+x][c] += 0.02f;
+ for (y = 1; y < 6; y++) {
+ if(x + y > 7)
+ continue;
+ glow_alphaf[c+x][c-y] += 0.02f;
+ glow_alphaf[c-x][c+y] += 0.02f;
+ glow_alphaf[c+x][c+y] += 0.02f;
+ glow_alphaf[c-x][c-y] += 0.02f;
+ }
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, glowAlpha);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 11, 11, GL_ALPHA, GL_FLOAT, glow_alphaf);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ c = 3;
+
+ for (x=-3; x<4; x++)
+ {
+ for (y=-3; y<4; y++)
+ {
+ if (abs(x)+abs(y) <2 && !(abs(x)==2||abs(y)==2))
+ blur_alphaf[c+x][c-y] = 0.11f;
+ if (abs(x)+abs(y) <=3 && abs(x)+abs(y))
+ blur_alphaf[c+x][c-y] = 0.08f;
+ if (abs(x)+abs(y) == 2)
+ blur_alphaf[c+x][c-y] = 0.04f;
+ }
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, blurAlpha);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 7, 7, GL_ALPHA, GL_FLOAT, blur_alphaf);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+#endif
+}
+
+void Renderer::render_parts()
+{
+ int deca, decr, decg, decb, cola, colr, colg, colb, firea, firer, fireg, fireb, pixel_mode, q, i, t, nx, ny, x, y, caddress;
+ int orbd[4] = {0, 0, 0, 0}, orbl[4] = {0, 0, 0, 0};
+ float gradv, flicker, fnx, fny;
+ Particle * parts;
+ part_transition *ptransitions;
+ Element *elements;
+ if(!sim)
+ return;
+ parts = sim->parts;
+ elements = sim->elements;
+#ifdef OGLR
+ int cfireV = 0, cfireC = 0, cfire = 0;
+ int csmokeV = 0, csmokeC = 0, csmoke = 0;
+ int cblobV = 0, cblobC = 0, cblob = 0;
+ int cblurV = 0, cblurC = 0, cblur = 0;
+ int cglowV = 0, cglowC = 0, cglow = 0;
+ int cflatV = 0, cflatC = 0, cflat = 0;
+ int caddV = 0, caddC = 0, cadd = 0;
+ int clineV = 0, clineC = 0, cline = 0;
+ GLint origBlendSrc, origBlendDst, prevFbo;
+
+ glGetIntegerv(GL_BLEND_SRC, &origBlendSrc);
+ glGetIntegerv(GL_BLEND_DST, &origBlendDst);
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo);
+ //Render to the particle FBO
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo);
+ glTranslated(0, MENUSIZE, 0);
+#else
+ if (gridSize)//draws the grid
+ {
+ for (ny=0; ny<YRES; ny++)
+ for (nx=0; nx<XRES; nx++)
+ {
+ if (ny%(4*gridSize)==0)
+ blendpixel(nx, ny, 100, 100, 100, 80);
+ if (nx%(4*gridSize)==0)
+ blendpixel(nx, ny, 100, 100, 100, 80);
+ }
+ }
+#endif
+ for(i = 0; i<=sim->parts_lastActiveIndex; i++) {
+ if (sim->parts[i].type && sim->parts[i].type >= 0 && sim->parts[i].type < PT_NUM) {
+ t = sim->parts[i].type;
+
+ nx = (int)(sim->parts[i].x+0.5f);
+ ny = (int)(sim->parts[i].y+0.5f);
+ fnx = sim->parts[i].x;
+ fny = sim->parts[i].y;
+
+ if((sim->photons[ny][nx]&0xFF) && !(sim->elements[t].Properties & TYPE_ENERGY) && t!=PT_STKM && t!=PT_STKM2 && t!=PT_FIGH)
+ continue;
+ if(nx >= XRES || nx < 0 || ny >= YRES || ny < 0)
+ continue;
+
+ //Defaults
+ pixel_mode = 0 | PMODE_FLAT;
+ cola = 255;
+ colr = PIXR(elements[t].Colour);
+ colg = PIXG(elements[t].Colour);
+ colb = PIXB(elements[t].Colour);
+ firer = fireg = fireb = firea = 0;
+
+ deca = (sim->parts[i].dcolour>>24)&0xFF;
+ decr = (sim->parts[i].dcolour>>16)&0xFF;
+ decg = (sim->parts[i].dcolour>>8)&0xFF;
+ decb = (sim->parts[i].dcolour)&0xFF;
+
+ if(decorations_enable && blackDecorations)
+ {
+ if(deca < 250 || decr > 5 || decg > 5 || decb > 5)
+ deca = 0;
+ else
+ {
+ deca = 255;
+ decr = decg = decb = 0;
+ }
+ }
+
+ {
+ if (graphicscache[t].isready)
+ {
+ pixel_mode = graphicscache[t].pixel_mode;
+ cola = graphicscache[t].cola;
+ colr = graphicscache[t].colr;
+ colg = graphicscache[t].colg;
+ colb = graphicscache[t].colb;
+ firea = graphicscache[t].firea;
+ firer = graphicscache[t].firer;
+ fireg = graphicscache[t].fireg;
+ fireb = graphicscache[t].fireb;
+ }
+ else if(!(colour_mode & COLOUR_BASC))
+ {
+ if (elements[t].Graphics)
+ {
+ if ((*(elements[t].Graphics))(this, &(sim->parts[i]), nx, ny, &pixel_mode, &cola, &colr, &colg, &colb, &firea, &firer, &fireg, &fireb)) //That's a lot of args, a struct might be better
+ {
+ graphicscache[t].isready = 1;
+ graphicscache[t].pixel_mode = pixel_mode;
+ graphicscache[t].cola = cola;
+ graphicscache[t].colr = colr;
+ graphicscache[t].colg = colg;
+ graphicscache[t].colb = colb;
+ graphicscache[t].firea = firea;
+ graphicscache[t].firer = firer;
+ graphicscache[t].fireg = fireg;
+ graphicscache[t].fireb = fireb;
+ }
+ }
+ else
+ {
+ graphicscache[t].isready = 1;
+ graphicscache[t].pixel_mode = pixel_mode;
+ graphicscache[t].cola = cola;
+ graphicscache[t].colr = colr;
+ graphicscache[t].colg = colg;
+ graphicscache[t].colb = colb;
+ graphicscache[t].firea = firea;
+ graphicscache[t].firer = firer;
+ graphicscache[t].fireg = fireg;
+ graphicscache[t].fireb = fireb;
+ }
+ }
+ if((elements[t].Properties & PROP_HOT_GLOW) && sim->parts[i].temp>(elements[t].HighTemperature-800.0f))
+ {
+ gradv = 3.1415/(2*elements[t].HighTemperature-(elements[t].HighTemperature-800.0f));
+ caddress = (sim->parts[i].temp>elements[t].HighTemperature)?elements[t].HighTemperature-(elements[t].HighTemperature-800.0f):sim->parts[i].temp-(elements[t].HighTemperature-800.0f);
+ colr += sin(gradv*caddress) * 226;;
+ colg += sin(gradv*caddress*4.55 +3.14) * 34;
+ colb += sin(gradv*caddress*2.22 +3.14) * 64;
+ }
+
+ if((pixel_mode & FIRE_ADD) && !(render_mode & FIRE_ADD))
+ pixel_mode |= PMODE_GLOW;
+ if((pixel_mode & FIRE_BLEND) && !(render_mode & FIRE_BLEND))
+ pixel_mode |= PMODE_BLUR;
+ if((pixel_mode & PMODE_BLUR) && !(render_mode & PMODE_BLUR))
+ pixel_mode |= PMODE_FLAT;
+ if((pixel_mode & PMODE_GLOW) && !(render_mode & PMODE_GLOW))
+ pixel_mode |= PMODE_BLEND;
+ if (render_mode & PMODE_BLOB)
+ pixel_mode |= PMODE_BLOB;
+
+ pixel_mode &= render_mode;
+
+ //Alter colour based on display mode
+ if(colour_mode & COLOUR_HEAT)
+ {
+ caddress = restrict_flt((int)( restrict_flt((float)(sim->parts[i].temp+(-MIN_TEMP)), 0.0f, MAX_TEMP+(-MIN_TEMP)) / ((MAX_TEMP+(-MIN_TEMP))/1024) ) *3, 0.0f, (1024.0f*3)-3);
+ firea = 255;
+ firer = colr = (unsigned char)color_data[caddress];
+ fireg = colg = (unsigned char)color_data[caddress+1];
+ fireb = colb = (unsigned char)color_data[caddress+2];
+ cola = 255;
+ if(pixel_mode & (FIREMODE | PMODE_GLOW)) pixel_mode = (pixel_mode & ~(FIREMODE|PMODE_GLOW)) | PMODE_BLUR;
+ }
+ else if(colour_mode & COLOUR_LIFE)
+ {
+ gradv = 0.4f;
+ if (!(sim->parts[i].life<5))
+ q = sqrt((float)sim->parts[i].life);
+ else
+ q = sim->parts[i].life;
+ colr = colg = colb = sin(gradv*q) * 100 + 128;
+ cola = 255;
+ if(pixel_mode & (FIREMODE | PMODE_GLOW)) pixel_mode = (pixel_mode & ~(FIREMODE|PMODE_GLOW)) | PMODE_BLUR;
+ }
+ else if(colour_mode & COLOUR_BASC)
+ {
+ colr = PIXR(elements[t].Colour);
+ colg = PIXG(elements[t].Colour);
+ colb = PIXB(elements[t].Colour);
+ pixel_mode = PMODE_FLAT;
+ }
+
+ //Apply decoration colour
+ if(!(colour_mode & ~COLOUR_GRAD) && decorations_enable && deca)
+ {
+ if(!(pixel_mode & NO_DECO))
+ {
+ colr = (deca*decr + (255-deca)*colr) >> 8;
+ colg = (deca*decg + (255-deca)*colg) >> 8;
+ colb = (deca*decb + (255-deca)*colb) >> 8;
+ }
+
+ if(pixel_mode & DECO_FIRE)
+ {
+ firer = (deca*decr + (255-deca)*firer) >> 8;
+ fireg = (deca*decg + (255-deca)*fireg) >> 8;
+ fireb = (deca*decb + (255-deca)*fireb) >> 8;
+ }
+ }
+
+ if (colour_mode & COLOUR_GRAD)
+ {
+ float frequency = 0.05;
+ int q = sim->parts[i].temp-40;
+ colr = sin(frequency*q) * 16 + colr;
+ colg = sin(frequency*q) * 16 + colg;
+ colb = sin(frequency*q) * 16 + colb;
+ if(pixel_mode & (FIREMODE | PMODE_GLOW)) pixel_mode = (pixel_mode & ~(FIREMODE|PMODE_GLOW)) | PMODE_BLUR;
+ }
+
+ #ifndef OGLR
+ //All colours are now set, check ranges
+ if(colr>255) colr = 255;
+ else if(colr<0) colr = 0;
+ if(colg>255) colg = 255;
+ else if(colg<0) colg = 0;
+ if(colb>255) colb = 255;
+ else if(colb<0) colb = 0;
+ if(cola>255) cola = 255;
+ else if(cola<0) cola = 0;
+
+ if(firer>255) firer = 255;
+ else if(firer<0) firer = 0;
+ if(fireg>255) fireg = 255;
+ else if(fireg<0) fireg = 0;
+ if(fireb>255) fireb = 255;
+ else if(fireb<0) fireb = 0;
+ if(firea>255) firea = 255;
+ else if(firea<0) firea = 0;
+ #endif
+
+ //Pixel rendering
+ if (pixel_mode & EFFECT_LINES)
+ {
+ if (t==PT_SOAP)
+ {
+ if ((parts[i].ctype&7) == 7)
+ draw_line(nx, ny, (int)(parts[parts[i].tmp].x+0.5f), (int)(parts[parts[i].tmp].y+0.5f), colr, colg, colb, cola);
+ }
+ }
+ if(pixel_mode & PSPEC_STICKMAN)
+ {
+ char buff[4]; //Buffer for HP
+ int s;
+ int legr, legg, legb;
+ playerst *cplayer;
+ if(t==PT_STKM)
+ cplayer = &sim->player;
+ else if(t==PT_STKM2)
+ cplayer = &sim->player2;
+ else if(t==PT_FIGH)
+ cplayer = &sim->fighters[(unsigned char)sim->parts[i].tmp];
+ else
+ continue;
+
+ if (mousePosX>(nx-3) && mousePosX<(nx+3) && mousePosY<(ny+3) && mousePosY>(ny-3)) //If mouse is in the head
+ {
+ sprintf(buff, "%3d", sim->parts[i].life); //Show HP
+ drawtext(mousePosX-8-2*(sim->parts[i].life<100)-2*(sim->parts[i].life<10), mousePosY-12, buff, 255, 255, 255, 255);
+ }
+
+ if (colour_mode!=COLOUR_HEAT)
+ {
+ if (cplayer->elem<PT_NUM && cplayer->elem > 0)
+ {
+ colr = PIXR(elements[cplayer->elem].Colour);
+ colg = PIXG(elements[cplayer->elem].Colour);
+ colb = PIXB(elements[cplayer->elem].Colour);
+ }
+ else
+ {
+ colr = 0x80;
+ colg = 0x80;
+ colb = 0xFF;
+ }
+ }
+#ifdef OGLR
+ glColor4f(((float)colr)/255.0f, ((float)colg)/255.0f, ((float)colb)/255.0f, 1.0f);
+ glBegin(GL_LINE_STRIP);
+ if(t==PT_FIGH)
+ {
+ glVertex2f(fnx, fny+2);
+ glVertex2f(fnx+2, fny);
+ glVertex2f(fnx, fny-2);
+ glVertex2f(fnx-2, fny);
+ glVertex2f(fnx, fny+2);
+ }
+ else
+ {
+ glVertex2f(fnx-2, fny-2);
+ glVertex2f(fnx+2, fny-2);
+ glVertex2f(fnx+2, fny+2);
+ glVertex2f(fnx-2, fny+2);
+ glVertex2f(fnx-2, fny-2);
+ }
+ glEnd();
+ glBegin(GL_LINES);
+
+ if (colour_mode!=COLOUR_HEAT)
+ {
+ if (t==PT_STKM2)
+ glColor4f(100.0f/255.0f, 100.0f/255.0f, 1.0f, 1.0f);
+ else
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ }
+
+ glVertex2f(nx, ny+3);
+ glVertex2f(cplayer->legs[0], cplayer->legs[1]);
+
+ glVertex2f(cplayer->legs[0], cplayer->legs[1]);
+ glVertex2f(cplayer->legs[4], cplayer->legs[5]);
+
+ glVertex2f(nx, ny+3);
+ glVertex2f(cplayer->legs[8], cplayer->legs[9]);
+
+ glVertex2f(cplayer->legs[8], cplayer->legs[9]);
+ glVertex2f(cplayer->legs[12], cplayer->legs[13]);
+ glEnd();
+#else
+ if (t==PT_STKM2)
+ {
+ legr = 100;
+ legg = 100;
+ legb = 255;
+ }
+ else
+ {
+ legr = 255;
+ legg = 255;
+ legb = 255;
+ }
+
+ if (colour_mode==COLOUR_HEAT)
+ {
+ legr = colr;
+ legg = colg;
+ legb = colb;
+ }
+
+ //head
+ if(t==PT_FIGH)
+ {
+ draw_line(nx, ny+2, nx+2, ny, colr, colg, colb, 255);
+ draw_line(nx+2, ny, nx, ny-2, colr, colg, colb, 255);
+ draw_line(nx, ny-2, nx-2, ny, colr, colg, colb, 255);
+ draw_line(nx-2, ny, nx, ny+2, colr, colg, colb, 255);
+ }
+ else
+ {
+ draw_line(nx-2, ny+2, nx+2, ny+2, colr, colg, colb, 255);
+ draw_line(nx-2, ny-2, nx+2, ny-2, colr, colg, colb, 255);
+ draw_line(nx-2, ny-2, nx-2, ny+2, colr, colg, colb, 255);
+ draw_line(nx+2, ny-2, nx+2, ny+2, colr, colg, colb, 255);
+ }
+ //legs
+ draw_line(nx, ny+3, cplayer->legs[0], cplayer->legs[1], legr, legg, legb, 255);
+ draw_line(cplayer->legs[0], cplayer->legs[1], cplayer->legs[4], cplayer->legs[5], legr, legg, legb, 255);
+ draw_line(nx, ny+3, cplayer->legs[8], cplayer->legs[9], legr, legg, legb, 255);
+ draw_line(cplayer->legs[8], cplayer->legs[9], cplayer->legs[12], cplayer->legs[13], legr, legg, legb, 255);
+#endif
+ }
+ if(pixel_mode & PMODE_FLAT)
+ {
+#ifdef OGLR
+ flatV[cflatV++] = nx;
+ flatV[cflatV++] = ny;
+ flatC[cflatC++] = ((float)colr)/255.0f;
+ flatC[cflatC++] = ((float)colg)/255.0f;
+ flatC[cflatC++] = ((float)colb)/255.0f;
+ flatC[cflatC++] = 1.0f;
+ cflat++;
+#else
+ vid[ny*(VIDXRES)+nx] = PIXRGB(colr,colg,colb);
+#endif
+ }
+ if(pixel_mode & PMODE_BLEND)
+ {
+#ifdef OGLR
+ flatV[cflatV++] = nx;
+ flatV[cflatV++] = ny;
+ flatC[cflatC++] = ((float)colr)/255.0f;
+ flatC[cflatC++] = ((float)colg)/255.0f;
+ flatC[cflatC++] = ((float)colb)/255.0f;
+ flatC[cflatC++] = ((float)cola)/255.0f;
+ cflat++;
+#else
+ blendpixel(nx, ny, colr, colg, colb, cola);
+#endif
+ }
+ if(pixel_mode & PMODE_ADD)
+ {
+#ifdef OGLR
+ addV[caddV++] = nx;
+ addV[caddV++] = ny;
+ addC[caddC++] = ((float)colr)/255.0f;
+ addC[caddC++] = ((float)colg)/255.0f;
+ addC[caddC++] = ((float)colb)/255.0f;
+ addC[caddC++] = ((float)cola)/255.0f;
+ cadd++;
+#else
+ addpixel(nx, ny, colr, colg, colb, cola);
+#endif
+ }
+ if(pixel_mode & PMODE_BLOB)
+ {
+#ifdef OGLR
+ blobV[cblobV++] = nx;
+ blobV[cblobV++] = ny;
+ blobC[cblobC++] = ((float)colr)/255.0f;
+ blobC[cblobC++] = ((float)colg)/255.0f;
+ blobC[cblobC++] = ((float)colb)/255.0f;
+ blobC[cblobC++] = 1.0f;
+ cblob++;
+#else
+ vid[ny*(VIDXRES)+nx] = PIXRGB(colr,colg,colb);
+
+ blendpixel(nx+1, ny, colr, colg, colb, 223);
+ blendpixel(nx-1, ny, colr, colg, colb, 223);
+ blendpixel(nx, ny+1, colr, colg, colb, 223);
+ blendpixel(nx, ny-1, colr, colg, colb, 223);
+
+ blendpixel(nx+1, ny-1, colr, colg, colb, 112);
+ blendpixel(nx-1, ny-1, colr, colg, colb, 112);
+ blendpixel(nx+1, ny+1, colr, colg, colb, 112);
+ blendpixel(nx-1, ny+1, colr, colg, colb, 112);
+#endif
+ }
+ if(pixel_mode & PMODE_GLOW)
+ {
+ int cola1 = (5*cola)/255;
+#ifdef OGLR
+ glowV[cglowV++] = nx;
+ glowV[cglowV++] = ny;
+ glowC[cglowC++] = ((float)colr)/255.0f;
+ glowC[cglowC++] = ((float)colg)/255.0f;
+ glowC[cglowC++] = ((float)colb)/255.0f;
+ glowC[cglowC++] = 1.0f;
+ cglow++;
+#else
+ addpixel(nx, ny, colr, colg, colb, (192*cola)/255);
+ addpixel(nx+1, ny, colr, colg, colb, (96*cola)/255);
+ addpixel(nx-1, ny, colr, colg, colb, (96*cola)/255);
+ addpixel(nx, ny+1, colr, colg, colb, (96*cola)/255);
+ addpixel(nx, ny-1, colr, colg, colb, (96*cola)/255);
+
+ for (x = 1; x < 6; x++) {
+ addpixel(nx, ny-x, colr, colg, colb, cola1);
+ addpixel(nx, ny+x, colr, colg, colb, cola1);
+ addpixel(nx-x, ny, colr, colg, colb, cola1);
+ addpixel(nx+x, ny, colr, colg, colb, cola1);
+ for (y = 1; y < 6; y++) {
+ if(x + y > 7)
+ continue;
+ addpixel(nx+x, ny-y, colr, colg, colb, cola1);
+ addpixel(nx-x, ny+y, colr, colg, colb, cola1);
+ addpixel(nx+x, ny+y, colr, colg, colb, cola1);
+ addpixel(nx-x, ny-y, colr, colg, colb, cola1);
+ }
+ }
+#endif
+ }
+ if(pixel_mode & PMODE_BLUR)
+ {
+#ifdef OGLR
+ blurV[cblurV++] = nx;
+ blurV[cblurV++] = ny;
+ blurC[cblurC++] = ((float)colr)/255.0f;
+ blurC[cblurC++] = ((float)colg)/255.0f;
+ blurC[cblurC++] = ((float)colb)/255.0f;
+ blurC[cblurC++] = 1.0f;
+ cblur++;
+#else
+ for (x=-3; x<4; x++)
+ {
+ for (y=-3; y<4; y++)
+ {
+ if (abs(x)+abs(y) <2 && !(abs(x)==2||abs(y)==2))
+ blendpixel(x+nx, y+ny, colr, colg, colb, 30);
+ if (abs(x)+abs(y) <=3 && abs(x)+abs(y))
+ blendpixel(x+nx, y+ny, colr, colg, colb, 20);
+ if (abs(x)+abs(y) == 2)
+ blendpixel(x+nx, y+ny, colr, colg, colb, 10);
+ }
+ }
+#endif
+ }
+ if(pixel_mode & PMODE_SPARK)
+ {
+ flicker = rand()%20;
+#ifdef OGLR
+ //Oh god, this is awful
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx-5;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 1.0f - ((float)flicker)/30;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx+5;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny-5;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 1.0f - ((float)flicker)/30;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny+5;
+ cline++;
+#else
+ gradv = 4*sim->parts[i].life + flicker;
+ for (x = 0; gradv>0.5; x++) {
+ addpixel(nx+x, ny, colr, colg, colb, gradv);
+ addpixel(nx-x, ny, colr, colg, colb, gradv);
+
+ addpixel(nx, ny+x, colr, colg, colb, gradv);
+ addpixel(nx, ny-x, colr, colg, colb, gradv);
+ gradv = gradv/1.5f;
+ }
+#endif
+ }
+ if(pixel_mode & PMODE_FLARE)
+ {
+ flicker = rand()%20;
+#ifdef OGLR
+ //Oh god, this is awful
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx-10;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 1.0f - ((float)flicker)/40;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx+10;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny-10;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 1.0f - ((float)flicker)/30;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny+10;
+ cline++;
+#else
+ gradv = flicker + fabs(parts[i].vx)*17 + fabs(sim->parts[i].vy)*17;
+ blendpixel(nx, ny, colr, colg, colb, (gradv*4)>255?255:(gradv*4) );
+ blendpixel(nx+1, ny, colr, colg, colb, (gradv*2)>255?255:(gradv*2) );
+ blendpixel(nx-1, ny, colr, colg, colb, (gradv*2)>255?255:(gradv*2) );
+ blendpixel(nx, ny+1, colr, colg, colb, (gradv*2)>255?255:(gradv*2) );
+ blendpixel(nx, ny-1, colr, colg, colb, (gradv*2)>255?255:(gradv*2) );
+ if (gradv>255) gradv=255;
+ blendpixel(nx+1, ny-1, colr, colg, colb, gradv);
+ blendpixel(nx-1, ny-1, colr, colg, colb, gradv);
+ blendpixel(nx+1, ny+1, colr, colg, colb, gradv);
+ blendpixel(nx-1, ny+1, colr, colg, colb, gradv);
+ for (x = 1; gradv>0.5; x++) {
+ addpixel(nx+x, ny, colr, colg, colb, gradv);
+ addpixel(nx-x, ny, colr, colg, colb, gradv);
+ addpixel(nx, ny+x, colr, colg, colb, gradv);
+ addpixel(nx, ny-x, colr, colg, colb, gradv);
+ gradv = gradv/1.2f;
+ }
+#endif
+ }
+ if(pixel_mode & PMODE_LFLARE)
+ {
+ flicker = rand()%20;
+#ifdef OGLR
+ //Oh god, this is awful
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx-70;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 1.0f - ((float)flicker)/30;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx+70;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny-70;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 1.0f - ((float)flicker)/50;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny;
+ cline++;
+
+ lineC[clineC++] = ((float)colr)/255.0f;
+ lineC[clineC++] = ((float)colg)/255.0f;
+ lineC[clineC++] = ((float)colb)/255.0f;
+ lineC[clineC++] = 0.0f;
+ lineV[clineV++] = fnx;
+ lineV[clineV++] = fny+70;
+ cline++;
+#else
+ gradv = flicker + fabs(parts[i].vx)*17 + fabs(parts[i].vy)*17;
+ blendpixel(nx, ny, colr, colg, colb, (gradv*4)>255?255:(gradv*4) );
+ blendpixel(nx+1, ny, colr, colg, colb, (gradv*2)>255?255:(gradv*2) );
+ blendpixel(nx-1, ny, colr, colg, colb, (gradv*2)>255?255:(gradv*2) );
+ blendpixel(nx, ny+1, colr, colg, colb, (gradv*2)>255?255:(gradv*2) );
+ blendpixel(nx, ny-1, colr, colg, colb, (gradv*2)>255?255:(gradv*2) );
+ if (gradv>255) gradv=255;
+ blendpixel(nx+1, ny-1, colr, colg, colb, gradv);
+ blendpixel(nx-1, ny-1, colr, colg, colb, gradv);
+ blendpixel(nx+1, ny+1, colr, colg, colb, gradv);
+ blendpixel(nx-1, ny+1, colr, colg, colb, gradv);
+ for (x = 1; gradv>0.5; x++) {
+ addpixel(nx+x, ny, colr, colg, colb, gradv);
+ addpixel(nx-x, ny, colr, colg, colb, gradv);
+ addpixel(nx, ny+x, colr, colg, colb, gradv);
+ addpixel(nx, ny-x, colr, colg, colb, gradv);
+ gradv = gradv/1.01f;
+ }
+#endif
+ }
+ if (pixel_mode & EFFECT_GRAVIN)
+ {
+ int nxo = 0;
+ int nyo = 0;
+ int r;
+ int fire_rv = 0;
+ float drad = 0.0f;
+ float ddist = 0.0f;
+ sim->orbitalparts_get(parts[i].life, parts[i].ctype, orbd, orbl);
+ for (r = 0; r < 4; r++) {
+ ddist = ((float)orbd[r])/16.0f;
+ drad = (M_PI * ((float)orbl[r]) / 180.0f)*1.41f;
+ nxo = (int)(ddist*cos(drad));
+ nyo = (int)(ddist*sin(drad));
+ if (ny+nyo>0 && ny+nyo<YRES && nx+nxo>0 && nx+nxo<XRES && (sim->pmap[ny+nyo][nx+nxo]&0xFF) != PT_PRTI)
+ addpixel(nx+nxo, ny+nyo, colr, colg, colb, 255-orbd[r]);
+ }
+ }
+ if (pixel_mode & EFFECT_GRAVOUT)
+ {
+ int nxo = 0;
+ int nyo = 0;
+ int r;
+ int fire_bv = 0;
+ float drad = 0.0f;
+ float ddist = 0.0f;
+ sim->orbitalparts_get(parts[i].life, parts[i].ctype, orbd, orbl);
+ for (r = 0; r < 4; r++) {
+ ddist = ((float)orbd[r])/16.0f;
+ drad = (M_PI * ((float)orbl[r]) / 180.0f)*1.41f;
+ nxo = (int)(ddist*cos(drad));
+ nyo = (int)(ddist*sin(drad));
+ if (ny+nyo>0 && ny+nyo<YRES && nx+nxo>0 && nx+nxo<XRES && (sim->pmap[ny+nyo][nx+nxo]&0xFF) != PT_PRTO)
+ addpixel(nx+nxo, ny+nyo, colr, colg, colb, 255-orbd[r]);
+ }
+ }
+ if (pixel_mode & EFFECT_DBGLINES)
+ {
+ if (mousePosX == nx && mousePosY == ny && debugLines)//draw lines connecting wifi/portal channels
+ {
+ int z;
+ int type = parts[i].type;
+ if (type == PT_PRTI)
+ type = PT_PRTO;
+ else if (type == PT_PRTO)
+ type = PT_PRTI;
+ for (z = 0; z<NPART; z++) {
+ if (parts[z].type)
+ {
+ if (parts[z].type==type&&parts[z].tmp==parts[i].tmp)
+ xor_line(nx,ny,(int)(parts[z].x+0.5f),(int)(parts[z].y+0.5f));
+ }
+ }
+ }
+ }
+ //Fire effects
+ if(firea && (pixel_mode & FIRE_BLEND))
+ {
+#ifdef OGLR
+ smokeV[csmokeV++] = nx;
+ smokeV[csmokeV++] = ny;
+ smokeC[csmokeC++] = ((float)firer)/255.0f;
+ smokeC[csmokeC++] = ((float)fireg)/255.0f;
+ smokeC[csmokeC++] = ((float)fireb)/255.0f;
+ smokeC[csmokeC++] = ((float)firea)/255.0f;
+ csmoke++;
+#else
+ firea /= 2;
+ fire_r[ny/CELL][nx/CELL] = (firea*firer + (255-firea)*fire_r[ny/CELL][nx/CELL]) >> 8;
+ fire_g[ny/CELL][nx/CELL] = (firea*fireg + (255-firea)*fire_g[ny/CELL][nx/CELL]) >> 8;
+ fire_b[ny/CELL][nx/CELL] = (firea*fireb + (255-firea)*fire_b[ny/CELL][nx/CELL]) >> 8;
+#endif
+ }
+ if(firea && (pixel_mode & FIRE_ADD))
+ {
+#ifdef OGLR
+ fireV[cfireV++] = nx;
+ fireV[cfireV++] = ny;
+ fireC[cfireC++] = ((float)firer)/255.0f;
+ fireC[cfireC++] = ((float)fireg)/255.0f;
+ fireC[cfireC++] = ((float)fireb)/255.0f;
+ fireC[cfireC++] = ((float)firea)/255.0f;
+ cfire++;
+#else
+ firea /= 8;
+ firer = ((firea*firer) >> 8) + fire_r[ny/CELL][nx/CELL];
+ fireg = ((firea*fireg) >> 8) + fire_g[ny/CELL][nx/CELL];
+ fireb = ((firea*fireb) >> 8) + fire_b[ny/CELL][nx/CELL];
+
+ if(firer>255)
+ firer = 255;
+ if(fireg>255)
+ fireg = 255;
+ if(fireb>255)
+ fireb = 255;
+
+ fire_r[ny/CELL][nx/CELL] = firer;
+ fire_g[ny/CELL][nx/CELL] = fireg;
+ fire_b[ny/CELL][nx/CELL] = fireb;
+#endif
+ }
+ }
+ }
+ }
+#ifdef OGLR
+
+ //Go into array mode
+ glEnableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ if(cflat)
+ {
+ // -- BEGIN FLAT -- //
+ //Set point size (size of fire texture)
+ glPointSize(1.0f);
+
+ glColorPointer(4, GL_FLOAT, 0, &flatC[0]);
+ glVertexPointer(2, GL_INT, 0, &flatV[0]);
+
+ glDrawArrays(GL_POINTS, 0, cflat);
+
+ //Clear some stuff we set
+ // -- END FLAT -- //
+ }
+
+ if(cblob)
+ {
+ // -- BEGIN BLOB -- //
+ glEnable( GL_POINT_SMOOTH ); //Blobs!
+ glPointSize(2.5f);
+
+ glColorPointer(4, GL_FLOAT, 0, &blobC[0]);
+ glVertexPointer(2, GL_INT, 0, &blobV[0]);
+
+ glDrawArrays(GL_POINTS, 0, cblob);
+
+ //Clear some stuff we set
+ glDisable( GL_POINT_SMOOTH );
+ // -- END BLOB -- //
+ }
+
+ if(cglow || cblur)
+ {
+ // -- BEGIN GLOW -- //
+ //Start and prepare fire program
+ glEnable(GL_TEXTURE_2D);
+ glUseProgram(fireProg);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, glowAlpha);
+ glUniform1i(glGetUniformLocation(fireProg, "fireAlpha"), 0);
+
+ glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT);
+
+ //Make sure we can use texture coords on points
+ glEnable(GL_POINT_SPRITE);
+ glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+ glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
+
+ //Set point size (size of fire texture)
+ glPointSize(11.0f);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+
+ if(cglow)
+ {
+ glColorPointer(4, GL_FLOAT, 0, &glowC[0]);
+ glVertexPointer(2, GL_INT, 0, &glowV[0]);
+
+ glDrawArrays(GL_POINTS, 0, cglow);
+ }
+
+ glPointSize(7.0f);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ if(cblur)
+ {
+ glBindTexture(GL_TEXTURE_2D, blurAlpha);
+
+ glColorPointer(4, GL_FLOAT, 0, &blurC[0]);
+ glVertexPointer(2, GL_INT, 0, &blurV[0]);
+
+ glDrawArrays(GL_POINTS, 0, cblur);
+ }
+
+ //Clear some stuff we set
+ glDisable(GL_POINT_SPRITE);
+ glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
+ glUseProgram(0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+ // -- END GLOW -- //
+ }
+
+ if(cadd)
+ {
+ // -- BEGIN ADD -- //
+ //Set point size (size of fire texture)
+ glPointSize(1.0f);
+
+ glColorPointer(4, GL_FLOAT, 0, &addC[0]);
+ glVertexPointer(2, GL_INT, 0, &addV[0]);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ glDrawArrays(GL_POINTS, 0, cadd);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ //Clear some stuff we set
+ // -- END ADD -- //
+ }
+
+ if(cline)
+ {
+ // -- BEGIN LINES -- //
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ glEnable( GL_LINE_SMOOTH );
+ glColorPointer(4, GL_FLOAT, 0, &lineC[0]);
+ glVertexPointer(2, GL_FLOAT, 0, &lineV[0]);
+
+ glDrawArrays(GL_LINE_STRIP, 0, cline);
+
+ //Clear some stuff we set
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_LINE_SMOOTH);
+ // -- END LINES -- //
+ }
+
+ if(cfire || csmoke)
+ {
+ // -- BEGIN FIRE -- //
+ //Start and prepare fire program
+ glEnable(GL_TEXTURE_2D);
+ glUseProgram(fireProg);
+ //glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, fireAlpha);
+ glUniform1i(glGetUniformLocation(fireProg, "fireAlpha"), 0);
+
+ //Make sure we can use texture coords on points
+ glEnable(GL_POINT_SPRITE);
+ glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+ glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
+
+ //Set point size (size of fire texture)
+ glPointSize(CELL*3);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+
+ if(cfire)
+ {
+ glColorPointer(4, GL_FLOAT, 0, &fireC[0]);
+ glVertexPointer(2, GL_INT, 0, &fireV[0]);
+
+ glDrawArrays(GL_POINTS, 0, cfire);
+ }
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ if(csmoke)
+ {
+ glColorPointer(4, GL_FLOAT, 0, &smokeC[0]);
+ glVertexPointer(2, GL_INT, 0, &smokeV[0]);
+
+ glDrawArrays(GL_POINTS, 0, csmoke);
+ }
+
+ //Clear some stuff we set
+ glDisable(GL_POINT_SPRITE);
+ glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
+ glUseProgram(0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+ // -- END FIRE -- //
+ }
+
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ //Reset FBO
+ glTranslated(0, -MENUSIZE, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);
+
+ glBlendFunc(origBlendSrc, origBlendDst);
+#endif
+}
+
+void Renderer::draw_other() // EMP effect
+{
+ int i, j;
+ int emp_decor = sim->emp_decor;
+ if (emp_decor>40) emp_decor = 40;
+ if (emp_decor<0) emp_decor = 0;
+ if (!(render_mode & EFFECT)) // not in nothing mode
+ return;
+ if (emp_decor>0)
+ {
+#ifdef OGLR
+ GLint prevFbo;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo);
+ glTranslated(0, MENUSIZE, 0);
+ float femp_decor = ((float)emp_decor)/255.0f;
+ /*int r=emp_decor*2.5, g=100+emp_decor*1.5, b=255;
+ int a=(1.0*emp_decor/110)*255;
+ if (r>255) r=255;
+ if (g>255) g=255;
+ if (b>255) g=255;
+ if (a>255) a=255;*/
+ glBegin(GL_QUADS);
+ glColor4f(femp_decor*2.5f, 0.4f+femp_decor*1.5f, 1.0f+femp_decor*1.5f, femp_decor/0.44f);
+ glVertex2f(0, MENUSIZE);
+ glVertex2f(XRES, MENUSIZE);
+ glVertex2f(XRES, YRES+MENUSIZE);
+ glVertex2f(0, YRES+MENUSIZE);
+ glEnd();
+ glTranslated(0, -MENUSIZE, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);
+#else
+ int r=emp_decor*2.5, g=100+emp_decor*1.5, b=255;
+ int a=(1.0*emp_decor/110)*255;
+ if (r>255) r=255;
+ if (g>255) g=255;
+ if (b>255) g=255;
+ if (a>255) a=255;
+ for (j=0; j<YRES; j++)
+ for (i=0; i<XRES; i++)
+ {
+ blendpixel(i, j, r, g, b, a);
+ }
+#endif
+ }
+}
+
+void Renderer::draw_grav()
+{
+ int x, y, i, ca;
+ float nx, ny, dist;
+
+ if(!gravityFieldEnabled)
+ return;
+
+ for (y=0; y<YRES/CELL; y++)
+ {
+ for (x=0; x<XRES/CELL; x++)
+ {
+ ca = y*(XRES/CELL)+x;
+ if(fabsf(sim->gravx[ca]) <= 0.001f && fabsf(sim->gravy[ca]) <= 0.001f)
+ continue;
+ nx = x*CELL;
+ ny = y*CELL;
+ dist = fabsf(sim->gravy[ca])+fabsf(sim->gravx[ca]);
+ for(i = 0; i < 4; i++)
+ {
+ nx -= sim->gravx[ca]*0.5f;
+ ny -= sim->gravy[ca]*0.5f;
+ addpixel((int)(nx+0.5f), (int)(ny+0.5f), 255, 255, 255, (int)(dist*20.0f));
+ }
+ }
+ }
+}
+
+void Renderer::draw_air()
+{
+ if(!sim->aheat_enable && (display_mode & DISPLAY_AIRH))
+ return;
+#ifndef OGLR
+ if(!(display_mode & DISPLAY_AIR))
+ return;
+ int x, y, i, j;
+ float (*pv)[XRES/CELL] = sim->air->pv;
+ float (*hv)[XRES/CELL] = sim->air->hv;
+ float (*vx)[XRES/CELL] = sim->air->vx;
+ float (*vy)[XRES/CELL] = sim->air->vy;
+ pixel c;
+ for (y=0; y<YRES/CELL; y++)
+ for (x=0; x<XRES/CELL; x++)
+ {
+ if (display_mode & DISPLAY_AIRP)
+ {
+ if (pv[y][x] > 0.0f)
+ c = PIXRGB(clamp_flt(pv[y][x], 0.0f, 8.0f), 0, 0);//positive pressure is red!
+ else
+ c = PIXRGB(0, 0, clamp_flt(-pv[y][x], 0.0f, 8.0f));//negative pressure is blue!
+ }
+ else if (display_mode & DISPLAY_AIRV)
+ {
+ c = PIXRGB(clamp_flt(fabsf(vx[y][x]), 0.0f, 8.0f),//vx adds red
+ clamp_flt(pv[y][x], 0.0f, 8.0f),//pressure adds green
+ clamp_flt(fabsf(vy[y][x]), 0.0f, 8.0f));//vy adds blue
+ }
+ else if (display_mode & DISPLAY_AIRH)
+ {
+ float ttemp = hv[y][x]+(-MIN_TEMP);
+ int caddress = restrict_flt((int)( restrict_flt(ttemp, 0.0f, MAX_TEMP+(-MIN_TEMP)) / ((MAX_TEMP+(-MIN_TEMP))/1024) ) *3, 0.0f, (1024.0f*3)-3);
+ c = PIXRGB((int)((unsigned char)color_data[caddress]*0.7f), (int)((unsigned char)color_data[caddress+1]*0.7f), (int)((unsigned char)color_data[caddress+2]*0.7f));
+ //c = PIXRGB(clamp_flt(fabsf(vx[y][x]), 0.0f, 8.0f),//vx adds red
+ // clamp_flt(hv[y][x], 0.0f, 1600.0f),//heat adds green
+ // clamp_flt(fabsf(vy[y][x]), 0.0f, 8.0f));//vy adds blue
+ }
+ else if (display_mode & DISPLAY_AIRC)
+ {
+ int r;
+ int g;
+ int b;
+ // velocity adds grey
+ r = clamp_flt(fabsf(vx[y][x]), 0.0f, 24.0f) + clamp_flt(fabsf(vy[y][x]), 0.0f, 20.0f);
+ g = clamp_flt(fabsf(vx[y][x]), 0.0f, 20.0f) + clamp_flt(fabsf(vy[y][x]), 0.0f, 24.0f);
+ b = clamp_flt(fabsf(vx[y][x]), 0.0f, 24.0f) + clamp_flt(fabsf(vy[y][x]), 0.0f, 20.0f);
+ if (pv[y][x] > 0.0f)
+ {
+ r += clamp_flt(pv[y][x], 0.0f, 16.0f);//pressure adds red!
+ if (r>255)
+ r=255;
+ if (g>255)
+ g=255;
+ if (b>255)
+ b=255;
+ c = PIXRGB(r, g, b);
+ }
+ else
+ {
+ b += clamp_flt(-pv[y][x], 0.0f, 16.0f);//pressure adds blue!
+ if (r>255)
+ r=255;
+ if (g>255)
+ g=255;
+ if (b>255)
+ b=255;
+ c = PIXRGB(r, g, b);
+ }
+ }
+ for (j=0; j<CELL; j++)//draws the colors
+ for (i=0; i<CELL; i++)
+ vid[(x*CELL+i) + (y*CELL+j)*(VIDXRES)] = c;
+ }
+#else
+ int sdl_scale = 1;
+ GLuint airProg;
+ GLint prevFbo;
+ if(display_mode & DISPLAY_AIRC)
+ {
+ airProg = airProg_Cracker;
+ }
+ else if(display_mode & DISPLAY_AIRV)
+ {
+ airProg = airProg_Velocity;
+ }
+ else if(display_mode & DISPLAY_AIRP)
+ {
+ airProg = airProg_Pressure;
+ }
+ else
+ {
+ return;
+ }
+
+ glEnable( GL_TEXTURE_2D );
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo);
+ glTranslated(0, MENUSIZE, 0);
+
+ glUseProgram(airProg);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, airVX);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XRES/CELL, YRES/CELL, GL_RED, GL_FLOAT, sim->air->vx);
+ glUniform1i(glGetUniformLocation(airProg, "airX"), 0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, airVY);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XRES/CELL, YRES/CELL, GL_GREEN, GL_FLOAT, sim->air->vy);
+ glUniform1i(glGetUniformLocation(airProg, "airY"), 1);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, airPV);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XRES/CELL, YRES/CELL, GL_BLUE, GL_FLOAT, sim->air->pv);
+ glUniform1i(glGetUniformLocation(airProg, "airP"), 2);
+ glActiveTexture(GL_TEXTURE0);
+
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glBegin(GL_QUADS);
+ glTexCoord2d(1, 1);
+ glVertex3f(XRES*sdl_scale, YRES*sdl_scale, 1.0);
+ glTexCoord2d(0, 1);
+ glVertex3f(0, YRES*sdl_scale, 1.0);
+ glTexCoord2d(0, 0);
+ glVertex3f(0, 0, 1.0);
+ glTexCoord2d(1, 0);
+ glVertex3f(XRES*sdl_scale, 0, 1.0);
+ glEnd();
+
+ glUseProgram(0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glTranslated(0, -MENUSIZE, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);
+ glDisable( GL_TEXTURE_2D );
+#endif
+}
+
+void Renderer::draw_grav_zones()
+{
+ if(!gravityZonesEnabled)
+ return;
+
+ int x, y, i, j;
+ for (y=0; y<YRES/CELL; y++)
+ {
+ for (x=0; x<XRES/CELL; x++)
+ {
+ if(sim->grav->gravmask[y*(XRES/CELL)+x])
+ {
+ for (j=0; j<CELL; j++)//draws the colors
+ for (i=0; i<CELL; i++)
+ if(i == j)
+ blendpixel(x*CELL+i, y*CELL+j, 255, 200, 0, 120);
+ else
+ blendpixel(x*CELL+i, y*CELL+j, 32, 32, 32, 120);
+ }
+ }
+ }
+}
+
+void Renderer::drawblob(int x, int y, unsigned char cr, unsigned char cg, unsigned char cb)
+{
+ blendpixel(x+1, y, cr, cg, cb, 112);
+ blendpixel(x-1, y, cr, cg, cb, 112);
+ blendpixel(x, y+1, cr, cg, cb, 112);
+ blendpixel(x, y-1, cr, cg, cb, 112);
+
+ blendpixel(x+1, y-1, cr, cg, cb, 64);
+ blendpixel(x-1, y-1, cr, cg, cb, 64);
+ blendpixel(x+1, y+1, cr, cg, cb, 64);
+ blendpixel(x-1, y+1, cr, cg, cb, 64);
+}
+
+pixel Renderer::GetPixel(int x, int y)
+{
+ if (x<0 || y<0 || x>=VIDXRES || y>=VIDYRES)
+ return 0;
+#ifdef OGLR
+ return 0;
+#else
+ return vid[(y*VIDXRES)+x];
+#endif
+}
+
+Renderer::Renderer(Graphics * g, Simulation * sim):
+ sim(NULL),
+ g(NULL),
+ zoomWindowPosition(0, 0),
+ zoomScopePosition(0, 0),
+ zoomScopeSize(32),
+ ZFACTOR(8),
+ zoomEnabled(false),
+ decorations_enable(1),
+ gravityFieldEnabled(false),
+ gravityZonesEnabled(false),
+ mousePosX(-1),
+ mousePosY(-1),
+ display_mode(0),
+ render_mode(0),
+ colour_mode(0),
+ gridSize(0),
+ blackDecorations(false),
+ debugLines(false)
+{
+ this->g = g;
+ this->sim = sim;
+#if !defined(OGLR)
+#if defined(OGLI)
+ vid = new pixel[VIDXRES*VIDYRES];
+#else
+ vid = g->vid;
+#endif
+ persistentVid = new pixel[VIDXRES*YRES];
+ warpVid = new pixel[VIDXRES*VIDYRES];
+#endif
+
+ memset(fire_r, 0, sizeof(fire_r));
+ memset(fire_g, 0, sizeof(fire_g));
+ memset(fire_b, 0, sizeof(fire_b));
+
+ //Set defauly display modes
+ SetColourMode(COLOUR_DEFAULT);
+ AddRenderMode(RENDER_BASC);
+ AddRenderMode(RENDER_FIRE);
+
+ //Render mode presets. Possibly load from config in future?
+ renderModePresets = new RenderPreset[11];
+
+ renderModePresets[0].Name = "Alternative Velocity Display";
+ renderModePresets[0].RenderModes.push_back(RENDER_EFFE);
+ renderModePresets[0].RenderModes.push_back(RENDER_BASC);
+ renderModePresets[0].DisplayModes.push_back(DISPLAY_AIRC);
+
+ renderModePresets[1].Name = "Velocity Display";
+ renderModePresets[1].RenderModes.push_back(RENDER_EFFE);
+ renderModePresets[1].RenderModes.push_back(RENDER_BASC);
+ renderModePresets[1].DisplayModes.push_back(DISPLAY_AIRV);
+
+ renderModePresets[2].Name = "Pressure Display";
+ renderModePresets[2].RenderModes.push_back(RENDER_EFFE);
+ renderModePresets[2].RenderModes.push_back(RENDER_BASC);
+ renderModePresets[2].DisplayModes.push_back(DISPLAY_AIRP);
+
+ renderModePresets[3].Name = "Persistent Display";
+ renderModePresets[3].RenderModes.push_back(RENDER_EFFE);
+ renderModePresets[3].RenderModes.push_back(RENDER_BASC);
+ renderModePresets[3].DisplayModes.push_back(DISPLAY_PERS);
+
+ renderModePresets[4].Name = "Fire Display";
+ renderModePresets[4].RenderModes.push_back(RENDER_FIRE);
+ renderModePresets[4].RenderModes.push_back(RENDER_EFFE);
+ renderModePresets[4].RenderModes.push_back(RENDER_BASC);
+
+ renderModePresets[5].Name = "Blob Display";
+ renderModePresets[5].RenderModes.push_back(RENDER_FIRE);
+ renderModePresets[5].RenderModes.push_back(RENDER_EFFE);
+ renderModePresets[5].RenderModes.push_back(RENDER_BLOB);
+
+ renderModePresets[6].Name = "Heat Display";
+ renderModePresets[6].RenderModes.push_back(RENDER_BASC);
+ renderModePresets[6].DisplayModes.push_back(DISPLAY_AIRH);
+ renderModePresets[6].ColourMode = COLOUR_HEAT;
+
+ renderModePresets[7].Name = "Fancy Display";
+ renderModePresets[7].RenderModes.push_back(RENDER_FIRE);
+ renderModePresets[7].RenderModes.push_back(RENDER_GLOW);
+ renderModePresets[7].RenderModes.push_back(RENDER_BLUR);
+ renderModePresets[7].RenderModes.push_back(RENDER_EFFE);
+ renderModePresets[7].RenderModes.push_back(RENDER_BASC);
+ renderModePresets[7].DisplayModes.push_back(DISPLAY_WARP);
+
+ renderModePresets[8].Name = "Nothing Display";
+ renderModePresets[8].RenderModes.push_back(RENDER_BASC);
+
+ renderModePresets[9].Name = "Heat Gradient Display";
+ renderModePresets[9].RenderModes.push_back(RENDER_BASC);
+ renderModePresets[9].ColourMode = COLOUR_GRAD;
+
+ renderModePresets[10].Name = "Life Gradient Display";
+ renderModePresets[10].RenderModes.push_back(RENDER_BASC);
+ renderModePresets[10].ColourMode = COLOUR_LIFE;
+
+ //Prepare the graphics cache
+ graphicscache = (gcache_item *)malloc(sizeof(gcache_item)*PT_NUM);
+ memset(graphicscache, 0, sizeof(gcache_item)*PT_NUM);
+
+ int fireColoursCount = 4;
+ pixel fireColours[] = {PIXPACK(0xAF9F0F), PIXPACK(0xDFBF6F), PIXPACK(0x60300F), PIXPACK(0x000000)};
+ float fireColoursPoints[] = {1.0f, 0.9f, 0.5f, 0.0f};
+
+ int plasmaColoursCount = 5;
+ pixel plasmaColours[] = {PIXPACK(0xAFFFFF), PIXPACK(0xAFFFFF), PIXPACK(0x301060), PIXPACK(0x301040), PIXPACK(0x000000)};
+ float plasmaColoursPoints[] = {1.0f, 0.9f, 0.5f, 0.25, 0.0f};
+
+ flm_data = Graphics::GenerateGradient(fireColours, fireColoursPoints, fireColoursCount, 200);
+ plasma_data = Graphics::GenerateGradient(plasmaColours, plasmaColoursPoints, plasmaColoursCount, 200);
+
+#ifdef OGLR
+ //FBO Texture
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &partsFboTex);
+ glBindTexture(GL_TEXTURE_2D, partsFboTex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, XRES, YRES, 0, GL_RGBA, GL_FLOAT, NULL);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+
+ //FBO
+ glGenFramebuffers(1, &partsFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo);
+ glEnable(GL_BLEND);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, partsFboTex, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Reset framebuffer binding
+ glDisable(GL_TEXTURE_2D);
+
+ //Texture for air to be drawn
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &airBuf);
+ glBindTexture(GL_TEXTURE_2D, airBuf);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, XRES/CELL, YRES/CELL, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ //Zoom texture
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &zoomTex);
+ glBindTexture(GL_TEXTURE_2D, zoomTex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ //Texture for velocity maps for gravity
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &partsTFX);
+ glBindTexture(GL_TEXTURE_2D, partsTFX);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, XRES/CELL, YRES/CELL, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glGenTextures(1, &partsTFY);
+ glBindTexture(GL_TEXTURE_2D, partsTFY);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, XRES/CELL, YRES/CELL, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ //Texture for velocity maps for air
+ //TODO: Combine all air maps into 3D array or structs
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &airVX);
+ glBindTexture(GL_TEXTURE_2D, airVX);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, XRES/CELL, YRES/CELL, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glGenTextures(1, &airVY);
+ glBindTexture(GL_TEXTURE_2D, airVY);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, XRES/CELL, YRES/CELL, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glGenTextures(1, &airPV);
+ glBindTexture(GL_TEXTURE_2D, airPV);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, XRES/CELL, YRES/CELL, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ //Fire alpha texture
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &fireAlpha);
+ glBindTexture(GL_TEXTURE_2D, fireAlpha);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, CELL*3, CELL*3, 0, GL_ALPHA, GL_FLOAT, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ //Glow alpha texture
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &glowAlpha);
+ glBindTexture(GL_TEXTURE_2D, glowAlpha);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 11, 11, 0, GL_ALPHA, GL_FLOAT, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+
+ //Blur Alpha texture
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &blurAlpha);
+ glBindTexture(GL_TEXTURE_2D, blurAlpha);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 7, 7, 0, GL_ALPHA, GL_FLOAT, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ //Temptexture
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &textTexture);
+ glBindTexture(GL_TEXTURE_2D, textTexture);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ loadShaders();
+#endif
+ prepare_alpha(CELL, 1.0f);
+}
+
+void Renderer::CompileRenderMode()
+{
+ int old_render_mode = render_mode;
+ render_mode = 0;
+ for(int i = 0; i < render_modes.size(); i++)
+ render_mode |= render_modes[i];
+
+ //If firemode is removed, clear the fire display
+ if(!(render_mode & FIREMODE) && (old_render_mode & FIREMODE))
+ {
+ ClearAccumulation();
+ }
+}
+
+void Renderer::ClearAccumulation()
+{
+ std::fill(fire_r[0]+0, fire_r[(YRES/CELL)-1]+((XRES/CELL)-1), 0);
+ std::fill(fire_g[0]+0, fire_g[(YRES/CELL)-1]+((XRES/CELL)-1), 0);
+ std::fill(fire_b[0]+0, fire_b[(YRES/CELL)-1]+((XRES/CELL)-1), 0);
+#ifndef OGLR
+ std::fill(persistentVid, persistentVid+(VIDXRES*YRES), 0);
+#endif
+}
+
+void Renderer::AddRenderMode(unsigned int mode)
+{
+ for(int i = 0; i < render_modes.size(); i++)
+ {
+ if(render_modes[i] == mode)
+ {
+ return;
+ }
+ }
+ render_modes.push_back(mode);
+ CompileRenderMode();
+}
+
+void Renderer::RemoveRenderMode(unsigned int mode)
+{
+ for(int i = 0; i < render_modes.size(); i++)
+ {
+ if(render_modes[i] == mode)
+ {
+ render_modes.erase(render_modes.begin() + i);
+ i = 0;
+ }
+ }
+ CompileRenderMode();
+}
+
+void Renderer::SetRenderMode(std::vector<unsigned int> render)
+{
+ render_modes = render;
+ CompileRenderMode();
+}
+
+std::vector<unsigned int> Renderer::GetRenderMode()
+{
+ return render_modes;
+}
+
+void Renderer::CompileDisplayMode()
+{
+ display_mode = 0;
+ for(int i = 0; i < display_modes.size(); i++)
+ display_mode |= display_modes[i];
+}
+
+void Renderer::AddDisplayMode(unsigned int mode)
+{
+ for(int i = 0; i < display_modes.size(); i++)
+ {
+ if(display_modes[i] == mode)
+ {
+ return;
+ }
+ if(display_modes[i] & DISPLAY_AIR)
+ {
+ display_modes.erase(display_modes.begin()+i);
+ }
+ }
+ display_modes.push_back(mode);
+ CompileDisplayMode();
+}
+
+void Renderer::RemoveDisplayMode(unsigned int mode)
+{
+ for(int i = 0; i < display_modes.size(); i++)
+ {
+ if(display_modes[i] == mode)
+ {
+ display_modes.erase(display_modes.begin() + i);
+ i = 0;
+ }
+ }
+ CompileDisplayMode();
+}
+
+void Renderer::SetDisplayMode(std::vector<unsigned int> display)
+{
+ display_modes = display;
+ CompileDisplayMode();
+}
+
+std::vector<unsigned int> Renderer::GetDisplayMode()
+{
+ return display_modes;
+}
+
+void Renderer::SetColourMode(unsigned int mode)
+{
+ colour_mode = mode;
+}
+
+unsigned int Renderer::GetColourMode()
+{
+ return colour_mode;
+}
+
+VideoBuffer Renderer::DumpFrame()
+{
+#ifdef OGLR
+#elif defined(OGLI)
+ VideoBuffer newBuffer(XRES, YRES);
+ std::copy(vid, vid+(XRES*YRES), newBuffer.Buffer);
+ return newBuffer;
+#else
+ VideoBuffer newBuffer(XRES, YRES);
+ for(int y = 0; y < YRES; y++)
+ {
+ std::copy(vid+(y*(XRES+BARSIZE)), vid+(y*(XRES+BARSIZE))+XRES, newBuffer.Buffer+(y*XRES));
+ }
+ return newBuffer;
+#endif
+}
+
+Renderer::~Renderer()
+{
+ delete[] renderModePresets;
+
+#if !defined(OGLR)
+#if defined(OGLI)
+ delete[] vid;
+#endif
+ delete[] persistentVid;
+ delete[] warpVid;
+#endif
+ free(graphicscache);
+ free(flm_data);
+ free(plasma_data);
+}
+
+#define PIXELMETHODS_CLASS Renderer
+
+#ifdef OGLR
+#include "OpenGLDrawMethods.inl"
+#else
+#include "RasterDrawMethods.inl"
+#endif
+
+#undef PIXELMETHODS_CLASS
+
diff --git a/src/graphics/Renderer.h b/src/graphics/Renderer.h
new file mode 100644
index 0000000..fa12a8c
--- /dev/null
+++ b/src/graphics/Renderer.h
@@ -0,0 +1,184 @@
+#ifndef RENDERER_H
+#define RENDERER_H
+
+#include <vector>
+#ifdef OGLR
+#include "OpenGLHeaders.h"
+#endif
+
+#include "Config.h"
+#include "client/Client.h"
+#include "Graphics.h"
+#include "interface/Point.h"
+#include "game/RenderPreset.h"
+
+class Simulation;
+
+class Graphics;
+
+struct gcache_item
+{
+ int isready;
+ int pixel_mode;
+ int cola, colr, colg, colb;
+ int firea, firer, fireg, fireb;
+ gcache_item() :
+ isready(0),
+ pixel_mode(0),
+ cola(0),
+ colr(0),
+ colg(0),
+ colb(0),
+ firea(0),
+ firer(0),
+ fireg(0),
+ fireb(0)
+ {
+ }
+};
+typedef struct gcache_item gcache_item;
+
+class Renderer
+{
+public:
+ std::vector<unsigned int> render_modes;
+ unsigned int render_mode;
+ unsigned int colour_mode;
+ std::vector<unsigned int> display_modes;
+ unsigned int display_mode;
+ RenderPreset * renderModePresets;
+ //
+ unsigned char fire_r[YRES/CELL][XRES/CELL];
+ unsigned char fire_g[YRES/CELL][XRES/CELL];
+ unsigned char fire_b[YRES/CELL][XRES/CELL];
+ unsigned int fire_alpha[CELL*3][CELL*3];
+ char * flm_data;
+ char * plasma_data;
+ //
+ bool gravityZonesEnabled;
+ bool gravityFieldEnabled;
+ int decorations_enable;
+ bool blackDecorations;
+ bool debugLines;
+ Simulation * sim;
+ Graphics * g;
+ gcache_item *graphicscache;
+
+ //Mouse position for debug information
+ int mousePosX, mousePosY;
+
+ //Zoom window
+ ui::Point zoomWindowPosition;
+ ui::Point zoomScopePosition;
+ int zoomScopeSize;
+ bool zoomEnabled;
+ int ZFACTOR;
+
+ //Renderers
+ void RenderBegin();
+ void RenderEnd();
+
+ void RenderZoom();
+ void DrawWalls();
+ void DrawSigns();
+ void render_gravlensing(pixel * source);
+ void render_fire();
+ void prepare_alpha(int size, float intensity);
+ void render_parts();
+ void draw_grav_zones();
+ void draw_air();
+ void draw_grav();
+ void draw_other();
+ void FinaliseParts();
+
+ void ClearAccumulation();
+ void clearScreen(float alpha);
+
+ //class SolidsRenderer;
+
+#ifdef OGLR
+ void checkShader(GLuint shader, char * shname);
+ void checkProgram(GLuint program, char * progname);
+ void loadShaders();
+ GLuint vidBuf,textTexture;
+ GLint prevFbo;
+#endif
+ pixel * vid;
+ pixel * persistentVid;
+ pixel * warpVid;
+ void blendpixel(int x, int y, int r, int g, int b, int a);
+ void addpixel(int x, int y, int r, int g, int b, int a);
+
+ void draw_icon(int x, int y, Icon icon);
+
+ int drawtext(int x, int y, const char *s, int r, int g, int b, int a);
+ int drawtext(int x, int y, std::string s, int r, int g, int b, int a);
+ int drawchar(int x, int y, int c, int r, int g, int b, int a);
+ int addchar(int x, int y, int c, int r, int g, int b, int a);
+
+ void xor_pixel(int x, int y);
+ void xor_line(int x, int y, int x2, int y2);
+ void xor_rect(int x, int y, int width, int height);
+ void xor_bitmap(unsigned char * bitmap, int x, int y, int w, int h);
+
+ void draw_line(int x, int y, int x2, int y2, int r, int g, int b, int a);
+ void drawrect(int x, int y, int width, int height, int r, int g, int b, int a);
+ void fillrect(int x, int y, int width, int height, int r, int g, int b, int a);
+ void clearrect(int x, int y, int width, int height);
+ void gradientrect(int x, int y, int width, int height, int r, int g, int b, int a, int r2, int g2, int b2, int a2);
+
+ void draw_image(pixel *img, int x, int y, int w, int h, int a);
+
+ VideoBuffer DumpFrame();
+
+ void drawblob(int x, int y, unsigned char cr, unsigned char cg, unsigned char cb);
+
+ pixel GetPixel(int x, int y);
+ //...
+ //Display mode modifiers
+ void CompileDisplayMode();
+ void CompileRenderMode();
+ void AddRenderMode(unsigned int mode);
+ void SetRenderMode(std::vector<unsigned int> render);
+ std::vector<unsigned int> GetRenderMode();
+ void RemoveRenderMode(unsigned int mode);
+ void AddDisplayMode(unsigned int mode);
+ void RemoveDisplayMode(unsigned int mode);
+ void SetDisplayMode(std::vector<unsigned int> display);
+ std::vector<unsigned int> GetDisplayMode();
+ void SetColourMode(unsigned int mode);
+ unsigned int GetColourMode();
+
+ int GetGridSize() { return gridSize; }
+ void SetGridSize(int value) { gridSize = value; }
+
+ static VideoBuffer * WallIcon(int wallID, int width, int height);
+
+ Renderer(Graphics * g, Simulation * sim);
+ ~Renderer();
+
+private:
+ int gridSize;
+#ifdef OGLR
+ GLuint zoomTex, airBuf, fireAlpha, glowAlpha, blurAlpha, partsFboTex, partsFbo, partsTFX, partsTFY, airPV, airVY, airVX;
+ GLuint fireProg, airProg_Pressure, airProg_Velocity, airProg_Cracker, lensProg;
+ GLuint fireV[(YRES*XRES)*2];
+ GLfloat fireC[(YRES*XRES)*4];
+ GLuint smokeV[(YRES*XRES)*2];
+ GLfloat smokeC[(YRES*XRES)*4];
+ GLuint blobV[(YRES*XRES)*2];
+ GLfloat blobC[(YRES*XRES)*4];
+ GLuint blurV[(YRES*XRES)*2];
+ GLfloat blurC[(YRES*XRES)*4];
+ GLuint glowV[(YRES*XRES)*2];
+ GLfloat glowC[(YRES*XRES)*4];
+ GLuint flatV[(YRES*XRES)*2];
+ GLfloat flatC[(YRES*XRES)*4];
+ GLuint addV[(YRES*XRES)*2];
+ GLfloat addC[(YRES*XRES)*4];
+ GLfloat lineV[(((YRES*XRES)*2)*6)];
+ GLfloat lineC[(((YRES*XRES)*2)*6)];
+#endif
+};
+
+#endif
diff --git a/src/interface/Appearance.cpp b/src/interface/Appearance.cpp
new file mode 100644
index 0000000..f2f562c
--- /dev/null
+++ b/src/interface/Appearance.cpp
@@ -0,0 +1,61 @@
+//
+// Appearance.cpp
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 15/05/2012.
+//
+
+#include <iostream>
+#include "Appearance.h"
+
+namespace ui
+{
+ Appearance::Appearance():
+ HorizontalAlign(AlignCentre),
+ VerticalAlign(AlignMiddle),
+
+ BackgroundHover(20, 20, 20),
+ BackgroundInactive(0, 0, 0),
+ BackgroundActive(255, 255, 255),
+ BackgroundDisabled(10, 10, 10),
+
+ TextHover(255, 255, 255),
+ TextInactive(255, 255, 255),
+ TextActive(0, 0, 0),
+ TextDisabled(100, 100, 100),
+
+ BorderHover(255, 255, 255),
+ BorderInactive(200, 200, 200),
+ BorderActive(235, 235, 235),
+ BorderDisabled(100, 100, 100),
+
+ Margin(1, 4),
+ Border(1),
+
+ icon(NoIcon),
+
+ texture(NULL)
+ {};
+
+ VideoBuffer * Appearance::GetTexture()
+ {
+ return texture;
+ }
+
+ void Appearance::SetTexture(VideoBuffer * texture)
+ {
+ if(this->texture)
+ delete this->texture;
+ if(texture)
+ this->texture = new VideoBuffer(texture);
+ else
+ this->texture = NULL;
+ }
+
+ Appearance::~Appearance()
+ {
+ if(texture)
+ delete texture;
+ }
+
+}
diff --git a/src/interface/Appearance.h b/src/interface/Appearance.h
new file mode 100644
index 0000000..b876269
--- /dev/null
+++ b/src/interface/Appearance.h
@@ -0,0 +1,64 @@
+//
+// Appearance.h
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 15/05/2012.
+//
+
+#ifndef The_Powder_Toy_Appearance_h
+#define The_Powder_Toy_Appearance_h
+
+#include "Border.h"
+#include "Colour.h"
+#include "graphics/Graphics.h"
+
+namespace ui
+{
+ class Appearance
+ {
+ private:
+ VideoBuffer * texture;
+ public:
+ enum HorizontalAlignment
+ {
+ AlignLeft, AlignCentre, AlignRight
+ };
+
+ enum VerticalAlignment
+ {
+ AlignTop, AlignMiddle, AlignBottom
+ };
+
+ VerticalAlignment VerticalAlign;
+ HorizontalAlignment HorizontalAlign;
+
+ ui::Colour BackgroundHover;
+ ui::Colour BackgroundInactive;
+ ui::Colour BackgroundActive;
+ ui::Colour BackgroundDisabled;
+
+ ui::Colour TextHover;
+ ui::Colour TextInactive;
+ ui::Colour TextActive;
+ ui::Colour TextDisabled;
+
+ ui::Colour BorderHover;
+ ui::Colour BorderInactive;
+ ui::Colour BorderActive;
+ ui::Colour BorderDisabled;
+
+ ui::Border Margin;
+
+ ui::Border Border;
+
+ Icon icon;
+
+ VideoBuffer * GetTexture();
+ void SetTexture(VideoBuffer * texture);
+
+ Appearance();
+ ~Appearance();
+ };
+}
+
+#endif
diff --git a/src/interface/Border.h b/src/interface/Border.h
new file mode 100644
index 0000000..a1ceb81
--- /dev/null
+++ b/src/interface/Border.h
@@ -0,0 +1,69 @@
+#pragma once
+#include "Platform.h"
+
+namespace ui
+{
+
+ struct Border
+ {
+#if ENABLE_FLOAT_UI
+# define BORDER_T float
+#else
+# define BORDER_T int
+#endif
+
+ BORDER_T Top;
+ BORDER_T Right;
+ BORDER_T Bottom;
+ BORDER_T Left;
+
+ Border(BORDER_T all):
+ Top(all),
+ Right(all),
+ Bottom(all),
+ Left(all)
+ {
+ }
+
+ Border(BORDER_T v, BORDER_T h):
+ Top(v),
+ Right(h),
+ Bottom(v),
+ Left(h)
+ {
+ }
+
+ Border(BORDER_T top, BORDER_T right, BORDER_T bottom, BORDER_T left):
+ Top(top),
+ Right(right),
+ Bottom(bottom),
+ Left(left)
+ {
+ }
+
+ inline bool operator == (const int& v) const
+ {
+ return (Top == v && Right == v && Bottom == v && Left == v);
+ }
+
+ inline bool operator == (const Border& v) const
+ {
+ return (Top == v.Top && Right == v.Right && Bottom == v.Bottom && Left == v.Left);
+ }
+
+ inline bool operator != (const Border& v) const
+ {
+ return (Top != v.Top || Right != v.Right || Bottom != v.Bottom || Left != v.Left);
+ }
+
+ inline void operator = (const Border& v)
+ {
+ Top = v.Top;
+ Right = v.Right;
+ Bottom = v.Bottom;
+ Left = v.Left;
+ }
+
+ };
+
+}
diff --git a/src/interface/Button.cpp b/src/interface/Button.cpp
new file mode 100644
index 0000000..fbf9b54
--- /dev/null
+++ b/src/interface/Button.cpp
@@ -0,0 +1,222 @@
+/*
+ * Button.cpp
+ *
+ * Created on: Jan 8, 2012
+ * Author: Simon
+ */
+
+#include <iostream>
+#include "interface/Button.h"
+#include "graphics/Graphics.h"
+#include "Engine.h"
+#include "Misc.h"
+
+namespace ui {
+
+Button::Button(Point position, Point size, std::string buttonText, std::string toolTip):
+ Component(position, size),
+ ButtonText(buttonText),
+ isMouseInside(false),
+ isButtonDown(false),
+ isTogglable(false),
+ toggle(false),
+ actionCallback(NULL),
+ Enabled(true),
+ toolTip(toolTip)
+{
+ TextPosition();
+}
+
+void Button::TextPosition()
+{
+ buttonDisplayText = ButtonText;
+ if(buttonDisplayText.length())
+ {
+ if(Graphics::textwidth((char *)buttonDisplayText.c_str()) > Size.X - (Appearance.icon? 22 : 0))
+ {
+ int position = Graphics::textwidthx((char *)buttonDisplayText.c_str(), Size.X - (Appearance.icon? 38 : 22));
+ buttonDisplayText = buttonDisplayText.erase(position, buttonDisplayText.length()-position);
+ buttonDisplayText += "...";
+ }
+ }
+
+ Component::TextPosition(buttonDisplayText);
+}
+
+void Button::SetIcon(Icon icon)
+{
+ Appearance.icon = icon;
+ TextPosition();
+}
+
+void Button::SetText(std::string buttonText)
+{
+ ButtonText = buttonText;
+ TextPosition();
+}
+
+void Button::SetTogglable(bool togglable)
+{
+ toggle = false;
+ isTogglable = togglable;
+}
+
+bool Button::GetTogglable()
+{
+ return isTogglable;
+}
+
+TPT_NO_INLINE bool Button::GetToggleState()
+{
+ return toggle;
+}
+
+TPT_NO_INLINE void Button::SetToggleState(bool state)
+{
+ toggle = state;
+}
+
+void Button::Draw(const Point& screenPos)
+{
+ if(!drawn)
+ {
+ TextPosition();
+ drawn = true;
+ }
+ Graphics * g = ui::Engine::Ref().g;
+ Point Position = screenPos;
+ ui::Colour bgColour(0, 0, 0);
+
+ ui::Colour textColour = Appearance.TextInactive;
+ ui::Colour borderColour = Appearance.BorderInactive;
+ ui::Colour backgroundColour = Appearance.BackgroundInactive;
+
+ if(Enabled)
+ {
+ if(isButtonDown || (isTogglable && toggle))
+ {
+ textColour = Appearance.TextActive;
+ borderColour = Appearance.BorderActive;
+ backgroundColour = Appearance.BackgroundActive;
+ }
+ else if (isMouseInside)
+ {
+ textColour = Appearance.TextHover;
+ borderColour = Appearance.BorderHover;
+ backgroundColour = Appearance.BackgroundHover;
+ }
+ else
+ {
+ textColour = Appearance.TextInactive;
+ borderColour = Appearance.BorderInactive;
+ backgroundColour = Appearance.BackgroundInactive;
+ }
+ }
+ else
+ {
+ textColour = Appearance.TextDisabled;
+ borderColour = Appearance.BorderDisabled;
+ backgroundColour = Appearance.BackgroundDisabled;
+ }
+
+ bgColour = Appearance.BackgroundInactive;
+ g->fillrect(Position.X+1, Position.Y+1, Size.X-2, Size.Y-2, backgroundColour.Red, backgroundColour.Green, backgroundColour.Blue, backgroundColour.Alpha);
+ if(Appearance.Border == 1)
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha);
+ else
+ {
+ if(Appearance.Border.Top)
+ g->draw_line(Position.X, Position.Y, Position.X+Size.X-1, Position.Y, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha);
+ if(Appearance.Border.Bottom)
+ g->draw_line(Position.X, Position.Y+Size.Y-1, Position.X+Size.X-1, Position.Y+Size.Y-1, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha);
+ if(Appearance.Border.Left)
+ g->draw_line(Position.X, Position.Y, Position.X, Position.Y+Size.Y-1, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha);
+ if(Appearance.Border.Right)
+ g->draw_line(Position.X+Size.X-1, Position.Y, Position.X+Size.X-1, Position.Y+Size.Y-1, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha);
+ }
+ g->drawtext(Position.X+textPosition.X, Position.Y+textPosition.Y, buttonDisplayText, textColour.Red, textColour.Green, textColour.Blue, textColour.Alpha);
+
+ bool iconInvert = (backgroundColour.Blue + (3*backgroundColour.Green) + (2*backgroundColour.Red))>544?true:false;
+
+ if(Appearance.icon)
+ {
+ if(Enabled)
+ if(isButtonDown || (isTogglable && toggle))
+ {
+ g->draw_icon(Position.X+iconPosition.X, Position.Y+iconPosition.Y, Appearance.icon, 255, iconInvert);
+ }
+ else
+ {
+ g->draw_icon(Position.X+iconPosition.X, Position.Y+iconPosition.Y, Appearance.icon, 255, iconInvert);
+ }
+ else
+ g->draw_icon(Position.X+iconPosition.X, Position.Y+iconPosition.Y, Appearance.icon, 180, iconInvert);
+ }
+}
+
+void Button::OnMouseUnclick(int x, int y, unsigned int button)
+{
+ if(button != 1)
+ {
+ return;
+ }
+
+ if(isButtonDown)
+ {
+ isButtonDown = false;
+ DoAction();
+ }
+}
+
+void Button::OnMouseClick(int x, int y, unsigned int button)
+{
+ if(button != 1) return;
+ if(isTogglable)
+ {
+ toggle = !toggle;
+ }
+ isButtonDown = true;
+}
+
+void Button::OnMouseEnter(int x, int y)
+{
+ isMouseInside = true;
+ if(!Enabled)
+ return;
+ if(actionCallback)
+ actionCallback->MouseEnterCallback(this);
+ if(toolTip.length()>0 && GetParentWindow())
+ {
+ GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip);
+ }
+}
+
+
+void Button::OnMouseLeave(int x, int y)
+{
+ isMouseInside = false;
+ isButtonDown = false;
+}
+
+void Button::DoAction()
+{
+ if(!Enabled)
+ return;
+ if(actionCallback)
+ actionCallback->ActionCallback(this);
+}
+
+void Button::SetActionCallback(ButtonAction * action)
+{
+ if(actionCallback)
+ delete actionCallback;
+ actionCallback = action;
+}
+
+Button::~Button()
+{
+ if(actionCallback)
+ delete actionCallback;
+}
+
+} /* namespace ui */
diff --git a/src/interface/Button.h b/src/interface/Button.h
new file mode 100644
index 0000000..5bc7fc3
--- /dev/null
+++ b/src/interface/Button.h
@@ -0,0 +1,68 @@
+/*
+ * Button.h
+ *
+ * Created on: Jan 8, 2012
+ * Author: Simon
+ */
+
+#ifndef BUTTON_H_
+#define BUTTON_H_
+
+#include <string>
+#include "Misc.h"
+#include "Component.h"
+#include "Colour.h"
+
+namespace ui
+{
+class Button;
+class ButtonAction
+{
+public:
+ virtual void ActionCallback(ui::Button * sender) {}
+ virtual void MouseEnterCallback(ui::Button * sender) {}
+ virtual ~ButtonAction() {}
+};
+
+class Button : public Component
+{
+public:
+ Button(Point position = Point(0, 0), Point size = Point(0, 0), std::string buttonText = "", std::string toolTip = "");
+ virtual ~Button();
+
+ bool Toggleable;
+ bool Enabled;
+
+ virtual void OnMouseClick(int x, int y, unsigned int button);
+ virtual void OnMouseUnclick(int x, int y, unsigned int button);
+ //virtual void OnMouseUp(int x, int y, unsigned int button);
+
+ virtual void OnMouseEnter(int x, int y);
+ virtual void OnMouseLeave(int x, int y);
+
+ virtual void Draw(const Point& screenPos);
+
+ virtual void TextPosition();
+ inline bool GetState() { return state; }
+ virtual void DoAction(); //action of button what ever it may be
+ void SetTogglable(bool isTogglable);
+ bool GetTogglable();
+ TPT_NO_INLINE bool GetToggleState();
+ TPT_NO_INLINE void SetToggleState(bool state);
+ void SetActionCallback(ButtonAction * action);
+ ButtonAction * GetActionCallback() { return actionCallback; }
+ void SetText(std::string buttonText);
+ void SetIcon(Icon icon);
+ inline std::string GetText() { return ButtonText; };
+protected:
+
+ std::string toolTip;
+ std::string buttonDisplayText;
+ std::string ButtonText;
+
+ bool isButtonDown, state, isMouseInside, isTogglable, toggle;
+ ButtonAction * actionCallback;
+
+};
+}
+#endif /* BUTTON_H_ */
diff --git a/src/interface/Checkbox.cpp b/src/interface/Checkbox.cpp
new file mode 100644
index 0000000..22c6a42
--- /dev/null
+++ b/src/interface/Checkbox.cpp
@@ -0,0 +1,112 @@
+/*
+ * Checkbox.cpp
+ *
+ * Created on: Jan 26, 2012
+ * Author: Simon
+ */
+
+#include "Checkbox.h"
+
+using namespace ui;
+
+Checkbox::Checkbox(ui::Point position, ui::Point size, std::string text, std::string toolTip):
+ Component(position, size),
+ text(text),
+ toolTip(toolTip),
+ isMouseOver(false),
+ checked(false),
+ actionCallback(NULL)
+{
+ // TODO Auto-generated constructor stub
+
+}
+
+void Checkbox::SetText(std::string text)
+{
+ this->text = text;
+}
+
+std::string Checkbox::GetText()
+{
+ return text;
+}
+
+void Checkbox::SetIcon(Icon icon)
+{
+ Appearance.icon = icon;
+ iconPosition.X = 16;
+ iconPosition.Y = 3;
+}
+
+void Checkbox::OnMouseClick(int x, int y, unsigned int button)
+{
+ if(checked)
+ {
+ checked = false;
+ }
+ else
+ {
+ checked = true;
+ }
+ if(actionCallback)
+ actionCallback->ActionCallback(this);
+}
+
+void Checkbox::OnMouseUp(int x, int y, unsigned int button)
+{
+
+}
+
+
+void Checkbox::OnMouseEnter(int x, int y)
+{
+ isMouseOver = true;
+ if(toolTip.length()>0 && GetParentWindow())
+ {
+ GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip);
+ }
+}
+
+void Checkbox::OnMouseLeave(int x, int y)
+{
+ isMouseOver = false;
+}
+
+void Checkbox::Draw(const Point& screenPos)
+{
+ Graphics * g = Engine::Ref().g;
+ if(checked)
+ {
+ g->fillrect(screenPos.X+5, screenPos.Y+5, 6, 6, 255, 255, 255, 255);
+ }
+ if(isMouseOver)
+ {
+ g->drawrect(screenPos.X+2, screenPos.Y+2, 12, 12, 255, 255, 255, 255);
+ g->fillrect(screenPos.X+5, screenPos.Y+5, 6, 6, 255, 255, 255, 170);
+ if (!Appearance.icon)
+ g->drawtext(screenPos.X+18, screenPos.Y+4, text, 255, 255, 255, 255);
+ else
+ g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon, 255);
+ }
+ else
+ {
+ g->drawrect(screenPos.X+2, screenPos.Y+2, 12, 12, 255, 255, 255, 200);
+ if (!Appearance.icon)
+ g->drawtext(screenPos.X+18, screenPos.Y+4, text, 255, 255, 255, 200);
+ else
+ g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon, 200);
+ }
+}
+
+void Checkbox::SetActionCallback(CheckboxAction * action)
+{
+ if(actionCallback)
+ delete actionCallback;
+ actionCallback = action;
+}
+
+Checkbox::~Checkbox() {
+ if(actionCallback)
+ delete actionCallback;
+}
+
diff --git a/src/interface/Checkbox.h b/src/interface/Checkbox.h
new file mode 100644
index 0000000..1323831
--- /dev/null
+++ b/src/interface/Checkbox.h
@@ -0,0 +1,46 @@
+/*
+ * Checkbox.h
+ *
+ * Created on: Jan 26, 2012
+ * Author: Simon
+ */
+
+#ifndef CHECKBOX_H_
+#define CHECKBOX_H_
+
+#include <string>
+#include "Component.h"
+namespace ui
+{
+class Checkbox;
+class CheckboxAction
+{
+public:
+ virtual void ActionCallback(ui::Checkbox * sender) {}
+ virtual ~CheckboxAction() {}
+};
+class Checkbox: public ui::Component {
+ std::string text;
+ std::string toolTip;
+ bool checked;
+ bool isMouseOver;
+ CheckboxAction * actionCallback;
+public:
+ Checkbox(ui::Point position, ui::Point size, std::string text, std::string toolTip);
+ void SetText(std::string text);
+ std::string GetText();
+ void SetIcon(Icon icon);
+ void Draw(const Point& screenPos);
+ virtual void OnMouseEnter(int x, int y);
+ virtual void OnMouseLeave(int x, int y);
+ virtual void OnMouseClick(int x, int y, unsigned int button);
+ virtual void OnMouseUp(int x, int y, unsigned int button);
+ void SetActionCallback(CheckboxAction * action);
+ CheckboxAction * GetActionCallback() { return actionCallback; }
+ bool GetChecked() { return checked; }
+ void SetChecked(bool checked_) { checked = checked_; }
+ virtual ~Checkbox();
+};
+}
+
+#endif /* CHECKBOX_H_ */
diff --git a/src/interface/Colour.h b/src/interface/Colour.h
new file mode 100644
index 0000000..194b9c9
--- /dev/null
+++ b/src/interface/Colour.h
@@ -0,0 +1,24 @@
+#ifndef COLOUR_H
+#define COLOUR_H
+
+namespace ui
+{
+class Colour
+{
+public:
+ unsigned char Red, Green, Blue, Alpha;
+ Colour(unsigned char red, unsigned char green, unsigned char blue):
+ Red(red), Green(green), Blue(blue), Alpha(255)
+ {
+ }
+ Colour(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha):
+ Red(red), Green(green), Blue(blue), Alpha(alpha)
+ {
+ }
+ Colour()
+ {
+ }
+};
+}
+
+#endif
diff --git a/src/interface/Component.cpp b/src/interface/Component.cpp
new file mode 100644
index 0000000..d009c5b
--- /dev/null
+++ b/src/interface/Component.cpp
@@ -0,0 +1,244 @@
+//#include "Platform.h"
+#include <iostream>
+#include "interface/Component.h"
+#include "interface/Engine.h"
+#include "interface/Point.h"
+#include "interface/Window.h"
+#include "interface/Panel.h"
+#include "interface/ContextMenu.h"
+
+using namespace ui;
+
+Component::Component(Window* parent_state):
+ parentstate_(parent_state),
+ _parent(NULL),
+ Position(Point(0,0)),
+ Size(Point(0,0)),
+ Locked(false),
+ Visible(true),
+ textPosition(0, 0),
+ textSize(0, 0),
+ iconPosition(0, 0),
+ drawn(false),
+ menu(NULL)
+{
+
+}
+
+Component::Component(Point position, Point size):
+ parentstate_(0),
+ _parent(NULL),
+ Position(position),
+ Size(size),
+ Locked(false),
+ Visible(true),
+ textPosition(0, 0),
+ textSize(0, 0),
+ iconPosition(0, 0),
+ drawn(false),
+ menu(NULL)
+{
+
+}
+
+Component::Component():
+ parentstate_(NULL),
+ _parent(NULL),
+ Position(Point(0,0)),
+ Size(Point(0,0)),
+ Locked(false),
+ Visible(true),
+ textPosition(0, 0),
+ textSize(0, 0),
+ iconPosition(0, 0),
+ drawn(false),
+ menu(NULL)
+{
+
+}
+
+void Component::Refresh()
+{
+ drawn = false;
+}
+
+void Component::TextPosition(std::string displayText)
+{
+
+ textPosition = ui::Point(0, 0);
+
+ int textWidth, textHeight = 10;
+ Graphics::textsize((char*)displayText.c_str(), textWidth, textHeight);
+ textSize.X = textWidth; textSize.Y = textHeight;
+ textHeight-=3;
+ textWidth-=1;
+ if(Appearance.icon)
+ {
+ textWidth += 13;
+ }
+
+ int textAreaWidth = Size.X-(Appearance.Margin.Right+Appearance.Margin.Left);
+ int textAreaHeight = Size.Y-(Appearance.Margin.Top+Appearance.Margin.Bottom);
+
+ switch(Appearance.VerticalAlign)
+ {
+ case ui::Appearance::AlignTop:
+ textPosition.Y = Appearance.Margin.Top+2;
+ break;
+ case ui::Appearance::AlignMiddle:
+ textPosition.Y = Appearance.Margin.Top+((textAreaHeight-textHeight)/2);
+ break;
+ case ui::Appearance::AlignBottom:
+ textPosition.Y = Size.Y-(textHeight+Appearance.Margin.Bottom);
+ break;
+ }
+
+ switch(Appearance.HorizontalAlign)
+ {
+ case ui::Appearance::AlignLeft:
+ textPosition.X = Appearance.Margin.Left;
+ break;
+ case ui::Appearance::AlignCentre:
+ textPosition.X = Appearance.Margin.Left+((textAreaWidth-textWidth)/2);
+ break;
+ case ui::Appearance::AlignRight:
+ textPosition.X = Size.X-(textWidth+Appearance.Margin.Right);
+ break;
+ }
+ if(Appearance.icon)
+ {
+ iconPosition = textPosition-ui::Point(0, 1);
+ textPosition.X += 15;
+ }
+}
+
+bool Component::IsFocused() const
+{
+ if(parentstate_)
+ return parentstate_->IsFocused(this);
+ return false;
+}
+
+void Component::SetParentWindow(Window* window)
+{
+ parentstate_ = window;
+}
+
+void Component::SetParent(Panel* new_parent)
+{
+ if(new_parent == NULL)
+ {
+ if(_parent != NULL)
+ {
+ // remove from current parent and send component to parent state
+ for(int i = 0; i < _parent->GetChildCount(); ++i)
+ {
+ if(_parent->GetChild(i) == this)
+ {
+ // remove ourself from parent component
+ _parent->RemoveChild(i, false);
+
+ // add ourself to the parent state
+ GetParentWindow()->AddComponent(this);
+
+ //done in this loop.
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ // remove from parent state (if in parent state) and place in new parent
+ if(GetParentWindow())
+ GetParentWindow()->RemoveComponent(this);
+ new_parent->children.push_back(this);
+ }
+ this->_parent = new_parent;
+}
+
+Point Component::GetScreenPos()
+{
+ Point newPos(0,0);
+ if(GetParentWindow())
+ newPos += GetParentWindow()->Position;
+ if(GetParent())
+ newPos += GetParent()->Position + GetParent()->ViewportPosition;
+ newPos += Position;
+ return newPos;
+}
+
+// ***** OVERRIDEABLES *****
+// Kept empty.
+
+void Component::OnContextMenuAction(int item)
+{
+
+}
+
+void Component::Draw(const Point& screenPos)
+{
+ drawn = true;
+}
+
+void Component::Tick(float dt)
+{
+}
+
+void Component::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+}
+
+void Component::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+}
+
+void Component::OnMouseClick(int localx, int localy, unsigned button)
+{
+}
+
+void Component::OnMouseDown(int x, int y, unsigned button)
+{
+}
+
+void Component::OnMouseHover(int localx, int localy)
+{
+}
+
+void Component::OnMouseMoved(int localx, int localy, int dx, int dy)
+{
+}
+
+void Component::OnMouseMovedInside(int localx, int localy, int dx, int dy)
+{
+}
+
+void Component::OnMouseEnter(int localx, int localy)
+{
+}
+
+void Component::OnMouseLeave(int localx, int localy)
+{
+}
+
+void Component::OnMouseUnclick(int localx, int localy, unsigned button)
+{
+}
+
+void Component::OnMouseUp(int x, int y, unsigned button)
+{
+}
+
+void Component::OnMouseWheel(int localx, int localy, int d)
+{
+}
+
+void Component::OnMouseWheelInside(int localx, int localy, int d)
+{
+}
+
+Component::~Component()
+{
+ if(menu)
+ delete menu;
+}
diff --git a/src/interface/Component.h b/src/interface/Component.h
new file mode 100644
index 0000000..c034952
--- /dev/null
+++ b/src/interface/Component.h
@@ -0,0 +1,224 @@
+#pragma once
+
+#include "Appearance.h"
+#include "Point.h"
+#include "Window.h"
+#include "Platform.h"
+
+namespace ui
+{
+ class ContextMenu;
+ class Window;
+ class Panel;
+
+ /* class Component
+ *
+ * An interactive UI component that can be added to a state or an XComponent*.
+ * *See sys::XComponent
+ */
+ class Component
+ {
+ private:
+ Window* parentstate_;
+ Panel* _parent;
+ protected:
+ bool drawn;
+ ui::Point textPosition;
+ ui::Point textSize;
+ ui::Point iconPosition;
+ ui::ContextMenu * menu;
+ public:
+ Component(Window* parent_state);
+ Component(Point position, Point size);
+ Component();
+ virtual ~Component();
+
+ void* UserData;
+ inline Window* const GetParentWindow() const { return parentstate_; }
+ bool IsFocused() const;
+
+ void Invalidate() { drawn = false; }
+
+ Point Position;
+ Point Size;
+ bool Locked;
+ bool Visible;
+
+ ui::Appearance Appearance;
+ //virtual void SetAppearance(ui::Appearance);
+ //ui::Appearance GetAppearance();
+ virtual void TextPosition(std::string);
+
+ void Refresh();
+
+ Point GetScreenPos();
+
+ /* See the parent of this component.
+ * If new_parent is NULL, this component will have no parent. (THIS DOES NOT delete THE COMPONENT. See XComponent::RemoveChild)
+ */
+ void SetParentWindow(Window* window);
+ void SetParent(Panel* new_parent);
+
+ //Get the parent component.
+ inline Panel* const GetParent() const { return _parent; }
+
+ virtual void OnContextMenuAction(int item);
+
+ //UI functions:
+ /*
+ void Tick(float dt);
+ void Draw(const Point& screenPos);
+
+ void OnMouseHover(int localx, int localy);
+ void OnMouseMoved(int localx, int localy, int dx, int dy);
+ void OnMouseMovedInside(int localx, int localy, int dx, int dy);
+ void OnMouseEnter(int localx, int localy);
+ void OnMouseLeave(int localx, int localy);
+ void OnMouseDown(int x, int y, unsigned int button);
+ void OnMouseUp(int x, int y, unsigned int button);
+ void OnMouseClick(int localx, int localy, unsigned int button);
+ void OnMouseUnclick(int localx, int localy, unsigned int button);
+ void OnMouseWheel(int localx, int localy, int d);
+ void OnMouseWheelInside(int localx, int localy, int d);
+ void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ */
+
+ ///
+ // Called: Every tick.
+ // Params:
+ // dt: The change in time.
+ ///
+ virtual void Tick(float dt);
+
+ ///
+ // Called: When ready to draw.
+ // Params:
+ // None
+ ///
+ virtual void Draw(const Point& screenPos);
+
+
+
+
+ ///
+ // Called: When the mouse is currently hovering over the item. (Called every tick)
+ // Params:
+ // localx: Local mouse X position.
+ // localy: Local mouse Y position.
+ ///
+ virtual void OnMouseHover(int localx, int localy);
+
+ ///
+ // Called: When the mouse moves.
+ // Params:
+ // localx: Local mouse X position.
+ // localy: Local mouse Y position.
+ // dx: Mouse X delta.
+ // dy: Mouse Y delta.
+ ///
+ virtual void OnMouseMoved(int localx, int localy, int dx, int dy);
+
+ ///
+ // Called: When the mouse moves.
+ // Params:
+ // localx: Local mouse X position.
+ // localy: Local mouse Y position.
+ // dx: Mouse X delta.
+ // dy: Mouse Y delta.
+ ///
+ virtual void OnMouseMovedInside(int localx, int localy, int dx, int dy);
+
+ ///
+ // Called: When the mouse moves on top of the item.
+ // Params:
+ // localx: Local mouse X position.
+ // localy: Local mouse Y position.
+ // dx: Mouse X delta.
+ // dy: Mouse Y delta.
+ ///
+ virtual void OnMouseEnter(int localx, int localy);
+
+ ///
+ // Called: When the mouse leaves the item.
+ // Params:
+ // localx: Local mouse X position.
+ // localy: Local mouse Y position.
+ ///
+ virtual void OnMouseLeave(int localx, int localy);
+
+ ///
+ // Called: When a mouse button is pressed.
+ // Params:
+ // x: X position of the mouse.
+ // y: Y position of the mouse.
+ // button: The button that is being held down.
+ ///
+ virtual void OnMouseDown(int x, int y, unsigned button);
+
+ ///
+ // Called: When a mouse button is released.
+ // Params:
+ // x: X position of the mouse.
+ // y: Y position of the mouse.
+ // button: The button that is being released.
+ ///
+ virtual void OnMouseUp(int x, int y, unsigned button);
+
+ ///
+ // Called: When a mouse button is pressed on top of the item.
+ // Params:
+ // x: X position of the mouse.
+ // y: Y position of the mouse.
+ // button: The button that is being held down.
+ ///
+ virtual void OnMouseClick(int localx, int localy, unsigned button);
+
+ ///
+ // Called: When a mouse button is released on top of the item.
+ // Params:
+ // x: X position of the mouse.
+ // y: Y position of the mouse.
+ // button: The button that is being released.
+ ///
+ virtual void OnMouseUnclick(int localx, int localy, unsigned button);
+
+ ///
+ // Called: When the mouse wheel moves/changes.
+ // Params:
+ // localx: Local mouse X position.
+ // localy: Local mouse Y position.
+ // d: The mouse wheel movement value.
+ ///
+ virtual void OnMouseWheel(int localx, int localy, int d);
+
+ ///
+ // Called: When the mouse wheel moves/changes on top of the item.
+ // Params:
+ // localx: Local mouse X position.
+ // localy: Local mouse Y position.
+ // d: The mouse wheel movement value.
+ ///
+ virtual void OnMouseWheelInside(int localx, int localy, int d);
+
+ ///
+ // Called: When a key is pressed.
+ // Params:
+ // key: The value of the key that is being pressed.
+ // shift: Shift key is down.
+ // ctrl: Control key is down.
+ // alt: Alternate key is down.
+ ///
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+
+ ///
+ // Called: When a key is released.
+ // Params:
+ // key: The value of the key that is being released.
+ // shift: Shift key is released.
+ // ctrl: Control key is released.
+ // alt: Alternate key is released.
+ ///
+ virtual void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ };
+}
diff --git a/src/interface/ContextMenu.cpp b/src/interface/ContextMenu.cpp
new file mode 100644
index 0000000..0d34e19
--- /dev/null
+++ b/src/interface/ContextMenu.cpp
@@ -0,0 +1,99 @@
+#include "ContextMenu.h"
+
+using namespace ui;
+
+class ContextMenu::ItemSelectedAction: public ButtonAction
+{
+ ContextMenu * window;
+ int item;
+public:
+ ItemSelectedAction(ContextMenu * window, int itemID): window(window), item(itemID) { }
+ virtual void ActionCallback(ui::Button *sender)
+ {
+ window->ActionCallback(sender, item);
+ }
+};
+
+ContextMenu::ContextMenu(Component * source):
+ Window(ui::Point(0, 0), ui::Point(0, 0)),
+ Appearance(source->Appearance),
+ source(source)
+{
+}
+
+void ContextMenu::Show(ui::Point position)
+{
+ for(int i = 0; i < buttons.size(); i++)
+ {
+ RemoveComponent(buttons[i]);
+ delete buttons[i];
+ }
+ buttons.clear();
+
+ Position = position;
+ Size.Y = items.size()*16;
+ Size.X = 100;
+
+ int currentY = 1;
+ for(int i = 0; i < items.size(); i++)
+ {
+ Button * tempButton = new Button(Point(1, currentY), Point(Size.X-2, 16), items[i].Text);
+ tempButton->Appearance = Appearance;
+ tempButton->Enabled = items[i].Enabled;
+ tempButton->SetActionCallback(new ItemSelectedAction(this, items[i].ID));
+ buttons.push_back(tempButton);
+ AddComponent(tempButton);
+ currentY += 15;
+ }
+
+ ui::Engine::Ref().ShowWindow(this);
+}
+
+void ContextMenu::ActionCallback(ui::Button *sender, int item)
+{
+ ui::Engine::Ref().CloseWindow();
+ Halt();
+ source->OnContextMenuAction(item);
+}
+
+void ContextMenu::OnMouseDown(int x, int y, unsigned button)
+{
+ if(!(x > Position.X && y > Position.Y && y < Position.Y+Size.Y && x < Position.X+Size.X)) //Clicked outside window
+ ui::Engine::Ref().CloseWindow();
+}
+
+void ContextMenu::SetItem(int id, std::string text)
+{
+ for(int i = 0; i < items.size(); i++)
+ {
+ if(items[i].ID == id)
+ {
+ items[i].Text = text;
+ break;
+ }
+ }
+}
+
+void ContextMenu::RemoveItem(int id)
+{
+ for(int i = 0; i < items.size(); i++)
+ {
+ if(items[i].ID == id)
+ {
+ items.erase(items.begin()+i);
+ break;
+ }
+ }
+}
+
+void ContextMenu::AddItem(ContextMenuItem item)
+{
+ items.push_back(item);
+}
+
+void ContextMenu::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ g->fillrect(Position.X, Position.Y, Size.X, Size.Y, 100, 100, 100, 255);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, Appearance.BackgroundInactive.Red, Appearance.BackgroundInactive.Green, Appearance.BackgroundInactive.Blue, Appearance.BackgroundInactive.Alpha);
+} \ No newline at end of file
diff --git a/src/interface/ContextMenu.h b/src/interface/ContextMenu.h
new file mode 100644
index 0000000..e5549d3
--- /dev/null
+++ b/src/interface/ContextMenu.h
@@ -0,0 +1,40 @@
+#ifndef The_Powder_Toy_ContextMenu_h
+#define The_Powder_Toy_ContextMenu_h
+
+#include "Window.h"
+#include "Appearance.h"
+#include "Button.h"
+
+namespace ui
+{
+
+class ContextMenuItem
+{
+public:
+ int ID;
+ std::string Text;
+ bool Enabled;
+ ContextMenuItem(std::string text, int id, bool enabled) : Text(text), ID(id), Enabled(enabled) {}
+};
+
+class ContextMenu: public ui::Window, public ButtonAction {
+ std::vector<Button*> buttons;
+ std::vector<ContextMenuItem> items;
+ bool isMouseInside;
+ ui::Component * source;
+public:
+ ui::Appearance Appearance;
+ class ItemSelectedAction;
+ ContextMenu(Component * source);
+ virtual void ActionCallback(ui::Button *sender, int item);
+ virtual void AddItem(ContextMenuItem item);
+ virtual void RemoveItem(int id);
+ virtual void SetItem(int id, std::string text);
+ virtual void Show(ui::Point position);
+ virtual void OnDraw();
+ virtual void OnMouseDown(int x, int y, unsigned button);
+ virtual ~ContextMenu() {}
+};
+}
+
+#endif \ No newline at end of file
diff --git a/src/interface/DropDown.cpp b/src/interface/DropDown.cpp
new file mode 100644
index 0000000..0b05aac
--- /dev/null
+++ b/src/interface/DropDown.cpp
@@ -0,0 +1,204 @@
+/*
+ * DropDown.cpp
+ *
+ * Created on: Apr 16, 2012
+ * Author: Simon
+ */
+
+#include <iostream>
+#include "Style.h"
+#include "Button.h"
+#include "DropDown.h"
+
+namespace ui {
+
+class ItemSelectedAction;
+class DropDownWindow: public ui::Window {
+ friend class ItemSelectedAction;
+ Appearance appearance;
+ DropDown * dropDown;
+ std::vector<Button> buttons;
+ bool isMouseInside;
+public:
+ class ItemSelectedAction: public ButtonAction
+ {
+ DropDownWindow * window;
+ std::string option;
+ public:
+ ItemSelectedAction(DropDownWindow * window, std::string option): window(window), option(option) { }
+ virtual void ActionCallback(ui::Button *sender)
+ {
+ ui::Engine::Ref().CloseWindow();
+ window->setOption(option);
+ window->SelfDestruct();
+ }
+ };
+ DropDownWindow(DropDown * dropDown):
+ Window(ui::Point(dropDown->Position.X+dropDown->GetParentWindow()->Position.X-5, dropDown->Position.Y+dropDown->GetParentWindow()->Position.Y-3), ui::Point(dropDown->Size.X+10, 1+dropDown->options.size()*16)),
+ dropDown(dropDown),
+ appearance(dropDown->Appearance)
+ {
+ int currentY = 1;
+ for(int i = 0; i < dropDown->options.size(); i++)
+ {
+ Button * tempButton = new Button(Point(1, currentY), Point(Size.X-2, 16), dropDown->options[i].first);
+ tempButton->Appearance = appearance;
+ if(i)
+ tempButton->Appearance.Border = ui::Border(0, 1, 1, 1);
+ tempButton->SetActionCallback(new ItemSelectedAction(this, dropDown->options[i].first));
+ AddComponent(tempButton);
+ currentY += 16;
+ }
+ }
+ virtual void OnDraw()
+ {
+ Graphics * g = ui::Engine::Ref().g;
+ g->clearrect(Position.X, Position.Y, Size.X, Size.Y);
+ }
+ void setOption(std::string option)
+ {
+ dropDown->SetOption(option);
+ if(dropDown->callback)
+ {
+ int optionIndex = 0;
+ for(optionIndex = 0; optionIndex < dropDown->options.size(); optionIndex++)
+ {
+ if(option == dropDown->options[optionIndex].first)
+ break;
+ }
+ dropDown->callback->OptionChanged(dropDown, dropDown->options[optionIndex]);
+ }
+ }
+ virtual void OnTryExit(ExitMethod method)
+ {
+ ui::Engine::Ref().CloseWindow();
+ SelfDestruct();
+ }
+ virtual ~DropDownWindow() {}
+};
+
+DropDown::DropDown(Point position, Point size):
+ Component(position, size),
+ isMouseInside(false),
+ optionIndex(-1),
+ callback(NULL)
+{
+}
+
+void DropDown::OnMouseClick(int x, int y, unsigned int button)
+{
+ DropDownWindow * newWindow = new DropDownWindow(this);
+ ui::Engine::Ref().ShowWindow(newWindow);
+}
+
+void DropDown::Draw(const Point& screenPos)
+{
+ if(!drawn)
+ {
+ if(optionIndex!=-1)
+ TextPosition(options[optionIndex].first);
+ drawn = true;
+ }
+ Graphics * g = ui::Engine::Ref().g;
+ Point Position = screenPos;
+
+ ui::Colour textColour = Appearance.TextInactive;
+ ui::Colour borderColour = Appearance.BorderInactive;
+ ui::Colour backgroundColour = Appearance.BackgroundInactive;
+
+ if (isMouseInside)
+ {
+ textColour = Appearance.TextHover;
+ borderColour = Appearance.BorderHover;
+ backgroundColour = Appearance.BackgroundHover;
+ }
+ else
+ {
+ textColour = Appearance.TextInactive;
+ borderColour = Appearance.BorderInactive;
+ backgroundColour = Appearance.BackgroundInactive;
+ }
+
+ g->fillrect(Position.X-1, Position.Y-1, Size.X+2, Size.Y+2, backgroundColour.Red, backgroundColour.Green, backgroundColour.Blue, backgroundColour.Alpha);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha);
+ if(optionIndex!=-1)
+ g->drawtext(Position.X+textPosition.X, Position.Y+textPosition.Y, options[optionIndex].first, textColour.Red, textColour.Green, textColour.Blue, textColour.Alpha);
+}
+
+void DropDown::OnMouseEnter(int x, int y)
+{
+ isMouseInside = true;
+}
+
+void DropDown::OnMouseLeave(int x, int y)
+{
+ isMouseInside = false;
+}
+ std::pair<std::string, int> DropDown::GetOption()
+ {
+ if(optionIndex!=-1)
+ {
+ return options[optionIndex];
+ }
+ return std::pair<std::string, int>("", -1);
+ }
+
+ void DropDown::SetOption(std::string option)
+ {
+ for(int i = 0; i < options.size(); i++)
+ {
+ if(options[i].first == option)
+ {
+ optionIndex = i;
+ TextPosition(options[optionIndex].first);
+ return;
+ }
+ }
+ }
+ void DropDown::SetOption(int option)
+ {
+ for(int i = 0; i < options.size(); i++)
+ {
+ if(options[i].second == option)
+ {
+ optionIndex = i;
+ TextPosition(options[optionIndex].first);
+ return;
+ }
+ }
+ }
+ void DropDown::AddOption(std::pair<std::string, int> option)
+ {
+ for(int i = 0; i < options.size(); i++)
+ {
+ if(options[i] == option)
+ return;
+ }
+ options.push_back(option);
+ }
+ void DropDown::RemoveOption(std::string option)
+ {
+ start:
+ for(int i = 0; i < options.size(); i++)
+ {
+ if(options[i].first == option)
+ {
+ if(i == optionIndex)
+ optionIndex = -1;
+ options.erase(options.begin()+i);
+ goto start;
+ }
+ }
+ }
+ void DropDown::SetOptions(std::vector<std::pair<std::string, int> > options)
+ {
+ this->options = options;
+ }
+
+
+DropDown::~DropDown() {
+ if(callback)
+ delete callback;
+}
+
+} /* namespace ui */
diff --git a/src/interface/DropDown.h b/src/interface/DropDown.h
new file mode 100644
index 0000000..3aba971
--- /dev/null
+++ b/src/interface/DropDown.h
@@ -0,0 +1,48 @@
+/*
+ * DropDown.h
+ *
+ * Created on: Apr 16, 2012
+ * Author: Simon
+ */
+
+#ifndef DROPDOWN_H_
+#define DROPDOWN_H_
+
+#include <utility>
+#include "Component.h"
+#include "Colour.h"
+
+namespace ui {
+
+class DropDown;
+class DropDownWindow;
+class DropDownAction
+{
+public:
+ virtual void OptionChanged(DropDown * sender, std::pair<std::string, int> newOption) {}
+ virtual ~DropDownAction() {}
+};
+class DropDown: public ui::Component {
+ friend class DropDownWindow;
+ bool isMouseInside;
+ int optionIndex;
+ DropDownAction * callback;
+ std::vector<std::pair<std::string, int> > options;
+public:
+ DropDown(Point position, Point size);
+ std::pair<std::string, int> GetOption();
+ void SetOption(int option);
+ void SetOption(std::string option);
+ void AddOption(std::pair<std::string, int> option);
+ void RemoveOption(std::string option);
+ void SetOptions(std::vector<std::pair<std::string, int> > options);
+ void SetActionCallback(DropDownAction * action) { callback = action;}
+ virtual void Draw(const Point& screenPos);
+ virtual void OnMouseClick(int x, int y, unsigned int button);
+ virtual void OnMouseEnter(int x, int y);
+ virtual void OnMouseLeave(int x, int y);
+ virtual ~DropDown();
+};
+
+} /* namespace ui */
+#endif /* DROPDOWN_H_ */
diff --git a/src/interface/Engine.cpp b/src/interface/Engine.cpp
new file mode 100644
index 0000000..cdfadc8
--- /dev/null
+++ b/src/interface/Engine.cpp
@@ -0,0 +1,306 @@
+#include <iostream>
+#include <stack>
+#include <cstdio>
+#include <time.h>
+
+#include "Config.h"
+#include "interface/Window.h"
+#include "interface/Platform.h"
+#include "interface/Engine.h"
+#include "graphics/Graphics.h"
+
+using namespace ui;
+using namespace std;
+
+Engine::Engine():
+ state_(NULL),
+ maxWidth(0),
+ maxHeight(0),
+ mouseb_(0),
+ mousex_(0),
+ mousey_(0),
+ mousexp_(0),
+ mouseyp_(0),
+ FpsLimit(60.0f),
+ windows(stack<Window*>()),
+ mousePositions(stack<Point>()),
+ lastBuffer(NULL),
+ prevBuffers(stack<pixel*>()),
+ windowTargetPosition(0, 0),
+ FrameIndex(0),
+ Fullscreen(false),
+ Scale(1),
+ FastQuit(1),
+ break_(false),
+ lastTick(0)
+{
+}
+
+Engine::~Engine()
+{
+ if(state_ != NULL)
+ delete state_;
+ //Dispose of any Windows.
+ while(!windows.empty())
+ {
+ delete windows.top();
+ windows.pop();
+ }
+}
+
+void Engine::Begin(int width, int height)
+{
+ //engine is now ready
+ running_ = true;
+
+ width_ = width;
+ height_ = height;
+}
+
+void Engine::Break()
+{
+ break_ = true;
+}
+
+void Engine::UnBreak()
+{
+ break_ = false;
+}
+
+void Engine::Exit()
+{
+ running_ = false;
+}
+
+void Engine::ShowWindow(Window * window)
+{
+ windowOpenState = 0.0f;
+ if(window->Position.X==-1)
+ {
+ window->Position.X = (width_-window->Size.X)/2;
+ }
+ if(window->Position.Y==-1)
+ {
+ window->Position.Y = (height_-window->Size.Y)/2;
+ }
+ /*if(window->Position.Y > 0)
+ {
+ windowTargetPosition = window->Position;
+ window->Position = Point(windowTargetPosition.X, height_);
+ }*/
+ if(state_)
+ {
+ if(lastBuffer)
+ {
+ prevBuffers.push(lastBuffer);
+ }
+ lastBuffer = (pixel*)malloc((width_ * height_) * PIXELSIZE);
+
+#ifndef OGLI
+ memcpy(lastBuffer, g->vid, (width_ * height_) * PIXELSIZE);
+#endif
+
+ windows.push(state_);
+ mousePositions.push(ui::Point(mousex_, mousey_));
+ }
+ if(state_)
+ state_->DoBlur();
+
+ state_ = window;
+
+}
+
+int Engine::CloseWindow()
+{
+ if(!windows.empty())
+ {
+ if(!prevBuffers.empty())
+ {
+ lastBuffer = prevBuffers.top();
+ prevBuffers.pop();
+ }
+ else
+ {
+ free(lastBuffer);
+ lastBuffer = NULL;
+ }
+ state_ = windows.top();
+ windows.pop();
+
+ if(state_)
+ state_->DoFocus();
+
+ ui::Point mouseState = mousePositions.top();
+ mousePositions.pop();
+ if(state_)
+ {
+ mousexp_ = mouseState.X;
+ mouseyp_ = mouseState.Y;
+ state_->DoMouseMove(mousex_, mousey_, mousex_ - mousexp_, mousey_ - mouseyp_);
+ mousexp_ = mousex_;
+ mouseyp_ = mousey_;
+ }
+ return 0;
+ }
+ else
+ {
+ state_ = NULL;
+ return 1;
+ }
+}
+
+/*void Engine::SetState(State * state)
+{
+ if(state_) //queue if currently in a state
+ statequeued_ = state;
+ else
+ {
+ state_ = state;
+ if(state_)
+ state_->DoInitialized();
+ }
+}*/
+
+void Engine::SetSize(int width, int height)
+{
+ width_ = width;
+ height_ = height;
+}
+
+void Engine::SetMaxSize(int width, int height)
+{
+ maxWidth = width;
+ maxHeight = height;
+}
+
+void Engine::Tick()
+{
+ if(state_ != NULL)
+ state_->DoTick(dt);
+
+
+ lastTick = clock();
+ if(windowOpenState<1.0f)
+ {
+ if(lastBuffer)
+ {
+ pixel * vid = g->vid;
+ g->vid = lastBuffer;
+ g->fillrect(0, 0, width_, height_, 0, 0, 0, 5);
+ g->vid = vid;
+
+ }
+ /*if(windowTargetPosition.Y < state_->Position.Y)
+ {
+ state_->Position.Y += windowTargetPosition.Y/20;
+ }*/
+ windowOpenState += 0.05f;//*dt;
+ }
+
+ /*if(statequeued_ != NULL)
+ {
+ if(state_ != NULL)
+ {
+ state_->DoExit();
+ delete state_;
+ state_ = NULL;
+ }
+ state_ = statequeued_;
+ statequeued_ = NULL;
+
+ if(state_ != NULL)
+ state_->DoInitialized();
+ }*/
+}
+
+void Engine::Draw()
+{
+ if(lastBuffer && !(state_->Position.X == 0 && state_->Position.Y == 0 && state_->Size.X == width_ && state_->Size.Y == height_))
+ {
+ g->Acquire();
+ g->Clear();
+#ifndef OGLI
+ memcpy(g->vid, lastBuffer, (width_ * height_) * PIXELSIZE);
+#endif
+ }
+ else
+ {
+ g->Clear();
+ }
+ if(state_)
+ state_->DoDraw();
+
+#ifdef DEBUG
+ char fpsText[512];
+ sprintf(fpsText, "FPS: %.2f, Delta: %.3f", fps, dt);
+ ui::Engine::Ref().g->drawtext(10, 10, fpsText, 255, 255, 255, 255);
+#endif
+ g->Finalise();
+ g->Release();
+ FrameIndex++;
+ FrameIndex %= 7200;
+}
+
+void Engine::SetFps(float fps)
+{
+ this->fps = fps;
+ if(FpsLimit > 2.0f)
+ this->dt = FpsLimit/fps;
+ else
+ this->dt = 1.0f;
+}
+
+void Engine::onKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(state_)
+ state_->DoKeyPress(key, character, shift, ctrl, alt);
+}
+
+void Engine::onKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(state_)
+ state_->DoKeyRelease(key, character, shift, ctrl, alt);
+}
+
+void Engine::onMouseClick(int x, int y, unsigned button)
+{
+ mouseb_ |= button;
+ if(state_)
+ state_->DoMouseDown(x, y, button);
+}
+
+void Engine::onMouseUnclick(int x, int y, unsigned button)
+{
+ mouseb_ &= ~button;
+ if(state_)
+ state_->DoMouseUp(x, y, button);
+}
+
+void Engine::onMouseMove(int x, int y)
+{
+ mousex_ = x;
+ mousey_ = y;
+ if(state_)
+ {
+ state_->DoMouseMove(x, y, mousex_ - mousexp_, mousey_ - mouseyp_);
+ }
+ mousexp_ = x;
+ mouseyp_ = y;
+}
+
+void Engine::onMouseWheel(int x, int y, int delta)
+{
+ if(state_)
+ state_->DoMouseWheel(x, y, delta);
+}
+
+void Engine::onResize(int newWidth, int newHeight)
+{
+ SetSize(newWidth, newHeight);
+}
+
+void Engine::onClose()
+{
+ if(state_)
+ state_->DoExit();
+}
diff --git a/src/interface/Engine.h b/src/interface/Engine.h
new file mode 100644
index 0000000..fb110e4
--- /dev/null
+++ b/src/interface/Engine.h
@@ -0,0 +1,108 @@
+#pragma once
+
+#include <stack>
+#include "Singleton.h"
+#include "Platform.h"
+#include "graphics/Graphics.h"
+#include "Window.h"
+
+namespace ui
+{
+ class Window;
+
+ /* class Engine
+ *
+ * Controls the User Interface.
+ * Send user inputs to the Engine and the appropriate controls and components will interact.
+ */
+ class Engine: public Singleton<Engine>
+ {
+ public:
+ Engine();
+ ~Engine();
+
+ void ShowWindow(Window * window);
+ int CloseWindow();
+
+ void onMouseMove(int x, int y);
+ void onMouseClick(int x, int y, unsigned button);
+ void onMouseUnclick(int x, int y, unsigned button);
+ void onMouseWheel(int x, int y, int delta);
+ void onKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ void onKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ void onResize(int newWidth, int newHeight);
+ void onClose();
+
+ void Begin(int width, int height);
+ inline bool Running() { return running_; }
+ inline bool Broken() { return break_; }
+ inline int LastTick() { return lastTick; }
+ inline void LastTick(int tick) { lastTick = tick; }
+ void Exit();
+ void Break();
+ void UnBreak();
+
+ void SetFullscreen(bool fullscreen) { Fullscreen = fullscreen; }
+ inline bool GetFullscreen() { return Fullscreen; }
+ void SetScale(int scale) { Scale = scale; }
+ inline int GetScale() { return Scale; }
+ void SetFastQuit(bool fastquit) { FastQuit = fastquit; }
+ inline bool GetFastQuit() {return FastQuit; }
+
+ void Tick();
+ void Draw();
+
+ void SetFps(float fps);
+ inline float GetFps() { return fps; };
+
+ inline int GetMouseButton() { return mouseb_; }
+ inline int GetMouseX() { return mousex_; }
+ inline int GetMouseY() { return mousey_; }
+ inline int GetWidth() { return width_; }
+ inline int GetHeight() { return height_; }
+ inline int GetMaxWidth() { return maxWidth; }
+ inline int GetMaxHeight() { return maxHeight; }
+
+ TPT_NO_INLINE void SetMaxSize(int width, int height);
+
+ inline void SetSize(int width, int height);
+
+ //void SetState(Window* state);
+ //inline State* GetState() { return state_; }
+ inline Window* GetWindow() { return state_; }
+ float FpsLimit;
+ Graphics * g;
+ int Scale;
+ bool Fullscreen;
+
+ unsigned int FrameIndex;
+ private:
+ float dt;
+ float fps;
+ pixel * lastBuffer;
+ std::stack<pixel*> prevBuffers;
+ std::stack<Window*> windows;
+ std::stack<Point> mousePositions;
+ //Window* statequeued_;
+ Window* state_;
+ Point windowTargetPosition;
+ float windowOpenState;
+
+ bool running_;
+ bool break_;
+ bool FastQuit;
+
+ int lastTick;
+ int mouseb_;
+ int mousex_;
+ int mousey_;
+ int mousexp_;
+ int mouseyp_;
+ int width_;
+ int height_;
+
+ int maxWidth;
+ int maxHeight;
+ };
+
+}
diff --git a/src/interface/Keys.h b/src/interface/Keys.h
new file mode 100644
index 0000000..e532d4e
--- /dev/null
+++ b/src/interface/Keys.h
@@ -0,0 +1,84 @@
+
+#if defined(USE_SDL)
+#include "SDL/SDL.h"
+#define KEY_UP SDLK_UP
+#define KEY_DOWN SDLK_DOWN
+#define KEY_RIGHT SDLK_RIGHT
+#define KEY_LEFT SDLK_LEFT
+#define KEY_HOME SDLK_HOME
+#define KEY_END SDLK_END
+#define KEY_BACKSPACE SDLK_BACKSPACE
+#define KEY_DELETE SDLK_DELETE
+#define KEY_TAB SDLK_TAB
+#define KEY_RETURN SDLK_RETURN
+#define KEY_ENTER SDLK_KP_ENTER
+#define KEY_ESCAPE SDLK_ESCAPE
+
+#define KEY_CTRL SDLK_LCTRL
+#define KEY_ALT SDLK_LALT
+#define KEY_SHIFT SDLK_LSHIFT
+
+#define KEY_MOD_NONE KMOD_NONE
+#define KEY_MOD_LSHIFT KMOD_LSHIFT
+#define KEY_MOD_RSHIFT KMOD_RSHIFT
+#define KEY_MOD_LCONTROL KMOD_LCTRL
+#define KEY_MOD_RCONTROL KMOD_RCTRL
+#define KEY_MOD_LALT KMOD_LALT
+#define KEY_MOD_RALT KMOD_RALT
+#define KEY_MOD_LMETA KMOD_LMETA
+#define KEY_MOD_RMETA KMOD_RMETA
+#define KEY_MOD_NUM KMOD_NUM
+#define KEY_MOD_CAPS KMOD_CAPS
+#define KEY_MOD_MODE KMOD_MODE
+#define KEY_MOD_RESERVED KMOD_RESERVED
+
+#define KEY_MOD_CONTROL KEY_MOD_RCONTROL | KEY_MOD_LCONTROl
+#define KEY_MOD_ALT KEY_MOD_RALT | KEY_MOD_LALT
+#define KEY_MOD_SHIFT KEY_MOD_RSHIFT | KEY_MOD_LSHIFT
+
+#define KEY_a SDLK_a
+#define KEY_d SDLK_d
+#define KEY_s SDLK_s
+#define KEY_w SDLK_w
+
+#define KEY_F1 SDLK_F1
+
+#define BUTTON_LEFT SDL_BUTTON_LEFT
+#define BUTTON_MIDDLE SDL_BUTTON_MIDDLE
+#define BUTTON_RIGHT SDL_BUTTON_RIGHT
+
+#else
+
+#define KEY_UP 1
+#define KEY_DOWN 2
+#define KEY_RIGHT 3
+#define KEY_LEFT 4
+#define KEY_HOME 5
+#define KEY_END 6
+#define KEY_BACKSPACE 7
+#define KEY_DELETE 8
+#define KEY_TAB 9
+#define KEY_RETURN 10
+#define KEY_ENTER 11
+#define KEY_ESCAPE 12
+
+#define KEY_CTRL 13
+#define KEY_ALT 14
+#define KEY_SHIFT 15
+
+#define KEY_MOD_CONTROL 16
+#define KEY_MOD_ALT 17
+#define KEY_MOD_SHIFT 18
+
+#define BUTTON_LEFT 19
+#define BUTTON_MIDDLE 20
+#define BUTTON_RIGHT 21
+
+#define KEY_a 22
+#define KEY_d 23
+#define KEY_s 24
+#define KEY_w 25
+
+#define KEY_F1 26
+
+#endif
diff --git a/src/interface/Label.cpp b/src/interface/Label.cpp
new file mode 100644
index 0000000..80387ce
--- /dev/null
+++ b/src/interface/Label.cpp
@@ -0,0 +1,418 @@
+#include <string>
+#include "Config.h"
+#include "Point.h"
+#include "Label.h"
+#include "Keys.h"
+#include "ContextMenu.h"
+
+using namespace ui;
+
+Label::Label(Point position, Point size, std::string labelText):
+ Component(position, size),
+ text(labelText),
+ textColour(255, 255, 255),
+ selectionIndex0(-1),
+ selectionIndex1(-1),
+ selectionXL(-1),
+ selectionXH(-1),
+ multiline(false),
+ selecting(false),
+ autoHeight(size.Y==-1?true:false),
+ caret(-1)
+{
+ menu = new ContextMenu(this);
+ menu->AddItem(ContextMenuItem("Copy", 0, true));
+}
+
+Label::~Label()
+{
+
+}
+
+void Label::SetMultiline(bool status)
+{
+ multiline = status;
+ if(status)
+ {
+ updateMultiline();
+ updateSelection();
+ TextPosition(textLines);
+ }
+ else
+ {
+ TextPosition(text);
+ }
+}
+
+void Label::SetText(std::string text)
+{
+ this->text = text;
+ if(multiline)
+ {
+ updateMultiline();
+ updateSelection();
+ TextPosition(textLines);
+ }
+ else
+ {
+ TextPosition(text);
+ }
+}
+
+void Label::AutoHeight()
+{
+ bool oldAH = autoHeight;
+ autoHeight = true;
+ updateMultiline();
+ autoHeight = oldAH;
+}
+
+void Label::updateMultiline()
+{
+ int lines = 1;
+ if(text.length()>0)
+ {
+ char * rawText = new char[text.length()+1];
+ std::copy(text.begin(), text.end(), rawText);
+ rawText[text.length()] = 0;
+
+ char c, pc = 0;
+ int charIndex = 0;
+
+ int wordWidth = 0;
+ int lineWidth = 0;
+ char * wordStart = NULL;
+ while(c = rawText[charIndex++])
+ {
+ switch(c)
+ {
+ case ' ':
+ lineWidth += Graphics::CharWidth(c);
+ lineWidth += wordWidth;
+ wordWidth = 0;
+ break;
+ case '\n':
+ lineWidth = wordWidth = 0;
+ lines++;
+ break;
+ default:
+ if(pc == ' ')
+ {
+ wordStart = &rawText[charIndex-2];
+ }
+ wordWidth += Graphics::CharWidth(c);
+ if(lineWidth + wordWidth >= Size.X-(Appearance.Margin.Left+Appearance.Margin.Right))
+ {
+ if(wordStart && *wordStart)
+ *wordStart = '\n';
+ else if(!wordStart)
+ rawText[charIndex-1] = '\n';
+ lineWidth = wordWidth;
+ wordWidth = 0;
+ lines++;
+ }
+ break;
+ }
+ pc = c;
+ }
+ if(autoHeight)
+ {
+ Size.Y = lines*12;
+ }
+ textLines = std::string(rawText);
+ delete[] rawText;
+ /*int currentWidth = 0;
+ char * lastSpace = NULL;
+ char * currentWord = rawText;
+ char * nextSpace;
+ while(true)
+ {
+ nextSpace = strchr(currentWord+1, ' ');
+ if(nextSpace)
+ nextSpace[0] = 0;
+ int width = Graphics::textwidth(currentWord);
+ if(width+currentWidth >= Size.X-(Appearance.Margin.Left+Appearance.Margin.Right))
+ {
+ currentWidth = width;
+ if(currentWord!=rawText)
+ {
+ currentWord[0] = '\n';
+ lines++;
+ }
+ }
+ else
+ currentWidth += width;
+ if(nextSpace)
+ nextSpace[0] = ' ';
+ if(!currentWord[0] || !currentWord[1] || !(currentWord = strchr(currentWord+1, ' ')))
+ break;
+ }
+ if(autoHeight)
+ {
+ Size.Y = lines*12;
+ }
+ textLines = std::string(rawText);
+ delete[] rawText;*/
+ }
+ else
+ {
+ if(autoHeight)
+ {
+ Size.Y = 12;
+ }
+ textLines = std::string("");
+ }
+}
+
+std::string Label::GetText()
+{
+ return this->text;
+}
+
+void Label::OnContextMenuAction(int item)
+{
+ switch(item)
+ {
+ case 0:
+ copySelection();
+ break;
+ }
+}
+
+void Label::OnMouseClick(int x, int y, unsigned button)
+{
+ if(button == BUTTON_RIGHT)
+ {
+ if(menu)
+ menu->Show(GetScreenPos() + ui::Point(x, y));
+ }
+ else
+ {
+ selecting = true;
+ if(multiline)
+ selectionIndex0 = Graphics::CharIndexAtPosition((char*)textLines.c_str(), x-textPosition.X, y-textPosition.Y);
+ else
+ selectionIndex0 = Graphics::CharIndexAtPosition((char*)text.c_str(), x-textPosition.X, y-textPosition.Y);
+ selectionIndex1 = selectionIndex0;
+
+ updateSelection();
+ }
+}
+
+void Label::copySelection()
+{
+ std::string currentText;
+
+ if(multiline)
+ currentText = textLines;
+ else
+ currentText = text;
+
+ if(selectionIndex1 > selectionIndex0) {
+ clipboard_push_text((char*)currentText.substr(selectionIndex0, selectionIndex1-selectionIndex0).c_str());
+ } else if(selectionIndex0 > selectionIndex1) {
+ clipboard_push_text((char*)currentText.substr(selectionIndex1, selectionIndex0-selectionIndex1).c_str());
+ } else {
+ clipboard_push_text((char*)currentText.c_str());
+ }
+}
+
+void Label::OnMouseUp(int x, int y, unsigned button)
+{
+ selecting = false;
+}
+
+void Label::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(ctrl && key == 'c')
+ {
+ copySelection();
+ }
+}
+
+void Label::OnMouseMoved(int localx, int localy, int dx, int dy)
+{
+ if(selecting)
+ {
+ if(multiline)
+ selectionIndex1 = Graphics::CharIndexAtPosition((char*)textLines.c_str(), localx-textPosition.X, localy-textPosition.Y);
+ else
+ selectionIndex1 = Graphics::CharIndexAtPosition((char*)text.c_str(), localx-textPosition.X, localy-textPosition.Y);
+ updateSelection();
+ }
+}
+
+void Label::Tick(float dt)
+{
+ if(!this->IsFocused() && (selecting || (selectionIndex0 != -1 && selectionIndex1 != -1)))
+ {
+ ClearSelection();
+ }
+}
+
+int Label::getLowerSelectionBound()
+{
+ return (selectionIndex0 > selectionIndex1) ? selectionIndex1 : selectionIndex0;
+}
+
+int Label::getHigherSelectionBound()
+{
+ return (selectionIndex0 > selectionIndex1) ? selectionIndex0 : selectionIndex1;
+}
+
+bool Label::HasSelection()
+{
+ if(selectionIndex0 != -1 && selectionIndex1 != -1 && selectionIndex0 != selectionIndex1)
+ return true;
+ return false;
+}
+
+void Label::ClearSelection()
+{
+ selecting = false;
+ selectionIndex0 = -1;
+ selectionIndex1 = -1;
+ updateSelection();
+}
+
+void Label::updateSelection()
+{
+ std::string currentText;
+
+ if(selectionIndex0 < 0) selectionIndex0 = 0;
+ if(selectionIndex0 > text.length()) selectionIndex0 = text.length();
+ if(selectionIndex1 < 0) selectionIndex1 = 0;
+ if(selectionIndex1 > text.length()) selectionIndex1 = text.length();
+
+ if(selectionIndex0 == -1 || selectionIndex1 == -1)
+ {
+ selectionXH = -1;
+ selectionXL = -1;
+
+ textFragments = std::string(currentText);
+ return;
+ }
+
+ if(multiline)
+ currentText = textLines;
+ else
+ currentText = text;
+
+ if(selectionIndex1 > selectionIndex0) {
+ selectionLineH = Graphics::PositionAtCharIndex((char*)currentText.c_str(), selectionIndex1, selectionXH, selectionYH);
+ selectionLineL = Graphics::PositionAtCharIndex((char*)currentText.c_str(), selectionIndex0, selectionXL, selectionYL);
+
+ textFragments = std::string(currentText);
+ //textFragments.insert(selectionIndex1, "\x0E");
+ //textFragments.insert(selectionIndex0, "\x0F\x01\x01\x01");
+ textFragments.insert(selectionIndex1, "\x01");
+ textFragments.insert(selectionIndex0, "\x01");
+ } else if(selectionIndex0 > selectionIndex1) {
+ selectionLineH = Graphics::PositionAtCharIndex((char*)currentText.c_str(), selectionIndex0, selectionXH, selectionYH);
+ selectionLineL = Graphics::PositionAtCharIndex((char*)currentText.c_str(), selectionIndex1, selectionXL, selectionYL);
+
+ textFragments = std::string(currentText);
+ //textFragments.insert(selectionIndex0, "\x0E");
+ //textFragments.insert(selectionIndex1, "\x0F\x01\x01\x01");
+ textFragments.insert(selectionIndex0, "\x01");
+ textFragments.insert(selectionIndex1, "\x01");
+ } else {
+ selectionXH = -1;
+ selectionXL = -1;
+
+ textFragments = std::string(currentText);
+ }
+
+ if(displayText.length())
+ {
+ displayText = tDisplayText;
+ if(selectionIndex1 > selectionIndex0) {
+ int tSelectionIndex1 = Graphics::CharIndexAtPosition((char*)displayText.c_str(), selectionXH, selectionYH);
+ int tSelectionIndex0 = Graphics::CharIndexAtPosition((char*)displayText.c_str(), selectionXL, selectionYL);
+
+ displayText.insert(tSelectionIndex1, "\x01");
+ displayText.insert(tSelectionIndex0, "\x01");
+ } else if(selectionIndex0 > selectionIndex1) {
+ int tSelectionIndex0 = Graphics::CharIndexAtPosition((char*)displayText.c_str(), selectionXH, selectionYH);
+ int tSelectionIndex1 = Graphics::CharIndexAtPosition((char*)displayText.c_str(), selectionXL, selectionYL);
+
+ displayText.insert(tSelectionIndex0, "\x01");
+ displayText.insert(tSelectionIndex1, "\x01");
+ }
+ }
+}
+
+void Label::SetDisplayText(std::string newText)
+{
+ displayText = newText;
+ tDisplayText = displayText;
+}
+
+void Label::Draw(const Point& screenPos)
+{
+ if(!drawn)
+ {
+ if(multiline)
+ {
+ TextPosition(textLines);
+ updateMultiline();
+ updateSelection();
+ }
+ else
+ TextPosition(text);
+ drawn = true;
+ }
+ Graphics * g = Engine::Ref().g;
+
+ std::string cDisplayText = displayText;
+
+ if(!cDisplayText.length())
+ {
+ if(selectionXL != -1 && selectionXH != -1)
+ {
+ cDisplayText = textFragments;
+ }
+ else
+ {
+ if(multiline)
+ cDisplayText = textLines;
+ else
+ cDisplayText = text;
+ }
+ }
+
+ if(multiline)
+ {
+ if(selectionXL != -1 && selectionXH != -1)
+ {
+ if(selectionLineH - selectionLineL > 0)
+ {
+ g->fillrect(screenPos.X+textPosition.X+selectionXL, (screenPos.Y+textPosition.Y-1)+selectionYL, textSize.X-(selectionXL), 10, 255, 255, 255, 255);
+ for(int i = 1; i < selectionLineH-selectionLineL; i++)
+ {
+ g->fillrect(screenPos.X+textPosition.X, (screenPos.Y+textPosition.Y-1)+selectionYL+(i*12), textSize.X, 10, 255, 255, 255, 255);
+ }
+ g->fillrect(screenPos.X+textPosition.X, (screenPos.Y+textPosition.Y-1)+selectionYH, selectionXH, 10, 255, 255, 255, 255);
+
+ } else {
+ g->fillrect(screenPos.X+textPosition.X+selectionXL, screenPos.Y+selectionYL+textPosition.Y-1, selectionXH-(selectionXL), 10, 255, 255, 255, 255);
+ }
+ g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255);
+ }
+ else
+ {
+ g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255);
+ }
+ } else {
+ if(selectionXL != -1 && selectionXH != -1)
+ {
+ g->fillrect(screenPos.X+textPosition.X+selectionXL, screenPos.Y+textPosition.Y-1, selectionXH-(selectionXL), 10, 255, 255, 255, 255);
+ g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255);
+ }
+ else
+ {
+ g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255);
+ }
+ }
+}
+
diff --git a/src/interface/Label.h b/src/interface/Label.h
new file mode 100644
index 0000000..f5fa1a7
--- /dev/null
+++ b/src/interface/Label.h
@@ -0,0 +1,72 @@
+#ifndef LABEL_H
+#define LABEL_H
+
+#include <string>
+
+#include "Component.h"
+#include "Misc.h"
+#include "Colour.h"
+
+namespace ui
+{
+ class Label : public Component
+ {
+ protected:
+ std::string textFragments;
+ std::string textLines;
+ std::string displayText;
+ std::string tDisplayText;
+
+ std::string text;
+ Colour textColour;
+ int caret;
+ int selectionIndex0;
+ int selectionIndex1;
+
+ int selectionXL;
+ int selectionXH;
+ int selectionYL;
+ int selectionYH;
+ int selectionLineL;
+ int selectionLineH;
+
+ bool multiline;
+ bool selecting;
+ bool autoHeight;
+
+ void updateMultiline();
+ void updateSelection();
+
+ int getLowerSelectionBound();
+ int getHigherSelectionBound();
+
+ virtual void copySelection();
+ public:
+ //Label(Window* parent_state, std::string labelText);
+ Label(Point position, Point size, std::string labelText);
+ //Label(std::string labelText);
+ virtual ~Label();
+
+ virtual void SetMultiline(bool status);
+
+ virtual void SetText(std::string text);
+ virtual void SetDisplayText(std::string newText);
+ virtual std::string GetText();
+
+ virtual bool HasSelection();
+ virtual void ClearSelection();
+ virtual void AutoHeight();
+
+ void SetTextColour(Colour textColour) { this->textColour = textColour; }
+
+ virtual void OnContextMenuAction(int item);
+ virtual void OnMouseClick(int x, int y, unsigned button);
+ virtual void OnMouseUp(int x, int y, unsigned button);
+ virtual void OnMouseMoved(int localx, int localy, int dx, int dy);
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual void Draw(const Point& screenPos);
+ virtual void Tick(float dt);
+ };
+}
+
+#endif // LABEL_H
diff --git a/src/interface/LuaProgressBar.h b/src/interface/LuaProgressBar.h
new file mode 100644
index 0000000..dc2ef4e
--- /dev/null
+++ b/src/interface/LuaProgressBar.h
@@ -0,0 +1,30 @@
+#pragma once
+
+extern "C" {
+ #include "lua.h"
+ #include "lauxlib.h"
+ #include "lualib.h"
+}
+
+#include "LuaLuna.h"
+#include "LuaComponent.h"
+
+namespace ui
+{
+ class ProgressBar;
+}
+
+class LuaScriptInterface;
+
+class LuaProgressBar: public LuaComponent
+{
+ ui::ProgressBar * progressBar;
+ int onValueChangedFunction;
+ int value(lua_State * l);
+public:
+ static const char className[];
+ static Luna<LuaProgressBar>::RegType methods[];
+
+ LuaProgressBar(lua_State * l);
+ ~LuaProgressBar();
+}; \ No newline at end of file
diff --git a/src/interface/Panel.cpp b/src/interface/Panel.cpp
new file mode 100644
index 0000000..d673037
--- /dev/null
+++ b/src/interface/Panel.cpp
@@ -0,0 +1,454 @@
+#pragma once
+#include <vector>
+//#include "Platform.h"
+
+#include "interface/Panel.h"
+
+#include "interface/Point.h"
+#include "interface/Window.h"
+#include "interface/Component.h"
+#include "graphics/Graphics.h"
+
+using namespace ui;
+
+Panel::Panel(Point position, Point size):
+ Component(position, size),
+ InnerSize(size),
+ ViewportPosition(0, 0),
+ mouseInside(false)
+{
+#ifdef OGLI
+ GLint lastVid;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &lastVid);
+
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &myVidTex);
+ glBindTexture(GL_TEXTURE_2D, myVidTex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, XRES+BARSIZE, YRES+MENUSIZE, 0, GL_RGBA, GL_FLOAT, NULL);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+
+ //FBO
+ glGenFramebuffers(1, &myVid);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, myVid);
+ glEnable(GL_BLEND);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, myVidTex, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Reset framebuffer binding
+ glDisable(GL_TEXTURE_2D);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, lastVid);
+#else
+ myVid = new pixel[(XRES+BARSIZE)*(YRES+MENUSIZE)];
+#endif
+}
+
+Panel::~Panel()
+{
+ for(unsigned i = 0; i < children.size(); ++i)
+ {
+ if( children[i] )
+ delete children[i];
+ }
+#ifdef OGLI
+ glDeleteTextures(1, &myVidTex);
+ glDeleteFramebuffers(1, &myVid);
+#else
+ delete[] myVid;
+#endif
+}
+
+void Panel::AddChild(Component* c)
+{
+ c->SetParent(this);
+ c->SetParentWindow(this->GetParentWindow());
+}
+
+int Panel::GetChildCount()
+{
+ return children.size();
+}
+
+Component* Panel::GetChild(unsigned idx)
+{
+ return children[idx];
+}
+
+void Panel::RemoveChild(Component* c)
+{
+ for(int i = 0; i < children.size(); ++i)
+ {
+ if(children[i] == c)
+ {
+ //remove child from parent. Does not free memory
+ children.erase(children.begin() + i);
+ break;
+ }
+ }
+}
+
+void Panel::RemoveChild(unsigned idx, bool freeMem)
+{
+ if(freeMem)
+ delete children[idx];
+
+ children.erase(children.begin() + idx);
+}
+
+void Panel::Draw(const Point& screenPos)
+{
+
+ // draw ourself first
+ XDraw(screenPos);
+#ifdef OGLI
+ GLint lastVid;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &lastVid);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, myVid);
+ glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+#else
+ pixel * lastVid = ui::Engine::Ref().g->vid;
+ ui::Engine::Ref().g->vid = myVid;
+ std::fill(myVid, myVid+((XRES+BARSIZE)*(YRES+MENUSIZE)), 0);
+#endif
+
+ // attempt to draw all children
+ for(int i = 0; i < children.size(); ++i)
+ {
+ // the component must be visible
+ if(children[i]->Visible)
+ {
+ //check if the component is in the screen, draw if it is
+ if( children[i]->Position.X + ViewportPosition.X + children[i]->Size.X >= 0 &&
+ children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y >= 0 &&
+ children[i]->Position.X + ViewportPosition.X < ui::Engine::Ref().GetWidth() &&
+ children[i]->Position.Y + ViewportPosition.Y < ui::Engine::Ref().GetHeight() )
+ {
+ Point scrpos = /*screenPos + */children[i]->Position + ViewportPosition;
+ children[i]->Draw(scrpos);
+ }
+ }
+ }
+
+#ifdef OGLI
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, lastVid);
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, myVidTex);
+
+ int x = screenPos.X, y = screenPos.Y;
+ int h = Size.Y, w = Size.X;
+
+ double texX = double(Size.X)/double(XRES+BARSIZE), texY = 1, texYB = 1-(double(Size.Y)/double(YRES+MENUSIZE));
+
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glBegin(GL_QUADS);
+ glTexCoord2d(0, texYB);
+ glVertex2f(x, y+h);
+ glTexCoord2d(texX, texYB);
+ glVertex2f(x+w, y+h);
+ glTexCoord2d(texX, texY);
+ glVertex2f(x+w, y);
+ glTexCoord2d(0, texY);
+ glVertex2f(x, y);
+ glEnd();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+#else
+ ui::Engine::Ref().g->vid = lastVid;
+
+ //dst=(pixel *)sdl_scrn->pixels+y*sdl_scrn->pitch/PIXELSIZE+x;
+ for (int row = 0; row < Size.Y; row++)
+ {
+ std::copy(myVid+(row*(XRES+BARSIZE)), myVid+(row*(XRES+BARSIZE))+Size.X, lastVid+((screenPos.Y+row)*(XRES+BARSIZE))+screenPos.X);
+ }
+#endif
+}
+
+void Panel::Tick(float dt)
+{
+ // tick ourself first
+ XTick(dt);
+
+ // tick our children
+ for(unsigned i = 0; i < children.size(); ++i)
+ children[i]->Tick(dt);
+}
+
+void Panel::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ XOnKeyPress(key, character, shift, ctrl, alt);
+}
+
+void Panel::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ XOnKeyRelease(key, character, shift, ctrl, alt);
+}
+
+void Panel::OnMouseClick(int localx, int localy, unsigned button)
+{
+ bool childclicked = false;
+
+ //check if clicked a child
+ for(int i = children.size()-1; i >= 0 ; --i)
+ {
+ //child must be unlocked
+ if(!children[i]->Locked)
+ {
+ //is mouse inside?
+ if( localx >= children[i]->Position.X + ViewportPosition.X &&
+ localy >= children[i]->Position.Y + ViewportPosition.Y &&
+ localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
+ localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
+ {
+ childclicked = true;
+ GetParentWindow()->FocusComponent(children[i]);
+ children[i]->OnMouseClick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button);
+ break;
+ }
+ }
+ }
+
+ //if a child wasn't clicked, send click to ourself
+ if(!childclicked)
+ {
+ XOnMouseClick(localx, localy, button);
+ GetParentWindow()->FocusComponent(this);
+ }
+}
+
+void Panel::OnMouseDown(int x, int y, unsigned button)
+{
+ XOnMouseDown(x, y, button);
+ for(int i = 0; i < children.size(); ++i)
+ {
+ if(!children[i]->Locked)
+ children[i]->OnMouseDown(x, y, button);
+ }
+}
+
+void Panel::OnMouseHover(int localx, int localy)
+{
+ // check if hovering on children
+ for(int i = children.size() - 1; i >= 0; --i)
+ {
+ if(!children[i]->Locked)
+ {
+ if( localx >= children[i]->Position.X &&
+ localy >= children[i]->Position.Y &&
+ localx < children[i]->Position.X + children[i]->Size.X &&
+ localy < children[i]->Position.Y + children[i]->Size.Y )
+ {
+ children[i]->OnMouseHover(localx - children[i]->Position.X, localy - children[i]->Position.Y);
+ break;
+ }
+ }
+ }
+
+ // always allow hover on parent (?)
+ XOnMouseHover(localx, localy);
+}
+
+void Panel::OnMouseMoved(int localx, int localy, int dx, int dy)
+{
+ XOnMouseMoved(localx, localy, dx, dy);
+ for(int i = 0; i < children.size(); ++i)
+ {
+ if(!children[i]->Locked)
+ children[i]->OnMouseMoved(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, dx, dy);
+ }
+}
+
+void Panel::OnMouseMovedInside(int localx, int localy, int dx, int dy)
+{
+ for(int i = 0; i < children.size(); ++i)
+ {
+ if(!children[i]->Locked)
+ {
+ Point local (localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y)
+ , prevlocal (local.X - dx, local.Y - dy);
+
+ // mouse currently inside?
+ if( local.X >= 0 &&
+ local.Y >= 0 &&
+ local.X < children[i]->Size.X &&
+ local.Y < children[i]->Size.Y )
+ {
+ children[i]->OnMouseMovedInside(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, dx, dy);
+
+ // was the mouse outside?
+ if(!(prevlocal.X >= 0 &&
+ prevlocal.Y >= 0 &&
+ prevlocal.X < children[i]->Size.X &&
+ prevlocal.Y < children[i]->Size.Y ) )
+ {
+ children[i]->OnMouseEnter(local.X, local.Y);
+ }
+ }
+ // if not currently inside
+ else
+ {
+ // was the mouse inside?
+ if( prevlocal.X >= 0 &&
+ prevlocal.Y >= 0 &&
+ prevlocal.X < children[i]->Size.X &&
+ prevlocal.Y < children[i]->Size.Y )
+ {
+ children[i]->OnMouseLeave(local.X, local.Y);
+ }
+
+ }
+ }
+ }
+
+ // always allow hover on parent (?)
+ XOnMouseMovedInside(localx, localy, dx, dy);
+}
+
+void Panel::OnMouseEnter(int localx, int localy)
+{
+ mouseInside = true;
+ XOnMouseEnter(localx, localy);
+}
+
+void Panel::OnMouseLeave(int localx, int localy)
+{
+ mouseInside = false;
+ XOnMouseLeave(localx, localy);
+}
+
+void Panel::OnMouseUnclick(int localx, int localy, unsigned button)
+{
+ bool childunclicked = false;
+
+ //check if clicked a child
+ for(int i = children.size()-1; i >= 0 ; --i)
+ {
+ //child must be unlocked
+ if(!children[i]->Locked)
+ {
+ //is mouse inside?
+ if( localx >= children[i]->Position.X + ViewportPosition.X &&
+ localy >= children[i]->Position.Y + ViewportPosition.Y &&
+ localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
+ localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
+ {
+ childunclicked = true;
+ children[i]->OnMouseUnclick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button);
+ break;
+ }
+ }
+ }
+
+ //if a child wasn't clicked, send click to ourself
+ if(!childunclicked)
+ {
+ XOnMouseUnclick(localx, localy, button);
+ }
+}
+
+void Panel::OnMouseUp(int x, int y, unsigned button)
+{
+ XOnMouseUp(x, y, button);
+ for(int i = 0; i < children.size(); ++i)
+ {
+ if(!children[i]->Locked)
+ children[i]->OnMouseUp(x, y, button);
+ }
+}
+
+void Panel::OnMouseWheel(int localx, int localy, int d)
+{
+ XOnMouseWheel(localx, localy, d);
+ for(int i = 0; i < children.size(); ++i)
+ {
+ if(!children[i]->Locked)
+ children[i]->OnMouseWheel(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, d);
+ }
+}
+
+void Panel::OnMouseWheelInside(int localx, int localy, int d)
+{
+ XOnMouseWheelInside(localx, localy, d);
+ //check if clicked a child
+ for(int i = children.size()-1; i >= 0 ; --i)
+ {
+ //child must be unlocked
+ if(!children[i]->Locked)
+ {
+ //is mouse inside?
+ if( localx >= children[i]->Position.X + ViewportPosition.X &&
+ localy >= children[i]->Position.Y + ViewportPosition.Y &&
+ localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
+ localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
+ {
+ children[i]->OnMouseWheelInside(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, d);
+ break;
+ }
+ }
+ }
+}
+
+// ***** OVERRIDEABLES *****
+// Kept empty.
+
+void Panel::XDraw(const Point& screenPos)
+{
+}
+
+void Panel::XTick(float dt)
+{
+}
+
+void Panel::XOnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+}
+
+void Panel::XOnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+}
+
+void Panel::XOnMouseClick(int localx, int localy, unsigned button)
+{
+}
+
+void Panel::XOnMouseDown(int x, int y, unsigned button)
+{
+}
+
+void Panel::XOnMouseHover(int localx, int localy)
+{
+}
+
+void Panel::XOnMouseMoved(int localx, int localy, int dx, int dy)
+{
+}
+
+void Panel::XOnMouseMovedInside(int localx, int localy, int dx, int dy)
+{
+}
+
+void Panel::XOnMouseEnter(int localx, int localy)
+{
+}
+
+void Panel::XOnMouseLeave(int localx, int localy)
+{
+}
+
+void Panel::XOnMouseUnclick(int localx, int localy, unsigned button)
+{
+}
+
+void Panel::XOnMouseUp(int x, int y, unsigned button)
+{
+}
+
+void Panel::XOnMouseWheel(int localx, int localy, int d)
+{
+}
+
+void Panel::XOnMouseWheelInside(int localx, int localy, int d)
+{
+}
diff --git a/src/interface/Panel.h b/src/interface/Panel.h
new file mode 100644
index 0000000..cfeace3
--- /dev/null
+++ b/src/interface/Panel.h
@@ -0,0 +1,150 @@
+#pragma once
+#include <vector>
+//#include "Platform.h"
+
+#include "interface/Point.h"
+#include "interface/Window.h"
+#include "interface/Component.h"
+
+#ifdef OGLI
+#include "graphics/OpenGLHeaders.h"
+#endif
+
+
+class Graphics;
+namespace ui
+{
+ /* class XComponent
+ *
+ * An eXtension of the Component class.
+ * Adds the ability to have child components.
+ *
+ * See sys::Component
+ */
+
+class Component;
+ class Panel : public Component
+ {
+ public:
+ friend class Component;
+
+#ifdef OGLI
+ GLuint myVid, myVidTex;
+#else
+ pixel * myVid;
+#endif
+ ui::Point InnerSize;
+ ui::Point ViewportPosition;
+
+ Panel(Point position, Point size);
+ virtual ~Panel();
+
+ /* Add a child component.
+ * Similar to XComponent::SetParent
+ *
+ * If the component is already parented, then this will become the new parent.
+ */
+ void AddChild(Component* c);
+
+ // Remove child from component. This DOES NOT free the component from memory.
+ void RemoveChild(Component* c);
+
+ // Remove child from component. This WILL free the component from memory unless told otherwise.
+ void RemoveChild(unsigned idx, bool freeMem = true);
+
+ //Grab the number of children this component owns.
+ int GetChildCount();
+
+ //Get child of this component by index.
+ Component* GetChild(unsigned idx);
+
+ void Tick(float dt);
+ void Draw(const Point& screenPos);
+
+ void OnMouseHover(int localx, int localy);
+ void OnMouseMoved(int localx, int localy, int dx, int dy);
+ void OnMouseMovedInside(int localx, int localy, int dx, int dy);
+ void OnMouseEnter(int localx, int localy);
+ void OnMouseLeave(int localx, int localy);
+ void OnMouseDown(int x, int y, unsigned button);
+ void OnMouseUp(int x, int y, unsigned button);
+ void OnMouseClick(int localx, int localy, unsigned button);
+ void OnMouseUnclick(int localx, int localy, unsigned button);
+ void OnMouseWheel(int localx, int localy, int d);
+ void OnMouseWheelInside(int localx, int localy, int d);
+ void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+
+ protected:
+ // child components
+ std::vector<ui::Component*> children;
+ bool mouseInside;
+
+ //UI functions:
+ /*
+ void XTick(float dt);
+ void XDraw(const Point& screenPos);
+
+ void XOnMouseHover(int localx, int localy);
+ void XOnMouseMoved(int localx, int localy, int dx, int dy);
+ void XOnMouseMovedInside(int localx, int localy, int dx, int dy);
+ void XOnMouseEnter(int localx, int localy);
+ void XOnMouseLeave(int localx, int localy);
+ void XOnMouseDown(int x, int y, unsigned int button);
+ void XOnMouseUp(int x, int y, unsigned int button);
+ void XOnMouseClick(int localx, int localy, unsigned int button);
+ void XOnMouseUnclick(int localx, int localy, unsigned int button);
+ void XOnMouseWheel(int localx, int localy, int d);
+ void XOnMouseWheelInside(int localx, int localy, int d);
+ void XOnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ void XOnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ */
+
+ // Overridable. Called by XComponent::Tick()
+ virtual void XTick(float dt);
+
+ // Overridable. Called by XComponent::Draw()
+ virtual void XDraw(const Point& screenPos);
+
+
+ // Overridable. Called by XComponent::XOnMouseHover()
+ virtual void XOnMouseHover(int localx, int localy);
+
+ // Overridable. Called by XComponent::OnMouseMoved()
+ virtual void XOnMouseMoved(int localx, int localy, int dx, int dy);
+
+ // Overridable. Called by XComponent::OnMouseMovedInside()
+ virtual void XOnMouseMovedInside(int localx, int localy, int dx, int dy);
+
+ // Overridable. Called by XComponent::OnMouseEnter()
+ virtual void XOnMouseEnter(int localx, int localy);
+
+ // Overridable. Called by XComponent::OnMouseLeave()
+ virtual void XOnMouseLeave(int localx, int localy);
+
+ // Overridable. Called by XComponent::OnMouseDown()
+ virtual void XOnMouseDown(int x, int y, unsigned button);
+
+ // Overridable. Called by XComponent::OnMouseUp()
+ virtual void XOnMouseUp(int x, int y, unsigned button);
+
+ // Overridable. Called by XComponent::OnMouseClick()
+ virtual void XOnMouseClick(int localx, int localy, unsigned button);
+
+ // Overridable. Called by XComponent::OnMouseUnclick()
+ virtual void XOnMouseUnclick(int localx, int localy, unsigned button);
+
+ // Overridable. Called by XComponent::OnMouseWheel()
+ virtual void XOnMouseWheel(int localx, int localy, int d);
+
+ // Overridable. Called by XComponent::OnMouseWheelInside()
+ virtual void XOnMouseWheelInside(int localx, int localy, int d);
+
+ // Overridable. Called by XComponent::OnKeyPress()
+ virtual void XOnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+
+ // Overridable. Called by XComponent::OnKeyRelease()
+ virtual void XOnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ };
+
+}
diff --git a/src/interface/Platform.h b/src/interface/Platform.h
new file mode 100644
index 0000000..5940303
--- /dev/null
+++ b/src/interface/Platform.h
@@ -0,0 +1,71 @@
+#pragma once
+
+typedef unsigned short Uint16;
+
+/* ***** Primitive Types ***** */
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+#include <climits>
+namespace sys
+{
+
+#if UCHAR_MAX == 0xFF //char
+ typedef signed char s8;
+ typedef unsigned char u8;
+#else
+# error No 8-Bit Integer supported.
+#endif
+#if USHRT_MAX == 0xFFFF //short
+ typedef signed short s16;
+ typedef unsigned short u16;
+#elif UINT_MAX == 0xFFFF
+ typedef signed int s16;
+ typedef unsigned int u16;
+#elif ULONG_MAX == 0xFFFF
+ typedef signed long s16;
+ typedef unsigned long u16;
+ #else
+ # error No 16-Bit Integer supported.
+ #endif
+ #if USHRT_MAX == 0xFFFFFFFF //int
+ typedef signed short s32;
+ typedef unsigned short u32;
+#elif UINT_MAX == 0xFFFFFFFF
+ typedef signed int s32;
+ typedef unsigned int u32;
+#elif ULONG_MAX == 0xFFFFFFFF
+ typedef signed long s32;
+ typedef unsigned long u32;
+ #else
+ # error No 32-Bit Integer supported.
+ #endif
+#if UINT_MAX == 0xFFFFFFFFFFFFFFFF //long
+ typedef signed int s64;
+ typedef unsigned int u64;
+#elif ULONG_MAX == 0xFFFFFFFFFFFFFFFF
+ typedef signed long s64;
+ typedef unsigned long u64;
+#elif ULLONG_MAX == 0xFFFFFFFFFFFFFFFF
+ typedef signed long long s64;
+ typedef unsigned long long u64;
+#else
+# pragma message("Warning: 64-bit not supported. s64 and u64 defined as 32-bit.")
+ typedef s32 s64;
+ typedef u32 u64;
+#endif
+//floating
+typedef float f32;
+typedef double f64;
+//misc
+typedef u8 byte;
+typedef u8 ubyte;
+typedef s8 sbyte;
+typedef s64 llong;
+typedef s64 sllong;
+typedef u64 ullong;
+typedef char* cstring;
+
+} //namespace sys
diff --git a/src/interface/Point.h b/src/interface/Point.h
new file mode 100644
index 0000000..d2eff6b
--- /dev/null
+++ b/src/interface/Point.h
@@ -0,0 +1,146 @@
+#pragma once
+#include "Platform.h"
+
+namespace ui
+{
+
+//Lightweight 2D Int32/Float32 Point struct for UI
+struct Point
+{
+#if ENABLE_FLOAT_UI
+# define POINT_T float
+#else
+# define POINT_T int
+#endif
+
+ POINT_T X;
+ POINT_T Y;
+
+ Point(POINT_T x, POINT_T y)
+ : X(x)
+ , Y(y)
+ {
+ }
+
+ inline Point operator - () const
+ {
+ return Point(-X, -Y);
+ }
+
+ inline Point operator + (const Point& v) const
+ {
+ return Point(X + v.X, Y + v.Y);
+ }
+
+ inline Point operator + (const int v) const
+ {
+ return Point(X + v, Y + v);
+ }
+
+ inline Point operator - (const Point& v) const
+ {
+ return Point(X - v.X, Y - v.Y);
+ }
+
+ inline Point operator - (const int v) const
+ {
+ return Point(X - v, Y - v);
+ }
+
+ inline Point operator * (const Point& v) const
+ {
+ return Point(X * v.X, Y * v.Y);
+ }
+
+ inline Point operator * (int v) const
+ {
+ return Point(X * static_cast<POINT_T>(v), Y * static_cast<POINT_T>(v));
+ }
+
+ inline Point operator * (float v) const
+ {
+ return Point(X * static_cast<POINT_T>(v), Y * static_cast<POINT_T>(v));
+ }
+
+ inline Point operator / (const Point& v) const
+ {
+ return Point(X / v.X, Y / v.Y);
+ }
+
+ inline Point operator / (int v) const
+ {
+ return Point(X / static_cast<POINT_T>(v), Y / static_cast<POINT_T>(v));
+ }
+
+ inline Point operator / (float v) const
+ {
+ return Point(X / static_cast<POINT_T>(v), Y / static_cast<POINT_T>(v));
+ }
+
+ inline void operator += (const Point& v)
+ {
+ X += v.X;
+ Y += v.Y;
+ }
+
+ inline void operator -= (const Point& v)
+ {
+ X -= v.X;
+ Y -= v.Y;
+ }
+
+ inline void operator *= (const Point& v)
+ {
+ X *= v.X;
+ Y *= v.Y;
+ }
+
+ inline void operator *= (int v)
+ {
+ X *= static_cast<POINT_T>(v);
+ Y *= static_cast<POINT_T>(v);
+ }
+
+ inline void operator *= (float v)
+ {
+ X *= static_cast<POINT_T>(v);
+ Y *= static_cast<POINT_T>(v);
+ }
+
+ inline void operator /= (const Point& v)
+ {
+ X /= v.X;
+ Y /= v.Y;
+ }
+
+ inline void operator /= (int v)
+ {
+ X /= static_cast<POINT_T>(v);
+ Y /= static_cast<POINT_T>(v);
+ }
+
+ inline void operator /= (float v)
+ {
+ X /= static_cast<POINT_T>(v);
+ Y /= static_cast<POINT_T>(v);
+ }
+
+ inline bool operator == (const Point& v) const
+ {
+ return (X == v.X && Y == v.Y);
+ }
+
+ inline bool operator != (const Point& v) const
+ {
+ return (X != v.X || Y != v.Y);
+ }
+
+ inline void operator = (const Point& v)
+ {
+ X = v.X;
+ Y = v.Y;
+ }
+
+};
+
+}
diff --git a/src/interface/ProgressBar.cpp b/src/interface/ProgressBar.cpp
new file mode 100644
index 0000000..eda88f6
--- /dev/null
+++ b/src/interface/ProgressBar.cpp
@@ -0,0 +1,81 @@
+#include "ProgressBar.h"
+#include "Style.h"
+
+using namespace ui;
+
+ProgressBar::ProgressBar(Point position, Point size, int startProgress, std::string startStatus):
+ Component(position, size),
+ intermediatePos(0.0f),
+ progressStatus(""),
+ progress(0)
+{
+ SetStatus(startStatus);
+ SetProgress(startProgress);
+}
+
+void ProgressBar::SetProgress(int progress)
+{
+ this->progress = progress;
+ if(this->progress > 100)
+ this->progress = 100;
+}
+
+int ProgressBar::GetProgress()
+{
+ return progress;
+}
+
+void ProgressBar::SetStatus(std::string status)
+{
+ progressStatus = status;
+}
+
+std::string ProgressBar::GetStatus()
+{
+ return progressStatus;
+}
+
+void ProgressBar::Draw(const Point & screenPos)
+{
+ Graphics * g = ui::Engine::Ref().g;
+
+ ui::Colour progressBarColour = style::Colour::WarningTitle;
+
+ g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255);
+
+ if(progress!=-1)
+ {
+ if(progress > 0)
+ {
+ if(progress > 100)
+ progress = 100;
+ float size = float(Size.X-4)*(float(progress)/100.0f); // TIL...
+ size = std::min(std::max(size, 0.0f), float(Size.X-4));
+ g->fillrect(screenPos.X + 2, screenPos.Y + 2, size, Size.Y-4, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255);
+ }
+ } else {
+ int size = 40, rsize = 0;
+ float position = float(Size.X-4)*(intermediatePos/100.0f);
+ if(position + size - 1 > Size.X-4)
+ {
+ size = (Size.X-4)-position+1;
+ rsize = 40-size;
+ }
+ g->fillrect(screenPos.X + 2 + position, screenPos.Y + 2, size, Size.Y-4, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255);
+ if(rsize)
+ {
+ g->fillrect(screenPos.X + 2, screenPos.Y + 2, rsize, Size.Y-4, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255);
+ }
+ }
+ if(progress<50)
+ g->drawtext(screenPos.X + ((Size.X-Graphics::textwidth(progressStatus.c_str()))/2), screenPos.Y + (Size.Y-8)/2, progressStatus, 255, 255, 255, 255);
+ else
+ g->drawtext(screenPos.X + ((Size.X-Graphics::textwidth(progressStatus.c_str()))/2), screenPos.Y + (Size.Y-8)/2, progressStatus, 0, 0, 0, 255);
+}
+
+void ProgressBar::Tick(float dt)
+{
+ intermediatePos += 1.0f*dt;
+ if(intermediatePos>100.0f)
+ intermediatePos = 0.0f;
+} \ No newline at end of file
diff --git a/src/interface/ProgressBar.h b/src/interface/ProgressBar.h
new file mode 100644
index 0000000..fc47f0c
--- /dev/null
+++ b/src/interface/ProgressBar.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "Component.h"
+
+namespace ui
+{
+ class ProgressBar: public Component
+ {
+ int progress;
+ float intermediatePos;
+ std::string progressStatus;
+ public:
+ ProgressBar(Point position, Point size, int startProgress = 0, std::string startStatus = "");
+ virtual void SetProgress(int progress);
+ virtual int GetProgress();
+ virtual void SetStatus(std::string status);
+ virtual std::string GetStatus();
+ virtual void Draw(const Point & screenPos);
+ virtual void Tick(float dt);
+ };
+}
diff --git a/src/interface/RichLabel.cpp b/src/interface/RichLabel.cpp
new file mode 100644
index 0000000..84dc500
--- /dev/null
+++ b/src/interface/RichLabel.cpp
@@ -0,0 +1,198 @@
+#include <vector>
+#include <exception>
+
+#include "RichLabel.h"
+#include "Misc.h"
+#include "interface/Point.h"
+#include "interface/Component.h"
+#include "graphics/Graphics.h"
+
+using namespace ui;
+
+struct RichTextParseException: public std::exception {
+ std::string message;
+public:
+ RichTextParseException(std::string message_ = "Parse error"): message(message_) {}
+ const char * what() const throw()
+ {
+ return message.c_str();
+ }
+ ~RichTextParseException() throw() {};
+};
+
+RichLabel::RichLabel(Point position, Point size, std::string labelText):
+ Component(position, size),
+ textSource(labelText),
+ displayText("")
+{
+ updateRichText();
+}
+
+RichLabel::~RichLabel()
+{
+
+}
+
+void RichLabel::updateRichText()
+{
+ regions.clear();
+ displayText = "";
+
+ if(textSource.length())
+ {
+
+ enum State { ReadText, ReadData, ReadRegion, ReadDataStart };
+ State state = ReadText;
+
+ int currentDataPos = 0;
+ char * currentData = new char[textSource.length()+1];
+ std::fill(currentData, currentData+textSource.length()+1, 0);
+
+ int finalTextPos = 0;
+ char * finalText = new char[textSource.length()+1];
+ std::fill(finalText, finalText+textSource.length()+1, 0);
+
+ int originalTextPos = 0;
+ char * originalText = new char[textSource.length()+1];
+ std::copy(textSource.begin(), textSource.end(), originalText);
+ originalText[textSource.length()] = 0;
+
+ int stackPos = -1;
+ RichTextRegion * regionsStack = new RichTextRegion[256];
+
+ try
+ {
+ while(originalText[originalTextPos])
+ {
+ char current = originalText[originalTextPos];
+
+ if(state == ReadText)
+ {
+ if(current == '{')
+ {
+ if(stackPos > 255)
+ throw RichTextParseException("Too many nested regions");
+ stackPos++;
+ regionsStack[stackPos].start = finalTextPos;
+ regionsStack[stackPos].finish = finalTextPos;
+ state = ReadRegion;
+ }
+ else if(current == '}')
+ {
+ if(stackPos >= 0)
+ {
+ currentData[currentDataPos] = 0;
+ regionsStack[stackPos].actionData = std::string(currentData);
+ regions.push_back(regionsStack[stackPos]);
+ stackPos--;
+ }
+ else
+ {
+ throw RichTextParseException("Unexpected '}'");
+ }
+ }
+ else
+ {
+ finalText[finalTextPos++] = current;
+ finalText[finalTextPos] = 0;
+ if(stackPos >= 0)
+ {
+ regionsStack[stackPos].finish = finalTextPos;
+ }
+ }
+ }
+ else if(state == ReadData)
+ {
+ if(current == '|')
+ {
+ state = ReadText;
+ }
+ else
+ {
+ currentData[currentDataPos++] = current;
+ currentData[currentDataPos] = 0;
+ }
+ }
+ else if(state == ReadDataStart)
+ {
+ if(current != ':')
+ {
+ throw RichTextParseException("Expected ':'");
+ }
+ state = ReadData;
+ currentDataPos = 0;
+ }
+ else if(state == ReadRegion)
+ {
+ if(stackPos >= 0)
+ {
+ regionsStack[stackPos].action = current;
+ state = ReadDataStart;
+ }
+ else
+ {
+ throw RichTextParseException();
+ }
+ }
+
+ originalTextPos++;
+ }
+
+ if(stackPos != -1)
+ throw RichTextParseException("Unclosed region");
+
+ finalText[finalTextPos] = 0;
+ displayText = std::string(finalText);
+ }
+ catch (const RichTextParseException & e)
+ {
+ displayText = "\br[Parse exception: " + std::string(e.what()) + "]";
+ regions.clear();
+ }
+ delete[] currentData;
+ delete[] finalText;
+ delete[] originalText;
+ delete[] regionsStack;
+ }
+ TextPosition(displayText);
+}
+
+void RichLabel::SetText(std::string text)
+{
+ textSource = text;
+ updateRichText();
+}
+
+std::string RichLabel::GetDisplayText()
+{
+ return displayText;
+}
+
+std::string RichLabel::GetText()
+{
+ return textSource;
+}
+
+void RichLabel::Draw(const Point& screenPos)
+{
+ Graphics * g = ui::Engine::Ref().g;
+ ui::Colour textColour = Appearance.TextInactive;
+ g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, displayText, textColour.Red, textColour.Green, textColour.Blue, 255);
+}
+
+void RichLabel::OnMouseClick(int x, int y, unsigned button)
+{
+ int cursorPosition = Graphics::CharIndexAtPosition((char*)displayText.c_str(), x-textPosition.X, y-textPosition.Y);
+ for(std::vector<RichTextRegion>::iterator iter = regions.begin(), end = regions.end(); iter != end; ++iter)
+ {
+ if((*iter).start <= cursorPosition && (*iter).finish >= cursorPosition)
+ {
+ switch((*iter).action)
+ {
+ case 'a':
+ OpenURI((*iter).actionData);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/interface/RichLabel.h b/src/interface/RichLabel.h
new file mode 100644
index 0000000..d9682f2
--- /dev/null
+++ b/src/interface/RichLabel.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <string>
+
+#include "Component.h"
+#include "Colour.h"
+
+namespace ui
+{
+ class RichLabel : public Component
+ {
+ public:
+ struct RichTextRegion
+ {
+ int start;
+ int finish;
+ int action;
+ std::string actionData;
+ };
+
+ RichLabel(Point position, Point size, std::string richText);
+
+ virtual ~RichLabel();
+
+ virtual void SetText(std::string text);
+ virtual std::string GetDisplayText();
+ virtual std::string GetText();
+
+ virtual void Draw(const Point& screenPos);
+ virtual void OnMouseClick(int x, int y, unsigned button);
+ protected:
+ std::string textSource;
+ std::string displayText;
+
+ std::vector<RichTextRegion> regions;
+
+ void updateRichText();
+ };
+}
diff --git a/src/interface/SaveButton.cpp b/src/interface/SaveButton.cpp
new file mode 100644
index 0000000..eb2640b
--- /dev/null
+++ b/src/interface/SaveButton.cpp
@@ -0,0 +1,415 @@
+#include <iostream>
+#include <typeinfo>
+
+#include "SaveButton.h"
+#include "client/SaveInfo.h"
+#include "graphics/Graphics.h"
+#include "Engine.h"
+#include "client/ThumbnailBroker.h"
+#include "simulation/SaveRenderer.h"
+#include "Format.h"
+#include "ContextMenu.h"
+#include "Keys.h"
+
+namespace ui {
+
+SaveButton::SaveButton(Point position, Point size, SaveInfo * save):
+ Component(position, size),
+ file(NULL),
+ save(save),
+ thumbnail(NULL),
+ isMouseInside(false),
+ isButtonDown(false),
+ actionCallback(NULL),
+ selectable(false),
+ selected(false),
+ waitingForThumb(false),
+ isMouseInsideAuthor(false),
+ isMouseInsideHistory(false),
+ showVotes(false)
+{
+ menu = new ContextMenu(this);
+ menu->AddItem(ContextMenuItem("Open", 0, true));
+ menu->AddItem(ContextMenuItem("Select", 1, true));
+ menu->AddItem(ContextMenuItem("View History", 2, true));
+ menu->AddItem(ContextMenuItem("More by this user", 3, true));
+
+ if(save)
+ {
+ name = save->name;
+ if(Graphics::textwidth((char *)name.c_str()) > Size.X)
+ {
+ int position = Graphics::textwidthx((char *)name.c_str(), Size.X - 22);
+ name = name.erase(position, name.length()-position);
+ name += "...";
+ }
+
+ std::string votes, icon;
+ int j;
+
+ votes = format::NumberToString<int>(save->GetVotesUp()-save->GetVotesDown());
+ icon += 0xBB;
+ for (int j = 1; j < votes.length(); j++)
+ icon += 0xBC;
+ icon += 0xB9;
+ icon += 0xBA;
+
+ votesBackground = icon;
+
+ for (std::string::iterator iter = icon.begin(), end = icon.end(); iter != end; ++iter)
+ *iter -= 14;
+
+ votesBackground2 = icon;
+
+ for (std::string::iterator iter = votes.begin(), end = votes.end(); iter != end; ++iter)
+ if(*iter != '-')
+ *iter += 127;
+
+ votesString = votes;
+
+ int voteMax = std::max(save->GetVotesUp(),save->GetVotesDown());
+ if (voteMax)
+ {
+ if (voteMax < 34)
+ {
+ float ry = 33.0f/voteMax;
+ if (voteMax<8)
+ ry = ry/(8-voteMax);
+ voteBarHeightUp = (int)(save->GetVotesUp()*ry)-1;
+ voteBarHeightDown = (int)(save->GetVotesDown()*ry)-1;
+ }
+ else
+ {
+ float ry = voteMax/33.0f;
+ voteBarHeightUp = (int)(save->GetVotesUp()/ry)-1;
+ voteBarHeightDown = (int)(save->GetVotesDown()/ry)-1;
+ }
+ }
+ else
+ {
+ voteBarHeightUp = 0;
+ voteBarHeightDown = 0;
+ }
+ }
+}
+
+SaveButton::SaveButton(Point position, Point size, SaveFile * file):
+ Component(position, size),
+ save(NULL),
+ file(file),
+ thumbnail(NULL),
+ isMouseInside(false),
+ isButtonDown(false),
+ actionCallback(NULL),
+ selectable(false),
+ selected(false),
+ wantsDraw(false),
+ waitingForThumb(false),
+ isMouseInsideAuthor(false),
+ isMouseInsideHistory(false),
+ showVotes(false)
+{
+ if(file)
+ {
+ name = file->GetDisplayName();
+ if(Graphics::textwidth((char *)name.c_str()) > Size.X)
+ {
+ int position = Graphics::textwidthx((char *)name.c_str(), Size.X - 22);
+ name = name.erase(position, name.length()-position);
+ name += "...";
+ }
+ }
+}
+
+SaveButton::~SaveButton()
+{
+ ThumbnailBroker::Ref().DetachThumbnailListener(this);
+
+ if(thumbnail)
+ delete thumbnail;
+ if(actionCallback)
+ delete actionCallback;
+ if(save)
+ delete save;
+ if(file)
+ delete file;
+}
+
+void SaveButton::OnThumbnailReady(Thumbnail * thumb)
+{
+ if(thumb)
+ {
+ if(thumbnail)
+ delete thumbnail;
+ thumbnail = thumb;
+ waitingForThumb = false;
+ }
+}
+
+void SaveButton::Tick(float dt)
+{
+ if(!thumbnail && !waitingForThumb)
+ {
+ if(save)
+ {
+ if(save->GetGameSave())
+ {
+ waitingForThumb = true;
+ ThumbnailBroker::Ref().RenderThumbnail(save->GetGameSave(), Size.X-3, Size.Y-25, this);
+ }
+ else if(save->GetID())
+ {
+ waitingForThumb = true;
+ ThumbnailBroker::Ref().RetrieveThumbnail(save->GetID(), save->GetVersion(), Size.X-3, Size.Y-25, this);
+ }
+ }
+ else if(file && file->GetGameSave())
+ {
+ waitingForThumb = true;
+ ThumbnailBroker::Ref().RenderThumbnail(file->GetGameSave(), Size.X-3, Size.Y-25, this);
+ }
+ }
+}
+
+void SaveButton::Draw(const Point& screenPos)
+{
+ Graphics * g = ui::Engine::Ref().g;
+ float scaleFactor;
+ ui::Point thumbBoxSize(0, 0);
+
+ wantsDraw = true;
+
+ if(selected && selectable)
+ {
+ g->fillrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 100, 170, 255, 100);
+ }
+
+ if(thumbnail)
+ {
+ thumbBoxSize = ui::Point(thumbnail->Size.X, thumbnail->Size.Y);
+ if(save && save->id)
+ g->draw_image(thumbnail->Data, screenPos.X-3+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbnail->Size.X, thumbnail->Size.Y, 255);
+ else
+ g->draw_image(thumbnail->Data, screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbnail->Size.X, thumbnail->Size.Y, 255);
+ }
+ else
+ {
+ scaleFactor = (Size.Y-25)/((float)YRES);
+ thumbBoxSize = ui::Point(((float)XRES)*scaleFactor, ((float)YRES)*scaleFactor);
+ }
+ if(save)
+ {
+ if(save->id)
+ {
+ if(isMouseInside)
+ {
+ g->drawrect(screenPos.X-3+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 210, 230, 255, 255);
+ g->drawrect(screenPos.X-4+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, 7, thumbBoxSize.Y, 210, 230, 255, 255);
+ }
+ else
+ {
+ g->drawrect(screenPos.X-3+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 180, 180, 180, 255);
+ g->drawrect(screenPos.X-4+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, 7, thumbBoxSize.Y, 180, 180, 180, 255);
+ }
+
+ g->fillrect(screenPos.X-3+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+1+(Size.Y-20-thumbBoxSize.Y)/2, 5, (thumbBoxSize.Y+1)/2-1, 0, 107, 10, 255);
+ g->fillrect(screenPos.X-3+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-20)/2, 5, thumbBoxSize.Y/2-1, 107, 10, 0, 255);
+
+ g->fillrect(screenPos.X-2+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-20)/2-voteBarHeightUp, 3, voteBarHeightUp, 57, 187, 57, 255); //green
+ g->fillrect(screenPos.X-2+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-20)/2, 3, voteBarHeightDown, 187, 57, 57, 255); //red
+ }
+ else
+ {
+ if(isMouseInside)
+ g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 210, 230, 255, 255);
+ else
+ g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 180, 180, 180, 255);
+ }
+
+ if(isMouseInside && !isMouseInsideAuthor)
+ g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)name.c_str()))/2, screenPos.Y+Size.Y - 21, name, 255, 255, 255, 255);
+ else
+ g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)name.c_str()))/2, screenPos.Y+Size.Y - 21, name, 180, 180, 180, 255);
+
+ if(isMouseInsideAuthor)
+ g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)save->userName.c_str()))/2, screenPos.Y+Size.Y - 10, save->userName, 200, 230, 255, 255);
+ else
+ g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)save->userName.c_str()))/2, screenPos.Y+Size.Y - 10, save->userName, 100, 130, 160, 255);
+ if (showVotes)// && !isMouseInside)
+ {
+ int x = screenPos.X-7+(Size.X-thumbBoxSize.X)/2+thumbBoxSize.X-Graphics::textwidth(votesBackground.c_str());
+ int y = screenPos.Y-23+(Size.Y-thumbBoxSize.Y)/2+thumbBoxSize.Y;
+ g->drawtext(x, y, votesBackground, 16, 72, 16, 255);
+ g->drawtext(x, y, votesBackground2, 192, 192, 192, 255);
+ g->drawtext(x+3, y, votesString, 255, 255, 255, 255);
+ }
+ if (isMouseInsideHistory && showVotes)
+ {
+ int x = screenPos.X;
+ int y = screenPos.Y-15+(Size.Y-thumbBoxSize.Y)/2+thumbBoxSize.Y;
+ g->fillrect(x+1, y+1, 7, 8, 255, 255, 255, 255);
+ if (isMouseInsideHistory) {
+ g->drawtext(x, y, "\xA6", 200, 100, 80, 255);
+ } else {
+ g->drawtext(x, y, "\xA6", 160, 70, 50, 255);
+ }
+ }
+ if (!save->GetPublished())
+ {
+ g->drawtext(screenPos.X, screenPos.Y-2, "\xCD", 255, 255, 255, 255);
+ g->drawtext(screenPos.X, screenPos.Y-2, "\xCE", 212, 151, 81, 255);
+ }
+ }
+ if(file)
+ {
+ if(isMouseInside)
+ g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 210, 230, 255, 255);
+ else
+ g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 180, 180, 180, 255);
+
+ if(isMouseInside)
+ {
+ g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)name.c_str()))/2, screenPos.Y+Size.Y - 21, name, 255, 255, 255, 255);
+ }
+ else
+ {
+ g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)name.c_str()))/2, screenPos.Y+Size.Y - 21, name, 180, 180, 180, 255);
+ }
+ }
+
+ if(isMouseInside && selectable)
+ {
+ g->clearrect(screenPos.X+(Size.X-20), screenPos.Y+6, 14, 14);
+ g->drawrect(screenPos.X+(Size.X-20), screenPos.Y+6, 14, 14, 255, 255, 255, 255);
+ if(selected)
+ g->fillrect(screenPos.X+(Size.X-18), screenPos.Y+8, 10, 10, 255, 255, 255, 255);
+ }
+}
+
+void SaveButton::OnMouseUnclick(int x, int y, unsigned int button)
+{
+ if(button != 1)
+ {
+ return; //left click only!
+ }
+
+ if(x>=Size.X-20 && y>=6 && y<=20 && x<=Size.X-6 && selectable)
+ {
+ selected = !selected;
+ DoSelection();
+ return;
+ }
+
+ if(isButtonDown)
+ {
+ isButtonDown = false;
+ if(isMouseInsideAuthor)
+ DoAuthorAction();
+ else if(isMouseInsideHistory)
+ DoHistoryAction();
+ else
+ DoAction();
+ }
+}
+
+void SaveButton::OnContextMenuAction(int item)
+{
+ switch(item)
+ {
+ case 0:
+ DoAction();
+ break;
+ case 1:
+ selected = !selected;
+ DoSelection();
+ break;
+ case 2:
+ DoHistoryAction();
+ break;
+ case 3:
+ DoAuthorAction();
+ break;
+ }
+}
+
+void SaveButton::OnMouseClick(int x, int y, unsigned int button)
+{
+ if(button == BUTTON_RIGHT)
+ {
+ if(menu)
+ menu->Show(GetScreenPos() + ui::Point(x, y));
+ }
+ else
+ {
+ isButtonDown = true;
+ if(button !=1 && selectable)
+ {
+ selected = !selected;
+ DoSelection();
+ }
+
+ }
+}
+
+void SaveButton::OnMouseMovedInside(int x, int y, int dx, int dy)
+{
+ if(y > Size.Y-11)
+ isMouseInsideAuthor = true;
+ else
+ isMouseInsideAuthor = false;
+
+ if(showVotes && y > Size.Y-29 && y < Size.Y - 18 && x > 0 && x < 9)
+ isMouseInsideHistory = true;
+ else
+ isMouseInsideHistory = false;
+}
+
+void SaveButton::OnMouseEnter(int x, int y)
+{
+ isMouseInside = true;
+}
+
+void SaveButton::OnMouseLeave(int x, int y)
+{
+ isMouseInside = false;
+ isMouseInsideAuthor = false;
+ isMouseInsideHistory = false;
+}
+
+void SaveButton::DoHistoryAction()
+{
+ if(actionCallback)
+ actionCallback->HistoryActionCallback(this);
+}
+
+void SaveButton::DoAuthorAction()
+{
+ if(actionCallback)
+ actionCallback->AuthorActionCallback(this);
+}
+
+void SaveButton::DoAction()
+{
+ if(actionCallback)
+ actionCallback->ActionCallback(this);
+}
+
+void SaveButton::DoSelection()
+{
+ if(menu)
+ {
+ if(selected)
+ menu->SetItem(1, "Deselect");
+ else
+ menu->SetItem(1, "Select");
+ }
+ if(selectable && actionCallback)
+ actionCallback->SelectedCallback(this);
+}
+
+void SaveButton::SetActionCallback(SaveButtonAction * action)
+{
+ actionCallback = action;
+}
+
+} /* namespace ui */
diff --git a/src/interface/SaveButton.h b/src/interface/SaveButton.h
new file mode 100644
index 0000000..74dbc00
--- /dev/null
+++ b/src/interface/SaveButton.h
@@ -0,0 +1,83 @@
+#ifndef SAVEBUTTON_H_
+#define SAVEBUTTON_H_
+
+#include <string>
+
+#include "Component.h"
+#include "client/SaveFile.h"
+#include "client/SaveInfo.h"
+#include "client/ThumbnailListener.h"
+#include "graphics/Graphics.h"
+#include "search/Thumbnail.h"
+#include "interface/Colour.h"
+
+namespace ui
+{
+class SaveButton;
+class SaveButtonAction
+{
+public:
+ virtual void ActionCallback(ui::SaveButton * sender) {}
+ virtual void AuthorActionCallback(ui::SaveButton * sender) {}
+ virtual void HistoryActionCallback(ui::SaveButton * sender) {}
+ virtual void SelectedCallback(ui::SaveButton * sender) {}
+ virtual ~SaveButtonAction() {}
+};
+
+class SaveButton : public Component, public ThumbnailListener
+{
+ SaveFile * file;
+ SaveInfo * save;
+ Thumbnail * thumbnail;
+ std::string name;
+ std::string votesString;
+ std::string votesBackground;
+ std::string votesBackground2;
+ int voteBarHeightUp;
+ int voteBarHeightDown;
+ bool wantsDraw;
+ bool waitingForThumb;
+ bool isMouseInsideAuthor;
+ bool isMouseInsideHistory;
+ bool showVotes;
+public:
+ SaveButton(Point position, Point size, SaveInfo * save);
+ SaveButton(Point position, Point size, SaveFile * file);
+ virtual ~SaveButton();
+
+ virtual void OnMouseClick(int x, int y, unsigned int button);
+ virtual void OnMouseUnclick(int x, int y, unsigned int button);
+
+ virtual void OnMouseEnter(int x, int y);
+ virtual void OnMouseLeave(int x, int y);
+
+ virtual void OnMouseMovedInside(int x, int y, int dx, int dy);
+
+ virtual void OnContextMenuAction(int item);
+
+ virtual void Draw(const Point& screenPos);
+ virtual void Tick(float dt);
+
+ virtual void OnThumbnailReady(Thumbnail * thumb);
+
+ void SetSelected(bool selected_) { selected = selected_; }
+ bool GetSelected() { return selected; }
+ void SetSelectable(bool selectable_) { selectable = selectable_; }
+ bool GetSelectable() { return selectable; }
+ void SetShowVotes(bool showVotes_) { showVotes = showVotes_; }
+
+ SaveInfo * GetSave() { return save; }
+ SaveFile * GetSaveFile() { return file; }
+ inline bool GetState() { return state; }
+ virtual void DoAction();
+ virtual void DoAuthorAction();
+ virtual void DoHistoryAction();
+ virtual void DoSelection();
+ void SetActionCallback(SaveButtonAction * action);
+protected:
+ bool isButtonDown, state, isMouseInside, selected, selectable;
+ SaveButtonAction * actionCallback;
+};
+}
+#endif /* BUTTON_H_ */
+
diff --git a/src/interface/ScrollPanel.cpp b/src/interface/ScrollPanel.cpp
new file mode 100644
index 0000000..c2d4f66
--- /dev/null
+++ b/src/interface/ScrollPanel.cpp
@@ -0,0 +1,121 @@
+#include <iostream>
+#include "ScrollPanel.h"
+
+using namespace ui;
+
+ScrollPanel::ScrollPanel(Point position, Point size):
+ Panel(position, size),
+ maxOffset(0, 0),
+ offsetX(0),
+ offsetY(0),
+ yScrollVel(0.0f),
+ xScrollVel(0.0f),
+ scrollBarWidth(0)
+{
+
+}
+
+int ScrollPanel::GetScrollLimit()
+{
+ if(maxOffset.Y == -ViewportPosition.Y)
+ return 1;
+ else if(ViewportPosition.Y == 0)
+ return -1;
+ return 0;
+}
+
+void ScrollPanel::XOnMouseWheelInside(int localx, int localy, int d)
+{
+ if(!d)
+ return;
+ yScrollVel -= d*2;
+}
+
+void ScrollPanel::Draw(const Point& screenPos)
+{
+ Panel::Draw(screenPos);
+
+ Graphics * g = ui::Engine::Ref().g;
+
+ //Vertical scroll bar
+ if(maxOffset.Y>0 && InnerSize.Y>0)
+ {
+ float scrollHeight = float(Size.Y)*(float(Size.Y)/float(InnerSize.Y));
+ float scrollPos = 0;
+ if(-ViewportPosition.Y>0)
+ {
+ scrollPos = float(Size.Y-scrollHeight)*(float(offsetY)/float(maxOffset.Y));
+ }
+
+ g->fillrect(screenPos.X+(Size.X-scrollBarWidth), screenPos.Y, scrollBarWidth, Size.Y, 125, 125, 125, 100);
+ g->fillrect(screenPos.X+(Size.X-scrollBarWidth), screenPos.Y+scrollPos, scrollBarWidth, scrollHeight, 255, 255, 255, 255);
+ }
+}
+
+void ScrollPanel::XTick(float dt)
+{
+ //if(yScrollVel > 7.0f) yScrollVel = 7.0f;
+ //if(yScrollVel < -7.0f) yScrollVel = -7.0f;
+ if(yScrollVel > -0.5f && yScrollVel < 0.5)
+ yScrollVel = 0;
+
+ if(xScrollVel > 7.0f) xScrollVel = 7.0f;
+ if(xScrollVel < -7.0f) xScrollVel = -7.0f;
+ if(xScrollVel > -0.5f && xScrollVel < 0.5)
+ xScrollVel = 0;
+
+ maxOffset = InnerSize-Size;
+ maxOffset.Y = std::max(0, maxOffset.Y);
+ maxOffset.X = std::max(0, maxOffset.X);
+
+ int oldOffsetY = offsetY;
+ offsetY += yScrollVel;
+ int oldOffsetX = offsetX;
+ offsetX += xScrollVel;
+
+ yScrollVel*=0.98f;
+ xScrollVel*=0.98f;
+
+ if(oldOffsetY!=int(offsetY))
+ {
+ if(offsetY<0)
+ {
+ offsetY = 0;
+ yScrollVel = 0;
+ //commentsBegin = true;
+ //commentsEnd = false;
+ }
+ else if(offsetY>maxOffset.Y)
+ {
+ offsetY = maxOffset.Y;
+ yScrollVel = 0;
+ //commentsEnd = true;
+ //commentsBegin = false;
+ }
+ else
+ {
+ //commentsEnd = false;
+ //commentsBegin = false;
+ }
+ ViewportPosition.Y = -offsetY;
+ }
+ else
+ {
+ if(offsetY<0)
+ {
+ offsetY = 0;
+ yScrollVel = 0;
+ ViewportPosition.Y = -offsetY;
+ }
+ else if(offsetY>maxOffset.Y)
+ {
+ offsetY = maxOffset.Y;
+ ViewportPosition.Y = -offsetY;
+ }
+ }
+
+ if(mouseInside && scrollBarWidth < 6)
+ scrollBarWidth++;
+ else if(!mouseInside && scrollBarWidth > 0)
+ scrollBarWidth--;
+} \ No newline at end of file
diff --git a/src/interface/ScrollPanel.h b/src/interface/ScrollPanel.h
new file mode 100644
index 0000000..fc54b31
--- /dev/null
+++ b/src/interface/ScrollPanel.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Panel.h"
+
+namespace ui
+{
+ class ScrollPanel: public Panel
+ {
+ protected:
+ int scrollBarWidth;
+ Point maxOffset;
+ float offsetX;
+ float offsetY;
+ float yScrollVel;
+ float xScrollVel;
+ public:
+ ScrollPanel(Point position, Point size);
+
+ int GetScrollLimit();
+
+ virtual void Draw(const Point& screenPos);
+ virtual void XTick(float dt);
+ virtual void XOnMouseWheelInside(int localx, int localy, int d);
+ };
+} \ No newline at end of file
diff --git a/src/interface/Slider.cpp b/src/interface/Slider.cpp
new file mode 100644
index 0000000..d5639cb
--- /dev/null
+++ b/src/interface/Slider.cpp
@@ -0,0 +1,149 @@
+/*
+ * Slider.cpp
+ *
+ * Created on: Mar 3, 2012
+ * Author: Simon
+ */
+
+#include <iostream>
+#include "Slider.h"
+#include "Colour.h"
+
+namespace ui {
+
+Slider::Slider(Point position, Point size, int steps):
+ Component(position, size),
+ sliderSteps(steps),
+ sliderPosition(0),
+ isMouseDown(false),
+ bgGradient(NULL),
+ col1(0, 0, 0, 0),
+ col2(0, 0, 0, 0)
+{
+ // TODO Auto-generated constructor stub
+
+}
+
+void Slider::updatePosition(int position)
+{
+ if(position < 3)
+ position = 3;
+ if(position > Size.X-3)
+ position = Size.X-3;
+
+ float fPosition = position-3;
+ float fSize = Size.X-6;
+ float fSteps = sliderSteps;
+
+ float fSliderPosition = (fPosition/fSize)*sliderSteps;//position;//((x-3)/(Size.X-6))*sliderSteps;
+
+ int newSliderPosition = fSliderPosition;
+
+ if(newSliderPosition == sliderPosition)
+ return;
+
+ sliderPosition = newSliderPosition;
+
+ if(actionCallback)
+ {
+ actionCallback->ValueChangedCallback(this);
+ }
+}
+
+void Slider::OnMouseMoved(int x, int y, int dx, int dy)
+{
+ if(isMouseDown)
+ {
+ updatePosition(x);
+ }
+}
+
+void Slider::OnMouseClick(int x, int y, unsigned button)
+{
+ isMouseDown = true;
+ updatePosition(x);
+}
+
+void Slider::OnMouseUp(int x, int y, unsigned button)
+{
+ if(isMouseDown)
+ {
+ isMouseDown = false;
+ }
+}
+
+
+void Slider::SetColour(Colour col1, Colour col2)
+{
+ pixel pix[2] = {PIXRGB(col1.Red, col1.Green, col1.Blue), PIXRGB(col2.Red, col2.Green, col2.Blue)};
+ float fl[2] = {0.0f, 1.0f};
+ if(bgGradient)
+ free(bgGradient);
+ this->col1 = col1;
+ this->col2 = col2;
+ bgGradient = (unsigned char*)Graphics::GenerateGradient(pix, fl, 2, Size.X-7);
+}
+
+int Slider::GetValue()
+{
+ return sliderPosition;
+}
+
+void Slider::SetValue(int value)
+{
+ if(value < 0)
+ value = 0;
+ if(value > sliderSteps)
+ value = sliderSteps;
+ sliderPosition = value;
+}
+
+int Slider::GetSteps()
+{
+ return sliderSteps;
+}
+
+void Slider::SetSteps(int steps)
+{
+ if(steps < 0)
+ steps = 0;
+ if(steps < sliderPosition)
+ sliderPosition = steps;
+ sliderSteps = steps;
+}
+
+void Slider::Draw(const Point& screenPos)
+{
+ Graphics * g = Engine::Ref().g;
+ //g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255);
+
+ if(bgGradient)
+ {
+#ifndef OGLI
+ for (int j = 3; j < Size.Y-7; j++)
+ for (int i = 3; i < Size.X-7; i++)
+ g->blendpixel(screenPos.X+i+2, screenPos.Y+j+2, bgGradient[(i-3)*3], bgGradient[(i-3)*3+1], bgGradient[(i-3)*3+2], 255);
+#else
+ g->gradientrect(screenPos.X+5, screenPos.Y+5, Size.X-10, Size.Y-10, col1.Red, col1.Green, col1.Blue, col1.Alpha, col2.Red, col2.Green, col2.Blue, col2.Alpha);
+#endif
+ }
+
+ g->drawrect(screenPos.X+3, screenPos.Y+3, Size.X-6, Size.Y-6, 255, 255, 255, 255);
+
+ float fPosition = sliderPosition;
+ float fSize = Size.X-6;
+ float fSteps = sliderSteps;
+
+ float fSliderX = (fSize/fSteps)*fPosition;//sliderPosition;//((Size.X-6)/sliderSteps)*sliderPosition;
+ int sliderX = fSliderX;
+ sliderX += 3;
+
+ g->fillrect(screenPos.X+sliderX-2, screenPos.Y+1, 4, Size.Y-2, 20, 20, 20, 255);
+ g->drawrect(screenPos.X+sliderX-2, screenPos.Y+1, 4, Size.Y-2, 200, 200, 200, 255);
+}
+
+Slider::~Slider()
+{
+}
+
+} /* namespace ui */
diff --git a/src/interface/Slider.h b/src/interface/Slider.h
new file mode 100644
index 0000000..65ca0ba
--- /dev/null
+++ b/src/interface/Slider.h
@@ -0,0 +1,47 @@
+/*
+ * Slider.h
+ *
+ * Created on: Mar 3, 2012
+ * Author: Simon
+ */
+
+#ifndef SLIDER_H_
+#define SLIDER_H_
+
+#include "Component.h"
+#include "Colour.h"
+
+namespace ui {
+class Slider;
+class SliderAction
+{
+public:
+ virtual void ValueChangedCallback(ui::Slider * sender) {}
+ virtual ~SliderAction() {}
+};
+class Slider: public ui::Component {
+ friend class SliderAction;
+ int sliderSteps;
+ int sliderPosition;
+ bool isMouseDown;
+ unsigned char * bgGradient;
+ SliderAction * actionCallback;
+ Colour col1, col2;
+ void updatePosition(int position);
+public:
+ Slider(Point position, Point size, int steps);
+ virtual void OnMouseMoved(int x, int y, int dx, int dy);
+ virtual void OnMouseClick(int x, int y, unsigned button);
+ virtual void OnMouseUp(int x, int y, unsigned button);
+ virtual void Draw(const Point& screenPos);
+ void SetColour(Colour col1, Colour col2);
+ void SetActionCallback(SliderAction * action) { actionCallback = action; }
+ int GetValue();
+ void SetValue(int value);
+ int GetSteps();
+ void SetSteps(int steps);
+ virtual ~Slider();
+};
+
+} /* namespace ui */
+#endif /* SLIDER_H_ */
diff --git a/src/interface/Spinner.cpp b/src/interface/Spinner.cpp
new file mode 100644
index 0000000..ea3d1fa
--- /dev/null
+++ b/src/interface/Spinner.cpp
@@ -0,0 +1,45 @@
+/*
+ * Spinner.cpp
+ *
+ * Created on: Feb 11, 2012
+ * Author: Simon
+ */
+
+
+#include <cmath>
+#include <iostream>
+#include "Spinner.h"
+
+using namespace ui;
+
+Spinner::Spinner(Point position, Point size):
+ Component(position, size), cValue(0),
+ tickInternal(0)
+{
+}
+void Spinner::Tick(float dt)
+{
+ tickInternal++;
+ if(tickInternal == 4)
+ {
+ cValue += 0.25f;//0.05f;
+ tickInternal = 0;
+ }
+}
+void Spinner::Draw(const Point& screenPos)
+{
+ Graphics * g = ui::Engine::Ref().g;
+ int baseX = screenPos.X+(Size.X/2);
+ int baseY = screenPos.Y+(Size.Y/2);
+ int lineInner = (Size.X/2);
+ int lineOuter = (Size.X/2)+3;
+ for(float t = 0.0f; t < 6.0f; t+=0.25f)
+ {
+ //g->drawblob(baseX+(sin(cValue+t)*(Size.X/2)), baseY+(cos(cValue+t)*(Size.X/2)), t*255, t*255, t*255);
+ g->draw_line(baseX+(sin(cValue+t)*lineInner), baseY+(cos(cValue+t)*lineInner), baseX+(sin(cValue+t)*lineOuter), baseY+(cos(cValue+t)*lineOuter), (t/6)*255, (t/6)*255, (t/6)*255, 255);
+ }
+}
+Spinner::~Spinner()
+{
+
+}
diff --git a/src/interface/Spinner.h b/src/interface/Spinner.h
new file mode 100644
index 0000000..23d4141
--- /dev/null
+++ b/src/interface/Spinner.h
@@ -0,0 +1,30 @@
+/*
+ * Spinner.h
+ *
+ * Created on: Feb 11, 2012
+ * Author: Simon
+ */
+
+#ifndef SPINNER_H_
+#define SPINNER_H_
+
+#include "Component.h"
+
+namespace ui
+{
+
+class Spinner: public Component
+{
+ float cValue;
+ int tickInternal;
+public:
+ Spinner(Point position, Point size);
+ virtual void Tick(float dt);
+ virtual void Draw(const Point& screenPos);
+ virtual ~Spinner();
+};
+
+}
+
+
+#endif /* SPINNER_H_ */
diff --git a/src/interface/Textbox.cpp b/src/interface/Textbox.cpp
new file mode 100644
index 0000000..383f07b
--- /dev/null
+++ b/src/interface/Textbox.cpp
@@ -0,0 +1,707 @@
+#include <string>
+#include <iostream>
+#include <stdexcept>
+#include <time.h>
+#include "Config.h"
+#include "interface/Point.h"
+#include "interface/Textbox.h"
+#include "interface/Keys.h"
+#include "ContextMenu.h"
+
+using namespace ui;
+
+Textbox::Textbox(Point position, Point size, std::string textboxText, std::string textboxPlaceholder):
+ Label(position, size, ""),
+ actionCallback(NULL),
+ masked(false),
+ border(true),
+ mouseDown(false),
+ limit(std::string::npos),
+ inputType(All),
+ keyDown(0),
+ characterDown(0),
+ ReadOnly(false)
+{
+ placeHolder = textboxPlaceholder;
+
+ SetText(textboxText);
+ cursor = text.length();
+
+ menu->RemoveItem(0);
+ menu->AddItem(ContextMenuItem("Cut", 1, true));
+ menu->AddItem(ContextMenuItem("Copy", 0, true));
+ menu->AddItem(ContextMenuItem("Paste", 2, true));
+}
+
+Textbox::~Textbox()
+{
+ if(actionCallback)
+ delete actionCallback;
+}
+
+void Textbox::SetHidden(bool hidden)
+{
+ menu->RemoveItem(0);
+ menu->RemoveItem(1);
+ menu->RemoveItem(2);
+ menu->AddItem(ContextMenuItem("Cut", 1, !hidden));
+ menu->AddItem(ContextMenuItem("Copy", 0, !hidden));
+ menu->AddItem(ContextMenuItem("Paste", 2, true));
+
+ masked = hidden;
+}
+
+void Textbox::SetPlaceholder(std::string text)
+{
+ placeHolder = text;
+}
+
+void Textbox::SetText(std::string newText)
+{
+ backingText = newText;
+
+ if(masked)
+ {
+ std::string maskedText = std::string(newText);
+ std::fill(maskedText.begin(), maskedText.end(), '\x8D');
+ Label::SetText(maskedText);
+ }
+ else
+ Label::SetText(newText);
+
+ cursor = newText.length();
+
+ if(cursor)
+ {
+ Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
+ }
+ else
+ {
+ cursorPositionY = cursorPositionX = 0;
+ }
+}
+
+Textbox::ValidInput Textbox::GetInputType()
+{
+ return inputType;
+}
+
+void Textbox::SetInputType(ValidInput input)
+{
+ inputType = input;
+}
+
+void Textbox::SetLimit(size_t limit)
+{
+ this->limit = limit;
+}
+
+size_t Textbox::GetLimit()
+{
+ return limit;
+}
+
+std::string Textbox::GetText()
+{
+ return backingText;
+}
+
+void Textbox::OnContextMenuAction(int item)
+{
+ switch(item)
+ {
+ case 0:
+ copySelection();
+ break;
+ case 1:
+ cutSelection();
+ break;
+ case 2:
+ pasteIntoSelection();
+ break;
+ }
+}
+
+void Textbox::cutSelection()
+{
+ char * clipboardText;
+ clipboardText = clipboard_pull_text();
+ std::string newText = std::string(clipboardText);
+ free(clipboardText);
+ if(HasSelection())
+ {
+ if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
+ return;
+ clipboard_push_text((char*)backingText.substr(getLowerSelectionBound(), getHigherSelectionBound()-getLowerSelectionBound()).c_str());
+ backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
+ cursor = getLowerSelectionBound();
+ }
+ else
+ {
+ clipboard_push_text((char*)backingText.c_str());
+ backingText.clear();
+ }
+ ClearSelection();
+
+ if(masked)
+ {
+ std::string maskedText = std::string(backingText);
+ std::fill(maskedText.begin(), maskedText.end(), '\x8D');
+ Label::SetText(maskedText);
+ }
+ else
+ {
+ text = backingText;
+ }
+ if(actionCallback)
+ actionCallback->TextChangedCallback(this);
+
+ if(multiline)
+ updateMultiline();
+ updateSelection();
+ TextPosition(text);
+
+ if(cursor)
+ {
+ Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
+ }
+ else
+ {
+ cursorPositionY = cursorPositionX = 0;
+ }
+}
+
+void Textbox::selectAll()
+{
+ selectionIndex0 = 0;
+ selectionIndex1 = text.length();
+ updateSelection();
+}
+
+void Textbox::pasteIntoSelection()
+{
+ char * clipboardText;
+ clipboardText = clipboard_pull_text();
+ std::string newText = std::string(clipboardText);
+ free(clipboardText);
+ if(HasSelection())
+ {
+ if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
+ return;
+ backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
+ cursor = getLowerSelectionBound();
+ }
+ for(std::string::iterator iter = newText.begin(), end = newText.end(); iter != end; ++iter)
+ {
+ if(!CharacterValid(*iter))
+ {
+ if(inputType == All)
+ {
+ if(*iter == '\n' || *iter == '\r')
+ *iter = ' ';
+ else
+ *iter = '?';
+ }
+ else
+ *iter = '0';
+ }
+ }
+
+ int regionWidth = Size.X;
+ if(Appearance.icon)
+ regionWidth -= 13;
+ regionWidth -= Appearance.Margin.Left;
+ regionWidth -= Appearance.Margin.Right;
+
+ if(limit!=std::string::npos)
+ {
+ if(limit-backingText.length() >= 0)
+ newText = newText.substr(0, limit-backingText.length());
+ else
+ newText = "";
+ }
+ else if(!multiline && Graphics::textwidth((char*)std::string(backingText+newText).c_str()) > regionWidth)
+ {
+ int pLimit = regionWidth - Graphics::textwidth((char*)backingText.c_str());
+ int cIndex = Graphics::CharIndexAtPosition((char *)newText.c_str(), pLimit, 0);
+
+ if(cIndex > 0)
+ newText = newText.substr(0, cIndex);
+ else
+ newText = "";
+ }
+
+ backingText.insert(cursor, newText);
+ cursor = cursor+newText.length();
+ ClearSelection();
+
+ if(masked)
+ {
+ std::string maskedText = std::string(backingText);
+ std::fill(maskedText.begin(), maskedText.end(), '\x8D');
+ Label::SetText(maskedText);
+ }
+ else
+ {
+ text = backingText;
+ }
+ if(actionCallback)
+ actionCallback->TextChangedCallback(this);
+
+ if(multiline)
+ updateMultiline();
+ updateSelection();
+ if(multiline)
+ TextPosition(textLines);
+ else
+ TextPosition(text);
+
+ if(cursor)
+ {
+ Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
+ }
+ else
+ {
+ cursorPositionY = cursorPositionX = 0;
+ }
+}
+
+bool Textbox::CharacterValid(Uint16 character)
+{
+ switch(inputType)
+ {
+ case Number:
+ case Numeric:
+ return (character >= '0' && character <= '9');
+ case All:
+ default:
+ return (character >= ' ' && character < 127);
+ }
+ return false;
+}
+
+void Textbox::Tick(float dt)
+{
+ Label::Tick(dt);
+ if(!IsFocused())
+ {
+ keyDown = 0;
+ characterDown = 0;
+ }
+ if((keyDown || characterDown) && repeatTime <= clock())
+ {
+ OnVKeyPress(keyDown, characterDown, false, false, false);
+ repeatTime = clock()+(0.03 * CLOCKS_PER_SEC);
+ }
+}
+
+void Textbox::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ keyDown = 0;
+ characterDown = 0;
+}
+
+void Textbox::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ characterDown = character;
+ keyDown = key;
+ repeatTime = clock()+(0.3 * CLOCKS_PER_SEC);
+ OnVKeyPress(key, character, shift, ctrl, alt);
+}
+
+void Textbox::OnVKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ bool changed = false;
+ if(ctrl && key == 'c' && !masked)
+ {
+ copySelection();
+ return;
+ }
+ if(ctrl && key == 'v' && !ReadOnly)
+ {
+ pasteIntoSelection();
+ return;
+ }
+ if(ctrl && key == 'x' && !masked && !ReadOnly)
+ {
+ cutSelection();
+ return;
+ }
+ if(ctrl && key == 'a')
+ {
+ selectAll();
+ return;
+ }
+
+ try
+ {
+ switch(key)
+ {
+ case KEY_HOME:
+ cursor = 0;
+ ClearSelection();
+ break;
+ case KEY_END:
+ cursor = backingText.length();
+ ClearSelection();
+ break;
+ case KEY_LEFT:
+ if(cursor > 0)
+ cursor--;
+ ClearSelection();
+ break;
+ case KEY_RIGHT:
+ if(cursor < backingText.length())
+ cursor++;
+ ClearSelection();
+ break;
+ case KEY_DELETE:
+ if(ReadOnly)
+ break;
+ if(HasSelection())
+ {
+ if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
+ return;
+ backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
+ cursor = getLowerSelectionBound();
+ changed = true;
+ }
+ else if(backingText.length() && cursor < backingText.length())
+ {
+ if(ctrl)
+ backingText.erase(cursor, backingText.length()-cursor);
+ else
+ backingText.erase(cursor, 1);
+ changed = true;
+ }
+ ClearSelection();
+ break;
+ case KEY_BACKSPACE:
+ if(ReadOnly)
+ break;
+ if(HasSelection())
+ {
+ if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
+ return;
+ backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
+ cursor = getLowerSelectionBound();
+ changed = true;
+ }
+ else if(backingText.length() && cursor > 0)
+ {
+ if(ctrl)
+ {
+ backingText.erase(0, cursor);
+ cursor = 0;
+ }
+ else
+ {
+ backingText.erase(cursor-1, 1);
+ cursor--;
+ }
+ changed = true;
+ }
+ ClearSelection();
+ break;
+ }
+ if(CharacterValid(character) && !ReadOnly)
+ {
+ if(HasSelection())
+ {
+ if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
+ return;
+ backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
+ cursor = getLowerSelectionBound();
+ }
+
+ int regionWidth = Size.X;
+ if(Appearance.icon)
+ regionWidth -= 13;
+ regionWidth -= Appearance.Margin.Left;
+ regionWidth -= Appearance.Margin.Right;
+ if((limit==std::string::npos || backingText.length() < limit) && (Graphics::textwidth((char*)std::string(backingText+char(character)).c_str()) <= regionWidth || multiline || limit!=std::string::npos))
+ {
+ if(cursor == backingText.length())
+ {
+ backingText += character;
+ }
+ else
+ {
+ backingText.insert(cursor, 1, (char)character);
+ }
+ cursor++;
+ }
+ changed = true;
+ ClearSelection();
+ }
+ }
+ catch(std::out_of_range &e)
+ {
+ cursor = 0;
+ backingText = "";
+ }
+ if(inputType == Number)
+ {
+ //Remove extra preceding 0's
+ while(backingText[0] == '0' && backingText.length()>1)
+ backingText.erase(backingText.begin());
+ }
+ if(cursor > backingText.length())
+ cursor = backingText.length();
+ if(changed)
+ {
+ if(masked)
+ {
+ std::string maskedText = std::string(backingText);
+ std::fill(maskedText.begin(), maskedText.end(), '\x8D');
+ Label::SetText(maskedText);
+ }
+ else
+ {
+ text = backingText;
+ }
+ if(actionCallback)
+ actionCallback->TextChangedCallback(this);
+ }
+
+ if(multiline)
+ updateMultiline();
+ updateSelection();
+ if(multiline)
+ TextPosition(textLines);
+ else
+ TextPosition(text);
+
+ if(cursor)
+ {
+ Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
+ }
+ else
+ {
+ cursorPositionY = cursorPositionX = 0;
+ }
+}
+
+void Textbox::OnMouseClick(int x, int y, unsigned button)
+{
+
+ if(button != BUTTON_RIGHT)
+ {
+ mouseDown = true;
+ cursor = Graphics::CharIndexAtPosition(multiline?((char*)textLines.c_str()):((char*)text.c_str()), x-textPosition.X, y-textPosition.Y);
+ if(cursor)
+ {
+ Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
+ }
+ else
+ {
+ cursorPositionY = cursorPositionX = 0;
+ }
+ }
+ Label::OnMouseClick(x, y, button);
+}
+
+void Textbox::OnMouseUp(int x, int y, unsigned button)
+{
+ mouseDown = false;
+ Label::OnMouseUp(x, y, button);
+}
+
+void Textbox::OnMouseMoved(int localx, int localy, int dx, int dy)
+{
+ if(mouseDown)
+ {
+ cursor = Graphics::CharIndexAtPosition(multiline?((char*)textLines.c_str()):((char*)text.c_str()), localx-textPosition.X, localy-textPosition.Y);
+ if(cursor)
+ {
+ Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
+ }
+ else
+ {
+ cursorPositionY = cursorPositionX = 0;
+ }
+ }
+ Label::OnMouseMoved(localx, localy, dx, dy);
+}
+
+void Textbox::Draw(const Point& screenPos)
+{
+ Label::Draw(screenPos);
+
+ Graphics * g = Engine::Ref().g;
+ if(IsFocused())
+ {
+ if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255);
+ g->draw_line(screenPos.X+textPosition.X+cursorPositionX, screenPos.Y-2+textPosition.Y+cursorPositionY, screenPos.X+textPosition.X+cursorPositionX, screenPos.Y+9+textPosition.Y+cursorPositionY, 255, 255, 255, 255);
+ }
+ else
+ {
+ if(!text.length())
+ {
+ g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, placeHolder, textColour.Red, textColour.Green, textColour.Blue, 170);
+ }
+ if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 160, 160, 160, 255);
+ }
+ if(Appearance.icon)
+ g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon);
+}
+
+/*
+Textbox::Textbox(Point position, Point size, std::string textboxText):
+ Component(position, size),
+ text(textboxText),
+ actionCallback(NULL),
+ masked(false),
+ border(true)
+{
+ SetText(textboxText);
+ cursor = text.length();
+}
+
+Textbox::~Textbox()
+{
+ if(actionCallback)
+ delete actionCallback;
+}
+
+void Textbox::TextPosition()
+{
+ if(cursor)
+ {
+ cursorPosition = Graphics::textnwidth((char *)displayText.c_str(), cursor);
+ }
+ else
+ {
+ cursorPosition = 0;
+ }
+ Component::TextPosition(displayText);
+}
+
+void Textbox::SetText(std::string text)
+{
+ cursor = text.length();
+ this->text = text;
+ this->displayText = text;
+ TextPosition();
+}
+
+
+void Textbox::SetDisplayText(std::string text)
+{
+ displayText = text;
+ TextPosition();
+}
+
+std::string Textbox::GetText()
+{
+ return text;
+}
+
+void Textbox::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ bool changed = false;
+ try
+ {
+ switch(key)
+ {
+ case KEY_HOME:
+ cursor = 0;
+ break;
+ case KEY_END:
+ cursor = text.length();
+ break;
+ case KEY_LEFT:
+ if(cursor > 0)
+ cursor--;
+ break;
+ case KEY_RIGHT:
+ if(cursor < text.length())
+ cursor++;
+ break;
+ case KEY_DELETE:
+ if(text.length() && cursor < text.length())
+ {
+ if(ctrl)
+ text.erase(cursor, text.length()-cursor);
+ else
+ text.erase(cursor, 1);
+ changed = true;
+ }
+ break;
+ case KEY_BACKSPACE:
+ if(text.length() && cursor > 0)
+ {
+ if(ctrl)
+ {
+ text.erase(0, cursor);
+ cursor = 0;
+ }
+ else
+ {
+ text.erase(cursor-1, 1);
+ cursor--;
+ }
+ changed = true;
+ }
+ break;
+ }
+ if(character >= ' ' && character < 127)
+ {
+ if(cursor == text.length())
+ {
+ text += character;
+ }
+ else
+ {
+ text.insert(cursor, 1, (char)character);
+ }
+ cursor++;
+ changed = true;
+ }
+ }
+ catch(std::out_of_range &e)
+ {
+ cursor = 0;
+ text = "";
+ }
+ if(changed)
+ {
+ if(masked)
+ {
+ char * tempText = new char[text.length()+1];
+ std::fill(tempText, tempText+text.length(), 0x8d);
+ tempText[text.length()] = 0;
+ displayText = std::string(tempText);
+ delete tempText;
+ }
+ else
+ {
+ displayText = text;
+ }
+ if(actionCallback)
+ actionCallback->TextChangedCallback(this);
+ }
+ TextPosition();
+}
+
+void Textbox::Draw(const Point& screenPos)
+{
+ if(!drawn)
+ {
+ TextPosition();
+ drawn = true;
+ }
+ Graphics * g = Engine::Ref().g;
+ if(IsFocused())
+ {
+ if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255);
+ g->draw_line(screenPos.X+textPosition.X+cursorPosition, screenPos.Y+3, screenPos.X+textPosition.X+cursorPosition, screenPos.Y+12, 255, 255, 255, XRES+BARSIZE);
+ }
+ else
+ {
+ if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 160, 160, 160, 255);
+ }
+ g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, displayText, 255, 255, 255, 255);
+ if(Appearance.icon)
+ g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon);
+}*/
diff --git a/src/interface/Textbox.h b/src/interface/Textbox.h
new file mode 100644
index 0000000..7d06111
--- /dev/null
+++ b/src/interface/Textbox.h
@@ -0,0 +1,108 @@
+#ifndef TEXTBOX_H
+#define TEXTBOX_H
+
+#include <string>
+
+#include "Label.h"
+#include "Misc.h"
+
+namespace ui
+{
+class Textbox;
+class TextboxAction
+{
+public:
+ virtual void TextChangedCallback(ui::Textbox * sender) {}
+ virtual ~TextboxAction() {}
+};
+
+class Textbox : public Label
+{
+ friend class TextboxAction;
+public:
+ bool ReadOnly;
+ enum ValidInput { All, Numeric, Number };
+ Textbox(Point position, Point size, std::string textboxText = "", std::string textboxPlaceholder = "");
+ virtual ~Textbox();
+
+ virtual void SetText(std::string text);
+ virtual std::string GetText();
+
+ virtual void SetPlaceholder(std::string text);
+
+ void SetBorder(bool border) { this->border = border; };
+ void SetHidden(bool hidden);
+ bool GetHidden() { return masked; }
+ void SetActionCallback(TextboxAction * action) { actionCallback = action; }
+
+ void SetLimit(size_t limit);
+ size_t GetLimit();
+
+ ValidInput GetInputType();
+ void SetInputType(ValidInput input);
+
+ //Determines if the given character is valid given the input type
+ bool CharacterValid(Uint16 character);
+
+ virtual void Tick(float dt);
+ virtual void OnContextMenuAction(int item);
+ virtual void OnMouseClick(int x, int y, unsigned button);
+ virtual void OnMouseUp(int x, int y, unsigned button);
+ virtual void OnMouseMoved(int localx, int localy, int dx, int dy);
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual void OnVKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual void Draw(const Point& screenPos);
+
+protected:
+ ValidInput inputType;
+ size_t limit;
+ int repeatTime;
+ int keyDown;
+ Uint16 characterDown;
+ bool mouseDown;
+ bool masked, border;
+ int cursor, cursorPositionX, cursorPositionY;
+ TextboxAction *actionCallback;
+ std::string backingText;
+ std::string placeHolder;
+
+ virtual void selectAll();
+ virtual void cutSelection();
+ virtual void pasteIntoSelection();
+};
+
+/*class Textbox : public Component
+{
+ friend class TextboxAction;
+protected:
+ std::string text;
+ std::string displayText;
+ int cursor, cursorPosition;
+ TextboxAction *actionCallback;
+ bool masked;
+ bool border;
+public:
+ Textbox(Point position, Point size, std::string textboxText);
+ virtual ~Textbox();
+
+ virtual void SetText(std::string text);
+ virtual void SetDisplayText(std::string text);
+ std::string GetText();
+ void SetActionCallback(TextboxAction * action) { actionCallback = action; }
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+
+ void SetHidden(bool hidden) { masked = hidden; }
+ bool GetHidden() { return masked; }
+
+ void SetBorder(bool border) {this->border = border;}
+
+ void TextPosition();
+
+ virtual void Draw(const Point& screenPos);
+};
+}*/
+}
+
+
+#endif // TEXTBOX_H
diff --git a/src/interface/Window.cpp b/src/interface/Window.cpp
new file mode 100644
index 0000000..e36f9c1
--- /dev/null
+++ b/src/interface/Window.cpp
@@ -0,0 +1,558 @@
+#include <iostream>
+#include "Window.h"
+#include "Keys.h"
+#include "Component.h"
+#include "interface/Point.h"
+#include "interface/Button.h"
+
+using namespace ui;
+
+Window::Window(Point _position, Point _size):
+ Position(_position),
+ Size(_size),
+ focusedComponent_(NULL),
+ AllowExclusiveDrawing(true),
+ halt(false),
+ destruct(false),
+ stop(false),
+ cancelButton(NULL),
+ okayButton(NULL)
+#ifdef DEBUG
+ ,debugMode(false)
+#endif
+{
+}
+
+Window::~Window()
+{
+ for(unsigned i = 0, sz = Components.size(); i < sz; ++i)
+ if( Components[i] )
+ {
+ delete Components[i];
+ if(Components[i]==focusedComponent_)
+ focusedComponent_ = NULL;
+ }
+ Components.clear();
+}
+
+void Window::AddComponent(Component* c)
+{
+ // TODO: do a check if component was already added?
+ if(c->GetParentWindow()==NULL)
+ {
+ c->SetParentWindow(this);
+ Components.push_back(c);
+
+ if(Engine::Ref().GetMouseX() > Position.X + c->Position.X && Engine::Ref().GetMouseX() < Position.X + c->Position.X + c->Size.X &&
+ Engine::Ref().GetMouseY() > Position.Y + c->Position.Y && Engine::Ref().GetMouseY() < Position.Y + c->Position.Y + c->Size.Y)
+ c->OnMouseEnter(Engine::Ref().GetMouseX() - (Position.X + c->Position.X), Engine::Ref().GetMouseY() - (Position.Y + c->Position.Y));
+ }
+ else
+ {
+ //Component already has a state, don't sad it
+ }
+}
+
+unsigned Window::GetComponentCount()
+{
+ return Components.size();
+}
+
+Component* Window::GetComponent(unsigned idx)
+{
+ return Components[idx];
+}
+
+void Window::RemoveComponent(Component* c)
+{
+ // remove component WITHOUT freeing it.
+ for(unsigned i = 0; i < Components.size(); ++i)
+ {
+ // find the appropriate component index
+ if(Components[i] == c)
+ {
+ //Make sure any events don't continue
+ halt = true;
+ if(Components[i]==focusedComponent_)
+ focusedComponent_ = NULL;
+
+ Components.erase(Components.begin() + i);
+
+ // we're done
+ return;
+ }
+ }
+}
+
+void Window::OnTryExit(ExitMethod method)
+{
+ if(cancelButton && method != MouseOutside)
+ cancelButton->DoAction();
+}
+
+void Window::OnTryOkay(OkayMethod method)
+{
+ if(okayButton)
+ okayButton->DoAction();
+}
+
+void Window::RemoveComponent(unsigned idx)
+{
+ halt = true;
+ // free component and remove it.
+ if(Components[idx]==focusedComponent_)
+ focusedComponent_ = NULL;
+ delete Components[idx];
+ Components.erase(Components.begin() + idx);
+}
+
+bool Window::IsFocused(const Component* c) const
+{
+ return c == focusedComponent_;
+}
+
+void Window::FocusComponent(Component* c)
+{
+ this->focusedComponent_ = c;
+}
+
+void Window::DoExit()
+{
+
+ OnExit();
+}
+
+void Window::DoInitialized()
+{
+
+ OnInitialized();
+}
+
+void Window::DoBlur()
+{
+
+ OnBlur();
+}
+
+void Window::DoFocus()
+{
+
+ OnFocus();
+}
+
+void Window::DoDraw()
+{
+ OnDraw();
+ //draw
+ for(int i = 0, sz = Components.size(); i < sz; ++i)
+ if(Components[i]->Visible)
+ {
+ if(AllowExclusiveDrawing)
+ {
+ Point scrpos(Components[i]->Position.X + Position.X, Components[i]->Position.Y + Position.Y);
+ Components[i]->Draw(scrpos);
+ }
+ else
+ {
+ if( Components[i]->Position.X+Position.X + Components[i]->Size.X >= 0 &&
+ Components[i]->Position.Y+Position.Y + Components[i]->Size.Y >= 0 &&
+ Components[i]->Position.X+Position.X < ui::Engine::Ref().GetWidth() &&
+ Components[i]->Position.Y+Position.Y < ui::Engine::Ref().GetHeight() )
+ {
+ Point scrpos(Components[i]->Position.X + Position.X, Components[i]->Position.Y + Position.Y);
+ Components[i]->Draw( Point(scrpos) );
+ }
+ }
+#ifdef DEBUG
+ if(debugMode)
+ {
+ if(focusedComponent_==Components[i])
+ {
+ ui::Engine::Ref().g->fillrect(Components[i]->Position.X+Position.X, Components[i]->Position.Y+Position.Y, Components[i]->Size.X, Components[i]->Size.Y, 0, 255, 0, 90);
+ }
+ else
+ {
+ ui::Engine::Ref().g->fillrect(Components[i]->Position.X+Position.X, Components[i]->Position.Y+Position.Y, Components[i]->Size.X, Components[i]->Size.Y, 255, 0, 0, 90);
+ }
+ }
+#endif
+ }
+#ifdef DEBUG
+ if(debugMode)
+ {
+ if(focusedComponent_)
+ {
+ int xPos = focusedComponent_->Position.X+focusedComponent_->Size.X+5+Position.X;
+ Graphics * g = ui::Engine::Ref().g;
+ char tempString[512];
+ char tempString2[512];
+
+ sprintf(tempString, "Position: L %d, R %d, T: %d, B: %d", focusedComponent_->Position.X, Size.X-(focusedComponent_->Position.X+focusedComponent_->Size.X), focusedComponent_->Position.Y, Size.Y-(focusedComponent_->Position.Y+focusedComponent_->Size.Y));
+ sprintf(tempString2, "Size: %d, %d", focusedComponent_->Size.X, focusedComponent_->Size.Y);
+
+ if(Graphics::textwidth(tempString)+xPos > XRES+BARSIZE)
+ xPos = XRES+BARSIZE-(Graphics::textwidth(tempString)+5);
+ if(Graphics::textwidth(tempString2)+xPos > XRES+BARSIZE)
+ xPos = XRES+BARSIZE-(Graphics::textwidth(tempString2)+5);
+
+ g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+1, tempString, 0, 0, 0, 200);
+ g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y, tempString, 255, 255, 255, 255);
+ g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+13, tempString2, 0, 0, 0, 200);
+ g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+12, tempString2, 255, 255, 255, 255);
+ }
+ return;
+ }
+#endif
+
+}
+
+void Window::DoTick(float dt)
+{
+#ifdef DEBUG
+ if(debugMode)
+ return;
+#endif
+ //on mouse hover
+ for(int i = Components.size() - 1; i >= 0 && !halt; --i)
+ {
+ if(!Components[i]->Locked &&
+ ui::Engine::Ref().GetMouseX() >= Components[i]->Position.X+Position.X &&
+ ui::Engine::Ref().GetMouseY() >= Components[i]->Position.Y+Position.Y &&
+ ui::Engine::Ref().GetMouseX() < Components[i]->Position.X+Position.X + Components[i]->Size.X &&
+ ui::Engine::Ref().GetMouseY() < Components[i]->Position.Y+Position.Y + Components[i]->Size.Y )
+ {
+ Components[i]->OnMouseHover(ui::Engine::Ref().GetMouseX() - (Components[i]->Position.X + Position.X), ui::Engine::Ref().GetMouseY() - (Components[i]->Position.Y + Position.Y));
+ break;
+ }
+ }
+
+ //tick
+ for(int i = 0, sz = Components.size(); i < sz && !halt; ++i)
+ {
+ Components[i]->Tick(dt);
+ }
+
+ halt = false;
+ stop = false;
+
+ OnTick(dt);
+
+ if(destruct)
+ finalise();
+}
+
+void Window::DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+#ifdef DEBUG
+ if(key == KEY_TAB && ctrl)
+ debugMode = !debugMode;
+ if(debugMode)
+ {
+ if(focusedComponent_!=NULL)
+ {
+ if(shift)
+ {
+ if(key == KEY_UP)
+ focusedComponent_->Size.Y--;
+ if(key == KEY_DOWN)
+ focusedComponent_->Size.Y++;
+ if(key == KEY_LEFT)
+ focusedComponent_->Size.X--;
+ if(key == KEY_RIGHT)
+ focusedComponent_->Size.X++;
+ }
+ if(ctrl)
+ {
+ if(key == KEY_UP)
+ focusedComponent_->Size.Y++;
+ if(key == KEY_DOWN)
+ focusedComponent_->Size.Y--;
+ if(key == KEY_LEFT)
+ focusedComponent_->Size.X++;
+ if(key == KEY_RIGHT)
+ focusedComponent_->Size.X--;
+ }
+ if(!shift)
+ {
+ if(key == KEY_UP)
+ focusedComponent_->Position.Y--;
+ if(key == KEY_DOWN)
+ focusedComponent_->Position.Y++;
+ if(key == KEY_LEFT)
+ focusedComponent_->Position.X--;
+ if(key == KEY_RIGHT)
+ focusedComponent_->Position.X++;
+ }
+ if (key == KEY_DELETE)
+ {
+ RemoveComponent(focusedComponent_);
+ halt = false;
+ }
+ }
+ else
+ {
+ if(shift)
+ {
+ if(key == KEY_UP)
+ Size.Y--;
+ if(key == KEY_DOWN)
+ Size.Y++;
+ if(key == KEY_LEFT)
+ Size.X--;
+ if(key == KEY_RIGHT)
+ Size.X++;
+ }
+ if(ctrl)
+ {
+ if(key == KEY_UP)
+ Size.Y++;
+ if(key == KEY_DOWN)
+ Size.Y--;
+ if(key == KEY_LEFT)
+ Size.X++;
+ if(key == KEY_RIGHT)
+ Size.X--;
+ }
+ if(!shift)
+ {
+ if(key == KEY_UP)
+ Position.Y--;
+ if(key == KEY_DOWN)
+ Position.Y++;
+ if(key == KEY_LEFT)
+ Position.X--;
+ if(key == KEY_RIGHT)
+ Position.X++;
+ }
+ }
+ return;
+ }
+#endif
+ //on key press
+ if(focusedComponent_ != NULL)
+ {
+ if(!focusedComponent_->Locked && focusedComponent_->Visible)
+ focusedComponent_->OnKeyPress(key, character, shift, ctrl, alt);
+ }
+
+ if(!stop)
+ OnKeyPress(key, character, shift, ctrl, alt);
+
+ if(key == KEY_ESCAPE)
+ OnTryExit(Escape);
+
+ if(key == KEY_ENTER || key == KEY_RETURN)
+ OnTryOkay(Enter);
+
+ if(destruct)
+ finalise();
+}
+
+void Window::DoKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+#ifdef DEBUG
+ if(debugMode)
+ return;
+#endif
+ //on key unpress
+ if(focusedComponent_ != NULL)
+ {
+ if(!focusedComponent_->Locked && focusedComponent_->Visible)
+ focusedComponent_->OnKeyRelease(key, character, shift, ctrl, alt);
+ }
+
+ if(!stop)
+ OnKeyRelease(key, character, shift, ctrl, alt);
+ if(destruct)
+ finalise();
+}
+
+void Window::DoMouseDown(int x_, int y_, unsigned button)
+{
+ //on mouse click
+ int x = x_ - Position.X;
+ int y = y_ - Position.Y;
+ bool clickState = false;
+ for(int i = Components.size() - 1; i > -1 && !halt; --i)
+ {
+ if(!Components[i]->Locked && Components[i]->Visible)
+ {
+ if(x >= Components[i]->Position.X && y >= Components[i]->Position.Y && x < Components[i]->Position.X + Components[i]->Size.X && y < Components[i]->Position.Y + Components[i]->Size.Y)
+ {
+ FocusComponent(Components[i]);
+#ifdef DEBUG
+ if(!debugMode)
+#endif
+ Components[i]->OnMouseClick(x - Components[i]->Position.X, y - Components[i]->Position.Y, button);
+ clickState = true;
+ break;
+ }
+ }
+ }
+
+ if(!clickState)
+ FocusComponent(NULL);
+
+#ifdef DEBUG
+ if(debugMode)
+ return;
+#endif
+
+ //on mouse down
+ for(int i = Components.size() - 1; i > -1 && !halt; --i)
+ {
+ if(!Components[i]->Locked && Components[i]->Visible)
+ Components[i]->OnMouseDown(x, y, button);
+ }
+
+ if(!stop)
+ OnMouseDown(x_, y_, button);
+
+ if(x_ < Position.X || y_ < Position.Y || x_ > Position.X+Size.X || y_ > Position.Y+Size.Y)
+ OnTryExit(MouseOutside);
+
+ if(destruct)
+ finalise();
+}
+
+void Window::DoMouseMove(int x_, int y_, int dx, int dy)
+{
+ //on mouse move (if true, and inside)
+ int x = x_ - Position.X;
+ int y = y_ - Position.Y;
+#ifdef DEBUG
+ if(debugMode)
+ return;
+#endif
+ for(int i = Components.size() - 1; i > -1 && !halt; --i)
+ {
+ if(!Components[i]->Locked && Components[i]->Visible)
+ {
+ Point local (x - Components[i]->Position.X, y - Components[i]->Position.Y)
+ , a (local.X - dx, local.Y - dy);
+
+ Components[i]->OnMouseMoved(local.X, local.Y, dx, dy);
+
+ if(local.X >= 0 &&
+ local.Y >= 0 &&
+ local.X < Components[i]->Size.X &&
+ local.Y < Components[i]->Size.Y && !halt)
+ {
+ Components[i]->OnMouseMovedInside(local.X, local.Y, dx, dy);
+
+ // entering?
+ if(!(
+ a.X >= 0 &&
+ a.Y >= 0 &&
+ a.X < Components[i]->Size.X &&
+ a.Y < Components[i]->Size.Y ))
+ {
+ Components[i]->OnMouseEnter(local.X, local.Y);
+ }
+ }
+ else if(!halt)
+ {
+ // leaving?
+ if( a.X >= 0 &&
+ a.Y >= 0 &&
+ a.X < Components[i]->Size.X &&
+ a.Y < Components[i]->Size.Y )
+ {
+ Components[i]->OnMouseLeave(local.X, local.Y);
+ }
+
+ }
+ }
+ }
+
+ if(!stop)
+ OnMouseMove(x_, y_, dx, dy);
+ if(destruct)
+ finalise();
+}
+
+void Window::DoMouseUp(int x_, int y_, unsigned button)
+{
+ int x = x_ - Position.X;
+ int y = y_ - Position.Y;
+#ifdef DEBUG
+ if(debugMode)
+ return;
+#endif
+ //on mouse unclick
+ for(int i = Components.size() - 1; i >= 0 && !halt; --i)
+ {
+ if(!Components[i]->Locked && Components[i]->Visible)
+ {
+ if(x >= Components[i]->Position.X && y >= Components[i]->Position.Y && x < Components[i]->Position.X + Components[i]->Size.X && y < Components[i]->Position.Y + Components[i]->Size.Y)
+ {
+ Components[i]->OnMouseUnclick(x - Components[i]->Position.X, y - Components[i]->Position.Y, button);
+ break;
+ }
+ }
+ }
+
+ //on mouse up
+ for(int i = Components.size() - 1; i >= 0 && !halt; --i)
+ {
+ if(!Components[i]->Locked && Components[i]->Visible)
+ Components[i]->OnMouseUp(x, y, button);
+ }
+
+ if(!stop)
+ OnMouseUp(x_, y_, button);
+ if(destruct)
+ finalise();
+}
+
+void Window::DoMouseWheel(int x_, int y_, int d)
+{
+ int x = x_ - Position.X;
+ int y = y_ - Position.Y;
+#ifdef DEBUG
+ if(debugMode)
+ return;
+#endif
+ //on mouse wheel focused
+ for(int i = Components.size() - 1; i >= 0 && !halt; --i)
+ {
+ if(x >= Components[i]->Position.X && y >= Components[i]->Position.Y && x < Components[i]->Position.X + Components[i]->Size.X && y < Components[i]->Position.Y + Components[i]->Size.Y)
+ {
+ if(!Components[i]->Locked && Components[i]->Visible)
+ Components[i]->OnMouseWheelInside(x - Components[i]->Position.X, y - Components[i]->Position.Y, d);
+ break;
+ }
+ }
+
+ //on mouse wheel
+ for(int i = Components.size() - 1; i >= 0 && !halt; --i)
+ {
+ if(!Components[i]->Locked && Components[i]->Visible)
+ Components[i]->OnMouseWheel(x - Components[i]->Position.X, y - Components[i]->Position.Y, d);
+ }
+
+ if(!stop)
+ OnMouseWheel(x_, y_, d);
+
+ if(destruct)
+ finalise();
+}
+
+void Window::finalise()
+{
+ delete this;
+}
+
+void Window::SelfDestruct()
+{
+ destruct = true;
+ halt = true;
+ stop = true;
+}
+
+void Window::Halt()
+{
+ stop = true;
+ halt = true;
+}
+
diff --git a/src/interface/Window.h b/src/interface/Window.h
new file mode 100644
index 0000000..7906393
--- /dev/null
+++ b/src/interface/Window.h
@@ -0,0 +1,135 @@
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <vector>
+#include "interface/Point.h"
+#include "Engine.h"
+
+namespace ui
+{
+
+enum ChromeStyle
+{
+ None, Title, Resizable
+};
+//class State;
+ class Engine;
+ class Component;
+ class Button;
+
+ /* class State
+ *
+ * A UI state. Contains all components.
+ */
+ class Window
+ {
+ public:
+ Point Position;
+ Point Size;
+
+ Window(Point _position, Point _size);
+ virtual ~Window();
+
+ void SetOkayButton(ui::Button * button) { okayButton = button; }
+ void SetCancelButton(ui::Button * button) { cancelButton = button; }
+
+ bool AllowExclusiveDrawing; //false will not call draw on objects outside of bounds
+
+ // Add Component to state
+ void AddComponent(Component* c);
+
+ // Get the number of components this state has.
+ unsigned GetComponentCount();
+
+ // Get component by index. (See GetComponentCount())
+ Component* GetComponent(unsigned idx);
+
+ // Remove a component from state. NOTE: This DOES NOT free component from memory.
+ void RemoveComponent(Component* c);
+
+ // Remove a component from state. NOTE: This WILL free component from memory.
+ void RemoveComponent(unsigned idx);
+
+ virtual void ToolTip(Component * sender, ui::Point mousePosition, std::string toolTip) {}
+
+ virtual void DoInitialized();
+ virtual void DoExit();
+ virtual void DoTick(float dt);
+ virtual void DoDraw();
+ virtual void DoFocus();
+ virtual void DoBlur();
+
+ virtual void DoMouseMove(int x, int y, int dx, int dy);
+ virtual void DoMouseDown(int x, int y, unsigned button);
+ virtual void DoMouseUp(int x, int y, unsigned button);
+ virtual void DoMouseWheel(int x, int y, int d);
+ virtual void DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual void DoKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+
+ //Sets halt and destroy, this causes the Windows to stop sending events and remove itself.
+ void SelfDestruct();
+ void Halt();
+
+ bool IsFocused(const Component* c) const;
+ void FocusComponent(Component* c);
+
+ void* UserData;
+
+ enum OkayMethod { Enter, OkayButton };
+ enum ExitMethod { MouseOutside, Escape, ExitButton };
+
+ protected:
+ ui::Button * okayButton;
+ ui::Button * cancelButton;
+
+ virtual void OnInitialized() {}
+ virtual void OnExit() {}
+ virtual void OnTick(float dt) {}
+ virtual void OnDraw() {}
+ virtual void OnFocus() {}
+ virtual void OnBlur() {}
+
+ virtual void OnTryExit(ExitMethod);
+ virtual void OnTryOkay(OkayMethod);
+
+ virtual void OnMouseMove(int x, int y, int dx, int dy) {}
+ virtual void OnMouseDown(int x, int y, unsigned button) {}
+ virtual void OnMouseUp(int x, int y, unsigned button) {}
+ virtual void OnMouseWheel(int x, int y, int d) {}
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) {}
+ virtual void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) {}
+ std::vector<Component*> Components;
+ Component* focusedComponent_;
+ ChromeStyle chrome;
+
+ //These controls allow a component to call the destruction of the Window inside an event (called by the Window)
+ void finalise();
+ bool halt;
+ bool destruct;
+ bool stop;
+#ifdef DEBUG
+ bool debugMode;
+#endif
+
+ };
+
+
+/*class Window : public State
+{
+private:
+ ChromeStyle chrome;
+public:
+ Window(Point _position, Point _size);
+ Point Position;
+ Point Size;
+
+ virtual void DoTick(float dt);
+ virtual void DoDraw();
+
+ virtual void DoMouseMove(int x, int y, int dx, int dy);
+ virtual void DoMouseDown(int x, int y, unsigned button);
+ virtual void DoMouseUp(int x, int y, unsigned button);
+ virtual void DoMouseWheel(int x, int y, int d);
+};*/
+}
+#endif // WINDOW_H
diff --git a/src/localbrowser/LocalBrowserController.cpp b/src/localbrowser/LocalBrowserController.cpp
new file mode 100644
index 0000000..e932e72
--- /dev/null
+++ b/src/localbrowser/LocalBrowserController.cpp
@@ -0,0 +1,172 @@
+/*
+ * StampsController.cpp
+ *
+ * Created on: Mar 29, 2012
+ * Author: Simon
+ */
+#include <sstream>
+
+#include "client/Client.h"
+#include "LocalBrowserController.h"
+#include "interface/Engine.h"
+#include "dialogues/ConfirmPrompt.h"
+#include "tasks/TaskWindow.h"
+#include "tasks/Task.h"
+
+#include "LocalBrowserModel.h"
+#include "LocalBrowserView.h"
+
+LocalBrowserController::LocalBrowserController(ControllerCallback * callback):
+ HasDone(false)
+{
+ browserModel = new LocalBrowserModel();
+ browserView = new LocalBrowserView();
+ browserView->AttachController(this);
+ browserModel->AddObserver(browserView);
+
+ this->callback = callback;
+
+ browserModel->UpdateSavesList(1);
+}
+
+void LocalBrowserController::OpenSave(SaveFile * save)
+{
+ browserModel->SetSave(save);
+}
+
+SaveFile * LocalBrowserController::GetSave()
+{
+ return browserModel->GetSave();
+}
+
+void LocalBrowserController::RemoveSelected()
+{
+ class RemoveSelectedConfirmation: public ConfirmDialogueCallback {
+ public:
+ LocalBrowserController * c;
+ RemoveSelectedConfirmation(LocalBrowserController * c_) { c = c_; }
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ c->removeSelectedC();
+ }
+ virtual ~RemoveSelectedConfirmation() { }
+ };
+
+ std::stringstream desc;
+ desc << "Are you sure you want to delete " << browserModel->GetSelected().size() << " stamp";
+ if(browserModel->GetSelected().size()>1)
+ desc << "s";
+ new ConfirmPrompt("Delete stamps", desc.str(), new RemoveSelectedConfirmation(this));
+}
+
+void LocalBrowserController::removeSelectedC()
+{
+ class RemoveSavesTask : public Task
+ {
+ std::vector<std::string> saves;
+ LocalBrowserController * c;
+ public:
+ RemoveSavesTask(LocalBrowserController * c, std::vector<std::string> saves_) : c(c) { saves = saves_; }
+ virtual bool doWork()
+ {
+ for(int i = 0; i < saves.size(); i++)
+ {
+ std::stringstream saveName;
+ saveName << "Deleting stamp [" << saves[i] << "] ...";
+ notifyStatus(saveName.str());
+ Client::Ref().DeleteStamp(saves[i]);
+ notifyProgress((float(i+1)/float(saves.size())*100));
+ }
+ return true;
+ }
+ virtual void after()
+ {
+ c->RefreshSavesList();
+ }
+ };
+
+ std::vector<std::string> selected = browserModel->GetSelected();
+ new TaskWindow("Removing saves", new RemoveSavesTask(this, selected));
+}
+
+void LocalBrowserController::RescanStamps()
+{
+ class RescanConfirmation: public ConfirmDialogueCallback {
+ public:
+ LocalBrowserController * c;
+ RescanConfirmation(LocalBrowserController * c_) { c = c_; }
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ c->rescanStampsC();
+ }
+ virtual ~RescanConfirmation() { }
+ };
+
+ std::stringstream desc;
+ desc << "Rescanning the stamps folder can find stamps added to the stamps folder or recover stamps when the stamps.def file has been lost or damaged. However, be warned that this will mess up the current sorting order";
+ new ConfirmPrompt("Rescan", desc.str(), new RescanConfirmation(this));
+}
+
+void LocalBrowserController::rescanStampsC()
+{
+ browserModel->RescanStamps();
+ browserModel->UpdateSavesList(browserModel->GetPageNum());
+}
+
+void LocalBrowserController::RefreshSavesList()
+{
+ ClearSelection();
+ browserModel->UpdateSavesList(browserModel->GetPageNum());
+}
+
+void LocalBrowserController::ClearSelection()
+{
+ browserModel->ClearSelected();
+}
+
+void LocalBrowserController::NextPage()
+{
+ if(browserModel->GetPageNum() < browserModel->GetPageCount())
+ browserModel->UpdateSavesList(browserModel->GetPageNum()+1);
+}
+
+void LocalBrowserController::PrevPage()
+{
+ if(browserModel->GetPageNum()>1)
+ browserModel->UpdateSavesList(browserModel->GetPageNum()-1);
+}
+
+void LocalBrowserController::Update()
+{
+ if(browserModel->GetSave())
+ {
+ Exit();
+ }
+}
+
+void LocalBrowserController::Selected(std::string saveName, bool selected)
+{
+ if(selected)
+ browserModel->SelectSave(saveName);
+ else
+ browserModel->DeselectSave(saveName);
+}
+
+void LocalBrowserController::Exit()
+{
+ if(ui::Engine::Ref().GetWindow() == browserView)
+ ui::Engine::Ref().CloseWindow();
+ if(callback)
+ callback->ControllerExit();
+ HasDone = true;
+}
+
+LocalBrowserController::~LocalBrowserController() {
+ if(ui::Engine::Ref().GetWindow() == browserView)
+ ui::Engine::Ref().CloseWindow();
+ if(callback)
+ delete callback;
+ delete browserModel;
+ delete browserView;
+}
+
diff --git a/src/localbrowser/LocalBrowserController.h b/src/localbrowser/LocalBrowserController.h
new file mode 100644
index 0000000..efa9c93
--- /dev/null
+++ b/src/localbrowser/LocalBrowserController.h
@@ -0,0 +1,42 @@
+/*
+ * StampsController.h
+ *
+ * Created on: Mar 29, 2012
+ * Author: Simon
+ */
+
+#ifndef STAMPSCONTROLLER_H_
+#define STAMPSCONTROLLER_H_
+
+#include "Controller.h"
+#include "LocalBrowserView.h"
+#include "client/SaveInfo.h"
+
+class LocalBrowserView;
+class LocalBrowserModel;
+class LocalBrowserController {
+ ControllerCallback * callback;
+ LocalBrowserView * browserView;
+ LocalBrowserModel * browserModel;
+public:
+ bool HasDone;
+ LocalBrowserController(ControllerCallback * callback);
+ LocalBrowserView * GetView() {return browserView;}
+ SaveFile * GetSave();
+ void RemoveSelected();
+ void removeSelectedC();
+ void ClearSelection();
+ void Selected(std::string stampID, bool selected);
+ void RescanStamps();
+ void rescanStampsC();
+ void RefreshSavesList();
+ void OpenSave(SaveFile * stamp);
+ void SetStamp();
+ void NextPage();
+ void PrevPage();
+ void Update();
+ void Exit();
+ virtual ~LocalBrowserController();
+};
+
+#endif /* STAMPSCONTROLLER_H_ */
diff --git a/src/localbrowser/LocalBrowserModel.cpp b/src/localbrowser/LocalBrowserModel.cpp
new file mode 100644
index 0000000..9e869cc
--- /dev/null
+++ b/src/localbrowser/LocalBrowserModel.cpp
@@ -0,0 +1,143 @@
+/*
+ * StampsModel.cpp
+ *
+ * Created on: Mar 29, 2012
+ * Author: Simon
+ */
+
+#include "LocalBrowserModel.h"
+#include "LocalBrowserView.h"
+#include "LocalBrowserModelException.h"
+#include "client/Client.h"
+#include "client/SaveFile.h"
+
+LocalBrowserModel::LocalBrowserModel():
+ stamp(NULL),
+ currentPage(1)
+{
+ // TODO Auto-generated constructor stub
+ //stampIDs = Client::Ref().GetStamps();
+ stampIDs = Client::Ref().GetStamps(0, 16);
+}
+
+
+std::vector<SaveFile*> LocalBrowserModel::GetSavesList()
+{
+ return savesList;
+}
+
+void LocalBrowserModel::AddObserver(LocalBrowserView * observer)
+{
+ observers.push_back(observer);
+ observer->NotifySavesListChanged(this);
+ observer->NotifyPageChanged(this);
+}
+
+void LocalBrowserModel::notifySavesListChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifySavesListChanged(this);
+ }
+}
+
+void LocalBrowserModel::notifyPageChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyPageChanged(this);
+ }
+}
+
+SaveFile * LocalBrowserModel::GetSave()
+{
+ return stamp;
+}
+
+void LocalBrowserModel::SetSave(SaveFile * newStamp)
+{
+ if(stamp)
+ delete stamp;
+ stamp = new SaveFile(*newStamp);
+}
+
+void LocalBrowserModel::UpdateSavesList(int pageNumber)
+{
+ std::vector<SaveFile*> tempSavesList = savesList;
+ savesList.clear();
+ currentPage = pageNumber;
+ notifyPageChanged();
+ notifySavesListChanged();
+ //notifyStampsListChanged();
+ /*for(int i = 0; i < tempSavesList.size(); i++)
+ {
+ delete tempSavesList[i];
+ }*/
+
+ stampIDs = Client::Ref().GetStamps((pageNumber-1)*20, 20);
+
+ for(int i = 0; i<stampIDs.size(); i++)
+ {
+ SaveFile * tempSave = Client::Ref().GetStamp(stampIDs[i]);
+ if(tempSave)
+ {
+ savesList.push_back(tempSave);
+ }
+ }
+ notifySavesListChanged();
+}
+
+void LocalBrowserModel::RescanStamps()
+{
+ Client::Ref().RescanStamps();
+}
+
+int LocalBrowserModel::GetPageCount()
+{
+ return std::max(1, (int)(std::ceil(float(Client::Ref().GetStampsCount())/20.0f)));
+}
+
+void LocalBrowserModel::SelectSave(std::string stampID)
+{
+ for(int i = 0; i < selected.size(); i++)
+ {
+ if(selected[i]==stampID)
+ {
+ return;
+ }
+ }
+ selected.push_back(stampID);
+ notifySelectedChanged();
+}
+
+void LocalBrowserModel::DeselectSave(std::string stampID)
+{
+ bool changed = false;
+restart:
+ for(int i = 0; i < selected.size(); i++)
+ {
+ if(selected[i]==stampID)
+ {
+ selected.erase(selected.begin()+i);
+ changed = true;
+ goto restart; //Just ensure all cases are removed.
+ }
+ }
+ if(changed)
+ notifySelectedChanged();
+}
+
+void LocalBrowserModel::notifySelectedChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ LocalBrowserView* cObserver = observers[i];
+ cObserver->NotifySelectedChanged(this);
+ }
+}
+
+LocalBrowserModel::~LocalBrowserModel() {
+ if(stamp)
+ delete stamp;
+}
+
diff --git a/src/localbrowser/LocalBrowserModel.h b/src/localbrowser/LocalBrowserModel.h
new file mode 100644
index 0000000..c2a6f9b
--- /dev/null
+++ b/src/localbrowser/LocalBrowserModel.h
@@ -0,0 +1,45 @@
+/*
+ * StampsModel.h
+ *
+ * Created on: Mar 29, 2012
+ * Author: Simon
+ */
+
+#ifndef STAMPSMODEL_H_
+#define STAMPSMODEL_H_
+
+#include <vector>
+#include <string>
+#include <cmath>
+
+class SaveFile;
+
+class LocalBrowserView;
+class LocalBrowserModel {
+ std::vector<std::string> selected;
+ SaveFile * stamp;
+ std::vector<std::string> stampIDs;
+ std::vector<SaveFile*> savesList;
+ std::vector<LocalBrowserView*> observers;
+ int currentPage;
+ void notifySavesListChanged();
+ void notifyPageChanged();
+ void notifySelectedChanged();
+public:
+ LocalBrowserModel();
+ int GetPageCount();
+ int GetPageNum() { return currentPage; }
+ void AddObserver(LocalBrowserView * observer);
+ std::vector<SaveFile *> GetSavesList();
+ void UpdateSavesList(int pageNumber);
+ void RescanStamps();
+ SaveFile * GetSave();
+ void SetSave(SaveFile * newStamp);
+ std::vector<std::string> GetSelected() { return selected; }
+ void ClearSelected() { selected.clear(); notifySelectedChanged(); }
+ void SelectSave(std::string stampID);
+ void DeselectSave(std::string stampID);
+ virtual ~LocalBrowserModel();
+};
+
+#endif /* STAMPSMODEL_H_ */
diff --git a/src/localbrowser/LocalBrowserModelException.h b/src/localbrowser/LocalBrowserModelException.h
new file mode 100644
index 0000000..087f7df
--- /dev/null
+++ b/src/localbrowser/LocalBrowserModelException.h
@@ -0,0 +1,23 @@
+/*
+ * StampsModelException.h
+ *
+ * Created on: Mar 29, 2012
+ * Author: Simon
+ */
+
+#ifndef STAMPSMODELEXCEPTION_H_
+#define STAMPSMODELEXCEPTION_H_
+
+#include <string>
+#include <exception>
+using namespace std;
+
+class LocalBrowserModelException {
+ string message;
+public:
+ LocalBrowserModelException(string message_): message(message_) {};
+ const char * what() const throw() { return message.c_str(); };
+ ~LocalBrowserModelException() throw() {};
+};
+
+#endif /* STAMPSMODELEXCEPTION_H_ */
diff --git a/src/localbrowser/LocalBrowserView.cpp b/src/localbrowser/LocalBrowserView.cpp
new file mode 100644
index 0000000..a6efc4c
--- /dev/null
+++ b/src/localbrowser/LocalBrowserView.cpp
@@ -0,0 +1,220 @@
+/*
+ * StampsView.cpp
+ *
+ * Created on: Mar 29, 2012
+ * Author: Simon
+ */
+
+#include <sstream>
+#include "client/Client.h"
+#include "LocalBrowserView.h"
+
+#include "interface/Button.h"
+#include "interface/Textbox.h"
+#include "interface/Label.h"
+#include "interface/SaveButton.h"
+#include "interface/Keys.h"
+
+#include "dialogues/ErrorMessage.h"
+#include "dialogues/ConfirmPrompt.h"
+#include "LocalBrowserController.h"
+#include "LocalBrowserModel.h"
+#include "LocalBrowserModelException.h"
+
+LocalBrowserView::LocalBrowserView():
+ ui::Window(ui::Point(0, 0), ui::Point(XRES+BARSIZE, YRES+MENUSIZE))
+{
+ nextButton = new ui::Button(ui::Point(XRES+BARSIZE-52, YRES+MENUSIZE-18), ui::Point(50, 16), "Next \x95");
+ previousButton = new ui::Button(ui::Point(1, YRES+MENUSIZE-18), ui::Point(50, 16), "\x96 Prev");
+ undeleteButton = new ui::Button(ui::Point(XRES+BARSIZE-122, YRES+MENUSIZE-18), ui::Point(60, 16), "Rescan");
+ infoLabel = new ui::Label(ui::Point(51, YRES+MENUSIZE-18), ui::Point(XRES+BARSIZE-102, 16), "Loading...");
+ AddComponent(infoLabel);
+ AddComponent(nextButton);
+ AddComponent(previousButton);
+ AddComponent(undeleteButton);
+
+ class NextPageAction : public ui::ButtonAction
+ {
+ LocalBrowserView * v;
+ public:
+ NextPageAction(LocalBrowserView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->NextPage();
+ }
+ };
+ nextButton->SetActionCallback(new NextPageAction(this));
+ nextButton->Appearance.HorizontalAlign = ui::Appearance::AlignRight;
+ nextButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+
+ class PrevPageAction : public ui::ButtonAction
+ {
+ LocalBrowserView * v;
+ public:
+ PrevPageAction(LocalBrowserView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->PrevPage();
+ }
+ };
+ previousButton->SetActionCallback(new PrevPageAction(this));
+ previousButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ previousButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+
+ class UndeleteAction : public ui::ButtonAction
+ {
+ LocalBrowserView * v;
+ public:
+ UndeleteAction(LocalBrowserView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->RescanStamps();
+ }
+ };
+ undeleteButton->SetActionCallback(new UndeleteAction(this));
+
+ class RemoveSelectedAction : public ui::ButtonAction
+ {
+ LocalBrowserView * v;
+ public:
+ RemoveSelectedAction(LocalBrowserView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->RemoveSelected();
+ }
+ };
+
+ removeSelected = new ui::Button(ui::Point((((XRES+BARSIZE)-100)/2), YRES+MENUSIZE-18), ui::Point(100, 16), "Delete");
+ removeSelected->Visible = false;
+ removeSelected->SetActionCallback(new RemoveSelectedAction(this));
+ AddComponent(removeSelected);
+}
+
+void LocalBrowserView::OnTick(float dt)
+{
+ c->Update();
+}
+
+void LocalBrowserView::NotifyPageChanged(LocalBrowserModel * sender)
+{
+ std::stringstream pageInfo;
+ pageInfo << "Page " << sender->GetPageNum() << " of " << sender->GetPageCount();
+ infoLabel->SetText(pageInfo.str());
+ if(sender->GetPageNum() == 1)
+ {
+ previousButton->Visible = false;
+ }
+ else
+ {
+ previousButton->Visible = true;
+ }
+ if(sender->GetPageNum() == sender->GetPageCount())
+ {
+ nextButton->Visible = false;
+ }
+ else
+ {
+ nextButton->Visible = true;
+ }
+}
+
+void LocalBrowserView::NotifySavesListChanged(LocalBrowserModel * sender)
+{
+ int i = 0;
+ int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 2;
+ int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset;
+
+ vector<SaveFile*> saves = sender->GetSavesList();
+ for(i = 0; i < stampButtons.size(); i++)
+ {
+ RemoveComponent(stampButtons[i]);
+ delete stampButtons[i];
+ }
+ stampButtons.clear();
+ buttonXOffset = 0;
+ buttonYOffset = 50;
+ buttonAreaWidth = Size.X;
+ buttonAreaHeight = Size.Y - buttonYOffset - 18;
+ buttonWidth = (buttonAreaWidth/savesX) - buttonPadding*2;
+ buttonHeight = (buttonAreaHeight/savesY) - buttonPadding*2;
+ class SaveOpenAction: public ui::SaveButtonAction
+ {
+ LocalBrowserView * v;
+ public:
+ SaveOpenAction(LocalBrowserView * _v) { v = _v; }
+ virtual void ActionCallback(ui::SaveButton * sender)
+ {
+ if(sender->GetSaveFile())
+ v->c->OpenSave(sender->GetSaveFile());
+ }
+ virtual void SelectedCallback(ui::SaveButton * sender)
+ {
+ if(sender->GetSaveFile())
+ v->c->Selected(sender->GetSaveFile()->GetName(), sender->GetSelected());
+ }
+ };
+ for(i = 0; i < saves.size(); i++)
+ {
+ if(saveX == savesX)
+ {
+ if(saveY == savesY-1)
+ break;
+ saveX = 0;
+ saveY++;
+ }
+ ui::SaveButton * saveButton;
+ saveButton = new ui::SaveButton(
+ ui::Point(
+ buttonXOffset + buttonPadding + saveX*(buttonWidth+buttonPadding*2),
+ buttonYOffset + buttonPadding + saveY*(buttonHeight+buttonPadding*2)
+ ),
+ ui::Point(buttonWidth, buttonHeight),
+ saves[i]);
+ saveButton->SetSelectable(true);
+ saveButton->SetActionCallback(new SaveOpenAction(this));
+ stampButtons.push_back(saveButton);
+ AddComponent(saveButton);
+ saveX++;
+ }
+}
+
+void LocalBrowserView::NotifySelectedChanged(LocalBrowserModel * sender)
+{
+ vector<std::string> selected = sender->GetSelected();
+ for(int j = 0; j < stampButtons.size(); j++)
+ {
+ stampButtons[j]->SetSelected(false);
+ for(int i = 0; i < selected.size(); i++)
+ {
+ if(stampButtons[j]->GetSaveFile()->GetName()==selected[i])
+ stampButtons[j]->SetSelected(true);
+ }
+ }
+
+ if(selected.size())
+ {
+ removeSelected->Visible = true;
+ }
+ else
+ removeSelected->Visible = false;
+}
+
+void LocalBrowserView::OnMouseWheel(int x, int y, int d)
+{
+ if(!d)
+ return;
+ if(d<0)
+ c->NextPage();
+ else
+ c->PrevPage();
+}
+void LocalBrowserView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(key==KEY_ESCAPE)
+ c->Exit();
+}
+
+LocalBrowserView::~LocalBrowserView() {
+ // TODO Auto-generated destructor stub
+}
+
diff --git a/src/localbrowser/LocalBrowserView.h b/src/localbrowser/LocalBrowserView.h
new file mode 100644
index 0000000..7a8c813
--- /dev/null
+++ b/src/localbrowser/LocalBrowserView.h
@@ -0,0 +1,44 @@
+/*
+ * StampsView.h
+ *
+ * Created on: Mar 29, 2012
+ * Author: Simon
+ */
+
+#ifndef STAMPSVIEW_H_
+#define STAMPSVIEW_H_
+
+#include <vector>
+#include "interface/Window.h"
+
+namespace ui
+{
+ class Label;
+ class Button;
+ class SaveButton;
+}
+
+class LocalBrowserController;
+class LocalBrowserModel;
+class LocalBrowserView: public ui::Window {
+ LocalBrowserController * c;
+ std::vector<ui::SaveButton*> stampButtons;
+ ui::Button * undeleteButton;
+ ui::Button * previousButton;
+ ui::Button * nextButton;
+ ui::Label * infoLabel;
+ ui::Button * removeSelected;
+public:
+ LocalBrowserView();
+ //virtual void OnDraw();
+ virtual void OnTick(float dt);
+ void AttachController(LocalBrowserController * c_) { c = c_; };
+ void NotifyPageChanged(LocalBrowserModel * sender);
+ void NotifySavesListChanged(LocalBrowserModel * sender);
+ void NotifySelectedChanged(LocalBrowserModel * sender);
+ virtual void OnMouseWheel(int x, int y, int d);
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual ~LocalBrowserView();
+};
+
+#endif /* STAMPSVIEW_H_ */
diff --git a/src/login/LoginController.cpp b/src/login/LoginController.cpp
new file mode 100644
index 0000000..f27ad59
--- /dev/null
+++ b/src/login/LoginController.cpp
@@ -0,0 +1,59 @@
+/*
+ * LoginController.cpp
+ *
+ * Created on: Jan 24, 2012
+ * Author: Simon
+ */
+
+#include "LoginController.h"
+#include "client/User.h"
+#include "client/Client.h"
+
+LoginController::LoginController(ControllerCallback * callback):
+ HasExited(false)
+{
+ // TODO Auto-generated constructor stub
+ loginView = new LoginView();
+ loginModel = new LoginModel();
+
+ loginView->AttachController(this);
+ loginModel->AddObserver(loginView);
+
+ this->callback = callback;
+
+}
+
+void LoginController::Login(string username, string password)
+{
+ loginModel->Login(username, password);
+}
+
+User LoginController::GetUser()
+{
+ return loginModel->GetUser();
+}
+
+void LoginController::Exit()
+{
+ if(ui::Engine::Ref().GetWindow() == loginView)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ if(callback)
+ callback->ControllerExit();
+ else
+ {
+ Client::Ref().SetAuthUser(loginModel->GetUser());
+ }
+ HasExited = true;
+}
+
+LoginController::~LoginController() {
+ if(ui::Engine::Ref().GetWindow() == loginView)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ delete loginModel;
+ delete loginView;
+}
+
diff --git a/src/login/LoginController.h b/src/login/LoginController.h
new file mode 100644
index 0000000..b20e187
--- /dev/null
+++ b/src/login/LoginController.h
@@ -0,0 +1,35 @@
+/*
+ * LoginController.h
+ *
+ * Created on: Jan 24, 2012
+ * Author: Simon
+ */
+
+#ifndef LOGINCONTROLLER_H_
+#define LOGINCONTROLLER_H_
+
+#include <string>
+#include "LoginView.h"
+#include "LoginModel.h"
+#include "Controller.h"
+#include "client/User.h"
+
+using namespace std;
+
+class LoginView;
+class LoginModel;
+class LoginController {
+ LoginView * loginView;
+ LoginModel * loginModel;
+ ControllerCallback * callback;
+public:
+ bool HasExited;
+ LoginController(ControllerCallback * callback = NULL);
+ void Login(string username, string password);
+ void Exit();
+ LoginView * GetView() { return loginView; }
+ User GetUser();
+ virtual ~LoginController();
+};
+
+#endif /* LOGINCONTROLLER_H_ */
diff --git a/src/login/LoginModel.cpp b/src/login/LoginModel.cpp
new file mode 100644
index 0000000..3015613
--- /dev/null
+++ b/src/login/LoginModel.cpp
@@ -0,0 +1,67 @@
+/*
+ * LoginModel.cpp
+ *
+ * Created on: Jan 24, 2012
+ * Author: Simon
+ */
+
+#include "LoginModel.h"
+
+LoginModel::LoginModel():
+ currentUser(0, "")
+{
+ // TODO Auto-generated constructor stub
+
+}
+
+void LoginModel::Login(string username, string password)
+{
+ statusText = "Logging in...";
+ loginStatus = false;
+ notifyStatusChanged();
+ LoginStatus status = Client::Ref().Login(username, password, currentUser);
+ switch(status)
+ {
+ case LoginOkay:
+ statusText = "Logged in";
+ loginStatus = true;
+ break;
+ case LoginError:
+ statusText = "Error: " + Client::Ref().GetLastError();
+ break;
+ }
+ notifyStatusChanged();
+}
+
+void LoginModel::AddObserver(LoginView * observer)
+{
+ observers.push_back(observer);
+}
+
+string LoginModel::GetStatusText()
+{
+ return statusText;
+}
+
+User LoginModel::GetUser()
+{
+ return currentUser;
+}
+
+bool LoginModel::GetStatus()
+{
+ return loginStatus;
+}
+
+void LoginModel::notifyStatusChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyStatusChanged(this);
+ }
+}
+
+LoginModel::~LoginModel() {
+ // TODO Auto-generated destructor stub
+}
+
diff --git a/src/login/LoginModel.h b/src/login/LoginModel.h
new file mode 100644
index 0000000..121d78e
--- /dev/null
+++ b/src/login/LoginModel.h
@@ -0,0 +1,35 @@
+/*
+ * LoginModel.h
+ *
+ * Created on: Jan 24, 2012
+ * Author: Simon
+ */
+
+#ifndef LOGINMODEL_H_
+#define LOGINMODEL_H_
+
+#include <vector>
+#include <string>
+#include "LoginView.h"
+#include "client/Client.h"
+
+using namespace std;
+
+class LoginView;
+class LoginModel {
+ vector<LoginView*> observers;
+ string statusText;
+ bool loginStatus;
+ void notifyStatusChanged();
+ User currentUser;
+public:
+ LoginModel();
+ void Login(string username, string password);
+ void AddObserver(LoginView * observer);
+ string GetStatusText();
+ bool GetStatus();
+ User GetUser();
+ virtual ~LoginModel();
+};
+
+#endif /* LOGINMODEL_H_ */
diff --git a/src/login/LoginView.cpp b/src/login/LoginView.cpp
new file mode 100644
index 0000000..8cbdcfb
--- /dev/null
+++ b/src/login/LoginView.cpp
@@ -0,0 +1,151 @@
+/*
+ * LoginView.cpp
+ *
+ * Created on: Jan 24, 2012
+ * Author: Simon
+ */
+
+#include "LoginView.h"
+
+#include "interface/Button.h"
+#include "interface/Label.h"
+#include "interface/Textbox.h"
+#include "interface/Keys.h"
+#include "Style.h"
+
+class LoginView::LoginAction : public ui::ButtonAction
+{
+ LoginView * v;
+public:
+ LoginAction(LoginView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->Login(v->usernameField->GetText(), v->passwordField->GetText());
+ }
+};
+
+class LoginView::CancelAction : public ui::ButtonAction
+{
+ LoginView * v;
+public:
+ CancelAction(LoginView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->Exit();
+ }
+};
+
+LoginView::LoginView():
+ ui::Window(ui::Point(-1, -1), ui::Point(200, 87)),
+ loginButton(new ui::Button(ui::Point(200-100, 87-17), ui::Point(100, 17), "Sign in")),
+ cancelButton(new ui::Button(ui::Point(0, 87-17), ui::Point(101, 17), "Cancel")),
+ titleLabel(new ui::Label(ui::Point(4, 5), ui::Point(200-16, 16), "Server login")),
+ usernameField(new ui::Textbox(ui::Point(8, 25), ui::Point(200-16, 17), Client::Ref().GetAuthUser().Username, "[username]")),
+ passwordField(new ui::Textbox(ui::Point(8, 46), ui::Point(200-16, 17), "", "[password]")),
+ infoLabel(new ui::Label(ui::Point(8, 67), ui::Point(200-16, 16), "")),
+ targetSize(0, 0)
+{
+ targetSize = Size;
+ FocusComponent(usernameField);
+
+ infoLabel->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; infoLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ infoLabel->Visible = false;
+ AddComponent(infoLabel);
+
+ AddComponent(loginButton);
+ SetOkayButton(loginButton);
+ loginButton->Appearance.HorizontalAlign = ui::Appearance::AlignRight;
+ loginButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ loginButton->Appearance.TextInactive = style::Colour::ConfirmButton;
+ loginButton->SetActionCallback(new LoginAction(this));
+ AddComponent(cancelButton);
+ SetCancelButton(cancelButton);
+ cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ cancelButton->SetActionCallback(new CancelAction(this));
+ AddComponent(titleLabel);
+ titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+
+ AddComponent(usernameField);
+ usernameField->Appearance.icon = IconUsername;
+ usernameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; usernameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(passwordField);
+ passwordField->Appearance.icon = IconPassword;
+ passwordField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; passwordField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ passwordField->SetHidden(true);
+}
+
+void LoginView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ switch(key)
+ {
+ case KEY_TAB:
+ if(IsFocused(usernameField))
+ FocusComponent(passwordField);
+ else
+ FocusComponent(usernameField);
+ break;
+ }
+}
+
+void LoginView::NotifyStatusChanged(LoginModel * sender)
+{
+ if(!infoLabel->GetText().length() && sender->GetStatusText().length())
+ {
+ targetSize = Size+ui::Point(0, 18);
+ infoLabel->Visible = true;
+ }
+ infoLabel->SetText(sender->GetStatusText());
+ if(sender->GetStatus())
+ {
+ c->Exit();
+ }
+}
+
+void LoginView::OnTick(float dt)
+{
+ //if(targetSize != Size)
+ {
+ ui::Point difference = targetSize-Size;
+ if(difference.X!=0)
+ {
+ int xdiff = difference.X/5;
+ if(xdiff == 0)
+ xdiff = 1*isign(difference.X);
+ Size.X += xdiff;
+ }
+ if(difference.Y!=0)
+ {
+ int ydiff = difference.Y/5;
+ if(ydiff == 0)
+ ydiff = 1*isign(difference.Y);
+ Size.Y += ydiff;
+ }
+
+ loginButton->Position.Y = Size.Y-17;
+ cancelButton->Position.Y = Size.Y-17;
+ }
+}
+
+void LoginView::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
+}
+
+LoginView::~LoginView() {
+ RemoveComponent(titleLabel);
+ RemoveComponent(loginButton);
+ RemoveComponent(cancelButton);
+ RemoveComponent(usernameField);
+ RemoveComponent(passwordField);
+ RemoveComponent(infoLabel);
+ delete cancelButton;
+ delete loginButton;
+ delete titleLabel;
+ delete usernameField;
+ delete passwordField;
+ delete infoLabel;
+}
+
diff --git a/src/login/LoginView.h b/src/login/LoginView.h
new file mode 100644
index 0000000..fe87a3f
--- /dev/null
+++ b/src/login/LoginView.h
@@ -0,0 +1,45 @@
+/*
+ * LoginView.h
+ *
+ * Created on: Jan 24, 2012
+ * Author: Simon
+ */
+
+#ifndef LOGINVIEW_H_
+#define LOGINVIEW_H_
+
+#include "interface/Window.h"
+#include "LoginController.h"
+#include "LoginModel.h"
+
+namespace ui
+{
+ class Textbox;
+ class Button;
+ class Label;
+}
+
+class LoginController;
+class LoginMode;
+class LoginView: public ui::Window {
+ LoginController * c;
+ ui::Point targetSize;
+ ui::Button * loginButton;
+ ui::Button * cancelButton;
+ ui::Label * titleLabel;
+ ui::Label * infoLabel;
+ ui::Textbox * usernameField;
+ ui::Textbox * passwordField;
+public:
+ class LoginAction;
+ class CancelAction;
+ LoginView();
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ void AttachController(LoginController * c_) { c = c_; }
+ void NotifyStatusChanged(LoginModel * sender);
+ virtual void OnDraw();
+ virtual void OnTick(float dt);
+ virtual ~LoginView();
+};
+
+#endif /* LOGINVIEW_H_ */
diff --git a/src/options/OptionsController.cpp b/src/options/OptionsController.cpp
new file mode 100644
index 0000000..3973fed
--- /dev/null
+++ b/src/options/OptionsController.cpp
@@ -0,0 +1,113 @@
+/*
+ * OptionsController.cpp
+ *
+ * Created on: Apr 14, 2012
+ * Author: Simon
+ */
+
+#include "OptionsController.h"
+#include "dialogues/ErrorMessage.h"
+
+OptionsController::OptionsController(GameModel * gModel_, ControllerCallback * callback_):
+ callback(callback_),
+ gModel(gModel_),
+ HasExited(false)
+{
+ view = new OptionsView();
+ model = new OptionsModel(gModel);
+ model->AddObserver(view);
+
+ view->AttachController(this);
+
+}
+
+void OptionsController::SetHeatSimulation(bool state)
+{
+ model->SetHeatSimulation(state);
+}
+
+void OptionsController::SetAmbientHeatSimulation(bool state)
+{
+ model->SetAmbientHeatSimulation(state);
+}
+
+void OptionsController::SetNewtonianGravity(bool state)
+{
+ model->SetNewtonianGravity(state);
+}
+
+void OptionsController::SetWaterEqualisation(bool state)
+{
+ model->SetWaterEqualisation(state);
+}
+
+void OptionsController::SetGravityMode(int gravityMode)
+{
+ model->SetGravityMode(gravityMode);
+}
+
+void OptionsController::SetAirMode(int airMode)
+{
+ model->SetAirMode(airMode);
+}
+
+void OptionsController::SetEdgeMode(int airMode)
+{
+ model->SetEdgeMode(airMode);
+}
+
+void OptionsController::SetFullscreen(bool fullscreen)
+{
+ model->SetFullscreen(fullscreen);
+}
+
+void OptionsController::SetScale(bool scale)
+{
+ if(scale)
+ {
+ if(ui::Engine::Ref().GetMaxWidth() >= ui::Engine::Ref().GetWidth() * 2 && ui::Engine::Ref().GetMaxHeight() >= ui::Engine::Ref().GetHeight() * 2)
+ model->SetScale(scale);
+ else
+ {
+ new ErrorMessage("Screen resolution error", "Your screen size is too small to use this scale mode.");
+ model->SetScale(false);
+ }
+ }
+ else
+ model->SetScale(scale);
+
+}
+
+void OptionsController::SetFastQuit(bool fastquit)
+{
+ model->SetFastQuit(fastquit);
+}
+
+OptionsView * OptionsController::GetView()
+{
+ return view;
+}
+
+void OptionsController::Exit()
+{
+ if(ui::Engine::Ref().GetWindow() == view)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ if(callback)
+ callback->ControllerExit();
+ HasExited = true;
+}
+
+
+OptionsController::~OptionsController() {
+ if(ui::Engine::Ref().GetWindow() == view)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ delete model;
+ delete view;
+ if(callback)
+ delete callback;
+}
+
diff --git a/src/options/OptionsController.h b/src/options/OptionsController.h
new file mode 100644
index 0000000..1df92ba
--- /dev/null
+++ b/src/options/OptionsController.h
@@ -0,0 +1,42 @@
+/*
+ * OptionsController.h
+ *
+ * Created on: Apr 14, 2012
+ * Author: Simon
+ */
+
+#ifndef OPTIONSCONTROLLER_H_
+#define OPTIONSCONTROLLER_H_
+
+#include "Controller.h"
+#include "simulation/Simulation.h"
+#include "OptionsView.h"
+#include "OptionsModel.h"
+
+class GameModel;
+class OptionsModel;
+class OptionsView;
+class OptionsController {
+ GameModel * gModel;
+ OptionsView * view;
+ OptionsModel * model;
+ ControllerCallback * callback;
+public:
+ bool HasExited;
+ OptionsController(GameModel * gModel_, ControllerCallback * callback_);
+ void SetHeatSimulation(bool state);
+ void SetAmbientHeatSimulation(bool state);
+ void SetNewtonianGravity(bool state);
+ void SetWaterEqualisation(bool state);
+ void SetGravityMode(int gravityMode);
+ void SetAirMode(int airMode);
+ void SetEdgeMode(int airMode);
+ void SetFullscreen(bool fullscreen);
+ void SetScale(bool scale);
+ void SetFastQuit(bool fastquit);
+ void Exit();
+ OptionsView * GetView();
+ virtual ~OptionsController();
+};
+
+#endif /* OPTIONSCONTROLLER_H_ */
diff --git a/src/options/OptionsModel.cpp b/src/options/OptionsModel.cpp
new file mode 100644
index 0000000..c3517fa
--- /dev/null
+++ b/src/options/OptionsModel.cpp
@@ -0,0 +1,145 @@
+/*
+ * OptionsModel.cpp
+ *
+ * Created on: Apr 14, 2012
+ * Author: Simon
+ */
+
+#include "simulation/Air.h"
+#include "game/GameModel.h"
+#include "OptionsModel.h"
+
+OptionsModel::OptionsModel(GameModel * gModel_) {
+ gModel = gModel_;
+ sim = gModel->GetSimulation();
+}
+
+void OptionsModel::AddObserver(OptionsView* view)
+{
+ observers.push_back(view);
+ view->NotifySettingsChanged(this);
+}
+
+bool OptionsModel::GetHeatSimulation()
+{
+ return sim->legacy_enable?false:true;
+}
+
+void OptionsModel::SetHeatSimulation(bool state)
+{
+ sim->legacy_enable = state?0:1;
+ notifySettingsChanged();
+}
+
+bool OptionsModel::GetAmbientHeatSimulation()
+{
+ return sim->aheat_enable?true:false;
+}
+
+void OptionsModel::SetAmbientHeatSimulation(bool state)
+{
+ sim->aheat_enable = state?1:0;
+ notifySettingsChanged();
+}
+
+bool OptionsModel::GetNewtonianGravity()
+{
+ return sim->grav->ngrav_enable?true:false;
+}
+
+void OptionsModel::SetNewtonianGravity(bool state)
+{
+ if(state)
+ sim->grav->start_grav_async();
+ else
+ sim->grav->stop_grav_async();
+ notifySettingsChanged();
+}
+
+bool OptionsModel::GetWaterEqualisation()
+{
+ return sim->water_equal_test?true:false;
+}
+
+void OptionsModel::SetWaterEqualisation(bool state)
+{
+ sim->water_equal_test = state?1:0;
+ notifySettingsChanged();
+}
+
+int OptionsModel::GetAirMode()
+{
+ return sim->air->airMode;
+}
+void OptionsModel::SetAirMode(int airMode)
+{
+ sim->air->airMode = airMode;
+ notifySettingsChanged();
+}
+
+int OptionsModel::GetEdgeMode()
+{
+ return gModel->GetEdgeMode();
+}
+void OptionsModel::SetEdgeMode(int edgeMode)
+{
+ gModel->SetEdgeMode(edgeMode);
+ notifySettingsChanged();
+}
+
+int OptionsModel::GetGravityMode()
+{
+ return sim->gravityMode;
+}
+void OptionsModel::SetGravityMode(int gravityMode)
+{
+ sim->gravityMode = gravityMode;
+ notifySettingsChanged();
+}
+
+bool OptionsModel::GetScale()
+{
+ return ui::Engine::Ref().GetScale()==2;
+}
+void OptionsModel::SetScale(bool doubleScale)
+{
+ ui::Engine::Ref().SetScale(doubleScale?2:1);
+ Client::Ref().SetPref("Scale", int(doubleScale?2:1));
+ notifySettingsChanged();
+}
+
+
+bool OptionsModel::GetFullscreen()
+{
+ return ui::Engine::Ref().GetFullscreen();
+}
+void OptionsModel::SetFullscreen(bool fullscreen)
+{
+ ui::Engine::Ref().SetFullscreen(fullscreen);
+ Client::Ref().SetPref("Fullscreen", bool(fullscreen));
+ notifySettingsChanged();
+}
+
+bool OptionsModel::GetFastQuit()
+{
+ return ui::Engine::Ref().GetFastQuit();
+}
+void OptionsModel::SetFastQuit(bool fastquit)
+{
+ ui::Engine::Ref().SetFastQuit(fastquit);
+ Client::Ref().SetPref("FastQuit", bool(fastquit));
+ notifySettingsChanged();
+}
+
+void OptionsModel::notifySettingsChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifySettingsChanged(this);
+ }
+}
+
+OptionsModel::~OptionsModel() {
+ // TODO Auto-generated destructor stub
+}
+
diff --git a/src/options/OptionsModel.h b/src/options/OptionsModel.h
new file mode 100644
index 0000000..c15fb88
--- /dev/null
+++ b/src/options/OptionsModel.h
@@ -0,0 +1,48 @@
+/*
+ * OptionsModel.h
+ *
+ * Created on: Apr 14, 2012
+ * Author: Simon
+ */
+
+#ifndef OPTIONSMODEL_H_
+#define OPTIONSMODEL_H_
+#include <vector>
+#include "OptionsView.h"
+#include "simulation/Simulation.h"
+
+class GameModel;
+class Simulation;
+class OptionsView;
+class OptionsModel {
+ GameModel * gModel;
+ Simulation * sim;
+ std::vector<OptionsView*> observers;
+ void notifySettingsChanged();
+public:
+ OptionsModel(GameModel * gModel);
+ void AddObserver(OptionsView* view);
+ bool GetHeatSimulation();
+ void SetHeatSimulation(bool state);
+ bool GetAmbientHeatSimulation();
+ void SetAmbientHeatSimulation(bool state);
+ bool GetNewtonianGravity();
+ void SetNewtonianGravity(bool state);
+ bool GetWaterEqualisation();
+ void SetWaterEqualisation(bool state);
+ int GetAirMode();
+ void SetAirMode(int airMode);
+ int GetEdgeMode();
+ void SetEdgeMode(int edgeMode);
+ int GetGravityMode();
+ void SetGravityMode(int gravityMode);
+ bool GetFullscreen();
+ void SetFullscreen(bool fullscreen);
+ bool GetFastQuit();
+ void SetFastQuit(bool fastquit);
+ bool GetScale();
+ void SetScale(bool scale);
+ virtual ~OptionsModel();
+};
+
+#endif /* OPTIONSMODEL_H_ */
diff --git a/src/options/OptionsView.cpp b/src/options/OptionsView.cpp
new file mode 100644
index 0000000..3adf7d7
--- /dev/null
+++ b/src/options/OptionsView.cpp
@@ -0,0 +1,240 @@
+/*
+ * OptionsView.cpp
+ *
+ * Created on: Apr 14, 2012
+ * Author: Simon
+ */
+
+#include "OptionsView.h"
+#include "Style.h"
+#include "interface/Button.h"
+#include "interface/Label.h"
+#include "interface/DropDown.h"
+
+OptionsView::OptionsView():
+ ui::Window(ui::Point(-1, -1), ui::Point(300, 290)){
+
+ ui::Label * tempLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 14), "Simulation Options");
+ tempLabel->SetTextColour(style::Colour::InformationTitle);
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+
+ class HeatSimulationAction: public ui::CheckboxAction
+ {
+ OptionsView * v;
+ public:
+ HeatSimulationAction(OptionsView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetHeatSimulation(sender->GetChecked()); }
+ };
+
+ heatSimulation = new ui::Checkbox(ui::Point(8, 23), ui::Point(Size.X-6, 16), "Heat simulation \bgIntroduced in version 34", "");
+ heatSimulation->SetActionCallback(new HeatSimulationAction(this));
+ AddComponent(heatSimulation);
+ tempLabel = new ui::Label(ui::Point(24, heatSimulation->Position.Y+14), ui::Point(Size.X-28, 16), "\bgCan cause odd behaviour with very old saves");
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+
+ class AmbientHeatSimulationAction: public ui::CheckboxAction
+ {
+ OptionsView * v;
+ public:
+ AmbientHeatSimulationAction(OptionsView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetAmbientHeatSimulation(sender->GetChecked()); }
+ };
+
+ ambientHeatSimulation = new ui::Checkbox(ui::Point(8, 53), ui::Point(Size.X-6, 16), "Ambient heat simulation \bgIntroduced in version 50", "");
+ ambientHeatSimulation->SetActionCallback(new AmbientHeatSimulationAction(this));
+ AddComponent(ambientHeatSimulation);
+ tempLabel = new ui::Label(ui::Point(24, ambientHeatSimulation->Position.Y+14), ui::Point(Size.X-28, 16), "\bgCan cause odd behaviour with old saves");
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+
+ class NewtonianGravityAction: public ui::CheckboxAction
+ {
+ OptionsView * v;
+ public:
+ NewtonianGravityAction(OptionsView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetNewtonianGravity(sender->GetChecked()); }
+ };
+
+ newtonianGravity = new ui::Checkbox(ui::Point(8, 83), ui::Point(Size.X-6, 16), "Newtonian gravity \bgIntroduced in version 48", "");
+ newtonianGravity->SetActionCallback(new NewtonianGravityAction(this));
+ AddComponent(newtonianGravity);
+ tempLabel = new ui::Label(ui::Point(24, newtonianGravity->Position.Y+14), ui::Point(Size.X-28, 16), "\bgMay cause poor performance on older computers");
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+
+ class WaterEqualisationAction: public ui::CheckboxAction
+ {
+ OptionsView * v;
+ public:
+ WaterEqualisationAction(OptionsView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetWaterEqualisation(sender->GetChecked()); }
+ };
+
+ waterEqualisation = new ui::Checkbox(ui::Point(8, 113), ui::Point(Size.X-6, 16), "Water equalisation \bgIntroduced in version 61", "");
+ waterEqualisation->SetActionCallback(new WaterEqualisationAction(this));
+ AddComponent(waterEqualisation);
+ tempLabel = new ui::Label(ui::Point(24, waterEqualisation->Position.Y+14), ui::Point(Size.X-28, 16), "\bgMay cause poor performance with a lot of water");
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+
+ class AirModeChanged: public ui::DropDownAction
+ {
+ OptionsView * v;
+ public:
+ AirModeChanged(OptionsView * v): v(v) { }
+ virtual void OptionChanged(ui::DropDown * sender, std::pair<std::string, int> option) { v->c->SetAirMode(option.second); }
+ };
+ airMode = new ui::DropDown(ui::Point(Size.X-88, 146), ui::Point(80, 16));
+ AddComponent(airMode);
+ airMode->AddOption(std::pair<std::string, int>("On", 0));
+ airMode->AddOption(std::pair<std::string, int>("Pressure off", 1));
+ airMode->AddOption(std::pair<std::string, int>("Velocity off", 2));
+ airMode->AddOption(std::pair<std::string, int>("Off", 3));
+ airMode->AddOption(std::pair<std::string, int>("No Update", 4));
+ airMode->SetActionCallback(new AirModeChanged(this));
+
+ tempLabel = new ui::Label(ui::Point(8, 146), ui::Point(Size.X-96, 16), "Air Simulation Mode");
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+
+ class GravityModeChanged: public ui::DropDownAction
+ {
+ OptionsView * v;
+ public:
+ GravityModeChanged(OptionsView * v): v(v) { }
+ virtual void OptionChanged(ui::DropDown * sender, std::pair<std::string, int> option) { v->c->SetGravityMode(option.second); }
+ };
+
+ gravityMode = new ui::DropDown(ui::Point(Size.X-88, 166), ui::Point(80, 16));
+ AddComponent(gravityMode);
+ gravityMode->AddOption(std::pair<std::string, int>("Vertical", 0));
+ gravityMode->AddOption(std::pair<std::string, int>("Off", 1));
+ gravityMode->AddOption(std::pair<std::string, int>("Radial", 2));
+ gravityMode->SetActionCallback(new GravityModeChanged(this));
+
+ tempLabel = new ui::Label(ui::Point(8, 166), ui::Point(Size.X-96, 16), "Gravity Simulation Mode");
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+
+ class EdgeModeChanged: public ui::DropDownAction
+ {
+ OptionsView * v;
+ public:
+ EdgeModeChanged(OptionsView * v): v(v) { }
+ virtual void OptionChanged(ui::DropDown * sender, std::pair<std::string, int> option) { v->c->SetEdgeMode(option.second); }
+ };
+
+ edgeMode = new ui::DropDown(ui::Point(Size.X-88, 186), ui::Point(80, 16));
+ AddComponent(edgeMode);
+ edgeMode->AddOption(std::pair<std::string, int>("Void", 0));
+ edgeMode->AddOption(std::pair<std::string, int>("Solid", 1));
+ edgeMode->SetActionCallback(new EdgeModeChanged(this));
+
+ tempLabel = new ui::Label(ui::Point(8, 186), ui::Point(Size.X-96, 16), "Edge Mode");
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+
+ class ScaleAction: public ui::CheckboxAction
+ {
+ OptionsView * v;
+ public:
+ ScaleAction(OptionsView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetScale(sender->GetChecked()); }
+ };
+
+ scale = new ui::Checkbox(ui::Point(8, 210), ui::Point(Size.X-6, 16), "Large screen", "");
+ scale->SetActionCallback(new ScaleAction(this));
+ tempLabel = new ui::Label(ui::Point(scale->Position.X+Graphics::textwidth(scale->GetText().c_str())+20, scale->Position.Y), ui::Point(Size.X-28, 16), "\bg- Double window size for smaller screen");
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+ AddComponent(scale);
+
+
+ class FullscreenAction: public ui::CheckboxAction
+ {
+ OptionsView * v;
+ public:
+ FullscreenAction(OptionsView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetFullscreen(sender->GetChecked()); }
+ };
+
+ fullscreen = new ui::Checkbox(ui::Point(8, 230), ui::Point(Size.X-6, 16), "Fullscreen", "");
+ fullscreen->SetActionCallback(new FullscreenAction(this));
+ tempLabel = new ui::Label(ui::Point(fullscreen->Position.X+Graphics::textwidth(fullscreen->GetText().c_str())+20, fullscreen->Position.Y), ui::Point(Size.X-28, 16), "\bg- Use the entire screen");
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+ AddComponent(fullscreen);
+
+
+ class FastQuitAction: public ui::CheckboxAction
+ {
+ OptionsView * v;
+ public:
+ FastQuitAction(OptionsView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetFastQuit(sender->GetChecked()); }
+ };
+
+ fastquit = new ui::Checkbox(ui::Point(8, 250), ui::Point(Size.X-6, 16), "Fast Quit", "");
+ fastquit->SetActionCallback(new FastQuitAction(this));
+ tempLabel = new ui::Label(ui::Point(fastquit->Position.X+Graphics::textwidth(fastquit->GetText().c_str())+20, fastquit->Position.Y), ui::Point(Size.X-28, 16), "\bg- Always exit completely when hitting close");
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tempLabel);
+ AddComponent(fastquit);
+
+ class CloseAction: public ui::ButtonAction
+ {
+ public:
+ OptionsView * v;
+ CloseAction(OptionsView * v_) { v = v_; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->Exit();
+ }
+ };
+
+ ui::Button * tempButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "OK");
+ tempButton->SetActionCallback(new CloseAction(this));
+ AddComponent(tempButton);
+ SetCancelButton(tempButton);
+ SetOkayButton(tempButton);
+}
+
+void OptionsView::NotifySettingsChanged(OptionsModel * sender)
+{
+ heatSimulation->SetChecked(sender->GetHeatSimulation());
+ ambientHeatSimulation->SetChecked(sender->GetAmbientHeatSimulation());
+ newtonianGravity->SetChecked(sender->GetNewtonianGravity());
+ waterEqualisation->SetChecked(sender->GetWaterEqualisation());
+ airMode->SetOption(sender->GetAirMode());
+ gravityMode->SetOption(sender->GetGravityMode());
+ edgeMode->SetOption(sender->GetEdgeMode());
+ scale->SetChecked(sender->GetScale());
+ fullscreen->SetChecked(sender->GetFullscreen());
+ fastquit->SetChecked(sender->GetFastQuit());
+}
+
+void OptionsView::AttachController(OptionsController * c_)
+{
+ c = c_;
+}
+
+void OptionsView::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
+ g->draw_line(Position.X+1, Position.Y+scale->Position.Y-4, Position.X+Size.X-1, Position.Y+scale->Position.Y-4, 255, 255, 255, 180);
+}
+
+void OptionsView::OnTryExit(ExitMethod method)
+{
+ c->Exit();
+}
+
+
+OptionsView::~OptionsView() {
+ // TODO Auto-generated destructor stub
+}
+
diff --git a/src/options/OptionsView.h b/src/options/OptionsView.h
new file mode 100644
index 0000000..a4c9854
--- /dev/null
+++ b/src/options/OptionsView.h
@@ -0,0 +1,40 @@
+/*
+ * OptionsView.h
+ *
+ * Created on: Apr 14, 2012
+ * Author: Simon
+ */
+
+#ifndef OPTIONSVIEW_H_
+#define OPTIONSVIEW_H_
+
+#include "interface/Window.h"
+#include "OptionsController.h"
+#include "interface/Checkbox.h"
+#include "interface/DropDown.h"
+#include "OptionsModel.h"
+
+class OptionsModel;
+class OptionsController;
+class OptionsView: public ui::Window {
+ OptionsController * c;
+ ui::Checkbox * heatSimulation;
+ ui::Checkbox * ambientHeatSimulation;
+ ui::Checkbox * newtonianGravity;
+ ui::Checkbox * waterEqualisation;
+ ui::DropDown * airMode;
+ ui::DropDown * gravityMode;
+ ui::DropDown * edgeMode;
+ ui::Checkbox * scale;
+ ui::Checkbox * fullscreen;
+ ui::Checkbox * fastquit;
+public:
+ OptionsView();
+ void NotifySettingsChanged(OptionsModel * sender);
+ void AttachController(OptionsController * c_);
+ void OnDraw();
+ void OnTryExit(ExitMethod method);
+ virtual ~OptionsView();
+};
+
+#endif /* OPTIONSVIEW_H_ */
diff --git a/src/pim/Generator.cpp b/src/pim/Generator.cpp
new file mode 100644
index 0000000..a791211
--- /dev/null
+++ b/src/pim/Generator.cpp
@@ -0,0 +1,518 @@
+//Code generator for bytecode
+#include <sstream>
+#include <fstream>
+#include "Format.h"
+#include "Generator.h"
+#include "Opcodes.h"
+namespace pim
+{
+ namespace compiler
+ {
+ Generator::Generator() :
+ output(std::cout),
+ labelCounter(0),
+ programCounter(0)
+ {
+
+ }
+
+ void Generator::defineLabel(std::string label)
+ {
+ Label newLabel;
+ newLabel.Name = label;
+ newLabel.Position = programCounter;//program.size();
+ labelPositions.push_back(newLabel);
+ }
+
+ void Generator::writeOpcode(int opcode)
+ {
+ programCounter++;
+ program.push_back(opcode);
+ }
+
+ void Generator::writeConstant(std::string constant)
+ {
+ writeConstant(format::StringToNumber<int>(constant));
+ }
+
+ void Generator::writeConstant(int constant)
+ {
+ program.push_back(constant & 0xFF);
+ program.push_back((constant>>8) & 0xFF);
+ program.push_back((constant>>16) & 0xFF);
+ program.push_back((constant>>24) & 0xFF);
+ }
+
+ void Generator::writeConstantPlaceholder(std::string label)
+ {
+ placeholders.push_back(Placeholder(program.size(), label));
+ program.push_back(0);
+ program.push_back(0);
+ program.push_back(0);
+ program.push_back(0);
+ }
+
+ void Generator::writeConstantPlaceholder(int * value)
+ {
+ valuePlaceholders.push_back(ValuePlaceholder(program.size(), value));
+ program.push_back(0);
+ program.push_back(0);
+ program.push_back(0);
+ program.push_back(0);
+ }
+
+ void Generator::writeConstantPropertyPlaceholder(std::string property)
+ {
+ propertyPlaceholders.push_back(PropertyPlaceholder(program.size(), property));
+ program.push_back(0);
+ program.push_back(0);
+ program.push_back(0);
+ program.push_back(0);
+ }
+
+ void Generator::writeConstantMacroPlaceholder(std::string macro)
+ {
+ macroPlaceholders.push_back(MacroPlaceholder(program.size(), macro));
+ program.push_back(0);
+ program.push_back(0);
+ program.push_back(0);
+ program.push_back(0);
+ }
+
+ std::vector<unsigned char> Generator::Finish()
+ {
+ //All compile time labels, macros, etc
+ for(std::vector<Placeholder>::iterator iter = placeholders.begin(), end = placeholders.end(); iter != end; ++iter)
+ {
+ bool found = false;
+ Placeholder cPosition = *iter;
+ for(std::vector<Label>::iterator iter2 = labelPositions.begin(), end2 = labelPositions.end(); iter2 != end2; ++iter2)
+ {
+ Label cLabel = *iter2;
+ if(cPosition.second == cLabel.Name)
+ {
+ std::cout << "Setting placeholder at " << cPosition.first << " with " << cLabel.Position << " for" << cPosition.second << std::endl;
+ found = true;
+ program[cPosition.first] = cLabel.Position & 0xFF;
+ program[cPosition.first+1] = (cLabel.Position >> 8) & 0xFF;
+ program[cPosition.first+2] = (cLabel.Position >> 16) & 0xFF;
+ program[cPosition.first+3] = (cLabel.Position >> 24) & 0xFF;
+ break;
+ }
+ }
+ if(!found)
+ throw SymbolNotFoundException(cPosition.second);
+ }
+
+ for(std::vector<ValuePlaceholder>::iterator iter = valuePlaceholders.begin(), end = valuePlaceholders.end(); iter != end; ++iter)
+ {
+ ValuePlaceholder cPosition = *iter;
+ int value = *cPosition.second;
+
+ std::cout << "Setting value placeholder at " << cPosition.first << " with " << value << std::endl;
+
+
+ program[cPosition.first] = value & 0xFF;
+ program[cPosition.first+1] = (value >> 8) & 0xFF;
+ program[cPosition.first+2] = (value >> 16) & 0xFF;
+ program[cPosition.first+3] = (value >> 24) & 0xFF;
+ }
+
+ //Build file
+ int macroSizePos, propSizePos, codeSizePos, macroSize = 0, propSize = 0, codeSize = program.size();
+ std::vector<unsigned char> file;
+ file.push_back('P');
+ file.push_back('V');
+ file.push_back('M');
+ file.push_back('1');
+
+
+ macroSizePos = file.size();
+ file.push_back(0);
+ file.push_back(0);
+ file.push_back(0);
+ file.push_back(0);
+
+ propSizePos = file.size();
+ file.push_back(0);
+ file.push_back(0);
+ file.push_back(0);
+ file.push_back(0);
+
+ codeSizePos = file.size();
+ file.push_back(0);
+ file.push_back(0);
+ file.push_back(0);
+ file.push_back(0);
+
+ //Macros
+ for(std::vector<MacroPlaceholder>::iterator iter = macroPlaceholders.begin(), end = macroPlaceholders.end(); iter != end; ++iter)
+ {
+ MacroPlaceholder cPosition = *iter;
+ int position = cPosition.first;
+
+ file.push_back(position & 0xFF);
+ file.push_back((position >> 8) & 0xFF);
+ file.push_back((position >> 16) & 0xFF);
+ file.push_back((position >> 24) & 0xFF);
+ macroSize += 4;
+
+ file.push_back(cPosition.second.length());
+ macroSize += 1;
+ file.insert(file.end(), cPosition.second.begin(), cPosition.second.end());
+ macroSize += cPosition.second.length();
+ }
+
+ file[macroSizePos] = macroSize & 0xFF;
+ file[macroSizePos+1] = (macroSize >> 8) & 0xFF;
+ file[macroSizePos+2] = (macroSize >> 16) & 0xFF;
+ file[macroSizePos+3] = (macroSize >> 24) & 0xFF;
+
+
+ //Macros
+ for(std::vector<PropertyPlaceholder>::iterator iter = propertyPlaceholders.begin(), end = propertyPlaceholders.end(); iter != end; ++iter)
+ {
+ PropertyPlaceholder cPosition = *iter;
+ int position = cPosition.first;
+
+ file.push_back(position & 0xFF);
+ file.push_back((position >> 8) & 0xFF);
+ file.push_back((position >> 16) & 0xFF);
+ file.push_back((position >> 24) & 0xFF);
+ propSize += 4;
+
+ file.push_back(cPosition.second.length());
+ propSize += 1;
+ file.insert(file.end(), cPosition.second.begin(), cPosition.second.end());
+ propSize += cPosition.second.length();
+ }
+
+ file[propSizePos] = propSize & 0xFF;
+ file[propSizePos+1] = (propSize >> 8) & 0xFF;
+ file[propSizePos+2] = (propSize >> 16) & 0xFF;
+ file[propSizePos+3] = (propSize >> 24) & 0xFF;
+
+ file.insert(file.end(), program.begin(), program.end());
+
+ file[codeSizePos] = codeSize & 0xFF;
+ file[codeSizePos+1] = (codeSize >> 8) & 0xFF;
+ file[codeSizePos+2] = (codeSize >> 16) & 0xFF;
+ file[codeSizePos+3] = (codeSize >> 24) & 0xFF;
+
+ std::ofstream newFile("test.pvm");
+ for(std::vector<unsigned char>::iterator iter = file.begin(), end = file.end(); iter != end; ++iter)
+ {
+ newFile.put(*iter);
+ }
+ newFile.close();
+
+ return file;
+ }
+
+ std::string Generator::UniqueLabel(std::string prefix)
+ {
+ std::stringstream label;
+ label << prefix;
+ label << "_";
+ label << labelCounter;
+ label << "_";
+ return label.str();
+ }
+
+ void Generator::PushScope(std::string label)
+ {
+ scopes.push(currentScope);
+ Scope * prevScope = currentScope;
+ currentScope = new Scope();
+ defineLabel(label);
+
+ output << "." << label << std::endl;
+ }
+
+ void Generator::PushLocalScope(std::string label)
+ {
+ scopes.push(currentScope);
+ Scope * prevScope = currentScope;
+ currentScope = new Scope();
+ currentScope->Definitions.insert(currentScope->Definitions.begin(), prevScope->Definitions.begin(), prevScope->Definitions.end());
+ currentScope->FrameSize = prevScope->FrameSize;
+ defineLabel(label);
+
+ output << "." << label << std::endl;
+ }
+
+ void Generator::PopScope()
+ {
+
+ writeOpcode(Opcode::Return);
+ writeConstant(currentScope->LocalFrameSize);
+
+ output << "return " << currentScope->LocalFrameSize << std::endl;
+
+ currentScope = scopes.top();
+ scopes.pop();
+ }
+
+ void Generator::ScopeLabel(std::string label)
+ {
+ //defineLabelwriteOpcode("." << label);
+ defineLabel(label);
+
+ output << "." << label << std::endl;
+ }
+
+ void Generator::LocalEnter()
+ {
+ writeOpcode(Opcode::LocalEnter);
+ writeConstantPlaceholder(&(currentScope->LocalFrameSize));
+
+ output << "enter " << "#" << std::endl;
+ }
+
+ void Generator::ScopeVariableType(int type)
+ {
+ variableType = type;
+ }
+
+ void Generator::ScopeVariable(std::string label)
+ {
+ currentScope->Definitions.push_back(Definition(label, variableType, currentScope->FrameSize));
+ currentScope->FrameSize += 4;
+ currentScope->LocalFrameSize += 4;
+
+ output << "#declare " << label << " " << currentScope->FrameSize-4 << std::endl;
+ }
+
+ void Generator::PushVariableAddress(std::string label)
+ {
+ //writeOpcode("address"); << " " << currentScope->GetDefinition(label).StackPosition
+ }
+
+ void Generator::LoadVariable(std::string label)
+ {
+ writeOpcode(Opcode::Load);
+ writeConstant(currentScope->GetDefinition(label).StackPosition);
+
+ output << "load " << label << std::endl;
+ }
+
+ void Generator::StoreVariable(std::string label)
+ {
+ writeOpcode(Opcode::Store);
+ writeConstant(currentScope->GetDefinition(label).StackPosition);
+
+ output << "store " << label << std::endl;
+ }
+
+ void Generator::RTConstant(std::string name)
+ {
+ writeOpcode(Opcode::Constant);
+ writeConstantMacroPlaceholder(name);
+
+ output << "const " << name << std::endl;
+ }
+
+ void Generator::Constant(std::string constant)
+ {
+ writeOpcode(Opcode::Constant);
+ writeConstant(constant);
+
+ output << "const " << constant << std::endl;
+
+ }
+
+ void Generator::Increment(std::string constant)
+ {
+ writeOpcode(Opcode::Increment);
+ writeConstant(constant);
+
+ output << "inc " << constant << std::endl;
+ }
+
+ void Generator::Discard()
+ {
+ writeOpcode(Opcode::Discard);
+
+ output << "discard" << std::endl;
+ }
+
+ void Generator::Duplicate()
+ {
+ writeOpcode(Opcode::Duplicate);
+
+ output << "duplicate" << std::endl;
+ }
+
+ void Generator::Add()
+ {
+ writeOpcode(Opcode::Add);
+
+ output << "add" << std::endl;
+ }
+
+ void Generator::Subtract()
+ {
+ writeOpcode(Opcode::Subtract);
+
+ output << "sub" << std::endl;
+ }
+
+ void Generator::Multiply()
+ {
+ writeOpcode(Opcode::Multiply);
+
+ output << "mul" << std::endl;
+ }
+
+ void Generator::Divide()
+ {
+ writeOpcode(Opcode::Divide);
+
+ output << "div" << std::endl;
+ }
+
+ void Generator::Modulus()
+ {
+ writeOpcode(Opcode::Modulus);
+
+ output << "add" << std::endl;
+ }
+
+ void Generator::Negate()
+ {
+ writeOpcode(Opcode::Negate);
+
+ output << "neg" << std::endl;
+ }
+
+ void Generator::CreateParticle()
+ {
+ writeOpcode(Opcode::Create);
+
+ output << "create" << std::endl;
+ }
+
+ void Generator::TransformParticle()
+ {
+ writeOpcode(Opcode::Transform);
+
+ output << "transform" << std::endl;
+ }
+
+ void Generator::GetParticle()
+ {
+ writeOpcode(Opcode::Get);
+
+ output << "getpart" << std::endl;
+ }
+
+ void Generator::GetPosition()
+ {
+ writeOpcode(Opcode::Position);
+
+ output << "getpos" << std::endl;
+ }
+
+ void Generator::KillParticle()
+ {
+ writeOpcode(Opcode::Kill);
+
+ output << "kill" << std::endl;
+ }
+
+ void Generator::LoadProperty(std::string property)
+ {
+ writeOpcode(Opcode::LoadProperty);
+ writeConstantPropertyPlaceholder(property);
+
+ output << "loadprop " << property << std::endl;
+ }
+
+ void Generator::StoreProperty(std::string property)
+ {
+ writeOpcode(Opcode::StoreProperty);
+ writeConstantPropertyPlaceholder(property);
+
+ output << "storeprop " << property << std::endl;
+ }
+
+ void Generator::IntegerToDecimal()
+ {
+
+ }
+
+ void Generator::DecimalToInteger()
+ {
+
+ }
+
+
+ void Generator::JumpEqual(std::string label)
+ {
+ writeOpcode(Opcode::JumpEqual);
+ writeConstantPlaceholder(label);
+
+ output << "jumpe " << label << std::endl;
+ }
+
+ void Generator::JumpNotEqual(std::string label)
+ {
+ writeOpcode(Opcode::JumpNotEqual);
+ writeConstantPlaceholder(label);
+
+ output << "jumpne " << label << std::endl;
+ }
+
+ void Generator::JumpGreater(std::string label)
+ {
+ writeOpcode(Opcode::JumpGreater);
+ writeConstantPlaceholder(label);
+
+ output << "jumpg " << label << std::endl;
+ }
+
+ void Generator::JumpGreaterEqual(std::string label)
+ {
+ writeOpcode(Opcode::JumpGreaterEqual);
+ writeConstantPlaceholder(label);
+
+ output << "jumpge " << label << std::endl;
+ }
+
+ void Generator::JumpLess(std::string label)
+ {
+ writeOpcode(Opcode::JumpLess);
+ writeConstantPlaceholder(label);
+
+ output << "jumpl " << label << std::endl;
+ }
+
+ void Generator::JumpLessEqual(std::string label)
+ {
+ writeOpcode(Opcode::JumpLessEqual);
+ writeConstantPlaceholder(label);
+
+ output << "jumple " << label << std::endl;
+ }
+
+ void Generator::Jump(std::string label)
+ {
+ writeOpcode(Opcode::Jump);
+ writeConstantPlaceholder(label);
+
+ output << "jump " << label << std::endl;
+ }
+
+
+ void Generator::Call(int arguments, std::string label)
+ {
+
+ }
+
+ void Generator::Return()
+ {
+
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/pim/Generator.h b/src/pim/Generator.h
new file mode 100644
index 0000000..2eb1029
--- /dev/null
+++ b/src/pim/Generator.h
@@ -0,0 +1,178 @@
+#pragma once
+
+#include <cstring>
+#include <vector>
+#include <stack>
+#include <iostream>
+#include "Token.h"
+namespace pim
+{
+ namespace compiler
+ {
+ class VariableNotFoundException: public std::exception
+ {
+ char * error;
+ public:
+ VariableNotFoundException(std::string variable) {
+ error = strdup(std::string("Could not find the variable \""+variable+"\" in the current scope").c_str());
+ }
+ const char * what() const throw()
+ {
+ return error;
+ }
+ ~VariableNotFoundException() throw() {};
+ };
+
+ class SymbolNotFoundException: public std::exception
+ {
+ char * error;
+ public:
+ SymbolNotFoundException(std::string variable) {
+ error = strdup(std::string("Could not find the symbol \""+variable+"\".").c_str());
+ }
+ const char * what() const throw()
+ {
+ return error;
+ }
+ ~SymbolNotFoundException() throw() {};
+ };
+ class Type
+ {
+ enum { Integer = Token::IntegerSymbol, Decimal = Token::DecimalSymbol };
+ };
+ class Definition
+ {
+ public:
+ std::string Name;
+ int Type;
+ int StackPosition;
+ Definition(std::string name, int type, int position) :
+ Type(type),
+ Name(name),
+ StackPosition(position)
+ {
+
+ }
+ };
+
+ struct Label
+ {
+ std::string Name;
+ int Position;
+ };
+
+ class Scope
+ {
+ public:
+ std::vector<Definition> Definitions;
+ std::vector<Label> Labels;
+ int FrameSize;
+ int LocalFrameSize;
+ Scope():
+ FrameSize(0),
+ LocalFrameSize(0)
+ {
+
+ }
+ Definition GetDefinition(std::string name)
+ {
+ for(std::vector<Definition>::iterator iter = Definitions.begin(), end = Definitions.end(); iter != end; ++iter)
+ {
+ if((*iter).Name == name)
+ return *iter;
+ }
+ throw VariableNotFoundException(name);
+ }
+ };
+
+ class Generator
+ {
+ int variableType;
+ std::stack<Scope*> scopes;
+ Scope * currentScope;
+ std::ostream & output;
+ int labelCounter;
+ int programCounter;
+
+ typedef std::pair<int, std::string> Placeholder;
+ std::vector<Placeholder> placeholders;
+
+ typedef std::pair<int, int*> ValuePlaceholder;
+ std::vector<ValuePlaceholder> valuePlaceholders;
+
+ typedef std::pair<int, std::string> PropertyPlaceholder;
+ std::vector<PropertyPlaceholder> propertyPlaceholders;
+
+ typedef std::pair<int, std::string> MacroPlaceholder;
+ std::vector<MacroPlaceholder> macroPlaceholders;
+
+ std::vector<Label> labelPositions;
+
+ std::vector<unsigned char> program;
+
+ void defineLabel(std::string label);
+ void writeOpcode(int opcode);
+ void writeConstant(std::string constant);
+ void writeConstant(int constant);
+ void writeConstantPlaceholder(std::string label);
+ void writeConstantPlaceholder(int * value);
+ void writeConstantMacroPlaceholder(std::string macro);
+ void writeConstantPropertyPlaceholder(std::string property);
+
+ public:
+ Generator();
+
+ std::vector<unsigned char> Finish();
+
+ std::string UniqueLabel(std::string prefix);
+
+ void PushScope(std::string label);
+ void PushLocalScope(std::string label);
+ void LocalEnter();
+ void PopScope();
+
+ void ScopeLabel(std::string label);
+ void ScopeVariableType(int type);
+ void ScopeVariable(std::string label);
+
+ void PushVariableAddress(std::string label);
+// void Store();
+ void LoadVariable(std::string label);
+ void StoreVariable(std::string label);
+
+ void Duplicate();
+ void Discard();
+ void RTConstant(std::string name);
+ void Constant(std::string constant);
+ void Increment(std::string constant);
+ void Add();
+ void Subtract();
+ void Multiply();
+ void Divide();
+ void Modulus();
+ void Negate();
+
+ void TransformParticle();
+ void CreateParticle();
+ void GetParticle();
+ void GetPosition();
+ void KillParticle();
+ void LoadProperty(std::string property);
+ void StoreProperty(std::string property);
+
+ void IntegerToDecimal();
+ void DecimalToInteger();
+
+ void JumpEqual(std::string label);
+ void JumpNotEqual(std::string label);
+ void JumpGreater(std::string label);
+ void JumpGreaterEqual(std::string label);
+ void JumpLess(std::string label);
+ void JumpLessEqual(std::string label);
+ void Jump(std::string label);
+
+ void Call(int arguments, std::string label);
+ void Return();
+ };
+ }
+} \ No newline at end of file
diff --git a/src/pim/Machine.cpp b/src/pim/Machine.cpp
new file mode 100644
index 0000000..a954d31
--- /dev/null
+++ b/src/pim/Machine.cpp
@@ -0,0 +1,637 @@
+//Virtual machine
+
+#include <iostream>
+#include "Machine.h"
+#include "Opcodes.h"
+#include "simulation/Simulation.h"
+namespace pim
+{
+ /*unsigned char * rom;
+ int romSize;
+ int romMask;
+
+ unsigned char * ram;
+ int ramSize;
+ int ramMask;
+
+ int programStack;
+ int callStack;*/
+
+ VirtualMachine::VirtualMachine(Simulation * simulation) :
+ rom(NULL),
+ ram(NULL),
+ sim(simulation)
+ {
+
+ }
+
+ void VirtualMachine::LoadProgram(std::vector<unsigned char> fileData)
+ {
+ int lastBit = 0;
+
+ if(!(fileData[0] == 'P' && fileData[1] == 'V' && fileData[2] == 'M' && fileData[3] == '1' && fileData.size() >= 16))
+ {
+ throw InvalidProgramException();
+ }
+
+ int macroSize = 0, propSize = 0, codeSize = 0;
+ macroSize = fileData[4];
+ macroSize |= fileData[5] << 8;
+ macroSize |= fileData[6] << 16;
+ macroSize |= fileData[7] << 24;
+
+ propSize = fileData[8];
+ propSize |= fileData[9] << 8;
+ propSize |= fileData[10] << 16;
+ propSize |= fileData[11] << 24;
+
+ codeSize = fileData[12];
+ codeSize |= fileData[13] << 8;
+ codeSize |= fileData[14] << 16;
+ codeSize |= fileData[15] << 24;
+
+ if(fileData.size() < 16 + macroSize + propSize + codeSize)
+ {
+ throw InvalidProgramException();
+ }
+
+ //std::vector<std::pair<int, int> > insertions;
+
+ int macroOffset = 16;
+ int propOffset = macroOffset+macroSize;
+ int codeOffset = propOffset+propSize;
+
+
+ int filePosition = macroOffset;
+ while(filePosition + 4 < macroSize + macroOffset)
+ {
+ std::string macro;
+ int macroPosition;
+ int macroValue;
+
+ macroPosition = fileData[filePosition++];
+ macroPosition |= fileData[filePosition++] << 8;
+ macroPosition |= fileData[filePosition++] << 16;
+ macroPosition |= fileData[filePosition++] << 24;
+
+ int stringLength = fileData[filePosition++];
+ if(filePosition + stringLength > macroSize + macroOffset)
+ {
+ throw InvalidProgramException();
+ }
+
+ macro.insert(macro.begin(), fileData.begin()+filePosition, fileData.begin()+filePosition+stringLength);
+ filePosition += stringLength;
+
+ bool resolved = false;
+ for(int i = 0; i < PT_NUM; i++)
+ {
+ if(sim->elements[i].Enabled && sim->elements[i].Identifier == macro)
+ {
+ macroValue = i;
+ resolved = true;
+ }
+ }
+ if(!resolved)
+ {
+ throw UnresolvedValueException(macro);
+ }
+
+ if(macroPosition + 3 >= codeSize)
+ {
+ throw InvalidProgramException();
+ }
+
+ std::cout << "Macro insertion [" << macro << "] at " << macroPosition << " with " << macroValue << std::endl;
+
+ fileData[codeOffset+macroPosition] = macroValue & 0xFF;
+ fileData[codeOffset+macroPosition+1] = (macroValue >> 8) & 0xFF;
+ fileData[codeOffset+macroPosition+2] = (macroValue >> 16) & 0xFF;
+ fileData[codeOffset+macroPosition+3] = (macroValue >> 24) & 0xFF;
+ //insertions.push_back(std::pair<int, int>(macroPosition, macroValue));
+ }
+
+ filePosition = propOffset;
+ while(filePosition + 4 < propSize + propOffset)
+ {
+ std::string prop;
+ int propPosition;
+ int propValue;
+
+ propPosition = fileData[filePosition++];
+ propPosition |= fileData[filePosition++] << 8;
+ propPosition |= fileData[filePosition++] << 16;
+ propPosition |= fileData[filePosition++] << 24;
+
+ int stringLength = fileData[filePosition++];
+ if(filePosition + stringLength > propSize + propOffset)
+ {
+ throw InvalidProgramException();
+ }
+
+ prop.insert(prop.begin(), fileData.begin()+filePosition, fileData.begin()+filePosition+stringLength);
+ filePosition += stringLength;
+
+ bool resolved = false;
+
+ std::vector<StructProperty> properties = Particle::GetProperties();
+ for(std::vector<StructProperty>::iterator iter = properties.begin(), end = properties.end(); iter != end; ++iter)
+ {
+ StructProperty property = *iter;
+ std::cout << property.Offset << std::endl;
+ if(property.Name == prop &&
+ (property.Type == StructProperty::ParticleType ||
+ property.Type == StructProperty::Colour ||
+ property.Type == StructProperty::Integer ||
+ property.Type == StructProperty::UInteger ||
+ property.Type == StructProperty::Float)
+ )
+ {
+ propValue = property.Offset;
+ resolved = true;
+ break;
+ }
+ }
+ if(!resolved)
+ {
+ throw UnresolvedValueException(prop);
+ }
+
+ if(propPosition + 3 >= codeSize)
+ {
+ throw InvalidProgramException();
+ }
+
+ std::cout << "Property insertion [" << prop << "] at " << propPosition << " with " << propValue << std::endl;
+
+ fileData[codeOffset+propPosition] = propValue & 0xFF;
+ fileData[codeOffset+propPosition+1] = (propValue >> 8) & 0xFF;
+ fileData[codeOffset+propPosition+2] = (propValue >> 16) & 0xFF;
+ fileData[codeOffset+propPosition+3] = (propValue >> 24) & 0xFF;
+ //insertions.push_back(std::pair<int, int>(macroPosition, macroValue));
+ }
+
+ std::vector<unsigned char> programData;
+ programData.insert(programData.begin(), fileData.begin()+codeOffset, fileData.begin()+codeOffset+codeSize);
+
+ romSize = programData.size();
+
+ for (lastBit = 0; romSize > (1 << lastBit); lastBit++ ) { }
+ romSize = 1 << lastBit;
+ romMask = romSize - 1;
+
+ rom = new Instruction[romSize];
+
+ int pc = 0;
+ int programPosition = 0;
+
+ while(programPosition < programData.size())
+ {
+ int argSize = 0;
+ Instruction instruction;
+ instruction.Opcode = programData[programPosition++];
+ if(argSize = OpcodeArgSize(instruction.Opcode))
+ {
+ if(argSize == 4 && programPosition+3 < programData.size())
+ {
+ int tempInt = 0;
+ tempInt |= programData[programPosition];
+ tempInt |= programData[programPosition+1] << 8;
+ tempInt |= programData[programPosition+2] << 16;
+ tempInt |= programData[programPosition+3] << 24;
+
+
+ std::cout << "Got integer " << tempInt << std::endl;
+
+ if(instruction.Opcode == Opcode::LoadProperty || instruction.Opcode == Opcode::StoreProperty)
+ {
+ if(tempInt > offsetof(Particle, dcolour))
+ throw InvalidProgramException();
+ }
+
+ instruction.Parameter.Integer = tempInt;
+
+ programPosition += 4;
+ }
+ }
+ else
+ {
+ instruction.Parameter.Integer = 0;
+ }
+ rom[pc++] = instruction;
+ }
+ romSize = pc;
+
+ ramSize = 1024;
+ ramMask = ramSize - 1;
+
+ ram = new unsigned char[ramSize];
+ programStack = ramSize-1;
+ callStack = ramSize-260;
+
+ framePointer = callStack;
+ callStack += WORDSIZE; //Since there's nothing on the stack, it shouldn't point to the item on the bottom
+ }
+
+ int VirtualMachine::OpcodeArgSize(int opcode)
+ {
+ switch(opcode)
+ {
+ case Opcode::Load:
+ case Opcode::Store:
+ case Opcode::Constant:
+ case Opcode::Increment:
+ case Opcode::JumpEqual:
+ case Opcode::JumpNotEqual:
+ case Opcode::JumpGreater:
+ case Opcode::JumpGreaterEqual:
+ case Opcode::JumpLess:
+ case Opcode::JumpLessEqual:
+ case Opcode::Jump:
+ case Opcode::Return:
+ case Opcode::LocalEnter:
+ case Opcode::LoadProperty:
+ case Opcode::StoreProperty:
+ return 4;
+ case Opcode::Discard:
+ case Opcode::Duplicate:
+ case Opcode::Add:
+ case Opcode::Subtract:
+ case Opcode::Multiply:
+ case Opcode::Divide:
+ case Opcode::Modulus:
+ case Opcode::Negate:
+ case Opcode::Create:
+ case Opcode::Transform:
+ case Opcode::Get:
+ case Opcode::Position:
+ case Opcode::Kill:
+ return 0;
+ }
+ }
+
+ void VirtualMachine::Run()
+ {
+ //std::cout << "CS: " << callStack << " PS: " << programStack << std::endl;
+ //std::string names[] = { "Load", "Store", "Constant", "Increment", "Discard", "Duplicate", "Add", "Subtract", "Multiply", "Divide", "Modulus", "Negate", "Create", "Transform", "Get", "Position", "Kill", "JumpEqual", "JumpNotEqual", "JumpGreater", "JumpGreaterEqual", "JumpLess", "JumpLessEqual", "Jump", "Return", "LocalEnter"};
+
+ Word temp1;
+ Word temp2;
+ Word temp3;
+ Word temp4;
+ int temp;
+ while(programCounter < romSize)
+ {
+ Word argument = rom[programCounter].Parameter;
+ //std::cerr << programCounter << "\t" << names[rom[programCounter].Opcode] << "\t" << argument.Integer << std::endl;//"\t";
+ switch(rom[programCounter].Opcode)
+ {
+ case Opcode::Load:
+ PSPush(CSA(argument.Integer));
+ break;
+ case Opcode::Store:
+ CSA(argument.Integer) = PSPop();
+ break;
+ case Opcode::Constant:
+ PSPush(argument);
+ break;
+ case Opcode::Increment:
+ PS().Integer += argument.Integer;
+ break;
+ case Opcode::Discard:
+ programStack += WORDSIZE;
+ break;
+ case Opcode::Duplicate:
+ PSPush(PS());
+ break;
+ case Opcode::Add:
+ PSPush(PSPop().Integer + PSPop().Integer);
+ break;
+ case Opcode::Subtract:
+ temp1 = PSPop();
+ PSPush(PSPop().Integer - temp1.Integer);
+ break;
+ case Opcode::Multiply:
+ PSPush(PSPop().Integer * PSPop().Integer);
+ break;
+ case Opcode::Divide:
+ temp1 = PSPop();
+ PSPush(PSPop().Integer / temp1.Integer);
+ break;
+ case Opcode::Modulus:
+ temp1 = PSPop();
+ PSPush(PSPop().Integer % temp1.Integer);
+ break;
+ case Opcode::Negate:
+ PS().Integer = -PS().Integer;
+ break;
+ case Opcode::Create:
+ temp1 = PSPop();
+ temp2 = PSPop();
+ temp3 = PSPop();
+ PSPush(sim->create_part(PSPop().Integer, temp3.Integer, temp2.Integer, temp1.Integer));
+ break;
+ case Opcode::Transform:
+ PSPop();
+ PSPop();
+ PSPush((Word)-1);
+ break;
+ case Opcode::Get:
+ temp1 = PSPop();
+ temp2 = PSPop();
+ if(temp1.Integer < 0 || temp1.Integer >= YRES || temp2.Integer < 0 || temp2.Integer >= XRES || !(temp = sim->pmap[temp1.Integer][temp2.Integer]))
+ {
+ PSPush(-1);
+ break;
+ }
+ PSPush(temp>>8);
+ break;
+ case Opcode::Position:
+ temp1 = PSPop();
+ if(temp1.Integer < 0 || temp1.Integer >= NPART || !sim->parts[temp1.Integer].type)
+ {
+ PSPush(-1);
+ PSPush(-1);
+ break;
+ }
+ PSPush((int)sim->parts[temp1.Integer].x);
+ PSPush((int)sim->parts[temp1.Integer].y);
+ break;
+ case Opcode::Kill:
+ sim->kill_part(PSPop().Integer);
+ PSPush((Word)0);
+ break;
+ case Opcode::LoadProperty:
+ PSPush(PPROP(PSPop().Integer, argument.Integer));
+ break;
+ case Opcode::StoreProperty:
+ temp1 = PSPop();
+ PPROP(temp1.Integer, argument.Integer) = PSPop();
+ break;
+ case Opcode::JumpEqual:
+ if(PSPop().Integer == PSPop().Integer)
+ programCounter = argument.Integer-1;
+ break;
+ case Opcode::JumpNotEqual:
+ if(PSPop().Integer != PSPop().Integer)
+ programCounter = argument.Integer-1;
+ break;
+ case Opcode::JumpGreater:
+ temp1 = PSPop();
+ if(PSPop().Integer > temp1.Integer)
+ programCounter = argument.Integer-1;
+ break;
+ case Opcode::JumpGreaterEqual:
+ temp1 = PSPop();
+ if(PSPop().Integer >= temp1.Integer)
+ programCounter = argument.Integer-1;
+ break;
+ case Opcode::JumpLess:
+ temp1 = PSPop();
+ if(PSPop().Integer < temp1.Integer)
+ programCounter = argument.Integer-1;
+ break;
+ case Opcode::JumpLessEqual:
+ temp1 = PSPop();
+ if(PSPop().Integer <= temp1.Integer)
+ programCounter = argument.Integer-1;
+ break;
+ case Opcode::Jump:
+ programCounter = argument.Integer-1;
+ break;
+ case Opcode::Return:
+ callStack += argument.Integer;
+ break;
+ case Opcode::LocalEnter:
+ callStack -= argument.Integer;
+ break;
+ }
+ //std::cout << programStack << std::endl;
+ programCounter++;
+ }
+ //std::cout << "CS: " << callStack << " PS: " << programStack << std::endl;
+ }
+
+ void VirtualMachine::Compile()
+ {
+ while(programCounter < romSize)
+ {
+ Word argument = rom[programCounter].Parameter;
+ switch(rom[programCounter].Opcode)
+ {
+ case Opcode::Load:
+ emit("83 EF 04"); //sub edi 4
+
+ //Load value at base stack + offset into eax
+ emit("8B 85"); //mov eax [ebp+ram+offset]
+ emit((intptr_t) (ram - argument.Integer));
+
+ //Store value in eax onto top of program stack
+ emit("89 07"); //mov [edi], eax
+ emit((intptr_t) (ram));
+ break;
+ case Opcode::Store:
+ //Load value on top of the program stack into eax
+ emit("8B 07"); //mov eax [edi]
+ emit((intptr_t) (ram));
+
+ //Load value in eax onto top of program stack
+ emit("89 85"); //mov [ebp+ram+offset], eax
+ emit((intptr_t) (ram - argument.Integer));
+
+ emit("83 C7 04"); //add edi 4
+ break;
+ case Opcode::Constant:
+ emit("83 EF 04"); //sub edi 4
+
+ emit("C7 07"); //mov [edi] constant
+ emit((int) (argument.Integer));
+ break;
+ case Opcode::Increment:
+ emit("81 07"); //add [edi] constant
+ emit((int) (argument.Integer));
+ break;
+ case Opcode::Discard:
+ emit("83 C7 04"); //add edi 4
+ break;
+ case Opcode::Duplicate:
+ //Copy value on stack into register
+ emit("8B 07"); //mov eax [edi]
+ //Adjust program stack pointer
+ emit("83 EF 04"); //sub edi 4
+ //Move value in eax into program stack
+ emit("89 07"); //mov [edi], eax
+ break;
+ case Opcode::Add:
+ emit("8B 07"); //mov eax [edi]
+ emit("01 47 04"); //add [edi+4] eax
+ emit("83 C7 04"); //add edi 4
+ break;
+ case Opcode::Subtract:
+ emit("8B 07"); //mov eax [edi]
+ emit("29 47 04"); //sub [edi+4] eax
+ emit("83 C7 04"); //add edi 4
+ break;
+ case Opcode::Multiply:
+ emit("8B 47 04"); //mov eax [edi+4]
+ emit("F7 2F"); //imul [edi]
+ emit("89 47 04"); //mov [edi+4] eax
+ emit("83 C7 04"); //add edi 4
+ break;
+ case Opcode::Divide:
+ emit("8B 47 04");//mov eax [edi+4]
+ emit("99"); //cdq
+ emit("F7 3F"); //idiv [edi]
+ emit("89 47 04"); //mov [edi+4] eax
+ emit("83 C7 04"); //add edi 4
+ break;
+ case Opcode::Modulus:
+ emit("8B 47 04"); // mov eax [edi+4]
+ emit("99"); // cdq
+ emit("F7 3F"); // idiv [edi]
+ emit("89 57 04"); // mov [edi+4] edx
+ emit("83 C7 04"); //add edi 4
+ break;
+ case Opcode::Negate:
+ emit("F7 1F"); //neg [edi]
+ break;
+ case Opcode::Create:
+ //temp1 = PSPop();
+ //temp2 = PSPop();
+ //temp3 = PSPop();
+ //PSPush(sim->create_part(PSPop().Integer, temp3.Integer, temp2.Integer, temp1.Integer));
+ break;
+ case Opcode::Transform:
+ //PSPop();
+ //PSPop();
+ //PSPush((Word)-1);
+ break;
+ case Opcode::Get:
+ //temp1 = PSPop();
+ //temp2 = PSPop();
+ //if(temp1.Integer < 0 || temp1.Integer >= YRES || temp2.Integer < 0 || temp2.Integer >= XRES || !(temp = sim->pmap[temp1.Integer][temp2.Integer]))
+ //{
+ // PSPush(-1);
+ // break;
+ //}
+ //PSPush(temp>>8);
+ break;
+ case Opcode::Position:
+ //temp1 = PSPop();
+ //if(temp1.Integer < 0 || temp1.Integer >= NPART || !sim->parts[temp1.Integer].type)
+ //{
+ // PSPush(-1);
+ // PSPush(-1);
+ // break;
+ //}
+ //PSPush((int)sim->parts[temp1.Integer].x);
+ //PSPush((int)sim->parts[temp1.Integer].y);
+ break;
+ case Opcode::Kill:
+ //sim->kill_part(PSPop().Integer);
+ //PSPush((Word)0);
+ break;
+ case Opcode::LoadProperty:
+ //PSPush(PPROP(PSPop().Integer, argument.Integer));
+ break;
+ case Opcode::StoreProperty:
+ //temp1 = PSPop();
+ //PPROP(temp1.Integer, argument.Integer) = PSPop();
+ break;
+ case Opcode::JumpEqual:
+ emit("83 C7 04"); //add edi 8
+ emit("8B 47 FC"); //mov eax, dword ptr [edi-4]
+ emit("3B 47 F7"); //cmp eax, dword ptr [edi-8]
+ emit("75 06"); //jne +6
+ emit("FF 25"); //jmp [0x12345678]
+ emit(0);
+ break;
+ case Opcode::JumpNotEqual:
+ emit("83 C7 04"); //add edi 8
+ emit("8B 47 FC"); //mov eax, dword ptr [edi-4]
+ emit("3B 47 F7"); //cmp eax, dword ptr [edi-8]
+ emit("74 06"); //je +6
+ emit("FF 25"); //jmp [0x12345678]
+ emit(0);
+ break;
+ case Opcode::JumpGreater:
+ emit("83 C7 04"); //add edi 8
+ emit("8B 47 FC"); //mov eax, dword ptr [edi-4]
+ emit("3B 47 F7"); //cmp eax, dword ptr [edi-8]
+ emit("7E 06"); //jng +6
+ emit("FF 25"); //jmp [0x12345678]
+ emit(0);
+ break;
+ case Opcode::JumpGreaterEqual:
+ emit("83 C7 04"); //add edi 8
+ emit("8B 47 FC"); //mov eax, dword ptr [edi-4]
+ emit("3B 47 F7"); //cmp eax, dword ptr [edi-8]
+ emit("7C 06"); //jnge +6
+ emit("FF 25"); //jmp [0x12345678]
+ emit(0);
+ break;
+ case Opcode::JumpLess:
+ emit("83 C7 04"); //add edi 8
+ emit("8B 47 FC"); //mov eax, dword ptr [edi-4]
+ emit("3B 47 F7"); //cmp eax, dword ptr [edi-8]
+ emit("7D 06"); //jnl +6
+ emit("FF 25"); //jmp [0x12345678]
+ emit(0);
+ break;
+ case Opcode::JumpLessEqual:
+ emit("83 C7 04"); //add edi 8
+ emit("8B 47 FC"); //mov eax, dword ptr [edi-4]
+ emit("3B 47 F7"); //cmp eax, dword ptr [edi-8]
+ emit("7F 06"); //jnle +6
+ emit("FF 25"); //jmp [0x12345678]
+ emit(0);
+ break;
+ case Opcode::Jump:
+ //programCounter = argument.Integer-1;
+ break;
+ case Opcode::Return:
+ emit("81 C6"); //add esi constant
+ emit(argument.Integer);
+ break;
+ case Opcode::LocalEnter:
+ emit("81 EE"); //sub esi constant
+ emit(argument.Integer);
+ break;
+ }
+ //std::cout << programStack << std::endl;
+ programCounter++;
+ }
+ //std::cout << "CS: " << callStack << " PS: " << programStack << std::endl;
+ }
+
+ void VirtualMachine::emit(std::string opcode)
+ {
+
+ }
+
+ void VirtualMachine::emit(int constant)
+ {
+
+ }
+
+ void VirtualMachine::CallCompiled(std::string entryPoint)
+ {
+
+ }
+
+ void VirtualMachine::CallCompiled(int entryPoint)
+ {
+
+ }
+
+ void VirtualMachine::Call(std::string entryPoint)
+ {
+
+ }
+
+ void VirtualMachine::Call(int entryPoint)
+ {
+ programCounter = entryPoint;
+ Run();
+ }
+}
diff --git a/src/pim/Machine.h b/src/pim/Machine.h
new file mode 100644
index 0000000..3bf1131
--- /dev/null
+++ b/src/pim/Machine.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include <cstring>
+
+class Simulation;
+namespace pim
+{
+ union Word
+ {
+ int Integer;
+ float Decimal;
+
+ Word(int integer) : Integer(integer) {}
+ Word(float decimal) : Decimal(decimal) {}
+ Word() {}
+ };
+ struct Instruction
+ {
+ int Opcode;
+ Word Parameter;
+ };
+ class InvalidProgramException: public std::exception
+ {
+ public:
+ InvalidProgramException() { }
+ const char * what() const throw()
+ {
+ return "Invalid program";
+ }
+ ~InvalidProgramException() throw() {};
+ };
+ class UnresolvedValueException: public std::exception
+ {
+ char * error;
+ public:
+ UnresolvedValueException(std::string value) {
+ error = strdup(std::string("Unresolved value: " + value).c_str());
+ }
+ const char * what() const throw()
+ {
+ return error;
+ }
+ ~UnresolvedValueException() throw() {};
+ };
+ class VirtualMachine
+ {
+
+ #define WORDSIZE 4
+
+ //#define OPDEF(name) void op##name(int parameter);
+ //#include "Opcodes.inl"
+ //#undef OPDEF
+
+ Simulation * sim;
+
+ Instruction * rom;
+ int romSize;
+ int romMask;
+
+ unsigned char * compiledRom;
+ int compiledRomSize;
+
+ unsigned char * ram;
+ int ramSize;
+ int ramMask;
+
+ #define CSA(argument) (*((Word*)&ram[framePointer-argument]))
+ #define CS() (*((Word*)&ram[callStack]))
+ #define PS() (*((Word*)&ram[programStack]))
+ #define PPROP(index, property) (*((Word*)(((char*)&sim->parts[(index)])+property)))
+
+ int programStack; //Points to the item on top of the Program Stack
+ int callStack; //Points to the item on top of the call stack
+ int framePointer; //Points to the bottom (first item) on the current frame of the call stack
+
+ //Instruction * instructions;
+
+ int programCounter;
+
+ void emit(std::string opcode);
+ void emit(int constant);
+ public:
+ VirtualMachine(Simulation * sim);
+ int OpcodeArgSize(int opcode);
+ void LoadProgram(std::vector<unsigned char> programData);
+ void Run();
+ void Compile();
+ void CallCompiled(std::string entryPoint);
+ void CallCompiled(int entryPoint);
+ void Call(std::string entryPoint);
+ void Call(int entryPoint);
+ inline void PSPush(Word word)
+ {
+ programStack -= WORDSIZE;
+ PS() = word;
+ }
+
+ inline Word PSPop()
+ {
+ Word word = PS();
+ programStack += WORDSIZE;
+ return word;
+ }
+
+ inline void CSPush(Word word)
+ {
+ callStack -= WORDSIZE;
+ CS() = word;
+ }
+
+ inline Word CSPop()
+ {
+ Word word = CS();
+ callStack += WORDSIZE;
+ return word;
+ }
+ };
+} \ No newline at end of file
diff --git a/src/pim/Opcodes.h b/src/pim/Opcodes.h
new file mode 100644
index 0000000..4e1a7ce
--- /dev/null
+++ b/src/pim/Opcodes.h
@@ -0,0 +1,12 @@
+namespace pim
+{
+ struct Opcode
+ {
+ enum
+ {
+ #define OPDEF(name) name,
+ #include "Opcodes.inl"
+ #undef OPDEF
+ };
+ };
+} \ No newline at end of file
diff --git a/src/pim/Opcodes.inl b/src/pim/Opcodes.inl
new file mode 100644
index 0000000..4b28294
--- /dev/null
+++ b/src/pim/Opcodes.inl
@@ -0,0 +1,28 @@
+OPDEF(Load)
+OPDEF(Store)
+OPDEF(Constant)
+OPDEF(Increment)
+OPDEF(Discard)
+OPDEF(Duplicate)
+OPDEF(Add)
+OPDEF(Subtract)
+OPDEF(Multiply)
+OPDEF(Divide)
+OPDEF(Modulus)
+OPDEF(Negate)
+OPDEF(Create)
+OPDEF(Transform)
+OPDEF(Get)
+OPDEF(Position)
+OPDEF(Kill)
+OPDEF(LoadProperty)
+OPDEF(StoreProperty)
+OPDEF(JumpEqual)
+OPDEF(JumpNotEqual)
+OPDEF(JumpGreater)
+OPDEF(JumpGreaterEqual)
+OPDEF(JumpLess)
+OPDEF(JumpLessEqual)
+OPDEF(Jump)
+OPDEF(Return)
+OPDEF(LocalEnter) \ No newline at end of file
diff --git a/src/pim/Parser.cpp b/src/pim/Parser.cpp
new file mode 100644
index 0000000..4b164c8
--- /dev/null
+++ b/src/pim/Parser.cpp
@@ -0,0 +1,653 @@
+//Syntax analyser
+#include "Parser.h"
+#include "Format.h"
+namespace pim
+{
+ namespace compiler
+ {
+ Parser::Parser(std::stringstream & source_) :
+ source(source_)
+ {
+ scanner = new Scanner(source);
+ generator = new Generator();
+
+ token = scanner->NextToken();
+
+ }
+
+ std::vector<unsigned char> Parser::Compile()
+ {
+ program();
+ return generator->Finish();
+ }
+
+ /*
+ <program> ::= <function list>
+ */
+ void Parser::program()
+ {
+ functionList();
+ }
+
+ /*
+ <function list> ::= <function> | <function> <function list>
+ */
+ void Parser::functionList()
+ {
+ function();
+ while(look(Token::FunctionSymbol))
+ function();
+ }
+
+ /*
+ <function> ::= function identifier ( <declaration list> ) <block> end
+ */
+ void Parser::function()
+ {
+ std::string functionName;
+
+ expect(Token::FunctionSymbol);
+
+ functionName = token.Source;
+ //generator->ScopeLabel(functionName); //Function name
+ generator->PushScope(functionName);
+ expect(Token::Identifier);
+
+ expect(Token::LeftBracket);
+ if(!accept(Token::RightBracket))
+ {
+ argumentList();
+ expect(Token::RightBracket);
+ }
+ block();
+ expect(Token::EndSymbol);
+ generator->Return();
+
+ generator->PopScope();
+ }
+
+ /*
+ <function call> ::= identifier ( <expression list> )
+ */
+ void Parser::functionCall()
+ {
+ std::string functionName;
+
+ functionName = token.Source;
+ expect(Token::Identifier);
+ expect(Token::LeftBracket);
+ expressionList();
+ expect(Token::RightBracket);
+ //generator->Call(functionName);
+ }
+
+ /*
+ <block> ::= <declaration list> <statement list>
+ */
+ void Parser::block()
+ {
+ if(look(Token::IntegerSymbol) || look(Token::DecimalSymbol) || look(Token::ParticleSymbol))
+ declarationList();
+ statementList();
+ }
+
+ /*
+ <argument list> ::= <argument> | <argument> , <argument list>
+ */
+ void Parser::argumentList()
+ {
+ argument();
+ while(accept(Token::CommaSymbol))
+ argument();
+ }
+
+ /*
+ <argument> ::= integer identifier | decimal identifier | particle identifier
+ */
+ void Parser::argument()
+ {
+ generator->ScopeVariableType(token.Symbol);
+ if(!accept(Token::IntegerSymbol))
+ if(!accept(Token::DecimalSymbol))
+ if(!accept(Token::ParticleSymbol))
+ throw ParserExpectException(token, "type name");
+ generator->ScopeVariable(token.Source);
+ expect(Token::Identifier);
+ }
+
+ /*
+ <declaration list> ::= <declaration> | <declaration> , <declaration list>
+ */
+ void Parser::declarationList()
+ {
+ declaration();
+ while(accept(Token::CommaSymbol))
+ declaration();
+ }
+
+ /*
+ <declaration> ::= integer <identifier list> | decimal <identifier list> | particle <identifier list>
+ */
+ void Parser::declaration()
+ {
+ generator->ScopeVariableType(token.Symbol);
+ if(!accept(Token::IntegerSymbol))
+ if(!accept(Token::DecimalSymbol))
+ if(!accept(Token::ParticleSymbol))
+ throw ParserExpectException(token, "type name");
+ identifierList();
+ }
+
+ /*
+ <identifier list> ::= identifier | identifier , <identifier list>
+ */
+ void Parser::identifierList()
+ {
+ generator->ScopeVariable(token.Source);
+ expect(Token::Identifier);
+ while(accept(Token::CommaSymbol))
+ {
+ generator->ScopeVariable(token.Source);
+ expect(Token::Identifier);
+ }
+ }
+
+ /*
+ <statement list> ::= <statement> | <statement> <statement list>
+ */
+ void Parser::statementList()
+ {
+ statement();
+ while(!look(Token::EndSymbol) && !look(Token::ElseIfSymbol))
+ statement();
+ }
+
+ /*
+ <statement> ::= <neighbour statement> | <if statement> | <assignment statement> | <function call> | <particle action> | break | continue
+ */
+ void Parser::statement()
+ {
+ //generator->Begin(NonTerminal::Statement);
+ if(look(Token::NeighbourSymbol))
+ {
+ neighbourStatement();
+ }
+ else if(look(Token::IfSymbol))
+ {
+ ifStatement();
+ }
+ else if(look(Token::CreateSymbol) || look(Token::KillSymbol) || look(Token::GetSymbol) || look(Token::TransformSymbol))
+ {
+ particleAction();
+ generator->Discard();
+ }
+ else if(look(Token::BreakSymbol))
+ {
+ expect(Token::BreakSymbol);
+ generator->Jump(breakLabel);
+ }
+ else if(look(Token::ContinueSymbol))
+ {
+ expect(Token::ContinueSymbol);
+ generator->Jump(continueLabel);
+ }
+ else if(look(Token::Identifier))
+ {
+ assigmentStatement();
+ }
+ //generator->End(NonTerminal::Statement);
+ }
+
+ /*
+ <particle action> ::= <kill statement> | <create statement> | <transform statement>
+ */
+ void Parser::particleAction()
+ {
+ if(look(Token::KillSymbol))
+ {
+ killStatement();
+ }
+ else if(look(Token::CreateSymbol))
+ {
+ createStatement();
+ }
+ else if(look(Token::TransformSymbol))
+ {
+ transformStatement();
+ }
+
+ }
+
+ /*
+ <kill statement> ::= kill ( <expression> )
+ */
+ void Parser::killStatement()
+ {
+ expect(Token::KillSymbol);
+ expect(Token::LeftBracket);
+ expression();
+ expect(Token::RightBracket);
+ generator->KillParticle();
+ }
+
+ /*
+ <create statement> ::= create ( <expression>, <expression>, <expression>, <expression> )
+ */
+ void Parser::createStatement()
+ {
+ expect(Token::CreateSymbol);
+ expect(Token::LeftBracket);
+ expression();
+ expect(Token::CommaSymbol);
+ expression();
+ expect(Token::CommaSymbol);
+ expression();
+ expect(Token::CommaSymbol);
+ expression();
+ expect(Token::RightBracket);
+ generator->CreateParticle();
+ }
+
+ /*
+ <transform statement> ::= transform ( <expression>, <expression> )
+ */
+ void Parser::transformStatement()
+ {
+ expect(Token::TransformSymbol);
+ expect(Token::LeftBracket);
+ expression();
+ expect(Token::CommaSymbol);
+ expression();
+ expect(Token::RightBracket);
+ generator->TransformParticle();
+ }
+
+ /*
+ <get statement> ::= get ( <expression>, <expression> )
+ */
+ void Parser::getStatement()
+ {
+ expect(Token::GetSymbol);
+ expect(Token::LeftBracket);
+ expression();
+ expect(Token::CommaSymbol);
+ expression();
+ expect(Token::RightBracket);
+ generator->GetParticle();
+ }
+
+ /*
+ <neighbour statement> ::= neighbour identifier for <expression> do <block> end | neighbour identifier for <expression>, <expression> do <block> end
+ */
+ void Parser::neighbourStatement()
+ {
+ std::string neighbourVariable;
+ std::string loopLabel = generator->UniqueLabel("neighbour");
+ std::string xVar = loopLabel+"X";
+ std::string xMin = loopLabel+"minX";
+ std::string xMax = loopLabel+"maxX";
+ std::string yVar = loopLabel+"Y";
+ std::string yMax = loopLabel+"maxY";
+ breakLabel = loopLabel+"End";
+ continueLabel = loopLabel+"Next";
+
+ expect(Token::NeighbourSymbol);
+
+ generator->PushLocalScope(loopLabel+"Start");
+ neighbourVariable = token.Source;
+ expect(Token::Identifier);
+ generator->ScopeVariableType(Token::IntegerConstant);
+ generator->ScopeVariable(neighbourVariable);
+ generator->ScopeVariable(xVar);
+ generator->ScopeVariable(yVar);
+ generator->ScopeVariable(xMin);
+ generator->ScopeVariable(xMax);
+ generator->ScopeVariable(yMax);
+
+ generator->LocalEnter();
+
+ expect(Token::OfSymbol);
+
+ //Initialise position
+ expression();
+ generator->GetPosition();
+ generator->Duplicate();
+ generator->Increment("-1");
+ generator->StoreVariable(yVar);
+ generator->Increment("1");
+ generator->StoreVariable(yMax);
+
+ generator->Duplicate();
+ generator->Increment("-1");
+ generator->Duplicate();
+ generator->StoreVariable(xVar);
+ generator->StoreVariable(xMin);
+ generator->Increment("1");
+ generator->StoreVariable(xMax);
+
+ //if(accept(Token::CommaSymbol))
+ // expression();
+ expect(Token::DoSymbol);
+
+ generator->ScopeLabel(loopLabel+"Next");
+
+
+
+
+
+ //Check X
+ generator->LoadVariable(xVar);
+ generator->LoadVariable(xMax);
+ //generator->Duplicate(); //Duplicate xvar so it can be used for incrementing
+
+ generator->JumpLessEqual(loopLabel+"Begin");
+ //if(xVar > xMax) {
+
+ //Reset X, increment Y
+ generator->LoadVariable(xMin);
+ generator->StoreVariable(xVar);
+
+ generator->LoadVariable(yVar);
+ generator->Increment("1");
+ generator->Duplicate();
+ generator->StoreVariable(yVar);
+
+
+ //Check Y
+ generator->LoadVariable(yMax);
+ generator->JumpGreater(loopLabel+"End");
+
+ //}
+
+ //Start of loop
+ generator->ScopeLabel(loopLabel+"Begin");
+
+ generator->LoadVariable(xVar);
+ generator->LoadVariable(yVar);
+ generator->GetParticle();
+ generator->StoreVariable(neighbourVariable);
+
+ block();
+
+ //Increment X
+ generator->LoadVariable(xVar);
+ generator->Increment("1");
+ generator->StoreVariable(xVar);
+
+ //Next element
+ generator->Jump(loopLabel+"Next");
+
+ generator->ScopeLabel(loopLabel+"End");
+ generator->Return();
+ generator->PopScope();
+ expect(Token::EndSymbol);
+ }
+
+ /*
+ <if statement> ::= if <condition> then <block> end
+ */
+ void Parser::ifStatement()
+ {
+ std::string label = generator->UniqueLabel("if");
+ int blockNum = 0;
+ expect(Token::IfSymbol);
+ condition(label+format::NumberToString<int>(blockNum));
+ expect(Token::ThenSymbol);
+ block();
+ while(accept(Token::ElseIfSymbol))
+ {
+ generator->ScopeLabel(label+format::NumberToString<int>(blockNum++));
+ condition(label+format::NumberToString<int>(blockNum));
+ expect(Token::ThenSymbol);
+ block();
+ }
+ if(accept(Token::ElseSymbol))
+ {
+ generator->ScopeLabel(label+format::NumberToString<int>(blockNum++));
+ block();
+ }
+ else
+ {
+ generator->ScopeLabel(label+format::NumberToString<int>(blockNum++));
+ }
+ expect(Token::EndSymbol);
+ //generator->End(NonTerminal::IfStatement);
+ }
+
+ /*
+ <condition> ::= <expression> <conditional operator> <expression>
+ */
+ void Parser::condition(std::string jumpLabel)
+ {
+ expression();
+
+ Token token = forward();
+
+ expression();
+
+ if(token.Symbol == Token::GreaterSymbol)
+ {
+ generator->JumpLessEqual(jumpLabel);
+ }
+ else if(token.Symbol == Token::GreaterEqualSymbol)
+ {
+ generator->JumpLess(jumpLabel);
+ }
+ else if(token.Symbol == Token::EqualSymbol)
+ {
+ generator->JumpNotEqual(jumpLabel);
+ }
+ else if(token.Symbol == Token::NotEqualSymbol)
+ {
+ generator->JumpEqual(jumpLabel);
+ }
+ else if(token.Symbol == Token::LessSymbol)
+ {
+ generator->JumpGreaterEqual(jumpLabel);
+ }
+ else if(token.Symbol == Token::LessEqualSymbol)
+ {
+ generator->JumpGreater(jumpLabel);
+ }
+ else
+ throw ParserExpectException(token, "conditional operator");
+ }
+
+ /*
+ <assigment statement> ::= identifier = <expression> | identifier.property = <expression>
+ */
+ void Parser::assigmentStatement()
+ {
+ std::string variable = token.Source;
+ expect(Token::Identifier);
+ if(accept(Token::AssignSymbol))
+ {
+ expression();
+ generator->StoreVariable(variable);
+ }
+ else if(accept(Token::DotSymbol))
+ {
+ std::string property = token.Source;
+ expect(Token::Identifier);
+ expect(Token::AssignSymbol);
+ expression();
+ generator->LoadVariable(variable);
+ generator->StoreProperty(property);
+ }
+ }
+
+ /*
+ <expression list> ::= <expression> | <expression> , <expression list>
+ */
+ void Parser::expressionList()
+ {
+ //generator->Begin(NonTerminal::ExpressionList);
+ expression();
+ while(accept(Token::CommaSymbol))
+ expression();
+ //generator->End(NonTerminal::ExpressionList);
+ }
+
+ /*
+ <expression> ::= <term> | <expression> + <term> | <expression> - <term>
+ */
+ void Parser::expression()
+ {
+ term();
+ int as = token.Symbol;
+ while(accept(Token::PlusSymbol) || accept(Token::MinusSymbol))
+ {
+ term();
+ if(as == Token::PlusSymbol)
+ generator->Add();
+ else if(as == Token::MinusSymbol)
+ generator->Subtract();
+ }
+ //generator->End(NonTerminal::Expression);
+ }
+
+ /*
+ <term> ::= <factor> | <term> * <factor> | <term> / <factor>
+ */
+ void Parser::term()
+ {
+ //generator->Begin(NonTerminal::Term);
+ factor();
+ int md = token.Symbol;
+ while(accept(Token::MultiplySymbol) || accept(Token::DivideSymbol))
+ {
+ factor();
+ if(md == Token::MultiplySymbol)
+ generator->Multiply();
+ else if(md == Token::DivideSymbol)
+ generator->Divide();
+ }
+ //generator->End(NonTerminal::Term);
+ }
+
+ /*
+ <factor> ::= <variable value> | - <variable value> | numberConstant | - numberConstant | ( <expression> ) | - ( <expression> )
+ */
+ void Parser::factor()
+ {
+ bool doNegate = false;
+ std::string factor = token.Source;
+ if(accept(Token::MinusSymbol))
+ {
+ factor = token.Source;
+ doNegate = true;
+ }
+ if(accept(Token::IntegerConstant) || accept(Token::DecimalConstant))
+ {
+ if(doNegate)
+ {
+ doNegate = false;
+ generator->Constant("-" + factor);
+ }
+ else
+ generator->Constant(factor);
+ }
+ else if(accept(Token::LeftBracket))
+ {
+ expression();
+ expect(Token::RightBracket);
+ }
+ else
+ {
+ variableValue();
+ }
+ if(doNegate)
+ generator->Negate();
+ }
+
+ /*
+ <variable value> ::= <function call> | identifier | identifier.property | rtmacro | <particle action>
+ */
+ void Parser::variableValue()
+ {
+ std::string variable = token.Source;
+ if(accept(Token::Identifier))
+ {
+ if(look(Token::LeftBracket))
+ {
+ back();
+ functionCall();
+ }
+ else
+ {
+ if(accept(Token::DotSymbol))
+ {
+ std::string property = token.Source;
+ expect(Token::Identifier);
+ generator->LoadVariable(variable);
+ generator->LoadProperty(property);
+ }
+ else
+ {
+ generator->LoadVariable(variable);
+ }
+ }
+ }
+ else if(accept(Token::RTMacro))
+ {
+ generator->RTConstant(variable);
+ }
+ else
+ {
+ particleAction();
+ }
+ }
+
+ bool Parser::accept(int symbol)
+ {
+ if(symbol == token.Symbol)
+ {
+ lastToken = token;
+ if(previousTokens.size())
+ {
+ token = previousTokens.top();
+ previousTokens.pop();
+ }
+ else
+ token = scanner->NextToken();
+ //std::cout << "Symbol " << Token::SymbolNames[symbol] << " " << lastToken.Source << std::endl;
+ return true;
+ }
+ //std::cout << "Bad Symbol " << Token::SymbolNames[symbol] << " " << token.Source << " (" << token.GetName() << ")" << std::endl;
+ return false;
+ }
+
+
+ bool Parser::look(int symbol)
+ {
+ if(symbol == token.Symbol)
+ return true;
+ return false;
+ }
+
+ void Parser::back()
+ {
+ previousTokens.push(token);
+ token = lastToken;
+ }
+
+ Token Parser::forward()
+ {
+ lastToken = token;
+ if(previousTokens.size())
+ {
+ token = previousTokens.top();
+ previousTokens.pop();
+ }
+ else
+ token = scanner->NextToken();
+ return lastToken;
+ }
+
+ void Parser::expect(int symbol)
+ {
+ if(!accept(symbol))
+ throw ParserExpectException(token, symbol);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/pim/Parser.h b/src/pim/Parser.h
new file mode 100644
index 0000000..9a4f736
--- /dev/null
+++ b/src/pim/Parser.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include <string>
+#include <cstring>
+#include <sstream>
+#include "Scanner.h"
+#include "Generator.h"
+#include "Token.h"
+namespace pim
+{
+ namespace compiler
+ {
+ class ParserExpectException: public std::exception
+ {
+ char * error;
+ public:
+ ParserExpectException(Token token, int expectingSymbol) {
+ error = strdup(std::string("Expecting " + Token::SymbolNames[expectingSymbol] + " got " + token.Source).c_str());
+ }
+ ParserExpectException(Token token, std::string expectingString) {
+ error = strdup(std::string("Expecting " + expectingString + " got " + token.Source).c_str());
+ }
+ const char * what() const throw()
+ {
+ return error;
+ }
+ ~ParserExpectException() throw() {};
+ };
+ class Parser
+ {
+ std::stringstream & source;
+ Generator * generator;
+ Scanner * scanner;
+ Token token;
+ Token lastToken;
+ std::string breakLabel;
+ std::string continueLabel;
+ std::stack<Token> previousTokens;
+
+ void program();
+ void functionList();
+ void function();
+ void functionCall();
+ void block();
+ void argumentList();
+ void argument();
+ void declarationList();
+ void declaration();
+ void identifierList();
+ void statementList();
+ void statement();
+ void neighbourStatement();
+ void ifStatement();
+ void condition(std::string jumpLabel);
+ void assigmentStatement();
+ void particleAction();
+ void killStatement();
+ void getStatement();
+ void createStatement();
+ void transformStatement();
+ void expressionList();
+
+ void expression();
+ void term();
+ void factor();
+ void variableValue();
+
+ Token forward();
+ bool accept(int symbol);
+ bool look(int symbol);
+ void back();
+ void expect(int symbol);
+ public:
+ Parser(std::stringstream & source_);
+
+ std::vector<unsigned char> Compile();
+ };
+ }
+} \ No newline at end of file
diff --git a/src/pim/Scanner.cpp b/src/pim/Scanner.cpp
new file mode 100644
index 0000000..e6d145a
--- /dev/null
+++ b/src/pim/Scanner.cpp
@@ -0,0 +1,199 @@
+//Lexical analyser
+#include <algorithm>
+#include <cctype>
+#include "Scanner.h"
+
+namespace pim
+{
+ namespace compiler
+ {
+ Scanner::Scanner(std::stringstream & source_) :
+ source(source_)
+ {
+ nextCharacter();
+ }
+
+ Token Scanner::NextToken()
+ {
+ //Read whitespace, newlines and comments
+ while(
+ cChar == ' ' || cChar == '\t' ||
+ cChar == '\r' || cChar == '\n' ||
+ cChar == '/')
+ {
+ if(cChar == '/')
+ {
+ nextCharacter();
+ if(cChar == '/')
+ {
+ while(cChar != '\n' && cChar != '\r')
+ nextCharacter();
+ }
+ else
+ return Token(Token::DivideSymbol, "/", cLine);
+ }
+
+ if(cChar == '\r')
+ {
+ nextCharacter();
+ if(cChar == '\n')
+ cLine++;
+ else
+ continue;
+ }
+ else if(cChar == '\n')
+ cLine++;
+
+ nextCharacter();
+ }
+
+ if(std::isalpha(cChar)) //Read alphanumeric symbols
+ {
+ cToken.clear();
+ while(std::isalpha(cChar) || std::isdigit(cChar))
+ {
+ cToken.push_back(cChar);
+ nextCharacter();
+ }
+
+ std::transform(cToken.begin(), cToken.end(), cToken.begin(), ::tolower);
+
+ for(int i = 0; i < Token::SymbolNumber; i++)
+ if(Token::SymbolNames[i] == cToken)
+ return Token(i, cToken, cLine);
+ return Token(Token::Identifier, cToken, cLine);
+ }
+ else if(std::isdigit(cChar)) //Read numeric constants
+ {
+ bool decimal = false;
+ cToken.clear();
+ while(std::isdigit(cChar))
+ {
+ cToken.push_back(cChar);
+ nextCharacter();
+ }
+ if(cChar == '.')
+ {
+ decimal = true;
+ cToken.push_back(cChar);
+ nextCharacter();
+ while(std::isdigit(cChar))
+ {
+ cToken.push_back(cChar);
+ nextCharacter();
+ }
+ }
+ if(decimal)
+ return Token(Token::DecimalConstant, cToken, cLine);
+ return Token(Token::IntegerConstant, cToken, cLine);
+ }
+ else if(cChar == '[')
+ {
+ cToken.clear();
+ nextCharacter();
+ while(std::isalpha(cChar) || std::isdigit(cChar) || cChar == '_' || cChar == '-')
+ {
+ cToken.push_back(cChar);
+ nextCharacter();
+ }
+ nextCharacter();
+
+ std::transform(cToken.begin(), cToken.end(), cToken.begin(), ::toupper);
+
+ return Token(Token::RTMacro, cToken, cLine);
+ }
+ else if(cChar == '=')
+ {
+ nextCharacter();
+ if(cChar == '=')
+ {
+ nextCharacter();
+ return Token(Token::EqualSymbol, "==", cLine);
+ }
+ return Token(Token::AssignSymbol, "=", cLine);
+ }
+ else if(cChar == '!')
+ {
+ nextCharacter();
+ if(cChar == '=')
+ return Token(Token::NotEqualSymbol, "==", cLine);
+ }
+ else if(cChar == '(')
+ {
+ nextCharacter();
+ return Token(Token::LeftBracket, "(", cLine);
+ }
+ else if(cChar == ')')
+ {
+ nextCharacter();
+ return Token(Token::RightBracket, ")", cLine);
+ }
+ else if(cChar == '/')
+ {
+ nextCharacter();
+ return Token(Token::DivideSymbol, "/", cLine);
+ }
+ else if(cChar == '*')
+ {
+ nextCharacter();
+ return Token(Token::MultiplySymbol, "*", cLine);
+ }
+ else if(cChar == '+')
+ {
+ nextCharacter();
+ return Token(Token::PlusSymbol, "+", cLine);
+ }
+ else if(cChar == '-')
+ {
+ nextCharacter();
+ return Token(Token::MinusSymbol, "-", cLine);
+ }
+ else if(cChar == '%')
+ {
+ nextCharacter();
+ return Token(Token::ModuloSymbol, "%", cLine);
+ }
+ else if(cChar == '<')
+ {
+ nextCharacter();
+ if(cChar == '=')
+ {
+ return Token(Token::LessEqualSymbol, "<=", cLine);
+ }
+ return Token(Token::LessSymbol, "<", cLine);
+ }
+ else if(cChar == '>')
+ {
+ nextCharacter();
+ if(cChar == '=')
+ {
+ return Token(Token::GreaterEqualSymbol, ">=", cLine);
+ }
+ return Token(Token::GreaterSymbol, ">", cLine);
+ }
+ else if(cChar == ',')
+ {
+ nextCharacter();
+ return Token(Token::CommaSymbol, ",", cLine);
+ }
+ else if(cChar == '.')
+ {
+ nextCharacter();
+ return Token(Token::DotSymbol, ".", cLine);
+ }
+ else
+ {
+ nextCharacter();
+ return Token(Token::InvalidSymbol, std::string(1, cChar), cLine);
+ }
+ }
+
+ void Scanner::nextCharacter()
+ {
+ if(source.good())
+ cChar = source.get();
+ else
+ cChar = 0;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/pim/Scanner.h b/src/pim/Scanner.h
new file mode 100644
index 0000000..2e8143c
--- /dev/null
+++ b/src/pim/Scanner.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <string>
+#include <sstream>
+#include "Token.h"
+namespace pim
+{
+ namespace compiler
+ {
+ class Scanner
+ {
+ char cChar;
+ int cLine;
+ std::string cToken;
+ std::stringstream & source;
+ void nextCharacter();
+ public:
+ Scanner(std::stringstream & source_);
+ Token NextToken();
+ };
+ }
+} \ No newline at end of file
diff --git a/src/pim/Token.cpp b/src/pim/Token.cpp
new file mode 100644
index 0000000..453e92f
--- /dev/null
+++ b/src/pim/Token.cpp
@@ -0,0 +1,51 @@
+#include "Token.h"
+
+namespace pim
+{
+ namespace compiler
+ {
+ std::string Token::SymbolNames[] = {
+ "=",
+ "function",
+ "(",
+ ")",
+ "/",
+ "*",
+ "+",
+ "-",
+ "%",
+ "INTEGER",
+ "DECIMAL",
+ "PARTICLE",
+ "integer",
+ "decimal",
+ "particle",
+ "is",
+ "<",
+ "<=",
+ ">",
+ ">=",
+ "==",
+ "!=",
+ "neighbour",
+ "do",
+ "of",
+ "break",
+ "continue",
+ "if",
+ "else",
+ "elseif",
+ "then",
+ "end",
+ "kill",
+ "create",
+ "transform",
+ "get",
+ "RUNTIMEMACRO",
+ "IDENTIFIER",
+ ",",
+ ".",
+ "INVALID SYMBOL"
+ };
+ }
+} \ No newline at end of file
diff --git a/src/pim/Token.h b/src/pim/Token.h
new file mode 100644
index 0000000..15cd48f
--- /dev/null
+++ b/src/pim/Token.h
@@ -0,0 +1,83 @@
+#pragma once
+
+#include <string>
+namespace pim
+{
+ namespace compiler
+ {
+ class Token
+ {
+ public:
+ static std::string SymbolNames[];
+
+ enum
+ {
+ AssignSymbol = 0,
+ FunctionSymbol,
+
+ LeftBracket,
+ RightBracket,
+ DivideSymbol,
+ MultiplySymbol,
+ PlusSymbol,
+ MinusSymbol,
+ ModuloSymbol,
+
+ IntegerConstant,
+ DecimalConstant,
+ ParticleConstant,
+
+ IntegerSymbol,
+ DecimalSymbol,
+ ParticleSymbol,
+
+ IsSymbol,
+ LessSymbol,
+ LessEqualSymbol,
+ GreaterSymbol,
+ GreaterEqualSymbol,
+ NotEqualSymbol,
+ EqualSymbol,
+
+ NeighbourSymbol,
+ DoSymbol,
+ OfSymbol,
+ BreakSymbol,
+ ContinueSymbol,
+ IfSymbol,
+ ElseSymbol,
+ ElseIfSymbol,
+ ThenSymbol,
+ EndSymbol,
+
+ KillSymbol,
+ CreateSymbol,
+ TransformSymbol,
+ GetSymbol,
+
+ RTMacro,
+ Identifier,
+
+ CommaSymbol,
+ DotSymbol,
+
+ InvalidSymbol,
+
+ SymbolNumber
+ };
+ int Symbol;
+ int LineNumber;
+ std::string Source;
+
+ Token(int symbol = InvalidSymbol, std::string source = "HERP DERP", int lineNumber = 0) :
+ Symbol(symbol),
+ Source(source),
+ LineNumber(lineNumber) {}
+
+ std::string GetName()
+ {
+ return SymbolNames[Symbol];
+ }
+ };
+ }
+} \ No newline at end of file
diff --git a/src/powdertoyjava/OpenGLCanvasMacOS.h b/src/powdertoyjava/OpenGLCanvasMacOS.h
new file mode 100644
index 0000000..cee495a
--- /dev/null
+++ b/src/powdertoyjava/OpenGLCanvasMacOS.h
@@ -0,0 +1,35 @@
+#ifdef USE_JNI
+#import <jawt_md.h>
+
+#import <Cocoa/Cocoa.h>
+#import <AppKit/NSOpenGL.h>
+
+NSOpenGLPixelFormat* defaultPixelFormat();
+
+NSOpenGLContext* ensureContext(NSOpenGLContext* openGLContext, NSView *view);
+
+typedef struct {
+ JAWT* awt;
+ JAWT_DrawingSurface* ds;
+ JAWT_DrawingSurfaceInfo* dsi;
+ JAWT_MacOSXDrawingSurfaceInfo* dsi_mac;
+ NSView *view;
+ NSOpenGLContext* openGLContext;
+} ContextInfo;
+
+ContextInfo* getContext(JNIEnv *env, jobject canvas);
+
+void freeContext(JNIEnv *env, jobject canvas, ContextInfo* ci);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ JNIEXPORT jboolean JNICALL Java_OpenGLCanvas_beginOpenGL(JNIEnv *env, jobject canvas);
+ JNIEXPORT void JNICALL Java_OpenGLCanvas_endOpenGL(JNIEnv *env, jobject canvas);
+ JNIEXPORT void JNICALL Java_OpenGLCanvas_updateOpenGL(JNIEnv *env, jobject canvas);
+ JNIEXPORT void JNICALL Java_OpenGLCanvas_allocOpenGL(JNIEnv *env, jobject canvas);
+ JNIEXPORT void JNICALL Java_OpenGLCanvas_releaseOpenGL(JNIEnv *env, jobject canvas);
+#ifdef __cplusplus
+}
+#endif
+#endif \ No newline at end of file
diff --git a/src/powdertoyjava/OpenGLCanvasMacOS.mm b/src/powdertoyjava/OpenGLCanvasMacOS.mm
new file mode 100644
index 0000000..c169c5f
--- /dev/null
+++ b/src/powdertoyjava/OpenGLCanvasMacOS.mm
@@ -0,0 +1,169 @@
+#ifdef USE_JNI
+#include "OpenGLCanvasMacOS.h"
+
+static jfieldID ctxID = NULL;
+
+NSOpenGLPixelFormat* defaultPixelFormat()
+{
+ NSOpenGLPixelFormatAttribute attributes [] = {
+ NSOpenGLPFAWindow,
+ NSOpenGLPFADoubleBuffer,
+ NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)16,
+ 0
+ };
+ return [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
+}
+
+NSOpenGLContext* ensureContext(NSOpenGLContext* openGLContext, NSView *view) {
+ NSOpenGLContext* _openGLContext = openGLContext;
+ if (!_openGLContext) {
+ NSOpenGLPixelFormat* pixelFormat = defaultPixelFormat();
+ _openGLContext = [[NSOpenGLContext alloc]
+ initWithFormat:pixelFormat
+ shareContext:nil];
+ [pixelFormat release];
+ }
+ if ([_openGLContext view] != view) {
+ [_openGLContext setView:view];
+ }
+ [_openGLContext makeCurrentContext];
+
+ return _openGLContext;
+}
+
+ContextInfo* getContext(JNIEnv *env, jobject canvas)
+{
+ if (!ctxID) {
+ jclass cls = env->GetObjectClass(canvas);
+ ctxID = env->GetFieldID(cls, "openGLContext", "J");
+ }
+
+ ContextInfo *ci = (ContextInfo *)(long)(env->GetLongField(canvas, ctxID));
+
+ if (!ci) {
+ ci = (ContextInfo *)calloc(sizeof(ContextInfo), 1);
+ ci->awt = (JAWT *)calloc(sizeof(JAWT), 1);
+ env->SetLongField(canvas, ctxID, (jlong)(long)ci);
+ }
+
+ return ci;
+}
+
+void freeContext(JNIEnv *env, jobject canvas, ContextInfo* ci)
+{
+ if (ci) {
+ free(ci->awt);
+ free(ci);
+ env->SetLongField(canvas, ctxID, 0L);
+ }
+}
+
+JNIEXPORT jboolean JNICALL Java_OpenGLCanvas_beginOpenGL(JNIEnv *env, jobject canvas)
+{
+ ContextInfo *ci = getContext(env, canvas);
+
+ // Lock the drawing surface
+ // You must lock EACH TIME before drawing
+ jint lock = ci->ds->Lock(ci->ds);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+ assert((lock & JAWT_LOCK_ERROR) == 0);
+
+ // Get the drawing surface info
+ ci->dsi = ci->ds->GetDrawingSurfaceInfo(ci->ds);
+
+ // Check DrawingSurfaceInfo. This can be NULL on Mac OS X
+ // if the windowing system is not ready
+ if (ci->dsi != NULL) {
+ // Get the platform-specific drawing info
+ // We will use this to get at Cocoa and CoreGraphics
+ // See <JavaVM/jawt_md.h>
+ ci->dsi_mac = (JAWT_MacOSXDrawingSurfaceInfo*)ci->dsi->platformInfo;
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+
+ // Get the corresponding peer from the caller canvas
+ ci->view = ci->dsi_mac->cocoaViewRef;
+ ci->openGLContext = ensureContext(ci->openGLContext, ci->view);
+
+ return JNI_TRUE;
+ }
+
+ return JNI_FALSE;
+}
+
+JNIEXPORT void JNICALL Java_OpenGLCanvas_endOpenGL(JNIEnv *env, jobject canvas)
+{
+ ContextInfo *ci = getContext(env, canvas);
+
+ [ci->openGLContext flushBuffer];
+
+ // Free the DrawingSurfaceInfo
+ ci->ds->FreeDrawingSurfaceInfo(ci->dsi);
+ if (env->ExceptionOccurred()){
+ env->ExceptionDescribe();
+ }
+
+ // Unlock the drawing surface
+ // You must unlock EACH TIME when done drawing
+ ci->ds->Unlock(ci->ds);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+}
+
+JNIEXPORT void JNICALL Java_OpenGLCanvas_updateOpenGL(JNIEnv *env, jobject canvas)
+{
+ ContextInfo *ci = getContext(env, canvas);
+
+ [ci->openGLContext update];
+}
+
+JNIEXPORT void JNICALL Java_OpenGLCanvas_allocOpenGL(JNIEnv *env, jobject canvas)
+{
+ ContextInfo *ci = getContext(env, canvas);
+
+ jboolean result = JNI_FALSE;
+
+ // get the AWT
+ ci->awt->version = JAWT_VERSION_1_4;
+ result = JAWT_GetAWT(env, ci->awt);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+ assert(result != JNI_FALSE);
+
+ // Get the drawing surface. This can be safely cached.
+ // Anything below the DS (DSI, contexts, etc)
+ // can possibly change/go away and should not be cached.
+ ci->ds = ci->awt->GetDrawingSurface(env, canvas);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+ assert(ci->ds != NULL);
+
+ NSLog(@"Alloc Context %d", ci);
+}
+
+JNIEXPORT void JNICALL Java_OpenGLCanvas_releaseOpenGL(JNIEnv *env, jobject canvas)
+{
+ ContextInfo *ci = getContext(env, canvas);
+ NSLog(@"Release Context %d", ci);
+ if (ci->openGLContext) {
+ if ([ci->openGLContext view] /* == self */) {
+ [ci->openGLContext clearDrawable];
+ }
+ [ci->openGLContext release];
+ }
+
+ // Free the drawing surface (if not caching it)
+ ci->awt->FreeDrawingSurface(ci->ds);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+
+ freeContext(env, canvas, ci);
+}
+#endif \ No newline at end of file
diff --git a/src/powdertoyjava/OpenGLCanvasWin32.cpp b/src/powdertoyjava/OpenGLCanvasWin32.cpp
new file mode 100644
index 0000000..7028d86
--- /dev/null
+++ b/src/powdertoyjava/OpenGLCanvasWin32.cpp
@@ -0,0 +1,169 @@
+#if defined(USE_JNI) && defined(WIN)
+#include "OpenGLCanvasWin32.h"
+
+static jfieldID ctxID = NULL;
+
+int defaultPixelFormat(PIXELFORMATDESCRIPTOR* pfd)
+{
+ ::ZeroMemory( pfd, sizeof( PIXELFORMATDESCRIPTOR ) );
+ pfd->nSize = sizeof( PIXELFORMATDESCRIPTOR );
+ pfd->nVersion = 1;
+ pfd->dwFlags = PFD_DRAW_TO_WINDOW |
+ PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+ pfd->iPixelType = PFD_TYPE_RGBA;
+ pfd->cColorBits = 24;
+ pfd->cDepthBits = 16;
+ pfd->iLayerType = PFD_MAIN_PLANE;
+ return 0;
+}
+
+HGLRC ensureContext(JAWT_Win32DrawingSurfaceInfo* dsi_win, HGLRC hRC) {
+
+ if (!hRC) {
+ int iFormat;
+ PIXELFORMATDESCRIPTOR pfd;
+ defaultPixelFormat(&pfd);
+
+ iFormat = ChoosePixelFormat( dsi_win->hdc, &pfd );
+ SetPixelFormat( dsi_win->hdc, iFormat, &pfd );
+
+ hRC = wglCreateContext( dsi_win->hdc );
+ }
+ if (1 && wglGetCurrentDC() != dsi_win->hdc) {
+ wglMakeCurrent( dsi_win->hdc, hRC );
+ }
+
+ return hRC;
+}
+
+ContextInfo* getContext(JNIEnv *env, jobject canvas)
+{
+ ContextInfo *ci;
+ if (!ctxID) {
+ jclass cls = env->GetObjectClass(canvas);
+ ctxID = env->GetFieldID(cls, "openGLContext", "J");
+ }
+
+ ci = (ContextInfo *)(long)(env->GetLongField(canvas, ctxID));
+
+ if (!ci) {
+ ci = (ContextInfo *)calloc(sizeof(ContextInfo), 1);
+ ci->awt = (JAWT *)calloc(sizeof(JAWT), 1);
+ env->SetLongField(canvas, ctxID, (jlong)(long)ci);
+ }
+
+ return ci;
+}
+
+void freeContext(JNIEnv *env, jobject canvas, ContextInfo* ci)
+{
+ if (ci) {
+ free(ci->awt);
+ free(ci);
+ env->SetLongField(canvas, ctxID, 0L);
+ }
+}
+
+JNIEXPORT jboolean JNICALL Java_OpenGLCanvas_beginOpenGL(JNIEnv *env, jobject canvas)
+{
+ jint lock;
+ ContextInfo *ci = getContext(env, canvas);
+
+ // Get the drawing surface. This can be safely cached -- not in win32
+ // Anything below the DS (DSI, contexts, etc)
+ // can possibly change/go away and should not be cached.
+ ci->ds = ci->awt->GetDrawingSurface(env, canvas);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+ assert(ci->ds != NULL);
+
+ // Lock the drawing surface
+ // You must lock EACH TIME before drawing
+ lock = ci->ds->Lock(ci->ds);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+ assert((lock & JAWT_LOCK_ERROR) == 0);
+
+ // Get the drawing surface info
+ ci->dsi = ci->ds->GetDrawingSurfaceInfo(ci->ds);
+
+ // Check DrawingSurfaceInfo. This can be NULL on Mac OS X
+ // if the windowing system is not ready
+ if (ci->dsi != NULL) {
+ // Get the platform-specific drawing info
+ // We will use this to get at Cocoa and CoreGraphics
+ // See <JavaVM/jawt_md.h>
+ ci->dsi_win = (JAWT_Win32DrawingSurfaceInfo*)ci->dsi->platformInfo;
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+
+ // Get the corresponding peer from the caller canvas
+ ci->hRC = ensureContext(ci->dsi_win, ci->hRC);
+
+ return JNI_TRUE;
+ }
+
+ return JNI_FALSE;
+}
+
+JNIEXPORT void JNICALL Java_OpenGLCanvas_endOpenGL(JNIEnv *env, jobject canvas)
+{
+ ContextInfo *ci = getContext(env, canvas);
+
+ SwapBuffers( ci->dsi_win->hdc );
+
+ // Free the DrawingSurfaceInfo
+ ci->ds->FreeDrawingSurfaceInfo(ci->dsi);
+ if (env->ExceptionOccurred()){
+ env->ExceptionDescribe();
+ }
+
+ // Unlock the drawing surface
+ // You must unlock EACH TIME when done drawing
+ ci->ds->Unlock(ci->ds);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+
+ // Free the drawing surface (if not caching it)
+ ci->awt->FreeDrawingSurface(ci->ds);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+}
+
+JNIEXPORT void JNICALL Java_OpenGLCanvas_updateOpenGL(JNIEnv *env, jobject canvas)
+{
+ ContextInfo *ci = getContext(env, canvas);
+
+ wglMakeCurrent( ci->dsi_win->hdc, ci->hRC );
+}
+
+JNIEXPORT void JNICALL Java_OpenGLCanvas_allocOpenGL(JNIEnv *env, jobject canvas)
+{
+ ContextInfo *ci = getContext(env, canvas);
+
+ jboolean result = JNI_FALSE;
+
+ // get the AWT
+ ci->awt->version = JAWT_VERSION_1_4;
+ result = JAWT_GetAWT(env, ci->awt);
+ if (env->ExceptionOccurred()) {
+ env->ExceptionDescribe();
+ }
+ assert(result != JNI_FALSE);
+}
+
+JNIEXPORT void JNICALL Java_OpenGLCanvas_releaseOpenGL(JNIEnv *env, jobject canvas)
+{
+ ContextInfo *ci = getContext(env, canvas);
+ if (ci->hRC) {
+ wglDeleteContext(ci->hRC);
+ }
+
+ freeContext(env, canvas, ci);
+}
+#endif
diff --git a/src/powdertoyjava/OpenGLCanvasWin32.h b/src/powdertoyjava/OpenGLCanvasWin32.h
new file mode 100644
index 0000000..bff6b20
--- /dev/null
+++ b/src/powdertoyjava/OpenGLCanvasWin32.h
@@ -0,0 +1,35 @@
+#ifdef USE_JNI
+#import <jawt_md.h>
+
+#include <windows.h>
+#include <assert.h>
+#include <gl/gl.h>
+
+int defaultPixelFormat(PIXELFORMATDESCRIPTOR* pfd);
+
+HGLRC ensureContext(JAWT_Win32DrawingSurfaceInfo* dsi_win, HGLRC hRC);
+
+typedef struct {
+ JAWT* awt;
+ JAWT_DrawingSurface* ds;
+ JAWT_DrawingSurfaceInfo* dsi;
+ JAWT_Win32DrawingSurfaceInfo* dsi_win;
+ HGLRC hRC;
+} ContextInfo;
+
+ContextInfo* getContext(JNIEnv *env, jobject canvas);
+
+void freeContext(JNIEnv *env, jobject canvas, ContextInfo* ci);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ JNIEXPORT jboolean JNICALL Java_OpenGLCanvas_beginOpenGL(JNIEnv *env, jobject canvas);
+ JNIEXPORT void JNICALL Java_OpenGLCanvas_endOpenGL(JNIEnv *env, jobject canvas);
+ JNIEXPORT void JNICALL Java_OpenGLCanvas_updateOpenGL(JNIEnv *env, jobject canvas);
+ JNIEXPORT void JNICALL Java_OpenGLCanvas_allocOpenGL(JNIEnv *env, jobject canvas);
+ JNIEXPORT void JNICALL Java_OpenGLCanvas_releaseOpenGL(JNIEnv *env, jobject canvas);
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/powdertoyjava/PowderToyJava.cpp b/src/powdertoyjava/PowderToyJava.cpp
new file mode 100644
index 0000000..835fdc4
--- /dev/null
+++ b/src/powdertoyjava/PowderToyJava.cpp
@@ -0,0 +1,77 @@
+#if defined(USE_JNI)
+
+#include <time.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "Config.h"
+#include "PowderToyJava.h"
+#include "graphics/Graphics.h"
+
+#include "game/GameController.h"
+
+using namespace std;
+
+GameController * gameController;
+ui::Engine * engine;
+
+int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0;
+float fps = 0, delta = 1.0f;
+
+JNIEXPORT void JNICALL Java_PowderToy_initialise(JNIEnv * env, jobject canvas)
+{
+ ui::Engine::Ref().g = new Graphics();
+
+ engine = &ui::Engine::Ref();
+ engine->Begin(XRES+BARSIZE, YRES+MENUSIZE);
+
+ gameController = new GameController();
+ engine->ShowWindow(gameController->GetView());
+ engine->SetFps(fps);
+}
+
+JNIEXPORT void JNICALL Java_PowderToy_tick(JNIEnv * env, jobject canvas)
+{
+ engine->Tick();
+}
+
+JNIEXPORT void JNICALL Java_PowderToy_draw(JNIEnv * env, jobject canvas)
+{
+ engine->Draw();
+ engine->g->Finalise();
+}
+
+JNIEXPORT void JNICALL Java_PowderToy_finish(JNIEnv * env, jobject canvas)
+{
+ ui::Engine::Ref().CloseWindow();
+ delete gameController;
+ delete ui::Engine::Ref().g;
+}
+
+JNIEXPORT jint JNICALL Java_PowderToy_getWidth(JNIEnv * env, jobject canvas)
+{
+ return XRES+BARSIZE;
+}
+
+JNIEXPORT jint JNICALL Java_PowderToy_getHeight(JNIEnv * env, jobject canvas)
+{
+ return YRES+MENUSIZE;
+}
+
+JNIEXPORT void JNICALL Java_PowderToy_mousePressed(JNIEnv * env, jobject canvas, jint mouseX, jint mouseY, jint mouseButton)
+{
+ engine->onMouseClick(mouseX, mouseY, mouseButton);
+}
+
+JNIEXPORT void JNICALL Java_PowderToy_mouseReleased(JNIEnv * env, jobject canvas, jint mouseX, jint mouseY, jint mouseButton)
+{
+ engine->onMouseUnclick(mouseX, mouseY, mouseButton);
+}
+
+JNIEXPORT void JNICALL Java_PowderToy_mouseMoved(JNIEnv * env, jobject canvas, jint mouseX, jint mouseY)
+{
+ engine->onMouseMove(mouseX, mouseY);
+}
+
+#endif
diff --git a/src/powdertoyjava/PowderToyJava.h b/src/powdertoyjava/PowderToyJava.h
new file mode 100644
index 0000000..d39a8a9
--- /dev/null
+++ b/src/powdertoyjava/PowderToyJava.h
@@ -0,0 +1,21 @@
+#include <jni.h>
+
+#ifndef POWDERTOYJAVA
+#define POWDERTOYJAVA
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ JNIEXPORT void JNICALL Java_PowderToy_initialise(JNIEnv *, jobject);
+ JNIEXPORT void JNICALL Java_PowderToy_tick(JNIEnv *, jobject);
+ JNIEXPORT void JNICALL Java_PowderToy_draw(JNIEnv *, jobject);
+ JNIEXPORT void JNICALL Java_PowderToy_finish(JNIEnv *, jobject);
+ JNIEXPORT jint JNICALL Java_PowderToy_getWidth(JNIEnv * env, jobject canvas);
+ JNIEXPORT jint JNICALL Java_PowderToy_getHeight(JNIEnv * env, jobject canvas);
+ JNIEXPORT void JNICALL Java_PowderToy_mousePressed(JNIEnv * env, jobject canvas, jint mouseX, jint mouseY, jint mouseButton);
+ JNIEXPORT void JNICALL Java_PowderToy_mouseReleased(JNIEnv * env, jobject canvas, jint mouseX, jint mouseY, jint mouseButton);
+ JNIEXPORT void JNICALL Java_PowderToy_mouseMoved(JNIEnv * env, jobject canvas, jint mouseX, jint mouseY);
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/preview/Comment.h b/src/preview/Comment.h
new file mode 100644
index 0000000..bf4b8d4
--- /dev/null
+++ b/src/preview/Comment.h
@@ -0,0 +1,34 @@
+/*
+ * Comment.h
+ *
+ * Created on: Feb 11, 2012
+ * Author: Simon
+ */
+
+#ifndef COMMENT_H_
+#define COMMENT_H_
+
+#include <string>
+
+class SaveComment
+{
+public:
+ int authorID;
+ std::string authorName;
+ std::string comment;
+ SaveComment(int userID, std::string username, std::string commentText):
+ authorID(userID), authorName(username), comment(commentText)
+ {
+ }
+ SaveComment(const SaveComment & comment):
+ authorID(comment.authorID), authorName(comment.authorName), comment(comment.comment)
+ {
+ }
+ SaveComment(const SaveComment * comment):
+ authorID(comment->authorID), authorName(comment->authorName), comment(comment->comment)
+ {
+ }
+};
+
+
+#endif /* COMMENT_H_ */
diff --git a/src/preview/PreviewController.cpp b/src/preview/PreviewController.cpp
new file mode 100644
index 0000000..89e3ca6
--- /dev/null
+++ b/src/preview/PreviewController.cpp
@@ -0,0 +1,196 @@
+/*
+ * PreviewController.cpp
+ *
+ * Created on: Jan 21, 2012
+ * Author: Simon
+ */
+
+#include <sstream>
+#include "client/Client.h"
+#include "PreviewController.h"
+#include "PreviewView.h"
+#include "PreviewModel.h"
+#include "PreviewModelException.h"
+#include "dialogues/ErrorMessage.h"
+#include "login/LoginController.h"
+#include "Controller.h"
+
+PreviewController::PreviewController(int saveID, int saveDate, ControllerCallback * callback):
+ HasExited(false),
+ saveId(saveID),
+ saveDate(saveDate),
+ loginWindow(NULL)
+{
+ previewModel = new PreviewModel();
+ previewView = new PreviewView();
+ previewModel->AddObserver(previewView);
+ previewView->AttachController(this);
+
+ previewModel->UpdateSave(saveID, saveDate);
+
+ if(Client::Ref().GetAuthUser().ID)
+ {
+ previewModel->SetCommentBoxEnabled(true);
+ }
+
+ Client::Ref().AddListener(this);
+
+ this->callback = callback;
+}
+
+PreviewController::PreviewController(int saveID, ControllerCallback * callback):
+ HasExited(false),
+ saveId(saveID),
+ saveDate(0),
+ loginWindow(NULL)
+{
+ previewModel = new PreviewModel();
+ previewView = new PreviewView();
+ previewModel->AddObserver(previewView);
+ previewView->AttachController(this);
+
+ previewModel->UpdateSave(saveID, 0);
+
+ if(Client::Ref().GetAuthUser().ID)
+ {
+ previewModel->SetCommentBoxEnabled(true);
+ }
+
+ Client::Ref().AddListener(this);
+
+ this->callback = callback;
+}
+
+void PreviewController::Update()
+{
+ if(loginWindow && loginWindow->HasExited == true)
+ {
+ delete loginWindow;
+ loginWindow = NULL;
+ }
+
+ try
+ {
+ previewModel->Update();
+ }
+ catch (PreviewModelException & e)
+ {
+ Exit();
+ new ErrorMessage("Error", e.what());
+ }
+ if(previewModel->GetDoOpen() && previewModel->GetSave() && previewModel->GetSave()->GetGameSave())
+ {
+ Exit();
+ }
+}
+
+void PreviewController::SubmitComment(std::string comment)
+{
+ if(comment.length() < 4)
+ {
+ new ErrorMessage("Error", "Comment is too short");
+ }
+ else
+ {
+ RequestStatus status = Client::Ref().AddComment(saveId, comment);
+ if(status != RequestOkay)
+ {
+ new ErrorMessage("Error Submitting comment", Client::Ref().GetLastError());
+ }
+ else
+ {
+ previewModel->UpdateComments(1);
+ }
+ }
+}
+
+void PreviewController::ShowLogin()
+{
+ loginWindow = new LoginController();
+ ui::Engine::Ref().ShowWindow(loginWindow->GetView());
+}
+
+void PreviewController::NotifyAuthUserChanged(Client * sender)
+{
+ previewModel->SetCommentBoxEnabled(sender->GetAuthUser().ID);
+}
+
+SaveInfo * PreviewController::GetSave()
+{
+ return previewModel->GetSave();
+}
+
+bool PreviewController::GetDoOpen()
+{
+ return previewModel->GetDoOpen();
+}
+
+void PreviewController::DoOpen()
+{
+ previewModel->SetDoOpen(true);
+}
+
+void PreviewController::Report(std::string message)
+{
+ if(Client::Ref().ReportSave(saveId, message) == RequestOkay)
+ {
+ Exit();
+ new ErrorMessage("Information", "Report submitted"); //TODO: InfoMessage
+ }
+ else
+ new ErrorMessage("Error", "Unable file report");
+}
+
+void PreviewController::FavouriteSave()
+{
+ if(previewModel->GetSave() && Client::Ref().GetAuthUser().ID)
+ {
+ if(previewModel->GetSave()->Favourite)
+ previewModel->SetFavourite(false);
+ else
+ previewModel->SetFavourite(true);
+ }
+}
+
+void PreviewController::OpenInBrowser()
+{
+ std::stringstream uriStream;
+ uriStream << "http://" << SERVER << "/Browse/View.html?ID=" << saveId;
+ OpenURI(uriStream.str());
+}
+
+void PreviewController::NextCommentPage()
+{
+ if(previewModel->GetCommentsPageNum() < previewModel->GetCommentsPageCount() && previewModel->GetCommentsLoaded())
+ previewModel->UpdateComments(previewModel->GetCommentsPageNum()+1);
+}
+
+void PreviewController::PrevCommentPage()
+{
+ if(previewModel->GetCommentsPageNum()>1 && previewModel->GetCommentsLoaded())
+ previewModel->UpdateComments(previewModel->GetCommentsPageNum()-1);
+}
+
+void PreviewController::Exit()
+{
+ if(ui::Engine::Ref().GetWindow() == previewView)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ HasExited = true;
+ if(callback)
+ callback->ControllerExit();
+}
+
+PreviewController::~PreviewController() {
+ if(ui::Engine::Ref().GetWindow() == previewView)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ Client::Ref().RemoveListener(this);
+ delete previewModel;
+ delete previewView;
+ if(callback)
+ delete callback;
+}
+
diff --git a/src/preview/PreviewController.h b/src/preview/PreviewController.h
new file mode 100644
index 0000000..c8c3f8e
--- /dev/null
+++ b/src/preview/PreviewController.h
@@ -0,0 +1,52 @@
+/*
+ * PreviewController.h
+ *
+ * Created on: Jan 21, 2012
+ * Author: Simon
+ */
+
+#ifndef PREVIEWCONTROLLER_H_
+#define PREVIEWCONTROLLER_H_
+
+#include "preview/PreviewModel.h"
+#include "preview/PreviewView.h"
+#include "Controller.h"
+#include "client/SaveInfo.h"
+#include "client/ClientListener.h"
+
+class LoginController;
+class PreviewModel;
+class PreviewView;
+class PreviewController: public ClientListener {
+ int saveId;
+ int saveDate;
+ PreviewModel * previewModel;
+ PreviewView * previewView;
+ LoginController * loginWindow;
+ ControllerCallback * callback;
+public:
+ virtual void NotifyAuthUserChanged(Client * sender);
+ inline int SaveID() { return saveId; };
+
+ bool HasExited;
+ PreviewController(int saveID, ControllerCallback * callback);
+ PreviewController(int saveID, int saveDate, ControllerCallback * callback);
+ void Exit();
+ void DoOpen();
+ void OpenInBrowser();
+ void Report(std::string message);
+ void ShowLogin();
+ bool GetDoOpen();
+ SaveInfo * GetSave();
+ PreviewView * GetView() { return previewView; }
+ void Update();
+ void FavouriteSave();
+ void SubmitComment(std::string comment);
+
+ void NextCommentPage();
+ void PrevCommentPage();
+
+ virtual ~PreviewController();
+};
+
+#endif /* PREVIEWCONTROLLER_H_ */
diff --git a/src/preview/PreviewModel.cpp b/src/preview/PreviewModel.cpp
new file mode 100644
index 0000000..20f6bfd
--- /dev/null
+++ b/src/preview/PreviewModel.cpp
@@ -0,0 +1,320 @@
+/*
+ * PreviewModel.cpp
+ *
+ * Created on: Jan 21, 2012
+ * Author: Simon
+ */
+
+#include <cmath>
+#include "PreviewModel.h"
+#include "client/Client.h"
+#include "client/GameSave.h";
+#include "PreviewModelException.h"
+
+PreviewModel::PreviewModel():
+ save(NULL),
+ saveComments(NULL),
+ doOpen(false),
+ updateSaveDataWorking(false),
+ updateSaveDataFinished(false),
+ updateSaveInfoWorking(false),
+ updateSaveInfoFinished(false),
+ updateSaveCommentsWorking(false),
+ updateSaveCommentsFinished(false),
+ commentsTotal(0),
+ commentsPageNumber(1),
+ commentBoxEnabled(false)
+{
+ // TODO Auto-generated constructor stub
+
+}
+
+void * PreviewModel::updateSaveInfoTHelper(void * obj)
+{
+ return ((PreviewModel*)obj)->updateSaveInfoT();
+}
+
+void * PreviewModel::updateSaveDataTHelper(void * obj)
+{
+ return ((PreviewModel*)obj)->updateSaveDataT();
+}
+
+void * PreviewModel::updateSaveCommentsTHelper(void * obj)
+{
+ return ((PreviewModel*)obj)->updateSaveCommentsT();
+}
+
+void * PreviewModel::updateSaveInfoT()
+{
+ SaveInfo * tempSave = Client::Ref().GetSave(tSaveID, tSaveDate);
+ updateSaveInfoFinished = true;
+ return tempSave;
+}
+
+void * PreviewModel::updateSaveDataT()
+{
+ int tempDataSize;
+ unsigned char * tempData = Client::Ref().GetSaveData(tSaveID, tSaveDate, tempDataSize);
+ saveDataBuffer.clear();
+ saveDataBuffer.insert(saveDataBuffer.begin(), tempData, tempData+tempDataSize);
+ updateSaveDataFinished = true;
+ return NULL;
+}
+
+void * PreviewModel::updateSaveCommentsT()
+{
+ //Haha, j/k
+ std::vector<SaveComment*> * tempComments = Client::Ref().GetComments(tSaveID, (commentsPageNumber-1)*20, 20);
+ updateSaveCommentsFinished = true;
+ return tempComments;
+}
+
+void PreviewModel::SetFavourite(bool favourite)
+{
+ if(save)
+ {
+ Client::Ref().FavouriteSave(save->id, favourite);
+ save->Favourite = favourite;
+ notifySaveChanged();
+ }
+}
+
+bool PreviewModel::GetCommentBoxEnabled()
+{
+ return commentBoxEnabled;
+}
+
+void PreviewModel::SetCommentBoxEnabled(bool enabledState)
+{
+ if(enabledState != commentBoxEnabled)
+ {
+ commentBoxEnabled = enabledState;
+ notifyCommentBoxEnabledChanged();
+ }
+}
+
+void PreviewModel::UpdateSave(int saveID, int saveDate)
+{
+ this->tSaveID = saveID;
+ this->tSaveDate = saveDate;
+
+ if(save)
+ {
+ delete save;
+ save = NULL;
+ }
+ saveDataBuffer.clear();
+ if(saveComments)
+ {
+ for(int i = 0; i < saveComments->size(); i++)
+ delete saveComments->at(i);
+ delete saveComments;
+ saveComments = NULL;
+ }
+ notifySaveChanged();
+ notifySaveCommentsChanged();
+
+ if(!updateSaveDataWorking)
+ {
+ updateSaveDataWorking = true;
+ updateSaveDataFinished = false;
+ pthread_create(&updateSaveDataThread, 0, &PreviewModel::updateSaveDataTHelper, this);
+ }
+
+ if(!updateSaveInfoWorking)
+ {
+ updateSaveInfoWorking = true;
+ updateSaveInfoFinished = false;
+ pthread_create(&updateSaveInfoThread, 0, &PreviewModel::updateSaveInfoTHelper, this);
+ }
+
+ if(!updateSaveCommentsWorking)
+ {
+ commentsLoaded = false;
+ updateSaveCommentsWorking = true;
+ updateSaveCommentsFinished = false;
+ pthread_create(&updateSaveCommentsThread, 0, &PreviewModel::updateSaveCommentsTHelper, this);
+ }
+}
+
+void PreviewModel::SetDoOpen(bool doOpen)
+{
+ this->doOpen = doOpen;
+}
+
+bool PreviewModel::GetDoOpen()
+{
+ return doOpen;
+}
+
+SaveInfo * PreviewModel::GetSave()
+{
+ return save;
+}
+
+int PreviewModel::GetCommentsPageNum()
+{
+ return commentsPageNumber;
+}
+
+int PreviewModel::GetCommentsPageCount()
+{
+ return max(1, (int)(ceil(commentsTotal/20.0f)));
+}
+
+bool PreviewModel::GetCommentsLoaded()
+{
+ return commentsLoaded;
+}
+
+void PreviewModel::UpdateComments(int pageNumber)
+{
+ commentsLoaded = false;
+ if(saveComments)
+ {
+ for(int i = 0; i < saveComments->size(); i++)
+ delete saveComments->at(i);
+ delete saveComments;
+ saveComments = NULL;
+ }
+
+ //resultCount = 0;
+ commentsPageNumber = pageNumber;
+ notifySaveCommentsChanged();
+ notifyCommentsPageChanged();
+
+ //Threading
+ if(!updateSaveCommentsWorking)
+ {
+ updateSaveCommentsFinished = false;
+ updateSaveCommentsWorking = true;
+ pthread_create(&updateSaveCommentsThread, 0, &PreviewModel::updateSaveCommentsTHelper, this);
+ }
+}
+
+std::vector<SaveComment*> * PreviewModel::GetComments()
+{
+ return saveComments;
+}
+
+void PreviewModel::notifySaveChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifySaveChanged(this);
+ }
+}
+
+void PreviewModel::notifyCommentBoxEnabledChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyCommentBoxEnabledChanged(this);
+ }
+}
+
+void PreviewModel::notifyCommentsPageChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyCommentsPageChanged(this);
+ }
+}
+
+void PreviewModel::notifySaveCommentsChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyCommentsChanged(this);
+ }
+}
+
+void PreviewModel::AddObserver(PreviewView * observer) {
+ observers.push_back(observer);
+ observer->NotifySaveChanged(this);
+ observer->NotifyCommentsChanged(this);
+ observer->NotifyCommentsPageChanged(this);
+ observer->NotifyCommentBoxEnabledChanged(this);
+}
+
+void PreviewModel::Update()
+{
+ if(updateSaveDataWorking)
+ {
+ if(updateSaveDataFinished)
+ {
+ updateSaveDataWorking = false;
+ pthread_join(updateSaveDataThread, NULL);
+
+ if(updateSaveInfoFinished && save)
+ {
+ commentsTotal = save->Comments;
+ try
+ {
+ save->SetGameSave(new GameSave(&saveDataBuffer[0], saveDataBuffer.size()));
+ }
+ catch(ParseException &e)
+ {
+ throw PreviewModelException("Save file corrupt or from newer version");
+ }
+ notifySaveChanged();
+ notifyCommentsPageChanged();
+ }
+ }
+ }
+
+ if(updateSaveInfoWorking)
+ {
+ if(updateSaveInfoFinished)
+ {
+ if(save)
+ {
+ delete save;
+ save = NULL;
+ }
+ updateSaveInfoWorking = false;
+ pthread_join(updateSaveInfoThread, (void**)(&save));
+ if(updateSaveDataFinished && save)
+ {
+ commentsTotal = save->Comments;
+ try
+ {
+ save->SetGameSave(new GameSave(&saveDataBuffer[0], saveDataBuffer.size()));
+ }
+ catch(ParseException &e)
+ {
+ throw PreviewModelException("Save file corrupt or from newer version");
+ }
+ notifyCommentsPageChanged();
+ }
+ notifySaveChanged();
+
+ if(!save)
+ throw PreviewModelException("Unable to load save");
+ }
+ }
+
+ if(updateSaveCommentsWorking)
+ {
+ if(updateSaveCommentsFinished)
+ {
+ if(saveComments)
+ {
+ for(int i = 0; i < saveComments->size(); i++)
+ delete saveComments->at(i);
+ delete saveComments;
+ saveComments = NULL;
+ }
+ commentsLoaded = true;
+ updateSaveCommentsWorking = false;
+ pthread_join(updateSaveCommentsThread, (void**)(&saveComments));
+ notifySaveCommentsChanged();
+ }
+ }
+}
+
+PreviewModel::~PreviewModel() {
+ if(save)
+ delete save;
+}
+
diff --git a/src/preview/PreviewModel.h b/src/preview/PreviewModel.h
new file mode 100644
index 0000000..77cab37
--- /dev/null
+++ b/src/preview/PreviewModel.h
@@ -0,0 +1,89 @@
+/*
+ * PreviewModel.h
+ *
+ * Created on: Jan 21, 2012
+ * Author: Simon
+ */
+
+#ifndef PREVIEWMODEL_H_
+#define PREVIEWMODEL_H_
+
+#include <vector>
+#include <iostream>
+#include <pthread.h>
+ #undef GetUserName //God dammit microsoft!
+#include "PreviewView.h"
+#include "client/SaveInfo.h"
+#include "preview/Comment.h"
+#include "search/Thumbnail.h"
+
+using namespace std;
+
+struct SaveData
+{
+ unsigned char * data;
+ int length;
+};
+
+class PreviewView;
+class PreviewModel {
+ bool doOpen;
+ bool commentBoxEnabled;
+ vector<PreviewView*> observers;
+ SaveInfo * save;
+ vector<char> saveDataBuffer;
+ std::vector<SaveComment*> * saveComments;
+ void notifySaveChanged();
+ void notifySaveCommentsChanged();
+ void notifyCommentsPageChanged();
+ void notifyCommentBoxEnabledChanged();
+
+ //Background retrieval
+ int tSaveID;
+ int tSaveDate;
+
+ //
+ bool commentsLoaded;
+ int commentsTotal;
+ int commentsPageNumber;
+
+ bool updateSaveDataWorking;
+ volatile bool updateSaveDataFinished;
+ pthread_t updateSaveDataThread;
+ static void * updateSaveDataTHelper(void * obj);
+ void * updateSaveDataT();
+
+ bool updateSaveInfoWorking;
+ volatile bool updateSaveInfoFinished;
+ pthread_t updateSaveInfoThread;
+ static void * updateSaveInfoTHelper(void * obj);
+ void * updateSaveInfoT();
+
+ bool updateSaveCommentsWorking;
+ volatile bool updateSaveCommentsFinished;
+ pthread_t updateSaveCommentsThread;
+ static void * updateSaveCommentsTHelper(void * obj);
+ void * updateSaveCommentsT();
+public:
+ PreviewModel();
+ SaveInfo * GetSave();
+ std::vector<SaveComment*> * GetComments();
+
+ bool GetCommentBoxEnabled();
+ void SetCommentBoxEnabled(bool enabledState);
+
+ bool GetCommentsLoaded();
+ int GetCommentsPageNum();
+ int GetCommentsPageCount();
+ void UpdateComments(int pageNumber);
+
+ void AddObserver(PreviewView * observer);
+ void UpdateSave(int saveID, int saveDate);
+ void SetFavourite(bool favourite);
+ bool GetDoOpen();
+ void SetDoOpen(bool doOpen);
+ void Update();
+ virtual ~PreviewModel();
+};
+
+#endif /* PREVIEWMODEL_H_ */
diff --git a/src/preview/PreviewModelException.h b/src/preview/PreviewModelException.h
new file mode 100644
index 0000000..261d203
--- /dev/null
+++ b/src/preview/PreviewModelException.h
@@ -0,0 +1,26 @@
+/*
+ * PreviewModelException.h
+ *
+ * Created on: Apr 14, 2012
+ * Author: Simon
+ */
+
+#ifndef PREVIEWMODELEXCEPTION_H_
+#define PREVIEWMODELEXCEPTION_H_
+
+#include <string>
+#include <exception>
+using namespace std;
+
+struct PreviewModelException: public exception {
+ string message;
+public:
+ PreviewModelException(string message_): message(message_) {}
+ const char * what() const throw()
+ {
+ return message.c_str();
+ }
+ ~PreviewModelException() throw() {};
+};
+
+#endif /* PREVIEWMODELEXCEPTION_H_ */
diff --git a/src/preview/PreviewView.cpp b/src/preview/PreviewView.cpp
new file mode 100644
index 0000000..445142a
--- /dev/null
+++ b/src/preview/PreviewView.cpp
@@ -0,0 +1,560 @@
+/*
+ * PreviewView.cpp
+ *
+ * Created on: Jan 21, 2012
+ * Author: Simon
+ */
+
+#include <sstream>
+#include <vector>
+#include <cmath>
+#include "PreviewView.h"
+#include "dialogues/TextPrompt.h"
+#include "simulation/SaveRenderer.h"
+#include "interface/Point.h"
+#include "interface/Window.h"
+#include "interface/Textbox.h"
+#include "Style.h"
+#include "Format.h"
+#include "search/Thumbnail.h"
+#include "client/Client.h"
+#include "interface/ScrollPanel.h"
+
+class PreviewView::LoginAction: public ui::ButtonAction
+{
+ PreviewView * v;
+public:
+ LoginAction(PreviewView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ v->c->ShowLogin();
+ }
+};
+
+class PreviewView::SubmitCommentAction: public ui::ButtonAction
+{
+ PreviewView * v;
+public:
+ SubmitCommentAction(PreviewView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ v->submitComment();
+ }
+};
+
+class PreviewView::AutoCommentSizeAction: public ui::TextboxAction
+{
+ PreviewView * v;
+public:
+ AutoCommentSizeAction(PreviewView * v): v(v) {}
+ virtual void TextChangedCallback(ui::Textbox * sender) {
+ v->commentBoxAutoHeight();
+ }
+};
+
+PreviewView::PreviewView():
+ ui::Window(ui::Point(-1, -1), ui::Point((XRES/2)+200, (YRES/2)+150)),
+ savePreview(NULL),
+ doOpen(false),
+ addCommentBox(NULL),
+ submitCommentButton(NULL),
+ commentBoxHeight(20)
+{
+ class FavAction: public ui::ButtonAction
+ {
+ PreviewView * v;
+ public:
+ FavAction(PreviewView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ v->c->FavouriteSave();
+ }
+ };
+
+ favButton = new ui::Button(ui::Point(50, Size.Y-19), ui::Point(51, 19), "Fav");
+ favButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; favButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ favButton->SetIcon(IconFavourite);
+ favButton->SetActionCallback(new FavAction(this));
+ favButton->Enabled = Client::Ref().GetAuthUser().ID?true:false;
+ AddComponent(favButton);
+
+ class ReportPromptCallback: public TextDialogueCallback {
+ public:
+ PreviewView * v;
+ ReportPromptCallback(PreviewView * v_) { v = v_; }
+ virtual void TextCallback(TextPrompt::DialogueResult result, std::string resultText) {
+ if (result == TextPrompt::ResultOkay)
+ v->c->Report(resultText);
+ }
+ virtual ~ReportPromptCallback() { }
+ };
+
+ class ReportAction: public ui::ButtonAction
+ {
+ PreviewView * v;
+ public:
+ ReportAction(PreviewView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ new TextPrompt("Report Save", "Things to consider when reporting:\n\bw1)\bg When reporting stolen saves, please include the ID of the original save.\n\bw2)\bg Do not waste staff time with fake or bogus reports, doing so may result in a ban.", "", "[reason]", true, new ReportPromptCallback(v));
+ }
+ };
+ reportButton = new ui::Button(ui::Point(100, Size.Y-19), ui::Point(51, 19), "Report");
+ reportButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; reportButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ reportButton->SetIcon(IconReport);
+ reportButton->SetActionCallback(new ReportAction(this));
+ reportButton->Enabled = Client::Ref().GetAuthUser().ID?true:false;
+ AddComponent(reportButton);
+
+ class OpenAction: public ui::ButtonAction
+ {
+ PreviewView * v;
+ public:
+ OpenAction(PreviewView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ v->c->DoOpen();
+ }
+ };
+ openButton = new ui::Button(ui::Point(0, Size.Y-19), ui::Point(51, 19), "Open");
+ openButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; openButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ openButton->SetIcon(IconOpen);
+ openButton->SetActionCallback(new OpenAction(this));
+ AddComponent(openButton);
+ SetOkayButton(openButton);
+
+ class BrowserOpenAction: public ui::ButtonAction
+ {
+ PreviewView * v;
+ public:
+ BrowserOpenAction(PreviewView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ v->c->OpenInBrowser();
+ }
+ };
+
+ browserOpenButton = new ui::Button(ui::Point((XRES/2)-107, Size.Y-19), ui::Point(108, 19), "Open in browser");
+ browserOpenButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; browserOpenButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ browserOpenButton->SetIcon(IconOpen);
+ browserOpenButton->SetActionCallback(new BrowserOpenAction(this));
+ AddComponent(browserOpenButton);
+
+ saveNameLabel = new ui::Label(ui::Point(5, (YRES/2)+4), ui::Point(100, 16), "");
+ saveNameLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ saveNameLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(saveNameLabel);
+
+ saveDescriptionLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15+17), ui::Point((XRES/2)-10, Size.Y-((YRES/2)+4+15+17)-21), "");
+ saveDescriptionLabel->SetMultiline(true);
+ saveDescriptionLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ saveDescriptionLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+ saveDescriptionLabel->SetTextColour(ui::Colour(180, 180, 180));
+ AddComponent(saveDescriptionLabel);
+
+ authorDateLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15), ui::Point(200, 16), "");
+ authorDateLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ authorDateLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(authorDateLabel);
+
+ viewsLabel = new ui::Label(ui::Point((XRES/2)-80, (YRES/2)+4+15), ui::Point(80, 16), "");
+ viewsLabel->Appearance.HorizontalAlign = ui::Appearance::AlignRight;
+ viewsLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(viewsLabel);
+
+
+ pageInfo = new ui::Label(ui::Point((XRES/2) + 5, Size.Y+1), ui::Point(Size.X-((XRES/2) + 10), 15), "Page 1 of 1");
+ pageInfo->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; authorDateLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+
+ saveIDTextbox = new ui::Textbox(ui::Point((XRES/2)-55, Size.Y-40), ui::Point(50, 16), "0000000");
+ saveIDTextbox->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
+ saveIDTextbox->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ saveIDTextbox->ReadOnly = true;
+ AddComponent(saveIDTextbox);
+
+ class CopyIDAction: public ui::ButtonAction
+ {
+ PreviewView * v;
+ public:
+ CopyIDAction(PreviewView * v_){ v = v_; }
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ clipboard_push_text((char*)v->saveIDTextbox->GetText().c_str());
+ }
+ };
+
+ ui::Button * tempButton = new ui::Button(ui::Point((XRES/2)-130, Size.Y-40), ui::Point(70, 16), "Copy Save ID");
+ tempButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
+ tempButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ tempButton->SetActionCallback(new CopyIDAction(this));
+ AddComponent(tempButton);
+
+ commentsPanel = new ui::ScrollPanel(ui::Point((XRES/2)+1, 1), ui::Point((Size.X-(XRES/2))-2, Size.Y-commentBoxHeight));
+ AddComponent(commentsPanel);
+
+ AddComponent(pageInfo);
+}
+
+void PreviewView::AttachController(PreviewController * controller)
+{
+ c = controller;
+ saveIDTextbox->SetText(format::NumberToString<int>(c->SaveID()));
+}
+
+void PreviewView::commentBoxAutoHeight()
+{
+ if(!addCommentBox)
+ return;
+ int textWidth = Graphics::textwidth(addCommentBox->GetText().c_str());
+ if(textWidth+15 > Size.X-(XRES/2)-48)
+ {
+ addCommentBox->SetMultiline(true);
+ addCommentBox->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+
+ int oldSize = addCommentBox->Size.Y;
+ addCommentBox->AutoHeight();
+ int newSize = addCommentBox->Size.Y+5;
+ addCommentBox->Size.Y = oldSize;
+
+ commentBoxHeight = newSize+22;
+ commentBoxPositionX = (XRES/2)+4;
+ commentBoxPositionY = Size.Y-(newSize+21);
+ commentBoxSizeX = Size.X-(XRES/2)-8;
+ commentBoxSizeY = newSize;
+ }
+ else
+ {
+ commentBoxHeight = 20;
+ addCommentBox->SetMultiline(false);
+ addCommentBox->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+
+ commentBoxPositionX = (XRES/2)+4;
+ commentBoxPositionY = Size.Y-19;
+ commentBoxSizeX = Size.X-(XRES/2)-48;
+ commentBoxSizeY = 17;
+ }
+ commentsPanel->Size.Y = Size.Y-commentBoxHeight;
+}
+
+void PreviewView::DoDraw()
+{
+ Window::DoDraw();
+ Graphics * g = ui::Engine::Ref().g;
+ for(int i = 0; i < commentTextComponents.size(); i++)
+ {
+ int linePos = commentTextComponents[i]->Position.Y+commentsPanel->ViewportPosition.Y+commentTextComponents[i]->Size.Y+4;
+ if(linePos > 0 && linePos < Size.Y-commentBoxHeight)
+ g->draw_line(
+ Position.X+1+XRES/2,
+ Position.Y+linePos,
+ Position.X+Size.X-2,
+ Position.Y+linePos,
+ 255, 255, 255, 100);
+ }
+ if(c->GetDoOpen())
+ {
+ g->fillrect(Position.X+(Size.X/2)-101, Position.Y+(Size.Y/2)-26, 202, 52, 0, 0, 0, 210);
+ g->drawrect(Position.X+(Size.X/2)-100, Position.Y+(Size.Y/2)-25, 200, 50, 255, 255, 255, 180);
+ g->drawtext(Position.X+(Size.X/2)-(Graphics::textwidth("Loading save...")/2), Position.Y+(Size.Y/2)-5, "Loading save...", style::Colour::InformationTitle.Red, style::Colour::InformationTitle.Green, style::Colour::InformationTitle.Blue, 255);
+ }
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
+
+}
+
+void PreviewView::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+
+ //Window Background+Outline
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+4, Size.Y+4);
+
+ //Save preview (top-left)
+ if(savePreview && savePreview->Data)
+ {
+ g->draw_image(savePreview->Data, (Position.X+1)+(((XRES/2)-savePreview->Size.X)/2), (Position.Y+1)+(((YRES/2)-savePreview->Size.Y)/2), savePreview->Size.X, savePreview->Size.Y, 255);
+ }
+ g->drawrect(Position.X, Position.Y, (XRES/2)+1, (YRES/2)+1, 255, 255, 255, 100);
+ g->draw_line(Position.X+XRES/2, Position.Y+1, Position.X+XRES/2, Position.Y+Size.Y-2, 200, 200, 200, 255);
+
+ if(!(!votesUp && !votesDown))
+ {
+ float ryf;
+ int nyu, nyd;
+ int lv = (votesUp>votesDown)?votesUp:votesDown;
+ lv = (lv>10)?lv:10;
+
+ if (50>lv)
+ {
+ ryf = 50.0f/((float)lv);
+ nyu = votesUp*ryf;
+ nyd = votesDown*ryf;
+ }
+ else
+ {
+ ryf = ((float)lv)/50.0f;
+ nyu = votesUp/ryf;
+ nyd = votesDown/ryf;
+ }
+ nyu = nyu>50?50:nyu;
+ nyd = nyd>50?50:nyd;
+
+ g->fillrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+3, 53, 7, 0, 107, 10, 255);
+ g->fillrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+9, 53, 7, 107, 10, 0, 255);
+ g->drawrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+3, 53, 7, 128, 128, 128, 255);
+ g->drawrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+9, 53, 7, 128, 128, 128, 255);
+
+ g->fillrect(Position.X+(XRES/2)-4-nyu, Position.Y+(YRES/2)+5, nyu, 3, 57, 187, 57, 255);
+ g->fillrect(Position.X+(XRES/2)-4-nyd, Position.Y+(YRES/2)+11, nyd, 3, 187, 57, 57, 255);
+ }
+}
+
+void PreviewView::OnTick(float dt)
+{
+ if(addCommentBox)
+ {
+ ui::Point positionDiff = ui::Point(commentBoxPositionX, commentBoxPositionY)-addCommentBox->Position;
+ ui::Point sizeDiff = ui::Point(commentBoxSizeX, commentBoxSizeY)-addCommentBox->Size;
+
+ if(positionDiff.X!=0)
+ {
+ int xdiff = positionDiff.X/5;
+ if(xdiff == 0)
+ xdiff = 1*isign(positionDiff.X);
+ addCommentBox->Position.X += xdiff;
+ }
+ if(positionDiff.Y!=0)
+ {
+ int ydiff = positionDiff.Y/5;
+ if(ydiff == 0)
+ ydiff = 1*isign(positionDiff.Y);
+ addCommentBox->Position.Y += ydiff;
+ }
+
+ if(sizeDiff.X!=0)
+ {
+ int xdiff = sizeDiff.X/5;
+ if(xdiff == 0)
+ xdiff = 1*isign(sizeDiff.X);
+ addCommentBox->Size.X += xdiff;
+ addCommentBox->Invalidate();
+ }
+ if(sizeDiff.Y!=0)
+ {
+ int ydiff = sizeDiff.Y/5;
+ if(ydiff == 0)
+ ydiff = 1*isign(sizeDiff.Y);
+ addCommentBox->Size.Y += ydiff;
+ addCommentBox->Invalidate();
+ }
+ }
+
+ c->Update();
+}
+
+void PreviewView::OnTryExit(ExitMethod method)
+{
+ c->Exit();
+}
+
+void PreviewView::OnMouseWheel(int x, int y, int d)
+{
+ if(commentsPanel->GetScrollLimit() == 1 && d < 0)
+ c->NextCommentPage();
+ if(commentsPanel->GetScrollLimit() == -1 && d > 0)
+ c->PrevCommentPage();
+
+}
+
+void PreviewView::NotifySaveChanged(PreviewModel * sender)
+{
+ SaveInfo * save = sender->GetSave();
+ if(savePreview)
+ delete savePreview;
+ savePreview = NULL;
+ if(save)
+ {
+ votesUp = save->votesUp;
+ votesDown = save->votesDown;
+ saveNameLabel->SetText(save->name);
+ authorDateLabel->SetText("\bgAuthor:\bw " + save->userName + " \bgDate:\bw " + format::UnixtimeToDateMini(save->date));
+ viewsLabel->SetText("\bgViews:\bw " + format::NumberToString<int>(save->Views));
+ saveDescriptionLabel->SetText(save->Description);
+ if(save->Favourite)
+ {
+ favButton->Enabled = true;
+ favButton->SetText("Unfav");
+ }
+ else if(Client::Ref().GetAuthUser().ID)
+ {
+ favButton->Enabled = true;
+ favButton->SetText("Fav");
+ }
+ else
+ {
+ favButton->SetText("Fav");
+ favButton->Enabled = false;
+ }
+
+ if(save->GetGameSave())
+ {
+ savePreview = SaveRenderer::Ref().Render(save->GetGameSave(), false, true);
+
+ if(savePreview && savePreview->Data && !(savePreview->Size.X == XRES/2 && savePreview->Size.Y == YRES/2))
+ {
+ int newSizeX, newSizeY;
+ pixel * oldData = savePreview->Data;
+ float factorX = ((float)XRES/2)/((float)savePreview->Size.X);
+ float factorY = ((float)YRES/2)/((float)savePreview->Size.Y);
+ float scaleFactor = factorY < factorX ? factorY : factorX;
+ savePreview->Data = Graphics::resample_img(oldData, savePreview->Size.X, savePreview->Size.Y, savePreview->Size.X*scaleFactor, savePreview->Size.Y*scaleFactor);
+ free(oldData);
+ savePreview->Size.X *= scaleFactor;
+ savePreview->Size.Y *= scaleFactor;
+ }
+ }
+ }
+ else
+ {
+ votesUp = 0;
+ votesDown = 0;
+ saveNameLabel->SetText("");
+ authorDateLabel->SetText("");
+ saveDescriptionLabel->SetText("");
+ favButton->Enabled = false;
+ }
+}
+
+void PreviewView::submitComment()
+{
+ if(addCommentBox)
+ {
+ std::string comment = std::string(addCommentBox->GetText());
+ submitCommentButton->Enabled = false;
+ addCommentBox->SetText("");
+ addCommentBox->SetPlaceholder("Submitting comment");
+ FocusComponent(NULL);
+
+ c->SubmitComment(comment);
+
+ addCommentBox->SetPlaceholder("Add comment");
+ submitCommentButton->Enabled = true;
+
+ commentBoxAutoHeight();
+ }
+}
+
+void PreviewView::NotifyCommentBoxEnabledChanged(PreviewModel * sender)
+{
+ if(addCommentBox)
+ {
+ RemoveComponent(addCommentBox);
+ addCommentBox = NULL;
+ delete addCommentBox;
+ }
+ if(submitCommentButton)
+ {
+ RemoveComponent(submitCommentButton);
+ submitCommentButton = NULL;
+ delete submitCommentButton;
+ }
+ if(sender->GetCommentBoxEnabled())
+ {
+ commentBoxPositionX = (XRES/2)+4;
+ commentBoxPositionY = Size.Y-19;
+ commentBoxSizeX = Size.X-(XRES/2)-48;
+ commentBoxSizeY = 17;
+
+ addCommentBox = new ui::Textbox(ui::Point((XRES/2)+4, Size.Y-19), ui::Point(Size.X-(XRES/2)-48, 17), "", "Add Comment");
+ addCommentBox->SetActionCallback(new AutoCommentSizeAction(this));
+ addCommentBox->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ AddComponent(addCommentBox);
+ submitCommentButton = new ui::Button(ui::Point(Size.X-40, Size.Y-19), ui::Point(40, 19), "Submit");
+ submitCommentButton->SetActionCallback(new SubmitCommentAction(this));
+ //submitCommentButton->Enabled = false;
+ AddComponent(submitCommentButton);
+ }
+ else
+ {
+ submitCommentButton = new ui::Button(ui::Point(XRES/2, Size.Y-19), ui::Point(Size.X-(XRES/2), 19), "Login to comment");
+ submitCommentButton->SetActionCallback(new LoginAction(this));
+ AddComponent(submitCommentButton);
+ }
+}
+
+void PreviewView::NotifyCommentsPageChanged(PreviewModel * sender)
+{
+ std::stringstream pageInfoStream;
+ pageInfoStream << "Page " << sender->GetCommentsPageNum() << " of " << sender->GetCommentsPageCount();
+ pageInfo->SetText(pageInfoStream.str());
+}
+
+void PreviewView::NotifyCommentsChanged(PreviewModel * sender)
+{
+ if(sender->GetComments())
+ {
+ comments = std::vector<SaveComment>(sender->GetComments()->begin(), sender->GetComments()->end());
+ }
+ else
+ {
+ comments.clear();
+ }
+
+ {
+ for(int i = 0; i < commentComponents.size(); i++)
+ {
+ commentsPanel->RemoveChild(commentComponents[i]);
+ delete commentComponents[i];
+ }
+ commentComponents.clear();
+ commentTextComponents.clear();
+
+ int currentY = 0;//-yOffset;
+ ui::Label * tempUsername;
+ ui::Label * tempComment;
+ for(int i = 0; i < comments.size(); i++)
+ {
+ int usernameY = currentY+5, commentY;
+ tempUsername = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 10), 16), comments[i].authorName);
+ tempUsername->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ tempUsername->Appearance.VerticalAlign = ui::Appearance::AlignBottom;
+ currentY += 16;
+
+ commentComponents.push_back(tempUsername);
+ commentsPanel->AddChild(tempUsername);
+
+
+ commentY = currentY+5;
+ tempComment = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 10), -1), comments[i].comment);
+ tempComment->SetMultiline(true);
+ tempComment->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ tempComment->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+ tempComment->SetTextColour(ui::Colour(180, 180, 180));
+ currentY += tempComment->Size.Y+4;
+
+ commentComponents.push_back(tempComment);
+ commentsPanel->AddChild(tempComment);
+ commentTextComponents.push_back(tempComment);
+ }
+
+ commentsPanel->InnerSize = ui::Point(commentsPanel->Size.X, currentY+4);
+ }
+}
+
+/*void PreviewView::NotifyPreviewChanged(PreviewModel * sender)
+{
+ savePreview = sender->GetGameSave();
+ if(savePreview && savePreview->Data && !(savePreview->Size.X == XRES/2 && savePreview->Size.Y == YRES/2))
+ {
+ int newSizeX, newSizeY;
+ float factorX = ((float)XRES/2)/((float)savePreview->Size.X);
+ float factorY = ((float)YRES/2)/((float)savePreview->Size.Y);
+ float scaleFactor = factorY < factorX ? factorY : factorX;
+ savePreview->Data = Graphics::resample_img(savePreview->Data, savePreview->Size.X, savePreview->Size.Y, savePreview->Size.X*scaleFactor, savePreview->Size.Y*scaleFactor);
+ savePreview->Size.X *= scaleFactor;
+ savePreview->Size.Y *= scaleFactor;
+ }
+}*/
+
+PreviewView::~PreviewView() {
+}
+
diff --git a/src/preview/PreviewView.h b/src/preview/PreviewView.h
new file mode 100644
index 0000000..2dc667b
--- /dev/null
+++ b/src/preview/PreviewView.h
@@ -0,0 +1,78 @@
+/*
+ * PreviewView.h
+ *
+ * Created on: Jan 21, 2012
+ * Author: Simon
+ */
+
+#ifndef PREVIEWVIEW_H_
+#define PREVIEWVIEW_H_
+
+#include <vector>
+#include "Comment.h"
+#include "interface/Window.h"
+#include "preview/PreviewController.h"
+#include "preview/PreviewModel.h"
+#include "interface/Button.h"
+#include "search/Thumbnail.h"
+#include "interface/Label.h"
+#include "interface/Textbox.h"
+
+namespace ui
+{
+ class ScrollPanel;
+}
+
+class PreviewModel;
+class PreviewController;
+class PreviewView: public ui::Window {
+ class SubmitCommentAction;
+ class LoginAction;
+ class AutoCommentSizeAction;
+ PreviewController * c;
+ Thumbnail * savePreview;
+ ui::Button * openButton;
+ ui::Button * browserOpenButton;
+ ui::Button * favButton;
+ ui::Button * reportButton;
+ ui::Button * submitCommentButton;
+ ui::Textbox * addCommentBox;
+ ui::Label * saveNameLabel;
+ ui::Label * authorDateLabel;
+ ui::Label * pageInfo;
+ ui::Label * saveDescriptionLabel;
+ ui::Label * viewsLabel;
+ ui::Textbox * saveIDTextbox;
+ ui::ScrollPanel * commentsPanel;
+ std::vector<SaveComment> comments;
+ std::vector<ui::Component*> commentComponents;
+ std::vector<ui::Component*> commentTextComponents;
+ int votesUp;
+ int votesDown;
+ bool doOpen;
+
+ int commentBoxHeight;
+ float commentBoxPositionX;
+ float commentBoxPositionY;
+ float commentBoxSizeX;
+ float commentBoxSizeY;
+
+ void displayComments();
+ void commentBoxAutoHeight();
+ void submitComment();
+public:
+ void AttachController(PreviewController * controller);
+ PreviewView();
+ void NotifySaveChanged(PreviewModel * sender);
+ void NotifyCommentsChanged(PreviewModel * sender);
+ void NotifyCommentsPageChanged(PreviewModel * sender);
+ void NotifyCommentBoxEnabledChanged(PreviewModel * sender);
+ virtual void OnDraw();
+ virtual void DoDraw();
+ virtual void OnTick(float dt);
+ virtual void OnTryExit(ExitMethod method);
+ virtual void OnMouseWheel(int x, int y, int d);
+ virtual ~PreviewView();
+};
+
+#endif /* PREVIEWVIEW_H_ */
diff --git a/src/render/RenderController.cpp b/src/render/RenderController.cpp
new file mode 100644
index 0000000..e98200a
--- /dev/null
+++ b/src/render/RenderController.cpp
@@ -0,0 +1,74 @@
+/*
+ * RenderController.cpp
+ *
+ * Created on: Jan 23, 2012
+ * Author: Simon
+ */
+
+#include "RenderController.h"
+
+RenderController::RenderController(Renderer * ren, ControllerCallback * callback):
+ HasExited(false)
+{
+ renderView = new RenderView();
+ renderModel = new RenderModel();
+
+ renderView->AttachController(this);
+ renderModel->AddObserver(renderView);
+
+ renderModel->SetRenderer(ren);
+ this->callback = callback;
+}
+
+void RenderController::SetRenderMode(unsigned int renderMode)
+{
+ renderModel->SetRenderMode(renderMode);
+}
+
+void RenderController::UnsetRenderMode(unsigned int renderMode)
+{
+ renderModel->UnsetRenderMode(renderMode);
+}
+
+void RenderController::SetDisplayMode(unsigned int renderMode)
+{
+ renderModel->SetDisplayMode(renderMode);
+}
+
+void RenderController::UnsetDisplayMode(unsigned int renderMode)
+{
+ renderModel->UnsetDisplayMode(renderMode);
+}
+
+void RenderController::SetColourMode(unsigned int renderMode)
+{
+ renderModel->SetColourMode(renderMode);
+}
+
+void RenderController::LoadRenderPreset(int presetNum)
+{
+ renderModel->LoadRenderPreset(presetNum);
+}
+
+void RenderController::Exit()
+{
+ if(ui::Engine::Ref().GetWindow() == renderView)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ if(callback)
+ callback->ControllerExit();
+ HasExited = true;
+}
+
+RenderController::~RenderController() {
+ if(ui::Engine::Ref().GetWindow() == renderView)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ if(callback)
+ delete callback;
+ delete renderModel;
+ delete renderView;
+}
+
diff --git a/src/render/RenderController.h b/src/render/RenderController.h
new file mode 100644
index 0000000..8ea44a4
--- /dev/null
+++ b/src/render/RenderController.h
@@ -0,0 +1,36 @@
+/*
+ * RenderController.h
+ *
+ * Created on: Jan 23, 2012
+ * Author: Simon
+ */
+
+#ifndef RENDERCONTROLLER_H_
+#define RENDERCONTROLLER_H_
+
+#include "RenderView.h"
+#include "RenderModel.h"
+#include "graphics/Renderer.h"
+#include "Controller.h"
+
+class RenderView;
+class RenderModel;
+class RenderController {
+ RenderView * renderView;
+ RenderModel * renderModel;
+ ControllerCallback * callback;
+public:
+ bool HasExited;
+ RenderController(Renderer * ren, ControllerCallback * callback = NULL);
+ void Exit();
+ RenderView * GetView() { return renderView; }
+ virtual ~RenderController();
+ void SetRenderMode(unsigned int renderMode);
+ void UnsetRenderMode(unsigned int renderMode);
+ void SetDisplayMode(unsigned int renderMode);
+ void UnsetDisplayMode(unsigned int renderMode);
+ void SetColourMode(unsigned int renderMode);
+ void LoadRenderPreset(int presetNum);
+};
+
+#endif /* RENDERCONTROLLER_H_ */
diff --git a/src/render/RenderModel.cpp b/src/render/RenderModel.cpp
new file mode 100644
index 0000000..556493c
--- /dev/null
+++ b/src/render/RenderModel.cpp
@@ -0,0 +1,143 @@
+/*
+ * RenderModel.cpp
+ *
+ * Created on: Jan 23, 2012
+ * Author: Simon
+ */
+
+#include "RenderModel.h"
+
+RenderModel::RenderModel():
+ renderer(NULL)
+{
+
+}
+
+void RenderModel::AddObserver(RenderView * observer)
+{
+ observers.push_back(observer);
+ observer->NotifyRendererChanged(this);
+ observer->NotifyRenderChanged(this);
+ observer->NotifyDisplayChanged(this);
+ observer->NotifyColourChanged(this);
+}
+
+void RenderModel::SetRenderMode(unsigned int renderMode)
+{
+ if(renderer)
+ renderer->AddRenderMode(renderMode);
+ notifyRenderChanged();
+}
+
+void RenderModel::UnsetRenderMode(unsigned int renderMode)
+{
+ if(renderer)
+ renderer->RemoveRenderMode(renderMode);
+ notifyRenderChanged();
+}
+
+unsigned int RenderModel::GetRenderMode()
+{
+ if(renderer)
+ return renderer->render_mode;
+ else
+ return 0;
+}
+
+void RenderModel::SetDisplayMode(unsigned int displayMode)
+{
+ if(renderer)
+ renderer->AddDisplayMode(displayMode);
+ notifyDisplayChanged();
+}
+
+void RenderModel::UnsetDisplayMode(unsigned int displayMode)
+{
+ if(renderer)
+ renderer->RemoveDisplayMode(displayMode);
+ notifyDisplayChanged();
+}
+
+unsigned int RenderModel::GetDisplayMode()
+{
+ if(renderer)
+ return renderer->display_mode;
+ else
+ return 0;
+}
+
+void RenderModel::SetColourMode(unsigned int colourMode)
+{
+ if(renderer)
+ renderer->SetColourMode(colourMode);
+ notifyColourChanged();
+}
+
+unsigned int RenderModel::GetColourMode()
+{
+ if(renderer)
+ return renderer->colour_mode;
+ else
+ return 0;
+}
+
+void RenderModel::LoadRenderPreset(int presetNum)
+{
+ RenderPreset preset = renderer->renderModePresets[presetNum];
+ renderer->SetRenderMode(preset.RenderModes);
+ renderer->SetDisplayMode(preset.DisplayModes);
+ renderer->SetColourMode(preset.ColourMode);
+ notifyRenderChanged();
+ notifyDisplayChanged();
+ notifyColourChanged();
+}
+
+void RenderModel::SetRenderer(Renderer * ren)
+{
+ renderer = ren;
+ notifyRendererChanged();
+ notifyRenderChanged();
+ notifyDisplayChanged();
+ notifyColourChanged();
+}
+
+Renderer * RenderModel::GetRenderer()
+{
+ return renderer;
+}
+
+void RenderModel::notifyRendererChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyRendererChanged(this);
+ }
+}
+
+void RenderModel::notifyRenderChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyRenderChanged(this);
+ }
+}
+
+void RenderModel::notifyDisplayChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyDisplayChanged(this);
+ }
+}
+
+void RenderModel::notifyColourChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyColourChanged(this);
+ }
+}
+
+RenderModel::~RenderModel() {
+ // TODO Auto-generated destructor stub
+}
diff --git a/src/render/RenderModel.h b/src/render/RenderModel.h
new file mode 100644
index 0000000..8a8b1cd
--- /dev/null
+++ b/src/render/RenderModel.h
@@ -0,0 +1,42 @@
+/*
+ * RenderModel.h
+ *
+ * Created on: Jan 23, 2012
+ * Author: Simon
+ */
+
+#ifndef RENDERMODEL_H_
+#define RENDERMODEL_H_
+
+#include <vector>
+#include "RenderView.h"
+#include "graphics/Renderer.h"
+
+using namespace std;
+
+class RenderView;
+class RenderModel {
+ vector<RenderView*> observers;
+ Renderer * renderer;
+ void notifyRendererChanged();
+ void notifyRenderChanged();
+ void notifyDisplayChanged();
+ void notifyColourChanged();
+public:
+ RenderModel();
+ Renderer * GetRenderer();
+ void AddObserver(RenderView * observer);
+ void SetRenderer(Renderer * ren);
+ void SetRenderMode(unsigned int renderMode);
+ void UnsetRenderMode(unsigned int renderMode);
+ unsigned int GetRenderMode();
+ void SetDisplayMode(unsigned int displayMode);
+ void UnsetDisplayMode(unsigned int displayMode);
+ unsigned int GetDisplayMode();
+ void SetColourMode(unsigned int colourMode);
+ unsigned int GetColourMode();
+ void LoadRenderPreset(int presetNum);
+ virtual ~RenderModel();
+};
+
+#endif /* RENDERMODEL_H_ */
diff --git a/src/render/RenderView.cpp b/src/render/RenderView.cpp
new file mode 100644
index 0000000..ad299b0
--- /dev/null
+++ b/src/render/RenderView.cpp
@@ -0,0 +1,409 @@
+/*
+ * RenderView.cpp
+ *
+ * Created on: Jan 23, 2012
+ * Author: Simon
+ */
+
+#include "simulation/ElementGraphics.h"
+#include "graphics/Graphics.h"
+#include "graphics/Renderer.h"
+#include "RenderView.h"
+
+class RenderView::RenderModeAction: public ui::CheckboxAction
+{
+ RenderView * v;
+public:
+ unsigned int renderMode;
+ RenderModeAction(RenderView * v_, unsigned int renderMode_)
+ {
+ v = v_;
+ renderMode = renderMode_;
+ }
+ virtual void ActionCallback(ui::Checkbox * sender)
+ {
+ if(sender->GetChecked())
+ v->c->SetRenderMode(renderMode);
+ else
+ v->c->UnsetRenderMode(renderMode);
+ }
+};
+
+class RenderView::DisplayModeAction: public ui::CheckboxAction
+{
+ RenderView * v;
+public:
+ unsigned int displayMode;
+ DisplayModeAction(RenderView * v_, unsigned int displayMode_)
+ {
+ v = v_;
+ displayMode = displayMode_;
+ }
+ virtual void ActionCallback(ui::Checkbox * sender)
+ {
+ if(sender->GetChecked())
+ v->c->SetDisplayMode(displayMode);
+ else
+ v->c->UnsetDisplayMode(displayMode);
+ }
+};
+
+class RenderView::ColourModeAction: public ui::CheckboxAction
+{
+ RenderView * v;
+public:
+ unsigned int colourMode;
+ ColourModeAction(RenderView * v_, unsigned int colourMode_)
+ {
+ v = v_;
+ colourMode = colourMode_;
+ }
+ virtual void ActionCallback(ui::Checkbox * sender)
+ {
+ if(sender->GetChecked())
+ v->c->SetColourMode(colourMode);
+ else
+ v->c->SetColourMode(0);
+ }
+};
+
+class RenderView::RenderPresetAction: public ui::ButtonAction
+{
+ RenderView * v;
+public:
+ int renderPreset;
+ RenderPresetAction(RenderView * v_, int renderPreset_)
+ {
+ v = v_;
+ renderPreset = renderPreset_;
+ }
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ v->c->LoadRenderPreset(renderPreset);
+ }
+};
+
+RenderView::RenderView():
+ ui::Window(ui::Point(0, 0), ui::Point(XRES, YRES+MENUSIZE)),
+ toolTip(""),
+ toolTipPresence(0),
+ ren(NULL)
+{
+ ui::Button * presetButton;
+ int presetButtonOffset = 375;
+ int checkboxOffset = 1;
+ int cSpace = 32;
+ int sSpace = 38;
+
+ presetButton = new ui::Button(ui::Point(presetButtonOffset+200, YRES+6), ui::Point(30, 13), "", "Velocity display mode preset");
+ presetButton->SetIcon(IconVelocity);
+ presetButton->SetActionCallback(new RenderPresetAction(this, 1));
+ AddComponent(presetButton);
+
+ presetButton = new ui::Button(ui::Point(presetButtonOffset+200, YRES+6+18), ui::Point(30, 13), "", "Pressure display mode preset");
+ presetButton->SetIcon(IconPressure);
+ presetButton->SetActionCallback(new RenderPresetAction(this, 2));
+ AddComponent(presetButton);
+
+ presetButton = new ui::Button(ui::Point(presetButtonOffset+161, YRES+6), ui::Point(30, 13), "", "Persistent display mode preset");
+ presetButton->SetIcon(IconPersistant);
+ presetButton->SetActionCallback(new RenderPresetAction(this, 3));
+ AddComponent(presetButton);
+
+ presetButton = new ui::Button(ui::Point(presetButtonOffset+161, YRES+6+18), ui::Point(30, 13), "", "Fire display mode preset");
+ presetButton->SetIcon(IconFire);
+ presetButton->SetActionCallback(new RenderPresetAction(this, 4));
+ AddComponent(presetButton);
+
+ presetButton = new ui::Button(ui::Point(presetButtonOffset+122, YRES+6), ui::Point(30, 13), "", "Blob display mode preset");
+ presetButton->SetIcon(IconBlob);
+ presetButton->SetActionCallback(new RenderPresetAction(this, 5));
+ AddComponent(presetButton);
+
+ presetButton = new ui::Button(ui::Point(presetButtonOffset+122, YRES+6+18), ui::Point(30, 13), "", "Heat display mode preset");
+ presetButton->SetIcon(IconHeat);
+ presetButton->SetActionCallback(new RenderPresetAction(this, 6));
+ AddComponent(presetButton);
+
+ presetButton = new ui::Button(ui::Point(presetButtonOffset+83, YRES+6), ui::Point(30, 13), "", "Fancy display mode preset");
+ presetButton->SetIcon(IconBlur);
+ presetButton->SetActionCallback(new RenderPresetAction(this, 7));
+ AddComponent(presetButton);
+
+ presetButton = new ui::Button(ui::Point(presetButtonOffset+83, YRES+6+18), ui::Point(30, 13), "", "Nothing display mode preset");
+ presetButton->SetIcon(IconBasic);
+ presetButton->SetActionCallback(new RenderPresetAction(this, 8));
+ AddComponent(presetButton);
+
+ presetButton = new ui::Button(ui::Point(presetButtonOffset+44, YRES+6), ui::Point(30, 13), "", "Heat gradient display mode preset");
+ presetButton->SetIcon(IconGradient);
+ presetButton->SetActionCallback(new RenderPresetAction(this, 9));
+ AddComponent(presetButton);
+
+ presetButton = new ui::Button(ui::Point(presetButtonOffset+44, YRES+6+18), ui::Point(30, 13), "", "Alternative Velocity display mode preset");
+ presetButton->SetIcon(IconAltAir);
+ presetButton->SetActionCallback(new RenderPresetAction(this, 0));
+ AddComponent(presetButton);
+
+ presetButton = new ui::Button(ui::Point(presetButtonOffset+5, YRES+6), ui::Point(30, 13), "", "Life display mode preset");
+ presetButton->SetIcon(IconLife);
+ presetButton->SetActionCallback(new RenderPresetAction(this, 10));
+ AddComponent(presetButton);
+
+ ui::Checkbox * tCheckbox;
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Effects", "Adds Special flare effects to some elements");
+ renderModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconEffect);
+ tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_EFFE));
+ AddComponent(tCheckbox);
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Fire", "Fire effect for gasses");
+ renderModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconFire);
+ tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_FIRE));
+ AddComponent(tCheckbox);
+
+ checkboxOffset += cSpace;
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Glow", "Glow effect on some elements");
+ renderModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconGlow);
+ tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_GLOW));
+ AddComponent(tCheckbox);
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Blur", "Blur effect for liquids");
+ renderModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconBlur);
+ tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_BLUR));
+ AddComponent(tCheckbox);
+
+ checkboxOffset += cSpace;
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Blob", "Makes everything be drawn like a blob");
+ renderModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconBlob);
+ tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_BLOB));
+ AddComponent(tCheckbox);
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Point", "Basic rendering, without this, most things will be invisible");
+ renderModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconBasic);
+ tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_BASC));
+ AddComponent(tCheckbox);
+
+ checkboxOffset += sSpace;
+ line1 = checkboxOffset-5;
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Alt. Air", "Displays pressure as red and blue, and velocity as white");
+ displayModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconAltAir);
+ tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_AIRC));
+ AddComponent(tCheckbox);
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Pressure", "Displays pressure, red is positive and blue is negative");
+ displayModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconPressure);
+ tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_AIRP));
+ AddComponent(tCheckbox);
+
+ checkboxOffset += cSpace;
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Velocity", "Displays velocity and positive pressure: up/down adds blue, right/left adds red, still pressure adds green");
+ displayModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconVelocity);
+ tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_AIRV));
+ AddComponent(tCheckbox);
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Air-heat", "Displays the temperature of the air like heat display does");
+ displayModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconHeat);
+ tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_AIRH));
+ AddComponent(tCheckbox);
+
+ /*tCheckbox = new ui::Checkbox(ui::Point(216, YRES+4), ui::Point(30, 16), "Air", "");
+ displayModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconAltAir);
+ tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_AIR));
+ AddComponent(tCheckbox);*/
+
+ checkboxOffset += sSpace;
+ line2 = checkboxOffset-5;
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Warp", "Gravity lensing, Newtonian Gravity bends light with this on");
+ displayModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconWarp);
+ tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_WARP));
+ AddComponent(tCheckbox);
+
+#ifdef OGLR
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Effect", "I don't know what this does..."); //I would remove the whole checkbox, but then there's a large empty space
+#else
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Effect", "Does nothing");
+#endif
+ displayModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconEffect);
+ tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_EFFE));
+ AddComponent(tCheckbox);
+
+ checkboxOffset += cSpace;
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Persistent", "Element paths persist on the screen for a while");
+ displayModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconPersistant);
+ tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_PERS));
+ AddComponent(tCheckbox);
+
+ checkboxOffset += sSpace;
+ line3 = checkboxOffset-5;
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Heat", "Displays temperatures of the elements, dark blue is coldest, pink is hottest");
+ colourModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconHeat);
+ tCheckbox->SetActionCallback(new ColourModeAction(this, COLOUR_HEAT));
+ AddComponent(tCheckbox);
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Life", "Displays the life value of elements in greyscale gradients");
+ colourModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconLife);
+ tCheckbox->SetActionCallback(new ColourModeAction(this, COLOUR_LIFE));
+ AddComponent(tCheckbox);
+
+ checkboxOffset += cSpace;
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "H-Gradient", "Changes colors of elements slightly to show heat diffusing through them");
+ colourModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconGradient);
+ tCheckbox->SetActionCallback(new ColourModeAction(this, COLOUR_GRAD));
+ AddComponent(tCheckbox);
+
+ tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Basic", "No special effects at all for anything, overrides all other options and deco");
+ colourModes.push_back(tCheckbox);
+ tCheckbox->SetIcon(IconBasic);
+ tCheckbox->SetActionCallback(new ColourModeAction(this, COLOUR_BASC));
+ AddComponent(tCheckbox);
+
+ checkboxOffset += sSpace;
+ line4 = checkboxOffset-5;
+}
+
+void RenderView::OnMouseDown(int x, int y, unsigned button)
+{
+ if(x > XRES || y < YRES)
+ c->Exit();
+}
+
+void RenderView::NotifyRendererChanged(RenderModel * sender)
+{
+ ren = sender->GetRenderer();
+}
+
+void RenderView::NotifyRenderChanged(RenderModel * sender)
+{
+ for(int i = 0; i < renderModes.size(); i++)
+ {
+ if(renderModes[i]->GetActionCallback())
+ {
+ //Compares bitmasks at the moment, this means that "Point" is always on when other options that depend on it are, this might confuse some users, TODO: get the full list and compare that?
+ RenderModeAction * action = (RenderModeAction *)(renderModes[i]->GetActionCallback());
+ if(action->renderMode == (sender->GetRenderMode() & action->renderMode))
+ {
+ renderModes[i]->SetChecked(true);
+ }
+ else
+ {
+ renderModes[i]->SetChecked(false);
+ }
+ }
+ }
+}
+
+void RenderView::NotifyDisplayChanged(RenderModel * sender)
+{
+ for(int i = 0; i < displayModes.size(); i++)
+ {
+ if(displayModes[i]->GetActionCallback())
+ {
+ DisplayModeAction * action = (DisplayModeAction *)(displayModes[i]->GetActionCallback());
+ if(action->displayMode == (sender->GetDisplayMode() & action->displayMode))
+ {
+ displayModes[i]->SetChecked(true);
+ }
+ else
+ {
+ displayModes[i]->SetChecked(false);
+ }
+ }
+ }
+}
+
+void RenderView::NotifyColourChanged(RenderModel * sender)
+{
+ for(int i = 0; i < colourModes.size(); i++)
+ {
+ if(colourModes[i]->GetActionCallback())
+ {
+ ColourModeAction * action = (ColourModeAction *)(colourModes[i]->GetActionCallback());
+ if(action->colourMode == sender->GetColourMode())
+ {
+ colourModes[i]->SetChecked(true);
+ }
+ else
+ {
+ colourModes[i]->SetChecked(false);
+ }
+ }
+ }
+}
+
+void RenderView::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ g->clearrect(-1, -1, XRES+BARSIZE+1, YRES+MENUSIZE+1);
+ if(ren)
+ {
+ ren->clearScreen(1.0f);
+ ren->RenderBegin();
+ ren->RenderEnd();
+ }
+ g->draw_line(0, YRES, XRES-1, YRES, 200, 200, 200, 255);
+ g->draw_line(line1, YRES, line1, YRES+MENUSIZE, 200, 200, 200, 255);
+ g->draw_line(line2, YRES, line2, YRES+MENUSIZE, 200, 200, 200, 255);
+ g->draw_line(line3, YRES, line3, YRES+MENUSIZE, 200, 200, 200, 255);
+ g->draw_line(line4, YRES, line4, YRES+MENUSIZE, 200, 200, 200, 255);
+ g->draw_line(XRES, 0, XRES, YRES+MENUSIZE, 255, 255, 255, 255);
+ if(toolTipPresence && toolTip.length())
+ {
+ g->drawtext(6, Size.Y-MENUSIZE-12, (char*)toolTip.c_str(), 255, 255, 255, toolTipPresence>51?255:toolTipPresence*5);
+ }
+}
+
+void RenderView::OnTick(float dt)
+{
+ if(toolTipPresence>0)
+ {
+ toolTipPresence -= int(dt)>0?int(dt):1;
+ if(toolTipPresence<0)
+ toolTipPresence = 0;
+ }
+}
+
+void RenderView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if (shift && key == '1')
+ c->LoadRenderPreset(10);
+ else if(key >= '0' && key <= '9')
+ {
+ c->LoadRenderPreset(key-'0');
+ }
+}
+
+void RenderView::ToolTip(ui::Component * sender, ui::Point mousePosition, std::string toolTip)
+{
+ this->toolTip = toolTip;
+ toolTipPresence = 500;
+}
+
+RenderView::~RenderView() {
+ // TODO Auto-generated destructor stub
+}
diff --git a/src/render/RenderView.h b/src/render/RenderView.h
new file mode 100644
index 0000000..9e4819b
--- /dev/null
+++ b/src/render/RenderView.h
@@ -0,0 +1,50 @@
+/*
+ * RenderView.h
+ *
+ * Created on: Jan 23, 2012
+ * Author: Simon
+ */
+
+#ifndef RENDERVIEW_H_
+#define RENDERVIEW_H_
+
+
+#include <vector>
+#include "interface/Window.h"
+#include "RenderController.h"
+#include "RenderModel.h"
+#include "graphics/Renderer.h"
+#include "interface/Checkbox.h"
+#include "interface/Button.h"
+
+class RenderController;
+class RenderModel;
+class RenderView: public ui::Window {
+ RenderController * c;
+ Renderer * ren;
+ std::vector<ui::Checkbox*> renderModes;
+ std::vector<ui::Checkbox*> displayModes;
+ std::vector<ui::Checkbox*> colourModes;
+ std::string toolTip;
+ int toolTipPresence;
+ int line1, line2, line3, line4;
+public:
+ class RenderModeAction;
+ class DisplayModeAction;
+ class ColourModeAction;
+ class RenderPresetAction;
+ RenderView();
+ void NotifyRendererChanged(RenderModel * sender);
+ void NotifyRenderChanged(RenderModel * sender);
+ void NotifyDisplayChanged(RenderModel * sender);
+ void NotifyColourChanged(RenderModel * sender);
+ void AttachController(RenderController * c_) { c = c_; }
+ void OnMouseDown(int x, int y, unsigned button);
+ virtual void OnDraw();
+ virtual void OnTick(float dt);
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ virtual void ToolTip(ui::Component * sender, ui::Point mousePosition, std::string toolTip);
+ virtual ~RenderView();
+};
+
+#endif /* RENDERVIEW_H_ */
diff --git a/src/save/LocalSaveActivity.cpp b/src/save/LocalSaveActivity.cpp
new file mode 100644
index 0000000..b57a993
--- /dev/null
+++ b/src/save/LocalSaveActivity.cpp
@@ -0,0 +1,138 @@
+#include "LocalSaveActivity.h"
+#include "interface/Label.h"
+#include "interface/Textbox.h"
+#include "interface/Button.h"
+#include "search/Thumbnail.h"
+#include "client/ThumbnailBroker.h"
+#include "dialogues/ErrorMessage.h"
+#include "dialogues/ConfirmPrompt.h"
+#include "client/Client.h"
+#include "client/GameSave.h"
+#include "Style.h"
+
+class LocalSaveActivity::CancelAction: public ui::ButtonAction
+{
+ LocalSaveActivity * a;
+public:
+ CancelAction(LocalSaveActivity * a) : a(a) {}
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ a->Exit();
+ }
+};
+
+class LocalSaveActivity::SaveAction: public ui::ButtonAction
+{
+ LocalSaveActivity * a;
+public:
+ SaveAction(LocalSaveActivity * a) : a(a) {}
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ a->Save();
+ }
+};
+
+LocalSaveActivity::LocalSaveActivity(SaveFile save, FileSavedCallback * callback) :
+ WindowActivity(ui::Point(-1, -1), ui::Point(220, 200)),
+ thumbnail(NULL),
+ save(save),
+ callback(callback)
+{
+ ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 16), "Save to computer:");
+ titleLabel->SetTextColour(style::Colour::InformationTitle);
+ titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(titleLabel);
+
+ filenameField = new ui::Textbox(ui::Point(8, 25), ui::Point(Size.X-16, 16), save.GetDisplayName(), "[filename]");
+ filenameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ filenameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ AddComponent(filenameField);
+
+ ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X-75, 16), "Cancel");
+ cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ cancelButton->Appearance.BorderInactive = ui::Colour(200, 200, 200);
+ cancelButton->SetActionCallback(new CancelAction(this));
+ AddComponent(cancelButton);
+ SetCancelButton(cancelButton);
+
+ ui::Button * okayButton = new ui::Button(ui::Point(Size.X-76, Size.Y-16), ui::Point(76, 16), "Save");
+ okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ okayButton->Appearance.TextInactive = style::Colour::InformationTitle;
+ okayButton->SetActionCallback(new SaveAction(this));
+ AddComponent(okayButton);
+ SetOkayButton(okayButton);
+
+ if(save.GetGameSave())
+ ThumbnailBroker::Ref().RenderThumbnail(save.GetGameSave(), true, false, Size.X-16, -1, this);
+}
+
+void LocalSaveActivity::Save()
+{
+ class FileOverwriteConfirmation: public ConfirmDialogueCallback {
+ public:
+ LocalSaveActivity * a;
+ std::string filename;
+ FileOverwriteConfirmation(LocalSaveActivity * a, std::string finalFilename) : a(a), filename(finalFilename) {}
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ {
+ a->saveWrite(filename);
+ a->Exit();
+ }
+ }
+ virtual ~FileOverwriteConfirmation() { }
+ };
+
+ if(filenameField->GetText().length())
+ {
+ std::string finalFilename = std::string(LOCAL_SAVE_DIR) + std::string(PATH_SEP) + filenameField->GetText() + ".cps";
+ save.SetDisplayName(filenameField->GetText());
+ save.SetFileName(finalFilename);
+ if(Client::Ref().FileExists(finalFilename))
+ {
+ new ConfirmPrompt("Overwrite file", "Are you sure you wish to overwrite\n"+finalFilename, new FileOverwriteConfirmation(this, finalFilename));
+ }
+ else
+ {
+ saveWrite(finalFilename);
+ Exit();
+ }
+ }
+ else
+ {
+ new ErrorMessage("Error", "You must specify a filename.");
+ }
+}
+
+void LocalSaveActivity::saveWrite(std::string finalFilename)
+{
+ Client::Ref().MakeDirectory(LOCAL_SAVE_DIR);
+ Client::Ref().WriteFile(save.GetGameSave()->Serialise(), finalFilename);
+ callback->FileSaved(&save);
+}
+
+void LocalSaveActivity::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
+
+ if(thumbnail)
+ {
+ g->draw_image(thumbnail->Data, Position.X+(Size.X-thumbnail->Size.X)/2, Position.Y+45, thumbnail->Size.X, thumbnail->Size.Y, 255);
+ g->drawrect(Position.X+(Size.X-thumbnail->Size.X)/2, Position.Y+45, thumbnail->Size.X, thumbnail->Size.Y, 180, 180, 180, 255);
+ }
+}
+
+void LocalSaveActivity::OnThumbnailReady(Thumbnail * thumbnail)
+{
+ this->thumbnail = thumbnail;
+}
+
+LocalSaveActivity::~LocalSaveActivity()
+{
+
+} \ No newline at end of file
diff --git a/src/save/LocalSaveActivity.h b/src/save/LocalSaveActivity.h
new file mode 100644
index 0000000..aacbc0b
--- /dev/null
+++ b/src/save/LocalSaveActivity.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "Activity.h"
+#include "client/SaveFile.h"
+#include "client/ThumbnailListener.h"
+
+namespace ui
+{
+ class Textbox;
+}
+
+class Thumbnail;
+
+class FileSavedCallback
+{
+public:
+ FileSavedCallback() {}
+ virtual ~FileSavedCallback() {}
+ virtual void FileSaved(SaveFile * file) {}
+};
+
+class LocalSaveActivity: public WindowActivity, public ThumbnailListener
+{
+ SaveFile save;
+ Thumbnail * thumbnail;
+ ui::Textbox * filenameField;
+ class CancelAction;
+ class SaveAction;
+ friend class CancelAction;
+ friend class SaveAction;
+ FileSavedCallback * callback;
+public:
+ LocalSaveActivity(SaveFile save, FileSavedCallback * callback);
+ void saveWrite(std::string finalFilename);
+ virtual void Save();
+ virtual void OnDraw();
+ virtual void OnThumbnailReady(Thumbnail * thumbnail);
+ virtual ~LocalSaveActivity();
+}; \ No newline at end of file
diff --git a/src/save/ServerSaveActivity.cpp b/src/save/ServerSaveActivity.cpp
new file mode 100644
index 0000000..eda807b
--- /dev/null
+++ b/src/save/ServerSaveActivity.cpp
@@ -0,0 +1,261 @@
+#include "ServerSaveActivity.h"
+#include "interface/Label.h"
+#include "interface/Textbox.h"
+#include "interface/Button.h"
+#include "interface/Checkbox.h"
+#include "search/Thumbnail.h"
+#include "client/ThumbnailBroker.h"
+#include "dialogues/ErrorMessage.h"
+#include "dialogues/ConfirmPrompt.h"
+#include "client/Client.h"
+#include "tasks/Task.h"
+#include "Style.h"
+
+class ServerSaveActivity::CancelAction: public ui::ButtonAction
+{
+ ServerSaveActivity * a;
+public:
+ CancelAction(ServerSaveActivity * a) : a(a) {}
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ a->Exit();
+ }
+};
+
+class ServerSaveActivity::SaveAction: public ui::ButtonAction
+{
+ ServerSaveActivity * a;
+public:
+ SaveAction(ServerSaveActivity * a) : a(a) {}
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ a->Save();
+ }
+};
+
+class SaveUploadTask: public Task
+{
+ SaveInfo save;
+
+ virtual void before()
+ {
+
+ }
+
+ virtual void after()
+ {
+
+ }
+
+ virtual bool doWork()
+ {
+ notifyProgress(-1);
+ return Client::Ref().UploadSave(save) == RequestOkay;
+ }
+
+public:
+ SaveInfo GetSave()
+ {
+ return save;
+ }
+
+ SaveUploadTask(SaveInfo save):
+ save(save)
+ {
+
+ }
+};
+
+ServerSaveActivity::ServerSaveActivity(SaveInfo save, ServerSaveActivity::SaveUploadedCallback * callback) :
+ WindowActivity(ui::Point(-1, -1), ui::Point(440, 200)),
+ thumbnail(NULL),
+ save(save),
+ callback(callback),
+ saveUploadTask(NULL)
+{
+ ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point((Size.X/2)-8, 16), "Save to server:");
+ titleLabel->SetTextColour(style::Colour::InformationTitle);
+ titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(titleLabel);
+
+ ui::Label * previewLabel = new ui::Label(ui::Point((Size.X/2)+4, 5), ui::Point((Size.X/2)-8, 16), "Preview:");
+ previewLabel->SetTextColour(style::Colour::InformationTitle);
+ previewLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ previewLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(previewLabel);
+
+ nameField = new ui::Textbox(ui::Point(8, 25), ui::Point((Size.X/2)-16, 16), save.GetName(), "[save name]");
+ nameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ nameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ AddComponent(nameField);
+
+ descriptionField = new ui::Textbox(ui::Point(8, 65), ui::Point((Size.X/2)-16, Size.Y-(65+16+4)), save.GetDescription(), "[save description]");
+ descriptionField->SetMultiline(true);
+ descriptionField->SetLimit(254);
+ descriptionField->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+ descriptionField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ AddComponent(descriptionField);
+
+ publishedCheckbox = new ui::Checkbox(ui::Point(8, 45), ui::Point((Size.X/2)-16, 16), "Publish", "");
+ if(Client::Ref().GetAuthUser().Username != save.GetUserName())
+ {
+ //Save is not owned by the user, disable by default
+ publishedCheckbox->SetChecked(false);
+ }
+ else
+ {
+ //Save belongs to the current user, use published state already set
+ publishedCheckbox->SetChecked(save.GetPublished());
+ }
+ AddComponent(publishedCheckbox);
+
+ ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point((Size.X/2)-75, 16), "Cancel");
+ cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ cancelButton->Appearance.BorderInactive = ui::Colour(200, 200, 200);
+ cancelButton->SetActionCallback(new CancelAction(this));
+ AddComponent(cancelButton);
+ SetCancelButton(cancelButton);
+
+ ui::Button * okayButton = new ui::Button(ui::Point((Size.X/2)-76, Size.Y-16), ui::Point(76, 16), "Save");
+ okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ okayButton->Appearance.TextInactive = style::Colour::InformationTitle;
+ okayButton->SetActionCallback(new SaveAction(this));
+ AddComponent(okayButton);
+ SetOkayButton(okayButton);
+
+ if(save.GetGameSave())
+ ThumbnailBroker::Ref().RenderThumbnail(save.GetGameSave(), false, true, (Size.X/2)-16, -1, this);
+}
+
+ServerSaveActivity::ServerSaveActivity(SaveInfo save, bool saveNow, ServerSaveActivity::SaveUploadedCallback * callback) :
+ WindowActivity(ui::Point(-1, -1), ui::Point(200, 50)),
+ thumbnail(NULL),
+ save(save),
+ callback(callback),
+ saveUploadTask(NULL)
+{
+ ui::Label * titleLabel = new ui::Label(ui::Point(0, 0), Size, "Saving to server...");
+ titleLabel->SetTextColour(style::Colour::InformationTitle);
+ titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
+ titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(titleLabel);
+
+ saveUploadTask = new SaveUploadTask(save);
+ saveUploadTask->AddTaskListener(this);
+ saveUploadTask->Start();
+}
+
+void ServerSaveActivity::NotifyDone(Task * task)
+{
+ if(!task->GetSuccess())
+ {
+ Exit();
+ new ErrorMessage("Error", Client::Ref().GetLastError());
+ }
+ else
+ {
+ if(callback)
+ {
+ callback->SaveUploaded(save);
+ }
+ Exit();
+ }
+}
+
+void ServerSaveActivity::Save()
+{
+ class PublishConfirmation: public ConfirmDialogueCallback {
+ public:
+ ServerSaveActivity * a;
+ PublishConfirmation(ServerSaveActivity * a) : a(a) {}
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ {
+ a->Exit();
+ a->saveUpload();
+ }
+ }
+ virtual ~PublishConfirmation() { }
+ };
+
+ if(nameField->GetText().length())
+ {
+ if(Client::Ref().GetAuthUser().Username != save.GetUserName() && publishedCheckbox->GetChecked())
+ {
+ new ConfirmPrompt("Publish", "This save was created by " + save.GetUserName() + ", you're about to publish this under your own name; If you haven't been given permission by the author to do so, please untick the publish box, otherwise continue", new PublishConfirmation(this));
+ }
+ else
+ {
+ Exit();
+ saveUpload();
+ }
+ }
+ else
+ {
+ new ErrorMessage("Error", "You must specify a save name.");
+ }
+}
+
+void ServerSaveActivity::saveUpload()
+{
+ save.SetName(nameField->GetText());
+ save.SetDescription(descriptionField->GetText());
+ save.SetPublished(publishedCheckbox->GetChecked());
+ save.SetUserName(Client::Ref().GetAuthUser().Username);
+ save.SetID(0);
+
+ if(Client::Ref().UploadSave(save) != RequestOkay)
+ {
+ new ErrorMessage("Error", "Upload failed with error:\n"+Client::Ref().GetLastError());
+ }
+ else if(callback)
+ {
+ callback->SaveUploaded(save);
+ }
+}
+
+void ServerSaveActivity::Exit()
+{
+ if(callback)
+ {
+ delete callback;
+ callback = NULL;
+ }
+ WindowActivity::Exit();
+}
+
+void ServerSaveActivity::OnTick(float dt)
+{
+ if(saveUploadTask)
+ saveUploadTask->Poll();
+}
+
+void ServerSaveActivity::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
+
+ if(Size.X>220)
+ g->draw_line(Position.X+(Size.X/2)-1, Position.Y, Position.X+(Size.X/2)-1, Position.Y+Size.Y-1, 255, 255, 255, 255);
+
+ if(thumbnail)
+ {
+ g->draw_image(thumbnail->Data, Position.X+(Size.X/2)+((Size.X/2)-thumbnail->Size.X)/2, Position.Y+25, thumbnail->Size.X, thumbnail->Size.Y, 255);
+ g->drawrect(Position.X+(Size.X/2)+((Size.X/2)-thumbnail->Size.X)/2, Position.Y+25, thumbnail->Size.X, thumbnail->Size.Y, 180, 180, 180, 255);
+ }
+}
+
+void ServerSaveActivity::OnThumbnailReady(Thumbnail * thumbnail)
+{
+ this->thumbnail = thumbnail;
+}
+
+ServerSaveActivity::~ServerSaveActivity()
+{
+ if(saveUploadTask)
+ delete saveUploadTask;
+} \ No newline at end of file
diff --git a/src/save/ServerSaveActivity.h b/src/save/ServerSaveActivity.h
new file mode 100644
index 0000000..4bf581c
--- /dev/null
+++ b/src/save/ServerSaveActivity.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "Activity.h"
+#include "client/SaveInfo.h"
+#include "client/ThumbnailListener.h"
+#include "tasks/TaskListener.h"
+
+namespace ui
+{
+ class Textbox;
+ class Checkbox;
+}
+
+class Task;
+class Thumbnail;
+class ServerSaveActivity: public WindowActivity, public ThumbnailListener, public TaskListener
+{
+public:
+ class SaveUploadedCallback
+ {
+ public:
+ SaveUploadedCallback() {}
+ virtual ~SaveUploadedCallback() {}
+ virtual void SaveUploaded(SaveInfo save) {}
+ };
+ ServerSaveActivity(SaveInfo save, SaveUploadedCallback * callback);
+ ServerSaveActivity(SaveInfo save, bool saveNow, SaveUploadedCallback * callback);
+ void saveUpload();
+ virtual void Save();
+ virtual void Exit();
+ virtual void OnDraw();
+ virtual void OnThumbnailReady(Thumbnail * thumbnail);
+ virtual void OnTick(float dt);
+ virtual ~ServerSaveActivity();
+protected:
+ virtual void NotifyDone(Task * task);
+ Task * saveUploadTask;
+ SaveUploadedCallback * callback;
+ SaveInfo save;
+ Thumbnail * thumbnail;
+ ui::Textbox * nameField;
+ ui::Textbox * descriptionField;
+ ui::Checkbox * publishedCheckbox;
+ class CancelAction;
+ class SaveAction;
+ friend class CancelAction;
+ friend class SaveAction;
+}; \ No newline at end of file
diff --git a/src/search/SearchController.cpp b/src/search/SearchController.cpp
new file mode 100644
index 0000000..7930b65
--- /dev/null
+++ b/src/search/SearchController.cpp
@@ -0,0 +1,357 @@
+#include <string>
+#include <sstream>
+#include "SearchController.h"
+#include "SearchModel.h"
+#include "SearchView.h"
+#include "interface/Panel.h"
+#include "dialogues/ConfirmPrompt.h"
+#include "dialogues/ErrorMessage.h"
+#include "preview/PreviewController.h"
+#include "client/Client.h"
+#include "tasks/Task.h"
+#include "tasks/TaskWindow.h"
+
+class SearchController::OpenCallback: public ControllerCallback
+{
+ SearchController * cc;
+public:
+ OpenCallback(SearchController * cc_) { cc = cc_; }
+ virtual void ControllerExit()
+ {
+ if(cc->activePreview->GetDoOpen() && cc->activePreview->GetSave())
+ {
+ cc->searchModel->SetLoadedSave(cc->activePreview->GetSave());
+ }
+ else
+ {
+ cc->searchModel->SetLoadedSave(NULL);
+ }
+
+ }
+};
+
+SearchController::SearchController(ControllerCallback * callback):
+ activePreview(NULL),
+ HasExited(false),
+ nextQueryTime(0.0f),
+ nextQueryDone(true),
+ searchModel(NULL)
+{
+ searchModel = new SearchModel();
+ searchView = new SearchView();
+ searchModel->AddObserver(searchView);
+ searchView->AttachController(this);
+
+ searchModel->UpdateSaveList(1, "");
+
+ this->callback = callback;
+
+ //Set up interface
+ //windowPanel.AddChild();
+}
+
+SaveInfo * SearchController::GetLoadedSave()
+{
+ return searchModel->GetLoadedSave();
+}
+
+void SearchController::Update()
+{
+ if(!nextQueryDone && nextQueryTime < clock())
+ {
+ nextQueryDone = true;
+ searchModel->UpdateSaveList(1, nextQuery);
+ }
+ searchModel->Update();
+ if(activePreview && activePreview->HasExited)
+ {
+ delete activePreview;
+ activePreview = NULL;
+ if(searchModel->GetLoadedSave())
+ {
+ Exit();
+ }
+ }
+}
+
+void SearchController::Exit()
+{
+ if(ui::Engine::Ref().GetWindow() == searchView)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ if(callback)
+ callback->ControllerExit();
+ //HasExited = true;
+}
+
+SearchController::~SearchController()
+{
+ if(activePreview)
+ delete activePreview;
+ if(ui::Engine::Ref().GetWindow() == searchView)
+ {
+ ui::Engine::Ref().CloseWindow();
+ }
+ delete searchModel;
+ delete searchView;
+}
+
+void SearchController::DoSearch(std::string query, bool now)
+{
+ nextQuery = query;
+ if(!now)
+ {
+ nextQueryTime = clock()+(0.6 * CLOCKS_PER_SEC);
+ nextQueryDone = false;
+ }
+ else
+ {
+ nextQueryDone = true;
+ searchModel->UpdateSaveList(1, nextQuery);
+ }
+ //searchModel->UpdateSaveList(1, query);
+}
+
+void SearchController::PrevPage()
+{
+ if(searchModel->GetPageNum()>1)
+ searchModel->UpdateSaveList(searchModel->GetPageNum()-1, searchModel->GetLastQuery());
+}
+
+void SearchController::NextPage()
+{
+ if(searchModel->GetPageNum() <= searchModel->GetPageCount())
+ searchModel->UpdateSaveList(searchModel->GetPageNum()+1, searchModel->GetLastQuery());
+}
+
+void SearchController::ChangeSort()
+{
+ if(searchModel->GetSort() == "new")
+ {
+ searchModel->SetSort("best");
+ }
+ else
+ {
+ searchModel->SetSort("new");
+ }
+ searchModel->UpdateSaveList(1, searchModel->GetLastQuery());
+}
+
+void SearchController::ShowOwn(bool show)
+{
+ if(Client::Ref().GetAuthUser().ID)
+ {
+ searchModel->SetShowFavourite(false);
+ searchModel->SetShowOwn(show);
+ }
+ else
+ searchModel->SetShowOwn(false);
+ searchModel->UpdateSaveList(1, searchModel->GetLastQuery());
+}
+
+void SearchController::ShowFavourite(bool show)
+{
+ if(Client::Ref().GetAuthUser().ID)
+ {
+ searchModel->SetShowOwn(false);
+ searchModel->SetShowFavourite(show);
+ }
+ else
+ searchModel->SetShowFavourite(false);
+ searchModel->UpdateSaveList(1, searchModel->GetLastQuery());
+}
+
+void SearchController::Selected(int saveID, bool selected)
+{
+ if(!Client::Ref().GetAuthUser().ID)
+ return;
+
+ if(selected)
+ searchModel->SelectSave(saveID);
+ else
+ searchModel->DeselectSave(saveID);
+}
+
+void SearchController::OpenSave(int saveID)
+{
+ if(activePreview)
+ delete activePreview;
+ activePreview = new PreviewController(saveID, new OpenCallback(this));
+ ui::Engine::Ref().ShowWindow(activePreview->GetView());
+}
+
+void SearchController::OpenSave(int saveID, int saveDate)
+{
+ if(activePreview)
+ delete activePreview;
+ activePreview = new PreviewController(saveID, saveDate, new OpenCallback(this));
+ ui::Engine::Ref().ShowWindow(activePreview->GetView());
+}
+
+void SearchController::ClearSelection()
+{
+ searchModel->ClearSelected();
+}
+
+void SearchController::RemoveSelected()
+{
+ class RemoveSelectedConfirmation: public ConfirmDialogueCallback {
+ public:
+ SearchController * c;
+ RemoveSelectedConfirmation(SearchController * c_) { c = c_; }
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ c->removeSelectedC();
+ }
+ virtual ~RemoveSelectedConfirmation() { }
+ };
+
+ std::stringstream desc;
+ desc << "Are you sure you want to delete " << searchModel->GetSelected().size() << " save";
+ if(searchModel->GetSelected().size()>1)
+ desc << "s";
+ new ConfirmPrompt("Delete saves", desc.str(), new RemoveSelectedConfirmation(this));
+}
+
+void SearchController::removeSelectedC()
+{
+ class RemoveSavesTask : public Task
+ {
+ std::vector<int> saves;
+ public:
+ RemoveSavesTask(std::vector<int> saves_) { saves = saves_; }
+ virtual bool doWork()
+ {
+ for(int i = 0; i < saves.size(); i++)
+ {
+ std::stringstream saveID;
+ saveID << "Deleting save [" << saves[i] << "] ...";
+ notifyStatus(saveID.str());
+ if(Client::Ref().DeleteSave(saves[i])!=RequestOkay)
+ {
+ std::stringstream saveIDF;
+ saveIDF << "\boFailed to delete [" << saves[i] << "] ...";
+ notifyStatus(saveIDF.str());
+ }
+ notifyProgress((float(i+1)/float(saves.size())*100));
+ }
+ return true;
+ }
+ };
+
+ std::vector<int> selected = searchModel->GetSelected();
+ new TaskWindow("Removing saves", new RemoveSavesTask(selected));
+ ClearSelection();
+ searchModel->UpdateSaveList(searchModel->GetPageNum(), searchModel->GetLastQuery());
+}
+
+void SearchController::UnpublishSelected()
+{
+ class UnpublishSelectedConfirmation: public ConfirmDialogueCallback {
+ public:
+ SearchController * c;
+ UnpublishSelectedConfirmation(SearchController * c_) { c = c_; }
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ c->unpublishSelectedC();
+ }
+ virtual ~UnpublishSelectedConfirmation() { }
+ };
+
+ std::stringstream desc;
+ desc << "Are you sure you want to hide " << searchModel->GetSelected().size() << " save";
+ if(searchModel->GetSelected().size()>1)
+ desc << "s";
+ new ConfirmPrompt("Unpublish saves", desc.str(), new UnpublishSelectedConfirmation(this));
+}
+
+void SearchController::unpublishSelectedC()
+{
+ class UnpublishSavesTask : public Task
+ {
+ std::vector<int> saves;
+ public:
+ UnpublishSavesTask(std::vector<int> saves_) { saves = saves_; }
+ virtual bool doWork()
+ {
+ for(int i = 0; i < saves.size(); i++)
+ {
+ std::stringstream saveID;
+ saveID << "Hiding save [" << saves[i] << "] ...";
+ notifyStatus(saveID.str());
+ if(Client::Ref().UnpublishSave(saves[i])!=RequestOkay)
+ {
+ std::stringstream saveIDF;
+ saveIDF << "\boFailed to hide [" << saves[i] << "] ...";
+ notifyStatus(saveIDF.str());
+ }
+ notifyProgress((float(i+1)/float(saves.size())*100));
+ }
+ return true;
+ }
+ };
+
+ std::vector<int> selected = searchModel->GetSelected();
+ new TaskWindow("Unpublishing saves", new UnpublishSavesTask(selected));
+ ClearSelection();
+ searchModel->UpdateSaveList(searchModel->GetPageNum(), searchModel->GetLastQuery());
+}
+
+void SearchController::FavouriteSelected()
+{
+ class FavouriteSavesTask : public Task
+ {
+ std::vector<int> saves;
+ public:
+ FavouriteSavesTask(std::vector<int> saves_) { saves = saves_; }
+ virtual bool doWork()
+ {
+ for(int i = 0; i < saves.size(); i++)
+ {
+ std::stringstream saveID;
+ saveID << "Favouring save [" << saves[i] << "] ...";
+ notifyStatus(saveID.str());
+ if(Client::Ref().FavouriteSave(saves[i], true)!=RequestOkay)
+ {
+ std::stringstream saveIDF;
+ saveIDF << "\boFailed to favourite [" << saves[i] << "] ...";
+ notifyStatus(saveIDF.str());
+ }
+ notifyProgress((float(i+1)/float(saves.size())*100));
+ }
+ return true;
+ }
+ };
+
+ class UnfavouriteSavesTask : public Task
+ {
+ std::vector<int> saves;
+ public:
+ UnfavouriteSavesTask(std::vector<int> saves_) { saves = saves_; }
+ virtual bool doWork()
+ {
+ for(int i = 0; i < saves.size(); i++)
+ {
+ std::stringstream saveID;
+ saveID << "Unfavouring save [" << saves[i] << "] ...";
+ notifyStatus(saveID.str());
+ if(Client::Ref().FavouriteSave(saves[i], false)!=RequestOkay)
+ {
+ std::stringstream saveIDF;
+ saveIDF << "\boFailed to remove [" << saves[i] << "] ...";
+ notifyStatus(saveIDF.str());
+ }
+ notifyProgress((float(i+1)/float(saves.size())*100));
+ }
+ return true;
+ }
+ };
+
+ std::vector<int> selected = searchModel->GetSelected();
+ if(!searchModel->GetShowFavourite())
+ new TaskWindow("Favouring saves", new FavouriteSavesTask(selected));
+ else
+ new TaskWindow("Unfavouring saves", new UnfavouriteSavesTask(selected));
+ ClearSelection();
+}
diff --git a/src/search/SearchController.h b/src/search/SearchController.h
new file mode 100644
index 0000000..66d464a
--- /dev/null
+++ b/src/search/SearchController.h
@@ -0,0 +1,50 @@
+#ifndef SEARCHCONTROLLER_H
+#define SEARCHCONTROLLER_H
+
+#include "interface/Panel.h"
+#include "SearchModel.h"
+#include "SearchView.h"
+#include "preview/PreviewController.h"
+#include "Controller.h"
+#include "client/SaveInfo.h"
+
+class SearchView;
+class SearchModel;
+class SearchController
+{
+private:
+ SearchModel * searchModel;
+ SearchView * searchView;
+ PreviewController * activePreview;
+ ControllerCallback * callback;
+
+ double nextQueryTime;
+ std::string nextQuery;
+ bool nextQueryDone;
+ void removeSelectedC();
+ void unpublishSelectedC();
+public:
+ class OpenCallback;
+ bool HasExited;
+ SearchController(ControllerCallback * callback = NULL);
+ ~SearchController();
+ SearchView * GetView() { return searchView; }
+ void Exit();
+ void DoSearch(std::string query, bool now = false);
+ void NextPage();
+ void PrevPage();
+ void ChangeSort();
+ void ShowOwn(bool show);
+ void ShowFavourite(bool show);
+ void Selected(int saveID, bool selected);
+ void OpenSave(int saveID);
+ void OpenSave(int saveID, int saveDate);
+ void Update();
+ void ClearSelection();
+ void RemoveSelected();
+ void UnpublishSelected();
+ void FavouriteSelected();
+ SaveInfo * GetLoadedSave();
+};
+
+#endif // SEARCHCONTROLLER_H
diff --git a/src/search/SearchModel.cpp b/src/search/SearchModel.cpp
new file mode 100644
index 0000000..913e95b
--- /dev/null
+++ b/src/search/SearchModel.cpp
@@ -0,0 +1,283 @@
+#include "SearchModel.h"
+#include "client/SaveInfo.h"
+
+#include "client/Client.h"
+
+SearchModel::SearchModel():
+ currentSort("best"),
+ showOwn(false),
+ showFavourite(false),
+ loadedSave(NULL),
+ updateSaveListWorking(false),
+ updateSaveListFinished(false),
+ updateTagListWorking(false),
+ updateTagListFinished(false),
+ saveListLoaded(false),
+ currentPage(1),
+ resultCount(0),
+ showTags(true)
+{
+}
+
+void SearchModel::SetShowTags(bool show)
+{
+ showTags = show;
+}
+
+bool SearchModel::GetShowTags()
+{
+ return showTags;
+}
+
+void * SearchModel::updateSaveListTHelper(void * obj)
+{
+ return ((SearchModel *)obj)->updateSaveListT();
+}
+
+void * SearchModel::updateSaveListT()
+{
+ std::string category = "";
+ if(showFavourite)
+ category = "Favourites";
+ if(showOwn && Client::Ref().GetAuthUser().ID)
+ category = "by:"+Client::Ref().GetAuthUser().Username;
+ vector<SaveInfo*> * saveList = Client::Ref().SearchSaves((currentPage-1)*20, 20, lastQuery, currentSort=="new"?"date":"votes", category, thResultCount);
+
+ updateSaveListFinished = true;
+ return saveList;
+}
+
+void * SearchModel::updateTagListTHelper(void * obj)
+{
+ return ((SearchModel *)obj)->updateTagListT();
+}
+
+void * SearchModel::updateTagListT()
+{
+ int tagResultCount;
+ std::vector<std::pair<std::string, int> > * tagList = Client::Ref().GetTags(0, 24, "", tagResultCount);
+
+ updateTagListFinished = true;
+ return tagList;
+}
+
+void SearchModel::UpdateSaveList(int pageNumber, std::string query)
+{
+ //Threading
+ if(!updateSaveListWorking && !updateTagListWorking)
+ {
+ lastQuery = query;
+ lastError = "";
+ saveListLoaded = false;
+ saveList.clear();
+ //resultCount = 0;
+ currentPage = pageNumber;
+
+ if(pageNumber == 1 && !showOwn && !showFavourite && currentSort == "best" && query == "")
+ SetShowTags(true);
+ else
+ SetShowTags(false);
+
+ notifySaveListChanged();
+ notifyTagListChanged();
+ notifyPageChanged();
+ selected.clear();
+ notifySelectedChanged();
+
+ if(GetShowTags() && !tagList.size())
+ {
+ updateTagListFinished = false;
+ updateTagListWorking = true;
+ pthread_create(&updateTagListThread, 0, &SearchModel::updateTagListTHelper, this);
+ }
+
+ updateSaveListFinished = false;
+ updateSaveListWorking = true;
+ pthread_create(&updateSaveListThread, 0, &SearchModel::updateSaveListTHelper, this);
+ }
+}
+
+void SearchModel::SetLoadedSave(SaveInfo * save)
+{
+ if(loadedSave != save && loadedSave)
+ delete loadedSave;
+ if(save)
+ {
+ loadedSave = new SaveInfo(*save);
+ }
+ else
+ {
+ loadedSave = NULL;
+ }
+}
+
+SaveInfo * SearchModel::GetLoadedSave(){
+ return loadedSave;
+}
+
+vector<SaveInfo*> SearchModel::GetSaveList()
+{
+ return saveList;
+}
+
+vector<pair<string, int> > SearchModel::GetTagList()
+{
+ return tagList;
+}
+
+void SearchModel::Update()
+{
+ if(updateSaveListWorking)
+ {
+ if(updateSaveListFinished)
+ {
+ updateSaveListWorking = false;
+ lastError = "";
+ saveListLoaded = true;
+
+ vector<SaveInfo*> * tempSaveList;
+ pthread_join(updateSaveListThread, (void**)&tempSaveList);
+
+ if(tempSaveList)
+ {
+ saveList = *tempSaveList;
+ delete tempSaveList;
+ }
+
+ if(!saveList.size())
+ {
+ lastError = Client::Ref().GetLastError();
+ }
+
+ resultCount = thResultCount;
+ notifyPageChanged();
+ notifySaveListChanged();
+ }
+ }
+ if(updateTagListWorking)
+ {
+ if(updateTagListFinished)
+ {
+ updateTagListWorking = false;
+
+ vector<pair<string, int> > * tempTagList;
+ pthread_join(updateTagListThread, (void**)&tempTagList);
+
+ if(tempTagList)
+ {
+ tagList = *tempTagList;
+ delete tempTagList;
+ }
+ notifyTagListChanged();
+ }
+ }
+}
+
+void SearchModel::AddObserver(SearchView * observer)
+{
+ observers.push_back(observer);
+ observer->NotifySaveListChanged(this);
+ observer->NotifyPageChanged(this);
+ observer->NotifySortChanged(this);
+ observer->NotifyShowOwnChanged(this);
+ observer->NotifyTagListChanged(this);
+}
+
+void SearchModel::SelectSave(int saveID)
+{
+ for(int i = 0; i < selected.size(); i++)
+ {
+ if(selected[i]==saveID)
+ {
+ return;
+ }
+ }
+ selected.push_back(saveID);
+ notifySelectedChanged();
+}
+
+void SearchModel::DeselectSave(int saveID)
+{
+ bool changed = false;
+restart:
+ for(int i = 0; i < selected.size(); i++)
+ {
+ if(selected[i]==saveID)
+ {
+ selected.erase(selected.begin()+i);
+ changed = true;
+ goto restart; //Just ensure all cases are removed.
+ }
+ }
+ if(changed)
+ notifySelectedChanged();
+}
+
+void SearchModel::notifySaveListChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ SearchView* cObserver = observers[i];
+ cObserver->NotifySaveListChanged(this);
+ }
+}
+
+void SearchModel::notifyTagListChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ SearchView* cObserver = observers[i];
+ cObserver->NotifyTagListChanged(this);
+ }
+}
+
+void SearchModel::notifyPageChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ SearchView* cObserver = observers[i];
+ cObserver->NotifyPageChanged(this);
+ }
+}
+
+void SearchModel::notifySortChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ SearchView* cObserver = observers[i];
+ cObserver->NotifySortChanged(this);
+ }
+}
+
+void SearchModel::notifyShowOwnChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ SearchView* cObserver = observers[i];
+ cObserver->NotifyShowOwnChanged(this);
+ }
+}
+
+void SearchModel::notifyShowFavouriteChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ SearchView* cObserver = observers[i];
+ cObserver->NotifyShowOwnChanged(this);
+ }
+}
+
+void SearchModel::notifySelectedChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ SearchView* cObserver = observers[i];
+ cObserver->NotifySelectedChanged(this);
+ }
+}
+
+SearchModel::~SearchModel()
+{
+ if(loadedSave)
+ delete loadedSave;
+}
diff --git a/src/search/SearchModel.h b/src/search/SearchModel.h
new file mode 100644
index 0000000..9e93f05
--- /dev/null
+++ b/src/search/SearchModel.h
@@ -0,0 +1,83 @@
+#ifndef SEARCHMODEL_H
+#define SEARCHMODEL_H
+
+#include <vector>
+#include <string>
+#include <pthread.h>
+#undef GetUserName //God dammit microsoft!
+#include <cmath>
+#include "client/SaveInfo.h"
+#include "SearchView.h"
+
+using namespace std;
+
+class SearchView;
+class SearchModel
+{
+private:
+ SaveInfo * loadedSave;
+ string currentSort;
+ string lastQuery;
+ string lastError;
+ vector<int> selected;
+ vector<SearchView*> observers;
+ vector<SaveInfo*> saveList;
+ vector<pair<string, int> > tagList;
+ int currentPage;
+ int resultCount;
+ int thResultCount;
+ bool showOwn;
+ bool showFavourite;
+ bool showTags;
+ void notifySaveListChanged();
+ void notifyTagListChanged();
+ void notifySelectedChanged();
+ void notifyPageChanged();
+ void notifySortChanged();
+ void notifyShowOwnChanged();
+ void notifyShowFavouriteChanged();
+
+ //Variables and methods for background save request
+ bool saveListLoaded;
+ bool updateSaveListWorking;
+ volatile bool updateSaveListFinished;
+ pthread_t updateSaveListThread;
+ static void * updateSaveListTHelper(void * obj);
+ void * updateSaveListT();
+
+ bool updateTagListWorking;
+ volatile bool updateTagListFinished;
+ pthread_t updateTagListThread;
+ static void * updateTagListTHelper(void * obj);
+ void * updateTagListT();
+public:
+ SearchModel();
+ virtual ~SearchModel();
+
+ void SetShowTags(bool show);
+ bool GetShowTags();
+ void AddObserver(SearchView * observer);
+ void UpdateSaveList(int pageNumber, std::string query);
+ vector<SaveInfo*> GetSaveList();
+ vector<pair<string, int> > GetTagList();
+ string GetLastError() { return lastError; }
+ int GetPageCount() { return max(1, (int)(ceil(resultCount/16.0f))); }
+ int GetPageNum() { return currentPage; }
+ std::string GetLastQuery() { return lastQuery; }
+ void SetSort(string sort) { if(!updateSaveListWorking) { currentSort = sort; } notifySortChanged(); }
+ string GetSort() { return currentSort; }
+ void SetShowOwn(bool show) { if(!updateSaveListWorking) { if(show!=showOwn) { showOwn = show; } } notifyShowOwnChanged(); }
+ bool GetShowOwn() { return showOwn; }
+ void SetShowFavourite(bool show) { if(show!=showFavourite && !updateSaveListWorking) { showFavourite = show; } notifyShowFavouriteChanged(); }
+ bool GetShowFavourite() { return showFavourite; }
+ void SetLoadedSave(SaveInfo * save);
+ SaveInfo * GetLoadedSave();
+ bool GetSavesLoaded() { return saveListLoaded; }
+ vector<int> GetSelected() { return selected; }
+ void ClearSelected() { selected.clear(); notifySelectedChanged(); }
+ void SelectSave(int saveID);
+ void DeselectSave(int saveID);
+ void Update();
+};
+
+#endif // SEARCHMODEL_H
diff --git a/src/search/SearchView.cpp b/src/search/SearchView.cpp
new file mode 100644
index 0000000..c1d6597
--- /dev/null
+++ b/src/search/SearchView.cpp
@@ -0,0 +1,703 @@
+#include <sstream>
+
+#include "SearchView.h"
+#include "client/Client.h"
+#include "interface/Keys.h"
+#include "interface/SaveButton.h"
+#include "interface/Label.h"
+#include "interface/RichLabel.h"
+#include "interface/Textbox.h"
+#include "Misc.h"
+
+SearchView::SearchView():
+ ui::Window(ui::Point(0, 0), ui::Point(XRES+BARSIZE, YRES+MENUSIZE)),
+ saveButtons(vector<ui::SaveButton*>()),
+ errorLabel(NULL),
+ c(NULL)
+{
+
+ Client::Ref().AddListener(this);
+
+ nextButton = new ui::Button(ui::Point(XRES+BARSIZE-52, YRES+MENUSIZE-18), ui::Point(50, 16), "Next \x95");
+ previousButton = new ui::Button(ui::Point(1, YRES+MENUSIZE-18), ui::Point(50, 16), "\x96 Prev");
+ infoLabel = new ui::Label(ui::Point(260, YRES+MENUSIZE-18), ui::Point(XRES+BARSIZE-520, 16), "Page 1 of 1");
+ tagsLabel = new ui::Label(ui::Point(270, YRES+MENUSIZE-18), ui::Point(XRES+BARSIZE-540, 16), "\boPopular Tags:");
+ motdLabel = new ui::RichLabel(ui::Point(51, YRES+MENUSIZE-18), ui::Point(XRES+BARSIZE-102, 16), Client::Ref().GetMessageOfTheDay());
+
+ class SearchAction : public ui::TextboxAction
+ {
+ SearchView * v;
+ public:
+ SearchAction(SearchView * _v) { v = _v; }
+ void TextChangedCallback(ui::Textbox * sender)
+ {
+ v->doSearch();
+ }
+ };
+ searchField = new ui::Textbox(ui::Point(60, 10), ui::Point((XRES+BARSIZE)-238, 17), "", "[search]");
+ searchField->Appearance.icon = IconSearch;
+ searchField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ searchField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ searchField->SetActionCallback(new SearchAction(this));
+
+
+ class SortAction : public ui::ButtonAction
+ {
+ SearchView * v;
+ public:
+ SortAction(SearchView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->ChangeSort();
+ }
+ };
+ sortButton = new ui::Button(ui::Point(XRES+BARSIZE-140, 10), ui::Point(61, 17), "Sort");
+ sortButton->SetIcon(IconVoteSort);
+ sortButton->SetActionCallback(new SortAction(this));
+ sortButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
+ sortButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(sortButton);
+
+ class MyOwnAction : public ui::ButtonAction
+ {
+ SearchView * v;
+ public:
+ MyOwnAction(SearchView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->ShowOwn(sender->GetToggleState());
+ }
+ };
+ ownButton = new ui::Button(ui::Point(XRES+BARSIZE-70, 10), ui::Point(61, 17), "My Own");
+ ownButton->SetIcon(IconMyOwn);
+ ownButton->SetTogglable(true);
+ ownButton->SetActionCallback(new MyOwnAction(this));
+ ownButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
+ ownButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(ownButton);
+
+ class FavAction : public ui::ButtonAction
+ {
+ SearchView * v;
+ public:
+ FavAction(SearchView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->ShowFavourite(sender->GetToggleState());
+ }
+ };
+ favButton = new ui::Button(searchField->Position+ui::Point(searchField->Size.X+15, 0), ui::Point(17, 17), "");
+ favButton->SetIcon(IconFavourite);
+ favButton->SetTogglable(true);
+ favButton->Appearance.Margin.Left+=2;
+ favButton->SetActionCallback(new FavAction(this));
+ favButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
+ favButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ favButton->Appearance.BorderInactive = ui::Colour(170,170,170);
+ AddComponent(favButton);
+
+ class ClearSearchAction : public ui::ButtonAction
+ {
+ SearchView * v;
+ public:
+ ClearSearchAction(SearchView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->clearSearch();
+ }
+ };
+ ui::Button * clearSearchButton = new ui::Button(searchField->Position+ui::Point(searchField->Size.X-1, 0), ui::Point(17, 17), "");
+ clearSearchButton->SetIcon(IconClose);
+ clearSearchButton->SetActionCallback(new ClearSearchAction(this));
+ clearSearchButton->Appearance.Margin.Left+=2;
+ clearSearchButton->Appearance.Margin.Top+=2;
+ clearSearchButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
+ clearSearchButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ clearSearchButton->Appearance.BorderInactive = ui::Colour(170,170,170);
+ AddComponent(clearSearchButton);
+
+ class NextPageAction : public ui::ButtonAction
+ {
+ SearchView * v;
+ public:
+ NextPageAction(SearchView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->NextPage();
+ }
+ };
+ nextButton->SetActionCallback(new NextPageAction(this));
+ nextButton->Appearance.HorizontalAlign = ui::Appearance::AlignRight;
+ nextButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ class PrevPageAction : public ui::ButtonAction
+ {
+ SearchView * v;
+ public:
+ PrevPageAction(SearchView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->PrevPage();
+ }
+ };
+ previousButton->SetActionCallback(new PrevPageAction(this));
+ previousButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ previousButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(nextButton);
+ AddComponent(previousButton);
+ AddComponent(searchField);
+ AddComponent(infoLabel);
+
+ loadingSpinner = new ui::Spinner(ui::Point(((XRES+BARSIZE)/2)-12, ((YRES+MENUSIZE)/2)+12), ui::Point(24, 24));
+ AddComponent(loadingSpinner);
+
+ ui::Label * searchPrompt = new ui::Label(ui::Point(10, 10), ui::Point(50, 16), "Search:");
+ searchPrompt->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ searchPrompt->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(searchPrompt);
+
+ class RemoveSelectedAction : public ui::ButtonAction
+ {
+ SearchView * v;
+ public:
+ RemoveSelectedAction(SearchView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->RemoveSelected();
+ }
+ };
+
+ class UnpublishSelectedAction : public ui::ButtonAction
+ {
+ SearchView * v;
+ public:
+ UnpublishSelectedAction(SearchView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->UnpublishSelected();
+ }
+ };
+
+ class FavouriteSelectedAction : public ui::ButtonAction
+ {
+ SearchView * v;
+ public:
+ FavouriteSelectedAction(SearchView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->FavouriteSelected();
+ }
+ };
+
+ class ClearSelectionAction : public ui::ButtonAction
+ {
+ SearchView * v;
+ public:
+ ClearSelectionAction(SearchView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->ClearSelection();
+ }
+ };
+
+ removeSelected = new ui::Button(ui::Point((((XRES+BARSIZE)-415)/2), YRES+MENUSIZE-18), ui::Point(100, 16), "Delete");
+ removeSelected->Visible = false;
+ removeSelected->SetActionCallback(new RemoveSelectedAction(this));
+ AddComponent(removeSelected);
+
+ unpublishSelected = new ui::Button(ui::Point((((XRES+BARSIZE)-415)/2)+105, YRES+MENUSIZE-18), ui::Point(100, 16), "Unpublish");
+ unpublishSelected->Visible = false;
+ unpublishSelected->SetActionCallback(new UnpublishSelectedAction(this));
+ AddComponent(unpublishSelected);
+
+ favouriteSelected = new ui::Button(ui::Point((((XRES+BARSIZE)-415)/2)+210, YRES+MENUSIZE-18), ui::Point(100, 16), "Favourite");
+ favouriteSelected->Visible = false;
+ favouriteSelected->SetActionCallback(new FavouriteSelectedAction(this));
+ AddComponent(favouriteSelected);
+
+ clearSelection = new ui::Button(ui::Point((((XRES+BARSIZE)-415)/2)+315, YRES+MENUSIZE-18), ui::Point(100, 16), "Clear selection");
+ clearSelection->Visible = false;
+ clearSelection->SetActionCallback(new ClearSelectionAction(this));
+ AddComponent(clearSelection);
+
+ CheckAccess();
+}
+
+void SearchView::NotifyMessageOfTheDay(Client * sender)
+{
+ motdLabel->SetText(sender->GetMessageOfTheDay());
+}
+
+void SearchView::doSearch()
+{
+ c->DoSearch(searchField->GetText());
+}
+
+
+void SearchView::clearSearch()
+{
+ searchField->SetText("");
+ c->DoSearch(searchField->GetText(), true);
+}
+
+void SearchView::OnTryOkay(OkayMethod method)
+{
+ c->DoSearch(searchField->GetText(), true);
+}
+
+SearchView::~SearchView()
+{
+ Client::Ref().RemoveListener(this);
+ RemoveComponent(nextButton);
+ RemoveComponent(previousButton);
+ RemoveComponent(infoLabel);
+
+ delete nextButton;
+ delete previousButton;
+ delete infoLabel;
+}
+
+void SearchView::Search(std::string query)
+{
+ searchField->SetText(query);
+ c->DoSearch(query, true);
+}
+
+void SearchView::NotifySortChanged(SearchModel * sender)
+{
+ if(sender->GetSort() == "best")
+ {
+ sortButton->SetText("By votes");
+ sortButton->SetIcon(IconVoteSort);
+ }
+ else
+ {
+ sortButton->SetText("By date");
+ sortButton->SetIcon(IconDateSort);
+ }
+}
+
+void SearchView::NotifyShowOwnChanged(SearchModel * sender)
+{
+ ownButton->SetToggleState(sender->GetShowOwn());
+ if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator)
+ {
+ unpublishSelected->Enabled = true;
+ removeSelected->Enabled = true;
+ }
+ else if(sender->GetShowFavourite())
+ {
+ unpublishSelected->Enabled = false;
+ removeSelected->Enabled = false;
+ }
+ else
+ {
+ unpublishSelected->Enabled = false;
+ removeSelected->Enabled = false;
+ }
+}
+
+void SearchView::NotifyShowFavouriteChanged(SearchModel * sender)
+{
+ favButton->SetToggleState(sender->GetShowFavourite());
+ if(sender->GetShowFavourite())
+ {
+ unpublishSelected->Enabled = false;
+ removeSelected->Enabled = false;
+ }
+ else if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator)
+ {
+ unpublishSelected->Enabled = true;
+ removeSelected->Enabled = true;
+ }
+ else
+ {
+ unpublishSelected->Enabled = false;
+ removeSelected->Enabled = false;
+ }
+}
+
+void SearchView::NotifyPageChanged(SearchModel * sender)
+{
+ std::stringstream pageInfo;
+ pageInfo << "Page " << sender->GetPageNum() << " of " << sender->GetPageCount();
+ infoLabel->SetText(pageInfo.str());
+ if(sender->GetPageNum() == 1)
+ {
+ previousButton->Visible = false;
+ }
+ else
+ {
+ previousButton->Visible = true;
+ }
+ if(sender->GetPageNum() == sender->GetPageCount())
+ {
+ nextButton->Visible = false;
+ }
+ else
+ {
+ nextButton->Visible = true;
+ }
+}
+
+void SearchView::NotifyAuthUserChanged(Client * sender)
+{
+ CheckAccess();
+}
+
+void SearchView::CheckAccess()
+{
+ if(c)
+ {
+ c->ClearSelection();
+
+ if(ownButton->GetToggleState())
+ ownButton->DoAction();
+ if(favButton->GetToggleState())
+ favButton->DoAction();
+ }
+
+ if(Client::Ref().GetAuthUser().ID)
+ {
+ ownButton->Enabled = true;
+ favButton->Enabled = true;
+ favouriteSelected->Enabled = true;
+
+ if(Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator)
+ {
+ unpublishSelected->Enabled = true;
+ removeSelected->Enabled = true;
+ for(int i = 0; i < saveButtons.size(); i++)
+ {
+ saveButtons[i]->SetSelectable(true);
+ }
+ }
+
+ }
+ else
+ {
+ ownButton->Enabled = false;
+ favButton->Enabled = false;
+
+
+ favouriteSelected->Enabled = false;
+ unpublishSelected->Enabled = false;
+ removeSelected->Enabled = false;
+
+ for(int i = 0; i < saveButtons.size(); i++)
+ {
+ saveButtons[i]->SetSelectable(false);
+ saveButtons[i]->SetSelected(false);
+ }
+ }
+}
+
+void SearchView::NotifyTagListChanged(SearchModel * sender)
+{
+ int i = 0;
+ int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 1;
+ int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset;
+
+ int tagWidth, tagHeight, tagX = 0, tagY = 0, tagsX = 6, tagsY = 4, tagPadding = 1;
+ int tagAreaWidth, tagAreaHeight, tagXOffset, tagYOffset;
+
+ vector<pair<string, int> > tags = sender->GetTagList();
+
+ RemoveComponent(motdLabel);
+ motdLabel->SetParentWindow(NULL);
+
+ RemoveComponent(tagsLabel);
+ tagsLabel->SetParentWindow(NULL);
+
+ for(i = 0; i < tagButtons.size(); i++)
+ {
+ RemoveComponent(tagButtons[i]);
+ delete tagButtons[i];
+ }
+ tagButtons.clear();
+
+ buttonYOffset = 28;
+ buttonXOffset = buttonPadding;
+ buttonAreaWidth = Size.X;
+ buttonAreaHeight = Size.Y - buttonYOffset - 18;
+
+ if(sender->GetShowTags())
+ {
+ buttonYOffset += (buttonAreaHeight/savesY) - buttonPadding*2;
+ buttonAreaHeight = Size.Y - buttonYOffset - 18;
+ savesY--;
+
+ tagXOffset = tagPadding;
+ tagYOffset = 60;
+ tagAreaWidth = Size.X;
+ tagAreaHeight = ((buttonAreaHeight/savesY) - buttonPadding*2)-(tagYOffset-28)-5;
+ tagWidth = (tagAreaWidth/tagsX) - tagPadding*2;
+ tagHeight = (tagAreaHeight/tagsY) - tagPadding*2;
+
+ AddComponent(tagsLabel);
+ tagsLabel->Position.Y = tagYOffset-16;
+
+ AddComponent(motdLabel);
+ motdLabel->Position.Y = tagYOffset-30;
+ }
+
+ class TagAction: public ui::ButtonAction
+ {
+ SearchView * v;
+ std::string tag;
+ public:
+ TagAction(SearchView * v, std::string tag) : v(v), tag(tag) {}
+ virtual void ActionCallback(ui::Button * sender)
+ {
+ v->Search(tag);
+ }
+ };
+ if(sender->GetShowTags())
+ {
+ for(i = 0; i < tags.size(); i++)
+ {
+ int maxTagVotes = tags[0].second;
+
+ pair<string, int> tag = tags[i];
+
+ if(tagX == tagsX)
+ {
+ if(tagY == tagsY-1)
+ break;
+ tagX = 0;
+ tagY++;
+ }
+
+ int tagAlpha = 192;
+ if (maxTagVotes)
+ tagAlpha = 127+(128*tag.second)/maxTagVotes;
+
+ ui::Button * tagButton;
+ tagButton = new ui::Button(
+ ui::Point(
+ tagXOffset + tagPadding + tagX*(tagWidth+tagPadding*2),
+ tagYOffset + tagPadding + tagY*(tagHeight+tagPadding*2)
+ ),
+ ui::Point(tagWidth, tagHeight),
+ tag.first
+ );
+ tagButton->SetActionCallback(new TagAction(this, tag.first));
+ tagButton->Appearance.BorderInactive = ui::Colour(0, 0, 0);
+ tagButton->Appearance.BorderHover = ui::Colour(0, 0, 0);
+ tagButton->Appearance.BorderActive = ui::Colour(0, 0, 0);
+ tagButton->Appearance.BackgroundHover = ui::Colour(0, 0, 0);
+
+ tagButton->Appearance.TextInactive = ui::Colour(tagAlpha, tagAlpha, tagAlpha);
+ tagButton->Appearance.TextHover = ui::Colour((tagAlpha*5)/6, (tagAlpha*5)/6, tagAlpha);
+ AddComponent(tagButton);
+ tagButtons.push_back(tagButton);
+ tagX++;
+
+ }
+ }
+}
+
+void SearchView::NotifySaveListChanged(SearchModel * sender)
+{
+ int i = 0;
+ int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 1;
+ int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset;
+
+ int tagWidth, tagHeight, tagX = 0, tagY = 0, tagsX = 6, tagsY = 4, tagPadding = 1;
+ int tagAreaWidth, tagAreaHeight, tagXOffset, tagYOffset;
+
+ vector<SaveInfo*> saves = sender->GetSaveList();
+ //string messageOfTheDay = sender->GetMessageOfTheDay();
+
+ if(sender->GetShowFavourite())
+ favouriteSelected->SetText("Unfavourite");
+ else
+ favouriteSelected->SetText("Favourite");
+
+ Client::Ref().ClearThumbnailRequests();
+ for(i = 0; i < saveButtons.size(); i++)
+ {
+ RemoveComponent(saveButtons[i]);
+ delete saveButtons[i];
+ }
+ saveButtons.clear();
+ if(!sender->GetSavesLoaded())
+ {
+ nextButton->Enabled = false;
+ previousButton->Enabled = false;
+ favButton->Enabled = false;
+ }
+ else
+ {
+ nextButton->Enabled = true;
+ previousButton->Enabled = true;
+ if (Client::Ref().GetAuthUser().ID)
+ favButton->Enabled = true;
+ }
+ if (!sender->GetSavesLoaded() || favButton->GetToggleState())
+ {
+ ownButton->Enabled = false;
+ sortButton->Enabled = false;
+ }
+ else
+ {
+ if (Client::Ref().GetAuthUser().ID)
+ ownButton->Enabled = true;
+ sortButton->Enabled = true;
+ }
+ if(!saves.size())
+ {
+ loadingSpinner->Visible = false;
+ if(!errorLabel)
+ {
+ errorLabel = new ui::Label(ui::Point(((XRES+BARSIZE)/2)-100, ((YRES+MENUSIZE)/2)-6), ui::Point(200, 12), "Error");
+ AddComponent(errorLabel);
+ }
+ if(!sender->GetSavesLoaded())
+ {
+ errorLabel->SetText("Loading...");
+ loadingSpinner->Visible = true;
+ }
+ else
+ {
+ if(sender->GetLastError().length())
+ errorLabel->SetText("\bo" + sender->GetLastError());
+ else
+ errorLabel->SetText("\boNo saves found");
+ }
+ }
+ else
+ {
+ loadingSpinner->Visible = false;
+ if(errorLabel)
+ {
+ RemoveComponent(errorLabel);
+ delete errorLabel;
+ errorLabel = NULL;
+ }
+
+ buttonYOffset = 28;
+ buttonXOffset = buttonPadding;
+ buttonAreaWidth = Size.X;
+ buttonAreaHeight = Size.Y - buttonYOffset - 18;
+
+ if(sender->GetShowTags())
+ {
+ buttonYOffset += (buttonAreaHeight/savesY) - buttonPadding*2;
+ buttonAreaHeight = Size.Y - buttonYOffset - 18;
+ savesY--;
+
+ tagXOffset = tagPadding;
+ tagYOffset = 60;
+ tagAreaWidth = Size.X;
+ tagAreaHeight = ((buttonAreaHeight/savesY) - buttonPadding*2)-(tagYOffset-28)-5;
+ tagWidth = (tagAreaWidth/tagsX) - tagPadding*2;
+ tagHeight = (tagAreaHeight/tagsY) - tagPadding*2;
+ }
+
+ buttonWidth = (buttonAreaWidth/savesX) - buttonPadding*2;
+ buttonHeight = (buttonAreaHeight/savesY) - buttonPadding*2;
+
+
+
+ class SaveOpenAction: public ui::SaveButtonAction
+ {
+ SearchView * v;
+ public:
+ SaveOpenAction(SearchView * _v) { v = _v; }
+ virtual void ActionCallback(ui::SaveButton * sender)
+ {
+ v->c->OpenSave(sender->GetSave()->GetID(), sender->GetSave()->GetVersion());
+ }
+ virtual void SelectedCallback(ui::SaveButton * sender)
+ {
+ v->c->Selected(sender->GetSave()->GetID(), sender->GetSelected());
+ }
+ virtual void AuthorActionCallback(ui::SaveButton * sender)
+ {
+ v->Search("user:"+sender->GetSave()->GetUserName());
+ }
+ virtual void HistoryActionCallback(ui::SaveButton * sender)
+ {
+ stringstream search;
+ search << "history:" << sender->GetSave()->GetID();
+ v->Search(search.str());
+ }
+ };
+ for(i = 0; i < saves.size(); i++)
+ {
+ if(saveX == savesX)
+ {
+ if(saveY == savesY-1)
+ break;
+ saveX = 0;
+ saveY++;
+ }
+ ui::SaveButton * saveButton;
+ saveButton = new ui::SaveButton(
+ ui::Point(
+ buttonXOffset + buttonPadding + saveX*(buttonWidth+buttonPadding*2),
+ buttonYOffset + buttonPadding + saveY*(buttonHeight+buttonPadding*2)
+ ),
+ ui::Point(buttonWidth, buttonHeight),
+ saves[i]);
+ saveButton->SetActionCallback(new SaveOpenAction(this));
+ if(Client::Ref().GetAuthUser().ID)
+ saveButton->SetSelectable(true);
+ if (saves[i]->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator)
+ saveButton->SetShowVotes(true);
+ saveButtons.push_back(saveButton);
+ AddComponent(saveButton);
+ saveX++;
+ }
+ }
+}
+
+void SearchView::NotifySelectedChanged(SearchModel * sender)
+{
+ vector<int> selected = sender->GetSelected();
+ for(int j = 0; j < saveButtons.size(); j++)
+ {
+ saveButtons[j]->SetSelected(false);
+ for(int i = 0; i < selected.size(); i++)
+ {
+ if(saveButtons[j]->GetSave()->GetID()==selected[i])
+ saveButtons[j]->SetSelected(true);
+ }
+ }
+
+ if(selected.size())
+ {
+ removeSelected->Visible = true;
+ unpublishSelected->Visible = true;
+ favouriteSelected->Visible = true;
+ clearSelection->Visible = true;
+ }
+ else
+ {
+ removeSelected->Visible = false;
+ unpublishSelected->Visible = false;
+ favouriteSelected->Visible = false;
+ clearSelection->Visible = false;
+ }
+}
+
+void SearchView::OnTick(float dt)
+{
+ c->Update();
+}
+
+void SearchView::OnMouseWheel(int x, int y, int d)
+{
+ if(!d)
+ return;
+ if(d<0)
+ c->NextPage();
+ else
+ c->PrevPage();
+}
+void SearchView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ if(key==KEY_ESCAPE)
+ c->Exit();
+}
+
diff --git a/src/search/SearchView.h b/src/search/SearchView.h
new file mode 100644
index 0000000..05ecde2
--- /dev/null
+++ b/src/search/SearchView.h
@@ -0,0 +1,74 @@
+#ifndef SEARCHVIEW_H
+#define SEARCHVIEW_H
+
+#include <vector>
+#include "SearchController.h"
+#include "interface/SaveButton.h"
+#include "interface/Button.h"
+#include "interface/Label.h"
+#include "interface/Spinner.h"
+#include "interface/Textbox.h"
+#include "client/ClientListener.h"
+
+using namespace std;
+
+namespace ui
+{
+ class RichLabel;
+ class SaveButton;
+ class Button;
+ class Label;
+ class Spinner;
+ class Textbox;
+}
+
+class SearchModel;
+class SearchController;
+
+class SearchView: public ui::Window, public ClientListener
+{
+private:
+ SearchController * c;
+ vector<ui::SaveButton*> saveButtons;
+ vector<ui::Button*> tagButtons;
+ ui::Button * favButton;
+ ui::Button * nextButton;
+ ui::Button * previousButton;
+ ui::Label * errorLabel;
+ ui::Textbox * searchField;
+ ui::Label * infoLabel;
+ ui::Label * tagsLabel;
+ ui::RichLabel * motdLabel;
+ ui::Button * sortButton;
+ ui::Button * ownButton;
+ ui::Spinner * loadingSpinner;
+
+ ui::Button * removeSelected;
+ ui::Button * unpublishSelected;
+ ui::Button * favouriteSelected;
+ ui::Button * clearSelection;
+ void clearSearch();
+ void doSearch();
+public:
+ void NotifyTagListChanged(SearchModel * sender);
+ void NotifySaveListChanged(SearchModel * sender);
+ void NotifySelectedChanged(SearchModel * sender);
+ void NotifyPageChanged(SearchModel * sender);
+ void NotifySortChanged(SearchModel * sender);
+ void NotifyShowOwnChanged(SearchModel * sender);
+ void NotifyShowFavouriteChanged(SearchModel * sender);
+ void NotifyAuthUserChanged(Client * sender);
+ void NotifyMessageOfTheDay(Client * sender);
+ void CheckAccess();
+ virtual void OnTryOkay(OkayMethod method);
+ SearchView();
+ virtual ~SearchView();
+ void AttachController(SearchController * _c) { c = _c; }
+ virtual void Search(std::string);
+ virtual void OnTick(float dt);
+ virtual void OnMouseWheel(int x, int y, int d);
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+
+};
+
+#endif // SEARCHVIEW_H
diff --git a/src/search/Thumbnail.cpp b/src/search/Thumbnail.cpp
new file mode 100644
index 0000000..e1bc6f8
--- /dev/null
+++ b/src/search/Thumbnail.cpp
@@ -0,0 +1,82 @@
+/*
+ * Thumbnail.cpp
+ *
+ * Created on: Apr 3, 2012
+ * Author: Simon
+ */
+
+#include "Thumbnail.h"
+
+Thumbnail::Thumbnail(const Thumbnail & thumb):
+ ID(thumb.ID),
+ Datestamp(thumb.Datestamp),
+ Data(thumb.Data),
+ Size(thumb.Size)
+{
+ //Ensure the actual thumbnail data is copied
+ if(thumb.Data)
+ {
+ Data = (pixel *)malloc((thumb.Size.X*thumb.Size.Y) * PIXELSIZE);
+ memcpy(Data, thumb.Data, (thumb.Size.X*thumb.Size.Y) * PIXELSIZE);
+ }
+ else
+ {
+ Data = NULL;
+ }
+}
+
+Thumbnail::Thumbnail(int _id, int _datestamp, pixel * _data, ui::Point _size):
+ ID(_id),
+ Datestamp(_datestamp),
+ Data(_data),
+ Size(_size)
+{
+ if(_data)
+ {
+ Data = (pixel *)malloc((_size.X*_size.Y) * PIXELSIZE);
+ memcpy(Data, _data, (_size.X*_size.Y) * PIXELSIZE);
+ }
+ else
+ {
+ Data = NULL;
+ }
+}
+
+void Thumbnail::Resize(int width, int height)
+{
+ Resize(ui::Point(width, height));
+}
+
+void Thumbnail::Resize(ui::Point newSize)
+{
+ float scaleFactorX = 1.0f, scaleFactorY = 1.0f;
+ if(Size.Y > newSize.Y)
+ {
+ scaleFactorY = float(newSize.Y)/((float)Size.Y);
+ }
+ if(Size.X > newSize.X)
+ {
+ scaleFactorX = float(newSize.X)/((float)Size.X);
+ }
+ if(newSize.X == -1)
+ scaleFactorX = scaleFactorY;
+ if(newSize.Y == -1)
+ scaleFactorY = scaleFactorX;
+ if(scaleFactorY < 1.0f || scaleFactorX < 1.0f)
+ {
+ float scaleFactor = scaleFactorY < scaleFactorX ? scaleFactorY : scaleFactorX;
+ pixel * thumbData = Data;
+ Data = Graphics::resample_img(thumbData, Size.X, Size.Y, Size.X * scaleFactor, Size.Y * scaleFactor);
+ Size.X *= scaleFactor;
+ Size.Y *= scaleFactor;
+ free(thumbData);
+ }
+}
+
+Thumbnail::~Thumbnail()
+{
+ if(Data)
+ {
+ free(Data);
+ }
+}
diff --git a/src/search/Thumbnail.h b/src/search/Thumbnail.h
new file mode 100644
index 0000000..74ebbad
--- /dev/null
+++ b/src/search/Thumbnail.h
@@ -0,0 +1,25 @@
+#ifndef THUMBNAIL_H
+#define THUMBNAIL_H
+
+#include <iostream>
+#include "graphics/Graphics.h"
+#include "interface/Point.h"
+
+class Thumbnail
+{
+public:
+ Thumbnail(const Thumbnail & thumb);
+
+ Thumbnail(int _id, int _datestamp, pixel * _data, ui::Point _size);
+
+ ~Thumbnail();
+
+ void Resize(int Width, int Height);
+ void Resize(ui::Point newSize);
+
+ int ID, Datestamp;
+ ui::Point Size;
+ pixel * Data;
+};
+
+#endif // THUMBNAIL_H
diff --git a/src/simulation/Air.cpp b/src/simulation/Air.cpp
new file mode 100644
index 0000000..10c4569
--- /dev/null
+++ b/src/simulation/Air.cpp
@@ -0,0 +1,321 @@
+#include <cmath>
+#include <algorithm>
+#include "Config.h"
+#include "Air.h"
+#include "Simulation.h"
+//#include <powder.h>
+//#include <defines.h>
+#include "Gravity.h"
+
+/*float kernel[9];
+
+float vx[YRES/CELL][XRES/CELL], ovx[YRES/CELL][XRES/CELL];
+float vy[YRES/CELL][XRES/CELL], ovy[YRES/CELL][XRES/CELL];
+float pv[YRES/CELL][XRES/CELL], opv[YRES/CELL][XRES/CELL];
+unsigned char bmap_blockair[YRES/CELL][XRES/CELL];
+
+float cb_vx[YRES/CELL][XRES/CELL];
+float cb_vy[YRES/CELL][XRES/CELL];
+float cb_pv[YRES/CELL][XRES/CELL];
+float cb_hv[YRES/CELL][XRES/CELL];
+
+float fvx[YRES/CELL][XRES/CELL], fvy[YRES/CELL][XRES/CELL];
+
+float hv[YRES/CELL][XRES/CELL], ohv[YRES/CELL][XRES/CELL]; // For Ambient Heat */
+
+void Air::make_kernel(void) //used for velocity
+{
+ int i, j;
+ float s = 0.0f;
+ for (j=-1; j<2; j++)
+ for (i=-1; i<2; i++)
+ {
+ kernel[(i+1)+3*(j+1)] = expf(-2.0f*(i*i+j*j));
+ s += kernel[(i+1)+3*(j+1)];
+ }
+ s = 1.0f / s;
+ for (j=-1; j<2; j++)
+ for (i=-1; i<2; i++)
+ kernel[(i+1)+3*(j+1)] *= s;
+}
+
+void Air::Clear()
+{
+ std::fill(&hv[0][0], &hv[0][0]+((XRES/CELL)*(YRES/CELL)), 273.15f + 22.0f);
+ std::fill(&pv[0][0], &pv[0][0]+((XRES/CELL)*(YRES/CELL)), 0.0f);
+ std::fill(&vy[0][0], &vy[0][0]+((XRES/CELL)*(YRES/CELL)), 0.0f);
+ std::fill(&vx[0][0], &vx[0][0]+((XRES/CELL)*(YRES/CELL)), 0.0f);
+}
+
+void Air::update_airh(void)
+{
+ int x, y, i, j;
+ float odh, dh, dx, dy, f, tx, ty;
+ for (i=0; i<YRES/CELL; i++) //reduces pressure/velocity on the edges every frame
+ {
+ hv[i][0] = 295.15f;
+ hv[i][1] = 295.15f;
+ hv[i][XRES/CELL-3] = 295.15f;
+ hv[i][XRES/CELL-2] = 295.15f;
+ hv[i][XRES/CELL-1] = 295.15f;
+ }
+ for (i=0; i<XRES/CELL; i++) //reduces pressure/velocity on the edges every frame
+ {
+ hv[0][i] = 295.15f;
+ hv[1][i] = 295.15f;
+ hv[YRES/CELL-3][i] = 295.15f;
+ hv[YRES/CELL-2][i] = 295.15f;
+ hv[YRES/CELL-1][i] = 295.15f;
+ }
+ for (y=0; y<YRES/CELL; y++) //update velocity and pressure
+ {
+ for (x=0; x<XRES/CELL; x++)
+ {
+ dh = 0.0f;
+ dx = 0.0f;
+ dy = 0.0f;
+ for (j=-1; j<2; j++)
+ {
+ for (i=-1; i<2; i++)
+ {
+ if (y+j>0 && y+j<YRES/CELL-2 &&
+ x+i>0 && x+i<XRES/CELL-2 &&
+ !bmap_blockairh[y+j][x+i])
+ {
+ f = kernel[i+1+(j+1)*3];
+ dh += hv[y+j][x+i]*f;
+ dx += vx[y+j][x+i]*f;
+ dy += vy[y+j][x+i]*f;
+ }
+ else
+ {
+ f = kernel[i+1+(j+1)*3];
+ dh += hv[y][x]*f;
+ dx += vx[y][x]*f;
+ dy += vy[y][x]*f;
+ }
+ }
+ }
+ tx = x - dx*0.7f;
+ ty = y - dy*0.7f;
+ i = (int)tx;
+ j = (int)ty;
+ tx -= i;
+ ty -= j;
+ if (i>=2 && i<XRES/CELL-3 && j>=2 && j<YRES/CELL-3)
+ {
+ odh = dh;
+ dh *= 1.0f - AIR_VADV;
+ dh += AIR_VADV*(1.0f-tx)*(1.0f-ty)*(bmap_blockairh[j][i] ? odh : hv[j][i]);
+ dh += AIR_VADV*tx*(1.0f-ty)*(bmap_blockairh[j][i+1] ? odh : hv[j][i+1]);
+ dh += AIR_VADV*(1.0f-tx)*ty*(bmap_blockairh[j+1][i] ? odh : hv[j+1][i]);
+ dh += AIR_VADV*tx*ty*(bmap_blockairh[j+1][i+1] ? odh : hv[j+1][i+1]);
+ }
+ if(!sim.gravityMode)
+ { //Vertical gravity only for the time being
+ float airdiff = hv[y-1][x]-hv[y][x];
+ if(airdiff>0 && !bmap_blockairh[y-1][x])
+ vy[y][x] -= airdiff/5000.0f;
+ }
+ ohv[y][x] = dh;
+ }
+ }
+ memcpy(hv, ohv, sizeof(hv));
+}
+
+void Air::update_air(void)
+{
+ int x = 0, y = 0, i = 0, j = 0;
+ float dp = 0.0f, dx = 0.0f, dy = 0.0f, f = 0.0f, tx = 0.0f, ty = 0.0f;
+
+ if (airMode != 4) { //airMode 4 is no air/pressure update
+
+ for (i=0; i<YRES/CELL; i++) //reduces pressure/velocity on the edges every frame
+ {
+ pv[i][0] = pv[i][0]*0.8f;
+ pv[i][1] = pv[i][1]*0.8f;
+ pv[i][2] = pv[i][2]*0.8f;
+ pv[i][XRES/CELL-2] = pv[i][XRES/CELL-2]*0.8f;
+ pv[i][XRES/CELL-1] = pv[i][XRES/CELL-1]*0.8f;
+ vx[i][0] = vx[i][1]*0.9f;
+ vx[i][1] = vx[i][2]*0.9f;
+ vx[i][XRES/CELL-2] = vx[i][XRES/CELL-3]*0.9f;
+ vx[i][XRES/CELL-1] = vx[i][XRES/CELL-2]*0.9f;
+ vy[i][0] = vy[i][1]*0.9f;
+ vy[i][1] = vy[i][2]*0.9f;
+ vy[i][XRES/CELL-2] = vy[i][XRES/CELL-3]*0.9f;
+ vy[i][XRES/CELL-1] = vy[i][XRES/CELL-2]*0.9f;
+ }
+ for (i=0; i<XRES/CELL; i++) //reduces pressure/velocity on the edges every frame
+ {
+ pv[0][i] = pv[0][i]*0.8f;
+ pv[1][i] = pv[1][i]*0.8f;
+ pv[2][i] = pv[2][i]*0.8f;
+ pv[YRES/CELL-2][i] = pv[YRES/CELL-2][i]*0.8f;
+ pv[YRES/CELL-1][i] = pv[YRES/CELL-1][i]*0.8f;
+ vx[0][i] = vx[1][i]*0.9f;
+ vx[1][i] = vx[2][i]*0.9f;
+ vx[YRES/CELL-2][i] = vx[YRES/CELL-3][i]*0.9f;
+ vx[YRES/CELL-1][i] = vx[YRES/CELL-2][i]*0.9f;
+ vy[0][i] = vy[1][i]*0.9f;
+ vy[1][i] = vy[2][i]*0.9f;
+ vy[YRES/CELL-2][i] = vy[YRES/CELL-3][i]*0.9f;
+ vy[YRES/CELL-1][i] = vy[YRES/CELL-2][i]*0.9f;
+ }
+
+ for (j=1; j<YRES/CELL; j++) //clear some velocities near walls
+ {
+ for (i=1; i<XRES/CELL; i++)
+ {
+ if (bmap_blockair[j][i])
+ {
+ vx[j][i] = 0.0f;
+ vx[j][i-1] = 0.0f;
+ vy[j][i] = 0.0f;
+ vy[j-1][i] = 0.0f;
+ }
+ }
+ }
+
+ for (y=1; y<YRES/CELL; y++) //pressure adjustments from velocity
+ for (x=1; x<XRES/CELL; x++)
+ {
+ dp = 0.0f;
+ dp += vx[y][x-1] - vx[y][x];
+ dp += vy[y-1][x] - vy[y][x];
+ pv[y][x] *= AIR_PLOSS;
+ pv[y][x] += dp*AIR_TSTEPP;
+ }
+
+ for (y=0; y<YRES/CELL-1; y++) //velocity adjustments from pressure
+ for (x=0; x<XRES/CELL-1; x++)
+ {
+ dx = dy = 0.0f;
+ dx += pv[y][x] - pv[y][x+1];
+ dy += pv[y][x] - pv[y+1][x];
+ vx[y][x] *= AIR_VLOSS;
+ vy[y][x] *= AIR_VLOSS;
+ vx[y][x] += dx*AIR_TSTEPV;
+ vy[y][x] += dy*AIR_TSTEPV;
+ if (bmap_blockair[y][x] || bmap_blockair[y][x+1])
+ vx[y][x] = 0;
+ if (bmap_blockair[y][x] || bmap_blockair[y+1][x])
+ vy[y][x] = 0;
+ }
+
+ for (y=0; y<YRES/CELL; y++) //update velocity and pressure
+ for (x=0; x<XRES/CELL; x++)
+ {
+ dx = 0.0f;
+ dy = 0.0f;
+ dp = 0.0f;
+ for (j=-1; j<2; j++)
+ for (i=-1; i<2; i++)
+ if (y+j>0 && y+j<YRES/CELL-1 &&
+ x+i>0 && x+i<XRES/CELL-1 &&
+ !bmap_blockair[y+j][x+i])
+ {
+ f = kernel[i+1+(j+1)*3];
+ dx += vx[y+j][x+i]*f;
+ dy += vy[y+j][x+i]*f;
+ dp += pv[y+j][x+i]*f;
+ }
+ else
+ {
+ f = kernel[i+1+(j+1)*3];
+ dx += vx[y][x]*f;
+ dy += vy[y][x]*f;
+ dp += pv[y][x]*f;
+ }
+
+ tx = x - dx*0.7f;
+ ty = y - dy*0.7f;
+ i = (int)tx;
+ j = (int)ty;
+ tx -= i;
+ ty -= j;
+ if (i>=2 && i<XRES/CELL-3 &&
+ j>=2 && j<YRES/CELL-3)
+ {
+ dx *= 1.0f - AIR_VADV;
+ dy *= 1.0f - AIR_VADV;
+
+ dx += AIR_VADV*(1.0f-tx)*(1.0f-ty)*vx[j][i];
+ dy += AIR_VADV*(1.0f-tx)*(1.0f-ty)*vy[j][i];
+
+ dx += AIR_VADV*tx*(1.0f-ty)*vx[j][i+1];
+ dy += AIR_VADV*tx*(1.0f-ty)*vy[j][i+1];
+
+ dx += AIR_VADV*(1.0f-tx)*ty*vx[j+1][i];
+ dy += AIR_VADV*(1.0f-tx)*ty*vy[j+1][i];
+
+ dx += AIR_VADV*tx*ty*vx[j+1][i+1];
+ dy += AIR_VADV*tx*ty*vy[j+1][i+1];
+ }
+
+ if (bmap[y][x] == WL_FAN)
+ {
+ dx += fvx[y][x];
+ dy += fvy[y][x];
+ }
+ // pressure/velocity caps
+ if (dp > 256.0f) dp = 256.0f;
+ if (dp < -256.0f) dp = -256.0f;
+ if (dx > 256.0f) dx = 256.0f;
+ if (dx < -256.0f) dx = -256.0f;
+ if (dy > 256.0f) dy = 256.0f;
+ if (dy < -256.0f) dy = -256.0f;
+
+
+ switch (airMode)
+ {
+ default:
+ case 0: //Default
+ break;
+ case 1: //0 Pressure
+ dp = 0.0f;
+ break;
+ case 2: //0 Velocity
+ dx = 0.0f;
+ dy = 0.0f;
+ break;
+ case 3: //0 Air
+ dx = 0.0f;
+ dy = 0.0f;
+ dp = 0.0f;
+ break;
+ case 4: //No Update
+ break;
+ }
+
+ ovx[y][x] = dx;
+ ovy[y][x] = dy;
+ opv[y][x] = dp;
+ }
+ memcpy(vx, ovx, sizeof(vx));
+ memcpy(vy, ovy, sizeof(vy));
+ memcpy(pv, opv, sizeof(pv));
+ }
+}
+
+void Air::Invert()
+{
+ int nx, ny;
+ for (nx = 0; nx<XRES/CELL; nx++)
+ for (ny = 0; ny<YRES/CELL; ny++)
+ {
+ pv[ny][nx] = -pv[ny][nx];
+ vx[ny][nx] = -vx[ny][nx];
+ vy[ny][nx] = -vy[ny][nx];
+ }
+}
+
+Air::Air(Simulation & simulation):
+ airMode(0),
+ sim(simulation)
+{
+ //Simulation should do this.
+ make_kernel();
+
+
+}
diff --git a/src/simulation/Air.h b/src/simulation/Air.h
new file mode 100644
index 0000000..936f54b
--- /dev/null
+++ b/src/simulation/Air.h
@@ -0,0 +1,37 @@
+#ifndef AIR_H
+#define AIR_H
+#include "Config.h"
+
+class Simulation;
+
+class Air
+{
+public:
+ Simulation & sim;
+ int airMode;
+ //Arrays from the simulation
+ unsigned char (*bmap)[XRES/CELL];
+ unsigned char (*emap)[XRES/CELL];
+ float (*fvx)[XRES/CELL];
+ float (*fvy)[XRES/CELL];
+ //
+ float vx[YRES/CELL][XRES/CELL];
+ float ovx[YRES/CELL][XRES/CELL];
+ float vy[YRES/CELL][XRES/CELL];
+ float ovy[YRES/CELL][XRES/CELL];
+ float pv[YRES/CELL][XRES/CELL];
+ float opv[YRES/CELL][XRES/CELL];
+ float hv[YRES/CELL][XRES/CELL];
+ float ohv[YRES/CELL][XRES/CELL]; // Ambient Heat
+ unsigned char bmap_blockair[YRES/CELL][XRES/CELL];
+ unsigned char bmap_blockairh[YRES/CELL][XRES/CELL];
+ float kernel[9];
+ void make_kernel(void);
+ void update_airh(void);
+ void update_air(void);
+ void Clear();
+ void Invert();
+ Air(Simulation & sim);
+};
+
+#endif
diff --git a/src/simulation/Element.h b/src/simulation/Element.h
new file mode 100644
index 0000000..3c28e2f
--- /dev/null
+++ b/src/simulation/Element.h
@@ -0,0 +1,23 @@
+#ifndef ELEMENT_H
+#define ELEMENT_H
+// This header should be included by all files in src/elements/
+
+#include <cmath>
+#include "Simulation.h"
+#include "graphics/Renderer.h"
+#include "Gravity.h"
+#include "Misc.h"
+#include "ElementGraphics.h"
+
+#define IPL -257.0f
+#define IPH 257.0f
+#define ITL MIN_TEMP-1
+#define ITH MAX_TEMP+1
+
+// no transition (PT_NONE means kill part)
+#define NT -1
+
+// special transition - lava ctypes etc need extra code, which is only found and run if ST is given
+#define ST PT_NUM
+
+#endif
diff --git a/src/simulation/ElementGraphics.h b/src/simulation/ElementGraphics.h
new file mode 100644
index 0000000..0f3723c
--- /dev/null
+++ b/src/simulation/ElementGraphics.h
@@ -0,0 +1,55 @@
+#ifndef PGRAPHICS_H
+#define PGRAPHICS_H
+
+#define PMODE 0x00000FFF
+#define PMODE_NONE 0x00000000
+#define PMODE_FLAT 0x00000001
+#define PMODE_BLOB 0x00000002
+#define PMODE_BLUR 0x00000004
+#define PMODE_GLOW 0x00000008
+#define PMODE_SPARK 0x00000010
+#define PMODE_FLARE 0x00000020
+#define PMODE_LFLARE 0x00000040
+#define PMODE_ADD 0x00000080
+#define PMODE_BLEND 0x00000100
+#define PSPEC_STICKMAN 0x00000200
+
+#define OPTIONS 0x0000F000
+#define NO_DECO 0x00001000
+#define DECO_FIRE 0x00002000
+
+#define FIREMODE 0x00FF0000
+#define FIRE_ADD 0x00010000
+#define FIRE_BLEND 0x00020000
+
+#define EFFECT 0xFF000000
+#define EFFECT_GRAVIN 0x01000000
+#define EFFECT_GRAVOUT 0x02000000
+#define EFFECT_LINES 0x04000000
+#define EFFECT_DBGLINES 0x08000000
+
+#define RENDER_EFFE OPTIONS | PSPEC_STICKMAN | EFFECT | PMODE_SPARK | PMODE_FLARE | PMODE_LFLARE
+#define RENDER_FIRE OPTIONS | PSPEC_STICKMAN | /*PMODE_FLAT |*/ PMODE_ADD | PMODE_BLEND | FIREMODE
+#define RENDER_GLOW OPTIONS | PSPEC_STICKMAN | /*PMODE_FLAT |*/ PMODE_GLOW | PMODE_ADD | PMODE_BLEND
+#define RENDER_BLUR OPTIONS | PSPEC_STICKMAN | /*PMODE_FLAT |*/ PMODE_BLUR | PMODE_ADD | PMODE_BLEND
+#define RENDER_BLOB OPTIONS | PSPEC_STICKMAN | /*PMODE_FLAT |*/ PMODE_BLOB | PMODE_ADD | PMODE_BLEND
+#define RENDER_BASC OPTIONS | PSPEC_STICKMAN | PMODE_FLAT | PMODE_ADD | PMODE_BLEND
+#define RENDER_NONE OPTIONS | PSPEC_STICKMAN | PMODE_FLAT
+
+#define COLOUR_HEAT 0x00000001
+#define COLOUR_LIFE 0x00000002
+#define COLOUR_GRAD 0x00000004
+#define COLOUR_BASC 0x00000008
+
+#define COLOUR_DEFAULT 0x00000000
+
+#define DISPLAY_AIRC 0x00000001
+#define DISPLAY_AIRP 0x00000002
+#define DISPLAY_AIRV 0x00000004
+#define DISPLAY_AIRH 0x00000008
+#define DISPLAY_AIR 0x0000000F
+#define DISPLAY_WARP 0x00000010
+#define DISPLAY_PERS 0x00000020
+#define DISPLAY_EFFE 0x00000040
+
+#endif
diff --git a/src/simulation/Elements.h b/src/simulation/Elements.h
new file mode 100644
index 0000000..781a8f8
--- /dev/null
+++ b/src/simulation/Elements.h
@@ -0,0 +1,100 @@
+/*
+ * Elements.h
+ *
+ * Created on: Jan 5, 2012
+ * Author: Simon
+ */
+
+#ifndef ELEMENTS_H_
+#define ELEMENTS_H_
+
+//#include "Config.h"
+//#include "Simulation.h"
+
+#define R_TEMP 22
+#define MAX_TEMP 9999
+#define MIN_TEMP 0
+#define O_MAX_TEMP 3500
+#define O_MIN_TEMP -273
+
+#define TYPE_PART 0x00001 //1 Powders
+#define TYPE_LIQUID 0x00002 //2 Liquids
+#define TYPE_SOLID 0x00004 //4 Solids
+#define TYPE_GAS 0x00008 //8 Gasses (Includes plasma)
+#define TYPE_ENERGY 0x00010 //16 Energy (Thunder, Light, Neutrons etc.)
+#define PROP_CONDUCTS 0x00020 //32 Conducts electricity
+#define PROP_BLACK 0x00040 //64 Absorbs Photons (not currently implemented or used, a photwl attribute might be better)
+#define PROP_NEUTPENETRATE 0x00080 //128 Penetrated by neutrons
+#define PROP_NEUTABSORB 0x00100 //256 Absorbs neutrons, reflect is default
+#define PROP_NEUTPASS 0x00200 //512 Neutrons pass through, such as with glass
+#define PROP_DEADLY 0x00400 //1024 Is deadly for stickman
+#define PROP_HOT_GLOW 0x00800 //2048 Hot Metal Glow
+#define PROP_LIFE 0x01000 //4096 Is a GoL type
+#define PROP_RADIOACTIVE 0x02000 //8192 Radioactive
+#define PROP_LIFE_DEC 0x04000 //2^14 Life decreases by one every frame if > zero
+#define PROP_LIFE_KILL 0x08000 //2^15 Kill when life value is <= zero
+#define PROP_LIFE_KILL_DEC 0x10000 //2^16 Kill when life value is decremented to <= zero
+#define PROP_SPARKSETTLE 0x20000 //2^17 Allow Sparks/Embers to settle
+#define PROP_NOAMBHEAT 0x40000 //2^18 Don't transfer or receive heat from ambient heat.
+
+#define FLAG_STAGNANT 1
+#define FLAG_SKIPMOVE 0x2 // skip movement for one frame, only implemented for PHOT
+#define FLAG_MOVABLE 0x4 // if can move
+
+#define ST_NONE 0
+#define ST_SOLID 1
+#define ST_LIQUID 2
+#define ST_GAS 3
+
+#define UPDATE_FUNC_ARGS Simulation* sim, int i, int x, int y, int surround_space, int nt, Particle *parts, int pmap[YRES][XRES]
+#define UPDATE_FUNC_SUBCALL_ARGS sim, i, x, y, surround_space, nt, parts, pmap
+
+#define GRAPHICS_FUNC_ARGS Renderer * ren, Particle *cpart, int nx, int ny, int *pixel_mode, int* cola, int *colr, int *colg, int *colb, int *firea, int *firer, int *fireg, int *fireb
+#define GRAPHICS_FUNC_SUBCALL_ARGS ren, cpart, nx, ny, pixel_mode, cola, colr, colg, colb, firea, firer, fireg, fireb
+
+#define SPC_AIR 236
+#define SPC_HEAT 237
+#define SPC_COOL 238
+#define SPC_VACUUM 239
+#define SPC_WIND 241
+#define SPC_PGRV 243
+#define SPC_NGRV 244
+#define SPC_PROP 246
+
+
+#define NGT_GOL 0
+#define NGT_HLIF 1
+#define NGT_ASIM 2
+#define NGT_2x2 3
+#define NGT_DANI 4
+#define NGT_AMOE 5
+#define NGT_MOVE 6
+#define NGT_PGOL 7
+#define NGT_DMOE 8
+#define NGT_34 9
+#define NGT_LLIF 10
+#define NGT_STAN 11
+#define NGT_SEED 12
+#define NGT_MAZE 13
+#define NGT_COAG 14
+#define NGT_WALL 15
+#define NGT_GNAR 16
+#define NGT_REPL 17
+#define NGT_MYST 18
+#define NGT_LOTE 19
+#define NGT_FRG2 20
+#define NGT_STAR 21
+#define NGT_FROG 22
+#define NGT_BRAN 23
+
+#define OLD_PT_WIND 147
+
+//#define PT_NUM 161
+#define PT_NUM 256
+
+struct playerst;
+
+#include "ElementClasses.h"
+
+
+#endif /* ELEMENTS_H_ */
diff --git a/src/simulation/GOLMenu.h b/src/simulation/GOLMenu.h
new file mode 100644
index 0000000..36bb5dd
--- /dev/null
+++ b/src/simulation/GOLMenu.h
@@ -0,0 +1,20 @@
+//
+// GOLMenu.h
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 04/06/2012.
+// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#ifndef The_Powder_Toy_GOLMenu_h
+#define The_Powder_Toy_GOLMenu_h
+
+struct gol_menu
+{
+ const char *name;
+ pixel colour;
+ int goltype;
+ const char *description;
+};
+
+#endif
diff --git a/src/simulation/Gravity.cpp b/src/simulation/Gravity.cpp
new file mode 100644
index 0000000..552c02d
--- /dev/null
+++ b/src/simulation/Gravity.cpp
@@ -0,0 +1,527 @@
+#include <cmath>
+#include <sys/types.h>
+#include <pthread.h>
+#undef GetUserName //God dammit microsoft!
+#include "Config.h"
+#include "Gravity.h"
+//#include "powder.h"
+
+void Gravity::bilinear_interpolation(float *src, float *dst, int sw, int sh, int rw, int rh)
+{
+ int y, x, fxceil, fyceil;
+ float fx, fy, fyc, fxc;
+ double intp;
+ float tr, tl, br, bl;
+ //Bilinear interpolation for upscaling
+ for (y=0; y<rh; y++)
+ for (x=0; x<rw; x++)
+ {
+ fx = ((float)x)*((float)sw)/((float)rw);
+ fy = ((float)y)*((float)sh)/((float)rh);
+ fxc = modf(fx, &intp);
+ fyc = modf(fy, &intp);
+ fxceil = (int)ceil(fx);
+ fyceil = (int)ceil(fy);
+ if (fxceil>=sw) fxceil = sw-1;
+ if (fyceil>=sh) fyceil = sh-1;
+ tr = src[sw*(int)floor(fy)+fxceil];
+ tl = src[sw*(int)floor(fy)+(int)floor(fx)];
+ br = src[sw*fyceil+fxceil];
+ bl = src[sw*fyceil+(int)floor(fx)];
+ dst[rw*y+x] = ((tl*(1.0f-fxc))+(tr*(fxc)))*(1.0f-fyc) + ((bl*(1.0f-fxc))+(br*(fxc)))*(fyc);
+ }
+}
+
+void Gravity::Clear()
+{
+ std::fill(gravy, gravy+((XRES/CELL)*(YRES/CELL)), 0.0f);
+ std::fill(gravx, gravx+((XRES/CELL)*(YRES/CELL)), 0.0f);
+ std::fill(gravp, gravp+((XRES/CELL)*(YRES/CELL)), 0.0f);
+ std::fill(gravmap, gravmap+((XRES/CELL)*(YRES/CELL)), 0.0f);
+ std::fill(gravmask, gravmask+((XRES/CELL)*(YRES/CELL)), 0xFFFFFFFF);
+}
+
+void Gravity::gravity_init()
+{
+ ngrav_enable = 0;
+ //Allocate full size Gravmaps
+ th_ogravmap = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float));
+ th_gravmap = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float));
+ th_gravy = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float));
+ th_gravx = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float));
+ th_gravp = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float));
+ gravmap = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float));
+ gravy = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float));
+ gravx = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float));
+ gravp = (float *)calloc((XRES/CELL)*(YRES/CELL), sizeof(float));
+ gravmask = (unsigned int *)calloc((XRES/CELL)*(YRES/CELL), sizeof(unsigned));
+}
+
+void Gravity::gravity_cleanup()
+{
+#ifdef GRAVFFT
+ grav_fft_cleanup();
+#endif
+ //Free gravity info
+ free(th_ogravmap);
+ free(th_gravmap);
+ free(th_gravy);
+ free(th_gravx);
+ free(th_gravp);
+ free(gravmap);
+ free(gravy);
+ free(gravx);
+ free(gravp);
+ free(gravmask);
+}
+
+void Gravity::gravity_update_async()
+{
+ int result;
+ if(ngrav_enable)
+ {
+ pthread_mutex_lock(&gravmutex);
+ result = grav_ready;
+ if(result) //Did the gravity thread finish?
+ {
+ //if (!sys_pause||framerender){ //Only update if not paused
+ //Switch the full size gravmaps, we don't really need the two above any more
+ float *tmpf;
+
+ if(th_gravchanged)
+ {
+ #if !defined(GRAVFFT) && defined(GRAV_DIFF)
+ memcpy(gravy, th_gravy, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memcpy(gravx, th_gravx, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memcpy(gravp, th_gravp, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ #else
+ tmpf = gravy;
+ gravy = th_gravy;
+ th_gravy = tmpf;
+
+ tmpf = gravx;
+ gravx = th_gravx;
+ th_gravx = tmpf;
+
+ tmpf = gravp;
+ gravp = th_gravp;
+ th_gravp = tmpf;
+ #endif
+ }
+
+ tmpf = gravmap;
+ gravmap = th_gravmap;
+ th_gravmap = tmpf;
+
+ grav_ready = 0; //Tell the other thread that we're ready for it to continue
+ pthread_cond_signal(&gravcv);
+ //}
+ }
+ pthread_mutex_unlock(&gravmutex);
+ //Apply the gravity mask
+ membwand(gravy, gravmask, (XRES/CELL)*(YRES/CELL)*sizeof(float), (XRES/CELL)*(YRES/CELL)*sizeof(unsigned));
+ membwand(gravx, gravmask, (XRES/CELL)*(YRES/CELL)*sizeof(float), (XRES/CELL)*(YRES/CELL)*sizeof(unsigned));
+ memset(gravmap, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ }
+}
+
+void *Gravity::update_grav_async_helper(void * context)
+{
+ ((Gravity *)context)->update_grav_async();
+ return NULL;
+}
+
+void Gravity::update_grav_async()
+{
+ int done = 0;
+ int thread_done = 0;
+ memset(th_ogravmap, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memset(th_gravmap, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memset(th_gravy, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memset(th_gravx, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memset(th_gravp, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ //memset(th_gravy, 0, XRES*YRES*sizeof(float));
+ //memset(th_gravx, 0, XRES*YRES*sizeof(float));
+ //memset(th_gravp, 0, XRES*YRES*sizeof(float));
+ while(!thread_done){
+ if(!done){
+ update_grav();
+ done = 1;
+ pthread_mutex_lock(&gravmutex);
+
+ grav_ready = done;
+ thread_done = gravthread_done;
+
+ pthread_mutex_unlock(&gravmutex);
+ } else {
+ pthread_mutex_lock(&gravmutex);
+ pthread_cond_wait(&gravcv, &gravmutex);
+
+ done = grav_ready;
+ thread_done = gravthread_done;
+
+ pthread_mutex_unlock(&gravmutex);
+ }
+ }
+ pthread_exit(NULL);
+}
+
+void Gravity::start_grav_async()
+{
+ if(ngrav_enable) //If it's already enabled, restart it
+ stop_grav_async();
+
+ gravthread_done = 0;
+ grav_ready = 0;
+ pthread_mutex_init (&gravmutex, NULL);
+ pthread_cond_init(&gravcv, NULL);
+ pthread_create(&gravthread, NULL, &Gravity::update_grav_async_helper, this); //Start asynchronous gravity simulation
+ ngrav_enable = 1;
+
+ memset(gravy, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memset(gravx, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memset(gravp, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memset(gravmap, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+}
+
+void Gravity::stop_grav_async()
+{
+ if(ngrav_enable){
+ pthread_mutex_lock(&gravmutex);
+ gravthread_done = 1;
+ pthread_cond_signal(&gravcv);
+ pthread_mutex_unlock(&gravmutex);
+ pthread_join(gravthread, NULL);
+ pthread_mutex_destroy(&gravmutex); //Destroy the mutex
+ ngrav_enable = 0;
+ }
+ //Clear the grav velocities
+ memset(gravy, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memset(gravx, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memset(gravp, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memset(gravmap, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+}
+
+#ifdef GRAVFFT
+
+void Gravity::grav_fft_init()
+{
+ int xblock2 = XRES/CELL*2;
+ int yblock2 = YRES/CELL*2;
+ int x, y, fft_tsize = (xblock2/2+1)*yblock2;
+ float distance, scaleFactor;
+ fftwf_plan plan_ptgravx, plan_ptgravy;
+ if (grav_fft_status) return;
+
+ //use fftw malloc function to ensure arrays are aligned, to get better performance
+ th_ptgravx = (float*)fftwf_malloc(xblock2*yblock2*sizeof(float));
+ th_ptgravy = (float*)fftwf_malloc(xblock2*yblock2*sizeof(float));
+ th_ptgravxt = (fftwf_complex*)fftwf_malloc(fft_tsize*sizeof(fftwf_complex));
+ th_ptgravyt = (fftwf_complex*)fftwf_malloc(fft_tsize*sizeof(fftwf_complex));
+ th_gravmapbig = (float*)fftwf_malloc(xblock2*yblock2*sizeof(float));
+ th_gravmapbigt = (fftwf_complex*)fftwf_malloc(fft_tsize*sizeof(fftwf_complex));
+ th_gravxbig = (float*)fftwf_malloc(xblock2*yblock2*sizeof(float));
+ th_gravybig = (float*)fftwf_malloc(xblock2*yblock2*sizeof(float));
+ th_gravxbigt = (fftwf_complex*)fftwf_malloc(fft_tsize*sizeof(fftwf_complex));
+ th_gravybigt = (fftwf_complex*)fftwf_malloc(fft_tsize*sizeof(fftwf_complex));
+
+ //select best algorithm, could use FFTW_PATIENT or FFTW_EXHAUSTIVE but that increases the time taken to plan, and I don't see much increase in execution speed
+ plan_ptgravx = fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_ptgravx, th_ptgravxt, FFTW_MEASURE);
+ plan_ptgravy = fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_ptgravy, th_ptgravyt, FFTW_MEASURE);
+ plan_gravmap = fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_gravmapbig, th_gravmapbigt, FFTW_MEASURE);
+ plan_gravx_inverse = fftwf_plan_dft_c2r_2d(yblock2, xblock2, th_gravxbigt, th_gravxbig, FFTW_MEASURE);
+ plan_gravy_inverse = fftwf_plan_dft_c2r_2d(yblock2, xblock2, th_gravybigt, th_gravybig, FFTW_MEASURE);
+
+ //(XRES/CELL)*(YRES/CELL)*4 is size of data array, scaling needed because FFTW calculates an unnormalized DFT
+ scaleFactor = -M_GRAV/((XRES/CELL)*(YRES/CELL)*4);
+ //calculate velocity map caused by a point mass
+ for (y=0; y<yblock2; y++)
+ {
+ for (x=0; x<xblock2; x++)
+ {
+ if (x==XRES/CELL && y==YRES/CELL) continue;
+ distance = sqrtf(pow(x-(XRES/CELL), 2.0f) + pow(y-(YRES/CELL), 2.0f));
+ th_ptgravx[y*xblock2+x] = scaleFactor*(x-(XRES/CELL)) / pow(distance, 3.0f);
+ th_ptgravy[y*xblock2+x] = scaleFactor*(y-(YRES/CELL)) / pow(distance, 3.0f);
+ }
+ }
+ th_ptgravx[yblock2*xblock2/2+xblock2/2] = 0.0f;
+ th_ptgravy[yblock2*xblock2/2+xblock2/2] = 0.0f;
+
+ //transform point mass velocity maps
+ fftwf_execute(plan_ptgravx);
+ fftwf_execute(plan_ptgravy);
+ fftwf_destroy_plan(plan_ptgravx);
+ fftwf_destroy_plan(plan_ptgravy);
+ fftwf_free(th_ptgravx);
+ fftwf_free(th_ptgravy);
+
+ //clear padded gravmap
+ memset(th_gravmapbig,0,xblock2*yblock2*sizeof(float));
+
+ grav_fft_status = true;
+}
+
+void Gravity::grav_fft_cleanup()
+{
+ if (!grav_fft_status) return;
+ fftwf_free(th_ptgravxt);
+ fftwf_free(th_ptgravyt);
+ fftwf_free(th_gravmapbig);
+ fftwf_free(th_gravmapbigt);
+ fftwf_free(th_gravxbig);
+ fftwf_free(th_gravybig);
+ fftwf_free(th_gravxbigt);
+ fftwf_free(th_gravybigt);
+ fftwf_destroy_plan(plan_gravmap);
+ fftwf_destroy_plan(plan_gravx_inverse);
+ fftwf_destroy_plan(plan_gravy_inverse);
+ grav_fft_status = false;
+}
+
+void Gravity::update_grav()
+{
+ int x, y, changed = 0;
+ int xblock2 = XRES/CELL*2, yblock2 = YRES/CELL*2;
+ int i, fft_tsize = (xblock2/2+1)*yblock2;
+ float mr, mc, pr, pc, gr, gc;
+ for (y=0; y<YRES/CELL; y++)
+ {
+ if(changed)
+ break;
+ for (x=0; x<XRES/CELL; x++)
+ {
+ if(th_ogravmap[y*(XRES/CELL)+x]!=th_gravmap[y*(XRES/CELL)+x]){
+ changed = 1;
+ break;
+ }
+ }
+ }
+ if(changed)
+ {
+ th_gravchanged = 1;
+ if (!grav_fft_status) grav_fft_init();
+
+ //copy gravmap into padded gravmap array
+ for (y=0; y<YRES/CELL; y++)
+ {
+ for (x=0; x<XRES/CELL; x++)
+ {
+ th_gravmapbig[(y+YRES/CELL)*xblock2+XRES/CELL+x] = th_gravmap[y*(XRES/CELL)+x];
+ }
+ }
+ //transform gravmap
+ fftwf_execute(plan_gravmap);
+ //do convolution (multiply the complex numbers)
+ for (i=0; i<fft_tsize; i++)
+ {
+ mr = th_gravmapbigt[i][0];
+ mc = th_gravmapbigt[i][1];
+ pr = th_ptgravxt[i][0];
+ pc = th_ptgravxt[i][1];
+ gr = mr*pr-mc*pc;
+ gc = mr*pc+mc*pr;
+ th_gravxbigt[i][0] = gr;
+ th_gravxbigt[i][1] = gc;
+ pr = th_ptgravyt[i][0];
+ pc = th_ptgravyt[i][1];
+ gr = mr*pr-mc*pc;
+ gc = mr*pc+mc*pr;
+ th_gravybigt[i][0] = gr;
+ th_gravybigt[i][1] = gc;
+ }
+ //inverse transform, and copy from padded arrays into normal velocity maps
+ fftwf_execute(plan_gravx_inverse);
+ fftwf_execute(plan_gravy_inverse);
+ for (y=0; y<YRES/CELL; y++)
+ {
+ for (x=0; x<XRES/CELL; x++)
+ {
+ th_gravx[y*(XRES/CELL)+x] = th_gravxbig[y*xblock2+x];
+ th_gravy[y*(XRES/CELL)+x] = th_gravybig[y*xblock2+x];
+ th_gravp[y*(XRES/CELL)+x] = sqrtf(pow(th_gravxbig[y*xblock2+x],2)+pow(th_gravybig[y*xblock2+x],2));
+ }
+ }
+ }
+ else
+ {
+ th_gravchanged = 0;
+ }
+ memcpy(th_ogravmap, th_gravmap, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+}
+
+#else
+// gravity without fast Fourier transforms
+
+void Gravity::update_grav(void)
+{
+ int x, y, i, j, changed = 0;
+ float val, distance;
+ th_gravchanged = 0;
+#ifndef GRAV_DIFF
+ //Find any changed cells
+ for (i=0; i<YRES/CELL; i++)
+ {
+ if(changed)
+ break;
+ for (j=0; j<XRES/CELL; j++)
+ {
+ if(th_ogravmap[i*(XRES/CELL)+j]!=th_gravmap[i*(XRES/CELL)+j]){
+ changed = 1;
+ break;
+ }
+ }
+ }
+ if(!changed)
+ goto fin;
+ memset(th_gravy, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+ memset(th_gravx, 0, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+#endif
+ th_gravchanged = 1;
+ for (i = 0; i < YRES / CELL; i++) {
+ for (j = 0; j < XRES / CELL; j++) {
+#ifdef GRAV_DIFF
+ if (th_ogravmap[i*(XRES/CELL)+j] != th_gravmap[i*(XRES/CELL)+j])
+ {
+#else
+ if (th_gravmap[i*(XRES/CELL)+j] > 0.0001f || th_gravmap[i*(XRES/CELL)+j]<-0.0001f) //Only calculate with populated or changed cells.
+ {
+#endif
+ for (y = 0; y < YRES / CELL; y++) {
+ for (x = 0; x < XRES / CELL; x++) {
+ if (x == j && y == i)//Ensure it doesn't calculate with itself
+ continue;
+ distance = sqrt(pow(j - x, 2.0f) + pow(i - y, 2.0f));
+#ifdef GRAV_DIFF
+ val = th_gravmap[i*(XRES/CELL)+j] - th_ogravmap[i*(XRES/CELL)+j];
+#else
+ val = th_gravmap[i*(XRES/CELL)+j];
+#endif
+ th_gravx[y*(XRES/CELL)+x] += M_GRAV * val * (j - x) / pow(distance, 3.0f);
+ th_gravy[y*(XRES/CELL)+x] += M_GRAV * val * (i - y) / pow(distance, 3.0f);
+ th_gravp[y*(XRES/CELL)+x] += M_GRAV * val / pow(distance, 2.0f);
+ }
+ }
+ }
+ }
+ }
+fin:
+ memcpy(th_ogravmap, th_gravmap, (XRES/CELL)*(YRES/CELL)*sizeof(float));
+}
+#endif
+
+
+
+void Gravity::grav_mask_r(int x, int y, char checkmap[YRES/CELL][XRES/CELL], char shape[YRES/CELL][XRES/CELL], char *shapeout)
+{
+ if(x < 0 || x >= XRES/CELL || y < 0 || y >= YRES/CELL)
+ return;
+ if(x == 0 || y ==0 || y == (YRES/CELL)-1 || x == (XRES/CELL)-1)
+ *shapeout = 1;
+
+ int x1 = x, x2 = x;
+ while (x1 >= 1)
+ {
+ if(checkmap[y][x1-1] || bmap[y][x1-1]==WL_GRAV)
+ break;
+ x1--;
+ }
+ while (x2 < (XRES/CELL)-1)
+ {
+ if(checkmap[y][x2+1] || bmap[y][x2+1]==WL_GRAV)
+ break;
+ x2++;
+ }
+
+ // fill span
+ for (x = x1; x <= x2; x++)
+ checkmap[y][x] = shape[y][x] = 1;
+
+ if(y >= 1)
+ for(x = x1; x <= x2; x++)
+ if(!checkmap[y-1][x] && bmap[y-1][x]!=WL_GRAV)
+ grav_mask_r(x, y-1, checkmap, shape, shapeout);
+ if(y < (YRES/CELL)-1)
+ for(x = x1; x <= x2; x++)
+ if(!checkmap[y+1][x] && bmap[y+1][x]!=WL_GRAV)
+ grav_mask_r(x, y+1, checkmap, shape, shapeout);
+ return;
+}
+void Gravity::mask_free(mask_el *c_mask_el){
+ if(c_mask_el==NULL)
+ return;
+ if(c_mask_el->next!=NULL)
+ mask_free((mask_el*)c_mask_el->next);
+ free(c_mask_el->shape);
+ free(c_mask_el);
+}
+void Gravity::gravity_mask()
+{
+ char checkmap[YRES/CELL][XRES/CELL];
+ int x = 0, y = 0, i, j;
+ unsigned maskvalue;
+ mask_el *t_mask_el = NULL;
+ mask_el *c_mask_el = NULL;
+ if(!gravmask)
+ return;
+ memset(checkmap, 0, sizeof(checkmap));
+ for(x = 0; x < XRES/CELL; x++)
+ {
+ for(y = 0; y < YRES/CELL; y++)
+ {
+ if(bmap[y][x]!=WL_GRAV && checkmap[y][x] == 0)
+ {
+ //Create a new shape
+ if(t_mask_el==NULL){
+ t_mask_el = (mask_el *)malloc(sizeof(mask_el));
+ t_mask_el->shape = (char *)malloc((XRES/CELL)*(YRES/CELL));
+ memset(t_mask_el->shape, 0, (XRES/CELL)*(YRES/CELL));
+ t_mask_el->shapeout = 0;
+ t_mask_el->next = NULL;
+ c_mask_el = t_mask_el;
+ } else {
+ c_mask_el->next = (mask_el *)malloc(sizeof(mask_el));
+ c_mask_el = (mask_el *)c_mask_el->next;
+ c_mask_el->shape = (char *)malloc((XRES/CELL)*(YRES/CELL));
+ memset(c_mask_el->shape, 0, (XRES/CELL)*(YRES/CELL));
+ c_mask_el->shapeout = 0;
+ c_mask_el->next = NULL;
+ }
+ //Fill the shape
+ grav_mask_r(x, y, (char (*)[XRES/CELL])checkmap, (char (*)[XRES/CELL])c_mask_el->shape, (char*)&c_mask_el->shapeout);
+ }
+ }
+ }
+ c_mask_el = t_mask_el;
+ memset(gravmask, 0, (XRES/CELL)*(YRES/CELL)*sizeof(unsigned));
+ while(c_mask_el!=NULL)
+ {
+ char *cshape = c_mask_el->shape;
+ for(x = 0; x < XRES/CELL; x++)
+ {
+ for(y = 0; y < YRES/CELL; y++)
+ {
+ if(cshape[y*(XRES/CELL)+x]){
+ if(c_mask_el->shapeout)
+ maskvalue = 0xFFFFFFFF;
+ else
+ maskvalue = 0x00000000;
+ gravmask[y*(XRES/CELL)+x] = maskvalue;
+ }
+ }
+ }
+ c_mask_el = (mask_el*)c_mask_el->next;
+ }
+ mask_free(t_mask_el);
+}
+
+Gravity::Gravity():
+ grav_fft_status(false)
+{
+ gravity_init();
+}
+
+Gravity::~Gravity()
+{
+ gravity_cleanup();
+}
diff --git a/src/simulation/Gravity.h b/src/simulation/Gravity.h
new file mode 100644
index 0000000..ec31b6a
--- /dev/null
+++ b/src/simulation/Gravity.h
@@ -0,0 +1,123 @@
+#ifndef GRAVITY_H
+#define GRAVITY_H
+
+#include <pthread.h>
+#undef GetUserName //God dammit microsoft!
+#include "Config.h"
+#include "Simulation.h"
+
+#ifdef GRAVFFT
+#include <fftw3.h>
+#endif
+
+class Simulation;
+
+struct mask_el {
+ char *shape;
+ char shapeout;
+ void *next;
+};
+typedef struct mask_el mask_el;
+
+
+/*
+ * float *gravmap = NULL;//Maps to be used by the main thread
+ float *gravp = NULL;
+ float *gravy = NULL;
+ float *gravx = NULL;
+ unsigned *gravmask = NULL;
+
+ float *th_ogravmap = NULL;// Maps to be processed by the gravity thread
+ float *th_gravmap = NULL;
+ float *th_gravx = NULL;
+ float *th_gravy = NULL;
+ float *th_gravp = NULL;
+
+ int th_gravchanged = 0;
+
+ pthread_t gravthread;
+ pthread_mutex_t gravmutex;
+ pthread_cond_t gravcv;
+ int grav_ready = 0;
+ int gravthread_done = 0;
+ */
+class Gravity
+{
+private:
+
+ float *th_ogravmap;
+ float *th_gravmap;
+ float *th_gravx;
+ float *th_gravy;
+ float *th_gravp;
+
+ int th_gravchanged;
+
+ pthread_t gravthread;
+ pthread_mutex_t gravmutex;
+ pthread_cond_t gravcv;
+ int grav_ready;
+ int gravthread_done;
+
+#ifdef GRAVFFT
+ bool grav_fft_status;
+ float *th_ptgravx, *th_ptgravy, *th_gravmapbig, *th_gravxbig, *th_gravybig;
+ fftwf_complex *th_ptgravxt, *th_ptgravyt, *th_gravmapbigt, *th_gravxbigt, *th_gravybigt;
+ fftwf_plan plan_gravmap, plan_gravx_inverse, plan_gravy_inverse;
+#endif
+
+ //Simulation * sim;
+public:
+ unsigned *gravmask;
+ float *gravmap;
+ float *gravp;
+ float *gravy;
+ float *gravx;
+ unsigned char (*bmap)[XRES/CELL];
+ int ngrav_enable;
+ void grav_mask_r(int x, int y, char checkmap[YRES/CELL][XRES/CELL], char shape[YRES/CELL][XRES/CELL], char *shapeout);
+ void mask_free(mask_el *c_mask_el);
+
+ void Clear();
+
+ void gravity_init();
+ void gravity_cleanup();
+ void gravity_update_async();
+
+ static void *update_grav_async_helper(void * context);
+ void update_grav_async();
+
+ void start_grav_async();
+ void stop_grav_async();
+ void update_grav();
+ void gravity_mask();
+
+ void bilinear_interpolation(float *src, float *dst, int sw, int sh, int rw, int rh);
+
+ #ifdef GRAVFFT
+ void grav_fft_init();
+ void grav_fft_cleanup();
+ #endif
+
+ Gravity();
+ ~Gravity();
+};
+
+/*extern int ngrav_enable; //Newtonian gravity
+extern int gravwl_timeout;
+extern int gravityMode;*/
+
+/*float *gravmap;//Maps to be used by the main thread
+float *gravp;
+float *gravy;
+float *gravx;
+unsigned *gravmask;
+
+float *th_ogravmap;// Maps to be processed by the gravity thread
+float *th_gravmap;
+float *th_gravx;
+float *th_gravy;
+float *th_gravp;*/
+
+
+#endif
diff --git a/src/simulation/MenuSection.h b/src/simulation/MenuSection.h
new file mode 100644
index 0000000..b8e16fe
--- /dev/null
+++ b/src/simulation/MenuSection.h
@@ -0,0 +1,20 @@
+//
+// MenuSection.h
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 04/06/2012.
+// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#ifndef The_Powder_Toy_MenuSection_h
+#define The_Powder_Toy_MenuSection_h
+
+struct menu_section
+{
+ char *icon;
+ const char *name;
+ int itemcount;
+ int doshow;
+};
+
+#endif
diff --git a/src/simulation/Particle.cpp b/src/simulation/Particle.cpp
new file mode 100644
index 0000000..115ed95
--- /dev/null
+++ b/src/simulation/Particle.cpp
@@ -0,0 +1,27 @@
+/*
+ * Particle.cpp
+ *
+ * Created on: Jun 6, 2012
+ * Author: Simon
+ */
+
+#include <cstddef>
+#include "Particle.h"
+
+std::vector<StructProperty> Particle::GetProperties()
+{
+ std::vector<StructProperty> properties;
+ properties.push_back(StructProperty("type", StructProperty::ParticleType, offsetof(Particle, type)));
+ properties.push_back(StructProperty("life", StructProperty::ParticleType, offsetof(Particle, life)));
+ properties.push_back(StructProperty("ctype", StructProperty::ParticleType, offsetof(Particle, ctype)));
+ properties.push_back(StructProperty("x", StructProperty::Float, offsetof(Particle, x)));
+ properties.push_back(StructProperty("y", StructProperty::Float, offsetof(Particle, y)));
+ properties.push_back(StructProperty("vx", StructProperty::Float, offsetof(Particle, vx)));
+ properties.push_back(StructProperty("vy", StructProperty::Float, offsetof(Particle, vy)));
+ properties.push_back(StructProperty("temp", StructProperty::Float, offsetof(Particle, temp)));
+ properties.push_back(StructProperty("flags", StructProperty::UInteger, offsetof(Particle, flags)));
+ properties.push_back(StructProperty("tmp", StructProperty::Integer, offsetof(Particle, tmp)));
+ properties.push_back(StructProperty("tmp2", StructProperty::Integer, offsetof(Particle, tmp2)));
+ properties.push_back(StructProperty("dcolour", StructProperty::UInteger, offsetof(Particle, dcolour)));
+ return properties;
+}
diff --git a/src/simulation/Particle.h b/src/simulation/Particle.h
new file mode 100644
index 0000000..bb0297e
--- /dev/null
+++ b/src/simulation/Particle.h
@@ -0,0 +1,31 @@
+//
+// Particle.h
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 04/06/2012.
+// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#ifndef The_Powder_Toy_Particle_h
+#define The_Powder_Toy_Particle_h
+
+#include <vector>
+#include "StructProperty.h"
+
+struct Particle
+{
+ int type;
+ int life, ctype;
+ float x, y, vx, vy;
+ float temp;
+ float pavg[2];
+ int flags;
+ int tmp;
+ int tmp2;
+ unsigned int dcolour;
+ /** Returns a list of properties, their type and offset within the structure that can be changed
+ by higher-level processes refering to them by name such as Lua or the property tool **/
+ static std::vector<StructProperty> GetProperties();
+};
+
+#endif
diff --git a/src/simulation/Player.h b/src/simulation/Player.h
new file mode 100644
index 0000000..af2b68e
--- /dev/null
+++ b/src/simulation/Player.h
@@ -0,0 +1,23 @@
+//
+// Player.h
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 04/06/2012.
+// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#ifndef The_Powder_Toy_Player_h
+#define The_Powder_Toy_Player_h
+
+struct playerst
+{
+ char comm; //command cell
+ char pcomm; //previous command
+ int elem; //element power
+ float legs[16]; //legs' positions
+ float accs[8]; //accelerations
+ char spwn; //if stick man was spawned
+ unsigned int frames; //frames since last particle spawn - used when spawning LIGH
+};
+
+#endif
diff --git a/src/simulation/Sample.h b/src/simulation/Sample.h
new file mode 100644
index 0000000..3605a5d
--- /dev/null
+++ b/src/simulation/Sample.h
@@ -0,0 +1,29 @@
+
+
+#ifndef The_Powder_Toy_Sample_h
+#define The_Powder_Toy_Sample_h
+
+#include "Particle.h"
+
+class SimulationSample
+{
+public:
+ Particle particle;
+ int ParticleID;
+ int PositionX, PositionY;
+ float AirPressure;
+ float AirTemperature;
+ float AirVelocityX;
+ float AirVelocityY;
+
+ int WallType;
+ float Gravity;
+ float GravityVelocityX;
+ float GravityVelocityY;
+
+ int NumParts;
+
+ SimulationSample() : PositionX(0), PositionY(0), ParticleID(0), particle(), AirPressure(0), AirVelocityX(0), AirVelocityY(0), WallType(0), Gravity(0), GravityVelocityX(0), GravityVelocityY(0), AirTemperature(0), NumParts(0) {}
+};
+
+#endif \ No newline at end of file
diff --git a/src/simulation/SaveRenderer.cpp b/src/simulation/SaveRenderer.cpp
new file mode 100644
index 0000000..fc30a28
--- /dev/null
+++ b/src/simulation/SaveRenderer.cpp
@@ -0,0 +1,183 @@
+/*
+ * SaveRenderer.cpp
+ *
+ * Created on: Apr 3, 2012
+ * Author: Simon
+ */
+
+#include "SaveRenderer.h"
+#include "client/GameSave.h"
+#include "graphics/Graphics.h"
+#include "Simulation.h"
+#include "graphics/Renderer.h"
+#include "search/Thumbnail.h"
+
+
+SaveRenderer::SaveRenderer(){
+ g = new Graphics();
+ sim = new Simulation();
+ ren = new Renderer(g, sim);
+ ren->decorations_enable = true;
+ ren->blackDecorations = true;
+
+#if defined(OGLR) || defined(OGLI)
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &fboTex);
+ glBindTexture(GL_TEXTURE_2D, fboTex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, XRES, YRES, 0, GL_RGBA, GL_FLOAT, NULL);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+
+ //FBO
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+ glEnable(GL_BLEND);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Reset framebuffer binding
+ glDisable(GL_TEXTURE_2D);
+#endif
+}
+
+Thumbnail * SaveRenderer::Render(GameSave * save, bool decorations, bool fire)
+{
+ int width, height;
+ Thumbnail * tempThumb;
+ width = save->blockWidth;
+ height = save->blockHeight;
+ bool doCollapse = save->Collapsed();
+
+ g->Acquire();
+ g->Clear();
+ sim->clear_sim();
+
+ if(!sim->Load(save))
+ {
+ ren->decorations_enable = true;
+ ren->blackDecorations = !decorations;
+#if defined(OGLR) || defined(OGLI)
+ pixel * pData = NULL;
+ unsigned char * texData = NULL;
+
+ glTranslated(0, MENUSIZE, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ ren->clearScreen(1.0f);
+ ren->ClearAccumulation();
+
+#ifdef OGLR
+ ren->RenderBegin();
+ ren->RenderEnd();
+#else
+ if (fire)
+ {
+ int frame = 15;
+ while(frame)
+ {
+ frame--;
+ ren->render_parts();
+ ren->render_fire();
+ ren->clearScreen(1.0f);
+ }
+ }
+
+ ren->RenderBegin();
+ ren->RenderEnd();
+#endif
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ glTranslated(0, -MENUSIZE, 0);
+
+ glEnable( GL_TEXTURE_2D );
+ glBindTexture(GL_TEXTURE_2D, fboTex);
+
+ pData = new pixel[XRES*YRES];
+ texData = new unsigned char[(XRES*YRES)*PIXELSIZE];
+ std::fill(texData, texData+(XRES*YRES)*PIXELSIZE, 0xDD);
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
+ glDisable(GL_TEXTURE_2D);
+
+ for(int x = 0; x < width*CELL; x++)
+ {
+ for(int y = 0; y < height*CELL; y++)
+ {
+ unsigned char red = texData[((((YRES-1-y)*XRES)+x)*4)];
+ unsigned char green = texData[((((YRES-1-y)*XRES)+x)*4)+1];
+ unsigned char blue = texData[((((YRES-1-y)*XRES)+x)*4)+2];
+
+ pData[(y*(width*CELL))+x] = PIXRGBA(red, green, blue, 255);
+ }
+ }
+
+ tempThumb = new Thumbnail(0, 0, pData, ui::Point(width*CELL, height*CELL));
+ delete[] pData;
+ delete[] texData;
+ pData = NULL;
+#else
+ pixel * pData = NULL;
+ pixel * dst;
+ pixel * src = g->vid;
+
+ ren->ClearAccumulation();
+
+ if (fire)
+ {
+ int frame = 15;
+ while(frame)
+ {
+ frame--;
+ ren->render_parts();
+ ren->render_fire();
+ ren->clearScreen(1.0f);
+ }
+ }
+
+ ren->RenderBegin();
+ ren->RenderEnd();
+
+
+ pData = (pixel *)malloc(PIXELSIZE * ((width*CELL)*(height*CELL)));
+ dst = pData;
+ for(int i = 0; i < height*CELL; i++)
+ {
+ memcpy(dst, src, (width*CELL)*PIXELSIZE);
+ dst+=(width*CELL);///PIXELSIZE;
+ src+=XRES+BARSIZE;
+ }
+ tempThumb = new Thumbnail(0, 0, pData, ui::Point(width*CELL, height*CELL));
+ if(pData)
+ free(pData);
+#endif
+ }
+ if(doCollapse)
+ save->Collapse();
+ g->Release();
+ return tempThumb;
+}
+
+Thumbnail * SaveRenderer::Render(unsigned char * saveData, int dataSize, bool decorations, bool fire)
+{
+ GameSave * tempSave;
+ try {
+ tempSave = new GameSave((char*)saveData, dataSize);
+ } catch (std::exception & e) {
+
+ //Todo: make this look a little less shit
+ VideoBuffer buffer(64, 64);
+ buffer.BlendCharacter(32, 32, 'x', 255, 255, 255, 255);
+
+ Thumbnail * thumb = new Thumbnail(0, 0, buffer.Buffer, ui::Point(64, 64));
+
+ return thumb;
+ }
+ Thumbnail * thumb = Render(tempSave, decorations, fire);
+ delete tempSave;
+ return thumb;
+}
+
+SaveRenderer::~SaveRenderer() {
+ // TODO Auto-generated destructor stub
+}
+
diff --git a/src/simulation/SaveRenderer.h b/src/simulation/SaveRenderer.h
new file mode 100644
index 0000000..be4a83b
--- /dev/null
+++ b/src/simulation/SaveRenderer.h
@@ -0,0 +1,37 @@
+/*
+ * SaveRenderer.h
+ *
+ * Created on: Apr 3, 2012
+ * Author: Simon
+ */
+
+#ifndef SAVERENDERER_H_
+#define SAVERENDERER_H_
+#ifdef OGLI
+#include "graphics/OpenGLHeaders.h"
+#endif
+#include "Singleton.h"
+
+class GameSave;
+class Thumbnail;
+class Graphics;
+class Simulation;
+class Renderer;
+
+class SaveRenderer: public Singleton<SaveRenderer> {
+ Graphics * g;
+ Simulation * sim;
+ Renderer * ren;
+public:
+ SaveRenderer();
+ Thumbnail * Render(GameSave * save, bool decorations = true, bool fire = true);
+ Thumbnail * Render(unsigned char * saveData, int saveDataSize, bool decorations = true, bool fire = true);
+ virtual ~SaveRenderer();
+
+private:
+#if defined(OGLR) || defined(OGLI)
+ GLuint fboTex, fbo;
+#endif
+};
+
+#endif /* SAVERENDERER_H_ */
diff --git a/src/simulation/Sign.cpp b/src/simulation/Sign.cpp
new file mode 100644
index 0000000..2caf6c3
--- /dev/null
+++ b/src/simulation/Sign.cpp
@@ -0,0 +1,55 @@
+/*
+ * Sign.cpp
+ *
+ * Created on: Jun 25, 2012
+ * Author: Simon
+ */
+
+#include "Sign.h"
+#include "graphics/Graphics.h"
+#include "Misc.h"
+
+sign::sign(std::string text_, int x_, int y_, Justification justification_):
+ text(text_),
+ x(x_),
+ y(y_),
+ ju(justification_)
+{
+}
+
+void sign::pos(int & x0, int & y0, int & w, int & h)
+{
+ //Changing width if sign have special content
+ if (text == "{p}")
+ {
+ w = Graphics::textwidth("Pressure: -000.00");
+ }
+ else if (text == "{t}")
+ {
+ w = Graphics::textwidth("Temp: 0000.00");
+ }
+ else if (sregexp(text.c_str(), "^{[c|t]:[0-9]*|.*}$")==0)
+ {
+ int sldr, startm;
+ char buff[256];
+ memset(buff, 0, sizeof(buff));
+ for (sldr=3; text[sldr-1] != '|'; sldr++)
+ startm = sldr + 1;
+
+ sldr = startm;
+ while (text[sldr] != '}')
+ {
+ buff[sldr - startm] = text[sldr];
+ sldr++;
+ }
+ w = Graphics::textwidth(buff) + 5;
+ }
+ else
+ {
+ w = Graphics::textwidth(text.c_str()) + 5;
+ }
+ h = 14;
+ x0 = (ju == 2) ? x - w :
+ (ju == 1) ? x - w/2 : x;
+ y0 = (y > 18) ? y - 18 : y + 4;
+}
diff --git a/src/simulation/Sign.h b/src/simulation/Sign.h
new file mode 100644
index 0000000..14b7dac
--- /dev/null
+++ b/src/simulation/Sign.h
@@ -0,0 +1,26 @@
+//
+// Sign.h
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 04/06/2012.
+// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#ifndef The_Powder_Toy_Sign_h
+#define The_Powder_Toy_Sign_h
+
+#include <string>
+
+class sign
+{
+public:
+ enum Justification { Left = 0, Centre = 1, Right = 2 };
+ sign(std::string text_, int x_, int y_, Justification justification_);
+ int x, y;
+ Justification ju;
+ std::string text;
+
+ void pos(int & x0, int & y0, int & w, int & h);
+};
+
+#endif
diff --git a/src/simulation/Simulation.cpp b/src/simulation/Simulation.cpp
new file mode 100644
index 0000000..cfe39ac
--- /dev/null
+++ b/src/simulation/Simulation.cpp
@@ -0,0 +1,4835 @@
+//#include <cstdlib>
+#include <cmath>
+#include <math.h>
+#if !defined(_MSC_VER)
+#include <strings.h>
+#else
+#include <windows.h>
+#endif
+#include "Config.h"
+#include "Simulation.h"
+#include "Elements.h"
+//#include "ElementFunctions.h"
+#include "Air.h"
+#include "Gravity.h"
+#include "elements/Element.h"
+
+//#include "graphics/Renderer.h"
+//#include "graphics/Graphics.h"
+#include "Misc.h"
+#include "Tools.h"
+#include "game/Brush.h"
+#include "client/GameSave.h"
+#include "Sample.h"
+#include "Snapshot.h"
+//#include "StorageClasses.h"
+
+#undef LUACONSOLE
+//#include "cat/LuaScriptHelper.h"
+
+int Simulation::Load(GameSave * save)
+{
+ return Load(0, 0, save);
+}
+
+int Simulation::Load(int fullX, int fullY, GameSave * save)
+{
+ int blockX, blockY, x, y, r;
+
+ if(!save) return 0;
+ save->Expand();
+
+ //Align to blockMap
+ blockX = fullX/CELL;
+ blockY = fullY/CELL;
+ fullX = blockX*CELL;
+ fullY = blockY*CELL;
+
+ int partMap[PT_NUM];
+ for(int i = 0; i < PT_NUM; i++)
+ {
+ partMap[i] = i;
+ }
+ if(save->palette.size())
+ {
+ for(std::vector<GameSave::PaletteItem>::iterator iter = save->palette.begin(), end = save->palette.end(); iter != end; ++iter)
+ {
+ GameSave::PaletteItem pi = *iter;
+ if(pi.second >= 0 && pi.second < PT_NUM)
+ {
+ int myId = 0;//pi.second;
+ for(int i = 0; i < PT_NUM; i++)
+ {
+ if(elements[i].Enabled && elements[i].Identifier == pi.first)
+ myId = i;
+ }
+ partMap[pi.second] = myId;
+ }
+ }
+ }
+
+ int i;
+ for(int n = 0; n < NPART && n < save->particlesCount; n++)
+ {
+ Particle tempPart = save->particles[n];
+ tempPart.x += (float)fullX;
+ tempPart.y += (float)fullY;
+ x = int(tempPart.x + 0.5f);
+ y = int(tempPart.y + 0.5f);
+
+ if(tempPart.type >= 0 && tempPart.type < PT_NUM)
+ tempPart.type = partMap[tempPart.type];
+
+ if ((player.spwn == 1 && tempPart.type==PT_STKM) || (player2.spwn == 1 && tempPart.type==PT_STKM2))
+ continue;
+ if (!elements[tempPart.type].Enabled)
+ continue;
+
+ if(r = pmap[y][x])
+ {
+ //Replace existing
+ parts[r>>8] = tempPart;
+ i = r>>8;
+ pmap[y][x] = 0;
+ elementCount[parts[r>>8].type]--;
+ elementCount[tempPart.type]++;
+ }
+ else
+ {
+ //Allocate new particle
+ if (pfree == -1)
+ break;
+ i = pfree;
+ pfree = parts[i].life;
+ if (i>parts_lastActiveIndex) parts_lastActiveIndex = i;
+ parts[i] = tempPart;
+
+ elementCount[tempPart.type]++;
+ }
+
+ if (parts[i].type == PT_STKM)
+ {
+ Element_STKM::STKM_init_legs(this, &player, i);
+ player.spwn = 1;
+ player.elem = PT_DUST;
+ }
+ else if (parts[i].type == PT_STKM2)
+ {
+ Element_STKM::STKM_init_legs(this, &player2, i);
+ player2.spwn = 1;
+ player2.elem = PT_DUST;
+ }
+ else if (parts[i].type == PT_FIGH)
+ {
+ //TODO: 100 should be replaced with a macro
+ for(int fcount = 0; fcount < 100; fcount++)
+ {
+ if(!fighters[fcount].spwn)
+ {
+ fighcount++;
+ //currentPart.tmp = fcount;
+ parts[i].tmp = fcount;
+ Element_STKM::STKM_init_legs(this, &(fighters[fcount]), i);
+ fighters[fcount].spwn = 1;
+ fighters[fcount].elem = PT_DUST;
+ break;
+ }
+ }
+ }
+ }
+ parts_lastActiveIndex = NPART-1;
+ force_stacking_check = 1;
+ Element_PPIP::ppip_changed = 1;
+ for(int i = 0; i < save->signs.size() && signs.size() < MAXSIGNS; i++)
+ {
+ sign tempSign = save->signs[i];
+ tempSign.x += fullX;
+ tempSign.y += fullY;
+ signs.push_back(tempSign);
+ }
+ for(int saveBlockX = 0; saveBlockX < save->blockWidth; saveBlockX++)
+ {
+ for(int saveBlockY = 0; saveBlockY < save->blockHeight; saveBlockY++)
+ {
+ if(save->blockMap[saveBlockY][saveBlockX])
+ {
+ bmap[saveBlockY+blockY][saveBlockX+blockX] = save->blockMap[saveBlockY][saveBlockX];
+ fvx[saveBlockY+blockY][saveBlockX+blockX] = save->fanVelX[saveBlockY][saveBlockX];
+ fvy[saveBlockY+blockY][saveBlockX+blockX] = save->fanVelY[saveBlockY][saveBlockX];
+ }
+ }
+ }
+
+ gravWallChanged = true;
+
+ return 0;
+}
+
+GameSave * Simulation::Save()
+{
+ return Save(0, 0, XRES, YRES);
+}
+
+GameSave * Simulation::Save(int fullX, int fullY, int fullX2, int fullY2)
+{
+ int blockX, blockY, blockX2, blockY2, fullW, fullH, blockW, blockH;
+ //Normalise incoming coords
+ int swapTemp;
+ if(fullY>fullY2)
+ {
+ swapTemp = fullY;
+ fullY = fullY2;
+ fullY2 = swapTemp;
+ }
+ if(fullX>fullX2)
+ {
+ swapTemp = fullX;
+ fullX = fullX2;
+ fullX2 = swapTemp;
+ }
+
+ //Align coords to blockMap
+ blockX = fullX/CELL;
+ blockY = fullY/CELL;
+
+ blockX2 = fullX2/CELL;
+ blockY2 = fullY2/CELL;
+
+ fullX = blockX*CELL;
+ fullY = blockY*CELL;
+
+ fullX2 = blockX2*CELL;
+ fullY2 = blockY2*CELL;
+
+ blockW = blockX2-blockX;
+ blockH = blockY2-blockY;
+ fullW = fullX2-fullX;
+ fullH = fullY2-fullY;
+
+ GameSave * newSave = new GameSave(blockW, blockH);
+
+ int storedParts = 0;
+ int elementCount[PT_NUM];
+ std::fill(elementCount, elementCount+PT_NUM, 0);
+ for(int i = 0; i < NPART; i++)
+ {
+ int x, y;
+ x = int(parts[i].x + 0.5f);
+ y = int(parts[i].y + 0.5f);
+ if(parts[i].type && x >= fullX && y >= fullY && x < fullX2 && y < fullY2)
+ {
+ Particle tempPart = parts[i];
+ tempPart.x -= fullX;
+ tempPart.y -= fullY;
+ if(elements[tempPart.type].Enabled)
+ {
+ *newSave << tempPart;
+ storedParts++;
+ elementCount[tempPart.type]++;
+ }
+ }
+ }
+
+ if(storedParts)
+ {
+ for(int i = 0; i < PT_NUM; i++)
+ {
+ if(elements[i].Enabled && elementCount[i])
+ {
+ newSave->palette.push_back(GameSave::PaletteItem(elements[i].Identifier, i));
+ }
+ }
+ }
+
+ for(int i = 0; i < MAXSIGNS && i < signs.size(); i++)
+ {
+ if(signs[i].text.length() && signs[i].x >= fullX && signs[i].y >= fullY && signs[i].x < fullX2 && signs[i].y < fullY2)
+ {
+ sign tempSign = signs[i];
+ tempSign.x -= fullX;
+ tempSign.y -= fullY;
+ *newSave << tempSign;
+ }
+ }
+
+ for(int saveBlockX = 0; saveBlockX < newSave->blockWidth; saveBlockX++)
+ {
+ for(int saveBlockY = 0; saveBlockY < newSave->blockHeight; saveBlockY++)
+ {
+ if(bmap[saveBlockY+blockY][saveBlockX+blockX])
+ {
+ newSave->blockMap[saveBlockY][saveBlockX] = bmap[saveBlockY+blockY][saveBlockX+blockX];
+ newSave->fanVelX[saveBlockY][saveBlockX] = fvx[saveBlockY+blockY][saveBlockX+blockX];
+ newSave->fanVelY[saveBlockY][saveBlockX] = fvy[saveBlockY+blockY][saveBlockX+blockX];
+ }
+ }
+ }
+
+ return newSave;
+}
+
+Snapshot * Simulation::CreateSnapshot()
+{
+ Snapshot * snap = new Snapshot();
+ snap->AirPressure.insert(snap->AirPressure.begin(), &pv[0][0], &pv[0][0]+((XRES/CELL)*(YRES/CELL)));
+ snap->AirVelocityX.insert(snap->AirVelocityX.begin(), &vx[0][0], &vx[0][0]+((XRES/CELL)*(YRES/CELL)));
+ snap->AirVelocityY.insert(snap->AirVelocityY.begin(), &vy[0][0], &vy[0][0]+((XRES/CELL)*(YRES/CELL)));
+ snap->AmbientHeat.insert(snap->AmbientHeat.begin(), &hv[0][0], &hv[0][0]+((XRES/CELL)*(YRES/CELL)));
+ snap->Particles.insert(snap->Particles.begin(), parts, parts+NPART);
+ snap->PortalParticles.insert(snap->PortalParticles.begin(), &portalp[0][0][0], &portalp[CHANNELS-1][8-1][80-1]);
+ snap->WirelessData.insert(snap->WirelessData.begin(), &wireless[0][0], &wireless[CHANNELS-1][2-1]);
+ snap->GravVelocityX.insert(snap->GravVelocityX.begin(), gravx, gravx+((XRES/CELL)*(YRES/CELL)));
+ snap->GravVelocityY.insert(snap->GravVelocityY.begin(), gravy, gravy+((XRES/CELL)*(YRES/CELL)));
+ snap->GravValue.insert(snap->GravValue.begin(), gravp, gravp+((XRES/CELL)*(YRES/CELL)));
+ snap->GravMap.insert(snap->GravMap.begin(), gravmap, gravmap+((XRES/CELL)*(YRES/CELL)));
+ snap->BlockMap.insert(snap->BlockMap.begin(), &bmap[0][0], &bmap[0][0]+((XRES/CELL)*(YRES/CELL)));
+ snap->ElecMap.insert(snap->ElecMap.begin(), &emap[0][0], &emap[0][0]+((XRES/CELL)*(YRES/CELL)));
+ snap->FanVelocityX.insert(snap->FanVelocityX.begin(), &fvx[0][0], &fvx[0][0]+((XRES/CELL)*(YRES/CELL)));
+ snap->FanVelocityY.insert(snap->FanVelocityY.begin(), &fvy[0][0], &fvy[0][0]+((XRES/CELL)*(YRES/CELL)));
+ return snap;
+}
+
+void Simulation::Restore(const Snapshot & snap)
+{
+ parts_lastActiveIndex = NPART-1;
+ std::copy(snap.AirPressure.begin(), snap.AirPressure.end(), &pv[0][0]);
+ std::copy(snap.AirVelocityX.begin(), snap.AirVelocityX.end(), &vx[0][0]);
+ std::copy(snap.AirVelocityY.begin(), snap.AirVelocityY.end(), &vy[0][0]);
+ std::copy(snap.AmbientHeat.begin(), snap.AmbientHeat.end(), &hv[0][0]);
+ std::copy(snap.Particles.begin(), snap.Particles.end(), parts);
+ std::copy(snap.PortalParticles.begin(), snap.PortalParticles.end(), &portalp[0][0][0]);
+ std::copy(snap.WirelessData.begin(), snap.WirelessData.end(), &wireless[0][0]);
+ std::copy(snap.GravVelocityX.begin(), snap.GravVelocityX.end(), gravx);
+ std::copy(snap.GravVelocityY.begin(), snap.GravVelocityY.end(), gravy);
+ std::copy(snap.GravValue.begin(), snap.GravValue.end(), gravp);
+ std::copy(snap.GravMap.begin(), snap.GravMap.end(), gravmap);
+ std::copy(snap.BlockMap.begin(), snap.BlockMap.end(), &bmap[0][0]);
+ std::copy(snap.ElecMap.begin(), snap.ElecMap.end(), &emap[0][0]);
+ std::copy(snap.FanVelocityX.begin(), snap.FanVelocityX.end(), &fvx[0][0]);
+ std::copy(snap.FanVelocityY.begin(), snap.FanVelocityY.end(), &fvy[0][0]);
+}
+
+/*int Simulation::Load(unsigned char * data, int dataLength)
+{
+ return SaveLoader::Load(data, dataLength, this, true, 0, 0);
+}
+
+int Simulation::Load(int x, int y, unsigned char * data, int dataLength)
+{
+ return SaveLoader::Load(data, dataLength, this, false, x, y);
+}
+
+unsigned char * Simulation::Save(int & dataLength)
+{
+ return SaveLoader::Build(dataLength, this, 0, 0, XRES, YRES);
+}
+
+unsigned char * Simulation::Save(int x1, int y1, int x2, int y2, int & dataLength)
+{
+ return SaveLoader::Build(dataLength, this, x1, y1, x2-x1, y2-y1);
+}*/
+
+void Simulation::clear_area(int area_x, int area_y, int area_w, int area_h)
+{
+ int cx = 0;
+ int cy = 0;
+ for (cy=0; cy<area_h; cy++)
+ {
+ for (cx=0; cx<area_w; cx++)
+ {
+ if(bmap[(cy+area_y)/CELL][(cx+area_x)/CELL] == WL_GRAV)
+ gravWallChanged = true;
+ bmap[(cy+area_y)/CELL][(cx+area_x)/CELL] = 0;
+ delete_part(cx+area_x, cy+area_y, 0);
+ }
+ }
+}
+
+void Simulation::CreateBox(int x1, int y1, int x2, int y2, int c, int flags)
+{
+ int i, j;
+ if (c==SPC_PROP)
+ return;
+ if (x1>x2)
+ {
+ i = x2;
+ x2 = x1;
+ x1 = i;
+ }
+ if (y1>y2)
+ {
+ j = y2;
+ y2 = y1;
+ y1 = j;
+ }
+ for (j=y1; j<=y2; j++)
+ for (i=x1; i<=x2; i++)
+ CreateParts(i, j, 0, 0, c, flags);
+}
+
+void Simulation::CreateWallBox(int x1, int y1, int x2, int y2, int c, int flags)
+{
+ int i, j;
+ if (x1>x2)
+ {
+ i = x2;
+ x2 = x1;
+ x1 = i;
+ }
+ if (y1>y2)
+ {
+ j = y2;
+ y2 = y1;
+ y1 = j;
+ }
+ for (j=y1; j<=y2; j++)
+ for (i=x1; i<=x2; i++)
+ CreateWalls(i, j, 0, 0, c, flags);
+}
+
+int Simulation::flood_prop_2(int x, int y, size_t propoffset, void * propvalue, StructProperty::PropertyType proptype, int parttype, char * bitmap)
+{
+ int x1, x2, i, dy = 1;
+ x1 = x2 = x;
+ while (x1>=CELL)
+ {
+ if ((pmap[y][x1-1]&0xFF)!=parttype || bitmap[(y*XRES)+x1-1])
+ {
+ break;
+ }
+ x1--;
+ }
+ while (x2<XRES-CELL)
+ {
+ if ((pmap[y][x2+1]&0xFF)!=parttype || bitmap[(y*XRES)+x2+1])
+ {
+ break;
+ }
+ x2++;
+ }
+ for (x=x1; x<=x2; x++)
+ {
+ i = pmap[y][x]>>8;
+ switch (proptype) {
+ case StructProperty::Float:
+ *((float*)(((char*)&parts[i])+propoffset)) = *((float*)propvalue);
+ break;
+
+ case StructProperty::ParticleType:
+ case StructProperty::Integer:
+ *((int*)(((char*)&parts[i])+propoffset)) = *((int*)propvalue);
+ break;
+
+ case StructProperty::UInteger:
+ *((unsigned int*)(((char*)&parts[i])+propoffset)) = *((unsigned int*)propvalue);
+ break;
+
+ default:
+ break;
+ }
+ bitmap[(y*XRES)+x] = 1;
+ }
+ if (y>=CELL+dy)
+ for (x=x1; x<=x2; x++)
+ if ((pmap[y-dy][x]&0xFF)==parttype && !bitmap[((y-dy)*XRES)+x])
+ if (!flood_prop_2(x, y-dy, propoffset, propvalue, proptype, parttype, bitmap))
+ return 0;
+ if (y<YRES-CELL-dy)
+ for (x=x1; x<=x2; x++)
+ if ((pmap[y+dy][x]&0xFF)==parttype && !bitmap[((y+dy)*XRES)+x])
+ if (!flood_prop_2(x, y+dy, propoffset, propvalue, proptype, parttype, bitmap))
+ return 0;
+ return 1;
+}
+
+int Simulation::flood_prop(int x, int y, size_t propoffset, void * propvalue, StructProperty::PropertyType proptype)
+{
+ int r = 0;
+ char * bitmap = (char *)malloc(XRES*YRES); //Bitmap for checking
+ memset(bitmap, 0, XRES*YRES);
+ r = pmap[y][x];
+ flood_prop_2(x, y, propoffset, propvalue, proptype, r&0xFF, bitmap);
+ free(bitmap);
+ return 0;
+}
+
+SimulationSample Simulation::Get(int x, int y)
+{
+ SimulationSample sample;
+ sample.PositionX = x;
+ sample.PositionY = y;
+ if(pmap[y][x])
+ {
+ sample.particle = parts[pmap[y][x]>>8];
+ sample.ParticleID = pmap[y][x]>>8;
+ }
+ else if(photons[y][x])
+ {
+ sample.particle = parts[photons[y][x]>>8];
+ sample.ParticleID = photons[y][x]>>8;
+ }
+ if (bmap[y/CELL][x/CELL])
+ {
+ sample.WallType = bmap[y/CELL][x/CELL];
+ }
+ sample.AirPressure = pv[y/CELL][x/CELL];
+ sample.AirTemperature = hv[y/CELL][x/CELL];
+ sample.AirVelocityX = vx[y/CELL][x/CELL];
+ sample.AirVelocityY = vy[y/CELL][x/CELL];
+
+ if(grav->ngrav_enable)
+ {
+ sample.Gravity = gravp[(y/CELL)*(XRES/CELL)+(x/CELL)];
+ sample.GravityVelocityX = gravx[(y/CELL)*(XRES/CELL)+(x/CELL)];
+ sample.GravityVelocityY = gravy[(y/CELL)*(XRES/CELL)+(x/CELL)];
+ }
+
+ sample.NumParts = NUM_PARTS;
+ return sample;
+}
+
+#define PMAP_CMP_CONDUCTIVE(pmap, t) (((pmap)&0xFF)==(t) || (((pmap)&0xFF)==PT_SPRK && parts[(pmap)>>8].ctype==(t)))
+
+int Simulation::FloodINST(int x, int y, int fullc, int cm)
+{
+ int c = fullc&0xFF;
+ int x1, x2, dy = (c<PT_NUM)?1:CELL;
+ int co = c;
+ int coord_stack_limit = XRES*YRES;
+ unsigned short (*coord_stack)[2];
+ int coord_stack_size = 0;
+ int created_something = 0;
+
+ if (c>=PT_NUM)
+ return 0;
+
+ if (cm==-1)
+ {
+ if (c==0)
+ {
+ cm = pmap[y][x]&0xFF;
+ if (!cm)
+ return 0;
+ }
+ else
+ cm = 0;
+ }
+
+ if ((pmap[y][x]&0xFF)!=cm || parts[pmap[y][x]>>8].life!=0)
+ return 1;
+
+ coord_stack = (short unsigned int (*)[2])malloc(sizeof(unsigned short)*2*coord_stack_limit);
+ coord_stack[coord_stack_size][0] = x;
+ coord_stack[coord_stack_size][1] = y;
+ coord_stack_size++;
+
+ do
+ {
+ coord_stack_size--;
+ x = coord_stack[coord_stack_size][0];
+ y = coord_stack[coord_stack_size][1];
+ x1 = x2 = x;
+ // go left as far as possible
+ while (x1>=CELL)
+ {
+ if ((pmap[y][x1-1]&0xFF)!=cm || parts[pmap[y][x1-1]>>8].life!=0)
+ {
+ break;
+ }
+ x1--;
+ }
+ // go right as far as possible
+ while (x2<XRES-CELL)
+ {
+ if ((pmap[y][x2+1]&0xFF)!=cm || parts[pmap[y][x2+1]>>8].life!=0)
+ {
+ break;
+ }
+ x2++;
+ }
+ // fill span
+ for (x=x1; x<=x2; x++)
+ {
+ if (create_part(-1, x, y, fullc)>=0)
+ created_something = 1;
+ }
+
+ // add vertically adjacent pixels to stack
+ // (wire crossing for INST)
+ if (y>=CELL+1 && x1==x2 &&
+ PMAP_CMP_CONDUCTIVE(pmap[y-1][x1-1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y-1][x1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y-1][x1+1], cm) &&
+ !PMAP_CMP_CONDUCTIVE(pmap[y-2][x1-1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y-2][x1], cm) && !PMAP_CMP_CONDUCTIVE(pmap[y-2][x1+1], cm))
+ {
+ // travelling vertically up, skipping a horizontal line
+ if ((pmap[y-2][x1]&0xFF)==cm && !parts[pmap[y-2][x1]>>8].life)
+ {
+ coord_stack[coord_stack_size][0] = x1;
+ coord_stack[coord_stack_size][1] = y-2;
+ coord_stack_size++;
+ if (coord_stack_size>=coord_stack_limit)
+ {
+ free(coord_stack);
+ return -1;
+ }
+ }
+ }
+ else if (y>=CELL+1)
+ {
+ for (x=x1; x<=x2; x++)
+ {
+ if ((pmap[y-1][x]&0xFF)==cm && !parts[pmap[y-1][x]>>8].life)
+ {
+ if (x==x1 || x==x2 || y>=YRES-CELL-1 || !PMAP_CMP_CONDUCTIVE(pmap[y+1][x], cm))
+ {
+ // if at the end of a horizontal section, or if it's a T junction
+ coord_stack[coord_stack_size][0] = x;
+ coord_stack[coord_stack_size][1] = y-1;
+ coord_stack_size++;
+ if (coord_stack_size>=coord_stack_limit)
+ {
+ free(coord_stack);
+ return -1;
+ }
+ }
+ }
+ }
+ }
+
+ if (y<YRES-CELL-1 && x1==x2 &&
+ PMAP_CMP_CONDUCTIVE(pmap[y+1][x1-1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y+1][x1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y+1][x1+1], cm) &&
+ !PMAP_CMP_CONDUCTIVE(pmap[y+2][x1-1], cm) && PMAP_CMP_CONDUCTIVE(pmap[y+2][x1], cm) && !PMAP_CMP_CONDUCTIVE(pmap[y+2][x1+1], cm))
+ {
+ // travelling vertically down, skipping a horizontal line
+ if ((pmap[y+2][x1]&0xFF)==cm && !parts[pmap[y+2][x1]>>8].life)
+ {
+ coord_stack[coord_stack_size][0] = x1;
+ coord_stack[coord_stack_size][1] = y+2;
+ coord_stack_size++;
+ if (coord_stack_size>=coord_stack_limit)
+ {
+ free(coord_stack);
+ return -1;
+ }
+ }
+ }
+ else if (y<YRES-CELL-1)
+ {
+ for (x=x1; x<=x2; x++)
+ {
+ if ((pmap[y+1][x]&0xFF)==cm && !parts[pmap[y+1][x]>>8].life)
+ {
+ if (x==x1 || x==x2 || y<0 || !PMAP_CMP_CONDUCTIVE(pmap[y-1][x], cm))
+ {
+ // if at the end of a horizontal section, or if it's a T junction
+ coord_stack[coord_stack_size][0] = x;
+ coord_stack[coord_stack_size][1] = y+1;
+ coord_stack_size++;
+ if (coord_stack_size>=coord_stack_limit)
+ {
+ free(coord_stack);
+ return -1;
+ }
+ }
+
+ }
+ }
+ }
+ } while (coord_stack_size>0);
+ free(coord_stack);
+ return created_something;
+}
+
+
+int Simulation::FloodParts(int x, int y, int fullc, int cm, int bm, int flags)
+{
+ int c = fullc&0xFF;
+ int x1, x2, dy = (c<PT_NUM)?1:CELL;
+ int co = c;
+ int coord_stack_limit = XRES*YRES;
+ unsigned short (*coord_stack)[2];
+ int coord_stack_size = 0;
+ int created_something = 0;
+
+ if (c==SPC_PROP)
+ return 0;
+ if (cm==-1)
+ {
+ if (c==0)
+ {
+ cm = pmap[y][x]&0xFF;
+ if (!cm)
+ return 0;
+ }
+ else
+ cm = 0;
+ }
+ if (bm==-1)
+ {
+ bm = bmap[y/CELL][x/CELL];
+ }
+
+ if (((pmap[y][x]&0xFF)!=cm || bmap[y/CELL][x/CELL]!=bm ))
+ return 1;
+
+ coord_stack = (short unsigned int (*)[2])malloc(sizeof(unsigned short)*2*coord_stack_limit);
+ coord_stack[coord_stack_size][0] = x;
+ coord_stack[coord_stack_size][1] = y;
+ coord_stack_size++;
+
+ do
+ {
+ coord_stack_size--;
+ x = coord_stack[coord_stack_size][0];
+ y = coord_stack[coord_stack_size][1];
+ x1 = x2 = x;
+ // go left as far as possible
+ while (x1>=CELL)
+ {
+ if ((pmap[y][x1-1]&0xFF)!=cm || bmap[y/CELL][(x1-1)/CELL]!=bm)
+ {
+ break;
+ }
+ x1--;
+ }
+ // go right as far as possible
+ while (x2<XRES-CELL)
+ {
+ if ((pmap[y][x2+1]&0xFF)!=cm || bmap[y/CELL][(x2+1)/CELL]!=bm)
+ {
+ break;
+ }
+ x2++;
+ }
+ // fill span
+ for (x=x1; x<=x2; x++)
+ {
+ if (CreateParts(x, y, 0, 0, fullc, flags))
+ created_something = 1;
+ }
+
+ if (y>=CELL+dy)
+ for (x=x1; x<=x2; x++)
+ if ((pmap[y-dy][x]&0xFF)==cm && bmap[(y-dy)/CELL][x/CELL]==bm)
+ {
+ coord_stack[coord_stack_size][0] = x;
+ coord_stack[coord_stack_size][1] = y-dy;
+ coord_stack_size++;
+ if (coord_stack_size>=coord_stack_limit)
+ {
+ free(coord_stack);
+ return -1;
+ }
+ }
+
+ if (y<YRES-CELL-dy)
+ for (x=x1; x<=x2; x++)
+ if ((pmap[y+dy][x]&0xFF)==cm && bmap[(y+dy)/CELL][x/CELL]==bm)
+ {
+ coord_stack[coord_stack_size][0] = x;
+ coord_stack[coord_stack_size][1] = y+dy;
+ coord_stack_size++;
+ if (coord_stack_size>=coord_stack_limit)
+ {
+ free(coord_stack);
+ return -1;
+ }
+ }
+ } while (coord_stack_size>0);
+ free(coord_stack);
+ return created_something;
+}
+
+int Simulation::FloodWalls(int x, int y, int c, int cm, int bm, int flags)
+{
+ int x1, x2, dy = CELL;
+ int co = c;
+ if (cm==-1)
+ {
+ cm = pmap[y][x]&0xFF;
+ }
+ if (bm==-1)
+ {
+ if (c==WL_ERASE)
+ {
+ bm = bmap[y/CELL][x/CELL];
+ if (!bm)
+ return 0;
+ }
+ else
+ bm = 0;
+ }
+
+ if (((pmap[y][x]&0xFF)!=cm || bmap[y/CELL][x/CELL]!=bm )/*||( (flags&BRUSH_SPECIFIC_DELETE) && cm!=SLALT)*/)
+ return 1;
+
+ // go left as far as possible
+ x1 = x2 = x;
+ while (x1>=CELL)
+ {
+ if ((pmap[y][x1-1]&0xFF)!=cm || bmap[y/CELL][(x1-1)/CELL]!=bm)
+ {
+ break;
+ }
+ x1--;
+ }
+ while (x2<XRES-CELL)
+ {
+ if ((pmap[y][x2+1]&0xFF)!=cm || bmap[y/CELL][(x2+1)/CELL]!=bm)
+ {
+ break;
+ }
+ x2++;
+ }
+
+ // fill span
+ for (x=x1; x<=x2; x++)
+ {
+ if (!CreateWalls(x, y, 0, 0, c, flags))
+ return 0;
+ }
+ // fill children
+ if (y>=CELL+dy)
+ for (x=x1; x<=x2; x++)
+ if ((pmap[y-dy][x]&0xFF)==cm && bmap[(y-dy)/CELL][x/CELL]==bm)
+ if (!FloodWalls(x, y-dy, c, cm, bm, flags))
+ return 0;
+ if (y<YRES-CELL-dy)
+ for (x=x1; x<=x2; x++)
+ if ((pmap[y+dy][x]&0xFF)==cm && bmap[(y+dy)/CELL][x/CELL]==bm)
+ if (!FloodWalls(x, y+dy, c, cm, bm, flags))
+ return 0;
+ return 1;
+}
+int Simulation::flood_water(int x, int y, int i, int originaly, int check)
+{
+ int x1 = 0,x2 = 0;
+ // go left as far as possible
+ x1 = x2 = x;
+ if (!pmap[y][x])
+ return 1;
+
+ while (x1>=CELL)
+ {
+ if ((elements[(pmap[y][x1-1]&0xFF)].Falldown)!=2)
+ {
+ break;
+ }
+ x1--;
+ }
+ while (x2<XRES-CELL)
+ {
+ if ((elements[(pmap[y][x2+1]&0xFF)].Falldown)!=2)
+ {
+ break;
+ }
+ x2++;
+ }
+
+ // fill span
+ for (x=x1; x<=x2; x++)
+ {
+ parts[pmap[y][x]>>8].tmp2 = !check;//flag it as checked, maybe shouldn't use .tmp2
+ //check above, maybe around other sides too?
+ if ( ((y-1) > originaly) && !pmap[y-1][x] && eval_move(parts[i].type, x, y-1, NULL))
+ {
+ int oldx = (int)(parts[i].x + 0.5f);
+ int oldy = (int)(parts[i].y + 0.5f);
+ pmap[y-1][x] = pmap[oldy][oldx];
+ pmap[oldy][oldx] = 0;
+ parts[i].x = x;
+ parts[i].y = y-1;
+ return 0;
+ }
+ }
+ // fill children
+
+ if (y>=CELL+1)
+ for (x=x1; x<=x2; x++)
+ if ((elements[(pmap[y-1][x]&0xFF)].Falldown)==2 && parts[pmap[y-1][x]>>8].tmp2 == check)
+ if (!flood_water(x, y-1, i, originaly, check))
+ return 0;
+ if (y<YRES-CELL-1)
+ for (x=x1; x<=x2; x++)
+ if ((elements[(pmap[y+1][x]&0xFF)].Falldown)==2 && parts[pmap[y+1][x]>>8].tmp2 == check)
+ if (!flood_water(x, y+1, i, originaly, check))
+ return 0;
+ return 1;
+}
+
+//wrapper around create_part to create TESC with correct tmp value
+int Simulation::create_part_add_props(int p, int x, int y, int tv, int rx, int ry)
+{
+ p=create_part(p, x, y, tv);
+ if (tv==PT_TESC)
+ {
+ parts[p].tmp=rx*4+ry*4+7;
+ if (parts[p].tmp>300)
+ parts[p].tmp=300;
+ }
+ return p;
+}
+
+void Simulation::SetEdgeMode(int newEdgeMode)
+{
+ edgeMode = newEdgeMode;
+ switch(edgeMode)
+ {
+ case 0:
+ for(int i = 0; i<(XRES/CELL); i++)
+ {
+ bmap[0][i] = 0;
+ bmap[YRES/CELL-1][i] = 0;
+ }
+ for(int i = 1; i<((YRES/CELL)-1); i++)
+ {
+ bmap[i][0] = 0;
+ bmap[i][XRES/CELL-1] = 0;
+ }
+ break;
+ case 1:
+ int i;
+ for(i=0; i<(XRES/CELL); i++)
+ {
+ bmap[0][i] = WL_WALL;
+ bmap[YRES/CELL-1][i] = WL_WALL;
+ }
+ for(i=1; i<((YRES/CELL)-1); i++)
+ {
+ bmap[i][0] = WL_WALL;
+ bmap[i][XRES/CELL-1] = WL_WALL;
+ }
+ break;
+ default:
+ SetEdgeMode(0);
+ }
+}
+
+void Simulation::ApplyDecoration(int x, int y, int colR_, int colG_, int colB_, int colA_, int mode)
+{
+ int rp;
+ float tr, tg, tb, ta, colR = colR_, colG = colG_, colB = colB_, colA = colA_;
+ float strength = 0.01f;
+ rp = pmap[y][x];
+ if (!rp)
+ return;
+
+ ta = (parts[rp>>8].dcolour>>24)&0xFF;
+ tr = (parts[rp>>8].dcolour>>16)&0xFF;
+ tg = (parts[rp>>8].dcolour>>8)&0xFF;
+ tb = (parts[rp>>8].dcolour)&0xFF;
+
+ ta /= 255.0f; tr /= 255.0f; tg /= 255.0f; tb /= 255.0f;
+ colR /= 255.0f; colG /= 255.0f; colB /= 255.0f; colA /= 255.0f;
+
+ if (mode == DECO_DRAW)
+ {
+ ta = colA;
+ tr = colR;
+ tg = colG;
+ tb = colB;
+ }
+ else if (mode == DECO_CLEAR)
+ {
+ ta = tr = tg = tb = 0.0f;
+ }
+ else if (mode == DECO_ADD)
+ {
+ //ta += (colA*strength)*colA;
+ tr += (colR*strength)*colA;
+ tg += (colG*strength)*colA;
+ tb += (colB*strength)*colA;
+ }
+ else if (mode == DECO_SUBTRACT)
+ {
+ //ta -= (colA*strength)*colA;
+ tr -= (colR*strength)*colA;
+ tg -= (colG*strength)*colA;
+ tb -= (colB*strength)*colA;
+ }
+ else if (mode == DECO_MULTIPLY)
+ {
+ tr *= 1.0f+(colR*strength)*colA;
+ tg *= 1.0f+(colG*strength)*colA;
+ tb *= 1.0f+(colB*strength)*colA;
+ }
+ else if (mode == DECO_DIVIDE)
+ {
+ tr /= 1.0f+(colR*strength)*colA;
+ tg /= 1.0f+(colG*strength)*colA;
+ tb /= 1.0f+(colB*strength)*colA;
+ }
+ else if (mode == DECO_SMUDGE)
+ {
+ float tas = 0.0f, trs = 0.0f, tgs = 0.0f, tbs = 0.0f;
+
+ int rx, ry;
+ float num = 0;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ {
+ if ((pmap[y+ry][x+rx]&0xFF) && parts[pmap[y+ry][x+rx]>>8].dcolour)
+ {
+ Particle part = parts[pmap[y+ry][x+rx]>>8];
+ num += 1.0f;
+ tas += ((float)((part.dcolour>>24)&0xFF))/255.0f;
+ trs += ((float)((part.dcolour>>16)&0xFF))/255.0f;
+ tgs += ((float)((part.dcolour>>8)&0xFF))/255.0f;
+ tbs += ((float)((part.dcolour)&0xFF))/255.0f;
+ }
+ }
+ if (num == 0)
+ return;
+ ta = ((tas/num));//*0.8f) + (ta*0.2f);
+ tr = ((trs/num));//*0.8f) + (tr*0.2f);
+ tg = ((tgs/num));//*0.8f) + (tg*0.2f);
+ tb = ((tbs/num));//*0.8f) + (tb*0.2f);
+ }
+
+ ta *= 255.0f; tr *= 255.0f; tg *= 255.0f; tb *= 255.0f;
+ ta += .5f; tr += .5f; tg += .5f; tb += .5f;
+
+ colA_ = ta;
+ colR_ = tr;
+ colG_ = tg;
+ colB_ = tb;
+
+ if(colA_ > 255)
+ colA_ = 255;
+ else if(colA_ < 0)
+ colA_ = 0;
+ if(colR_ > 255)
+ colR_ = 255;
+ else if(colR_ < 0)
+ colR_ = 0;
+ if(colG_ > 255)
+ colG_ = 255;
+ else if(colG_ < 0)
+ colG_ = 0;
+ if(colB_ > 255)
+ colB_ = 255;
+ else if(colB_ < 0)
+ colB_ = 0;
+ parts[rp>>8].dcolour = ((colA_<<24)|(colR_<<16)|(colG_<<8)|colB_);
+}
+
+void Simulation::ApplyDecorationPoint(int positionX, int positionY, int colR, int colG, int colB, int colA, int mode, Brush * cBrush)
+{
+ int i, j;
+
+ if(cBrush)
+ {
+ int radiusX, radiusY, sizeX, sizeY;
+
+ radiusX = cBrush->GetRadius().X;
+ radiusY = cBrush->GetRadius().Y;
+
+ sizeX = cBrush->GetSize().X;
+ sizeY = cBrush->GetSize().Y;
+
+ unsigned char *bitmap = cBrush->GetBitmap();
+ for(int y = 0; y < sizeY; y++)
+ {
+ for(int x = 0; x < sizeX; x++)
+ {
+ if(bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES))
+ {
+ ApplyDecoration(positionX+(x-radiusX), positionY+(y-radiusY), colR, colG, colB, colA, mode);
+ }
+ }
+ }
+ }
+}
+
+void Simulation::ApplyDecorationBox(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode)
+{
+ int i, j;
+
+ if (x1>x2)
+ {
+ i = x2;
+ x2 = x1;
+ x1 = i;
+ }
+ if (y1>y2)
+ {
+ j = y2;
+ y2 = y1;
+ y1 = j;
+ }
+ for (j=y1; j<=y2; j++)
+ for (i=x1; i<=x2; i++)
+ ApplyDecoration(i, j, colR, colG, colB, colA, mode);
+}
+
+void Simulation::ApplyDecorationLine(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode, Brush * cBrush)
+{
+ int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy, rx, ry;
+ float e, de;
+
+ if(cBrush)
+ {
+ rx = cBrush->GetRadius().X;
+ ry = cBrush->GetRadius().Y;
+ }
+
+ if (cp)
+ {
+ y = x1;
+ x1 = y1;
+ y1 = y;
+ y = x2;
+ x2 = y2;
+ y2 = y;
+ }
+ if (x1 > x2)
+ {
+ y = x1;
+ x1 = x2;
+ x2 = y;
+ y = y1;
+ y1 = y2;
+ y2 = y;
+ }
+ dx = x2 - x1;
+ dy = abs(y2 - y1);
+ e = 0.0f;
+ if (dx)
+ de = dy/(float)dx;
+ else
+ de = 0.0f;
+ y = y1;
+ sy = (y1<y2) ? 1 : -1;
+ for (x=x1; x<=x2; x++)
+ {
+ if (cp)
+ ApplyDecorationPoint(y, x, colR, colG, colB, colA, mode, cBrush);
+ else
+ ApplyDecorationPoint(x, y, colR, colG, colB, colA, mode, cBrush);
+ e += de;
+ if (e >= 0.5f)
+ {
+ y += sy;
+ if (!(rx+ry))
+ {
+ if (cp)
+ ApplyDecorationPoint(y, x, colR, colG, colB, colA, mode, cBrush);
+ else
+ ApplyDecorationPoint(x, y, colR, colG, colB, colA, mode, cBrush);
+ }
+ e -= 1.0f;
+ }
+ }
+}
+
+int Simulation::Tool(int x, int y, int tool, float strength)
+{
+ if(tools[tool])
+ {
+ Particle * cpart = NULL;
+ int r;
+ if(r = pmap[y][x])
+ cpart = &(parts[r>>8]);
+ else if(r = photons[y][x])
+ cpart = &(parts[r>>8]);
+ return tools[tool]->Perform(this, cpart, x, y, strength);
+ }
+ return 0;
+}
+
+int Simulation::ToolBrush(int positionX, int positionY, int tool, Brush * cBrush, float strength)
+{
+ if(cBrush)
+ {
+ int radiusX, radiusY, sizeX, sizeY;
+
+ radiusX = cBrush->GetRadius().X;
+ radiusY = cBrush->GetRadius().Y;
+
+ sizeX = cBrush->GetSize().X;
+ sizeY = cBrush->GetSize().Y;
+ unsigned char *bitmap = cBrush->GetBitmap();
+ for(int y = 0; y < sizeY; y++)
+ for(int x = 0; x < sizeX; x++)
+ if(bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES))
+ Tool(positionX+(x-radiusX), positionY+(y-radiusY), tool, strength);
+ }
+ return 0;
+}
+
+void Simulation::ToolLine(int x1, int y1, int x2, int y2, int tool, Brush * cBrush, float strength)
+{
+ int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy, rx, ry;
+ float e, de;
+ rx = cBrush->GetRadius().X;
+ ry = cBrush->GetRadius().Y;
+ if (cp)
+ {
+ y = x1;
+ x1 = y1;
+ y1 = y;
+ y = x2;
+ x2 = y2;
+ y2 = y;
+ }
+ if (x1 > x2)
+ {
+ y = x1;
+ x1 = x2;
+ x2 = y;
+ y = y1;
+ y1 = y2;
+ y2 = y;
+ }
+ dx = x2 - x1;
+ dy = abs(y2 - y1);
+ e = 0.0f;
+ if (dx)
+ de = dy/(float)dx;
+ else
+ de = 0.0f;
+ y = y1;
+ sy = (y1<y2) ? 1 : -1;
+ for (x=x1; x<=x2; x++)
+ {
+ if (cp)
+ ToolBrush(y, x, tool, cBrush, strength);
+ else
+ ToolBrush(x, y, tool, cBrush, strength);
+ e += de;
+ if (e >= 0.5f)
+ {
+ y += sy;
+ if ((!(rx+ry)) && ((y1<y2) ? (y<=y2) : (y>=y2)))
+ {
+ if (cp)
+ ToolBrush(y, x, tool, cBrush, strength);
+ else
+ ToolBrush(x, y, tool, cBrush, strength);
+ }
+ e -= 1.0f;
+ }
+ }
+}
+void Simulation::ToolBox(int x1, int y1, int x2, int y2, int tool, Brush * cBrush, float strength)
+{
+ int i, j;
+ if (x1>x2)
+ {
+ i = x2;
+ x2 = x1;
+ x1 = i;
+ }
+ if (y1>y2)
+ {
+ j = y2;
+ y2 = y1;
+ y1 = j;
+ }
+ for (j=y1; j<=y2; j++)
+ for (i=x1; i<=x2; i++)
+ Tool(i, j, tool, strength);
+}
+
+int Simulation::CreateParts(int positionX, int positionY, int c, Brush * cBrush)
+{
+ if(cBrush)
+ {
+ int radiusX, radiusY, sizeX, sizeY;
+
+ radiusX = cBrush->GetRadius().X;
+ radiusY = cBrush->GetRadius().Y;
+
+ sizeX = cBrush->GetSize().X;
+ sizeY = cBrush->GetSize().Y;
+
+ unsigned char *bitmap = cBrush->GetBitmap();
+
+ if(c == PT_NONE)
+ {
+ for(int y = 0; y < sizeY; y++)
+ {
+ for(int x = 0; x < sizeX; x++)
+ {
+ if(bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES))
+ {
+ delete_part(positionX+(x-radiusX), positionY+(y-radiusY), 0);
+ }
+ }
+ }
+ }
+ else
+ {
+ for(int y = 0; y < sizeY; y++)
+ {
+ for(int x = 0; x < sizeX; x++)
+ {
+ if(bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES))
+ {
+ create_part(-2, positionX+(x-radiusX), positionY+(y-radiusY), c);
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+int Simulation::CreateParts(int x, int y, int rx, int ry, int c, int flags)
+{
+ int i, j, r, f = 0, u, v, oy, ox, b = 0, dw = 0, stemp = 0, p;
+ int wall = c - 100;
+ if (c==SPC_WIND || c==PT_FIGH)
+ return 0;
+
+ if (c==PT_LIGH)
+ {
+ if (lighting_recreate>0 && rx+ry>0)
+ return 0;
+ p=create_part(-2, x, y, c);
+ if (p!=-1)
+ {
+ parts[p].life=rx+ry;
+ if (parts[p].life>55)
+ parts[p].life=55;
+ parts[p].temp=parts[p].life*150; // temperature of the lighting shows the power of the lighting
+ lighting_recreate+=parts[p].life/2+1;
+ return 1;
+ }
+ else return 0;
+ }
+
+ //eraser
+ if (c == 0)
+ {
+ if (rx==0&&ry==0)
+ {
+ delete_part(x, y, 0);
+ }
+ else
+ {
+ for (j=-ry; j<=ry; j++)
+ for (i=-rx; i<=rx; i++)
+ delete_part(x+i, y+j, 0);
+ }
+ return 1;
+ }
+
+ if (c == SPC_AIR || c == SPC_HEAT || c == SPC_COOL || c == SPC_VACUUM || c == SPC_PGRV || c == SPC_NGRV)
+ {
+ if (rx==0&&ry==0)
+ {
+ create_part(-2, x, y, c);
+ }
+ else
+ {
+ for (j=-ry; j<=ry; j++)
+ for (i=-rx; i<=rx; i++)
+ {
+ if ( x+i<0 || y+j<0 || x+i>=XRES || y+j>=YRES)
+ continue;
+ create_part(-2, x+i, y+j, c);
+ }
+ }
+ return 1;
+ }
+
+ //else, no special modes, draw element like normal.
+ if (rx==0&&ry==0)//workaround for 1pixel brush/floodfill crashing. todo: find a better fix later.
+ {
+ if (create_part_add_props(-2, x, y, c, rx, ry)==-1)
+ f = 1;
+ }
+ else
+ {
+ for (j=-ry; j<=ry; j++)
+ for (i=-rx; i<=rx; i++)
+ if (create_part_add_props(-2, x+i, y+j, c, rx, ry)==-1)
+ f = 1;
+ }
+ return !f;
+}
+
+int Simulation::CreateWalls(int x, int y, int rx, int ry, int c, int flags, Brush * cBrush)
+{
+ int i, j, r, f = 0, u, v, oy, ox, b = 0, dw = 0, stemp = 0, p;//n;
+
+ if(cBrush)
+ {
+ rx = cBrush->GetRadius().X;
+ ry = cBrush->GetRadius().Y;
+ }
+
+ int wall = c;
+
+ if (wall == WL_ERASE)
+ b = 0;
+ else
+ b = wall;
+
+ ry = ry/CELL;
+ rx = rx/CELL;
+ x = x/CELL;
+ y = y/CELL;
+ x -= rx;///2;
+ y -= ry;///2;
+ for (ox=x; ox<=x+rx+rx; ox++)
+ {
+ for (oy=y; oy<=y+ry+ry; oy++)
+ {
+ if (ox>=0&&ox<XRES/CELL&&oy>=0&&oy<YRES/CELL)
+ {
+ i = ox;
+ j = oy;
+ if (b==WL_FAN)
+ {
+ fvx[j][i] = 0.0f;
+ fvy[j][i] = 0.0f;
+ }
+ if (b==WL_GRAV || bmap[j][i]==WL_GRAV)
+ {
+ gravWallChanged = true;
+ }
+ if (b==WL_STREAM)
+ {
+ i = x + rx;///2;
+ j = y + ry;///2;
+ for (v=-1; v<2; v++)
+ for (u=-1; u<2; u++)
+ if (i+u>=0 && i+u<XRES/CELL &&
+ j+v>=0 && j+v<YRES/CELL &&
+ bmap[j+v][i+u] == WL_STREAM)
+ return 1;
+ bmap[j][i] = WL_STREAM;
+ continue;
+ }
+ bmap[j][i] = b;
+ }
+ }
+ }
+ return 1;
+}
+
+void Simulation::CreateLine(int x1, int y1, int x2, int y2, int c, Brush * cBrush)
+{
+ int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy, rx, ry;
+ rx = cBrush->GetRadius().X;
+ ry = cBrush->GetRadius().Y;
+ float e, de;
+ if (c==SPC_PROP)
+ return;
+ if (cp)
+ {
+ y = x1;
+ x1 = y1;
+ y1 = y;
+ y = x2;
+ x2 = y2;
+ y2 = y;
+ }
+ if (x1 > x2)
+ {
+ y = x1;
+ x1 = x2;
+ x2 = y;
+ y = y1;
+ y1 = y2;
+ y2 = y;
+ }
+ dx = x2 - x1;
+ dy = abs(y2 - y1);
+ e = 0.0f;
+ if (dx)
+ de = dy/(float)dx;
+ else
+ de = 0.0f;
+ y = y1;
+ sy = (y1<y2) ? 1 : -1;
+ for (x=x1; x<=x2; x++)
+ {
+ if (cp)
+ CreateParts(y, x, c, cBrush);
+ else
+ CreateParts(x, y, c, cBrush);
+ e += de;
+ if (e >= 0.5f)
+ {
+ y += sy;
+ if ((c==WL_EHOLE+100 || c==WL_ALLOWGAS+100 || c==WL_ALLOWENERGY+100 || c==WL_ALLOWALLELEC+100 || c==WL_ALLOWSOLID+100 || c==WL_ALLOWAIR+100 || c==WL_WALL+100 || c==WL_DESTROYALL+100 || c==WL_ALLOWLIQUID+100 || c==WL_FAN+100 || c==WL_STREAM+100 || c==WL_DETECT+100 || c==WL_EWALL+100 || c==WL_WALLELEC+100 || !(rx+ry))
+ && ((y1<y2) ? (y<=y2) : (y>=y2)))
+ {
+ if (cp)
+ CreateParts(y, x, c, cBrush);
+ else
+ CreateParts(x, y, c, cBrush);
+ }
+ e -= 1.0f;
+ }
+ }
+}
+
+void Simulation::CreateLine(int x1, int y1, int x2, int y2, int rx, int ry, int c, int flags)
+{
+ int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy;
+ float e, de;
+ if (c==SPC_PROP)
+ return;
+ if (cp)
+ {
+ y = x1;
+ x1 = y1;
+ y1 = y;
+ y = x2;
+ x2 = y2;
+ y2 = y;
+ }
+ if (x1 > x2)
+ {
+ y = x1;
+ x1 = x2;
+ x2 = y;
+ y = y1;
+ y1 = y2;
+ y2 = y;
+ }
+ dx = x2 - x1;
+ dy = abs(y2 - y1);
+ e = 0.0f;
+ if (dx)
+ de = dy/(float)dx;
+ else
+ de = 0.0f;
+ y = y1;
+ sy = (y1<y2) ? 1 : -1;
+ for (x=x1; x<=x2; x++)
+ {
+ if (cp)
+ CreateParts(y, x, rx, ry, c, flags);
+ else
+ CreateParts(x, y, rx, ry, c, flags);
+ e += de;
+ if (e >= 0.5f)
+ {
+ y += sy;
+ if ((c==WL_EHOLE+100 || c==WL_ALLOWGAS+100 || c==WL_ALLOWENERGY+100 || c==WL_ALLOWALLELEC+100 || c==WL_ALLOWSOLID+100 || c==WL_ALLOWAIR+100 || c==WL_WALL+100 || c==WL_DESTROYALL+100 || c==WL_ALLOWLIQUID+100 || c==WL_FAN+100 || c==WL_STREAM+100 || c==WL_DETECT+100 || c==WL_EWALL+100 || c==WL_WALLELEC+100 || !(rx+ry))
+ && ((y1<y2) ? (y<=y2) : (y>=y2)))
+ {
+ if (cp)
+ CreateParts(y, x, rx, ry, c, flags);
+ else
+ CreateParts(x, y, rx, ry, c, flags);
+ }
+ e -= 1.0f;
+ }
+ }
+}
+
+void Simulation::CreateWallLine(int x1, int y1, int x2, int y2, int rx, int ry, int c, int flags, Brush * cBrush)
+{
+ int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy;
+ float e, de;
+ if (cp)
+ {
+ y = x1;
+ x1 = y1;
+ y1 = y;
+ y = x2;
+ x2 = y2;
+ y2 = y;
+ }
+ if (x1 > x2)
+ {
+ y = x1;
+ x1 = x2;
+ x2 = y;
+ y = y1;
+ y1 = y2;
+ y2 = y;
+ }
+ dx = x2 - x1;
+ dy = abs(y2 - y1);
+ e = 0.0f;
+ if (dx)
+ de = dy/(float)dx;
+ else
+ de = 0.0f;
+ y = y1;
+ sy = (y1<y2) ? 1 : -1;
+ for (x=x1; x<=x2; x++)
+ {
+ if (cp)
+ CreateWalls(y, x, rx, ry, c, flags, cBrush);
+ else
+ CreateWalls(x, y, rx, ry, c, flags, cBrush);
+ e += de;
+ if (e >= 0.5f)
+ {
+ y += sy;
+ if (!(rx+ry) && ((y1<y2) ? (y<=y2) : (y>=y2)))
+ {
+ if (cp)
+ CreateWalls(y, x, rx, ry, c, flags, cBrush);
+ else
+ CreateWalls(x, y, rx, ry, c, flags, cBrush);
+ }
+ e -= 1.0f;
+ }
+ }
+}
+
+void *Simulation::transform_save(void *odata, int *size, matrix2d transform, vector2d translate)
+{
+ void *ndata;
+ unsigned char (*bmapo)[XRES/CELL] = (unsigned char (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(unsigned char));
+ unsigned char (*bmapn)[XRES/CELL] = (unsigned char (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(unsigned char));
+ Particle *partst = (Particle*)calloc(sizeof(Particle), NPART);
+ sign *signst = (sign*)calloc(MAXSIGNS, sizeof(sign));
+ unsigned (*pmapt)[XRES] = (unsigned (*)[XRES])calloc(YRES*XRES, sizeof(unsigned));
+ float (*fvxo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
+ float (*fvyo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
+ float (*fvxn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
+ float (*fvyn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
+ float (*vxo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
+ float (*vyo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
+ float (*vxn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
+ float (*vyn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
+ float (*pvo)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
+ float (*pvn)[XRES/CELL] = (float (*)[XRES/CELL])calloc((YRES/CELL)*(XRES/CELL), sizeof(float));
+ int i, x, y, nx, ny, w, h, nw, nh;
+ vector2d pos, tmp, ctl, cbr;
+ vector2d vel;
+ vector2d cornerso[4];
+ unsigned char *odatac = (unsigned char *)odata;
+ //if (parse_save(odata, *size, 0, 0, 0, bmapo, vxo, vyo, pvo, fvxo, fvyo, signst, partst, pmapt)) //TODO: Implement
+ {
+ free(bmapo);
+ free(bmapn);
+ free(partst);
+ free(signst);
+ free(pmapt);
+ free(fvxo);
+ free(fvyo);
+ free(fvxn);
+ free(fvyn);
+ free(vxo);
+ free(vyo);
+ free(vxn);
+ free(vyn);
+ free(pvo);
+ free(pvn);
+ return odata;
+ }
+ w = odatac[6]*CELL;
+ h = odatac[7]*CELL;
+ // undo any translation caused by rotation
+ cornerso[0] = v2d_new(0,0);
+ cornerso[1] = v2d_new(w-1,0);
+ cornerso[2] = v2d_new(0,h-1);
+ cornerso[3] = v2d_new(w-1,h-1);
+ for (i=0; i<4; i++)
+ {
+ tmp = m2d_multiply_v2d(transform,cornerso[i]);
+ if (i==0) ctl = cbr = tmp; // top left, bottom right corner
+ if (tmp.x<ctl.x) ctl.x = tmp.x;
+ if (tmp.y<ctl.y) ctl.y = tmp.y;
+ if (tmp.x>cbr.x) cbr.x = tmp.x;
+ if (tmp.y>cbr.y) cbr.y = tmp.y;
+ }
+ // casting as int doesn't quite do what we want with negative numbers, so use floor()
+ tmp = v2d_new(floor(ctl.x+0.5f),floor(ctl.y+0.5f));
+ translate = v2d_sub(translate,tmp);
+ nw = floor(cbr.x+0.5f)-floor(ctl.x+0.5f)+1;
+ nh = floor(cbr.y+0.5f)-floor(ctl.y+0.5f)+1;
+ if (nw>XRES) nw = XRES;
+ if (nh>YRES) nh = YRES;
+ // rotate and translate signs, parts, walls
+ for (i=0; i<MAXSIGNS; i++)
+ {
+ if (!signst[i].text[0]) continue;
+ pos = v2d_new(signst[i].x, signst[i].y);
+ pos = v2d_add(m2d_multiply_v2d(transform,pos),translate);
+ nx = floor(pos.x+0.5f);
+ ny = floor(pos.y+0.5f);
+ if (nx<0 || nx>=nw || ny<0 || ny>=nh)
+ {
+ signst[i].text[0] = 0;
+ continue;
+ }
+ signst[i].x = nx;
+ signst[i].y = ny;
+ }
+ for (i=0; i<NPART; i++)
+ {
+ if (!partst[i].type) continue;
+ pos = v2d_new(partst[i].x, partst[i].y);
+ pos = v2d_add(m2d_multiply_v2d(transform,pos),translate);
+ nx = floor(pos.x+0.5f);
+ ny = floor(pos.y+0.5f);
+ if (nx<0 || nx>=nw || ny<0 || ny>=nh)
+ {
+ partst[i].type = PT_NONE;
+ continue;
+ }
+ partst[i].x = nx;
+ partst[i].y = ny;
+ vel = v2d_new(partst[i].vx, partst[i].vy);
+ vel = m2d_multiply_v2d(transform, vel);
+ partst[i].vx = vel.x;
+ partst[i].vy = vel.y;
+ }
+ for (y=0; y<YRES/CELL; y++)
+ for (x=0; x<XRES/CELL; x++)
+ {
+ pos = v2d_new(x*CELL+CELL*0.4f, y*CELL+CELL*0.4f);
+ pos = v2d_add(m2d_multiply_v2d(transform,pos),translate);
+ nx = pos.x/CELL;
+ ny = pos.y/CELL;
+ if (nx<0 || nx>=nw/CELL || ny<0 || ny>=nh/CELL)
+ continue;
+ if (bmapo[y][x])
+ {
+ bmapn[ny][nx] = bmapo[y][x];
+ if (bmapo[y][x]==WL_FAN)
+ {
+ vel = v2d_new(fvxo[y][x], fvyo[y][x]);
+ vel = m2d_multiply_v2d(transform, vel);
+ fvxn[ny][nx] = vel.x;
+ fvyn[ny][nx] = vel.y;
+ }
+ }
+ vel = v2d_new(vxo[y][x], vyo[y][x]);
+ vel = m2d_multiply_v2d(transform, vel);
+ vxn[ny][nx] = vel.x;
+ vyn[ny][nx] = vel.y;
+ pvn[ny][nx] = pvo[y][x];
+ }
+ //ndata = build_save(size,0,0,nw,nh,bmapn,vxn,vyn,pvn,fvxn,fvyn,signst,partst); //TODO: IMPLEMENT
+ free(bmapo);
+ free(bmapn);
+ free(partst);
+ free(signst);
+ free(pmapt);
+ free(fvxo);
+ free(fvyo);
+ free(fvxn);
+ free(fvyn);
+ free(vxo);
+ free(vyo);
+ free(vxn);
+ free(vyn);
+ free(pvo);
+ free(pvn);
+ return ndata;
+}
+
+TPT_NO_INLINE void Simulation::orbitalparts_get(int block1, int block2, int resblock1[], int resblock2[])
+{
+ resblock1[0] = (block1&0x000000FF);
+ resblock1[1] = (block1&0x0000FF00)>>8;
+ resblock1[2] = (block1&0x00FF0000)>>16;
+ resblock1[3] = (block1&0xFF000000)>>24;
+
+ resblock2[0] = (block2&0x000000FF);
+ resblock2[1] = (block2&0x0000FF00)>>8;
+ resblock2[2] = (block2&0x00FF0000)>>16;
+ resblock2[3] = (block2&0xFF000000)>>24;
+}
+
+TPT_NO_INLINE void Simulation::orbitalparts_set(int *block1, int *block2, int resblock1[], int resblock2[])
+{
+ int block1tmp = 0;
+ int block2tmp = 0;
+
+ block1tmp = (resblock1[0]&0xFF);
+ block1tmp |= (resblock1[1]&0xFF)<<8;
+ block1tmp |= (resblock1[2]&0xFF)<<16;
+ block1tmp |= (resblock1[3]&0xFF)<<24;
+
+ block2tmp = (resblock2[0]&0xFF);
+ block2tmp |= (resblock2[1]&0xFF)<<8;
+ block2tmp |= (resblock2[2]&0xFF)<<16;
+ block2tmp |= (resblock2[3]&0xFF)<<24;
+
+ *block1 = block1tmp;
+ *block2 = block2tmp;
+}
+
+inline int Simulation::is_wire(int x, int y)
+{
+ return bmap[y][x]==WL_DETECT || bmap[y][x]==WL_EWALL || bmap[y][x]==WL_ALLOWLIQUID || bmap[y][x]==WL_WALLELEC || bmap[y][x]==WL_ALLOWALLELEC || bmap[y][x]==WL_EHOLE;
+}
+
+inline int Simulation::is_wire_off(int x, int y)
+{
+ return (bmap[y][x]==WL_DETECT || bmap[y][x]==WL_EWALL || bmap[y][x]==WL_ALLOWLIQUID || bmap[y][x]==WL_WALLELEC || bmap[y][x]==WL_ALLOWALLELEC || bmap[y][x]==WL_EHOLE) && emap[y][x]<8;
+}
+
+int Simulation::get_wavelength_bin(int *wm)
+{
+ int i, w0=30, wM=0;
+
+ if (!*wm)
+ return -1;
+
+ for (i=0; i<30; i++)
+ if (*wm & (1<<i)) {
+ if (i < w0)
+ w0 = i;
+ if (i > wM)
+ wM = i;
+ }
+
+ if (wM-w0 < 5)
+ return (wM+w0)/2;
+
+ i = rand() % (wM-w0-3);
+ i += w0;
+
+ *wm &= 0x1F << i;
+ return i + 2;
+}
+
+void Simulation::set_emap(int x, int y)
+{
+ int x1, x2;
+
+ if (!is_wire_off(x, y))
+ return;
+
+ // go left as far as possible
+ x1 = x2 = x;
+ while (x1>0)
+ {
+ if (!is_wire_off(x1-1, y))
+ break;
+ x1--;
+ }
+ while (x2<XRES/CELL-1)
+ {
+ if (!is_wire_off(x2+1, y))
+ break;
+ x2++;
+ }
+
+ // fill span
+ for (x=x1; x<=x2; x++)
+ emap[y][x] = 16;
+
+ // fill children
+
+ if (y>1 && x1==x2 &&
+ is_wire(x1-1, y-1) && is_wire(x1, y-1) && is_wire(x1+1, y-1) &&
+ !is_wire(x1-1, y-2) && is_wire(x1, y-2) && !is_wire(x1+1, y-2))
+ set_emap(x1, y-2);
+ else if (y>0)
+ for (x=x1; x<=x2; x++)
+ if (is_wire_off(x, y-1))
+ {
+ if (x==x1 || x==x2 || y>=YRES/CELL-1 ||
+ is_wire(x-1, y-1) || is_wire(x+1, y-1) ||
+ is_wire(x-1, y+1) || !is_wire(x, y+1) || is_wire(x+1, y+1))
+ set_emap(x, y-1);
+ }
+
+ if (y<YRES/CELL-2 && x1==x2 &&
+ is_wire(x1-1, y+1) && is_wire(x1, y+1) && is_wire(x1+1, y+1) &&
+ !is_wire(x1-1, y+2) && is_wire(x1, y+2) && !is_wire(x1+1, y+2))
+ set_emap(x1, y+2);
+ else if (y<YRES/CELL-1)
+ for (x=x1; x<=x2; x++)
+ if (is_wire_off(x, y+1))
+ {
+ if (x==x1 || x==x2 || y<0 ||
+ is_wire(x-1, y+1) || is_wire(x+1, y+1) ||
+ is_wire(x-1, y-1) || !is_wire(x, y-1) || is_wire(x+1, y-1))
+ set_emap(x, y+1);
+ }
+}
+
+int Simulation::parts_avg(int ci, int ni,int t)
+{
+ if (t==PT_INSL)//to keep electronics working
+ {
+ int pmr = pmap[((int)(parts[ci].y+0.5f) + (int)(parts[ni].y+0.5f))/2][((int)(parts[ci].x+0.5f) + (int)(parts[ni].x+0.5f))/2];
+ if (pmr)
+ return parts[pmr>>8].type;
+ else
+ return PT_NONE;
+ }
+ else
+ {
+ int pmr2 = pmap[(int)((parts[ci].y + parts[ni].y)/2+0.5f)][(int)((parts[ci].x + parts[ni].x)/2+0.5f)];//seems to be more accurate.
+ if (pmr2)
+ {
+ if (parts[pmr2>>8].type==t)
+ return t;
+ }
+ else
+ return PT_NONE;
+ }
+ return PT_NONE;
+}
+
+
+int Simulation::nearest_part(int ci, int t, int max_d)
+{
+ int distance = (max_d!=-1)?max_d:MAX_DISTANCE;
+ int ndistance = 0;
+ int id = -1;
+ int i = 0;
+ int cx = (int)parts[ci].x;
+ int cy = (int)parts[ci].y;
+ for (i=0; i<=parts_lastActiveIndex; i++)
+ {
+ if ((parts[i].type==t||(t==-1&&parts[i].type))&&!parts[i].life&&i!=ci)
+ {
+ ndistance = abs(cx-parts[i].x)+abs(cy-parts[i].y);// Faster but less accurate Older: sqrt(pow(cx-parts[i].x, 2)+pow(cy-parts[i].y, 2));
+ if (ndistance<distance)
+ {
+ distance = ndistance;
+ id = i;
+ }
+ }
+ }
+ return id;
+}
+
+void Simulation::create_arc(int sx, int sy, int dx, int dy, int midpoints, int variance, int type, int flags)
+{
+ int i;
+ float xint, yint;
+ int *xmid, *ymid;
+ int voffset = variance/2;
+ xmid = (int *)calloc(midpoints + 2, sizeof(int));
+ ymid = (int *)calloc(midpoints + 2, sizeof(int));
+ xint = (float)(dx-sx)/(float)(midpoints+1.0f);
+ yint = (float)(dy-sy)/(float)(midpoints+1.0f);
+ xmid[0] = sx;
+ xmid[midpoints+1] = dx;
+ ymid[0] = sy;
+ ymid[midpoints+1] = dy;
+
+ for(i = 1; i <= midpoints; i++)
+ {
+ ymid[i] = ymid[i-1]+yint;
+ xmid[i] = xmid[i-1]+xint;
+ }
+
+ for(i = 0; i <= midpoints; i++)
+ {
+ if(i!=midpoints)
+ {
+ xmid[i+1] += (rand()%variance)-voffset;
+ ymid[i+1] += (rand()%variance)-voffset;
+ }
+ CreateLine(xmid[i], ymid[i], xmid[i+1], ymid[i+1], 0, 0, type, flags);
+ }
+ free(xmid);
+ free(ymid);
+}
+
+void Simulation::clear_sim(void)
+{
+ int i, x, y;
+ emp_decor = 0;
+ signs.clear();
+ memset(bmap, 0, sizeof(bmap));
+ memset(emap, 0, sizeof(emap));
+ memset(parts, 0, sizeof(Particle)*NPART);
+ for (i=0; i<NPART-1; i++)
+ parts[i].life = i+1;
+ parts[NPART-1].life = -1;
+ pfree = 0;
+ parts_lastActiveIndex = 0;
+ memset(pmap, 0, sizeof(pmap));
+ if(fvx)
+ memset(fvx, 0, sizeof(fvx));
+ if(fvy)
+ memset(fvy, 0, sizeof(fvy));
+ memset(photons, 0, sizeof(photons));
+ memset(wireless, 0, sizeof(wireless));
+ memset(gol2, 0, sizeof(gol2));
+ memset(portalp, 0, sizeof(portalp));
+ memset(fighters, 0, sizeof(fighters));
+ std::fill(elementCount, elementCount+PT_NUM, 0);
+ fighcount = 0;
+ player.spwn = 0;
+ player2.spwn = 0;
+ //memset(pers_bg, 0, (XRES+BARSIZE)*YRES*PIXELSIZE);
+ //memset(fire_r, 0, sizeof(fire_r));
+ //memset(fire_g, 0, sizeof(fire_g));
+ //memset(fire_b, 0, sizeof(fire_b));
+ //if(gravmask)
+ //memset(gravmask, 0xFFFFFFFF, (XRES/CELL)*(YRES/CELL)*sizeof(unsigned));
+ if(grav)
+ grav->Clear();
+ if(air)
+ air->Clear();
+ SetEdgeMode(edgeMode);
+}
+void Simulation::init_can_move()
+{
+ // can_move[moving type][type at destination]
+ // 0 = No move/Bounce
+ // 1 = Swap
+ // 2 = Both particles occupy the same space.
+ // 3 = Varies, go run some extra checks
+ int t, rt, stkm_move;
+ for (rt=0;rt<PT_NUM;rt++)
+ can_move[0][rt] = 0; // particles that don't exist shouldn't move...
+ for (t=1;t<PT_NUM;t++)
+ for (rt=0;rt<PT_NUM;rt++)
+ can_move[t][rt] = 1;
+ for (rt=1;rt<PT_NUM;rt++)
+ {
+ can_move[PT_PHOT][rt] = 2;
+ }
+ for (t=1;t<PT_NUM;t++)
+ {
+ for (rt=1;rt<PT_NUM;rt++)
+ {
+ // weight check, also prevents particles of same type displacing each other
+ if (elements[t].Weight <= elements[rt].Weight || rt==PT_GEL) can_move[t][rt] = 0;
+ if (t==PT_NEUT && (elements[rt].Properties&PROP_NEUTPASS))
+ can_move[t][rt] = 2;
+ if (t==PT_NEUT && (elements[rt].Properties&PROP_NEUTABSORB))
+ can_move[t][rt] = 1;
+ if (t==PT_NEUT && (elements[rt].Properties&PROP_NEUTPENETRATE))
+ can_move[t][rt] = 1;
+ if ((elements[t].Properties&PROP_NEUTPENETRATE) && rt==PT_NEUT)
+ can_move[t][rt] = 0;
+ if ((elements[t].Properties&TYPE_ENERGY) && (elements[rt].Properties&TYPE_ENERGY))
+ can_move[t][rt] = 2;
+ }
+ }
+ can_move[PT_DEST][PT_DMND] = 0;
+ can_move[PT_DEST][PT_CLNE] = 0;
+ can_move[PT_DEST][PT_PCLN] = 0;
+ can_move[PT_DEST][PT_BCLN] = 0;
+ can_move[PT_DEST][PT_PBCN] = 0;
+ can_move[PT_BIZR][PT_FILT] = 2;
+ can_move[PT_BIZRG][PT_FILT] = 2;
+ for (t=0;t<PT_NUM;t++)
+ {
+ //spark shouldn't move
+ can_move[PT_SPRK][t] = 0;
+ stkm_move = 0;
+ if (elements[t].Properties & (TYPE_LIQUID | TYPE_GAS))
+ stkm_move = 2;
+ if (!t || t==PT_PRTO || t==PT_SPAWN || t==PT_SPAWN2)
+ stkm_move = 2;
+ can_move[PT_STKM][t] = stkm_move;
+ can_move[PT_STKM2][t] = stkm_move;
+ can_move[PT_FIGH][t] = stkm_move;
+ }
+ for (t=1;t<PT_NUM;t++)
+ {
+ // make them eat things
+ can_move[t][PT_BHOL] = 1;
+ can_move[t][PT_NBHL] = 1;
+ can_move[t][PT_STKM] = 0;
+ can_move[t][PT_STKM2] = 0;
+ can_move[t][PT_FIGH] = 0;
+ //INVIS behaviour varies with pressure
+ can_move[t][PT_INVIS] = 3;
+ //stop CNCT being displaced by other particles
+ can_move[t][PT_CNCT] = 0;
+ //void behaviour varies with powered state and ctype
+ can_move[t][PT_PVOD] = 3;
+ can_move[t][PT_VOID] = 3;
+ can_move[t][PT_EMBR] = 0;
+ can_move[PT_EMBR][t] = 0;
+ if (elements[t].Properties&TYPE_ENERGY)
+ {
+ can_move[t][PT_VIBR] = 1;
+ can_move[t][PT_BVBR] = 1;
+ }
+ }
+ for (t=0;t<PT_NUM;t++)
+ {
+ if (t==PT_GLAS || t==PT_PHOT || t==PT_CLNE || t==PT_PCLN
+ || t==PT_GLOW || t==PT_WATR || t==PT_DSTW || t==PT_SLTW
+ || t==PT_ISOZ || t==PT_ISZS || t==PT_FILT || t==PT_INVIS
+ || t==PT_QRTZ || t==PT_PQRT)
+ can_move[PT_PHOT][t] = 2;
+ }
+ can_move[PT_ELEC][PT_LCRY] = 2;
+ can_move[PT_ELEC][PT_EXOT] = 2;
+ can_move[PT_NEUT][PT_EXOT] = 2;
+ can_move[PT_PHOT][PT_LCRY] = 3;//varies according to LCRY life
+
+ can_move[PT_PHOT][PT_BIZR] = 2;
+ can_move[PT_ELEC][PT_BIZR] = 2;
+ can_move[PT_PHOT][PT_BIZRG] = 2;
+ can_move[PT_ELEC][PT_BIZRG] = 2;
+ can_move[PT_PHOT][PT_BIZRS] = 2;
+ can_move[PT_ELEC][PT_BIZRS] = 2;
+
+ can_move[PT_NEUT][PT_INVIS] = 2;
+ //whol eats anar
+ can_move[PT_ANAR][PT_WHOL] = 1;
+ can_move[PT_ANAR][PT_NWHL] = 1;
+ can_move[PT_ELEC][PT_DEUT] = 1;
+ can_move[PT_THDR][PT_THDR] = 2;
+ can_move[PT_EMBR][PT_EMBR] = 2;
+}
+
+/*
+ RETURN-value explenation
+1 = Swap
+0 = No move/Bounce
+2 = Both particles occupy the same space.
+ */
+int Simulation::eval_move(int pt, int nx, int ny, unsigned *rr)
+{
+ unsigned r;
+ int result;
+
+ if (nx<0 || ny<0 || nx>=XRES || ny>=YRES)
+ return 0;
+
+ r = pmap[ny][nx];
+ if (r)
+ r = (r&~0xFF) | parts[r>>8].type;
+ if (rr)
+ *rr = r;
+ if (pt>=PT_NUM || (r&0xFF)>=PT_NUM)
+ return 0;
+ result = can_move[pt][r&0xFF];
+ if (result==3)
+ {
+ if ((pt==PT_PHOT || pt==PT_ELEC) && (r&0xFF)==PT_LCRY)
+ result = (parts[r>>8].life > 5)? 2 : 0;
+ if ((r&0xFF)==PT_INVIS)
+ {
+ if (pv[ny/CELL][nx/CELL]>4.0f || pv[ny/CELL][nx/CELL]<-4.0f) result = 2;
+ else result = 0;
+ }
+ if ((r&0xFF)==PT_PVOD)
+ {
+ if (parts[r>>8].life == 10)
+ {
+ if(!parts[r>>8].ctype || (parts[r>>8].ctype==pt)!=(parts[r>>8].tmp&1))
+ result = 1;
+ else
+ result = 0;
+ }
+ else result = 0;
+ }
+ if ((r&0xFF)==PT_VOID)
+ {
+ if(!parts[r>>8].ctype || (parts[r>>8].ctype==pt)!=(parts[r>>8].tmp&1))
+ result = 1;
+ else
+ result = 0;
+ }
+ }
+ if (bmap[ny/CELL][nx/CELL])
+ {
+ if (bmap[ny/CELL][nx/CELL]==WL_ALLOWGAS && !(elements[pt].Properties&TYPE_GAS))// && elements[pt].Falldown!=0 && pt!=PT_FIRE && pt!=PT_SMKE)
+ return 0;
+ if (bmap[ny/CELL][nx/CELL]==WL_ALLOWENERGY && !(elements[pt].Properties&TYPE_ENERGY))// && elements[pt].Falldown!=0 && pt!=PT_FIRE && pt!=PT_SMKE)
+ return 0;
+ if (bmap[ny/CELL][nx/CELL]==WL_ALLOWLIQUID && elements[pt].Falldown!=2)
+ return 0;
+ if (bmap[ny/CELL][nx/CELL]==WL_ALLOWSOLID && elements[pt].Falldown!=1)
+ return 0;
+ if (bmap[ny/CELL][nx/CELL]==WL_ALLOWAIR || bmap[ny/CELL][nx/CELL]==WL_WALL || bmap[ny/CELL][nx/CELL]==WL_WALLELEC)
+ return 0;
+ if (bmap[ny/CELL][nx/CELL]==WL_EWALL && !emap[ny/CELL][nx/CELL])
+ return 0;
+ if (bmap[ny/CELL][nx/CELL]==WL_EHOLE && !emap[ny/CELL][nx/CELL] && !(elements[pt].Properties&TYPE_SOLID) && !(elements[r&0xFF].Properties&TYPE_SOLID))
+ return 2;
+ }
+ return result;
+}
+
+int Simulation::try_move(int i, int x, int y, int nx, int ny)
+{
+ unsigned r, e;
+
+ if (x==nx && y==ny)
+ return 1;
+ if (nx<0 || ny<0 || nx>=XRES || ny>=YRES)
+ return 1;
+
+ e = eval_move(parts[i].type, nx, ny, &r);
+
+ /* half-silvered mirror */
+ if (!e && parts[i].type==PT_PHOT &&
+ (((r&0xFF)==PT_BMTL && rand()<RAND_MAX/2) ||
+ (pmap[y][x]&0xFF)==PT_BMTL))
+ e = 2;
+
+ if (!e) //if no movement
+ {
+ if (!(elements[parts[i].type].Properties & TYPE_ENERGY))
+ return 0;
+ if (!legacy_enable && parts[i].type==PT_PHOT && r)//PHOT heat conduction
+ {
+ if ((r & 0xFF) == PT_COAL || (r & 0xFF) == PT_BCOL)
+ parts[r>>8].temp = parts[i].temp;
+
+ if ((r & 0xFF) < PT_NUM && elements[r&0xFF].HeatConduct && ((r&0xFF)!=PT_HSWC||parts[r>>8].life==10) && (r&0xFF)!=PT_FILT)
+ parts[i].temp = parts[r>>8].temp = restrict_flt((parts[r>>8].temp+parts[i].temp)/2, MIN_TEMP, MAX_TEMP);
+ }
+ if ((parts[i].type==PT_NEUT || parts[i].type==PT_ELEC) && ((r&0xFF)==PT_CLNE || (r&0xFF)==PT_PCLN || (r&0xFF)==PT_BCLN || (r&0xFF)==PT_PBCN)) {
+ if (!parts[r>>8].ctype)
+ parts[r>>8].ctype = parts[i].type;
+ }
+ if ((r&0xFF)==PT_PRTI && (elements[parts[i].type].Properties & TYPE_ENERGY))
+ {
+ int nnx, count;
+ for (count=0; count<8; count++)
+ {
+ if (isign(x-nx)==isign(portal_rx[count]) && isign(y-ny)==isign(portal_ry[count]))
+ break;
+ }
+ count = count%8;
+ parts[r>>8].tmp = (int)((parts[r>>8].temp-73.15f)/100+1);
+ if (parts[r>>8].tmp>=CHANNELS) parts[r>>8].tmp = CHANNELS-1;
+ else if (parts[r>>8].tmp<0) parts[r>>8].tmp = 0;
+ for ( nnx=0; nnx<80; nnx++)
+ if (!portalp[parts[r>>8].tmp][count][nnx].type)
+ {
+ portalp[parts[r>>8].tmp][count][nnx] = parts[i];
+ parts[i].type=PT_NONE;
+ break;
+ }
+ }
+ return 0;
+ }
+
+ if (e == 2) //if occupy same space
+ {
+ if (parts[i].type == PT_PHOT && (r&0xFF)==PT_GLOW && !parts[r>>8].life)
+ if (rand() < RAND_MAX/30)
+ {
+ parts[r>>8].life = 120;
+ create_gain_photon(i);
+ }
+ if (parts[i].type == PT_PHOT && (r&0xFF)==PT_FILT)
+ {
+ int temp_bin = (int)((parts[r>>8].temp-273.0f)*0.025f);
+ if (temp_bin < 0) temp_bin = 0;
+ if (temp_bin > 25) temp_bin = 25;
+ if(!parts[r>>8].tmp){
+ parts[i].ctype = 0x1F << temp_bin; //Assign Colour
+ } else if(parts[r>>8].tmp==1){
+ parts[i].ctype &= 0x1F << temp_bin; //Filter Colour
+ } else if(parts[r>>8].tmp==2){
+ parts[i].ctype |= 0x1F << temp_bin; //Add Colour
+ } else if(parts[r>>8].tmp==3){
+ parts[i].ctype &= ~(0x1F << temp_bin); //Subtract Colour
+ }
+ }
+ if (parts[i].type == PT_NEUT && (r&0xFF)==PT_GLAS) {
+ if (rand() < RAND_MAX/10)
+ create_cherenkov_photon(i);
+ }
+ if (parts[i].type == PT_PHOT && (r&0xFF)==PT_INVIS && pv[ny/CELL][nx/CELL]<=4.0f && pv[ny/CELL][nx/CELL]>=-4.0f) {
+ part_change_type(i,x,y,PT_NEUT);
+ parts[i].ctype = 0;
+ }
+ if ((parts[i].type==PT_BIZR||parts[i].type==PT_BIZRG) && (r&0xFF)==PT_FILT)
+ {
+ int temp_bin = (int)((parts[r>>8].temp-273.0f)*0.025f);
+ if (temp_bin < 0) temp_bin = 0;
+ if (temp_bin > 25) temp_bin = 25;
+ parts[i].ctype = 0x1F << temp_bin;
+ }
+ if (((r&0xFF)==PT_BIZR || (r&0xFF)==PT_BIZRG || (r&0xFF)==PT_BIZRS) && parts[i].type==PT_PHOT)
+ {
+ part_change_type(i, x, y, PT_ELEC);
+ parts[i].ctype = 0;
+ }
+ return 1;
+ }
+ //else e=1 , we are trying to swap the particles, return 0 no swap/move, 1 is still overlap/move, because the swap takes place later
+
+ if (parts[i].type == PT_NEUT && (elements[r & 0xFF].Properties & PROP_NEUTABSORB))
+ {
+ kill_part(i);
+ return 0;
+ }
+ if ((r&0xFF)==PT_VOID || (r&0xFF)==PT_PVOD) //this is where void eats particles
+ {
+ //void ctype already checked in eval_move
+ kill_part(i);
+ return 0;
+ }
+ if ((r&0xFF)==PT_BHOL || (r&0xFF)==PT_NBHL) //this is where blackhole eats particles
+ {
+ if (!legacy_enable)
+ {
+ parts[r>>8].temp = restrict_flt(parts[r>>8].temp+parts[i].temp/2, MIN_TEMP, MAX_TEMP);//3.0f;
+ }
+ kill_part(i);
+ return 0;
+ }
+ if (((r&0xFF)==PT_WHOL||(r&0xFF)==PT_NWHL) && parts[i].type==PT_ANAR) //whitehole eats anar
+ {
+ if (!legacy_enable)
+ {
+ parts[r>>8].temp = restrict_flt(parts[r>>8].temp- (MAX_TEMP-parts[i].temp)/2, MIN_TEMP, MAX_TEMP);
+ }
+ kill_part(i);
+ return 0;
+ }
+ if ((r&0xFF)==PT_DEUT && parts[i].type==PT_ELEC)
+ {
+ if(parts[r>>8].life < 6000)
+ parts[r>>8].life += 1;
+ parts[r>>8].temp = 0;
+ kill_part(i);
+ return 0;
+ }
+ if (((r&0xFF)==PT_VIBR || (r&0xFF)==PT_BVBR) && (elements[parts[i].type].Properties & TYPE_ENERGY))
+ {
+ parts[r>>8].tmp += 20;
+ kill_part(i);
+ return 0;
+ }
+
+ if (parts[i].type==PT_CNCT && y<ny && (pmap[y+1][x]&0xFF)==PT_CNCT)//check below CNCT for another CNCT
+ return 0;
+
+ if ((bmap[y/CELL][x/CELL]==WL_EHOLE && !emap[y/CELL][x/CELL]) && !(bmap[ny/CELL][nx/CELL]==WL_EHOLE && !emap[ny/CELL][nx/CELL]))
+ return 0;
+
+ if(parts[i].type==PT_GBMB&&parts[i].life>0)
+ return 0;
+
+ e = r >> 8; //e is now the particle number at r (pmap[ny][nx])
+ if (r)//the swap part, if we make it this far, swap
+ {
+ if (parts[i].type==PT_NEUT) {
+ // target material is NEUTPENETRATE, meaning it gets moved around when neutron passes
+ unsigned s = pmap[y][x];
+ if (s && !(elements[s&0xFF].Properties&PROP_NEUTPENETRATE))
+ return 1; // if the element currently underneath neutron isn't NEUTPENETRATE, don't move anything except the neutron
+ // if nothing is currently underneath neutron, only move target particle
+ if (s)
+ {
+ pmap[ny][nx] = (s&~(0xFF))|parts[s>>8].type;
+ parts[s>>8].x = nx;
+ parts[s>>8].y = ny;
+ }
+ else pmap[ny][nx] = 0;
+ parts[e].x = x;
+ parts[e].y = y;
+ pmap[y][x] = (e<<8)|parts[e].type;
+ return 1;
+ }
+
+ if ((pmap[ny][nx]>>8)==e) pmap[ny][nx] = 0;
+ parts[e].x += x-nx;
+ parts[e].y += y-ny;
+ pmap[(int)(parts[e].y+0.5f)][(int)(parts[e].x+0.5f)] = (e<<8)|parts[e].type;
+ }
+ return 1;
+}
+
+// try to move particle, and if successful update pmap and parts[i].x,y
+int Simulation::do_move(int i, int x, int y, float nxf, float nyf)
+{
+ int nx = (int)(nxf+0.5f), ny = (int)(nyf+0.5f), result;
+ if (parts[i].type == PT_NONE)
+ return 0;
+ result = try_move(i, x, y, nx, ny);
+ if (result)
+ {
+ int t = parts[i].type;
+ parts[i].x = nxf;
+ parts[i].y = nyf;
+ if (ny!=y || nx!=x)
+ {
+ if ((pmap[y][x]>>8)==i) pmap[y][x] = 0;
+ else if ((photons[y][x]>>8)==i) photons[y][x] = 0;
+ if (nx<CELL || nx>=XRES-CELL || ny<CELL || ny>=YRES-CELL)//kill_part if particle is out of bounds
+ {
+ kill_part(i);
+ return -1;
+ }
+ if (elements[t].Properties & TYPE_ENERGY)
+ photons[ny][nx] = t|(i<<8);
+ else if (t)
+ pmap[ny][nx] = t|(i<<8);
+ }
+ }
+ return result;
+}
+
+int Simulation::pn_junction_sprk(int x, int y, int pt)
+{
+ unsigned r = pmap[y][x];
+ if ((r & 0xFF) != pt)
+ return 0;
+ r >>= 8;
+ if (parts[r].type != pt)
+ return 0;
+ if (parts[r].life != 0)
+ return 0;
+
+ parts[r].ctype = pt;
+ part_change_type(r,x,y,PT_SPRK);
+ parts[r].life = 4;
+ return 1;
+}
+
+void Simulation::photoelectric_effect(int nx, int ny)//create sparks from PHOT when hitting PSCN and NSCN
+{
+ unsigned r = pmap[ny][nx];
+
+ if ((r&0xFF) == PT_PSCN) {
+ if ((pmap[ny][nx-1] & 0xFF) == PT_NSCN ||
+ (pmap[ny][nx+1] & 0xFF) == PT_NSCN ||
+ (pmap[ny-1][nx] & 0xFF) == PT_NSCN ||
+ (pmap[ny+1][nx] & 0xFF) == PT_NSCN)
+ pn_junction_sprk(nx, ny, PT_PSCN);
+ }
+}
+
+unsigned Simulation::direction_to_map(float dx, float dy, int t)
+{
+ // TODO:
+ // Adding extra directions causes some inaccuracies.
+ // Not adding them causes problems with some diagonal surfaces (photons absorbed instead of reflected).
+ // For now, don't add them.
+ // Solution may involve more intelligent setting of initial i0 value in find_next_boundary?
+ // or rewriting normal/boundary finding code
+
+ return (dx >= 0) |
+ (((dx + dy) >= 0) << 1) | /* 567 */
+ ((dy >= 0) << 2) | /* 4+0 */
+ (((dy - dx) >= 0) << 3) | /* 321 */
+ ((dx <= 0) << 4) |
+ (((dx + dy) <= 0) << 5) |
+ ((dy <= 0) << 6) |
+ (((dy - dx) <= 0) << 7);
+ /*
+ return (dx >= -0.001) |
+ (((dx + dy) >= -0.001) << 1) | // 567
+ ((dy >= -0.001) << 2) | // 4+0
+ (((dy - dx) >= -0.001) << 3) | // 321
+ ((dx <= 0.001) << 4) |
+ (((dx + dy) <= 0.001) << 5) |
+ ((dy <= 0.001) << 6) |
+ (((dy - dx) <= 0.001) << 7);
+ }*/
+}
+
+int Simulation::is_blocking(int t, int x, int y)
+{
+ if (t & REFRACT) {
+ if (x<0 || y<0 || x>=XRES || y>=YRES)
+ return 0;
+ if ((pmap[y][x] & 0xFF) == PT_GLAS)
+ return 1;
+ return 0;
+ }
+
+ return !eval_move(t, x, y, NULL);
+}
+
+int Simulation::is_boundary(int pt, int x, int y)
+{
+ if (!is_blocking(pt,x,y))
+ return 0;
+ if (is_blocking(pt,x,y-1) && is_blocking(pt,x,y+1) && is_blocking(pt,x-1,y) && is_blocking(pt,x+1,y))
+ return 0;
+ return 1;
+}
+
+int Simulation::find_next_boundary(int pt, int *x, int *y, int dm, int *em)
+{
+ static int dx[8] = {1,1,0,-1,-1,-1,0,1};
+ static int dy[8] = {0,1,1,1,0,-1,-1,-1};
+ static int de[8] = {0x83,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xC1};
+ int i, ii, i0;
+
+ if (*x <= 0 || *x >= XRES-1 || *y <= 0 || *y >= YRES-1)
+ return 0;
+
+ if (*em != -1) {
+ i0 = *em;
+ dm &= de[i0];
+ } else
+ i0 = 0;
+
+ for (ii=0; ii<8; ii++) {
+ i = (ii + i0) & 7;
+ if ((dm & (1 << i)) && is_boundary(pt, *x+dx[i], *y+dy[i])) {
+ *x += dx[i];
+ *y += dy[i];
+ *em = i;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int Simulation::get_normal(int pt, int x, int y, float dx, float dy, float *nx, float *ny)
+{
+ int ldm, rdm, lm, rm;
+ int lx, ly, lv, rx, ry, rv;
+ int i, j;
+ float r, ex, ey;
+
+ if (!dx && !dy)
+ return 0;
+
+ if (!is_boundary(pt, x, y))
+ return 0;
+
+ ldm = direction_to_map(-dy, dx, pt);
+ rdm = direction_to_map(dy, -dx, pt);
+ lx = rx = x;
+ ly = ry = y;
+ lv = rv = 1;
+ lm = rm = -1;
+
+ j = 0;
+ for (i=0; i<SURF_RANGE; i++) {
+ if (lv)
+ lv = find_next_boundary(pt, &lx, &ly, ldm, &lm);
+ if (rv)
+ rv = find_next_boundary(pt, &rx, &ry, rdm, &rm);
+ j += lv + rv;
+ if (!lv && !rv)
+ break;
+ }
+
+ if (j < NORMAL_MIN_EST)
+ return 0;
+
+ if ((lx == rx) && (ly == ry))
+ return 0;
+
+ ex = rx - lx;
+ ey = ry - ly;
+ r = 1.0f/hypot(ex, ey);
+ *nx = ey * r;
+ *ny = -ex * r;
+
+ return 1;
+}
+
+int Simulation::get_normal_interp(int pt, float x0, float y0, float dx, float dy, float *nx, float *ny)
+{
+ int x, y, i;
+
+ dx /= NORMAL_FRAC;
+ dy /= NORMAL_FRAC;
+
+ for (i=0; i<NORMAL_INTERP; i++) {
+ x = (int)(x0 + 0.5f);
+ y = (int)(y0 + 0.5f);
+ if (is_boundary(pt, x, y))
+ break;
+ x0 += dx;
+ y0 += dy;
+ }
+ if (i >= NORMAL_INTERP)
+ return 0;
+
+ if (pt == PT_PHOT)
+ photoelectric_effect(x, y);
+
+ return get_normal(pt, x, y, dx, dy, nx, ny);
+}
+
+//For soap only
+void Simulation::detach(int i)
+{
+ if ((parts[i].ctype&2) == 2)
+ {
+ if ((parts[parts[i].tmp].ctype&4) == 4)
+ parts[parts[i].tmp].ctype ^= 4;
+ }
+
+ if ((parts[i].ctype&4) == 4)
+ {
+ if ((parts[parts[i].tmp2].ctype&2) == 2)
+ parts[parts[i].tmp2].ctype ^= 2;
+ }
+
+ parts[i].ctype = 0;
+}
+
+void Simulation::kill_part(int i)//kills particle number i
+{
+ int x, y;
+
+ // Remove from pmap even if type==0, otherwise infinite recursion occurs when flood fill deleting
+ // a particle which sets type to 0 without calling kill_part (such as LIFE)
+ x = (int)(parts[i].x+0.5f);
+ y = (int)(parts[i].y+0.5f);
+ if (x>=0 && y>=0 && x<XRES && y<YRES) {
+ if ((pmap[y][x]>>8)==i)
+ pmap[y][x] = 0;
+ else if ((photons[y][x]>>8)==i)
+ photons[y][x] = 0;
+ }
+
+ if (parts[i].type == PT_NONE)
+ return;
+
+ if(parts[i].type > 0 && parts[i].type < PT_NUM && elementCount[parts[i].type])
+ elementCount[parts[i].type]--;
+ if (parts[i].type == PT_STKM)
+ {
+ player.spwn = 0;
+ }
+ if (parts[i].type == PT_STKM2)
+ {
+ player2.spwn = 0;
+ }
+ if (parts[i].type == PT_FIGH)
+ {
+ fighters[(unsigned char)parts[i].tmp].spwn = 0;
+ fighcount--;
+ }
+ if (parts[i].type == PT_SOAP)
+ {
+ detach(i);
+ }
+
+ parts[i].type = PT_NONE;
+ parts[i].life = pfree;
+ pfree = i;
+}
+
+void Simulation::part_change_type(int i, int x, int y, int t)//changes the type of particle number i, to t. This also changes pmap at the same time.
+{
+ if (x<0 || y<0 || x>=XRES || y>=YRES || i>=NPART || t<0 || t>=PT_NUM)
+ return;
+ if (!elements[t].Enabled)
+ t = PT_NONE;
+
+ if (parts[i].type == PT_STKM)
+ player.spwn = 0;
+
+ if (parts[i].type == PT_STKM2)
+ player2.spwn = 0;
+
+ if (parts[i].type == PT_FIGH)
+ {
+ fighters[(unsigned char)parts[i].tmp].spwn = 0;
+ fighcount--;
+ }
+
+ parts[i].type = t;
+ if (elements[t].Properties & TYPE_ENERGY)
+ {
+ photons[y][x] = t|(i<<8);
+ if ((pmap[y][x]>>8)==i)
+ pmap[y][x] = 0;
+ }
+ else
+ {
+ pmap[y][x] = t|(i<<8);
+ if ((photons[y][x]>>8)==i)
+ photons[y][x] = 0;
+ }
+}
+
+//the function for creating a particle, use p=-1 for creating a new particle, -2 is from a brush, or a particle number to replace a particle.
+//tv = Type (8 bits) + Var (24 bits), var is usually 0
+int Simulation::create_part(int p, int x, int y, int tv)
+{
+ int i;
+
+ int t = tv & 0xFF;
+ int v = (tv >> 8) & 0xFFFFFF;
+
+ if (x<0 || y<0 || x>=XRES || y>=YRES || ((t<=0 || t>=PT_NUM)&&t!=SPC_HEAT&&t!=SPC_COOL&&t!=SPC_AIR&&t!=SPC_VACUUM&&t!=SPC_PGRV&&t!=SPC_NGRV))
+ return -1;
+ if (t>=0 && t<PT_NUM && !elements[t].Enabled)
+ return -1;
+ if(t==SPC_PROP) {
+ return -1; //Prop tool works on a mouse click basic, make sure it doesn't do anything here
+ }
+
+ /*if (t==SPC_HEAT||t==SPC_COOL)
+ {
+ if ((pmap[y][x]&0xFF)!=PT_NONE&&(pmap[y][x]&0xFF)<PT_NUM)
+ {
+ if (t==SPC_HEAT&&parts[pmap[y][x]>>8].temp<MAX_TEMP)
+ {
+ if ((pmap[y][x]&0xFF)==PT_PUMP || (pmap[y][x]&0xFF)==PT_GPMP) {
+ parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp + 0.1f, MIN_TEMP, MAX_TEMP);
+ } else if ((sdl_mod & (KMOD_SHIFT)) && (sdl_mod & (KMOD_CTRL))) {
+ parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp + 50.0f, MIN_TEMP, MAX_TEMP);
+ } else {
+ parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp + 4.0f, MIN_TEMP, MAX_TEMP);
+ }
+ }
+ if (t==SPC_COOL&&parts[pmap[y][x]>>8].temp>MIN_TEMP)
+ {
+ if ((pmap[y][x]&0xFF)==PT_PUMP || (pmap[y][x]&0xFF)==PT_GPMP) {
+ parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp - 0.1f, MIN_TEMP, MAX_TEMP);
+ } else if ((sdl_mod & (KMOD_SHIFT)) && (sdl_mod & (KMOD_CTRL))) {
+ parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp - 50.0f, MIN_TEMP, MAX_TEMP);
+ } else {
+ parts[pmap[y][x]>>8].temp = restrict_flt(parts[pmap[y][x]>>8].temp - 4.0f, MIN_TEMP, MAX_TEMP);
+ }
+ }
+ return pmap[y][x]>>8;
+ }
+ else
+ {
+ return -1;
+ }
+ }*/
+ if (t==SPC_AIR)
+ {
+ pv[y/CELL][x/CELL] += 0.03f;
+ if (y+CELL<YRES)
+ pv[y/CELL+1][x/CELL] += 0.03f;
+ if (x+CELL<XRES)
+ {
+ pv[y/CELL][x/CELL+1] += 0.03f;
+ if (y+CELL<YRES)
+ pv[y/CELL+1][x/CELL+1] += 0.03f;
+ }
+ return -1;
+ }
+ if (t==SPC_VACUUM)
+ {
+ pv[y/CELL][x/CELL] -= 0.03f;
+ if (y+CELL<YRES)
+ pv[y/CELL+1][x/CELL] -= 0.03f;
+ if (x+CELL<XRES)
+ {
+ pv[y/CELL][x/CELL+1] -= 0.03f;
+ if (y+CELL<YRES)
+ pv[y/CELL+1][x/CELL+1] -= 0.03f;
+ }
+ return -1;
+ }
+ if (t==SPC_PGRV)
+ {
+ gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] = 5;
+ return -1;
+ }
+ if (t==SPC_NGRV)
+ {
+ gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] = -5;
+ return -1;
+ }
+
+
+ if (t==PT_SPRK)
+ {
+ int type = pmap[y][x]&0xFF;
+ int index = pmap[y][x]>>8;
+ if(type == PT_WIRE)
+ {
+ parts[index].ctype = PT_DUST;
+ }
+ if (!(type == PT_INST || (elements[type].Properties&PROP_CONDUCTS)))
+ return -1;
+ if (parts[index].life!=0)
+ return -1;
+ if (p == -2 && type == PT_INST)
+ {
+ FloodINST(x, y, PT_SPRK, PT_INST);
+ return index;
+ }
+ parts[index].type = PT_SPRK;
+ parts[index].life = 4;
+ parts[index].ctype = type;
+ pmap[y][x] = (pmap[y][x]&~0xFF) | PT_SPRK;
+ if (parts[index].temp+10.0f < 673.0f && !legacy_enable && (type==PT_METL || type == PT_BMTL || type == PT_BRMT || type == PT_PSCN || type == PT_NSCN || type == PT_ETRD || type == PT_NBLE || type == PT_IRON))
+ parts[index].temp = parts[index].temp+10.0f;
+ return index;
+ }
+ if (t==PT_SPAWN&&elementCount[PT_SPAWN])
+ return -1;
+ if (t==PT_SPAWN2&&elementCount[PT_SPAWN2])
+ return -1;
+ if (p==-1)//creating from anything but brush
+ {
+ // If there is a particle, only allow creation if the new particle can occupy the same space as the existing particle
+ // If there isn't a particle but there is a wall, check whether the new particle is allowed to be in it
+ // (not "!=2" for wall check because eval_move returns 1 for moving into empty space)
+ // If there's no particle and no wall, assume creation is allowed
+ if (pmap[y][x] ? (eval_move(t, x, y, NULL)!=2) : (bmap[y/CELL][x/CELL] && eval_move(t, x, y, NULL)==0))
+ {
+ if ((pmap[y][x]&0xFF)!=PT_SPAWN&&(pmap[y][x]&0xFF)!=PT_SPAWN2)
+ {
+ if (t!=PT_STKM&&t!=PT_STKM2&&t!=PT_FIGH)
+ {
+ return -1;
+ }
+ }
+ }
+ if (pfree == -1)
+ return -1;
+ i = pfree;
+ pfree = parts[i].life;
+ }
+ else if (p==-2)//creating from brush
+ {
+ if (pmap[y][x])
+ {
+ if ((
+ ((pmap[y][x]&0xFF)==PT_STOR&&!(elements[t].Properties&TYPE_SOLID))||
+ (pmap[y][x]&0xFF)==PT_CLNE||
+ (pmap[y][x]&0xFF)==PT_BCLN||
+ (pmap[y][x]&0xFF)==PT_CONV||
+ ((pmap[y][x]&0xFF)==PT_PCLN&&t!=PT_PSCN&&t!=PT_NSCN)||
+ ((pmap[y][x]&0xFF)==PT_PBCN&&t!=PT_PSCN&&t!=PT_NSCN)
+ )&&(
+ t!=PT_CLNE&&t!=PT_PCLN&&
+ t!=PT_BCLN&&t!=PT_STKM&&
+ t!=PT_STKM2&&t!=PT_PBCN&&
+ t!=PT_STOR&&t!=PT_FIGH)
+ )
+ {
+ parts[pmap[y][x]>>8].ctype = t;
+ if (t==PT_LIFE && v<NGOLALT && (pmap[y][x]&0xFF)!=PT_STOR) parts[pmap[y][x]>>8].tmp = v;
+ }
+ else if ((pmap[y][x]&0xFF) == PT_DTEC && (pmap[y][x]&0xFF) != t)
+ {
+ parts[pmap[y][x]>>8].ctype = t;
+ if (t==PT_LIFE && v<NGOLALT)
+ parts[pmap[y][x]>>8].tmp = v;
+ }
+ return -1;
+ }
+ if (photons[y][x] && (elements[t].Properties & TYPE_ENERGY))
+ return -1;
+ if (pfree == -1)
+ return -1;
+ i = pfree;
+ pfree = parts[i].life;
+ }
+ else if (p==-3)//skip pmap checks, e.g. for sing explosion
+ {
+ if (pfree == -1)
+ return -1;
+ i = pfree;
+ pfree = parts[i].life;
+ }
+ else
+ {
+ int oldX = (int)(parts[p].x+0.5f);
+ int oldY = (int)(parts[p].y+0.5f);
+ if ((pmap[oldY][oldX]>>8)==p)
+ pmap[oldY][oldX] = 0;
+ if ((photons[oldY][oldX]>>8)==p)
+ photons[oldY][oldX] = 0;
+ i = p;
+ }
+
+ if (i>parts_lastActiveIndex) parts_lastActiveIndex = i;
+
+ parts[i].dcolour = 0;
+ parts[i].flags = 0;
+ if (t==PT_GLAS)
+ {
+ parts[i].pavg[1] = pv[y/CELL][x/CELL];
+ }
+ else if (t==PT_QRTZ)
+ {
+ parts[i].pavg[1] = pv[y/CELL][x/CELL];
+ }
+ else
+ {
+ parts[i].pavg[0] = 0.0f;
+ parts[i].pavg[1] = 0.0f;
+ }
+ if (t!=PT_STKM&&t!=PT_STKM2&&t!=PT_FIGH)//set everything to default values first, except for stickman.
+ {
+ parts[i].x = (float)x;
+ parts[i].y = (float)y;
+ parts[i].type = t;
+ parts[i].vx = 0;
+ parts[i].vy = 0;
+ parts[i].life = 0;
+ parts[i].ctype = 0;
+ parts[i].temp = elements[t].Temperature;
+ parts[i].tmp = 0;
+ parts[i].tmp2 = 0;
+ }
+ switch (t)
+ {
+ case PT_SOAP:
+ parts[i].tmp = -1;
+ parts[i].tmp2 = -1;
+ break;
+ case PT_ACID: case PT_CAUS:
+ parts[i].life = 75;
+ break;
+ /*Testing
+ case PT_WOOD:
+ parts[i].life = 150;
+ break;
+ End Testing*/
+ case PT_WARP:
+ parts[i].life = rand()%95+70;
+ break;
+ case PT_FUSE:
+ parts[i].life = 50;
+ parts[i].tmp = 50;
+ break;
+ case PT_LIFE:
+ if (v<NGOLALT)
+ {
+ parts[i].tmp = grule[v+1][9] - 1;
+ parts[i].ctype = v;
+ }
+ break;
+ case PT_DEUT:
+ parts[i].life = 10;
+ break;
+ case PT_MERC:
+ parts[i].tmp = 10;
+ break;
+ case PT_BRAY:
+ parts[i].life = 30;
+ break;
+ case PT_GPMP: case PT_PUMP:
+ parts[i].life = 10;
+ break;
+ case PT_SING:
+ parts[i].life = rand()%50+60;
+ break;
+ case PT_QRTZ:
+ parts[i].tmp = (rand()%11);
+ break;
+ case PT_PQRT:
+ parts[i].tmp = (rand()%11);
+ break;
+ case PT_CLST:
+ parts[i].tmp = (rand()%7);
+ break;
+ case PT_FSEP:
+ parts[i].life = 50;
+ break;
+ case PT_COAL:
+ parts[i].life = 110;
+ parts[i].tmp = 50;
+ break;
+ case PT_IGNT:
+ parts[i].life = 3;
+ break;
+ case PT_FRZW:
+ parts[i].life = 100;
+ break;
+ case PT_PPIP:
+ case PT_PIPE:
+ parts[i].life = 60;
+ break;
+ case PT_BCOL:
+ parts[i].life = 110;
+ break;
+ case PT_FIRE:
+ parts[i].life = rand()%50+120;
+ break;
+ case PT_PLSM:
+ parts[i].life = rand()%150+50;
+ break;
+ case PT_HFLM:
+ parts[i].life = rand()%150+50;
+ break;
+ case PT_LAVA:
+ parts[i].life = rand()%120+240;
+ break;
+ case PT_NBLE:
+ parts[i].life = 0;
+ break;
+ case PT_ICEI:
+ parts[i].ctype = PT_WATR;
+ break;
+ case PT_MORT:
+ parts[i].vx = 2;
+ break;
+ case PT_EXOT:
+ parts[i].life = 1000;
+ parts[i].tmp = 244;
+ break;
+ case PT_EMBR:
+ parts[i].life = 50;
+ break;
+ case PT_TESC:
+ parts[i].tmp = v;
+ if (parts[i].tmp > 300)
+ parts[i].tmp=300;
+ break;
+ case PT_STKM:
+ if (player.spwn==0)
+ {
+ parts[i].x = (float)x;
+ parts[i].y = (float)y;
+ parts[i].type = PT_STKM;
+ parts[i].vx = 0;
+ parts[i].vy = 0;
+ parts[i].life = 100;
+ parts[i].ctype = 0;
+ parts[i].temp = elements[t].Temperature;
+ Element_STKM::STKM_init_legs(this, &player, i);
+ player.spwn = 1;
+ player.elem = PT_DUST;
+ }
+ else
+ {
+ return -1;
+ }
+ create_part(-3,x,y,PT_SPAWN);
+ elementCount[PT_SPAWN] = 1;
+ break;
+ case PT_STKM2:
+ if (player2.spwn==0)
+ {
+ parts[i].x = (float)x;
+ parts[i].y = (float)y;
+ parts[i].type = PT_STKM2;
+ parts[i].vx = 0;
+ parts[i].vy = 0;
+ parts[i].life = 100;
+ parts[i].ctype = 0;
+ parts[i].temp = elements[t].Temperature;
+ Element_STKM::STKM_init_legs(this, &player2, i);
+ player2.spwn = 1;
+ player2.elem = PT_DUST;
+ }
+ else
+ {
+ return -1;
+ }
+ create_part(-3,x,y,PT_SPAWN2);
+ elementCount[PT_SPAWN2] = 1;
+ break;
+ case PT_BIZR: case PT_BIZRG: case PT_BIZRS:
+ parts[i].ctype = 0x47FFFF;
+ break;
+ case PT_DTEC:
+ parts[i].tmp2 = 2;
+ case PT_TSNS:
+ parts[i].tmp2 = 2;
+ break;
+ default:
+ if (t==PT_FIGH)
+ {
+ unsigned char fcount = 0;
+ while (fcount < 100 && fcount < (fighcount+1) && fighters[fcount].spwn==1) fcount++;
+ if (fcount < 100 && fighters[fcount].spwn==0)
+ {
+ parts[i].x = (float)x;
+ parts[i].y = (float)y;
+ parts[i].type = PT_FIGH;
+ parts[i].vx = 0;
+ parts[i].vy = 0;
+ parts[i].life = 100;
+ parts[i].ctype = 0;
+ parts[i].tmp = fcount;
+ parts[i].temp = elements[t].Temperature;
+ Element_STKM::STKM_init_legs(this, &fighters[fcount], i);
+ fighters[fcount].spwn = 1;
+ fighters[fcount].elem = PT_DUST;
+ fighcount++;
+
+ return i;
+ }
+ return -1;
+ }
+ if (t==PT_PHOT)
+ {
+ float a = (rand()%8) * 0.78540f;
+ parts[i].life = 680;
+ parts[i].ctype = 0x3FFFFFFF;
+ parts[i].vx = 3.0f*cosf(a);
+ parts[i].vy = 3.0f*sinf(a);
+ }
+ if (t==PT_ELEC)
+ {
+ float a = (rand()%360)*3.14159f/180.0f;
+ parts[i].life = 680;
+ parts[i].vx = 2.0f*cosf(a);
+ parts[i].vy = 2.0f*sinf(a);
+ }
+ if (t==PT_NEUT)
+ {
+ float r = (rand()%128+128)/127.0f;
+ float a = (rand()%360)*3.14159f/180.0f;
+ parts[i].life = rand()%480+480;
+ parts[i].vx = r*cosf(a);
+ parts[i].vy = r*sinf(a);
+ }
+ if (t==PT_TRON)
+ {
+ int randhue = rand()%360;
+ int randomdir = rand()%4;
+ parts[i].tmp = 1|(randomdir<<5)|(randhue<<7);//set as a head and a direction
+ parts[i].tmp2 = 4;//tail
+ parts[i].life = 5;
+ }
+ if (t==PT_LIGH)
+ {
+ float gx, gy, gsize;
+ if (p!=-2)
+ {
+ parts[i].life=30;
+ parts[i].temp=parts[i].life*150.0f; // temperature of the lighting shows the power of the lighting
+ }
+ GetGravityField(x, y, 1.0f, 1.0f, gx, gy);
+ gsize = gx*gx+gy*gy;
+ if (gsize<0.0016f)
+ {
+ float angle = (rand()%6284)*0.001f;//(in radians, between 0 and 2*pi)
+ gsize = sqrtf(gsize);
+ // randomness in weak gravity fields (more randomness with weaker fields)
+ gx += cosf(angle)*(0.04f-gsize);
+ gy += sinf(angle)*(0.04f-gsize);
+ }
+ parts[i].tmp = (((int)(atan2f(-gy, gx)*(180.0f/M_PI)))+rand()%40-20+360)%360;
+ parts[i].tmp2 = 4;
+ }
+ break;
+ }
+ //and finally set the pmap/photon maps to the newly created particle
+ if (elements[t].Properties & TYPE_ENERGY)
+ photons[y][x] = t|(i<<8);
+ else if (t!=PT_STKM && t!=PT_STKM2 && t!=PT_FIGH)
+ pmap[y][x] = t|(i<<8);
+
+ //Fancy dust effects for powder types
+ if((elements[t].Properties & TYPE_PART) && pretty_powder)
+ {
+ int colr, colg, colb, randa;
+ randa = (rand()%30)-15;
+ colr = (PIXR(elements[t].Colour)+sandcolour+(rand()%20)-10+randa);
+ colg = (PIXG(elements[t].Colour)+sandcolour+(rand()%20)-10+randa);
+ colb = (PIXB(elements[t].Colour)+sandcolour+(rand()%20)-10+randa);
+ colr = colr>255 ? 255 : (colr<0 ? 0 : colr);
+ colg = colg>255 ? 255 : (colg<0 ? 0 : colg);
+ colb = colb>255 ? 255 : (colb<0 ? 0 : colb);
+ parts[i].dcolour = 0xFF000000 | (colr<<16) | (colg<<8) | colb;
+ }
+ elementCount[t]++;
+ return i;
+}
+
+void Simulation::GetGravityField(int x, int y, float particleGrav, float newtonGrav, float & pGravX, float & pGravY)
+{
+ pGravX = newtonGrav*gravx[(y/CELL)*(XRES/CELL)+(x/CELL)];
+ pGravY = newtonGrav*gravy[(y/CELL)*(XRES/CELL)+(x/CELL)];
+ switch (gravityMode)
+ {
+ default:
+ case 0: //normal, vertical gravity
+ pGravY += particleGrav;
+ break;
+ case 1: //no gravity
+ break;
+ case 2: //radial gravity
+ if (x-XCNTR != 0 || y-YCNTR != 0)
+ {
+ float pGravMult = particleGrav/sqrtf((x-XCNTR)*(x-XCNTR) + (y-YCNTR)*(y-YCNTR));
+ pGravX -= pGravMult * (float)(x - XCNTR);
+ pGravY -= pGravMult * (float)(y - YCNTR);
+ }
+ }
+}
+
+void Simulation::create_gain_photon(int pp)//photons from PHOT going through GLOW
+{
+ float xx, yy;
+ int i, lr, temp_bin, nx, ny;
+
+ if (pfree == -1)
+ return;
+ i = pfree;
+
+ lr = rand() % 2;
+
+ if (lr) {
+ xx = parts[pp].x - 0.3*parts[pp].vy;
+ yy = parts[pp].y + 0.3*parts[pp].vx;
+ } else {
+ xx = parts[pp].x + 0.3*parts[pp].vy;
+ yy = parts[pp].y - 0.3*parts[pp].vx;
+ }
+
+ nx = (int)(xx + 0.5f);
+ ny = (int)(yy + 0.5f);
+
+ if (nx<0 || ny<0 || nx>=XRES || ny>=YRES)
+ return;
+
+ if ((pmap[ny][nx] & 0xFF) != PT_GLOW)
+ return;
+
+ pfree = parts[i].life;
+ if (i>parts_lastActiveIndex) parts_lastActiveIndex = i;
+
+ parts[i].type = PT_PHOT;
+ parts[i].life = 680;
+ parts[i].x = xx;
+ parts[i].y = yy;
+ parts[i].vx = parts[pp].vx;
+ parts[i].vy = parts[pp].vy;
+ parts[i].temp = parts[pmap[ny][nx] >> 8].temp;
+ parts[i].tmp = 0;
+ parts[i].pavg[0] = parts[i].pavg[1] = 0.0f;
+ photons[ny][nx] = PT_PHOT|(i<<8);
+
+ temp_bin = (int)((parts[i].temp-273.0f)*0.25f);
+ if (temp_bin < 0) temp_bin = 0;
+ if (temp_bin > 25) temp_bin = 25;
+ parts[i].ctype = 0x1F << temp_bin;
+}
+
+void Simulation::create_cherenkov_photon(int pp)//photons from NEUT going through GLAS
+{
+ int i, lr, nx, ny;
+ float r, eff_ior;
+
+ if (pfree == -1)
+ return;
+ i = pfree;
+
+ nx = (int)(parts[pp].x + 0.5f);
+ ny = (int)(parts[pp].y + 0.5f);
+ if ((pmap[ny][nx] & 0xFF) != PT_GLAS)
+ return;
+
+ if (hypotf(parts[pp].vx, parts[pp].vy) < 1.44f)
+ return;
+
+ pfree = parts[i].life;
+ if (i>parts_lastActiveIndex) parts_lastActiveIndex = i;
+
+ lr = rand() % 2;
+
+ parts[i].type = PT_PHOT;
+ parts[i].ctype = 0x00000F80;
+ parts[i].life = 680;
+ parts[i].x = parts[pp].x;
+ parts[i].y = parts[pp].y;
+ parts[i].temp = parts[pmap[ny][nx] >> 8].temp;
+ parts[i].tmp = 0;
+ parts[i].pavg[0] = parts[i].pavg[1] = 0.0f;
+ photons[ny][nx] = PT_PHOT|(i<<8);
+
+ if (lr) {
+ parts[i].vx = parts[pp].vx - 2.5f*parts[pp].vy;
+ parts[i].vy = parts[pp].vy + 2.5f*parts[pp].vx;
+ } else {
+ parts[i].vx = parts[pp].vx + 2.5f*parts[pp].vy;
+ parts[i].vy = parts[pp].vy - 2.5f*parts[pp].vx;
+ }
+
+ /* photons have speed of light. no discussion. */
+ r = 1.269 / hypotf(parts[i].vx, parts[i].vy);
+ parts[i].vx *= r;
+ parts[i].vy *= r;
+}
+
+void Simulation::delete_part(int x, int y, int flags)//calls kill_part with the particle located at x,y
+{
+ unsigned i;
+
+ if (x<0 || y<0 || x>=XRES || y>=YRES)
+ return;
+ if (photons[y][x]) {
+ i = photons[y][x];
+ } else {
+ i = pmap[y][x];
+ }
+
+ if (!i)
+ return;
+ kill_part(i>>8);
+}
+
+void Simulation::update_particles_i(int start, int inc)
+{
+ int i, j, x, y, t, nx, ny, r, surround_space, s, lt, rt, nt, nnx, nny, q, golnum, goldelete, z, neighbors, createdsomething;
+ float mv, dx, dy, ix, iy, lx, ly, nrx, nry, dp, ctemph, ctempl, gravtot;
+ int fin_x, fin_y, clear_x, clear_y, stagnant;
+ float fin_xf, fin_yf, clear_xf, clear_yf;
+ float nn, ct1, ct2, swappage;
+ float pt = R_TEMP;
+ float c_heat = 0.0f;
+ int h_count = 0;
+ int starti = (start*-1);
+ int surround[8];
+ int surround_hconduct[8];
+ int lighting_ok=1;
+ unsigned int elem_properties;
+ float pGravX, pGravY, pGravD;
+ int excessive_stacking_found = 0;
+
+ currentTick++;
+
+ if (lighting_recreate>0)
+ {
+ for (i=0; i<=parts_lastActiveIndex; i++)
+ {
+ if (parts[i].type==PT_LIGH && parts[i].tmp2>0)
+ {
+ lighting_ok=0;
+ break;
+ }
+ }
+ }
+
+ if (lighting_ok)
+ lighting_recreate--;
+
+ if (lighting_recreate<0)
+ lighting_recreate=1;
+
+ if (lighting_recreate>21)
+ lighting_recreate=21;
+
+ //if (sys_pause&&!framerender)//do nothing if paused
+ // return;
+
+ if (force_stacking_check || (rand()%10)==0)
+ {
+ force_stacking_check = 0;
+ excessive_stacking_found = 0;
+ for (y=0; y<YRES; y++)
+ {
+ for (x=0; x<XRES; x++)
+ {
+ // Use a threshold, since some particle stacking can be normal (e.g. BIZR + FILT)
+ // Setting pmap_count[y][x] > NPART means BHOL will form in that spot
+ if (pmap_count[y][x]>5)
+ {
+ if (bmap[y/CELL][x/CELL]==WL_EHOLE)
+ {
+ // Allow more stacking in E-hole
+ if (pmap_count[y][x]>1500)
+ {
+ pmap_count[y][x] = pmap_count[y][x] + NPART;
+ excessive_stacking_found = 1;
+ }
+ }
+ else if (pmap_count[y][x]>1500 || (rand()%1600)<=(pmap_count[y][x]+100))
+ {
+ pmap_count[y][x] = pmap_count[y][x] + NPART;
+ excessive_stacking_found = 1;
+ }
+ }
+ }
+ }
+ if (excessive_stacking_found)
+ {
+ for (i=0; i<=parts_lastActiveIndex; i++)
+ {
+ if (parts[i].type)
+ {
+ t = parts[i].type;
+ x = (int)(parts[i].x+0.5f);
+ y = (int)(parts[i].y+0.5f);
+ if (x>=0 && y>=0 && x<XRES && y<YRES && !(elements[t].Properties&TYPE_ENERGY))
+ {
+ if (pmap_count[y][x]>=NPART)
+ {
+ if (pmap_count[y][x]>NPART)
+ {
+ create_part(i, x, y, PT_NBHL);
+ parts[i].temp = MAX_TEMP;
+ parts[i].tmp = pmap_count[y][x]-NPART;//strength of grav field
+ if (parts[i].tmp>51200) parts[i].tmp = 51200;
+ pmap_count[y][x] = NPART;
+ }
+ else
+ {
+ kill_part(i);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (ISLOVE || ISLOLZ) //LOVE and LOLZ element handling
+ {
+ int nx, nnx, ny, nny, r, rt;
+ ISLOVE = 0;
+ ISLOLZ = 0;
+ for (ny=0; ny<YRES-4; ny++)
+ {
+ for (nx=0; nx<XRES-4; nx++)
+ {
+ r=pmap[ny][nx];
+ if (!r)
+ {
+ continue;
+ }
+ else if ((ny<9||nx<9||ny>YRES-7||nx>XRES-10)&&(parts[r>>8].type==PT_LOVE||parts[r>>8].type==PT_LOLZ))
+ kill_part(r>>8);
+ else if (parts[r>>8].type==PT_LOVE)
+ {
+ love[nx/9][ny/9] = 1;
+ }
+ else if (parts[r>>8].type==PT_LOLZ)
+ {
+ lolz[nx/9][ny/9] = 1;
+ }
+ }
+ }
+ for (nx=9; nx<=XRES-18; nx++)
+ {
+ for (ny=9; ny<=YRES-7; ny++)
+ {
+ if (love[nx/9][ny/9]==1)
+ {
+ for ( nnx=0; nnx<9; nnx++)
+ for ( nny=0; nny<9; nny++)
+ {
+ if (ny+nny>0&&ny+nny<YRES&&nx+nnx>=0&&nx+nnx<XRES)
+ {
+ rt=pmap[ny+nny][nx+nnx];
+ if (!rt&&Element_LOVE::RuleTable[nnx][nny]==1)
+ create_part(-1,nx+nnx,ny+nny,PT_LOVE);
+ else if (!rt)
+ continue;
+ else if (parts[rt>>8].type==PT_LOVE&&Element_LOVE::RuleTable[nnx][nny]==0)
+ kill_part(rt>>8);
+ }
+ }
+ }
+ love[nx/9][ny/9]=0;
+ if (lolz[nx/9][ny/9]==1)
+ {
+ for ( nnx=0; nnx<9; nnx++)
+ for ( nny=0; nny<9; nny++)
+ {
+ if (ny+nny>0&&ny+nny<YRES&&nx+nnx>=0&&nx+nnx<XRES)
+ {
+ rt=pmap[ny+nny][nx+nnx];
+ if (!rt&&Element_LOLZ::RuleTable[nny][nnx]==1)
+ create_part(-1,nx+nnx,ny+nny,PT_LOLZ);
+ else if (!rt)
+ continue;
+ else if (parts[rt>>8].type==PT_LOLZ&&Element_LOLZ::RuleTable[nny][nnx]==0)
+ kill_part(rt>>8);
+
+ }
+ }
+ }
+ lolz[nx/9][ny/9]=0;
+ }
+ }
+ }
+
+ //wire!
+ if(elementCount[PT_WIRE] > 0)
+ {
+ for (nx=0; nx<XRES; nx++)
+ {
+ for (ny=0; ny<YRES; ny++)
+ {
+ r = pmap[ny][nx];
+ if (!r)
+ continue;
+ if(parts[r>>8].type==PT_WIRE)
+ parts[r>>8].tmp=parts[r>>8].ctype;
+ }
+ }
+ }
+
+ if (Element_PPIP::ppip_changed)
+ {
+ for (i=0; i<=parts_lastActiveIndex; i++)
+ {
+ if (parts[i].type==PT_PPIP)
+ {
+ parts[i].tmp |= (parts[i].tmp&0xE0000000)>>3;
+ parts[i].tmp &= ~0xE0000000;
+ }
+ }
+ Element_PPIP::ppip_changed = 0;
+ }
+
+ //game of life!
+ if (elementCount[PT_LIFE]>0&&++CGOL>=GSPEED)//GSPEED is frames per generation
+ {
+ int createdsomething = 0;
+ CGOL=0;
+ ISGOL=0;
+ for (ny=CELL; ny<YRES-CELL; ny++)
+ {//go through every particle and set neighbor map
+ for (nx=CELL; nx<XRES-CELL; nx++)
+ {
+ r = pmap[ny][nx];
+ if (!r)
+ {
+ gol[ny][nx] = 0;
+ continue;
+ }
+ else
+ {
+ //for ( golnum=1; golnum<=NGOL; golnum++) //This shouldn't be necessary any more.
+ //{
+ if (parts[r>>8].type==PT_LIFE/* && parts[r>>8].ctype==golnum-1*/)
+ {
+ golnum = parts[r>>8].ctype+1;
+ if (golnum<=0 || golnum>NGOLALT) {
+ parts[r>>8].type = PT_NONE;
+ continue;
+ }
+ if (parts[r>>8].tmp == grule[golnum][9]-1) {
+ gol[ny][nx] = golnum;
+ for ( nnx=-1; nnx<2; nnx++)
+ {
+ for ( nny=-1; nny<2; nny++)//it will count itself as its own neighbor, which is needed, but will have 1 extra for delete check
+ {
+ rt = pmap[((ny+nny+YRES-3*CELL)%(YRES-2*CELL))+CELL][((nx+nnx+XRES-3*CELL)%(XRES-2*CELL))+CELL];
+ if (!rt || (rt&0xFF)==PT_LIFE)
+ {
+ gol2[((ny+nny+YRES-3*CELL)%(YRES-2*CELL))+CELL][((nx+nnx+XRES-3*CELL)%(XRES-2*CELL))+CELL][golnum] ++;
+ gol2[((ny+nny+YRES-3*CELL)%(YRES-2*CELL))+CELL][((nx+nnx+XRES-3*CELL)%(XRES-2*CELL))+CELL][0] ++;
+ }
+ }
+ }
+ } else {
+ parts[r>>8].tmp --;
+ if (parts[r>>8].tmp<=0)
+ parts[r>>8].type = PT_NONE;//using kill_part makes it not work
+ }
+ }
+ //}
+ }
+ }
+ }
+ for (ny=CELL; ny<YRES-CELL; ny++)
+ { //go through every particle again, but check neighbor map, then update particles
+ for (nx=CELL; nx<XRES-CELL; nx++)
+ {
+ r = pmap[ny][nx];
+ neighbors = gol2[ny][nx][0];
+ if (neighbors==0 || !((r&0xFF)==PT_LIFE || !(r&0xFF)))
+ continue;
+ for ( golnum = 1; golnum<=NGOL; golnum++)
+ {
+ goldelete = neighbors;
+ if (gol[ny][nx]==0&&grule[golnum][goldelete]>=2&&gol2[ny][nx][golnum]>=(goldelete%2)+goldelete/2)
+ {
+ if (create_part(-1, nx, ny, PT_LIFE|((golnum-1)<<8)))
+ createdsomething = 1;
+ }
+ else if (gol[ny][nx]==golnum&&(grule[golnum][goldelete-1]==0||grule[golnum][goldelete-1]==2))//subtract 1 because it counted itself
+ {
+ if (parts[r>>8].tmp==grule[golnum][9]-1)
+ parts[r>>8].tmp --;
+ }
+ if (r && parts[r>>8].tmp<=0)
+ parts[r>>8].type = PT_NONE;//using kill_part makes it not work
+ }
+ for ( z = 0; z<=NGOL; z++)
+ gol2[ny][nx][z] = 0;//this improves performance A LOT compared to the memset, i was getting ~23 more fps with this.
+ }
+ }
+ //memset(gol2, 0, sizeof(gol2));
+ }
+ if (ISWIRE>0)//wifi channel reseting
+ {
+ for ( q = 0; q<(int)(MAX_TEMP-73.15f)/100+2; q++)
+ {
+ wireless[q][0] = wireless[q][1];
+ wireless[q][1] = 0;
+ }
+ ISWIRE--;
+ }
+
+ bool elementRecount = !(currentTick%180);
+ if(elementRecount)
+ {
+ std::fill(elementCount, elementCount+PT_NUM, 0);
+ }
+
+ for (i=0; i<=parts_lastActiveIndex; i++)
+ if (parts[i].type)
+ {
+ t = parts[i].type;
+ if (t<0 || t>=PT_NUM || !elements[t].Enabled)
+ {
+ kill_part(i);
+ continue;
+ }
+
+
+ elementCount[t]++;
+
+ elem_properties = elements[t].Properties;
+ if (parts[i].life>0 && (elem_properties&PROP_LIFE_DEC))
+ {
+ // automatically decrease life
+ parts[i].life--;
+ if (parts[i].life<=0 && (elem_properties&(PROP_LIFE_KILL_DEC|PROP_LIFE_KILL)))
+ {
+ // kill on change to no life
+ kill_part(i);
+ continue;
+ }
+ }
+ else if (parts[i].life<=0 && (elem_properties&PROP_LIFE_KILL))
+ {
+ // kill if no life
+ kill_part(i);
+ continue;
+ }
+ }
+ //the main particle loop function, goes over all particles.
+
+ for (i=0; i<=parts_lastActiveIndex; i++)
+ if (parts[i].type)
+ {
+ t = parts[i].type;
+
+ x = (int)(parts[i].x+0.5f);
+ y = (int)(parts[i].y+0.5f);
+
+ //this kills any particle out of the screen, or in a wall where it isn't supposed to go
+ if (x<CELL || y<CELL || x>=XRES-CELL || y>=YRES-CELL ||
+ (bmap[y/CELL][x/CELL] &&
+ (bmap[y/CELL][x/CELL]==WL_WALL ||
+ bmap[y/CELL][x/CELL]==WL_WALLELEC ||
+ bmap[y/CELL][x/CELL]==WL_ALLOWAIR ||
+ (bmap[y/CELL][x/CELL]==WL_DESTROYALL) ||
+ (bmap[y/CELL][x/CELL]==WL_ALLOWLIQUID && elements[t].Falldown!=2) ||
+ (bmap[y/CELL][x/CELL]==WL_ALLOWSOLID && elements[t].Falldown!=1) ||
+ (bmap[y/CELL][x/CELL]==WL_ALLOWGAS && !(elements[t].Properties&TYPE_GAS)) || //&& elements[t].Falldown!=0 && parts[i].type!=PT_FIRE && parts[i].type!=PT_SMKE && parts[i].type!=PT_HFLM) ||
+ (bmap[y/CELL][x/CELL]==WL_ALLOWENERGY && !(elements[t].Properties&TYPE_ENERGY)) ||
+ (bmap[y/CELL][x/CELL]==WL_DETECT && (t==PT_METL || t==PT_SPRK)) ||
+ (bmap[y/CELL][x/CELL]==WL_EWALL && !emap[y/CELL][x/CELL])) && (t!=PT_STKM) && (t!=PT_STKM2) && (t!=PT_FIGH)))
+ {
+ kill_part(i);
+ continue;
+ }
+ if (bmap[y/CELL][x/CELL]==WL_DETECT && emap[y/CELL][x/CELL]<8)
+ set_emap(x/CELL, y/CELL);
+
+ //adding to velocity from the particle's velocity
+ vx[y/CELL][x/CELL] = vx[y/CELL][x/CELL]*elements[t].AirLoss + elements[t].AirDrag*parts[i].vx;
+ vy[y/CELL][x/CELL] = vy[y/CELL][x/CELL]*elements[t].AirLoss + elements[t].AirDrag*parts[i].vy;
+
+ if (t==PT_GAS||t==PT_NBLE)
+ {
+ if (pv[y/CELL][x/CELL]<3.5f)
+ pv[y/CELL][x/CELL] += elements[t].HotAir*(3.5f-pv[y/CELL][x/CELL]);
+ if (y+CELL<YRES && pv[y/CELL+1][x/CELL]<3.5f)
+ pv[y/CELL+1][x/CELL] += elements[t].HotAir*(3.5f-pv[y/CELL+1][x/CELL]);
+ if (x+CELL<XRES)
+ {
+ if (pv[y/CELL][x/CELL+1]<3.5f)
+ pv[y/CELL][x/CELL+1] += elements[t].HotAir*(3.5f-pv[y/CELL][x/CELL+1]);
+ if (y+CELL<YRES && pv[y/CELL+1][x/CELL+1]<3.5f)
+ pv[y/CELL+1][x/CELL+1] += elements[t].HotAir*(3.5f-pv[y/CELL+1][x/CELL+1]);
+ }
+ }
+ else//add the hotair variable to the pressure map, like black hole, or white hole.
+ {
+ pv[y/CELL][x/CELL] += elements[t].HotAir;
+ if (y+CELL<YRES)
+ pv[y/CELL+1][x/CELL] += elements[t].HotAir;
+ if (x+CELL<XRES)
+ {
+ pv[y/CELL][x/CELL+1] += elements[t].HotAir;
+ if (y+CELL<YRES)
+ pv[y/CELL+1][x/CELL+1] += elements[t].HotAir;
+ }
+ }
+
+ //Gravity mode by Moach
+ switch (gravityMode)
+ {
+ default:
+ case 0:
+ pGravX = 0.0f;
+ pGravY = elements[t].Gravity;
+ break;
+ case 1:
+ pGravX = pGravY = 0.0f;
+ break;
+ case 2:
+ pGravD = 0.01f - hypotf((x - XCNTR), (y - YCNTR));
+ pGravX = elements[t].Gravity * ((float)(x - XCNTR) / pGravD);
+ pGravY = elements[t].Gravity * ((float)(y - YCNTR) / pGravD);
+ break;
+ }
+ //Get some gravity from the gravity map
+ if (t==PT_ANAR)
+ {
+ // perhaps we should have a ptypes variable for this
+ pGravX -= gravx[(y/CELL)*(XRES/CELL)+(x/CELL)];
+ pGravY -= gravy[(y/CELL)*(XRES/CELL)+(x/CELL)];
+ }
+ else if(t!=PT_STKM && t!=PT_STKM2 && t!=PT_FIGH && !(elements[t].Properties & TYPE_SOLID))
+ {
+ pGravX += gravx[(y/CELL)*(XRES/CELL)+(x/CELL)];
+ pGravY += gravy[(y/CELL)*(XRES/CELL)+(x/CELL)];
+ }
+ //velocity updates for the particle
+ if (!(parts[i].flags&FLAG_MOVABLE))
+ {
+ parts[i].vx *= elements[t].Loss;
+ parts[i].vy *= elements[t].Loss;
+ }
+ //particle gets velocity from the vx and vy maps
+ parts[i].vx += elements[t].Advection*vx[y/CELL][x/CELL] + pGravX;
+ parts[i].vy += elements[t].Advection*vy[y/CELL][x/CELL] + pGravY;
+
+
+ if (elements[t].Diffusion)//the random diffusion that gasses have
+ {
+#ifdef REALISTIC
+ //The magic number controlls diffusion speed
+ parts[i].vx += 0.05*sqrtf(parts[i].temp)*elements[t].Diffusion*(rand()/(0.5f*RAND_MAX)-1.0f);
+ parts[i].vy += 0.05*sqrtf(parts[i].temp)*elements[t].Diffusion*(rand()/(0.5f*RAND_MAX)-1.0f);
+#else
+ parts[i].vx += elements[t].Diffusion*(rand()/(0.5f*RAND_MAX)-1.0f);
+ parts[i].vy += elements[t].Diffusion*(rand()/(0.5f*RAND_MAX)-1.0f);
+#endif
+ }
+
+ j = surround_space = nt = 0;//if nt is greater than 1 after this, then there is a particle around the current particle, that is NOT the current particle's type, for water movement.
+ for (nx=-1; nx<2; nx++)
+ for (ny=-1; ny<2; ny++) {
+ if (nx||ny) {
+ surround[j] = r = pmap[y+ny][x+nx];
+ j++;
+ if (!(r&0xFF))
+ surround_space++;//there is empty space
+ if ((r&0xFF)!=t)
+ nt++;//there is nothing or a different particle
+ }
+ }
+
+ float gel_scale = 1.0f;
+ if (t==PT_GEL)
+ gel_scale = parts[i].tmp*2.55f;
+
+ if (!legacy_enable)
+ {
+ if (y-2 >= 0 && y-2 < YRES && (elements[t].Properties&TYPE_LIQUID) && (t!=PT_GEL || gel_scale>(1+rand()%255))) {//some heat convection for liquids
+ r = pmap[y-2][x];
+ if (!(!r || parts[i].type != (r&0xFF))) {
+ if (parts[i].temp>parts[r>>8].temp) {
+ swappage = parts[i].temp;
+ parts[i].temp = parts[r>>8].temp;
+ parts[r>>8].temp = swappage;
+ }
+ }
+ }
+
+ //heat transfer code
+ h_count = 0;
+#ifdef REALISTIC
+ if (t&&(t!=PT_HSWC||parts[i].life==10)&&(elements[t].HeatConduct*gel_scale))
+ {
+ float c_Cm = 0.0f;
+#else
+ if (t&&(t!=PT_HSWC||parts[i].life==10)&&(elements[t].HeatConduct*gel_scale)>(rand()%250))
+ {
+ float c_Cm = 0.0f;
+#endif
+ if (aheat_enable && !(elements[t].Properties&PROP_NOAMBHEAT))
+ {
+#ifdef REALISTIC
+ c_heat = parts[i].temp*96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight) + hv[y/CELL][x/CELL]*100*(pv[y/CELL][x/CELL]+273.15f)/256;
+ c_Cm = 96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight) + 100*(pv[y/CELL][x/CELL]+273.15f)/256;
+ pt = c_heat/c_Cm;
+ pt = restrict_flt(pt, -MAX_TEMP+MIN_TEMP, MAX_TEMP-MIN_TEMP);
+ parts[i].temp = pt;
+ //Pressure increase from heat (temporary)
+ pv[y/CELL][x/CELL] += (pt-hv[y/CELL][x/CELL])*0.004;
+ hv[y/CELL][x/CELL] = pt;
+#else
+ c_heat = (hv[y/CELL][x/CELL]-parts[i].temp)*0.04;
+ c_heat = restrict_flt(c_heat, -MAX_TEMP+MIN_TEMP, MAX_TEMP-MIN_TEMP);
+ parts[i].temp += c_heat;
+ hv[y/CELL][x/CELL] -= c_heat;
+#endif
+ }
+ c_heat = 0.0f;
+ c_Cm = 0.0f;
+ for (j=0; j<8; j++)
+ {
+ surround_hconduct[j] = i;
+ r = surround[j];
+ if (!r)
+ continue;
+ rt = r&0xFF;
+ if (rt&&elements[rt].HeatConduct&&(rt!=PT_HSWC||parts[r>>8].life==10)
+ &&(t!=PT_FILT||(rt!=PT_BRAY&&rt!=PT_BIZR&&rt!=PT_BIZRG))
+ &&(rt!=PT_FILT||(t!=PT_BRAY&&t!=PT_PHOT&&t!=PT_BIZR&&t!=PT_BIZRG))
+ &&(t!=PT_ELEC||rt!=PT_DEUT)
+ &&(t!=PT_DEUT||rt!=PT_ELEC))
+ {
+ surround_hconduct[j] = r>>8;
+#ifdef REALISTIC
+ if (rt==PT_GEL)
+ gel_scale = parts[r>>8].tmp*2.55f;
+ else gel_scale = 1.0f;
+
+ c_heat += parts[r>>8].temp*96.645/elements[rt].HeatConduct*gel_scale*fabs(elements[rt].Weight);
+ c_Cm += 96.645/elements[rt].HeatConduct*gel_scale*fabs(elements[rt].Weight);
+#else
+ c_heat += parts[r>>8].temp;
+#endif
+ h_count++;
+ }
+ }
+#ifdef REALISTIC
+ if (t==PT_GEL)
+ gel_scale = parts[i].tmp*2.55f;
+ else gel_scale = 1.0f;
+
+ if (t == PT_PHOT)
+ pt = (c_heat+parts[i].temp*96.645)/(c_Cm+96.645);
+ else
+ pt = (c_heat+parts[i].temp*96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight))/(c_Cm+96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight));
+
+ c_heat += parts[i].temp*96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight);
+ c_Cm += 96.645/elements[t].HeatConduct*gel_scale*fabs(elements[t].Weight);
+ parts[i].temp = restrict_flt(pt, MIN_TEMP, MAX_TEMP);
+#else
+ pt = (c_heat+parts[i].temp)/(h_count+1);
+ pt = parts[i].temp = restrict_flt(pt, MIN_TEMP, MAX_TEMP);
+ for (j=0; j<8; j++)
+ {
+ parts[surround_hconduct[j]].temp = pt;
+ }
+#endif
+
+ ctemph = ctempl = pt;
+ // change boiling point with pressure
+ if ((elements[t].State==ST_LIQUID && elements[t].HighTemperatureTransition>-1 && elements[t].HighTemperatureTransition<PT_NUM && elements[elements[t].HighTemperatureTransition].State==ST_GAS)
+ || t==PT_LNTG || t==PT_SLTW)
+ ctemph -= 2.0f*pv[y/CELL][x/CELL];
+ else if ((elements[t].State==ST_GAS && elements[t].LowTemperatureTransition>-1 && elements[t].LowTemperatureTransition<PT_NUM && elements[elements[t].LowTemperatureTransition].State==ST_LIQUID)
+ || t==PT_WTRV)
+ ctempl -= 2.0f*pv[y/CELL][x/CELL];
+ s = 1;
+
+ //A fix for ice with ctype = 0
+ if ((t==PT_ICEI || t==PT_SNOW) && (parts[i].ctype==0 || parts[i].ctype>=PT_NUM || parts[i].ctype==PT_ICEI || parts[i].ctype==PT_SNOW))
+ parts[i].ctype = PT_WATR;
+
+ if (ctemph>elements[t].HighTemperature&&elements[t].HighTemperatureTransition>-1) {
+ // particle type change due to high temperature
+#ifdef REALISTIC
+ float dbt = ctempl - pt;
+ if (elements[t].HighTemperatureTransition!=PT_NUM)
+ {
+ if (platent[t] <= (c_heat - (elements[t].HighTemperature - dbt)*c_Cm))
+ {
+ pt = (c_heat - platent[t])/c_Cm;
+ t = elements[t].HighTemperatureTransition;
+ }
+ else
+ {
+ parts[i].temp = restrict_flt(elements[t].HighTemperature - dbt, MIN_TEMP, MAX_TEMP);
+ s = 0;
+ }
+ }
+ #else
+ if (elements[t].HighTemperatureTransition!=PT_NUM)
+ t = elements[t].HighTemperatureTransition;
+#endif
+ else if (t==PT_ICEI || t==PT_SNOW) {
+ if (parts[i].ctype<PT_NUM&&parts[i].ctype!=t) {
+ if (elements[parts[i].ctype].LowTemperatureTransition==t&&pt<=elements[parts[i].ctype].LowTemperature) s = 0;
+ else {
+#ifdef REALISTIC
+ //One ice table value for all it's kinds
+ if (platent[t] <= (c_heat - (elements[parts[i].ctype].LowTemperature - dbt)*c_Cm))
+ {
+ pt = (c_heat - platent[t])/c_Cm;
+ t = parts[i].ctype;
+ parts[i].ctype = PT_NONE;
+ parts[i].life = 0;
+ }
+ else
+ {
+ parts[i].temp = restrict_flt(elements[parts[i].ctype].LowTemperature - dbt, MIN_TEMP, MAX_TEMP);
+ s = 0;
+ }
+ #else
+ t = parts[i].ctype;
+ parts[i].ctype = PT_NONE;
+ parts[i].life = 0;
+#endif
+ }
+ }
+ else s = 0;
+ }
+ else if (t==PT_SLTW) {
+#ifdef REALISTIC
+ if (platent[t] <= (c_heat - (elements[t].HighTemperature - dbt)*c_Cm))
+ {
+ pt = (c_heat - platent[t])/c_Cm;
+
+ if (rand()%4==0) t = PT_SALT;
+ else t = PT_WTRV;
+ }
+ else
+ {
+ parts[i].temp = restrict_flt(elements[t].HighTemperature - dbt, MIN_TEMP, MAX_TEMP);
+ s = 0;
+ }
+#else
+ if (rand()%4==0) t = PT_SALT;
+ else t = PT_WTRV;
+#endif
+ }
+ else s = 0;
+ } else if (ctempl<elements[t].LowTemperature&&elements[t].LowTemperatureTransition>-1) {
+ // particle type change due to low temperature
+#ifdef REALISTIC
+ float dbt = ctempl - pt;
+ if (elements[t].LowTemperatureTransition!=PT_NUM)
+ {
+ if (platent[elements[t].LowTemperatureTransition] >= (c_heat - (elements[t].LowTemperature - dbt)*c_Cm))
+ {
+ pt = (c_heat + platent[elements[t].LowTemperatureTransition])/c_Cm;
+ t = elements[t].LowTemperatureTransition;
+ }
+ else
+ {
+ parts[i].temp = restrict_flt(elements[t].LowTemperature - dbt, MIN_TEMP, MAX_TEMP);
+ s = 0;
+ }
+ }
+#else
+ if (elements[t].LowTemperatureTransition!=PT_NUM)
+ t = elements[t].LowTemperatureTransition;
+#endif
+ else if (t==PT_WTRV) {
+ if (pt<273.0f) t = PT_RIME;
+ else t = PT_DSTW;
+ }
+ else if (t==PT_LAVA) {
+ if (parts[i].ctype>0 && parts[i].ctype<PT_NUM && parts[i].ctype!=PT_LAVA) {
+ if (parts[i].ctype==PT_THRM&&pt>=elements[PT_BMTL].HighTemperature) s = 0;
+ else if ((parts[i].ctype==PT_VIBR || parts[i].ctype==PT_BVBR) && pt>=273.15f) s = 0;
+ else if (elements[parts[i].ctype].HighTemperatureTransition==PT_LAVA) {
+ if (pt>=elements[parts[i].ctype].HighTemperature) s = 0;
+ }
+ else if (pt>=973.0f) s = 0; // freezing point for lava with any other (not listed in ptransitions as turning into lava) ctype
+ if (s) {
+ t = parts[i].ctype;
+ parts[i].ctype = PT_NONE;
+ if (t==PT_THRM) {
+ parts[i].tmp = 0;
+ t = PT_BMTL;
+ }
+ if (t==PT_PLUT)
+ {
+ parts[i].tmp = 0;
+ t = PT_LAVA;
+ }
+ }
+ }
+ else if (pt<973.0f) t = PT_STNE;
+ else s = 0;
+ }
+ else s = 0;
+ }
+ else s = 0;
+#ifdef REALISTIC
+ pt = restrict_flt(pt, MIN_TEMP, MAX_TEMP);
+ for (j=0; j<8; j++)
+ {
+ parts[surround_hconduct[j]].temp = pt;
+ }
+#endif
+ if (s) { // particle type change occurred
+ if (t==PT_ICEI||t==PT_LAVA||t==PT_SNOW)
+ parts[i].ctype = parts[i].type;
+ if (!(t==PT_ICEI&&parts[i].ctype==PT_FRZW)) parts[i].life = 0;
+ if (elements[t].State==ST_GAS&&elements[parts[i].type].State!=ST_GAS)
+ pv[y/CELL][x/CELL] += 0.50f;
+ part_change_type(i,x,y,t);
+ if (t==PT_FIRE||t==PT_PLSM||t==PT_HFLM)
+ parts[i].life = rand()%50+120;
+ if (t==PT_LAVA) {
+ if (parts[i].ctype==PT_BRMT) parts[i].ctype = PT_BMTL;
+ else if (parts[i].ctype==PT_SAND) parts[i].ctype = PT_GLAS;
+ else if (parts[i].ctype==PT_BGLA) parts[i].ctype = PT_GLAS;
+ else if (parts[i].ctype==PT_PQRT) parts[i].ctype = PT_QRTZ;
+ parts[i].life = rand()%120+240;
+ }
+ if (t==PT_NONE) {
+ kill_part(i);
+ goto killed;
+ }
+ }
+
+ pt = parts[i].temp = restrict_flt(parts[i].temp, MIN_TEMP, MAX_TEMP);
+ if (t==PT_LAVA) {
+ parts[i].life = restrict_flt((parts[i].temp-700)/7, 0.0f, 400.0f);
+ if (parts[i].ctype==PT_THRM&&parts[i].tmp>0)
+ {
+ parts[i].tmp--;
+ parts[i].temp = 3500;
+ }
+ if (parts[i].ctype==PT_PLUT&&parts[i].tmp>0)
+ {
+ parts[i].tmp--;
+ parts[i].temp = MAX_TEMP;
+ }
+ }
+#ifdef REALISTIC //needed to fix update_particles_i parsing
+ }
+#else
+ }
+#endif
+ else parts[i].temp = restrict_flt(parts[i].temp, MIN_TEMP, MAX_TEMP);
+ }
+
+ if (t==PT_LIFE)
+ {
+ parts[i].temp = restrict_flt(parts[i].temp-50.0f, MIN_TEMP, MAX_TEMP);
+ //ISGOL=1;//means there is a life particle on screen
+ }
+ if (t==PT_WIRE)
+ {
+ //wire_placed = 1;
+ }
+ //spark updates from walls
+ if ((elements[t].Properties&PROP_CONDUCTS) || t==PT_SPRK)
+ {
+ nx = x % CELL;
+ if (nx == 0)
+ nx = x/CELL - 1;
+ else if (nx == CELL-1)
+ nx = x/CELL + 1;
+ else
+ nx = x/CELL;
+ ny = y % CELL;
+ if (ny == 0)
+ ny = y/CELL - 1;
+ else if (ny == CELL-1)
+ ny = y/CELL + 1;
+ else
+ ny = y/CELL;
+ if (nx>=0 && ny>=0 && nx<XRES/CELL && ny<YRES/CELL)
+ {
+ if (t!=PT_SPRK)
+ {
+ if (emap[ny][nx]==12 && !parts[i].life)
+ {
+ part_change_type(i,x,y,PT_SPRK);
+ parts[i].life = 4;
+ parts[i].ctype = t;
+ t = PT_SPRK;
+ }
+ }
+ else if (bmap[ny][nx]==WL_DETECT || bmap[ny][nx]==WL_EWALL || bmap[ny][nx]==WL_ALLOWLIQUID || bmap[ny][nx]==WL_WALLELEC || bmap[ny][nx]==WL_ALLOWALLELEC || bmap[ny][nx]==WL_EHOLE)
+ set_emap(nx, ny);
+ }
+ }
+
+ //the basic explosion, from the .explosive variable
+ if ((elements[t].Explosive&2) && pv[y/CELL][x/CELL]>2.5f)
+ {
+ parts[i].life = rand()%80+180;
+ parts[i].temp = restrict_flt(elements[PT_FIRE].Temperature + (elements[t].Flammable/2), MIN_TEMP, MAX_TEMP);
+ t = PT_FIRE;
+ part_change_type(i,x,y,t);
+ pv[y/CELL][x/CELL] += 0.25f * CFDS;
+ }
+
+
+ s = 1;
+ gravtot = fabs(gravy[(y/CELL)*(XRES/CELL)+(x/CELL)])+fabs(gravx[(y/CELL)*(XRES/CELL)+(x/CELL)]);
+ if (pv[y/CELL][x/CELL]>elements[t].HighPressure&&elements[t].HighPressureTransition>-1) {
+ // particle type change due to high pressure
+ if (elements[t].HighPressureTransition!=PT_NUM)
+ t = elements[t].HighPressureTransition;
+ else if (t==PT_BMTL) {
+ if (pv[y/CELL][x/CELL]>2.5f)
+ t = PT_BRMT;
+ else if (pv[y/CELL][x/CELL]>1.0f && parts[i].tmp==1)
+ t = PT_BRMT;
+ else s = 0;
+ }
+ else s = 0;
+ } else if (pv[y/CELL][x/CELL]<elements[t].LowPressure&&elements[t].LowPressureTransition>-1) {
+ // particle type change due to low pressure
+ if (elements[t].LowPressureTransition!=PT_NUM)
+ t = elements[t].LowPressureTransition;
+ else s = 0;
+ } else if (gravtot>(elements[t].HighPressure/4.0f)&&elements[t].HighPressureTransition>-1) {
+ // particle type change due to high gravity
+ if (elements[t].HighPressureTransition!=PT_NUM)
+ t = elements[t].HighPressureTransition;
+ else if (t==PT_BMTL) {
+ if (gravtot>0.625f)
+ t = PT_BRMT;
+ else if (gravtot>0.25f && parts[i].tmp==1)
+ t = PT_BRMT;
+ else s = 0;
+ }
+ else s = 0;
+ } else s = 0;
+ if (s) { // particle type change occurred
+ parts[i].life = 0;
+ part_change_type(i,x,y,t);
+ if (t==PT_FIRE)
+ parts[i].life = rand()%50+120;
+ if (t==PT_NONE) {
+ kill_part(i);
+ goto killed;
+ }
+ }
+
+ //call the particle update function, if there is one
+#ifdef LUACONSOLE
+ if (elements[t].Update && lua_el_mode[t] != 2)
+#else
+ if (elements[t].Update)
+#endif
+ {
+ if ((*(elements[t].Update))(this, i,x,y,surround_space,nt, parts, pmap))
+ continue;
+ else if (t==PT_WARP)
+ {
+ // Warp does some movement in its update func, update variables to avoid incorrect data in pmap
+ x = (int)(parts[i].x+0.5f);
+ y = (int)(parts[i].y+0.5f);
+ }
+ }
+#ifdef LUACONSOLE
+ if(lua_el_mode[t])
+ {
+ if(luacon_part_update(t,i,x,y,surround_space,nt))
+ continue;
+ // Need to update variables, in case they've been changed by Lua
+ x = (int)(parts[i].x+0.5f);
+ y = (int)(parts[i].y+0.5f);
+ }
+#endif
+
+
+ if(legacy_enable)//if heat sim is off
+ Element::legacyUpdate(this, i,x,y,surround_space,nt, parts, pmap);
+
+killed:
+ if (parts[i].type == PT_NONE)//if its dead, skip to next particle
+ continue;
+
+ if (!parts[i].vx&&!parts[i].vy)//if its not moving, skip to next particle, movement code it next
+ continue;
+
+ mv = fmaxf(fabsf(parts[i].vx), fabsf(parts[i].vy));
+ if (mv < ISTP)
+ {
+ clear_x = x;
+ clear_y = y;
+ clear_xf = parts[i].x;
+ clear_yf = parts[i].y;
+ fin_xf = clear_xf + parts[i].vx;
+ fin_yf = clear_yf + parts[i].vy;
+ fin_x = (int)(fin_xf+0.5f);
+ fin_y = (int)(fin_yf+0.5f);
+ }
+ else
+ {
+ // interpolate to see if there is anything in the way
+ dx = parts[i].vx*ISTP/mv;
+ dy = parts[i].vy*ISTP/mv;
+ fin_xf = parts[i].x;
+ fin_yf = parts[i].y;
+ while (1)
+ {
+ mv -= ISTP;
+ fin_xf += dx;
+ fin_yf += dy;
+ fin_x = (int)(fin_xf+0.5f);
+ fin_y = (int)(fin_yf+0.5f);
+ if (mv <= 0.0f)
+ {
+ // nothing found
+ fin_xf = parts[i].x + parts[i].vx;
+ fin_yf = parts[i].y + parts[i].vy;
+ fin_x = (int)(fin_xf+0.5f);
+ fin_y = (int)(fin_yf+0.5f);
+ clear_xf = fin_xf-dx;
+ clear_yf = fin_yf-dy;
+ clear_x = (int)(clear_xf+0.5f);
+ clear_y = (int)(clear_yf+0.5f);
+ break;
+ }
+ if (fin_x<CELL || fin_y<CELL || fin_x>=XRES-CELL || fin_y>=YRES-CELL || pmap[fin_y][fin_x] || (bmap[fin_y/CELL][fin_x/CELL] && (bmap[fin_y/CELL][fin_x/CELL]==WL_DESTROYALL || !eval_move(t,fin_x,fin_y,NULL))))
+ {
+ // found an obstacle
+ clear_xf = fin_xf-dx;
+ clear_yf = fin_yf-dy;
+ clear_x = (int)(clear_xf+0.5f);
+ clear_y = (int)(clear_yf+0.5f);
+ break;
+ }
+ if (bmap[fin_y/CELL][fin_x/CELL]==WL_DETECT && emap[fin_y/CELL][fin_x/CELL]<8)
+ set_emap(fin_x/CELL, fin_y/CELL);
+ }
+ }
+
+ stagnant = parts[i].flags & FLAG_STAGNANT;
+ parts[i].flags &= ~FLAG_STAGNANT;
+
+ if (t==PT_STKM || t==PT_STKM2 || t==PT_FIGH)
+ {
+ int nx, ny;
+ //head movement, let head pass through anything
+ parts[i].x += parts[i].vx;
+ parts[i].y += parts[i].vy;
+ nx = (int)((float)parts[i].x+0.5f);
+ ny = (int)((float)parts[i].y+0.5f);
+ if (ny!=y || nx!=x)
+ {
+ if ((pmap[y][x]>>8)==i) pmap[y][x] = 0;
+ else if ((photons[y][x]>>8)==i) photons[y][x] = 0;
+ if (nx<CELL || nx>=XRES-CELL || ny<CELL || ny>=YRES-CELL)
+ {
+ kill_part(i);
+ continue;
+ }
+ if (elements[t].Properties & TYPE_ENERGY)
+ photons[ny][nx] = t|(i<<8);
+ else if (t)
+ pmap[ny][nx] = t|(i<<8);
+ }
+ }
+ else if (elements[t].Properties & TYPE_ENERGY)
+ {
+ if (t == PT_PHOT) {
+ if (parts[i].flags&FLAG_SKIPMOVE)
+ {
+ parts[i].flags &= ~FLAG_SKIPMOVE;
+ continue;
+ }
+
+ rt = pmap[fin_y][fin_x] & 0xFF;
+ lt = pmap[y][x] & 0xFF;
+
+ r = eval_move(PT_PHOT, fin_x, fin_y, NULL);
+ if (((rt==PT_GLAS && lt!=PT_GLAS) || (rt!=PT_GLAS && lt==PT_GLAS)) && r) {
+ if (!get_normal_interp(REFRACT|t, parts[i].x, parts[i].y, parts[i].vx, parts[i].vy, &nrx, &nry)) {
+ kill_part(i);
+ continue;
+ }
+
+ r = get_wavelength_bin(&parts[i].ctype);
+ if (r == -1) {
+ kill_part(i);
+ continue;
+ }
+ nn = GLASS_IOR - GLASS_DISP*(r-15)/15.0f;
+ nn *= nn;
+ nrx = -nrx;
+ nry = -nry;
+ if (rt==PT_GLAS && lt!=PT_GLAS)
+ nn = 1.0f/nn;
+ ct1 = parts[i].vx*nrx + parts[i].vy*nry;
+ ct2 = 1.0f - (nn*nn)*(1.0f-(ct1*ct1));
+ if (ct2 < 0.0f) {
+ // total internal reflection
+ parts[i].vx -= 2.0f*ct1*nrx;
+ parts[i].vy -= 2.0f*ct1*nry;
+ fin_xf = parts[i].x;
+ fin_yf = parts[i].y;
+ fin_x = x;
+ fin_y = y;
+ } else {
+ // refraction
+ ct2 = sqrtf(ct2);
+ ct2 = ct2 - nn*ct1;
+ parts[i].vx = nn*parts[i].vx + ct2*nrx;
+ parts[i].vy = nn*parts[i].vy + ct2*nry;
+ }
+ }
+ }
+ if (stagnant)//FLAG_STAGNANT set, was reflected on previous frame
+ {
+ // cast coords as int then back to float for compatibility with existing saves
+ if (!do_move(i, x, y, (float)fin_x, (float)fin_y) && parts[i].type) {
+ kill_part(i);
+ continue;
+ }
+ }
+ else if (!do_move(i, x, y, fin_xf, fin_yf))
+ {
+ if (parts[i].type == PT_NONE)
+ continue;
+ // reflection
+ parts[i].flags |= FLAG_STAGNANT;
+ if (t==PT_NEUT && 100>(rand()%1000))
+ {
+ kill_part(i);
+ continue;
+ }
+ r = pmap[fin_y][fin_x];
+
+ if (((r&0xFF)==PT_PIPE || (r&0xFF) == PT_PPIP) && !(parts[r>>8].tmp&0xFF))
+ {
+ parts[r>>8].tmp = (parts[r>>8].tmp&~0xFF) | parts[i].type;
+ parts[r>>8].temp = parts[i].temp;
+ parts[r>>8].tmp2 = parts[i].life;
+ parts[r>>8].pavg[0] = parts[i].tmp;
+ parts[r>>8].pavg[1] = parts[i].ctype;
+ kill_part(i);
+ continue;
+ }
+
+ // this should be replaced with a particle type attribute ("photwl" or something)
+ if ((r & 0xFF) == PT_PSCN) parts[i].ctype = 0x00000000;
+ if ((r & 0xFF) == PT_NSCN) parts[i].ctype = 0x00000000;
+ if ((r & 0xFF) == PT_SPRK) parts[i].ctype = 0x00000000;
+ if ((r & 0xFF) == PT_COAL) parts[i].ctype = 0x00000000;
+ if ((r & 0xFF) == PT_BCOL) parts[i].ctype = 0x00000000;
+ if ((r & 0xFF) == PT_PLEX) parts[i].ctype &= 0x1F00003E;
+ if ((r & 0xFF) == PT_NITR) parts[i].ctype &= 0x0007C000;
+ if ((r & 0xFF) == PT_NBLE) parts[i].ctype &= 0x3FFF8000;
+ if ((r & 0xFF) == PT_LAVA) parts[i].ctype &= 0x3FF00000;
+ if ((r & 0xFF) == PT_ACID) parts[i].ctype &= 0x1FE001FE;
+ if ((r & 0xFF) == PT_DUST) parts[i].ctype &= 0x3FFFFFC0;
+ if ((r & 0xFF) == PT_SNOW) parts[i].ctype &= 0x03FFFFFF;
+ if ((r & 0xFF) == PT_GOO) parts[i].ctype &= 0x3FFAAA00;
+ if ((r & 0xFF) == PT_PLNT) parts[i].ctype &= 0x0007C000;
+ if ((r & 0xFF) == PT_PLUT) parts[i].ctype &= 0x001FCE00;
+ if ((r & 0xFF) == PT_URAN) parts[i].ctype &= 0x003FC000;
+
+ if (get_normal_interp(t, parts[i].x, parts[i].y, parts[i].vx, parts[i].vy, &nrx, &nry)) {
+ dp = nrx*parts[i].vx + nry*parts[i].vy;
+ parts[i].vx -= 2.0f*dp*nrx;
+ parts[i].vy -= 2.0f*dp*nry;
+ // leave the actual movement until next frame so that reflection of fast particles and refraction happen correctly
+ } else {
+ if (t!=PT_NEUT)
+ kill_part(i);
+ continue;
+ }
+ if (!(parts[i].ctype&0x3FFFFFFF)&&t!=PT_NEUT&&t!=PT_ELEC) {
+ kill_part(i);
+ continue;
+ }
+ }
+ }
+ else if (elements[t].Falldown==0)
+ {
+ // gasses and solids (but not powders)
+ if (!do_move(i, x, y, fin_xf, fin_yf))
+ {
+ if (parts[i].type == PT_NONE)
+ continue;
+ // can't move there, so bounce off
+ // TODO
+ // TODO: Work out what previous TODO was for
+ if (fin_x>x+ISTP) fin_x=x+ISTP;
+ if (fin_x<x-ISTP) fin_x=x-ISTP;
+ if (fin_y>y+ISTP) fin_y=y+ISTP;
+ if (fin_y<y-ISTP) fin_y=y-ISTP;
+ if (do_move(i, x, y, 0.25f+(float)(2*x-fin_x), 0.25f+fin_y))
+ {
+ parts[i].vx *= elements[t].Collision;
+ }
+ else if (do_move(i, x, y, 0.25f+fin_x, 0.25f+(float)(2*y-fin_y)))
+ {
+ parts[i].vy *= elements[t].Collision;
+ }
+ else
+ {
+ parts[i].vx *= elements[t].Collision;
+ parts[i].vy *= elements[t].Collision;
+ }
+ }
+ }
+ else
+ {
+ if (water_equal_test && elements[t].Falldown == 2 && 1>= rand()%400)//checking stagnant is cool, but then it doesn't update when you change it later.
+ {
+ if (!flood_water(x,y,i,y, parts[i].tmp2))
+ goto movedone;
+ }
+ // liquids and powders
+ if (!do_move(i, x, y, fin_xf, fin_yf))
+ {
+ if (parts[i].type == PT_NONE)
+ continue;
+ if (fin_x!=x && do_move(i, x, y, fin_xf, clear_yf))
+ {
+ parts[i].vx *= elements[t].Collision;
+ parts[i].vy *= elements[t].Collision;
+ }
+ else if (fin_y!=y && do_move(i, x, y, clear_xf, fin_yf))
+ {
+ parts[i].vx *= elements[t].Collision;
+ parts[i].vy *= elements[t].Collision;
+ }
+ else
+ {
+ s = 1;
+ r = (rand()%2)*2-1;
+ if ((clear_x!=x || clear_y!=y || nt || surround_space) &&
+ (fabsf(parts[i].vx)>0.01f || fabsf(parts[i].vy)>0.01f))
+ {
+ // allow diagonal movement if target position is blocked
+ // but no point trying this if particle is stuck in a block of identical particles
+ dx = parts[i].vx - parts[i].vy*r;
+ dy = parts[i].vy + parts[i].vx*r;
+ if (fabsf(dy)>fabsf(dx))
+ mv = fabsf(dy);
+ else
+ mv = fabsf(dx);
+ dx /= mv;
+ dy /= mv;
+ if (do_move(i, x, y, clear_xf+dx, clear_yf+dy))
+ {
+ parts[i].vx *= elements[t].Collision;
+ parts[i].vy *= elements[t].Collision;
+ goto movedone;
+ }
+ swappage = dx;
+ dx = dy*r;
+ dy = -swappage*r;
+ if (do_move(i, x, y, clear_xf+dx, clear_yf+dy))
+ {
+ parts[i].vx *= elements[t].Collision;
+ parts[i].vy *= elements[t].Collision;
+ goto movedone;
+ }
+ }
+ if (elements[t].Falldown>1 && !grav->ngrav_enable && gravityMode==0 && parts[i].vy>fabsf(parts[i].vx))
+ {
+ s = 0;
+ // stagnant is true if FLAG_STAGNANT was set for this particle in previous frame
+ if (!stagnant || nt) //nt is if there is an something else besides the current particle type, around the particle
+ rt = 30;//slight less water lag, although it changes how it moves a lot
+ else
+ rt = 10;
+
+ if (t==PT_GEL)
+ rt = parts[i].tmp*0.20f+5.0f;
+
+ for (j=clear_x+r; j>=0 && j>=clear_x-rt && j<clear_x+rt && j<XRES; j+=r)
+ {
+ if (((pmap[fin_y][j]&0xFF)!=t || bmap[fin_y/CELL][j/CELL])
+ && (s=do_move(i, x, y, (float)j, fin_yf)))
+ {
+ nx = (int)(parts[i].x+0.5f);
+ ny = (int)(parts[i].y+0.5f);
+ break;
+ }
+ if (fin_y!=clear_y && ((pmap[clear_y][j]&0xFF)!=t || bmap[clear_y/CELL][j/CELL])
+ && (s=do_move(i, x, y, (float)j, clear_yf)))
+ {
+ nx = (int)(parts[i].x+0.5f);
+ ny = (int)(parts[i].y+0.5f);
+ break;
+ }
+ if ((pmap[clear_y][j]&0xFF)!=t || (bmap[clear_y/CELL][j/CELL] && bmap[clear_y/CELL][j/CELL]!=WL_STREAM))
+ break;
+ }
+ if (parts[i].vy>0)
+ r = 1;
+ else
+ r = -1;
+ if (s==1)
+ for (j=ny+r; j>=0 && j<YRES && j>=ny-rt && j<ny+rt; j+=r)
+ {
+ if (((pmap[j][nx]&0xFF)!=t || bmap[j/CELL][nx/CELL]) && do_move(i, nx, ny, (float)nx, (float)j))
+ break;
+ if ((pmap[j][nx]&255)!=t || (bmap[j/CELL][nx/CELL] && bmap[j/CELL][nx/CELL]!=WL_STREAM))
+ break;
+ }
+ else if (s==-1) {} // particle is out of bounds
+ else if ((clear_x!=x||clear_y!=y) && do_move(i, x, y, clear_xf, clear_yf)) {}
+ else parts[i].flags |= FLAG_STAGNANT;
+ parts[i].vx *= elements[t].Collision;
+ parts[i].vy *= elements[t].Collision;
+ }
+ else if (elements[t].Falldown>1 && fabsf(pGravX*parts[i].vx+pGravY*parts[i].vy)>fabsf(pGravY*parts[i].vx-pGravX*parts[i].vy))
+ {
+ float nxf, nyf, prev_pGravX, prev_pGravY, ptGrav = elements[t].Gravity;
+ s = 0;
+ // stagnant is true if FLAG_STAGNANT was set for this particle in previous frame
+ if (!stagnant || nt) //nt is if there is an something else besides the current particle type, around the particle
+ rt = 30;//slight less water lag, although it changes how it moves a lot
+ else
+ rt = 10;
+ nxf = clear_xf;
+ nyf = clear_yf;
+ for (j=0;j<rt;j++)
+ {
+ switch (gravityMode)
+ {
+ default:
+ case 0:
+ pGravX = 0.0f;
+ pGravY = ptGrav;
+ break;
+ case 1:
+ pGravX = pGravY = 0.0f;
+ break;
+ case 2:
+ pGravD = 0.01f - hypotf((nx - XCNTR), (ny - YCNTR));
+ pGravX = ptGrav * ((float)(nx - XCNTR) / pGravD);
+ pGravY = ptGrav * ((float)(ny - YCNTR) / pGravD);
+ break;
+ }
+ pGravX += gravx[(ny/CELL)*(XRES/CELL)+(nx/CELL)];
+ pGravY += gravy[(ny/CELL)*(XRES/CELL)+(nx/CELL)];
+ if (fabsf(pGravY)>fabsf(pGravX))
+ mv = fabsf(pGravY);
+ else
+ mv = fabsf(pGravX);
+ if (mv<0.0001f) break;
+ pGravX /= mv;
+ pGravY /= mv;
+ if (j)
+ {
+ nxf += r*(pGravY*2.0f-prev_pGravY);
+ nyf += -r*(pGravX*2.0f-prev_pGravX);
+ }
+ else
+ {
+ nxf += r*pGravY;
+ nyf += -r*pGravX;
+ }
+ prev_pGravX = pGravX;
+ prev_pGravY = pGravY;
+ nx = (int)(nxf+0.5f);
+ ny = (int)(nyf+0.5f);
+ if (nx<0 || ny<0 || nx>=XRES || ny >=YRES)
+ break;
+ if ((pmap[ny][nx]&0xFF)!=t || bmap[ny/CELL][nx/CELL])
+ {
+ s = do_move(i, x, y, nxf, nyf);
+ if (s)
+ {
+ nx = (int)(parts[i].x+0.5f);
+ ny = (int)(parts[i].y+0.5f);
+ break;
+ }
+ if (bmap[ny/CELL][nx/CELL]!=WL_STREAM)
+ break;
+ }
+ }
+ if (s==1)
+ {
+ clear_x = nx;
+ clear_y = ny;
+ for (j=0;j<rt;j++)
+ {
+ switch (gravityMode)
+ {
+ default:
+ case 0:
+ pGravX = 0.0f;
+ pGravY = ptGrav;
+ break;
+ case 1:
+ pGravX = pGravY = 0.0f;
+ break;
+ case 2:
+ pGravD = 0.01f - hypotf((nx - XCNTR), (ny - YCNTR));
+ pGravX = ptGrav * ((float)(nx - XCNTR) / pGravD);
+ pGravY = ptGrav * ((float)(ny - YCNTR) / pGravD);
+ break;
+ }
+ pGravX += gravx[(ny/CELL)*(XRES/CELL)+(nx/CELL)];
+ pGravY += gravy[(ny/CELL)*(XRES/CELL)+(nx/CELL)];
+ if (fabsf(pGravY)>fabsf(pGravX))
+ mv = fabsf(pGravY);
+ else
+ mv = fabsf(pGravX);
+ if (mv<0.0001f) break;
+ pGravX /= mv;
+ pGravY /= mv;
+ nxf += pGravX;
+ nyf += pGravY;
+ nx = (int)(nxf+0.5f);
+ ny = (int)(nyf+0.5f);
+ if (nx<0 || ny<0 || nx>=XRES || ny>=YRES)
+ break;
+ if ((pmap[ny][nx]&0xFF)!=t || bmap[ny/CELL][nx/CELL])
+ {
+ s = do_move(i, clear_x, clear_y, nxf, nyf);
+ if (s || bmap[ny/CELL][nx/CELL]!=WL_STREAM)
+ break;
+ }
+ }
+ }
+ else if (s==-1) {} // particle is out of bounds
+ else if ((clear_x!=x||clear_y!=y) && do_move(i, x, y, clear_xf, clear_yf)) {}
+ else parts[i].flags |= FLAG_STAGNANT;
+ parts[i].vx *= elements[t].Collision;
+ parts[i].vy *= elements[t].Collision;
+ }
+ else
+ {
+ // if interpolation was done, try moving to last clear position
+ if ((clear_x!=x||clear_y!=y) && do_move(i, x, y, clear_xf, clear_yf)) {}
+ else parts[i].flags |= FLAG_STAGNANT;
+ parts[i].vx *= elements[t].Collision;
+ parts[i].vy *= elements[t].Collision;
+ }
+ }
+ }
+ }
+movedone:
+ continue;
+ }
+}
+
+int Simulation::GetParticleType(std::string type)
+{
+ int i = -1;
+ char * txt = (char*)type.c_str();
+
+ // alternative names for some elements
+ if (strcasecmp(txt,"C4")==0) i = PT_PLEX;
+ else if (strcasecmp(txt,"C5")==0) i = PT_C5;
+ else if (strcasecmp(txt,"NONE")==0) i = PT_NONE;
+ for (i=1; i<PT_NUM; i++) {
+ if (strcasecmp(txt, elements[i].Name)==0 && strlen(elements[i].Name) && elements[i].Enabled)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void Simulation::update_particles()//doesn't update the particles themselves, but some other things
+{
+ int i, j, x, y, t, nx, ny, r, cr,cg,cb, l = -1;
+ float lx, ly;
+ int lastPartUsed = 0;
+ int lastPartUnused = -1;
+#ifdef MT
+ int pt = 0, pc = 0;
+ pthread_t *InterThreads;
+#endif
+
+ if(!sys_pause||framerender)
+ {
+ air->update_air();
+
+ if(aheat_enable)
+ air->update_airh();
+
+ if(grav->ngrav_enable)
+ {
+ grav->gravity_update_async();
+
+ //Get updated buffer pointers for gravity
+ gravx = grav->gravx;
+ gravy = grav->gravy;
+ gravp = grav->gravp;
+ gravmap = grav->gravmap;
+
+ if(gravWallChanged)
+ {
+ grav->gravity_mask();
+ gravWallChanged = false;
+ }
+ }
+ if(emp_decor>0)
+ emp_decor -= emp_decor/25+2;
+ if(emp_decor < 0)
+ emp_decor = 0;
+ }
+ sandcolour = (int)(20.0f*sin((float)sandcolour_frame*(M_PI/180.0f)));
+ sandcolour_frame = (sandcolour_frame++)%360;
+
+ memset(pmap, 0, sizeof(pmap));
+ memset(pmap_count, 0, sizeof(pmap_count));
+ memset(photons, 0, sizeof(photons));
+ NUM_PARTS = 0;
+ for (i=0; i<=parts_lastActiveIndex; i++)//the particle loop that resets the pmap/photon maps every frame, to update them.
+ {
+ if (parts[i].type)
+ {
+ t = parts[i].type;
+ x = (int)(parts[i].x+0.5f);
+ y = (int)(parts[i].y+0.5f);
+ if (x>=0 && y>=0 && x<XRES && y<YRES)
+ {
+ if (elements[t].Properties & TYPE_ENERGY)
+ photons[y][x] = t|(i<<8);
+ else
+ {
+ // Particles are sometimes allowed to go inside INVS and FILT
+ // To make particles collide correctly when inside these elements, these elements must not overwrite an existing pmap entry from particles inside them
+ if (!pmap[y][x] || (t!=PT_INVIS && t!= PT_FILT))
+ pmap[y][x] = t|(i<<8);
+ // (there are a few exceptions, including energy particles - currently no limit on stacking those)
+ if (t!=PT_THDR && t!=PT_EMBR && t!=PT_FIGH && t!=PT_PLSM)
+ pmap_count[y][x]++;
+ }
+ }
+ lastPartUsed = i;
+ NUM_PARTS ++;
+ }
+ else
+ {
+ if (lastPartUnused<0) pfree = i;
+ else parts[lastPartUnused].life = i;
+ lastPartUnused = i;
+ }
+ }
+ if (lastPartUnused==-1)
+ {
+ if (parts_lastActiveIndex>=NPART-1) pfree = -1;
+ else pfree = parts_lastActiveIndex+1;
+ }
+ else
+ {
+ if (parts_lastActiveIndex>=NPART-1) parts[lastPartUnused].life = -1;
+ else parts[lastPartUnused].life = parts_lastActiveIndex+1;
+ }
+ parts_lastActiveIndex = lastPartUsed;
+ if (!sys_pause||framerender)
+ {
+ for (y=0; y<YRES/CELL; y++)
+ {
+ for (x=0; x<XRES/CELL; x++)
+ {
+ if (emap[y][x])
+ emap[y][x] --;
+ air->bmap_blockair[y][x] = (bmap[y][x]==WL_WALL || bmap[y][x]==WL_WALLELEC || (bmap[y][x]==WL_EWALL && !emap[y][x]));
+ air->bmap_blockairh[y][x] = (bmap[y][x]==WL_WALL || bmap[y][x]==WL_WALLELEC || bmap[y][x]==WL_GRAV || (bmap[y][x]==WL_EWALL && !emap[y][x]));
+ }
+ }
+ }
+
+ if(!sys_pause||framerender)
+ update_particles_i(0, 1);
+
+ if(framerender)
+ framerender--;
+ // this should probably be elsewhere
+ /*for (y=0; y<YRES/CELL; y++)
+ for (x=0; x<XRES/CELL; x++)
+ if (bmap[y][x]==WL_STREAM)
+ {
+ lx = x*CELL + CELL*0.5f;
+ ly = y*CELL + CELL*0.5f;
+ for (t=0; t<1024; t++)
+ {
+ nx = (int)(lx+0.5f);
+ ny = (int)(ly+0.5f);
+ if (nx<0 || nx>=XRES || ny<0 || ny>=YRES)
+ break;
+ addpixel(vid, nx, ny, 255, 255, 255, 64);
+ i = nx/CELL;
+ j = ny/CELL;
+ lx += vx[j][i]*0.125f;
+ ly += vy[j][i]*0.125f;
+ if (bmap[j][i]==WL_STREAM && i!=x && j!=y)
+ break;
+ }
+ drawtext(vid, x*CELL, y*CELL-2, "\x8D", 255, 255, 255, 128);
+ }
+*/
+}
+
+Simulation::~Simulation()
+{
+ delete[] platent;
+ delete grav;
+ delete air;
+ for(int i = 0; i < tools.size(); i++)
+ delete tools[i];
+}
+
+Simulation::Simulation():
+ sys_pause(0),
+ framerender(false),
+ pretty_powder(0),
+ sandcolour_frame(0)
+{
+
+ int tportal_rx[] = {-1, 0, 1, 1, 1, 0,-1,-1};
+ int tportal_ry[] = {-1,-1,-1, 0, 1, 1, 1, 0};
+
+ memcpy(portal_rx, tportal_rx, sizeof(tportal_rx));
+ memcpy(portal_ry, tportal_ry, sizeof(tportal_ry));
+
+ currentTick = 0;
+ std::fill(elementCount, elementCount+PT_NUM, 0);
+
+ //Create and attach gravity simulation
+ grav = new Gravity();
+ //Give air sim references to our data
+ grav->bmap = bmap;
+ //Gravity sim gives us maps to use
+ gravx = grav->gravx;
+ gravy = grav->gravy;
+ gravp = grav->gravp;
+ gravmap = grav->gravmap;
+
+ //Create and attach air simulation
+ air = new Air(*this);
+ //Give air sim references to our data
+ air->bmap = bmap;
+ air->emap = emap;
+ air->fvx = fvx;
+ air->fvy = fvy;
+ //Air sim gives us maps to use
+ vx = air->vx;
+ vy = air->vy;
+ pv = air->pv;
+ hv = air->hv;
+
+ int menuCount;
+ menu_section * msectionsT = LoadMenus(menuCount);
+ memcpy(msections, msectionsT, menuCount * sizeof(menu_section));
+ free(msectionsT);
+
+ int wallCount;
+ wall_type * wtypesT = LoadWalls(wallCount);
+ memcpy(wtypes, wtypesT, wallCount * sizeof(wall_type));
+ free(wtypesT);
+
+ platent = new unsigned[PT_NUM];
+ int latentCount;
+ unsigned int * platentT = LoadLatent(latentCount);
+ memcpy(platent, platentT, latentCount * sizeof(unsigned int));
+ free(platentT);
+
+ //elements = new Element[PT_NUM];
+ std::vector<Element> elementList = GetElements();
+ for(int i = 0; i < PT_NUM; i++)
+ {
+ if(i < elementList.size())
+ elements[i] = elementList[i];
+ else
+ elements[i] = Element();
+ }
+
+ tools = GetTools();
+
+ int golRulesCount;
+ int * golRulesT = LoadGOLRules(golRulesCount);
+ memcpy(grule, golRulesT, sizeof(int) * (golRulesCount*10));
+ free(golRulesT);
+
+ int golTypesCount;
+ int * golTypesT = LoadGOLTypes(golTypesCount);
+ memcpy(goltype, golTypesT, sizeof(int) * (golTypesCount));
+ free(golTypesT);
+
+ int golMenuCount;
+ gol_menu * golMenuT = LoadGOLMenu(golMenuCount);
+ memcpy(gmenu, golMenuT, sizeof(gol_menu) * golMenuCount);
+ free(golMenuT);
+
+ init_can_move();
+ clear_sim();
+
+ grav->gravity_mask();
+}
diff --git a/src/simulation/Simulation.h b/src/simulation/Simulation.h
new file mode 100644
index 0000000..9cdd3a7
--- /dev/null
+++ b/src/simulation/Simulation.h
@@ -0,0 +1,214 @@
+/*
+ * Simulation.h
+ *
+ * Created on: Jan 2, 2012
+ * Author: Simon
+ */
+
+#ifndef SIMULATION_H_
+#define SIMULATION_H_
+#include <cstring>
+#include <cstddef>
+#include <vector>
+
+#include "Config.h"
+#include "Elements.h"
+#include "SimulationData.h"
+#include "Sign.h"
+#include "Particle.h"
+#include "Player.h"
+#include "WallType.h"
+#include "GOLMenu.h"
+#include "MenuSection.h"
+#include "elements/Element.h"
+
+#define CHANNELS ((int)(MAX_TEMP-73)/100+2)
+
+class Snapshot;
+class SimTool;
+class Brush;
+struct SimulationSample;
+struct matrix2d;
+struct vector2d;
+
+class Simulation;
+class Renderer;
+class Gravity;
+class Air;
+class GameSave;
+
+//#ifdef _cplusplus
+class Simulation
+{
+private:
+public:
+
+ Gravity * grav;
+ Air * air;
+
+ std::vector<sign> signs;
+ Element elements[PT_NUM];
+ //Element * elements;
+ std::vector<SimTool*> tools;
+ unsigned int * platent;
+ wall_type wtypes[UI_WALLCOUNT];
+ gol_menu gmenu[NGOL];
+ int goltype[NGOL];
+ int grule[NGOL+1][10];
+ menu_section msections[SC_TOTAL];
+
+ int currentTick;
+
+ playerst player;
+ playerst player2;
+ playerst fighters[256]; //255 is the maximum number of fighters
+ unsigned char fighcount; //Contains the number of fighters
+ int lighting_recreate;
+ bool gravWallChanged;
+ Particle portalp[CHANNELS][8][80];
+ Particle emptyparticle;
+ int portal_rx[8];
+ int portal_ry[8];
+ int wireless[CHANNELS][2];
+ char can_move[PT_NUM][PT_NUM];
+ int parts_lastActiveIndex;// = NPART-1;
+ int pfree;
+ int NUM_PARTS;
+ int elementCount[PT_NUM];
+ int ISWIRE;
+ int force_stacking_check;
+ int emp_decor;
+ //Gol sim
+ int CGOL;
+ int ISGOL;
+ int GSPEED;
+ unsigned char gol[YRES][XRES];
+ unsigned char gol2[YRES][XRES][NGOL+1];
+ //Air sim
+ float (*vx)[XRES/CELL];
+ float (*vy)[XRES/CELL];
+ float (*pv)[XRES/CELL];
+ float (*hv)[XRES/CELL];
+ //Gravity sim
+ float *gravx;//gravx[(YRES/CELL) * (XRES/CELL)];
+ float *gravy;//gravy[(YRES/CELL) * (XRES/CELL)];
+ float *gravp;//gravp[(YRES/CELL) * (XRES/CELL)];
+ float *gravmap;//gravmap[(YRES/CELL) * (XRES/CELL)];
+ //Walls
+ unsigned char bmap[YRES/CELL][XRES/CELL];
+ unsigned char emap[YRES/CELL][XRES/CELL];
+ float fvx[YRES/CELL][XRES/CELL];
+ float fvy[YRES/CELL][XRES/CELL];
+ //Particles
+ Particle parts[NPART];
+ int pmap[YRES][XRES];
+ int photons[YRES][XRES];
+ int pmap_count[YRES][XRES];
+ //
+ int edgeMode;
+ int gravityMode;
+ //int airMode;
+ int legacy_enable;
+ int aheat_enable;
+ int VINE_MODE;
+ int water_equal_test;
+ int sys_pause;
+ int framerender;
+ int pretty_powder;
+ int sandcolour;
+ int sandcolour_frame;
+ bool ISLOVE;
+ int love[XRES/9][YRES/9];
+ bool ISLOLZ;
+ int lolz[XRES/9][YRES/9];
+
+ int Load(GameSave * save);
+ int Load(int x, int y, GameSave * save);
+ GameSave * Save();
+ GameSave * Save(int x1, int y1, int x2, int y2);
+ SimulationSample Get(int x, int y);
+
+ Snapshot * CreateSnapshot();
+ void Restore(const Snapshot & snap);
+
+ TPT_NO_INLINE int is_blocking(int t, int x, int y);
+ TPT_NO_INLINE int is_boundary(int pt, int x, int y);
+ TPT_NO_INLINE int find_next_boundary(int pt, int *x, int *y, int dm, int *em);
+ TPT_NO_INLINE int pn_junction_sprk(int x, int y, int pt);
+ TPT_NO_INLINE void photoelectric_effect(int nx, int ny);
+ TPT_NO_INLINE unsigned direction_to_map(float dx, float dy, int t);
+ TPT_NO_INLINE int do_move(int i, int x, int y, float nxf, float nyf);
+ TPT_NO_INLINE int try_move(int i, int x, int y, int nx, int ny);
+ TPT_NO_INLINE int eval_move(int pt, int nx, int ny, unsigned *rr);
+ void init_can_move();
+ void create_cherenkov_photon(int pp);
+ void create_gain_photon(int pp);
+ TPT_NO_INLINE void kill_part(int i);
+ int flood_prop(int x, int y, size_t propoffset, void * propvalue, StructProperty::PropertyType proptype);
+ int flood_prop_2(int x, int y, size_t propoffset, void * propvalue, StructProperty::PropertyType proptype, int parttype, char * bitmap);
+ int flood_water(int x, int y, int i, int originaly, int check);
+ TPT_NO_INLINE void detach(int i);
+ TPT_NO_INLINE void part_change_type(int i, int x, int y, int t);
+ TPT_NO_INLINE int create_part_add_props(int p, int x, int y, int tv, int rx, int ry);
+ //int InCurrentBrush(int i, int j, int rx, int ry);
+ //int get_brush_flags();
+ TPT_NO_INLINE int create_part(int p, int x, int y, int t);
+ TPT_NO_INLINE void delete_part(int x, int y, int flags);
+ void get_sign_pos(int i, int *x0, int *y0, int *w, int *h);
+ TPT_NO_INLINE int is_wire(int x, int y);
+ TPT_NO_INLINE int is_wire_off(int x, int y);
+ TPT_NO_INLINE void set_emap(int x, int y);
+ TPT_NO_INLINE int parts_avg(int ci, int ni, int t);
+ void create_arc(int sx, int sy, int dx, int dy, int midpoints, int variance, int type, int flags);
+ int nearest_part(int ci, int t, int max_d);
+ void update_particles_i(int start, int inc);
+ void update_particles();
+ void rotate_area(int area_x, int area_y, int area_w, int area_h, int invert);
+ void clear_area(int area_x, int area_y, int area_w, int area_h);
+
+ void SetEdgeMode(int newEdgeMode);
+
+ int Tool(int x, int y, int tool, float strength = 1.0f);
+ int ToolBrush(int x, int y, int tool, Brush * cBrush, float strength = 1.0f);
+ void ToolLine(int x1, int y1, int x2, int y2, int tool, Brush * cBrush, float strength = 1.0f);
+ void ToolBox(int x1, int y1, int x2, int y2, int tool, Brush * cBrush, float strength = 1.0f);
+
+ void CreateBox(int x1, int y1, int x2, int y2, int c, int flags);
+ int FloodINST(int x, int y, int fullc, int cm);
+ int FloodParts(int x, int y, int c, int cm, int bm, int flags);
+ //Create particles from brush/mask
+ int CreateParts(int positionX, int positionY, int c, Brush * cBrush);
+ //Old particle creation, will create a crappy square, do not use
+ int CreateParts(int x, int y, int rx, int ry, int c, int flags);
+ void CreateLine(int x1, int y1, int x2, int y2, int c, Brush * cBrush);
+ void CreateLine(int x1, int y1, int x2, int y2, int rx, int ry, int c, int flags);
+
+ void CreateWallBox(int x1, int y1, int x2, int y2, int c, int flags);
+ int FloodWalls(int x, int y, int c, int cm, int bm, int flags);
+ int CreateWalls(int x, int y, int rx, int ry, int c, int flags, Brush * cBrush = NULL);
+ void CreateWallLine(int x1, int y1, int x2, int y2, int rx, int ry, int c, int flags, Brush * cBrush = NULL);
+
+ void ApplyDecoration(int x, int y, int colR, int colG, int colB, int colA, int mode);
+ void ApplyDecorationPoint(int x, int y, int colR, int colG, int colB, int colA, int mode, Brush * cBrush = NULL);
+ void ApplyDecorationLine(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode, Brush * cBrush = NULL);
+ void ApplyDecorationBox(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode);
+
+ void GetGravityField(int x, int y, float particleGrav, float newtonGrav, float & pGravX, float & pGravY);
+
+ int GetParticleType(std::string type);
+
+ void *transform_save(void *odata, int *size, matrix2d transform, vector2d translate);
+ TPT_NO_INLINE void orbitalparts_get(int block1, int block2, int resblock1[], int resblock2[]);
+ TPT_NO_INLINE void orbitalparts_set(int *block1, int *block2, int resblock1[], int resblock2[]);
+ TPT_NO_INLINE int get_wavelength_bin(int *wm);
+ TPT_NO_INLINE int get_normal(int pt, int x, int y, float dx, float dy, float *nx, float *ny);
+ TPT_NO_INLINE int get_normal_interp(int pt, float x0, float y0, float dx, float dy, float *nx, float *ny);
+ void clear_sim();
+ void UpdateParticles();
+ Simulation();
+ ~Simulation();
+};
+
+//#endif
+
+#endif /* SIMULATION_H_ */
diff --git a/src/simulation/SimulationData.cpp b/src/simulation/SimulationData.cpp
new file mode 100644
index 0000000..e338e2f
--- /dev/null
+++ b/src/simulation/SimulationData.cpp
@@ -0,0 +1,344 @@
+#/*
+ * SimulationData.cpp
+ *
+ * Created on: Jan 24, 2012
+ * Author: Simon
+ */
+#include "SimulationData.h"
+//#include "ElementFunctions.h"
+#include "ElementGraphics.h"
+#include "Elements.h"
+
+gol_menu * LoadGOLMenu(int & golMenuCount)
+{
+ gol_menu golMenu[NGOL] =
+ {
+ {"GOL", PIXPACK(0x0CAC00), 0, "Game Of Life: Begin 3/Stay 23"},
+ {"HLIF", PIXPACK(0xFF0000), 1, "High Life: B36/S23"},
+ {"ASIM", PIXPACK(0x0000FF), 2, "Assimilation: B345/S4567"},
+ {"2x2", PIXPACK(0xFFFF00), 3, "2x2: B36/S125"},
+ {"DANI", PIXPACK(0x00FFFF), 4, "Day and Night: B3678/S34678"},
+ {"AMOE", PIXPACK(0xFF00FF), 5, "Amoeba: B357/S1358"},
+ {"MOVE", PIXPACK(0xFFFFFF), 6, "'Move' particles. Does not move things.. it is a life type: B368/S245"},
+ {"PGOL", PIXPACK(0xE05010), 7, "Pseudo Life: B357/S238"},
+ {"DMOE", PIXPACK(0x500000), 8, "Diamoeba: B35678/S5678"},
+ {"34", PIXPACK(0x500050), 9, "34: B34/S34"},
+ {"LLIF", PIXPACK(0x505050), 10, "Long Life: B345/S5"},
+ {"STAN", PIXPACK(0x5000FF), 11, "Stains: B3678/S235678"},
+ {"SEED", PIXPACK(0xFBEC7D), 12, "Seeds: B2/S"},
+ {"MAZE", PIXPACK(0xA8E4A0), 13, "Maze: B3/S12345"},
+ {"COAG", PIXPACK(0x9ACD32), 14, "Coagulations: B378/S235678"},
+ {"WALL", PIXPACK(0x0047AB), 15, "Walled cities: B45678/S2345"},
+ {"GNAR", PIXPACK(0xE5B73B), 16, "Gnarl: B1/S1"},
+ {"REPL", PIXPACK(0x259588), 17, "Replicator: B1357/S1357"},
+ {"MYST", PIXPACK(0x0C3C00), 18, "Mystery: B3458/S05678"},
+ {"LOTE", PIXPACK(0xFF0000), 19, "Living on the Edge: B37/S3458/4"},
+ {"FRG2", PIXPACK(0x00FF00), 20, "Like Frogs rule: B3/S124/3"},
+ {"STAR", PIXPACK(0x0000FF), 21, "Like Star Wars rule: B278/S3456/6"},
+ {"FROG", PIXPACK(0x00AA00), 22, "Frogs: B34/S12/3"},
+ {"BRAN", PIXPACK(0xCCCC00), 23, "Brian 6: B246/S6/3"}
+ };
+ golMenuCount = NGOL;
+ gol_menu * golMenuT = (gol_menu*)malloc(NGOL*sizeof(gol_menu));
+ memcpy(golMenuT, golMenu, NGOL*sizeof(gol_menu));
+ return golMenuT;
+}
+
+int * LoadGOLRules(int & golRuleCount)
+{
+ int golRules[NGOL+1][10] =
+ {
+ // 0,1,2,3,4,5,6,7,8,STATES live=1 spawn=2 spawn&live=3 States are kind of how long until it dies, normal ones use two states(living,dead) for others the intermediate states live but do nothing
+ {0,0,0,0,0,0,0,0,0,2},//blank
+ {0,0,1,3,0,0,0,0,0,2},//GOL
+ {0,0,1,3,0,0,2,0,0,2},//HLIF
+ {0,0,0,2,3,3,1,1,0,2},//ASIM
+ {0,1,1,2,0,1,2,0,0,2},//2x2
+ {0,0,0,3,1,0,3,3,3,2},//DANI
+ {0,1,0,3,0,3,0,2,1,2},//AMOE
+ {0,0,1,2,1,1,2,0,2,2},//MOVE
+ {0,0,1,3,0,2,0,2,1,2},//PGOL
+ {0,0,0,2,0,3,3,3,3,2},//DMOE
+ {0,0,0,3,3,0,0,0,0,2},//34
+ {0,0,0,2,2,3,0,0,0,2},//LLIF
+ {0,0,1,3,0,1,3,3,3,2},//STAN
+ {0,0,2,0,0,0,0,0,0,2},//SEED
+ {0,1,1,3,1,1,0,0,0,2},//MAZE
+ {0,0,1,3,0,1,1,3,3,2},//COAG
+ {0,0,1,1,3,3,2,2,2,2},//WALL
+ {0,3,0,0,0,0,0,0,0,2},//GNAR
+ {0,3,0,3,0,3,0,3,0,2},//REPL
+ {1,0,0,2,2,3,1,1,3,2},//MYST
+ {0,0,0,3,1,1,0,2,1,4},//LOTE
+ {0,1,1,2,1,0,0,0,0,3},//FRG2
+ {0,0,2,1,1,1,1,2,2,6},//STAR
+ {0,1,1,2,2,0,0,0,0,3},//FROG
+ {0,0,2,0,2,0,3,0,0,3},//BRAN
+ };
+ golRuleCount = NGOL+1;
+ int * golRulesT = (int*)malloc((golRuleCount*10)*sizeof(int));
+ memcpy(golRulesT, golRules, (golRuleCount*10)*sizeof(int));
+ return golRulesT;
+}
+
+int * LoadGOLTypes(int & golTypeCount)
+{
+ int golTypes[NGOL] =
+ {
+ GT_GOL,
+ GT_HLIF,
+ GT_ASIM,
+ GT_2x2,
+ GT_DANI,
+ GT_AMOE,
+ GT_MOVE,
+ GT_PGOL,
+ GT_DMOE,
+ GT_34,
+ GT_LLIF,
+ GT_STAN,
+ GT_SEED,
+ GT_MAZE,
+ GT_COAG,
+ GT_WALL,
+ GT_GNAR,
+ GT_REPL,
+ GT_MYST,
+ GT_LOTE,
+ GT_FRG2,
+ GT_STAR,
+ GT_FROG,
+ GT_BRAN,
+ };
+ golTypeCount = NGOL;
+ int * golTypesT = (int*)malloc((golTypeCount)*sizeof(int));
+ memcpy(golTypesT, golTypes, (golTypeCount)*sizeof(int));
+ return golTypesT;
+}
+
+wall_type * LoadWalls(int & wallCount)
+{
+ wall_type wtypes[] =
+ {
+ {PIXPACK(0x808080), PIXPACK(0x000000), 0, Renderer::WallIcon, "ERASE", "Erases walls."},
+ {PIXPACK(0xC0C0C0), PIXPACK(0x101010), 0, Renderer::WallIcon, "CONDUCTIVE WALL","Wall. Indestructible. Blocks everything. Conductive."},
+ {PIXPACK(0x808080), PIXPACK(0x808080), 0, Renderer::WallIcon, "EWALL", "E-Wall. Becomes transparent when electricity is connected."},
+ {PIXPACK(0xFF8080), PIXPACK(0xFF2008), 1, Renderer::WallIcon, "DETECTOR", "Detector. Generates electricity when a particle is inside."},
+ {PIXPACK(0x808080), PIXPACK(0x000000), 0, Renderer::WallIcon, "STREAMLINE", "Streamline. Set start point of a streamline."},
+ {PIXPACK(0x8080FF), PIXPACK(0x000000), 1, Renderer::WallIcon, "FAN", "Fan. Accelerates air. Use line tool to set direction and strength."},
+ {PIXPACK(0xC0C0C0), PIXPACK(0x101010), 2, Renderer::WallIcon, "LIQUID WALL", "Wall. Blocks most particles but lets liquids through. Conductive."},
+ {PIXPACK(0x808080), PIXPACK(0x000000), 1, Renderer::WallIcon, "ABSORB WALL", "Wall. Absorbs particles but lets air currents through."},
+ {PIXPACK(0x808080), PIXPACK(0x000000), 3, Renderer::WallIcon, "WALL", "Wall. Indestructible. Blocks everything."},
+ {PIXPACK(0x3C3C3C), PIXPACK(0x000000), 1, Renderer::WallIcon, "AIRONLY WALL", "Wall. Indestructible. Blocks particles, allows air"},
+ {PIXPACK(0x575757), PIXPACK(0x000000), 1, Renderer::WallIcon, "POWDER WALL", "Wall. Indestructible. Blocks liquids and gasses, allows powders"},
+ {PIXPACK(0xFFFF22), PIXPACK(0x101010), 2, Renderer::WallIcon, "CONDUCTOR", "Conductor, allows particles, conducts electricity"},
+ {PIXPACK(0x242424), PIXPACK(0x101010), 0, Renderer::WallIcon, "EHOLE", "E-Hole, absorbs particles, release them when powered"},
+ {PIXPACK(0x579777), PIXPACK(0x000000), 1, Renderer::WallIcon, "GAS WALL", "Wall. Indestructible. Blocks liquids and solids, allows gasses"},
+ {PIXPACK(0xFFEE00), PIXPACK(0xAA9900), 4, Renderer::WallIcon, "GRAVITY WALL", "Gravity wall"},
+ {PIXPACK(0xFFAA00), PIXPACK(0xAA5500), 4, Renderer::WallIcon, "ENERGY WALL", "Energy wall, allows only energy type particles to pass"},
+ };
+ wallCount = UI_WALLCOUNT;
+ wall_type * wtypesT = (wall_type*)malloc(UI_WALLCOUNT*sizeof(wall_type));
+ memcpy(wtypesT, wtypes, UI_WALLCOUNT*sizeof(wall_type));
+ return wtypesT;
+}
+
+menu_section * LoadMenus(int & menuCount)
+{
+ menu_section msections[] = //doshow does not do anything currently.
+ {
+ {"\xC1", "Walls", 0, 1},
+ {"\xC2", "Electronics", 0, 1},
+ {"\xD6", "Powered Materials", 0, 1},
+ {"\x99", "Sensors", 0, 1},
+ {"\xE2", "Force", 0, 1},
+ {"\xC3", "Explosives", 0, 1},
+ {"\xC5", "Gasses", 0, 1},
+ {"\xC4", "Liquids", 0, 1},
+ {"\xD0", "Powders", 0, 1},
+ {"\xD1", "Solids", 0, 1},
+ {"\xC6", "Radioactive", 0, 1},
+ {"\xCC", "Special", 0, 1},
+ {"\xD2", "Game Of Life", 0, 1},
+ {"\xD7", "Tools", 0, 1},
+ {"\xE4", "Decoration tools", 0, 1},
+ {"\xC8", "Cracker", 0, 0},
+ {"\xC8", "Cracker!", 0, 0},
+ };
+ menuCount = SC_TOTAL;
+ menu_section * msectionsT = (menu_section*)malloc(SC_TOTAL*sizeof(menu_section));
+ memcpy(msectionsT, msections, SC_TOTAL*sizeof(menu_section));
+ return msectionsT;
+}
+
+unsigned int * LoadLatent(int & elementCount)
+{
+ unsigned int platent[PT_NUM] =
+ {
+ /* NONE */ 0,
+ /* DUST */ 0,
+ /* WATR */ 7500,
+ /* OIL */ 0,
+ /* FIRE */ 0,
+ /* STNE */ 0,
+ /* LAVA */ 0,
+ /* GUN */ 0,
+ /* NITR */ 0,
+ /* CLNE */ 0,
+ /* GAS */ 0,
+ /* C-4 */ 0,
+ /* GOO */ 0,
+ /* ICE */ 1095,
+ /* METL */ 919,
+ /* SPRK */ 0,
+ /* SNOW */ 1095,
+ /* WOOD */ 0,
+ /* NEUT */ 0,
+ /* PLUT */ 0,
+ /* PLNT */ 0,
+ /* ACID */ 0,
+ /* VOID */ 0,
+ /* WTRV */ 0,
+ /* CNCT */ 0,
+ /* DSTW */ 7500,
+ /* SALT */ 0,
+ /* SLTW */ 7500,
+ /* DMND */ 0,
+ /* BMTL */ 0,
+ /* BRMT */ 0,
+ /* PHOT */ 0,
+ /* URAN */ 0,
+ /* WAX */ 0,
+ /* MWAX */ 0,
+ /* PSCN */ 0,
+ /* NSCN */ 0,
+ /* LN2 */ 0,
+ /* INSL */ 0,
+ /* VACU */ 0,
+ /* VENT */ 0,
+ /* RBDM */ 0,
+ /* LRBD */ 0,
+ /* NTCT */ 0,
+ /* SAND */ 0,
+ /* GLAS */ 0,
+ /* PTCT */ 0,
+ /* BGLA */ 0,
+ /* THDR */ 0,
+ /* PLSM */ 0,
+ /* ETRD */ 0,
+ /* NICE */ 0,
+ /* NBLE */ 0,
+ /* BTRY */ 0,
+ /* LCRY */ 0,
+ /* STKM */ 0,
+ /* SWCH */ 0,
+ /* SMKE */ 0,
+ /* DESL */ 0,
+ /* COAL */ 0,
+ /* LO2 */ 0,
+ /* O2 */ 0,
+ /* INWR */ 0,
+ /* YEST */ 0,
+ /* DYST */ 0,
+ /* THRM */ 0,
+ /* GLOW */ 0,
+ /* BRCK */ 0,
+ /* CFLM */ 0,
+ /* FIRW */ 0,
+ /* FUSE */ 0,
+ /* FSEP */ 0,
+ /* AMTR */ 0,
+ /* BCOL */ 0,
+ /* PCLN */ 0,
+ /* HSWC */ 0,
+ /* IRON */ 0,
+ /* MORT */ 0,
+ /* LIFE */ 0,
+ /* DLAY */ 0,
+ /* CO2 */ 0,
+ /* DRIC */ 0,
+ /* CBNW */ 7500,
+ /* STOR */ 0,
+ /* STOR */ 0,
+ /* FREE */ 0,
+ /* FREE */ 0,
+ /* FREE */ 0,
+ /* FREE */ 0,
+ /* FREE */ 0,
+ /* SPNG */ 0,
+ /* RIME */ 0,
+ /* FOG */ 0,
+ /* BCLN */ 0,
+ /* LOVE */ 0,
+ /* DEUT */ 0,
+ /* WARP */ 0,
+ /* PUMP */ 0,
+ /* FWRK */ 0,
+ /* PIPE */ 0,
+ /* FRZZ */ 0,
+ /* FRZW */ 0,
+ /* GRAV */ 0,
+ /* BIZR */ 0,
+ /* BIZRG*/ 0,
+ /* BIZRS*/ 0,
+ /* INST */ 0,
+ /* ISOZ */ 0,
+ /* ISZS */ 0,
+ /* PRTI */ 0,
+ /* PRTO */ 0,
+ /* PSTE */ 0,
+ /* PSTS */ 0,
+ /* ANAR */ 0,
+ /* VINE */ 0,
+ /* INVS */ 0,
+ /* EQVE */ 0,
+ /* SPWN2*/ 0,
+ /* SPAWN*/ 0,
+ /* SHLD1*/ 0,
+ /* SHLD2*/ 0,
+ /* SHLD3*/ 0,
+ /* SHLD4*/ 0,
+ /* LOlZ */ 0,
+ /* WIFI */ 0,
+ /* FILT */ 0,
+ /* ARAY */ 0,
+ /* BRAY */ 0,
+ /* STKM2*/ 0,
+ /* BOMB */ 0,
+ /* C-5 */ 0,
+ /* SING */ 0,
+ /* QRTZ */ 0,
+ /* PQRT */ 0,
+ /* EMP */ 0,
+ /* BREL */ 0,
+ /* ELEC */ 0,
+ /* ACEL */ 0,
+ /* DCEL */ 0,
+ /* TNT */ 0,
+ /* IGNP */ 0,
+ /* BOYL */ 0,
+ /* GEL */ 0,
+ /* FREE */ 0,
+ /* FREE */ 0,
+ /* FREE */ 0,
+ /* FREE */ 0,
+ /* WIND */ 0,
+ /* H2 */ 0,
+ /* SOAP */ 0,
+ /* NBHL */ 0,
+ /* NWHL */ 0,
+ /* MERC */ 0,
+ /* PBCN */ 0,
+ /* GPMP */ 0,
+ /* CLST */ 0,
+ /* WIRE */ 0,
+ /* GBMB */ 0,
+ /* FIGH */ 0,
+ /* FRAY */ 0,
+ /* REPL */ 0,
+ };
+ elementCount = PT_NUM;
+ unsigned int * platentT = (unsigned int*)malloc(PT_NUM*sizeof(unsigned int));
+ memcpy(platentT, platent, PT_NUM*sizeof(unsigned int));
+ return platentT;
+}
diff --git a/src/simulation/SimulationData.h b/src/simulation/SimulationData.h
new file mode 100644
index 0000000..1d47813
--- /dev/null
+++ b/src/simulation/SimulationData.h
@@ -0,0 +1,178 @@
+/*
+ * SimulationData.h
+ *
+ * Created on: Jan 24, 2012
+ * Author: Simon
+ */
+
+#include <vector>
+
+#define SC_WALL 0
+#define SC_ELEC 1
+#define SC_POWERED 2
+#define SC_SENSOR 3
+#define SC_FORCE 4
+#define SC_EXPLOSIVE 5
+#define SC_GAS 6
+#define SC_LIQUID 7
+#define SC_POWDERS 8
+#define SC_SOLIDS 9
+#define SC_NUCLEAR 10
+#define SC_SPECIAL 11
+#define SC_LIFE 12
+#define SC_TOOL 13
+#define SC_DECO 14
+#define SC_CRACKER 15
+#define SC_CRACKER2 16
+#define SC_TOTAL 15
+
+#define UI_WALLSTART 222
+#define UI_ACTUALSTART 122
+#define UI_WALLCOUNT 16
+
+#define O_WL_WALLELEC 122
+#define O_WL_EWALL 123
+#define O_WL_DETECT 124
+#define O_WL_STREAM 125
+#define O_WL_SIGN 126
+#define O_WL_FAN 127
+#define O_WL_FANHELPER 255
+#define O_WL_ALLOWLIQUID 128
+#define O_WL_DESTROYALL 129
+#define O_WL_ERASE 130
+#define O_WL_WALL 131
+#define O_WL_ALLOWAIR 132
+#define O_WL_ALLOWSOLID 133
+#define O_WL_ALLOWALLELEC 134
+#define O_WL_EHOLE 135
+#define O_WL_ALLOWGAS 140
+#define O_WL_GRAV 142
+#define O_WL_ALLOWENERGY 145
+
+
+#define WL_ERASE 0
+#define WL_WALLELEC 1
+#define WL_EWALL 2
+#define WL_DETECT 3
+#define WL_STREAM 4
+#define WL_FAN 5
+#define WL_ALLOWLIQUID 6
+#define WL_DESTROYALL 7
+#define WL_WALL 8
+#define WL_ALLOWAIR 9
+#define WL_ALLOWSOLID 10
+#define WL_ALLOWALLELEC 11
+#define WL_EHOLE 12
+#define WL_ALLOWGAS 13
+#define WL_GRAV 14
+#define WL_ALLOWENERGY 15
+#define WL_FLOODHELPER 255
+
+#define SPC_AIR 236
+#define SPC_HEAT 237
+#define SPC_COOL 238
+#define SPC_VACUUM 239
+#define SPC_WIND 241
+#define SPC_PGRV 243
+#define SPC_NGRV 244
+#define SPC_PROP 246
+
+#define DECO_DRAW 0
+#define DECO_ADD 1
+#define DECO_SUBTRACT 2
+#define DECO_MULTIPLY 3
+#define DECO_DIVIDE 4
+#define DECO_SMUDGE 5
+#define DECO_CLEAR 6
+
+//Old IDs for GOL types
+#define GT_GOL 78
+#define GT_HLIF 79
+#define GT_ASIM 80
+#define GT_2x2 81
+#define GT_DANI 82
+#define GT_AMOE 83
+#define GT_MOVE 84
+#define GT_PGOL 85
+#define GT_DMOE 86
+#define GT_34 87
+#define GT_LLIF 88
+#define GT_STAN 89
+#define GT_SEED 134
+#define GT_MAZE 135
+#define GT_COAG 136
+#define GT_WALL 137
+#define GT_GNAR 138
+#define GT_REPL 139
+#define GT_MYST 140
+#define GT_LOTE 142
+#define GT_FRG2 143
+#define GT_STAR 144
+#define GT_FROG 145
+#define GT_BRAN 146
+
+//New IDs for GOL types
+#define NGT_GOL 0
+#define NGT_HLIF 1
+#define NGT_ASIM 2
+#define NGT_2x2 3
+#define NGT_DANI 4
+#define NGT_AMOE 5
+#define NGT_MOVE 6
+#define NGT_PGOL 7
+#define NGT_DMOE 8
+#define NGT_34 9
+#define NGT_LLIF 10
+#define NGT_STAN 11
+#define NGT_SEED 12
+#define NGT_MAZE 13
+#define NGT_COAG 14
+#define NGT_WALL 15
+#define NGT_GNAR 16
+#define NGT_REPL 17
+#define NGT_MYST 18
+#define NGT_LOTE 19
+#define NGT_FRG2 20
+#define NGT_STAR 21
+#define NGT_FROG 22
+#define NGT_BRAN 23
+
+#ifndef SIMULATIONDATA_H_
+#define SIMULATIONDATA_H_
+
+//#include "elements/NULLElement.h"
+//#include "Simulation.h"
+
+/*class Simulation;
+class Renderer;
+struct Particle;*/
+
+struct part_type;
+
+struct part_transition;
+
+struct wall_type;
+
+struct gol_menu;
+
+struct menu_section;
+
+struct wall_type;
+
+class SimTool;
+
+class Element;
+
+gol_menu * LoadGOLMenu(int & golMenuCount);
+
+int * LoadGOLTypes(int & golTypeCount);
+
+int * LoadGOLRules(int & golRuleCount);
+
+wall_type * LoadWalls(int & wallCount);
+
+menu_section * LoadMenus(int & menuCount);
+
+unsigned int * LoadLatent(int & elementCount);
+
+#endif /* SIMULATIONDATA_H_ */
diff --git a/src/simulation/Snapshot.h b/src/simulation/Snapshot.h
new file mode 100644
index 0000000..d47f363
--- /dev/null
+++ b/src/simulation/Snapshot.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <vector>
+
+#include "Particle.h"
+
+class Snapshot
+{
+public:
+ std::vector<float> AirPressure;
+ std::vector<float> AirVelocityX;
+ std::vector<float> AirVelocityY;
+ std::vector<float> AmbientHeat;
+
+ std::vector<Particle> Particles;
+ std::vector<Particle> PortalParticles;
+
+ std::vector<int> WirelessData;
+
+ std::vector<float> GravVelocityX;
+ std::vector<float> GravVelocityY;
+ std::vector<float> GravValue;
+ std::vector<float> GravMap;
+
+ std::vector<unsigned char> BlockMap;
+ std::vector<unsigned char> ElecMap;
+
+ std::vector<float> FanVelocityX;
+ std::vector<float> FanVelocityY;
+
+ Snapshot() :
+ AirPressure(),
+ AirVelocityX(),
+ AirVelocityY(),
+ AmbientHeat(),
+ Particles(),
+ PortalParticles(),
+ WirelessData(),
+ GravVelocityX(),
+ GravVelocityY(),
+ GravValue(),
+ GravMap(),
+ BlockMap(),
+ ElecMap(),
+ FanVelocityX(),
+ FanVelocityY()
+ {
+
+ }
+
+ virtual ~Snapshot()
+ {
+
+ }
+}; \ No newline at end of file
diff --git a/src/simulation/StorageClasses.h b/src/simulation/StorageClasses.h
new file mode 100644
index 0000000..45b9b00
--- /dev/null
+++ b/src/simulation/StorageClasses.h
@@ -0,0 +1,54 @@
+#ifndef STORAGECLASSES_H_
+#define STORAGECLASSES_H_
+
+#include <string>
+#include "Elements.h"
+
+class Renderer;
+class Simulation;
+
+/*struct part_type
+{
+ char *name;
+ pixel pcolors;
+ float advection;
+ float airdrag;
+ float airloss;
+ float loss;
+ float collision;
+ float gravity;
+ float diffusion;
+ float hotair;
+ int falldown;
+ int flammable;
+ int explosive;
+ int meltable;
+ int hardness;
+ int menu;
+ int enabled;
+ int weight;
+ int menusection;
+ float heat;
+ unsigned char hconduct;
+ char *descs;
+ char state;
+ unsigned int properties;
+ int (*update_func) (UPDATE_FUNC_ARGS);
+ int (*graphics_func) (GRAPHICS_FUNC_ARGS);
+};
+typedef struct part_type part_type;*/
+
+/*struct part_transition
+{
+ float plv; // transition occurs if pv is lower than this
+ int plt;
+ float phv; // transition occurs if pv is higher than this
+ int pht;
+ float tlv; // transition occurs if t is lower than this
+ int tlt;
+ float thv; // transition occurs if t is higher than this
+ int tht;
+};
+typedef struct part_transition part_transition;*/
+
+#endif \ No newline at end of file
diff --git a/src/simulation/StructProperty.h b/src/simulation/StructProperty.h
new file mode 100644
index 0000000..5c46c7e
--- /dev/null
+++ b/src/simulation/StructProperty.h
@@ -0,0 +1,39 @@
+//
+// StructProperty.h
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 04/06/2012.
+// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#ifndef The_Powder_Toy_StructProperty_h
+#define The_Powder_Toy_StructProperty_h
+
+#include <string>
+#include <stdint.h>
+
+struct StructProperty
+{
+ enum PropertyType { ParticleType, Colour, Integer, UInteger, Float, String, Char, UChar };
+ std::string Name;
+ PropertyType Type;
+ intptr_t Offset;
+
+ StructProperty(std::string name, PropertyType type, intptr_t offset):
+ Name(name),
+ Type(type),
+ Offset(offset)
+ {
+
+ }
+
+ StructProperty():
+ Name(""),
+ Type(Char),
+ Offset(0)
+ {
+
+ }
+};
+
+#endif
diff --git a/src/simulation/Tools.h b/src/simulation/Tools.h
new file mode 100644
index 0000000..525701e
--- /dev/null
+++ b/src/simulation/Tools.h
@@ -0,0 +1,7 @@
+#ifndef TOOLS_H_
+#define TOOLS_H_
+
+#include "ToolClasses.h"
+
+
+#endif
diff --git a/src/simulation/WallType.h b/src/simulation/WallType.h
new file mode 100644
index 0000000..c2d916a
--- /dev/null
+++ b/src/simulation/WallType.h
@@ -0,0 +1,25 @@
+//
+// WallType.h
+// The Powder Toy
+//
+// Created by Simon Robertshaw on 04/06/2012.
+// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#ifndef The_Powder_Toy_WallType_h
+#define The_Powder_Toy_WallType_h
+
+#include "graphics/Graphics.h"
+class VideoBuffer;
+
+struct wall_type
+{
+ pixel colour;
+ pixel eglow; // if emap set, add this to fire glow
+ int drawstyle;
+ VideoBuffer * (*textureGen)(int, int, int);
+ const char *name;
+ const char *descs;
+};
+
+#endif
diff --git a/src/simulation/elements/116.cpp b/src/simulation/elements/116.cpp
new file mode 100644
index 0000000..9697c97
--- /dev/null
+++ b/src/simulation/elements/116.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_116 PT_116 116
+Element_116::Element_116()
+{
+ Identifier = "DEFAULT_PT_116";
+ Name = "EQVE";
+ Colour = PIXPACK(0xFFE0A0);
+ MenuVisible = 0;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 85;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Shared velocity test";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+
+}
+
+Element_116::~Element_116() {} \ No newline at end of file
diff --git a/src/simulation/elements/146.cpp b/src/simulation/elements/146.cpp
new file mode 100644
index 0000000..51f4d3b
--- /dev/null
+++ b/src/simulation/elements/146.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_146 PT_146 146
+Element_146::Element_146()
+{
+ Identifier = "DEFAULT_PT_146";
+ Name = "BRAN";
+ Colour = PIXPACK(0xCCCC00);
+ MenuVisible = 0;
+ MenuSection = SC_LIFE;
+ Enabled = 0;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = 9000.0f;
+ HeatConduct = 40;
+ Description = "Brian 6 S6/B246/3";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID|PROP_LIFE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+
+}
+
+Element_146::~Element_146() {} \ No newline at end of file
diff --git a/src/simulation/elements/ACEL.cpp b/src/simulation/elements/ACEL.cpp
new file mode 100644
index 0000000..8dfad79
--- /dev/null
+++ b/src/simulation/elements/ACEL.cpp
@@ -0,0 +1,85 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_ACEL PT_ACEL 137
+Element_ACEL::Element_ACEL()
+{
+ Identifier = "DEFAULT_PT_ACEL";
+ Name = "ACEL";
+ Colour = PIXPACK(0x0099CC);
+ MenuVisible = 1;
+ MenuSection = SC_FORCE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Accelerator";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_ACEL::update;
+ Graphics = &Element_ACEL::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_ACEL static int update(UPDATE_FUNC_ARGS)
+int Element_ACEL::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ parts[i].tmp = 0;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry) && !(rx && ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if(!r)
+ r = sim->photons[y+ry][x+rx];
+ if ((r>>8)>=NPART || !r)
+ continue;
+ if(sim->elements[r&0xFF].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS | TYPE_ENERGY))
+ {
+ parts[r>>8].vx *= 1.1f;
+ parts[r>>8].vy *= 1.1f;
+ parts[i].tmp = 1;
+ }
+ }
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_ACEL static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_ACEL::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ if(cpart->tmp)
+ *pixel_mode |= PMODE_GLOW;
+ return 0;
+}
+
+
+Element_ACEL::~Element_ACEL() {} \ No newline at end of file
diff --git a/src/simulation/elements/ACID.cpp b/src/simulation/elements/ACID.cpp
new file mode 100644
index 0000000..fb6cb81
--- /dev/null
+++ b/src/simulation/elements/ACID.cpp
@@ -0,0 +1,144 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_ACID PT_ACID 21
+Element_ACID::Element_ACID()
+{
+ Identifier = "DEFAULT_PT_ACID";
+ Name = "ACID";
+ Colour = PIXPACK(0xED55FF);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 40;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 10;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 34;
+ Description = "Dissolves almost everything.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_DEADLY;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_ACID::update;
+ Graphics = &Element_ACID::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_ACID static int update(UPDATE_FUNC_ARGS)
+int Element_ACID::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, trade, np;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_ACID && (r&0xFF)!=PT_CAUS)
+ {
+ if ((r&0xFF)==PT_PLEX || (r&0xFF)==PT_NITR || (r&0xFF)==PT_GUNP || (r&0xFF)==PT_RBDM || (r&0xFF)==PT_LRBD)
+ {
+ sim->part_change_type(i,x,y,PT_FIRE);
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_FIRE);
+ parts[i].life = 4;
+ parts[r>>8].life = 4;
+ }
+ else if ((r&0xFF)==PT_WTRV)
+ {
+ if(!(rand()%250))
+ {
+ sim->part_change_type(i, x, y, PT_CAUS);
+ parts[i].life = (rand()%50)+25;
+ sim->kill_part(r>>8);
+ }
+ }
+ else if (((r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PCLN && sim->elements[r&0xFF].Hardness>(rand()%1000))&&parts[i].life>=50)
+ {
+ if (sim->parts_avg(i, r>>8,PT_GLAS)!= PT_GLAS)//GLAS protects stuff from acid
+ {
+ float newtemp = ((60.0f-(float)sim->elements[r&0xFF].Hardness))*7.0f;
+ if(newtemp < 0){
+ newtemp = 0;
+ }
+ parts[i].temp += newtemp;
+ parts[i].life--;
+ sim->kill_part(r>>8);
+ }
+ }
+ else if (parts[i].life<=50)
+ {
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ }
+ for ( trade = 0; trade<2; trade ++)
+ {
+ rx = rand()%5-2;
+ ry = rand()%5-2;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if ((r>>8)>=NPART || !r)
+ continue;
+ if ((r&0xFF)==PT_ACID&&(parts[i].life>parts[r>>8].life)&&parts[i].life>0)//diffusion
+ {
+ int temp = parts[i].life - parts[r>>8].life;
+ if (temp ==1)
+ {
+ parts[r>>8].life ++;
+ parts[i].life --;
+ }
+ else if (temp>0)
+ {
+ parts[r>>8].life += temp/2;
+ parts[i].life -= temp/2;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_ACID static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_ACID::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int s = cpart->life;
+ if (s>75) s = 75; //These two should not be here.
+ if (s<49) s = 49;
+ s = (s-49)*3;
+ if (s==0) s = 1;
+ *colr += s*4;
+ *colg += s*1;
+ *colb += s*2;
+ *pixel_mode |= PMODE_BLUR;
+ return 0;
+}
+
+
+Element_ACID::~Element_ACID() {} \ No newline at end of file
diff --git a/src/simulation/elements/AMTR.cpp b/src/simulation/elements/AMTR.cpp
new file mode 100644
index 0000000..ff25bb6
--- /dev/null
+++ b/src/simulation/elements/AMTR.cpp
@@ -0,0 +1,79 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_AMTR PT_AMTR 72
+Element_AMTR::Element_AMTR()
+{
+ Identifier = "DEFAULT_PT_AMTR";
+ Name = "AMTR";
+ Colour = PIXPACK(0x808080);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.80f;
+ Collision = 0.00f;
+ Gravity = 0.10f;
+ Diffusion = 1.00f;
+ HotAir = 0.0000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Anti-Matter, Destroys a majority of particles";
+
+ State = ST_NONE;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_AMTR::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_AMTR static int update(UPDATE_FUNC_ARGS)
+int Element_AMTR::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_AMTR && (r&0xFF)!=PT_DMND && (r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PCLN && (r&0xFF)!=PT_NONE && (r&0xFF)!=PT_PHOT && (r&0xFF)!=PT_VOID && (r&0xFF)!=PT_BHOL && (r&0xFF)!=PT_NBHL && (r&0xFF)!=PT_PRTI && (r&0xFF)!=PT_PRTO)
+ {
+ parts[i].life++;
+ if (parts[i].life==4)
+ {
+ sim->kill_part(i);
+ return 1;
+ }
+ if (10>(rand()/(RAND_MAX/100)))
+ sim->create_part(r>>8, x+rx, y+ry, PT_PHOT);
+ else
+ sim->kill_part(r>>8);
+ sim->pv[y/CELL][x/CELL] -= 2.0f;
+ }
+ }
+ return 0;
+}
+
+
+Element_AMTR::~Element_AMTR() {} \ No newline at end of file
diff --git a/src/simulation/elements/ANAR.cpp b/src/simulation/elements/ANAR.cpp
new file mode 100644
index 0000000..f8bcd49
--- /dev/null
+++ b/src/simulation/elements/ANAR.cpp
@@ -0,0 +1,78 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_ANAR PT_ANAR 113
+Element_ANAR::Element_ANAR()
+{
+ Identifier = "DEFAULT_PT_ANAR";
+ Name = "ANAR";
+ Colour = PIXPACK(0xFFFFEE);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = -0.7f;
+ AirDrag = -0.02f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.80f;
+ Collision = 0.1f;
+ Gravity = -0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 85;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Very light dust. Behaves opposite gravity";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_ANAR::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_ANAR static int update(UPDATE_FUNC_ARGS)
+int Element_ANAR::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+
+ //if (parts[i].temp >= 0.23)
+ // parts[i].temp --;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_HFLM)
+ {
+ if (1>rand()%22)
+ {
+ sim->part_change_type(i,x,y,PT_HFLM);
+ parts[i].life = rand()%150+50;
+ parts[r>>8].temp = parts[i].temp = 0;
+ sim->pv[y/CELL][x/CELL] -= 0.5;
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_ANAR::~Element_ANAR() {} \ No newline at end of file
diff --git a/src/simulation/elements/ARAY.cpp b/src/simulation/elements/ARAY.cpp
new file mode 100644
index 0000000..2794bef
--- /dev/null
+++ b/src/simulation/elements/ARAY.cpp
@@ -0,0 +1,154 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_ARAY PT_ARAY 126
+Element_ARAY::Element_ARAY()
+{
+ Identifier = "DEFAULT_PT_ARAY";
+ Name = "ARAY";
+ Colour = PIXPACK(0xFFBB00);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Ray Emitter. Rays create points when they collide";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_ARAY::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_ARAY static int update(UPDATE_FUNC_ARGS)
+int Element_ARAY::update(UPDATE_FUNC_ARGS)
+ {
+ int r, nxx, nyy, docontinue, nxi, nyi, rx, ry, nr, ry1, rx1;
+ if (parts[i].life==0) {
+ int colored =0;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_SPRK && parts[r>>8].life==3) {
+ int destroy = (parts[r>>8].ctype==PT_PSCN)?1:0;
+ int nostop = (parts[r>>8].ctype==PT_INST)?1:0;
+ for (docontinue = 1, nxx = 0, nyy = 0, nxi = rx*-1, nyi = ry*-1; docontinue; nyy+=nyi, nxx+=nxi) {
+ if (!(x+nxi+nxx<XRES && y+nyi+nyy<YRES && x+nxi+nxx >= 0 && y+nyi+nyy >= 0)) {
+ break;
+ }
+ r = pmap[y+nyi+nyy][x+nxi+nxx];
+ if (!r) {
+ int nr = sim->create_part(-1, x+nxi+nxx, y+nyi+nyy, PT_BRAY);
+ if (nr!=-1) {
+ if (destroy) {//if it came from PSCN
+ parts[nr].tmp = 2;
+ parts[nr].life = 2;
+ } else
+ parts[nr].ctype = colored;
+ parts[nr].temp = parts[i].temp;
+ }
+ } else if (!destroy) {
+ if ((r&0xFF)==PT_BRAY&&parts[r>>8].tmp==0) {//if it hits another BRAY that isn't red
+ if (nyy!=0 || nxx!=0) {
+ parts[r>>8].life = 1020;//makes it last a while
+ parts[r>>8].tmp = 1;
+ if (!parts[r>>8].ctype)//and colors it if it isn't already
+ parts[r>>8].ctype = colored;
+ }
+ docontinue = 0;//then stop it
+ } else if ((r&0xFF)==PT_BRAY&&parts[r>>8].tmp==1) {//if it hits one that already was a long life, reset it
+ parts[r>>8].life = 1020;
+ //docontinue = 1;
+ } else if ((r&0xFF)==PT_FILT) {//get color if passed through FILT
+ colored = parts[r>>8].ctype;
+ //this if prevents BRAY from stopping on certain materials
+ } else if ((r&0xFF)!=PT_STOR && (r&0xFF)!=PT_INWR && ((r&0xFF)!=PT_SPRK || parts[r>>8].ctype!=PT_INWR) && (r&0xFF)!=PT_ARAY && (r&0xFF)!=PT_WIFI && !((r&0xFF)==PT_SWCH && parts[r>>8].life>=10)) {
+ if (nyy!=0 || nxx!=0) {
+ sim->create_part(-1, x+nxi+nxx, y+nyi+nyy, PT_SPRK);
+ }
+ if (!(nostop && parts[r>>8].type==PT_SPRK && parts[r>>8].ctype >= 0 && parts[r>>8].ctype < PT_NUM && (sim->elements[parts[r>>8].ctype].Properties&PROP_CONDUCTS))) {
+ docontinue = 0;
+ } else {
+ docontinue = 1;
+ }
+ } else if((r&0xFF)==PT_STOR) {
+ if(parts[r>>8].tmp)
+ {
+ //Cause STOR to release
+ for(ry1 = 1; ry1 >= -1; ry1--){
+ for(rx1 = 0; rx1 >= -1 && rx1 <= 1; rx1 = -rx1-rx1+1){
+ int np = sim->create_part(-1, x+nxi+nxx+rx1, y+nyi+nyy+ry1, parts[r>>8].tmp);
+ if (np!=-1)
+ {
+ parts[np].temp = parts[r>>8].temp;
+ parts[np].life = parts[r>>8].tmp2;
+ parts[np].tmp = parts[r>>8].pavg[0];
+ parts[np].ctype = parts[r>>8].pavg[1];
+ parts[r>>8].tmp = 0;
+ parts[r>>8].life = 10;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ parts[r>>8].life = 10;
+ }
+ }
+ } else if (destroy) {
+ if ((r&0xFF)==PT_BRAY) {
+ parts[r>>8].life = 1;
+ docontinue = 1;
+ //this if prevents red BRAY from stopping on certain materials
+ } else if ((r&0xFF)==PT_STOR || (r&0xFF)==PT_INWR || ((r&0xFF)==PT_SPRK && parts[r>>8].ctype==PT_INWR) || (r&0xFF)==PT_ARAY || (r&0xFF)==PT_WIFI || (r&0xFF)==PT_FILT || ((r&0xFF)==PT_SWCH && parts[r>>8].life>=10)) {
+ if((r&0xFF)==PT_STOR)
+ {
+ parts[r>>8].tmp = 0;
+ parts[r>>8].life = 0;
+ }
+ docontinue = 1;
+ } else {
+ docontinue = 0;
+ }
+ }
+ }
+ }
+ //parts[i].life = 4;
+ }
+ }
+ return 0;
+}
+
+
+Element_ARAY::~Element_ARAY() {} \ No newline at end of file
diff --git a/src/simulation/elements/BANG.cpp b/src/simulation/elements/BANG.cpp
new file mode 100644
index 0000000..8922e0e
--- /dev/null
+++ b/src/simulation/elements/BANG.cpp
@@ -0,0 +1,131 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BANG PT_BANG 139
+Element_BANG::Element_BANG()
+{
+ Identifier = "DEFAULT_PT_BANG";
+ Name = "TNT";
+ Colour = PIXPACK(0xC05050);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 88;
+ Description = "Explosive.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID | PROP_NEUTPENETRATE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_BANG::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_BANG static int update(UPDATE_FUNC_ARGS)
+int Element_BANG::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, nb;
+ if(parts[i].tmp==0)
+ {
+ if(parts[i].temp>=673.0f)
+ parts[i].tmp = 1;
+ else
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_FIRE || (r&0xFF)==PT_PLSM)
+ {
+ parts[i].tmp = 1;
+ }
+ else if ((r&0xFF)==PT_SPRK || (r&0xFF)==PT_LIGH)
+ {
+ parts[i].tmp = 1;
+ }
+ }
+
+ }
+ else if(parts[i].tmp==1)
+ {
+ if ((pmap[y][x]>>8 == i))
+ {
+ int tempvalue = 2;
+ sim->flood_prop(x, y, offsetof(Particle, tmp), &tempvalue, StructProperty::Integer);
+ }
+ parts[i].tmp = 2;
+ }
+ else if(parts[i].tmp==2)
+ {
+ parts[i].tmp = 3;
+ }
+ else if(parts[i].tmp>=3)
+ {
+ float otemp = parts[i].temp-275.13f;
+ //Explode!!
+ sim->pv[y/CELL][x/CELL] += 0.5f;
+ parts[i].tmp = 0;
+ if(!(rand()%3))
+ {
+ if(!(rand()%2))
+ {
+ sim->create_part(i, x, y, PT_FIRE);
+ parts[i].temp = restrict_flt((MAX_TEMP/4)+otemp, MIN_TEMP, MAX_TEMP);
+ }
+ else
+ {
+ sim->create_part(i, x, y, PT_SMKE);
+ parts[i].life = rand()%50+500;
+ parts[i].temp = restrict_flt((MAX_TEMP/4)+otemp, MIN_TEMP, MAX_TEMP);
+ }
+ }
+ else
+ {
+ if(!(rand()%15))
+ {
+ sim->create_part(i, x, y, PT_EMBR);
+ parts[i].tmp = 0;
+ parts[i].life = 50;
+ parts[i].temp = restrict_flt((MAX_TEMP/3)+otemp, MIN_TEMP, MAX_TEMP);
+ parts[i].vx = rand()%20-10;
+ parts[i].vy = rand()%20-10;
+ }
+ else
+ {
+ sim->kill_part(i);
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+
+Element_BANG::~Element_BANG() {}
diff --git a/src/simulation/elements/BCLN.cpp b/src/simulation/elements/BCLN.cpp
new file mode 100644
index 0000000..80a5d44
--- /dev/null
+++ b/src/simulation/elements/BCLN.cpp
@@ -0,0 +1,99 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BCLN PT_BCLN 93
+Element_BCLN::Element_BCLN()
+{
+ Identifier = "DEFAULT_PT_BCLN";
+ Name = "BCLN";
+ Colour = PIXPACK(0xFFD040);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.97f;
+ Loss = 0.50f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 12;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Breakable Clone.";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_BCLN::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_BCLN static int update(UPDATE_FUNC_ARGS)
+int Element_BCLN::update(UPDATE_FUNC_ARGS)
+ {
+ if (!parts[i].life && sim->pv[y/CELL][x/CELL]>4.0f)
+ parts[i].life = rand()%40+80;
+ if (parts[i].life)
+ {
+ float advection = 0.1f;
+ parts[i].vx += advection*sim->vx[y/CELL][x/CELL];
+ parts[i].vy += advection*sim->vy[y/CELL][x/CELL];
+ }
+ if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled || (parts[i].ctype==PT_LIFE && (parts[i].tmp<0 || parts[i].tmp>=NGOLALT)))
+ {
+ int r, rx, ry;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES)
+ {
+ r = sim->photons[y+ry][x+rx];
+ if (!r)
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PCLN &&
+ (r&0xFF)!=PT_BCLN && (r&0xFF)!=PT_STKM &&
+ (r&0xFF)!=PT_PBCN && (r&0xFF)!=PT_STKM2 &&
+ (r&0xFF)<PT_NUM)
+ {
+ parts[i].ctype = r&0xFF;
+ if ((r&0xFF)==PT_LIFE || (r&0xFF)==PT_LAVA)
+ parts[i].tmp = parts[r>>8].ctype;
+ }
+ }
+ }
+ else {
+ if (parts[i].ctype==PT_LIFE) sim->create_part(-1, x+rand()%3-1, y+rand()%3-1, parts[i].ctype|(parts[i].tmp<<8));
+ else if (parts[i].ctype!=PT_LIGH || (rand()%30)==0)
+ {
+ int np = sim->create_part(-1, x+rand()%3-1, y+rand()%3-1, parts[i].ctype);
+ if (np>=0)
+ {
+ if (parts[i].ctype==PT_LAVA && parts[i].tmp>0 && parts[i].tmp<PT_NUM && sim->elements[parts[i].tmp].HighTemperatureTransition==PT_LAVA)
+ parts[np].ctype = parts[i].tmp;
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_BCLN::~Element_BCLN() {}
diff --git a/src/simulation/elements/BCOL.cpp b/src/simulation/elements/BCOL.cpp
new file mode 100644
index 0000000..1fb6f82
--- /dev/null
+++ b/src/simulation/elements/BCOL.cpp
@@ -0,0 +1,146 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BCOL PT_BCOL 73
+Element_BCOL::Element_BCOL()
+{
+ Identifier = "DEFAULT_PT_BCOL";
+ Name = "BCOL";
+ Colour = PIXPACK(0x333333);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.3f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 5;
+ Hardness = 2;
+
+ Weight = 90;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 150;
+ Description = "Broken Coal. Heavy particles. See COAL";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_BCOL::update;
+ Graphics = &Element_BCOL::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_BCOL static int update(UPDATE_FUNC_ARGS)
+int Element_BCOL::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, trade, temp;
+ if (parts[i].life<=0) {
+ sim->create_part(i, x, y, PT_FIRE);
+ return 1;
+ } else if (parts[i].life < 100) {
+ parts[i].life--;
+ sim->create_part(-1, x+rand()%3-1, y+rand()%3-1, PT_FIRE);
+ }
+
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_FIRE || (r&0xFF)==PT_PLSM) && 1>(rand()%500))
+ {
+ if (parts[i].life>100) {
+ parts[i].life = 99;
+ }
+ }
+ if ((r&0xFF)==PT_LAVA && 1>(rand()%500))
+ {
+ if (parts[r>>8].ctype == PT_IRON) {
+ parts[r>>8].ctype = PT_METL;
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ }
+ /*if(100-parts[i].life > parts[i].tmp2)
+ parts[i].tmp2 = 100-parts[i].life;
+ if(parts[i].tmp2 < 0) parts[i].tmp2 = 0;
+ for ( trade = 0; trade<4; trade ++)
+ {
+ rx = rand()%5-2;
+ ry = rand()%5-2;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_COAL || (r&0xFF)==PT_BCOL)&&(parts[i].tmp2>parts[r>>8].tmp2)&&parts[i].tmp2>0)//diffusion
+ {
+ int temp = parts[i].tmp2 - parts[r>>8].tmp2;
+ if(temp < 10)
+ continue;
+ if (temp ==1)
+ {
+ parts[r>>8].tmp2 ++;
+ parts[i].tmp2 --;
+ }
+ else if (temp>0)
+ {
+ parts[r>>8].tmp2 += temp/2;
+ parts[i].tmp2 -= temp/2;
+ }
+ }
+ }
+ }*/
+ if(parts[i].temp > parts[i].tmp2)
+ parts[i].tmp2 = parts[i].temp;
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_BCOL static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_BCOL::graphics(GRAPHICS_FUNC_ARGS)
+ //Both COAL and Broken Coal
+{
+ *colr += (cpart->tmp2-295.15f)/3;
+
+ if (*colr > 170)
+ *colr = 170;
+ if (*colr < *colg)
+ *colr = *colg;
+
+ *colg = *colb = *colr;
+
+ if((cpart->temp-295.15f) > 300.0f-200.0f)
+ {
+ float frequency = 3.1415/(2*300.0f-(300.0f-200.0f));
+ int q = ((cpart->temp-295.15f)>300.0f)?300.0f-(300.0f-200.0f):(cpart->temp-295.15f)-(300.0f-200.0f);
+
+ *colr += sin(frequency*q) * 226;
+ *colg += sin(frequency*q*4.55 +3.14) * 34;
+ *colb += sin(frequency*q*2.22 +3.14) * 64;
+ }
+ return 0;
+}
+
+
+
+Element_BCOL::~Element_BCOL() {} \ No newline at end of file
diff --git a/src/simulation/elements/BGLA.cpp b/src/simulation/elements/BGLA.cpp
new file mode 100644
index 0000000..79e7fe2
--- /dev/null
+++ b/src/simulation/elements/BGLA.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BGLA PT_BGLA 47
+Element_BGLA::Element_BGLA()
+{
+ Identifier = "DEFAULT_PT_BGLA";
+ Name = "BGLA";
+ Colour = PIXPACK(0x606060);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.3f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 5;
+ Hardness = 0;
+
+ Weight = 90;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 150;
+ Description = "Broken Glass, Heavy particles. Meltable. Bagels.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART | PROP_HOT_GLOW;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1973.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = NULL;
+
+}
+
+Element_BGLA::~Element_BGLA() {} \ No newline at end of file
diff --git a/src/simulation/elements/BHOL.cpp b/src/simulation/elements/BHOL.cpp
new file mode 100644
index 0000000..35eda8c
--- /dev/null
+++ b/src/simulation/elements/BHOL.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BHOL PT_BHOL 39
+Element_BHOL::Element_BHOL()
+{
+ Identifier = "DEFAULT_PT_BHOL";
+ Name = "VACU";
+ Colour = PIXPACK(0x303030);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = -0.01f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+70.0f+273.15f;
+ HeatConduct = 255;
+ Description = "Vacuum, sucks in other particles and heats up.";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+
+}
+
+Element_BHOL::~Element_BHOL() {} \ No newline at end of file
diff --git a/src/simulation/elements/BIZR.cpp b/src/simulation/elements/BIZR.cpp
new file mode 100644
index 0000000..13df996
--- /dev/null
+++ b/src/simulation/elements/BIZR.cpp
@@ -0,0 +1,124 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BIZR PT_BIZR 103
+Element_BIZR::Element_BIZR()
+{
+ Identifier = "DEFAULT_PT_BIZR";
+ Name = "BIZR";
+ Colour = PIXPACK(0x00FF77);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 30;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 29;
+ Description = "Bizarre... contradicts the normal state changes.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 100.0f;
+ LowTemperatureTransition = PT_BIZRG;
+ HighTemperature = 400.0f;
+ HighTemperatureTransition = PT_BIZRS;
+
+ Update = &Element_BIZR::update;
+ Graphics = &Element_BIZR::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_BIZR static int update(UPDATE_FUNC_ARGS)
+int Element_BIZR::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, nr, ng, nb, na;
+ float tr, tg, tb, ta, mr, mg, mb, ma;
+ float blend;
+ if(parts[i].dcolour){
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_BIZR && (r&0xFF)!=PT_BIZRG && (r&0xFF)!=PT_BIZRS)
+ {
+ blend = 0.95f;
+ tr = (parts[r>>8].dcolour>>16)&0xFF;
+ tg = (parts[r>>8].dcolour>>8)&0xFF;
+ tb = (parts[r>>8].dcolour)&0xFF;
+ ta = (parts[r>>8].dcolour>>24)&0xFF;
+
+ mr = (parts[i].dcolour>>16)&0xFF;
+ mg = (parts[i].dcolour>>8)&0xFF;
+ mb = (parts[i].dcolour)&0xFF;
+ ma = (parts[i].dcolour>>24)&0xFF;
+
+ nr = (tr*blend) + (mr*(1-blend));
+ ng = (tg*blend) + (mg*(1-blend));
+ nb = (tb*blend) + (mb*(1-blend));
+ na = (ta*blend) + (ma*(1-blend));
+
+ parts[r>>8].dcolour = nr<<16 | ng<<8 | nb | na<<24;
+ }
+ }
+ }
+ if(((r = sim->photons[y][x])&0xFF)==PT_PHOT || ((r = pmap[y][x])&0xFF)==PT_PHOT)
+ {
+ sim->part_change_type(r>>8, x, y, PT_ELEC);
+ parts[r>>8].ctype = 0;
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_BIZR static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_BIZR::graphics(GRAPHICS_FUNC_ARGS)
+ //BIZR, BIZRG, BIZRS
+{
+ int x = 0;
+ *colg = 0;
+ *colb = 0;
+ *colr = 0;
+ for (x=0; x<12; x++) {
+ *colr += (cpart->ctype >> (x+18)) & 1;
+ *colb += (cpart->ctype >> x) & 1;
+ }
+ for (x=0; x<12; x++)
+ *colg += (cpart->ctype >> (x+9)) & 1;
+ x = 624/(*colr+*colg+*colb+1);
+ *colr *= x;
+ *colg *= x;
+ *colb *= x;
+ if(fabs(cpart->vx)+fabs(cpart->vy)>0)
+ {
+ *firea = 255;
+ *fireg = *colg/5 * fabs(cpart->vx)+fabs(cpart->vy);
+ *fireb = *colb/5 * fabs(cpart->vx)+fabs(cpart->vy);
+ *firer = *colr/5 * fabs(cpart->vx)+fabs(cpart->vy);
+ *pixel_mode |= FIRE_ADD;
+ }
+ return 0;
+}
+
+
+Element_BIZR::~Element_BIZR() {} \ No newline at end of file
diff --git a/src/simulation/elements/BIZRG.cpp b/src/simulation/elements/BIZRG.cpp
new file mode 100644
index 0000000..1e5f27f
--- /dev/null
+++ b/src/simulation/elements/BIZRG.cpp
@@ -0,0 +1,124 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BIZRG PT_BIZRG 104
+Element_BIZRG::Element_BIZRG()
+{
+ Identifier = "DEFAULT_PT_BIZRG";
+ Name = "BIZG";
+ Colour = PIXPACK(0x00FFBB);
+ MenuVisible = 1;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 1.0f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.30f;
+ Collision = -0.1f;
+ Gravity = 0.0f;
+ Diffusion = 2.75f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 1;
+
+ Temperature = R_TEMP-200.0f+273.15f;
+ HeatConduct = 42;
+ Description = "Bizarre gas";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 100.0f;
+ HighTemperatureTransition = PT_BIZR;
+
+ Update = &Element_BIZRG::update;
+ Graphics = &Element_BIZRG::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_BIZRG static int update(UPDATE_FUNC_ARGS)
+int Element_BIZRG::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, nr, ng, nb, na;
+ float tr, tg, tb, ta, mr, mg, mb, ma;
+ float blend;
+ if(parts[i].dcolour){
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_BIZR && (r&0xFF)!=PT_BIZRG && (r&0xFF)!=PT_BIZRS)
+ {
+ blend = 0.95f;
+ tr = (parts[r>>8].dcolour>>16)&0xFF;
+ tg = (parts[r>>8].dcolour>>8)&0xFF;
+ tb = (parts[r>>8].dcolour)&0xFF;
+ ta = (parts[r>>8].dcolour>>24)&0xFF;
+
+ mr = (parts[i].dcolour>>16)&0xFF;
+ mg = (parts[i].dcolour>>8)&0xFF;
+ mb = (parts[i].dcolour)&0xFF;
+ ma = (parts[i].dcolour>>24)&0xFF;
+
+ nr = (tr*blend) + (mr*(1-blend));
+ ng = (tg*blend) + (mg*(1-blend));
+ nb = (tb*blend) + (mb*(1-blend));
+ na = (ta*blend) + (ma*(1-blend));
+
+ parts[r>>8].dcolour = nr<<16 | ng<<8 | nb | na<<24;
+ }
+ }
+ }
+ if(((r = sim->photons[y][x])&0xFF)==PT_PHOT || ((r = pmap[y][x])&0xFF)==PT_PHOT)
+ {
+ sim->part_change_type(r>>8, x, y, PT_ELEC);
+ parts[r>>8].ctype = 0;
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_BIZRG static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_BIZRG::graphics(GRAPHICS_FUNC_ARGS)
+ //BIZR, BIZRG, BIZRS
+{
+ int x = 0;
+ *colg = 0;
+ *colb = 0;
+ *colr = 0;
+ for (x=0; x<12; x++) {
+ *colr += (cpart->ctype >> (x+18)) & 1;
+ *colb += (cpart->ctype >> x) & 1;
+ }
+ for (x=0; x<12; x++)
+ *colg += (cpart->ctype >> (x+9)) & 1;
+ x = 624/(*colr+*colg+*colb+1);
+ *colr *= x;
+ *colg *= x;
+ *colb *= x;
+ if(fabs(cpart->vx)+fabs(cpart->vy)>0)
+ {
+ *firea = 255;
+ *fireg = *colg/5 * fabs(cpart->vx)+fabs(cpart->vy);
+ *fireb = *colb/5 * fabs(cpart->vx)+fabs(cpart->vy);
+ *firer = *colr/5 * fabs(cpart->vx)+fabs(cpart->vy);
+ *pixel_mode |= FIRE_ADD;
+ }
+ return 0;
+}
+
+
+Element_BIZRG::~Element_BIZRG() {} \ No newline at end of file
diff --git a/src/simulation/elements/BIZRS.cpp b/src/simulation/elements/BIZRS.cpp
new file mode 100644
index 0000000..439a723
--- /dev/null
+++ b/src/simulation/elements/BIZRS.cpp
@@ -0,0 +1,124 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BIZRS PT_BIZRS 105
+Element_BIZRS::Element_BIZRS()
+{
+ Identifier = "DEFAULT_PT_BIZRS";
+ Name = "BIZS";
+ Colour = PIXPACK(0x00E455);
+ MenuVisible = 1;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+300.0f+273.15f;
+ HeatConduct = 251;
+ Description = "Bizarre solid";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 400.0f;
+ LowTemperatureTransition = PT_BIZR;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_BIZRS::update;
+ Graphics = &Element_BIZRS::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_BIZRS static int update(UPDATE_FUNC_ARGS)
+int Element_BIZRS::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, nr, ng, nb, na;
+ float tr, tg, tb, ta, mr, mg, mb, ma;
+ float blend;
+ if(parts[i].dcolour){
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_BIZR && (r&0xFF)!=PT_BIZRG && (r&0xFF)!=PT_BIZRS)
+ {
+ blend = 0.95f;
+ tr = (parts[r>>8].dcolour>>16)&0xFF;
+ tg = (parts[r>>8].dcolour>>8)&0xFF;
+ tb = (parts[r>>8].dcolour)&0xFF;
+ ta = (parts[r>>8].dcolour>>24)&0xFF;
+
+ mr = (parts[i].dcolour>>16)&0xFF;
+ mg = (parts[i].dcolour>>8)&0xFF;
+ mb = (parts[i].dcolour)&0xFF;
+ ma = (parts[i].dcolour>>24)&0xFF;
+
+ nr = (tr*blend) + (mr*(1-blend));
+ ng = (tg*blend) + (mg*(1-blend));
+ nb = (tb*blend) + (mb*(1-blend));
+ na = (ta*blend) + (ma*(1-blend));
+
+ parts[r>>8].dcolour = nr<<16 | ng<<8 | nb | na<<24;
+ }
+ }
+ }
+ if(((r = sim->photons[y][x])&0xFF)==PT_PHOT || ((r = pmap[y][x])&0xFF)==PT_PHOT)
+ {
+ sim->part_change_type(r>>8, x, y, PT_ELEC);
+ parts[r>>8].ctype = 0;
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_BIZRS static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_BIZRS::graphics(GRAPHICS_FUNC_ARGS)
+ //BIZR, BIZRG, BIZRS
+{
+ int x = 0;
+ *colg = 0;
+ *colb = 0;
+ *colr = 0;
+ for (x=0; x<12; x++) {
+ *colr += (cpart->ctype >> (x+18)) & 1;
+ *colb += (cpart->ctype >> x) & 1;
+ }
+ for (x=0; x<12; x++)
+ *colg += (cpart->ctype >> (x+9)) & 1;
+ x = 624/(*colr+*colg+*colb+1);
+ *colr *= x;
+ *colg *= x;
+ *colb *= x;
+ if(fabs(cpart->vx)+fabs(cpart->vy)>0)
+ {
+ *firea = 255;
+ *fireg = *colg/5 * fabs(cpart->vx)+fabs(cpart->vy);
+ *fireb = *colb/5 * fabs(cpart->vx)+fabs(cpart->vy);
+ *firer = *colr/5 * fabs(cpart->vx)+fabs(cpart->vy);
+ *pixel_mode |= FIRE_ADD;
+ }
+ return 0;
+}
+
+
+Element_BIZRS::~Element_BIZRS() {} \ No newline at end of file
diff --git a/src/simulation/elements/BMTL.cpp b/src/simulation/elements/BMTL.cpp
new file mode 100644
index 0000000..adc5289
--- /dev/null
+++ b/src/simulation/elements/BMTL.cpp
@@ -0,0 +1,80 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BMTL PT_BMTL 29
+Element_BMTL::Element_BMTL()
+{
+ Identifier = "DEFAULT_PT_BMTL";
+ Name = "BMTL";
+ Colour = PIXPACK(0x505070);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Breakable metal.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 1.0f;
+ HighPressureTransition = ST;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1273.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = &Element_BMTL::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_BMTL static int update(UPDATE_FUNC_ARGS)
+int Element_BMTL::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, rt, tempFactor;
+ if (parts[i].tmp>1)
+ {
+ parts[i].tmp--;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ rt = parts[r>>8].type;
+ if ((rt==PT_METL || rt==PT_IRON) && 1>(rand()/(RAND_MAX/100)))
+ {
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_BMTL);
+ parts[r>>8].tmp=(parts[i].tmp<=7)?parts[i].tmp=1:parts[i].tmp-(rand()%5);//rand()/(RAND_MAX/300)+100;
+ }
+ }
+ }
+ else if (parts[i].tmp==1 && 1>rand()%1000)
+ {
+ parts[i].tmp = 0;
+ sim->part_change_type(i,x,y,PT_BRMT);
+ }
+ return 0;
+}
+
+
+Element_BMTL::~Element_BMTL() {} \ No newline at end of file
diff --git a/src/simulation/elements/BOMB.cpp b/src/simulation/elements/BOMB.cpp
new file mode 100644
index 0000000..0335e59
--- /dev/null
+++ b/src/simulation/elements/BOMB.cpp
@@ -0,0 +1,113 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BOMB PT_BOMB 129
+Element_BOMB::Element_BOMB()
+{
+ Identifier = "DEFAULT_PT_BOMB";
+ Name = "BOMB";
+ Colour = PIXPACK(0xFFF288);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 30;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 29;
+ Description = "Bomb.";
+
+ State = ST_NONE;
+ Properties = TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC|PROP_SPARKSETTLE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_BOMB::update;
+ Graphics = &Element_BOMB::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_BOMB static int update(UPDATE_FUNC_ARGS)
+int Element_BOMB::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, nb;
+
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_BOMB && (r&0xFF)!=PT_EMBR && (r&0xFF)!=PT_DMND && (r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PCLN && (r&0xFF)!=PT_BCLN && (r&0xFF)!=PT_VIBR)
+ {
+ int rad = 8;
+ int nxi;
+ int nxj;
+ pmap[y][x] = 0;
+ for (nxj=-rad; nxj<=rad; nxj++)
+ for (nxi=-rad; nxi<=rad; nxi++)
+ if ((pow((float)nxi,2))/(pow((float)rad,2))+(pow((float)nxj,2))/(pow((float)rad,2))<=1)
+ if ((pmap[y+nxj][x+nxi]&0xFF)!=PT_DMND && (pmap[y+nxj][x+nxi]&0xFF)!=PT_CLNE && (pmap[y+nxj][x+nxi]&0xFF)!=PT_PCLN && (pmap[y+nxj][x+nxi]&0xFF)!=PT_BCLN && (pmap[y+nxj][x+nxi]&0xFF)!=PT_VIBR)
+ {
+ sim->delete_part(x+nxi, y+nxj, 0);
+ sim->pv[(y+nxj)/CELL][(x+nxi)/CELL] += 0.1f;
+ nb = sim->create_part(-3, x+nxi, y+nxj, PT_EMBR);
+ if (nb!=-1)
+ {
+ parts[nb].tmp = 2;
+ parts[nb].life = 2;
+ parts[nb].temp = MAX_TEMP;
+ }
+ }
+ for (nxj=-(rad+1); nxj<=(rad+1); nxj++)
+ for (nxi=-(rad+1); nxi<=(rad+1); nxi++)
+ if ((pow((float)nxi,2))/(pow((float)(rad+1),2))+(pow((float)nxj,2))/(pow((float)(rad+1),2))<=1 && !(pmap[y+nxj][x+nxi]&0xFF))
+ {
+ nb = sim->create_part(-3, x+nxi, y+nxj, PT_EMBR);
+ if (nb!=-1)
+ {
+ parts[nb].tmp = 0;
+ parts[nb].life = 50;
+ parts[nb].temp = MAX_TEMP;
+ parts[nb].vx = rand()%40-20;
+ parts[nb].vy = rand()%40-20;
+ }
+ }
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_BOMB static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_BOMB::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *pixel_mode |= PMODE_FLARE;
+ return 1;
+}
+
+
+Element_BOMB::~Element_BOMB() {}
diff --git a/src/simulation/elements/BOYL.cpp b/src/simulation/elements/BOYL.cpp
new file mode 100644
index 0000000..18ebee8
--- /dev/null
+++ b/src/simulation/elements/BOYL.cpp
@@ -0,0 +1,94 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BOYL PT_BOYL 141
+Element_BOYL::Element_BOYL()
+{
+ Identifier = "DEFAULT_PT_BOYL";
+ Name = "BOYL";
+ Colour = PIXPACK(0x0A3200);
+ MenuVisible = 1;
+ MenuSection = SC_GAS;
+ Enabled = 1;
+
+ Advection = 1.0f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.30f;
+ Collision = -0.1f;
+ Gravity = 0.0f;
+ Diffusion = 0.18f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 1;
+
+ Temperature = R_TEMP+2.0f +273.15f;
+ HeatConduct = 42;
+ Description = "Boyle, variable pressure gas. Expands when heated.";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_BOYL::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_BOYL static int update(UPDATE_FUNC_ARGS)
+int Element_BOYL::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ if (sim->pv[y/CELL][x/CELL]<(parts[i].temp/100))
+ sim->pv[y/CELL][x/CELL] += 0.001f*((parts[i].temp/100)-sim->pv[y/CELL][x/CELL]);
+ if (y+CELL<YRES && sim->pv[y/CELL+1][x/CELL]<(parts[i].temp/100))
+ sim->pv[y/CELL+1][x/CELL] += 0.001f*((parts[i].temp/100)-sim->pv[y/CELL+1][x/CELL]);
+ if (x+CELL<XRES)
+ {
+ sim->pv[y/CELL][x/CELL+1] += 0.001f*((parts[i].temp/100)-sim->pv[y/CELL][x/CELL+1]);
+ if (y+CELL<YRES)
+ sim->pv[y/CELL+1][x/CELL+1] += 0.001f*((parts[i].temp/100)-sim->pv[y/CELL+1][x/CELL+1]);
+ }
+ if (y-CELL>=0 && sim->pv[y/CELL-1][x/CELL]<(parts[i].temp/100))
+ sim->pv[y/CELL-1][x/CELL] += 0.001f*((parts[i].temp/100)-sim->pv[y/CELL-1][x/CELL]);
+ if (x-CELL>=0)
+ {
+ sim->pv[y/CELL][x/CELL-1] += 0.001f*((parts[i].temp/100)-sim->pv[y/CELL][x/CELL-1]);
+ if (y-CELL>=0)
+ sim->pv[y/CELL-1][x/CELL-1] += 0.001f*((parts[i].temp/100)-sim->pv[y/CELL-1][x/CELL-1]);
+ }
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 &&
+ x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_WATR && 1>rand()%30)
+ {
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_FOG);
+ }
+ else if ((r&0xFF)==PT_O2 && 1>rand()%9)
+ {
+ sim->kill_part(r>>8);
+ sim->part_change_type(i,x,y,PT_WATR);
+ sim->pv[y/CELL][x/CELL] += 4.0;
+ }
+ }
+ return 0;
+}
+
+
+Element_BOYL::~Element_BOYL() {} \ No newline at end of file
diff --git a/src/simulation/elements/BRAY.cpp b/src/simulation/elements/BRAY.cpp
new file mode 100644
index 0000000..dfcd080
--- /dev/null
+++ b/src/simulation/elements/BRAY.cpp
@@ -0,0 +1,109 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BRAY PT_BRAY 127
+Element_BRAY::Element_BRAY()
+{
+ Identifier = "DEFAULT_PT_BRAY";
+ Name = "BRAY";
+ Colour = PIXPACK(0xFFFFFF);
+ MenuVisible = 0;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Ray Point. Rays create points when they collide";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC|PROP_LIFE_KILL;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+ Graphics = &Element_BRAY::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_BRAY static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_BRAY::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int x, trans = 255;
+ if(cpart->tmp==0)
+ {
+ trans = cpart->life * 7;
+ if (trans>255) trans = 255;
+ if (cpart->ctype) {
+ *colg = 0;
+ *colb = 0;
+ *colr = 0;
+ for (x=0; x<12; x++) {
+ *colr += (cpart->ctype >> (x+18)) & 1;
+ *colb += (cpart->ctype >> x) & 1;
+ }
+ for (x=0; x<12; x++)
+ *colg += (cpart->ctype >> (x+9)) & 1;
+ x = 624/(*colr+*colg+*colb+1);
+ *colr *= x;
+ *colg *= x;
+ *colb *= x;
+ }
+ }
+ else if(cpart->tmp==1)
+ {
+ trans = cpart->life/4;
+ if (trans>255) trans = 255;
+ if (cpart->ctype) {
+ *colg = 0;
+ *colb = 0;
+ *colr = 0;
+ for (x=0; x<12; x++) {
+ *colr += (cpart->ctype >> (x+18)) & 1;
+ *colb += (cpart->ctype >> x) & 1;
+ }
+ for (x=0; x<12; x++)
+ *colg += (cpart->ctype >> (x+9)) & 1;
+ x = 624/(*colr+*colg+*colb+1);
+ *colr *= x;
+ *colg *= x;
+ *colb *= x;
+ }
+ }
+ else if(cpart->tmp==2)
+ {
+ trans = cpart->life*100;
+ if (trans>255) trans = 255;
+ *colr = 255;
+ *colg = 150;
+ *colb = 50;
+ }
+ *cola = trans;
+ *pixel_mode &= ~PMODE;
+ *pixel_mode |= PMODE_BLEND | PMODE_GLOW;
+ return 0;
+}
+
+
+Element_BRAY::~Element_BRAY() {} \ No newline at end of file
diff --git a/src/simulation/elements/BRCK.cpp b/src/simulation/elements/BRCK.cpp
new file mode 100644
index 0000000..6769aa3
--- /dev/null
+++ b/src/simulation/elements/BRCK.cpp
@@ -0,0 +1,65 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BRCK PT_BRCK 67
+Element_BRCK::Element_BRCK()
+{
+ Identifier = "DEFAULT_PT_BRCK";
+ Name = "BRCK";
+ Colour = PIXPACK(0x808080);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Brick, breakable building material.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_HOT_GLOW;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 8.8f;
+ HighPressureTransition = PT_STNE;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1223.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = NULL;
+ Graphics = &Element_BRCK::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_BRCK static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_BRCK::graphics(GRAPHICS_FUNC_ARGS)
+{
+ if (cpart->tmp == 1)
+ {
+ *pixel_mode |= FIRE_ADD;
+ *colb += 100;
+
+ *firea = 40;
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+ }
+ return 0;
+}
+
+Element_BRCK::~Element_BRCK() {} \ No newline at end of file
diff --git a/src/simulation/elements/BREC.cpp b/src/simulation/elements/BREC.cpp
new file mode 100644
index 0000000..e260a7c
--- /dev/null
+++ b/src/simulation/elements/BREC.cpp
@@ -0,0 +1,64 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BREC PT_BREC 135
+Element_BREC::Element_BREC()
+{
+ Identifier = "DEFAULT_PT_BREC";
+ Name = "BREL";
+ Colour = PIXPACK(0x707060);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.18f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 2;
+ Hardness = 2;
+
+ Weight = 90;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 211;
+ Description = "Broken electronics";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_BREC::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_BREC static int update(UPDATE_FUNC_ARGS)
+int Element_BREC::update(UPDATE_FUNC_ARGS)
+{
+ int np;
+ if (1>rand()%200 && (sim->pv[y/CELL][x/CELL] > 30.0f) && parts[i].temp>9000 && parts[i].life>0)
+ {
+ sim->part_change_type(i, x ,y ,PT_EXOT);
+ parts[i].life = 1000;
+ }
+ if ((sim->pv[y/CELL][x/CELL] > 10.0f) && (parts[i].life>0)) {
+ parts[i].temp = parts[i].temp + (sim->pv[y/CELL][x/CELL])/8;
+ }
+ return 0;
+}
+
+Element_BREC::~Element_BREC() {}
diff --git a/src/simulation/elements/BRMT.cpp b/src/simulation/elements/BRMT.cpp
new file mode 100644
index 0000000..55eb243
--- /dev/null
+++ b/src/simulation/elements/BRMT.cpp
@@ -0,0 +1,85 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BRMT PT_BRMT 30
+Element_BRMT::Element_BRMT()
+{
+ Identifier = "DEFAULT_PT_BRMT";
+ Name = "BRMT";
+ Colour = PIXPACK(0x705060);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.3f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 2;
+ Hardness = 2;
+
+ Weight = 90;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 211;
+ Description = "Broken metal.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1273.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = &Element_BRMT::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_BRMT static int update(UPDATE_FUNC_ARGS)
+int Element_BRMT::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, rt, tempFactor;
+ if (parts[i].temp > (250.0f+273.15f))
+ {
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ rt = parts[r>>8].type;
+ tempFactor = 1000 - (((250.0f+273.15f)-parts[i].temp)*2);
+ if(tempFactor < 2)
+ tempFactor = 2;
+ if ((rt==PT_BREC) && 1 > (rand()%tempFactor))
+ {
+ if(rand()%2)
+ {
+ sim->create_part(r>>8, x+rx, y+ry, PT_THRM);
+ }
+ else
+ { sim->create_part(i, x, y, PT_THRM);
+ }
+ return 1;
+ //part_change_type(r>>8,x+rx,y+ry,PT_BMTL);
+ //parts[r>>8].tmp=(parts[i].tmp<=7)?parts[i].tmp=1:parts[i].tmp-(rand()%5);//rand()/(RAND_MAX/300)+100;
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_BRMT::~Element_BRMT() {} \ No newline at end of file
diff --git a/src/simulation/elements/BTRY.cpp b/src/simulation/elements/BTRY.cpp
new file mode 100644
index 0000000..efbb3d2
--- /dev/null
+++ b/src/simulation/elements/BTRY.cpp
@@ -0,0 +1,75 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BTRY PT_BTRY 53
+Element_BTRY::Element_BTRY()
+{
+ Identifier = "DEFAULT_PT_BTRY";
+ Name = "BTRY";
+ Colour = PIXPACK(0x858505);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Solid. Generates Electricity.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 2273.0f;
+ HighTemperatureTransition = PT_PLSM;
+
+ Update = &Element_BTRY::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_BTRY static int update(UPDATE_FUNC_ARGS)
+int Element_BTRY::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, rt;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ rt = parts[r>>8].type;
+ if (sim->parts_avg(i,r>>8,PT_INSL) != PT_INSL)
+ {
+ if ((sim->elements[rt].Properties&PROP_CONDUCTS) && !(rt==PT_WATR||rt==PT_SLTW||rt==PT_NTCT||rt==PT_PTCT||rt==PT_INWR) && parts[r>>8].life==0 && abs(rx)+abs(ry) < 4)
+ {
+ parts[r>>8].life = 4;
+ parts[r>>8].ctype = rt;
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SPRK);
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_BTRY::~Element_BTRY() {} \ No newline at end of file
diff --git a/src/simulation/elements/BVBR.cpp b/src/simulation/elements/BVBR.cpp
new file mode 100644
index 0000000..dfff186
--- /dev/null
+++ b/src/simulation/elements/BVBR.cpp
@@ -0,0 +1,50 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_BVBR PT_BVBR 166
+Element_BVBR::Element_BVBR()
+{
+ Identifier = "DEFAULT_PT_BVBR";
+ Name = "BVBR";
+ Colour = PIXPACK(0x005000);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.3f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.15f;
+ Diffusion = 0.00f;
+ HotAir = 0.0000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 67;
+
+ Temperature = 273.15f;
+ HeatConduct = 164;
+ Description = "Broken vibranium.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_VIBR::update;
+ Graphics = &Element_VIBR::graphics;
+
+}
+
+Element_BVBR::~Element_BVBR() {} \ No newline at end of file
diff --git a/src/simulation/elements/C5.cpp b/src/simulation/elements/C5.cpp
new file mode 100644
index 0000000..28e2055
--- /dev/null
+++ b/src/simulation/elements/C5.cpp
@@ -0,0 +1,75 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_C5 PT_C5 130
+Element_C5::Element_C5()
+{
+ Identifier = "DEFAULT_PT_C5";
+ Name = "C-5";
+ Colour = PIXPACK(0x2050E0);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 88;
+ Description = "Cold explosive";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID | PROP_NEUTPENETRATE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_C5::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_C5 static int update(UPDATE_FUNC_ARGS)
+int Element_C5::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)!=PT_C5 && parts[r>>8].temp<100 && sim->elements[r&0xFF].HeatConduct && ((r&0xFF)!=PT_HSWC||parts[r>>8].life==10)) || (r&0xFF)==PT_HFLM)
+ {
+ if (1>rand()%6)
+ {
+ sim->part_change_type(i,x,y,PT_HFLM);
+ parts[r>>8].temp = parts[i].temp = 0;
+ parts[i].life = rand()%150+50;
+ sim->pv[y/CELL][x/CELL] += 1.5;
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_C5::~Element_C5() {} \ No newline at end of file
diff --git a/src/simulation/elements/CAUS.cpp b/src/simulation/elements/CAUS.cpp
new file mode 100644
index 0000000..a8fc0da
--- /dev/null
+++ b/src/simulation/elements/CAUS.cpp
@@ -0,0 +1,86 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_CAUS PT_CAUS 86
+Element_CAUS::Element_CAUS()
+{
+ Identifier = "DEFAULT_PT_CAUS";
+ Name = "CAUS";
+ Colour = PIXPACK(0x80FFA0);
+ MenuVisible = 1;
+ MenuSection = SC_GAS;
+ Enabled = 1;
+
+ Advection = 2.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.30f;
+ Collision = -0.1f;
+ Gravity = 0.0f;
+ Diffusion = 1.50f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 1;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Caustic Gas, acts like Acid";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS|PROP_DEADLY;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_CAUS::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_CAUS static int update(UPDATE_FUNC_ARGS)
+int Element_CAUS::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, trade, np;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_ACID && (r&0xFF)!=PT_CAUS)
+ {
+ if (((r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PCLN && sim->elements[r&0xFF].Hardness>(rand()%1000))&&parts[i].life>=50)
+ {
+ if (sim->parts_avg(i, r>>8,PT_GLAS)!= PT_GLAS)//GLAS protects stuff from acid
+ {
+ float newtemp = ((60.0f-(float)sim->elements[r&0xFF].Hardness))*7.0f;
+ if(newtemp < 0){
+ newtemp = 0;
+ }
+ parts[i].temp += newtemp;
+ parts[i].life--;
+ sim->kill_part(r>>8);
+ }
+ }
+ else if (parts[i].life<=50)
+ {
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_CAUS::~Element_CAUS() {} \ No newline at end of file
diff --git a/src/simulation/elements/CBNW.cpp b/src/simulation/elements/CBNW.cpp
new file mode 100644
index 0000000..4331750
--- /dev/null
+++ b/src/simulation/elements/CBNW.cpp
@@ -0,0 +1,153 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_CBNW PT_CBNW 82
+Element_CBNW::Element_CBNW()
+{
+ Identifier = "DEFAULT_PT_CBNW";
+ Name = "BUBW";
+ Colour = PIXPACK(0x2030D0);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 30;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 29;
+ Description = "Carbonated water. Conducts electricity. Freezes. Extinguishes fires.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_NEUTPENETRATE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 273.15f;
+ LowTemperatureTransition = PT_ICEI;
+ HighTemperature = 373.0f;
+ HighTemperatureTransition = PT_WTRV;
+
+ Update = &Element_CBNW::update;
+ Graphics = &Element_CBNW::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_CBNW static int update(UPDATE_FUNC_ARGS)
+int Element_CBNW::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, oldt;
+ oldt = parts[i].tmp;
+ if (sim->pv[y/CELL][x/CELL]<=3)
+ {
+ if(20>(rand()%80000))
+ {
+ sim->part_change_type(i,x,y,PT_CO2);
+ parts[i].ctype = 5;
+ sim->pv[y/CELL][x/CELL] += 0.5f;
+ }
+ else if(sim->pv[y/CELL][x/CELL]<=-0.5)
+ {
+ sim->part_change_type(i,x,y,PT_CO2);
+ parts[i].ctype = 5;
+ sim->pv[y/CELL][x/CELL] += 0.5f;
+ }
+ }
+ if (parts[i].tmp>0)
+ parts[i].tmp--;
+ if(!(rand()%200))
+ {
+ parts[i].tmp2 = rand()%40;
+ } else if(parts[i].tmp2!=20) {
+ parts[i].tmp2 -= (parts[i].tmp2>20)?1:-1;
+ }
+ if(oldt==1)
+ {
+ //Explode
+ if(rand()%4)
+ {
+ sim->part_change_type(i,x,y,PT_CO2);
+ parts[i].ctype = 5;
+ sim->pv[y/CELL][x/CELL] += 0.2f;
+ }
+ }
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((sim->elements[r&0xFF].Properties&TYPE_PART) && parts[i].tmp == 0 && 1>(rand()%250))
+ {
+ //Start explode
+ parts[i].tmp = rand()%25;//(rand()%100)+50;
+ }
+ else if((sim->elements[r&0xFF].Properties&TYPE_SOLID) && (r&0xFF)!=PT_DMND && (r&0xFF)!=PT_GLAS && parts[i].tmp == 0 && (2-sim->pv[y/CELL][x/CELL])>(rand()%20000))
+ {
+ if(rand()%2)
+ {
+ sim->part_change_type(i,x,y,PT_CO2);
+ parts[i].ctype = 5;
+ sim->pv[y/CELL][x/CELL] += 0.2f;
+ }
+ }
+ if ((r&0xFF)==PT_CBNW)
+ {
+ if(!parts[i].tmp && parts[r>>8].tmp)
+ {
+ parts[i].tmp = parts[r>>8].tmp;
+ if((r>>8)>i) //If the other particle hasn't been life updated
+ parts[i].tmp--;
+ }
+ else if(parts[i].tmp && !parts[r>>8].tmp)
+ {
+ parts[r>>8].tmp = parts[i].tmp;
+ if((r>>8)>i) //If the other particle hasn't been life updated
+ parts[r>>8].tmp++;
+ }
+ }
+ if (((r&0xFF)==PT_RBDM||(r&0xFF)==PT_LRBD) && (sim->legacy_enable||parts[i].temp>(273.15f+12.0f)) && 1>(rand()%500))
+ {
+ sim->part_change_type(i,x,y,PT_FIRE);
+ parts[i].life = 4;
+ parts[i].ctype = PT_WATR;
+ }
+ if ((r&0xFF)==PT_FIRE && parts[r>>8].ctype!=PT_WATR){
+ sim->kill_part(r>>8);
+ if(1>(rand()%150)){
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_CBNW static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_CBNW::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int z = cpart->tmp2 - 20;//speckles!
+ *colr += z * 1;
+ *colg += z * 2;
+ *colb += z * 8;
+ return 0;
+}
+
+
+Element_CBNW::~Element_CBNW() {} \ No newline at end of file
diff --git a/src/simulation/elements/CLNE.cpp b/src/simulation/elements/CLNE.cpp
new file mode 100644
index 0000000..6ffe4f1
--- /dev/null
+++ b/src/simulation/elements/CLNE.cpp
@@ -0,0 +1,91 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_CLNE PT_CLNE 9
+Element_CLNE::Element_CLNE()
+{
+ Identifier = "DEFAULT_PT_CLNE";
+ Name = "CLNE";
+ Colour = PIXPACK(0xFFD010);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Solid. Duplicates any particles it touches.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_CLNE::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_CLNE static int update(UPDATE_FUNC_ARGS)
+int Element_CLNE::update(UPDATE_FUNC_ARGS)
+ {
+ if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled || (parts[i].ctype==PT_LIFE && (parts[i].tmp<0 || parts[i].tmp>=NGOLALT)))
+ {
+ int r, rx, ry;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES)
+ {
+ r = sim->photons[y+ry][x+rx];
+ if (!r)
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PCLN &&
+ (r&0xFF)!=PT_BCLN && (r&0xFF)!=PT_STKM &&
+ (r&0xFF)!=PT_PBCN && (r&0xFF)!=PT_STKM2 &&
+ (r&0xFF)<PT_NUM)
+ {
+ parts[i].ctype = r&0xFF;
+ if ((r&0xFF)==PT_LIFE || (r&0xFF)==PT_LAVA)
+ parts[i].tmp = parts[r>>8].ctype;
+ }
+ }
+ }
+ else {
+ if (parts[i].ctype==PT_LIFE) sim->create_part(-1, x+rand()%3-1, y+rand()%3-1, parts[i].ctype|(parts[i].tmp<<8));
+ else if (parts[i].ctype!=PT_LIGH || (rand()%30)==0)
+ {
+ int np = sim->create_part(-1, x+rand()%3-1, y+rand()%3-1, parts[i].ctype);
+ if (np>=0)
+ {
+ if (parts[i].ctype==PT_LAVA && parts[i].tmp>0 && parts[i].tmp<PT_NUM && sim->elements[parts[i].tmp].HighTemperatureTransition==PT_LAVA)
+ parts[np].ctype = parts[i].tmp;
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_CLNE::~Element_CLNE() {}
diff --git a/src/simulation/elements/CLST.cpp b/src/simulation/elements/CLST.cpp
new file mode 100644
index 0000000..3cab1f7
--- /dev/null
+++ b/src/simulation/elements/CLST.cpp
@@ -0,0 +1,101 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_CLST PT_CLST 155
+Element_CLST::Element_CLST()
+{
+ Identifier = "DEFAULT_PT_CLST";
+ Name = "CLST";
+ Colour = PIXPACK(0xE4A4A4);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.2f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 2;
+ Hardness = 2;
+
+ Weight = 55;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Clay dust. Produces paste when mixed with water.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1256.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = &Element_CLST::update;
+ Graphics = &Element_CLST::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_CLST static int update(UPDATE_FUNC_ARGS)
+int Element_CLST::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ float cxy;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_WATR && 1>(rand()%1500))
+ {
+ sim->part_change_type(i,x,y,PT_PSTS);
+ sim->kill_part(r>>8);
+ }
+ if ((r&0xFF)==PT_NITR)
+ {
+ sim->create_part(i, x, y, PT_BANG);
+ sim->create_part(r>>8, x+rx, y+ry, PT_BANG);
+ }
+ if ((r&0xFF)==PT_CLST)
+ {
+ if(parts[i].temp <195)
+ cxy = 0.05;
+ if(parts[i].temp >= 195 && parts[i].temp <295)
+ cxy = 0.015;
+ if(parts[i].temp >= 295 && parts[i].temp <350)
+ cxy = 0.01;
+ if(parts[i].temp > 350)
+ cxy = 0.005;
+ parts[i].vx += cxy*rx;
+ parts[i].vy += cxy*ry;//These two can be set not to calculate over 350 later. They do virtually nothing over 0.005.
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_CLST static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_CLST::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int z = cpart->tmp - 5;//speckles!
+ *colr += z * 16;
+ *colg += z * 16;
+ *colb += z * 16;
+ return 0;
+}
+
+
+Element_CLST::~Element_CLST() {} \ No newline at end of file
diff --git a/src/simulation/elements/CNCT.cpp b/src/simulation/elements/CNCT.cpp
new file mode 100644
index 0000000..77f70be
--- /dev/null
+++ b/src/simulation/elements/CNCT.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_CNCT PT_CNCT 24
+Element_CNCT::Element_CNCT()
+{
+ Identifier = "DEFAULT_PT_CNCT";
+ Name = "CNCT";
+ Colour = PIXPACK(0xC0C0C0);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.3f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 2;
+ Hardness = 2;
+
+ Weight = 55;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 100;
+ Description = "Concrete, stronger than stone.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART|PROP_HOT_GLOW;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1123.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = NULL;
+
+}
+
+Element_CNCT::~Element_CNCT() {} \ No newline at end of file
diff --git a/src/simulation/elements/CO2.cpp b/src/simulation/elements/CO2.cpp
new file mode 100644
index 0000000..49614fb
--- /dev/null
+++ b/src/simulation/elements/CO2.cpp
@@ -0,0 +1,106 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_CO2 PT_CO2 80
+Element_CO2::Element_CO2()
+{
+ Identifier = "DEFAULT_PT_CO2";
+ Name = "CO2";
+ Colour = PIXPACK(0x666666);
+ MenuVisible = 1;
+ MenuSection = SC_GAS;
+ Enabled = 1;
+
+ Advection = 2.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.30f;
+ Collision = -0.1f;
+ Gravity = 0.1f;
+ Diffusion = 1.0f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 1;
+
+ Temperature = R_TEMP+273.15f;
+ HeatConduct = 88;
+ Description = "Carbon Dioxide";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 194.65f;
+ LowTemperatureTransition = PT_DRIC;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_CO2::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_CO2 static int update(UPDATE_FUNC_ARGS)
+int Element_CO2::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (parts[i].ctype==5 && 20>(rand()%40000))
+ {
+ if (sim->create_part(-1, x+rx, y+ry, PT_WATR)>=0)
+ parts[i].ctype = 0;
+ }
+ if ((r>>8)>=NPART || !r)
+ continue;
+ if ((r&0xFF)==PT_FIRE){
+ sim->kill_part(r>>8);
+ if(1>(rand()%150)){
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ if (((r&0xFF)==PT_WATR || (r&0xFF)==PT_DSTW) && 1>(rand()%250))
+ {
+ sim->part_change_type(r>>8, x+rx, y+ry, PT_CBNW);
+ if (parts[i].ctype==5) //conserve number of water particles - ctype=5 means this CO2 hasn't released the water particle from BUBW yet
+ sim->create_part(i, x, y, PT_WATR);
+ else
+ sim->kill_part(i);
+ }
+ }
+ if (parts[i].temp > 9773.15 && sim->pv[y/CELL][x/CELL] > 200.0f)
+ {
+ if (rand()%5 < 1)
+ {
+ int j;
+ sim->create_part(i,x,y,PT_O2);
+
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_NEUT);
+ if (j != -1)
+ parts[j].temp = 15000;
+ if (!(rand()%50))
+ {
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_ELEC);
+ if (j != -1)
+ parts[j].temp = 15000;
+ }
+
+ parts[i].temp = 15000;
+ sim->pv[y/CELL][x/CELL] += 100;
+ }
+ }
+ return 0;
+}
+
+
+Element_CO2::~Element_CO2() {}
diff --git a/src/simulation/elements/COAL.cpp b/src/simulation/elements/COAL.cpp
new file mode 100644
index 0000000..eae7450
--- /dev/null
+++ b/src/simulation/elements/COAL.cpp
@@ -0,0 +1,153 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_COAL PT_COAL 59
+Element_COAL::Element_COAL()
+{
+ Identifier = "DEFAULT_PT_COAL";
+ Name = "COAL";
+ Colour = PIXPACK(0x222222);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.0f;
+ HotAir = 0.0f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 200;
+ Description = "Solid. Burns slowly.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_COAL::update;
+ Graphics = &Element_COAL::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_COAL static int update(UPDATE_FUNC_ARGS)
+int Element_COAL::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, trade, temp;
+ if (parts[i].life<=0) {
+ sim->create_part(i, x, y, PT_FIRE);
+ return 1;
+ } else if (parts[i].life < 100) {
+ parts[i].life--;
+ sim->create_part(-1, x+rand()%3-1, y+rand()%3-1, PT_FIRE);
+ }
+ if ((sim->pv[y/CELL][x/CELL] > 4.3f)&&parts[i].tmp>40)
+ parts[i].tmp=39;
+ else if (parts[i].tmp<40&&parts[i].tmp>0)
+ parts[i].tmp--;
+ else if (parts[i].tmp<=0) {
+ sim->create_part(i, x, y, PT_BCOL);
+ return 1;
+ }
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_FIRE || (r&0xFF)==PT_PLSM) && 1>(rand()%500))
+ {
+ if (parts[i].life>100) {
+ parts[i].life = 99;
+ }
+ }
+ if ((r&0xFF)==PT_LAVA && 1>(rand()%500))
+ {
+ if (parts[r>>8].ctype == PT_IRON) {
+ parts[r>>8].ctype = PT_METL;
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ }
+ /*if(100-parts[i].life > parts[i].tmp2)
+ parts[i].tmp2 = 100-parts[i].life;
+ if(parts[i].tmp2 < 0) parts[i].tmp2 = 0;
+ for ( trade = 0; trade<4; trade ++)
+ {
+ rx = rand()%5-2;
+ ry = rand()%5-2;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_COAL || (r&0xFF)==PT_BCOL)&&(parts[i].tmp2>parts[r>>8].tmp2)&&parts[i].tmp2>0)//diffusion
+ {
+ int temp = parts[i].tmp2 - parts[r>>8].tmp2;
+ if(temp < 10)
+ continue;
+ if (temp ==1)
+ {
+ parts[r>>8].tmp2 ++;
+ parts[i].tmp2 --;
+ }
+ else if (temp>0)
+ {
+ parts[r>>8].tmp2 += temp/2;
+ parts[i].tmp2 -= temp/2;
+ }
+ }
+ }
+ }*/
+ if(parts[i].temp > parts[i].tmp2)
+ parts[i].tmp2 = parts[i].temp;
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_COAL static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_COAL::graphics(GRAPHICS_FUNC_ARGS)
+ //Both COAL and Broken Coal
+{
+ *colr += (cpart->tmp2-295.15f)/3;
+
+ if (*colr > 170)
+ *colr = 170;
+ if (*colr < *colg)
+ *colr = *colg;
+
+ *colg = *colb = *colr;
+
+ if((cpart->temp-295.15f) > 300.0f-200.0f)
+ {
+ float frequency = 3.1415/(2*300.0f-(300.0f-200.0f));
+ int q = ((cpart->temp-295.15f)>300.0f)?300.0f-(300.0f-200.0f):(cpart->temp-295.15f)-(300.0f-200.0f);
+
+ *colr += sin(frequency*q) * 226;
+ *colg += sin(frequency*q*4.55 +3.14) * 34;
+ *colb += sin(frequency*q*2.22 +3.14) * 64;
+ }
+ return 0;
+}
+
+
+
+Element_COAL::~Element_COAL() {} \ No newline at end of file
diff --git a/src/simulation/elements/CONV.cpp b/src/simulation/elements/CONV.cpp
new file mode 100644
index 0000000..6ad1336
--- /dev/null
+++ b/src/simulation/elements/CONV.cpp
@@ -0,0 +1,96 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_CONV PT_CONV 85
+Element_CONV::Element_CONV()
+{
+ Identifier = "DEFAULT_PT_CONV";
+ Name = "CONV";
+ Colour = PIXPACK(0x0AAB0A);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Solid. Converts whatever touches it into its ctype.";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_CONV::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_CONV static int update(UPDATE_FUNC_ARGS)
+int Element_CONV::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled || (parts[i].ctype==PT_LIFE && (parts[i].tmp<0 || parts[i].tmp>=NGOLALT)))
+ {
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES)
+ {
+ r = sim->photons[y+ry][x+rx];
+ if (!r)
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PCLN &&
+ (r&0xFF)!=PT_BCLN && (r&0xFF)!=PT_STKM &&
+ (r&0xFF)!=PT_PBCN && (r&0xFF)!=PT_STKM2 &&
+ (r&0xFF)!=PT_CONV && (r&0xFF)<PT_NUM)
+ {
+ parts[i].ctype = r&0xFF;
+ if ((r&0xFF)==PT_LIFE)
+ parts[i].tmp = parts[r>>8].ctype;
+ }
+ }
+ }
+ else if(parts[i].ctype>0 && parts[i].ctype<PT_NUM && sim->elements[parts[i].ctype].Enabled && parts[i].ctype!=PT_CONV) {
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES)
+ {
+ r = sim->photons[y+ry][x+rx];
+ if (!r)
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if((r&0xFF)!=PT_CONV && (r&0xFF)!=PT_DMND && (r&0xFF)!=parts[i].ctype)
+ {
+ if (parts[i].ctype==PT_LIFE) sim->create_part(r>>8, x+rx, y+ry, parts[i].ctype|(parts[i].tmp<<8));
+ else sim->create_part(r>>8, x+rx, y+ry, parts[i].ctype);
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_CONV::~Element_CONV() {} \ No newline at end of file
diff --git a/src/simulation/elements/DESL.cpp b/src/simulation/elements/DESL.cpp
new file mode 100644
index 0000000..a79086d
--- /dev/null
+++ b/src/simulation/elements/DESL.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DESL PT_DESL 58
+Element_DESL::Element_DESL()
+{
+ Identifier = "DEFAULT_PT_DESL";
+ Name = "DESL";
+ Colour = PIXPACK(0x440000);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 1.0f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.0f;
+ HotAir = 0.0f * CFDS;
+ Falldown = 2;
+
+ Flammable = 2;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 5;
+
+ Weight = 15;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 42;
+ Description = "Liquid. Explodes under high pressure and temperatures";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 5.0f;
+ HighPressureTransition = PT_FIRE;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 335.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = NULL;
+
+}
+
+Element_DESL::~Element_DESL() {} \ No newline at end of file
diff --git a/src/simulation/elements/DEST.cpp b/src/simulation/elements/DEST.cpp
new file mode 100644
index 0000000..a4f7891
--- /dev/null
+++ b/src/simulation/elements/DEST.cpp
@@ -0,0 +1,121 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DEST PT_DEST 89
+Element_DEST::Element_DEST()
+{
+ Identifier = "DEFAULT_PT_DEST";
+ Name = "DEST";
+ Colour = PIXPACK(0xFF3311);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = -0.05f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.4f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 101;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 150;
+ Description = "More destructive Bomb.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_DEST::update;
+ Graphics = &Element_DEST::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_DEST static int update(UPDATE_FUNC_ARGS)
+int Element_DEST::update(UPDATE_FUNC_ARGS)
+ {
+ int r,rx,ry,topv;
+ rx=rand()%5-2;
+ ry=rand()%5-2;
+
+ r = pmap[y+ry][x+rx];
+ if (!r || (r&0xFF)==PT_DEST || (r&0xFF)==PT_DMND || (r&0xFF)==PT_BCLN || (r&0xFF)==PT_CLNE || (r&0xFF)==PT_PCLN || (r&0xFF)==PT_PBCN)
+ return 0;
+
+ if (parts[i].life<=0 || parts[i].life>37)
+ {
+ parts[i].life=30+rand()%20;
+ parts[i].temp+=20000;
+ sim->pv[y/CELL][x/CELL]+=60.0f;
+ }
+ parts[i].temp+=10000;
+ if ((r&0xFF)==PT_PLUT || (r&0xFF)==PT_DEUT)
+ {
+ sim->pv[y/CELL][x/CELL]+=20.0f;
+ parts[i].temp+=18000;
+ if (rand()%2==0)
+ {
+ float orig_temp = parts[r>>8].temp;
+ sim->create_part(r>>8, x+rx, y+ry, PT_NEUT);
+ parts[r>>8].temp = restrict_flt(orig_temp+40000.0f, MIN_TEMP, MAX_TEMP);
+ sim->pv[y/CELL][x/CELL] += 10.0f;
+ parts[i].life-=4;
+ }
+ }
+ else if ((r&0xFF)==PT_INSL)
+ {
+ sim->create_part(r>>8, x+rx, y+ry, PT_PLSM);
+ }
+ else if (rand()%3==0)
+ {
+ sim->kill_part(r>>8);
+ parts[i].life -= 4*((sim->elements[r&0xFF].Properties&TYPE_SOLID)?3:1);
+ if (parts[i].life<=0)
+ parts[i].life=1;
+ parts[i].temp+=10000;
+ }
+ else
+ {
+ if (sim->elements[r&0xFF].HeatConduct) parts[r>>8].temp = restrict_flt(parts[r>>8].temp+10000.0f, MIN_TEMP, MAX_TEMP);
+ }
+ topv=sim->pv[y/CELL][x/CELL]/9+parts[r>>8].temp/900;
+ if (topv>40.0f)
+ topv=40.0f;
+ sim->pv[y/CELL][x/CELL]+=40.0f+topv;
+ parts[i].temp = restrict_flt(parts[i].temp, MIN_TEMP, MAX_TEMP);
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_DEST static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_DEST::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ if(cpart->life)
+ {
+ *pixel_mode |= PMODE_LFLARE;
+ }
+ else
+ {
+ *pixel_mode |= PMODE_SPARK;
+ }
+ return 0;
+}
+
+
+Element_DEST::~Element_DEST() {} \ No newline at end of file
diff --git a/src/simulation/elements/DEUT.cpp b/src/simulation/elements/DEUT.cpp
new file mode 100644
index 0000000..b8209e4
--- /dev/null
+++ b/src/simulation/elements/DEUT.cpp
@@ -0,0 +1,149 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DEUT PT_DEUT 95
+Element_DEUT::Element_DEUT()
+{
+ Identifier = "DEFAULT_PT_DEUT";
+ Name = "DEUT";
+ Colour = PIXPACK(0x00153F);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 31;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Deuterium oxide. Volume changes with temp, radioactive with neutrons.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_NEUTPENETRATE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_DEUT::update;
+ Graphics = &Element_DEUT::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_DEUT static int update(UPDATE_FUNC_ARGS)
+int Element_DEUT::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, trade, np;
+ float gravtot = fabs(sim->gravy[(y/CELL)*(XRES/CELL)+(x/CELL)])+fabs(sim->gravx[(y/CELL)*(XRES/CELL)+(x/CELL)]);
+ int maxlife = ((10000/(parts[i].temp + 1))-1);
+ if ((10000%((int)parts[i].temp+1))>rand()%((int)parts[i].temp+1))
+ maxlife ++;
+ // Compress when Newtonian gravity is applied
+ // multiplier=1 when gravtot=0, multiplier -> 5 as gravtot -> inf
+ maxlife = maxlife*(5.0f - 8.0f/(gravtot+2.0f));
+ if (parts[i].life < maxlife)
+ {
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r || (parts[i].life >=maxlife))
+ continue;
+ if ((r&0xFF)==PT_DEUT&&33>=rand()/(RAND_MAX/100)+1)
+ {
+ if ((parts[i].life + parts[r>>8].life + 1) <= maxlife)
+ {
+ parts[i].life += parts[r>>8].life + 1;
+ sim->kill_part(r>>8);
+ }
+ }
+ }
+ }
+ else
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (parts[i].life<=maxlife)
+ continue;
+ if ((!r)&&parts[i].life>=1)//if nothing then create deut
+ {
+ np = sim->create_part(-1,x+rx,y+ry,PT_DEUT);
+ if (np<0) continue;
+ parts[i].life--;
+ parts[np].temp = parts[i].temp;
+ parts[np].life = 0;
+ }
+ }
+ for ( trade = 0; trade<4; trade ++)
+ {
+ rx = rand()%5-2;
+ ry = rand()%5-2;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_DEUT&&(parts[i].life>parts[r>>8].life)&&parts[i].life>0)//diffusion
+ {
+ int temp = parts[i].life - parts[r>>8].life;
+ if (temp ==1)
+ {
+ parts[r>>8].life ++;
+ parts[i].life --;
+ }
+ else if (temp>0)
+ {
+ parts[r>>8].life += temp/2;
+ parts[i].life -= temp/2;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_DEUT static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_DEUT::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ if(cpart->life>=700)
+ {
+ *firea = 60;
+ *firer = *colr += cpart->life*1;
+ *fireg = *colg += cpart->life*2;
+ *fireb = *colb += cpart->life*3;
+ *pixel_mode |= PMODE_GLOW | FIRE_ADD;
+ }
+ else
+ {
+ *colr += cpart->life*1;
+ *colg += cpart->life*2;
+ *colb += cpart->life*3;
+ *pixel_mode |= PMODE_BLUR;
+ }
+ return 0;
+}
+
+
+Element_DEUT::~Element_DEUT() {} \ No newline at end of file
diff --git a/src/simulation/elements/DLAY.cpp b/src/simulation/elements/DLAY.cpp
new file mode 100644
index 0000000..e69c202
--- /dev/null
+++ b/src/simulation/elements/DLAY.cpp
@@ -0,0 +1,111 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DLAY PT_DLAY 79
+Element_DLAY::Element_DLAY()
+{
+ Identifier = "DEFAULT_PT_DLAY";
+ Name = "DLAY";
+ Colour = PIXPACK(0x753590);
+ MenuVisible = 1;
+ MenuSection = SC_POWERED;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = 4.0f+273.15f;
+ HeatConduct = 0;
+ Description = "Conducts with temperature-dependent delay. (use HEAT/COOL).";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_DLAY::update;
+ Graphics = &Element_DLAY::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_DLAY static int update(UPDATE_FUNC_ARGS)
+int Element_DLAY::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, oldl;
+ oldl = parts[i].life;
+ if (parts[i].life>0)
+ parts[i].life--;
+ //if (parts[i].life==1)
+ //{
+ if (parts[i].temp>=MAX_TEMP+273.15f)
+ parts[i].temp = MAX_TEMP+273.15f;
+ if (parts[i].temp<= 1.0f+273.15f)
+ parts[i].temp = 1.0f+273.15f;
+
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r || sim->parts_avg(r>>8, i,PT_INSL)==PT_INSL)
+ continue;
+ if ((r&0xFF)==PT_SPRK && parts[i].life==0 && parts[r>>8].life>0 && parts[r>>8].life<4 && parts[r>>8].ctype==PT_PSCN)
+ {
+ parts[i].life = (int)(parts[i].temp-273.15);
+ }
+ else if ((r&0xFF)==PT_DLAY)
+ {
+ if(!parts[i].life && parts[r>>8].life)
+ {
+ parts[i].life = parts[r>>8].life;
+ if((r>>8)>i) //If the other particle hasn't been life updated
+ parts[i].life--;
+ }
+ else if(parts[i].life && !parts[r>>8].life)
+ {
+ parts[r>>8].life = parts[i].life;
+ if((r>>8)>i) //If the other particle hasn't been life updated
+ parts[r>>8].life++;
+ }
+ }
+ else if((r&0xFF)==PT_NSCN && oldl==1)
+ {
+ sim->create_part(-1, x+rx, y+ry, PT_SPRK);
+ }
+ }
+ //}
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_DLAY static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_DLAY::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int stage = (int)(((float)cpart->life/(cpart->temp-273.15))*100.0f);
+ *colr += stage;
+ *colg += stage;
+ *colb += stage;
+ return 0;
+}
+
+
+Element_DLAY::~Element_DLAY() {} \ No newline at end of file
diff --git a/src/simulation/elements/DMG.cpp b/src/simulation/elements/DMG.cpp
new file mode 100644
index 0000000..9c19199
--- /dev/null
+++ b/src/simulation/elements/DMG.cpp
@@ -0,0 +1,118 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DMG PT_DMG 163
+Element_DMG::Element_DMG()
+{
+ Identifier = "DEFAULT_PT_DMG";
+ Name = "DMG";
+ Colour = PIXPACK(0x88FF88);
+ MenuVisible = 1;
+ MenuSection = SC_FORCE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 30;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 29;
+ Description = "DMG.";
+
+ State = ST_NONE;
+ Properties = TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC|PROP_SPARKSETTLE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_DMG::update;
+ Graphics = &Element_DMG::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_DMG static int update(UPDATE_FUNC_ARGS)
+int Element_DMG::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rr, rx, ry, nb, nxi, nxj, t, dist;
+ int rad = 25;
+ float angle, fx, fy;
+
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_DMG && (r&0xFF)!=PT_EMBR && (r&0xFF)!=PT_DMND && (r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PCLN && (r&0xFF)!=PT_BCLN)
+ {
+ sim->kill_part(i);
+ for (nxj=-rad; nxj<=rad; nxj++)
+ for (nxi=-rad; nxi<=rad; nxi++)
+ if (x+nxi>=0 && y+nxj>=0 && x+nxi<XRES && y+nxj<YRES && (nxi || nxj))
+ {
+ dist = sqrt(pow(nxi, 2.0f)+pow(nxj, 2.0f));//;(pow((float)nxi,2))/(pow((float)rad,2))+(pow((float)nxj,2))/(pow((float)rad,2));
+ if (!dist || (dist <= rad))
+ {
+ rr = pmap[y+nxj][x+nxi];
+ if (rr)
+ {
+ angle = atan2((float)nxj, nxi);
+ fx = cos(angle) * 7.0f;
+ fy = sin(angle) * 7.0f;
+
+ parts[rr>>8].vx += fx;
+ parts[rr>>8].vy += fy;
+
+ sim->vx[(y+nxj)/CELL][(x+nxi)/CELL] += fx;
+ sim->vy[(y+nxj)/CELL][(x+nxi)/CELL] += fy;
+
+ sim->pv[(y+nxj)/CELL][(x+nxi)/CELL] += 1.0f;
+
+ t = parts[rr>>8].type;
+ if(t && sim->elements[t].HighPressureTransition>-1 && sim->elements[t].HighPressureTransition<PT_NUM)
+ sim->part_change_type(rr>>8, x+nxi, y+nxj, sim->elements[t].HighPressureTransition);
+ else if(t == PT_BMTL)
+ sim->part_change_type(rr>>8, x+nxi, y+nxj, PT_BRMT);
+ else if(t == PT_GLAS)
+ sim->part_change_type(rr>>8, x+nxi, y+nxj, PT_BGLA);
+ else if(t == PT_COAL)
+ sim->part_change_type(rr>>8, x+nxi, y+nxj, PT_BCOL);
+ else if(t == PT_QRTZ)
+ sim->part_change_type(rr>>8, x+nxi, y+nxj, PT_PQRT);
+ }
+ }
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_DMG static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_DMG::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *pixel_mode |= PMODE_FLARE;
+ return 1;
+}
+
+
+Element_DMG::~Element_DMG() {}
diff --git a/src/simulation/elements/DMND.cpp b/src/simulation/elements/DMND.cpp
new file mode 100644
index 0000000..3c6d24a
--- /dev/null
+++ b/src/simulation/elements/DMND.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DMND PT_DMND 28
+Element_DMND::Element_DMND()
+{
+ Identifier = "DEFAULT_PT_DMND";
+ Name = "DMND";
+ Colour = PIXPACK(0xCCFFFF);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 186;
+ Description = "Diamond. Indestructible.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+
+}
+
+Element_DMND::~Element_DMND() {} \ No newline at end of file
diff --git a/src/simulation/elements/DRIC.cpp b/src/simulation/elements/DRIC.cpp
new file mode 100644
index 0000000..ea2a3c8
--- /dev/null
+++ b/src/simulation/elements/DRIC.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DRIC PT_DRIC 81
+Element_DRIC::Element_DRIC()
+{
+ Identifier = "DEFAULT_PT_DRIC";
+ Name = "DRIC";
+ Colour = PIXPACK(0xE0E0E0);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = -0.0005f* CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 100;
+
+ Temperature = 172.65f;
+ HeatConduct = 2;
+ Description = "Dry Ice.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 195.65f;
+ HighTemperatureTransition = PT_CO2;
+
+ Update = NULL;
+
+}
+
+Element_DRIC::~Element_DRIC() {} \ No newline at end of file
diff --git a/src/simulation/elements/DSTW.cpp b/src/simulation/elements/DSTW.cpp
new file mode 100644
index 0000000..8b16107
--- /dev/null
+++ b/src/simulation/elements/DSTW.cpp
@@ -0,0 +1,92 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DSTW PT_DSTW 25
+Element_DSTW::Element_DSTW()
+{
+ Identifier = "DEFAULT_PT_DSTW";
+ Name = "DSTW";
+ Colour = PIXPACK(0x1020C0);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 30;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 23;
+ Description = "Distilled water, does not conduct electricity.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_NEUTPENETRATE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 273.15f;
+ LowTemperatureTransition = PT_ICEI;
+ HighTemperature = 373.0f;
+ HighTemperatureTransition = PT_WTRV;
+
+ Update = &Element_DSTW::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_DSTW static int update(UPDATE_FUNC_ARGS)
+int Element_DSTW::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_SALT && 1>(rand()%250))
+ {
+ sim->part_change_type(i,x,y,PT_SLTW);
+ // on average, convert 3 DSTW to SLTW before SALT turns into SLTW
+ if (rand()%3==0)
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SLTW);
+ }
+ if (((r&0xFF)==PT_WATR||(r&0xFF)==PT_SLTW) && 1>(rand()%500))
+ {
+ sim->part_change_type(i,x,y,PT_WATR);
+ }
+ if ((r&0xFF)==PT_SLTW && 1>(rand()%10000))
+ {
+ sim->part_change_type(i,x,y,PT_SLTW);
+ }
+ if (((r&0xFF)==PT_RBDM||(r&0xFF)==PT_LRBD) && (sim->legacy_enable||parts[i].temp>12.0f) && 1>(rand()%500))
+ {
+ sim->part_change_type(i,x,y,PT_FIRE);
+ parts[i].life = 4;
+ }
+ if ((r&0xFF)==PT_FIRE){
+ sim->kill_part(r>>8);
+ if(1>(rand()%150)){
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_DSTW::~Element_DSTW() {} \ No newline at end of file
diff --git a/src/simulation/elements/DTEC.cpp b/src/simulation/elements/DTEC.cpp
new file mode 100644
index 0000000..6d67cf2
--- /dev/null
+++ b/src/simulation/elements/DTEC.cpp
@@ -0,0 +1,99 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DTEC PT_DTEC 162
+Element_DTEC::Element_DTEC()
+{
+ Identifier = "DEFAULT_PT_DTEC";
+ Name = "DTEC";
+ Colour = PIXPACK(0xFD9D18);
+ MenuVisible = 1;
+ MenuSection = SC_SENSOR;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Creates a spark when something with its ctype is nearby";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_DTEC::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_DTEC static int in_radius(int rd, int x, int y)
+int Element_DTEC::in_radius(int rd, int x, int y)
+{
+ return (pow((double)x,2)*pow((double)rd,2)+pow((double)y,2)*pow((double)rd,2)<=pow((double)rd,2)*pow((double)rd,2));
+}
+
+//#TPT-Directive ElementHeader Element_DTEC static int update(UPDATE_FUNC_ARGS)
+int Element_DTEC::update(UPDATE_FUNC_ARGS)
+{
+ int r, rx, ry, rt, rd = parts[i].tmp2;
+ if (rd > 25) parts[i].tmp2 = rd = 25;
+ if (parts[i].life)
+ {
+ parts[i].life = 0;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ rt = parts[r>>8].type;
+ if (sim->parts_avg(i,r>>8,PT_INSL) != PT_INSL)
+ {
+ if ((sim->elements[rt].Properties&PROP_CONDUCTS) && !(rt==PT_WATR||rt==PT_SLTW||rt==PT_NTCT||rt==PT_PTCT||rt==PT_INWR) && parts[r>>8].life==0 && in_radius(rd, rx, ry))
+ {
+ parts[r>>8].life = 4;
+ parts[r>>8].ctype = rt;
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SPRK);
+ }
+ }
+ }
+ }
+ for (rx=-rd; rx<rd+1; rx++)
+ for (ry=-rd; ry<rd+1; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if(!r)
+ r = sim->photons[y+ry][x+rx];
+ if(!r)
+ continue;
+ if (parts[r>>8].type == parts[i].ctype && (parts[i].ctype != PT_LIFE || parts[i].tmp == parts[r>>8].tmp))
+ parts[i].life = 1;
+ }
+ return 0;
+}
+
+
+
+Element_DTEC::~Element_DTEC() {}
diff --git a/src/simulation/elements/DUST.cpp b/src/simulation/elements/DUST.cpp
new file mode 100644
index 0000000..924dabb
--- /dev/null
+++ b/src/simulation/elements/DUST.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DUST PT_DUST 1
+Element_DUST::Element_DUST()
+{
+ Identifier = "DEFAULT_PT_DUST";
+ Name = "DUST";
+ Colour = PIXPACK(0xFFE0A0);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 10;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 85;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Very light dust. Flammable.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+ Graphics = NULL;
+}
+
+Element_DUST::~Element_DUST() {} \ No newline at end of file
diff --git a/src/simulation/elements/DYST.cpp b/src/simulation/elements/DYST.cpp
new file mode 100644
index 0000000..fdf16bb
--- /dev/null
+++ b/src/simulation/elements/DYST.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DYST PT_DYST 64
+Element_DYST::Element_DYST()
+{
+ Identifier = "DEFAULT_PT_DYST";
+ Name = "DYST";
+ Colour = PIXPACK(0xBBB0A0);
+ MenuVisible = 0;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 20;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 80;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Dead Yeast.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 473.0f;
+ HighTemperatureTransition = PT_DUST;
+
+ Update = NULL;
+
+}
+
+Element_DYST::~Element_DYST() {} \ No newline at end of file
diff --git a/src/simulation/elements/ELEC.cpp b/src/simulation/elements/ELEC.cpp
new file mode 100644
index 0000000..c602da8
--- /dev/null
+++ b/src/simulation/elements/ELEC.cpp
@@ -0,0 +1,160 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_ELEC PT_ELEC 136
+Element_ELEC::Element_ELEC()
+{
+ Identifier = "DEFAULT_PT_ELEC";
+ Name = "ELEC";
+ Colour = PIXPACK(0xDFEFFF);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 1.00f;
+ Collision = -0.99f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = -1;
+
+ Temperature = R_TEMP+200.0f+273.15f;
+ HeatConduct = 251;
+ Description = "Electrons";
+
+ State = ST_GAS;
+ Properties = TYPE_ENERGY|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_ELEC::update;
+ Graphics = &Element_ELEC::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_ELEC static int update(UPDATE_FUNC_ARGS)
+int Element_ELEC::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rt, rx, ry, nb, rrx, rry;
+ float rr, rrr;
+ parts[i].pavg[0] = x;
+ parts[i].pavg[1] = y;
+ if(pmap[y][x]==PT_GLOW)
+ {
+ sim->part_change_type(i, x, y, PT_PHOT);
+ }
+ for (rx=-2; rx<=2; rx++)
+ for (ry=-2; ry<=2; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES) {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ r = sim->photons[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_GLAS)
+ {
+ for (rrx=-1; rrx<=1; rrx++)
+ {
+ for (rry=-1; rry<=1; rry++)
+ {
+ if (x+rx+rrx>=0 && y+ry+rry>=0 && x+rx+rrx<XRES && y+ry+rry<YRES) {
+ nb = sim->create_part(-1, x+rx+rrx, y+ry+rry, PT_EMBR);
+ if (nb!=-1) {
+ parts[nb].tmp = 0;
+ parts[nb].life = 50;
+ parts[nb].temp = parts[i].temp*0.8f;
+ parts[nb].vx = rand()%20-10;
+ parts[nb].vy = rand()%20-10;
+ }
+ }
+ }
+ }
+ //fire_r[y/CELL][x/CELL] += rand()%200; //D: Doesn't work with OpenGL, also shouldn't be here
+ //fire_g[y/CELL][x/CELL] += rand()%200;
+ //fire_b[y/CELL][x/CELL] += rand()%200;
+ /* possible alternative, but doesn't work well at the moment because FIRE_ADD divides firea by 8, so the glow isn't strong enough
+ create_part(i, x, y, PT_EMBR);
+ parts[i].tmp = 2;
+ parts[i].life = 2;
+ parts[i].ctype = ((rand()%200)<<16) | ((rand()%200)<<8) | (rand()%200);
+ */
+ sim->kill_part(i);
+ return 1;
+ }
+ if ((r&0xFF)==PT_LCRY)
+ {
+ parts[r>>8].tmp2 = 5+rand()%5;
+ }
+ if ((r&0xFF)==PT_WATR || (r&0xFF)==PT_DSTW || (r&0xFF)==PT_SLTW || (r&0xFF)==PT_CBNW)
+ {
+ if(rand()<RAND_MAX/3)
+ {
+ sim->create_part(r>>8, x+rx, y+ry, PT_O2);
+ return 1;
+ }
+ else
+ {
+ sim->create_part(r>>8, x+rx, y+ry, PT_H2);
+ return 1;
+ }
+ }
+ if ((r&0xFF)==PT_NEUT && !pmap[y+ry][x+rx])
+ {
+ sim->part_change_type(r>>8, x+rx, y+ry, PT_H2);
+ parts[r>>8].life = 0;
+ parts[r>>8].ctype = 0;
+ }
+ if ((r&0xFF)==PT_DEUT)
+ {
+ if(parts[r>>8].life < 6000)
+ parts[r>>8].life += 1;
+ parts[r>>8].temp = 0;
+ sim->kill_part(i);
+ return 1;
+ }
+ if ((r&0xFF)==PT_EXOT)
+ {
+ parts[r>>8].tmp2 += 5;
+ parts[r>>8].life = 1000;
+ }
+ if ((sim->elements[r&0xFF].Properties & PROP_CONDUCTS) && ((r&0xFF)!=PT_NBLE||parts[i].temp<2273.15))
+ {
+ sim->create_part(-1, x+rx, y+ry, PT_SPRK);
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_ELEC static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_ELEC::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *firea = 70;
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+
+ *pixel_mode |= FIRE_ADD;
+ return 0;
+}
+
+
+Element_ELEC::~Element_ELEC() {}
diff --git a/src/simulation/elements/EMBR.cpp b/src/simulation/elements/EMBR.cpp
new file mode 100644
index 0000000..5ba37c2
--- /dev/null
+++ b/src/simulation/elements/EMBR.cpp
@@ -0,0 +1,126 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_EMBR PT_EMBR 147
+Element_EMBR::Element_EMBR()
+{
+
+ //{"EMBR", PIXPACK(0xFFF288), 0.4f, 0.001f * CFDS, 0.99f, 0.90f, 0.0f, 0.07f, 0.00f, 0.000f * CFDS, 1, 0, 0, 0, 20, 0, 1, 30, SC_EXPLOSIVE, 500.0f +273.15f, 29, "Sparks. Formed by explosions.", ST_NONE, TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL|PROP_SPARKSETTLE, &update_EMBR, &graphics_EMBR},
+
+ Identifier = "DEFAULT_PT_EMBR";
+ Name = "EMBR";
+ Colour = PIXPACK(0xFFF288);
+ MenuVisible = 0;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.001f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.90f;
+ Collision = 0.0f;
+ Gravity = 0.07f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 30;
+
+ Temperature = 500.0f +273.15f;
+ HeatConduct = 29;
+ Description = "Sparks. Formed by explosions.";
+
+ State = ST_NONE;
+ Properties = TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL|PROP_SPARKSETTLE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_EMBR::update;
+ Graphics = &Element_EMBR::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_EMBR static int update(UPDATE_FUNC_ARGS)
+int Element_EMBR::update(UPDATE_FUNC_ARGS) {
+ int r, rx, ry, nb;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((sim->elements[r&0xFF].Properties & (TYPE_SOLID | TYPE_PART | TYPE_LIQUID)) && !(sim->elements[r&0xFF].Properties & PROP_SPARKSETTLE))
+ {
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+//#TPT-Directive ElementHeader Element_EMBR static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_EMBR::graphics(GRAPHICS_FUNC_ARGS)
+{
+ if (cpart->ctype&0xFFFFFF)
+ {
+ int maxComponent;
+ *colr = (cpart->ctype&0xFF0000)>>16;
+ *colg = (cpart->ctype&0x00FF00)>>8;
+ *colb = (cpart->ctype&0x0000FF);
+ maxComponent = *colr;
+
+ if (*colg>maxComponent) maxComponent = *colg;
+ if (*colb>maxComponent) maxComponent = *colb;
+ if (maxComponent<60)//make sure it isn't too dark to see
+ {
+ float multiplier = 60.0f/maxComponent;
+ *colr *= multiplier;
+ *colg *= multiplier;
+ *colb *= multiplier;
+ }
+ }
+ else if (cpart->tmp != 0)
+ {
+ *colr = *colg = *colb = 255;
+ }
+
+ if (ren->decorations_enable && cpart->dcolour)
+ {
+ int a = (cpart->dcolour>>24)&0xFF;
+ *colr = (a*((cpart->dcolour>>16)&0xFF) + (255-a)**colr) >> 8;
+ *colg = (a*((cpart->dcolour>>8)&0xFF) + (255-a)**colg) >> 8;
+ *colb = (a*((cpart->dcolour)&0xFF) + (255-a)**colb) >> 8;
+ }
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+ if (cpart->tmp==1)
+ {
+ *pixel_mode = FIRE_ADD | PMODE_BLEND | PMODE_GLOW;
+ *firea = (cpart->life-15)*4;
+ *cola = (cpart->life+15)*4;
+ }
+ else if (cpart->tmp==2)
+ {
+ *pixel_mode = PMODE_FLAT | FIRE_ADD;
+ *firea = 255;
+ }
+ else
+ {
+ *pixel_mode = PMODE_SPARK | PMODE_ADD;
+ if (cpart->life<64) *cola = 4*cpart->life;
+ }
+ return 0;
+}
+
+Element_EMBR::~Element_EMBR() {}
diff --git a/src/simulation/elements/EMP.cpp b/src/simulation/elements/EMP.cpp
new file mode 100644
index 0000000..e21f69c
--- /dev/null
+++ b/src/simulation/elements/EMP.cpp
@@ -0,0 +1,183 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_EMP PT_EMP 134
+Element_EMP::Element_EMP()
+{
+ Identifier = "DEFAULT_PT_EMP";
+ Name = "EMP";
+ Colour = PIXPACK(0x66AAFF);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.0f;
+ HotAir = 0.0f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 3;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 121;
+ Description = "Breaks activated electronics.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_EMP::update;
+ Graphics = &Element_EMP::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_EMP static int update(UPDATE_FUNC_ARGS)
+int Element_EMP::update(UPDATE_FUNC_ARGS)
+ {
+ int r,rx,ry,ok=0,t,n,nx,ny;
+ if (parts[i].life)
+ return 0;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_SPRK && parts[r>>8].life>0 && parts[r>>8].life<4)
+ {
+ ok=1;
+ break;
+ }
+ }
+ if (!ok)
+ return 0;
+ parts[i].life=220;
+ sim->emp_decor += 3;
+ if (sim->emp_decor > 40)
+ sim->emp_decor = 40;
+ for (r=0; r<=sim->parts_lastActiveIndex; r++)
+ {
+ t=parts[r].type;
+ rx=parts[r].x;
+ ry=parts[r].y;
+ if (t==PT_SPRK || (t==PT_SWCH && parts[r].life!=0 && parts[r].life!=10) || (t==PT_WIRE && parts[r].ctype>0))
+ {
+ int is_elec=0;
+ if ((parts[r].ctype==PT_PSCN || parts[r].ctype==PT_NSCN || parts[r].ctype==PT_PTCT ||
+ parts[r].ctype==PT_NTCT || parts[r].ctype==PT_INST || parts[r].ctype==PT_SWCH) || t==PT_WIRE || t==PT_SWCH)
+ {
+ is_elec=1;
+ if (sim->elements[parts[r].type].HeatConduct && rand()%100==0)
+ parts[r].temp = restrict_flt(parts[r].temp+3000.0f, MIN_TEMP, MAX_TEMP);
+ if (rand()%80==0)
+ sim->part_change_type(r, rx, ry, PT_BREC);
+ else if (rand()%120==0)
+ sim->part_change_type(r, rx, ry, PT_NTCT);
+ }
+
+ for (nx=-2; nx<3; nx++)
+ for (ny=-2; ny<3; ny++)
+ if (rx+nx>=0 && ry+ny>=0 && rx+nx<XRES && ry+ny<YRES && (rx || ry))
+ {
+ n = pmap[ry+ny][rx+nx];
+ if (!n)
+ continue;
+ /*if ((n&0xFF)==PT_BTRY && rand()%60==0)
+ {
+ part_change_type(n>>8, rx+nx, ry+ny, PT_PLSM);
+ parts[n>>8].life=rand()%100+70;
+ parts[n>>8].temp+=3000;
+ }*/
+
+ //Some elements should only be affected by wire/swch, or by a spark on inst/semiconductor
+ //So not affected by spark on metl, watr etc
+ if (is_elec)
+ {
+ if (((n&0xFF)==PT_METL || (n&0xFF)==PT_BMTL) && rand()%280==0)
+ {
+ parts[n>>8].temp = restrict_flt(parts[n>>8].temp+3000.0f, MIN_TEMP, MAX_TEMP);
+ }
+ if ((n&0xFF)==PT_BMTL && rand()%160==0)
+ {
+ sim->part_change_type(n>>8, rx+nx, ry+ny, PT_BMTL);//TODO: Redundant, was this meant to be BRMT or something?
+ parts[n>>8].temp = restrict_flt(parts[n>>8].temp+1000.0f, MIN_TEMP, MAX_TEMP);
+ }
+ if ((n&0xFF)==PT_METL && rand()%300==0)
+ {
+ sim->part_change_type(n>>8, rx+nx, ry+ny, PT_BMTL);
+ }
+ if ((n&0xFF)==PT_WIFI && rand()%8==0)
+ {
+ //Randomise channel
+ parts[n>>8].temp = rand()%MAX_TEMP;
+ }
+ if ((n&0xFF)==PT_WIFI && rand()%16==0)
+ {
+ sim->create_part(n>>8, rx+nx, ry+ny, PT_BREC);
+ parts[n>>8].temp = restrict_flt(parts[n>>8].temp+1000.0f, MIN_TEMP, MAX_TEMP);
+ }
+ }
+ if ((n&0xFF)==PT_SWCH && rand()%100==0)
+ {
+ sim->part_change_type(n>>8, rx+nx, ry+ny, PT_BREC);
+ }
+ if ((n&0xFF)==PT_SWCH && rand()%100==0)
+ {
+ parts[n>>8].temp = restrict_flt(parts[n>>8].temp+2000.0f, MIN_TEMP, MAX_TEMP);
+ }
+ if ((n&0xFF)==PT_ARAY && rand()%60==0)
+ {
+ sim->create_part(n>>8, rx+nx, ry+ny, PT_BREC);
+ parts[n>>8].temp = restrict_flt(parts[n>>8].temp+1000.0f, MIN_TEMP, MAX_TEMP);
+ }
+ if (t==PT_DLAY && rand()%70==0)
+ {
+ //Randomise delay
+ parts[n>>8].temp = (rand()%256) + 273.15f;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_EMP static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_EMP::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ if(cpart->life)
+ {
+ *colr = cpart->life*1.5;
+ *colg = cpart->life*1.5;
+ *colb = 200-(cpart->life);
+ if (*colr>255)
+ *colr = 255;
+ if (*colg>255)
+ *colg = 255;
+ if (*colb>255)
+ *colb = 255;
+ if (*colb<=0)
+ *colb = 0;
+ }
+ return 0;
+}
+
+
+Element_EMP::~Element_EMP() {} \ No newline at end of file
diff --git a/src/simulation/elements/ETRD.cpp b/src/simulation/elements/ETRD.cpp
new file mode 100644
index 0000000..3a63f6f
--- /dev/null
+++ b/src/simulation/elements/ETRD.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_ETRD PT_ETRD 50
+Element_ETRD::Element_ETRD()
+{
+ Identifier = "DEFAULT_PT_ETRD";
+ Name = "ETRD";
+ Colour = PIXPACK(0x404040);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Electrode. Creates a surface that allows Plasma arcs. (Use sparingly)";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+
+}
+
+Element_ETRD::~Element_ETRD() {} \ No newline at end of file
diff --git a/src/simulation/elements/EXOT.cpp b/src/simulation/elements/EXOT.cpp
new file mode 100644
index 0000000..03eca2f
--- /dev/null
+++ b/src/simulation/elements/EXOT.cpp
@@ -0,0 +1,211 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_EXOT PT_EXOT 145
+Element_EXOT::Element_EXOT()
+{
+ Identifier = "DEFAULT_PT_EXOT";
+ Name = "EXOT";
+ Colour = PIXPACK(0x404040);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.3f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.15f;
+ Diffusion = 0.00f;
+ HotAir = 0.0003f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 2;
+
+ Weight = 46;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 250;
+ Description = "Exotic matter. Explodes with excess exposure to electrons.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_EXOT::update;
+ Graphics = &Element_EXOT::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_EXOT static int update(UPDATE_FUNC_ARGS)
+int Element_EXOT::update(UPDATE_FUNC_ARGS) {
+ int r, rt, rx, ry, nb, rrx, rry, trade, tym, t;
+ t = parts[i].type;
+ for (rx=-2; rx<=2; rx++)
+ for (ry=-2; ry<=2; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES) {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF) == PT_WARP)
+ {
+ if (parts[r>>8].tmp2>2000)
+ if (1>rand()%100)
+ {
+ parts[i].tmp2 += 100;
+ }
+ }
+ else if ((r&0xFF) == PT_EXOT && parts[r>>8].life == 1500 && 1>rand()%1000)
+ parts[i].life = 1500;
+ else if ((r&0xFF) == PT_LAVA)
+ {
+ if (parts[r>>8].ctype == PT_TTAN && 1>rand()%10)
+ {
+ parts[r>>8].ctype = PT_VIBR;
+ sim->kill_part(i);
+ return 1;
+ }
+ /*else if (parts[r>>8].ctype == PT_VIBR && 1>rand()%1000)
+ {
+ sim->kill_part(i);
+ return 1;
+ }*/
+ }
+ if ((parts[i].tmp>245) && (parts[i].life>1000))
+ if ((r&0xFF)!=PT_EXOT && (r&0xFF)!=PT_BREC && (r&0xFF)!=PT_DMND && (r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PRTI && (r&0xFF)!=PT_PRTO && (r&0xFF)!=PT_PCLN && (r&0xFF)!=PT_PHOT && (r&0xFF)!=PT_VOID && (r&0xFF)!=PT_NBHL && (r&0xFF)!=PT_WARP && (r&0xFF)!=PT_NEUT)
+ {
+ sim->create_part(i, x, y, parts[r>>8].type);
+ return 0;
+ }
+ }
+ parts[i].tmp--;
+ parts[i].tmp2--;
+ if (parts[i].tmp<1 || parts[i].tmp>250)
+ parts[i].tmp = 250;
+ if (parts[i].tmp2<1)
+ parts[i].tmp2 = 1;
+ else if (parts[i].tmp2>6000)
+ {
+ parts[i].tmp2 = 10000;
+ if (parts[i].life<1001)
+ {
+ sim->part_change_type(i, x, y, PT_WARP);
+ return 0;
+ }
+ }
+ else if(parts[i].life<1001)
+ sim->pv[y/CELL][x/CELL] += (parts[i].tmp2*CFDS)/160000;
+ if (sim->pv[y/CELL][x/CELL]>200 && parts[i].temp>9000 && parts[i].tmp2>200)
+ {
+ parts[i].tmp2 = 6000;
+ sim->part_change_type(i, x, y, PT_WARP);
+ return 0;
+ }
+ if (parts[i].tmp2>100)
+ {
+ for ( trade = 0; trade<9; trade ++)
+ {
+ rx = rand()%5-2;
+ ry = rand()%5-2;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==t && (parts[i].tmp2>parts[r>>8].tmp2) && parts[r>>8].tmp2>=0 )//diffusion
+ {
+ tym = parts[i].tmp2 - parts[r>>8].tmp2;
+ if (tym ==1)
+ {
+ parts[r>>8].tmp2 ++;
+ parts[i].tmp2 --;
+ break;
+ }
+ if (tym>0)
+ {
+ parts[r>>8].tmp2 += tym/2;
+ parts[i].tmp2 -= tym/2;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (parts[i].temp<273.15f)
+ {
+ parts[i].vx = 0;
+ parts[i].vy = 0;
+ sim->pv[y/CELL][x/CELL] -= 0.01;
+ parts[i].tmp--;
+ }
+ return 0;
+
+}
+
+//#TPT-Directive ElementHeader Element_EXOT static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_EXOT::graphics(GRAPHICS_FUNC_ARGS)
+{
+ int q = cpart->temp;
+ int b = cpart->tmp;
+ int c = cpart->tmp2;
+ if (cpart->life < 1001)
+ {
+ if ((cpart->tmp2 - 1)>rand()%1000)
+ {
+ float frequency = 0.04045;
+ *colr = (sin(frequency*c + 4) * 127 + 150);
+ *colg = (sin(frequency*c + 6) * 127 + 150);
+ *colb = (sin(frequency*c + 8) * 127 + 150);
+
+ *firea = 100;
+ *firer = 0;
+ *fireg = 0;
+ *fireb = 0;
+
+ *pixel_mode |= PMODE_FLAT;
+ *pixel_mode |= PMODE_FLARE;
+ }
+ else
+ {
+ float frequency = 0.00045;
+ *colr = (sin(frequency*q + 4) * 127 + (b/1.7));
+ *colg = (sin(frequency*q + 6) * 127 + (b/1.7));
+ *colb = (sin(frequency*q + 8) * 127 + (b/1.7));
+ *cola = cpart->tmp / 6;
+
+ *firea = *cola;
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+
+ *pixel_mode |= FIRE_ADD;
+ *pixel_mode |= PMODE_BLUR;
+ }
+ }
+ else
+ {
+ float frequency = 0.01300;
+ *colr = (sin(frequency*q + 6.00) * 127 + ((b/2.9) + 80));
+ *colg = (sin(frequency*q + 6.00) * 127 + ((b/2.9) + 80));
+ *colb = (sin(frequency*q + 6.00) * 127 + ((b/2.9) + 80));
+ *cola = cpart->tmp / 6;
+ *firea = *cola;
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+ *pixel_mode |= FIRE_ADD;
+ *pixel_mode |= PMODE_BLUR;
+ }
+ return 0;
+}
+
+Element_EXOT::~Element_EXOT() {}
diff --git a/src/simulation/elements/Element.cpp b/src/simulation/elements/Element.cpp
new file mode 100644
index 0000000..9c1002a
--- /dev/null
+++ b/src/simulation/elements/Element.cpp
@@ -0,0 +1,225 @@
+#include "simulation/Elements.h"
+#include "simulation/StructProperty.h"
+
+Element::Element():
+ Identifier("DEFAULT_INVALID"),
+ Name(""),
+ Colour(PIXPACK(0xFF00FF)),
+ MenuVisible(0),
+ MenuSection(0),
+ Enabled(0),
+
+ Advection(0.0f),
+ AirDrag(-0.0f * CFDS),
+ AirLoss(1.0f),
+ Loss(1.0f),
+ Collision(0.0f),
+ Gravity(0.0f),
+ Diffusion(0.0f),
+ HotAir(0.0f * CFDS),
+ Falldown(0),
+
+ Flammable(0),
+ Explosive(0),
+ Meltable(0),
+ Hardness(30),
+
+ Weight(50),
+
+ Temperature(273.15f),
+ HeatConduct(128),
+ Description("No description"),
+
+ State(ST_SOLID),
+ Properties(TYPE_SOLID),
+
+ LowPressure(IPL),
+ LowPressureTransition(NT),
+ HighPressure(IPH),
+ HighPressureTransition(NT),
+ LowTemperature(ITL),
+ LowTemperatureTransition(NT),
+ HighTemperature(ITH),
+ HighTemperatureTransition(NT),
+
+ Update(NULL),
+ Graphics(&Element::defaultGraphics),
+ IconGenerator(NULL)
+{
+}
+
+std::vector<StructProperty> Element::GetProperties()
+{
+ std::vector<StructProperty> properties;
+ properties.push_back(StructProperty("Name", StructProperty::String, offsetof(Element, Name)));
+ properties.push_back(StructProperty("Colour", StructProperty::Colour, offsetof(Element, Colour)));
+ properties.push_back(StructProperty("MenuVisible", StructProperty::Integer, offsetof(Element, MenuVisible)));
+ properties.push_back(StructProperty("MenuSection", StructProperty::Integer, offsetof(Element, MenuSection)));
+ properties.push_back(StructProperty("Advection", StructProperty::Float, offsetof(Element, Advection)));
+ properties.push_back(StructProperty("AirDrag", StructProperty::Float, offsetof(Element, AirDrag)));
+ properties.push_back(StructProperty("AirLoss", StructProperty::Float, offsetof(Element, AirLoss)));
+ properties.push_back(StructProperty("Loss", StructProperty::Float, offsetof(Element, Loss)));
+ properties.push_back(StructProperty("Collision", StructProperty::Float, offsetof(Element, Collision)));
+ properties.push_back(StructProperty("Gravity", StructProperty::Float, offsetof(Element, Gravity)));
+ properties.push_back(StructProperty("Diffusion", StructProperty::Float, offsetof(Element, Diffusion)));
+ properties.push_back(StructProperty("HotAir", StructProperty::Float, offsetof(Element, HotAir)));
+ properties.push_back(StructProperty("Falldown", StructProperty::Integer, offsetof(Element, Falldown)));
+ properties.push_back(StructProperty("Flammable", StructProperty::Integer, offsetof(Element, Flammable)));
+ properties.push_back(StructProperty("Explosive", StructProperty::Integer, offsetof(Element, Explosive)));
+ properties.push_back(StructProperty("Meltable", StructProperty::Integer, offsetof(Element, Meltable)));
+ properties.push_back(StructProperty("Hardness", StructProperty::Integer, offsetof(Element, Hardness)));
+ properties.push_back(StructProperty("Weight", StructProperty::Integer, offsetof(Element, Weight)));
+ properties.push_back(StructProperty("Temperature", StructProperty::Float, offsetof(Element, Temperature)));
+ properties.push_back(StructProperty("HeatConduct", StructProperty::UChar, offsetof(Element, HeatConduct)));
+ properties.push_back(StructProperty("Description", StructProperty::String, offsetof(Element, Description)));
+ properties.push_back(StructProperty("State", StructProperty::Char, offsetof(Element, State)));
+ properties.push_back(StructProperty("Properties", StructProperty::Integer, offsetof(Element, Properties)));
+ properties.push_back(StructProperty("LowPressure", StructProperty::Float, offsetof(Element, LowPressure)));
+ properties.push_back(StructProperty("LowPressureTransition", StructProperty::Integer, offsetof(Element, LowPressureTransition)));
+ properties.push_back(StructProperty("HighPressure", StructProperty::Float, offsetof(Element, HighPressure)));
+ properties.push_back(StructProperty("HighPressureTransition", StructProperty::Integer, offsetof(Element, HighPressureTransition)));
+ properties.push_back(StructProperty("LowTemperature", StructProperty::Float, offsetof(Element, LowTemperature)));
+ properties.push_back(StructProperty("LowTemperatureTransition", StructProperty::Integer, offsetof(Element, LowTemperatureTransition)));
+ properties.push_back(StructProperty("HighTemperature", StructProperty::Float, offsetof(Element, HighTemperature)));
+ properties.push_back(StructProperty("HighTemperatureTransition", StructProperty::Integer, offsetof(Element, HighTemperatureTransition)));
+ return properties;
+}
+
+int Element::legacyUpdate(UPDATE_FUNC_ARGS) {
+ int r, rx, ry, rt;
+ int t = parts[i].type;
+ if (t==PT_WTRV) {
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 &&
+ x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_WATR||(r&0xFF)==PT_DSTW||(r&0xFF)==PT_SLTW) && 1>(rand()%1000))
+ {
+ sim->part_change_type(i,x,y,PT_WATR);
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_WATR);
+ }
+ if (((r&0xFF)==PT_ICEI || (r&0xFF)==PT_SNOW) && 1>(rand()%1000))
+ {
+ sim->part_change_type(i,x,y,PT_WATR);
+ if (1>(rand()%1000))
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_WATR);
+ }
+ }
+ }
+ else if (t==PT_WATR) {
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 &&
+ x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_FIRE || (r&0xFF)==PT_LAVA) && 1>(rand()%10))
+ {
+ sim->part_change_type(i,x,y,PT_WTRV);
+ }
+ }
+ }
+ else if (t==PT_SLTW) {
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 &&
+ x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_FIRE || (r&0xFF)==PT_LAVA) && 1>(rand()%10))
+ {
+ if (rand()%4==0) sim->part_change_type(i,x,y,PT_SALT);
+ else sim->part_change_type(i,x,y,PT_WTRV);
+ }
+ }
+ }
+ else if (t==PT_DSTW) {
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 &&
+ x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_FIRE || (r&0xFF)==PT_LAVA) && 1>(rand()%10))
+ {
+ sim->part_change_type(i,x,y,PT_WTRV);
+ }
+ }
+ }
+ else if (t==PT_ICEI) {
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_WATR || (r&0xFF)==PT_DSTW) && 1>(rand()%1000))
+ {
+ sim->part_change_type(i,x,y,PT_ICEI);
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_ICEI);
+ }
+ }
+ }
+ else if (t==PT_SNOW) {
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_WATR || (r&0xFF)==PT_DSTW) && 1>(rand()%1000))
+ {
+ sim->part_change_type(i,x,y,PT_ICEI);
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_ICEI);
+ }
+ if (((r&0xFF)==PT_WATR || (r&0xFF)==PT_DSTW) && 15>(rand()%1000))
+ sim->part_change_type(i,x,y,PT_WATR);
+ }
+ }
+ if (t==PT_WTRV && sim->pv[y/CELL][x/CELL]>4.0f)
+ sim->part_change_type(i,x,y,PT_DSTW);
+ if (t==PT_OIL && sim->pv[y/CELL][x/CELL]<-6.0f)
+ sim->part_change_type(i,x,y,PT_GAS);
+ if (t==PT_GAS && sim->pv[y/CELL][x/CELL]>6.0f)
+ sim->part_change_type(i,x,y,PT_OIL);
+ if (t==PT_DESL && sim->pv[y/CELL][x/CELL]>12.0f)
+ {
+ sim->part_change_type(i,x,y,PT_FIRE);
+ parts[i].life = rand()%50+120;
+ }
+ return 0;
+}
+
+int Element::defaultGraphics(GRAPHICS_FUNC_ARGS)
+{
+ int t = cpart->type;
+ //Property based defaults
+ if(ren->sim->elements[t].Properties & PROP_RADIOACTIVE) *pixel_mode |= PMODE_GLOW;
+ if(ren->sim->elements[t].Properties & TYPE_LIQUID)
+ {
+ *pixel_mode |= PMODE_BLUR;
+ }
+ if(ren->sim->elements[t].Properties & TYPE_GAS)
+ {
+ *pixel_mode &= ~PMODE;
+ *pixel_mode |= FIRE_BLEND;
+ *firer = *colr/2;
+ *fireg = *colg/2;
+ *fireb = *colb/2;
+ *firea = 125;
+ *pixel_mode |= DECO_FIRE;
+ }
+ return 1;
+}
diff --git a/src/simulation/elements/Element.h b/src/simulation/elements/Element.h
new file mode 100644
index 0000000..b51bddc
--- /dev/null
+++ b/src/simulation/elements/Element.h
@@ -0,0 +1,63 @@
+#ifndef ELEMENTCLASS_H
+#define ELEMENTCLASS_H
+
+#include "simulation/Simulation.h"
+#include "graphics/Renderer.h"
+#include "simulation/Elements.h"
+#include "simulation/StructProperty.h"
+
+class Simulation;
+class Renderer;
+struct Particle;
+class Element
+{
+public:
+ char *Identifier;
+ char *Name;
+ pixel Colour;
+ float Advection;
+ float AirDrag;
+ float AirLoss;
+ float Loss;
+ float Collision;
+ float Gravity;
+ float Diffusion;
+ float HotAir;
+ int Falldown;
+ int Flammable;
+ int Explosive;
+ int Meltable;
+ int Hardness;
+ int MenuVisible;
+ int Enabled;
+ int Weight;
+ int MenuSection;
+ float Temperature;
+ unsigned char HeatConduct;
+ char *Description;
+ char State;
+ unsigned int Properties;
+ int (*Update) (UPDATE_FUNC_ARGS);
+ int (*Graphics) (GRAPHICS_FUNC_ARGS);
+ VideoBuffer * (*IconGenerator)(int, int, int);
+
+ float HighPressure;
+ int HighPressureTransition;
+ float LowPressure;
+ int LowPressureTransition;
+ float HighTemperature;
+ int HighTemperatureTransition;
+ float LowTemperature;
+ int LowTemperatureTransition;
+
+ Element();
+ virtual ~Element() {}
+ static int defaultGraphics(GRAPHICS_FUNC_ARGS);
+ static int legacyUpdate(UPDATE_FUNC_ARGS);
+
+ /** Returns a list of properties, their type and offset within the structure that can be changed
+ by higher-level processes refering to them by name such as Lua or the property tool **/
+ static std::vector<StructProperty> GetProperties();
+};
+
+#endif
diff --git a/src/simulation/elements/FIGH.cpp b/src/simulation/elements/FIGH.cpp
new file mode 100644
index 0000000..66be5bd
--- /dev/null
+++ b/src/simulation/elements/FIGH.cpp
@@ -0,0 +1,140 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_FIGH PT_FIGH 158
+Element_FIGH::Element_FIGH()
+{
+ Identifier = "DEFAULT_PT_FIGH";
+ Name = "FIGH";
+ Colour = PIXPACK(0xFFE0A0);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.5f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.2f;
+ Loss = 1.0f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.0f;
+ HotAir = 0.00f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 50;
+
+ Temperature = R_TEMP+14.6f+273.15f;
+ HeatConduct = 0;
+ Description = "Fighter. Tries to kill stickmen.";
+
+ State = ST_NONE;
+ Properties = 0;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 620.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = &Element_FIGH::update;
+ Graphics = &Element_STKM::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_FIGH static int update(UPDATE_FUNC_ARGS)
+int Element_FIGH::update(UPDATE_FUNC_ARGS)
+
+{
+ playerst* figh = &sim->fighters[(unsigned char)parts[i].tmp];
+
+ unsigned int tarx, tary;
+
+ parts[i].tmp2 = 0; //0 - stay in place, 1 - seek a stick man
+
+ //Set target cords
+ if (sim->player.spwn && sim->player2.spwn)
+ {
+ if ((pow((float)sim->player.legs[2]-x, 2) + pow((float)sim->player.legs[3]-y, 2))<=
+ (pow((float)sim->player2.legs[2]-x, 2) + pow((float)sim->player2.legs[3]-y, 2)))
+ {
+ tarx = (unsigned int)sim->player.legs[2];
+ tary = (unsigned int)sim->player.legs[3];
+ }
+ else
+ {
+ tarx = (unsigned int)sim->player2.legs[2];
+ tary = (unsigned int)sim->player2.legs[3];
+ }
+ parts[i].tmp2 = 1;
+ }
+ else
+ {
+ if (sim->player.spwn)
+ {
+ tarx = (unsigned int)sim->player.legs[2];
+ tary = (unsigned int)sim->player.legs[3];
+ parts[i].tmp2 = 1;
+ }
+ if (sim->player2.spwn)
+ {
+ tarx = (unsigned int)sim->player2.legs[2];
+ tary = (unsigned int)sim->player2.legs[3];
+ parts[i].tmp2 = 1;
+ }
+ }
+
+ switch (parts[i].tmp2)
+ {
+ case 1:
+ if ((pow(float(tarx-x), 2) + pow(float(tary-y), 2))<600)
+ {
+ if (figh->elem == PT_LIGH || figh->elem == PT_NEUT
+ || sim->elements[figh->elem].Properties&(PROP_DEADLY|PROP_RADIOACTIVE)
+ || sim->elements[figh->elem].Temperature>=323 || sim->elements[figh->elem].Temperature<=243)
+ figh->comm = (int)figh->comm | 0x08;
+ }
+ else
+ if (tarx<x)
+ {
+ if(!(sim->eval_move(PT_FIGH, figh->legs[4]-10, figh->legs[5]+6, NULL)
+ && sim->eval_move(PT_FIGH, figh->legs[4]-10, figh->legs[5]+3, NULL)))
+ figh->comm = 0x01;
+ else
+ figh->comm = 0x02;
+
+ if (!sim->eval_move(PT_FIGH, figh->legs[4]-4, figh->legs[5]-1, NULL)
+ || !sim->eval_move(PT_FIGH, figh->legs[12]-4, figh->legs[13]-1, NULL)
+ || sim->eval_move(PT_FIGH, 2*figh->legs[4]-figh->legs[6], figh->legs[5]+5, NULL))
+ figh->comm = (int)figh->comm | 0x04;
+ }
+ else
+ {
+ if (!(sim->eval_move(PT_FIGH, figh->legs[12]+10, figh->legs[13]+6, NULL)
+ && sim->eval_move(PT_FIGH, figh->legs[12]+10, figh->legs[13]+3, NULL)))
+ figh->comm = 0x02;
+ else
+ figh->comm = 0x01;
+
+ if (!sim->eval_move(PT_FIGH, figh->legs[4]+4, figh->legs[5]-1, NULL)
+ || !sim->eval_move(PT_FIGH, figh->legs[4]+4, figh->legs[5]-1, NULL)
+ || sim->eval_move(PT_FIGH, 2*figh->legs[12]-figh->legs[14], figh->legs[13]+5, NULL))
+ figh->comm = (int)figh->comm | 0x04;
+ }
+ break;
+ default:
+ figh->comm = 0;
+ break;
+ }
+
+ figh->pcomm = figh->comm;
+
+ Element_STKM::run_stickman(figh, UPDATE_FUNC_SUBCALL_ARGS);
+ return 0;
+}
+
+Element_FIGH::~Element_FIGH() {} \ No newline at end of file
diff --git a/src/simulation/elements/FILT.cpp b/src/simulation/elements/FILT.cpp
new file mode 100644
index 0000000..58501c2
--- /dev/null
+++ b/src/simulation/elements/FILT.cpp
@@ -0,0 +1,77 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_FILT PT_FILT 125
+Element_FILT::Element_FILT()
+{
+ Identifier = "DEFAULT_PT_FILT";
+ Name = "FILT";
+ Colour = PIXPACK(0x000056);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Filter for photons, changes the color.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID | PROP_NOAMBHEAT;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+ Graphics = &Element_FILT::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_FILT static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_FILT::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int x, temp_bin = (int)((cpart->temp-273.0f)*0.025f);
+ if (temp_bin < 0) temp_bin = 0;
+ if (temp_bin > 25) temp_bin = 25;
+ cpart->ctype = 0x1F << temp_bin;
+ *colg = 0;
+ *colb = 0;
+ *colr = 0;
+ for (x=0; x<12; x++) {
+ *colr += (cpart->ctype >> (x+18)) & 1;
+ *colb += (cpart->ctype >> x) & 1;
+ }
+ for (x=0; x<12; x++)
+ *colg += (cpart->ctype >> (x+9)) & 1;
+ x = 624/(*colr+*colg+*colb+1);
+ *cola = 127;
+ *colr *= x;
+ *colg *= x;
+ *colb *= x;
+ *pixel_mode &= ~PMODE;
+ *pixel_mode |= PMODE_BLEND;
+ return 0;
+}
+
+
+Element_FILT::~Element_FILT() {}
diff --git a/src/simulation/elements/FIRE.cpp b/src/simulation/elements/FIRE.cpp
new file mode 100644
index 0000000..f9313fa
--- /dev/null
+++ b/src/simulation/elements/FIRE.cpp
@@ -0,0 +1,201 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_FIRE PT_FIRE 4
+Element_FIRE::Element_FIRE()
+{
+ Identifier = "DEFAULT_PT_FIRE";
+ Name = "FIRE";
+ Colour = PIXPACK(0xFF1000);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.9f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.97f;
+ Loss = 0.20f;
+ Collision = 0.0f;
+ Gravity = -0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.001f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 2;
+
+ Temperature = R_TEMP+400.0f+273.15f;
+ HeatConduct = 88;
+ Description = "Ignites flammable materials. Heats air.";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS|PROP_LIFE_DEC|PROP_LIFE_KILL;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 2773.0f;
+ HighTemperatureTransition = PT_PLSM;
+
+ Update = &Element_FIRE::update;
+ Graphics = &Element_FIRE::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_FIRE static int update(UPDATE_FUNC_ARGS)
+int Element_FIRE::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, rt, t = parts[i].type;
+ if (t==PT_PLSM&&parts[i].ctype == PT_NBLE&&parts[i].life <=1)
+ {
+ t = PT_NBLE;
+ sim->part_change_type(i,x,y,t);
+ parts[i].life = 0;
+ }
+ if(t==PT_FIRE && parts[i].life <=1)
+ {
+ if ((parts[i].tmp&0x3) == 3){
+ t = PT_DSTW;
+ sim->part_change_type(i,x,y,t);
+ parts[i].life = 0;
+ parts[i].ctype = PT_FIRE;
+ }
+ else if (parts[i].temp<625)
+ {
+ t = PT_SMKE;
+ sim->part_change_type(i,x,y,t);
+ parts[i].life = rand()%20+250;
+ }
+ }
+ if(t==PT_PLSM && parts[i].life <=1)
+ {
+ if ((parts[i].tmp&0x3) == 3){
+ t = PT_DSTW;
+ sim->part_change_type(i,x,y,t);
+ parts[i].life = 0;
+ parts[i].ctype = PT_FIRE;
+ }
+ }
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (sim->bmap[(y+ry)/CELL][(x+rx)/CELL] && sim->bmap[(y+ry)/CELL][(x+rx)/CELL]!=WL_STREAM)
+ continue;
+ rt = parts[r>>8].type;
+ if ((surround_space || sim->elements[rt].Explosive) &&
+ (t!=PT_SPRK || (rt!=PT_RBDM && rt!=PT_LRBD && rt!=PT_INSL)) &&
+ (t!=PT_PHOT || rt!=PT_INSL) &&
+ (rt!=PT_SPNG || parts[r>>8].life==0) &&
+ sim->elements[rt].Flammable && (sim->elements[rt].Flammable + (int)(sim->pv[(y+ry)/CELL][(x+rx)/CELL]*10.0f))>(rand()%1000))
+ {
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_FIRE);
+ parts[r>>8].temp = restrict_flt(sim->elements[PT_FIRE].Temperature + (sim->elements[rt].Flammable/2), MIN_TEMP, MAX_TEMP);
+ parts[r>>8].life = rand()%80+180;
+ parts[r>>8].tmp = parts[r>>8].ctype = 0;
+ if (sim->elements[rt].Explosive)
+ sim->pv[y/CELL][x/CELL] += 0.25f * CFDS;
+ }
+ }
+ if (sim->legacy_enable) updateLegacy(UPDATE_FUNC_SUBCALL_ARGS);
+ return 0;
+}
+
+//#TPT-Directive ElementHeader Element_FIRE static int updateLegacy(UPDATE_FUNC_ARGS)
+int Element_FIRE::updateLegacy(UPDATE_FUNC_ARGS) {
+ int r, rx, ry, rt, lpv, t = parts[i].type;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (sim->bmap[(y+ry)/CELL][(x+rx)/CELL] && sim->bmap[(y+ry)/CELL][(x+rx)/CELL]!=WL_STREAM)
+ continue;
+ rt = r&0xFF;
+ lpv = (int)sim->pv[(y+ry)/CELL][(x+rx)/CELL];
+ if (lpv < 1) lpv = 1;
+ if (t!=PT_SPRK && sim->elements[rt].Meltable && ((rt!=PT_RBDM && rt!=PT_LRBD) || t!=PT_SPRK) && ((t!=PT_FIRE&&t!=PT_PLSM) || (rt!=PT_METL && rt!=PT_IRON && rt!=PT_ETRD && rt!=PT_PSCN && rt!=PT_NSCN && rt!=PT_NTCT && rt!=PT_PTCT && rt!=PT_BMTL && rt!=PT_BRMT && rt!=PT_SALT && rt!=PT_INWR)) &&
+ sim->elements[rt].Meltable*lpv>(rand()%1000))
+ {
+ if (t!=PT_LAVA || parts[i].life>0)
+ {
+ parts[r>>8].ctype = (rt==PT_BRMT)?PT_BMTL:parts[r>>8].type;
+ parts[r>>8].ctype = (parts[r>>8].ctype==PT_SAND)?PT_GLAS:parts[r>>8].ctype;
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_LAVA);
+ parts[r>>8].life = rand()%120+240;
+ }
+ else
+ {
+ parts[i].life = 0;
+ t = parts[i].type = (parts[i].ctype)?parts[i].ctype:PT_STNE;
+ parts[i].ctype = PT_NONE;//rt;
+ sim->part_change_type(i,x,y,t);
+ return 1;
+ }
+ }
+ if (t!=PT_SPRK && (rt==PT_ICEI || rt==PT_SNOW))
+ {
+ parts[r>>8].type = PT_WATR;
+ if (t==PT_FIRE)
+ {
+ sim->kill_part(i);
+ return 1;
+ }
+ if (t==PT_LAVA)
+ {
+ parts[i].life = 0;
+ t = parts[i].type = PT_STNE;
+ sim->part_change_type(i,x,y,t);
+ }
+ }
+ if (t!=PT_SPRK && (rt==PT_WATR || rt==PT_DSTW || rt==PT_SLTW))
+ {
+ sim->kill_part(r>>8);
+ if (t==PT_FIRE)
+ {
+ sim->kill_part(i);
+ return 1;
+ }
+ if (t==PT_LAVA)
+ {
+ parts[i].life = 0;
+ t = parts[i].type = (parts[i].ctype)?parts[i].ctype:PT_STNE;
+ parts[i].ctype = PT_NONE;
+ sim->part_change_type(i,x,y,t);
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_FIRE static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_FIRE::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int caddress = restrict_flt(restrict_flt((float)cpart->life, 0.0f, 200.0f)*3, 0.0f, (200.0f*3)-3);
+ *colr = (unsigned char)ren->flm_data[caddress];
+ *colg = (unsigned char)ren->flm_data[caddress+1];
+ *colb = (unsigned char)ren->flm_data[caddress+2];
+
+ *firea = 255;
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+
+ *pixel_mode = PMODE_NONE; //Clear default, don't draw pixel
+ *pixel_mode |= FIRE_ADD;
+ //Returning 0 means dynamic, do not cache
+ return 0;
+}
+
+Element_FIRE::~Element_FIRE() {} \ No newline at end of file
diff --git a/src/simulation/elements/FIRW.cpp b/src/simulation/elements/FIRW.cpp
new file mode 100644
index 0000000..bc047df
--- /dev/null
+++ b/src/simulation/elements/FIRW.cpp
@@ -0,0 +1,134 @@
+#include "simulation/Elements.h"
+extern "C"
+{
+ #include "hmap.h"
+}
+//#TPT-Directive ElementClass Element_FIRW PT_FIRW 69
+Element_FIRW::Element_FIRW()
+{
+ Identifier = "DEFAULT_PT_FIRW";
+ Name = "FIRW";
+ Colour = PIXPACK(0xFFA040);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.2f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 55;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Fireworks!";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_FIRW::update;
+ Graphics = &Element_FIRW::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_FIRW static int update(UPDATE_FUNC_ARGS)
+int Element_FIRW::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, rt, np;
+ if (parts[i].tmp<=0) {
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ rt = parts[r>>8].type;
+ if (rt==PT_FIRE||rt==PT_PLSM||rt==PT_THDR)
+ {
+ float gx, gy, multiplier;
+ sim->GetGravityField(x, y, sim->elements[PT_FIRW].Gravity, 1.0f, gx, gy);
+ if (gx*gx+gy*gy < 0.001f)
+ {
+ float angle = (rand()%6284)*0.001f;//(in radians, between 0 and 2*pi)
+ gx += sinf(angle)*sim->elements[PT_FIRW].Gravity*0.5f;
+ gy += cosf(angle)*sim->elements[PT_FIRW].Gravity*0.5f;
+ }
+ parts[i].tmp = 1;
+ parts[i].life = rand()%10+20;
+ multiplier = (parts[i].life+20)*0.2f/sqrtf(gx*gx+gy*gy);
+ parts[i].vx -= gx*multiplier;
+ parts[i].vy -= gy*multiplier;
+ return 0;
+ }
+ }
+ }
+ else if (parts[i].tmp==1) {
+ if (parts[i].life<=0) {
+ parts[i].tmp=2;
+ } else {
+ parts[i].flags &= ~FLAG_STAGNANT;
+ }
+ }
+ else if (parts[i].tmp>=2)
+ {
+ float angle, magnitude;
+ int caddress = (rand()%200)*3;
+ int n;
+ unsigned col = (((unsigned char)(firw_data[caddress]))<<16) | (((unsigned char)(firw_data[caddress+1]))<<8) | ((unsigned char)(firw_data[caddress+2]));
+ for (n=0; n<40; n++)
+ {
+ np = sim->create_part(-3, x, y, PT_EMBR);
+ if (np>-1)
+ {
+ magnitude = ((rand()%60)+40)*0.05f;
+ angle = (rand()%6284)*0.001f;//(in radians, between 0 and 2*pi)
+ parts[np].vx = parts[i].vx*0.5f + cosf(angle)*magnitude;
+ parts[np].vy = parts[i].vy*0.5f + sinf(angle)*magnitude;
+ parts[np].ctype = col;
+ parts[np].tmp = 1;
+ parts[np].life = rand()%40+70;
+ parts[np].temp = (rand()%500)+5750.0f;
+ parts[np].dcolour = parts[i].dcolour;
+ }
+ }
+ sim->pv[y/CELL][x/CELL] += 8.0f;
+ sim->kill_part(i);
+ return 1;
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_FIRW static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_FIRW::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ if(cpart->tmp > 0)
+ {
+ *pixel_mode |= PMODE_GLOW;
+ }
+ return 0;
+}
+
+
+Element_FIRW::~Element_FIRW() {} \ No newline at end of file
diff --git a/src/simulation/elements/FOG.cpp b/src/simulation/elements/FOG.cpp
new file mode 100644
index 0000000..9cfbe62
--- /dev/null
+++ b/src/simulation/elements/FOG.cpp
@@ -0,0 +1,73 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_FOG PT_FOG 92
+Element_FOG::Element_FOG()
+{
+ Identifier = "DEFAULT_PT_FOG";
+ Name = "FOG";
+ Colour = PIXPACK(0xAAAAAA);
+ MenuVisible = 1;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 0.8f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.4f;
+ Loss = 0.70f;
+ Collision = -0.1f;
+ Gravity = 0.0f;
+ Diffusion = 0.99f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 1;
+
+ Temperature = 243.15f;
+ HeatConduct = 100;
+ Description = "Not quite Steam";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 373.15f;
+ HighTemperatureTransition = PT_WTRV;
+
+ Update = &Element_FOG::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_FOG static int update(UPDATE_FUNC_ARGS)
+int Element_FOG::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (sim->elements[r&0xFF].State==ST_SOLID&&5>=rand()%50&&parts[i].life==0&&!((r&0xFF)==PT_CLNE||(r&0xFF)==PT_PCLN)) // TODO: should this also exclude BCLN?
+ {
+ sim->part_change_type(i,x,y,PT_RIME);
+ }
+ if ((r&0xFF)==PT_SPRK)
+ {
+ parts[i].life += rand()%20;
+ }
+ }
+ return 0;
+}
+
+
+Element_FOG::~Element_FOG() {} \ No newline at end of file
diff --git a/src/simulation/elements/FRAY.cpp b/src/simulation/elements/FRAY.cpp
new file mode 100644
index 0000000..57e2b22
--- /dev/null
+++ b/src/simulation/elements/FRAY.cpp
@@ -0,0 +1,80 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_FRAY PT_FRAY 159
+Element_FRAY::Element_FRAY()
+{
+ Identifier = "DEFAULT_PT_FRAY";
+ Name = "FRAY";
+ Colour = PIXPACK(0x00BBFF);
+ MenuVisible = 1;
+ MenuSection = SC_FORCE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = 20.0f+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Force Emitter. Push or pull objects based on temp value, use like ARAY";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_FRAY::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_FRAY static int update(UPDATE_FUNC_ARGS)
+int Element_FRAY::update(UPDATE_FUNC_ARGS)
+ {
+ int r, nxx, nyy, docontinue, len, nxi, nyi, rx, ry, nr, ry1, rx1;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_SPRK) {
+ for (docontinue = 1, nxx = 0, nyy = 0, nxi = rx*-1, nyi = ry*-1, len = 0; docontinue; nyy+=nyi, nxx+=nxi, len++) {
+ if (!(x+nxi+nxx<XRES && y+nyi+nyy<YRES && x+nxi+nxx >= 0 && y+nyi+nyy >= 0) || len>10) {
+ break;
+ }
+ r = pmap[y+nyi+nyy][x+nxi+nxx];
+ if (!r)
+ r = sim->photons[y+nyi+nyy][x+nxi+nxx];
+
+ if (r && !(sim->elements[r&0xFF].Properties & TYPE_SOLID)){
+ parts[r>>8].vx += nxi*((parts[i].temp-273.15)/10.0f);
+ parts[r>>8].vy += nyi*((parts[i].temp-273.15)/10.0f);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_FRAY::~Element_FRAY() {} \ No newline at end of file
diff --git a/src/simulation/elements/FRZW.cpp b/src/simulation/elements/FRZW.cpp
new file mode 100644
index 0000000..5bdbb21
--- /dev/null
+++ b/src/simulation/elements/FRZW.cpp
@@ -0,0 +1,81 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_FRZW PT_FRZW 101
+Element_FRZW::Element_FRZW()
+{
+ Identifier = "DEFAULT_PT_FRZW";
+ Name = "FRZW";
+ Colour = PIXPACK(0x1020C0);
+ MenuVisible = 1;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 30;
+
+ Temperature = 120.0f;
+ HeatConduct = 29;
+ Description = "Freeze water. Hybrid liquid formed when Freeze powder melts.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID||PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 53.0f;
+ HighTemperatureTransition = PT_ICEI;
+
+ Update = &Element_FRZW::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_FRZW static int update(UPDATE_FUNC_ARGS)
+int Element_FRZW::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_WATR&&5>rand()%70)
+ {
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_FRZW);
+ }
+ }
+ if (parts[i].life==0&&13>rand()%2500)
+ {
+ sim->part_change_type(i,x,y,PT_ICEI);
+ parts[i].ctype=PT_FRZW;
+ parts[i].temp = restrict_flt(parts[i].temp-200.0f, MIN_TEMP, MAX_TEMP);
+ }
+ else if ((100-(parts[i].life))>rand()%50000)
+ {
+ sim->part_change_type(i,x,y,PT_ICEI);
+ parts[i].ctype=PT_FRZW;
+ parts[i].temp = restrict_flt(parts[i].temp-200.0f, MIN_TEMP, MAX_TEMP);
+ }
+ return 0;
+}
+
+
+Element_FRZW::~Element_FRZW() {} \ No newline at end of file
diff --git a/src/simulation/elements/FRZZ.cpp b/src/simulation/elements/FRZZ.cpp
new file mode 100644
index 0000000..47a5c0f
--- /dev/null
+++ b/src/simulation/elements/FRZZ.cpp
@@ -0,0 +1,76 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_FRZZ PT_FRZZ 100
+Element_FRZZ::Element_FRZZ()
+{
+ Identifier = "DEFAULT_PT_FRZZ";
+ Name = "FRZZ";
+ Colour = PIXPACK(0xC0E0FF);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.90f;
+ Collision = -0.1f;
+ Gravity = 0.05f;
+ Diffusion = 0.01f;
+ HotAir = -0.00005f* CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 50;
+
+ Temperature = 253.15f;
+ HeatConduct = 46;
+ Description = "Freeze powder. When melted, forms ice that always cools. Spreads with regular water.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 1.8f;
+ HighPressureTransition = PT_SNOW;
+ LowTemperature = 50.0f;
+ LowTemperatureTransition = PT_ICEI;
+ HighTemperature = 273.15;
+ HighTemperatureTransition = PT_WATR;
+
+ Update = &Element_FRZZ::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_FRZZ static int update(UPDATE_FUNC_ARGS)
+int Element_FRZZ::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_WATR&&5>rand()%100)
+ {
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_FRZW);
+ parts[r>>8].life = 100;
+ parts[i].type = PT_NONE;
+ }
+
+ }
+ if (parts[i].type==PT_NONE) {
+ sim->kill_part(i);
+ return 1;
+ }
+ return 0;
+}
+
+
+Element_FRZZ::~Element_FRZZ() {} \ No newline at end of file
diff --git a/src/simulation/elements/FSEP.cpp b/src/simulation/elements/FSEP.cpp
new file mode 100644
index 0000000..52e4a67
--- /dev/null
+++ b/src/simulation/elements/FSEP.cpp
@@ -0,0 +1,86 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_FSEP PT_FSEP 71
+Element_FSEP::Element_FSEP()
+{
+ Identifier = "DEFAULT_PT_FSEP";
+ Name = "FSEP";
+ Colour = PIXPACK(0x63AD5F);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 70;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Fuse Powder. See FUSE.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_FSEP::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_FSEP static int update(UPDATE_FUNC_ARGS)
+int Element_FSEP::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ if (parts[i].life<=0) {
+ r = sim->create_part(i, x, y, PT_PLSM);
+ if (r!=-1)
+ parts[r].life = 50;
+ return 1;
+ } else if (parts[i].life < 40) {
+ parts[i].life--;
+ if ((rand()%10)==0) {
+ r = sim->create_part(-1, (rx=x+rand()%3-1), (ry=y+rand()%3-1), PT_PLSM);
+ if (r!=-1)
+ parts[r].life = 50;
+ }
+ }
+ else {
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_SPRK || (parts[i].temp>=(273.15+400.0f))) && 1>(rand()%15))
+ {
+ if (parts[i].life>40) {
+ parts[i].life = 39;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_FSEP::~Element_FSEP() {} \ No newline at end of file
diff --git a/src/simulation/elements/FUSE.cpp b/src/simulation/elements/FUSE.cpp
new file mode 100644
index 0000000..946e86b
--- /dev/null
+++ b/src/simulation/elements/FUSE.cpp
@@ -0,0 +1,92 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_FUSE PT_FUSE 70
+Element_FUSE::Element_FUSE()
+{
+ Identifier = "DEFAULT_PT_FUSE";
+ Name = "FUSE";
+ Colour = PIXPACK(0x0A5706);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.0f;
+ HotAir = 0.0f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 200;
+ Description = "Solid. Burns slowly. Ignites at somewhat high temperatures and electricity.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_FUSE::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_FUSE static int update(UPDATE_FUNC_ARGS)
+int Element_FUSE::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ if (parts[i].life<=0) {
+ r = sim->create_part(i, x, y, PT_PLSM);
+ if (r!=-1)
+ parts[r].life = 50;
+ return 1;
+ } else if (parts[i].life < 40) {
+ parts[i].life--;
+ if ((rand()%100)==0) {
+ r = sim->create_part(-1, (rx=x+rand()%3-1), (ry=y+rand()%3-1), PT_PLSM);
+ if (r!=-1)
+ parts[r].life = 50;
+ }
+ }
+ if ((sim->pv[y/CELL][x/CELL] > 2.7f)&&parts[i].tmp>40)
+ parts[i].tmp=39;
+ else if (parts[i].tmp<40&&parts[i].tmp>0)
+ parts[i].tmp--;
+ else if (parts[i].tmp<=0) {
+ sim->create_part(i, x, y, PT_FSEP);
+ return 1;
+ }
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_SPRK || ((parts[i].temp>=(273.15+700.0f)) && 1>(rand()%20)))
+ {
+ if (parts[i].life>40) {
+ parts[i].life = 39;
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_FUSE::~Element_FUSE() {} \ No newline at end of file
diff --git a/src/simulation/elements/FWRK.cpp b/src/simulation/elements/FWRK.cpp
new file mode 100644
index 0000000..f05db69
--- /dev/null
+++ b/src/simulation/elements/FWRK.cpp
@@ -0,0 +1,120 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_FWRK PT_FWRK 98
+Element_FWRK::Element_FWRK()
+{
+ Identifier = "DEFAULT_PT_FWRK";
+ Name = "FWRK";
+ Colour = PIXPACK(0x666666);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.4f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 97;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 100;
+ Description = "First fireworks made, activated by heat/neutrons.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_FWRK::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_FWRK static int update(UPDATE_FUNC_ARGS)
+int Element_FWRK::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, np;
+ if (parts[i].life==0 && ((parts[i].temp>400&&(9+parts[i].temp/40)>rand()%100000&&surround_space)||parts[i].ctype==PT_DUST))
+ {
+ float gx, gy, multiplier, gmax;
+ int randTmp;
+ sim->GetGravityField(x, y, sim->elements[PT_FWRK].Gravity, 1.0f, gx, gy);
+ if (gx*gx+gy*gy < 0.001f)
+ {
+ float angle = (rand()%6284)*0.001f;//(in radians, between 0 and 2*pi)
+ gx += sinf(angle)*sim->elements[PT_FWRK].Gravity*0.5f;
+ gy += cosf(angle)*sim->elements[PT_FWRK].Gravity*0.5f;
+ }
+ gmax = std::max(fabsf(gx), fabsf(gy));
+ if (sim->eval_move(PT_FWRK, (int)(x-(gx/gmax)+0.5f), (int)(y-(gy/gmax)+0.5f), NULL))
+ {
+ multiplier = 15.0f/sqrtf(gx*gx+gy*gy);
+
+ //Some variation in speed parallel to gravity direction
+ randTmp = (rand()%200)-100;
+ gx += gx*randTmp*0.002f;
+ gy += gy*randTmp*0.002f;
+ //and a bit more variation in speed perpendicular to gravity direction
+ randTmp = (rand()%200)-100;
+ gx += -gy*randTmp*0.005f;
+ gy += gx*randTmp*0.005f;
+
+ parts[i].life=rand()%10+18;
+ parts[i].ctype=0;
+ parts[i].vx -= gx*multiplier;
+ parts[i].vy -= gy*multiplier;
+ parts[i].dcolour = parts[i].dcolour;
+ return 0;
+ }
+ }
+ if (parts[i].life>=45)
+ parts[i].life=0;
+ if (parts[i].life<3&&parts[i].life>0)
+ {
+ int r = (rand()%245+11);
+ int g = (rand()%245+11);
+ int b = (rand()%245+11);
+ int n;
+ float angle, magnitude;
+ unsigned col = (r<<16) | (g<<8) | b;
+ for (n=0; n<40; n++)
+ {
+ np = sim->create_part(-3, x, y, PT_EMBR);
+ if (np>-1)
+ {
+ magnitude = ((rand()%60)+40)*0.05f;
+ angle = (rand()%6284)*0.001f;//(in radians, between 0 and 2*pi)
+ parts[np].vx = parts[i].vx*0.5f + cosf(angle)*magnitude;
+ parts[np].vy = parts[i].vy*0.5f + sinf(angle)*magnitude;
+ parts[np].ctype = col;
+ parts[np].tmp = 1;
+ parts[np].life = rand()%40+70;
+ parts[np].temp = (rand()%500)+5750.0f;
+ parts[np].dcolour = parts[i].dcolour;
+ }
+ }
+ sim->pv[y/CELL][x/CELL] += 8.0f;
+ sim->kill_part(i);
+ return 1;
+ }
+ return 0;
+}
+
+
+Element_FWRK::~Element_FWRK() {} \ No newline at end of file
diff --git a/src/simulation/elements/GAS.cpp b/src/simulation/elements/GAS.cpp
new file mode 100644
index 0000000..4e87b76
--- /dev/null
+++ b/src/simulation/elements/GAS.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_GAS PT_GAS 10
+Element_GAS::Element_GAS()
+{
+ Identifier = "DEFAULT_PT_GAS";
+ Name = "GAS";
+ Colour = PIXPACK(0xE0FF20);
+ MenuVisible = 1;
+ MenuSection = SC_GAS;
+ Enabled = 1;
+
+ Advection = 1.0f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.30f;
+ Collision = -0.1f;
+ Gravity = 0.0f;
+ Diffusion = 0.75f;
+ HotAir = 0.001f * CFDS;
+ Falldown = 0;
+
+ Flammable = 600;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 1;
+
+ Temperature = R_TEMP+2.0f +273.15f;
+ HeatConduct = 42;
+ Description = "Gas. Diffuses. Flammable. Liquefies under pressure.";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 6.0f;
+ HighPressureTransition = PT_OIL;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 573.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = NULL;
+
+}
+
+Element_GAS::~Element_GAS() {} \ No newline at end of file
diff --git a/src/simulation/elements/GBMB.cpp b/src/simulation/elements/GBMB.cpp
new file mode 100644
index 0000000..827f062
--- /dev/null
+++ b/src/simulation/elements/GBMB.cpp
@@ -0,0 +1,93 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_GBMB PT_GBMB 157
+Element_GBMB::Element_GBMB()
+{
+ Identifier = "DEFAULT_PT_GBMB";
+ Name = "GBMB";
+ Colour = PIXPACK(0x1144BB);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 30;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 29;
+ Description = "Sticks to first object it touches then produces strong gravity push.";
+
+ State = ST_NONE;
+ Properties = TYPE_PART|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_GBMB::update;
+ Graphics = &Element_GBMB::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_GBMB static int update(UPDATE_FUNC_ARGS)
+int Element_GBMB::update(UPDATE_FUNC_ARGS)
+ {
+ int rx,ry,r;
+ if (parts[i].life<=0)
+ {
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ {
+ r = pmap[y+ry][x+rx];
+ if(!r)
+ continue;
+ if((r&0xFF)!=PT_BOMB && (r&0xFF)!=PT_GBMB &&
+ (r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PCLN &&
+ (r&0xFF)!=PT_DMND)
+ {
+ parts[i].life=60;
+ break;
+ }
+ }
+ }
+ if(parts[i].life>20)
+ sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] = 20;
+ if(parts[i].life<20 && parts[i].life>=1)
+ sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] = -80;
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_GBMB static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_GBMB::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ if (cpart->life <= 0) {
+ *pixel_mode |= PMODE_FLARE;
+ }
+ else
+ {
+ *pixel_mode |= PMODE_SPARK;
+ }
+ return 0;
+}
+
+
+Element_GBMB::~Element_GBMB() {} \ No newline at end of file
diff --git a/src/simulation/elements/GEL.cpp b/src/simulation/elements/GEL.cpp
new file mode 100644
index 0000000..5a139db
--- /dev/null
+++ b/src/simulation/elements/GEL.cpp
@@ -0,0 +1,155 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_GEL PT_GEL 142
+Element_GEL::Element_GEL()
+{
+ Identifier = "DEFAULT_PT_GEL";
+ Name = "GEL";
+ Colour = PIXPACK(0xFF9900);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 35;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 29;
+ Description = "Gel. A liquid with variable viscosity and heat conductivity";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_LIFE_DEC|PROP_NEUTPENETRATE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_GEL::update;
+ Graphics = &Element_GEL::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_GEL static int update(UPDATE_FUNC_ARGS)
+int Element_GEL::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ int absorbChanceDenom;
+ if (parts[i].tmp>100) parts[i].tmp = 100;
+ if (parts[i].tmp<0) parts[i].tmp = 0;
+ absorbChanceDenom = parts[i].tmp*10 + 500;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+
+ //Desaturation
+ if (((r&0xFF)==PT_WATR || (r&0xFF)==PT_DSTW || (r&0xFF)==PT_FRZW) && parts[i].tmp<100 && 500>rand()%absorbChanceDenom)
+ {
+ parts[i].tmp++;
+ sim->kill_part(r>>8);
+ }
+ if (((r&0xFF)==PT_PSTE) && parts[i].tmp<100 && 20>rand()%absorbChanceDenom)
+ {
+ parts[i].tmp++;
+ sim->create_part(r>>8, x+rx, y+ry, PT_CLST);
+ }
+ if (((r&0xFF)==PT_SLTW) && parts[i].tmp<100 && 50>rand()%absorbChanceDenom)
+ {
+ parts[i].tmp++;
+ if (rand()%4)
+ sim->kill_part(r>>8);
+ else
+ sim->part_change_type(r>>8, x+rx, y+ry, PT_SALT);
+ }
+ if (((r&0xFF)==PT_CBNW) && parts[i].tmp<100 && 100>rand()%absorbChanceDenom)
+ {
+ parts[i].tmp++;
+ sim->part_change_type(r>>8, x+rx, y+ry, PT_CO2);
+ }
+
+ if ((r&0xFF)==PT_SPNG && parts[i].tmp<100 && ((parts[r>>8].life+1)>parts[i].tmp))
+ {
+ parts[r>>8].life--;
+ parts[i].tmp++;
+ }
+
+ char gel = 0;
+ if ((r&0xFF)==PT_GEL)
+ gel = 1;
+
+ //Concentration diffusion
+ if (gel && (parts[r>>8].tmp+1)<parts[i].tmp)
+ {
+ parts[r>>8].tmp++;
+ parts[i].tmp--;
+ }
+
+ if ((r&0xFF)==PT_SPNG && (parts[r>>8].life+1)<parts[i].tmp)
+ {
+ parts[r>>8].life++;
+ parts[i].tmp--;
+ }
+
+ float dx, dy;
+ dx = parts[i].x - parts[r>>8].x;
+ dy = parts[i].y - parts[r>>8].y;
+
+ //Stickness
+ if ((dx*dx + dy*dy)>1.5 && (gel || !sim->elements[r&0xFF].Falldown || (fabs((float)rx)<2 && fabs((float)ry)<2)))
+ {
+ float per, nd;
+ nd = dx*dx + dy*dy - 0.5;
+
+ per = 5*(1 - parts[i].tmp/100)*(nd/(dx*dx + dy*dy + nd) - 0.5);
+ if (sim->elements[r&0xFF].State==ST_LIQUID)
+ per *= 0.1;
+
+ dx *= per; dy *= per;
+ parts[i].vx += dx;
+ parts[i].vy += dy;
+ if ((sim->elements[r&0xFF].Properties&TYPE_PART) || (r&0xFF)==PT_GOO)
+ {
+ parts[r>>8].vx -= dx;
+ parts[r>>8].vy -= dy;
+ }
+ }
+ }
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_GEL static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_GEL::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int q = cpart->tmp;
+ *colr = q*(32-255)/120+255;
+ *colg = q*(48-186)/120+186;
+ *colb = q*208/120;
+ return 0;
+}
+
+
+
+Element_GEL::~Element_GEL() {}
diff --git a/src/simulation/elements/GLAS.cpp b/src/simulation/elements/GLAS.cpp
new file mode 100644
index 0000000..b752a0b
--- /dev/null
+++ b/src/simulation/elements/GLAS.cpp
@@ -0,0 +1,62 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_GLAS PT_GLAS 45
+Element_GLAS::Element_GLAS()
+{
+ Identifier = "DEFAULT_PT_GLAS";
+ Name = "GLAS";
+ Colour = PIXPACK(0x404040);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 150;
+ Description = "Solid. Meltable. Shatters under pressure";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID | PROP_NEUTPASS | PROP_HOT_GLOW | PROP_SPARKSETTLE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1973.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = &Element_GLAS::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_GLAS static int update(UPDATE_FUNC_ARGS)
+int Element_GLAS::update(UPDATE_FUNC_ARGS)
+ {
+ parts[i].pavg[0] = parts[i].pavg[1];
+ parts[i].pavg[1] = sim->pv[y/CELL][x/CELL];
+ if (parts[i].pavg[1]-parts[i].pavg[0] > 0.25f || parts[i].pavg[1]-parts[i].pavg[0] < -0.25f)
+ {
+ sim->part_change_type(i,x,y,PT_BGLA);
+ }
+ return 0;
+}
+
+
+Element_GLAS::~Element_GLAS() {} \ No newline at end of file
diff --git a/src/simulation/elements/GLOW.cpp b/src/simulation/elements/GLOW.cpp
new file mode 100644
index 0000000..775e188
--- /dev/null
+++ b/src/simulation/elements/GLOW.cpp
@@ -0,0 +1,96 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_GLOW PT_GLOW 66
+Element_GLOW::Element_GLOW()
+{
+ Identifier = "DEFAULT_PT_GLOW";
+ Name = "GLOW";
+ Colour = PIXPACK(0x445464);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.3f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.15f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 2;
+
+ Weight = 40;
+
+ Temperature = R_TEMP+20.0f+273.15f;
+ HeatConduct = 44;
+ Description = "Glow, Glows under pressure";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_GLOW::update;
+ Graphics = &Element_GLOW::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_GLOW static int update(UPDATE_FUNC_ARGS)
+int Element_GLOW::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_WATR&&5>(rand()%2000))
+ {
+ parts[i].type = PT_NONE;
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_DEUT);
+ parts[r>>8].life = 10;
+ }
+ }
+ parts[i].ctype = sim->pv[y/CELL][x/CELL]*16;
+
+ parts[i].tmp = abs((int)((sim->vx[y/CELL][x/CELL]+sim->vy[y/CELL][x/CELL])*16.0f)) + abs((int)((parts[i].vx+parts[i].vy)*64.0f));
+ //printf("%f %f\n", parts[i].vx, parts[i].vy);
+ if (parts[i].type==PT_NONE) {
+ sim->kill_part(i);
+ return 1;
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_GLOW static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_GLOW::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *firer = restrict_flt(cpart->temp-(275.13f+32.0f), 0, 128)/50.0f;
+ *fireg = restrict_flt(cpart->ctype, 0, 128)/50.0f;
+ *fireb = restrict_flt(cpart->tmp, 0, 128)/50.0f;
+
+ *colr = restrict_flt(64.0f+cpart->temp-(275.13f+32.0f), 0, 255);
+ *colg = restrict_flt(64.0f+cpart->ctype, 0, 255);
+ *colb = restrict_flt(64.0f+cpart->tmp, 0, 255);
+
+ *pixel_mode |= FIRE_ADD;
+ return 0;
+}
+
+
+Element_GLOW::~Element_GLOW() {} \ No newline at end of file
diff --git a/src/simulation/elements/GOO.cpp b/src/simulation/elements/GOO.cpp
new file mode 100644
index 0000000..3dcef31
--- /dev/null
+++ b/src/simulation/elements/GOO.cpp
@@ -0,0 +1,64 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_GOO PT_GOO 12
+Element_GOO::Element_GOO()
+{
+ Identifier = "DEFAULT_PT_GOO";
+ Name = "GOO";
+ Colour = PIXPACK(0x804000);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.97f;
+ Loss = 0.50f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 12;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 75;
+ Description = "Solid. Deforms and disappears under pressure.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID | PROP_NEUTPENETRATE|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_GOO::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_GOO static int update(UPDATE_FUNC_ARGS)
+int Element_GOO::update(UPDATE_FUNC_ARGS)
+ {
+ if (!parts[i].life && sim->pv[y/CELL][x/CELL]>1.0f)
+ parts[i].life = rand()%80+300;
+ if (parts[i].life)
+ {
+ float advection = 0.1f;
+ parts[i].vx += advection*sim->vx[y/CELL][x/CELL];
+ parts[i].vy += advection*sim->vy[y/CELL][x/CELL];
+ }
+ return 0;
+}
+
+
+Element_GOO::~Element_GOO() {} \ No newline at end of file
diff --git a/src/simulation/elements/GPMP.cpp b/src/simulation/elements/GPMP.cpp
new file mode 100644
index 0000000..3cf51a3
--- /dev/null
+++ b/src/simulation/elements/GPMP.cpp
@@ -0,0 +1,94 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_GPMP PT_GPMP 154
+Element_GPMP::Element_GPMP()
+{
+ Identifier = "DEFAULT_PT_GPMP";
+ Name = "GPMP";
+ Colour = PIXPACK(0x0A3B3B);
+ MenuVisible = 1;
+ MenuSection = SC_POWERED;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = 0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Changes gravity to its temp when activated. (use HEAT/COOL).";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_GPMP::update;
+ Graphics = &Element_GPMP::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_GPMP static int update(UPDATE_FUNC_ARGS)
+int Element_GPMP::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ if (parts[i].life>0 && parts[i].life!=10)
+ parts[i].life--;
+ if (parts[i].life==10)
+ {
+ if (parts[i].temp>=256.0+273.15)
+ parts[i].temp=256.0+273.15;
+ if (parts[i].temp<= -256.0+273.15)
+ parts[i].temp = -256.0+273.15;
+
+ sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] = 0.2f*(parts[i].temp-273.15);
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_GPMP)
+ {
+ if (parts[r>>8].life<10&&parts[r>>8].life>0)
+ parts[i].life = 9;
+ else if (parts[r>>8].life==0)
+ parts[r>>8].life = 10;
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_GPMP static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_GPMP::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int lifemod = ((cpart->life>10?10:cpart->life)*19);
+ *colg += lifemod;
+ *colb += lifemod;
+ return 0;
+}
+
+
+Element_GPMP::~Element_GPMP() {}
diff --git a/src/simulation/elements/GRAV.cpp b/src/simulation/elements/GRAV.cpp
new file mode 100644
index 0000000..b913a28
--- /dev/null
+++ b/src/simulation/elements/GRAV.cpp
@@ -0,0 +1,111 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_GRAV PT_GRAV 102
+Element_GRAV::Element_GRAV()
+{
+ Identifier = "DEFAULT_PT_GRAV";
+ Name = "GRAV";
+ Colour = PIXPACK(0xFFE0A0);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 1.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 10;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 85;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Very light dust. Changes colour based on velocity.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_GRAV::update;
+ Graphics = &Element_GRAV::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_GRAV static int update(UPDATE_FUNC_ARGS)
+int Element_GRAV::update(UPDATE_FUNC_ARGS)
+ {
+ /*int t = parts[i].type;
+ if (t==PT_LOVE)
+ ISLOVE=1;
+ else if (t==PT_LOLZ)
+ ISLOLZ=1;
+ else if (t==PT_GRAV)
+ ISGRAV=1;*/
+ return 0;
+}
+
+int lastIndex;
+
+//#TPT-Directive ElementHeader Element_GRAV static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_GRAV::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int GRAV_R, GRAV_B, GRAV_G, GRAV_R2, GRAV_B2, GRAV_G2;
+
+ GRAV_R = std::abs((ren->sim->currentTick%120)-60);
+ GRAV_G = std::abs(((ren->sim->currentTick+60)%120)-60);
+ GRAV_B = std::abs(((ren->sim->currentTick+120)%120)-60);
+ GRAV_R2 = std::abs((ren->sim->currentTick%60)-30);
+ GRAV_G2 = std::abs(((ren->sim->currentTick+30)%60)-30);
+ GRAV_B2 = std::abs(((ren->sim->currentTick+60)%60)-30);
+
+
+ *colr = 20;
+ *colg = 20;
+ *colb = 20;
+ if (cpart->vx>0)
+ {
+ *colr += (cpart->vx)*GRAV_R;
+ *colg += (cpart->vx)*GRAV_G;
+ *colb += (cpart->vx)*GRAV_B;
+ }
+ if (cpart->vy>0)
+ {
+ *colr += (cpart->vy)*GRAV_G;
+ *colg += (cpart->vy)*GRAV_B;
+ *colb += (cpart->vy)*GRAV_R;
+
+ }
+ if (cpart->vx<0)
+ {
+ *colr -= (cpart->vx)*GRAV_B;
+ *colg -= (cpart->vx)*GRAV_R;
+ *colb -= (cpart->vx)*GRAV_G;
+
+ }
+ if (cpart->vy<0)
+ {
+ *colr -= (cpart->vy)*GRAV_R2;
+ *colg -= (cpart->vy)*GRAV_G2;
+ *colb -= (cpart->vy)*GRAV_B2;
+ }
+ return 0;
+}
+
+
+Element_GRAV::~Element_GRAV() {} \ No newline at end of file
diff --git a/src/simulation/elements/GUNP.cpp b/src/simulation/elements/GUNP.cpp
new file mode 100644
index 0000000..798e0f7
--- /dev/null
+++ b/src/simulation/elements/GUNP.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_GUNP PT_GUNP 7
+Element_GUNP::Element_GUNP()
+{
+ Identifier = "DEFAULT_PT_GUNP";
+ Name = "GUN";
+ Colour = PIXPACK(0xC0C0D0);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.80f;
+ Collision = -0.1f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 600;
+ Explosive = 1;
+ Meltable = 0;
+ Hardness = 10;
+
+ Weight = 85;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 97;
+ Description = "Light dust. Explosive.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 673.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = NULL;
+
+}
+
+Element_GUNP::~Element_GUNP() {} \ No newline at end of file
diff --git a/src/simulation/elements/H2.cpp b/src/simulation/elements/H2.cpp
new file mode 100644
index 0000000..2b6c31b
--- /dev/null
+++ b/src/simulation/elements/H2.cpp
@@ -0,0 +1,124 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_H2 PT_H2 148
+Element_H2::Element_H2()
+{
+ Identifier = "DEFAULT_PT_H2";
+ Name = "HYGN";
+ Colour = PIXPACK(0x5070FF);
+ MenuVisible = 1;
+ MenuSection = SC_GAS;
+ Enabled = 1;
+
+ Advection = 2.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.30f;
+ Collision = -0.10f;
+ Gravity = 0.00f;
+ Diffusion = 3.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 1;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Combines with O2 to make WATR";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_H2::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_H2 static int update(UPDATE_FUNC_ARGS)
+int Element_H2::update(UPDATE_FUNC_ARGS)
+{
+ int r,rx,ry,rt;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ rt = (r&0xFF);
+ if (!r)
+ continue;
+ if (sim->pv[y/CELL][x/CELL] > 8.0f && rt == PT_DESL) // This will not work. DESL turns to fire above 5.0 pressure
+ {
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_WATR);
+ sim->part_change_type(i,x,y,PT_OIL);
+ }
+ if (parts[r>>8].temp > 2273.15 && sim->pv[y/CELL][x/CELL] > 45.0f)
+ continue;
+ if (sim->pv[y/CELL][x/CELL] <= 45.0f)
+ {
+ if (rt==PT_FIRE)
+ {
+ parts[r>>8].temp=2473.15;
+ if(parts[r>>8].tmp&0x02)
+ parts[r>>8].temp=3473;
+ parts[r>>8].tmp |= 1;
+ }
+ if (rt==PT_FIRE || (rt==PT_PLSM && !(parts[r>>8].tmp&4)) || (rt==PT_LAVA && parts[r>>8].ctype != PT_BMTL))
+ {
+ sim->create_part(i,x,y,PT_FIRE);
+ parts[i].temp+=(rand()/(RAND_MAX/100));
+ parts[i].tmp |= 1;
+ }
+ }
+ }
+ if (parts[i].temp > 2273.15 && sim->pv[y/CELL][x/CELL] > 50.0f)
+ {
+ if (rand()%5 < 1)
+ {
+ int j;
+ float temp = parts[i].temp;
+ sim->create_part(i,x,y,PT_NBLE);
+
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_NEUT);
+ if (j != -1)
+ parts[j].temp = temp;
+ if (!(rand()%10))
+ {
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_ELEC);
+ if (j != -1)
+ parts[j].temp = temp;
+ }
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_PHOT);
+ if (j != -1)
+ {
+ parts[j].ctype = 0x7C0000;
+ parts[j].temp = temp;
+ }
+
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_PLSM);
+ if (j != -1)
+ {
+ parts[j].temp = temp;
+ parts[j].tmp |= 4;
+ }
+
+ parts[i].temp = temp+750+rand()%500;
+ sim->pv[y/CELL][x/CELL] += 30;
+ }
+ }
+ return 0;
+}
+
+
+Element_H2::~Element_H2() {}
diff --git a/src/simulation/elements/HFLM.cpp b/src/simulation/elements/HFLM.cpp
new file mode 100644
index 0000000..e4fb3b4
--- /dev/null
+++ b/src/simulation/elements/HFLM.cpp
@@ -0,0 +1,75 @@
+#include "simulation/Elements.h"
+extern "C"
+{
+ #include "hmap.h"
+}
+
+//#TPT-Directive ElementClass Element_HFLM PT_HFLM 68
+Element_HFLM::Element_HFLM()
+{
+ Identifier = "DEFAULT_PT_HFLM";
+ Name = "CFLM";
+ Colour = PIXPACK(0x8080FF);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.9f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.97f;
+ Loss = 0.20f;
+ Collision = 0.0f;
+ Gravity = -0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.0005f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 2;
+
+ Temperature = 0.0f;
+ HeatConduct = 88;
+ Description = "Sub-zero flame.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_GAS|PROP_LIFE_DEC|PROP_LIFE_KILL;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+ Graphics = &Element_HFLM::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_HFLM static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_HFLM::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int caddress = restrict_flt(restrict_flt((float)((int)(cpart->life/2)), 0.0f, 200.0f)*3, 0.0f, (200.0f*3)-3);
+ *colr = (unsigned char)hflm_data[caddress];
+ *colg = (unsigned char)hflm_data[caddress+1];
+ *colb = (unsigned char)hflm_data[caddress+2];
+
+ *firea = 255;
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+
+ *pixel_mode = PMODE_NONE; //Clear default, don't draw pixel
+ *pixel_mode |= FIRE_ADD;
+ //Returning 0 means dynamic, do not cache
+ return 0;
+}
+
+
+Element_HFLM::~Element_HFLM() {} \ No newline at end of file
diff --git a/src/simulation/elements/HSWC.cpp b/src/simulation/elements/HSWC.cpp
new file mode 100644
index 0000000..0f3c7e7
--- /dev/null
+++ b/src/simulation/elements/HSWC.cpp
@@ -0,0 +1,87 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_HSWC PT_HSWC 75
+Element_HSWC::Element_HSWC()
+{
+ Identifier = "DEFAULT_PT_HSWC";
+ Name = "HSWC";
+ Colour = PIXPACK(0x3B0A0A);
+ MenuVisible = 1;
+ MenuSection = SC_POWERED;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Heat switch. Conducts Heat only when activated";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_HSWC::update;
+ Graphics = &Element_HSWC::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_HSWC static int update(UPDATE_FUNC_ARGS)
+int Element_HSWC::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ if (parts[i].life>0 && parts[i].life!=10)
+ parts[i].life--;
+ if (parts[i].life==10)
+ {
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_HSWC)
+ {
+ if (parts[r>>8].life<10&&parts[r>>8].life>0)
+ parts[i].life = 9;
+ else if (parts[r>>8].life==0)
+ parts[r>>8].life = 10;
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_HSWC static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_HSWC::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int lifemod = ((cpart->life>10?10:cpart->life)*19);
+ *colr += lifemod;
+ return 0;
+}
+
+
+Element_HSWC::~Element_HSWC() {} \ No newline at end of file
diff --git a/src/simulation/elements/ICEI.cpp b/src/simulation/elements/ICEI.cpp
new file mode 100644
index 0000000..3624877
--- /dev/null
+++ b/src/simulation/elements/ICEI.cpp
@@ -0,0 +1,76 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_ICEI PT_ICEI 13
+Element_ICEI::Element_ICEI()
+{
+ Identifier = "DEFAULT_PT_ICEI";
+ Name = "ICE";
+ Colour = PIXPACK(0xA0C0FF);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = -0.0003f* CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 100;
+
+ Temperature = R_TEMP-50.0f+273.15f;
+ HeatConduct = 46;
+ Description = "Solid. Freezes water. Crushes under pressure. Cools down air.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 0.8f;
+ HighPressureTransition = PT_SNOW;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 252.05f;
+ HighTemperatureTransition = ST;
+
+ Update = &Element_ICEI::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_ICEI static int update(UPDATE_FUNC_ARGS)
+int Element_ICEI::update(UPDATE_FUNC_ARGS)
+ { //currently used for snow as well
+ int r, rx, ry;
+ if (parts[i].ctype==PT_FRZW)//get colder if it is from FRZW
+ {
+ parts[i].temp = restrict_flt(parts[i].temp-1.0f, MIN_TEMP, MAX_TEMP);
+ }
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_SALT || (r&0xFF)==PT_SLTW) && parts[i].temp > sim->elements[PT_SLTW].LowTemperature && 1>(rand()%1000))
+ {
+ sim->part_change_type(i,x,y,PT_SLTW);
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SLTW);
+ }
+ if (((r&0xFF)==PT_FRZZ) && (parts[i].ctype=PT_FRZW) && 1>(rand()%1000))
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_ICEI);
+ }
+ return 0;
+}
+
+
+Element_ICEI::~Element_ICEI() {} \ No newline at end of file
diff --git a/src/simulation/elements/IGNT.cpp b/src/simulation/elements/IGNT.cpp
new file mode 100644
index 0000000..de26eb7
--- /dev/null
+++ b/src/simulation/elements/IGNT.cpp
@@ -0,0 +1,95 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_IGNT PT_IGNT 140
+Element_IGNT::Element_IGNT()
+{
+ Identifier = "DEFAULT_PT_IGNT";
+ Name = "IGNC";
+ Colour = PIXPACK(0xC0B050);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 88;
+ Description = "Ignition cord.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID | PROP_NEUTPENETRATE | PROP_SPARKSETTLE | PROP_LIFE_KILL;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 673.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = &Element_IGNT::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_IGNT static int update(UPDATE_FUNC_ARGS)
+int Element_IGNT::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ if(parts[i].tmp==0)
+ {
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_FIRE || (r&0xFF)==PT_PLSM)
+ {
+ parts[i].tmp = 1;
+ }
+ else if ((r&0xFF)==PT_SPRK || (r&0xFF)==PT_LIGH || ((r&0xFF)==PT_IGNT && parts[r>>8].life==1))
+ {
+ parts[i].tmp = 1;
+ }
+ }
+ }
+ else if(parts[i].life > 0)
+ {
+ if(rand()%3)
+ {
+ int nb = sim->create_part(-1, x+rand()%3-1, y+rand()%3-1, PT_EMBR);
+ if (nb!=-1) {
+ parts[nb].tmp = 0;
+ parts[nb].life = 30;
+ parts[nb].vx = rand()%20-10;
+ parts[nb].vy = rand()%20-10;
+ parts[nb].temp = restrict_flt(400.0f+parts[i].temp-273.15, MIN_TEMP, MAX_TEMP);
+ }
+ }
+ else
+ {
+ sim->create_part(-1, x+rand()%3-1, y+rand()%3-1, PT_FIRE);
+ }
+ parts[i].life--;
+ }
+ return 0;
+}
+
+
+Element_IGNT::~Element_IGNT() {} \ No newline at end of file
diff --git a/src/simulation/elements/INSL.cpp b/src/simulation/elements/INSL.cpp
new file mode 100644
index 0000000..8685775
--- /dev/null
+++ b/src/simulation/elements/INSL.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_INSL PT_INSL 38
+Element_INSL::Element_INSL()
+{
+ Identifier = "DEFAULT_PT_INSL";
+ Name = "INSL";
+ Colour = PIXPACK(0x9EA3B6);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 7;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 10;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Insulator, does not conduct heat or electricity.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+
+}
+
+Element_INSL::~Element_INSL() {} \ No newline at end of file
diff --git a/src/simulation/elements/INST.cpp b/src/simulation/elements/INST.cpp
new file mode 100644
index 0000000..a8a8d3e
--- /dev/null
+++ b/src/simulation/elements/INST.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_INST PT_INST 106
+Element_INST::Element_INST()
+{
+ Identifier = "DEFAULT_PT_INST";
+ Name = "INST";
+ Colour = PIXPACK(0x404039);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Instantly conducts, PSCN to charge, NSCN to take.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+
+}
+
+Element_INST::~Element_INST() {} \ No newline at end of file
diff --git a/src/simulation/elements/INVIS.cpp b/src/simulation/elements/INVIS.cpp
new file mode 100644
index 0000000..54d722d
--- /dev/null
+++ b/src/simulation/elements/INVIS.cpp
@@ -0,0 +1,75 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_INVIS PT_INVIS 115
+Element_INVIS::Element_INVIS()
+{
+ Identifier = "DEFAULT_PT_INVIS";
+ Name = "INVS";
+ Colour = PIXPACK(0x00CCCC);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 15;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 164;
+ Description = "Invisible to everything while under pressure.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID | PROP_NEUTPASS;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_INVIS::update;
+ Graphics = &Element_INVIS::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_INVIS static int update(UPDATE_FUNC_ARGS)
+int Element_INVIS::update(UPDATE_FUNC_ARGS)
+{
+ if (sim->pv[y/CELL][x/CELL]>4.0f || sim->pv[y/CELL][x/CELL]<-4.0f)
+ parts[i].tmp = 1;
+ else
+ parts[i].tmp = 0;
+ return 0;
+}
+
+//#TPT-Directive ElementHeader Element_INVIS static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_INVIS::graphics(GRAPHICS_FUNC_ARGS)
+{
+ //pv[ny/CELL][nx/CELL]>4.0f || pv[ny/CELL][nx/CELL]<-4.0f
+ if(cpart->tmp)
+ {
+ *cola = 100;
+ *colr = 15;
+ *colg = 0;
+ *colb = 150;
+ *pixel_mode = PMODE_BLEND;
+ }
+ return 0;
+}
+
+
+Element_INVIS::~Element_INVIS() {}
diff --git a/src/simulation/elements/INWR.cpp b/src/simulation/elements/INWR.cpp
new file mode 100644
index 0000000..35d57ec
--- /dev/null
+++ b/src/simulation/elements/INWR.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_INWR PT_INWR 62
+Element_INWR::Element_INWR()
+{
+ Identifier = "DEFAULT_PT_INWR";
+ Name = "INWR";
+ Colour = PIXPACK(0x544141);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Insulated Wire. Doesn't conduct to metal or semiconductors.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1687.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = NULL;
+
+}
+
+Element_INWR::~Element_INWR() {} \ No newline at end of file
diff --git a/src/simulation/elements/IRON.cpp b/src/simulation/elements/IRON.cpp
new file mode 100644
index 0000000..1542da9
--- /dev/null
+++ b/src/simulation/elements/IRON.cpp
@@ -0,0 +1,76 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_IRON PT_IRON 76
+Element_IRON::Element_IRON()
+{
+ Identifier = "DEFAULT_PT_IRON";
+ Name = "IRON";
+ Colour = PIXPACK(0x707070);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 50;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Rusts with salt, can be used for electrolysis of WATR";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1687.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = &Element_IRON::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_IRON static int update(UPDATE_FUNC_ARGS)
+int Element_IRON::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((((r&0xFF) == PT_SALT && 15>(rand()/(RAND_MAX/700))) ||
+ ((r&0xFF) == PT_SLTW && 30>(rand()/(RAND_MAX/2000))) ||
+ ((r&0xFF) == PT_WATR && 5 >(rand()/(RAND_MAX/6000))) ||
+ ((r&0xFF) == PT_O2 && 2 >(rand()/(RAND_MAX/500))) ||
+ ((r&0xFF) == PT_LO2))&&
+ (!(parts[i].life))
+ )
+ {
+ sim->part_change_type(i,x,y,PT_BMTL);
+ parts[i].tmp=(rand()/(RAND_MAX/10))+20;
+ }
+ }
+ return 0;
+}
+
+
+Element_IRON::~Element_IRON() {} \ No newline at end of file
diff --git a/src/simulation/elements/ISOZ.cpp b/src/simulation/elements/ISOZ.cpp
new file mode 100644
index 0000000..05df489
--- /dev/null
+++ b/src/simulation/elements/ISOZ.cpp
@@ -0,0 +1,65 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_ISOZ PT_ISOZ 107
+Element_ISOZ::Element_ISOZ()
+{
+ Identifier = "DEFAULT_PT_ISOZ";
+ Name = "ISOZ";
+ Colour = PIXPACK(0xAA30D0);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 24;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 29;
+ Description = "Radioactive liquid";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_NEUTPENETRATE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 160.0f;
+ LowTemperatureTransition = PT_ISZS;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_ISOZ::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_ISOZ static int update(UPDATE_FUNC_ARGS)
+int Element_ISOZ::update(UPDATE_FUNC_ARGS)
+ { // for both ISZS and ISOZ
+ float rr, rrr;
+ if (1>rand()%200 && ((int)(-4.0f*(sim->pv[y/CELL][x/CELL])))>(rand()%1000))
+ {
+ sim->create_part(i, x, y, PT_PHOT);
+ rr = (rand()%228+128)/127.0f;
+ rrr = (rand()%360)*3.14159f/180.0f;
+ parts[i].vx = rr*cosf(rrr);
+ parts[i].vy = rr*sinf(rrr);
+ }
+ return 0;
+}
+
+
+Element_ISOZ::~Element_ISOZ() {} \ No newline at end of file
diff --git a/src/simulation/elements/ISZS.cpp b/src/simulation/elements/ISZS.cpp
new file mode 100644
index 0000000..b12f34c
--- /dev/null
+++ b/src/simulation/elements/ISZS.cpp
@@ -0,0 +1,65 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_ISZS PT_ISZS 108
+Element_ISZS::Element_ISZS()
+{
+ Identifier = "DEFAULT_PT_ISZS";
+ Name = "ISZS";
+ Colour = PIXPACK(0x662089);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = -0.0007f* CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = 140.00f;
+ HeatConduct = 251;
+ Description = "Solid form of ISOZ, slowly decays.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 300.0f;
+ HighTemperatureTransition = PT_ISOZ;
+
+ Update = &Element_ISZS::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_ISZS static int update(UPDATE_FUNC_ARGS)
+int Element_ISZS::update(UPDATE_FUNC_ARGS)
+ { // for both ISZS and ISOZ
+ float rr, rrr;
+ if (1>rand()%200 && ((int)(-4.0f*(sim->pv[y/CELL][x/CELL])))>(rand()%1000))
+ {
+ sim->create_part(i, x, y, PT_PHOT);
+ rr = (rand()%228+128)/127.0f;
+ rrr = (rand()%360)*3.14159f/180.0f;
+ parts[i].vx = rr*cosf(rrr);
+ parts[i].vy = rr*sinf(rrr);
+ }
+ return 0;
+}
+
+
+Element_ISZS::~Element_ISZS() {} \ No newline at end of file
diff --git a/src/simulation/elements/LAVA.cpp b/src/simulation/elements/LAVA.cpp
new file mode 100644
index 0000000..35eefec
--- /dev/null
+++ b/src/simulation/elements/LAVA.cpp
@@ -0,0 +1,71 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_LAVA PT_LAVA 6
+Element_LAVA::Element_LAVA()
+{
+ Identifier = "DEFAULT_PT_LAVA";
+ Name = "LAVA";
+ Colour = PIXPACK(0xE05010);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.3f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.15f;
+ Diffusion = 0.00f;
+ HotAir = 0.0003f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 2;
+
+ Weight = 45;
+
+ Temperature = R_TEMP+1500.0f+273.15f;
+ HeatConduct = 60;
+ Description = "Heavy liquid. Ignites flammable materials. Solidifies when cold.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 2573.15f;
+ LowTemperatureTransition = ST;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_FIRE::update;
+ Graphics = &Element_LAVA::graphics;
+}
+
+
+//#TPT-Directive ElementHeader Element_LAVA static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_LAVA::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *colr = cpart->life * 2 + 0xE0;
+ *colg = cpart->life * 1 + 0x50;
+ *colb = cpart->life / 2 + 0x10;
+ if (*colr>255) *colr = 255;
+ if (*colg>192) *colg = 192;
+ if (*colb>128) *colb = 128;
+ *firea = 40;
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+ *pixel_mode |= FIRE_ADD;
+ *pixel_mode |= PMODE_BLUR;
+ //Returning 0 means dynamic, do not cache
+ return 0;
+}
+
+
+Element_LAVA::~Element_LAVA() {} \ No newline at end of file
diff --git a/src/simulation/elements/LCRY.cpp b/src/simulation/elements/LCRY.cpp
new file mode 100644
index 0000000..7cd4def
--- /dev/null
+++ b/src/simulation/elements/LCRY.cpp
@@ -0,0 +1,154 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_LCRY PT_LCRY 54
+Element_LCRY::Element_LCRY()
+{
+ Identifier = "DEFAULT_PT_LCRY";
+ Name = "LCRY";
+ Colour = PIXPACK(0x505050);
+ MenuVisible = 1;
+ MenuSection = SC_POWERED;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Liquid Crystal. Changes colour when charged. (PSCN Charges, NSCN Discharges)";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1273.0f;
+ HighTemperatureTransition = PT_BGLA;
+
+ Update = &Element_LCRY::update;
+ Graphics = &Element_LCRY::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_LCRY static int update(UPDATE_FUNC_ARGS)
+int Element_LCRY::update(UPDATE_FUNC_ARGS)
+
+{
+ int r, rx, ry;
+ if(parts[i].tmp==1 || parts[i].tmp==0)
+ {
+ if(parts[i].tmp==1)
+ {
+ if(parts[i].life<=0)
+ parts[i].tmp = 0;
+ else
+ {
+ parts[i].life-=2;
+ if(parts[i].life < 0)
+ parts[i].life = 0;
+ parts[i].tmp2 = parts[i].life;
+ }
+ }
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_LCRY && parts[r>>8].tmp == 3)
+ {
+ parts[r>>8].tmp = 1;
+ }
+ }
+ }
+ else if(parts[i].tmp==2 || parts[i].tmp==3)
+ {
+ if(parts[i].tmp==2)
+ {
+ if(parts[i].life>=10)
+ parts[i].tmp = 3;
+ else
+ {
+ parts[i].life+=2;
+ if(parts[i].life > 10)
+ parts[i].life = 10;
+ parts[i].tmp2 = parts[i].life;
+ }
+ }
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_LCRY && parts[r>>8].tmp == 0)
+ {
+ parts[r>>8].tmp = 2;
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_LCRY static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_LCRY::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ if(ren->decorations_enable && cpart->dcolour && (cpart->dcolour&0xFF000000))
+ {
+ *colr = (cpart->dcolour>>16)&0xFF;
+ *colg = (cpart->dcolour>>8)&0xFF;
+ *colb = (cpart->dcolour)&0xFF;
+
+ if(cpart->tmp2<10){
+ *colr /= 10-cpart->tmp2;
+ *colg /= 10-cpart->tmp2;
+ *colb /= 10-cpart->tmp2;
+ }
+
+ }
+ else
+ {
+ *colr = *colg = *colb = 0x50+((cpart->tmp2>10?10:cpart->tmp2)*10);
+ }
+ *pixel_mode |= NO_DECO;
+ return 0;
+
+ /*int lifemod = ((cpart->tmp2>10?10:cpart->tmp2)*10);
+ *colr += lifemod;
+ *colg += lifemod;
+ *colb += lifemod;
+ if(decorations_enable && cpart->dcolour && cpart->dcolour&0xFF000000)
+ {
+ lifemod *= 2.5f;
+ if(lifemod < 40)
+ lifemod = 40;
+ *colr = (lifemod*((cpart->dcolour>>16)&0xFF) + (255-lifemod)**colr) >> 8;
+ *colg = (lifemod*((cpart->dcolour>>8)&0xFF) + (255-lifemod)**colg) >> 8;
+ *colb = (lifemod*((cpart->dcolour)&0xFF) + (255-lifemod)**colb) >> 8;
+ }
+ *pixel_mode |= NO_DECO;
+ return 0;*/
+}
+
+
+Element_LCRY::~Element_LCRY() {} \ No newline at end of file
diff --git a/src/simulation/elements/LIFE.cpp b/src/simulation/elements/LIFE.cpp
new file mode 100644
index 0000000..4207931
--- /dev/null
+++ b/src/simulation/elements/LIFE.cpp
@@ -0,0 +1,125 @@
+#include "simulation/Elements.h"
+
+bool Element_GOL_colourInit = false;
+pixel Element_GOL_colour[NGOL];
+
+//#TPT-Directive ElementClass Element_LIFE PT_LIFE 78
+Element_LIFE::Element_LIFE()
+{
+ Identifier = "DEFAULT_PT_LIFE";
+ Name = "LIFE";
+ Colour = PIXPACK(0x0CAC00);
+ MenuVisible = 0;
+ MenuSection = SC_LIFE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = 9000.0f;
+ HeatConduct = 40;
+ Description = "Game Of Life! B3/S23";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID|PROP_LIFE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+ Graphics = &Element_LIFE::graphics;
+
+ if(!Element_GOL_colourInit)
+ {
+ Element_GOL_colourInit = true;
+
+
+ int golMenuCount;
+ gol_menu * golMenuT = LoadGOLMenu(golMenuCount);
+ for(int i = 0; i < golMenuCount && i < NGOL; i++)
+ {
+ Element_GOL_colour[i] = golMenuT[i].colour;
+ }
+ free(golMenuT);
+ }
+}
+
+
+//#TPT-Directive ElementHeader Element_LIFE static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_LIFE::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ pixel pc;
+ if (cpart->ctype==NGT_LOTE)//colors for life states
+ {
+ if (cpart->tmp==2)
+ pc = PIXRGB(255, 128, 0);
+ else if (cpart->tmp==1)
+ pc = PIXRGB(255, 255, 0);
+ else
+ pc = PIXRGB(255, 0, 0);
+ }
+ else if (cpart->ctype==NGT_FRG2)//colors for life states
+ {
+ if (cpart->tmp==2)
+ pc = PIXRGB(0, 100, 50);
+ else
+ pc = PIXRGB(0, 255, 90);
+ }
+ else if (cpart->ctype==NGT_STAR)//colors for life states
+ {
+ if (cpart->tmp==4)
+ pc = PIXRGB(0, 0, 128);
+ else if (cpart->tmp==3)
+ pc = PIXRGB(0, 0, 150);
+ else if (cpart->tmp==2)
+ pc = PIXRGB(0, 0, 190);
+ else if (cpart->tmp==1)
+ pc = PIXRGB(0, 0, 230);
+ else
+ pc = PIXRGB(0, 0, 70);
+ }
+ else if (cpart->ctype==NGT_FROG)//colors for life states
+ {
+ if (cpart->tmp==2)
+ pc = PIXRGB(0, 100, 0);
+ else
+ pc = PIXRGB(0, 255, 0);
+ }
+ else if (cpart->ctype==NGT_BRAN)//colors for life states
+ {
+ if (cpart->tmp==1)
+ pc = PIXRGB(150, 150, 0);
+ else
+ pc = PIXRGB(255, 255, 0);
+ } else {
+ pc = Element_GOL_colour[cpart->ctype];
+ }
+ *colr = PIXR(pc);
+ *colg = PIXG(pc);
+ *colb = PIXB(pc);
+ return 0;
+}
+
+
+Element_LIFE::~Element_LIFE() {} \ No newline at end of file
diff --git a/src/simulation/elements/LIGH.cpp b/src/simulation/elements/LIGH.cpp
new file mode 100644
index 0000000..c0c723e
--- /dev/null
+++ b/src/simulation/elements/LIGH.cpp
@@ -0,0 +1,359 @@
+#include "simulation/Elements.h"
+
+//#TPT-Directive ElementClass Element_LIGH PT_LIGH 87
+Element_LIGH::Element_LIGH()
+{
+ Identifier = "DEFAULT_PT_LIGH";
+ Name = "LIGH";
+ Colour = PIXPACK(0xFFFFC0);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "More realistic lightning. Set pen size to set the size of the lightning.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_LIGH::update;
+ Graphics = &Element_LIGH::graphics;
+}
+
+#define LIGHTING_POWER 0.65
+
+//#TPT-Directive ElementHeader Element_LIGH static int update(UPDATE_FUNC_ARGS)
+int Element_LIGH::update(UPDATE_FUNC_ARGS)
+
+{
+ /*
+ *
+ * tmp2:
+ * -1 - part will be removed
+ * 0 - "branches" of the lightning
+ * 1 - bending
+ * 2 - branching
+ * 3 - transfer spark or make destruction
+ * 4 - first pixel
+ *
+ * life - "thickness" of lighting (but anyway one pixel)
+ *
+ * tmp - angle of lighting, measured in degrees anticlockwise from the positive x direction
+ *
+ */
+ int r,rx,ry, multipler, powderful;
+ float angle, angle2=-1;
+ int pNear = 0;
+ powderful = powderful = parts[i].temp*(1+parts[i].life/40)*LIGHTING_POWER;
+ Element_FIRE::update(UPDATE_FUNC_SUBCALL_ARGS);
+ if (sim->aheat_enable)
+ {
+ sim->hv[y/CELL][x/CELL]+=powderful/50;
+ if (sim->hv[y/CELL][x/CELL]>MAX_TEMP)
+ sim->hv[y/CELL][x/CELL]=MAX_TEMP;
+ }
+
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_LIGH && (r&0xFF)!=PT_TESC)
+ {
+ if ((r&0xFF)!=PT_CLNE&&(r&0xFF)!=PT_THDR&&(r&0xFF)!=PT_DMND&&(r&0xFF)!=PT_FIRE&&(r&0xFF)!=PT_NEUT&&(r&0xFF)!=PT_PHOT)
+ {
+ if ((sim->elements[r&0xFF].Properties&PROP_CONDUCTS) && parts[r>>8].life==0)
+ {
+ sim->create_part(r>>8,x+rx,y+ry,PT_SPRK);
+ }
+ sim->pv[y/CELL][x/CELL] += powderful/400;
+ if (sim->elements[r&0xFF].HeatConduct) parts[r>>8].temp = restrict_flt(parts[r>>8].temp+powderful/1.5, MIN_TEMP, MAX_TEMP);
+ }
+ if ((r&0xFF)==PT_DEUT || (r&0xFF)==PT_PLUT) // start nuclear reactions
+ {
+ parts[r>>8].temp = restrict_flt(parts[r>>8].temp+powderful, MIN_TEMP, MAX_TEMP);
+ sim->pv[y/CELL][x/CELL] +=powderful/35;
+ if (rand()%3==0)
+ {
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_NEUT);
+ parts[r>>8].life = rand()%480+480;
+ parts[r>>8].vx=rand()%10-5;
+ parts[r>>8].vy=rand()%10-5;
+ }
+ }
+ if ((r&0xFF)==PT_COAL || (r&0xFF)==PT_BCOL) // ignite coal
+ {
+ if (parts[r>>8].life>100) {
+ parts[r>>8].life = 99;
+ }
+ }
+ if (sim->elements[r&0xFF].HeatConduct)
+ parts[r>>8].temp = restrict_flt(parts[r>>8].temp+powderful/10, MIN_TEMP, MAX_TEMP);
+ if (((r&0xFF)==PT_STKM && sim->player.elem!=PT_LIGH) || ((r&0xFF)==PT_STKM2 && sim->player2.elem!=PT_LIGH))
+ {
+ parts[r>>8].life-=powderful/100;
+ }
+ }
+ }
+ if (parts[i].tmp2==3)
+ {
+ parts[i].tmp2=0;
+ return 1;
+ }
+
+ if (parts[i].tmp2==-1)
+ {
+ sim->kill_part(i);
+ return 1;
+ }
+ if (parts[i].tmp2<=0 || parts[i].life<=1)
+ {
+ if (parts[i].tmp2>0)
+ parts[i].tmp2=0;
+ parts[i].tmp2--;
+ return 1;
+ }
+ if (parts[i].tmp2<=-2)
+ {
+ sim->kill_part(i);
+ return 1;
+ }
+
+ angle2=-1;
+
+ pNear = LIGH_nearest_part(sim, i, parts[i].life*2.5);
+ if (pNear!=-1)
+ {
+ int t=parts[pNear].type;
+ float n_angle; // angle to nearest part
+ float angle_diff;
+ rx=parts[pNear].x-x;
+ ry=parts[pNear].y-y;
+ if (rx!=0 || ry!=0)
+ n_angle = atan2f(-ry, rx);
+ else
+ n_angle = 0;
+ if (n_angle<0)
+ n_angle+=M_PI*2;
+ angle_diff = fabsf(n_angle-parts[i].tmp*M_PI/180);
+ if (angle_diff>M_PI)
+ angle_diff = M_PI*2 - angle_diff;
+ if (parts[i].life<5 || angle_diff<M_PI*0.8) // lightning strike
+ {
+ create_line_par(sim, x, y, x+rx, y+ry, PT_LIGH, parts[i].temp, parts[i].life, parts[i].tmp-90, 0);
+
+ if (t!=PT_TESC)
+ {
+ pNear=contact_part(sim, pNear, PT_LIGH);
+ if (pNear!=-1)
+ {
+ parts[pNear].tmp2=3;
+ parts[pNear].life=(int)(1.0*parts[i].life/2-1);
+ parts[pNear].tmp=parts[i].tmp-180;
+ parts[pNear].temp=parts[i].temp;
+ }
+ }
+ }
+ else pNear=-1;
+ }
+
+ //if (parts[i].tmp2==1/* || near!=-1*/)
+ //angle=0;//parts[i].tmp-30+rand()%60;
+ angle = parts[i].tmp-30+rand()%60;
+ if (angle<0)
+ angle+=360;
+ if (angle>=360)
+ angle-=360;
+ if (parts[i].tmp2==2 && pNear==-1)
+ {
+ angle2=angle+100-rand()%200;
+ if (angle2<0)
+ angle2+=360;
+ if (angle2>=360)
+ angle-=360;
+ }
+
+ multipler=parts[i].life*1.5+rand()%((int)(parts[i].life+1));
+ rx=cos(angle*M_PI/180)*multipler;
+ ry=-sin(angle*M_PI/180)*multipler;
+ create_line_par(sim, x, y, x+rx, y+ry, PT_LIGH, parts[i].temp, parts[i].life, angle, 0);
+
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if ((r&0xFF)==PT_LIGH)
+ {
+ parts[r>>8].tmp2=1+(rand()%200>parts[i].tmp2*parts[i].tmp2/10+60);
+ parts[r>>8].life=(int)(1.0*parts[i].life/1.5-rand()%2);
+ parts[r>>8].tmp=angle;
+ parts[r>>8].temp=parts[i].temp;
+ }
+ }
+
+ if (angle2!=-1)
+ {
+ multipler=parts[i].life*1.5+rand()%((int)(parts[i].life+1));
+ rx=cos(angle2*M_PI/180)*multipler;
+ ry=-sin(angle2*M_PI/180)*multipler;
+ create_line_par(sim, x, y, x+rx, y+ry, PT_LIGH, parts[i].temp, parts[i].life, angle2, 0);
+
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if ((r&0xFF)==PT_LIGH)
+ {
+ parts[r>>8].tmp2=1+(rand()%200>parts[i].tmp2*parts[i].tmp2/10+40);
+ parts[r>>8].life=(int)(1.0*parts[i].life/1.5-rand()%2);
+ parts[r>>8].tmp=angle;
+ parts[r>>8].temp=parts[i].temp;
+ }
+ }
+ }
+
+ parts[i].tmp2=-1;
+ return 1;
+}
+
+//#TPT-Directive ElementHeader Element_LIGH static int LIGH_nearest_part(Simulation * sim, int ci, int max_d)
+int Element_LIGH::LIGH_nearest_part(Simulation * sim, int ci, int max_d)
+{
+ int distance = (max_d!=-1)?max_d:MAX_DISTANCE;
+ int ndistance = 0;
+ int id = -1;
+ int i = 0;
+ int cx = (int)sim->parts[ci].x;
+ int cy = (int)sim->parts[ci].y;
+ for (i=0; i<=sim->parts_lastActiveIndex; i++)
+ {
+ if (sim->parts[i].type && sim->parts[i].life && i!=ci && sim->parts[i].type!=PT_LIGH && sim->parts[i].type!=PT_THDR && sim->parts[i].type!=PT_NEUT && sim->parts[i].type!=PT_PHOT)
+ {
+ ndistance = abs(cx-sim->parts[i].x)+abs(cy-sim->parts[i].y);// Faster but less accurate Older: sqrt(pow(cx-parts[i].x, 2)+pow(cy-parts[i].y, 2));
+ if (ndistance<distance)
+ {
+ distance = ndistance;
+ id = i;
+ }
+ }
+ }
+ return id;
+}
+
+//#TPT-Directive ElementHeader Element_LIGH static int contact_part(Simulation * sim, int i, int tp)
+int Element_LIGH::contact_part(Simulation * sim, int i, int tp)
+{
+ int x=sim->parts[i].x, y=sim->parts[i].y;
+ int r,rx,ry;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = sim->pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==tp)
+ return r>>8;
+ }
+ return -1;
+}
+
+//#TPT-Directive ElementHeader Element_LIGH static void create_line_par(Simulation * sim, int x1, int y1, int x2, int y2, int c, int temp, int life, int tmp, int tmp2)
+void Element_LIGH::create_line_par(Simulation * sim, int x1, int y1, int x2, int y2, int c, int temp, int life, int tmp, int tmp2)
+{
+ int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy;
+ float e, de;
+ if (c==WL_EHOLE || c==WL_ALLOWGAS || c==WL_ALLOWALLELEC || c==WL_ALLOWSOLID || c==WL_ALLOWAIR || c==WL_WALL || c==WL_DESTROYALL || c==WL_ALLOWLIQUID || c==WL_FAN || c==WL_STREAM || c==WL_DETECT || c==WL_EWALL || c==WL_WALLELEC)
+ return; // this function only for particles, no walls
+ if (cp)
+ {
+ y = x1;
+ x1 = y1;
+ y1 = y;
+ y = x2;
+ x2 = y2;
+ y2 = y;
+ }
+ if (x1 > x2)
+ {
+ y = x1;
+ x1 = x2;
+ x2 = y;
+ y = y1;
+ y1 = y2;
+ y2 = y;
+ }
+ dx = x2 - x1;
+ dy = abs(y2 - y1);
+ e = 0.0f;
+ if (dx)
+ de = dy/(float)dx;
+ else
+ de = 0.0f;
+ y = y1;
+ sy = (y1<y2) ? 1 : -1;
+ for (x=x1; x<=x2; x++)
+ {
+ int p;
+ if (cp)
+ p = sim->create_part(-1, y, x, c);
+ else
+ p = sim->create_part(-1, x, y,c);
+ if (p!=-1)
+ {
+ sim->parts[p].life = life;
+ sim->parts[p].temp = temp;
+ sim->parts[p].tmp = tmp;
+ sim->parts[p].tmp2 = tmp2;
+ }
+ e += de;
+ if (e >= 0.5f)
+ {
+ y += sy;
+ e -= 1.0f;
+ }
+ }
+}
+
+
+//#TPT-Directive ElementHeader Element_LIGH static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_LIGH::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *firea = 120;
+ *firer = *colr = 235;
+ *fireg = *colg = 245;
+ *fireb = *colb = 255;
+ *pixel_mode |= PMODE_GLOW | FIRE_ADD;
+ return 1;
+}
+
+
+Element_LIGH::~Element_LIGH() {} \ No newline at end of file
diff --git a/src/simulation/elements/LNTG.cpp b/src/simulation/elements/LNTG.cpp
new file mode 100644
index 0000000..ac97191
--- /dev/null
+++ b/src/simulation/elements/LNTG.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_LNTG PT_LNTG 37
+Element_LNTG::Element_LNTG()
+{
+ Identifier = "DEFAULT_PT_LNTG";
+ Name = "LN2";
+ Colour = PIXPACK(0x80A0DF);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 30;
+
+ Temperature = 70.15f;
+ HeatConduct = 70;
+ Description = "Liquid Nitrogen. Very cold.";
+
+ State = ST_SOLID;
+ Properties = TYPE_LIQUID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 63.0f;
+ LowTemperatureTransition = PT_NICE;
+ HighTemperature = 77.0f;
+ HighTemperatureTransition = PT_NONE;
+
+ Update = NULL;
+
+}
+
+Element_LNTG::~Element_LNTG() {} \ No newline at end of file
diff --git a/src/simulation/elements/LO2.cpp b/src/simulation/elements/LO2.cpp
new file mode 100644
index 0000000..e032b9e
--- /dev/null
+++ b/src/simulation/elements/LO2.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_LO2 PT_LO2 60
+Element_LO2::Element_LO2()
+{
+ Identifier = "DEFAULT_PT_LO2";
+ Name = "LOXY";
+ Colour = PIXPACK(0x80A0EF);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 5000;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 30;
+
+ Temperature = 80.0f;
+ HeatConduct = 70;
+ Description = "Liquid Oxygen. Very cold. Reacts with fire";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 90.1f;
+ HighTemperatureTransition = PT_O2;
+
+ Update = NULL;
+
+}
+
+Element_LO2::~Element_LO2() {} \ No newline at end of file
diff --git a/src/simulation/elements/LOLZ.cpp b/src/simulation/elements/LOLZ.cpp
new file mode 100644
index 0000000..a5e8371
--- /dev/null
+++ b/src/simulation/elements/LOLZ.cpp
@@ -0,0 +1,71 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_LOLZ PT_LOLZ 123
+Element_LOLZ::Element_LOLZ()
+{
+ Identifier = "DEFAULT_PT_LOLZ";
+ Name = "LOLZ";
+ Colour = PIXPACK(0x569212);
+ MenuVisible = 1;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.0f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = 373.0f;
+ HeatConduct = 40;
+ Description = "Lolz";
+
+ State = ST_GAS;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_LOLZ::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_LOLZ static int RuleTable[9][9]
+int Element_LOLZ::RuleTable[9][9] =
+{
+ {0,0,0,0,0,0,0,0,0},
+ {1,0,0,0,0,0,1,0,0},
+ {1,0,0,0,0,0,1,0,0},
+ {1,0,0,1,1,0,0,1,0},
+ {1,0,1,0,0,1,0,1,0},
+ {1,0,1,0,0,1,0,1,0},
+ {0,1,0,1,1,0,0,1,0},
+ {0,1,0,0,0,0,0,1,0},
+ {0,1,0,0,0,0,0,1,0},
+};
+
+//#TPT-Directive ElementHeader Element_LOLZ static int update(UPDATE_FUNC_ARGS)
+int Element_LOLZ::update(UPDATE_FUNC_ARGS)
+ {
+ sim->ISLOLZ = true;
+ return 0;
+}
+
+
+Element_LOLZ::~Element_LOLZ() {} \ No newline at end of file
diff --git a/src/simulation/elements/LOVE.cpp b/src/simulation/elements/LOVE.cpp
new file mode 100644
index 0000000..3e7b3d4
--- /dev/null
+++ b/src/simulation/elements/LOVE.cpp
@@ -0,0 +1,71 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_LOVE PT_LOVE 94
+Element_LOVE::Element_LOVE()
+{
+ Identifier = "DEFAULT_PT_LOVE";
+ Name = "LOVE";
+ Colour = PIXPACK(0xFF30FF);
+ MenuVisible = 1;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.0f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = 373.0f;
+ HeatConduct = 40;
+ Description = "Love...";
+
+ State = ST_GAS;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_LOVE::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_LOVE static int RuleTable[9][9]
+int Element_LOVE::RuleTable[9][9] =
+{
+ {0,0,1,1,0,0,0,0,0},
+ {0,1,0,0,1,1,0,0,0},
+ {1,0,0,0,0,0,1,0,0},
+ {1,0,0,0,0,0,0,1,0},
+ {0,1,0,0,0,0,0,0,1},
+ {1,0,0,0,0,0,0,1,0},
+ {1,0,0,0,0,0,1,0,0},
+ {0,1,0,0,1,1,0,0,0},
+ {0,0,1,1,0,0,0,0,0},
+};
+
+//#TPT-Directive ElementHeader Element_LOVE static int update(UPDATE_FUNC_ARGS)
+int Element_LOVE::update(UPDATE_FUNC_ARGS)
+ {
+ sim->ISLOVE = true;
+ return 0;
+}
+
+
+Element_LOVE::~Element_LOVE() {} \ No newline at end of file
diff --git a/src/simulation/elements/LRBD.cpp b/src/simulation/elements/LRBD.cpp
new file mode 100644
index 0000000..f609cce
--- /dev/null
+++ b/src/simulation/elements/LRBD.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_LRBD PT_LRBD 42
+Element_LRBD::Element_LRBD()
+{
+ Identifier = "DEFAULT_PT_LRBD";
+ Name = "LRBD";
+ Colour = PIXPACK(0xAAAAAA);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.3f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.15f;
+ Diffusion = 0.00f;
+ HotAir = 0.000001f* CFDS;
+ Falldown = 2;
+
+ Flammable = 1000;
+ Explosive = 1;
+ Meltable = 0;
+ Hardness = 2;
+
+ Weight = 45;
+
+ Temperature = R_TEMP+45.0f+273.15f;
+ HeatConduct = 170;
+ Description = "Liquid Rubidium.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_CONDUCTS|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 311.0f;
+ LowTemperatureTransition = PT_RBDM;
+ HighTemperature = 961.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = NULL;
+
+}
+
+Element_LRBD::~Element_LRBD() {} \ No newline at end of file
diff --git a/src/simulation/elements/MERC.cpp b/src/simulation/elements/MERC.cpp
new file mode 100644
index 0000000..1fec32a
--- /dev/null
+++ b/src/simulation/elements/MERC.cpp
@@ -0,0 +1,121 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_MERC PT_MERC 152
+Element_MERC::Element_MERC()
+{
+ Identifier = "DEFAULT_PT_MERC";
+ Name = "MERC";
+ Colour = PIXPACK(0x736B6D);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.3f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 91;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Mercury. Volume changes with temperature, Conductive.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_CONDUCTS|PROP_NEUTABSORB|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_MERC::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_MERC static int update(UPDATE_FUNC_ARGS)
+int Element_MERC::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, trade, np;
+ int maxtmp = ((10000/(parts[i].temp + 1))-1);
+ if ((10000%((int)parts[i].temp+1))>rand()%((int)parts[i].temp+1))
+ maxtmp ++;
+ if (parts[i].tmp < maxtmp)
+ {
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r || (parts[i].tmp >=maxtmp))
+ continue;
+ if ((r&0xFF)==PT_MERC&&33>=rand()/(RAND_MAX/100)+1)
+ {
+ if ((parts[i].tmp + parts[r>>8].tmp + 1) <= maxtmp)
+ {
+ parts[i].tmp += parts[r>>8].tmp + 1;
+ sim->kill_part(r>>8);
+ }
+ }
+ }
+ }
+ else
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (parts[i].tmp<=maxtmp)
+ continue;
+ if ((!r)&&parts[i].tmp>=1)//if nothing then create deut
+ {
+ np = sim->create_part(-1,x+rx,y+ry,PT_MERC);
+ if (np<0) continue;
+ parts[i].tmp--;
+ parts[np].temp = parts[i].temp;
+ parts[np].tmp = 0;
+ }
+ }
+ for ( trade = 0; trade<4; trade ++)
+ {
+ rx = rand()%5-2;
+ ry = rand()%5-2;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_MERC&&(parts[i].tmp>parts[r>>8].tmp)&&parts[i].tmp>0)//diffusion
+ {
+ int temp = parts[i].tmp - parts[r>>8].tmp;
+ if (temp ==1)
+ {
+ parts[r>>8].tmp ++;
+ parts[i].tmp --;
+ }
+ else if (temp>0)
+ {
+ parts[r>>8].tmp += temp/2;
+ parts[i].tmp -= temp/2;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_MERC::~Element_MERC() {} \ No newline at end of file
diff --git a/src/simulation/elements/METL.cpp b/src/simulation/elements/METL.cpp
new file mode 100644
index 0000000..17b09d0
--- /dev/null
+++ b/src/simulation/elements/METL.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_METL PT_METL 14
+Element_METL::Element_METL()
+{
+ Identifier = "DEFAULT_PT_METL";
+ Name = "METL";
+ Colour = PIXPACK(0x404060);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Solid. Conducts electricity. Meltable.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1273.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = NULL;
+
+}
+
+Element_METL::~Element_METL() {} \ No newline at end of file
diff --git a/src/simulation/elements/MORT.cpp b/src/simulation/elements/MORT.cpp
new file mode 100644
index 0000000..f8f1d8c
--- /dev/null
+++ b/src/simulation/elements/MORT.cpp
@@ -0,0 +1,57 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_MORT PT_MORT 77
+Element_MORT::Element_MORT()
+{
+ Identifier = "DEFAULT_PT_MORT";
+ Name = "MORT";
+ Colour = PIXPACK(0xE0E0E0);
+ MenuVisible = 1;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 1.00f;
+ Collision = -0.99f;
+ Gravity = 0.0f;
+ Diffusion = 0.01f;
+ HotAir = 0.002f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = -1;
+
+ Temperature = R_TEMP+4.0f +273.15f;
+ HeatConduct = 60;
+ Description = "Steam Train.";
+
+ State = ST_NONE;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_MORT::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_MORT static int update(UPDATE_FUNC_ARGS)
+int Element_MORT::update(UPDATE_FUNC_ARGS)
+ {
+ sim->create_part(-1, x, y-1, PT_SMKE);
+ return 0;
+}
+
+
+Element_MORT::~Element_MORT() {} \ No newline at end of file
diff --git a/src/simulation/elements/MWAX.cpp b/src/simulation/elements/MWAX.cpp
new file mode 100644
index 0000000..4153893
--- /dev/null
+++ b/src/simulation/elements/MWAX.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_MWAX PT_MWAX 34
+Element_MWAX::Element_MWAX()
+{
+ Identifier = "DEFAULT_PT_MWAX";
+ Name = "MWAX";
+ Colour = PIXPACK(0xE0E0AA);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.3f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.15f;
+ Diffusion = 0.00f;
+ HotAir = 0.000001f* CFDS;
+ Falldown = 2;
+
+ Flammable = 5;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 2;
+
+ Weight = 25;
+
+ Temperature = R_TEMP+28.0f+273.15f;
+ HeatConduct = 44;
+ Description = "Liquid Wax.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 318.0f;
+ LowTemperatureTransition = PT_WAX;
+ HighTemperature = 673.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = NULL;
+
+}
+
+Element_MWAX::~Element_MWAX() {} \ No newline at end of file
diff --git a/src/simulation/elements/NBHL.cpp b/src/simulation/elements/NBHL.cpp
new file mode 100644
index 0000000..2ccb864
--- /dev/null
+++ b/src/simulation/elements/NBHL.cpp
@@ -0,0 +1,60 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_NBHL PT_NBHL 150
+Element_NBHL::Element_NBHL()
+{
+ Identifier = "DEFAULT_PT_NBHL";
+ Name = "BHOL";
+ Colour = PIXPACK(0x202020);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 186;
+ Description = "Black hole (Requires newtonian gravity)";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_NBHL::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_NBHL static int update(UPDATE_FUNC_ARGS)
+int Element_NBHL::update(UPDATE_FUNC_ARGS)
+ {
+ if (parts[i].tmp)
+ sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] += restrict_flt(0.001f*parts[i].tmp, 0.1f, 51.2f);
+ else
+ sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] += 0.1f;
+ return 0;
+}
+
+
+Element_NBHL::~Element_NBHL() {}
diff --git a/src/simulation/elements/NBLE.cpp b/src/simulation/elements/NBLE.cpp
new file mode 100644
index 0000000..3093d45
--- /dev/null
+++ b/src/simulation/elements/NBLE.cpp
@@ -0,0 +1,93 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_NBLE PT_NBLE 52
+Element_NBLE::Element_NBLE()
+{
+ Identifier = "DEFAULT_PT_NBLE";
+ Name = "NBLE";
+ Colour = PIXPACK(0xEB4917);
+ MenuVisible = 1;
+ MenuSection = SC_GAS;
+ Enabled = 1;
+
+ Advection = 1.0f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.30f;
+ Collision = -0.1f;
+ Gravity = 0.0f;
+ Diffusion = 0.75f;
+ HotAir = 0.001f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 1;
+
+ Temperature = R_TEMP+2.0f +273.15f;
+ HeatConduct = 106;
+ Description = "Noble Gas. Diffuses. Conductive. Ionizes into plasma when introduced to electricity";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS|PROP_CONDUCTS|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_NBLE::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_NBLE static int update(UPDATE_FUNC_ARGS)
+int Element_NBLE::update(UPDATE_FUNC_ARGS)
+
+{
+ if (parts[i].temp > 5273.15 && sim->pv[y/CELL][x/CELL] > 100.0f)
+ {
+ parts[i].tmp = 1;
+ if (rand()%5 < 1)
+ {
+ int j;
+ float temp = parts[i].temp;
+ sim->create_part(i,x,y,PT_CO2);
+
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_NEUT);
+ if (j != -1)
+ parts[j].temp = temp;
+ if (!(rand()%25))
+ {
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_ELEC);
+ if (j != -1)
+ parts[j].temp = temp;
+ }
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_PHOT);
+ if (j != -1)
+ {
+ parts[j].ctype = 0xF800000;
+ parts[j].temp = temp;
+ }
+
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_PLSM);
+ if (j != -1)
+ {
+ parts[j].temp = temp;
+ parts[j].tmp |= 4;
+ }
+
+ parts[i].temp = temp+1750+rand()%500;
+ sim->pv[y/CELL][x/CELL] += 50;
+ }
+ }
+ return 0;
+}
+
+
+Element_NBLE::~Element_NBLE() {} \ No newline at end of file
diff --git a/src/simulation/elements/NEUT.cpp b/src/simulation/elements/NEUT.cpp
new file mode 100644
index 0000000..4641370
--- /dev/null
+++ b/src/simulation/elements/NEUT.cpp
@@ -0,0 +1,207 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_NEUT PT_NEUT 18
+Element_NEUT::Element_NEUT()
+{
+ Identifier = "DEFAULT_PT_NEUT";
+ Name = "NEUT";
+ Colour = PIXPACK(0x20E0FF);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 1.00f;
+ Collision = -0.99f;
+ Gravity = 0.0f;
+ Diffusion = 0.01f;
+ HotAir = 0.002f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = -1;
+
+ Temperature = R_TEMP+4.0f +273.15f;
+ HeatConduct = 60;
+ Description = "Neutrons. Interact with matter in odd ways.";
+
+ State = ST_GAS;
+ Properties = TYPE_ENERGY|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_NEUT::update;
+ Graphics = &Element_NEUT::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_NEUT static int update(UPDATE_FUNC_ARGS)
+int Element_NEUT::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, rt;
+ int pressureFactor = 3 + (int)sim->pv[y/CELL][x/CELL];
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_WATR || (r&0xFF)==PT_ICEI || (r&0xFF)==PT_SNOW)
+ {
+ parts[i].vx *= 0.995;
+ parts[i].vy *= 0.995;
+ }
+ if ((r&0xFF)==PT_PLUT && pressureFactor>(rand()%1000))
+ {
+ if (33>rand()%100)
+ {
+ sim->create_part(r>>8, x+rx, y+ry, rand()%3 ? PT_LAVA : PT_URAN);
+ parts[r>>8].temp = MAX_TEMP;
+ if (parts[r>>8].type==PT_LAVA) {
+ parts[r>>8].tmp = 100;
+ parts[r>>8].ctype = PT_PLUT;
+ }
+ }
+ else
+ {
+ sim->create_part(r>>8, x+rx, y+ry, PT_NEUT);
+ parts[r>>8].vx = 0.25f*parts[r>>8].vx + parts[i].vx;
+ parts[r>>8].vy = 0.25f*parts[r>>8].vy + parts[i].vy;
+ }
+ sim->pv[y/CELL][x/CELL] += 10.0f * CFDS; //Used to be 2, some people said nukes weren't powerful enough
+ Element_FIRE::update(UPDATE_FUNC_SUBCALL_ARGS);
+ }
+#ifdef SDEUT
+ else if ((r&0xFF)==PT_DEUT && (pressureFactor+1+(parts[r>>8].life/100))>(rand()%1000))
+ {
+ create_n_parts(sim, parts[r>>8].life, x+rx, y+ry, parts[i].vx, parts[i].vy, restrict_flt(parts[r>>8].temp + parts[r>>8].life*500, MIN_TEMP, MAX_TEMP), PT_NEUT);
+ sim->kill_part(r>>8);
+ }
+#else
+ else if ((r&0xFF)==PT_DEUT && (pressureFactor+1)>(rand()%1000))
+ {
+ create_part(r>>8, x+rx, y+ry, PT_NEUT);
+ parts[r>>8].vx = 0.25f*parts[r>>8].vx + parts[i].vx;
+ parts[r>>8].vy = 0.25f*parts[r>>8].vy + parts[i].vy;
+ if (parts[r>>8].life>0)
+ {
+ parts[r>>8].life --;
+ parts[r>>8].temp = restrict_flt(parts[r>>8].temp + parts[r>>8].life*17, MIN_TEMP, MAX_TEMP);
+ pv[y/CELL][x/CELL] += 6.0f * CFDS;
+ }
+ else
+ sim.kill_part(r>>8);
+ }
+#endif
+ else if ((r&0xFF)==PT_GUNP && 15>(rand()%1000))
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_DUST);
+ else if ((r&0xFF)==PT_DYST && 15>(rand()%1000))
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_YEST);
+ else if ((r&0xFF)==PT_YEST)
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_DYST);
+ else if ((r&0xFF)==PT_WATR && 15>(rand()%100))
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_DSTW);
+ else if ((r&0xFF)==PT_PLEX && 15>(rand()%1000))
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_GOO);
+ else if ((r&0xFF)==PT_NITR && 15>(rand()%1000))
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_DESL);
+ else if ((r&0xFF)==PT_PLNT && 5>(rand()%100))
+ sim->create_part(r>>8, x+rx, y+ry, PT_WOOD);
+ else if ((r&0xFF)==PT_DESL && 15>(rand()%1000))
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_GAS);
+ else if ((r&0xFF)==PT_COAL && 5>(rand()%100))
+ sim->create_part(r>>8, x+rx, y+ry, PT_WOOD);
+ else if ((r&0xFF)==PT_DUST && 5>(rand()%100))
+ sim->part_change_type(r>>8, x+rx, y+ry, PT_FWRK);
+ else if ((r&0xFF)==PT_FWRK && 5>(rand()%100))
+ parts[r>>8].ctype = PT_DUST;
+ else if ((r&0xFF)==PT_ACID && 5>(rand()%100))
+ sim->create_part(r>>8, x+rx, y+ry, PT_ISOZ);
+ else if ((r&0xFF)==PT_TTAN && 5>(rand()%100))
+ {
+ sim->kill_part(i);
+ return 1;
+ }
+ else if ((r&0xFF)==PT_EXOT && 5>(rand()%100))
+ parts[r>>8].life = 1500;
+ /*if(parts[r>>8].type>1 && parts[r>>8].type!=PT_NEUT && parts[r>>8].type-1!=PT_NEUT && parts[r>>8].type-1!=PT_STKM &&
+ (elements[parts[r>>8].type-1].menusection==SC_LIQUID||
+ elements[parts[r>>8].type-1].menusection==SC_EXPLOSIVE||
+ elements[parts[r>>8].type-1].menusection==SC_GAS||
+ elements[parts[r>>8].type-1].menusection==SC_POWDERS) && 15>(rand()%1000))
+ parts[r>>8].type--;*/
+ }
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_NEUT static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_NEUT::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *firea = 120;
+ *firer = 10;
+ *fireg = 80;
+ *fireb = 120;
+
+ *pixel_mode |= FIRE_ADD;
+ return 1;
+}
+
+//#TPT-Directive ElementHeader Element_NEUT static int create_n_parts(Simulation * sim, int n, int x, int y, float vx, float vy, float temp, int t)
+int Element_NEUT::create_n_parts(Simulation * sim, int n, int x, int y, float vx, float vy, float temp, int t)//testing a new deut create part
+{
+ int i, c;
+ n = (n/50);
+ if (n<1) {
+ n = 1;
+ }
+ if (n>340) {
+ n = 340;
+ }
+ if (x<0 || y<0 || x>=XRES || y>=YRES || t<0 || t>=PT_NUM || !sim->elements[t].Enabled)
+ return -1;
+
+ for (c=0; c<n; c++) {
+ float r = (rand()%128+128)/127.0f;
+ float a = (rand()%360)*M_PI/180.0f;
+ if (sim->pfree == -1)
+ return -1;
+ i = sim->pfree;
+ sim->pfree = sim->parts[i].life;
+ if (i>sim->parts_lastActiveIndex) sim->parts_lastActiveIndex = i;
+
+ sim->parts[i].x = (float)x;
+ sim->parts[i].y = (float)y;
+ sim->parts[i].type = t;
+ sim->parts[i].life = rand()%480+480;
+ sim->parts[i].vx = r*cosf(a);
+ sim->parts[i].vy = r*sinf(a);
+ sim->parts[i].ctype = 0;
+ sim->parts[i].temp = temp;
+ sim->parts[i].tmp = 0;
+ if (t!=PT_STKM&&t!=PT_STKM2 && t!=PT_PHOT && t!=PT_NEUT && !sim->pmap[y][x])
+ sim->pmap[y][x] = t|(i<<8);
+ else if ((t==PT_PHOT||t==PT_NEUT) && !sim->photons[y][x])
+ sim->photons[y][x] = t|(i<<8);
+
+ sim->pv[y/CELL][x/CELL] += 6.0f * CFDS;
+ }
+ return 0;
+}
+
+
+Element_NEUT::~Element_NEUT() {}
diff --git a/src/simulation/elements/NICE.cpp b/src/simulation/elements/NICE.cpp
new file mode 100644
index 0000000..8a6086e
--- /dev/null
+++ b/src/simulation/elements/NICE.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_NICE PT_NICE 51
+Element_NICE::Element_NICE()
+{
+ Identifier = "DEFAULT_PT_NICE";
+ Name = "NICE";
+ Colour = PIXPACK(0xC0E0FF);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = -0.0005f* CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 100;
+
+ Temperature = 35.0f;
+ HeatConduct = 46;
+ Description = "Nitrogen Ice.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 63.1f;
+ HighTemperatureTransition = PT_LNTG;
+
+ Update = NULL;
+
+}
+
+Element_NICE::~Element_NICE() {} \ No newline at end of file
diff --git a/src/simulation/elements/NITR.cpp b/src/simulation/elements/NITR.cpp
new file mode 100644
index 0000000..bf0feee
--- /dev/null
+++ b/src/simulation/elements/NITR.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_NITR PT_NITR 8
+Element_NITR::Element_NITR()
+{
+ Identifier = "DEFAULT_PT_NITR";
+ Name = "NITR";
+ Colour = PIXPACK(0x20E010);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.5f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.92f;
+ Loss = 0.97f;
+ Collision = 0.0f;
+ Gravity = 0.2f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 1000;
+ Explosive = 2;
+ Meltable = 0;
+ Hardness = 3;
+
+ Weight = 23;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 50;
+ Description = "Liquid. Pressure sensitive explosive.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 673.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = NULL;
+
+}
+
+Element_NITR::~Element_NITR() {} \ No newline at end of file
diff --git a/src/simulation/elements/NONE.cpp b/src/simulation/elements/NONE.cpp
new file mode 100644
index 0000000..95d9d3e
--- /dev/null
+++ b/src/simulation/elements/NONE.cpp
@@ -0,0 +1,66 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_NONE PT_NONE 0
+Element_NONE::Element_NONE()
+{
+ Identifier = "DEFAULT_PT_NONE";
+ Name = "";
+ Colour = PIXPACK(0x000000);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Erases particles.";
+
+ State = ST_NONE;
+ Properties = 0;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+ IconGenerator = &Element_NONE::iconGen;
+}
+
+//#TPT-Directive ElementHeader Element_NONE static VideoBuffer * iconGen(int, int, int)
+VideoBuffer * Element_NONE::iconGen(int wallID, int width, int height)
+{
+ VideoBuffer * newTexture = new VideoBuffer(width, height);
+
+ for (int j=3; j<(width-4)/2; j++)
+ {
+ newTexture->SetPixel(j+6, j, 0xFF, 0, 0, 255);
+ newTexture->SetPixel(j+7, j, 0xFF, 0, 0, 255);
+ newTexture->SetPixel(-j+19, j, 0xFF, 0, 0, 255);
+ newTexture->SetPixel(-j+20, j, 0xFF, 0, 0, 255);
+ }
+
+ return newTexture;
+}
+
+
+Element_NONE::~Element_NONE() {} \ No newline at end of file
diff --git a/src/simulation/elements/NSCN.cpp b/src/simulation/elements/NSCN.cpp
new file mode 100644
index 0000000..c16e9c4
--- /dev/null
+++ b/src/simulation/elements/NSCN.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_NSCN PT_NSCN 36
+Element_NSCN::Element_NSCN()
+{
+ Identifier = "DEFAULT_PT_NSCN";
+ Name = "NSCN";
+ Colour = PIXPACK(0x505080);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "N-Type Silicon, Will not transfer current to P-Type Silicon.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1687.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = NULL;
+
+}
+
+Element_NSCN::~Element_NSCN() {} \ No newline at end of file
diff --git a/src/simulation/elements/NTCT.cpp b/src/simulation/elements/NTCT.cpp
new file mode 100644
index 0000000..2241468
--- /dev/null
+++ b/src/simulation/elements/NTCT.cpp
@@ -0,0 +1,58 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_NTCT PT_NTCT 43
+Element_NTCT::Element_NTCT()
+{
+ Identifier = "DEFAULT_PT_NTCT";
+ Name = "NTCT";
+ Colour = PIXPACK(0x505040);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Semi-conductor. Only conducts electricity when hot (More than 100C)";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1687.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = &Element_NTCT::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_NTCT static int update(UPDATE_FUNC_ARGS)
+int Element_NTCT::update(UPDATE_FUNC_ARGS)
+ {
+ if (parts[i].temp>295.0f)
+ parts[i].temp -= 2.5f;
+ return 0;
+}
+
+
+Element_NTCT::~Element_NTCT() {} \ No newline at end of file
diff --git a/src/simulation/elements/NWHL.cpp b/src/simulation/elements/NWHL.cpp
new file mode 100644
index 0000000..7fcda4a
--- /dev/null
+++ b/src/simulation/elements/NWHL.cpp
@@ -0,0 +1,57 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_NWHL PT_NWHL 151
+Element_NWHL::Element_NWHL()
+{
+ Identifier = "DEFAULT_PT_NWHL";
+ Name = "WHOL";
+ Colour = PIXPACK(0xFFFFFF);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 186;
+ Description = "White hole (Requires newtonian gravity)";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_NWHL::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_NWHL static int update(UPDATE_FUNC_ARGS)
+int Element_NWHL::update(UPDATE_FUNC_ARGS)
+ {
+ sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] -= 0.1f;
+ return 0;
+}
+
+
+Element_NWHL::~Element_NWHL() {} \ No newline at end of file
diff --git a/src/simulation/elements/O2.cpp b/src/simulation/elements/O2.cpp
new file mode 100644
index 0000000..20bd902
--- /dev/null
+++ b/src/simulation/elements/O2.cpp
@@ -0,0 +1,104 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_O2 PT_O2 61
+Element_O2::Element_O2()
+{
+ Identifier = "DEFAULT_PT_O2";
+ Name = "OXYG";
+ Colour = PIXPACK(0x80A0FF);
+ MenuVisible = 1;
+ MenuSection = SC_GAS;
+ Enabled = 1;
+
+ Advection = 2.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.30f;
+ Collision = -0.1f;
+ Gravity = 0.0f;
+ Diffusion = 3.0f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 1;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Gas. Ignites easily.";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 90.0f;
+ LowTemperatureTransition = PT_LO2;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_O2::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_O2 static int update(UPDATE_FUNC_ARGS)
+int Element_O2::update(UPDATE_FUNC_ARGS)
+{
+ int r,rx,ry;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+
+ if ((r&0xFF)==PT_FIRE)
+ {
+ parts[r>>8].temp+=(rand()/(RAND_MAX/100));
+ if(parts[r>>8].tmp&0x01)
+ parts[r>>8].temp=3473;
+ parts[r>>8].tmp |= 2;
+ }
+ if ((r&0xFF)==PT_FIRE || ((r&0xFF)==PT_PLSM && !(parts[r>>8].tmp&4)))
+ {
+ sim->create_part(i,x,y,PT_FIRE);
+ parts[i].temp+=(rand()/(RAND_MAX/100));
+ parts[i].tmp |= 2;
+ }
+
+ }
+ if (parts[i].temp > 9973.15 && sim->pv[y/CELL][x/CELL] > 250.0f && abs(sim->gravx[((y/CELL)*(XRES/CELL))+(x/CELL)]) + abs(sim->gravy[((y/CELL)*(XRES/CELL))+(x/CELL)]) > 20)
+ {
+ if (rand()%5 < 1)
+ {
+ int j;
+ sim->create_part(i,x,y,PT_BRMT);
+
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_NEUT);
+ if (j != -1)
+ parts[j].temp = 15000;
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_PHOT);
+ if (j != -1)
+ parts[j].temp = 15000;
+ j = sim->create_part(-3,x+rand()%3-1,y+rand()%3-1,PT_PLSM);
+ if (j != -1)
+ {
+ parts[j].temp = 15000;
+ parts[j].tmp |= 4;
+ }
+
+ parts[i].temp = 15000;
+ sim->pv[y/CELL][x/CELL] += 300;
+ }
+ }
+ return 0;
+}
+
+
+Element_O2::~Element_O2() {}
diff --git a/src/simulation/elements/OIL.cpp b/src/simulation/elements/OIL.cpp
new file mode 100644
index 0000000..5dd7595
--- /dev/null
+++ b/src/simulation/elements/OIL.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_OIL PT_OIL 3
+Element_OIL::Element_OIL()
+{
+ Identifier = "DEFAULT_PT_OIL";
+ Name = "OIL";
+ Colour = PIXPACK(0x404010);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 20;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 5;
+
+ Weight = 20;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 42;
+ Description = "Liquid. Flammable.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 333.0f;
+ HighTemperatureTransition = PT_GAS;
+
+ Update = NULL;
+
+}
+
+Element_OIL::~Element_OIL() {} \ No newline at end of file
diff --git a/src/simulation/elements/PBCN.cpp b/src/simulation/elements/PBCN.cpp
new file mode 100644
index 0000000..e3bc7ce
--- /dev/null
+++ b/src/simulation/elements/PBCN.cpp
@@ -0,0 +1,164 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PBCN PT_PBCN 153
+Element_PBCN::Element_PBCN()
+{
+ Identifier = "DEFAULT_PT_PBCN";
+ Name = "PBCN";
+ Colour = PIXPACK(0x3B1D0A);
+ MenuVisible = 1;
+ MenuSection = SC_POWERED;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.97f;
+ Loss = 0.50f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 12;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Powered breakable clone";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_PBCN::update;
+ Graphics = &Element_PBCN::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_PBCN static int update(UPDATE_FUNC_ARGS)
+int Element_PBCN::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ if (parts[i].life>0 && parts[i].life!=10)
+ parts[i].life--;
+ if (!parts[i].tmp2 && sim->pv[y/CELL][x/CELL]>4.0f)
+ parts[i].tmp2 = rand()%40+80;
+ if (parts[i].tmp2)
+ {
+ float advection = 0.1f;
+ parts[i].vx += advection*sim->vx[y/CELL][x/CELL];
+ parts[i].vy += advection*sim->vy[y/CELL][x/CELL];
+ parts[i].tmp2--;
+ if(!parts[i].tmp2){
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled || (parts[i].ctype==PT_LIFE && (parts[i].tmp<0 || parts[i].tmp>=NGOLALT)))
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES)
+ {
+ r = sim->photons[y+ry][x+rx];
+ if (!r)
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PCLN &&
+ (r&0xFF)!=PT_BCLN && (r&0xFF)!=PT_SPRK &&
+ (r&0xFF)!=PT_NSCN && (r&0xFF)!=PT_PSCN &&
+ (r&0xFF)!=PT_STKM && (r&0xFF)!=PT_STKM2 &&
+ (r&0xFF)!=PT_PBCN && (r&0xFF)<PT_NUM)
+ {
+ parts[i].ctype = r&0xFF;
+ if ((r&0xFF)==PT_LIFE || (r&0xFF)==PT_LAVA)
+ parts[i].tmp = parts[r>>8].ctype;
+ }
+ }
+ if (parts[i].life==10)
+ {
+
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_PBCN)
+ {
+ if (parts[r>>8].life<10&&parts[r>>8].life>0)
+ parts[i].life = 9;
+ else if (parts[r>>8].life==0)
+ parts[r>>8].life = 10;
+ }
+ }
+ }
+ if (parts[i].ctype>0 && parts[i].ctype<PT_NUM && sim->elements[parts[i].ctype].Enabled && parts[i].life==10) {
+ if (parts[i].ctype==PT_PHOT) {//create photons a different way
+ for (rx=-1; rx<2; rx++)
+ {
+ for (ry = -1; ry < 2; ry++)
+ {
+ if (rx || ry)
+ {
+ int r = sim->create_part(-1, x + rx, y + ry,
+ parts[i].ctype);
+ if (r != -1)
+ {
+ parts[r].vx = rx * 3;
+ parts[r].vy = ry * 3;
+ if (r>i)
+ {
+ // Make sure movement doesn't happen until next frame, to avoid gaps in the beams of photons produced
+ parts[r].flags |= FLAG_SKIPMOVE;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (parts[i].ctype==PT_LIFE) {//create life a different way
+ for (rx=-1; rx<2; rx++) {
+ for (ry=-1; ry<2; ry++) {
+ sim->create_part(-1, x+rx, y+ry, parts[i].ctype|(parts[i].tmp<<8));
+ }
+ }
+ }
+ else if (parts[i].ctype!=PT_LIGH || (rand()%30)==0)
+ {
+ int np = sim->create_part(-1, x+rand()%3-1, y+rand()%3-1, parts[i].ctype);
+ if (np>=0)
+ {
+ if (parts[i].ctype==PT_LAVA && parts[i].tmp>0 && parts[i].tmp<PT_NUM && sim->elements[parts[i].tmp].HighTemperatureTransition==PT_LAVA)
+ parts[np].ctype = parts[i].tmp;
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_PBCN static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_PBCN::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int lifemod = ((cpart->life>10?10:cpart->life)*10);
+ *colr += lifemod;
+ *colg += lifemod/2;
+ return 0;
+}
+
+
+Element_PBCN::~Element_PBCN() {}
diff --git a/src/simulation/elements/PCLN.cpp b/src/simulation/elements/PCLN.cpp
new file mode 100644
index 0000000..7e6b3d7
--- /dev/null
+++ b/src/simulation/elements/PCLN.cpp
@@ -0,0 +1,154 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PCLN PT_PCLN 74
+Element_PCLN::Element_PCLN()
+{
+ Identifier = "DEFAULT_PT_PCLN";
+ Name = "PCLN";
+ Colour = PIXPACK(0x3B3B0A);
+ MenuVisible = 1;
+ MenuSection = SC_POWERED;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Solid. When activated, duplicates any particles it touches.";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_PCLN::update;
+ Graphics = &Element_PCLN::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_PCLN static int update(UPDATE_FUNC_ARGS)
+int Element_PCLN::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ if (parts[i].life>0 && parts[i].life!=10)
+ parts[i].life--;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_SPRK && parts[r>>8].life>0 && parts[r>>8].life<4)
+ {
+ if (parts[r>>8].ctype==PT_PSCN)
+ parts[i].life = 10;
+ else if (parts[r>>8].ctype==PT_NSCN)
+ parts[i].life = 9;
+ }
+ if ((r&0xFF)==PT_PCLN)
+ {
+ if (parts[i].life==10&&parts[r>>8].life<10&&parts[r>>8].life>0)
+ parts[i].life = 9;
+ else if (parts[i].life==0&&parts[r>>8].life==10)
+ parts[i].life = 10;
+ }
+ }
+ if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled || (parts[i].ctype==PT_LIFE && (parts[i].tmp<0 || parts[i].tmp>=NGOLALT)))
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES)
+ {
+ r = sim->photons[y+ry][x+rx];
+ if (!r)
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_CLNE && (r&0xFF)!=PT_PCLN &&
+ (r&0xFF)!=PT_BCLN && (r&0xFF)!=PT_SPRK &&
+ (r&0xFF)!=PT_NSCN && (r&0xFF)!=PT_PSCN &&
+ (r&0xFF)!=PT_STKM && (r&0xFF)!=PT_STKM2 &&
+ (r&0xFF)!=PT_PBCN && (r&0xFF)<PT_NUM)
+ {
+ parts[i].ctype = r&0xFF;
+ if ((r&0xFF)==PT_LIFE || (r&0xFF)==PT_LAVA)
+ parts[i].tmp = parts[r>>8].ctype;
+ }
+ }
+ if (parts[i].ctype>0 && parts[i].ctype<PT_NUM && sim->elements[parts[i].ctype].Enabled && parts[i].life==10) {
+ if (parts[i].ctype==PT_PHOT) {//create photons a different way
+ for (rx=-1; rx<2; rx++)
+ {
+ for (ry = -1; ry < 2; ry++)
+ {
+ if (rx || ry)
+ {
+ int r = sim->create_part(-1, x + rx, y + ry,
+ parts[i].ctype);
+ if (r != -1)
+ {
+ parts[r].vx = rx * 3;
+ parts[r].vy = ry * 3;
+ if (r>i)
+ {
+ // Make sure movement doesn't happen until next frame, to avoid gaps in the beams of photons produced
+ parts[r].flags |= FLAG_SKIPMOVE;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (parts[i].ctype==PT_LIFE) {//create life a different way
+ for (rx=-1; rx<2; rx++) {
+ for (ry=-1; ry<2; ry++) {
+ sim->create_part(-1, x+rx, y+ry, parts[i].ctype|(parts[i].tmp<<8));
+ }
+ }
+ }
+ else if (parts[i].ctype!=PT_LIGH || (rand()%30)==0)
+ {
+ int np = sim->create_part(-1, x+rand()%3-1, y+rand()%3-1, parts[i].ctype);
+ if (np>=0)
+ {
+ if (parts[i].ctype==PT_LAVA && parts[i].tmp>0 && parts[i].tmp<PT_NUM && sim->elements[parts[i].tmp].HighTemperatureTransition==PT_LAVA)
+ parts[np].ctype = parts[i].tmp;
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_PCLN static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_PCLN::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int lifemod = ((cpart->life>10?10:cpart->life)*10);
+ *colr += lifemod;
+ *colg += lifemod;
+ return 0;
+}
+
+
+Element_PCLN::~Element_PCLN() {}
diff --git a/src/simulation/elements/PHOT.cpp b/src/simulation/elements/PHOT.cpp
new file mode 100644
index 0000000..ac8c5c8
--- /dev/null
+++ b/src/simulation/elements/PHOT.cpp
@@ -0,0 +1,141 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PHOT PT_PHOT 31
+Element_PHOT::Element_PHOT()
+{
+ Identifier = "DEFAULT_PT_PHOT";
+ Name = "PHOT";
+ Colour = PIXPACK(0xFFFFFF);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 1.00f;
+ Collision = -0.99f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = -1;
+
+ Temperature = R_TEMP+900.0f+273.15f;
+ HeatConduct = 251;
+ Description = "Photons. Travel in straight lines.";
+
+ State = ST_GAS;
+ Properties = TYPE_ENERGY|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_PHOT::update;
+ Graphics = &Element_PHOT::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_PHOT static int update(UPDATE_FUNC_ARGS)
+int Element_PHOT::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rt, rx, ry;
+ float rr, rrr;
+ parts[i].pavg[0] = x;
+ parts[i].pavg[1] = y;
+ if (!(parts[i].ctype&0x3FFFFFFF)) {
+ sim->kill_part(i);
+ return 1;
+ }
+ if (parts[i].temp > 506)
+ if (1>rand()%10) Element_FIRE::update(UPDATE_FUNC_SUBCALL_ARGS);
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES && (rx || ry)) {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_ISOZ && 5>(rand()%2000))
+ {
+ parts[i].vx *= 0.90;
+ parts[i].vy *= 0.90;
+ sim->create_part(r>>8, x+rx, y+ry, PT_PHOT);
+ rrr = (rand()%360)*3.14159f/180.0f;
+ rr = (rand()%128+128)/127.0f;
+ parts[r>>8].vx = rr*cosf(rrr);
+ parts[r>>8].vy = rr*sinf(rrr);
+ sim->pv[y/CELL][x/CELL] -= 15.0f * CFDS;
+ }
+ if ((r&0xFF)==PT_ISZS && 5>(rand()%2000))
+ {
+ parts[i].vx *= 0.90;
+ parts[i].vy *= 0.90;
+ sim->create_part(r>>8, x+rx, y+ry, PT_PHOT);
+ rr = (rand()%228+128)/127.0f;
+ rrr = (rand()%360)*3.14159f/180.0f;
+ parts[r>>8].vx = rr*cosf(rrr);
+ parts[r>>8].vy = rr*sinf(rrr);
+ sim->pv[y/CELL][x/CELL] -= 15.0f * CFDS;
+ }
+ }
+ r = pmap[y][x];
+ if((r&0xFF) == PT_QRTZ && r)// && parts[i].ctype==0x3FFFFFFF)
+ {
+ float a = (rand()%360)*3.14159f/180.0f;
+ parts[i].vx = 3.0f*cosf(a);
+ parts[i].vy = 3.0f*sinf(a);
+ if(parts[i].ctype == 0x3FFFFFFF)
+ parts[i].ctype = 0x1F<<(rand()%26);
+ parts[i].life++; //Delay death
+ }
+ //r = pmap[y][x];
+ //rt = r&0xFF;
+ /*if (rt==PT_CLNE || rt==PT_PCLN || rt==PT_BCLN || rt==PT_PBCN) {
+ if (!parts[r>>8].ctype)
+ parts[r>>8].ctype = PT_PHOT;
+ }*/
+
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_PHOT static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_PHOT::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int x = 0;
+ *colr = *colg = *colb = 0;
+ for (x=0; x<12; x++) {
+ *colr += (cpart->ctype >> (x+18)) & 1;
+ *colb += (cpart->ctype >> x) & 1;
+ }
+ for (x=0; x<12; x++)
+ *colg += (cpart->ctype >> (x+9)) & 1;
+ x = 624/(*colr+*colg+*colb+1);
+ *colr *= x;
+ *colg *= x;
+ *colb *= x;
+
+ *firea = 100;
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+
+ *pixel_mode &= ~PMODE_FLAT;
+ *pixel_mode |= FIRE_ADD | PMODE_ADD;
+ return 0;
+}
+
+
+Element_PHOT::~Element_PHOT() {} \ No newline at end of file
diff --git a/src/simulation/elements/PIPE.cpp b/src/simulation/elements/PIPE.cpp
new file mode 100644
index 0000000..3d631a1
--- /dev/null
+++ b/src/simulation/elements/PIPE.cpp
@@ -0,0 +1,516 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PIPE PT_PIPE 99
+Element_PIPE::Element_PIPE()
+{
+ Identifier = "DEFAULT_PT_PIPE";
+ Name = "PIPE";
+ Colour = PIXPACK(0x444444);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = 273.15f;
+ HeatConduct = 0;
+ Description = "Moves elements around, read FAQ on website for help.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 10.0f;
+ HighPressureTransition = PT_BRMT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_PIPE::update;
+ Graphics = &Element_PIPE::graphics;
+}
+
+#define PFLAG_NORMALSPEED 0x00010000
+// parts[].tmp flags
+// trigger flags to be processed this frame (trigger flags for next frame are shifted 3 bits to the left):
+#define PPIP_TMPFLAG_TRIGGER_ON 0x10000000
+#define PPIP_TMPFLAG_TRIGGER_OFF 0x08000000
+#define PPIP_TMPFLAG_TRIGGER_REVERSE 0x04000000
+#define PPIP_TMPFLAG_TRIGGERS 0x1C000000
+// current status of the pipe
+#define PPIP_TMPFLAG_PAUSED 0x02000000
+#define PPIP_TMPFLAG_REVERSED 0x01000000
+// 0x000000FF element
+// 0x00000100 is single pixel pipe
+// 0x00000200 will transfer like a single pixel pipe when in forward mode
+// 0x00001C00 forward single pixel pipe direction
+// 0x00002000 will transfer like a single pixel pipe when in reverse mode
+// 0x0001C000 reverse single pixel pipe direction
+
+signed char pos_1_rx[] = {-1,-1,-1, 0, 0, 1, 1, 1};
+signed char pos_1_ry[] = {-1, 0, 1,-1, 1,-1, 0, 1};
+
+//#TPT-Directive ElementHeader Element_PIPE static int update(UPDATE_FUNC_ARGS)
+int Element_PIPE::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, np;
+ int rnd, rndstore;
+ if (parts[i].tmp & PPIP_TMPFLAG_TRIGGERS)
+ {
+ int pause_changed = 0;
+ if (parts[i].tmp & PPIP_TMPFLAG_TRIGGER_ON) // TRIGGER_ON overrides TRIGGER_OFF
+ {
+ if (parts[i].tmp & PPIP_TMPFLAG_PAUSED)
+ pause_changed = 1;
+ parts[i].tmp &= ~PPIP_TMPFLAG_PAUSED;
+ }
+ else if (parts[i].tmp & PPIP_TMPFLAG_TRIGGER_OFF)
+ {
+ if (!(parts[i].tmp & PPIP_TMPFLAG_PAUSED))
+ pause_changed = 1;
+ parts[i].tmp |= PPIP_TMPFLAG_PAUSED;
+ }
+ if (pause_changed)
+ {
+ int rx, ry, r;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ {
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if ((r&0xFF) == PT_BRCK)
+ {
+ if (parts[i].tmp & PPIP_TMPFLAG_PAUSED)
+ parts[r>>8].tmp = 0;
+ else
+ parts[r>>8].tmp = 1; //make surrounding BRCK glow
+ }
+ }
+ }
+ }
+
+ if (parts[i].tmp & PPIP_TMPFLAG_TRIGGER_REVERSE)
+ {
+ parts[i].tmp ^= PPIP_TMPFLAG_REVERSED;
+ if (parts[i].ctype == 2) //Switch colors so it goes in reverse
+ parts[i].ctype = 4;
+ else if (parts[i].ctype == 4)
+ parts[i].ctype = 2;
+ if (parts[i].tmp & 0x100) //Switch one pixel pipe direction
+ {
+ int coords = (parts[i].tmp>>13)&0xF;
+ int coords2 = (parts[i].tmp>>9)&0xF;
+ parts[i].tmp &= ~0x1FE00;
+ parts[i].tmp |= coords<<9;
+ parts[i].tmp |= coords2<<13;
+ }
+ }
+
+ parts[i].tmp &= ~PPIP_TMPFLAG_TRIGGERS;
+ }
+ if (parts[i].ctype>=2 && parts[i].ctype<=4 && !(parts[i].tmp & PPIP_TMPFLAG_PAUSED))
+ {
+ if (parts[i].life==3)
+ {
+ int lastneighbor = -1;
+ int neighborcount = 0;
+ int count = 0;
+ // make automatic pipe pattern
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_PIPE || (r&0xFF) == PT_PPIP)&&parts[r>>8].ctype==1)
+ {
+ parts[r>>8].ctype = (((parts[i].ctype)%3)+2);//reverse
+ parts[r>>8].life = 6;
+ if ( parts[i].tmp&0x100)//is a single pixel pipe
+ {
+ parts[r>>8].tmp |= 0x200;//will transfer to a single pixel pipe
+ parts[r>>8].tmp |= count<<10;//coords of where it came from
+ parts[i].tmp |= ((7-count)<<14);
+ parts[i].tmp |= 0x2000;
+ }
+ neighborcount ++;
+ lastneighbor = r>>8;
+ }
+ else if (((r&0xFF)==PT_PIPE || (r&0xFF) == PT_PPIP)&&parts[r>>8].ctype!=(((parts[i].ctype-1)%3)+2))
+ {
+ neighborcount ++;
+ lastneighbor = r>>8;
+ }
+ count++;
+ }
+ if(neighborcount == 1)
+ parts[lastneighbor].tmp |= 0x100;
+ }
+ else
+ {
+ if (parts[i].flags&PFLAG_NORMALSPEED)//skip particle push to prevent particle number being higher causing speed up
+ {
+ parts[i].flags &= ~PFLAG_NORMALSPEED;
+ }
+ else
+ {
+ pushParticle(sim, i,0,i);
+ }
+
+ if (nt)//there is something besides PIPE around current particle
+ {
+ rndstore = rand();
+ rnd = rndstore&7;
+ rndstore = rndstore>>3;
+ rx = pos_1_rx[rnd];
+ ry = pos_1_ry[rnd];
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES)
+ {
+ r = pmap[y+ry][x+rx];
+ if(!r)
+ r = sim->photons[y+ry][x+rx];
+ if (surround_space && !r && (parts[i].tmp&0xFF)!=0) //creating at end
+ {
+ np = sim->create_part(-1,x+rx,y+ry,parts[i].tmp&0xFF);
+ if (np!=-1)
+ {
+ transfer_pipe_to_part(parts+i, parts+np);
+ }
+ }
+ //try eating particle at entrance
+ else if ((parts[i].tmp&0xFF) == 0 && (sim->elements[r&0xFF].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS | TYPE_ENERGY)))
+ {
+ if ((r&0xFF)==PT_SOAP)
+ sim->detach(r>>8);
+ transfer_part_to_pipe(parts+(r>>8), parts+i);
+ sim->kill_part(r>>8);
+ }
+ else if ((parts[i].tmp&0xFF) == 0 && (r&0xFF)==PT_STOR && parts[r>>8].tmp && (sim->elements[parts[r>>8].tmp].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS | TYPE_ENERGY)))
+ {
+ // STOR stores properties in the same places as PIPE does
+ transfer_pipe_to_pipe(parts+(r>>8), parts+i);
+ }
+ }
+ }
+ }
+ }
+ else if (!parts[i].ctype && parts[i].life<=10)
+ {
+ if (parts[i].temp<272.15)//manual pipe colors
+ {
+ if (parts[i].temp>173.25&&parts[i].temp<273.15)
+ {
+ parts[i].ctype = 2;
+ parts[i].life = 0;
+ }
+ if (parts[i].temp>73.25&&parts[i].temp<=173.15)
+ {
+ parts[i].ctype = 3;
+ parts[i].life = 0;
+ }
+ if (parts[i].temp>=0&&parts[i].temp<=73.15)
+ {
+ parts[i].ctype = 4;
+ parts[i].life = 0;
+ }
+ }
+ else
+ {
+ // make a border
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ {
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ {
+ int index = sim->create_part(-1,x+rx,y+ry,PT_BRCK);//BRCK border, people didn't like DMND
+ if (parts[i].type == PT_PPIP && index != -1)
+ parts[index].tmp = 1;
+ }
+ }
+ }
+ if (parts[i].life<=1)
+ parts[i].ctype = 1;
+ }
+ }
+ else if (parts[i].ctype==1)//wait for empty space before starting to generate automatic pipe pattern
+ {
+ if (!parts[i].life)
+ {
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ if (!pmap[y+ry][x+rx] && sim->bmap[(y+ry)/CELL][(x+rx)/CELL]!=WL_ALLOWAIR && sim->bmap[(y+ry)/CELL][(x+rx)/CELL]!=WL_WALL && sim->bmap[(y+ry)/CELL][(x+rx)/CELL]!=WL_WALLELEC && (sim->bmap[(y+ry)/CELL][(x+rx)/CELL]!=WL_EWALL || sim->emap[(y+ry)/CELL][(x+rx)/CELL]))
+ parts[i].life=50;
+ }
+ }
+ else if (parts[i].life==5)//check for beginning of pipe single pixel
+ {
+ int issingle = 1;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (((r&0xFF)==PT_PIPE || (r&0xFF) == PT_PPIP) && parts[i].ctype==1 && parts[i].life )
+ issingle = 0;
+ }
+ if (issingle)
+ parts[i].tmp |= 0x100;
+ }
+ else if (parts[i].life==2)
+ {
+ parts[i].ctype = 2;
+ parts[i].life = 6;
+ }
+ }
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_PIPE static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_PIPE::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+
+ if ((cpart->tmp&0xFF)>0 && (cpart->tmp&0xFF)<PT_NUM)
+ {
+ //Create a temp. particle and do a subcall.
+ Particle tpart;
+ int t;
+ memset(&tpart, 0, sizeof(Particle));
+ tpart.type = cpart->tmp&0xFF;
+ tpart.temp = cpart->temp;
+ tpart.life = cpart->tmp2;
+ tpart.tmp = cpart->pavg[0];
+ tpart.ctype = cpart->pavg[1];
+ if (tpart.type == PT_PHOT && tpart.ctype == 0x40000000)
+ tpart.ctype = 0x3FFFFFFF;
+ t = tpart.type;
+ if (ren->graphicscache[t].isready)
+ {
+ *pixel_mode = ren->graphicscache[t].pixel_mode;
+ *cola = ren->graphicscache[t].cola;
+ *colr = ren->graphicscache[t].colr;
+ *colg = ren->graphicscache[t].colg;
+ *colb = ren->graphicscache[t].colb;
+ *firea = ren->graphicscache[t].firea;
+ *firer = ren->graphicscache[t].firer;
+ *fireg = ren->graphicscache[t].fireg;
+ *fireb = ren->graphicscache[t].fireb;
+ }
+ else
+ {
+ *colr = PIXR(ren->sim->elements[t].Colour);
+ *colg = PIXG(ren->sim->elements[t].Colour);
+ *colb = PIXB(ren->sim->elements[t].Colour);
+ if (ren->sim->elements[t].Graphics)
+ {
+ (*(ren->sim->elements[t].Graphics))(ren, &tpart, nx, ny, pixel_mode, cola, colr, colg, colb, firea, firer, fireg, fireb);
+ }
+ else
+ {
+ Element::defaultGraphics(ren, &tpart, nx, ny, pixel_mode, cola, colr, colg, colb, firea, firer, fireg, fireb);
+ }
+ }
+ //*colr = PIXR(elements[cpart->tmp&0xFF].pcolors);
+ //*colg = PIXG(elements[cpart->tmp&0xFF].pcolors);
+ //*colb = PIXB(elements[cpart->tmp&0xFF].pcolors);
+ }
+ else
+ {
+ if (cpart->ctype==2)
+ {
+ *colr = 50;
+ *colg = 1;
+ *colb = 1;
+ }
+ else if (cpart->ctype==3)
+ {
+ *colr = 1;
+ *colg = 50;
+ *colb = 1;
+ }
+ else if (cpart->ctype==4)
+ {
+ *colr = 1;
+ *colg = 1;
+ *colb = 50;
+ }
+ else if (cpart->temp<272.15&&cpart->ctype!=1)
+ {
+ if (cpart->temp>173.25&&cpart->temp<273.15)
+ {
+ *colr = 50;
+ *colg = 1;
+ *colb = 1;
+ }
+ if (cpart->temp>73.25&&cpart->temp<=173.15)
+ {
+ *colr = 1;
+ *colg = 50;
+ *colb = 1;
+ }
+ if (cpart->temp>=0&&cpart->temp<=73.15)
+ {
+ *colr = 1;
+ *colg = 1;
+ *colb = 50;
+ }
+ }
+ }
+ return 0;
+}
+
+//#TPT-Directive ElementHeader Element_PIPE static void transfer_pipe_to_part(Particle *pipe, Particle *part)
+void Element_PIPE::transfer_pipe_to_part(Particle *pipe, Particle *part)
+{
+ part->type = (pipe->tmp & 0xFF);
+ part->temp = pipe->temp;
+ part->life = pipe->tmp2;
+ part->tmp = pipe->pavg[0];
+ part->ctype = pipe->pavg[1];
+ pipe->tmp &= ~0xFF;
+
+ if (part->type != PT_PHOT && part->type != PT_ELEC && part->type != PT_NEUT)
+ {
+ part->vx = 0.0f;
+ part->vy = 0.0f;
+ }
+ else if (part->type == PT_PHOT && part->ctype == 0x40000000)
+ part->ctype = 0x3FFFFFFF;
+ part->tmp2 = 0;
+ part->flags = 0;
+ part->dcolour = 0;
+}
+
+//#TPT-Directive ElementHeader Element_PIPE static void transfer_part_to_pipe(Particle *part, Particle *pipe)
+void Element_PIPE::transfer_part_to_pipe(Particle *part, Particle *pipe)
+{
+ pipe->tmp = (pipe->tmp&~0xFF) | part->type;
+ pipe->temp = part->temp;
+ pipe->tmp2 = part->life;
+ pipe->pavg[0] = part->tmp;
+ pipe->pavg[1] = part->ctype;
+}
+
+//#TPT-Directive ElementHeader Element_PIPE static void transfer_pipe_to_pipe(Particle *src, Particle *dest)
+void Element_PIPE::transfer_pipe_to_pipe(Particle *src, Particle *dest)
+{
+ dest->tmp = (dest->tmp&~0xFF) | (src->tmp&0xFF);
+ dest->temp = src->temp;
+ dest->tmp2 = src->tmp2;
+ dest->pavg[0] = src->pavg[0];
+ dest->pavg[1] = src->pavg[1];
+ src->tmp &= ~0xFF;
+}
+
+//#TPT-Directive ElementHeader Element_PIPE static void pushParticle(Simulation * sim, int i, int count, int original)
+void Element_PIPE::pushParticle(Simulation * sim, int i, int count, int original)
+{
+ int rndstore, rnd, rx, ry, r, x, y, np, q, notctype=(((sim->parts[i].ctype)%3)+2);
+ if ((sim->parts[i].tmp&0xFF) == 0 || count >= 2)//don't push if there is nothing there, max speed of 2 per frame
+ return;
+ x = (int)(sim->parts[i].x+0.5f);
+ y = (int)(sim->parts[i].y+0.5f);
+ if( !(sim->parts[i].tmp&0x200) )
+ {
+ //normal random push
+ rndstore = rand();
+ // RAND_MAX is at least 32767 on all platforms i.e. pow(8,5)-1
+ // so can go 5 cycles without regenerating rndstore
+ for (q=0; q<3; q++)//try to push 3 times
+ {
+ rnd = rndstore&7;
+ rndstore = rndstore>>3;
+ rx = pos_1_rx[rnd];
+ ry = pos_1_ry[rnd];
+ if (x+rx>=0 && y+ry>=0 && x+rx<XRES && y+ry<YRES)
+ {
+ r = sim->pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ else if (((r&0xFF)==PT_PIPE || (r&0xFF) == PT_PPIP) && sim->parts[r>>8].ctype!=notctype && (sim->parts[r>>8].tmp&0xFF)==0)
+ {
+ transfer_pipe_to_pipe(sim->parts+i, sim->parts+(r>>8));
+ if (r>>8 > original)
+ sim->parts[r>>8].flags |= PFLAG_NORMALSPEED;//skip particle push, normalizes speed
+ count++;
+ pushParticle(sim, r>>8,count,original);
+ }
+ else if ((r&0xFF) == PT_PRTI) //Pass particles into PRTI for a pipe speed increase
+ {
+ int nnx;
+ for (nnx=0; nnx<80; nnx++)
+ if (!sim->portalp[sim->parts[r>>8].tmp][count][nnx].type)
+ {
+ transfer_pipe_to_part(sim->parts+i, &(sim->portalp[sim->parts[r>>8].tmp][count][nnx]));
+ count++;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else //predefined 1 pixel thick pipe movement
+ {
+ int coords = 7 - ((sim->parts[i].tmp>>10)&7);
+ r = sim->pmap[y+ pos_1_ry[coords]][x+ pos_1_rx[coords]];
+ if (((r&0xFF)==PT_PIPE || (r&0xFF) == PT_PPIP) && sim->parts[r>>8].ctype!=notctype && (sim->parts[r>>8].tmp&0xFF)==0)
+ {
+ transfer_pipe_to_pipe(sim->parts+i, sim->parts+(r>>8));
+ if (r>>8 > original)
+ sim->parts[r>>8].flags |= PFLAG_NORMALSPEED;//skip particle push, normalizes speed
+ count++;
+ pushParticle(sim, r>>8,count,original);
+ }
+ else if ((r&0xFF) == PT_PRTI) //Pass particles into PRTI for a pipe speed increase
+ {
+ int nnx;
+ for (nnx=0; nnx<80; nnx++)
+ if (!sim->portalp[sim->parts[r>>8].tmp][count][nnx].type)
+ {
+ transfer_pipe_to_part(sim->parts+i, &(sim->portalp[sim->parts[r>>8].tmp][count][nnx]));
+ count++;
+ break;
+ }
+ }
+ else if ((r&0xFF) == PT_NONE) //Move particles out of pipe automatically, much faster at ends
+ {
+ rx = pos_1_rx[coords];
+ ry = pos_1_ry[coords];
+ np = sim->create_part(-1,x+rx,y+ry,sim->parts[i].tmp&0xFF);
+ if (np!=-1)
+ {
+ transfer_pipe_to_part(sim->parts+i, sim->parts+np);
+ }
+ }
+
+ }
+ return;
+}
+
+
+Element_PIPE::~Element_PIPE() {}
diff --git a/src/simulation/elements/PLEX.cpp b/src/simulation/elements/PLEX.cpp
new file mode 100644
index 0000000..c2f157b
--- /dev/null
+++ b/src/simulation/elements/PLEX.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PLEX PT_PLEX 11
+Element_PLEX::Element_PLEX()
+{
+ Identifier = "DEFAULT_PT_PLEX";
+ Name = "C-4";
+ Colour = PIXPACK(0xD080E0);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 1000;
+ Explosive = 2;
+ Meltable = 50;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 88;
+ Description = "Solid. Pressure sensitive explosive.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID | PROP_NEUTPENETRATE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 673.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = NULL;
+
+}
+
+Element_PLEX::~Element_PLEX() {} \ No newline at end of file
diff --git a/src/simulation/elements/PLNT.cpp b/src/simulation/elements/PLNT.cpp
new file mode 100644
index 0000000..6e8202c
--- /dev/null
+++ b/src/simulation/elements/PLNT.cpp
@@ -0,0 +1,126 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PLNT PT_PLNT 20
+Element_PLNT::Element_PLNT()
+{
+ Identifier = "DEFAULT_PT_PLNT";
+ Name = "PLNT";
+ Colour = PIXPACK(0x0CAC00);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 20;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 10;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 65;
+ Description = "Plant, drinks water and grows.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_NEUTPENETRATE|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 573.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = &Element_PLNT::update;
+ Graphics = &Element_PLNT::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_PLNT static int update(UPDATE_FUNC_ARGS)
+int Element_PLNT::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, np;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_WATR && 1>(rand()%250))
+ {
+ np = sim->create_part(r>>8,x+rx,y+ry,PT_PLNT);
+ if (np<0) continue;
+ parts[np].life = 0;
+ }
+ else if ((r&0xFF)==PT_LAVA && 1>(rand()%250))
+ {
+ sim->part_change_type(i,x,y,PT_FIRE);
+ parts[i].life = 4;
+ }
+ else if (((r&0xFF)==PT_SMKE || (r&0xFF)==PT_CO2) && (1>rand()%250))
+ {
+ sim->kill_part(r>>8);
+ parts[i].life = rand()%60 + 60;
+ }
+ else if (surround_space && ((r&0xFF)==PT_WOOD) && (1>rand()%20) && (abs(rx+ry)<=2) && (sim->VINE_MODE || parts[i].tmp==1) )
+ {
+ int nnx = rand()%3 -1;
+ int nny = rand()%3 -1;
+ if (x+rx+nnx>=0 && y+ry+nny>0 && x+rx+nnx<XRES && y+ry+nny<YRES && (nnx || nny))
+ {
+ if (pmap[y+ry+nny][x+rx+nnx])
+ continue;
+ np = sim->create_part(-1,x+rx+nnx,y+ry+nny,PT_VINE);
+ if (np<0) continue;
+ parts[np].temp = parts[i].temp;
+ }
+ }
+ }
+ if (parts[i].life==2)
+ {
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ sim->create_part(-1,x+rx,y+ry,PT_O2);
+ }
+ parts[i].life = 0;
+ }
+ if (parts[i].temp > 350 && parts[i].temp > parts[i].tmp2)
+ parts[i].tmp2 = (int)parts[i].temp;
+ return 0;
+}
+
+//#TPT-Directive ElementHeader Element_PLNT static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_PLNT::graphics(GRAPHICS_FUNC_ARGS)
+{
+ float maxtemp = std::max((float)cpart->tmp2, cpart->temp);
+ if (maxtemp > 300)
+ {
+ *colr += (int)restrict_flt((maxtemp-300)/5,0,58);
+ *colg -= (int)restrict_flt((maxtemp-300)/2,0,102);
+ *colb += (int)restrict_flt((maxtemp-300)/5,0,70);
+ }
+ if (maxtemp < 273)
+ {
+ *colg += (int)restrict_flt((273-maxtemp)/4,0,255);
+ *colb += (int)restrict_flt((273-maxtemp)/1.5,0,255);
+ }
+ return 0;
+}
+
+
+Element_PLNT::~Element_PLNT() {} \ No newline at end of file
diff --git a/src/simulation/elements/PLSM.cpp b/src/simulation/elements/PLSM.cpp
new file mode 100644
index 0000000..4812b84
--- /dev/null
+++ b/src/simulation/elements/PLSM.cpp
@@ -0,0 +1,70 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PLSM PT_PLSM 49
+Element_PLSM::Element_PLSM()
+{
+ Identifier = "DEFAULT_PT_PLSM";
+ Name = "PLSM";
+ Colour = PIXPACK(0xBB99FF);
+ MenuVisible = 1;
+ MenuSection = SC_GAS;
+ Enabled = 1;
+
+ Advection = 0.9f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.97f;
+ Loss = 0.20f;
+ Collision = 0.0f;
+ Gravity = -0.1f;
+ Diffusion = 0.30f;
+ HotAir = 0.001f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 1;
+
+ Temperature = 10000.0f +273.15f;
+ HeatConduct = 5;
+ Description = "Plasma, extremely hot.";
+
+ State = ST_NONE;
+ Properties = TYPE_GAS|PROP_LIFE_DEC|PROP_LIFE_KILL;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_FIRE::update;
+ Graphics = &Element_PLSM::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_PLSM static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_PLSM::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int caddress = restrict_flt(restrict_flt((float)cpart->life, 0.0f, 200.0f)*3, 0.0f, (200.0f*3)-3);
+ *colr = (unsigned char)ren->plasma_data[caddress];
+ *colg = (unsigned char)ren->plasma_data[caddress+1];
+ *colb = (unsigned char)ren->plasma_data[caddress+2];
+
+ *firea = 255;
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+
+ *pixel_mode = PMODE_GLOW | PMODE_ADD; //Clear default, don't draw pixel
+ *pixel_mode |= FIRE_ADD;
+ //Returning 0 means dynamic, do not cache
+ return 0;
+}
+
+
+Element_PLSM::~Element_PLSM() {} \ No newline at end of file
diff --git a/src/simulation/elements/PLUT.cpp b/src/simulation/elements/PLUT.cpp
new file mode 100644
index 0000000..c9b759a
--- /dev/null
+++ b/src/simulation/elements/PLUT.cpp
@@ -0,0 +1,60 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PLUT PT_PLUT 19
+Element_PLUT::Element_PLUT()
+{
+ Identifier = "DEFAULT_PT_PLUT";
+ Name = "PLUT";
+ Colour = PIXPACK(0x407020);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.4f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 90;
+
+ Temperature = R_TEMP+4.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Heavy particles. Fissile. Generates neutrons under pressure.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART|PROP_NEUTPENETRATE|PROP_RADIOACTIVE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_PLUT::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_PLUT static int update(UPDATE_FUNC_ARGS)
+int Element_PLUT::update(UPDATE_FUNC_ARGS)
+ {
+ if (1>rand()%100 && ((int)(5.0f*sim->pv[y/CELL][x/CELL]))>(rand()%1000))
+ {
+ sim->create_part(i, x, y, PT_NEUT);
+ }
+ return 0;
+}
+
+
+Element_PLUT::~Element_PLUT() {} \ No newline at end of file
diff --git a/src/simulation/elements/PPIP.cpp b/src/simulation/elements/PPIP.cpp
new file mode 100644
index 0000000..03830a7
--- /dev/null
+++ b/src/simulation/elements/PPIP.cpp
@@ -0,0 +1,160 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PPIP PT_PPIP 161
+Element_PPIP::Element_PPIP()
+{
+ Identifier = "DEFAULT_PT_PPIP";
+ Name = "PPIP";
+ Colour = PIXPACK(0x444466);
+ MenuVisible = 1;
+ MenuSection = SC_POWERED;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = 273.15f;
+ HeatConduct = 0;
+ Description = "Powered version of PIPE, use PSCN/NSCN to Activate/Deactivate.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_PIPE::update;
+ Graphics = &Element_PIPE::graphics;
+}
+
+#define PFLAG_NORMALSPEED 0x00010000
+// parts[].tmp flags
+// trigger flags to be processed this frame (trigger flags for next frame are shifted 3 bits to the left):
+#define PPIP_TMPFLAG_TRIGGER_ON 0x10000000
+#define PPIP_TMPFLAG_TRIGGER_OFF 0x08000000
+#define PPIP_TMPFLAG_TRIGGER_REVERSE 0x04000000
+#define PPIP_TMPFLAG_TRIGGERS 0x1C000000
+// current status of the pipe
+#define PPIP_TMPFLAG_PAUSED 0x02000000
+#define PPIP_TMPFLAG_REVERSED 0x01000000
+// 0x000000FF element
+// 0x00000100 is single pixel pipe
+// 0x00000200 will transfer like a single pixel pipe when in forward mode
+// 0x00001C00 forward single pixel pipe direction
+// 0x00002000 will transfer like a single pixel pipe when in reverse mode
+// 0x0001C000 reverse single pixel pipe direction
+
+//#TPT-Directive ElementHeader Element_PPIP static int ppip_changed
+int Element_PPIP::ppip_changed = 0;
+
+//#TPT-Directive ElementHeader Element_PPIP static void flood_trigger(Simulation * sim, int x, int y, int sparkedBy)
+void Element_PPIP::flood_trigger(Simulation * sim, int x, int y, int sparkedBy)
+{
+ int coord_stack_limit = XRES*YRES;
+ unsigned short (*coord_stack)[2];
+ int coord_stack_size = 0;
+ int x1, x2;
+
+ Particle * parts = sim->parts;
+ int (*pmap)[XRES] = sim->pmap;
+
+ // Separate flags for on and off in case PPIP is sparked by PSCN and NSCN on the same frame
+ // - then PSCN can override NSCN and behaviour is not dependent on particle order
+ int prop = 0;
+ if (sparkedBy==PT_PSCN) prop = PPIP_TMPFLAG_TRIGGER_ON << 3;
+ else if (sparkedBy==PT_NSCN) prop = PPIP_TMPFLAG_TRIGGER_OFF << 3;
+ else if (sparkedBy==PT_INST) prop = PPIP_TMPFLAG_TRIGGER_REVERSE << 3;
+
+ if (prop==0 || (pmap[y][x]&0xFF)!=PT_PPIP || (parts[pmap[y][x]>>8].tmp & prop))
+ return;
+
+ coord_stack = new unsigned short[coord_stack_limit][2];
+ coord_stack[coord_stack_size][0] = x;
+ coord_stack[coord_stack_size][1] = y;
+ coord_stack_size++;
+
+ do
+ {
+ coord_stack_size--;
+ x = coord_stack[coord_stack_size][0];
+ y = coord_stack[coord_stack_size][1];
+ x1 = x2 = x;
+ // go left as far as possible
+ while (x1>=CELL)
+ {
+ if ((pmap[y][x1-1]&0xFF)!=PT_PPIP)
+ {
+ break;
+ }
+ x1--;
+ }
+ // go right as far as possible
+ while (x2<XRES-CELL)
+ {
+ if ((pmap[y][x2+1]&0xFF)!=PT_PPIP)
+ {
+ break;
+ }
+ x2++;
+ }
+ // fill span
+ for (x=x1; x<=x2; x++)
+ {
+ if (!(parts[pmap[y][x]>>8].tmp & prop))
+ ppip_changed = 1;
+ parts[pmap[y][x]>>8].tmp |= prop;
+ }
+
+ // add adjacent pixels to stack
+ // +-1 to x limits to include diagonally adjacent pixels
+ // Don't need to check x bounds here, because already limited to [CELL, XRES-CELL]
+ if (y>=CELL+1)
+ for (x=x1-1; x<=x2+1; x++)
+ if ((pmap[y-1][x]&0xFF)==PT_PPIP && !(parts[pmap[y-1][x]>>8].tmp & prop))
+ {
+ coord_stack[coord_stack_size][0] = x;
+ coord_stack[coord_stack_size][1] = y-1;
+ coord_stack_size++;
+ if (coord_stack_size>=coord_stack_limit)
+ {
+ delete[] coord_stack;
+ return;
+ }
+ }
+ if (y<YRES-CELL-1)
+ for (x=x1-1; x<=x2+1; x++)
+ if ((pmap[y+1][x]&0xFF)==PT_PPIP && !(parts[pmap[y+1][x]>>8].tmp & prop))
+ {
+ coord_stack[coord_stack_size][0] = x;
+ coord_stack[coord_stack_size][1] = y+1;
+ coord_stack_size++;
+ if (coord_stack_size>=coord_stack_limit)
+ {
+ delete[] coord_stack;
+ return;
+ }
+ }
+ } while (coord_stack_size>0);
+ delete[] coord_stack;
+}
+
+Element_PPIP::~Element_PPIP() {}
diff --git a/src/simulation/elements/PQRT.cpp b/src/simulation/elements/PQRT.cpp
new file mode 100644
index 0000000..8c51947
--- /dev/null
+++ b/src/simulation/elements/PQRT.cpp
@@ -0,0 +1,168 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PQRT PT_PQRT 133
+Element_PQRT::Element_PQRT()
+{
+ Identifier = "DEFAULT_PT_PQRT";
+ Name = "PQRT";
+ Colour = PIXPACK(0x88BBBB);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.27f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 90;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 3;
+ Description = "Broken quartz.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART| PROP_HOT_GLOW;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 2573.15f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = &Element_PQRT::update;
+ Graphics = &Element_PQRT::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_PQRT static int update(UPDATE_FUNC_ARGS)
+int Element_PQRT::update(UPDATE_FUNC_ARGS)
+ {
+ int r, tmp, trade, rx, ry, np, t;
+ t = parts[i].type;
+ if (t == PT_QRTZ)
+ {
+ parts[i].pavg[0] = parts[i].pavg[1];
+ parts[i].pavg[1] = sim->pv[y/CELL][x/CELL];
+ if (parts[i].pavg[1]-parts[i].pavg[0] > 0.05*(parts[i].temp/3) || parts[i].pavg[1]-parts[i].pavg[0] < -0.05*(parts[i].temp/3))
+ {
+ sim->part_change_type(i,x,y,PT_PQRT);
+ }
+ }
+ // absorb SLTW
+ if (parts[i].ctype!=-1)
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ else if ((r&0xFF)==PT_SLTW && (1>rand()%2500))
+ {
+ sim->kill_part(r>>8);
+ parts[i].ctype ++;
+ }
+ }
+ // grow if absorbed SLTW
+ if (parts[i].ctype>0)
+ {
+ for ( trade = 0; trade<5; trade ++)
+ {
+ rx = rand()%3-1;
+ ry = rand()%3-1;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r && parts[i].ctype!=0)
+ {
+ np = sim->create_part(-1,x+rx,y+ry,PT_QRTZ);
+ if (np>-1)
+ {
+ parts[np].tmp = parts[i].tmp;
+ parts[i].ctype--;
+ if (5>rand()%10)
+ {
+ parts[np].ctype=-1;//dead qrtz
+ }
+ else if (!parts[i].ctype && 1>rand()%15)
+ {
+ parts[i].ctype=-1;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ // diffuse absorbed SLTW
+ if (parts[i].ctype>0)
+ {
+ for ( trade = 0; trade<9; trade ++)
+ {
+ rx = rand()%5-2;
+ ry = rand()%5-2;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==t && (parts[i].ctype>parts[r>>8].ctype) && parts[r>>8].ctype>=0 )//diffusion
+ {
+ tmp = parts[i].ctype - parts[r>>8].ctype;
+ if (tmp ==1)
+ {
+ parts[r>>8].ctype ++;
+ parts[i].ctype --;
+ break;
+ }
+ if (tmp>0)
+ {
+ parts[r>>8].ctype += tmp/2;
+ parts[i].ctype -= tmp/2;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_PQRT static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_PQRT::graphics(GRAPHICS_FUNC_ARGS)
+ //QRTZ and PQRT
+{
+ int t = cpart->type, z = cpart->tmp - 5;//speckles!
+ /*if (cpart->temp>(ptransitions[t].thv-800.0f))//hotglow for quartz
+ {
+ float frequency = 3.1415/(2*ptransitions[t].thv-(ptransitions[t].thv-800.0f));
+ int q = (cpart->temp>ptransitions[t].thv)?ptransitions[t].thv-(ptransitions[t].thv-800.0f):cpart->temp-(ptransitions[t].thv-800.0f);
+ *colr += sin(frequency*q) * 226 + (z * 16);
+ *colg += sin(frequency*q*4.55 +3.14) * 34 + (z * 16);
+ *colb += sin(frequency*q*2.22 +3.14) * 64 + (z * 16);
+ }
+ else*/
+ {
+ *colr += z * 16;
+ *colg += z * 16;
+ *colb += z * 16;
+ }
+ return 0;
+}
+
+
+Element_PQRT::~Element_PQRT() {} \ No newline at end of file
diff --git a/src/simulation/elements/PRTI.cpp b/src/simulation/elements/PRTI.cpp
new file mode 100644
index 0000000..8383d90
--- /dev/null
+++ b/src/simulation/elements/PRTI.cpp
@@ -0,0 +1,140 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PRTI PT_PRTI 109
+Element_PRTI::Element_PRTI()
+{
+ Identifier = "DEFAULT_PT_PRTI";
+ Name = "PRTI";
+ Colour = PIXPACK(0xEB5917);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = -0.005f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Portal IN. Things go in here, now with channels (same as WIFI)";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_PRTI::update;
+ Graphics = &Element_PRTI::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_PRTI static int update(UPDATE_FUNC_ARGS)
+int Element_PRTI::update(UPDATE_FUNC_ARGS)
+ {
+ int r, nnx, rx, ry, fe = 0;
+ int count =0;
+ parts[i].tmp = (int)((parts[i].temp-73.15f)/100+1);
+ if (parts[i].tmp>=CHANNELS) parts[i].tmp = CHANNELS-1;
+ else if (parts[i].tmp<0) parts[i].tmp = 0;
+ for (count=0; count<8; count++)
+ {
+ rx = sim->portal_rx[count];
+ ry = sim->portal_ry[count];
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ fe = 1;
+ if (!r || (r&0xFF)==PT_PRTI || (r&0xFF)==PT_PRTO || (!(sim->elements[r&0xFF].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS | TYPE_ENERGY)) && (r&0xFF)!=PT_SPRK))
+ {
+ r = sim->photons[y+ry][x+rx];
+ if (!r || (r&0xFF)==PT_PRTI || (r&0xFF)==PT_PRTO || (!(sim->elements[r&0xFF].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS | TYPE_ENERGY)) && (r&0xFF)!=PT_SPRK))
+ continue;
+ }
+
+ if ((r&0xFF)==PT_STKM || (r&0xFF)==PT_STKM2 || (r&0xFF)==PT_FIGH)
+ continue;// Handling these is a bit more complicated, and is done in STKM_interact()
+
+ if ((r&0xFF) == PT_SOAP)
+ sim->detach(r>>8);
+
+ for ( nnx=0; nnx<80; nnx++)
+ if (!sim->portalp[parts[i].tmp][count][nnx].type)
+ {
+ sim->portalp[parts[i].tmp][count][nnx] = parts[r>>8];
+ if ((r&0xFF)==PT_SPRK)
+ sim->part_change_type(r>>8,x+rx,y+ry,parts[r>>8].ctype);
+ else
+ sim->kill_part(r>>8);
+ fe = 1;
+ break;
+ }
+ }
+ }
+
+
+ if (fe) {
+ int orbd[4] = {0, 0, 0, 0}; //Orbital distances
+ int orbl[4] = {0, 0, 0, 0}; //Orbital locations
+ if (!sim->parts[i].life) parts[i].life = rand()*rand()*rand();
+ if (!sim->parts[i].ctype) parts[i].ctype = rand()*rand()*rand();
+ sim->orbitalparts_get(parts[i].life, parts[i].ctype, orbd, orbl);
+ for (r = 0; r < 4; r++) {
+ if (orbd[r]>1) {
+ orbd[r] -= 12;
+ if (orbd[r]<1) {
+ orbd[r] = (rand()%128)+128;
+ orbl[r] = rand()%255;
+ } else {
+ orbl[r] += 2;
+ orbl[r] = orbl[r]%255;
+ }
+ } else {
+ orbd[r] = (rand()%128)+128;
+ orbl[r] = rand()%255;
+ }
+ }
+ sim->orbitalparts_set(&parts[i].life, &parts[i].ctype, orbd, orbl);
+ } else {
+ parts[i].life = 0;
+ parts[i].ctype = 0;
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_PRTI static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_PRTI::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *firea = 8;
+ *firer = 255;
+ *fireg = 0;
+ *fireb = 0;
+ *pixel_mode |= EFFECT_DBGLINES;
+ *pixel_mode |= EFFECT_GRAVIN;
+ *pixel_mode &= ~PMODE;
+ *pixel_mode |= PMODE_ADD;
+ return 1;
+}
+
+
+Element_PRTI::~Element_PRTI() {}
diff --git a/src/simulation/elements/PRTO.cpp b/src/simulation/elements/PRTO.cpp
new file mode 100644
index 0000000..b4554ec
--- /dev/null
+++ b/src/simulation/elements/PRTO.cpp
@@ -0,0 +1,177 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PRTO PT_PRTO 110
+Element_PRTO::Element_PRTO()
+{
+ Identifier = "DEFAULT_PT_PRTO";
+ Name = "PRTO";
+ Colour = PIXPACK(0x0020EB);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.005f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Portal OUT. Things come out here, now with channels (same as WIFI)";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_PRTO::update;
+ Graphics = &Element_PRTO::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_PRTO static int update(UPDATE_FUNC_ARGS)
+int Element_PRTO::update(UPDATE_FUNC_ARGS)
+ {
+ int r, nnx, rx, ry, np, fe = 0;
+ int count = 0;
+ parts[i].tmp = (int)((parts[i].temp-73.15f)/100+1);
+ if (parts[i].tmp>=CHANNELS) parts[i].tmp = CHANNELS-1;
+ else if (parts[i].tmp<0) parts[i].tmp = 0;
+ for (count=0; count<8; count++)
+ {
+ rx = sim->portal_rx[count];
+ ry = sim->portal_ry[count];
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ fe = 1;
+ if (r)
+ continue;
+ if (!r)
+ {
+ for ( nnx =0 ; nnx<80; nnx++)
+ {
+ int randomness = (count + rand()%3-1 + 4)%8;//add -1,0,or 1 to count
+ if (sim->portalp[parts[i].tmp][randomness][nnx].type==PT_SPRK)// TODO: make it look better, spark creation
+ {
+ sim->create_part(-1,x+1,y,PT_SPRK);
+ sim->create_part(-1,x+1,y+1,PT_SPRK);
+ sim->create_part(-1,x+1,y-1,PT_SPRK);
+ sim->create_part(-1,x,y-1,PT_SPRK);
+ sim->create_part(-1,x,y+1,PT_SPRK);
+ sim->create_part(-1,x-1,y+1,PT_SPRK);
+ sim->create_part(-1,x-1,y,PT_SPRK);
+ sim->create_part(-1,x-1,y-1,PT_SPRK);
+ sim->portalp[parts[i].tmp][randomness][nnx] = sim->emptyparticle;
+ break;
+ }
+ else if (sim->portalp[parts[i].tmp][randomness][nnx].type)
+ {
+ if (sim->portalp[parts[i].tmp][randomness][nnx].type==PT_STKM)
+ sim->player.spwn = 0;
+ if (sim->portalp[parts[i].tmp][randomness][nnx].type==PT_STKM2)
+ sim->player2.spwn = 0;
+ if (sim->portalp[parts[i].tmp][randomness][nnx].type==PT_FIGH)
+ {
+ sim->fighcount--;
+ sim->fighters[(unsigned char)sim->portalp[parts[i].tmp][randomness][nnx].tmp].spwn = 0;
+ }
+ np = sim->create_part(-1, x+rx, y+ry, sim->portalp[parts[i].tmp][randomness][nnx].type);
+ if (np<0)
+ {
+ if (sim->portalp[parts[i].tmp][randomness][nnx].type==PT_STKM)
+ sim->player.spwn = 1;
+ if (sim->portalp[parts[i].tmp][randomness][nnx].type==PT_STKM2)
+ sim->player2.spwn = 1;
+ if (sim->portalp[parts[i].tmp][randomness][nnx].type==PT_FIGH)
+ {
+ sim->fighcount++;
+ sim->fighters[(unsigned char)sim->portalp[parts[i].tmp][randomness][nnx].tmp].spwn = 1;
+ }
+ continue;
+ }
+ if (parts[np].type==PT_FIGH)
+ {
+ // Release the fighters[] element allocated by create_part, the one reserved when the fighter went into the portal will be used
+ sim->fighters[(unsigned char)parts[np].tmp].spwn = 0;
+ sim->fighters[(unsigned char)sim->portalp[parts[i].tmp][randomness][nnx].tmp].spwn = 1;
+ }
+ parts[np] = sim->portalp[parts[i].tmp][randomness][nnx];
+ parts[np].x = x+rx;
+ parts[np].y = y+ry;
+ sim->portalp[parts[i].tmp][randomness][nnx] = sim->emptyparticle;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (fe) {
+ int orbd[4] = {0, 0, 0, 0}; //Orbital distances
+ int orbl[4] = {0, 0, 0, 0}; //Orbital locations
+ if (!sim->parts[i].life) parts[i].life = rand()*rand()*rand();
+ if (!sim->parts[i].ctype) parts[i].ctype = rand()*rand()*rand();
+ sim->orbitalparts_get(parts[i].life, parts[i].ctype, orbd, orbl);
+ for (r = 0; r < 4; r++) {
+ if (orbd[r]<254) {
+ orbd[r] += 16;
+ if (orbd[r]>254) {
+ orbd[r] = 0;
+ orbl[r] = rand()%255;
+ }
+ else
+ {
+ orbl[r] += 1;
+ orbl[r] = orbl[r]%255;
+ }
+ //orbl[r] += 1;
+ //orbl[r] = orbl[r]%255;
+ } else {
+ orbd[r] = 0;
+ orbl[r] = rand()%255;
+ }
+ }
+ sim->orbitalparts_set(&parts[i].life, &parts[i].ctype, orbd, orbl);
+ } else {
+ parts[i].life = 0;
+ parts[i].ctype = 0;
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_PRTO static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_PRTO::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *firea = 8;
+ *firer = 0;
+ *fireg = 0;
+ *fireb = 255;
+ *pixel_mode |= EFFECT_DBGLINES;
+ *pixel_mode |= EFFECT_GRAVOUT;
+ *pixel_mode &= ~PMODE;
+ *pixel_mode |= PMODE_ADD;
+ return 1;
+}
+
+
+Element_PRTO::~Element_PRTO() {} \ No newline at end of file
diff --git a/src/simulation/elements/PSCN.cpp b/src/simulation/elements/PSCN.cpp
new file mode 100644
index 0000000..b074f7b
--- /dev/null
+++ b/src/simulation/elements/PSCN.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PSCN PT_PSCN 35
+Element_PSCN::Element_PSCN()
+{
+ Identifier = "DEFAULT_PT_PSCN";
+ Name = "PSCN";
+ Colour = PIXPACK(0x805050);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "P-Type Silicon, Will transfer current to any conductor.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1687.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = NULL;
+
+}
+
+Element_PSCN::~Element_PSCN() {} \ No newline at end of file
diff --git a/src/simulation/elements/PSTE.cpp b/src/simulation/elements/PSTE.cpp
new file mode 100644
index 0000000..6efea51
--- /dev/null
+++ b/src/simulation/elements/PSTE.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PSTE PT_PSTE 111
+Element_PSTE::Element_PSTE()
+{
+ Identifier = "DEFAULT_PT_PSTE";
+ Name = "PSTE";
+ Colour = PIXPACK(0xAA99AA);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 31;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 29;
+ Description = "Colloid, Hardens under pressure";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 0.5f;
+ HighPressureTransition = PT_PSTS;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 747.0f;
+ HighTemperatureTransition = PT_BRCK;
+
+ Update = NULL;
+
+}
+
+Element_PSTE::~Element_PSTE() {} \ No newline at end of file
diff --git a/src/simulation/elements/PSTS.cpp b/src/simulation/elements/PSTS.cpp
new file mode 100644
index 0000000..5dd331c
--- /dev/null
+++ b/src/simulation/elements/PSTS.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PSTS PT_PSTS 112
+Element_PSTS::Element_PSTS()
+{
+ Identifier = "DEFAULT_PT_PSTS";
+ Name = "PSTS";
+ Colour = PIXPACK(0x776677);
+ MenuVisible = 0;
+ MenuSection = SC_CRACKER;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 100;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 29;
+ Description = "Solid form of PSTE, temporary";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = 0.5f;
+ LowPressureTransition = PT_PSTE;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+
+}
+
+Element_PSTS::~Element_PSTS() {} \ No newline at end of file
diff --git a/src/simulation/elements/PTCT.cpp b/src/simulation/elements/PTCT.cpp
new file mode 100644
index 0000000..bda58c7
--- /dev/null
+++ b/src/simulation/elements/PTCT.cpp
@@ -0,0 +1,58 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PTCT PT_PTCT 46
+Element_PTCT::Element_PTCT()
+{
+ Identifier = "DEFAULT_PT_PTCT";
+ Name = "PTCT";
+ Colour = PIXPACK(0x405050);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Semi-conductor. Only conducts electricity when cold (Less than 100C)";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1414.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = &Element_PTCT::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_PTCT static int update(UPDATE_FUNC_ARGS)
+int Element_PTCT::update(UPDATE_FUNC_ARGS)
+ {
+ if (parts[i].temp>295.0f)
+ parts[i].temp -= 2.5f;
+ return 0;
+}
+
+
+Element_PTCT::~Element_PTCT() {} \ No newline at end of file
diff --git a/src/simulation/elements/PUMP.cpp b/src/simulation/elements/PUMP.cpp
new file mode 100644
index 0000000..2d62799
--- /dev/null
+++ b/src/simulation/elements/PUMP.cpp
@@ -0,0 +1,98 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PUMP PT_PUMP 97
+Element_PUMP::Element_PUMP()
+{
+ Identifier = "DEFAULT_PT_PUMP";
+ Name = "PUMP";
+ Colour = PIXPACK(0x0A0A3B);
+ MenuVisible = 1;
+ MenuSection = SC_POWERED;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 10;
+
+ Weight = 100;
+
+ Temperature = 273.15f;
+ HeatConduct = 0;
+ Description = "Changes pressure to its temp when activated. (use HEAT/COOL).";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_PUMP::update;
+ Graphics = &Element_PUMP::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_PUMP static int update(UPDATE_FUNC_ARGS)
+int Element_PUMP::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ if (parts[i].life>0 && parts[i].life!=10)
+ parts[i].life--;
+ if (parts[i].life==10)
+ {
+ if (parts[i].temp>=256.0+273.15)
+ parts[i].temp=256.0+273.15;
+ if (parts[i].temp<= -256.0+273.15)
+ parts[i].temp = -256.0+273.15;
+
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if ((x+rx)-CELL>=0 && (y+ry)-CELL>0 && (x+rx)+CELL<XRES && (y+ry)+CELL<YRES && !(rx && ry))
+ {
+ sim->pv[(y/CELL)+ry][(x/CELL)+rx] += 0.1f*((parts[i].temp-273.15)-sim->pv[(y/CELL)+ry][(x/CELL)+rx]);
+ }
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_PUMP)
+ {
+ if (parts[r>>8].life<10&&parts[r>>8].life>0)
+ parts[i].life = 9;
+ else if (parts[r>>8].life==0)
+ parts[r>>8].life = 10;
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_PUMP static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_PUMP::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int lifemod = ((cpart->life>10?10:cpart->life)*19);
+ *colb += lifemod;
+ return 0;
+}
+
+
+Element_PUMP::~Element_PUMP() {}
diff --git a/src/simulation/elements/PVOD.cpp b/src/simulation/elements/PVOD.cpp
new file mode 100644
index 0000000..267c7bd
--- /dev/null
+++ b/src/simulation/elements/PVOD.cpp
@@ -0,0 +1,91 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_PVOD PT_PVOD 84
+Element_PVOD::Element_PVOD()
+{
+ Identifier = "DEFAULT_PT_PVOD";
+ Name = "PVOD";
+ Colour = PIXPACK(0x792020);
+ MenuVisible = 1;
+ MenuSection = SC_POWERED;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Solid. When activated, destroys entering particles";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_PVOD::update;
+ Graphics = &Element_PVOD::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_PVOD static int update(UPDATE_FUNC_ARGS)
+int Element_PVOD::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ if (parts[i].life>0 && parts[i].life!=10)
+ parts[i].life--;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_SPRK && parts[r>>8].life>0 && parts[r>>8].life<4)
+ {
+ if (parts[r>>8].ctype==PT_PSCN)
+ parts[i].life = 10;
+ else if (parts[r>>8].ctype==PT_NSCN)
+ parts[i].life = 9;
+ }
+ if ((r&0xFF)==PT_PVOD)
+ {
+ if (parts[i].life==10&&parts[r>>8].life<10&&parts[r>>8].life>0)
+ parts[i].life = 9;
+ else if (parts[i].life==0&&parts[r>>8].life==10)
+ parts[i].life = 10;
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_PVOD static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_PVOD::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ int lifemod = ((cpart->life>10?10:cpart->life)*16);
+ *colr += lifemod;
+ return 0;
+}
+
+
+Element_PVOD::~Element_PVOD() {} \ No newline at end of file
diff --git a/src/simulation/elements/QRTZ.cpp b/src/simulation/elements/QRTZ.cpp
new file mode 100644
index 0000000..20aa97d
--- /dev/null
+++ b/src/simulation/elements/QRTZ.cpp
@@ -0,0 +1,168 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_QRTZ PT_QRTZ 132
+Element_QRTZ::Element_QRTZ()
+{
+ Identifier = "DEFAULT_PT_QRTZ";
+ Name = "QRTZ";
+ Colour = PIXPACK(0xAADDDD);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 3;
+ Description = "Quartz, breakable mineral. Conducts but becomes brittle at lower temperatures.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_HOT_GLOW|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 2573.15f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = &Element_QRTZ::update;
+ Graphics = &Element_QRTZ::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_QRTZ static int update(UPDATE_FUNC_ARGS)
+int Element_QRTZ::update(UPDATE_FUNC_ARGS)
+ {
+ int r, tmp, trade, rx, ry, np, t;
+ t = parts[i].type;
+ if (t == PT_QRTZ)
+ {
+ parts[i].pavg[0] = parts[i].pavg[1];
+ parts[i].pavg[1] = sim->pv[y/CELL][x/CELL];
+ if (parts[i].pavg[1]-parts[i].pavg[0] > 0.05*(parts[i].temp/3) || parts[i].pavg[1]-parts[i].pavg[0] < -0.05*(parts[i].temp/3))
+ {
+ sim->part_change_type(i,x,y,PT_PQRT);
+ }
+ }
+ // absorb SLTW
+ if (parts[i].ctype!=-1)
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ else if ((r&0xFF)==PT_SLTW && (1>rand()%2500))
+ {
+ sim->kill_part(r>>8);
+ parts[i].ctype ++;
+ }
+ }
+ // grow if absorbed SLTW
+ if (parts[i].ctype>0)
+ {
+ for ( trade = 0; trade<5; trade ++)
+ {
+ rx = rand()%3-1;
+ ry = rand()%3-1;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r && parts[i].ctype!=0)
+ {
+ np = sim->create_part(-1,x+rx,y+ry,PT_QRTZ);
+ if (np>-1)
+ {
+ parts[np].tmp = parts[i].tmp;
+ parts[i].ctype--;
+ if (5>rand()%10)
+ {
+ parts[np].ctype=-1;//dead qrtz
+ }
+ else if (!parts[i].ctype && 1>rand()%15)
+ {
+ parts[i].ctype=-1;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ // diffuse absorbed SLTW
+ if (parts[i].ctype>0)
+ {
+ for ( trade = 0; trade<9; trade ++)
+ {
+ rx = rand()%5-2;
+ ry = rand()%5-2;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==t && (parts[i].ctype>parts[r>>8].ctype) && parts[r>>8].ctype>=0 )//diffusion
+ {
+ tmp = parts[i].ctype - parts[r>>8].ctype;
+ if (tmp ==1)
+ {
+ parts[r>>8].ctype ++;
+ parts[i].ctype --;
+ break;
+ }
+ if (tmp>0)
+ {
+ parts[r>>8].ctype += tmp/2;
+ parts[i].ctype -= tmp/2;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_QRTZ static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_QRTZ::graphics(GRAPHICS_FUNC_ARGS)
+ //QRTZ and PQRT
+{
+ int t = cpart->type, z = cpart->tmp - 5;//speckles!
+ /*if (cpart->temp>(ptransitions[t].thv-800.0f))//hotglow for quartz
+ {
+ float frequency = 3.1415/(2*ptransitions[t].thv-(ptransitions[t].thv-800.0f));
+ int q = (cpart->temp>ptransitions[t].thv)?ptransitions[t].thv-(ptransitions[t].thv-800.0f):cpart->temp-(ptransitions[t].thv-800.0f);
+ *colr += sin(frequency*q) * 226 + (z * 16);
+ *colg += sin(frequency*q*4.55 +3.14) * 34 + (z * 16);
+ *colb += sin(frequency*q*2.22 +3.14) * 64 + (z * 16);
+ }
+ else*/
+ {
+ *colr += z * 16;
+ *colg += z * 16;
+ *colb += z * 16;
+ }
+ return 0;
+}
+
+
+Element_QRTZ::~Element_QRTZ() {} \ No newline at end of file
diff --git a/src/simulation/elements/RBDM.cpp b/src/simulation/elements/RBDM.cpp
new file mode 100644
index 0000000..90a332b
--- /dev/null
+++ b/src/simulation/elements/RBDM.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_RBDM PT_RBDM 41
+Element_RBDM::Element_RBDM()
+{
+ Identifier = "DEFAULT_PT_RBDM";
+ Name = "RBDM";
+ Colour = PIXPACK(0xCCCCCC);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 1000;
+ Explosive = 1;
+ Meltable = 50;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 240;
+ Description = "Rubidium, explosive, especially on contact with water, low melting point";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 312.0f;
+ HighTemperatureTransition = PT_LRBD;
+
+ Update = NULL;
+
+}
+
+Element_RBDM::~Element_RBDM() {} \ No newline at end of file
diff --git a/src/simulation/elements/REPL.cpp b/src/simulation/elements/REPL.cpp
new file mode 100644
index 0000000..b2deaee
--- /dev/null
+++ b/src/simulation/elements/REPL.cpp
@@ -0,0 +1,73 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_REPL PT_REPL 160
+Element_REPL::Element_REPL()
+{
+ Identifier = "DEFAULT_PT_REPL";
+ Name = "RPEL";
+ Colour = PIXPACK(0x99CC00);
+ MenuVisible = 1;
+ MenuSection = SC_FORCE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = 20.0f+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Repel or attract particles based on temp value.";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_REPL::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_REPL static int update(UPDATE_FUNC_ARGS)
+int Element_REPL::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, ri;
+ for(ri = 0; ri <= 10; ri++)
+ {
+ rx = (rand()%20)-10;
+ ry = (rand()%20)-10;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ r = sim->photons[y+ry][x+rx];
+
+ if (r && !(sim->elements[r&0xFF].Properties & TYPE_SOLID)){
+ parts[r>>8].vx += isign(rx)*((parts[i].temp-273.15)/10.0f);
+ parts[r>>8].vy += isign(ry)*((parts[i].temp-273.15)/10.0f);
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_REPL::~Element_REPL() {} \ No newline at end of file
diff --git a/src/simulation/elements/RIME.cpp b/src/simulation/elements/RIME.cpp
new file mode 100644
index 0000000..1ff2fad
--- /dev/null
+++ b/src/simulation/elements/RIME.cpp
@@ -0,0 +1,77 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_RIME PT_RIME 91
+Element_RIME::Element_RIME()
+{
+ Identifier = "DEFAULT_PT_RIME";
+ Name = "RIME";
+ Colour = PIXPACK(0xCCCCCC);
+ MenuVisible = 1;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 0.00f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.00f;
+ Loss = 1.00f;
+ Collision = 0.00f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 100;
+
+ Temperature = 243.15f;
+ HeatConduct = 100;
+ Description = "Not quite Ice";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 273.15f;
+ HighTemperatureTransition = PT_WATR;
+
+ Update = &Element_RIME::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_RIME static int update(UPDATE_FUNC_ARGS)
+int Element_RIME::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ parts[i].vx = 0;
+ parts[i].vy = 0;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_SPRK)
+ {
+ sim->part_change_type(i,x,y,PT_FOG);
+ parts[i].life = rand()%50 + 60;
+ }
+ else if ((r&0xFF)==PT_FOG&&parts[r>>8].life>0)
+ {
+ sim->part_change_type(i,x,y,PT_FOG);
+ parts[i].life = parts[r>>8].life;
+ }
+ }
+ return 0;
+}
+
+
+Element_RIME::~Element_RIME() {} \ No newline at end of file
diff --git a/src/simulation/elements/SALT.cpp b/src/simulation/elements/SALT.cpp
new file mode 100644
index 0000000..a2e6274
--- /dev/null
+++ b/src/simulation/elements/SALT.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SALT PT_SALT 26
+Element_SALT::Element_SALT()
+{
+ Identifier = "DEFAULT_PT_SALT";
+ Name = "SALT";
+ Colour = PIXPACK(0xFFFFFF);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.3f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 5;
+ Hardness = 1;
+
+ Weight = 75;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 110;
+ Description = "Salt, dissolves in water.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1173.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = NULL;
+
+}
+
+Element_SALT::~Element_SALT() {} \ No newline at end of file
diff --git a/src/simulation/elements/SAND.cpp b/src/simulation/elements/SAND.cpp
new file mode 100644
index 0000000..a1d9c00
--- /dev/null
+++ b/src/simulation/elements/SAND.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SAND PT_SAND 44
+Element_SAND::Element_SAND()
+{
+ Identifier = "DEFAULT_PT_SAND";
+ Name = "SAND";
+ Colour = PIXPACK(0xFFD090);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.3f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 5;
+ Hardness = 1;
+
+ Weight = 90;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 150;
+ Description = "Sand, Heavy particles. Meltable.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1973.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = NULL;
+
+}
+
+Element_SAND::~Element_SAND() {} \ No newline at end of file
diff --git a/src/simulation/elements/SHLD1.cpp b/src/simulation/elements/SHLD1.cpp
new file mode 100644
index 0000000..605180a
--- /dev/null
+++ b/src/simulation/elements/SHLD1.cpp
@@ -0,0 +1,88 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SHLD1 PT_SHLD1 119
+Element_SHLD1::Element_SHLD1()
+{
+ Identifier = "DEFAULT_PT_SHLD1";
+ Name = "SHLD";
+ Colour = PIXPACK(0xAAAAAA);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Shield, spark it to grow";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 7.0f;
+ HighPressureTransition = PT_NONE;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_SHLD1::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_SHLD1 static int update(UPDATE_FUNC_ARGS)
+int Element_SHLD1::update(UPDATE_FUNC_ARGS)
+ {
+ int r, nnx, nny, rx, ry;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ else if ((r&0xFF)==PT_SPRK&&parts[i].life==0)
+ {
+ if (55>rand()%200&&parts[i].life==0)
+ {
+ sim->part_change_type(i,x,y,PT_SHLD2);
+ parts[i].life = 7;
+ }
+ for ( nnx=-1; nnx<2; nnx++)
+ for ( nny=-1; nny<2; nny++)
+ {
+ if (!pmap[y+ry+nny][x+rx+nnx])
+ {
+ sim->create_part(-1,x+rx+nnx,y+ry+nny,PT_SHLD1);
+ //parts[pmap[y+ny+nny][x+nx+nnx]>>8].life=7;
+ }
+ }
+ }
+ else if ((r&0xFF)==PT_SHLD3&&4>rand()%10)
+ {
+ sim->part_change_type(i,x,y,PT_SHLD2);
+ parts[i].life = 7;
+ }
+ }
+ return 0;
+}
+
+
+
+Element_SHLD1::~Element_SHLD1() {} \ No newline at end of file
diff --git a/src/simulation/elements/SHLD2.cpp b/src/simulation/elements/SHLD2.cpp
new file mode 100644
index 0000000..c00e4c9
--- /dev/null
+++ b/src/simulation/elements/SHLD2.cpp
@@ -0,0 +1,91 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SHLD2 PT_SHLD2 120
+Element_SHLD2::Element_SHLD2()
+{
+ Identifier = "DEFAULT_PT_SHLD2";
+ Name = "SHD2";
+ Colour = PIXPACK(0x777777);
+ MenuVisible = 0;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Shield lvl 2";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 15.0f;
+ HighPressureTransition = PT_NONE;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_SHLD2::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_SHLD2 static int update(UPDATE_FUNC_ARGS)
+int Element_SHLD2::update(UPDATE_FUNC_ARGS)
+ {
+ int r, nnx, nny, rx, ry, np;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r && parts[i].life>0)
+ sim->create_part(-1,x+rx,y+ry,PT_SHLD1);
+ if (!r)
+ continue;
+ else if ((r&0xFF)==PT_SPRK&&parts[i].life==0)
+ {
+ if (25>rand()%200&&parts[i].life==0)
+ {
+ sim->part_change_type(i,x,y,PT_SHLD3);
+ parts[i].life = 7;
+ }
+ for ( nnx=-1; nnx<2; nnx++)
+ for ( nny=-1; nny<2; nny++)
+ {
+ if (!pmap[y+ry+nny][x+rx+nnx])
+ {
+ np = sim->create_part(-1,x+rx+nnx,y+ry+nny,PT_SHLD1);
+ if (np<0) continue;
+ parts[np].life=7;
+ }
+ }
+ }
+ else if ((r&0xFF)==PT_SHLD4&&4>rand()%10)
+ {
+ sim->part_change_type(i,x,y,PT_SHLD3);
+ parts[i].life = 7;
+ }
+ }
+ return 0;
+}
+
+
+
+Element_SHLD2::~Element_SHLD2() {} \ No newline at end of file
diff --git a/src/simulation/elements/SHLD3.cpp b/src/simulation/elements/SHLD3.cpp
new file mode 100644
index 0000000..633a8c4
--- /dev/null
+++ b/src/simulation/elements/SHLD3.cpp
@@ -0,0 +1,101 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SHLD3 PT_SHLD3 121
+Element_SHLD3::Element_SHLD3()
+{
+ Identifier = "DEFAULT_PT_SHLD3";
+ Name = "SHD3";
+ Colour = PIXPACK(0x444444);
+ MenuVisible = 0;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Shield lvl 3";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 25.0f;
+ HighPressureTransition = PT_NONE;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_SHLD3::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_SHLD3 static int update(UPDATE_FUNC_ARGS)
+int Element_SHLD3::update(UPDATE_FUNC_ARGS)
+ {
+ int r, nnx, nny, rx, ry, np;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ {
+ if (1>rand()%2500)
+ {
+ np = sim->create_part(-1,x+rx,y+ry,PT_SHLD1);
+ if (np<0) continue;
+ parts[np].life=7;
+ sim->part_change_type(i,x,y,PT_SHLD2);
+ }
+ else
+ continue;
+
+ }
+ if ((r&0xFF)==PT_SHLD1 && parts[i].life>3)
+ {
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SHLD2);
+ parts[r>>8].life=7;
+ }
+ else if ((r&0xFF)==PT_SPRK&&parts[i].life==0)
+ {
+ if (18>rand()%3000&&parts[i].life==0)
+ {
+ sim->part_change_type(i,x,y,PT_SHLD4);
+ parts[i].life = 7;
+ }
+ for ( nnx=-1; nnx<2; nnx++)
+ for ( nny=-1; nny<2; nny++)
+ {
+
+ if (!pmap[y+ry+nny][x+rx+nnx])
+ {
+ np = sim->create_part(-1,x+rx+nnx,y+ry+nny,PT_SHLD1);
+ if (np<0) continue;
+ parts[np].life=7;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+
+Element_SHLD3::~Element_SHLD3() {} \ No newline at end of file
diff --git a/src/simulation/elements/SHLD4.cpp b/src/simulation/elements/SHLD4.cpp
new file mode 100644
index 0000000..95c8ae4
--- /dev/null
+++ b/src/simulation/elements/SHLD4.cpp
@@ -0,0 +1,92 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SHLD4 PT_SHLD4 122
+Element_SHLD4::Element_SHLD4()
+{
+ Identifier = "DEFAULT_PT_SHLD4";
+ Name = "SHD4";
+ Colour = PIXPACK(0x212121);
+ MenuVisible = 0;
+ MenuSection = SC_CRACKER2;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Shield lvl 4";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 40.0f;
+ HighPressureTransition = PT_NONE;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_SHLD4::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_SHLD4 static int update(UPDATE_FUNC_ARGS)
+int Element_SHLD4::update(UPDATE_FUNC_ARGS)
+ {
+ int r, nnx, nny, rx, ry, np;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ {
+ if (1>rand()%5500)
+ {
+ np = sim->create_part(-1,x+rx,y+ry,PT_SHLD1);
+ if (np<0) continue;
+ parts[np].life=7;
+ sim->part_change_type(i,x,y,PT_SHLD2);
+ }
+ else
+ continue;
+
+ }
+ if ((r&0xFF)==PT_SHLD2 && parts[i].life>3)
+ {
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SHLD3);
+ parts[r>>8].life = 7;
+ }
+ else if ((r&0xFF)==PT_SPRK&&parts[i].life==0)
+ for ( nnx=-1; nnx<2; nnx++)
+ for ( nny=-1; nny<2; nny++)
+ {
+ if (!pmap[y+ry+nny][x+rx+nnx])
+ {
+ np = sim->create_part(-1,x+rx+nnx,y+ry+nny,PT_SHLD1);
+ if (np<0) continue;
+ parts[np].life=7;
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_SHLD4::~Element_SHLD4() {} \ No newline at end of file
diff --git a/src/simulation/elements/SING.cpp b/src/simulation/elements/SING.cpp
new file mode 100644
index 0000000..78641a8
--- /dev/null
+++ b/src/simulation/elements/SING.cpp
@@ -0,0 +1,155 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SING PT_SING 131
+Element_SING::Element_SING()
+{
+ Identifier = "DEFAULT_PT_SING";
+ Name = "SING";
+ Colour = PIXPACK(0x242424);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.36f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.80f;
+ Collision = 0.1f;
+ Gravity = 0.12f;
+ Diffusion = 0.00f;
+ HotAir = -0.001f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 86;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Singularity";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_SING::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_SING static int update(UPDATE_FUNC_ARGS)
+int Element_SING::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, cry, crx, rad, nxi, nxj, nb, j, spawncount;
+ int singularity = -parts[i].life;
+ float angle, v;
+
+ if (sim->pv[y/CELL][x/CELL]<singularity)
+ sim->pv[y/CELL][x/CELL] += 0.1f*(singularity-sim->pv[y/CELL][x/CELL]);
+ if (y+CELL<YRES && sim->pv[y/CELL+1][x/CELL]<singularity)
+ sim->pv[y/CELL+1][x/CELL] += 0.1f*(singularity-sim->pv[y/CELL+1][x/CELL]);
+ if (x+CELL<XRES)
+ {
+ sim->pv[y/CELL][x/CELL+1] += 0.1f*(singularity-sim->pv[y/CELL][x/CELL+1]);
+ if (y+CELL<YRES)
+ sim->pv[y/CELL+1][x/CELL+1] += 0.1f*(singularity-sim->pv[y/CELL+1][x/CELL+1]);
+ }
+ if (y-CELL>=0 && sim->pv[y/CELL-1][x/CELL]<singularity)
+ sim->pv[y/CELL-1][x/CELL] += 0.1f*(singularity-sim->pv[y/CELL-1][x/CELL]);
+ if (x-CELL>=0)
+ {
+ sim->pv[y/CELL][x/CELL-1] += 0.1f*(singularity-sim->pv[y/CELL][x/CELL-1]);
+ if (y-CELL>=0)
+ sim->pv[y/CELL-1][x/CELL-1] += 0.1f*(singularity-sim->pv[y/CELL-1][x/CELL-1]);
+ }
+ if (parts[i].life<1) {
+ //Pop!
+ for (rx=-1; rx<2; rx++) {
+ crx = (x/CELL)+rx;
+ for (ry=-1; ry<2; ry++) {
+ cry = (y/CELL)+ry;
+ if (cry >= 0 && crx >= 0 && crx < (XRES/CELL) && cry < (YRES/CELL)) {
+ sim->pv[cry][crx] += (float)parts[i].tmp;
+ }
+ }
+ }
+ spawncount = (parts[i].tmp>255)?255:parts[i].tmp;
+ if (spawncount>=1)
+ spawncount = spawncount/8;
+ spawncount = spawncount*spawncount*M_PI;
+ for (j=0;j<spawncount;j++)
+ {
+ switch(rand()%3)
+ {
+ case 0:
+ nb = sim->create_part(-3, x, y, PT_PHOT);
+ break;
+ case 1:
+ nb = sim->create_part(-3, x, y, PT_NEUT);
+ break;
+ case 2:
+ nb = sim->create_part(-3, x, y, PT_ELEC);
+ break;
+ }
+ if (nb!=-1) {
+ parts[nb].life = (rand()%300);
+ parts[nb].temp = MAX_TEMP/2;
+ angle = rand()*2.0f*M_PI/RAND_MAX;
+ v = (float)(rand())*5.0f/RAND_MAX;
+ parts[nb].vx = v*cosf(angle);
+ parts[nb].vy = v*sinf(angle);
+ }
+ else if (sim->pfree==-1)
+ break;//if we've run out of particles, stop trying to create them - saves a lot of lag on "sing bomb" saves
+ }
+ sim->kill_part(i);
+ return 1;
+ }
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_DMND&&33>=rand()/(RAND_MAX/100)+1)
+ {
+ if ((r&0xFF)==PT_SING && parts[r>>8].life >10)
+ {
+ if (parts[i].life+parts[r>>8].life > 255)
+ continue;
+ parts[i].life += parts[r>>8].life;
+ }
+ else
+ {
+ if (parts[i].life+3 > 255)
+ {
+ if (parts[r>>8].type!=PT_SING && 1>rand()%100)
+ {
+ int np;
+ np = sim->create_part(r>>8,x+rx,y+ry,PT_SING);
+ parts[np].life = rand()%50+60;
+ }
+ continue;
+ }
+ parts[i].life += 3;
+ parts[i].tmp++;
+ }
+ parts[i].temp = restrict_flt(parts[r>>8].temp+parts[i].temp, MIN_TEMP, MAX_TEMP);
+ sim->kill_part(r>>8);
+ }
+ }
+ return 0;
+}
+
+
+Element_SING::~Element_SING() {}
diff --git a/src/simulation/elements/SLTW.cpp b/src/simulation/elements/SLTW.cpp
new file mode 100644
index 0000000..c422a9f
--- /dev/null
+++ b/src/simulation/elements/SLTW.cpp
@@ -0,0 +1,81 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SLTW PT_SLTW 27
+Element_SLTW::Element_SLTW()
+{
+ Identifier = "DEFAULT_PT_SLTW";
+ Name = "SLTW";
+ Colour = PIXPACK(0x4050F0);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 35;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 75;
+ Description = "Saltwater, conducts electricity, difficult to freeze.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_NEUTPENETRATE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 252.05f;
+ LowTemperatureTransition = PT_ICEI;
+ HighTemperature = 383.0f;
+ HighTemperatureTransition = ST;
+
+ Update = &Element_SLTW::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_SLTW static int update(UPDATE_FUNC_ARGS)
+int Element_SLTW::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_SALT && 1>(rand()%10000))
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SLTW);
+ if ((r&0xFF)==PT_PLNT&&5>(rand()%1000))
+ sim->kill_part(r>>8);
+ if (((r&0xFF)==PT_RBDM||(r&0xFF)==PT_LRBD) && !sim->legacy_enable && parts[i].temp>(273.15f+12.0f) && 1>(rand()%500))
+ {
+ sim->part_change_type(i,x,y,PT_FIRE);
+ parts[i].life = 4;
+ }
+ if ((r&0xFF)==PT_FIRE){
+ sim->kill_part(r>>8);
+ if(1>(rand()%150)){
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_SLTW::~Element_SLTW() {}
diff --git a/src/simulation/elements/SMKE.cpp b/src/simulation/elements/SMKE.cpp
new file mode 100644
index 0000000..a00eca2
--- /dev/null
+++ b/src/simulation/elements/SMKE.cpp
@@ -0,0 +1,68 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SMKE PT_SMKE 57
+Element_SMKE::Element_SMKE()
+{
+ Identifier = "DEFAULT_PT_SMKE";
+ Name = "SMKE";
+ Colour = PIXPACK(0x222222);
+ MenuVisible = 1;
+ MenuSection = SC_GAS;
+ Enabled = 1;
+
+ Advection = 0.9f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.97f;
+ Loss = 0.20f;
+ Collision = 0.0f;
+ Gravity = -0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.001f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 1;
+
+ Temperature = R_TEMP+320.0f+273.15f;
+ HeatConduct = 88;
+ Description = "Smoke";
+
+ State = ST_SOLID;
+ Properties = TYPE_GAS|PROP_LIFE_DEC|PROP_LIFE_KILL_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 625.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = NULL;
+ Graphics = &Element_SMKE::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_SMKE static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_SMKE::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *colr = 55;
+ *colg = 55;
+ *colb = 55;
+
+ *firea = 75;
+ *firer = 55;
+ *fireg = 55;
+ *fireb = 55;
+
+ *pixel_mode = PMODE_NONE; //Clear default, don't draw pixel
+ *pixel_mode |= FIRE_BLEND;
+ //Returning 1 means static, cache as we please
+ return 1;
+}
+
+Element_SMKE::~Element_SMKE() {} \ No newline at end of file
diff --git a/src/simulation/elements/SNOW.cpp b/src/simulation/elements/SNOW.cpp
new file mode 100644
index 0000000..4e592f7
--- /dev/null
+++ b/src/simulation/elements/SNOW.cpp
@@ -0,0 +1,74 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SNOW PT_SNOW 16
+Element_SNOW::Element_SNOW()
+{
+ Identifier = "DEFAULT_PT_SNOW";
+ Name = "SNOW";
+ Colour = PIXPACK(0xC0E0FF);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.90f;
+ Collision = -0.1f;
+ Gravity = 0.05f;
+ Diffusion = 0.01f;
+ HotAir = -0.00005f* CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 50;
+
+ Temperature = R_TEMP-30.0f+273.15f;
+ HeatConduct = 46;
+ Description = "Light particles.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 273.0f;
+ HighTemperatureTransition = ST;
+
+ Update = &Element_SNOW::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_SNOW static int update(UPDATE_FUNC_ARGS)
+int Element_SNOW::update(UPDATE_FUNC_ARGS)
+ { //currently used for snow as well
+ int r, rx, ry;
+ if (parts[i].ctype==PT_FRZW)//get colder if it is from FRZW
+ {
+ parts[i].temp = restrict_flt(parts[i].temp-1.0f, MIN_TEMP, MAX_TEMP);
+ }
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_SALT || (r&0xFF)==PT_SLTW) && 1>(rand()%1000))
+ {
+ sim->part_change_type(i,x,y,PT_SLTW);
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SLTW);
+ }
+ }
+ return 0;
+}
+
+
+Element_SNOW::~Element_SNOW() {}
diff --git a/src/simulation/elements/SOAP.cpp b/src/simulation/elements/SOAP.cpp
new file mode 100644
index 0000000..1d3a55b
--- /dev/null
+++ b/src/simulation/elements/SOAP.cpp
@@ -0,0 +1,291 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SOAP PT_SOAP 149
+Element_SOAP::Element_SOAP()
+{
+ Identifier = "DEFAULT_PT_SOAP";
+ Name = "SOAP";
+ Colour = PIXPACK(0xF5F5DC);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 35;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 29;
+ Description = "Soap. Creates bubbles.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_NEUTPENETRATE|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITL;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_SOAP::update;
+ Graphics = &Element_SOAP::graphics;
+
+}
+
+//#TPT-Directive ElementHeader Element_SOAP static void attach(Particle * parts, int i1, int i2)
+void Element_SOAP::attach(Particle * parts, int i1, int i2)
+{
+ if (!(parts[i2].ctype&4))
+ {
+ parts[i1].ctype |= 2;
+ parts[i1].tmp = i2;
+
+ parts[i2].ctype |= 4;
+ parts[i2].tmp2 = i1;
+ }
+ else if (!(parts[i2].ctype&2))
+ {
+ parts[i1].ctype |= 4;
+ parts[i1].tmp2= i2;
+
+ parts[i2].ctype |= 2;
+ parts[i2].tmp = i1;
+ }
+}
+
+//#TPT-Directive ElementHeader Element_SOAP static int update(UPDATE_FUNC_ARGS)
+int Element_SOAP::update(UPDATE_FUNC_ARGS)
+
+{
+ int r, rx, ry, nr, ng, nb, na;
+ float tr, tg, tb, ta;
+ float blend;
+
+ //0x01 - bubble on/off
+ //0x02 - first mate yes/no
+ //0x04 - "back" mate yes/no
+
+ if (parts[i].ctype&1)
+ {
+ if (parts[i].temp>0)
+ {
+ if (parts[i].life<=0)
+ {
+ if ((parts[i].ctype&6) != 6 && (parts[i].ctype&6))
+ {
+ int target;
+
+ target = i;
+
+ while((parts[target].ctype&6) != 6 && (parts[target].ctype&6))
+ {
+ if (parts[target].ctype&2)
+ {
+ target = parts[target].tmp;
+ sim->detach(target);
+ }
+
+ if (parts[target].ctype&4)
+ {
+ target = parts[target].tmp2;
+ sim->detach(target);
+ }
+ }
+ }
+
+ if ((parts[i].ctype&6) != 6)
+ parts[i].ctype = 0;
+
+ if ((parts[i].ctype&6) == 6 && (parts[parts[i].tmp].ctype&6) == 6 && parts[parts[i].tmp].tmp == i)
+ sim->detach(i);
+ }
+
+ parts[i].vy -= 0.1f;
+
+ parts[i].vy *= 0.5f;
+ parts[i].vx *= 0.5f;
+ }
+
+ if(!(parts[i].ctype&2))
+ {
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+
+ if ((parts[r>>8].type == PT_SOAP) && (parts[r>>8].ctype&1) && !(parts[r>>8].ctype&4))
+ Element_SOAP::attach(parts, i, r>>8);
+ }
+ }
+ else
+ {
+ if (parts[i].life<=0)
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r && !sim->bmap[(y+ry)/CELL][(x+rx)/CELL])
+ continue;
+
+ if (parts[i].temp>0)
+ {
+ if (sim->bmap[(y+ry)/CELL][(x+rx)/CELL]
+ || (r && sim->elements[r&0xFF].State != ST_GAS
+ && (r&0xFF) != PT_SOAP && (r&0xFF) != PT_GLAS))
+ {
+ sim->detach(i);
+ continue;
+ }
+ }
+
+ if ((r&0xFF) == PT_SOAP && parts[r>>8].ctype == 1)
+ {
+ int buf;
+
+ buf = parts[i].tmp;
+
+ parts[i].tmp = r>>8;
+ parts[buf].tmp2 = r>>8;
+ parts[r>>8].tmp2 = i;
+ parts[r>>8].tmp = buf;
+ parts[r>>8].ctype = 7;
+ }
+
+ if ((r&0xFF) == PT_SOAP && parts[r>>8].ctype == 7 && parts[i].tmp != r>>8 && parts[i].tmp2 != r>>8)
+ {
+ int buf;
+
+ parts[parts[i].tmp].tmp2 = parts[r>>8].tmp2;
+ parts[parts[r>>8].tmp2].tmp = parts[i].tmp;
+ parts[r>>8].tmp2 = i;
+ parts[i].tmp = r>>8;
+ }
+ }
+ }
+
+ if(parts[i].ctype&2)
+ {
+ float d, dx, dy;
+
+ dx = parts[i].x - parts[parts[i].tmp].x;
+ dy = parts[i].y - parts[parts[i].tmp].y;
+
+ d = 9/(pow(dx, 2)+pow(dy, 2)+9)-0.5;
+
+ parts[parts[i].tmp].vx -= dx*d;
+ parts[parts[i].tmp].vy -= dy*d;
+
+ parts[i].vx += dx*d;
+ parts[i].vy += dy*d;
+
+ if ((parts[parts[i].tmp].ctype&2) && (parts[parts[i].tmp].ctype&1)
+ && (parts[parts[parts[i].tmp].tmp].ctype&2) && (parts[parts[parts[i].tmp].tmp].ctype&1))
+ {
+ int ii;
+
+ ii = parts[parts[parts[i].tmp].tmp].tmp;
+
+ dx = parts[ii].x - parts[parts[i].tmp].x;
+ dy = parts[ii].y - parts[parts[i].tmp].y;
+
+ d = 81/(pow(dx, 2)+pow(dy, 2)+81)-0.5;
+
+ parts[parts[i].tmp].vx -= dx*d*0.5f;
+ parts[parts[i].tmp].vy -= dy*d*0.5f;
+
+ parts[ii].vx += dx*d*0.5f;
+ parts[ii].vy += dy*d*0.5f;
+ }
+ }
+ }
+ else
+ {
+ if (sim->pv[y/CELL][x/CELL]>0.5f || sim->pv[y/CELL][x/CELL]<(-0.5f))
+ {
+ parts[i].ctype = 1;
+ parts[i].life = 10;
+ }
+
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+
+ if ((r&0xFF) == PT_OIL)
+ {
+ float ax, ay;
+
+ parts[i].vy -= 0.1f;
+
+ parts[i].vy *= 0.5f;
+ parts[i].vx *= 0.5f;
+
+ ax = (parts[i].vx + parts[r>>8].vx)/2;
+ ay = (parts[i].vy + parts[r>>8].vy)/2;
+
+ parts[i].vx = ax;
+ parts[i].vy = ay;
+ parts[r>>8].vx = ax;
+ parts[r>>8].vy = ay;
+ }
+ }
+ }
+
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_SOAP)
+ {
+ blend = 0.85f;
+ tr = (parts[r>>8].dcolour>>16)&0xFF;
+ tg = (parts[r>>8].dcolour>>8)&0xFF;
+ tb = (parts[r>>8].dcolour)&0xFF;
+ ta = (parts[r>>8].dcolour>>24)&0xFF;
+
+ nr = (tr*blend);
+ ng = (tg*blend);
+ nb = (tb*blend);
+ na = (ta*blend);
+
+ parts[r>>8].dcolour = nr<<16 | ng<<8 | nb | na<<24;
+ }
+ }
+
+ return 0;
+}
+
+//#TPT-Directive ElementHeader Element_SOAP static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_SOAP::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *pixel_mode |= EFFECT_LINES;
+ return 1;
+}
+
+Element_SOAP::~Element_SOAP() {}
diff --git a/src/simulation/elements/SPAWN.cpp b/src/simulation/elements/SPAWN.cpp
new file mode 100644
index 0000000..85295f2
--- /dev/null
+++ b/src/simulation/elements/SPAWN.cpp
@@ -0,0 +1,60 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SPAWN PT_SPAWN 118
+Element_SPAWN::Element_SPAWN()
+{
+ Identifier = "DEFAULT_PT_SPAWN";
+ Name = "SPWN";
+ Colour = PIXPACK(0xAAAAAA);
+ MenuVisible = 0;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "STKM spawn point";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_SPAWN::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_SPAWN static int update(UPDATE_FUNC_ARGS)
+int Element_SPAWN::update(UPDATE_FUNC_ARGS)
+ {
+ if (!sim->player.spwn)
+ sim->create_part(-1, x, y, PT_STKM);
+
+ return 0;
+}
+
+
+
+Element_SPAWN::~Element_SPAWN() {} \ No newline at end of file
diff --git a/src/simulation/elements/SPAWN2.cpp b/src/simulation/elements/SPAWN2.cpp
new file mode 100644
index 0000000..3cc048b
--- /dev/null
+++ b/src/simulation/elements/SPAWN2.cpp
@@ -0,0 +1,60 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SPAWN2 PT_SPAWN2 117
+Element_SPAWN2::Element_SPAWN2()
+{
+ Identifier = "DEFAULT_PT_SPAWN2";
+ Name = "SPWN2";
+ Colour = PIXPACK(0xAAAAAA);
+ MenuVisible = 0;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "STK2 spawn point";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_SPAWN2::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_SPAWN2 static int update(UPDATE_FUNC_ARGS)
+int Element_SPAWN2::update(UPDATE_FUNC_ARGS)
+ {
+ if (!sim->player2.spwn)
+ sim->create_part(-1, x, y, PT_STKM2);
+
+ return 0;
+}
+
+
+
+Element_SPAWN2::~Element_SPAWN2() {} \ No newline at end of file
diff --git a/src/simulation/elements/SPNG.cpp b/src/simulation/elements/SPNG.cpp
new file mode 100644
index 0000000..95dd12f
--- /dev/null
+++ b/src/simulation/elements/SPNG.cpp
@@ -0,0 +1,198 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SPNG PT_SPNG 90
+Element_SPNG::Element_SPNG()
+{
+ Identifier = "DEFAULT_PT_SPNG";
+ Name = "SPNG";
+ Colour = PIXPACK(0xFFBE30);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.00f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.00f;
+ Loss = 0.00f;
+ Collision = 0.00f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 20;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "A sponge, absorbs water.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 2730.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = &Element_SPNG::update;
+ Graphics = &Element_SPNG::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_SPNG static int update(UPDATE_FUNC_ARGS)
+int Element_SPNG::update(UPDATE_FUNC_ARGS)
+ {
+ int r, trade, rx, ry, tmp, np;
+ int limit = 50;
+ if (parts[i].life<limit && sim->pv[y/CELL][x/CELL]<=3&&sim->pv[y/CELL][x/CELL]>=-3&&parts[i].temp<=374.0f)
+ {
+ int absorbChanceDenom = parts[i].life*10000/limit + 500;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_WATR || (r&0xFF)==PT_DSTW || (r&0xFF)==PT_FRZW) && parts[i].life<limit && 500>rand()%absorbChanceDenom)
+ {
+ parts[i].life++;
+ sim->kill_part(r>>8);
+ }
+ if ((r&0xFF)==PT_SLTW && parts[i].life<limit && 50>rand()%absorbChanceDenom)
+ {
+ parts[i].life++;
+ if (rand()%4)
+ sim->kill_part(r>>8);
+ else
+ sim->part_change_type(r>>8, x+rx, y+ry, PT_SALT);
+ }
+ if ((r&0xFF)==PT_CBNW && parts[i].life<limit && 100>rand()%absorbChanceDenom)
+ {
+ parts[i].life++;
+ sim->part_change_type(r>>8, x+rx, y+ry, PT_CO2);
+ }
+ if ((r&0xFF)==PT_PSTE && parts[i].life<limit && 20>rand()%absorbChanceDenom)
+ {
+ parts[i].life++;
+ sim->create_part(r>>8, x+rx, y+ry, PT_CLST);
+ }
+ }
+ }
+ else
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if ((!r)&&parts[i].life>=1)//if nothing then create water
+ {
+ np = sim->create_part(-1,x+rx,y+ry,PT_WATR);
+ if (np>-1) parts[i].life--;
+ }
+ }
+ for ( trade = 0; trade<9; trade ++)
+ {
+ rx = rand()%5-2;
+ ry = rand()%5-2;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_SPNG&&(parts[i].life>parts[r>>8].life)&&parts[i].life>0)//diffusion
+ {
+ tmp = parts[i].life - parts[r>>8].life;
+ if (tmp ==1)
+ {
+ parts[r>>8].life ++;
+ parts[i].life --;
+ trade = 9;
+ }
+ else if (tmp>0)
+ {
+ parts[r>>8].life += tmp/2;
+ parts[i].life -= tmp/2;
+ trade = 9;
+ }
+ }
+ }
+ }
+ tmp = 0;
+ if (parts[i].life>0)
+ {
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_FIRE)
+ {
+ tmp++;
+ if (parts[r>>8].life>60)
+ parts[r>>8].life -= parts[r>>8].life/60;
+ else if (parts[r>>8].life>2)
+ parts[r>>8].life--;
+ }
+ }
+ }
+ if (tmp && parts[i].life>3)
+ parts[i].life -= parts[i].life/3;
+ if (tmp>1)
+ tmp = tmp/2;
+ if (tmp || parts[i].temp>=374)
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if ((!r)&&parts[i].life>=1)//if nothing then create steam
+ {
+ np = sim->create_part(-1,x+rx,y+ry,PT_WTRV);
+ if (np>-1)
+ {
+ parts[np].temp = parts[i].temp;
+ tmp--;
+ parts[i].life--;
+ parts[i].temp -= 20.0f;
+ }
+ }
+ }
+ if (tmp>0)
+ {
+ if (parts[i].life>tmp)
+ parts[i].life -= tmp;
+ else
+ parts[i].life = 0;
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_SPNG static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_SPNG::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *colr -= cpart->life*15;
+ *colg -= cpart->life*15;
+ *colb -= cpart->life*15;
+ if (*colr<=50)
+ *colr = 50;
+ if (*colg<=50)
+ *colg = 50;
+ if (*colb<=20)
+ *colb = 20;
+ return 0;
+}
+
+
+Element_SPNG::~Element_SPNG() {}
diff --git a/src/simulation/elements/SPRK.cpp b/src/simulation/elements/SPRK.cpp
new file mode 100644
index 0000000..09638ff
--- /dev/null
+++ b/src/simulation/elements/SPRK.cpp
@@ -0,0 +1,290 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SPRK PT_SPRK 15
+Element_SPRK::Element_SPRK()
+{
+ Identifier = "DEFAULT_PT_SPRK";
+ Name = "SPRK";
+ Colour = PIXPACK(0xFFFF80);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.001f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Electricity. Conducted by metal and water.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_SPRK::update;
+ Graphics = &Element_SPRK::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_SPRK static int update(UPDATE_FUNC_ARGS)
+int Element_SPRK::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, rt, conduct_sprk, nearp, pavg, ct = parts[i].ctype;
+ Element_FIRE::update(UPDATE_FUNC_SUBCALL_ARGS);
+
+ if (parts[i].life<=0)
+ {
+ if (ct==PT_WATR||ct==PT_SLTW||ct==PT_PSCN||ct==PT_NSCN||ct==PT_ETRD||ct==PT_INWR)
+ parts[i].temp = R_TEMP + 273.15f;
+ if (ct<=0 || ct>=PT_NUM || !sim->elements[parts[i].ctype].Enabled)
+ ct = PT_METL;
+ sim->part_change_type(i,x,y,ct);
+ parts[i].ctype = PT_NONE;
+ parts[i].life = 4;
+ if (ct == PT_WATR)
+ parts[i].life = 64;
+ if (ct == PT_SLTW)
+ parts[i].life = 54;
+ if (ct == PT_SWCH)
+ parts[i].life = 14;
+ return 0;
+ }
+ if (ct==PT_SPRK)
+ {
+ sim->kill_part(i);
+ return 1;
+ }
+ else if (ct==PT_NTCT || ct==PT_PTCT)
+ {
+ Element_NTCT::update(UPDATE_FUNC_SUBCALL_ARGS);
+ }
+ else if (ct==PT_ETRD&&parts[i].life==1)
+ {
+ nearp = sim->nearest_part(i, PT_ETRD, -1);
+ if (nearp!=-1 && sim->parts_avg(i, nearp, PT_INSL)!=PT_INSL)
+ {
+ sim->CreateLine(x, y, (int)(parts[nearp].x+0.5f), (int)(parts[nearp].y+0.5f), 0, 0, PT_PLSM, 0);
+ sim->part_change_type(i,x,y,ct);
+ ct = parts[i].ctype = PT_NONE;
+ parts[i].life = 20;
+ sim->part_change_type(nearp,(int)(parts[nearp].x+0.5f),(int)(parts[nearp].y+0.5f),PT_SPRK);
+ parts[nearp].life = 9;
+ parts[nearp].ctype = PT_ETRD;
+ }
+ }
+ else if (ct==PT_NBLE&&parts[i].life<=1&&parts[i].tmp!=1)
+ {
+ parts[i].life = rand()%150+50;
+ sim->part_change_type(i,x,y,PT_PLSM);
+ parts[i].ctype = PT_NBLE;
+ if (parts[i].temp > 5273.15)
+ parts[i].tmp |= 4;
+ parts[i].temp = 3500;
+ sim->pv[y/CELL][x/CELL] += 1;
+ }
+ else if (ct==PT_TESC) // tesla coil code
+ {
+ if (parts[i].tmp>300)
+ parts[i].tmp=300;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (r)
+ continue;
+ if (parts[i].tmp>4 && rand()%(parts[i].tmp*parts[i].tmp/20+6)==0)
+ {
+ int p = sim->create_part(-1, x+rx*2, y+ry*2, PT_LIGH);
+ if (p!=-1)
+ {
+ parts[p].life=rand()%(2+parts[i].tmp/15)+parts[i].tmp/7;
+ if (parts[i].life>60)
+ parts[i].life=60;
+ parts[p].temp=parts[p].life*parts[i].tmp/2.5;
+ parts[p].tmp2=1;
+ parts[p].tmp=atan2(-ry, (float)rx)/M_PI*360;
+ parts[i].temp-=parts[i].tmp*2+parts[i].temp/5; // slight self-cooling
+ if (fabs(sim->pv[y/CELL][x/CELL])!=0.0f)
+ {
+ if (fabs(sim->pv[y/CELL][x/CELL])<=0.5f)
+ sim->pv[y/CELL][x/CELL]=0;
+ else
+ sim->pv[y/CELL][x/CELL]-=(sim->pv[y/CELL][x/CELL]>0)?0.5:-0.5;
+ }
+ }
+ }
+ }
+ }
+ else if (ct==PT_IRON) {
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF) == PT_DSTW && 30>(rand()/(RAND_MAX/1000))) ||
+ ((r&0xFF) == PT_SLTW && 30>(rand()/(RAND_MAX/1000))) ||
+ ((r&0xFF) == PT_WATR && 30>(rand()/(RAND_MAX/1000))))
+ {
+ if (rand()<RAND_MAX/3)
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_O2);
+ else
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_H2);
+ }
+ }
+ }
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ rt = parts[r>>8].type;
+ conduct_sprk = 1;
+
+
+ pavg = sim->parts_avg(r>>8, i,PT_INSL);
+ if ((rt==PT_SWCH||(rt==PT_SPRK&&parts[r>>8].ctype==PT_SWCH)) && pavg!=PT_INSL && parts[i].life<4) // make sparked SWCH turn off correctly
+ {
+ if (rt==PT_SWCH&&ct==PT_PSCN&&parts[r>>8].life<10) {
+ parts[r>>8].life = 10;
+ }
+ if (ct==PT_NSCN) {
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SWCH);
+ parts[r>>8].ctype = PT_NONE;
+ parts[r>>8].life = 9;
+ }
+ }
+ else if ((ct==PT_PSCN||ct==PT_NSCN) && (rt==PT_PUMP||rt==PT_GPMP||rt==PT_HSWC||rt==PT_PBCN) && parts[i].life<4) // PROP_PTOGGLE, Maybe? We seem to use 2 different methods for handling actived elements, this one seems better. Yes, use this one for new elements, PCLN is different for compatibility with existing saves
+ {
+ if (ct==PT_PSCN) parts[r>>8].life = 10;
+ else if (ct==PT_NSCN && parts[r>>8].life>=10) parts[r>>8].life = 9;
+ }
+ else if ((ct==PT_PSCN||ct==PT_NSCN) && (rt==PT_LCRY&&abs(rx)<2&&abs(ry)<2) && parts[i].life<4)
+ {
+ if (ct==PT_PSCN && parts[r>>8].tmp == 0) parts[r>>8].tmp = 2;
+ else if (ct==PT_NSCN && parts[r>>8].tmp == 3) parts[r>>8].tmp = 1;
+ }
+
+ if (rt == PT_PPIP && parts[i].life == 3 && pavg!=PT_INSL)
+ {
+ if (ct == PT_NSCN || ct == PT_PSCN || ct == PT_INST)
+ Element_PPIP::flood_trigger(sim, x+rx, y+ry, ct);
+ }
+
+ // ct = spark from material, rt = spark to material. Make conduct_sprk = 0 if conduction not allowed
+
+ if (pavg == PT_INSL) conduct_sprk = 0;
+ if (!((sim->elements[rt].Properties&PROP_CONDUCTS)||rt==PT_INST||rt==PT_QRTZ)) conduct_sprk = 0;
+ if (abs(rx)+abs(ry)>=4 &&ct!=PT_SWCH&&rt!=PT_SWCH)
+ conduct_sprk = 0;
+
+
+ if (ct==PT_METL && (rt==PT_NTCT||rt==PT_PTCT||rt==PT_INWR||(rt==PT_SPRK&&(parts[r>>8].ctype==PT_NTCT||parts[r>>8].ctype==PT_PTCT))) && pavg!=PT_INSL && parts[i].life<4)
+ {
+ parts[r>>8].temp = 473.0f;
+ if (rt==PT_NTCT||rt==PT_PTCT)
+ conduct_sprk = 0;
+ }
+ if (ct==PT_NTCT && !(rt==PT_PSCN || rt==PT_NTCT || (rt==PT_NSCN&&parts[i].temp>373.0f)))
+ conduct_sprk = 0;
+ if (ct==PT_PTCT && !(rt==PT_PSCN || rt==PT_PTCT || (rt==PT_NSCN&&parts[i].temp<373.0f)))
+ conduct_sprk = 0;
+ if (ct==PT_INWR && !(rt==PT_NSCN || rt==PT_INWR || rt==PT_PSCN))
+ conduct_sprk = 0;
+ if (ct==PT_NSCN && rt==PT_PSCN)
+ conduct_sprk = 0;
+ if (ct==PT_ETRD && !(rt==PT_METL||rt==PT_ETRD||rt==PT_BMTL||rt==PT_BRMT||rt==PT_LRBD||rt==PT_RBDM||rt==PT_PSCN||rt==PT_NSCN))
+ conduct_sprk = 0;
+ if (ct==PT_INST&&rt!=PT_NSCN) conduct_sprk = 0;
+ if (ct==PT_SWCH && (rt==PT_PSCN||rt==PT_NSCN||rt==PT_WATR||rt==PT_SLTW||rt==PT_NTCT||rt==PT_PTCT||rt==PT_INWR))
+ conduct_sprk = 0;
+ if (rt==PT_QRTZ && !((ct==PT_NSCN||ct==PT_METL||ct==PT_PSCN||ct==PT_QRTZ) && (parts[r>>8].temp<173.15||sim->pv[(y+ry)/CELL][(x+rx)/CELL]>8)))
+ conduct_sprk = 0;
+ if (rt==PT_NTCT && !(ct==PT_NSCN || ct==PT_NTCT || (ct==PT_PSCN&&parts[r>>8].temp>373.0f)))
+ conduct_sprk = 0;
+ if (rt==PT_PTCT && !(ct==PT_NSCN || ct==PT_PTCT || (ct==PT_PSCN&&parts[r>>8].temp<373.0f)))
+ conduct_sprk = 0;
+ if (rt==PT_INWR && !(ct==PT_NSCN || ct==PT_INWR || ct==PT_PSCN))
+ conduct_sprk = 0;
+ if (rt==PT_INST&&ct!=PT_PSCN)
+ conduct_sprk = 0;
+ if (rt == PT_NBLE && parts[r>>8].tmp == 1)
+ conduct_sprk = 0;
+
+ if (conduct_sprk) {
+ if (rt==PT_WATR||rt==PT_SLTW) {
+ if (parts[r>>8].life==0 && parts[i].life<3)
+ {
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SPRK);
+ if (rt==PT_WATR) parts[r>>8].life = 6;
+ else parts[r>>8].life = 5;
+ parts[r>>8].ctype = rt;
+ }
+ }
+ else if (rt==PT_INST) {
+ if (parts[r>>8].life==0 && parts[i].life<4)
+ {
+ sim->FloodINST(x+rx,y+ry,PT_SPRK,PT_INST);//spark the wire
+ }
+ }
+ else if (parts[r>>8].life==0 && parts[i].life<4) {
+ parts[r>>8].life = 4;
+ parts[r>>8].ctype = rt;
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SPRK);
+ if (parts[r>>8].temp+10.0f<673.0f&&!sim->legacy_enable&&(rt==PT_METL||rt==PT_BMTL||rt==PT_BRMT||rt==PT_PSCN||rt==PT_NSCN||rt==PT_ETRD||rt==PT_NBLE||rt==PT_IRON))
+ parts[r>>8].temp = parts[r>>8].temp+10.0f;
+ }
+ else if (ct==PT_ETRD && parts[i].life==5)
+ {
+ sim->part_change_type(i,x,y,ct);
+ parts[i].ctype = PT_NONE;
+ parts[i].life = 20;
+ parts[r>>8].life = 4;
+ parts[r>>8].ctype = rt;
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SPRK);
+ }
+ }
+ }
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_SPRK static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_SPRK::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *firea = 80;
+ *firer = 170;
+ *fireg = 200;
+ *fireb = 220;
+ *pixel_mode |= FIRE_ADD;
+ return 1;
+}
+
+
+Element_SPRK::~Element_SPRK() {} \ No newline at end of file
diff --git a/src/simulation/elements/STKM.cpp b/src/simulation/elements/STKM.cpp
new file mode 100644
index 0000000..081720e
--- /dev/null
+++ b/src/simulation/elements/STKM.cpp
@@ -0,0 +1,557 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_STKM PT_STKM 55
+Element_STKM::Element_STKM()
+{
+ Identifier = "DEFAULT_PT_STKM";
+ Name = "STKM";
+ Colour = PIXPACK(0xFFE0A0);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.5f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.2f;
+ Loss = 1.0f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.0f;
+ HotAir = 0.00f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 50;
+
+ Temperature = R_TEMP+14.6f+273.15f;
+ HeatConduct = 0;
+ Description = "Stickman. Don't kill him!";
+
+ State = ST_NONE;
+ Properties = 0;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 620.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = &Element_STKM::update;
+ Graphics = &Element_STKM::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_STKM static int update(UPDATE_FUNC_ARGS)
+int Element_STKM::update(UPDATE_FUNC_ARGS)
+
+{
+ run_stickman(&sim->player, UPDATE_FUNC_SUBCALL_ARGS);
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_STKM static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_STKM::graphics(GRAPHICS_FUNC_ARGS)
+{
+ *colr = *colg = *colb = *cola = 0;
+ *pixel_mode = PSPEC_STICKMAN;
+ return 1;
+}
+
+#define INBOND(x, y) ((x)>=0 && (y)>=0 && (x)<XRES && (y)<YRES)
+
+//#TPT-Directive ElementHeader Element_STKM static int run_stickman(playerst* playerp, UPDATE_FUNC_ARGS)
+int Element_STKM::run_stickman(playerst* playerp, UPDATE_FUNC_ARGS) {
+ int r, rx, ry;
+ int t = parts[i].type;
+ float pp, d;
+ float dt = 0.9;///(FPSB*FPSB); //Delta time in square
+ float gvx, gvy;
+ float gx, gy, dl, dr;
+
+ if ((parts[i].ctype>0 && parts[i].ctype<PT_NUM && sim->elements[parts[i].ctype].Enabled && sim->elements[parts[i].ctype].Falldown>0) || parts[i].ctype==SPC_AIR || parts[i].ctype == PT_NEUT || parts[i].ctype == PT_PHOT || parts[i].ctype == PT_LIGH)
+ playerp->elem = parts[i].ctype;
+ playerp->frames++;
+
+ //Temperature handling
+ if (parts[i].temp<243)
+ parts[i].life -= 1;
+ if ((parts[i].temp<309.6f) && (parts[i].temp>=243))
+ parts[i].temp += 1;
+
+ //Death
+ if (parts[i].life<1 || (sim->pv[y/CELL][x/CELL]>=4.5f && playerp->elem != SPC_AIR) ) //If his HP is less that 0 or there is very big wind...
+ {
+ for (r=-2; r<=1; r++)
+ {
+ sim->create_part(-1, x+r, y-2, playerp->elem);
+ sim->create_part(-1, x+r+1, y+2, playerp->elem);
+ sim->create_part(-1, x-2, y+r+1, playerp->elem);
+ sim->create_part(-1, x+2, y+r, playerp->elem);
+ }
+ sim->kill_part(i); //Kill him
+ return 1;
+ }
+
+ //Follow gravity
+ gvx = gvy = 0.0f;
+ switch (sim->gravityMode)
+ {
+ default:
+ case 0:
+ gvy = 1;
+ break;
+ case 1:
+ gvy = gvx = 0.0f;
+ break;
+ case 2:
+ {
+ float gravd;
+ gravd = 0.01f - hypotf((parts[i].x - XCNTR), (parts[i].y - YCNTR));
+ gvx = ((float)(parts[i].x - XCNTR) / gravd);
+ gvy = ((float)(parts[i].y - YCNTR) / gravd);
+ }
+ break;
+ }
+
+ gvx += sim->gravx[((int)parts[i].y/CELL)*(XRES/CELL)+((int)parts[i].x/CELL)];
+ gvy += sim->gravy[((int)parts[i].y/CELL)*(XRES/CELL)+((int)parts[i].x/CELL)];
+
+ parts[i].vx -= gvx*dt; //Head up!
+ parts[i].vy -= gvy*dt;
+
+ //Verlet integration
+ pp = 2*playerp->legs[0]-playerp->legs[2]+playerp->accs[0]*dt*dt;
+ playerp->legs[2] = playerp->legs[0];
+ playerp->legs[0] = pp;
+ pp = 2*playerp->legs[1]-playerp->legs[3]+playerp->accs[1]*dt*dt;
+ playerp->legs[3] = playerp->legs[1];
+ playerp->legs[1] = pp;
+
+ pp = 2*playerp->legs[4]-playerp->legs[6]+(playerp->accs[2]+gvx)*dt*dt;
+ playerp->legs[6] = playerp->legs[4];
+ playerp->legs[4] = pp;
+ pp = 2*playerp->legs[5]-playerp->legs[7]+(playerp->accs[3]+gvy)*dt*dt;
+ playerp->legs[7] = playerp->legs[5];
+ playerp->legs[5] = pp;
+
+ pp = 2*playerp->legs[8]-playerp->legs[10]+playerp->accs[4]*dt*dt;
+ playerp->legs[10] = playerp->legs[8];
+ playerp->legs[8] = pp;
+ pp = 2*playerp->legs[9]-playerp->legs[11]+playerp->accs[5]*dt*dt;
+ playerp->legs[11] = playerp->legs[9];
+ playerp->legs[9] = pp;
+
+ pp = 2*playerp->legs[12]-playerp->legs[14]+(playerp->accs[6]+gvx)*dt*dt;
+ playerp->legs[14] = playerp->legs[12];
+ playerp->legs[12] = pp;
+ pp = 2*playerp->legs[13]-playerp->legs[15]+(playerp->accs[7]+gvy)*dt*dt;
+ playerp->legs[15] = playerp->legs[13];
+ playerp->legs[13] = pp;
+
+ //Setting accseleration to 0
+ playerp->accs[0] = 0;
+ playerp->accs[1] = 0;
+
+ playerp->accs[2] = 0;
+ playerp->accs[3] = 0;
+
+ playerp->accs[4] = 0;
+ playerp->accs[5] = 0;
+
+ playerp->accs[6] = 0;
+ playerp->accs[7] = 0;
+
+ gx = (playerp->legs[4] + playerp->legs[12])/2 - gvy;
+ gy = (playerp->legs[5] + playerp->legs[13])/2 + gvx;
+ dl = pow(gx - playerp->legs[4], 2) + pow(gy - playerp->legs[5], 2);
+ dr = pow(gx - playerp->legs[12], 2) + pow(gy - playerp->legs[13], 2);
+
+ //Go left
+ if (((int)(playerp->comm)&0x01) == 0x01)
+ {
+ if (dl>dr)
+ {
+ if (!sim->eval_move(t, playerp->legs[4], playerp->legs[5], NULL))
+ {
+ playerp->accs[2] = -3*gvy-3*gvx;
+ playerp->accs[3] = 3*gvx-3*gvy;
+ playerp->accs[0] = -gvy;
+ playerp->accs[1] = gvx;
+ }
+ }
+ else
+ {
+ if (!sim->eval_move(t, playerp->legs[12], playerp->legs[13], NULL))
+ {
+ playerp->accs[6] = -3*gvy-3*gvx;
+ playerp->accs[7] = 3*gvx-3*gvy;
+ playerp->accs[0] = -gvy;
+ playerp->accs[1] = gvx;
+ }
+ }
+ }
+
+ //Go right
+ if (((int)(playerp->comm)&0x02) == 0x02)
+ {
+ if (dl<dr)
+ {
+ if (!sim->eval_move(t, playerp->legs[4], playerp->legs[5], NULL))
+ {
+ playerp->accs[2] = 3*gvy-3*gvx;
+ playerp->accs[3] = -3*gvx-3*gvy;
+ playerp->accs[0] = gvy;
+ playerp->accs[1] = -gvx;
+ }
+ }
+ else
+ {
+ if (!sim->eval_move(t, playerp->legs[12], playerp->legs[13], NULL))
+ {
+ playerp->accs[6] = 3*gvy-3*gvx;
+ playerp->accs[7] = -3*gvx-3*gvy;
+ playerp->accs[0] = gvy;
+ playerp->accs[1] = -gvx;
+ }
+ }
+ }
+
+ //Jump
+ if (((int)(playerp->comm)&0x04) == 0x04 &&
+ (!sim->eval_move(t, playerp->legs[4], playerp->legs[5], NULL) || !sim->eval_move(t, playerp->legs[12], playerp->legs[13], NULL)))
+ {
+ parts[i].vy -= 4*gvy;
+ playerp->accs[3] -= gvy;
+ playerp->accs[7] -= gvy;
+ }
+
+ //Charge detector wall if foot inside
+ if (sim->bmap[(int)(playerp->legs[5]+0.5)/CELL][(int)(playerp->legs[4]+0.5)/CELL]==WL_DETECT)
+ sim->set_emap((int)playerp->legs[4]/CELL, (int)playerp->legs[5]/CELL);
+ if (sim->bmap[(int)(playerp->legs[13]+0.5)/CELL][(int)(playerp->legs[12]+0.5)/CELL]==WL_DETECT)
+ sim->set_emap((int)(playerp->legs[12]+0.5)/CELL, (int)(playerp->legs[13]+0.5)/CELL);
+
+ //Searching for particles near head
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ r = sim->photons[y+ry][x+rx];
+
+ if (!r && !sim->bmap[(y+ry)/CELL][(x+rx)/CELL])
+ continue;
+
+ if (sim->elements[r&0xFF].Falldown!=0 || sim->elements[r&0xFF].State == ST_GAS
+ || sim->elements[r&0xFF].Properties&TYPE_GAS
+ || sim->elements[r&0xFF].Properties&TYPE_LIQUID
+ || (r&0xFF) == PT_NEUT || (r&0xFF) == PT_PHOT)
+ {
+ playerp->elem = r&0xFF; //Current element
+ }
+ if ((r&0xFF)==PT_TESC || (r&0xFF)==PT_LIGH)
+ playerp->elem = PT_LIGH;
+ if ((r&0xFF) == PT_PLNT && parts[i].life<100) //Plant gives him 5 HP
+ {
+ if (parts[i].life<=95)
+ parts[i].life += 5;
+ else
+ parts[i].life = 100;
+ sim->kill_part(r>>8);
+ }
+
+ if ((r&0xFF) == PT_NEUT)
+ {
+ if (parts[i].life<=100) parts[i].life -= (102-parts[i].life)/2;
+ else parts[i].life *= 0.9f;
+ sim->kill_part(r>>8);
+ }
+ if (sim->bmap[(ry+y)/CELL][(rx+x)/CELL]==WL_FAN)
+ playerp->elem = SPC_AIR;
+ if ((r&0xFF)==PT_PRTI)
+ Element_STKM::STKM_interact(sim, playerp, i, rx, ry);
+ if (!parts[i].type)//STKM_interact may kill STKM
+ return 1;
+ }
+
+ //Head position
+ rx = x + 3*((((int)playerp->pcomm)&0x02) == 0x02) - 3*((((int)playerp->pcomm)&0x01) == 0x01);
+ ry = y - 3*(playerp->pcomm == 0);
+
+ //Spawn
+ if (((int)(playerp->comm)&0x08) == 0x08)
+ {
+ ry -= 2*(rand()%2)+1;
+ r = pmap[ry][rx];
+ if (sim->elements[r&0xFF].State == ST_SOLID)
+ {
+ sim->create_part(-1, rx, ry, PT_SPRK);
+ playerp->frames = 0;
+ }
+ else
+ {
+ int np = -1;
+ if (playerp->elem == SPC_AIR)
+ sim->CreateParts(rx + 3*((((int)playerp->pcomm)&0x02) == 0x02) - 3*((((int)playerp->pcomm)&0x01) == 0x01), ry, 4, 4, SPC_AIR, 0);
+ else if (playerp->elem==PT_LIGH && playerp->frames<30)//limit lightning creation rate
+ np = -1;
+ else
+ np = sim->create_part(-1, rx, ry, playerp->elem);
+ if ( (np < NPART) && np>=0)
+ {
+ if (playerp->elem == PT_PHOT)
+ {
+ int random = abs(rand()%3-1)*3;
+ if (random==0)
+ {
+ sim->kill_part(np);
+ }
+ else
+ {
+ parts[np].vy = 0;
+ if (((int)playerp->pcomm)&(0x01|0x02))
+ parts[np].vx = (((((int)playerp->pcomm)&0x02) == 0x02) - (((int)(playerp->pcomm)&0x01) == 0x01))*random;
+ else
+ parts[np].vx = random;
+ }
+ }
+ else if (playerp->elem == PT_LIGH)
+ {
+ float angle;
+ int power = 100;
+ if (gvx!=0 || gvy!=0)
+ angle = atan2(gvx, gvy)*180.0f/M_PI;
+ else
+ angle = rand()%360;
+ if (((int)playerp->comm)&0x01)
+ angle += 180;
+ if (angle>360)
+ angle-=360;
+ if (angle<0)
+ angle+=360;
+ parts[np].tmp = angle;
+ parts[np].life=rand()%(2+power/15)+power/7;
+ parts[np].temp=parts[np].life*power/2.5;
+ parts[np].tmp2=1;
+ }
+ else if (playerp->elem != SPC_AIR)
+ {
+ parts[np].vx -= -gvy*(5*((((int)playerp->pcomm)&0x02) == 0x02) - 5*(((int)(playerp->pcomm)&0x01) == 0x01));
+ parts[np].vy -= gvx*(5*((((int)playerp->pcomm)&0x02) == 0x02) - 5*(((int)(playerp->pcomm)&0x01) == 0x01));
+ parts[i].vx -= (sim->elements[(int)playerp->elem].Weight*parts[np].vx)/1000;
+ }
+ playerp->frames = 0;
+ }
+
+ }
+ }
+
+ //Simulation of joints
+ d = 25/(pow((playerp->legs[0]-playerp->legs[4]), 2) + pow((playerp->legs[1]-playerp->legs[5]), 2)+25) - 0.5; //Fast distance
+ playerp->legs[4] -= (playerp->legs[0]-playerp->legs[4])*d;
+ playerp->legs[5] -= (playerp->legs[1]-playerp->legs[5])*d;
+ playerp->legs[0] += (playerp->legs[0]-playerp->legs[4])*d;
+ playerp->legs[1] += (playerp->legs[1]-playerp->legs[5])*d;
+
+ d = 25/(pow((playerp->legs[8]-playerp->legs[12]), 2) + pow((playerp->legs[9]-playerp->legs[13]), 2)+25) - 0.5;
+ playerp->legs[12] -= (playerp->legs[8]-playerp->legs[12])*d;
+ playerp->legs[13] -= (playerp->legs[9]-playerp->legs[13])*d;
+ playerp->legs[8] += (playerp->legs[8]-playerp->legs[12])*d;
+ playerp->legs[9] += (playerp->legs[9]-playerp->legs[13])*d;
+
+ d = 36/(pow((playerp->legs[0]-parts[i].x), 2) + pow((playerp->legs[1]-parts[i].y), 2)+36) - 0.5;
+ parts[i].vx -= (playerp->legs[0]-parts[i].x)*d;
+ parts[i].vy -= (playerp->legs[1]-parts[i].y)*d;
+ playerp->legs[0] += (playerp->legs[0]-parts[i].x)*d;
+ playerp->legs[1] += (playerp->legs[1]-parts[i].y)*d;
+
+ d = 36/(pow((playerp->legs[8]-parts[i].x), 2) + pow((playerp->legs[9]-parts[i].y), 2)+36) - 0.5;
+ parts[i].vx -= (playerp->legs[8]-parts[i].x)*d;
+ parts[i].vy -= (playerp->legs[9]-parts[i].y)*d;
+ playerp->legs[8] += (playerp->legs[8]-parts[i].x)*d;
+ playerp->legs[9] += (playerp->legs[9]-parts[i].y)*d;
+
+ if (INBOND(playerp->legs[4], playerp->legs[5]) && !sim->eval_move(t, playerp->legs[4], playerp->legs[5], NULL))
+ {
+ playerp->legs[4] = playerp->legs[6];
+ playerp->legs[5] = playerp->legs[7];
+ }
+
+ if (INBOND(playerp->legs[12], playerp->legs[13]) && !sim->eval_move(t, playerp->legs[12], playerp->legs[13], NULL))
+ {
+ playerp->legs[12] = playerp->legs[14];
+ playerp->legs[13] = playerp->legs[15];
+ }
+
+ //This makes stick man "pop" from obstacles
+ if (INBOND(playerp->legs[4], playerp->legs[5]) && !sim->eval_move(t, playerp->legs[4], playerp->legs[5], NULL))
+ {
+ float t;
+ t = playerp->legs[4]; playerp->legs[4] = playerp->legs[6]; playerp->legs[6] = t;
+ t = playerp->legs[5]; playerp->legs[5] = playerp->legs[7]; playerp->legs[7] = t;
+ }
+
+ if (INBOND(playerp->legs[12], playerp->legs[13]) && !sim->eval_move(t, playerp->legs[12], playerp->legs[13], NULL))
+ {
+ float t;
+ t = playerp->legs[12]; playerp->legs[12] = playerp->legs[14]; playerp->legs[14] = t;
+ t = playerp->legs[13]; playerp->legs[13] = playerp->legs[15]; playerp->legs[15] = t;
+ }
+
+ //Keeping legs distance
+ if ((pow((playerp->legs[4] - playerp->legs[12]), 2) + pow((playerp->legs[5]-playerp->legs[13]), 2))<16)
+ {
+ float tvx, tvy;
+ tvx = -gvy;
+ tvy = gvx;
+
+ if (tvx || tvy)
+ {
+ playerp->accs[2] -= 0.2*tvx/hypot(tvx, tvy);
+ playerp->accs[3] -= 0.2*tvy/hypot(tvx, tvy);
+
+ playerp->accs[6] += 0.2*tvx/hypot(tvx, tvy);
+ playerp->accs[7] += 0.2*tvy/hypot(tvx, tvy);
+ }
+ }
+
+ if ((pow((playerp->legs[0] - playerp->legs[8]), 2) + pow((playerp->legs[1]-playerp->legs[9]), 2))<16)
+ {
+ float tvx, tvy;
+ tvx = -gvy;
+ tvy = gvx;
+
+ if (tvx || tvy)
+ {
+ playerp->accs[0] -= 0.2*tvx/hypot(tvx, tvy);
+ playerp->accs[1] -= 0.2*tvy/hypot(tvx, tvy);
+
+ playerp->accs[4] += 0.2*tvx/hypot(tvx, tvy);
+ playerp->accs[5] += 0.2*tvy/hypot(tvx, tvy);
+ }
+ }
+
+ //If legs touch something
+ Element_STKM::STKM_interact(sim, playerp, i, (int)(playerp->legs[4]+0.5), (int)(playerp->legs[5]+0.5));
+ Element_STKM::STKM_interact(sim, playerp, i, (int)(playerp->legs[12]+0.5), (int)(playerp->legs[13]+0.5));
+ Element_STKM::STKM_interact(sim, playerp, i, (int)(playerp->legs[4]+0.5), (int)playerp->legs[5]);
+ Element_STKM::STKM_interact(sim, playerp, i, (int)(playerp->legs[12]+0.5), (int)playerp->legs[13]);
+ if (!parts[i].type)
+ return 1;
+
+ parts[i].ctype = playerp->elem;
+ return 0;
+}
+
+//#TPT-Directive ElementHeader Element_STKM static void STKM_interact(Simulation * sim, playerst* playerp, int i, int x, int y)
+void Element_STKM::STKM_interact(Simulation * sim, playerst* playerp, int i, int x, int y)
+{
+ int r;
+ if (x<0 || y<0 || x>=XRES || y>=YRES || !sim->parts[i].type)
+ return;
+ r = sim->pmap[y][x];
+ if (r)
+ {
+ if ((r&0xFF)==PT_SPRK && playerp->elem!=PT_LIGH) //If on charge
+ {
+ sim->parts[i].life -= (int)(rand()*20/RAND_MAX)+32;
+ }
+
+ if (sim->elements[r&0xFF].HeatConduct && ((playerp->elem!=PT_LIGH && sim->parts[r>>8].temp>=323) || sim->parts[r>>8].temp<=243))
+ {
+ sim->parts[i].life -= 2;
+ playerp->accs[3] -= 1;
+ }
+
+ if (sim->elements[r&0xFF].Properties&PROP_DEADLY)
+ switch (r&0xFF)
+ {
+ case PT_ACID:
+ sim->parts[i].life -= 5;
+ break;
+ default:
+ sim->parts[i].life -= 1;
+ break;
+ }
+
+ if (sim->elements[r&0xFF].Properties&PROP_RADIOACTIVE)
+ sim->parts[i].life -= 1;
+
+ if ((r&0xFF)==PT_PRTI && sim->parts[i].type)
+ {
+ int nnx, count=1;//gives rx=0, ry=1 in update_PRTO
+ sim->parts[r>>8].tmp = (int)((sim->parts[r>>8].temp-73.15f)/100+1);
+ if (sim->parts[r>>8].tmp>=CHANNELS) sim->parts[r>>8].tmp = CHANNELS-1;
+ else if (sim->parts[r>>8].tmp<0) sim->parts[r>>8].tmp = 0;
+ for (nnx=0; nnx<80; nnx++)
+ if (!sim->portalp[sim->parts[r>>8].tmp][count][nnx].type)
+ {
+ sim->portalp[sim->parts[r>>8].tmp][count][nnx] = sim->parts[i];
+ sim->kill_part(i);
+ //stop new STKM/fighters being created to replace the ones in the portal:
+ playerp->spwn = 1;
+ if (sim->portalp[sim->parts[r>>8].tmp][count][nnx].type==PT_FIGH)
+ sim->fighcount++;
+ break;
+ }
+ }
+ if (((r&0xFF)==PT_BHOL || (r&0xFF)==PT_NBHL) && sim->parts[i].type)
+ {
+ if (!sim->legacy_enable)
+ {
+ sim->parts[r>>8].temp = restrict_flt(sim->parts[r>>8].temp+sim->parts[i].temp/2, MIN_TEMP, MAX_TEMP);
+ }
+ sim->kill_part(i);
+ }
+ if (((r&0xFF)==PT_VOID || ((r&0xFF)==PT_PVOD && sim->parts[r>>8].life==10)) && (!sim->parts[r>>8].ctype || (sim->parts[r>>8].ctype==sim->parts[i].type)!=(sim->parts[r>>8].tmp&1)) && sim->parts[i].type)
+ {
+ sim->kill_part(i);
+ }
+ }
+}
+
+//#TPT-Directive ElementHeader Element_STKM static void STKM_init_legs(Simulation * sim, playerst* playerp, int i)
+void Element_STKM::STKM_init_legs(Simulation * sim, playerst* playerp, int i)
+{
+ int x, y;
+
+ x = (int)(sim->parts[i].x+0.5f);
+ y = (int)(sim->parts[i].y+0.5f);
+
+ playerp->legs[0] = x-1;
+ playerp->legs[1] = y+6;
+ playerp->legs[2] = x-1;
+ playerp->legs[3] = y+6;
+
+ playerp->legs[4] = x-3;
+ playerp->legs[5] = y+12;
+ playerp->legs[6] = x-3;
+ playerp->legs[7] = y+12;
+
+ playerp->legs[8] = x+1;
+ playerp->legs[9] = y+6;
+ playerp->legs[10] = x+1;
+ playerp->legs[11] = y+6;
+
+ playerp->legs[12] = x+3;
+ playerp->legs[13] = y+12;
+ playerp->legs[14] = x+3;
+ playerp->legs[15] = y+12;
+
+ for (int i = 0; i < 8; i++)
+ playerp->accs[i] = 0;
+ playerp->comm = 0;
+ playerp->pcomm = 0;
+ playerp->frames = 0;
+}
+
+
+Element_STKM::~Element_STKM() {}
diff --git a/src/simulation/elements/STKM2.cpp b/src/simulation/elements/STKM2.cpp
new file mode 100644
index 0000000..a2c0073
--- /dev/null
+++ b/src/simulation/elements/STKM2.cpp
@@ -0,0 +1,56 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_STKM2 PT_STKM2 128
+Element_STKM2::Element_STKM2()
+{
+ Identifier = "DEFAULT_PT_STKM2";
+ Name = "STK2";
+ Colour = PIXPACK(0x6464FF);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.5f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.2f;
+ Loss = 1.0f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.0f;
+ HotAir = 0.00f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 50;
+
+ Temperature = R_TEMP+14.6f+273.15f;
+ HeatConduct = 0;
+ Description = "Stickman. Don't kill him!";
+
+ State = ST_NONE;
+ Properties = 0;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 620.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = &Element_STKM2::update;
+ Graphics = &Element_STKM::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_STKM2 static int update(UPDATE_FUNC_ARGS)
+int Element_STKM2::update(UPDATE_FUNC_ARGS)
+ {
+ Element_STKM::run_stickman(&sim->player2, UPDATE_FUNC_SUBCALL_ARGS);
+ return 0;
+}
+
+Element_STKM2::~Element_STKM2() {} \ No newline at end of file
diff --git a/src/simulation/elements/STNE.cpp b/src/simulation/elements/STNE.cpp
new file mode 100644
index 0000000..ff5251d
--- /dev/null
+++ b/src/simulation/elements/STNE.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_STNE PT_STNE 5
+Element_STNE::Element_STNE()
+{
+ Identifier = "DEFAULT_PT_STNE";
+ Name = "STNE";
+ Colour = PIXPACK(0xA0A0A0);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.3f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 5;
+ Hardness = 1;
+
+ Weight = 90;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 150;
+ Description = "Heavy particles. Meltable.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 983.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = NULL;
+
+}
+
+Element_STNE::~Element_STNE() {} \ No newline at end of file
diff --git a/src/simulation/elements/STOR.cpp b/src/simulation/elements/STOR.cpp
new file mode 100644
index 0000000..c6064cd
--- /dev/null
+++ b/src/simulation/elements/STOR.cpp
@@ -0,0 +1,112 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_STOR PT_STOR 83
+Element_STOR::Element_STOR()
+{
+ Identifier = "DEFAULT_PT_STOR";
+ Name = "STOR";
+ Colour = PIXPACK(0x50DFDF);
+ MenuVisible = 1;
+ MenuSection = SC_POWERED;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Solid. Stores a single particle, releases when charged with PSCN, also passes to PIPE";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_STOR::update;
+ Graphics = &Element_STOR::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_STOR static int update(UPDATE_FUNC_ARGS)
+int Element_STOR::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, np, rx1, ry1;
+ if(parts[i].life && !parts[i].tmp)
+ parts[i].life--;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if ((r>>8)>=NPART || !r)
+ continue;
+ if (!parts[i].tmp && !parts[i].life && (r&0xFF)!=PT_STOR && !(sim->elements[(r&0xFF)].Properties&TYPE_SOLID) && (!parts[i].ctype || (r&0xFF)==parts[i].ctype))
+ {
+ parts[i].tmp = parts[r>>8].type;
+ parts[i].temp = parts[r>>8].temp;
+ parts[i].tmp2 = parts[r>>8].life;
+ parts[i].pavg[0] = parts[r>>8].tmp;
+ parts[i].pavg[1] = parts[r>>8].ctype;
+ sim->kill_part(r>>8);
+ }
+ if(parts[i].tmp && (r&0xFF)==PT_SPRK && parts[r>>8].ctype==PT_PSCN && parts[r>>8].life>0 && parts[r>>8].life<4)
+ {
+ for(ry1 = 1; ry1 >= -1; ry1--){
+ for(rx1 = 0; rx1 >= -1 && rx1 <= 1; rx1 = -rx1-rx1+1){ // Oscilate the X starting at 0, 1, -1, 3, -5, etc (Though stop at -1)
+ np = sim->create_part(-1,x+rx1,y+ry1,parts[i].tmp);
+ if (np!=-1)
+ {
+ parts[np].temp = parts[i].temp;
+ parts[np].life = parts[i].tmp2;
+ parts[np].tmp = parts[i].pavg[0];
+ parts[np].ctype = parts[i].pavg[1];
+ parts[i].tmp = 0;
+ parts[i].life = 10;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_STOR static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_STOR::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ if(cpart->tmp){
+ *pixel_mode |= PMODE_GLOW;
+ *colr = 0x50;
+ *colg = 0xDF;
+ *colb = 0xDF;
+ } else {
+ *colr = 0x20;
+ *colg = 0xAF;
+ *colb = 0xAF;
+ }
+ return 0;
+}
+
+
+Element_STOR::~Element_STOR() {}
diff --git a/src/simulation/elements/SWCH.cpp b/src/simulation/elements/SWCH.cpp
new file mode 100644
index 0000000..818af27
--- /dev/null
+++ b/src/simulation/elements/SWCH.cpp
@@ -0,0 +1,113 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_SWCH PT_SWCH 56
+Element_SWCH::Element_SWCH()
+{
+ Identifier = "DEFAULT_PT_SWCH";
+ Name = "SWCH";
+ Colour = PIXPACK(0x103B11);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Solid. Only conducts when switched on. (PSCN switches on, NSCN switches off)";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_SWCH::update;
+ Graphics = &Element_SWCH::graphics;
+}
+
+bool isRedBRAY(UPDATE_FUNC_ARGS, int xc, int yc)
+{
+ return (pmap[yc][xc]&0xFF) == PT_BRAY && parts[pmap[yc][xc]>>8].tmp == 2;
+}
+
+//#TPT-Directive ElementHeader Element_SWCH static int update(UPDATE_FUNC_ARGS)
+int Element_SWCH::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rt, rx, ry;
+ if (parts[i].life>0 && parts[i].life!=10)
+ parts[i].life--;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (sim->parts_avg(i,r>>8,PT_INSL)!=PT_INSL) {
+ rt = r&0xFF;
+ if (rt==PT_SWCH)
+ {
+ if (parts[i].life>=10&&parts[r>>8].life<10&&parts[r>>8].life>0)
+ parts[i].life = 9;
+ else if (parts[i].life==0&&parts[r>>8].life>=10)
+ {
+ //Set to other particle's life instead of 10, otherwise spark loops form when SWCH is sparked while turning on
+ parts[i].life = parts[r>>8].life;
+ }
+ }
+ else if (rt==PT_SPRK&&parts[i].life==10&&parts[r>>8].ctype!=PT_PSCN&&parts[r>>8].ctype!=PT_NSCN) {
+ sim->part_change_type(i,x,y,PT_SPRK);
+ parts[i].ctype = PT_SWCH;
+ parts[i].life = 4;
+ }
+ }
+ }
+ //turn SWCH on/off from two red BRAYS. There must be one either above or below, and one either left or right to work, and it can't come from the side, it must be a diagonal beam
+ if (!(pmap[y-1][x-1]&0xFF) && !(pmap[y-1][x+1]&0xFF) && (isRedBRAY(UPDATE_FUNC_SUBCALL_ARGS, x, y-1) || isRedBRAY(UPDATE_FUNC_SUBCALL_ARGS, x, y+1)) && (isRedBRAY(UPDATE_FUNC_SUBCALL_ARGS, x+1, y) || isRedBRAY(UPDATE_FUNC_SUBCALL_ARGS, x-1, y)))
+ {
+ if (parts[i].life == 10)
+ parts[i].life = 9;
+ else if (parts[i].life <= 5)
+ parts[i].life = 14;
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_SWCH static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_SWCH::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ if(cpart->life >= 10)
+ {
+ *colr = 17;
+ *colg = 217;
+ *colb = 24;
+ *pixel_mode |= PMODE_GLOW;
+ }
+ return 0;
+}
+
+
+Element_SWCH::~Element_SWCH() {} \ No newline at end of file
diff --git a/src/simulation/elements/TESC.cpp b/src/simulation/elements/TESC.cpp
new file mode 100644
index 0000000..d8b4e9f
--- /dev/null
+++ b/src/simulation/elements/TESC.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_TESC PT_TESC 88
+Element_TESC::Element_TESC()
+{
+ Identifier = "DEFAULT_PT_TESC";
+ Name = "TESC";
+ Colour = PIXPACK(0x707040);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Tesla coil!";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_HOT_GLOW;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+
+}
+
+Element_TESC::~Element_TESC() {} \ No newline at end of file
diff --git a/src/simulation/elements/THDR.cpp b/src/simulation/elements/THDR.cpp
new file mode 100644
index 0000000..6ee1dea
--- /dev/null
+++ b/src/simulation/elements/THDR.cpp
@@ -0,0 +1,102 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_THDR PT_THDR 48
+Element_THDR::Element_THDR()
+{
+ Identifier = "DEFAULT_PT_THDR";
+ Name = "THDR";
+ Colour = PIXPACK(0xFFFFA0);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.0f;
+ Loss = 0.30f;
+ Collision = -0.99f;
+ Gravity = 0.6f;
+ Diffusion = 0.62f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 1;
+
+ Temperature = 9000.0f +273.15f;
+ HeatConduct = 1;
+ Description = "Lightning! Very hot, inflicts damage upon most materials, transfers current to metals.";
+
+ State = ST_NONE;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_THDR::update;
+ Graphics = &Element_THDR::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_THDR static int update(UPDATE_FUNC_ARGS)
+int Element_THDR::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((sim->elements[r&0xFF].Properties&PROP_CONDUCTS) && parts[r>>8].life==0 && !((r&0xFF)==PT_WATR||(r&0xFF)==PT_SLTW) && parts[r>>8].ctype!=PT_SPRK)
+ {
+ parts[i].type = PT_NONE;
+ parts[r>>8].ctype = parts[r>>8].type;
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SPRK);
+ parts[r>>8].life = 4;
+ }
+ else if ((r&0xFF)!=PT_CLNE&&(r&0xFF)!=PT_THDR&&(r&0xFF)!=PT_SPRK&&(r&0xFF)!=PT_DMND&&(r&0xFF)!=PT_FIRE&&(r&0xFF)!=PT_NEUT&&(r&0xFF)!=PT_PHOT&&(r&0xFF))
+ {
+ sim->pv[y/CELL][x/CELL] += 100.0f;
+ if (sim->legacy_enable&&1>(rand()%200))
+ {
+ parts[i].life = rand()%50+120;
+ sim->part_change_type(i,x,y,PT_FIRE);
+ }
+ else
+ {
+ parts[i].type = PT_NONE;
+ }
+ }
+ }
+ if (parts[i].type==PT_NONE) {
+ sim->kill_part(i);
+ return 1;
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_THDR static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_THDR::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ *firea = 160;
+ *fireg = 192;
+ *fireb = 255;
+ *firer = 144;
+ *pixel_mode |= FIRE_ADD;
+ return 1;
+}
+
+
+Element_THDR::~Element_THDR() {}
diff --git a/src/simulation/elements/THRM.cpp b/src/simulation/elements/THRM.cpp
new file mode 100644
index 0000000..e7d1dde
--- /dev/null
+++ b/src/simulation/elements/THRM.cpp
@@ -0,0 +1,80 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_THRM PT_THRM 65
+Element_THRM::Element_THRM()
+{
+ Identifier = "DEFAULT_PT_THRM";
+ Name = "THRM";
+ Colour = PIXPACK(0xA08090);
+ MenuVisible = 1;
+ MenuSection = SC_EXPLOSIVE;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.04f * CFDS;
+ AirLoss = 0.94f;
+ Loss = 0.95f;
+ Collision = -0.1f;
+ Gravity = 0.3f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 2;
+ Hardness = 2;
+
+ Weight = 90;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 211;
+ Description = "Thermite. Burns at extremely high temperature.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_THRM::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_THRM static int update(UPDATE_FUNC_ARGS)
+int Element_THRM::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_FIRE || (r&0xFF)==PT_PLSM || (r&0xFF)==PT_LAVA)) // TODO: could this go in update_PYRO?
+ {
+ if (1>(rand()%500)) {
+ sim->part_change_type(i,x,y,PT_LAVA);
+ parts[i].ctype = PT_BMTL;
+ parts[i].temp = 3500.0f;
+ sim->pv[y/CELL][x/CELL] += 50.0f;
+ } else {
+ sim->part_change_type(i,x,y,PT_LAVA);
+ parts[i].life = 400;
+ parts[i].ctype = PT_THRM;
+ parts[i].temp = 3500.0f;
+ parts[i].tmp = 20;
+ }
+ }
+ }
+ return 0;
+}
+
+
+Element_THRM::~Element_THRM() {} \ No newline at end of file
diff --git a/src/simulation/elements/TRON.cpp b/src/simulation/elements/TRON.cpp
new file mode 100644
index 0000000..36493a7
--- /dev/null
+++ b/src/simulation/elements/TRON.cpp
@@ -0,0 +1,236 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_TRON PT_TRON 143
+Element_TRON::Element_TRON()
+{
+ Identifier = "DEFAULT_PT_TRON";
+ Name = "TRON";
+ Colour = PIXPACK(0xA9FF00);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = 0.0f;
+ HeatConduct = 40;
+ Description = "Smart particles, Travels in straight lines and avoids obstacles. Grows with time.";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC|PROP_LIFE_KILL;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_TRON::update;
+ Graphics = &Element_TRON::graphics;
+
+ Element_TRON::init_graphics();
+}
+
+#define TRON_HEAD 1
+#define TRON_NOGROW 2
+#define TRON_WAIT 4 //it was just created, so WAIT a frame
+#define TRON_NODIE 8
+#define TRON_DEATH 16 //Crashed, now dying
+int tron_rx[4] = {-1, 0, 1, 0};
+int tron_ry[4] = { 0,-1, 0, 1};
+unsigned int tron_colours[32];
+
+//#TPT-Directive ElementHeader Element_TRON static void init_graphics()
+void Element_TRON::init_graphics()
+{
+ int i;
+ int r, g, b;
+ for (i=0; i<32; i++)
+ {
+ HSV_to_RGB(i<<4,255,255,&r,&g,&b);
+ tron_colours[i] = r<<16 | g<<8 | b;
+ }
+}
+
+//#TPT-Directive ElementHeader Element_TRON static int update(UPDATE_FUNC_ARGS)
+int Element_TRON::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry, np;
+ if (parts[i].tmp&TRON_WAIT)
+ {
+ parts[i].tmp &= ~TRON_WAIT;
+ return 0;
+ }
+ if (parts[i].tmp&TRON_HEAD)
+ {
+ int firstdircheck = 0,seconddir,seconddircheck = 0,lastdir,lastdircheck = 0;
+ int direction = (parts[i].tmp>>5 & 0x3);
+ int originaldir = direction;
+
+ //random turn
+ int random = rand()%340;
+ if (random==1 || random==3)
+ {
+ //randomly turn left(3) or right(1)
+ direction = (direction + random)%4;
+ }
+
+ //check infront
+ //do sight check
+ firstdircheck = Element_TRON::trymovetron(sim,x,y,direction,i,parts[i].tmp2);
+ if (firstdircheck < parts[i].tmp2)
+ {
+ if (originaldir != direction) //if we just tried a random turn, don't pick random again
+ {
+ seconddir = originaldir;
+ lastdir = (direction + 2)%4;
+ }
+ else
+ {
+ seconddir = (direction + ((rand()%2)*2)+1)% 4;
+ lastdir = (seconddir + 2)%4;
+ }
+ seconddircheck = trymovetron(sim,x,y,seconddir,i,parts[i].tmp2);
+ lastdircheck = trymovetron(sim,x,y,lastdir,i,parts[i].tmp2);
+ }
+ //find the best move
+ if (seconddircheck > firstdircheck)
+ direction = seconddir;
+ if (lastdircheck > seconddircheck && lastdircheck > firstdircheck)
+ direction = lastdir;
+ //now try making new head, even if it fails
+ if (Element_TRON::new_tronhead(sim,x + tron_rx[direction],y + tron_ry[direction],i,direction) == -1)
+ {
+ //ohgod crash
+ parts[i].tmp |= TRON_DEATH;
+ //trigger tail death for TRON_NODIE, or is that mode even needed? just set a high tail length(but it still won't start dying when it crashes)
+ }
+
+ //set own life and clear .tmp (it dies if it can't move anyway)
+ parts[i].life = parts[i].tmp2;
+ parts[i].tmp &= parts[i].tmp&0xF818;
+ }
+ else // fade tail deco, or prevent tail from dieing
+ {
+ if (parts[i].tmp&TRON_NODIE)
+ parts[i].life++;
+ //parts[i].dcolour = clamp_flt((float)parts[i].life/(float)parts[i].tmp2,0,1.0f) << 24 | parts[i].dcolour&0x00FFFFFF;
+ }
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_TRON static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_TRON::graphics(GRAPHICS_FUNC_ARGS)
+ {
+ unsigned int col = tron_colours[(cpart->tmp&0xF800)>>11];
+ if(cpart->tmp & TRON_HEAD)
+ *pixel_mode |= PMODE_GLOW;
+ *colr = (col & 0xFF0000)>>16;
+ *colg = (col & 0x00FF00)>>8;
+ *colb = (col & 0x0000FF);
+ if(cpart->tmp & TRON_DEATH)
+ {
+ *pixel_mode |= FIRE_ADD | PMODE_FLARE;
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+ *firea = 255;
+ }
+ if(cpart->life < cpart->tmp2 && !(cpart->tmp & TRON_HEAD))
+ {
+ *pixel_mode |= PMODE_BLEND;
+ *pixel_mode &= ~PMODE_FLAT;
+ *cola = (int)((((float)cpart->life)/((float)cpart->tmp2))*255.0f);
+ }
+ return 0;
+}
+
+//#TPT-Directive ElementHeader Element_TRON static int new_tronhead(Simulation * sim, int x, int y, int i, int direction)
+int Element_TRON::new_tronhead(Simulation * sim, int x, int y, int i, int direction)
+{
+ int np = sim->create_part(-1, x , y ,PT_TRON);
+ if (np==-1)
+ return -1;
+ if (sim->parts[i].life >= 100) // increase tail length
+ {
+ if (!(sim->parts[i].tmp&TRON_NOGROW))
+ sim->parts[i].tmp2++;
+ sim->parts[i].life = 5;
+ }
+ //give new head our properties
+ sim->parts[np].tmp = 1 | direction<<5 | sim->parts[i].tmp&(TRON_NOGROW|TRON_NODIE) | (sim->parts[i].tmp&0xF800);
+ if (np > i)
+ sim->parts[np].tmp |= TRON_WAIT;
+
+ sim->parts[np].ctype = sim->parts[i].ctype;
+ sim->parts[np].tmp2 = sim->parts[i].tmp2;
+ sim->parts[np].life = sim->parts[i].life + 2;
+ return 1;
+}
+
+//#TPT-Directive ElementHeader Element_TRON static int trymovetron(Simulation * sim, int x, int y, int dir, int i, int len)
+int Element_TRON::trymovetron(Simulation * sim, int x, int y, int dir, int i, int len)
+{
+ int k,j,r,rx,ry,tx,ty,count;
+ count = 0;
+ rx = x;
+ ry = y;
+ for (k = 1; k <= len; k ++)
+ {
+ rx += tron_rx[dir];
+ ry += tron_ry[dir];
+ r = sim->pmap[ry][rx];
+ if (!r && !sim->bmap[(ry)/CELL][(rx)/CELL] && ry > CELL && rx > CELL && ry < YRES-CELL && rx < XRES-CELL)
+ {
+ count++;
+ for (tx = rx - tron_ry[dir] , ty = ry - tron_rx[dir], j=1; abs(tx-rx) < (len-k) && abs(ty-ry) < (len-k); tx-=tron_ry[dir],ty-=tron_rx[dir],j++)
+ {
+ r = sim->pmap[ty][tx];
+ if (!r && !sim->bmap[(ty)/CELL][(tx)/CELL] && ty > CELL && tx > CELL && ty < YRES-CELL && tx < XRES-CELL)
+ {
+ if (j == (len-k))//there is a safe path, so we can break out
+ return len+1;
+ count++;
+ }
+ else //we hit a block so no need to check farther here
+ break;
+ }
+ for (tx = rx + tron_ry[dir] , ty = ry + tron_rx[dir], j=1; abs(tx-rx) < (len-k) && abs(ty-ry) < (len-k); tx+=tron_ry[dir],ty+=tron_rx[dir],j++)
+ {
+ r = sim->pmap[ty][tx];
+ if (!r && !sim->bmap[(ty)/CELL][(tx)/CELL] && ty > CELL && tx > CELL && ty < YRES-CELL && tx < XRES-CELL)
+ {
+ if (j == (len-k))
+ return len+1;
+ count++;
+ }
+ else
+ break;
+ }
+ }
+ else //a block infront, no need to continue
+ break;
+ }
+ return count;
+}
+
+Element_TRON::~Element_TRON() {}
diff --git a/src/simulation/elements/TSNS.cpp b/src/simulation/elements/TSNS.cpp
new file mode 100644
index 0000000..d5c8aa7
--- /dev/null
+++ b/src/simulation/elements/TSNS.cpp
@@ -0,0 +1,93 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_TSNS PT_TSNS 164
+Element_TSNS::Element_TSNS()
+{
+ Identifier = "DEFAULT_PT_TSNS";
+ Name = "TSNS";
+ Colour = PIXPACK(0xFD9D18);
+ MenuVisible = 1;
+ MenuSection = SC_SENSOR;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Creates a spark when there's a nearby particle with a greater temperature";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_TSNS::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_TSNS static int update(UPDATE_FUNC_ARGS)
+int Element_TSNS::update(UPDATE_FUNC_ARGS)
+{
+ int r, rx, ry, rt, rd = parts[i].tmp2;
+ if (rd > 25) parts[i].tmp2 = rd = 25;
+ if (parts[i].life)
+ {
+ parts[i].life = 0;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ rt = parts[r>>8].type;
+ if (sim->parts_avg(i,r>>8,PT_INSL) != PT_INSL)
+ {
+ if ((sim->elements[rt].Properties&PROP_CONDUCTS) && !(rt==PT_WATR||rt==PT_SLTW||rt==PT_NTCT||rt==PT_PTCT||rt==PT_INWR) && parts[r>>8].life==0 && Element_DTEC::in_radius(rd, rx, ry))
+ {
+ parts[r>>8].life = 4;
+ parts[r>>8].ctype = rt;
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SPRK);
+ }
+ }
+ }
+ }
+ for (rx=-rd; rx<rd+1; rx++)
+ for (ry=-rd; ry<rd+1; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if(!r)
+ r = sim->photons[y+ry][x+rx];
+ if(!r)
+ continue;
+ if (parts[r>>8].temp > parts[i].temp && parts[r>>8].type != PT_TSNS)
+ parts[i].life = 1;
+ }
+ return 0;
+}
+
+
+
+Element_TSNS::~Element_TSNS() {}
diff --git a/src/simulation/elements/TTAN.cpp b/src/simulation/elements/TTAN.cpp
new file mode 100644
index 0000000..c51319d
--- /dev/null
+++ b/src/simulation/elements/TTAN.cpp
@@ -0,0 +1,76 @@
+#include "simulation/Elements.h"
+#include "simulation/Air.h"
+//#TPT-Directive ElementClass Element_TTAN PT_TTAN 144
+Element_TTAN::Element_TTAN()
+{
+ Identifier = "DEFAULT_PT_TTAN";
+ Name = "TTAN";
+ Colour = PIXPACK(0x909090);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 1;
+ Hardness = 50;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Titanium, Higher melting temperature than other metals, blocks all air pressure";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_HOT_GLOW|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 1941.0f;
+ HighTemperatureTransition = PT_LAVA;
+
+ Update = &Element_TTAN::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_TTAN static int update(UPDATE_FUNC_ARGS)
+int Element_TTAN::update(UPDATE_FUNC_ARGS)
+ {
+ int nx, ny, ttan = 0;
+ if(nt<=2)
+ ttan = 2;
+ else if(parts[i].tmp)
+ ttan = 2;
+ else if(nt<=6)
+ for (nx=-1; nx<2; nx++) {
+ for (ny=-1; ny<2; ny++) {
+ if ((!nx != !ny) && x+nx>=0 && y+ny>=0 && x+nx<XRES && y+ny<YRES) {
+ if((pmap[y+ny][x+nx]&0xFF)==PT_TTAN)
+ ttan++;
+ }
+ }
+ }
+
+ if(ttan>=2) {
+ sim->air->bmap_blockair[y/CELL][x/CELL] = 1;
+ sim->air->bmap_blockairh[y/CELL][x/CELL] = 1;
+ }
+ return 0;
+}
+
+
+Element_TTAN::~Element_TTAN() {}
diff --git a/src/simulation/elements/URAN.cpp b/src/simulation/elements/URAN.cpp
new file mode 100644
index 0000000..a988bf3
--- /dev/null
+++ b/src/simulation/elements/URAN.cpp
@@ -0,0 +1,61 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_URAN PT_URAN 32
+Element_URAN::Element_URAN()
+{
+ Identifier = "DEFAULT_PT_URAN";
+ Name = "URAN";
+ Colour = PIXPACK(0x707020);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.4f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.4f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 90;
+
+ Temperature = R_TEMP+30.0f+273.15f;
+ HeatConduct = 251;
+ Description = "Heavy particles. Generates heat under pressure.";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART | PROP_RADIOACTIVE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_URAN::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_URAN static int update(UPDATE_FUNC_ARGS)
+int Element_URAN::update(UPDATE_FUNC_ARGS)
+ {
+ if (!sim->legacy_enable && sim->pv[y/CELL][x/CELL]>0.0f)
+ {
+ float atemp = parts[i].temp + (-MIN_TEMP);
+ parts[i].temp = restrict_flt((atemp*(1+(sim->pv[y/CELL][x/CELL]/2000)))+MIN_TEMP, MIN_TEMP, MAX_TEMP);
+ }
+ return 0;
+}
+
+
+Element_URAN::~Element_URAN() {} \ No newline at end of file
diff --git a/src/simulation/elements/VIBR.cpp b/src/simulation/elements/VIBR.cpp
new file mode 100644
index 0000000..68119f7
--- /dev/null
+++ b/src/simulation/elements/VIBR.cpp
@@ -0,0 +1,235 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_VIBR PT_VIBR 165
+Element_VIBR::Element_VIBR()
+{
+ Identifier = "DEFAULT_PT_VIBR";
+ Name = "VIBR";
+ Colour = PIXPACK(0x005000);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.85f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = 273.15f;
+ HeatConduct = 251;
+ Description = "Vibranium. Stores energy and releases it in violent explosions.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID|PROP_LIFE_DEC;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_VIBR::update;
+ Graphics = &Element_VIBR::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_VIBR static int update(UPDATE_FUNC_ARGS)
+int Element_VIBR::update(UPDATE_FUNC_ARGS) {
+ int r, rx, ry;
+ int trade, transfer;
+ if (parts[i].ctype == 1) //leaving in, just because
+ {
+ if (sim->pv[y/CELL][x/CELL] > -2.5 || parts[i].tmp)
+ {
+ parts[i].ctype = 0;
+ sim->part_change_type(i, x, y, PT_VIBR);
+ }
+ }
+ else if (!parts[i].life) //if not exploding
+ {
+ //Heat absorption code
+ if (parts[i].temp > 274.65f)
+ {
+ parts[i].tmp++;
+ parts[i].temp -= 3;
+ }
+ if (parts[i].temp < 271.65f)
+ {
+ parts[i].tmp--;
+ parts[i].temp += 3;
+ }
+ //Pressure absorption code
+ if (sim->pv[y/CELL][x/CELL] > 2.5)
+ {
+ parts[i].tmp += 7;
+ sim->pv[y/CELL][x/CELL]--;
+ }
+ if (sim->pv[y/CELL][x/CELL] < -2.5)
+ {
+ parts[i].tmp -= 2;
+ sim->pv[y/CELL][x/CELL]++;
+ }
+ //initiate explosion counter
+ if (parts[i].tmp > 1000)
+ parts[i].life = 750;
+ }
+ else //if it is exploding
+ {
+ //Release sparks before explode
+ if (parts[i].life < 300)
+ {
+ rx = rand()%3-1;
+ ry = rand()%3-1;
+ r = pmap[y+ry][x+rx];
+ if ((r&0xFF) && (r&0xFF) != PT_BREC && (sim->elements[r&0xFF].Properties&PROP_CONDUCTS) && !parts[r>>8].life)
+ {
+ parts[r>>8].life = 4;
+ parts[r>>8].ctype = r&0xFF;
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SPRK);
+ }
+ }
+ //Release all heat
+ if (parts[i].life < 500)
+ {
+ int random = rand();
+ rx = random%7-3;
+ ry = (random>>3)%7-3;
+ if(x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES)
+ {
+ r = pmap[y+ry][x+rx];
+ if ((r&0xFF) && (r&0xFF)!=PT_VIBR && (r&0xFF)!=PT_BVBR && sim->elements[r&0xFF].HeatConduct && ((r&0xFF)!=PT_HSWC||parts[r>>8].life==10))
+ {
+ parts[r>>8].temp += parts[i].tmp*3;
+ parts[i].tmp = 0;
+ }
+ }
+ }
+ //Explosion code
+ if (parts[i].life == 1)
+ {
+ int random = rand(), index;
+ sim->create_part(i, x, y, PT_EXOT);
+ parts[i].tmp2 = rand()%1000;
+ index = sim->create_part(-3,x+((random>>4)&3)-1,y+((random>>6)&3)-1,PT_ELEC);
+ if (index != -1)
+ parts[index].temp = 7000;
+ index = sim->create_part(-3,x+((random>>8)&3)-1,y+((random>>10)&3)-1,PT_PHOT);
+ if (index != -1)
+ parts[index].temp = 7000;
+ index = sim->create_part(-1,x+((random>>12)&3)-1,y+rand()%3-1,PT_BREC);
+ if (index != -1)
+ parts[index].temp = 7000;
+ parts[i].temp=9000;
+ sim->pv[y/CELL][x/CELL] += 50;
+
+ return 1;
+ }
+ }
+ //Neighbor check loop
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ r = sim->photons[y+ry][x+rx];
+ if (!r)
+ continue;
+ //Melts into EXOT
+ if ((r&0xFF) == PT_EXOT && !(rand()%250))
+ {
+ sim->create_part(i, x, y, PT_EXOT);
+ }
+ else if ((r&0xFF) == PT_ANAR)
+ {
+ sim->part_change_type(i,x,y,PT_BVBR);
+ sim->pv[y/CELL][x/CELL] -= 1;
+ }
+ else if (parts[i].life && ((r&0xFF)==PT_VIBR || (r&0xFF)==PT_BVBR) && !parts[r>>8].life)
+ {
+ parts[r>>8].tmp += 10;
+ }
+ //Absorbs energy particles
+ if ((sim->elements[r&0xFF].Properties & TYPE_ENERGY))
+ {
+ parts[i].tmp += 20;
+ sim->kill_part(r>>8);
+ }
+ }
+ for (trade = 0; trade < 9; trade++)
+ {
+ int random = rand();
+ rx = random%7-3;
+ ry = (random>>3)%7-3;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if ((r&0xFF) != PT_VIBR && (r&0xFF) != PT_BVBR)
+ continue;
+ if (parts[i].tmp > parts[r>>8].tmp)
+ {
+ transfer = parts[i].tmp - parts[r>>8].tmp;
+ if (transfer == 1)
+ {
+ parts[r>>8].tmp += 1;
+ parts[i].tmp -= 1;
+ trade = 9;
+ }
+ else if (transfer > 0)
+ {
+ parts[r>>8].tmp += transfer/2;
+ parts[i].tmp -= transfer/2;
+ trade = 9;
+ }
+ }
+ }
+ }
+ if (parts[i].tmp < 0)
+ parts[i].tmp = 0; // only preventing because negative tmp doesn't save
+ return 0;
+}
+
+//#TPT-Directive ElementHeader Element_VIBR static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_VIBR::graphics(GRAPHICS_FUNC_ARGS)
+{
+ int gradient = cpart->tmp/10;
+ if (gradient >= 100 || cpart->life)
+ {
+ *colr = (int)(fabs(sin(exp((750.0f-cpart->life)/170)))*200.0f);
+ *colg = 255;
+ *colb = (int)(fabs(sin(exp((750.0f-cpart->life)/170)))*200.0f);
+ *firea = 90;
+ *firer = *colr;
+ *fireg = *colg;
+ *fireb = *colb;
+ *pixel_mode = PMODE_NONE;
+ *pixel_mode |= FIRE_BLEND;
+ }
+ else if (gradient < 100)
+ {
+ *colr += (int)restrict_flt(gradient*2.0f,0,255);
+ *colg += (int)restrict_flt(gradient*2.0f,0,175);
+ *colb += (int)restrict_flt(gradient*2.0f,0,255);
+ *firea = (int)restrict_flt(gradient*.6f,0,60);
+ *firer = *colr/2;
+ *fireg = *colg/2;
+ *fireb = *colb/2;
+ *pixel_mode |= FIRE_BLEND;
+ }
+ return 0;
+}
+
+Element_VIBR::~Element_VIBR() {}
diff --git a/src/simulation/elements/VINE.cpp b/src/simulation/elements/VINE.cpp
new file mode 100644
index 0000000..e31c8cd
--- /dev/null
+++ b/src/simulation/elements/VINE.cpp
@@ -0,0 +1,91 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_VINE PT_VINE 114
+Element_VINE::Element_VINE()
+{
+ Identifier = "DEFAULT_PT_VINE";
+ Name = "VINE";
+ Colour = PIXPACK(0x079A00);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 20;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 10;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 65;
+ Description = "Vine, grows";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 573.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = &Element_VINE::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_VINE static int update(UPDATE_FUNC_ARGS)
+int Element_VINE::update(UPDATE_FUNC_ARGS)
+ {
+ int r, np, rx =(rand()%3)-1, ry=(rand()%3)-1;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (1>rand()%15)
+ sim->part_change_type(i,x,y,PT_PLNT);
+ else if (!r)
+ {
+ np = sim->create_part(-1,x+rx,y+ry,PT_VINE);
+ if (np<0) return 0;
+ parts[np].temp = parts[i].temp;
+ parts[i].tmp = 1;
+ sim->part_change_type(i,x,y,PT_PLNT);
+ }
+ }
+ if (parts[i].temp > 350 && parts[i].temp > parts[i].tmp2)
+ parts[i].tmp2 = (int)parts[i].temp;
+ return 0;
+}
+
+//#TPT-Directive ElementHeader Element_VINE static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_VINE::graphics(GRAPHICS_FUNC_ARGS)
+{
+ float maxtemp = std::max((float)cpart->tmp2, cpart->temp);
+ if (maxtemp > 300)
+ {
+ *colr += (int)restrict_flt((maxtemp-300)/5,0,58);
+ *colg -= (int)restrict_flt((maxtemp-300)/2,0,102);
+ *colb += (int)restrict_flt((maxtemp-300)/5,0,70);
+ }
+ if (maxtemp < 273)
+ {
+ *colg += (int)restrict_flt((273-maxtemp)/4,0,255);
+ *colb += (int)restrict_flt((273-maxtemp)/1.5,0,255);
+ }
+ return 0;
+}
+
+
+Element_VINE::~Element_VINE() {} \ No newline at end of file
diff --git a/src/simulation/elements/VOID.cpp b/src/simulation/elements/VOID.cpp
new file mode 100644
index 0000000..05eb872
--- /dev/null
+++ b/src/simulation/elements/VOID.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_VOID PT_VOID 22
+Element_VOID::Element_VOID()
+{
+ Identifier = "DEFAULT_PT_VOID";
+ Name = "VOID";
+ Colour = PIXPACK(0x790B0B);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 1.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = -0.0003f* CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Hole, will drain away any particles.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+
+}
+
+Element_VOID::~Element_VOID() {} \ No newline at end of file
diff --git a/src/simulation/elements/WARP.cpp b/src/simulation/elements/WARP.cpp
new file mode 100644
index 0000000..bb8f8e1
--- /dev/null
+++ b/src/simulation/elements/WARP.cpp
@@ -0,0 +1,95 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_WARP PT_WARP 96
+Element_WARP::Element_WARP()
+{
+ Identifier = "DEFAULT_PT_WARP";
+ Name = "WARP";
+ Colour = PIXPACK(0x101010);
+ MenuVisible = 1;
+ MenuSection = SC_NUCLEAR;
+ Enabled = 1;
+
+ Advection = 0.8f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.9f;
+ Loss = 0.70f;
+ Collision = -0.1f;
+ Gravity = 0.0f;
+ Diffusion = 3.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 1;
+
+ Temperature = R_TEMP +273.15f;
+ HeatConduct = 100;
+ Description = "Displaces other elements.";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS|PROP_LIFE_DEC|PROP_LIFE_KILL;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_WARP::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_WARP static int update(UPDATE_FUNC_ARGS)
+int Element_WARP::update(UPDATE_FUNC_ARGS)
+ {
+ int trade, r, rx, ry;
+ if (parts[i].tmp2>2000)
+ {
+ parts[i].temp = 10000;
+ sim->pv[y/CELL][x/CELL] += (parts[i].tmp2/5000) * CFDS;
+ if (2>rand()%100)
+ sim->create_part(-3, x, y, PT_ELEC);
+ }
+ for ( trade = 0; trade<5; trade ++)
+ {
+ rx = rand()%3-1;
+ ry = rand()%3-1;
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)!=PT_WARP&&(r&0xFF)!=PT_STKM&&(r&0xFF)!=PT_STKM2&&(r&0xFF)!=PT_DMND&&(r&0xFF)!=PT_CLNE&&(r&0xFF)!=PT_BCLN&&(r&0xFF)!=PT_PCLN)
+ {
+ parts[i].x = parts[r>>8].x;
+ parts[i].y = parts[r>>8].y;
+ parts[r>>8].x = x;
+ parts[r>>8].y = y;
+ parts[r>>8].vx = (rand()%4)-1.5;
+ parts[r>>8].vy = (rand()%4)-2;
+ parts[i].life += 4;
+ pmap[y][x] = r;
+ pmap[y+ry][x+rx] = (i<<8)|parts[i].type;
+ trade = 5;
+ }
+ }
+ }
+ return 0;
+}
+
+//#TPT-Directive ElementHeader Element_WARP static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_WARP::graphics(GRAPHICS_FUNC_ARGS)
+{
+ *colr = *colg = *colb = *cola = 0;
+ *pixel_mode &= ~PMODE;
+ return 0;
+}
+
+Element_WARP::~Element_WARP() {}
diff --git a/src/simulation/elements/WATR.cpp b/src/simulation/elements/WATR.cpp
new file mode 100644
index 0000000..f0fde66
--- /dev/null
+++ b/src/simulation/elements/WATR.cpp
@@ -0,0 +1,89 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_WATR PT_WATR 2
+Element_WATR::Element_WATR()
+{
+ Identifier = "DEFAULT_PT_WATR";
+ Name = "WATR";
+ Colour = PIXPACK(0x2030D0);
+ MenuVisible = 1;
+ MenuSection = SC_LIQUID;
+ Enabled = 1;
+
+ Advection = 0.6f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.98f;
+ Loss = 0.95f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 2;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 20;
+
+ Weight = 30;
+
+ Temperature = R_TEMP-2.0f +273.15f;
+ HeatConduct = 29;
+ Description = "Liquid. Conducts electricity. Freezes. Extinguishes fires.";
+
+ State = ST_LIQUID;
+ Properties = TYPE_LIQUID|PROP_CONDUCTS|PROP_LIFE_DEC|PROP_NEUTPENETRATE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 273.15f;
+ LowTemperatureTransition = PT_ICEI;
+ HighTemperature = 373.0f;
+ HighTemperatureTransition = PT_WTRV;
+
+ Update = &Element_WATR::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_WATR static int update(UPDATE_FUNC_ARGS)
+int Element_WATR::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_SALT && 1>(rand()%250))
+ {
+ sim->part_change_type(i,x,y,PT_SLTW);
+ // on average, convert 3 WATR to SLTW before SALT turns into SLTW
+ if (rand()%3==0)
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SLTW);
+ }
+ if (((r&0xFF)==PT_RBDM||(r&0xFF)==PT_LRBD) && (sim->legacy_enable||parts[i].temp>(273.15f+12.0f)) && 1>(rand()%500))
+ {
+ sim->part_change_type(i,x,y,PT_FIRE);
+ parts[i].life = 4;
+ parts[i].ctype = PT_WATR;
+ }
+ if ((r&0xFF)==PT_FIRE && parts[r>>8].ctype!=PT_WATR){
+ sim->kill_part(r>>8);
+ if(1>(rand()%150)){
+ sim->kill_part(i);
+ return 1;
+ }
+ }
+ /*if ((r&0xFF)==PT_CNCT && 1>(rand()%500)) Concrete+Water to paste, not very popular
+ {
+ part_change_type(i,x,y,PT_PSTE);
+ sim.kill_part(r>>8);
+ }*/
+ }
+ return 0;
+}
+
+Element_WATR::~Element_WATR() {}
diff --git a/src/simulation/elements/WAX.cpp b/src/simulation/elements/WAX.cpp
new file mode 100644
index 0000000..5f9a113
--- /dev/null
+++ b/src/simulation/elements/WAX.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_WAX PT_WAX 33
+Element_WAX::Element_WAX()
+{
+ Identifier = "DEFAULT_PT_WAX";
+ Name = "WAX";
+ Colour = PIXPACK(0xF0F0BB);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 10;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 44;
+ Description = "Wax. Melts at moderately high temperatures.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 319.0f;
+ HighTemperatureTransition = PT_MWAX;
+
+ Update = NULL;
+
+}
+
+Element_WAX::~Element_WAX() {} \ No newline at end of file
diff --git a/src/simulation/elements/WHOL.cpp b/src/simulation/elements/WHOL.cpp
new file mode 100644
index 0000000..3007960
--- /dev/null
+++ b/src/simulation/elements/WHOL.cpp
@@ -0,0 +1,49 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_WHOL PT_WHOL 40
+Element_WHOL::Element_WHOL()
+{
+ Identifier = "DEFAULT_PT_WHOL";
+ Name = "VENT";
+ Colour = PIXPACK(0xEFEFEF);
+ MenuVisible = 1;
+ MenuSection = SC_SPECIAL;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.95f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.010f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP-16.0f+273.15f;
+ HeatConduct = 255;
+ Description = "Air vent, creates pressure and pushes other particles away.";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = NULL;
+
+}
+
+Element_WHOL::~Element_WHOL() {} \ No newline at end of file
diff --git a/src/simulation/elements/WIFI.cpp b/src/simulation/elements/WIFI.cpp
new file mode 100644
index 0000000..4cbf0d5
--- /dev/null
+++ b/src/simulation/elements/WIFI.cpp
@@ -0,0 +1,101 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_WIFI PT_WIFI 124
+Element_WIFI::Element_WIFI()
+{
+ Identifier = "DEFAULT_PT_WIFI";
+ Name = "WIFI";
+ Colour = PIXPACK(0x40A060);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 2;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 0;
+ Description = "Wireless transmitter, color coded.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = 15.0f;
+ HighPressureTransition = PT_BRMT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_WIFI::update;
+ Graphics = &Element_WIFI::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_WIFI static int update(UPDATE_FUNC_ARGS)
+int Element_WIFI::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ parts[i].tmp = (int)((parts[i].temp-73.15f)/100+1);
+ if (parts[i].tmp>=CHANNELS) parts[i].tmp = CHANNELS-1;
+ else if (parts[i].tmp<0) parts[i].tmp = 0;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ // wireless[][0] - whether channel is active on this frame
+ // wireless[][1] - whether channel should be active on next frame
+ if (sim->wireless[parts[i].tmp][0])
+ {
+ if (((r&0xFF)==PT_NSCN||(r&0xFF)==PT_PSCN||(r&0xFF)==PT_INWR)&&parts[r>>8].life==0 && sim->wireless[parts[i].tmp][0])
+ {
+ parts[r>>8].ctype = r&0xFF;
+ sim->part_change_type(r>>8,x+rx,y+ry,PT_SPRK);
+ parts[r>>8].life = 4;
+ }
+ }
+ else
+ {
+ if ((r&0xFF)==PT_SPRK && parts[r>>8].ctype!=PT_NSCN && parts[r>>8].life>=3)
+ {
+ sim->wireless[parts[i].tmp][1] = 1;
+ sim->ISWIRE = 2;
+ }
+ }
+ }
+ return 0;
+}
+
+
+//#TPT-Directive ElementHeader Element_WIFI static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_WIFI::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ float frequency = 0.0628;
+ int q = cpart->tmp;
+ *colr = sin(frequency*q + 0) * 127 + 128;
+ *colg = sin(frequency*q + 2) * 127 + 128;
+ *colb = sin(frequency*q + 4) * 127 + 128;
+ *pixel_mode |= EFFECT_DBGLINES;
+ return 0;
+}
+
+
+Element_WIFI::~Element_WIFI() {} \ No newline at end of file
diff --git a/src/simulation/elements/WIRE.cpp b/src/simulation/elements/WIRE.cpp
new file mode 100644
index 0000000..4f59060
--- /dev/null
+++ b/src/simulation/elements/WIRE.cpp
@@ -0,0 +1,127 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_WIRE PT_WIRE 156
+Element_WIRE::Element_WIRE()
+{
+ Identifier = "DEFAULT_PT_WIRE";
+ Name = "WIRE";
+ Colour = PIXPACK(0xFFCC00);
+ MenuVisible = 1;
+ MenuSection = SC_ELEC;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.00f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 0;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 250;
+ Description = "WireWorld wires.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_WIRE::update;
+ Graphics = &Element_WIRE::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_WIRE static int update(UPDATE_FUNC_ARGS)
+int Element_WIRE::update(UPDATE_FUNC_ARGS)
+ {
+ int s,r,rx,ry,count;
+ /*
+ 0: wire
+ 1: spark head
+ 2: spark tail
+
+ tmp is previous state, ctype is current state
+ */
+ //parts[i].tmp=parts[i].ctype;
+ parts[i].ctype=0;
+ if(parts[i].tmp==1)
+ {
+ parts[i].ctype=2;
+ }
+ if(parts[i].tmp==2)
+ {
+ parts[i].ctype=0;
+ }
+
+ count=0;
+ for(rx=-1; rx<2; rx++)
+ for(ry=-1; ry<2; ry++)
+ {
+ if(x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if((r&0xFF)==PT_SPRK && parts[r>>8].life==3 && parts[r>>8].ctype==PT_PSCN)
+ {
+ parts[i].ctype=1;
+ return 0;
+ }
+ else if((r&0xFF)==PT_NSCN && parts[i].tmp==1){sim->create_part(-1, x+rx, y+ry, PT_SPRK);}
+ else if((r&0xFF)==PT_WIRE && parts[r>>8].tmp==1 && !parts[i].tmp){count++;}
+ }
+ }
+ if(count==1 || count==2)
+ parts[i].ctype=1;
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_WIRE static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_WIRE::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ if (cpart->ctype==0)
+ {
+ *colr = 255;
+ *colg = 204;
+ *colb = 0;
+ return 0;
+ }
+ if (cpart->ctype==1)
+ {
+ *colr = 50;
+ *colg = 100;
+ *colb = 255;
+ //*pixel_mode |= PMODE_GLOW;
+ return 0;
+ }
+ if (cpart->ctype==2)
+ {
+ *colr = 255;
+ *colg = 100;
+ *colb = 50;
+ //*pixel_mode |= PMODE_GLOW;
+ return 0;
+ }
+ return 0;
+}
+
+
+Element_WIRE::~Element_WIRE() {} \ No newline at end of file
diff --git a/src/simulation/elements/WOOD.cpp b/src/simulation/elements/WOOD.cpp
new file mode 100644
index 0000000..54e2e50
--- /dev/null
+++ b/src/simulation/elements/WOOD.cpp
@@ -0,0 +1,70 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_WOOD PT_WOOD 17
+Element_WOOD::Element_WOOD()
+{
+ Identifier = "DEFAULT_PT_WOOD";
+ Name = "WOOD";
+ Colour = PIXPACK(0xC0A040);
+ MenuVisible = 1;
+ MenuSection = SC_SOLIDS;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 20;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 15;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 164;
+ Description = "Solid. Flammable.";
+
+ State = ST_SOLID;
+ Properties = TYPE_SOLID | PROP_NEUTPENETRATE;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 873.0f;
+ HighTemperatureTransition = PT_FIRE;
+
+ Update = NULL;
+ Graphics = &Element_WOOD::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_WOOD static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_WOOD::graphics(GRAPHICS_FUNC_ARGS)
+{
+ float maxtemp = std::max((float)cpart->tmp, cpart->temp);
+ if (maxtemp > 400)
+ {
+ *colr -= (int)restrict_flt((maxtemp-400)/3,0,172);
+ *colg -= (int)restrict_flt((maxtemp-400)/4,0,140);
+ *colb -= (int)restrict_flt((maxtemp-400)/20,0,44);
+ if (maxtemp > 450)
+ cpart->tmp = (int)maxtemp;
+ }
+ if (maxtemp < 273)
+ {
+ *colr -= (int)restrict_flt((273-maxtemp)/5,0,40);
+ *colg += (int)restrict_flt((273-maxtemp)/4,0,40);
+ *colb += (int)restrict_flt((273-maxtemp)/1.5,0,150);
+ }
+ return 0;
+}
+
+Element_WOOD::~Element_WOOD() {} \ No newline at end of file
diff --git a/src/simulation/elements/WTRV.cpp b/src/simulation/elements/WTRV.cpp
new file mode 100644
index 0000000..fb72db8
--- /dev/null
+++ b/src/simulation/elements/WTRV.cpp
@@ -0,0 +1,73 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_WTRV PT_WTRV 23
+Element_WTRV::Element_WTRV()
+{
+ Identifier = "DEFAULT_PT_WTRV";
+ Name = "WTRV";
+ Colour = PIXPACK(0xA0A0FF);
+ MenuVisible = 1;
+ MenuSection = SC_GAS;
+ Enabled = 1;
+
+ Advection = 1.0f;
+ AirDrag = 0.01f * CFDS;
+ AirLoss = 0.99f;
+ Loss = 0.30f;
+ Collision = -0.1f;
+ Gravity = -0.1f;
+ Diffusion = 0.75f;
+ HotAir = 0.0003f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 4;
+
+ Weight = 1;
+
+ Temperature = R_TEMP+100.0f+273.15f;
+ HeatConduct = 48;
+ Description = "Steam, heats up air, produced from hot water.";
+
+ State = ST_GAS;
+ Properties = TYPE_GAS;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = 371.0f;
+ LowTemperatureTransition = ST;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_WTRV::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_WTRV static int update(UPDATE_FUNC_ARGS)
+int Element_WTRV::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if (((r&0xFF)==PT_RBDM||(r&0xFF)==PT_LRBD) && !sim->legacy_enable && parts[i].temp>(273.15f+12.0f) && 1>(rand()%500))
+ {
+ sim->part_change_type(i,x,y,PT_FIRE);
+ parts[i].life = 4;
+ parts[i].ctype = PT_WATR;
+ }
+ }
+ if(parts[i].temp>1273&&parts[i].ctype==PT_FIRE)
+ parts[i].temp-=parts[i].temp/1000;
+ return 0;
+}
+
+
+Element_WTRV::~Element_WTRV() {} \ No newline at end of file
diff --git a/src/simulation/elements/YEST.cpp b/src/simulation/elements/YEST.cpp
new file mode 100644
index 0000000..99df265
--- /dev/null
+++ b/src/simulation/elements/YEST.cpp
@@ -0,0 +1,72 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_YEST PT_YEST 63
+Element_YEST::Element_YEST()
+{
+ Identifier = "DEFAULT_PT_YEST";
+ Name = "YEST";
+ Colour = PIXPACK(0xEEE0C0);
+ MenuVisible = 1;
+ MenuSection = SC_POWDERS;
+ Enabled = 1;
+
+ Advection = 0.7f;
+ AirDrag = 0.02f * CFDS;
+ AirLoss = 0.96f;
+ Loss = 0.80f;
+ Collision = 0.0f;
+ Gravity = 0.1f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 1;
+
+ Flammable = 15;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 30;
+
+ Weight = 80;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 70;
+ Description = "Yeast, grows when warm (~37C).";
+
+ State = ST_SOLID;
+ Properties = TYPE_PART;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = 373.0f;
+ HighTemperatureTransition = PT_DYST;
+
+ Update = &Element_YEST::update;
+
+}
+
+//#TPT-Directive ElementHeader Element_YEST static int update(UPDATE_FUNC_ARGS)
+int Element_YEST::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ for (rx=-2; rx<3; rx++)
+ for (ry=-2; ry<3; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if (!r)
+ continue;
+ if ((r&0xFF)==PT_DYST && 1>(rand()%30) && !sim->legacy_enable)
+ {
+ sim->part_change_type(i,x,y,PT_DYST);
+ }
+ }
+ if (parts[i].temp>303&&parts[i].temp<317) {
+ sim->create_part(-1, x+rand()%3-1, y+rand()%3-1, PT_YEST);
+ }
+ return 0;
+}
+
+
+Element_YEST::~Element_YEST() {} \ No newline at end of file
diff --git a/src/simulation/elements/dcel.cpp b/src/simulation/elements/dcel.cpp
new file mode 100644
index 0000000..07f930b
--- /dev/null
+++ b/src/simulation/elements/dcel.cpp
@@ -0,0 +1,85 @@
+#include "simulation/Elements.h"
+//#TPT-Directive ElementClass Element_DCEL PT_DCEL 138
+Element_DCEL::Element_DCEL()
+{
+ Identifier = "DEFAULT_PT_DCEL";
+ Name = "DCEL";
+ Colour = PIXPACK(0x99CC00);
+ MenuVisible = 1;
+ MenuSection = SC_FORCE;
+ Enabled = 1;
+
+ Advection = 0.0f;
+ AirDrag = 0.00f * CFDS;
+ AirLoss = 0.90f;
+ Loss = 0.00f;
+ Collision = 0.0f;
+ Gravity = 0.0f;
+ Diffusion = 0.00f;
+ HotAir = 0.000f * CFDS;
+ Falldown = 0;
+
+ Flammable = 0;
+ Explosive = 0;
+ Meltable = 0;
+ Hardness = 1;
+
+ Weight = 100;
+
+ Temperature = R_TEMP+0.0f +273.15f;
+ HeatConduct = 251;
+ Description = "Decelerator";
+
+ State = ST_NONE;
+ Properties = TYPE_SOLID;
+
+ LowPressure = IPL;
+ LowPressureTransition = NT;
+ HighPressure = IPH;
+ HighPressureTransition = NT;
+ LowTemperature = ITL;
+ LowTemperatureTransition = NT;
+ HighTemperature = ITH;
+ HighTemperatureTransition = NT;
+
+ Update = &Element_DCEL::update;
+ Graphics = &Element_DCEL::graphics;
+}
+
+//#TPT-Directive ElementHeader Element_DCEL static int update(UPDATE_FUNC_ARGS)
+int Element_DCEL::update(UPDATE_FUNC_ARGS)
+ {
+ int r, rx, ry;
+ parts[i].tmp = 0;
+ for (rx=-1; rx<2; rx++)
+ for (ry=-1; ry<2; ry++)
+ if (x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES && (rx || ry) && !(rx && ry))
+ {
+ r = pmap[y+ry][x+rx];
+ if(!r)
+ r = sim->photons[y+ry][x+rx];
+ if ((r>>8)>=NPART || !r)
+ continue;
+ if(sim->elements[r&0xFF].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS | TYPE_ENERGY))
+ {
+ parts[r>>8].vx *= 0.9f;
+ parts[r>>8].vy *= 0.9f;
+ parts[i].tmp = 1;
+ }
+ }
+ return 0;
+}
+
+
+
+//#TPT-Directive ElementHeader Element_DCEL static int graphics(GRAPHICS_FUNC_ARGS)
+int Element_DCEL::graphics(GRAPHICS_FUNC_ARGS)
+
+{
+ if(cpart->tmp)
+ *pixel_mode |= PMODE_GLOW;
+ return 0;
+}
+
+
+Element_DCEL::~Element_DCEL() {} \ No newline at end of file
diff --git a/src/simulation/tools/AirTool.cpp b/src/simulation/tools/AirTool.cpp
new file mode 100644
index 0000000..9fd7f79
--- /dev/null
+++ b/src/simulation/tools/AirTool.cpp
@@ -0,0 +1,22 @@
+#include "simulation/Tools.h"
+#include "simulation/Air.h"
+//#TPT-Directive ToolClass Tool_Air TOOL_AIR 3
+Tool_Air::Tool_Air()
+{
+ Identifier = "DEFAULT_TOOL_AIR";
+ Name = "AIR";
+ Colour = PIXPACK(0xFFFFFF);
+ Description = "Creates air pressure";
+}
+
+int Tool_Air::Perform(Simulation * sim, Particle * cpart, int x, int y, float strength)
+{
+ sim->air->pv[y/CELL][x/CELL] += 0.03f*strength;
+ if(sim->air->pv[y/CELL][x/CELL] > 256.0f)
+ sim->air->pv[y/CELL][x/CELL] = 256.0f;
+ if(sim->air->pv[y/CELL][x/CELL] < -256.0f)
+ sim->air->pv[y/CELL][x/CELL] = -256.0f;
+ return 1;
+}
+
+Tool_Air::~Tool_Air() {} \ No newline at end of file
diff --git a/src/simulation/tools/Cool.cpp b/src/simulation/tools/Cool.cpp
new file mode 100644
index 0000000..b1b57b1
--- /dev/null
+++ b/src/simulation/tools/Cool.cpp
@@ -0,0 +1,23 @@
+#include "simulation/Tools.h"
+//#TPT-Directive ToolClass Tool_Cool TOOL_COOL 1
+Tool_Cool::Tool_Cool()
+{
+ Identifier = "DEFAULT_TOOL_COOL";
+ Name = "COOL";
+ Colour = PIXPACK(0x00DDFF);
+ Description = "Cools particles";
+}
+
+int Tool_Cool::Perform(Simulation * sim, Particle * cpart, int x, int y, float strength)
+{
+ if(!cpart)
+ return 0;
+ cpart->temp -= strength;
+ if(cpart->temp > MAX_TEMP)
+ cpart->temp = MAX_TEMP;
+ if(cpart->temp < 0)
+ cpart->temp = 0;
+ return 1;
+}
+
+Tool_Cool::~Tool_Cool() {} \ No newline at end of file
diff --git a/src/simulation/tools/GravTool.cpp b/src/simulation/tools/GravTool.cpp
new file mode 100644
index 0000000..630ba12
--- /dev/null
+++ b/src/simulation/tools/GravTool.cpp
@@ -0,0 +1,18 @@
+#include "simulation/Tools.h"
+#include "simulation/Simulation.h"
+//#TPT-Directive ToolClass Tool_Grav TOOL_GRAV 4
+Tool_Grav::Tool_Grav()
+{
+ Identifier = "DEFAULT_TOOL_GRAV";
+ Name = "GRAV";
+ Colour = PIXPACK(0xCCCCFF);
+ Description = "Creates a short-lasting gravity well";
+}
+
+int Tool_Grav::Perform(Simulation * sim, Particle * cpart, int x, int y, float strength)
+{
+ sim->gravmap[((y/CELL)*(XRES/CELL))+(x/CELL)] += 0.03f*strength;
+ return 1;
+}
+
+Tool_Grav::~Tool_Grav() {} \ No newline at end of file
diff --git a/src/simulation/tools/Heat.cpp b/src/simulation/tools/Heat.cpp
new file mode 100644
index 0000000..f28274c
--- /dev/null
+++ b/src/simulation/tools/Heat.cpp
@@ -0,0 +1,23 @@
+#include "simulation/Tools.h"
+//#TPT-Directive ToolClass Tool_Heat TOOL_HEAT 0
+Tool_Heat::Tool_Heat()
+{
+ Identifier = "DEFAULT_TOOL_HEAT";
+ Name = "HEAT";
+ Colour = PIXPACK(0xFFDD00);
+ Description = "Heats particles";
+}
+
+int Tool_Heat::Perform(Simulation * sim, Particle * cpart, int x, int y, float strength)
+{
+ if(!cpart)
+ return 0;
+ cpart->temp += strength;
+ if(cpart->temp > MAX_TEMP)
+ cpart->temp = MAX_TEMP;
+ if(cpart->temp < 0)
+ cpart->temp = 0;
+ return 1;
+}
+
+Tool_Heat::~Tool_Heat() {} \ No newline at end of file
diff --git a/src/simulation/tools/NGrv.cpp b/src/simulation/tools/NGrv.cpp
new file mode 100644
index 0000000..525d697
--- /dev/null
+++ b/src/simulation/tools/NGrv.cpp
@@ -0,0 +1,18 @@
+#include "simulation/Tools.h"
+#include "simulation/Simulation.h"
+//#TPT-Directive ToolClass Tool_NGrv TOOL_NGRV 5
+Tool_NGrv::Tool_NGrv()
+{
+ Identifier = "DEFAULT_TOOL_NGRV";
+ Name = "NGRV";
+ Colour = PIXPACK(0xAACCFF);
+ Description = "Creates a short-lasting negative gravity well";
+}
+
+int Tool_NGrv::Perform(Simulation * sim, Particle * cpart, int x, int y, float strength)
+{
+ sim->gravmap[((y/CELL)*(XRES/CELL))+(x/CELL)] -= 0.03f*strength;
+ return 1;
+}
+
+Tool_NGrv::~Tool_NGrv() {} \ No newline at end of file
diff --git a/src/simulation/tools/SimTool.cpp b/src/simulation/tools/SimTool.cpp
new file mode 100644
index 0000000..d7015fa
--- /dev/null
+++ b/src/simulation/tools/SimTool.cpp
@@ -0,0 +1,10 @@
+#include "simulation/Element.h"
+#include "simulation/Tools.h"
+
+SimTool::SimTool():
+Identifier("DEFAULT_TOOL_INVALID"),
+Name(""),
+Colour(PIXPACK(0xFFFFFF)),
+Description("NULL Tool, does NOTHING")
+{
+} \ No newline at end of file
diff --git a/src/simulation/tools/SimTool.h b/src/simulation/tools/SimTool.h
new file mode 100644
index 0000000..c32ba5b
--- /dev/null
+++ b/src/simulation/tools/SimTool.h
@@ -0,0 +1,23 @@
+#ifndef SIMTOOL_H
+#define SIMTOOL_H
+
+#include "simulation/Simulation.h"
+#include "graphics/Renderer.h"
+#include "simulation/Elements.h"
+
+class Simulation;
+struct Particle;
+class SimTool
+{
+public:
+ char *Identifier;
+ char *Name;
+ pixel Colour;
+ char *Description;
+
+ SimTool();
+ virtual ~SimTool() {}
+ virtual int Perform(Simulation * sim, Particle * cpart, int x, int y, float strength) { return 0; }
+};
+
+#endif \ No newline at end of file
diff --git a/src/simulation/tools/Vac.cpp b/src/simulation/tools/Vac.cpp
new file mode 100644
index 0000000..aa319e2
--- /dev/null
+++ b/src/simulation/tools/Vac.cpp
@@ -0,0 +1,22 @@
+#include "simulation/Tools.h"
+#include "simulation/Air.h"
+//#TPT-Directive ToolClass Tool_Vac TOOL_VAC 2
+Tool_Vac::Tool_Vac()
+{
+ Identifier = "DEFAULT_TOOL_VAC";
+ Name = "VAC";
+ Colour = PIXPACK(0x303030);
+ Description = "Removes air pressure";
+}
+
+int Tool_Vac::Perform(Simulation * sim, Particle * cpart, int x, int y, float strength)
+{
+ sim->air->pv[y/CELL][x/CELL] -= 0.03f*strength;
+ if(sim->air->pv[y/CELL][x/CELL] > 256.0f)
+ sim->air->pv[y/CELL][x/CELL] = 256.0f;
+ if(sim->air->pv[y/CELL][x/CELL] < -256.0f)
+ sim->air->pv[y/CELL][x/CELL] = -256.0f;
+ return 1;
+}
+
+Tool_Vac::~Tool_Vac() {}
diff --git a/src/tags/TagsController.cpp b/src/tags/TagsController.cpp
new file mode 100644
index 0000000..cf373e6
--- /dev/null
+++ b/src/tags/TagsController.cpp
@@ -0,0 +1,60 @@
+/*
+ * TagsController.cpp
+ *
+ * Created on: Mar 5, 2012
+ * Author: Simon
+ */
+
+#include "TagsController.h"
+#include "interface/Engine.h"
+
+#include "TagsModel.h"
+#include "TagsView.h"
+
+TagsController::TagsController(ControllerCallback * callback, SaveInfo * save):
+ HasDone(false)
+{
+ tagsModel = new TagsModel();
+ tagsView = new TagsView();
+ tagsView->AttachController(this);
+ tagsModel->AddObserver(tagsView);
+
+ tagsModel->SetSave(save);
+
+ this->callback = callback;
+}
+
+SaveInfo * TagsController::GetSave()
+{
+ return tagsModel->GetSave();
+}
+
+void TagsController::RemoveTag(std::string tag)
+{
+ tagsModel->RemoveTag(tag);
+}
+
+
+void TagsController::AddTag(std::string tag)
+{
+ tagsModel->AddTag(tag);
+}
+
+void TagsController::Exit()
+{
+ if(ui::Engine::Ref().GetWindow() == tagsView)
+ ui::Engine::Ref().CloseWindow();
+ if(callback)
+ callback->ControllerExit();
+ HasDone = true;
+}
+
+TagsController::~TagsController() {
+ if(ui::Engine::Ref().GetWindow() == tagsView)
+ ui::Engine::Ref().CloseWindow();
+ delete tagsModel;
+ delete tagsView;
+ if(callback)
+ delete callback;
+}
+
diff --git a/src/tags/TagsController.h b/src/tags/TagsController.h
new file mode 100644
index 0000000..ee68a03
--- /dev/null
+++ b/src/tags/TagsController.h
@@ -0,0 +1,32 @@
+/*
+ * TagsController.h
+ *
+ * Created on: Mar 5, 2012
+ * Author: Simon
+ */
+
+#ifndef TAGSCONTROLLER_H_
+#define TAGSCONTROLLER_H_
+
+#include "Controller.h"
+#include "TagsView.h"
+#include "client/SaveInfo.h"
+
+class TagsView;
+class TagsModel;
+class TagsController {
+ ControllerCallback * callback;
+ TagsView * tagsView;
+ TagsModel * tagsModel;
+public:
+ bool HasDone;
+ TagsController(ControllerCallback * callback, SaveInfo * save);
+ TagsView * GetView() {return tagsView;}
+ SaveInfo * GetSave();
+ void RemoveTag(std::string tag);
+ void AddTag(std::string tag);
+ void Exit();
+ virtual ~TagsController();
+};
+
+#endif /* TAGSCONTROLLER_H_ */
diff --git a/src/tags/TagsModel.cpp b/src/tags/TagsModel.cpp
new file mode 100644
index 0000000..b4b3ff9
--- /dev/null
+++ b/src/tags/TagsModel.cpp
@@ -0,0 +1,85 @@
+/*
+ * TagsModel.cpp
+ *
+ * Created on: Mar 5, 2012
+ * Author: Simon
+ */
+
+#include "TagsModel.h"
+#include "TagsView.h"
+#include "client/Client.h"
+#include "TagsModelException.h"
+#include "client/SaveInfo.h"
+
+TagsModel::TagsModel():
+ save(NULL)
+{
+ // TODO Auto-generated constructor stub
+
+}
+
+void TagsModel::SetSave(SaveInfo * save)
+{
+ this->save = save;
+ notifyTagsChanged();
+}
+
+SaveInfo * TagsModel::GetSave()
+{
+ return save;
+}
+
+void TagsModel::RemoveTag(std::string tag)
+{
+ if(save)
+ {
+ std::vector<std::string> * tags = Client::Ref().RemoveTag(save->GetID(), tag);
+ if(tags)
+ {
+ save->SetTags(std::vector<std::string>(*tags));
+ notifyTagsChanged();
+ delete tags;
+ }
+ else
+ {
+ throw TagsModelException(Client::Ref().GetLastError());
+ }
+ }
+}
+
+void TagsModel::AddTag(std::string tag)
+{
+ if(save)
+ {
+ std::vector<std::string> * tags = Client::Ref().AddTag(save->GetID(), tag);
+ if(tags)
+ {
+ save->SetTags(std::vector<std::string>(*tags));
+ notifyTagsChanged();
+ delete tags;
+ }
+ else
+ {
+ throw TagsModelException(Client::Ref().GetLastError());
+ }
+ }
+}
+
+void TagsModel::AddObserver(TagsView * observer)
+{
+ observers.push_back(observer);
+ observer->NotifyTagsChanged(this);
+}
+
+void TagsModel::notifyTagsChanged()
+{
+ for(int i = 0; i < observers.size(); i++)
+ {
+ observers[i]->NotifyTagsChanged(this);
+ }
+}
+
+TagsModel::~TagsModel() {
+ // TODO Auto-generated destructor stub
+}
+
diff --git a/src/tags/TagsModel.h b/src/tags/TagsModel.h
new file mode 100644
index 0000000..e1c3b32
--- /dev/null
+++ b/src/tags/TagsModel.h
@@ -0,0 +1,31 @@
+/*
+ * TagsModel.h
+ *
+ * Created on: Mar 5, 2012
+ * Author: Simon
+ */
+
+#ifndef TAGSMODEL_H_
+#define TAGSMODEL_H_
+
+#include <vector>
+#include <string>
+
+class SaveInfo;
+
+class TagsView;
+class TagsModel {
+ SaveInfo * save;
+ std::vector<TagsView*> observers;
+ void notifyTagsChanged();
+public:
+ TagsModel();
+ void AddObserver(TagsView * observer);
+ void SetSave(SaveInfo * save);
+ void RemoveTag(std::string tag);
+ void AddTag(std::string tag);
+ SaveInfo * GetSave();
+ virtual ~TagsModel();
+};
+
+#endif /* TAGSMODEL_H_ */
diff --git a/src/tags/TagsModelException.h b/src/tags/TagsModelException.h
new file mode 100644
index 0000000..10aaa31
--- /dev/null
+++ b/src/tags/TagsModelException.h
@@ -0,0 +1,22 @@
+/*
+ * TagsModelException.h
+ *
+ * Created on: Mar 29, 2012
+ * Author: Simon
+ */
+
+#ifndef TAGSMODELEXCEPTION_H_
+#define TAGSMODELEXCEPTION_H_
+
+#include <string>
+#include <exception>
+
+class TagsModelException {
+ std::string message;
+public:
+ TagsModelException(std::string message_): message(message_) {};
+ const char * what() const throw() { return message.c_str(); };
+ ~TagsModelException() throw() {};
+};
+
+#endif /* TAGSMODELEXCEPTION_H_ */
diff --git a/src/tags/TagsView.cpp b/src/tags/TagsView.cpp
new file mode 100644
index 0000000..f5aef91
--- /dev/null
+++ b/src/tags/TagsView.cpp
@@ -0,0 +1,168 @@
+/*
+ * TagsView.cpp
+ *
+ * Created on: Mar 5, 2012
+ * Author: Simon
+ */
+
+#include "client/Client.h"
+#include "TagsView.h"
+
+#include "dialogues/ErrorMessage.h"
+#include "TagsController.h"
+#include "TagsModel.h"
+#include "TagsModelException.h"
+
+#include "interface/Button.h"
+#include "interface/Textbox.h"
+#include "interface/Label.h"
+#include "interface/Keys.h"
+
+TagsView::TagsView():
+ ui::Window(ui::Point(-1, -1), ui::Point(195, 250))
+{
+
+ class CloseAction : public ui::ButtonAction
+ {
+ TagsView * v;
+ public:
+ CloseAction(TagsView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->c->Exit();
+ }
+ };
+ closeButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(195, 16), "Close");
+ closeButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ closeButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ closeButton->SetActionCallback(new CloseAction(this));
+ AddComponent(closeButton);
+
+
+ tagInput = new ui::Textbox(ui::Point(8, Size.Y-40), ui::Point(Size.X-60, 16), "", "[new tag]");
+ tagInput->Appearance.icon = IconTag;
+ tagInput->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ tagInput->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(tagInput);
+
+ class AddTagAction : public ui::ButtonAction
+ {
+ TagsView * v;
+ public:
+ AddTagAction(TagsView * _v) { v = _v; }
+ void ActionCallback(ui::Button * sender)
+ {
+ v->addTag();
+ }
+ };
+ addButton = new ui::Button(ui::Point(tagInput->Position.X+tagInput->Size.X+4, tagInput->Position.Y), ui::Point(40, 16), "Add");
+ addButton->Appearance.icon = IconAdd;
+ addButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ addButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ addButton->SetActionCallback(new AddTagAction(this));
+ AddComponent(addButton);
+
+ title = new ui::Label(ui::Point(5, 5), ui::Point(185, 16), "Manage tags:");
+ title->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ title->Appearance.VerticalAlign = ui::Appearance::AlignTop;
+ AddComponent(title);
+}
+
+void TagsView::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
+}
+
+void TagsView::NotifyTagsChanged(TagsModel * sender)
+{
+ for(int i = 0; i < tags.size(); i++)
+ {
+ RemoveComponent(tags[i]);
+ delete tags[i];
+ }
+ tags.clear();
+
+
+ class DeleteTagAction : public ui::ButtonAction
+ {
+ TagsView * v;
+ std::string tag;
+ public:
+ DeleteTagAction(TagsView * _v, std::string tag) { v = _v; this->tag = tag; }
+ void ActionCallback(ui::Button * sender)
+ {
+ try
+ {
+ v->c->RemoveTag(tag);
+ }
+ catch(TagsModelException & ex)
+ {
+ new ErrorMessage("Could not remove tag", ex.what());
+ }
+ }
+ };
+
+ if(sender->GetSave())
+ {
+ for(int i = 0; i < sender->GetSave()->GetTags().size(); i++)
+ {
+ ui::Label * tempLabel = new ui::Label(ui::Point(35, 35+(16*i)), ui::Point(120, 16), sender->GetSave()->GetTags()[i]);
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ tags.push_back(tempLabel);
+ AddComponent(tempLabel);
+
+ if(sender->GetSave()->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator)
+ {
+ ui::Button * tempButton = new ui::Button(ui::Point(15, 37+(16*i)), ui::Point(11, 12));
+ tempButton->Appearance.icon = IconDelete;
+ tempButton->Appearance.Border = ui::Border(0);
+ tempButton->Appearance.Margin.Top += 2;
+ tempButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
+ tempButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ tempButton->SetActionCallback(new DeleteTagAction(this, sender->GetSave()->GetTags()[i]));
+ tags.push_back(tempButton);
+ AddComponent(tempButton);
+ }
+ }
+ }
+}
+
+void TagsView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
+{
+ switch(key)
+ {
+ /*case KEY_TAB:
+ if(IsFocused(usernameField))
+ FocusComponent(passwordField);
+ else
+ FocusComponent(usernameField);
+ break;*/
+ case KEY_ENTER:
+ case KEY_RETURN:
+ if(IsFocused(tagInput))
+ {
+ addTag();
+ }
+ break;
+ }
+}
+
+void TagsView::addTag()
+{
+ try
+ {
+ c->AddTag(tagInput->GetText());
+ }
+ catch(TagsModelException & ex)
+ {
+ new ErrorMessage("Could not add tag", ex.what());
+ }
+ tagInput->SetText("");
+}
+
+TagsView::~TagsView() {
+ // TODO Auto-generated destructor stub
+}
+
diff --git a/src/tags/TagsView.h b/src/tags/TagsView.h
new file mode 100644
index 0000000..b2dabc0
--- /dev/null
+++ b/src/tags/TagsView.h
@@ -0,0 +1,40 @@
+/*
+ * TagsView.h
+ *
+ * Created on: Mar 5, 2012
+ * Author: Simon
+ */
+
+#ifndef TAGSVIEW_H_
+#define TAGSVIEW_H_
+
+#include <vector>
+#include "interface/Window.h"
+
+namespace ui
+{
+ class Button;
+ class Textbox;
+ class Label;
+}
+
+class TagsController;
+class TagsModel;
+class TagsView: public ui::Window {
+ TagsController * c;
+ ui::Button * addButton;
+ ui::Button * closeButton;
+ ui::Label * title;
+ ui::Textbox * tagInput;
+ std::vector<ui::Component*> tags;
+ void addTag();
+public:
+ TagsView();
+ virtual void OnDraw();
+ void AttachController(TagsController * c_) { c = c_; };
+ virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
+ void NotifyTagsChanged(TagsModel * sender);
+ virtual ~TagsView();
+};
+
+#endif /* TAGSVIEW_H_ */
diff --git a/src/tasks/Task.cpp b/src/tasks/Task.cpp
new file mode 100644
index 0000000..d29fe04
--- /dev/null
+++ b/src/tasks/Task.cpp
@@ -0,0 +1,185 @@
+/*
+ * Task.cpp
+ *
+ * Created on: Apr 6, 2012
+ * Author: Simon
+ */
+
+#include "Task.h"
+#include "TaskListener.h"
+
+void Task::AddTaskListener(TaskListener * listener)
+{
+ this->listener = listener;
+ notifyProgressMain();
+ notifyStatusMain();
+}
+
+void Task::Start()
+{
+ thDone = false;
+ done = false;
+ progress = 0;
+ status = "";
+ //taskMutex = PTHREAD_MUTEX_INITIALIZER;
+ before();
+ pthread_mutex_init (&taskMutex, NULL);
+ pthread_create(&doWorkThread, 0, &Task::doWork_helper, this);
+}
+
+int Task::GetProgress()
+{
+ return progress;
+}
+
+std::string Task::GetStatus()
+{
+ return status;
+}
+
+std::string Task::GetError()
+{
+ return error;
+}
+
+bool Task::GetDone()
+{
+ return done;
+}
+
+bool Task::GetSuccess()
+{
+ return success;
+}
+
+void Task::Poll()
+{
+ if(!done)
+ {
+ int newProgress;
+ bool newDone = false;
+ bool newSuccess = false;
+ std::string newStatus;
+ std::string newError;
+ pthread_mutex_lock(&taskMutex);
+ newProgress = thProgress;
+ newDone = thDone;
+ newSuccess = thSuccess;
+ newStatus = std::string(thStatus);
+ newError = std::string(thError);
+ pthread_mutex_unlock(&taskMutex);
+
+ success = newSuccess;
+
+ if(newProgress!=progress) {
+ progress = newProgress;
+ notifyProgressMain();
+ }
+
+ if(newError!=error) {
+ error = std::string(newError);
+ notifyErrorMain();
+ }
+
+ if(newStatus!=status) {
+ status = std::string(newStatus);
+ notifyStatusMain();
+ }
+
+ if(newDone!=done)
+ {
+ done = newDone;
+
+ pthread_join(doWorkThread, NULL);
+ pthread_mutex_destroy(&taskMutex);
+
+ after();
+
+ notifyDoneMain();
+ }
+ }
+}
+
+Task::~Task()
+{
+ if(!done)
+ {
+ pthread_join(doWorkThread, NULL);
+ pthread_mutex_destroy(&taskMutex);
+ }
+}
+
+void Task::before()
+{
+
+}
+
+bool Task::doWork()
+{
+ notifyStatus("Fake progress");
+ for(int i = 0; i < 100; i++)
+ {
+ notifyProgress(i);
+ }
+ return true;
+}
+
+void Task::after()
+{
+
+}
+
+void * Task::doWork_helper(void * ref)
+{
+ bool newSuccess = ((Task*)ref)->doWork();
+ pthread_mutex_lock(&((Task*)ref)->taskMutex);
+ ((Task*)ref)->thSuccess = newSuccess;
+ ((Task*)ref)->thDone = true;
+ pthread_mutex_unlock(&((Task*)ref)->taskMutex);
+ return NULL;
+}
+
+void Task::notifyProgress(int progress)
+{
+ pthread_mutex_lock(&taskMutex);
+ thProgress = progress;
+ pthread_mutex_unlock(&taskMutex);
+}
+
+void Task::notifyStatus(std::string status)
+{
+ pthread_mutex_lock(&taskMutex);
+ thStatus = std::string(status);
+ pthread_mutex_unlock(&taskMutex);
+}
+
+void Task::notifyError(std::string error)
+{
+ pthread_mutex_lock(&taskMutex);
+ thError = std::string(error);
+ pthread_mutex_unlock(&taskMutex);
+}
+
+void Task::notifyProgressMain()
+{
+ if(listener)
+ listener->NotifyProgress(this);
+}
+
+void Task::notifyStatusMain()
+{
+ if(listener)
+ listener->NotifyStatus(this);
+}
+
+void Task::notifyDoneMain()
+{
+ if(listener)
+ listener->NotifyDone(this);
+}
+
+void Task::notifyErrorMain()
+{
+ if(listener)
+ listener->NotifyError(this);
+}
diff --git a/src/tasks/Task.h b/src/tasks/Task.h
new file mode 100644
index 0000000..a025ac1
--- /dev/null
+++ b/src/tasks/Task.h
@@ -0,0 +1,63 @@
+/*
+ * Task.h
+ *
+ * Created on: Apr 6, 2012
+ * Author: Simon
+ */
+
+#ifndef TASK_H_
+#define TASK_H_
+
+#include <string>
+#include <pthread.h>
+#undef GetUserName //God dammit microsoft!
+#include "TaskListener.h"
+
+class TaskListener;
+class Task {
+public:
+ void AddTaskListener(TaskListener * listener);
+ void Start();
+ int GetProgress();
+ bool GetDone();
+ bool GetSuccess();
+ std::string GetError();
+ std::string GetStatus();
+ void Poll();
+ Task() : listener(NULL) { progress = 0; }
+ virtual ~Task();
+protected:
+ int progress;
+ bool done;
+ bool success;
+ std::string status;
+ std::string error;
+
+ int thProgress;
+ bool thDone;
+ bool thSuccess;
+ std::string thStatus;
+ std::string thError;
+
+ TaskListener * listener;
+ pthread_t doWorkThread;
+ pthread_mutex_t taskMutex;
+ pthread_cond_t taskCond;
+
+
+ virtual void before();
+ virtual void after();
+ virtual bool doWork();
+ static void * doWork_helper(void * ref);
+
+ virtual void notifyProgress(int progress);
+ virtual void notifyError(std::string error);
+ virtual void notifyStatus(std::string status);
+
+ virtual void notifyProgressMain();
+ virtual void notifyErrorMain();
+ virtual void notifyStatusMain();
+ virtual void notifyDoneMain();
+};
+
+#endif /* TASK_H_ */
diff --git a/src/tasks/TaskListener.h b/src/tasks/TaskListener.h
new file mode 100644
index 0000000..7405c03
--- /dev/null
+++ b/src/tasks/TaskListener.h
@@ -0,0 +1,21 @@
+/*
+ * TaskListener.h
+ *
+ * Created on: Apr 6, 2012
+ * Author: Simon
+ */
+
+#ifndef TASKLISTENER_H_
+#define TASKLISTENER_H_
+
+class Task;
+class TaskListener {
+public:
+ virtual void NotifyDone(Task * task) {}
+ virtual void NotifyError(Task * task) {}
+ virtual void NotifyProgress(Task * task) {}
+ virtual void NotifyStatus(Task * task) {}
+ virtual ~TaskListener() {}
+};
+
+#endif /* TASK_H_ */
diff --git a/src/tasks/TaskWindow.cpp b/src/tasks/TaskWindow.cpp
new file mode 100644
index 0000000..b3055d1
--- /dev/null
+++ b/src/tasks/TaskWindow.cpp
@@ -0,0 +1,133 @@
+/*
+ * TaskWindow.cpp
+ *
+ * Created on: Apr 6, 2012
+ * Author: Simon
+ */
+
+#include <sstream>
+#include "interface/Label.h"
+#include "TaskWindow.h"
+#include "dialogues/ErrorMessage.h"
+#include "Style.h"
+#include "Task.h"
+
+TaskWindow::TaskWindow(std::string title_, Task * task_, bool closeOnDone):
+ task(task_),
+ title(title_),
+ ui::Window(ui::Point(-1, -1), ui::Point(240, 60)),
+ progress(0),
+ done(false),
+ closeOnDone(closeOnDone),
+ progressStatus("0%")
+{
+
+ ui::Label * tempLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 15), title);
+ tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ tempLabel->SetTextColour(style::Colour::WarningTitle);
+ AddComponent(tempLabel);
+
+ statusLabel = new ui::Label(ui::Point(4, 23), ui::Point(Size.X-8, 15), "");
+ statusLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
+ statusLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
+ AddComponent(statusLabel);
+
+ ui::Engine::Ref().ShowWindow(this);
+
+ task->AddTaskListener(this);
+ task->Start();
+}
+
+void TaskWindow::NotifyStatus(Task * task)
+{
+ statusLabel->SetText(task->GetStatus());
+}
+
+void TaskWindow::NotifyError(Task * task)
+{
+ new ErrorMessage("Error", task->GetError());
+}
+
+void TaskWindow::NotifyDone(Task * task)
+{
+ if(closeOnDone)
+ Exit();
+}
+
+void TaskWindow::Exit()
+{
+ if(ui::Engine::Ref().GetWindow()==this)
+ {
+ ui::Engine::Ref().CloseWindow();
+ SelfDestruct();
+ }
+}
+
+void TaskWindow::NotifyProgress(Task * task)
+{
+ progress = task->GetProgress();
+ std::stringstream pStream;
+ if(progress>-1)
+ {
+ pStream << progress << "%";
+ }
+ else
+ {
+ pStream << "Please wait...";
+ }
+ progressStatus = pStream.str();
+}
+
+void TaskWindow::OnTick(float dt)
+{
+ intermediatePos += 1.0f*dt;
+ if(intermediatePos>100.0f)
+ intermediatePos = 0.0f;
+ task->Poll();
+}
+
+void TaskWindow::OnDraw()
+{
+ Graphics * g = ui::Engine::Ref().g;
+ g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
+ g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
+
+ g->draw_line(Position.X, Position.Y + Size.Y-17, Position.X + Size.X - 1, Position.Y + Size.Y-17, 255, 255, 255, 255);
+
+ ui::Colour progressBarColour = style::Colour::WarningTitle;
+
+ if(progress!=-1)
+ {
+ if(progress > 0)
+ {
+ if(progress > 100)
+ progress = 100;
+ float size = float(Size.X-4)*(float(progress)/100.0f); // TIL...
+ size = std::min(std::max(size, 0.0f), float(Size.X-4));
+ g->fillrect(Position.X + 2, Position.Y + Size.Y-15, size, 13, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255);
+ }
+ } else {
+ int size = 40, rsize = 0;
+ float position = float(Size.X-4)*(intermediatePos/100.0f);
+ if(position + size - 1 > Size.X-4)
+ {
+ size = (Size.X-4)-position+1;
+ rsize = 40-size;
+ }
+ g->fillrect(Position.X + 2 + position, Position.Y + Size.Y-15, size, 13, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255);
+ if(rsize)
+ {
+ g->fillrect(Position.X + 2, Position.Y + Size.Y-15, rsize, 13, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255);
+ }
+ }
+ if(progress<50)
+ g->drawtext(Position.X + ((Size.X-Graphics::textwidth(progressStatus.c_str()))/2), Position.Y + Size.Y-13, progressStatus, 255, 255, 255, 255);
+ else
+ g->drawtext(Position.X + ((Size.X-Graphics::textwidth(progressStatus.c_str()))/2), Position.Y + Size.Y-13, progressStatus, 0, 0, 0, 255);
+}
+
+TaskWindow::~TaskWindow() {
+ delete task;
+}
+
diff --git a/src/tasks/TaskWindow.h b/src/tasks/TaskWindow.h
new file mode 100644
index 0000000..820265c
--- /dev/null
+++ b/src/tasks/TaskWindow.h
@@ -0,0 +1,38 @@
+/*
+ * TaskWindow.h
+ *
+ * Created on: Apr 6, 2012
+ * Author: Simon
+ */
+
+#ifndef TASKWINDOW_H_
+#define TASKWINDOW_H_
+
+#include <string>
+#include "interface/Label.h"
+#include "interface/Window.h"
+#include "tasks/TaskListener.h"
+
+class Task;
+class TaskWindow: public ui::Window, public TaskListener {
+ Task * task;
+ std::string title;
+ int progress;
+ float intermediatePos;
+ bool done;
+ bool closeOnDone;
+ ui::Label * statusLabel;
+ std::string progressStatus;
+public:
+ TaskWindow(std::string title_, Task * task_, bool closeOnDone = true);
+ virtual void NotifyStatus(Task * task);
+ virtual void NotifyDone(Task * task);
+ virtual void NotifyProgress(Task * task);
+ virtual void NotifyError(Task * task);
+ virtual void OnTick(float dt);
+ virtual void OnDraw();
+ virtual void Exit();
+ virtual ~TaskWindow();
+};
+
+#endif /* TASKWINDOW_H_ */
diff --git a/src/tests/PowderInteractionMachine.cpp b/src/tests/PowderInteractionMachine.cpp
new file mode 100644
index 0000000..c2b37a2
--- /dev/null
+++ b/src/tests/PowderInteractionMachine.cpp
@@ -0,0 +1,21 @@
+#ifdef TEST
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include "pim/Parser.h"
+
+int main(int argc, char * argv[])
+{
+ std::ifstream file("test.p");
+
+ std::stringstream buffer;
+
+ buffer << file.rdbuf();
+ file.close();
+
+ pim::compiler::Parser * parser = new pim::compiler::Parser(buffer);
+
+ parser->Compile();
+}
+#endif \ No newline at end of file
diff --git a/src/tests/VirtualMachineTest.cpp b/src/tests/VirtualMachineTest.cpp
new file mode 100644
index 0000000..ee92999
--- /dev/null
+++ b/src/tests/VirtualMachineTest.cpp
@@ -0,0 +1,16 @@
+#ifdef TEST
+
+#include <iostream>
+#include "virtualmachine/VirtualMachine.h"
+
+int main(int argc, char * argv[])
+{
+ vm::VirtualMachine * vm = new vm::VirtualMachine(2);
+ vm->LoadProgram("test.qvm");
+ while(true)
+ {
+ vm->Call(0/*, 0, 88, 12*/);
+ std::cout << "Return value: " << vm->Pop<vm::uint4_t>() << std::endl;
+ }
+}
+#endif \ No newline at end of file
diff --git a/src/tests/test.c b/src/tests/test.c
new file mode 100644
index 0000000..06b9e95
--- /dev/null
+++ b/src/tests/test.c
@@ -0,0 +1,171 @@
+int g;
+
+int trapp(int a, int b);
+float compl(float* a, unsigned int n);
+void spam();
+
+int main()
+{
+ int l, r;
+ int a, b, m, d, s, as;
+ int b1, b2, b3;
+ float f, dd;
+ int i;
+ int k, j, o;
+ unsigned int ua, ub, um, ud, us, uas;
+ int re;
+ float arr[] = { 1.6f, -3.0f, 1.41f, -0.01f };
+ float* point;
+ float res;
+
+ trap_Print("System calls work\n");
+
+ if (!1)
+ return -1;
+
+ trap_Print("Negation works\n");
+
+ if (!(9 == 9 && 6!=4 && 100>10 && 34<85))
+ return -1;
+
+ trap_Print("Basic comparisons work\n");
+
+ b1 = 9 & 3;
+ b2 = 7 | 8;
+ b3 = 10 ^ 3;
+
+ if (!(b1 == 1 && b2 == 15 && b3 == 9))
+ return -1;
+
+ trap_Print("Boolean functions work\n");
+
+ l = 2;
+ l = l<<2;
+
+ r = 9;
+ r = r>>2;
+
+ if (!(l == 8 && r == 2))
+ return -1;
+
+ trap_Print("Bit shifts work\n");
+
+ a = 6;
+ b = 2*2;
+
+ m = a*b;
+ d = a/b;
+ s = a+b;
+ as = a-b;
+
+ if (!(m == 24 && d == 1 && s == 10 && as == 2))
+ return -1;
+
+ trap_Print("Arithmetics works\n");
+
+ f = 5.1f;
+ dd = 3.1415f;
+
+ if (!((f*dd)>15.0f && (f*dd)<17.0f && (f-dd)<2.0f && (f-dd)>1.0f))
+ return -1;
+
+ trap_Print("Floating point arithmetics works\n");
+
+ i = f*dd;
+
+ if (!(i==16))
+ return -1;
+
+ trap_Print("Float to Int works\n");
+
+ f = i+1;
+
+ if (!(f>16.0f && f<18.0f))
+ return -1;
+
+ trap_Print("Int to Float works\n");
+
+ j = 0; o = 0;
+ for(k=0; k<5; k++)
+ j++;
+
+ for(k=5; k>0; k--)
+ o++;
+
+ if (!(o == 5 && j == 5))
+ return -1;
+
+ trap_Print("Cycles work\n");
+
+ g = 11;
+ g++;
+
+ if (!(g == 12))
+ return -1;
+
+ trap_Print("Global variables work\n");
+
+ ua = 6;
+ ub = 2*2;
+
+ um = ua*ub;
+ ud = ua/ub;
+ us = ua+ub;
+ uas = ua-ub;
+
+ if (!(um == 24 && ud == 1 && us == 10 && uas == 2))
+ return -1;
+
+ trap_Print("Unsigned ints work\n");
+
+ point = &arr[0];
+ point += 2;
+ if (!(*point == 1.41f))
+ return -1;
+
+ trap_Print("Pointer arithmetics works\n");
+
+ re = trapp(12, 4);
+
+ if (!(re == 8))
+ return -1;
+
+ trap_Print("Function calls work\n");
+
+ res = compl(arr, 4);
+
+ if (!(res<0.01f && res>(-0.01f)))
+ return -1;
+
+ trap_Print("Complex function call works\n");
+
+ for(i=0; i<1000000; i++)
+ spam();
+
+ trap_Print("Stack spam check passed\n");
+
+ trap_Print("All checks are passed!\n");
+
+ return 10;
+}
+
+int trapp(int a, int b)
+{
+ int c;
+ c = (a+b)/2;
+ return c;
+}
+
+float compl(float* a, unsigned int n)
+{
+ int s, i;
+ for(i=0; i<n; i++)
+ s+=a[i];
+ return s;
+}
+
+void spam()
+{
+ return;
+}
+
diff --git a/src/tests/test.qvm b/src/tests/test.qvm
new file mode 100644
index 0000000..22b99b2
--- /dev/null
+++ b/src/tests/test.qvm
Binary files differ
diff --git a/src/update/UpdateActivity.cpp b/src/update/UpdateActivity.cpp
new file mode 100644
index 0000000..0f22df7
--- /dev/null
+++ b/src/update/UpdateActivity.cpp
@@ -0,0 +1,172 @@
+/*
+ * UpdateActivity.cpp
+ *
+ * Created on: Jun 20, 2012
+ * Author: Simon
+ */
+
+#include <bzlib.h>
+#include <sstream>
+#include "dialogues/ConfirmPrompt.h"
+#include "interface/Engine.h"
+#include "UpdateActivity.h"
+#include "tasks/Task.h"
+#include "client/HTTP.h"
+#include "client/Client.h"
+#include "Update.h"
+#include "Misc.h"
+
+
+class UpdateDownloadTask : public Task
+{
+public:
+ UpdateDownloadTask(std::string updateName, UpdateActivity * a) : updateName(updateName), a(a) {};
+private:
+ UpdateActivity * a;
+ std::string updateName;
+ virtual void notifyDoneMain(){
+ a->NotifyDone(this);
+ }
+ virtual void notifyErrorMain()
+ {
+ a->NotifyError(this);
+ }
+ virtual bool doWork()
+ {
+ std::stringstream errorStream;
+ void * request = http_async_req_start(NULL, (char*)updateName.c_str(), NULL, 0, 0);
+ notifyStatus("Downloading update");
+ notifyProgress(-1);
+ while(!http_async_req_status(request))
+ {
+ int total, done;
+ http_async_get_length(request, &total, &done);
+ notifyProgress((float(done)/float(total))*100.0f);
+ }
+
+ char * data;
+ int dataLength, status;
+ data = http_async_req_stop(request, &status, &dataLength);
+ if (status!=200)
+ {
+ if (data)
+ free(data);
+ errorStream << "Server responded with Status " << status;
+ notifyError("Could not download update");
+ return false;
+ }
+ if (!data)
+ {
+ errorStream << "Server responded with nothing";
+ notifyError("Server did not return any data");
+ return false;
+ }
+
+ notifyStatus("Unpacking update");
+ notifyProgress(-1);
+
+ int uncompressedLength;
+
+ if(dataLength<16)
+ {
+ errorStream << "Unsufficient data, got " << dataLength << " bytes";
+ goto corrupt;
+ }
+ if (data[0]!=0x42 || data[1]!=0x75 || data[2]!=0x54 || data[3]!=0x54)
+ {
+ errorStream << "Invalid update format";
+ goto corrupt;
+ }
+
+ uncompressedLength = (unsigned char)data[4];
+ uncompressedLength |= ((unsigned char)data[5])<<8;
+ uncompressedLength |= ((unsigned char)data[6])<<16;
+ uncompressedLength |= ((unsigned char)data[7])<<24;
+
+ char * res;
+ res = (char *)malloc(uncompressedLength);
+ if (!res)
+ {
+ errorStream << "Unable to allocate " << uncompressedLength << " bytes of memory for decompression";
+ goto corrupt;
+ }
+
+ int dstate;
+ dstate = BZ2_bzBuffToBuffDecompress((char *)res, (unsigned *)&uncompressedLength, (char *)(data+8), dataLength-8, 0, 0);
+ if (dstate)
+ {
+ errorStream << "Unable to decompress update: " << dstate;
+ free(res);
+ goto corrupt;
+ }
+
+ free(data);
+
+ notifyStatus("Applying update");
+ notifyProgress(-1);
+
+ Client::Ref().SetPref("version.update", true);
+ Client::Ref().WritePrefs();
+ if (update_start(res, uncompressedLength))
+ {
+ Client::Ref().SetPref("version.update", false);
+ update_cleanup();
+ notifyError("Update failed - try downloading a new version.");
+ return false;
+ }
+
+ return true;
+
+ corrupt:
+ notifyError("Downloaded update is corrupted\n" + errorStream.str());
+ free(data);
+ return false;
+ }
+};
+
+UpdateActivity::UpdateActivity() {
+ std::stringstream file;
+ file << "http://" << SERVER << Client::Ref().GetUpdateInfo().File;
+ updateDownloadTask = new UpdateDownloadTask(file.str(), this);
+ updateWindow = new TaskWindow("Downloading update...", updateDownloadTask, true);
+}
+
+void UpdateActivity::NotifyDone(Task * sender)
+{
+ if(sender->GetSuccess())
+ {
+ Exit();
+ }
+}
+
+void UpdateActivity::Exit()
+{
+ updateWindow->Exit();
+ ui::Engine::Ref().Exit();
+ delete this;
+}
+
+void UpdateActivity::NotifyError(Task * sender)
+{
+ class ErrorMessageCallback: public ConfirmDialogueCallback
+ {
+ UpdateActivity * a;
+ public:
+ ErrorMessageCallback(UpdateActivity * a_) { a = a_; }
+ virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {
+ if (result == ConfirmPrompt::ResultOkay)
+ {
+ OpenURI("http://powdertoy.co.uk/Download.html");
+ }
+ a->Exit();
+ }
+ virtual ~ErrorMessageCallback() { }
+ };
+ new ConfirmPrompt("Autoupdate failed", "Please visit the website to download a newer version.\nError: " + sender->GetError(), new ErrorMessageCallback(this));
+}
+
+
+UpdateActivity::~UpdateActivity() {
+ // TODO Auto-generated destructor stub
+}
+
diff --git a/src/update/UpdateActivity.h b/src/update/UpdateActivity.h
new file mode 100644
index 0000000..533aaf7
--- /dev/null
+++ b/src/update/UpdateActivity.h
@@ -0,0 +1,24 @@
+/*
+ * UpdateActivity.h
+ *
+ * Created on: Jun 20, 2012
+ * Author: Simon
+ */
+
+#ifndef UPDATEACTIVITY_H_
+#define UPDATEACTIVITY_H_
+
+#include "tasks/TaskWindow.h"
+
+class UpdateActivity {
+ Task * updateDownloadTask;
+ TaskWindow * updateWindow;
+public:
+ UpdateActivity();
+ virtual ~UpdateActivity();
+ void Exit();
+ virtual void NotifyDone(Task * sender);
+ virtual void NotifyError(Task * sender);
+};
+
+#endif /* UPDATEACTIVITY_H_ */
diff --git a/src/virtualmachine/Exceptions.h b/src/virtualmachine/Exceptions.h
new file mode 100644
index 0000000..9628d10
--- /dev/null
+++ b/src/virtualmachine/Exceptions.h
@@ -0,0 +1,100 @@
+#pragma once
+#include <stdexcept>
+#include <cstring>
+#include "Format.h"
+
+namespace vm
+{
+ class RuntimeException: public std::exception
+ {
+ char * error;
+ public:
+ RuntimeException() : error(NULL) {}
+ RuntimeException(char * message) : error(strdup(message)) {}
+ const char * what() const throw()
+ {
+ if(error)
+ return error;
+ else
+ return "VirtualMachine runtime exception";
+ }
+ ~RuntimeException() throw() {};
+ };
+
+ class StackOverflowException: public RuntimeException
+ {
+ public:
+ StackOverflowException() {}
+ const char * what() const throw()
+ {
+ return "VirtualMachine Stack overflow";
+ }
+ ~StackOverflowException() throw() {};
+ };
+
+ class StackUnderflowException: public RuntimeException
+ {
+ public:
+ StackUnderflowException() {}
+ const char * what() const throw()
+ {
+ return "VirtualMachine Stack underflow";
+ }
+ ~StackUnderflowException() throw() {};
+ };
+
+ class AccessViolationException: public RuntimeException
+ {
+ int address;
+ char * _what;
+ public:
+ AccessViolationException(int address = 0) : address(address)
+ {
+ _what = strdup(std::string("VirtualMachine Access violation at "+format::NumberToString<int>(address)).c_str());
+ }
+ const char * what() const throw()
+ {
+ if(address)
+ return _what;
+ return "VirtualMachine Access violation";
+ }
+ ~AccessViolationException() throw() {};
+ };
+
+ class JITException: public RuntimeException
+ {
+ char * _what;
+ public:
+ JITException(const char * what2)
+ {
+ _what = strdup(what2);
+ }
+ const char * what() const throw()
+ {
+ return _what;
+ }
+ ~JITException() throw() {};
+ };
+
+ class OutOfMemoryException: public RuntimeException
+ {
+ public:
+ OutOfMemoryException() {}
+ const char * what() const throw()
+ {
+ return "VirtualMachine Out of memory";
+ }
+ ~OutOfMemoryException() throw() {};
+ };
+
+ class InvalidProgramException: public RuntimeException
+ {
+ public:
+ InvalidProgramException() {}
+ const char * what() const throw()
+ {
+ return "Could not load program";
+ }
+ ~InvalidProgramException() throw() {};
+ };
+} \ No newline at end of file
diff --git a/src/virtualmachine/JustInTime.cpp b/src/virtualmachine/JustInTime.cpp
new file mode 100644
index 0000000..9929d81
--- /dev/null
+++ b/src/virtualmachine/JustInTime.cpp
@@ -0,0 +1,1144 @@
+#ifdef VMJIT
+
+#include <cstdio>
+#include "VirtualMachine.h"
+
+#ifdef WIN32
+#include "Windows.h"
+#endif
+
+namespace vm
+{
+ #define OP(n) OP##n
+ /*
+
+ eax scratch
+ ebx scratch
+ ecx scratch (required for shifts)
+ edx scratch (required for divisions)
+ esi RP
+ edi DP
+
+ */
+
+ // TTimo: initialised the statics, this fixes a crash when entering a compiled VM
+ static unsigned char *buf = NULL;
+ static unsigned char *jused = NULL;
+ static int compiledOfs = 0;
+ static int pc = 0;
+
+ //static int callMask = 0; // bk001213 - init
+ static int eDP;
+ static int eRP;
+ static int instruction, pass;
+ static int lastConst = 0;
+ static int oc0, oc1, pop0, pop1;
+
+ static int eRamMask = 0;
+ static int eRomMask = 0;
+ static int * eInstructionPointers = NULL;
+
+ static int eSyscallNum;
+ static void * eRam = NULL;
+ static VirtualMachine * eVM = NULL;
+
+ static int callFromCompiledPtr = (int)VirtualMachine::callFromCompiled;
+ static int callSyscallPtr = (int)VirtualMachine::callSyscall;
+
+ typedef enum
+ {
+ LAST_COMMAND_NONE = 0,
+ LAST_COMMAND_MOV_EDI_EAX,
+ LAST_COMMAND_SUB_DI_4,
+ LAST_COMMAND_SUB_DI_8,
+ } ELastCommand;
+
+ static ELastCommand LastCommand;
+
+ void VirtualMachine::callSyscall()
+ {
+ //throw RuntimeException("Turd");
+
+ /*VirtualMachine * savedVM;
+ int * callOpStack2;
+
+ savedVM = eVM;
+ callOpStack2 = (int*)eOpStack;
+
+ // save the stack to allow recursive VM entry
+ eVM->DP = eDP - 4;
+ *(int *)((byte *)eVM->ram + eDP + 4) = eSyscallNum;
+ //VM_LogSyscalls( (int *)((byte *)currentVM->dataBase + programStack + 4) );
+ *(callOpStack2+1) = eVM->syscall( *(int *)((unsigned char *)eVM->ram + eDP + 4) );
+
+ eVM = savedVM;*/
+ }
+
+ void VirtualMachine::callFromCompiled()
+ {
+ /*__asm__("doAsmCall: \n\t" \
+ " movl (%%edi),%%eax \n\t" \
+ " subl $4,%%edi \n\t" \
+ " orl %%eax,%%eax \n\t" \
+ " jl systemCall \n\t" \
+ " shll $2,%%eax \n\t" \
+ " addl %3,%%eax \n\t" \
+ " call *(%%eax) \n\t" \
+ " movl (%%edi),%%eax \n\t" \
+ " andl %5, %%eax \n\t" \
+ " jmp doret \n\t" \
+ "systemCall: \n\t" \
+ " negl %%eax \n\t" \
+ " decl %%eax \n\t" \
+ " movl %%eax,%0 \n\t" \
+ " movl %%esi,%1 \n\t" \
+ " movl %%edi,%2 \n\t" \
+ " pushl %%ecx \n\t" \
+ " pushl %%esi \n\t" \
+ " pushl %%edi \n\t" \
+ " call *%4 \n\t" \
+ " popl %%edi \n\t" \
+ " popl %%esi \n\t" \
+ " popl %%ecx \n\t" \
+ " addl $4,%%edi \n\t" \
+ "doret: \n\t" \
+ " ret \n\t" \
+ : "=rm" (eSyscallNum), "=rm" (eDP), "=rm" (eOpStack) \
+ : "rm" (eInstructionPointers), "r" (callSyscall), "m" (eRomMask) \
+ : "ax", "di", "si", "cx" \
+ );*/
+ //" call *%4 \n\t"
+
+ //" negl %%eax \n\t"
+ // " decl %%eax \n\t"
+ __asm__ volatile ("doAsmCall: \n\t" \
+ " movl (%%edi),%%eax \n\t" \
+ " subl $4,%%edi \n\t" \
+ " orl %%eax,%%eax \n\t" \
+ " jl systemCall \n\t" \
+ " shll $2,%%eax \n\t" \
+ " addl %3,%%eax \n\t" \
+ " call *(%%eax) \n\t" \
+ " movl (%%edi),%%eax \n\t" \
+ " andl %5, %%eax \n\t" \
+ " ret \n\t" \
+ "systemCall: \n\t" \
+ " movl %%eax,%0 \n\t" \
+ " movl %%esi,%1 \n\t" \
+ " movl %%edi,%2 \n\t" \
+ " pushl %%ecx \n\t" \
+ " pushl %%esi \n\t" \
+ " pushl %%edi \n\t" \
+ : "=rm" (eSyscallNum), "=rm" (eRP), "=rm" (eDP) \
+ : "rm" (eInstructionPointers), "r" (callSyscall), "m" (eRomMask) \
+ : "ax", "di", "si", "cx" \
+ );
+ //printf("Syscall: %d\n", eSyscallNum);
+ //throw RuntimeException("Turd");
+
+ eVM->DP = eDP-((int)eRam);
+ eVM->syscall(eSyscallNum);
+ eDP = eVM->DP+((int)eRam)-4;
+
+ //" addl $4,%edi \n\t"
+ // " ret \n\t"
+ __asm__ volatile ("popl %edi \n\t" \
+ " popl %esi \n\t" \
+ " popl %ecx \n\t" \
+ "doret: \n\t" \
+ );
+ }
+
+ int VirtualMachine::CallCompiled(int address)
+ {
+ //int stack[1024];
+ //int stack[100];
+ int programStack;
+ int stackOnEntry;
+ unsigned char * image;
+ void * entryPoint;
+ int * oldInstructionPointers;
+ void * oldDataStack;
+
+ oldDataStack = eRam;
+ oldInstructionPointers = eInstructionPointers;
+
+ eVM = this;
+ eInstructionPointers = instructionPointers;
+
+ eRomMask = romMask;
+ eRamMask = ramMask;
+
+ // we might be called recursively, so this might not be the very top
+ eDP = ((int)ram)+DP;
+ stackOnEntry = DP;
+
+ // set up the stack frame
+ image = (unsigned char *)ram;//vm->dataBase;
+
+ eDP -= 48;
+
+ //*(int *)&image[ programStack + 44] = args[9];
+ //*(int *)&image[ programStack + 40] = args[8];
+ //*(int *)&image[ programStack + 36] = args[7];
+ //*(int *)&image[ programStack + 32] = args[6];
+ //*(int *)&image[ programStack + 28] = args[5];
+ //*(int *)&image[ programStack + 24] = args[4];
+ //*(int *)&image[ programStack + 20] = args[3];
+ //*(int *)&image[ programStack + 16] = args[2];
+ //*(int *)&image[ programStack + 12] = args[1];
+ //*(int *)&image[ programStack + 8 ] = args[0];
+ //*(int *)&image[ programStack + 4 ] = 0; // return stack
+ //*(int *)&image[ programStack ] = -1; // will terminate the loop on return
+
+ // off we go into generated code...
+ entryPoint = compiledRom;//0;//vm->codeBase;
+ eRam = ram+dataStack;
+
+ #if defined(_MSC_VER)
+ __asm {
+ pushad
+ mov esi, DP;
+ mov edi, opStack
+ call entryPoint
+ mov DP, esi
+ mov opStack, edi
+ popad
+ }
+ #else
+ {
+ static int memDP;
+ static int memRP;
+ static void *memEntryPoint;
+
+ memDP = DP+((int)ram);
+ memRP = RP;
+ memEntryPoint = entryPoint;
+
+ __asm__(" pushal \r\n" \
+ " movl %0,%%esi \r\n" \
+ " movl %1,%%edi \r\n" \
+ " call *%2 \r\n" \
+ " movl %%esi,%0 \r\n" \
+ " movl %%edi,%1 \r\n" \
+ " popal \r\n" \
+ : "=m" (memRP), "=m" (memDP) \
+ : "m" (memEntryPoint), "0" (memRP), "1" (memDP) \
+ : "si", "di" \
+ );
+
+ DP = memDP-((int)ram);
+ RP = memRP;
+ }
+ #endif
+
+ if ( eRam != ram+dataStack ) {
+ throw RuntimeException("opStack corrupted in compiled code");
+ }
+ if ( DP != stackOnEntry+4 ) {
+ printf("DP: %d, stackOnEntry: %d\n", DP, stackOnEntry);
+ throw RuntimeException("programStack corrupted in compiled code");
+ }
+
+ DP = stackOnEntry;
+
+ // in case we were recursively called by another vm
+ eInstructionPointers = oldInstructionPointers;
+ eRam = oldDataStack;
+
+ return 0;//*(int *)eOpStack;
+ }
+
+ bool VirtualMachine::Compile()
+ {
+ Instruction op;
+ int maxLength;
+ int v;
+ int i;
+ bool opt;
+
+ // allocate a very large temp buffer, we will shrink it later
+ maxLength = romSize * 8;
+ buf = new unsigned char[maxLength];
+ jused = new unsigned char[romSize + 2];
+ instructionPointers = new int[romSize];
+ std::fill(jused, jused+romSize+2, 0);
+
+ for(pass=0; pass<2; pass++) {
+ oc0 = -23423;
+ oc1 = -234354;
+ pop0 = -43435;
+ pop1 = -545455;
+
+ // translate all instructions
+ pc = 0;
+ instruction = 0;
+ compiledOfs = 0;
+
+ LastCommand = LAST_COMMAND_NONE;
+
+ while (instruction < romSize)
+ {
+ if (compiledOfs > maxLength - 16)
+ {
+ throw JITException("Compile: maxLength exceeded");
+ }
+
+ instructionPointers[instruction] = compiledOfs;
+ instruction++;
+
+ if (pc > romSize)
+ {
+ throw JITException("Compile: program counter run off the edge");
+ }
+
+ op = rom[pc];
+ pc++;
+ switch ( op.Operation )
+ {
+ case 0:
+ break;
+ case OP(BREAK):
+ emitInstruction( "CC" ); // int 3
+ break;
+ case OP(ENTER):
+ //emitInstruction( "CC" ); // int 3
+ emitInstruction( "81 EE" ); // sub esi, 0x12345678
+ emit4( constant4() );
+ break;
+ case OP(CONST):
+ if (rom[pc].Operation == OP(LOAD4))
+ {
+ emitAddEDI4();
+ emitInstruction( "BB" ); // mov ebx, 0x12345678
+ emit4( (constant4()&ramMask) + (int)ram);
+ emitInstruction( "8B 03" ); // mov eax, dword ptr [ebx]
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ pc++; // OP(LOAD4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(LOAD2))
+ {
+ emitAddEDI4();
+ emitInstruction( "BB" ); // mov ebx, 0x12345678
+ emit4( (constant4()&ramMask) + (int)ram);
+ emitInstruction( "0F B7 03" ); // movzx eax, word ptr [ebx]
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ pc++; // OP(LOAD4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(LOAD1))
+ {
+ emitAddEDI4();
+ emitInstruction( "BB" ); // mov ebx, 0x12345678
+ emit4( (constant4()&ramMask) + (int)ram);
+ emitInstruction( "0F B6 03" ); // movzx eax, byte ptr [ebx]
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ pc++; // OP(LOAD4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(STORE4))
+ {
+ opt = emitMovEBXEDI((ramMask & ~3));
+ emitInstruction( "B8" ); // mov eax, 0x12345678
+ emit4( constant4() );
+ // if (!opt) {
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask & ~3 );
+ // }
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ pc++; // OP(STORE4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(STORE2))
+ {
+ opt = emitMovEBXEDI((ramMask & ~1));
+ emitInstruction( "B8" ); // mov eax, 0x12345678
+ emit4( constant4() );
+ // if (!opt) {
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask & ~1 );
+ // }
+ emitInstruction( "66 89 83" ); // mov word ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ pc++; // OP(STORE4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(STORE1))
+ {
+ opt = emitMovEBXEDI(ramMask);
+ emitInstruction( "B8" ); // mov eax, 0x12345678
+ emit4( constant4() );
+ // if (!opt) {
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask );
+ // }
+ emitInstruction( "88 83" ); // mov byte ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ pc++; // OP(STORE4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(ADD))
+ {
+ emitInstruction( "81 07" ); // add dword ptr [edi], 0x1234567
+ emit4( constant4() );
+ pc++; // OP(ADD)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(SUB))
+ {
+ emitInstruction( "81 2F" ); // sub dword ptr [edi], 0x1234567
+ emit4( constant4() );
+ pc++; // OP(ADD)
+ instruction += 1;
+ break;
+ }
+ emitAddEDI4();
+ emitInstruction( "C7 07" ); // mov dword ptr [edi], 0x12345678
+ lastConst = constant4();
+ emit4( lastConst );
+ if (rom[pc].Operation == OP(JUMP))
+ {
+ jused[lastConst] = 1;
+ }
+ break;
+ case OP(LOCAL):
+ emitAddEDI4();
+ emitInstruction( "8D 86" ); // lea eax, [0x12345678 + esi]
+ oc0 = oc1;
+ oc1 = constant4();
+ emit4( oc1 );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ case OP(ARG):
+ emitMovEAXEDI(); // mov eax,dword ptr [edi]
+ emitInstruction( "89 86" ); // mov dword ptr [esi+ram],eax
+ // FIXME: range check
+ emit4( constant1() + (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(CALL):
+ emitInstruction("C7 86"); // mov dword ptr [esi+ram],0x12345678
+ emit4((int)ram);
+ emit4(pc);
+ emitInstruction("FF 15"); // call callFromCompiled
+ emit4((int)&callFromCompiledPtr);
+ break;
+ case OP(PUSH):
+ emitAddEDI4();
+ break;
+ case OP(POP):
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(LEAVE):
+ v = constant4();
+ emitInstruction( "81 C6" ); // add esi, 0x12345678
+ emit4( v );
+ emitInstruction( "C3" ); // ret
+ break;
+ case OP(LOAD4):
+ if (rom[pc].Operation == OP(CONST) && rom[pc+1].Operation == OP(ADD) && rom[pc+2].Operation == OP(STORE4))
+ {
+ if (oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL))
+ {
+ compiledOfs -= 11;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ }
+ pc++; // OP(CONST)
+ v = constant4();
+ emitMovEBXEDI(ramMask);
+ if (v == 1 && oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL))
+ {
+ emitInstruction( "FF 83"); // inc dword ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ }
+ else
+ {
+ emitInstruction( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ emitInstruction( "05" ); // add eax, const
+ emit4( v );
+ if (oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL))
+ {
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ }
+ else
+ {
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "8B 1F" ); // mov ebx, dword ptr [edi]
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ }
+ }
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ pc++; // OP(ADD)
+ pc++; // OP(STORE)
+ instruction += 3;
+ break;
+ }
+
+ if (rom[pc].Operation == OP(CONST) && rom[pc+1].Operation == OP(SUB) && rom[pc+2].Operation == OP(STORE4))
+ {
+ if (oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL)) {
+ compiledOfs -= 11;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ }
+ emitMovEBXEDI(ramMask);
+ emitInstruction( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ pc++; // OP(CONST)
+ v = constant4();
+ if (v == 1 && oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL))
+ {
+ emitInstruction( "FF 8B"); // dec dword ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ }
+ else
+ {
+ emitInstruction( "2D" ); // sub eax, const
+ emit4( v );
+ if (oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL))
+ {
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ }
+ else
+ {
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "8B 1F" ); // mov ebx, dword ptr [edi]
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ }
+ }
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ pc++; // OP(SUB)
+ pc++; // OP(STORE)
+ instruction += 3;
+ break;
+ }
+
+ if (buf[compiledOfs-2] == 0x89 && buf[compiledOfs-1] == 0x07)
+ {
+ compiledOfs -= 2;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ emitInstruction( "8B 80"); // mov eax, dword ptr [eax + 0x1234567]
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ }
+ emitMovEBXEDI(ramMask);
+ emitInstruction( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ case OP(LOAD2):
+ emitMovEBXEDI(ramMask);
+ emitInstruction( "0F B7 83" ); // movzx eax, word ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ case OP(LOAD1):
+ emitMovEBXEDI(ramMask);
+ emitInstruction( "0F B6 83" ); // movzx eax, byte ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ case OP(STORE4):
+ emitMovEAXEDI();
+ emitInstruction( "8B 5F FC" ); // mov ebx, dword ptr [edi-4]
+ // if (pop1 != OP(CALL)) {
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask & ~3 );
+ // }
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ break;
+ case OP(STORE2):
+ emitMovEAXEDI();
+ emitInstruction( "8B 5F FC" ); // mov ebx, dword ptr [edi-4]
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask & ~1 );
+ emitInstruction( "66 89 83" ); // mov word ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ break;
+ case OP(STORE1):
+ emitMovEAXEDI();
+ emitInstruction( "8B 5F FC" ); // mov ebx, dword ptr [edi-4]
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask );
+ emitInstruction( "88 83" ); // mov byte ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ break;
+
+ case OP(EQ):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "75 06" ); // jne +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(NE):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "74 06" ); // je +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LTI):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "7D 06" ); // jnl +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LEI):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "7F 06" ); // jnle +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GTI):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "7E 06" ); // jng +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GEI):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "7C 06" ); // jnge +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LTU):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "73 06" ); // jnb +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LEU):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "77 06" ); // jnbe +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GTU):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "76 06" ); // jna +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GEU):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "72 06" ); // jnae +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(EQF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 40" ); // test ah,0x40
+ emitInstruction( "74 06" ); // je +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(NEF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 40" ); // test ah,0x40
+ emitInstruction( "75 06" ); // jne +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LTF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 01" ); // test ah,0x01
+ emitInstruction( "74 06" ); // je +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LEF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 41" ); // test ah,0x41
+ emitInstruction( "74 06" ); // je +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GTF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 41" ); // test ah,0x41
+ emitInstruction( "75 06" ); // jne +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GEF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 01" ); // test ah,0x01
+ emitInstruction( "75 06" ); // jne +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(NEGI):
+ emitInstruction( "F7 1F" ); // neg dword ptr [edi]
+ break;
+ case OP(ADD):
+ emitMovEAXEDI(); // mov eax, dword ptr [edi]
+ emitInstruction( "01 47 FC" ); // add dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(SUB):
+ emitMovEAXEDI(); // mov eax, dword ptr [edi]
+ emitInstruction( "29 47 FC" ); // sub dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(DIVI):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "99" ); // cdq
+ emitInstruction( "F7 3F" ); // idiv dword ptr [edi]
+ emitInstruction( "89 47 FC" ); // mov dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(DIVU):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "33 D2" ); // xor edx, edx
+ emitInstruction( "F7 37" ); // div dword ptr [edi]
+ emitInstruction( "89 47 FC" ); // mov dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(MODI):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "99" ); // cdq
+ emitInstruction( "F7 3F" ); // idiv dword ptr [edi]
+ emitInstruction( "89 57 FC" ); // mov dword ptr [edi-4],edx
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(MODU):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "33 D2" ); // xor edx, edx
+ emitInstruction( "F7 37" ); // div dword ptr [edi]
+ emitInstruction( "89 57 FC" ); // mov dword ptr [edi-4],edx
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(MULI):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "F7 2F" ); // imul dword ptr [edi]
+ emitInstruction( "89 47 FC" ); // mov dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(MULU):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "F7 27" ); // mul dword ptr [edi]
+ emitInstruction( "89 47 FC" ); // mov dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(BAND):
+ emitMovEAXEDI(); // mov eax, dword ptr [edi]
+ emitInstruction( "21 47 FC" ); // and dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(BOR):
+ emitMovEAXEDI(); // mov eax, dword ptr [edi]
+ emitInstruction( "09 47 FC" ); // or dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(BXOR):
+ emitMovEAXEDI(); // mov eax, dword ptr [edi]
+ emitInstruction( "31 47 FC" ); // xor dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(BCOM):
+ emitInstruction( "F7 17" ); // not dword ptr [edi]
+ break;
+ case OP(LSH):
+ emitInstruction( "8B 0F" ); // mov ecx, dword ptr [edi]
+ emitInstruction( "D3 67 FC" ); // shl dword ptr [edi-4], cl
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(RSHI):
+ emitInstruction( "8B 0F" ); // mov ecx, dword ptr [edi]
+ emitInstruction( "D3 7F FC" ); // sar dword ptr [edi-4], cl
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(RSHU):
+ emitInstruction( "8B 0F" ); // mov ecx, dword ptr [edi]
+ emitInstruction( "D3 6F FC" ); // shr dword ptr [edi-4], cl
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(NEGF):
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "D9 E0" ); // fchs
+ emitInstruction( "D9 1F" ); // fstp dword ptr [edi]
+ break;
+ case OP(ADDF):
+ emitInstruction( "D9 47 FC" ); // fld dword ptr [edi-4]
+ emitInstruction( "D8 07" ); // fadd dword ptr [edi]
+ emitInstruction( "D9 5F FC" ); // fstp dword ptr [edi-4]
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(SUBF):
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "D8 67 04" ); // fsub dword ptr [edi+4]
+ emitInstruction( "D9 1F" ); // fstp dword ptr [edi]
+ break;
+ case OP(DIVF):
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "D8 77 04" ); // fdiv dword ptr [edi+4]
+ emitInstruction( "D9 1F" ); // fstp dword ptr [edi]
+ break;
+ case OP(MULF):
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "D8 4f 04" ); // fmul dword ptr [edi+4]
+ emitInstruction( "D9 1F" ); // fstp dword ptr [edi]
+ break;
+ case OP(CVIF):
+ emitInstruction( "DB 07" ); // fild dword ptr [edi]
+ emitInstruction( "D9 1F" ); // fstp dword ptr [edi]
+ break;
+ case OP(CVFI):
+ #ifndef FTOL_PTR // WHENHELLISFROZENOVER // bk001213 - was used in 1.17
+ // not IEEE complient, but simple and fast
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "DB 1F" ); // fistp dword ptr [edi]
+ #else // FTOL_PTR
+ // call the library conversion function
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "FF 15" ); // call ftolPtr
+ emit4( (int)&ftolPtr );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ #endif
+ break;
+ case OP(SEX8):
+ emitInstruction( "0F BE 07" ); // movsx eax, byte ptr [edi]
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ case OP(SEX16):
+ emitInstruction( "0F BF 07" ); // movsx eax, word ptr [edi]
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+
+ case OP(BLOCK_COPY):
+ // FIXME: range check
+ emitInstruction( "56" ); // push esi
+ emitInstruction( "57" ); // push edi
+ emitInstruction( "8B 37" ); // mov esi,[edi]
+ emitInstruction( "8B 7F FC" ); // mov edi,[edi-4]
+ emitInstruction( "B9" ); // mov ecx,0x12345678
+ emit4( constant4() >> 2 );
+ emitInstruction( "B8" ); // mov eax, ramMask
+ emit4( ramMask );
+ emitInstruction( "BB" ); // mov ebx, ram
+ emit4( (int)ram );
+ emitInstruction( "23 F0" ); // and esi, eax
+ emitInstruction( "03 F3" ); // add esi, ebx
+ emitInstruction( "23 F8" ); // and edi, eax
+ emitInstruction( "03 FB" ); // add edi, ebx
+ emitInstruction( "F3 A5" ); // rep movsd
+ emitInstruction( "5F" ); // pop edi
+ emitInstruction( "5E" ); // pop esi
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ break;
+
+ case OP(JUMP):
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "8B 47 04" ); // mov eax,dword ptr [edi+4]
+ // FIXME: range check
+ emitInstruction( "FF 24 85" ); // jmp dword ptr [instructionPointers + eax * 4]
+ emit4( (int)instructionPointers );
+ break;
+ default:
+ throw JITException("Compile: bad opcode");
+ }
+ pop0 = pop1;
+ pop1 = op.Operation;
+ }
+ }
+
+ // copy to an exact size buffer on the hunk
+ //codeLength = compiledOfs;
+ //codeBase = Hunk_Alloc( compiledOfs, h_low );
+
+ //Com_Memcpy( codeBase, buf, compiledOfs );
+
+ compiledRom = new char[compiledOfs];
+ std::copy(buf, buf+compiledOfs, compiledRom);
+ compiledRomSize = compiledOfs;
+ compiledRomMask = compiledOfs;
+
+ delete[] buf;
+ delete[] jused;
+
+ //printf( "VM file %s compiled to %i bytes of code\n", name, compiledOfs);
+
+ // offset all the instruction pointers for the new location
+ for ( i = 0 ; i < /*header->instructionCount*/ romSize ; i++ ) {
+ instructionPointers[i] += (int)rom;
+ }
+
+#ifdef WIN32
+ VirtualProtect(compiledRom, compiledRomSize, PAGE_EXECUTE, NULL);
+#endif
+ #if 0 // ndef _WIN32
+ // Must make the newly generated code executable
+ {
+ int r;
+ unsigned long addr;
+ int psize = getpagesize();
+
+ addr = ((int)codeBase & ~(psize-1)) - psize;
+
+ r = mprotect((char*)addr, codeLength + (int)codeBase - addr + psize,
+ PROT_READ | PROT_WRITE | PROT_EXEC );
+
+ if (r < 0)
+ Com_Error( ERR_FATAL, "mprotect failed to change PROT_EXEC" );
+ }
+ #endif
+ return true;
+
+ }
+
+ int VirtualMachine::constant4()
+ {
+ int v;
+
+ v = rom[pc-1].Parameter.int4;// | (rom[pc-1].Parameter<<8) | (rom[pc-1].Parameter<<16) | (rom[pc-1].Parameter<<24);
+ return v;
+ }
+
+ int VirtualMachine::constant1()
+ {
+ int v;
+
+ v = rom[pc-1].Parameter.uint1;
+ return v;
+ }
+
+ void VirtualMachine::emit1(int v)
+ {
+ buf[compiledOfs] = v;
+ compiledOfs++;
+
+ LastCommand = LAST_COMMAND_NONE;
+ }
+
+ void VirtualMachine::emit4(int v)
+ {
+ emit1(v & 255);
+ emit1((v >> 8) & 255);
+ emit1((v >> 16) & 255);
+ emit1((v >> 24) & 255);
+ }
+
+ void VirtualMachine::emitInstruction(const char *string)
+ {
+ int c1, c2;
+ int v;
+
+ while (true)
+ {
+ c1 = string[0];
+ c2 = string[1];
+
+ v = (hex( c1 ) << 4) | hex(c2);
+ emit1( v );
+
+ if (!string[2])
+ {
+ break;
+ }
+ string += 3;
+ }
+ }
+
+ void VirtualMachine::emitCommand(int command_)
+ {
+ ELastCommand command = (ELastCommand)command_;
+ switch(command)
+ {
+ case LAST_COMMAND_MOV_EDI_EAX:
+ emitInstruction( "89 07" ); // mov dword ptr [edi], eax
+ break;
+ case LAST_COMMAND_SUB_DI_4:
+ emitInstruction( "83 EF 04" ); // sub edi, 4
+ break;
+ case LAST_COMMAND_SUB_DI_8:
+ emitInstruction( "83 EF 08" ); // sub edi, 8
+ break;
+ default:
+ break;
+ }
+ LastCommand = command;
+ }
+
+ void VirtualMachine::emitAddEDI4()
+ {
+ if (LastCommand == LAST_COMMAND_SUB_DI_4 && jused[instruction-1] == 0)
+ { // sub di,4
+ compiledOfs -= 3;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ return;
+ }
+ if (LastCommand == LAST_COMMAND_SUB_DI_8 && jused[instruction-1] == 0)
+ { // sub di,8
+ compiledOfs -= 3;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ emitInstruction( "83 EF 04" ); // sub edi,4
+ return;
+ }
+ emitInstruction( "83 C7 04" ); // add edi,4
+ }
+
+ void VirtualMachine::emitMovEAXEDI()
+ {
+ if (LastCommand == LAST_COMMAND_MOV_EDI_EAX)
+ { // mov [edi], eax
+ compiledOfs -= 2;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ return;
+ }
+ if (pop1 == OP(DIVI) || pop1 == OP(DIVU) || pop1 == OP(MULI) || pop1 == OP(MULU) || pop1 == OP(STORE4) || pop1 == OP(STORE2) || pop1 == OP(STORE1) )
+ {
+ return;
+ }
+ if (pop1 == OP(CONST) && buf[compiledOfs-6] == 0xC7 && buf[compiledOfs-5] == 0x07)
+ { // mov edi, 0x123456
+ compiledOfs -= 6;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ emitInstruction( "B8" ); // mov eax, 0x12345678
+ emit4( lastConst );
+ return;
+ }
+ emitInstruction( "8B 07" ); // mov eax, dword ptr [edi]
+ }
+
+ bool VirtualMachine::emitMovEBXEDI(int andit)
+ {
+ if (LastCommand == LAST_COMMAND_MOV_EDI_EAX)
+ { // mov [edi], eax
+ compiledOfs -= 2;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ emitInstruction( "8B D8"); // mov bx, eax
+ return false;
+ }
+ if (pop1 == OP(DIVI) || pop1 == OP(DIVU) || pop1 == OP(MULI) || pop1 == OP(MULU) || pop1 == OP(STORE4) || pop1 == OP(STORE2) || pop1 == OP(STORE1) )
+ {
+ emitInstruction( "8B D8"); // mov bx, eax
+ return false;
+ }
+ if (pop1 == OP(CONST) && buf[compiledOfs-6] == 0xC7 && buf[compiledOfs-5] == 0x07 )
+ { // mov edi, 0x123456
+ compiledOfs -= 6;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ emitInstruction( "BB" ); // mov ebx, 0x12345678
+ if (andit) {
+ emit4( lastConst & andit );
+ } else {
+ emit4( lastConst );
+ }
+ return true;
+ }
+
+ emitInstruction( "8B 1F" ); // mov ebx, dword ptr [edi]
+ return false;
+ }
+
+ int VirtualMachine::hex(int c)
+ {
+ if (c >= 'a' && c <= 'f')
+ return 10 + c - 'a';
+ if (c >= 'A' && c <= 'F')
+ return 10 + c - 'A';
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ throw JITException("hex: bad character");
+
+ return 0;
+ }
+
+ #undef OP
+}
+
+#endif \ No newline at end of file
diff --git a/src/virtualmachine/Operations.cpp b/src/virtualmachine/Operations.cpp
new file mode 100644
index 0000000..0c998a8
--- /dev/null
+++ b/src/virtualmachine/Operations.cpp
@@ -0,0 +1,356 @@
+#include "VirtualMachine.h"
+
+namespace vm
+{
+ #define OPDEF(n) &VirtualMachine::Op##n,
+ OperationFunction VirtualMachine::operations[] =
+ {
+ #include "Operations.inl"
+ };
+ #undef OPDEF
+
+ #define OPDEF(n) int VirtualMachine::Op##n(word parameter)
+
+ #define R0 (r[0])
+ #define R1 (r[1])
+ #define R2 (r[2])
+
+
+ OPDEF(UNDEF)
+ {
+ /* Die horribly. */
+ throw RuntimeException();
+ return -1;
+ }
+
+ OPDEF(IGNORE)
+ {
+ /* NOP */
+ throw RuntimeException();
+ return 0;
+ }
+
+ OPDEF(BREAK)
+ {
+ /* Usage never spotted. */
+ /* Die horribly? */
+ throw RuntimeException();
+ return -1;
+ }
+
+ /*
+ Stack on entering...
+
+ no locals: ENTER 8
+ 1 words locals: ENTER 16
+ 2 words locals: ENTER 20
+ 3 words locals: ENTER 24
+ etc.
+
+ address of argument:
+ ADDRFP4 v => OP_LOCAL (16 + currentLocals + currentArgs + v)
+ address of local:
+ ADDRLP4 v => OP_LOCAL (8 + currentArgs + v)
+
+ RP [ ] ??? (oldPC?)
+ [ ] ???
+ [ ] \
+ ... > locals (args marshalling)
+ [ ] /
+ [ ] \
+ ... > locals
+ [ ] / (ADDRLP4 v => OP_LOCAL (8 + currentArgs + v))
+ (oldRP?) [ ] ???
+ [ ] ???
+ [ ] (my args?)
+ ...
+ [ ]
+ */
+
+ OPDEF(ENTER) /* ??? */
+ {
+ while (parameter.int4 > (2 * sizeof(word)))
+ {
+ RPush<int4_t>(0); /* init zero */
+ parameter.int4 -= sizeof(word);
+ }
+ RPush(Pop()); //Program Counter
+ RPush<int4_t>(0); //Unknown
+ return 0;
+ }
+
+ OPDEF(LEAVE) /* ??? */
+ {
+ RPop(); //Unknown
+ parameter.int4 -= sizeof(word);
+ PC = RPop<int4_t>(); //Program counter
+ parameter.int4 -= sizeof(word);
+ while (parameter.int4 > 0)
+ {
+ RPop();
+ parameter.int4 -= sizeof(word);
+ }
+ return 0;
+ }
+
+ OPDEF(CALL) /* Call subroutine. */
+ {
+ R0 = Pop();
+ Push<int4_t>(PC);
+ PC = R0.int4;
+ return 0;
+ }
+
+ OPDEF(PUSH) /* [DP] <- 0; DP++ */
+ {
+ Push(0);
+ return 0;
+ }
+
+ OPDEF(POP) /* DP-- */
+ {
+ Pop();
+ return 0;
+ }
+
+ OPDEF(CONST) /* [DP] <- parm; DP++ */
+ {
+ Push(parameter);
+ return 0;
+ }
+
+ OPDEF(LOCAL) /* [DP] <- [RP-n] */
+ {
+ Push<int4_t>(RP + parameter.int4);
+ return 0;
+ }
+
+ OPDEF(JUMP) /* PC <- [DP] */
+ {
+ PC = Pop<int4_t>();
+ return 0;
+ }
+
+ #define CMP(type, op) \
+ { \
+ R0 = Pop(); \
+ cm = (Pop<type##_t>() op R0.type); \
+ if (cm) \
+ PC = parameter.uint4; \
+ return 0; \
+ }
+
+ OPDEF(EQ) /* if [DP] == [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, ==)
+
+ OPDEF(NE) /* if [DP] == [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, !=)
+
+ OPDEF(LTI) /* if [DP] < [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, <)
+
+ OPDEF(LEI) /* if [DP] <= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, <=)
+
+ OPDEF(GTI) /* if [DP] > [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, >)
+
+ OPDEF(GEI) /* if [DP] >= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, >=)
+
+ OPDEF(LTU) /* if [DP] < [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(uint4, <)
+
+ OPDEF(LEU) /* if [DP] <= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(uint4, <=)
+
+ OPDEF(GTU) /* if [DP] > [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(uint4, >)
+
+ OPDEF(GEU) /* if [DP] >= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(uint4, >=)
+
+ OPDEF(EQF) /* if [DP] == [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, ==)
+
+ OPDEF(NEF) /* if [DP] != [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, !=)
+
+ OPDEF(LTF) /* if [DP] < [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, <)
+
+ OPDEF(LEF) /* if [DP] <= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, <=)
+
+ OPDEF(GTF) /* if [DP] > [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, >)
+
+ OPDEF(GEF) /* if [DP] >= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, >=)
+
+
+ OPDEF(LOAD1) /* [DP] <- [[DP]] */
+ {
+ Push<uint1_t>(Get<uint1_t>(Pop<uint4_t>()));
+ return 0;
+ }
+
+ OPDEF(LOAD2) /* [DP] <- [[DP]] */
+ {
+ Push<uint2_t>(Get<uint2_t>(Pop<uint4_t>()));
+ return 0;
+ }
+
+ OPDEF(LOAD4) /* [DP] <- [[DP]] */
+ {
+ Push<uint4_t>(Get<uint4_t>(Pop<uint4_t>()));
+ return 0;
+ }
+
+ OPDEF(STORE1) /* [DP-1] <- [DP]; DP <- DP-2 */
+ {
+ Set<uint1_t>(Pop<uint4_t>(), Pop<uint1_t>());
+ return 0;
+ }
+
+ OPDEF(STORE2) /* [DP-1] <- [DP]; DP <- DP-2 */
+ {
+ Set<uint2_t>(Pop<uint4_t>(), Pop<uint2_t>());
+ return 0;
+ }
+
+ OPDEF(STORE4) /* [DP-1] <- [DP]; DP <- DP-2 */
+ {
+ Set<uint4_t>(Pop<uint4_t>(), Pop<uint4_t>());
+ return 0;
+ }
+
+ OPDEF(ARG) /* Marshal TOS to to-call argument list */
+ {
+ Marshal(parameter.uint1, Pop());
+ return 0;
+ }
+
+ OPDEF(BLOCK_COPY) /* XXX */
+ {
+ R1 = Pop();
+ R0 = Pop();
+ if(R0.int4 >= 0 && R0.int4 + parameter.int4 < ramSize && R1.int4 >= 0 && R1.int4 + parameter.int4 < ramSize)
+ memcpy(ram + R0.int4, ram + R1.int4, parameter.int4);
+ else
+ throw AccessViolationException();
+ return -1;
+ }
+
+ OPDEF(SEX8) /* Sign-extend 8-bit */
+ {
+ R0 = Pop();
+ if(R0.uint4 & 0x80)
+ R0.uint4 |= 0xFFFFFF80;
+ Push(R0);
+ return 0;
+ }
+
+ OPDEF(SEX16) /* Sign-extend 16-bit */
+ {
+ R0 = Pop();
+ if(R0.uint4 & 0x8000)
+ R0.uint4 |= 0xFFFF8000;
+ Push(R0);
+ return 0;
+ }
+
+ #define UNOP(type, op) \
+ { \
+ Push<type##_t>(op Pop<type##_t>()); \
+ return 0; \
+ }
+
+ #define BINOP(type, op) \
+ { \
+ R0 = Pop(); \
+ Push<type##_t>(Pop<type##_t>() op R0.type); \
+ return 0; \
+ }
+
+ OPDEF(NEGI) /* [DP] <- -[DP] */
+ UNOP(int4, -)
+
+ OPDEF(ADD) /* [DP-1] <- [DP-1] + [DP]; DP <- DP-1 */
+ BINOP(int4, +)
+
+ OPDEF(SUB) /* [DP-1] <- [DP-1] - [DP]; DP <- DP-1 */
+ BINOP(int4, -)
+
+ OPDEF(DIVI) /* [DP-1] <- [DP-1] / [DP]; DP <- DP-1 */
+ BINOP(int4, /)
+
+ OPDEF(DIVU) /* [DP-1] <- [DP-1] / [DP]; DP <- DP-1 */
+ BINOP(uint4, /)
+
+ OPDEF(MODI) /* [DP-1] <- [DP-1] % [DP]; DP <- DP-1 */
+ BINOP(int4, %)
+
+ OPDEF(MODU) /* [DP-1] <- [DP-1] % [DP]; DP <- DP-1 */
+ BINOP(uint4, %)
+
+ OPDEF(MULI) /* [DP-1] <- [DP-1] * [DP]; DP <- DP-1 */
+ BINOP(int4, *)
+
+ OPDEF(MULU) /* [DP-1] <- [DP-1] * [DP]; DP <- OP-1 */
+ BINOP(uint4, *)
+
+ OPDEF(BAND) /* [DP-1] <- [DP-1] & [DP]; DP <- DP-1 */
+ BINOP(uint4, &)
+
+ OPDEF(BOR) /* [DP-1] <- [DP-1] | [DP]; DP <- DP-1 */
+ BINOP(uint4, |)
+
+ OPDEF(BXOR) /* [DP-1] <- [DP-1] ^ [DP]; DP <- DP-1 */
+ BINOP(uint4, ^)
+
+ OPDEF(BCOM) /* [DP] <- ~[DP] */
+ UNOP(uint4, ~)
+
+ OPDEF(LSH) /* [DP-1] <- [DP-1] << [DP]; DP <- DP-1 */
+ BINOP(uint4, <<)
+
+ OPDEF(RSHI) /* [DP-1] <- [DP-1] >> [DP]; DP <- DP-1 */
+ {
+ R1.int4 = Pop<int4_t>();
+ R0.int4 = Pop<int4_t>();
+ R2.int4 = R0.int4 >> R1.int4;
+ Push(R2);
+ return 0;
+ }
+
+ OPDEF(RSHU) /* [DP-1] <- [DP-1] >> [DP]; DP <- DP-1 */
+ BINOP(uint4, >>)
+
+ OPDEF(NEGF) /* [DP] <- -[DP] */
+ UNOP(float4, -)
+
+ OPDEF(ADDF) /* [DP-1] <- [DP-1] + [DP]; DP <- DP-1 */
+ BINOP(float4, +)
+
+ OPDEF(SUBF) /* [DP-1] <- [DP-1] - [DP]; DP <- DP-1 */
+ BINOP(float4, -)
+
+ OPDEF(DIVF) /* [DP-1] <- [DP-1] / [DP]; DP <- DP-1 */
+ BINOP(float4, /)
+
+ OPDEF(MULF) /* [DP-1] <- [DP-1] / [DP]; DP <- DP-1 */
+ BINOP(float4, *)
+
+ OPDEF(CVIF) /* [DP] <- [DP] */
+ {
+ Push<float4_t>(Pop<int4_t>());
+ return 0;
+ }
+
+ OPDEF(CVFI) /* [DP] <- [DP] */
+ {
+ Push<int4_t>(Pop<float4_t>());
+ return 0;
+ }
+}
diff --git a/src/virtualmachine/Operations.inl b/src/virtualmachine/Operations.inl
new file mode 100644
index 0000000..a1d4b43
--- /dev/null
+++ b/src/virtualmachine/Operations.inl
@@ -0,0 +1,60 @@
+OPDEF(UNDEF)
+OPDEF(IGNORE) /* no-op */
+OPDEF(BREAK) /* ??? */
+OPDEF(ENTER) /* Begin subroutine. */
+OPDEF(LEAVE) /* End subroutine. */
+OPDEF(CALL) /* Call subroutine. */
+OPDEF(PUSH) /* push to stack. */
+OPDEF(POP) /* discard top-of-stack. */
+OPDEF(CONST) /* load constant to stack. */
+OPDEF(LOCAL) /* get local variable. */
+OPDEF(JUMP) /* unconditional jump. */
+OPDEF(EQ) /* compare integers, jump if equal. */
+OPDEF(NE) /* compare integers, jump if not equal. */
+OPDEF(LTI) /* compare integers, jump if less-than. */
+OPDEF(LEI) /* compare integers, jump if less-than-or-equal. */
+OPDEF(GTI) /* compare integers, jump if greater-than. */
+OPDEF(GEI) /* compare integers, jump if greater-than-or-equal. */
+OPDEF(LTU) /* compare unsigned integers, jump if less-than */
+OPDEF(LEU) /* compare unsigned integers, jump if less-than-or-equal */
+OPDEF(GTU) /* compare unsigned integers, jump if greater-than */
+OPDEF(GEU) /* compare unsigned integers, jump if greater-than-or-equal */
+OPDEF(EQF) /* compare floats, jump if equal */
+OPDEF(NEF) /* compare floats, jump if not-equal */
+OPDEF(LTF) /* compare floats, jump if less-than */
+OPDEF(LEF) /* compare floats, jump if less-than-or-equal */
+OPDEF(GTF) /* compare floats, jump if greater-than */
+OPDEF(GEF) /* compare floats, jump if greater-than-or-equal */
+OPDEF(LOAD1) /* load 1-byte from memory */
+OPDEF(LOAD2) /* load 2-byte from memory */
+OPDEF(LOAD4) /* load 4-byte from memory */
+OPDEF(STORE1) /* store 1-byte to memory */
+OPDEF(STORE2) /* store 2-byte to memory */
+OPDEF(STORE4) /* store 4-byte to memory */
+OPDEF(ARG) /* marshal argument */
+OPDEF(BLOCK_COPY) /* block copy... */
+OPDEF(SEX8) /* Pedophilia */
+OPDEF(SEX16) /* Sign-Extend 16-bit */
+OPDEF(NEGI) /* Negate integer. */
+OPDEF(ADD) /* Add integers (two's complement). */
+OPDEF(SUB) /* Subtract integers (two's complement). */
+OPDEF(DIVI) /* Divide signed integers. */
+OPDEF(DIVU) /* Divide unsigned integers. */
+OPDEF(MODI) /* Modulus (signed). */
+OPDEF(MODU) /* Modulus (unsigned). */
+OPDEF(MULI) /* Multiply signed integers. */
+OPDEF(MULU) /* Multiply unsigned integers. */
+OPDEF(BAND) /* Bitwise AND */
+OPDEF(BOR) /* Bitwise OR */
+OPDEF(BXOR) /* Bitwise eXclusive-OR */
+OPDEF(BCOM) /* Bitwise COMplement */
+OPDEF(LSH) /* Left-shift */
+OPDEF(RSHI) /* Right-shift (algebraic; preserve sign) */
+OPDEF(RSHU) /* Right-shift (bitwise; ignore sign) */
+OPDEF(NEGF) /* Negate float */
+OPDEF(ADDF) /* Add floats */
+OPDEF(SUBF) /* Subtract floats */
+OPDEF(DIVF) /* Divide floats */
+OPDEF(MULF) /* Multiply floats */
+OPDEF(CVIF) /* Convert to integer from float */
+OPDEF(CVFI) /* Convert to float from integer */ \ No newline at end of file
diff --git a/src/virtualmachine/Syscalls.cpp b/src/virtualmachine/Syscalls.cpp
new file mode 100644
index 0000000..31b7dd4
--- /dev/null
+++ b/src/virtualmachine/Syscalls.cpp
@@ -0,0 +1,99 @@
+#include <cstdio>
+#include <cstdlib>
+#include <cmath>
+#include "VirtualMachine.h"
+#include "simulation/Simulation.h"
+#include "graphics/Renderer.h"
+
+namespace vm
+{
+ #define ARG(n) (Get(RP + ((2 + n) * sizeof(word))))
+
+ #define TRAPDEF(f) int VirtualMachine::trap##f()
+
+ TRAPDEF(sin)
+ {
+ Push<float4_t>(sin(ARG(0).float4));
+ return 0;
+ }
+
+ TRAPDEF(cos)
+ {
+ Push<float4_t>(cos(ARG(0).float4));
+ return 0;
+ }
+
+ TRAPDEF(atan2)
+ {
+ Push<float4_t>(atan2(ARG(0).float4, ARG(1).float4));
+ return 0;
+ }
+
+ TRAPDEF(sqrt)
+ {
+ Push<float4_t>(sqrt(ARG(0).float4));
+ return 0;
+ }
+
+ TRAPDEF(floor)
+ {
+ Push<float4_t>(floor(ARG(0).float4));
+ return 0;
+ }
+
+ TRAPDEF(ceil)
+ {
+ Push<float4_t>(ceil(ARG(0).float4));
+ return 0;
+ }
+
+
+ TRAPDEF(print)
+ {
+ char *text;
+ text = (char*)(ram) + ARG(0).int4;
+ printf("%s", text);
+ return 0;
+ }
+
+
+ TRAPDEF(error)
+ {
+ char *msg;
+ msg = (char*)(ram) + ARG(0).int4;
+ printf("%s", msg);
+ End();
+ return 0;
+ }
+
+
+ TRAPDEF(partCreate)
+ {
+ Push<int4_t>(sim->create_part(ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4));
+ return 0;
+ }
+
+ TRAPDEF(partChangeType)
+ {
+ sim->part_change_type(ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4);
+ return 0;
+ }
+
+ TRAPDEF(pmapData)
+ {
+ Push<int4_t>(sim->pmap[ARG(1).int4][ARG(0).int4]);
+ return 0;
+ }
+
+ TRAPDEF(deletePart)
+ {
+ sim->delete_part(ARG(0).int4, ARG(1).int4, ARG(2).int4);
+ return 0;
+ }
+
+ TRAPDEF(killPart)
+ {
+ sim->kill_part(ARG(0).int4);
+ return 0;
+ }
+}
diff --git a/src/virtualmachine/Syscalls.inl b/src/virtualmachine/Syscalls.inl
new file mode 100644
index 0000000..75455c1
--- /dev/null
+++ b/src/virtualmachine/Syscalls.inl
@@ -0,0 +1,14 @@
+TRAPDEF(-1, sin)
+TRAPDEF(-2, cos)
+TRAPDEF(-3, atan2)
+TRAPDEF(-4, sqrt)
+TRAPDEF(-5, floor)
+TRAPDEF(-6, ceil)
+
+TRAPDEF(-7, error)
+TRAPDEF(-8, print)
+TRAPDEF(-9, partCreate)
+TRAPDEF(-10, partChangeType)
+TRAPDEF(-11, pmapData)
+TRAPDEF(-12, deletePart)
+TRAPDEF(-13, killPart)
diff --git a/src/virtualmachine/VirtualMachine.cpp b/src/virtualmachine/VirtualMachine.cpp
new file mode 100644
index 0000000..929723a
--- /dev/null
+++ b/src/virtualmachine/VirtualMachine.cpp
@@ -0,0 +1,405 @@
+#include <string>
+#include <cstring>
+#include <stdio.h>
+#include <stdlib.h>
+#include "VirtualMachine.h"
+
+namespace vm
+{
+
+ VirtualMachine::VirtualMachine(int hunkMbytes):
+ bigEndian(false),
+ hunk(NULL),
+ hunkSize(1048576),
+ hunkFree(0),
+ rom(NULL),
+ romSize(0),
+ ram(NULL),
+ ramSize(0),
+ dataStack(0),
+ returnStack(0),
+ DP(0), /* Datastack pointer. */
+ RP(0), /* Return stack pointer. */
+ PC(0),
+ cm(0),
+ cycles(0),
+ sim(NULL),
+ ren(NULL)
+ {
+ hunk = new char[hunkSize];
+ std::fill(hunk, hunk+hunkSize, 0);
+ }
+
+ VirtualMachine::~VirtualMachine()
+ {
+ delete[] hunk;
+ }
+
+ #define DEBUGTRACE(args, ...) printf(args);
+
+ int VirtualMachine::opcodeParameterSize(int opcode)
+ {
+ #define OP(n) OP##n
+ switch (opcode)
+ {
+ case OP(ENTER):
+ case OP(LEAVE):
+ case OP(LOCAL):
+ case OP(EQ):
+ case OP(NE):
+ case OP(LTI):
+ case OP(LEI):
+ case OP(GTI):
+ case OP(GEI):
+ case OP(LTU):
+ case OP(LEU):
+ case OP(GTU):
+ case OP(GEU):
+ case OP(EQF):
+ case OP(NEF):
+ case OP(LTF):
+ case OP(LEF):
+ case OP(GTF):
+ case OP(GEF):
+ case OP(CONST):
+ case OP(BLOCK_COPY):
+ return sizeof(uint4_t);
+ break;
+ case OP(ARG):
+ return sizeof(uint1_t);
+ break;
+ }
+ return 0;
+ #undef OP
+ }
+
+ /* Read one octet from file. */
+ int VirtualMachine::readByte(std::istream & input)
+ {
+ int o;
+ o = input.get();
+ if (o < 0) o = 0; /* EOF (hack) */
+ return o;
+ }
+
+ /* Read little-endian 32-bit integer from file. */
+ int VirtualMachine::readInt(std::istream & input)
+ {
+ int a, b, c, d, n;
+
+ a = readByte(input);
+ b = readByte(input);
+ c = readByte(input);
+ d = readByte(input);
+ n = (a) | (b << 8) | (c << 16) | (d << 24);
+ return n;
+ }
+
+ int VirtualMachine::readProgram(std::istream & input)
+ {
+ qvm_header_t qvminfo;
+ int i, n;
+ uint1_t x[4];
+ word w;
+
+ DEBUGTRACE("Loading file...\n");
+ qvminfo.magic = readInt(input); /* magic. */
+ if (qvminfo.magic != QVM_MAGIC)
+ {
+ DEBUGTRACE("Invalid magic");
+ throw InvalidProgramException();
+ //q3vm_error("Does not appear to be a QVM file.");
+ /* XXX: option to force continue. */
+ return 0;
+ }
+ DEBUGTRACE("Magic OK\n");
+ /* variable-length instructions mean instruction count != code length */
+ qvminfo.inscount = readInt(input);
+ qvminfo.codeoff = readInt(input);
+ qvminfo.codelen = readInt(input);
+ qvminfo.dataoff = readInt(input);
+ qvminfo.datalen = readInt(input);
+ qvminfo.litlen = readInt(input);
+ qvminfo.bsslen = readInt(input);
+
+ /* Code segment should follow... */
+ /* XXX: use fseek with SEEK_CUR? */
+ DEBUGTRACE("Searching for .code @ %d from %d\n", qvminfo.codeoff, input.tellg());
+
+ // rom = (q3vm_rom_t*)(hunk); /* ROM-in-hunk */
+ rom = (Instruction*)calloc(qvminfo.inscount, sizeof(rom[0]));
+ while (input.tellg() < qvminfo.codeoff)
+ readByte(input);
+ while (romSize < qvminfo.inscount)
+ {
+ n = readByte(input);
+ w.int4 = 0;
+ if ((i = opcodeParameterSize(n)))
+ {
+ x[0] = x[1] = x[2] = x[3] = 0;
+ input.readsome((char*)x, i);
+ w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
+ }
+ rom[romSize].Operation = n;
+ rom[romSize].Parameter = w;
+ romSize++;
+ }
+ DEBUGTRACE("After loading code: at %d, should be %d\n", input.tellg(), qvminfo.codeoff + qvminfo.codelen);
+
+ /* Then data segment. */
+ // ram = hunk + ((romlen + 3) & ~3); /* RAM-in-hunk */
+ ram = hunk;
+ DEBUGTRACE("Searching for .data @ %d from %d\n", qvminfo.dataoff, input.tellg());
+ while (input.tellg() < qvminfo.dataoff)
+ readByte(input);
+ for (n = 0; n < (qvminfo.datalen / sizeof(uint1_t)); n++)
+ {
+ i = input.readsome((char*)x, sizeof(x));
+ w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
+ *((word*)(ram + ramSize)) = w;
+ ramSize += sizeof(word);
+ }
+
+ /* lit segment follows data segment. */
+ /* Assembler should have already padded properly. */
+ DEBUGTRACE("Loading .lit\n");
+ for (n = 0; n < (qvminfo.litlen / sizeof(uint1_t)); n++)
+ {
+ i = input.readsome((char*)x, sizeof(x));
+ memcpy(&(w.uint1), &x, sizeof(x)); /* no byte-swapping. */
+ *((word*)(ram + ramSize)) = w;
+ ramSize += sizeof(word);
+ }
+ /* bss segment. */
+ DEBUGTRACE("Allocating .bss %d (%X) bytes\n", qvminfo.bsslen, qvminfo.bsslen);
+ /* huge empty chunk. */
+ ramSize += qvminfo.bsslen;
+
+ hunkFree = hunkSize - ((ramSize * sizeof(uint1_t)) + 4);
+
+ DEBUGTRACE("VM hunk has %d of %d bytes free (RAM = %d B).\n", hunkFree, hunkSize, ramSize);
+ if (ramSize > hunkSize)
+ {
+ throw OutOfMemoryException();
+ return 0;
+ }
+
+ /* set up stack. */
+ {
+ int stacksize = 0x10000;
+ dataStack = ramSize - (stacksize / 2);
+ returnStack = ramSize;
+ //returnStack = dataStack+4;
+ RP = returnStack;
+ DP = dataStack;
+ }
+
+ /* set up PC for return-to-termination. */
+ PC = romSize + 1;
+
+ ramMask = ramSize;
+
+ return 1;
+ }
+
+ int VirtualMachine::LoadProgram(std::vector<char> data)
+ {
+ /*class vectorwrapbuf : public std::basic_streambuf<char, std::char_traits<char> >
+ {
+ public:
+ vectorwrapbuf(std::vector<char> &vec) {
+ setg(vec.data(), vec.data(), vec.data() + vec.size());
+ }
+ };
+ vectorwrapbuf databuf(data);
+ std::istream is(&databuf);
+ return readProgram(is);*/
+ std::stringstream ss(std::string(data.begin(), data.end()));
+ return readProgram((std::istream &)ss);
+ }
+
+ int VirtualMachine::LoadProgram(char * filename)
+ {
+ /*FILE * qvmfile = fopen(filename, "rb");
+ qvm_header_t qvminfo;
+ int i, n;
+ uint1_t x[4];
+ word w;
+
+ DEBUGTRACE("Loading file...\n");
+ qvminfo.magic = readInt(qvmfile);
+ if (qvminfo.magic != QVM_MAGIC)
+ {
+ DEBUGTRACE("Invalid magic");
+ return 0;
+ }
+ DEBUGTRACE("Magic OK\n");
+
+ qvminfo.inscount = readInt(qvmfile);
+ qvminfo.codeoff = readInt(qvmfile);
+ qvminfo.codelen = readInt(qvmfile);
+ qvminfo.dataoff = readInt(qvmfile);
+ qvminfo.datalen = readInt(qvmfile);
+ qvminfo.litlen = readInt(qvmfile);
+ qvminfo.bsslen = readInt(qvmfile);
+
+
+ DEBUGTRACE("Searching for .code @ %d from %d\n", qvminfo.codeoff, ftell(qvmfile));
+
+ rom = (Instruction*)calloc(qvminfo.inscount, sizeof(rom[0]));
+ while (ftell(qvmfile) < qvminfo.codeoff)
+ readByte(qvmfile);
+ while (romSize < qvminfo.inscount)
+ {
+ n = readByte(qvmfile);
+ w.int4 = 0;
+ if ((i = opcodeParameterSize(n)))
+ {
+ x[0] = x[1] = x[2] = x[3] = 0;
+ fread(&x, 1, i, qvmfile);
+ w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
+ }
+ rom[romSize].Operation = n;
+ rom[romSize].Parameter = w;
+ romSize++;
+ }
+ DEBUGTRACE("After loading code: at %d, should be %d\n", ftell(qvmfile), qvminfo.codeoff + qvminfo.codelen);
+
+
+ ram = hunk;
+ DEBUGTRACE("Searching for .data @ %d from %d\n", qvminfo.dataoff, ftell(qvmfile));
+ while (ftell(qvmfile) < qvminfo.dataoff)
+ readByte(qvmfile);
+ for (n = 0; n < (qvminfo.datalen / sizeof(uint1_t)); n++)
+ {
+ i = fread(&x, 1, sizeof(x), qvmfile);
+ w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
+ *((word*)(ram + ramSize)) = w;
+ ramSize += sizeof(word);
+ }
+
+
+ DEBUGTRACE("Loading .lit\n");
+ for (n = 0; n < (qvminfo.litlen / sizeof(uint1_t)); n++)
+ {
+ i = fread(&x, 1, sizeof(x), qvmfile);
+ memcpy(&(w.uint1), &x, sizeof(x));
+ *((word*)(ram + ramSize)) = w;
+ ramSize += sizeof(word);
+ }
+
+ DEBUGTRACE("Allocating .bss %d (%X) bytes\n", qvminfo.bsslen, qvminfo.bsslen);
+ ramSize += qvminfo.bsslen;
+
+ hunkFree = hunkSize - ((ramSize * sizeof(uint1_t)) + 4);
+
+ DEBUGTRACE("VM hunk has %d of %d bytes free (RAM = %d B).\n", hunkFree, hunkSize, ramSize);
+ if (ramSize > hunkSize)
+ {
+ throw OutOfMemoryException();
+ return 0;
+ }
+
+
+ {
+ int stacksize = 0x10000;
+ dataStack = ramSize - (stacksize / 2);
+ //returnStack = ramSize;
+ returnStack = dataStack+4;
+ RP = returnStack;
+ DP = dataStack;
+ }
+
+
+ PC = romSize + 1;
+
+ ramMask = ramSize;
+
+ return 1;*/
+ return 0; //temporary, something has to be returned for now
+ }
+
+ void VirtualMachine::End()
+ {
+ PC = romSize+1;
+ }
+
+ int VirtualMachine::CallInterpreted(int address)
+ {
+ word w;
+ int i, argCount = 0;
+
+ /* Set up call. */
+ OpPUSH(w);
+ DEBUGTRACE("Starting with PC=%d, DP=%d, RP=%d to %d\n", PC, DP, RP, address);
+ w.int4 = (argCount + 2) * sizeof(word);
+ OpENTER(w);
+ i = 8;
+ /**w.int4 = arg0; Marshal(i, w); i += 4;
+ w.int4 = arg1; Marshal(i, w); i += 4;
+ w.int4 = arg2; Marshal(i, w); i += 4;
+ w.int4 = arg3; Marshal(i, w); i += 4;
+ w.int4 = arg4; Marshal(i, w); i += 4;
+ w.int4 = arg5; Marshal(i, w); i += 4;
+ w.int4 = arg6; Marshal(i, w); i += 4;
+ w.int4 = arg7; Marshal(i, w); i += 4;
+ w.int4 = arg8; Marshal(i, w); i += 4;
+ w.int4 = arg9; Marshal(i, w); i += 4;
+ w.int4 = arg10; Marshal(i, w); i += 4;
+ w.int4 = arg11; Marshal(i, w); i += 4;
+ w.int4 = arg12; Marshal(i, w); i += 4;*/
+ w.int4 = address;
+ Push(w);
+ OpCALL(w);
+ DEBUGTRACE("Upon running PC=%d, DP=%d, RP=%d\n", PC, DP, RP);
+ Run();
+ DEBUGTRACE("At finish PC=%d, DP=%d, RP=%d\n", PC, DP, RP);
+ w.int4 = (argCount + 2) * sizeof(word);
+ OpLEAVE(w);
+ OpPOP(w);
+ PC = romSize + 1;
+ return 0;
+ }
+
+ int VirtualMachine::Run()
+ {
+ bool running = true;
+ int operation;
+ word parameter;
+ while(running)
+ {
+ cycles++;
+ if(PC > romSize)
+ {
+ running = false;
+ continue;
+ }
+ if (PC < 0)
+ {
+ syscall(PC);
+ continue;
+ }
+ operation = rom[PC].Operation;
+ parameter = rom[PC].Parameter;
+ PC++;
+ (this->*operations[operation])(parameter);
+ }
+ return 1;
+ }
+
+
+
+ int VirtualMachine::syscall(int trap)
+ {
+ PC = Pop<int4_t>();
+
+ switch (trap)
+ {
+ #define TRAPDEF(n, f) case n: trap##f(); break;
+ #include "Syscalls.inl"
+ #undef TRAPDEF
+ }
+
+ return 1;
+ }
+} \ No newline at end of file
diff --git a/src/virtualmachine/VirtualMachine.h b/src/virtualmachine/VirtualMachine.h
new file mode 100644
index 0000000..b295d02
--- /dev/null
+++ b/src/virtualmachine/VirtualMachine.h
@@ -0,0 +1,282 @@
+#pragma once
+
+#include "Exceptions.h"
+
+class Simulation;
+class Renderer;
+
+namespace vm
+{
+
+ class VirtualMachine;
+
+ typedef char ram_t;
+
+
+
+ typedef unsigned int uint4_t;
+ typedef signed int int4_t;
+
+ typedef unsigned short uint2_t;
+ typedef signed short int2_t;
+
+ typedef unsigned char uint1_t;
+ typedef signed char int1_t;
+
+ typedef float float4_t;
+
+ union word
+ {
+ uint4_t uint4;
+ int4_t int4;
+ uint2_t uint2;
+ int2_t int2;
+ uint1_t uint1;
+ int1_t int1;
+ float4_t float4;
+ };
+
+ typedef int (VirtualMachine::*OperationFunction)(word parameter);
+
+ struct Instruction
+ {
+ int Operation;
+ word Parameter;
+ //opfunc opfunc;
+ };
+
+ enum
+ {
+ QVM_MAGIC = 0x12721444,
+ };
+
+ struct qvm_header_t
+ {
+ int magic;
+ /* not-entirely-RISC ISA, so instruction count != codelen */
+ int inscount; /* instruction count. */
+ int codeoff; /* file offset of code segment. */
+ int codelen; /* length of code segment, in octets. */
+ int dataoff; /* file offset of data segment. */
+ int datalen; /* length of data segment, in octets. */
+ int litlen; /* length of lit segment (which is embedded in data segment). */
+ int bsslen; /* length of bss segment. */
+ };
+
+ class VirtualMachine
+ {
+
+ int * instructionPointers;
+
+ bool bigEndian; /* host is big-endian (requires byte-swapping). */
+
+ /* Memory spaces. */
+ char * hunk; /* hunk space (malloc'd). */
+ int hunkSize; /* total hunk size. */
+ int hunkFree; /* free pointer. */
+
+ /* Read-Only Memory (code). */
+ Instruction * rom;
+ int romSize;
+ int romMask;
+
+ char * compiledRom;
+ int compiledRomSize;
+ int compiledRomMask;
+
+ /* Random-Access Memory (data). */
+ ram_t *ram;
+ int ramSize;
+ int ramMask;
+
+ int dataStack;
+ int returnStack;
+
+ word r[4]; /* registers. */
+ int DP; /* Datastack pointer. */
+ int RP; /* Return stack pointer. */
+ int PC; /* Program Counter. */
+ // int AP; /* Argument pointer. (hrm...) */
+
+ /* various flags. */
+ int cm:1;
+
+ /* Execution time */
+ int cycles;
+
+ #define TRAPDEF(n, f) int trap##f();
+ #include "Syscalls.inl"
+ #undef TRAPDEF
+
+ static OperationFunction operations[];
+
+
+ #define OPDEF(n) OP##n,
+ enum {
+ #include "Operations.inl"
+ };
+ #undef OPDEF
+
+ int readProgram(std::istream & input);
+ int readByte(std::istream & input);
+ int readInt(std::istream & input);
+ int opcodeParameterSize(int opcode);
+ int syscall(int programCounter);
+
+ //Used by the JIT
+ #ifdef VMJIT
+ int constant4();
+ int constant1();
+ void emit1(int v);
+ void emit4(int v);
+ void emitInstruction(const char *string);
+ void emitCommand(int command);
+ void emitAddEDI4();
+ void emitMovEAXEDI();
+ bool emitMovEBXEDI(int andit);
+ static int hex(int c);
+ #endif
+public:
+ #ifdef VMJIT
+ static void callFromCompiled();
+ static void callSyscall();
+ bool Compile();
+ int CallCompiled(int address);
+ #endif
+ Simulation * sim;
+ Renderer * ren;
+
+ #define OPDEF(n) int Op##n(word parameter);
+ #include "Operations.inl"
+ #undef OPDEF
+
+ VirtualMachine(int hunkMbytes);
+ virtual ~VirtualMachine();
+
+ int LoadProgram(char * filename);
+ int LoadProgram(std::vector<char> fileData);
+ int Run();
+ int CallInterpreted(int address);
+ void End();
+ void Marshal(int address, word element)
+ {
+ ram_t * ptr = ram+RP+address;
+ if(ptr < ram || ptr > ram+ramSize - sizeof(word))
+ throw AccessViolationException(RP+address);
+ *((word*)ptr) = element;
+ }
+
+ template <typename T> T Get(int address)
+ {
+ ram_t * ptr = ram+address;
+ if(ptr < ram || ptr > ram+ramSize - sizeof(word))
+ throw AccessViolationException(address);
+ return *((T*)ptr);
+ }
+
+ template <typename T> void Set(int address, T value)
+ {
+ ram_t * ptr = ram+address;
+ if(ptr < ram || ptr > ram+ramSize - sizeof(word))
+ throw AccessViolationException(address);
+ *((T*)ptr) = value;
+ }
+
+ template <typename T> T Pop ()
+ {
+ ram_t * ptr = ram+DP;
+ if(DP + sizeof(word) < hunkSize)
+ DP += sizeof(word);
+ else
+ throw StackUnderflowException();
+ return *((T*)ptr);
+ };
+
+ template <typename T> T RPop ()
+ {
+ ram_t * ptr = ram+RP;
+ if(RP + sizeof(word) < hunkSize)
+ RP += sizeof(word);
+ else
+ throw StackUnderflowException();
+ return *((T*)ptr);
+ };
+
+ template <typename T> void Push(T value)
+ {
+ if(DP - sizeof(word) >= 0)
+ DP -= sizeof(word);
+ else
+ throw StackOverflowException();
+ ram_t * ptr = ram+DP;
+ *((T*)ptr) = value;
+ };
+
+ template <typename T> void RPush(T value)
+ {
+ if(RP - sizeof(word) >= 0)
+ RP -= sizeof(word);
+ else
+ throw StackOverflowException();
+ ram_t * ptr = ram+RP;
+ *((T*)ptr) = value;
+ };
+
+ word Get(int address)
+ {
+ ram_t * ptr = ram+address;
+ if(ptr < ram || ptr > ram+ramSize - sizeof(word))
+ throw AccessViolationException(address);
+ return *((word*)ptr);
+ }
+
+ void Set(int address, word value)
+ {
+ ram_t * ptr = ram+address;
+ if(ptr < ram || ptr > ram+ramSize - sizeof(word))
+ throw AccessViolationException(address);
+ *((word*)ptr) = value;
+ }
+
+ word Pop()
+ {
+ ram_t * ptr = ram+DP;
+ if(DP + sizeof(word) < hunkSize)
+ DP += sizeof(word);
+ else
+ throw StackUnderflowException();
+ return *((word*)ptr);
+ };
+
+ void Push(word value)
+ {
+ if(DP - sizeof(word) >= 0)
+ DP -= sizeof(word);
+ else
+ throw StackOverflowException();
+ ram_t * ptr = ram+DP;
+ *((word*)ptr) = value;
+ };
+
+ word RPop()
+ {
+ ram_t * ptr = ram+RP;
+ if(RP + sizeof(word) < hunkSize)
+ RP += sizeof(word);
+ else
+ throw StackUnderflowException();
+ return *((word*)ptr);
+ };
+
+ void RPush(word value)
+ {
+ if(RP - sizeof(word) >= 0)
+ RP -= sizeof(word);
+ else
+ throw StackOverflowException();
+ ram_t * ptr = ram+RP;
+ *((word*)ptr) = value;
+ };
+ };
+
+}